From bc5bd4154ea9aa98aeb35fed883400c9bd8b930e Mon Sep 17 00:00:00 2001 From: mrcolor Date: Tue, 8 Dec 2009 11:05:11 +0000 Subject: [PATCH] our current experimental Neutrino branch git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@27 e54a6e83-5905-42d5-8d5c-058d10e6a962 --- .svnignore | 12 + AUTHORS | 31 + ChangeLog | 0 Makefile.am | 3 + NEWS | 0 acinclude.m4 | 406 + autogen.sh | 72 + configure.ac | 111 + cross-configure.cool | 32 + cross-configure.tuxbox | 25 + data/Makefile.am | 1 + data/fonts/12.pcf.gz | Bin 0 -> 4303 bytes data/fonts/14B.pcf.gz | Bin 0 -> 4468 bytes data/fonts/15B.pcf.gz | Bin 0 -> 4844 bytes data/fonts/Makefile.am | 7 + data/fonts/README.pcf | 10 + data/fonts/md_khmurabi_10.ttf | Bin 0 -> 247668 bytes data/fonts/micron.ttf | Bin 0 -> 28768 bytes data/fonts/micron_bold.ttf | Bin 0 -> 28588 bytes data/fonts/micron_italic.ttf | Bin 0 -> 30652 bytes data/icons/0.raw | Bin 0 -> 293 bytes data/icons/1.raw | Bin 0 -> 293 bytes data/icons/16_9.raw | Bin 0 -> 213 bytes data/icons/16_9_gray.raw | Bin 0 -> 213 bytes data/icons/2.raw | Bin 0 -> 293 bytes data/icons/3.raw | Bin 0 -> 293 bytes data/icons/4.raw | Bin 0 -> 293 bytes data/icons/5.raw | Bin 0 -> 293 bytes data/icons/6.raw | Bin 0 -> 293 bytes data/icons/7.raw | Bin 0 -> 293 bytes data/icons/8.raw | Bin 0 -> 293 bytes data/icons/9.raw | Bin 0 -> 293 bytes data/icons/Makefile.am | 17 + data/icons/audio.raw | Bin 0 -> 293 bytes data/icons/blau.raw | Bin 0 -> 133 bytes data/icons/bookmarkmanager.raw | Bin 0 -> 293 bytes data/icons/border_lr.raw | Bin 0 -> 4613 bytes data/icons/border_ul.raw | Bin 0 -> 4613 bytes data/icons/ca.raw | Bin 0 -> 197 bytes data/icons/colors.raw | Bin 0 -> 293 bytes data/icons/dbox.raw | Bin 0 -> 293 bytes data/icons/dbox_small.raw | Bin 0 -> 167 bytes data/icons/dd.raw | Bin 0 -> 213 bytes data/icons/dd_avail.raw | Bin 0 -> 213 bytes data/icons/dd_gray.raw | Bin 0 -> 213 bytes data/icons/down.raw | Bin 0 -> 293 bytes data/icons/error.raw | Bin 0 -> 293 bytes data/icons/features.raw | Bin 0 -> 293 bytes data/icons/file.raw | Bin 0 -> 133 bytes data/icons/folder.raw | Bin 0 -> 133 bytes data/icons/fta.raw | Bin 0 -> 197 bytes data/icons/games.raw | Bin 0 -> 293 bytes data/icons/gelb.raw | Bin 0 -> 133 bytes data/icons/gruen.raw | Bin 0 -> 133 bytes data/icons/help.raw | Bin 0 -> 293 bytes data/icons/help_small.raw | Bin 0 -> 167 bytes data/icons/hidden.raw | Bin 0 -> 293 bytes data/icons/home.raw | Bin 0 -> 293 bytes data/icons/info.raw | Bin 0 -> 293 bytes data/icons/keybinding.raw | Bin 0 -> 293 bytes data/icons/language.raw | Bin 0 -> 293 bytes data/icons/lcd.raw | Bin 0 -> 293 bytes data/icons/left.raw | Bin 0 -> 293 bytes data/icons/lock.raw | Bin 0 -> 293 bytes data/icons/mainmenue.raw | Bin 0 -> 293 bytes data/icons/mounted.raw | Bin 0 -> 293 bytes data/icons/movie.raw | Bin 0 -> 133 bytes data/icons/movieplayer.raw | Bin 0 -> 80657 bytes data/icons/mp3-0.jpg | Bin 0 -> 165943 bytes data/icons/mp3-1.jpg | Bin 0 -> 107179 bytes data/icons/mp3-2.jpg | Bin 0 -> 83130 bytes data/icons/mp3-3.jpg | Bin 0 -> 323758 bytes data/icons/mp3-4.jpg | Bin 0 -> 141297 bytes data/icons/mp3-5.jpg | Bin 0 -> 210180 bytes data/icons/mp3-6.jpg | Bin 0 -> 52168 bytes data/icons/mp3-7.jpg | Bin 0 -> 44528 bytes data/icons/mp3.jpg | Bin 0 -> 278903 bytes data/icons/mp3.raw | Bin 0 -> 133 bytes data/icons/mute.raw | Bin 0 -> 501 bytes data/icons/mute_small.raw | Bin 0 -> 293 bytes data/icons/network.raw | Bin 0 -> 293 bytes data/icons/not_mounted.raw | Bin 0 -> 293 bytes data/icons/numericpad.raw | Bin 0 -> 12405 bytes data/icons/ok.raw | Bin 0 -> 293 bytes data/icons/power.raw | Bin 0 -> 133 bytes data/icons/radar.pal | Bin 0 -> 768 bytes data/icons/radar0.raw | Bin 0 -> 4101 bytes data/icons/radar1.raw | Bin 0 -> 4101 bytes data/icons/radar2.raw | Bin 0 -> 4101 bytes data/icons/radar3.raw | Bin 0 -> 4101 bytes data/icons/radar4.raw | Bin 0 -> 4101 bytes data/icons/radar5.raw | Bin 0 -> 4101 bytes data/icons/radar6.raw | Bin 0 -> 4101 bytes data/icons/radar7.raw | Bin 0 -> 4101 bytes data/icons/radar8.raw | Bin 0 -> 4101 bytes data/icons/radar9.raw | Bin 0 -> 4101 bytes data/icons/radio.jpg | Bin 0 -> 193824 bytes data/icons/radiomode.jpg | Bin 0 -> 193824 bytes data/icons/radiomode.pal | Bin 0 -> 768 bytes data/icons/radiomode.raw | 1 + data/icons/recording.raw | Bin 0 -> 293 bytes data/icons/right.raw | Bin 0 -> 293 bytes data/icons/rot.raw | Bin 0 -> 133 bytes data/icons/scan.jpg | Bin 0 -> 198803 bytes data/icons/scan.pal | 1 + data/icons/scan.raw | 1 + data/icons/settings.raw | Bin 0 -> 293 bytes data/icons/shell.raw | Bin 0 -> 293 bytes data/icons/shutdown.jpg | Bin 0 -> 262311 bytes data/icons/shutdown.pal | Bin 0 -> 768 bytes data/icons/shutdown.raw | 4 + data/icons/softupdate.raw | Bin 0 -> 293 bytes data/icons/streaming.raw | Bin 0 -> 293 bytes data/icons/subt.raw | Bin 0 -> 133 bytes data/icons/subt_gray.raw | Bin 0 -> 133 bytes data/icons/timer.raw | Bin 0 -> 293 bytes data/icons/up.raw | Bin 0 -> 293 bytes data/icons/upnp.raw | Bin 0 -> 269 bytes data/icons/video.raw | Bin 0 -> 293 bytes data/icons/volume.raw | Bin 0 -> 4613 bytes data/icons/volumebody.raw | Bin 0 -> 665 bytes data/icons/volumeslider2.raw | Bin 0 -> 133 bytes data/icons/volumeslider2alpha.raw | Bin 0 -> 133 bytes data/icons/volumeslider2blue.raw | Bin 0 -> 133 bytes data/icons/volumeslider2green.raw | Bin 0 -> 133 bytes data/icons/volumeslider2red.raw | Bin 0 -> 133 bytes data/icons/vtxt.raw | Bin 0 -> 133 bytes data/icons/vtxt_gray.raw | Bin 0 -> 133 bytes data/inetradio/radio-stations.xml | 892 ++ data/iso-codes/Makefile.am | 5 + data/iso-codes/iso-639.tab | 489 + data/lcd/Makefile.am | 1 + data/lcd/clock/Makefile.am | 8 + data/lcd/clock/d_a.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_b.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_c.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_d.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_e.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_f.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_g.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_h.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_i.bmp | Bin 0 -> 126 bytes data/lcd/clock/d_j.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_a.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_b.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_c.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_d.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_e.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_f.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_g.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_h.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_i.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_j.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_k.bmp | Bin 0 -> 126 bytes data/lcd/clock/m_l.bmp | Bin 0 -> 126 bytes data/lcd/clock/t_a.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_b.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_c.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_d.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_e.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_f.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_g.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_h.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_i.bmp | Bin 0 -> 190 bytes data/lcd/clock/t_j.bmp | Bin 0 -> 190 bytes data/lcd/clock/w_a.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_b.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_c.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_d.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_e.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_f.bmp | Bin 0 -> 126 bytes data/lcd/clock/w_g.bmp | Bin 0 -> 126 bytes data/lcd/icons/Makefile.am | 10 + data/lcd/icons/lcd.png | Bin 0 -> 336 bytes data/lcd/icons/lcd2.png | Bin 0 -> 317 bytes data/lcd/icons/lcd3.png | Bin 0 -> 223 bytes data/lcd/icons/power.png | Bin 0 -> 352 bytes data/lcd/icons/rclock.raw | Bin 0 -> 213 bytes data/lcd/icons/setup.png | Bin 0 -> 219 bytes data/locale/Makefile.am | 46 + data/locale/_readme.txt | 67 + data/locale/bayrisch.locale | 708 ++ data/locale/bosanski.locale | 722 ++ data/locale/ch-baslerdeutsch.locale | 722 ++ data/locale/ch-berndeutsch.locale | 896 ++ data/locale/check.locale.files | 9 + data/locale/create-locals-update.pl | 50 + data/locale/create.locals.h | 43 + data/locale/create.locals_intern.h | 49 + data/locale/deutsch.locale | 1323 +++ data/locale/dutch.locale | 1237 +++ data/locale/english.locale | 1349 +++ data/locale/francais.locale | 807 ++ data/locale/french.locale | 1237 +++ data/locale/hungarian.locale | 1237 +++ data/locale/italiano.locale | 1237 +++ data/locale/nederlands.locale | 771 ++ data/locale/polski.locale | 1237 +++ data/locale/portugues.locale | 1237 +++ data/locale/russkij.locale | 1186 +++ data/locale/suomi.locale | 1049 ++ data/locale/svenska.locale | 954 ++ doc/ir.html | 74 + doc/picviewer.html | 39 + lib/Makefile.am | 13 + lib/connection/Makefile.am | 5 + lib/connection/basicclient.cpp | 164 + lib/connection/basicclient.h | 51 + lib/connection/basicmessage.h | 43 + lib/connection/basicserver.cpp | 172 + lib/connection/basicserver.h | 60 + lib/connection/basicsocket.cpp | 140 + lib/connection/basicsocket.h | 35 + lib/connection/messagetools.cpp | 96 + lib/connection/messagetools.h | 34 + lib/controldclient/Makefile.am | 10 + lib/controldclient/controldMsg.h | 172 + lib/controldclient/controldclient.cpp | 316 + lib/controldclient/controldclient.h | 196 + lib/controldclient/controldtypes.h | 62 + lib/libconfigfile/Makefile.am | 5 + lib/libconfigfile/configfile.cpp | 415 + lib/libconfigfile/configfile.h | 111 + lib/libcoolstream/.svnignore | 5 + lib/libcoolstream/audio_cs.h | 115 + lib/libcoolstream/cs_types.h | 11 + lib/libcoolstream/dmx_cs.h | 78 + lib/libcoolstream/dvb-ci.h | 62 + lib/libcoolstream/init_cs.h | 5 + lib/libcoolstream/playback_cs.h | 68 + lib/libcoolstream/pwrmngr.h | 53 + lib/libcoolstream/record_cs.h | 38 + lib/libcoolstream/video_cs.h | 203 + lib/libdvbsub/Debug.cpp | 41 + lib/libdvbsub/Debug.hpp | 28 + lib/libdvbsub/Makefile.am | 11 + lib/libdvbsub/PacketQueue.cpp | 47 + lib/libdvbsub/PacketQueue.hpp | 20 + lib/libdvbsub/dvbsub.cpp | 369 + lib/libdvbsub/dvbsub_thread.hpp | 8 + lib/libdvbsub/dvbsubtitle.cpp | 1200 +++ lib/libdvbsub/dvbsubtitle.h | 51 + lib/libdvbsub/helpers.cpp | 50 + lib/libdvbsub/helpers.hpp | 14 + lib/libdvbsub/osd.cpp | 287 + lib/libdvbsub/osd.h | 147 + lib/libdvbsub/reader_thread.hpp | 6 + lib/libdvbsub/tools.cpp | 267 + lib/libdvbsub/tools.h | 193 + lib/libeventserver/Makefile.am | 5 + lib/libeventserver/eventserver.cpp | 115 + lib/libeventserver/eventserver.h | 95 + lib/libmd5sum/Makefile.am | 5 + lib/libmd5sum/getline.h | 38 + lib/libmd5sum/libmd5sum.c | 87 + lib/libmd5sum/libmd5sum.h | 45 + lib/libmd5sum/md5.c | 413 + lib/libmd5sum/md5.h | 146 + lib/libmd5sum/sys2.h | 347 + lib/libmd5sum/system.h | 245 + lib/libnet/Makefile.am | 5 + lib/libnet/libnet.c | 260 + lib/libnet/libnet.h | 27 + lib/libnet/network_interfaces.cpp | 373 + lib/libnet/network_interfaces.h | 35 + lib/libtuxtxt/Makefile.am | 9 + lib/libtuxtxt/libtuxtxt.cpp | 95 + lib/libtuxtxt/tuxtxt.cpp | 6176 +++++++++++ lib/libtuxtxt/tuxtxt.h | 1506 +++ lib/libtuxtxt/tuxtxt_common.h | 1098 ++ lib/libtuxtxt/tuxtxt_def.h | 370 + lib/libupnpclient/Makefile.am | 8 + lib/libupnpclient/UPNPDevice.cpp | 465 + lib/libupnpclient/UPNPService.cpp | 172 + lib/libupnpclient/UPNPSocket.cpp | 159 + lib/libupnpclient/upnpclient.h | 119 + lib/sectionsdclient/Makefile.am | 18 + lib/sectionsdclient/sectionsdMsg.h | 375 + lib/sectionsdclient/sectionsdclient.cpp | 803 ++ lib/sectionsdclient/sectionsdclient.h | 251 + lib/sectionsdclient/sectionsdcontrol.cpp | 75 + lib/sectionsdclient/sectionsdtypes.h | 33 + lib/timerdclient/Makefile.am | 11 + lib/timerdclient/timerdclient.cpp | 483 + lib/timerdclient/timerdclient.h | 175 + lib/timerdclient/timerdmsg.h | 155 + lib/timerdclient/timerdtypes.h | 168 + lib/xmltree/Makefile.am | 8 + lib/xmltree/asciitab.h | 52 + lib/xmltree/hashtab.c | 134 + lib/xmltree/hashtab.h | 59 + lib/xmltree/iasctab.h | 53 + lib/xmltree/xmlinterface.cpp | 178 + lib/xmltree/xmlinterface.h | 93 + lib/xmltree/xmlparse.cpp | 2734 +++++ lib/xmltree/xmlparse.h | 317 + lib/xmltree/xmlrole.c | 1269 +++ lib/xmltree/xmlrole.h | 101 + lib/xmltree/xmltimpl.c | 1655 +++ lib/xmltree/xmltimpl.h | 60 + lib/xmltree/xmltok.c | 1164 +++ lib/xmltree/xmltok.h | 276 + lib/xmltree/xmltok/latin1tab.h | 62 + lib/xmltree/xmltok/nametab.h | 150 + lib/xmltree/xmltok/utf8tab.h | 63 + lib/xmltree/xmltok/xmldef.h | 63 + lib/xmltree/xmltree.cpp | 446 + lib/xmltree/xmltree.h | 156 + src/Makefile.am | 71 + src/daemonc/Makefile.am | 16 + src/daemonc/remotecontrol.cpp | 692 ++ src/daemonc/remotecontrol.h | 121 + src/driver/Makefile.am | 42 + src/driver/audiodec/Makefile.am | 22 + src/driver/audiodec/basedec.cpp | 241 + src/driver/audiodec/basedec.h | 63 + src/driver/audiodec/cdrdec.cpp | 65 + src/driver/audiodec/cdrdec.h | 43 + src/driver/audiodec/crc.c | 109 + src/driver/audiodec/crc.h | 29 + src/driver/audiodec/fft.c | 274 + src/driver/audiodec/fft.h | 43 + src/driver/audiodec/int_fft.c | 528 + src/driver/audiodec/mp3dec.cpp | 1372 +++ src/driver/audiodec/mp3dec.h | 81 + src/driver/audiodec/oggdec.cpp | 348 + src/driver/audiodec/oggdec.h | 78 + src/driver/audiodec/tag.c | 331 + src/driver/audiodec/tag.h | 183 + src/driver/audiodec/vis.cpp | 185 + src/driver/audiodec/wavdec.cpp | 213 + src/driver/audiodec/wavdec.h | 54 + src/driver/audiofile.cpp | 71 + src/driver/audiofile.h | 58 + src/driver/audiometadata.cpp | 108 + src/driver/audiometadata.h | 88 + src/driver/audioplay.cpp | 223 + src/driver/audioplay.h | 77 + src/driver/aviaext.cpp | 170 + src/driver/bigclock.cpp | 125 + src/driver/bigclock.h | 35 + src/driver/capture.cpp | 308 + src/driver/capture.h | 81 + src/driver/encoding.cpp | 44 + src/driver/encoding.h | 31 + src/driver/fb_window.cpp | 82 + src/driver/fb_window.h | 52 + src/driver/file.cpp | 110 + src/driver/file.h | 87 + src/driver/fontrenderer.cpp | 630 ++ src/driver/fontrenderer.h | 124 + src/driver/framebuffer.cpp | 1409 +++ src/driver/framebuffer.h | 198 + src/driver/genpsi.c | 282 + src/driver/genpsi.h | 38 + src/driver/netfile.cpp | 2023 ++++ src/driver/netfile.h | 214 + src/driver/pictureviewer/Makefile.am | 15 + src/driver/pictureviewer/bmp.cpp | 263 + src/driver/pictureviewer/config.h | 5 + src/driver/pictureviewer/crw.cpp | 281 + src/driver/pictureviewer/fb_display.cpp | 349 + src/driver/pictureviewer/fb_display.h | 8 + src/driver/pictureviewer/gif.cpp | 160 + src/driver/pictureviewer/jpeg.cpp | 310 + .../pictureviewer/make15color_errdiff.cpp | 155 + src/driver/pictureviewer/pictureviewer.cpp | 540 + src/driver/pictureviewer/pictureviewer.h | 114 + src/driver/pictureviewer/picv_client_server.h | 37 + src/driver/pictureviewer/png.cpp | 158 + src/driver/pictureviewer/resize.cpp | 80 + src/driver/pig.cpp | 319 + src/driver/pig.h | 83 + src/driver/rcinput.cpp | 1487 +++ src/driver/rcinput.h | 247 + src/driver/rfmod.cpp | 134 + src/driver/rfmod.h | 23 + src/driver/ringbuffer.c | 297 + src/driver/ringbuffer.h | 40 + src/driver/screen_max.cpp | 51 + src/driver/screen_max.h | 33 + src/driver/showframe.c | 80 + src/driver/shutdown_count.cpp | 97 + src/driver/shutdown_count.h | 53 + src/driver/slotbuffer.c | 107 + src/driver/slotbuffer.h | 44 + src/driver/stream2file.cpp | 185 + src/driver/stream2file.h | 59 + src/driver/streamts.cpp | 487 + src/driver/vcrcontrol.cpp | 941 ++ src/driver/vcrcontrol.h | 221 + src/driver/vfd.cpp | 899 ++ src/driver/vfd.h | 172 + src/driver/wav.h | 65 + src/global.h | 90 + src/gui/Makefile.am | 37 + src/gui/alphasetup.cpp | 272 + src/gui/alphasetup.h | 78 + src/gui/audio_select.cpp | 131 + src/gui/audio_select.h | 47 + src/gui/audioplayer.cpp | 2882 +++++ src/gui/audioplayer.h | 260 + src/gui/bedit/Makefile.am | 20 + src/gui/bedit/bouqueteditor_bouquets.cpp | 579 + src/gui/bedit/bouqueteditor_bouquets.h | 121 + src/gui/bedit/bouqueteditor_channels.cpp | 417 + src/gui/bedit/bouqueteditor_channels.h | 103 + src/gui/bedit/bouqueteditor_chanselect.cpp | 176 + src/gui/bedit/bouqueteditor_chanselect.h | 70 + src/gui/bookmarkmanager.cpp | 489 + src/gui/bookmarkmanager.h | 115 + src/gui/bouquetlist.cpp | 549 + src/gui/bouquetlist.h | 123 + src/gui/cam_menu.cpp | 402 + src/gui/cam_menu.h | 48 + src/gui/ch_mosaic.cpp | 256 + src/gui/ch_mosaic.h | 70 + src/gui/channellist.cpp | 1645 +++ src/gui/channellist.h | 135 + src/gui/color.cpp | 90 + src/gui/color.h | 100 + src/gui/customcolor.h | 57 + src/gui/dboxinfo.cpp | 207 + src/gui/dboxinfo.h | 66 + src/gui/dmx_tspidbandwidth.c | 235 + src/gui/epg_menu.cpp | 80 + src/gui/epg_menu.h | 43 + src/gui/epgplus.cpp | 1309 +++ src/gui/epgplus.h | 556 + src/gui/epgview.cpp | 1080 ++ src/gui/epgview.h | 108 + src/gui/eventlist.cpp | 1077 ++ src/gui/eventlist.h | 150 + src/gui/favorites.cpp | 169 + src/gui/favorites.h | 47 + src/gui/filebrowser.cpp | 1518 +++ src/gui/filebrowser.h | 233 + src/gui/hdd_menu.cpp | 458 + src/gui/hdd_menu.h | 55 + src/gui/imageinfo.cpp | 233 + src/gui/imageinfo.h | 69 + src/gui/infoclock.cpp | 87 + src/gui/infoclock.h | 36 + src/gui/infoviewer.cpp | 1316 +++ src/gui/infoviewer.h | 137 + src/gui/motorcontrol.cpp | 705 ++ src/gui/motorcontrol.h | 90 + src/gui/moviebrowser.cpp | 4317 ++++++++ src/gui/moviebrowser.h | 709 ++ src/gui/movieinfo.cpp | 1042 ++ src/gui/movieinfo.h | 204 + src/gui/movieplayer.cpp | 1339 +++ src/gui/movieplayer.h | 117 + src/gui/nfs.cpp | 406 + src/gui/nfs.h | 86 + src/gui/pictureviewer.cpp | 720 ++ src/gui/pictureviewer.h | 120 + src/gui/pluginlist.cpp | 371 + src/gui/pluginlist.h | 113 + src/gui/plugins.cpp | 618 ++ src/gui/plugins.h | 131 + src/gui/rc_lock.cpp | 104 + src/gui/rc_lock.h | 47 + src/gui/ringbuffer.c | 308 + src/gui/scale.cpp | 129 + src/gui/scale.h | 22 + src/gui/scan.cpp | 505 + src/gui/scan.h | 83 + src/gui/screensetup.cpp | 305 + src/gui/screensetup.h | 66 + src/gui/sleeptimer.cpp | 94 + src/gui/sleeptimer.h | 35 + src/gui/streaminfo2.cpp | 807 ++ src/gui/streaminfo2.h | 114 + src/gui/timeosd.cpp | 137 + src/gui/timeosd.h | 60 + src/gui/timerlist.cpp | 1179 +++ src/gui/timerlist.h | 98 + src/gui/update.cpp | 647 ++ src/gui/update.h | 82 + src/gui/upnpbrowser.cpp | 1382 +++ src/gui/upnpbrowser.h | 115 + src/gui/widget/Makefile.am | 24 + src/gui/widget/buttons.cpp | 37 + src/gui/widget/buttons.h | 37 + src/gui/widget/colorchooser.cpp | 256 + src/gui/widget/colorchooser.h | 73 + src/gui/widget/drawable.cpp | 150 + src/gui/widget/drawable.h | 164 + src/gui/widget/helpbox.cpp | 111 + src/gui/widget/helpbox.h | 64 + src/gui/widget/hintbox.cpp | 279 + src/gui/widget/hintbox.h | 82 + src/gui/widget/hintboxext.cpp | 314 + src/gui/widget/hintboxext.h | 84 + src/gui/widget/icons.h | 97 + src/gui/widget/keychooser.cpp | 169 + src/gui/widget/keychooser.h | 106 + src/gui/widget/lcdcontroler.cpp | 336 + src/gui/widget/lcdcontroler.h | 75 + src/gui/widget/listbox.cpp | 255 + src/gui/widget/listbox.h | 89 + src/gui/widget/listbox_legacy.cpp | 58 + src/gui/widget/listbox_legacy.h | 46 + src/gui/widget/listframe.cpp | 679 ++ src/gui/widget/listframe.h | 164 + src/gui/widget/menue.cpp | 1331 +++ src/gui/widget/menue.h | 399 + src/gui/widget/messagebox.cpp | 301 + src/gui/widget/messagebox.h | 84 + src/gui/widget/mountchooser.cpp | 98 + src/gui/widget/mountchooser.h | 67 + src/gui/widget/msgbox.cpp | 860 ++ src/gui/widget/msgbox.h | 171 + src/gui/widget/progressstatus.h | 39 + src/gui/widget/progresswindow.cpp | 178 + src/gui/widget/progresswindow.h | 77 + src/gui/widget/rgbcsynccontroler.cpp | 189 + src/gui/widget/rgbcsynccontroler.h | 73 + src/gui/widget/stringinput.cpp | 841 ++ src/gui/widget/stringinput.h | 163 + src/gui/widget/stringinput_ext.cpp | 668 ++ src/gui/widget/stringinput_ext.h | 232 + src/gui/widget/textbox.cpp | 533 + src/gui/widget/textbox.h | 168 + src/gui/widget/vfdcontroler.cpp | 270 + src/gui/widget/vfdcontroler.h | 74 + src/lcddisplay/lcddisplay.h | 77 + src/mymenu.h | 63 + src/neutrino.cpp | 4711 +++++++++ src/neutrino.h | 255 + src/neutrinoMessages.h | 168 + src/neutrino_menue.cpp | 3019 ++++++ src/nhttpd/AUTHORS | 21 + src/nhttpd/COPYING | 340 + src/nhttpd/Makefile.am | 18 + src/nhttpd/README | 18 + src/nhttpd/doc/Changelog.txt | 320 + .../doc/design/Kollaborationsdiagramm1.gif | 0 src/nhttpd/doc/design/Sequenzdiagramm1.gif | Bin 0 -> 13586 bytes src/nhttpd/doc/design/Webserver.gif | Bin 0 -> 14498 bytes src/nhttpd/doc/design/big_picturer.gif | 0 src/nhttpd/doc/design/mods.gif | Bin 0 -> 1483 bytes src/nhttpd/doc/design/nhttpd.gif | Bin 0 -> 10208 bytes src/nhttpd/doc/design/nhttpdNeutrinoAPI.gif | Bin 0 -> 7764 bytes src/nhttpd/doc/index.html | 22 + src/nhttpd/doc/nhttpd_controlapi.html | 1798 ++++ src/nhttpd/doc/nhttpd_implementation.html | 19 + src/nhttpd/doc/yhttpd.css | 51 + src/nhttpd/doc/yhttpd_conf.txt | 65 + src/nhttpd/doc/yhttpd_documentation.html | 15 + src/nhttpd/nhttpd | 121 + src/nhttpd/nhttpd.conf | 24 + src/nhttpd/tuxboxapi/AUTHORS | 20 + src/nhttpd/tuxboxapi/Makefile.am | 20 + src/nhttpd/tuxboxapi/controlapi.cpp | 2145 ++++ src/nhttpd/tuxboxapi/controlapi.h | 115 + src/nhttpd/tuxboxapi/lcdapi.cpp | 220 + src/nhttpd/tuxboxapi/lcdapi.h | 61 + src/nhttpd/tuxboxapi/neutrinoapi.cpp | 449 + src/nhttpd/tuxboxapi/neutrinoapi.h | 89 + src/nhttpd/tuxboxapi/neutrinoyparser.cpp | 1166 +++ src/nhttpd/tuxboxapi/neutrinoyparser.h | 70 + src/nhttpd/web/AUTHORS | 3 + src/nhttpd/web/Makefile.am | 15 + src/nhttpd/web/Y_About.yhtm | 27 + src/nhttpd/web/Y_Baselib.js | 384 + src/nhttpd/web/Y_Blocks.txt | 394 + .../web/Y_Boxcontrol_Bouquet_Editor_Edit.yhtm | 95 + .../web/Y_Boxcontrol_Bouquet_Editor_Main.yhtm | 186 + src/nhttpd/web/Y_Boxcontrol_Bouquetlist.yhtm | 11 + src/nhttpd/web/Y_Boxcontrol_Bouquets.yhtm | 15 + src/nhttpd/web/Y_Boxcontrol_Channellist.yhtm | 28 + src/nhttpd/web/Y_Boxcontrol_Menue.yhtm | 115 + src/nhttpd/web/Y_Boxcontrol_Messages.yhtm | 40 + src/nhttpd/web/Y_Dyn_Pages.yhtm | 1 + src/nhttpd/web/Y_EPG_Plus.yhtm | 211 + src/nhttpd/web/Y_Ext_Menue.yhtm | 72 + src/nhttpd/web/Y_Ext_Settings.yhtm | 37 + src/nhttpd/web/Y_Ext_Update.yhtm | 236 + src/nhttpd/web/Y_Ext_Update_refresh.yhtm | 23 + src/nhttpd/web/Y_Info_Help.yhtm | 18 + src/nhttpd/web/Y_Info_Menue.yhtm | 25 + src/nhttpd/web/Y_Info_Updates.yhtm | 47 + src/nhttpd/web/Y_Live.yhtm | 471 + src/nhttpd/web/Y_LiveViewFull.yhtm | 45 + src/nhttpd/web/Y_Live_DoubleView.yhtm | 19 + src/nhttpd/web/Y_Live_EPG.yhtm | 122 + src/nhttpd/web/Y_Live_EPG_Info.yhtm | 22 + src/nhttpd/web/Y_Live_Menue.yhtm | 85 + src/nhttpd/web/Y_Live_Msg.yhtm | 14 + src/nhttpd/web/Y_Live_Record.yhtm | 325 + src/nhttpd/web/Y_Live_Transcode.yhtm | 174 + src/nhttpd/web/Y_Main.css | 678 ++ src/nhttpd/web/Y_Menue.yhtm | 110 + src/nhttpd/web/Y_Settings.yhtm | 17 + src/nhttpd/web/Y_Settings_Backup.yhtm | 67 + src/nhttpd/web/Y_Settings_Live.yhtm | 69 + src/nhttpd/web/Y_Settings_Menue.yhtm | 71 + src/nhttpd/web/Y_Settings_Skin.yhtm | 36 + src/nhttpd/web/Y_Settings_Timer.yhtm | 60 + src/nhttpd/web/Y_Settings_VNC.yhtm | 58 + src/nhttpd/web/Y_Settings_automount.yhtm | 182 + .../web/Y_Settings_automount_liste.yhtm | 55 + src/nhttpd/web/Y_Settings_mount.yhtm | 87 + src/nhttpd/web/Y_Settings_mount_liste.yhtm | 45 + src/nhttpd/web/Y_Settings_neutrino_forms.yhtm | 2 + src/nhttpd/web/Y_Settings_nhttpd.yhtm | 116 + src/nhttpd/web/Y_Settings_ucodes.yhtm | 61 + src/nhttpd/web/Y_Settings_umount_liste.yhtm | 38 + src/nhttpd/web/Y_Settings_wol.yhtm | 53 + src/nhttpd/web/Y_Settings_yWeb.yhtm | 78 + src/nhttpd/web/Y_Settings_zapit.yhtm | 56 + src/nhttpd/web/Y_StreamInfo.yhtm | 73 + src/nhttpd/web/Y_Timer_Edit.yhtm | 166 + src/nhttpd/web/Y_Timer_List.yhtm | 55 + src/nhttpd/web/Y_Timer_Menue.yhtm | 24 + src/nhttpd/web/Y_Tools_Bootlogo.yhtm | 36 + src/nhttpd/web/Y_Tools_Boxcontrol.yhtm | 77 + src/nhttpd/web/Y_Tools_Check_Install.yhtm | 148 + src/nhttpd/web/Y_Tools_Cmd.yhtm | 91 + src/nhttpd/web/Y_Tools_Flash_Menue.yhtm | 101 + src/nhttpd/web/Y_Tools_Flash_Upload.yhtm | 162 + src/nhttpd/web/Y_Tools_Info_Menue.yhtm | 47 + src/nhttpd/web/Y_Tools_Installer.yhtm | 42 + src/nhttpd/web/Y_Tools_Menue.yhtm | 48 + src/nhttpd/web/Y_Tools_NetStream.yhtm | 125 + src/nhttpd/web/Y_Tools_Rcsim.yhtm | 19 + src/nhttpd/web/Y_Tools_Timer_Sync.yhtm | 385 + src/nhttpd/web/Y_Tools_fbshot.yhtm | 67 + src/nhttpd/web/Y_Tools_remote_osd.yhtm | 60 + src/nhttpd/web/Y_VLC.js | 246 + src/nhttpd/web/Y_Version.txt | 6 + src/nhttpd/web/Y_blank.htm | 10 + src/nhttpd/web/Y_neutrino_Blocks.txt | 1230 +++ src/nhttpd/web/channels.js | 46 + src/nhttpd/web/channels.txt | 25 + src/nhttpd/web/extentions.txt | 1 + src/nhttpd/web/favicon.ico | Bin 0 -> 894 bytes src/nhttpd/web/images/Makefile.am | 8 + src/nhttpd/web/images/addfolder.gif | Bin 0 -> 224 bytes src/nhttpd/web/images/arrowdown.gif | Bin 0 -> 195 bytes src/nhttpd/web/images/arrowup.gif | Bin 0 -> 192 bytes src/nhttpd/web/images/audiobar.gif | Bin 0 -> 634 bytes src/nhttpd/web/images/blank.gif | Bin 0 -> 43 bytes src/nhttpd/web/images/body-trans-l.gif | Bin 0 -> 2706 bytes src/nhttpd/web/images/body-trans-r.gif | Bin 0 -> 8279 bytes src/nhttpd/web/images/check_green.gif | Bin 0 -> 357 bytes src/nhttpd/web/images/chmod.gif | Bin 0 -> 346 bytes src/nhttpd/web/images/delete.gif | Bin 0 -> 220 bytes src/nhttpd/web/images/download.gif | Bin 0 -> 337 bytes src/nhttpd/web/images/elist.gif | Bin 0 -> 336 bytes src/nhttpd/web/images/epg.png | Bin 0 -> 209 bytes src/nhttpd/web/images/excl_yellow.gif | Bin 0 -> 596 bytes src/nhttpd/web/images/ftype_file.gif | Bin 0 -> 372 bytes src/nhttpd/web/images/ftype_folder.gif | Bin 0 -> 329 bytes src/nhttpd/web/images/ftype_link.gif | Bin 0 -> 327 bytes src/nhttpd/web/images/fullscreen.png | Bin 0 -> 516 bytes src/nhttpd/web/images/head-blue-l.gif | Bin 0 -> 1116 bytes src/nhttpd/web/images/head-blue-r.gif | Bin 0 -> 2583 bytes src/nhttpd/web/images/head-orange-l.gif | Bin 0 -> 1121 bytes src/nhttpd/web/images/head-orange-r.gif | Bin 0 -> 2561 bytes src/nhttpd/web/images/head-trans-l.gif | Bin 0 -> 776 bytes src/nhttpd/web/images/head-trans-r.gif | Bin 0 -> 2014 bytes src/nhttpd/web/images/help.gif | Bin 0 -> 1021 bytes src/nhttpd/web/images/help_white.gif | Bin 0 -> 93 bytes src/nhttpd/web/images/hidden.gif | Bin 0 -> 1478 bytes src/nhttpd/web/images/home-dir.gif | Bin 0 -> 352 bytes src/nhttpd/web/images/hourglas.gif | Bin 0 -> 819 bytes src/nhttpd/web/images/live.gif | Bin 0 -> 644 bytes src/nhttpd/web/images/live.png | Bin 0 -> 696 bytes src/nhttpd/web/images/live_popup.gif | Bin 0 -> 642 bytes src/nhttpd/web/images/live_popup.png | Bin 0 -> 809 bytes src/nhttpd/web/images/livelock.png | Bin 0 -> 824 bytes src/nhttpd/web/images/liveunlock.png | Bin 0 -> 815 bytes src/nhttpd/web/images/lock.gif | Bin 0 -> 459 bytes src/nhttpd/web/images/modify.png | Bin 0 -> 1331 bytes src/nhttpd/web/images/new.gif | Bin 0 -> 173 bytes src/nhttpd/web/images/ok.gif | Bin 0 -> 459 bytes src/nhttpd/web/images/pause.png | Bin 0 -> 721 bytes src/nhttpd/web/images/play.png | Bin 0 -> 717 bytes src/nhttpd/web/images/properties.gif | Bin 0 -> 214 bytes src/nhttpd/web/images/rc.gif | Bin 0 -> 20540 bytes src/nhttpd/web/images/rc_sagem.jpg | Bin 0 -> 13244 bytes src/nhttpd/web/images/rcd.jpg | Bin 0 -> 37918 bytes src/nhttpd/web/images/record.gif | Bin 0 -> 1035 bytes src/nhttpd/web/images/reload.gif | Bin 0 -> 343 bytes src/nhttpd/web/images/remove.png | Bin 0 -> 1295 bytes src/nhttpd/web/images/rename.gif | Bin 0 -> 345 bytes src/nhttpd/web/images/smallwait.gif | Bin 0 -> 310 bytes src/nhttpd/web/images/snapshot.png | Bin 0 -> 656 bytes src/nhttpd/web/images/stop.png | Bin 0 -> 695 bytes src/nhttpd/web/images/streaminfo.png | Bin 0 -> 501 bytes src/nhttpd/web/images/timer.gif | Bin 0 -> 1267 bytes src/nhttpd/web/images/transcode.png | Bin 0 -> 813 bytes src/nhttpd/web/images/tux.gif | Bin 0 -> 1692 bytes src/nhttpd/web/images/unlock.gif | Bin 0 -> 333 bytes src/nhttpd/web/images/upload.gif | Bin 0 -> 338 bytes src/nhttpd/web/images/visible.gif | Bin 0 -> 1478 bytes src/nhttpd/web/images/vlc.gif | Bin 0 -> 581 bytes src/nhttpd/web/images/volumedown.png | Bin 0 -> 711 bytes src/nhttpd/web/images/volumemute.png | Bin 0 -> 474 bytes src/nhttpd/web/images/volumeunmute.png | Bin 0 -> 417 bytes src/nhttpd/web/images/volumeup.png | Bin 0 -> 684 bytes src/nhttpd/web/images/wait.gif | Bin 0 -> 878 bytes src/nhttpd/web/images/x_red.gif | Bin 0 -> 411 bytes src/nhttpd/web/images/y.png | Bin 0 -> 908 bytes src/nhttpd/web/index.html | 12 + src/nhttpd/web/robots.txt | 4 + src/nhttpd/web/scripts/Makefile.am | 8 + src/nhttpd/web/scripts/Y_Live.sh | 118 + src/nhttpd/web/scripts/Y_Plugins.sh | 70 + src/nhttpd/web/scripts/Y_Tools.sh | 579 + src/nhttpd/web/scripts/_Y_Globals.sh | 42 + src/nhttpd/web/scripts/_Y_Library.sh | 207 + src/nhttpd/web/scripts/api.sh | 53 + src/nhttpd/yconfig.h | 236 + src/nhttpd/yhttpd.cpp | 494 + src/nhttpd/yhttpd.h | 50 + src/nhttpd/yhttpd_core/AUTHORS | 4 + src/nhttpd/yhttpd_core/Makefile.am | 18 + src/nhttpd/yhttpd_core/README | 22 + src/nhttpd/yhttpd_core/helper.cpp | 280 + src/nhttpd/yhttpd_core/helper.h | 42 + src/nhttpd/yhttpd_core/yconnection.cpp | 147 + src/nhttpd/yhttpd_core/yconnection.h | 73 + src/nhttpd/yhttpd_core/yhook.cpp | 357 + src/nhttpd/yhttpd_core/yhook.h | 187 + src/nhttpd/yhttpd_core/ylogging.cpp | 94 + src/nhttpd/yhttpd_core/ylogging.h | 86 + src/nhttpd/yhttpd_core/yrequest.cpp | 601 ++ src/nhttpd/yhttpd_core/yrequest.h | 52 + src/nhttpd/yhttpd_core/yresponse.cpp | 251 + src/nhttpd/yhttpd_core/yresponse.h | 56 + src/nhttpd/yhttpd_core/ysocket.cpp | 504 + src/nhttpd/yhttpd_core/ysocket.h | 98 + src/nhttpd/yhttpd_core/ytypes_globals.h | 157 + src/nhttpd/yhttpd_core/ywebserver.cpp | 507 + src/nhttpd/yhttpd_core/ywebserver.h | 91 + src/nhttpd/yhttpd_mods/AUTHORS | 3 + src/nhttpd/yhttpd_mods/Makefile.am | 18 + src/nhttpd/yhttpd_mods/mod_auth.cpp | 108 + src/nhttpd/yhttpd_mods/mod_auth.h | 30 + src/nhttpd/yhttpd_mods/mod_cache.cpp | 257 + src/nhttpd/yhttpd_mods/mod_cache.h | 56 + src/nhttpd/yhttpd_mods/mod_sendfile.cpp | 214 + src/nhttpd/yhttpd_mods/mod_sendfile.h | 39 + src/nhttpd/yhttpd_mods/mod_testhook.cpp | 17 + src/nhttpd/yhttpd_mods/mod_testhook.h | 21 + src/nhttpd/yhttpd_mods/mod_weblog.cpp | 364 + src/nhttpd/yhttpd_mods/mod_weblog.h | 43 + src/nhttpd/yhttpd_mods/mod_yparser.cpp | 779 ++ src/nhttpd/yhttpd_mods/mod_yparser.h | 121 + src/plugin.h | 46 + src/sectionsd/FreesatTables.hpp | 3052 ++++++ src/sectionsd/Makefile.am | 14 + src/sectionsd/SIbouquets.hpp | 141 + src/sectionsd/SIevents.cpp | 581 + src/sectionsd/SIevents.hpp | 474 + src/sectionsd/SIlanguage.cpp | 210 + src/sectionsd/SIlanguage.hpp | 62 + src/sectionsd/SInetworks.hpp | 183 + src/sectionsd/SIsections.cpp | 1594 +++ src/sectionsd/SIsections.hpp | 983 ++ src/sectionsd/SIservices.hpp | 208 + src/sectionsd/SIutils.cpp | 300 + src/sectionsd/SIutils.hpp | 57 + src/sectionsd/bat.cpp | 76 + src/sectionsd/debug.cpp | 35 + src/sectionsd/debug.h | 40 + src/sectionsd/dmx.cpp | 861 ++ src/sectionsd/dmx.h | 108 + src/sectionsd/dmxapi.cpp | 121 + src/sectionsd/dmxapi.h | 45 + src/sectionsd/edvbstring.cpp | 611 ++ src/sectionsd/edvbstring.h | 8 + src/sectionsd/epg.cpp | 138 + src/sectionsd/epgMini.cpp | 86 + src/sectionsd/nit.cpp | 76 + src/sectionsd/sdt.cpp | 85 + src/sectionsd/sectionsd.cpp | 9309 +++++++++++++++++ src/system/Makefile.am | 22 + src/system/configure_network.cpp | 117 + src/system/configure_network.h | 58 + src/system/debug.cpp | 32 + src/system/debug.h | 40 + src/system/flashtool.cpp | 538 + src/system/flashtool.h | 131 + src/system/fsmounter.cpp | 384 + src/system/fsmounter.h | 110 + src/system/httptool.cpp | 128 + src/system/httptool.h | 59 + src/system/lastchannel.cpp | 150 + src/system/lastchannel.h | 58 + src/system/localize.cpp | 350 + src/system/localize.h | 67 + src/system/locals.h | 1357 +++ src/system/locals_intern.h | 1356 +++ src/system/ping-config.h | 118 + src/system/ping-linux.h | 67 + src/system/ping.c | 271 + src/system/ping.h | 86 + src/system/setting_helpers.cpp | 966 ++ src/system/setting_helpers.h | 306 + src/system/settings.cpp | 199 + src/system/settings.h | 535 + src/system/timer.cpp | 7 + src/system/timer.h | 7 + src/timerd/Makefile.am | 13 + src/timerd/debug.h | 35 + src/timerd/doku.html | 125 + src/timerd/timerd.cpp | 481 + src/timerd/timermanager.cpp | 1414 +++ src/timerd/timermanager.h | 252 + src/videosettings.h | 19 + src/zapit/Makefile.am | 3 + src/zapit/include/Makefile.am | 1 + src/zapit/include/linux/dvb/audio.h | 125 + src/zapit/include/linux/dvb/ca.h | 91 + src/zapit/include/linux/dvb/dmx.h | 182 + src/zapit/include/linux/dvb/frontend.h | 417 + src/zapit/include/linux/dvb/net.h | 41 + src/zapit/include/linux/dvb/osd.h | 111 + src/zapit/include/linux/dvb/version.h | 29 + src/zapit/include/linux/dvb/video.h | 199 + src/zapit/include/zapit/Makefile.am | 10 + src/zapit/include/zapit/bat.h | 27 + src/zapit/include/zapit/bouquets.h | 151 + src/zapit/include/zapit/cam.h | 40 + src/zapit/include/zapit/channel.h | 212 + src/zapit/include/zapit/ci.h | 97 + src/zapit/include/zapit/client/Makefile.am | 3 + src/zapit/include/zapit/client/msgtypes.h | 345 + src/zapit/include/zapit/client/zapitclient.h | 511 + src/zapit/include/zapit/client/zapittools.h | 36 + src/zapit/include/zapit/client/zapittypes.h | 174 + src/zapit/include/zapit/debug.h | 105 + src/zapit/include/zapit/descriptors.h | 104 + src/zapit/include/zapit/dvbstring.h | 58 + src/zapit/include/zapit/frontend_c.h | 181 + src/zapit/include/zapit/getservices.h | 89 + src/zapit/include/zapit/nit.h | 27 + src/zapit/include/zapit/pat.h | 29 + src/zapit/include/zapit/pmt.h | 32 + src/zapit/include/zapit/satconfig.h | 32 + src/zapit/include/zapit/scan.h | 21 + src/zapit/include/zapit/sdt.h | 33 + src/zapit/include/zapit/settings.h | 59 + src/zapit/include/zapit/transponder.h | 81 + src/zapit/include/zapit/types.h | 44 + src/zapit/include/zapit/zapit.h | 39 + src/zapit/lib/Makefile.am | 10 + src/zapit/lib/zapitclient.cpp | 1018 ++ src/zapit/lib/zapittools.cpp | 123 + src/zapit/src/Changes | 2 + src/zapit/src/Makefile.am | 36 + src/zapit/src/bat.cpp | 149 + src/zapit/src/bouquets.cpp | 684 ++ src/zapit/src/ca.h | 91 + src/zapit/src/cam.cpp | 71 + src/zapit/src/channel.cpp | 262 + src/zapit/src/ci.cpp | 195 + src/zapit/src/descriptors.cpp | 964 ++ src/zapit/src/dvbstring.cpp | 250 + src/zapit/src/frontend.cpp | 1550 +++ src/zapit/src/getservices.cpp | 618 ++ src/zapit/src/nit.cpp | 219 + src/zapit/src/pat.cpp | 122 + src/zapit/src/pmt.cpp | 584 ++ src/zapit/src/pzapit.cpp | 612 ++ src/zapit/src/scan.cpp | 632 ++ src/zapit/src/sdt.cpp | 442 + src/zapit/src/transponder.cpp | 140 + src/zapit/src/udpstreampes.cpp | 843 ++ src/zapit/src/udpstreampes.h | 51 + src/zapit/src/zapit.cpp | 2425 +++++ 876 files changed, 193775 insertions(+) create mode 100644 .svnignore create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 acinclude.m4 create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100755 cross-configure.cool create mode 100755 cross-configure.tuxbox create mode 100644 data/Makefile.am create mode 100644 data/fonts/12.pcf.gz create mode 100644 data/fonts/14B.pcf.gz create mode 100644 data/fonts/15B.pcf.gz create mode 100644 data/fonts/Makefile.am create mode 100644 data/fonts/README.pcf create mode 100644 data/fonts/md_khmurabi_10.ttf create mode 100644 data/fonts/micron.ttf create mode 100644 data/fonts/micron_bold.ttf create mode 100644 data/fonts/micron_italic.ttf create mode 100644 data/icons/0.raw create mode 100644 data/icons/1.raw create mode 100644 data/icons/16_9.raw create mode 100644 data/icons/16_9_gray.raw create mode 100644 data/icons/2.raw create mode 100644 data/icons/3.raw create mode 100644 data/icons/4.raw create mode 100644 data/icons/5.raw create mode 100644 data/icons/6.raw create mode 100644 data/icons/7.raw create mode 100644 data/icons/8.raw create mode 100644 data/icons/9.raw create mode 100644 data/icons/Makefile.am create mode 100644 data/icons/audio.raw create mode 100644 data/icons/blau.raw create mode 100755 data/icons/bookmarkmanager.raw create mode 100644 data/icons/border_lr.raw create mode 100644 data/icons/border_ul.raw create mode 100644 data/icons/ca.raw create mode 100644 data/icons/colors.raw create mode 100644 data/icons/dbox.raw create mode 100644 data/icons/dbox_small.raw create mode 100644 data/icons/dd.raw create mode 100644 data/icons/dd_avail.raw create mode 100644 data/icons/dd_gray.raw create mode 100644 data/icons/down.raw create mode 100644 data/icons/error.raw create mode 100644 data/icons/features.raw create mode 100644 data/icons/file.raw create mode 100644 data/icons/folder.raw create mode 100644 data/icons/fta.raw create mode 100644 data/icons/games.raw create mode 100644 data/icons/gelb.raw create mode 100644 data/icons/gruen.raw create mode 100644 data/icons/help.raw create mode 100644 data/icons/help_small.raw create mode 100644 data/icons/hidden.raw create mode 100644 data/icons/home.raw create mode 100644 data/icons/info.raw create mode 100644 data/icons/keybinding.raw create mode 100644 data/icons/language.raw create mode 100644 data/icons/lcd.raw create mode 100644 data/icons/left.raw create mode 100644 data/icons/lock.raw create mode 100644 data/icons/mainmenue.raw create mode 100644 data/icons/mounted.raw create mode 100644 data/icons/movie.raw create mode 100644 data/icons/movieplayer.raw create mode 100644 data/icons/mp3-0.jpg create mode 100644 data/icons/mp3-1.jpg create mode 100644 data/icons/mp3-2.jpg create mode 100644 data/icons/mp3-3.jpg create mode 100644 data/icons/mp3-4.jpg create mode 100644 data/icons/mp3-5.jpg create mode 100644 data/icons/mp3-6.jpg create mode 100644 data/icons/mp3-7.jpg create mode 100644 data/icons/mp3.jpg create mode 100644 data/icons/mp3.raw create mode 100644 data/icons/mute.raw create mode 100644 data/icons/mute_small.raw create mode 100644 data/icons/network.raw create mode 100644 data/icons/not_mounted.raw create mode 100644 data/icons/numericpad.raw create mode 100644 data/icons/ok.raw create mode 100644 data/icons/power.raw create mode 100644 data/icons/radar.pal create mode 100644 data/icons/radar0.raw create mode 100644 data/icons/radar1.raw create mode 100644 data/icons/radar2.raw create mode 100644 data/icons/radar3.raw create mode 100644 data/icons/radar4.raw create mode 100644 data/icons/radar5.raw create mode 100644 data/icons/radar6.raw create mode 100644 data/icons/radar7.raw create mode 100644 data/icons/radar8.raw create mode 100644 data/icons/radar9.raw create mode 100644 data/icons/radio.jpg create mode 100644 data/icons/radiomode.jpg create mode 100644 data/icons/radiomode.pal create mode 100644 data/icons/radiomode.raw create mode 100644 data/icons/recording.raw create mode 100644 data/icons/right.raw create mode 100644 data/icons/rot.raw create mode 100644 data/icons/scan.jpg create mode 100644 data/icons/scan.pal create mode 100644 data/icons/scan.raw create mode 100644 data/icons/settings.raw create mode 100644 data/icons/shell.raw create mode 100644 data/icons/shutdown.jpg create mode 100644 data/icons/shutdown.pal create mode 100644 data/icons/shutdown.raw create mode 100644 data/icons/softupdate.raw create mode 100644 data/icons/streaming.raw create mode 100644 data/icons/subt.raw create mode 100644 data/icons/subt_gray.raw create mode 100644 data/icons/timer.raw create mode 100644 data/icons/up.raw create mode 100644 data/icons/upnp.raw create mode 100644 data/icons/video.raw create mode 100644 data/icons/volume.raw create mode 100644 data/icons/volumebody.raw create mode 100644 data/icons/volumeslider2.raw create mode 100644 data/icons/volumeslider2alpha.raw create mode 100644 data/icons/volumeslider2blue.raw create mode 100644 data/icons/volumeslider2green.raw create mode 100644 data/icons/volumeslider2red.raw create mode 100644 data/icons/vtxt.raw create mode 100644 data/icons/vtxt_gray.raw create mode 100644 data/inetradio/radio-stations.xml create mode 100644 data/iso-codes/Makefile.am create mode 100644 data/iso-codes/iso-639.tab create mode 100644 data/lcd/Makefile.am create mode 100644 data/lcd/clock/Makefile.am create mode 100644 data/lcd/clock/d_a.bmp create mode 100644 data/lcd/clock/d_b.bmp create mode 100644 data/lcd/clock/d_c.bmp create mode 100644 data/lcd/clock/d_d.bmp create mode 100644 data/lcd/clock/d_e.bmp create mode 100644 data/lcd/clock/d_f.bmp create mode 100644 data/lcd/clock/d_g.bmp create mode 100644 data/lcd/clock/d_h.bmp create mode 100644 data/lcd/clock/d_i.bmp create mode 100644 data/lcd/clock/d_j.bmp create mode 100644 data/lcd/clock/m_a.bmp create mode 100644 data/lcd/clock/m_b.bmp create mode 100644 data/lcd/clock/m_c.bmp create mode 100644 data/lcd/clock/m_d.bmp create mode 100644 data/lcd/clock/m_e.bmp create mode 100644 data/lcd/clock/m_f.bmp create mode 100644 data/lcd/clock/m_g.bmp create mode 100644 data/lcd/clock/m_h.bmp create mode 100644 data/lcd/clock/m_i.bmp create mode 100644 data/lcd/clock/m_j.bmp create mode 100644 data/lcd/clock/m_k.bmp create mode 100644 data/lcd/clock/m_l.bmp create mode 100644 data/lcd/clock/t_a.bmp create mode 100644 data/lcd/clock/t_b.bmp create mode 100644 data/lcd/clock/t_c.bmp create mode 100644 data/lcd/clock/t_d.bmp create mode 100644 data/lcd/clock/t_e.bmp create mode 100644 data/lcd/clock/t_f.bmp create mode 100644 data/lcd/clock/t_g.bmp create mode 100644 data/lcd/clock/t_h.bmp create mode 100644 data/lcd/clock/t_i.bmp create mode 100644 data/lcd/clock/t_j.bmp create mode 100644 data/lcd/clock/w_a.bmp create mode 100644 data/lcd/clock/w_b.bmp create mode 100644 data/lcd/clock/w_c.bmp create mode 100644 data/lcd/clock/w_d.bmp create mode 100644 data/lcd/clock/w_e.bmp create mode 100644 data/lcd/clock/w_f.bmp create mode 100644 data/lcd/clock/w_g.bmp create mode 100644 data/lcd/icons/Makefile.am create mode 100644 data/lcd/icons/lcd.png create mode 100644 data/lcd/icons/lcd2.png create mode 100644 data/lcd/icons/lcd3.png create mode 100644 data/lcd/icons/power.png create mode 100644 data/lcd/icons/rclock.raw create mode 100644 data/lcd/icons/setup.png create mode 100644 data/locale/Makefile.am create mode 100644 data/locale/_readme.txt create mode 100644 data/locale/bayrisch.locale create mode 100644 data/locale/bosanski.locale create mode 100644 data/locale/ch-baslerdeutsch.locale create mode 100644 data/locale/ch-berndeutsch.locale create mode 100755 data/locale/check.locale.files create mode 100755 data/locale/create-locals-update.pl create mode 100755 data/locale/create.locals.h create mode 100755 data/locale/create.locals_intern.h create mode 100644 data/locale/deutsch.locale create mode 100644 data/locale/dutch.locale create mode 100644 data/locale/english.locale create mode 100644 data/locale/francais.locale create mode 100644 data/locale/french.locale create mode 100644 data/locale/hungarian.locale create mode 100644 data/locale/italiano.locale create mode 100644 data/locale/nederlands.locale create mode 100644 data/locale/polski.locale create mode 100644 data/locale/portugues.locale create mode 100644 data/locale/russkij.locale create mode 100644 data/locale/suomi.locale create mode 100644 data/locale/svenska.locale create mode 100644 doc/ir.html create mode 100644 doc/picviewer.html create mode 100644 lib/Makefile.am create mode 100644 lib/connection/Makefile.am create mode 100644 lib/connection/basicclient.cpp create mode 100644 lib/connection/basicclient.h create mode 100644 lib/connection/basicmessage.h create mode 100644 lib/connection/basicserver.cpp create mode 100644 lib/connection/basicserver.h create mode 100644 lib/connection/basicsocket.cpp create mode 100644 lib/connection/basicsocket.h create mode 100644 lib/connection/messagetools.cpp create mode 100644 lib/connection/messagetools.h create mode 100644 lib/controldclient/Makefile.am create mode 100644 lib/controldclient/controldMsg.h create mode 100644 lib/controldclient/controldclient.cpp create mode 100644 lib/controldclient/controldclient.h create mode 100644 lib/controldclient/controldtypes.h create mode 100644 lib/libconfigfile/Makefile.am create mode 100644 lib/libconfigfile/configfile.cpp create mode 100644 lib/libconfigfile/configfile.h create mode 100644 lib/libcoolstream/.svnignore create mode 100644 lib/libcoolstream/audio_cs.h create mode 100644 lib/libcoolstream/cs_types.h create mode 100644 lib/libcoolstream/dmx_cs.h create mode 100644 lib/libcoolstream/dvb-ci.h create mode 100644 lib/libcoolstream/init_cs.h create mode 100644 lib/libcoolstream/playback_cs.h create mode 100644 lib/libcoolstream/pwrmngr.h create mode 100644 lib/libcoolstream/record_cs.h create mode 100644 lib/libcoolstream/video_cs.h create mode 100644 lib/libdvbsub/Debug.cpp create mode 100644 lib/libdvbsub/Debug.hpp create mode 100644 lib/libdvbsub/Makefile.am create mode 100644 lib/libdvbsub/PacketQueue.cpp create mode 100644 lib/libdvbsub/PacketQueue.hpp create mode 100644 lib/libdvbsub/dvbsub.cpp create mode 100644 lib/libdvbsub/dvbsub_thread.hpp create mode 100644 lib/libdvbsub/dvbsubtitle.cpp create mode 100644 lib/libdvbsub/dvbsubtitle.h create mode 100644 lib/libdvbsub/helpers.cpp create mode 100644 lib/libdvbsub/helpers.hpp create mode 100644 lib/libdvbsub/osd.cpp create mode 100644 lib/libdvbsub/osd.h create mode 100644 lib/libdvbsub/reader_thread.hpp create mode 100644 lib/libdvbsub/tools.cpp create mode 100644 lib/libdvbsub/tools.h create mode 100644 lib/libeventserver/Makefile.am create mode 100644 lib/libeventserver/eventserver.cpp create mode 100644 lib/libeventserver/eventserver.h create mode 100644 lib/libmd5sum/Makefile.am create mode 100644 lib/libmd5sum/getline.h create mode 100644 lib/libmd5sum/libmd5sum.c create mode 100644 lib/libmd5sum/libmd5sum.h create mode 100644 lib/libmd5sum/md5.c create mode 100644 lib/libmd5sum/md5.h create mode 100644 lib/libmd5sum/sys2.h create mode 100644 lib/libmd5sum/system.h create mode 100644 lib/libnet/Makefile.am create mode 100644 lib/libnet/libnet.c create mode 100644 lib/libnet/libnet.h create mode 100644 lib/libnet/network_interfaces.cpp create mode 100644 lib/libnet/network_interfaces.h create mode 100644 lib/libtuxtxt/Makefile.am create mode 100644 lib/libtuxtxt/libtuxtxt.cpp create mode 100644 lib/libtuxtxt/tuxtxt.cpp create mode 100644 lib/libtuxtxt/tuxtxt.h create mode 100644 lib/libtuxtxt/tuxtxt_common.h create mode 100644 lib/libtuxtxt/tuxtxt_def.h create mode 100644 lib/libupnpclient/Makefile.am create mode 100644 lib/libupnpclient/UPNPDevice.cpp create mode 100644 lib/libupnpclient/UPNPService.cpp create mode 100644 lib/libupnpclient/UPNPSocket.cpp create mode 100644 lib/libupnpclient/upnpclient.h create mode 100644 lib/sectionsdclient/Makefile.am create mode 100644 lib/sectionsdclient/sectionsdMsg.h create mode 100644 lib/sectionsdclient/sectionsdclient.cpp create mode 100644 lib/sectionsdclient/sectionsdclient.h create mode 100644 lib/sectionsdclient/sectionsdcontrol.cpp create mode 100644 lib/sectionsdclient/sectionsdtypes.h create mode 100644 lib/timerdclient/Makefile.am create mode 100644 lib/timerdclient/timerdclient.cpp create mode 100644 lib/timerdclient/timerdclient.h create mode 100644 lib/timerdclient/timerdmsg.h create mode 100644 lib/timerdclient/timerdtypes.h create mode 100644 lib/xmltree/Makefile.am create mode 100644 lib/xmltree/asciitab.h create mode 100644 lib/xmltree/hashtab.c create mode 100644 lib/xmltree/hashtab.h create mode 100644 lib/xmltree/iasctab.h create mode 100644 lib/xmltree/xmlinterface.cpp create mode 100644 lib/xmltree/xmlinterface.h create mode 100644 lib/xmltree/xmlparse.cpp create mode 100644 lib/xmltree/xmlparse.h create mode 100644 lib/xmltree/xmlrole.c create mode 100644 lib/xmltree/xmlrole.h create mode 100644 lib/xmltree/xmltimpl.c create mode 100644 lib/xmltree/xmltimpl.h create mode 100644 lib/xmltree/xmltok.c create mode 100644 lib/xmltree/xmltok.h create mode 100644 lib/xmltree/xmltok/latin1tab.h create mode 100644 lib/xmltree/xmltok/nametab.h create mode 100644 lib/xmltree/xmltok/utf8tab.h create mode 100644 lib/xmltree/xmltok/xmldef.h create mode 100644 lib/xmltree/xmltree.cpp create mode 100644 lib/xmltree/xmltree.h create mode 100644 src/Makefile.am create mode 100644 src/daemonc/Makefile.am create mode 100644 src/daemonc/remotecontrol.cpp create mode 100644 src/daemonc/remotecontrol.h create mode 100644 src/driver/Makefile.am create mode 100644 src/driver/audiodec/Makefile.am create mode 100644 src/driver/audiodec/basedec.cpp create mode 100644 src/driver/audiodec/basedec.h create mode 100644 src/driver/audiodec/cdrdec.cpp create mode 100644 src/driver/audiodec/cdrdec.h create mode 100644 src/driver/audiodec/crc.c create mode 100644 src/driver/audiodec/crc.h create mode 100644 src/driver/audiodec/fft.c create mode 100644 src/driver/audiodec/fft.h create mode 100644 src/driver/audiodec/int_fft.c create mode 100644 src/driver/audiodec/mp3dec.cpp create mode 100644 src/driver/audiodec/mp3dec.h create mode 100644 src/driver/audiodec/oggdec.cpp create mode 100644 src/driver/audiodec/oggdec.h create mode 100644 src/driver/audiodec/tag.c create mode 100644 src/driver/audiodec/tag.h create mode 100644 src/driver/audiodec/vis.cpp create mode 100644 src/driver/audiodec/wavdec.cpp create mode 100644 src/driver/audiodec/wavdec.h create mode 100644 src/driver/audiofile.cpp create mode 100644 src/driver/audiofile.h create mode 100644 src/driver/audiometadata.cpp create mode 100644 src/driver/audiometadata.h create mode 100644 src/driver/audioplay.cpp create mode 100644 src/driver/audioplay.h create mode 100644 src/driver/aviaext.cpp create mode 100644 src/driver/bigclock.cpp create mode 100644 src/driver/bigclock.h create mode 100644 src/driver/capture.cpp create mode 100644 src/driver/capture.h create mode 100644 src/driver/encoding.cpp create mode 100644 src/driver/encoding.h create mode 100644 src/driver/fb_window.cpp create mode 100644 src/driver/fb_window.h create mode 100644 src/driver/file.cpp create mode 100644 src/driver/file.h create mode 100644 src/driver/fontrenderer.cpp create mode 100644 src/driver/fontrenderer.h create mode 100644 src/driver/framebuffer.cpp create mode 100644 src/driver/framebuffer.h create mode 100644 src/driver/genpsi.c create mode 100644 src/driver/genpsi.h create mode 100644 src/driver/netfile.cpp create mode 100644 src/driver/netfile.h create mode 100644 src/driver/pictureviewer/Makefile.am create mode 100644 src/driver/pictureviewer/bmp.cpp create mode 100644 src/driver/pictureviewer/config.h create mode 100644 src/driver/pictureviewer/crw.cpp create mode 100644 src/driver/pictureviewer/fb_display.cpp create mode 100644 src/driver/pictureviewer/fb_display.h create mode 100644 src/driver/pictureviewer/gif.cpp create mode 100644 src/driver/pictureviewer/jpeg.cpp create mode 100644 src/driver/pictureviewer/make15color_errdiff.cpp create mode 100644 src/driver/pictureviewer/pictureviewer.cpp create mode 100644 src/driver/pictureviewer/pictureviewer.h create mode 100644 src/driver/pictureviewer/picv_client_server.h create mode 100644 src/driver/pictureviewer/png.cpp create mode 100644 src/driver/pictureviewer/resize.cpp create mode 100644 src/driver/pig.cpp create mode 100644 src/driver/pig.h create mode 100644 src/driver/rcinput.cpp create mode 100644 src/driver/rcinput.h create mode 100644 src/driver/rfmod.cpp create mode 100644 src/driver/rfmod.h create mode 100755 src/driver/ringbuffer.c create mode 100755 src/driver/ringbuffer.h create mode 100644 src/driver/screen_max.cpp create mode 100644 src/driver/screen_max.h create mode 100644 src/driver/showframe.c create mode 100644 src/driver/shutdown_count.cpp create mode 100644 src/driver/shutdown_count.h create mode 100644 src/driver/slotbuffer.c create mode 100644 src/driver/slotbuffer.h create mode 100644 src/driver/stream2file.cpp create mode 100644 src/driver/stream2file.h create mode 100644 src/driver/streamts.cpp create mode 100644 src/driver/vcrcontrol.cpp create mode 100644 src/driver/vcrcontrol.h create mode 100644 src/driver/vfd.cpp create mode 100644 src/driver/vfd.h create mode 100644 src/driver/wav.h create mode 100644 src/global.h create mode 100644 src/gui/Makefile.am create mode 100644 src/gui/alphasetup.cpp create mode 100644 src/gui/alphasetup.h create mode 100644 src/gui/audio_select.cpp create mode 100644 src/gui/audio_select.h create mode 100644 src/gui/audioplayer.cpp create mode 100644 src/gui/audioplayer.h create mode 100644 src/gui/bedit/Makefile.am create mode 100644 src/gui/bedit/bouqueteditor_bouquets.cpp create mode 100644 src/gui/bedit/bouqueteditor_bouquets.h create mode 100644 src/gui/bedit/bouqueteditor_channels.cpp create mode 100644 src/gui/bedit/bouqueteditor_channels.h create mode 100644 src/gui/bedit/bouqueteditor_chanselect.cpp create mode 100644 src/gui/bedit/bouqueteditor_chanselect.h create mode 100644 src/gui/bookmarkmanager.cpp create mode 100644 src/gui/bookmarkmanager.h create mode 100644 src/gui/bouquetlist.cpp create mode 100644 src/gui/bouquetlist.h create mode 100644 src/gui/cam_menu.cpp create mode 100644 src/gui/cam_menu.h create mode 100644 src/gui/ch_mosaic.cpp create mode 100644 src/gui/ch_mosaic.h create mode 100644 src/gui/channellist.cpp create mode 100644 src/gui/channellist.h create mode 100644 src/gui/color.cpp create mode 100644 src/gui/color.h create mode 100644 src/gui/customcolor.h create mode 100644 src/gui/dboxinfo.cpp create mode 100644 src/gui/dboxinfo.h create mode 100644 src/gui/dmx_tspidbandwidth.c create mode 100644 src/gui/epg_menu.cpp create mode 100644 src/gui/epg_menu.h create mode 100644 src/gui/epgplus.cpp create mode 100644 src/gui/epgplus.h create mode 100644 src/gui/epgview.cpp create mode 100644 src/gui/epgview.h create mode 100644 src/gui/eventlist.cpp create mode 100644 src/gui/eventlist.h create mode 100644 src/gui/favorites.cpp create mode 100644 src/gui/favorites.h create mode 100644 src/gui/filebrowser.cpp create mode 100644 src/gui/filebrowser.h create mode 100644 src/gui/hdd_menu.cpp create mode 100644 src/gui/hdd_menu.h create mode 100644 src/gui/imageinfo.cpp create mode 100644 src/gui/imageinfo.h create mode 100644 src/gui/infoclock.cpp create mode 100644 src/gui/infoclock.h create mode 100644 src/gui/infoviewer.cpp create mode 100644 src/gui/infoviewer.h create mode 100644 src/gui/motorcontrol.cpp create mode 100644 src/gui/motorcontrol.h create mode 100644 src/gui/moviebrowser.cpp create mode 100644 src/gui/moviebrowser.h create mode 100644 src/gui/movieinfo.cpp create mode 100644 src/gui/movieinfo.h create mode 100644 src/gui/movieplayer.cpp create mode 100644 src/gui/movieplayer.h create mode 100644 src/gui/nfs.cpp create mode 100644 src/gui/nfs.h create mode 100644 src/gui/pictureviewer.cpp create mode 100644 src/gui/pictureviewer.h create mode 100644 src/gui/pluginlist.cpp create mode 100644 src/gui/pluginlist.h create mode 100644 src/gui/plugins.cpp create mode 100644 src/gui/plugins.h create mode 100644 src/gui/rc_lock.cpp create mode 100644 src/gui/rc_lock.h create mode 100755 src/gui/ringbuffer.c create mode 100644 src/gui/scale.cpp create mode 100644 src/gui/scale.h create mode 100644 src/gui/scan.cpp create mode 100644 src/gui/scan.h create mode 100644 src/gui/screensetup.cpp create mode 100644 src/gui/screensetup.h create mode 100644 src/gui/sleeptimer.cpp create mode 100644 src/gui/sleeptimer.h create mode 100644 src/gui/streaminfo2.cpp create mode 100644 src/gui/streaminfo2.h create mode 100644 src/gui/timeosd.cpp create mode 100644 src/gui/timeosd.h create mode 100644 src/gui/timerlist.cpp create mode 100644 src/gui/timerlist.h create mode 100644 src/gui/update.cpp create mode 100644 src/gui/update.h create mode 100644 src/gui/upnpbrowser.cpp create mode 100644 src/gui/upnpbrowser.h create mode 100644 src/gui/widget/Makefile.am create mode 100644 src/gui/widget/buttons.cpp create mode 100644 src/gui/widget/buttons.h create mode 100644 src/gui/widget/colorchooser.cpp create mode 100644 src/gui/widget/colorchooser.h create mode 100644 src/gui/widget/drawable.cpp create mode 100644 src/gui/widget/drawable.h create mode 100644 src/gui/widget/helpbox.cpp create mode 100644 src/gui/widget/helpbox.h create mode 100644 src/gui/widget/hintbox.cpp create mode 100644 src/gui/widget/hintbox.h create mode 100644 src/gui/widget/hintboxext.cpp create mode 100644 src/gui/widget/hintboxext.h create mode 100644 src/gui/widget/icons.h create mode 100644 src/gui/widget/keychooser.cpp create mode 100644 src/gui/widget/keychooser.h create mode 100644 src/gui/widget/lcdcontroler.cpp create mode 100644 src/gui/widget/lcdcontroler.h create mode 100644 src/gui/widget/listbox.cpp create mode 100644 src/gui/widget/listbox.h create mode 100644 src/gui/widget/listbox_legacy.cpp create mode 100644 src/gui/widget/listbox_legacy.h create mode 100644 src/gui/widget/listframe.cpp create mode 100644 src/gui/widget/listframe.h create mode 100644 src/gui/widget/menue.cpp create mode 100644 src/gui/widget/menue.h create mode 100644 src/gui/widget/messagebox.cpp create mode 100644 src/gui/widget/messagebox.h create mode 100644 src/gui/widget/mountchooser.cpp create mode 100644 src/gui/widget/mountchooser.h create mode 100644 src/gui/widget/msgbox.cpp create mode 100644 src/gui/widget/msgbox.h create mode 100644 src/gui/widget/progressstatus.h create mode 100644 src/gui/widget/progresswindow.cpp create mode 100644 src/gui/widget/progresswindow.h create mode 100644 src/gui/widget/rgbcsynccontroler.cpp create mode 100644 src/gui/widget/rgbcsynccontroler.h create mode 100644 src/gui/widget/stringinput.cpp create mode 100644 src/gui/widget/stringinput.h create mode 100644 src/gui/widget/stringinput_ext.cpp create mode 100644 src/gui/widget/stringinput_ext.h create mode 100644 src/gui/widget/textbox.cpp create mode 100644 src/gui/widget/textbox.h create mode 100644 src/gui/widget/vfdcontroler.cpp create mode 100644 src/gui/widget/vfdcontroler.h create mode 100644 src/lcddisplay/lcddisplay.h create mode 100644 src/mymenu.h create mode 100644 src/neutrino.cpp create mode 100644 src/neutrino.h create mode 100644 src/neutrinoMessages.h create mode 100644 src/neutrino_menue.cpp create mode 100644 src/nhttpd/AUTHORS create mode 100644 src/nhttpd/COPYING create mode 100644 src/nhttpd/Makefile.am create mode 100644 src/nhttpd/README create mode 100644 src/nhttpd/doc/Changelog.txt create mode 100644 src/nhttpd/doc/design/Kollaborationsdiagramm1.gif create mode 100644 src/nhttpd/doc/design/Sequenzdiagramm1.gif create mode 100644 src/nhttpd/doc/design/Webserver.gif create mode 100644 src/nhttpd/doc/design/big_picturer.gif create mode 100644 src/nhttpd/doc/design/mods.gif create mode 100644 src/nhttpd/doc/design/nhttpd.gif create mode 100644 src/nhttpd/doc/design/nhttpdNeutrinoAPI.gif create mode 100644 src/nhttpd/doc/index.html create mode 100644 src/nhttpd/doc/nhttpd_controlapi.html create mode 100644 src/nhttpd/doc/nhttpd_implementation.html create mode 100644 src/nhttpd/doc/yhttpd.css create mode 100644 src/nhttpd/doc/yhttpd_conf.txt create mode 100644 src/nhttpd/doc/yhttpd_documentation.html create mode 100755 src/nhttpd/nhttpd create mode 100644 src/nhttpd/nhttpd.conf create mode 100644 src/nhttpd/tuxboxapi/AUTHORS create mode 100644 src/nhttpd/tuxboxapi/Makefile.am create mode 100644 src/nhttpd/tuxboxapi/controlapi.cpp create mode 100644 src/nhttpd/tuxboxapi/controlapi.h create mode 100755 src/nhttpd/tuxboxapi/lcdapi.cpp create mode 100755 src/nhttpd/tuxboxapi/lcdapi.h create mode 100644 src/nhttpd/tuxboxapi/neutrinoapi.cpp create mode 100644 src/nhttpd/tuxboxapi/neutrinoapi.h create mode 100644 src/nhttpd/tuxboxapi/neutrinoyparser.cpp create mode 100644 src/nhttpd/tuxboxapi/neutrinoyparser.h create mode 100644 src/nhttpd/web/AUTHORS create mode 100644 src/nhttpd/web/Makefile.am create mode 100644 src/nhttpd/web/Y_About.yhtm create mode 100644 src/nhttpd/web/Y_Baselib.js create mode 100644 src/nhttpd/web/Y_Blocks.txt create mode 100644 src/nhttpd/web/Y_Boxcontrol_Bouquet_Editor_Edit.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Bouquet_Editor_Main.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Bouquetlist.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Bouquets.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Channellist.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Boxcontrol_Messages.yhtm create mode 100644 src/nhttpd/web/Y_Dyn_Pages.yhtm create mode 100644 src/nhttpd/web/Y_EPG_Plus.yhtm create mode 100644 src/nhttpd/web/Y_Ext_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Ext_Settings.yhtm create mode 100644 src/nhttpd/web/Y_Ext_Update.yhtm create mode 100644 src/nhttpd/web/Y_Ext_Update_refresh.yhtm create mode 100644 src/nhttpd/web/Y_Info_Help.yhtm create mode 100644 src/nhttpd/web/Y_Info_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Info_Updates.yhtm create mode 100644 src/nhttpd/web/Y_Live.yhtm create mode 100644 src/nhttpd/web/Y_LiveViewFull.yhtm create mode 100644 src/nhttpd/web/Y_Live_DoubleView.yhtm create mode 100644 src/nhttpd/web/Y_Live_EPG.yhtm create mode 100644 src/nhttpd/web/Y_Live_EPG_Info.yhtm create mode 100644 src/nhttpd/web/Y_Live_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Live_Msg.yhtm create mode 100644 src/nhttpd/web/Y_Live_Record.yhtm create mode 100644 src/nhttpd/web/Y_Live_Transcode.yhtm create mode 100644 src/nhttpd/web/Y_Main.css create mode 100644 src/nhttpd/web/Y_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Settings.yhtm create mode 100644 src/nhttpd/web/Y_Settings_Backup.yhtm create mode 100644 src/nhttpd/web/Y_Settings_Live.yhtm create mode 100644 src/nhttpd/web/Y_Settings_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Settings_Skin.yhtm create mode 100644 src/nhttpd/web/Y_Settings_Timer.yhtm create mode 100644 src/nhttpd/web/Y_Settings_VNC.yhtm create mode 100644 src/nhttpd/web/Y_Settings_automount.yhtm create mode 100644 src/nhttpd/web/Y_Settings_automount_liste.yhtm create mode 100644 src/nhttpd/web/Y_Settings_mount.yhtm create mode 100644 src/nhttpd/web/Y_Settings_mount_liste.yhtm create mode 100644 src/nhttpd/web/Y_Settings_neutrino_forms.yhtm create mode 100644 src/nhttpd/web/Y_Settings_nhttpd.yhtm create mode 100644 src/nhttpd/web/Y_Settings_ucodes.yhtm create mode 100644 src/nhttpd/web/Y_Settings_umount_liste.yhtm create mode 100644 src/nhttpd/web/Y_Settings_wol.yhtm create mode 100644 src/nhttpd/web/Y_Settings_yWeb.yhtm create mode 100644 src/nhttpd/web/Y_Settings_zapit.yhtm create mode 100644 src/nhttpd/web/Y_StreamInfo.yhtm create mode 100644 src/nhttpd/web/Y_Timer_Edit.yhtm create mode 100644 src/nhttpd/web/Y_Timer_List.yhtm create mode 100644 src/nhttpd/web/Y_Timer_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Bootlogo.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Boxcontrol.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Check_Install.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Cmd.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Flash_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Flash_Upload.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Info_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Installer.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Menue.yhtm create mode 100644 src/nhttpd/web/Y_Tools_NetStream.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Rcsim.yhtm create mode 100644 src/nhttpd/web/Y_Tools_Timer_Sync.yhtm create mode 100644 src/nhttpd/web/Y_Tools_fbshot.yhtm create mode 100644 src/nhttpd/web/Y_Tools_remote_osd.yhtm create mode 100644 src/nhttpd/web/Y_VLC.js create mode 100644 src/nhttpd/web/Y_Version.txt create mode 100644 src/nhttpd/web/Y_blank.htm create mode 100644 src/nhttpd/web/Y_neutrino_Blocks.txt create mode 100644 src/nhttpd/web/channels.js create mode 100644 src/nhttpd/web/channels.txt create mode 100644 src/nhttpd/web/extentions.txt create mode 100644 src/nhttpd/web/favicon.ico create mode 100644 src/nhttpd/web/images/Makefile.am create mode 100755 src/nhttpd/web/images/addfolder.gif create mode 100644 src/nhttpd/web/images/arrowdown.gif create mode 100644 src/nhttpd/web/images/arrowup.gif create mode 100644 src/nhttpd/web/images/audiobar.gif create mode 100644 src/nhttpd/web/images/blank.gif create mode 100644 src/nhttpd/web/images/body-trans-l.gif create mode 100644 src/nhttpd/web/images/body-trans-r.gif create mode 100644 src/nhttpd/web/images/check_green.gif create mode 100755 src/nhttpd/web/images/chmod.gif create mode 100755 src/nhttpd/web/images/delete.gif create mode 100755 src/nhttpd/web/images/download.gif create mode 100644 src/nhttpd/web/images/elist.gif create mode 100755 src/nhttpd/web/images/epg.png create mode 100644 src/nhttpd/web/images/excl_yellow.gif create mode 100755 src/nhttpd/web/images/ftype_file.gif create mode 100755 src/nhttpd/web/images/ftype_folder.gif create mode 100755 src/nhttpd/web/images/ftype_link.gif create mode 100755 src/nhttpd/web/images/fullscreen.png create mode 100644 src/nhttpd/web/images/head-blue-l.gif create mode 100644 src/nhttpd/web/images/head-blue-r.gif create mode 100644 src/nhttpd/web/images/head-orange-l.gif create mode 100644 src/nhttpd/web/images/head-orange-r.gif create mode 100644 src/nhttpd/web/images/head-trans-l.gif create mode 100644 src/nhttpd/web/images/head-trans-r.gif create mode 100644 src/nhttpd/web/images/help.gif create mode 100644 src/nhttpd/web/images/help_white.gif create mode 100644 src/nhttpd/web/images/hidden.gif create mode 100755 src/nhttpd/web/images/home-dir.gif create mode 100644 src/nhttpd/web/images/hourglas.gif create mode 100755 src/nhttpd/web/images/live.gif create mode 100755 src/nhttpd/web/images/live.png create mode 100755 src/nhttpd/web/images/live_popup.gif create mode 100755 src/nhttpd/web/images/live_popup.png create mode 100755 src/nhttpd/web/images/livelock.png create mode 100755 src/nhttpd/web/images/liveunlock.png create mode 100644 src/nhttpd/web/images/lock.gif create mode 100644 src/nhttpd/web/images/modify.png create mode 100644 src/nhttpd/web/images/new.gif create mode 100644 src/nhttpd/web/images/ok.gif create mode 100755 src/nhttpd/web/images/pause.png create mode 100755 src/nhttpd/web/images/play.png create mode 100755 src/nhttpd/web/images/properties.gif create mode 100644 src/nhttpd/web/images/rc.gif create mode 100644 src/nhttpd/web/images/rc_sagem.jpg create mode 100644 src/nhttpd/web/images/rcd.jpg create mode 100644 src/nhttpd/web/images/record.gif create mode 100644 src/nhttpd/web/images/reload.gif create mode 100644 src/nhttpd/web/images/remove.png create mode 100755 src/nhttpd/web/images/rename.gif create mode 100755 src/nhttpd/web/images/smallwait.gif create mode 100755 src/nhttpd/web/images/snapshot.png create mode 100755 src/nhttpd/web/images/stop.png create mode 100644 src/nhttpd/web/images/streaminfo.png create mode 100644 src/nhttpd/web/images/timer.gif create mode 100755 src/nhttpd/web/images/transcode.png create mode 100644 src/nhttpd/web/images/tux.gif create mode 100644 src/nhttpd/web/images/unlock.gif create mode 100755 src/nhttpd/web/images/upload.gif create mode 100644 src/nhttpd/web/images/visible.gif create mode 100755 src/nhttpd/web/images/vlc.gif create mode 100755 src/nhttpd/web/images/volumedown.png create mode 100755 src/nhttpd/web/images/volumemute.png create mode 100755 src/nhttpd/web/images/volumeunmute.png create mode 100755 src/nhttpd/web/images/volumeup.png create mode 100644 src/nhttpd/web/images/wait.gif create mode 100644 src/nhttpd/web/images/x_red.gif create mode 100644 src/nhttpd/web/images/y.png create mode 100644 src/nhttpd/web/index.html create mode 100644 src/nhttpd/web/robots.txt create mode 100644 src/nhttpd/web/scripts/Makefile.am create mode 100755 src/nhttpd/web/scripts/Y_Live.sh create mode 100755 src/nhttpd/web/scripts/Y_Plugins.sh create mode 100755 src/nhttpd/web/scripts/Y_Tools.sh create mode 100755 src/nhttpd/web/scripts/_Y_Globals.sh create mode 100755 src/nhttpd/web/scripts/_Y_Library.sh create mode 100755 src/nhttpd/web/scripts/api.sh create mode 100644 src/nhttpd/yconfig.h create mode 100644 src/nhttpd/yhttpd.cpp create mode 100644 src/nhttpd/yhttpd.h create mode 100644 src/nhttpd/yhttpd_core/AUTHORS create mode 100644 src/nhttpd/yhttpd_core/Makefile.am create mode 100644 src/nhttpd/yhttpd_core/README create mode 100644 src/nhttpd/yhttpd_core/helper.cpp create mode 100644 src/nhttpd/yhttpd_core/helper.h create mode 100644 src/nhttpd/yhttpd_core/yconnection.cpp create mode 100644 src/nhttpd/yhttpd_core/yconnection.h create mode 100644 src/nhttpd/yhttpd_core/yhook.cpp create mode 100644 src/nhttpd/yhttpd_core/yhook.h create mode 100644 src/nhttpd/yhttpd_core/ylogging.cpp create mode 100644 src/nhttpd/yhttpd_core/ylogging.h create mode 100644 src/nhttpd/yhttpd_core/yrequest.cpp create mode 100644 src/nhttpd/yhttpd_core/yrequest.h create mode 100644 src/nhttpd/yhttpd_core/yresponse.cpp create mode 100644 src/nhttpd/yhttpd_core/yresponse.h create mode 100644 src/nhttpd/yhttpd_core/ysocket.cpp create mode 100644 src/nhttpd/yhttpd_core/ysocket.h create mode 100644 src/nhttpd/yhttpd_core/ytypes_globals.h create mode 100644 src/nhttpd/yhttpd_core/ywebserver.cpp create mode 100644 src/nhttpd/yhttpd_core/ywebserver.h create mode 100644 src/nhttpd/yhttpd_mods/AUTHORS create mode 100644 src/nhttpd/yhttpd_mods/Makefile.am create mode 100644 src/nhttpd/yhttpd_mods/mod_auth.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_auth.h create mode 100644 src/nhttpd/yhttpd_mods/mod_cache.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_cache.h create mode 100644 src/nhttpd/yhttpd_mods/mod_sendfile.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_sendfile.h create mode 100644 src/nhttpd/yhttpd_mods/mod_testhook.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_testhook.h create mode 100644 src/nhttpd/yhttpd_mods/mod_weblog.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_weblog.h create mode 100644 src/nhttpd/yhttpd_mods/mod_yparser.cpp create mode 100644 src/nhttpd/yhttpd_mods/mod_yparser.h create mode 100644 src/plugin.h create mode 100644 src/sectionsd/FreesatTables.hpp create mode 100644 src/sectionsd/Makefile.am create mode 100644 src/sectionsd/SIbouquets.hpp create mode 100644 src/sectionsd/SIevents.cpp create mode 100644 src/sectionsd/SIevents.hpp create mode 100644 src/sectionsd/SIlanguage.cpp create mode 100644 src/sectionsd/SIlanguage.hpp create mode 100644 src/sectionsd/SInetworks.hpp create mode 100644 src/sectionsd/SIsections.cpp create mode 100644 src/sectionsd/SIsections.hpp create mode 100644 src/sectionsd/SIservices.hpp create mode 100644 src/sectionsd/SIutils.cpp create mode 100644 src/sectionsd/SIutils.hpp create mode 100644 src/sectionsd/bat.cpp create mode 100644 src/sectionsd/debug.cpp create mode 100644 src/sectionsd/debug.h create mode 100644 src/sectionsd/dmx.cpp create mode 100644 src/sectionsd/dmx.h create mode 100644 src/sectionsd/dmxapi.cpp create mode 100644 src/sectionsd/dmxapi.h create mode 100644 src/sectionsd/edvbstring.cpp create mode 100644 src/sectionsd/edvbstring.h create mode 100644 src/sectionsd/epg.cpp create mode 100644 src/sectionsd/epgMini.cpp create mode 100644 src/sectionsd/nit.cpp create mode 100644 src/sectionsd/sdt.cpp create mode 100644 src/sectionsd/sectionsd.cpp create mode 100644 src/system/Makefile.am create mode 100644 src/system/configure_network.cpp create mode 100644 src/system/configure_network.h create mode 100644 src/system/debug.cpp create mode 100644 src/system/debug.h create mode 100644 src/system/flashtool.cpp create mode 100644 src/system/flashtool.h create mode 100644 src/system/fsmounter.cpp create mode 100644 src/system/fsmounter.h create mode 100644 src/system/httptool.cpp create mode 100644 src/system/httptool.h create mode 100644 src/system/lastchannel.cpp create mode 100644 src/system/lastchannel.h create mode 100644 src/system/localize.cpp create mode 100644 src/system/localize.h create mode 100644 src/system/locals.h create mode 100644 src/system/locals_intern.h create mode 100644 src/system/ping-config.h create mode 100644 src/system/ping-linux.h create mode 100644 src/system/ping.c create mode 100644 src/system/ping.h create mode 100644 src/system/setting_helpers.cpp create mode 100644 src/system/setting_helpers.h create mode 100644 src/system/settings.cpp create mode 100644 src/system/settings.h create mode 100644 src/system/timer.cpp create mode 100644 src/system/timer.h create mode 100644 src/timerd/Makefile.am create mode 100644 src/timerd/debug.h create mode 100644 src/timerd/doku.html create mode 100644 src/timerd/timerd.cpp create mode 100644 src/timerd/timermanager.cpp create mode 100644 src/timerd/timermanager.h create mode 100644 src/videosettings.h create mode 100644 src/zapit/Makefile.am create mode 100644 src/zapit/include/Makefile.am create mode 100644 src/zapit/include/linux/dvb/audio.h create mode 100644 src/zapit/include/linux/dvb/ca.h create mode 100644 src/zapit/include/linux/dvb/dmx.h create mode 100644 src/zapit/include/linux/dvb/frontend.h create mode 100644 src/zapit/include/linux/dvb/net.h create mode 100644 src/zapit/include/linux/dvb/osd.h create mode 100644 src/zapit/include/linux/dvb/version.h create mode 100644 src/zapit/include/linux/dvb/video.h create mode 100644 src/zapit/include/zapit/Makefile.am create mode 100644 src/zapit/include/zapit/bat.h create mode 100644 src/zapit/include/zapit/bouquets.h create mode 100644 src/zapit/include/zapit/cam.h create mode 100644 src/zapit/include/zapit/channel.h create mode 100644 src/zapit/include/zapit/ci.h create mode 100644 src/zapit/include/zapit/client/Makefile.am create mode 100644 src/zapit/include/zapit/client/msgtypes.h create mode 100644 src/zapit/include/zapit/client/zapitclient.h create mode 100644 src/zapit/include/zapit/client/zapittools.h create mode 100644 src/zapit/include/zapit/client/zapittypes.h create mode 100644 src/zapit/include/zapit/debug.h create mode 100644 src/zapit/include/zapit/descriptors.h create mode 100644 src/zapit/include/zapit/dvbstring.h create mode 100644 src/zapit/include/zapit/frontend_c.h create mode 100644 src/zapit/include/zapit/getservices.h create mode 100644 src/zapit/include/zapit/nit.h create mode 100644 src/zapit/include/zapit/pat.h create mode 100644 src/zapit/include/zapit/pmt.h create mode 100644 src/zapit/include/zapit/satconfig.h create mode 100644 src/zapit/include/zapit/scan.h create mode 100644 src/zapit/include/zapit/sdt.h create mode 100644 src/zapit/include/zapit/settings.h create mode 100644 src/zapit/include/zapit/transponder.h create mode 100644 src/zapit/include/zapit/types.h create mode 100644 src/zapit/include/zapit/zapit.h create mode 100644 src/zapit/lib/Makefile.am create mode 100644 src/zapit/lib/zapitclient.cpp create mode 100644 src/zapit/lib/zapittools.cpp create mode 100644 src/zapit/src/Changes create mode 100644 src/zapit/src/Makefile.am create mode 100644 src/zapit/src/bat.cpp create mode 100644 src/zapit/src/bouquets.cpp create mode 100644 src/zapit/src/ca.h create mode 100644 src/zapit/src/cam.cpp create mode 100644 src/zapit/src/channel.cpp create mode 100644 src/zapit/src/ci.cpp create mode 100644 src/zapit/src/descriptors.cpp create mode 100644 src/zapit/src/dvbstring.cpp create mode 100644 src/zapit/src/frontend.cpp create mode 100644 src/zapit/src/getservices.cpp create mode 100644 src/zapit/src/nit.cpp create mode 100644 src/zapit/src/pat.cpp create mode 100644 src/zapit/src/pmt.cpp create mode 100644 src/zapit/src/pzapit.cpp create mode 100644 src/zapit/src/scan.cpp create mode 100644 src/zapit/src/sdt.cpp create mode 100644 src/zapit/src/transponder.cpp create mode 100644 src/zapit/src/udpstreampes.cpp create mode 100644 src/zapit/src/udpstreampes.h create mode 100644 src/zapit/src/zapit.cpp diff --git a/.svnignore b/.svnignore new file mode 100644 index 000000000..e44c6013e --- /dev/null +++ b/.svnignore @@ -0,0 +1,12 @@ +autom4te.cache +*.in +aclocal.m4 +config.* +configure +depcomp +install-sh +*.pc +libtool +ltmain.sh +missing +stamp.h1 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..2af2daa33 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,31 @@ +In order of appearance in cvs: +------------------------------------------------ +"Funboard" +Steffen Hehn +Philipp Leusmann +Thomas Waldmann +Georg Lukas +Peter Feldbaumer +Rainer Scherg +M. Langer +Andreas Monzner +Matthias Kramer +Bastian Blank +Andreas Oberritter +Henning Heinold +Christian Scheele +Frank Bormann +Darius Hulboj +Dirk Szymanski +Michael Lantzen +Fakili Maho +Wolfram Joost +Alexander Wild +Ralf Gandy +Sven Traenkle +Florian Schirmer +Mike Bretz + +Also many thanks to everyone who supported development +without having cvs write access + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..1356a4690 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = gnu + +SUBDIRS = lib src data diff --git a/NEWS b/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 000000000..19409caae --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,406 @@ +AC_DEFUN([TUXBOX_APPS],[ +AM_CONFIG_HEADER(config.h) +AM_MAINTAINER_MODE + +AC_GNU_SOURCE +AC_SYS_LARGEFILE + +AC_ARG_WITH(target, + [ --with-target=TARGET target for compilation [[native,cdk]]], + [TARGET="$withval"],[TARGET="native"]) + +AC_ARG_WITH(targetprefix, + [ --with-targetprefix=PATH prefix relative to target root (only applicable in cdk mode)], + [targetprefix="$withval"],[targetprefix="NONE"]) + +AC_ARG_WITH(debug, + [ --without-debug disable debugging code], + [DEBUG="$withval"],[DEBUG="yes"]) + +if test "$DEBUG" = "yes"; then + DEBUG_CFLAGS="-g3 -ggdb" + AC_DEFINE(DEBUG,1,[Enable debug messages]) +fi + +AC_MSG_CHECKING(target) + +if test "$TARGET" = "native"; then + AC_MSG_RESULT(native) + + if test "$CFLAGS" = "" -a "$CXXFLAGS" = ""; then + CFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS" + CXXFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS" + fi + if test "$prefix" = "NONE"; then + prefix=/usr/local + fi + targetprefix=$prefix +elif test "$TARGET" = "cdk"; then + AC_MSG_RESULT(cdk) + + if test "$CC" = "" -a "$CXX" = ""; then + CC=powerpc-tuxbox-linux-gnu-gcc CXX=powerpc-tuxbox-linux-gnu-g++ + fi + if test "$CFLAGS" = "" -a "$CXXFLAGS" = ""; then + CFLAGS="-Wall -Os -mcpu=823 -pipe $DEBUG_CFLAGS" + CXXFLAGS="-Wall -Os -mcpu=823 -pipe $DEBUG_CFLAGS" + fi + if test "$prefix" = "NONE"; then + AC_MSG_ERROR(invalid prefix, you need to specify one in cdk mode) + fi + if test "$targetprefix" = "NONE"; then + targetprefix="" + fi + if test "$host_alias" = ""; then + cross_compiling=yes + host_alias=powerpc-tuxbox-linux-gnu + fi +else + AC_MSG_RESULT(none) + AC_MSG_ERROR([invalid target $TARGET, choose on from native,cdk]); +fi + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + +check_path () { + return $(perl -e "if(\"$1\"=~m#^/usr/(local/)?bin#){print \"0\"}else{print \"1\";}") +} + +]) + +AC_DEFUN([TUXBOX_APPS_DIRECTORY_ONE],[ +AC_ARG_WITH($1,[ $6$7 [[PREFIX$4$5]]],[ + _$2=$withval + if test "$TARGET" = "cdk"; then + $2=`eval echo "${targetprefix}$withval"` + else + $2=$withval + fi + TARGET_$2=${$2} +],[ + $2="\${$3}$5" + if test "$TARGET" = "cdk"; then + _$2=`eval echo "${target$3}$5"` + else + _$2=`eval echo "${$3}$5"` + fi + TARGET_$2=$_$2 +]) + +dnl automake <= 1.6 don't support this +dnl AC_SUBST($2) +AC_DEFINE_UNQUOTED($2,"$_$2",$7) +AC_SUBST(TARGET_$2) +]) + +AC_DEFUN([TUXBOX_APPS_DIRECTORY],[ +AC_REQUIRE([TUXBOX_APPS]) + +if test "$TARGET" = "cdk"; then + datadir="\${prefix}/share" + sysconfdir="\${prefix}/etc" + localstatedir="\${prefix}/var" + libdir="\${prefix}/lib" + targetdatadir="\${targetprefix}/share" + targetsysconfdir="\${targetprefix}/etc" + targetlocalstatedir="\${targetprefix}/var" + targetlibdir="\${targetprefix}/lib" +fi + +TUXBOX_APPS_DIRECTORY_ONE(configdir,CONFIGDIR,localstatedir,/var,/tuxbox/config, + [--with-configdir=PATH ],[where to find the config files]) + +TUXBOX_APPS_DIRECTORY_ONE(datadir,DATADIR,datadir,/share,/tuxbox, + [--with-datadir=PATH ],[where to find data]) + +TUXBOX_APPS_DIRECTORY_ONE(fontdir,FONTDIR,datadir,/share,/fonts, + [--with-fontdir=PATH ],[where to find the fonts]) + +TUXBOX_APPS_DIRECTORY_ONE(gamesdir,GAMESDIR,localstatedir,/var,/tuxbox/games, + [--with-gamesdir=PATH ],[where games data is stored]) + +TUXBOX_APPS_DIRECTORY_ONE(libdir,LIBDIR,libdir,/lib,/tuxbox, + [--with-libdir=PATH ],[where to find the internal libs]) + +TUXBOX_APPS_DIRECTORY_ONE(plugindir,PLUGINDIR,libdir,/lib,/tuxbox/plugins, + [--with-plugindir=PATH ],[where to find the plugins]) + +TUXBOX_APPS_DIRECTORY_ONE(ucodedir,UCODEDIR,localstatedir,/var,/tuxbox/ucodes, + [--with-ucodedir=PATH ],[where to find the ucodes]) + +TUXBOX_APPS_DIRECTORY_ONE(themesdir,THEMESDIR,datadir,/share,/tuxbox/neutrino/themes, + [--with-themesdir=PATH ],[where to find the themes (don't change)]) +]) + +dnl automake <= 1.6 needs this specifications +AC_SUBST(CONFIGDIR) +AC_SUBST(DATADIR) +AC_SUBST(FONTDIR) +AC_SUBST(GAMESDIR) +AC_SUBST(LIBDIR) +AC_SUBST(PLUGINDIR) +AC_SUBST(UCODEDIR) +AC_SUBST(THEMESDIR) +dnl end workaround + +AC_DEFUN([TUXBOX_APPS_ENDIAN],[ +AC_CHECK_HEADERS(endian.h) +AC_C_BIGENDIAN +]) + +AC_DEFUN([TUXBOX_APPS_DRIVER],[ +AC_ARG_WITH(driver, + [ --with-driver=PATH path for driver sources [[NONE]]], + [DRIVER="$withval"],[DRIVER=""]) + +if test -d "$DRIVER/include"; then + AC_DEFINE(HAVE_DBOX2_DRIVER,1,[Define to 1 if you have the dbox2 driver sources]) +else + AC_MSG_ERROR([can't find driver sources]) +fi + +AC_SUBST(DRIVER) + +CPPFLAGS="$CPPFLAGS -I$DRIVER/include" +]) + +AC_DEFUN([TUXBOX_APPS_DVB],[ +AC_ARG_WITH(dvbincludes, + [ --with-dvbincludes=PATH path for dvb includes [[NONE]]], + [DVBINCLUDES="$withval"],[DVBINCLUDES=""]) + +if test "$DVBINCLUDES"; then + CPPFLAGS="$CPPFLAGS -I$DVBINCLUDES" +fi + +AC_CHECK_HEADERS(ost/dmx.h,[ + DVB_API_VERSION=1 + AC_MSG_NOTICE([found dvb version 1]) +]) + +if test -z "$DVB_API_VERSION"; then +AC_CHECK_HEADERS(linux/dvb/version.h,[ + AC_LANG_PREPROC_REQUIRE() + AC_REQUIRE([AC_PROG_EGREP]) + AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +#include +version DVB_API_VERSION + ]])]) + DVB_API_VERSION=`(eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | $EGREP "^version" | sed "s,version\ ,,"` + rm -f conftest* + + AC_MSG_NOTICE([found dvb version $DVB_API_VERSION]) +]) +fi + +if test "$DVB_API_VERSION"; then + AC_DEFINE(HAVE_DVB,1,[Define to 1 if you have the dvb includes]) + AC_DEFINE_UNQUOTED(HAVE_DVB_API_VERSION,$DVB_API_VERSION,[Define to the version of the dvb api]) +else + AC_MSG_ERROR([can't find dvb headers]) +fi +]) + +AC_DEFUN([TUXBOX_APPS_CAPTURE],[ +AC_CHECK_HEADER(linux/dvb/avia/avia_gt_capture.h,[ + AC_DEFINE(HAVE_OLD_CAPTURE_API,1,[Define this if you want to use the old dbox2 capture API]) + AC_MSG_NOTICE([using old demux capture API])],[ + AC_MSG_NOTICE([using v4l2 capture API]) + ]) +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_CONFIG],[ +AC_PATH_PROG($1_CONFIG,$2,no) +if test "$$1_CONFIG" != "no"; then + if test "$TARGET" = "cdk" && check_path "$$1_CONFIG"; then + AC_MSG_$3([could not find a suitable version of $2]); + else + if test "$1" = "CURL"; then + $1_CFLAGS=$($$1_CONFIG --cflags) + $1_LIBS=$($$1_CONFIG --libs) + else + if test "$1" = "FREETYPE"; then + $1_CFLAGS=$($$1_CONFIG --cflags) + $1_LIBS=$($$1_CONFIG --libs) + else + $1_CFLAGS=$($$1_CONFIG --prefix=$targetprefix --cflags) + $1_LIBS=$($$1_CONFIG --prefix=$targetprefix --libs) + fi + fi + fi +fi + +AC_SUBST($1_CFLAGS) +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG],[ +_TUXBOX_APPS_LIB_CONFIG($1,$2,ERROR) +if test "$$1_CONFIG" = "no"; then + AC_MSG_ERROR([could not find $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_CHECK],[ +_TUXBOX_APPS_LIB_CONFIG($1,$2,WARN) +]) + +AC_DEFUN([TUXBOX_APPS_PKGCONFIG],[ +AC_PATH_PROG(PKG_CONFIG, pkg-config,no) +if test "$PKG_CONFIG" = "no" ; then + AC_MSG_ERROR([could not find pkg-config]); +fi +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_PKGCONFIG],[ +AC_REQUIRE([TUXBOX_APPS_PKGCONFIG]) +AC_MSG_CHECKING(for package $2) +if PKG_CONFIG_PATH="${prefix}/lib/pkgconfig" $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + $1_CFLAGS=$(PKG_CONFIG_PATH="${prefix}/lib/pkgconfig" $PKG_CONFIG --cflags "$2") + $1_LIBS=$(PKG_CONFIG_PATH="${prefix}/lib/pkgconfig" $PKG_CONFIG --libs "$2") +else + AC_MSG_RESULT(no) +fi + +AC_SUBST($1_CFLAGS) +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG],[ +_TUXBOX_APPS_LIB_PKGCONFIG($1,$2) +if test -z "$$1_CFLAGS" ; then + AC_MSG_ERROR([could not find package $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG_CHECK],[ +_TUXBOX_APPS_LIB_PKGCONFIG($1,$2) +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_SYMBOL],[ +AC_CHECK_LIB($2,$3,HAVE_$1="yes",HAVE_$1="no") +if test "$HAVE_$1" = "yes"; then + $1_LIBS=-l$2 +fi + +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_SYMBOL],[ +_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,ERROR) +if test "$HAVE_$1" = "no"; then + AC_MSG_ERROR([could not find $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_SYMBOL],[ +_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN) +]) + +AC_DEFUN([TUXBOX_APPS_GETTEXT],[ +AC_PATH_PROG(MSGFMT, msgfmt, no) +AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) +AC_PATH_PROG(XGETTEXT, xgettext, no) +AC_PATH_PROG(MSGMERGE, msgmerge, no) + +AC_MSG_CHECKING([whether NLS is requested]) +AC_ARG_ENABLE(nls, + [ --disable-nls do not use Native Language Support], + USE_NLS=$enableval, USE_NLS=yes) +AC_MSG_RESULT($USE_NLS) +AC_SUBST(USE_NLS) + +if test "$USE_NLS" = "yes"; then + AC_CACHE_CHECK([for GNU gettext in libc], gt_cv_func_gnugettext_libc,[ + AC_TRY_LINK([ + #include + #ifndef __GNU_GETTEXT_SUPPORTED_REVISION + #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) + #endif + extern int _nl_msg_cat_cntr; + extern int *_nl_domain_bindings; + ],[ + bindtextdomain ("", ""); + return (int) gettext ("") + _nl_msg_cat_cntr + *_nl_domain_bindings; + ], gt_cv_func_gnugettext_libc=yes, gt_cv_func_gnugettext_libc=no + )] + ) + + if test "$gt_cv_func_gnugettext_libc" = "yes"; then + AC_DEFINE(ENABLE_NLS, 1, [Define to 1 if translation of program messages to the user's native language is requested.]) + gt_use_preinstalled_gnugettext=yes + else + USE_NLS=no + fi +fi + +if test -f "$srcdir/po/LINGUAS"; then + ALL_LINGUAS=$(sed -e "/^#/d" "$srcdir/po/LINGUAS") +fi + +POFILES= +GMOFILES= +UPDATEPOFILES= +DUMMYPOFILES= +for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" +done +INST_LINGUAS= +if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test -n "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done +fi +CATALOGS= +if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done +fi +AC_SUBST(POFILES) +AC_SUBST(GMOFILES) +AC_SUBST(UPDATEPOFILES) +AC_SUBST(DUMMYPOFILES) +AC_SUBST(CATALOGS) +]) + +dnl backward compatiblity +AC_DEFUN([AC_GNU_SOURCE], +[AH_VERBATIM([_GNU_SOURCE], +[/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif])dnl +AC_BEFORE([$0], [AC_COMPILE_IFELSE])dnl +AC_BEFORE([$0], [AC_RUN_IFELSE])dnl +AC_DEFINE([_GNU_SOURCE]) +]) + +AC_DEFUN([AC_PROG_EGREP], +[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], + [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi]) + EGREP=$ac_cv_prog_egrep + AC_SUBST([EGREP]) +]) + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 000000000..2240566a3 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +package="tuxbox-neutrino" + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +cd "$srcdir" +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +(libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have libtool installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +#(gettext --version) < /dev/null > /dev/null 2>&1 || { +# echo +# echo "You must have gettext installed to compile $package." +# echo "Download the appropriate package for your system," +# echo "or get the source from one of the GNU ftp sites" +# echo "listed in http://www.gnu.org/order/ftp.html" +# DIE=1 +#} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if [ ! -e acinclude.m4 ]; then + for i in .. ../.. ../../..; do + if [ -e `pwd`/$i/acinclude.m4 ]; then + ln -s `pwd`/$i/acinclude.m4 . + fi + done +fi + +echo "Generating configuration files for $package, please wait...." + +echo " aclocal $ACLOCAL_FLAGS" +aclocal $ACLOCAL_FLAGS +echo " libtoolize --automake" +libtoolize --automake +#echo " gettextize" +#gettextize +echo " autoconf" +autoconf +echo " autoheader" +autoheader +echo " automake --add-missing" +automake --add-missing diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000..77f3b8538 --- /dev/null +++ b/configure.ac @@ -0,0 +1,111 @@ +AC_INIT(tuxbox-neutrino,1.0.1) +AM_INIT_AUTOMAKE(tuxbox-neutrino,1.0.1) + +TUXBOX_APPS +TUXBOX_APPS_DIRECTORY + +AC_PROG_CC +AC_PROG_CXX +AC_DISABLE_STATIC +AM_PROG_LIBTOOL + +TUXBOX_APPS_DVB +#TUXBOX_APPS_DRIVER +TUXBOX_APPS_LIB_CONFIG(CURL,curl-config) +TUXBOX_APPS_LIB_CONFIG(FREETYPE,freetype-config) +TUXBOX_APPS_LIB_PKGCONFIG(OPENSSL,openssl) +TUXBOX_APPS_LIB_PKGCONFIG(ID3TAG,libid3tag) +TUXBOX_APPS_LIB_PKGCONFIG(MAD,libmad) +#TUXBOX_APPS_LIB_PKGCONFIG(VORBISIDEC,tremor) +TUXBOX_APPS_LIB_PKGCONFIG(PNG,libpng) +TUXBOX_APPS_LIB_PKGCONFIG(AVFORMAT,libavformat) +TUXBOX_APPS_LIB_PKGCONFIG(AVCODEC,libavcodec) +TUXBOX_APPS_LIB_PKGCONFIG(AVUTIL,libavutil) +#TUXBOX_APPS_LIB_PKGCONFIG(CONFIGFILE,tuxbox-configfile) +#TUXBOX_APPS_LIB_PKGCONFIG(CONNECTION,tuxbox-connection) +#TUXBOX_APPS_LIB_PKGCONFIG(EVENTSERVER,tuxbox-eventserver) +#TUXBOX_APPS_LIB_PKGCONFIG(LCDDISPLAY,tuxbox-lcddisplay) +#TUXBOX_APPS_LIB_PKGCONFIG(LIRCDCLIENT,tuxbox-lircdclient) +#TUXBOX_APPS_LIB_PKGCONFIG(NET,tuxbox-net) +#TUXBOX_APPS_LIB_PKGCONFIG(PLUGINS,tuxbox-plugins) +#TUXBOX_APPS_LIB_PKGCONFIG(TUXBOX,tuxbox) +#TUXBOX_APPS_LIB_PKGCONFIG(UCODES,tuxbox-ucodes) +#TUXBOX_APPS_LIB_PKGCONFIG(MPEGTOOLS,tuxbox-mpegtools) +#TUXBOX_APPS_LIB_PKGCONFIG(TUXTXT,tuxbox-tuxtxt) + +AC_ARG_ENABLE(keyboard-no-rc, + [ --enable-keyboard-no-rc enable keyboard control, disable rc control], + [AC_DEFINE(KEYBOARD_INSTEAD_OF_REMOTE_CONTROL,1,[enable keyboard control, disable rc control])]) + +AC_ARG_ENABLE(restore-prev-mode, + [ --enable-restore-prev-mode enable return from graphics mode], + [AC_DEFINE(RETURN_FROM_GRAPHICS_MODE,1,[enable return from graphics mode])]) + +# +# Check for libtdservicedb - the new one - for testing only +# +#CSL_VERSION=0.0.1 +#FCSL_VERSION=`$PKG_CONFIG --modversion libcoolstream` +#AC_MSG_CHECKING(for package libcoolstream >= $CSL_VERSION) +#if $PKG_CONFIG --atleast-version $CSL_VERSION libcoolstream ; then + #AC_MSG_RESULT(found (version $FCSL_VERSION)) + #LIBCS_CFLAGS=`$PKG_CONFIG --cflags libcoolstream` + #LIBCS_LIBS=`$PKG_CONFIG --libs libcoolstream` +#else + #AC_MSG_ERROR([ +#*** libcoolstream $CSL_VERSION or newer is required! *** + #]) +#fi +AC_SUBST(LIBCS_CFLAGS) +AC_SUBST(LIBCS_LIBS) +AC_SUBST(FREETYPE_CFLAGS) +AC_SUBST(FREETYPE_LIBS) +AC_SUBST(VORBISIDEC_CFLAGS) +AC_SUBST(VORBISIDEC_LIBS) + +AC_OUTPUT([ +Makefile +lib/Makefile +lib/libconfigfile/Makefile +lib/connection/Makefile +lib/libeventserver/Makefile +lib/libmd5sum/Makefile +lib/libnet/Makefile +lib/xmltree/Makefile +lib/controldclient/Makefile +lib/sectionsdclient/Makefile +lib/timerdclient/Makefile +lib/libtuxtxt/Makefile +lib/libdvbsub/Makefile +lib/libupnpclient/Makefile +src/nhttpd/Makefile +src/nhttpd/web/Makefile +src/nhttpd/web/images/Makefile +src/nhttpd/web/scripts/Makefile +src/nhttpd/tuxboxapi/Makefile +src/nhttpd/yhttpd_core/Makefile +src/nhttpd/yhttpd_mods/Makefile +src/Makefile +src/daemonc/Makefile +src/driver/pictureviewer/Makefile +src/driver/audiodec/Makefile +src/driver/Makefile +src/gui/Makefile +src/gui/bedit/Makefile +src/gui/widget/Makefile +src/system/Makefile +data/Makefile +data/fonts/Makefile +data/icons/Makefile +data/iso-codes/Makefile +data/lcd/Makefile +data/lcd/icons/Makefile +data/lcd/clock/Makefile +data/locale/Makefile +src/sectionsd/Makefile +src/timerd/Makefile +src/zapit/Makefile +src/zapit/lib/Makefile +src/zapit/src/Makefile +]) + diff --git a/cross-configure.cool b/cross-configure.cool new file mode 100755 index 000000000..00d022f0f --- /dev/null +++ b/cross-configure.cool @@ -0,0 +1,32 @@ +#!/bin/sh + +PREFIX=/opt/newcross/arm-cx2450x-linux-gnueabi/arm-cx2450x-linux-gnueabi/sys-root/usr +CCPATH= +HOST=arm-cx2450x-linux-gnueabi + +export PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig +export CC=$CCPATH$HOST-gcc +export AR=$CCPATH$HOST-ar +export NM=$CCPATH$HOST-nm +export RANLIB=$CCPATH$HOST-ranlib +export OBJDUMP=$CCPATH$HOST-objdump +export STRIP=$CCPATH$HOST-strip + +export CFLAGS="-Wall -g0 -O2 \ + -D__KERNEL_STRICT_NAMES -DUSE_NEVIS_GXA \ + -I"$PWD"/include -I"$PWD"/include/linux/dvb -I"$PREFIX"/include/freetype2" + +export CXXFLAGS="-Wall -g0 -O2 \ + -D__KERNEL_STRICT_NAMES -DUSE_NEVIS_GXA \ + -I"$PWD"/include -I"$PWD"/include/linux/dvb -I"$PREFIX"/include/freetype2" + +export LDFLAGS="-L"$PREFIX"/lib -lcurl -lssl -lcrypto -ldl" + +export DVB_API_VERSION=3 +export FREETYPE_CONFIG=$PREFIX/bin/freetype-config +export CURL_CONFIG=$PREFIX/bin/curl-config +export PKG_CONFIG_PATH==$PREFIX/lib/pkgconfig + +./autogen.sh +./configure --prefix=${PREFIX} --build=i386-pc-linux-gnu --host=$HOST --without-driver --with-target=cdk --with-targetprefix="" "$*" + diff --git a/cross-configure.tuxbox b/cross-configure.tuxbox new file mode 100755 index 000000000..ec653ef7e --- /dev/null +++ b/cross-configure.tuxbox @@ -0,0 +1,25 @@ +#!/bin/sh + +PREFIX=$HOME/dbox2/cdkroot +CCPATH= +HOST=powerpc-tuxbox-linux-gnu + +export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig +export CC=$CCPATH$HOST-gcc +export AR=$CCPATH$HOST-ar +export NM=$CCPATH$HOST-nm +export RANLIB=$CCPATH$HOST-ranlib +export OBJDUMP=$CCPATH$HOST-objdump +export STRIP=$CCPATH$HOST-strip + +export CFLAGS="-std=gnu99 -Wall -O2 -g0 -DNDEBUG -I"$PREFIX"/include/freetype2 -I"$PWD"/include -I"$PWD"/include/linux/dvb -DHAVE_DBOX2" +export CXXFLAGS="-std=gnu++98 -Wall -O2 -g0 -DNDEBUG -I"$PREFIX"/include/freetype2 -I"$PWD"/include -I"$PWD"/include/linux/dvbooo -DHAVE_DBOX2" + +export LDFLAGS="-L"$PREFIX"/lib -lcurl -lssl -lcrypto -ldl" + +export DVB_API_VERSION=3 +export FREETYPE_CONFIG=$PREFIX/bin/freetype-config + +./autogen.sh +./configure --prefix=$PREFIX --build=i386-pc-linux-gnu --host=$HOST --without-driver --with-target=cdk --with-targetprefix="/" "$*" + diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 000000000..4fa783ab4 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = fonts icons iso-codes lcd locale diff --git a/data/fonts/12.pcf.gz b/data/fonts/12.pcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..161c87d77ff2713782597d07c27c259347d81cb1 GIT binary patch literal 4303 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 B!bkuB literal 0 HcmV?d00001 diff --git a/data/fonts/14B.pcf.gz b/data/fonts/14B.pcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..e3bd7cd7b24aff3f67b8407056a3bcf5b07050ad GIT binary patch literal 4468 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 SzIcBx@P7a%XU0NkQvd)ap^&2h literal 0 HcmV?d00001 diff --git a/data/fonts/Makefile.am b/data/fonts/Makefile.am new file mode 100644 index 000000000..c6ce3f4a4 --- /dev/null +++ b/data/fonts/Makefile.am @@ -0,0 +1,7 @@ +installdir = $(FONTDIR) + +install_DATA = \ + micron.ttf micron_bold.ttf micron_italic.ttf \ + 12.pcf.gz 14B.pcf.gz 15B.pcf.gz \ + md_khmurabi_10.ttf + diff --git a/data/fonts/README.pcf b/data/fonts/README.pcf new file mode 100644 index 000000000..1f4f51fd8 --- /dev/null +++ b/data/fonts/README.pcf @@ -0,0 +1,10 @@ +12.pcf,gz, 14B.pcf.gz and 15B.pcf.gz are +Bitmap fonts taken from X11. Original names are + +6x12-ISO8859-1.pcf.gz +7x14-ISO8859-1.pcf.gz +9x15-ISO8859-1.pcf.gz + +Fontnames inside the fonts have been patched to Fix12,Fix14,Fix15 +(original name Fixed), cause tuxbox lcd-fontrenderer does not support +2 files for the same font with diffrent pixel sizes. diff --git a/data/fonts/md_khmurabi_10.ttf b/data/fonts/md_khmurabi_10.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2e8af5fd8c750850cfc22a13bcd3c79a71d5fa58 GIT binary patch literal 247668 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 literal 0 HcmV?d00001 diff --git a/data/fonts/micron.ttf b/data/fonts/micron.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7696532c5470b0faf842a2f416b4f02aaea3ab7b GIT binary patch literal 28768 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 Nm^=l}dK5YnEU>|yP-*Lv5x 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 E7qI($6aWAK literal 0 HcmV?d00001 diff --git a/data/icons/0.raw b/data/icons/0.raw new file mode 100644 index 0000000000000000000000000000000000000000..4c7cad03c23397cb1a13be271428ce0c7c763db1 GIT binary patch literal 293 zcmb1OkYFGhbN~TFF$aj*(a`|nL6~3}7!%0h0I~o7{{bW$KWo1xrxE f<^XDjnF(UU?1DQ7#X%rlA?3~@C`lo$X2mF#T^ literal 0 HcmV?d00001 diff --git a/data/icons/1.raw b/data/icons/1.raw new file mode 100644 index 0000000000000000000000000000000000000000..97d379fdd90e062f66c5d881714e44ec290eb369 GIT binary patch literal 293 zcmb1OkYFGhbN~TFF$aj*(a`|nL6~3}7!%0h0J8i4{|AXQAhSUNKnXBK37Z3K4A=sg b`5;3Z;LbsD5XhY%9YD|kVnbXFVG$+)h23OV literal 0 HcmV?d00001 diff --git a/data/icons/16_9.raw b/data/icons/16_9.raw new file mode 100644 index 0000000000000000000000000000000000000000..1dfd0a709a9ab8efd8bc1e919ddbe3dae10289ab GIT binary patch literal 213 zcmb1Q5MW^7-i-ho5KIt@0RhlC+#9$-(z`b>0A+S>*v$<_P!3QW$OmzN6i^w017$&E XKpX~`GVTosWk^QjbOEwUQM>>E+xS$` literal 0 HcmV?d00001 diff --git a/data/icons/16_9_gray.raw b/data/icons/16_9_gray.raw new file mode 100644 index 0000000000000000000000000000000000000000..cef4c4882544780f93c2cf0574e35f58eebf14ea GIT binary patch literal 213 zcmZ{e!3w}I2t#wsvFCnmj(&FI){!!(2?ngglp#E#J*XY@ks(RH+AafI;r9x8E`-%|bL&c8Rfh5RHJ{of9pd lWDce|x%9y!ZqsvEMqLUv_#Zz{-f>=mn3FC@$(cIoUKbUxXodg) literal 0 HcmV?d00001 diff --git a/data/icons/3.raw b/data/icons/3.raw new file mode 100644 index 0000000000000000000000000000000000000000..41e7b42ecd77d864f5b9babd112c84b5e4d955c2 GIT binary patch literal 293 zcma)$F%G~W3R09jGPXLY&CG_4Zo<$!x?3XC<7S2T>R}GjBXz noMas6B9}JK?OU^7Pt6)S*gtm`)9WkFD-d(i1vy!%Mdy0}!6Rqa literal 0 HcmV?d00001 diff --git a/data/icons/4.raw b/data/icons/4.raw new file mode 100644 index 0000000000000000000000000000000000000000..ed31f741e76b22aa9993fbee50fbb47ea8804ccf GIT binary patch literal 293 zcma)$F&4le2tyq`g2!cK}!kF1|d=IPNnqyUwo=dUAuE*&6PR9)!DTf&c&j literal 0 HcmV?d00001 diff --git a/data/icons/5.raw b/data/icons/5.raw new file mode 100644 index 0000000000000000000000000000000000000000..c8208aafc8bcba911846a7676e95362a4bc0c285 GIT binary patch literal 293 zcma)$F%keF2m>v7g3qO-^SgFjBEo2OrqG2%C%yIdEQ2B}L^I_SxST``0v_%Ym!6E- k2De+tBxb&xGTvg-)1W@T_7C)HqGRGX(GAPYLG*eEA@v{(vfE~i4oiRfX i_a%Q9A#u1{I+sNlN{;(w(Ou_Ph&>}wowGH!H~IhvL1pa# literal 0 HcmV?d00001 diff --git a/data/icons/8.raw b/data/icons/8.raw new file mode 100644 index 0000000000000000000000000000000000000000..544f7cf02933caae80cbea409e9ebbc0d18801c1 GIT binary patch literal 293 zcma)$F%G~W3W=@z$)%s?0qMg~PmHtK#kjiO8 literal 0 HcmV?d00001 diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am new file mode 100644 index 000000000..da8d65ed0 --- /dev/null +++ b/data/icons/Makefile.am @@ -0,0 +1,17 @@ +installdir = $(DATADIR)/neutrino/icons + +install_DATA = \ + 16_9.raw 16_9_gray.raw audio.raw blau.raw border_lr.raw border_ul.raw \ + colors.raw dbox.raw dd.raw dd_avail.raw dd_gray.raw error.raw features.raw \ + file.raw folder.raw games.raw gelb.raw gruen.raw help.raw help_small.raw \ + home.raw info.raw keybinding.raw language.raw lcd.raw lock.raw mainmenue.raw \ + mounted.raw mp3.raw mute.raw network.raw not_mounted.raw numericpad.raw ok.raw power.raw radar.pal \ + radar0.raw radar1.raw radar2.raw radar3.raw radar4.raw radar5.raw radar6.raw \ + radar7.raw radar8.raw radar9.raw radiomode.pal radiomode.raw recording.raw \ + rot.raw scan.pal scan.raw settings.raw shell.raw shutdown.pal shutdown.raw softupdate.raw \ + streaming.raw timer.raw video.raw volume.raw volumebody.raw volumeslider2.raw \ + volumeslider2alpha.raw volumeslider2blue.raw volumeslider2green.raw \ + volumeslider2red.raw vtxt.raw vtxt_gray.raw movie.raw movieplayer.raw \ + hidden.raw bookmarkmanager.raw mute_small.raw dbox_small.raw \ + 0.raw 1.raw 2.raw 3.raw 4.raw 5.raw 6.raw 7.raw 8.raw 9.raw \ + up.raw right.raw down.raw left.raw fta.raw ca.raw diff --git a/data/icons/audio.raw b/data/icons/audio.raw new file mode 100644 index 0000000000000000000000000000000000000000..f90b8c1c6837edef0f20a69c9ddc51b29aa9b269 GIT binary patch literal 293 zcmaKnAsWLV429#0*|)E*6Pz0|*f>J4FmqEz=l17kQ(66+&EqWfIQ??OF-Hxy e$>#5BaOvJ%4X%-=KSs{cxbkDV3-i7G8}I{5n9SM$ literal 0 HcmV?d00001 diff --git a/data/icons/blau.raw b/data/icons/blau.raw new file mode 100644 index 0000000000000000000000000000000000000000..6514d89c29bdf156767092a4c34af5d433e2f92e GIT binary patch literal 133 scmWe&5MaOt&YS^@o&f?Ng9k((K+qsANCFql1Cl^e4^{{@5KN&G07&vI!2kdN literal 0 HcmV?d00001 diff --git a/data/icons/bookmarkmanager.raw b/data/icons/bookmarkmanager.raw new file mode 100755 index 0000000000000000000000000000000000000000..788c83e406c86823ed184e6928afc4d203e7d88c GIT binary patch literal 293 zcmYL_u@S>C3LlxdK(R(VOXl8AEfW1LWV#-DEZK#_hL=;cP&YoY&J(~ zgfeKE>?3fj!wt$|tj3>;*AHU@ q6H~smd$OQrs{lLgyFjLALDZ~*I|hItH0^%C|IQqE3uLYN{F58>*1S&u literal 0 HcmV?d00001 diff --git a/data/icons/border_lr.raw b/data/icons/border_lr.raw new file mode 100644 index 0000000000000000000000000000000000000000..6124b20a83b253bf8c0a70f3677b1427985a202d GIT binary patch literal 4613 zcmdUyF>>QD3`N;x*6|^81a-QGT6L;bsozEi@SU;%L6G9LCzOi$Ca;MEaRv0m)3E;(JF7aBjL+CMFcRUdJE&lx2SM8*;url$oauSfKGPF1ZnXa)kUTevm&{x%#0i zCP;btj;MaTp`2&=rtb`8yx3754ZS9h$9vEJjRuQaq2u+?&mZusT}wUQ-p|W!SM@Xh zd6eH|eqNei>-cNlV$<%K{}t!?dtMqk$z5~#o)<&HqaF1#+5WF$sPdEJ_4$DOGM*=B zXV3X{(dU)JmB)dgVmhc0cHcK1Wxly#AlRI2aQGd5T^H|#Hg|p3Ag++{|P#GdE0*3Ud)0nb-ER^JtHyvPHO1(4rF9(X(h1t#CE zeBe*&X!0g3_;UVjQ-ybK60Ze>_wrF+dn&whpY+eOMS~A^lKy-LgAekVmjcX3`OHfJ z=0n=-f3}qQXs6I0hKNsg3Vs;6i%)hceKth;2l;Hj(&qrEf0WO5N_`4|`iaL`*cxA6 zect35PaA}f_E#8D9d2Nw8LIje82ki&#fgf1j~nn7XPyQy^02{(!^gpgxHK?{UjmN- zK;QE#s*oRe`UE9Uc7*o@Y98eW9}HDI+Y#OydQ9HWhVT623zM_;tH1a0yImW--r3LB zt6il3^!8i+0xuHI&qMP&|6btpzcicnTKV7odH$K_hO&P5?JV`#(72CIUi@_1|D6pH z-@W}SzDk@Ap5HJ5^1XRGH~4*%c@=Qs7x#jpn4DGbp6sKa#E%UD|Ayc9EqZ~?*iO<9 zfiZr?V|(u6bAU>p?2!IpJ_jiLlYQ1d%4b7apY5~$(y)hiWuEWR;FF=uv;Dy*Lx~@D zgpUOhKjejv1tKrrx>K`K5 z0*;@}8TSSA!m^PaZ{&?1sHF)kuVFzH=fhmO!SD|EyjD=&BiqF{y{ZR1uWy$gvrk^VFU!T4J6foHk literal 0 HcmV?d00001 diff --git a/data/icons/ca.raw b/data/icons/ca.raw new file mode 100644 index 0000000000000000000000000000000000000000..72e6921df108009b0cd48391e534b79358333f9e GIT binary patch literal 197 zcmZ`yF%keE2x50>?Ef!t=X0Tz*N~j&fg(-}fE`dEi^?9gK^p1BT`C}hy*xZ;p10KU OhIBkb>ycC@|Jnj5Do@@3 literal 0 HcmV?d00001 diff --git a/data/icons/colors.raw b/data/icons/colors.raw new file mode 100644 index 0000000000000000000000000000000000000000..04032bf79fa4cd025974480acae7ffac8fbcba2b GIT binary patch literal 293 zcmZ{gF%Ezr3;?^~LR|SN9lO~t_yglx9QZ)LV0?m3Tw_QvU;a0J-HtD*ylh literal 0 HcmV?d00001 diff --git a/data/icons/dd.raw b/data/icons/dd.raw new file mode 100644 index 0000000000000000000000000000000000000000..d0da4717e49509d59a1cceb973e0578459fd5778 GIT binary patch literal 213 zcmb1Q5MW^7-VFi_U~&VPgmQp_AW5Li1_mI(0488GNQwa@0g+(nvawB> literal 0 HcmV?d00001 diff --git a/data/icons/dd_avail.raw b/data/icons/dd_avail.raw new file mode 100644 index 0000000000000000000000000000000000000000..e24eae9b249b0c37b622d55c86422aeddccc4a8f GIT binary patch literal 213 zcmb1Q5MW?nF9!hzFj)X5p&XzfND?ShzyKr|zyyp2Nil#VATkU<4vI)d#R~v9;4yIk literal 0 HcmV?d00001 diff --git a/data/icons/dd_gray.raw b/data/icons/dd_gray.raw new file mode 100644 index 0000000000000000000000000000000000000000..f900747891c6729bc0bca9a9f8a75956d594b7d4 GIT binary patch literal 213 zcmb`BF%AGA2m?DZI{R8ipKV*z=q|%yxkTt_KnxYkG*{qIk|sgG*j)+|Wx&Aqr|^8u Pjc4g~jxVKAg;^md7g#uZ literal 0 HcmV?d00001 diff --git a/data/icons/down.raw b/data/icons/down.raw new file mode 100644 index 0000000000000000000000000000000000000000..568ab860c8f3214d79922080933dec9c1931f723 GIT binary patch literal 293 zcma)$I}X4g3_}@n1df|A8`tgD`Xf{^5lcuf4vsv>9Bmck&q1t8PDX7uVu+tpydF9! j+bpa*uNP7p2{MC$hXrllx1Flvn`gJ(NS)o9?K}Da)EH&$ literal 0 HcmV?d00001 diff --git a/data/icons/error.raw b/data/icons/error.raw new file mode 100644 index 0000000000000000000000000000000000000000..c4d9dfcd3fad51820ba106d5d233069f52b797dd GIT binary patch literal 293 zcmb1OkYM1wiwFD%f?kOH{h2`U1H%3ZV$TOlfQ6wFU}1;^NcjJMAOX|?5(cw@Iw)lW ujRISMY)(H=2xiv}kOUBb9S4#C0Sds}2FI+8?VxnvWHh9u>sMQy4~`t?I^G0x^z86wKRX2y*KW;d8BGF3O6z QCx!1{x4T+$j2Yj#FW>vtjsO4v literal 0 HcmV?d00001 diff --git a/data/icons/file.raw b/data/icons/file.raw new file mode 100644 index 0000000000000000000000000000000000000000..4d4e911be8091ca2ff1db8015cdefb5e0fbbb8b1 GIT binary patch literal 133 zcmWe&5MbcF+W`W1@80eI|NsB@UMT$oNdM^Q{{WN+iU0o&qNAHc_)z$U?|2Kg6K+w?75CSJGAl|lJh)J#EAj00}5nO*`qc{BfYpw1!S<7hv&@mmRjDB Mj%R2+lFH;?TlX+JApigX literal 0 HcmV?d00001 diff --git a/data/icons/games.raw b/data/icons/games.raw new file mode 100644 index 0000000000000000000000000000000000000000..06202519c56fa2c972e2a5e2a90a51aca9647261 GIT binary patch literal 293 zcmZ9H!41G53GP5r%1G zYp9(mR9s?KFo6Bw`BcI-324b-d0N(WrNdN!< literal 0 HcmV?d00001 diff --git a/data/icons/gruen.raw b/data/icons/gruen.raw new file mode 100644 index 0000000000000000000000000000000000000000..d742246dcfefd724f0fb742e0c0c574e1ad0749f GIT binary patch literal 133 scmWe&5MaOtl$5}tNee*m>ZB`gtMf`eR3E!RrO<{)M^c1FEyL=)sWSzDm= k(FYj^5*t$<^LzZqYb$u$qyIObIG;ewsg0G{sX4vT19jM8z5oCK literal 0 HcmV?d00001 diff --git a/data/icons/help_small.raw b/data/icons/help_small.raw new file mode 100644 index 0000000000000000000000000000000000000000..4549a5d136a655b7f6484f36fcf1baf4f71d1bff GIT binary patch literal 167 zcmY+8u?@g55CXkG8c%&q3S2=iJf?7DMr``fac(2v=*;K%F_GW!rRDX3A??1^cQO& SZLE0<96kbeipJi<`N$v7UcyHJ literal 0 HcmV?d00001 diff --git a/data/icons/home.raw b/data/icons/home.raw new file mode 100644 index 0000000000000000000000000000000000000000..2e78492733f74d8b604d3852a56bb6f56dfc862c GIT binary patch literal 293 zcma)$ITFAi3~yRjdt|SQW?-m ifExL+NqQKL7H9J`CEnIQD?fE!g}mowiF3A2`rZq&ykv*~ literal 0 HcmV?d00001 diff --git a/data/icons/info.raw b/data/icons/info.raw new file mode 100644 index 0000000000000000000000000000000000000000..6827fa5efca0b68c27f68fe1e34ca9a19683bd1c GIT binary patch literal 293 zcmZXPyA8k~3`8l@L~a?#gRww%$p)-INy8i|XmaO(K?)Um@BE7)hO_Sds}2FI+8?VxnvWHh9u>sMQy4~`t?I^G0x^z86wKRX2y*KW;d8BGF3O6z QCx!1{x4T+$j2Yj#FW>vtjsO4v literal 0 HcmV?d00001 diff --git a/data/icons/keybinding.raw b/data/icons/keybinding.raw new file mode 100644 index 0000000000000000000000000000000000000000..8d38cd8dc5b1ecc10bb19ecc025bfcbe2aede2ba GIT binary patch literal 293 zcmZ`!IS#-e49gZ4cAl9nT`IA`TlyNtF8NDyfJ9Z*NZ15BfdU1x1jb}mhR}D-GU9%K zVd^ZY&zL#SaLejs$4y5x^4NQCjTILm5ZY3zFU?*iMy|N)U%&NpsGocguw@UOrf|J1 PT$2}VYqY;@{^COqnr?|d literal 0 HcmV?d00001 diff --git a/data/icons/language.raw b/data/icons/language.raw new file mode 100644 index 0000000000000000000000000000000000000000..d322ffd7ecb5633798a208b09d2695fce33142ab GIT binary patch literal 293 zcmYL_F%APE3 zpszwFPbKI9(4};#w$g1qF`T9M$pIMfHccU4oT4s><#EB5$=aTv=98-BeNJR`GbfYt z?4vxKix=2awML<<<(O({XD*YU5-=i7cEm1XBS!LwOrR9N%hLqP(0B6xQ~0ERNalUr L;s1O1twZDsBPov7g3q<2^Sjr1EQo`hsg@8lz4i7iHAPrJ)xi|#n?xG~{@fVw+>En= g!C81AOjG5WIk#e=d=t*UPMxa|Jw3HMM{C>{{TJ_MzW@LL literal 0 HcmV?d00001 diff --git a/data/icons/lock.raw b/data/icons/lock.raw new file mode 100644 index 0000000000000000000000000000000000000000..04b6e69949426e6386b99ae51bf814dbb01d97ad GIT binary patch literal 293 zcmb`Au?+w)48qv^K>ojg-On|xgp{RQS%L^MrP4ov16#oKd2}2J#o~v*4L25BhZ)Y~ KcPViuzupbL{53)V literal 0 HcmV?d00001 diff --git a/data/icons/mainmenue.raw b/data/icons/mainmenue.raw new file mode 100644 index 0000000000000000000000000000000000000000..9d913c7c9ae8ba1c4e911a2fa7aad292b577b008 GIT binary patch literal 293 zcmaiv!41G53*O;<-aS)G)^ zdlLlHBh$3<)5il1?@}5om7h-Gb=J2D9y&Zvd!3=9Au~mo|92P9bgEukC_{GjKXwhX F3@_ev?5h9( literal 0 HcmV?d00001 diff --git a/data/icons/mounted.raw b/data/icons/mounted.raw new file mode 100644 index 0000000000000000000000000000000000000000..81acf7670a1151edf5b433616a0acdf0d83c6e83 GIT binary patch literal 293 zcmbV{%L%|R3`4U(?^!*EEMU+;HfWdn6CVmalnNuiD6t`P%yBVf>19UUQ6b@4QO)a( o;c4ek$6fE?lQl{K^~Axf7=&}mq*+2pm%2MG4`2BCncDH+Z~A8Gy8r+H literal 0 HcmV?d00001 diff --git a/data/icons/movie.raw b/data/icons/movie.raw new file mode 100644 index 0000000000000000000000000000000000000000..ea1085a35aa974469369e86f7e7b3fa5e5ec35a1 GIT binary patch literal 133 zcmWe&5MbcFd$$7!dhg!7+yDRn{|YEw4-{x_`2U}Q;qF~~cA!kbUE?48{XhN}T(;j( z-{1egz`ptekosT1-Ti^TzoWn40y9t?q^`l5A0&VGF0%nh{oT6_3_yc{G{}4q-Chm^ IK$-yz07P+5i~s-t literal 0 HcmV?d00001 diff --git a/data/icons/movieplayer.raw b/data/icons/movieplayer.raw new file mode 100644 index 0000000000000000000000000000000000000000..c8788f5bf1c0347d32acf902c1ab6b8a262850a4 GIT binary patch literal 80657 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 literal 0 HcmV?d00001 diff --git a/data/icons/mp3-0.jpg b/data/icons/mp3-0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73364958a1d5cf6b040c16b7fa7dc61b047919fc GIT binary patch literal 165943 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⩔5J#B%1XEIK9B3Z(uZ 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%0A1itp*|kfu+(ih+>^59>ebtnIw#(>Q=J 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�c~7B|M~ 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 UvT%dOl|A?8IdX%z`2QCFA9Vb^ZU6uP literal 0 HcmV?d00001 diff --git a/data/icons/mp3-1.jpg b/data/icons/mp3-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e4826fde1d0ceb28a94196cfda5130b5ca0e1159 GIT binary patch literal 107179 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 VO|*GigM`cxH$jmEo;XE+|Jn9L<{JP2 literal 0 HcmV?d00001 diff --git a/data/icons/mp3-2.jpg b/data/icons/mp3-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..079fb16dfcb30f57bbfbb809b6e24728bbad2410 GIT binary patch literal 83130 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 A1^@s6 literal 0 HcmV?d00001 diff --git a/data/icons/mp3-3.jpg b/data/icons/mp3-3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..420aa232494ebfce527973a3f0049cd13d18ca29 GIT binary patch literal 323758 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%0D;-AyAI6Oo}iVIO|em(`vVpO>h>liSf!Yf1`* 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 alfA!VD${Z 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%0A)UfC#K;|Orq{9WjQs1ScCpesV@fFKDIuNK zzB;&sLei=g3`*RskAt`Ej$G4J@9@4jJsAASmpk(af((y5>^0C^)&l zYWq{3t&YwA0DNCrm>Hy(O!8y~#|}wyy_Tv}Dx6t}-}l5HOGO-UsdweQyWhv-1jr*2uzRp0 zkbC0KQDu^3mB~u=lyyw;M=)wxwWtiY0FVabxb27l5_p7jAP`LqZh??UH*1a9Y&ItL z{{Vb%y*-$HH>bLEbrMug(Y#AY_vwKk0i17O-*0Z%#bptk<2R(Ftj%Gf1PIcmt(&I` zi`w?N3O^Vb>Z%}+#?3vQAAnu{&RW4hNOP*eZYp$UoI$ zd*W@3f~9zc4LiYCBh)!>sEoz}rH{8szq;c0*johG`hz#C$?39siuvTr=^dV)nV;Dk zHzW?&6DZ57^BTCRX=5)m?h-NnrY69DoILga0HSlxMrMO8&nS&N?HH9oAcCiZafvq< zDt(qvp9@_XM^T`;Bb_>Rd%rfvV3VaGbyZ?6J?n96V}*2^k^2LWmDwdp0;niKQhSlO zzhm1D`XsbYIUrPFBnvL6?4I&8HTm0{c*P5Fbp{~Yat*J7v%JEiHq9zCDx`rXo%OQA z5Q;EuZ)%Nz$1=2{l4(~ksGgoN3$ip&^$CyzrAWB2Ct`bH41F1$GS_63EHXZqP*+nj zi;cOpjyWtwo+u_25J+W=xKv=pH`8Vvk9&BwD~(|iBTFfYCsSrs)NgJre>-0kZf)H6 z#v9RdRLdgXK5p_16{jY|eoyv}T zn^5HRZjshiP9qOIs*wPAxjc8=;bfIt^foz4={~_$CNyEx5Ja{E#@Mo!S4eA-szM2) zvDX&1l!gYt+uGcnUz~e_euesiFGtCnJT`@8W?2f@6a!|p?k|rR&2sjos*xNWVC*f( z$A86VJe0AD7!pb3-^MzCsaZ6u0B>W#_&A)3Ljplt_rK<*DGc=w`<`waD(ck`e_*T&r;cAwYc)ESQ5!Eozy99# zxgE&c6G|ePQ$2k{QhOF!I9?_{Q>m;bmJE%jS_w z4uq$Y`vf=KU?VkFA%+)_$f_kXAiqsen}Kq~_^`tar_r)8B{jFT}0 zx$FhI51%-{)7iWkPGHqjG8T`4$B0Q6rVWLB>az(DpztXllHuvKQ!!oNKT2$Rlk0bGiO$M4_nRRs3RT9et zaXEONClW>+6=7q@*!Vn*8JyK*Z$p))S(nw*)yEJ1`bnnLvq^us1%{Ds+u?a)v~D1Q z+BRg@Gp_2u9fJ+5ZgxBEfuv_PW(-BF3FGmDVr>jF%++%|&T7bLX|mLjReqk9>lg~4 zSYL8Yu5aHI$|z&YVtA@us@g&tAs?|)z=Nv$weASFY!69KHC;?})X>W;^0t`cMJ&vG z06E5qeG}?weKQxR*C?FjZ$HwdDDf9P_>L=UGhzHMLShHGM8#BZ1`CJK0=?xJ9u0k74H>%QHHv37Mgm zRJs)Qxl?2K!v+mS+Ygos70T+V#BnWTGW8>!ryw+9%3@Tu=KJ2`0>Kl>LoZHr(->JL zMqs_b8gIEf@G(?V(tR~oUYcnqeLV?QODcP=+J(K>8x6&=c2AepW_3wLK~D44FlJOj zE_8xVC%`}34QL(o3>Q&8;4D9=^YM?5AX|JFmF2aWom8+_W8k1!1Oir>~-~-l_&6cVm0s;Xi(|?YX-7_t+Tby8{k=KWpd z%EOlx(1Pcy(U|5<(w6VJ(Q|%*S4kR6zmTUg_F^Jr$4RajRFwEFbUt29yyy}$Q zt`sQWzyJ(IDqEohVi*^ttK1XZ4yQ zvVkR~3=Dwo8pof5^Mb3h{MRta=yIC6x0aTHS!tppX!RSLVO4HtS(wbT)>Y3_GR6^J zX$z35HUj(swkwU?4Ob*z?!SC=K_-=m<+nlflZb=rEEoZ64)^cziG>oeW^lK;Cfu9@ zdz;*w^L#U;r>2uDsHl>yQlchKoFs20?{YsIe|%j!5$yVoo=TZIFh?ZP5NxUo8x75X z$I6uA0?f_TEoA@;Y3@!rmeti!RSD@TWv7?|1d+K`7Zy8@gO0}q>CEg{E%b{E-1BTO z5^RJ{jVQQn`6T1oKS_N@=#fO(SE+JpHk3xo1xBBAx{vjn*jusfk1OFvJuzBzMA6>Wlz(++N^$!jol@)O*6V_HBq8Vl5yl2^-qP{9~^uFvf;ESo>HH>AwE} zpUx>%gu7wMEj)0@@iH?O&=9M8S7WFj@r~!{hpv+)`kcD1k{N01VlceD9;2W)HvpaY zJRE0W%6&Sy3{uqRs4HHb=PMB>d)Xi(PR)Cp-qylQz7MUfN^Yz$lBY|)%djVZov@!w z(Q$r?T(Px9(*V_OVfU`0Nzq~tacdDxkt*b1WQ#bH>&O z_s5%Mk}J(mPghj*l#ej#%NlBQQ?na#cI}24J7qCvk;hjg^W|$bnU!?A$9_QUNbQHo z1vSQAwk#hVuVAJ&mRRE#F6$wXJXo*qz4MqOQ8SyiwBDRkKG-y*QNb(p&Mi82WoON>fn{O%l~B z1`%A9Xy3CharnTLr^j857_L|MT z7bo+EQBy$l6SFj;P*BE!F0MDd&tu~sG>1vls~z?l#rN@s^mG-|(X4T-fu^v6GZ{T% zZY;g8-wQvg+oMN)0Lw;@Wl=mcxTtwpx`Aa2q*}-3=WJ0I^J*0Ys-@Rku{TrR+kb34 zt<2glN~TXNkbf?6%@aqaWp5kuFuOI(a`8M;JSp?EQ0nVcJa0}{+IOR)p?OEs-XWG02;J|v9QEz%(c0tM^>et zSl>>aWVP+Rt;ikmTu4P+y)`naI#NYbacz3Gkkx+A5K!$)>YI-izJLn-3Hg* z!(;gv)n=KEc&$ilsZu#;!oU+oog{F_$;Ej#S6!;6qF%F2Q4X~#bh#Jg;iF^|u0YkI zl2?#Ts#>RQhy#m%&LcM%$ymuYQkST-0tI@`x#~uC1M^CCwqc)Qu8pB_YZLjAV8pAVWp=W85p_oXqDs3VWvla)N zgR#W3<8f&(WwFUocc=4;?5i`1t_rBCXQ(X^OEWgdpPS!nV%luYYx6{dFwHfFn8+dN zxGG7zTm7+^%V(-SqNSdWV?yP^#?L0mPiDU#_Bb0f`lzd=Eh4*#7?)PoHD7YA;}LL+ zY@&32fUl*=>GF9cn@dIz?!#5K@sAJA^a@Ix$~uSX!zWHXRQ5>C?h3a7kBl>)JbJn@ zLNhp{hiHS!tfo`2I)%--=Z&$M^|URJk(Fh6;^qljfV#p5N;bW>Cxh}kVi+UwYEQ7U z(ft)}ezixUd|K_rm`GUt~F)HB)B?G(%7t{TQbUA}JfT{Ox_b zW5%6ZC(UK1o@+}KY$PxrXl?zfz@1!;@823+rjIbqDe3F#>SUgZs#wx7?t78D0eg$6 z7Pb<&H#GY)j?DI1np9=gQayS4x@x7=(K!sW_drP8Z*Dweed!Nb)8zh~ugkKkdW6ho zj1rP)vXHlvz_pIs-w8iiy)2ntd-YNEDYD9et`K7grHHu#)(gPKi_rOhrYZeAxt42{ z(>7J6NdCA?C7CsyDkKBA17ZdaN%E&!V<`5Fj$Kswkz|daS?SfP;9|vtsNBX@+qL)Y zwmoOiS#>9a@` zE?A9gB!Fs79_d2O-F?wTTw~;}3mQDrb%SJ9d2m`T@N4>OB7d#vzu#l4WzwYa&_8s7M_NW?ji;ZWh+J&G4=q<6QO?Tnb14fBf^{Idwmb?9^Dv--ik?{@(4bUAi6WZ@H|_q%8c$Jr##)S~ znpk2K(MnkzE={)o0KM_#`5qQ*xm2x|$2i6m2kKPx!c450g=D0V>LaF056#Z_$NH8p zPeU|o0I8Q!1`fqSp61rJ*TC78P>x0yEEw1^!1-QnO_o=!S)f|BP|AxTx{HDdAm7eC zX!!04Q*Drp?(7GnOD#-m0+n|n`uqO)++`k;lB?Yjj7U=30zkO~w*wrqoyeyb#1yyM@~EMm8Us}EG?D?L$3P$IZ-M?VhUBWnQGEo}&{aVMO;Hr6 zWsj&8mc%D!{2i@;S`ycNCr!r#j5@8$>FDK_Df5_WUM6UlqUKd0ZEer=p4Rb=Zbdhu z{{a4-W)arXNcrJ5h144Z+X2m}t5!D!XPU=K^%Wu*VvQ~}Bgf|i%&sc5Cbf4quxk^5*%g?Ae;{|kX*~+nEdof=NeV*NDuj>#@^?74nFmLv zk*1>e4p`paKN!9cVW>vwMJNXP9BNbWMh?qr>S0=_(8Cz2jKv@@i)p>~zZ`NfE7BQT z1c@~vC4L3)bI1~gDp3?l_h)H9I@~p^q~C#W1Dsb(n#o=5gDP2#JP;L9BU5W7h$P&G zKh=sTtqV*DF^=Q`f-?H^1xm=V$0$||QbBb#JP?sj^X6kmd~V(!P--w$$;S`bQg`u)i`f2A(JfQUJ^m^xEJZkM_k=BF7YJ z<(RM~)TprWW8WR7nPZO45ReOOcsP|@xfD-b8TZRW{{S@^8c`*TK~Af7C$ShC)RMJ7 zr_DGDF5r#$IMJ*!#O*Xp(3Kjl4T#1U)%Av19tI&4_Z^R)#yEWG@Wmqd6XZ+NKB8|= zX0TULG-9GR5-B_n+Y$Xj^{sEIv+1)M`7b-!Y?~-iz_v0~GD{Q&Du~r&Mz~@MvjfK6 z$9xGMmXNx&&-D}A9yD_{)gz}Ot44yRwg~dIXqn`ym7Ay-ALXvrvF=XzMLuI&Au1l- zt$a$Nq?;izkrHSc03SE`!ns|I*r_Z2tKl+ceR>f^qU5NQOmAazbNgd&`g5GgOfw9s zmL^K<+9635K$l9!tlr?tK@14n;~sd9v80;Q`I~5MUvB&D-xp9;yfUlC#4tAnNd#gX zoRtp=ah03D)t^FD`X4rig0iBXXPOn6S{vHOxA@1Os>v*?8mV~;SyXb^d+L8t=DFk3 z$(mM8QqaP&MqSHkMHT~m1qOLFK0#ZOttA|>)yR^xuLx<7t?i}xQNH%ZR~YOP;^k-> zd8i*QfhnlwO1RzrqH`_L*KHszwj##n``|U0Snd7rExq7iiW-Nds1qVY?6($?1F5*S z;O&71+7?3`l`{adO_s|bnn>OkNN!o9W%u84`)>9?%w{SBLc6r`l3s53y(=#&Ohr(?J08LYyo^9<5jnN=fc3 zu(|9pD}0@mYAPwN$>lZKc2k<=mFY^kn!*FsQq{+3U9N7P({i@AgL~lIz*l<4x~iut z$#UvcqJml83QI&1AWJd#HQP=5Z()SFs?v(NK7poceY6>^tnIz@?aluHYzWdY8ygZV zZMN8T6?P{&TuYfCnvDf46(3TinpPGm)7sV+xIEnV+Z;J>Kua^j9V=8f zMO?D0G0>#g6K~jKw9v7JdbsK$U#BAXecI;U(ccnbJC|k?(MqyH`dtVyw{;-g5#Zkp zyCBeoT)<;N%=#bF}2@*y}x%!u~ zl{;x8d@R!w!ES|0-3R4aeG=vnL!Zf6M@uz1nO+!!J6%qK z6p#v*02AYUEJ}HtH%%GJsbUv>DmfIMC3qyBGP)wua$Ezs+QRoc z_`{!5=k$5aPF(P0G&y=wQlt{4C@J+NNBu|N?mh5;+evG4{@BUts4Y{``o^#IqcNiW zI@I0xN_Y_xLiQ~% za4gMpZ6t05t$X*t$mdZPU5}123x;wPYzf$37e=f?YX_zqdkyeRt+CAu<*?*}v8xa9 zAyK4LeK06v3w=fJ=WRCq@$9r;tVuWN19}+IQqiP@L~7Y}dr8#HQ=9e{$BX)UJepdT z8HQ;2d@jZ^%S>i)nn~4b+mHpXeeZw_wu}hdhSkUT$2{=IBTbz+^Gg!fL2lC2Ni3vw z6+O?P39uZK;BGndimB_WsYA6TOVc!{Vt$hpEQggOau3ef;uLHC*3*4eD>Dz~#d0+d+!6a?=}`vxJ0X}kqOH?KnJqwx0;KRM zj^M82dmiK*0to~VK(^TWWGde43|D>c#k_aL7eY5h@=Y{>M3TFNT`8%F(mW3woJnMo zRw03wN3w#%lY5JP_@FSg_r!oCTw`)Tr`csp6H`>lPck%fM#WiJZlrP!EUQXr%wnFU zssW8!MltBp#BRLrZ$0riY34MmIZ)V~u82Nm&uY zs0^Wo>PX;@_}q|brDbRynr4c6Z*c$>kw{=RJ9yi_&JN3SDk^M~GNY)NqvP&3Q@@tQ%NR6fq{|MSVm_H~ ze&C%zc-q(b#a$NZG@6pi!ELK^w>IYAIQlqbl0h6mw2W*37rrxeDn6AOGp0l;*1d?e z#u(C5=QX)xl@P&ORY8{v$iOqjN*wZgEzkEjUo>)}DU=;fp_g&Gp8o*jj2%TSR9Q7| zOE8uMnUQ6tMk=$eW*J5RTTXR+{Vh$%NC_lL0!Sx_BRItvaoFyCAiysFe}+Qi38~ov#ekZD$;7P@A$qu z1dUHn9Ejy8K^NQ`8~)(pf(YbW5(jJ=19oboFnYh#74=m$89%3kK=XtVn5oMm?HUCJ z%-x5Rg;_pjRhCpsEhLJacwU}TVplyUuwVlNzTAs(&I6(}x0~vkWj!O(=_o$??W*UF zc;6pJ3Xs>a+i-qwf|Uo5W@90ON~#Jh!djfat{fdD7D5G;%62w3#Z`4x6^_j!q;Vq_ z=|Z{@0oavMu)VRU^c_BR>a6{;6wczNZ1P2pvRbEI5@|ZYb8C&p*6)O#smb3V&T6vR zC#R`uS=u8k4jCA!vh{=lL!n>&T73NBJdV!HNKvTV+iVj}JW;ed;SxrN-UAdaFSsOrFyh8sMj4$& zH5aHQsPy$_U?E8_Pf<{GPsb-gHaEWq#s{UEf3~Rr=@!tOfpBhbYvb&$i>VCq z3dpID<@Q8AeY- z>55t`%9Cn}*!D93Eq-}84Zp&%ihHvGq=qbRNx!=i&;SKkl6kP>409+c^7p1{9L@^2 znw?~nMuc=(uc|YCNb)dmr*bJXT(dQ=Ov5gvT($u*j-p*JEzPvXE$%-UtXHyfUW#(e zk29^LntEC(<$@p`-6Jfi@=t6nt43LyRgL@$W6{4)e^hfHNcvwl$!Va0#$uX!j)3U6 zu_Mkr`nsj(s4}*jo@}6Bj68SQrjlAIRT?g!TyiaoVH)Q^97d#oNx2Jt=O05=JaJ1D zax%v-0!0?H0Cv7Fl6bm75}qYs054;xk-hoa{{Zodq9&`-ljQ4?<~eS9;+~bso}Q*E zO>)#omK`OK?x=Y-;Nv>=CMhbG0;VZcr@ccRH@LAp5w_#!10_tqPGHp`KnPGtAwjk5 zI2YRx1N&j(y@k6}8J9;9EizJO85J@+(!%05V z*#7{&*!m&uCsl>^1YZ9Dj9N4FEexsz3kjB)Tj}JGi29qzE!f*`_^zK*Hl6gT-K}uH zIHosSqV&`9A$03j~?|FOFm7TNtac|VX1~flBqi( z+l)&(%PFa|KcS+nhcRHP5?WxzWoGSTVmY?m#wVIib9pli(xP_j0Y+=L!k_$6R{ZTk0y?Gl41F#PYzzs6=d^LRHP)mJkXtDP(U$}e`q{{aEuS0dB&gC zKBt@1l@yg#6m;>+4KRjALA=Bdy^kfapD`T)ik&9*CxSjPvD;r`WJx^|1h6bvZg}y7 zW0IIqtZ=~Mq?YSQ%nGs_SOIO1JKq$?9Q6uWgE3VC=fL9@h~w#5bu55;?k|1zwYbH2 zp%vYnk<+Cb>AMgA03Zg$c;B00(WIw*jvD9;MPrp=C7o5+l?VQx+-zJgQoZAj>pD0i=~Jr);2fAiixGJ&8zxA8)Ko<64_KR$){gTm{5S$8*jzW{&9wx zB^1?_A{u%|o!F9z8WMjScD5HOJB26kF!$2=)qX>m#aWkBEj>(XRI>~7$oyhCEq@iV z(5BUuRAsGA3`OKZ@2gy6?~caXj@W0DWsf{n3PT6!UNj(`Uq2gSy4e!9Gfxu&6P-gb_6H1Wx8AaJ*}w>ZgG zniYLCZ!3Gt7jzp9^nv>w_U8u!ZYj1I>CtUGePrrK7f1jD1!8VM`-wj|X_wPAOLSIc zL|_A!JA7bBLM%xVHmJf25!>L~z8F(zfa(cmHap*Jb$TNEG*v#Iq-@@bgvTW`FAQYK zF&89{yJN-~`t}FQq@>9x^JSbyHNo%G(>CJ#56&}6YV$aWYWP*@l_QELHfIFv1-ZA* zBkJs4@-s;Zu|Ia-R=srrV35iIWR z0RS5ie4J62&_hv|RMa+MPfsibo># z?6=X%n_t|c$K6CFV$G4{_fk~0R%t#7CN$M zQbAbfsE7Xma?Nhz{Ww`eMOgIDS}C2PiGVU`Bn$aC=EjrdFZe5xT_2(SJ(AEhBFZYo zSqLKzJ1F2|IqO=3MH}Vu=1>Y^Z_>X10KKt{^pC5nGUk#ef3h+}Y&!w5wiVX;h*ejn zVLUO+rN5aQ6TQ0}dH8%Bk<8^iq*hrY$3tIHOOt;Cg<*rGT!!X#Z)yJk!NwS6^5nIY zv_g_JM_NWoqai2o!v6riR7|$*JlTKod!F7CQ?7HtlSBJvK#^CRZM&1{4H?tBdoF zEZL)pr7vefc6en@S6!Ia(`8h2a8$HUcXS$hsy8~1K1MPgq?T%I&Y?hoB33Ax2`Ym7 z08bQh~DJc zx#I=gqTivG-5C|AK(J+%5@;q>WMxvz60r9+k;yx4@rct`tp#N|<=&#GPLedSl|~H| zfDjjUu)i1ajb3e+dUj>Yb847s;-%7ItolK+GJ~KiG2ETE*kdhIlIAk{ZYr7zT2#xP zq=>2s0#=hvirnwPy|K$_mZpj4-m}d!no25++DYb?kOZeJLnBD7xhh4D)&yZ6@u?a( zY1(?`qgN(cc5oARB-jmy;@e<+#a=pwqfe$6eL9D^PU6PrwmyQArYfg~BBP|nMX>-e z9l`H@*y&NUa=E`of~(VCS5%Ld)lgK^ibGiO#ZtQ9h#Qr2U<-hM#w+xmQJ2*sC=`AJfhw z>Agg2BgO$c^Qz=*4gPS^#+UNho<`n}NRRarqK2b2Y=uM=S%O=pK)fc_AbfLdE6mcD z_+<;zR?5+njj8%c`?KP?$1`XnN8k-&1T- z$56TB{{Xxvjh0zCIjW%Kij5vyQ3SePNVO2VmK%#-zl?DjpQ9@=4$bW#gSY=c`kdwIgIpsV_0nNm{DVnPIxbnhCmzSsPD$91Z+ovL((RCGCw z7pEq!sLNoIj;2n4oJ8*ETx~L3wz=Lw-2`_*A~7sZ7qu@Z$bHV?+nW}q7*%q zfWG5>v8B&*Nhkfa4U@<@s^SsN`DRRnj|& zDNIJ-Txl1-^>Oiq5X8?FT|%sB7$P#AKnjvgtO>arbA`Nrof@Q_r`h#Qe5_Vr0d zAxB^bVTj6CPRHVwk4OC?^$uB+!<$K%WbxOc(vx;8{ISP=Zb;)8jP5C^vnp)DYB5h8 zOMOK?wYoBDAh~zF5-GZn9 zj@RdWW9eQkM4fS-$G7i*#BHgh4lJOO7~0>AURul}Z5t>z2KT?8oL3WZ)sx87w?hS( zCb1rtzqB!5-)|TvE~BrnM-<3pF{yUaFJ%Jc*l)1!g0c*@nX2G=;|!5J8smelrKP>V zv0K=JEG{By%{<~OrJ5EEv%%f zNTHSKr4uT;23z+G2;5-rP}b$ylr;0w<^KRnSVKKyEBhp3*9BMlK=Fa;Ge4xuE2+|k zl2cZobsa9y*n{lfW3~pRxRATbVj&3*st)$!_r`=3x9m3E4!s*&kkWm8dEE=uq}0r? z6gFg*+co@O6x8NTH4GHumYD@SZy!^q?{LkkmjI0v&u;;MF^hE&avWC9aJ+LCvR8>^GF|Cm< zc0GnPrNxYuqw3lExob!wI)Jg?07SEB>T5(|EQ_cpKi>}$mKf=6ET-Ra0AFln{Zp7l z0!Wb&BA;?Dk#YU;&E@$Y5-)=ro7p_(x~h(bJyjIJW>yCB0!59z!?z#`w0UF>kavj_IJ47P_8C?}#a<5hSdtrQ=-@$tap4fR2!<7?ec$G&6IMxF(avxQK>EwC7Cph~|(U!+v zv&t!bJ6&CqOd$-0!y-K~GX6rSm{sX8Kqfo-=JRvlb&^ef#gdyJB%v9FpYhx2bRlCvm~W3{I=> zy|06PI|f}>na2)mnH;*COA}DB8A`BHRB0QVkO%LM`hIMm__Sv`O1U!f$EdW0M1Wgu zI=1JWJC%#V_64928(pv1@-ZX}eJ#oUquciHhZXrGv>62sdG!>M(^bgP#nPA=6sQD- zPDecOaH*tbV(tTKHw5-RP7HJ_?t=2TXlJXEmU-fcNVbNiS7eSdE_DzABoHlcBw?qc zeJh&!k1~#%Ev1%sW4fZ;kDtaf`rUrh7w1j!zWpfuMa;bwokdwq)0Qx7(J-=*MTz{| z5zgeJ^z6O`2RfOR^-e=wl2)}PbP_>28#HdDGW-y5ifHz}381&9)zl;){?wdAY@{t>1RJ;^fZ0j2TrW%3h)WK*^obDgSI9m_C=VmWph)bAcG5Uo@^D^bTT@k04_>NT$mx+` zfXqqL7(Uek`wn)%)hKD}UMCQiMzIRollzT`-x&qzl(1FQ5u!tNTU z_<)#LS$aJ!CzH;r+v3>eJc~~%yp-|Epc}^_Hf_E)#H;0_m+8$dA*BfltoD?@Rzb_y z{kQNo7^+EAqtcEcV8OMK@8gdaz5v{V4Ng{lJd~@K$}?1|E^9>o#)n7(KApJ)6Q~~e zr!=P#JQF>1HIGvennN3wXv-6Hdx7s3-=29mlVw?gWO3(4mQUf$Ii?i=dkmpN6u+Ag zaKFcs&M};*>w#ca*41lw{{Zx2FC!M&LW97;a8OmrKywJASA}O-54>tJFeO(_?rs6! zkDOA{v`sp6llg!t)}XT3gR$+tFlx|dPnPBxtW^}bWd%)LL}>Fm#lD!3UD=0$&F_J- z>7b*j>eW*`;6}=dB7PdE?^{rmLQgb2T)r z8DwN9$YEw1{9>u>hN%@=+jz&;e(FH&j%rc0zkYCC6*^S3f=N1VBfLs110TuQ5BI|- zMAB{1T_QY_EGY<;j03Xn_ThY__Co0s+KmXS!~Vx z#BE`%+#de`oHYv03v5?=dH(>)?}M`p`g**An9y8>Y8s!`f{T$lhgI>oJ%%a z{{XsLs$deDcTf$1=GP<<-xHImQA}}@i~KOR=aO+K-ImIOZoms)7v@y5(A1b^k~bw4 z)uV90s|$m-8+JR6_<)*8X`-X{l}2Vzv5iEZEy*4M;|}SeKcTj1nbXwe8ErHfbqyT! zNYTkO3f)SOPOWki?YQ7t$80H4B!T4A7@*YbTXS>H{O^MY(hQtLXyp3SDSxTz`#M%0 z-8Sb|`mgb9SyKjC4p8JyCa*OhAx1h(IyX8>>~(zKUjD!O)SoA~W-`NeM60Y~QJ8pI~LLuzy+SlY(cJ+MrR^m;loBebyp z05G8h0N>O393L2=Q>&y!8@UU1xxJ5mKWtk(F(p)y!zpwhYqVu`e#Gs$IMx!SHzvby zJYvLVL#Y_4@-W6cqOo$wvwEaaM=6pR-C13=pc@qpbr)goBfooKY{h7ytLe~Gy<9pJ z^(2R;+KPh0*E<7od+%%a#UUU{Ao0Xr>=BBf_YgkOe)yb3$G9rj9xu)(EOm66MA6Lx z)J7ywz^IW4*!+u+@rWoaNdT2l2_S4R;!%AqED}*vPbF+|O%XvHC0MlABpVPcEwSI5 z?TgM^Tl^+G*`c)?NYNyrfxD;#i*w(`DOhw-3Ww=1nTnwj`_Qe4B}V|?Yxvt0YV0q9 zGyaWV>6T4N>JM4aW%b#VIdoIY?x>J^62klbc*j3ey+0m7>N)A4uBWf1GB-x12L)sR z*w|j;$8GVOr3S=rk#{Ka{Msck%GW!^iH^uEx(o#cD(0~ZF5hAUf^&9(-dvGxpA+2Qs z-yGxM<<>P3Dis=TgYMsx^MZse2*FjISyAtx+h(!0*tP~M?gTZ3?0fuTf?B{_?R~sp z$SEc{d)8hRjbvE#v)V#PxGKDnw&Y>W{tu9|0-#)Zw)naNG_hwYR>UQbyUzWw2%dIT zP;QYez&0Bbzs?HHB&43FsYOX8G)xDv-&ec6zAwfZbYP69DXz_8t!%c6dYLJr>qxPU zS|c2>9N1i9w1U1|uB6kjmWGwl<{^MpX4o4Y&yBD>7GYaLW)ziE(~09T%3^Z5(DJ6- zgKja(DddJxQ5Q*`Gpf?4WjEV{vB5HG%c^q$HeE+Xv~K~xW&-8S5iSL4&%u9#Z?I|!)WIe(NqIXPf^#4q&lie zjNBbbW3z2-@5#g&1bZ_mVga}36$=?I6sq5A@3+nxXLT8EOq9))W$i^AbkWB!d5oP& zK=(b!7P;Qtu|y~gXQujwy<>GC+TTC6I`YXZjPkp?I~HP3B=Ubaf|54|*B02~!~k%^ zSK|1pLbeqX2kC>Tu=erD?fc^JlPrv_8!ITYi+#T*{A0CCPPCMUDa6vclQ1YpOzPQp z*zLW?oKG)#Osc|dSDkoK>n@C#3FLQ>ag0x98pt1ZK2=r>1$~!GYVC-baA1nGfJR> z%v5L`k;cP*PZ(UmTK@pd18)8C0H8o6F193qn*G~vdwAa#AE(rsUBElI1MT=8e>iwp zWEq#u9TrLZl@0PZ4?hv16>4XuT7{{YBT zsPymCT|dzdu({c z;wx3?()ueZe^F+Ml0{n-P`T8PIkt-r_uAN7pHat_XB9MX%9E@T7{d|>Yi)6C4Mjzo zL?oGL>X)G!$sy^m(1(U6us(4_PSq2jBm(Vgv9R3tJ}@J0cBQYtNv<_pEi|(T;g;HC zmA$Wf3*O)37qt;F$533iRY@&zV8Hj^xW#J>l1(j&5|?Kowy@aS&KhKyJo%7#>6EeEl!d3FIfiL9VuzJt zZ8q2-yIA);oJAs6gIcp|ZS1OasPVo#mC{m4JH15m#}G%*5koehJ-5GNd|}Q*n9Y_{ zJkdsl%zAy|>-j6+@r72EMGn{L-$!Kmj&D&cRPc2~5th;v+3n`v?}FqZTvGJHxMOlp ze;fG3GJN{0A+4yWq^OcHI-Pdf_wD;&6wImW5~<{dN0L$ey)9y`eov9{hKtCi?PwBE zYLTMLAJfZ|RvPAe_n^sC^4#8fb z0!%;(N{$7sVaID=ntC^eQ4Ly<#dQq~r=7jY z>EdGvc>s}#+=~v!wmAHsE*>&TY^E4Imf7dNgqtIynl;TR9hggImAF&b5pHjQy?mK< zMtwU_Q}ij+6BuQ(mICB_6Z3{%s>>EU#hQXx<4K5qibzRU9&gF{`NBU{P*l^^MNV^A z>RLh7e|^;n88T0VaCWf%#{x$uUOt+%uOydtT{jE=0DKWoPULl2 zU5NI9r0E0A$Kw{#<#iO*gvzkmL1q46u)g4eJYW7W%~-YU?o&%wR30j1mU*mCNf!4| zE>7Mtt;wX#DE&7*JTDY5OqwSSKqr3~pQI=uYUE>GPK`3!Q5zKreT}~({qgD5KA+37 z{HbQ9XqBQRAGWE~iVNB%XG#zQAF1R8%!ol&?tgODo(|23^D6;@!^O@$%8nLm;A>i>Fcb zD=JB#i4@w_zaW0xH@;|)4AOD!YWuZWUih%8o8jWtqiZb7O4)~t2O@s-<%5`S(#<9 zS60nU0zxW&j-h&iT}b3u57Ghht(~bdVZ0G2%MT^V63}X?g+O% zjuHI<&MG8f6B?PM4pmeI7rAd}78W>T4HZPyYFa47N$rkO`Vqh*WpC-32U_HE5v{nI(~o zfvlVk{(EB{)HMlCvdI?BVFbmuMGd$hf6t75nf*u1vi|^3Qbm_#)md(>48~a`0an9{ z@sG~=gwRD1G9t8|;~tb!fZxB6O~>{)h6zg~E?Q=la?y1@O%$12S)^4|^01twl;>5{ z9vHQ)VsDKXrf6xa^N1j7nmTb93iq6nYz6yqwlS62u~_IHDk-T?OgxP3{JGdMI+bhy zUf+?6a*Xn7e8Fm=juOjaRfWgMx6Utx$;HH3@l8?s)|=BgTzL}16Dy(jDPy(mk1h3o zq$bPnSkv^P(|t>E=Zr(rpQx46(8?MGk(OOuC~s?Ta6bmzVYjb+e@~eDc`4C4Q(EP8 zgTEdB0DN)ahm(sXJ*U}O)T3vOXYMLlq^YVCOvD2-Zrck1bK1ik$~{4x)8!LobeW^W zK!Cf(2=8&QJnnzz6;xH#%+pH?DA26UeIwh(*W}?8wInG*!+uS<$FAWQrg1GhD{*0P z%x$%fz+%X%C6ZwGRd6=4fH6sF06NqKZTR04RFQ3PLjiIMTW-hkir$Gevruy^vTsN9 zCzh^E(I+hu76t(s7AD;4+Xz}vVgMb8#kEOEH9{_0wFL}It!5(O>@@*y*W(e)vfBKr zrb;UMXNGhOsTgjz7a!jP8cU$dXVC=(15FtWx8(ay`yIY9xyow2E0@Jp4MiOD)cqMH z=bb=VtPitK{NvB5)#Q;;0cLY$9!@lw^5xP}<>^C8&`%90j;tNbl7=@UpNuv|%9FE3 z(s}`^a#e+-(#KO=4t_o`zFVS~+yFMd3%Nk5!b_cs_&vG&;2h$mWOhIpJ?z7^$2?-Q zdPrK`lFFlc?8Z7s9fd@)Dnf5%)qdv%dbcR8$nz;O+MOb%7dpx?W$bn%z7foeOI}11 zEXx~vWJ{<&BKzV00H*S6&(zfuts|t9#S={ZX#gY++wyVECtH)y^q!K~nf|6S=+hk% zSV^W`O2qCi2alfkm#KX^nN>td9d$i^s1{ae4xt^#+I$g;Y9nZ$VhI~qZH`yh&r?$% znrU7+CF(f7{yYJDVpE3D@s^me0*F}{buKOeYiTzd{Ng0X6b&EKrthdqZ>$gW-uAcc z+YEC`3Y@-ZlBSw!Z>T{uW=>uv`&)0uKAJqXs(G3z{{V-@1u$eu%Q>1r03cXwSlo;3 zJmO9<{57$3<~bhJ>f$?{C5Y9(n_^JW%QUGB5j3(nI#mV9+ku7>&qF3tO;1fi(bY#G zGE_9AnH1W~r-5s8-uQ%7ii;F)6_zl^RDRba@pJKqgjIAaWmMuZ1AsL%3vPJc`(jB0 zGFSl=|uX|jBRiRz;dTU$<9dQuH58Q5zDZ?8~19tgrr zzB;;z%_S5~D3jl!!%^FR@q;~8>fFmarlh3I$|Gg*bYew{#>1QVIEG)I%-KR^T{t>v zD3m#fbvU-2TUp{5{xFOVoTpZxZ^6e- zJXg6Y7E|<#dMvD0U^paq!&(Z-OsmzDwuK6Xx3B`h55UD4ZAVI=OcEEeu+cJVQ*c2x z^S;IEs|4zUUQ5Hss$5)mrYnmS;D2xC@8k~0%bou15ZbAH&Ls47`tSgEyLhxOw%nbcLx9-^WC zqGgMwre*5bTS&F|u;a!&Pe0`TDV>-n7udEKRi*0yiVeXGw;WpFb|ahzSF9r$0M<5d zoN4_(971oW^&8bv#35!GIEJXiZqlPW zGTJg9PL}d~i(hPP6C;hXmyFRFt0pTf60GCf7rNs}*pvGagmoEtMtG%Q+9g=^Ky%KJUpMG4(ZE)X-Ez7fdZe1dx;j*bqgD+#8NY9M#P$9flD~06+pASLXe&8lg%Z-OaDQ_c$UtX_r>0-CZLRuxlyO zcsujYY+XT>R##=!)%5Tzl=LXCP)n%N80k}DZg=nwDyg1&xsmH?Wto*0EpjnpsG`cE z{ea+M8`+EM;QDAO=K7jxexf~LVr#TuF&bQ3+IGFX98nC+fr2>!x!;^n%X6{X__RS( zDL$yv$@IngU0nx8yI)SOFKz9Hvlf`Sd{Ep zhdXnyz*MyY3E`Wg9K9x1F~}~hV|y^}4}2S(=5l74t!;eK$4qKpsnLl}h1`2Z_W%rQ z3ZbHA)tOK6T!MnKmTEfbRw*hvLe8rG)<$8e0q@4gd{OJgtbbTk!BPA!N}|abIcQN% zPLKwhZTb1W6$-|Nzv`iiB5Z7qy47~K2b*Aen9NS=)rvr@2DET*-*5K7qc++Nqe|Sf zHK~qD!IoChtb^P&7H}+M76e?}i=DAvPn70)fUKy?X04rXEmYAs{`0G=@G)RmMIF%;AgQq@UN#&n5o97TPhB07mGc{lII*ikf#Brw8| z#*G*uq)Y0S=J!7%jfOS03P`G=DPWo4nl?$JIvEJu5w*7bV(iKoAgnP{Jnb5)>gz?* zaMl`zo1KBZyo^50vOL9O{v()E(B#nl6jH@zy3R`bECFM!>`l(VgL`2mbyZ$tS3Y51 zRP_}y4RTE^mv=mJc{l)Q2IHn~t*F|@`vKng;Mm_2BH&-YIEw%{BN$MK)V&O@)ryLe zEubuJ{Xry$}r9SO)1-``C1J4+sUt%wgp=EVtBr=w5GN7!Xfik!{(+T7WsCXW6hpCLK1q6}YcELGzVOK`%kyOb~ za6^H68+Ie}fm%S%s@v{&-+VNAa(472+&Cnb&K3h;pbQm3*J!GYY9vI5RH3b4w*Vj5 z+SbJtRMOKjbx^NS1dDPF!Q36YW2gsAH~#>WSKDK-^L$o_^v=9*6s9QwR#RdL;~X4- zf3^2GI;Mt0mqnR0l1Q@1=-j^S5Dz= z?$~c__r4aX76k9VY-2v`>oB=Cx4^+vR5dkp^;5vAW2BF(IWj7cx`5i}ea`~|`#}T- zVmBj!_`W$SMI^%F`i9VR|FMhVUCiWsEnOoaV?@t{{Se9 z3!U$KV?TSxsIu66;)Wn4^$VMlr)+exi)bVeH@F=C0NWbO)z*vPGec>jGDg7c%1F2F zF(S?*0Dw_>xV7+oFQ~IPeK9|#(sdbR^$SxZT_q&QqQL65p|@adg>22h+#cND7(dbt zrkN1CeKZmYk&rB+>V*FQQtm8AlY;U|Dq@mJV4ezRg!SV#_cJcqRM`0@$8EnDrk1Xb z8an8IhKkmj6%oxTWolOh63P_WyRW;p_|X29dcq$^dXM<5`6Ui)y%&sA)veGLA9v(q zU7*KLXQ9z$bYG}HrpZr|b&Rq5(uEp=qo0c5P)_W9=?D^Q{-7z9!{8~4L}-fYDh zO4p`(NGpR&66h-%scshg5IFI+6I0bm4NA`%sbxC}5IEqC!P~wck~T?fs#`L~0tM2) zaQ?0NIE*n#G@>Y@X(T^tsJTCYF{t#{qbsw|O=q5=imHZ^iztLkGgQl^r6=t~0_3r= z_!h#fr!}7`tBS8FR;A1!nI?j_9#-%>Gy;%nd|Yl$ndvBF#}O zkeJU|h23m5`^IiT08(09F8kFWj4s_?Br|9beN3^6FYPf(42CO2EuPC#d%) z+;7^$VYUX3#k*d>VWZF*-H}_^Zg)5~M2!MR6y|?!r?h2sw{gDb1KB!O(eAy&2H<$c zo6w$~q4f<#dgdAR9#CoN9-(BbV`-Uxpj)}Yl$2GoV^-*2S(nns1q>+irF`*+lc@3Z zY|ZSm1Gl#K)xp3TqZ39oERxG1F3#ssWr-~_NjTp6$I#%R#0-{3`{`VeUFS$TP$qxsw9I%N{y;rdx5zJu<$!#sE_jo z)*m>?TMLb^Ds`QjHP~402gV-JQstF?pRK8@%$^$RVH(m>h;FkfCs8fh-2CH}G&vL% zxl==1HGL$s>pf~Bh~$m;*BX;?wYfat{F~Gi8Goj8T&c23N}21l^s43;dUaEOXU~&^ zcIg%4O3c2JEDwA_)_p3%*S0NvZMYXv&MRvJdsuiq@me|kgwV!elj_^)+o18ehHw7> zHutul>KkwHabw3r8JO3B<~Xk5ys4{00=?%zQ7xh3hp-UFj5)2(N?0j3O zGRYed2f8N?yMTEcUw$wo&{RiN&}dpI*|v?Zaec=;@-gvejoLQ_N)j1QWB&krULT^h zK^j0ew!3d{ICJW#A*8FMh8pa?rXQzAQ%JtpwR?s#Ne^nW$l=Lovly$XB>IhD)1QKm1C)CjHMwi=Twe5hGQ_?B{BHNERHk&ev9ImRGS{hlUsVGG5zs!Uw{)=}!ABMVts`eULauoum#z4Zs_V+k6Q}JyZaQB`6lx0ga7~>_-Q1Hpfbn(4~r@&434M z?}D=b0Ha9*O&iKd*%SZ|?|cNz(ns|S@w3Sx1xMTuB-@J{;F+YRR$rqsv|=%Ss*CEd z)y4L`@Z8N56AbZ2eX&M&I}ye`hxBXLwD}c8i%~@jM)81j0EAd`@^R#F>ys&(5UzT1 ztF4r2-(z!Y-(krE2l{3!=J7>W6vjPTWK?Y|KL>CzrwHbQj!uV*lqI8G`j_e&9NRLU zqMDfXW|YWexY4BWp>59G-xwLDa^#d#w~TsUt9=z;>3>fxeql>TP%{XELfXk_Pu+2D z_Q#sagtq7fg|;|sQiP%Y-qIH(##H+m!J1}N^wl&|8jV#+8Z*1NV1sbBy@=zV7$ej9 zT-nBH6$K^B%=%+2Ir!TFv~`ow$z3$*rWQ!ZX@GsfwU+lH!?q=pW_1~5WL1=syj2k$ zNWk!ZFws>P$FXkYuc5wwl-FdLl$2S8If_`eXvntTwj25%)Rn%UuWFhUjL1Pxe0SLX zaHlcPa~#IJ<`u2=GECcNGLxw@qr*Ssk zmdI;;Nnh5033WcXJ9CSDb?FImiq?LJja;25w<6weiT%(y{{R^JhG=Q}$x_t?q=1DH5CLKK-^$;7H1(cSL6q0QPnYItTCzkLnMc}B zY*!{wC^MQ$dddM%XN_Gc;>TXAb&0??Qynnsyf)rQO-)+F=u zhf;D@(3JlGO*J-SZ>ED;ZkgTVxKO< zdP@_mji%R72s($_br0Jb>12~MYU+q7Y3ZYZ0eFL005;pn$0w1B6KP3kraX-+Ks6PF z)t4h!zMu`o_wq3hRaDYd(^C^@_iTo0Er8b?*((?49t3P{`R6>it@hTfm0^!w4~GiGzm6zLwSaJ$_6{`h5&9tSD@ zjN6~o&&gM^Mi-(6;oV>)U zl6SDuc^h^(trDJUX$eXC$~Eg%xzaf#VzO$w5-F!vdL+5lNt;omu|_hgnrdQ3l-^ed z+%Qslwziwo)N)eK)REYUgV~ZW2(`~|!N-_X87ZSkGY6R}Rj+G?0Z#po;|}U8>m;Rk zXqGtARaEImup+<6I zuUytPgC)(YB!I%#P|8nCo;LA-Ge^uQb9kbNO-VFTB#g7Fsn?}<*d4!q_|a$ZXIUm| zmgm*-Q&!hhNwfr(Ew_EGYk0)vHk@Lb{)#B7f2LMQ z8*h(Vr^+VvCU!F%%8CfhFfkOh*8V}};~pEGRaYRVk=9yv?CJJ3yNh@3aku?9^;ERA zX-^chNHqm=MxsZIae0_>$~>dlY*SHmi~j&qJq+|AYADeAwk!_a_WuB!U}`9HEUs5d zK@6)b4FrMJf0!S8+W!FC!N;zCpl8&lG>u^Dnc`((s@C&+9&wIz%l@IIo{niIGds%0 zK(|J{*4D6ZoF|J3ak&kePfWwUVVGWvEvV5;MM$Rjj4x!)Me4oMLctx#O#)3*!|2s*Wb5sjI`OlWt39MO+h?} z^CK-p*V)y%KsW>ZIOs-!8Zz2nz}Wns?Ti&&U1U(C z-%U#-5NL?Xu`R9z^qc+0+~39ys?X}AqK=VkKDLfE=^{wFLEG-N{&135nwEv;Lln@} zsf~%Tbt*wN9FG^;`{IpH-+%fgr0nmgMp*LEwp~?IQ7c9K{;~k7>`5w%b6_v~{9rXS zY`RgWjgbK%)mvPbv9a4q+s*gI`E+u9Y!k&8*Bh&;YcL>ld*er#&z5A~oatrJthtV5 z0b0o&ZX#V|=>Ur>5EpU9vCGed(nWqZDA@1x`U=4&UlPY78K0{}(q1IjxFjCoNC)SP z5$i0Hwu{uQmDjRWRn)jM3(P zqO&Z`CCuq(s-88dM~V84A91U}_{U6Cr7ldHBuRUt6gf{iLr2 zyM8f*dTGnXh9;Dos%c&Ce%?=PdatOzQ1b@#qh${~bx_y)(yf#U9e=2mxwiX$G3R-T zS5qZ1deoW*MwXbxC)@7cmT1I^WrY6-9 zYhKs$+TL*qJw0DOVVBayQ5989bV^b46*kg6-e^Po-WqCZg4qCBRQrqe^C;{zjgTDLY8|s|vEY4{1`Jzg; z%vLhBRTDTXAOgu?IIzMFu+UK0gSO)r;PTX^c8IyGZKOT*c5nPwHGI+vYFa@v>R92} zAsx#0++ifTnl>&qFk;7U3C9*jWe9A+MTcW=&Nz%LLk+vF`5&BK3Nck(6O-fySTlRJ z1HHJ#w3QDF5Rx*J&5k&(!XzskoicsFhqqUY{xI?^t}NTt5>rxCWz=!Z1Eh%Tw5T*+ zP1tG&Vm~-pW=m|*jr$Tmyny>^@6CsteKj0n3FfDrB}n7|6YOFS3<2EU$NOQvOG6XA zO+@ii)a_}>STo1i5~9(ONJMA9QktSHxZ zWl`Yv9~k9yP*c>jF-ny^DKsmh`(+&7_r#u5YH1;8qq${aCB5s-z&u#r{0vbP?F_8Z zZ+nH+xgR6v5tMhKy%tj}BA#<0kV*=O0Q*kn_x-VKP_+}`Ks3~Q=nt*UC7k|cLfrD8yfV4!e68{oz+fC)gc#z3~^iN9-j!QtYISV@8~ zTi*QqV!0X^is(yEXu)QEfKj<(O|cxxlBSZT zT56~yhG>nNH(M1x2*z>t9#7CC>zbwJk<61hCE3l(6UqGi;JRk$g}4Cu7#28Phf!6? z2Ik#|&JU)imUht;O#6a#?!w=UXvPo*%jF~bk#A(qg&j%k^Av&zA;=Lv96%Y!6(PI83s(l;za)@kp_ic901an-Ii?xZd~p#!?L0f|5d$)T0%%1PTZ{@3`XF zRhj3FH7ihvU~N+wgxW7DxVD{N>G;NKWE8Ea>Ckz&?~f}tlQ_#r=!_!P{T_kqEXN_u zb115Es!!qcjS2=$MZ+EL!v1ibDNd<4ED~y`4yUI2i7I}0zBBdE zRM5)I8?Lvqu-o^=*>r}pFpicY7gOnHv26pp5I3;rZx|~xZ3b8xRH;aqqRfDVWxmb6 z;2v?oC}WrL(=X0TzJ$?L%@b=@WF!(uY*|$xtP)OTCT2RBi(Ik!JProW74S%j@N- z&1mQ+q=&5(a|YE`*8`@u+WX&oV0SwOW51J&@~SMpp{oUTd7*ge-QWTyldT})J-*xd z;}Kx7Y(_d--4$X-i5k)@G5}ADHscigfEUm~7XTaMvgt~NV;CS2btLWwZ{On`ubQ5E z*=D4pS~wlGBnsui4*`zn_rNpRD{Xs^#wOJjCX?v;^8GhctI^hUYqhm$;`ZA3`ks=m zcq(cjr;=EylsxXMb&YieyB^z+KYUm2ux%}*++Nsg5P|3Mj@{&uw1R9degMZp6Jyw$ zelSK;Q%gxxG?jUDHR#a1YLdvv5tvvwDnYuP#rDRt5r&R=Ao`Na9XORDX>V|>+wKPZ z0(k9;Dxwfo%MP&m9H?M6Ya3r5LkyFvn8v=E7HhSvqrX2ml`_X2d?Kt#(GEI-A~oo~ z`bpaM;`r8OvYo)W*xtk*aiH`pc?)`yjXg@$3zf8>RJ1>EUn2`+j-ID8C#vEZCLv6g z))!XAt+nmX;~O8XzHoMNir>ja$9sG&f^nuNzq8+*Cpsh>qz|` zlh-e&=ggd~Yb`050UJ|k*q-|Je%8iSoPsa+TXTYC==GEd%S3yXu^+YYZ6;?FIi)Rh zw3KYLv8tqBP7HGC7E>bJUj5DR&CrkR@1RNuvdJK=HMJEG%N#eQSmF*OlH=Osk)>^Y z#rt7MM@rTlT&K z=x8Z9QBHA9m(wjF%kw9#GgBm!g8`4DA^W4;oj`x9z7~}_yK#bQ;GUkYT3Up7=88ib zql+>yBoKdbf^!_XGW^~0`Ev>?n9!;Es%J7nz<_o(2fdEmgM~BOvZ!-P$Z~1&b!h&M zqGX;#F{xtc4w4)09G!sx_rPqdj4nY01MN2(j1`*FLrW|o3c7lgmLVw+7Cj}!GU z7T>l8E&R1lxkDbp_dn+X&0d-GOsXXYFy+@s=IgjOAKMqLMM$HTgij(;&mzktU|cgO zHb1Z@`{LY}E~w0^UAW;jIk~oZpSpd4J7aX05zTDtV?YZFb zfANd*YNVjd>8j+Tg0UfyU8$x7$0!E+THDz0aoozSYO^Yul%|53Ii+V5@dMM06L5+S z`bImKXP(0Hc!ZityJktGX|0XGAhgN>7Ycg z;1B-*P704IshcOM&3d%ro|Z(3mFH0@E(sb?olG`2AZ>;Q#lqm<3^MGmGOMGj%&BOk zsie!*Uaf$-M67J2__^BvcD^kwEHGJTvP?bbl5=Cj5BuOgjdVP!&jw)xRh1BAl(S{j zYe@wv(J=a;Zl&7W7hPJlF&qnD2qiSDNee|J#-x_&1^^%%Y0`Nl?|b4fur48a3L_9p z>EhgA<3m$KX<8y>s+u;DVN?=YHd}_;;M^0pBHQ5y+eL3aVGRv66wfSlY~NE9b{^uw z+J~Mk`(Vn9)}~y$FZBLo1w9oFL$NW^sGUQw*K%xJj>^Yxj3*_RXVffv_WuCO;qGge zP>h!@&67WiGOepJHC-4->eFd9QtWmmhW8z?UX2#sgsveHN+W3*0U|r71dE#!y@B|_ zRFh3XR}AUXGNq)B`do3`aDG9+L7Ppk#X-Hc!4c04bkaxkaWhE}K_f9Nj1BcIhyb1} zZ;aTevN0!%Y*t))l?T zQRDvDt+HydmQ!Z+Z5$LePbwtau-6#5MpgSsxVPr~V7$LD$>=>U#*6rjKBQ@0Hkn;T zIi+N=OIrGgxaW){64c2^Ve3d~Vmi8AL!=Y7puAs-|Vd*-Gt#uO(85Q$Ra;uuw|n0Kx*91>qoG4i4tu;NXm= zx~D44>9Z;s0#wt;B#DZK3c&MYvHkEyef06?RZ->jOHoQCA}Ns!Ya;L%FC%W)TDu=2 z_QnzR7-XJm#i?1{s-&1P(k$A^y~ViS7G{)nv>Ba6CTC4GJzP#!Sf^4Q8=r9Vfs|;p zh)5vZZ@4(JrlG0{nt3LcrJR{1k;@nHd+&dn;;W-*E-F}%>`YvBp(zmTBS}dwMev^tILoL9^$kQB9CZyy^Rm(-o4G*XyA1YC1Tw|%%p5~up5n!d*1?}b(94~c7JFE zZ+uP|*qstG!~r3-UvJoc*v(m!u&k!COusRvu8*TD#N9z0$*B#F!G{bFjBo3IR`blx zf?6+7GUl|Vib_~wkwS<|0j1Y%ira4X+a6~X@2HVrEC-BObx0O8(|yRewa@1pihYGj zQPO6s(cZrG52hf@V9BdZGt-m84dHR^yDfL}>$XixiXFUsDMxI9AF_pJeHw9N`*>|Ednx8>c z`nW|-*?la+DP(y-kP-l}Qg^o4%T`d;Q6vpf14NY(iDD+!Wj)8n8s@U+8BmiX%vz?M zP=eIV_X}}xaeHt__rO_sT1whF+WG@mOtJ?s$#qGP5p~t$@x~6)Hlzg;Z=`-Pcw4#b zJm8%Eq8V#?uw^jiu}FdAhAA0_l1T>So$X=Uj574ieHLGu$5jZdcUngq1SCvUje`O- z@A<_cDpmyq-51T5GW>H&E9+NHwY1LB2LFbNtd|jrx7BN+^JLyRZD+bbR4;TLUzM>RH zjYNW7Y8bMD2q*e??lCOVvZFA}Dl02$deg{m(lUT#@vzu%F+mJvDo&TS_dJ|8Ax@EI zAa*-pmQ2*^Qxn!k&rFUUO9F1bIT#u!i=rZ^>N8sUw?FZQ6qIg6kunzA!%4&Tf|kqb zHOH4HN~oev#5@X3^otRz&pUoG^*waUEx|0#6cRw?PS`UtrJ6xcBcUY6a!+e*gFP%L zGWR*YXG=|AOALl5qIn8P<&@dSkPaIDIP5JsNGCP*CS#iA^whb9WV4ENA`s-83?`UF z;rgr*!dw*uhvLKF+W5KRLgHJ;8cM9_s@k-H*!Vb!jxu!@8v$ju?SK1YryLg=B{wT9 zlQOKEs9hmpDk_oE3O%*}fB@h9@d#CfFC$axR$!t_A7STjn|8xbOJx%0nWPy^Zeo&R zN;@@`Hn}JE7>;*YEoDlHQUvI##8w0dr%1432E_S1oCK;|QMbm~en&h}TDDqO+$7ZTzMD3rOMxBBHCRRQHf`&202nDHr%$GjxFPiIeD#9 zqTXrtNh0VgNe^lQhf;L+<&Kk*kWodr#6s%WkLpc)l0eqa=VR>BjXfD zT+va>8pjI>h`SWKC6Dx;*T9u6H`Ew_c%;6E1&7_ZJa)yja5Y@-PRgA?tUz}NqQd8M zvBg(J?C(>lBy1l|kKM>0I3gJ)R_aFO9>&ZD{OxZT4I!0jW2xykM6poai2xEU_&9Tv zG;!3%)ewyUhbrKWW8?FN)d<_MO++&FMD+_HQr9kdC!S6j<=MRS*;P#yBvGVQE&SHe zsmhK8`wRSILFdLbnlxjnVsd?p+kc#A zAc^J%b`o`U5=prhw->{?oYkVandX?N0K1Z7QMQwQelR5k&zI@9RLyM-PF}t z)D_!hZg%GWF~ghwp0Y_vzKTOP6-1^p)Yeth<#9m?hBGi@AP%YzoAdB6hBwy(JvF7;9?3$BZ?|rS#M}O>9|=Ej>Yw&^m%I zwf_L$85xm;hG-&{Kw)Mp&N<~Lo0Ju*FBtLPW31!Sl4bNwoyT2F(DgJSlFVF{7d%+< zaEwJK@W}*W8G{)MMx$%Bx&Hu+GR!LE%nMP{#Vj;o)z+EZ=~o~EY=6EtzLfMs%au(e ziK!=u4JU0T_uI}TJj=(5d7j3K< z#p2yq%1ebD766Wa9AH_&rFft~Y1qUyktkrq@7sWO+qN36WgSPR242fa43x^wvUKev ztWCOMhi&h3i)7MLTNORhCzRxQWo<2RpF2oJBuvppZ~V(2=?8#+z7l0}O-Y!;I%{HF zGK(8Ej|UdzFHKK5rpzgw`hW=7F%Jg!xEvec%*m=`tcpf>TKZ8;DWF(wdk+5qJ@GW1 zwEATm>PGgGj*mW^%acsvxf<^rfB=@>;g2{kH}r;ROjUD6^)k$FAPzM-@DFR@C2d}7 zM3K`;ldfLTL}uzBV)A&oJ+UuQdiyY!BZ>ntilQ@RDR6EE#~dFHYfc>%c_Skuox0Fw zbu`+VAsg=-+}QR9&iF$HQ=0mMx;(oqi&NAy1ybM!w6`y+$7+mZ2!XQrz)t`!|9R(%T`rY$J1ZG$T82E!HS(TM3NE8@*svU<3>^C{b+ z+KAG3Ci`Nnih5Y8nxiwz>S}4_SB+UEG2UB$sEZqSA2>&Ee#5ShPyHDlXi3Z>Jad+!arZUW?y(6Fko+b)ryrX~l?R;Q;GwM2=p1K+647Aa?8WCmG z)&|5M-v0pK1$xueuj(p+))Jb5AsYVRR^B-$9zXhg-|9JDk3nXK$FKYuiJ+*a42dur z$GKx}KsVp;Mk$6dC@@`GSy%-miw~QVwk>M96O}~_RFaDIm6}=1$3%!n+o#XX@GU?l zo;lJvBfh29KE_~fLjD05_VFoIGs~M@merD%g-l^ph`JC%Z@tfa2w6cY#Eu9Yi{t2A zq=a2u*d8u7#j#CHw9OmSF`T-vm#M;mxVPrpkL_tTIuFbef3L#HH+5 zDfqtFZd>0%Qf20zimNjEdP!ayc_j%OR1tLqfp#Npv1W6YCR>@+Q6*#*YXfN$LfVo~ z!R|2YCiMMFRLz-DWsjQW)UBwA5tU?3?WhB>w)VvyoTZ|C+1E0vl8%woiC!r6y05@J zuZ2_O+r1*39T_Qlg}`PeGS}06uWo;~IU$lX05y_B_J-t+EFfvXl+Vwp9o=(!_%5#(H3_QNM=lZ`SlOFVStp^<=;6mX>9 z@PBLw^%r|d?NRL|M}ce=A~^;`)S)-y{NNh?j+W4@ZudqfeKzhtIMdO+6d4NxZM=*S zRwa*4u+lh9Ay_Z~i;zXHeg@oNRYq`FBM_qJNWF)j^M;gOo6jn8S@YUD8Kj`5W7M5D z5s_j^?grz0bIM9PY-q)^=t~wF`HTg%tZj!>vB#R`;7ctn0?JL;jWMRz`L`eE8Ok}H zNef8K>`mF2?oa0rvXp_twC=)7sEt%yHv8Q6$4Z=d@)aiC7KkT~joaz}0GBA!t&?q! zW9JEKX?l#IN~;s6b6_v$9`)*st1HRBPAIae9dioWpXXCNZ+?~cZ*T379g!(xo&KR6 zmrDQ?S`nmOZr6w8U@VoGM7CPE}|V{40cH{@a6RUK6MG0&xt%380bDUwAb zv0H;;bsx>GfRi(Vmnx~CG!)WFJgVzb%6gI5pK6}K_rR3ZRKjHX#F|(^I)rQkowl(f z4Stz2hNyK`L$G7IgR#b&^!wHGdS5we`go$9x@C0AmtDoU#$hSqwz*otwFLpL!1g2z zTC}ax*0HcsZE?lzfhMIRpwc`($pfThBEltHK(X=>xq zsDLc0o9~aa$1*$(C`ke^Zkc1H$AfM0MQv2|GRaX;>?D!(F&10=?Xbl)RI}5}vCkj5 zqY@wv8`?$37wy{|FRJ|uTL!15ndLFS#Iq9JGHf{VelZGZQYSrSX9D1^t_`+0x@$4i zhQ+z~wjl$gbqelK4&{L$j|Sh{6L{2Whr4?Sp0XvWWuXpX3N-a?t5t>XZLNFT;@BoE z+I7onDQ2dIK`31^u{xq4-`itt?`_5w)k_scZADZtMNJLM2o=qcc2G{|2YO#IqNI|x zYMQ8^CXPjoWu+6WjKB>5AGNJ=AF&vmq}$sXG_*t0l4dm7#8TvqP?hIW#vvp*x}89N z*Tf~wDklu{i;D#(;C8@r*2OEkGjyc7=m?+-f8N-pt)ON{iLA^l>7;<4gWmk(MqGLZ znl7rV>loT+)TjagqQt2A{{X%Ol1r8%_P=v{Uei_OyDOrKs5n+u)f;VN&5qv(0WrGX z;^goJu-xs4TSRJprHwQ_Sr#@WSp!*Zxv}s!zALQycuXderK$@;ttl~-(IMqGL}ybdotSD|M0}K>?Txx52O&`nA$}w{?apaP4s%V1-YCfBfGAi3)-g*dBC@0Nssp zI;ir>x_rW?E=t8o72%FzdTv#LA;2QXx-@!8Ew1|z zY&6@w`(pvnGfN>zRleAXDX`k-{Ner<5%=sYq*NQGuVud_|I<*%{uF^r?D6UQux5sgR)3-7;caxf*dk}tQ;8cUbv@p^KfGnR@~%4Df%;i)nE9#Ffc zuH+5vfk-iLpgKB~SSO~EnI!6k0zv-(QafDx4;$Xb_uCid87^T#M+H@MCYaPJyfMfK zXF@I{TaD29zAB2Q6{(?r}tk_zQBREjj%_kvuSfY&PgJut!g;Iij69# zGQ3a%bf&NO00Ds}??KgUQX_K21!f=?KaV&wEX)>$p1Kp4(Nv1aIzj3Xvp{tNsxk<( zZENqzz|zXhk*>EoS5EDc4|x!RH*syXUt@jja4)$~NNjJg-uNz(sc2!X zr<#H}+NKd0>uLrTxM1sIH?X$%7C419EOitWEMh zmefg47G+Lpsx+t~Xvr5|KghNcMuWXD%$qb!zDO!*U7@R|JxN523dlnZIxIHZoA}!v zjrx!Jqc`d%SC&)f*`m>!Xru$DPD+N;vDn;}KHrXU0orX2OTRJ zNnFTPgAFO7_XO{~`)!M;Xy&Mu5-8crFapB+;L%O|KQW40>M>IUQr=p3WPWF4 zwmTk0bdCsPopNhH8lW`MMk2JgyT~5;f#6%Q!wmWystmzl%5w^e>OP!jtu!4b)KnK5 zZgvLX8}Do4xpL^&CXX|YwvzJHykA5dnUDYuvIw@5djoQAIKZ7fEJKkUh3-ZF032e; z(y<@lL&Xl1coLTXDd@IM#ZX)E<$k{UgaTtiL3thdoK8HFaq0 zB`P@7EpK)9=Zqxe^+j>xRPCN>MaR2xFiuUH=2V4XG>54Epx#qmm*ttO>nO8W>lvHAGJU6N|HSxRD(rZua176T%gnE+=C zeaHmvZS#Y&yuotnn&+jOvNVNe-ahTKCxY!%WL7 z$g=E`8i+Ia^9uT?BY&rgoauu1)~?J&oou`U4bC$Jf<=w4dtz$gSU%p~*1ioeDJ5YM zqOCcdd>um4kr1h6I6q zu+oiDX9~%rTLuTKtYh@Sq@fWQzKA5H${b3>%ldERj{iShZ5B z)4NlwQmTmLy8D#Y&K^5pWzOANrZB9chujwiV4l{R7y_C5Pw6hxLmARv??!^y*C%_OVL5^T}usE4T1NgP%| zxf-;OwMjPZiYVIxrM6Nd0#!4|Bf)=oLu04FHaq@s8-htIe`OaAs7g1FRHvsAPsQARs!e>=<$|xg#rFMuUw$LL8!sj;^9;s_P9P=sUvYF0SKn zr1rgz`|oc!2_a@9PQv`!{{a4sXlOE&i#nqIFHM+1meg8fFGA8iIc?-o!Qk)4*kM!j z)ETyBJuy7;Ws@twjXX=#cv$xvfKQRW5xz_cIuMVfm5HUIYHpn-nJ)VExEsFXYxwPj zrYj=22Y%;m@wfEuQ(aUhiZQ8ThDlLK7FMv|aq-3!X4zD*Xo-LY{=^M(4||T-$?mMA z@zhfg6QF~x0QVNMESq?vRt*_`=yDD%Lvz&GBV4ca?OF zS3GlzT^!HwE~#J)MKp~hb{bieOCL8EHpM}7s3*UUaMo;+wKB-+l8#k~E!uL2Nd)a< zi9=O1bTh({(FChiR~;Z-0&WOAtw~ac8AHF`W9FGX5KqQZ;2(lHxVB^o~v| z7fRS1U8)tXxF%@zfXZ#{+!Oi4t7loKX(Nn62)dwR!GCjtYVzl!nrJErj;fuC%?oT) zgJK7q4b+Q4PLyElxpB`Hx8oE`kg`wN8QNJjODk()+v;naI?KIPnN!Jt4w$0xW)3E`GTk(VOpegZWswZl^#-g@3VTijlhBQzkP~_hV}i zKo^EfiyM=$XyImM)VehdK(Myh&}SKJv{^P+ zQ!Lbas!`b@h#sP-BF^U4H@$_AZSbBrUT7LXQA_{_4#VAZwvpUmi7F)cEAcpWx{`%yUODQ>5Q%+(uiocteB!FgI-IVll5~i=Nb$Clr~-z>1{)vS z5^{_FEPgF7vMtIeB&nJd3RueF6a_xx;CVZYd&lXop}jp(L72gvG_;wVASXeFmItx1 z$CFL!OwwrbudWormN>^mLW)W#Hq=xfziv+W)cQNuHFR0q*3^ikGBIVENC#3ueT#47 z2O`NVRToTrZVFp9-lX&eTvdYknop10CW z)N!It=x(oOA0r-17GIxcc`a^rO#pg0O{H*)xA?v{hnFOx{#8KB70c>lDJbQY<>|ah z(u~MUzMVjGZzp~*MNVHlRM6BkG9)q^26g1^dk*5qA2hQwI!IKTToSwDalFi`@>)U; zkQ`sJ81!QqC%%Y2Avx`9W)+65K9)6s5+o5t($=G3bdLUT6$}qiRFNoMY}NtT0sE7Q zEI^mnaN2!=K(?*9__gqD4qhrM>R_FIoz-rlSu~a0Y(=id$BZ+Q#-;dh{LrIy`-ORK1e~n-gdy=t)--*&nKs$srrae zEuW^zy@mEW-ybDphM30`vMf6t1nsKa;u~>K^s=klIXafrp-HgR%h(J7a2>{gLkn&& zR;v~_+>S?#6P9K1*F`<5Ryn{?1VvBti8dnq3lBK1mtt-73zD@gd4vyD3(EqyGDZ!H zE%yN7u4b83*_~9h5XnnQZeT(Sp4L)-z5`g`6k;J0ih&_6+Wmxf7V~^JrpoJ=FZ$=9 znW<5gjGS8he&09_F<)z=CwicKpQ9dNvel?%jO%Ee8%Rg=8*zU)Iyl=htThcR5Gyee z#M6+lAOz{(`g;sR=&I*et*h$D8OK&@Tnq2doC`@EJh_xGv$%!i2uNGCuWNzl3euCi zHA!fVOO^_Z>PofAx!(mxs;X$-s-(?j(lwiq2LpT$n8(qpcokmi#^IMsCdb;r9#q0ftBD-*%jh*X&#on*5VPTj@);2gHJ zJp)9t%hoarDvPbVo_WXA%`DSN29>l#w1)O9JM-ThIB?25xysAspDN;ltJaI_r8J#X zrDpzKvF;1=Y<*cAW+bNtlr*cNXBJ&Mf`1tLouQm`snMA3ZpZq2V7$-i=7I#LXIGkmZn{}^4zAiL^ zGQlN%I_0$VF|}K{VKHkPTkY{|a;CbzJf;l2)-_0^>LaR+M*LsDov^wIl16PYtVG(@ zW4736*)zqFrgAFaiCnS>?{_E7yAPZhsK!@6{;(-J(M>$Ll=2Bo)eTa|SktE3c)hv3 z@#r}xtm-oAKcua6c{)1ROo6OFH^vVr$)>N({{V$kRKqlkzzKmRZENj`XL$`~Ls?Tz zB8DMKvq!q_E-}lW9vsqG=KlcTtS2ZfBkxgXIhA&L)e%%wsc=m@vsr8g{{S9%!kQ{N zTKvAQvV7hsYGEOS4#M)N=FP?aJK~8mYUW6ydWjh+qiEnl!`iLQt}TUh+aYFEH&Ce` zc5gq7U1OAC#b{n!os~xK9%93&!Cv}_TaXXI+Xur_Qyx(8bkpf2lrV0XxnM0|*0{HU zfb+V1$!eBN!5t>j0*6u#fyUP2+hLVHV+~GFGMMNDWT-Ms&RL9rn-v7vZ?Gfd4?V>+ ztpPBPOyoy5t&{5OO)O#-nO;xour~hyYhk`$Hf8irO4QV!MI^{B2=-Xs%X4vk@tLco z&2oujTFBi)DKYE0Rs(B;!5Ai*hcc_nq|F&=siBHwj595ZHHWuu*10DVoPD;6Mn}En zSuG}ThQ3#AFA$7IW$I}koO!S6ucztgDp1ihOJzEVlyFV2fqfb4YT5|;CP`Q%ZL5to z0>I-x>i+;y&7RC&rm5hJ+AuaDMfWGj@3uHRe;Yn5&9|ZFD7fg$1bSNeB0oU=RCt@K zzKj0=Q2zkrI~*kXK8(@T!bI^!AO)Q+g}Jf#{{Zg|4LeO!B}SLX#-YqReTcqZJPuH%ViAS(QOP=ib*Fn_yh;Df(>L=!$5lS19R!Vw>P{{Vr3EOfyc zXyg=+B~w$2i3R%;z$bx@K^1LMm|$6^NVPdcyJ_2VZr#7O9KLg!02tVqKTsF zBswN#Yu%q$+}2_^?SL}e=Q7G`Dx}Kl+9s-3h1?4lM(6wB!cvvA2_(BIGr%OLT53Hm zv1pV?+SW(ka64>x#U?WCaO>?-MaWPaYKC17Tpko-92+YeZr1EiHp1Zp&pAlp zECT8+d+p}^uv~P=*#lG{OQhu(w8K{bizh$-05&hp>$0g}%f?@owJear7NnU=3EKOc zcKl)sxyCFsGX^j)tbJqN$(br z*y;=k7Z=;&*!N%4PgmsCbSx?(jv9u$G3Qs3Pn<`{%`Y7}LN{fgqdhi$fAtPh`CV$I zWji{6NJF;v_TT>4&n!@VG|y3_$e>3O0!5APSXf%Z+m29--SEovV*cLs*cm1)|it<58s*d4`z zH$NCtRU0uHK|RRB>6}jA%u#mlfA_-4AZMJY7dP72c|c1Aq{}lZiW=J5SY4^<5lu@2 zV2Z}V{{X$PGD}d3@JB3*8;dfAATas)#Y|45GJbK-#a_qSKv5|{bLR}Gr2vIs!Ef#=cedPMSR^s)9W;|t6|*hv ze&fOU!P$G!{bWYEIZZy@MS{VIw`=Wt;n2OCPeA5*HuOnUSSO$VHEO5G>CzgMUHNnYl1dm$7}~xQi+zm zGf_fazqqDs_&LAzkz@$sAyM0)Q6mdw2f;&9T8I0uQM=_bb}F&*90DKe%MWyQ&QCmsQ?f`@4hPY2q}J^BP4Ok3HpZz z{+J_zeB-j$#?jruWK&|ka2ytdnwN=PBO#qHZ*BO;K3aP$or^OOYMBZ~A`In%#&%!E zC|8iMmPwd1J=urd`M)^OdW$v4^D5_-izaNU1QIk$42D)LZKYhfwvcbQ>~NX%l&~AO z2KeJ*GEu|ZAq!E4hwiZUrTTwe;hAizQj_IiY_O;1v z?Q9j8Q&Gc}(pE#6MJ-)J$S0%`5MTn@Nhe|(sl^`9Dat}DSsBAsN+vgN0y9*J1_r>a0 z`m8|a*vXSVM!zF+dBKsw@*@L>)mr)Dz#%KS(7H>2_V{n#{hx zIEOT@q@-4%nwd0^Tih3JPNH`=+Z_3`ymXP*gT_)#$)B4t`naewNNT2_r>sbv7p9yO z9JapAZS8>SWHL=7EK3@*lc7e+I{|*fj2m9?nt~LtH@V+nd*kGem2M_k3NT?5vG*Wt zaC=|BIJ{?ZvJ#Hz0ID(jRQbc-Mf!5H)Vbv@QAE+lQBxpD+Eb-&e4H=J=BI}=rk1(` zQA-@C)5NC5&^6gX`4=C)53S9hk2y8j9W0qztjzH$OkAKWO4y$Q!v6pmZpEmf*Xnebi8kigTi7Zc~H@e`3JRiaT0Bl&ws@v~<{{VbWxfHBY z8itlfL8vjgKsF10`+sZ(gheYVs1ig>P6m^ukGl82_Zwl(RSr*?(Nks13sPonB&F*o zs#xjDmG&vP_Sl1S-s1>ZTaGcv=5^N+IRZ-tQTb6(X}=r_~!6h2##>I+6I1m}iB17GYg<;4ee64U!=FKVGd1;|XFgq&$xJ7Tndd7snsq7*kiJ^i z+X2WbExj53pm_|nJy}l!-OkqB;Bqbn$M1qBi>D)dfY91N{qYBA%8*o<3lpyUTk(vY znP0NnzNw(4lu(FZO;HM!jE=Kj3t`gvM?y@m9AOW)5o1Yje zlD?n#EnZ(&6-`6z^dwXjj+#QlTNJSksYSbXI9L|C-m#?hm#r&vnmoFm zk_ku&9AmKVeor{YR_1k8@X|q0;KN3p8e{-}Ej1R?ak<={a3eEoH@hyYu=9=ZB|c+6 zA^d+Iqq?@ZRsFFzRbClg=186}85NY?OPgq;i>=2vYwABzW?A-e7HOTxD!hRmS>L|rO9qp-FVgRRZf$ix$H*6o6{!6&LO zqNr$UVs#LZ7;P%*Tl|k3^-|2(e0AN!D}={NIdM5;Mpa0L zGk;UPd~rh_fN9DwJG-73oAP^K7^Gv%qc1}o512z8M6DbKMG{J?PL;ij0p7yr2l~33 zC8PBPZ7-+u88Zy7IY_3dmNJbZc_4v_=6NjHGgU#G)6_>$jnk%R)IB_ouV)&;vnUrA zQg_1XDM`!4cUqZMr7?86gQyOh-$y!kwfMf+z^0V}@^6Vg+!4Ot_s0q{sB0S?fwtGd zxqe`2KnqV#sIk1S7N*k6q^|b|ZT|o}<6HDBX}T+{>PauuNdiD6_bQ@kUmFh_ViGc1 zK9O}`F2vjJ@;_^1P3wwJM$y(qPw9-_yD_F(Yqdo5`U^}?*{1s_;`qy`2ISt~j`(E3 zCB>Z#$57V56LK$uD5`7n_~fakd0~#GHm7$`bjYUKkonr%U}!o^GMS@k68oyPiT&}< zYXE_buePIcf@E|s2F`4LM<))mzf)&9-40trndURp)8&x#DC2T140pbfw-`u~oh;W| zA+@i$II^BOWi8W9S{GAoz#Nghkwi$S3KX%_OJ3mK))wCeww!-P@+8E)H^xjzYs0s+=Tdc^!-i9+ZS?_JDXI*Vu7_>k}hS zPS8tDRaGWx?;x#(&liw7G;Cf;34ZG-$vh5psu68?PQPI^CEp?Q>y^b4l5k>r41lP)D$iC-r7jM*5?wEC8u#1c;ze= zwZ!_ATmx^;`~2bi0{tFGQqq`=x!j|t7fLcioe}>4pl`D7b{k{Sy$kBEO=THneqQvs zZ0%o8u!ou{SjI=(;QW((d3|+F4C_jinRn8dV$myyEN6R@t%2E1jjTLjucPu>{OdTT zqRZ)xMO2aik@2eiu_@zRUn8duMy9DBuKxf>y(Q@x{XFwYEHc!zMP({&?K^|>wmd`i zyDjwfFRA5=FU;X|P&O3px5pgqiiKyWy$_q!<<-#5Lr{=8>D=1Z+TR$$<~fvk)m24i zXI5#;MC?7Kx{e0R@K2mW4mW9?xw531{q!cTf{9<-T7(g`pkaJfM=dQZ%Ocb>I<>(G zYha4`I+W{{M-?=MCM=Ugw~@;LG-~dnu(vnD(t%_#$*ma;ijlZI#yeDRO!H+bc8^*8 zL;Xys^>3y!oPs(iC@CnFZyZ4X05R=rlm7sm;{`!N@;6W+BXMvpFTee;Ejz%J<|p>6 zwe=Jlffv{wM>zKn{{UBi8!u2YZOLuc_m`(nvc^p zpQLl;zfQ;03sln~SVi>d19txaO}k^xlB+fJ{b-hoX(=aPvQ*jBd>+@w>CZss{{W}i zKd3Vtxa!+zjB_hda(x%KJT?z%R1@CK&8JIy0HBpOA%IrY|5N)_1?lDkB zXa4}E!AZ5iy|Gxu9CB{TBUN@CMfUh#{QP6#AGr754J4+fO1D~Ol2}#!hDFlMFR>!# z*dZyTmPOPF3IIWF20CMk#BSIf99sHOK9YfpeY=BebNgdal;w#wWImrUN;t|giRFl} zxl%1_9(chs`e*4Die#&&s5MZDSs^zt@4B~xhnW=}Bs9$PlnAV`+fWY7K_KZpg}MA< z%NpV|7SB5V4^s_zqUe(?!hvq*j2l-+QxJ-tp>(WQQQu;GcE;j-ib{OO2}~}FDuzdN zvm4uk<8E;*+ai`^GDe}9E((rY7KRAPSvMw~7;4HnDbk$?gHuGV8YwjvR@@fc*!*Ht zxph?aE%YDFF+gW>#7AXOcDAA~Z}W#g!Q=!XOS)X2i}QeM^5*~qMq}FJoD7~j{*|Gz3vXV~rw0rERw7zSzpd?o^dwbUx63-xkXYq-LCivo^9cZV351 zX0o{wBJ7+XfTTUyJ%X5$xvjQ*_#S9Y25Q$-<0jhRcE zkVSz10P7QmnEg3zHoIEn`3DZNe6prcuM`rlnLW2takrd$H6Q7$(qyeu4yQr`X_D9A z>ezV0=JK)O{!9_YDJwjBw$e`SAchOt$q=%R+hXeaHlUfsOJ2Pk8!LxRHn?p-^TybH zU+MbFK^;TD7M!z)orcUuAmK$+49gnylxbb`t*&o<{&7f2PMYXn;sseOwe@dPIg#a% z10%}%ncCZ%{{Y51ApolaO4{}vu>P8`zo+NPgcMO7W~1t57HcT`LH__@-wS1{o?!w` z6sGAlTm@XzD+|eecd3MbNA?bZG@WFpQNm1+z(W%lWnowC~Wm074o~OqRL10~%jV zSG_!iB&Pl$8^%#-n6VeO#@`#@zf<~hn<1*Fi|Zk&5=O2}d=G<#Mk5_V%S!Bm=R*ef zFYYiUC}xI?lZ32!lxF#jN2K!{mKkXyrytFJsk0Gr?OXTbY-FcLrIhO-T%9bVmHqo+ z%s!sX8!T+qA)Dx(Qz$G48~FY3R$U!MZFMz0BB*v{5vbT|`Tp4ZGFX(;yfkJBOhc)u^{cV&1--wF z3rRIaBYa+=vRwZ^qRY1P2_$K=w7@jYZQj?!vfh8Ky#O5_KK{PDr z{bEQifaqh(FM@c+YG@d_j4%gYl1aF}86>fhxGFi_ulr$ja`Y-Mq^e3a0CyHR>c7`w z8N*fxS~%d3OlT}hNB;mP5u?c|WXq{@3XH-!XuZiOea@x1Py(Bq@qak3mX^C$N+L*c zro#G){{W|+F`>#b+@6xIq*TE*L=2`lqm{J0auMF&H`@}OV~+@Fe?=h{_QVw}1ziaK zB~w^xVtYUxy@Dgkj(6{hV#z0yA~}UTk2Fa?PDCwj$k>5-al!b+CiM1cNdr~7NeGG|C@zHGeY<)80BmeB+^y^LiC&IMX%A3XowS?W z1;(q^exb?geJ?FE;i@wM8HLs}Z3J`2BWwxzQ;d{SyX-CzYSG~}E12iG#5D0z$5B}9 z-(qQb$t8(l!9Veb-=|p+tMxS<9c=N?l#_k5owu?4@qlR78XV%5YO;+Sx*cR$4dPu} zk{5n2afUNxnU!1SKBS|gWud2^eJ)o>?XzwLzuy_oki{btLkpwF6W3W_C0 zB@8s}_G#`%Yu}t;dNHLvGOY2>a>e3k&7vo3pCsDj4&t5)xlJ{Bcxj$}Zz@I|&|1dV z-MejkKo8>?)if~CQ$rerhS9~KNEcwuzbBJ}7&t*i`Vvo+tSi>p4EZ)&l-1HlO;6UL z26lp{Q%EnnFehu=3u6;l>D5+=OiBQ-RwnE_?~U*3C!;ejPuE9G;pC0|w9ea^1;-tS zJYzAFK?m_#dVIj85*I5x(nea!b~jPu;|U$(* zq8Yl@W>~h;Pi{HGEb@|;pEi;TdMdgohJn+n_ei$_=WBDw_&8AwJeLxUdJAb154QgR zpEx>IsGfJ8R(TjWR*{JXN^qa@vl6k+z8;ka46Ott% zs)R_)6M{Pvk6QKJ2cqeHA(u;$&eB#gs>l_!ZEf`r&OC+dWvO`PBl&>@VfG%-Ep4{L zNGs)vSf!(yRM1X>R9$>xbHnm9@2~z!LTW0ARV{3g{cT5HR2on`gc3IPj&ExWJIFKc zm0etuQ9#mE!c|HG8p3V&C|$2@x5jyC*~E?1QsIEm2`v2XJ~_pif7B#}B61Xua*hv- zR(Rsq*$SlZL@Mg$nOL-pSZqmdSHZy}1byBHHu<0G5Tx{adR=Fso|1G|k>ZS*BRrI2 zZ@6QEen$tzQP8n+N1U_~m5wM;nrpF=?d@T@_zn_dtCzA{MyBZi|_Q+e6m3du}kltQ8$dZAWzg zVTNJRtIecpI6jscB(yM@E)wR}EPvk>=Q7d76*N@Px>Ul2Dww!sXAE~+cD1p| zJ-5GW+T)xnsASP~DtEE5{{YRgz~sxiMRTGvGpp+g1&RLzpbUiYO@P}7lQ zp8nvwZlrO9P*#{$q7h9Lf^^VX&@oNHx}WJgaxu8d{cA^0`eR+`U*Oc!R#Z+NI-?ja ziUGZkHy`)JGIDR~S04&?VHC8ZcJ52XMTRw&%$hSkp5L7bGay zc>H17R7SyS0s~+U!*kpAz|uybY{9-j!BwvqnZ#POa5dWh0OJEjJ+>Zs=N#>CMr^jL zE~}-drl^{ok}8!lLd*yffJ&QuToHmPXl2dn>VArz2m(g)nEIhr5e3bQ8=bpydtwZ? zk6S#B7RsAg3--1vur3tseTnh&fxtYG$W=r|t_+0O3m!b+>RO=l#I^M@r9P0X(WrJ+ z7vLUyVwwt0y>HXaSrsCbb`we)LDHm)SQEJx{G1I=(8nSAw0A~~kf^{gVhxElJ^NdH zB%+^aXvxQ-?5ixS&a)vyK(WbC$nL80EMTgsW4jOr!0mvkq=-}})HID4dq@!3ir?G; zJ71hq-~(%b3Ebh&r>Qb|s55rVGp3-HI&0CQ)0qWJ>iY@5uv>fJ2J8~8A+yy}xJq`J z))<;VWePV1fB;(77xRgm)Md>iwUH^N3b8=<)WL}-PS)F#bKeWJv1JO6_J(fzdHiBW zQjn{=H;gi3zROBBktotvR*jewdtZ!AC6#@O&I#D4^WPMkENJ~uS+DIMa@)`Q;&kR# zllfYU09#jaZum9=EI_#1o18(3AOb8-;{Gx4Dq8;lJ-_D`M5q3s(8`)_3FiD@w@4Mb zWCToOELP+Fu~bl@kTp+RpbnOJg~%jad}Gmazg1*aUW1R(*_K&JMqaGU`NBigo;3@= zQDQ~F9QVhcRZ2;<{j0zm1M_T35^e0P;T29S;!hlOlT)=Ub4egVl0CyhK_}b41ao{H z={ouhpE{(>b9|m0-idBnl35C@g4W!P#ks(AvQx(uO3h2wX=5o9>(kO}k9W@37Wu^z zN7CL{*ohM9bEJMpk+Hy_KCG^14KuPL#)OipI8oyhrd67%IE;%5u>qaI(w`?BnmCf0 zozkF5B$|wo=pe^qwa*6p?ThQHTRPIw!#vly z5ypj>f!@QC1@J~^lI9c{btY#uB+=#drOh1fEG7unYg@tC;4Nq^H?p}Vhc5LC&yZKu z!%q~)>1t&DT){Zh+ITj&+s+0_C8nq?1ne104y4I;;t2&yQngCPO*%M2{gsH= zbAH$+DvY^uhox+|wR=8#Hz-YB*?_t z-Usi3JwHX3R_0$zo96!j!fEui>I@Ai3`lnMKP6H@Us-Y-g%wl4+!xMl8~?XC!V(-uTMjLwyxAb6nBF>ZQ>n06&5T__~HV zn&K#7m|ErIjfuRDKpIqcbFc%xDUN**%@VOLsxmz3iPi$1pp@T-`Fx(J#7{Yc+ zvKObSmI+owmN^+69Y|#$*n&G7-w~&cCY=&V(gkJ;UG72;CfnQbj?B+zGKOb%xhHeJ z_ax%@&1PWHQmBqcc><#70G76pKfw6Lpx59F!z4l`b$HfL88r{MU!U=axU&!q_rVl& z6_r$Vbr3+(MMoORJAy45mI?{~0Qra;dBs%_ye^^i`;aiw!16EKzs5RFXVGq1Ls6Jj ztvxSHO4lICBF3V?o!Pb=cgJL!j8jh(bYQfCJrX;eQIYpr)&vve8w+CGr#Q@VsOu9i zsxU2WJ5y8uirV69U3UiD4aWEmXlH~WmKhL8x{$JwsCgIX_Qpzvt!Uv0BSNms$L>Bc}KP`%F^cgHI!=B1p)Q47T-gJ^MMVlF?%D_@>QHQ1!a#4#tH1{meJ z)?t>#RZm4y=}%P|j#!`Ej-*=UPS@PnjmKk!qyfRcGS$$Q9Pt9FDC1ZMdmDGfG*t0a8M@U;OcEw^E{xjP4UkwH6XflK zR)Hn<*yGgI`E`93R{3s84p_0%2xcizWHl16-75q3lYR!+Kso*K^6YzfKi?Z1r_dgu z%zX)6>nch_IZX_rDv3*tN>Bd)9(#-kt}2F=&DoN0Luoq@HyE-RU15?goO)%`?kEWs zCvban`{0+TuKo}7g)c-ZI;FZI{X(|;?W)!sUfu1AYH4a{ouaFNM(&8(Tnm+9s1fi% z!wXfiqP7O@zAvGwN@|E^G|^6Bb$t?UtO5I3dxMHJNOdlalkPS)9yi0hpVJxMXOmWD zbQyHj6qI&p=0qi92g&V?Da9>@Fq3)~W|d!GUr}Aqq^9f2&LO9dL~-2S$i0+y80{5o z)Rff`!D00Y)0LS6doB&P8*T>U9D+Joa|n{6b)9^n~~!h4@A-Wey=d7Z&K#c&`?7QHmH+avOfx_ zr(?aZ@G!P2T3Y#X$}G7hj;d8y=4qq>8fIbv2l`gT4e(d1@^g^-n!71;9;!(?Y$KdZ zx3D`n*@pb!#omnK;_Zu>kyxr+s3%E0k^cY~ldGr~zgm6@a z1OwoWv1-)_*R$XaufW8~_8)Q8grM3s)T^wl)ci&{E^?;Cg6bI%xz5zOJV#%!Q5J*|6RA7(XQU6@Z&{Y6(|R6K>s zFZ8X>=EB4F#MV(1Gg6pga_p=nBJP&H!1y=c@rOW=UQHHTE@Mp=XH`8dBR#5SjRUd` z_f@#RZLn;!g{OKC3hsH?xMtR#qgFDxFXCz+>w2-`#O1Jijs+| z>Exnl)}G;GRC?1j z^*(VvVVhP-}-F?9BF|osrm!Rax+f9t= za(vc&wrt*>j-b%fxJhaP9-1-S0^9<7V0w9`N0Ct6%ru=!3EYk^-)?Z)ioY~ZOHE4| zSIb&mGeQGc$VJuO+h4MugN1L>ZkdEKX+T&rUhUrA2gWHB912&WYpP| zS%h$r{P@qcuf4x)Wgtl)nClFL8`z5xhE&TnL#(BBF0c!^_LJJ@4T}@!%g{L|3icC6 z60sZK8Vs(ONXQX)zQ@i0%Wt|HEKK0OkO>y>aktCpDQO6J=QF&qiC|8+$11YAI1(mUWO;x+ZCqaaTlU5IJaW}Y z5!}Q{(xffRHMSP{J7Ymd2pN2_)KMe1Q%H~#abd;3J@6+an9)ycQB_$zd^C|vU4=S? zSCs0|yX-dEZQJvYsNp6FrBfB-7a_g6!8Z1e_-6TIeJ5EMS$^dluKZ%SWqN6@Y2%z2 z%34rPn~q1wz>OtQ3vbwlvV_A>t0Y}1CQw3=?%#`yFRIIDf@P=rbTYgMf zM_*4asN$WOjA$c|VPK=SG-9OfbO|RO%*2s}1TyOJe%LyhY3OL^vnopF{vp<4U}$}) ztM;&CvtNC!j%E=AJK0wDB;cBvvYLFB3VLc;WXxVR`kAL2Mo9%$Fb#!(Cs)Z9#B#=( zq2Z)gmd&F}k*tzQ1wjP>lR2f8uHD{jPNDr+m`rKO^pS1{9t zXd@~tfwv@W!TG_7EVWWBjPgw(QSN3eaadz-Lb)UK9!s8jZ~p*7Fj!aX^w<%z7T^$Y zEIzn2ex5Xc%ar+Wne05;_B z-yEy{n~mFh-IRVqp|)>W)d8Mfu98^7LmLxu=lgAiQ)ude9=QPna=aUF`(didM^}{6 z)4>%?m5~-Q10~R@Ab_jq3v@AJiZn8UNn>rsJ8^Yg5uCk;(q)xZ8B~?gQpoT?KzV>A zMf~A((Yw~U>a?hL63R5(b|(!fD{JAPn=VS35|&~Nk*W5-=Mi{TSYl~i%SJTV+Q1F~ z{{T3hwB40eNh6tdTlK3m3W%UFL}dlq+N^jVIHEWtcMr$AeY@bqG|?*RLP$RBUkq}~ z6-gaBbOQE~tj)&5=l z+}*~<#fZOrXDet^>fS|bZe0$W-ot_hJ+^#;ma4K^Sd3E1BHhR~SnPHHV*#J#qbSS@ zv!51ti&m68*zZ+X=G;5 z-u*XYe&0AcI(SA?4P0(IokosW%WivvYiupHFI-}x*#;UQp#Bgi6(ws7nnWRX(Qa>m z=AwdXNou65jL8&=i%xD?@7+l4i9@GJXH%y37VZ7{+Y^?Sm{Zg|)ku#9k`QiWx!%NV z2*R}EyA~?hRYVa}!4JFNA{A)E5b=2zvM2ae`&Za_ogmj})O@QX-Zg#d4 z(N@BdRZ~^K@^zFlI{m@L%94Co?}oGI2CpWj%hr+@%qHeU15v)h=l8_2tdHyc_EIS+ zY}Wb@Im&1ZvB?Wr*pPIB0LFXO)wH#lvq-d%^w1C^2G_m!xWzu7&6<{$qKLy3k1FUc z;`%@Rk}=x<08h^g6hU; zT~zgRM>o_<{Pkd@AO8SrqG*60uWn*;LuPN0doPn8Q;&cx7njdF~J$_8609*^Dp8iY!Z8jQD{{XT$7!sbHnhA@@FQjR^5Loyga6SxmZi2GS z=qxNyPh1Uf`h|}0vq#_O6?h~^au)F)hO zLce?4#v9Z%V)|rF4scpJsR){lZ>c~w4A$DmwgQ$FGBQaTB#gl8QDOzSwk)8hMOk{V zvzJ{lur?93{^C3B+Z2==s`M(G*q&sRQfd-LRLVf(Rqg%li}I|_s)ntrBX{epIu(km z00_3={CMw!>x@+uok?7|b_Lw12mE%n_+{&AKTK0C3|S2FR7$COhi#RP+ze^WCZN}5 zj2lg-(4W=btIWMZlw&ZF-RZ`V;g27V$6>w^=fX;qk1&dYS~^LINd3(5lD(CWkNLwY zdTgplXZp9P`jW~irMNr*+S@a5^d5#tB&U_Cna4&|a#cY4l&!AB?QB^|Lq;j&moTh)Wigh3M4`*LJTSPh z=W~X_nklo!Y@RxbWix;6!E3q;?d}_sgME9DjGnk<%{tVyNURx9uAN)3Ad8cH5Bs?F zdl@ZqW-XzHDax$=3;}EFJs;Z%SxTwT%2?T6Wf0nF9%kg_+ns}NgDnK^pd^BLB-GRC) ztW)k_s;b1DBe%u?)J~)WXV{x@hxK%iEio|t+Ib^`iWY(7iZLOKNg4n&fG_#ON|Q*+ z*s7fxHyXHW*k2DTC6+~krdn8{jHrk-XcRF~xj#GZd*c59Px@|b&pM{062%;oNu>+5 zlzF+u+4fr5erGOOGr=QM%22Yk!M58U7)2Yr1<8b_Z@L0?VT zLGj}U&XMfknBw?TqbZ z9I;X?sS~faST^KeoNF^`k4#0Fsh~BVM$m%MI2Y=Fpqu@t&NJ1rM7D^D0F!MYzw>M0 z!;i+PbT01Lo}FfyOED%tw*9{tRh!OWFBkMN1PgA&Vc+o9Xjx-+R0mN3y|+7HZ&YR1 zKKF)3(i-sgxZ>Oc@rdP(#>zrlpd7wPDj=2!;)&%E86|3lQD7`io9qY!@^5Sno2@hy zG<6YD%86En&m#>iRjhP@xa>FI08aDQNYd2HuB!W;tZn}Q)+3ow&dW_y`E%1zNW{eY zO{B50u_xVQQ;17iI5E@`#}lb=ayR`C@K@M}DkQdrDi7 zLEirW7+F;xMZiX2Lk%T)8}oqkN}y*FyGb*!9Y-UU@7(_Ye)x`DulTW}1cPC#r)kzC zfk`KGfANF;Rb84)unf*h*Hncghpm5XW9(HVa0U?3%R;3h zt*hr0?G8^)h`|D*Bx7Oi<&T?R5&2tTX&G@10jH&w8D*%I(3*(dG=yu~z=Q4<-uSkH zk`$0LDJr7peoi9?Kt>zhmQWnEw>`+WY#RkQX{2pskZc$W0x{W#8dTaNCt=#@)(uKR zIM^7Cdb{ixoONu1(uWxdXk2n_`U8DkPRaqBSKNCF+^bDH4BB7XF*@eeu+3UrebP z3JrsRMeI1f3W}0y*I1#5w5~#;4p@_52kvptEPo^9%EhFq0nx_{%JkhL)e^7rqaI=?vWh z6RC*=o%T4F^CM8gO(galj@Wi`%G!*+T*snxM&daLPyjaA0nY<`DV{b3gKWcM4%nP_ zX1KfkKB1;sqR>lI^znv!T{cfiQ(q+{Ih_$m<7tIOLo_J*Q&xd}^L*m6cDB~+PR*HVTDNhZx>dw;k0#OtM}s;`<_dPJj+Ml6ss0UFUl;30C*s3E>9eXZjvq>7= zTtMJ8>_D-x9NYV0IR}j!-_YE@(K)ZFO*gHp-j<1BVH1O-YTs;Sy@c}mA1a-{yDZSGTwiCkhi&TIKr)>l6o!Aa*EuSF{sOPI$5c! zsm_^TVY(lZFc0#B#Eq&oBy}7cn|TBNF?~$WEla$$4%*F{S0t7mP6Vh7B+{*|P}ndA z-<|ltYK2emn#ic7V=a4;t5vSPEsOJZp)F3CahQdHM9(Svq};JvaJzxuj0jxG5(Wx1 zdqstb{Q1WU*nmaBHroIMr3$)8lU39cwXA$#ysB1;a5{3by0W)Pw6O$Q*YW=VZ=46H zb_8})3<5IAxw{ef8=g-10PlxFc_0^HzZ)k`x7PG2tbUF)Rd_UbO3$L*dHI9S1U%=C<^HSH@e`R_xSu^ zyuU8YE9k1KsOZ{QGWga?sS$taqc)Ju`;ak8Pb5_xbagFmbb>bo5N<7f@b_H`qoeG( zXqv58FrH{B$Zby)Vb~iTI}$d%uknuNRrOVCPYks(%MB!58f1~R2-f09wESg<-u>QQPu7j89pTBu3DRfct`roA0^g{CLNG zhA^cp+OAsr+r_c-tRgKz&bDF-k!uSJZ;aJ|zbDM|44%3={I;@sDwR^JEG|{mNFdmc zAe>7)sicy$!Isrl%a=kg57m$yTwS&VfxYIR}d2082u2vTJuNh;^whPD z{{XtERyNqE(%N}Djje=*fsH^O?n_@|`(p$rHI|Zk`KFJK(CbLOLx%wFB3*cggU0&f~ zVZQ$WcjE%RgpA11N@6mswg84gNH_70qeNp=NaZye{_ETk;0|wmSEK+9j-AgJ7;T@y zMNeG}RFuY_r%e}F3ZXEPLZGv&4S+Wu-rQg|XuVjjnpnSvPNqnNrbH;M72MkQA-|+~ z9qv40i`-&>EOB7ib7797n$Ca-z3zNq{L41YGkB=r%xfmAN;p=Ym`eLa7$(X*3!DpC zQ$2NK&oc&u_7VH4vVh~f$IAaViAVI0L3)o+iZa2WOqkSSm8BF333ps574(9d( z_>qB@v8d&;$g*_XQQ1f~H~sNtL~&C~NYu4SSkD^B>CzxaCC~E6KX(A_w)h@&m0jd? z(#6PNHqvjnw`^ll9(wmG%rak4H4bN+)8@4a41dpptLhU*fu%$I3k!C(6AH?)m;V3{ z6*7uWuUDh1*{;^rZ#LTal1)e`XNqcPlBtC?@brc&bTK2yOZhK*cZ3bIRYZLf{@y`N9(TZAq<#TOdm0}bNn;zoW zPJOBiaCpV*O;l)_ut6(Rtb#U|NhlbLg})cDy|5m-GJ4rqwu_E0`{0+UwLjBB(=2QQ zI<%V?^I`rw;7gOvK0i1*dSa*eSQ^BWV}5Un-o)P%f+v+Ec+uacF4|O`Bgy#V5CDR2 zsD3a)ixpVMtmz6izsEu?-)sOUk>G|RMu_jF zbO3kclWqS1d_pxP9j~U`A14@O=sLcgXlg4d1ywy>iF#`!C7B$9ZEAM^03!jUP10#Y zC|!28s~h>YBdCHxw%-^ot~wN;q?QWPHB5nxL?#^~5)ZTnzq;do1+jpASyfTjn^jZF z?_ng1AtGCG`L%`~M$ISdFpvTT4MSx-09NiBI+PI*V%^$OIlhAHEf+%R|Caj^cFYa~c@v9w=U( zIbLDx3gwsq_m6P`!)x20I1Cma)z6GXDbcFd7q>WR6uB!<%#u*mrBQ_zCt840s1LoE z5y2Y;9C^c&rryZW=JoWsJyuf;#b|2eNopbhd)Y6f-<_^@#6Ff{?2r_x5yB>Rgxn(S zd-%1*@kLB=#E}%9IZLT5iEv{#wVM6=?Y2IJUD8HGcT`)mC^k?}C*t^I2K@vi$n@{l zMR@|MM#0D{ZSQXTTyKfetPf2wlAuKBpvIaRx`yBpa5me0{9rXU-v&^}En6iuJSy`s zBwX6#!QQ}Olh`{eKva+KwjNVuwQyw7XO#&N%c`epaKcF1Mr8qwkejb<$N=FrM9Va; z>kLsbYn6`rN5B{}DWaP&j;KdfO$erd&dW5yGav*FC=cBCVPzYe96Nf@QvQMOa!CMO z_8Veo%_Hh|B>6ajS4)Rh1Z-{&#~%&O*s^5Vdb0)Ck$sJiIKCO3V=1kKZ}+G+4P$Y9 z4itvae&O2%P^~vvOIv9W%RmfD zl@6q|miCc*Z*95w#SaNswSq2Mz+Zkj{9uZDC!T2GfHadjok&Hk&vI;b7_vI4PF(aG zG%q5_x!|ZETlOauP&NL9dUrC1E?SnW%aXpDSXl%z$jXT&tg-;4U6-}+zwRZC?s>i? z6hYlpm`04iYBwPT_ck2uiN_-%NeW5)w;(---D9!CMij+tz6xj29Qs&B#Hen>i(C)y zhdK2IS4C2gW%;V+aP-x>sS8J?irlZbb~}9G*P*h^i#Nz~I47o-JkpjmmI;kw!vp(; z!|JuX01fbtnJCy)iA$x0;8e3IYmJFLt=wVaxiQ>$#*sR@)y^|oSF1`qzBGL*@FqXB;DHaF6;|{$~ zLk$*Wa>`uoGipjj(FCX%APw$d0pomHIMR|-2QR6fiWMqjB4?`BEL4JpZT;_id|{Sb zNh{QSI>gT)KVaMw_`w-|TRg0Oo2X<}CdfAnVoCg1bBErX%GA|CX=09caFL7ebKmic zsK*}L6-HA2DEf~oVHa0PMc^%ozT2E?az&IqpdB4?h}B^*y)B?@#MvxCYjv3FOQ^+p^=u`sob9< z8_!7OR8F$BbZsHgsWjMH_!lh8dXXvs16&W!jAOE?o+bs1)Mr<+tBgfN=?Ou~`yL^vk83f>CS%CyYI=jzzU9JM`6q8*EAA zZ`&2Y42c^=@=4JTsbej+-f=kRl2*Gkpu1*O@dr_-y2#*tA;Gh z(oubDPfXDR&=LsN{^s0ld=ZS5e6$i!Qd2=mB3Glg)J|o(xF^~I+iQ96ic2NRiSTJF zE2b|sNoBj#v-?JoV{v=_aQaNbuP~WIM-tTEXAub5^#ClyZEIn5R!Lixtut3Z)cttG zhzYY;lgo|6vny$46JrG=eF3rYLKH(PuEms*{wZQB)th^Ypa#M zpP%!Cs@W#Yo}~~-43{Owo}uza_9wu_xf~Ew*7d4Pu-Y;g>EjnIq}#y!oCp|}3K*~Y zNaP$wO-fCn<7p%`Owxv2qIw~gK(oy@wVd4a*Zm{D73R5x9+=vIGS3`pvTFNvT-@*5 zj2Qp{LH@o|lTQ|8nRGQU%RJT>j$5UecW>W(AZh=uN5w3 z4Dv!{NZRa*`b!TN?fJucNm$BehDPd}xghc9{9&U8ojn=JQM~jXO_$YXVhXD0O0lx& zWB&kbY`;*lJd%qnk=Ah?LnsJ2xF3vo{S{2q&D2Rs@3x)0;g6|v?82rvW12_NWh&aQ zxc>n3jB;i1vQILSmWPbj6PAh+=_O$fS4xc*xd&ix+XT^imadjq9vY&CEs*#hjA!a0 zdYTcKy+nlzXqn!`i8nR^_aOPUAsVkj@k!K8S1;2)b-4ckj9ub{y8_8CM*jdV^-XqL zSrp1|ApxAbYQ6Wr_Qig-%qE{PS?c7Ms^|={sQ{=vd~t+%B|Sc1=}!$UOm(6#k%Vwo zNp~c%KWMjXT?BH~Q~v-7hMQJpHAbGHV8AZ^(c|-e*pCcxpESA|$;nY(QAIv)RJ8Od zBQp=S_tz!wB|%M1rbwe=%Q1^Xu_JJI;C97}O(Y2m zvdDr|x@$r&y|z8^_ti&7)mnMroXrl243S?&`L+A}2XWa5m6DDN^Q~v` ziljVJREk+*LK*CLdu*cI6WajhvD3>;f{rShY0z|>B!zWY@^{|&!&dsHx#y5WUo2@e zUPzUQQb4e8_!!(|%MDE>B*IeWz`wfX$@uM#EPh;knY8^KGQ;r3%^#;sA5#IDUKHX+ z;NPlQ+^ZCIuRyE4>BnrOpb#0=;#^3Xc z=Yn!h$m30(#gJ7{QBW;A$p>FZCDiJ*_`Wb%`oUG0Oc8~PlW-3I003csr)nB#8B$>j zixe!xY6OyZ;@EMO$wLsDv8&b^rT+k+uA23EBG}`_iC~kR+hgSWB!x{n47OTn=;3)0 z!$&JG^8gzW&KA*YNkq!bmN&MoAdimt)V)OxV-yfq(;78Mm?X%a>6`L!iZ!E_PM1xc zlTcEevs+?EfsVX$jXP9D#-5lxQR`0PX_T`aMbr`rw-|quQsvO))KzlBF;`-;$oA6R z>^tp*d4xiql6aYwYDc$8B^gEg3vY3NC#rbLz?* zy&zd~;O$Pz&OnfCPS+U7QO?x4l{CvOJ(g8KvThptUA{NKKc|_S)aGBqvv!t6u~iD1 z$TlZoe%PllniEpuCSrlCHP{pLi1?pqxHqzyprGz6L0O(&w0@wdj;?xkZF)$JZoBWc z*xGuBD99;&2NTg$BT863wkG6n^N%;Es`of7WtYnw81+O^h@+RNpST0#e0AjUM3N+zK)|>7@?F9j&HeR+TV-`T!{j-jAPWk&o}Mc8HpgN zGgZpcGN(~P5IHA{p55@unJPju$m!CNMusNj+ikHdIkHL_yz)+eN@r=gQeO&o(m5A`n>#$LJ}rARQ+8ff%|(g|+D`~02$ zFlJv_UrvolO*D%Y!DR<+yo@u3d95=k;$DDSwqRIO0}xYnH@*R_mZ{|lHAsRj+_8^U z*bRo;@5UcfQmq8A)X5sSFQ};^ESYIm4WiTAZp`^f`t} z(V}CdeT*!_ao-E`FHcI^rlm%8Pq?n&3)pXte7JH;YtZn@lPiNgo>X=As}1=(jxB5) zE2Lx&&PV_Oacf~cKUOKu!+ysKy+c;Cv~3#Bv&37fszvU2-~QMaEz0YjmTJ|4 zcY>b6cM+RJPS#`Fi~;H>nmSvx*Fs&7H}Q#Q`oc~xWij3Hk`(0`u2%$kisrPH6Hg>y zQAr${QYXF3Ft*~`TNG7hlgmdOQco<47$OMEl8JM20N`Bs#hDc}8HIcl)7Dc`M=&U6 znNEc7sM~vDtgS^9vm|CW3>q>@>J~d(UyeD)myI;E`z?}9>VVoAm4 zVcA>UQAgT*Z-%j$S<3Ev8w_1h4WXu#yeyRnT|{yQWdPgW2I_X;#!iIfPXr z%9_5OtW&sAEDx$VK0)u}0=wv76kF#IYdh)&&*#P-SY^u75;+x9M3}_VBLo0|12yk+ zeou|R+aG4MZ8UVXO;-eqG7=$2C84pf4m)3wYwe4w3=-8l(^5PTfC*4~nnwiRzW)I4 zfu>|x86yPT^K2}!IaeU^tD@|+5yL=bA%HCqE&`V1-u!KikDmr?Dh1xd_X;V@)yD&Qu@r$CUms7QqOOvqKM*cB8s!~*#_~w(@ zM8z3hZ)1HxkZ<@HBQeWTfoLlJx|XQbN7XUMAuS06i$23kcd)=msO(vm#GUX&`F&db z6@o}>1>soa4#euUhz8)_vBL1xuwAgKKT&5HDXCgI=~J&9k+hb%kz^KPFaDw58QE7+ zy@+ol?r_sNhLtM>G|N>Gjx|}EZ_mc-+*_vH2$6R$ET~kA)IDa-k^q#x{(#B z>1}?u!=jz>J;>Zr=F45VL+ zj6o928>UV+_r*sZ?pU!ARvlQj)x;STBmzJr3)^vy%OXoswGz@sv0Mg*CF!ZK1LoJ~ z3mh`YP0H`K``}0^6PT!SpprXWTMX%FR-Y$)-!h_^=%?3DNiW=CK$pdBl;+1UxuANPLt9Uq@V^T#bh~GCxn(Kc+wR40BSoF{>1UAu#J}D{=h3boRa! z)l<(;QzR3{W0p23>fEUqTiKd*WMP_3Zc(=?Njk0h!I^Gm^h;G7RMfQ}S59DKJYmXT zeM!E|EwR8Qqi2oGVNp~DR43i!{&5W*^=QD&v0yK=4#&0y zrp;>Lp_Y=4s(2%)K^*bNBL$g3xYjRYW7~`dx|y_s4Y812QlelEsOoHhcIV?3P}Wyv z)0&DJnW?4H$<>LKzuYlN8K!TuBLlE0(1HLU3v(&o?DILscS^7(G0347%&JCfc%xSWzv{Q9xrfm!) zk6LRgKqCAw7qJ*;lzN=xSyUq;hFToimS}40mTh8qUcg2Y)?06LeeZ>L=y;{<&r>ue zc52SMf{X!18ns*7&uikBRK1ereA?H;D!NCe%W0=|re&_x;Pa z;|y}RDJtt^Ii6wkNXYNkFc~i3604+tsMue%@!wBVQC8;kl-Z4E(9%mXtn~_-`YKNC zVa>O~hAQ-D(G!wQ{9?+8B&U56Etrdd4afJv`KiigT2{%Z>tX5BqSf`}5(b+7=TZQ& z9jp%?aL+%^Jw02IWYl?or$r5AC}CYvwG76t?QyZ$Y%Vs%T;C=5ML}_UGiVf>vk_oO zu)h8<&n5K^agsq@4pm((Bz5rgDui~tk>2`m+i}|gS3=NK5^1KUjzA=eIYHIaZP$IT zw|@A)By72A+~%}ZHB|jJT`@l4xA{o>&jY+6jjsF1f;!%YK zn5z--#qq-OOp&FXijn{uoEem6by;DldN)LRh#H=ob7H zxIZ`|jUH(trl&w=cCggIDe=xO$S5JqGe~JFs?8iMuEt>zlE5iHXd?Vu_rPonY477+SotT z`TbsdB_h-=LTg}6x7wR)YQ{0QbfV}FZM}d4wmnv@NK>> z&03OQM=`;|slNeXH(uZ~5eX%%Y=*lI6 zM+~GiZ^2SHI2~mwg+cqi@1Of(YGWXlEC^eRcRnyvqi@jCqN=Mjnzu5qd8+4;#Iw{j z%a&&3kSs2I+W67_l;v4Qb?X{zvplP&qNScWRL@eyQa9oMtkfW@coHTJt#wRLd ziJSRyX*U6vhTpa=idfkud7^!C8^r1ckZ-s*-;To#wx}_r(+$jN;-s&dnmN*Vqmhwi zkB713d>lp>RFO-rr3UPvk$;PfLKdcmSmd2nq!qhGsHMCDM*jdiW07>(9JTdaX|db) z#o*~APHDGc@X4WzMs$)2pTWc5Mo>qXXY_OvW^v`Q)IUw?r6eRub|l-jGqHsaVB2;l zZ}En3D@8nMAOhi9>cH%G2W)mjYef2Ek5kh;8PwFD#M3j5oB0t`NA|pHvgsZ_I9n@5 z?7cVycDAo|!TH|=F=mju*-o7&NVUc)L^_7o$6RsR1C<0xm&Hpp8&y0~K`2>Y>E%{% zgz$IvumJ3SF-~(82+Zm~hBZN?)_Nv=Jxv|8Y1;Pjf}T2f;YNl+U4@(wE)BQ0#~7oe z#9Xi1FMk6CVinH)A7$u0%EIKX)@yIt{(l%go&vy&5w^nI?Z!Jr1kynq0J_GkSp~bC zXen}ezlY0SiW(?NtB^-0jlAuPy$uC#aR7{{Y&wa%ouPzbreCj6!N@;;S@sArh-;(g?Wz#@ME*eww0Mau~Ey6=k;f z<9t4wrt}(NOAK<+m#2_*5=BOe{^qzlk0YJ2T^(~$PZX=Pk+sm+8yoF~mX+luFja>G z2Sl3SjT~xpCfoKV{{Zia{C^{+tfxLZOxNW3+OA-)Q6ze*rY_PQh%9$zu;U)RkX2=} zu~n-{(H}=y7V6FM@$-)gqo9+nlB{guRDwXhn_s>?SM;)$Sm~a3X97WHBe=&r)=2$V zM@snQt)8orREl`!iRrqm<7PduT_jIEQ?!fLv9P(?*22nKh@p*R(Wq#;p6GVAEnN34 z%HnA%=jmM77BCYhku=>dcys241n2R~J@WZSncW1Jv2oG!hqS_a@*qgIJFE^|0nuOpCzKHhoeI zMVCsSkM!XLHBmz}a!E>OL(R8v%|p$KA^rpqB4H+z74cgNYbT~SXw$ykZ)xYV+SQa}WNM#r2v{V(+G9)0R~ z>1s0=vc%HP9ge_`0L3KZ8=D~RRS+D{HO#Y7PBSXGY7HzxC>BF~3JLa%TGag!9E#e5 zfYFeq*7JWL;H>7Bj*m2}rZr7ek-G$%D;}fYlYZWCEC9JxXT7#iTEJto5=ttLX(xLP zN0*|o_1%;GA+7;GADkB$YGzhLxjxZ-#gB2@0aG%vG;4ik+gKblrHU`7CPk1+8fimaW>*XqG1LjFQlCU*#2HC4HOIGZ zKR6RHrKU*_N}x+(eZfkCJnx7`x+^#AZ<6|=qbV^*6m!xV^E!~!~8n*dFS{{YW8FvAqw zka;@d!Dp9QyW4%Y?SkoRDI=1dCRwMaT}DXMh}istzQ=4)(_m6{(Xqap5&;K;e0?Io zUTI}X7DAuP38zS7x!cD(3@$}|1|1JF2&pRRs$N>vhICM1iH*#r`>6T08G61Hh#ap=4raxxK*R;C2}Ee4i>9WldS#g9NfGdKA$mc2gSEyxl3a{i}UV zVgxT)S>mg08Q!nMO<;sQAQzYjBPlS816SF{yuoZwOqOy znN`HnL_>($HwnM=k`Hf>oG{EK%ig+Arb!~D*brn4!_VgD{xG7lrkz8yX2#?IM)t>a zEvsiY^pPH#$w18$Fspe&M5KU+$zywrV?AFR#hA2J6N#ma>0+!IZ+-{HG~S`Cp{t&m zGP*-X3@WY~TbZ8!0C~Q{84S^iKuKucVbW85%m2 zAz~!mS$E%TU6T60G05^f(vFeh>C~W7j+(-6%Jv>H;ZHM!<2Y%1JqW=#IJ0M+CZ}GJ z&;o!#Yut|e@r2p;sw(Mf9#xPeiW1vWDmENaQ0?VYSOAA}M zH@*J=Y$EjzY-?&#R0UP7ZL#QiAN3qrF*8WsArK-YH~)Mt#jCT$Cc%k6%b|cRLJl&a+V0ICFETv`j*>X`)|H9&0R@d zm@M=ZCoV&vBw}LfHnArAo1O69Kbgs=*{6%sJ$5XvuQJHKx+x3A3ATfAr*1(zZ;ExK zZvs-hbd?cCL)0lGI~C*O#us{TJbb#EDrIvttl)H;bRyp3Z{He|>AL#r6HsGh+%P{K zvDNcbn#lA;;#FwIy42H=^R|Q0DRvA$x$=_#WFzd=E%{{t45X~wE^b~vp1xQl`1KI zqB)B?)GGiZUG@uOaxLc-tj0=yswSx!c#Usx1?X)?!<;uVlTG?y|Az9VGKxHs5aQ}aZGYGMHo#MZCbZo&u(xy zqMA6F5#$jDED-K4dA18&cNA!(`!#e`b64ewmc=7WPZp^JXn>VtYm!Fx!u-i->S^1c z1SuM>uui>hE)U}PuhJ4`)LDpZrKOH}6vo6MBTH;=;BU@7H`8CHvrUxCQ#+xOQ+pGA z@4pz~&*ZE!sdQf+K1$K!nSC?TR0x{rgo$-bmm8l4k^cB5w7IR4OGHDvymKOGQaq^s zk+&FW>N-rvBhHqtnPp>6zh~f zDxqnfLsk^b&bpY9#hC6eX!@+M*W09O7Azf@n{O&e5(E56Rt%;!eh|mQr z3$~H4{{Vi&=MnKdymNZ&*|D@~Ddg2EyMB1HmP0gvRLdu z{*i9jQ|Yf#<%yd|HDw}5ut*ETtJ^5P;{Gwqm&jQE0PgRiu*WHS=zC2=OJ7+)9Mn#e zKA9q?OUS1CTKf`l+Pa*&3EGRPI@5+Ih-@5<$hhDe<5a0b>MW~1&giRR$*7d9k>&9l zv&gD+*d9Cak1ME^rL4SByzgSgR1(%4pTO^lWAb@hU5`9GQXI=sNMxdthb)E{hBl6+ z*|mzBlXAfQyBlrB5l>MJRfw9J0n{G&#mWowYw!K>ep^uvhblCmjxaQ-9Fz5ItK#Rj z9#vX7v9Ro8mOo zvBM?QsXhiHFP9@r(84ac)Hsi0+qCuI|07L4X!Xeu0+gi z+nsM5n6hfK*CN`mRF9gVNE=@g1|qtSaNTH{bP}~L!M8Sn52l)kdh>h7>2OaK^EHgBNXL1zH901 z${Ly~xge;IVInp5Hrn_HHm<9$%p|R;fvc_vhDoMl6| zU`_4**y*&E$kTllR552RRV8iz~q{lIg%n<2ai}vm3 z^NviiPe`OwTd>-ioXb4SAdaG0Dry!brl(a)u?w;w1N&flDQl^-6%_KxPgJ_2xHc=X z3^(Jr!6zcW6E<0l+-?iMF)IvEV7iE zXH2n1F;2*3veGSmJJ-bX(uow!6mOy3FSre3V{Z01D_uQiVN)WtPM3Sau6m7Y@a@SU zTYGPA*jCIpU(qyfZVA2bzvmQ_X=swG+3A^wr#%Hv z`aS$Z>na!3T~Y-h2aE5wU>AGi$nz|=T6#)c!gxnQtT?A|<8p@0a5ok{ZH+hTFR!x? zRr-cp{Hz&#@EBQ1 zc{Md99MERa2&H+U49EusPh*Ur7IH7MbGAN_g3MkpRY|&nZ(;ucIOL`~Awu^*eZc49 z-LTY3P0(kk;L549Nny!rrmmtIOmj;QN!ww2t<9`%Ft-;=6RkW`7eca@(gT7;xwssD z{Nj)e{xDxcj52v;qLDLLnwgr8c-TWB3|+5qbRW5P-w*!)^%g~0>D=)%Pfy1_e_v3u zQ`bi)NomFX%hLkZJ~p;98Km?zb>ZmflP-rsDT!UB0fKF)o~raRWWI#Sy-G8h3X_>?A4?nm z05&UssACsX4@H9YBarD0sy4drFVCI#9&l9}%b8SDQ7r2kvjj3!NJMd5fLVwh)fc%w zF*(J#Cb}CNicZ$RQ0S9l3BPf*vGt=+#>HP&zzu-!z7Natdiwm}Ww~@eUr^3tjUe~+ z4;Nc=entkYp{l2^G!w%jRIBvbCQ=(!?P5IN4aqk`@o8NSeKQB8CeFlp)n;Q)mYQ}Y z;fOq|Hjikx8g+N(*jtj+P}66WvscqeQAHH$W{x>}Zt;V-4Zf0m?`$t3M3keeE2~(m z3lMx96`ILIC17m6j#@}WXbMX&QMH8{n)VwLb8g;F8i9Vx;HGNq#o?f%l0!R@3_zD< z1-+wgO|i`HE3V3FLsw%2!=A}lKiJ*;M^#aB) z;fS&m$>d_+)L&NSzOwZ_R(YLE8`jD~xkU=T(P3+1ZOff{8AdJ_dlXgyQPOH*IE^7t z!OEX-8=q~qx%~X#S=9tl=?t<9>$_Yw`w%}EVUp2QR!<_sS4m7#o#)j&;T4rd%7TBa z_Fni7IT2%&OBz0_Sexrh5=jGt;NK3F0lO(E6xm28xjSMv7qBOM!NQkNlyI#lR@^Kv?{&43Z&X+Bu zsEU^?hD^cajp-|iCR%1jBfFQ`~p!XT1qYNRYCjiUogYOo9D{{Xd)1&pG- zmS?n2mDfE3R#R2<)mTYR<)a0Z8(e|7;QWjq>lLArYI>}pWBLT9f&vy$%q8jSaNgA? zd-kx{T;XMKnc;?}Qb~PCOGpb6yI77zg@*VZ5fPbMa{V%F#YW(KcEZ&obE`#pK5bV* zm05K*XFVkjWg0_WrFL`-8q5bE5q-I~2&BlR&1-X1%4+HADYEEfs546IPt}PJ+!f8q zJ9~FHL=C;ejC8R);_Z7mlk{CvQ!PTn9Mj1n#Gw;g6Qyr^4+FihT{TzIQdUz#Sych5 z(G`M`V7R<_)sug@Z4@nN^`fNnHdnbp+P zNg7o+cBhG8T_HdSybo_bo_=v%G^Qq2jU+OLxGLmaTEuO>DAG^oz`;}$wRLs0^t7=_ zB~-4l#To;tn417C-+Ucb!jo*4R|O?jV+z;QH60gFb>>v+G-yubEx~Je7`H0SMp2sm zT|G4=a-eintcP4~2p}GYdj8sEI z0=&{R!3;W_Dz%Bbo9$tKZ`%On)o(#p)mLB&l#`}K$}qD4NH(~>`0O!$Q@QhX_KoOu)1n1sCc;J5s}A15Hpno zkcWZ)0NMWlj6uow4LkP-46FOncBMMFaFUL40B-a5|(P+VUB-owpZDnxweWSxu%IZE~lL`M}Uj&6dkZA&rx$ zB`y?oE(NR@?ia}Y@GQ|vfLt%PLKWJ^=J!5vZ_><^X;~yiVU>!+#1X8HZP?qs4XK%G zjTtm_ut!XS6Vd`RC{jr*-PrM}-Z971UtRR{w9>~6vgo6M>MkNI!)uTMK2F|n>4B~2 zUaDQzF>A2^T-bAh>T4^&dRnQ4Mu^2Y*J%uDq}frs55^;y(A4GCv(Z(kN#K#9l~uz4 zz}Sn6gZUT~Uc+N<1{_YE9U|tyY&+xP2<&#n^yDm^M&(7;!s7j|JY%)SH>qZcTq{`n zzy|&H8(RE)V=m3;q--!;l2oFlB(k?oGp%C}byC1wkDf6>4$+6yyy>;~$1HDqfN@2m zUW4JDMx;t-b||i2Ndri@u{Y-X4~!FAJXKY)R6$7%ByD0Lh?0r715qdZY%-HTeOS?G=ZY@0 ziD-57+D^gkeLtITk4OD4^p-*D&s{~7L7B}@K_R?y)rArp3y?te?s&(PLs2U###oRA zg^w7~(q?qj@KCK4Jdsqw%+t;#Kg}eKATRXkwYKNu9Wic7mtajr+dU8UXC z$||Ynrle~t22x9~1+Lcf*qaPunx8_cbs#qDe&f&hI322`uBm5{nV-ow{Nn0q45|Ye z4w0ob8|~xZW75Gd`&k^c-J;%_-%{=Ih7sj;Y>e$oI)NCL{WD;M+g1EuzA26dvXXtL zoIK0YW=3zRm#3#g10k?m-$~zb@y0u&4O=9Yx+|lABw(_hJ9fczCZbuyX%AD{S~ZPs zZQS5&=QX4brixQrs*pz}x^kaR_u+Tm-|FvtRp}hVHp^?)vn#5amQ_YgU;@QhS%)4m z!!I$;qlsD594Z@=8ht-pg3@RWxjIhPgaP)|efZyF@sCRuI!WYPAt`Q`LJ1)Y zBQbCo9tges{{U=8Ac8<;B%!%tqQ=;F92siSt5j3fNmR82xLE*0AB8q2Q9ba!YIO|J zx#-BDiPmqwAZ^~;V9E9lBTSx_QoR``_Q*!K)sDn@925A*q|xRvP*N>AGA@N4+wYG$ z$%;cPGN4P}+yfK8;P}R`)U)M}QApWRPDe$4p`|xc{q5jmoB2AUrl`|Yda3E@MM}oa z(*tY$*z;`bJI(W)+Guj<=lZ#(vaC0{kT%ovYvILzQE6hm)GL6>TXS)7vEJV}0;?!k z>CHq1`lN$pBv{|b#}Ap~PGA23nbU%Chkb&6pNz%rPfsMOQSOWy$51>U#yz=mT9>4X zqN_Wqdb!jkLc03`{0v}yJCj35u)PyV9z7+4FuOPPTHUd^&9mTV2pUUc7g3cP&)TY?Oyfm~Grf5sELf^|W0LJ6K_||3;OPNWjC0K(xt2;;) zR_50|?Y=Qt`a_rI6;z*El@Al2d`D6@;P)8yVdG+N?b*jX@eM6}fs zbzj;wF&{oYF^csadov^sWYrq!H*2UD+QY{9)cQ8FC!ad{T;fRS=_X(rWd~GN9ltov zdftYD4BECT37TqEeQ-$2a_D$B9DXqP)G^J$Muo_n;+>yP$2jzoOHV;TPBO90F9un|Zk5lo|Ao}v3MlroA$>_lwy;u zk>cBE2uk9hw#Z+<0C~oT)LAt?TbAY3kk`Xd^%3iM!kZx**yA58njSN#F|Z&5dwza! zYR^35OKLi|0Fz;F7-tsbnsgYoO){xSl4iVM25o|ibJz?-gpyK-NCenlVt+UI8Vl$@#uo%=oU4Qx-RmY~JL#jaFnu zO+`~nGuaiHfY^I?CxONkXA?wTSQt2Ua3?XaD%K}(J_qbhEXwO(NR&ql!5~5#Q-T=Z zYk0z}-l`!}su@5m1=*W};Na!Q8DU>zLnqnjQ&UkC70uGr@gp9XAO<4)Tw%2o%QL|v z8Rmt}w8j8+7uwg_`1?H>RUJanQ&XXKy6*Vg(6I4mKEI1Yz^W0yqi*iX&+J`c}MLW|_wdh6Waz%!tWh4?i^KUqR>Cew1HJ9>Y3JhRo*q8ZYwS%X<;F^_sJhMO@vfN!CB*3_lkyrvz_qFYfSE3S;a*4BAY{<~HDj3kI_q?pXU>u8!?`wSH zDPQUu`u_maxP3mJm!}iKC}*#VAY@`&U{7o(Rg#Gys!El8EKI)rl#RH*o8fTz*(E0_ zXxTAEEgJQW<9XnSD@&j$Nb@KX?jV_Wi`k4i*8r(gt^D6W}&QvO(2#z z2H|%n;+J>QPP9UfRAtIP`zj;BCQna(q*?*|(~rdMu`bxA=uL@zjH+HG9bz z2l|I^j8e5y5DAjt5(bmL-xxuaXSDS*Bg;mpfkxpC41bH%<@3C>b@dQN>Q&Uq_bIj1+a8hV&(#)R zPfbiE){(B)2U73n_s4=}wQ<%@8l-{=7=x&vxF0wtB#xqjDusCg_Kojr@8cZVv%@s{ zv{w{3s%DGVUZ9??do*{1>a&aL-1qKn#xoUJi_=3I#XNl{05g(EUnkBz3)PfM+YD*x+v!oH zKWMeF$fZ3009g<9i?YnO)6)v5=Z1=SV3nCsoNP$nP536(KfV~GNlZg(Wso(2u(iAI zkErt*s-kFSs+vUx>K)5yHUn$%umcchXrKAPnJw%rHb0DZ;Kdk4Q4Er|q3(0)Y_57b z80a#`rj{s#btJiNv;P3E!2DqrZa$(DQBUNo z?S4KmeH1ZG0?f_?jKpYGxd-u%#R=zJB=kp`hUm}JQ`gi>98=0ly9B#@_Qt7wD_QAm z{z@#s^yE;zQyU}Q8=D=)?TM^Z<|U3pse#mhp7<+2o>5UvM^Tn8T{SErCE0_|z4fqT z_gLG;7CsNfk#gD(&ZQQ|FnT@P(piV7C~G773VF)M4AKs(t@gMZU-67gu`N4CSz6%* zKw_pDfpr4vRe<95;PNnjX;`8vh$$wR)7)q-wmWws_QG7&YDtQ%H8)N*L49r+i`(39 zVYT=jzHv!ooMqT0ILRi7u5BDtlhQ>~XeYN)F@5x#f^bGjOD=Vo$CqW%OH)-SJt)uu z%s?l|QuMme&dVDp zAffI@Z}EYp&)|-LQOija6_B`ZDyH*Ndjlr-{EX9B8@x2q*1YKo!)c~fx0 z_8?x?z@Re3%CZzgYk}X!4$4}dI`~CwlZs}Lb;MgaOYQ~sy}aW1usgZC5?NDUR7#?0 zt7{~lu1A_#q%SLz?q(+B{%?*{$4Ij;(j==Mh1JzJCBLfH?S3(;^iErmW?rnPq^zrY zJhDm15;+=?FSYw#*1jY9pOs|USE*{K@;d(jtfP&FiEYir$j4VWtei#o-wswH5z$HX z>lIOtsBI>+Kgk!h$scyx>@Y2KQN=UE8^Pe za{?7qi-tDd#{~}bIk>z7Frm1GGRjrFa zr==*&^#q#`w%BPNahv*&a40NCd-IJ|4Q_Fh47b)zQBGcIo1o1ivz9|+sRrYZ0|`8- zEp|RQ?|lmM+^(}Ms*;+zo@geFo5ss#7+oUE7ⅇ(hFI(7vm2-bL!gPR%f#2)$ST; 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 z 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

&Ms+VcvdEYHIOiF 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 }UL1OA@ga63&TP|r+?hB>XP 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 WSV8H2O_Ovq>}%>mxGhq;w%>K*e_{ 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%0AdYu3_MiX)M2?C8!zIgJ6#M2Rv7N(W}x)rJdq0 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

!yLI?VRgwm8oD)o^U;z$IMI57 zSN%r%G3@(x*L%Nz6`5V+F$l|(8j?bjz$ef4%(>Z~mqDS{>bi_NZk-gmja#tvk^ouD zcjCFc$EjHsXZ5#wn~we6@6G1T9;Z{H)h4S+Ji2+~Hj+lgSx>klwnB8?hGvj6Zu^1m zZ_a1E?Ryy6Zze~Bmm?<2jhe>2Kc+hA=wql?P0(w9Tcmw4I;ee5OLcwe?BcjTCpH4+^X=;=K*kjM_fy2!8U?_74yb~P_p%&h26)@9r%(W@TWGmdPWQZURq zB!cPa2Uh@jv%BLWM|_|Hm=H-ljrPGJsI9C|9Pw0hHKeP?DFkD+gPchb?cKpPSN?M~ zsFBBxX(F>56)Y%`-n@&zdBEF+2EGRug=%y{NzX&7b>6Skx@L!`Yx;(xQKaeU(di+P z$yim5kfX(ud7oFQ_5CY`6!hZ}jf+?Eea}7e9++s0ZyT<*`6J&kZ`*x+CZwsbNIk4# zsVL!*sE$D%Fy1Mz12ABDXLA8;92+h^!JLdSE%KVhALG&m3N5D#9WRen;J3 z9j_KvjhfOuf`F^OYmJjwTbJRxE(BfI<6D zgT_w&+M(X>&Tv9vX)S0FD*5N*_Q=bAfe^VMcm(2)Hfzu43ozek2p=Z#^_qz_T9?#K zw|kpAR4D!j8Jo#;M<9|wp?nW)19JjECb>TuZ`5iVLNue85&cTqIAcQZzl?|6q^Yss zjL*u|U7dsi8kkW&_`Y*AG=7^>+C!`KAN1kGWgGBD zqWgUzC#(Gfb=7z8Ul|DPE|Ps*{*IO>no1-pDI|f(<08sb3$MqV`aKua$DzMW*L^ON z^q!{%mPmD4rJHe?pKv4!`$opwT>IzEyBA{2NhgwWJpms1z!GS3aB)cE?e=wtxI~`S-jIMqb3J9QOPiT(oH-Ml~o$+ST$8kNo(_ISkem zFTliEf~-_k0y*w6o#HxZ<5+getV>C|`hlty;<^5DUPpCBW+Wh`9>VVdAgX}Bcawec zAU4svy5g{5GyxQD^4e%pSf+QLIE-nzRj%CF6f61X+a(bIo39zveYowKB9lnE8Do=9 z@2O`}lKPYpMeZAGl6dFEW^wt+1{(8&=P4wv>Y20R8aqe^1g3c3thh zFW(b2s+}8YwJeDtZ@<%ZwV-&h>~d4KOdxxA!cDni2Yas>-ftvkcVGiCJ4FjUh5|+K zD=KvD`ajirk=Fg2Hx2!+>)YodG7D;;k}pl4VD(pegR&K2|e)f zW`;_g73FX#oSpz3@H&;SY%nxI9kbT@ORY`x-(Asl%`7@yMx#kEkzv%rB#o=KDH&$&>c>@^!f#xQVUdMNB2MMFEP(M z>KsWRKthG2P@;gY!||Tq)16aU(A|2LmwZ#TuA5Mjm1vw1Y$*F0=ifQ=w>o-11mSsQ z@%F#}0I9yGAN273S^60VO!an|4`Jf#zkV*W^KDcO6P0x$y~ci){{Z!8>Q?^%r|0Q2 z)TBK)%ecjVzkdDsto+wmJIdwR%U`*?^L`2WH~yZ#{ZsMDK0p5er~d%fM*ukrNf+dI z!6g|cYg&dL zXwl4!rCYHX?|lud=(;bdv>L9vp?z;oie+s?3j0im4=NAH&0qB=TlD_`Q~JB8Yd)Ci z&!OpZNLx<>KI0nu5_6pAep!!4{{URPK3e%xgm8JzHS>Ag@<(qu)#nmP?sI~Y3FCo% z^StMIz-V7N*~YleyY6u!3H`E;!+-#x@rXD%uSnW`JGs{BnsD$prWRehSdX=hUDvi0 z%8ZhkWoM;L#`dV7KNu+AZhkQbd-0U6I6V2y#&iZUnj<R93=2h>{Y<2gn zr0r^%T{NDIt09Rc1N|;69FQie_{LuOyk8?HdpP5^EgB6*k7JA&?c01^*Un6tMVh+& z@r;IsBJ+)4WYc8xPb5)#lNV43QM&ev_VJ1K(4tB52?pbRA!)HBT5>8CAD{NDWnaBEK0%c}8YTlP-EO>2(svERjO)kgn+nTL>H4+OMXQapdVNRgA~6P)Y{kesm)P-NzE`F%Z!E4R+BQ4< zW|E`Z5tkMjT{0}b`LUNKQt-xXVsxX%NP-ltkO<@h-!{l8tPVZ1JgeM}2{>0P7-fu6 zzWJ6#_s>DVCy{(+dE#Z^l&Py0FmdGbr5-u28J&lm-70fk^E;p?o2+bctgJZ6F{LUh zkH%;#k|2X)jKzalm}9}CpHU8?XjB;V=pI5?3Kj>R{E^!W4)>G$lxBc$(JdrR#Az!qf558ib@tuYKaR{T8BiLdvyvo$b zHF&uAr}3F;6pP}l;hGsD<+~Bw8yH90LEz`w_3x>m`h)1Mo6(@?T_-^&M}?YQQc_E* zwjb4J?jrjS7o7RqEXeeJE*yDo*TJv=b~l&NL96N-xb&J>TT`f)?2Qy#AI3z0InDiF zTLz|GGwAd%Xnh?zM&5%r`HTlHZ2tgJ^IT@EXI^tDRw1}5XpcCLTaMeS;_~ywvMA*B zV{;%Rs-9PxqyG642OJW7jLkkJ+wqEPoTUoyidT-=iyD>cbnd^Y(eyoEMR;J;Xtf7^ z$rF^8s7MIrz_0=1p4sE!IJuU#<_N_&)y0e|ipu4jEejWacg6-MSJ+@U*twoj2t4p> z8c7R~O;{DctH4x^q}369^NkWn0Ddr?mAZtoO{v``rL_bquJe6>01kb-e%QK5;+5P> zID`ad1gSfS94{XIu~Z`~nH_^wsH@y>&$pbJ@0qg}_M)mgqHo!JF>b@3j1mP_DzChI zfmoSsBob=%k!hnU9%l%pm?*MnSv&!lw>?^pJTgfXo8lxlBZ2NSS6u1S^lq`K>6)j7 z=F-Yza}0t+BpNisf<8uONt!wRXz3cs72@(J=p8~6Ce ziIq2Qj6JCW?~j~JWE;p){X~EUNz6eu6ezC4I~ijpaS-!yf78t6Kl`X$vt zst~zfS_a&Y@~6njV<_a-Q3G;U+nhn)KS=)orkei%`gf-KSLztPsp;D7M^=|k2TOQZ zY)Lt?-`n5L7UAK>a^;re;r^bVr)hfTk5Se1kE_r}GPa@weXS}3%7fy^7nAD~>2*C% zQ>f8te@Uj2R%oEzyVFv5Tk}KCM^^E)%wtWf;9}{an^7`B1Z^y?cB&w<$Jh*GH)lY= zf_Nk2DCSm)9ru*}lqTzs1Nq4lc|)j`phyg^VvLqlC+$*d$RDu&GY3jL^V=O*gXblA zNE$VoGdir$wH~|;Q%;CJK zlg;M>7LWBC>_-{s{Xo4LX44*u`nQbf)7LtM0Ew&b3t#EJ+2wQZmmcP`W?T|`@s~{& zrTToD^^6@nQY>uShYY}wE5Cz~ycR&O8LPiaXWP<>UY2?T>Ayp$Zme|yl5sEjVlAfE zWmz{y?>zU3l@b2ham5f%{{R_HR;q1GizJ(ODk=?%7l0^s^T&MkH2qE~dIUWdO%8%B zx}!PQJZIHEZF*&g`gppo=QG@Nl3t!LCxSR50|U5|$-;Z*0te?hSmqh%^e&UE>pI5L z=wlBeLh~>z1tKYCJfC252v4v!<)YPB$jw?EPM!@ut!2{s(n}u5Wc3RAO}G-&JMajj z@0s3=o%YDWv$zM3?}G1~)2q^?8eJ^Qs)AEG7BR+2XsL*%-|CnCgR^MAyT$xsb9t^^e*TjWPyHsD zeJNf~T~eiOzzwCmsICXe%z%=h0(du?m8#RM)=R6@B-12Th>_H25D=gN+szJ7bCUCY zu~x~fEGYALo8u?Yy_tz)XBh($6n7?u2^~zKqC*%JLhqd0&^m6ju64aHO3=mX>ov2i zF(~5QjRE|e*OOLlWo0VGO;|O?Q6#8ZFpJ3qk~tOam+BoiRnxkrtEOv0!Ku=_$11ya zWxHR^ip;BH@N)EBf_w9wo4PMeldAOv(Z@cX24!a7sE|g01ue-ub6kApH=*{9BSLzL{7;M=_!4-FP=if50P^wj|klXF zQqJ4D`O1TNoB|t=a;S4rxmqJUWmmRBBgxHLM1}PMCYRBS-M-z`$F?m>WXeV?(C?C7 z{AZ;B(M_RPeKwC+Wh`fDDm|NcUA5ltoSTTNuq2u!itUVmS9~HkzeZ&Q z>c7twfqM*iwDh`^np3BprfHBhmT-~74=$;*0YV~alvBkOsRNN$M zA89@~vws<@(yX<{TTP~ESxI3WMZz?!4%EMT$qH} ze2k=eStQd-s8GI~L_JYA~h4(yWx9!IC#V+|AyOPX%ljkH?G`bMP9|o`y zz#L-DL;S>6P+3C{xC7i|8GW-`FTPEqyCW*iW|4USfycHZ1l%b|70)@KuRP)uyd3K} zU(P8^(94KdCK;J{?UH2n&5SD~>yr#_ppQA8RQ4IY)D!K-XOU~SI5ElR$cMficC%5g z)JG=1X(utpu_JR1cE4x(FC&T=SiTEqmd1(DWVVOB`Y(zX6}3E3wt}!C1iAsRwY8KOu&=4-+%c1 zvLseeia4!m{>L7vaeEP!-Fd3Y=juhI9eiV2aHH4irPwGYjuwA9T<9ObE1w>3(fwJhMg zuHD7Y^^@1bY7f>nmNB^Wuo+yWuXw z%1PjcAQcCL-zbj=ibZHkI{~q`R?~c%^P2jXNz`=x27_5Nx@S#vZ7gdX%I=^sU%Ufq zY<505&p*XhILnC`!~md%1fJr%V*c6L0mEji-vBgy@twChV4*=bL$JvJ?T(DfZA~8) zmYYV<=(PH_ng&_R0E-|lyKDNFxVz%}=BeW8l9U=Yo&};>T}cH=2nhfv?po{eGFbE3 zfeTAS4o&yQ)5$C|B#_F{NT$`*sHykA7>rqobI-Y~3~ZC{?T~lxj*>b?=&eg2rXJ$& z{swe0G@z;@5}>mY;14*5z7ek+KYTaXRbbA_p#=No^_opqicuD>2Zln5;azC$VPO6U zAm)3jtNZw2(=%vWyjwRxg&UpTpKG?7Q@da#LiAyu{`Y4G2+NPq&Q?f{Pm z&*K%Wu|D`eoEOi0iN4}4i35z&syf(o@o4ocEOCGPqm9 zefd8*eygw4bqOMuQ>mX&(ZsN!nfDb%6skRd6e|z5vl+{hp-oUE_QW{)c+9h;LD35k zyPVJ;lap=@UHHvwIrgJNDnzof5gU>fyb?R&1Av|@xWd(Dg$v+hj34~6yH}HU=N%;f z04&GpD-yxCSKY^t#z#1LBgRG@p6-PLYzyt22=>GW9T}Af1MO}PAHGZ1W&L?hkZFs5 zk{q~NAJj!%{@I@KgA755EQ|Jl7mEY^@P0gFJuFG~kjIPsIF=4d!RH#e?|3ec z7|7vwS_iN|yWsocF~EF|-uUpRsNr`i-Y5q!j_R(fG8H9AuxR)r&UxBdq=IFY4KkYtK8x!w ztUCAB-CIqo`iH3gqIlv>I?CVxh&UHRFPABQj??31*Ug-GWRfXXSXq(O+OnD;;nkwU zsgf0yCRo_0kuPfidyDXKWC)=OIbv4ZilgoGo#!ob!$|KWjUYOn&W|! zIKcPskb9p<_4)pt*UzhiL!j1~bkab{tAI+19d_;j_F;ApgPvq<3H1!BDng18lY-YD-~vu56#{tv&^)c*j9`u_mYuzFsV zce}mU;=5*0$8ll-Ci~{N-4+_xXaQ(~Y~Oq;Y;rPruS+~IY2AW3)ovRbf`Mp;utz76 zSzeZDo@8wtP>3Q{jhLAQ?t7{SnxR}*zDEEexIFp7GjJ@9!3vJdIpaGO8G;oCgK%)< z6ii{0Q&`Gu)+I$Ij%?{#nDm`O9aOq;s7Rl2jImJF@;S(jEW=Cst(6Y{08e%Q0IILI ze%U;|K0Q9M)wSJcs%ko?P@K!F)Ujz?fHxo8GsZ}DBCh=AWI1@K?Cifc7Acgu>cZC9kKwA2F5y^V^fnzEXM4f zIm<&w$Q*gh%GqeSV73LG=eACqYeZ&_RZn6v1A;}}Wf0i>WZY>(mQ%>d0){0ZpHk?G z;<%&!`BY@|h$>6iqkIAQ%2wjB(I6YIjHs#yAeZcZGthk$k#yKK%`7D+W>rOy7X@nV zdx6gv-!(2b3A^kHJexdaQ1`KXWEZ&0!6U{=QbysmzDKq`H#-n;;*4-F50i2ogkaJ$ zFd$JPzc|sR?cZ{(E%IvqF;w>yImc1ti^E$UHmKSG9nZdS7mWK^nU!{ecv0A6C=@;X z=HkEt{qG-c5DqXIxC$2-WdJX=XmP-yk3ODVJkmijY>^Ezp9eRrqyXD@-^Mx%!CYgY z?Ub!YR#FnHA{i5U17&WkdtP?jZNI*0k}$JiP`=mz_{$X|!5Q0gOmg)#UJoEw_s*g@ z7BYb>^LTdrV;t%Qc)U`%psp!Tj992pq6oiljGHcLC@~o@tg^D=(4%9=8K9;qPmHmO z%juJK`NShOa;VA_5Z%caSt7p}$j45aJs0?NZnDUAWW=^M}V7tb`4uZ3O$@Y|SF~J~MQSWSXyx&gRChy3EoR^$ea^xxB+L+S?B zjR&K8Ei>w+^(9pM7rj~J{0?#Y{{YoG{-@A-M@gDJEK&5eWrg90B!0zc8PSr}y8{ltJ5;AW;m#(*TB0A_bybDt$E zw0cRkbF3PDG`fjc(xhOPJbM9|BvPzF6h?YIH(#ykdg%4NJ6ATBQyuJi-_Znb!ih}qrFEfelNaY z+DV`+o(5B?=(U|cQu;l1o^3qw{{U1{sa9L(vCL)HPCw%dcHX!qv^h_AJR+pU!@oLYwC`S zIgv{lHrX3)`yTb45hALGe)&@JdVZL8*>|@1yovA56Moy-lLD~`@089WKSi&9tGI2h z)y@9>vlVfy=e|-E(-@{RNg~J;dO=hW#>Iks3cOYsJj<-6Wu zWPvo3E5x!(B`T9+)8rczdA>y)#W54KF$!&Of=4;iM$x{;Doq1wz>Y=u!GCQ~{NOkz zDFIkeB>DEiM2pTiLpIX3c9V7D^9iq}Yfgpblk~cI{YaWBnKuR8D{%h+`40y&1C&P_ zc1^sf1gHk7i0_k8Z9}=^EH?Fmq^klrtoljyeRJyLr_)c>X4cE4jdc20HClpLAOzBr zz)?qw9maW|QS?rs^#1@*{{Ro_QO6#j#T>MfD~+z9mz5-TBaHJwBep${FJ|0U<6=O& ztMiC^;~`zv>R3_Y>lY;I?UiVYMGcz7 zwJE97$k9z3hetz2SQ`XaY#NEB)K2rwBS|cbS>276C%Gpj331-?D8M<%;tNCL46Y+VNT$p#VI2k(O* zAd$u#;M^YE_p#eJ5kyuES7#si&gb{fOmrTqew}})Mv=e66H+v-s03{il`I#%-2!qi zHC31rsz@A?2W(z(gF1o{m~Rxoc^G9=6om~M^H>Ze^PSIp?kM-cj4n7pv*RhISeim6 zl*FM)1TFJi58UDueS$&`(ntsA{{RkJaeZ#3bqfGZJTUrE2+52Qyz+N9J^B9GEi{qG zsfp!`NQ%I+9?UPcPYbC~q>=Xv;N_DsZQXi$DY{mtuQdAflSQF-ciE*)<~|8k0goc+ za(iYt%o1x0>dT}~OCL=I`Klee=6trIX?2=|B&>nR0GbLo6gz%1IgM_?m0jUk{WVy} z*=yv1{{YTKPc?K7E&JgrLWbgr_s0ck)P9yDG!c=gA;+gUwG=M+KKy?fn+GO`u;Vss zv@+=;f=OkCy?Df^55r#!A*t=5x=Pgna z>njv*sGc}<@UuwdA|irIH*P*j$mgr{$5e-;_1!m8)aYZ^Bqw*G?#KzM2GoKGG{NX1xtg2ZIYN2U?HgCQFaV(NQ zjs|LU)b#ymS>Q2G69QwFKipcJrNxN@gU$kXF=Jex8S2EH6n#7EjYgn#l+^zK`dTny zc&okl+zr?~Q5f>INv}Ok5ytL29@soq0>qMk+c=*Yipz#N_Q~Z?4fIWm?lI#?`OW=J zdQ$0hC#TJ+l6Hpmf>a8rCgZ{oc^hk!T;5}4kJANRi zs3nICN&8R!ar@$q_AyOyx7(a?%7JzVk8ISqsfF&sI2BmYEPHpz^f?HX*Nku|;ALS%sR4zKJK9l}3!hC~g28WqPrYOSC$>Clq&2k=I2GR<`!?*sODq87<)k86Nsb{H z{{Y&yYKJ}YC_eBvZRfwXFg*9oMpROE<90Q8t6N1L{qczvz^~sBVc#wyW2bFMXu_+7 z+qiDCXMx_h@0r2I3~GU)iIweW`rfI!-kYjxblQHR+I2zb&Y$JFqU`~K*Axa|kg=JV z#t^Y5ZBQ5Okw>5?3kL5!U#WWcQ2L9gj-k|=6F7+?xT09b)KII!fI9)s5@l<8dCF@j zgk;a!NDLd~VNUtELqZZc2a${k-DLBL3$hcoi6D+e_r?LX?&gmdnoB1puCl1bZjD%@ zkce(-EEj@#{{U>PWOHEl<0iPShbV_2hc2#m6$gSrJbPxFm2geb%Eo+VbYjPI5=rh( zS~3C49c5{Z^O{{yNwzsRtqy9ubD0qHmyFo?#zy66hQHNyTvxtT4~&Foipwv2WbL@J z9>?1%yxv87WuxaCHK=vUA+C7D0`p$7cGb)zXrhRORH)Nv-~n~+Yn;a|jD^vEvFa#!+{z>|!iYqvHxM zIARu!lKYWM;FncdSB( zHTq=m1}4&`!OF&KBQ0dgjMmABQw(CV?8Vudiei=c%}D22V@Ul)^+RmGyKj2<$;zOy z*?W9ndd=$!G;$VKwNO^O4t(Zw9p-G-YZE2#c+B!A8SLu%Zn6y|TB-E1#{}2u$jCyy zccbS#_ge9lj&0AzdGqukdyhfx?=9YY2HkO=H^*Xl88#-aZJMrCLL`%HiaubRe- z>Hevy=z5;F4w6Rc`e_z9rvvRqZovL>xg0MW~n+JT96 zEVOtAx$%+M(XXh`??(keU~22Q`5Bz;%CJbIaiL@6W_D0`&5ok@?U0c;y2l*#kXkT2 z@Ne58BKI|$+AmsUi%%Sm&O)m9C69q$IT_=dO(d{O8IDCI9a(6p2a(TX#$%K^!0VG? znpuA8I369atinXH~Q9aIHE+zOwekROFhUvK<+Gk zzTY|W#_rBz-p*!G{{T=dNdv}u9S=jN>YZ*a6Kk~nHW^%x)M}XQh z)CAP(e@!0NCQXI80FWqqnjgMEagb7bSzSFrAPrlGV_rs23923M&JHWFyi>F5{{ZRF z(>)jJ7wU`F{-)COA*0aAsesOfzfmCV8rPF~^BUPS(g=S|r}Q?)7EkjFZ*MK}e%R}! zzL?tI)#ca&6o<0#^B~kTC?< zPzSbH=`|xo`qK-IIemgcKGNR9-y5m6jkVIAo|8wV-TqR{!JH4002h;a=6DhZmD)JT zD20g%PkJ9X9+Oi{4P9rk^-osm{a2|&s_DHCP10eG2;yiWc6D}D0i2dMY_W3 zMxg1V(MaIM6Bbn!!D_Q(@H4fO#i`d#s2X(B$0L?lV!f$-fV(s@P$J1XM)Ism%mH== zlzW=XrT!FF(|3L_X2a7pXQoTk(mnbp<`5Zy6}*B#uKj^Ij~rOjrqRBmOR3Z>kgDm_ zGb_jyn#dn$JhA7;jEqhYl2Ka06nt3kiOw{MBxEdz$VdzU6~Pt`Wv}pAnYm^rg$q70 z{bw3~LwtcZZYjI%zk2t`<1r+XPU3u=mb7%8biFsJY4!0(1g`{v)h3Kcj7Nr3!5;@9 zG=^DPQ$Vv4Komc01@1LUU$m28a&O-j_^KbAv%O!%potzSq;lXQ{iQ|y-T~*gd{G>& z0hePKmA00y0X{!G9F{f6BdcJ9{=|&2bovqWcUu1d@I&>Vr5E^5>pw-m)?NOt-PN-9 zRe7qtQz9gr7=U>I@-D1$4P-x2H=*cyujyUvzAxUfSOSJk$`0z< zJNEB}e$4&^?1ok`DO4ow1la==lGSAKz`O$Rp4WW)VhmM8YE|ux+ShDD<2ZAj*zz($ z!9ZQDWR694InJdVs|p~|7%;y)fCnBjAUor;GXZC?p^H`K@b~z@F9YdUNvK((k*8q3 ziz|StZ<`g*AB@k8hiNp5)+>?Q=La0|kq;g-hsJQPJZEHv%~kQ9mqF5Wy;;_OiS;9) z^t8_;6$z8plz&Woe@*-N&oxw#Nb!!U!X|x|wg&3#d*Mt9=ZqcBcbp^hlPl<-Op_C; zib*5dzyy7#bseVY@C_VUWs-SJvr5kAyE9olVn)GOIZ(hJLF{sxK?&5y`hvn8<9S~7 zeAmt=_yd*z4*6Nxm?D^wJvG=$kLu*|ea>5~4Fr8tRGls5lSCyyr+JhK3V7TBpldUv z(yT8CawBpXmiQ!Hf4RqYE7wIGv$(wP8P&&Q^M#CvBgnk$J56009&?nCYsL{M#1q2p z{qPLnEE9dRl;=pAI(Dv24zV>+EKKQiB#b)p2;?3Qe3s(EiQsZ85P}+rqvU&K0Hg@Dsw>%SdO4! zeuZR$MOmY1kjokoX}YXmBK%{CnLTBQ1xZB}tK#mg{sw3BlwNLuAPYYktWwcX2^K&e z=Ny>*AwY-RZ`!!8`N@oL#xgB-H{*=u3S0ujFTGxNZ&eYGM{erlwlDU>{@B)3O023C z0rw$dM+6?+_sJth7MVz3I3W4IKejNj@6J(pBM9%Dr9d_bK0ZEkvr$F#q7x-f?x{zW zTQ^*PddjEiy+#budV5aN0K2X)_ssFD6=nm0-x)`_tgy;{3E%)Rl;p-#FZp}cd~V3qR;G;}4r(kE%OhSclC?+ocIPb_=OLz+NQv~^ zt@|9J9&vP)uWrz$$J{T2+bV|_K?7=aQR$-_q?Ljo+y~rv9Gs-lymGR-gM_@ooKz};8AvC`d}JBYG|{J$ z6w^->nq`tl1JjVP20A>@^L@F=g}mkMSw$JBoU7x$I9ulmE;@tQUpX^C4;Vt`oferU zk(No)01&q4x+D(Vc|Ea8GJ+UaK1VplNC7_iMHj_>_|eHEYRIih)nnfZuala9W`suD zLx&@|9k1Z|z!!{gjKGiy1HZ-)yk?o+)7yY8-deM^kIrf_QQM9oa}!7h+6V{t#sd!1 znqew+1S=35gI;kq=9UBvyR3J`0E--Ri#3&(;}>AQdCk)4S*w~hhWCRgL`1#2)@@pn5E2t;644GOtnwc*VlPqSnb$-9qdY4Gi zbiHP&rPAm@8gw9ifcMV^Rjl-QP~oFi+_W#p$jq`Q8JWgTqn(UX#uU=9VO_w?b0HQp z({%t4R`0jQXD}D>+|c~tj$EV4l}d`|kbK>E4#vW;$;brFbV54BHV-p4*tWgu2lIZcm0J&Jur&^3J@OVjllnKaJ2{W#P_ zB9u7_Ngr?@(s=B1#-4fKO&*oyV+?Fr6rL2CvOj^ErMBUTG&tunx*P0HIoEGJzor*c z`(z|uJ7(=bPgT7z!7uF$Y>dq!^TuZ!>oS%}RpVwxQV3vZgYSv17pRo9-n$kzEt9+F zo^sa6cPSwD;~S`Izfm5ZKhwb%p&zGlr5EIzZ*$M~%wJlTW6N>P26&`dBX(C)VS%C#zIqWPa67i3KI0X| zwurOZ@5g+_DvA_uj(F{yZ2ENDd#P$>o^}sB(n?eX1=x}R@z0EsNa+lbsry`0ar1dX z2+6Rl%$U^WP%F+YWf*gsI$XL)I+mJkewnp8RFzt2RrVPW9#l7r9&qH=u#wIw?VCEB zx_R~biL|u-qA$DCTi{=s9&;XYvnxa(QMx+e)DJ0M`B5~s1xSL+V1rR%+VZlj^-+CGm1s)f>NOL}Qa0V8oI zn!n#XbCp4-{WPXVkWL8nzyoO>Z2tfmnxcuU)y1TfM!K0-PazEP2Rj&!#E!)CjjlRh zLrR)r8J0VhwiP?cJa;!jx#J=FdH(=Rbv}%qh<%1lzi#TfzkHY^95BwEl!i2Qki4rI zRkZ{Yyx8DZ{QG3Ef>nVAi1UbE9W>Kw)ul67S(83Bd!&zkoC0JzDiD|O^oZ0lvUvOE!-sO+rD ztW|j=4%sj8xkW@UyR@NQNZOQbzxu@UjU$;GaS93c0Drbfm~*hE-glE#z#dLoMn;lD z>N#d7jmMm^iPui5`f`PcVJleR_V}#NW~$A1tkUA1zHjYWvq0@pR0YIxZY}KFN z1u>Wt4A!iqqi{zc)-HAoLfUOantkRVw!(=j2^`-S+qZmK<&tThX;{3D#hH(CPjiWb z1*^p|k54pWIG9U7vnVXIZ-e6^D=(sxPaKSO8gn=(%B#GsF%%p2JlC9tOX=9+X+$Ow zBZ!LzDSQ$;8vXGAHFd^zDcMM3GH)S~b}3`IfWLw6a#(rEEe@Vp;+4=Q*i(Dk0BXSE z`|^7XtV2S2bl#&(HJjJcpgl6Y?!PzMzkJF*aLKU9eLcZ1?c8~5U299&6 zGOor9JutLv5ncO#e9dGjA*%SSU&d6Tew;G987%HlyeQ(0{{T5Co-?~S4D8Hk3nuvS zh%oucsSBbkf#O*UEh!|8rpF?`^PE*XSQ@(KjxH15jF!5IV}X?I-$W_m{9wPF?-kAn zMhoLRvCUmGr)zqqr0KL8X*ClBX$nTRcB+}XsisSAz zUx09rQDbV{BPN^JoVJHi`fpkN7$)BvM^dfU;ai8u@N#STf(1M=+rj3sM}LeXz2L+O z&};9`ZtMLEP80DO3~^Q0$P=&MB>f;Qb%xPofC z_&G1>Vi|cI!Nb)WaT(D4}o1xfd z%`_AL0EX(dO0XD|q;6f8cr9dY2cPttBe(CEj|U}80Ui$`@t9JBVvmc?hG}6eVs=8x zQLD&3#x34Ty$+M0gIA~4&8XHcku>m^LeaAc9{7&H;GTIrahX~)jzqUvRDjK}_&vwx z5@dHxKS17{eVeaw@9pFF%^KZoy4`22QV0top;(Y2 zQh+%F@n_uTPmJT=H+&krTB+iNp^jEq*@Lo<#EYX}x&Cr%MDlZ^a~y%dQb)M4fOo=4 zqh6F#X$MA|PVB;R>mpP@?(s#BB7r@u9y0&~-=7#=%PK7>+A6@Je?Iuyc|959mtsk= zgKcqYX!ax9H6|N$pZIfhAeTkop$&ALbx87UN-GL@4er~Wz@=N)XP7o9Dbvg z*&5QVp1^n*Dvcfw&Pl_JtZH4tincY$p^GlS=hG}x#_st|JW@?ELmKXOV5&Tldkk&H zLDyuAjYO%VM#ep%k&4ZGlac0N1QdrsviWgGKYjQ9n z_9HbAF}At`#SH8VMTMFd$*f=j1nvTd0Qt=e*p@Ek*zRI%ljjLM0DEE#v#dfW%1F%Q z9qLws^Ti)IB98dsi9DOml~EsRNF)?K;0W>CoaQ59s9Io6o9~+A+QZn}=J1Z#vHi_f z2g$-QWbu{UH(uX0j5zIxq*oy2w9+hdER7OM0kW@je~e3pFeaSlD*Xar<53Og}AZ#=%K2AfN+k_|4kHL)QZXGJQl$FUcT<*vHA z!w>j!>ISI{#!{Yz9l>h%UuyC1h=%;;c+VMRJoB1vR;bQd4>>56OeRlNLtSj<{{T5f zJn@WmjS6_pDI@wuFzv9LD|{30nNzoX(hPeOYPRjCcRkJJTjHt`SKwuMBv7o3ZT8k( zD!y`#urcY8+W9?f4{{WIQOsWY8 zNgfFt<1G4TdmvCTlw&IVV}=pGj~I-+ng+8;Lsy7U#ObDNzHfq4lR&J0(dD*I+x1seq3dNQXSvn;46 z;f`FGHBTgf2Vy`JEcVPAnAsYbSDf_bQMRj_@0e#^_~tzP*z~Mp)i2zt8|@{#0C+wQ zW0?9<6z4J2wt_fQJ@-oMpmBt5N<1@I=d+L2(PuKNF)b!4noi|KX zLh05*;Yd&kv(G1=-#lqo*yetm>o%lD&vD1GXmDEju5&Z5zns>>jes%|gYB84)<+kD zT`P!$i&80F&9L6Vb@zK^(b_3iRISQvSikL=-P{KX9>I^ zL;-M@%>%{O29HM1t0|~-MCJu_wv+CvHNZ8`?VRmycEWKO3iEgEl5jA1)2xLXQ5skv8r-1KBDv{Pok}wtC)WqAa#U6MS&%R@o>Ad~2 zU^si{)pQp6BmV%xde8XC>SyXA$^IH)+vrt|+wYsJs?K)d<2K{v6c#&F62#B|cg~hK zidK$PWOOvj!iYY^X9g(Ykd+|VAmwJ388p_*Giw?~X(ea1Wgu4-cX!RjfuZm!o{US}kp{?&s!AnZBVP=R5V!Sm#PGEG|UrL5NJbee5UyW-Kt zVu5H4ideB6cjTV=l~}6=Qb7j69u5LaHTgcnJ&k|UT~|f)zJuyX?Kmn z$y=>KvD|V-N##z#-+!p??aP+$yU*tWOCm<`tFoj208j^p{NiIe?W?Xu@NXi~tkJ%j z4K%GZ4mA;(6=RSyAhIcd{*%XJ<1=8xki$q+*45iU1I^j@#Lmvhi0|6)V{dOyXB%$& zXQaBWjy{=fA?ec|u60|+v)B6sJ8Ndj?|kmR+aU8w(t~54V~d@H*fd57SaiCHV3v5` zk>ZNIM^z*x_Os&^VRnJ$8+M@p+W>j?9nWmtq3fMnL(%o!BT25B9R|N))j+LFF2YLR z;GE0TpGcbcUtJ87X@i3-alXc7@&Th?#z&+9tl&K3NKnX1DGPi8dthQs3+>w=`Yxx^ zH9t*gx<^z_rPB3H)U>ZM#SDuxKnW;TMIeQ4=DYSe^PJ~jzd6=wuZ&Z(W}NB2!u2ZJ zIb_uDMoGic#=O`oT-}aujE0G0ZB$Dt$kCS@l|QJ9=Du@p)O|Bq(mJHNOX&SSGs==Ec&xKb{k+qDbFg7Naac0oNXzJApHrw^E}pl}LI7AOCj67Y%=ktobn5xS zaAD+k6?hX0J3$mLk~`v`gPrF)BpAq!+Eb3A5DJgm1s z9KM52+P!o-bTRIc!jDowi_g0r0QtxQK?Gq?O^^>9pNv_JI$c51O&}3LAYf4XiWmr@*JvkSq{{VzJl4*a$M3MSw8UqoJ zY1~U)?z-Z=@V5+)JMG#>kbB>Jtn(zR3Z|VRD4STRKjs@hakJmY9@!&7PtFSGgNW7V zfuJe5kncd_YX+cw>#_L7PU=$ZbBTl+#z9)7*Xv}Kc_h_L?Wq3%_ZEH zi!?meScEC_lz80{9fb!3uRX=qC65?IpBTs;`)6lgjBw<}Bc-5j>!0WL$in!?fccsiIaTciD{XMcL%}0YQYANRW6c!Tu?~*cnXPR3-+e&)_!X3> z?c~_=k;e+#H|#quuDHK!R{o6|?{K@7Z_6Gq1BDUUU6n})Er=b5YzyBJ^kIw!DfjV> zd8iJ(val+=sIMT6S%54>?H>59$kpkoQ9uw)^IV)hoHQ~I0K3_(&m56|EriN%7M>KC66$}F@28ghKagM0lF#SHB zIO5hM$vP~OB3&zTDBqF)068VG_j6e|p_SlymN#hQAUgrV-)lZG)!WnAxb3^E;`35B zD{K;{pz*p5#)`e4w!Bx)7~3+%+rC*(IjI|*o|4NE>FE%HAt;oSF5|e099L?%0>C2K?5H|h?PY(JzkU#2}@QopawSB}}{Msq|*7fD)>@t26^Xh)W!JWCy<8GaoT3bp zcFQJ5PPwj4MDpq4)4b9=P~WDE))x6RJZ5AZUR6eDQz2jo1b}!v_sT{XD^<}H)rI3I zKxLvpJ@BZ_Kmc>`ltD(vj35#R94{TQfW@5|8KjY&&9fUOs{k1EPqq~m<2553tF~Cb zp4nX_QOT%C<&;8ek(zdl z1u_+osrJPwUB+rCaY`EGV#6UzyilMpC3g>F=QT3?VlrgOI);jU46%BJ13YUg>~Pc% zBe!Gmo^*-lJsF~93e2Pi76~2mE2+%JDqUT)GBhVcr;-I<>--ZhHHO;7~r<m0CSYm!utJwpHCX;(U__(_6UdNi9CFq&n~+Y^zybAaH^z|XaVOtQ6i*m0zeSZ z5*Fl^1orm)d}XG%<#iiWZvOyJHAlhB@*T^xAT79^+}ZfdH7tydyQ-GFesWU??R_xZ z54z3S_|46$)UQu{2#}3C$RuQ;%K$(={!U?+lVAWnvj=)YR=XtypKdE88(}AEwSqYv zvu?I%=Zj8}>0*S(9>--Sq$2OQ$C9&a^+rTr9&(aLot#>Nab1*2e!ySnq44;hx>5UR+fmwJO`Zm$HZz>op8 ziJ^9K+PM-!jES+ffc=0c;17z(esi8S3?lRa4F0W~6gJlt&-0P(eC1dV7I?{?YYc8u z1uRVqHF%_D9YAswtb30*6kAnaoaK6LA`W)TLgj*w7v%o{;l`FnW-M6Q75jT+k8X1| z^xQtBB1TaZ-l!@GJ8F$`KWsWZLDK6X)QIAg#NmQUyOF5f5#(gCPamAaN7Tw@o#rZv zU5gb#N-x-W89XNk?Uh;?*nb@73g*pb3x|8nFl!ztgliU@(PO#d3{9a`x6x~O!j5u1FMw!)GJ0P<>w$;hNnA=?_4 zY9^Ffp~*S7sL#_S>G}w?S}9=E#TyM=uP)&;79D@P14Dm74!dcO04(P#%JAfZ;@j(7^`%|}{3aiK!X13X*;_%3! zUW11eITu*~!JUNUop>{|u?<~5H*I2t)J3Lc1;mmX0D)t-pKROpU(}s%>W-}aTd(yQ z`i8aDQKp%#h*3a(z-N=4luoWQfuasEk<3Ce$6zIZ`9J+)@(xgjJc6ldYz{~_c$wKD ztRR!z;N;iFb_7Sj%6g%~DGMFseWU$H#~yN`00VFhU|%>x0LN$q zQ5=jjqa=hl0lSgy@m@1$Md~A_YMy`5={0-dc(!UI-lKAr+z0;Qb`OFE7sEZq0xKHi zFxNhqZ`%I=@M4o^;|MGT1Hr@qW3+%xdCd`N_vq!+deN@NRoHIY{lAQX+7xXj$os$Z z_{NdZk}$D^Wm;kh`;DG|^V=$C^u4o~uNy%ebAI{C6sr={K;Tz=T!tM6<3L&V_seUZ z88s~0Z8_7%Kms!5sL;NCO`M*dS#QUtg!D`JE$7-fk7kyET0%LlDB=tfqln( zldOtUbUa}+SkU7al4}@u=OJsdrHk80?92rV=P9CH8IE(RmPB=q;Z$1=);K=GubhjJ zSd|nB;9qQ(V6rlixDjH<;{Z9-cqg`S;Nt?OH&sSuAzWIM#5p=6!~41VgkcHD1^&w*_Pi#k{f6Ae6}vVinkJ+&mIIVx?{XngsXu$~R&YRlZ0TN9~S>jMo)wPM$wnNa75t?$vJ8qw;WyS0K@^ zZbjsG7NBZ?p~fc@Z1WKuilr4q)SAJtt{4J5McXf_{FKo%P6fHp@5#y`fmmKGKX7g=z?xhg0V z!0%a>4Wy9mw~jdOeY0B48H;Iz2Ng`FyIt7wPanoElB%TUh|i49#xJweW;UaBilgn6 zj?GZ#80jlw5#KI{oK@C2>C2+@p$a7R3xpfVs;lo`7$a<>q^+>IQV6U0%SGJt_{@zZToYSPOnq)~IYs#^TCvgYcwoy17_RPuK zG)LO9g>(Y4(S@)HG*3KaQ!JUJuuxxWo-T#)nj?TzyQYmAF~BGKxvCYD()uC)0L~6p zkM#Gta~Oxk5fbpUn;j(cEK zqmH8)S2Y--s?X-=c{s|WJuj!aHn*+o*F~U=NihU~SPlU9Ii6SyGPG_!6Lplx-J8_xxblmk2$u?8%zXv*S6CcwEvD0xrn- zz*CE&kc*Oqr5Z8&AGb&y&rVY;FR#9@n0Hs5gDw3*_gkGWQjpeCt=n zFDEyjsEluKOz%)l8mk`p=gOmX9^Pv`IhVd*opQ&Un+u{6!*vd5oNl?M(7_}FL#;Zs zcopO(GQOLU_a{B;-_99tZBV1nY|82h8HAk+b329HMUpw9GfaYs zIi1C3D_V?ZF(Ue*ieKCm(X4}T)(0K|%g8bkXFD2|7a1elEW;#x@tpa$6Fu@oW}Qo{ zju~TEz)&e~!0*q#L5G~fq)CT?ia0!r%QG=7Yy!fK42jHE)tV@jv&AyVi?_epO^Y-= zhilBPG2X9^**47Ua_W-$DH?iiBxX;%6s?B)4}LuS<>Ttwh1Y(u;?m0}qG6|jc7d~P z?W(Xm`}3Gje7>Wn>vZ}pKDK=%@#(cYy$M+eNj920Pntg2$CTckWxq-3pj}?RJv>ga zEV2bwm4FLpxjE>4arFEBG4&>^qRG@WI`-3$4Ei~Qkv{--LWROVV^!jrLx`r$V zwO=`&QW>mYZznnB^|_FT6yv&pXfSR&jOfriZD&*;`OB=yETxp4%m)j{xyd4qR1me#b6n>JAlUDM#qvLltNxfv z_K8_Gd7yV!`(O6K5=Ra}2&q5{Uq3jv<2ZNZ))d+evhE&0HIfQF^OmoXd}q7#!fd+Y8W89d|PBceDr-#rZmw?n7tI{hxArbnsN>H-KgEh7V`rm{dB8ys_lgF7#&(rR?c44RERu_Wp0L|sS@wndL|VB`^_d8bs5x-CEG%rheGaYu8KZpb~2 z5ID^0YWU~J#(N)3_5D{u`j18R3_>klvrDL1f@-4@M&)RY!1o^*TV&|GhDTQ@!)p%U z@rj%vLpx*1)Cv2;0*IxiyfeWd|qjC&FEdX1r(!bt_Oe4 zQO1~QBPjBnL1y(7sM-e=M(m#XH&U!aRp$mE5D5T|esVw!8_!Rzb*cKMqB*3}=wLC$ z9ARM7ssa&qf?9Ha;E{BHxxuA0T50uq$aK1BCDctTgpMfGvpFV!0Cy)EDRo^xTx5af(&`~| zMOdq$998!RC#IflHlZ~dd0r`FV94my%12@d>~ZylOw3YqIBIr~trEmp@A63b!wkP~ zOGU1m{{Y3hhJm`=&_eS?D9`}KH4H3oJ^@gC`{#}k{WB)huo^8~UIzFEzd6=ut(x}2 zal>x<<|A4B+0}nSxj4z6JS=y}C)$w?5?W*VBFD%JEu1!So1z8!JWI!kc z_yfSd8J&e54doRw5D|e;cX{%;py=LT)Jvj@d9=^~&mm9gQa}Z(wh zRE)}`9>ULVFj(7EZu{O4Q`7#Fy8GW1i0Tnm@z!gF*WL0MkqQUH+e~uN)Kekg>qK+L58o2dv%Ci!tFn`QK04CSK=7IA^Eje+M zn(h1Naqpd3ucO1G)a&AdU#$HenTg*xjJZxdhbI~{S>ldKU(<;h+KHtWc;w(5X6~7< z{{Rn@Q1L{r^1|^WmSr*S;IN`Os~qvk%tgN4v^(E!a+*4D2fiO`##tOe=!W|XZX&$) zz{GeJLlEP4UB4OF!~+_*QU_vv&NQll5Th2c_gd8-Z=9jS3FiGd)B2C-mfy1e-QBLe z@jPJ8y2NRd+ZbY~C4c~*frgFcC?4Yve)+pXdRpmqtF2v2&SC>d(J%@haI)a?M%(S? zvlEpyN*0A4d!KxDU@0Ji2?S#jSe7J`c_jJ37^<*Vpu&&H&K!}9O*#!sx;9?opfPVVo@5Wa_)HRwoHF|w6q2r5J7>adX(kNime{c`&k7@?1 zOE#HdX;dhhF(iL*Hl&e!cI}d{!Nta0W|w)*Jwde1B!8uWp;e7l?oIog4p<)m`*_NA zD7tiSr~L+|X_cTuF_ti!BtO)miLg8$KG~a4iyY4yOtT`A71SDUk$mMYn=t#HK(MPY zOZ!x}0GhLcSlTIGHY!RnQdNNly@>CKITkRvQr~DGQ0{Lu%SMJ7wOWYu@J7;0D+E;n z%M-u@H+@CW`lr+VR&JNpG*f7`u#xIRD(z*lV#na;pQWBnJjpx`$t+|turxs(j%@3F zU#NAyqc2hF+R630$w)6W&RLs^1U?tNv{KA+a=w5RFrko`I+w6VoFd5oU4tNl{*{X4(*_|1=}iV3Q|J+rU_ z#v8^nrc=f_1OZ}!?UM$wu}37Nh~sA=OAw;y4(Al$MPsWG$Agpo?=^mpQo01!G%hsH zA&q3N3xEk9nj;;>2o5+oG_$}Q$nKnvS!R zY)#E(ggK2E6JPdSi>Dq0I2x! zl}p+WHQMtmP2)7g!?j*5O46OO>6zm^W{CTD%{8m9Y^r3}wrPSHBuO0>)-h~-h1M%z zq7n%Lz{;p_mB?1-;NnIPYvU}IKm?i{zA=o~Mp!E-JU4u#Yit2DFF987s=2lvpHPcW z({+eCUr{=hF{v`5l%^w<{j(OvBh);D=Q$T0Mp9!kWGb)5I;K-)F%}QV$5lD3NqTrf zW`$2-j1c+6R&bZV7(Fxbnsy8!1&$6Z)3t2n&e-bCXt z&NJE9vdHDcX&(S>TdoIwbIa5y@N#*1yxB*5%P8Wrd#dg`$pheCLO4Bn88@mOq^LLd z9||kU%D82Dt^qvu$g+Y>ouKy3av|PTh(a#!P&(g`^;c&uZzr+ZS@Ejw)pqXRu(x285u{~4;7kSO%q&Zc?TJb8jR8miw<)` zjycG%=f-obN~V?#B)UCZlj}VRqEKLq(=F=~KqCJD(?ZWViApIY(|9UC7gTG5mYZhp zun@F6o9E7FleTl^9VI5vLt6nt$I153M@i7SoShPXs5JUfKTpy~k?JHr(vH`)Nb_`i zXPVikrfVBdDH2I2frD>EHM@c<`_=f)T({}h@#gmPsBY(heB|4{RQJia!;~_na?q8Snu_o_^YfED=Q`=fq8z@jsO$Aw9X5ketkg@T(ry}f zUP4kW#@lG@Eb?n2agLgErfpKgA%R>YNwn<@Y=M6CGZuE-x%W{*!jXk!RHPI>2q(4E_MgJ<*j#6z{g+8U2z*yk5wfj1qX*cZS# z(lKVTL7`j^0|>e?jF^nJfNrg~&++Y)(@O$MJzY3-+Em#mk}CKkvF8#IGo!~67YFWY zTRZ}P+VW$+Ysmrcg-8r8{&L!XN-&d1QbE3&BX&o>W4set^OFgh0{{) z6Odl$UDqA&_RP~n(MZv~ZXM`@{l+eJoKcAks-XZQxgE|79(dsADIkJ4BeB67V^K9h zd*e;0nq)+lR&g5bKuzr)!{Zid9$8vRT)Vj?nT?W9oE6SV)xb6^`1Z%186cF$D(!Jr zF8*;!m9lsv!S9QF;;E1)cP6>ucfcy>niw;(3hEv?9ZYbDfItWgO^`V}`{c)ed>KII zIX6P=IIHY=6mmq)J@8`{V2Z}6uDQto@1B>`8XldYn@pWMt4Gw9O*C@K=@TG|Mi$Zi z!;+_q93L6xilcXUK@~)K@06)|HJXWZFzDTZf){5++65M?H{A9apBx0(*3Eaq*Zkv0 zr=8$=(k3w@1!n_*P0&A@KgMUM)!LZO%E<)lr;d3dkz$aQjmrJ3MOX*TSk4oXqdI?1 zDlQ*>-@mn0`+Q=828B{lD3F#~O|3vRj{f4WHuQnge@oOq#A4Pw!%zPJqcAdsS$7U( zvC8ef+}&rN8kS-Oa0%qsYyb_t`M%g#euD-YI5B0|Y=Ks5+X~h5#&Gu;BSvEM-*|HL zHV}9m^LU>)Ym70*@3^8fZ5x_}(D%W5Vt_)j2^5k@Db0@D{{ZK<9XtARB%4D8BZHdc z(dZMUX!S#>BhNIhv+0&Ix)l{oXM=Wl9@x4)CYE!oX4l83SRyh*>Z!SiAOsb3NH@K+r{9n zd}SsJceak7rA=0)U~N=7a8;Ysl#rr*#u`K~j=@tuX^FQ#+6d=uX|U2$f+;SS<6 z7;m+RBz%wl@Vk~&6h6^h3ct_3C}epjmwT`-)GvTA%CbbkUX+yw?I74ayJx5Lhtt~6 zsk+pCFGVhz85|aAH8U(x#-LedW7q-CVbsQSbLw=h=}J|KdWv@r4|PAbXXQ5`?>3=? z8U#@16dXCY!(U7N2S zb#Zl9S&t}**Xu0z!HaYG)WFS<0EHI+q*tSngI`;64Ri{fk*S82WA_&in>Q?#%kf!tp>o^Kg==f`83?L{Dy)0Je{ zk6i6xVBr4Q);j^6n#4WNIq6HK>95js@@aKaop2I1swosIAOvmL*K9~8#R{)E=X_Ue zYh9OTb@r|+EI=%AhK*ibG_KkQ*9l<=k_gHf2J%QI!uKPN`6m<>m^F)`;~f}i8CFzh zPfq7EDP&T_km zKtkE8?_W8?-#02c_oI%@y0`+pyy7jh`6T}UCmxTSL71s;u}BmLCi(M?8?yfZOETD_ zN5)g9NvDfekE(;$gkJOwMu_eYxbK=5M%_cGYFDG_H8R4R^q*>ZhC*L zU=V*fRCZCc{mah>u=)JrsvV`cpliQ5T{ola#&rd~9mCYw{abw2BhJT1sFzKtN#JuF zh})HigOrL&4N6RffeqjLe;Hq>Kf=Gpbzk6rs{a5E^$+R)08?JB&l{_Y{A3{rcNcb2 z1+5FS{{R`Nw$BVpZ#%o+H}8x)WbBr+$@7j5ZIz6rwwhJiyMEDE`Nxd71(v`Z5;7@d zg%<@Yl zvKb_D?t7AXIaq;U#1Zz39r%>X@@x z#CUx_jMO9JwlI&jTE(npFN{`;<2@M>cBlvxj^^(2O&%;>Pd%de$ufAX^rVb<9AqSq zCUxdX9~quR&q_%1ET}6W^Cn*Thfule&k5PQDcf~^FFgH1_sNb=CUozfZlQg%*VHa~ z+JzkB9&gC=HIaoPtk?&f^Cw<&*VF{`$l7<`2KcYWd6R+-;_`EO+LS895JB=Y6r39! z^V68AF4Bi~BZIrK_dfZbsc1DiSB)k>8c2ky6@z`GSUh&aew-}(k1Aowym79Q9+f{% zjbdo$kzPhIsc*LHYY}6)x;*iMVVT)%bHMk^*v!4B)TM%VjiA!5m@I0n6^Y!ya@_mo z2?lS~Y2=C+WO9XM1fUEF6l;(>e))z=Jmxc_S>I?J0(i{sDDpE*?#w|nC;`B(*`7ho z`{z2;LS)*iL#u8VK=L{4b2H|K9GrvuXF5=9oP*9)vT~AtjKyvua&3-r^m<)Jw^nZ4 zoJTZpz#ub^)R_ca*^C0;9rGNTo}zINeB07A`b}R{)9JO^(V^1GAX#+<3y74STWI9h zu*}+&MAS%;N$SQJg?g>R_%)c9>A619HGqyU`(bVcQM?(RO+X}70g7>rSI#et$!h5m zXkzMmc{-M!T}F*$256#?Bf9wkh%|m^4u{dJetL`^kU$+^qZmF!Fqv^Usynm$A%JRlNH4GVm zyK1xjBZ7FrFuu~qU?FddHgiUkS1y-Pr_^axW1a@wuR*P@#e>E~7{qJ{ZT2YN0D1oa zj6hF(iKj9dpVb68W4IAU{PBZu5tY~puy=VM81t{(8`Tn)P{i}WJ`MTFkVRM{&p3y8 z&KNVYN0C)pmBS7>0{|sRBgPM$WJ2z!C=zRvL;1-z^=)RSsX6Kz$1>_=ZLZ{~27m*A zIO8y95J4lz&cnrSiz!n~DAnW2==(NABc%Y*Cvo@ZC!LaCOuAM{&Z0?Cgm(x<1T)y+ z+2B?4&O83H=8ZX|uC>W}>5VIqJwITwgNoJ3|pGcv?jZz?Q-=7+vEuxZy- zDAF{Kr`~-dAOVWs76%tu9PAMOag>wx?x0UM+Xm7U9w_|a^N~9{l@XULs!q{-4|>3$ zb7#f?q_qKL_`<-e6=fiUL=oE}Q;ivAZRlvPAmY)YeY1yrkh`BZf!YWXBtRsx#q%l?m5y1xq;togI zxLw~oa6PidU?Fxw@yh3GDsY7>xAHP5@36vWK6AkyllhAmQhbD}u{RIqli z9mRla;}cmlbIs~Sr%gKTQLzakzyO|o$iLq%*1*-k`dw7Ic{Ue`$~M*vEE+UN1bpFw zDK#=A5JFyNY5@TyzAGk;LBT-Vg#vtYh8aCM750O)-5k3e0|FTC?mOOc&|zZ0Jd5v) z7D<`dD>+fQh#iN%C+~nd74;4Ms(9^mF&DqaQcplb7p7m-=hO@ZcjCWnmPWesvyJCu z8j@HOKx6b9^#(vhyNzt4YhRk@4^Po*x{j!6v~d|_D$EoRI8a#k_y;nmC4c~ucmQVx zWv~ez^_Dfe>%7s&O`viwzZm)yf?ZNas+pmHpS{&L6(5Tin{*?m$I|E3HmUyr#1d6l zbmjojyks`!EKdY|v@6fXTCwM_1F^m{hBQhfl~B5%VgY9CK2A~gLS}#n<{{~2Qfh_v z_&+#|Ok*ngj;R`*#)V&Vh%>z6BC{jKD@5aKAu7WA5#Jj~VOh}X5#AyFN{zIi-<*$g zS9r=mBE^p*1@>cLb_V-%+cvU;xa> zg@X`OZ5PR_%0MG$Q7L5v^R!(I)=yR1RXo*Wu5atz7hBOapQqQvVUd+2NZ5_WH&Dvl zSfU8?mL744dJYuY+1eY6J*yvA z3^6;bTBMu;04fOG$9_lcktXO_KG*=~Ya0Er)FX^HoQlqNuO_bo)$^KU*?6@x$ETN1 z9IlYdUZi1&R<71O)?1{*)8=Xa0K_$N>4Lz`363rMw;~fJt1Y0GI06gS@y2>jRV9H9c>_?2;4V_8|^gTu4)5|k@b!{Ymo2yqKi{g!s z+a$5b@ic)Lq7k`(;9qQn(VaPuO9L_Sc%j(S1Y(&5GprA1yP6rrsfoP8A{AXS80I`z#IsC8W%{6k074xNJjr%5jOpMZYT z=R9>jF&Nps+rDv;fN;#ahmqSgHbr~ADzZ3@qYQSj1dqr60C?!c3bVBL0Or1{sp(TD zi~Ku7E}5g#s%nePztxlzY&iQt6~{fZE}B(>Xyw?F?Ka5kYI*E^#!(%Lmp-2Dx5f9Y zc-VOtk-H?-UVDE>{Xebxm#^viE{aV&TAA--Y9$e%pieA#91P#eNRiX@?Nr?>QPj{! z`h7f!9E8XNTU}E3J&z-{K@T`J3nVi;8 zfs!!$OOGHQ8Dn#j(Vv{UHP^mn45mUQxshU@ms)bU_O#$G1 zW%TM@wu-vGO=CRlPdidE&S;HtQI6TBIwGiFzG^CclxWc-*xpqZ^Py3=Zo2bXp;Q8P z_VIYJK@*XdSk{GI`QsWS<1ZR~WX(xq_{I_ZW~GlPv0sd}$CYC-S-qfQv|kzM`Xu7C zKN)MCuPNf^wn9kx?VhxeaoaM<7mCM5ys4w(wqliQJ!#Uj4C&4-Jk3HHBgT2!h0j}1 zIL|v!JLEY$ol57Qsznik8}207uHU|Tvn_%=_!XXBu8H7R7{{BN$+NT3b&f2O;NuKAl-k%a!A^zs*JeQ?8I z0Bs|iv)=n=89y1OLErtd0fI@msA}+Yo#@6{2N|AD`KD$p?8FNu%?ijqb80h5lGHGH z3j|cZ3)p9^)O1=1dPav;2U1D(bC>n>qBaH!G`7csefZBE^PTQD+)2UbB_`rIC$|}J z?-*^T`i9fTjFhZg-2uQ9GZm?f{88_IF#|MgBt|OAY!GOJl#xoc5J%@C*~^r4gQ=9t z<%+74-n`>^wQ3{ zw~J7uDk6jTslD9zukU%v`0G2Ygb+&iH(X?o8D7K?dC9+gD^Qq?$o9v@e{9DlqGWl+ zqgV5m-}dZ#Wf!zv08gBi{0zgaE2{+zRMG5lPXhh%jSxHh;??n->p+cC1}Yc>drxD4 zo=MKmWhq3F$gy1DON3Z9F#XQew%Gd&_qu;s&}crF>YW!+&@@`TZmvk09-!{trj#w% zN;^*L7k=59-R!&|uFB37kiXP@uNMmtSd|+=J+HQLU9MD-!JFY zG3olIk@V8Z63U9z$}j~_9|UL9^xvhvoqzgm)q3wx`jS02UDLcLN7E=V)I{csZU=%r zyN};KDo1G=*puK{yr-IT3+F4Vi6WFNQ>4mOPUQpuNU}NdJ7k9^lUPYjf`Q$9o8q$j z1ZsRME1`vZXr;|>lh=Q|)Gub$XwQ51Ir z@5N%{FaZ>YQcc($#z?u?S=oTxZ)lrKKV5aN~lvxoQ{5Pky)H0GP6*n?`NCk{{lSO9@0RwlgbQE^pigt-Ie=Cm z9l&r1^PDUjtFL_KJD8dR&Cu*IPF7_ED-mLi9^)D!fsd%Al!MsUHOKeFtY#|BYP&@a zF7R5eqGXmRLc$=bHVpEJcH~0iK*aE-5~opR1myqe)@ZBqK)5eQ5&% zcc`k`Dvv%!V0YgNAc+Rcw;kI3@QAWIGA8G+qiT;~cy#l;5_*wPalj-4-;7rk#i{eg z0yw;|%XC6H+{y_H$s@)GQm1L6eZjooNCX0C_pIVRz}tbcMR^$yp+{s5w2`<`{9_^7 zz=A;>gOkE6cg7SbaD?%CgMf2ylI#f95W^ME!Z1t)O3H(Yn{a4V;!v6r6r@9~L*hUd}BNed%9WLOLX zF9d(v1>0F19|J9|*6Q`XkLtBDOdyCu7V~=$c2V(@nNm4fBaljhspyOSjz8$X#*G{r zSu|Ryrq&2%h%ciS!hK>*5o3Wvo^vh^<2ueT9R=}Ve4K6p6iMTpLE9Ng1B}#KWdxc4 zl6+wVixq|mWP(8~PEpi?tT-T#BRV%JAdvDwzIo^O$v~r6Lmk#m0Bx-K%{sHA{Y~i> zQ3zH0WgxLMH+K2I+cl{+mD)hg>{xMR^PCC3Y#iyG9%f`g8Z#0U5<-t`VxR-fus%Bx zj}-N~PQRsT`ae_B>v~Al>h%bNNvZXje@wxxj?u$d`8&ZS5kaslApD-@p_~_DTD>=iB3q&2CnWkUVCh z!SBvDY@`XNFii5y=_-NgAWIH!n>WW8p?=$}hsA9y&jhI~PwBe$Z){nL8A*r`hp;Bd z$&=fP;|h`w9x;mfhU5--!VV}SvA>LKzWJ!mtt6sG2h&KvP$>rngd*`PmE+%b_$AFiJ$=F}2Z3~A(z&sJb&1w2Rrd4%^Y$7renSnofv-cbRIQGN_&E=-lMX1s=lgTgY zmOvv#P$E<|Pvf5eS0^DE??$L*xcF`DarU)t2aDk6p*EjQr`1Cl)}-;gXzuW-I|&w6 zjdD92&7e&KXmxL?)XN^F6;7Hpk8;RK=W)Nbhdy#Z@Hx#+#e z^@$?)ZSS||-wSisPBi-cLUkUcKBG;ggGi1`2x8$70oYk5`{d(q8HzJUG$RigxiiMT zP4PE9^IT2)=bstPQ8sEfIXA@JvwRKq+M2!yyz=61*|HWRCtf_Bww6>=az-Q@NCN7Q zgZpJ-Y{wXJ63Ri?#DIOs@0ucELm2DEx5RT~RhX3u#T}Va(GPsd0ld`-k0K=52GTMHPZ9ea<73%hdVL zH&jS_2J6mxT8?i#&0iJ>#pe8#=4xz`bI+dn0G@Nvm=t<4rmVR!KJ(ln=Rwwdcxe7_lte zl1V2q!nTaYv1Ch>KP$LCIZd%k6VyO8U z0vU}cB%T1ySH|(L>Q1$<`h%&Sq12uxoW_ydaV%@KR4Cy<9e~d+Ko&xwg8*m|*yfF9 zmr%kVsSZz+T&IwO4AP3D*pf@B_kHh$zexTGvH%IDoH>nPbP>rfoMy9nt(oc);oUbhP)=lx@^scQZ zPtxnO>!i_Y7g-OaT6hDreE$Ga6Uw*G&UvJo0?l|Cqp2Ms*Uoe+BI$ib^hAUf34C37 ztd#MH#AHa1y?0WZ+wgvKH`CvUoE*{1%_J?RjyT{b>PcrWd#|vs6_DrO0B^VnlozXXoHIiG}Ekhpt}KVXmjpQY&vFuObqe6$41o}k8U}m=O*mT+nADStmaEb zx*0TjiL@^8xr#!p(J*x+abbsX_{r%s^Xl~pr_#eD(>bI>TV_81V#VqDgVih+x!Nq} zMI*x?k{5Jw*_6>AjE%yZQbDm@^MGQdfUrBCYy{oj9j9&hJ-NsMuxO5OWn(CT+0|Uu z(5C$PY9{djZ%o$jQ1QEdp#sOg1@r8+IEXK*?oRDW{QLC;qPM1d}qABD_Ap#iv zMh_rTMS(yb=bpnYOyq(EEl}s@26`>hH4)F~>Lt@a$4~lzUSUN_?gGXA#CY$Xaqp4E zA(4??0Z_6EVpja$9^J7SnX1V&C|T`-_^SQ$hkS#xv7}<|S*ppiLa-^K7gdct*$7OqV`zBxTq znj^K9{l#|1)@!tyj+q>1M1RCRcb0u&l#?iGrQ6t1vwm+W>FMX538Q)0z+YfV&d56P zky=LxidmS@QRL#|M(Uxh0}?13KWX>L7cTP2`dHv)UraG|J3u6w2cG0{k+W5NVF0lT zqmpX_0AbExLl8v}Xy;@GHAb-Sw}VEnoGBEJj_8cZ7zM~2C?>him_{C>V#)yItzR7C zfDsSH)C&mVMHai4LVRCyY9i89Z+mMUQb{{{R?Foh!94 zF>%DF)7e0RZxvXO6_|y){AR~WLa66!NQuEan*0OD9{8+9{h)!Ci%9R&rq+od2^5PN zMvw<3f#ff8IOO=_-xF0Va?D;R76hS4q>woI{&1eFIx1~0H5i{c7M?kx zjpdP1Os)|{nzF}$K2Bz41lUl#b{NScDzd4d;*TFUh@B}Sv~4UUknJ>R*d%em!~}p> z!RHsgSj@*xA@t)5ys#m8z6D=;C}L?+GyAY$M4@Wi}t{vva=4$-wxy7+<47Os*siS zS9u|KeZ+UHuTdIB*3J4BmGq)R=@~!{SH*$FSb!H^q4W0Gz4R2GK5;(S#8p ze^+nbYyns8=O4WvSX7opiK16vkNr!0HFmrMw`^%6^yOt{3;`fA@(K9F%|>d9w;Mj? z6hN!w-?k|3Jm-GHID6u>{{ZiVcJIa%X(4rbqDK}k!Q;+T(nF*5j-fqIQ*Wb3bye>6 zthHeWwdJE{MJ%P1Fe+&G9!5zNyQ+#7Ym=I~j*?!VubW3R#PUMrSj8Z8C*g+#V=VDc z+gCzO3w2#IJz81d&^)R7mx%*9$9$CsxvQ-E{;Q_{0HzWD0Mp%6oikn3gC3jX+=HV@WoVA7eQgVvE#)RnfPS*v5<45wAneuJrMbYG~0QRqm~`mna- zdNpc%gW%^)?;LOJHSooz*2@GsnDtSkQq344U*ugJM7lICvP1FCFgeqW#wwSLhnwe| zm^@)mBH3oe|5Tpurg``11aq*rj6tT1rE1N!Y{CAdr*QcMyf90kpi50g;>Jqf1 zn%aH-aKtwnJQLWHLi0NJ%7t9UkdOtwvJzr5amEh`V?+V8-XHce&;5&2go+^?KP9|xTqf8Z1NgI!U zf%BY%lj8B=v)XxCd}~Jb%@Mf(QT?+Lk_{gDXq%cju{mSWX_{F~jX;xh1p$^rRhXeR zC=9$!&1R#dlm7tLTIo4G7-b=L-yYfMK$0{9|(qJ&sl*$Kx5_Z0@~H6wMTZNXXnhz(4PrVLo%sfw|4lJ~+jCFy_d6 zW(XK&*jq7Ji&eFI+KtxM;pi&2AAd*_v@J@e4jzIoy^sMI7X=!M$1J5rCy@twSD zapr0mi=$pM%G923Jq>Tz?3NDFMT28J*{0hZiLnmq`-tRMwrP%aktRrW5z8cEMOFl% z?8Dm=>t3m(>fKsRUW=zg)8SdsWc7|UPW z1uo%#>iEo(ch5sXAFHab)<>kIX7wD9I8tkY-1hzR&U5datEO~Ia2T!EV~}D;BfeTj zU7#(4Uv-}6At!9bZW4JUm6_#{-I&=dY?JSivAW2=jH7lZJ4zt^KpLYW#m$rew{AQ9 z)^e_sv}hB_p^`oM9A=55ja3;X4q0u55wzcLoX*u(W4~<0bn)taL#@#DZlMcwo{XI} z1QEtyibj)rs({aJ&9Ma6jPVHX;nS!1x)SRk+G?rFL?7rM|R zIgNFR(v@P*L<><}@kq718O{FLR4~NwNJyA}RFRNC;P$`A`{pT$D+CQ{cYmbaR!sNL zPwFjxlc@DwR;NSLqUoAhUvzp%ot+~UR0|SM@1AC5pzLy*c!ZLyZ6ZV%{{UOv5923{ z%udii;83BNMLqaA+0}>b+ve}~$pJQO^PDrP&M;PH3aUx$034i=M{Z6G^b!u6`VCL1 zshsPBpwq^Y4UP@(+)1!U6~{S(yk!l7ie18j0VcULFeog+7Oa{a;Qh0*3QV4yg*S#H zgI*7joUvRDF7z8%4W#|X?~pyX#P6I%9`}+m?c6sLx6OCO;|V3I>w#ER0Y+`=D!4t* z<0MhR?PEH*%E{>ZQTk#1NjFs`!QkKH40XI)Z5Q9$5$l~9 z7^|sP28bev#qpKYGfk+LN13H!#QKhTZwO2)k$p_sPop)u?x)j=C#5E$9g;JZ1%}b= z*Pk|cIZLX{G@%5HBW7d=?Ickn@tR>85Aey_StlCdA_!GfdSyA>NxKL7YQA$Sf=vu& zjK?5X+jI|m9E9HOdV6-A`@gFAuWHI_)gh9$K z-PLvH7msXkxk&^X%IuLsPb^F$kdmgTH6CjvjHi1lGwoaMZiY%d4zr@tXxBz`olYHh zucldL5vvbD&D?EZSry!I&Ux9VM;4w)wJki)8ICQ{g^zG{+yxVS3MZe8f~~7I;I-Pp z=i?}>4l4;8SgEc(jsQou_sh*X$)<)8r;0Nj+=>q8zkYmtW`{{1wD54-5EQW>b{M&{ zrY4?kR7t7U4I&N1$YU%Pg^mX%&lqkA+GbgVA_tN|8nIy+CV@T8d+;$X>dsrK>h&7^ zY?|Fv#%bmyq;e>enadIa@yEt$SgNYXBV{N{k~X#W9(evR79Fb)dtt?scg-3dDb?yk z3oKC*3EeAgUIq8e<10Cg$#APGP@u-oZg8d+#TDDl_s#~192^ANg`s!f$<82#q6j#x zY17R*Y4s7uC|KP`s)W|?2(QmM6*y4~DGH>K$s;bB4OE>zUZEbIR?@=j9J1~ERoD`s zpK=uc00$ZCR#LPsG~-Zq>@HA!!t4v}-_9p(y}9|tz5u{G=UByRjL$fWO03K`eTYvM zYyJ4ejoKpWRZWei#nwqrIK#d$cyY<^SWkK!{xB)~dvoC79GuWRk?fIGolSsjb60?0 z=bY2j`hK&fYDS-51Hmgql7(*NSwkuP!3Q&|g;rgPY*n7)@tOKJ(GFJYi{Rm2dGClj zUT)}}KUdXyo}Z>@Ra;T1kVhJ`gan?}O#%DmY?DOon$8e;1dI62)J9>M;E+?Jm1;IP z;~F1N)L&GvOZ&F>uZ-6kyl^Ks^q!+Oi&qr!#ix|VxGGN=Wc!612ao44Da$mG4I)Od zu#<5v#EK$?0mb|I%~_^UQrn9H0UX`d5oeNn;x9kHzG>4gnmuOCUg;rHLwYPM{t5ZV zagS0M=nFQ{SMqVSI)s%XRDw9YG}uaAza;K9d-Gg-WP!#qsBY9N=PxqEf-@Yxp=7uF zYiYjUIh|dCg$!$-KK<~KwaStWdmb}$vsViKnUcQHYVXbwWM4lgA`Ve*#~@$MYXZTG z5oYm~vLMkHj;A#%D=EqbJ%PnYi@_wy$QCjI1%M{~hnxu2+cmnHP@LbU)kOxoPe1A7 zl_J_&Hx#m0aA@{8nm(@jev7KcRQU(aXhz$} zq7N6DkSoH|G^@LHY@>gl#x!ltYIb$#BZ~*O8R&G)P}6H6)ELrFO~Y+rVA1Sz$b^%1 z;92K1MEK*RjfoqtkIrd{#$0&G zGCEFAN+;tr!u;o%6XxjNJ&Ct=;y&x`e;KDvJf55{o1$gf2t4^2NnspgB)N$W4IIz{Io4CC5 zbu$lqS+yTIgHb*4ohKYxT|~Y4&ofgeesa2*NFSW@brSdIHmy!s_7@=0DnQ^I@^vrC z=QL_0@1Az1O1^QC%a$$tRO{do$2{f< zO8wi39C1U=Y19xHfNXo@VbW@Kny56oc(+TaaFGIQaVEQ*EytSoUUN!gZOc|Y#pWsO zazPd>U!2#jjo_Y0WfbwTRu^#jX%_qKEQ_g0Ui8-1Y zI!8$AU29m{PM4#bS2&HNDH7S*qS*cb8RW}rVD}lfO{>*uB{JyemKh3_EgKayMG?n} z&puWp^PH`%eI+e;$wsLzp(Bz}3NZnsU`St+T;&)Rx~|r9Bb&}vwF@s6p{xRE`{rin zEaxERvlXrFHt&*`gIVkJSb8+kYmZgdYBbU&oVJif5OE~7DO-`WRrAhy8iffVUBSLb zY~{qWaS`$00Owbe7JEW;pbya z*6asACp{0UH2pJ0)@1(x0+(4oOmGgGZCpN^$hA$tBzLjjIr8P+I!N&kY*IyL?vp1? zkEkQ{STz%ADYv8&1afHA5(w{JGX#WgHQUBy$11fM7Ba|$StEh@$e!6v%n+{P0pyF| z$wkoeD~=$}V*&Y{qC zKTv7h6iE`d8|{rS#U1tU=UPB4T;;Qy)fk19u0={{Xg8zBBJ1E`6}ETl-dp`8mi( zA%_Reaxh>F6G>A@(WM`_Et{@5{N|34)HS^$sOkEjPnqD6RE3)NHJ(Qt3a&mfIc+AMeO%s-o;H#~ z2@0agp=XcA1wFuk2m-u;-xlznz!i-gZy}^sV?@vwz~tf@x->c+M^KI~rLBDyl-~K4 zFHo-rzS|dOhwqshw-61F2MJIpDF(+s7&{(vLE{EeQyGzrO3H_@V`P5#nIpi_q{!08 z>L51Dy0;H&%IdU}>5?X$JwH_hk&C^`NdW%bSoM$`#F16RrS#k=#7RiZ_PDLrIj z~eXw!S?)dYMLz;E3ua@$01r75DFJ~XFbA>ooXViuxFUh< ze~f)h6KQoaY4tI@GRDOuM_Oegf(ZA-jHr=!f>QaeKtaiKb%$GDrTEX0iw7 zhD_w0=NgIUn!!iDdj9}P=`>wWu4uYnRqE@~dUm396YAX=))CuzQOT<~88zgK%0%da z_onj8N^Fg$`{jC^FlqXZq75#jYM_o*NTv!@NTASaJ*;zCnCa(ZJ$G&v~-wdg>>cbBV zJ0F~hO?JnTKHpIh{X4D9JmMVM{adIvMvtR5sm_8q+fb1I0HjF30^gDVAGQ}%V;zC9 z_|Ef&hRxf?>#e?jI2D;qsUY^Tl6G;xFplGz$U8eJsjpqY^u-$cKc|l)COh(QHP~{J zN?I7=*%%R%Osh=S?tJ9NF;o4_3sE2d3HJ8JxdeWe=Gac{x5fM7Spa}Y2fsWVc%^ux zSrMs_*gnSvQDfPak18Y==zRS|q$4}bQ~e_0SK zZ&iP|1HeBxoB$5){l-&3^TEFPqo!!*>ODtKpwfgh>LpmC48cKWG%TJ6fK2j`^8$Pe{@yQ0fb&n_H*RhYU>9 zk`PjpMu9eJhsn(3Z6qHCvE%(zR9}yrHfpaFbA00vzzYOlIkrR-c~7v$D))kJ`&J+% zfC=Ds7&~AM%K?1W6(;Wa_s)!q+U53lAbZw(JsYKT{eF{dyJz&#)JqQN(J@q$^z-?^vlU zkuZ4Ok%#(}(ZlxoN=%@V#BAq|-1~p^oxVaSdkp<2hTK$)jyOha$NdS13^xQkZvZ?~h>4%;w#RDujHk5#PGX=RoT8mnp~Rk0W* zfj-uF&onRJ0|``{UTwfx$eE5583!GOJ}{Zx@{zwXIu@4qaNn zHZRx?*^wM=3tZoPs$GCQni;6I=$3%8`did&8@F@!S2gi@qd_!L>SKLGKD0`}ykWNP z9@pbM<+O6FD1t!;#%VLNtZH`DV`!xV&GYZy7}mC{g2bqyb_QvQ^UjIv@GP9w8<1%5 zdsb`4rzfEs=D4Kso@zb#tkD#eS-jS#JbKW`MsI>k9rMSEQpU;T=GY}5W^8)DA2+%m zNBVeuHk;O_^>+cb@9o?BcgjHPM%aT~ao;?Mq|H!DFYdGW#z$$*>qRdax(!1FC>-rQ z#qpjba^|>Y`ORVZN3#^UvMIZBo-9)4sO9nwC*=1c)KptY9GeF;M>wIL98>(}gq!Cq zX4khI_1NRCqvt$j)O_Uzq;2zejyYQO{{U0-iYInf+sFdXIprNk&Le6*PFTfmmVa}Z zC7zE4voq>He90{Q9(&`1R-S(R=bt>kII~Ujn5Ub+II(#DLDsdF+jf`l&Cs&5A>;cl}`+>%c@J$(9!q!a3QGdhZ96<}~H z_^er5+cQkZ$vkFj8jPnixgLG8(|xtuk$K%$TKd+7o*g`1lq(SQqY1b#7Hhj7C(dHi zBOK^aQMW1HyM;B8&2#PBGf3mpDX?AM?>~%pJme(rUpdPE09r;v!5NwI@*Xogo^zds zB*!G(@!P%zRrEuIazP=9A87XH-@anECZNY6{P_Ik8@bI|O>B^KoqmxUOcPLJ+#V3p z9i*SJtL^!sFFA9roMzXW$AT||gv@aLKn3HqVlm0Te8q2#z;507@tXDSt))n1 z*UPAq0umJ;bW0fpjS_5fdsn_=Vk@=@87%r}Lf;l_*S2%L*G@e|m>+Hjd_zYn#4{Fm zHr7oKIXPWfPzcp_iX>m%&vX9(o-;bsj@3l^d72qE?q2&?F#bmwr=UMdi>M}}tVJ%P zOA{+Tnl{+et&33fcJX}C%m>`y3__3(2flN?x^c}k?+MkskwjJ#AuAbcA#b(*JmJ)s zrjlu4jy*hJ+Qqn%0X=~|>m%Utc)2$zPCu2BS#aoc^az4MTuVptRHMys4g zt2JPF&UO}t)jRa$(E3mXSxmm5jCzES1s3mIcKqgI))jV+J%@bm_u~cNbj4&#ZKQ29 zPqFsJW@a`?B;d1Ed>k$UnhYC($iRUI&*ukoomthA6)FkbySU?@IHwqYIqCIiVxLT% zQ%k4Q=`_y55dhS0ed?og5KiC;=KFZ@k@e=rPzT-#C245(IH%y?I6$*l0Q}=QR&rH# zf_N8oogzF^G;XC?O)x#dz@!6YUmp1&%4z*yR0wx^hk>`b_|7oN`hQ<(^!IJuTwPsa zZZ;2N@t3K@bIx!yY*@p=9l)>ei;_5C4T3=(zd6%N^2pLi#ofs)#MvhkI=RUYJaJja zu+A9LAx7HNO#{Kd_Q?|s;bUM50*CX1z9?f-@+txb<5o=!rlu1Hrbr9ME}t{k>Zv%4337GNbEuI zaiH|cc-aw)(xWpGu-||F{qZJ`(v{=hkF|>0n+N0{Y?Y3lS!bE%o<@>M7_++@EKhPy zK8Hy@vr@2W;Ff1R?TvRe4o?_#`dPIR&m>9;z?6)>t>Dsxp znuS(TEG|`mU_ll=!6Wy|-SE%dLc*&lUT=a=Y+o368zj*u-vH!f41ieNOX#IAGD`h|K@ zb-iNrH`PB(61VAFQlLs>zqfE4U3aYW#{&o&pIxkepC3goP2MPT`?yt9c$z#sQ^E*bKnieHi z?`ZbCR-%5;N9Pa&3$bD>&>R8nhBtVnSrC-5|oj>&lPMV-8eNZ=T2i}e0ZW-O_i(96bN2-@gs78)xT%?MB z(!kLfA%)qUi!m%ua(Tcz;wa~53MZb}TW;gDlDEMkvIdxm zGyn%zE{X#V{MYT8XVhtBe^IpT9hJA0U=^H_K?1lPxWih_`({N(-M&qfq>e9f_Qbo! zJ3TQ#@4ij(UyRk!I&P<{br|$ILkN|_5M`BPUI8cWVgTS}Zg)hR)D2#;eOf}Zxqm_- z6n06a1duuW=56MR-vr%f9yq?)T_nw?lxh|zP zzztaju{`4;q6j>W*`Q@K+n0*%jPBA%(n!Ufjj<<^aZB%OW63{pKkt{-MGSGk(P~y^ zkvB-8Nc7E-x1Rg}c=yBXXGsd$Ip^CwFYyRlqBeo_5Xq@C2^Uydy+uHzdSG7NzI$I8 z=OVFE;TWp-AaF907{d_}C3vmFpPb)Rc2H|L;df_p>}`qUoKfb@RxGOwCRLP&MG>as zxqv5-D0`lJVJ21{tc8zZWb@mP?UDJ%B$hvR;06tEM=fXu&WRB;a=QZ{U zO)x3CyN`SpcYbnXjBC8ms*q@L#n3)5#)a4@{{Aw1g#bOVzTg3_4`c6&QdpT8z#-LJ zZuS0>e>fNz+@p6L=-vQNrdIeS>)hoU0=hvvI0c_-p$5pVNIkwVIlchz=CPFEiej=W zIwXNb1#7SEi+*w<+!qxW)T@JKT5Q^w$;n$qw1-YkFq8 zOVIQkMtveCmss+86K({_w1C2|fDgx<-;Bl-KW;Mf+K1%il-4j|c(ZB2IY{Jq$tdMX z1R6dMafeAHX*#^oiBphQ_N(K_@0RJBm~@(^l6@3fr7^k)WFnbkqfT1V1|bqlCT8bY7sm0$pC9G`JlnG{%KU{JnK zd@6INqt4a`#xj$V2hKP-sLIP{EenNfDj$L<_U)2|8dg<}cQFTn@tm#BGLJiHgcfj$ z3lqQqcgn}Klevn}_RPq{at$9C>eXlwSa2qh_Gi z=$xqp8X(<_^mOswoa(2OZJ)3kxefEr#(3!Dc0K#$<9i+RUYW}t=TFzBpG~WUw6IKd z5rhulmWz;Wet56n5hSny08r01H~Gy}@vvDGsUQ(!!R?!pL$691J7&mb=aZgHEEWRsr!W@nID4oDU4{9@&K2BC_v zQ^5z3?U^NFf2W==@tp2isCmqi$@7DSp(_tQeCAnLl>VVW_PnO2P|lH~1p+nNC|8^3 z-!aPgYZcz947*iHRuxyVR!6vX6f$;T5-Aw*=orM~71*k7DxG3qonVS4{X{Yu zBM(uvl;nzUW5@T-e5-tTvdUw}Olh0vYsaIpnlWs z&S!2;J9fls@tJglYzzj?+lAd%oa2h@O9I^26_~BSS1L&W8w7GMCmv~?Ht?7DxsRS>%^plnm$~}ov4So({)XN;&xh7avDBa5l(D0)B3(1~v zW^t){#~k$fR2>>lnbc+KI_*Rn$m3I?hB$Yu&RA{?);54F-5m2(S&b+p`wT3^$XUQA z&5X3gcs>h2w=VU6I4MYj`zl9vTNftby(nwQXJ7V;Fz3O+2qr~U+hl+@P#@nqQcSE{ZIW+qZ`!gyML*j) zwu;(sk80-wcjLE=83$RazB9A4u};p;7)Nf|CaEXVH%yC8njpHAk(u=y$u6V{UA=`- z=jRD+Dd~HEPdDns-~R3Y0A|YmoweTiyZy5!5DEK5pNtN>n!alpHuJfN50>^I9 z$8FtsykQw3PUZW7BkmqQ+bRZjjz@VB5)s8lwtwF((llS<9ZOH58l66v%;9}qL+p({ zS&w0vaHzHd!1*~X>}HI{?6#}}72Kx5uWV1-HEFcb&8E~$`k3KpR5Bw2a+?B$4*q$} z!!4J?W(ty^)p98DjXEkg2`b16gSl?G7hL;x&#dU(L+Gf#>E~6@^&XL|o9bC-jOcZV zHANI-`hj00{{ZDzKO*W@jSv`Ezd26Ll0eSdYjqmBy1+TdGRf(B1>4gNyJhixuO?GD zbe0v;vU)c;a8^y<(g%w9{{W1E&dE!McHG6N3q63LgE&=-?T*L-?@~6FAOd;*KYRTsIqxH{yXHmWdM_%-x!nq z1<6oJ9@w{#5;mH<{AQgK=+-?)>QZKx)Bga|lwbgR9IzviSI50%i8Dx``f5!t6rjkx>$<*~)HG1(wR#WzJkh8EnLg5fTKtS3P4(?x(^~m;eJ)VCt)XDmBQiWD!Q6xq zMu8moCnKxty4^;jUp%rb(reRf?<`@M{lSR@@(qsToaKI3ldt^9@(tsnp+@_ge;Ytv z{`pDbjUa|Z)Fh7TOP~Z8$Rq%L@4pzjO-%YtLTWV9G*Y3pb?y#y`ss<+sT?dC=-<9@ zBsKo|>itpIwVzRIqSotT(#0fNX!PJ|;8>SxC{RdIM9}2<#5}3tCLBr)61vSMB8MFMJmdDh&{4S)&84$jW4KE zV(#@S_t)K3;_k8`NxJYaJ2+pAiPl*bCx|-;+=9vv1dFftIL2*9=|l!uKTo@GO~-EP z?_IAYf-!mqmC1$mR8gSl}HH|(o5fSeT8MX zvs1@`BX@A~dQ^miMA@zf9{Dk3(82k}-B9INDzR4D-Hms_f8~|hJKg!skE_;ddTd%3 zc9Tz~Eb)SPk9BUL@@p~VymkCz7E+3#03ecEn?D#MLnMlTs4PIXGzagT+#3G?Y?FcK z8te=4f;K4Ohj46hg_yGzCz5RDf|1EEiQ-3b8_{(KgI$UKaB)WN{9>@8MT7V}V6$EF z!A=j1V0g)k7~q`NhYPyL3U-#_uDjw7>ie#|);m@GrocQ{qH;zSP=J?QlktIgZa^d0 z=PNJ-0!HCRk8zBPyPgLG^TtVJnrI-F5ULh+8@C`PuEi7E_Q)!eM1!=Nyze8q_`qG1 z7|1n!kAs4ud*`?Gm(+T{ruAB_PLEHcowYhyG^{l+ETL7_i2(Lt;P}rD>|q_a?~m3A za`bb!g8hZ^aDl)!4d0$}V=tqC>Lyr~<3ewl7%1R-oZPF@@C_c{8N=TUK^jF_8)B11 zs_QyUo}(DU1i!Q~vst-CJV??=sH5&>b@M?a9Y0T^5kQeL!F1(C7dB7a zd!HGI9H!qz#kBG&O0LQqX#}bBo|n}9M@`kb#QNc@=c_q^BH-jBISunl`0 zqZcTFjj0x*?MGGm6UXqOZRoY5FqwFThk4&6SsGI@p;N}Al6nQo7dDV7s*j)8E|^O(ql3Jp+N$#pX0uAlX#IkB%fnd ziob*3DJpE3#7dH<9G&+go8RBYV{b}Po08h9w$!FZs6A6vJNDkQMb#dmRP8-u&l)jG zj1o1gC@MzLP-3solkf8f$PaSo+~>T{b-i9LkKwpCln}izB*}nT-LY0sE;W1y#~!!*6Q=(*SL)Y-S*;_1;2 zH|7!euDwtPLR^{HZ(YH-x44oWOjf@ z7zoVrsg;&horzGtUxCzzkD&lOl!qonQdM#e$-!;4y&CjP5^(f7JZy)oo!Et-rF1s7 z+<*kXH)OhWZ@H-Q(Vt)LEcLw2-B`UiUGc6{Go>?pxi!o0g}qt!xsS6{THYO-zFn7) zb9wh4Ky?g|=gxB*rx4h0iGHi$)*QHV~j2g_ax< zkc_RiE@Ix%xR-e0rI|nH+o!xiN8m}&d@)hp8x^t(Eb^w*;_%(y00a>nC%c}6`i$E^ zbGFG>u~q}1m`c)}g%SFe_%bM7KJE_AI{f0keJ?_$G$4D%9|bQkVlcIR{&c|&uxKsN z<%np7{~gp($ZbSgIdWxrl1@M7#nY3AAHV7$bgW65 zUkH{-xaw0;LK!I-Cbt~QS$@FlvMtogdAQE(AUu!9+bGN_(^$XYvTpcz7CAHYY6b$2 z!i3~Z=MZ)%H%}vkbTt~8=VbEyrZBLOu1Tmb)f#wbJZZc|!yfw8=w}&P{F@L_Y4D+h z+&3&NL_KI6bz%kv9jQs^@-c+J(E~^N$zvrtcBJ+6%+tR;NuEUMm@`KF(^|Dk!%>T` z#56Ro#xk}o_<_{=qbL$P6-i$a9mg7W2-?^-Fj6uGYRIpdsTJJ z3FFGcHS}TUcdhi*v}Nnuu|99TfRUOAySbt&=hNka$PRXnRK7eziTA0Jj3}I2Hf-w} z6$hEUPQFvBc3Ic0jhfJTnCM`;%BIy$w^s#jJCXNA4#MVVjN~5pzQX90nOA=@uE_@P zq-SSnKKYz*qUYDKms#lqVrHUH^nK4Lr*6H|DqsF^06x_QHG*)7&VFV!?oa2R6r;oc1qN@IvN_luYe(L1e9&w2}{g z5-S3{D;}h{G`;>H7Zor|f5o(oyE*d_B2xqK2(6m){s&MLcj_QmA^u%%Rh{xhZ$p=0 zY!{{5NbQ^|8}JZW>AXSNn^7(ZyS->UiaoB8%9sxSdm34>_x-f69T%gBtf<&G9GdU0 z7vL3;J?y}1T#=v4U-<0PTCF)}tX>+yQ5g~-%F6x<{J=D&lZD*Jp#MKjY{sZh?s9%@uSyzYl8(vqb z&F<`l`UpP{(O<9ej9l0X3r3)ENtokNuhGlKtuLIP8RXQ02ZT^zR+`|sAC-#m{Zf|f zbX*RTRg#`+@?!b2wnt_~{^fiR)9xtR`&AMh5ygh>jVg9rX=!u4gMQ^p(AvsQ&*ID2 z-!@U^F1Sr-?Wp~6_B@qj?l>{b(L#szL3;E;IAp`_KyuFcOx$Icyk&UAf|%+>c&eCe zUVHmbc|4TjXZuek3BskMzXqI^wN$pgZ!HF<>0Q^q3=LkE3Sr`Z`r9NDyy39qVOQC8 zE-SiMVG4m5UQ`2#YnzXvVA%uzEbF#$=t>}Y>McIF^}dSX-f*0@-7JHZ%9^I!2hHBIR_ z!$111kqchUU&(Dqx9H?|!m|!)`RUhiWZqvmIv_cNTISKm&!s+0iQMl+K)=G$`bW`x zZI{gCk08a)XC#`uO&V+OJ)2w2@#29re|_my56jGzx3gc($}5(~_|>_SV5GufN7Fak z`fN9!Y%g7yBeF=~*Lqwa8)p-x24nn*JMBDECeKKTIuhhUmQsJ zDddaf1IJicKgD1_Xd@mum+bbV_?dWwJP51clF1@-z3H!L2SEm-Q3<%Q%c3kwi;|4Z(cc%&Qe73yQ^4bm1c|!cvi$Rb zl!XAHR(UDah6B5uXzm9(KndUs2uvFzL`}-m%-je+^LT5q_^ok2G;ZO5*Y0HhiQTN$ z=oh=N{BiQKiQ1!PGQ1qD+B!jP0(o6@r%De&oKDL&UC6_^CDoi2-*lpwtGkw9G1d=! zfjf3V9B*a2!krvqq)zV$yN|5rvPtilUO~?s$!x=-B372D^_&trXLK({to+cD0UnH< zRc_le0aS7>?P3POONMVmVVAO&r%sqp(+CDhm2;GagPR%G3@6I3j3>(zy>pBoLOJ}- zt37C>*Q3EW*tli?AnAMrAs2bc!^2=R1oxHSNv(qCRlDsh?i1QU@>@yKq@DlDqHdrs z-J40jCM8Q4&;U3#E(h>{^^wl>h{{hTFXAz=%9>it$g>LsXUNw>|5uRzL zY+|R2_6L(pZ_YTr{s)kUxQi?5esc4$nb0KU*w{SQTo?WuzYHK zB%a?5jy9Xyp|_v0&f(>LO`Un#XtZw)KC`aj%G8#$64|s;9e)mgKjsoxvfbkc!R{i& zwI(V&I$%p|O_OT_xm@RPN=Qztn4NBhemGZ#7CQg+=SrXlK;!{P@luoHZL*;a6a~8$ zn{S(%=gGEWN|zjeNEE0Xxl#U$rMLDh$21VR0ZnbKi7{Q^E7GAUt8!BO=r3a6jaN*x z06Fm!xWTR9GNajpZ$A+CQhQ%+v5r&Yil{Pj^h?LSD>{PFAfQ9k#i%mV%;Vr5*)kCi z`Gw$%QCe*tx|}5*Is$XT)c*j-$;*&O%DJ;MpBhNGT#ZgarbO2q3Yk47+5Ki;cBWd{avDqb{@ND^b zZFE)zj!k9y;kP2EUX^KDlcvu&b;4rxm;NnZRAT(!5mPWvpM9BNHu4!MJq<2_mQVP= zx2SeLGU+t3*U>Xzr z?KMSXej>+3h=zbYuE+WG?sn&Lz2Z%3g(O`w=|rd4ij?0_>oXp5$GgGFo8Ft9uF2O8^{j>)GgGfXT>1v63|W3vcaOF;GBoTs zXOO|hQ6S5tsdzOle}%oaia#ZtQkG#1dz59ysw_yv+RrBgZ>U-qOhG!DJz9TCyFjO* zL3+_1zwx8P)8KouJM~2_`8Zo)UYQWJG)z;R#JPgui*})C7pT*GzI_={d8cDun_=}j zZC`RSa&zq>Ns&F-hIeSB?ugKoGudY7esw~Jvupj=)D4;k+I2crKZ9^dHQdYMr=TNs z!yM;G{;imo^RV1n_&%*xR~e^2Ul~_C_y-Mtb6~iE}S@U@! z`xPRhiCs;s1DIRKC+1w4rzy|!m8iDM@r-Vv`n+uL)=?@nr*c78QhTgnJ4@G_CRMDk z=3C|brzFHr)4W~zkxzwa{+8o)$w>W|dSO42kX97k@j8=EvNc~hFRLc;QQz-};rZD2 zr9F$BX0^G;Ga-sGdxr&e2Yqj-KbI^8JnWdK{;hcuJlP#znPkPA+BuPG@Lt9ts4*O? zSChO}Y686^8?lo-m+|ztKsohs^3R`~_t#P%aC-HAMHf{FWzfTW>PK}V{W#WKJOz;r$w-8;}Z-k)2 zzc|$&7gZHKW=$}1z3heYc#8z=w3?Ogy;jb3^p9v4fWxUKpLznXZ*Y}2r_=&-;S)IT z6IbIve!qa-b7raSfmO!aA|`633ckI`Q-x=PWX(b5EB(nLUw>)g^03tzVP-zN3~BU$ zqji*B^&UfphK3Qd?t^f)*QM^Oh>{zx!tmDeO=E0g zRz%Ad^~*Cr{!@EXY)Qhtjb}pC^S;J9K&<>MD1<#pf@7<2m#lKfKQLPB{JL5EGtWAE zZu8NJEQqaKRpVFlpZCBObB^^}hpu6B%ER#AW`cupKKYZ#D85t@r1C8CSj!%!-E>Hk z{7Ncd6yY{AKKDtz&g%8U@mfd2O8<1hlK!ryhTtpa%rGK;rdZxFgt&r$B)4SB`)0)1 zs%oKm2LJ8x-AV9R$dTF=N(V2Vpi`Pyt7}oKMK}4iBWzJR#Uj>qRpEe((X}yI$SiUN zy$)h^=_`qA9@0WkrRB$%>&@sk%$Gmlk^53%$_Sn|-?y#|EmE0pW7h;f%{EnD;WbLp z=;cJ2|FNzK;M9#!D?VT`NvWKD*~$+iz@KpU!4yIM1HA6vFT#b5YuF<~D|>V+=DJmz z1y%C_AS})*S-94~_Lku`aNYXT_cDiDN_wq+_ou_{`3!@{xglzDAqKTS(GO>F9$t>$6bITa2?Qy* z{ydD7(0}uNbnZE#N`C6&9%k3P^epYN;JS zEfsA*D5tonz1bsDwpNDV5^WDcU8rEI8I5AOu2KHl@981b_GMu4IohVM(4+Wmx5e~1M zD^Cw}A60P7lL!8k|KaCw5;7hte$81e)>+X5y+#LEIoxhL6he4Xd3jvP4d`jkk8XZ{ z-u!u8^zWr{OF!lk}|Ktol2* zDaar<{CT#)`13Ne-(jx_(9Q649->#JKUO)`Nw9H7w&I!i&o$NJ++<#3-&|zy9 zyz_bWmw9R3b7rt-7r5gFFxW76e=Yw58N?dCrlo@C=1~+b&ToeITDI8TIuwHvWxy6> zc#~veK4^w8wKhW)#-;trJTin#XSy%G1qf`+A5<3Um+J!c7SqcZO*jS$^^f9yUaPA& zae@x-G*6EV_6n4$0>GhqotnizhFEol(U|;3>Ir9yezd$0cZzvO=$-YxUh6{lo0v+s z>(t5!PJPu}9L%I;h^&P^m~bZ~yu&^`666@dsJsI0YxNf!Xabhl&VQ?>VcDDSxXeqb z#|90w;ubWf^oCdB0|bK#STl*I46!Wow!h99O2&_N*-AS$(*_!te7(UBaeK3mH?--K z{PJd&nt$#qd1~pH2+|BRls!79taHZ=zM8JUGL_ui0!R#>9!l4~*EUd`8R*8o@$Gx& zvc2enUf4(F>CNH^M~m-Boh;yPj!A2aozMGaA=$CDDSSRWWM}G*%stD|y2cFr^@5tprF5Cw1B187htPQM7t@jwxbnZE>2jL=rMCdfW>mTSHwV z&{2n+Y$H+vZWpzIO~Z$0a)Y@NnBO^ZrI)QIo0vxY;I0thJ}OGXK&WErA^tFF-jL;UWbtM%$I5_8o%hP>9BpTs?eiZqrKnzJ_uO*wdA}8 z!{FKDbPL`zdrTooak_E)+y~>v9D*clod^Z_|VX3(Yo2q^IJ)tk#01Ud@v|{1T?^J=8+*KYa!|6#e!DL zS$MqBTQ*bB=HG*81?reZsb3E*0&+>TcAVF^wT12)DlClguKjp%oC*iAhNoWn=FzYgz{>^9Y`=J=(}Z)l zdyC)M=@niN{gUF-lCDp%5ED`JX(A!~iQfY?v(cwe8)Ik;(m?V05;s%i%a?X{?FFee_`nHq5MJQ@pp9EyN zE`zLdcczn`$`1Us2wJAq;tP_NhuTD()NYips$cY=l0{h4C%$&=!m>hMW=JLzHMgqv zH%$&4NB*=nV9Ju_=gRCEzx<|pTqiD3n^CuV<5V^ysi0OLK>GYm?90|Mtwiw+>cy~s za!%=0LTs_G2Dxt8euR9_S$J$KL<~%K#)fax-*xD3giIz57c5+T(?ACc3x`3s_i|4& zbp-bM8_u{-hY{K9243AgD{BZ}uAdVDM0+!Pr%q}$xh@+AS%?%h4cioVC8gS1Ea-aWokRT~O(DG*eLU7jwy_Wo!}A3eH+Wl9Fm| z>qd=X(N9>L(>?A>gC}d}I|mVR=R=&ytEGLM^Lm3^8BV8YpkStL#2*`oqMzi`W5hlX z(_VIm%{r1{?q56>{QEa3>=G{YgySk6j7@D^q4n$oZ_l-Ts-ljk4fbEf zyAJ?mzwsKng}tmmeWTcT7_4BO{~Y6{&zKIbnnO0LHNO<3?;<=|_=V7Kn$*zGcQoB| z*-asHv0oDobZvNx$tZLXm*DEZa%hgc3{6MceoalPROS{q4^c6p~Vp@^%bv0ShyaPk>w!*3lpwJuRdACgwG z=b=s26S&^f>2^N(ciue-1-%${QBFqMTMb9ETyb)&-$6x?RSqK4yiwSUbb_u!;(mm5 z<-&POtk7WD=WjE&VLKvjsA>tw5d_4RVUYnoiu5b!`sV+2ZrW(}KR`lcv2&mLoo1Qk zx%ID(%Wh1_bk>dQcLF0(3QWt1a3!xt=GN!)>sxGYcgf5>ZYV8k;S$tYvcGuO zd4AaNv{l$}T(VRaRt`%cGIY2nhOXs(EBE}#Exag^D_ee(p~Q?@EO$xL$8V^YY6Vvo z@*na~zBmfw#JvYrM)EEvDZMz%ojgz@IVWIjW{`;T0)Gk857F&E^ElB%v5@L%p}qyN zcpg>ntw9sucsd`cdu-$Q!?5+GEw27#9aM)p9%P7I-C}DH}r!?rmCziOtUGxLucJ_j*(J^kbp(c=|hWu64b`iVpc zRe#U|lJVmuez-3V<{tHm$v}Yy7$xy!ANSMC;;PV)uM^D}<&ZiYV}^lv2he1o3;8RN zLj)A^3IOgU31V?sI%}V{39$8m(ajaY#>Kk1D1Q{3kn{Jj|$X0CK${FMN7?Yl=ox8ke(J zt#8$RB5>{-Hjlg)H{=P1EM3@z1u6b&_UqkZQ>hbg4-j=d-~FtJaZ15}I|n>4=tz7L>sikjxGqy0%P7{3=6NP>8Aov>A~ zu+5ca5dD!FP_n{1Y`x4M=}kK_181fR3Rh^Y+a=bTSErLzXsY)l{H+H4Tus44OooL2 za7P~_0|T`bC7PTZJ|@lG^r@mTQf(o)+% z@?QMqNY4LTz^}&Ziu5Q49*~u-i%RBZf8SeFxmRVRWiR#{^u%Qe@|b$)O@y@@ZNn+E zqXs{Wbz}3}v`9KsCPdx=X)-^kRwS4%%Tx45m{%QirMW0OGY}GBrx7{`&CBmnnJF}X z>LX~(7wV%gOoNV0(R_e-RCJI-Jgn~{gkYQi#b-awgC@3+MA3bi9sb$FiKJdKjqh$y z4;-55F74qFY{%HiO0BhUk(q5^liml)Da+z3#jw(9*E(hq+J^C2==XxCfsGyH=|C}+x<FU-_DoIrmCY#0y>^XZU=jd7L^16C z60%mmCp6`aqsBx?zsZFcRpy#xAxA{MsNmAY zLahd7A;<`Kxj(s@LM3%6vz?cEm{ltnEW`_b3`qO>{0A`2dZGy=fb5-71x9;*n)APY z^D);-@3Zw83rt9CK4Y;wX6QRQS)`BuFm~oT&2mAXL_Z!Z`~X~&E`g)IxrgBRTI=#( zzq0W{C?t+NCc(T{vV}f*EFB`^pe&n$U+yn6o&i0q z+W4{SyY%P7q+Br$8?9*aB%7y=QUMCw&H-~S8kOvj@R#^wXp5g7R08Cp_(&=>K9B16;6Ny@}ngPrFSz3R=C;eELp7D_-!3A z?;YIxcb=HIPDt`Zhbu$Pou0p#1Ym%<9G2Ou5hiT`AeLq%wOJQ5!i+m|t1lH=Nv{h` zt_vzd%F~O9+RdxPGyeK~MNDzevBp5NqdNI|Zj3y?G=1Qsq%FUAl4Z+O z`8VPjAoyy(Y7U$2_@Pp`I%v&Ld3#*0@vwKp{pSdiCi^AN2sElCk-~s2BaHg8xrJ)w zhmVl*FT;zgi3vhC>L02jL3HnzcAL*-ornBV4dNNC_SEB7!qalkkr9#y6}sTmvasbv zP#LTTJvMp^Da`Y!Mab-0cl!xjwJAM@4n@PKto*}t9T4y)v z3Ah*njp)Ye?FtT9FyPi80W%5?`JD@&Gv1QPDxK*%!#(@+r*hCF`S=d`G?x zJ*6)LyzoV__DCA8og5QsL-Lsx&o20fO|Lw@%v2h{G0HzbdVdJv2Q5;s&8wPjNYfYE z+i-+DF=K+o08O;ySVnudCP*TP^%WflL|kEIh4vHsmj<@8vu|4ei)Y4Q1+ z2C}j_B$Psr-;2waAGW?uGkzsXEyp`2d)u9sARJgqVsK(5;zNef}5G;fk8 za2g^ZFi($QEKy22pjgQ^E<|Ceh;65WQGKCnW6?|jwI5&cIUayF4k`rUe;_i2n=2}6 zNQ#45>yTQGH&N`PC^yJur!H{=yYU`xv`L{5)k6WwtW{bxkh4S2!(_SMct3z8fdj&_ zrU7hyxSF}ksaBoH{B635<^A$kd$Bh-&b>mK>^FO9oz1+KrkB-=w8z6vUzu{Z$+Gt{le$A)b(5?r=9rl&-`qqF3Ke-NFKOK^7+-wq^eA z>rWopa`+A%&!%@z54X!)>9cEoGY_AQQKv`{fi zU3i^YWpbuj{i@;o#wlk0`C66n_ckl`-?MNQnV^(e1_)E%n;o`JPtgv)t0SMe--W&7 zEK7wTMRDP^E7WSxxCF(cEcms!B6=Le%j0Dqc~S5fuSPc+rg)X4lQ5-V8yfC`e~NiA zR}%ZmSXhO8Omnp~p^3&mXX!X7lkS-`O*H7y=k_mngY;UGVK#{_wjXsVTArUf$|sw7 z3Nd7T#GlYOT$I}tJ^OMi6qXM!)@Is!obv)!la1O}NMd25kdO3LM?cFFD>C+ed=bd8 z5Z}_xQ!4TFxV`$HW(V|#o&+5)iny^OE%G)HC99&1H@|SAveU}gPGH0XDX>c)dv^yn zYdjyzz|BvgSgU5Bo&NabI?GePS3l(Qj~L!1)gI+XR8s2Ii1)%0Ojren->oBg4*#kU zbWQs;+zFh~DDM@>)CIixR6Voz{9|NCB7`y9;ldhINYNt^F{#fme*C`Q^NUjjFq;2F z&MqH~qW)=Fv8X|8hy0bG1e(|9VZa@3#^sI1DeL~HkXW)^wIdksKOgm@{h-iurxFGy zzl3YZtR|{m};X_ zM&K!3KGRBj_|ac*O^sZ(<@EuN9Qo5-3L8irUsMLA;AG`|y6^0ZQS(2c&0nbY;;7Dx z9!0LphNi+!pYOG0fa!66D*7-^RWto-w4yi2UXh-v%Y*pVhW7w1eX3Yf_ve=pM@6xN z)l1+8ULZtP_fRKx6EJ%{CY*~P)|ql<)%6~S-bCR`f88WPY$`XRB=gZ z;#H8;8x3~WKm2wB0QYl_D-=A{@M9gp3yzK5z`FB`!4VGKEvlZs6l&4BS0gCC!i9ni z+BC^Gh06in6IAub$5+f^NW*d`gV@xOmbyTq8;8n=6d20D%r~M4bHf@5myP#d$wLJ- zPPLs{-}+!NmtzlnEdrq#%V07tfDE12h{Je4=%=pyo3_FnZ+NifNd!Xt-*urFS6;Ca zb7V@RmUBn#`92tE@=%9vUoX8?2inZ!y> zT^Jx80+O#95WcEW+Re~X68xUc^?cFuR>5>l8#e?$qZ?4x+ulP?&0BpVIR*KnB|n$= z%h!ESysG{ZYk$*)Qx3-UJ2ZxP1-1n{2F%8NUrSIeDflK=NxRGk&^^ zfxNX}!Er%B23;`zKQpD}3}2aQ8LP$zuyBXe=GMBg+CULc#UJru`N4+UK5VX#G#ire zm+5Q@y-p7_eb(iG2i+u16TKSTY22Mv+}V1{KnUldL|gM$V2@MLj1@e{Lu8CFYV->l z=*?>|`}|2YABHfO^UHw22gfHKkzx1G$QzKiwuoSKYRCw2P@(wppW-CLy}fn;p{eM)UOC4|wf6nsY)6wp=?au#F&Q465vvzh*o=)LfJ1NK zoK-62wJ{FZ_UVtybB?mTt~iq-qulE5ydE+_IjyBf%|;=;R|7Iq7Mz)%(E*STMRVds zI)T%yJ)0S#sm}oVw#gIrPuNKVJXP4zlD`uRT6@3Z_z{ylS2`Ej^ZGH}-sugdQ~Soy z`JwrZ8T4XMHd=6mU~K9+=5_a%7jxaMu_p9UkqZFobpi?gRcCW-a-L4i6Aaes~AGpv1Q37C(r}yUfmOai81`F$Ej>|GNvf}csk(U-V^xOm#Z%2ywt#z0kuOw0cK_K-i(w!z@sk@!wVRI6=y#B_v-3{}T429#v zEi2CkwB&sz_@3x`4*nltp+K|h+Dq#{fX2#te`ll+LZ`+R*KQ!$J2iAskTvDBf4Kkb zaA%tEhFUDYhpDgxooUE3!j!9Xc zycmpq3}ytI`!hy512^X!Sc2;=sb7?mAQU7LaUFNtch>Ejc_gH*;oC_hfe+M=6FYSI z`OZ-sKZT{%lig}}8AoAgIW}@n#M>3BO#-Cboa?G{k@5>wvL_JsyJxRWQ(55DVIgSt zk=%5rlyzXJb~B^GjDl2 zdvc5uEwskLyu;`rGgyYgqu)zPV-!2Gk#>3?f5u_AV)>4U`1mGllRLU62@l$Hel==F zb-Y58hXM=ore-G)pJPW+k~A#=cJvsxO3cj`DiFUq?@>fk%>^bQ&^rUX`(UODXLyC} zb9*#YE<|n7zH7DvANsWkQ0A6{mtQ2WZN6jVY{rpzj&na+KLQfH;tMzts|QPs1db8Q zZVoiBND9V`%5S5c7oLD0wEm-w@vgkOJ^rl5<|wnm8181CK=BSy%>6Ns0c$}!MuK6x z!if?K^nO*V5EO>x|9ILBZo_#LdtNC#Z$aX}Q_quj!=ZYY<5^Re{;Vc#g*gNfpq{z! z!6nNemyCWI1zR=~>_g_iG!w7x4xW7&HERS3yi&_@BaS~2gTZmp*^F#1?)s|@2x^PP zMrBHy)gM(mlB=moj}ZiQV=j>f7}`(WA47W?2?;2v3hfj^!0lL*=LYxfNUyS|3r9p$?(Q#$T!%@OEzb_eddegsrl zc+TS1QF8xe5~d;YE4mu7wR1N5$V_;EU|e>LIbWkVo4498ge+SN8tiD*05^?mW-|Ot zKK4FQp7tm;`L5oxxIterv%)#G)+DORyWKfnB=2&X5wZ+n0aNeVyX$8j zWzD&6&DG^tl?5_BjQ48+-oKr`ODd>N^&k=GI6X~{v678p{IbWc@2*JM% z9PBB?8#-b7l0L)QH6k+dMi2FC)$~U=-u?}Q0rjfcUWnenBE5f>hRZIy2bhLbZn{8G zK5MJ#Jx4rClEOyo#wVG8`@1;M$7qH1V(YzAT%z_rz*b@1X}{@P18yZB%bnIwO5?}q zkmvVGqbJ*GjIxVhX^FqUAE&r5ZlO~Cx5t9Rw5EgqgY(y%L`X)~FBVmpIqdQ3-^r%m z4LFUBp$C-^Y$0pLmbd+>-u;Z(i~H1{PIsJ6J|d?&N>Od`21vZz;H#DCm|gB^ zEt9CV!!IMyqfBUbiRtG~meC3s?X2g73J&}JA3#pdgxm&Dqhj_Ex!B#9t7(BWrj;>8 zQWBxcJH;}~&@Ufy`Brs=9bzT505pXXsq39DKaI5Aan|g2E7rkXP9?^RXIgNZn`&*1 zsW27Ar>XXfZUL`yiW5WDv`+W-hTy^KEzXf!zpWx06xf#e+a;dnLaBh-TSBlU*wVtz z8y$dL;WO8_cxfH}uG{|szV&Tbvyb>?f1k|N4VRdzz(o%|nM=~sTktk!i2F(CNY^0^ zT6VO^IV}bikJ|D=kV4$9mB3mbnoHgy+N<@#L4V!^-OQWd-B;9TzPNXPqolv0*w z3ra?(HKW5*+tuEz61(spKqj_pwWx(oXlj!am<$&O>Op=(REz+2&UCW!VbBy!lWYqq z1JdF|g%=4tKp%cUwbW}1wsR8MHQn)wBArS-7;$^RKSU`6!l}*Bi7Y0!bFKdQHyjF0 zG1tyWm?_AKVIuL3dR;{Cb>o2WW@T@bV98Lx)-9Z+RaLO->~lS=K$dBi&C4}=PpR9} zD>v+nO&5NKy!p(mOQ3n2Cq-3SD1zfmO3u8D*Qoe*L??}YSsH6l^7?!{fPPkeo;OWL zzPsRg26tAb{j-1CSnH(8tNDNxC--cX<}~41Z61gN)Z&NT&%U~dxD&sh*9H-aeQI9w zGy8e!@{WvF!ZDErNqGAV{{al}Q7exQB~bg{PcPeZOXk{rT_q3Xo)(lR*@HPHharG- zG|!VMJB4`UsrC1>E;4^Ig=wU9g^Rh>{OL09f=UHs+hTH#B~?!9OZ;CFzmEs(3jXZI zmZfQyl?#>G8v1V@wS6S6ANSW%xCtDp9!%rFeZ^w#9$YbReBAOE7P#j2sw~=a7Z#BA znojCm#6phFQDeZQ%Rz}L9$letJeEC-_EzH)E^YPl#$&v(IQUVVcf}X{k0J8s=e-tX zDW$MAM-64^65{nD4%w@5w$!^H78o?KA-iE#G_j5Q;TGo#Up(y~VbGLXl%X9Hgz)S2?g1GI00UE{$#1 zN=#^FH=iLn9`*YHBUBRb$mu~>PNV*FzpBJSOWeFzOfjYJn zCf(hR)6PURX(gw$*Z$$4x)@k_&Ev;e(~0Ts&+pWqMRZybXMng6W3DHRVRHcg@7Bj3dn=mtMOr87JZe0Tor`?Mh~c!W;amJMFU%gT z^k9H#j1rA~*!W_<4}nHR3E#*ZPxrvY2`wZ97X#xpGkmyN_=C1G{p;Xl&mBAc>IxUl zcGg`WeU`{Z&>%Lsww_hYyCO`0c!glERBM^ps*-e{c%G}^7T2jOxEI^}GzWfUCw*4u z*wPzgzjU0*%-i|>IzrZU-5BuJV%YyY!cg^s)~Yw?D0A;xSFhiaI!Ti-b06x%W{4Ci zNGJxJ_&SW8JP49#-njy9vj;q&bczd4PB~<_p_rL*o}#~~;cUL~5vZ{HjhW%=Yf7Fp6eWm+zXQ-tafJCeyU#o8 zYHV1QUf!Ii$v!ylE7OE6@EDJWGGcRQ)+Us9KN<^_>#l047hJ z7dmqR-i2HZUq`iR*6H;rd_J8}t!s}s1qky~lQGIpB1$7azId!}pokVJ7&bq>F|!ML zl49+dSMB2P{QxJIoas&1nrTk?TEjI{`oA?Z9Qd=Bxq9%k_QDVDOId_p)8@Mi%=;iT z2?ohYk~B`nl?@bCRA`0e%9v|Bj%w0Oe*7-EVP>58HZA#edRpK84*pGMioCe$paX!e zB=!OB4$1xH0U9WLxIqkc-GE?nudB586~v0)l$91~1U!KMyKc@pc3T z<}}Y0cj#^{QJ>r0zqSZ%sMY0-+BU4tmAz_GD<_RlqPVgk=bJ&92+nav#K zwZNxm1#RKyEetkI+ye?e^8Phw6Bf}qim;imGP6z3!sYP8wgXsP+J+cEob&vnHmgJ5lGo6J`E+_}K`?KCIljZXf`Alm?7FUoxOKD6ziVe4FiHKq2^# zOui@|(d6>OXs;q`I{{4ow?~JDxHnQP3z9+?pzHsOM}MZsXjhGJO42*GA`l=!GiG=T z1p&b93b}DIcSr%A>9@?onM-&hc5LlkRzHy9&x||>$hi|R*u-SRn4158LN;}z3b(Mi z8aJw`dIwwP46*EiS>BWU7`)mv1nHLZg-3%rE8sXu<7O*0PXn_Z$LSHaLBgWKB*iL5 zFPFH3yG!Z`>c$S+;OTg|ttiu|g4SmxB> zR|fYJy-% zE31{#DAT6Qyu8@n>2x1*ft*a7_4t0h!^zwRURu+0{K*PhZ^U5r@|`j`F}LI4c%!?p z0u<=}Fyu!HUqv&yL@Vqb`dgXx7$+Z2CEk=hPKGbp%!7E2Q*5?I?fjxZUmy@x7qg8l zC7eE`faen|KbAFRHj&^sG0&o4sxvywN#K<bJIPwx$pvPRe)kKFD;YFpp@d?K3f2sNXujF+Z-{#snfC%ZXwgqtG_wASLvM*!yCD;JQfb99GM zasc){9SyVCG}9kV`v6FX36)d6+gNgdJEXapkVNCqk=tN{+x@;oW9qU2_wudbTj!%| zOQpqw^1TD`N&ewo7=T7R^R#nJkL_0Fzn8oC<_>CTCo>7M`gLqEl8v0zDj8=$IT>f^ zR*1g+WkYnqaW!Yr*NAtMKu0=8h;tUZ&h57yl+3G1=jh1CMhGOO+m%hT<2(i~+zFBf zsP3FKFmsG5-s(Hkhv=e96wiME&4<^IRRTFGPJVSYrd@sYKVGWSX7!vmS#EQ^xYP!E3Zq~Ki zJY50w0FJMwCZ@(2s8B9XTjjND*od=ecoT59g?_|`B;kl$3w-eY&OJoM70?$&+zbg;GLXzgYfegGPy zB%2Ii2_{_-E+Wkv0Z|403qJFjfZHY67?o|oWJUg^}tyDu* zHye11))))&@+}vsr)b5b$ELq5ti)MUF?|PUPLuEVrTTNYnNeCLTgnPfzc}o)ba?eO zghBpp%39Vwonp15s}X4}?DQyZ*2NU+8?P_SICj^fl0d1#M#F~y7cKpkVA_SIe5Lgt z@P2Fmxi>e%=I9DVH`C3H>3#81pQ*uuscG!zA@KU>UnB z;n~jfc0R&2vb`?Vg)2s%O)S3n1Je-PC)y=q%G%H23bZ3|)6f}im(7-j7N|(p=d0+7 zb0rnJ&PfMc-u|9X37RNJ4WD>nKM3%U+-|!7j2MDUSvKxRbHCO{TRp9{lAK_)$S-@R zQcA_4p$b3F*_mG={5i$0D@g&gF3t@x8Ll)IB;wY-c~|OAu(T7YK?PT_DSzjBAUikw zO+@wh+NWTPeb~1h6F)XYW9*9&u?(WXwbihxwgEw4r3ruHg%Q^CTeORR#(s8SN+~{R zay>%11CJo-Fyt?2@JoG>_Svtub5?d7t+UHwb#dO-W(+W8N5Nnlm5n0iZC6JLePD&^ zC%bt$ijlGpqWZyfjN}%cTo8h3Ca0A!_aVqPXZyKt@I-~zNK)pSZBmQki$)ONG8+b1 z|4+Ze|3qkFicN+&b@~LCaB1l^ z;x6lMsY^1_Jfmn(O74i6qmux4VT=rwn=D7R(Ei|3-@?)wBG?V5&uH>>3dy3-?Dd7& zeuTu{J&clFNB3!gf5z7X%}P!Bmto_N<6_o{yLJu{x(!noBSXk>sdbP^6A<4G;Sy)C z5;S!V|7|APnJ-AZ!7+dx1&w;-w#eZJ4@!cCt$kn2GW>1WEkMjuL0?~UbbfzS@~2w^ z5#go3Pd4v=-(OZ$0Je2vCQrLEIhCL{oYbg64 zTV?2RnuBYRGvX8^e<8o4tYu@8u4$jw&AjI}pMI8eX7s0~v!NimZ)W91wA{iil&^l& zivX!K7dfcR0*_9S~~N1Liq=G}!yvr8JwDLQtIUX^t6DkcoqlYP%;6u!+Lv zikV&nR^0a&xRl0xBCZ^DWL@qr44`-O@=az)O@cHLYkp=@2FFVc>I0tYyRjr=dyuh5~ntGnM zSJZW~>-E~Xb$YEs877uiEbheeSf1zSGpHoc{NbQigU@q#zAm`QeXg3xqtMHuLY{=K zuHc{~mAdD+?sypx6sW7+=J3TENYtv$T#%!h&MmRn#92om8|JVe(i2ny((U8V=Q>Fm zL%I1h2XA4ESI%}K*H!0v&ObSG=&qM;(DJbdeUY=ht$Drca2yhvWFX5jac?L zyw~R=G3oVwiLL2CToC)X1D-4A zkK-muJQH<{!96t~oyP1Dz{CATB$7!alicGr&8xI9YWuA5`(lm_<2}CVC<{E-1D}jR zaid_Mk}r-vF-arNah8iW>N!yXp!&8BPQWNrE3-5 zs~P2YiBaTs*}?*NQRHHAUUDw{VdAQd-~@sQ;Ee7`yXWU1?|*Cdvhp)=v!23@IA4MM{MVHj7>j1I`QS=l=OZpK*cO>+upp^Z0i$fr`Fd`gVEC<*&grfa62>e zTfOUy!pc>O`5oxq3(N0T^^a5P9+M|V*RGOXUXN1E40CVN%q2h_LjXws08qLk!Lgob zQbFy&v(8sbr;}7<2+@yDHjwXdya8sfx%kNC^@{%8XSP~d-dLfDf(3<2sZ;=vNj#rD z{N&XnWp@Zlt!^N2GSEk-pHi`zhMF?Gyq3TaJ_nQAE|k=z2qkY!l9%?D?_W5%x_wl7 zo}Hx8LDQj*Z9k}Fo;}6p1mE4weV}nbao;oPVKXeiZM2NNvxV*X@Aw?!D$S{*D3Mv0 za)Zdayd{vwL20a;zaH2XRj+e}r6gE9pKMF4N71s8M9v(kU$J)^UEDy41*&Eq~ zWV7sl>d)5leSP&z{{T>GbQ)a-qHU_tX_IU~Du5_=zB$j2SzS?-AGNB*W5<@m%1L?< zKg%Z9zi%fL&?dWQF|tiiJ%%`jUqjXrzoolo{iki87w?;62nhGa@D4PhNP}tRi(`iS zw=FO}*!suQ85HRwkq{z=5-?&~zT?0gUC_%#gawmwZXht^1K$DQ&>)YDQ&pW}hkM9j z@35hreAXr74}No7fc?fj$LUtq^*49EEAfX%sr@*Y{HUZdAy2(8ZTTL@=Ouh&E~EXM z3A4WYR~4KO9q~tuU`EorM<>6LngXWC8EFA8X#kaq zCz1u6bYovrZ3x#({u%m@(%<=By>F`XTzAaMdBg_@KG|b8)dA<5tfXXa0f`IbfoCEO z_{J7ZX61~jV-4iPVF%k7&Mgrk#?wc|XS?+Vo2m3!Q=s)nj0hLjM1|47^%YoKE#Dh> zzBA{$dB!B3NhIa%GUA}uIY^YbZrlQSHJOx~;A4Y#&TD#MV#`8_Cb{@H);k6yWClL% z?l@K845dqNjxQNttCfF8*W9~HZLPxZzi({l=5#Hw zitYtXc^=tWqLWLZiJE8~HA8m9aL$x})CXhnnF%Ur-ySjJbaI%}GBk0-#iMQOxNTra z=BWD~F$00zUPL!;ageJx4BJ6Gn!5STYd_+fs~$7Cz}y#}=dj4gz`ZLHJ6Ra2z>o={ z=gwNrV@k@=mJ0M3Z6tXa#Hd9f?3=M&vpVt#vB19dow%Fl9ZPTL#x~$TIiJOj*f)IR zIWvtj28^!i`-O@(#!r3?$Y4A-seF=nuWI+nSj_3XS{$>XkZlSnc>e&m?UUVkGXD4j zd);NL6nP5;1d35Zj8=O98sK|kGY(H~dt$SLbhXX*?U}W#RJvCJ{&P=Gf_*^M%chzb zgb*{hmQ_;A!jQN9N1nr;`R7?ChjX0!qjpA8tPK!&Am%S44BLehz&wldisX*`Upd-{ z=Y*h|H$qf0h6Gxk#C&9l%jBe5O-$_qY1&x~NJ}CXJ3&3jAGSdI^MzSd+pg{p7ya^O zkG^H0C))uz%e1%*)Gw3mo{wH$m(#l3vGiSO>Y5;+M?Cs}PkAfP*=DGdT=={#YE5~B z@k72$o^jxe%_hL;oD*);I2V&WuQ6*_yxLt(o(N~3O)5vIQqa4h0l+j$pFDyyKP1q~ zPps6<6fsLYj~ohCC1xZbCddQFB%FvNcH%bxK_lZc+gP@NG?HqSb+gF?G)Z(#42Ry=f@6KLHVV+pSqUq5i3uv}U1G)Dk z<~iLwQl3|qXO+k->{yzvPCu`$gG6-Rq_OFzVIfN)T&;k9YR(6fOZpUzVR5Pw64Gz` zN>HJ;yWR77oUtGRNv;L>!`|AH4@cc$hBn%8QOr|uVK{CDx4r$mx; zNhA>?Fa^jYDEH$n)bumxCP=2(eMr_+D&ehISUh(8cNv?WAA4nWs^l9N!R?y{i%5DP z)()c=>kgyR{T{~Mvai(L{WW}5-edto3q6~VKkuI3)V(bu(L>g{cAiZ?N7YEt=@xxL zNiCoo>@5+=@&|k|sTo_P#h`s&pRTs6L#kae%cY1y8`O5%R5g*cN|JA%9A}p-GDWx4 zjni_5UBaw;k8zt4Z2^hDQ68pwS{e>y17m~5aBXUZesJlMeR2t=n9jO=vHDFR-|2u& zE%pEd_`J^a-^YAphfArRDW!u?B&h=KmC;ENi>p4u&2x}mq|_H?j=>7#02`1GZyDp{G3s?~ZiiFTx_+Na(&5s}rj3$)LOTIW!atB>pm*cb7@1 zj&PrHFmN4g8t0IEj1WjhrDQU?9l)0z$Ig3N?!Pj0eP`3~yc*35&W|3dZ7>t&iy261lO7;wl}(`dutt&R&KJI zrrkR*h(rw#g7_c4Hifd*|_^SQ$9EgT+ntxHd0c%W8 z;C$jn%^L?sD#~mT+#Kh3X$*zQb~}q6_wSQiF|I}F$E2frny>L6Scg)h)AZ<32Jdy= zCcmfZ^l)jlGil|H4O$sA?&7^g1gRtU1ZH13O+v_z=}91coDHwI55^02cL^F7ZKJvE z^MPw{3`iHr7%!U`@wUeewOiqZj6rDD5R6o?1cBW6%FNnhri4baiH7e~vkj-7C|4N_ zle4j%%PIp@qJ;oI`^CsRH0hsJG)FBwd*>e4g+n`p1X(;;p@51e@dk!9k@mEU7}`y>*8{g6{j-Arth-d-k~tWo zDL9Er`cBVk3tH7Tk>8)r1un3D{{Z74qBG43z_AB#e$oLgUf$K` zAVCI?eD!{n`h%(cGuC42{T^k9SrW3Nv})i6Qm2Llb|*Z^ourkWjk6L-$lSpPilN^h zX4P=a6_kOn_c(w83A#Q9zIFlG+1a8=qtt2xOp6ljRX&mc1pqF*o-F&El1nm{^!RkICbvZ{joN5~lt~3KTmF`>BRm#u#ODBad)9Xb$s`{YnVe+0ZMI-v#vV42 zx1R^w22yv8%KA}yU?>D`yEV!E^FchTD2hehvB3eTHTNIyiyI=NhKxC3N=YMWzW)I6 z``|lv19ry27272WeYva|vGma%m^FW_ht>7HKhpqr)n5M9SI2w2$ysVLp)I$CHGgrh z7mB8h&_9u#jFG#mGNUr6wjgjnIVU3SGzeM*n>E`3W-0+ZFJY9@Y30>P=|VR>6SnHW z7II;j0-%~b$G!*aw-UPm0Pem`;^lPGMI7Pght`Z;rsB89xXI!jor60hHSc5FED3Ep zM9vf`+^R=&`TS%~cg9LP8d8Pr6spkRo94O1!;%ajT9d~HyWwJ_3R*nbycs6l7fqj6 zpw(*O8<$KJx`iasHGB#HeAj%W1KyJD1&$f6h?!azhK<#I?VtlsmjbDyT^aTPhwrJ{7X(ramCWlPg zZ95;bCX;zyKlf0)&6I`FA9Kg*SAu9BeCC-et0Qe-K^_haH_g``*fCYHrqP~_91Efg ztXbKgQ|g;!Dk(O06hSxhnEkM};Ghve@;Jegdfii^$)o6fH>DAF zg4!9q9aM5uLQUo^M+?>BrfC@J?K^$A}JJdZ$caz0j!EO$nk?;Sml%iJEv5a{;q1b{%~f@<+W`>(zN zZ^khzv<1FL-G2F`w@}oLUban0)5E6@Z_DNi5oNFQUi_I41i|!4%5Bgh*zhZMLv<4NAX-PMm zkhj~myH$Jn%1kr^r|HZus5;Hh>#HdA!tu9h?oWHajJm0xb)MhGUVSXk!vyju2|AXH z?gqdlAIBi(dgr*qnhS`7!t$%23A7bv`;Ge#IWIYD^dIhuCf^?bo2wo8J@LABi&xaO z(CBm;X>~fO+$2#%rJdOJ01oE}tUXBz8arnD7TZ{8lpkuURuhnz>s^4wjW;jFa|? zEKif}ak|EZ`e#+ubonQOcGF7H86c26Zb7S79mmdQAa0=6KN+cKCEOV#d1Nl+vo~|^ zoObO5UHQBUN|3;L;>H;myoI>7HfT`4#%LEA>0E%@hl5zRg89iR&2_*WR~~ZOeO9AS z4DrJIQgjI@plxg#{y@yzjaLYUP?376{$TBuYsL3E&Ioj0nTgkqBEf!HDON zAK&|ARlecpDJ;IMttQ1y#`dV7J_b|tv_TtryUx5!IkqarT~%3>fa2Eti>&Lv86_l; zIVZ+Z+C^VM?RK#xNbGxM1%r4;$8(4ej5mnIFC23jZQyO-0(n1-h&)iP_~nPD7hteS z-Q@gZqZW&07L#>R3%0xb{9qlf{xE4^)M{r)H1NqZuG>)&NoGF43{F8H*du}tIxJJ) zoOsQ++-P4X773aeQdA0A7%tF>s?Ci|R#Y^SW|A4dMo+p4_QDMqzaJCyDh zK;3?Gme><^MmrJXJ?v7}jt10*+CoZ#5FR8v~Z&Ce61;44enN1uOrSWV$4et zZ#q{^st^t?&o}$z$@787si_@fsb~*V)D2FnOQaF$jP6S;LWo4a90Nl19nmswriHJwgH)#^mi`>}cLbXGUS^ zp;15^H)qMosNBF31)k@&7Vkz*-$m4ZmDk5CzoMT<)KVP+YxNJLn|i|=R}4*kz_0{( zIrEull2ZiIq<|_=f4l;0_#MtHku+L(=PervHvpl5VP50Me8jzW)#Lh1(81q;FBbX0 zvN|zjVy1|&YlGhrBbeY|LW9RR&`i+CSydR7EB7d2$ge(fN8DXuTE{WtIyF}hZM228 z6?T2Uj8b?ws;tEA1kkfXu+Chod|{LVO|V`Gvy9=j5|b1l8oiN%XaUYmfnE)J_s4_R z^#bY3hkvjHCe86*zGpt#pQO3QA$Z$$T;IM^Q>WBvHBxDH5j-^UhM|(dv3#OM~fs8I$0&_#hwTUO&X8XudLFP5CQiE0{0W!&mX=%nmALZ z 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 J53}5V|Jg)p5xW2Y literal 0 HcmV?d00001 diff --git a/data/icons/mp3-5.jpg b/data/icons/mp3-5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1abefd2c263947e408c36d3972da15fca4790951 GIT binary patch literal 210180 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

>eIS&*dR)lqtk9QDUauWs;Md76FGqE3YQ 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�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

E*&Dk{ zQEhT}TjL2VfT-g=$K(1@g6j~DMUh$6e8~ziF3H@f`eTFbSxPkYTw9XphTiJNSj-bz z!z6GqONixByN*LAxWVo1-iATpC@*76%^p^P3CZ5YfIk3pjyw0Fcb{XM2|9V9g2@Y$ z{HlMMz3@*5-|?dwM~Lj=y!$H?hhDvQ5!HAdzG!ot6~JmSf3c6(d@ZTkn@QI03y7wT zu&wFN0Q7*vlkJ{sj|wb|(pk?Wazc2wBobpMz7Jd;-&*wDVmNQ@7C9W4z}|MCI5;>w z=NS0sIIlLbwvy8QQrs+($j#-(WdzE>j_e-CjAM$dS={2(yC!&)T4=B!y0yQ!NzrDS zH4IsYrO5tZc=r9OmZxo}ORH>}S4)7AQ*crT z?f(GEv2m!p)@+GyBh@c9kX?E(LY0)D-cLMp)wsnL(@mDIYVnzOKHM~K z!6Tn;c<1w>W9%A|Ph)aC%h-?uv~6R9k&O5L^t7L{-9z@Iv4xd&8X z)F16ZUi{mU)py(v2?q{{Vrs`|@7!{;P3eFWD~{S`xX2U~&lVzvsOcxwek!Mb)~g z2-yQ1fO?wqSJQQ`50tqt9sQ2blOhs!eBjovLDbB4Z#pwQu~V4GQIXT?4r%1a{!wC# zuT0P*9C>nMG`yf83Yh?O&m<4+Q5%6gFuY@P5(le>!S^2ZBDgZ_B!(XtHHoE>NT+u3 z%N}vt{{WTGAQl}DJ9~ptUaXscf>(O;&q6!YDy#v0-h$qbk=)%knhP;>`yzCpU)iiqIQ$%)9U{K#e7B-kW&YDP!2jU&MRj4vJ&KgQ6gO1 znBoZ6Lye4Z%D?CDRdp3F9RC1lia6XIzEqh~gmu9@{vR|+;z^`=?L$X&ibf?HHihk< z*#2}QQ`Cjc;Wu!TxkAbdWQGfj@%?^=wTrf7)pm1kkgObnL1DNy3D34i$4YrDp(zs~ zkhVzwXUBy$X{zBw^~PvcrUOwUwS znpd=(+s42`UvVK1q*RtyuLJI1xWfnA2CPOJ;K-4w7dFQvXFHaoOL*=iUqFdu$x;WU zeboDMDcY@*PqaPEYJlCq0o*Yn;~CKeeeV8XF5NTV4#5KY4wsXMa$YP6K`F&Wo^>ti
  • tpQB&K?; fRJsC$3KaW2V*^#C_U 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 YIR>oWL%1;hVbZ9^*z~n(wrA1**`{evSpWb4 literal 0 HcmV?d00001 diff --git a/data/icons/mp3-6.jpg b/data/icons/mp3-6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..711ed95392fd988b1bad27a47d2ded1f6b7fbef6 GIT binary patch literal 52168 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

    60nT4ejRy9$0%Yrho1Rc!usoY6PGlG zMi-p*g_@%ZNaBsV@#8zmQfbicrH6joTlZi(|h*d8PyxQIw-bOzGU3{8iD(&k;0_w>{^Qyg4 z`#LbLQ;SHxWHV8{w;G|BHnof2;mk$p4P}S;CV!c%YOs!n^6Hubp2_rAtK8Ri#5%?K zEk(2n1GF^rtmIg1(t~c43B&YDwsfP>>9Mgqpj+`IYk7MF(jS*+Zac14?~}E}^rQSZ zA{`kY$m11v_g`9cZ0bC4G^3OzWs4@B=56U3WpA8D-*!ys#WYd6ibgl_f?(Lv?AUbq zYI)Hy0w&o&&^>cK={%9Zj_4*y_feBvt2*O62xqB@+89_=N^a84+20bIL2qC)CsVV- zKw9hwmyf?CwB9Uu+4udy8`kI0u3ql*Gsz4Y-ux}`WUVYP(>Z-mbWwg`j1C@%0AUv{ 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 literal 0 HcmV?d00001 diff --git a/data/icons/mp3-7.jpg b/data/icons/mp3-7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..396af3556668962986a936848a3b11ea23cbc786 GIT binary patch literal 44528 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
    X!fD4a1NX^Q*xumA_HyVx~jF z5)PvAEPo7Y^v+m_#_{vF6L&%vTz;ESuh`HtgJV~o$Lnz3ZPUs`VQ0aum0VMx0t^HT zK4S%A-)~W=0C)-hYy9$||4HQCb1BBC8rH-dvQ6es3(}XFe zV}KrlW=v2MaNtW6>BkISj`iM^Cd^l&XUl(*@sF>snr`_Fu(k1@(p-GBV{;=X@;jvj zfA0Z}(SdO%NL5F`WkJdgA~S92+<`vN<}j&(j4KK|^vjt+rra?VQpasVR?)(?P~eE7 z=G}!R+YWytR0P9S5@ee*dFOx`=SLAa-fbJbn3Us$$>T^OCyVB1@Az6|spIKg z>O4;H5=nIww*PWsS3g9@=IQX<&TkGjS&Z(tKBGR29n1znrQ`bbo{7FkzyoJiP#YqT zMsSqcSoomr)eE^<{&;;3(IS^f$A3c&WVnbNt2e_(kLp8tA@we5E41=wBomB<(r(=Og_&L?rxRh|i{x8ACnMXle_kr#Sz?rpWGEger zkWTx#I;BxdwYq5zvH;~76`mEtfduS@B5+lCmSGd`)(3LuiXr&OAyW*vJfl>v)bTCC zi#o3Bh<9ZoiPASNzwv`#kh*^A@&z=L#o2O*q(#Ntdu_@1>Feu;qUmFR0=;`XRYc6M zPs_G^`{eDIRO;4i;RY$9c{-Fnf{yV@2pkwp9j%JEjm1Ql*%bkSFzEACL&;UQiqmJY zkpZg==K&2cu)Iy4K?}(w1(LZZx6EYBNjhcg{HMcjgCVPT=OamnOyZh0)*qg|7mH8=EN`XHTPTvqi>TGRs9y3mi;aI@>Gao zn>{E$#@~CGQ@Mr~;{QDYXK>+>tX*J+NDF3VF(G;#`oh3-sUqtkc=KF39xZqppzNtZ zcDU&Ypl#C6FiZ|$ui7LY9~KA1%V}9au06bC%8Ilo6qO!62}War;~Ti}@vs$nO%uI+ zJJI+?z!6FLu9+s9cbs>>Sx%o}JPfD(d#ZOwx8%0nQ#6@2qcS?6Fmy0^^AQ%mZNs&( zymK#LM*Z`-){IBTMYR_Js&!jT`?DVSUlpa9?2x>vH6oH2ZvI%GRNxk&;-6k4(PEz4 zo0Z*E5~s$k46?cNU(_Fsq4}dS`V@b=+5W}p?QD8Qd&0&8OmUmlj?*V7(6P`W+_!T^ zROG#?5h5N;LI1v_?zh&b?U29!5Gp!$?A%g*RfdXPC3Rm>8}7$buiQMHyPsszv+W?G zdW?VnpFAnq)olym-PR^a(0G@;otQg7Y&BF>2YI_~ib0RMBpCDt{+a6iEk`GgBo)f> zw|o4z1OK?~`{8^TIa)AZZr2;wA=jWM zeB1aW8CZR~jwbuL$uS!$Y221*k?@vnW#hjqa3I$OR6#;w_T6hOJXBE;aHv)hLK@P# z1GbC9O*OWKH>Z2A973`m{T1FSnmw$N?Y7!JJip-w^iMOS7%oT7ir@?)Em?P;Kl)QK z<6jW!-;>lkmIHS~7#v_jf5WC$@7!}#;x+flVY~CTYFkJ<#f?u|E|ORk`OK&@vWHH953I z{K8C5tOY0wFgyEnjnqWlV`C>U5ZUz?lkesE?cvbK+j>Hnm8^Dpf`{eGRKf+`FukUE z1b(?TWco&Uaje_iMSkJS+H)-S!}Sl8y|+H5p%%5Mc0=B42`Z|2lD>bVf4J-Jarx_N zEJg}J?_N%YW|n>Y?t$c}{W$j4RX`!p7h?El7_ zIuHp)v0j>DOWyeOI&Vj7U@)qYdm*h9WIRjd1_3p2hRN8f!$*xPCLRz@wVx@JSr=gv zSH`QA*rAbv`;xQVtxb3QeGD-9jkFOqr$V`+cUaqp%kFplzHVAadRP~g6%HaIcgWh$ zRmG^Gp4+ym(moq#A&6^*EIF)(%H-!y4{*uA znIW+gO#d-Z2aM$g)w+t|dOIww(ENb3x2B!aOhYO=U>g;J^wn?z*nF-dcOwM7ffl^} z6@B}j*cRD8)jV-Epvv$Bv{PgKGm4`CJr=00GOdzyG8<>2{6AJ%|FQL#eeeGB_Jj1+ zHNyQA$)M_REM5f#R)Js7iS?;eXg{yPjRAu}d)D`G5|JJBltN|TP&GC5H2IIl~{49m(8PGND(^7&Qr?8#UaBLhz9zDLLGx9U` zLiN-n6Z-@5UGTKkh4d_Z#2p$=3U%Vl3yAn4=WuYjoiy{$M6;Y9ajhFDi?6kT%DTbS z6YwNe#S?ggzVg|CRqtm_!5n=6BzD>9^o{YuHW8_~S=?G#YA7DE_Cj^KXt6k~U}8fZN19xZgM_m4IisjBnJq#pL?iCTan;o9Mjfj9ioQiV^ zU!QZEYgUstY3l1q1NWyADEWsY!ELg2w7Ktal$rbbS&*D1Pl%Z3pe9h?{PB`AGw{1q z(6Uo`5;6lr{vasPVS;iMkaedv=A z{3&2A$z}W4j2aXqfpX1yP#aZgwi7|VJ09R)fJ!^}B|>^7w65w^+rA6u|E?qd@6t~w zp(d9gB73OA%YCKy0XIcweJ{)04VUw!hb?Lt^S%?cOmDrsC>*YX%vhHmZ+yJ;>X7d| z$wFw9Sb%-GirMpyflaNoGXQyzHxiCaZ?)D%g%oofW}xQ|QU5BHb54(q@8)>lG-}qs zob8ng=7#3i;b!xP-MMEZ##aOHM$(;Y;tIsI7*C_HM?D8bUR0=0AW-5cBUx zlbp|DyD)nIz-p_M2tEg7dmcwBOvEL=k^wXYJXq9wKKk4hs9tZt9i8X>wwrPr-Xc^u z=+yvqb`Vr&&ox|wd_gAQVO)Z_nC>~_KiB&qCF!$fZGg8;NQ3qI&8_KV$(E#F&|YvO zpDfMR*L*+y^PL2GMyxJrjP9&@aZ5>#WOPeaxo^He;R7iuXW1my>kT>UKb8z1DKS&$F?YsI$_AAeL*+XYgQ7Xo zJZV6!@&kwwhE!g}=XJEr+sohi&&irwO{<=FgL2+>Jd#yR7gCcY^C;{+*3^annYBSlB{3Uv9KVZkF=eRDF7*oa9 zn1cCr1LWZn+C!9H-q^H}y^wYjC@o+3(n{DN&5tmoa7vgaHz!j=K4VJ;=o>(#|I%#Z zwyUv;duL6zHCu<{+leAMCPA3sFCJZ<#f1H)n(ge{X(C!|mspjG%PK|zdNYYB5O*?a z0xsV?3n2V$%V@eWn_1oT0A+i{FVz6umP=Mim%HV)_SPQ|6A33H8p2 z8s`jK-mC;lQur+yzk98q#SFG_j%`$GqfD0i@T>>kAgvW}Z83u|znw2hOn<4QcE?tF zR7DJx#ZpqM?&EyWJ^H;glyX6tI|>&Ah7JfC{Q2cKAS|dLfKeU&enw$LD@S1!E#?Z| z91`%td>`GjD9P9WH7V>3KZ7hB7Z8eZY2p+)uWUd?>1RN~Ki<}N&&H)`8>Bg~2xQ8? zYOb*(-NB`)Yrf>d-K5VEr`70DZ7C{wG7=1=?)>dO`G|;eswu#xysbzQ`(BJ^d^rbH z5lNvECCq_{4ideoawLPMO1{q@Bt1ma4 zJuU7ST~C?Q489JGJrEZ&lywQ&AwJ`V(>3(6_wA(T+qq4gd=#`)DrZ^i`%QAVzV2J) z2FSd3y{UrCc3;n4?`U`DmAP!w!XLPaOz4glfzfA-R9`rJAal2hkIMmzo7x&Q$AMxVaboxQY=dAMu;dzPNpF^iRGF2;2BlL9(owGEl0L&8>7+1I` zI;mOrJUaZnoXmvk%iQH62@FAxI>N@W+~rIbLED`VZ>jO&C&BQu*J}&G zL6~5mW9lK~>2O^RdEvlp<&cjUJ|UgM+zj}eIa+>;=`s}ZI)wrkQqbLV?UbVGrXj?< zkGIeVmwsC&lJ?a2_AK=bf&;!y7=I^~7Y~KZ`ge+5_0k!~(K_GP@x9Qd66+ zK)oNho^BXbxfpg}Gq#@#OjfeGsd$S?VEIAnDW$7gOU|bD(N=)|i_45XQ6E&v|s4NVfU1m;r}uRHB*jBh2Xc&`IB zw`wKXi|>!-#e}V|W_o@@O(35|RB0d9Q^m!|aJXK3AT+%Cl3^jJXQI8R>^g~)ndy2u_%IY`dV+DrnWa1*Mezi>4zKBm(X^eU7xbE zDZh0EWLkVwvm`M0-&%`2wyTIuxi92q!KK@|B-=IHk6v(Owe>qxaflw8uS8-Y-7>3N zZM4O_J)nxlTq{7~`kg5SCYfiU^OZbiVW=T1W^UQN>?PXOvklH=TkglN8cE5kP{&uN zWZq(x%}Xb;=FM=d!-nJ->iS0n2jM?p&aeBGO7>q1Ec8&~bT>FWgfo7SkiS)B7^uoU znNfH^$5tci!Yc}^(2e&-H492C1ZUy16ZgL< zN%;qiFDU4ZU32Mo)~ZUA424}w{qD)_dPR>~QwL>TpK!5}A~K-i)swqh90|_JvK$o8 zB6vF&U&0g!Bf4MgiWA-3nCCua+^BX~+~q>NkV?lY%q#xb&@RQk$gRcn;nF#|MX8tf zzPRl^wh?h;@4XeV4Q<^6=H+Q&2F9}&vP){r1MuB2TtX*G$QNNNLw0 z=Ijm(?fn!e;EeBOFce2hZxP-7VAMPuo%uNm0+NlXEs4^Hs^I=^Nrs#+7oTlpKKb4u z`74%yIS@fd^%>!D}^z0e@!7AtuwngeY zwhe58Np6Bjyh35HPbt^_sE=vq=;Px;qL}$qP7QUPKCs_!`d|}L(gF)Ju~^?n zXyVNIBd1g6=ql_Je!vV=kH=m4j_2lfR#jm)#GwfXRr^ruF27{xpepZ6nBsIHJLpXMNziCM+ z3tZt{PK9KTd8z89T;Dewf;DokrXyA7X3N;3_7=X545f1}?H#%&AadZ%U|4bqr|hn0 zsqX$dxl!N3{iN6ZBHzEf+!I(P)vPt&v**AgSL(5aeKs1K*Do=#epItfI8iKYt^-4x zs`=gg8lXTkGPb54r*`Po7p0Zvk?@PMAEYx*9$Xxf^xa+Uruzy}VJ^__ETw+x_=7*{ zSX>mFYV~7h81bKkKTF*i=1U$OD+qLE*=^}nxce1v)0^)W+C0;ZO@1`{yq>~?dz_SI zKG?U`$V}MXnizYW5WK4@%xcl3APJwCP!Qw$12)3^dQiDy@?7Y_u{K*p8nN!k2Pw(S zcgz-;0fU(nlNUdCpzqTwF;6Eh+-WmKj6D2wfQTMmupQVn(OH2JP&u5+X2CF4fD6;B z&E!9?_1tn<<6%7F%0StHQtGvmrh#lrBw4}2!T?|Hh49qj=I_}@3w3pCXtzaV_!AN`%kWl(MrKa^M@%_mW7Q-I; zp7mM*H~S3b=Nh37nz&#%9h~<4xJ5E^HX62{S_HRr!M!$IEzth-kga+|AUivY^BL4p zS1*rYA`2Uqfb{I88c{pGTVjw_-%|ek7d!s$nd}+TRhhFh_e%z?6(lKNgaNgQtWI39 zX-&OJHyn2DKcWB1rQ*g&9c#L)Huc@Mg|c2|i?Nj(eNV<_KVj@^m9XJUjUCQ{vFoiS zsnnf;ZO-3H-_)6{3&5A2N!a!l6pYzU>$S>Q3dzr?=6coE_}_i`Zt0ntUa!=}ph+T?o2ShZ)P0VWqzxT-&JE&1=R!Pdc&z zp7An!UTHJlkLe*vuXJAYOYl}=#F5we7cw?n0aA3S2MOODbn|4Nrgz)94TZB4%yQ~y zmm0xL>;|?VV9g}VNy9sjzpl@r|5Kq;m*44R&jrfDODcgOnY$jex19he>DnK`^F~A)BmbhI7k# zBG)LK8{cKe?d+RCoh%q6tQ)6>-aHNN{*gheJXa27htR5d zqYvH#PNvGFt#H9Lju9+h@HqK646ZypTMd-6a=XisCO?d~MjV${RL&Z%0*h+c+NW$OnIRs(|DMaxn^tU;jcOC&tt&{6R}Vbdx}iQQ z3iNAp01J7B$#4161?2Q^U)6^Xb?tY9>UIYad`>un_dtQq@lvyS;h_;6sE&8d%+M^f zyM29q=_A*B;K~6e1C~*STjf$fG*lwW@Sm`1BUA(B%BhV9o*n|yPN%El*v1PTu|5@I zH)BcssobS^|q|7OJ@znJ4UQ!e@CMXr#P8YH38$LLm-e)z3&yhR6F zwu6KU46pzYz|;({avP=~ub}){wtVUHbc^*P36U6S4Ro!5zU#-38wNq)X5kEeS&y`!{;^+S>5 zNv5yf6U_*|k0k`#Udw$I8Jn>g(JtDNl&&J~s0|~^f*CWct6ihI+#q$KWh4FP$vy_d zcs=P)%L!TE=+aD%NL4Vlj16R{%CU{orP|V&GF(YJ(U~pt36Q+7JV zu(gOiAyL^ew%Q1<*jBmJ7G*#)9}!t_OlNtQc7pi&-InvuvFAjF^;+MSnrOUXH`pa{ z{8`3$PH$J1Jt5-MyAvV?1F4J!XQf%{28%Ier{fRyb)0fvv61bvKX#-w+P8`g_f1eF z$#59iMw@p#*vu72vNFy%VypDvRJnPS>_wK8_h;FuP6UNZ?9`Czx}4X6{w*JuzROSp~ajUBsU4ESH|k8-HXVfa2zfIERy;UJWXZzOVFMRFE)R zI$QDXh0FTWsQn3S_jKgT7`8??-r(*>zCDu;AGE`y*Mtr!7+z2|Rnc z&hZ1b@b0U0;}6(5e4?<4>)A;a1*4jHwxnAcHWPGwsVg4b0PtZw-MMk+^}UG+!7~<~ zIm%YyFP!yW1V1;6+5Bu z6}&W@s!^e8I(dxmgc^SM$Yo23^5XFRjHU0l-3<9HN}fn)+$y`dXt2MB05pSTog~oSNHVE2+!gKr-WC*g8T!;^z9z%J=ad39r#G%wwj9$bQWISr0{lU}RxW~OjjNxW?+f*xUCV-%5z^(QGpbqT zP1zJSpT0h~wTBKR;+_@7U-9fpAdzjB`50|cQRDfbKztVWORQxHyx_pVo_Cbp?t~KlWmPPQkddZ)!!T6 zelPsmYRs5c>A={%_0b#^ACB?KtAHJfV&1|^qCC#>+%4Jch3fsM?L{z%Wx#7eOh;T#bX|+WqXFJ=Y#C&+g9- z-ET~2jbc!yXDv#W*t7C&xUs@BGdSq=_pSBQ#eV&jHB(Qj&ZFk$2ut=flJ0<@GSSPB zWR|kA72F;5ws<7!sCfd53}?*u*{Z|U?l`;4#Y0CH!;ix{uT3xDR(Z`ll;T9{3Mm%z z4V2@y^21zl?fBWr?C1^K+N+BMml4Cv)1@Y+5*;3K{29q&TfSkc(wenzwxS`KrkN?li-tHd0q-84$f(0wgBN)NpB{hr9M;~jzLgyq9^d(p#H$xcV?PnB%GtiL-P ztc2kE%D};h4I=2;7UhXdE3G|e8p*7PyC&VdT>SWuMf3G2|JOO+d1#It8Fjgr@pG9)takLfd`v+Vu`P4hV#qcJ$s21V@Oomh_)GUi zyq1%(ub6>h|CkXQE%gWC=|PH5xm`vfvF~=$Kla&;c9t>lG>onA{91trOF(Wgnm$S zu3amrUcaE03>~bmUQhHJsCxL-SoJO@00KUKoyewDk(fyAhVO{EcdQCsc7o9yi-IVw z4m(>-ht}dvi`>udJh?28%02MT{N#r!>ODcj}YZa-Yp`Ld<&yk?nEsLs=J2An6zfcuun|3(QnN2rC)05L}2Nv(I@ej`HutE zwbkdZ9kh-RJ}Hnzk*lQa&mbyj;+$F-G9{M#)>P+qoy={%{z&S4vQBd51Uz2MLL?;8 zzxYH%B?7p7ADl%9rZTKhLP)k$2o$bW3VyTExYOcdCFN*-Wby{weYfG%oZjx3`f4c< zOZvQEh?7kp))bZkK!Ng}e{DKZxh) z5Y2bqzRSv-&DtM(GJz3L@Yf|9V_vyC@@ShMzSd6Dz zj{fBA{JIs;EX&e}drRcc713`Evo6pEo;fNDfG-ivWZm^21n}K+Rf#ZrQl+iH_ywCLMpWiT8-+ zmBEG^$`MF$>1~b(+R;V~0~+;=UquBOU2+%`Mx}?A+J5fA%0_wc#8YZZx7`|^;C_~G z5&TudPrQ?va5c(Ium`3R!9p4$e#?3%{ha3z5y9KD)r7&jfP1x@-*34PftL+x+vRA8(QIuN~< zuVw5=tLbB#OayMUcB{9V+fe7KI};1qmbbIqNJV8-$Al=M*!4d@&5ycC^!a)1Y=X>j z&g5i`(^W?k7Ex}-WFA}6-joLMAt18-T3h4mUE}Q*c?aE8c==b-=Dp$o*^7+S6Za}j zeQU(~X0Z%qe|K}xqb+_H9_f%!vhcvOgO;FQ4i;PFV{3zm>N@fat~id4eOA#WxR{0! z@$zON-|enRhHw3d8ilvb0@)p{Y3Rdm?Jvp65BM2FAGncM#PeF(T-t)?%GPc~^G0+* z(p;Xd4kP}PzYP6WCR*}}{5@_qJE5~7xh)37xG3lB{NM=ARV{_;#p;_k(Ms7<&29(A z8N+5t{2L`ttlwRvpTPF(NrEAd>Em9lB42{f>D^(&5F{n0$*ky0&VB9m%qJ2juCGwb z1t_lTgx-*hinNll&lA&>(Z@@-+1rYF?`}3nksWhKvg^zhN-lJ{Za+X~Il3~R)|6A8 zhN0_|$eE^-BRnWiqXA@2t6ujik`AF)>_s<<8+Lw{zfc+7KX+cyS(3Z4)E9RgTmP~S z=#}|~>BK%hw-j3kxoM&WpS!tmkU6pq^K6gy2jx zkOb9IWJ1%?{m_&3iCQ2SDpIKj2*~Uo+)n=L$7@%$E0!}z_K(j`52H<<*5y^((Zxo4 z@<@&1N`{131QYOPgeXPEqHvj*`ClAxM<8w8D}jA|;*Kx)e9NLe-tI1WU#2(0aX%rb z>3neio0PO>Xr1amJfEZAsiT(A{`_yKlK!HO=GEQt_K@PgZ||rB;z|NZdnmhJd;h)Q z6aV|dXHBdrvV#rL9avur>P6NdH5ft`ndjZ`@x^KT_sD^}m*f9)54NR?365a(;$_iS z1u!}2fRRnZ^I?ko-&kj4naQAx&KBZ$G~F^m$J_At+9wk(O4VDik_M&Bf)}=YbG&?S65dGIg`MJ*aa9rla}CE zH3bTw%kPzWgpj(Z%+aWomK(9tjm_E2#*lO1Y;1Lno(tX8__Gg_utTKmV)wz}ifeAX z92uEP6F+|4aQeF2#85o0BrfeoUdu@jvziSg3PjI2qo?t`s&mtiNxo#&eb1iH&j8rp ze{8hn_)gf{%_Mg$j1vMaq1feaxS* z#_Z(FCkF2u8X29-a0B6^BEPuWV{g?Ng?&CjEGbyX1J#3NIyJX_7DrmyvnYrwQ3Eik|W~n>qI^uW9^k&YUX@v_~MwBy)hnLP*Hxn?ezlD<?{BbwSk5~DjsV#4pl@C{X`ZWWgWKII5FOu(&gx3 znAM8P(it0qo=UP=CxTj~oh~C^{(Cey@+*5o67O~M#lRC1H1gSxcu$uqT0gba!imX| z5QZCvwraU{HE@zpW_lNJ9(1CZ!r|A z^Q@C<=^Pl-M{0bWR=L{>T*746Zs$ikiDrKQv~H-MVL5hx7!G{np}=xyt`nI!?Dr1t zQIA|e(?7!Jgd6O~Cs&3`@P80KqT9gV0xo(#5E9*T!+z?XU=K;NBb!@^eA>ntSuk*x z9Gy^pQ0fwlkA^XA2sK=2nu|4aJepGwrFAbckL%&U2LJ-jM9D&0Gi@cb(2I4!{7d>N zcQ+J5cY|ehezS515Mdk4PDWw*1ousFnMapd7z`zE8rd zk3Ufu8wqY%E==kM52t7H>{H-?OL;=27YQ4KZ5Et21#=X+n^5j@ZRAB3*!Zy*oq0%S_OOz?TF<2}?0$D-Oy zIl>dd#4J*ks22YX-inR?jFkNv7M~~9(-!8tA03#=&bdBU=sQO7P;K7Q&p#43xCioU z;jx1cYPpG6Mu0o}Vn4?yN-`g1dVs|vSKnm? z1^^!31<%7%qc|f~T%e&YAd=3em|9diJ2O&qz4(=@&)h!<+mc0ZzhV>Of7nG$A}f8{ z0oE_hb5S}8S3+jHs`Yl)q+S=V{o?0;ay@C`v*47#z$p4~1jEi*t&hlzh>*lJ)|}`u zROHxB2sFs%d<7huLCtfK>9) zH4;DRF%x{UqhnE)I0R&X6|zldZVKBUTb?0@H?7<)RNZFyA&&moM@VtEJWVvY z?OrpR*R*>aD8JQ%$9COWWBk?`qm2YnQ)dVNWMjTr;NPO$=^${=6bTna? zm;u9<8;#>>R~zfKjxmFo(nwW|BN_m0*!Mzdl&oz}*HJKwv!J){%^Gn&fA4IuK(axk zWAQL&ch+;ZUpkqAGHzSvZE;eX@ZinH=cBij;Xfoo~#^6sgRXI-JI}6zEXR=?gPnQ9JV~A)B_^;UTe)e(TO$A$p0K1_(R7XaH&rj31*A( zYUu$7Bjt8`6+%^2s?Y)U*`X5Zl}ZyRFl}Xp$|tGu9N@>Gb;aHYgWV6#n~y3JGY45V z-#Y_k=*}Kh1a@WeyVUZEP(6rwTgMPvviB_HJZk(wGSv8qF}S(nD*DGx56Pu$^vZOx z(kNEySBdUlUYqAhC$h3N7QElKUy3v}xm$s+#chbACWzt&VHx(@yfrrQBzQGJot!}; zyqKJd0#UBH9EEFsVR?BrgAa$F`q`?@1OFT``Pm7&-~!FM7W*o4(RtSajyxk@Tx_aL z*V5aCBlps~e4EDJ)q?yAv?AiYSy!?qI9j1v`rQ5qivmbJG{H>+wFaxpA729qO2cT+ zGxIMw&(ycOGDsC0RO7qTZ*pwkdtpdsNBO{2c=M|WAlTMk1mAgGom)Y=CPD*qLJm?Q z!cdz4*6&~b2_&b$2xw0h*K~43s^=a_4ng~s1kx&wUHbGGZe$!RevtQRh`%r>FQe} zjYRidV&z_@3C*BS#bNyx=BtoC%u3y`MwidNA-KT{K}8k}>+=UmG>!+SVvE7gLrt`7 z1Cu-y;}L;PMOD5y#j&4XaUh$&$)p{Wyf|;Lu7ypzZm0r(8M!a~K7F)iCs}SaAUh*~ zh~kn!=$#GwO2>U2J%)FNrPcJ16Q%=y*5zlJsr8@bmB$b9?|3d!s|D{OG75J@l7HQ9 zDBe37gmSxPXyk9?pgx1S<2gwaqj|M~`%W^6#b5ZBsaFm+BST(^zjkcG ze@^l__E0=lxHjUVS<|$J-=~R%dP2Ek7QyZ`mG0vN@~nyJ>R=JZKVF58>c>~#W=0)g zRlDPz@x`t1*CVDygBc{=;Ou&HIy(9_DPUHp=c_1}#{1OgH=Y>^M;$Zv(QfZSUeD%W zE?1?I&C5R|-=iU>(79BmKFO&9Q%f>rTVMy8C-Ni{-AnN_=2WNrHS(p7ZC$1w- zBPZQ@<>lpeLm7NU5G@sEYo0S&@`yCm=x){@_≷(Qc4_NKC(Y>D(5kq!Mk%2B%uL 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 literal 0 HcmV?d00001 diff --git a/data/icons/network.raw b/data/icons/network.raw new file mode 100644 index 0000000000000000000000000000000000000000..f551a53891c9132a67839fe943068a67cb17fd1c GIT binary patch literal 293 zcmY+9K@xx<35Dwzva57Y_MdiU;4gz(+F2w|8~IK2u9 Jzy$}80RSIR?mhqj literal 0 HcmV?d00001 diff --git a/data/icons/numericpad.raw b/data/icons/numericpad.raw new file mode 100644 index 0000000000000000000000000000000000000000..5806fa4874feb306de83cfbef33b5cca47c82bcb GIT binary patch literal 12405 zcmeI2&yKS=48R>gNPD9#;>>g^&TVJZGyRHqj)L!;c*ARkjO4!bHwA%eV9OMiL$!|!iu40B5GB2%9!6w*HL{79?T$5XFw z6h7I%^q8)swX9Q3*hkG>N!T|LX<4V*H{ey|Exz4oQbo2e-pfJ$X#9M<*zy|J#gFhd zRSwl|531T*E6BcWmOzk0ILE6xnuum$2-7 zx?DKICr%5#C+mXShcDPVE&ga6;yAjs6x8CvjEj z5AoDZb@rX`v$MdFmnB&epV!GOulKh?uRIFps=R6i2B2-vA>eX1dPX9`|`Krfxe4@ zZCFTM^@7q;YW=Bv++*ISn`4g^`rqF#-21Q)beeB>YBWFYMBi>a$07uLtq|+MYURtX zV70Km?0BlA=~0-f`|_c6%_Qu7)GOEHi-o<9P!jm_wNuNtCbEA6&! z>EX$&n~y)aDUkL-p*_5)=3|NT(%a6ElH?ugJ&AI-C}G*CGkERU!K>Bd^;&^%wC}y~vBo`~D^S zit0Ayh3Ky{z&=X5Dv}QBa6g{6*qp-wKkz!}l6}#IwlWjmrgUWc?fOHu_BGYab7vurIrM z&wGNZE}0QxzVXao^0aS111s?Mhd4A5ytLW*`lH^~xyL^Lc|~aWPLcNq?myU*WPYzM z^q|T5w|i%kK=3v{`wt#9D)aG7tzAjPPm1LH5VDbnKWEG90p4MofO=WOdJVzWYX1{& z16z0r@*qc*e0@CjTFyR$lcvBQvon*Ns?v&d|Ap?EB!dhyT9SAA`=0OutK3T+&)v$tALcO~xt?;t2mIw~8Z@u6 z@^89dV6PeGtSx#@9?ad1tYLvS3{7_PP}KKLW6J{1tLA+WQoouv8hT&&lzsP@vE~27 zllz6yg%1^&zj$837Lld?U3dcHg>t{JptK-jy!0mW-36ru5#wdoprt#X_ZNBQOL-di zE#vhi_OVl^ui=g6!SlSR;r}r8Ja4jJAX7h5(sZ+W$Ny&-ujlO>QGMZuC*$F%X;1qX zj6X^K=?@tXPfd^QWmk&E>eKOSIuvEePxEU0SEZH~{HJ-Nr?&9Jn*?OOs^^<=zvUAw Qw|mvR4_fBIXY%pC0Wvchod5s; literal 0 HcmV?d00001 diff --git a/data/icons/ok.raw b/data/icons/ok.raw new file mode 100644 index 0000000000000000000000000000000000000000..bf98a4d23ef921f9cd737c1b2040fa6b80f63046 GIT binary patch literal 293 zcma)$F%G~m2m>?b33)D~w|tk48VE_N4y_}hBN*h-M*A&LY3`1Ic$XNHLN@)dkj-@R nK>${pYB3Ksy>YI+{+EB>0dJQ4eo1zmMClPz0h5j@?&Zrp~OHb8jzU=B^K_{d!M8TNZ{+|>*wox9;b`>to$DbZQXok zcChns?NSN(gMjye5&%xMOBQbKpa7&N6ME!6M9?49V+O#}#u-e{)z?LKGAEG9 z`(!e8B4{NfJntNFFt|XqLM#McmW3-o1|VNU?lN!{2=Enf5U7E~zJm@mU?`#YvGys-$ZHZN%$^+>`vNkQ7)fg*q^i2>Lv&>b-f4 z3Cy6!yZbyI4G(Q|fS&Ke_Uvo=H}%`@!tg6=imK?h-#fTmLx`lmcxW-1o>$LBAFk%iGY@IfOsRk zaQ5V6<>Qte;8DaaB>Fz=4Ilt*pafKfS?Hs4pSuxA6)6elL8##3&-WLi5esxdI->8b xlqzLUQ*Q5umRuR6=Z#(>k7ox1={JUJ@k4x<dYAB_HfW4WueAlyQQm8C03yugQ%7l0I?Or>KcjIa zvg(~CCs^QJc!TOz7HQ3c73LNAtUAr~>gX}X0YAf+>U|enK_F1|iTBk#l`w-&0Q}MN z^4KOYtb5ag;P?oCj5PE=P-&2Q4jKXANp@psGK#GN)NUs9M7>qEs~bs(?j<+?p2u#| z?m*xBEr9fXqfgD0vRRqEQqOXjQ~;VcOV|% zz-K35!s>8i3dq6A^9fevV|rf_fcM^XC$SSu-vz`3xH=)i2SR|I=;iqh00G}Mxub5+ zTfmcGc!-#poKXlO+#*aD!+Z3vs}xRSCY1fWP1ZJP=?X zKC}d`0$#@(1wDiG_ag};1A2d>A&1ieLj2I$LuWA00W!o-k#_|429Us;0+FyV_!(%w zy~ECrL`{OcLnIznERhz=5*Xz0x4 Z((!-@gLH8^89?4m7rvQvpmeU6{Rb(voW}qF literal 0 HcmV?d00001 diff --git a/data/icons/radar2.raw b/data/icons/radar2.raw new file mode 100644 index 0000000000000000000000000000000000000000..3e7f25a7676463a1a25c89e3ba80483b7f19097c GIT binary patch literal 4101 zcma)93y!NW3|u{FRnqdk(cb@5&*Ohg!fsHDLo@b_?NIpn`uX}dA6FXlRpVb0)XnkA z(N%{OW3={mtzgRZw!B`j_2+pU`~Gh4*7p54?ebOE_j6rF;QgIP+mFLs^oVXIO9=cS zFg|%Wiw=?|j?O69jX&+Z8;1l6Q{ad_iY`8PO+=vFQn;+E-^QT5HI)uCgW=~+vE`^9 zd_Hy%a9@}WDx)8&!2-(}_!M1okUgjkA(F-p@9=xsJo}YFa=`3Ed*q<@fQpbDgahCY zFWUfw9>@nbg5wQtaUUw6g>2jh>HzT5TlN+mnai=+-T~6h1ncNy4;T~|NdfTSWuO~_ z4%%o&AMOGcz~Q~`f=5?M(H&cf8W}h^pcB-`(E&50| z#M4sEzUqc{`p^hG)BohiX0r=%b7RmX0g!LY<2<7-p@jo_2-!pHBKnNMEwZ)~3t)W2 z?Jd&|KXxfpjnpR z7kUFi5snB~trrArBL)?ZsC{JdMFQACWnh0HAn?=ypN$+WHwJzK7Xh>A#9{j$F_7%X z4ZI4c`KuF97)bUNj|`5qi-T3-cU6e+TtT`8&xZ*}4CEmO(Pngb@0#w zP2lJPEn*KB0uK&U0IJ9?Walw=`CjaOE<6mjZ+D}ZgZapvvGDP>R{;8A2WAb>Kk@}?WY-NCV& z{^Uq1a6Z9_j7Y=#OC3}J1K*{G3~@j=q?$;2`O(-@pCl~({`mg*_uSs0J&&rt2~h9W zBdfa(nL5?j<=%vuE|=l+4I6*o*VX&$^=hs6>$cnPd3?UsVFd0wVR1<7*JUL#B3;Q4 z2JZyUpJpl&BwQG}5v4$l-9@N|02*ES4#L-uSlM4)gBU39DcqTd_oPBiILtZ>zxz(y zIMT=1xlmm|;R*JjKs^|-oPqxfJ0x_T%7hpA!d@z9nqnxx>TB;vA}*wm!6*RyHL;gK zM!(uCJ8*cJep!gufeHYevMcx(|5%!Qbbxd-p?A@&8@rvo;NqXE1P8!39~^ZQKvAG` zzy=1rDi9^9pyZbRx&XpIxqtBAD1*ickO0u%pbTx=1BH!|0LzEiW!o$Zhk3!xA2lHI z1G#rKftsD>5zMS#`)J?zhyMw^@8{O2IWYB{ztS(2+5vnGANiN-hYM_shXif(bIuKC z&wtkc`FE#l3nYXXQ+o$+fQfEfz)no%wh>!T~2>2xh-aov7W&1y`y~EQ zzzWG(AAeHd?&cfXwGa6T9y4f?{!ao<@NdIF;ve+#3s6kqG4sQ_9`X_cc4|mqT|?XP zN%YCUP!Q;~z_bSTeJkUGf~HHC$z8y_3srvQUv{3u)j^Jc(qH|9n|?ZN>5K{-K`OxM zi+^82fYZWX;T++c-hI7&F+0zH37`KFfPnWsGC=ygd}jZ`KEow~P(ahi^d)|tID2y% z{;9_b_$3x5kRJGR0eN*T;a=dvUa<5t{ICCkbY48}Ukn)cYJJ!c5A;Bq<@bK`tkHlm wy6Rbl_kADtMgIGFUZ(q3w8!XWrcOn<+?z0dxeR=~;gHaI9Q^P_fc}u#fAxW!#{d8T literal 0 HcmV?d00001 diff --git a/data/icons/radar4.raw b/data/icons/radar4.raw new file mode 100644 index 0000000000000000000000000000000000000000..afb68e74b5359a49082a29df9547542e2e5289a0 GIT binary patch literal 4101 zcma)<3sS={3`7|irg45wIT80i6bvOz`-hIOuXT?X#ms5)!{)LgaGWm%iYS5!bngp zVhaGi3SbTrmek>Z8^QU2o2ngC7*(fhEBAp00Is!b>Y)OnuGNP+jT4aGOqi*Alhxx) zovw!rz?VqgHcd3sPu>GgU@lKFI|m^ZH$eQTa~gW9J{#c*kO9bF(S4W?)h!J*@WQ_1 zhNNTThxWyWhd?@c6ezR*H}*5;X9YdP>5n&G>!1!CYZFf3Mf%pRji6a+XU^N5_aI4p z!*KOJdkP~xOa_b(oKIWXOc_U>JMW7}lNO*kmlfh zr@eBgKh1yHmqO~3&pZF?L3j~3ZJgTi`i=W%IranmM13vJ!+*@3>v8P+Lb#j1*TDN{ z(Eo*SQJ*`I8<6UM=a;?V?x1aj%=rG{8>riW_4p?IfPEVwh^55o%kzf{X!%p_OOGem zcOvuFL)&)jH}zo+>`zGSKziU(;r?O1$RANWz`=V5EcNH7BKMTNRAHrqe36h;UjxV! z@Ubo5_3Nv0>0T9&Y9LQ9;LQLRt^yyZhgQN4GU_s$H_k=ggVIR`J5U9XKg;Tt@ zZ#^KpATf-_b&p-fC8v`7a-PPj7t|Gz(n14fh%wSIfV*pfagU| zow>Tb0VL2)IE5#@eMGyM+l}+@H;`KdL^18Yawd165jvV%04F8cffkByx2 literal 0 HcmV?d00001 diff --git a/data/icons/radar5.raw b/data/icons/radar5.raw new file mode 100644 index 0000000000000000000000000000000000000000..38753247ced83eeae52a3a0eaca72c86160a96f1 GIT binary patch literal 4101 zcma)<3v%lq3`IMgdGi8}0cS(doBJ5{9Wk_~5E37kW;n z9%~0%+{M-^a1I)nd*mtl{}40`Q~{SU@P-FK2U!DG(K#UXZ*oURelfWQ(*g2bqs?9g z=lD=WB#c-554nr0N;MP*h`(Dose4e-5E2DwE!59E$O`yTT?WY8o==pNEx{Ge1bo`3;}jbXr-4oHR7Z?FS`34b7NZSA7Y_3IWD<6=YM+;ZJ8GB)UK{|pm-!zlqy z0uu?z@CgV-9481C)W>!(#Y{hZ1;a%E+2(+Df~uz;qOKIk1qk_WSzF#n3K&FB4UQko zyRuCI=EUcZw%$TeK;sRJR68d7PJvy(ksq@EOL@Isjwf@eKrerM1s+YI=d!#To4=)-$YZr~{c;vJ+5 z=k`IwIfGe(AV4F!Ud}v?t~Wtf0*)T?*IMM{*$b6ms~|XvwG$D@U;U=Qiw2t-mwrAy zO##H2?|ZA&8c3^XE5^ zs6kw5>|8kwk3CC(m4HG91+5LbUcdxD|8A>?i(lX%eRzG-H>Z!ZlVFa5aSymS5(qa~ UOx+xEkeQpQ+rtKev7Pk&3o$&L*IM_Mqof)EBCDw5fRR0$pjXj zq%H!)ohUjv-7qZb!GZcv;G^-sk)ai$qiodRVfl2Dj~2pUI$(Gmsy5+iVomT0o(WEu zQ!=Qq^uWJSha)Ltz`uoO(pLwLfZe~y4s|rCK_>wI+~u=hj*=e^cq15|;aexrYp(r* zl55xq0I&7j&TFwyrl5=_`RP~yYBv)`_2l-rbp`H@E&=e2-p=i@2&UXT@Gjs048N}P z0>^fd1{bJp9YaTzq0IiP!o>xbQ41ybvHEDh2ZS0w#MHn4q*?+hJB3^QHcFPZN(8d|x zKYRnN12pwO;#_cidH%Qp4(eupuE81~O7N7QUFcT`c7X;U^>Pr!lXd699^kQikYc}@Q$E4 z&`asaLjDDu6MXJ|OuVz3Wi4m`&*J>DN3y9_z67dI*&9*c`PQMMQ)n8PXn) z^RaiRbq;e8AnriXt)A!OybR{{#XLCB=nA~n>%7jB@jryMFKB+AMS~cq-c$IsU4Ul= zuoEq!!>q&bc5RyxWCegBCx~!z@Hkm6*=P+WEPLQv+4havMeV)-S`Qp!zz4V{a902k zaEgwA-Itx3InWtM?BkSVFbIIJ4F0G9A^@V}6%KuIAPHP??t>R!0pN|@D;yD0fcF7V z1OTZ6^vHd0G6$5vdJPN@t^(jqJ~()502OcofO~mR@G1z`z);p+H$d=Sz15%zqy-#A zKwnOP3_!jauJ485T>&CKy4WB393`-5xBAC^^lD9Y?7Mz( z>ICthLkakjA*k%dP0#OU{XiXUD8^pLLqGk@@aUqipRoQXqM5?EkqeOWYq|P6`*0$6 zuyq1t3fTv%WAo&1sGrN1`%hg=tG3_+%rZDUo(ixfv``6$I&iaLe<4yphm|MNa4o1? zeEKJf8#wU10@y<-zf2&JIvSq=`g;2lf>unQp7sI=2blx8dqH0f7y;pJIuoMqV#v!s zz_bP@=rVob5dsO&8tl4Odx+7Lr5q=36%hRB-=M$NAK_9#Ttq?@XygE;$d$+8$%ZRH za5u2%AQV3l_!REcZRJe{UR9=kifz~y7wPjONAPMRgPSO+c2*w`UuFUk2*HhQQRn3C z$;q(1X9E0}_ZZhg>_hqBKupE=_PxyK-Y7P-s!4rl3QB3 sV={)9yui^tfnMLtH~r{_=G{(^c5=*gHbs7--FY&G{^N%o&v6g=2l0TN00000 literal 0 HcmV?d00001 diff --git a/data/icons/radar8.raw b/data/icons/radar8.raw new file mode 100644 index 0000000000000000000000000000000000000000..5331ad5910822edc09ccc8bbf7f76149f0deb585 GIT binary patch literal 4101 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

    Xgb}1r)Qz* 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�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 bwij`J!r}_r?O4z{hri0--{Sj;Z!i7{Rcs#| literal 0 HcmV?d00001 diff --git a/data/icons/radiomode.pal b/data/icons/radiomode.pal new file mode 100644 index 0000000000000000000000000000000000000000..22605624a843e714fdcbb823814555eec649b792 GIT binary patch literal 768 zcmeHDO=}ZT6g^Sl#tK(cxFrdBq^Z+1Eh7nrgyMj-FB*A5;bBmCX@n_JMzjp#A`8Jq z!GSEKAk=_lAx2k0Hxd7z&)?wI%UPVy%>@dV@)b}ceb1Ah=tNn2FfC^RO*v5T=EkK_K3j=iDh3CN8(r< zBqoVjVn{;DvLC7uRDvMRZ;rqzcmhcwlQnq-mFCqcf;1PNLf&0gZy^~}hCNR{pf*Kw zit1N%zSYasO0m_fwW^h<8AQF!^1*KXVY3odJH7CB+$fxMHzuvZo8I-o&hGQQ?Wf@_ zw;!f=>mpi8{c`bvQd%ifl)?JVfou)ecU}j7o#RGLZ>vZ9wR=JR=ze&()p}uvXJ-b} zBYZTtu=vu$+~UGv?lH!)kDnHc5ta_W9To|eW2|DV60A~W2Q<4wpA1bM&7RPALqCRB o=3`on=~t4=4tdt$&zjF4^Wx-M)|K~0&do;l@>%Zx{lf?T00i`00RR91 literal 0 HcmV?d00001 diff --git a/data/icons/radiomode.raw b/data/icons/radiomode.raw new file mode 100644 index 000000000..804824dd6 --- /dev/null +++ b/data/icons/radiomode.raw @@ -0,0 +1 @@ +Ð@ÿ/./...................... . . . . . .. . ...............././/////////////=//=/==/==/=/=/=/=/=/=////..  .../...  .//==F=@F=========@@@@=@==@=========F=====F@F=======/////=/===F=@@G@G@GCGCF@=F======F=@@@G@@G@G@GCGCGCGCGCGCGCGCG@@G@GCGG@GGG@GG@GGGGG@GF@F@F@FF@FFGFFFF=F@F@F@F@FF@FF@FFFFFFFFFFFFFFFF@FF@FF@FFFFFFFFFFFFFFFFFFFFFFFF=F=F=====/====/=//////////(/.*.(/(.(.((.((.(.*/(/*(/**.*(((((((((((((((((((((((((((((((((%(((((%((((%(((%((%((%((%((((%(((((*(*****(**(**(*(( (/====@@F@@@@@@G@GCGCOGLGOLOOOOOLOOOOOOMOTOTTTTTTTOTOOOOTOTTTTTTTVTTVTVVTYVVTTTTTTTTVVYYYYYYYYYYYYYYYYYY[YYYYYYYYYYYYYYYYVYYVYVVVTVTVVVVVVVVVVTVTSTVTVTVTVTVTVSVVVVVVVVVVVUVVSVSVSVSVSSSSS/./............./......... .. . . . . ... ...................//2/////=/=/=/====F==@F==@=F=@F@=@F@F=F@===/.. .././....   . ..2=FCGCGCGCGCGC@GCGCGCGCGCGCG@F@F@GCG@G@G@G@CF@G@F======F=F@F@@FGGCGOGOGCG@F@@F@F@F@@GGGGCOGCOCOLOLOOLGLOOOOOOCOGGGCGOGOCOGOGOOOOOOOOOGOGG@GGGGGGGOGGGG@GFG@GGGGGG@GGGGGGGFGFGFGFGFGFGG@GGFGFG@GGGGGGGGGGGGFGGFGGFGF@FGF@F@F@F=F=F=F==F=====/=///=//=///=/8/=/==*=====/=/8/=*/*=*/**/*****8**********8***8*888888*8*8**********************&***(*****8**8*8*8*8*8****((( .===@F@CGCGGOCOOOOOOOOOMOOTOMOTOTOMOTOTOTTTTTTTTTTOOOTOTTTTTTVTYVVVTVTVTTTTTOTTTTTWVYYYYYYYY[YYY[YY[Y[YY[Y[Y[Y[YYYYYYYYYYYYYYYYYVVVTVVVVVVVVVVVVVTVTTVTVVVTVVTVVTVSVVVVVVVSVVSVSVSVSSVSTS//.........../....... . .. . ... . . .. .. .. .........../../////2/=/==2=/=2=F=F@F@@@@F@@@F@@F@@@@@F@F@F==/. ../...  ....../FCGCGLGOCOCOCOCGLGLGLGCGLG@G@GCGCGCGCG@GCGCG@@@F@F=F=F=@F@F@FCGCOGOLGOCG@G@G@G@G@GGCOOLOLOOOMOOOOOOMOOTOTTOOOLOOLOOOOOOOOMOTOOTOOOOOOOCOGGGGGGOGOGGGGGGCGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG@G@@F@F@=F@F@@@@==========/======@=@=========8==8==*=8=*=*8*8/8=*=8=8/88=88=88=88=8=888=8888888888*8*8*8*8*8&8********8*888888888888=8=88=****(((//==@@G@@GCGLOOMOTOMTMTOTTTTTTTTTTTTTTTTTTTTTTTTTOOOTOTTTTTTVTYVYYYYYYYYTTTTTQTTTWVVYYYYYY[YY[Z[YYYZ[Y[Y[Z[Z[[Z[Y[Y[YYYYY[Y[YY[YYYYYYVVVVVVTVVVTVVTVVTVTVTVVSVVVVVVVSVSVSVVSVTUTSVSVSSVSS//..........././..... .. .. . .. . . . ... ... ............./.2//////2/////=/====F@F@F@=F@=F@@F@F@F@=F=F==...   .  ......//FCGCGCOCOCCGCGCCCGC@CGCG@@@@G@GC@@G@GC@@F@@F@F@==/=F==F=F@F@F@@G@G@@F@F@FCFG@F@F@GGCGGCGLGOOOLOOLOOOOOMOOOLOGOGGGOGOOOOOOOOMOOOOOGGGCGG@GGCGGGGGGGGG@G@G@GGGGGGGGGGGGGFGFGFGGGGGGGGGGFG@GFGGGGGGGGGGGGGGGFGFFG@GF@FCF@F@F@=F=F=====F========/=/======@====8===/8/*//8/=*/8/*/8**/8*/**8*/*8*8/*8*8**8*8*888=8*8*****************&*****&****&********8*8*8*8*8****((((( .===F@F@@@G@GCGGLOOOOOTOTOTOTOMOOTMOTTMTTTTTTTTOOOMOTTTOTTTTTTVTYVVYTTTTTTTTTTWTTYYYYYYYYY[ZY[YZ[Y[YZ[Z[Y[Y[Y[Y[Y[Y[Y[Y[YYY[YY[Y[Y[YXYYVVVVVVVVVTVVTVTVTSVTVVVVVVVVVSVVTVSVTVVUSVSTSTSST/./......././/........ .. . .. .. .. .. . .. ...../..../../.2/.//.///////.////2=======/=/==/=/===/=/==////..  .... ./=F@CFC@@FC@F@@F=@@F@@===F=F@F@F=F=F=F=F=F=F==////2//=/=/=/=2=F=F=F=F=F@F@@F=F=F@@G@GCG@GCGCGCGGCGCGGGGCGGGGGGGGGGGGGGOGOGOGOGGG@G@FF@F@F@F@F@FFF@FF=F@FF@F@F@F@F@FFFF@FFFFFFFFFFFFFFFFF@FF@FFFFFFF@FFF@FFFFFFF@F@F@F@=F=2===//=2===/=//////////=*/=//*//*.((.*.(.(/*(/*/(*/*(/(*.((*(.(*((.(((((((((((*(*(*(*((((*(((%((((%((((%((%(((%((((%(((((*(*(*(*((((((( //===@F@F@@@G@G@GCGOOOOMOOTOOOMOOTOTTTTTTTTTTOOOOTOTOTOTOTOTTTTTTTTTTTTTTTTTTTTVYYYYYYY[ZY[YZ[YY[ZY[Y[Y[Z[Y[Z[Y[Y[Y[Y[Y[YYYYYYYY[YY[Y[YXYYYVVVVVVTVVTVVTVTVSVVVVVVVVVVVVSVVSVSVSVSSVSTU/../.././././1/./..... ... . .. .. .. .. .. .............././///1////./..//////======2==/=/=/==2=/2///=///..  ... ../=F@@GF@@F@@@===F=@=F====@F@F===F=F====F====2/////=/=2=/=/F/=2==2=F=F@@F@F@F=F@FG@G@G@G@G@GGCG@GGGCGGGGCGGGGGGGGGGGGGOOGOGGGGCFGF@F@G@F@F@F@F@FFF@F@FF@FF@FF@FFFF@FF@FFFFFFFFFF@FFF@FFFFFFFFFFFF@FFF@FF@F@F@=F=F=F=@F==F=2=F=2==/=/==/=/=/====/=/=/*//(/.*./(.(/*/*//*.**(*.**.(*((.*(((((((*((*((*((*(*(**(*(*((((*(((%((*(*%*((%((%(((%((((%((((%(((((((*( ./=@F@F@@G@G@G@GGCGGOOGOOOOOMOOTOMTOTTTTTTTTTOOOMOTTOTOTOTOTOTTTTTTTTTTTTVTTVTVYYYYY[YYYYYY[YY[ZY[YY[Y[Z[Y[Z[Y[Y[YY[YY[Y[Y[Y[Y[YYYYYXY[Y[YXYYYVVVVVVVTVVSVTVTVSVSVVVVXVVVVVVVVSVSTSSVSS/......../.2//.2....... .. .. .. .. . . .. ........../../.2//2////////././////=2======/=/=//=2==////./////..  ....../F@G@G@@G@F@F=F@=@F=@=F@F@@F=@F=@F===F==2===///////2/=/=F==2==2==F=@@F@@F@==F@F@G@FCF@F@FCGCGGCGGGGCGCGGGGGGGGGGGGGOOGOGGGCG@GF@FG@F@F@F@F@F@F@F@FF@F@F@F@F@F@FGFFFFGFFFFFFF@FFFF@FFFF@FFFFFFGFFFFFFF@FF@FFF=F==F=F=F====2==2==/==2=========/=//*/.(/(/.(//*//*/*//(*/**.*(*.*((((((((*.*(**(*(*(*((*(*((*(((((*%((((%(((((%(((((%(((%((((%((((*(*(((*((*((*(..=F@@@G@G@GCG@GCGLGOOOOOOGOOGLGOOOMOOTTTTTTOOOOMOOOOTOOTOTTOTOTOTTTTTTVTTTTTVWVYYYYYYYYYYYZ[ZY[YY[ZY[Z[Y[Z[YY[ZY[YY[Z[Y[Y[Y[YYYYXYYYYYYY[YYXYXYYXYVVVVVTVVSVTVTVVVSVVVVXYXVVVVSVUVSSVS././././.2/./2......... .... . .. . . .. . .. ... ......././//2//////./////2/F==F=F=F@@F@F@F@@F@==.//./////..     . ..../././@GLOOLOGLCGCGCGCGC@G@CGCGCG@@G@G@G@G@F@@F@==2==2=@==@F@@G@@F@F=@F@G@G@G@@FG@FGCG@GG@G@GCGGOOOOOOOOGOOOOOOOOOOOOOOOOOOOOOGGOGGOGCGGCGGCGG@GG@GGGGGGGGGGGGGGGGGGGGGGGGGGGGFGGFG@GFGG@GGGGGGGGGGGGGGGGG@GGFG@F@@F@@F@F@F@F=F=F@F=FF@@@G@@C@@@======/==/==============8=/=*=*/***/*/**8=8*88=*88*8=*888/***8*88*88**8*8************(*&*(**(&******8*****8*8*8***( //=@FC@GCGCGCGGGOGOOOOMOOMOOOOGOLOOOMOOTMOOOOOOOMOOTOTTTTTTTTTTTTTTTTVTVTTVTVVYYYYYYYYYYYY[YY[YYZY[Y[Y[Y[YY[ZY[Y[Y[Z[Y[Y[Y[Y[[[[YYYXYYYXVYYYYYXYYYVVYVVVVSVVVSVVTVSVTVVVVVXVVXVVUVSVSV././././/./2./.2..1......... .. . . .. . .. ... .....././/2//2=/=2=//////=/===F@@@F@@F@@F@@@G@@F=//....////..    ./////////2//F=GOMOOMOOLOCGCGCCGCGLGLGLGCGGCGCGCG@G@G@F@F===F=F@GCGOGOCGCG@G@GGCGGCGGG@G@GGCGGGGCG@GGCOOMTTOTOOMOOOOMOTOTOTOTTQTTTOOOOOGOOOOOOCOGGOGGOGCGGGGOOOGGGGGCGGCGGGGGOGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGCGG@GG@GF@G@G@@F@F@G@@@@@@GGCGC@G@@@=@==@@@=@@@=@=@=@========8==8=*8=8/8=8=8=88=888=8=8888=8888=888888888888888**88*********8*&8**8*8888888888888888=8* .(..(.  ../=@@GCGOCOGLOOLOMOTMTOTOTOOOMOOOOMOTOTTOTOOOCOGOOOTTTVVVVTVTTTTTTTTVTTTTTTTTTYVYYYYVTVTYYYY[YYY[YYYYY[Y[Z[Y[Y[YYY[Y[Y[Y[Y[Y[YYY[Y[YY[YYYVYVYXYYXYXYYXYYVVVVVTVTVSVTVSVSVSVVVVVUVVUVSVS/./.2./.2/./2././1........ .. .. . . . . ... .. ......././/2///2/=//.///2//====F=====@F@@=F@=G@@F/... . .. .  .//2=F=F=2////==@GOMOOOLOCOCCCF@G@CGCGC@@G@G@@@F@@G@@@F@==F===@F@GCGLGCGCG@G@F@G@GCG@G@F@F@@GF@G@G@G@G@GOOOOOOOOOOOOOOOOOOTOQOQOOOQOOOGGCGCGCGCGCGCGCGCGGCGGGGGGGGGCGGGGGGGGGGGGGGGGGGG@G@F@GFG@GFGGGGGGGHGGGGGGGGG@GFG@GF@F@@F@F@F@F@F@=F@F@G@@@F@@F@=====@@@@@@=@=@==========8===8=8/8/8/8=88=888=8=8*8=*8=88/8*=88*88/888=8888*******%****&***(*((&****************8*8**( .*//=//...(.//=FCGCGCGCOCOOOOOOOOMOOTOMOOOOOOOOOMOOTOOOGGGLGOOTTTVTVVVVVTTTTTVTTVTTTTTOTTTTVTTVTVWVTYY[Y[Y[Z[Y[ZYYZYYY[Y[Y[[Y[Y[Y[Y[Y[Y[[Y[Y[Y[YY[YXYYXYVYXYYYYYYXYXYXYVXVVVVSVTVSVVVSVVSVVSVUSVSS//.//.2/.2/./1.2./............ .. . . ... .. ... ....../2.////2=///././////2=2=/=//=/==/=======2/.   ..//F==F=@=2///F=@G@GCGC@G@F@@F==@F@@F@F==F=F=F==F==F==F//=2==F=@F@@G@G@@F@F=@F@F@F@F@F@F=FF@F@F@F@=F=F@GCGCGCGGGGGGGGOOOOGOOOOOGGGGGF@G@F@F@F@F@F@F@F@F@G@GF@F@F@F@F@F@F@F@F@FFFGF@F@FF=FF=F=FF@FFFFFFFFFF@FF=F@F=FF@F@F=F=F==F==F====F=F=F==F=F====/======/===/=/=*/=//*/*/*//*/*/*/**.**.*(*.*(*(*(*.*(*(*(**((*(**(*((*(*((*((*((%(((((%((%((%((((%((*((*(*(*(**(****( (.//=/===/////==@@G@GCGCGGCGOOOOOOOMOOOOOOOLOOOMOOOTOMOOOOOOMTTTTVTVTVTVTTTTTTTVTTVTVTTTTTTTTTTTTTVTVYYY[Y[Z[Y[Z[YYYYYYYYYYY[Z[Y[Y[Y[Y[[Y[Y[YY[Y[YY[Y[YY[YXYYXYXYYYXYYYXYYXYVVVVSVVTVSVVSVTVSVSVSVS//1//2.//.2.././.1/.1..1.... .. . .. .. ... .. ........///./2=//2/././////=/=/=/2=/=/=/==2=======/  ../2==F=FF@F=F==F@G@GCG@@@@@F===F@@F@==F=F====/====F=F======F==F=@F@@G@G@F@@F@F@F@GF@F@F@F@F@F@F@@F@=F@GGCGOGGGG@GGGGGOGOOQOGOGOGGGGFGG@F@F@G@F@@FG@F@GG@GF@F@GF@GF@F@F@FF@FFF@FF@FF@FF=FFF=F@FFF@FF@FFFFFFFFF@FF@F@FF@F@F=F=F==F==F=F=F==F=F====================/=/=//*///*//*/*/*/*/***.***.**.**((/*(**(**(*(*(**(**(**(***((*((%(((((%((((((((((((%*(*(*((*(*(*****(* (///====@=@==@=@FC@GCG@GCGOCOOOOOMOOOMOOTOMOOTOOTOMOTOOOOMOOTTTTVTVTVVTTTTTTTTTTVTVVTVTTTTTTTTTTTTOTTYYYY[Z[Y[Z[Y[Y[YYYYYYYYYYY[Y[Y[[Y[Y[Y[Y[Y[YY[YXYY[XY[Y[YY[YYX[Y[Y[YXYYXYXYVXVVYUVVSVSVSTSVSVSV////2//1/./1./1.//1/./....... .. . .. .. .. .. .. ...././2////2=/././///=2/=/=/=/=//=/=2===F=F=F=/. ..  .//=F=F@@G@G@@FG@G@GCGCGGG@F@=@F@@F@F@F@F==F=2==2======F=F===F==F@@FG@G@F@F=F@=F@FG@G@F@F@FG@G@GGF@F=@FCGCGGCGGGGGGGGOOOGOGOOGGGGGGGGF@GF@G=F@@F@@F@@GF@GF@GF@F@FGF@FF@F@F@FGF@GFF@FF@F@FF=F=FF@FF@FF@FFF=F=F=F=F@FF@F@F@=F===F==F==F=F=F==F==F@F=F=============/==//=//*///*///**/*/*/**(/**(/(**(/*(*(*(*(**(*(**(**(**(**(*((((*((%(((((%(((((%((((*((*(*(*(((*(******(( (///===@=@@@@@G@GCGCGCOCGCGOGOOOOOMOOOOTOOOMOOTOMOTOTOTOTOTTOTTTTTVTVTVTTTOTOTOTTVTVTVVVVVTYVYVVTTTTTYYYYYYY[Y[Y[Z[Y[Y[Y[YYYYYYYYYYYY[Y[Y[Y[Y[Y[Y[YY[YXYYXYY[X[Y[Y[YX[Y[Y[YXYYXYXYXYXYVVVVSVSVSTSSS/2/2///.2/./../.1/./1/.2..... .. .. .... .. . . ......2/.//2///2/././//2=====F=====F====@F@@G@@@G@/.. ../....   /2=@FCGGOOOGOOOOOOOLOOOLOLGCGGCOCGCG@GCGC@@F@@F@@F@FCF@G@@F@@@G@G@GGCGOGCG@@G@GGGGGGGOGGOGOGGOOOCGC@G@OOOOOOOOOOOOOOOTOTOOTOOOOOOGOGGOGGCGG@G@GGGGCGGGOGCGGGGGGGGGGGGGGGGGGGGGGGGGGGCG@GGFGF@FG@GFGGFGGGFGFGFG@GFGGG@GG@F@F@F@F@F@F@F@@F@FC@GCFC@G@G@@C@@@@@@@@@@@@@@@@@==========8===8==8*=8=*8/88*8=8=88=88=888888888*88*888888*******(*&*(*(*&*(*************8**8**88*8*8*(..*===@=@@@F@@@G@CGCCOOLOOOOOMOMOMOOTOTMOOMOTOTOMOTOTMOTTOTOTTTTTTVTVTVTTTTOTTTTTTTVTYTYYVYYYYYYYYVTYYYYYYYYYYYYZY[Y[Z[Z[Y[[Y[YYYYYYYYYYYXY[Y[Y[Y[Y[XY[Y[YYXY[YY[X[Y[Y[Y[Y[X[[Y[YYXYXYXYXYVVUVSVSSTS/2//2/2/.2..1.../.2./2........ .. .. .... .. .... ....//./2//2=//.//=/====F@F@==F====F@F@@FCGGGC@G@=....//=./....   /=FGCGOOMOOOOOMOOMOTOTTOOOLOOLOOOCGLGCOCGG@@FC@G@@G@GCG@GG@G@GG@GCGCOOLGGCGG@GGOOOMOOOOMOOOOTOOLOOGCGOMOTOMOOMOTOTOTTTTQTTTTTOOOOOOOOOOGGCGGLGGCGOGOOOGGOGGGOGLGOGGOGGGGGGGGGGGGGGGGGGGCGGG@GCGGGGGGGGGGGGGGGG@GGGGGGCGGGG@G@G@G@G@GGGGCGGGGCGLGGC@CG@GCGCCGCCGCC@C@@@@@@@@@=@@@===8===8==8=88=8=8=8=888=888=888@88=8888888888888888*8*****8**8**8**888888888888*888888=8=88/8(./==@@@@@@G@GCGCGCGCOLGOLOOTMOTTTTTTVTTTTTTTTTTTTTTTTTTTOTTTTTOTTTTTVTTTTOTOTTTTVVVYVVYVVYYYYYYYYVTVYY[Y[[Y[YYY[YYY[Z[Y[Y[Z[[Y[[Y[YYVVVYVYYVXY[Y[Y[Y[Y[XY[Y[YXYXYYY[X[Y[Y[X[YY[X[[Y[YXYYXVYVXVVVUVUS//2//2///./../.1/./2.//1/.1.... .. .. ... ... .... ../.2/.//2//././////=2=========F=====F@@F@@@FG@F=/ ..///./..  .2=@GCOOOOOOOOOOOOOOOLOOLOCOLOOCGCGCGCGC@F@@@F@F@F@@@G@@@@@G@@@GCG@GCGG@G@F@GCGGOOOOOOOOOOOOOOOOC@G@GOOOLOOOOOOOMOOOOQOTOQOQOOOOOOOGOGGCGGCGCGGCGCGCG@GCG@GCGGGGGGGGGGGGGGGGGGGGG@GGG@G@G@F@F@FGF@GFG@GFGFG@GFG@GFG@G@G@F@F@F@F@F@G@GCGCGCGCGCG@@G@@G@@@@@@@C@@@@@@@@@@@@=======8==8==8=*=88/8=*888=88=888=8888888888=8*=*8*8/88*8********&(***&*(**********(***(*(*****8***((( (/==@@@@G@@@C@@CGCGCGCGCOOGOOTOTOTTTTTTTTTTTTOTMOTMOTOTTTTTTTTTTVTVTTTTOTOTTTTVVYVYYYYYYVYYYYYYVTVVY[Y[Y[Y[Y[YY[Y[Y[Y[Y[[[Y[[Y[[Y[Y[YVYVVVVVVVYXYXY[Y[Y[Y[YXY[YXYXYYVYXY[YY[X[Y[Y[X[Y[VXYXVVVUYUVXY2/2/2//2.2...1/..2.//1/.......... .... ... .... ..../././/.//./..///.//=====/===2=/////==2===F==2===/  .. .   ./F=G@GCGGGGGGCGG@GGCGC@G@G@GC@@F@F@F=F==F=F==F===F===F=F=F=F==F@F@F=F=F=@F@GCGCGOGOGOGOGOGCGCF@=F@GCGCGCGCGOCOGOGOOGOGOOGGOGGGGGGGG@G@F@F@F@G@F@F@F@F@F@F@F@F@F@F@G@F@F@F@F=F@F@F@FF=F=F@F=F@=FF=FFFFFF@FFF=F=F=F=F@=F=F=F=F=F@F@F@F@F@F@F@==F===F===@=@=====@=====/=//8//*/*/*/*/(***.*(/*(/*.*(*(*(((*(*(*(*(**(*((*((*(((((((((((((((%(((((%(((((%(((%(((((((((((((//=@=@@@@G@G@G@@CCG@GCGGLGOOOOOMOTTTTTTTTTTTVTTTTTTTTOTTTTTTTTTTVTTTTOTOTTTTYVYVYYYYYYYYYYYVVTTVVY[Y[Y[Y[Z[Y[[Z[Y[Z[Y[Y[Z[[[[Y[[Y[YY[YYXYVVVVVVVYXYXY[Y[X[Y[YXYYYXYXYYXYX[Y[Y[X[[Y[X[YXYXVVXVVXVVX/=2=/2//.././../../2.//1./.......... .. ... ......../././/.//../././////=2===2===//=/=/=/==2==2===2=/.      ../F@G@GGGGGGG@G@G@G@@FC@G@G@@F@F@@F=====F======F==2=F===F==F=F=@=F===F=F=G@GGGGOGOGGOCOGGCGG@F=F@GCGG@GGCGGOOGOGOGOGOGGGOGGGGGGGGG@G@GFCFG@G@F@@@F@F@F@F@F@F@F@FGF@GFGF@FF@FF@FF@FF@F=F=F=F=F=F@FF=FF@FFFF=F=F=F=F@F=F@F=F@F@@F@F@F@F@F===F@F@F@F==F==F==@======/==/==//*=/*/*.*/*/*/*(*/(**(/**(*.*(**(*(*(***(**(*((((%(((%((((((((((((((%(((((%((((((((((%((((( .===@@F@G@GCGCGLGCOLGOLOGLOOOOOOOMOTOTTTTVTTTVTVTVTTTTTTTTTTTTVTTTTTTOTTTTVYVYYYYYYYYYYYYYVTTTTTYYYY[Y[Y[Y[Y[Z[[Z[[Y[[Z[[Y[Y[[Y[[Y[[Y[Y[YYXYVVVVVVYYXYXYYYXY[YYXYYXYYXYXYVXYXY[YX[Y[X[Y[XYXYXVXVXY2/=2//2//1./1.2./1/./1/...1....... ... .. ..... ...//./././/..././//./=/=/=/=====/=/=/=/=2==2==2==/=//.     ../2@FCGGGGOGGGGGGGFCGG@GCGCG@G@F@F@@F=F=F=F=F=F========F==F====F=F=@F===F@GCGCGCGCGOGLGGOCGG@F@F@GCGCGCGGCOGOGOGOGGOGOGGGGGGGGGGGGCGG@G@G@G@F@@F@F@@@F=F@F=F@F@F@GFG@FF@F@FF@FF@FF@F@F=F==F=F=F=F=FFF@FFF@FF=F=FF=F=F@F@F@F@F@F@F@@@F===F@F@F@@@=@@F=====@========/===/8/=*/*/*/**/*(*/*(*/(/***/**((*.*(**(***(*((((((%(((((%((((((((((((((%((((((%(((%(((((%((  (./=@=@C@@C@GCCGCGCOLOOOLOOOOMOOMOOOTOTOTOTTTTTTTTTTVTVTVTVTTVTTTVTTOTOTTTVVYYYYYYYYYYYYYYVVTTTTYYYYYYYY[Y[Y[Y[Y[[Y[[[Y[[[Y[YY[YY[[Y[Y[[Y[YYYXYYXYXYXYYYXYXYYXYXYYXYXYVYVXYVYXYXY[XYXY[XYXY[XYXYXYX=2/2/2//2./1./.../.2./.2/....1..... .... . ......./././//./....///.///2/=/==F=F===F=F==F=@F@F@F@F=F=F==.  . ../////=@GGOOOTMOTTOOOOOOOLOOOOOOLOCGLGCGCFC@GCGCG@@@G@F@G@F@@@G@G@G@C@G@@F@FGCGGOOOOOOOOOOOMOOOG@G@@GGOLOOGOOOOMOOOOOOOOOOOOOOOOOOOGOGOGOGOGOGGGGCGG@G@G@G@G@GF@G@GGGGCGGGCGGGGGG@GFG@GGGFG@G@GF@F@F@GF@GFGGG@GFGG@GFG@GFGGGGGGGGGGGGCGF@GCGGCGCGCG@GCGCCG@C@@G@@C@@@@@@@@@@@@======8=====8=8=8=88=8=888=8888=8*8=*88*8**8*******(****(**(((((*(((*%((*((((*(*((((%(((((**(*(((((..(.(//=@CCGCGCGCGCLGCOCOLOOOMOOTOMOOTOMOTOTTMTOTTMTTTTTTTTTTVTVTVTTTTTOTTTTVVVYYYYYYYYYYYYYYVVTTTTVYYYYYYYYYYYYY[Y[Z[[Z[[[[Y[[[[YY[YYYY[YY[[Y[[Y[YXYYYXYVXYYXYYXYYYXYXYVVVUYVXYXYXYXYXYX[VYXYXVVXYXYXY=2=/2/////1/...2.2...2...2......... ... ... ......./././/.././////2//===/==F=F@@F@@F@@@@F@@G@@@F@F@=F==/.  . . .../2=F=FF@GOOTTTTTTTTTTOOOOOOMOTLOOOOLOGCGCGCGCGLGCGGCGCG@G@G@GC@G@GCGGCG@G@GCGGLOOOMOTOMOTTTOTOGCGG@GOMOOOTOMTTTOTTTTTTTTTQTTOTOTOOOOOOOOOOOOOOOCGGCGCGGCGGCGGGCGGGCGGOGOGOGGCGGGGGGGGGCGGCGG@G@GG@GGCGGGGGGGGGGGGGGGGGGGGOOOGCOGGOCGCGGCGCGGLGGLGCGLGCOCGCGCGC@GCG@CG@C@@@@@@@@@@@@=@8=@=8==@8=9=@9@9=9@8=9=888=888=8=888*8******8****8*************8****8********************(((( (.*/=/==@@@GCCOCOLLOLOLOLOOMOTMTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTVTTTTTOTTTVVVYYYYYYYYYY[YYYVTTTTVYYYYYYYYYYYYYYY[Y[Z[[Y[[[[[Y[Y[[YY[YYYXYYYX[Y[Y[Y[YXYYXYYVVYXYVXYXYYXVVVVVVVVVVYXYXYYVVXVVVUVUVVUVXV=2/2//2/1//.2/./././1./1/../......... .. ... ...././././/....././//////2//=/F======@=F==@F@@F@F=======/=.  ...//=F@G@GGGOOOTTTTQTOOOOOOLOOOOOLOLGLGCG@G@G@GCGCG@G@@@G@@@F@@F@G@@G@@G@G@G@GCGOGLOOOOOOOOOOOCG@F@@GGOOOOOOMOOOOMOOOOOOOQOOOOOOQOGOGOGGOOGOOCGCG@GCG@GCF@G@G@@G@G@GGGCGGCGGCGGG@GGGG@G@G@GFCFG@GF@GF@F@FFG@G@GGFGG@GGGGGGGOGGCGGGCG@GG@GCGGCGGCGCGCGCGCGCCGC@@G@@@@@@@@@@@@@@=@=@===8====8=8=8=8=8=8888=888/8*=*8=88/888*8***8**********(*(*(*(*(((*((**(*(*(**(*(((*((*(*(((((((.*/=====@@CCGLGLGLCOLOLOLOLOOOMOTOTMOTOTMOTTTTTTTTTTOTTTTTTTTTTVTVTTTTTTTTTVTYVVVYYYYYYY[YYYYTTTTTVYYYYY[YYYYYYYYYYYYYYY[[Y[Y[Y[[[Y[[Y[YXYYYXYYVYY[V[Y[Y[XYYXYVVVVVVVYXVYXYVVVVVUVUVVVVXVXYXVXVVUVSVSVSV/2=/2///./2/.2.2./1/./1/..2........ .... ... ..../.././.../..///.///./////2=//==2===/=====2==/=///2////..    ../2=F@FG@G@GOOGOOOGOGGG@G@GCGCGG@G@@F@F==F@F@F@F@F@FF==F==F==F==F==F====F=F@F@G@G@GCGCOGCGG@@F=F=F@GCGCGGOGOGLGOGOGOOGOGOGGGGGGGGGG@GG@GCG@G@F@@F@F@F@@F=F=F@F@F@@F@F=@F@F@F@F@F@F@F@F=F=F=F=F=F=F=F@FF@FFFFF@F@FF@GFG@G@F@GF@F@F@F@F@@F@F@F@F@F@F@F@@=F@@F=@==F==@======/==/8/*=*/**/**/*(/(*/***/***(**(***(**((*(*(*(((*((%(((%(((%((((((((%((((%((((%(((%(((%(((%.*//==@@=@@GCCCGLGCGLGCOCOLOOMOTOTOTMTTOTTTTTTTTTTTTTTTTTTTTTVTTVTTVTTTTTTTVVTYVVVVYYYYYYYYVTTTTTVYYY[YYYYYY[YY[Y[Y[Y[Y[YYYYYY[[Y[Y[Y[Y[Y[YYYYVVVVVVXYX[YY[YYXYVVVUVVVVVVVXVXYXVVVVUVUVVVVXYVVXYVVUSVSSS/=2///2.2///2./.2./.2./.2........... ..... ......../././...././///////////=///2=/==/==2====/=2/=2/////...    ..//=F@G@GG@GGGOGGGGGGGG@GG@G@G@@F@F@@==F==F=F=@@F=F=@====F=====F=F=@F=F=@F@F@@@G@G@GGCGGGC@F=@=F=@G@GGGLGGOGOGOGOGOGOGGGGOGGGGGGGGGG@GGGCG@G@G@G@F@G=F@=F@@F@F@@F@@F@F@F@=F@F@F@FF@FF=F=F=F=F=F=F=F=F=F=FF=FFF@FG@GFG@GFGF@F@F@F@F@FG@G@F@G=@@F=F@F@@F@@@F=@===========/==/8//8/*=*//*/***/****.***/**/*(/**(/(*(*(*(*(*((((((((%((((((%(((((%(((((((%(((((%(((((%((( (.*/==@=@@@C@CGLCGLGLGLGLOLOOOMOOTMOTOTOTTMTTTTTTTTTTTTTTTTTVTTVTTVTTVTTTVTVVVVVYVYVVYYYYYYVTTTTTVYYYYYY[YYYYYYYYYYYY[YYY[Y[YYYYYYY[Y[Y[Y[Y[YYXYXYVVVVVVVYXYXYYXYVVVVSVVVVVVVVVVXYXVVYUVUVVVXVXYXVXYXVUVS2/2/2/.///2.//2/./1./.2.//.1/.......... ... . .../../......././/.///////////2=/=/===2==/=2==2=///////....   . ./2=F@GGGGGOOOGOGOGOGGGGGGCGGCGGG@G@F@F@=F@@F@F@F@@@F=F=F===F==F==@F=F=@F=@F@F@FG@GGCGCGCG@F=F=F=F@GCGCGOGLGOGOCOGGGGGOGGGGOGGGGGGGCGG@G@G@F@F@GF@F@F@F@F@F@@F@F@F@F@@F@=F@=F@F@F@F@F=F=F=F=F=F=F=F=F=F=F=FF=F@FG@GFGGFG@GF@FG@FG@FG@F@FG@F@F@F@@@F@F@@@F@@@F@=@===========//8//*=*/*=**/*/**/***.***/(****/*****((((*(((*((((((((((%(((((%((((%((((((%(((((((((%(((((.*/===@@@@C@GCCOCLGLGLGLGLOOMOOTMOTTMTOTMTTOTTTTTVTTTTTTTTVTTVTVTVTVTTVVTVVTVVVVVYVYVYYYYVTTTTTTVYYYY[YYYYY[YY[YY[Y[YY[Y[YYYY[YYYYYYYXYYX[Y[[Y[YYXYVVVVVVVVVYXVVVVVVVVSVVSVSVVSVVVVVUYXVVXVVXVYXYXYXYXVVU/2////2.2//2///2/./2./.2../............. .. ..............././///////////=/==/===F=====@=@=F===F===2=///...  ..///=F@@GOOMOTTTOTOTOTOOOOOOOMOOOOOOOGCGGCGGCGC@GCGCGGCG@@G@F@@F@@G@G@G@G@G@GGCGGGCGOLOGOOGCG@F@F@F@GCGOOOOMOOOOMOOOOOOOOOOOMOOOOOOOOOOOGGCGGCGGCGGCG@G@@G@G@G@G@G@G@G@GCGG@GG@GGGG@GG@G@FG@F@F@FCFCFG@GFG@GFG@GGGGGGGOGGOGGGGGGGGCGGGGGCOGCGCGCGCGCGCGCGGLGCGCCG@@G@@@@C@@@@@@@=@====8===8=@8==8=8=8=8=88=8=88=*8=88=*8/88*888*8**8*********(***(******(***(******(*(*(***(*(((((( (//8=@@C@CGCOCOLOLOLOLOLOOLOMOTMOTTTTTTTTTTTTTTTTTTTTVTTTVTTVTVVTVVTVVVTVTVTVVTYVYVYVYYVYVTTTTTTVVYYYYYYY[YYYYYYYYYYY[YYYYY[YYYYYY[YXYYYYYYXY[Y[X[YXYXVVVVVUVVTVVVVVVVVVSVTVSVVSVTVUVUVVUVYXVYXXYXYXYXVXVX/=2/2////2//2///1/./2.//1/................ ......./../.....//////=/=/=/=====2=F===F@@F@F@@F@F=F==F===///...  ..//2=F@GGOMOTTTTTTTTTTTTTTTTTTOTOMOOMOOOCGOGCGGOGGOGLGCGCGCGCGCFCGGCGCGCGCGCGCGGCOCOOOOMOOOCG@@@@@@@GCOOOMOTTTTTOTOTOTOTOMOTTQTTTTTOTOTOOOOOCOGOOGCGGCGGGGCGGCGGGCGGCGGCGGCGGCGCGGCGGGGG@GG@G@GG@GFCG@GFGCFG@GGOGOGOOGOOOOOOOOOOOOGOOOGCOGGOGOGOGCOGCOOLOOLOCGCGCGCGCGCCGC@CC@@C@@@@@@@@8@@@9@@@@@8=8=8=8=88=98=8@88=888=88=8=8=88=88=888=88*888*8**8*8*88*8888888**8**88*88*****(((((/*=@@CCLGLLOLLLOMOMTMOTMOMOTTMTTTTVTVTTTVTTTTTTVTVTVTTVTTTTVTVTVTVTVVTVVTVVTVTVVYVYYYYYVVTTTOTTTVVYYYY[YYYYY[YYY[YYYYYY[Y[YYY[YY[YYYYXYYXYYYXYXYY[Y[YYXYXVVVVSVSVSVSVSVVVSVVVSVTVSVTVSVSVSVUVUVVXXYXXVVXVU2/2///1/2/2//2///2///1/././/../...................../.. ...././/////////=/=/===/F=======F=========////....  ..//=F@GGGOMOTTTTOTOOOOOOOTOTOOOOOOOOOCGCOGCOCGCGCGCGCGCG@G@G@@G@@@G@G@GCG@GCGCGGCGOLOOOGCG@@F@F=F@F@GCOOOOOOOMOOMOOOOOOOOOOOOOOOOOOOTOOGCGCGCGCGCG@@G@@@G@G@GCG@G@GG@G@GCG@GG@G@GG@G@G@G@F@F@F@F@F@GF@G@FG@GGGGGGGGGGGGGGOGOGOGGGCGGCGGGCGCGCGCOGCGCGCGCGCGCCGCGCG@@@@@C@@@C@@@@@====8==8==8=8=8=88=8*8/8=88*=888=888=888/88888=888=88/8**8******8*(************8************((((( ((/8=@@CCCGCLGLOLOLOLOMOOLOMOOOMOTTMTTTTTTTTSTTTTVTVTTVTTVTVTVTVTVTVVVVVVVVTVVVVVVYVVVVVTTTOTTTVTVVYYYYYYY[YYYYYYYYYYXYYYYYYYYYYYYY[YYYYYYYXYYYYXYXYYX[YXYXYXYVVSVTVTSVTSVVSVTVSVSVSVSVSSVSVSVUVUVVUVXVXVUV/2//2////2/2//2///1///./2.././............ ............ ../././////=///=////=//=///=2=/=//////2///...... ..2/=F=G@GGOOOGOGGOGOGOGOGOGOCGGCGG@G@G@G@@F@F@F@F@@F@F@F=F==F==F=F=@F==F@F@F@@F@G@GCG@G=====2===F=F@GCGGLGCOGOGOGOCGGGGOOGOOGGOOOOGCG@G@G@G@G@F@F@F@F@F@F@F@F@F@F@F@F@F@F@F@F=F=F@F=F=F=F==F=2=F=F=F==F=F@F@F@F@G@FCFCFCF@G@F@G@GF@G@F@F@F@F@@F@G@@@F@@F@@F@@@@=@@==@==========/8/8/8/*=**/**/**/(*.*(*((/**(**(**/*(*/***/*/******(*(*(((((((*((((((((((*(*((((((((((((((((.*/=@@@CCCGCCGCOLOLOLOLOLOOOMOTOTTTTTMTTMTTTTTVTTVTVTVTVTVTVTVTVTVVTVTVWVVVVVVWYVYVYYVTTTOTTTTTVTYYYYY[YYYY[YY[YYYYYYYYYYXYYXYYY[YYXY[YXYYYXYXYYXYYXYXYY[YXYYXVVVVSVTSTVSTVSVTVSVSVSVSVSVSVSVSSVUVUVUVSVUV//2///2/2//2/2/2///2.2/.//./......................... .......//.//////=///////=//=/=/=/=/=///////..... ./2==F@GGCGGGGGGGGGGGGGGGGGGG@GG@G@F@@F@F@F@@F@F@F=@F=@===F==F==F=F=F@F@F@@=F@@G@GG@@G=F==2===2===F@FCGCGGCGCOCGLGOGLGGGOGGOOGOGGCGF@G@G@GF@F@@F@@@F@F@F@F@F@@@F@@F@F@@F@F@=F=F=F@F=F@=F==2=======F=F=F@F@F@F@GFFG@GFG@GGF@GF@FF@G@FF@F@F@F@FG@F@F@F@F@@F@@F@=@F=@====@======/==/=*/*/**(/**(*/(*/*(/*.**(*/*/*/****/***/***8**/**(*(((*(((((*(((*((*(*(*(((((((((((((((((.*=8@@@CCCCCGLCLOLOLOLOOMOOMOOMTOTMTTTTTTTTTTSTVTTVTVVVTVVTVTVVTVVTVVVTVVVVWVYVYVYYVVTTOTOTOTTTTVTYYYYYY[YYY[YYYYYYYYYXYYYYYYYXYYYYYYXYYYXYYXYYXYYXVVVYXVY[X[YXYYVVSVVSTSVTSVSVVSVSVVSVSVSSVSVSVSSVSVSVSVS2///2//2//2///2//2//////.//........................... ..../..///.////////.././//=/=/=2/=//////./.... . .//F=G@GGOOGOGGOGGGGGGOGOGGGGGCGG@G@GG@GG@F@F@@@F@F@@F@F@F==F=F@F=@=@F@=@F@F@FG@G@G@G@F=2===2===F==@G@GGCGGLGGCOGCGCGOGGGOGOGOGG@F@F@FCG@G@G@F@@F@F@F@=@F=@F@F@@F@F@F@F@F@=F@=F=@F=F=F==F==2=2=F=F==F@F@F@F=FF@GF@GFG@GF@GFF@G@F@F@F@F@FG@F@@@F@F@@@@F@@@F@@F@@@@F@=@=============*=*/*/(**/***/***/***/*.****/**/***/***/*/*/*(*(*((*(((((((((((((*((**(*(((((((((((/=8@@C@GCGCLGLOLOLOLOMOOLOOMOOTMTTTTTTTTTTTTTTTVTVTVTVVTVVVVTVVVTVVTVVVVTYVVVVVYVYVTTTTOTOTTTTTTVVYYY[YYY[YYYYY[YYYYXYYYYYVYVYVVSVYYYXYYXYYYXYYVVVVVVVVVVVVYXY[XY[VXYVVVSTSTVSVTVVUVVSVSVSSVSSSVSSVSSSSSS//2//2//2//2/2/2///2//1//1///...................... .. ...../././/.////=///.. ../=F=F=====/=/=//////.....  /F@GGOOTOOTOMOOTOOOTOTOTOTOOOOOOOCOOOOLGOOGOGGGCGCGGCG@G@@G@GC@GCGGCGCGG@GCGOCOGOCGGCF@F=F=@F=F@F@GCGOLOOOOLOOOOOOOOOOOOOMOOOOG@G@G@GGGOCGG@GCGGCG@C@G@@G@@G@G@G@G@G@GCG@G@G@@G@GFCFGF@F@F@F@F@F@GGG@GF@G@GGGGGGOGGGGOGGGOGGGGGGGGOGGGCGGCGGGCOGLGGGCGGGCGCLGCGCC@C@C@C@C@@@@@@@====8=8==8=8=8=8=88=8=8=8=8==98=8=888=88=888*8=**8***********8*8***8***8**((***(*(((((==@@CCLCCLOLOLOLTOMTTOTMOTOTOTOTOTTMTTTTTTSTVTVTVTVTVVTVVVTVVVTVVVVVVVVVVVVVVYVYVVVTTOTTOTTTOTTTTVYYYYYYYYYY[YYYYY[YYY[YYXYYVTTTTTSVYYXYYXYXYYXYXVVVSVSVVSVVYXYYXXY[XYXVVVSVSVSVSVSVUVSVSVSSSVSSVSSVSTSTS/2//2/2//2//2///2//////.//.//./.........../........ .........///////=/===//. ..../=F=F@F==F==/=//////......  ./F@OOMOTOTOTQTTQTTTTTTTTTQTTTOOOOMOOMOOOOOOOLOGCOGGOCGGCGGCGCGGCGCGCOGCGCGOLOOOLGOOCG@F@F@F@F@@G@GCOOOOOOMOOOOMOOMOTOMOTTOTOOGGGCGGGOLOOOCOGGOCGGLGGCGGCGGG@G@GGCGGLGCGG@GCGG@G@GG@G@G@G@F@@FGGGG@GGGCGG@GG@GGGOGOOGOOOOOGOOOGOOGOCOOOGLGOOCOOOLOOLOCOCOLOLGLGCOCGCGCCGCCCCCC@C@C@@@=@=8@=@8=8@8==8=8=88=9@@9@@8@8=8=9=88=8=8888=*8*88*8*=888=88=88=8=8=88*(**/***(((.8@CCLOLOMOLMLTMOTSTTTSTTTTTTTTTTSTTVTVTVVTVVTVTVTVTVTVVTVTVTVVVVVWVVYVVYVVVYYVYVVTTTTTOTTTTTTTTTVYYYYYYYXYYYYYXYYYYXY[YY[YYYVTSTTMTVVVYVYYYVXYVXVVVVVSVTSVSVVVVYXYXYX[YXYXYVVSVSVSVSVSVSVSVSVSVSSVSSSVSSS//2//2/2//2//2/2/2//2.//.//.//./.................. ........././../././//=/.. . ./======/=////....... . .  .=FCOOOOOOOOOOOOOOTQTTOOOOOOOOOOOOOMOOOOOGOOGCGCGCGCGCFC@GCGCG@G@GCGCGCGCGOGCOGCGCG@F@@F@F@F@F@@F@GGLOOLGOGLOOOOOOOOOOOOOOGG@G@G@G@G@OGGCGGCGCGCGCGCG@@@F@@F@@@GG@G@G@G@G@G@@G@G@G@GF@F@F@F@@F@G@G@G@G@GF@G@G@GGGGGGGGGOGCGCGGCGCGGGCGGGCGCGOCGLGCGCOGCGCGCGCCGCCC@C@@@@@@C@@@@@@@=@=8==8=8=8=8=8=8=8=8=8=8=88=888=88*=8=888/8/8****/****8**=8*88/8*8*=88**((((((/8@CCCLLGLLOLOLMOTMTMTTTMTMOTMOTOTTTTTSTTTSTVTVTVTVTVTVVTVTVVTVTVVVVVWVVYVYVVYVYVTTTTOTOTOTTTTTTVYYYYYYYYYYYXYYY[YYYYYYXYYVVTSTTTVTVVVSVTVSVVVVVVVVVVVVVSTSTSSVUVVYYVXYXYXYXYXVVVSVSSVSVSVSVSVSVSVSVSTSTS/2/2//2/2///2/////////.//.//./............./..... .......././..././/.///=.. ..///////... .  ./=FGGGGGGGGGGOGOGOOGOGOGOGOGGGGGGGGGG@GGCG@G@F@F@F@F=@F@F@F@@F@F@F@F@F@@G@G@@F@F@@F=F==2=F==F=F=FCGCGGCGCGGCGCGCGCGCGGCF@F@F@F@FF=@G@GCG@G@G@@G@G@@@F@F=F==F=F=F@F=F=F==F=F==F=F=F=F=F==F=F=F=F=FF=F=F=F=F=F==F@F@G@F@F@GFG@GF@G@F@G@F@G@@G@@G@@G@@@@F@@F@@F@@@@=@=@=@==@===@===8=*/***.**/**(***.**(/(*(*(/**/(**.*(/*(/(*(*(((((((((*((**(*************(((.=9@@CCCLCGLGLOLMOMOTMTOTMOTTOTMTTTMTTTTVTTVTTTVTVVVTVVTVVVTVTVTVTVTVVVVVYVYYVYVTVTTTTOTOTTOTTTTYYYYYYYYYYYYYYXYYYY[YXYYVTTTTTVTVSYYXYVTSTTSTSVVVVVVVVVVVVSVTSTSTSSSYVVXYX[YX[YXYXVSVSSSSSVSVUVUVSVSSSVST//2/2=/2//2///2///2/.//.//././/./......../..... . ......../...././././///=/.  ..//./....   ./=FG@GFGGGGGGGGGGGGGGGGGGG@GFGGGG@G@GGFG@FG@F@F@F@==F@F@@F=F@@F@@F=@F@G@G@F@FG@F@F=F=F==F=F==F=@G@GGCG@GGCGGCGGCGGCG@GF@F@F=F@F=@F@GG@G@GG@G@G@G@@F@@F@==F=F==F=F===F======F==F=F==F==F==F===F=F=F@F==F=F=F=F=F@FF@FGFG@FF@FG@FF@GF@FG@F@F@F@F@@@F@F@@F@@@F@@@F=@==============*/*/*/(/*(*(/(/(/(*(.(((/(/**(/**.**.**(/*(*/*(*(*(*(*(*(*********(**(**(*(*(/8@@CCCOCLCOLLOLOMTMTTMTTTTMTTOTTTTTTTSTVTVTTVTVTVTVVVVVVVVVTVVTVVTVVTVVVVWVVYVVVTTTTTTTTOTTOTTVYYYYYYYYYYYYXYYYYXYYYYVSTVTTVTSTVVYXYYYXVVTTSTMTSTSVSVUVUVSVSVTSTSTTSTSVVVVVXYX[YXYXSVSSVSSTSSVSVUVUVSTSS2//2=2//2///2//2//.////././/././/./..../........ ( .../././..../././/./////. (. . ..    ./2@FGFGGGGGGGGGGOOOOGGGGGGGGGGGGGGGGGGGGGG@G@GFG@F@F@F@@F@F@@F@@F@@F@F@G@G@F@G@F@F@F@=F=F===F==F@GGCG@GGCGG@GCG@G@@F@F@@F=F@F@F=F=G@GCGCG@G@G@G@G@G@F@@F=F===F====F=F===F=2==F==F=F==F=F=F==F=F=F=F=F@F=F===F=F=F@F@F@G@FGFCFG@G@GF@FCF@G@FG@G@@FC@FCF@@G@FC@F@@@F@@=@=@=======/=*//*/*(/*/(***.**.*(/**.*(*/(**.**/(*.*(*(*(**.*((*((**(*/(********(****(*(((.8=@@CCLCOLLLOLOMMOMTMTTTMTTTTMTTTTMTTTTVTTTVTTVTVVTVVVTVVVVVVVVVVVVTVVVVWVVVVTVVVVTTTTTTOTTOTTVVYYYYYYYYYVYYYYXYYYVVTTTTTTTTTTTSVYXYYYXYYVVVMTTMTMTSTMVTSVVSVSVTSTMTMTSMTSSSVVXYXYXVVUVSSTSSSTSSSSVSVUVSS/2/=2/2//2//////2//2.//././/.//.//............ . ........//./../././/.//=//=.. .   .. . . . . .  ../F@GOOOOOOOOOOOTTOTOOTOOTOOOOOOOOOOOOOOOOOOOGOOGGGCGGGGGCGGCGGCGGCGGCGOGGOGCOGCOGCGCGCF@F@F@F@FFCGOCGOGLGOGOGOCGG@G@G@G@G@G@G@G@F@GGOOGLGGLGLGCGGCGCGCG@G@@G@@@F=F@F@@F@F@F@F@F@FC@@GG@@F@@G@FCF@G@G@GFCF@GF@@FG@G@GGGGGGGOGGOCGGOGGOGGOGGOCGCGGOLGOCGLGLGLCGLCGCGCCCC@CC@C@@@@@====8===*=*=8=/88=88=8*=8=*=88=*=8*=8=8*8=*=88=88*88*8=8=888=8=8*=88=8*=*8/8*=**==@CCLLOLLOMOMOMOMTMTTTTSTTTTTTTMTTTTTVTTVTVTTVTTVTVVVVVVVVVVVVVVVVVYVVVVVVVVVVVTVTVTTTTOTTTTTTYYYYYYVVVYVVYYVVYVTVTTTTTTTTTTTMTTYYYYXYYVXVVVVSTSTMTMTMMTMTMTSVSVVSTSTMTMTMTSSTVUVVVUVUVUVVSTSSTSTSSTSSVSV/2=2/=2///2//2.///2///.//./././/././.././...... ......//.//.//./////=/=/2===/.. .. ..    . . ... .....   ../=FGGOOOOOTOTOWTTWTTTTTQTTOTOOTOTOTOOTOTOOOOOOOOOOOGGGOGOGCOGCOGOCGGGOOOOLGOGLOOGCGOGGCGGCFG@FG@GOOOOOLOOOOOOLGGCG@GGCGGCGGCGGCG@GCOOMOOMOOOLOLOOOOGGOGOGOGCGCF@@@G@@G@@G@@@F@@@G@GGCFCFG@GGCGGC@G@G@GGGCGG@GG@G@GFGGGOGLGOGOGOGOOOOOOOOOOLOGOOLOLOOOLOLOLOOLOOLLGLGLGCCGCC@@@@@@=@=======8@==9@=8=8=8=8=88=8=8=88=8=88=8=888=88@8@8@8@898@8@8@8@99=889=9=888=888/*=CCLLLMLMMTMMMTMTTSTTSVTVVVTVSTVTVTVVVVVVVVVVVTVTVTVTVTVVVVVVVVVVYYYVVYYYVYVYVVVVTVTTTTOTTOTTTYYYYYYYYYVYVVVVTTTTTMTTTTTTTTTMTTTVYYXYYYXYVVVVVVVVSTTMTMMMTLMMOMVSTVSTSTMTMTMTMSTSSSVUSVUVSVSVSSTSTSTSSVSSS/2//2//2/2//////2///.//./../././//..//.....(... . (.....//../..//.//.//////=/// . .  . . . . .  ..F=GGOGOOOOQOOOTQTTQTQOTTQOOOQOTQOOOOOOOOOOOOOOOGGOCGOCGGGGCGGCGCGCGCOOLGOCGCGGCGCGCGGG@F@@F@F@GGCOGOGOLOOOGCG@F@G@@G@G@G@G@G@F@F@GOLOOCGOCOCGGCGCGCGCGCGCG@@F@F@F@F@F@F@F@F@FF@F@@F@G@FCG@G@G@G@GF@GF@G@GF@G@F@FG@@GCGGGCGGCGCGGGGOGGGGGGCGCGCGGLGCOCGLGCLGCOCGLCGCGCC@@@@@@========8==8==8=8=8=8=*8*=8*=88=8=8=8/88=*=8=8=88=88=88=888=88=88888=888=8888=*88/8=((*=@CCCLOLOLOMOMMOMMTMTMTMTTTSTTTTTTTTSTTVTVTVTVTVTTVTVVVVTVVVVVVVYVYVYYYYVYYYYYYVVYTTTTOTOMTOTVVYYYYYYYYYVVVTTTMTOTTTMTTTTTTMTTTVVXYYVVVVVVVVXVVVVVVUTTMTMOMOMOMMOSTMVTSTSTMTMTMTMTSMTSSVSVSVSVSSSTSMTSTSTM//2=/2/2///2/////.///.//.//.//.//.//./......(. (. ..(.././/.././/.//////.///2=/.       ..=FF@GGGGGGGOGOOOOGOOOGGGGGGOGGGGGGGGGG@G@GG@G@@G@FG@F@F@F@G@F@F@FCG@G@@FCFCG@F@G@F@F@=F==F==F@GGCGCGCGG@G@F=F@F@F@F@F@@F@=F=F=F@GCGCGCGCG@G@CG@G@F@FCF@F@F@=========2===/=/==/==2=F=F=F@F=F@F=F=F=F=F=F=F=F=F==F=F=F=@F@F@F@GF@G@G@G@G@G@@FG@@@G@G@G@@@G@@G@@G@@@@@=====/=//*//*/*//*/*=*.**.*(/(*.*(*.(((/(**/(*/(**(/*(*/******=*8/*8/8**/*/******/**/**(/*(**(=8@CCLCLLLMOMOMMTMOMTMOTMTTTMTTMTTSTTTVTVTVVVTVTVVTVVVVVVVVVVVVVVYVYVYYYYYYYYYYYYVVTTOOOMOOTVVVYVYVYYYYYYVTTOTOTMTTTTTTMTMTTTTSVYYVXYVVXVVVVVVVVVUVVVUVMMMOMOMOMMOMMMTSMTSMMTMTMTMTMTMTSTSSUSVSSTSSTSMTMTM2/2/2/2=2////2///////.//./.././/././//....(... .(....../././././//////./////=///      ..2=FGFGGFGGGGGOGGGGGGGGGGGGGGGGGGGGG@GFGFCFCFF@FF@F@F@F@F@F@F@F@FCF@G@FG@G@GF@G@GF@F@F=2==2=F@GG@GGGGG@GG=F=F@F@F@G@F@F@@F=F==F@GCGCGCGCG@G@G@F@G@G@G@G@@F@F=F=F=2=2===2===2=/2=/F==F@=@F=F@F@F===F=F=F=F@F=F=F=F==F=F=F@F@F@F@F@FF@FF@F@F@@F@F@@F@@@F@@F@F@@F@F@@=====/=/////*//*/*/*/(/*(.**(/(*(*/(**/*(*.*(*/(**.***/***/*/*/8**/*8/**/*****/*(/*(/****.**/**.*=@CCCCLOLLMOMMOMTMMOTMTTMTTTTTTTTTTTSTTTVTVTVVVTVVVVVVVVVVVVVVYVVYVYYVYYYYYYYYYYYTTTTOTOTTTVVVVVVYYYYYVVTOTMTTTTTTTMTTTTTMTTTVVYVYVVVVVVVVVVVVVVVUVVVUVTSMOMLOMOMMOMMTMMMTMMMMMMTMMMTMMTMMTMSTSSTSSTSTMMM/=2/=2///2/2///2//.///.//..//.//.//././..... (....(..../././/.//////////////=//.  .. ..2=GGGGGGGGOOOGOQOOGGGGGGGOGGGGGGGGGGGGGGG@G@G@G@GF@GF@F@G@F@FCFG@G@FCFCFG@@GFG@GF@=2==2=F=FG@GG@GCGG@F@F@F@FGCG@G@F@@F=F=F==@GCG@GCGGCGG@C@@G@G@G@F@@F@@F@F=======2===2===/=//=/=F=F=F@@=F@=F=F=F==F=F=F==F==F==F==F=F@F@F@@F@FG@G@GF@G@G@FCG@G@@G@@G@@G@@G@@@@@@@======/=////*/*//(/**.**.*(*/*.**/*(*/(**/(**/(*/*/**/***/*8**/8**/8****/**/(*******(*/(**(/**((=@CCCLCLOLMMOMMTOMTMTMTTTTTSTTTTTTSTTVTTVTVVTVTVVVVVVVVVVYVVYVVYVVYVYYYYYYYYYYYYYTTTOTOTTTVVVVVVVYYYYVTTTMTTTTTSTTTTTTMTOTMTTVYVVVVVVVVVVVVVVVSVSVVUVVSVSTSMOMOLMTMMTMMOMMTMMMOMMMMMMTMMMMMMMMTSTSSTSSTST2/=2//2/2//////////.///./.././/./////....(.... .(......../././/.////////2///2==/..     . . ...//......//FGOOOOOOTTQTQOTOTTOOOOTOTOOTQTOOOOOOOOOOGOGGOGOGGGCGGGGGGOGOGOOOGOGOOGOGGOGOCGGCF@FF=F@FGCGGOGOGOCGG@GFGCOGOOGGCG@G@@@@@@FCGCOLOOOOOOLOCGGGCGGCGGCGCG@G@@@F@F@F@F@F@@F@=F=F=F=F@@@G@G@GG@GC@G@G@FG@F@FCG@GF@F@FF@FG@GG@GGGGGGCGGGGGOGGOGOGOGCOGOCGOCOGCOCGLGCGCGCGC@@@=@===========8==/8/8/=8=88=8=8=8=8/8=8=*8=8=88=88=8=88=8=88@8=8@8=888=88/8/8/8/**8/*=*8/*=(*=CLLLLLMMOMTMTMTMTMTTMTTTSTTVTSTTTTTTTVTVTVVTVTVTVVVVVYYVVVVYVYYYYYYYYYYYYVYYYVTTTOTOTTTVYVVVYVVYVVVTOTTTTVTVTVTTTTMTTOTMTTVVVVVVVVVVVVVVVVVVVVSVSVTSVSVSVSTMLOMOMOMMTMTMMOMTMMMOMMMMMMMMMMMMMMTMTMTSTMM/2/2=2=2/2//2//2////.///.//././/.///./....( .......(//.../////////=/=/========F=/.     .....2/2//////.//=FOOOQTTWTTTWTTWTQTTTTWTTWTTTQTTQTOOOOOOOOOOOOOGOCOOGOOOGOOOOOOOOOOOOOOOOOOOOLOG@F@F@FG@GGOOOOOOOGCFGCOOTOTOOMOGCGCGG@G@G@GLOOMOTOMOMOOOOLOOLGOCGLGCGOCOGCG@@G@@G@G@@F@F@F=F@G@G@@G@GCG@GGGGCFG@G@GG@GG@GG@GG@GG@GGGCGGGGGGGGGGCGOGOOLOLOOOOLOLOOLOLOLOLOOLGCGCOCCCG@@@====@==========@8=8=8==8=@8@8@8=8=8=8=8=8=8@@8@=9@9@@8@9@9@9@9@99=888=888888=8=8=8=8=8==8(.8@LLMMTMTSSTSTSTTTTSVTVVTVVVVVTVVVVVVVTVVVTVVVVTVVTVVVVVVYVVVYVYYYYYYYYYYYVYYVTTOTOMTTTVVVYVYVYYVVTOTMTTVVVVVTTTTMTOTMTOTMTVVVVVVVVVVVVVVVVVVVSVVVSVTSVSVSVSTSTMLMMOMMTMMMMMOMMOMMMMOMMMMMMMMMMMMMMMTSTS/2=/2=2////2///////.//.//.//.//./////......( ...(.././...../././///////2/=2//2=/=/..    ..././2=2///../2=FOOOTQTOTQTQTOOOOQOTOTQTQTQTQOOOOTOOOOGOGGGGCGGGCGGCGCOGOGOGGGGGOGOGCOGLGCOG@@F@F@F@F@GGCGGCGCF@FG@OOOMOOOOCG@F@@F@F@F@@GGLOOLOOOLOOLOCGOGCGCGCG@GCGC@G@@G@@F@@@@F@F==F=@@@@F@F@F@@G@G@G@@G@G@FF@FG@FG@F@F@F@@G@G@G@GGCGCGCG@G@GGCGOGOCGOCGOCGLGCOCOCGCCGCGC@@G@@@@==@==========8==8==8=*=8=88=8=8=8=88=*8/8=8=88=88=8=88=88=8=8=888=88=888/8=8=*8=***8/*/**/*/(/CCLOLMOMTMTMMTMTMTTTMTTTTSTTVTTTTTSTVTVTVVTVTVVTVVVVVVVVVVVYVYYYYYYYYYYYYYYTTOTOOTOTTVTYVVYYVVVTOTTTVVYVVVTVTMOTTTMOTMTTVVVVSVVSVVVVVVVVVVSVVVSVSVTSSVSTSTSTTSTMMTMOMMTMOTMMMLMMOMMTMOMMTMMOMMTMMMMMMM/=2/=2/2/2///2///2//.//.//.//.//////./..... .(....///../.././/.///////=////////=2/.     . ..../......../FGOGOOGGOGOGGGGGOOGQGOOOOOOGGGGOGG@G@G@G@G@F@F@F@GF@G@G@G@G@G@G@F@G@G@G@G@=F=2==2===F=F@F@F@F=F=GGCOOGOCGCF@F@F=======F@@GCGCG@GCG@GCG@@@@@F@F@F@F@F@=F==F=F=F===/=/===2=F/==2=====F===F=F=F==F=F=F=F==F=F=2=F==F=F@F@F@F@F@F@F@F@G@@G@@G@@G@@@G@@@F@@F=@=====/==/=/////*////*//*/*/(*/(*.*.*(/*****.(*(.**.**/*/8*=**/**/**/*8*/*8*/**/**(**(**/**/*(*.**.*.(/@CLLLMOMMMTMTMMTMTTTSTTTTVTTTTTTTTTVVTVVTVVVTVVVVTVVVVVVVVVVYYYYYYYYYYYYYVTTOOMTOTTTTVVVVVVTTTTTTTYYVYVVVTTOMOTOMTOTOTSVVVVTVTVVVVVVVVSVVSVSVTVSVSVVSVSTSTMSTTSTMTSOMMOMMMOMOMMMMMMMMOMMMMTMMMOMMTMTM/2/=2/=2///2///2/////.//.///././////../..... .(./././//./..././/.//////2//////=///.   ...1........ ./FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG@GFG@GF@F@F@F@F@F@F@F@GF@FF@FGF@G@GF@G@G=F==2==2==F=F=F=F@F==F=GGGOGLOGOCF@F=F==F==F=F=G@GCG@GC@GCGCG@G@G@F@@@F@@@F=F@@F@=F===2=2==F=F=====2==/F/F===F=F==F=F==F=F=F=F===2=F==2=F=FF@F@F@F@F=F=F@F@F@F@F@F@F@F@=@F=@====//=/=/===/=/=/////8/=/*//*/*/(*(.*(*(/**/(/*(/(*(/(**/**/***/***/**/**/8*/***/****/(**.***/(*/*(*.*(((/CCLOLMOMTMMOTTMTTTTTSTTTTSTTTTTTTSTTVTVVVTVVVVVVVVVVVVVVVVVYVVYVYVYYYVYVTOTOTTOTTTOTTTTTTTTOOTTVYYYYVYVTTMOOMOOMOMOMTTVTVSVVVSVVSVVSVVSTVTVSTSVTVVSVVSVMTMTMMTSTSSTSOMMOMLMMLMOMOMMMMMOMMMMOMMMMMMTM/=/2=2//2////2=///////.//.///./////././.... (./../////./././././/.////=///////2=//     . . ......./FFGGGGGOGGGGGGOGOGOOQOOQGGGGGGGGGG@G@GFCFG@FF@FG@FG@GFCFG@FG@F@FG@G@G@@F=2=/=2==2==F==F=F=F=FGCOGOOGLGGCF@F==>=2>====@F@G@G@@GGCGCG@G@G@@@F@F@F@F@F@F@F@F====/===F====F=2==/=/=2=/F====2====F====F==F=F==2==2=F=F=F@F@F@GF@F@F@FG@G@G@G@F@@F@F@@@F======/==========/===////*/=*//*/*(/(*(/(**.****/(*.(*(/***/**=*8*/***/***=**8*/*8*/*/***/***/*(*/**.*(/(. (=CLLMOMMMMTMMTMTTTSTTVTSTTVTTTTTSTTVTVTVTVVVVVVVVVVYVVYVVVVVYVVYVYVVYVVTOTOTMOTTOTTOTTTOTOOMTTVVVYYYYVVSTOMOOOMOOOMOTTSVTVTSVTVVVTVTVTSVTSTVSTSVSVVSVSVSTMTMMTMSTSTSSTMLOMOLMOMLMOMMMOMMOMMMMLMTMMTM/2==//2//2/2=/////////.////.//////.///........././//.////././././//////=////=/=///.  . . .  .. .2.//.//2/=GGOOOTQOOOOOTOTQTQTTWOTTQTOOOOOOOOOOGOGGGGCGGGCGOOGOGOGCGGGGOGLGGGOGGG@@F=F@@F@F@F=F@F@F@FGLOOOMOOMOOOCGF?IE?E?I>I>I@CGLGCGCOCOGLGOCOCGGCGCGC@G@GCGCGCG@@F@F@F@F@@G@F@F@F@F=F=@F@=F@F@@F@F@F@FF@F@FF@F@F@F@@F@FCG@GGGGGCGGGGGGCGGGOGOGCGGCGCGCG@@G@F@@@=@=@@@@@@@@@=@=@=@=@=====8==8/8/*==8=8/=8/8=*8=*=*==8=8=8===8=8=8=8=8@=8@@8=@8=8=8=8=8/8=*=*=*8//*/(( (/@LMOMTTMTTMTMTTMTTSTTVTVVMTTTSTTTTTSTVTVTVTVVVVVVVVVVVYVVVVVYVYVYVYVVTOTOOMOTTOTOTTOOOMOOTTTVVYVYVYVVVTTOOMOLOLOLOLMOTVTSTVTVTVSVVTSTTSTSTTSVSTVSVVSVTSTSTMTMMTSTSTSSTSMOLLLLLMOMLMOMMMLMOMLMMMMMTM==2=2==2=/2=/2/2/2//////////./////././/...../../////////////////////=/=2/=/F/===2/.  ... ....... . .././2/////=2=GTQTTTOTOTTQTQTWTTWTTTWTTWTTQTOOOOOOOOOOOOGOOOOOOOOOOCGOCGOOOOOOLOLGCF@F@@F@F@G@G@G@G@GGOOMOTOTTOTOOOPwwwuwvuvuIEIBIAOCOLOOLOOOOLOOOLOOLGGCGGCOLGLOOCG@G@@F@G@CGG@G@@@G@F@F@F@F@F@@G@G@GF@F@@F@GGCFG@G@G@G@G@G@GGGCGOGOGGOGGCGGOOOCOGOCGOCGCGCG@G@@G@@@@GCG@G@C@@@@@@@@@@==@=@=@====8==8=8==8=8=8=8=8=8=8==@8=@9@9@@9=9@@9@9@@9@@9@@9@@@9@9@8=8=8=8==8=*/*.(.(.(*=LTSSSVSVSVTVSVVTVVVVVVVVVVVVVTVTVTVVTVTVVVVTVTVVTVVVVVVVVVVVVYVYYVVTTOOOMOOOTOTTOTMOTOTMTTVVVVVVYVVVTMTOMOOLOLOLOLOTTSTTTTSTVTVTSVTVTSTTSTMTSSVSVTVSSVTSTMMTMMTMSSTSTSTMMTLLLLLLOMMLMOMLMLMOMOMTMM2/=2=2=2/2//2////=//////////////.//.//...../..////////////././././//.////////2/=//. . ....... .. . ./././.2/2///FGOTQOOOOQOTQTOTQTOOOQOTOOTOOOOOOOGOGOCGOGCGGOCOGGOGCGCGGCGCGCOGGOCG=F==F=F@F@F@F@FG@GGGOOOOOMOOOOOPBzt|~t~tttvtuv??IAGCGCCGCGLGLGLGCGCGCGCGCGCGGLG@G@@@F@@@F@F@@F@F@@@F@@==@==F@F@F@F@=F@=F=F@F@F@F@F@F@F@F@F@F@G@GGCGGCGCG@GGCGOGCGCOCG@G@@F@@@F@@F@@@@@@@@@@@@@@=@=@==@======8=8=/*=*/8*=8==*=8/8=*=8=8=8=8==8==8==8=8=9=9@@9=9=8=8=9=8=8=8=8=*/8/*/*/( .(.((/9LMTSTSTTSTMTTMTTTSTTTSTTTSTTVTSTTTTSTVTTVTVTVTVTVVVTVTVTVVTVVYVVVWTOMOOOOOMOTOOTOTTOTTTVVVVVVVVVVVTTTTOOLOOLOLOLOMOTTTMTMTTTTSVTTSTSTTMTTSTSTSTVSVSVTSTMTMMTMTMTSTSTMMTMMMTLLLLLLLOMLOMLOMLMLMMTM/2=2/2/=2=2=////2////////////////.//.....././/.//////////////././/.///.////////./.    ... ... ..............//FGGGGGGGGOGGGQGOGGGGOGGOGGGOGGG@G@G@G@G@G@G@G@F@@@@F@F@F@F@G@FC@@F===2==2===F=F@F@FG@GGOGOOOGOGOCI|~{~{{{{{|~y|tvqE?IAF@@F@@@G@G@@G@@G@F@@@F@@G@@@F@F==F=F====/F=====2==2=/2=/=/==2==2==2=2==2==F=F==F===2==2=F==F@F@F@F@F@F@F@F@F@G=F==F==F=====2==/==/=======/=/=/=/=*///*/*//(/**.(/(/(*.*(*(/(*/(*.**/**/**/**=*/8*=*8/88/8=*8/8*8/*8/8*/**/(*((.((  ((.(((.((.(/=COMMTMTMTTTMTTSTTTSTTVTVTTVTTTTTSTVTTSVTVTVVTVTVVTVVTVVVTVVTVVVYVTTOOOOMOOOTOTTTTTTTTSVVVVYVVVVVVVTTTOMOOMOLOLOLOMTTTMTMTMTMTTMTSTTSTTSTSTTSTSVSTSVTSTSTMTMTMMTMTMTMTMTMTMMTMMLLLLLLMLLMLLMOMOMMO/=2///2/=2//2/////2/////////////..//../..//.//////=/=/////////././//./././///././.   ..........2/2=2/2/.....2=FG@FGGGGGGGGGGGGGGGGGGGGGGGFGF@F@FGF@@FF@F@F@F@F@F@F@F@@@F@F@G@F@F===2==F=F@@@G@G@GGCGOGOGOGGGAz}}}}}}}}}{}{~sttuvw?IA@F@@F@@@@G@@G@@@@@F@G@G@F@@=@F=@====2=/===F=2==/==/=/=/==2==2===2===2==F=2==F==2=2==2===2=F=F=F=F=F=F=F=F@F@F==F=/=F/=/=2=/==/=/==/==/=/=//=////=////*/*/.*.*.*(/(*(*.*/(**.*(**.**/**/**/**/*/**=*8/8=*8=8*8/*8*/****/**(/(/(( .(.((.*.*/====8/*.(.*/8CLMTMTMTSTSTSTTTSTTVTTSTVTTSTTTTTTTSVTVTVSVTVTVTVTVTVTVTVTVVTVVTVTTOOOOOOMTTTTTTTTTTVTVVYYVYVVVVVVTTTTMOOLOLOLOLOTTSTTMTOMOMOTMTMTTMTMTTTSTSTVSTVSTSVTSSTSTMTMTMTMMMMMMTMMMMMMMMLLLLLLLOLMLOMLOML2//2=2/2=/2=///2////2//////////./././..././/////=///==/=/////././/./.././/.//./....  . ..../..//2=2=FFF=F/2/2/2@FGGGGGGGGGQGQGGGGGGGGGGGGGGGGGG@G@G@G@G@GF@G@F@@F@F@@@F@F@F@@GF@F=F=F=F@F@G@G@G@GGCGGGOOGGGAPw}f}}}a}{{{ttzEw>I>@F@@F@FAF@FAF@FAF@A@FAF@>F=@=F=F=2===2==2====2=//=/2=/=/=/==2===2==2==2==F==2=F==2==2=F=2=F=F@F=F=F=F@F@F@F=F==F=F===2====/=======/====/=/=//=///=*////*/(/**/(/*/(**.**.**(/(**.*/*/*/***/8*=*=*/8=8=8/=8=*=*/*8/*/***/(*.((( (((.**/*==9@@C@@@@=8==@COMMMTTSTTSTTSTSTVTSTTVTSTVTVTSTTTTTSTVVVVVTVVVTVVTVVVTVTVTVVVTVTTTTMTOTTTTTVTVTVTVTVVVVVVVVYVVVVVVTTTTMTOOLOLGLOMTTTMTOMOMOMOMOMMTMTTMTSTVSVSVSVTSTSTSTSTMTMMMTMTMTMTMOMOMOMMTMMMOMCLLLLOLMLMLOM/2=2//2=2/=/2//////2//////////./././..././////////=/=/=/==/////.//././././/.///.. .   .....//=/=/==F@FGGGOOGGGF@FGGGGOOOOQOQOWOTQOOQOOOOOOTOOOOOOOOOOLGOOOGLGGCGGCGGCGCGGCGGCGLGGLGCG@GCG@GCGGCGOOOGOOOOMOOPPIw~nnononmm}}}a}~||zzwwBIBIBIBIBIBIBIBIBPIBIBIBIBIBIBIBI>I>I>I>I>F>F>F>F=F==F=F=F=F=F@F=@F@FF@F@F@FF@F@F@@F@F@@F@F@G@G@G@G@G@G@G@G@G@FCF@@F@F@@@F@F@F@@@@F@@@=@@@=@=@=@=@=@==========8===8===8/8=8/=8=/8=8===8===8=8==@8@@@@@@@9@@@@@@@=8=8=8==*/**/*.(.  (.(.*//8/=@@@CCCLLLCLCCCCLLMTTMTSTTSTVTSTTSTTVTVTSVTTVSTTSTTSTVTVTSVTVVVTVVTVVVTVTTVTVTVVTVTTTTTTTTVTVTVTVTVTVVVVVVVVVVVVYVVVVVTTTTMOLOCOLTTSTTMOMOMOMOMOMOLMOMMTTSTVTVSVVSVTSTMTSTMTMMTMTMTMMMTMMOMLLMOMMMMMMMMOLLLLOLOMLO=2//2=/=2=2=/=2=2//////////////././....///////=/=//======/==////////.//////////...   . ....././/=F==F=FGGGGOOOOOOGOOGOOOQTQTQTWOTWTTQTTQTTQTTOTTTOOOOOMOOOOOMOOOOOCOGCOCGGLGLGLGOLOGOGGOGCOGOOGLOOOOMOOMOOMOOPxwn¥¢¢£¢¢¥¥onmmf}}}~|~||zzzwwwwwBwBwBwPwNwBwPwBwNwBwBwBwBw?Bw?E?E?I?v?I?I?D?I>D>>F=F=F@F@F@F@@F@FG@G@GG@G@GF@F@F@@G@GFCGGGGGG@GFGG@GGG@GG@G@G@CG@G@@@G@@G@G@@G@@@@@@@@@@@@@@@@@@@@@=@=@=@===8===8=8=8==8=8===9===8=@=9@@@C@CC@CCCCCCC@@@@@9==8=8==8==*/*.( ..(//8===@@@CCLLMLMMTLMMMMTMTSVVSVVVVVVVVSVVVVVVVVVTVVVVVVVVVVSVVVVVVVVVVVVVVVVVVVVVTVTVTVVTVTTTVTVTTVTVVVTVVVTVVVVVVVVVVVVVVYVVVVVTTOLOLGLTTMTMOTLMOOLLMLMOMOMOMLOMTSTSVTSVSTSTSMMMOSMOMMMTMTMTMMOMMLOLLLMOMMOMTMMMOLLLOLOL/=2/2/2/=//2=////2///////////./../../././////=///=/=/=2=/==//=///./.././././....   . ..../////=2=F@F@GOOOGGOGOOGOGGOOOOQOQOQOQOQOQOOOOOOOOOOOOOOOOOOOLOOGOCGCGCGCGGAGAICGCGCGCGCGCGCGCGCGCGCGCOOGLOGOOOPPw~n¢££££œ£££££¥oommfkf}}}~~||vzuzvuwwuwwwBwwwBwwBwwBwww?w?w?ww?v?EEvEEEEwED>>=>F==F@F@F=F@F@F@F@FG@F@@F=@F@F@F@F@F@F@F@F@F@G@G@G@@G@F@@G@F@@F@F@@@F@@F@@@G@@@@=@=@=@=@=@==@====8===8==*=8=*===8==8=8/=8==8==8=@8@@@9C@@@:@@@@9@@@@9==8=8/8=8/*/*.(( .(/(//8===@=@CCCLOLOLMOMOMOMMTMTSTSVTSTSVTTSTTSTSTSTTSTVTSTTSTTTTVSVTVVVVVVTVVVVVTVVVTVVVTVVTVTVTTTVTTVTTVTVTVVVVVVVTVVVVVVVVVVVVVTTMOOLOLTTMTMOTLOOLOLOLOMOMTMOLMLOMOMTMSTSTSTSTTMTLOLMOLMMMOMMTMMMOMLMLOLLLMMMMTMTMMOLLOLL2/2/=2=/2///2/2///2/////.///././......//////=/=//2/=====/=/=/////./././/./../..    . .././/=//2=FF@GF@FGGFGF@FGGGGGGGGOGOGGOGOGGGGGGGGGGGGGG@G@G@GC@G@@G=@F@I>I?v?w>F@@F@@F@@F@F@F@@@G@@G@G@GAGINIww~¥œ£œ¢¢¢¢¢¢¢œ££¢oommmmk}}}}~~tttttvtuvuEuwuwuvuvuwuvuvuvuvuvuEvqvrErErEq6t6EqE66E6;5;D;/D=/==2===2==F==F==F=F/==2=2==2===F==F==F=F=F=F=F=F==F=F=F=F=====/==2==F/====/==/===/==/=/=*/=*//*/*.*/(*.*.*(/*(/*(/*/**/***/8/8*=8=8=8=8=8@8=8*=*=**/*(/*.*.((. (.(//*====@@@@CCCLCLLLLOLMOMOMMMTTMTTSTTSTVTVTTSTTTSTVTSVTVTSVTVTVTVVSVVVVVVVVVVVVVVVVTVVVTVVTTVTTTTTTSTTVTVTVVVTVVVVVTVVVVVVVVVVTTMOLOOTTTMTOMOMOLOLOLOLMOMOMTTMTLOLOLLTLMTTMTSTMTLLOCCCGLOLOMMOMTLMOMLLLMOLOLMTMMTTMMOMOL/2=2//2////2=/=/2///////////.../....././///=/=/2=/=/=/=/=/=/==/////././/././..   ......//2=/=2@FF@FFF@FGFFF@FGFGFGGGGGHGGGGFGGGGGG@GGFGGFGF@F@G@F@F@F=F>>II?vuvu~t~|~uF@G@F@F@F@@F>FAIBI?I?w?wwzwz|~}n¢œ¢™…—ƒ¢‚ƒ¢ƒ¢¢¢£¦ollmmm}}}}~{|tttttvtutvutvttvtutvutvtuvuvuvutvtutErt6tqtqqqEq666E6;6;D;;2=/2====2=F/F/=F====F==2==/2=/=2==F=2==F==2==2==F=F==F==2====2==F/==2=========/====/=/=////*///*//*/*/*//*/*//*/*/*/*/*/*/*8/8=8=8==@8=@=@8@==/*/*/*/*/*(/(((.(  .(./*/====@@@@CCCGLLLLOMLMOMLMOMTMTMTTMTSTTTSTTTSTTSTTMTTSTVTVTSVTVVSVVVVVVVVVTVVVVVVVVVVVTVVTVTVTTTTTTMTTTSTVTVTVVVTVVVVTVVVVVVTTMOOLOMTTMTOMTMOMOLOCLCGLOLOMMTMTMTMTMOLOLOLLLLMOMMOMOLLCC@@CCCCCLGMOLMLOMLMLMMOLOLOMMOTMTM/2//=/2=///2/=2=2///////////./../..././///==/=/=/=2==/2==/=////////////.//./..  ..././//=F=@F@G@GFGGGGGOGGOOGOOGOQOQQOQOQOQOQOOOTOOOOOOOOOOOOOCOGCOGAIBwwzz~||}~}~zGCGCGCAGIAIBIBwBwwwzwz|z|z~|~nœœ™—…ƒƒƒ‚‚‚‚¥ƒ¢ƒ¢£¥olmmm}}~}|z~z|w|wuvzzvzuzwuzvzuzvzzwzzwzzvzzvtvtuttvttvtvrvqvD>D>>F=@F=FF@F@F@F@FFF@F@F@F@F==F=F@FF@F@F@F=F=F=F@G@G@F@F@F@@F@F@F@F@F@FF@F@F@@@@@@@@=@@==@@=@=@=@=@=======@==@=8==@==9=@@=@=@=9@@@@CC@CC@CCCC@C@@=8/8===8==*=/*/*.   (../*====@@C@CLCCLOLOLMMMMTMMTMTMTMTTSTSTTTSTTTTSTTTMTMTMTTTSTVTVVSVTVTVVSVVVVVVVVVVVVYVVVTVTVTVVTTVTTTTTMTOTTTVTTVTVTVVTVVVVVVVVTTMOOLOMTMOTLTOMOMOMOLOCOLGLGLOLMOMTMMMMMMMLCLGLOLOLOLMLOLCC@@@@C@CCCCCLOLLMOMOMMMOLOLOLMOMTM/=2=2==2=2==2=/2==2=//2=/////.//.././//2/=2==/=2===/=/===2==/=////////=/////..  ....////=2=@F@FGGGCGGGGOGOGOOOOOQOOQTOTWOTQOTQTOTTQTOTOTOOOOOOOOOOLOPBwwzz||~~}}}}zGPGPBPIBPwBwwwzzzzz||||€~|~~n¢£™—…ƒƒ‚‚‚‚‚‚‚‚¢ƒ¢£¦olmmm}}~|||z|wzvzvztzzvzvzzvzzvzzwzwzwzwzzwuzztvt~t~t~ttttvtvEEEI>F>F>F>I>I>I>FI@F@GF@G@F@@F@F@F@F@F@F@FF@F@F@F@@G@GG@G@@@G@@G@@F@@FG@G@FC@GCGC@@C@@@@C@@@@@@@C@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@CCCCCCLCCLCCCCLCCC@==8/8==8=/8/*/*.  .(.*//==@@@CCCLGLOMOMMMTTTTSTVSTSTSVTVVSVTVVVVVSVVTVTSTTTTTSTVTVVSVTVVVVVVVVVVVVVSVTVVVVVVVVTVVTVVTVTVTTTTMTOTTTSTTVTTTVTVVTVTVTVVTTTOLOLMTTOMOOMOMOMOLLOLOCCLGLLOLOMOMMOMTMOMMOLLOCOCOLOCOCCCGC@C@@C@C@CC@LCCLLMOMTMMOMOLOLOLOM/////2//2=///2=//2//////////./.....././/2==2==/==2=/=/=/==/=///////////.//....  ...../=====F@G@FGF@GGGGGGGOOOGOOOOQOQOOQOOQOOOOOOOOOQOOOOOOOOOGGPBwwzz~~~}}nmn|IwBwIwzwzz~z||~|~|~~|~~}~}|¢£™…ƒƒƒ‚‚j‚j‚j‚‚¥ƒ¢™¢oommm}}|~|~|||t|~{|~{|~t|t||t||tz|zz|w|wztztzt~~~{~aaaaaaa~{~sttEtErvttttt~tvttvtvqvE?I?I=>F=@F@F=@F@F@F@F@F==F==F==F=F@@G@F@F@F@F@F@@F@@F@F@F@G@GCG@G@@@@@@@@@@@@@=@=@===========8=====8==@8@=@=9@@@CCCCCCCCCCCCCCC@@@/8/*/*/*/*/*.(  .(///==@@CCGCLLLOLOLMOMMMTMMTMTMTMTSTTSTSTTTTTTSTMTTTMTMTTTMTTSTTSTVTSVVSVVVSVVTVVVVVVVVVVVTVTVTVTVTVTTTOTTMTTTTTSTVTTVTTTVTVVTVTTOLOLTTTTMOMOOLOLOMOLLOLGCCCOCLOLOLMOMMOMTLMMOMOLLGLOCOCCCC@@C@@@@C@C@CC@CCCCOLMOMTMMOMLOCOLO//2////2=/2=//2=/=////=2///////...//./2/=2==2==F===2===2===//////.///.//.....   ..././//F==F==F=F@FF@FG@GFGGFGGGGGGGGGGGGGGGGGOGGGGGGGGGCG@GI?vzt~{}}fmnno¥¥¥n}vzvt|t|t~y~{~{~~}~}~}~}~}~}¥£™™ƒƒ‚‚‚jjjjj‚j‚ƒ˜—¢™ollmmmm}}}~}{{a}eee}aa{{{{{{{{{{}{{{{{~{~{~{{}afnnooo¥o¥oonnnfffeeeaea}aaa{a{{ytsttqvqE6ED;D==2==2=2=2=2/2/2//2/2/=2=F=F=F===2====F=F=F=F=F@F=F@@F==========/=/=/=/=====/8/8/=*=*=*=*=*=*/8/8/8/=8===8@=@=9=@=@8@=@==8/((.((.((.  ..(//===@@@CGCGLCLOLLOLMOMOMOMMMTMTMTTMTTMTTSTTMTMTMTTMTMTTMTTSVTVTSTVTVTVVVVSVTVTVSVVVVTVVTVTVTVTSTTTMTTOTOTTTTTVTSTTTTTTSTTTTOLOLOMTTTTTOOLOLOLOLOLOLCLGCCCGLCOLLOMOMMOMOMOMOMTLLOLGCCC@@@@@@@@@@@C@@C@@C@CCCGLOMOTMTOMOLOC///2=/2///=2/=/2//////////.//./.././/.//2=2===F==2==/===/==2=///////.//....   ( .////===F=2=F=FF=FF@FGFGFGFGFG@GFGFGFFGG@GGGG@GFGFG@GFCFG?v|t{{}fkmno¥¦££££¢{~y|~{~{{|{{}~}{}~}~}~}~}}o¢£—ƒƒƒ‚jjjjjjjj‚‚‚ƒ¢™£™¤oolmmmmff}efffkffffee}e}e}ee}}}}}}}}}ffno¥¢¢œœœœœœœœœœœ¢¢oonkfkfffeea}a{a{ysstsqqE6E;D>2==2=2=2=2/2=//2=2=/2=/=2=/==2===2==2=2===F=F=F=F=F=========/=/=///=/=*/=///*//*///8//8/*//8/*=/8/8===8@=@=9@=@=@@=@@====/*.*(.(.( (.///===@@CGCLCOLOLOLMOMMMMTMMOTMTTMTMTMTTMTTMTMTMTTMMTMOMTMTSTTSTTSTTSTVTSVTVTVTSVTVTVSVTVVTVTSTTTTTTMTOMOTMTTTTTSTVTVTTTTTTMOOLOOLTTTTMOMOOLOLOLOLGCGCGCGCGCCOCOLOLOMOLMOMOMMOMTMLOLCGC@@@@=@@@@@C@C@@@@@C@CC@CCCLOLMOMOLLO/2=//2//=2//2/=/=//////////.//./././2///=/=2========2====2=///////.//.....   ....///=2==2=F=F=F=FFG@GFG@GFG@GFGGFGGGGGGGGGGGGGGGGGGFGAGww|{}}fkmoo¢£££££œn}~{~{{}{}{}~}{}~}~}~}}~}}no£™¢ƒƒ‚‚jjjjhjjj‚‚‚¢ƒ™™¤ololoooommkmmkmllnmmmkkkfkkmmmmmkmkkfkkmfmmmnmo¥œœ££œ¢£¢£œ£œ£¢¥olmmmkffeeaeaa{{ssssqtqE6EE>D>2===2=/=2==F==2==2=2==/=2==F=F==F==F@FF@FF@F@F===@===@======/=/===/===/===/=*/=*=/8/=*=/8=*=*==8/=8===@9=@@@=@=@=@=@@8=/*.(.*.(. .(//=/=@@@@CCGLOLLOLOMOMTMOMTMTMTMTMTTSTTMTMTMTMTTMTMTMOTMTMTMTSTSTTSTVTSTTTSTVTSTTVTVSVTVTSTTVTVTSTTTTOMOMOOOTMTTTVTTTTSTTTOOCOGLOMTTTTMTOMOLOLOLLGCCGCCG@GCGCCCCOCOMOMOLMOMOLMMOMTMOLLC@@@=@=@=@@@@@C@@C@@C@CC@GCCCOLOMOLOL/=/2///2//=2=/2/2////////.///.//.//.//2/2=/=F==F==2===F====/////./////...  ...//==F@@F@@F@G@FG@GGGOGOOGOGOGOOOGOOOOOOGOOOOOOOOOPPPPIPww|}}mmo¥¢£££££™££n~}~}|~}|}~}|}~}~}|}}}}o¥£—ƒƒ‚‚jjjhjjjjj‚‚‚¢—£¦¤¤o¤o¥¦£¢¥ololooo¥o¥¥o¥ooiolollmmmmmmollolo¤o¤¦££££¢¢£¢££££œ£¢onmkfke}a}{{tytsvtvtvuvE?I>F>F=F@F@F@FF@F@FF@F=F=FF=F@F@F@F@G@GCGCGG@G@G@GCG@GCG@G@@C@@=@@@@@@@@@@@@=@@=======@==@@@@@@@=@@@@@@CCCCCC@CC@CCCCC@C@===/*//*/(.(.//==@@C@GLOLOMOMOMMOMTMTMTSTTSTTSTTSTSTTSTTTMTTTTTMTTMTMMOTTMTTTSTTSTTTSTTTTTTSTTTSTTTVTVTSTVTTSTTTTTTMOTOOMOMOTTTTTTVTSTTTOCOCOCOLTTTTTMOMOOLOLLG@CG@CG@CG@C@CGCCOCLOLLMOLLOLMOMOMMOMOLL@@@=====@=@@@@@@@@@@@@CC@CGCGCGLOCLO=2=//2=/=2==2====/=2=///////////2//2///=/=2==F==F===F==F===/////.///////..  ...//===@F@@FG@GGGCGGGOOOOOOOOOOOOOOOOQOOOOOOOTOOOOPPPPxIxzwz}mno¥¢££™£™š›££¢}~}|}~}|}~}~}|}|}}}lo¦‘£…¢‚‚‚jjjhhjhjj‚‚˜—™¦¤o¤¦¦££”¢¥o¤¥¦¢££££—¦¤¤¤o¤lo¤o¤¤o¤¤¤¤¤¤¦£ž££¢£¢££££¢¦¢¦¢¢££¢£œ£¢onfe}a}{{{{{{~ytt~tttztwv?II>F@FG@G@G@GF@G@F@F=F=F@F@G@GGCGGCOGGOCGGGG@GGCGCGCCGCG@G@CG@@@C@@@C@@@C@@@C@@@@@@@@@@@@@@C@@CCCCCCLCLLLCCCCCCCLCCC@@==*//*//*/(. .==@CCGLMOMOMTSTSTTSTSTVTVVVVVVSVTSVVTVVSVSVVSVTVSVTSTTSTSTVSTSVTSVTVSVTVTSTVTTSTTTTSTTTTTTVTTTTTTTMTTMTOMOOOOMOOTTSTTTTTTOCGLGOLOMTTMTMTOOMOLLGC@G@@GCGCG@@G@@@CGCCGLOLOLOLLOLOLMOMOLLOLLC@@@=@===@=@@@@@@@@C@@CC@GCCGCGCGCC/=2////2//2///2/////////.////././//////2=2/===2==2=2====2=//......././... ...//====F=@F@G@GFCGGGOGGOGOOGOGOOOOOOOOOOOOOGOPPPIPwwwzzzz}moo¢£££™™™š››››o}~}~}~}}}~}}~}}mmmmo¤£‘™¢ƒƒ‚jjjhjjjjj‚‚‚ƒ™‰ž™¦¤¦¡£ž£££¡—££›ž“‘››‘›‘‘ž£—¦¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¦£‘£¢™¦¢££££¦¥oooooo¥¥££¢¢¢£¢offe}a}{{{{{{{{{{{y~y~t~tttwuvE?I>IIFAFF@FF=F=F=F=F@F=F@@G@GCGCGC@GCG@GCGCGCGCG@@G@@@G@@@G@@@C@@@@@@@@@@@@=@=@@@@@@@@@@@@@@CCCCCCCLCC@@@@@@@@@==/*.(.(.*.(..   /=@@GCGLOLMOTMTTMTTMTTSTSTSTTSTTSTMTMTSTTTSTTMTTTTMTMMTMTMOSTTMTTSTSTMTTSTTMTTTSTTTTTTSTTTTTMTTTMTTTMOTOMOLOOOMOTOTTTTSOOCOCGLGLOTMTTOTMOMOLGC@G@@@G@CGCCG@@G@@@@@CCCLOLOLOLOLLOLOMLOLLOLCCC@@==@====@=@=@@@@@@@C@C@GCC@GCGC/2//2///////2=/2///////.//././/2/2//2=/////=2=====/=/=2=///... .../.... """!,4..////=/2==2=>F=FAFFGF@FGFGFAFIFGIGIFIIAIIBIIwvwwzw|z~~~mo¥££™—™™Ž››“£n}}~}}}}}}}}}mlommmlo¤¦£‘™…„‚‚‚jjjjhjj‚‚‚˜…™‘š¡¡šž›£‡ž‘žžžžž‘››‰š™‰£‘‘™¡¤¤¤  ¤    ¤¤¤™££™¢™¢™£££¢¥oioilllolo¥œ£¢¥¢œ¢offaaa{a{{{{{ay{{{{{{{y{{{stttEtEEEEEEEEEDD;DD>2>2=2=FF=F=F=@F=F@F@@F@F@F=F==@F@======F===F============/=8=/=8/=8/===8====@=@=@@@@@@==/*/*//*/(.   /==@@CGCGLOLOMOMLMTMTTSTTSTSTTMTMOTMTMTMMOMTMOTMTMTMOMTOMMOMMTTMTTSTSTTTTTMTMTTTMTTMTTTTTTTTTMTOTMOTTMOOLOLOLOOMOTTMTOOCGCGLGLGLOTMTTTOMOOLCF@@@@@CGCCGC@G@@@==@@@@GCCLCGLGCOCOLLOLLOLCOCLCC@C@========@==@@@@@@C@@C@GCCGCG/=/2///2//2////////2///.//.//.////2=/////=2==2=F==/=2////5.4 ,  .... "#$$$q64;////>2>DD>EEII?I?IIBI?II?IIIwI?IwIwIw?vwvvzvt~t~~|{}fm¥££™—™™‡‡‰ŽŽŽ›”¦na}{}}}fmf}}foo¥¥o¤oo¤¤¤¡›‘™™…ƒ‚jjjjjjjj‚‚ƒƒ™ž›¡†š›ž£Ž‡››‘žžž’›››Žš£‰‡™—™ž—¡   ¡  ¤ ¤¦¡‘£¢¢™¢™£££¢¤oiolilillhoo¥œ£¢¥¢£¥nffeaaaa{a{{yy`{y`{y{y{{{y~ststtqtEt6vqEqEqE6E6EEEEEE;E>D>D>>F=F=F=@=======F====F=====================/=/=*=/==8=====8======@=@@@=@==/*./(.(. /@@@GCGLOLMOMOTMOTMTMTSTTSTTMTTMTMTTTMTTMTMOTMMOMTMTMOMTOMTMMTMTTSTTMTTMTTMTMTTTMTTMTTTSTMTTTTMTMTOMOMOOLOOCOOOMOTOOLGCGCGCGCOLOMTMTOMOMCG@@@@F@G@CGCGCG@@@=====F@@@CGCCCCLGLLOLLOLCLGLCGCCC@CC@@======@=@=@@@@@G@@GCG@CGC/2/=/2=//////2/////////.///./.2/2=//2=/2//=/==2====2=/;564,-$$$#$,#, ,. "^^^^`pssq6;D;DED>>F=F@F@F@F@=F@@F===F=@=@================================@=@@=@@@@@@@==//(..  =@G@CLGLOOMOMTOMMTMTTSTSTSTTMTMTMTMTSTTMTTMMTOMTMOMTMOMMOMTMOMMTMTMTMTTMTMTTMTMTMMTMOTMTTTMTOTMOTMOMOOLOCOCOLOOMOOLGCGCG@GCGCOLOOMOTMOLGC@@=F@=@@GCG@G@@@========@@@@CG@GCLGLGLLCOCLCCLCCCCCC@C@@=@=========@=@@@@@CG@GC@=/=2///2/=2/////.///////.//.////=/2=///=/2=2=2=/F=F=D;56--q$$$$$$$$$$$$$$,#""""^^^^^^^`{y{{tE??I?Ew?wwwwwwwwwwwwwwzzzwwwwwwwxwwwzwzzz~z|z~||~mo£™™……………‡‡‰Ž›”¢n}mno¥omno¢£‘ž‘‘‘£š¡¡š›“‡™ƒƒ‚‚‚‚‚‚‚‚ƒ……™‘žŽš†šŒž‘š‡›‡›Ž››ŽŽ‡‡™……™…™‡‘‘š¡–¡ ¡ ¡¦‡‘£™¢¢™¢¢™££¥¤o¤o¤¤¥¤ooillol¥œ£¥¥¢œ¢offfeeee}aaaaa{{a{{{~{~ttttvvtEvvvvwvuvvzvzvzvtvzvuwuw?wIBIIAIGAG@GCG@GGC@GC@GC@G@G@GCCG@@C@@C@@@@@@@@C@C@C@CCC@CCCCCCCCCCCCCCCCCCC===//(.   ./@COMOMTMTTTTSTVTTTVSVTVTVTVSTVTSVTVSVTSTSTTMTMMOTMOMOMOMOMOMTMTMOTMTMTMTMTMOTMTOTMTTMOTMOTMTOTMOTMOOLOLGOCOGLOOLGCG@GCCG@C@GCGLOOMOLOCC@@F@=@@@G@G@C@@F====/=====@@@@CCCCOCOCGLCLGLGLCCGCC@C@@@@@@@==========@=@@@@CG@GC=F==2==/=2=/2/2//2=/////////2/=2=2====2=/===2===F==>D>E6qq`$`$`$^$^$^$$$$#$^"#"#^^^^^^^__aa}a}a|vI;EEqtsp``^`^`^^`^^^^^^^^^^""""^^^^]^_]___eeeefe}{t~vtzvzwwwzwwzwzz~~~|~zzzwwwzwwzwzzzz~~|~|~~}mo¦™‡……ƒƒ………‡ž™omno¦£žž¦oo¦£“‡‡‡‡‡‡‰›‘“ŒšŒ‘“‡™…ƒƒƒƒƒƒ………‡“‘šš†šš›ž‡™‰™‡‰Ž‘”žžž”ž‘Ž‡™…ƒƒƒ¢ƒ—…™‘‘›¡–¡ ¡¡›“£‡™™…¢—™£‘™¤¦¡£“‘ž£¦¤¤¤i¤¤¢£¢¢¥¢£¢omkfkfffffea}a}a}a}{{{{~tttEvuEvEEuv?v?vwEwuwEw?IBI?IBIIIAIAIIAGAGGCGCGCGCGCGCCG@GC@GCG@G@C@C@@C@@@@@C@CCCC@CCCCCCCCCCCCCCCCCCCLGCLC@==/.  ../=LOMTTTTTMTSTVTSVTVVSVVVTVSVSVTSTSVTSVTSTTSTTMTMTMTLMOLOLOMLMMOMMTMMOMTMOTMMTMOTMTMTOMOMOTOOMOMOMOMOOLOCGCCGC@@G@@CG@G@G@@G@@GCOLOCLG@@@@@F@@@@F@F======/=/==/===@@CCGCCGCGCCCCCGCCCCCCGCC@C@@@@@@@C@@===/=====@@@@@@C@/=2/////2////..5;5.;45;4;14;4;;2;D;2>/2;/2//=2=2>>DEEEuttss`^`^`^_^_^^^^^^^^^^^^^^^^^^^^^^]^]^]_]__b_cecfkkkffffa~{~ttvzvvztt~~~}~~~~~z~wzwwzww~z~~~~~}}}fmo™‘£………ƒƒƒƒ…‡›¦nfkmo¦£žžžž™¦¢‘ž›‡‡‰‡™‡™‡‰›ž›šš›“‘‡‡™…………………™‡“‘šš– ¡š›‘£‡‡™‡™‡›‘“ŽŒ››’“‡™„¢ƒƒƒƒƒ¢…™‘‘š¡– ¡š‘‘™™…¢ƒ…¢…œ—¡ ™ž££›£›šŸ¤¤¤i¤¢£¢¢¢œ£jnmkmkkkffffeeeaeaaaaa{{{yyttqqEq6EqEEqEEqEuEvEE?EEEIE>>I>I>I>I>>I>FAF@F@F@F@F@F@@@=@F@@=@=@===@@=@=@===@=@=@=@=@@@@@@@@@@=@@@@@@@@C@@@==/.  ../@GLOLOMOMOTMOMTTMTTTTMTTSTTTMTTMTMTTMTTMTMTMTMOMTOMOMOLOLOLOMMOMOMTMTMOTMTOMTMTMOTMOTOMOOMOOLOOOLOOLOCOCGCCF@F@FCF@@G@@G@@G@CGCGLGCCG@@F@==@F=========/==/=/=/=@FCG@CCGCC@GC@CCCCCCLGCCCC@@@@@@@C@@@@@@@==/=====@@@@@G/2//2/2/////.1;.5;45;45;4666666E6ED>ID>D>ID>D>D>D>>F==@====F@=F@F=F@@=@=@===@===@=============@=@=@@@=@@@@@@=@@@@@@@@@@=//   ../=COLOMOOMOMOTMOTTMTTMTTTMTSTMTMTSTTMTTMTTMTMOTMTMTOMOLOLLOLOLOMMOMOLOMMMOMTMOTMOTMOTLOTLOOMOLOLOLOLOLGCGCG@@F=@@F@@@@F@@F@@G@COCCCG@@@@@@=======F==2===//=//=/=@@@GCGCC@G@@@G@@C@CGCCCCCCC@@@@@@@@@@@@@@====/====@@@@@/////////2./.;45.6446466q-q6qqqqvqqEt6qEq666E6EEEEttttt~y{a{___c_c_bcbcbc]b]]]^]]]]]^]]^]^]^]]^]]]_b_bcbbcbkkkklhoŸ¢£œœœœœ¢¥nfa~~{~{}}a}}}non~~~~}{}}}}fkml¥™……ƒƒƒƒ……‡“™ollo—“ž‘›’“‘ž“Ž‡‡…ƒƒƒƒ……™‡›“Œššš›ž‘‘›‰›‘£¦Ÿ¤¤¤ ¤¡™™¢ƒ¢ƒ™™‘‘š¡–†šš‘‘¢„ƒ‚¥‚ƒ˜ƒ—™‘‘š–¡¡›‘™„ƒ˜‚¢‚¢™™¦ —ž£‘ž£—¤¤¤i¤¥£¢¢¢¢¢£¢ohlllkkmkkfkfefeeaeaea}{{yysqtqEqEqqqEqvuEvEuEE?IE>I>>I>DI>I>DI>I>I=AF@F@F@@@G@F@G@@G@G@@@@F@@@@@@@@@@@=@@=@@@@@@@@@@C@@@@@@@@@@@G@C@@@@=//   .../=GCOMOOMOTOMTOTMTTMTTTMTTMTTMTMTMTMTSTTMTMTMTMMOTMOLMOLOLLOLOMOLMOMOMOLOMOMOTLTMOTMOTLOMOLOLOCOLOLGLGLGCCG@@=@=F@@F@F@@F@@F@GCCGC@@@@@F=@==F======/==2==//=/===F@G@C@GC@C@@@@@@@C@CCCCCC@C@@@@@@@@@@@@@@@@==@=====@=@@=2=/2=/2///2.5/46464666q-qqqtqttqtttqtttqttqtqtttttt~{{{}a}aeeccccbcbcbbbbbcb]b]_c]]]]]]]]^]]_]]b]b_bbcbkbkkglgh¤¢œ£™¢¢¢¢œœœ¥na}}}no¢œo~}}~}}}mmo¦‘£‡……ƒƒƒƒ……‡‘‘¦¤oo¡žž››Ž›““”›‡‡…ƒƒ‚‚ƒƒƒ…‡™›“šš¡†š›‘ž‘‘“‘‘£¥¥i¤¤¤¤¤¤¡£™—¢ƒƒ¢„™‰‘™¡–¡–¡š‘‘‰¢ƒƒ‚‚‚‚ƒƒ™‰“£¡–—‘™—˜‚Ÿ‚‚¥‚¢¦ŸŸ¡£‘££¦Ÿ¤ioi¤¢£¢¦¢¢¢£¢ollmmmkmkkkffffef}e}a}a}{{~ttvqvqv6vqvEvuvwEBIw>IAIIIAIAIAIIAIAIAAGGGGCOCOGOLOLGLOLGLGCGCGCCGCGCCCCCCCCCCCCCCCCLLLLLOLLOCLGLOCOLLOLOLCC@@==.  ...//==GLOTTTTTTTTSTVTTVTVSVTVVSVTSVTSTVTSVTSVTSTTTMTTMTMOMOOLLOCLOLLOLOMLMOLMOMLOMOMOMOMOMOMOOLOLGLGLGLOCOCGCGC@F@F=@=@F@F@=F@=F@G@GCG@G@@@F@==F==/=2==/====/=////==@@@@@GC@G@@C@@@@@@@@C@CGCC@@@@@@=@@=@@=@@=@@@=@=======@===F=/2=/=2///;5;;6566666qqqtqttsttttttttttsttsttt{~{{}}a}efeffckbkkkbkgkdgdbdddghjjdc]]]]]_]_]b_bbbbbcbkbkkgkho¤¢£™¢¢—¢¢¥¢¢œœ¢nmmmnno¢£££o}}~mmoo¡‡……ƒƒƒ…ƒ‡‰‘‘¦¤¤¤£ž‘ŽŽŽŽŽ›‘‘Ž‡‡……ƒ‚‚‚‚‚ƒ……‡‘“›¡ ¡¡¡¡™š£¢¢¦¥¥oololo¤¤¤¤¦‡‘™¢ƒ¢ƒƒ¢ƒ™‘‘š¡–¡†¡š‘™„˜‚‚‚‚˜ƒ—™‘‘š¡š‘™„ƒ‚‚‚j‚‚˜™™Ÿ¤¥¦¦¦¦Ÿ¤oioio¥™¢¢¥¢¥¢œ¢olkmkmkkfmfkeffeea}a}~{t~ttqvrvrvuvuv?wIwIBIIAPIAIPIAPINIPGAPGPPPOOOOMOOOOOOOOLOOLOLGCOCLOLOLOLOLOCOCGLCOLOLOLOMLOLLOMLOLOLOMOLMOLGCC@@//  ..///=@FCTTSVVTVSVVVVVVVVVVYVVVVVVVVVVVVVVVVVVVVVUVSVTVTTVTTMTOMOLOMOMOMLOMOMOMLOMLOMOMOTMOMOMOOLOLGLGLGLGLGCCGCG@@F@F=F=@=@@F=@==@=@@G@@@F@@@@=====/==/==2===/=/=/===F@@@@@G@@C@@@@@@=@@@@C@@C@C@@@@@=@=@@@@=@@=@=@=@@=@===@=2=/2///2////1;5;46646q6qqqqqtqstsststttstststs{t{{{}a}a}feffkkkkkgkggggggggggj¥Šœjbb_b]cb]bcbbbbcbbkbkkgkglij™œ™¢ƒƒ˜•ƒ˜Ÿ˜¢¢œonfmmmmmnoo££œœ£onmmmmo¤™‘‡…ƒƒƒƒƒ…‡“£Ÿ¤¤¦›ž’ŒŽŽŽŽŽŽŽ‡‰…ƒƒ‚‚‚‚‚ƒ…™‡‘š¡ ¤¤¤¤¤o¤oooololol¤o¤¤¤¤ ™ž™™¢ƒƒŸƒ—™‘š–† †š‘‰…ƒƒƒ‚‚‚ƒ™…“›š‡“‡¢ƒ‚‚j‚‚‚Ÿ˜£—Ÿ¤Ÿ¤¤i¤iiioŸ™™¢¥¢¥¥¢œ¢hnkkkkkfmffkefffeffefee}a}{{{sttttttttvtvvuEv?I?IIAIAIAIAIAIPAIPIPNGPCOLOLOLOLOLOLOLOCCGCCCGCCCOCOCLGLGCCCGCCCCOLOLOLLOLLOLLGLLOLLOLOCC@@@==/.  ../==@@GLTTTTSTTTTSTVVSVVVVSVVVSVTVSVTSTSTSTVTSTTSTTMTTSTMTOMOLOLLOCOLOLOLLOLOLOLOLOLOLOLOMOOMOLOOCGCOCGLGCGCCGCF@@===@=F@F==@F==F@F@==F=@F==F=====////=/=/=/=/=/===@=@=F@@@@G@G@@@@=@@@@@@C@@@@@@@=@=@==@==@=@=@=@==@@=@@@@=/2/////././..D446466-6q$qqsqsssstsysytytsstyt{y{{{{a}a}feffkffkkkgkglgligiiiiŸ™œœ¥hgdddddddbbbbbbkbdgkgkglij™œ¢ƒƒ¥ƒ‚ƒ‚ƒ˜Ÿƒ™££¤olmmmmooo¦££¢¢££¥nmmmmmmmmlo¤¤š‡……ƒƒƒ……‡‘‘šŸ¤¤¡žŽŽŽ‡Ž‡ŽŽŽ‡‡……‚‚‚‚‚ƒƒ…‡‰›™¤¤¤olololollololl¤i¤¤   ¡š—…¢ƒ˜ƒ¢„™‘‘šš†¡‹–š“…—ƒ‚‚‚ƒƒƒ™‡‘‘šŽ’‘‡…ƒƒ‚‚‚‚‚ƒƒ™‘‘—¡ŸŸ¤Ÿ¤Ÿ¥—œœ¢¥˜¥˜¥¢œ¥okkkfkfkfffffffkfffefefeaaaa{{y`ssssstsstsqtEqEEEEEE?DIE>I>IAI>IAAIAI@G@G@G@G@G@G@G@@G@@@@F@@@F@@@C@C@@C@@@@@@@@@@C@CGC@C@G@@@C@CG@C@@@@===///.  ..//=FCGLOMOMOMMTOTTMTTTTTTTSTTTSTTMTTTTMTTMTMTMTMMOTMOTMOMOMOLOLCOCLGLCLOLOLOLOLOCOLOLOLOLOOLOLOCOCCGCGCCCGCCG@@F@==F====@===@F=@==F======F=====/=/=///=//=///===F=@=@=@F@@@@@@@@@=@@=@@@@@@@@@@======@=@===@=@=@=@=@=@@@@@/=///.//2./2.;5446466q-qqqqsqsrssssystysytyy{~y{{{}a}}feeffkkkmkghggigiiiiiŸ™‘™™™•ji‚¥…˜‚jhhbdbgbgkgkgglij¦œ¢ƒ¥ƒ‚‚‚‚j‚‚ƒ¢…™‘‘™¡¤olooo¤¢£££¢¢££¥oooooolmmmllolo¤¤¡£‘……ƒƒƒƒ……‰‘“— ¤¡¦’’ŽŽ‡Ž‡Ž‡ŽŽ‘‡……ƒƒ‚‚ƒƒ…ˆ‡‰‘¡¤¤olololoi¥¤¥¥¦Ÿ¤¤     ¡ š‘™…ƒƒƒƒƒ™…‰‘“šš‹ ‹š‘‘‰…ƒƒ‚‚ƒƒ¢…™›“ŽŽ‘‘‡‰…ƒ‚‚‚‚‚ƒƒ…™‘“‘‡š™—™™‘™¢ƒ¥˜¥˜¥¢¢hkkkkkfkfkfkekefefffefeeaeaa{`{`ss`sssssstsqtqt6E6EEEE?EEI>I>IIAIIAIAAF@G@@G@@G@@G@@F@F@F@@=@F@@@@@G@G@@@@@@@@@@@@@G@@@C@@@G@@G@@CFC@@@====///.   .../==GCOMOMTOTOMTTMTTTTTSTTTSTTTTTTMTTTTMTTMTMTMOTMTMTOMTMOMOLLOLCOCLOCLLCLLLGLCLGLLOLOLOLOLOLOLGCGCCGCGCGCG@CF@=F=====F==@F=F=@=======F=======/=///////////=/=F==@====F@=@F@G@F@=@@=@@@=@@@@@=@==========@=@=@=@===@=@@@@G/2///./././.;5.6466-6$qqqqqsqsssssyys{sysyt{y{y{{}{}aeeffkkkkkkllgligiigii¤„‘™™‡—™œˆjiggkggglglgiiŸœ™ƒƒƒ‚‚j‚j‚‚‚‚ƒ……‰‘‘— ¤¤¤¤¥¢£££¢¢£££¦ ¤¤¤¦¢¥oolool¤¤¤¤¡‘‡…„ƒƒ……‡‘›¡¤¤ —›“Ž‡‡‡Ž““‘‰‡……ƒƒƒƒ……‡‡£“£¤¤o¤llo¤¥¢¢££“‘›¡¡  ¡ ¡ ¡š›‘‰™—¢ƒƒ¢ƒ™‡“‘š‹‹¡†›‘™„ƒƒ‚ƒƒƒ…‡“›Ž’“‡…ƒƒ‚‚‚‚‚‚ƒ……‰‘“‘“‘“‘‘‘™…¢ƒ¢ƒ¢¢ƒœ£¥olkkkkkfkfkeffeffekefeeeaeaaay{`ysssssstststqt6tEEEEuEv?I?IIAIAIAIAIAI@G@GG@GG@GG@G@@@@G@@G@@@@@GCG@GCG@GC@G@@@G@CG@CCG@GCG@GC@GCGC@G======///.. ...//=@GLOTOMMOTMOTTTTTSTTTTTTTSTTSTTTTSTTTMTMOMOTMOMTMMOTMOMOLOLOLLGLCOCOLGLGCOCOCOCLOLOLOLOLOCGLCGCGCCG@CG@G@G=F===/=============/=/====@@F@===//////////=/====@=F=====@=@@@@=@@=@=@@=@@=@@@@==============@=@===@==@=@@@@=/=2//////.2;5;44646-6-6qqqqsqstssys{syyys{y{{{{{a}aeeffkfkkkmlkhlghiiiiii•™‘£‡™‘‘‘‘‰™™‰œ‘ƒjigglggiiiij—‰¢ƒ‚Ÿ‚j‚jjjj‚‚‚ƒ…™›“›¡¡ ¦¦£££™£š£ž£¡¡¡¦££”¢¤o¤o¤¤¤¤¤ ¡›‘‰‡……ƒƒ………Ž“£¡¤¤¤¦›“Ž‡Ž‡‡‡Ž“‘“‰‡………„……‡‰‰›‘—¦¤¤lo¤¤™žžž‘ž‘£ž‘š¡¡ ¡¡ ¡–¡›‘—…ƒ¢ƒƒ—…™‡›”’š‹‹‹š‘‘……ƒ˜ƒƒƒ…™‡‘ž››“‡…ƒƒ‚‚‚‚‚‚‚ƒƒ……‡‰‰‰‰™…—ƒ…¢ƒ¢…¢™£Ÿolllmkmkkkfkfkffkefefeeaaaa{y{ytsttttttttttEtvqvuvuvw?wIBIPIPPPAPPPPPPGOCOLOOLOOLOLOOOCOLOCOOLOLOLMOLMOLOLOLOLOLOLLOLOLOLOLOLOOLOLOLC@@======///.   ( /.///==GCMTTTTVTTVVSVVVVVVVVVVVVVVVVVVVSVTVSVTTTTSTMTTMOTMOMOMLOLOLGLGLGLCCLCLCLGCCLCGLCOCOCOCOCOCGCCGCG@CG@CG@@@====/=2=======F==/==/==F@F@F@@F=//=///.//////==F==F========F@@@@=@=@=@=@=@=@@=@@=====/=/=======@=@====@==@@@=2=/////////D;5;46665656E6EqEqtqtttstyts~ty~y{{{}{}}efeffkfkkkkllgliiiiiii¤•‘‰‡‰™£‘‘™™‡™™…™‰‘‘—Ÿiiiiiiii¤¦™ƒƒ‚‚j‚jjjjjj‚‚ƒƒ…‰‘“šš¡Œ‘ž£™™š™£›‘›ššš›ž£ž£¦¤¤¤o¤¤¤¤¡¡›‘‡…ƒ…ƒ……‡“™¤¤ ¤¦›“Ž‡‡‡‰‡‘““‘‰‡‡‡ˆ‡…‰‡Ž›“£¦¤¤o¤¤¤¡›ž£›£š››ž£¡¡¡ ‹ š ¡š“‰™—ƒƒ—˜…‡‰››”’š‹‹š›‘‰™„ƒƒ¢„…™‡›“Ž›“‘‡…ƒ‚‚‚‚‚‚‚‚ƒƒƒ„ƒ…—……¢„ƒƒ¢ƒ¢ƒ—˜—™™‘™Ÿ¤iioilllmmkkmfkfkfkefefea}a{{y~tttvt|vttvtvtvuvEw?w?IwPwPPPPPPPPPPPPPOPOMOOMOTOMOOMOLOMOOMOMOOMOMOTMTMTOMTOMOMOMOMOTLMOMOMOTMOMTMOMOC@==///=/=/..    ...///==/=FCOTVVVVVVVVVVYVYYVYVYYVYVVVVVVYVVVVVVVVVVTSTTTTMTTTMTMMMOLLOLLOLOLOLOLOLLOLOCLOLLOLOLOLOCCGCGC@CG@GC@@G=F==//=/=/=2=====/==/=2=F@@@@@F@=/=/////.//.//=/=======/=/=/==@=F@@=F@==@=@==@=@======/==/8=======@==@====@=@@/=/////.////;4.6464-646656Eqqqqqtqssssssstsyyyy{{}a}eefckckkkkkklhlgigiiii•–£‘™™š™‰‰‡™‡™‡™‡‡™‘‘—–¤¤¤¤i¤iŸ‡™ƒ‚‚‚jjjjjhjjj‚‚ƒ…‡›“Žšš’“‡£‡£‡››ž›ŒŒžžž£ž£š¦¤¤¤¤¤¤  ¡›‘‰‡……„ƒ……‰›‘š¤¤¤¤¦›“Ž‡‡‡‡‡‡“‘‘‘›‰‰‡Ž‡Œ›”£¡¤¤o¤¤¤ ¡£ž›š£‡›‘‘š¡ ¡ ¡ ¡¡¡›‘£…™—¢ƒ…™™‡›žžŒ‹‹š›“™…™…„…™…‰Ž›”›Œ‘ž‡……ƒ‚‚‚‚‚‚‚‚‚ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ¢…¢™¦¤¤i¤iliollllmmmkkkfkfefeeeaa}{{t~tttttttvrtEtEEE?EI?IwBIBPwNPPPIPPPPPPPOOOLOOOLOOOLOOLOLOLOOOMOOLOMOLOMOMOLMOOLOLOLOLOOMOLOMOLOMOOLOCG=//(..(.(..     ...////=/==@OMTTTSTVTVVVVVVVVVSVVVVVVVVVSVVVVSVTTSTTMOMOMOMOLOLOOLOLGCCGCCLCLCOCLGCCGCGCGCGCOCCOCLGLCG@GCG@G@G@G@==//=///=2=/=2===/=/===@@F@F@F@==2//////./////=2====/=/////===F=@=@=@=@======@=@==@====/////*=======@=======@F///./...../.4;4464-4-64666q6q6qqtqssssststsysy{y{{}aaefeckkkkkkkdlghliiiii¤•‘‡‰‰‡™š™™‰‡™‰‡™‡‡‰›“—–––   •–£‘™„ƒ‚‚jjjjhjjjjjj‚ƒ……“›šŽž‘Ž‡‡‰‡›‡‘ž›Œžž›››”£¡ ¤¤¤¤¤ ¦¡›‘‡……ƒ……‡‡‘“—¤¤¤¤¦“Ž‡‰‡‡‰‡Ž‘‘£‘“›ŽŽŽŽ‘’”£—¤oo¤¤¤¤ ¡›ž›š‰š‰›ž¡– ¡ ¡  ¡£‘™…™ƒ™—™…™›‘ž›š‹ ‹Ž‘™‡™…™…™‡Ž›“‘›šŒ“‘‡…ƒƒ‚‚‚‚‚‚‚‚‚‚‚‚ƒ‚ƒ‚ƒƒƒ‚ƒƒ¢ƒ™—‰£Ÿ¤i¤l¤ililhlllllkmkkkkfkffeeeaa{{yysstsstsqtq6qEEEEEE?DI?wIIIBIBIBIBIAGAGG@GCGCGCGCGCCG@GCGCCGCCGCGCGCGCGCCGCGCGCGCGCGCCGCGCGCGCGCCGC@@/..        ....//.///==GMOMTMTTTMTTTTTTTTTTMTTTTSTTTTTMTTMTMOTLOMOLMOLOMOLLOLOCCOCLGLGLCLCCCCGCC@C@GCCCOCCOCCGCCG@G@G@@G@=F==/=//////=/==/=2===F@F@=@@@F@==2=////././/.//===/=/=///////====@==@=F@=========@=====/=///*.//======@===@==F@/.//./.././..44646--6-64666q6qqqqsqssssstssyyyy{{{a}eefccckbkdkdlkggggigii•–‡™‡™‡‰‰š™‰‡™‡™‡‰‡‘žŽ–––––––—‘™ƒƒ‚‚jjjjhjhhjjj‚‚ƒ…‡‘“ŽŒ“›Ž™‡™Ž›“›Œžž’›››ž›š¡¤¤¤¤ ¤ ¡›‘‡……………‡‰‘›¦¤¤¤¤¤™žŽŽ‡‡‡‡‰Ž‘‘‡›‘“‘›‘’““™¥¤o¤l¤¤¤  ¡›‘›‡£‡‰™£‘›¡¡¡   ¡ ¡¡‘‘™™—™¢…™™›“›š–¡–¡š‘‡™™…™—™‰‘‘‘šš¡†›“‘‡‡…ƒƒ‚‚‚‚‚‚j‚‚‚‚‚‚‚‚‚‚ƒƒƒ¢ƒ—¢™—¤¤ii¤lioilllllhkmkkkkfkffffeeeaaay{{`ysssssqttEqEEE;?I>I>?DI?w?wIBIBIBPIBGIPCPLOOOMTTTTTSTTTTVTSVTVSVTVSVVVVVVYVYYXYYYVVYVVVVVVVVVVVVVVVVVVVVTVTTMG@F=//..    ./F@GLOOTMTTOTTTTMTTTTTTTTSTVVVVVVYVVVVSVTTVTVTVTVTVTVTTTTTTTTMTTMTMOOOLGCGCGCC@GCCGCC@@@C@@@@@@@@@@@C@@@=============/=/=//////////=///////././/.////.....  . ......... . .. ....././././././////=/=//////.//./////////./////.//=/=///........... .,.4)4.,.4.4)4)44-64-66q6666q66q6q6qqs``___]cbbbbbdbbgbdbgbggggg•‰…¢……™…™‡“‡¡–‡ž‡……™…‡‡“›šŒžž›››››“ž›Œš’“‘›‡‰‡™‡›‘“›‹š‹›“‡……………‡‡‘““““ŽŽ‰‰‡‡‡‡Ž‘ž’›žž›ŽŽŽ‰‡‡………‡‡“£¦ommmmno““›ŽŽŽ›£‘¦nm}}|||z|zzuzw?z??wBzwz~nnnononoooooooonnnkfefeea``p-4*.(.(.(. (...( .( ..(....(...( .(. (...., , ,,,,-,,,,,    . .. ........./././//////2//2/=/==>>F>D?I?I>I>IAF@G@G@GCGGLOLOOOMOMOMOTLTOMOTMOTMOTTMTTTSTTTSTTTMTOTMTTTTTTSTTTTTMTOTMOOGC=//..  /=@GCOLOOOLOMOOLOOMOMOMOMOMOTMTTTSVTTTTTMTMTOTTMTTTMOTTMTOOMOOLOOLOLOCGCG@@@@F@@@@@@@C@@@@==@=@=@@@@=@=@====/=/====/==/=///////.///////////.//.//.//..... .  . ....... ( . .. ../.././././/.//////////////.//././///.//.//.////////=/.......... ...,...,...4...44446464646666666666q6qp``^^__]_bbbbbbbbbgbgbggggh•œ‰¢ƒ…¢……™™‘›—–š‘‘‡‰…‡™‰‡‰“‘Œš’ž’›’žžžž”ž›ŽŽ‡›ž‘‹‹‹‹›“›‡‰™………‰‡Ž’‘ŽŽ‡‡‰‡‰‡ŽŽ“ž›žž’›ŽŽŽ‡‡‡…‡ˆ‡›‘™¥mmfn¥žžŽ›Ž£om}zzzuzuwuw?z?w?w?w?www€~€w~w~z~~}}}aa}a}{aa{a_y_spp-4( (. (....( . (....../.....( ...(. (. . .  ' , , ,   ( ..... ..././././././////=/=/=2=F=F=F@@=F=F@F=@@@@@G@CGCCGCOLOLOLOMOMOMOMOMOOMOTOTOTOTTMTTTTTMTOTOTOTOMTOTTMTTOTOTMOTOLG@F//   .=FCGLOOLOMOOOMOMOOMOMOMOMOTMOTTTTTSTTTTTMTTMTOTTMOTTTOTOTLOOLOLOLOLGCCG@G@@@@@@@@@@@@@@@@=@=@=@=@=@@@=====/==/=/===/==/////.//././/.//././.../(/./..... . . . .... .. . . ......./../././/././/////////.//.///./////.//.//.////=//=.......... ............ ....)4.44)5;46;46;6666646-p`^^_]_]b]bbbbbbbbbbbbgbggj……ƒ¢……¢…‡‘‘‡––‡‘‘‡‡™‡…‡‰Ž›“ŽšŒ”ž›››’’žž’›Ž›Ž›Ž›Ž‘ž›‹‹š‹š“‘‡‡………‡‰ŽŽŽŽŽŽŽ‡‡‰‡Ž‡’žž’”’›ŽŽ‰‡‰‡‡‡‰Ž‘‘£¥m}}n¥”ž›‘¦n}|uzuwuwuw?w?w?w?BBB>ABIAIPIPGAIwIwvvtEqq6qqppqrprpp--...(...( .(....../../..(........( . ( .  . (      . ( .. .(...././././/././////=/====F=@=@F@@@@@@G@F@G@G@GCGCOCOLOMOTMOTTMTTTTTTMTTTTMTTTTTTTTTTTTTTMTOTTTTTTTTTTTTMTTOTMOOOC@=/..   ..=GOLOOMOOTOMOOTOMOTOMOMOTLTTMOTMTTTTTMTTOTMTTMOTTMOTTMTOTOMOOLOLGLCGCC@@F@@@F@@@@@@@@@=@@==@==@=@@=@=====///=//=/=//=/=//.///././././(../.(....../.. (. .  . .( .. ( . . . ..././/././././/.////////.//.//.////.///.//.//./////=//./././............/........//./..;/;.;;.;5;46464--$`^^^]_]b]]b]bbbbbbbbbgbgg˜ˆƒƒ¢……™‘™Ÿ•–—‘™‡‰…‰™‡Žž›šŒžž’›Ž›Ž›Ž››Ž›ŽŽ››’ž›š‹ ‹‹š›ž‡‡™‡‰‡‡ŽŽŽ›ŽŽŽ‰Ž‡Ž‡›Žžžžžž’žž”‘‡‡‰‰‡Ž‡›‘£o}}}}¢”ž’£om}|zzwBxzBBwBwBNwNPNPPLTMMTLLMOMOMLO@A==@=@=@=>=;/;;;;6);./*.//(/./..(/.//////=//=/////././/...(..(......(...(...( ..... (..wD././/.//.//.////=/==F=F@F@@@GCGCGCGOCGOCGLGLGLOLOLOMTTTTVTVVVVVVVVVVVVVVVVYVVYVYYYYYYYVVVVVVVVVVVVVVVVVVVVVVVTVTTOOCF=/..   . ./FCOTTTTTTTTTTSTVTVTTVTTTTTTSTVTVVVTVSVTVTSTTTVTSTVTTTTTTTMTOMOOMOOOLOGCGCG@@@@@@@@@@@@@@@@@@@@@@@@@@=@====////////=///////././././././(.(.....(... .. (  . ... . . . ...././././../././/.//.////.//.////.////././././//////=////////././/.//////.//.///////////////////;/;..44,-$$^^_]]]]b]b]]]b]bbbbbbggjŠ™¢ƒ—˜…œ‘™•¤¤•–‘‡™‡™‡‰‡š“›‹šžž›Ž›Ž›Ž››Ž››››‘ž“›šš¡‹ ¡‹šž‘‰‡‡‰…‡‰ŽŽŽ’’“‘Ž‡‡ŽŽŽ›“ž›’”ž“££‘‡‡‡ŽŽ›ž£n||||~¥£”¦n|€zNxzNxBxNxNxNPPMPMMTMTMMTMTMMMOLC@@@@@@@=@=====/=//=////*//////(//./////=====/=/=//=/////*./././(..(/...(..(...(....(...(Dw..€§x.///////////=/==/==F=@@@G@GCOCOCOLOLOLOLOLOLOMOOMTTTTTSVVYVYVYVYYYXYYXYYYXYYYYYXYY[YXYYYYYYVYVYYYVVVYYYYVYVYVYVTTMOG@=//.      ...///GOTVTVVTVTVVVVVVVVYVVVVVVSVVVVVVVVVVVVVVVVVVTVVVVVVVVVTTVTTTTTTMTMOOLOCGCGCG@G@GCGCGC@C@@C@@@@@@@@@@@@====/////////////(//./*././(/....(...(... ( . . . .  . . .. . . . .../././././.././/.////././/.//.//////.////././/.////////././/.../......./.......././..//..//.///./..... ,,#$^^^^^]]]]]]]]]]]bbbbbbghˆœƒ¢ƒ¢™™Ÿiiii•‰™‡™™‡™‘“šš†šžž’Ž›Ž›ŽŽ›‘’“““““‘šš¡   ¡ ¡š›“‡‰‡‰‡Ž‡ŽŽ“ž“ž‘‡‡ŽŽŽŽ’ž›¡™££¦¥¥£‘£‡Ž‰‡Ž‡›Žž£o}~|€||€}o£¢ozzNzNwBBwNBNNNOLLLMOMMMMMOMLMOLOL@@===========//=/=*////*//(//.*..(..*//////////I€w=//.*..;D.(.(...(.w€€€5.(.( (.. . €§D€§(.//.///////////=/===F@@G@GCGCCGLOCOLGLGLOLOLOLOOMOMTTTTSVVVVVVVXVYYVYVYVYYYYXYYYYYYYYXYYYVVVVVVVVTTTTSVVVVVVTTTOOCCF=/..     ...//=FMTTTTTSTTTTTVTVTVTTSTTTTTTSTTTSVTVTSTVTSTTTTSTTTSTTTTTMOTMTOMOOLOLGCG@@@@@@@@@@@@@@@@@@@@=@@@@@@=@====/////.*/..(/..*..(...(.*..(/.(..(. .(  . ( . . . . . . .. .../././.../...//.//././/./..//././//////.///.//.////=................................./...(..///./.... ##"#^^]]]]]]]]]]]bbbbhƒœ¢‚¢ˆ™jgggiii‰‘™™™™™™‰›‘™¡–¡¡Œ“‘›‰›™š£‘”‘‘£š¦¡¤ ¤ ¤   ¡š“‘š‰Ž‡ŽŽ““’Ž“‘›‡›ŽŽ›‘ž£¦¤¤oool¤¥£‘‡ŽŒ‰ŽŽŽ’o}~||||||~~nm||wBww?w?BIB@@CCCCCCCCLCCCCCCCCCC==///8////*/*./*.*..(.(.(.(.(.. . (. (....(/.(€§€...( E ( E§€~.  .  (€§§§ .§§Iv/./././././////=/=/=====F=@@F@@G@GCG@GCG@GCG@GCGCGLGLOLOMTTTTSTTTTTSTTSTTVSVVVVVVVVVVVTVTSTTTTTTTOOLGCGOLOOOMOCG@F=/...  ..././FOMTTOTTMOTTMTTTMTTOMOLOMOOMOTMOTMTTMOTOMMOTOTMTOMMOMOOMOLOLOCOCCGC@@@@F@=========@===================////./.(./(..(...(..(/..(..*..(..( .  . ( .  . . . . . . .../../../..../././../...././..//.////.////.////.//////........................ ....../......(..../(... """]]]]]]]]bbbjŠ™˜¢Š¢jgggdggi¢™™—¢—™™Ÿ¤ ¤ ¡™ž£—™™™™ž£š¦Ÿ¤¤¤¤¤¤¤¤¤ Ÿ¡›‘Ž™š‰ŽŽ‘ž›š‡‘‘šŽ›’“™¤olmmmmmmo™Ž‡ŽŽŽŽŽž£n}~|||z|zt|~|}}|wBu????IA@@@@CC@CCCLGLCCCCCGCCC==//*///*€w//.*./.(/.(/(..(.(. € ( (wE...v§€x§2(  4 .( 5€0 . (.€5 ( H . 5§§§x.///.///////=/=/==2===F==@F@@F@@G@@@G@G@G@G@GCCCGCGLGLOMOOMOMTMTOTTTMTTTTTTTTTTTTVTTTTVTTTTTTMTTOTLG@G@GCOLGCG@@=//..   ..../2/=FOMTTOTOTOMTTTMTTMOMOMOOLOLOMOMTOTMTTMOTOMTMOMOMOOTOLOLOOLOCOCGCCG@@@F===F====@====@===@======@=====////*..(...(../(..(...(..(...(..(...   ( . . . . . .. . . ........../.../..../....../...../.././///////./////./////................/........... .../.... .(. .( ""]]]]]]bh‚œœœ˜hdbbbbbgh¥Š¢¢¢¢™¢¤¤i¤¤¤¤¡™‘£¢™™™££“™ ¤¤¤¤¤¤¤¤¤¤¤ —žŽ™šž›—¡¡™ž£››‘“¢onmffml¥™ž£ŽŽŽŽŽ“£n~|zzzuwuzwuwwzz?wB??IA8@@@@C@CCCCLCLCCCCGLCCC@=///=/*/w/./*/./€€E..(.(v€ .4 ( 5~v4. (€§H.€  v§§D ( ,v§w. ( .§€ .§.(2H2w§H././///////=/=/======F==@@@F@G@G@GCGCCCGCGCOCGCOCOLOLOOMOTMTTTTTTTSTTTVTSVTVVVVVVVVVVVTVTVTTTTTTTTOOG@@F@@G@@F==/..   ...2///2=@OTTTOMOTOTMTTOTTMOMOOLOOLOOMOMOTOMTOTMOMOOMOMOMOOLOOLOCOCOCOCGCC@G@@=@=============@====@==========///(/(.(..(/.(...(..( ( .( ..(...    . . . . . . . . . ........................./...../././////.////.////.///////...././/.///./...../.....././//...  ]]]]]h˜˜jdbbb]bbbbdjœ˜¥‚¢œœ¥ihliii¤i¤Ÿ™£™¢¦™™™££“£¥¤i¤o¤io¤o¤¤¤¤¡ž££‘‘£Ÿ¤¤¤¦‘£›£‘¢om}}mm¥££‡›ŽŽ’“¢€zzzzwzBwBwNzPwNNPPCCLCCLLLCLLMMTMTMMTMMMTxxP@@=@@===wF/I/==/€>/=///§2€§x...;§€5..(.€€. §w...(..€1(..(wv..(D€v1(......Fx3/////////==/===F=F@FC@CCGCGCCOCOLOLOMOMOMTMMOMTMTMTTTTSTVVVVVVVVVVVYYYYXYYYYYYYYYXYYYYXYYYYYYYYYYYYVVOOGCG@@F@==....            ..///=2==F@GTTTVTTTVTVTVVTSTTTTMOMOMTMTTTTTTVTTTTTTTTTTTTTTMTOMOOLOOLOLOLOLGCGC@@@@=@===@=@@@=@=@@@=@=@========///(.(.(..(..(.. ( .( ... ( .(    . ( . . . . . .. . . . ... .. .. ... . . . .. ..........././/./////./////////.////////////////////////.//././//////....  ]]]ddb]]]]]]]]]chœ˜j¢œ¢¥hklklllilio¤¢££¢¢¦¢¦¢¢££¤olollolllloo¤¤¦¢£££¦ooloo¤£‘££¦o}}}}}}}mmo££›ŽŽ›ž¢€zxzNxBxNxNNPPPLPxxTLLLOLMOMTSTWSTST€xC@@@@@@x€w>===§€===/>§w.///§€3w...(€§§§ ..2/€v/..(...1(........../1/./...(..(/./////=/=/=====@@=C@G@GCGCOLGLGLLOLOMOMTMTTTTTTVTSTTTVTVSVVVVVVVYVYVYXY[Y[Y[[[[[[Y[Y[Y[Y[Y[[Y[Y[Y[Y[YYYYVTOCG@G=F///..         ...//2==F=F@F@OVVVVVVTVVVVVVVVTVTTTTTTTTTTSVTVTVVTVSVTVTSTTVTTTTMOMTOMOMOMOLOMOCGCG@@@@@@@@@@@@@@@@@@@@@@@@@@@@====///*..(..(..(...(..(.. . (.      .. . .. . .. . . . . . . . .. . . . ...../........././//.///////.///////.//....././////......./....../././/.... ]]]]]^]^]d˜¢˜œ˜hdfcckkklkllllh¥œ£¢¢¢¦¢£¢olllllllmlmllllooo¥oonmmmllo¦£¥n}}}~}|~}||}mn¥£££›Ž›ž¥€zNzxzNwzzNNPLLxOCLCx€TOMxSMMx§Zx@@@@@@@=€I§§€F=/*=§€HI>///w§Z€§/.(/w€5.( v€€3w3D ..(.(.........(...( (.(..(.(.*../(..(..(...(//.///=/=====F@@@G@GCGCOCGLGCOLOLOMOMOMTMTMTTTMTTTSVTTVTVSVVVVVVYYYYYXY[YY[YY[YYVVVVTYYYYYYYYYYYYYYYVTOGCF==/// .        ...//2====F=F@OTTTTTTTSTTTSTTTTMTMOOMOMOTOTTTMTTTTTMTOTMTTMOTMOOLOLOOLOLGLOLGCCG@@@F@======@@=@@=@=@==@==@======//*/.(.(.(....(.. (         . ( . . . . . .. . . . . . . . ..../............/.././//./////.////............../..  (.. .....(.././.. ]c˜˜˜jdc___eccckckkckknj¢œ£¢¢¥¢œ¢ommkmkmkmfkmkkkfkkfnfkkkmmloo¦¥ofa}{{{|~}|~|}~}}mo™‘££›o~zzBwBwBB§x9CCA§€PC@@€§KCCOOVZA=======/I§I(//.Iw./(D€15D€2.( . 1E5465EE555vEvvv~w~€~€€~€€~€~€~€~€~vvvvvvEEvDEDD/./.(...(..(/.//////==/======F@@=@@@@@@@@@@C@GCGCOLGLOLOLOLOLOMOMOTLTOMTOMTOTTSVVVVYVYYVYVVVTTOLOCOMTVVVVVVVVVVVTTTOCF=F/...    ..///2=2=/==2@GMTTOTOMOMTOMOMOOLOLOOLOLOLOOOOMOOLOOMOLOOLOOLOLGCGCCCG@GC@CGC@@@@@====================/===/=/=/=//*/..(.(......(          . ( . . . . . . . . . . . . . . ......./.............././///////.///..... ......./...  .. ../..././.... ^dhn_^^^^^^`__e_e_eecfn¥¢œœ¢¢œ¥nfefeee}e}ee}ee}efeeefkkkfffa{{y|y|y||t|~t|y}}fmo¦£ž£o~zwwBz?Bz§9C@P§O@@CO§§ZCCCCLOx§@=====/==F€€/.(///€€E..(....EE55v€v€~€~€~€€€~€~€~€~€€€€€€€€€€€€€€€€€~€~€~€~€~€€€€€€€€€€€€v€E5D;...(/.///=/=======@=@=@@@F@G@@@G@@@G@@CGCGLGCOCOLOLOLOLOMOOMOOMOOMOTOTTTTVTVVVVVVVVTTOC@G@GGOTTTVTTTVTTTTTOGCF=/..     .../2./2/==2==F==FOMOTTOTOOMOMOMOOLOLOLOLOOOLOMOOOMOMOOMOLOLOLOLGLGCGCG@C@CG@C@G@@@@========/=========/===/==/==//8///*..(.( (.( ( .         . . . . . . . . . . . . . . . .. ................ . .././/////./////............../...  ..../........(. ^^^"""#$p^`^``^`_`aeefn¥¢œœ¢oeaay{yy{{{{{aa{aa{a{}aeee}{{{{~ytt|tutu|ut|t|tz|{}mno¦¢£¦zwBzB?BAw§§PCCC§Z€xC@@Cx§TGCLCLO€xF==========8/==v€.*/.E;DDvww€w€€€€€€€€€€~€~w~€~~v~vvsvvsvs5s5v5v55555555555555q5svvvv~v€~€€€€€€€€€€€~€vD//////=/=/=====@=@@F@C@@@G@@@G@CGCGCCLOLOLOLOLOMOOMTOTMTTMOTOTMTTTTVTVVVYVYYVVVVTTGCF@G@GLTVVVVVTVTTTTOOC@F//..   ..//=/2==2==2=2==F@OTMOTOMOLOOMOMOOLOLOLOOLOOOOLOOOOOMOLOOMOLOLOLGCCG@C@G@@@G@@@@@@@@@====/===/=====/==/==/=/=/*=///*/.(.(..(.... (     ( . . . . . .. . . . . . .. .. ..............  ...///////.//.////./.../././///./..  .//.////.//... #$$$p^p`pp`p``aafnn¥of~srrurtrttyytyy|yy|rt|y|{|ru|uutwuw?z?wwzwzwzwzzzz|}}nnonxzPNPNPPT§YLLO\§ZPLLMTZYZ\TMMTMOLOC@@@@@@@@@@@@@GIPI€w€€€€€€€€€€€~wwvvvvv5DD121..1. . . . ......;..4...   . ..1/15DEvw€~€€€€€€€D/./=/=/===@=@G@G@GCGLOLOCOLOLOOLOMMTMTTMTTTSTVTTVTVVVVVVVVVVVVVVVYYYXY[Y[Y[[YYYYVTOOGCGGOMVYYYYYYYYYVVTOOCF=/..      .../=FF@@F@F=F=F@F=@FOTTVTTTTTTTMTTTTMOTOOMOOTMTTOTTMOTMOTMTOTMTOMOOLOLGCGCGCCCGCCGCGC@@@@@=@==@=@=@================///*.(.(.(..( (.       . . . . . . . . ... ... ............  . ././/.////.//./////////////////./.  ./////////./.. '!-$,-,-,)-4-6-qs~a~~q6<<;>>>>>>??I??w???wwu?wzzzz|zBNCCCCLCCCCCCCCCCC@CCCCCCCCGLCCGCOCOCOCPGxx€€€€€€€xxPPPG@F=/*/*./(/.*..(.(.(..( ( .         ( .    ( ( .(..(./.*/////////////=/==/=/=======@=@@@@CCCGCCGCCGLOLOMLMOMOLOLMOOMOMTMTTTSTTTTTVTVTVVVVVVVVTTOMOGCGLG@==2==F@@GCOTTOTOOCGCF=F///.    ..2/F@GCGGCF@F===FF@OOMTTTMOOTOOMOOOOLOOOLOOLOLOOOLOLGLGOLOLOOOLOCCGCG@@@@@@@@@@@@@=@@=@======/=/8/=/=//8///=*=///8//(/(.(.(..(.             . . . . . . . ...1..... ... ......  ......././././///.............. .. ....   ..(.    ( (,(4).;);.;;);;;;;;;;);;);;;;;;;+;;+;/>;==>=>>>=>>>E>?E???w???BBBBBN@CCCCCCCCCCC@C@@C@@@C@CCGCCCCLCLCGPxxx€€€€€xIII@=@=@F====*///(/(.*..(.(.(. ( (. ( (   ( ( (        (..( .(..(//////////////=//=/==/========@@=@@C@GCCCGCCCCOCOLOLOLOLOLOLOLOMOOTMTTTTTMTTTTTSTTTTTTMOOLGG@G@G@G@F=//2//F=F@GGOCOGCFF===//...      ./2=F@GGGGG@@FF=@@GLOTOTMTOTOMOOMOOMOOLGLOGLOOLOLOOLOGLCGLGLGLOCOCCGC@@@@@@@=@@@@@@=@======/=//=/8/=/=//*=////=*///*/(/((.( (..(           . . . . . 1 . 0..1...... .. . . .. . .. ....../././////............... .......    .    ( (.(..(.;(;./;.;.;.;.;(;/.;*;/)//+//*======8==>>>>>>?>???I??IAAPGCCCCCCCCCLCLCCCCCC@C@CCCCGCLCLCOLPxx€x€xxxxPG@@=@==@@@=@====///*/*//*///*/.(.(.( .. (    ..        (. (....(../.*//////=/=/=/==/==============@@@@@G@CGLCOCOLLMLOMOLMOMOLOLOMOMOTTTTTTSTVTVTVTVVVTTTTOOOCGGCGG@G@GG@F/////2=F@G@G@FF==2/2/....  ../=F@FCGGCGG@@F@GGOMOTTOTTMOTOMOOMOOLOOCOLGOLOOLGLOGLGCGCCOCGLCLGCCC@G@@@@@@=@=@@@@=@======/=/8/=//=*=////8/=//=/*/*.(.(.( (.(         . . . .. . . .0. . . . ......1.. . . . . . . ...........//.//.//.././././/......././../..      .(.((.*/**////*/*=*/*=/8>=*====8=8@@9=@@@CCCCC@ACAAGNGAPCPLLOMMMMLMMMLMMMLTLMLLLLLLOLLOLOMMOMTxZ€Z€ZxZTTTTMOLCCC@CCCCC@C@@@=@======@=@==@===////*./(.(.(.(.(.(..*.(..( ( .      ..(.(..(.(.(././/*//=/=//=/=========@=@=@@@@@CGCGLLOLMOMTMTSTTVSTSVTUTSTVTSTVTVSVVVVVYVYYVYVYYYVVVTTTOMOOOOOLOOOOLOG@F=2/=/=F@FG@F@==2//./.....    .../........../2=F@GGGCOOGGGLGOTTTVTTTTTTTTTTTTTMOOOMOOOMOOTOMOOMOOOLOOLOLOLOLOLOLGCCG@C@@@C@@C@@@@@@@===========/=====/=/=/=/=*//*.(.((.....             ... ... . . . . . . 1 1.1.1... . . . . .. ...........//.//////////////////.////.//////..    .((.(((.(*.*//*=8==8=/8==8====8=8=@@9@@@@C@CCCCCCCCLCLCCLLLLLLMMTMMTMMTMMTMTMTSMTMTMOMOMOMMMTMTWx€\€ZxYVTTSTTMTMOLCCCCCCCCCC@C@=@@@@@@@@C@@@@@@====///*/./*..(.(./*./.(.(.( (..(.( ( ( ( (..(///*/.*//.*/.////=*============@=@@@@@@C@@CGCCCOLOLMTMTTSTTVVSVVVYVVVVVVVVVVSVVYVYXYYXYYXYVVVTSTTMOOMOOOMOTOOMOOTOTOOCF==F/F==F=F@=2=2..........     ../1//2=2/2//./2/=F=FCGGGOLOOOOOMTTVVTVVVTVTVTVTVTTTTTTMTOTTTMTTTTOTMOOMOOMOMOMOMOMOLOLCOCCCCCGCCGCG@@@C@@@@@@=@=================/=/*/.(.(.(.(.(...           . ... ... . . . . . . .....0.. . . . . .............././/././/././/.///./....././../////..      ((.((.**/(/*/*/*/*/=8=8==*=8=8=8=9@@9@@@CCCCC@CCCCCLCLLLLMLMLMOMLMOMLMMMMOMMOMLOLLLOLTxx€Z€ZxUTSTMTMMMTMMOLCCCC@CC@@@@@@=@=@@@@C@@@@@@@=@===//*..*.(..(.(.(..(.(.(        ..(.(.(.(..(.(..(..(/*/////8/=/=========@@@@@@@C@CCCCCLOLOMMTMMTSTSVVSVVVSVSVTVSVVVVVVVVVVVVVVTTTOMOOOOMOOOMOTTTTTTTTTTOMOGCF===/2=2//2//.... .      .../1////2=/F///=2=F=F@G@GCG@GCOOMTTTTTTTTTTTTTTTTTTMOOOOLOOMOOMOOMOOLOLOLGCOLGLOLOCLCGCC@@@@@@@@@@@@@@=@============/==/=*=//=*//*/*.((.. (           .. .. .. . . 1 .0. 1.1... . . . .... ..............///./.... ........ .... ....../....     ( ( ((.(((/(*/(/*/*/*/*/***/*/*/*=*=*=*=8==8===9=@@@@@@@@CCCL@CCCCCCCCCCCCCLCCCCCCC@CPx€xxxTPMLLNLCCLCOCLGC@@@==@====@==/=============*==8//*.(..( ( (        ..(..(.(.(.(./(../(/.*/=//=/8/=/=========@=@=@@==@@@@@@CCGCGLGLOMOMMTMMMTMTMTMTTMTTTTTTMTTTMOMOCGGCGCGCGLGOMTTTTMTTOTMTOOOCF=F/2///...    ......1/./2==F=2===F=F=F@F=F@F@GGOMTOTOMOOLOMOMOLOOOLOLGCOCGLGOLGOCGCGCCG@C@C@CCCCC@@@@@@@@=@=@===========/==//*///=*//////*///*/*..(.(( (         . .. ... .. . . .. ...0. . . . .............../././/.//............ ... ... ...../.......    (( ((.((.(.*/(/*/*/*/*/*/*/8/8/*=**=*=8=8=8==8@=@@@=9@@CCCCCCCC@CCCCCCCCCCCCCCCC@P€xxxOLCLLCCCGCCOCLCOCCC@=====@===========@=@====/==/=*=//(.(.(.( (          ...(..(.(.(..(/(..(..(.//(//=/*=/==/=/====@=@=@=@@@@=@@=@@@C@GCCCCLCLOLOMOMOMOMOMOTMTTTTMTTTTMTTOMOOCG@@@G@GCOOOTMOTTOTTMOTOMOOOG=F=2/2...  ..1...2//=F=F@FF@F@FF=F=F=F=F@GGOMTOOOOMOOOOOMOOMOOLOCGGLGLGOCOCOCGCGC@C@G@@G@G@C@C@@@@@=@===========/==//=*==/=///*///*////*/*/(/((.((     . .. ... . . .0 . 1. 1. .0. . . . ... ....... ......./././/1//............ .... .. ../..........     (( ((( ((((.**.*(/*/*/**/8*/**/8*/8*=8/=8=8=@=@8@@@@@CCCCCCCCCCCCCCCCCCLCLCLCCGPxxPPGCCCOLGLOCLCOCCOCLCC@=@==@=@@@@=@=@=@@@@@======/8==/*/(..(.(... (      ((. (.(.(.(..(.(.(..(.*.(/.(//*////=*=/===/====@=@@@@@=@@@@@@@@CGCCGLOLOLMMOTMTTMTMTTSTTSVTVTVSTVTTSTTTMOLGGCG@GGLOTTTTTTTTTTTTTTTTTMOG@F==2..   ...././/2=F=GCGGGG@@F@@F===F=F@GGOMOTOOOOOLOOOOMOOOLGOCGCGCGLGCCOCCGCG@G@@@C@@C@C@@@@@@@=@==@=======/==/==/=/*=//=//*/////*//(//(..(.( (        . . ... ... . .1 .. 0. . . ... ..... .........././././2/.//././././/./..../.../../////////.....    ( (.(.(.(*.*//*/*=*==8=8/8=@8=@8@@@@9@@@C@CCCCCCLCLLLLMLMLMLMLLMLMLMMLMTMOMMTxTOMOLMOMMTMMMMMMMMTMMMOCC@@C@CCCCCOCCCLCCCCCCCC@@@@@@@@==/=*/=//(//(.(.(..(.( ( (   .(.(.*..(./(//(/(/./(/*./(..(//(///*/=/====/====@@@@C@CCCCCCCCLLLOLLMOMTMTMTSVVSVVVVYXVVVVYYXYYXYYYXYYYXYYYVVTMOOOLOMTTTVVVVVYVYVYVVVVVVTTTOOG@2/.    .2.2/2/2==FCGGLOOOOOOOGCF@F@F=F@GOTTTTTTTMTTTMTOTOMOOMOOOLOOLOLOLOLCOCGCGCGCCGCCGCCCCC@C@@@@@@@@@=@===@===========/=//=*/=///*/*/*.*.((.        . .. ....   .0 . . . . . . .. ... ...... ......./..//./././/.//.////////.//////////////////////....   (((.((((.((/*.*/*/8/==8====9=@@8@@@@@@9@@CCCCCCLCLLLLLLLMMMMMTMMMMMMMTMTMTSTMTMTTMTMMTMMTMTSTSTTMTTTMTMTMLCCCCCCCOLOMLMOMOLOLLOCCC@@@@@@@@===*===//////*/..(.(.(.(.(.(..((.*/*//*/*//*///*//*///*.//*///*////=/==========@@@@CCLCLGLLOLMOLTMMTMTTSTTSVTVVVVYXYXYYXYXY[Y[Y[[Y[Y[Y[Y[Y[YXYVVTMOOMOOTTTVYYYYYYYYYYYYYVVVTTTTOG==.     ..//2/2=F@@GOOMTTTTTTTOOOGG@F@F@GCTTTTTTTTTTTTTTMTTOTOTOLOMOOMOMOMOMOLLOLGLGLCOLCCOCCCCCCCCC@@@@C@@@@@@@@@=@@=@==========/=/=///*//*/(.(.((         . ... .   .0 . 0. . . .. ...... ............././/./2/./2/././././../.../..../../////////......   .(.((((.*.*/*/*/*=8=8=8==8=8@=9@@9@@C@CCCCCLCLLLLOLLMLMLMOLLMLOMLMMMMMMMOMOMOMOLMOSTTMMMMMOMMMMOCC@@@@CCCLLOLOLLOLLLCGCC@C@@@@@@==/*/=/*//=/*//(.*.(.(...(. ( ..(.((/.(.(/(/.*./.(.(..*.(.(./.*/*//*/=/8========@@@CCCGLCLCLOLOLOLOMMOSTMTTSTSVVVVVVVVVVVVVVVXYVYYXYYYXYYYXYVTTTOMOOOLOOMTTTVVVVVVVVVVVVTTTTOOOGG==.   ..2//=2=F=GCGOOOMOTOTMOOOCF@F@FF@GLOTTTTMTTOTMOOOLOOLOLOOGLGLOOLOLGLGCCCCCCGC@CC@CC@C@@@@@@@@@=@==@===========8=/=/8////*//*//*/*.*.(.(( (       . .. .    0. . . ....1..1 ................/.2/.///.///......... .. ..... .(.....(....(... .    ( ( (( (.(((/*/*/*(/**/*8/8/*8/8=*=8=8=8@8@@9@@@@C@CCCC@CCCCCCCCCCCCCCLCOCCLCCCCCGLOLLGLGCCLGCGC@======@@C@CCCGCCC@@@@@@====8==/*/(.(.(.*.(.(.(.( (      (.(.(.(.((.(.(.*.*./(/.(.(./.*./(/.*//8/==*===/=====@=@@@@@C@CCCCCCCCGLOCOLMOLMOLLLOLLOCLCOLCLOOMMTMTTTTVTSTTTOMOLG@@@@@@G@GGOOTMTTTTTTTOTOOOGGCG@F=2/.  ./2./2=F=G@GCGOLGOOOOOOG@F=2==2=@GOOLOOOOLOOLOCOCOCGCG@GCG@CGC@G@C@G@@@@@=@@@@@=@@@=@===========/8/=*=/=/8///*//*///*//(/.(/.(/(.(.(.      . . .    . . .....1........ ............./././//.2//2/......... . (..... ..(/.....(.. ( .  .(.(.(((.((.(/(**(/*/8/8/*=*=8=8*=8==8=@8@=@@@@C@9C@CCCCC@CC@@CCCCCLCCCCCCCCGCLCLCLCLCOCCCCCC@======@@CCGC@CCC@@C@C@@@@======*/(.*.((..(.(..(.( (.   (.(..(.((.( ((.(.*.*.(.(.*.(..*./*.*/*//8//*/(/*./*/====@@@@@@C@C@C@CCCCCLLOCLOCCGC@C@@@@@@@@@G@CGCGCGLOLOLOOLOCGC@@F@F@F@FCFCGLOOOTMOTOMOOGGCG@GF==F//..  .//2===@G@GGCGGLGOGOOOOO@F==2==2@GLOOOOOLOOCOGCGGCG@C@G@CG@C@G@@G@@@@@@@@@=@@@@=@=@========8/===*=/=*=/=/=//*/*/.*/.*./(/(.*.(/(.(.(      ..     ..1......1...1..1............./.2.2//.2//....... . (. .( ..( ...(../..( (     . ( (.(((.(*((.*.***/*8/*/8=*=8=8=8==9@=@9@@@C@C@C@CCCCCCCCCCC@CCCCCLCLCOCCCCCOLOLOCOLCOLGLC@====8=@@CCCCCCCCC@CC@@@@@=@=====*.*(.(.((/(.(.(.( ( (    .(.(.(.((.(.( (.(/(/(.(/(..(/.*./.*//(/*/(.(.(..(.((.(/*==@@@@CC@CGCCGCLOCLOLO@GC@@@@@@@@@@@@G@C@GC@GCGCOCOLOLGLCG@G@@F@@@G@GGOOMTTTTTTTTOOCG@F@F@F==2//.  2//F=F@GGCGCGCGOGGOGOOOOCFF=2==2@GOOLOOOLOOCGLGCG@G@G@F@G@G@@@G@@@@@@=@@@@=@=@=@=======8/===*==/8///*/*///=/*.*./(./*...*.(/(.*.(( (      .      . ......1...... . ............/.././/.2///2/////...........(...///////...    ( (( ((.((.*.**(*/8/8=8=8=8@@@9@@@C@CCCCCCCLLLLLLLLLLMLMMLMLLLMLLMLMOMMMMTMMTLMTMMMTMMMMTMLL@C@@CCCLMOMOLOMOLOLOLLOLCCCCCCC@==///*///*/*//*/.*./(..(.(.((..(..*./.*..(.((..(/(/(/.*.*.*./(/(/(/.(.(.(( ((.( ((.(.(/*/==@@CCOLCLOMLOMLOMLOLCCC@C@GCGCGCGCGCCOLOLOLMOMOMOOMOOMOLOCGCGCGGOMOTTTTVVVVVVVTTOOG@FF@=F@F==//..   .F=F@GGCOOOOOOOOOOMOMOTTTOG@F=F=FCOOMOTOTOMOOOLGCGGCGCCGCGCGCGCC@CGC@@@C@@@@C@@@@@@@@===@=====@========8==/=//*/(//(//*//(//*/(/(.(.       . .        . ..1../....1... .. ..............1/1///2/////////./.././//.////////////.     ( (.(.((.((.((/*.**/*=8/88=8=8@@@9@@CC:CCLCCLCLLLLLMLMLMLMMMMMMTMMMMMMMMMTMMTMTMTTMVSTSTMTSTTTMTMLCCCLCMTMTMMMMMOMMMMOMOLOLLOCLCC@===/=*=/*///*//*//*./*/*.(*.(..*///=///*/(.(.(.((.*/.*/*.*//*/*/./*.((.(.((.(.(.(.(.*.*//*===@@@CCCCCCLCGLCCCCGCGCGCCGCCGLOLOLOLOLOLOLOMOMOMOTMTMOMOMGGCOCOLOTTVVVVVYYVVVVVOO@F==F==F==2=2/..  ./F@GCOOOMOTTTOMOTOTOTTTTTTOOG@F@GOMTTTTTTTOTOOMOOLGCGCCGCGCOCGLGCCCGCCCCCCCCCCCCC@@@@@@@@@@@@@@@=========8===//8///*////=*//*//*/.(.(       .        .......1.2... .. ..1.........../..././1///2/=///.......(.....././..////(.    .(.((.(((.*(/**/8*=8=8=8@@8@9@9C@:CCCCCLLLLLLLLLLLMLMLMMMMOLMOLOMOMLMOMMMTMTMTMMTMMMMMOLCCCCLOMLOLOLOLOLOLOLLLLLLLCCCCC@@==/8//*/*/*/*/(/*..*..(.(.(.(.(/.*//(/.(.(( ( (.(.(( ((.(.((    ( ((.((.(.(.(.*//*=/========@=@=@@@@@@@@@@C@CGLCLOLLLOLGLOLOLOLOLOLOOLOLOOLGCGCG@GCOMOTTVTVVVVVTTOO@=2///.//2//....  ./=FCGCGOOOOOOOOOOOOOOOMOTOTOOG@G@GOOOTOMOOOLOLGCGCG@@G@@G@C@CGCCG@C@@@@@@@@@@@@@@@@==========8====8==/=*//*//8/(/.*.(.*.*.*/*/(/((.         . .          . 1.../..... .. ... .. ...........1/1//.2//......... . (...(....(.....    .   ((.(((.((*(/(*/*/*8*8=*=888=8=8@9@9@9C@C@@9CC@CCCCCCCLCCCCCCCCCCCCGLLOLOLLGLGLGCLC@@@@CCCCCCCCC@CCCCCCC@@@@@@@@@@@=/8/*.*.(.((.( (..(( (.(. ( (..(/(/.(.(        ( (..(.(.(.((.((/(//*/.///*=/=====@@@@GCGCLOLMMTOMMOMLOLCGCGCGCG@CGCG@@G@@==@F@F@GCOOOMOTOOTTOO@2//... .. ..  /=F@FCG@GCGOCGGCGCGCGGGGLGOOGG@G@GGLOGGLGCGCGCG@@@F@=F@=@=@=@@=@@@================8=/8/8/=*//*//*//*/(/*/*/*./(.(.(.(.(.(.(.*.(.(        .          . ....1.1.. .. .. ..1... ......./..../.2/./2/./.... . ( .(..(....../.(.     (.(((.(((/(/*((/(**=*/8*8=*=8=9=9=9@@9@C@@:@@C@C@CCCCCCCCCC@CCCCCCCCLGLLOLCCLCLCLGCCCCCCCCC@CC@@C@@C@C@C@C@@@@@@@@@===/*//(/(.(((.( ((.(....(..(./(..(.(  (     .(((/(/*/*///*///=/*///=/8/====@@CCGLOLLOMOMTMTTMTTTMTMTMTMTMOLOCOC@G@@F@=F======F@@G@GOOMOOOOLO@F/...  ./F@@G@GGGCGGCGGGGG@G@G@GGCGOOOGGOGOCOGCOCGCG@G@F@@=F=@F===@=F@@=@==@=======@=======*/=/*/=*//*//*/(/*./(/*/*//(.(.(( (.(.(.(.((         .           . . .. .0..0.. 1................./..2../2././..... ( .. ( ..(...(.......     (.((.(/(*(*/(*(/***/8*=*8888=8@@@9@@9C@C@CC@C@CCCCCCCCCCCCCCCCCCCLCOLOLLLGCOCOCCLCCCCCCCCCCC@C@C@C@CCC@C@C@@@@@@@@==/8//*/.*(.(((.(.(( (( (...(.(.(.(..( ( (    (/*/========@=@=@@@@@CC@@@C@CCLCLLOMLMOMMOMMTMTTMTSTSTSTTTTTSTVTSTTTMOOOLG@@F@F@F@@FGCGOLOOOTOMOOG@/...    ./=F@G@G@GCGGGCGGCGG@GF@F@GGGOGOOOCOGOCGGCGCG@CF@@F=@===@==@==@===@===============8=/8/=/*//*//*/*//(/(/*./(/(/*.((.(( (((.(.(.((                     . . . . 1. 1.....1........./...1/..2./.2//////........(..././/.///.//...   ((.(**(/*/**/*/*=*=8=88@@@9@CCCCLCLLLLLLLLLLLLLLLMLMLMMLMMMMOMOMMTMSVSTMMMMMMMTMMOMMOMMMOLLOLOLOLOLOLOLOLGLCOCLCCCC@@=======*=/*//*/*./(./(.(..(.*./(/.(.(.(..   (/*==@@@@@@@C@@@CCCCCLCLLLMOLLLOMMTMTSTSTVTVSVVVVVVYXVVYVVVVVVVYXYVYXYVVVVVTVVTTTMTTTTTTOTTTTVTVTTVTOO@2/..       .=F@GGCGGLGOOCOOOOOGGGCGG@F@GLOMOTOMOOOOOLOGCGGCG@CF@F@@@@@@@@@@@@@@@===@=@=@@@@@==@====/===*=/8//*///*//*/*//*//*/(.(.(..(.((.( ((                 1 .. . 1......1..../.1...../1/.2.////////./.......////////////.//.(   ( (   (.(**/*/88=8=88=8=8=8=@@9@@:@:CLLLLLLMLMLLMLMLMLMMMMMMMTMTMTMSTMSTMTVVSVSTTTSTTMTMTMTMTSTMMTLMOLOMLMMMOMOMOLLOLOLOLLCC@@@@=8@==8=/8/=*//*=*//*/*///(///(//.(.(..      ((/*==@9@@@CCCCCCCCLCLLLOMOMMTMTMTMTMTSSVSVVSVVVVYXYXYXYYXYXYXYXYXYYXYXYYXYVVVVVVVSVTVTTVTVTTTVTVTVVTVTTOO=/...      . . ./F@GGOLOOGLOOOOOOOOOOGGCG@GGCOOTTTTTTMTOOOLOLGLGCGC@G@G@G@G@G@CC@@@@@@@@@@@@@@@@@@@@=@=========8=/=8/=*/8//*=/8/=/*/.*(/(((.(.(.((.( (                  . 1 .1.../1./....2.../..2../.2/.2.///./....(...(.././//.///...(..  .((.**/*8**/*8/8/8=88=8=9@9C@:CLCLLLLLLLLLLLLLLLLMOMMLMMMMMMMTLTMSSVSTMMMMTMMMOMOMOMMOMTLLOLLLLLOLOLLLLCLCGLCCLCLCCC@@=@==8===*=*///*/*./*.(.(.(./(.*.(.(...    ((/*/*=*=@=@@@@C@CCCCCCCLLLMOLLMMOMMMTMTTSTSVSVVVVXYXYXYXYXYVXYVVVYXYYYYXYVVVVTSVTTTTTTSTTTTTTSTTTTTTTTOMOG@/.    ..//2/==F@GCGCGGCGCGCGLGGCGG@F@@F@@GOCOOOOOLOLGLGGCGC@@G=@@F@@@@@@@@@@@@@=====@=@=@=@======8==/*/8///*//*/*/*/*/*/*/*/*/(*.((( ((.(.(                       . . ...1....../1./...1.....1/./1//...... (.. ( ..(....(./......(   (.(((*/*/***/*/***/*8/8*=88=99=9@9@9@@9@@9C@@@CCCCCCCCCCCLLCLLLOLMOMOLOCOLCGCCCCCCCGLCCCCC@C@C@@C@CC@@C@@@@@@@@@@@@==8/8/*/*.*/(.*/(.*.(.(.(.(.( (. (.( ((    (.*.*.*.*/8/8=8=@=9=@=@@@@@@CCCLCOCLLCOCLLLLOMOMMMTMTTVTVSVVTVTSTSTSTTVSTSTTMTMOMOOMOLOOOOLOOLOLOOOMOOOOLOG@F=/.  ..2==2///=F=F==F@F@F=F@@F@@F@F==2=F=@GCGCG@G@G@@@F@@F@=======F===============/==/8===8=/8//*/*//(/*/*.*.*.*.(/(/(.*.(.(.((.((.                       .0 ..0...1./1.../..1../../1/./1./.2./.... ..(. (.....(./../...( .     (((.**/**/*8*8/8*8/8*8=888=9=9@@9@@@9@C@C9C@@CCCCC@CCCLCCOLCLLLMOLLOLLGLCLCCCC@CCCCCCGC@C@@C@C@C@C@C@@C@@@@C@@C@@===8//8/*//*/*/(/*./((.((..( .(.(.( ((     ..*/(/(*/*/*/8===8=@=@@@@@@@C@C@CCCCLCCCLCCLGLLOMOMOMMTMTSTTSTTMTMTMTMTMTTTMTMOMOOOLOLOOLOLOOLOOCOOOLOOOOOGCG@F=/.  ../=F@@F/2//===F==F=2==F/F==F=F====2==F@G@G@G@G@F@@F@=@F=================/====/==/8/==/8///*//*/*.*./*.*.*.**.*(/(.((*((.((.((                        . 1..1....../..2./../1/./1///1//./...... ...(....(./../......         .(*.***/**/8/8/8*8/888=8@8@9@=9@C@9@@:@@C@CC@CCCCCCCCCOLLLLOMOMMOLLOLLLOCCGCCGCGCCGCCC@CCC@C@@C@CC@C@@C@C@@@C@@@===8=/*/8///*/*/.*/(.(.(.(.( (.( ((.(     .(.*.*.*.*/(/8/=8==@8@@@@@@@C@CCLCLLLOLLLLLCLLLOLLMOMMTMTTVSVTVSTSTTTMTTTTSTTMTTMTOMOLOLOOOOLOOOLOLOOOMOOOLGGCF@F//   ./2@FG@@=/2/2//F/=///2///2/=====2==/=F=F@@G@@F@@@F@=F================/===/=/=/=*=/=*=/=*=/*//*/*./*.*/.*.*.*(.(.*(.(.((*.((.((                       0...1..1..1..1./.2/1/.2//.2/./1/.///./....../././////.///.....     ((/(**/*8=88=8=8=@8@8@@9CC:CCLCLCLLLLLCLLLLLLMLMLLMLMMTMMTMSTSVVSVTSTSTSMOMLOMLOLOMLOLOLOLLOLLOLOLOLOLLOLOLCLLCCCC@@@=@===========*=/=*//*.(.(..(.(.(.(.(      (.(.(/(/(*/*=8==@@@@@CCCCCLLLLMMMMMTMSSTSTMTSTMTSTSTVSVVVXYVXYXYYXYVXVVVVVVVVVVVSVTVTTMOTTTTTTSTTTTTTTTTTTTTOOOCF@F=/   .../F@GLGOGG@F=2=/F/F/==/2=/=/=2=F@F=F==F@FC@GCG@G@CF@@@F@F===@=@@=@==@===============8=====8=/=/=*/=*//8.**/*/*/*.**.*.*.*/(*.(((.                    ..1...1..1.....2./1//1//1//2.//2.//////////./////////=////....          (.( ((*=*=8=88==9=@9@@@9@:CCLCLCLLLLMLLLLLMLLLMLMMMMTMMTMTSSSVSVVVVVVUVVSVVTSMMTMOMTMMTMMOMMOLMLMOMMMOMMLOMLOLLMOLLLCC@@@@@@@@@@=@=========//*//*//(.(.(.(/(.(    ( (.((.(.( ( ((((/(*//*====@@@CCCCLCLMLMMMTMTSTSVSVVVSSVSVSVSVSVVVVXVYXYX[Y[YXYXYXYXYXYXYVVVVVVVSTTTTTSTTVTTVTVTTVTTVTTTOOOCG@=F/.     . ../2@GOOOLOGCG@F=2==2=2==/=2/=/==F@@F@F@F@F@G@GCCGCCGCG@@@F@@@@@@=@@@@@@==@===@======@=@=============8=/8//*/*/*/=*/*/*/*/*//*/(.(((.(                     .1..1...1....1..2./2/.2///1//1///////./.../././//////.//.....    (.(****/*=8=8=8=9@9=9@:C9CCCCCLLLCLLLLCLLLLLOLOLMOMMMMTMTMSTUSSVSVSTSSTMTLOMOMOLOMOLOMOLLOLOLLOLOLLOLLOLLLOLLLLC@@=@=@8=====8===8==8//*=*//(..(.(.(.( ((   ((..(    ((.(*/*/=8==@9@@CCCCCLLLOMLMMTSSTSVSVSVSTSVMSVSVSVVVUYYXYYXYXYXYXYVVVVVVVVVVVSTVTMTOMTTTTTTSTTTTTTTTMTOOOLG@F@==/.   ...//=FCOGGCOGCG@G=2/=2=//2=//////=2=F==F==/=F@=F@@F@@F@@F=========@==============8====8/===8=/8/=8/=*//8.*/*/*/**.*(.*.*.**.*.(((.(                    ...1..1. .1....1.2./2.//2//.2/.2/.....(....(..../../.......        ((.***/***/8*=8=88=8@8@99@@@9@@9@@@9@@@@CC@CC@CCCLCGLLLLLMOMOLOMOMOMOCCLCGCCCCCGCCCCCG@C@C@CCCC@CCC@CC@C@C@C@===*=8==*=*///*/*/*//*/(..(.(.(..(.(  (.(   .(.*/*/*/8/8==8=8@@@@@C@CCCLLLOMMMMTMTMMTMMOMMTMMTMSTSTSVTVVVVVTSTTTMTTMTMTMTOLOOCGGCOLOOLOOOLOOOLOOGLG@F@=2=2...  ..../=@G@@F@F@F@==2//.//.//.//./././/=//=////=/=F==F=====////===/==/=//////*////*////*=/*//*/*//*/*/*.**.(((.((.(.((.((((.((((.(( (                 . 1...1..1..1.1....2.2/1///1///1//./.. (. (...././.../.....( .       ((/(/(**/8*=*=88=8@8@@@9=@@@@9@@@@9@@@@@C@C@@CCCCCCLCLCLLMOLLOMOLMOLLLGCCCCGCGCCCGC@C@C@C@C@C@CC@CCC@CCCCC@@======/8=//=*//*//*/*/.*.*./(..(.(.(.(   ( (( (   ((.(/**/8/=*==8==8@=@@@@C@@CCCCLLLMOMMMMTMMOMMMOMMMTMTMTSTSTSTSTTSTTTMTMOTMTOMOMOCC@@G@GCOCOLOLOOOLGGCGG@@F=F////..  ......2=@GF@@@F@F=F=F==2.///.////.././////////.///=/==/==/=/////=/=/=/=/=/=///*///*////*//*//*/*//*/*/*.*(.(((.(((((.((.(((.(.((.((.                  .. 1...1.......1..1/.//.2/./2.//....( .. .(..././../..... .          (((**.***/8*8=8=8=9=9@@9@@9@@@@9@@@@@C@C@CC@@CCCLCLCLOLLOMLOLLLMOLMOLOLLGCCCGCCGCCGCGCGCG@CC@CCC@C@CCCGC@C@@====8===/8//=*//*/*./*/.*.*..(.(.(.(( (  (    (.((.**/*//8/8=====8@@@9C@CCCCCLLLMMMMTMTSTSTSTMTSTMTTSTSSTSTSVTVSVTSVTTTTSTMTTMOOLGCF@@F@GLOOLOOOLOOOOCG@F@==2///..   . ...../=F@F@F@=F@=F=F===//////////....//////./....///=//=///////////////*////*////*////*//*//*/*/*/(/*/(/((.(((.(.(((.((.((((.(((.               ....1...1.1.1..1...1/1//.2./././/.... ( .///////.//.....            ((.*.**8/8*=8=8=@9@@@CCCCCCCCCLCCCLLCLLLLLLLLLLLMLTMTMMMTSTSTSTSTSTSTSTMTMMTMMMOMLOMLOLOLOLLOLOLLOLOLOLOLMLCC@@@@@@@=@@======8/=/=/=*//////*//*.(.((.       (.((.((.(/*/*==8@@@CCCCCLLLLMLLMMMMTSTSVVUVXYXYXVVVVVVVUVVYVYXYVYXYXYYXYVVVVVVVVSVTTTLG@@F@@GCOTTTTTTTTTTOOCG@F=F==2/..  ..././2///=FCGGCG@CG@G@@G@G@F@F========///.///=////./...//=/////./*///====/=/=*/=/////*/=/=/8///8//=*=/*/*/*/**.*/(*(.(/*/(*.(/(((.(((.(                  . .1..1.......1....../.2./.2.././... ...///////////...  .    ( (   ( ( ((.((.(((.***=*=8=8=@8@9@@CCCLCLLLLLLLLLLLLLLLLLMLMMMOMMTMMTSTSTSVSVVSVSVTVSTSTSVTTSTOTMTMMTMMOMMLTMMTLMOLMLOMMMMOLCCCCCCC@C@@C@@@@@====8======/=/=//8//*//(.   ( (.(.(.*.(*//*/*(.*/(*/*===@@CCCLLLMLMMMMMMTMSTSVUVXXYXYXY[XXYXXXYXYXYXXXYXYXYY[XY[YXYXYVVVVVVVMOO@@F===F@GLTTTTTTVTTTOC@F@==2//...    ...//2//////=FCGCGCGGCGCGCG@G@G@G@F@@F=====/////=////.././..//////.(././=============/============8=/=8==8=*=*//*/*/**/*/**/*/(*/*(/*/*(.((                     . .....1.1.1...1../1../..........  ..//////.....  .          (.((((/**/*8=88=8=8@8@@CCC@CCCCLCCCCLLCLCLLLLOLLLOMMOMOMTMSTSSTSTSTTSTTMTSTMTMTMTLMOLOMOLMOMOLOMLOLLOLLLOLOLL@@@@@@@@@@@@@=@=====8=/==///*////*/*/*.(.((   (.(/(/*/*(/((.(.((.(/*/8@@CCCLCLLLMLLMMLMMTMSSVUVVXYXXYXYXYXYYXYXYXYYYXYXYXYVXYXYYXYVVVVVSVTOL@F===/=/==GCOTTTTTMOOOCF==/2...   . .../////////FCG@G@@G@G@@G@@@@@@F@F==@=====//.///.....(...(.. (. (..(..(////=/*////8////*//*//*//*=*/*/*/*/**/*(/(.*/(*.*.((.((.((.(.((( (               ..1.......1.........1...1....  .(..../...    (   ( (. ( (((((/(**/*8/*8/8=8=9@9@@@9@@@@@9@@9@@@@@C@@CCCCCCCCCLLOLOLOMOMLLOLLOLCOLOLOCOCGCCOCCGCCGCCCG@CG@C@CCCCC@==@==@=8====8===8=/8//*=/*//*.*.*.*./(.(/(.(  (.(/*//=*//*(.( (.(((/*==@8@@@@C@CCCCCCCLCLLOMMMTSSSTSVSVVUVSVVSVVSVSVSTVTSVTVVTVVTSTTMTMTMOLG==///././//=FCOOOOGLG@F=//.  ...........//F==F====F==F=F=======////////.....( . ...     .(/////*/*/.*.*//*//*/(/*/(.*/(.(/(.((.((((((.((.*.((( ((.               ...1.2.1....1...1.. ..... ..   .(/.... .  ( (  (( ( (   ((.((.***/*8/8*8/8=8==@9=@@@C@:@@@@@@@9@C@C@C@@CCGCC@CCLLLLLLOLLOLOLGLCLOLLCOLGLCCGCCGCG@GCCGCCCG@CG@C@CC@====@===@==8============*//*./(/.*./(/(/((.(.( .(//*===8=*/*. (.((/(==8=@@@@9@@@@C@CCCCCCCLLLOMMMTSTSSTSSTSTSSTSTSTSTSTSTTSTSTMTTTTMTMTOMOMO@=//./......//FCGLGOC@@==/..  .. ...........//@F==F================/=//////... (  .......     (.///*./*/./(/(/(/*.*.(.*(.(/(*.*.(/((.(.(((.(((((.((  (                . ..1....1....... .. .. .....   .....(.  .( (  ( (( (   (.((((/(*/8/8=*=8*==8@=@@9@@@@@@@C@@@@@9@C@@@C@CCCCCCCOCOLOLOMOMOMLOLOLOLLOLLOLGLGLOCCGCGCGCCCGCCCG@CC@@@@==@===@======@=@@@@===8==//*/.*.*/.(/(.(/(.(.(  .(/*=====/8/(((.((.((.(/*/8@=@=9@@@CCCCCCCLCLLLLMMMTSSTSSTSVSVVSVSVSVVVSVSVVSVTSVTVTVSTTSTTTTMTMOL@=///.//./////F@GOCG@G==./ .  ..../...../../...../=====F=2===/F=====/==/////////...  ( ../....     .(.//(//(//(.*.*/.*.*.(.((/((.((*.(*.(((.(((.(.(((..                    . ..1..1/..1..1.. . .. 1.. ./..(      ......  (.( ( . ( ((      (((.(((/**/*=8=8=8=8=@=@@@C@CCCCLCLLCLCCLCLLLLMLLMLOMOMMOMMTMTSTSSTSSTTSTMTMTMTTMTMMTMMTOMOMOMTLMOMOLOLOLOLCC@@@@@@@@@@@CCCCCCCCCCC@@@===//////*//*/(/.*.(.( (.(//*=/8/==/*.(.*.(.(/(/**==@@@CCCCLLLMMMMMMSMSSSVSVVXVXYXYXYXYXYXYXYXYXYXYXYXYYXYXYXYYVXYVVVVVVTVTTM@F=/=/=/=/=2==FCOOLGCF==//.  .../=====/////////.///F@FCF@@@F@F@@F@F@@@=@@=====/=/=///...(. .(//=////..  (  .(//////*//*//(/*/*.**/*/(/**/*(*/*(*.(*.((/(*.(.(((.( ( (                    . ...1..1...1. . .. ... .../=/.(     .....(  (.(.(.(.( (.(.(.(.(.( (  ( ((.(((.**/*/8=8=@@@9@C@@CCCCCCLCLLLLLLLLLLLLMMMMMMMTMMTMTTMTMSVSVSVTVVVSVVTSTVSTTSVTTSTTTTMTMTTTTMTTMMTMMOMLOCCC@CCCCC@CCLLLLLMLMLLLCL@C@@=====/=/*////*//*/(.(   (..(//*=====8=/*=/*/*/*/*/*/=@@CCCCCLLLMMTSTSVVSVUVUVUXVXYXYX[X[X[Y[X[Y[[X[YX[Y[Y[XY[YXYYXYYXYVYVVVVVVSTT@====/=2====F=F@GLGCF==//..  .../=F=F@G@==//////////F@G@CGCG@@@G@@G@@@@@@@@==========////....//=/====////.(...(...*/=/=/*//=*///*/*=*//**/**/*/8/8/*=*/**/(**/**.*.*.(.(( (((.(((                  . . . ..1....1.. . ....1......././(.        .( ( (. ( (.( ((  ( (.(((.*(/**/8=8==8@8@@@:@@CCCCCCCCCLCLCLLLLLOLOMOLMMOMLMMTMTMTMTSSTSTVTSTTSTTMTTMTMTMTMMTOMTMOMTOMOMOLMOLLC@@@@@@@@@C@CCLOCLOLGLCCC@C=@==/8///*///*.*.(.((.  (.(.(.*.*/=/*=/*/*/*/*(/*/8=@@@C@CCCLLOMLMTSSTSSSVSVUVUVXYXYXYVXYXYXYXYXYXYXYXYXYYXYXYXYYXYVVVVVSVTSTTTMO@/2=/=//2=/=2====F===/...  ./2=F=@@G@F=///.//////==F@@G@@F@=@F@@F@@==@======/=///=///./...///=//=////.(.(. ( .(/*//*/*.*/*/(/*.**/*/*(**/(*(/**(/*(.((.((.(((.(((. (( (                . . . ..1... 1 . .. ....1.../.(..(.      ...(.(..(..(( (      .((.(((/(*/8*==8=8@@=@@@9@@@@@@C@@@@@@@@C@CC@CCCCCGCGCCLCOCLOLOLOLLOLLOLLLOLOLOLOLOCGCOCOCOCCOCOCOCGCC@@=======@@@CCCCCCCCCC@@@@@====*//*/*/.(/(./(.(.  ( (./*//8/=/8/8/*/*/*.*/=8@@@@@@9@@@CCCLLLLLLOMLMMMMMTSSTSSSTSTSVSVVUVVVVVVTVVSVVVVVVTSTTTSTMTMOTLOLOCLF=//.//././/./.......  .///=F======/..... ..//==2======/=//=//=///=//*//*./(.//(/....(///.//././/..  (  .(.(.(.(.((((.((.(((.(.(((.(((.(((.((((.(((.(((.(. ( (                  . . . . 1 . . ...1...../...(...(    . (  ..(.( (..(.(.( (    .(((.*(*/*/8=8===@9@@@@@@@@C@@@C@@C@@C@C@CCC@CCCCCCCLGLLCOLCOLOLOLLOLOLOLLOLOLOLOLGLGLGCOCOLCOCOCCCC@@@@=@=@=@@C@CC@CCCC@CC@C@=@====/8//*.*/(//(./(.   . ( (.*//8/=/8//*/*/**/*/8@@=@@@=@@9@@@@CCCCLLLLLLLLMLMMMTMMTMTMTMSTSVSVSTSSTVSTTSVSTSTTTTSTMOTLOMOLOCLGCC@/......../......  ..//==2==F===//......///===/====/=///////=/////*./*./.(././/.././//.///.././(.   (./(.(.(.(.(((.((.((((/(((.(*(.(((.(.(.((.(.(((.(( (                     . . . . . .. .....1/2/..(..(..     . (..  .(.(..(.(.(..(..( ( ((  ((.(((.(***/8/=8==@@@@C@@C@@@@C@@C@@@C@C@C@CCCCCCCGCLGLLOCLGLLOLLLOLOLOLLOLLOLLOLOLLGLCLGCLGLGLCCGLGC@@@@@@@@CCCCC@CCCCCCCC@@@@@=======*//*///*//(.(   ..(//=*=/=*/=*/*//*/8=@=@@@9@@@@@@@CCCCLLLLLLLMMMMTMTSMTSSTSSSTSVSVSVVUVVVTSVVVTVVVVSVTTTTSOTMOMOLOLOLOCG/./../././... .  ../2=======F==///.//.=/===2=/=/=/=//////*//*///*./*...(./(//.///.///./.*././..( (.((.(((((.(((.(*.(/((.((.(.((.(((.(((.(.(((.(((.( ((                 . . ...1./////..(/.////(     (. (. (   ( (..(.(..(.(( ((.( ((.(.(.(.( (( (.((/*/*/*/8=8==@@@@CCCCLCLCLCCCLCCLLLLLLLLLLMLMOMMMMOMSTMMTMTTMTTMTTSTSTTSVTSTVSTTTTMTMTMOTMTMTMOMMLOLLLLLCLLLLLLLLLLLLLLGCCCC@C@CC@@@@@====/=///(.   ( (  (./*/8/=*=/=*/8/=*/*==@@@C@C@CCCCCCLLLMLMMTSTSVSVUVUVXYXVXVVXYXYXYXYXYXYXYXYYXYXYXYXYYXYVVVVVVSVTTTTTMTTMOL=/////////....  .//=F=@F@@G@@G@@=====@F@F@@=@@=@@=======/==/=====/==/=/////////=//=///////////(.  (.(*.*.(/(.*.*(/(*(*.*(/((((/(.*.((.((.(((.*.(/*/(/.((..(                       . .. ...2/2////////=/=/.     .( .(. .   .(.(..(...((.(.(.(..(.(.*.(.*.*.(.*.(/(*/*/*/*/8====@@@CCCLCLLLOLMOLOLLOLOMLMLMLLMOMTMTMTMTSTSTTTSTSTVTSVTVSVVTVVSVVVVVVVVTSTTTTSTTTTTMTTMTMMTMOSTMMTMMMOMMOMOMOLLLCCLCLCCLCCCC@@@@==//(.    .( ( (.(///=*/=/==8===*==8==@C@CCCLCLCLLLLLMLMMTMTSVUVUVUXYXXYXYXYXYXYXYXYXY[X[Y[X[Y[X[Y[YYXYXYYXYVVVVVTVSTVTTSTTTOL@=////////...   ..//=@F@@F@CGCCGCG@G@G@G@@@@@F@@@=@@=@=================/=///==/====/==////=/=/=//.(   (/(/*/**//**/***/***/***/**(*/(*/*.*.(/(/*/*/*///*/*.(.(.((.                . . ..1..2/2=2/.(..*//////.    ( ..( .(. ...( .(.*.(.(.(.(.(.((..(.(.(.(.(/(.(.(.(/(.*/*/*/8/=8=@@@CCCCCCLOLCOCLCLGLLLLLLLLLMOMLMOMMTMTMTMTMTTMTMTTSTTTTSTSTVSVVVSTTSTTMTMOTMMTOMMOMLTMOMMOMOMLOMOLMOLLLLCLC@CCCCCLCCCC@@@==/*/(       (//*//*//8/=*=//*=*/8=@@C@C@CCCLCLCLLLLOMLMMTSSSVSVUXVXYXXYXYXVVVXVVXYXYYXYYXYXYYXVYXYYYXYVVVSVTVSTTMTMTMTMOMOG=//././...   ...//===F@@F@@FC@G@@G@@@@@F@@==========/==/==/==/=/==/=/////*/////////=//////*////(  ((/*.(*.(((.((.(.*((.((.(.((.((.((((.((.*.(/(/(/(/(.(( (                 . ..////2=/.(..(.//(./.   ( .(..... . (.(.(.(.(./(.((.(.(..((.*.(.(.(.(.(.(.(.(((.((/*/(/*/8=@@=@@@CGCCG@CCCC@CC@CCC@@CCCCCCCCOCLOLOCGLGLGLOLLOOLMOLOMOMOMOMOTMOMOLOCOLGLGCOCGCGLCGLLOLCOLCLLCOCCLCCC@@@@@@CCCCC@@@==/*.(.     (/.//*//*=/*=/8/=*//8=@@@@@@@@@C@@@@C@@CCCCCLLLLMLMMMMTMSTSMTSTMSTMSTSTVUVVVUVVVVSVTSVTSTTTSMOTMMOOLOLOCOCOCOCCG==.. .   ...///=/=/==============/=/=/=////*./*/*.*/*.*/(/.*/(///(././(////*./.(..*././(...(.(((.(( ((((.((( ((.(((.(((.((.(.(.(/*/(/.*/(/.(( (            .              . . .1.2///2( (..(/././.(    .( .(...(.( .( .(..(/.(.(.(.(.(.(.(.(.(.((.((.((.((/(/(..((.*.**/*/*===@@@C@C@CCCC@CC@CCCCG@CC@CCCCCCCLCOLLLCOCGLCLOLOLOMOLLOMOLMOMTMTMOTLOLLOLLGLCOCCCCLGLLLOLLOLCOLOCLOCCCCCC@C@CC@GCC@==/*/(.(( (      (.*////*=/*/=*//8//8==@@@@@@@@C@@CC@C@C@C@@CCCCLCLLLLMLMMMMTMMTMMMMOMMTMSTSVSVTSTVTTSTTSTTTTMTMTOMOMOCGCGCGCGCG@CF=/  .....////=/=/=/==/========/=///////*//(/.*//(/(//(/(/./*././.(./.(/././.*./.(/.///(/(..((.(.((.(.((.((.(((.((.(.(.*(/(.*/.*/(//(/(.(.         ( . .               . ../././//.( .(/./.*./..     . (...(..(....(..(..(/.*.(..(.(.(.(.*.*(.(.(.(.(.*.(.*/(/(.*//(/*/*/8==@@@@@CGCG@GCCCC@CCCCC@CCCCCCCCCGLLOCOCLGLGLOLOLOLLOLOMLLOMMOTMTOMTMOMOLOLLGLCGLGLGLLGLLOLLOLLOLCLCOCLCCCCCC@CC@@==//*/.((.       (.(/.*//=/*/=*//*=/*==8@@@@@@C@@@C@CC@CCCCCCCCCCLLLLMMMMMTMTSTSTSTMTMTMTSTVVVVVVVVVSVVTVTTSTSTTTTMTMMOLLLGCLCGCGCGC@@=/.    . .././//////=/=/==========///=//////*/.(/(/*./*.*.*.(.*./*./././.(/./(//./(..(./(//(.(.(.((.(..(( ((.( ((.(.(.*(.(.(.*.*/(/*/*./(.( (           .  .            . .1....2/./.(./////////*..   ( ........(....(.. (..(./(/(..(.(.(.(./(.*..(.(/((.*/(//*///*//*/=/=8/====@@CCCCCLGLOCOLCLLLOLLLLLOMOMOMMOMMTMMMTMTMTMTTSTVTVSTSVTVSVVVVVVVVVVVTVTTMTMOMOMTMMTMTMMMTMLMOMMOMOMOLOLLOCOCOC@==////*//(.(       (..*///*/=/*//*/*/*=8=@@C@CCC@CLCLLCLLLLLMLLMMMMMMSSTSVSVSVUVUVXVXVUVVUVVXVYXXYXYXYXYYXYXYYYVVVVVVVVTSTTMMOOMOMOMOLOGC=/..     ...///======F==F==@=@@F@@F@@@============/=*/=*=/==/*=///=//8////////////////*./*///*//*.(.((.(.(((.((((.((.((((.(*./*//*/*./(/(//.*/*/*/./(        . . (            ...../1///2//////=/=/=/////(  . ..(..(......(....(...(.//(/./*//(..*//*////*/*//*///*//*=/=8/=/8=========@@C@CCLOLOMLMOMOMOMOMMOMOMMTMTMMTMTMTSTTSTTSTVTVVSVVVTVVVVVVVVVVVVVVXYVVVVSTTTTTTTSTTSTSTTSTTSTSTMTMMMMMMTMLMOLC@====/==//*.(....    ( ( (..*//=/==8=//*/=/*==8@CCCCLCCLLLLLLLMLMLTMMTMMTMSSVSVUVUVUXVXVXYXYXVVUVVUVYXX[Y[Y[Y[YX[YYYXYXYXYXYXVVVTTTTMTOMOMOMOOLOCG=/.   ...///===F=F@====@@@F@@@G@@@@@@@==========8==/===8====*==/8/===/=////=/=/=/=//=//*///=//*=/*  (.(((((((((.(((/(*/*.*/*/*//*///*/*/////=/*=//=*//.( (          ( . .        ....././/////.(/.////*////(/.   .(........(......(...(..(/..*.(.(..*..(/.*./(..*.*.(../*/*/*//*/=/8/===8/====@@@C@CCLOLGLLOLLOLLOLLLOMMOMOMMOMMTMMMTMTTMTSTTVTSTVSVSVVVSVVVVVVVVVVVVVTSTTMTMTMTMTMTSTMMMMOMOMLOMOLOMLOLOL@=/*////*//*.(( .   ( (..(///*//*//*//*/*/*==@@CCCCCCCCCCCCLLLOLLLOMLMMMTMMSSSSVSVSUVSVUVUVSVUSVVUVVVVXYXYXYXYYXYVVYVXVVVVVVVSTTMMOLOLLGLOCCGC@@=/   . .///=======F======@@@@@=@@========/=*=/=/8/*///*/8/=*=//*////*/////*/////*/(//.*/*./*/(/(((  ((.(((.(.((.*.*.*/(/(.*./*.*//*//*////*/(.           . ..               ........2./2.(..(/.(...(...(  ........./././........(../(/..*./(.(/(/(//.*/.*.(/.*.(.(.(./*/*/*.*//*///*/*=====@@@@CGCCCGCCGCCCCCCCCGLCCCCOCLGLCGLGLLLGLOMOMOMOMOMOMMOTMMTMTMTMMTTMMTOLOLOLOLOLMOMOLOLOLLGLCOCLOLLLLLCC@=*/(/(//*/.(.((   ((..(.(.(./*///*//*/=/8//*/*=/8@@@C@C@C@@@@@@@@C@CCCCCCLCLCLLLLLLMMMMMMMMMMMLMMMTMMMTMMTSTVTVSVTVTTVSTTSTTTSTMTMOMOLGCGC@G@@@G@@=F=/.  (..////////////////=/===/=/=//*/.(.(/(/(/(/(*.*.(/(.*.(/(.*./(/(/(..(.(.(../(./(..(.(/((.(. (((((.*(.((.(.*.*.(/(/.*.//(/*/////*.((            ( .                .. ......./..(...(..*../(. (   ......./.../././.../....(./.(/.*./.(//.*.(/.*../(/(/.(./(/(//(/(///=*=/8/=/8/====@=@@C@GCCCCGCCGCCCGCLGCLCGLCLOLCLGLLGLOLOLOMOMOMOMMOTMTMOTMTMTTMTTTTTMTMOLOLLOLOLMOLMOLOLLCCOCLLLLOLLGC@=/*.*/.*//((.( (   .(..(.*./.*///*./*//*//*//8/=*/8==@@C@CCC@C@@@C@C@CC@CCCCCCCLCLCLLLLLLLMLMLOLMOMOMOMOMOMMOMMTMTSTTSTSTTTMTTTMTTTMOMOOLGCGC@@@@@F==F==//.   ..././//////////////=/==/=////(/.(/(.((((.(/((.*.(.(.*.(.*..(./.(.(.(..(.*.(./(.(/.(.*.*(.(( .( (((.*(.*.(/(.*.*.*.*./*//.*//=//*/.(.        . . ..             . ......../.2.( .(...(/.(..(   . .././.././.././././.../..(/.*.*./*.*/./(//(//(///(./(.(/.*///*//*////=*//8/==8=@=@@@CCGCG@CCCGCCGCCCOCCGCCLGLLCGLLGLOCLOLMOMOMOMLOTMMOTMTTMTTTTMTTTSTTMTOMOLOLMOMOMOLLOLLGLLCLOCOLLOCC=/*/*/.*/*/(/(.(( (..(.*./////*////*//*./*//*=/8//*/8/=*=@@@CCCCC@C@C@@C@CCCCCCCCLLLLLLLLMLMLMMMMMMMLMMMMTMTMTSTTMTTSTVSVTVTSTSTTSTTTMTTTMMOLLOCGCG@@@@@@@==//.  ..././/////////////=/=//=////*./(.(.*(.*((/(.*.((*.((.*.*.*..(/(./(./(.(..(.(..(.(/(.(/.(((.(((.((.(/((.(.(.(/(/.*/(//(/*///*///*/.((          (              . . ........././/*.//./.(..(  ....//.//.//./.././/././.(/../././*/./*/.*/./*./*./*///*/.///*//=/============@@@@@@CCGLGLLGLOLLOLLOMMMTMMMMTMMTMMTMTTMTTSTVVTVTVSVVTVVVVVVVVVVVVVVVYVVVVVVTSTSTTSTMTSTSMMMOMOMMOMLMOLC@==/*/=*/=/*.(.(.*.*////////*==/=/=/*//.*//(/*///8/==*/8==8@@C@CCCLCLCCLCLLLLLLLMMMMMMSMSSMSTSVSVSVSVSVSVVSVVVVVVVUVVVVXYXYXYXYVVVVVVVVVVVVTVTTMTMOLLGLGCGC@@@=//.  ...////=/==/=/=//================/=*/*/*//*//*/8/8//*/8///*=/*///*///*/.*/.*./*///*./*/*.*/*.*(.( ((.*(/(/*.*.(/(/(/*./*.*//*///=/*///*..(     . . .              . . .........//////////////(..  . .././/.2/1//.2.././///./..//.*//////=/*//////*//=//////*/==========@=@=@@@@@@@CCCCCOLOLLOMOLOMTOOMMTTMTMTMTSTSTTMTTSTSVTVVVVVVVVVVVVXYYXYXYXYYXYXYYXYXYXYVVVSVVSVVSVSVTSTSTTSMTMTMMMLC========8//*//*//===================//*///*///8===8===8===@@CCLCLLLLLLLLMLLMLMMMMMMSTSSTUVSVUVUVXVUVXVUVUVYXVXYXYXYXYXYYXYYXYYXYXVVVVVVVVVVVVSTTTMTOLOLGLCGCG=@//  ...///=/================@=@=@======*==/=*/8=/8/=8=/8=*=/8/=8/==8/=//*//*=////*//*//*//*//*//**/**** (*.*/*/*//*/(/*///8///*//=*==/===////*.(     .           . ........2/1/.(/./.*.//.(..(   ....//2.//2/.2/././//./.././././///*///*//*//*/.//*////*////*///=*==========@=@@@@@@C@CCOLOCOLOOLOLOOMMOMOMTMMTMMMTMTTSTTSTVVSVSVTVSVVVVVVVYVYXYYXYYXYYYYVVVSVTSTTSTTSTMTMTMMMTMMOMOLC=@==8=/==*//*.(/*===8======/========*////*/(/*/=*=/=8=*=/8/8=@@CCCCCCLCLLLCLLLLLOMLMMMMMTSMSVSVSVUVSVUSVSSVUVSVVUVVVVVVVVXYXYXYYVVVSVTSVSVVSVTSTTMOMOLOCGCG@@@===/.   (.././/////=//////=/=========/=/=/8/**//*/*/*/*/*/*=*/8/*//*/*//*//*/*/*/*./(/(.*./*.*.*/(.*/(.(.((((.((.(.((.((.(*/*.*/*/*/*////*/=//*/.(.(       (              . . ..........(..(..(..(..  ..//./2/2/2/.2/././//././/././//////*////*/./*.//./*//.*//*//*/./*//===/=*===8====@=@@C@C@C@GCCGCGCGCGCGLGLGLLOCOLOLOLOLMOMTOMOMTMOMTMTTMTSTTTSTVTVTVSVSTTTMMTOMOMOMMOMMOLOLOLOLLCC@===*//*//*/*.(/*/==========/===/==/8//=/*/*/*//*/=8=/8=/8/8==@9@CCCCCCCCCCCC@CC@CCCCCCLCLCLLLMLMMMLMMLMMLMMMMOMMTMTMTSTSTTTTTSTTSTMTMMOMOMOMOTMOMOLOCCG@@@=====///.  0111110  (..././*/.//*//./*./(/.*/*/.*(.(.(.(((((.((.((.((.(.((.((.(.(/(/(.(.(.(.(.(.(.((.(.(.(.((.((.((.((.((*(.((.(((.*.*/(//*./*//=///*=///(..    . (                    .. . .......(..(...(. (  ..2/2/2//2/2/./2.2/2/./////.//////////*////*///.*///.////*//.*//*/=/=/=*=/=/======@@@@@G@GCGCGCCGCCCOCLCCOLCOLLGLOLLLOLOMTOMOMOMOMOMTMTTTTTMTTVSTVTVTVTTTSTTTMMMOMMTMMOMOMOLLLLCC@@==*/=*==*//*.(//=8==@=@==@====/=====/==//*//*//*=//=*=8/8=*=/8@@CCCCCCCCCCCCCCCCCCCCCCCCCLCLCLLLCLLLLLMOLMOLOMMOMMTMTTMTOTMTMTTTTMTMOMOLOLOLOMOMOLOLCGC@@@=@==/=/...  013FHHHHHH3310  (..././.*/../.*.///(//.*/(/.*(.((((.(.(((.((((.((*(/(((.((/(.(/(/.*.(.((.(.(.(.(..(.(*((.(((.((((.(.((*.((/(.*.*/*/*//*/=//*=////*..(.        .                     . ... .......(./(..(...(.  .//.2/2//2/2/1//.///1///////.////////*//////////////*///////*.///*=/===/=/======@=@@@@CC@G@CCG@CGCGCGCGLCOLLLGLLLOLOLMTOMTMTOMTMMTMTTMTTSTTTSTTVTSVTVTSTTTSTMOMTMMOMOMOLLLCGCC@@====/8=/=/8//(/*/===@@@@@@@======/=/=/==/8/=*//*//*=/8/=8/=*=8=@@CC@CCCCCCCCCCCCCCCCCCCCLCLCCLCLLLMLMLMLMMLMOMMMTMTOTTTTTTSTTTTTTTTTTTMOMOLLOLOLOLOLOLOCGC@@@====///...  12HHRRRRRRRRRRHH310 ....(././(/..//(/(/.*./(/*.*.((/(.(( ((.((.((.(.(.(/((*.((.(/(/(.(/(.(/(.(.((.(((((.(((.(((.(((  (((*.(((/(.*.*.*//*///*//=//=*///*.(.      ..                       . .. ......//*././//(...   ..2//2=2//2/2/2.2/2///2//////.//*//=///=/////*/////////=/==/=/=/====@==@=@==@@@@@@C@CCGLGLGLOLOOLOLOMOMOTMTMTMTMTTSTVTVVVVVTVVSVVVVVVVVVVVYVVYXYYYXYYXYYXVVVVVTSTVTMTMLOLOCCC@@@@========8//*//==@@@@@@C@@@@@=========/==/8/=*//8//*/8/=*==8==@@C@CCCCLCLCLLLLLMLMLLMLMMMLMLMMMMSSSSSVSVSVSTSTVSVVVVVVVVYVYYYYYYYYYVVVSVTMMOLTMTTMTTMTMOLOCGC@@@===//....12HHRRHHHHHHHHHHRRRH310  ..///////////*///=///*=//*=//*/*/*/*/*/*/*.*/(**/**/*/*/*/*/*/(/*///*/*.*.(.*.(.(.(.((.(/(*.(((.(.((.((( /(((/(.*.*(/*/(/*//////*//=///*//.((                          . . . .... ...//=///*///...(    ./2//2=2//2///2.///2//2/////////==///=/=====/==/========@=======@@@@@@@@@@@@@C@CCCCOLOLLOMOMOOMOTMTOTTTTTSVTTSTVSVVVVVVXVVVVVVVVXYXYXYXYXYXYXYY[X[Y[YXYYXYXYXVVVTSOMOLLCLCCC@C@@C@@@=@@===8==@=C@CCCLCLLCC@@@=======8========8=/8==8===@=@@9@CCLCLCLLLLLMLMLMMMMMMMMMMMMLMMMSTSSTSVVUVVUVVVVUVVVVYYYYYYYYYYZ[Z[Z[YYVVVVTTMMOOLTTTMTTMTMOLOCG@@@@==////2FHHRRH3H33333233HHHHRRH320 ..///=//=/===//*==/===/*=/=*=/8//*/*/*/*/*/***/*/*/*=/8=/=*/*/*=//8/8///*/*/*.*/(((.((.**(/**/**/((.(.*((.(.(  (.*/*.**/*/*//=*=//*=/=/===/=/=/*//.(            .        ... .. .........//*/./.*..(.     ...2.2=/22=2/2/2/2/2//2//////././////////=////=//=//=/=======/=/====@=@===@=@@@=@@@@@CCCGCGLOLOLOLOMOMOOMMTMTMTTMTTVTVSVVVVVVVVVVVVVVYVYYYYXYYYXYYYYXYYXYYYYXVVTSTLLCC@@C@@@@@@@=@@=@=@=8/=8/==@@=@@CCCCCCC@@==@===8====8==*==/*=/*/8/==8===@@@CCCCCCCCCLLCLLOLOLOMLMLLLLLCLLLMMTMSTSTSSVSVSSVTVSVVVYYYYZY\Z\Z\Z\Z[YYVVTSTTMOCCGCCOMOMOMOCGC@@@@===//2/2FHRRH333232232323233HHHRRH31  (..(.//*///////*///*//*/*.*//*/**/*/*/*.*/*/(*(/***/(/**/*/*/*/(/*/*/*/*/.(/((.((.((((.(((.((/(((/(((.(.(((  ((.*.*/*/*/*//*///*////*=//8///*..(             .               . . . .......*.(..(...(    (..2./2/2=22=2/2//2//2//2//1///.//////////=/=/=/=///=////=/=///=//8/======/===/=======@=@@@CGCG@GCGCOCGCGCOCOLGLLOMOLMOTMTMMTMMMTMTSTTSTTSTSTVSTVVVVVVTVVVSVSTTSMOC@@=@@=@=@=====@8=====/=/8/===@@@@CCCCC@@@@====8==/=/8=/=8=/=8=*===/8====8==@CCCCCCCCCCCCLCCLCLCLCC@CC@@8@@CCCLCLLLLLMOMMOMTMOTTTTVWYZZZ\Z\ZZZZZ\ZZTTTMOOLOC@====@GCGCGC@@====////./.2FHRHH3221323223223232333HHRHH10  .(..(..(.(.*..(.(.(/(.(.(((.(((.((.((((.(( (((.((((.*.((((.(.(.(.((.(((.(  .(((.(((.((( ((( .(( (.((.(*/(/*/(/*/*////=//*////*///(.          .               . . .. ......(..(..(..    . ..///2/2=2/2=2//2/=2//2/////.///.///////=///=/=/=////=/==/==/=///=/==/==/=/====/=====@=@=@@C@C@GCGCGCGCGLGLOLOLOLOLMOMOMTMOTMOTMTMTMTTTSTTTTSTVTVSVTVSVTVSVTVSTTLC@@@@=@=@===@=@=@=======8=====@=@@CCCGCCC@@=@@=@===8==/8==/8==/==8=====8===@@@CCCCCCCCCCLCCCCLCCLCCC@@9==@8@@CCCCCCLCOLOLOMOOTTTTWYZZZZ\ZZZZZZYZYZZZYTOTOLOLC@==//===@@@@@===///./...2FHRHH3232323333F33333323333HHHH30  ..( .( .(/./(./(.(.*(.(.(.((.((.(((.(( (( (.(((.((.(.(((.*.((.((.(*.(*(.( (((( (((.(((.((( (.(( .(. ((.*.*(//*/*//////*//=//=*=////*..       (  .              . . ...... ..*..(/.(.   .../.2/2//2=2=2/2/2/2/2/2/2/2///.//./////////=//=/=/=/=//=/==/==/=/=/====/==/======/=====@@=@@@G@GCCGCGCCGCOCOLOCOLOLOMOMTOTMTMTMTMTMTTMTTTTSTVTSVTVTVSVTVVSVVVVSTLG@@@=@==@=@@=@=@=@=@=========@@@@@CGLGLCG@@@@@@=@=@===8=/=8==========8==/===@CCCCCLCCCLCCCCLCCCLCCCC@@@8=9=@:CCLCLLLLOLMOTMTTTWYZZZZZ\ZZZZYZYYWYWYZZZZVTOMOLOCC@==///===@@@====///..2.FHRHHF3F33F3H3HH3HHH3H33322323HHH31.(..(. (..(..(/(..(.(.(.(.(((.(((.(((.(( ((( ((.(.(.(((.*(((.*((.((((.(((.(( ( ((.((.((.(.((.((.(( ((.(   ((.*.*/*.*//*//=/=/*//////8////(.(      . . (               . . . ......./(//...(..  ...//2//2/=2/2=2=2/=2=/2//2//2//././///////=///=/=/=====/=============@=@@=@@=@@@@=@@@@@@@C@CGCCCOLOLOMOOTOTTTTTTTTSVTTVTVVVVVVVVVVVVVVYVVVXYYVYYXYYXYYX[YXYXYXYXVOLCCC@C@@@@@C@@@@@@@@@@==@=@@@@G@GCOLOLGCC@@@@@@@@@@=@======8====@=====8===@@CCLLLLLLLCLCCLCCLLLLLLLCCC@CCCCLMMMMMTSTSTTTVVYYZYZ\Z\Z\ZZZZYZYZYZYZYYYZZ\YYVVTTMMOLC@===/==F@@@=@==//=/2FHRRHF3F3HH3HHHHHHHHHHHHH33323233HH31 ...*././.(///*/////*./(/(/(/(/(*/(/*/*/*/**(/**(/((/(((/(*(/*.*/*/(/*/(/(/*(/*.((.((((( (( ((.((.(((.((((.((.(.( (.( (( *(/(/(//*////=*////=/*//=///8/.(.    .. (           . .. . . . .. ....(..(/./..(..   ...///2/=2//2=2=2/2//2//2//2//2///////=//==/===============@@@@===@@@@@C@@C@@C@C@@@@C@CCCCCCCCOCOLOMTOTTTTSVTVTSVTVTVVVVVVVXYVVVXVYXVVXYXYYYXYXYX[Y[[Y[[Y[[[[[[YXVMOLGLCCCCLCCCCCCCCCC@@@@G@GG@GGGOOTMTOOOOCGCGCGLCGCC@@@=@@=@=@@@@@@@@@@@@@@@CLMLMOMMOMLLLLLLLLCLLLLCLCCCCCLMMMTSTUVTVVVVYYYZZ\Z\Z\ZZZYZYZYZYZYZYZYZYZZZZYYVTVTTMOLC@======/===/=/=//=FHRQHHFHHHHHHHHHHHRHRHHHHHHH3323233HHH0 .(/////////*.//=*/=//*/*/*/*/*/*/*/*//*/*/*=*/*/*(/**.***/**/**//*/*/**/*/*/*/**/*(.(.(.((.((.((.**.*(*/*.**.**.*.*( .((.(..(((./*//*///8/=======/=/==*/==//=*..(    ( . ( .   .......... .. ... .. ( . (     ./.2/2/2/2=/2//2=/2=2/2/2/2///2///////////=///=/===========@========@@@@=@@@@@@@@@@@G@C@C@C@GCGCOLOOMOOTOTTSTTTTSTVTSTVSVVVVVVVVVVVVVVVYXYXYYYXYYXYY[XY[XYY[YXYVLOLCCCC@C@@@@@C@C@@@G@G@G@GGGGOOOTTWTTOOOGGGCGGCGCC@@@@@===@=@=@@@@=@==8===@CLLOLMOMOLLCLCCC@@@C@C@CC@CCCCCLLMMTMTSTSTVVWYZZZ\ZZZZYZYYZYZYZYZYZYZYZYWYZ\ZZVTTTMOLOCC@==//////(..(.../2FRRQH2HFHHHHRHRHRRHRHRRHRHHHH3323123HH3 .(..(/(.(./(/*./*/*/*.((.(/(.*.*/*/*.*/*/((/(*(.(((.((.((.(((/(**(/((.*(/(((.(*(.((((((((.(((.(((.*.(.(.(((.(( ((. .(.((.(/*/*////*//=/*/////=*//=//*/.(     (           .. .... . .    ..//2/2/2=2/=2/=2/2//2//2//2////////////=//=/=/=//=/=//=/=====/=/=========/==@==/====@=====@=@=@@G@CGLGLOLOOLOLOLOLOMOMOTMMTMOMMTMMTMTMTTTTVSVVVVYVVVVVVVVVVVVLOCCGC@@@@@@@=@@@@@@G@@GGGGQOQWWWWZZZYZWWWWTQOOOGGCGCG@@@@@@=@=@=@=@@=======@@@CCLLGLCLCCCC@@=8=@=8@=@8=@8=@@:CCCLOLOTOTWWZZZZZZYZYYWZYWZZYZZZZYZWYWWWTWWYZZQOOLGC@@@@==./(. (    2HRHH3F3HHHHRHRRRRRRRRRRRHRHHHH3323113HH1 (.(.(...(..(.(.(.(.((((((.((((((.((((.((.((((.((.((.((( (((.((.((.((.((.(.(.((.((/(*.(((.(. (( .(.(.(/*./*/=/=/8//=//*=///=/*///*..(                 . . . .   ..//2///2=2/=2//2=2//2=/2/2/2///////////=/=/=/=/=/=/=/======/=/=/=====================@=@===@=@F@G@GCOCOOLOLOLOLOMOMOMTOMOTMOMTMOMTMTTMTSTTVTVVVVVVSVVVVVVVSTLCGCCCC@@@@=@@@=@FCGGGOQQWWWWZZZZZZZZZZZZZZZWWWWTQOOGGG@G@@@=@@@=@@=@=@===8@@@CCCLCLCLCLC@@===8==@=@8@@=9@@@C@CCGCOOOTWZZZZZZWYWWWWWYWZYWZZZWZWZWZWWWWWTWWZZWOOCCG@@@===/..   3RHH3F3HHHHRHRRRRRRRRRRRRRHRHHH3H31113HH30 (...(...( (.(.(.(( (..((.(.(.((.(.((.((.(( (.(.(.(( (((.(((((((( (.((((.(((/(/(*.*(.((( .((.(.(/(/*./*//=//=*/=///=*///=///*/(.(    (            . ... . .    ..//2=2//2=2/2/=2/2//2=2//=//////=///=///=/=/=/=/=//=/====/==/===/=====/======/====@========@@@G@G@GCGLGLOLOLOMOMOMOTMMTTMOTMMTMTMTTSTTTSVTVVVVVVSVVSVVVVSTLOCCCCCC@@@@=@=@@@GOQQWWWZZWZZWZZZZZZZZZZZZZZZZZZZZWWTQOGCG@F@@@@@=@=@===@=@@@@CCCCLCLCC@@@=8===8==@=@8@@@@9@CCGLOOTWYZZZZZWYWWWVWYZYZZWZZZZZZZZZZZYZWWWWTWWZZTGOC@@@=@=///(.(   2HRH32FHHHHRRRRRRRRRRRRRRRRRRRHHH333311HR31 (.(. .(( ((.((.((.((((.((.((.((.(( ((..(.(((.((( ((.(.((((((((((((.((.*((/(/(//(*.(.(   .(/(.(/(////*/=//=/*=///=/*/////*/.(.(                . . ..   .(   ..2/2=2=/2/2/2/=2/2=2///2/=////=//=//==/=/==/=/=======@=======@@=@@@=@@@C@@@@@@@C@@@C@C@@@@GCGCOOOOMOTTTTTTTSTTVTVVTVVVVVVVVVVVVVVVVXVYYXYX[Y[XYXYXYYXYXVVTMTLOMLOLCCC@C@@GOWWWZZZZWWWWWWYZYZYZYWZYWZWZWZZZZZZZZWWTQGCG@F@@@@@@@@@@C@C@CCLLOLOLLCC@@@@==8@@@@@@@CCCCLLLLMTTTZYZZZZYZVWYWYZYZZZZZZZZZZZZZZZZZZZZZYZWYWYZZWTOMOOCCC@@==//(.(   1FRHH323HHHRRRRRRWWRZRZRZRRRRRRRHHH332133HR1 (.(.((.(.((..(.*.*.(/(.*.*.*.*.*((.**(.(*.(*(*.((*.((((.(.((((((((((.((.((((((((((( .((.(((.(.(/(/*/(/*/(/(   ((/(/(/(//*/=/=*=//=/=*//=/=/8///*/*.(           . ... ....    ....   ...//2/2=/2//2=2//2//2=/==2=/==2==/==================@F@@@@@@@@@@@@C@@G@GC@@CC@CGCCGCGC@C@C@OLOLOMTOTTTTSTVVTVVVVVVVVVVVYVXVVVVXYXYXYXYX[Y[Y[[YYXYYXY[YXYUTMTSTMMMOLOLCCCOOTZZZZWWWWWWVYZYZYZWYWYWYWYWWYWWZWZZZZZZZWTQOOGCGCC@CCCCCCCCCCLLOLLLLGCC@@@@C@@@@C@CCCLCLLLLTMTVZYZZZZYZYWYWYZYZZYZZZZ\ZZ\Z\Z\Z\ZZZZZZZYZWYZZZZVTOMOLGCC@@==///((.  .2HRH323F3HHRRRRRWRZRZRZRZRZRWRRRRHHH33213HR30 .*.*.(/*./*/(/**/**/*/*/*/***/*/**/***/(**/(**/*(*/*/((((*/*.*/*(/*(*.*((.(((((((((((((.(.(/*/*/*/*///*/*/*/( (((.(///*/////8/=======/===/=/=//*///*/(.(         . . . .. ... .. .   .(..    ..//=2/=/2/=2/=2/2=/2/=/=//2=///=/=/====/==========F@@F=@=F@@@@@@@@@@@@@C@@@C@@C@@@@C@@@G@CCGLOOLOMOTOTTTTSTVTVVSVVVVVUVVVVVVVVYXYYXYYYX[Y[VXYVVVVXVVVVSTMMMMOLOLOLCCCCCOWZZWTWTWTWTWYZYWYWYWZWZWYWZWWWWVWWWYZZZZZZWTGGG@GC@C@C@@@@@@C@C@C@C@@@@@@@@@@9@@@@@CC@CCCLLOTVZZZZYYYWTWTWVWWYZYZZZZZZ\ZZZZ\ZZZ\Z\ZZZZZYWWWYZZWTOOLCCC@@==/*.(.( .HRHH233HHHRHRRRWWWWZRZZWZRZRWRRRRHHH33223HR30 (.( (.((.(.((.(.(.(/*/*(/(*(/((*.*.(*(.((/(((.*(.(((.((((((((((((.(((((((( ((((((.(((.*.*/*/*/(/(/(  ( ((/*./(/(///=/===/8///=*/=//=/*///*/((.            .         ..2/=2/2=2//2//=///=/=/=2=/=/=//2==/=/==/==/==/=/==========F=========@===========@========@@@CGCGGCGOCOOOOMOMTMOTTMTMTMTMTMTMTSTSTVSVVVVVVSTLMOMTMTMMMOLOLGLCOCCC@@@@@GLWZZTQOQTWTZYZWWZWZWZZYWZZWZWYWWWWWVWWWWZWZZWWOGG@G@@@@@=@@=@==@=@=@@@@=@8==8====8===9==8@@@CQZZWVWOQOQTOWQWWWWWZZZZZZZZZZ\ZZZ\ZZZZZZZZWWWWWWZZWOGC@@@==//(.    1HRH323F3HHRRRRWRZRZZZZRZZZRZRWRRRRHHH33133RH0  (.((((((.(((.*(.(((.((.((( ((.(((.( ((((((((..(((.(*.*.*//(/*/**.((  .(.*/.*.(/*//==/==/=/*/=//=*/=///*//(.(                        ( ...2/2=/2/=/=2/2=//2=/2//=/=/2/=/=/==2===2===/==/==F=========@==============@===========@=@@@@CGCCGCGGGLOOOMOTOMTMTTMOMTMOTMTTSTTSTVSVVVVSTMOLMOMTTTTMMOLLOLCLCCCCCCCCCOQZWWTQWVWYZYWYZYZZZZYZZZZZYZWZYWZWWWWTWWWWZZWWQOG@G@@@@@=@==@==@=@==@=@==@@=9===8====8@=8=@@OWZWOOOOTQQTQWWTWZWZZZZZZZZZZZZ\ZZZ\ZZZZZZWZWWTWWZWQCG@F@==*.  2RHH133HHHRRRRRWZRZZRZZZZRZZRZRRRRRHH3H321HRH1  (.(..(.((.(*.(((.(((.(((.((.(( ((((.(((((((((((.(((.(((/(/*//*//*/.(( (.(.*//.(.//*/===/==/=//8//=//8/=/*//(.(                     . . . ...//2////2/2=//=/=/=/=/=/2=//=/==2===2====2===2=====F=======@================@========@=@=@G@CGCGCOGOOOOMOTMTTTTTTMTTMTTMTSTSTVTSVVVVVVSOLOLOMTMTSTMTLLOLOLGLCCCCCGCCOOWZZTWVWYZYZYZZWZZZZZZZZZZZZZZZZZYZWYWWTWWWWZZWWOG@G@@=@@@=@=@@===@=9=@@=8===8==8==8==8=/8@@OWWWTOOOQTQWTWWWYWZZZZZZZ\Z\Z\ZZ\ZZZZ\ZZZZZWWWWWWZZQGCF@@==/(  1HRH323F3HHRRRRWRZZRZZZZZZZZZZRZRRRRRHH33233HR1 ( (((( ((.(.(*.*(.(.(.(.(.(((( (((.(((((((((( (.((.*.(.*/*.*///*/*/(. (( (.(/*./(../*/===/==//=/8///=//*///*..(                        .......... . ../////=/=//=2///=2=//=/=//=/==2==2====F======@F=F@@=@F@@@@F@@F@C@@@@@@@G@CG@@GC@@@@@CGCGCLOLOLOOTMTTTTVTVVVVVVVVVVVVVVVVVYXYXYYXYXY[VVSTTSTVVVVXYVUTSMTMMMOMOLLOLOLOTZZZYYZYZYZYZZZZZZZZZZZ\ZZ\ZZZZZZZZZWZWWWWVWWZZWWOOGCGCCC@C@C@@@C@@C@@@@@@@8=====9==@=@=@@@COTYZWTTWTWVWWYZYZZZZZZ\Z\Z\Z\Z\\Z\\\Z\ZZ\ZZZZYWWZZZWOOLCCC==(( .2HRH323HHHRRRRWWZRZZZZZZZZZZZZZRZRRRRHHH3323RH3  ((.((.(.((/(//*/*/*//*=*///*/(//.*((.((.(.((.((((((((((((((((.(.((*.(/*/.*//*//(/( ( (.(.(./*.(.(/./=/===/8////=/*/=/=///*/(.(              (   ...//////.........///=//2=/=/=2==2==2====2==F==F=F=F@@=@F=@F@@G@@FCFCGCGCC@GC@GCC@@CGCCG@CGCCGCG@CCGCCLOLOMOMTTTTTVVVVVVVYYYXYXYXYXYXVYXYXY[X[[[[[YXVTSTVSVVY[YXYYUTSTTTMTMMTMMMMOTTY\Z\YYZYZZZZZZZZ\Z\Z\\Z\\Z\\Z\ZZZZZZYZZYZWVWYZZZWTOOLOCLCCCCCCCCCCCCC@C@@@@@@9@@@@@@@@@@CCCLTWZZYVTYYWYZYZZZZZ\Z\Z\Z\\Z\\Z\\Z\Z\Z\ZZ\ZZZZWYWZZZTOOCCC@=.( .FRRH3F3HHHRRRRWRZZZZZZZZZZZZZRZZRWRRRRH33323HR3   (.(.(.((*.*/*/*/=*/=/========/=//=////*/**/*.**(.((.((.((.(((((((((((((.(((*/*/*/*/*/8///8//=*//((.(.(.*.//*./*//*==========/=/=/8/=///*.(.      ..( ...   . ... . . ...... .. .       ..../2/2/2///......////=//=2=//=///2==/2=/=/==/F/=====F======F@@F@@F@@@G@G@@@@G@@@@@@@@@@G@@G@CF@@@@@CGCGLLOLOLTOTTTTTTTVTVVVVYVVYVYVVVXYXYXYYXY[Y[YVSTTMTVVVXY[YXVSTTMMTMMOMOLOLOLTTZZ\YZYZYZYZZZZZ\Z\Z\Z\Z\Z\Z\Z\ZZZZZZZZZWWYWWWWWZWWTOGCOCCCC@C@@@C@@@@@8@===8====8=@=8=@@@@COMTYZZYWTWYWYWZZZZZZZZ\Z\Z\\Z\\Z\\Z\\Z\Z\ZZZZZWWWZZWOOCG@@@/(  1HRH323HHHRRRRWZZRZZZZZZZZZZZZZZRZRRRRHHH3323RH3   (.((.(.(((/*//*//8/=/====F====2=////(/.(.*(.((((((.(.((.(/(.*.(/*/*//*//*/( (.*(..(.(.(.(////(====/=//*//=//=*=//*/*.       . . ( .     .  . .       ...///2/2=/2///..../.=/2=/=//=/=/=/=2==/=2=2=====2==2===F==F=======@=F@=@=F=====@===================@=@@@@@@CGCGLOOOMOTOMTOTTSTTTTSTMTTSTVVVVVVVVVVUTLOLOLMTSVVVSVMOLLOLLGLCOCCCGCOMTYZZZYZYZYZZZZZ\ZZ\Z\Z\\Z\\\Z\Z\ZZZZZZZZZWWWWWTWWZWWOGCG@@@@@@=9===8=8==*=8==8=8==*/8/8/8=8@CGOTZZWTWTWWWWWWZZZZZZZ\Z\Z\Z\Z\Z\\Z\ZZZZZZZWWWWWWZWQG=@==/*.1HRH323F3HRRRRWRZZZZZZZ\ZZ\ZZZZZZRZRRRRH33232HR3  ( ((.(/////=/FFFGHGGHGHGFFF2//./((.(( (((.((.(/*.*.*.*//=*//*//(.*.*..(.(..(.*///*/=====/=/=*=/=/=/=*/(.(    . ..( ..                 ././2/2/2=/=2=/////./=///=2=/2==2/=2==2=/===2====2===F=====@=F@F=@=F=@F@====@=@==========@==@======@=@@=@@@@GCCOGLOOOOOOMOMOTTMTMOTMTTTSTSVSVVVVVVSTOLLOMTTVVVVTSOMOLLLGLCOCCCGLLTTTVZ\ZYZYZZZZZ\ZZ\Z\Z\\Z\\\Z\\Z\Z\ZZ\ZZZZZZWZWWWWTWZWWOGCC@@@@@===8==8/=*=8=*=8/8=*==*=*==8=@CCGOWWZTQQTWTWWZWZZZZZZZ\Z\Z\Z\Z\Z\ZZ\Z\ZZZZWWWQWWZWQGF==//*2HRH13F3HHRRRRWZRZZZZZZZ\ZZZZZZZRZRRRRHHH3323RH1 .(.(/./==FFGQQQRQWWWWWWRQQQQHFF=2/(..(( ( (((.*(.(*/(*/.*//*=//*//((.*//(..(.(..*=/=/*/==/==/=//*=/=/8///((.     ....( .(                ./////=2=2=2=/=/////////=2=//==/=/==2==/F/=F=2=F/==F===F==F=F@=@F==@F=@F@===F=F====================@=@@@@@G@CGLOLOOMOTOTTTTTMTTTTTTTSTVTVSVVVVVYXVTMMOMOTSVVVVVSTMOLOLLLLGLCCCCOMTTTVZZZZYZYZZZ[Z\Z\Z\\\\\\\\\\Z\Z\Z\ZZZZZZZZZZWWWWWWWZWWOGCG@@@@=8==8/=8==8=/=8===8===8==8==@C@CCOOWZWTQWQWWWWZZZZZZ\ZZ\Z\Z\\\Z\Z\Z\ZZZZZZWZWWQWWZQG===//*/ 2RH3233HHHRRRWRZZZZZZZ\ZZZ\ZZZZZZRZRRRRH33323RH3   (.../=FFQQQWWWWRWQRQRQRWQRWRWRQHFF=..(.((((.(((.*(/(*/(/*/*//*/=*=//(////(.(..(./*===//*===/=/=/=*=/=//=*.(.     ( ( (..                      ..///2/=2=2=/=/==/=2=/2=/=//=2=/=2===2===2====F==2==F=F=F@F@@FG@@FG@@G@@G@@G@G@CF@@@@@@@@C@G@@G@CGCCCCGCOCCOLTTSTVTVTTVTVVVVVVVVYYVYYXYYYXYY[Y[[Y[YVVVVVVYY[Y[YYVTSTMTTMTMMOMOMTVVVVVY\ZZYZZY\ZZ\Z\Z\\Z\\\\\\\\\\\Z\Z\Z\Z\ZZZZZZZYZTWYZZZTTOOCC@@@@@@@@=@@@=@9@@=@@@@@@@@@@@CLLCLLLOVZZWVWTWYWZZZZZZZ\Z\Z\\\Z\\\\\Z\Z\Z\ZZZZWWWWWZZWOG@@===*/( HRH323FHHHRRRWZRZZZZZ\ZZ\ZZZZZZZRZRRRRHHH332HHH1    ( ( ( (.//=FGQWWWWRQQQQGGQGGGGGGQHQQRQWQQGF=/.(.((.(.*(*/*.*.*/(/(//*/=/8//8/(.*///(.(/.(./=/==//*===/=//=/*===///*.((       ( ....       .         .///=2=/F/=2==2=2/=/=/=/=/=/==/F==2==F=F=F@F=@F=F=F@F@F@@G@GGCGGC@GGCGGCG@GCCGC@GCGCGCG@GCCGCCGCCGCGLCOLOLOMTTVVVVVVYVYYYYYYYYYY[YYYY[Y[[[[Y[[[[[YYYVYYY[Y[[Y[YYVVTVTSTSTSTMMVYXYYYYY\\Z[Z[Z\Z\Z\\\\\\\\\\\\\\\\\\\\Z\Z\Z\ZZZZZZZZYZWYZZZTTOLGLCC@C@@@C@C@C@@CCC@CC@CC@C@CCLLLLLOMOTZZZWVWWWYZZZZZ\ZZ\\\Z\\\\\Z\\\\Z\ZZZZZZZZWWWZZWQGC@@===**1FRH3233HHRRRRWZZZZZZZZ\ZZ\ZZZZZZZRZRRRRH33323RH1   ( .((.(((.(.((.//FFQQWWQRQQGGHGFGFFGFFGFFFFFFGGRQWRQGF=/.((.(((.((.*(/*/*/*//*/*/*/=*/=/==/8/(./=//*./(/(/===@=//======/=/==/==*//.(.        (...(.   . ..(.... .. . . (. ..  (  .   .///2/=2/=2==F/F/=2=//=2=/2==2=/F===2==2=F=F=F===F=F=F=F=FG@G@G@G@G@G@@G@@@G@@G@@@@@@@@@C@G@@@@GCCGCCGCCCOLOMTTSVTVVVVVVVYYYYZY\ZYYYXYYXYY[Y[Y[Y[YYYYYYYY[YY[YYVVTSTTTMTMTOMTUYVVVVVYZ\ZZZYZ[Z\Z\Z\\\\\\\\\\\\\\Z\\\\\Z\Z\Z\Z\ZZZZZYWWYZZZOOOCC@C@@C@@@@9@@@@9@@C@@@@@@@@CCLCCCCCLOOTWZYWTWWWWZWZZZZ\ZZ\\Z\\\\\\Z\Z\Z\ZZZZZWWWWWWZWQCF@===*/*.1HRH323HHHHRRRWRZZZZZ\ZZ\ZZ\ZZZZZRZRRRRHH3323HRH0   ..( ..=FHQQWRQGHFFFF@FF@F@F@FF@F=F==2FFFGQQRGF=/.((((.*(*/*/**/*/*/*.*//8/=8/==((/*//*.(/.(//======(===/==/=/=====//*.(.       ..       . ( .  .        .  ..//=/=/===2=2=/2=/=/=2==/=2==2==F=F=2==F=F=F=F=2===F==F=FFFGF@F@F@F@F@=FF@=F==F==F=F=@=F@==F===@@=@=@@@@@CCGLOMOTMTTTTTWTWYZZZ\ZZYYVVVYVYVYYYYYYYYZYZZYZYYYVTVTOMOMOLOLOLLOSTUVSTTVVZZ\ZYZZZZZ\Z\Z\\\\\\\\\\\\\\\\Z\\Z\Z\ZZZZZZZZWWWWWWZWWGC@@@=@=8=8=8=8=8=8=8=8==8=*8==@9@@@9@@@CGWWZWQWQWWWWZZZZZZ\Z\\Z\Z\Z\\Z\Z\ZZZZZZWWWQQWZWQF=///((.(1HR32323HHRRRRZRZZZZZZZZZ\ZZZZZZZZRRRRRH3H332HR30    ( /2FQQRQHFFFF=F=2=F==FF=2=F=2==2=2//F2FGRQQF=..(.((/(**/*/*/*/*/*//*=/=*=/=///8//(./*./*/=====/*=====/==/=/===//*(.(        .                    ..   ../2//=2=/===F2==2==2====2==2==F=F===2=F==F==F==F=F=F=F=FFG@GFGF@F@@F@F==@F@@=F=@======@=@=@=@=@F@=@@@@@@@G@COLOOOMOTTTWVZZZZZZZZZYVTVTVVVVYYYY[Z\ZZZ\Z\ZZZZYYTTTTOOMOLOLOLTVVVSVTVTVYZ\ZYZYZ\Z\Z\\\Z\\\\\\\\\\\\\\\\\\\\Z\\Z\ZZZZZYWWTWWZWOG@@@@@===8==8=8==*=8/=8/=*==8@@@@=@@@@@@GGWWWWOQWWWWWZZZZZZ\ZZ\\\\\Z\\Z\ZZZZZZWWWRQQWWWGF=/.*.(( 1HR3213F3HRRRRWZZRZZZ\Z\ZZZZZZZZRZRZRRRHH3323HR3  ../FHQRGHFF=2=2=F=F/F2F=FFFFFFFF=F/=2//=2FFGRQF2/( (*.**/**/*/*//*/*/*/=*/=/8=/=(.*//*.(/*//*====8==/=====/=======8//((.(     (     ( . (              ... .../=/==/=2=F=2=2=2===2==/2==F==F=F=F=F==F=F@=F==F=F=F=FF@FGFG@GFF@F@@F=F=@@F=@F=F@@==@=@F=@==@F@@=@=@@F@CGCGLOMOTTTTYZYZZZZZZYZ\ZZVVVVVYVYYY[Z\\Z\\ZZZZ\Z\Z\ZYWTTTOOMOLLLTVVVVVSVTVTZZZZZYZZZZ\Z\Z\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZZWZWWTWZZWOGC@@@=@==8=8==8=8/8*/*/*===9@@@@@9@=@=@@GQWZWQWQWWWWZWZZZZZ\\Z\Z\\\Z\Z\ZZZZZZWWWWQQWWWRF=///(.(( 1HR32133HHRRRRZRZZZZZZZZZ\ZZZZZZZRRRRRHH3H313HR3   ../FHQRHFF2/F=2F=2F=2FFFHGGQQQQHGHFFF/F/2=/FFFQQH=..(.((*/(**/*=*=/*/*//8/=*=/=*=/*.(/=*/(/=*//8/==@==/======/=/=====*//((.        ( ( .( ..               /./.  . ...///2==2==2=F=2==2=2==F/F=2=F=F=F==F==F==F@F=F=F=F=FFF@GGGGGGGG@GFG@G@F@G@G@G@GCGCG@G@GCCGCG@GCCGCGCGCLGLOMOTTTVVVYZZ\Z\\\Y[ZYZ\\Y[YY[Y[Y[Z\\\\\\\[Z\YZZ\Z\Z\Z\YYVVTTVTMTVX[YXYYXYYYY\\\YZ[Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZYWWZWZZTTOOCCCCC@CCC@@@@8@=8=8=@@CCCCCLCCCCCCC@G@OWWZWTWWWWWZZZZZ\Z\Z\\\\Z\\\Z\Z\ZZZZZZWWWQWWZQG@====*/*.(. 2HR3323FHHRRRRWZRZZZZZ\ZZZZZZZZRZRZRRRRH3323HRH2  .(./FHQQHHF=2=F2=F=FFFFFGQQQWWWWWWWWQQGHF=F/2=/FFFQQH=. ((.(**/***/=8==*=8//8/=*=/=*==/((/=/((/=*=/=8=@=@=/========/==8===/(((.     . (. .(..(.....(..( .               ///..   ....././/2==2====2=F/=2F/=F=2====F==F=F=FF=F=F@F@F@F@F@F@FGFGGGGGGOGGGGGGCG@GCGGCGOCGLGCGCCGCGCGCGLCGLGCOCLOLOMOTMTTVYYZ\[\\\Z[Z\Y[Z[\\\Y[Y[Y[\[\\\\\\[\[Z[Z[Z[Z[\\\Z\YYYVVTTVV[[[[[[Y[YYYYZ\\Z\Z\[\\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZYZYZZZZTTLOLCLCLCLCCC@@@9=@=9@CCLLLLLCLCCCCC@GCGOOZZWWWWWWZWZZZZZ\Z\\Z\\\\\\\Z\Z\ZZZZZWWWWWZZWGF@====8/*.(...2HRH232HHHRRRWRZZZZZZZZZ\ZZZZZZZRZRRRRHH3H32HRH1   (.(.(./FGQQHGF=F=FFF=FFFFGGQWRWWRRQQQRQWWWWQGHFF=F/=2FGQQF=.((((.((/***/*=*=======8===/==8=/=8=//(/*/(/===8==@=@@@=/==8==/=======8/*.((      ( ( . ( ..(..(././.*./..(..(..(...  . ( ..         /.(..   ..../////2=/===2====2=2=F2=2==2=F=F==F==F=F=FF=F@F@FF=F=FFFF@GFGGGGGGGG@GG@G@@F@G@GGCGGCGCFCCGCGCGCGCCGCGCGCGLGLOMTTTTYZZ\ZZZ[ZZ[YZY\Z\Z[Z[Z[Z[\\\\\[\[\[Z\YZYZZYZYZ[Z\ZZYVTVTTYX[Y[Y[XYYXYY[Z\Z[ZZZ\Z\\Z\\\\\\\\\\\\\\\\\\\\\\\\\Z\Z\Z\ZZZZZYWYZZZTTOLLCCCCCCC@C9==8=8=8@@CCCCCCC@C@9@@@@@@GOQZZWTQWWWWZZZZZZ\Z\\Z\\\Z\Z\Z\ZZZZZZWWWQWWZRG===/*//*.(. .2HRH3133HHRRRRZRZZZZZZZZZZZZZZZRZRRRRHH3H323HRH0   (./FGRHHF=22=2F2=2F/FFQRWRQQHFFFFFFFFGRQRROFF=2=2=/FHQGF/   ((((.**/**/8/8=8===8/=8//8/8=/=8/(.*/((/===8==@=@@@//======8/=====8/*.(.      .(..(./(..(..(..(. (. (. . (           ...   ....////=/=2=2=F/F=2==2=F/F2F===F=F=F=F=F=FF=F=F@F@F=F@F@FFFFFG@GFGG@F@FF@F@=F@@F@@F@@F@F@F@=@=@==@=F@@@@=@@@@GCGCOOTWYZZZZYZYZYZYZYYZZ\ZYYYZYZ\Z\YZ[Z\YZZYZYZYZYZYZZZZZZZWTOTTVYYXVYXYYVVVVYZ\ZZZ[ZZ\ZZ\\Z\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZZWWWWWZZWOGC@C@@@8=8=8=*=*/*/8*=8===9=8=8=8=8====@@QQZWQQQWQWWWZZZZZ\Z\\Z\Z\\Z\ZZZZZZZWWWQQQWWWF///.(.( .HRH322H3HRRRRRZRZZZZZZZZZZZZRZRRRRRRHH33313HR3 ..FHQHFF/2=2=2=2F/=FHQRQHGF2//....///2=FGRQQFF2=/2//FHQHF.((((((.***/*=/8===8==8=/=*=/8=8===*(/*/(*======@@@@@@8===8====*====8//*.(     ....(. (..(..             ( ...   . .././//=2==2===F===2==2F/F==2=F=F=F=F=F=F=F=F=F@F@F@FF@FFF@FGFGFGG@GGFG@F@FF@F@F@F@F@F@@F=F@F@F@F@@@F@F@F@@F@CG@OOOTZZZZYWYWYYZYZYZYZ\ZZYZYZ\Z\ZY\YZ\Y\ZZZZZZZZZYZYYWYZZZYWTTVVYXYXYVXYVVVVYZZ\ZZZZZZ\Z\Z\\\\\\\\\\\\\\\\\\\\\\\Z\\Z\Z\ZZZZWWWTQWZWQOCF@@@=8=8=**/***/*/8=/8=8=8@=8=8=*=====FGOWWWQQQWWRWZWZZZZ\ZZ\Z\Z\Z\ZZZZZZWWWRRQQRZRF2.(.1HRH1323HHHRRRWRZZZZZZZZZZZZZZRZRRRRHH3H3133RH1 .FHQHF2/2/2F2=2=2=FHQQHGF2..(..(.(.(..//FFQRQH=F/2=2/FHQH=. ((.(((*/**/8/=8===8==8=*=*==8==8==/((/((/8===@=@@@@@=/===8==/===8==8=*/(.      ( (..(..(. (            ( ..   ....///=2==/F=2=F==2==2=F2=F2=F=F=F=F=F=F=F=F=F@F@F@F@FFFF@FFFGFGGGFGGF@FG@F@=F@F@F@@FG@FF=@F@@F=F@F@@@F@@@F@C@GCOCOTZZZYWWYZYZYZYZYZYZ\\ZZY\Z\ZZ[ZY\Y\Z[ZZZYZZYZZZZZYZYZYZZZTVVVVYYYXYVXYVVVYY\Z\YZY\ZZ\Z\\\\\\\\\\\\\\\\\\\\\\\\\\Z\Z\ZZZZZZWWWTWZZWOOC@@@@=8=8==8/*=*8=*8/8=8==8=8==*=8=8===@GWWWWQQQWWWWZZZZZZ\Z\\Z\Z\ZZ\ZZZZWZRWQQQRWWF//...HRH3133HHHRRRRZRZRZZZZZZZZZRZRRZRRRHH33313HH30  .FHGHF2=2=2F/2=22=FHQRHF//..(  ( (.(.(..=FHQGH2=2//2=FQQF/.(((((.((*/**=*===8===8=/8=/8/=8===8=*.(/(/*@==@@@=@@@@@*====8=/8==8===*/*(     (  (...(.(.(       ( ( ./.(  ..././//=/F/==F==F=F/F/F/=F2=F=F=F=F=F=F=F2=F=F@F@F@FFF@FFGFG@GGFGGGGGGGGG@@FG@G@GGGGGGGCGGGGGGCGCGCGGLCGLOLOLOOMOTTYZZZYZYZYZY\Z[Z[Z\[\\\\\\\\[\[\[\\[\\\[Z\Z\Z\Z[ZZZZYZZZ\ZZYYY[Y[[Y[Y[YXYYYZ\\Z\YZZ\Z\\\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\Z\ZZZZYWWYZZZVOMOLCCC@@@@@@@9@@@=@9=@=@@9@@@@9=@=@=@@@GGOZZWWQWWWWZWZZZZZ\Z\Z\Z\Z\ZZZZZZZWWWWQQWZRG2//.(  .HRH3223HHHRRRRRZRZZZZZZZZRZRZRRRRRHHH33213HR3  (./FHQHF2=2=F2F2F2=FFHQQHF//..(.(.( ((.( (.*./FGRQFF/F/=2FHQHF.(  (((.((((.*/**==8===8==8==*=8===8===@=*((((/=8@=@@@@@@@@==8=//8/=8==8==8=*.(  . ( ( (..(.(/.(.(.  (..( (..         ../..(.   .././/=/2==F=2=F=F==2=2==2=F=F=F=F=F=F=F2=F=F@F@GFCFF@FGGFGGGGGGGOGOGGOGGGGGCGGCOGGLGOOOGOGGGOGGCGGCOLOOLOLMOMTOTSTYZZ\YZYZY\Z[Z\Z\[\[\\\\\\\[\[Z\[\\\\\\\\\Z\Z\Z\Z\Z[Z\YZ[Z\\[Y[[[[[[[[[[[[[[Y\\\Z\[Z\\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZYZWYZZZTOOLLCCCCCC@@@C@C@@@@@9@@C@@@9@@@@9@@=C@CGOWZWWWWWWWWZZZZZZZ\Z\Z\Z\Z\Z\ZZZZWZWRQQWWWQ=//.(((( .HRH3232H3HHRRRRRZRZRZZRZZZRZRRRRRRHH3H313HHH0    .(./FHQGFF2=FFFF=F=F=FHQQH=////(..(.(..(((..(/.//FQQHFF=2=2=FQQH/.(((.(((((((((.((**=*=8====@8===8===*====9==@=*.((/8=@=@=@@@@@@@====*/=*=*==8===*/(.  ..(..(...(.(./*//.(..(.(.(.(..(.(..( ..(( (     ..( .(.    ...///2==2==F=F===F/===2===FF=F@F=FF=FF=F=2FF@FF@GFFFFFFF@FGFGFGGGGGGGGGGGG@FG@GGCGGGGGGGCGGGGG@GGCGLGLGLOOLOOMMTTTWY\ZZYZYZYZ\Z[Z[ZZ\Z\\\\\[Z\Z[\\[\\\\\\Z\Z\\Z\Z\Z\ZZYZZZ[\\Z[YY[Y[Y[YXY[YYY[Y\\ZZZ[ZZ\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZZYWZZZZTOLGLCC@@@9@@9@9@@@9=@=9@9@@9=@8=8=9=@@@@CGQWZWWOQWWWWWZZZZZZ\Z\Z\Z\ZZZZZZWZRWWRQWWWH=...  HRH21233HHHRRRRWRZRZRZZRRZRZRRRRRHHH33213HH3  (.FHQHFF/=2F2=2F2F=FHQQH=/..(.((.( (.(.../FQRG2=2/=2=HQQ2.( ((((((((((.*/*=8=8===8==8==8==8====@8@==*.((/=9=@@@@@@C@@@/==*//*/8===8===*/((   . ( (.(.(.*..  ( (.(.(((      ( (.   .././=/=2==F===F=F=2=2==2=F=F@F=F@F=F=F2=FF=FF@FG@FFF@GFGFFGFGFGGGGGGFGFG@FF@F@FGF@FG@G@FF@F@F@F@F@F@@G@G@CG@CGOOOOTZZZWVWYZYZZZZZZZZYZZ[ZZZZYZYZZZ\Z[\\Z\Z\ZZZ\ZZZZZZZZYZYZZ\ZYYYYYYXYXYYYYYYYYZ\ZZYZZZZ\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZ\ZZZZZWWWTZZZQOC@@@=9=8=*/8/8*=8=*8/*=*8/**/**/*/8/8===@GQWWQQQQRWRWWZZZZZZZ\ZZ\ZZZZZZZWWWRRQGRWRH/.  3RH311233HHHRRRRRRZRZRRZRRRRRRRRHH3333113RH0  .2FHGF22/2F2=2F/22FFQHH/.. (      .(/2GRQF22=2/2FQRF/.(((((((((.((**/8/8====8===8==8==8=@===9=@=*/((==@@@@@@@@@@9@/==8/8//8===8==8.(.  .(.( ( (.(          .( (  ..//2=2==F=F=F=F=====2==F==F@FF@F=F=F=FF=FFF@FFFGFGFFF@FGFFGFGGHGGGGGFG@GFGF@GF@FFGFG@FG@F@F@F@F=F@G@@G@G@CG@COOOOWZZZWWVWZZZYZZZZZZZZYZZZ[ZZ[Z[Z[Z\Z\\\Z\Z\Z\ZZ\ZZZZZZZYZZ[Z\\YYYYYVVYYXYYXYYYZ\Z\ZZZ[Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZZWWWWWWZWO@@@===8/*=**/8/8=*=**=*/*/8/8/*****/=*===FGWWWQQQRQWWWWZZZZZZZ\ZZZZZZZZRZRWRQHHQWRH/  2RH111123H3HHRRRRRRRRRRRZRRRRRRHHH3H3113HH3  /FHQF22/2=22F22=2=FHRH/..  (   ../FQRG2=2=2/FHQHF.(((.(((((((/*/8=8=8==8====8==8==8=@@@=9=9=8/*/8@@@@@@@C@@@@=8==*/*=*=8===8=**(. .( (  (         .((   ..//=/==2=F=F=F=F=2=F/F=/F=F@F=FF=F==F=FFFFFF@GFGF@FFGFGFGFGGFGGGGGHGGGGFGF@GFGFG@GFGF@FF@GFF@F@F@FG@G@G@G@CGCOLOTWZZZYWYZWYZZZZ[Z\YZ[ZZZYZZ[ZZ\Z\\\\\Z\\Z\Z\Z\Z\Z\ZZZZZZZY\\\Z[YYYYVYXYYXYYYYYYZ\ZZYZZZZ\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\Z\Z\ZZZZZWWTWWZWQGC@@==8=*8*/*=*=8=*=8**8/*/*/*/*/***/*///=FQWWRQGRQRWWWZZZZZZZZ\ZZ\ZZZZZRWRRRGQRWRH/.1HH3101133HHHHHRRRRRRRRRRRRRRRHHH3333133HH0  .FHRH2/2=22F=2F/22FHQH2/ ( (   ..=HWHF2/2=/2FQRF. ((((((((((***/8/8=8==8===8===8===@9=@@8@==8/*==@9@@@C@@@@@@8===*/*/=8==8==*/((   . ( (         (( (..    ../2=2==F=F=F=F=F==2==F=F=F@F@F=F=F=F=FF@FFFGFGFGFF@FGFGFGFGGHGGQGGGGGGGGFGGGG@GGGGGGGGGGGGCGCGGGLOGOOOOOOOOMOTTYZZ\ZYZZYZZ\Z\[Z\\\\Z\[\[\[\\[\\\[\\\\\\\\\\\\\Z\Z\Z\\[\\[\\\\\[[[Y[Y[Y[Y[YYYYY\\Z\Y\Z\\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZYWYZZZYOOCC@@@9@=8@=9@@@@9@9=@8@9=9=8=8==8=*/8===FGWWWQQQWWWWZWZZZZ\ZZZ\ZZZZZZWZWWRQRHQWWH/...HH31011233H3HRHRRRRRRRRRRRRHHHH3H33113HH3  ..FHQHF22=2FF2FF2=FFFQRF/..( .((.. ((  (..2GRQF2=2/=2FHQH/.(((((((((((/***=*=8=8==8/=@==8==8=@=@9@=9@@=8/8=@@@@@@@@@@@@@*===8/8/=8==8=/*/((  (( (. ( ((.(... (..((.( (    .(....(   ../2==2==F=F=F===F=2=F==F=F=F@=F==F=F=FFF@GFGG@GFGGFGGGGGGGGGGQQOGQGGGOGGGOGGOGGGGOOOOGOGOOOOGCOOOOOOMOOMTOTTTTVYZ\ZZYZY\Z[Z\\Z\\[\\[\Z\\Z\[\\[\\\\\\\\\\\\\\\\\\\\\\\\\\\[\\\\[[[[[[[[[[[[[[[Y\\\[Z\[Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\\Z\ZZZZYWZZZZTOLCCC@@@9@9@@@9CCCC@9@@9@@9@8=88=8/8==8===GQWWWQQQWWWWZZZZZZZ\ZZ\ZZZZZWWWRWRQQRWWHF.(  3HH10011233H3HHHRRRRRRRRRHRHHH3H33112HHH0 (..=HQRHF=2FF2FF=FFFFFRQH=..(.(..(..(.(.( (  .(./HQRFF/2=2=FHQQF.(((((((((((*(/8**=8=8=8==8==@==8=@=8@=@9@=9@@@=8==@@@C@9C@@@@@=8==8==8/=8===8/*.(((.(( (.(.(.(.(((.(.(.((((((.((.((.(((.(.(  (    (( ((.      ..//2=F=F==F@=F=F=2===F=F@F=F@F=F=FFF=FFGFF@FGFGFFFGF@GFGFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGOGGCGGGOGOOOOOOOOMOOTTTWZ\ZZYZZY\Z\Z[\Z\\Z\Z\[Z[\Z[\\\\\\\\\\\\\\\\\Z\\Z\Z\\\\[\\\\\\\[[[[[Y[Y[Y[YYYY[Z\\Z\Y\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZWYWZZZTOCG@@@8@=8=8=9@@9@9@@9@8=8=*8*=**/**/*/(/==HQWWRQQRWWWZWZZZZZZ\ZZZZZZZRZRRRRGHQWWHF..1HH200011233H3HHHHHHHRHHHHH3H33331113HH1 ./FRQHF2F2F2F2F2F2=FHQQF...( (..(...( (    (.FRQH2=2/2/2FQRF. (((((((*(/*/8*=8==8=8==8=@@==8==9@=@@9@@@@@8=8@@9@C@@C9@@@@===@8===8===8=*/*.((   (( ( ( (  (( (.( ((..( ((.(   . ..2/=2===F=F@F=F===F=F=F=FF@FF=FF@F=F@FFF@GFGFGFGFFFFGFGGGHGHGHGGHGFGGFGF@GFGFFFG@GG@F@FG@G@F@GF@G@G@GCGCGGCOOOOTZZZZYZYZZZZZZZ\ZZ\Z[ZZZZZZZZY\Z[\\\\\\\\\Z\Z\\Z\Z\Z\Z\\[\[\\\\YYYYYYYXYVVXVYYVYZ\ZYZYZ[ZZZ\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZZWWWWWZZQG@@===******/88=8=8*8/***/**/(*(((((( (./2QWRQQQQRWRWZWZZZZZZZ\ZZZZRZRRRRHHHHRWH/. 1H310 00123333H3H3HHHH3H3H333321012HH30 .2HRHF222=2F2F2F222FRQH/.  ........   .FHWH=2/2=2/FHWH/.((((((/(***/8*=8=8==8===8@8==9==9@@@9@@@9@@@=9@@@@:@@@C@9@@8====@==8====8/*.((( (.(((.     (.((  . ..//2==2=F=FF@F=F=F=2=F=F@F@F@FF@F@FF@F@FGFFGGFGFGF@FGFGHGGGGGGGGGGGGGGFGFGFGGGFG@GGGGGFCGGGFCF@GF@GFGGG@G@GGCOOOWZZZYWZYZZZZZ\ZZZ\ZZZZ[ZZ[Z\Y\Z\Z\Z\\\\\Z\\\\Z\\Z\\\\\Z\\Z\[\\[Z[YY[YXYYXYVVVVVYZZ\ZYZZZZ\ZZ\Z\Z\Z\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZZWWWWWWZQG@===*/*/*/*8=8=88/8**/***.*(*.((.( (./FRWRQHQRQWRWZZZZZZZZZZZZZZRRRRHHHFHRRH202H3100011233333H33H3H3H3333110113H30 .FRQH22=2F2F2F22F2FHRQ2..  . /22FHFHF2/.   .2QRH2/2=//2=HRG2((((((**/*8*=*=8=8=8=8=@8@=9==8=@@@@9@@@@@9@@9@@@@C@9@C@@@@8=@8@@@=@@8==8/*.(.( ( (.(.(((  ( (( (    ....2/2/F=F=F=F=FF==F/==F=F=F@FFG@FF@FF@FF@F@FGFGGGFFFFFFGFGGHGGHGGQHGHGGGFGFFGGFGGFGGGFG@GFG@GG@GF@G@GFG@GGGGCGOOOOWZZZZYZZZZZ[ZZ\Z\Z\Z\ZZ\ZZZ[Z\Z\Z\\\\\\\\Z\Z\\Z\\\Z\\\\\[\[Z\\\Z[Y[YYYXYYVVVVVYYZ\ZZZYZZ[Z\ZZ\Z\\\\Z\\\\\\\\\\\\\\\\\\Z\\Z\ZZZZZZWWTWWZWO@@===***/**=8=8=8*/8**/(*(/*(((( ./FQWWQHQQRRWWWZZZZZZZZZZZZRZRRRRHH3HRRH. 02H310 00011232333333333331110013H30 2HRHF2F2F2FF2F2F22FQRH2. . ../FHQRQRRQQH2.   .2QRHF/2/2/2=HQRF.(((((*(*/**88/8=8=@8@=8=@8@=8=9@@9@@@9@9@@9@=9@@@C@C9@C@@@@8=@=@@@@@@=8==*/((.( ( (.((  ( (.((.((.(     ....//2=2=F=F=FF=F=FF=F2==F=F@FG@GF@FF@F@FF@FFGFGGFGFGFGFGFGFGGHGGGQGGGGGGGGGFGGGOGGGGGGOGGGOGOGCGGOGGOGOOOOOMOOTOTTWYZ\ZZZZYZ\Z\\\Z\\[\\[\\[\[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[Y[[Y[Y[Y[Y\\\Y\[Z\Z\\\\\\\Z\\\\\\\\\\\\\\\\\\\\\\\Z\\Z\ZZZZWWYZZZQOC@@@==8=9@9@@9@@@9==8=*=**/*/*(((((..(/FGWWRQQQWWWWZWZZZZZ\ZZZZZZWWRRHHHFHRRH 12H3200 00111132333232311101013H30 ..HQRGF2FFFFFFFFFF2FHRRH/...(./FHQRQRQQRQRQGF.   .2QRG22=2/2/2FRQF.(((**/8/8/8=8@8@@8=8@=@8@=@8@@@9@9@@C9@@9@9@9C@@@C9@C@C@==8@@@@@9@@@==*/*((.( (( ((.(.(( (((*(/((.((((( (( .   .../2//==F=F=F=FF=F=F=F=F==FFG@FFGF@FG@FF@FF@GFGFGGGGFGFGFGFGGGOGQGQOGQGGQGGGGGGOOOOOOOOOOOOOOOOOOOOOOOOOMOTOTOTTTTTYZZ\ZZZ[Z\Z\Z\\\\\\\\\[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[\\\\[\[[[[[[[[[[[[Y[[\\\[Z[\Z[\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\Z\Z\ZZZZZWWYZZWOOCC@@@@@@CC:C@C9@@88=8=****/(*.*(((((((.//=HQZWQQQRWWWWZZZZZZZZ\ZZZZZRWRRHHHHRRF.02H3F310100111111111110101123H31  .2QRRFFFFHFHHFHFFFFFHWRF/..../FQRQHGHFFFFGRRG2.  (.FQRHF/2=2=/2FQRF/ (((((*(*8/888=8=9@@9=@8@@@9=@8@@9@@:@@9@9@@@9@@9C@CC@C@C@@@8=@@@@@@CC@@=8//*(.(((.(((.(.((*.(.((*.*/**/((((.((((((((..(((.     ...2/2=2F=F=F=F=F=FF=F=F=FF=FGF@GF@FGF@FFG@GFG@GFGGFGFFGFFGFGFGHGGGQGGHGGGGGGGGGGGGOGOGOOGOGOOGGGGGGGLOOOOLOOOTOTOTWTZZ\ZYZZZ\Z\Z\Z\Z\Z\Z\\Z\[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[\\\\\[[[[[[Y[Y[Y[Y[[YZ[\\Z\YZ[ZZ\Z\Z\Z\\\\\\\\\\\\\\\\\\\\\\\\\\Z\ZZZZZWYWWZZWOC@@@@=9@@@@9@9@@8=8=****.((*((((.((.(///FQWWRQQQWRWZWZZZZZ\ZZZZZZWWRRRHHFHRRF 1HHH321110010111100100113HH30 .FRRHFFFFHFHFHFHFF2HQWHF.. ./2GRHFF2/2=22FFQRF.   .FHQH2/2/2/2/FHWH/(((**/8*8=88=9=9@@9@@@9=@9=@8@@9@@9@@9@@9@@9C@@CC@CCC@CC@@8=@@@@@@@@9==*.*((.((.(((((.((( ((((((/((((((((( (( ...//2==F=F=FF=FF=F=FF=F=FF@FF@FGF@F@FG@GF@GFGFGGFGFGFGFFGFGFGFGGHGGGGGHGGHGFGFGGGGGGGGGGGGGGG@GFCFCGFG@GCGCGGGOOOQOTWZZZZYZZZZZZZZ\Z\ZZ[ZZ[Z\\\\\\\\\\\\\\\\\Z\Z\Z\\\\\\\\\\[\[\\\\[[YYYYYYYXYYYYYYYYZZ\ZZYZZZYZZ[ZZ\ZZ\Z\Z\\\\\\\\\\\\\\\\Z\Z\ZZZZZZWWWWWWZWGF@/=8==8=8=8=*8*8/(((.(./HRWWGQQRWRWWZZZZZZZZZZZZWWRRRFHFHRR2 033HHH3322111011110133FH330 2HRQF2FFFFHFFHFFF2FFRRQ1. ..HQHF2//2/2//22HQH. ..FRQH/2=2=2//2GRH/.((((**/8*8=88=9=9@@@9@@@@9@9=9@@9@@9@@9@@9@@9@:@C@CCCCC@C@@=8@@=@@@C@@=*/*.((...(.( ((((.((( ( ..2/2=F=F=F=F=F@F=F=F=FF=F@GF@FGFFFGF@GFGFGGFGGFGGFGFFGFFGFGFGFGGGQHGGFGGGGFGGGGFGGGGGGGGGGGGGFG@GGGG@GGCGGCGCOGOOOWWZZZZWZZZZZZ\ZZZZ\ZZ\ZZ\\\Z\\\Z\Z\Z\\\ZZ\Z\\Z\Z\Z\\\\\\\\[Z\\\\YY[YYYXYXYYXYYVYYYYZ\\Z\ZYZZYZZZZZZ\Z\Z\Z\Z\\\\\\\\\\\\\\Z\Z\ZZZZZWWWWQWZWQ@==/=8=8=8==8=*=**(((.(./FQWWRHQRRWRWWZZZZZZZZZZRZRWRRHF3HRR2013HHHHH3H3H33H3H3HHH31.2RRHF2F3FHFHFHFHFF2HQRH2. .2GRF2/2.2/2/2/2HRH/  /FRRF=2/22=2/FHRQ2( ((((((((*=*88=8=9=@9@9@@9@@@9@9=@9@@9C@C9@C9@9@@9@CCCCCCCCCC@8==@@@9@@@@@=*/(.((.(( (((((.( (( ../2=2=FF=F=F=FF=F=FF=F@FF@F@FFG@FFGF@GFG@GGGGGGGFGFGFGFGFGGFGHGGGGGHGGGGGGGGGGGGGGGGGGGGGGG@GFGG@GGGGGG@GCGOOOOOTTZZ\ZYZZZZZZ\ZZ\Z\Z\ZZ\Z\\\\\\\\Z\[\\\\\Z\Z\Z\\\Z\\\\\\\[\\[\\\[Z[Y[YYYXYYXYYXYYYYYYZ\Z\Z\YZZZZZZZZZ\Z\Z\Z\Z\Z\\\\\\\\\Z\\Z\ZZZZZZWWWWQWZWQG==@==8==8=8/8/**(( (.(./=HWWRGQRQRWWZRZZZZZZZZZZRZRRRHHFHRH2 013HHHHRHRRHRHHH30 .FRRFF2FHFHHFHHHFHFFHRWH.. ..FRRF.2.2/2/2./2FRG2 ./HRQF2/=2=2=2/HQQ2.((((((**=*88=8=9@@@9@@@9@9@@9@8@9@@9@@@:@@9@9@C9CCCCCCCCCCC@8/=8=@=@@@@@=*/*(.( ((.(( (((((( (   ...2=F=F=FF=FF=F=F=F=FF@F@FFFG@FFG@FGFG@GGGGGFGGGFGGFGFGFGFGFGFGGQHGGGGGGGGGGGGGGGGOGOGGGGGGOGOGOGOOOOOOOOTOMTTTTWVZZ\ZZZZZ\Z\Z\\Z\Z\Z\\[\\\\\\\\\\\\\\\\\\\\\\\Z\\\\\\\\\\\[\\\\\[[[[[[[[[[[[Y[[[[[[[[Z\[\\\\Z[Z[\[\Z\Z\\Z\\\\\\Z\\\\\\\\\\\Z\\ZZZZZZWWWWZZWGCGCCCC@@@@@@8@=*/*((((((((((((.**(////FGWWWQQRQWWWZZZZZZZZZZZZWWWRRGHFHRR20013333331300 .2HWRFFHFHHHGHQHHHFHFHRWH2....FRGF2//2/2/2/2=HRH/ .2GRHF/2/F2=2=2GRQ2.(((((((**=*88@8@8@9C@:@C@9@@@9=@9@9@:@@@:@:@@9@CC9CLCCCLCCCC@8/=@8=@9C@@@@*/*(.((.(.((.(((((((((((((((((((.(  . /2/F=F=F=F=F=F=F=F@FF@FF@FFG@FFGFG@GFG@GGGGGGGGFGFGFGGGGGGGGGQGQGGQOQQGGOQOOOOOOOOOOOOOOOOOOOOOOOOTOTTTTTTTTVVWYZ\Z\Z[Z\Z\Z\Z\\\\\\\[\\\\\\\\\\\\\\\\\\\Z\\\\\\\\\\\\\\\\\\\\\\[[\[[[[[[[[[[[[[[[[[[[[Z\\\\\\Z\ZZ\Z\Z\Z\Z\Z\\\\\\\\\\\\\\\\Z\Z\ZZZWZWTWZZWTOCLCCCCC@@C@9=*/**/(((((((((((.((/**//*//=/=GRZWRQQRWWWWZZZZZZZZZZWZWWRRQHHHRR2  ..FRRQHFHGHQHQQHQHGHHFGRWH=1../FRRF2/2/2=2/2=FGRH/   ..FRQGF2=22=22=2GRQF.((((((((**=888@8@@9@@9C@:@@9@9@8@9@@9C@:@9@:@C9@CCCLCLLCLCLCC@9==9=@CCCCCC@8/***.(*(*.*(/(((((((((/(**(*(((((((((((((((((( . .1/=F=FFF=FF=F=FF@F@FFGFF@GFF@FGF@GFG@GGGGGGGGGFGFGFGFGHGHGHGGHGFGGGGGGGGGGGGGGGOOOGGGGGOGGGOOOOOOOOOOOOTOTTTWVWY\ZZZZZZ\Z\Z\Z\Z\Z\Z\\[\\\\\\\\\[\\\\\Z\\Z\Z\Z\\\\\\\\\\[\\\\\[\[[[[[[[[[Y[[Y[[[[[[Z[Y[YYZ\Z\\Z[Z[ZZZZ\Z\Z\Z\\\\\\\\\\\\Z\Z\Z\ZZZZZWWWWWZZOOCGC@@@@9@8=****/((.((((( (.(((.(*.*/*////=FQWWRQQWRWWWZZZZZZZZZZZRWWRRQHFHRH2  /HRQHFFHHGHQHQHQHQHGHHWRR2....FRQF2//2/2/2/FHQH2. /FRQF2/2=2F=2/FFQRF. ((((((((((**=8=8@8@9@CC9C@9@@@99@8@@9@C@:@C9@:@@:CCC:LCLCCLCCCC@8/8=8=CCCC@@8=*/(((.(((.((((((((/((((((((((((  ....2/2F=FFF=F=F@F@FFF@GF@FFGFGF@GFGFG@GGGGGGGGGFGGFGFGFGGGGFGHGGGHGGQGGGHGGGGGGGGGGGG@GFGG@GGGGGGGGOGOGOOGOOOOOOWZZ\ZZZZZZZZZ\Z\ZZZZ\ZZ\\\\\\\\\[\\\\\ZZZZ\Z\Z\Z\\\\\\\\[\[\\\[\XY[YYYYYYYYVYXYYYYYYYYYYYYYYZZZ\ZZZZYZZZZZZZ\ZZ\Z\Z\\\\Z\\Z\ZZZZZZWWWQQQWZWQG@====8=**/((.((((( (.( (.(..//FHWWRQHQRWRZRZZZZZZZZRZWRRRRHHFHRR1 2HWHHF3FHHQHQHQHGHHFHHRWH2. .2FRHFF22222FFHHHF. .2HQHF2=22=22=2=HQQF.((((%((((/888@8@9@9C@C9C@:@9@9@9=9C@9C@9@:C@:@:@CCCLCLLCLCCCCC@=**=8=@C9C@=8/((.(( (((((( . 1..2/=2F=FF=F@FF@F@FGFFGF@FGF@FGFG@GFGGG@GGGGFGGGGFGFGGHGGGHGGGGGHGGGGGGGQGGGGGGGGGFGFGG@GFG@GGGOGGOGOGGOOOOOTQTZZZZZWZZZZZ\ZZZ\Z\ZZZZZ\\\Z\\\Z\Z[\ZZZ\ZZZZZ\ZZ\\\\\\[\\[\\\\[Y[YY[Y[YXYYXYXYYXYYYXYYYVVTTWVYZZZZZYWZWZZZZZZ\Z\Z\\Z\Z\\Z\\Z\ZZZZZWWWRQQWWWQF@===*/*/(((.((.(((((  ( .(........//2GWWRQHQRWWWZWZZZZZZZZRZRWRQHFHHRH1 2HRRF2HFHHHQHRHHRHHHFHRWR2.. .2HQRHGHHHQHRQHF.  ..FRQH2=2=22=2=2=HQRF.(((%((((((**=8@8@9@@9CC@:@@9@9@@9@9@:@@:@C:@:C@:@:CCCLCLLCCLCCCC@8/8/8@@C@@9=8/*(.(.((  ....1/2/=F=FFF@FF@FF@FFG@FFGF@FGFG@GFGG@GGGGGGGGFGGFGGGFGGGHGGHGGGHGGGGQGGGQGGGGGGGGFGFG@GGFGGGGGGGGOGOGLGOOOOOOTWYZZZZZZZZZZZ\Z\ZZZ\Z\Z[Z\\\Z\\\\[\Z\ZZZZ\Z\ZZ\Z\\\\\\\[Z\[\\\Y[Y[YYYYYYXYYYYXY[YXYYYYYVVVVTTYYZZZZZZWYWZZZZZZ\Z\Z\\Z\\\\Z\Z\ZZZZZWWWWQQWWZQF==//***(.((((((.((((  . ./2/2F=2=2=2/FHWWRRQRQWRWWZWZZZZRZRZRWRRRHFFHRH1.FRRHFFFHHQHRQRQRGHHGHHQWR2.. ./FHRRRWRRQHF2.  .2QRHF/2/F2F2=2=2HQQF.( (((((((((((**=8@9@9@@:@C:@:@@9@9@9@@9C9CC:CC9CC9CC:CLCLLCLLCCLCCC@=*8=8@CC@@=8=*.(((((((  .1..1.2.2/FFF@FF@F@FFGF@GF@GFGF@GFGFG@GFGGG@GGGFGGGGFGGGGGGGGGGQGGGGGQGGGQGOOGGGGGGGGGGGGGGOGGGGOOOOOTOTOTTTTTTWTYZ\\ZZZZ\Z\\\Z\\\\\Z\Z\\\\\\\\\\\\\\[\Z\\Z\Z\\Z\\\\\\[\\\\\\\\[\[[[[[[[[[[[[[[[[[[[[[[[Y[YYYYYYYYZZZZZZZZZZZ\Z\Z\\Z\\\Z\\\\\Z\ZZZZZZWWWQWZZQG@====8/**/*/*/*/*/*/(.(...//2FFGHQQQRQQQHGHGQWZWQQQWWWZWZZZZZZZZZWWRWRGHHHHRH..HRRGFHHGRHRQRQRQRQQHHGRRWH2.. ..1=2HFHHFF2..    ./HQHF22F22F/F22/FFRQF.((((((((((((((*/8=9=@9@:@CC9C@:@9@9@@9@:@C:CC:C@:CC:C@:CLCLLCLLCCLCCC@=8=8@CCLCC@@8**.(((((((*(*(((*(((  .0..1./1/2/2/FFF@FFFGF@FFGFGF@GFFGFG@GFGFGGGGGGGGFGFGGGGGGQGQGGQQQQGGQOQOGOQOQOQOOGOOOGGGGOOOOOOOOOOOTOTTTTTTTTVTVWYZZ\Z\Y\Z\\Z\\\\Z\\\\[\Z\\\\\\\\\[\[\Z\Z\Z\Z\Z\\\\\\\\\\[\\\\[\[[\[[[[[[[[[[[[[[[[[[[[[[[Y[Y[YYYYZY\ZZYZZZ\ZZ\Z\Z\\\\\\\Z\Z\\Z\ZZZZWWWWWQWZWG@@===8=*=*/8/*8/8/**/*////=FQQQWWWWWWWWWWWQQWWWWRQWWWWWZWZZZZZZRZWWWRRRHFFRRH1 1HWRHFHHQHQRQQRQRQRQQHRQWWHF... ..../././..(   ( (.2FQRHF2=2F=F2F2=2=HQRF.((((((%(%(((((**8=9@@:@:@:C@:@:@@9@9@:@C:@CC:CCC:@C:CC:LLLLLLLLLLLLLLC=@@9CLMLLCC@@8**((((((((((((((((***/***(((((((((((((  ..1....2./1/1/2=F@FFG@FFGF@GF@FGFG@FGFGFG@GFGGG@GGGFGGFGGGGGGGGGGGQGGGGGQGGGGOGQOGGGGGGGGGGGGGOGGGOGOOOOOOTQTOTTTTTTWVZ\Z\YZZ\Z\\Z\\\\Z\\Z\Z\[\\\\\\\\\[ZZ[Z\Y\Z\Z\Z\\\\\[\\[\\\\[\[[[[[[[[[[Y[[[[[[[[[[[[Y[YY[YXYYVTVVYZZZZZYZZZ\Z\Z\Z\Z\Z\\\\\Z\ZZ\ZZZZWWWQQWZWGF===*/**/*****/*/*.*/.//=FHQRWRRQRHQHQRRQWWWWZWRQQRWRWWWZWZZWZZZRZRRRRGHHFHRH 1HRRFHFHHRQRQRWRWRQRQHGHRWRF2..       ../FHRHF2F2=2F2F2=2/2HQRF.( (((((((((((((*/*8@9@9@@:@:CC9CC9C9@9@:CC:CCC:C9CCC:CCC:LLLLLCLLLLLCLC@8=@8CLLCCC@@*/((((((((((((*(**((((( . ...2/2//2./2/.2=FF@FFGF@FGF@GFGFGFG@GGFGFGFGG@GGGGFGGFGGGGGGGQGGGQGQGGGQGOGGGOQGGGGGGGGGGFG@GGGGGGGGGGOGOOGOOOGOOOOOTWWZZZZZZZZZZ\ZZ\Z\ZZ\Z[ZZ\\\YZ\\ZZZYZZZYZZZYZYZZ[\[\\[\\\\\[\X[Y[YYYYYYYYYYYYYYYYYYYYYXYVVVVTTOOOOTWZZZZZYZZZZZZ\ZZ\Z\ZZ\ZZ\Z\ZZZZWWWRQQQWWRG2=*.*(/(((.(.(((((.(../FHQRRQHHFF2FFF2FFHHRRRRRHQHQRWRWWZWZRZWWZRWWRRRHH23HR3 2HRHFFHHQHRQRRRQRRQRHQHHRRRH2..      ..2FHQHF2F2F2FFF2F2=2=HRQ2.(((((((((((((*(=8=9@:@:@CC:CC9CC9C@:@9CC@:CC:@C:C@C:CC:CLCLLLLLLLLLCCC98=8@@CCC@@8** (((( .1.2/2//2/=2//2/2=2=FFGF@GFGFGFG@GFGFGFG@GFGG@GGGGFGGFGGFGGGGGGQGGGQGQGGGGQGQGGGOGOGGGGGGGFGFGGGG@GG@GOGOGGOOGOOGLOOOOOWYZ\ZZZZZZ\ZZ\ZZ\Z\ZZZZZZZ\Z\YZZ\ZZZYZYZYZZZYZZZZ[ZZ[\[\\[\Y[Y[YY[Y[YYYY[YYXYYXYYXYYVVVVVVSTMOMOOOOTWZZZZWZZZZZZZZZZ\Z\Z\Z\ZZZZZZZWWWRQQHRWWGF///.((.((.(((.(.(../=FHQRQHFF2F2F2=2F22FFFHHHHHHGRQRWRWWWZZRZWWWWRWRHHFF3RR22RRHF3HHHRQRRRRRWRRQRHHHHQRRH2..    ../2HQHHHF2F2FF2FFF2F/2FHWH=.( ((((((((((((((((***8=8@9@CC9CC:CC:CC9C@9C9CCC:CC:CC:CCC:CCLCJCLLLLLLLLLLC@@8=@@CCC@9=**((((((((((((((( ..1/2/2=2=F2=2/2/F=FFF@FGFGFG@GFGFG@GFGFGFG@GFGGGGGFGGFGGFGFGGQGGGGQGQGQGOGQGGGOGQGOGGGGGGGG@GGGGGFGGGGGGOGOGOOGOOOOOOTWWZZZZZZZ\Z\Z\Z\Z\Z\Z\Z[Z\\ZZZYZZZ\ZYZYZZYZYZZY\YZ\[Z\\\\Z[YY[Y[YYYYYY[YYXY[YYYXYYYYXYYVVVSTTTMOTMTTWYZZZZYZZZZZZZ\ZZ\ZZ\ZZ\ZZZZZZWWWRRQQQWWHF//*.*.((.((.((.(.//=HQRQHHFF2F2F=F2F2F=2F2FFFHFHQRQRWWWWWWZZRZWWRWRRQHHFFHR3 FRRH3HHHQRRRRWRWRRRRQRGHHHRRQH2..       ..2FGRQHHF2FFFFFFF2FF2F/FHWH2.(((((((((((((((((/*88=9@9@C:@CC:@C:CC:@C9C:@CC:CC:C@:CCC:CCLCLLJLLLLLLLLCLC@8=@CCCC@8=*((((((((((((((%(( 1./1//2==F=F==F=2=FF=FFGF@GFGGFG@GFFG@GGGFGFGG@GFGGGGFGGFGFGGGGQGGQGQOGGQGQOGOQGOOOGGGGGGGGGGGGGGGGOGOOOOOMOOTTOTMTTTTTYZ\\Z\Z\\Z\\\\\\\\\\\\\\[Z\\ZYZYZ\Z\Z\ZZYZYZZ[Z[\Z\\\\\\[\[[\[[[[[[[[[[[[[[[[[[[[[[[[Y[Y[YYXVVVVVVVVYYZ\Z\ZYZZZZ\ZZ\ZZ\Z\Z\Z\Z\ZZZZWWWRQQWWWQF===//*/*(/(/(/*///FGQRQGHFFFFFFFHFHFFFFFFFFHFHGGQQQRWWWWZWZWZWZRZRRRQHHFHRH20001111323223110  2FRRHHHGRHRQWRWWRWWWRRRQRHQHQRRGF2/.. . . ../2FGRRRGHFFFFFFFFFFFFF22=FRRG.. (((((((((((((((((((*/8*8=9@C9C@CC:CCC:@C:C@:@:C9C@:CCC:CCCC:CLCLLLLLLLLLLLMLLCC@@CMLLCC@8=*/**/*****(*(((((((((((((((( .2.2=/2F=2=F=F=F=FFF@FFGF@FGFG@GFG@FGFGG@GFGFGGG@GGGFGGGFGGGGQGQGOQOQOOQOOQOOOOQOQOOOOGOGGGGGOGOGOOOOMOOTOTTTTTTTVTVWYYZ\Z\Z[Z\\\Z\\\\\\\Z\\[\\Z\\\ZYYYZ[\\\\\\\Z\[Z\\\\\\\\\\[[\[[[\[[[[[[[[[[[[[[[[[[[[[[[[[Y[XYYXYVVYVYYYZ[\ZZZZZZZZZZZ\Z\Z\Z\Z\ZZZZZZWWWWQQRWZQG=====*/*/*/*.*//=FGQWQHGFFGFHFGHGHGHGHFFHFFFGHGHQRQRWWWWWZWZWZRZRWWRQHHFHRR133HHHHHHHHHHHHHHHH ./HWRQFHHRQRWRWWWWWWWWWRRQQHQHQRWRHF/2/...../.2/2FHGRRWQHHFFFFHFFHFHFFFF2F2FRQF/ ((((((((((((((((((((((**=88=99@@:CC@C:CC:CCC9CC9@:@:C@:CCC:CCLCLJLLMLMMMMLMMMMMMMCCCCLMMMLCC=88/8*8=*8/8*/(*((((((*((((((((%(((( 1.//2==2=F=2=F=FF@FFGF@FFGFG@GFGGFGFG@GFGFG@GFG@GGGFGGFGFGFGGGGOGQGOGQGOOQOGGQOOOOOOGGGGGGGGGGGGGGGOOOOOOMOOTOTTTTTTVWYZ\\ZZ\Z\Z\\Z\Z\Z\\\\Z\Z\YZ\ZZYZYYZYZ\Z\Z\\Z\\Z\\\\\\\[\[[[[[[[[[[[[[[[[[[[[[[[[[Y[Y[Y[Y[Y[XYVVVVSVVVYYZZ\ZZYZZZZZZ\ZZZ\ZZZZZZZZZZZWWWRQQQWZRF=///*/(/((.(.(//=HQWQHGHFHHFHGHGHGHGHFGHFFHFHFHGHQQQRRWWWWWWWWZRWWRQQHHFHRH20H333133313111113HH0.2HWRHHHQRQRRWWWWWWWWRWRWRRHHHQRRRRRGHF2=22=FFFHQRRWRRQHHFFFHFHFHFHFFFFF2FFHQRF.( (((((((((((((((((((((((((/*=*88@9@C:CCCC:CC:CCC9C@:@:@C:C@:CCC:CCCCLLLLMLLLLMLMMMMLL@@CCMOMLC@@88*/***/****((((((((((((((( ..2/2/F=2=F=2=F=FG@FGFGF@FGFGG@GGGFG@GGGFG@GFG@GGGGGFGGFGGFGGGQGGGOQGGGOGOGOGGOGOQGGGGGGGGGGGGGG@GGGGGGLGOOOOOOOOOOTOTWWZZ\ZZZZZZ\Z\Z\\ZZ\Z\Z\YZZZ\ZYTVWVWYZZZZZZ\Z\Z\ZZ\[[[[[[[Y[[Y[YYYYYYYYYYYYYYYYYVYYYYYVVVVSTTTMTMOTOTTWTZZZZZYWZZZZZZZZZZZ\ZZZZZZWWWWRRQHQRWRF//.(.(((.((..//FQRQHFFFHFHFHHHGHHGFHFFFFF2FFFFHHHHRQRRWRWRWWRWWRRRRHGFFHRR 1323H1 .HWRHHHHQRRWRWWWWWWWWRWRRQRHHHHHRWRWRRHRHQRHRRWRWRRQHHFFHFHHFHFHFHFHFFF2FFHWH2.( (((((((((((((((((((((((((**88=889@CCC9CCC:CC:CCC9C@:@:C@:CC:C@C:CLCLLLLLMLMLLLOMOMLL@@=@CLCC@@=*/((.(((((.(((((((%(((((  1.2/2=2=F=2=F=FFGF@FGFGFGFG@GFGFG@GFGFG@GFGFGGFGGGG@GGGFGGGGGGGGQGOQGQGOGOGGOGOOGGOGGGGGGG@GGGGGGGGGGLGOGOOOOOMOOTOTQTZZ\ZZZZZ\ZZ\Z\Z\Z\Z\Z\ZZWZZZZWTWTTTTTWYYZZZZZZYZ[YZ[ZYYY[Y[YYYY[YY[Y[Y[YY[YYYYYVYVXVVVVSTVTTMTOMOMOOTOTYZZZZWWWWZWZZZZZZZZZZZZZZZWWWRQQHQWWRF/..((.(.((..(/2GQRGFFHFHFHGHQHHGHHGFHFFFF2F2F3FFHGHRRRRWRWWRWRRWRQRHHF2HRH13303H0 2HWRFHHHRQRRWWWWWWWWWWRWRRRQHHHHHRRRWRWWWWRWRWRRRHHHFFHHFHHHFHHHHFHFFFF2FFQRH/.(((((((((((((((((((((((((((*.8*8=89@:@CCCCC:CCC:CC9C:@C:CC@:CC:CCCCCLCLLLMLLLMLMMLMLMLC@=@@CLGC@==*/*((((*(((((((((((((((( ../2/2==2=F=F=FF@GFGF@FGF@GFGG@GGFGFG@GFGFG@GFGGG@GGFGGG@GGGGGGOGOOGOGOGOOGOGOOGOGGGGGGGGFGGGGG@GGGGGOGOOOOOOMOOTOTQTWYZ\ZZZZZZ\Z\Z\Z\\Z\Z\ZZZZYZ\ZZVTTWTWTTTWVWVYVYYYY[[Z[[[Y[Y[Y[YXYYY[YYYYYYYYYXYYVXYYVVVVVVVTVTTTTTMTTTTWTZZZZWWWZWZZZZZZZZZZZZZZWWZRWRRQGRRWRF/...(((.(.../HQRQHFHFHGHHQGHQHGHGHHFFFF2F2F2FFFHHHQRRRRWRWWRWRRRQHGHFHHRH.33 1H1 2HWRHFHHRRRWRWWWZWZWWWWRWRRRQHHHHHHQRRRRRRRRRHHHHGFHHHHHHHGHHHHGHHGFHFF2FHQQF.( (((((((((((((((((((((((((((((*(=*8=89@C:CCCCC:CC:CCC9C9CC:CC9CC:CCCLCLCLLLLMLMMLMOMLMOLL@@=@CLLC@@==**(*((((((((((((((((((((((  1.2///F2=F=2=FF@FFGFGF@GFFG@GFGFG@GFGFG@GFGG@GGGGGG@GGGGGGGGGGGGOGOOGOGOOGOGGOOOGGGGGGGGGGGGGGGGGGGLOOOOMOTTTTTTTTTVVYZZ\Z\Z\\Z\\\\\\\\\\\\\ZZZZZZ\ZZYYVYVYYYVYYYYYY[\[\[[\[[\[[[[[[[[[[[[[[[[[[[[[[[[Y[Y[Y[YXYY[YXYVVVVVVVYVZZZ\ZZYZWZZZZZZZZZZZZZZZZZWWWWRQRGRZRF///(..(.(.(.//FQRQHGFHGHQGQQRQQQHQHGHGHFFFFFFFFFHHHQHQRQRWRWWWWRWRQRHHFFQRH 0H3 3H3  .2QWRHHHQRRWWWZWZZZWZZZWZRWWRRRQQHQHHHHQHQHHGHHHGHHHHHHQHQHQHQHQHHFHFFFFFFHWH2( ( ((((((((((((((((((((((((((((***88@89@C:CCCCCCC:CC:CCC9CC:C:CC:CCCCLCLLLLLMLMMMMMTMTSMTMCCCCMOMLC@@==8*/*****(((((((((((((((((((  0. ..2/2=2=2=F=F@FGFFG@FFFG@GFGG@GFGFGFG@GFG@GGFGGG@GGGGGGGGGGGOGOOOOQOOOQOOOOOOOOOOGOGOGGGOGGOGGOGOOOOMOTTTTTTTVTVVYVYYZ\\\Z\Z[\\Z\\\\\\\\\\\Z\ZZ[\Z\YYYYYYYYYYY[Y[[[\[\[\[[\[[\[[\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Y[YYYYYYYYZYZZZ\ZZZZYZZZZZZZZZZZZZZZZZWZWWWRQQQRWWF=/./*.*(/*/./=HWRQGHGGQQRQRQQRQRQQGHGHGFFFFFFFFFFHGHQRQRWRWRWRWRWRQQHGHHQRH.0H3003H1  ./RWRHHQRQRWWWWZZWZZZZWZWZWWWWRRRQRGQHQHQHQHRQHQHRQHQQQQHQHQQHQHGHHGHFFFFFRRF/.(((((((((((((((((((((((((((((((**=*89=:@C:CLCLC:CCC:@C:CC:CC9CCC:LCLCLLMMLMMSTSTSSVSVSVSSLLCLMSTMMGCC@8=8=8/**(((((((((((*(((((((((((((((((  ....1/2//==F=F=FFF@GF@FGFG@FFGFGFGG@GGFGFG@FGFG@GGGGGG@GGGGGGGGOGGGOGOGOGOOOOGGOOOOGGGGGGGGGGGGCGGGGCGOOOOMOTOTTTTTTTTWVYZZ\Z\ZZ\Z\\\\\\\\\\\\\ZZZZZZ\ZZZYYVVVVYYYY[YY[[[[[[\[[[[[[[[[[[[[[[[[[[[[[[[[[Y[Y[Y[YY[YY[Y[Y[YYYZYZZ\Z\Z\ZZYWZWZYZZZZZZZZZZZZZWZRWWRQQHHQWRH//..((.((.(//FQRQHHGHQHQQRQRQRQHQHRGHHFFFFHFHFHFFFHHGHRQRRRWRWRRRQQHHFFHQRF 1H30003H3  .2HWRHGHQRRWWWZRZZZZZZZZZWZRWWWRWRRRRQRHRHRQRGRQRQRQRQHRQRRHQHHQHHGHFFFFFHRQ2.( ((((((((((((*((((((((((((((((((**8=899@C:CLCCLCCCC:CCC:@:C:CC9CCCCCLLLLLLMLMMMMMTMTMSTSTMCCCCOMMOLC@==88*****(((((((((((**(((((((((((((((  1.1.1/.2/2/F2=F=FF@FFFGF@FGF@GFGFG@GFGFGG@GF@FG@GFGG@GGGFGGGGGGGGOGOGGOOGGGOOOGOGOGOGGGGGGGGGGGGGGCGCGGGLGOGOOOOOMOOOOTOTTWVZ\ZZZZZZ\Z\Z\Z\\\\Z\ZZZZZYZZ\ZYTTQTTTVVYYYYY[[[\[[[[[[[[[Y[YYYYY[Y[YYYYYYYYYYVVYVVVYVVYYVYYYYZYZZ\ZZZZZZZWWWWWWWWWZWZZZZZZZWZWWWWWRWRGHQRWRF/... (../HQWHHFHGHQRQQRQRQHRGHHHFFFFFFHRQRHHF2FFHHHHQRQRRRRRQHHHHF2FRH21H100003H3 1HWRHHHHRQRWWWZZZZZZZZZWZWZWWRWRRRRRQRRQRQRHRRQRQRQRRRQRQRQRQHHHHHFHF2FHQRF/.( ((((((((((((((((((((((((((((((((((((**8=89@:CCCLC:CCLCC:CC:CC:C:CC:CCLCCLLLLLLMMMMMMMMMTMMOML@@@@@COCC@@=8/(*(((((((((((((((*(*((((((((((((((((  . ..1/2//2=2==2=F=FF@GF@FFGFGFG@FGFGFGG@GFGFGF@FG@GGFGGG@GGGGGGGGOGOGOGOGOGOGOGOGOGOGOGGGGGGGGGG@GGGGGCGOGGOOGLOOOOTOTOOTOWTWZZ\ZZZZZZ\Z\Z\\Z\\Z\ZZZZZZWZZZZWOTOTTTVVVVYY[Z[Y[[Y[Y[YYY[YYY[Y[YYYYY[YYXYYVYVVVVVVVVYVVYYZYZ\Z\ZZZZYWWWTWWTWTWWWWWZWZWZWWZWZWWWWWRRQHGHQWRH..( ((...FQWRFFGHQHQQRQRQRQRQHQHHHFFFHHQRRRRRHHF3FHHHHRHRQHRHQHHHF2FHHR13H100001H3 1HWRHFHRRRRWWZRZZZZZZZZZZZRZWWWWRWRRRRRRRRRRQRRRWRRRRRRRQRQHRGRGHHFFFFFHRQF.( (((((((((((((((((((((((((((((%(((((((*88=89@C:CCJCLCCJCLC:C:CC:CCC:CCCLCLCLLMLMMTMMMMMTMMTMMML@=@9CLCCC@==*/*(*((((((((((((((*((*(((((((((((((((((  .1../1/2/=2=2=F=F=FFF@FF@FGF@GFGGFG@GFGFGFG@FGF@GFGGGGGG@GFGGGGGOGOGOGOOGOGGOGOGGGGGOGGGGGGGGGGGGGCGCGGGCOGOGLOOOMOOMOOTOTTWVZ\ZZZZ\Z\Z\\\\\\\\Z\ZZZZZYZZZZZWTTTTYVYYYY[[[[[[[Y[[Y[Y[YYY[YY[YY[YYYYYXYYYXYVVVVVVVYYYYZY\Z\Z\ZZYYWWVWTWTWTWQWWWWWWWWWWZWWWWZWWWRWRRQHHQRWF1.(   (./FRRQHFHGRQQRQRQRQRQRHHHGFFFHHRQHHHQRQHHFFFHHHQHRHRHQHHHFF22HRH1331000 00 000HH0 2HWRHHHHRRWRWWZZZZZZZZZZZZZZZWWWWRWWRWRRWRWRWWWWRWWWWRWRRRRQHRHHHHFFF2HQRF/ (((((((((((((((((((((((((((((((%(((((((((**8=9@9CCCLCJCLCL:LC:C:CC:CCC:CLCCLCLLLMLTMMMTMMMMTMTMOM@@@=@CLCCC@==*/(*(*(((*(((((((((/((/(((((((((((((((((((( 1 .1.2//=2/=2=F=F=F=F@FGF@F@GFGFG@GFGFG@GFGFG@F@FG@GFGGGGFGGGGGGGGOOOOGOGOGCGGOOGGGGGOGGGGGGGGGGGGCGGGCGOGOOOOOOMOTTTTTVTVWVYYZ\\Z\Z[\Z\\\\\\\\\\Z\Z\ZZZZZZ\ZZYYYY[Y[[[[[\[[\[[\[[\[[[[[[[[[[[[[[[[[[[[[[Y[Y[Y[Y[Y[[[\\\\\\[Z[ZYZYYYWYWYWWVWWVWYWZWZWWWZWZWWWWWWRQQQHGRWRH/..(( (../HWRGHGHQQRQRWRWRRQRQQQHHHFHQQRFF/2FHQRHHHFHHGHQHQRGHQHHFF2FHRH.3H100000 000 00HH0 .2HWRHHHQRRWWWZZWZZZZZZZZZZZZZZZZWZWWWWWWWWWWWWWWWWWRWWRRWRQRQHQHHHFFFFHQQ2.( (((((((((((/(((.(((((((((((((((((((((((**889@C:CCLCJCLCLC:CC:C:CC:CCCLCLCLCLLMMMMTMMTSTSVVSVVSMCCCCCOMLLCC@=88/8*/*/(*.((((((((((*(*(((((((((((((((((( ..1/1/2/=2=2=F=2=F=FF@FF@F@FFG@FGFGG@GFGFG@GFGFFGFG@GGGGGG@GGGGGGGOOOOOOOOOOOOOOOGGOOOOOOGOGOGGGGGGOGOOLOOOMOMOTTTTTTVTVVVVYYZZ\\\Z\\Z\\\\\\\\\\\\Z\ZZ\ZZ[Z\Z\YY[[[[[[[\[[\[[\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Y[[[Y[\[\\\\\[\[\Y[ZYZYZYZYWYWWVWWYWZWWWWZWWWWZWWWWWWRQQHGQWRG2/.(.(../FQWRGHGGQRQWWWWWWWWRQRQHGHGRRQF2././/FHQRHFHHGRHQRHRHHHHFF2FHRH 0HH100000110 00 13H1 .2GWRHHGRQRWWWZWZZZZZZZ\ZZZZZZZZZZWZWZWWZWWWWWWWWWZWWWWWWRRRQRQHGHFHFFHQRF/.((((((((.((((.((((((((((((((((%((((%(((((**=88@C:CCLCJCLCLCLC:CCCCCL:CLCCLLLMMMTSTSVSTUVVVVXYVVVLCLCLLMTMOL@@==88=*8*/**(.((((((((((*(((((((((((((((((((( .1./.2/2==2==2F=F=F=FF@F@FF@FGFGFG@GFGGFGFGFG@GF@GFG@GGG@GGGGGGGGGOOOGOOOGGCGCGGGGGGGOGGGGGGGGGGGGGCGGCGOOOOOOMOOOTTTTVTVTVWYYZ\\Z\Z\Z\Z\Z\\\\\\\Z\Z\ZZZZZZZ\ZZYYY[Y[[[[\[[\[[[\[[[[[[[[[[[\[[[[[[[[[[[Y[YY[Y[Y[Y[\\\\[\Y\YZ[ZZ[ZYZYWYWYWWVWWYWWWWWWWWWWWWWWWWRWQQQQHHQRWH/..   (.FRWRFHGHQQQRWWRWRWRRQRGHHHQRQF/.. ..1FRQHHFHHHGHHQHHHHFF22FHRF 0H31000.101100 003R1 /HRRQHHHQRRWWWZZZZZZ\ZZZ\ZZZZZZZZZZZWZZWZWZWWZWZWWWWWRWRWRQRQHHHHFFFFRQH/.((((((((((((/(*(((((((((((((((((((%((((((***=89@C:LCLCLCJCLCCC:CLCJCCCL:CLCLLLLOMMTMSTSTSVVVSVTSMC@CCCCLOLC@@==*/*=*.**.*(.(((((((.((((((((((((((((((((( ..2//2=2=F2=F=FF=FF@F@FF@FG@FGF@FGFGG@GFG@GFGFFG@GFGGGGGG@GGGGGGGOOGOOOOGCGGGCGGGGGGOGGGGGGOGGGGGGGCGOCGOGOOOOOOOOOMOTOTOTTTWYZ\ZZZZZZZZ\Z\Z\Z\Z\ZZZZZZZYZZZZZYYYYYYY[[[[[[[[[[[[Y[Y[YY[Y[Y[Y[YY[YYYYYYVYYVVYYYYZZ[ZZZYZYZYZYZYZYWWWWTWOWQTWWZZWWWQQQRWQWRWWRWRQRQHGHGWRG2. .FRRGHFHQQRQWRWWRWWRWRQHHHHRRH2.  2FRRFFFFHHHHHHHFF32222HR20H310 01011011 00 02H31FRRHFHHHRRWRZRZZZZZZZZZZZ\ZZZZZZZZZZWZWZRZWZRZRZRZRWWWRWRRQHQHGHFFFHRGF. (((((((.((.*(/((.(((((((((((((((%((((((((***8=9@C:LCJCLCLCJCCLC:LCLCLCLCCCLLLLMLMMTMMMTMTMTMTMTLC@@@@@CCCC@==/***.*/(/*.((.(((((*(*((((((((((((((((((( 1.2.2/=2==F=F=F@FF@F@F@F@FFF@FGFCFG@GGFGFGFG@GFFGGGG@GGGGGGGGGGGGGOOGOOOGGCGGGGG@GGOGGGGGGGGGGGGGGOGCGOGCOGOLOOOOOMOTOTTTOTWTYZ\ZZZ\ZZZZZZZ\Z\Z\ZZZZZZZZWZWZ\ZZYYYYYYY[Y[[Y[Y[YYY[YYYYYY[YY[YYYYYYXYYXVVVVVVYYYZ\ZZ[ZYZYZYZYZYZYZYWWWTWQTWWZZZZZZWWWQQQWRWQRWQRQQQHGHHWWH/.    (.HQRHHGHQHQRWWRWWWRWRRQHHHQRRH.  2HRHF3F2FHFHFF3F2221FHH21H3 00.011111110 03H30.FRRHFHHRQRRWWZWZZZZZZ\ZZZZZZZZZZZZZZZZZZZWZRZWZWWWZRWRWRRQRGHHHFFFHQRF2 (((((((((((.**(*((((((((%((((%((((((((((***88=9CCCL:LCJCLCLCJCCLCLCLCLLCLLLLMOMTMTMTMMTTMTMTMOL@@@@C@@C@@===//*/(/*(/(/(((((((/(((((((((((((((((((( ../2/2==2=F=FFF@F@F@FFF@F@F@GF@FG@GFG@GFGFGFGF@GFGGGGG@GGGG@GGGGOGOGOOCGOCG@GCGGGGGGOGGGGGOGOGCGGGGOGCOGOOOOOOOOLOOTOTMTTTTTWYZ\ZZZZZZZZ\Z\Z\Z\Z\Z\ZZZZZZYWZZ\ZYYYYY[Y[Y[Y[[Y[Y[YYY[Y[Y[Y[YY[Y[YXYYYYYVVVYVYYYY\Z[ZYZYYZ[ZZZZZZYZZYWWWTWQWZWZWWWWZWWRQQQQQWQRQQRQQHGHQRWH/  .HRQHHFHQQRQWWWWWRWRWRQHGHRWRF.  1HRRF2F3FHFFHFF322222RH11H30 011111111100 01HH0.2RRHFHHHRRRRWWZZZZZZZZZ\ZZ\ZZZZZZZZZZZZZZZZZWZRZWWWWWRRRRQHQHGHFFHQRH/.( ((((((.((/(*(/*.*.((((((((%((((((((((((((**/88=9CCCL:CLCLLCCLCLCLCLCLLCLLCLLLMMMMTMMTMTMTSTMTMOC9@CC@@@C@@===/*.*/.**.*((((((*((*((*((((((((((((((( 0.2//2=2=F2=F@F@FF@FG@GFF@FF@FGFGF@GGFGG@GFG@GFGG@GGGGGGG@GGGGGGOGOGGGOCGGGGGGCG@GGOGGGGGGGGGOGGOGGOGGLGOGOOOOOMOTTTTTVTVTYYYZZ\\\\Z\ZZ\Z\Z\Z\Z\Z\Z\Z\ZZZZYZ\\\Y[Y[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Y[[Y[Y[[[\\\[Z[\[\Z\[\[\[\Z[ZZYYWYWYZZZQWQQWWWWWWQQQQQWQWQQQQQGGQRWH2.   (1HWRGHGHQQRQWRWWWWWWRRQRHQRWRH/   .HRRHFFFHFHHFFFF2222FRH 1R3 01112222121110 1HH1.FRQHHFHHQRRWWWWZZZZZZ\ZZ\ZZ\ZZ\ZZZZZZZZZZZRZZRZWWWWRWRWRQRQHHHFFHQRGF..(((((((((*.*(*.**((((((((((%((((((%((%((((***8*8@8C:CCL:LLCLCLCLCLCLLLCLLLLLLMLMTMTSTSTSVTVVVSVSMCCCMLLCCCC@C@==/*/(/(*.((((((*(*(*(*(*((((((((((((( ./2/=2==2=F=FF@F@F@FF@GFG@FGF@FG@GFGCFGFG@GFGFGGGGGGGGGGGGGGGGGOOOGOGGOGGCGCGGGGCGOOOGGGOOGOOGOOGOCOGOOOOMOMOTTTTVVVVTVVYYYYY\\\Z\[ZZ\Z\Z\\\\\\\Z\Z\Z\ZZZZ[Z\\Z[Z[[[\[[[[[\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Z\\\[\[Z[\[\[\Z\[Z\[Z\YZZYWYZZZWOOGOGQWZWWWQQWQQWQQQQQQHGQWWH=..  ./HWRHHGHQRQWWWWWWWWWWRRQQHRWWHF..  .FRRHF3FFHFFFH3F2222HRF 2H30 01112323232111013H3 .2RRQFFHHQQRWRZWZZZZZZZZZZ\ZZ\ZZZZZZZZZZZZZZZZZWZRZRWWRWRQRGHGFHGHQQF/.(((((((.((*.*(*.*(/(((((((((((((((%((((((((***=*8=@9CCLCCLLCLCLLCLLLLLLLLLLMMMTSSVSVVVSVVVXVVYVVVMLMMTMLLCCCGC@@///*/*.(((((((*/(*/(*.*(.(((((((%((( 1./2/2=2==FF=FF@FFFG@FGF@FG@GFG@FG@GGG@GFGFGFGGG@GGGGGGGGGGGGGGGOGGCGCGGCGGGCGG@GGOGOGGGGGOGGOGGOGOCGGOGOOOOOMOTTTTTTVTVTYYYYZ\\\ZZZZZZZ\ZZ\Z\Z\Z\ZZ\ZZ[ZZZZZ\Z[Y[[[[[[[[[[[[[[\[[[[[[[[[[[[[[[[[Y[Y[YY[Y[Y[\\\[Z[Z[Z\Z\Z[\Z\Z\ZZZYZYWZZZZTGGGGGGGWWWWQQQQQQQRQQGGHGHWRQ2.(   /HWRGFHGHQRQRWRWWWWRWRQRQHQRWRF1.   1HRRF2F3F3FH3FF222.2HR23H20 01121323223111013H3 1HRRFFFHHRQRWRZRZZZZZZ\ZZZ\ZZZ\ZZ\ZZZZZZZZZRZRZRZRWRRRRRQHHHFFFHQRF2.( ((((((*.*(.((*.*(((((((((%(((((((((((((%(((***=88=9=:CCLCLCLCLCLLLLLLLLLLLLLMMMTSTSSTSTSVSVVVVSVMLLLMTMLC@@CG===/*.(.((((((((((***/****((*((((((((((( .2.2=2=2=2@FF@F@F@FG@FGF@GFG@FG@FGFGFGFG@GFGGGGGGGGGGGGGGGGGGGOGGGGGGCGGCGGGCGGCGOGGGGGOGGGOGGOGOGGOGOOCOOOOOOMOTOTTMTOTTTWVYZ\ZZZZZZZZZZZZZZZZ\ZZZZZZZZYZYZZZZYYYYYYYYYYYYY[YY[YY[Y[Y[YY[YYYYYYYVYVXYYYYYY\ZZZYZYZZYZY\ZZZZYZZYZZWWTWZZWQFF@F@FGQWZWQQQQQQQQGRHGHHQRWH/..   /HRRHFHHGRQRWRWWRWWRRWRRHGHRRRHF1.  2HRH32223F3F2F2222.2HH23H3 0011232323222110.3HH0.FRHH2FHHHRRRRWWWZZZZZZZZZZZZZZZZZZZZZZZZRZZWZRWRRRRRRRHHHHFFHHQRFF.((((((.(((*/(*.*(*(*(((((((((%((((((((((%(((((%((****88=8@@@:CCLCLLLCLLLLLLLLLLLLMLMOMMMTMTMMTMTMTSTMMMLCCLLLLC@@G@F==//(/(((((((((((**/****(.(((((((((((((((  .1.2/2=2=2=F=F=FF@GF@GF@FG@GFG@GFG@G@GFGFGG@GFGGGGGGGGGGGGGGGGGGGGCGCGGGG@GCGGGCGGGOGGGGCGOGGGGOGGOCGOGOOOOOOMOTOTMTTTOTTTTVWYZ\Z\YZZZZZZZ\Z\Z\ZZZZZZZZZWYWYWZZZYYYYYYYYXVVVYYXYYYYYYYYYYYYYYXYYVXVVVVVYVYYZ[\ZYZYZYZZ[ZZZ[ZZZZZZZYWVWWWZWOGFF@F@FGWZWQQQQQHQHQHGHGHGRWH2.(  (.HWRHFFHHQRRRWRWWWRWWRRQHHHHHRRHH2..0 .1FHR3222223F223222.12HH13H3 001123232332311112HH0 2HRHF3FHHHRRRWRWRZRZZZZZZZZZZZZZZZZZZZZZZRZRWRWRWRRRRGHHHFHHHQRF2.( ((((((((.**(*.*.(*(((((((((((%(((((((((((((((*****8=8@@9C:LCLCLCLLLLLLLLLLMLLMLMMTMTMTMMTMTMTSTMTMOLCLMOLO@@FCF@==/((.(((((((((((*(/**/*(*(((((((((((((((( .../2//=2=2=F=FF@FF@GFGF@FGFG@GFG@GFGGFG@GFGG@GGGGGGOGGGGGGGGGGGGCGGGCGGCG@GGGCG@GOGOGCGGGGGOGGGGOGOGLOOOOOOLOOTMOTOTTTTTTTTYYZ\ZZZZZZZZZZZZZZZZZZZZZZZYZWYWYZZZZYYYYYYYVVVVVXY[Y[Y[Y[Y[Y[YXYYYXYYXYYXYYYYYY\Z\YYZYZ[ZZZ[ZZZZZZYZZWYWWWZZWQF@F@F@GGWZWQQQQGQQHQHHGHFHWRG2.    .HRQHFHHGHRQRRWRWRWRRRQRQHHHHHRQRRHF2F3HRRH22123222F322211.FHH0HH1001113233F332221111HH3 .FRHF2FFHHRQRRWWWWZZZZZZZZZZZZZZZZZZZRZRZWWWWRRRRRRRHHGHFFHHQHH// (((((((.((*(/((/((((.((((((((%(((((((((((%(((%((((***8*8=8@@@:CLCLCLLLLLLLLLLLLMLLMMMMMTMTMTMMTSTTSTMTMMCLLMOLG@G@GF==//((.((((((((((*****(*((((((((((%((((((    1.1/1/2/2/=2=F=F@F@GF@FGF@GFCFGF@GFG@GFG@GFGG@GGGGGGGGOGGGGGGGGGGCGGGGGCGGG@GGCGGGGGOGGOGOGOGGGGOGOGOOOOOOLOOMTTOTTTTVTVTVVYYYY\\\\ZZYZZZ\Z\Z\Z\Z\Z\Z\ZZZZYZYZY\\Y[Y[[[YXYYYX[[[[[[[[[[[[[[[[[[[Y[Y[Y[Y[[Y[Y\\\[Z[\[Z\Z\[\\Z\[\Z\ZZZYZVWZZZQOGG@G@GQWZWQQQQHQHQHGHGFHGRWH/..  .FRWHFFHHQQRQWRWWWWWRWRRQQHHHHHRRRRRRRRRRHF22222F3F22F221.23R3 03H30 12323F33H33232112HR3  2HRHF3FHHHRRRRWWWWRZZZZZZZZZZZZZZZZZZZZRZRWRWRRRRHQHHHFHFHRHH=.. ((((((((((.**.*((*.((*((((((((((((((%(((%(((%((((%(****88=8@9@CLCLCLLLLLLMLMLMLLMMMOMTSTSSSTSTSVSVVUVSVSTMMTMMOGG@G@F==..((.(/**(*(**(***/(((((((((((((%((((( . 1..2/2/=2=/2=2=2=F=FF@FGFCFFG@GFG@GFG@GFGGG@GGGG@GGGGGOGGGGGGGOGGGGGGCGCGGOCGGGGGCGGOGOOGGOGOOGOGOGOOOOOOOMOTTMTTTTTTVVVVVVYYYYY[Z\\\[ZZZZ\ZZZ\Z\Z\Z\ZZ\Z[ZZYZYZZZ\Z[[[Y[YYYXY[Y[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\[Z[\[\[\Z\\[\Z\\Z[ZZZYZVWZZWOOGOGGQWZWWQQQGQQQGQHGHGFQRWH=..   ..FRRQFHHGQRQRWRWRWRWRRRQRRHHHHFHQRRWRWRRGHF3F3F32F322222112FR2 HH11012333H3H3F33222113RH0 /HQRFFFFHHHQRWRWWWZRZRZZZZZZZZZZZZRZRZRZRWWRRRRRQHHHGFHFHQRF2.. ((((((((.((((*(/*(((((((((((((((%((((((((%(((((((%(((*****88@=@@:CCLCLCLLLLLMLLMLMTMTSVSVSVVVSVSVVXVXYXYVVUTSSTTMOGGGCF==/*.**(**/(*/*(*(*((((((((((((((%(((((  . .1/2/2//2=/2=2=F=F@FF@GFG@GFG@GFG@GFG@GG@GFG@GGGGGGGGGGOGGGGOGGGGGCGGGGCGGG@G@GGGCGGGGLGGOGGOGGOGOGOOOOOOOOOOTOTMTTTTTVTVTVVYYYY\Z\\ZZZZZZZZ\ZZZZZZZ\ZZZZZYZYZZZ\YZYY[YYYVVVVY[[[[[[[[[[[[[[[Y[Y[Y[Y[Y[YY[YYY[\\Z[Z[Z\Z\[\Z[Z\Z\Y\ZZZYZTWWWZWWWOQOQWWWWQGGQGHQHGHGHGFHHQWH2.. 2GWHHFHHHQRQRWRWRWRWRRRRHQHHHHFHFHHHHHHHF3F3F3F32F32221.11HH10HH301123F3H3H3H33321112HH1 2HQHH2FFHHHRRRRRWRZRZRZRZZRZZRZRZZRZRWRWRRRRRRHHHHFFHHHQRF2. (((((.(((((.(*/(((*(((*((((((((((%(((((%(((%(((%((((%(*(***8=8=9@@CCLCLCLLLLLLMLLMOMMMMMMTSSSMTSSTSVSVVVSVSTMMMMOLGGC@G@===/(/(/***/**((((/(((((((((((((((%(((((( ..../2/2/2/2/=F=F=FF=F@FGF@FG@GFG@GFGG@GGFGG@GGGGGGGGGGOGGGGGGGGGGGGCGGCGGGGCGG@GG@GCGOGCOGOGOGOGGOGOOOOOOOMOOMOOTOTTTTTTTTTTTTYWYZ\ZZYZYZZZZZZZZZZZZZZYZYWYWZZZZYWVVVTVTLOLOMVVVVYYY[Y[Y[YYYYYYYVXVYVXVVYVVVYYZ\ZZYZYZYZZZZZZZZZZZZYZWWWTQWWWZWWWWWWWWQGHGHQGHGHGHHHFFHRWH/.   /HRRHFFGHQRQRRRRWRRRRRRQRHRHHHFHFFHFHFHF3FFF3F3F3222211 02HH00HH0011233H3H3H3H3322112HR3 2HHHF32FFHHHRRRRRRWRWRZRWZRZRZRWRWRRRRRRRRRHHHHFFHFHHRHF2. ((((((((((.(((.***((((((*(((((((%(((((((((%(%(((%(((((****88==@@9CCLCLLLLLLMLLMLMMMMMMTMTSTMSTMMMTSMSTSMTMLMOMLMGOGG@F@==/**/(*/**(**((((((*(((((((((((((((( .1.2//2//2=2=2=F=F=FF@F@GFGFG@GFG@GFGGG@GGG@GGGGGGGGGGGOGGGGGGGGGGGGCGGGCGG@GGCGG@GGGOGGCGGOGOGGOGOGOOOOOOOMOTOTMTTTTTTMTTTTTTVVYZ\\ZZYWZWZZZZZZZZZZZWZWWWWZZZZWTTOTTTOMOLOLTTVVVVXYXYYYYYYXYXVYVVVVVVVVVVVVVYYZ\ZYZYZ[ZZ[ZZZ[Z[ZZYZWWWTWQOQWWWWZWWWWQGHGGQHQGHQHGHGFFHQWH/. 1FRRHFHHGHQRQRQRRRRRRRRRRHRHHHHH3HF3HF3HFH3HF3F3F322111 3H30HH1011233F3HH3H3H3231113R3  2HHHF23F3HHHRRRRRRRRRRWRWRWRWRWRRRRRRRRHHHHHHF3FFHHQHF2.((((((((.((((((((((*.(*((*((((*((((((%(((((%(((%((((((%(((****88==@@@:LCLCLLLLLLLMLMMMMMMTMSTSTMTMTMMTSTSTMMTMLLMOMLOGGG@@==//***.*(/(*((((((((((((((((((((((((( ..2.2//2=/2/F=F=F=F=FF@FFG@FG@GF@GFGG@GGGGGGGGGGGGGGGOGGGGGGGGGGGGGCGGGCGGCG@GCGG@GCGGCGGGOGOGOGOOGOOOOOOMOOTOTOTTMTTTTTTMTTTVTYYZZ\ZYWZWYZZYZZZZZZZYWYWWYZZZZVWOTTTTTOOLOLOSVVVXYXYYX[YXYYYXYYXVYXVYVVVVVVVVYYZ\Z[ZYZYZYZZYZZZZ[ZZZYWWWTQQOQQQQQWRQGGHGHGHGHQGGHGHFFFHRWH/ (  2QRQFFHHGHQHRQRRRRRRRRRQRHQHHHHHHHHHHHHHHHHHH3F3232211 02H31H3201132H3HHHHHH3H321113HH0 .2HHHF23FFHHHHRHRRRRRRRWRRRWRRRRRRRRRHHHHHHF2FFFHRRHF1 ((((((.(((((.((.(((**(*((((((((((((%((((%(((((((((((((%(*(***=88=@@CCLLCLLLLLMLLMLMMTMTMSTSTSTMTMTMSTSSTMTMMMOMLMOLOGCGG=F=*/*(*.*(((((((((*(*((*(((((((((((((((  ...2//2/2/=/=2=F=F=F=FF@FG@FGFGFG@GGFGFGGGGG@GGGGGGGGGOGGGGGGGGGGGGGGGCGGGGG@GG@GCGG@GOGGCGGOGOGOOGOOOOOOOOMOTOTTTTTTVTTVTVVVVVYYY[\\\ZYZYZZZZZZYZZYZZYZYZZZZZYWVTVTVVTTTMTTVVX[Y[[[[[[[[[[[[Y[[Y[Y[Y[[Y[XYYYXY[Z\\\[\[\[Z\[Z\[\\Z[Z[ZYZWVWTQOOQOQGGHGHFGHGGHGHQHGHGHFFGRWF2. /HRQHFFHHQHQHRQRRQRRRRRRRQHRHRHHHHHHHHQHHHHHHHHF3F2211 1HH31HH111133FHHHHHHH33322113HH1 .FHRHHF2HFHHHHRRQRRRRRRRWRRRRRRRRRHHHHHHFF3FFHHRRF2.. (((((((((/((((((((((*.*(*((*(*(((((((%(((((%(((((((((((((***88=8@@9CLCLCLLLLLMLLMOMMTMTSMTSMSSSTSSVSVSVSSSTMMMMOMMOOGCG@@=//*.(((((((((((((((*(*(((((((%((((((  .1//=2==2=F2=F=F=FF=F@FF@F@FG@GFGFG@GGGGGGGGGGGG@GGGGOGOGGGGGGGGGGCGGGCOGCG@G@GGCGCGGGGCGGOGOGOGOOOOOOOMOTTTTTTVTVVVTYVVVVVYYYY[Y[\\\\ZYZZYZZZYZZZZYZYZZZ\Z[ZYVVVVVVVTVTTSVV[[Y[[[[[[[[[[[[[[[[[[[[[[[[Y[[[[Y[Y[\[\\[Z[\[Z\[\Z[\[Z\ZYZYYWWTWOQOGGGHGFGGHGGQGQGQGQGHGFGHWRH/.(  .FRRGHFHGHHHRHRQRRQRQRRQRRRQHRHRHQHQRHRHQHQHHHHHF32211.1HH11HH11123F3HHHHHHHHH332122HR2 2FHRHHFFFFHHHHHRHRQRRRRRRRRRRRRHQHHHHFFF3FHHRRGH2/  (((.((.(((((/(((((((((***(*((((((((%(((((%(((((%((((((((((%***888@@9CLJLLLLLMLMMMMMTSSSVSVVSVVVSUSVSVVXVVVUTSTMTMMMTLOGGG===/*((((((((((((((*(*(*((((((((((((( ..2/2/2/2==F=FF==FF=F@FF@FGFG@GFCFGFGGGGGG@GGGGGGGGGOGGGGGGGGGGGGGCGGGGGGCG@GGCGGCGGOOGCGGGOOGOGOGOOOOOOOOTOTTTTTTTVTTVTVVVVYYYYZ[\\Z[ZYZYZYZZYZYZZYZZZZZZYWVTTTTVTTTMTMTTXYX[Y[X[Y[[Y[[Y[Y[Y[YX[Y[Y[Y[YYYYY[YY[Z\\\[ZZ[ZY\Y\Z[ZZYZYZYWWVWOTOGGHGFFFHGFHGHGHGHGHGHGHFQRQF/...HQRHFFFFHHHQHRQRRRRQRRRQRHRRHRHRHRHRHRHQHHHHHHF3221.02HH02HH101323HHHHHHHHHH3H3211HHH0 .FHRHHHF3FFHFHHHHRHRHQHRHRHQHHHHHFFF3F3FHRQHH2..  ((((((((/*(*.((((((((*.**(*(((((((((%(((((%(((((%((%((%((((*%**8=8@C:CLLLCLLLLLMLLMLMTSTMMSTSSSTSTSTSSVSVSSTMMMMMOMMOMGGC@@//*((((((((((((((**(*((((((((((((((  ../2/2=2=2=F=FF=F@FF@F@F@FGF@GFG@GFGGGGGGGGGGGGGGGOGOGGGGGGGGGGCGGGCOGGG@GGCGCGGCGOGOGCOGOGOGOOOOOOOOOTOTOTTTTTTTTTTMTTTTVVVVYYYZ\ZZVWWWYWZYWZWYWWZZZZYWTTOOOTMOLOLOCOLTVVVVVVVVYVVXVVYXYVVVVVYXYYVVVVVVVVVYVYYZZZ\ZYZYZYZYZYZYZYZYWYWOTQOGGH@FFFFFHGHGHGHGHFGHFFFGQRQF.  .2HRGF2F3FHHHHQHRQRRQRRRHRRHRHRHRHHRHRHRHHHHHHF3F220.02H33H311123H3HHHHHHHH3H322113HH0 2FRHHHF32F2F3FHFHHHHHHHHHHHFF3F223F3HRRRHF2 ((.((.*(/*(/(*.((((((*(**(**(((((%((((%(((%(((((((((%%((****8=9@C:LLLLLLLLLLMLMLMMTMTSTSTMTMMMMMMTMSTMTMTMMMMMMTMOLGG@@=/((((((((((((((*(*((((((((((((*( .2./=2==F=FF=F=FF@FF@FFFCFGF@GF@GG@GGGG@GGGGGGGGGOGGGGGGGGGGGGGCGGGGGCGG@GCGGCGCOGOGGOCOGOOGOOGOOOOOOOTOTTTTTTTTTTTTTMTTVTVVVYYZ\ZZYZTWWYWWWVWWWZZZZWTOOOOOMOOLOLGCOCMTVSVVSVVSVSVVVVVVVVVVVVVVYVVVVSVVSVVVVVYYY\Z\ZZZYZYZYZYZYWYWYWVWOQOGGFGFFFFFFFHFGHGHFHFGFFFHQWRF...FHRHF2FFFHHHHRHRHRQRHRQRHRQHRHRHRHRHRHHHHHHH3F321.013H23R3101133HHHHHRHHHH3H31112HH1 1FHRRHHF323F2F3FF3FH3FFF3F3F22F3FHRQRHF1. ((((((.*/*(**(*(((*((***(*((*((((((%(((((((((%(((%((((((%**88=9C:CLLLLLLLLLMLMLMMMTSMTSMTSTMMMMMMTMSTSTMTMMMMMMTLMOGCF=*.((((((((((((((*(*(((((%((((((((  ../2/=2=2F=FF=F@FF@F@F@FG@GGF@GFG@GGGGGGGGGGGGGGOGOGOGGGGGGGGGCGGGCOGGCGGCGGCGCGOOOOCGOGOOGOOOOOOOOOOTOTTTTTTTTTTTTTTTTTTVVVVYYZZ\ZYWWVWWWVWWWYZZZYWTTOTOOMOTOMOLLGLOSVVVVVVVVVVSVVVVVXYXYVXYXYXYVVVVVVVVSVVVVYYYZZ\Z\Z[ZYZYYYWYYYWVWTTOQGGGFFFFFFFFFHFFFFFFFFFFFGQWR/.  .HHQF3F2FFHHHHHQHRHRQRHRQHRHRHRHRHRHHRHHHHHFH32220.1HH11R310123F3HHHRHHHHHH332111HH3 2HHRRHHF3F22F22F3F23F3F2F2FHHHRRHHH21 ((.(*(*(/*(**.(((.*(/(****((((((%((((%(((%(((((%(%(((((*(*88@:CJCLLLLLMLMLOMLMMMMTMMTSMSMSMMMMMMTSMSSTMMTMMMOMMOMG@@=/*(((((((((((((((*(*((((((%(((((( . ./2/=2=F=F@FF@F@FFG@FGFG@GFG@GFGG@GGGGGGGGGGGGGOGOGGGGGGGGGGGCOGGGOGGC@GGCGGCOGOOOOGOOOGOOGOOOOOOOOOTTOTTTTVTVTVTVSVVVYYVYYYY[\\\ZYYWYWYWYYZZZZZYWVTVTVTVVSVTTTMTSVYX[Y[X[YXYXYXY[X[Y[[Y[[Y[[Y[YXYY[XY[YYXY[YY[Z[Z\\\\\[Z[ZYZYYYYYVWTQOOOGFGFFFFFFFFFFFFFFFFFFFFQWR/.  /HHHHFF2HFHHHGHRHRHRHRQRRHRQRHRHRHRHRHQHHHH3F2221.2HH02HH01123HHHHHHRHHHH3H322113RH0 .1FHQRRRHHHHF3FHFHFHFHHHHRHRQRRHH2. ((((.(/*(*/*(**((*((***(**(*((((((%((%(((((%((((%%((((**&*8@9CL:LLLLLLLLMLLMOMMMMTMMTSTMTSTMTMSTSVSSTSMMMMMMTMLOL@@=//*((((((((%(((((((((((((((((((   ...1/2=2=F=FF@FF@FF@F@FGFG@GFG@GFG@GGGGGGGGGGGGOGGGOGGGGGGGGGGGOGGCGGGCGG@GGCGLGGOOOOOOOGOOOOOOOOOOOTTOTTVTTVVVVVVVVVVYVYYXYY[Y[YZ\\[ZYYWYYZYZZ\ZYYYVVVTVVVVVYVVSTTSVY[[Y[X[Y[X[Y[Y[X[[[[[[[[[[[[Y[[[[Y[[Y[[[Y[Y[Y[Y[\[\\\\\Z\YZYYZYYYYWTTQOGOGFGFFFFFFFFFFFFFFFFFGQWH=.( ./HQRHFFFFFHHHHHHQHRQRHQRQRHRQHRQHRHRHHHHHFH2F221.3HH 3R311132H3HHHRHRHHHH3H31112HH2 .2FHRRRWRRRQRQRHRQRQRRRRRRHHF2.  ((*((**(****.**.**(***(**(*((((((%((%((((((%((((%((%((**88@:CLLLMLMMMMMTMMSTSTSSVSSVSSVSUSSSVSVUVUVSTMMMMMMOLLCC@==/*.(((((((((((((((((%((((((( ..../2=2F@FF@F@F@GFG@GFGFGG@FGF@GFGGGGGGGGGGGGGGOGOGGGGGGGGGGGLGGGCGGCGC@GGCGGLOOOOOOOOOOOGOOOOOOOOOTQTTTTTTTVTVVTVVTVVYYYYVYYY[Z\ZYZTWYWZZZZZYYYTTTTVTVVVVVVSTTMVVXY[X[YX[YXYXYXY[X[Y[[Y[[[Y[YXYXYX[YX[YXY[YYYYYYYYZ[\[\\Z\Z\YZYZYWVTTQOOGGFFFFFFF=F2F=F2F2=2=2GRRG1(  2FHRHFF3FFFFHHHHQHHQRHRHRHRHRHRHHRHHHHHF3F3221.1HH32HH001233HHHHHHHHHHHH323112HH3  .2FHHRQRRWRWWRWRRRRQHHF2..(((((((/(*/***(**(*(*(**(**(((((%(((((((%(((((%((%*(***889LCLLLLLLLMLMMMLMLMMMMTMMMTMMTMSMMSSSTSSTSMMMMMMLLCCC@@==/*((((((%(((((((((((((((((  . .../2=F=F@F@FFF@GFGFG@GFGFG@GFGG@GGGGGGGGGGGGGGOGGGGGGGGGGGOGLGGOGGCGG@GCGGLGOOOOOOOOOOOOOOOOOOOOOTOTTTTTTVTTTSTTTTTTTVSVVTVVYZZ\YWTWWYZZZYWTTOTOTOMOMTVSTTMOLMTVVVVVSVVVSVSTSTSVVVXVYXYVYYVVVVVVVVYVVVVVVSVVVTVVVYYVZYZZZZZ\ZZZYZWWTWOOGGGGFF=FF2F=2F2F=F2F2FHQRF.  2FHRHHF3F2FFFHHHHHHHHHHHHHHHHHHHHHHHF3F322211 1HH23H3101123H3HHHHRHHH333221013R3  .222FHHHHHHHHH222. ((((((*(***/**(/*(**(**(*(((((((%((((%((((%(((((%%((***89@CLLLLLMLMMMMMMMMMMMMSMTSMMMMMMMMMMTSSMSMTMMMMLLLCCCC@===**(((((((((((%(((((((((  . . .2/=FF=FF@F@GFGFG@GFGGG@GFG@GFGGGGGGGGGGOGGGOGGGG@GGGGGGOGOGGGCGGG@GGCGGCGOOOOOOOOOOOOOOOOOOOOMOOTOTTTTTTVTVTVTSTVTSVTTTVVTYYZ\ZYWWYZZZWTTOTOOMOOMTTTSTTMOMOMTSVSTVSVSVTSTSTTMSVVVUVYXVVVVVVVSVVVVVVSVSVTTSTSTVSVVVVTVVYZYZZZZZZZZZWWTWQQOGHGFHFHFHFFHFHFHGHQRH/  /FHQRHFF3F3FFFHHHHHHHHHHHHHHHHHHH3H3F32221. 02FH02H3001123HHHHHHHHHHH33221102HH1 1 1 1. . ((((((((/(**/**/**(**(*(**(*((((%((((%(((%(((((%((%(*(*88@:CLLLLLMMOMMMMMMMMMMTMMTMTMMMTMMMMSMSTSSMTMMLLLCCCCCC@==/***(((((%(((((((%((((((  . .. ../2=F=F@FF@FFG@GFGFGGGFG@GGFGGGGGGGGGOGGOGGGOGCGG@GGGGGOGOOCOGGCGG@GCGCOGCOOMOOOOOOOOOOOOOOOMOOTOTTTTTTTTVTTTTVTTTTTVTVTTVTYZZ\ZZZZZWTTOTOOMOOMOTSVVVTVTMMTTVVVVVUVTVSVTSTSSVTVUVVYXYXYVVVVVVVVVVVVVVSVTVTSTVTVVTVTTTTTVVWYYZYZZZZZZZZWWWWQRQQQQRQRQRQRQRQRRQF  .2HRQRHFF22F3FFHFHFHHHHHHHHHH3HFF3F3222111 2H3 3H30 02323H3HHHHH3H3H32211 2HH2((((((((**(**/****(*((*(*(*((((((%(((((((%((((((((((((*(*8=9CLLLMLLMMMMMMMMMMMMMSTMSMMSMMSMMMMSTSTSMMMMLLLCCCCCCC@==8/*(((((((((%%(((((( . .. .......1/2=F=FF@FF@GFGFG@GFGGG@GFGG@GGGGGGGGGGGGGGOCGG@GGGGGGGOGOOGGGOGCGC@GGCOGOOOOOOOOOOOOOOOOOMOOOOTOTTOTTVTVTVVVVVVVVVVVVVVVVYYY[Z\ZZZYWTTVTTTTTTTVVYXYYXYXVVVXYXY[XYX[YXYXYXYYXYXY[X[Y[Y[YXYXY[XY[XYXYXYXYXYXYXYXYXYXYVVVVVVTTVTYWYYWYWZWWWWWWWWRWRRWRRQRQQHQHF.  .2FHRRRHHFH2F3FFHFFHFHFHHHFFHF3F3F22221. 0.3H33H3 011233FH3HHHHH3H33221111FRH (((((((((((/*(**/****(*((**(*(*(((((%(((%(((((%((((%(***8@9CLLLLMLMMMMMMMMMMMTMSMTMMMMMMMMMMMSTSMTMMLLLLLCLLCCGC@@=*8(((((((((((((((((( ...//././../1///F=FF@FF@FF@FGFGFGGG@GGG@GFGGGGGGGGOGGGGGGGCGG@G@GGOGOOGOLGOGGCGGG@GOCOGLOOOOOOOOOMOOOMOOOTOTOTTTVTVVVVVYVYVVVVVVVVVYVYYYYY[\Z[ZVTVTVTTVTVTVVVX[[[[[YXYXY[[[[Y[[[Y[[X[Y[XY[X[Y[[X[[[[[[[[[X[[Y[[[[Y[X[YXYX[Y[[[YXYXVVYVVSVTVTVTYVTVTTWOQOQQHGHGHFGHFF2F2//. .2FHRRRHHHHFFF3FH2HFFF3HF3F3FF322222.1 01FH13H30 111323HH3HH3HH3F33211.12HH2 ((((((((((((*((**/**(***(*((((((((((%((((%(%(%((((%((((((*(*8=@CMMMMTMSTSTSTSSSSSSVSVSVSSSSSSSSSSSSSTSMMLLLLLLLLLLLG@@@=8*(((((((((%(%((((( ...././//2/.//2/2=F=F@FF@FGFG@GFGGGGG@GGFGG@GGGGGGGGGGGGGCGG@GGGGGGGGOOOOCGGOG@GCGCGOGOGOOOOOOOOOOOOOOOOTOMOTOTOTTTTTVTVVTVVTVSTVTVVVVVYYYYYYZTTTTTOTTTTMTTVVYYYXYYXYYXYXYXY[XY[X[YXYXYXYXYXYXYX[Y[XYXYXY[Y[XYXYXYXYYXYXYYXYXYXYVVUVUTSTTSTTSTTTOOOGCGCF===/////......    12FHRRRQHHHHFF3F3F3F3F3F2F222222.11.012H303H3 0 113233H3H3H33H322111 12HQF (((((((((((((((((**(*/***(*(*(((*((((((%(((((((%(((((%(***88@CLLLMOMMMMMMMMMMMMSMSMSMMMMMMMMMMMTMSMMMMLLLLMLMLMOCC@@=8/(((((((((((((((((((( ...../////1//2==FF@F@FFGF@FGFG@GGG@GGFGGGGGGGGGGGGGGG@GGCGG@GGGGGOGOOOOGLGGCGGCGGOCOGOOOOOOOOOOOOOMOTOOOTOTOTTTTTTVTVTTVTSTTTTTSTTTSVTVVYYVTTOOOOLOOLOLLOMTVVYVYUVTSVVVVVVUYUVVSVVSVSVVSVVSVSVVVSVVVVVUVVVVVVVVVVSSVTSVSTUTVMTMTMTMMTMOMMMMOMMOLCC@@===/...(..  .22HHRRRRHRHHHF3F3F3222222222121222FHH13H20 0 123233F3H3H33231111 12HH1((((((((((((((((((.***(**(***(((((*((((((%(((((((%((%((((***8@@CLLLMMMMMMMMMMMTSMSMSMTMMMMMMMMMMMTMSMMMMLMMMMMMOMGCC@@/**(((((((((((((%(((((( .././/2///=2==F=FF@FF@FGFG@GFGGGGGGGG@GGGGGGGGCOGGGGCGGGCG@GGGGGOGOOOOOGLGGCGCGCOGOCOOOLOOOOOOMOOOOOTOMOTOTOTTTTTTTVTVTTVTSVTTVTSTTVVVVVVVTOOLOOOLOLGLGLTMVVUVSVTSTSTVSVVSVSVTSTSTSTMTTSTMTTSSVVSVSVSVSVVSVVSSTSTSTSTSSTSSSTMMTMMTMMMTMOMMOMMOLGC@@==*/((( (  .12FHHRRRRRRRHHHHHHHFHF3FF3HFHHHHH312H30 1112323232323211110 2HH2 (((((((((((((((/((*(*(/*(*/(*(*(*(((((((%((((%((%((%(((**8=9CCLMLMMMMMMMMMMMMMTMMTMMMMMMMMMMMMSTSTMMMMMMMMMMMOLGC@@8**((((((((((((((((((((( ...///=2=2=/=F=F=FF@FFF@GFGF@GFGG@GGGGGGGGGGGGGCGCGCFGCGG@GG@GGOGOOOOOGOOLGGCGGCGOCOGOOOOOOOOOOOTOOTOOTOMOTTTTTTTVTTVTVTSTVTTSTVTVTVTVVVVTTTOOLOLOCOLGLOMTVVVVSVTVSVVSVVSVVTVVSVSTSTSTSTSTSTSTSVTVSVSVVVVVUVVSVSVSSTSTSVSTMMTMMTMTMTMTMMMOMMMOLC@@@==**(.((  122HHHRRRRWRRRRRRHRRHRRRHRHH31 3H10 0111232323232111110 0 0FHF (((((((((((((*(*(*(.(*(**(**(**(((((*((((((%(%((%((((%(*(8=8@CLLMLMMMMMMMMMMSMMSMMMMMMMMMMMMMMMSTSTMMMMMMTMMMMOCG@==*/*(((((((((%(((((((((((  .////=/=//=2=F=F@F@FGF@FGFGFG@GGFGGGGGGGGGGGGOCGGGG@GGGCGG@GGGGOGGOOOOOOGOLGGCGGCOGOGLOOOOOOOOMOOOTOOTOTOTTOTTTTTTVSTVVVVVTVVVTVVVVVVVVYYVVVTMOOMMOMOMMTVVXYXYYXYXYXYXYXYXYXXYXYYXYVVXYVVVVVXYYXYXYXYXYX[YXYXYXYXYXYXYXYXYUVVVVUVVUVSVSTSSTMTMLCC@@==8/(((  .122FFHHHRHRHRRHRHHHHH221 2H10 01112122212121111 0 12HH2((((((((((((.(*.(((((*.**(/(*.*(((((((((%(((%((%((((((***=@@CLLMMMMMMMMMMMMSMMMMMMMMMMMMMMMMMTSSMSTMMMMSTMMTLCG@===***((((((((((((((((((((  .///==F=2=F=F=F@FF@FF@FGFG@GFGFGG@GG@GGGGGGGOGGCGG@G@GCGGGG@GGGGOGOOOOOOOOOCGGCGCOGLOOOMOOOOOOOOOTOOTOMOTTMTTTTVVVTVVYVVVVVVVVVVVYYYVYYXYYYVTTTMTTMTTMTTVX[[[[X[Y[[[[[[[Y[[X[Y[XYXYXYXYXYXYXYXYXYX[Y[[[[X[[X[X[XY[X[Y[[X[XYXVXVVXVVXVVSVSVSVSSTLCC@@@=8/((((  . 1.12222F222222.1 3H10 011111121121110.1 2HHF.(((((((*(*(*((***(*.(**(**(*(*((((((((((((%(((((%(((%(((***8@9CLMMMTSTSSSSSSSMSMSSMSSMMSMSSSMSSSSSTSTSMTMTMSMMMOC@@@=/=**(((((((((((((((((((((((  .///=2==2===F=F@F@F@FGFGFGFG@GFGGFGGGGGGGGGGOGGCGG@GGGCGGCGGGOGOGOOOOOOOOOOCGGCGOOCOOOOMOOOOOMOOOOTOTOTOTOTMTTTTTSVTVTSTVVVSVVVSVVVVVVYVVVSTOMOLOMOLOMTTVYXYYXYXYXYXYXY[XYXYXYYXYXYXYVVXVVVVVXYXYXXYXYXYXYXYXYXYXYXYXXYXVUVSVUVUVUVSSSTSTSMTMLLC@=@88/(((   1H10 0 10111110110 1 0 3QH.((((((.*(/*((/(*((((*/(*.(*(*((((((((((%((((%(%(((((**8=9CCLMLMLMMMMMMMMMMMTMMMMMMMMTMTMMTSTSSSTSSMMSTMTMLL@@@===/*/(((((((((((((((((((((((( ..//=/=/=2=F=F@FF@FGF@FGFGFG@GGFGG@GGGGGGOGGCGGGCG@GGGCGGGCGGOGOGOOOOOOOOOGCGLGOCOGLOOTOOOLOOTOTOOTOTMOTTOTTMTTTVTTTVTVTVTVSTVTVVTVSVTVTTMOOLGLGCOCOCLOMTVSVSVSVVVYXVVSVVVSVTSTTTSTSTMTMMMMMTMTMTVUVVVVVVSVTSVVSVUSVSTSTMMMMMMMMTMTMMMMMMMLLLCC@@@=8**.((1H100 0 0 0 0 0 0 0  1HH2(((((((*(*(**(*(*(*.((*(**(((.*((((((((((((((((((%(((((**8=9CLLMLMMMMMMMMMMMMMMMMMMMMMSMSMTMSSTSSSTSMTMSMMOLLG@@@==/8*(((((((((((((((((((((((((  .../=2=2=F=F=F@F@FG@FGGFG@GFGG@GFGGG@GGGGGCGOGGCGGG@GGGOCGGGOGOGLOOOOOOOOOCOGGLGOOLOOTOMOOOOMOTOMOTOTOTMTTMOTTTVTSVTTSTVSVTTVTVSVVTVTTTTTMTLOCOCOCOLGLLTMTTMTMTVUVUVVSVSTSTSTMTSTMTMTMMMTLMOMMMTSTSVSSSSTSMSTSSTSTSSTSTMMMMOMMTMMMMMMMMMMMLLLCLC@==8/*((1H3110001 0 0 0000  .2H3 ((((((((.**(/*(.*((*(((*(**((((((((((((%(((%%%(((%(*(**8=9CLLMLMMMMMMLMMMMMMMMMMMMTMSSSMSMTSSTSSSTSMTMTMLLLCCF@@==/=**((((((((((((((((((((((((  .../=/=2==F=F=F@F@FGG@GFGFGFGGGFG@GFGGGGGGGCGGGGCGGGCGOGGCGGCOGGOGOOOOOOOOOCOGOCOOOLOOTOOOMOOTOOTTMTOTTOTMTTTTTTSTTTVTTTTVTVSVTVTVVTSTTMTOMOLLCOCLCOLGLOMMMMOMSVVVVUVVVTVVSTSTMTSTMTSMTMMOMMOMTMSTSVUSTSTMSTVSVSVSSTSSSTMMMMMTMMMTMMOMMMMMLLLLCCC@=88*/((03HH3H3H33322110 0 0    1HH.(((((*((**(*(*((((((((*((*(((((((((((((((((%(%((((((((**89CCLLMLMMMMMMLMMMMMMMMMMSMSMTSTSSMTSSVSTSTSMTMMLLLCCC@@@===*/*(*((((((((((((((((((((((((  .. ...=/=2==2=F=F@F@GFGFGFG@GGFGG@GFGG@GGGGGGLGGOCGGGCGGGCOGGGOGCOGOOOOOOOOOOOOCOGOLGOOOMOTMOOTOTOTOTOTTMOTTTTTTTTSTTVTTSTVTSTTVTVVVVVSTTTTMTTMTOLOMOMMOMTMTSTTVSYXY[XYXYXXYXYXYYVXYVXVVVVSVSVSVSVVVXYXYXYUVVVXYXYXYXYXYXYUVVSVSVUVUVSSSTSVMMMLMLLLLC@=8=**((00223HHHHHRHH3H2211 0  .2HF.(((((((((/(*/(*(.*(((((((((*((*((((((((((((%((((%((*(**=9C:LMLMMMMMLMMLMMMMMMTMMTMSTSSSTSSTSSVSSVMTSMMOMLLLCLG@@@@==*/**((((((((((*(((((((((((((  (......../////F===F=F=F=F@F@GFCGGGFGG@GGGFG@GGGGG@GGGOGGGCGGGCGGOOGCGGOGLGGOOMOOOOOMOGOOCOGOLOLOTTOOOMOTTTOTTOTOTMTOTTTTSTVVTVTVTVVVVVVVVVVVXVVTSTVTTTTMTMTMTTTSTTSTSVSVV[X[[[[X[Y[Y[XYXXYXYXYXYXVVVUVVUVXYXY[XYXYXVXYX[X[XY[XXYXXYXVUVUXVXVUVUVUSVSSSMMMMMMLCC@98/* 002323HHHHHH3F2211.1 . ./HH2(((((((((*(*(*((((((((((((((((((((((%((((%%(((%((((*****88@:LMMMMMMTSTMSMTSSSSTSSSSVSSTUSSTSSTSSVSSSTMMMMMLLLLLLOCG@@=88/**((((((((((/(((*((((((((((. ..(././/.////===2=/=F=F@F@FG@GFG@GGGGGG@GGFGG@GGGGCOGCGGGGCGGGGOGOGCOGOGOOOOOOOOOOOOOOGOLGOOOOMOTTMOOTMOTTOTTMTOTTTTTTTTTTTSTTSTSTTVSTSVVVVTVTMTMTMTMTOMOLOLMMOMMTMMOMSVVXYXYXYYXYXYXYXYYXYYXVVUSSVSTSVUVVXYXVXYUVUVVXVYXYXXYXYXYUVSVUVUVUSVSSSTSSMTMMMMMLMLLC9=8*((01133HHHHHH2F22/1...2FQF (((((((((((.(*(/((((((((((*(((((((((%((((%%(((((*(*(8=99CLLMLMLMMMMLMMMMTSMTMSTSSTSVSSTSSTSSVSTSTMTMMMMLMOMLOLC@@=8*/*(((((((((/(/((((((((((((( ...(....//.//=2=/=2==F=F@F@FG@GGGGGG@GGGG@GFGGGGGGGGOGOGGCGGGCGOGOCGGOGOOGOOMOOOOMOOOOOGOLGOLOOTTTOTOTOTTMTOTTTMTTTMTTSTTTTTSTTTTVSTTTTSVTTVMTMOOLOMOLLOCOLGLOCOCCGLCCLLVVUVVVUVUVVSVVSTSTSMTMMMOMLOLLMOMMTMTMTMMTMMMTSSTSSVSSSSMMMLMMMMMMMLMMMMTMMMMMMMMLLLLLC9=**0133HHHRHHHF222=HHF (((((((((((((*(((*(((((((((((((((((%(((%((%(((((((**8=9CCJLLMLMMMMLMMMTMSSMTSSTSSTSSTSSTSVSVSVSTMSTMTMTMMMTMOLC@==**/*(.((((.*(**(/((((((((((( ( ....////////==2==F==F@F@FG@GFG@GGGGGGGGG@GGGG@GGCGGGGOGGGCGGGCOGGCGOGOOOGOOMOOMOTOOOOGOGLOGOMOTTTOTMTOTOTTTMTTTMTTTTTTSTVTTSTVTSTVSTSTTSVTSTMMOMOLOOLLGLCOCOCLOCLCCCLOSSVSVSSTSSTSSTSTSMTMMTLMLOLLLLOMMMMMMMLMMMMTMMTMSTSSTSTSMMMMMMMMMMLMMMMMSMMMMMMMMMLMLLCC9**( 22FHHHRHQHHQHF ((((((((((((((((.(*(((((((%(((((((((((%((((%%(((%(((((**=9@9CLLLMLMMMMMMMMSTSMSTSSTSSTSSTSSSTUSVSTSSTSSTMTMTMMMMOLC@=**/(*(((.*((.*.*(*(((/(*(/(( ....(///./////=2=/=2=F=F=F@@FG@GGGGGGGGGGGGGGGGGG@GGOGOGGGGGGG@GOOGOGCGOOGOOOOMOOTOTOOOOCOOGLOOOMTTTMOTOTTMTOTTTTTTMTTTTTTTSTTVTSTSTTVTSVTVTSVTTLMOLOLLOCLGLCOCOCLCGCCCCMTSVVSVSVSVSVSTSTSTMTMMOLMLLLLLLMMOMMMLMMOMTSTMTMSTSSSTSTMMMLMOMMOMMLMMMMTMSMMMMMMMLLLLL:@8/(( .12FHHGHHH.((((((((((((((((((((*(((((((((%(((((((((%((((((((*8=9@:CLLLMMMMMMMTMSMTSTSSTSSTSSTSSTSVSVSVSVTSTSSTSTSTMTMMLL@@8/**.*.*(((/(**.*/*/*(*/(*(. ..////========2=F==2==F=F=F@FGFG@G@GGGGGGGGGGGGGGGGCGGOGGGGGGGGGCGOOGCGGOOOGOOMOTOTOTOOOOGCOGOLOOTMTTTTOTMTOTTMTTTMTTTTMTTTTTSTTSTTSVTSTVTSVSVVVTTMOMLMOLOLMOLLMOMOMMOMOMSYXYXYXYXYXYXYXVVVXVVUSVSSTSSTSTSVSSTSTSTUSVUXVUVUVXYXXYXVUTSVSVSSUSSSVSSSSSSSMSMSMMMMLLLC:88(( ../2/// ((((((((((((((((*(/(((((((((((((((%(((((((((%(((((*8=9CCJLMLMMMMTMSMTSSTSSTSTSSTSSTSSTSVSVUVSSSVTSSTSTMTMTMOLC@=**/**(/(/(*(/****/**/(*/(*(   (//=====F@F=F==@=F=F=F=F=F@F@FG@GFGGGGGGGGGGGGGGCGGGCGGOCGGG@GG@GOGOGGGGOGOOOOOTTTOTOTOOOCOGOOGLOOTTTTOTMOTTMTOTMTTTTMTTTTTSTVVTSVTVSVVVVVVVVVVVVSTMTMOTMMOMTMTMMOSTMMMMTVX[[[[[X[Y[XYXYXYXYXVUVVSVUVSVSSTUVVUSVSVUVUYXXYXYXX[X[XYXXVUUVUVUVSVUVUVUVUVSUSUSSSMSSMLLC:@8*(  ((((((((((((((((((((((*((((((%(((%(((%(((((((((((((*(/8@9CCLMMMMSTSSVSVSVSUVSVSSTSSTSSTSSTSVVUTVSVSVSTSTSTMTMMMLCC@=8**/**(*/**/*/*=**/**(*.*(    .///=/==@=====F====2@F=F=F@FGF@GFG@GGGGGGGGGGGCGGCGGOGGGGGGGGGGGOGOOGGOGOOOOOMOTTOTOOOOLOGOCOOOOMTTTTTOTOTOTOTTTMTTTTTTMTTTTSTSTSTVTSTSTSTSTVSVSTMOLOMLOLOLOLOLOMOLOMOMMVXYXYXYXYXYXYVUVVUVSSVSSTSSTSSTMMMSSTMTMSTSVUVXVXVXYXYXYXVUSVSSSSSSSSSSSSSSSSSSSMMMMMMLLL9C998=8((((((*(((((((((((((((((((((((%(%((%%(((((**=9@CCLLMLMMTMSMSTSSTSSTSSTSSTSSTSVSVUVVVSVSVSVSSTSTMTMOLLC@@==**/*/**/*=*=**/***.*/(/(  (.////=/=/2///=F/F==F=F=F@FF@FG@GFGGGGGGGGGGGGCGGGCOGGGGGGGGGGGGOOOGOGGOOOOOOTTOTOMOOOOLOGOLOOOOMTTTOTOMTOTMTTMTTTMTTTTMTTSTTVTSTSTSVTSTSVSTSTSTMOLLGCCGCCLCOCOCCLCCCCLTVVUVUVSVSSTSMMMOMMOMLLLLLLMLLLLLCLLLLLCLLLLMMMMMMSSTSSSSMMMLLMLLLLLLMMMMMMMMMMMMMMLLLLJC9@9@888*((.((((((((((((((((((((((((((((%(((((*88@9CLLLMMMSMTSTSSTSSTSTSSTSSTSVSTSVVVUVVSVVTVTSTSSTMMLOLC@@@=@8*/*=*=8/8=8/8/(*.*/(*.  ..////=/=/=2===2==F=F=F@FGFG@FGFGGGGGGGGGGGCGGGGCGOGGGGGGGGGGGGGOOGGOGGOOOOTOTTOOTOMOOOOCOOOOOOTTMTTOMOTMOTOTTMTTTTMTMTMTTSTSTTSVSTSTSTSTSVSVTSTLOCLGCCCGCCCLCCCGCCCGMMVUVSVSSTSTMMOMLMLLLLLOLLLLLLLLCCCCCLCCCCCLLLMMMMMTMSSTSTMMLMLLMLLLLMMMMMMSMSMMMMMMLMLL:C9@99@888**((((((.((((((((((((((((((((%((((((((/8=@:CCLLMMTMSSSTSVSSVSTSSTSVSSSSVSVUVVVVVSVVSVTSVTSTMOLLLCC@@@@@8=8=*=8/8/8/*/*.**/(/  ...///=/=/=///=F==F=F@F=F@FG@GFG@G@GGGGGGGCGGGGCGGOGGGGGGGGGGGGGOGOOOGOGOOOOOTOMOTOMOOOLOGOLOOLOMTTTOTOTOTTMTTTMTTTMTMTMTMTTSTSTSTSVTSTSTSTSTSVTMMOCCCGCCCGLGCLGCCCCCLMVVUVSSVSSTSTMMLOLLLLLLLLMLLLLLLLLCCCCLCLCLCLLOMMMMMSTSSSTMMLLMLLLLLLMLMMMSMSMMMMMMMLLJCC:@:@9@9=88**((((((((((((((((((((%((%((((((*=8@CCLLMMMTSTSVSSVSTSSTSSVMVTVSVSVVVVUVVVSVVSVTSTTMMMOLLCCCCC@@@=8==8==*=*/*(*(/.(/(   .( .     .//===F=F===F/F==F==F@F@F@GFG@GFG@GGGGGGCGGGGCGGOGGGGGGGGGGGGGGGOOOOGOGOGOOOOOTOTOOTOOOOLOOOOOOOOTTOTOLOTOMTOTMTMTTTMTMTMTMTTSTTSTSTSSTSTSVTSVSVSTLOLGLOLOLOLLOLMOLMOSVYXYX[YXYXYXVUSTSSTMTMTSMSSSSSMSMMMMMLMMMMMMTSSTSVSVUYXYXVUVSSSSSSMSMSSSSUSSUSSSSMMMMLLJCL:C:C:C:C9=8*(((((((((((%((((((%((((((.**=@CCCLLMMTSSTSSVSVSTSSVSTSVSVSVSVUVVVUVVVSVVSVTVTTMMOMOLLCLLCC@C==8=/8=/*/*/.*.*.*(...(..(. (. ...//===F@@@@F===F@F=F@F@F@FG@GF@G@GG@GCGCGGGGGGGGCGGGGGGGGGGGGGGOGGOOOGOGOGOOOOOTMOTOOMOOOOOOOOOMOMOTTMOOMOTOTMTTTTTMTTMTMOTMTSTSTSTSTSVTSVSVSVVVVVSMMOLLOMLMLMOMMOMMTMVX[X[[X[X[XYXYXVSVSSSTSSTUVSVSVSTSSMMMMLMTMTSTSSVUVUXYXXXXXYUVUVUVSSSVSUUVUXVXUUUSSSSMMMMMLLLMLMLLL:C98**((((((((((((((%(%%(((%((%((((((((((*(*(/***=@CCLLMMSSVSSVSVVUVSVSTVSVSVSVUTVUVVVVUVVVSVTVSTSTTMMTMMOMLLLLC@@@==8/*/*/**.(/(.(/ .(. (..( (. .(.//====F=F=F======F=F@F@F@G@GG@GG@GGGGGGGGGGGCGGGGGGGGGGGGGGGGGOGOOOOGOGOOOOOOOTOOMOOOOOOOOLOOOOOMOTTOMOOMTOTMTTOTTMTMOTMTMTTSTMTSTTSMTSTSTSSTSSVTMOCLCLOLOLOLLOMOLMMVVX[YX[VXYXYXVSVSMTMTMMMMTMSMTMSMMMMLLLLLLMMLMTMSTSVSUYXYXYUUSVSSSSSTMSMSSUVSSSSSSMMMMLMLLJLLMLJLLL:9@8**((((((((((((%(%(((((((((.(*=8@@CLLMMTMTSSVSVSVSVSVSVSVVUVUSVVVVVVVVSVVSVTVSTTSTTTTMOMOLLCC@@@=*//*/*/*/.(.*.(    .  ( ..//.//////=//2=2==2=F@F@FG@G@GG@GGCGGGGGGGGGGGGGGGG@GGGOGOGGOGOGOGOOOGOGOGOOOOMOTOOOTOMOOOLOOMOOMOTMOTOMOMOTMTOTMTMTMTMTTMTMTMTSTMTSTMSTMSTSTSTSTSOLCCGCCCCCLCCCCCCGLMVVSVVSVSVSSTMMLLLLLLCCLCLCLLLLCLCCCCC@@C@CCCCLCLLLLMMMSSTMMLLLMLLMLMLMLMMMSMSMMMMLLLJLLJLLLJCLLJCCC:9=**(((((((((((((((.((((/*=9@CCLLMTSSTSVSVSVSVSVVSVSVVUVTVVVXVVVVVSVVVSVTVSVTSTSTTMMMOCCC@@=*=*/*//*/*.(.*.   ....///////=///=//F=2=F=F@F@G@G@G@GG@GGCGGGGGGGGGGGGGGGGGGGGOOGGOGOGOOOOOGOGOGOOOOTOOOTOTOOOOOMOOOMOOTOTMOMOMOTTMTMTMTMTMTMTMTTMTSTMTMTSTMSTSSTSVSTTSMOCCCCCGCLGCCCCGCLOSVUVVSVSSSTSMMMOMLLLLCLCCLCCLCLCCCCC@@C@9@@CCCCCLCCLLMMMMMMMLMLLMLLMLLMMMMSSSMSMMMLMLMLLJLLJLLJLLL:C:@98(((((((((((((((((((((((((*/8@@CLLMMTSSTSVVVSVSVSVUVVSVVUVSVVVVVVVVVVSVVVVVVVSVTVTMTTMLLCC@@==*/*=*//*.*.(.(  ( ..../////==//=/2/=/=2===F@F@F@GFGCGGGG@GGGGGGGGGGGGGGGG@GOGOGOOOGGOGOOOOOGOGOGOOOOMOTOOOMOOOOOLOOOOOMOOTTTOMOMOTMOTMOTMTTMTMTTMTSTMTMSTMTSTMSTSTSTSMTMTLLC@GCCCCCLCGCCCLTSVSVUVSVSTSTMMMMOMLLLLLCLCLCLCLLCCCC@@@@@9C@CCCLCCLCLLMMTMMMLLMLLMLLMLMLMMSSSSMSMMMLMLKLLLLLLLJLLLL:C:=8**((((((((((((.((.(.*(=8@CCLLMTMVSVSSVVSVSVVUVSVVUVVSVVXYVVVVVVVVVVVVVVVSVTVTTMTMOL@@===8/*///*/*.*.*. ( (...(../*/====@=@==F@==2===2===F@F@FCG@G@GGCGGGGGGGOGGGGGGGGGGGGGOOGOOGGOGOGOOOOOGOOGOOOOTOTOOOTOMOOOOMOOMOOMOTTMOOMMOTTMOTMTMTTMTMTMTMTSTTMTMMTSTSTSSTSTSTSSTLCCLCLOLOLLMLOMTXYXYXYXYXYXVVUVVUVSSSSMSMMMMMMMMMMMLLLLLCLCLCLMLMMLMMSTSVSVUVSSSSSSSSSSSSSUUUUUUSSSMSMMSMSMMMMMMMLMLML:C8=*(((((((((((*/*(*/((*(/(/8@@CCLMMTMSTVSVSVSVSVVSVVUVVVVSVVVXVVVVVVVVVVVVVVVTVSVTTTTLCC@@===*////*/(/.(.( (.(.//////===@@F@F@@@F@F==F=F=F=F@@F@G@GGG@GGGGGGGGOGGOGGGGGGGGGGOOGOOGOGOGOOGOOOOGOGOOOOOMOOOMOOTOOOMOOOMOOOOMTTOMOMTMOTOTMTOMTMTMTMTMTMTMTSMTSSTSVSVSVSSVSVVSTLLOLOMMMOMOMMTVX[[[X[Y[XYXYXVXVXVXVVUVSSSTMSTSSTMMMLLLLLLLLLMMMMMMMMTSMSVUVUVUVUVUVUUSVUXVXXXXXVUUUSUSUSUSSSSSSMSMSMLLC:@8**(((((((((%(((((((((((/**/8**/*/**8/=9@CLLMMSTSVSVVVVVVUVVVUVVVVUVVVVXYVVVVVVVVVVVVVVVVSVTTSTMOCC@@@===*///*//(.(/( (..*.///====@F@F@F@=F===2==2==F@F@F@G@GG@GGGGGGGGGOGGGGGGGGGGOOOGOGOGOGGOGOOOOOGOGOOGOOOTTOOOTOMOOOOMOOOMOMOTMTOMOMOTMMOTMTMOTMTMTMMTMTSMTMTMSTSMTSTMTSTSMSTMOCLLLLOLLOLOLTUYXYXYXYXYXYXVVUVUVVSUTSSTMMMMMMMMMLMLLCCCCCCLCLOMOLLMLLMLMSSSSSSSSSSSSSSSVUUXVVUUSSSMSMSMSMMMMMMMJMLLJCC:@8=*((((((((((((*/(/***(/(/*==8@CCLOMTSTSSVSVSVVUVVSVVUVVVSVVVXYVVVVVVVVVVVVVVVVTVTTMOLLCC@@====/*//*/(/(.(. (.(////==/==/==/=//2==2==F=F@F@G@GCG@GGGGGGGGGOGGGGGGGGGGGOOGOGOGGGOGOGOOOOGOOGOOOOOTOTOOTOOMOOOOLOOOOOMOTTTOMOMTOMTMOTMTMTMTMMOMTMTMMTMTMSTMSTSSTMMTSTMMLCCCCCCCCCCCCLTUVSVVVSVSTSTSMMMMMMMOLLLLLLLCCLCCCC@@@9@@8@8@CCCCCC@C@C@@@CCLCLLLLMLLMLMMMMSSSSSMMMMMMMMKLMJLJLLLCJCL:C:@988/(((((((.((**.**(/(/*==@@CCLLMTMTSTVVSVVSVVVUVVSVVVUVYYXVVVVVVVVVVVVVVVSVTTMTMOLGC@@@==/=//*/./(.(. (.../////=/=/2==/2=/=/=2==F=F=@G@G@GG@GGGGGGGGOGGGGGGGGGOGOGOGGOOGGGOGOOOOOOGOGOGOOOTTOOTOMTOOOMOOOMOOMOTMTOMOMOMOMOMTMOTMMTMMTMMMTMMTMMTSMTSMSTSMTSMTSTMMLCCCCCCCCC@CLTSVSVSVSTSSTSTMTMTMMMLMLLLLLCLLCCCCC@C@9==@8=@9@C@CC@@C9@@@@@@CLLLLMLMLMLMMSMSSSSMMMMMLKMMMMJMLL:LC:LCC:C9@9=8((((%(( (((.(*/(/*(*/=8==@@CCLMMMTMSTSVSVVSVVSVVVUVVSVXYVXVVVVVVVVVVXYVVTSTTMOTLOLCC@@===/*//*/(/(/( .(////==/====2==///==/==2=@F=F@F@G@FGGGGGGGGOGOGOGGGGOGGGOGGOGOGOGGOGOOOOOOGOGOOGOOOTOTOOTOOOMOOOOOLOOLOTMTOMOMOTLMTMOTMTMMTMMTMOMTMMTMTMTMTSTSTSMTSMTMTMOCCCCCCCCCCCLSVSVSVSSSTSSTMTMMMMTLMLLMLLLLLLCCCCC@@@@9@=@8@@CCCC@C@@@@9=9@@9CLLLMLLMLMMMMSMSSSSMMMMMMKMMLLJLLC:LCJLC:C:C988/(((((((.((.(*.*/(/*/8==8@CCLLTMMOMTSTVSVVSVVSVVVUVVVVXYYVXYVXYVYYVYVVSTTTTMTMTLOLCC@@==///*//*.((/ .../===@=@F@F@@F@=F/=F/=2=F=F=@F@F@FCGGGGGGGOGOGOGGGGGGGGGOGOGGOGGOGGOOGOOOOOGOGOOOOTOTOOTOTOMOOOOLOOMOOMOTMOMOMOMOMOMTMMOTMTMTMMMMMTMTMSTMSTMMTSTMSTMSVSTMLLLLLLLCLLOSVXYXYXYXYXYUVUVXVVUVUTUSSSSSTMMSMMLMLLLCCCCCCCCLLLLLLLLCCCCC@CCLLMSSSSSSSSUUUUUUUUUSSSSSMSMMMMLMLLKLLMLLLLJC@98**(((.(((.((/*/*.**/*====@@CCLLMOMOMTTSVSVVSVVVVUVVSVVYXYXYVYVYXYVVVUVTSTTMTTTMMOLOCC@===///*/(/(.*  ( (.//=@@@@G@@G@@F@=F=F=F==F=F=F@@F@@GGFGGGOGGGOGGGGGGOGGGOGOGOGGOGGGOGOOOOOOOOGOGOOOOTOTOOTOMOOOOOOLOOOLOMOTMOMOMOMMOMMTMTMTMTMMOMTMMMMTMSTSSTSSTSVSVSVVSVSMMOLMOMOLMOSY[X[X[X[YXXYXYXYXYXVUVVVVSVSUSSTSSMMMLLLLLLCCLCLMMLMLLLLLCCCCCCCLSMVSUUVUUUXVXXXXXXXSUUSUSSSSMMMSMMSMMSMMMMLLC@9**(((((((((((((((((((((.((.(/*/*.*/*/*//8==9@@@CLLLMTMMOMTTMVSVVSVVVVSVVVUYYXYVYXYXYVVVVSVTTSTTTTTMOMOLCC@@==//*/*../(/ .(//==@F@@F@F@@F=====/=F=F==F@F@F@GGGGGGGGOGOGOGGGGGGGGGOGOGOGGOGOGGOOOOOOOOGOOGOOOTTOTOOTOOOMOOOOOMOOMOMTOMOOMOMOMMOMTMOMMTMMTMMOMTMTMSTMTMTMMMTMSTSSTSTMOLLCLCLLLLMVXYXYXYXXYXYXVXVXVUVSUSSSSTSTSTMMMMLMLLLLCCCCCCCCLLLCLCLCC@@@9C9CCMMSSSSSMUSUUUUUUUSSSSMMMMMKLMMLMMLMMLMKLLLC:@8=((((((.((.((.*(/*/*/*/*/==8=@@@CLLOMOMOMMTSVTVVSVVUVVVVVXYYXYVYYVVVVSVTVTVTTTMTTTMTLOLCC@==/*/*/.*..*   ./.=/====/==/=2=2======F==F@@FG@GGGGGOGOGOGGGGGGGGGOGGOGOGOGOGOGOGOOOOOOOOGOOOOOOTTOTOTOTOOOOLOOOMOOLOMOMOMOMOMOMTMOTMMTMMTMMOMMMMTMTMSMTMMMLMTMMTMTSMOLCCC@CC@C@LMVUVUVSVSVSSTSTSMMMMOMLOLLLLLLLLLCCCCC@C@C@@9=8=9@@@@9@@@9=88/8=88@:LLLLMLMMMSSSSMMMMMJLJLLJLLLKLLLJLLJLLL:LC9@88*((  ((.(.((.*.*(/*/*====@@CCCLLMOMOMTTSVSVVVVVVVVVVYXYYXYVXVVVVTVSVTVTSTTTSTMOTLOLC@@==/*/./(/(.  ....///=/=2==/===2=F==F==F=F@G@GGFGGGGGGOGGGGGGGGOGGGGOGGOGOGOGOGOOOOOOOOOOGOOOOOTOTOTOTOLOOOMOOMOOOMOMOMOLMOMLMOMOMMTMMOMTMMMOMTMMTSTMTMTMOMLMMTMTMTMMLLC@C@C@CCCMSSVSVSSSTSTSSMTMMMMLLLMLLOLLLLLCLCCC@CC@@@9==8=8@@9@@@8@==8/***8=8@CLLLJLMMMSMSSMSMJLL:LJLLJLMLMLLLJLMLJLLCCC9@8**(  (.((((.(/(/*.*./*====@@@CLLMMOMOTMSVVVVVSVVVVVVVXYXYVVVVTVSVVTVVTSTTTSTTTMTOLOCC@@=/*//(/.(.  ....//=/=/==2==2=======F==F@@GFG@GGGGGOGGGGGGGGGGGGGGOGGOGGOGOOGOOOOOOOOOGOOOOOOOTTOTOMOOOOMOOOOOMOOMOOMOMOLMOMOMMTMOMTMMTMMOMTMMMTMTMSTMMMLLLOMLMMMTMOMLCC@C@CC@CLTUVSSVSTSSSTSTMMMMOMMLOLLLLLLLLLCLCCCCC@C@@@9==8=8@@@9@=9@8=*.**8=9@:LLLJLMMMMSMSMKLLJCCJLLJMMMMMMLLMLLJLLJCLC9=8*((. (..(((((/(/*/(//*====@@CCLOMTMMTMTSVTVVVVVVVVVVYXYYVVSTVTVTVSVTVSVTTSTTTMTMOLLC@@==//(/.*.(  ...(/.////=F==F@@=====2=F==F=F=@G@GFGGGGGGGGGGGGGGGGGGGGGGGOGOGGOOGOOGOOOOOOOOOOOOOOOOTTOTOOTOOOMOOMOOMOOLMOMOMOLMOMTLTMTMOMTMMTMMMOMMMTMTMSTMMMLLLLMMMTMSTMMOLCLCLLLLMVXYXYXYXYXVXVUXVUVSVSSSSTSSTSMMSMMMLLMLMLLCCCCC@@@CCCCCCCCC@88=8@9CCLMMLMMMMSSSUSSMMMMLKLLMMSMSSMMSMMMMMMLMMLLCC9=8(((((((((( ((.((.(.(( ((..(/*.*.*.(//*====@@CCLLMTMTTMTVVVVSVVVVVVVXYVVSVTVTSVVVVVVVSVTVTSTTMTOMOLGC@@=///*..(.   .(.././////=/==F@@FF=F=F=F=F=F@=F@G@GG@GGGGGGGGGG@GGGGGGGGGGOGOGOGGOOGOOOOOOOOOOGOOOOOOOTOTOMOOMOOOOMOOOOMOOOMOMOLOMOMOMOMOTMMMTMMOMTMMMTMSTSTSTSTLLLLOMTMTSSTMTMLLLLLMMMVX[[X[XXXXXYXXYXVXVUVVVSVSVSVSSSTMMMMMMMMLLLLCCCCC@:CLCLCCC:C@=8@9CLLMMSSMSSMKSSUUSSSSMMMMSUUUUUUUSSSSSSSSSMMMMLC:=8((((((((.(( ((.((.((.(((.((.*((.((..(.*/*/(//*//*/*/==8@@=@CCLOMMTTSTVSVVVVSVVVVSVVVVVSTVSVTVTVSVVVVSVTTVTTTMTOMOLC@@===/.(/(. ( .*..(////////======F=========F@F@G@G@GGGGGGGG@GGGGFGGGGGGGGGGOGOGOGOOGOOOOOOOOOOOOOOOOOTTOOTOTOOOMOOOOMOOMOLOMOMOMOMOMMOMMMTMOMMTMMMOMMMTMTMSMTMLLLCCLLLLMMMMMLLLCCLLLOLSYXYXYXYYXYXXVUVXSVSSSSSTSSSSMMMMMLMLLLLLLCCCCCC@@@9@CC:@CC9@888=9@:LLMMMLMJLLJLMMMKMMMMKLMMSSSSSSSMSMMMMMMMMLLLC@9/*((.(( (((.((((..(((.(((.(.(.(/(.*.(.(/(//8===@@CLOMMTSSTVVSVVVVVVVSVTVUVTVSVTVTVSVVVVVVSTVMTTTMTMOLOLC@===*//(.( ..(......(/.././/=2=/=2=2==F=@F=FCGGGGGGGGGGGGFG@GGGGFGGGGGOGOOOCOGOOOGOOOOOOOOOOOOOOOOOTTOOMOTOOOMOOOOMOOMOMOMOMOMLMOMMOTMOMTMMMOMTMMMMTMSTMTMMMOCCCCCCLCCLLCLC@C@@CCCCCMVSVUVSSSSSTSMMMMLMMMOMLMLLLLCLCCCCCCCC@@@9@8@=88=*88=88=8=8/****=8@9C:LC:C9:999CJLLJLLLKLMKSMSMMMMLMJMLLMLLLL:C9=8(((  (   ( (.(..(.(.(*.(.(.(/(//8===@@CLOMTTSTVSVVVSVVVTTTSVVTVSVVVVVVVVVVVVSVTTSTSTTMOTMOLG@@==/=(/.(.(...//./..(./..//=/===2====F=@F@G@G@GGG@GGFGGG@GGFGGGGGGGGGOGOGOGOOOGOOOOOOOOOOOOOOOOOOOTOTOOMOOOOMOOOOMOOMOOMOLMOLMOMOMMTMMTMMTMMMTMOMTMMTMMMTMMLCCCCCCCCCCLCCC@@C@CCCCLMSVSSTSSTSMSMMMMMOMLMMLLLLLCCCCCC@@@@9C@8@8=8=8=8/**=8=888**/**/*89@C:CL:C9@9@99CJLLJMLMMMSMSMMKMMLMLMLMLLLJLCC9@8=(((    ( (   ( (  (.(.(((.(.(.(.((.((./(/*==8=@CCLMMTMTSVTVVVVVVVTMTTVVVVSVVVVVVVVVVVVSTVTSTTMTMTOMOCC@@===/*/(  (.///////////././//=2===/F===F=@F@GGGGGGFG@GGGGFGGGGFGGGGGGCGOGOOGOGOOOOOOOOOTOOOOOOOOOMOTOOMOOOOMOOOMOOOMOOLMOMOMOMOMMOMTMOMTMMOMTMMTMMMTMMMTMMTMLCCCC@CCCCCCC@C@@@CC@CCCMMSTSSTSSTSTMMMMMMLMMMMLLLLLCCCCC@CC@9@9@@9=8=88=8*=*=88=*=***(**=9@:C:CL::999@99CJMMMMKMSMSMMMMLMMMLMMLMLMLLLCC9=8**(        . (  ( ((..((.((.((.(.(.(.*.*/8==@@CCOMMTTSVVSVVVSVSTTTTTVVVVVXYXYXVVVVVSTSTTSTTTMOTMOLOCC@===//*.  (./=/======@=/=========2===2===F@F@GFGG@GFGGGGGGGGGGGGG@GGGGGOGOGOGOOOOOGOOOTOOOOOOOOOOOOMOTOOOTOOOOMOOOMOOMOOOMOMOMOMOMMOMTMMTMTMMMTMMTMOMMTMMMTMTLLCCCCCCLCCLCLCCLCCLLLLMMVUXVXYXVXVUVUVUVUVSVUVSSSSMMMMLLLLLLCCCCCCC9@C@9@@8@8@9@98=8=8=8@:CCLJLMMLLJC:C:LMSSSUSUUUUSSSSSSSMSMSMSMMMMLLLCC9=8(((      (   (( (((.  (.((.(.(((.(((.((.((.(.(.(.(.*.(.(.((/(/8==@@CLLOMTTSVVVVTVVVTSTTTVVVVVVXYXYVVVSVTVMTMTMTMTMTMOMOLC@@===/*/   (./====@@@@@@==@=F=F==F===F==F=F=F@GG@GFGGFGG@GGGFGGGG@GGGGGGOGOOGLGGOOOOOOOOTOOOOOOOOOOOOTOOTOOMOOOOOTOOMOOMOLOMOMOMOMOMTMOMTMMMTMTMTMMTMOMMMTTSTSTMLLCLLLLLLLLLLLLLLMLMLMMSVXYXXYXXXXYXXYXVUYUVUVUVSSTMMMMLMLLLLCCLCCCCC9C@9@@9@9@@9@8888@9@:LLLMSSSSMLJLLJMSXXUXUXUXUUUUSUUUSSMMMMSSSMMMLLCC@8*(( ( (  ((..((.( (( (((.(.(((.((((.((.(((.(.((.((.(.((.(/(//*/*./*/*/(//*/*/==@@CCLLOMTSVTVSVVVVVTVTSVVVXVYXYYXVVVSVTSTTMTOTOTOMTTMOLGCC@===/*  (./=====@@=@==@F======/====2===F=FGFG@GFG@GGGGGGGGG@GGGGGGGGGOCOOGOGOOOOOOOOOTOOOOOOOOOOOOTMOOMOOTOMOOMOOMOOMOOLOMOMOMOMOMTMOMTMMTMTMTMMMOLOMMMMMTMMOCCCLCCCLCCCCCLCLLLCLLMMSVSVXXYXYXXYXVUVUSVUVSVSTMSMMMLLLCCCCC:C@:@9@@9@@9@8=88=88**/**88@9CJLLKSMMLLJLJLLSUSUSUSSSSMSSSSSMML:LCLLMMLLLL:@8=*.(  (    ((  . (((.( .(.((.(((.(((.(.((*.(.(.(.(.*.(.(.((.((/*/*==@@CCLMOTSVTVVVVVVVVSVTVVYXVYXYVVUTVTSTTMTMMOTOOOTMOTLLO@@@===*(./*//=/=====/===/=//=2=====F=F=F@FFGFGGFG@GGGGGGGGGGGGGGGGGGOCOGOGOGOOOOMOOOOOOOOOOOOOOMOOOOOOTOOOMOOMOOMOOMOMOLMOMOMOMMOMTMMOMTMSTMTMMOLLOMMMTMMTLLCC@CC@@C@9C@C@C@CC@@CCLLMMMTSSSTMSMSMMLMMMMLLLLLLCCC9@@9@9=988=88/8=8**/*8*/**((((((((**88@9C:LKLL::@::CJMSMMMMKLKLMJMMMMC:@9@9@:CLL:CC9=8*((.( (   (     (. ( ( (  ( (.( ((.((.((.(.(.(.(.(/(/.*==@@COLMTTVTVSVVVVVVVVVVVVVXYXYVVSVTSTSTTMTOMOOMOOTTMOMLCC@@=/= .////=//====/===2====/===2==F=F=GFG@GFGFGGGGGGGGGGGGGGGGGGOGGOGOGOOOOOOOOTOTOOOOOOOOOOOOMOTOMOOOOOMOOMOOMOOLOMOMOMLMOMTMOMMTMMTMTSMTMMLLLLOMOMMTMMLCCC@C@@@C@@:@CC@C@C@CCLLLLMMMSSSMTMMMMLMLLMLMLLCLCCCC9@98@8=88*=***8*/8**/***(*((((**=899CJLLJL::@:CJMMSMKLMLKLMMMKMMLC9@8=9=:CLCCC9@8/*((  .    .    (  ( (.( (( (.(.((.(.(.(.((.(.(.(.(/*.*/===@@OLTMTVVVVVVVVVVXVVVXVYXYVUVTSTTVTSTTMTOMOOOMOOTTMOLC@@==/ .////====@===/====/==2=2==F/F=F=FFGFG@GFGGGGG@GGGGGGGGGGGGLGOGOGOGOOOOOOOOOOOOOOOOOOOOOOOMOOOOTOMOOMOOOLOMOMOLOMOLMOMOMMTMOMTMTMTMTMTMOLLCLLMMMTMMOLCCC@C@@@:@@C@CCCCC@CCCLLLMMTSTSMSMTMLMLMLMLLLLLCCLCCC9@9@89=88*=*/*8*/****(**(((((((**8=9:CJLMLL:C:C:MMKMMLJMLKLMMMMMML:8@8888@9CCC:@@8/* ( (.   (   ..( ( ( ( ( (( ( (..(.(.(.(.(/(.((.(.(/(.*/*===@@LOMTVTVVVVVXVYVXYXYVVVVVSVSTSSTSTTMTMTOOTOOTOTOOMOCC@@==  ..(//==@=@@G@@G=@@=F=====/F/==F=2F@FFGFGFG@GGGGGGGGGGGGGGGGGGOGOOGOGOOOOOOOOTOTOOOOOOOOOOOMOTOMOOOOOMOOMOOLOOMOMOLMOMOMLOMOMTMMMTTMSMTMMMOLCCLOMMTMTMMLCCCCCLCCCLCLLLLLLMMMMMSSSVUXVXVXVUVSSVSSSSSSSMMMMLMLLCLC:C@9@8@89=9@8@8=8=8**8***((*(**=9@:CJLMMSSMMLKMMSSSSSSSSSSSSSUSSSMC@9@@9@9CJLLCC9=**   (  (  (( (.( (( ( ((.(.(.(.(.(.(.(.((.(((.((.(.(.(.(.((.*.(.(.((.(.(/*./*/===@@COMTVSVVVVVYXYVXYXYVVUVSVSTSTSTSTMTTMMOOMOOTOTOOLLCC@==   (.(.//===@@@GCGC@G@G@@@F=/=2==2=F=F=FFGFG@GFGGGG@GGGGGGGGGGGGGGOGGOOOGOGOOOOOOMOOOOOOOOOOOOOOMOOOOTOMOOOMOOMOOMOLOMOMOMLOMMOMMOMTMTMTTMMTMOMLCCLLOMSTSSTMMLLLLLLLLMLLMMMMMMSTSSSVUXXYXXXYXXVSVSUVSVUSSSSMSMMMLMLLCCC:@9@9@9@@9@9=88=8=8*8********8@:CLMSSSUUSSSMSUUUSUUSUUUUXUUUUUUMLC:@@:@:@CLLLC8=*(  .( ((.( (( .(.((.(.(.((.((..(.(/.(.(.((.(.((.(.(.*.(/(.(/(/*.*./(./*.*.*.(.*.(*/.*/*///===@CCOTSVVVVVXVVYXYYXYUVSVVVVSVSVSTVTSVTVMTOTTOTOOOOCCC@==  . (.///==@@@CG@@C@==F==/=/==2==F==F=FFGFGFGG@GGGGGGGGGGGGGGGGGOGOGOOOGOGOOOOOOTOTOOOOOOOOOOOOTOMOOOOOMOOMOOLMOMOLOMOMOMOMOMOMMMMTMTMTMTMMMMOLCCCLLOMTMMTMLCCCCCLCCLLLLLLMMTMSMSSVUVXXVXUVUSSMSSSSMTSMMMMMMLMLCL:CC:@@88=8=8@8888=8*********(*(((*88C:CJMSMSMMMMKSSSSSSMSSSSSSUSSSSSLC9@9@9=99@9@88((   .( (( ( ( ( ( (. ( ( (. (.(..(.(.(( (.((.(.(.(.(.(.(.(.(.(.((.((.(.(.((.(.(.((.(.*///8/==@@COMTVTVVVVVVVXYVVVSSVSSVTSTSTSTTSTSTTMOOOTOTMOOGC@@==   ..(/./=/==@========//=/F/F=F=FF=FF@FG@GFGFG@GGGGGGGGGOGGGGGOGOGOOOGOOOOOOOMQMOOOOOOOOOMOOMOOOOTOMOOMOOMOOMOOMOMOMOMOMOMOMOMTMMTMTMTMTMMTLLCCCCLLMMTMLLLC@C@@:@CCC@CCCCLLLLMLLMMMMSMTSMMLLCLLLLLLCLCLCC:C9C9@9@8888/8*****/***((((((((((((**8@9:CJLJL:LCJMMMLMLLKLMMLMMMMMLMC88=8888=88*/*(   ( ( (     (.  ....  ( (.((.(.(..(.(.(.(.((.((..(/(//*//=/==@@COTTSVVVVVVVVVUVSVSTVSVSTSTSTSTTSTTSTTOMOTOTOOCG@@==    .(././=//@==/====/=2=/=F==F=F=F=FGFGFG@GFGG@GGGGGGGGGOGGGGGOOOGOOOGOOOOOOOOTOOOOOOOOOOOOOMOTOOMOOMOOMOMOOMOMOLOLMOMOMLMOMMOMMTMMTMMTMMOMOLCCCCLOLMOMMLCC@@C@C@CCCCCCCLMLMLMLMMMSMMMMMLLCCCCLCCLCCCC:@C9C@9@9=9=*8*/*/8/**/*(((((((((((((*88@:CJLC::LLKMMLMLMLMLMMMMMMMMLMC988=88*=*8/*((    (.( (.   ( . ( (  ( (... (( ( .(.( ((( (.((.(..(.(..((.(.(.(/(///=*=/===@CCOSTVTVVVVVVUVVUTSVSVSVSTSVSTSTSTVTSTOTOMTOOOCG@C==  ..(./////====@=====/=/==/==F==F=F=FF@FG@GFGG@GGGGGGGGGGGOGGGGGOGOOOOOOGOOOOOOMOOTOOOOOOOOOOOOMOOOMOMOOOOMOOMOOMOMOOMOLMOLMOMMMMMTMMMTMMTMMMMLL@CCCLLOMLMOLLC@@C@CCC@CCCLLLMLLMMMMMTSMSMMLLCCLCLLLLLL:CCCC9@9@9@@88=8*/8***/****(((((((((%((*889:C::C::LLKMMMLMLMLMMMMMMLMMLLC8=8=888=***.(  ...( (   ..( ( . ( ( (..  .((.( (( (.(.(...(.(..( (.(.(.(.*//=/=*====@CCOTSVTVVSVVVUVVSSVSVSVSVSTVSTSTSTTVMTMOOTOOOGCGC@@  ..(.//////======@@=@GCF@@=F====/=F=F@F=FF=FFGFG@GFGFGFGGGGGGGGGGOGGGOGOOOOOOOOOOOOOOOOTOOOOOOOOMOOOOOMOMOOOMOOMOOMOMOMOMOMOMOLOMOMOMOTMMTMTMMMOMTMOMOCCCCCLLMOMMMMLLLCLLLLLLLMMSSSSSSUVUVXVXVUUSMMLMMMMMSMMMLLLLLLLLLCCC:@9@9=8@89=8=888*********(*((((((%**@9C:LL:LCJMMUUUUUUUSUSUSSSSSSSSSMLC9@9@9@8=8**((   .(.( ...(..(..(..(...(.(..(.(.( (..(.(.(.(.(.*./(.(.(.(.(./(..((.(.(.((..(.*.//===/=/==@@GCMTTVSVVVSVVVSVVVVVUVVSVVSVTSTTSTTVMTTOTOMOGCGC@@ (.(.(//=====@=@@@@@@C@@LGC@@@F=F@F=F==F@FF=F=F@G@FGGFG@GFGGGGGGGGGOGOGGOGOOOOOOOOGOOOOOMOOOOOOOOMOOOOOLOOOOMOMOOMOOMOOMOOMOMOMOMOMOMLMMMTMMMTMMOTMMTMMTMLLCGCLOMMTMTSTMMLLMLMMMMMTSSVUVUVXVXXYXXYUSSMLMMMSSSSMSMMMLMLMLLLLC:CC9@@9@@9@99=88*8***88**8(*****(***89@:LJCJLMJSSXXXXXXXUXUUUUUUUUUSUMMCCCC:C@9@8=**(  ( ..(.(..(.(.(.(..(..(...(..(././*/.(/.(.(.(..(//(/*/*//*//*//*.//(//.*.(/(..(..(.*./.*/=========@=CCOMTVVVVVVUVYXYXVYXYXYXVVVVVSVTVSVVSVTTOTOOOOLCGC  ..//======@@=@@=@@G@CG@@F=@==F===F==@F@F=F=F@GFG@GGFGG@GG@GGGGGGGOOGGOGOOOOOOOOOOOOOOOTOTOOOOOOOMOOMOOMOOMOOMOOMOTOMOMOMOMOLOLOMOMOMMOMTMMOMTMTMMOMMMOLCCCCCLOLMMMMMLLLLLLLMLLMMMTMSTSVUVXXVUUVMMLCCLLMMMMMMLLLCLJCLC:CC:@9@9@899=9=88=*8/*8*******(((%(((((**89@9:@JCJLMSVXVXVUUVSUUSSSSSSSMSMLCC:@9@9888/((    (...( ( ..(..(..(...(..(..(.(.(...(.(.(..(.(..*.*./(..*.(.(.(.(.(.(.((.(..(.(/(/==/===/===@@COMTMTSVVVSVVVVVXYXYXYVUVSVTSSTSTSTTSTOMOOOOGGCC  (.//=/==/==========C===/==/=@===F@F=F=F@F@GFGG@GFG@GFGGGGGGGGGGGOGOGOOOOOOOOOOOOOOOOOMOOOOOOOOOOOOMOOMOOOMOOLOMOOMOMOMOMOMOMOLMOMOMTMMOMTMMMMTMMTMOMLL@C@CCCCLOLLLCCCCCCCCCCCCCCLCLCLLMMTMMMLCC9=8=9CCCCCCC9@9@9@9@88=88=8*8/********(((((((((%(((*(**888@99CCMSMSMMMMMLMLMMLMLMMLLLCC9@9@8=***((  (  ( ( ( (     .( .. ( ( (.(.   ...( (.(..(.(.(.(..( (.(. (.(.(.(.(.(.(./*/==========@CGLMTVTSVTSTVSVVVXYXYXVVSVTSTSTSTVSTSTMTOTOOLOGC .(///=/=///===/=====@/==/F==F===F@=F=F=F@F@G@GFGCGFGCFGGGGGGGGOGOGOGGOOOOOOOOOOOOMOOOOTOOOOOOOOOMOOOOOMOOOLOMOOMOOMOMOMOMOMOLMOMOMMMOMTMMOMTMMOMMTMMTLCCC@@CCCLLCLCCCCCCCCCC@CCCCCCCLCLMMLLLLC@88=8=8@C:C@@9@9@9@8@888=8**=**8******(*(((((((%(*((((****889@9:CMMSMMMMLMLMMLMMLMLMMLLLCC:@C9=***((     .( ( .  . (... (. ... (.  ( (...(.(..(.(..(..(. (..(.( (.(.(. (.(//==========@@COMMTTSVSTTTTTSVYXYXYVSVSSTSTSTSTVSTTTTTMTOOOLG   .(//=/=//====@===@=@G=======@F===F====F@F=F@GFGG@GGGGG@GGGGGGGGGOGOGOGOOOOOOOOOOOOOOOTOMOOOOOOOOOOOOLOOOLOMOOLOMMOOMOMOMOMOLMOLMMOMMMMOMMMTMOMMTMMOMMMOCC@C@CCCCLCLCCCCCCCCCC@C@CC@CCCCLLMLLCC9=8=88=9CCCCCC9@@9@98=888=8**=*/*8****(*(((((((*%((%((((*8=89@99CMMMMMMLMKLMLMMMMMMMMLLLCC:C:@8/*((  (  ( .. (   . .. ( ...( .(.. (.  (..( (. (/(.(.(..(.( (. (.(..(..(.(..(.(//=========@@CLOMMTTSVTSOTTTTVXYYVVSVTSTSMSTSVSTVSTTTTTMOOOC  ( .(//==@===@@@CC@CC@GCOCG@G@F@F@F====F=F=F@F@GFCFGG@GGFGGGGGGGGGGGOOGOGOGOOOOOOOOOOOOOOOMOOOOOOOOOOOOOLOLOLOLOLOOOMOTOMTOMOMTOMLOMOMTOMOMMOMMOMTMMOMTMTMLLCCCCCCCLLLLLLLMLLMLLLLMLLLLLLLMMMMMLMCC@C9=@@CCLMLLLLCLCCCC9@@9@9@9@89@8@88=888********8*8*(((((((*****8*89@:CLLLLMSUUSSUSSUSSSSSUUUSSSMLMLMLMLC98***( . . (.(.. (. ( .(. (...(..(...(..(./.(.(.(..(...(..(./*.//*./.*...(.(.(.(..(.( (..(.(..(//=/======@@@COLTSTSVTTMTOMTTVVXVSTSTSSTSTSVTVSVVTSTTTTTOMO     .(..(//==@=@@@CGCOCOCOCGMOCGCGC@C@@F===F==F=F=F@GFG@GGGGGGGGGGGGOGGOGOGGOGOOGOOOOOOOOOOTOOOTOOOOOOOOOOOLOOLOOLOLOMOLOMOMOMOMTOMOMOMOMOMMOMMOMMOMMMOMTSMTSTSMMLCLCLLLLOMMMMMTMMMMMMMLMMLLLLLLMLMMLLCCCC@9CCCLMMMMMLLLLLC:CC9@9@@9@9@@9@989=88=8*88***888****(****888888=9C:LLJMLMSUXUUUUUXVXXXXXXXVSMMMMMMMMMLC8=8*(((((  (( .(..(...(..(..(..(...(..*...(..//(////././(..//.*///*//./*=/////////*//.*..*.(.(./(.(..(/*//======@=@@GLLMTSVVVVSTTTTTTSVYXVVVVSVSVSVVXVYXYVVTVTTMTOM      ...(/.=====@@CCGCCCCGCOLCCG@@F@F@=2===F==F=F@F@GGFG@GGG@GGGGGGOGGGGOOGGGGOOOOOOOOOOOOOMOOMOOOOOOOOLOOOLOOLGLOLOOLOMOMOTMTMOMOMMOMLMOMMOMOMOMMOMOMMMTMMMTMMOLCCCCCCLLCLLOLLMOLMLLMLLLLLCLCCLLCCCCC@9@9@@9@CLLMLLJCLC:C@9@9@99@8@88@9=8=88=8*8***********((((****8****88=9C:CCJCLMSUSUSVUUVXVXVXVSMMLC:LLMLLLC98=**(((   .. (.. (. (  .(...(........(..*..(..(..(..(.../(//*./*///.*..(.(.(.(..(.(.(.(.(..(.(.//=======@@CCGLMTSVVTTTMOMOOMTSVTMTSTSTSSVVXYVVSTTTOOOOOL     .(///===@@@@@@@@@@G@F@=F==F====2=F@F=F=F@GFG@GFGGGGGGGGGGOGOGGGOGOGGOOGOOOOOOOOOOTOOOTOMOOOOOOLOOOLOLOLOLOMOOOMOMOTMTMOTMOMMOMOMOMMLMOMOMMMOMTMMOMTMMMLCC@@@C@CCCCCCCCCCCC@C@C@@@9@@9=@@9=8=8=8=8*88=9CCCC9@9@888=*8*=*8**/******(*((((((((((((%(((*(*(((**8=89=9@9CLMLMMMMSSSSSSSMC9@88@9@:CCC9=(*(   . .. (. (   .   . (. ...(..(..( ( .( .(..(...(...(.(..(..(.(.( (( (..(...(.(./(//=/=====@@CCLOMTSVSTTMOOLOOLTMTSTSTSTSVVUYXVVTTMTOOOLOO    .(///===@@G=F@@F@CG==@=@F=F==2=F=F=F=F@F@GFG@GG@GGGGGGGOGOGOGGOGGGGGOOOOOOOOOOOOOTOMOOOOOOOLOOOCOLOGLOLOLOOMOLTOMTMOTMOMTOMMOMMOMOMOMMOMOMMTMMMTMMMMMMLCC@@C@CCCCCCCCCCCCCC@C@@@@9=@9==8=8/8/8/*/**=8@C9C@9@9=888/8**/*=**/*/***(*(((((%((((((((((%(*((((**8=8=99@:LMMMMSSSVSSSSML@88=*8@:CC:C9/*/(      ( .( ( (     (  ( .. .(....(. ( .( ..(. (...(/.(/.(..(. ( (...( (..(. (.(.(...*/=/====@@@CCOMMTSVTTMOMOLOLOMTSTVSVSVSVVXYXVTTTOTOOGLO    (   (..///=@@@@@C@@G@@GC@F======F====F@=F=F=FF@GFGFGGGGGGGGGOGGOGGGGOGOGOGOOOOOOOOOOOOOOOOTOOOOLOOOLOOCOLGLOLOMOOLOMOMOTMTMOMMTOMMOMMMOMMOMMOMMTMMOMMTMMOMMOLC@C@C@CCCCCCCCC@CCC@C@C9@=@@9@=8==8=8/8=*=8/9@CCC:@9@9=888/88=***/****(*(*(((((((%(((((*(**(*(((**88=9@9@:LMMSSSSSSSSSML9=*8**=8@:CC98=(*(   (  . (.( ..   . . . .( .......(.. (..(..(..(...(/.(..(..(..( (.(.(....(..(.(./.*//=/===@@@CCOMMTVSTTOLOLOLOOMTSVSVVSVVXYXVVVMOOOOOCOO....(..././(..(././===@CCGCOCGCLGCGLO@@GF@F=@F/=F=F@=F=F@F@GFCFG@GGGGGOGGOGGGGGGOGGGOGOOOOOOOOOOOOOTOMOOOLOOOCOOLOLOOCOLOLOMOMOOMOTMOTMMOMOMMOMOMTMOMMOMMOMMOMTMTMMMTMTMMLCCCCLCLLLLLLLMLLLLLLLLLCLCCCCCC@@@@@@@9@9=9CLMMLLLLCCCC9@@9@9@8@88=8888=88*8**********(*((((((**88*88*8*(**=8@:CCLLLMSUUVXVXXVXVUMC@98=88=:CLMLL@8=**((((   . (. .(......(.. ..(.. (......../.../...(../././/.*/.///////...(..(.(..(..(..(..(..(.(././//====@@@CLOMTSVTSOLOLOOLOMOVVVVUVUVVXVVVVTOMOOOGOG .(.(..*./*///./////==@@@GCLOLOLOLOCOLOCG@@G@F@@F===F@F=F=FF@GFG@GFGGGGGGGOGOGGOGGGOGOGOOOOOOOOOOOOOOOOOOMOOOGLOOCOGLOLOCOLOOLOOLOMOMOTMTOMTMOTMMOMMTMOMMOMMTMMTMMTSTSTSSSTMMLLLLLLMLMLMMMMMMMMLMLMLLLLLCLCCCC@@@@@@@9@CLMMMMLMLLLC:CCC9@:@9@9@8=898=88=888*8*********((((**8=8=88=****=8@C:LMMMMMSVUUXXXXXXXVSC9@9=9=9CLLMLC9@8=*(((((((((  ( (.....(..(..(...(.....(.......././/./////.//./////////////=//=/////////*..(.(.(..(.(..(.(.(//(////=/===@@CCGMMTSVVVVMOMTTTTTTVVXYXYXVXYXYXVVTOOTOOOO .(.(.(/./.*////=/===@@@GCCOCOCGCG@GLG@@F@F@F@=2=F=G=F=F=FF@GFG@GGGGGGGGGOGOGGGGOGOOGGOOOOOOOOOGOOOOOMOOOOOLGOOCOOCOLOLOLOOLOMOOOMOMOMTMOMOMTMOTMMMOMMMMOMMOMMMMMMMTMTMTSMMLCCCCLCLLLLOLOLLOLLLLLLCLCCCC@C@@:@@8@8=@8@LLMLLLL:CCC:@9@9@@9@88888=8**8*************(*((((%((***8*****(**8=9CLLLLMLMMMMMSSVXVUUSL@98=88=9@:CCC9@8=** ((    . ( .(...(..... (................./..(...../../*////////////.*/..(.(.(.(...(..( (.(..(.(..*/(//*/==@@CCOLMTTSTMOMOMOTMOMVVVVVVUVYXVVSTMOOOOGOO   ....*///======@F@@@@@@=@==@@=F==F=@F==F=F@F@F@F@F@GFGG@GGGGGGGGOGGGOGGGOGOGOGOOOOOOOOOGGOOOOOMOOOOCOGLOCOGLOOLOLOMOOLOLOMOMOMOMTMOMTMMTLTMMOTMMOMTMMTOMTMMMOMMTMOLCC@CCC@CCC@CCCCCC@@@@@9@@@9=9=8=8=*=*8**=8@C:@C:@C9@88=88=*8**/****((*((((((((%((((((((((((((**8=9@:C:CC9CCCCCLMMMLL9=***(***=89=8*/*((        .. . ( ( .. . .(. ...(...(. .(......(....(..(..(..(. ( (.( .(....( (..*.(//(//*//==@@CGLMMTTSTOLTMTOMOTSVSVSVVVVSSTSTMOOOGOO   .(..///=======@=@@F====F=F@===F==F@F==F=FCF@F@F@FGFG@GGFGGGGGGGOGGGOGGOGOOGOGOOOOOOOGOGGOOOOOOMOOOCOCOOCOOLOOOOLOLOMOLOLOMOMOMTLTMMOMMTMOMMMTMMTMMOMMTMOMTMTMMTMLL@C@CCC@CCCCCC@C@C@C@C9@@@=9==8=8/8*/*/***@9C@9@9@99@88=88*=*8****(**(((((%((((((((((((((*/88@@:C@C:CC9@9@:LMLMC9/*/*/*8*=8=8**/*(     .. ...  . ... . ( . ... . ... .(...../(.../...(/..(...( (...(. (..( (..(..(..*/./(//*==@@CGLOMTSSTMOTMOTOMTTVSTSSSTVTSTTSMOOOGO  .(../*//==@@@@=@@@F@@=F====@F@F===F=@@F==F@F@F@F@FF@GFG@GGGGGGGGGOGOGGGGOGOOGGOOOOOOOOGOGOGGOOOLOOOLGOCOOCOLOOLOOOOLOOLOLOMOMOMTMOMTMTMOMMTMMTMMOMTMMTMTMMTMMMTMMTMLLC@@CC@CC@CC@CCC@@C@@@9@9@@8@=8=8/*/***/8@@:@:@@9@@9=888/88***/***(*((((%(%((((((*((((((*8=9@9CC:@:@9@9@@:LMLLC9=***/**8/8=*8*/((       ( ... . . ( .. . .  . .( ..... ...(...../(.../.(...(..(..( (..(. (..( (...(/(./(/.*/./*==@@CCMOMTSTMOMTOMMOTSTSTTMOTMTSMTMTMOOOG (.(././/==@@CCCOCGCGCC@CG@C@G@GCG@@G=@F@@F=F@F@GF@F@F@FGG@GGGGGGGGGGGOGOGOGGOGOOGGOOOOOOOGOGOGOCOOOOLOOCOGLGLOOLOOLOLOOLOLOLOLOLMOMOTMMOMTMMTMMOMMTMTMMTMMMTMMTMTMTSSTMLLCLCLCLCLLLLLLLLLLLLLCCCCCCCCC@@@@8==8=9CLLLLLLCL:LC:@:9@9@99988=88*******(**(***(*(((*(%(((((((**888***((*=89@CLLLLLLLLLCCJCLMSSMML@9@8@@9@@9@@8==*/((    (. ( .(.............................../../../././///////////.///././(..(.(..(.(.( (...(.(..(/.*.*./(//(//==@CCOMMTSTTMTMTMOTMVTSTMMOMOMTTSTMTMOOO   (..*//===@@CGLOLOCOCOCGCCGCGCGCGLGCG@GCFGCF=F@FCF@F@FF@GGGGGGGGGGGGGGGOGGGGGOGOOGOGOOOOOGOOGGOGGLGOOOOLOCOGLOOCOOOMOOMOOLOLOLOLOMOMTMOMTMMOMTMMTMOMMMTTMTTSTSTSVSVSVSSVMMLLLLLMLMLMOLLLMLLLMLLLLLLLCLCCC@@@8=8@CLMLMMLMLLLLL:CC:C:C9@9@99=8888****************(**(**(*((**=88=8(**/*8@:CLLMLMLLMLLLLCCLSSUSUMLCC@9@C@CC@C9==**((((((((((   ...(. .(.....././..........//..........///./////.////////////////////////.*/.(.*..(/.*.(..(..(..*///*///////*/==@CCMOMSVSTSTTTTSTVVVVSTTMTOTMVTVSVSTTO .(.//===@@CGLOLCLGCGCGCG@@GC@COC@G@FC@@G==F=FCGF@F@FG@GGGGGGGGGGGGGGGGGOGGOOGOOGOGOOOOGOGOGOCGOGOLOOOLGLGGLOLOLOOOOOLOLOLOLOLOLMOMOTMMOTMTMTMMMTMTMTMTMMTMTMTMTMTMTMTMOLCCLCLCLCLLLLLLCLLCLCCCCCCCCC@@@9=8=8@:CLLLLCL:LC:C:@9@9@9@99=888=8*********(**(*((((%((*((((((((*****((((**=8@:CC:LLLLJLCC:CCLMSSSSMLLCC9C@9@@@@8=*/(((     .. . ( .......... ............... ../..../..././////////////.//.//.(.(.(..(.(.(.(..(.(.(.(..*..(/*/.*//(//*==@OCOMTSMTLMMTMTMTSTSMMOLOOOLTMTMTSTL  (.///==@@@@G@@@@@@=@@@@@@F=@@F===F@F@G==F@FCGF@FGF@GGGGGGGGGGGGOGGOGGOGGOOGOOGOGOOOGOGOGGOGGCOGLOOLOGLGCOOLOOLOLOOLOLOLOLOLOOMOMMOTMMTMMMTMTMMMTMTMTMMTMMTMMTMMTMMTLLCCCC@CC@C@@@@@@@@@9@@@@9@@8=8=8=*/**=9@C:@9@9@9@98=88888/8**8***(((((((((((((((**/88=9@9@9@9@9=8@9LLLLMLLC@C9@@8=8=***(.      ( . .. . . ..... . . ..............(....(....(....(..(..(.(.(..(...( ..(...*.*./(/.*..(/.*/=@CCCOLOLOLOMOLOMTSTTMOLOCOGMTMTSTMT .(//===@FC@@@F@@@@F@F@F@===F===F=@@G@F=F@F@GF@FF@FGGGGGGGGGGGGGGGGGGGGOGOOGOGGOGOOGOGGOGOGOGLGOOOOLGLGOCOOLOOOOLOOLOLOLOLOLMOMOTMMOMMMTMMTMTMTMTMSMTMTTMTSTMMTMMOMMLLC@C@C@CCC@C@@@@9@=9@@8@==9=8==*/**/8@@C9@:@9@88=888=*=8*8******(*(((((((%((((**=88@9@9@@9@9=9=CLLLLLLLLCCCC@=8/8/*.(     (  .. .. . .. . ... . . . . .... ... ...../......(......(...(...(..(..(.( (..( .(.(..*///*./(/.(.//8=@@CLGLCLGLOLGLMTSTSTMOLOGOCOMTSTMT  .*/===@@@G@@@@@G@@F@@@@F@=@F=F==F@F@F=F@F@GG@GF@FGGGGGGGGGGGGGGOGGOGGGGOGOGOGGOGOOGOGGOGGGCGOCOLOOLGOCOOCOLOLOOLOLOLOOLLLOOMOMOMTMOTOMTMTMTMTMTMTMTMTMTMTMTMMOMLMOLLCC@CCC@C@@@@@@@@9@@@@@@9@==8=8=8/88=@:CCC9@@9@988=88=*88/8******(*(((%((((((((%(((((**=8=9@9@9@8@9=9=CLMMLMLLMLLLLC@=8=*/*.      .. . .. . ... . . . . . ........... ...../..(../..(....(...(..(...(..(..( ( (.( (/.//////*.(./(.///@@@@CCCCCCCGLGLTSTSTMMMOLGOOMTTMTM     .//==@@GCOCGCGCGLGCOLGOLGCG@G@@F@F@G@G=F@F@G@FFGF@FGGGGGGGGGGGGGGGGGOGOGOGOGOGGGOGOGGOGCOGOCGCGOGOLOCOGLGLOOLGOLOOLOLOLOOLOLOMOMOMOMMMOMTMTMTMTSTMTMTMTSTMTSTMMOMOLLMLLLCCLLCLLLCCCLCCCCLCCCLCCCC@@@@9@@LLMLMLMLLCCC:C9@99@9@99@98888888*8*****(*(**(*(**(((*(*((((((((((((((((((*/*8=9@CCL:CCCCCLCCLSSSUSUSUSVSSSML@@@==8/((((     .(. . ( . ............................../../......////./////.//.//././(...*..(..(..(...( (..(..(//==/////.//.*//@=@@@@@@CCOCLOLMTVSTSTMOMOOOLTSTMT   ..(.//@@@GLLOLOCOLOMOOMOMOLGCGCGC@G@FCFCF=F@F@GG@FFG@GGGGGGGGOGOGGGGGGGGGGOGOGOGGGOGOGOGGGGOCGOCGOLOOOLGLGOCOOLOLOOLOLOLOLOLOLMOMOTLTMOMMTMTMTSTMTTSTSSTVSVVSVSTTMMMMOMMOMLMLMLMLLLLLLLLLLLLLLLLLCCCCC@@CLLMMSMMMMLMLLC:C:C:@:9@9@9@98=88888888********************/**(((*(((((((((*/88=9@:LLCLCLCLLLLLMVUVUVUVUUVVVUUMCC@@==/*.((((((((     .(. .(...................././/.//..../././//////.//////////////=//////////////.//*///..(.(..(.(..(.///===@===///////=@==@=@@@CCLOLMOTSVVVSVTSTMOTTTSVTS    ...*/=@CGCLGLCCGLOLOLOOLG@@@@G@F@=F@FC@F@F@FG@FGF@FGGGGGGGGGGGGGOGGOGGOGOGOGGOGGGOOGOGOGGGGCGOCGGLOOOCOGLGOLGOLOLOOLOOLOLOLOOMOMOTLTMOMMMTMTTMTTMTTTMTMTTSTMTSTMOLLCOLLLLCLLCLCLCLCCLCCCCLCLLCCCCC@@C9CCLMMMMMMLLLCC:C:C9@9@9@98=888888=8*8*****(*((*(***(*((*(*((((((((((((((((*(8=8@9CC:CCCC:CCCLSUVSUVSVSUSVUTUMC@@@8//((     ( . (. .. ( ............................/2/././...././//////.////././././.(./(...(.(..(. ( (.(.(.(.//=====///(/./*===/=*==@@CCOLLOMMTSTMTMTMOLOMTMTM   (../===@@@G@@G@GCGCCG@@F=F==F=F==FCFG=F@FF@GF@FF@FGGGGGGGGOGOGGGGGGGGGGOGOGGOGGGGOGOCGOCOGGGOCOGOOLOOCOCOOCOCOOLOOLOOLOLOMOOMOTMOMOMOMOMTMTTSTMTMSTSTMTTSTTSMMOLCCCCC@CC@C@C@@@@@@@9@@9@@@9@@9==8=8=9@@CCLCCC:C9@99=88=**8*8*8*8***(***(*(*(((%((((((((((((*/88=88=8@9@9CCMMMLMLMLMMLMMTML@==*/((      . . . .. . .. . ... .........................(....(....(...(..(..(..( ( ..(/.(/====/=//./..*/=//*///==@@CGCLOLOMSTMMMMMOMTMTTM   ..///==@@@G@@GCGCFC@F=@==F=@F=F=F@G@@FF@FFG@FG@FGFGGGGGGGGGGGGOGGGGGOGGOGOGOGGOGGOGGCGGGOCGGCOGLOOOOGLOGLOOCOOLOLOOOMOOMOMOMOMOTMMOTMTMTMTTMTTSTTMTMTMTSTMTTMMOCCCC@CCCCC@@C@@@@@@@@=@9=@@9@=9=8==8=@@:CCCC:CC9@@989=88*=*8/******(*(*(*((((%((((((**/8=8=8=8@=:CLMLMLMLMLLMOMMMMLC=*/(.    . . ( . . . . . . . . . .. ../.......................(.....(..(...(..( .....( ( (....(/=@==/=/////.*////(//8=@@@CGCOLCOMMMTMMTMOMTMTT   (...//===@@G@GCCGCCG@G@F=F===F@=F=@FCF@F=F@F@GF@FGFGGGGGGGGGGGGGGGOGGGGOGGOGOGOGGOCGOGGLGGGGOCGGOGLOOOLGOGOCOLGLOOLOOMOOLOOMOMOTMOMOMMTMOTMTMTTMTTMTSTMTTSTTMTMTLLCCCC@C@C@C@C@C@@@@@9@@@9@=@9==8=8==9@@CCLCC:CCC9@9@888=888**8**********(**((((((((((((((((/**8/8=9=9@@LMMMLMLMMMMMTMTSTSMC==*(.       . . ( . . . . .. . . .. ................./../.../...(/....(..(/..(......( (..//.(//====//////*/=//*/.//==@@@CGLGLOCOMMTMMOMTMTTM   ...(//===@@@GCOLOLOLOOLGCGCGC@F@@G@@F@F@GG@FF@FF@GFG@GFGGGGGGGGGGGGGGGGGGGOGGGOCGGLGGGOGOGGCGGCGGCGLGOOLOOOCOGOLGOCOOLOLOOOMOMOOMOMOTMTMOTMTMTMTTSTMTTSTMTSMTMTTSTTSOLLCCCLCLCLCLLCCLCCLLCLCCCCCCCC@@@@@@CCLMMMMMLMLLLLCCC::@9@99=888888888888*8*8****(*(****((***(((((((((((((((((((/*8=8=9@9CC@CLMUVUVSVSVSVUUVXVXYXVMC==*/(       . ........ ..............................//./../.././//////.//.///.////.././(...(..(. (.( .(.. (.(/..(/=======/////==///*//==@@@CCOLOLGLOLMOMOMTMTMT  (..(/./===@@@GCGLOLOOLTMOLLOGLGGCGG@GG@G@@GG@FF@F@FFG@GFGF@GGGGGGGGGGGGGOGOGGOGOGGGLGOGGCGOGOCGGGGCGGCGOCOOOOLOGLGOLGOLGLOOLOLOOOMOMOMTMOMTTMTMTMTMTTTSTSTTVSTTSTSVTSTSVTMLLOLLLLLMLLLLLLLLMLLLLLLCCCCCCC@C@CCLMMMMMSMMMMLMLLCL:C:@9:@99@88@8=8=8=88=8**********/*****(((((((**((((((*((((((**/*=8=@9CCCCLLSVUVUVUVUVXYXXXXXXXXVSL@@8/(((((((     ( (. .(.........../.........../.../..././././2//////.///////////////////////////./(./..(...(...(..(..//.//(=============@====/===@@CCOLOLOLOOOMTOTMTTSVT  ..(//===@@GCGCGCOLGLGLOGCGCGCG@@@F@G@F@F@FGCF@FF@FGFGFG@GGGGGGGGGGGGGGGGOGGCOGOCGGGGGCGGOGGOCGGCGGCGCOGOOLOOOGOLGOLOGLOOOLOOLOMOOMOMOTMTMTMTTMTOMTMTTMTTSTMTMTMTSTTMTMMTLLCCLCCLCLCLCCLCLCLLLCCCCCC@@@9@@9@@CLLMMMLLLLJLLC:C:C9C9@9898888=8888=8*8*****(*((***(*(.(((((((((*(**(((((((((((*(**8=8@9@CCCMMVUVUSVSVUUYXYXYXYXVUMLC=8/((       . ( ....................................../.../....//////////./././././././.(..(...(..(. (..(. (..(..////=///////==/=/=/*//==@@@COCGCGCGGGLOOMOTST .(./====@@@@@@@G@@@@G@@@F@F@F=F@F=@F@F@@GGF@F=F@FGFG@GFGGGGGG@GGGGGGGGOGOGCGGOGOGOGGCGGOGGGCGGGCGGCGOCOOOOOLOGOGOLOGLOLOOLOLOLOLOMOMTOTMTMOTMTMTTMTTSTMTMTMMTMMMTMTMMOMCCC@@C@@@@@@@@9@@@9@@9@=8=8=8=*/**8=8@9CC:C9@9@9@99=988=88*******(**(*(*(((((((%((((((((((((*/*=8=9@@LLMMMMMMMMMSTSSVSVMSMLLL@=8((      . . . . . . . . . .. ...... .........../.........(....(...(..( .(........( (..(..(//.*.///*////==/=/=/*//===@@CGCGCGGCGGGOOMTTS  .//===F@@@@F@@@F=F=@F@F=@=F=F=F=F@F@F@FG@GF@FFF@GF@GFGFGGGGGGGGGGGGCGGCGGGCGGCGCGCGOGCGOGGGCGGCGCOGCOGOLOOOLOOLGOLGOLOOLOLOOLOMOMOMTTMTMTTMTMOTMTTMTTTMTMTMMTMTMMTMMMMOLC@C@@@C@@@@@@9@@@@@9@=9=8==*8/8/*8=8@@:@9C@9@9@@989=888/8*/**(**((*(((((((((((((((**=8=@9@CMLLMLLLMMMMMMTMMTMMLLLL@@=*(       . .. . . . . .. .. . .. . . ... ......../.............(.......(..(. (..... (... (..(..(/..(.(./////==/=/===/(//==@@CGC@G@GGGCGGOOTS (.//==@@@GCGC@G@@G=F@@F@F@@@F@==F=F@F@F@FGCF@F@FF@GFGFGF@GGGG@GGGCGG@GGGGCGGGGGGGGGGGGGOGOGOGCGGGCGCOGOOOLOOOOLOOOOLGLOOLOLOLOLOMOOMTOTTMTMTMTTMOTMTTMTTMTMTOMTMTMMTMOMMLLC@@C@@@@@@@@8@@@9@@9==8=8=8/8/*/*8=9@@:@9@@9@99@9=99=88*8********(*((((((((((((((**((((/*/8=9@@@CLMMMMMMMTMTSSSSTSMSMMMLLC@=*(      . . . . . . . . . . . . . ... .... ....././...........(.../(...(...(.( (.....(. (...( ./(...(..*///===/=/==////===@C@G@GG@GGCGGLOOT  ..///=@GCGLOLOLOLOOCGCOGCGCGGCGCFCF@@FG@F@FGG@F@FGF@GFG@GGFGGGGGGGGGCGGCGOGGCGCGCGCGCGLGGCGGGGGGCGGCGOGLGLOOOOOOOLOOLOOLOLOOLOLOLOOMOOMTMOTTTMOTMTTMTMTTMTMTMTMTMTMTTMTSMTMTMLCCCCC@CCCCCCCLCCCCCCC@@@@@@@8@9=@CCLLLLLLLCCLCC:C:C9@9@989=88=88***8*******(*((*((((*(**((((((*/88=**(((((((.**==9@CCLLLLVSVUVSVUVXVXVXVXYXVXVSSMLC@8*(        ................................................../////.///././.../../../....(../(..( .(. ....../..(.//(...(/.///@@==/=====///==@@C@G@GGCGGGGGOMT ..///==@GCLOLOMOMOMOMOOLOOLOCGLOGCG@GG@GGCGF@GG@F@FFFFGFGFG@FGGGG@GGCGGG@GGCGGGCGGGGGGGGGCGGOGOGCGGCGGCOGOOOOOOOOOOLOOOLOOLOLOLOLOMOOMOMOTTMTOTSTTMTTTTSTTSVTTSTTTVSTSVSVSTUSSTMLLCCLCCLLCLLLCLCLCCCCCCC@@C@9@C:CLLMMLMLLLCJLLLL:LC:C:@9@9@988=88=88=*8**8/**/**************(**=888=8(((((((((((*/8=@CCLLLLLSVXVXVXYXXXXXXXYXXXYXXVUMMC@8/((( ((((        ... (.............../.../.../../............//.././//.////////////////.////////./././/..../...(...././...*//////./*///@@=====@===/=/=@GCGCGGGOGGGOOOOO  (././/==@GCOLOLOOLOMOOLOCOOCOCGCGG@@F@F@G@F@FGG@FF@GFFG@GFGFGFGGGGGGGCGGG@GGGCGG@GGCGCGCGCGGCGGOGCGGCOGGOGOGOOOOOOOOLOOOLOOOLOLOOOOLOOMOTMTOTMTTMTTMTMTMMTTMTSTMSTMTMMTTMTSMTSTMLCCC@CC@C@CCCCCCCCCC@@C9@9@9=9=@:CLCLC:LC:CCCJCC:C:@9@9@9888=88******8/*/**(*(*((****(**(*(*(.(**=88**(((((*/8@@CCCLLCMVUVUVUVUVYXYXXXYXYXVUVMLL@8*(.( ((((     . . ............................................../..////////.//.././../.../...(.....(....(....(../...(//.(..(..(.//=/=//==/==////=@@@@G@GCGCGCOOOO (..///==@@@G@CGCGCCGCGCG@@@F@@F@F=F@FF@GGF@FGG@FF@GFFGFG@GFGGGGG@GGGGCGFCGGGCG@G@GGCGGCGGGGCOGOGGOGGOCOCOGOOOOOOOOOOLOOOLOOLOOMOOMOTOMOTOMTMTTTTTTTMOTMMOTMTMTTMTMTMMMMMTLMOMLLCC@=@=9=@@9@=9@8@=8@8=8/8*/8*8=8@@9@9@9@89=9=88=88=88=8*8/***(((*((((((((((((((((((((*.((((.**=8@9@@9LMMMMMMMMSMSSVSVSSSTMMLC@8=(      . . . ... . . ..... .... ....................(....(.......(.. .(. (. ..(....(../..(..(..(..=///===/=/=*//===@@G@GGGOGOGOOO  .(..//===@@G@CGCGCGC@G@G@G@F@@F=F=F=F@FC@FG@GG@FFFGF@GFGFG@GGGGG@GGGGCF@G@GGG@GG@GCG@GGCGCGGGGOGOGCGOGOGOGOOOOOOOLOOOLOOOMOOLOOMOOMOOOMTOTTMTOTMTTMTMOTMTMTTMTMMTMOMMOMOMOMLLMLCC@@=@@==@8@==9=@=8=8==*8*/8=8@@9@9@@98@8=888=8888=88*=*8*/**(((((((((((((((((((.*=8=8@=9LLLMLLMLMMMTMSMSTMMMLLL@9=*(           . . . .. .. . . . . ... . .. .. ......../....../...(.....(.....(....( ..( .../ (../*..(..(..(.//*//==///=////===@CFCGCGGOOOOO (../////==@G@GCCGCGCGGCGCGC@F@F@@@F=F@F@GG@FFCGF@F@FGF@GFGFGFGGGGG@GGGG@F@GCGCFG@GCFCGCGGCGGCOGOGOGGCGOGOCOGOOOOOOOOLOOOMOOMOOMOOOMOOMOOOMTMTTMTTTTMTTMMOMTMTTSTMMTMOMLMOMMOMOLLLLC@@=@9=@==9==9=8==8=*=88*=8@9@@9@@9@8@889=888=888=888=***/*(/(((((((((((((((((((((((((((((.(*/8==9=@9LMMMMMMMSMSSSVSVSSSMLLC@8*((     ( . . . . ... . . . . . .. . . .. ....../...../..../../..../(........(........(.../..(/./.(...(. .(/..//====/=/=/*/===GCGCGOGOOOOO  ....///====@F@COLOOMOLOMOOLOOOOCGCGGG@G@FG@FG@F@FGGF@F@FGF@GFG@GFGGGGG@GGGCG@FGCGCFCG@GCGG@GCGCGGCOGCOGGCGGOGOGOGOOOLOMOOOLOOOOMOOMOTOMOOMOMOTOTMOTTMTTMTMOMOMTMMTMTTMMTMOMMMTMSMTMMMMLCCC@CC@CCCCC@C@@@@9@@@@9CCLLLLLCCCC:C@:@@9@@9@@9@@9@9@88=8*8/*8****8/****(****(*(*(***(*((((((***(( (((*/8=9@CCCCMSSVSVUVXVXVXXYXXYXVUMMLCC8=**( (( (.((((         .. ....... ....... .. ...............................//./////.//.////.//.//.....(...../.........///../*/..(..(..(.//(./====/=/=///===@G@GCOGOOOOO (.(.//=====F@@GCOLTTMTOTMTOMOTTOLOOGLGGGGCG@GGGCGFCG@F@FG=GFG@GFG@GGGCG@GGGG@@FCG@G@G@GG@GCGG@GGCGCOGOGCGGGOGGLGOGLOOOOOLOOMOOTOMOOOMOOMOTOTOMOTMTTMTTTSTTSTTMTMTSTSVSVTSTMMTMSTSVSVSTSTMLCLCCCCCCCCCCCC@C@:@CCLLLLMLLLMLLLCLC:CCC9C9C:@C:@C9@9@88=88/8/8=8*8=*=*=**/**/*******/****(*.***(((((((.(**=@9@CCLCSVUXXXYXXXX[X[X[YXXVUMMLLLC@8((.(.(((.(( ((.(((        ( . ( ...(.....................././/../.../././////././/././/////////////////=////////.///...///../././/./////////.///.*..///./==@=======//==@@G@OOOOOOMO  .././//===@F@@GLOMOOMOOMOOMOOOCOCGGCGCFG@F@GGG@F@GG=F@F@F@FGF@GFGG@GG@G@GG@GF@GGCG@G@GGCGGCG@GGCGGLGGGCGGCGOGOCOGOOOOOOMOOOOOMOTOMOMOTOTOMOOMOOTMOTMTTMTMTMOMOMMTTMTMTMMMOLOLMOMMTMTMMMLCCCC@CC@C@@@9@@9@@9@C:CLLLLLLLLCCC:@9@9@9@9@9@9@9@8@88=8*/****/**=******/****(**((*.(*(*.(((((((((((.**==9@C:CMSVUVXVXVXVXYXYXXVSMMMMLMLC8/( ((.(.((         ( ( . .. ... . .... ....................../.............//././///.////./././/./......././......(..//././//(..(..(..(/.(./====/=/=/////=@@@GLOMOTOO  (...////===F@FCGLGCOCGLGCOCO@G@G@G@F@F@F@F@GG@F@GF@F@F@FG@@GF@G@GGGG@G@GG@@F@GCG@FGCGG@GCGGCGCGCGOGOGCGGCGLGOGLGLOOOOOMOOOOOMOOTOOMOTOTOMOOMOMTTMOMTTMTMTTLLOLMMTMTMTMOMLCLCCLOLLLOLLLCC@@=@8@=8=8=8/8**=*=8@9@C9@:@@9@9=8=88/8*=*8/8*/8***/*((((((((((.(*(((((((((((((*/*8=8CLMMMMSMSSSVSVUVSMLLLLLLLC@8(           . .. . . ..... ........ . ................../............/...../........(//...//..(..(. (..//.../==/=//////.//@=FCOLTTMTO   . ( ..///=/===@@GCCGCGCCGCGCGCF@F@F@F@F@F@F@GG@F@FCF@F@F@F@FG@GFCGG@G@G@G@G@F@G@GG@G@GCG@GGCGGCGOCGCOCGGCGGCGOCOGOCOOLOOOMOMOOTOMOTOTOMTOOMOOTOMOTTMTMTMTMTMTLOLOMMMMMTMLOLCCCCCCLLLLLLLCC@@@===8=8=*=*/8**8=8@@9@C@:@@9=@88=8*8/*8**=***/**(((((((((((((((((((/*8=CLLMMMMMTMSMSSSSMLLCCLLLC9=*(           . . . . . . ... . . ... .... ... .. ......../...../../.../.../......../..../..(.././..///..(..(...(//...=/=/=/=////.//===@LOMTTTT  ...././====F@F@CGCGLCOCGCGLGCGG@G@G@F@F@F@FGG@F@FG=F@F@F@F@FG@FG@GGG@FG@G@GF@FCGC@FGCG@GC@GGCGGCOGCOGCGGCGLGGOCOOOLOOOOOOOOOMOMOTOMOTOTOTOMOOMOMTMOMTMTTMTTMOLLCOLMOMMTMMOLCCC@CCCCCGLCCCC@@@=9==8=*=*/*/*/*8=9@9@C@9@9@8=88/**8/*=**8/**/*((((((.*.((/(((((((((((**/8CLMMMMMMSSSVSVUVUSMCCCC:C@8/(            . . . .. .. . .. . . .. ..... ... ......./..../...../...../...././.../../......(//...///...(...(..//.../===/=/////./.=/=@GCOMTSV  ...////===@@GC@GCGLOLOMOMOTOOMTOOLOGOGCGGG@GFCGCGG=@@F@F@F@F@F@@GF@G@G@GCF@G@@G@FC@GG@@GG@GG@GCGGCGCGOCOGCGGCGOCOGOCOOCOLOOOMOOOMOTOTOTOMOTOTOMOMOTOTMTMOTMTMTMTMOCLGLMTTMTMMTMLLCCCCCLCLLLLCCCCC@@@@@@8@=@8=8=8=@9LCLLCCCCC@@9@9=8@8=9=88=8**/*((((((/***8/8*8=*******(*(((((*(*(*(((((((((((.**=9LMSUVUVUXVXX[[[[[XXSLCLCLCC8*( .(..( (((.( (             .  . . . ( . ............................2.../........././/.///./././///./././/.///././//../../././//.////.../.....///.//==/=/=///./..==/==GLOMTT   .(.////===@@GCGOLOLOLOTTMTTTTTMTOTTOOOLOOOOGGGGGGGLOF@FG@=F@=@F@F@FG@FG@GG@G@FCG@F@FG@GCF@G@G@GCGGCGGCOGCOGOCGLGGOCOLOLOOCOOOOOLOOOOOMTOTOTOTMTMOTOMTMOTMTTMTSTSTVTSOLOLOMSVSVTSTSOMLCOLCLLCLLLLLLCCCC@@C@@9@=@8=8=@9@CCLLCL:CCC@9@9@8@8=8=88*=**((((/*(*=8=88=888=8=8=*8/**/****/****(*(((((((((((((((/*=9CSSVUXXVXX[[[[[[[[[USLCCC:@@8* .(( ((((.((.(((.((           .  ( ( . ( . .... (... .../.......././/../......//1//2/2/////////////////////.//////////.////=//////////.//.//////=///.///////===//==@=====/////.=///@@OLOTS  (././===@@GCCGLOCOLOLTOMOMOMOOMTOMGOOCOOCGCGCG@GCGG@@@G=F=F@F=F@GFG@FGCG@@F@G@G@F@@G@G@FCG@G@GGCGCOGCGOCGOGOCGCGLGOLOOLOGLOLOOOMOOOOTMOTOTMOTMOMTOMOTMOTMTMTMTMTMTMOLCCLOMMMTMTMMOLCCCCCCCCCC@CCCC@@@9@@=8=8==8=8/88=8=9@@9@98=8*=*****.(*.(((.((.(((.***/**8/8*=*8*8/***/**((*(*(.*((((.(((((*=@LMSSVSVXY[[[[[[[[XXSL@9=9@8=(( . (.(((.((.(( (            (   . ... ............../...........././.1/1/....2././//////./././..//./././////./////././././//./////...//...//=///===@==/=//..../=.//=@OLTT  (....//==@FC@GCGCGCOCOCOLOCOOCGOOC@G@GCG@F@F@F@GCG@F@@F=F=F@F@F@F@G@FGCG@G@FG@G@F@G@GG@F@G@GGCGGCGCGGLGCOCOCOCOCOLOLOGLOOOLOOOOMOOMOOTOMTOTOMTOMTOMOMTMOTMTMTTMTMTMLGCCCGLOMMOMOLL@C@@@@=@@=@=9@==8=8=8=*/****(*.(*(*.***.*.(((((((((((*.*(*(.(((((((((((((8@CCLLMMSUXXXXXXXUSSLC@8*/8**(              . . . .. .1..... . ....1..1......1..../...1............../......//././//../......//.././/.../.(./.=/=./==F@===/=//..//////==@OLM      .(..(///==@@@@@CGCCGCOCGCGLGCGCGCGG@G@G@F@F@@F@FCG@F@FC==F=F@F=F@FG@FCFGG@F@G@@G@F@G@G@F@G@G@GGCGGCOCGCOCOGLGLGLGOLGOLOOOLOLOLOOOOOOMOTOTOMOTMOTMMTMOMOTMTMOMTMTTMTMMGCCCCCOLOMLOLLC@@@@=@=@=@===9==8=8/8*/*/*/(*.((((((((((((((((((((((((((((.((((((8@9CCLMSVXYXVUSSSMMLLC@8**/*((           .. . ... ... .. .. . .. ......1..../1..//../......./..../././..////.////././..././..////...///..///=//===@=/==/=/...///.//=@CGM       .(..//===@G@GCG@GCOCOLOLOCOGCGCOCGCFG@GG@@F@=F@FCGG@@F@F=@F@@F@F@FG@FCGCG@F@G@@@F@FCFG@@FG@GCGCGCGCGCGGLGLOGCGGLOLOLOOOLOOLOOOLOTOMOMOTMOTOMTMOTMOTMTMOMTMTOTMTMTMTMLOCCCC@CLOLMOLLCC@@@====8@==8===8=8=*/****.(*.((.((.((((((( ((((((*(*.((((((((((((((/*=9@CLSVXX[XXSMMMMMLML@@8=*/(((                   . . . . .... ... . .. ..1...1..../.1.//1/./2....../......././.././//./////../....///./////..//././//=///=F====2==//./.=/////=FCO       ..(./===@@GCOCOLOMOMOMOTMOTMTOMOOTOOLOOOLOG@GG@G@GCG@F@G@F=F@G=F@F@FG@G@G@@F@@FCG@@F@GG@F@GCGG@GGCGCGCGCGCOCOGCOCOGLOOLOOLOOOOOOOMOOOMOMOTTMOMOTMOTMTOMTOMTMTMOMTMTMTMMOCCCGCCLOMLMOMLCC@C@@C@@@@C@@@@@@@@8@==*8=**/***/**.*/*.*(.((((((((((((((((.(****/*8/8/8*8/8/8/********((((((((((((*8@@CMSXX[[[[XXVMSSSSSSSLL@9==*/*( .( ((.(.(( (( (.           (   . ..  .. ............1..1...1.....1...2./.2.2/.//2//2/2///////.///././//.//.//////=//////././////./////.//.././=////===F=/==/=///.=////=/=@G      . (..//==@@GCOLOMOMOTTTTSTTTTTTTTTMTTOOMOOOLOGCGCGCFOCG@F@G=F=F@@F=@F@@F@GG@G@F@@G@F@G=G@GCF@G@GCGCG@GCGCGCOCGLOCOCGLGLOLOOOLOOOLOOOMOMOOOMOTOMOMTOTMTMTMTMTMTTMTSTTTSTSTSMOLLLGLLOMTMMTLMCLCCCC@CCC@CCCC@@@@9=8==8=*=*/***/*****.**(*.(((((((((((((*.**.*.***=*=888=88=88=8=8=*=*8*.*((((((*.((/*@9CMUXX[[[[[XSSMSVSVUVSMCC@@@8=8.  (.(.((.(((.((.((.(((.(((.           . ( ... (. .( .. .. .. .........../1.//2//2/./1/1/.//2//2////2/2//2//2=2=2////2//////////////=/=/=====//=/////=///=/=///.=.////===/==2=@F=F==F==//==////=/=@   (. (  ..///==@CCGLOLOMMOMTOTMTMTOTMOTOTMOOLOOCOCG@G@@G@GC@@F@@==@FC@F=@F@F@@G@G@@F@@G@@F@@G@G@FCGCG@GCGCGCCCGCOCGLOCOCOCOLOLOOOLOOOLOLOOOMOMOMTOTMOMOTOMTOMOMOTMOMTMMOMMTMMTMOLCCCCCCCCOLLOLLCCC@@@@@@@@@9@=9==8=8*=*/**/**/**(/(/(((.(((((.(((((((.((((((((.((***=**=*8*=***/***/*(((((((**(((*8CLMSVXXXXUVSMMMMSSVSSML@C=@9==*( ((.((.(((.(( ( ( ((        (  . . ( ..  . . ............./../.2/../.2../.1./.2./2./2/2///2///2//////././////.///////==//////././/////////.//.../.=/=//=/==2===F====/==////2/==   (..( .  . (.//==@@@G@CGLOLOLOLOOOLOOLOLGOCG@@@G@F@==F@=FCGG@F@G=F=F@G=F=F@@F@FC@G@F@F@@F@F@@GGC@G@GCG@GCG@GCGCCGLGLGLGCOCOLOOMOMOOMOOLOLOMOOOMOMTOTMOTMTOMTOMTOMOTMTMOMTMOMMOMMOLG@@@@@@@CCCCCC@@@@=8=8==8=/8/8/**/*(/*(.((((((((((((((((.*((*((((((*8@9CLLMMMLLLLCLLLMMMLL@@=8=8=*/               . . ...1..1....1 ..1 ............/...1/.1./..../......../.././///2/=//////././//.=////..//.././=///=2/=/=F===F=F=F=//////// ..(..( ..  ..(.//===@@@G@GCOCOLOLOLOCGCOGCOC@F@F@@@F@=F=F@FCG@@@FC==F@@G==@F=@F@G@G@@F@F@@G@@FCGCG@@G@GCG@CG@GCGCCGLGLGLGLGLOLOLOOMOOOLOLOLOLOMOOMOMOTMOMOTMOTMMOMMOMOMTMOMMOMMOMMOLL@C@@@@@@@@C@CC@@@===8==*=8/=*/**/*(*.((.((.((((((.(((((.(((*=89@98@9C9CC:CCLMMMLCC@@9==8=*(               . . . .. ....1...... ...1. 1...1/.2.././1/.1/.2../.././..././///.//=///////././/////////.//../.=///2===//==F@F@F==F=/////=./ ( (..(.....  (...//===@@G@GCOLOLOLOOMOOOLOLGLGGC@@F@G@F==F=F@GCG@F@F@==F@G@=F=F@@F@@@G@@F@F@@F@@@G@G@G@GCG@GC@GCCCGCGCOCOCOCLOCOLOLOOMOLOLOOLOLOOMOOMOMTOMOMOTMTOTMOTMOMTMOMOMMOMOMOMLOL@@@@@@=@@=@@@@@@@===8/8/=*//*/***/(*(.((((((((.(((((.((((.(((**=888/88=9C@CCLLMMLLLC@@@@===*/(                 . . . ..1.1..1...1.. .. ........../1.2././.../././././././././////2=//////.///////=/=//.///..///=//=2==2/=F@F@@F@@F//////// . (...(. (. .(..////==@FCGLOMOMOTMTTTTTTTTTMOTOTMGOCOCCGCG@@G@@@GCG@F@G=F=F@G==F=F=@F@@G@F@@F@@@G@F@GCG@F@C@G@GC@GCGCGCGCOCOCOCOLOLOOLOLOMOLOLOLOMOOMOMOMOMOMOTMOMTMOTMOMMOMTMMOTMMOMMMTMMOCCC@@C@@@@CCCC@CC@@@@=@==9==8==*=*/8*/*/(*.*(.((((((.((.(((((((((/*8*8/*=888/****.*((((((((((((/89@9=8*8@9LCLLMMSMMMMLMLLCCC@@=8.(  ( ((..( ( (..((((.(( (           ( .. . . . . .... .....1.2.2.2./2././1.2....1/2.2//2/////2//2/2/2/2///2////.///=2//====///=/////=////=///.//..../=2=/==F=2//=F=@F@F@@F//./=./  (..(.......(...////====@GCCOMOMTTTTTSVTVTSTTTTSTTTOLOLOLOLGCGCGCG@OCG@=F@===FC@F==F=F@F@@F@F@@F@F@@G@@G@@@@G@C@G@GCGCGCCCGCOCOLGCOLGLOLOOMOLOLOLOLOMOLMOMOMOTMTMOTMOTSTMTTMTMTTTMTSTSTTMTTSTMLLCCCCCCC@CCCCCCCC@@@@@@@9=@===8=8==*=*/**/(((.(.(.(((.((.(((((((((((((((/8=8=8=88=8/8=*/***/**.*(/((((((*=8=98=*8@CCLMSMSMMMMLMLMLLCC@@==*( .(.(.(.(.(.(((((.(..(.(.(.(           (. ( . (. .. .( ... ... (... ............//1///2/2/2///.//2.//////2//2/2/2/2//2/2=2/=2=/=//=/2=/F==2=F=F====////=========//=//////==F/F=F=@=2=@F@@F@G@=/////2/.(...( ..(...(////===@@GLOLOLMTMTTTMTTTTMTTOMMOMOLGLOLGLG@@G@@FCGC@F@@F===G@F===F=F@F@C@@F@F@@F@@F@@G@G=@G@G@C@GC@CGCGLCGLGLOLGLOLOOLOLOMOMOLOLOLOLOMOOLMOMOTMOTMTMOMMOMMOMLMOMMMOMMMMMTLMMOLC@C@@@@@@@=@@=@@=8=====8=8=8/8/******/*((.((.((((.(((.(.((((((**=*8/8*=**/****.((((((((((((8*=*8/88=:LMLMLLLCLLLLCCCC@==8=/(  ( (.((.((..( (.(.(..          . . ( .. ( ( ...........1.2.1/1/1/2.2.2/1/..1.1/1/.2//1////2/2/////2//=2////////=//=====//2==/////2//2=///////..//2=2===F=F/=2=/F@F@F@F////=//  . . ( .. .(..(//////===@@CGCGLOLOLOOMOLOOLOOGLGCG@@G@CF@@==F@@=CGC@F@@=F==GC====@=F@F@G@F@@F@F@F@@FC@@F@@@G@GC@GCGCGCGLGCOCOLGCOLOLOLOLOMOLOLOLOMOMOMOLOMOMOMOMOMTMOTMOMMOMOMOMOMOMOMOLMOLLLCC@@@=@====8/=8/8=*=*=*//*/**/(*.(.(((.(( (.(*(/*.*(*.(((((((((((*(((*=9C@@9@@@9@@@@=8==8/*/*(                   .. ...........1..................1/1//..2.2//.2///.///////2=//2=/2=/=//.//=/=/=//=///=././/===2=F===2/==2=2=F=F=/////=/  .. (. ....*////=/==@F@@GCGCGLOLOOLOLGCLCOCCG@G@@FC@F=@===F=@G@G=@F@====G@=F=F=@F@F@@@F@F@@F@@F@F@G@@F@@C@GC@GCGLGCOCOCOCOLOLGLOOLOLOLOLOLOLOLOMOMOMOMOMOMOMTOMOTMMTOMMOMOMOMOMMOMLOMLMOLLLC@@@=@====*/*/*=/*=*/*/*/*/***(/((.((.( ( (((((((((((((((((((*=8@9@@9@@@8@===8===*=*/((                 . . . . 1. 1. 1.1......1......1/..1/1..2./1/./1//2./////.//////=///==/=////////=/2=///=//=/////2/F==2=F=F/=2==/=//===///2=// ( ( . (..../////====@@@GCCCOLOLOLOLOLOOLOCOCOCGC@G@G@@F===@==GC@G==@====@G@===@F@@@F@G@F@F@@F@=@@@F@@@@@G@CG@CGCGLGCOCOCOCOLOCOLLOLOLOLOMOLOMOMOMOMOMOLOMOMOMOTMOMOTMOMOMLMOMLOMMOMOLMOLLLOLC@@@=====8=/8/*/*/*/*/*/**/*/(*(/((.((.(( ((((.*/*/*/(/(((((((((((/8=8=9@@@9@@=8==8====8/*/.(    (               . . . 1.. 1.. 1.1.1....1......1../.1.2...2./2//.2///////////=/2/=/=///=//////=//=2=/=/2=//.//=2==F===F/=2=F/2/2/=2///2==. ( .. .(././/====@@GCGLLOOMOMTMTTTTTTMTTMOTTMOMOOCOLOCCG@@@F@=G@CF=F@====G@@=F==@F@F@@F@@=F=@F@F=@=@@@F@@GC@GC@GCGLGLGLOLGLGLOCOLGLOLOLOLMOLOLOMOMOMOLMOMOMOMOMOTMOMTMOMMOMLMOMMOMMMOMMOLMLLLLCCC@@@@===8==8=/*=*=*=/*8/8/*=*/***/**/(.(.(((.(((.((((*/*/8/8=*8=8*8/*/*(/(*/(*/*(/((((((*=88@8@@@:CCCCC===@@C@CC@@====/(  (..(.(..*.(.(.((..(.( (         . . . .. . . . ..............1....1..1.2./.2../2/2.//2/2//2//2/./2/2/2/=2///=/2=/==F/==F==2===//2/==/==/=/=/=/=2//2=F=2=F=F==2=F=/////2=////2=.  . .(...///===@@@@GCLOLOMOTMTTTSTVTSTTTSTTTTTMMOLOMOLOCG@G@@@@GC@F@=F=F==GCF=@=F@@@F@@F@=@===@=F@F@@@@=@@CG@GCCGCGLGLGLGLOCOLOCOLLOLOLOLOOMOLOLOMOMOMOLOMMOMTMTMTMTMTTTTMTMTTMTTMTTMTMMTMTMTMMLLCCC@C@@@@===8===*=8==8=8=8/8*=//*/*=*/**/((*.((((.((.((((((((.*/*=8=8/=8=8=8=8*8*/***/**(*/***/((((*8=9@@9@CCCCLCC@==8=CCCCCC@@@@==(   (. .(...*/(..(.(.(.(.(.(.((.(.(         . (.  ... ( .....( .. ..... ...............1././1.1/.2./1//1///2////2/2/2/2//2//2/2/==2=2==F=2===/F===F===F===F=====F===F==F==F=F//2=F=F@=F@F=F====2///2=/2//2/=/  . (..././/===@F@GCGLOLMOMOMTTMTTTMTMOTMMTMOMOCOLOLLG@@@=@==@G@@==@@===FCC=F==@F@@F@@F=F=@=F=====@@F@@F@CGCG@CGLGLGLGLOCOCOLOCOLCOLOLOLOLOMOMOLMOMOMOMOMOMOMOMOTOMOMMMOMOMOMLMOLMMOLLLOLLOLLOLCC@@@@==8==8=*/*/**/*/*/*/**/**/*/*.*.*.(/(((.(.(( ((.((/(*/*=*=8/*8/**/*/**/(*(/*.*((.(((**=888=8=9@@C@@9=/8==9@C@C@@@C@=/    ...(..(.(..(.*.(.(.(..(.(.              (. . . .. . .. ..... .......1..1..1..1.../.2.//1//1//2//2/2/2//2//2////2=/2=/=2=/==F/=2==F/==F/2//=2==/=2==/=2=//=/=F==F=F===2/F/2//2//=////=2/  . ..(/.///==/===@@@CGCGLOCLOLOLOLOLLOOLOCOCGC@@CF@@========@C@@==@=/==@G@==F=@=F=@F@===F===@=@=F@@@@@@@GC@GCCGLGCOCOLOCOLOLLCOLLOLOLOLOLOLOLOLMOMLMOLMOMLOTMTLTOMMOMMOMLMOMOMOLOMOCLC@CC@CCCCC@@==8=/8/*/*/*(/(*((.(*((.(.((((.(( ( ( ((*.(((*.*(.(*(((((((((((((((*.(**/**=*=/8/((.*=8===@=8=/(  .( .( (...                . . . . .. ... ..............1/.2./../.../2/1//2/////////=/=/=/=/=/==////==/=2==/=2==2/2/2==F==F=F=F/2==/2//2=2=2//==2    ....////=======@F@@GCGCOCOCLOLGLGLCOCOCCG@@@F@C@=@=====@=@F@@F==@====@G====F=@==@@F====@====@=@=@@F@@@CGCG@CGCLCOCOLOCLLOOLGLOCLLGLOLOLMLMOLOMOLOMOLMOMTLOMTOMOMOMOMOMOMOMOMOLLLLLCC@C@@@C@CC@@@=8=*//*/*.*(/(.*((.(((((.( (((.((. ((.(((.(((.(((.(.(((((((((((.(/*/8/*/*.(/*/=@@@@==/(     ..(                  . . . . 1 . . . ..1 .1...1../1./1/2.1/1/.2/..///2///////=/2/=/=2=/=2=//2==2==/=2==2==//2=2==F==F=2=2/F=2/2//=2=//2=2=   (.././//=/====F@@CGCCCOLGLOLOLOLOLOLOLGCCGC@@@@G@@========@G@@===@/==FC@F=====F=F=@@======@===@F@@=@@@GC@CG@CGLGLGLGLOLOLOLLGLOLGLLLOLOLOLOLLOMOMLMOMOMOMMOMMOMMOMMOMOMOLLMOMOLOLLLC@@@@@@@@@C@@@==8/*/*/*(*(/((.((.(.((.((.(.(( (  (.((.((.(.*(*.*(((.((.((((((((((.(((/*===*.((//8=@@@@=/(     ( .(... (( .(              . . . . . . . . .... ....1..2.2.2.2.2.2.=1/./2////////2=/=/=2==//F=/2/=/==/==2====2=2=/2=F==F==F==/2==2/=2/2==2./===  .(.///==F=F@FCCGLLOOLMOSTTTTTMTMTMMOMMOMOLOCOCOCG@@@F@=F==@@G=F=@=====G@===F=====@=========@==@=@@@=@@GCGCGCGCCOLCLOCOLOLOLLGLOCOLGLLOLOLLOLLOLOMOMOMOMOMOLMOMOMOMOMLMLMOLMOMOMOLMOLLC@@C@@C@C@C@@@@==8/8/8/*=*8/**/*/*(*/*(/*(/(/(.*(.((.(.(.(.((((/*(/*=*=*/8/8/8=*=***/*(.((((((/((/*(((.(/=9@@==//8=@CCCLC@=/(  .. (...(../(..(.(.(.(.(.(..         .   .  .  . . . . . ......... .......1...1..1..1...1./1/.2/2/2//2/2/2///2=///2=//2=/=======2===F===F==/2=F==2==2=2=F==2=/=2=F=F=2=F/=F==2/2/=2=//2=2=     .(///===@@G@GCOCOLMTOSTVTSVTSTVTTTTMTMOMOLOCOLOLGCC@@@@@@@GCC=@=@====@@F========F@=@===@===@=@=@=@@=@@CCCG@CGCCOCOCLLOLOLOLLGLLGLLOLLOLOLOLOLLOMLOMLMOMMOMOMTTMMTMTMOTMTMTMMMMMTMLMMLLLCCCCCCCCCCC@@@@=8=8=8==8/8/**/*/**/*/*//**/*/(/(.((((.((.((.*.(*/*/*8=8=8=8=8==8==*/*/**(.(((((((/(*(/(*/((.((/*=@@@@===@@CLLOLC@/.  .....(..//(/./.(..*..(.(..(.(.((           ... ( .. .. . (... ......... ............. 1..1............././/./1/.//2/2/===2=2/2=2/2=/F===F=2===F=F==F=@F=F@F=F===F==F=F=F@F=F=2=F@FFF=FF=FF=FF=2//2==2///=2=    ..///===F@@@GCGCOLOMTTMTTTMTMTMTMOMOOMOCOCGCCLGCC@@@@@@==@@G@===@==/=C@@=========@======@====@=@@@==@@GCCGC@GCOCLOCOCLOLLOLLGLCOLCOLLOLOLOLOLLOMLOMOLOMOMOLLOMOMOLOMOLMOLMOMOLOLOLOLLLCC@@@@@@@@8@==8==8=*=*/*8/**/***.*(.*.*(/(.*.((.((.(((.((((.((((.*(/*/*/8/8/8/*8/***((.((((((((((/(..(/*=@@@==@CCCLCC@=/(   ...(..(..*./.(/(.(.(..((.((              . . ( ( . . . . . ...... . ... .... .. .1..............2.///2/2/2=/2///F///2=2/2/=//2==2=====F=/=F===2===F==2=====F/2=2=2==F=2=F=2=F==2=2/=2=2/2==2    . .././/=/=====@@CGCGCOLOLOLOLOOLOCOCGCG@C@@@@@@@@======@==@@@@===@/===@G==/=======@======@==@=@=@@=@@@@CGCCG@CCOCOCLOLOLOLOLCGCOLOCOLLOLLLOLOLOLMOLMOMOMOLOLOLMOMOMLOLOMOLOLLOLLLLCCCCCCC@@=8==8=*=/8/*/*/***.*(.(((.((.(((.(. ( (.(*((*.*(*((.((..(.*/=*===@@@=@=/.  ......(...(.(               . .. ...0...1/2/1//2/.2/2../=///2/////=//2=2==/==F//=/=2=/F=2==2==F/2/2=F2=F==2=F==F=2//2=F//2/=2=   ( .././//=2=====@@@GCCGLLOLOLOLLGCO@CC@C@@@@@=@@@=@=====@==@@G@======/=F@@===/==========@===@===@@=@@@=@C@CGC@GCGLCOCLOLOLOLOLLCCOLLLGLLOLOLLOLLOLOMOLOMLLMOLLGLLMLOLLOLLOMLOLLOLOCLCCCCCCC@@===8=*=/**/8**/***.*((.((.((.( ((.(  .(((.(.(((.(.((((( /*/===@@=@@@=/(     . ( .( ( (                   . . . . ......1///1//1//2//.2//2///////=2===/==2==2=2/2===F===2=F=2=//2=F==F=2F=F=2F=F=2=2==2=/2===    ...././///====F@@FC@GCLOLOLOLOLOLLLGCGCCG@@@@@@@@@@@=====@===@@@===@/===@@@=/==/======@====@===@=@@@=@@=@GCCCG@@CGCOCOLLLOLLOLOCCCLGLLOCOLLOLLOLOLLOLMLOMOLLOLOCOCOLOLLOLLOLLOLLOLLCLCCCCCCCC@@==8/=*=**/***.***.(((.((.((.(.( ( (.(*.*(/*(/(*.((.( .*===@@@@=@=/.    ( .( .(..( (. (                       . . . . .1 .2.2.2/1///2=.2//2////2/////=/=/=2===2=/=/===F==F=F====2/2=/F=2=F==2F=F=F=/=2/F/F//2=2=   ( ..././//=F@F@GCLOLOLOMTMTTTMTMTMTOMLOLLOLOCGCGLGLC@@@@@=@==@@@@===@=/==@@@=/=/==/======@====@==@@@@=@@=@CGCCGC@@GCLCOLOLOLLOLOCCCLOCOLCOLLOLOLLOLLOLMOLOMOMOLOCCCCOLOLOLLOMLOLLLOLOLLLLLLLLCCC@@==8==8/=8==*=*=8/**/**/*/(/*(/(/(.(((.((.( (.(.(.(/*=*=8*==8=8=8/8*/((((((((((.((((.//=@@CCCC@@@@=/.(.   (...(..(../(/(./(..((..(         .      ( .  . . ...... . . .. .. . . .. .............../.2//2//2/2/2==2//F=/2=/=/=2/==F==F==F==F=2==F==F=F==F=2=F//2/F=F=2F=F==F==F/2=/F=2=2=2==     .....////====@@G@GLOLMTMTTTSTVTTTTTTSTMOMMOMLOCGLOLOLO@@@@@@@=@@@@@===@====F@===/=/====@====@==@=@=@@@@@@@@@CCGCC@@CCGLOLLOCOLLOLLGCCCOLOCOLOLOLOLOLOLMOLOMOMMMOMOLGLCOMMOMMTLMMMOMLOLMOMTLMOMLLLLC@@@=8=8=8=8==8=8==*8/*/**/*/*/*(/*.*.(.((((.(*(*8/8=8=8==8=8=8==*/**( ((((*(((((/(.(  (.*==C@CCC@@=@@=/..(      .(././//.*/.//*.*.(.(.(.(.(         (...(. . ( . ( .. .( . (.. .. ( .... .......... .... .... ......../../././2.///2/2=/=2=/F==2==F==2=F====F==F=F@F=F@=F=F=F@F@F@F=F=F=F=F/F=FF@F=F=FF=F=F/=2=2=F//==2=     ......//////=F=@GCGCOLOMOMTMTMTTMTMMOTMOMOMOLCCCCCGLCCC@@@==@===@@@===@==/==@@==/=//=====@====@=@=@=@@@@@@@=@GCCGC@@GCCLOLLGLOLLOLLCGCCCOLLOLOLLOLLOLOLOLLOLOOLLOLOLG@CCGLOLOLOLOLOLCLGLLLCOLCLLCCCC@@=8=8/8=8*=8/8*=**/*/*/*.(.(/(((.((((.(.(((//*/=*=*=8=8=8==*8*(( (((((. .*/=@@@@=/=/===/..(   ...(.(/..*./(.(..(.(.( (          .       .   . ...... . . . . . . . .. . ........./.../1/.//2/2//2/=F///F/F/==F/=2/=F========F==/=2=F=F==F===F==2/=2=F=F=F=2==F=F=2=2==F2=2=2==     ...././///////=/==@@@GCGCGLOLOLOLOLOLOLOCGCC@G@@@@@@@@=====@=====@@@@==@=====@@==//=/=/========@=@@==@@@@@@@@@CGCCGC@CGCOCOLCGLOLLOLCCGCCOLLOLOMLOLLOLOLOLLOLOLOLLLOLCG@@CCGLCLOLLCLCCC@@@CC@CC@C@@@@@8=***/*(/**.*(.*(.*(.(((( ( ( (.((.(((/*.**/(*.**.*(. .*//=/./(.(/=/..    (. (..( .. (                  .. ..... ...1.././1/.2/2/2/////2/=2///=/=/2=/F/==/2=2=====2==2=F/F=/2=2F=FF=F==2F=2=F/2=2==F=2==F=      ..././/////=//=/=/==F@@@CGCGCOLGLOLGLCOCCCC@C@@@@====@=@==========@@@@@=@@==/==@@==//=/====@==@==@=@@=@=@@@@@@@@CCOCCG@@CGLCOLCGLLLOLOCCCCGLOLLLOLMOLOLOLOLOLOLOLOLOLOLCC@@@@CGLCLGLGCCCC@@@@@@@@@@@@@@=@=8/**/(**(/**.(((.((..(( ( ( (( (((.(((.((*.*((.( (.////(...(./=/.    .( .(.                 . ...... .....1..../.2//./2=2//2=/=//=2===/==2===////F=2==F/F/=F=F/=2=F=F=F=2F==F=F=2=/2=2===2=2=     .././/////2=//=/=F==@G@GCCCOCOLOLLLOLLOCGLGCC@@@@@@@@===@======@==@@@@@=@@===/=@===//=/=========@=@@@=@@F@@@@@@@GCLGLC@CCGLCGLLCOLLLOLGCCCGLOLOLOLOMOLOLOLOLOLLOLOLLLOLGCG@@@C@CCLGLLGCCC@=======@8@=@=8==8/***.*(*.*(*.(((.(((.(   (.((.(((.(((.( (.*/..(./(/..//=.    . (....(..(                       .   . ...........1../..1././.2.=/////=2//2==/=2==2==/=2=2/=F=F/=F=2==F=2==2@FF=F===2F/F==2=2=2==2=F/=F     . .../////====F=F@@@G@GCOLOLOMMTMTMTMMTMTTMOMOLOLGCCGCGCC@@@===@==@=@@@@@@=@@=/===@===/=/=/====@===@=@@@=@@@@@@@@@CCGLGCG@@CGCOCOCCCOLLOLO@CCCOLLOLMOMOMOMOLOLOLOLLCOLOLOCGC@C@@@@CCLCOLOLLCCC@@=@=@==8@@=@@8=8=/8=*=8/8/8//*/*/*/(.(/((/(((.(((.((.(.((.(((.(( (.(.(((((((( (    (///////////=//==/..      ( ....(././(..(.(.(.( (                   .  . (..... . . . .. . . .. . .....././//1//1///2/2/////=2//=F=2=2==F====F==F====F==2==F=F=F=F=F=F=F=2=FF=F=F=F2==F=2F=2=2/=2==2===    .././===2=F@@@@G@GCGCOLOLOMOTTTTTSTTTTSTTSMOMOLLOLCCCOLGCC@@@@@@@@@@GC@@@@@@=/=======//=/=======@==@@@@=@@@G@@G@@GCCOCLG@@CCCOCLCCCOLLOLOCG@COLOLOOLOMOMOMOMOLOMOLOLLOLMLOLGCGCCCGCLLLLOMOLLCCC@@@@@@@@8==@===8===8=8=*=*=/*/*/***/*.**.(/(.((.(.(((.*(.(*(.((.((.(((((.((((((((.(*.((((((((((.   ( (   (.///=/////=/==/=@=/...    .......(.../.*///*./*.(.((.( ((          ... ( ( ( (.  .     ( ...... . ........... .. .. ... .............////////2//2//2/2/2/2==/2=F==F=F==F=F@@F@=F=F==F==F==F@F@F@F@F@F@F=F=F@FF@F=F=F=F==2=//2==2==F=2        . ...///2====F@F@F@@GCG@GCOLOMMOMTMOTMTMTMTMOLOLOLCGCGCCGC@@@@==@@@=@@@@@@@@=@===/==@===//=/=======@=@=@@@@@@@@@@C@@CGLGCLG@@GCCCOCCGCOLLOLOCGC@OLOLOMOMOMOMOLOLOLLOCOCLGLGCOCC@@@@@@@@CCCOCOCLCC@@====8==8=8/8=*=8/8/=*/*=*/*/***/*.**.(.((((.((.((.((.((.(((.((.(.( (((((.((((((((( ( ( (   (.(.////.////////==/. ..    ( ( .. (....*...*.(..(..(.                          . . . ...... . . . . . . . . . ...././//2/////2////2///=2//==2=/==F=2==F==F==F=====2=2@=F=F=F@F=F=FF=2=FF@F=F=F=F=F=F/2=2/F/F=2===      .( .///=/=2======F==@=@@@FCGCGLGLGLLOLOLOLOCCCC@C@@@@=@=@=@@====@@=@@@@@@=@@@@@=========///8========@@@@@@=@@C@C@C@C@LCLGLC@@CCCCLCCCCGLMOLLGCC@GLOLOLOLOLOLMOLOLOLLGCCGCCLCOCGC@@@@@@@@@@C@CC@@@@@===*=*/*/*.*.*.*.*.(((.((.(( (((.(.  (    (   .....(../././//=...     (  . (..( (.(                   . ............1/././.2=./2/2/2/2=//2==2==/=/=/2=//=/F===F=F=F=F=F=F/F=F=F=F=F=F=F=2=/2=2===2=F=F       . ....///=/==F===F===F==F=@F@G@GCOCGLGLCOCCOCG@@@@@@@====@=@=@@==@@=@@@@@===@@@@@=========//=/======@=@=@@@@@@@@C@C@C@CCOCLGCC@@GCCGLGCCGLLOLOCGC@CGLOLOLOLOLOOLOLLOLLGCC@GCCCGLCCC@@@@@@@@@@@@C@@@@@@==8//*/**/(/(.*((.*((.((.(.((.(.       .....(./.././/=//...       ( ..                       . ...../.../...1/1///=..=//2//=2/=/2===2==/2==/=2//=F=F==F=F==FF=F/2@F=F=F=F=F=F=F=2//=2=F==2==        . ...././/2==2==F===F==@F=@@@G@CGLOLLOLOLOLOCOCCC@@@@@C@@=@@@@=@@=@@=@=@@@====@@@@@=========//*===8====@@@@@@@=@@@C@CC@CCCLGLOCC@@CCLGCCCCGLOLMOCGCCGCOLOLOLOLLOLLOLCOLCGCC@@G@CCGCCCC@@@@@=@=@@=@@@@@@@@==8/*/*/**/(.*(.(.(((.(((..((.(. ( (          .........././/=@/...      . ( .. .(..(.( (                .         . .../..../1/././/./2=/1/2=/2///2//=F/==2===/==/=/2==F==F=F=F=F=F=2=F=FFF=F=F=F=F=2=2/2===2==F=        . .....././=/==F=@F@@@G@G@GCGCOLOMOMTMTMTMTMMOMMOLOLOLLLCCCGCCG@@@@@@@@@=@@@====@@C@@=======/=//*=/==@==@=@@@@@@@@@C@CCC@C@CLOCOCC@GCCCGCGC@COLOMOLGC@CGCOLOLLOLCOLOLGLGLCOCG@@C@C@GLGLGCC@@@@@@@@@@@CCCCCCCC@@====8/8//*/8/*//*/*****/(/*.*.*.(((.((((.*(.(((.((.*(.((((.(.(((((((((.(((.(((.( ( (.(.(.(   ...//...//////=FCG=/..   ..........(..(./.(..(..(.(..                    ( . . ( . ( . ........  . . ( . . ....././///2///=//2/2=/F=/===F=/2=F/==F=F==F==F===F/=2=F=F@F=F=F@F@F/=2@FF@F=F=F==F=F/=2=/F=F=F==F      . ....././/2=2=F=F@G@G@GCCGCOCOMOTTTMTTTTTTMTMTMTMOMLOMOLOLCGCLCGCC@@@@@@@C@@@==@@@@@@@========*/=/8==@=@=@@=@@@@@@@@CCCCG@CCLOCOLC@@GCCLCCG@CCOLOMOCG@@CCOLOLOLOLGLOCOLOLLOLLCGC@GCCLGLLLCC@C@@C@@@C@CCCCLCLCLCC@@===8==8/=8/8/*/*/**/*/**/*/**/*/(/*.*.(((.(/(/*.**/(/(((.((((((((((/((((/(*(/(((.((..(.(( ((( ((*.(.(.(.   ( .././/.././//==@GCC==/..  (. ...(....(.././.*./(/(.(..((.(..                  ( . .. .   ...  ..(............./.....( .. .... .. .. .......././////=2=2=2==/=2=F@=2@F=F==F==F==F@F@@F@=F=F====F@@F@F@F@F@F@F=F=FF@FF@FF=FF=F==2=2==F===F==      . . ..././/2//=====F=@@F@@G@GCGCOLOMOTMTMMTMOMOMOMOLOLOLLGLCCC@GC@@@@@@@@===@@====@@C@@@@==/8====*/8===8@=@=@@@@@@@@@@@C@CCCC@CLOLOLG@@CGCGCC@CGCGMOLOLG@G@COLOCOLOCCCCLGCOCLGCCC@@@@=@CCGCCC@@@@@@=@@@=@@@@C@CCC@C@@@==8=/8/*/***/**.*/*(/*(*.*(.*(.(((.*(.((.((/(/((.*((.(((((((((((.((.((.(.((. (.( ( (.(.(.((.   ( .......///==F@GCF=//.  .. ......(..(..(..(.(..(.(.((                                   . ..  ( ....... . ( . (  ( ( . .. ...////////=/=/2/==2=F/=F===2/=F//2====F==F======2==F==F@FF@F=F=F=2=F=F@FF=FF==F==2=/2/F=2=F==F     . ......//.////2====F========@@@G@GCOLGLOCOLCOCGLCGCCCCC@C@@@===@@@@@@=@=@===@@=====@C@@@@@==/8==8=/*===9@@=@@@@@@@@@@@@CCC@GCCCLOLOLCC@@CCGLG@@CCGLOLOLOC@GCCCOLLCOCGCCCCCOCOCGCG@@=@===@@@@C@@@=@=======8====8======8/*/*/*/(.(((.(.((.(( ((.. ( ((. (          ...=/==@F@==...      .(.(. (.( (                        . .....././../././/==1/=2=2////2//2=2=/==/=/=2=//2==F==F=F@=F=F=2=2=FF@F@FF=F==F==2=/2==F=F=2=     . ......//.2//2/=/=2=====F====F=F=@@GCGCCGCGCGCCCCCGCCG@@@@@@=@====@@@@@@=@=@==@@@====@@C@C@@===8/8==8==8=@=@@@=@@@C@@@@@@@@CCCCCGCLOLOLGC@GCCGLC@@@CGCOMOLOCC@GCCOLOCCCGCGCCCCCOCCCC@@@==@=@=@@@@@@===========*==/8/=*=/8/=*/*/**.(((.((( ((.((.((.((.((.(.(   .(    . .///==F@@==///.       ..  (                   . ......./../.2/.2=//2=2=//2=//2//=//2==/===//2/=2=F==F=F=F=F==2=F=F@FF@F=F==F==2/=2=F===F==    .  . . . ....././/.//2/=2=F=F====F===@=@FC@GLGLCOCOCOCOCCCCGCCCCC@@@@=@==@=@@@@=@=====@@@====@@C@@CC@@=*==@@8===8@@=@@@@@@@@@@@@@@@CCGCCCCLOLOLCGC@CCCGCCG@@CGLOMOLGCC@@GCLGLCCCC@GCGCCCGCCG@@@======@==@@=@========/==*=/*//*/.*/*/**.*/(/(((.((.(((.(.((.(((.(((.((.(    .  .(.   ( .////==@F@==//..    ( ......( (                 ( .           . (.../././././/.///=2/===2//=/2///=/=/=/=2=2==2=/==F==F=F=F=F=F=/=FF@FF@F@F=F=F==2=2/F==F===F   . . . . ......././2./2/=//2=======F=@@F@G@GCOLOMOMOMOMLMOMOMTLMOLOLLLCCCCC@@@@@@@@@@=@=@==@@@====@C@@CC@@==8==@@@8==@9=@@@@@C@C@@@@@@@@CCCCCGCLLLOLLCG@CGCCG@@G@C@GLOMOCGCG@@GCGLGCGCC@@@CG@C@CCG@@@@@=@=@@=@@@@@@@=@=@==@==@=====8/=/8//*/*/**/**/**/*=**=*=*/*/*/*.*.(((.((.(((.(.((((((((((((((.((( (((.(.(.(.(./(.(.   .. ..///=/=F@@F==//..   . . .........(...(..(.(..(.(.(                             .   .  . . . (.........( . . ( .. . . ...(....././///////2/=/2=F==/F=F=F/===/==2===F=====F==2=F==F=F=F@F=F@F=2=F=F@F@FF@F=F=F==2/==F==F=F=  . ( .. . .....././/.///=/2=/=/=F==F=@@F@GCGCGLGLOMOMTTMTMTMTMTTMTMMMOLOLOLLCC@C@@CC@C@@@@@@@=C@@@===@C@@C@C@=8===@C@=8=@@@@@C@@@@@C@@@@@@@CCGCLCCGLLOLOC@C@CGCCG@@G@CGCOLOLGC@@@CCGLOCOCCGC@CCGCGCCCCCGC@@@@@C@@@@@@C@@@@@@@@@@=@========8=8/=*/*8/**/**/*/***/***/*.**((/(*(.(*.(((.((.(((((((((((((*.**/*(/(..((.(((.(.*.(.((./(.*/*//(..        . ..././//=F@GC=@===//..    . ........../..(..(..*..(.(.(.(.                       (.. ( .  ( ..  . ... ( ( (...( . (...././/......(... (........(...///./////=/=/===/====F@F=F@@F==FF===F@F=F=F=F=F@F==F=F@F@F@F@G@F@FF=FF@GFGFC@FG@F@=F/=2/F=F=F==F. ( ( ... . . . ...././//2/2/=2==2==2===2====F@@@@G@GCOCOLOMOMMOMOLMOMTOMOLOLLOCCCC@@@@@=@@@@==@@=@==C@@8=8=@C@@CCCC==8@@@CC=8=@@@@@@C@@@@@@@@@@@@CCCCCCCGLOLLOCCG@CGCCGC@G@C@GCOLLOCG@@@CCCCCGCCC@@@@@@CC@GC@C@@F@@=@=@@@==@=@=@=======8===/8/8/*/8/**/(*.**.*(**.*(*.(((/((.((.(.(((.(.((.((((/(*.*(.( ( ( ((.(.(.((.(.(.(.(.(     ( ...././=@@F==////.   ( .............( .(..(.(..(.(( (                  (            .. ( ...(.../...(....( . . ( .......(././././///////=2/=/=F===F=@=2===2====F==========2==F==F=F@F@F@F@F/=FF@F@GFG@F@FF=F=2=2=F=F=F==. ( . (. . (. . .......//2//==2==F=F=F==2====2=========@@@CGCGLOCOLGLOLOCLCCCCC@@@@========@@@=@=@=@@=9=C@@==8=@C@@CCCC@==9=@CC@=8@@C@@@C@@C@C@@@@@@@CCCGLCCGLOLLOCCC@@GCCG@@@G@C@GCOLGCG@@F@@G@CCGCG@@@===@@@@G@G@@@=========/=/=//8//=*//*/*/(/.((/((.((.(((( (( ( (((((  .( (.(.( (    ..../==//////...    .  (   .(               (.. (        ( .. (......././.././.=///==2==/==///=2=/=/=2=2=/=2=/==2==F=F=F@F@F=F=FG@GF@G@F@F@F@==2/=F=F=F=F@...( . (. . .... . ../././/2===F=F=F@=F=F==F========2===@F@@CCGCOCCOCLGCLGCC@C@@@@=@=====8===@@=@=9=@@@@=C@@=8==@@C@CCCLC8=@9CCC@==9@C@C@C@@@@@C@@@@C@CCCCCOCCLOLOLGCG@CGCCGC@@@G@@CGCLGCGC@@@@@GC@CCC@@@=====@@@@@@@@@@======/==/=///*////*//*.*.*((.((.(( ( (( ((   ((   .(. ( ( .     . .//=////./...                         (        ( . ( ..(....../.././=./===2=/F/=2/=/==2==/=/=/=//2=F=2==F=F=F@F@F=F@FG@G@GF@GF@F@F=2=2=F=F=@F=...... ... .. .......././2/=2=F=F=F@F@=F=F========2=====@@@GCGCCLGLLOLLOLLCGCC@@@@@@=====@8===9@@=@@=@9@@=C@@@=8=:C@@CLCLC@=8@CLC@=@@@C@@C@C@@C@C@@@@@@@CGCCLGCGLLOLLCGCGCGCGCG@@@G@@@GCLGCG@@@@@@C@GCG@@@=======F@@C@@@@====/==///=//=*//*/.*/*.(.(.(.(((.(((( ((.(( (((( .(..( (..        ...(/////.....      . . . .( (                     .. (        ( (. ( .( ...../././/./././=//2=======/=//=2===2==/==2//2==F==F==F=F@F@=F=G@GF@FG@GF@F@F@F=2=F=F@F=@F/..................././2./2==2=F=F@@F@F===F=F===F==@=@G@GCGLOLOMTMOMTMOMMOMMOLLLLCCCC@C@@@==9=@=@@8@@=@@@=@@C@9==9@CC@CLLCC@=@8CLLC=@@@@C@@@C@@@CCC@@C@@CCCCCOCLGLMOLOLG@GCGCG@G@@@@G@@@GCGCG@G@=@@@@@@C@C@F@=====@@@@G@C@C=@====@========/==/=/8/=/8/*/***/(/(*.*(*(.*((/(.(.((.((((.(((( ((((((((****(*(((( (.(..  (.(( (.(.(.(.(..(.(...(      ..//=////./...    . . . .......(.......(..( ...( (                 . . ( . ( (           (  ...../.........*..... ( .(...(./../.*.//////=/////=/==F==@F=F@=F===F====F==F=F===F/=F==F=F=F@F@FGF=F=FG@G@G@F@G@GF@F===2@F@F=F@F//..................././///2==F==F@F@F@F=F==F=F==@F@F@@CGCLOLOMTMTTSTTSTTMTMMMOMLOLLLCCC@@@@@@@@@@@9@@@@@@@9C@@@9=@CCC9CLLLC@@=@CLLC=@@@@C@@@C@@C@CC@@@C@CCGCCOCLGLOMOLLGCC@GCCCG@G@@@G@@@CGCGC@G@@=@CGCGCGCCF@F@@=F@@@GCCGCC@@@@=@=@=============8=/8=*/*/***/**/*/**/**(/***/(*/*/(*(.(((.(.((*.*/*/*/*.(((((((.((.(( ((.((((.((.(.(.(./.*./(//(..(...(.    .  ( ..//=//////...   . . . ... .(...........(..(..(..(.(.( .((.(( (     ( ...             (  ..... (.......(.. (. .(. ( ( .. ..( (. .(..(.//////./.*.//.//(..(..(..(//.//*/.//////=/=/=====/==F@@F@@@F@@F@F==F@F@F@F@F@F@F==F=F@F@@F@F@G@G@F@FCGG@G@GG@GF@G@G=F/F@F@F@@F@=//.../.../.../././././1/2//2==F=F@=F@@F==F=========@@@@@G@LGLOLTLTMMTMMMOMMOLMOLLCLCCC@@@=8===8=9=@8@@9@@@@@CCC@@9@CCCCCLLLC@@@=@LLL=@@@@C@C@CCC@CCCC@@CC@CCCCOCOCOLMOOMGCGC@G@C@@@@G@@@@@@@C@G@@@@====@@@@@@@@=======@==@@@@@=@==========/=//*/=/*/*/*/***.(*(.*(((.(/(((.(((.((.((.((.(((.(((((((((((((( ((.(  ((/(.(.(.((.(.(..(..*..(..        ..///....(..   . . . ( . (.......(..(..(. ( ( ( (                 . ( . ( .( .( . . .     ( . ( . .(.(./...(..(..*...(......(..(../(/.///////=//=//=//=F@==F@@F=@F====F=F======F=====F=F=F=F=@F@F@G=F=F@FG@F@FG@FG@GF=F=F@F@F@F@@=///../././//.//.///././///2/2=F=F@F@F@F=F===F=2==2===/===@@@GCCCOCOLLGLCLGCLCCC@CC@@@==8==8===8=8=@@8@@=9@@@9CCCC@@@LCC9CLLLL@@==9CLL@=@9@@C@CCCC@CCCC@C@CC@GCCOCOCOLOMOLOCG@C@G@G@@@@@G@G@@@@@@@@@F@===F@F@@@@F======/===/=====/==/////*/*./*..(.((.(((.(( (.(.   (    (.( .         .//./...(.          ( . (                          ..( . ...( (.   .(.( (...(.....//.//.///=///=/===/==/==/===========2//=====F=F=F=@F=F=F@F@F@GF@FG@FG@G=F==GF@F@@F@@@////././///////./2.//1/2//2/==F@F@@F@F=F==F========/==/==@@@@G@CLCOCLGLCCCCCC@C@@@@====8==*==8=8=@9@=@9@@@@@9LCCC@@@CLC@CLLLLC=@=@CLLC=@@9@@CCCCCC@GCCGCCGC@CGCCLOCOLOMOMOLG@G@@@G@@G@@@@@@F@@@G=@@@=F=====@F==@======/==/=/=/==//=//////(/./(.(.(.((.(.  (  .((.( (   . ( (            ..//(.....                                             (  .       ....(..././././//=/=/==/===/=/2===2==2=2=F=/=2=F=2=F==F=F@F@=2=F@F@FG@G@FG@G@GF@F=G@GF@F@F@@F=//./////.//2/////2////2//2=2=F=F=@F@F@=======/=2==/====F@G@CCGLCLOLLOCLCCCCCCCC@@@@==8==8=8==8=8@@9@=@@9C@C@LCLC@@9CL@@CLLLL@@=@9CLLC=@@@@CCCCCCCCCCCCCCCG@CCGCLGLLOLOMOLLGC@G@@G@@@FCF@@@@@@@@@F@===============@====/==/==/=///=//////(/(/.(/((.(((.(((  (.( ((.(.( (   (.              (./....(.         . (. .                                    .( ( ( (....( (.  ( (.( .( ..(..//.//./////=//==/====/=/===2=====2====2//==F=F=F==F=F@F===F@F@F@FG@G@G@G@F@F@GF@@F@@FF@@F=///.////////2/2///2/2/2/2/=2@F=F=F@=F=F====F======@F@CCGCOLMOLOMMMTMMOMMOMLLMLLLCC@@@==8=8=8==8@=9@@9@@CCC@CLLCL@@CCLC@LLCLL@@@=@CLLC=@@@@CCCCCCGCCCGCGCCCG@CCGCOCLOLOMOLOLG@@@@@G@@@@@@@@@@@F@==@======/=====@=@@@@=@==============/==/=//=///*///*/*/*.(.((/(((/((.*((.((.((((.((((((((((((((((((((( ((.(.(/*/*.*.(.(.((.((.( .(..                .//(...... ( .......(..( ..( (. (. (.( ( .   .... .        ( . (. . . .......( .. ... (. ( . (    ....(...(...(../.(/.*//.(.(/(...*..(./././*//=/=/===/===@@=@@F=@=F===F=@=F=F=@F=F@===F=F@=F=F=F@=F@2==FF@F@G@GF@G@G@GF=F@GG@G@FF@@F@@@=/////////2/=//=2=/2//2=/2==2@F=F=F@F==F=2====F=@@@@CGCLOLOMMTMMTMTMTMTMMTMMMMLLMCCCC@@@@8@=9=@@@CC@C@C@CCCCCCLLL:@@CLC9CLLCLC@@=8CLLL@@@@@CCCCLCCLGCCCCGCCCGCCGCOCLOLOLOMOLGC@G@@G@@F@@@F@G@@@@@@@=@==========@@@@@C@@@@@========@========/=/=/=8//*/*/*/*/*.*.*(/*/*/(*/**/*/((*.*.(.((((((((((((((((((((((((.( (((.(*.*/*/*//*/*.(.(.((.(//(/.(...          ( .     ( (...///...  . . . . . ... ..../......(..(..(.(.(..(..(..(........ ...(.. ( .      . . . . (. ...(.... .../.....(.....(. .(.(. (... ( . (..(..(/..(.////*.//*///*///*./*///(//*///////==/========@@@@@@@@@@@==F@@F@@@F@@@G@F=F=@@@F@F@@F@F@@@F=F@@G@G@GGGGGGGGCF@=FG@G@FG@G@@F@F@==//////2//2/2/=/2=2=/2=2/=F==F@=@F=F======F=====F@@@GCCCOLOLOMOMMMMOMMMOMLMOLLLCC@@@@@8=8=8=8=9@9@@@C9CC@CC@LLLLCC@CLC@@LCLLL@@=@@LLLC@@@CCCGCOCOCLGCCCG@CCGCCGCGLCOLGMOLOMOC@G@@@@@@F@@@@@@@F=F=======/=///=/===@=@@@@@@====/===/=/=///=*//*//*//*/**.*(/((.(.(/(*.*(*(.*(.((.((.((((.(((((((((((.(((.(*.*(*.*.(*/(.(.( (.(.(.*..(..           .    ....(    . . ....(.........(..(.(.(..( ( .. . ... ( . .           ( .. .... .......... ( ..... (. .. (   ...(..(..(..(/./(/.*./.*./(/(/.(//(/.*.///*/==/=/======@F@@F@@@F@====@F@F@F@F@F@@F===F=F@F=F@F=@F@F==2@F@F@G@G@GC@G@GFF=G@GF@GFGF@F@@@=F==//////=2=/=/2=/==2=2/=2=/F==F=F=@F==F==============@@@@CCCCCCOCOCLLCCLCCC@CC@@@=8=8==8=8==9==@=9C@@C@CCCCC:LLLLCC@CCC@CCLCLLC@@=9LMLL@@@C@CCLCCOCCGCCCGCCCGCCOCGCCOLGLMOLOOLC@F@@F@@@@@F@@@@=@=======///=///////==/========//////(/./(./(.(.(.(.((.(..((.(( (( (((   (.((( (((( (( (( ((.(.(.(.      .    .(..          ......      ( (                     . ( .                   . (..( (.(.( (.( ((...(..( .(..//(.//.///=/===/====/==//==========F==F//F==F==F==F=F=F=2=F@F@F@G@GGGG@GG=F=G@GGFG@GF@@F@F@@=F/=//2////2=/=2=2=2==2==2==F=F==F=@F=======F====/=====@@@CCGCCCCLCCCCCC@@C@@@@=@==8=8==8=8=@8@=9@@C@9C@CCCCC9LLLMCC@CCL@CCLLLLC@@@@CMOM@@CCCCGLCOCOCCGCCGCCGCGCCGCGCCLGLOMOLOLGC@@@@@@F@@@F@F@=F@======///////////////===/====////////.*.(.(.(.(( ((.(. (( (( ((..(     (((.(((.(( (.( (..(( (.(.(..       . .   ....       . (. .   (  (                    .               . (. ( ..( (...( ( ../..(.././.==/===/=/===/=/======F====F=========F==F=F=F@==F@@@F@@G@G@GGG@G=@F@GFGFGFG@F@@@@F@===/2//2/=2=/2==2=2=2==2==2=F==F==F==F===2========/==@@@@CG@CCCCLCLGLCLCCC@C@C@@@===8=8==9=@=9@=@9@@@C9C@CCCCC:LLLLL@@CCCC@LLLLLL@@@@CMMML@CCCCCCCOCGCCCGCCGCGLGCLGCGCGCGLGLOLOLOC@G@@@@=@@=F=@F@=F======/=//////.//*//.//=/==/====/=/*//(/.(.(.(.(.(.((.(( (.(.(( (.( ((( (  ( .((.(((.(((.((( ( (.(.(.(.(.(.(.         .   . ( .         . (.. ... . ( ( . ( . ( (                       .. .(. .                  (..(..(.(.( (.(.( (.(..(.(....//(/.//.//=====/======/=/==F=====F==F==2==F==F==F=F=F@=F=F@F@@F@FG@GGCGGG=F=G@GFG@GFF=F@F@@F@===////2//=2=/=2==2==2=F==F=F=F=F=@F===F==/======@@CG@OLLLLMOMMLOMMMMMLLLLLLCLCCCC@@@@@@@@@9@@@@=9@@@C@CCC:CCCC:LLLLC@CLL@@LLLLLLC@@@CMTMOC@CCGCCCOCOCOCCGCCCGCGCCGCGCGCGLGLOLOLOGCG@@@@F@@===@=F==@======/=//////////=//=/======@=@========////*/*/.*/.**.*.*.*/(*/(*/(/(/(.*.(((.(((.((( (((.(.(.(.(.*.*//8//8//*/*.(.(.(.*..*.(.(.(./(..(.(..(              ..    . (.    . ........../../..(..(..(...(...(... ( .... .. . .    .  . . ..... ../.././..... (. ......(..(... (  ....(...(..(..(/.//(//.*/.*/.*/.*/.//*/////(/==//=/====@@@@@@@=@=@@===@F@@F@F@@F@@F===F@F@F@F@F@@@F@F==F@FG@G@@G@GGCG@F=F@GFGFGFG@==@@F@@F=F=/=////2/=/2=/2==2==2=2==F==F=@=F========/===@=@@CCOLOLOMMTMTMMTMTMMMMMLLLLMLLLCCCCC@C@C@C@@C@C@9C@C@CC@CC:CCCCLLMLC@CLLCCCLLLLLC@@@CLTMMCGC@CGCCLGLLGLCGCGCCGCGCCCGCG@CGCGCOLOLLCG@@@@F@F@F=@@=@F@F@=@========/=/==/===/=/=/===@@@@@@@@@======/=///8///8/*/**/*/*/*/*/**/*.**/*(/(**/(((.(/((.((.*/*/*=8=8===*=*/*/(.(.(//*/*/*//*/*/(/*..(/(.(.             . (    (......    . ( ....../...//.//./..(..(/..*..(//.(..(......(........ .. ( . .. . . .. .....  ..... ....... ././//.////./.............(...(..(...(././.*...(...//*/////*///////*//=*//=//=*///==========@G@G@@G@G@@G@@@@@G@C@G@@@G@G@F@@G@@@@@G@G@G@G@F@GCG@G@GGCGGCGGG@F=GFG@GFGFF==F@F@G@@F==2=/////2=/=2=2==2===F====F=F====F=====/===/===@@@CCLOLOMOMLOMOMMMOMOLLLLLLCCCCCC@@C@@C@@@@@9@@@9@@C9CCCCC:CC@CCLMML@CLLLCCLLLMLMC@@CLTTMLCCCCGCCCGLLGLGCCGCCGCGCGCCCG@CGCGCGLGLOCG@@F@@===F==F==========//=////*//=///=/////////============/====//*=/*/(/(/*.*/(/*.*.*(/((.((.((.((.(((.((((/*.*/*/*/*//*//*/(/(.(/(/*.*/.*.(/(.(.(.(..(..(.(             .(     .. .../../././../.*..(...(/.(...(.( . (. .....( . .  . . .   . .. . (..  ....././../..(./..(../.(....(...  (..(..*..(..(.(.(//*////(/*./*//*/*/*///*=////==//==//==@=@@@@@@@@F@@=F@@@F@F@F@F@F@==F@F@F@F@@F@F@F@F=G@G@@GF@G@G@GGG@F@=GFGFGF@GF/==F@@@@F@=F==////2//2=/==2==2=F/F=F===@F=F@=F=====//=/=======@@C@@CCCLCLCCCCLCCCC@@@@@@@=@8@@=@9@@@=9@@@9@@@9@CCC9CCCCCCC@LLMMLCCLLLCCLMOLMLLC@@CMTSTCGCCGCCCGLOLLGCCG@C@GCCG@GCG@C@G@CGCGLOLGC@G@F@==========F=====////.///.*.//.*/.//*//./(././(/.*.*.(.(/(/.(.(.(.(((((( (((.(.(((( ( (.(.*.*.(/(/((/(.(.((.( (.(.(.(.(.(.(.(..(.((.(.(.(.          ..       . .. ....(...(. .(....( . ( (                .. (. .... ... (        ....   .( (.(.(.(..(..(.(.(.((..*.(.(//.(////*.================@=F=@======F/==F==F=@F=F=F@F@=F=G@FG@@G@G@G@GGG=FFG@GFGGFGF//==@F@G@F@===2=/////=/2=/=2=F/=F===F=F==@=@=======/=/=/=======@@@CC@CCCCCCCCC@C@@C@@@=8@==@=9@@@@@@@=9@@@9@@C@9CCCC9C@CCCC@LLMMMLCLLLLCLMOMMOLCC@CTSTTLCGCCGCCGLGLOCGCG@C@G@GCCG@G@G@G@CGC@GLGLGC@@F@=F=====F===/==/=///////./*.//./.(././(/./././(..(.(..(.(.(.(.(.(..(( (.(.(( ( (( ((.((.(.*(.*.*(.(((.(.(.(.((.(.(.*.*.(.(.((..(.(.( (.(..(.        .          . ... .... (. . (. .( .                    . . (        ( .   ..(..(..(..( (.(.(./(.(.//(../(././=/==/==/====/==F=@=F==@=F===/=F===F@F=@F=@=F==F@G@@G@FCF@GFG@GF@FGGFGGFGFG///==F@@@@F@F===/2=/2///2==2==2==2===F===F@========/=//=/=====@@@CCCCCCCCLCCCCLCCC@@@C@@@@@9=@@@@@@@9@@@8@@@9@@C9C@CC:@CCCCC@LLMTMLLCMOLLLMMMMTMGCCCLTSTMCCGCCCLGLGLOCCCG@@C@CG@CG@G@@@FC@G@CGCGCOCG@F@=@====/===2==///////////./*././.(/(././(.*...*...(..(..(.(.(.(..((..(.(.( (.(.((.((.(/*.*.*.(.((.(( ((.(.(.(/*/(/(.((.(.( ((.((.(.(( (.(. ( (             .        . .............(....... (. . .              ( .. ......... . (       ..( .   ..(..(.(..(.(.(.(.((.(.(/(..(//.(/=/./*============/===@@=F@==F==F===F=F=F@F@F=@F=@@F@F@G@GFG@FG@@G@G@FF@GGGFGGGFG/./=/==F@F@@F@=2==////2//2/=F/F/F=======@===@=======/=/=/===@@CCLLOLLLLMLMMLMMLLMLLLLCCCCCCLCCC@CC@C@@@@@9@@@C9@@:@CCC@CCCCCCCCLMMTLLLLMOLOMTMMTMLGCCOSVTTCCOCOCCCCGCOCGCG@@G@G@G@G@@G@G@@@G@@GCCGCCG@@@F=F===/====/==/=/////////=//////////*/////////////////*.//*/*/(//*.*/(/(/(/((.*.*(//*/*/*/*/*//*.*.(.(.(.(.(.*.*/=/*//=*/*.*.(.(.(/(.(.((.(.( (.(( (  ..                ....../././....../././././../....(............ . .. . . .  . . .. .. .  . ( . . .. .. ....../././/.///./.../.........(.. ( .(././/(...(.../(/.*//=//*//(/*/*//*//*/=///=====/@/=/==@@@@@@@@@@@@@F@G@G@@G@@F@@F@F@@F@G@@F@F@@FG@F@G@G@GG@GFCFG@G@GFFFGGGGFGGGH.///====@G@F@F@==/2///=////2===2=======F==@==F====/==/=/===@@CCCOLLMTMMMTMTMTMMMMMLMLLLLLLLLLCLCCCLCCCC@@@@@9@C@@@C@CCCCCCCCCCCCMMTSMOLMMMOMMTMTMTMMGCLMTVTMLGCOCOCCGCGCCCGC@G@G@@@G@@@F@F@F@G@@G@GCCG@G@@@F@F=F=======/========/=/==/=////////=//=/=////=////////////=*///*//*//*//*//*=/*/8/*=//*//*.*//(/(/(//*//*/==/8====*//*/(/(/*//*.*/*.*.(.(.((.(.(.(.(/.( ( .( .          ...    . . .. ...//./////.//.///.//.//./././....................... ... . .. ............. .... ......... ....//.//////////.///.//././/......(..//*.////../*.//////=*===/*=//*//*//8/=*@==/@@@=/=@====C@CC@C@@CG@G@@@CGCCGCG@G@GCG=@GGCG@GGCGCG@G@G@@GGGGGGGGGGCGG@GGF@FGGGFGGHGH..///=2=F@@@@F@F==/=////////2/==/F=2=F==============/=/=/====@@@CCLOLLOLLMLMMMOMOMLLLLCLCLCLCCCCCC@C@@@9@@9@@C@:C@C@CCCCCCCCCCCCCLMMTSMOMTMMOMTMTSTTMGCOLTVSTLOCOCGCCGCGCGCGC@G@@G@@F@G@@G@G@@G@@F@G@@G@G@F=@======/=////////////*//=///////*//.///*////////*/////*//*./*/./*/(//*/*/*/(*/*.(.((.(.((./((.(.(/(/(/*//*//*//*/(/*.(.*/*//(/(/(.(.(.(.(( ( (...(. (               . ....././/./..././././/./.././............... . .. . . . .  . . . ..... . . ... ... .. (.......///././///./../...(.....(.....(..(./(/.(/.(././(/////*////*//*/*/*//*//=/*=====//@/=/=@@G@@@@@@@@@F@=@@G@G@@G@@F@@F=@@G@@@@G@@@FCFCF@G@G@GFG@GFG@GG@G=FGGGGGGGGFG.././/=/==F@F@@F===2///////////=/==========F=======/===/=/=/=/===@@CCCCCCCLCLCLCCCCC@@@@@@@@@@9=@@@@9@@@@@@9@@@C@C@CC@CCCCCCCLCCCCCMTSSTMMMTMMTMTSTSTSOLLOSVVTMLCLGCGC@C@CGCGCC@@@@G@@@@F@@@FCF@@@@@F@@@F@@@F=F=====2/=////.//..///./......(.......(..(.(..(....(..(.( (..(..(.(.(.((((.(.((.(.( (.(.(.(.*.*/(/(.(.(.(.(.*.*//(/(/(..(.(.(.((..(.*.   .                      ......................... . . . ..  .     .( .......... .. (. .(.( . (  . ( . .. (. .(.(.*./(.(.(.( (.(.((.8..(=/=.*/=./*/=============/======F@=@F@F==F@F@F@F@@F@F@@F@FF@FFFG@GGFG@GFG@GFFGGGGGHGGGH...//.//===F@@F@=F==//2/=///////2=/==========@======/==/==/=*=/===@9@CC@CCCCLCCCC@@@@@=9@@@=@=@9=@@@=@9@@@@@:@C@C@C@CC@CCCCLCCLCCCCCMTSTSTMTMTTMTTVTSTTTMLOSVVVTLGCCCGCG@@CGCGCGC@@@@@F@@F@@F@CF@G@F@F=@@F=@@F=@=====//////././(/././//../../../....(.....(........(....( (.(.( ((( (..((.(.(.(.(.(.(.*.*.(.(.((.(.(.(/*//*/*/(//*(/(.(.((.( ( (.(.(.  ( (                       ................... . .. . .    .     . . ( . ... .. ( ...      (  . ..(.(.(.(.((.((.(.(./(.(///(../(..*===/=/===/==/=/==@F=@F@=@F@F==F@@@@F@F@F@F@@F@F@FF@FGFGG@GFGGGF=FGGGGGGGHGG..././///===F@@@==F==////=////////=/=======@=======/==/==/=///=*==@@C@CCCCCCLCLCCCC@@C@@@@@C@@=@@=9@@@@@9@C@@C9@C@CCCCCCCLCCLCLCLCCCCMTSVSSTTSTSTSTVTSTSTMOMTSVVTMGCGCG@CG@@C@GCGCF@F@@@@@F@@F@G@F@@@=F===F===@=F===/=/=////../.././././.../...(./.../.(...(....(...(. (.( (.(.(.((.( (.( (.(.( (.(.(.(.(/(.(.(/(.*.(.(.*.*/*//*///*/*.*./(.*.(.(.(.((.((.(  (.( (   (                  . .................... .. .. .. . .. .  . . .  . . . (. ............. .. .....( .. . ( . ...(. .(.(..(.(..((.(.(..(.(.*/.*==////=/*//============/=======F@=F@=@F==@F@F@G@@@@F@F@F@F@F@FGFGGFGGGFG@GFFGGGFGGGGGH...././///=/==F@F===2=//2//////////==/==============/=====/====@@@CCCLLMLLLMLMMMLLLLLLLLLLLLLCCCCCC@C@@@@9@C@@@C9C@CC@CCCCLCLCLCLCLCCCLTSVVSSTSTSTSVVTVTSTTMMTTVSTMOCGCG@CG@@G@CGCCG@@@F@=@@F@@@@@F@F@=F===@==F==F=====/=/////////////////.///.///.//////////.///////.///*.//.*.//(/.(/(.(.*.(.(.(.(.*.*.*//*//*.*//(/*.*//*=/====*///*//*.(/(/(/(.(.(.(.(.(  (.( . (            . . ...././///////.//////./......../...../....... ...... . . . .. ......... ............. ......../././//////..//.//.//.//......./(..//.(./././*.///8/=/=*/=/*//*/*//*//==//@=@*====/==@@C@@@@@@@@@===@@@@@@G@@GC@G@FCGCGCGCGGCG@G@FG@FFGFG@GGFGFGGGHG=FGGGGGGHGGH..../././//=/=====F===/=//=/.////////=/==@==@=====/===/====8===@@CCLCLMMMMMMMTMMMMLLLLLMLLMMLMLCLCCCCCCCCC@C@C@C@@C@CCCCCCLCLLLLCLCLLCLCOSVVVSTSTSTTSVSTVTSTTTSTVVSMOCCCGC@GC@@G@C@C@@@@=@F@@F@F@=@@@@F@@F@F=@====F======/=////=/=//=/////////////=/////////////=//=/=/////*////*/////*/*/*.*/*/*.*/*/*///*/////=//=/*/====@========8/=*//8//*/(//*/*./*//(  .(.(.(.((..(..(            . .. .. .....//.////=//////////////././//../.././................... ................ ............. ././///./////////////////////=/////.././////*./////*////*=/=/===8//=*///*/=/8=====@@@===@===@CCC@@C@@@@@C@@@@G@GCGCGCGCGLGCGCOGCOGCOGLGGGCGG@GFGGGGGGGGGGGGGFGGGGGHGGGHG..../././////=/=F======////////////==/=============/===/8=/=8===@=@CCCCOLMLLMLMMLLLLCLCCLLLLLLLCCCCCC@@@@C@@C@C@C@:@CCCCLCCLCLLLLCLLLOLLLMTSVVVSVTSTVTVSVTVTSTTSTSVTTMGCGCG@GC@@@G@@G@@@@@=F@@F@@F@F=@=============/=/=////////////////////././././///////////*////////*//./(./*//(/(/(..(/(..(.(/(.(.(.*./*/*//*/*//=*===========//*/*/*/(/(//(/*.*//8/(. ((((.( (                     . .. ....././///////////////......../../.../...../...0. ... .. .. ...........  .............  /./...././././/////..//./////////../../.*./../(/.//(//*///*/=/*//8///*//*//*/==/*/@@@/==@/==@@@@@@@@@@@@@==@@@@@@@C@G@G@G@GCGCGCGCGCGCG@G@FGFGFG@GFGFGFGFGFGFFGGFGGGGGGG../.../../////==/=====//=///////////==/==========/=8==*==/8=/8/*/8=8=@@@C@CCCCCCC@C@@8@@@@C@C@@@@@@=@@C@@@@@C@C@9C@C@CCCLCLCLCLLLLLLOLLOLLMOSVVVUTVTSTVTVVSVTSTSTTSVTTTLCGC@CG@G@@@F@@@G@F@==F=@=@=@F=@F===F====2=/=/=////.//../././../..../...(....(....(....(..(.(...(.(..( ..(..(.(.(..((.*.(.(.(.(.(..(./(///=//=*//=/8/=//8///*/*//*/*/*/(//*=/*.(( (.(.((( (...                        . ....././///.././.................. .... . . .  . .  . .  ..... ....... .... ......../...(...( ..( (.( .( (.( (.(.(.(..*.*.(*.(.*.(.(*.8./(=/=(//=*./*============//=====F==@=@=@@=F@@F@@G@@G@@GF@FF@FF@FGFGFGFGGGFGHFFGGGGHGHGGH......./.././///==/====2/=////////=/===/==========/=8/==8/=/=*=/*/*==8=@@@9@@C@@@9@=@8=8@@@@@@@9@@@@@@@@@@C@@C@CC@@C@CCCLCLLCLLLLLLLLLLLMOLMMTVVVUTSTVTSVVVVSTTSTTTMVVVTLGCG@C@@@@G@@=@@@@@F@=F==F===@==F=========/=///////.//../..../../...(.....(.......(. .( ... .(.. (...( .(.( .(.(.(.(.((.((/((.(.*./*/=/=/8/=/=/=*=/=/=*/=*//*/*//*/*//*/=/*/(.(.(.*.(((.(((   ((                            . ...././././....... . . .. . . .. . . 0 .0  .  . . . . . ... .... .... .........(... ( ..  ..( ( ( ( .(.(.((.(.((.(.(.(/(.//*=.(//.(./=/=8/==/==/=//=======@=F=@F@=F@@@G@@G@@G@@GF@FFGFFG@GFGFGGFGHGFFGGHGGGGGHGF.../../././././///==2===//=/////=/=/=/====/====/==8/=/==/8=*=/8/8/=*=@=9C@CC@C@C@@@9@@@8@@CCC@C@@@@@@@@C@@@@C@C@C@C9C@CCLCLLLLLLLLMOMOMMOMLOMOMTSVVVTSTTSVVVVVSTTTTTSTVVVMOC@G@G@@@@@@F@F@@@@F@====@F=======F=2==/=/=/=/////.//..../(./...(..../(.....(... .( .(. (.. .(...(. .....(.(..(.(/.(/(.(.(.(//*//==/==/=/=*=///=/=*==///8///*/*//(/*=/*=.*.(.(/8/*.(((.(.( ( (                        .. .. .././/.////.//./....... ............ . .. . . . .  . . . . .  . . . .  ....................................(......(...( (.(.(.(..(.(..((./(.(.(.(/(.(///(/==///*=//(====/========//======F===F=@F=@F@F@@F@G@G@G@FGF@FG@GFGFGFGFGHGFGFGHGHGGHGGFG/...(../.../././///==/==/=////=//=/===/====/==/==/=8/==*=/8/=8===8@@@@CCLCLLLLLLCLCCCCCCLCLLLMLLCCCCCCC@C@C@@C@C@C@C@C@CCLCLLLLLLOLMMMMTMMOMOMLMMVVVSTSVTSVVVVVSTTSTTTSVTVTMG@C@@@@@F@@@=@=F@=F@=F====F===F======/=2=//////////.//./././/./././../././././/.//../././/././././.(../(./.(.(./*.//(/*//*//8/=================/======8=//*//*/*.*/=*=/*/(/*/8//*/*/((.(  (  (                      . ......././///=///=2/2////./../..././...1...1..1..1.1.1..1.1. ..0............ ....../..... ././//////.///.////..///././//////////=//////.//*.//(///*./*///=/=*==*=/=*//=/==8/@=@===@=/===@@@@@@@@@@@@==@@@@@@C@C@CF@CFC@GCGCGCGGCGGGGGGGGGGGGGGGGGGHGFGHGHGGGHGGGFGG/..../(../../././///==/=///=////====/====/==/=/8==/8==/=8==8===9=@=@9CCCLLLLMLLLLLLLCLLCLLMMMMLMLLLLCLCLCLCCC@@C@@CCCCCCCLLLLLMLMLMLMTMMMTMOMOMOMOMVVVVTSTTSVVVVVSTTTSTTSVVTSOC@@@@@@@@@@F@========F=======================//////.//////////////.//////////////////////////=/////*./////*////*//=/=/=========@=@@==@========@=@=@======*//*/*/=/=*/*/*/=8==8=/8/(.((((.( (.(.                 . . . . .. ...././/.//////2==F/==/2/2//2./2///.2//2///2./.2..2.2./.2..2.. 1 ....1......... ......//.//.. //////=///==////=//..=//////////=//=/=/===//=////=///*//=////8/=/======8====*=@@=/@@@8==@@===@@@@@@@@CC@C@@@C@CC@GCGCGCGCGCGCGCGLGOOOOLOOOOOOGGOGGGQGGQGGHGGHGGHGGGGGGGGG/......(.../...///////=/=/////=/=/==========///=*=/==/8=*=/8/8=8=8==9@@@CCCLCLCLLCCCCCCCCCLCLOLLLCCLCCCCCC@@C@C@C@C@CCCCCCLLLOLMOMLMMTMTMMTMMOLMOMLMTVVVSTSTTSVVVVVMTTTMTTSVTVTCCF@@@F@@=@=@==============/=/=////////=//=/=///.//..././../...../../././/././/.//././///////./.(/././(/./*.//=*/=/*=*/=*=/======================8==/8//*/*///8/8//*/==8==*==*/*/((.((( ( (  ..(                 ........././//2//2/=2/2/=2//./../.1//./2..2./..1.2....1/1.1..1. .1 1...........  ....../.... ././///=//////.//=/../////////////////=/=/=/////*/////*///*///*/=/8/=/8=/*/=/*==/8=@=@/=@=*/==@@@@@@@@@@@===C@@@@@@@G@C@G@CGCCG@GCOCGLGOGOGGGGGGGGGGHGGHGGHGHGHGHGHGGGGGF.//..././..(../././//////=///=//==/========/=8//=/8===/8=*=/8/8/8/*=**=8=9@@@9@@@9@=8=8=9@@@@C@@C@@@@C@C@CC@CCC@C@CCCCCCLCLLLMOMMMMMOMMTMTMTMMOMOLOMMTSYVVSTVTTVVVVVTMMTMTTMVTSTTC@@@@@F@=@=@=@=====F=/==2==//=///=/=//=///////////././...(....(...(....(....(....(....(....(...(..(../*./*///////8//*///=/8/=/=/=/=/=/==8========/8///*//*==/=*==8=====8=8/=*.((.((.(.(( (  .( (.(           .      .. .........///.///2....1..1....1........1....1..1. .. 1 .0 0 .. . .   .... ...  ........./....../.. .../........././././/../.(..(.(.*.(.(.(.(.(.(/.*/*/(/*.(/(=/*.*/=*.*/(/(/*=/=/8/=8===/=/=====@===@=@@F@=F@=@@F@G@GGGGGGG@G@GFGGHGGHGHGGHGHGHGGGHGGFGG../...(../...././/.////////=/////============/=/8/==*===*=*=*=*8/8*/*/*=8=8=@9@9@=8=8==8=8@@9@@@@C@C@CC@CC@CCCCCC@CCCCLCCLCLOLMMOMMMTMTMTMSMTMMMOMOMOMTSYVVSTTSTVSVVVVTMOMMTMTTSTTTC@F@=@@F==========/====//=///////////////////////////./...(.. ( .(......(.. ( ....(....././../(./(/.//(//*//*///*////*=/=/=/==/=========8===/8=//*//*====8===8====8===8/8(/(((/*/(((.((..(.(..                 . . . .......//1//./.1... .. ........1...1 1..0..0.1 . 0  0. . .  . . .. . . ........./....... ................./../..(/.(. (.(.(./(.(. (.(.(.(.*.(.(.(.(/*.(/*//(//(.(./*/*=/8//=8=//========@=F@@@=@F=@F@F@@G@GGFGGGGGFGG@GGGGGGGGHGGHGHGGFGGGGGGG../.(/...../(....//././////*//=//=/==========/==/8===8=/8=*=*=*=*=8=*8/*=8=@9@@9@@9@=8=8@=@@@@@C@@@@@CCCCCC@CCCCCCCCCLCLLCLLLLLMMMMMTMMTMTMTMTMTMOMMMTMOSVYVVMTTTVSVVVVVMOLMOMTTSTTTMC@@@=@========/==2=/=//////////////////////////////////......(....(........( .(.....(....(....(../(/.*./*./*////*/=/=/==/========8======*==/=*=/8===========8==@=8=8/*/(((/**/**.((.((  (.(.(.(..            .. .  . ........././/1//1/2./..1....1...1..1...1.....1..1.1.1.1. 0 ... ..   . ... ..  ...././..//.....//. ../........./././//.///./.(.(.*..*.*..(.(.(.(/*/*//*/*/.*//*.(=/*/(/*/*.*=/=*=//8====*/=======@=@=@@@F@@@F@@@FG@GFGGGGG@GGFGGFGGHGGHGGHGGHGGGHGHGGFGG.*./.../(./.././.././//./////////*=/=======/==*===/8==/=8/8=*=*8=8=8==@9@@CCCCLCCCCCCC@CCC@LCLCCLCLLCCCLCCCCCCCCCCLCLCLLLLOLOLLMOMMTMTMMTMTMTMMMMMMOMTMTMTMVYVTMTTTSVTVVVSTLOLLTMTTSTTO@@@=F@=======2===/=/=//////././/////////.//.////////=///////./././/./././././.././///.////.//.//./*///*////=*=/=/===/========8======8===8===8==@@@@@=@=@@==9=8==***((.**/*/****.*(.(.(.(..(...           ( . . . . . . . ......././1///2/2///2=2/2/2/.2/1./.1/.2///2/.2/2/2/1/.2./1..1.0 0 .1.......... .././1/./.. ..//////////=/.//=/../=///////////////===/=/=////=//=/======/=*=//8=/===/==*=====*==@====@=/*==@@@=@=@@@@@==@@@@@@C@C@GCGCCGCGCGCGGGGOGOGGGOGGGGGGGGGGGGGHGGHGGHGHGGGHGGFG..(.(......./.././././.*/././*//=//=======/====*=====8=/8=*==8==8==9@@9@@@C:LLCLLLLCLCLCCCCCLLLLLLLLLMLLLLLLLLCCLLLLCLLLLOLLLOLLMOMMTMTMMTMSMTMTTMTMMTTMTTMTMVVVTMTTSVVSVVVTMGLOLTMTTTSTT@@@==========/====/=/////////////////////////////=//=//=///=/=/=====/=///=//////=/=/=//=///=/===/========/=======@=@=@=@@=@=@@=@=@======@@@@C@@@@@@@@@=@==8=**/(/**==*=*8/*/**.(  (.(.(.(.( ( ( .         .. . .. . . .. . ... ...... ......//////2////2///=2=/2/=2//2/2//2.2/2/.2/2/2/2/2//2/2//2//2/.1. ..0 .1/2/.2./1. . .///2/2/2. .////=/F/==2=/.2===./=/===/==////=/=/=========/=/===============8===========8==@===@@@==@@8===@@@@@@@@@@@C@@@@CCCCGCGCLGCOLCOLCOGLOOOOOOOOOOOOOGOGGGOGQGGQGQGQGGQGGQGGGGGG..(/..*./.././.././././.(/.////*//=/=/==/==8/===8==/==8=/8/=*=*=8=8=8==8=9@@C:CCCCL:CCCC@@C@C@CCCCCLLCCLLCLCCCLCLLLLLOLLOLLMOMLMOMOMMTMTSMTMMTMMTMTTSMTTMTMTTMTVVSTOTSVTSTVSVTLGLOLTMTTMTTMG@=====/=====//=//=/////./////////////...././/../..//.////////////=/=////////=/=/=//=//=/////=/=//*////*/=/=============8====8====8@@@C@C@@@@@@@@@@@@=8=*/*(/(*=*=8/8/*/**/*((.(((.((.(.(...(.(     (       . ( . . . . . . . . .. . . ...././/./.2/./2/2/2/=2//2/1/.2.2..1.2.2/.2.2/2/2//2.2/2/1/1/.0 1 ...1/......  ..2.//./.. //2/2///2/2=2/./2=2.//=2/=///=///////=/=/=/=//=/////*========8====/8/===8===/8/=/*===@==@@/8/@@@@@@@@=@@@@==@@@@@@C@CCGCCCGLCGCOCGGGOOGOOGGGGGGGGGGGGHGGHGGHGHGGHGGHGHGGGG..(../..(/..//./././..(/./(/(///*///=/====/=8=/=*==*=/8/8/8/8*=*=*=*/*8/**88=8=9=9@@9@=8@8=9=@@@@9C@CCCCCCCLCLCLLOLOLLMOLLOLMLOLMLTMMTMSTMTMTMTMTSTMTTMTSTTMTMOMTVVTMOTSVTSTTVTTLGCOMMTTMTMTMG@====/=2====/=////////./././/////././.........(..(......(//.///././..././/./////////////=////=/=/=*=/=/=/============*==/===8=@CCCC@C@@@@@@@@@@@@8=/*/(/**/8=8=*=*/8*/**.((.*.*(.((.( (..( .(     (     . . (. . . . . . . . .  ............../..2...1/1..1...1......1......1/1..1.1.1.1.1  0...0.....    ....1... ....../.././. .//. ..//././/...././././/////(..(/*./*///*/*//*/*/*/(/*/*.*.*.*=(.8//8/*=//(/8/8=/=*=8/8/=*/==========@=@@@@G@@@G@GFG@GFGG@GGFGGGHGHGGHGGHGGGHGHGGHGGGGGFG..(..(..../..//././/./..*./(//(///*=//=/==8/==8==*=/8=/8/8/8/8*=*=*=8*/8*/**/8=8=8==8=@=8@==9=@@@@@C@CCCCCCLCLLLLLLMLOMLMOMLOMMOMMOMTMMTMSMTMMMTMTSTSTSTTMTTMTMOMTTVVTMMTVTSTTSTTMOLGLOTTMTOMMOC==/===//==2=///////.///.//.//.//./../......... ( .. ......../././..././///////////.////=/////=///=/=/===========/====8==@CCCCCCC@@@==9=@@@@===**/*(*/8=8==8=8*/8/***.((/**/*/(.((..(..( .( (   .  (    ... . ( . . . .. . . . . . . . ..../....1..1.....1....1..1. .1 1....1.1./.1.2..1.1...1 0 0 .0.. . .   . 1.....  ...../....... .... .../../../.......././../.(..(..*./(//*/*/*/.*.*./(/(.*.(.(//(.*/*//*//(.(/=/8/8/*/*=/=*/===========@=@@@@FG@FFG@GFGGFGFGGGGHGGGHGGGHGGGHGGHGGHGGHGHGGG../(../././././/././/./(/./(/.*//*///=*=====*==/8=/8/=8/=*/8/8/8/8=*=*8*=*8/88==9@@@9@@9=@9@=@@@@CC@CCCCCCLCCLLLLOLMOMLMOMLMMOMMMMTMMMTMTMTMTMTTMTSTSTTMTTMTTMOTMMOTVSVOMTVTVTMTTTSTOLCLOMTMOOMOOL@/=/=/=2=/==/////.//.///.//../.../.../......... (. .. ..........(../././//////.///.///=/=/=///=/=/===============/=@@@CCLCCCC@@@=8==8===@8=*/=**/*===9==8=8/*=*/*/((*/*/*/*/((.(.(.(...(.   ..  .   . .. .... . . .. ... . . . .. . . . ..../1......./.2/1//1/1/..1..1....1.1./1..1./1/1./1.1.1..0 1...1 .. .   .......1. .../../..//.. ./. ././//.///.//././/.///////(./*./(//*///*//*=/*/*/*/*//(/*/*=(.*//*//=*/*/=*===*=8=/8/=/8========@===@@=@G@G@G@GFGFG@GFGGFGFGGFGGGHGGHGGHGHGGHGGHGGGGGF..../.(..././..//./../../(./(//(///*=///=*===/8==*=/8/=*//8/8/8=8=8=8=8=8=9@8@@@CCCLCCCCC@@@C@CCCCLLCLLCLLLLLCLLLMLOMLMMMOMMOMMMOTMMMTMMTMTSTMTSTMTSTMTTMTMTMTTMOTMMOMVVTTMTTSTTMTMTTMOLGLOMTLOLOLOLF=/=/=/=/=/=////./..//.////././../.././................/....././..///.///////////////=///=///=/===============@@CCCLCLCCC@@=8=*==*==8===*=*//*=@=9@@8==8=*/***((/**/**/*/*.(( (( (.(.(..(  (  ..   ( ... .... ..... ... ... . . . .. .. .. .. ......../2///2//1/2///2/2/2//2/2.2..2.2./2/2./2/2/2//2/2//1//1. .1. 0 /1........ .1//1////. . .//2/2=/2/2=//..2=/..///=/==/==////////==/==/==///=/======@@=@@===========8=/8==@*==@=@/@==/=8=@@@=@@@@@@@@@@@@@@@@C@@CCCCGCGCOGOGOGOOOGGGGGGGGGGGGGGGGGGGGHGHGGHQGHGHGGHGG/../.../../.././././/./(./.*.//(//*///=*/==*===/=8/8/=*=*=**=8=8=@8@==9=9@@9@CCCCLLLLLCCCLCCCCCLLLLLMLMLMMLMMLOMLMMMLMOMOMMTMMOTMMTMTMTMTMTMTSSTSTMTMTSMTTMTMOTTMMOOMOMOSVTTMTVTTTMTMTTTMGLGLOMOOLOLOCO@F=/=2=/=/=/=///////./////////.///.///////.//././///./////././/.//.//.//.//./////=/===/=========@@=@=@@CCLCLLOLOLLC@@=@=8===*==8==*==*==8@@@@@@@@9==8=**/*.((*/**/8*/(((.((..(.(..(.(.(.  ( .... (. ( .. ....... ..... .......... . ..................../////2//2//2/2/2=/=2=2=2//2/2/2./22/2/2/2/2=2/2/2/2/22//1.1.1..1/2/2.2/1/ . /1//2///.... .///==2//==2=...==/../==2=2=======/=================/===@@@@@@@@@@@=====8=====8@@=@9@@@=@@===@@@@@C@@@@@@C@@CCCCCCCCCCGCGCLGLOOOOOOOOOOOOOOOOGOOOOGQGQGQGQQOQGQQGQGQGQGQGGG./.../../../././././././.*./(/./*///*=//=*==/8==/=*==*/8/=/8/8=8=8=*=8=8=8=8@@@9@CCCCCCCCCC@@CCCCCLLLLLLLLLLOMLLOMOMOMMOMMMMOMTMSTMTMTMTMTMTSTTSTSTTMTMTTMTTTMMOTTMOMOMOOMTVTTMTVTTTMTMTMOMOGLOLOOLOCOCGCCF=//////////////./../.../................../././././././././..././../../.././/////=/=/=/======@@@@CCGCGCCCCC@======8==8/8=*=/8==8===@@@@@@@9@@@@8=8=*(.*(/**/**/(((.((.(.(.(.(.(.(..( ( (   .... . . (.. ....... .. .. ... .. .. . . .. ...... .. . ....2///1//2.///2//2//2=2/2//1/1/1/./2/2/2/2/2/2//2//2./2.1. 1 .2.1...... //.2///.. ..../2/2/=/.2///. ./2/ //////=/==2////////=======/=/=*=/=8====@=@=@=====8===8=8=8=@@*=@=@@=@==*==@@@@@9@@@@@@@=C@@@@@@@@@@@CCGCGCOGGOGOGOGGGGGGGGGGGGGGGGGGGGGGHGGGHGGHGHGGHGG.../.(/.../.../...//././/././*./(////*/=/=*==/=8=/=*/8//8/8/8=/8/8==8/8=8*=8*=8=8=9=9@8@=9@@C@@CCCCCCCCCCCLCLLLLLLMMMMMTMOMTMMMTSMTSTMTSTSTTSTTSTTSTTMTMTTMTTTTMMOMTOMOMOMOMTTSTTTVTTTOMOTTLMOGLOLOLGCOCGCCG@@=//////////////./...........................................././///.//////=/==/=/=/====@@C@CCGCCCCC@@@====@===8===8=/8==8====@@C@@@@@@@@@@@@@8=*/*(*.**.**((.(((.((((.(.(.(./(.(.(. ( ( ( .(. (. (. (. ............. ... ...... ... . .. . .. . . .. .........1......2/./1/1..1..1.1..1.2.2.2/..1.1.1.1.1..0. 0 .0...1...   .1...... ..././....//. .... .//.././///../.././//////*/(//(/*//*/=*=*=/*=*/*/**.*.*/(*=*.*=*=*/=*.*.8=*=8/=8/8=8==8====@==@==@=@=@@F@F@G@G@GF@G@GFGG@GFGGGGHGHGHGHGGHGHGHGHGGHGHGH...(..../.../../..././/.//*.//*./*//=/=*//=*==8/==*=/*=*=/8/=8=8=/8=8==*=8==8/8/8=8==8=9=@@C@@CC@CCCCCCLCLCLCLLMOMLMMOMMMTMMTMTSTSTSTSTMTSTMTMTSTSTSTMTMTMTMTTMTOMOLTOMOMOLOLTTVTSTTTTTTMOMOTLOOLGLGLGLGCGCCGC@F=///././//.//////./............... .. ... ...//.////////=//=//=/=//=/=/=/=/========@@@CGCGCC@@F==============8===8==8==@9@CC@@@C@C@@C@@@@@@8=**/**(/(*(.(**.*((.(.((.(.*.*..(.(..(. ( .. (..(. .... .............. ...... ... ... ... ... .. . . . .. . .. .1. ....1..2.2./1/..1..1.1..1...2.1/1.1.1...1.1 . 0 0..1 .. .    .. ....  .. /......... ... .........../..(...///////.*./(/.*/*/*//*///*//*/(.*.*(.*.*/*(=*//*///(.(/*=/8/*=*=/8/*/=8===========F@F@F@FF@GFGF@GFG@GFGFGGFGGGGFGGGGHGGHGHGHGGHGHGFG../../(.../.././././../././*//.*////*=/=*=/=*=/=8/=*=/*=/8=*==8/8=8=8=*=8=8=8==8=8=@9=@@9@@@@C@CCCCCCLCCLCLLCLLMLMOMMMMTMMOMTMTSTSSTSTSTTSTTSTTSTTSTTSTMTMTMTTTMTMOMOMOLOLOMOOLOTTSTTTMTTTOMOMOMOOLGCCGLCGCGCGCCG@==//...///.//////./......... ........ ..... ......///.//./////////////////=/====@=@@@@=============@=======8====8=@@@CCC@C@C@@@@C@@@C@@9=8/8/*/**(.((.(/(*.((.(((.(.*./(.*.(.(...( ( .(..(..(. ....( ............... .......... ... .. .. ... . . . ...........1././/1//2./2.1.1..1.1./1/1/.2./1/..1.1..1 1 0.1... 1..   ........ . ...././1.../.. ... ../././/././...../////////*///*.*///*//*=*=*=*/*/**/*.**/(==*//*/8/=*=.**==*==8==8===8========@=@=@=@=@@F@F@F@F@FGF@G@GFG@GFGGGGGGFGGFGGHGHGHFGHGHGGFGF...(.../..../../././././.*/.////*/=/=/=/=*==/==/=*=/8=/*=/8==8/8==8=8==8=8@=@9@CC@CCCCCCCCC@CCCCCLLLLLMLMMMMLMMOMLMLMMMMOMTMMTSTMTSVTSTSTTSTSTSTTSTMTTSTMTTMTTMTMOTMOLOLOMOLOMOLOLTTTTTMTTMTOMOOOLOLOCCGCCGC@G@G@G@G@=///..././//.///////......................../..../../././/.//////////////=/=========================@===@=8@=@@@CCC@CCCC@C@C@@C@C@9==8/8=8*/*.((.(*((/(*.(((.((.(.*/*./*.(.(.(..(...(............././.......................... ................/../././2/2.//2/2/=2/=2/2/2/.2..2.2/2//2=2/2/2/2/2/2/.1.1.0..2.2.1/.2. 2/1/2/2. .......2=2//./2//. .//.. //=////=/////=////=/========/========@=@=@=@=@=@=8===8/==8=@=8@=@=@@=@//8@=@@@@@@@@@@@@@@CCCCCCCC@CCGCGCGGGOGGGOGGOGGGGOGOGOGGGGGGGGGGGGGGGHGHGHGGHGGGG.(..(/../..(..../.....*././/(////=/*==/=/=*==*==/==*=/8=8=/8==8=8==8==9==@9@@CCCLCCLCCLLCLCLCCLLLLLMMTMTMTMTSTMTMMMMMOMTMMMTMMTSTSTSTSTTSTTSTTSTSTTTSTTSTTMTTTMTMTOMMOMOLOLOLOLOLOLGMTTTMTOTMTOMOOLOCOCCGCCGCGCCGCG@G@G===/////.//////////////////////./../..//./././..../././/.////////////=/==/=================@@@=@@@@@@@@@@@C@CCCCCCCC@CC@C@C@C@@=8=8=8==8/*((((*.(/(*(/(/(.(.(/(/(/(/*./(/(.(..(.(...(..(.......(..././././........./././............../../..../././/2/.2/////2/2/=2=F/F/F/2=2/2/1/2/2=2=2=2=2=2/2//2/2./2.1.1./2/2.2/2. . ...../2//2//.....././=2=//./=2/...//...=/=/==/===///=/=/=====@=@=========@@@@@@@@@@@@@@@@====8===C@8@@@@@@@@@8==@@C@@C@C@@CCC@CCCLCCLCCCLCCGCGLGLGOOOOOOOOOOOOOOOOOQOOOOOOGGOGQGQGGGGGGQGQGQGG..(/../.././../..././(..(..////*///=/8/=*==/=/8/=/8=*=/8/8==8==8=8==8=8=8==9@@:C@CCCCCCCCCCCCC@CCLLLOLMMMMMMMMMMOMOMMMMTMTMTMTSTTSSTVTSTTSTTSTTSTTSTSTMTTMTTMTTTMTMOTOLOLOLOLOOLOLOLGLOOMTTMOTMOMOOOLGCGCFC@G@G@@@@G@F@F=F=/////././/.//.///.////////././................. ......././//.//./////=/=============@===@@@CC@@C@@@@CCCCCCCCCCCCC@CC@CC@=8=8=8==8=8**/*/(.((*(/(**(/(*/(*.*./*/(/*/.*.(.(..(.(...(..(...(.. .../././/.............../............................//.//1/2.///2/2/2=2=/=2//2.2/.2./2/2/2=2/2/2/2.2/1.1... .1/.2./1/.1 . .2///2//2. /...../2//2/.2=/.. /// ..//=////////////////===================8@@@@8@@@@8===8==8==@9=@@@@=@@@=/8=@9@@@@@@@@@@@9@CCCCCCCCCCGCCCGCGCGGCGGCGGGOGGGOGGOGOGGGGGGGGGGGGHGGHGHGHGGHGGH..(...(/......(/../...(/.(/./*.//=/*//=/=*=/8=/8=/==*===8/8==8==8=8=8=8==8=8@==8=9=9@@8@=@@@@C@CCCCLCLLLOLLOMOMLMMMLMTMMTMMTSTMTSTSSTVTSTSTSTMTTSTSTTTTMTTMTTTSTTMTOMOMOLOLOLOLGLOLOCGLCOOOTMTOTOOOLOGCCG@G@@@@G@G@@F@@F@=F===////.../......//////////././../../.../............////.///.//./////==/==========@@@@CCCC@C@@@@@CCCCCCCCCCCC@CCC@C@@8===8=8=8==8=***/**(*.*(*.*/*/(/*.*.*.*.*//*/*/(/..(.(..(.(..(..(....(..../././/./.......././././............/../.... ... .... ......1..2/2.2/1/2/2....1..1..1/2/1/2.2..1/.1...0 1 1.1....1. ........ . . /........ ... ......././....(..././(/////*///*//*/*/*/8/*=8/8/8/**/*/(/(/8=*==*=8/8//*.*==*=8=8==8=8======@=@@==@@@=@@@F@@F@F@F@F@F@G@FG@GFGGFGFGFGFFGFGFGHGFGHGHGGHGF..(./.(/././../../../.(...(/.///*//=/=*/=/8/=/=/8=/==*===8===8==8==8=8=8=9=@8@@8@=9=@=8==9=@@@CCCCCLCLLLLMLMOMMMMMOMMMMTMMTMTTSSTSTSTTSTSTTTSTTSTTTTSTTTSTTSTTTMTTMTLOLOLOLOLOLOOLGLOCGLGCOCOOTOMOOOLGGCCG@@G@G@@G@@@F@@F@=F=====///./../.....././///////.//./..//../.........//.////////.//.////=========@@@C@CCG@@@@@@@@@C@CCCCCCCCCCCCCCC@C==8==8==8=8=8=8/8/***/*(/*.**.**/*/**/*/*/*.*/*./*/*/(/.*.(..(..(...(....(../././//../...../..//./././......../.......... .. .. ......1...1../1//2./1.1.1..1.1.1...2./.2.1./1..0.1 0 .1..0....  .1...1.... ..... .... ..  .../.......(......(..(/./*./*//*/.*.*/*/*/*/*/*//**/(*.(*.8/*/=*/*=*/*(/(/8/=*=8/=*=/8==8=====@=@=@=@@F@=F@F@F@F@F@F@FG@GFGG@GFG@GFGFGFGFGFGFGFFGFHGFGF..(...(./././../../(...*..*./.*////*//=/*=/=8/==/8=====8==8===8==8=8==8@8==9=@=9@@@@@@@@@=9@@@@CCCCLCLLLLMOMMLMMTMMMMOMMTSTSTSTSTSVTSTTSTSTSTSTTMTTTTSTTTTTTSTTTMTMOTLMOOLOLOCOLOCOCLGLGCGCGCOLOOOLOOLGCG@G@@G@@G@F@F@F@F===F=F=F==////...././../.././.../..../././../......././/./////.//.////=/====@=C@@C@CG@@@@@@==@@@CCCCCCCCCCCCCCCCC@@=@==8==8=*==8=*=**/**/**/*(**.***/*/*/*/*/*//*/*//*/*/./*./(/(./(/./(..(....././././///../.......//.//............././....... ... ........../1/2.2/.2//2.2..1.....1/2/1/2./.1/1.1...1 ..1.1..1.1 .//1/... .1 ..... .... .. .//.././../.....(../*.//*./*//////8/*/*/*=8/8==8*=***/**/(/8=/8/==8=*=*/*=8===8===8===8====@=@@@@@@@@@@@F@F@F@@F@F@F@F@F@GFG@GFGFGFG@GFGFGFGFGFFGHFGFGFG..(./(..(/././/./.(..*..(..(/./.*///=*/=/*/=/==*=/=8=====8==8/=8/===8==8=@9=@@CCCCLLLLLCCCC@@C@CCCLLLOLMMMMMMTMMMMTMMTMTMTSTSTSTSTTSTSTTSTTSTTSTTTSTTTSTSTTTTSTTMTMTMOMOLOLOLOLGLOLGLGCOGCOCGCGCGLGLGCGCGC@G@@G@F@G@@F@F=F@F===F=@F@F=////./././///././/..././././//..../......//./////////.////==F@FC@G@@@@@=@=====@@@CCCCCCCCCCCCCCCCC@@8=9=8==8=8==*=**=**=*8*8/**/*.***/**/*/*/*/*/*/*/*/*//*/*//(//.*.*./(/./.*../..././///.///././../..//1///............//1//./.../../././2//2/2/2/2/=/2=2/2///2//1/1/2/2/2=2/2/2/2/2/1/1.1.1.2//1/.2/ . .. .2/2/=2=/ .//.. ..///2..///.. ///.../=/=///=////////.////=//=/=====@=@====@=@=@@@=@@@@==8==8=8=@@@@@@@@@@@=*=@@@@@@@C@@@@@@@@C@CCCCCCCCCCLCCCOGGCGGGGCGGGGGGGGGGGGGGGGGGGGGGGFGGFGFGFGFGGFGG..(...(..(././././//..(/.(./.*.//(////=*=/8/=/=/8=====8/==8==8==8==@=@9@=@9@@CCCLLMLLLLMLLLCCCCCCLLLMLMMTTSSTSSTSTSTSTMMTMSVTSVTSTSTTTSTTMTTSTSTSTTTSTTTVTSTTTMTTMOTMOMOMOOLOLGLGLGLGOCOCGCGGCGCGGCG@@@F@F@@G@G@CG@G@@@@@F@@F@@@F@F@@F@=F=====/////////.//././/.////.///.././/.///////////////==F@=@@F==========@@@CCCCCCCCCCCCCCCCCC@9=@8@@=9@=@8==8=8*=**/8**=***/****/**/*=*8/8//*/**/*/*//*////*///*.//(//.//*././../././////////./../.././/.2///........//1////////2.//2/2///2//2/2/2=2=2=2===F/2/2/2/2/.2/2=2=2=2=2/2=.2..1.1.2/2/2/2/2 ..1 .../2=2=/2/.1/......2///../=/...///...=/=========//////=/==/=======@=@@@@@@@@@@@@@@C@@9C@@@9=@8=C@C@CC@C@C@@==@CC@CCCCCCCCCCCCCCCCCLCCLOCLOLOLOLOOOOOOOOGOGOOGOGOGOOGOOGOGOGOGOQGGOGGGGGGGGGGG..*..(...(./././/.*.//..(/.(../(//*/////8/=/==*====*=====8=/=8===8=8===9==9@@9@CLLLLLLLLLCCCC@C@CCCCLLLOLMMMMTMTMTMTMMTMTSTSTVMTVTSTSTTSTSTSTTTVTVTSTTSTTTTSTTTMTMTMOTMOOLOLOLOLGOCOCOCOCGOCGCGGCGCGCF@F@=F==F=F@@@@F@FF==F==F=F=F=F==F==2====/==////./.........../...........././/.////////=///=/==/=========@@CGLCCLCCCCCCCCCC@C9@=@8@@=9=@8@@@@9@=8/**/*8*=8/*=***/*/*/*/8*=8=/8=*=*//*/(/*//*//=/*//*.//(//*.///../././//////////./.../.././///././..../.../././.2./.../././2/2///////2/2=2/=2=2/=2/2/2/2./2/2/2/2/2/2/1./2..1..2/2//2/2.. . .1 ./2=/2=2../2 . . //2// .//. . /...../=/=/=/=////////////=///*=/=/===@=@===@8=@=@9=@@@9=9=@8=8=@@@@@@@@@@@@8==@@C@C@C@@@@C@@@C@@CC@CCCCCCCCCGLGLGOGGOGCGCGGCGGGGGGGGGGGGGGGGGGGGGGFGFGF@FFGFG..(...(..(..*././/.*/.//./(././.*./*//=//8==/===8=====*===/8==8===8==@8@=@8@=@9@@@9CCC@@@C@@@@@C@CCCCLLLLLMMOMMTMMTMTSMTSTSTSTVSTTTSTTSTTSTTSVTSVTVTVTTMTTTTMTTMTMOTMOMOMOOLOLOOLGLGCGGCGLGCGCGCGGCG@G@@F=F=====F=F=@F@@F==F=F==F===F==2==2=2==2==F===/=/////././/././.../../.././///////////////=/==2====F@@CCGCLGLGCCCCC@C@@@=9=@@@9@@9=@9@@@C@C@8=8/*=*=8*/*8**=*8/8***/8/8/8=8/=8/*/*/*//*/=*=////*//*//.*/////*/././/.////////////./.././/.///2//.../..././.1/............././.1..1..././.2/2./2./1....1 1.1/1/2/1/./1.1.1. 1.1//1/1/. 0 /..2./. .. /.... .. ..  ../././////....(.(./(.(.*.(./(/*//8/=*/8/*=*/8/**/*=**/**/*=8==8=8==8==(/8=8==8==8@==8====8===@===@=@@@@@@F@G@FC@F@F@F@F@F@F@F@F@GFGFGHGHGFGHGFGFFFF@F@F@..(..(...(...(//.*/.//.*/..(./(/.*/.*//=/=/==/===8/=====8/=8==8==8===8@=9@=@@@9@@@@9@@@C@@@@@C@@@C@CLCLLLOMLMMTMTSMTMTSTSTSVTSTTVSTTSTSTSTSTTSTVTVTSVTSTTMTMTTMTMTMTMOTOMOMOOLOLOOCOCGCGCGCGGCGGCGGC@G@G@@@F===2===2===2=F==F=2==F=2=F==F==/==2==2==2==/==/===/==/==/////////.////.//////.////=/=/===@F@@G@CCGCCCGCCCC@@@@@==9=@@9=@@@9=@8@@9C9C@9@=8=8*8*=**8/8/8*=***/*8/*=*=8==8=/8/*/*/*//*=/=*==////*//*///*///////.///////=//////././././/.////2//.../../.././/.........1...1./..../.1.2./1/2.2.1./1.1 ..1...1../1.1.. 1.0 1.1.1./1.0 0 1.2/.... ...  ../. ... . ......././../.(...(...(.(..(.(.(/*///*//*/*/*/*/**/**/*(*((/8/8/=*=*=/*/(==8===8==8===8=8===8====8===@=@@@F@FG@FGF@FG=FGF@FF@FGFGFGFGGFGGGHGF@FF@FF@FF@FF..(...(./(..(..//./*.////.//.(.*..*////*=/====/===8=/=8====/8/=8==8==@8@@8@@9@@@@9@@CCCCC@C@@C@C@CCCCLCLLMLMMMMTSTSSTSTSTSTTSTVSTVSTTSTTSTTVTVTSVTVVTSTTTMTMTMTMTMOTMTLOMOOLOLOLOLOGLGGCGCG@GG@GGCG@G@F@@F@F@F===2==2=2=//2/===F=2==F=2=2=F/===/===2==/=2==2=/==2==2===/==///=////=/////=/=/==F@@F@G@G@G@@@F@=@=@@@@@@@@===@=@8@@@9@@8@9@C:C@CC9@@9@88=8/**/***=*8=*8=*8/**8/8=8=8=8=8=/8//*//=*===/=*=///*//////////////./////=//=//////./././//.///////././../..././..2...1../.2./.2..2.././.2//2//2/1..1. 1./1.2.2.2.2/1.1...0...2/2.2/1. ././1/. ... ..... ... .././/./////..(..(././(.(.(.(/.*//8/=*==*=/*/8/8/**/**/*/**==8=8=8==8=**/8======8===8====8===@===@=@@@@@@G@G@@G@FCF@FG@FF@FF@FFGFGFGGGGFGGFGFGFFF@FF@F=F@. (..(...(...(..*/.//.*/.*////(//.*///=/==*=/===/8/==/=8/=8==8==8===8=@=@9@@@@@@CCLLLMLMLLLLCCC@C@CCCLLCLLOLMMTMSSTSTSTSVTSTVTSTVTSVTVSTTSVTSTVTTVTSVTTTTTMTMOTMOTMTOMOTOMOOLOLOOLOOLGCGGCGG@G@G@GCG@G@G@F@F@=F=F==2==/2=///2/2/=/=2==F==2=2=2=/F/==/=2==/===2=/====/==/=/==/=/===2==F===F=@@F@@F@@=F==========@=@=@@@=@=@=@@=@@9=@@CCCC@C9C@9@@9@@9@8=8**=*8/88=88=*8=*8/8*=8/=8=8==8==*//8/=/=8===/=//8////*/=/=//////////////=//////////.//.///////////.2./././2/.2//./1/.//2//2/2/=2/2=2/2=/2=/2=/2=2//1..2/2//2/2/2//.1/2.1.1.2/2=2=2=2.1.. .1.2/2=/2/..//.. ./2//2..//. . .. . //////=/==/=///=//=/=///////=/*=/=====@@@@@=@@@=@8@=8==8=8=@9@@@@@C@@@@8==@@CC@@C@CCCCCCCCCCCCCCCCCCCCCCCOCGCOGGOOOGOGGGOGOGGGOGGGGGOGGOGGGGGGGG@GF@F@F@F@. (. .(..(..(./.(/.//.*//.*/.*//*/./*///=/=8==/==/8=/===8==8==8==8==@8@@@9@@@@CCCLLLMTMTMMMLLLLCCCCCCLLLLMMMMTSVVVUVVVSVTSTVTSTVTSVTVTTVSTTVSVTVSTVTTSTTTTTMTMTOMTOMOMOMOOMOLOLOLOLOOLOGCGG@GCGCF@G@GG@G@G@F@F@F@F=F=F=/2=2=/2/2/2/=2/=2==F=F====F===F===F=======F===F==2==2==2==2====F=====2==/==/=====F====@====@=@@=@@=@=@@8@@CCCCCCC9@@9@@8@8@9=8=8=8/8*8=*=8=8/8=8=*8/8=8=8=8=8==8==8//8/==/=8==/=///*////*/=/==/////////=//=/=////////.///./////////////.2////2/2/2//2/2/2=2=2==2==2=2=/F/=2==2F=/2/1.1/2//22/2/2=22//2/1..1=2=F/F=2=1.1.1.../2==2=2/.1//... ./=/=/.///. .......///=/=========/==========/=/8/====@=9@@@C@@@@@@@@@@@9@=@9@=C@@C@CCCCCCC=8@CCCCCCCCCCCCCLCCLCCLCCLCCLCLLOLLOLOOOOOOMOOOOOOOOOOOOOOOOOOOOOOOOOGGOGGG@GGGCGG@. (...(..*..(...(/./.*/.*///*.//*///=*/=/=/=8=/=/=8/=*==8==8==8===9=@=@@@9@@@@@CCCLLLMLMOLLLCCCCCCCCCLGLLOLMOMMSTVSVSVTSTVTSTVTSTVTVVSVVSVTVTSVTVTSTTVTTMTTMOTMOMOMOMOOMOOLOLOLGOLGLOGOGCGCG@G@GG@G@@G@G@F@F@==F===2=2=/2=2/////////////.//./2//=/=2=/=2==2=2=2=/=2=/=2==/=/2///////////2/=/===/==2============@==@=@@=@=@=@@CCCCCCCC@@9@8@8=9=9=9=9=8=88=8/8=8=8=8=88/88*8/8==8=8=8===8====8/=====8=/=////*/=/=/=/=/=/////////=/=/=//=//////////////=2////./././././//.2/./.2///2/2=/2=/2/2=2/2=2/=/2=/2...1/1//2//2/2=.1/.1.1.1/=22=2=2/.1... 1.2/2=2/=1.//1. . ./2/2../// .... ../////////=///////=/==/=///8//*/*/===8==@@@@8@@@@8==8=8=8=8=C9@@@@9@@@@9==9@@@@@@@CCCCCCCCCCCCCCCCCCCCCCCGCCGCGLGOOGOGLGOCGOGGOGGGGGOGOGGGGGGGGG@GF@G@@G@@F..(...(..(/.(/..(.(/.*/.*/./*//*////=*=/=*=/=8/==/8/==*====8===8@==@@8@@@@@@@@@@@@@CCCCCCCCCCCCCCCCLCLCLLMOMTMTSSVSVTVTSTSTVTSVTVTVVVSVTVSVTVTSTTTSTTTTTTMTTMOTOMOMOTOLOMOOLOLOCOOCOOCOGGCGG@G@GGCG@FG@F@@@FF=F=F==2=2=/2/2/2/2//.2//1///.2///././///////=/=/=/////2/////.//.//././///=//=/=/==2====F=F=@=@=@========@=@@CCCCCCCCC@9@8=88=8=88@8@8@9=8=8=8=88=8=8=8=88=8=8=8==9=@@8=====8======8=====/=*/=///*=/====/=//////=////=/=//////////.////////////2/./1//1/2.//.2.../1/.//1//1//././2/./2/2/1.. .1..1..1.1//1.1.1... .2/2/2/2/1.. .2/1/2..... /./.. ..   ....//.//.//..*..(//.*/(/.((.(.(/(*/*/*/*=*/*=*/8/**/**/***==8=8=8=8=8=(*=8==8=8==8==8=@==9=@=@8@=@@@@=@@@@@@FCF@G@G@F@FG@GFG@G@G@GFGFGFGF@FG@FF@F@F@F=F@= ..(.(...(..(..*..(./(//*///(///8///=/=*==/==/8=/==/=8===8====8=@=@@@@=@@@@@9@@@@C@C@CCCCCCCCCCCLCOCLLOLOMMMTMTSTVSVVSVTVSTVTVSVVTVSVVVSVTVSTVTVTSVTTSTTTTMTTMOMOTOMOMOOLOLOGLOGLGOCGOGLGGCG@G@GF@GGC@F@FF@F=F=F===2=2=/2=///.2/.///.//2//././././././//.//./././/.//.2//.//.///./////=//=/=2==/=========@=========@@CCCCCCCCC@@9=8=89=889=9@8@9@9=9@=8=88=8=88=88=8=8/8/8=8@=9=@=@9==============8====//=*=//==/===/==//=////=//=/===//////////=//=/2=//2//.//.//2//1//2.2./.2/1//.2./1/1/./.2././1/1. 0 .1 1.1.1.1.1..1..0.0.2./1/2.1. .0. 0.1/2.//. ...  ..... ...   ....../../.....(..(/...(.(.(.(.(.(/(/(/*/*/*/*/*/**.*(**.*/8*=*/8/8/8=**/*=8/=/8==8===8===8@=@=@===@=@==@=@@F@@F@F@FG@FG@FG@GFGFGFG@GFG@FGFGF@FG@F@=F@==@=....(.(/././(.(.(.(.*.//*./*////*=/=*==/=8/=/=8=/=8/==8===@8=@=@8@=@@9@@@@@@@@@@@@CC@CCCCCCCLCLCLLCLLLLLMOMTMTSTSVTVVVSVVTSVTVTVSVVVVTVVSVTVTSTTVTTSTTTTTTMOTOTLOMOOOLOLOLOCOCOGLGOCOGCGCGGG@GFCF@G@GG@F@F=F=F=F=F==2/2=2/2//./.2/./2//.//2./..//.//.///.2//.2././2//.//////.///////2/=/=/=/=/=2====F=@F=@======@@@CCC@CC@@@@8=8=88=888=98@8@99=9@9=9=9=8=88=8=8=8=8=8=8=8=8@@8@=@=@9@=@==@==@=======/=*=//=/=======/==////=///=/=/=/=/=////////=/2=///2////.2/2///2//2/2.2/1/.//1///2.//1/2/./2/2/... ...1.1.1./2/.1./1. 1../2//2/2..1 . . ./1/2./. 2.. ../.. ... .  ....//.//././..(././(///.*/.(..(.*/*.*/*/*/*/8/*/*/***/***/8==8=8=8=8=8=**===8==8===8===@=@@=@9@@@@@@@@@@=@@@@FG@FG@G@GF@FCFGFCFG@GFGFGFF@F@F@F@F@F@F@====@ (..(..(.*..(.(..(.(/.*.//*//*//*//*/==/8=/==*==/8==8/==8===9==@=@@8@@@C@@@@@C@CCLLLLLMLLMLLLCLCOCLLOLLOLMMTMTSTTSVVSVVVVVSVTVSVVTVVSVVVVTVSTVTSVTTVTVTSTTTTMTMOTOLOMOLOLOCOCOGLGOCOGGCGGCGCGG@GG@GFG@F@G@F@F===F=/F/F=/2/2//1/2././/.////.//.//.//.////2///.///.2////2./2///1//2/////=/2/=/=/=/=========@===@=@@=@@@@@@@@8@@=9=8=@88@8@8@89@9@99=9=9@8@8=8=8=88=8=88=88=8=8=@8@@@8@@@@@==@===@=@========/=/==/=/===/==/==////=/=/=====////////=//=//==////2/////2/2/2//2//2//2/2=/2=2==2=/2=2=2=2/=2.1..2.2./.2/2//2./2/1.1.2/2=2=2==1...1.1.1/2=/2=2../2/. ./2//.../. ... . ..///=/=/==/=///////=/=/=/=//=/*=/=8/8==8====8==@=@@==8==8==8@@C@@@@@@@@@8=8@@@@@@@@CC@CC@CCCCCCCCCCCCCCCCCCCGLOCGOCOGOGOGOGOCOGOGGOGGGGCGGGGG@GGGGGCG@@@C@@@ ( .( (./(.*./(.(/(..(.*.*./*//*//*//8//8/==*=/=*===/8====8@==8@=@=@@8@@@C@C@C@CLCLLLMMMMMMMOMLOLLLLLLLOMMOMTSVTVVVVVVSVVVVVVSVTVTVVSVVVTVSVVTSVTTTSTTTTTTTTTTMOTMOTOLOLOOCOCOCGLGCOGOCOGCGG@GG@G@G@G@FGF@FGF@F@F=F=F==2F=//2///./2/2///2/2//2//.//////2////2//2/////2///2//2///2/.///=2/=/=/=/=/=/=2==@=@=@@@@=@======@8@@=@8=@8=88@8=98=9=98@9=99@8@9=9=8=8=8=88=8=88=88=8@=9=@@@@@@=@@@@==@==@=@=@======/=/=/=========/=/=//=/==/==/==/=//////=2/==2==2=//2/2/2/=/==2==2=/2=2==2==2==F/F==2=====2=2...2/2/2/2///22/2/2//2/.2=F2=FF=2.2/1.....==2==2=/2=/.... ////...//.........////========/=/=/==/=====/==/==/=====8==@@9=@@@@9@C@@=9==9==CC@CCCCCCCCC=9@CCC@CCCCCCCCCCCCCLCLCLLLLLLLLLLOLOLOOLOOOOOOOOOOOOOOOOOOOOOGOCGCGGGGCGGCGLGCCCGC@.. (.(.(/.(/.(.(..(.(/.*/./*///*//8//8/====/8=/=8=/====8==8===@8@=@@@@@C@C@@C@@CCCLCLLOLOMOMLCLCLLOCOLLOMMMTMTSTSVTVSVVVVVVVVVVSVTVTVVSVVSVTVVTVSVTVTVSTVTTTTMTOMOMOOLOLOLGLGGCGLGGLGGCGCGCGG@GG@GG@G@@F@FF=F=FF=F=2F=2=2=2/2/1/./1/.2/./.2././../1//.//2/.//.//.2.///////2//2/////////=/=////=/=2======F=@===========@=@8@@=9=88=88=9=9@8@8@8@8=89=8@8@888=888=*=88=8=@8@=8=@9@@@@@@@@@=@=8=@==@=@=@======/===/=========/=/=//==/====/=///////=/=/=2==2=2=/=2/2=2/2=2/2/=2/2//2=2/2=2=2=/2=/=2=2=2=....2//2.2.2.2/2./1/2/.2.2=2=2=F2=1/1..1.1/2/=2/=1./2=1 . ../2/..... .... .. /////=2====/////////=/////*/=//8/=8===8=8==*=8==@==8=8=8=8=8@@C9@@@@9@@@8=@@@@@@@@@@@CCCCCC@CCCCCCCCCLCCCCCCCGLOCGLGOGOCOCGOGGLOGOGCGCGCGGCG@GCGCGCCGC@C@@@@ ...(../(.(.*.((.(/(/.*/.*.//*/*///*=/8==/=/=8/===8/==8==@8==@=@9=@@@@@C@C@C@C@C@CCCCCCCLCLOCLLLLLLLMOMOMMTTTSTTVTVVSVVVVVVVVVVSVVTVTVSVVVSVVTVTVTSTVTTSTTTTMTOMOMOOLOLGOCGCGCGOCGLGOCGGCG@G@GG@G@GFG@FF@FF==F=2=F=2==2//2//.2/./.2//1//./../././/.///.//.2/.2//1/2/2/2//////////2/=////=/=///=/=/=====@==@========8=@@=@8==8=88=888@89@8@8@88@88@88@8=988=88=88=8=8@8=8@=9@=@@@@@@@@@@@=@==@=@@@=@=@=====/=/==========/=//=/=========/=/=/=////===/F=/==2/=/=2/=2/=2=2/2//2//1//////.////2//1///1.. .1........1.1.2..1..1/2.2/2/2. 1. . .2/2./2/ ./1/ ./.. .  ... //.//.///../..(./(./(.(/(.(./(/*.*.*/*/*.*/*/*==*/**/**(/88=8=8=8=8=8**=8=8=8=8=8==8===8@===9@@@@@@@=@@@@@@@@G@@G@G@GF@F@@F@F@F@G@FG@F@F@F@F@F@@@@@@=@=@=. ( (.(/.(..(.(.(.(/(/(///(//(//*//8/=/=8===*===*====8==8====8@=@@@@@@C@@CCCCCCCC@CCCCCLCCOCLLLOLLOLOMOMMTMTMTSTTSVTVVVVVVVVVVVVVSVVTVSVVTVTVVVSVTVTVTSTTTTTTTOTOMOOOOLOGCGCGCGCGGCGGCGGCG@GG@G@G@GFG@GFGF@F=F=F=2==2=2=2//2//.2.2.//1//1//./..././/.2/./2/.//./2///////=/2//2./2////2=/=/////=///=/======@=====/=====8@=@=98=8=*8=8=9@8@98=88=88=8@8898=888=88=8=8=9=9@8@=@9@@@@@@@@@@@@==@@=@@=@@@@========/===========/=/=/=========//=///=/=2=2====2=2=/2/2/2/=2==2/=/2///2//2/2/.2/././2./.2... ...2.1.1 .1...1.1.1.0./2..2/2/0..1 . ..2./.// .2.. .....    .. ./././/./...(...(..(..(.(..(.(.*.*.(/((/(/**/*=*/**/((/(/*/*=*=*=*=*/*(=8/8=8===8===8===8====8===@=@===@=@@F@@F@G@F@FG@@F@GF@G@F@GF@F@F@@F@@F@@@=@@@@=@=@ ( ((..(.(/(/((.(.(.(.*/(/*.*.*.*//*//=8=/====*=====8===8===9==@=9=@@@@@C@CCCC@CCCCCCCCCCLCLCLLLLLOLLLMLMMOTMTTSTVTVVSVVVVVVVVVVVVVSVVVTVSVVVVSVVVTVSTTTTSTTTMTTMOTOMOOLOLGLGCGGCGCGGCGCG@GG@G@G@GFGG@GFG@FF@FF=2=F2==2=2=2/2/1/././2/./2./2.././.1/././/.//.2/1/./2/2/2/2///2//.//.///////=////////=/============/=8===@8@=8=8=8/88=89=98@9=988=88@8=8=88=888=8888@@8@9=@9@=@9@@@@@@C@@@@@@=@@@=@@@@@@@====/===============/=/==========//=//=/====F=2===/=2=/=/2=2=2==2=2/2/2//////2/.//2.///2//.. . .2/.1....1..1 .2.1.1.1//1/2=2/.1... 1 2//1/1/ /./. ..... .   .. .//.//////./../././/(.(..(.(.(/*./*/(*/*/(*//*/=8/**/***(=88/8=8=8=8=(/*=8==8=8===8===8====@==@8@@@=@@@@@@@@@@G@G@GG@G@FF@F@@F@FG@@G@@F@F@@F@@@F@@@@=@=@=@( (.(.(.(.(/(.(.((.(.*.*/.*/*./*.*//8//==8======8/====8===8===8=@=@@9@@@C@CCCCCCCLCLLLLLMOMLOLCOLLLLOMOMOMTTSTSVTSVTVVVVVVVVVVVVVVVVVVSVVVVSVVVTVSVTVTVSTTTTTTMTOTOMOOOLOGLGCGCGGCGG@GGGGC@GG@G@GFCGFGFCFF@FFF=FF==F/F/F/2=2/2/1/1//.2/.//1//2./2///2//2//2/2/2/2/2///2/2//2////./2/////2=/////////=/=/========/=/===8===@8==8=8/8*=888@8@8@8=8=898=8888=88=888=8@8@8@9=@9@@@@@@@C@C@@@@@@@@@@@@@@@@@@@@=8====/=====@========/============//=/2==2===F=F=F/=//2/2=2=2=2==2==2=/2=2=2=/==2=/=2==2=2/....2=2=/2/2./2..2/.2../2/2/2=F=F.2.2.1...2/2=/2=.1/2/ .. .2/// .. .. . . (./..//==/====/=/=/=/==/=///=////*=/==*===/=8==8==@=C===8@=8=*@@@@@@@@@@@@8=9@@@@CC@C@@C@@C@CC@CCCCCCCCCCCCCCCCGLOCLOLOOGOGOGOGCGGCGOCGGCGGCGCG@G@GCC@CCC@@C@@@( ( (..(.(/(.(.(.((.(.*.*//(/*/(//*//8/=*====8==/==8===8====9==@=8@@@@@@CC@CCCLCLLLLMMMTMMTMMMOLLOLMOLMMTTSTVVVVVVVXVVVVVVVVVVVVVVVVVVVVSVVVVSVVTVVVSVTTTTTMTTTOTTOTOMOOLGLGGCGCG@GCGG@G@G@GFCFG@GFGG@GG@GFF@FF=FF=F=2F/2=2///2/./2.//2/2//.2./././///=/2=/==2=/=2=2==/F/=2///2/2//////////=//=//////=//=/==/==/==/8===8@=8=88=88/888=8=9=98@8@88=899=888=88=88=88=9@9@9=@9@@@@@@@@@C@@C@@@@@@@=@@@@@@@@=@==================/===============/==/===F==F==F=2=/=2=2===2=F==F==2==2===F/F/==F==F/F=/2..1/===2=2//2//1/.2//2/2/2/2=FFF=2/2/1.2..===2==2.=2//...../2//.... ... ..../...==/===F===/============///8/=/=8======8====@8@@C@@@@9==8@@@9@@C@CC@C@==CC@CCCCCCCCCCCCCCCCCCCCCLCCCLCLCLLOLOLOMOLMOLOOOOOOOLOOLOCOOLOLGOGGOGLGOCOCCLCCCGLC( ( (.(.((.*.((.(.(.*.*/(/*//*/(/*/*/=/=8======8=====8===8==@8==@@=9@@C@CCCCCCLCLCLLOMLMOMOLOLLLLOMLMOMMMMTTSTVSVTVTVVSVVVVVVVYVYXYXVVVVVVSVVVVVSVVTVTSTTTTMTMTOTMOTOOOLGOCGCGGCG@G@GGG@GG@G@GFG@GFG@GFGFGFF@FF=F2=F2==2/2/2//2.//1//.2./1/./1./.1./.2//1/2.//2//2/2/2/2=/2////././/.///=/////////=//=/=/==/=/=/==/8===8==9=88/88*=8888@8@89=9=899=98@898=88=889=8@8@=9@@9@@@@@@@C@C@@C@@@@@@@@@@@@@@@@@==========@=@==========================2=F=F==F==2=2=////2/==2=2==2==2=2=2=2==2=2=2=2==2/..../2=2=2/2/2/2.1/1.1/1/2/2/F=FF..2/.....1/2/2/2/.2/2/ . .=//. . .. ....././/=/===/=/=//=///=/=///*//*//*//*//*=/8/8=8===C8==8==8=8=@@8@@9@@@9@@8=@9@@C@:@C@@@C@@CCC@C@CCCCCC@CCCCCCCCLGLOLOCOLGLGOGGGGCGCGLGCOCGCGCGCGCCGCCCCC@C@CG@..(.(.(.(.(/(.(.(.(.*/(//*.*//*//*/=*====8======8===8==@8@==@=9@@@@@@C@CCCLCCCLCLLLCLLLLLLLLLOLLMOMOMMOMTMTSTTVSVTVSVVVVVVVXYYXYVYXYVVVVVVSVTVVVSVTVTTSTTTTOMTOTMOTLOOLGOCGCGCGFCFG@G@G@GFG@GFG@GFG@GFGF@FFFFF=F2=F2=2=2//2/2.2./1/.//1/.2./.../.2./.2/./2./1///./2///1//1/./././//./////=////=/////=/=/====/=/=/8/8==8=8=8=8=*=8=8=88@8@89=9=899@99=989=88=8=98=99@@9@@8@@@@C@@C@C@CC@C@@@@@@@@@@@@@@@@@=======@=@=@==================F=F========F==F==F=/2=2=2=2==2==2=2=2/=2///2//////2//2/. .. /2////2...1. .1.1.1..2./1/F//1..1... ../2//./. /... ....   ( ../././//./././.*/./(/(/.(.((.*.(/(.*.*.*.*/*/*==**.8*(/(=*/8*=*8/8=8/**=8=8=8=8=8==8==8==@==8====@==@=@@@@@@@@@@@G@@@@F@@F@@F@@F@@@@@F@@F@F@F@@@@=@=====@=.( (...(.(.(.((.(.*.*/*.*//*//*//*/=/8=======8===8==8==@=9=@=@=@9@C@CCCCCCCCLCLLLLLLLLLLLLLLLLOLMOMMOMTMTTSTSVTVSVTVTVVVVVYYXYVYXYYXYVVVVVVVSVTVVSTVTTTTMTTOTMOTOOMOOOLOGOCGG@G@G@GFCFG@GFG@GFGF@FGFGF@GFF=FF=F=F2=2=2=2/2//2/1/.2.2.//1./.2...2./2.//1//1//1/2//./2/./2/.2/.///.////////////////=////=/=/=/=/=*=/8=8=8=8=8=8=8=888=88@8@8989@9=9@9@9@898=988=89=9@@9@@@9@@@@@C@CCCCCCC@C@@@@@@@@@@@@@@@@==@@=@@=@@=@============@=======F====F===F==F==F/=///2/2=2==F=2===2//2=/=/2//2///./.. ..1//2.2..1.. 1. . .1./1.2//2/..1. . 2/..2/...... ....     (..../.....(..(../(/.(.(.(.(.(.(.((.(.(.((/(.(*=*.*(=((((/8*/*/8/*=*=*(=*=*=*=*=8===8===8==8===8===8======@=@@@@@@@@@F@@F@@F@@F@@F@F@@F@F@F@@@@=@@@==@====@( (( (( ((.(.((/(.(.(.*.*//*/*/*///*=/*=/8========8==8==8==@=@8@@=@@@@@@CCCCCLCLCLLLLLLOLLLOLLOLLMOMLMTMMTMMTTSTVSVTVTVVSVVVYVXYYXYYXYYXYXVVVVSVVTVSVTTVTSTTTMTOTTOMOOMOOOLOGOCGG@GF@FG@GFG@GF@FGF@GFG@FGF@FFF=FF=F2=F/F/2/2/2/.2.2.2./.2/./1./.2./.2/.2/./2.//.//1/2.//2.//1/./2.//././///////=//////=//=//=/=*/==*=*==8=8=8=8=88=8=889=8@8@9@8@99@9@9@98@888=98=9@89@@9@@@9@@C@C@CCCCCCCC@C@C@@@@@@@@@@@@@@==@=@=@@=@===@========F==@F=@F=@=F===F=F==F=F==F/2=/=2=2=2==2==2=2=/=2/2//=2///2/2... ../2//2/.2.2. . .1.1..1.1./F=2.1.... . ../2..2. 1... ../..     ../..///../././/(//(//.(.(.(.(/(/(.(.(.(/(*/*/*=*/*/=/*.**/8/8=*=8/8=**=8=8=8=8==8==8==8=====8==@======@@@@@@@@@@@@F@@F@@F@G@FGF@F@@F@@F@@=@@@@=@@=@==@====( .(  ( (( (.((.(/(.*./(/*/*//*///=///*=/8========8==8===9=@=@=@9@@@@@C@CCCCLCLCCLLMMMMMTSMMMLMOLMLOMMTMMTMTTSVTVVVVSVVVVVYVYYXYYXYYXYYXYVVVVVVTVVTTVTSTTTTTMTTOMOTOMOOOLOOLGGCGG@GG@GF@GF@FGF@FGF@FFFG@FFF@FF=FF==2F/2=2=/2/2/.2.2.2./.2./..2./1/./.2./2./.2/2.//./2.2/.2.//2.//.//.//.////=/////////=//=//=/=/8/=*=8/=8=8=8=8=88=88=88=9=999@9@99@99@999=988=9=9=9@9@@9@@@@C@@CCCCCCCCCCC@C@@C@@@@@@@@@@@@@=@=@@=@=@=@=@======@==@==@F@@F@@@F@==F=F==@=F=F==2////=2==F==2===2===F==F==F====//1..2/==2==2/=2/1..1...1/2/2/2=F==2.=1../1.2=2/2=/..//2. . /2//.. . .. .......////=////////=//=/==/=/=/=*/=/==/*/=*///*==8/=@@8==@@8=*=@9=@@9@@@9@@=8@@@@@@@@9@@@@@C@C@C@CC@CC@CCCCCC@CCCCCOLCCOCCCGCOCOCGOOGOCOCOGGCGGCGCGCCGCCCCCCCCC@@(  ( (.((( (.(.(.*.*//(//*//*=//*=/=*=/=8=======8==8===@8@@=@@@8@@@C@CCCCLCLLLMMTMTSTSVSSTSTMMOMMTMTMTSTVSVVVVVYXYVVVVVVXYXYYXYXYYXYYXYVVVVVVSVTSTTVTVTTTMTOTTOMOOOMOOOOOLGGGGCGF@FGFG@GF@F@F@FF@FGFF@FFFFF=F=FF=2=2=22//2/2./2./.2.2./2././1/2/1//.1//2././2.2/1/2/1///1///2/.//..///////=///=//////=//=/=/=/8/=*=*==8=88@8@8=8@88=98@9@9@99@9@9@:9@9@89=88=9=9@9@9@@9@C@@C@CCCCCCCCCCCCC@@@C@@@@@@@@@@@@@@=@@=@@=@@=@=@==@==@@=@F=@@F@@F@@F=F===F=F@F@=F/==2=2==2==F=F=F=F=F==F=F=F@F=F=2///2/F=F=F==F2=/.1//1.2/1/2//=FFF=/2=//2...=2==/F..2/2/.. . ////...../.. ....(././///=/===/=/=/=/========/====8====8=/=8/8/===8=@C==8C@==8@@@9@@@@@@@9@=9@@@@9@@@C@CCCCCCCCCCCCCCCCCCCCCCLCLLOLLOLLCOLOLLOLOLOLOOLOLOOOLGOOOLOLOLCOCOCLGCCCCC.( (  ...((.(.(/(.*.*/*//*/=/*=/=/=*/=8=/====8==8===8===@=9@@@@@@@@C@C@CCCLCLCOLMMMMMTMTMMMMMTMMOMMTMMTMTTSTVVVVVVXVVVVVVYYXYYXYYXYXYYXYVVVVVTVSTVTSTTSTTTMTOTOOMOOOOLOOOOCOGCGG@GFG@FGFFF@FFFFFF@FF@FF@F@FFF=F=2F/2=2=2//2/2./1.1.2.2./2.2/.2.2..2/..2/.2/.///1//.2/1///2.////1/./././/////=////=//=/=//=/8/=/8/8/*=*8=8=8@8@8@8@9=9=9=99@9@9@99@9@9@9989=988@9@9@@9C@@9C@C@CCCCCCCCCCCCC@C@@@@@C@@C@@@@@@@@=@=@=@@=@@@=@@=@==F@F@@F@=@F@@F@F=F=F@=@F=F=F==2==/=2==2==2==2==2==F==F=F=F==/..../=2=2=2=/=2.1.1..1../2/2/2@F=2//2/./...=/2=/2..//./ . .2/2. . .... ......(.////////////////=*=//==/*//8/==/8=/8///*/=*/*@@/8/=9/8=*@9=@9@@9@@@@8=@9@@@9@@9@@9@@@@@C@CCC@C@@C@CC@CCC@CCCCCCCGCCCCCGCOCGLGLGOCOCGOCGCGCOCGCGCCGCCCCCC@CC.(( ( ( ((.((.((.(/(/*/*//*//=/8/=/=8/===8====8===8==@8@8@@@@@@@@@@@C@CCCCCLCLLLLOLOLMMMMMOTMMTMTMTMTMTMTMTSTSTSVVVVVVXYVXYYXYYXYXYYXYVYVVVVVVVVTSTTVTTTTTTMTOTOMOMOOOOOLOOGGGCGGFG@FGF@GFF@FF@FFF@FFF@FFFF@FF=F2=2=2/2/2//2/1/1/...2.2/2.2/1/1/1/.1/./.2/.2/1//1/2./2.2//2/2.///././/.//=/////=//=////=/=/=*=/*//*=*/8*=8=8=8=8==9@89@8@9@99@9=99@89@9@998=9@8@9@9@@9C@@@:@CCCCCCCCCCCCCCC@C@C@@C@C@@C@@@@@@==@=@@@@=@@@@F@=F@@@=@F@@F@@F@@F@=@F=F@F@F@F=F===2=2=2==2=F===2==2/2/===/F/2/ ....2//.///2/2.. .. . .1.....=F=2../.1.. /1//.// .... /./. . .   (...(.(..(.(/.(/(/.(/(/(/.*.*/*.*/(/(/(.((/*(.*=/*(/8.*.*=*=8/8/8*=*8/(/*8/8/*=*8/8==8=8===8===8========@=@=@@@@@@@@=@@@@@@@G@@@@@@@F@F@@F@@@@@@@@=@=@@=@===..(.(  .(.(.(./(/(//*//=*=/=/8/=/=8/====8====8==@=8==9@=9@@@@@@@C@C@CCCLCLLLLLLLLMLMOMTMMTMTSTMTMTMTMTTSTTTVTVSVVVVYVVYVVYXYYXYXYYXYVXYVVVVVSVTVTSTTTTTTTMTOOOMOOOOOLOOOOCGGGGG@GF@FGF@FFF@F=FFFF@FFF@FFFF=FF=2=2=2=2=2/2//1/1.2..2.2.2/1//.2.1/.2.2.//1///1/2./2./2//2/.///.//././/.////=///////=/=/=/=/8/=*=*/**/*8=8=8=9=9=9=9=9@89@9@99@8989@98@9@99=98@9=9@@:@@C9CCCC@CCCCCCCCCCCCCC@C@C@@C@C@C@@@@@@=@=@=@@@@@@@@@@@=@F@F@@F@@G@@F@@F@F=@F@@F@@=F@F=F==2===2=F=2=F/=2==/2=2==2=//. . /1/1/2/.2...0. 1 . ..1.1.2=2..1..... ...././ .... ../.. .    .( ..(...(..(..(...(.*..(.(.(..(///(/(/(.((.(*.((/*/((//(.*.8/*/**/*/8/*(**//8/***/**/*/*=8=8==8=====8====8===@==@==@=@=@=@@@@@@@F@F@F@@F@F@@F@F@@@=@=@==@=@=@=.(.( . (..(.((.(/(/*//8/=/8/=8/===8=====8====9=8=8=@8@@@@@@@@@@@C@CCCCCLLGLLLLLLOMMMOMTMSTSTSSTSTSTTMTSTSVTVSVVVVVXYYXYXYYXYYYYXYYXYVVVVVVVVSVTVTTVTTMTTTMTOMOOOOOLOOOOOOCOGGGGFGGF@FGF@FF=FF=F=FF=FFFFFFF=F2=2=2/2=2/2/2./1.2.1..2.//1/2.2./1..1.//1//1/2.2/2./2./2///2//1//../.././///////////////8/==/=/8/*/***/*8/8=8=8=9=9==98@9@8@9@98@989@99@9@999@8@9@9@@:@CC@CCCCCCCCCCCCCCCCCCC@C@CC@CC@CC@@@@@====@=@@@@G@@@@F@@F@@F@G@G@FCF@F@F=F@F@F@F@F@F@F==F=/F==F==F==2==2=2===2===2...1..2///2//2/.1..1. .1 .1...2=F/..//.2.. /./1//. ../. ./... .. .  . .(..(...(..(..(..(..(/.(.*.(/.*/.*./(/(.(.*.(((==(/*=**/*==8=8=/8/*/*=(/=*=**=*/*/*8/8/8===8===8@===@=@@=@@@=@@@@=@@=@@@@@@@@F@@@F@@F@@F@F@@F@F@@@@=@=@@@@@@=.( ((( ( .((( (.( (.(.(/(=//8/=8/=/=8=/===8=====9==8=@=8=@8@@9@C@C@C@@@CCCCCCLLLOLOMMMTMTSMSTMSSTSTSTSTSTTSVVVVVVYXYVVVYXYVYYXYYXYXYYXYYVXYVVVVVVVVSVTVTSTTTTMOTOMOMOOOOLOOOOGOGGGGGG@GFGF@FF=FF=F=FF=FFFFFFF=F=F=2=2/2=2/2/2/2..1...2..2.2/.2/.1..1/..2.2/2.///.2.2/2.2/2///2//././.../././////////////=/=/8/=*/*/*/***/88=88=9=8=8@8=8@89@998@99=989@99@9@9@9@9@@9C@@C@CCCCC@CCCCCCCCCCCCCCCC@C@CCC@CCCC@@@==@==@@@@@@G@G@@@F@@F@@G@@@G@@@F@F@F@F@@@F@@@F@F@F==F=2=F===F==F=2===F=F=FF=///1/.2=2=2==2=2/../.1/1.2./2/2@F=/2/2/2//..///2//2.//2/. . ////.. ./.. ... ..//.//.///////.///////8////=////==/==/===*///8=/8/=@=8/=8==8=@@@@=@@@8=@==*@=@=9==8=8@=8=9=@@@@@@CC@@C@C@CCCCCCCCCCCCCCCCCCGCGLGLGLGOCGGCGGCOGLGLGLGLCGCCCCCCCCCC.(( (.( (  ( (.(.((.(.(/*/=*=/===8/===8=====8@==8@=9=@=9=@9@@@@@@C@@@C@CCCLCLOLMMMTMTMSSVSVSVSVSVSVVVSVVVVVXYXY[YYXYXYVVYXYXYVYVYYXYYXVYVVYUVVVVVSVTVSTVTTTTTMTOOTOOOOOOLOOOOGGGGGGGG@GFGFF@FF=F=F2F=FF=F=FF=F2=2=2=2=2/2=2//2.2..1..2.1/.2/.2..2...1/1/.2/.2/1//2.2/2./2/2///././...../././///.///////*=/=/8//8/***/(**/8*=8=88=8=888@8@89@9@98@99=99=9@9@9@9@@9@@9@:C@CCCCCCC@CCCCLCLCCLCCCCCCC@CCC@CCC@@@@=@=@=@@@G@@C@G@@@F@@FC@G@G@G@G@@@F=@@F@@@FF@@F@@F@F=F=F=FF=F@F=F=F=F=F=F@F@2////2==/==F==F//2/1/2//2.2/1/=FG=2=2===//..2///==/.////... /=2=.....//. ./..(./././/=(/=//////////=/=/=/=*=/=/=====8==/==8==@=8=@@@=8@@===8C@C@C@@@@@@@@8@@@@@@@@@8@=@8@@@CCCCCCCCCCCCCCCLCLCLLLCLOCGLCCLLOLLOLOLOOOOOGCOOGLOOLOLLOLLLCLCCOCLOL.(.((.   (.((.( ((.(.(/*//*===/==8===@=@=@==@8==8@=9=@9=@@9@C@C@@C@@C@CCCLCLLOLLMOMMTMTSTSTSTSTSVSTVSVVVVVYYXYXYYYVVVVXYVYXYXYXVYVVYVVXVVYVVVVSVVVTVVTSTTTTOTMOOMOOOOOOOOOGOGGGGGGGGG@GFFF@FF2F=F2F=2FFF=FF=F=F2=2=2=2=2/2//2.2./1..1/1./1/1...1...1/1/.2/.//.2/./2/2//2//2///../../..///././////.////*=/=/8//*/***/**8/8=88=8=88=888=9=9@89@999@89@98@9@9@9@9@9@C@:@CCCC@CCCCCCCCLCCLCCCCCCCCCCCCCCCCC@@@@@=@=@@@@@C@G@@G@F@@F@@G@@@G@@@F@F@F@F@@F@G@F@G@F@F@==F===F==F=F===2===F=F=2/.2./=2//2==2==2.././.2.///1/2/F@//2=2/=2...//.///..////... ////.. ../. (./ ../.(////(////./*///*//=///////*//==/=//8//*//8=@=*/@9==*@@=8==@@@@@@9====8==8=@=9=8=8=8=8=8=9@@@@@C@C@C@@CCCCCCCCCLGCCCCGCCCCCGLCGLGLGCGCGCGCGCGCGCOCOCCCCCCCCCCCL.((.( ((  (( ( ((( (.((.(.*/*/=/8======@=@@@8@@==@=@8@=9=@9@@@9@@@C@@C@CCCCLLOLLOMOLMLOMMMTMSTSSVTSVVSVTVSVSVVVVVVVVUVVVVVYXYYVYVYXYVYXVVVVVVVVVVVVTVSVTTTTSTTTTOTTOOMOOOOOOOOGOGGGGGGGGFG@FFF=FF2=2F2F=F=F2F=2F2=F/F/F/2/2=2/2//.1..2..1/1.1..1../1...2.2/.2/1//.2/2.//2//2///////./.../././//.//.//./*///=*=/8/8/*/**/**8=8=8=88=8=8=98=9@9@9@9=999=99@9@9C@:@9@@9@C@CC@CCCC@CCCCCLCLCCLCCCCCCCCCCCCCCCCC@@@@=@=@@@@GC@GC@G@@F@@@G@FCG@G@G@@@F@@@G@G@G@G@G@F@@F=F=F=F@F==F==F/F/2//2=/....././..//2/2/...1.....1.....2=2...//2/. ... ... ././.. /... .... ( ( .( .(. .(.(...(.(.(.(..(.(.(.(.*.*/(.(.(.(.*=(/*.=8.*/8=.*/*==8=*==*=*/*(/*=*/*/**/**/**/8/8/8==8====@8===@=@@@@@@@@@@@@@@@@@@@@@@@@G@F@F@F@F@@@@@@@@@@@@=@@@@@@(.(.(( ((  ( ((..( (( (((.(/*//=/8===@=@=@@@@@@9@=@=8@@@9=@@9@@@C@@C@CC@CCCCLLOLMLMOLMLMTMMTSTSTSVSVTVVSVVVVVVVVVVVVVVVVVXYYVXYXYYVVXYVVVVVXVVVVVSVVTVTVSVTTTTTTTMOTOOOMOOOOOGOGGGGGGGG@GFGFGFFF=F2F=2=F2F2=F2=F/F2=2=2/=2=2/2/2/1/1.1/1..2./1.2.1..1.....2/.//2/.2///2//2//2//2///../..../././/.//./////*//=*=/8/*/*/*/8/8*=8=8=8=8=888=9=9=9@9@9@999=9@@9@9C9@C9C@9C@C@CCC@CCCCCCCCCLCLCLCCCCCCCCCCCCCCCCC@@@@@@=@@@@CG@G@@FC@F@F@@G@@G@G@F@G@GFCFG@G@G@G@G@F@F@@F@F@=@F@=F=2==2=/=///...../....2////..1... 1 .. ...2//...2.//.. ... .... .//.... ./... ... .. .......(...(..(.(..*.(.(..(/(/./(/.*.((.(.=(/(===*/.8//(/=*=/8/=*/*/*/(/*/*=*/8/*/***/**/*=*==8===8=====8=@=@@=@@@@=@@=@=@=@@@@@F@F@=F@F=@==F=@=@====@=@@=@@@@.(.(.((.( (  ( ((((.(( ((.(/(*//8=====@=@@@9@=@@@@9@@=9=@@@@9@C@@C@C@CCCCCCCLLLLOMLMOMOMMTMTSTSTSVTSVVVVVVVVVVVVVVVVVVVVVVYXYYVYXYXYYXYVVVVVVVVVSVVVTVSTVTTSTTTTMTTOTMOOOMOOOOGGGGGGGGGG@GFGFFFF=F=2F2F=2F=2F2=2=2=2=2/2=2=2/2//2/1/1.1/1..1.1..1..1..1.1.2.2/./2.//1//2//2=/2///2//./././/.//.//.//./*.///*/=*=/*=*/8/**8/8=88=8=8=8=8888@9@9@99@9@9@9@9@@9C9C9@:@C@:C@CC@CCCC@CCCCLCLCCLCCCCCCCCCCCCCCCCCCC@@@@@@@@C@G@C@C@G@F@@FCF@G@@G@G@G@G@GG@GG@G@GG@G@G@F@F@@F@F@F@=F==2==2=2=/....../.../=/2/2...........1.../=2..2.2/// ... ... ././/.. /./. . //. ( ( ..( .( ...(..( (.(..(.(.(./(.(..*/(.*..(.(.*.=*/(===.*==(//(====*=8/8//*/(/*/*/8//****/***/8/8=8=========@=@=@@@@@@@@@@@@@@@@@=@@@@@@@F@@F@F@F@@@@@=@@@@=@@@@@@@@(.(.(.(.( (  (..(( ((.((((.*.*/*=8==@=@@@=@@@@@@@@=@@@9=@@@@@@C@@C@CC@CCCLCLOLMOLMLTMTSTSTSVSVVSVVVVVVXVXYXYXYXYXYXVVVXVYVYXYXYVYXYYVXVVVVVVVSVVTVSVTVTTVTTTTTTTMTTOTTOOOOOOOGGGGGG@GFGFGFFFF=FFFF=2F2=F2F/F2=2=2=2/2=22/2=2/2///1//.2.1/1/.1..1...1.....2.2/.//1///2/2/2=2=2//////.//..//.//.//.//.//(///8//8/8/=*/8/8=8=8=8=888889=8@88@9@9@9@9@9@9@@9@9@@9C9C@:C@:C@CC@CCCCCCCCCLCLCCLCCCLCCCCCCLCLCCCCCC@C@@@@@@@@CG@G@@G@F@@FC@@G@G@G@FG@G@GCG@G@GGCGG@G@G@F@F@@F@@F==F===F/==F...../2/2/2=F=F=/2//2.2//1///.2/F=2/2===F/= ///..//../////.. ///... .///. (./ ..../(/./..(//./.*/.//8////*////*//=//=//*=/*/=/==8==@@@==@@====@@@@@@@=@@==8==8=====8@=8=8==8@=@@=@@@@@@C@C@CCCCCCCLCCCCCCCCCCCCCCCGCOCOCGCGCGCGGCGCGCCCC@CCCCCOCLGL(.*.(.((.((  ( ((( (.(.(.(/(*/*/8===@8@@@@@@@@@@@@@9@@@9@@C@@:@@@C@CCCCCCCLCLLOMMMTMSVSVVUVVVVVXVVXYXYXY[Y[[[[[[[Y[YXYYXYXYYXYYXYYVXYVVVVVVVSVTVTTVTVTSVTTTSTTTTTTTTOTTOOOOOOOGGGGGG@GFGF@FFFF=FF=FF2=F2F=2=2=2=2=2=2/2=2/2=2/2/2.2/1./1/.1/1.1.1....1.2..2.2/1//.2/////2//2=2//////.//.//.///.//./.*./*//*/*=/*=*=8=8/88=8=9@8@8=88=888=8@9@9@9@@9@9@9@9C9C@:@:@@:@CCCCC@CC@CCC@CCCCCLCCCCCCCCCCLCLCCLCCCCCCC@C@@G@G@C@G@G@@F@@F@@G@G@G@GG@G@GGCGGCGGCGCGCG@G@G@G@@@@F@F@F=F=F==F==../.2/////@F@F@F///2///2//2/2//F=F/2=2=F=F../=..///..//=//// =/=...../=/...//..../////(..////*//*///=/8////=*//=====/===/*=/=8@===CCC@=@C====@C@@@C@@@@@9@===8@=@@@=9=@=9=@@@@@@@@@CCCCCCCCCCLCLLLOLLLLLLLLCLCCLGCLLOLOLLGLOGLOGLOLGCGCCOCOLOLLOLLO(.(.((.(( ((. ( ( (.( (.((((/(/*/*==8@=@@@@@@C@C@@@@@@@@@9@@@C@C@9C@CCCCCCLCLOLLLOMMTMTMTSTSVSVVVVVVVVYXYYXYX[Y[Y[YXYYXYYVYXYYXYYVXYYVVVVVVVVTVTSVTTSTTVTTSTTTTTTTMTTTTOTOOOOOGOGGGG@GFGFFF@FFFFFFF=FF2=F2=F2F/2=2/2=2=2/2=2//2/2/.2//2./1.1..1..1.1...1....2.2/.2/./2/2//2=/2=2//////././/.//.//..(./(/.*//*/=*=/8=8=8=8=88@=9=9=9=89=8@88@9@9@@9@9@@9@9@@9@9@9C@9C@C:@CCCCC@CCCCCCCLCCCCCCCCLCLCCLCLLCCLCCCCCCC@G@@@@G@C@@G@@F@@F@@G@G@G@GGCGG@GCGGCGGCGGCGG@G@G@F@F@@F@F======2=//.....//./2==F==//2///////1//.2/=F=.//=F=F=.///.../..//////...///... //// (//...(.(././(./(/...(.*.*.*///*//*//*.//*=//*//*/*/=@=8/@@@=8@@@=8=@@@@@@@@8@===8=*=8=*=8==8=8=8==8@@@@@@@@@@@C@CCCCCCCCLCCLCCCCCCCCCCCCCCCLCGCGCCGCGCGCGCGCCC@CCCCGCLGCL(.((.(.((..(( ( ((.(( (.(.(.(/(/*/8===@8@@@@@C@@C@@@@@@@@@@:@@C@CC@C@CCCCCLCLLLOMLMMOSTMTTSSTVSVSVVSVVVVVXYXYVXYXVYYXYYVXYXYYYXYXYYXYVXVVVVVSVVTTTSTVTTTSTTTVTTTTTTTTTOTTTOOOOOOGGGGFGF@FFFFFFFF=FFF=F2F/F2=22=2=2=2/2/2/2/2/2/2/2/.2./21.2.2./1.1..1....2.../.2/./2././2/2/2////2///./././././././.(..(/.*//*/8/=8==8=8=8@8@9@9@9@9=88=888@8@9@9@9@9@@9@9@9C@9C9C@:@CCC:C@CCCC@CCCCCCCCCCCCLCCLCLLCLCLCLCCLCCCGCC@C@C@@@G@@@G@@F@@G@FCFGCGCGGCGCGGCGCGCGOGCGCGG@@G@@G@F@F@=F=F===//........../2=/=1................2/..2./=/=. ... ... .../.. ///. /..  .... (. (. ...( .( (..(.(.(.(.(..(//*./*.*.(.(.8/*.*===*/=8//(===8/=8=*=*=*/*/*/*.*/****/**/*/8/*=*=8=8==8=8===@@@@@@@@@@@@@@@=@=@=@=@@@@=@=F=@=F@=F@@=@F@=@@@@@@@@@@(.(.(.((.(((.( (.((.( (( ((((*/(/*==8=@@=@@@@9C@@C@@C@@@@@@@CCCC@CC@CCCCCCLCCLLLMOMTMTMTSTSVTVSVVSVVVVVVVVVVVYVXYXYXYYXYYYYXYYYXYYXYYVVVVVVVVSVTVTTVTTTTTTTVTTTTTTTTTTTOTOTOTOOOGOG@GFGFF@FFFF=FFFF=F2F=2F2F=2F/2=2/2=22=2/2//2/./2.2/1/2/1..11.2.1.1.1...1.1.2./1/./2/.///2//2//////.//../././../..*..(/(/.*/*/=*=8==8=8=@8@8@@9=99@8@8@888=9@9@9@9@9=9@@9@9C9@@9@@:CC@CCCCC@CCCCCCCCCCCCCCCLCLCLLCLCLGLCLCGLCCCCGC@G@G@@G@G@@F@F@@G@G@G@GGCGCGGCGGCGGCOGLGGCGCGG@@F@@@F@F==F==F=// .. ....../=/2=................//..../2=2/.... . ..../.. //... ..// ./. .....(  . .( ...(.(.(..(.(.(//(/.(/.(.(*.(//*.=8/=/*==/.*/8/==8/==/8/=*/*/*/*/(/*/***/**/*/=*=*==/8===8===8==@=@@@=@@@=@@@==@=================F==F======@=@@@@@@/(.((.(((..(( (( (( ((.( ((.(.(.*/*/8/==9=@@9@C@@C@CC@C@C@C@:@C@CCCCCC@CC@CCCLLLOLOMLMTMTMTSTVSVTVSVTVVVVVVVVXVXYVYVYVXYYXYXYYXYXYYXYYXVVVVVVVTVSVTTSTTTTTSTTTTTTTTTTTTTTTOTTOOOOOOOGGGGF@FFFF=FF=FF=FFF2F=2F2=F2=F/2=22//2/2/2/2/2.2/1/2.2/1/1./1..2..2..1/../1./1//2./2.2///2////.///././...././...*..(..*.*.*/*/=8==8=8@=9=@9=@9@@99@89=9=88=9@8@9@9@9@9@9@@9@9C9C@C9CCC:@CCCCCC@CCCCCCCCCLCLCLLLLLCLCLCLGLCOCOCCCGC@@@@G@@@G@G@@F@F@@G@GCGGCGOCGGCGCGOGOGLGGOCGCG@G@F@F@=F@=F===... ....././F/==.................=/../.2=/=/../. /. . .....( .//. .//. (./.( .(. .( ..( ( .( ( (..(.(.(..(.(//.*/.*.(/(.(/*/.*/===(==///*/==/==8/==*/*/*/**/(*/*(*/(**/*/*=*/==8==8====8=@=@@=@@@@@@=@@@=@@=@=@=@@=@@@=@====@=@F@@@@@@@@@@@@@@@@/((.(((.((( ((.(.( (.( (( (((.(((/*/*=8=8=@9=@@9@C@CCCC@CC@@C@@CCC@CCCCCCCCCCCCLLLLLMOMTMTMSTVTVSVVVVXVVVXVXYXY[Y[[X[[YYXYYVYXYYXYYXYYXYYXVVVVVSVTTVTTTTTMTTTTTTTTTTTTTTTTTTOTTOTOOOOGGGG@FGFFFF=F2F=F2F=F2=2=F2=F22=2/2=2/2//2/1/2.2/1/2/1/2.1/1.1/1.1..1...1../1//1///1//.2////1//////.//.../...../..(./(.(./*.*/8/=8=8==8==9=9@@8@9@@9@8@88=8889@8@9@8@9@9@9@@:@@C9@@:@CC@CCCC@CCC@CCCCCCCCLCLLLLLLLLOCCLCLLLCLCOCCC@CG@C@@G@@G@@G@@G@FG@GGCGGLGGCGCGGCGLGOGLGOGCGCG@G@F@F=@F==F=2/...../.../==F=F///./////////2//F/2//2==F@F//=/..//. ///////..///..( ////./// /(//..*....*../..(/.(./(/./(/.*///*//=////*/*./*==/==@@@@=@@==/=@@@@@=@@@@==@=8==8/8=/=*=8/=8=8====@=@@@@@@@@C@CCCCCC@CCCCCCCCCLCCCCCCCC@CCCCCGC@G@C@GCCGCGCCCCCLGCOCGC/*/(/(((.(.((.(.(..(((.((.(((.(/*/*=8==8@@@9@@C@CC@CCCCCCC@@CC@C@CCCCCC@CCCCCLCLOLMOMMMTTSVVSVVVYXYXYXYYXY[Y[[Y[[[[[[[[Y[YXYYYXYYXYXYYVVVVVVVSVTVSTTVTSTTTTMTTTTTTTTTTTTTTTTOTOTOOOOOOGGFGFF=FF=2F=2F=2F2F2F2=2=2F=2F/2/2//2/2//1///1/2.2/1/2.1/1.1./1.2.2.2..1./1//.2///2/.2/1////1////./......./...*..(..(.*.*/*/8=/8=8=@8=9=8@9@9@8@9@99=888=8=9@8@9@99=9@@9C@:@@:@C@:@:C@CCCCC@CCC@CCCCCCLCLLLLLLLLLCLOLCOCOLCOCGCGC@G@G@CG@@G@@F@F@G@G@GGCGCGCOGOCOGCOGLGGLOOCOGCG@@@F@F@@F=@F=....././///F=@F=//////////=2=/=F=F////F@F@=//=//.=/.///=////./==.....===/../=/(/.//////.(///./*///(///*/.*////*//===//=*=//*///@==/@@@@=@@C===@@@@@@C@@@@@@@==8====8==8=8=8@=@8@@@@9@@CCCCCCCCCCCLCLCLGCLGCLGLLOCLGCCCCLCOCGCCGCCCCGCCGCOLOLOLOLOLOLLO/(/(*.(.(((.((.( (((.(.(.((.(*(/(/**=*=8=8@@9@@@CCCCC@CCCCCCC@@C@CCCCCCCCCCCCLCLLOMLTMTMTSSTTSVSVVVYXYYXYYXYXY[XY[Y[Y[Y[YXYYXYYYYYXYYXYVVVVVVTVVSVTVTSTTTTTTTTTTTTTTTTTTTTTOTTTOOTOOOOGGGGFFFFFF=2F2=F2=F2=F/2F2=F2F/F2=2/2/2/2//21/2/2.2.2.1/1.2..2./1..1/.1./1/2.2//.2/./2.//./2.////./........../../(..*..(/(/*//8==8=8=8=8@8=@9@9@9@8@98=98=988@98@9=9@9@9@9@CC@:C@C:@CC@CCC@CCC@CCC@CCLCLLCLLLLLOLLLCLCLLCLOLCOCCCGC@@@CF@G@G@G@G@G@G@G@GGCOGOCOGOCOGOGCOGOLOOGLGCG@F@@F==F=F=/........../=F==////////////////F=//2//F==F////../// ///////../=...(..///.///(../*./(// /(/(..(..(.(./(.(.(..(/*///*///*/*//*/=///=@@@=@@@===@@@@@=@=@@=@==8==8/8/*/*/=*=8=8==8===8@@@@@@@C@CCCCCCCGCCCCCCCCCCCCCCCCGCCCCCCCG@CC@@@@CCGCCGCLGCOCGLGCC/(/(//*/*.((.((.(..((.((.(((.(/(.**/8/8/8=@8@@@@C9@CCCCCCCCCCCC@C@C@CCCCCCCCCCCLCLLLMOMMTMSTSSVVTVSVVVVVVVVXVVYXVYVVVVVVVVXYYXYXYYXYVVVVVVVVVVVSVTVTVTSTTTTTTTTTTMTTTTTTTTTTOTTOTOTOOOOOGGGG@GFF=FF=F2F2=F2=22=2=22=2=22=2/2/2/2/2/2/2/2/2.2/1.2.1.2..1..2..1/.1.2./2//2///2/./2/.////.///./...../..(/.././(..*.(/*/*/=*==8=8=8=8=9=9=9=9@9@9@8=888=9=9=99@9=9@9=@:@:@CC9C@C:@CCCCCC@CC@CCCCCCLCLLLLLOLCLLOCLLGLOLLLOCOCCGC@GC@GCCG@G@G@G@G@GGGCGGCGLGGOGGLGGLGOOOOOOLOGCG@@F@@F====/./.../....//==F/./..........././/2....//=/=../..../.../././. //. ( ./*// .*..(..(.(.( ...(.(.( (.( .(.(.(.(..(//(/(/(..(..*=/(///=*/==//*/=/======/8==/8/*/*/*/*/**.**/(**/*/*/*/8/8==9=@=@=@@=@=@@==@=@=@=@@@=@=@=@=@=@==@==F======@@@@@C@@@@@C@@/(/*.*/.*/*.((.(( ((.((.(((.(((/(*.**/*8=8=8@9=@@@C@CCCCLCLCCCCCCCCC@CC@CLCCCCCLCLGLOLMMTMTSTSTVSVTVVSVVVVVVVVXYVVVVVYVXYVYVXYYXYYYYXYXYVVVVVVVVTVVSVTVTTVTSTTTTTTTTTTTTTTTOTTOTOTOOOOOOOOGGGFF@FFFFF2=F2=2=2=2F/2=2F/F2=22/2/2/2/2/2/22/2.2/1/1.2.1.2.1/1.2.2.2.2.2./2//2///1//.//./2/2////./../....././././(..(/(/*/*/8*=/8=*=8=8=8=9@8@9@8@89=9=9=88@9=98@9=9=9@@@:@C9CC@:C@:@CCCCCC@CC@CCCLCLCLLLLCLLLCLLCOLCLOLOLLCOCCG@GCGCG@CGCG@@@G@G@GCGCOGOGOOCOGOCOGCOLOOLOOOOCGC@@F===F==/........../F==/////............//./...///2/........ .(..//.. .// ..//// ./( .(.(.(.( .(.( (..(.( .(.( (. ( (.(/(./(/.(.(.(///(//=/=/===*///=====/===/8==/=*//*/*/(/*(/***/*/**/=*/8/=8==8==@@=@=@@==@=@@=@=@=@@=@========@=============@@@@@@@@@@F@/(/*./*/*//*/((.((.(( (((.(((.((.(/*/*8/*=8=9@9@@@9CCCCCCLCCLCCCCC@CCC@CCCCCLCCLCLCLLLOMOMMTSTSVTVSVSVVVVVVVVVVVVXVYXYXYYXYXYYXYYXYYXYYVVVVVVSVTVVSVTVVSVTTTTTTTTTMTTTTTTOTOTOTOTOTOTOOOOOGGGGGGFFFF=F2F/F2F2=2F22=22=2=22=2//2/2/1/2/2/2/2/12/1.2..2.1..2./1./1/..2.2/.2//2/2//1//2.////2//././.......././.(/.*.*.(/*/*/=*8=*=*8=*8=8@8=99=9@9=98=8=9=98@8@99=9=@9@9@@9CC@:@C9CC@:CCC@CCCCCCCCCLCLCLLLCLOCLLGLCLOLLOLOLGCCGCGCG@CGCGCG@GC@G@GGCGGCGOOOGLOOLOGCOOOOOOMOOOOCGCG@F=F===//./../..../===2///./../..../././//././/=2=/../...//.././/./. /.. (.//// (/..(.*..(..( (.(.( .(.(....(...(/.(./*./(./(//=.(==/=/==//*/=======/=*==/8=/=*/*/**/(/***/***/*/*/8/=*==8=@=@@@@@@@@=@=@=@@@@@@@@@@=@=@=@=@==@@==@==@=@@@@@@@@@@G@@@@*/(/*/(/*/*///*(.((( (..((.((.((.*(/(/**=8=8=9@@9C@CCCCCLCCLCLCLCCCCC@CCCCLCCLCLCLCOLLOMMMTMTVTSTVTVSVVVVVVXVXYXYYXYY[Y[Y[[Y[YYXYYXYYYYVVVVSVVTVVTVTVVSVTTVTSTTTTTTTTTOTTTOTTOTOTOOTOOOOOOOGGGG@GFFFFF=F2=F2=22/22/22/22=22=22/2/2/2/22/22/2/1/1.11.1./1..2..2./1/2./1/2/2//2//.//.///2///2//./....../...../.(/..(/(.*/*/*=*/8*=**=88=8=9=9=98@8=9=98=9=99@9=9@8=9=9@@9@@C:@C@:@CCCC:CC@CCCCCCCLCLCLLCLOLCLCOCLGLCOLLOLOLCGCCCGCGCCGCGCGC@G@GCGCGGGOGOOOOLOOOOGLOOLOMOOMOOOCGC@=F=F=//././....../=F=//./////////////===//=/=F=F==///.//=/./==/==//./// .(..////..//(.(///*./(..//(/./(//.(/.*.(..(.(.(/.*//*///*//(/===/===@===@=/==@@=@@@=@========8===8//*=*/=*==/=8====8=@=@@@CCCCCCCCCCCCCCCCCCCCCCCCCCCCC@@@C@CCCCC@C@C@C@GCGLGLGLOLOLGL/*.*/*/(//*//*/(/(.((.(((.((.((.(*.***=*8/88@=9@:@CCCCCCCCCCLCLCCCCCCCCCCCLCLCLCLCLCLLLLOMOSTSTSTVSVVVYXYXYXYXY[Y[Y[[Y[[[[[[[Y[Y[YYYYXYVYXVVVTVSVTVVSVTVTVTSTTTTTTTTMTTTTOTTOOTOOOOOOOOOOOOGOGGGGG@GFF=FF=2F2=2F2=2F/2=22=2/22/2/2/2/2/2/2/2/22.2.2.1/1.2.1.2.1/1/1/2./1//2/.//2.//.2/.//2////../......../(../(..*.(/(/(/8/*=*=**8/*8/8=8=8=9=98@8=8=98@8@8@9@9=9@8@=9@@9@@C:@C9C@:CCCCCCCCCCCCCCCLLCLLCLCLGLCLGLCOLOLLOLOCLGCGCCCGCCGCGCGGC@GCGG@GGOOOOOOOOLOGCOOOOOOOTOOOLOGG@F=@F=/////.///./.=F==.//=//===////==/=F==2==F@G@F///////=/========//=//.(..==/=(..=*./(/=//=/(..*/./*///*//*///.*./*..(//*/////=//*=/==@//@@=@@@@@===@@@@@@@@@@@@@@@=@===8==8===8==8====8=@@@@@@@@CCCLCLCLLOCLOLCGLCGLLOCCOCCOCCCCCCCCCOCCCCGCCCCCOLOLMOLLOLOLO//(/*/*/*//*//*/(.(.((..(((.((.(((.(/(*/**8=8=8@@9@CC@CCLCLCLCCCLCCCCCCCCCCCLLLLLLOLLOLOMMMTMTTSVTVSVVSVYXYXYXYVYXYYXYYY[YY[Y[YXYYXYYYYYVVVVVVVTVSVTVTVTSTVTTTTTTTTTTTTTTOTOTOOOOOTOOOOOOOOOGGGGG@GFFFFFFF=F2=F/22=22=22=22=2/2/2/2.2/22/22/2/1/1.1/1.1..1/.1./1/1/./2./2./2/2././2.///1///././.............(..(.(.(.((/*.*/*8/*/*8*/8=*8=98=8@88@89=9=9=9@9@9@8@8@8@89@@9@@9C@C9CCCC:CCC@CCCCCCCLCLCCLLLGLCLGLCLGLLOLOLLLOCGCCGCGCGCGCGCGCGGCGGCGGOCOOOOOOOOCOGOMOTOMOOOMOOLOCF=@F=///.///../../==/////////=2/////2/==2/=/=@@@F=//.///==/========.//(....//=/ /./.(///*//( (./(..(.(/.(./(/(.(..(.(.(/.*.*//(/./*/==//=======@*/==@@@=@=@=====8====8=/8/8/8/=*=/8=8==8==8=8=@@@@@CCCCCCCCCCCCCCCCCCCCCCCCC@@@@@@CCC@CC@@C@@CG@CCLGLOLOCOCOC//*./(/*/*//*///*(/((.((( (((.((.(((/**/*8=8=8@9@@CCCCCCLCCLCLCCCCCCCCCLCLCLLLLLOLLLLOMOMMMTTSTTVSVTVVVVVVVVYVVVVVVYXYVVYXYYYYYXYYYXYXYXYVVVVVSVTVTSVTTVTTSTTTTTTTMTTTTOTTTOMOOOOOOOOOOOOGGOGGGGGFGG@FFFFF/F2F2=F2=222=22/22/2/1/2/2.2/2/22/2/1/1.1.1/1..1./1./1/1/2.2./2.///./1/.//.///.2//./........ (....(.(.(.((.*.****=*8/8*/*8*8*=8=88=88=8=9=9=98@9@8@9@9@8@8@89@@9C@9@CC@:CCCCCCCCCCCCCCCCLCLOCLLCOCLCOCLOLLOLOLOLGLGCGCCGCGCGGCGCGCGG@GCGGOOGOOOMOOOGMOOOMOOTOMOOOOCG=@F@=////////....=/=/././///=2/.../././/.//./F===......//..=/==/=/. *.  ( *//.( (.*.(.*./(/..(.(.(.(.(..(.(..(.( ( (./(.(./(./(//*/=.*/=/=/8/=//*.=======/==//*=/*=/*/*/*/*/*/**//*/*/*/*//*=*=*==8=@=@=@=@=@@=@@@=@@==@==@=@=======@=========@=@@@@@@@@@@@@//*.*/(*.*//*//*/(*(.((.(((.((.(((.(((/**=*8=8=@8@=@@C@CCCLCLCCCLCLCCCLCCLCLLCLLLLLOLMLOMOMTMTSTSVTVUVVVVVVVVVVVVVVVVVVYYYYXYYXYYYXYYYVYYVVVVVVTVVSVTTVTSTTTTTTTTTTTTTTMTOTOOOOOOOOOOOOOGGOGGGGGGGG@GFGFFFF=F2=2F2=2=2=22/2/2/2/2/1/2/22/2/2/2.1.2..1.2.1/.1.2.2./1/.2/1/1/1/2././/.2/./2/././....... ....( ..(.(.(((.*.**/*/**/**/*/8*888=89889=88@8@98@9@8@9@8@98@8@8@9@9C@9CC@C:CCCCCCCCCCCLCLCLCLCLLGCLGLCLOLOLLMOLOLLOCGCGCCGCGCGCGGCGGCGGCGLGOOOOOMOOGOOTOMTOOOTOOMOOC@F@@F/////////././==/..//////////./...//.../.=/==//...../../==/=///..   //*/ (/...*./(.( (.( (.(.(..(.(.(.(..(.(.(.(/.*./.(.(/./==/*/=/=/=*=./*/=/==/===/==//*/=*/=*/8//*/*/*=*=/*=*//*=*/*//=*===@=@=@@=@=@@=@@@=@=@=@========================@@@@@@@@@@@/.*/.*./*./*//.*/(/(*(.((.((.(((.((.(.(**/*8=8=8=@9@@9@@CCCCCLCLCLCLCCLCLCLCLLOLOLMOMLOMLMOMTMTTSTVSVTVVVVVVVVXVVVVVVVVXVYXYYYYYYXYYYVYVVVVVVVVVVSVTTVTSTTTTTTTTTMTTTTTTTOTTOTOOOOOOOOOOGOGGGGGGGG@GFGFGF@FFF=F2F/F2F2=22/22/2/1/2/2/1/2/2/2/2/2.2.1/.1.1/1.2..2.1/.2./1/1/./././.././/././/./../... .... .....(.(*.*(/*/8*=***=**8*=*=888=8=88@88=8@99=9@9@9@8@9=89=9@9@9@C9C@CC:CCCCCCCCCLCLCCLLGLCLCLCCLGLLOLOLOLOLOLLGLCGCGCGCGCGCGGLGGCGCGOGOOOOMOOGOMOTOTOTOTOMOTOLFC@G==/=////////./=//./././//=/////././././//=F==F/.....//.========..(.   */.*/./((../(/(.( (.(.(.(..(.(.(..(.(.(..(..*.(..*./(./(/*==//==*//=/=(/.===/=/=/8/=/8/=*//*//*/8/8//8/*//==*/=*/*=/8/=*=====@=@=@=@@=@@=@=@@=@==@=====================@@@@@@@@G@@@G/*.**.*./(.*./*.(*.*.(((.(.((.(((.((((.(**/8/8=8@=9=@@@9@C@CCCCLCCLCLCCLCLLLOLLLMOLLLOMOMOMMTMTMTSTVVVVVVVXYXYYXYYXYY[YYYX[Y[Y[[Y[YYXYYXYYVVVVVVVVTVSTVTTTSTTTTMTTTTTTTTMTOTOOMOOOOOOOOOOGOGGGGGG@GGGFG@FFFFFF=F2F=2F=2=2=2/22/2/2.2/2/1/2/2/2.2.2.1.1/1.1.2.1..1/1/1/1/./1../1.././../2././......... .. ( ( ..((.((.*(**/**8/***/**/8*8=88=8888=98=9=99@9=9@9@9=9@8@9=9@9C@:@CCCC@CCCLCCLCCLCLCCLCLCOCGLGLCLOLLOLOLOLOLOLGCCGCGGCGGLGOCGGCGGGOGOGOOOOOGOOOMOTOOMOTOTTOOCG@G@==2/=/=///////=//..././////////=//=/=/=/=G@G@@=/=///==/@@@@C@@@./=.(. (/=//( *// (.*//.*..(.*.(/.(..*.(/.*/.(..(...*.//*.//(./(///===/=========///======8=====/8/=*=/8/=/8//8/8===8===========@=@@=@@@CC@CCCC@CCCCCCCCCCCC@@C@@@@@@@@@C@@@@@@@C@CG@GCGLGCOCOC/*/(/*/(./(.(.(*.*(.*.*.*(((.(.*((/*.**/*/8=8=@@9@@@@@@=9@@@@CCCCCCLCLCLLCLOLLOMLMOMMMOMOMMTMTTMTTVSVVVVXYYY[Y[Y[[Y[Y[[[Y[[[Y[[[[Y[[[[Y[YYYYYVVVVVVTVTSTTTTTTTMTTMOTTTTTOTTOTOOOOOOOOOGOOGGGGGGGGGFG@GFGGF@FFFFF=2F2=2F2=22/2/2/2/2/2/1/2/1/2/2.2.2.2.1/1/1.2.2..1.1../1/../1......./.../...../....... ..  ( ( ( ((.(((/(**/***/**/8**=*8*=8*8=888=88@8=99@99@89@99=99@9=9@9CCC@:CCCCCCCCLCLCCCCLGLCLCLGLCLLOCLOLOLOLOLLOLOLGGLGCGLGCGOCOGOCGOGOGOGOOOOCGOMOTOMOOOTOTOMOOC@G@F===/=2=/=////==/.../././=//==/=/====/===GCCCG@===/===/=GCOCCGC=/==.(..(====/.*/=(..*////(.(/.*/////*///*///*//(//*////=//*//////*//@===@@=@=@===/=*@@@@@@========8=====8/=8==8======@=@@=@@@@@@@@@@C@@CCCCCCCCCCCCCCCCCCCCCCC@CCC@CCCCC@CCCC@C@CGCCCLGLOLOLOLO/(/**.//*.(.*.(((.(((.(((.(((((.((.*((.(***/8=8=@8@@@8@@@@@@@C@CCCCLCOLCOLLLLLLOLMLMOMMOMMOTMTTSTSTVTVVVVVXYXYYXYYY[YYXYYYYYX[YY[Y[YY[YYYYXYYVVVVVVTVTVTSTTTTTTTTOTMTOTMTOTOOMOTOOOOOOOGGGGGGGGGG@GGGFGFGFFFFFF=F2=F2F=22=22/22/2/2/1/2/1/2.2/1/1.1/1.2.1.2.2..2..2..1..1...../../........./........ .( (   ( (.((.((.(/(*/**/***/*8*/88*=88=8=89=88=9=98@9@9@8@9@8@9@9@:@C9CCCC@CCCCCLCCLCLCLCLCLCGLCLGLLLOLLOLOLOLOLOLOCOCGLGCGOCOGGLGGCOGOGOGOOOOCGCOOTTOTOTOTMOTOOCG@G@=F/==2=/=///===//.../../////////=/=/=/===CGCGC=/////=/=@CGCGC@C/==/(.(. */=/(.(//.(.(//(.(..(.*.*/(..(/(.*./(.(..(.(./*./*//*./(//*/==/*========/*/========8==*//*//*//*/*//*/*=*=/8====8=======@@=@@=@@@@@@@@C@@C@C@@C@C@@@@C@@@@@@@@@@C@@@@C@@@@@CGCCGCCCGLGC*/(/*//(//.(.(.(.((.(((.((.(.((.*((.*(*.*.*=*=8=@@9@@@@@=@9@@@C@CCCCLCCLCLLOLOLMOMOMMOMMMTMTMTMTTVTSVTVVVVYXYYYYVXYVYYYYXYYYYVYYYYYYYVYYXYYYVVVVVVTVTVTTTTTTTTMTOTTOTMTOTTOTOOOOOOOOOGOGOGGGGGGGGGG@GGFGF@GFFFFF=F2F/F2F/2=22/2/22/2/2.2/1/2.2/1/1/1/1/1/1/1.2.2.1.1./.1./1........../.../..........( . ( .   ( (( ((((/(*.**/**/*/**=*8/888=8888=888=98@8@9@9=9@9@9@9@@:@C9CCCCCCCCCLCLCLCLCLCLGLCLGLLGLOCOLLOLLCOLOLOLOLGLGOGLGOCOOGLGOGCOGOOGOOCFGOOMTOTOOMOTTOTMOG@G@F@=F=2===2=/=/F=//../..////////////////./=F@@@F////././===@@@=@=//.../(//.*.(.(./(/.( .((.(.(.(..(.(.(..(.*.(..*./*./*./(././*/==.========//(///8/*=/8//=*=/=*//*//8/*=*=/*/*//*=/8/=*/=8=/============@===@===@==================================@@@@@@@@*/*/*///(//*.(.((.((.(((.(.((/((/*(/(**/**/8==8@=@@@@@@@@@@=@@C@CCCCLCLCOCLLLMOLMOMMTMOMTMTMTTSTSVTVVVVVVVVVYVXYYYYYXYYYYYXYYYYVYYYYVYVYVYVYVVVVVVTVTVTTTTTTTTMTTOTOOTOTOTOMOTOOOOOOOGOGGGGGGGG@GGFGGFGFFF@FFF=F=F2F/F2=22/2/2/2/22/2/1/2.2/1/2.2/1/1/1/1.2.2/.2./.1....1.............................     ((.(((.*(**/****/**=*8/888=8=888=88=8989=9@9@9@9@@9@9C9@CC9CCCCCCCCLCLCLGLCLGLCCOCLCLOLOLOLLGLOCOLOLOLOOCOCOGLGOGLOGOCOOOGOGOOGC@GLTOTTOOMTTOTTOOGCGC@F===F==2=//=/===///./..//////.///////////==F@F///.././.===@=@@=/./(.  *./(../* (..*/.(..( (/.*..(.(..*.(.*..(.(.*./*/./*./(//./==/*========/.*///8/=*=/8//=*//*///*/*/*//*/*=/8//*=/=/=8/=/8=/==/=========@=======@===@============/=================@F=@@=//*//*/////.*.*..(.((.((.(((.*(*(/**/(**/*/*==8=@@@@@@@@@@@@@@@CCCCCGCLCLLOLOLMOMOMOMMMTMOTTMTSTVTVSVVVVVVVXYVYYVYXYYYYXYYYYYXYYYXYVYVYVYVVVVVVVVTVVTVTVTTTTMTTOTMOTOOMOTOOOTOOOMOOOOOGGGGGGGGGGGFG@GFG@FFFF@FF=F=F=2F2=2=2/2/2/2/2/2.2/2.2.2.2/1/1.2/1/2.2./1//1.1.1.1.....1......./.... .. ...... .(      (((((((/*(*/*/***/8*=8*=*8*8/888=888=98@98@9@9@9@@:@:@C9CC@CCCCCCLCLGLCLCOCLCGLCLOCOLLLOLOCOCOCOLGLOLOOLGOCGOGLOOOLGOOGOOGOO@GFGOOMTOOOMOTOTOTOCGCG@F=F=======/=/F==//////.////./////////////=F@@@=////////==@@@@@@=/=/(.( //(/.((//(..(//(/ ((.(.*.(.(/(.(..(/.(.(..*./*./*//././.*//==/=========/*.=*/=*/=*=/8///8/=*//*/*/*/*/8/*//8//*/==/=8==/===/=========@===@===========@==@======================@=@=@@F@///*/////*/*.*/(/(((.((.(.(((/(/**/**/***/8/8/==@@@@@@C@@@@@@@C@CCCLCLCLGLOLLOLMOMMMMOMTMTMTSTTSVTVVTVVVVVYYXY[Y[Y[Y[Y[YY[YY[Y[Y[[Y[Y[Y[YYYYVVVVVVVTVTTTTTTTTTMTOTTOMOOOOMOOOOMOOOOOOOOGGGGGGGGGG@GFGFFFGF@FFF=FF=2F=2=2=2/2/2/2/2/2/2.2/1/1/2.2/1/2.2.2/1/1/1/1/2../....1......./...... .. ..... (.      ((.((/**/***/***=*8*8=8=88/88*=88=898@998@9@@9C9@:@CC9CCC@CCCCCLCLCLOCLCOCLCOCLOLOLOLLGLCOCOCOLGLOLOOLGOCOGOLOOOOLOOOOGOGCF@GLOTOMOOOTTTOTOOCGG@F@F@F=F==2======/=//////////.//////=/====@GCGCO=======/@C@CCCCCC===/==.(===/=(.*=/((.*/*.*.(.(.(.(/*/(/.*./*/.*./*./(.*/.*/.//*..////===/===@=@===//*===/=*==8====/=*/=*//*/=*/8/*/8/=/8/===8=====@==@@=@=@=@@@@@@C@@@@@@@@@@@@@@@C@C@@@@@@@@@@@@@@@@@@@@@F@CG@GCC/////*/*//(//*.*//(/((.((.((.**/*=*8/8*=*=====8=@=@@CC@@C@@@@C@C@CCLCLCOLLLLOLOMOMMTMOMMTMTTMTTSTVVSVVVVVVXY[Y[Y[[[[[Y[Y[Y[[[Y[[[[[[[[[[Y[YYYYYVVVVTVTVTVTTTTTTTMTOMOOOTOOOOOMOOOOOOOOOGOGGGGGGGG@GGFG@GFFFFF@FF=FF=2=F/2/2=2//2/2/2/2/2.2/1/1/2.2.2./1/1/1//1.2.2.2.1.1..... 1........... . ... ....     (((((/*(*.***/***=*8/8*8=8*8=*=88=889=99@9@9@9C@9C@C:@CCCC@CLCCLGLCLGLCLCGLCOLLGLOLOCLGCOCOCOCOLOOLOOCOOGLOOOOOOOOOOOOGCF@GGOMOOOOOTTMTOTGGCG@G@F@=@F=F===2==F=/=//=///=///.////==/=F@=FLLGLOC@@=@@===COCOLOLLO=@====//@@===*/==*/(/=/*//(.(.(.*/=///*///*///*///*////*////////////==@@=@@@@@@@@==/=======8=====8====/===8/8==*==8===/==8===@@@@@@@@@@@@@@@@@@CCC@C@@@@@@@@C@CC@CCCCCC@@@@C@C@CCGCGCG@G@GC@GCGCOC//////*/(//(/*//*/*.*.(((.((.(***/*=*/8/*/8/====@@@@@C@@@C@C@CCCCLCOLLOLOLOLMOMMOMTMTMTTMTTSTSTVTVTVVVVVVVVYXYYYYYY[YY[YYYYY[Y[Y[[Y[YY[YYYYYYVVVTVVTVTVTTTTTTMTTOTOOMOOOOOOOOOOOOOOOOGOGGOGGGGGGGGFGFF@FF@FFF=FF=FF/F2=2=2/2=2/2/2/1/2/1/2.1/1/2.2/1./1/2.1//1/./1.1.....1............. .. . . (. ( .   ((.((*.**(/(**/**=*88=*8*=*8*88/88=89@89@9@:@9C@:@C@:@CCCCCCCLCLGLCLGCGLCOCOLOLCLOCOCOCOCOCOLGLGOLOOOLOOOOOOOOOOOOOOOCF@FCOOOOLGOMTTOTTOGCGGC@F@F@F@@=F=2====/=/=/////=/./////////====@GCCGC=====@==@CCCGCCGL====@=/*@@====(/=*.(/*//*/((.(.(./*/(/./*./(/.*..(/.(.*./(//*//./*///==========@===/*====/8//8/=*=/8//*/*//*//*/*/*//*/8//=8=======@====@=@@=@@=@@@@@@@@=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@C@@@@@@@CCCG/////*/*/(/*/(/*//*/***.(((.((/**/8/*=*=*==/==/==@@@C@CCCCCCCCCCLGLLOLLOMOMOMLOMMTMTMTMTSTMTTSTSVTVVVVVVVVVVYYXYYYXYYYYYXYYXYYYYYYYYYYVVYVYVVVVVVTVVTVTTVTTTTTTTMTTOOOOOOOOOOOOGOOGOGOGGGGOGGGGGGG@GFGFGFF@F=FF=F=F2=2=2/2=2/2/2/2//2.2/1/2.2/1/2.//1/.2.2/1/.1.1/../1...... .......... . . .. ( .(   (((((.**(*/**/**8*8*=8*8*8*8*88=88=989@9@9@@:@@:@C@C@CCCCCLCCOCLGCLCCOCLOCOLGLOCLGCOCLGLGLOLGLOOLOOOOOOGOOOOMOMOOOLF@@GOOLOOGCOTTTTTOOGCG@F@F@@F@F@F========/=/==/===///.///=//=====@@@=@=//=//=/=@@@@@=@@@/=(=//(.==*//(.(/(.(/*/*.(.(( (/./(/.*/.*/.*./(/.*..*./*/./.*/.//.//==@====@=====//===*=/=*//8//*=/*=//*//*=*=*=/8/=*//*///*=/=========/====/===========/=========@=======/======@==============@@//=//**.*.*/.*/*//*/*/**/(((.((.**/**=*=/=8/=/===@=@@CCCCCCCCCCLCLLOMLOLMOMLMOMTMOTMTSTTMTTMTTTSTVVSVVVVVVVXYYYXYY[YY[YYY[YYYYYYXYYYVYVVVVVVVVVTVTVVTVTTVTTSTTTTTTMOTOMOOOOOOGOOGOGOGGGGGGOGGGGGGGGFG@FGF@FFF=FF=FF=2=2=2=2/2//2/2/2//2/1/2/1/1/2/1/1/1./1./1.2/1.1/1..1...1..0...... ... . . ( ( (   ((.(((/((*(**/**=*8**=*8*8**88*8=88=98@@9@9@C9C@9C@CCCCCCCGLCCOCCLCCLGCLGLCOCLGCOCGLOCOLOCOLGOLOOOOOLOOOOOOOOOTOO@G@GGLOOGCGOMTTTTOOGGCG@F@@F@@F@F=F==F====/===/===///////=//====/=@F@=@///*///==@@=@=@=@=///=*.*/==*//((/(.(.*/*./*.(.(.(/*/.*//*/.*//(/.*./(//*./*////(/.////@===@====@==///=====*/=*//*=/*/=*//*/*//*/8/*/=*/=/8/=*/=/==/====/==/=/===/==========/======@==@=======/====================== \ No newline at end of file diff --git a/data/icons/recording.raw b/data/icons/recording.raw new file mode 100644 index 0000000000000000000000000000000000000000..f5123ea2c4949cb7f40eeadf79c4b675d650ea77 GIT binary patch literal 293 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�#6%k+5(wMIRd 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 literal 0 HcmV?d00001 diff --git a/data/icons/scan.pal b/data/icons/scan.pal new file mode 100644 index 000000000..bd5edc061 --- /dev/null +++ b/data/icons/scan.pal @@ -0,0 +1 @@ +e+AH,]GH&E]ba&fFL pir5#:G`6PP#o$06mD)b2.O@|'@@O,^2#V`}zkŽzk]O@2ˆ#]O@2##S##>#668‰z$1—59Vk#k]#O#}#]&7ƒ’˜³%2bLSj5«@#O#k2Ž2r#]2#@(M‘3M{#3O G›#O2]kž5pEj•Odb³"Ci#@#2O#2H}#2?7]*]†U‘¶5Oh˜„¾,k‘E~ #22@ª¯²¼ÄÇc…,¤+z–e¨Àf˜¨4@~œ'ªÊE™­,™«Oz‚˜ª{‡R‰22##2YY##32%wPw}zAoS‘¨•]]}˜}12#ª¨sÝÛËÛ΋ܷHÛ»k¼™H·™jÙE]E"Å«‡Ò†,o7¸€?H4סcbBªˆb–|`”Žˆ×„CćZ¶g4Ôb$ªN†C+#K#TLK}KG***,,*,,**,***,****************************)*************************************************,**31*,,............,,,.,,,,,***,***,*,,,*,,,,,,,,,,.,..............,,,******************,*,,,,,,,,***1***************,,*,*,,,,,*,,,,,,,,,*,,,,*.,.,....,.,,,,,,.,,.. \ No newline at end of file diff --git a/data/icons/scan.raw b/data/icons/scan.raw new file mode 100644 index 000000000..d4894dee8 --- /dev/null +++ b/data/icons/scan.raw @@ -0,0 +1 @@ +Ð@ÿjwjwjwzcziNNNiixNxQQQxQxQQz|PQxIxQQNQNNNNNNiN…N…ziwwwwwjwjwwwjwjdwjwjwjwwwwwwjjjjjPjLjLa‡¤¤¤µVQNPPLzNNNjLLNNLNLLLNNNNNNNNNNNNLP8P8NNNNNzNzNzNNNNiNNNNNNNNN2NNNP.<.22282NINN2QQNQNNNNNNziwjjjjjwjwwjjjjjjjojwdoddodwojojojojowjwwjwjjwjwjwjwjwjwjowwodododdddwddddjjjjjjjjjziN…NNi…NxxzzzQxxQxxQxxxQQxQxQxxQQxNxxxxxxixxxNxiNixQixxiNizNiQiiQixNzzii……N…N…Ni…N…N…i…i…i…i…i…zwzcw…wjwwwwwcimtaVjicjwwwjjmdjcjjjwwjjjj…c…wj…i…iiiiiixiixiiiiiii…iiiiii…iwwwwwwwwowdododdodododoowowojwwwwwjwwwwwwjwwjwjjjdjjcjVtq_µt|LjLjmmjjwjjjjwjwjjjjwjjojdddjddddjjjjjjjjjjjjjjjjjwjjj…N…NzNzNzLzLPLPLPjNj…jwjjjjwjwwwwwjwjjjjjjwjwj…jjjjjzN…zLzLjjjwwwwijwiijmmmdjcjjjwjjwjjwjjwjjwLjjjwjjjwNjjjjjPjPjjjjjjjjjjjjwjjjjLjNjNwzLzzzNNNzNzNzNNNNNNNNNiNiNzNNNQ%%wLwjwiwzczwN…NiNiQixQQxQxQNPPzQQIxQxNQQNQ…N…iN…N…ziw…wwwjwjcwwdwjwwjwjwjwjwjwwjjjjjPjLNa_¤¤ˆµVQNPjzNijNjzLLNNLNLN5GNGNNNNNNNN2LP8P2NNNNjzNzNzNziNNNNNNNN2NNNNN2P(.82222NQiQ2NQQixzNNzNjzijjwjwLwLwwwjjjjdwowdjodjodwojojojojowjwwLwjjwwjjwLwjwdwwowdojodydojdodddwjjjjLLwNwziNz…Ni…xQzPNQIxQxQxQxxxxQxQxQxxxQxNxQixxxxxixxxNixxzQiiiNizxzxNiQiNN…L…c……i…N…Ni…N…N…N…izi…N…N…iwzcL…wjiwwL…imaa|jizwjwijjmdjjwjwjwwjjwciwNwi…ii…iiixixiixiii…i…i…ii…i…ii…w…c…cwwdowjojoydojodwojojojodwjwwwjwwjwjwjwwjwdjjjjLdVtµ_qamLLjjmmjiwLwjwLwNwwjojwjwdŽwojwjjdjPdjjLwjjjjjLwjwjjiwNj…N…zi…N…zLzLz2PNjziL…zwjwjwwjwjcjcjwcjwwjjwNwL……jzjzzczwLwLjjwwwiwwjwLjmmmjLjwjwjwjjwjjwjjwjjcjjjjjjjjcjjPLjLPjPjjjjzwjjjjcjcwL…jiwNwNwzcNjiiiNNNzNNšzNNNNizzx˜iNiNQNjwwwjwwj…L…iN…NiiizNxxxNQxxxQQšPPzQMMxQNzQNxNxN…iN…iwNwL…wwcwjwjwdwojwwjwwjwwjwjwjLjjPPPNVq¤¤ˆ-VNNPjLLziLNLLNiLNLLLNNNNNiGNNNNNLNP8PNPNNzNLzzNNzNziNzNPNNNNNNNNNNPP<.25N2NGQN,QQNQNxNNNNzNj…jjjjwjwjjLwjjjwjjdwododojojojojowwjwojjwjLwjwcjwjwwjwdwwwdddjdwyddwdwdojjwjjjjjwN…i…NNizNxxQzzQIxxQxQxQIMMxQNQNQQxxxxixxQxxxNxxxNiixQxzxziiiiQxNxNiQ…Nii…jiiiNiiziN…iii…iii…ii…izwzc……jiwjwjwL…LjjLzjcjwwijdmjjcwjww…cjczwi…izwii…i…ixixixxixixixiiiiii…iiwzcwjwwjwowdod€dwoyoyoyd€oo†odwdwwjwcjwwwwwwwjwjwdjdjLjOaqa|iLjmmjwjijwjjjjwLjjjjowwwjojwwdjjdjjjjjjjjjjjjLwLwijjwzLzjizNizLzjPPLLPNLi…jwNjjwjjwjwjcwwjjcjjj…jwj…jLwLjjzijijjjjLwwwjwiizymmjciwjjcjcjcwLwcjcjcjwjjdjjLwjjjLLjPjjjjjjjjjLjLzjzjwjzjcjjjwzczLzjjNxIINNzzNNNzNzNzNizxxxNNzNjLwzcw…wwwzwwL…i…NzixIxNQQxMIMQP.|NQIIxQxQxQiNiNN…N…Nww…wwiwwwwwdwwjwwjdwjwwdwdjjLjjPPPdPVa__µa|2PjLNzzLzNjNLLNLNLNLNNNNNNixGNNNN8N8NNNNNNzNzNzNzNzNNLzN2NNNNQN2N28.V.2NNNQNxIQNQNQNNzNzNwzNL…wjjjwjwwjjwjddwowddjojojojojdwjowowjwjjwjwwjwjjcjwjwwwoydydodddodwyoowwjjLjcjj……izxz…NixxxzPNxQNxIxQQxMIQQQQQQxQxQxxxQixxxxxxxxNxxxNzNixixN˜ixNzQzNiiijw…ix…iziN…izizii…i…N…izwzc…jwiwjwwjcwjiIxijjjwwcwjymjjcjwwwcj…wjiwiwiii…iiiiiixixixiiiiiii…ii…ii……cw…wwjwjwdojojoydoddojodwyodwdwwjwwjwwwjwjwjwwjjjjjjjjjVataVjiLmmjcwijwLjwjwjjwjojwjwjowwwjwjjjjjjjjcjjjLwjzj……NwzNziziziN…NzNPNPNPNLzizczwjczLwjjjLwjwjwjjwcwj…j…L…zzjjcjwjjjLjwwwjwzcjdmmjwiijjcwwjwjjwjjwjwjwjLjmmjwjjjjjjLjjLjjjjjjLjjjjLwjwNwcwj…L…LzczjNLNNNQQzNNLjNNzNzNzšzQxxizzQwjjwwwjw…c…wN……Ni…iiNxixNxIQQxQxQP.|zQMIxQNxišNzii…i…wzwjww…wjwjwwjwwdwwjwojjdwjjjjjjOdPPPVVaaPPPjNjjjcjiLNLNLNLLNLNNLNLNiNNNNNNPLNNNNNNzzNjNzNNzNizNzNNzNNNNQN82828<//00/00000000000<ƒ‘„s„„„„„–‘t;9966666O<///0//////6///++4s„‘„„„„„„–s–‘t60966:888K8L8d8LLLLLLLddjKddcccdm``LLiy~„~~vvvv~s„lyKjyddydddddydyddyydddydddmjNyv~‰{~ƒ‰vyLLjwwwiN…w…cwciiiiiiiGIivƒƒvvvƒ~wIMIGIMIx……Nj†OPLiiIIm–Ÿ£¾GGi……cwcw…cccccwcwcwcwcwcwcwcwdwLwLwcLjdKwija_ˆˆ-VNLLjjdOLjLwLwjcjcwLwjcjjLLjLjLjLoodjdodjcjojjwjcwjwdwdjdjwdwwwwwwwdjdjdwdGw}‰•••‰pcLjoLdjdjowjdjdwodmp€}}}}Lor•pL8OddddddddddKocwowoojowwdowocoj[wwwwwwwwcwwjwjjjjjwjjjNiwvŠ„ƒ~ƒŠ„„vjNjmmmmmmmmmOmjmmdjNNmƒƒŠ‹‹ƒ„‹Š„„viIjjciicciiiiiiiiiiiiiiiiiiiiiiLjjjd|//4//////0;00:0:0;6/<0+lŠŠ„Šƒ„ƒs‹‹„„„v95LLLLLLLLLLLddddddOOOOOddddddocwwwcGov‹‹{v{vp~ƒƒ~yjdmdyjdyddowwcccccicccccccGcwv•‰••{wGccwccwcccwcLc[LjwwwjKNj{~~v{‰~{}d¾´³³´¾4;/;;;:0;;3474/;00:0::6;;;3600:0;0;0;;000;/4744/;;6::9::9Oƒ’ŠŠŠŠŠ„ŠƒŠŠŠƒpGGLLcccFGFcccLdOm`m]ddddccccc[cccccGG}•‹‹•‹vyLdod[[[o[o[cc[c[ccKLow[oocFop{‰{{‰{{€KLo]]O]pXmp`ppp]dodd[ccc}‰•‰‰••‰}FcccocFGF}•••‰}KFjddOyddddddddddwwcwccccccccccwccccccccccccocw[w[[w[wdwKKoKddd[cj[KdjKddddojoojo[j[wKwoK[Loddo[w[o[o[c[cKoc[o[[d[j[co[ccLK[[woo[EEO-®­¸®¯¯¯¶ª³“u~«»¯¯³·±¦¦¥µJDYodo[[o€€}}€o[o{‹Šlsa`FFFO`^+2UQ&2v6nk?X5a)8OX`7r)D-?)+SDSIkMy;-D23b?L$zqFP)R4_$Y*?(bnlbY#GKR_ORU#k3GH~*fqP}yj#hi}8Y|Q`mE7{Q}GIUts_M literal 0 HcmV?d00001 diff --git a/data/icons/shell.raw b/data/icons/shell.raw new file mode 100644 index 0000000000000000000000000000000000000000..3387e59a2e76b86f11c0600f9f2e053745c2612e GIT binary patch literal 293 zcma)%y9vNB3`Dg+dT**KWXx1TW2g!j{Y(mD$W`y2gs=w literal 0 HcmV?d00001 diff --git a/data/icons/shutdown.jpg b/data/icons/shutdown.jpg new file mode 100644 index 0000000000000000000000000000000000000000..92eeac03933ac4a872308c3b6b1c64546adf1c99 GIT binary patch literal 262311 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 z zD@3vY@H}sr^oLaa#bpY+;VlFwFwXvw#Q?vU6P1|#xATkH5(*^slYn`Fmy|h6=mBya z`|Y|Fuf-GkG9=W60)jKXS2j8CJG=m<(%06>QJGIQdr2-_o%(LyZ{l=eQszjQWz0%~ zK_qa`&p+X!CPdmwaWx7KraIs0sfPNrfTF}+$91#~-4pPFM^`ZtkWXwlDX$_Jc#cs! zNsHnYNLrUO=SoxxikZg(_AUIwiV5nSCUB^kFtgYww=DklhJzvTm zN}{QeDQgoCc1_6CKMaqaM)4A2)PgdX72I2!U*9-sa%CY)DL_HY;BLP!56m{3I&uo- zQ~vT&x~V9 zF$kOj08k~HK1ZR-(E@ToQCTI}mJ}Wvy@*8lxg~;t0g(aLUb&_Ik$2 zxY|(#wAYho$5?M7ol2ds6_vJnS~HZ11P(?-h;a?$J+*K>aA=l^ zi5D#VEqlQpFZUl0VHn4xxp?wA6biFx_-IK7DH^}k;lvQ1dBfN783lq`{dmNwb5H|4 z!4}?W4dqbN%zfi1Ac7=IP)T#{{{VPQ5J3b@OzCU`*OU-J1Q4#d9DE>;Q<&yG;lrKh z%)?<~4(L<7;p_N`5+}=L2QK4CLcuJ6g#D2st-9)20K?r3hL zOh6$cXf|Q($b0$2+${!V;ue=~xZkbu)U$`*6_dhcp=+mQB$v2-+VAs=bpj=(P}(Kz z3BJ4hbLhnF8mY|7mR9>eT(pJ~B+E>o!R+TkE!E(VfG6#304^Ao*VpP=A3ssT@s`F- zMAWM(uqLCAJ~y;l<(Xl3StCct<$vP*jA1rfCM>8I$)7=;zMk;d%SlR97Bq1B?&Zuv zh?OvW#GR9Ha{;#-b)HZFR7hal@8j>*GsI;oQbjGNh=fK-$m0Pv^)+ z2(Oqfkp2(X)uK2g5wqSGIi>aM^OQ=^s+L=#XggQa*M3F4UOG^wr92`s(V5FRJX=lt z@9oz`d3Pwd4QKidJ$)iZMQpWx`(!R3*T?zJAZivR&v{8ZCc@U$yv5m{ArjOw)kX-mRcvbtU530Ks|=01@AqFk9O29?+XbIpnC^9^Mg)e2c$#t9Yx__pzFS>klIOJ>j? zKYlRE26U991|(5e1?a{sxO}u3djRAzYrei2b2;7N^tk~u3s@V!18$A^XBAo;K~m}z zma=W&Gy6o^@fk}~N+nA+AmsiVo%jKxL!x2}hqnV2Sga->El0DI)`|`RHY0c2x;@2J zu`Cd?W0USYe-T)!s2fs{tGi!*x3^Dz6F7kjlmga62W~xEEUlHIpNLf^$pCze0)xLl z4mNms2_&a;iuc8?%#ekoB#kI{8hZG|nb;x084?+T_;tb+!_2WYW^B?Dkl+%)x2G=% zg_k~8c0amyh-Q8IU#9S9bo8sJ3o_Q>Lgd?BFUK#mOdt{t>qU(0G^8Ygrb%$BQ%)Q` zh_F;*g)E9`iXl1!{AK6DByj54lZSadN+jCl+t1tW6=^tp*#x_+9uI%Fo|lVO6)H%Q z1kzJ8YjYmH!p0Pka{>;QHuldHIN4Zq$WWFu9M=JhF|(fvJvYUIM4t6QY@(ZQY`1j# z@{2thrAU@k7D;P?yzu&4NN6RSqPUkJ0V1*u_Eur*KKpwDbuFZPgK>Nuz=(Z0Ob1lmXZe0ASfh0M^1m6LeQWc%`{>A?)*nE*t|5Er7L{O zW-Lu8?7_GbyPRr{?qbljp+#ga!#2_PTEfkrj5?7qEweSs#hAMen~#Ir+NQ%N%s|>& zNhQeKU)RCILey0h6%MMBWhgle8OX`~3)pEB0CAkF+`!L0!u`js%Boxmix%=v?8)}o}KlH+MW6O(3QomQkGTcia{*$#BeP5N|1G{R?2RQ4+}!8r_> z_}h+Iodh~7YP>Z&2tZPc2CEa}$B!L%#hFD2#ij{iD%vF6UgYk+qboJ9oKE5Lu|sl2tGNEt$Rc6YWwKb zr*p-0(B+bDkmS<0BCSm|5g|TQq%Y+8gXROa?7ZzBrbgIwCHEBCdJ`^AGi>baIRQa)kR}61 zMqu?cG=HIEA3rf%N`4_*KBjuswN99&l`Od-M4O)<%M%g1YH(6XV{+}}`t_`PP^9;y zBsfL1yNlR2A6(%StP5v*-!BO8Ocp+7Cl`nqg;Xg;CEfMp;F`M{czvpgNs)w-B&R6K zkj%^gU=Jqp55-`zqhhDN_v4g1l2mb&wVmm5Y zBzGuu?Jy;UtWEs*UOCo*VQUU{SnWQx@v!Oc(Xx9lk5$murF8WMM#Be5+1dkr!*!j^ zdS=SSqbdu6?Y@HAmuhbAjE6{Y^C!!gF#iBBNx35B!)_;iK#uL~Phrq1CrqfUlA|F^ z65zWYIqudwKjG)Y+&dJeO!Bk^AOo_NyK?;fBj)#FJ558}Wq`Ep{_sc{KUdZ} zDGEwUso0UFhU3GS=0tdoJzZ47 z%#!4GMQNyaxZHm*SfxbCa~Fo8X#fJtR%N%G{5XRe!hRQ0y2*>3IRa376)-KR-u_}Y z9xvJS)Fm|{U=vnBV18`D$a!U+?KNj?=)cp~DGe|!XiKFef&T!g@ohM^ab+TWpr8r) zK_Jz)>ARQKE;a@xOxdZGEmcwoSSbX8HAyCq!*#6{O3HM|pS4QU2-Paq{!P~v@OAf# znkrMWZ+$`MZUcGA!G=hbxd954iHlUt6zm`C%j*&nWUVs;yR7`l$%)VL76@8YdoNLD zVNm;l(l%3iw8>B;D4IPp1{~N91J124Pc~$g%|xZB%TOS>um$tJ?|WJtCYUO0uvV5* zZXbr{*Iy1%k|<{PWoaSE%I-#u&xf2Wsv|B)owT7eB!G*tEvDgqnW6L$9q9i4zV)Ea zlQB%72~|ZL@ip@lw-~~ni87>5oE6?FGk^(}#aZcWwQnf4;V73rnxLv&@T4ZrE-YJf z^NMa9nuI232V|{VUop0}55LMUy_u*=B-N&^NCinC0-!gGWH*n#i_V{7BJD*qr6{X0 z4N-mU2Mu`hc#c%*a`q6ZidXj4X^cp0f=3?{^-7MhGh|JYiG&iO2Gj4zUAbu79yUfB z*t%?5bu~&ROCf1cDB1*E>-pexiHuL-^oj}!iPGf|xnQgrLCGLHzt{5)B5Dt1>I-}K zrus&0J7Z>r964IV@$&;Lgas8B1ds(-F^y?OO^H&3uEX4$fX4V3?9n7DMKC0U&AsVoeAj)S4Q`(3UAFlA5`H zG{qqm+~uodJ(stLecA5g2WV?3r>!EKO+r8k+Dd*vd@lR<7-fJ7l#^3OT}Gij zc_Bc9etBK@^Noj9*~)I_nteosr85>kwX_9^7yG}II7&)bH+U!6{jCmSdW}Bv8kk@rNpRdP>Eq6k_c7UZazf);y1JMjXhwWQz=L*O9e4t2w*_J6AB6x7oY^S z7>4y>>_b!bjvuI0$!a~9%|olxLU?5*SwHE$*2qNf`h=aJkWRMr*Cvgxg7!j0 z$w;44w5$8K+8~y;^Um4wu`tw_4BC~{RZ>qZfMz)?OI^8c-==Wm4aF-AwbS>DGbKzc zl;&9T;lGCX?XC?Rhi0XlS8er-X3@q^#UXPr>Pruq-6S1L9xrP=TgDShhgT|Wq5=}6 zrHAdmrn~zn>E>iIBcpe`i#HqRK*)cjBC_4yT3F)8!}QkHX*W$Cs|(%vlP?dpvg5B& zF4fuHKj=Tg1$SQb6Y3g9z>$oT8&FF<4$<=hFHy8-p!8rguWNgV9BoDpo`&`B)W zecaTnsG4rp#4XOCH-ISDe5H}O9#UKe^{&1W!ZH~EFx$b+afSz8pw<5H<1Dbbh2J zPx*F8lcP$1{C1IVknBDrsSs9SU0P!#4({`9C+~d`a)KIuH~y3{>piq|ea8of!=x&xi57(pr7 zOZ1i*0I+Kd9vE1NafCXX3c@m1JJdQLE8PoLiMnax+!cbI9ZmPVn8IG79)BHJKZ#?N z##2(+`zo5*G@+?Yupit<|F#qQf4}YvK?L_mXY>)R{CbZjM8JRO(hE@W;STj4zVxXk z1_r9{^#?PTLW{S$4$j*)Ei^9sr8M^ z-!d6*T$$xk4DnGA`rOQUi%TOU4$xT7F$7@@1 zn)*+gO(m!_Fx95!g0f$(;HXuRy3Abn1IYqTbb}cp>8*sJsj^1}iG6RY5g|_Oo)qo&n`kHW9RHZor#wcu_{G}U1S#L`?4$|JdxYtW^ZkiRm47Vj2+A@NH+=C zyDwzc2Uy;YN}ke8TqGHmu79S_0h=23;~`73f!xmJEWh3{;dmgVcS@lD0Wy!THR^+e znJ05QitRB7bqiz$C0)@!4KF+4K6}|Pca;lJ>+;~9K}Pen{DT=sPyak=a~W951Q1a`il(uPVrbr zu3xmJXsKoSwv5zq{3~4>`Htx~IOVgX$72huu8mR6<${)lR^+4Kx%wI)Nw-=!Y2HUa z9QmKpI2EZu=K=tn6C(DdA?{|-m^{Bu7^Y(zI;lf2Cocn~?xyVUX4*wEpi=p5_oimr z;I^5*Pse^t)U12>WVF#~3*t+2j118fWMoQK^m++B>8CNj0aut5GHNfJUC!t327I4~ zXDWBGF~oM;48_E=?CA5SLb2ufv(ZDeo68P7Q}6Gr-W7&yhJkR>BrLBjr%0)Hd$p@PGWbMapHLIza%TlnLz_c|n2RziYLy1srIao^0 z6Q>yY@1}T8=dPvxG>C_aOan8@dP%O@j6Bhv9jdU(Q*xyOk@g(XWxR4d{`gnbNh0?V zjX9ra3Om~)SBLOsj!SW|@+gsqq5Rw(r?dJR^d*h7YA!Z0R)6`N&I79GD-K%hm>+=Q z=;jnpZ`SQ;q7b*fjFcmnF})*^vLB7uWLvzZq+|tsx((Ez_b#5sh--0nXOmUrfwQU+ zZ#u9F$Ajrq;Fe(Lyq%CQfA+U5N7B^Lb}OkEh=L>RE@^{<`m3Frf)d|5l0bbhjj#on z+=RGv_e=AB^at|pY%c*Gy%gq$+GT^|-P!-NIQOW?u%GN6{93eHSYKvl*WpF%7TJ0H zF#z)y*`@xD_2&HA)!TqNL(~B)auks*(eh9!&dTGA=?4#)gF2o81zol(gPjWI;iRY+ zo<%R;j}tm`q?)DuSyLI^{z~H`1fB^?-M3@a210wROs-#5U8NwIJMW&ah1!GWo_+@o zPtBBIqysAY<_gjrKyquZ5i-~NV6m`WO%N$4Ok1h;`B2*g4wSkJaPi`x)! zgFA|vAtP}j=dkgAE51o~i}(NKjv8aS5>oW)!xUm>Q3*-rVzTeM>_)%WqzgTh{Q{iZ znhQOTH*(p3NjV*Y-PgiATR=gC5!EJD;RpAxnBH;QagG$idUA=y97xdT=Q@QJqQ8``k^h55m||eS)Eqgljp_ zrc-UQ@dr7ZDO|$(Z>mI{pdhV6uveVz3(o$XFVZu*PXY!~L^+o->b}Mg;D1+5{_Tjz zP&P-@rKzmA4})A?T=q2Pt;Bhhl+?fdQ0ux3>NhpEbkOaB$%zDb|1-JE`nt&qVv>da z2mr9|WqrF-E;PvddI$&C$GhA6*uPq;3DT}2Z0%jmq2L+;hj!4C=5eX!+-5t0GLwLL z_Xh&T@+2p7@x&{so0zBbTa#ky&H=0`ek+~69c%YcBiiiq2Ft})td5mx$f0MH9SXTS zp0AKIbQIaY_+N(ae<^B3QRDw_sG>+hvY_AY?(lBDWAYCSQQ1c>7G*N!H~ z_Y^+G(jJ%ja&b)0{DDHUTD>SG3n!1|(?1IR{61C@!nWeDyPtiZ_zBSF3YSPwWRVU6 zkT58f`y2Jebx%*AwCOGn6;_nc97I9sy&Um1NDK49zTH&R7(+XEw%r@9)W%v+Jn+jy zH2M8Px~ZA8hh%R9U&59_ks@X|RlgG3*he~VO-xcRxAHFP`AI!WU)PTJ2K!fQ7Z>|$9Z%z3)yZ0O zX+FIAeHOPmaRE(1wfNhRY3_7B5V3vLopOz1q{=qn(yNlRwe5;|n)OD4ig%V9D1UMD z(sI0nQdittiM zw%n+G^+4B}f*bW~fbcO97Uk|@!Zj2D|My~cGrj*baT2@zhrSw=I3ZRMZ9iu;a{TIl zUwhwZG<-&dRI(@QBA9}JHAj#rdEds3^n71^kVZ@_tC=?cX1?aRtj_6tl)?XgN>raC z97ZNKSfJl{-GBWG1qQKtudl(wV(1PMdS)g^m3pHi;NDH8w_ud<>VeMg{m}yB$~TRD zn<6)j6Q<|G+924Hl!-TOm|~EB?jyY?q00Au-%eajJoKdHf{wqo+3Pu_gn7esA#kfN z!GABpAj-xiAgI?D-RCXt1-|{Cm2@bw+*pY}f+U8P#L6thjmQwKvVGMzVkI{I(Y0K+ zC5zH_Z8rU?n!603xhIm3d5pM0Xg7d!%S7_hu%QOB5zIBp^gYOR+BGqo67vU&*!j>B zy!P>Q^OzPgyCT@`eI#?%bvix8HoswS{c)kbM{F~h^<|dN@1DX^^?tltR%xM$#l|dI zwNPK5u%yc#Jo2!>ssF=zVHN0Jqm8B=Aw$7lCcW5e=* z=m5<(fkd~jgMCiW<&=(g-@Hp&%DQ()BBI6jgZ)Rch*5k(f8{QJ$2j_=Nx=9&+VC6& zPm2k!|M)Vd;xP!Mta+qpzI(BGpWAbkK{oqH$8hJLmDjEdT9<+4lo;qgO1u)z);j}r zwKy+u;QXYiLP#gmI?T-I84ekKTYEllzN}I9?3>=>bpsi>@TcySn^`Ktdn)^^DTTLB zjG9he%4Vjg0bqU;s=KGuK{xM9q5iR7*nb z8{&|jbCgkBDdmVXr}nVd(H+{0k6Gh!Owd9w`Hhmz*?d)=eM`c6A3FT}^DE)p!CO6V z-)c+Va{mWLbc@pMVBA!R1$ispnZIq*>&_F$QH|7Are-t=v0l=-$j?nW@pTc?+?33R z9kC;J`qHUJV4o=*I-*zh&HL*`Vyxs`{{Axo7byoLo0h;eX5h$O;XIqQm9~LI<3Ogl zk7xI-2jh##vx}`~_FzuJmTTg-s#*D|3e;H0Q%{+X?m0d*Pip=woC=I*3Uv|1i~tF> z|J`pzmI={yNfQvLl$?$Jnr8LN$IJW7d6!E3Dz6SM;T6&`812I3vjcrZjF)N~BgO(SMCO%2v%gZxWpypm%cS0#?PG-8FF8v%z zv~XZxEM=*64-N9Ew)r*GIH~H$0D!-h2iI-fd1yG<(y9808)p0Og#Q_M>h4lFU6^^O zh`M6F$ABKCT|6UjAdbSd{(2ciXO-^4QLefP*3t zIFTBAy<$Vt@h;r1XfV*vJ+W6d-YkVpqVv}eY4^Hh`QVf8{^{{lotA9R;ubJzJs?x0 z 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 literal 0 HcmV?d00001 diff --git a/data/icons/shutdown.raw b/data/icons/shutdown.raw new file mode 100644 index 000000000..1f81d5055 --- /dev/null +++ b/data/icons/shutdown.raw @@ -0,0 +1,4 @@ +Ð@ÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33333333333333333333333333þ3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2 2 2 2 2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22'2'''''''''''''''''''''''''''2'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''''*'***************************'*''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''**)))))))))))))))))))))))))))))))*)**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**))111111111111111111111111111)1)1))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)110#0000000000000000000000000000011))**22þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'*)1100((((((((((((&(((&(((((((((((&((00))*' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ22**110(//././//////.///.///.///////././/00))*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '*))0(//...//////&/&/&/&/&/&///&//./.../(11**' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'))10/....////&&(/&&(/(/(/&&(&&&(//.....(01)*'23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '*)1((...//((0(0(0("("("("("(0(0((&/..../01)*'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'))00/.../((00000"0"""""""0"00000(0//.../(11**23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*11(&.//((001#1##############1#100(/.//00)*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'))00///((001#1#1#####1#10#0(//.((11**23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**)1((//&001#11111111#0((///00))'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '*))00///((001111#100(/./(11**23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**11((/&(00111))1#10(///00))'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))00///((##11#00&//((11**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**11((/&(00#1  1##((///00))'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '*))00///((#   #""(&./(11**22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2**11((/((0"1  ##"(///00))'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 3 2'*)100///((# ïïïïï#""&&/((11**'2 3 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2 2'**11((/&(0"# ïïßïßïï #((///00))''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2222''''''''**))0(///((" ½ßßߥßßïï ""&&./(11)*''''''''2'22 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'2''*'***'*'*'*)11((/((""# ïïßߥ¹¥ßß #"(/&/00))**'*'*'***''''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''*'**)*)))))*)***)100///((" ïïßߥ¥…¥¥ßï"",//&(01))***)))))))****''22 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''*'**))))11111)1))*))11((/+&""ïߥ¥…¥…ßßïá ((///001)*)*))11111)1))****''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 22''****))1)11010000011)))110(///(($ ï便………¥…ßßá""++//(01)))1100000001111))*)**''22 2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**))))110100((&(&0011)))11(&/&("" ßߥ¥………¥¥ïï #"(/&/0011)1)10((&((00001111))****''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 3 22''**))1)110000((//..../(011)110(///(," ïߥ¥……………¥ßï "",+//(01)11#0(/..../(/((001011))*)**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''**))))110100((////....../0011)10((/+&""ßß…¥………¥¥ßï "(///(011)11(&.......//(&((001111))**'''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**))))1100((&(////./......./(001110(///(( ïߥ¥……>……¥ßï ""++//(001110(/.......././///((001111))**''22 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''**))111100((//////////./..../(011111(&/&("" áß¹¥…………¥¥ïï #"(/&/(011111(/.....//////////&&((0011))****'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''**))111100((////.////&/(/(//.../&001110(///(," ïߥ¥……………¥ßï "",+//(001110(.....//(/(////././/&&((0011))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''**))1100((&&////.//&&((0(0((//../(011110(&/+&""ßß……………¥¥ßï "(///(011110(/...((0(0((((////.////((0011))))**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**))110000////./././/((0(00000((//./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(//./&0000000((/&//./././/((0011))***'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''**))1100((/&//.&/&&((00101011100((///(0111#0(&/&("" áß¹¥…………¥¥ïï #"(/&/(0111#0(/./&001#1010100((&(&&/////((0011))****'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))11##((&///.///&((000000101110100&///(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((00101#1010000((&&//././/((0011))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))1100((//.////((((00##1#11111111#((///(0111#0(&/+&""ßß……………¥¥ßï "(///(011110(&/&&001111111#1#10#"0((&&/&.//&&0011)))*''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''))))0#00//././/&/((0"00####111111100&/./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(///((0011111###0#"0((((//././/((0011))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22'*)*)100((/////&&((0"00####11)111)11110(///(011100(&/&("" áß¹¥…………¥¥ïï #"(/&/(011100(&/&(001111)11)#11####"0(((&/&///&(0011))**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 ''**))1100((//.//&&((0"####111#100&///(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((0#111####"0""((//././/((#011))*'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''**))110(&////&/((""###1)111#10(///(0111#0(&/+&""ßß……………¥¥ßï "(///(011100(&//(001111))####"0((&&/&.//((0011))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))1100((//.//&/((0"###1100(/./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(///(0#011##"0""((//././/0011))**'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))1100((///&/((00#####)1##0(/&/(011100(&/&("" áß¹¥…………¥¥ïï #"(/&/(011100(&/&(001#11###00((&&/&///(011))**''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))1100//////&((0"####00(///(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((##1#"0""(&//.//((0011)*''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))110(/////((""#"#1 1##"(///(0111#0(&/+&""ßß……………¥¥ßï "(///(011100(&/&(00#1 ###"0((/&///((0011))''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''*)1100((//.//((""##  #"0(&./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(///((#  #"0((&&.////(011))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**)100((/&/&/((0"###   1##"(///(011100(&/&("" áß¹¥…………¥¥ïï #"(/&/(011100(&/&(001     1###00((/&/&&(011))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22'*))1100&/.//&&((0"# ïï #"0(///(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((# áïï ###"0((/////((#01)**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*))1100&//&/((0"### ïïïïïáá 1##((///(0111#0(&/+&""ßß……………¥¥ßï "(///(011100(&/&(0"# ïïïïïïï ##"0((&&///((01))**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))110(//.//(&""##  ïïßïß¹ßïïá #""(&./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(///((# ïïß¹ßïßïïáï #""((/&//./(0011**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22'*))110(///(&((00#1  áïï¹ïß½ßïßïï ###"(///(011100(&/&("" áß¹¥…………¥¥ïï #"(/&/(011100(&/&(001 ïïßßßïßïïïï ###"0((/&/&(0011**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))1#((///&(((0"# ïïß¹ßߥßßñßïï #"0(///(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((### ïïßßßßßßßßßïïï ##"0((//./&0011))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))10((///((0"### ïïßïßߥßßßßïï 1##"(///(0111#0(&/+&""ßß……………¥¥ßï "(///(011110(&/&(00#1 ïïß½ßߥßßßßïïïï ##"0((/&/&&0011)*'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**)100(&///&(""## ïïßïßßßߥßßßß¹½ïï #00(/./(001110(///(( ïߥ¥……>……¥ßï ""++./(001110(///((### ïïßßßߥߥßßßßïïá #""((+&.//(011))*'22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))10((/&/((0"### ïï¹ßß¹ßߥ¹ßßßïï )1##0(///(0111#0(&/&("" áß¹¥…………¥¥ïï #"(/&/(0111#1(&/&(001#1 ïïßïßßßßßßßßßïßïï "#""((/&/0011)**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**1100((/&/((0"#ïïßߥߥߥßßßßïïá 1#00&/./(001110(///(," ïߥ¥……………¥ßï "",+//(001110(///((##1 ïïßïßߥߥ¥¥é¥ééñßïï "-",&///(011))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))00((//&((0"## áïßïßߥߥߥßßï½áï 11##((///(011110(&/+&""ßß……………¥¥ßï "(///(011110(&//&001#1  ïïßñé¡¥¥Ý¡¢ééñßïñ #"",(+&/(011))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**1100///&/((0"# ïïßߥߥé¥ß¥ßßïßïá 1###((//./(001110(///(( ïߥ¥……>……¥ßï ""++//(001110(//./(00##1  ïïé颢ݢݢݢ¢é¥ñßï -",(++/((11))*'22þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))00((/&(""### ïïß¹ßߥߥßß½¹ïï )1#1#100&/.//(011111(&/&("" áß¹¥…………¥¥ïï #"(/&/(011111(/.//((0#1#11 ¹¹éé¢éÝ¢¶¢Ý¢¢ééñßïï #"",(+&/(011))''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**1100&&/&&((0"# áïßïßߥ¥¥¥¥ßßßßïï ###0#0(&/.../(011110(///(," ïߥ¥……………¥ßï "",+//(0011100/....((00### ñßñééÝݶ¶¶¢¶ÝŸ¢Ýééññï$",(+&/((11))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**)100((/&(""0# ïáïßßߥߥߥßßïïá 1###00((&/.../&0011)1#((/+&""ßß……………¥¥ßï "(///(011)110(/..//((00#0###ññé¡é¢ÝݶݶŸ¶¢¶¢Ýééññïï"",(+&/(011))*' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**1100///&/("0"# ½ïßߥ¥¥¥¥¥¥ßßïïá ##"0"0((//..../(011)110(///(( ïߥ¥……>……¥ßï ""++//(0011)00(&.....&&((0"## ââñâñééÝéÝŸè¶Ø¶ ¶¶Ý¶¢¢¡éï$",(+&/((11))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22**))#0((&((0"## ïïßߥ¹¥¥¥¹ßßßïï ####"0((&&//././/(011)))11(&/&("" áß¹¥…………¥¥ïï #"(/&/001))))10((//.//&/((""""#òââßñé¬é韢¶¶ØŸØŸ¶¢Ÿ¢éññïï#""((+&/(011)*'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**)100(&/&&("## ïïßߥ¥¥¥¥¥¥ßßïï ##"0((((//././///0011)))1100///(," ïߥ¥……………¥ßï "",+/&(01)))1)00((//././/+,(,"$òÑâââââñééééÝݶŸØ¶ØØ ¶ Ÿ¶ÝÝééññ$",(+//(011))'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**))10((/&(0"### ïïßߥ¥¥¥¥ß¥¹ßáá ##"0((///&.//&&((0011))*))11((/+&""ßß……………¥¥ßï "(///001)*)*))1100((/+++.+,,$Òâѽâߥßââ¨ñééé¡ÝݶŸ¶¶ ŸØ¶ Ÿ¶¢Ý¡éññ ",(+(+(011)*'û ý44ööö4ööööööööööööööööööööööööööööööööþöþöþöþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**)100(//&/("# ááßߥ¥¥¥…¥¥¥¥ßßïá  #"0((+&//...//((000011))***)10(///(( ïߥ¥……>……¥ßï ""++//(11))*)*))1100((,,-.--%ÒѨ>¨¨¨•…¨¥•é¨é•éÝõèÝèŸØØ ¶   ¶¶¶¶ééßñï$"-,,,,"$1)!úûüvÊêxÊxÊóxóxóxóxóxóxóxóxóxóxóxóÊóxóxóxóxóxóxôxôxô4ô4ö4ö4öôööööööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*))10((/&&0"## áïïßߥ¥¥¥¥¹ßïï ###"0((&&/&.//&&001011))**'*'**11((/&("" áß¹¥…………¥¥ïï #"(/&/00))**'**)*))11#0$0--ÒѨ¨•¨}>}>•…•……Ý•ÝõݬݟèÁØØØ؆ؠŸØŸ¶¡éññ$",,,"#1)!úûîîÊÊÊÊÊÊóÊÊÊÊÊÊçóÊóçóÊóÊóÊóÊóÊóÊóÊÊÊóÊóÊóÊóÊóÊóÊóÊóxóÊóxóxóxôxôxôôôxôôöôööööööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ32'*))10((/&/((0"# ïßߥ¥…¥…¥¥¥ßßßï ##""((//././/((0011))))**'''**)100///(," ïߥ¥……………¥ßï "",+/&(11)*'''****!)1%%¨¨DDCDCDCCCDfww}}»»Ý•ÝèõèŸèèØ؞؞  Ø ¶èÝÝéñï$$-----"$)!úyîyîvççççççççççççççççççççççççççççççççççççççççççççÊçÊçÊçÊÊÊçÊÊÊçÊÊÊÊÊÊÊÊxÊóxóxóxôxôxô4ööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''))1#((///("0# ïß¹¥¥¥¥¥¥¥ßß½ï #"0("(&/&.//((0011))*)**''22 2'**11((/+&""ßß……………¥¥ßï "(///00))''222''ú*!!ã½>?CB7B777B7A7BBCCww}»Ý•ÝèõèŸèèØ؆؞†ž† ¶¶¢Ýééï$$---#1)!úúì’vvìvìvçìçìçìçìçìçìçççççççççççççìçìçìçìçìççççççççççççççççççÊçÊçÊçÊçÊçÊÊóÊóÊóÊóÊóxôxôôöôööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))11((/&/((0"# áïßߥ¥…¥…¥…¥¥½ß #"0((//...//(,#01)****'' 2 2'*))00///(( ïߥ¥……>……¥ßï ""++//(11)*'2 üüûú©©¾ѨDC77@777777777;7;77BCww»»èèŸØèØ؞ܞžžžžžžØØŸèõéññò%$$$--$!©ÏÏyy’’’’’’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ìvìvìvìvììçìçvçvçìçìçìçìççççççççççÊçÊÊóxóxôôööööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**110(/&/((0## ïß¹¥¥¥¥¥¥¥¹ßïï ###"0((/&///((001)))!*ú'û2 3 22**11((/&("" áß¹¥…………¥¥ïï #"(/&/00))*û2üîyyϦ¾\>6B7<7UB=CdC=C6BBAU77;UC„w »èèÁèèØÀÜÜž¿žžžØžØØŸÝééñò $$-$$#!!úÞìÉ’’í’í’í’í’í’í’í’í’í’í’í’í’í’í’í’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’çìç’ç’çìçìçìçìççççççÊçÊÊóÊóxôôôôööööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'*)110(///((0"# ïßߥ¥…¥…¥…¥ßïï #"""((//.+/(,0#1)!!úúûyüü33 '')100///(," ïߥ¥……………¥ßï "",+++,#1)úûyvee¦©PP>?7<7IB=aTaTdfff6fCdCc7A;7Bdw »ÜØèÜØÜØžžµžµžžžžØØŸ¶õéññò%$$$%$$!©ÏÏ’Þ’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’É’’í’í’í’í’í’í’íÉì’ìÉì’ì’ì’ì’ì’ììççççÊÊÊÊóxóxô4öôööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '**)100///((0"# ïß¹¥¥…¥…¥¥ßßïï #"#""&&/+/++""##))!ú©úÏÞÞîî4þþþ 2**11((/+&""ßß…¥………¥¥ßï $",+,"#)!*úyee‹tnPO6<<<==ZTZZbbbbbbbfbffdaBU;7Bfw »èÜèØèÜÀÜÜžžµžžžžØØŸÝééïò $$$$-$$#!ÏÞÞ’É’É’ÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉíÉí’í’í’í’í’í’í’í’í’í’í’ì’ììççççÊçÊÊóÊóxôxôôööööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**)10(//.&(0"# ïßߥ¥…¥…¥…¥¥½ï #""((/&./-,,"$11!©Ï©Ï¦Ì¦Ì’ìêþþþþ3 '*))00///(($ ï¹¥¥……………ßßï $"-,-,$#!!Ϧ¦ttP\N67LI7IIllZZLZLZ[L[[M?[?[b?bbfTffBA;ACw£Ü»èèèØØÜܞܞžžžž  Ø¶ÝÝéñïò%$--%$$©ÏÏÌÞɌɌÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÉÉÛÉÉÉÛÉÉÉÛÉÉ’Éí’ì’ìvììççççÊÊÊÊóxóxô4öôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))11(&/&/("### ï便¥¥¥¥¥ßßïï #"0((++,,-"11©©ÏΦβ²²ÇÇÇDz³vöþþþþþ 2**11((++," ßߥ¥Ý¢Ýé¥ññ %$-$%÷ä÷¾¾mLB6<;<=llllZZLL[?MMMMMMNMMMMbbbbffd=A7;cb£»è»èèèØÀØ؞ܞžžžž  ¶èõéññ%%$---$©©ÏÏŒŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛŒÛ³Û³ÛŒÉŒÉŒÉÉ’Éì’ìvççÊçÊÊxxóxôxô4ööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'**110(/&/((0## ïïßߥ¥¥¥¥¹ßïï #""((,+,,$#ÎÎà΂˜±±ˆÆ°Ö°Æ±Ú’öþþöþ44ü2ú)11",,,-ñ¹é¡Ýééééññ%%$%ämrL<;VWkl{ZZZ[L[MNMNN€NNNNMNMMMMb?TTd=77B„} »ÝèõèèèèØØØ؞؞† ¶ØŸÝ¡¡ï$$-$$)!©ÌÌÛŒÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÉÛÉÛÛÛÛŒæŒæ³Œ³³³³³³³³³Œ³ŒŒÉe’É’’ìvççÊÊóÊóxôxôôôôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22**110(///((0"# ïïßߥ¥…¥¥¥ßïï "",,-,-$$÷ξËËˤ±ˆ°›››¯¯¯¯°‚’xxxxxxêîû*!1"-,-"$¹éߢéÝééßßïò%%%%%ãͽr[<;O>O>OOOONNNN[M?bbTc<7c”£»ÝÝéÝÝéÝõÝõè¶Ø؞ؠØØŸŸÝéññï òãòÒÒÒã¾¾§Ë²Ç²Ç²Ç²ÇåÇåÇåDz²åÇåDzÇåÇå²Ú²Ú²Ú²ÚàÚàÚ²Ú²²Ç‚tœtt‡tnšmm|m\\\\>\>\>\\\\mmmPnn‡‡ˆˆˆˆÆƱƱ××±ÙÆÙÆ×±ÙÙ××åÇàÇå²ÚÚæÛë³ÛæÛÛÉÉííììççóÊóxôóöôööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'*)11((///((0 ïߥ¥¥¥¥¥ßßï $"-$-$$$$ää¾Í¸·¯šš‘‘™™rrO†M[ZZJLMrmt‹³È³³³¦¦Î©¾%%%$%òòâïññïñâòòãòÒ%ÒÒ¥•N=<;I=lZLZ[?MMNNONOO>O>O>OOOONNNNMM?[Zba=77B”}»»Ý•Ý•Ý»õèÝèèØØØ؞؞†Ø¶è¢Ýñßñââ½âòÑÑÑÑÒÒÒòòãã¾¾ËËלללל××לٱٱٱٱ±±Ù±Ùœ×œÇ×ÇÇǜǜœ±±ˆˆ‡‡nnm|rr>ONN??6?666K666??MNOO>>r\||š|šš¯¯Å›ÖÅÖ°°Å°°ÆÖÆÆٜללà²ÚÈÚÚÈÛæ³æŒÉ’ÉÉí’ìçççÊÊôxó4ööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**)10(///((0##ïïß¹¥¥¥ßßßï "$$-$$$%äͤ·¸·š®|‘™r†M[==I<<<<=N\nœ²ÚÚÈÚÚ‹Î÷%$%%%òãòòòãòã%%%Ò½¨»bI;<=lZ[Z[[MMNNOO>O>O>>>>>>>OONNNNM[L[ZTc<;Ud£»•»ÝÝÝÝÝÝõèÝèèØؠ؞ؠØØŸ¶ÝÝ¡éññâßââÑâÑÑÒÑÒòãͤ¤ËƱ±±±±ÆÆÖÖ°ÖÅÖÅ°ÅÆ°Ö°ÖÖÆ°ÆÆÙÙٱٱ±±±ˆˆ‡›Pš‘\rN[66=6BIPˆ‚²²²‹²‹§Îää%%%%%%%ããòòòââÑâÑòòòòãòò%%ÒѨ>wa;7O>>>>>>>OOOONNMM[[ZZa^SAB”w»»••Ý»Ý•Ý»è•è»ØÜ؞؞؞؞ØØØèÝèÝ•é¨ß¨â¨Ñ¨ÑùÑÑòÑáḸ°¸Å¯ÕįÄÄÄÄÄÄÄÄÔ¯ÄÕÄÕ›ÅÅ°ÅÖÅ°°Ö°ˆÅ›¯š||\rON[L=I7<7<mˆ±ÇDz²²§§¾÷%%%%%òãòòòòòò òãÒѨ…}MI<O>>>>>>>>>>>OONONNMM[[ZlI<;=„££•»••Ý•Ý»Ý»è£ £žž žžž¿žžµ¿ÜØÜèèè•ÝÝ¥¨¥¨Ñ¨ÑÑÑѽ½ººš®®ÃÃÃÃÂÃÃÃÃÔÃÔÃÔÔÔ®ÄÔšÔÄÔÔÔÔ®ÔîÃÑ‘­†[ZII<<;<>>>>>>>>>>OOOONNMM[[ZZJI;Ac„w£»»}»»»»»££w„dƒƒ“µµµµµµŠµµµµžµÜ»»»•»••¨•¨•¨•¨¨¥¥¡¡™¡­­Ó™Â™ÂÂÃÂÃÃÃÃÃÃÃÃÃÂÂÓÓ­Ó­Ó¬¬Á†N[=I;;;<;>>>>>\>>>>>>OONNMNM[L{lXH;A`„ £»»»»»} wwddBAS^ƒ´´µ´µ´—´´´µ“µ“µ“£w£N}N}}}}•}»»…¶¢Ÿ¬¬­¬Ó­Ó­ÓÓÂÂÂÓÓÓÓ¬Ó¬Ó¬¬¬¬¬¬ÁÁ««†«€ZY<;;<š‡ˆ±œœ×œœ¾¾ääòÒ%Ò%Ò%òòòòòòòòòÑòÑòÑòÑòÒѨ¨••}wC;;=JZZ[?MMNNOO>O>>>>>>>>>>>OONONNM[ZZlJI;;_aMwž}£}£¼}ffBAR@5@Ruƒ´´´ŽŠƒƒƒuduupcUcUcCdCdfww“£  Ÿ«Á˜¬Á¬¬¬¬Ó¬¬¬¬Á¬ÁÁ«ÁÁÁ˜Á«Á«˜«À—{{XV;;GHVXXYXkklYXI<;;:;;<O>>>>\>r>>>>O>OONNMM[{ZlXV;HW{€ž£ ££wwdcAA@;RA@Rpƒ´Š‰‰ji__SGRGRGR;RARAApcƒ„““†««ÁÁÁÁÁÁÁÁ«Á«Á«««ÁÀ«ÀÁÀ«À«ÀÀ——{W<;;;VWXYkYkkkYXI<;;;>>>>>>>>>>OOOONNMM[[ZZYW;;HkŠµ€ž““„”c_@@@;S_SR@osƒƒƒ‰‰hhgg99999:9GRGRG@R@@@;ScdŠ“žž†À««««ÁÀ«ÀÀÀÀÀÀ¿ÀÀÀ¿À¿À¿¿¿€€lqH;G;HIWkYkklllY=<<;<>>>>>>>>>>O>OONNNM[[ZZlY<;;W€€—´´ƒƒ_S@:@SApp_RRRsu‰s~ggF989E9FggHHh_VH_SS;;@GRUu„Š—ÀÀÀÀÀÀÀÀ¿À¿À¿¿¿¿¿¿¿¿¿À€€lXH;;<™|š¯Å°ÆÆÙ±×××ÇåÇåàåàȲàÚààÚ²ÚàÚàÚÚÚÚÈÚÈÚëÚÈÚëÚëÛÛÛÉÛÉÉììççÊÊóÊóxôxôôöôööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöööööööööþöþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**110(///(("" ïïßߥ¥¢é¥ßßïñ%$%%%Òòѽ¥rž[X<<;<<==lYlYkXXWjiihhghgG:VM\š°°±±œ±×±±¤¤ÍäòòÒÑÒÒÒ%Ò%Ò%ÒÒÒÒÒÒÑÑù¨¨¨•¨¨¨¨¨¨¨•••»}“ƒ^;;VYkZZ[?MMNNOO>O>>>>>>>O>OONNNN?[ZZlkVH:Hj{ŽŽŽŽ‰uoRQ@RSApU^_S@RSoggEE8E8EEFFgghhhhisWs^_VSS@@@A^ƒŠµ€—¿¿—¿ª¿ª¿ª¿ª¿ª¿¿¿¿¿€€kW;;;\|n›°°±±×œÇÇÇDzÇಲÇÇÇàÇåÇàÇà²à‹à²à²à²à²à²Ú³Ú‹ÈÚ³ÈæŒÛŒ’Éí’çìççççÊçÊçÊÊxÊxxóxô4ôôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöööööôôôxôxôóôöööþþþþþþþþþþþþþþþþþþþöþöööööööööööööþöþöþöþöþöþöþööööööý2'*))#0+++(,""# ßßéé¥é¥ßßï %%$%%Òã½½¥¡ ZX<7O>>>>>>>>>OONONNMM[[ll=VG;hŽ–Ž‰oG9GS__^p^psS:9FFFE9EEEgFgg~g~hzhisuWjWX^^U_;;@ApƒŠ—€——¿—¿—¿—¿ª¿ª¿—¿¿¿¿À¿¿€€kI;<O>>>O>OONNNNM[L[llXVG:Gzz–––hG9GS__pps_hHg998E8EEgEgggggghghhisjWjjXuX^u^U;;@;HXƒŽŽ—ª—ªªª—ªªª¿ª¿—¿ª¿¿€€kW;;HX=llZlZllllYXH;;WZ€«†˜˜ÁÁ¬¬¬Á¬¬¬¬­¬­¬­†MJ7<=L[M?K<<=N>|m¯›ÖƱœ×œÇ‚ÇÇÇÇಲÇà²à²ÚÚÚ²Ú²Ú²Ú²à‹Ú²²²ÚÚà²à²ÚȳÈæŒæ³ÛÛÉÉ’Éí’í’í’ìÉì’ì’ì’ìvçvçìççççççÊçÊÊÊÊÊÊxxxxóxóxô4ô4ö4ööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööööööôöôöôööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöôxôxóçÊÊçççìììçìììççÊóööþþþöþööööôôôôóôxóÊóÊóÊóÊóÊóÊóÊóÊóÊóÊóÊóÊóÊÊÊÊçîûûú)1",-+,"$ïñé¡¥¥¢ééñßïï%$%%Òò½ß¹rMX;<} wwwwwww£} } » £ž´´‰HGGiXklZL[MNNONOO>O>>>O>OONONNMM?[ZlYjHGGhz––––zH9GHhVsss_hhhgg99EFEgggggggggg~hiijiX^qXk`J``^^A;:;HjkŽŽ—ªªªªªªª—ª¿ª¿ª¿———€X;;wwdaUcUUUccafŠwž“µŠ´zh9GhXXkJZLMMNNNNOOOOOOOOONNNN[[L[ZZkXHG9gh––zhG:GhhshV_h_VohggFFEgggggggggggghhiiW^X`Y`aYa``=^AS:;HiqŽ–ªªª–ªªªªªª—ª——¿Ž€X;;;WYZZZlZZZlZllI<;‘®Ä¯°ÅÖÅÖÅÖÅÖ°Ö°ÖÖÆÆٱٱÙÙ××××××Ç×DzÚÇå²ÈàÚàÚÚÈÈææÛæÉÛÉÉí’ì’ì’ì’ì’ì’í’í’íÉíÉÉÉíÉÉÛÉÛÉÛÉÛÉÛÉÛÉÛÉÉí’í’ì’çìççççÊÊÊÊóxôxôôööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöööôxóçÊìç’’É’Éí’ÉÉíÉ’Éì’’’í’íÉìÉì’ì’ìvçvçìççççççÊçÊÊxÊxxóxô4ööööþöþþþþþþþþþþþþþþþööôôxÊçÊçì’íÛÉŒæ³ëÈÈÚæÚÚ²²²Ç×Ç×Ùœ±ÆٱײÈÛ’vçvì’í’’É’ÉÉÉÉÉ’É’É’É’É’É’É’ÉÉÉɌ泳¦¦ÎÎ÷$$----$$ññééÝ¢Ý颡ßïò%%%%ù%%òãẹ¡€=;;IXkYlYllZZZZZllkkXjiihH:;=€rššÅ›Å›Õš®‘™N{YIVZNr‘¹‘r†[X;;:;;VVjWXXXWjWVHHGGFhh~hF9GhiXXkJZL[?MMNMNMNMMMM[[LZllkkWVG:9h~–––hG9GhijjWj^W^WsiihghggFgEgEgEgggghhiWXXkJlZZTLTZTZaa``VH:;HzŽ–––ª–ªªªªªŽŽjHG;rmP‡ˆ‚‹³³ŒŒÛŒÛŒæ³ÈÚÚ²²‚œË¤Íäã%%%%%%%òòÑâééݥݢ¶¢Ý¢¢éßïòòòòòÒÒÒÒÒòòãẹÙ­˜†[I;|nˆ±×œÇ‚ǜǂלױ±±±±±°ÆˆÖ°°ÅÅ›ÅÅÕįÄÄÔÔÄÄÃÃÃÙ™†[J<<<==ZZLZ[Z[[[[[[M[[?LJ=<<=N>™|š¯ÕįÄÄšÔ®®¹¹ßߨ¨¨¨ù¨••DfBA@AR@;dwž£ ØŸèõéññâѽÑÑÒÑÒÑÑÑâ½¹º¹Â­­Á˜€{V;;WXkYlZ[L[?[?[LLllXXhH:;W{€ÀÀ—khEFFgghghghhhhhHHgHghhjWXYlZL?MMNMNMNMNMMLLZlYYjiHGFzŽ–ªªªŽªŽ—ŽŽ{Žk{{[€N€N†O­¬ÁÁ˜Á««ÀÀ¿À¿ŽŽq<;<=lZZL[?[?[bLbbd`<;@Hsƒ‰–––––zhG:;WYlZ[bMMM?N?M?wbLZZYXHG:hzzhGHVYlZZ[MNMNNNNNNNMMM[KI;€K=rr‘\||š®ÄšÄÄÄÄÄÔÄÔÔîÃÔÃÃÃÙ­Nl<<™‘®ÃÔÔÔÔšÔÄÔšÄÕÄÄÄÕÄÕÄÕÄկկկկ՛֯śÖÖ°°Ö°ÖÆÙ±Ùœ×DzÇà²ÚÚ³³æ³æ³æ³æ³æ³æ³æ³æŒÛŒÛŒÉÛÉÉíÉì’ìvçìççççÊçÊçÊçÊçÊÊóxóxô4ô4ô4ô4ô4ô4ô4ô4ööööööþþþþþþþþþþþþþþþþööööôôôôxóxóÊóÊÊççççìç’ì’íÉíÉÉÛÉÛÛæÛææ³æ³ëÈëÈëÈëÈëÈëÈëÈëÈëÈÈÚÈÚÈÚÚÚÚ²åÇÇ××±ÙÆÆ°Æ°Ö°ÖÅůÕÄÕÄšÄÔÃÔÃÃÃÃÂÂÓÂÓÂÓÓ¬¬¬Ó¬¬¬¬ÁÁ˜˜†{XO>O>OOOONNM€[[Z{kqWVGGHqŽŽªª´Š{uUAA;_U^^u^usshhG9F~~–‰‰oS@@@_d££»»»»Ø «««À«ÀÀ¿¿——Žh;GVjqYllLZ[[[L[ZlkkWVGGhŽ¿¿——ŽqF9FhizjjjqXqXqjjhH9GG;:;HjXkYlllZZZ{ZZllYqXqjjVHGVq€—¿À««ÁÁ¬ÁÁ««€HG:G:GG;GHHhhhHG:Hz––ªªªªªªª–ªªªªªªªzGGHWXllZZ[L[[[L[bZaa`X^VgG9Fg~~–~GGGVXklZZ[[[[M[[bLZlYkXjHg9Ggzhg9GHjXkkZZ[[MMNNON†NNNNMM[ML[Z{ZlllYYXX=XWX=XXYYll{Z[Z[Z[L[Z[Z{lZl{JXHO>O>OOOONNMM?[ZlYXVH9GhŽ––‰s_R@@_Uccu^^sshhgF8Fg~~~~~]]Q@@@@c”£}££Ü»ØžÀØÀÀÀ¿¿ª—ŽŽzV:GhXXYYZZLL[Z[ZZYkXWHGGi——¿—¿—¿€ŽqGEGgiijjXXqXqjjiVG:GHG::HVXXqXYYlYlYlYYXqjjjjhHG;hŽ««Á˜Á««ŽhG9GGHHhHhhihiihGFGhz––ª–ªªª–ª–ª–ª–ª––zH:GVXYllZZ[L[L[LLKJ``^jVhG9EFg~~hg9GHWXkJZZ[?[?[ZLTZYYXXihG:9gh~gF9HhjXkJZL[[MMNNNNNNNNNMM[[L[Z[ZZZZlllllllllllZZZZLZ[L[Z[L[ZZZZZZJYVH;X{«˜«ÁÁ¬¬­¬Ó™ÂÂÂÂÃÃÃÃÔÚÔÔÔšÔÄÄÄÄÄÄÕÄÕ¯ÖÅÅÅÖ°Æœ±ÆٜלDzà²ÚÚ³³æ³æŒæŒæŒæŒæŒæŒæŒæŒÛŒÛŒÉÛÉÉ’Éí’ì’ìvçvçvçvççççççÊçÊÊxxóxóxóxóxóxóxóxô4ôôööööþþþþþþþþþþþþþþþþööööôôôôxóÊóÊóÊóÊóÊóÊóÊÊççççìç’ì’íÉÉÛÉÛÛæÛæÛ³æ³æ³ë³ëÈëÈëÈÈÚÈÚÚàÚ²à²ÇÇ×Ù×±ÙÆÆÖÖÅÅÅÅÕÅÄÄÄÄÔÄÔÔÔÔÃÔÂÃÂÂÓÓÓÓ¬Ó¬¬Á¬ÁÁÁÁ«Á««†{W;HiqXqjkXjVVHHHVWXXkYkYkkkYlkkqXVH;>>>>>>O>O>OONNMM[[ZlXjHG:Hz––ho::R>>>>>>O>OONONNM[ZZlYWVG:Ghzhg9:RH_^^^^WsWhshhGFEgg~~~ggEE8]]o]:5Spƒ”““µµÜžÀ¿À¿¿¿¿ªªŽŽzh:;HXXYYlZLZLZZZlYqWiHGGj——¿¿—¿€€qGEGgiijjXXqXXXXWiH::HHH9:HVijWjjjjXjjjjiihiHHGGGj——ÀÀ«ÀÀÀÀ¿¿——ŽhG9HhihiiihiiihiHH9GhŽ–ª–ª–ª–ª–ª–ª–ŽzgGGVXkllZZZLZ[ZZaJ``^WsshgE8Eg~~h99GiWYYlJZZLZLKZaaYXWjiigFEGg~hg9:GVWXYlZL?MMNNNNNNNNNNNMMMM[M[[?[[[L[L[Z[Z[Z[L[L[?[[[[[LLZ[ZZllYI;HV{€««««Á«ÁÁÁÁ¬¬­¬Ó­ÂÓ™ÃÂÃÃÃÃÔ®ÔÔÄÔÄÄÕÄ°ÅÅÅ°ÅÖ°ÖÖÆÆ×××Dz²ÚÚÈÈëÈæŒæŒÛŒÛŒÛŒÛŒÉÛÉÉÉÉ’É’ÉìÉì’ììçìççççççççççççççÊçÊÊóxóxóxóxóxóxóxóxô4ôôööööþöþþþþþþþþþþþþþþööööôôôôxóÊóÊóÊóÊóÊóÊóÊÊçÊççççìì’í’íÉÉÛÉÛÛæÛæÛ³æ³æ³æ³æ³æ³ëÈëÈÈÚÈÚÚ²à²Ç××ÙÙÆÆÆÖÅÖÅÕ¯ÕÄÄÄÄÔÔ®Ô®ÔÃÔÃÃÃÃÂÂÓÓ­Ó¬¬Á¬¬¬ÁÁ«Á««—kVGVWqXqXqXqXXjXXqXkXqXkYkXkYkYkYqjWH;:VqŽŽ¿¿¿À¿À¿«À«À«€<;>>>>>>>>>>>>OOOONNM[ZZYqiH9Ggzzh:9GhhW^j^X^jsjs~hHE9F~~~ggEEEF]ooo]Q]p”´„µµ¿žÀÀÀ¿À¿¿—¿ªªŽhGGVjqYllZZ{L[ZZllYqVH9HzŽ—¿¿À¿À¿€{G9FhijjqXkYkXqXjWV::HVhH:;HjWjizjjjjjzizhhgHFG;z——ÀÀ««Á««ÀÀ¿¿—ªzGGghhzijizijiziVHGFhz––ª–ªªª–ªªªªªªªŽŽqV:;O>>>>>>>>>>>OONNMM[[ZlXjHG9ghz~F9FHhss^^X^jsWsihhgF8gg~ggE88FEggooG8Qo‰”´“µµ¿žÀ¿¿¿¿ª¿ª—ŽŽzh:GhjXkYllZZLZZllYqjVG:HqŽ—À¿À¿€kHEFgiijjqXYXqXXjWH;9HhiHG9HHihihihihihhgHGG9GGj€««Á˜Á«ÁÀÀ¿¿ªªŽhG:gHihiijijWjijhhGFGzŽ–ªªª–ª–ªªªªª–ª–hH:HVYYllZZLZLZZaJ``^^phGG9Fg~~–~~G9:hiquYYlaZaZaJYY`XWiHgF99ghzzgG9GhjXkkZZ[[MMNNNNNNNNNNNNNMNMNMMMMMMMMMM[MMMMMMM[M[M[[L[ZLZLlYI<;IZ€««««Á«ÁÁÁÁ¬Á¬Á¬¬­¬Ó¬Ó­ÂÓÂÂÂÂÃîÃÔ®ÔÃÔÔÕÄÄÄÕ›ÅÅÖ°ÆÖÙ±ÙœÇÇà²ÚÚÈȳÈæ³æ³æŒæŒæŒÛŒÉÛÉÛÉÉ’Éì’ììççççÊÊxÊxÊxÊÊÊÊÊÊÊóÊóxóxóxóxóxóxóxóxóxô4ôôööööþþþþþþþþþþþþþþþþööööôôóôxóÊóÊóÊóÊóÊóÊóÊÊÊÊççççìì’ì’íÉíÉÉÛÉÛÛæÛ³Û³Û³Û³Û³æ³ëÈëÈÈÚÈÚÚÚÚ²ÇÇÇÙ×ÙÙÆÆÖÖÅÅÕÕÄÕÄÄÔÔÄÄ®ÔÃÔÃÔÃÃÃÃÂÃÓÓ¬Ó¬¬Á¬ÁÁÁÁ««€qH;VjqXqXkXkXkXqXkXXXkXqXkYkYkYYXqXjiVG:GzŽª—ª—ª¿ª¿ª¿ª—Ž€lW;>>>>>\>>>r>>O>OONNMM[{lkjVG9F~zhg9GHiiWWXuXjjWiizhgE9g~~~gE8gggg~oo]9Qopƒ´´¿žÀ¿À¿¿¿¿—¿ª—ŽhGGVjkYllZZLZZZZYkXjHHGhq—¿¿ÀÀÀÀÀ€{H9FhhzjqXkqkXkXXjiH:GVjiHGGHHVhihihhHhgGFG:;Hq{†˜˜ÁÁÁ˜Á«ÀÀ¿—¿ªªzGGGhhjijjjjjjjjjVH:;h–ªªªªªªªªªªªªªªŽqHGGVXklllZZZZZKZaa``^sHG9Gg~––~H9GHijqXkalaZaJaJ`X^WHHF:FHh–zzGG:HVjXklZZ[[MMNNNNNNONNNNNNNNNNMNMNMNMNMNMMMMMMMMMM[M[[L[ZZllI<;VY{€««««ÁÁÁÁÁÁ¬Á¬¬Ó¬Ó­Ó­ÓÓÓÓÂÂÂÂÃîÃÔ®Ô®Ô®ÄÔÕÄÄÄÕÕÖÅÖ°ÖÖÙ±××ÇÇåàÚÚÈÚÈÈë³æ³æ³ææÛæÛÛÉÛÉÉíÉí’ììçìççÊÊóÊóÊóÊóÊóÊóÊóxóxóxóxóxóxóxóxóxóxôôôôööööþþþþþþþþþþþþþþþþþöööôôxôxóxxÊÊçÊçÊçÊçÊçÊçÊççççìì’ìÉíÉ’ÉÉÛɌیæŒæ³æ³æ³æ³æ³æȳÈÈÚÈ‹Ú‹Ú²àÇǜלÙÆÆÖÖÅ°ÅůկÕÔÔÄÕÔÔÃÔÃÔîÃÃÂÃÓÓ™Ó­¬¬¬ÁÁÁ¬««{X;;VXXqXqXXjXXXXXXXXXXYXYXYXYXYXYXXjjiH::gzŽŽªŽªªªªªªªŽŽqH;HXlZZ[[MMNNOO>O>>>>>>>>>>>>>OONONN[[ZZkqVH5Fgz~hF9FhhWsWjXjXWjiihhG9Egg~gg88EggggogGQ5]p‰ƒ´´¿ž¿¿¿¿¿¿¿ªªŽŽzhG;HjXkYllZZZZZJkXXiHGGh€¿ÀÀ««†[i9FgiijjXXqXYXXjjVH:GHjjiGG9GGHgHgHGGFF9:GHW{€††˜˜Á«Á««À«À¿——ªªŽiH9GHihiijjjjXjXjWHG9HjŽª–ªªªªªªªªªªªŽŽqiGGHWYllZJZZZKZaa``^pSGQGg~~––zgF9HhjuX`aaaaaaa``^IH;:FFhh––zhG:9HiXXkJZZ[[MMMMNMNNNNNNNNNNNMNMNMNMNMNMNMMMMMMMM[[[[Z[ZLll=I;HI{€††««˜«Á«ÁÁÁÁ¬Á¬¬Ó¬Ó¬ÓÂÓ­ÂÂÂÂÃÃÃÃÃÃÔÃÔÃÔÄÄÔ¯ÄÄÄÕÄÅÅÅÅÖ°ÆÆÙœ×DzÇà²Ú‹ÚÚÈȳȳȳ³æŒæŒÛŒÉÛÉÉí’ì’ìvççççÊçÊÊÊÊÊÊÊÊxÊxÊxÊxÊxÊxÊxÊxÊóxóxóxô4ôôööööþþþþþþþþþþþþþþþþööööôôóôxóÊóÊóÊóÊóÊóÊóÊóÊÊççççìçìì’íÉíÉÉÛÉÛÛÛÛ³Û³Û³Û³Û³æ³ëÈëÈÈÚÈÚÚÚÚ²åÇÇ××±ÙÆÆÖÖ°ÖÅůÅÄÄÔšÔšÔÔ®ÔÔÔÃÔÃÃÃÃÂÂÓÓ­Ó¬¬¬¬ÁÁ««€X;;WXkXqXqXkYqXqXqXqXqXkXkXkYkYkYXXqjjVH:GH–ªŽªªªªªªªªªŽiH;VY{Z[[€MNNOOOO>>>>>>>>\>>>>>>O>OON€[[ZlqjHG9HhzhG9HhjWXXXXkXXWjiihH99g~~~gE8gg~gog~gF8Fo‰‰´´——¿¿À¿À¿¿ª¿ª—Žh;GVjqXklZlZZZllYkXjH;Gj{««««Á«˜†€WF9HhzjqXqYkXqXqjjHH:HiqjVgG:GFGGGFG9F:HO>>>>>>>>>>>O>OONNMMLZlkWVF9Fhhh99GhijWX=YXXjXWjhhgFEg~~ggEEEggggggg]98opƒ´´——¿¿¿¿¿—¿ª—ŽŽiGGHjXqYllZlZJlJYXXiH:GjŽ€««Á«˜«˜«€l99GhhjjqXYYYXXXXWVH::VjziHHGGFG9GGHHIY{M«†«««À«À«ÀÀÀÀ¿¿—¿ªªŽŽzhFGHiijjXXqXYXqXXWV::HqŽŽªªªªªªªªªªªªª–iH:HIYYllZJZlZaa`^A;@RSs‰–––––hg9GHW^`u``aaa``cU;;@;HqŽŽªŽª–ª–zhGGGhVXYllZZ[[M[MMNMNNNNNNNNNNNNNNNMNNNMNMNMMMM[M[[L[Z[ZZYW;;O>>>>>>>>>>>>>O>OONNM[Zlkqi;9Gg~gG9HhzjXXYYkYkXjjzhH9Fg~~~gE8ggggggoggQ9]~‰Ž——¿¿À¿À¿¿—¿ª—ŽiHGVWqYkkllZlZllYkjiH;Hq{†«˜˜Á˜˜†lGEghzWjXkYkYkXqjjiVG;Hjq{qjjiVhiiXk{[€€††ÀÀ«À«À«À«À«ÀÀ¿À¿¿ª¿ªª–hGGhijjqXkYkYkYkXqVH:HiŽªª—ª—ª¿ªªª¿ª—ªªŽV;;V=llZlZZZZZa=_A@;Suƒ´ª–ª–––zgGGhsuu``aaadacU;7@<^ƒ{—€———ª—ªªŽ–zhGGGViqYll{Z[[MMMMNNNNNNNNONNNNNNNNNNNNNNMNMM[M[M[[[[Z{J=HHO>O>>>>>>>O>OONNMM[[lkXiG99HgH99GVijWXXYYYXXjjiiGF9gg~~gE8Eggggggg]E8]o‰‰Ž´—ª¿¿¿¿¿¿¿ª—ªŽiGGHjXXXkJllZJlYYXXhH:HX€†˜˜Á˜˜˜˜O[;9FhhjjXXkYYYYXXjWhH:;hqŽ{Ž{{Z{[€M€€€ÀÀÀ«ÀÀÀ«ÀÀÀÀÀÀ¿¿ª¿ª—ŽŽiGGHiiXXqXkYYYkYkXXH;:Hq{Ž—ª—ªªª—ªªªªªªŽŽqH;;IYllZlZZZa`UA@;Au”´ƒ´´´ª–so9:Ss^uudddddCU@;;Uc„Mµ€¿——ª—ŽªŽ–zhGG:HVXYllZZL?M[MMNMNMNNNNNNNNNMNNNMNMNMMMM[[[[L[L[ZZY=;;HXl€À«˜˜Á«Á«ÁÁÁÁ¬Á¬Á¬¬¬¬Â¬ÓÓÓ™ÂÂÂÂÃÃÃÃÃÃÔ®ÔÔÔÔÄÄÄÄÕ¯Õ¯ÅÅÅÅ°°ÆÆÆƱ±Ùœ×œ×œÇ×ÇÇÇÇÇDzDz²à²Ú²Ú‹ÈȳÈæŒÛŒÉÛÉ’íÉìvççççççÊçÊçÊçÊçÊçÊçÊçÊçÊÊóxóxô4öôööööþþþþþþþþþþþþþþþþööööôôóôxóÊóÊÊÊÊÊÊÊÊÊÊÊÊÊÊçÊççìçìì’í’íÉÉÛÉÛÛÛÛæÛ³Û³Û³Û³æ³ëÈëÈÈÚÈÚÚàÚ²àÇÇ××±Ù±ÙÆÆÖÖ°ÖÅůÕÄÕÄÕÄÄÔÄÔšÔšÔÔ®ÔÃÃÂÃÂÓ­Ó­Ó­­Á˜†€I<O>>>O>>>>>>>O>OON€MML{kqih:Fg~gF9HhjjXXkYlYkXqjjVHFFg~~~gg8Eg~gggogg]9Qo~–ªª¿¿¿¿À¿¿¿¿——ŽiH:VWqXkYllZllJlYqjiH;H€˜˜˜˜˜˜[V9GHijXXqYkYkYkYqjjH;:Hj{Ž†††††À«À«À«À«À«À«À«À«À«ÀÀ¿À¿¿——ŽŽiHGHiXXqXkYlllllYlXWH;;W————¿—¿ª¿ª¿ª¿————{XH<O>O>OOOONNNM[[ZlXWHG9ggH99giWXXYYlJlYYXXWigFFg~~~~EEEggg]ggg]FE9]~‰–ªª¿¿¿¿À¿¿——ªŽzGGHWjXXYYlllJlYYXXVHGO>O>OOOONNMM[ZlkjhG:GhgG9HhjXXYlklllYqjjVH:Fg~~~gg8EEggggggo]F8Foªª——¿¿À¿¿¿¿——ŽŽjHGHiXXYYlJlllllYkjiH;Hl€†{q€††NXF9giiXXkYlYlJlYkXXWV;:;q{——À†«À«À«À«À«À«ÀÀÀ«À«ÀÁ«««Á««À«ÀÀ¿¿——ŽŽqhGHVjXkYllZZZZZZZllWV;;•¨¨¨¨¨¨é¨éèÝØØÜܵµŠ{jH@;S_SA7Cf••¨¨Ñ¨ÑÑò½··¯·¯ÄÕÄÄÔÔÃÃÂÓ¬­˜˜†€ZX<<;<<=JLL[[M[MMMMNMMMMMMMMMM[M[[[[ZJI<;;;IY{N†˜¬¬¬¬¬¬¬¬Ó­Ó­Ó­ÓÓÂÓÓÂÂÂÂÃÃÂÃÃÃîÃÔÚ®ÔÔÄÄÄÄÕÄÕÄÕÕÅÅÅÅÅÅÖ°ÖÖÆÆٱٜٜללÇ×ÇDzÇà²àÇà²à²à²Ú²ÚÚÚÚÈȳȳ³æ³æŒÛŒÛŒÉÛÉÉ’Éí’ì’ì’ììççççÊÊÊÊóxóxô4öôööööþþþþþþþþþþþþþþþþþþööööôôôôxóÊóÊÊçÊçÊçÊçÊçÊçÊççççìç’ì’íÉíÉÉÛÉÛÛæÛ³æ³ë³ë³ë³ëÈëÈÈÚÈÚÚÚÚàà²àÇ×Ù×ÙÙ±ÙÆÆ°Ö°ÖÅ°°°¯ÕÄÄÄÄÔÄÔÄÄÄÔÔÔÔÃÔÃÃÃÃÃÃÙ­­†€I<¥¨½ÑÑÑÑÑâââõ¬èØžžµµŠƒ_;@ASAB”w•¨âÑÑÒòÒòÍ͸¸°°°ÅÖÅÅÅůÕÄĮÑ™N[=I<<OMK>>\½Pãäã%%%%%òñ顶¶ †ž€„„ddb •âòò%%%$%÷Χڲà²å²àÇÇÇå×Ù±±ÆÆ°ˆ›š|\>O??==>N?667B>N?6?66=6B6=6=6=KKL?€N†­­­­Ó™ÂÂÂÂÃÃÃÂÔ®ÔÃÔÃÔÃÔÔÔ®ÔÔÔ®ÔÄÄÔÄÄÄÕÕÕÕÕÅÅÅÅÖÅÖ°Ö°ÆÖÆÆÆÖÆÆÆÆÙ±ÙÙ××ÇÇÇÇÇÇåÇå²à²à²Ú²ÚÚÚÚÚÚÚàÚàÚÚÈÚÈÚÈÈëÈë³ææÛæÛæÛÛÉÛÉÉíÉí’ì’ììççççççÊÊóÊóxôxôôööööööþþþþþþþþþþþþþþþþþþþþþöööôô4ôxóxxÊÊçÊççìçìçìçìçìçìì’ìÉíÉ’ÉÉÛɌییæ³æȳȳȳȳȳÈÈÚÚ‹Ú²Ú²àDz×לÙƱÆÆÖÆ°°ÅÅÅÅÅÕÄÄÔÄÔÔÔÔ®ÔÃÔîÃÔÃÔîÃÃÂÂÂí­NJ;<=ZZ[ZZZZlZlY<;;kŽ¿——Ž{i;:HWXXkYkYlYkYkXXWH:;i–ŽŽŽzhGGHijXjXXYXkYYWIH;:G:;:G;VWJl[L[[MMMMM[[[[ZlYXiH9GGHF9GhiXXYYlJlYkXXjiHG:gh~~gEEEgghgogogogG5:HŽ——ÀÀÁÁ¬ÁÁ«À—€k<:HiXXYYllZZZZZJlYXWihhghggggE88gk€N€XF8GhjjYYllZZLL[ZZllYYjjhHG:9Hj{Ž—«˜¬¬Ó¬Ó™Â™Â‘ÂÂÃÂÃÂÂÓÓ¬¬ÁÁ˜«€€X<;VXllZZ[Z[L[?[L[ZZZZlZ=I;7I[N™™ÃÃÔÔÕÅÖÅÖ°Öˆ±t‚§¦Î©#$$---$$ïéé……O¶ ¶»ÝÝß$$---$$!©ÏÌÛ³æŒæ³æÈëȳÈÈȳȳ‹³‹³‹¦‹‹ttˆtPnPm\\>>O>OONONOOOO>rrr™™‘­™™ÂÂÃÂÃÃÔîÃÔîÃÔ®ÔÃÔÔÄÔÔÔÄÄÄÄÕÄÄÄÕÅÕÅ°ÕÅÅ°Å°°Ö°ÆÖÆÖÆÖ±ÆƱٱٱٜלÇÇÇÇåDzÇà²à²à²Ú²Ú‹Ú‹Ú‹Ú‹Ú²Ú‹Ú‹ÈÚÈȳȳÈæ³æŒæŒÛŒÉÛÉÉÉÉ’’ì’ì’ìvçìççÊçÊÊxxóxô4ôôööööþöþþþþþþþþþþþþþþþþþþþþööööôôôôxóÊóÊÊçÊççççççìçççççìç’ì’íÉíÉÉÛÉÛÛæÛæÛ³ëÈëÈëÈëÈëÈÈÈÈÚÈÚÚàÚ²àÇÇ××ÙÙÆÆÖÆÖÖÅÖÅÅÅÕÄÕÄÄÔš®Ô®šÔÔ®ÔÔÔÃÔ®®ÃÔÃÃÃí­†J<¶¶Ÿ…¥éï$$-----#!©yÞ’’ì’ì’ì’ì’ì’íÉ’É’Éí’ì’ì’ìÉ’É’ÉÉeɌɌeŒŒŒŒŒeŒeŒÉeÉe’É’Œæ³Ú²²ÇÙ±±ÆÆ°ÆÖ±œ±Ö±×לٱÙÆÙ±ÙœÙÆÙ±±ÆÆÆÙ±²œ×œ×ÇàÇÇÇÇDzÇåÇå²à²à²Ú²Ú‹ÚÚÚ‹ÈÚÈÚÈȳȳÈëÈæȳȳȳȳȳÈÈÈÈÈÈȳÈëÈæ³æŒæŒÛŒÛÛÉÛÉÉ’’ìÉì’ìvçìççÊçÊçÊÊóxóxô4öôööööþþþþþþþþþþþþþþþþþþþþþþþþöþööööôôxôxóÊóÊÊçÊçÊçÊçÊççççççìì’í’íÉÉÛÉÛÛæÛæÛ³ëÈëÈÈÈÈÈÈÈÈÚÈÚÚÚÚàÚ²àÇÇ××ÙÙÆÆÖÖ°ÖÅůÕÕÕÄÄÔš®Ô®ÃÃÃÃÃÃÃÃÃÂÃÃÃÂÃÂÂÂÂÂÂÂÓ¬¬˜†Z<;XZ[Z[ZZZ{ZZllW<;Vk€——¿——j;;HXXkkkkllZllklYW;;H{ŽŽiH:HVjijjjjjjiHGGVqŽŽªŽª–iH:HWlZ[L[[M[[[[ZZYqWVGGGhhH:GHijqYkklllJlYqjjHG9h~~gE9GhhshhhhhhhhHG9HzŽŽ¿À««Á˜Á«À—€lV:;VXYllZZ[L[L[ZZllXqWzh~ggggFE8Eh{€†€jF9GViqYkJZZ[Z[ZZZlYYjjizhzHH9Hq€†­ÂÂîĚÄÄÕÄÕÕÕÕÅÅ°ÅÖÅÕÄÔÃÃÂÓ˜€J<……ééïï $$----$1!ÏyÞ’’’’’’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ì’ìÉì’ìÉì’ìÉí’ìÉì’ì’ìvçìççÊçÊÊÊçç’’ŒæÈÚdzÇàÇÚÚÚ²ÚÚÚÚÈÚÈÚÈÚëÈÈÈÉÈÈÈÚȳ³æÈÈÚÈÈÈÈÈÈëÈëȳÈæÈæ³æÈæ³æ³æ³æŒæŒæŒæŒÛŒæŒæŒæ³æ³æ³æ³æ³³È³ÈëÈæ³æŒæŒÛŒÉÛÉÛÉÉ’’ì’ìvìvçìççççÊÊÊÊxxóxô4öôööööþþþþþþþþþþþþþþþþþþþþþþþþþþöþööööôôxôxóÊóÊÊÊÊÊÊÊÊÊÊçÊçÊççççìì’í’íÉÉÛÉÛÛæÛæÛ³ë³ë³ëÈëÈëÈÈÈÈÚÚÚÚ²àÇÇ××ÙÙÆÆÆÖÅ°ÅůÅÕÄÄšÄÔ®ÔÃÃÃÃÃÃÃÃÂÃÃÃÂÃÂÂÂÂÓÓ™Ó­Ó¬­˜†[I;=l{L[Z[L[Z{ZZYW;;V{Ž—————{H;;WXqXkkllZllllkY<;;WŽŽjH:HhzijizijiVGGgzŽŽªªª–ŽqiG;VYlZZ[L[[[L[llYqiHFGg~hhF:GViXXkklllllYkXjhH9Gh~~F9FhhVhihihzhihhG:Hq{——¿¿«À«ÀÀ——{X;;HXYllZZ[L[L[LZZlYqjjhhggEE8EFhk††˜†€WGEghjjkYllZZZZZllYqjjiziVH;;X€«Á­ÂîįÅÅ°ÅÖ°ÖÖÆÆÆÆÙ±ÙÆÖÅÅÄÄÔ®™™€I<­‘šÄˆÆDzÈȳ³æ³æŒæŒÛŒÉÛÉÛÉÉ’É’ÉìÉì’ì’’ÉÉŒŒ‹tnm>>O>O>O>>>>>>>>OO>O>>>rrr\‘mšÄÕÅŇ°¸¤¾÷$$%--"- ßߥ¥………¥¥ïï ""(///(01)*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöööö4ô4ôxôxxxóxóxô4ôôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöö4ô4ôxóÊóxxxóxóxôxôôô4öôööööþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöþöþöþöþöþööööööööööööôôôôxóÊóÊÊÊóÊÊçÊçÊçÊççççççççççççìçìç’ç’ììíÉÉææÈÈÚÚ²å××××ÆÖ°ÆÅÖ°Ö°ÅÄÄÄÕÄÄÄÄÔÄÄšÔÔÔÔ®ÔÃÔÂí­†J<[JLN­|š›±Çڳɒì’ì’ì’ì’ì’ìçççÊÊóxô4ööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ32**110(.//(( áïßߥ¥¥¥ßïï #""&/./(01**2 þþþþþþþþþþþþþþúÒ%þþþþþþþþþþþþþþþþüþþþþþþþþþþþþþþþþþþú!ùþþþþþþþþþþþþþþþþþþÒþþþþþþþþþþùúþþÒÒþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööôôÊóççìí’í’íìÛÉíæÛææÈëÈëÚÈÈÚàÚ²ÚÚÇ××ÆÙÖÖ°ÖÅÅšš|­N6<=LMMM[[K=™‘®|®®Ô®®Ã®‘™r†K<r|m›š››ÅÅÅÕÕÄÄ®®\rMInˆ²‹ÈÈæŒæŒÛŒÉÛÉÛÉÉÉÉ’É’É’’ɌےììççÊÊóxôöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ%þþþþþþ3 '*)101ùïߥºÑùäÍ ""(&///1ú' þ3þþþþþþþþþþ%þþþúþþþþþþþþþþþûþþþþþþþþþþþþþþþþþþþþþþþþúþþþþþþþ!þþþþþþþþþþþþþþþþüùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöööôóóôç’ÊÛíæÛÈȲLj‡mr?66?Nr\šn‡‡ˆ°Æ°ÆÖÖ°°Å¯nš\OBI6MM€MMLKI<\mn‡ˆtÇœ²œ×œÙ±±ÆƇ‡š|O6?76?N?6<66N>\\®®š®®ÃÔÃÃÃÙӭӭ­¬­NL<<;=JLZ[Z[ZZZ[ZJ<66666?N>\Pn‡‡ˆ°Ö°°›ÕÄÄÔÔ®ÔîÃÃÃÃî||™­OM6<;?666NO\m‡ˆœœÇÇ×××±ÆÖÖÅ°¯ÕÄÕÄÕÄÄšÕ¯¯š®|™r†L6<<<6K[[€MMK=<[O™™®ÃÔ®Ô®ÔÔÔÃÔ®Ô®šºº½½âÑÑÑÑÑÑòââéñéõŸ¶Ø؞؞ܞÀØèèõè軶}}DDD¨¨ÑÑããä¾Ë˲ÇÇDz²à²à²ààÚ²Ú²Úàà×ÙÆ°›¯®‘rMJ<;;H‡‚³ŒÉÉì’ì’ì’ì’ììçãçóùòöþþþþþþþ!!þþþþþþþþùþþþþþþþþþþþþþÒúþþþþþþþþþþþþþþþþþþþý!úþ 2'))10//0- ïßߥ¹ºáïá))##0(((011**2 þþþþûûûûúúúúúû!ú!!!!!!!!úúúúúúûûüûüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöþöþööööööööööôö4öôö4öôööööô4öôöôôxôÊÊ’ŒtP>NN>>mntt¦‹³‹³ÈÈ‹Ú²à×ǜưְÖÅÖ°°ÅÖ‡›nš||rNL7<7==LMM[K<=Mr™®®šÄÕÄկů¯ÄÄį·¸áá½òÒÒÒÒÒÑÑòÑâââééÝÝØ؆؞؞ØØèèÝ•Ý•¥•¨¨¨ùÒÒ%Ò¾¾§§²²Ú‹Ú‹Ú‹Ú‹Ú‹Ú‹Ú‹ÚÚàÇ×±Ö¯¯|‘rNZI<;;;N66<6=LL?6I=†\šš¯ÅÖÅÖ°ÖÖÆÆְƈƤ¤ääÒ%ÒÒòòòâññÝ¢¶¶ØØØØØÁèõÝñâãÑÑÒ%%%%÷÷ÎΦ¦ÈȳȳÈëÈëÈëÈëÈÈÈÈÚÈàDZƛ¯®Ã™†€JI<;;<;H<€\t‹ŒÉ’’ì’ì’ììçìççÊðîÒÒþþþþþþþþùùÒ!þþþþþþþþþþ!!þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþÒÒ%þþþþþÒ'*)10(///(01) Ẻºáá¾¾ë÷÷)%÷!úüûüûþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ4xve©tt‹¦eexxxxxxóççìíÉÉŒæŒÚ²Ú²à²àÇà²åœ×œ±ˆ°‡¯m|r>?6|š°°Æ±Ùœ××ÇÇÇDzÇÇ˧¾¾%Ò%ÒÒòòâééÝÝè¶ØØØèèõõââòòÒ%%-Ò%%÷©¦Ì³ŒŒæ³Œ³æ³æ³³³³È³ÈëÈë²ÇÆƇ›š®™™†[ZII<<N66BBB6N\Pˆ±ÇÇÚÚÚÚÈÈÈÚÈÚڋڧξ%%%%%òïñéÝ¢¶¶Ø¶èÝÝññ%$%$-$$1©©ÏÌÞŒÉÛÛÛÛæÛæÛæÛæÛ³ÛæÛææÚàœ±°›š®|‘rrO†M[Z{Nrn‹³ÉÉì’çìççççóÊóxôþþþþþþþþÒúûÒþþþþþþþþþþùûû%ùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'**11000#)))!ú!ú!úúúúûûûûüüýüýýýýýýýýýýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýýýýüüüüüúúú!ú!ú©©úþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ44xxÊxx44þþþþþþþööxxçÊçìÉÉÛɌیÛÉÉÉæŒæ³³ÈÈ‹Ú‚‚ˆˆnm\>ON??Nrmˆ‚Ú‹³ŒæŒæŒæŒæ³³ÈȦ¦ÎÎ÷%$%-%%%òòïééÝÝè¶èÝÝéßïò%$$----$!©ÌÌތɌɌɌییییæŒÛŒëÈà×Ùˆ°šš®®\™rr>‘t‹Ée’’ìvççççÊxó4ööùùþþþþþþþþ©ùþþþþþþþþþþþþþþþþ!ùùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'))1%%%%ä111)!**2ü33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ4þ44üüú!!úþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöööþþþþþþþþþþþþöôôxÊççííììíí’í’çìç’í’’ÉÉŒŒ³Œ‹‹ttnP\\\\mt‹ŒŒÉÉí’í’í’íÉÉÉÉŒÛÌÌ©©$-$-$%ïïßéݬݢݡéïï$---,-#)!úÏyÞ’É’É’ÉíÉíÉÉÛÉÛÉÛÉÛÉÛÛ³ÚÇDZְ›šš®®‘‘™‘‘¯ÇŒÉíìççççóxóxôööþþÒùþþÒþþþþþþþþþÒüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþû2úú!!äääääää÷ ÷))1100(((11)*'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüú!!!©þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööxóçÊÊÊÊÊÊÊçÊÊÊxóÊÊçÊççv’ÉÉŒŒ‹¦ttntnt‹Œe’vçççççìçççv’’’ÞÞÏÏ©$----$$ïñééݢݥ¥ñßï $$---,-#)!úyîy’’’ÉìÉíÉ’É’ÉÉÉÉÛÉÉÉÛÉŒ³‹²ÇÙ°°›¯šš®®®š°ÚŒ’’ççÊÊxxô4ööþþþþúüþþýÒþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûûû!ú÷ ÍÍááºááÍá #0((///(01)*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ44ýýûû!!!þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöxöôôôôôöööööööööööxôxÊçç’’ee¦e¦eŒeevvÊçÊÊÊÊÊÊÊçÊççvv’yÏú1"---$$ïïé¡¢ééééïï-",,."#1)úûyîvçvçvçìììì’í’í’íÉíÉí’ÉÛÛÈÚÇ×±ÖÅևśÅÅ×ÈíìÊÊóxôxööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûüûû!!1#1))) áºß¹ß¹ßïïã #"(/&/((11**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöþöþþþþþþþþþöþööôôxxÊÊ’v’’eeev’ççÊÊxÊxÊxÊxÊÊÊÊçÊvvÞyú!$,----$ ññ¥¥¥é¥ßßïï -"-,-,,"$1*úûyçvçvçvçvì’ì’ì’ì’í’ìÉìÉíÛ۳ڲǜ±Æ±Æƈ±²ÛìÊÊóxôôööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûüúú!©ú!ú!)!)#000000# ïïßߥßßßß #"0+/.//01))'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööööxôxxÊÊÊÊvÊÊÊÊóxôxôxôxôxôxóÊóÊxîÊûû!)#,,.,-"" ñ¹¡é¥ß¡¹ñ #"",+,+,#1)!*ûîîÊÊÊçÊççççççìçççìçìì’ì’í’ÉÛëÚÚÚåÇÇÇDz³’óxôôööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúûú!!!!úûüüý 2'*))00///((0## ïßßßß¹ß 1#0((///(0))'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööööxôxóxxÊxxxxóxôxôxôxôxôxóxxxxÊÊîîûú!)",,-,-"$ ïïßߥé¥ßßïï-(,+.+,#)!'ûîîÊÊÊÊÊçÊçÊççççìçvì’ì’ì’ìÉÉŒæ³æÈæÈë³ëÉçxööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüúú©©úúûüý4þþþþþþ 2'*)100//./(0"#ïïßߥßßïï #((//.&(11**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööööööôö4ô4ôôöôôôôôôôôôôôôôô4ôxôxxî2**)1"(,+,,"# ïïßñ¥é¥ßßï "((+++("11*ú'üêêôxóxxÊóÊÊçÊççççìç’ììçìçìçìí’ÉÉíÉÉ’çxöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûúú!©©!!úûýýþþþþþþþþþþþ ''))10(/.&&""# ïïßßßßßïï #"0&/.&(01)*'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþööööööööööööööôöôöôöôöôô4ô4ô4ô4ôêýü'!)#,+.+,," ññéߥééñßï "",+.++(01*ú'üêêxxxÊÊÊÊçÊççççììvìvììççççÊçÊÊççÊÊÊ4þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüûû!!©úúüüý4þþþþþþþþþþþþþþþþ3 ''*)10(&.//((" áïßߥߥßß #"0(&../001)*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöþöþöþöööööööööööööööö4öý ü'))#0(+,+,""# ïñéߥßßïï #""(+//(01))*'2ýýxôxóxóÊóÊÊÊÊçÊçÊçÊçÊÊóxóxôôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûüú!©!!úúüýþþþþþþþþþþþþþþþþþþþþþ2'**110(/&/((### ïïßßß¹ßï ###"(/&/(01)**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþöþöþöþöþöþöþöþöö4ö44ü2û*)10(.+/(,"" ïßߥߥßßïï #((//./(01)!*û2ü4xxxxÊóÊÊÊÊÊÊÊóÊóxxxóxô4ööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûûú©!!úúûý44þþþþþþþþþþþþþþþþþþþþþþþþþþ 22**110(///&&""# ïïßߥßßïï #"(//./(01)*'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**11((/++("# ïïßߥßßßß ###((/&/((11)*'û ýöýôôôxôôôôôôôôôôôôööööþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüúú!!úûüýý4þþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*))00///&(0"# ïïßߥßßïï #"0(&///0#))'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'*)10&/./&""#ïïßߥßßßï #""((///((11)ú'ü3ý44ö4ö4ööööööööþöþöþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûû!úúüý4þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ32'*))00//.//("# ïïßߥßßßßï "0((//.(01)*'23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '*))#0///&(0## ïïßߥ¹ßïï ##0((///(01)**22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûû!!!ûüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))10(&/&/""#1 ïßßßßßßï 1##"(&&.((11**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ32**110(///((0 áïßߥߥßßïï #""(&///001)**2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûú!úüýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))11(&///((0 ßߥߥßßïï#""&/./&00))'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'*)11(&.&/("## ïßߥߥßßï #""&&//&00))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúü!!úüýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**11((///((0##ïïßߥßßïï ##0(&///00))*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))00//.//""# ïïßߥßßßßï #((//./(00))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûû!©üýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'*)110(//.&(0"#ïïßߥߥßßá #0((../(011**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '*))00///((0##ïïßßéßßïï ###"(/&/((11))''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûüú©!!ü4þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**)100///((0"# ïß¹ßߥ¹ßï #0(/&/&(11)*'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**110(.//((0## ßßßߥßßïï #"0((///&(11))''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüû!÷÷!!üýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**)100&//&(""# ïßߥߥßßïï #""&/./&0#))*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ2'*)1#(&/&/("#1 áßߥ¹¥ßßïï #"0((/&/((11)*'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûú!!!üýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**)100&&/(&""## á¹ßßߥßßïï ##0(&///001)*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '*))00&/./&""# ïïßߥߥßßïï #"0((///(011)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ!ûýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))00&/.//((# ßߥߥßßßß 0((//.((11**22þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '*))00///&(0## ïïßߥßßßßï #"0((/&/(011**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûü!!!úýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**))10(&/&&("### ïïßߥßßßß 1##"(&&.&(11**'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**110(///((0# ïߥߥߥßßïï #""&(/&/(011)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûû!!ûþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2**))00((///(("# ïïßߥ¥¥ßßïï #""&/./&00))'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*)11((/&/("### ¹¹¥ß¥ß¥ß¹ #"0((/&&001))*'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþû!!ýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**))11((/&/((#"# ïïßߥߥßßïï #"0(&///00))*' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))00//.//("# ïïßߥ¥¥ß¥ßß "#""&&/&/(011)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))00((///((""# ïïßߥ¥¥ß¥ßï #"0((.//(011**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''))00&//&(0"# ïß¹¥¥¥ßßïï ##"0((/((001)**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2**))11((/&&((### ïïß¹¥¹¥ßßïï ###"(/&/((11**'2þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**110(///&(0"# ïïßߥ¥¥ß¥¹ï #""((/&&0011)*''23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))00((///((""# ïïßߥ¥¥¥¥ßßïá #"(//./(01))'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '**11((///((##1ï½ßߥ¥¥¹ßïï ##"0((/&(0011)*''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))11((/&/((## áßߥߥ¥¥¹ßïá #""(&//&01))'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)10(/.//((# ßߥ¥¥¥¥ßß¹ï "#""+&/&/(011))*'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**))00((///((""# ïïßߥ¥¥¥¥ßßïï #"0(&///(01)**23þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))10(&/&&0"#1 ßߥ¡¥¥¥¹ßïï ###"0((/((0011))''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))1#((/&&((#"# ßߥ¹¥¥¥¹ßïï ##0((///(01)**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ32'*))00////(""# ïßߥ¥…¥¥ßßïï #""((/&/(011))**22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''**)100((///((""# ïßߥ¥¥¥¥¥¥ïï ##((//.((11)*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**)10(///&(0"# ïïßߥ¥¥¥¥¹ßïï ##"0((/&/(011))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**)100((/&/((#"# ßߥ¥¥¥¥ß¥ñ½ 1##"(&//((#1)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*)110(//.((0"#ïïß饥…¥¥ßßïï "#""+&///((00))**'' 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22'*))1100&&///((""# áïßߥ¥…¥…¥¥ßßï #"(&/.//00))*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*)110(/&&((### ïïßߥ¥¥¥¥¹ßïï ###"0((/&/((11))**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22'*))1100(&/&&((### ïߥ¡¥¥¥¥¥ßß 1#"0(&//&00))*' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''))1#(&/&/((# ïߥ¥…¥…¥¥ßßïï #""((///((0011))*'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 ''**))110(/////((""# áïßߥ¥…¥…¥¥ßßïï #"0(&.//(01)**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''))10(&/&&""###½ß¥¥¥¥¥¥¥¹ßïï ##"0((/&/((0011))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''**))1#((///&/((## ïߥ¥¥¥…¥¥ßßïï #"0((///(01)**' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*)100///&/""# ïßߥ¥…¥…¥¥ßßïï "#""((//.//0011))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))1100((//./&((""# áïßߥ¥…¥…¥¥ßßïï#""((///((11)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**)100&//((0"# áß¹¥¥…¥¥¥¥¹ßïï #"0((/&///(011))**''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))1100((/&/((""## ïߥ¡¥¥…¥¥ßßïï ###"(/&/((11)*'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**110(///&(0"# áïßߥ¥…¥…¥¥ßßïï #"0((/////((0011))**'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 22''**))1100/////(("""# ïßߥ¥…¥…¥¥¥¥¹ï ##"(///&(01))*' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**110(///((0"# ïßߥ¥…¡…¥¥¹ßïï #"0((/&///((0011))**''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**)*)101((/////((""## ïߥ¥¥¥…¥¥¥¥ïï ##"(&&/&(01))''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)11((///((""# ïïßߥ¥…¥…¥¥ßßïï #"0((+&././/((0011))**''2 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 3 22''**))1100((././/((0"# áïßߥ¥…¥…¥…¥¥ßï #""/////001)**22þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)11((/&/("0## ïßߥ¥¥¥¥¥¥¹ßïï ##"0((/&///((0011))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''****))1100(//&/&/((0"# ïߥ¡¥¥…\¥¥¥ïï #"0((//&001)**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '*))10((//&((""# ïïßߥ¥…¥…¥¥ßßïï ##"0((//.///&000011))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''*'**))1100((//.//&+((0"# áïßߥ¥…¥…¥…¥¥ßï #"0(&///0011**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ '*))10((/&&("### ïïßߥ¥…¥¥¥¥¹ßïï ##"0((&&/////((0011))****''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**)))11100&&//.&/((0"## ááßߥ¥¥¥…¥¥ß¥ïï #"0((///0011**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2**))00(&//&((" ïïßߥ¥…¥…¥¥ßß¹ï #""((+&//.//&/(00011))****''22 333þþþþþþþþþþþþþþþþþþþþþþþþþþ33 22''''**))111100((//././/((""# ïïßߥ¥…¥…¥¥¥¥ßï #"0((///(011)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*))10((/&(0"## ïß¹¥¥¥¥¥¥¥¹ßïï ###"0((/&//.&/((0011))))****''22 33þþþþþþþþ3 22''''**)*))110000(&//.&/((((0"## ïïßߥ¡¥¥¥¥¥ßßïï #00((&&/(011**'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 '**)100(//&("(# ïïßߥ¥¥¥…¥¥ß¥¹ßá ##"0((&&//././/((001111))****'*''2222 3 3333 3 3 3 22''''****))))110000((//././/(("(0"# ïïßߥ¥¥¥…¥¥ßßïï #"0((/&/((11)*'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**)100&&/((0"## á¹ïßߥ¥¥¥¥¹ßß½ ###"#""((//././/((((0011)1))*)**'*''''2'2222 2 2 2 2 22'2''''*'****))))111#00((////.&/&&((0"### ïïßߥߥ¥¥¥¥ßßïï #"0((&&/((11))'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**1100&//&(""# áïßߥ饥…¥¥ß¥ßßïï #"#""((////.../.&&((001011)1))*)******'*'*'''''''''*'*'******)*))))11110000((//..././/((((0"# ïïßߥ¥¥¥¥¥¥ßßïï #"0((/&/((01))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**)100&(&((0"## ïïßߥ¹¥¥¥ßß¹ßïï ###0"0((+&/&././/((((001011)1))))*)*)*****************)*)*))1)11110000((/&//.&/&/(&((0"## ïïßߥ¹¥¥¥¹ßßßï #"0((&&/(011))'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'*)1100&//&(""## ïïßߥ¥¥¥¥ß¥ßßïï #"#"0((((//./././//&((0(00101#11)1)1)1)))))))))))1)1)1111010#00((/(//./...//&/((""""# ïß¹ßߥ¥¥¥¥ßßßßï #"0((&&/((#1))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**1100&(&((0"### ïïßߥߥߥߥßßïï ###"#"0((&(/&//././/&/(&((0(00#01011111111111111111010000(0((&&////.&///&/((0"0"#### ïïß½ßߥߥߥßßïï #"0((&&/((#1))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)110(&//&("(0# ïïßßßߥ¥¥ß¥ßßïßïï ##""("((+(////./././///&&((((0(0(00000000000(0((((/(/&////..././/&/((((""#"#  ïïßßßߥ¥¥ß¥ßßïï #"0((/&/((0#))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**1100&(&((00#1 ïïß¹éߥßéßßïïïï ####"#"0("((/&/&/&/&.////&/&/(&(((((&(((&(((/(/&/////&/&.&/&/(&((0(0"#### ïïß¹ßߥ¹¥ßßï¹ïï ##"0((&&/((11))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)1100&//&(((""# ïïßߥߥߥßßßßïïá ##"#""""((((/&/&//////././////////////////////.////////(&(("(""""## ïïßïßߥߥ¥¥ßßßßïï #"0((/&/((##))**2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**1100&&/&&"(0## ïïßïßߥ¹¥ßßßßïï ####"#""("((((+&/&/+/&/+/&///&///&///&/+/&/&/&+(&((((""0"### ïïßßß¹¥ß¥ß¥ßßïï ##"0((&&/((#1))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''*)1100&////((0"# ïïß½ßߥߥߥßßßßïï  #"#""""("("((((,(+(+&+&+&+&+&+&+&+&+(+(,(((("(""""#"#  ïïßïßߥߥߥߥßßïï ##""(&///((01))**22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**)100((/&/((0"### ïïßßßßßߥ¹ßßßïïá ###"#"#""""("("("("("&"("("("("(""""""#"#"### ïïïïßßßßßߥ¹ßïïïï ###"0((///(011))**2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**1100(&///((0"# ïïßßßߥߥߥßßïßïïï """"""""""""""""""""""""""""  ïïïïßßßߥߥߥßßï¹ï #"0((&&///((11))**2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**))00((///((0"### ïïïßßßߥßßßßßßï½áï "#"""""" ïïß½ßßßߥßßßßïßïï 1#"0((&(///(011))*'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**)100((///&+("#"# ïïßßßߥߥßßßßßßï½ïï $   ïïïß¹ßߥߥߥߥßß¹ßïï #"0((/////(011))**2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))11((///((""###1 ïïïïßßß¹ßßß¹ßßßïïïï   ïïïßïßßß¹ßßß¹ßïßïï 1##"0((/&//&0011)*''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))00((///&/((0"## áïïßßßߥߥߥßßñß¹ßïïïïá      ïïïßïßßßßßߥߥߥßßïßïï ###""((///&&0011)*''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))11((///&&((0"### ïáïïß¹ßߥߥߥßß¹ßïßïïáïá          ïáïï½ïß½ßßßߥߥßßßßïßïï ##"0(0(&///&(0011**'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22**))110(/////&("(0"# ïïßßßߥߥߥߥߥßßßßïßïïïïïï½              ïïïïïßïßßßßßߥߥߥߥßßßß¹ßïï ##"0((/&.//((0011)*'' þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2''))1100(&/&/&(0"0"### áïïßßß¹ßߥßéߥßßßß¹ßïßïïïïïïáïïïïïïïïïïïïßïßßß¹ßߥßéߥßßßß¹ßïïïï ###"0((&&///((11))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**)1100(&////&((0"##  áïïßïßñ¥ß¥ß¥ß¥ß¥ß¥ß¥ßßßßßßßß¹ßïßïßïßïßïßïßïßïßïßïßïßï¹ïßïßïßïßßßßßߥߥߥߥߥߥߥߥßßßßïïïï  #"0""(&//.//((01))**'' 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**)100((/////((0"###1 ïïßïßßßߥߥߥߥߥߥߥߥߥߥ¹ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßߥߥߥߥߥߥߥߥߥßßßßïïïï 1##"0((&&///&&0011))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**)110((//.//&/((0"##  ïïïïßßßߥߥߥ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ß¥ßßßßïßïï½ ##""((//././(0011))*'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**))1100&&///&&((0"##1# ïïïßïßßßߥߥ¹¥¥¥¥¥¥¥¥¥¥¥¡¥¥¥\¥¥…¡…¥…¥…¥…¡¥¥¥\¥¥¥¥¥¥¥¥¥¥¥¡¥¥¥¹¥ß¥¹ß¹ßïïïï  ##"0"0((/&///((00))**''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))1100((//.//&("(0"##  ïïïïßßßߥߥ¥¥¥¥¥…¥…¥…¥…¥………¥………………………………………¥…¥…¥…¥…¥…¥¥¥¥ß¥ß¥ßßïßïïï  ###"0((&&//.//((#01)**''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''*)1)10((//.&/&/((0"###1  ïïïßïßßßߥߥ¥¥¥¥¥¥¥…¥…¥…¥……………………………¥…¥…¡…¥…¥¥¥¥¹¥ß¥¹ßñßïïïï 1##"#"0((&////&&0011))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**))1#0(//././/((((0"## ïïïïß¹ßߥߥߥ¥¥¥…¥…¥………¥……………………………¥…¥…¥…¥¥¥¥ßßßßßßïßïïï   ##""((&///.//((0011))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ ''**))1100((/&/&/(&((0"#####  ïïïï¹ïß¹ßߥߥ¥¥¡¥¥¥¥¥¥¥\¥¥¥¥¥¥¥¡¥ß¥ßßßßïßïïïïáï  1###00"0((/&///&&(011))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2'**))1100((//././/((((""#"#   ïïïßïßßßߥߥߥ¥¥¥¥¥¥¥¥¥¥ß¥ßßßßßßïßïïïï  ##"0"0((/&//.//&(0011))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 2''**))1100((//.//&/((""#"####  ïïïïïïïßïßßßßßß߽߽ßïßï½ïïáï   #####"0((((///////((0011))**'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 ''**))1100((//././/&&((0(""##   ááïïïïïïïïïïïïïïïï   #"""0((((////.//&/(0111)*)*'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''**))1100((/////&/(("(0"######            #####"0"0((&&/&.////((0#11))**''2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**))1100((//././///((((0"0"###      ##"#"0("((/&//././/((0011)1))**'2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'**)*))1100((//./.&/&&((0(0"#"########"#"0""((((/&//.//&&((0011))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**))1100(0////..././/(+(("(""""##"##"#"""0((((/&//./././/((00111)))**'' 3 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 2'*'**))11000(&(//.//&/&+(("(0(0"0"##########"#"0"0("((&&/&/&.////((001111))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**))111100((////././///(/(((("(0"""#""#""#"#"#"#"""0""("((&(////./././/(((0#011))****'2 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''**))1)1100((&(////.//&/&/(&(("("(0"0"#"#"#""#""#""##"#"#"#"""#"""0("((((/(//////.////((001011))))**''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**)*))110100((////./././///+/(+(((&(("("("("""""""""""""""("("((((,(&(/&////././././/(/((0011)1))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 22''****))1)110000((///&//.&///&/&/(&(&(((("((("("("("("((("((((&(/(/&/&/&///////(&((001011))))**''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ 3 22''**))))11110000((&(//////.//////////&/&/&+(/&+(/&+&/&/&////////././/////((((001011)1))****''2 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''**)*))1)110100(0((&(/&/&///////&///&///&/&/&/&/&///&///////&/(/((((00001111))))**'*'222 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 2 22''**)*))1)11010000(0((((/&/&////////.///.///.////////&/(/((((0000101111))))****''22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 22''''****))))1)11#1010000(0(0((((&(((&(((&((((0(0(000010101111)1))*)****'''222 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 22''''****))))1)1111110101000#00000000000#000101010111111))))*)****''2'2 3 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 222''*'****)*)*))1)))1)111111111111111)111)1)1))))*)*)****''''22 þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ333 22'2''*'****)*)*)))))))))))))))))))))))*)*)******''''22 2 3 33þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3 222''''''*'*'*'***************'*'*''''''2'22 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ33 3 3 2 22'2'2'2'''''''''''''''''2'2222 3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3   3þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ3333333333333þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ \ No newline at end of file diff --git a/data/icons/softupdate.raw b/data/icons/softupdate.raw new file mode 100644 index 0000000000000000000000000000000000000000..f56892173960a0733a2cd8c2d8a0fee758ccb7df GIT binary patch literal 293 zcmY+9u@%4|2t-{VeQFkPJV%I4URFzK;}SMWcL5cGSAFjQVGn!QYRO$^Wt9u8lpVhk zOdk>`OE5UFl;JqyF`twh5yj~<{tQ?R0oEINA+2Y(DL2Q<9Ddke(yTjpmMgn|q#@q( JmNU8*{s4%@yp8|> literal 0 HcmV?d00001 diff --git a/data/icons/streaming.raw b/data/icons/streaming.raw new file mode 100644 index 0000000000000000000000000000000000000000..f551a53891c9132a67839fe943068a67cb17fd1c GIT binary patch literal 293 zcmY+9K@xx<3T^EV_?X J_7M%baRI-aKF0t6 literal 0 HcmV?d00001 diff --git a/data/icons/subt_gray.raw b/data/icons/subt_gray.raw new file mode 100644 index 0000000000000000000000000000000000000000..d21fbba9b1fa07bb1bc7c1dcff7acadad76588bd GIT binary patch literal 133 zcmZ9EF$w@M3<4c9_Q{SuyCC3LlxdK(R(VOXl8AEfW1LWV#-DEZK#_hL=;cP&YoY&J(~ zgfeKE>?3fj!wt$|tj3>;*AHU@ q6H~smd$OQrs{lLgyFjLALDZ~*I|hItH0^%C|IQqE3uLYN{F58>*1S&u literal 0 HcmV?d00001 diff --git a/data/icons/up.raw b/data/icons/up.raw new file mode 100644 index 0000000000000000000000000000000000000000..a6df634d6c0bdadb1ed8cb0921198dca15b6c3e0 GIT binary patch literal 293 zcma)0F%AGR2y*lZeXgThzk8bpVshzpB`Kw#4a=5YBLNLNWoGat*$L%-$mZlKayV#L jF$f&k3S=vwKf5jew14yczn0Kw1g^828ck=lX8RkwTCQd7 literal 0 HcmV?d00001 diff --git a/data/icons/upnp.raw b/data/icons/upnp.raw new file mode 100644 index 0000000000000000000000000000000000000000..4526efc8bff8e54f6530292117a9a094b89e0b85 GIT binary patch literal 269 zcmZXPK?=Yy2n2KM4@!S3yIKmVBxV>iiRZY+VT$Q~7nvIuV?uEwWRyxw z*-Bxtl9)kU0gNP?5Mo1=hwMUm*HZNjXlJY1ewgU#0$d}+vwM8M?g4o8SU6i^Jb&c_ DrG%Q| literal 0 HcmV?d00001 diff --git a/data/icons/video.raw b/data/icons/video.raw new file mode 100644 index 0000000000000000000000000000000000000000..9c561cde89d362a5d63ada11aea175bdba139d79 GIT binary patch literal 293 zcmbu4F%H8Z3`IS%psqO(VN;jD%WaD%E75*@1knxs-l-1g>3t? zNb5jUGNtfNtdx^;zOdpc4*=Rwt0v$r0{>)7A>nbC%^@-Y{?@P35$KZOWt(}dW@dNH ojS^p?`y+BKp<{AJvCU+-$&cJxrPB58`jt?hU19p2p1(c(0kZhTH~;_u literal 0 HcmV?d00001 diff --git a/data/icons/volume.raw b/data/icons/volume.raw new file mode 100644 index 0000000000000000000000000000000000000000..64fbc9c1237c53fc2465d2ab589189ea4729efe0 GIT binary patch literal 4613 zcmeH}yBWhE41|?JWwyvbG@8Y0dI9fZ34tJ=`@0mOG2PGI>0x<&)iN{OrjS z@UoX4Tzm&s?i{?xcmT!y&^$_jC4U+~=0EH|Ag2O&$cNyMj*SW|zK$D~zw9@W7Eb{3 zy#tD`&hHj5_zpL9`C$NuZ%ti3k?(W{KZ{|Q%25EE|M>$ru=T<1pO8N%VBvw6}~1@ literal 0 HcmV?d00001 diff --git a/data/icons/volumebody.raw b/data/icons/volumebody.raw new file mode 100644 index 0000000000000000000000000000000000000000..2f2a8e779b0b967e73197ee7c1ca33f717be99b9 GIT binary patch literal 665 vcmbY-^)QXhzq1aT$1F=L4X%# H56oTwHqAYK literal 0 HcmV?d00001 diff --git a/data/icons/volumeslider2alpha.raw b/data/icons/volumeslider2alpha.raw new file mode 100644 index 0000000000000000000000000000000000000000..24744c390ae68ad0a03490d81c54f117842cfdc3 GIT binary patch literal 133 zcmWe&5MbcF3k4S#z~CYKRQ4(hzq1aT$1EFK!6u! H56oTw1a?6I literal 0 HcmV?d00001 diff --git a/data/icons/volumeslider2blue.raw b/data/icons/volumeslider2blue.raw new file mode 100644 index 0000000000000000000000000000000000000000..2a07eadf8bc30188bf4126e66c2c3b7f916c9802 GIT binary patch literal 133 zcmWe&5MbcF3k4S#z~C5~#Kzk(77^6~>YpOio}hzq1aT$1FKK!6u! H56oTwGLI|i literal 0 HcmV?d00001 diff --git a/data/icons/volumeslider2red.raw b/data/icons/volumeslider2red.raw new file mode 100644 index 0000000000000000000000000000000000000000..e89cba548ba647125b23d46101012a1bca14ac59 GIT binary patch literal 133 zcmWe&5MbcF3k4S#z~C(RfSAz$0D&tl(EtDd literal 0 HcmV?d00001 diff --git a/data/inetradio/radio-stations.xml b/data/inetradio/radio-stations.xml new file mode 100644 index 000000000..f5f121944 --- /dev/null +++ b/data/inetradio/radio-stations.xml @@ -0,0 +1,892 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/iso-codes/Makefile.am b/data/iso-codes/Makefile.am new file mode 100644 index 000000000..e5ddfc030 --- /dev/null +++ b/data/iso-codes/Makefile.am @@ -0,0 +1,5 @@ +installdir = $(datadir)/iso-codes + +install_DATA = \ + iso-639.tab + diff --git a/data/iso-codes/iso-639.tab b/data/iso-codes/iso-639.tab new file mode 100644 index 000000000..445b0f32f --- /dev/null +++ b/data/iso-codes/iso-639.tab @@ -0,0 +1,489 @@ +## iso-639.tab +## +## Copyright (C) 2004 Alastair McKinstry +## Released under the GNU License; see file COPYING for details +## +## Last update: 2004-03-29 +## +## This file gives a list of all languages in the ISO-639 +## standard, and is used to provide translations (via gettext) +## +## Status: ISO 639-2:1998 + additions and changes until 2003-03-05 +## Source: http://lcweb.loc.gov/standards/iso639-2/englangn.html +## +## Columns: +## iso-639-2 terminology code +## iso-639-2 bibliography code +## iso-639-1 code (XX if none exists) +## Name (English) +## +## Notes to translators: +## Please translate Bøkmal using a-ring (U+00E5) +## Please translate Proveçal using c-cedilla (U+00E7) +## +aar aar aa Afar +abk abk ab Abkhazian +ace ace XX Achinese +ach ach XX Acoli +ada ada XX Adangme +ady ady XX Adyghe; Adygei +afa afa XX Afro-Asiatic (Other) +afh afh XX Afrihili +afr afr af Afrikaans +aka aka ak Akan +akk akk ak Akkadian +alb sqi sq Albanian +ale ale XX Aleut +alg alg XX Algonquian languages +amh amh am Amharic +ang ang XX English, Old (ca.450-1100) +apa apa XX Apache languages +ara ara ar Arabic +arc arc XX Aramaic +arg arg an Aragonese +arm hye hy Armenian +arn arn XX Araucanian +arp arp XX Arapaho +art art XX Artificial (Other) +arw arw XX Arawak +asm asm as Assamese +ast ast XX Asturian; Bable +ath ath XX Athapascan language +aus aus XX Australian languages +ava ava av Avaric +ave ave ae Avestan +awa awa XX Awadhi +aym aym ay Aymara +aze aze az Azerbaijani +bad bad XX Banda +bai bai XX Bamileke languages +bak bak ba Bashkir +bal bal XX Baluchi +bam bam bm Bambara +ban ban XX Balinese +baq eus eu Basque +bas bas XX Basa +bat bat XX Baltic (Other) +bej bej XX Beja +bel bel be Belarusian +bem bem XX Bemba +ben ben bn Bengali +ber ber XX Berber (Other) +bho bho XX Bhojpuri +bih bih bh Bihari +bik bik XX Bikol +bin bin XX Bini +bis bis bi Bislama +bla bla XX Siksika +bnt bnt XX Bantu (Other) +bos bos bs Bosnian +bra bra XX Braj +bre bre br Breton +btk btk XX Batak (Indonesia) +bua bua XX Buriat +bug bug XX Buginese +bul bul bg Bulgarian +bur mya my Burmese +byn byn XX Blin; Bilin +cad cad XX Caddo +cai cai XX Central American Indian (Other) +car car XX Carib +cat cat ca Catalan +cau cau XX Caucasian (Other) +ceb ceb XX Cebuano +cel cel XX Celtic (Other) +cha cha ch Chamorro +chb chb XX Chibcha +che che ce Chechen +chg chg XX Chagatai +chi zho zh Chinese +chk chk XX Chukese +chm chm XX Mari +chn chn XX Chinook jargon +cho cho XX Choctaw +chp chp XX Chipewyan +chr chr XX Cherokee +chu chu XX Church Slavic +chv chv cv Chuvash +chy chy XX Cheyenne +cmc cmc XX Chamic languages +cop cop XX Coptic +cor cor kw Cornish +cos cos co Corsican +cpe cpe XX English-based (Other) +cpf cpf XX French-based (Other) +cpp cpp XX Portuguese-based (Other) +cre cre cr Cree +crh crh XX Crimean Turkish; Crimean Tatar +crp crp XX Creoles and pidgins (Other) +csb csb XX Kashubian +cus cus XX Cushitic (Other) +cus cus XX Portuguese-based (Other) +cze ces cs Czech +dak dak XX Dakota +dan dan da Danish +dar dar XX Dargwa +dsb dsb XX Lower Sorbian +del del XX Delaware +den den XX Slave (Athapascan) +dgr dgr XX Dogrib +din din XX Dinka +div div dv Divehi +doi doi XX Dogri +dra dra XX Dravidian (Other) +dua dua XX Duala +dum dum XX Dutch, Middle (ca. 1050-1350) +dut nld nl Dutch +dyu dyu XX Dyula +dzo dzo dz Dzongkha +efi efi XX Efik +egy egy XX Egyptian (Ancient) +eka eka XX Ekajuk +elx elx XX Elamite +eng eng en English +enm enm XX English, Middle (1100-1500) +epo epo eo Esperanto +est est et Estonian +ewe ewe ee Ewe +ewo ewo XX Ewondo +fan fan XX Fang +fao fao fo Faroese +fat fat XX Fanti +fij fij fj Fijian +fin fin fi Finnish +fiu fiu XX Finno-Ugrian (Other) +fon fon XX Fon +fre fra fr French +frm frm XX French, Middle (ca.1400-1600) +fro fro XX French, Old (842-ca.1400) +fry fry fy Frisian +ful ful ff Fulah +fur fur XX Friulian +gaa gaa ga Ga +gay gay XX Gayo +gba gba XX Gbaya +gem gem XX Germanic (Other) +geo kat ka Georgian +ger deu de German +gez gez XX Geez +gil gil XX Gilbertese +gla gla gd Gaelic; Scottish +gle gle ga Irish +glg glg gl Gallegan +glv glv gv Manx +gmh gmh XX German, Middle High (ca.1050-1500) +goh goh XX German, Old High (ca.750-1050) +gon gon XX Gondi +gor gor XX Gorontalo +got got XX Gothic +grb grb XX Grebo +grc grc XX Greek, Ancient (to 1453) +gre ell el Greek, Modern (1453-) +grn grn gn Guarani +guj guj gu Gujarati +gwi gwi XX Gwichin +hai hai XX Haida +hat hat ht Haitian; Haitian Creole +hau hau ha Hausa +haw haw XX Hawaiian +heb heb he Hebrew +her her hz Herero +hil hil XX Hiligaynon +him him XX Himachali +hin hin hi Hindi +hit hit XX Hittite +hmn hmn XX Hmong +hmo hmo ho Hiri +hsb hsb XX Upper Sorbian +hun hun hu Hungarian +hup hup XX Hupa +iba iba XX Iban +ibo ibo ig Igbo +ice isl is Icelandic +ido ido io Ido +iii iii ii Sichuan Yi +ijo ijo XX Ijo +iku iku iu Inuktitut +ile ile ie Interlingue +ilo ilo XX Iloko +ina ina ia Interlingua +inc inc XX Indic (Other) +ind ind id Indonesian +ine ine XX Indo-European (Other) +inh inh XX Ingush +ipk ipk ik Inupiaq +ira ira XX Iranian (Other) +iro iro XX Iroquoian languages +ita ita it Italian +jav jav jv Javanese +jbo jbo XX Lojban +jpn jpn ja Japanese +jpr jpr XX Judeo-Persian +jrb jrb XX Judeo-Arabic +kaa kaa XX Kara-Kalpak +kab kab XX Kabyle +kac kac XX Kachin +kal kal kl Greenlandic (Kalaallisut) +kam kam XX Kamba +kan kan kn Kannada +kar kar XX Karen +kas kas ks Kashmiri +kau kau kr Kanuri +kaw kaw XX Kawi +kaz kaz kk Kazakh +kbd kbd XX Kabardian +kha kha XX Khazi +khi khi XX Khoisan (Other) +khm khm km Khmer +kho kho XX Khotanese +kik kik ki Kikuyu +kin kin rw Kinyarwanda +kir kir ky Kirghiz +kmb kmb XX Kimbundu +kok kok XX Konkani +kom kom kv Komi +kon kon kg Kongo +kor kor ko Korean +kos kos XX Kosraean +kpe kpe XX Kpelle +krc krc XX Karachay-Balkar +kro kro XX Kru +kru kru XX Kurukh +kua kua kj Kuanyama +kum kum XX Kumyk +kur kur ku Kurdish +kut kut XX Kutenai +lad lad XX Ladino +lah lah XX Lahnda +lam lam XX Lamba +lao lao lo Lao +lat lat la Latin +lav lav lv Latvian +lez lez XX Lezghian +lim lim li Limburgian +lin lin ln Lingala +lit lit lt Lithuanian +lol lol XX Mongo +loz loz XX Lozi +ltz ltz lb Luxembourgish +lua lua XX Luba-Lulua +lub lub lu Luba-Katanga +lug lug lg Ganda +lui lui XX Luiseno +lun lun XX Lunda +luo luo XX Luo (Kenya and Tanzania) +lus lus XX Lushai +mac mkd mk Macedonian +mad mad XX Madurese +mag mag XX Magahi +mah mah mh Marshallese +mai mai XX Maithili +mak mak XX Makasar +mal mal ml Malayalam +man man XX Mandingo +mao mri mi Maori +map map XX Austronesian (Other) +mar mar mr Marathi +mas mas XX Masai +may msa ms Malay +mdf mdf XX Moksha +mdr mdr XX Mandar +men men XX Mende +mga mga XX Irish, Middle (900-1200) +mic mic XX Micmac +min min XX Minangkabau +mis mis XX Miscellaneous languages +mkh mkh XX Mon-Khmer (Other) +mlg mlg mg Malagasy +mlt mlt mt Maltese +mnc mnc XX Manchu +mno mno XX Manobo languages +moh moh XX Mohawk +mol mol mo Moldavian +mon mon mn Mongolian +mos mos XX Mossi +mul mul XX Multiple languages +mun mun XX Munda languages +mus mus XX Creek +mwr mwr XX Marwari +myn myn XX Mayan languages +myv myv XX Erzya +nah nah XX Nahuatl +nai nai XX North American Indian (Other) +nap nap XX Neapolitan +nau nau na Nauru +nav nav nv Navaho +nbl nbl nr Ndebele, South +nde nde nd Ndebele, North +ndo ndo ng Ndonga +nds nds XX German, Low +nep nep ne Nepali +new new XX Newari +nia nia XX Nias +nic nic XX Niger-Kordofanian (Other) +niu niu XX Niuean +nno nno nn Norwegian Nynorsk +nob nob nb Bøkmal, Norwegian +nog nog XX Nogai +non non XX Norse, Old +nor nor no Norwegian +nso nso XX Sotho, Northern +nub nub XX Nubian languages +nwc nwc XX Classical Newari; Old Newari +nya nya ny Chewa; Chichewa; Nyanja +nym nym XX Nyankole +nyo nyo XX Nyoro +nzi nzi XX Nzima +oci oci oc Occitan (post 1500) +oji oji oj Ojibwa +ori ori or Oriya +orm orm om Oromo +osa osa XX Osage +oss oss os Ossetian +ota ota XX Turkish, Ottoman (1500-1928) +oto oto XX Otomian languages +paa paa XX Papuan (Other) +pag pag XX Pangasinan +pal pal XX Pahlavi +pam pam XX Pampanga +pan pan pa Panjabi +pap pap XX Papiamento +pau pau XX Palauan +peo peo XX Persian, Old (ca.600-400 B.C.) +per fas fa Persian +phi phi XX Philippine (Other) +phn phn XX Phoenician +pli pli pi Pali +pol pol pl Polish +por por pt Portuguese +pon pon XX Pohnpeian +pra pra XX Prakrit languages +pro pro XX Proveçal, Old (to 1500) +pus pus ps Pushto +que que qu Quechua +raj raj XX Rajasthani +rap rap XX Rapanui +rar rar XX Rarotongan +roa roa XX Romance (Other) +roh roh rm Raeto-Romance +rom rom XX Romany +rum ron ro Romanian +run run rn Rundi +rus rus ru Russian +sad sad XX Sandawe +sag sag sg Sango +sah sah XX Yakut +sai sai XX South American Indian (Other) +sal sal XX Salishan languages +sam sam XX Samaritan Aramaic +san san sa Sanskrit +sas sas XX Sasak +sat sat XX Santali +scc srp sr Serbian +sco sco XX Scots +scr hrv hr Croatian +sel sel XX Selkup +sem sem XX Semitic (Other) +sga sga XX Irish, Old (to 900) +sgn sgn XX Sign languages +shn shn XX Shan +sid sid XX Sidamo +sin sin si Sinhalese +sio sio XX Siouan languages +sit sit XX Sino-Tibetan (Other) +sla sla XX Slavic (Other) +slo slk sk Slovak +slv slv sl Slovenian +sma sma XX Southern Sami +sme sme se Northern Sami +smi smi XX Sami languages (Other) +smj smj XX Lule Sami +smn smn XX Inari Sami +smo smo sm Samoan +sms sms XX Skolt Sami +sna sna sn Shona +snd snd sd Sindhi +snk snk XX Soninke +sog sog XX Sogdian +som som so Somali +son son XX Songhai +sot sot st Sotho, Southern +spa spa es Spanish (Castilian) +srd srd sc Sardinian +srr srr XX Serer +ssa ssa XX Nilo-Saharan (Other) +ssw ssw ss Swati +suk suk XX Sukuma +sun sun su Sundanese +sus sus XX Susu +sux sux XX Sumerian +swa swa sw Swahili +swe swe sv Swedish +syr syr XX Syriac +tah tah ty Tahitian +tai tai XX Tai (Other) +tam tam ta Tamil +tao tso ts Tsonga +tat tat tt Tatar +tel tel te Telugu +tem tem XX Timne +ter ter XX Tereno +tet tet XX Tetum +tgk tgk tg Tajik +tgl tgl tl Tagalog +tha tha th Thai +tib bod bo Tibetan +tig tig XX Tigre +tir tir ti Tigrinya +tiv tiv XX Tiv +tkl tkl XX Tokelau +tlh tlh XX Klingon; tlhIngan-Hol +tli tli XX Tlinglit +tmh tmh XX Tamashek +tog tog XX Tonga (Nyasa) +ton ton to Tonga (Tonga Islands) +tpi tpi XX Tok Pisin +tsi tsi XX Tsimshian +tsn tsn tn Tswana +tuk tuk tk Turkmen +tum tum XX Tumbuka +tup tup XX Tupi languages +tur tur tr Turkish +tut tut XX Altaic (Other) +tvl tvl XX Tuvalu +twi twi tw Twi +tyv tyv XX Tuvinian +udm udm XX Udmurt +uga uga XX Ugaritic +uig uig ug Uighur +ukr ukr uk Ukrainian +umb umb XX Umbundu +und und XX Undetermined +urd urd ur Urdu +uzb uzb uz Uzbek +vai vai XX Vai +ven ven ve Venda +vie vie vi Vietnamese +vol vol vo Volapuk +vot vot XX Votic +wak wak XX Wakashan languages +wal wal XX Walamo +war war XX Waray +was was XX Washo +wel cym cy Welsh +wen wen XX Sorbian languages +wln wln wa Walloon +wol wol wo Wolof +xal xal XX Kalmyk +xho xho xh Xhosa +yao yao XX Yao +yap yap XX Yapese +yid yid yi Yiddish +yor yor yo Yoruba +ypk ypk XX Yupik languages +zap zap XX Zapotec +zen zen XX Zenaga +zha zha za Chuang; Zhuang +znd znd XX Zande +zul zul zu Zulu +zun zun XX Zuni diff --git a/data/lcd/Makefile.am b/data/lcd/Makefile.am new file mode 100644 index 000000000..5de814315 --- /dev/null +++ b/data/lcd/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = clock icons diff --git a/data/lcd/clock/Makefile.am b/data/lcd/clock/Makefile.am new file mode 100644 index 000000000..9dbe826bd --- /dev/null +++ b/data/lcd/clock/Makefile.am @@ -0,0 +1,8 @@ +installdir = $(DATADIR)/lcdd/clock + +install_DATA = \ + d_a.bmp d_b.bmp d_c.bmp d_d.bmp d_e.bmp d_f.bmp d_g.bmp d_h.bmp d_i.bmp d_j.bmp \ + m_a.bmp m_b.bmp m_c.bmp m_d.bmp m_e.bmp m_f.bmp m_g.bmp m_h.bmp m_i.bmp m_j.bmp m_k.bmp m_l.bmp \ + t_a.bmp t_b.bmp t_c.bmp t_d.bmp t_e.bmp t_f.bmp t_g.bmp t_h.bmp t_i.bmp t_j.bmp \ + w_a.bmp w_b.bmp w_c.bmp w_d.bmp w_e.bmp w_f.bmp w_g.bmp + diff --git a/data/lcd/clock/d_a.bmp b/data/lcd/clock/d_a.bmp new file mode 100644 index 0000000000000000000000000000000000000000..754645dea61d2ea6abc7c137d3ceafaa8108d9a9 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7ZKq8rdY!C*4|Ns93$$t#Y5B@Q*e*oei{}}jfpx6#b X%R%uxpd1u4|M|zj2*eCPwIIC!p)4JE literal 0 HcmV?d00001 diff --git a/data/lcd/clock/d_b.bmp b/data/lcd/clock/d_b.bmp new file mode 100644 index 0000000000000000000000000000000000000000..235d6f938e6c1b3c791abbb35814450c680b8b10 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7ZKq8rdY!C*4|Ns93$$t!t5B@PQAma}}J`5KC*+`i2 N2T(l_Gk`EqF97%?A94Ty literal 0 HcmV?d00001 diff --git a/data/lcd/clock/d_c.bmp b/data/lcd/clock/d_c.bmp new file mode 100644 index 0000000000000000000000000000000000000000..662c4fc037c542276a3ae5e19293d20a44652baf GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7ZKq8rdY!C*4|Ns93$$t#|fBrGB12HoYGd}pozyQJ( YKx_lWazOcc5X}4!qK@GQP!6aU0NlhLl>h($ literal 0 HcmV?d00001 diff --git a/data/lcd/clock/d_d.bmp b/data/lcd/clock/d_d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2003e3d79838ad5fcbeb7b63d1b539251d9c74ea GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7ZKq8rdY!C*4|Ns93$$t!tAO110{{Z4Y{}}jfAeaG2 dGXTXH9sn^=oIwsKKM#oi{bOJT@j-Gxy#UfC9?t*( literal 0 HcmV?d00001 diff --git a/data/lcd/clock/d_e.bmp b/data/lcd/clock/d_e.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a166428dd59adbd4b59215778b9c2cd5eea58578 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2!~#&v$iN7ZKq8rdY!C*4|Ns93$$tzC1yEe^k3s$)0`pn?V_*kj YW)R-9sd9T literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_a.bmp b/data/lcd/clock/m_a.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3a7996b272c4ca9656ae9802edb907a35e0fe2c8 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97)AH<}`|SHa_X*D_KF)l{5R4h` bSsrIR|MNfN@t^k?4*<=20Kp7E8W{ipsDBvU literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_b.bmp b/data/lcd/clock/m_b.bmp new file mode 100644 index 0000000000000000000000000000000000000000..45be0f8e44eb1d93719fbbde605c9f772c72f901 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97(-!Q2i5{708M-vLI3~& literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_d.bmp b/data/lcd/clock/m_d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a9219b646a1986c4042e55f86f045c3f083d3f0b GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|NsAIU|JGg c7~Wa;F+8*W$MD>G55ohXvJX&P0M-I0093mcaR2}S literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_g.bmp b/data/lcd/clock/m_g.bmp new file mode 100644 index 0000000000000000000000000000000000000000..516bd75d7269821cc9c14124f5e251114e711729 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97)9i;I9^ile@c_$n%L5GGEgmp@ imw3QXF7tq)7=-6MU?^aCz)%6k1q>EI9s?MI^#TC4<{1Hk%m1>+ExyYiU|`^fV)h0G1^@)V9})ln literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_i.bmp b/data/lcd/clock/m_i.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1943d42c2846fb507efc39acd02d0facc4ab6da8 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97l6***jlrJ%ctw5x^FM}n*yR-O lf9GQcV)i@bef<58|H!i+{viWYE#ttz!2f`Of&Bmj0|27K7T*8> literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_j.bmp b/data/lcd/clock/m_j.bmp new file mode 100644 index 0000000000000000000000000000000000000000..07e4f8449fa85e3858fb8c51467536247f5c544f GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97)BK0~E95`Vv#_Y%V^LxG!=gYI jgzo_Hxw-<`<8>Cc2V`t49>~alcp$_6-~mt%2txn>m=zjq literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_k.bmp b/data/lcd/clock/m_k.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0176fe9954e49fcf418a49b247de6e2d5ba78934 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|Ns97(+?khD0ukz$B&QSZGIGgm$_5? jUG`pi`P_5m#XsBY3O~2k6)-T=RWLBvSU@mX6O;e|xWye< literal 0 HcmV?d00001 diff --git a/data/lcd/clock/m_l.bmp b/data/lcd/clock/m_l.bmp new file mode 100644 index 0000000000000000000000000000000000000000..968658e09a28216a79ecce0aa0e8963b36797b30 GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#0o$x0K|+yhz^*50w4?m|NsAo(2Sq|{XhKk-^b?+9}1rTDJUra iV^L5pBU4fQ<6lMLhyN7?3=9?z7#Qq9xB;vMP5=O;2pz2e literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_a.bmp b/data/lcd/clock/t_a.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b086f452f8db0571b2806e4be981443bd93f0f4 GIT binary patch literal 190 zcmZ9_Jq`jP6a?VGEw-e!Fu~FzSV>~Y!;^)i{0fMiXFmc V=HH#3UELiE8=N|!ZiM{(`va{VJU9RV literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_b.bmp b/data/lcd/clock/t_b.bmp new file mode 100644 index 0000000000000000000000000000000000000000..86e227b846e0654a430e2adf7629d0b41aa0f142 GIT binary patch literal 190 zcmZ?r-Nyg{c0fu4h$Vnn0f-ra5FIc91wa@C{{R0EVSvbbEZDw*fdLnmKfu6%!2f`3 j5dOfx|DXVfEr3`Ci2pJCfAAlOf#!qo2MD$Y(og^ZQM@E@ literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_c.bmp b/data/lcd/clock/t_c.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bb3ce953099d042fd5b9fd15184dac3f6f439a90 GIT binary patch literal 190 zcmZ9^u?@o@5CzbWDO|dB5eb(W!Bvpbdlzp2lSPc;Y(PnY?()wrBKe~yPKf*O?Sm^X zUQe27A;)vizz1Vd)oVEChNKNqI|5BKlJD#h`#>oJu>j$q3QP?c$#;P>Qe&2#R(gaY R627AUz6P%qPPuPg%nwrqIClU5 literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_d.bmp b/data/lcd/clock/t_d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..75ec0544dc65c0cc25a87afb693e4a4c1da8e0e8 GIT binary patch literal 190 zcmZ9_u?@m75QX7Cq)6$O0?Cw&Kt&|_?7{|t#ZV*`F&m_GVcqd;cRcIU*(crkzP#zV zsa)hJXBl+vFuPDVAr_W VNo@;X^Wdi4r5Uvs?Qe7X_Xh(-H=6(e literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_e.bmp b/data/lcd/clock/t_e.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ea506c38d0b0956e1d7f840e562650f0a249994b GIT binary patch literal 190 zcmZ?r-Nyg{c0fu4h$Vnn0f-ra5FIc91wa@C{{R0EVSq^a0|?9yWJB@)|9^n$K=>a+ zJrvimKVYz9|G*#*#Qe=beE7pZb|7Yc{NV%R=MM#pzkgUT{QM!qQ2XN_gZ+>HKnye+ Qi2468Ft9^0&`J;h0Q`$J<^TWy literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_f.bmp b/data/lcd/clock/t_f.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3b91796671ba07d983b9ec56ce3dcd372a7d1b4c GIT binary patch literal 190 zcmZ9Fu?>YV3`8%a2whr=FG0x&R21~=!UX?kvM)x-2q{w_ABT`AIP2M`vn4OKV-xFCpH?05w literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_g.bmp b/data/lcd/clock/t_g.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a2f4ca2b8652e8cdd292307e3a220cd1a5c8c830 GIT binary patch literal 190 zcmZ9FF%H5o3`Kun=-8D3sU3TSPDEnoF5DoMxEJcyYxD>iJybC-Wb3z{ZCUpB^YvBJ zgEGMlMhL`N7Y3CocW+J2Me2M}@4E)iihoRLgU_K9PB4obxUp}Ao5F3z!Eun SxH^#*DX!A6Lza5uVbc%(-ZqH< literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_h.bmp b/data/lcd/clock/t_h.bmp new file mode 100644 index 0000000000000000000000000000000000000000..95994b027b2be569d3f406e4c63ce96ef0e8168e GIT binary patch literal 190 zcmZ9_u?c`M5Czb$7M5u(1T7uG%7DGQxPeY4j^YR@Eke$JIYjV>M~;M_%*z4G#BHF4 zdMdd7DS4nYB4UHiwC#XDO?Lsx1)#o775Qc|x$u<|i;0B*cdaaqEVY!pMDfDwOwjR9 G_2vPI04<*Y literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_i.bmp b/data/lcd/clock/t_i.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cf9e49a1a9b6cec42b3ee6d268df12a44c8d98f0 GIT binary patch literal 190 zcmZ9FF%E-33TMD()gE{&mq{XBWw{Q4S8&oMB98m=U>R`iNJ-6n=@CMeQ1SOx}u%2WGFh<~o(Z YmhmZW!Yz%ouj_U>kVlU*5a;9f22)r(+yDRo literal 0 HcmV?d00001 diff --git a/data/lcd/clock/t_j.bmp b/data/lcd/clock/t_j.bmp new file mode 100644 index 0000000000000000000000000000000000000000..38afd4770f61440bd6624779cedd4c9557e419d3 GIT binary patch literal 190 zcmZ9^F%H8Z3@Kv7$ljwWdrNt+b<&_Yk^ S#Y5EQLRT?z-)ZK*=s5xK0yrH2 literal 0 HcmV?d00001 diff --git a/data/lcd/clock/w_a.bmp b/data/lcd/clock/w_a.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2f9acf744004a53122f97cbe1441028a5d09890a GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#1cR(0K|+yhz^*50w4?m|Ns97)9ep^{Nw-l=O3H(ybmnaeFY5F ieHIMA&&e?bUGK>$;{R86vj1LbmFg)%9Y5?MYd|*8=0s!&*8-)M> literal 0 HcmV?d00001 diff --git a/data/lcd/clock/w_d.bmp b/data/lcd/clock/w_d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a04b318006fbcf1e70225e9c9b03b4b1937e430f GIT binary patch literal 126 zcmZ?rtz&=yJ0PV2#1cR(0K|+yhz^*50w4?m|Ns97(=vs3{(&%G<(&^eT)w_Ut00006P)t-s00030 z|No`gpWOfe00Cl4M??UK1szBL008(&L_t(|+GF?+1>gVw|EItj&H&?o{{R0^!0P{S z{>S5tKQu(+89q)Bp1{G=(D30HO0aWuD5(w}O z(D{xm0w4tAOHj&3GoKJ&294%N4Q~c^)cBw;U_#@w*h5|O0K(@|fJ#8=A~b#w0Y18V ibn{ry_)KW_6953`zq*RtT~K!b0000w_Ut00006P)t-s00030 z|No`gpWOfe0P;yhK~#9!WB3mR-~a#rr@$J{0ONoD|Nl?G>i=;5$K#AYG(_VWK28vx zz`@hd@ZlKa&nTe$hsmGkRi2qM=Yxy=4-3|EhED><^D20Nd=|@}RqOvVd=>`E%YgYm zHi+J5_&h;CuCixN!-p2jzps$Y2b%YfL9`yhf6n}$Ydu09fVuSH{h~qOeaVRu_5Dq?L6MjC!AvFF0RPz}U2=EQi`Hn0CAOzz}P|8O$ zpAcULjpj!UZw7YM_!wAFZx40N0|-A<0V)Bd>(TfwB=XVqW0;R-KLG#$p(MOiBO<+l P00000NkvXXu0mjf-DikR literal 0 HcmV?d00001 diff --git a/data/lcd/icons/lcd3.png b/data/lcd/icons/lcd3.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a32acc2d19af50f440c84a6b7f70786af70d73 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^6+rC3$P6T3H!B7LDYgKg5LY1m|NsA`H|O64vZi>t zIEGZ*N=|TK{`&3z!~^zi0jw#JYHd{}`?x!6O8!bbTYS_02ZQpNLV1OnG>QN0E$8`y zHJBIPNT`u}^y7?=441D$OY`{~E%VP8aP-zQT&-ziVX9#+*{9siz;W=0(3DRX&!``n z?zs48;NjVgvZi(~xMy7Wp|j!3Z-zt9o0jHl#I_vcS32Mx-k&GOEj6F>Y#kE=!|una Vj%Qi9jsV@q;OXk;vd$@?2>|XbR>uGU literal 0 HcmV?d00001 diff --git a/data/lcd/icons/power.png b/data/lcd/icons/power.png new file mode 100644 index 0000000000000000000000000000000000000000..a3667e75c509698e81ddfb70b945356055437159 GIT binary patch literal 352 zcmV-m0iXVfP)w_Ut00006P)t-s00030 z|No`gpWOfe0ToF^K~#9!WB3mR-~a#rr@$J{0ONoD|Nl?G>i=;5$K#AYG(_VWK28vx zz`@hd@ZlKa&nTe$hsmGkRi2qM=Yxy=4-3|EhED><^D20Nd=|@}RqOvVd=>`E%YgYm zHi+J5_&h;CuCixN!-p2jzps$Y2b%YfL9`yhf6n}$Ydu09-0b*o+1r!s@{{S^(p!lS^7u~<;;UNd)KR^jj29)sRZ(u+PZ}tZaDDlDkfdM7H y82>S%#3wM7qr^9Z{eRT-!2cgLy`X6)9{>R5Sh7k+`t;WT00009A~P-mLRVuD eNFpulkUw2aWcatf=GXol2BF;F|J~K;nEx-xY)AwE literal 0 HcmV?d00001 diff --git a/data/lcd/icons/setup.png b/data/lcd/icons/setup.png new file mode 100644 index 0000000000000000000000000000000000000000..091945c2112e309433bf58cd18bbf801718a50f3 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^6+rC3$P6T3H!B7LDYgKg5LY1m|NsA`H|O64vL<-C zIEGZ*O8&vmdLJmd{D9OshR5~){{Qzs@aliTzvGX4>`h!AOWOGf%sX)K@`Vo%B_5yG zSn#L)0w&M$guHB2@f}O QI>_k^p00i_>zopr01bm+i2wiq literal 0 HcmV?d00001 diff --git a/data/locale/Makefile.am b/data/locale/Makefile.am new file mode 100644 index 000000000..b47235c3a --- /dev/null +++ b/data/locale/Makefile.am @@ -0,0 +1,46 @@ +installdir = $(DATADIR)/neutrino/locale + +install_DATA = \ + bayrisch.locale \ + bosanski.locale \ + ch-baslerdeutsch.locale \ + ch-berndeutsch.locale \ + deutsch.locale \ + english.locale \ + francais.locale \ + italiano.locale \ + nederlands.locale \ + polski.locale \ + portugues.locale \ + russkij.locale \ + suomi.locale \ + svenska.locale + + +if MAINTAINER_MODE + +locals: locals.h locals_intern.h + +ordercheck: deutsch.locale + cut -d' ' -f1 deutsch.locale | LC_ALL=C sort | uniq > /tmp/log + cut -d' ' -f1 deutsch.locale | uniq | diff - /tmp/log || \ + (echo "ERROR: deutsch.locale not ordered or contains empty lines" && false) + +locals.h: ordercheck deutsch.locale + cut -d' ' -f1 deutsch.locale | LC_ALL=C sort | uniq | tr [:lower:] [:upper:] | tr \. \_ | tr \- \_ | tr -d \? | ./create.locals.h + +locals_intern.h: ordercheck deutsch.locale + cut -d' ' -f1 deutsch.locale | LC_ALL=C sort | uniq | ./create.locals_intern.h + +check: locals.h locals_intern.h + diff locals.h ../../src/system + diff locals_intern.h ../../src/system + +install-locals: locals.h locals_intern.h + cp locals.h locals_intern.h ../../src/system + @echo "Consider committing .../neutrino/src/system/[locals.h,locals_intern.h]" + +locals-clean: + rm -f locals.h locals_intern.h + +endif diff --git a/data/locale/_readme.txt b/data/locale/_readme.txt new file mode 100644 index 000000000..97044e9d4 --- /dev/null +++ b/data/locale/_readme.txt @@ -0,0 +1,67 @@ +Format of .locale files: +------------------------ +character encoding: UTF-8 +filename suffix : .locale + +Files must be strictly alphabetically ordered, and must not contain +any empty lines. + +Destination of .locale files: +----------------------------- +directory: /var/tuxbox/config/locale or /share/tuxbox/neutrino/locale + +Master file: +------------ +deutsch.locale is considered the master file. + + +Verfication of .locale files: +----------------------------- +Use the check.locale.files shell script for detecting +- violations of the sorting order, +- missing translations and +- legacy strings. + + + +How do I add a new locale string? +--------------------------------- +1.) +First of all, add the new string to deutsch.locale while preserving +the ordering. Do not add any empty lines. Use `make ordercheck' to for +verification. + +2.) +Enter the directory apps/tuxbox/neutrino/data/locale. + +3.) +Create new versions of the files apps/tuxbox/neutrino/src/system/locals.h +and apps/tuxbox/neutrino/src/system/locals_intern.h +using the command `make locals.h locals_intern.h'. + +4.) +Check the modifications with `make check', or with +diff locals.h ../../src/system/locals.h +diff locals_intern.h ../../src/system/locals_intern.h + +5.) +Copy the replacement file to their destination with `make install-locals', +or with +cp -p locals.h ../../src/system/locals.h +cp -p locals_intern.h ../../src/system/locals_intern.h + +6.) +To the extent possible, update other locale file. For this, the +Perl-script create-locals-update.pl may be useful. + +7.) +If committing the changes to CVS, commit both the involved +locale-files, apps/tuxbox/neutrino/src/system/locals.h, and +as apps/tuxbox/neutrino/src/system/locals_intern.h. + +Useful tools: +------------- +- emacs (add '(file-coding-system-alist (quote (("\\.locale\\'" . utf-8-unix) ("" undecided)))) to .emacs or use "C-x c utf-8 C-x C-f deutsch.locale") +- iconv +- sort +- uxterm diff --git a/data/locale/bayrisch.locale b/data/locale/bayrisch.locale new file mode 100644 index 000000000..693b7265b --- /dev/null +++ b/data/locale/bayrisch.locale @@ -0,0 +1,708 @@ +EPGMenu.epgplus Vorschau Ãœbersicht +EPGMenu.eventinfo Info zur Sendung +EPGMenu.eventlist Vorschau aktuells Programm +EPGMenu.head EPG - Programm Informationa +EPGMenu.streaminfo technische Informationa +EPGPlus.head Vorschau Ãœbasicht (EPG Plus) +EPGPlus.record Aufnema +EPGPlus.refresh_epg Aktualisiern +EPGPlus.remind Vormerkn +EPGPlus.scroll_mode Roin +EPGPlus.stretch_mode Streckn +GENRE.ARTS.0 Kunst / Kultur +GENRE.ARTS.1 Darstellende Künste +GENRE.ARTS.10 Kunst-/Kultur-Magazin +GENRE.ARTS.11 Mode +GENRE.ARTS.2 fine arts +GENRE.ARTS.3 Religion +GENRE.ARTS.4 Volkskunst +GENRE.ARTS.5 Literatur +GENRE.ARTS.6 Film/Kino +GENRE.ARTS.7 experimental film/video +GENRE.ARTS.8 Rundfunk/Presse +GENRE.ARTS.9 Neue Medien +GENRE.CHILDRENs_PROGRAMMES.0 Kinder / Jugendprogramm +GENRE.CHILDRENs_PROGRAMMES.1 Programm für Vorschulkinder +GENRE.CHILDRENs_PROGRAMMES.2 Unterhaltungsprogramm für 6 bis 14-Jährige +GENRE.CHILDRENs_PROGRAMMES.3 Unterhaltungsprogramm für 10 bis 16-Jährige +GENRE.CHILDRENs_PROGRAMMES.4 Informations- und Bildungsprogramm +GENRE.CHILDRENs_PROGRAMMES.5 Trickfilm +GENRE.DOCUS_MAGAZINES.0 Doku/Magazin +GENRE.DOCUS_MAGAZINES.1 Natur/Tiere/Umwelt +GENRE.DOCUS_MAGAZINES.2 Naturwissenschaft und Technik +GENRE.DOCUS_MAGAZINES.3 Medizin/Physiologie/Psychologie +GENRE.DOCUS_MAGAZINES.4 Ausland/Expeditions +GENRE.DOCUS_MAGAZINES.5 Sozial- und Geisteswissenschaften +GENRE.DOCUS_MAGAZINES.6 Weiterbildung +GENRE.DOCUS_MAGAZINES.7 Sprachen +GENRE.MOVIE.0 Spielfilm / Drama +GENRE.MOVIE.1 Kriminalfilm/Thriller +GENRE.MOVIE.2 Abenteuerfilm/Western/Kriegsfilm +GENRE.MOVIE.3 Science-Fiction/Fantasy/Horror +GENRE.MOVIE.4 Comedy +GENRE.MOVIE.5 Soap/Melodram/Folklore +GENRE.MOVIE.6 Romantik +GENRE.MOVIE.7 serious/classical/religious/historical movie/drama +GENRE.MOVIE.8 adult movie/Drama +GENRE.MUSIC_DANCE.0 Musik/Ballet/Tanz +GENRE.MUSIC_DANCE.1 Rock/Pop +GENRE.MUSIC_DANCE.2 serious music/Klassik +GENRE.MUSIC_DANCE.3 Volksmusik +GENRE.MUSIC_DANCE.4 Jazz +GENRE.MUSIC_DANCE.5 Musical/Oper +GENRE.MUSIC_DANCE.6 Ballet +GENRE.NEWS.0 Nachrichten +GENRE.NEWS.1 Nachrichten/Wetter +GENRE.NEWS.2 Nachrichtenmagazin +GENRE.NEWS.3 Dokumantation +GENRE.NEWS.4 Diskussion/Interview/Debatte +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 Gameshow/Quiz +GENRE.SHOW.2 varietee +GENRE.SHOW.3 Talkshow +GENRE.SOCIAL_POLITICAL.0 Sozial & Politikereignisse / Wirtschaft +GENRE.SOCIAL_POLITICAL.1 Magazin/Report/Dokumentation +GENRE.SOCIAL_POLITICAL.2 Wirtschaft und Soziales +GENRE.SOCIAL_POLITICAL.3 Besondere Menschen +GENRE.SPORTS.0 Sport +GENRE.SPORTS.1 Großereignisse (Olympische Spiele, Weltmeisterschaften usw.) +GENRE.SPORTS.10 equestrian +GENRE.SPORTS.11 martial sports +GENRE.SPORTS.2 Sportsmagazine +GENRE.SPORTS.3 Fußball +GENRE.SPORTS.4 Tennis +GENRE.SPORTS.5 Mannschaftssport +GENRE.SPORTS.6 Leichtathletik +GENRE.SPORTS.7 Motorsport +GENRE.SPORTS.8 Wassersport +GENRE.SPORTS.9 Wintersport +GENRE.TRAVEL_HOBBIES.0 Reise & Freizeit +GENRE.TRAVEL_HOBBIES.1 Reisen und Tourismus +GENRE.TRAVEL_HOBBIES.2 handicraft +GENRE.TRAVEL_HOBBIES.3 Motor +GENRE.TRAVEL_HOBBIES.4 Fitness und Gesundheit +GENRE.TRAVEL_HOBBIES.5 Kochen +GENRE.TRAVEL_HOBBIES.6 Einkauf +GENRE.TRAVEL_HOBBIES.7 Garten +GENRE.UNKNOWN Kenninet +apids.hint_1 Gems de APIDs zu streama getrend +apids.hint_2 mid ' ' in hexadezimol ei +apidselector.head Sproch auswaehn +audiomenu.PCMOffset Lautstärknabsenkung PCM +audiomenu.analogout Analog-Ausgong +audiomenu.avs avs +audiomenu.avs_control Eistain avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Audio-Eistellunga +audiomenu.lirc lirc +audiomenu.monoleft mono links +audiomenu.monoright mono rechts +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add dazuadoa +audioplayer.artist_title Interpret, Titl +audioplayer.defdir Start-Verz. +audioplayer.delete löschn +audioplayer.deleteall olle löschn +audioplayer.display_order Ozoagn +audioplayer.fastforward schnei vieri +audioplayer.follow aktuelle Liad +audioplayer.id3scan ID3-Tags durchsuacha +audioplayer.keylevel Tasdn-Umschoitung +audioplayer.pause Pause +audioplayer.play obspuin +audioplayer.playing Spuin +audioplayer.rewind rewind +audioplayer.screensaver_timeout Buidschirmschona (minutn, 0=aus) +audioplayer.shuffle wias kumt +audioplayer.stop stopn +audioplayer.title_artist Titl, Interpret +audioplayerpicsettings.general Musi spuin / Buidl schaugn +bookmarkmanager.delete Löschn +bookmarkmanager.name Lesezeichn +bookmarkmanager.rename Umbnenna +bookmarkmanager.select Auswäihn +bouqueteditor.add Dazuamacha +bouqueteditor.bouquetname Nama vom Bouquet +bouqueteditor.delete Wegmacha +bouqueteditor.discardingchanges D Änderunga wern verworfa. Kloana Moment ... +bouqueteditor.hide Vastecka +bouqueteditor.lock Sperrn +bouqueteditor.move Vaschiam +bouqueteditor.name Bouquets verwoitn +bouqueteditor.newbouquetname Da neie Nama vom Bouquet +bouqueteditor.rename Umnenna +bouqueteditor.return Jetz hammas +bouqueteditor.savechanges? Soin de Änderunga gspeichat wern? +bouqueteditor.savingchanges D Änderunga wern gspeichat. An Moment ... +bouqueteditor.switch Senda dazuadoa/wegmacha +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider Kabiobieta +channellist.head Olle Senda +channellist.nonefound Es san koane Senda gfundn worn!\nMachas bittschön a Sendersuach\n(dbox-Taste -> Service) +channellist.since seid %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blau +colorchooser.green grea +colorchooser.red rot +colormenu.background Hintergrundfarb +colormenu.background_head Hintergrundfarbe +colormenu.fade Menüs faden +colormenu.font Schriftgräßn +colormenu.gtx_alpha Alpha überblendunga +colormenu.head Farb-Eistellunga +colormenu.menucolors Menüfarbn +colormenu.statusbar Statusboikn +colormenu.textcolor Textfarb +colormenu.textcolor_head Textfarb +colormenu.themeselect Theme auswähn +colormenu.timing OSD Timeouts +colormenusetup.head Menüfarbn +colormenusetup.menucontent Fenstainhoit +colormenusetup.menucontent_inactive Fenstainhoit deaktiviert +colormenusetup.menucontent_selected Fenstainhoit ausgwähit +colormenusetup.menuhead Titelleistn +colorstatusbar.head Statusboikn-Farbn +colorstatusbar.text Statusboikn +colorthememenu.classic_theme Klassik +colorthememenu.head Theme auswähn +colorthememenu.neutrino_theme Neutrino Theme +date.Apr Apr +date.Aug Aug +date.Dec Dez +date.Feb Feb +date.Fri Fr +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mär +date.May Mai +date.Mon Mo +date.Nov Nov +date.Oct Okt +date.Sat Sa +date.Sep Sep +date.Sun So +date.Thu Do +date.Tue Di +date.Wed Mi +epglist.head Vorschau - %s +epglist.noevents Koa EPG do. +epgviewer.More_Screenings Des kumt no auf dem Senda... +epgviewer.nodetailed Koane bessan Informationen gfundn +epgviewer.notfound Koa EPG gfundn +favorites.addchannel Der Senda werd zum \n"De Meina" dazuado. \nDes Speichan dauert a bissl... +favorites.bouquetname De Meina +favorites.bqcreated Bouquet "De Meina" is oglegt worn...\n +favorites.chadded Der Senda is zu Eanare Favoriten dazuado worn...\n +favorites.chalreadyinbq Der Senda ist scho in Eanare Favoriten...\n +favorites.finalhint \nWans Eana vahaut ham\nder Bouquetverwaltung korrigiert werden.\n +favorites.menueadd Kanal Favoriten dazuadoa +favorites.nobouquets Favoriten genga nur wann Bouquets aktiviert san +filebrowser.delete Löschn +filebrowser.dodelete1 Soi +filebrowser.dodelete2 glöscht wern? +filebrowser.filter.active Fuita o +filebrowser.filter.inactive Fuita aus +filebrowser.head Datiesucha +filebrowser.mark markiern +filebrowser.nextpage Seitn viri +filebrowser.prevpage Seitn zruck +filebrowser.scan Verzeichnisse durchsuacha +filebrowser.select auswaehn +filebrowser.showrights Dateirechte ozoang +filebrowser.sort.date (Datum) +filebrowser.sort.name (Nama) +filebrowser.sort.namedirsfirst (Dateinama2) +filebrowser.sort.size (Greaß) +filebrowser.sort.type (Typ) +flashupdate.actionreadflash flash lesn +flashupdate.cantopenfile kon de Datei ned aufmacha +flashupdate.cantopenmtd kon des MTD-Device ned aufmacha +flashupdate.checkupdate nach ner neien Versio suacha +flashupdate.currentreleasecycle Releasezyklus +flashupdate.currentversion_sep De Version host +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot ImageTyp +flashupdate.currentversiontime Uhrzeit +flashupdate.erasefailed Flash löschn is schiafganga +flashupdate.erasing lösch des Flash +flashupdate.experimentalimage Sie ham an Schnappshuss ausgwaiht,\nbitte bedenkens, dass de Version ned durchprobiert is.\nWenns bled laft, geht Ihr Box danoch nimma.\n\nWoins de Version wirklich hernemma? +flashupdate.expertfunctions Experten-Funktionen +flashupdate.fileis0bytes de Datei hod 0 Byte +flashupdate.fileselector Datei auswähn +flashupdate.flashreadyreboot Des Imag is gflasht worn.\nJetz start ma de Box nei. +flashupdate.getinfofile hoi grod de Versionsinfo +flashupdate.getinfofileerror find nix +flashupdate.getupdatefile Update werd glon +flashupdate.getupdatefileerror kon des Update ned lon +flashupdate.globalprogress Ois: +flashupdate.head Aktualisiern +flashupdate.md5check Image obchecken +flashupdate.md5sumerror Des Image is hi +flashupdate.msgbox Des Image hob i gfundn:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWoins des Image runterlon \nund installiern? +flashupdate.msgbox_manual A neas Image hob i gfundn:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWoins des instaliern? +flashupdate.mtdselector Partition auswähn +flashupdate.programmingflash programmier des Flash +flashupdate.proxypassword Passwort +flashupdate.proxypassword_hint1 Gems as Proxy-Passwort ei +flashupdate.proxypassword_hint2 +flashupdate.proxyserver Proxyname +flashupdate.proxyserver_hint1 Gems an Proxynama oderd IP ei (Host:Port) +flashupdate.proxyserver_hint2 Wans nix eigem, hams a koan Proxy +flashupdate.proxyserver_sep Proxyserva +flashupdate.proxyusername Benutzernama +flashupdate.proxyusername_hint1 Gems ihrn Proxy-Benutzernama ei +flashupdate.proxyusername_hint2 Wans nix eigem wer koa Authentifizierung gmacht +flashupdate.readflash ganzes Flashimage aussalesn +flashupdate.readflashmtd einzelne Partition aussalesn +flashupdate.ready hobs +flashupdate.reallyflashmtd Woins echt flashen?\n\nWann was schiaflaft oder des Image hi is\wer de Box nimma startn.\n\nImagenama: %s\nZiel: %s +flashupdate.savesuccess Des Image is unter\nDateinamen %s gspeichat worn. +flashupdate.selectimage Vorhandne Images +flashupdate.titlereadflash Flash auslesn +flashupdate.titlewriteflash Flash schreim +flashupdate.updatemode Updatemodus +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuell (ftp) +flashupdate.url_file Eistellungsdatei +flashupdate.versioncheck Version obcheckn +flashupdate.writeflash ganzes Flashimage eispuin +flashupdate.writeflashmtd einzelne Partition eispuin +flashupdate.wrongbase De Releasezyklus hot net de richtige Version, bitte an andres Komplett-Image installiern +fontmenu.channellist Kanallistn +fontmenu.epg EPG +fontmenu.eventlist Eventlistn +fontmenu.gamelist Spuilistn +fontmenu.head Schriftgrössn Eistellunga +fontmenu.infobar Infoleistn +fontsize.channellist Kanallistn +fontsize.channellist_descr Beschreibung +fontsize.channellist_number Numma +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titl +fontsize.eventlist_datetime Eventlistn Datum/Zeid +fontsize.eventlist_itemlarge Eventlistn groß +fontsize.eventlist_itemsmall Eventlistn kloa +fontsize.eventlist_title Eventlistn Titl +fontsize.filebrowser_item Dateibrausa +fontsize.gamelist_itemlarge groß +fontsize.gamelist_itemsmall kloa +fontsize.hint Schrift werd glodn,\noan Moment bitte... +fontsize.infobar_channame Nama +fontsize.infobar_info Infobar Info +fontsize.infobar_number Numma +fontsize.infobar_small Infobar kloa +fontsize.menu Menutext +fontsize.menu_info Menu Info +fontsize.menu_title Menutitl +gtxalpha.alpha1 tr1 +gtxalpha.alpha2 tr2 +infoviewer.epgnotload Informationen no ned glon... +infoviewer.epgwait Wart no aufs EPG... +infoviewer.eventlist Programm +infoviewer.languages Tonwahl +infoviewer.motor_moving Antennenpositionierung +infoviewer.nocurrent Koa Information zum Programm +infoviewer.noepg Koa EPG do +infoviewer.notavailable Senda grod ned do +infoviewer.selecttime Ofangszeitn +infoviewer.streaminfo Spuizeig +infoviewer.subservice Bildregie +infoviewer.waittime Wart no aufd Uhrzeit... +ipsetup.hint_1 Nemmas 0...9 oda auffi oda obe +ipsetup.hint_2 Mit OK speichan, mit HOME obbrecha +keybindingmenu.RC Fernbedienung +keybindingmenu.addrecord neie Aufnahm +keybindingmenu.addrecord_head neie Aufnahm +keybindingmenu.addremind neia Umschoittermin +keybindingmenu.addremind_head neia Umschoittermin +keybindingmenu.allchannels_on_ok Sendalistn +keybindingmenu.bouquetchannels_on_ok Bouquet-Senda +keybindingmenu.bouquetdown Bouquet zruck +keybindingmenu.bouquetdown_head Bouquet zruck Dasdn +keybindingmenu.bouquethandling OK-Dasdn für +keybindingmenu.bouquetlist_on_ok Bouquetlistn +keybindingmenu.bouquetup Bouquet weida +keybindingmenu.bouquetup_head Bouquet weida Dasdn +keybindingmenu.cancel Sendalistn zuamacha +keybindingmenu.cancel_head Sendalistn zuamacha +keybindingmenu.channeldown Senda obe +keybindingmenu.channeldown_head Senda obe Dasdn +keybindingmenu.channellist Sendalistn +keybindingmenu.channelup Senda aufi +keybindingmenu.channelup_head Senda aufi Dasdn +keybindingmenu.head Dasdn-Eistellunga +keybindingmenu.modechange Modus wexln +keybindingmenu.pagedown Seitn obiblatln +keybindingmenu.pagedown_head Seitn obiblatln +keybindingmenu.pageup seitn aufiblatln +keybindingmenu.pageup_head Seitn aufiblatln +keybindingmenu.quickzap Schneiumschoitung +keybindingmenu.repeatblock Wiederholungsverzögerung +keybindingmenu.repeatblockgeneric Ofangsverzögerung +keybindingmenu.sort Sortierfoige ändan +keybindingmenu.sort_head Sortierfoige ändan +keybindingmenu.subchanneldown Untersenda zruck +keybindingmenu.subchanneldown_head Untersenda zruck +keybindingmenu.subchannelup Untersenda weida +keybindingmenu.subchannelup_head Untersenda weida +keybindingmenu.tvradiomode Fernseh-/Radio-Modus +keybindingmenu.tvradiomode_head Fernseh-/Radio-Modus +keychooser.head Neie Dasdn eistein +keychooser.text1 Dasdn drucka +keychooser.text2 Wanst ne wuist, wartn +keychoosermenu.currentkey Jetzdige Dasdn +keychoosermenu.setnew Neie Dasdn eistein +keychoosermenu.setnone Koa Dasdn +languagesetup.head Sprocheistellunga +languagesetup.select Sproch +lcdcontroler.brightness normale Helligkeid +lcdcontroler.brightnessstandby Standby Helligkeid +lcdcontroler.contrast Kontrast +lcdcontroler.head LCD Eistellunga +lcdmenu.autodimm Automatisch dimma +lcdmenu.head LCD Eistellunga +lcdmenu.inverse Invertieren +lcdmenu.lcdcontroler Kontrast / Helligkeid +lcdmenu.power Pauwa +lcdmenu.statusline Statuszeiln +lcdmenu.statusline.both change me +lcdmenu.statusline.playtime Spuizeit +lcdmenu.statusline.volume Lautstärkn +mainmenu.games Spuin +mainmenu.head Hauptmenü +mainmenu.movieplayer Fuimspuia +mainmenu.pausesectionsd EPG aussalesn +mainmenu.pictureviewer Buidlschauga +mainmenu.radiomode Radio hern +mainmenu.recording Streaming (aufnemma) +mainmenu.recording_start ofanga +mainmenu.recording_stop aufhearn +mainmenu.scartmode Scart hernemma +mainmenu.service Service +mainmenu.settings Eistellunga +mainmenu.shutdown Ausmacha +mainmenu.sleeptimer EischlofTimer +mainmenu.tvmode Fernseh schaugn +mainsettings.audio Audio +mainsettings.colors Forbn / Themes +mainsettings.head Eistellunga +mainsettings.keybinding Tasdn-Eistellunga +mainsettings.language Sproch +mainsettings.lcd LC-Disbläi +mainsettings.misc No a boar Eistellunga +mainsettings.network Netzwerk +mainsettings.recording Streamingserva +mainsettings.savesettingsnow Eistellunga jetz speichan +mainsettings.savesettingsnow_hint De Eistellunga wern jezt gespeichat,\noan Moment bitte... +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back Zruck +messagebox.back Zruck +messagebox.cancel Abbrecha +messagebox.discard Woins de Änderunga verwerfa? +messagebox.error Bläd glaffa +messagebox.info Information +messagebox.no Ned +messagebox.yes Scho +miscsettings.bootinfo Informationa beim Startn zoang +miscsettings.fb_destination EXPERT! Boot-Konsole +miscsettings.general Allgemein +miscsettings.head Diverse Eistellunga +miscsettings.infobar_sat_display Satelitn Informationsleistn +miscsettings.shutdown_real bissi ausmacha +miscsettings.shutdown_real_rcdelay verzögats Ausschoitn +miscsettings.startbhdriver BH-Mode Treiba lon +motorcontrol.head Motor-Eistellunga +movieplayer.bookmark Vormerka +movieplayer.bookmarkname Merkal Nama +movieplayer.bookmarkname_hint1 Bitte gems den Nama fürs neie Merkal o. +movieplayer.bookmarkname_hint2 movieplayer.skipping Umsortiern bitte wartn +movieplayer.buffering Buffern.. +movieplayer.defdir Start-Verz. +movieplayer.dvdplayback DVD +movieplayer.fileplayback Fuim mim VLC +movieplayer.goto Spring zu ... +movieplayer.goto.h1 = -> absoluter Sprung +movieplayer.goto.h2 +,- -> relativer Sprung +movieplayer.head Fuimspuia +movieplayer.nostreamingserver Koa Vabindung\nzum Streamingserva möglich. +movieplayer.pesplayback PES Abspuin (Experiment) +movieplayer.pleasewait Oan Moment, de Verbindung\nzum Streamingserva werd hergsteaid +movieplayer.toomanybookmarks Sie ham scho zvui Merkal oglegt.\nEs muas erst a andas glöscht wern. +movieplayer.tsplayback TS Abspuin +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp ROT/Hoam Stop\nGREA Resync\nGELB Pause/Weida\nBLAU Merkal setzn\n1 ca. 1 Minutn zruck\n3 ca. 1 Minutn viri\n4 ca. 5 Minutn zruck\n6 ca. 5 Minutn viri\n7 ca. 10 Minutn zruck\n9 ca. 10 Minutn viri\nHuif: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.wrongvlcversion Des is mit ehnara VLC-Version ne drin +networkmenu.broadcast Broadcast +networkmenu.gateway Standard Gateway +networkmenu.head Netzwerk Eistellunga +networkmenu.ipaddress IP-Adressn +networkmenu.nameserver Nameserva +networkmenu.netmask Netzmaskn +networkmenu.setupnow Netzwerkeistellunga jetz macha +networkmenu.setuponstartup Beim Startn Netzwerk setzn +networkmenu.show aktive Netzwerkeistellunga zoign +networkmenu.test Netzwerk ausprobiern +nfs.alreadymounted Verzeichnis is scho gmounted +nfs.automount Beim Startn mountn +nfs.dir NFS/CIFS Verzeichnis +nfs.ip NFS/CIFS Serva IP +nfs.localdir Verzeichnis auf da Box +nfs.mount NFS/CIFS Verzeichnis mountn +nfs.mount_options Mount-Optionen +nfs.mounterror Mount-Fähla +nfs.mounterror_notsup Mit dem Dateisystem kon i nix ofanga +nfs.mountnow Jetzad mountn +nfs.mounttimeout Mount-Fehla: Timeout +nfs.password CIFS Passwort +nfs.remount Verzeichniss nomoi mountn +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount NFS/CIFS Verzeichnis freigem +nfs.umounterror Fähla beim Freigem +nfs.username CIFS Benutzanama +nfsmenu.head NFS/CIFS Eistellunga +nvod.percentage (%d%% vorbei) +nvod.starting (Fangt in %d min o) +nvodselector.directormode Bildregie-Modus +nvodselector.head Anfangszeid auswaehn +nvodselector.subservice Perspektiven +options.default Voreistellunga nema +options.fb framebuffa +options.null null +options.off ausmacha +options.on omacha +options.serial seriell +parentallock.changepin PIN-Code eistain +parentallock.changepin_hint1 Gems jetz Eanan PIN-Code ei! +parentallock.changetolocked Bei vorgsperrde Bouquets +parentallock.head Jugendschutz PIN +parentallock.lockage Sendunga +parentallock.lockage12 ab 12 Jahr sperrn +parentallock.lockage16 ab 16 Jahr sperrn +parentallock.lockage18 ab 18 Jahr sperrn +parentallock.lockedchannel Gsperrda Senda +parentallock.lockedprogram Gperde Sendung(FSK ab %d) +parentallock.never Nia +parentallock.onsignal Wann a Vorsperre gsendet werd +parentallock.parentallock Jugendschutz +parentallock.prompt PIN eigem +pictureviewer.defdir Start-Verz. +pictureviewer.head Buidlschauga +pictureviewer.scaling Skalierung +pictureviewer.show Ozoign +pictureviewer.slide_time Diaschaugn-Ozoagdaua +pictureviewer.slideshow Diaschaugn +pictureviewer.sortorder andre Reihenfoign +pictureviewer.sortorder.date (Datum) +pictureviewer.sortorder.filename (Dateinama) +ping.ok kumt wos (ping) +ping.protocol kumt nix (Host or protocol error) +ping.socket kumt nix (Socket error) +ping.unreachable kumt nix (unreachable) +pinprotection.head PIN-Abfrage +pinprotection.wrongcode Gems den Code nomoi ei! +rclock.lockmsg D Fernbedienung von da dbox2 werd gsperrt.\nUm die Sperrn aufzhem, bitte\n und auf da Fernbedienung\ndrucka. +rclock.menueadd FB sperrn +rclock.title Fernbedienung sperrn +rclock.unlockmsg Fernbedienung reaktiviert... +recordingmenu.head Streamingserva Eistellunga +recordingmenu.no_scart Scart Umschoita untadrucka +recordingmenu.off aus +recordingmenu.recording_type Grät zum Aufnemma +recordingmenu.server Serva +recordingmenu.server_ip Aufnahmeserva IP +recordingmenu.server_mac MAC-Adressn +recordingmenu.server_port Aunahmeserva Port +recordingmenu.server_wakeup Serva zum Aufnemma (WOL) +recordingmenu.setupnow los moachs ! +recordingmenu.stopplayback Fuim ohoitn +recordingmenu.stopsectionsd Sectionsd ohoitn +recordingmenu.vcr Video Recorda +recordtimer.announce In weniga Minutn werd aufgnomma +repeatblocker.hint_1 Mindestzeit (ms) zwischn zwoa Dasdndrucka +repeatblocker.hint_2 0 schoitt an Blocka aus (Rot is " ") +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-Wiederholunga +satsetup.extended DiSEqC-Eistellunga +satsetup.extended_motor Motor-Eistellunga +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manuelle Motor-Eistellunga +satsetup.nodiseqc Koa DiSEqC +satsetup.satellite Satellit +satsetup.savesettingsnow Eistellunga jetz speichan +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Kabi: +scants.actsatellite Satellit: +scants.bouquet Bouquets +scants.bouquet_create nei macha +scants.bouquet_erase wegmacha +scants.bouquet_leave ned olanga +scants.bouquet_satellite Satelliten-Bouquet +scants.bouquet_update nei macha +scants.channel Kanal: +scants.failed Sendasuacha is gescheitat +scants.finished Senda sand gfundn worn! +scants.freqdata Frequenz: +scants.head Senda suacha +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Gesamt +scants.numberoftvservices TV +scants.provider Anbieter: +scants.startnow Ofanga mim Senda suacha +scants.transponders Transponder: +screensetup.lowerright grün = Buidlrand untn, rechts +screensetup.upperleft rot = Buidlrand om, links +servicemenu.head Service +servicemenu.reload Sendalistn nei loan +servicemenu.reload_hint Sendalistn wern nei glon,\noan Moment... +servicemenu.scants Senda suacha +servicemenu.ucodecheck UCodes obcheckn +servicemenu.update Software-aktualisiern +settings.missingoptionsconffile Es gibt neie Eistellunga.\nDe stenga jetz erstmoi auf Standard. +settings.noconffile De Eistellunga von Neutrino san ned\ngfundn worn. Mia nemma de Standardwerte. +shutdowntimer.announce De Box werd in oana Minutn runtagfahrn.\nObbrecha? +sleeptimerbox.announce Eischloftimer in min +sleeptimerbox.hint1 Einschlofzeit in Min. (000=aus) +sleeptimerbox.hint2 De Box schoit si noch der Zeid aus. +sleeptimerbox.title Eischloftimer +streamfeatures.head Features +streaminfo.aratio Verhältnis +streaminfo.aratio_unknown Verhältnis: ken i ned +streaminfo.audiotype Audiotyp +streaminfo.audiotype_unknown Audiotyp: ken i ned +streaminfo.bitrate Bitrate +streaminfo.framerate Bildrate +streaminfo.framerate_unknown Bildrate: ken i ned +streaminfo.head Informationen +streaminfo.not_available kumt nix +streaminfo.resolution Auflösung +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Fuimspuia Eistellunga +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Ausgschoitn +streamingmenu.on Eigschoitn +streamingmenu.server_ip Streamingserva IP +streamingmenu.server_port Streamingserva Port +streamingmenu.streaming_audiorate Datenrate Audio +streamingmenu.streaming_force_avi_rawaudio AC3 bei avi erzwinga +streamingmenu.streaming_force_transcode_video Transcod. a mpg/vcd Video +streamingmenu.streaming_resolution Auflösung +streamingmenu.streaming_server_cddrive DVD Laufwerk +streamingmenu.streaming_server_startdir Verzeichnis (VLC) +streamingmenu.streaming_transcode_audio Transcod. Audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec MPEG Video Codec +streamingmenu.streaming_type Streamingserva +streamingmenu.streaming_videorate Datenrate Video +streamingserver.noconnect Kao Verbindung zum Streamingserva.\nDe Aufnahme werd obbrocha. +stringinput.caps Groß-/Koabuchstobm +stringinput.clear Ois löschn +timer.eventrecord.msg De Sendung is zum Aufnemma vorgmerkt +timer.eventrecord.title Aufnemma vormerka +timer.eventtimed.msg De Sendung is vorgmerkt +timer.eventtimed.title Sendung vormerka +timerbar.channelswitch Umschoitn +timerbar.recordevent Aufnemma +timerlist.alarmtime Alarmzeid +timerlist.apids Audio PIDs +timerlist.bouquetselect Bouquet auswäihn +timerlist.channel Kanoi +timerlist.channelselect Senda auswäihn +timerlist.delete wegmacha +timerlist.menumodify Timer bearbeitn +timerlist.menunew Neia Timer +timerlist.message Nochricht +timerlist.moderadio Radio hern +timerlist.modeselect Modus auswäihn +timerlist.modetv Ferhsegn +timerlist.modify ändan +timerlist.name Timerlistn +timerlist.new Neia Timer +timerlist.program.unknown Kenn den Senda ned +timerlist.reload akualisiern +timerlist.repeat Widahoin +timerlist.repeat.biweekly olle zwoa Wocha +timerlist.repeat.byeventdescription wia da timer +timerlist.repeat.daily jeden tog +timerlist.repeat.fourweekly alle vier Wocha +timerlist.repeat.friday Fr +timerlist.repeat.monday Mo +timerlist.repeat.monthly jeden Monat +timerlist.repeat.once oimalig +timerlist.repeat.saturday Sa +timerlist.repeat.sunday So +timerlist.repeat.thursday Do +timerlist.repeat.tuesday Die +timerlist.repeat.unknown Kenn i ned +timerlist.repeat.wednesday Mi +timerlist.repeat.weekdays Wochatogs +timerlist.repeat.weekly jede woch +timerlist.save Speichan +timerlist.standby SB +timerlist.standby.off Ausm Standy aufwacha +timerlist.standby.on In Standy geh +timerlist.stoptime Stopzeid +timerlist.type Timertyp +timerlist.type.nextprogram Nächsta Senda +timerlist.type.record Aufnemma +timerlist.type.remind Erinnern +timerlist.type.shutdown Ausmacha +timerlist.type.sleeptimer Eischloftimer +timerlist.type.standby Bissl Ausmacha +timerlist.type.unknown Kenninet +timerlist.type.zapto Umschoitn +timerlist.weekdays Wochentog +timerlist.weekdays.hint_1 Mo Die Mi Do Fr Sa So +timerlist.weekdays.hint_2 'X'=Timer '-' koa Timer +timersettings.record_safety_time_after Aufnahmeend Korretua +timersettings.record_safety_time_after.hint_1 Korrekturzeid in Minutn (00=aus), de aufd +timersettings.record_safety_time_after.hint_2 Endzeid vom jeweilign Timer aufigrechent werd +timersettings.record_safety_time_before Aufnemma - Korrekturzeid +timersettings.record_safety_time_before.hint_1 De Zeid in Minutn (00=aus) werd beim Startn +timersettings.record_safety_time_before.hint_2 vom Timer obzogn +timersettings.separator Timer Eistellunga +timing.chanlist Kanalliste +timing.epg Epg +timing.filebrowser Filebrausa +timing.head OSD Timeouts +timing.hint_1 Einblendzeit in Sek., die das OSD +timing.hint_2 auf dem TV angezeigt wird +timing.infobar Infobar +timing.menu Menu +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCodes obcheckn +ucodecheck.ucode UCode +ucodes.failure Obacht, de µCodes san ned gfundn worn!\n\nLoans de doch bittschen mit FTP \nauffi, und startns de Box numoi! +videomenu.csync Sync.-Korrigiern +videomenu.head Video-Eistellunga +videomenu.rgb_centering RGB-Zentriern +videomenu.screensetup Bildbereich eistein +videomenu.vcrswitch Scart-Eigang automatisch +videomenu.videoformat Bildschirmformat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect automatisch +videomenu.videosignal Video Signalart +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce In na Minutn is schluss diff --git a/data/locale/bosanski.locale b/data/locale/bosanski.locale new file mode 100644 index 000000000..f4dbae3bd --- /dev/null +++ b/data/locale/bosanski.locale @@ -0,0 +1,722 @@ +EPGMenu.epgplus pregled predvidanja +EPGMenu.eventinfo informacije o emisiji +EPGMenu.eventlist predvidanje trenutacnog programa +EPGMenu.head epg - informacije o programu +EPGMenu.streaminfo tehnicke informacije +EPGPlus.head pregled predvidanja (epg plus) +EPGPlus.record snimanje +EPGPlus.refresh_epg obnoviti +EPGPlus.remind zapamtiti +EPGPlus.scroll_mode scroll modus +EPGPlus.stretch_mode stretch modus +GENRE.ARTS.0 umjetnost / kultura +GENRE.ARTS.1 umjetnost +GENRE.ARTS.10 magazin za umjetnost / kulturu +GENRE.ARTS.11 moda +GENRE.ARTS.2 umjetnost +GENRE.ARTS.3 vjera +GENRE.ARTS.4 umjetnost +GENRE.ARTS.5 literatura +GENRE.ARTS.6 film/kino +GENRE.ARTS.7 eksperimentalni film/video +GENRE.ARTS.8 rtv/Å¡tampa +GENRE.ARTS.9 nove medije +GENRE.CHILDRENs_PROGRAMMES.0 program za djecu / mlade +GENRE.CHILDRENs_PROGRAMMES.1 program za predÅ¡kolsku djecu +GENRE.CHILDRENs_PROGRAMMES.2 program za 6-o do 14-o godiÅ¡njake +GENRE.CHILDRENs_PROGRAMMES.3 program za 10-o do 16-o godiÅ¡njake +GENRE.CHILDRENs_PROGRAMMES.4 informativni i nauÄni program +GENRE.CHILDRENs_PROGRAMMES.5 crtani film +GENRE.DOCUS_MAGAZINES.0 dokumentacija/magazin +GENRE.DOCUS_MAGAZINES.1 priroda/životinje/okolina +GENRE.DOCUS_MAGAZINES.2 nauka i tehnika +GENRE.DOCUS_MAGAZINES.3 medicina/fisiologija/psihologija +GENRE.DOCUS_MAGAZINES.4 inozemstvo/ekspedicije +GENRE.DOCUS_MAGAZINES.5 nauka +GENRE.DOCUS_MAGAZINES.6 nauka +GENRE.DOCUS_MAGAZINES.7 jezici +GENRE.MOVIE.0 igrani film / drama +GENRE.MOVIE.1 kriminalni film / triler +GENRE.MOVIE.2 avanturni/kaubojski/ratni film +GENRE.MOVIE.3 nauÄna fantastika/fantazija/strava +GENRE.MOVIE.4 komedija +GENRE.MOVIE.5 soap/melodramatika/folklora +GENRE.MOVIE.6 romantika +GENRE.MOVIE.7 ozbiljno/klasiÄno/vjerno/povijesni film/drama +GENRE.MOVIE.8 film za odrasle/drama +GENRE.MUSIC_DANCE.0 muzika/balet/ples +GENRE.MUSIC_DANCE.1 rok/pop +GENRE.MUSIC_DANCE.2 ozbiljna muzika/klasika +GENRE.MUSIC_DANCE.3 narodna muzika +GENRE.MUSIC_DANCE.4 džez +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 balet +GENRE.NEWS.0 vijesti +GENRE.NEWS.1 vijesti/vrijeme +GENRE.NEWS.2 vijesni magazin +GENRE.NEWS.3 dokumantacija +GENRE.NEWS.4 diskusija/ispit/debata +GENRE.SHOW.0 show / gameshow +GENRE.SHOW.1 gameshow / kviz +GENRE.SHOW.2 variete +GENRE.SHOW.3 talkshow +GENRE.SOCIAL_POLITICAL.0 socialni & politiÄki dogadaji / privreda +GENRE.SOCIAL_POLITICAL.1 magazin/report/dokumentacija +GENRE.SOCIAL_POLITICAL.2 privreda i socialno +GENRE.SOCIAL_POLITICAL.3 naroÄiti ljudi +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 veledogaÄ‘aji (olimpijada, svjetsko prvenstvo itd.) +GENRE.SPORTS.10 jahanje +GENRE.SPORTS.11 borilaÄki sportovi +GENRE.SPORTS.2 sport magazin +GENRE.SPORTS.3 nogomet +GENRE.SPORTS.4 tenis +GENRE.SPORTS.5 ekipni sport +GENRE.SPORTS.6 atletika +GENRE.SPORTS.7 motorni sport +GENRE.SPORTS.8 vodeni sport +GENRE.SPORTS.9 zimski sport +GENRE.TRAVEL_HOBBIES.0 odmor +GENRE.TRAVEL_HOBBIES.1 odmor i turizam +GENRE.TRAVEL_HOBBIES.2 ruÄni rad +GENRE.TRAVEL_HOBBIES.3 motori +GENRE.TRAVEL_HOBBIES.4 sposobnost i zdravlje +GENRE.TRAVEL_HOBBIES.5 kuhanje +GENRE.TRAVEL_HOBBIES.6 kupovina +GENRE.TRAVEL_HOBBIES.7 vrt +GENRE.UNKNOWN nepoznato +apids.hint_1 Utipkajte heksadecimalne apids +apids.hint_2 za streaming razdvojene sa ' '. +apidselector.head izbor jezika +audiomenu.PCMOffset smanjivanje glasnoce pcm +audiomenu.analogout analogni izlaz +audiomenu.avs avs +audiomenu.avs_control upravljanje glasnosti avs/ost +audiomenu.dolbydigital dolby digital automaticno +audiomenu.head namjestiti ton +audiomenu.lirc lirc +audiomenu.monoleft mono lijevo +audiomenu.monoright mono desno +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add dodati +audioplayer.artist_title pjevaÄ, pjesma +audioplayer.defdir pocetni direktorij +audioplayer.delete izbrisati +audioplayer.deleteall sve izbrisati +audioplayer.display_order redoslijed +audioplayer.fastforward brzo naprijed +audioplayer.follow izabjere ovu pjesmu +audioplayer.id3scan Äitaj id3-tag +audioplayer.keylevel promijeniti tipke +audioplayer.pause pauza +audioplayer.play igrati +audioplayer.playing pjesma +audioplayer.rewind brzo natrag +audioplayer.screensaver_timeout Å¡tititi ekran (min, 0=ugaÅ¡eno) +audioplayer.shuffle sluÄaj +audioplayer.stop zaustaviti +audioplayer.title_artist pjesma, pjevaÄ +bookmarkmanager.delete izbrisati +bookmarkmanager.name zapamcena mjesta +bookmarkmanager.rename preimenovati +bookmarkmanager.select izabrati +bouqueteditor.add dodati +bouqueteditor.bouquetname ime buketa +bouqueteditor.delete izbrisati +bouqueteditor.discardingchanges Pobacuju se promjene. ÄŒekajte molim ... +bouqueteditor.hide sakriti +bouqueteditor.lock zabraniti +bouqueteditor.move pomaknuti +bouqueteditor.name namjestiti buket +bouqueteditor.newbouquetname novo ime buketa +bouqueteditor.rename preimenovati +bouqueteditor.return gotovo +bouqueteditor.savechanges? Hoćete li memorisati promjene? +bouqueteditor.savingchanges MemoriÅ¡u se promjene. ÄŒekajte molim ... +bouqueteditor.switch preuzeti/izbaciti kanal +bouqueteditor.switchmode tv/radio +bouquetlist.head buketi +cablesetup.provider snabdjevaÄ +channellist.head svi kanali +channellist.nonefound Nije naÄ‘en nijedan kanal!\nNapravite traženje kanala\n(dbox2-tipka -> servis) +channellist.since od %02d:%02d +colorchooser.alpha alfa +colorchooser.blue plavo +colorchooser.green zeleno +colorchooser.red crveno +colormenu.background boja pozadine +colormenu.background_head boja pozadine +colormenu.fade izblijediti izbore +colormenu.font veliÄina slova +colormenu.gtx_alpha providnost (gtx) +colormenu.head namjestiti boje +colormenu.menucolors boja izbora +colormenu.statusbar statusna traka +colormenu.textcolor boja teksta +colormenu.textcolor_head boja teksta +colormenu.themeselect izabrati teme +colormenu.timing osd tajm-aut +colormenusetup.head boja izbora +colormenusetup.menucontent sadržaj prozora +colormenusetup.menucontent_inactive deaktiviran sadržaj prozora +colormenusetup.menucontent_selected izabran sadržaj prozora +colormenusetup.menuhead izborno zaglavlje +colorstatusbar.head boja statusne trake +colorstatusbar.text statusna traka +colorthememenu.classic_theme klasiÄno +colorthememenu.head izabrati teme +colorthememenu.neutrino_theme neutrino tema +date.Apr apr +date.Aug aug +date.Dec dec +date.Feb feb +date.Fri pet +date.Jan jan +date.Jul jul +date.Jun jun +date.Mar mar +date.May maj +date.Mon pon +date.Nov nov +date.Oct okt +date.Sat sub +date.Sep sep +date.Sun ned +date.Thu Äet +date.Tue uto +date.Wed sri +epglist.head pregled - %s +epglist.noevents Nema epg informacije. +epgviewer.More_Screenings drugi termini na ovom kanalu +epgviewer.nodetailed nema opÅ¡irnih informacija +epgviewer.notfound nisu naÄ‘ene informacije o programu (epg) +favorites.addchannel Dodaje se trenutaÄni kanal\nbuketu"moji ljubimci".\nMemorisanje traje trenutak... +favorites.bouquetname moji ljubimci +favorites.bqcreated stvoren je buket "Moji ljubimci"...\n +favorites.chadded trenutaÄni kanal je dodan vaÅ¡im ljubimcima...\n +favorites.chalreadyinbq trenutaÄni kanal je već u vaÅ¡im ljubimcima...\n +favorites.finalhint \nSluÄajno dodati kanali mogu se\nizbrisati u namješćenju buketa.\n +favorites.menueadd dodati kanal ljubimcima +favorites.nobouquets moji ljubimci su samo u aktiviranim buketima mogući +filebrowser.delete izbrisati +filebrowser.dodelete1 Hocete +filebrowser.dodelete2 li izbrisati? +filebrowser.filter.active upaljen filter +filebrowser.filter.inactive ugaÅ¡en filter +filebrowser.head pretraživaÄ datoteka +filebrowser.mark oznaÄiti +filebrowser.nextpage strana naprijed +filebrowser.prevpage strana nazad +filebrowser.scan pretraži direktorije +filebrowser.select izabrati +filebrowser.showrights prava datoteke pokazati +filebrowser.sort.date (datum) +filebrowser.sort.name (ime datoteke) +filebrowser.sort.namedirsfirst (ime datoteke 2) +filebrowser.sort.size (velicina) +filebrowser.sort.type (tip) +flashupdate.action akcija: +flashupdate.actionreadflash uÄitaje se fleÅ¡ +flashupdate.actionwriteflash piÅ¡e se fleÅ¡ +flashupdate.cantopenfile nemože se otvoriti datoteka +flashupdate.cantopenmtd nemože se otvoriti mtd-uredaj +flashupdate.checkupdate tražiti novu verziju +flashupdate.currentreleasecycle release imidž +flashupdate.currentversion_sep instalirana verzija +flashupdate.currentversiondate datum +flashupdate.currentversionsnapshot vrsta imidža +flashupdate.currentversiontime vrijeme +flashupdate.erasefailed brisanje flesa nije uspjelo +flashupdate.erasing briÅ¡e se fleÅ¡ +flashupdate.experimentalimage Izabrali ste snapshot,\novo je verzija za pokusavanje i mozda vas\ndbox2 poslije obnavljanja nece vise raditi.\n\nHocete li stvarno sa ovom verzijom obnoviti softver? +flashupdate.expertfunctions funkcije za majstore +flashupdate.fileis0bytes veliÄina datoteke je 0 bajtova +flashupdate.fileselector izbor datoteke +flashupdate.flashreadyreboot Imidž je uspjeÅ¡no fleÅ¡ovan.\nVaÅ¡ dbox2 startuje sada ponovno. +flashupdate.getinfofile Äita se informacija o verziji +flashupdate.getinfofileerror nemože se dobiti informacija o verziji +flashupdate.getupdatefile uÄitaje se najnoviji softver +flashupdate.getupdatefileerror nemože se dobiti najnoviji softver +flashupdate.globalprogress glavni status: +flashupdate.head obnavljanje +flashupdate.md5check provjera imidža +flashupdate.md5sumerror imidž ima greÅ¡ke +flashupdate.msgbox NaÄ‘en je novi imidž:\nDatum: %s, %s\nOsnovni imidž: %s\nvrsta imidža: %s\n\nHoćete li ovu verziju sada preuzeti\nsa interneta i instalirati? +flashupdate.msgbox_manual NaÄ‘en je novi imidž:\nDatum: %s, %s\nOsnovni imidž: %s\nVrsta imidža: %s\n\nHoćete li ovu verziju sada instalirati? +flashupdate.mtdselector izbor particije +flashupdate.programmingflash programira se fleÅ¡ +flashupdate.proxypassword lozinka +flashupdate.proxypassword_hint1 utipkajte lozinku za proxy +flashupdate.proxypassword_hint2 +flashupdate.proxyserver proxyname +flashupdate.proxyserver_hint1 utipkajte ime proxya ili ip adresu (host:port) +flashupdate.proxyserver_hint2 prazno znaÄi da nema proxya +flashupdate.proxyserver_sep proxyserver +flashupdate.proxyusername korisnik +flashupdate.proxyusername_hint1 utipkajte ime korisnika za proxy +flashupdate.proxyusername_hint2 prazno znaÄi da nema provjere +flashupdate.readflash uÄitati Äitav fleÅ¡imidž +flashupdate.readflashmtd uÄitati jednu particiju +flashupdate.ready gotovo +flashupdate.reallyflashmtd Hoćete li stvarno nastaviti fleÅ¡ovanje?\n\nAko se desi greÅ¡ka ili imidž nije\nu redu, onda vaÅ¡ dbox2 neće viÅ¡e raditi.\n\nIme imidža: %s\nPozicija: %s +flashupdate.reboot startuje se dbox2 ponovno +flashupdate.savesuccess Imidž je uspjeÅ¡no memorisan \npod imenom %s. +flashupdate.selectimage izbor imidza +flashupdate.titlereadflash uÄitati fleÅ¡ +flashupdate.titlewriteflash pisati fleÅ¡ +flashupdate.updatemode softver se obnavlja +flashupdate.updatemode_internet sa interneta +flashupdate.updatemode_manual ruÄno (ftp) +flashupdate.url_file datoteka za konfiguraciju +flashupdate.versioncheck provjeriti verziju +flashupdate.writeflash pisati Äitav fleÅ¡imidž +flashupdate.writeflashmtd pisati jednu particiju +flashupdate.wrongbase Verzija release imidža ne paÅ¡e viÅ¡e,\ntreba se instalirati novi komletni imidž! +fontmenu.channellist lista kanala +fontmenu.epg epg +fontmenu.eventlist lista dogaÄ‘aja +fontmenu.gamelist lista igara +fontmenu.head namješćenje veliÄine slova +fontmenu.infobar informativna traka +fontsize.channellist lista kanala +fontsize.channellist_descr opis +fontsize.channellist_number broj +fontsize.epg_date epg datum +fontsize.epg_info1 epg informacija 1 +fontsize.epg_info2 epg informacija 2 +fontsize.epg_title naslov epgea +fontsize.eventlist_datetime lista dogaÄ‘aja za datum / vrijeme +fontsize.eventlist_itemlarge velika lista dogaÄ‘aja +fontsize.eventlist_itemsmall mala lista dogaÄ‘aja +fontsize.eventlist_title naslov liste dogaÄ‘aja +fontsize.filebrowser_item dio pretraživaÄa datoteka +fontsize.gamelist_itemlarge veliko +fontsize.gamelist_itemsmall malo +fontsize.hint slova se namješćaju,\nÄekajte molim ... +fontsize.infobar_channame infromativna traka za kanal +fontsize.infobar_info infromativna traka za informaciju +fontsize.infobar_number infromativna traka za broj +fontsize.infobar_small mala infromativna traka +fontsize.menu tekst u izboru +fontsize.menu_info izbor za informaciju +fontsize.menu_title naslov izbora +gtxalpha.alpha1 alpha 1 +gtxalpha.alpha2 alpha 2 +infoviewer.cantdecode kanal se ne može deÅ¡ifrovati +infoviewer.epgnotload informacije joÅ¡ nisu pristupaÄne... +infoviewer.epgwait Äeka se na epg informacije... +infoviewer.eventlist program +infoviewer.languages izbor tona +infoviewer.motor_moving namješćenje antene +infoviewer.nocurrent nema informacije o ovom programu +infoviewer.noepg nema epg informacije +infoviewer.notavailable kanal (trenutaÄno) nije pristupan +infoviewer.selecttime poÄetak +infoviewer.streaminfo osobine +infoviewer.subservice režija slike +infoviewer.waittime Äeka se na vrijeme ... +ipsetup.hint_1 upotrijebite 0..9 ili gore/dole +ipsetup.hint_2 ok memoriÅ¡e, home prekida +keybindingmenu.RC daljinska +keybindingmenu.addrecord dodati vremenski brojaÄ za snimanje +keybindingmenu.addrecord_head dodati vremenski brojaÄ za snimanje +keybindingmenu.addremind dodati vremenski brojaÄ za prebaciti +keybindingmenu.addremind_head dodati vremenski brojaÄ za prebaciti +keybindingmenu.allchannels_on_ok lista kanala +keybindingmenu.bouquetchannels_on_ok buket kanali +keybindingmenu.bouquetdown buket nazad +keybindingmenu.bouquetdown_head tipka za buket nazad +keybindingmenu.bouquethandling ok-tipka za +keybindingmenu.bouquetlist_on_ok lista buketa +keybindingmenu.bouquetup buket dalje +keybindingmenu.bouquetup_head tipka za buket dalje +keybindingmenu.cancel zatvoriti listu kanala +keybindingmenu.cancel_head zatvoriti listu kanala +keybindingmenu.channeldown kanal dole +keybindingmenu.channeldown_head tipka za kanal dole +keybindingmenu.channellist lista kanala +keybindingmenu.channelup kanal gore +keybindingmenu.channelup_head tipka za kanal gore +keybindingmenu.head namjestiti tipaljku +keybindingmenu.modechange promijeniti stanje +keybindingmenu.pagedown stranu dole +keybindingmenu.pagedown_head stranu dole +keybindingmenu.pageup stranu gore +keybindingmenu.pageup_head stranu gore +keybindingmenu.quickzap brzo prebacivanje +keybindingmenu.repeatblock Äekanje pri ponavljanju +keybindingmenu.repeatblockgeneric poÄetno Äekanje +keybindingmenu.sort promijeniti smjer sortiranja +keybindingmenu.sort_head promijeniti smjer sortiranja +keybindingmenu.subchanneldown podkanal nazad +keybindingmenu.subchanneldown_head podkanal nazad +keybindingmenu.subchannelup podkanal dalje +keybindingmenu.subchannelup_head podkanal dalje +keybindingmenu.tvradiomode tv/radio +keybindingmenu.tvradiomode_head tv/radio +keychooser.head namjestiti novu tipku +keychooser.text1 pritisnite tipku +keychooser.text2 Äekati na prekid... +keychoosermenu.currentkey sadaÅ¡nja tipka +keychoosermenu.setnew namjestiti novu tipku +keychoosermenu.setnone nijedna tipka +languagesetup.head izabrati jezik +languagesetup.select jezik +lcdcontroler.brightness normalna svjetlost +lcdcontroler.brightnessstandby svjetlost u pripravnosti +lcdcontroler.contrast kontrast +lcdcontroler.head namjestiti teÄni kristal +lcdmenu.autodimm automaticno smanjiti svjetlost +lcdmenu.head namjestiti teÄni kristal +lcdmenu.inverse invertirati +lcdmenu.lcdcontroler kontrast / svjetlost +lcdmenu.power struja +lcdmenu.statusline statusna linija +lcdmenu.statusline.both glasnost i trajanje +lcdmenu.statusline.playtime igrano vrijeme +lcdmenu.statusline.volume glasnost +mainmenu.games igre +mainmenu.head glavni izbor +mainmenu.movieplayer promatraÄ filmova +mainmenu.pausesectionsd Äitati epg +mainmenu.pictureviewer promatraÄ slika +mainmenu.radiomode radio +mainmenu.recording snimanje +mainmenu.recording_start start +mainmenu.recording_stop stop +mainmenu.scartmode av-scart ulaz +mainmenu.service servis +mainmenu.settings namješćenje +mainmenu.shutdown ugasiti +mainmenu.sleeptimer vremenski brojaÄ za ugasiti +mainmenu.tvmode tv +mainsettings.audio ton +mainsettings.colors boje / teme +mainsettings.head namješćenje +mainsettings.keybinding namjestiti tipku +mainsettings.language jezik +mainsettings.lcd teÄni kristal +mainsettings.misc namjestiti razne stvari +mainsettings.network mreža +mainsettings.plugins namješćenje plugina +mainsettings.recording snimanje +mainsettings.savesettingsnow namješćenje sada memorisati +mainsettings.savesettingsnow_hint memoriÅ¡e se namješćenje,\nÄekajte molim ... +mainsettings.scan namjestiti traženja kanala +mainsettings.streaming promatraÄ filmova +mainsettings.video video +menu.back nazad +messagebox.back nazad +messagebox.cancel prekid +messagebox.discard Hoćete li pobaciti promijenito? +messagebox.error greÅ¡ka +messagebox.info informacija +messagebox.no ne +messagebox.yes ja +miscsettings.bootinfo pokazati informacije pri startovanju +miscsettings.boxtype proizvoÄ‘aÄ dbox2 +miscsettings.fb_destination majstori! boot konsola +miscsettings.general generalno +miscsettings.head namjestiti razne stvari +miscsettings.infobar_sat_display informativna traka sateliti +miscsettings.shutdown_real pripravnost +miscsettings.shutdown_real_rcdelay ugasiti sa zastojanjem +miscsettings.startbhdriver startovati bh-mode driver +motorcontrol.head namješćenje motora +movieplayer.bookmark oznaka +movieplayer.bookmarkname ime zapamcenog mjesta +movieplayer.bookmarkname_hint1 Imenujte novo zapamceno mjesto +movieplayer.bookmarkname_hint2 +movieplayer.buffering predraditi... +movieplayer.defdir poÄetni direktorij +movieplayer.dvdplayback dvd +movieplayer.fileplayback datoteka preko vlcea +movieplayer.goto Skacem prema ... +movieplayer.goto.h1 = -> absolutni skok +movieplayer.goto.h2 +,- -> relativni skok +movieplayer.head promatraÄ filmova +movieplayer.nostreamingserver Nije bilo moguće\nuspostaviti vezu sa streamingserverom. +movieplayer.pesplayback odigrati pes (eksperimentalno) +movieplayer.pleasewait Uspostavlja se veza sa streamingserverom.\nÄŒekajte molim ... +movieplayer.toomanybookmarks Previse mjesta su zapamcena.\nMora se prvo jedno drugo izbrisati. +movieplayer.tsplayback odigrati ts +movieplayer.vcdplayback (s)vcd +movieplayer.vlchelp crveno/kuci stop\nzeleno resync\nzuto pausa/dalje\nplavo zapamtiti mjesto\n1 oko 1 minuta nazad\n3 oko 1 minuta naprijed\n4 oko 5 minuta nazad\n6 oko5 minuta naprijed\n7 oko 10 minuta nazad\n9 oko 10 minuta naprijed\npomoc:http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.wrongvlcversion funkcije nije moguca sa trenutacno upotrijebito verziji vlca +networkmenu.broadcast broadcast +networkmenu.gateway standard gateway +networkmenu.head namjestiti mrežu +networkmenu.ipaddress ip adresa +networkmenu.nameserver nameserver +networkmenu.netmask netmask +networkmenu.setupnow sada namjestiti podatke mreže +networkmenu.setuponstartup namjestiti mrežu pri startu +networkmenu.show pokazati aktivne podatke mreže +networkmenu.test provjeriti mrežu +nfs.alreadymounted direktorij je već montiran +nfs.automount pri startu montirati +nfs.dir nfs/cifs direktorij +nfs.ip ip nfs/cifs servera +nfs.localdir lokalni direktorij +nfs.mount montira nfs/cifs direktorij +nfs.mount_options opcije za montiranje +nfs.mounterror greÅ¡ka pri montiranju +nfs.mounterror_notsup ne podnosi se sistem datoteka +nfs.mountnow sada montirati +nfs.mounttimeout greska pri montiranju: tajm-aut +nfs.password lozinka za cifs +nfs.remount direktorije ponovno montirati +nfs.type tip +nfs.type_cifs cifs +nfs.type_nfs nfs +nfs.umount nfs/cifs direktorij sada demontirati +nfs.umounterror greÅ¡ka pri demontiranju +nfs.username ime korisnika za cifs +nfsmenu.head namješćenje nfsa/cifsa +nvod.percentage (%d%% proÅ¡lo) +nvod.starting (poÄetak za %d minuta) +nvodselector.directormode režija slike +nvodselector.head izabrati poÄetak +nvodselector.subservice perspektive +options.default standardno namješćenje +options.fb framebuffer +options.null null +options.off ugaÅ¡eno +options.on upaljeno +options.serial serijski +parentallock.changepin izabrati pin-broj +parentallock.changepin_hint1 Utipkajte novi pin-broj! +parentallock.changetolocked kod zabranjenih buketa +parentallock.head pin-broj za Å¡tićenje omladine +parentallock.lockage emisije +parentallock.lockage12 zabraniti od 12 godina +parentallock.lockage16 zabraniti od 16 godina +parentallock.lockage18 zabraniti od 18 godina +parentallock.lockedchannel ako je kanal zabranjen +parentallock.lockedprogram zabranjen program (od %d godina) +parentallock.never nikada +parentallock.onsignal ako ima privremena Å¡pera na kanalu +parentallock.onstart pri startu +parentallock.parentallock Å¡tićenje omladine +parentallock.prompt pitati za pin-broj +pictureviewer.defdir poÄetni direktorij +pictureviewer.head promatraÄ slika +pictureviewer.scaling skaliranje +pictureviewer.show pokazati +pictureviewer.slide_time trajanje pokazivanja slika +pictureviewer.slideshow pokazivanje slika +pictureviewer.sortorder promijeniti smjer sortiranja +pictureviewer.sortorder.date (datum) +pictureviewer.sortorder.filename (ime datoteke) +ping.ok pristupan (ping) +ping.protocol nije pristupan (host or protocol error) +ping.socket nije pristupan (socket error) +ping.unreachable nije pristupan (unreachable) +pinprotection.head kontrola pin-broja +pinprotection.wrongcode Utipkajte pin-broj ponovno! +rclock.lockmsg Speruje se daljinska dbox2.\n Za prekid spere, molim\n i na daljinskoj\n pretisnuti. +rclock.menueadd fb sperovati +rclock.title daljinsku sperovati +rclock.unlockmsg daljinska je reaktivirana... +recordingmenu.head namjestiti snimanje +recordingmenu.no_scart nema mijenjanja scarta +recordingmenu.off ugaÅ¡eno +recordingmenu.recording_type vrsta snimanja +recordingmenu.server server +recordingmenu.server_ip ip servera za snimanje +recordingmenu.server_mac mac adresa +recordingmenu.server_port port servera za snimanje +recordingmenu.server_wakeup server za snimanje wol +recordingmenu.setupnow namješćenje sada preuzeti +recordingmenu.stopplayback zaustaviti playback +recordingmenu.stopsectionsd zaustaviti sectionsd +recordingmenu.vcr video rikorder +recordingmenu.vcr_devicename ime video rikordera (lirc) +recordtimer.announce snimanje poÄinje za jednu minutu +repeatblocker.hint_1 vrijeme (u ms) izmedu 2 pritiska tipke +repeatblocker.hint_2 0 ugasi blokera (crveno je " ") +satsetup.diseqc diseqc +satsetup.diseqc10 diseqc 1.0 +satsetup.diseqc11 diseqc 1.1 +satsetup.diseqc12 diseqc 1.2 +satsetup.diseqcrepeat ponavljanje diseqca +satsetup.extended diseqc namjestiti +satsetup.extended_lnb namješćenje lnb-ea +satsetup.extended_motor namješćenje motora +satsetup.minidiseqc mini-diseqc +satsetup.motorcontrol namješćenje motora +satsetup.nodiseqc nema diseqca +satsetup.satellite satelit +satsetup.savesettingsnow namješćenje sada memorisati +satsetup.smatvremote smatv remote tuning +scants.actcable kablo: +scants.actsatellite satelit: +scants.bouquet bukete +scants.bouquet_create novo sastaviti +scants.bouquet_erase izbrisati +scants.bouquet_leave ne promijeniti +scants.bouquet_satellite satelitski buket +scants.bouquet_update obnoviti +scants.channel kanal: +scants.failed Neuspjesno traženje kanala! +scants.finished UspjeÅ¡no izvrÅ¡eno traženje kanala! +scants.freqdata frekvencija: +scants.head traženje kanala +scants.numberofdataservices data +scants.numberofradioservices radio +scants.numberoftotalservices ukupno +scants.numberoftvservices tv +scants.provider snabdjevaÄ: +scants.services servisi: +scants.startnow poÄeti traženje kanala +scants.transponders transponderi: +screensetup.lowerright zeleno = ekran dole, desno +screensetup.upperleft crveno = ekran gore, lijevo +servicemenu.head servis +servicemenu.reload liste kanale obnoviti +servicemenu.reload_hint obnavljaju se liste kanala,\nÄekajte molim ... +servicemenu.scants tražiti kanale +servicemenu.ucodecheck provjeriti ucodese +servicemenu.update obnoviti softver +settings.missingoptionsconffile Neutrino je opÅ¡iren.\nNove stvari su namješćene na standardne podatke. +settings.noconffile Podaci o neutrinu nisu naÄ‘eni.\nMeću se standardni podaci. +shutdowntimer.announce Dbox2 će se ugasiti za 1 minutu.\nPrekinuti gaÅ¡enje? +sleeptimerbox.announce vremenski brojaÄ Ä‡e ugasiti za 1 minutu +sleeptimerbox.hint1 ugasiti za XXX minuta. (000=ugaÅ¡eno) +sleeptimerbox.hint2 Dbox2 će se ugasiti za to vrijeme. +sleeptimerbox.title vremenski brojaÄ za ugasiti +streamfeatures.head osobine streama +streamfeatures.info informacija o streamu +streaminfo.aratio odnos +streaminfo.aratio_unknown nepoznat odnos +streaminfo.audiotype vrsta tona +streaminfo.audiotype_unknown nepoznata vrsta tona +streaminfo.bitrate bitrate +streaminfo.ecm_invalid nevažeći ca sistem +streaminfo.framerate framerate +streaminfo.framerate_unknown nepoznat framerate +streaminfo.head informacije +streaminfo.not_available nije pristupno +streaminfo.not_crypted nije Å¡ifrovano +streaminfo.resolution rezolucija +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head namješćenje promatraÄa filmova +streamingmenu.mpeg1 mpeg1 +streamingmenu.mpeg2 mpeg2 +streamingmenu.off ugaseno +streamingmenu.on upaljeno +streamingmenu.server_ip ip streamingservera +streamingmenu.server_port port streamingservera +streamingmenu.streaming_audiorate brzina audia +streamingmenu.streaming_force_avi_rawaudio prisili ac3 kod avia +streamingmenu.streaming_force_transcode_video transkod. i mpg/vcd video +streamingmenu.streaming_resolution resolucija +streamingmenu.streaming_server_cddrive dvd +streamingmenu.streaming_server_startdir direktorij (vlc) +streamingmenu.streaming_transcode_audio transkod. audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec mpeg video vodec +streamingmenu.streaming_type streamingserver +streamingmenu.streaming_videorate brzina videa +streamingserver.noconnect Ne postoji veza sa streamingserverom.\nPrekid snimanja. +stringinput.caps velika/mala slova +stringinput.clear sve izbrisati +timer.eventrecord.msg emisija je zapamćena za snimanje +timer.eventrecord.title zapamtiti snimanje +timer.eventtimed.msg Emisija je zapamćena.\nDbox2 će se automatiÄno upaliti i prebaciti na emisiju. +timer.eventtimed.title zapamtiti emisiju +timerbar.channelswitch prebaciti +timerbar.recordevent snimati +timerlist.alarmtime vrijeme za uzbunu +timerlist.apids audio pids +timerlist.bouquetselect izabrati buket +timerlist.channel kanal +timerlist.channelselect izabrati kanal +timerlist.delete izbrisati +timerlist.empty trenutaÄno nema vremenskih brojaÄa +timerlist.menumodify obraÄ‘ivati vremenski brojaÄ +timerlist.menunew novi vremenski brojaÄ +timerlist.message obavijest +timerlist.moderadio radio kanali +timerlist.modeselect izbor +timerlist.modetv tv kanali +timerlist.modify obrada +timerlist.name lista vremenskih brojaÄa +timerlist.new novi vremenski brojaÄ +timerlist.program.unknown nepoznati program +timerlist.reload obnoviti +timerlist.repeat ponavljanje +timerlist.repeat.biweekly dvosedmniÄno +timerlist.repeat.byeventdescription vidi vremenski brojaÄ +timerlist.repeat.daily dnevno +timerlist.repeat.fourweekly ÄetvorosedmiÄno +timerlist.repeat.friday pet +timerlist.repeat.monday pon +timerlist.repeat.monthly mjeseÄno +timerlist.repeat.once jedanput +timerlist.repeat.saturday sub +timerlist.repeat.sunday ned +timerlist.repeat.thursday Äet +timerlist.repeat.tuesday uto +timerlist.repeat.unknown nepoznato +timerlist.repeat.wednesday sri +timerlist.repeat.weekdays ovim danima +timerlist.repeat.weekly sedmiÄno +timerlist.save memorisati vremenski brojaÄ +timerlist.standby pripravnost +timerlist.standby.off buÄ‘enje iz pripravnosti +timerlist.standby.on idi u pripravnost +timerlist.stoptime vrijeme za stop +timerlist.type vrsta vremenskog brojaÄa +timerlist.type.nextprogram drugi program +timerlist.type.record snimanje +timerlist.type.remind podsjećanje +timerlist.type.shutdown ugasiti +timerlist.type.sleeptimer vremenski brojaÄ za ugasiti +timerlist.type.standby pripravnost +timerlist.type.unknown nepoznato +timerlist.type.zapto prebaciti +timerlist.weekdays dani +timerlist.weekdays.hint_1 pon uto sri Äet pet sub ned +timerlist.weekdays.hint_2 'X'=vremenski brojaÄ '-' nema vremenskog brojaÄa +timersettings.record_safety_time_after ispravljanje kraja snimanja +timersettings.record_safety_time_after.hint_1 vrijeme u minutama koje se odbija od +timersettings.record_safety_time_after.hint_2 kraja vremeskog brojaÄa (00=ugaÅ¡eno) +timersettings.record_safety_time_before ispraviti poÄetak snimanja +timersettings.record_safety_time_before.hint_1 vrijeme u minutama koje se odbija od +timersettings.record_safety_time_before.hint_2 poÄetka vremeskog brojaÄa (00=ugaÅ¡eno) +timersettings.separator namješćenje vremenskog brojaÄa +timing.chanlist lista kanala +timing.epg epg +timing.filebrowser pretraživaÄ datoteka +timing.head osd tajm-auti +timing.hint_1 vrijeme u sekundama u kojemu +timing.hint_2 će biti pokazana informativna traka +timing.infobar infromativna traka +timing.menu izbor +ucodecheck.avia500 avia 500 +ucodecheck.avia600 avia 600 +ucodecheck.cam-alpha cam-alpha +ucodecheck.head provjera ucodesa +ucodecheck.ucode ucode +ucodes.failure Pažnja, nisu naÄ‘eni ucodesi!\n\nMolim sa ftpeom (ili dbox-bootmanagerom)\npostaviti, i dbox2 ponovno startovati! +videomenu.csync namjescenje sinkronizacije +videomenu.head namjestiti video +videomenu.rgb_centering centriranje rgbea +videomenu.screensetup namjestiti ekran +videomenu.vcrswitch automatiÄni av-scart ulaz +videomenu.videoformat format ekrana +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (lb) +videomenu.videoformat_431 4:3 (ps) +videomenu.videoformat_autodetect automatiÄno +videomenu.videosignal vrsta video signala +videomenu.videosignal_composite cvbs +videomenu.videosignal_rgb rgb + cvbs +videomenu.videosignal_svideo s-video +videomenu.videosignal_yuv_c yuv + cvbs +videomenu.videosignal_yuv_v yuv + vbs +zaptotimer.announce za jednu minutu će se dbox2 prebaciti diff --git a/data/locale/ch-baslerdeutsch.locale b/data/locale/ch-baslerdeutsch.locale new file mode 100644 index 000000000..201532198 --- /dev/null +++ b/data/locale/ch-baslerdeutsch.locale @@ -0,0 +1,722 @@ +EPGMenu.epgplus Vorschau Ãœbersicht +EPGMenu.eventinfo Info zuer Sendig +EPGMenu.eventlist Vorschau aktuelles Programm +EPGMenu.head EPG - Programm Information +EPGMenu.streaminfo technische Information +EPGPlus.head Vorschau Ãœbersicht (EPG Plus) +EPGPlus.record Uufnahm +EPGPlus.refresh_epg Aktualisiere +EPGPlus.remind Vormerke +EPGPlus.scroll_mode Scroll Mode +EPGPlus.stretch_mode Stretch Mode +GENRE.ARTS.0 Chunschd / Kultur +GENRE.ARTS.1 Darstellendi Chünschd +GENRE.ARTS.10 Chunschd-/Kultur-Magazin +GENRE.ARTS.11 Mode +GENRE.ARTS.2 fine arts +GENRE.ARTS.3 Religion +GENRE.ARTS.4 Volkskunst +GENRE.ARTS.5 Literatur +GENRE.ARTS.6 Film/Kino +GENRE.ARTS.7 experimental film/video +GENRE.ARTS.8 Rundfunk/Presse +GENRE.ARTS.9 Neui Mediä +GENRE.CHILDRENs_PROGRAMMES.0 Chinder / Jugendprogramm +GENRE.CHILDRENs_PROGRAMMES.1 Programm für Vorschuelchinder +GENRE.CHILDRENs_PROGRAMMES.2 Unterhaltigsprogramm für 6 bis 14-Jährigi +GENRE.CHILDRENs_PROGRAMMES.3 Unterhaltigsprogramm für 10 bis 16-Jährigi +GENRE.CHILDRENs_PROGRAMMES.4 Informations- un Bildigsprogramm +GENRE.CHILDRENs_PROGRAMMES.5 Trickfilm +GENRE.DOCUS_MAGAZINES.0 Doku/Magazin +GENRE.DOCUS_MAGAZINES.1 Natur/Tiere/Umwelt +GENRE.DOCUS_MAGAZINES.2 Naturwissenschaft un Technik +GENRE.DOCUS_MAGAZINES.3 Medizin/Physiologie/Psychologie +GENRE.DOCUS_MAGAZINES.4 Usland/Expeditions +GENRE.DOCUS_MAGAZINES.5 Sozial- un Geischeswissenschaften +GENRE.DOCUS_MAGAZINES.6 wiiderbildig +GENRE.DOCUS_MAGAZINES.7 Sprooche +GENRE.MOVIE.0 Spielfilm / Drama +GENRE.MOVIE.1 Kriminalfilm/Thriller +GENRE.MOVIE.2 Abenteuerfilm/Western/Kriegsfilm +GENRE.MOVIE.3 Science-Fiction/Fantasy/Horror +GENRE.MOVIE.4 Comedy +GENRE.MOVIE.5 Soap/Melodram/Folklore +GENRE.MOVIE.6 Romantik +GENRE.MOVIE.7 serious/classical/religious/hischorical movie/drama +GENRE.MOVIE.8 adult movie/Drama +GENRE.MUSIC_DANCE.0 Musik/Ballet/Tanz +GENRE.MUSIC_DANCE.1 Rock/Pop +GENRE.MUSIC_DANCE.2 serious music/Klassik +GENRE.MUSIC_DANCE.3 Volksmusik +GENRE.MUSIC_DANCE.4 Jazz +GENRE.MUSIC_DANCE.5 Musical/Oper +GENRE.MUSIC_DANCE.6 Ballet +GENRE.NEWS.0 Nachrichte +GENRE.NEWS.1 Nachrichte/Wetter +GENRE.NEWS.2 Nachrichtemagazin +GENRE.NEWS.3 Dokumantation +GENRE.NEWS.4 Diskussion/Interview/Debatte +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 Gameshow/Quiz +GENRE.SHOW.2 varietee +GENRE.SHOW.3 Talkshow +GENRE.SOCIAL_POLITICAL.0 Sozial & Politikereignisse / Wirtschaft +GENRE.SOCIAL_POLITICAL.1 Magazin/Report/Dokumentation +GENRE.SOCIAL_POLITICAL.2 Wirtschaft un Soziales +GENRE.SOCIAL_POLITICAL.3 Bsunderi Mänsche +GENRE.SPORTS.0 Sport +GENRE.SPORTS.1 Großereigniss (Olympische Spiele, Weltmeischerschafte usw.) +GENRE.SPORTS.10 equestrian +GENRE.SPORTS.11 martial sports +GENRE.SPORTS.2 Sportsmagazine +GENRE.SPORTS.3 Fußball +GENRE.SPORTS.4 Tennis +GENRE.SPORTS.5 Mannschaftssport +GENRE.SPORTS.6 Leichtathletik +GENRE.SPORTS.7 Motorsport +GENRE.SPORTS.8 Wassersport +GENRE.SPORTS.9 Wintersport +GENRE.TRAVEL_HOBBIES.0 Reise & Freizeit +GENRE.TRAVEL_HOBBIES.1 Reisen un Tourismus +GENRE.TRAVEL_HOBBIES.2 handicraft +GENRE.TRAVEL_HOBBIES.3 Motor +GENRE.TRAVEL_HOBBIES.4 Fitness un G'sundheit +GENRE.TRAVEL_HOBBIES.5 Choche +GENRE.TRAVEL_HOBBIES.6 Iichauf +GENRE.TRAVEL_HOBBIES.7 Garte +GENRE.UNKNOWN Unbekannt +apids.hint_1 Gib z'erschd d'zschdriemendi APIDs ii, +apids.hint_2 abdrennd durch ' ' in hexadezimaler Form +apidselector.head Sproochuswahl +audiomenu.PCMOffset Lutstärkeabsänkig PCM +audiomenu.analogout Analog-Usgang +audiomenu.avs avs +audiomenu.avs_control Luutstärgi Stüürig avs/os +audiomenu.dolbydigital Dolby Digital automatisch +audiomenu.head Audio-Iischdellige +audiomenu.lirc lirc +audiomenu.monoleft mono lings +audiomenu.monoright mono rächts +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add A'hänge +audioplayer.artist_title Interpret, Titel +audioplayer.defdir Start-Verz. +audioplayer.delete Lösche +audioplayer.deleteall Alles lösche +audioplayer.display_order Anzeig' +audioplayer.fastforward schnälle Vorl. +audioplayer.follow Selektiere akt. Track +audioplayer.id3scan scänn ID3-Tags +audioplayer.keylevel Taschde-Umschaldig +audioplayer.pause Pause +audioplayer.play Abspiele +audioplayer.playing aktuelle track +audioplayer.rewind schnälle Rückl. +audioplayer.screensaver_timeout Bildschirmschoner (min, 0=aus) +audioplayer.shuffle Zuefällig mische +audioplayer.stop A'halte +audioplayer.title_artist Titel, Interpret +bookmarkmanager.delete Lösche +bookmarkmanager.name Bookmarks +bookmarkmanager.rename Umbenenne +bookmarkmanager.select Uuswähle +bouqueteditor.add d'zuemache +bouqueteditor.bouquetname Name vom Bouquet' +bouqueteditor.delete Lösche +bouqueteditor.discardingchanges Änderige werde verworfe. Bitte warte ... +bouqueteditor.hide Verstäcke +bouqueteditor.lock Spääre +bouqueteditor.move Vschiebe +bouqueteditor.name Bouquet-Verwaltig +bouqueteditor.newbouquetname Neue Name fürs Bouquet' +bouqueteditor.rename Umbenenne +bouqueteditor.return Feddig +bouqueteditor.savechanges? Solle die Änderige g'speicheret werde? +bouqueteditor.savingchanges Änderige werde gspeicheret. Bitte warte ... +bouqueteditor.switch Kanal uffneh/entferne +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider Kabelabieder +channellist.head Alle Kanäle +channellist.nonefound M'r hänn keine Kanäl g'funde!\nMach e Kanalsüchi\n(dbox-Taste -> Service) +channellist.since sidd %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blau +colorchooser.green grien +colorchooser.red rot +colormenu.background Hintergrundfarb +colormenu.background_head Hintergrundfarb +colormenu.fade Menüs faden +colormenu.font Schriftgrössi +colormenu.gtx_alpha Transparänz (GTX) +colormenu.head Farb-Iischdellige +colormenu.menucolors Menüfarb +colormenu.statusbar Statusbalke +colormenu.textcolor Textfarb +colormenu.textcolor_head Textfarb +colormenu.themeselect Theme usswähle +colormenu.timing OSD Timeouts +colormenusetup.head Menüfarbe +colormenusetup.menucontent Fenschderinhalt +colormenusetup.menucontent_inactive Fenschderinhalt deaktiviert +colormenusetup.menucontent_selected Fenschderinhalt selektiert +colormenusetup.menuhead Titelleischde +colorstatusbar.head Statusbalken-Farbe +colorstatusbar.text Statusbalken +colorthememenu.classic_theme Klassik +colorthememenu.head Theme usswähle +colorthememenu.neutrino_theme Neutrino Theme +date.Apr Apr +date.Aug Aug +date.Dec Dez +date.Feb Feb +date.Fri Fr +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mär +date.May Mai +date.Mon Mä +date.Nov Nov +date.Oct Okt +date.Sat Sa +date.Sep Sep +date.Sun Su +date.Thu Du +date.Tue Ts +date.Wed Mi +epglist.head Vorschau - %s +epglist.noevents Keini EPG-Informatione verfuigbar. +epgviewer.More_Screenings wiidere Termine uf dämm Kanal +epgviewer.nodetailed Keini usführliche Informationen verfuigbar +epgviewer.notfound keini Programminformatione (EPG) g'funde +favorites.addchannel De aktuelle Kanal wirdm Bouquet \n"Mieni Favoridde" a'ghängt. \nS'speichere gooht e Moment... +favorites.bouquetname Mieni Favoridde +favorites.bqcreated Bouquet "Miini Favoridde" isch a'gelegt...\n +favorites.chadded De aktuelle Kanal isch Ihre Favoridde hinzugefuegt worde...\n +favorites.chalreadyinbq De aktuelle Kanal isch bereits in Ihre Favoridde...\n +favorites.finalhint \nVersehentlich dezuefügti Kanäl chönne mit\nd'r Bouquetverwaltig korrigiert werde.\n +favorites.menueadd Kanal Favoridde d'zue'due +favorites.nobouquets Favoridde sin numme mit aktivierte Bouquets mäglich +filebrowser.delete Lösche +filebrowser.dodelete1 Soll +filebrowser.dodelete2 glöscht werde? +filebrowser.filter.active Filter aah +filebrowser.filter.inactive Filter uus +filebrowser.head Filebrowser +filebrowser.mark Markiere +filebrowser.nextpage Sidde vor +filebrowser.prevpage Sidde zruck +filebrowser.scan Verzeichnisse durchsürche +filebrowser.select Usswähle +filebrowser.showrights Dateirechte a'zeige +filebrowser.sort.date (Datum) +filebrowser.sort.name (Dateiname) +filebrowser.sort.namedirsfirst (Dateiname2) +filebrowser.sort.size (Größe) +filebrowser.sort.type (Typ) +flashupdate.action Aktion: +flashupdate.actionreadflash lese flash +flashupdate.actionwriteflash schrieb grad s'flash +flashupdate.cantopenfile ka Datei nidd uffmache +flashupdate.cantopenmtd ka's MTD-Device nidd uffmache +flashupdate.checkupdate nach neuer Version sueche +flashupdate.currentreleasecycle Releasezyklus +flashupdate.currentversion_sep Installierdi Version +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot ImageTyp +flashupdate.currentversiontime Uhrzit +flashupdate.erasefailed Flash lösche fehlg'schlage +flashupdate.erasing lösch' grad s'Fläsh +flashupdate.experimentalimage Sie henn ä Snapshot usgwählt, bitte beachte Sie, dass\ndie Version untestet isch un u.U. Ihre Box nach dem Update\nnümmi funktionsfähig isch.\n\nSoll die Version wirklich installiert werde? +flashupdate.expertfunctions Experte-Funktione +flashupdate.fileis0bytes d'Dateigrößi isch 0 Byte +flashupdate.fileselector Datei-Usswahl +flashupdate.flashreadyreboot S'Image isch erfolgriich g'flasht.\nd'DBox wird jetz neu g'starded. +flashupdate.getinfofile lade Versioninfo +flashupdate.getinfofileerror ka Info nidd lade +flashupdate.getupdatefile lade Öpdate +flashupdate.getupdatefileerror ka Öpdate nidd lade +flashupdate.globalprogress G'samtstatus: +flashupdate.head Aktualisierig +flashupdate.md5check Imagepruefig +flashupdate.md5sumerror Des Image isch fehlerhaft +flashupdate.msgbox S'het folgendi neui Images:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWillsch die neui Version jetz abelade\nund installiere? +flashupdate.msgbox_manual S'het s'folgend neu Image:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWillsch die neui Version jetz installiere? +flashupdate.mtdselector Partitions-Usswahl +flashupdate.programmingflash programmier grad s'Fläsh +flashupdate.proxypassword Passwort +flashupdate.proxypassword_hint1 Gib's Proxy-Passwort Ii +flashupdate.proxypassword_hint2 +flashupdate.proxyserver Proxyname +flashupdate.proxyserver_hint1 Gib de Proxyname oder d'IP ii (Host:Port) +flashupdate.proxyserver_hint2 E leere Iintrag bedüded kei Proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Benutzername +flashupdate.proxyusername_hint1 Gib de Proxy-Benutzername ii +flashupdate.proxyusername_hint2 E leere Iintrag bedüded kei Authentifizierig +flashupdate.readflash ganzes Flashimage usläse +flashupdate.readflashmtd einzelni Partition usläse +flashupdate.ready ferdig +flashupdate.reallyflashmtd Willsch dä Flashvorgang wirklich mache?\n\nFalls es e'Fehler gitt odder s'Image nidd\nfunktionfäähig isch, wird d'DBox nümmi boote.\n\nImagename: %s\nZiel: %s +flashupdate.reboot Die DBox wird neu g'starded +flashupdate.savesuccess S'Image sich erfolgriich unterm \nDateiname %s gspeichrt. +flashupdate.selectimage Verfügbari Images +flashupdate.titlereadflash Flash ussläse +flashupdate.titlewriteflash Flash schriibe +flashupdate.updatemode Öpdatemodus +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuell (ftp) +flashupdate.url_file Konfigfile +flashupdate.versioncheck Versionsueberpruefig +flashupdate.writeflash ganzes Flashimage i'spile +flashupdate.writeflashmtd einzelni Partition i'spile +flashupdate.wrongbase Die Releasezyklus Version weicht ab, bitte\nzuerst neues Komplett-Image installieren! +fontmenu.channellist Kanallischde +fontmenu.epg EPG +fontmenu.eventlist Eventlist +fontmenu.gamelist Gameliste +fontmenu.head Schriftgrössen Iischdellige +fontmenu.infobar Infobar +fontsize.channellist Kanallischde +fontsize.channellist_descr Beschriebig +fontsize.channellist_number Nummere +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titel +fontsize.eventlist_datetime Eventlist Datum / Ziit +fontsize.eventlist_itemlarge Eventlist gross +fontsize.eventlist_itemsmall Eventlist chlei +fontsize.eventlist_title Eventlist Titel +fontsize.filebrowser_item Dateibrowser-Iidrag +fontsize.gamelist_itemlarge gross +fontsize.gamelist_itemsmall chlei +fontsize.hint Font wird initialisiert,\ne'Moment warde... +fontsize.infobar_channame Infobar Kanal +fontsize.infobar_info Infobar Info +fontsize.infobar_number Infobar Nummere +fontsize.infobar_small Infobar chlei +fontsize.menu Menutext +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titel +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.cantdecode Kanal ka nidd entschlüsselt werde +infoviewer.epgnotload Informatione no'nidd g'lade... +infoviewer.epgwait Wart' uf EPG-Informatione... +infoviewer.eventlist Programm +infoviewer.languages Tonwahl +infoviewer.motor_moving Antennepositionierig +infoviewer.nocurrent Kei Info zuem laufende Programm +infoviewer.noepg Kei EPG-Information verfuigbar +infoviewer.notavailable Kanal (zur Zit) nidd verfieegbar +infoviewer.selecttime Startzite +infoviewer.streaminfo Fiitschrs +infoviewer.subservice Bildregie +infoviewer.waittime Wart' uff'd Uhrzeit... +ipsetup.hint_1 Nimm 0..9 odder abe/uffe +ipsetup.hint_2 OK speichert, Home bricht ab +keybindingmenu.RC Fernbedienig +keybindingmenu.addrecord Uffnahme-Timer d'züfüge +keybindingmenu.addrecord_head Uffnahme-Timer d'züfüge +keybindingmenu.addremind Umschald-Timer d'züfüge +keybindingmenu.addremind_head Umschald-Timer d'züfüge +keybindingmenu.allchannels_on_ok Kanallischde +keybindingmenu.bouquetchannels_on_ok Bouquet-Kanäl +keybindingmenu.bouquetdown Bouquet z'ruck +keybindingmenu.bouquetdown_head Bouquet z'ruck Taste +keybindingmenu.bouquethandling OK-Taste für +keybindingmenu.bouquetlist_on_ok Bouquetlische +keybindingmenu.bouquetup Bouquet widder +keybindingmenu.bouquetup_head Bouquet widder Taste +keybindingmenu.cancel Kanallischde zuemache +keybindingmenu.cancel_head Kanallischde zuemache +keybindingmenu.channeldown Kanal abe +keybindingmenu.channeldown_head Kanal abe Taste +keybindingmenu.channellist Kanallischde +keybindingmenu.channelup Kanal uffe +keybindingmenu.channelup_head Kanal uffe Taste +keybindingmenu.head Tasten-Iischdellige +keybindingmenu.modechange Modus wäggsle +keybindingmenu.pagedown Sidde abe blätter +keybindingmenu.pagedown_head Sidde abe blättere +keybindingmenu.pageup Sidde uffe blättere +keybindingmenu.pageup_head Sidde uffe blättere +keybindingmenu.quickzap Schnällumschaldig +keybindingmenu.repeatblock Wiederholigsverzögerig +keybindingmenu.repeatblockgeneric Afangsverzögerig +keybindingmenu.sort Sortierreihefolg ändere +keybindingmenu.sort_head Sortierreihefolg ändere +keybindingmenu.subchanneldown Underkanal z'ruck +keybindingmenu.subchanneldown_head Underkanal z'ruck +keybindingmenu.subchannelup Underkanal wiider +keybindingmenu.subchannelup_head Underkanal wiider +keybindingmenu.tvradiomode Fernseh-/Radio-Modus +keybindingmenu.tvradiomode_head Fernseh-/Radio-Modus +keychooser.head Neue Taste Iistelle +keychooser.text1 Bitte Taste drucke +keychooser.text2 Zuem abbreche warde.. +keychoosermenu.currentkey Derzitige Taste +keychoosermenu.setnew Neue Taste Iistelle +keychoosermenu.setnone Kei Taste +languagesetup.head Sproochischellige +languagesetup.select Sprooch +lcdcontroler.brightness normale Helligkeit +lcdcontroler.brightnessstandby Standby Helligkeit +lcdcontroler.contrast Kontrast +lcdcontroler.head LCD Stettings +lcdmenu.autodimm Automatisch dimme +lcdmenu.head LCD Iistellige +lcdmenu.inverse Inverdiere +lcdmenu.lcdcontroler Kontrast / Hälligkeit +lcdmenu.power Pfuus +lcdmenu.statusline Statusziile +lcdmenu.statusline.both change me +lcdmenu.statusline.playtime Sendigsfortschritt +lcdmenu.statusline.volume Lutstärki +mainmenu.games Spiele +mainmenu.head Haupdmenü +mainmenu.movieplayer Movieplayer +mainmenu.pausesectionsd EPG uss'läse +mainmenu.pictureviewer Bildbedrachder +mainmenu.radiomode Radio-Modus +mainmenu.recording Sdreaming (ufnehh) +mainmenu.recording_start un los +mainmenu.recording_stop hald +mainmenu.scartmode Scart-Igang +mainmenu.service Sörvis +mainmenu.settings Iischdellige +mainmenu.shutdown Usschalde +mainmenu.sleeptimer Schloofschalder +mainmenu.tvmode TV-Modus +mainsettings.audio Audio +mainsettings.colors Farbe / Theme / Schrifd +mainsettings.head Iischdellige +mainsettings.keybinding Taste-Iischdellige +mainsettings.language Sprooch +mainsettings.lcd LC-Display +mainsettings.misc Diversi Iischdellige +mainsettings.network Netzwärk +mainsettings.plugins Plugin Iischdellige +mainsettings.recording Sdreamingsörfer +mainsettings.savesettingsnow Iischdellige jetz speichere +mainsettings.savesettingsnow_hint Iistellige wärde jetz g'speichered,\ne'Moment warde... +mainsettings.scan Iischdellige Kanalsürch +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back z'ruck +messagebox.back z'ruck +messagebox.cancel Abbruch +messagebox.discard Willsch d'Änderige verwärfe? +messagebox.error Fehler +messagebox.info Informatione +messagebox.no Nei +messagebox.yes Jo +miscsettings.bootinfo Infos bim Boote a'zeige +miscsettings.boxtype DBox Hersdeller +miscsettings.fb_destination EXPERT! Boot-Chonsole +miscsettings.general Allg'mein +miscsettings.head Diverse Iischdellige +miscsettings.infobar_sat_display Infobar Satellite-A'zeig +miscsettings.shutdown_real Sdändbymodus +miscsettings.shutdown_real_rcdelay V'rzögerede Shudown +miscsettings.startbhdriver BH-Mode Triiber lade +motorcontrol.head Motor-Setup +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmark Name +movieplayer.bookmarkname_hint1 Gib de Name für's neue Bookmark ii +movieplayer.bookmarkname_hint2 +movieplayer.buffering Puffere... +movieplayer.defdir Start-Verz. +movieplayer.dvdplayback DVD +movieplayer.fileplayback Datei via VLC +movieplayer.goto Spring' zue ... +movieplayer.goto.h1 = -> absolute Sprung +movieplayer.goto.h2 +,- -> relative Sprung +movieplayer.head Movieplayer +movieplayer.nostreamingserver Es het kei Verbindig\nzuem Streamingserver herg'stellt werde chönne. +movieplayer.pesplayback PES Abspiele (Experimendell) +movieplayer.pleasewait Bitte warde.\nD'Verbindig zuem Streamingserver wird herg'stellt. +movieplayer.toomanybookmarks D'hesch bereits z'viel Bookmarks a'glegt.\nEs mueß erscht e anderes g'löscht werde. +movieplayer.tsplayback TS Abspiele +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp ROT/Home Stop\nGRÃœN Resync\nGELB Paus/Widdr\nBLAU Bookmark setze\nd-box Ziit iiblände\nv Sprung\n1 ca. 1 Minute zruck\n3 ca. 1 Minute vor\n4 ca. 5 Minute zruck\n6 ca. 5 Minute vor\n7 ca. 10 Minute zruck\n9 ca. 10 Minute vor\nHilfe: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.wrongvlcversion Funktion isch mit dr benutzte Version vom VLC nit möglich +networkmenu.broadcast Broadcast +networkmenu.gateway Standard Gateway +networkmenu.head Netzwäerk Iischdellige +networkmenu.ipaddress IP-Adress' +networkmenu.nameserver Nameserver +networkmenu.netmask Netzmaske +networkmenu.setupnow NetzwärkIischdellige jetz zuewiise +networkmenu.setuponstartup Bim Starte Netzwärk setze +networkmenu.show aktive NetzwärkIischdellige zeige +networkmenu.test Netzwärk deschde +nfs.alreadymounted Verzeichnis bereits a'ghängt +nfs.automount Biem Start i'hänge +nfs.dir NFS/CIFS Server Verzeichniss +nfs.ip NFS/CIFS Server IP +nfs.localdir lokales Verzeichniss +nfs.mount NFS/CIFS Verzeichniss i'hänge +nfs.mount_options Mount-Optionen +nfs.mounterror I'häng - Fehler +nfs.mounterror_notsup Dateisystem-Typ wird nit understützt +nfs.mountnow Jetz i'hänge +nfs.mounttimeout Mount-Fehler: Timeout +nfs.password CIFS Passwort +nfs.remount Verzeichnisse erneut mounte +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount NFS/CIFS Verzeichniss abhänge +nfs.umounterror Abhäng-Fehler +nfs.username CIFS Benutzername +nfsmenu.head NFS/CIFS Iischdellige +nvod.percentage (%d%% umme) +nvod.starting (Goht los in %d min) +nvodselector.directormode Bildregie-Modus +nvodselector.head Anfangszit usswähle +nvodselector.subservice Perspektive +options.default Voristellige nee +options.fb frembaffer +options.null null +options.off us +options.on ii +options.serial säriell +parentallock.changepin PIN-Code feschdlege +parentallock.changepin_hint1 Gib de neu PIN-Code ii! +parentallock.changetolocked Bi vorgsperrte Bouquets +parentallock.head Goofe'schutz PIN +parentallock.lockage Sendige +parentallock.lockage12 ab 12 Joohr sperre +parentallock.lockage16 ab 16 Joohr sperre +parentallock.lockage18 ab 18 Joohr sperre +parentallock.lockedchannel Vorgesperrte Sänder... +parentallock.lockedprogram Gspärrtes Programm (FSK ab %d) +parentallock.never Nie +parentallock.onsignal Bi gsendeter Vorsperri +parentallock.onstart Bim Start +parentallock.parentallock Goofe'schutz +parentallock.prompt PIN-Iigab +pictureviewer.defdir Start-Verz. +pictureviewer.head Bildbedrachder +pictureviewer.scaling Skalierig +pictureviewer.show A'zeige +pictureviewer.slide_time Diaschau-A'zeigeduur +pictureviewer.slideshow Diaschau +pictureviewer.sortorder Sort.reihenf. äändere +pictureviewer.sortorder.date (Datum) +pictureviewer.sortorder.filename (Dateiname) +ping.ok isch erreichbar (ping) +ping.protocol isch nidd erreichbar (Host or protocol error) +ping.socket isch nidd erreichbar (Socket error) +ping.unreachable isch nidd erreichbar (unreachable) +pinprotection.head PIN-Abfroog +pinprotection.wrongcode Gib de Code nonemol ii! +rclock.lockmsg D'Fernbedienig der dbox2 wird gsperrt.\nUm'd Sperre uufz'hebe, bitte\n und uff dr Fernbedienig\ndrucke. +rclock.menueadd FB sperre +rclock.title Fernbedienung sperre +rclock.unlockmsg Fernbedienig reaktiviert... +recordingmenu.head Streamingserver Iischdellige +recordingmenu.no_scart Scart-Umschaldig underdrugge +recordingmenu.off uss +recordingmenu.recording_type Uffnahme g'rät +recordingmenu.server sörfer +recordingmenu.server_ip Uffnahmesörfer IP +recordingmenu.server_mac Mac Adräss +recordingmenu.server_port Uffnahmesörfer Port +recordingmenu.server_wakeup Uffnahmesörfer WOL +recordingmenu.setupnow Iinsdellige jetz überneh +recordingmenu.stopplayback Abspiele a'halde +recordingmenu.stopsectionsd Sectionsd a'halde +recordingmenu.vcr Videorecorder +recordingmenu.vcr_devicename VCR Name (lirc) +recordtimer.announce D'Ufnahm beginnt in weniga Minudde +repeatblocker.hint_1 Mindeschtzit (in ms) zwüsche 2 Tastedrück' +repeatblocker.hint_2 0 schaldet de Blocker us (Rot isch " ") +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-Widderholigge +satsetup.extended DiSEqC-Iinstellig +satsetup.extended_lnb LNB-Iistellige +satsetup.extended_motor Motor-Iistellige +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Motor-Setup +satsetup.nodiseqc Kein DiSEqC +satsetup.satellite Sadellid +satsetup.savesettingsnow Iischdellige jetz speichere +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Kabel: +scants.actsatellite Sadellid: +scants.bouquet Bouquets +scants.bouquet_create neu mache +scants.bouquet_erase lösche +scants.bouquet_leave nidd ändere +scants.bouquet_satellite Satellite-Bouquet: +scants.bouquet_update erneuere +scants.channel Kanal: +scants.failed Kanalsuechii fehlg'schlage! +scants.finished Kanalsueche erfolgrich beändet! +scants.freqdata Frequenz: +scants.head Kanalsuechi +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Alli zämme: +scants.numberoftvservices TV +scants.provider Abieter: +scants.services Services: +scants.startnow Kanalsuechi starde +scants.transponders Transponder: +screensetup.lowerright grün = Bildrand unde, rechts +screensetup.upperleft rot = Bildrand obe, links +servicemenu.head Sörvis +servicemenu.reload Kanallischde neu lade +servicemenu.reload_hint Kannallischde wärde neu g'lade,\ne'Moment warde... +servicemenu.scants Kanalsürchi +servicemenu.ucodecheck UCodes tschegge +servicemenu.update Software-Öbdaede +settings.missingoptionsconffile D'Neutrino-Iischdellige wurde erwiidered.\nDie neue Wärt werde uff Standard g'setzt. +settings.noconffile D'Neutrino-Iischdellige wurde nidd\ngfunde. S'werde d'Standardwert gno. +shutdowntimer.announce In einere Minudde wird d'Chischte abe'gfahre.\nShutdown abbräche? +sleeptimerbox.announce Ussschaltzit in 1 min +sleeptimerbox.hint1 Ussschaltzit in Min. (000=aus) +sleeptimerbox.hint2 D'dbox2 schaltet sich nach dere Zit uss. +sleeptimerbox.title Schloofuhr +streamfeatures.head Fiitschrs +streamfeatures.info Stream-Informatione +streaminfo.aratio Verhältnis +streaminfo.aratio_unknown Verhältnis: unbekannt +streaminfo.audiotype Audiotyp +streaminfo.audiotype_unknown Audiotyp: unbekannt +streaminfo.bitrate Bitrate +streaminfo.ecm_invalid igüldiges CA-Syschdem +streaminfo.framerate Bildrate +streaminfo.framerate_unknown Bildrate: unbekannt +streaminfo.head Informatione +streaminfo.not_available nidd verfuigbar +streaminfo.not_crypted nidd verschlüssld +streaminfo.resolution Uflösig +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Movieplayer Iistellige +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Deaktiviert +streamingmenu.on Aktiviert +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Port +streamingmenu.streaming_audiorate Daterade Audio +streamingmenu.streaming_force_avi_rawaudio Erzwinge AC3 bii avi +streamingmenu.streaming_force_transcode_video Transcod. auch mpg/vcd Video +streamingmenu.streaming_resolution Auflösung +streamingmenu.streaming_server_cddrive DVD Laufwerk +streamingmenu.streaming_server_startdir Verzeichniss (VLC) +streamingmenu.streaming_transcode_audio Transcod. Audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec MPEG Video Codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Daterade Video +streamingserver.noconnect Kei Verbindig zuem Streamingsörver.\nd'Uffnahm' wird abbroche. +stringinput.caps Groß-/Chleiueuchstabe +stringinput.clear OSD Timeouts +timer.eventrecord.msg D'Sendig isch zuer Aufnahm vorgmerkt +timer.eventrecord.title Uffnahm vormerke +timer.eventtimed.msg Die Sendig isch vorgmerkt +timer.eventtimed.title Sendig vormerke +timerbar.channelswitch Umschalte +timerbar.recordevent Ufneh +timerlist.alarmtime Alarmzit +timerlist.apids Audio PIDs +timerlist.bouquetselect Bouquet wähle +timerlist.channel Kanal +timerlist.channelselect Kanal wähle +timerlist.delete Lösche +timerlist.empty S'git momentan kei Timer +timerlist.menumodify Timer bearbeitde +timerlist.menunew Neue Timer +timerlist.message Noochricht +timerlist.moderadio Radio-Kanäl +timerlist.modeselect Modus Usswahl +timerlist.modetv TV-Kanäl +timerlist.modify Öpdate +timerlist.name timerliste +timerlist.new Neue Timer +timerlist.program.unknown Programm chenne m'r nid +timerlist.reload Aktualisiere +timerlist.repeat Widderholig +timerlist.repeat.biweekly alli 2 wuche +timerlist.repeat.byeventdescription lueg under timer +timerlist.repeat.daily jede daag +timerlist.repeat.fourweekly alli 4 wuche +timerlist.repeat.friday Fr +timerlist.repeat.monday Mä +timerlist.repeat.monthly jede moned +timerlist.repeat.once eimalig +timerlist.repeat.saturday Sa +timerlist.repeat.sunday Su +timerlist.repeat.thursday Du +timerlist.repeat.tuesday Ts +timerlist.repeat.unknown chenne m'r nid +timerlist.repeat.wednesday Mi +timerlist.repeat.weekdays An Wuchedaag +timerlist.repeat.weekly jedi wuche +timerlist.save speichere +timerlist.standby SB Modus +timerlist.standby.off Uff Standby goh +timerlist.standby.on Uffwache us Standby +timerlist.stoptime Stopzit +timerlist.type Timertyp +timerlist.type.nextprogram Näggschdes Programm +timerlist.type.record Uffnahm +timerlist.type.remind Erinnerig +timerlist.type.shutdown Abe fahre +timerlist.type.sleeptimer Schloofuhr +timerlist.type.standby Standby +timerlist.type.unknown chenne m'r nit +timerlist.type.zapto Umschalte +timerlist.weekdays Wuchedaag +timerlist.weekdays.hint_1 Mä Ts Mi Du Fr Sa Su +timerlist.weekdays.hint_2 'X'=Timer '-' kei Timer +timersettings.record_safety_time_after Uffnahmeänd-Korrekdur +timersettings.record_safety_time_after.hint_1 Korrekdurzit in Min. (00=aus), die uff'd End- +timersettings.record_safety_time_after.hint_2 zit vom jewilige Timer dezuezellt wird +timersettings.record_safety_time_before Uffnahmestart-Korrekdur +timersettings.record_safety_time_before.hint_1 Korregdurzit in Min. (00=aus), die bim Start +timersettings.record_safety_time_before.hint_2 vom jewilige Timer abzoge wärde +timersettings.separator Timeriischdellige +timing.chanlist Kanallischde +timing.epg Epg +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Iinbländzit in Sek., di's OSD +timing.hint_2 uffem TV a'zeigt wird +timing.infobar Infobar +timing.menu Menu +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode Ueberpruefig +ucodecheck.ucode UCode +ucodes.failure Achtig, die µCodes wurde nidd gefunde!\n\nBitte per FTP (oder DBox-BootManager)\nöploaden, un d'DBox neu starte! +videomenu.csync Sync.-Korrektur +videomenu.head Video-Iischdellige +videomenu.rgb_centering RGB-Zentrierig +videomenu.screensetup Bildbereich ischelle +videomenu.vcrswitch Scart-Eingang audomaddisch +videomenu.videoformat Bildschirmformat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect audomaddisch +videomenu.videosignal Video Signalart +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce In einere Minudde wird umgeschalde diff --git a/data/locale/ch-berndeutsch.locale b/data/locale/ch-berndeutsch.locale new file mode 100644 index 000000000..9e5b26f97 --- /dev/null +++ b/data/locale/ch-berndeutsch.locale @@ -0,0 +1,896 @@ +EPGMenu.epgplus Vorschou Ãœbersicht +EPGMenu.eventinfo Info zur Sändig +EPGMenu.eventlist Vorschou aktuells Programm +EPGMenu.head EPG - Programm Informatione +EPGMenu.streaminfo Technischi Information +EPGPlus.actions Aktione +EPGPlus.bybouquet_mode bouquetwiis +EPGPlus.bypage_mode sitewiis +EPGPlus.change_font_size Schriftgrössi ändere +EPGPlus.change_font_style Schriftstil +EPGPlus.change_size Größe ändere +EPGPlus.channelentry_font Kanäu +EPGPlus.channelentry_separationlineheight Höchi Linie zwüsche Kanäuiträg +EPGPlus.channelentry_width Breiti vo de Kanäuname +EPGPlus.channelevententry_font Sändige +EPGPlus.edit_fonts Schrifte ischtöue +EPGPlus.edit_sizes Abmässige ischtöue +EPGPlus.font_style_bold Fett +EPGPlus.font_style_italic Kursiv +EPGPlus.font_style_regular Normau +EPGPlus.footer_fontbouquetchannelname Selektierte Bouquet~/Kanauname +EPGPlus.footer_fontbuttons Buttons +EPGPlus.footer_fonteventdescription Titu 1 selekt. Sändig +EPGPlus.footer_fonteventshortdescription Titu 2 selekt. Sändig +EPGPlus.head Vorschau Ãœbersicht (EPG Plus) +EPGPlus.header_font Menütitu +EPGPlus.horgap1_height Höchi hor. Lücke 1 +EPGPlus.horgap2_height Höchi hor. Lücke 2 +EPGPlus.next_bouquet Bouquet vor +EPGPlus.options Optione +EPGPlus.page_down Site zrugg +EPGPlus.page_up Seite vor +EPGPlus.prev_bouquet Bouquet zrugg +EPGPlus.record Ufnahm +EPGPlus.refresh_epg Aktualisiere +EPGPlus.remind Vormerke +EPGPlus.reset_settings Ischtöuige zruggsetze +EPGPlus.save_settings Ischtöuige schpichere +EPGPlus.scroll_mode Scroll Mode +EPGPlus.select_font_name Schrift wähle +EPGPlus.settings Ischtöuige +EPGPlus.slider_width Breiti vom Slider +EPGPlus.stretch_mode Stretch Mode +EPGPlus.swap_mode Blättere +EPGPlus.timeline_fontdate Zytscala Datum +EPGPlus.timeline_fonttime Zytscala Zyt +EPGPlus.vergap1_width Breiti ver. Lücke 1 +EPGPlus.vergap2_width Breiti ver. Lücke 2 +EPGPlus.view_mode Modus +GENRE.ARTS.0 Kunscht / Kultur +GENRE.ARTS.1 Darschtöuendi Kunscht +GENRE.ARTS.10 Kunst-/Kultur-Magazin +GENRE.ARTS.11 Mode +GENRE.ARTS.2 Fine Arts +GENRE.ARTS.3 Religion +GENRE.ARTS.4 Voukskunscht +GENRE.ARTS.5 Literatur +GENRE.ARTS.6 Föum/Kino +GENRE.ARTS.7 Experimental Föum/Video +GENRE.ARTS.8 Rundfunk/Presse +GENRE.ARTS.9 Nöi Mediä +GENRE.CHILDRENs_PROGRAMMES.0 Chinder / Jugendprogramm +GENRE.CHILDRENs_PROGRAMMES.1 Programm für Vorschouching +GENRE.CHILDRENs_PROGRAMMES.2 Ungerhautigsprogramm für 6 bis 14-Jährigi +GENRE.CHILDRENs_PROGRAMMES.3 Ungerhautigsprogramm für 10 bis 16-Jährigi +GENRE.CHILDRENs_PROGRAMMES.4 Informations- und Böudigsprogramm +GENRE.CHILDRENs_PROGRAMMES.5 Trickföum +GENRE.DOCUS_MAGAZINES.0 Doku/Magazin +GENRE.DOCUS_MAGAZINES.1 Natur/Tiär/Umwäut +GENRE.DOCUS_MAGAZINES.2 Naturwüsseschaft und Technik +GENRE.DOCUS_MAGAZINES.3 Medizin/Psüchologie/Psychologie +GENRE.DOCUS_MAGAZINES.4 Usland/Expeditions +GENRE.DOCUS_MAGAZINES.5 Soziau- u Geischteswüsseschaft +GENRE.DOCUS_MAGAZINES.6 Witerböudig +GENRE.DOCUS_MAGAZINES.7 Schprooche +GENRE.MOVIE.0 Spöuföum / Drama +GENRE.MOVIE.1 Krimi/Thriller +GENRE.MOVIE.2 Abentürföum/Western/Chriägsföum +GENRE.MOVIE.3 Science-Fiction/Fäntäsy/Horror +GENRE.MOVIE.4 Comedy +GENRE.MOVIE.5 Soap/Melodrama/Folklore +GENRE.MOVIE.6 Romantik +GENRE.MOVIE.7 serious/classical/religious/historical movie/drama +GENRE.MOVIE.8 adult movie/Drama +GENRE.MUSIC_DANCE.0 Musik/Ballet/Tanz +GENRE.MUSIC_DANCE.1 Rock/Pop +GENRE.MUSIC_DANCE.2 serious music/Klassik +GENRE.MUSIC_DANCE.3 Vouksmusik +GENRE.MUSIC_DANCE.4 Jäzz +GENRE.MUSIC_DANCE.5 Musical/Oper +GENRE.MUSIC_DANCE.6 Ballet +GENRE.NEWS.0 Nachrichte +GENRE.NEWS.1 Nachrichte/Wätter +GENRE.NEWS.2 Nachrichtemagazin +GENRE.NEWS.3 Dokumentation +GENRE.NEWS.4 Diskussion/Interview/Debatte +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 Gameshow/Quiz +GENRE.SHOW.2 varietee +GENRE.SHOW.3 Talkshow +GENRE.SOCIAL_POLITICAL.0 Sozial & Politikereigniss / Wirtschaft +GENRE.SOCIAL_POLITICAL.1 Magazin/Report/Dokumentation +GENRE.SOCIAL_POLITICAL.2 Wirtschaft u Soziales +GENRE.SOCIAL_POLITICAL.3 Bsungeri Mönsche +GENRE.SPORTS.0 Schport +GENRE.SPORTS.1 Grossereigniss (Olympischi Schpiu, Wäutmeischterschaft usw.) +GENRE.SPORTS.10 Equestrian +GENRE.SPORTS.11 Martial Sports +GENRE.SPORTS.2 Schportmagazin +GENRE.SPORTS.3 Schutte +GENRE.SPORTS.4 Tennis +GENRE.SPORTS.5 Mannschaftsschport +GENRE.SPORTS.6 Liächtathletik +GENRE.SPORTS.7 Motorschport +GENRE.SPORTS.8 Wasserschport +GENRE.SPORTS.9 Winterschport +GENRE.TRAVEL_HOBBIES.0 Reise u Freizyt +GENRE.TRAVEL_HOBBIES.1 Reisen u Tourismus +GENRE.TRAVEL_HOBBIES.2 Handicraft +GENRE.TRAVEL_HOBBIES.3 Motor +GENRE.TRAVEL_HOBBIES.4 Fitness u Gsundheit +GENRE.TRAVEL_HOBBIES.5 Chochä +GENRE.TRAVEL_HOBBIES.6 Iichouf +GENRE.TRAVEL_HOBBIES.7 Garte +GENRE.UNKNOWN Unbekannt +apids.hint_1 Gib diä z'streamende APIDs i, +apids.hint_2 trennt dür ' ' i hexadezimaler Form +apidselector.head Schproochuswau +audiomenu.PCMOffset Lutschterkisänkig PCM +audiomenu.analogout Analog-Usgang +audiomenu.avs avs +audiomenu.avs_control Volume Schtüürig avs/ost +audiomenu.dolbydigital Dolby Digital outomatisch +audiomenu.head Audio-Ischtöuige +audiomenu.lirc lirc +audiomenu.monoleft mono links +audiomenu.monoright mono rächts +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add Dezuefüege +audioplayer.artist_title Interpret, Titu +audioplayer.building_search_index Erstöue Suechindex +audioplayer.button_select_title_by_id Sueche n. ID +audioplayer.button_select_title_by_name Sueche n. Name +audioplayer.defdir Start-Verz. +audioplayer.delete Lösche +audioplayer.deleteall Aui lösche +audioplayer.display_order Azeige +audioplayer.enable_sc_metadata Aktiviere Shoutcast Meta-Daten Parsing +audioplayer.fastforward schnäue Vorl. +audioplayer.follow Selektiere akt. Track +audioplayer.head Audio Playlist +audioplayer.highprio Höchi Decodier-Priorität +audioplayer.id3scan Scanne ID3-Tags +audioplayer.jump_backwards rückw. schpringe +audioplayer.jump_dialog_hint1 Bitte Schprungziu igä +audioplayer.jump_dialog_hint2 (relativ, i Sekunde) +audioplayer.jump_dialog_title Sprungziu igä +audioplayer.jump_forwards vorw. schpringe +audioplayer.keylevel Taschte-Umschautig +audioplayer.name Audioplayer +audioplayer.pause Pouse +audioplayer.play Abschpiele +audioplayer.playing Aktuelle Track +audioplayer.playlist_fileerror_msg Datei cha nid erschtöut wärde: +audioplayer.playlist_fileerror_title Fähler +audioplayer.playlist_fileoverwrite_msg Söu diä Datei überschriebe wärde: +audioplayer.playlist_fileoverwrite_title Ãœberschribe? +audioplayer.playlist_name Dateiname vo de Playlist +audioplayer.playlist_name_hint1 Gib de Dateiname vo de Playlist i +audioplayer.playlist_name_hint2 d'Erwiterig .m3u wird outomatisch agheicht +audioplayer.reading_files Lise d'Metadate ii +audioplayer.repeat_on Repeatmodus aktiviere +audioplayer.rewind schnäue Rückl. +audioplayer.save_playlist Playlist schpychere +audioplayer.screensaver_timeout Böudschirmschoner (min, 0=aus) +audioplayer.select_title_by_name Titusuechi noch Name (SMS) +audioplayer.show_playlist Playlist azeige +audioplayer.shuffle Shuffle +audioplayer.stop Stopp +audioplayer.title_artist Titu, Interpret +audioplayerpicsettings.general Audioplayer / Böudbetrachter +bookmarkmanager.delete Lösche +bookmarkmanager.name Bookmarks +bookmarkmanager.rename Umbenenne +bookmarkmanager.select Uswähle +bouqueteditor.add Dezuäfüäge +bouqueteditor.bouquetname Name vom Bouquet +bouqueteditor.delete Lösche +bouqueteditor.discardingchanges änderige wärde verworfe. Bitte warte ... +bouqueteditor.hide Vörschteckä +bouqueteditor.lock Schperre +bouqueteditor.move Vürschiäbe +bouqueteditor.name Bouquet-Verwautig +bouqueteditor.newbouquetname Nöiä Name vom Bouquet +bouqueteditor.rename Umbenänne +bouqueteditor.return Fertig +bouqueteditor.savechanges? Söue d'änderige gschpicheret wärde? +bouqueteditor.savingchanges änderige wärde gschpicheret. Bitte warte ... +bouqueteditor.switch Kanau ufnä/entfärne +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider Kabuabiäter +channellist.head Aui Kanäu +channellist.nonefound äs si kei Kanäu gfungä wordä!\nBitte füär ä Kanausuächi dürä\n(dbox-Taschtä -> Service) +channellist.since sit %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blau +colorchooser.green grüän +colorchooser.red rot +colormenu.background Hingergrundfarb +colormenu.background_head Hingergrundfarb +colormenu.fade Menüs faden +colormenu.font Schriftgrössi +colormenu.gtx_alpha Transparänz (GTX) +colormenu.head Farb-Ischtöuige +colormenu.menucolors Menüfarbe +colormenu.statusbar Schtatusbauke +colormenu.textcolor Textfarb +colormenu.textcolor_head Textfarb +colormenu.themeselect Theme uswähle +colormenu.timing OSD Timeouts +colormenusetup.head Menüfarbä +colormenusetup.menucontent Fäischterinhaut +colormenusetup.menucontent_inactive Fäischterinhaut deaktiviert +colormenusetup.menucontent_selected Fäischterinhaut selektiert +colormenusetup.menuhead Tituleischte +colorstatusbar.head Schtatusbaukä-Farb +colorstatusbar.text Schtatusbaukä +colorthememenu.classic_theme Klassik +colorthememenu.dblue_theme DarkBlue Theme +colorthememenu.dvb2k_theme DVB2000 Theme +colorthememenu.head Theme uswähle +colorthememenu.neutrino_theme Neutrino Theme +date.Apr Apr +date.Aug Aug +date.Dec Dez +date.Feb Feb +date.Fri Fr +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mär +date.May Mai +date.Mon Mo +date.Nov Nov +date.Oct Okt +date.Sat Sa +date.Sep Sep +date.Sun So +date.Thu Do +date.Tue Di +date.Wed Mi +epglist.head Vorschou - %s +epglist.noevents Kei EPG-Informationä verfüägbar. +epgviewer.More_Screenings Witäri Termin uf däm Kanau +epgviewer.nodetailed Kei usfüärlichi Informationä verfüägbar +epgviewer.notfound kei Programminformationä (EPG) gfunge +eventlistbar.channelswitch Umschaute +eventlistbar.eventsort Sortiere +eventlistbar.recordevent Ufnä +favorites.addchannel Dr aktuell Kanau wird zum Bouquet \n"Mini Favorite" däzuägfüegt. \ns'Schpichere bruucht ä Momänt... +favorites.bouquetname Mini Favorite +favorites.bqcreated Bouquet "Mini Favorite" isch aagleit worde...\n +favorites.chadded Dr aktuell Kanau isch zu dä Favoritä däzuägfüegt worde...\n +favorites.chalreadyinbq dr aktuell Kanau isch bereits i dä Favorite...\n +favorites.finalhint \nUs Versehä däzuegfüegti Kanäu chöi mit\ndr Bouquetverwautig korrigiert wärde.\n +favorites.menueadd Kanau i d'Favorite däzuefüege +favorites.nobouquets Favorite si nume mit aktivierte Bouquets müglech +filebrowser.delete Lösche +filebrowser.denydirectoryleave Startverzeichnis absolut +filebrowser.dodelete1 Söu +filebrowser.dodelete2 glöscht wärde? +filebrowser.filter.active Föuter a +filebrowser.filter.inactive Föuter us +filebrowser.head Filebrowser +filebrowser.mark Markiere +filebrowser.nextpage Site vor +filebrowser.prevpage Site zrugg +filebrowser.scan Dürsueche Verzeichnis +filebrowser.select Auswähle +filebrowser.showrights Dateirächt aazeige +filebrowser.sort.date (Datum) +filebrowser.sort.name (Dateiname) +filebrowser.sort.namedirsfirst (Dateiname2) +filebrowser.sort.size (Grössi) +filebrowser.sort.type (Typ) +filesystem.is.utf8 Dateisystem +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash lisä Flash +flashupdate.cantopenfile cha Datei nid öffnä +flashupdate.cantopenmtd cha s'MTD-Device nid öffnä +flashupdate.checkupdate noch nöiere version suäche +flashupdate.currentreleasecycle Releasezyklus +flashupdate.currentversion_sep Installierti Version +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot ImageTyp +flashupdate.currentversiontime Uhrzyt +flashupdate.erasefailed Flash lösche fäugschlage +flashupdate.erasing lösche Flash +flashupdate.experimentalimage Du hesch ä Snapshot usgwäut, bitte beacht,\ndass diä Version nid teschtet isch u\neventuell dini Box nochem Update\nnüm funktionsfähig isch.\n\Wosch diä Version würklech installiere? +flashupdate.expertfunctions Expertä-Funktione +flashupdate.fileis0bytes d'Dateigrössi isch 0 Byte +flashupdate.fileselector Datei-Uswau +flashupdate.flashreadyreboot s'Image isch erfougrich gflasht worde.\nd'Box wird jetzt nöi gschtartet. +flashupdate.getinfofile lade Versionsinfo +flashupdate.getinfofileerror cha s'Update nid läse +flashupdate.getupdatefile lade Update +flashupdate.getupdatefileerror cha s'Update nid ladä +flashupdate.globalprogress Gsamtschtatus: +flashupdate.head Aktualisiärig +flashupdate.md5check Imageprüäfig +flashupdate.md5sumerror s'Image isch fählerhaft +flashupdate.msgbox äs isch fougendes nöis Image gfunge wordä:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWosch diä Version jetzt abelade\nu inschtaliäre? +flashupdate.msgbox_manual äs isch äs nöis Image gfunge worde:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWosch diä Version jetzt inschtalliäre? +flashupdate.mtdselector Partitions-Uswau +flashupdate.programmingflash programmierä Flash +flashupdate.proxypassword Passwort +flashupdate.proxypassword_hint1 Gib s'Proxy +flashupdate.proxypassword_hint2 Passwort i +flashupdate.proxyserver Proxyname +flashupdate.proxyserver_hint1 Gib ä Proxyname oder IP i (Host:Port) +flashupdate.proxyserver_hint2 ä läre Iitrag bedütet kei Proxy +flashupdate.proxyserver_sep Proxysörver +flashupdate.proxyusername Benutzername +flashupdate.proxyusername_hint1 Gib ä Proxy-Benutzername i +flashupdate.proxyusername_hint2 ä lääre Iitrag bedütet kei Authentifizierig +flashupdate.readflash ganzes Flashimage usläse +flashupdate.readflashmtd einzelni Partition usläse +flashupdate.ready fertig +flashupdate.reallyflashmtd Wosch dä Flashvorgang würklech düräfüere?\n\nFaus ä Fähler uftritt oder s'Image nid\nfunktionstüchtig isch, wird dini Box nüm bootä.\n\nImagename: %s\nZiel: %s +flashupdate.savesuccess s'Image isch erfougrich ungerem\nDateiname %s gschpicheret worde. +flashupdate.selectimage Verfüegbari Images +flashupdate.squashfs.noversion Bi SquashFS Updates wärde Versionsüberprüefige momentan nur bi Web-Updates ungerstützt.\nWosch s'usgewäute Image würklech installiere? +flashupdate.titlereadflash Flash usläse +flashupdate.titlewriteflash Flash schribe +flashupdate.updatemode Updatemodus +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuell (ftp) +flashupdate.url_file Konfigfile +flashupdate.versioncheck Versionsüberprüäfig +flashupdate.writeflash ganzes Flashimage iischpile +flashupdate.writeflashmtd einzelni Partition iischpile +flashupdate.wrongbase s'Releasezyklus wiicht ab, bitte\nzersch äs nöis Komplett-Image inschtaliäre! +fontmenu.channellist Kanaulischte +fontmenu.epg EPG +fontmenu.eventlist Eventlist +fontmenu.gamelist Gamelischte +fontmenu.head Schriftgrössi Ischtöuige +fontmenu.infobar Infobar +fontsize.channel_num_zap Diräktuswau +fontsize.channellist Kanaulischte +fontsize.channellist_descr Beschribig +fontsize.channellist_number Nummer +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titu +fontsize.eventlist_datetime Eventlist Datum / Zyt +fontsize.eventlist_itemlarge Eventlist gross +fontsize.eventlist_itemsmall Eventlist chlii +fontsize.eventlist_title Eventlist Titu +fontsize.filebrowser_item Dateibrowser-Itrag +fontsize.gamelist_itemlarge gross +fontsize.gamelist_itemsmall chlii +fontsize.hint Font wird initialisiert,\nbitte warte... +fontsize.infobar_channame Infobar Kanau +fontsize.infobar_info Infobar Info +fontsize.infobar_number Infobar Nummer +fontsize.infobar_small Infobar chlii +fontsize.menu Menütext +fontsize.menu_info Menü Info +fontsize.menu_title Menü Titu +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload Informatione no nid glade... +infoviewer.epgwait Warte uf EPG-Informatione... +infoviewer.eventlist Programm +infoviewer.languages Tonwau +infoviewer.motor_moving Antennäpositioniärig +infoviewer.nocurrent Kei Infos zum loufende Programm +infoviewer.noepg Kei EPG-Informationä verfüegbar +infoviewer.notavailable Kanau (zur Zyt) nid verfüägbar +infoviewer.selecttime Schtartzyte +infoviewer.streaminfo Features +infoviewer.subchan_disp_pos Ungerkanauazeig +infoviewer.subservice Böudregie +infoviewer.waittime Warte uf d'Uhrzyt... +ipsetup.hint_1 Benutz 0..9 oder uäche/abä +ipsetup.hint_2 OK schpicheret, Home bricht ab +keybindingmenu.RC Färnbediänig +keybindingmenu.addrecord Ufnahm-Timer däzuäfüege +keybindingmenu.addrecord_head Ufnahm-Timer däzuäfüege +keybindingmenu.addremind Umschaut-Timer däzuäfüege +keybindingmenu.addremind_head Umschaut-Timer däzuäfüege +keybindingmenu.allchannels_on_ok Kanaulischtä +keybindingmenu.bouquetchannels_on_ok Bouquet-Kanäu +keybindingmenu.bouquetdown Bouquet zrugg +keybindingmenu.bouquetdown_head Bouquet zrugg Taschte +keybindingmenu.bouquethandling OK-Taschte für +keybindingmenu.bouquetlist_on_ok Bouquetlischtä +keybindingmenu.bouquetup Bouquet witer +keybindingmenu.bouquetup_head Bouquet witer Taschte +keybindingmenu.cancel Kanaulischtä schliässe +keybindingmenu.cancel_head Kanaulischtä schliässe +keybindingmenu.channeldown Kanau abä +keybindingmenu.channeldown_head Kanau abä Taschte +keybindingmenu.channellist Kanaulischtä +keybindingmenu.channelup Kanau uäche +keybindingmenu.channelup_head Kanau uäche Taschte +keybindingmenu.head Taschtä-Ischtöuige +keybindingmenu.lastchannel Letschte Kanau +keybindingmenu.lastchannel_head Letschte Kanau +keybindingmenu.modechange Modus wächsle +keybindingmenu.pagedown Sitä abäblettere +keybindingmenu.pagedown_head Sitä abäblettere +keybindingmenu.pageup Sitä uechäblettere +keybindingmenu.pageup_head Sitä uechäblettere +keybindingmenu.quickzap Schnäuumschautig +keybindingmenu.repeatblock Widerhouverzögerig +keybindingmenu.repeatblockgeneric Aafangsverzögerig +keybindingmenu.sort Sortierreiäfoug ändere +keybindingmenu.sort_head Sortierreiäfoug ändere +keybindingmenu.subchanneldown Ungerkanau zrugg +keybindingmenu.subchanneldown_head Ungerkanau zrugg +keybindingmenu.subchannelup Ungerkanau witer +keybindingmenu.subchannelup_head Ungerkanau witer +keybindingmenu.tvradiomode Fernseh-/Radio-Modus +keybindingmenu.tvradiomode_head Fernseh-/Radio-Modus +keybindingmenu.zaphistory Zapping-History Bouquet +keybindingmenu.zaphistory_head Zapping-History Bouquet +keychooser.head Nöii Taschte iischtöuä +keychooser.text1 Bitte Taschte drücke +keychooser.text2 Zum abbräche wartä.. +keychoosermenu.currentkey Momentani Taschte +keychoosermenu.setnew Nöii Taschte ischtöuä +keychoosermenu.setnone kei Taschte +languagesetup.head Schproochiischtöuige +languagesetup.select Schprooch +lcdcontroler.brightness normali Häuigkeit +lcdcontroler.brightnessstandby Standby Häuigkeit +lcdcontroler.contrast Kontrascht +lcdcontroler.head LCD Ischtöuigä +lcdmenu.autodimm Outomatisch dimme +lcdmenu.head LCD Ischtöuigä +lcdmenu.inverse Invertierä +lcdmenu.lcdcontroler Kontrascht / Häuigkeit +lcdmenu.power Power +lcdmenu.statusline Schtatuszilä +lcdmenu.statusline.both Lutschterchi / Fortschritt +lcdmenu.statusline.playtime Sändigsvortschritt +lcdmenu.statusline.volume Lutschterchi +mainmenu.audioplayer Audioplayer +mainmenu.games Schpili +mainmenu.head Houptmenü +mainmenu.movieplayer Movieplayer +mainmenu.pausesectionsd EPG usläse +mainmenu.pictureviewer Böudbetrachter +mainmenu.radiomode Radio-Modus +mainmenu.recording Ufnäh +mainmenu.recording_start Schtart +mainmenu.recording_stop Schtopp +mainmenu.scartmode Scart-Iigang +mainmenu.scripts Skripte +mainmenu.service Service +mainmenu.settings Ischtöuige +mainmenu.shutdown Usschaute +mainmenu.sleeptimer SleepTimer +mainmenu.tvmode TV-Modus +mainsettings.audio Audio +mainsettings.colors Farbä / Themes / Schrift +mainsettings.head Ischtöuige +mainsettings.keybinding Taschtä Ischtöuige +mainsettings.language Schprooch +mainsettings.lcd LC-Display +mainsettings.misc Diversi Ischtöuige +mainsettings.network Netzwärk +mainsettings.recording Ufnahm +mainsettings.savesettingsnow Ischtöuige jetzt schpichere +mainsettings.savesettingsnow_hint Ischtöuige wärde jetzt gschpicheret,\nbitte wartä... +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back Zrugg +messagebox.back Zrugg +messagebox.cancel Abbruch +messagebox.discard Wosch d'änderige verwärfe? +messagebox.error Fähler +messagebox.info Informatione +messagebox.no Nei +messagebox.yes Jo +miscsettings.bootinfo Infos bim Boote azeige +miscsettings.bootmenu Bootmenü azeige +miscsettings.driver_boot Triber- u Bootoptione +miscsettings.fb_destination EXPERT! Boot-Konsole +miscsettings.general Augemein +miscsettings.head Diversi Ischtöuige +miscsettings.hwsections HW-Sections verwände +miscsettings.infobar_sat_display Infobar Satelliteazeig +miscsettings.noaviawatchdog AVIA-Watchdog aktiviere +miscsettings.noenxwatchdog eNX-Watchdog aktiviere +miscsettings.pmtupdate PMT Update verwände +miscsettings.shutdown_real Standbymodus +miscsettings.shutdown_real_rcdelay Verzögerte Shutdown +miscsettings.sptsmode SPTS-Mode Triber lade +miscsettings.startbhdriver BH-Mode Triber lade +motorcontrol.head Motor-Setup +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmark Name +movieplayer.bookmarkname_hint1 Gib ä Name für's nöie Bookmark i. +movieplayer.bookmarkname_hint2 +movieplayer.buffering Puffern... +movieplayer.defdir Schtart-Verz. +movieplayer.defplugin Start-Plugin +movieplayer.dvdplayback DVD +movieplayer.fileplayback Datei via VLC +movieplayer.goto Schpringe zu ... +movieplayer.goto.h1 = -> absolute Schprung +movieplayer.goto.h2 +,- -> relative Schprung +movieplayer.head Movieplayer +movieplayer.nostreamingserver Verbindig zum\nStreamingsörver fäugschlage. +movieplayer.pleasewait Bitte warte.\nd'Verbindig zum Streamingsörver wird härgschtöut. +movieplayer.toomanybookmarks Du hesch scho zviu Bookmarks aagleit.\nEs muess zersch äs Angers glöscht wärde. +movieplayer.tshelp1 Stop +movieplayer.tshelp10 ca. 10 Minute zrugg +movieplayer.tshelp11 ca. 10 Minute vor +movieplayer.tshelp12 Höuf: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Uswau vo de Tonspur +movieplayer.tshelp3 Pouse/Witer +movieplayer.tshelp4 Bookmark setze +movieplayer.tshelp5 Zyt iblände +movieplayer.tshelp6 ca. 1 Minute zrugg +movieplayer.tshelp7 ca. 1 Minute vor +movieplayer.tshelp8 ca. 5 Minute zrugg +movieplayer.tshelp9 ca. 5 Minute vor +movieplayer.tsplayback TS Abschpile +movieplayer.tsplayback_pc TS Abschpiele mit PIN +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 ca. 10 Minute zrugg +movieplayer.vlchelp11 ca. 10 Minuten vor +movieplayer.vlchelp12 Höuf: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pouse/Witer +movieplayer.vlchelp4 Bookmark setze +movieplayer.vlchelp5 Zyt iblände +movieplayer.vlchelp6 ca. 1 Minute zrugg +movieplayer.vlchelp7 ca. 1 Minute vor +movieplayer.vlchelp8 ca. 5 Minute zrugg +movieplayer.vlchelp9 ca. 5 Minute vor +movieplayer.wrongvlcversion Funktion isch mit dere Version vom VLC nid müglech +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway Schtandard Gateway +networkmenu.head Netzwärk Ischtöuige +networkmenu.ipaddress IP-Adrässi +networkmenu.mount NFS/CIFS +networkmenu.nameserver Namesörver +networkmenu.netmask Netzmaske +networkmenu.setupnow Netzwärkischtöuigä jetzt zuäwise +networkmenu.setuponstartup Bim schtartä Netzwärk setze +networkmenu.show aktivi Netzwärkischtöuigä zeigä +networkmenu.test Netzwärk teschte +nfs.alreadymounted Verzeichnis bereits gemountet +nfs.automount Bim Schtart mounte +nfs.dir NFS/CIFS Verzeichnis +nfs.ip NFS/CIFS Sörver IP +nfs.localdir lokaus Verzeichnis +nfs.mount NFS/CIFS Verzeichnis mounte +nfs.mount_options Mount-Optione +nfs.mounterror Mount-Fähler +nfs.mounterror_notsup Dateisystem-Typ wird nid ungerstützt +nfs.mountnow Jetz mounte +nfs.mountok Mount erfougriich +nfs.mounttimeout Mount-Fehler: Timeout +nfs.password CIFS Passwort +nfs.remount Verzeichnisse erneut mounte +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount NFS/CIFS Verzeichnis unmounte +nfs.umounterror Umount-Fähler +nfs.username CIFS Benutzername +nfsmenu.head NFS/CIFS/FTPFS Ischtöuige +nvod.percentage (%d%% verbi) +nvod.starting (Beginn i %d min) +nvodselector.directormode Böudregie-Modus +nvodselector.head Aafangszyt uswähle +nvodselector.subservice Perschpektivä +options.default Vorischtöuige bruuchä +options.fb framebuffer +options.null nou +options.off aus +options.on ein +options.serial seriell +parentallock.changepin PIN-Code feschtlege +parentallock.changepin_hint1 Gib do dä nöi PIN-Code i! +parentallock.changetolocked Bi vorgschperrte Bouquets +parentallock.head Jugendschutz PIN +parentallock.lockage Sändige +parentallock.lockage12 ab 12 Johr sperre +parentallock.lockage16 ab 16 Johr sperre +parentallock.lockage18 ab 18 Johr sperre +parentallock.lockedchannel Vorgschperrte Sänder... +parentallock.lockedprogram Gschperrts Programm (FSK ab %d) +parentallock.never Niä +parentallock.onsignal Bi gsändeter Vorschperri +parentallock.parentallock Jugendschutz +parentallock.prompt PIN-Igab +pictureviewer.decode_server_ip Decoding-Server IP +pictureviewer.decode_server_port Decoding-Server Port +pictureviewer.defdir Schtart-Verz. +pictureviewer.head Böudbetrachter +pictureviewer.help1 Menü-Modus +pictureviewer.help10 Anzeige-Modus +pictureviewer.help11 Böud nöi iläse +pictureviewer.help12 vorhäriges Böud +pictureviewer.help13 nöchscht's Böud +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 Scroll obsi +pictureviewer.help17 Scroll links +pictureviewer.help18 Scroll rächts +pictureviewer.help19 Scroll nidsi +pictureviewer.help2 Böud azeige +pictureviewer.help20 Sortierig ändere +pictureviewer.help21 Bild unskaliert iläse +pictureviewer.help22 Modus verloh +pictureviewer.help3 Sortierig ändere +pictureviewer.help4 Böud unskaliert iläse +pictureviewer.help5 Diashow-Modus +pictureviewer.help6 vorhärigs Böud +pictureviewer.help7 nöchscht's Böud +pictureviewer.help8 Sortierig ändere +pictureviewer.help9 Modus verloh +pictureviewer.resize.color_average Ufwändig +pictureviewer.resize.none Keni +pictureviewer.resize.simple Eifach +pictureviewer.scaling Skalierig +pictureviewer.show Azeige +pictureviewer.slide_time Diaschou-Azeigduur +pictureviewer.slideshow Diaschou +pictureviewer.sortorder Sort.reiäfoug ändere +pictureviewer.sortorder.date (Datum) +pictureviewer.sortorder.filename (Dateiname) +ping.ok isch erreichbar (ping) +ping.protocol isch nid erreichbar (Host or protocol error) +ping.socket isch nid erreichbar (Socket error) +ping.unreachable isch nid erreichbar (unreachable) +pinprotection.head PIN-Abfroog +pinprotection.wrongcode Gib dä Code nomou i! +plugins.result Pluginusgab +rclock.lockmsg d'Färnbedienig vo de DBOX2 wird gschperrt.\num d'Schperri ufzhebe, bitte\n u uf dr Färnbedienig\ndruecke. +rclock.menueadd FB schperre +rclock.title Färnbedienig schperre +rclock.unlockmsg Färnbedienig reaktiviert... +recordingmenu.choose_direct_rec_dir Bi Sofortufn. Verzeichnisuswau +recordingmenu.defdir Ufnahmeverzeichnis +recordingmenu.epg_for_filename langi Dateiname (mit EPG-Date) +recordingmenu.file Diräkt (Datei) +recordingmenu.filesettingsseparator Diräktufnahm +recordingmenu.head Ufnahm Ischtöuige +recordingmenu.help Aufnahmegeräte:\n-----------------------\nServer:\nuf PC mit Höuf vomene Streaming-Programm\n\n(analoge) Videorekorder:\nüber VCR-Usgang\n\nDiräkt (Datei):\nuf es per NFS gemountetes Verzeichnis\noder ä interni Feschtplatte\nTS: SPTS-Mode Triber lade\nPES: SPTS-Mode Triber nid lade\n\n\nMaximali Dateigrössi:\n----------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: fasch unändlech (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart Ungerdrücke Scart-Umschautig +recordingmenu.off aus +recordingmenu.recording_type Ufnahme Grät +recordingmenu.ringbuffers Anz. Ringpuffer +recordingmenu.server Sörver +recordingmenu.server_ip Ufnahmesörver IP +recordingmenu.server_mac Mac Adrässi +recordingmenu.server_port Ufnahmesörver Port +recordingmenu.server_wakeup Ufnahmäsörver WOL +recordingmenu.setupnow Ischtöuigä jetzt übernää +recordingmenu.splitsize Max. Dateigrössi (MB) +recordingmenu.stopplayback Playbäck aahaute +recordingmenu.stopsectionsd Sectionsd aahaute +recordingmenu.stream_all_audio_pids Aui Audiopids ufzeichne +recordingmenu.stream_vtxt_pid Videotext ufzeichne +recordingmenu.use_fdatasync Synchrones Schribe (fdatasync) +recordingmenu.use_o_sync Synchrones Schribe (O_SYNC) +recordingmenu.vcr Videorekorder +recordtimer.announce d'Ufnahm beginnt i wenige Minute +repeatblocker.hint_1 Mindeschtzyt (in ms) zwüschä 2 Taschtädrück +repeatblocker.hint_2 0 schautet dä Blocker us (Rot isch " ") +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-Widerholige +satsetup.extended DiSEqC-Ischtöuige +satsetup.extended_motor Motor-Ischtöuige +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manuells Motor-Setup +satsetup.nodiseqc Keis DiSEqC +satsetup.satellite Satellit +satsetup.savesettingsnow Ischtöuigä jetzt schpichere +satsetup.smatvremote SMATV Remote Tuning +scantp.abort_scan Breche Scan ab +scantp.fec FEC +scantp.fec_1_2 1/2 +scantp.fec_2_3 2/3 +scantp.fec_3_4 3/4 +scantp.fec_5_6 5/6 +scantp.fec_7_8 7/8 +scantp.freq Frequänz +scantp.pol Polarisation +scantp.pol_h H +scantp.pol_v V +scantp.rate Symbol rate +scantp.scan Manual Scan +scantp.scanmode Schnäu Scan +scants.actcable Kabu: +scants.actsatellite Satellit: +scants.bouquet Bouquets +scants.bouquet_create nöi erschtöuä +scants.bouquet_erase lösche +scants.bouquet_leave nid ändere +scants.bouquet_satellite Satelliten-Bouquet +scants.bouquet_update erneuere +scants.channel Kanau: +scants.failed Kanausuächi fäugschlage! +scants.finished Kanausuächi erfougrich beändet! +scants.freqdata Frequänz: +scants.head Kanausuächi +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Gsamt +scants.numberoftvservices TV +scants.provider Aabieter: +scants.startnow Kanausuächi schtartä +scants.transponders Transponder: +screensetup.lowerright grün = Böudrand ungä, rächts +screensetup.upperleft rot = Böudrand obä, links +servicemenu.head Service +servicemenu.reload Kanaulischtä nöi lade +servicemenu.reload_hint Kanaulischtä wärde nöi glade,\nbitte wartä... +servicemenu.scants Kanausuächi +servicemenu.ucodecheck UCodes überprüäfe +servicemenu.update Software-Aktualisierig +settings.help Höuf +settings.missingoptionsconffile d'Neutrino-Ischtöuige si erwitert worde.\nDi nöiä Wärt wärde uf Standart gsetzt. +settings.noconffile d'Neutrino Ischtöuige si nid gfungä\nworde. äs wärde d'Standartwärt bruucht. +settings.pos_bottom_left unge links +settings.pos_bottom_right unge rächts +settings.pos_top_left obe links +settings.pos_top_right obe rächts +shutdowntimer.announce d'Box wird i einer Minute abägfahre.\nShutdown abbräche? +sleeptimerbox.announce Sleeptimer i 1 min +sleeptimerbox.hint1 Usschautzyt i Min. (000=aus) +sleeptimerbox.hint2 d'Box schautet sich noch derä Zyt uus. +sleeptimerbox.title Sleeptimer +streamfeatures.head Features +streaminfo.aratio Verhäutnis +streaminfo.aratio_unknown Verhäutnis: unbekannt +streaminfo.audiotype Audiotyp +streaminfo.audiotype_unknown Audiotyp: unbekannt +streaminfo.bitrate Bitrate +streaminfo.framerate Böudrate +streaminfo.framerate_unknown Böudrate: unbekannt +streaminfo.head Informatione +streaminfo.not_available nid verfüegbar +streaminfo.resolution Uflösig +streaminfo.signal Empfangssignau +streaming.buffer_overflow d'Ufnahm isch leider abbroche,\ndo d'Date nid schnäu gnue gschribe hei chöne wärde. +streaming.busy Eine oder mehreri Aufnahmprozäss si aktiv.\nSöt d'Ufnahm eigentlech beändet si,\nschafft ä Neustart vo Neutrino Abhiuf. +streaming.dir_not_writable s'Ufnahmeverzeichnis isch ni beschribbar.\ns'Ufnä isch wäg däm nid müglech. +streaming.success d'Ufnahm isch erfougrich beändet worde. +streaming.write_error d'Ufnahme isch leider abbroche,\ndo ä Fähler bim Schribe vo de Date ufträte isch. +streaming.write_error_open d'Ufnahm isch leider abbroche,\ndo diä entsprächend Datei nid zum Schribe g'öffnet cha wärde. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Movieplayer Iischtöue +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Deaktiviert +streamingmenu.on Aktiviert +streamingmenu.server_ip Streamingsörver IP +streamingmenu.server_port Streamingsörver Port +streamingmenu.streaming_audiorate Daterate Audio +streamingmenu.streaming_force_avi_rawaudio Erzwinge AC3 bei avi +streamingmenu.streaming_force_transcode_video Transcod. ou bi mpg/vcd Video +streamingmenu.streaming_resolution Uflösig +streamingmenu.streaming_server_cddrive DVD Loufwärk +streamingmenu.streaming_server_startdir Verzeichnis (VLC) +streamingmenu.streaming_transcode_audio Transcod. Audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec MPEG Video Codec +streamingmenu.streaming_type Streamingsörver +streamingmenu.streaming_videorate Daterate Video +streamingserver.noconnect Kei verbindig zum Streamingsörver.\nd'Ufnahm wird abbroche. +stringinput.caps Gross-/Chlibuächschtabe +stringinput.clear Aues lösche +timer.eventrecord.msg d'Sändig isch zur Ufnahm vorgmerkt worde +timer.eventrecord.title Ufnahm vormerke +timer.eventtimed.msg d'Sändig isch vorgmerkt +timer.eventtimed.title Sändig vormerke +timerbar.channelswitch Umschaute +timerbar.recordevent Ufnä +timerlist.alarmtime Alarmzyt +timerlist.apids Audio PIDs +timerlist.bouquetselect Bouquet wähle +timerlist.channel Kanau +timerlist.channelselect Kanau wähle +timerlist.delete Lösche +timerlist.menumodify Timer bearbeite +timerlist.menunew Nöie Timer +timerlist.message Nachricht +timerlist.moderadio Radio Kanäu +timerlist.modeselect Modus-Uswau +timerlist.modetv TV Kanäu +timerlist.modify Bearbeite +timerlist.name Timerlischte +timerlist.new Neuer Timer +timerlist.plugin Plugin +timerlist.program.unknown Programm unbekannt +timerlist.recording_dir Ufnahmeverzeichnis +timerlist.reload Aktualisiere +timerlist.repeat Widerholige +timerlist.repeat.biweekly 2-wöchentlech +timerlist.repeat.byeventdescription siehe timer +timerlist.repeat.daily täglech +timerlist.repeat.fourweekly 4-wöchentlech +timerlist.repeat.friday Fr +timerlist.repeat.monday Mo +timerlist.repeat.monthly monetlech +timerlist.repeat.once eimalig +timerlist.repeat.saturday Sa +timerlist.repeat.sunday So +timerlist.repeat.thursday Do +timerlist.repeat.tuesday Di +timerlist.repeat.unknown unbekannt +timerlist.repeat.wednesday Mi +timerlist.repeat.weekdays A Wochetäg +timerlist.repeat.weekly wöchentlech +timerlist.repeatcount Wiederholige +timerlist.repeatcount.help1 Azau vo de Timerusfüehrige +timerlist.repeatcount.help2 0 für unbegränzti Azau +timerlist.save Timer spichere +timerlist.standby SB Modus +timerlist.standby.off Ufwache us Standby +timerlist.standby.on Uf Standby go +timerlist.stoptime Schtoppzyt +timerlist.type Timertyp +timerlist.type.execplugin Plugin usführe +timerlist.type.nextprogram Nöchschts Programm +timerlist.type.record Ufnahm +timerlist.type.remind Erinnerig +timerlist.type.shutdown Shutdown +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Unbekannt +timerlist.type.zapto Umschaute +timerlist.weekdays Wochetäg +timerlist.weekdays.hint_1 Mo Di Mi Do Fr Sa So +timerlist.weekdays.hint_2 'X'=Timer '-' kei Timer +timersettings.record_safety_time_after Ufnahmeändi-Korrektur +timersettings.record_safety_time_after.hint_1 Korrekturzyt i Min. (00=us), diä uf d'änd- +timersettings.record_safety_time_after.hint_2 zyt vom jewilige Timer addiärt wird +timersettings.record_safety_time_before Ufnahmeschtart-Korrektur +timersettings.record_safety_time_before.hint_1 Korrekturzyt i Min. (00=us), diä bim Schtart +timersettings.record_safety_time_before.hint_2 vom jewilige Timer abzoge wärde +timersettings.separator Timerischtöuige +timing.chanlist Kanaulischtä +timing.epg EPG +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Ibländzyt i Sek., wiä lang s'OSD +timing.hint_2 uf äm TV aazeiget wird +timing.infobar Infobar +timing.menu Menü +timing.numericzap Umschaute mit Ziffertaschte +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode überprüäfig +ucodecheck.ucode UCode +ucodes.failure Achtung, d'µCodes si NID gfunge worde!\n\nBitte per FTP (oder DBox-BootManager)\nuploade, u d'Box nöi schtarte! +videomenu.csync Sync.-Korrektur +videomenu.head Video-Ischtöuige +videomenu.rgb_centering RGB-Zentrierig +videomenu.screensetup Böudbereich ischtöue +videomenu.vcrswitch Scart-Iigang outomatisch +videomenu.videoformat Böudschirmformat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect outomatisch +videomenu.videosignal Video Signauart +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce I einer Minute wird umgschautet diff --git a/data/locale/check.locale.files b/data/locale/check.locale.files new file mode 100755 index 000000000..ef63e8cfd --- /dev/null +++ b/data/locale/check.locale.files @@ -0,0 +1,9 @@ +#!/bin/sh +cut -d' ' -f1 deutsch.locale | sort | uniq > /tmp/log +for i in *.locale; do \ + echo $i:; \ + echo "----------------"; \ + cut -d' ' -f1 $i | diff -u - /tmp/log; \ + echo; \ +done +rm /tmp/log diff --git a/data/locale/create-locals-update.pl b/data/locale/create-locals-update.pl new file mode 100755 index 000000000..7abc7aaad --- /dev/null +++ b/data/locale/create-locals-update.pl @@ -0,0 +1,50 @@ +#! /usr/bin/perl + +# This proggie takes a (possibly outdated) locale file as its (only) +# argument, and creates a new version, with `-work` appended. The thus +# created tile contains all keys of the master file, and is intended +# to, after manual editing, replace the outdated locale file. + +# Written by Barf on 2005-12-10. + +$masterfilename = "deutsch.locale"; + +$#ARGV == 0 || die("Usage: create-locals-update.pl file.locale."); + +$no_errors = 0; +$last_was_ok = 1; +$localefilename = @ARGV[0]; +$outfilename = $localefilename . "-work"; + +open(masterfile, $masterfilename) || die("Could not open master file"); +open(localefile, $localefilename) || die("Could not open locale file"); +open(outfile, ">" . $outfilename) || die("Could not open output file"); + +while () { + $masterline = $_; + ($masterkey) = /([^ ]+)/; + ($junk, $mastertext) = /([^ ]+)[ ]+([^\n]+)/; + if ($last_was_ok) { + $localeline = ; + chop $localeline; + ($localekey) = ($localeline =~ /([^ ]+)/,$localline); + ($junk, $localetext) = ($localeline =~ /([^ ]+)[ ]+([^\n]+)/); + }; + if ($masterkey eq $localekey) { + print outfile $localeline, "\n"; + $last_was_ok = 1; + } else { + $no_errors++; + #print "|", $masterkey, "|", $mastertext, "|", $localekey, "|", $localetext, "|\n"; + print outfile $masterkey, " TRANSLATE ", $mastertext, "\n"; + $last_was_ok = 0; + } +} + +close(outfile); + +print "There were ", $no_errors, " error(s).\n"; + +if ($no_errors == 0) { + unlink($outfilename); +} diff --git a/data/locale/create.locals.h b/data/locale/create.locals.h new file mode 100755 index 000000000..6f0bbb788 --- /dev/null +++ b/data/locale/create.locals.h @@ -0,0 +1,43 @@ +#!/bin/bash +# usage: cut -d' ' -f1 deutsch.locale | LC_ALL=C sort | uniq | tr [:lower:] [:upper:] | tr \. \_ | tr \- \_ | tr -d \? | ./create.locals.h +cat > locals.h < + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +typedef enum +{ + NONEXISTANT_LOCALE, +EOF +while read id; do + if [[ \ + "$id" != "CAM_WRONG" \ + ]] ; + then + echo $'\t'"LOCALE_$id," >> locals.h; + fi +done +cat >> locals.h < locals_intern.h < + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +const char * locale_real_names[] = +{ + "INTERNAL ERROR - PLEASE REPORT", +EOF +while read id; do + if [[ \ + "$id" != "cam.wrong" \ + ]] ; + then + echo $'\t'"\"$id\"," >> locals_intern.h; + fi +done +cat >> locals_intern.h < Service) +channellist.since seit %02d:%02d +channellist.start ab %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blau +colorchooser.green grün +colorchooser.red rot +colormenu.background Hintergrundfarbe +colormenu.fade Menüs faden +colormenu.font Schriftgröße +colormenu.gtx_alpha Transparenz (GTX) +colormenu.head Farb-Einstellungen +colormenu.menucolors Menüfarben +colormenu.statusbar Statusbalken +colormenu.textcolor Textfarbe +colormenu.themeselect Theme auswählen +colormenu.timing OSD Timeouts +colormenusetup.head Menüfarben +colormenusetup.menucontent Fensterinhalt +colormenusetup.menucontent_inactive Fensterinhalt deaktiviert +colormenusetup.menucontent_selected Fensterinhalt selektiert +colormenusetup.menuhead Titelleiste +colorstatusbar.head Statusbalken-Farben +colorstatusbar.text Statusbalken +colorthememenu.classic_theme Klassik +colorthememenu.dblue_theme DarkBlue Theme +colorthememenu.dvb2k_theme DVB2000 Theme +colorthememenu.head Theme auswählen +colorthememenu.neutrino_theme Neutrino Theme +date.Apr Apr +date.Aug Aug +date.Dec Dez +date.Feb Feb +date.Fri Fr +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mär +date.May Mai +date.Mon Mo +date.Nov Nov +date.Oct Okt +date.Sat Sa +date.Sep Sep +date.Sun So +date.Thu Do +date.Tue Di +date.Wed Mi +epglist.head Vorschau - %s +epglist.noevents Keine EPG-Information verfügbar. +epgviewer.More_Screenings Weitere Termine auf diesem Kanal +epgviewer.nodetailed Keine ausführlichen Informationen verfügbar +epgviewer.notfound keine Programminformationen (EPG) gefunden +eventlistbar.channelswitch Umschalten +eventlistbar.eventsort Sortieren +eventlistbar.recordevent Aufnehmen +favorites.addchannel Der aktuelle Kanal wird dem Bouquet \n"Meine Favoriten" hinzugefügt. \nDie Speicherung benötigt einen Moment... +favorites.bouquetname Meine Favoriten +favorites.bqcreated Bouquet "Meine Favoriten" wurde angelegt...\n +favorites.chadded Der aktuelle Kanal wurde Ihren Favoriten hinzugefügt...\n +favorites.chalreadyinbq Der aktuelle Kanal ist bereits in Ihren Favoriten...\n +favorites.finalhint \nVersehentlich hinzugefügte Kanäle können mit\nder Bouquetverwaltung korrigiert werden.\n +favorites.menueadd Kanal Favoriten hinzufügen +favorites.nobouquets Favoriten sind nur mit aktivierten Bouquets möglich. +filebrowser.delete Löschen +filebrowser.denydirectoryleave Startverzeichnis absolut +filebrowser.dodelete1 Soll +filebrowser.dodelete2 gelöscht werden ? +filebrowser.filter.active Filter an +filebrowser.filter.inactive Filter aus +filebrowser.head Filebrowser +filebrowser.mark Markieren +filebrowser.nextpage Seite vor +filebrowser.prevpage Seite zurück +filebrowser.scan Durchsuche Verzeichnisse +filebrowser.select Auswählen +filebrowser.showrights Dateirechte anzeigen +filebrowser.sort.date (Datum) +filebrowser.sort.name (Dateiname) +filebrowser.sort.namedirsfirst (Dateiname2) +filebrowser.sort.size (Größe) +filebrowser.sort.type (Typ) +filesystem.is.utf8 Dateisystem +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash lese Flash +flashupdate.cantopenfile kann Datei nicht öffnen +flashupdate.cantopenmtd kann das MTD-Device nicht öffnen +flashupdate.checkupdate nach neuer Version suchen +flashupdate.currentreleasecycle Releasezyklus +flashupdate.currentversion_sep Installierte Version +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot ImageTyp +flashupdate.currentversiontime Uhrzeit +flashupdate.erasefailed Flash löschen fehlgeschlagen +flashupdate.erasing lösche Flash +flashupdate.experimentalimage Sie haben einen Snapshot ausgewählt, bitte beachten Sie,\ndass diese Version ungetestet ist und u.U. Ihre Box\nnach dem Update nicht mehr funktionsfähig ist.\n\nSoll diese Version wirklich installiert werden? +flashupdate.expertfunctions Experten-Funktionen +flashupdate.fileis0bytes die Dateigröße ist 0 Byte +flashupdate.fileselector Datei-Auswahl +flashupdate.flashreadyreboot Das Image wurde erfolgreich geflasht.\nIhre Box wird jetzt neu gestartet. +flashupdate.getinfofile lade Versioninfo +flashupdate.getinfofileerror kann Info nicht laden +flashupdate.getupdatefile lade Update +flashupdate.getupdatefileerror kann Update nicht laden +flashupdate.globalprogress Gesamtstatus: +flashupdate.head Aktualisierung +flashupdate.md5check Imageprüfung +flashupdate.md5sumerror Das Image ist fehlerhaft +flashupdate.msgbox Es wurde folgendes neues file gefunden:\nDatum: %s, %s\nBasisImage: %s\nTyp: %s\n\nWollen Sie diese Version jetzt herunterladen\nund installieren? +flashupdate.msgbox_manual Es wurde ein neues Image gefunden:\nDatum: %s, %s\nBasisImage: %s\nImageTyp: %s\n\nWollen Sie diese Version jetzt installieren? +flashupdate.mtdselector Partitions-Auswahl +flashupdate.programmingflash programmiere Flash +flashupdate.proxypassword Passwort +flashupdate.proxypassword_hint1 Geben sie das Proxy-Passwort ein +flashupdate.proxypassword_hint2 Ein leerer Eintrag bedeutet keine Authentifizierung +flashupdate.proxyserver Proxyname +flashupdate.proxyserver_hint1 Geben Sie den Proxynamen oder IP ein (Host:Port) +flashupdate.proxyserver_hint2 Ein leerer Eintrag bedeutet kein Proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Benutzername +flashupdate.proxyusername_hint1 Geben Sie den Proxy-Benutzernamen ein +flashupdate.proxyusername_hint2 Ein leerer Eintrag bedeutet keine Authentifizierung +flashupdate.readflash ganzes Flashimage auslesen +flashupdate.readflashmtd einzelne Partition auslesen +flashupdate.ready fertig +flashupdate.reallyflashmtd Wollen Sie den Flashvorgang wirklich durchführen?\n\nFalls ein Fehler auftritt oder das Image nicht\nfunktionfähig ist, wird Ihre box nicht mehr booten.\n\nImagename: %s\nZiel: %s +flashupdate.savesuccess Das Image wurde erfolgreich unter dem\nDateinamen %s gespeichert. +flashupdate.selectimage Verfügbare Images/Files +flashupdate.squashfs.noversion Bei SquashFS Updates werden Versionsüberprüfungen derzeit nur bei Web-Updates unterstützt.\nWollen Sie das ausgewählt Image wirklich installieren? +flashupdate.titlereadflash Flash auslesen +flashupdate.titlewriteflash Flash schreiben +flashupdate.updatemode Updatemodus +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuell (ftp) +flashupdate.url_file Konfigfile +flashupdate.versioncheck Versionsüberprüfung +flashupdate.writeflash ganzes Flashimage einspielen +flashupdate.writeflashmtd einzelne Partition einspielen +flashupdate.wrongbase Abweichende release Info\nFortfahren? +fontmenu.channellist Kanalliste +fontmenu.epg EPG +fontmenu.eventlist Eventlist +fontmenu.gamelist Gameliste +fontmenu.head Schriftgrössen Einstellungen +fontmenu.infobar Infobar +fontsize.channel_num_zap Direktauswahl +fontsize.channellist Kanalliste +fontsize.channellist_descr Beschreibung +fontsize.channellist_number Nummer +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titel +fontsize.eventlist_datetime Eventlist Datum / Zeit +fontsize.eventlist_itemlarge Eventlist groß +fontsize.eventlist_itemsmall Eventlist klein +fontsize.eventlist_title Eventlist Titel +fontsize.filebrowser_item Dateibrowser-Eintrag +fontsize.gamelist_itemlarge groß +fontsize.gamelist_itemsmall klein +fontsize.hint Font wird initialisiert,\nbitte warten... +fontsize.infobar_channame Infobar Kanal +fontsize.infobar_info Infobar Info +fontsize.infobar_number Infobar Nummer +fontsize.infobar_small Infobar klein +fontsize.menu Menutext +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titel +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload Informationen noch nicht geladen... +infoviewer.epgwait Warte auf EPG-Informationen... +infoviewer.eventlist EPG/Vorschau +infoviewer.languages Tonwahl +infoviewer.motor_moving Antennenpositionierung +infoviewer.next später +infoviewer.nocurrent Keine Info zu laufendem Programm +infoviewer.noepg Keine EPG-Information verfügbar +infoviewer.notavailable Kanal (zur Zeit) nicht verfügbar. +infoviewer.now jetzt +infoviewer.selecttime Startzeiten +infoviewer.streaminfo Features +infoviewer.subchan_disp_pos Unterkanalanzeige +infoviewer.subservice Bildregie +infoviewer.waittime Warte auf Uhrzeit... +ipsetup.hint_1 Benutzen Sie 0..9 oder hoch/runter +ipsetup.hint_2 OK speichert, LAME! bricht ab +keybindingmenu.RC Fernbedienung +keybindingmenu.addrecord Aufnahme-Timer hinzufügen +keybindingmenu.addremind Umschalt-Timer hinzufügen +keybindingmenu.allchannels_on_ok Kanalliste +keybindingmenu.bouquetchannels_on_ok Bouquet-Kanäle +keybindingmenu.bouquetdown Bouquet zurück +keybindingmenu.bouquethandling OK-Taste für +keybindingmenu.bouquetlist_on_ok Bouquetliste +keybindingmenu.bouquetup Bouquet weiter +keybindingmenu.cancel Kanalliste schließen +keybindingmenu.channeldown Kanal runter +keybindingmenu.channellist Kanalliste +keybindingmenu.channelup Kanal hoch +keybindingmenu.head Tasten-Einstellungen +keybindingmenu.lastchannel Letzter Kanal +keybindingmenu.modechange Modus wechsel +keybindingmenu.pagedown Seite runterblättern +keybindingmenu.pageup Seite hochblättern +keybindingmenu.quickzap Schnellumschaltung +keybindingmenu.repeatblock Wiederholungsverzögerung +keybindingmenu.repeatblockgeneric Anfangsverzögerung +keybindingmenu.sort Sortierreihenfolge ändern +keybindingmenu.subchanneldown Unterkanal zurück +keybindingmenu.subchannelup Unterkanal weiter +keybindingmenu.tvradiomode Fernseh-/Radio-Modus +keybindingmenu.zaphistory Zapping-History Bouquet +keychooser.head Neue Taste einstellen +keychooser.text1 Bitte Taste drücken +keychooser.text2 Zum Abbrechen warten.. +keychoosermenu.currentkey Derzeitige Taste +keychoosermenu.setnew Neue Taste einstellen +keychoosermenu.setnone Keine Taste +languagesetup.head Spracheinstellungen +languagesetup.select Sprache +lcdcontroler.brightness normale Helligkeit +lcdcontroler.brightnessstandby Standby Helligkeit +lcdcontroler.contrast Kontrast +lcdcontroler.head VFD Einstellungen +lcdmenu.autodimm Automatisch dimmen +lcdmenu.head VFD Einstellungen +lcdmenu.inverse Invertieren +lcdmenu.lcdcontroler Kontrast / Helligkeit +lcdmenu.power Power +lcdmenu.statusline Statuszeile +lcdmenu.statusline.both Lautstärke / Fortschritt +lcdmenu.statusline.playtime Sendungsfortschritt +lcdmenu.statusline.volume Lautstärke +mainmenu.audioplayer Audioplayer +mainmenu.games Spiele +mainmenu.head Hauptmenü +mainmenu.movieplayer Movieplayer +mainmenu.pausesectionsd EPG auslesen +mainmenu.pictureviewer Bildbetrachter +mainmenu.radiomode Radio-Modus +mainmenu.reboot Neustart +mainmenu.recording Aufnehmen +mainmenu.recording_start Start +mainmenu.recording_stop Stopp +mainmenu.scartmode Scart-Eingang +mainmenu.scripts Skripte +mainmenu.service Service +mainmenu.settings Einstellungen +mainmenu.shutdown Ausschalten +mainmenu.sleeptimer SleepTimer +mainmenu.tvmode TV-Modus +mainsettings.audio Audio +mainsettings.osd OSD Einstellungen +mainsettings.colors Farben / Themes / Schrift +mainsettings.head Einstellungen +mainsettings.keybinding Tasten Einstellungen +mainsettings.language Sprache +mainsettings.lcd VFD-Display +mainsettings.misc Diverse Einstellungen +mainsettings.network Netzwerk +mainsettings.recording Aufnahme +mainsettings.savesettingsnow Einstellungen jetzt speichern +mainsettings.savesettingsnow_hint Einstellungen werden jetzt gespeichert,\nbitte warten... +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back Zurück +messagebox.back Zurück +messagebox.cancel Abbruch +messagebox.discard Wollen Sie die Änderungen verwerfen? +messagebox.error Fehler +messagebox.info Information +messagebox.no Nein +messagebox.yes Ja +miscsettings.bootinfo Infos beim Booten anzeigen +miscsettings.bootmenu Bootmenü anzeigen +miscsettings.driver_boot Treiber- und Bootoptionen +miscsettings.fb_destination EXPERT! Boot-Konsole +miscsettings.general Allgemein +miscsettings.head Diverse Einstellungen +miscsettings.hwsections HW-Sections verwenden +miscsettings.infobar_sat_display Infobar Satellitenanzeige +miscsettings.noaviawatchdog AVIA-Watchdog aktivieren +miscsettings.noenxwatchdog eNX-Watchdog aktivieren +miscsettings.pmtupdate PMT Update verwenden +miscsettings.shutdown_real Standbymodus +miscsettings.shutdown_real_rcdelay Verzögerter Shutdown +miscsettings.shutdown_count Komplett ausschalten nach +miscsettings.sptsmode SPTS-Mode Treiber laden +miscsettings.startbhdriver BH-Mode Treiber laden +motorcontrol.head Motor-Setup +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmark Name +movieplayer.bookmarkname_hint1 Geben Sie den Namen für das neue Bookmark ein +movieplayer.bookmarkname_hint2 +movieplayer.buffering Puffern... +movieplayer.defdir Start-Verz. +movieplayer.defplugin Start-Plugin +movieplayer.dvdplayback DVD +movieplayer.fileplayback Abspielen (Multiformat) +movieplayer.goto Springe zu ... +movieplayer.goto.h1 = -> absoluter Sprung +movieplayer.goto.h2 +,- -> relativer Sprung +movieplayer.head Movieplayer +movieplayer.nostreamingserver Es konnte keine Verbindung\nzum Streamingserver hergestellt werden. +movieplayer.pesplayback PES Abspielen (Experimentell) +movieplayer.pleasewait Bitte warten Sie.\nDie Verbindung zum Streamingserver wird hergestellt. +movieplayer.toomanybookmarks Sie haben bereits zu viele Bookmarks angelegt.\nEs muß erst ein anderes gelöscht werden. +movieplayer.tshelp1 Stop +movieplayer.tshelp10 ca. 10 Minuten zurück +movieplayer.tshelp11 ca. 10 Minuten vor +movieplayer.tshelp12 Hilfe: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Auswahl der Tonspur +movieplayer.tshelp3 Pause/Weiter +movieplayer.tshelp4 Bookmark setzen +movieplayer.tshelp5 Zeit einblenden +movieplayer.tshelp6 ca. 1 Minute zurück +movieplayer.tshelp7 ca. 1 Minute vor +movieplayer.tshelp8 ca. 5 Minuten zurück +movieplayer.tshelp9 ca. 5 Minuten vor +movieplayer.tsplayback TS Abspielen +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 ca. 10 Minuten zurück +movieplayer.vlchelp11 ca. 10 Minuten vor +movieplayer.vlchelp12 Hilfe: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Weiter +movieplayer.vlchelp4 Bookmark setzen +movieplayer.vlchelp5 Zeit einblenden +movieplayer.vlchelp6 ca. 1 Minute zurück +movieplayer.vlchelp7 ca. 1 Minute vor +movieplayer.vlchelp8 ca. 5 Minuten zurück +movieplayer.vlchelp9 ca. 5 Minuten vor +movieplayer.wrongvlcversion Funktion ist mit der benutzten Version des VLC nicht möglich +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway Standard Gateway +networkmenu.head Netzwerk Einstellungen +networkmenu.ipaddress IP-Adresse +networkmenu.mount NFS/CIFS/FTPFS +networkmenu.nameserver Nameserver +networkmenu.netmask Netzmaske +networkmenu.ntpenable Syncronisieren via +networkmenu.ntprefresh NTP/DVB-Refresh +networkmenu.ntprefresh_hint1 NTP/DVB-Time-Sync in minuten +networkmenu.ntprefresh_hint2 braucht reboot oder epg-reset +networkmenu.ntpserver NTP-Server +networkmenu.ntpserver_hint1 NTP-Server Beispiel: ntp1.ptb.de +networkmenu.ntpserver_hint2 braucht reboot oder epg-reset +networkmenu.ntptitle Time-Syncronisation +networkmenu.setupnow Netzwerkeinstellungen jetzt zuweisen +networkmenu.setuponstartup Beim Starten Netzwerk setzen +networkmenu.show aktive Netzwerkeinstellungen zeigen +networkmenu.test Netzwerk testen +nfs.alreadymounted Verzeichnis bereits gemountet. +nfs.automount Beim Start mounten +nfs.dir Verzeichnis/Freigabe +nfs.ip Server IP +nfs.localdir lokales Verzeichnis +nfs.mount Netzwerk Verzeichnis mounten +nfs.mount_options Mount-Optionen +nfs.mountok Mount erfolgreich +nfs.mounterror Mount-Fehler +nfs.mounterror_notsup Dateisystem-Typ wird nicht unterstützt. +nfs.mountnow Jetzt mounten +nfs.mounttimeout Mount-Fehler: Timeout +nfs.password Passwort +nfs.remount Verzeichnisse erneut mounten +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount Netzwerk Verzeichnis unmounten +nfs.umounterror Umount-Fehler +nfs.username Benutzername +nfsmenu.head NFS/CIFS/FTPFS Einstellungen +nvod.percentage (%d%% vorbei) +nvod.starting (Beginn in %d min) +nvodselector.directormode Bildregie-Modus +nvodselector.head Anfangszeit auswählen +nvodselector.subservice Perspektiven +options.default Voreinstellungen benutzen +options.fb framebuffer +options.null null +options.off aus +options.on ein +options.serial seriell +parentallock.changepin PIN-Code festlegen +parentallock.changepin_hint1 Geben Sie hier Ihren neuen PIN-Code ein! +parentallock.changetolocked Bei vorgeperrten Bouquets +parentallock.head Jugendschutz PIN +parentallock.lockage Sendungen +parentallock.lockage12 ab 12 Jahren sperren +parentallock.lockage16 ab 16 Jahren sperren +parentallock.lockage18 ab 18 Jahren sperren +parentallock.lockedchannel Vorgesperrter Sender... +parentallock.lockedprogram Gesperrtes Programm (FSK ab %d) +parentallock.never Nie +parentallock.onsignal Bei gesendeter Vorsperre +parentallock.parentallock Jugendschutz +parentallock.prompt PIN-Eingabe +pictureviewer.defdir Start-Verz. +pictureviewer.head Bildbetrachter +pictureviewer.help1 Menu-Modus +pictureviewer.help10 Anzeige-Modus +pictureviewer.help11 Bild neu einlesen +pictureviewer.help12 vorheriges Bild +pictureviewer.help13 nachstes Bild +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 Scroll hoch +pictureviewer.help17 Scroll links +pictureviewer.help18 Scroll rechts +pictureviewer.help19 Scroll herunter +pictureviewer.help2 Bild anzeigen +pictureviewer.help20 Sortierung andern +pictureviewer.help21 Bild unskaliert einlesen +pictureviewer.help22 Modus verlassen +pictureviewer.help3 Sortierung andern +pictureviewer.help4 Bild unskaliert einlesen +pictureviewer.help5 Diashow-Modus +pictureviewer.help6 vorheriges Bild +pictureviewer.help7 nachstes Bild +pictureviewer.help8 Sortierung andern +pictureviewer.help9 Modus verlassen +pictureviewer.resize.color_average aufwendig +pictureviewer.resize.none keine +pictureviewer.resize.simple einfach +pictureviewer.scaling Skalierung +pictureviewer.show Anzeigen +pictureviewer.slide_time Diaschau-Anzeigedauer +pictureviewer.slideshow Diaschau +pictureviewer.sortorder Sort.reihenf. ändern +pictureviewer.sortorder.date (Datum) +pictureviewer.sortorder.filename (Dateiname) +ping.ok ist erreichbar (ping) +ping.protocol ist nicht erreichbar (Host or protocol error) +ping.socket ist nicht erreichbar (Socket error) +ping.unreachable ist nicht erreichbar (unreachable) +pinprotection.head PIN-Abfrage +pinprotection.wrongcode Geben Sie den Code nocheinmal ein! +plugins.result Pluginausgabe +rclock.lockmsg Die Fernbedienung der Coolstream wird gesperrt.\n Um die Sperre aufzuheben, bitte\n und

    auf der Fernbedienung\n druecken. +rclock.menueadd FB sperren +rclock.title Fernbedienung sperren +rclock.unlockmsg Fernbedienung reaktiviert... +recordingmenu.choose_direct_rec_dir Bei Sofortaufn. Verzeichnisauswahl +recordingmenu.defdir Aufnahmeverzeichnis +recordingmenu.epg_for_filename lange Dateinamen (mit EPG-Daten) +recordingmenu.file Direkt (Datei) +recordingmenu.filesettings Direktaufnahme Einstellungen +recordingmenu.head Aufnahme Einstellungen +recordingmenu.help Aufnahmegeräte:\n-----------------------\nServer:\nauf PC mit Hilfe eines Streaming-Programmes\n\n(analoger) Videorekorder:\nüber VCR-Ausgang\n\nDirekt (Datei):\nauf ein per NFS gemountetes Verzeichnis\noder eine interne Festplatte\nTS: SPTS-Mode Treiber laden(dbox2)\nPES: SPTS-Mode Treiber nicht laden(dbox2)\n\n\nMaximale Dateigröße:\n----------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: fast unendlich (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart Unterdrücke Scart-Umschaltung +recordingmenu.off aus +recordingmenu.recording_type Aufnahme Gerät +recordingmenu.ringbuffers Anz. Ringpuffer +recordingmenu.server Server +recordingmenu.server_ip Aufnahmeserver IP +recordingmenu.server_mac Mac Adresse +recordingmenu.server_port Aufnahmeserver Port +recordingmenu.server_wakeup Aufnahmeserver WOL +recordingmenu.setupnow Einstellungen jetzt übernehmen +recordingmenu.splitsize Max. Dateigröße (MB) +recordingmenu.stopplayback Playback anhalten +recordingmenu.stopsectionsd Sectionsd anhalten +recordingmenu.stream_vtxt_pid teletext aufzeichnen +recordingmenu.use_fdatasync Synchrones Schreiben (fdatasync) +recordingmenu.use_o_sync Synchrones Schreiben (O_SYNC) +recordingmenu.vcr Videorekorder +recordtimer.announce Die Aufnahme beginnt in wenigen Minuten. +repeatblocker.hint_1 Mindestzeit (in ms) zwischen 2 Tastendrücken +repeatblocker.hint_2 0 schaltet den Blocker aus (Rot ist " ") +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-Wiederholungen +satsetup.extended DiSEqC-Einstellungen +satsetup.extended_motor Motor-Einstellungen +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manuelle Motor-Einstellung +satsetup.nodiseqc Kein DiSEqC +satsetup.satellite Satellit +satsetup.savesettingsnow Einstellungen jetzt speichern +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Kabel: +scants.actsatellite Satellit: +scants.bouquet Bouquets +scants.bouquet_create neu erstellen +scants.bouquet_erase löschen +scants.bouquet_leave nicht ändern +scants.bouquet_satellite Satelliten-Bouquet +scants.bouquet_update erneuern +scants.channel Kanal: +scants.failed Kanalsuche fehlgeschlagen! +scants.finished Kanalsuche erfolgreich beendet! +scants.freqdata Frequenz: +scants.head Kanalsuche +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Gesamt +scants.numberoftvservices TV +scants.provider Anbieter: +scants.startnow Kanalsuche starten +scants.test Empfang Test +scants.transponders Transponder: +scants.select_tp Wählen Sie Transponder +screensetup.lowerright grün = Bildrand unten, rechts +screensetup.upperleft rot = Bildrand oben, links +servicemenu.head Service +servicemenu.reload Kanallisten neu laden +servicemenu.reload_hint Kanallisten werden neu geladen,\nbitte warten... +servicemenu.scants Kanalsuche +servicemenu.ucodecheck UCodes überprüfen +servicemenu.update Software-Aktualisierung +settings.help Help +settings.missingoptionsconffile Die Neutrino-Einstellungen wurden erweitert.\nDie neuen Werte werden auf Standard gesetzt. +settings.noconffile Die Neutrino-Einstellungen wurden nicht\ngefunden. Es werden Standardwerte benutzt. +settings.pos_bottom_left unten links +settings.pos_bottom_right unten rechts +settings.pos_top_left oben links +settings.pos_top_right oben rechts +shutdowntimer.announce Die Box wird in 1 min runtergefahren.\nShutdown abbrechen? +sleeptimerbox.announce Sleeptimer in 1 min +sleeptimerbox.hint1 Ausschaltzeit in Min. (000=aus) +sleeptimerbox.hint2 Die box schaltet sich nach dieser Zeit aus. +sleeptimerbox.title Sleeptimer +streamfeatures.head Features +streaminfo.aratio Verhältnis +streaminfo.aratio_unknown Verhältnis: unbekannt +streaminfo.audiotype Audiotyp +streaminfo.audiotype_unknown Audiotyp: unbekannt +streaminfo.bitrate Bitrate +streaminfo.framerate Bildrate +streaminfo.framerate_unknown Bildrate: unbekannt +streaminfo.head Tech. Information +streaminfo.not_available nicht verfügbar +streaminfo.resolution Auflösung +streaminfo.signal Empfangssignal +streaming.buffer_overflow Die Aufnahme wurde leider abgebrochen,\nda die Daten nicht schnell genug geschrieben werden konnten. +streaming.busy Ein oder mehrere Aufnahmeprozesse sind aktiv.\nSollte die Aufnahme eigentlich beendet sein,\nschafft ein Neustart von Neutrino Abhilfe. +streaming.dir_not_writable Das Aufnahmeverzeichnis ist nicht beschreibbar.\nAufnahmen sind daher nicht möglich. +streaming.success Die Aufnahme wurde erfolgreich beendet. +streaming.write_error Die Aufnahme wurde leider abgebrochen,\nda ein Fehler beim Schreiben der Daten auftrat. +streaming.write_error_open Die Aufnahme wurde leider abgebrochen,\nda die entsprechende Datei nicht zum Schreiben geöffnet werden konnte. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Movieplayer Einstellungen +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Deaktiviert +streamingmenu.on Aktiviert +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Port +streamingmenu.streaming_audiorate Datenrate Audio +streamingmenu.streaming_force_avi_rawaudio Erzwinge AC3 bei avi +streamingmenu.streaming_force_transcode_video Transcod. auch mpg/vcd Video +streamingmenu.streaming_resolution Auflösung +streamingmenu.streaming_server_cddrive DVD Laufwerk +streamingmenu.streaming_server_startdir Verzeichnis (VLC) +streamingmenu.streaming_transcode_audio Transcod. Audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec MPEG Video Codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datenrate Video +streamingserver.noconnect Keine Verbindung zum Streamingserver.\nAufnahme wird abgebrochen. +stringinput.caps Groß-/Kleinbuchstaben +stringinput.clear Alles löschen +timer.eventrecord.msg Die Sendung wurde zur Aufnahme vorgemerkt. +timer.eventrecord.title Aufnahme vormerken +timer.eventtimed.msg Die Sendung wurde vorgemerkt. +timer.eventtimed.title Sendung vormerken +timerbar.channelswitch Umschalten +timerbar.recordevent Aufnehmen +timerlist.alarmtime Alarmzeit +timerlist.apids Audio PIDs +timerlist.bouquetselect Bouquet wählen +timerlist.channel Kanal +timerlist.channelselect Kanal wählen +timerlist.delete Löschen +timerlist.menumodify Timer bearbeiten +timerlist.menunew Neuer Timer +timerlist.message Nachricht +timerlist.moderadio Radio Kanäle +timerlist.modeselect Modus-Auswahl +timerlist.modetv TV Kanäle +timerlist.modify Bearbeiten +timerlist.name Timerliste +timerlist.new Neuer Timer +timerlist.plugin Plugin +timerlist.program.unknown Programm unbekannt +timerlist.recording_dir Aufnahmeverzeichnis +timerlist.reload Aktualisieren +timerlist.repeat Wiederholung +timerlist.repeat.biweekly 2-wöchentlich +timerlist.repeat.byeventdescription siehe timer +timerlist.repeat.daily täglich +timerlist.repeat.fourweekly 4-wöchentlich +timerlist.repeat.friday Fr +timerlist.repeat.monday Mo +timerlist.repeat.monthly monatlich +timerlist.repeat.once einmalig +timerlist.repeat.saturday Sa +timerlist.repeat.sunday So +timerlist.repeat.thursday Do +timerlist.repeat.tuesday Di +timerlist.repeat.unknown unbekannt +timerlist.repeat.wednesday Mi +timerlist.repeat.weekdays An Wochentagen +timerlist.repeat.weekly wöchentlich +timerlist.repeatcount Wiederholungen +timerlist.repeatcount.help1 Anzahl der Timerausführungen +timerlist.repeatcount.help2 0 für unbegrenzte Anzahl +timerlist.save Timer speichern +timerlist.standby SB Modus +timerlist.standby.off Aufwachen aus Standby +timerlist.standby.on Auf Standby gehen +timerlist.stoptime Stoppzeit +timerlist.type Timertyp +timerlist.type.execplugin Plugin ausführen +timerlist.type.nextprogram Nächstes Programm +timerlist.type.record Aufnahme +timerlist.type.remind Erinnerung +timerlist.type.shutdown Shutdown +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Unbekannt +timerlist.type.zapto Umschalten +timerlist.weekdays Wochentage +timerlist.weekdays.hint_1 Mo Di Mi Do Fr Sa So +timerlist.weekdays.hint_2 'X'=Timer '-' kein Timer +timersettings.record_safety_time_after Aufnahmeende-Korrektur +timersettings.record_safety_time_after.hint_1 Korrekturzeit in Min. (00=aus), die auf die End- +timersettings.record_safety_time_after.hint_2 zeit des jeweiligen Timers addiert wird +timersettings.record_safety_time_before Aufnahmestart-Korrektur +timersettings.record_safety_time_before.hint_1 Korrekturzeit in Min. (00=aus), die beim Start +timersettings.record_safety_time_before.hint_2 des jeweiligen Timers abgezogen werden +timersettings.separator Timereinstellungen +timing.chanlist Kanalliste +timing.epg Epg +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Einblendzeit in Sek., die das OSD +timing.hint_2 auf dem TV angezeigt wird +timing.infobar Infobar +timing.menu Menu +timing.numericzap Umschalten mit Zifferntasten +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode Ãœberprüfung +ucodecheck.ucode UCode +ucodes.failure Achtung, die µCodes wurden NICHT gefunden!\n\nBitte per FTP (oder DBox-BootManager)\nuploaden, und die box neu starten! +videomenu.csync Sync.-Korrektur +videomenu.head Video-Einstellungen +videomenu.rgb_centering RGB-Zentrierung +videomenu.screensetup Bildbereich einstellen +videomenu.vcrswitch Scart-Eingang automatisch +videomenu.videoformat Bildschirmformat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 +videomenu.43mode 4:3 Content mode +videomenu.panscan Pan&Scan +videomenu.letterbox Letterbox +videomenu.fullscreen Full screen +videomenu.videoformat_autodetect automatisch +videomenu.videosignal Video Signalart +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YPbPr auf component +videomenu.videosignal_yuv_v YPbPr auf SCART +zaptotimer.announce In einer Minute wird umgeschaltet. +extra.auto Auto +extra.valid Valid +extra.all Alle +extra.sep_extra Extras +extra.save_settings Einstellungen speichern +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Zeige PAT +extra.dvbsnoop_cat Zeige CAT +extra.dvbsnoop_tsdt Zeige TSDT +extra.dvbsnoop_nit Zeige NIT +extra.dvbsnoop_sdt Zeige SDT +extra.dvbsnoop_eit Zeige EIT +extra.dvbsnoop_rst Zeige RST +extra.dvbsnoop_tdt Zeige TDT +extra.dvbsnoop_sit Zeige SIT +extra.dvbsnoop_pid Zeige manual PID +extra.dvbsnoop_scan Scan PIDs +extra.dvbsnoop_band Zeige pid Bandbreite +extra.logview Lass Log sehen +extra.english Englisch +extra.dboxinfo Box Info +extra.scan_mode Scan Modus +extra.start_tostandby Starten zum Standby +extra.rotor_swap Rotor dreht Ost/West +extra.use_log Loge nach /tmp/log +extra.spts_mode SPTS Modus +extra.hw_sect Hardware Sectionen +extra.logo Logo Nummer +extra.scan_full Total +extra.scan_fast Schnell +hdd_slow Langsam +hdd_middle Mitte +hdd_fast Schnell +hdd_ext3 Ext3fs +hdd_reiser Reiserfs +hdd_1min 1 min. +hdd_5min 5 min. +hdd_10min 10 min. +hdd_20min 20 min. +hdd_30min 30 min. +hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +hdd_sleep Schlaf Zeit +hdd_noise Laerm +hdd_activate Aktiviere Einstellungen +hdd_fs Filesystem +hdd_format Formatiere HDD +hdd_check Check Dateiesysteem +hdd_settings HDD Einstellungen +extra.clear_log Logdatei löschen +extra.zap_cycle Zap Zyklen +extra.sms_channel Sms-Modus Kanal +extra.manual_scan Manual Ansehen +extra.tp_freq Frequenz +extra.tp_rate Symbolrate +extra.tp_pol Polarization +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Zeit-Zone +extra.zapit_menu Zapit Optionen +extra.zapit_make_bouquet Mache restliche Kanalliste +extra.zapit_save_last_chan Speicher letzten Kanal +extra.zapit_motor_speed Rotor Geschwindigkeit (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet dem Bouquet hinzufuegen +extra.chadded Der aktuelle Kanal wird dem selektierter Bouquet hinzugefügt....\n +extra.chalreadyinbq Der aktueller Kanal ist bereits in selektiertem Bouquet....\n +extra.menu_left_exit "links" = Menu zurück +extra.zapit_write_names Schreibe Kanalname +extra.update_dir Update-Verzeichnis +extra.zapit_fast_zap Fast Zap +extra.cache Cache +extra.debug Debug +extra.zapit_sort_names Sort Kanal nach Name +extra.zapit_delete Lösche Kanal +extra.zapit_backup Sichere Kanelliste nach /tmp +extra.zapit_restore Stelle Kanalliste wieder von /tmp wieder her +pictureviewer.decode_server_ip Decode Server IP +pictureviewer.decode_server_port Decode Server Port +scants.abort_body Scanvorgang wirklich abbrechen? +scants.abort_header Breche Scan ab +recordingmenu.record_in_spts_mode in SPTS-Modus aufnehmen +servicemenu.getplugins Plugins neu laden +servicemenu.getplugins_hint Plugins werden neu geladen,\nbitte warten... +zapit.scantype Service-Auswahl +zapit.scantype.all Alle Services +zapit.scantype.radio Nur Radio +zapit.scantype.tv Nur TV +zapit.scantype.tvradio Nur TV & Radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Helligkeit nach Dimm-Timeout +lcdmenu.dim_time Dimm-Timeout +timerlist.overlapping_timer Timer überschneiden sich. Neuen Timer trotzdem anlegen? +extra.audio_run_player Audio Taste startet Player +mpkey.rewind Rücklauf +mpkey.forward Vorlauf +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio-Track +mpkey.time Zeitanzeige +mpkey.bookmark Speichere Bookmark +mpkey.plugin Starte Plugin +extra.key_click Tasten-Klick +extra.loadcolors Lade Farben von +extra.savecolors Speicher Farben als +extra.loadkeys Lade Tastenbelegung von +extra.savekeys Speichere Tastenbelegung als +extra.loadconfig Lade Einstellungen von +extra.saveconfig Speichere Einstellungen als +videomenu.videomode Videosystem +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One-touch-Plugin +extra.timeshift_pause Timeshift Pause +rfmod.rfmod RF Modulator +rfmod.carrier Unterträger-Frequenz +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Sound aktivieren +rfmod.channel Kanal +rfmod.finetune Feintuning +rfmod.standby Modulator aktivieren +rfmod.test Testpattern +extra.zapit_scanpids Scan/Use Pids +extra.zapit_hvoltage High (13.5/18.5) Voltage +extra.key_unlock Entsperrtaste +servicemenu.imageinfo Image-Info +imageinfo.creator Ersteller: +imageinfo.date Datum: +imageinfo.dokumentation Dokus: +imageinfo.forum Forum: +imageinfo.head Image Info: +imageinfo.homepage Homepage: +imageinfo.image Image: +imageinfo.license Lizenz: +imageinfo.version Version: +extra.cache_txt Teletext zwischenspeichern +extra.use_gotoxx Benutze gotoXX +extra.latitude Breitengrad +extra.longitude Längengrad +extra.ladirection Breitengrad-Ausrichtung +extra.lodirection Längengrad-Ausrichtung +extra.south Süden +extra.north Norden +extra.east Osten +extra.west Westen +epgextended.actors Darsteller +epgextended.director Regie +epgextended.guests Gäste +epgextended.original_title Original Titel +epgextended.presenter Moderator +epgextended.year_of_production Produktionsjahr +recordingmenu.save_in_channeldir Speichere im Kanalverzeichnis +servicemenu.restart Neutrino neu starten +servicemenu.restart_failed Neustart von Neutrino schlug fehl +servicemenu.restart_hint Neutrino wird neu gestartet... +servicemenu.restart_refused_recording Aufnahme läuft. Restart nicht möglich. +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Markierungen +moviebrowser.book_lastmoviestop letzter Wiedergabestop: +moviebrowser.book_movieend Filmende: +moviebrowser.book_moviestart Filstart: +moviebrowser.book_name Name: +moviebrowser.book_new Neue Markierung +moviebrowser.book_position Position: +moviebrowser.book_type Sprung (<0 zurück , >0 vor): +moviebrowser.book_type_backward Wiederholung +moviebrowser.book_type_forward Ãœberspringen +moviebrowser.edit_book Bookmark Ändern +moviebrowser.edit_book_name_info1 Neuer Markierungsname +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Neue Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Neue Sprunglänge (s) +moviebrowser.edit_book_type_info2 <0 zurück , >0 vor, 0: kein +moviebrowser.edit_serie Name der Serie +moviebrowser.error_no_movies Keine Filme gefunden +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Film starten +moviebrowser.foot_sort Sortieren: +moviebrowser.head TS Filmarchiv +moviebrowser.head_filter Filme nach Kategorie filtern: +moviebrowser.head_playlist Zuletzt gesehen: +moviebrowser.head_recordlist Zuletzt aufgenommen: +moviebrowser.hint_jumpbackward Rücksprung in 5 s\n '0' zum Weitersehen +moviebrowser.hint_jumpforward Werbung überspringen in 5 s\n '0' zum Ansehen +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum Weitersehen +moviebrowser.hint_newbook_backward Neue Wiederholung\n 'blue' für Endposition +moviebrowser.hint_newbook_forward Neuer Werbesprung\n 'blue' für Endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Kanel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Spieldauer (Min) +moviebrowser.info_parental_lockage Altersfreigabe +moviebrowser.info_parental_lockage_0year immer +moviebrowser.info_parental_lockage_12year 12 Jahre +moviebrowser.info_parental_lockage_16year 16 Jahre +moviebrowser.info_parental_lockage_18year 18 Jahre +moviebrowser.info_parental_lockage_6year 6 Jahre +moviebrowser.info_parental_lockage_always nie +moviebrowser.info_path Verzeichnis +moviebrowser.info_prevplaydate Letzte Wiedergabe +moviebrowser.info_prodcountry Land +moviebrowser.info_prodyear Jahr +moviebrowser.info_quality Qualität +moviebrowser.info_recorddate Aufnahmedatum +moviebrowser.info_serie Serie +moviebrowser.info_size Dateigrösse (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Bild +moviebrowser.menu_directories_head Verzeichnisse +moviebrowser.menu_help_head Hilfe +moviebrowser.menu_main_bookmarks Markierungen +moviebrowser.menu_main_head Einstellungen +moviebrowser.menu_main_movieinfo Film-Info +moviebrowser.menu_main_saveandback Speichern und verlassen +moviebrowser.menu_nfs_head NFS Einstellung +moviebrowser.menu_parental_lock_activated aktiviert +moviebrowser.menu_parental_lock_activated_no nein +moviebrowser.menu_parental_lock_activated_yes ja +moviebrowser.menu_parental_lock_activated_no_temp nein (temporär) +moviebrowser.menu_parental_lock_head Jugendschutz +moviebrowser.menu_parental_lock_rate_head Filme sperren ab +moviebrowser.scan_for_movies Suche Filme ... +moviebrowser.serie_existingname Vorhandene Serie +moviebrowser.serie_head Serie +moviebrowser.serie_name Name ändern +moviebrowser.short_audio Audio +moviebrowser.short_book Marker +moviebrowser.short_channel Kanal +moviebrowser.short_country Land +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage FSK +moviebrowser.short_path Pfad +moviebrowser.short_prevplaydate Zuletzt +moviebrowser.short_prodyear Jahr +moviebrowser.short_quality * (Qualität) +moviebrowser.short_recorddate Datum +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Film starten von Position: +moviebrowser.start_record_start Aufnahmestart +options.on.without_messages ohne Msg +extra.zapit_scan_sdt Scan SDT für Updates +extra.zapit_sdt_changed Kanalliste wird neu geladen. +miscsettings.channellist Kanalliste +miscsettings.channellist_epgtext_align Ausrichtung des Programmtextes +miscsettings.epg_head EPG Einstellungen +miscsettings.epg_cache EPG-Cache (Tage) +miscsettings.epg_cache_hint1 EPG-Daten im Voraus cachen? +miscsettings.epg_cache_hint2 Angabe in Tagen +miscsettings.epg_dir EPG Speicherverzeichnis +miscsettings.epg_max_events Max. Events +miscsettings.epg_max_events_hint1 Wieviele Events sollen max. gespeichert werden? +miscsettings.epg_max_events_hint2 Standard 6000, 0 (ohne Limit) +miscsettings.epg_old_events EPG verwerfen nach (Std.) +miscsettings.epg_old_events_hint1 Wie lange abgelaufene EPG-Daten aufheben? +miscsettings.epg_old_events_hint2 Angabe in Stunden +miscsettings.epg_save EPG zwischenspeichern? +miscsettings.virtual_zap_mode Virtual Zap +shutdown.recoding_query Aufnahme läuft. Trotzdem beenden? +recordingmenu.apids Voreinstellung für Tonspuren +recordingmenu.apids_ac3 AC3 Tonspuren aufnehmen +recordingmenu.apids_alt Alternative Tonspuren aufn. +recordingmenu.apids_std Standard Tonspur aufnehmen +recordingmenu.stream_pmt_pid PMT aufnehmen +recordingmenu.zap_on_announce Umschalten bei Ankündigung +timerlist.apids_dflt Voreingestellte Tonspuren aufn. +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR-Ausgang Signalart +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Anzeigeoptionen +moviebrowser.book_clear_all Lösche alle +moviebrowser.menu_save Veränderungen speichern +moviebrowser.menu_save_all Starte Ãœbernahme in allen sichtbaren Filminfos +moviebrowser.info_head_update Inhalte in allen sichtbaren Filminfos speichern +moviebrowser.update_if_dest_empty_only Ãœbernehmen nur wenn Ziel leer +moviebrowser.serie_auto_create Serien automatisch zuordnen +moviebrowser.load_default Lade Voreinstellung +moviebrowser.browser_row_head Spalten Einstellungen +moviebrowser.browser_row_nr Anzahl Spalten +moviebrowser.browser_row_item Spalteninfo +moviebrowser.browser_row_width Spaltenbreite [Punkte] +moviebrowser.reload_at_start Filminfos neu laden bei Start +moviebrowser.remount_at_start Filmdepot verbinden bei Start +moviebrowser.dir_head Zusätzliche Verzeichnisse +moviebrowser.dir Pfad +moviebrowser.use_dir Verzeichnis verwenden +moviebrowser.use_rec_dir Aufnahmeverzeichnis verwenden +moviebrowser.use_movie_dir Wiedergabeverzeichnis verwenden +moviebrowser.hide_series Verstecke Serien +moviebrowser.last_record_max_items Zeilen 'zuletzt gesehen' +moviebrowser.last_play_max_items Zeilen 'zuletzt aufgenommen' +moviebrowser.browser_frame_high Anzeigehöhe oberes Fenster [Punkte] +extra.auto_timeshift Automatische Ausnahme, Sekunden (0 = deaktiviert) +extra.auto_delete Automatische Löschung +extra.auto_to_record Timeshift zu den Aufnahmen hinzufügen +extra.record_time Timeshift Aufnahmezeit in Stunden +audioplayer.spectrum LCD Skala +extra.temp_timeshift Temporäres Timeshift +AUDIOSelectMenue.head Auswahl der Tonspur +infoviewer.languages Audio +mainmenu.clearsectionsd Lösche EPG Cache +moviebrowser.edit_book_name_info2 +usermenu.button_blue Benutzermenü 'blau' +usermenu.button_green Benutzermenü 'grün' +usermenu.button_red Benutzermenü 'rot' +usermenu.button_yellow Benutzermenü 'gelb' +usermenu.head Benutzermenü +usermenu.item_bar ---- Trennung ---- +usermenu.item_epg_misc EPG Funktionen +usermenu.item_none +usermenu.item_vtxt Videotext +usermenu.name Name +extra.fec_s2_qpsk_1_2 1/2 s2 qpsk +extra.fec_s2_qpsk_2_3 3/2 s2 qpsk +extra.fec_s2_qpsk_3_4 3/4 s2 qpsk +extra.fec_s2_qpsk_5_6 5/6 s2 qpsk +extra.fec_s2_qpsk_7_8 7/8 s2 qpsk +extra.fec_s2_qpsk_8_9 8/9 s2 qpsk +extra.fec_s2_qpsk_3_5 3/5 s2 qpsk +extra.fec_s2_qpsk_4_5 4/5 s2 qpsk +extra.fec_s2_qpsk_9_10 9/10 s2 qpsk +extra.fec_s2_8psk_1_2 1/2 s2 8psk +extra.fec_s2_8psk_2_3 3/2 s2 8psk +extra.fec_s2_8psk_3_4 3/4 s2 8psk +extra.fec_s2_8psk_5_6 5/6 s2 8psk +extra.fec_s2_8psk_7_8 7/8 s2 8psk +extra.fec_s2_8psk_8_9 8/9 s2 8psk +extra.fec_s2_8psk_3_5 3/5 s2 8psk +extra.fec_s2_8psk_4_5 4/5 s2 8psk +extra.fec_s2_8psk_9_10 9/10 s2 8psk +channellist.edit Bearbeiten +channellist.favs Favoriten +channellist.provs Provider +channellist.sats Satelliten +channellist.history History +channellist.current_tp Aktueller Transponder +video_mode_ok Video-Einstellungen ok ? +moviebrowser.edit_book_name_info2 book name info2 +satsetup.sat_setup Setup LNB-Konfiguration +satsetup.do_scan Satelliten scannen +satsetup.lofl LNB Low Offset +satsetup.lofh LNB High Offset +satsetup.lofs LNB Switch Offset +satsetup.diseqc_input Diseqc Einspeisung +satsetup.uncomm_input Uncommited +satsetup.motor_pos Rotor-Position +satsetup.auto_scan Ausgewählten Satelliten scannen +satsetup.auto_scan_all Alle Satelliten scannen +satsetup.manual_scan Manuller Frequenzscan +satsetup.use_nit Benutze NIT +satsetup.diseqc_advanced Erweiterte Auswahl +satsetup.diseqc_order Einspeisungsfolge +satsetup.diseqc_com_uncom Commited/Uncommited +satsetup.diseqc_uncom_com Uncommited/Commited +satsetup.comm_input Commited Input +satsetup.use_usals Benutze Usals +motorcontrol.user_menu Benutzermenü +motorcontrol.install_menu Motor Installation +motorcontrol.step_west Fahre/Schrittsetze Motor nach Westen (b,c) +motorcontrol.step_east Fahre/Schrittsetze Motor nach Osten (b,c) +motorcontrol.halt Stope Motor +motorcontrol.west_limit Setze westliches (soft) Limit +motorcontrol.east_limit Setze östliches (soft) Limit +motorcontrol.enable_limit Aktiviere (soft) Limit +motorcontrol.disable_limit Deaktiviere (soft) Limit +motorcontrol.ref_position Gehe zu Referenz Position +motorcontrol.calc_positions (Neu)-Berechne(n)) Positionen +motorcontrol.pos_increase Motor Position rauf (a) +motorcontrol.pos_decrease Motor Position runter (a) +motorcontrol.step_drive Umschalten Schrittsetzungs/Fahr Modus (b) +motorcontrol.store Speichere Motor Position (a) +motorcontrol.step_increase Schrittweite rauf (c) +motorcontrol.step_decrease Schrittweite runter (c) +motorcontrol.goto Gehe zu Motor Position (a) +motorcontrol.notdef Not used +motorcontrol.motor_pos (a) Motor Position: +motorcontrol.settings Motor Einstellungen +motorcontrol.movement (b) Bewegung: +motorcontrol.step_mode Schrittsetzungs Modus +motorcontrol.drive_mode Fahr Modus +motorcontrol.drive_mode_auto Fahr Modus/automatischer Stop +motorcontrol.timed_mode Zeitgesteuerter Schrittweiten Modus +motorcontrol.step_size (c) Schrittweite: +motorcontrol.stop_moving Stope bei guten Signal(moving) +motorcontrol.stop_stopped Stope bei schlechtem Signal(stopped) +motorcontrol.no_mode keine Sorge +motorcontrol.msec Millisekunden +motorcontrol.status Status +motorcontrol.sat_pos Satellitenposition (Step Mode): +mainsettings.timezone Zeitzone +scrambled_channel Verschlüsselung aktiv +neutrino_starting Neutrino wird gestartet... +reset_channels Lösche Kanäle +reset_settings Zurückstellen auf Werkseinstellungen +reset_all Werkseinstellungen zurücksetzen +reset_confirm Sind Sie sich sicher? +cam.settings CI CAM +cam.empty Steckplatz leer +cam.inserted In der Steckplatz befindet sich ein Cam +cam.removed CAM von der Steckplatz entfernt +cam.init_ok CAM Initialisierung vollendet +subtitles.head Untertitel +subtitles.stop Stop Untertitel +favorites.copy Kopiere Bouquet zu Favoriten diff --git a/data/locale/dutch.locale b/data/locale/dutch.locale new file mode 100644 index 000000000..8116729ff --- /dev/null +++ b/data/locale/dutch.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus Elec. Programma Gids +EPGMenu.eventinfo Details aktuele uitzending +EPGMenu.eventlist Programma gids aktuele zender +EPGMenu.head EPG - Elec-Programma-Gids +EPGMenu.streaminfo technische informatie +EPGPlus.actions Akties +EPGPlus.bybouquet_mode per bouquet +EPGPlus.bypage_mode per pagina +EPGPlus.change_font_size font grootte +EPGPlus.change_font_style font stijl +EPGPlus.change_size grootte +EPGPlus.channelentry_font kanaal font +EPGPlus.channelentry_separationlineheight afstand tussen de lijnen +EPGPlus.channelentry_width breedte +EPGPlus.channelevententry_font programma font +EPGPlus.edit_fonts edit fonts +EPGPlus.edit_sizes edit afmeting +EPGPlus.font_style_bold stijl bold +EPGPlus.font_style_italic stijl italic +EPGPlus.font_style_regular stijl normaal +EPGPlus.footer_fontbouquetchannelname selekteer bouquet/kanaalnaam +EPGPlus.footer_fontbuttons selekteer buttons +EPGPlus.footer_fonteventdescription selekteer aktuele uitzending +EPGPlus.footer_fonteventshortdescription selekteer verkorte uitzending +EPGPlus.head Programma gids Overzicht (EPG Plus) +EPGPlus.header_font kop font +EPGPlus.horgap1_height hoogte 1 +EPGPlus.horgap2_height hoogte 2 +EPGPlus.next_bouquet volgend bouquet +EPGPlus.options opties +EPGPlus.page_down pagina neer +EPGPlus.page_up pagina op +EPGPlus.prev_bouquet vorig bouquet +EPGPlus.record opnemen +EPGPlus.refresh_epg aktualiseren +EPGPlus.remind Herinnering +EPGPlus.reset_settings reset instellingen +EPGPlus.save_settings sla instellingen op +EPGPlus.scroll_mode Scroll Mode +EPGPlus.select_font_name font naam +EPGPlus.settings instellingen +EPGPlus.slider_width slider breedte +EPGPlus.stretch_mode Uitstrek Mode +EPGPlus.swap_mode swap mode +EPGPlus.timeline_fontdate font datum +EPGPlus.timeline_fonttime font tijd +EPGPlus.vergap1_width breedte 1 +EPGPlus.vergap2_width breedte 2 +EPGPlus.view_mode kijk mode +GENRE.ARTS.0 kunst / kultuur +GENRE.ARTS.1 uitvoerende kunst +GENRE.ARTS.10 kunst/kultuur series +GENRE.ARTS.11 mode +GENRE.ARTS.2 klein kunst +GENRE.ARTS.3 geloof +GENRE.ARTS.4 volks kunst +GENRE.ARTS.5 literatuur +GENRE.ARTS.6 film/bioscoop +GENRE.ARTS.7 experimentele film/video +GENRE.ARTS.8 radio/pers +GENRE.ARTS.9 nieuwe media +GENRE.CHILDRENs_PROGRAMMES.0 kinder / jeugd programma +GENRE.CHILDRENs_PROGRAMMES.1 kleuter programma's +GENRE.CHILDRENs_PROGRAMMES.2 ontwikkelings programma's voor 6 to 14 +GENRE.CHILDRENs_PROGRAMMES.3 ontwikkelings programma's voor 10 to 16 +GENRE.CHILDRENs_PROGRAMMES.4 informatie/educatie/school programma's +GENRE.CHILDRENs_PROGRAMMES.5 teken/poppen +GENRE.DOCUS_MAGAZINES.0 documentatie / series +GENRE.DOCUS_MAGAZINES.1 natuur/dieren/omgeving +GENRE.DOCUS_MAGAZINES.2 technologie/natuur wetenschappen +GENRE.DOCUS_MAGAZINES.3 medisch/physiologie/psychologie +GENRE.DOCUS_MAGAZINES.4 buitenland/expedities +GENRE.DOCUS_MAGAZINES.5 sociale/geestelijke wetenschappen +GENRE.DOCUS_MAGAZINES.6 verdere ontwikkeling +GENRE.DOCUS_MAGAZINES.7 talen +GENRE.MOVIE.0 film/drama +GENRE.MOVIE.1 detective/thriller +GENRE.MOVIE.2 avontuur/western/oorlog +GENRE.MOVIE.3 science fiction/fantasy/horror +GENRE.MOVIE.4 komedie +GENRE.MOVIE.5 soap/melodrama/folklore +GENRE.MOVIE.6 romantiek +GENRE.MOVIE.7 serieus/klassiek/geloof/histories film/drama +GENRE.MOVIE.8 volwassen film/drama +GENRE.MUSIC_DANCE.0 musiek / ballet / dans +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 serieuze muziek/klassieke muziek +GENRE.MUSIC_DANCE.3 volks/traditionele muziek +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 nieuws +GENRE.NEWS.1 nieuws/weer verslag +GENRE.NEWS.2 nieuws serie +GENRE.NEWS.3 documentatie +GENRE.NEWS.4 discussie/interview/debat +GENRE.SHOW.0 Show / Spelshow +GENRE.SHOW.1 spel show/quiz/wedstrijd +GENRE.SHOW.2 varietee show +GENRE.SHOW.3 praat show +GENRE.SOCIAL_POLITICAL.0 sociaal & politieke evenement / zakelijk +GENRE.SOCIAL_POLITICAL.1 series/verslag/documentaire +GENRE.SOCIAL_POLITICAL.2 economisch/sociaal advies +GENRE.SOCIAL_POLITICAL.3 buitengewone mensen +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 speciale uitzendingen (Olympische Spelen,Wereld Cup etc.) +GENRE.SPORTS.10 equestrian +GENRE.SPORTS.11 vecht sporten +GENRE.SPORTS.2 sport series +GENRE.SPORTS.3 voetbal +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 team sport (uitgezonderd voetbal) +GENRE.SPORTS.6 athletiek +GENRE.SPORTS.7 motor sport +GENRE.SPORTS.8 water sport +GENRE.SPORTS.9 winter sport +GENRE.TRAVEL_HOBBIES.0 reis & recreatie +GENRE.TRAVEL_HOBBIES.1 tourisme/reisen +GENRE.TRAVEL_HOBBIES.2 handwerken +GENRE.TRAVEL_HOBBIES.3 motor +GENRE.TRAVEL_HOBBIES.4 fitness & gezondheid +GENRE.TRAVEL_HOBBIES.5 koken +GENRE.TRAVEL_HOBBIES.6 advertentie/shopping +GENRE.TRAVEL_HOBBIES.7 tuinieren +GENRE.UNKNOWN onbekend +apids.hint_1 Geef APIDs om te streamen +apids.hint_2 in HEX in gescheiden door ' ' +apidselector.head Selecteer taal +audiomenu.PCMOffset Verlaag Volume PCM +audiomenu.analogout Analoge Output +audiomenu.avs avs +audiomenu.avs_control Volume Kontrole avs/oost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Audio Instellingen +audiomenu.lirc lirc +audiomenu.monoleft mono links +audiomenu.monoright mono rechts +audiomenu.ost oost +audiomenu.stereo stereo +audioplayer.add voegtoe +audioplayer.artist_title Artiest, Titel +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id zoek op ID +audioplayer.button_select_title_by_name zoek op naam +audioplayer.defdir start dir. +audioplayer.delete verwijder +audioplayer.deleteall verwijder alles +audioplayer.display_order Laat volgorde zien +audioplayer.fastforward snel vooruit. +audioplayer.follow auto selecteer aktuele +audioplayer.head Audio afspeellijst +audioplayer.highprio Hoge decodeeer prioriteit +audioplayer.id3scan Scan ID3 tags +audioplayer.jump_backwards spring terug +audioplayer.jump_dialog_hint1 Geef aub sprong doel in +audioplayer.jump_dialog_hint2 (relatief, in seconden) +audioplayer.jump_dialog_title Geef sprong doel in +audioplayer.jump_forwards spring vooruit +audioplayer.keylevel toets stand +audioplayer.name Audiospeler +audioplayer.pause pause +audioplayer.play Afspelen +audioplayer.playing Aktuele Track +audioplayer.playlist_fileerror_msg File niet gemaakt: +audioplayer.playlist_fileerror_title File fout +audioplayer.playlist_fileoverwrite_msg Wil je deze file overschrijven: +audioplayer.playlist_fileoverwrite_title Titel overschrijven? +audioplayer.playlist_name filenaam van de afspeellijst +audioplayer.playlist_name_hint1 Geef aub de filenaam van de afspeellijst in +audioplayer.playlist_name_hint2 De extensie .m3u wordt aut. toegevoegd +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.repeat_on herhaal mode inschakelen +audioplayer.rewind opnieuw spoelen +audioplayer.save_playlist sla afspeellijst op +audioplayer.screensaver_timeout screensaver timeout (min, 0=off) +audioplayer.select_title_by_name zoek titel bij naam (SMS) +audioplayer.show_playlist Toon afspeellijst +audioplayer.shuffle schudden +audioplayer.stop Stop +audioplayer.title_artist Titel, Artiest +audioplayerpicsettings.general audiospeler / plaatjeskijken +bookmarkmanager.delete wissen +bookmarkmanager.name bookmarks +bookmarkmanager.rename hernoem +bookmarkmanager.select selecteer +bouqueteditor.add voegtoe +bouqueteditor.bouquetname Naam van het bouquet +bouqueteditor.delete wissen +bouqueteditor.discardingchanges Herstel veranderingen. Geduld aub. +bouqueteditor.hide Verberg +bouqueteditor.lock Vergrendel +bouqueteditor.move Beweeg +bouqueteditor.name bouquet bewerker +bouqueteditor.newbouquetname Nieuwe naam van het bouquet +bouqueteditor.rename Hernoem +bouqueteditor.return klaar +bouqueteditor.savechanges? Wil je de veranderingen opslaan? +bouqueteditor.savingchanges Sla veranderingen op. Wacht aub ... +bouqueteditor.switch voegtoe/verwijder +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider kabel provider +channellist.head All Onderhoud +channellist.nonefound Geen kanalen gevonden!\nVoer aub een scan uit\n(DBOX-key -> onderhoud) +channellist.since start tijd %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blauw +colorchooser.green groen +colorchooser.red rood +colormenu.background Achtergrond +colormenu.fade Fade menu's +colormenu.font Font grootte +colormenu.gtx_alpha Transparentie (GTX) +colormenu.head Color Settings +colormenu.menucolors Menukleur +colormenu.statusbar Infobar +colormenu.textcolor Textkleur +colormenu.themeselect selecteer thema +colormenu.timing OSD Timing +colormenusetup.head Menu Kleuren +colormenusetup.menucontent Window-Inhoud +colormenusetup.menucontent_inactive Window-Inhoud inaktief +colormenusetup.menucontent_selected Window-Inhoud selekteren +colormenusetup.menuhead Menu Kop +colorstatusbar.head Infobalk kleur +colorstatusbar.text Infobalk text +colorthememenu.classic_theme Klassiek Thema +colorthememenu.dblue_theme Donkerblauw Thema +colorthememenu.dvb2k_theme DVB2000 Thema +colorthememenu.head Thema Kiezer +colorthememenu.neutrino_theme Neutrino Thema +date.Apr Apr +date.Aug Aug +date.Dec Dec +date.Feb Feb +date.Fri Vrij +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Maart +date.May Mei +date.Mon Maan +date.Nov Nov +date.Oct Okt +date.Sat Zat +date.Sep Sep +date.Sun Zon +date.Thu Don +date.Tue Dins +date.Wed Woens +epglist.head programma-gids- %s +epglist.noevents EPG niet aanwezig... +epgviewer.More_Screenings Volgend scherm +epgviewer.nodetailed Geen gedetaileerde informatie aanwezig +epgviewer.notfound geen epg gevonden +eventlistbar.channelswitch markeren +eventlistbar.eventsort sorteer +eventlistbar.recordevent neemop +favorites.addchannel Het aktuele kanaal wordt toegevoegd \naan het bouquet"My Favorites".\nDit duurt enkele seconden... +favorites.bouquetname My Favorites +favorites.bqcreated Bouquet "My Favorites" is gemaakt...\n +favorites.chadded Het aktuele Kanaal is aan je favorieten toegevoegd...\n +favorites.chalreadyinbq Het aktuele kanaal is reeds in je Favorieten...\n +favorites.finalhint \nGebruik de bouquet bewerker om je Favorieten aan te passen.\n +favorites.menueadd voeg Kanaal toe aan favorieten +favorites.nobouquets Favorieten zijn alleen beschikbaar bij geactiveerde Bouquets . +filebrowser.delete Wis +filebrowser.denydirectoryleave weiger dir teverlaten +filebrowser.dodelete1 Wis +filebrowser.dodelete2 ? +filebrowser.filter.active Filter aan +filebrowser.filter.inactive Filter uit +filebrowser.head Filebrowser +filebrowser.mark Markeer +filebrowser.nextpage volgende pagina +filebrowser.prevpage vorige pagina +filebrowser.scan Scanning folder +filebrowser.select Selecteer +filebrowser.showrights Toon file rechten +filebrowser.sort.date (datum) +filebrowser.sort.name (filenaam) +filebrowser.sort.namedirsfirst (filenaam2) +filebrowser.sort.size (grootte) +filebrowser.sort.type (type) +flashupdate.actionreadflash Flash lezen +flashupdate.cantopenfile kan de file niet openen +flashupdate.cantopenmtd kan mtd-device niet openen +flashupdate.checkupdate zoek naar nieuwe versie +flashupdate.currentreleasecycle Release cycle +flashupdate.currentversion_sep Aktuele versie +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot ImageType +flashupdate.currentversiontime Tijd +flashupdate.erasefailed flash wissen mislukt +flashupdate.erasing flash wissen +flashupdate.experimentalimage De geselecteerde image is een ongeteste versie,dit betekend\nbootup van de Box kan mislukken na update.\n\nWilt U werkelijk updaten naar deze versie? +flashupdate.expertfunctions Expert-functies +flashupdate.fileis0bytes De filegrootte is 0 Bytes +flashupdate.fileselector File-Kiezer +flashupdate.flashreadyreboot De image was succesvol geflashed.\nDe ruDbox gaat nu herstarten. +flashupdate.getinfofile haal versie info op +flashupdate.getinfofileerror Kan versie info niet ophalen +flashupdate.getupdatefile haal update file op +flashupdate.getupdatefileerror kan update niet ophalen +flashupdate.globalprogress voortgang: +flashupdate.head Software Update +flashupdate.md5check Image testen +flashupdate.md5sumerror Image heeft fouten +flashupdate.msgbox Volgende nieuwe image gevonden:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nWil je deze versie nu downloaden en installeren? +flashupdate.msgbox_manual De volgende nieuwe file gevonden:\nDate: %s, %s\nBaseImage: %s\nType: %s\n\nWil je deze versie nu installeren? +flashupdate.mtdselector Partition-Kiezer +flashupdate.programmingflash Flash programmeren +flashupdate.proxypassword Paswoord +flashupdate.proxypassword_hint1 geef de proxyserver paswoord in +flashupdate.proxypassword_hint2 niets ingeven betekend geen proxy-auth. +flashupdate.proxyserver Hostname +flashupdate.proxyserver_hint1 geef proxyserver naam of ip in, gebruik host:port +flashupdate.proxyserver_hint2 niets ingeven betekent geen proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Gebruikersnaam +flashupdate.proxyusername_hint1 geef de proxyserver gebruikersnaam in +flashupdate.proxyusername_hint2 niets ingeven betekent geen proxy-auth. +flashupdate.readflash Lees hele image +flashupdate.readflashmtd lees een partitie +flashupdate.ready ready +flashupdate.reallyflashmtd Wil je werkelijk flashen?\n\nAls een fout optreed of de image is niet\ngeldig, de Box wil niet booten na het flashen.\n\nImagenaam: %s\nTarget: %s +flashupdate.savesuccess De image was succesvol opgeslagen \nonder %s. +flashupdate.selectimage Beschikbare Images/Files +flashupdate.squashfs.noversion SquashFS versie test momenteel alleen ondersteund bij update via het Web.\nWeet je zeker dat je deze image wilt installeren? +flashupdate.titlereadflash Flash lezen +flashupdate.titlewriteflash Flash schrijven +flashupdate.updatemode Update mode +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual handmatig (ftp) +flashupdate.url_file config file +flashupdate.versioncheck test versie +flashupdate.writeflash schrijf hele image +flashupdate.writeflashmtd schrijf een partitie +flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Kanalen lijst +fontmenu.epg EPG +fontmenu.eventlist Programma gids +fontmenu.gamelist Spelen lijst +fontmenu.head Fontgrootte instellen +fontmenu.infobar Infobar +fontsize.channel_num_zap directe selectie +fontsize.channellist Kanalen lijst +fontsize.channellist_descr Beschrijving +fontsize.channellist_number Nummer +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titel +fontsize.eventlist_datetime datum / tijd +fontsize.eventlist_itemlarge groot +fontsize.eventlist_itemsmall klein +fontsize.eventlist_title Titel +fontsize.filebrowser_item filebrowser item +fontsize.gamelist_itemlarge groot +fontsize.gamelist_itemsmall klein +fontsize.hint Initialiseer font,\naub wachten... +fontsize.infobar_channame Kanaal naam +fontsize.infobar_info info +fontsize.infobar_number Nummer +fontsize.infobar_small klein +fontsize.menu Menutext +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titel +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG niet geladen.... +infoviewer.epgwait wacht op EPG... +infoviewer.eventlist Prg,s overzicht +infoviewer.languages Talen +infoviewer.motor_moving Motor aan het werk +infoviewer.nocurrent Geen info over aktueel programma beschikbaar +infoviewer.noepg EPG niet beschikbaar +infoviewer.notavailable Kanaal niet beschikbaar +infoviewer.selecttime Tijd kiezen +infoviewer.streaminfo Stream info +infoviewer.subservice Subservices +infoviewer.waittime Wacht op de tijd... +ipsetup.hint_1 Benut 0..9, of benut Op/Neer, +ipsetup.hint_2 OK slaat op, HOME breektaf +keybindingmenu.RC Key repeat-blocker +keybindingmenu.addrecord voeg record timer toe +keybindingmenu.addremind voeg zapto timer toe +keybindingmenu.allchannels_on_ok alle-kanalen +keybindingmenu.bouquetchannels_on_ok aktueel bouquet +keybindingmenu.bouquetdown bouquet terug +keybindingmenu.bouquethandling Bouquet-control +keybindingmenu.bouquetlist_on_ok bouquet-lijst +keybindingmenu.bouquetup volgende bouquet +keybindingmenu.cancel Kanalen lijst sluiten +keybindingmenu.channeldown kanaal omlaag +keybindingmenu.channellist kanalen lijst +keybindingmenu.channelup kanaal omhoog +keybindingmenu.head Toetsen Aanpassen +keybindingmenu.lastchannel Laatste kanaal +keybindingmenu.modechange Mode's wissel +keybindingmenu.pagedown pagina omlaag +keybindingmenu.pageup pagina omhoog +keybindingmenu.quickzap Vlug zappen +keybindingmenu.repeatblock herhaal vertraging +keybindingmenu.repeatblockgeneric begin vertraging +keybindingmenu.sort verander sorteer volgorde +keybindingmenu.subchanneldown subkanaal omlaag +keybindingmenu.subchannelup subkanaal omhoog +keybindingmenu.tvradiomode TV/Radio-mode +keybindingmenu.zaphistory Zap History Bouquet +keychooser.head verander Toets +keychooser.text1 Druk aub de nieuwe toets +keychooser.text2 wacht een paar seconden op abort +keychoosermenu.currentkey aktuele toets +keychoosermenu.setnew setup nieuwe toets +keychoosermenu.setnone geen toets +languagesetup.head Taal Keuze +languagesetup.select Taal +lcdcontroler.brightness normale Helderheid +lcdcontroler.brightnessstandby Standby Helderheid +lcdcontroler.contrast Kontrast +lcdcontroler.head LCD Instellingen +lcdmenu.autodimm Auto dimmer +lcdmenu.head LCD Instellingen +lcdmenu.inverse Omgekeerd +lcdmenu.lcdcontroler Kontrast / Helderheid +lcdmenu.power Verlichting +lcdmenu.statusline status lijn +lcdmenu.statusline.both volume / speelduur +lcdmenu.statusline.playtime speelduur +lcdmenu.statusline.volume volume +mainmenu.audioplayer Audiospeler +mainmenu.games Spellen +mainmenu.head Hoofd Menu +mainmenu.movieplayer Filmspeler +mainmenu.pausesectionsd Lees EPG +mainmenu.pictureviewer Plaatjeskijker +mainmenu.radiomode Radio-Mode +mainmenu.recording Opnemen +mainmenu.recording_start start +mainmenu.recording_stop stop +mainmenu.scartmode Scart-Mode +mainmenu.service Onderhoud +mainmenu.settings Instellingen +mainmenu.shutdown Uitzetten +mainmenu.sleeptimer SleepTimer +mainmenu.tvmode TV-Mode +mainsettings.audio Audio +mainsettings.colors Kleur / thema / font +mainsettings.head Instellingen +mainsettings.keybinding Toets Aanpassen +mainsettings.language Taal +mainsettings.lcd LC-Display +mainsettings.misc Diverse instellingen +mainsettings.network Netwerk +mainsettings.recording Opnemen +mainsettings.savesettingsnow sla instellingen nu op +mainsettings.savesettingsnow_hint Sla instellingen op,\nwacht aub... +mainsettings.streaming Filmspeler +mainsettings.video Video +menu.back terug +messagebox.back terug +messagebox.cancel herroep +messagebox.discard Niet uitvoeren? +messagebox.error Fout +messagebox.info Informatie +messagebox.no Nee +messagebox.yes Ja +miscsettings.bootinfo Toon info bij start +miscsettings.bootmenu Toon boot menu +miscsettings.driver_boot driver en boot opties +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general Algemeen +miscsettings.head Diverse instellingen +miscsettings.hwsections hardware secties gebruiken +miscsettings.infobar_sat_display Satelliet display op infobar +miscsettings.pmtupdate enable pmt update +miscsettings.shutdown_real Schakel standby in +miscsettings.shutdown_real_rcdelay Vertraagde shutdown +miscsettings.sptsmode gebruik spts mode +miscsettings.startbhdriver laad BH-Mode drivers +motorcontrol.head Motor-Setup +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmarknaam +movieplayer.bookmarkname_hint1 Geef een naam in voor je nieuwe bookmark +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffering... +movieplayer.defdir start dir. +movieplayer.dvdplayback DVD +movieplayer.fileplayback File via VLC +movieplayer.goto Spring naar ... +movieplayer.goto.h1 = -> absolute sprong +movieplayer.goto.h2 +,- -> relative sprong +movieplayer.head Filmspeler +movieplayer.nostreamingserver De stream server niet te bereiken. +movieplayer.pesplayback Speel PES (Experimenteel) +movieplayer.pleasewait Aub wachten.\nVerbind met stream server... +movieplayer.toomanybookmarks Er zijn teveel bookmarks.\nWis er eerst een van. +movieplayer.tsplayback Speel TS +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion Deze functie wordt niet ondersteund door deze versie van VLC +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway default gateway +networkmenu.head Netwerk Instellingen +networkmenu.ipaddress IP adres +networkmenu.mount NFS/CIFS +networkmenu.nameserver server naam +networkmenu.netmask netmask +networkmenu.setupnow netwerk veranderingen uitvoeren +networkmenu.setuponstartup netwerk bij startup +networkmenu.show Toon actief netwerk instellingen +networkmenu.test test netwerk nu +nfs.alreadymounted directory reeds gemounted +nfs.automount mount bij startup +nfs.dir directory/share +nfs.ip NFS/CIFS Server IP +nfs.localdir local dir +nfs.mount Mount NFS/CIFS volume +nfs.mount_options mount opties +nfs.mounterror mount fout +nfs.mounterror_notsup filesysteem type niet ondersteund +nfs.mountnow mount nu +nfs.mounttimeout mount fout: timeout +nfs.password CIFS paswoord +nfs.remount remount directories +nfs.type type +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount Unmount NFS/CIFS volume +nfs.umounterror unmounting volume fout +nfs.username CIFS gebruikersnaam +nfsmenu.head NFS/CIFS instellingen +nvod.percentage (%d%% over) +nvod.starting (start in %d min) +nvodselector.directormode Director-Mode +nvodselector.head Selecteer start-tijd +nvodselector.subservice Selecteer Subservice +options.default Reset naar defaults +options.fb framebuffer +options.null nul +options.off uit +options.on aan +options.serial serieel +parentallock.changepin verander PIN code +parentallock.changepin_hint1 Geef de nieuwe jeugd-bescherm pin code hier in! +parentallock.changetolocked bij gesloten bouquets +parentallock.head Geef Lock PIN code in +parentallock.lockage gesloten programma's +parentallock.lockage12 tot aan 12 jaar +parentallock.lockage16 tot aan 16 jaar +parentallock.lockage18 tot aan 18 jaar +parentallock.lockedchannel gelockt kanaal... +parentallock.lockedprogram GeLockt programma (tot aan %d jaar) +parentallock.never nooit +parentallock.onsignal bij uitgezending lock +parentallock.parentallock Jeugd bescherming +parentallock.prompt prompt voor PIN +pictureviewer.defdir start dir. +pictureviewer.head Plaatjes kijker +pictureviewer.resize.color_average uitvoerig +pictureviewer.resize.none niet +pictureviewer.resize.simple eenvoudig +pictureviewer.scaling scaling +pictureviewer.show Tonen +pictureviewer.slide_time diashow speel duur +pictureviewer.slideshow diashow +pictureviewer.sortorder verander sorteer volgorde +pictureviewer.sortorder.date (datum) +pictureviewer.sortorder.filename (filenaam) +ping.ok is bereikbaar (ping) +ping.protocol is onbereikbaar (host of protocol fout) +ping.socket is onbereikbaar (socket fout) +ping.unreachable is onbereikbaar +pinprotection.head Geef PIN code in +pinprotection.wrongcode PIN-Code was verkeerd! Probeer opnieuw. +rclock.lockmsg Je ruDbox A-fstand B-ediening is vergrendeld.\nOm je A-B te ontgrendelen, druk en \nop je A-fstand B-ediening. +rclock.menueadd Vergrendel A-B +rclock.title Vergrendel Afstand Bediening +rclock.unlockmsg A-fstand B-ediening gereactiveerd. +recordingmenu.defdir opneem directory +recordingmenu.file direct (file) +recordingmenu.head Opneem instellingen +recordingmenu.help Recording devices:\n--------------------------\nserver:\ngebruik stream software op een PC\n\n(analoog) vcr:\ngebruik de vcr uitgang\n\ndirect (file):\ndirect in een NFS mounted directory\nof op een interne hard disk\nTS: gebruik spts mode(dbox2)\nPES: niet spts mode(dbox2) gebruiken\n\n\nMax. file grootte:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: bijna ongelimiteerd (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart onderdruk scart mode schakelen +recordingmenu.off uit +recordingmenu.recording_type opneem device +recordingmenu.server server +recordingmenu.server_ip opneem server IP +recordingmenu.server_mac MAC adres +recordingmenu.server_port opneem server poort +recordingmenu.server_wakeup opneem server WOL +recordingmenu.setupnow activeer veranderingen nu +recordingmenu.splitsize Max. file grootte (MB) +recordingmenu.stopplayback stop terug spelen +recordingmenu.stopsectionsd stop sectionsd +recordingmenu.stream_vtxt_pid neem videotext op +recordingmenu.use_o_sync schrijf synchroon (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce Opname start over enkele minuten +repeatblocker.hint_1 Kortste tijd (in ms) tussen 2 toets aanslagen +repeatblocker.hint_2 0 schakeld de blocker uit (rood is space) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-herhalingen +satsetup.extended DiSEqC-Instellingen +satsetup.extended_motor Motor-Instellingen +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Motor handmatig instellen +satsetup.nodiseqc geen DiSEqC +satsetup.satellite Satelliet +satsetup.savesettingsnow sla instellingen nu op +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Kabel: +scants.actsatellite Satelliet: +scants.bouquet Bouquet +scants.bouquet_create creeer nieuw +scants.bouquet_erase wissen +scants.bouquet_leave niet veranderen +scants.bouquet_satellite Satelliet-Bouquet +scants.bouquet_update update +scants.channel kanaal: +scants.failed Transponderscan mislukt! +scants.finished Transponderscan succesvol beeindigd! +scants.freqdata Frequentie: +scants.head Scan transponder +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Totaal +scants.numberoftvservices TV +scants.provider Provider: +scants.startnow start scan +scants.transponders Transponders: +screensetup.lowerright groen = rechts onder instellen +screensetup.upperleft rood = links boven instellen +servicemenu.head Onderhoud +servicemenu.reload lees kanaallijst opnieuw in +servicemenu.reload_hint Lees kanaallijst opnieuw in,\ngeduld aub. +servicemenu.scants Scan onderhouden +servicemenu.ucodecheck Check ucodes +servicemenu.update Software Update +settings.help Help +settings.missingoptionsconffile De neutrino-instellingen zijn geupdated.\nNieuwe opties worden op default gezet. +settings.noconffile Geen neutrino-instellingen gevonden.\nGebruik default. +shutdowntimer.announce Box gaat shutdown over 1 min.\nShutdown afbreken? +sleeptimerbox.announce Sleeptimer over 1 min +sleeptimerbox.hint1 Shutdown tijd in min. (000=uit) +sleeptimerbox.hint2 De ruDbox gaat shutdown na deze tijd. +sleeptimerbox.title Sleeptimer +streamfeatures.head Features +streaminfo.aratio verhouding +streaminfo.aratio_unknown verhoudig: onbekend +streaminfo.audiotype Audiotype +streaminfo.audiotype_unknown Audiotype: onbekend +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: onbekend +streaminfo.head Stream-Informatie +streaminfo.not_available niet beschikbaar +streaminfo.resolution Resolutie +streaminfo.signal Ontvangst signaal +streaming.buffer_overflow De opname is helaas afgebroken,\nomdat de data niet snel genoeg geschreven kon worden. +streaming.busy Een of meerdere opneem processen zijn aktief.\nAls je dit bericht krijgt en geen opname is aktief,\nherstart Neutrino aub. +streaming.dir_not_writable De opneem directory is niet beschrijfbaar.\nOpname zal niet gaan. +streaming.success De opname is succesvol beeindigd. +streaming.write_error De opname was afgebroken,\nomdat er een fout optrad gedurende het schrijven. +streaming.write_error_open De opname is afgebroken,\nomdat de file niet geopend kon worden. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Filmspeler Instellingen +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off uit +streamingmenu.on aan +streamingmenu.server_ip Streamserver IP +streamingmenu.server_port Streamserver Poort +streamingmenu.streaming_audiorate Datarate Audio +streamingmenu.streaming_force_avi_rawaudio Forceeer AC3 bij AVI +streamingmenu.streaming_force_transcode_video Transcode MPG/VCD video +streamingmenu.streaming_resolution Resolutie +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Directory (VLC) +streamingmenu.streaming_transcode_audio Transcode audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG video codec +streamingmenu.streaming_type Streamserver +streamingmenu.streaming_videorate Datarate Video +streamingserver.noconnect Geen verbinding met streamserver.\nOpname gestopt +stringinput.caps Hoofd- / kleine letters +stringinput.clear alles schoonmaken +timer.eventrecord.msg ... DOEN, of LATEN +timer.eventrecord.title Markeer uitzending +timer.eventtimed.msg De uitzending is gemarkeerd.\nDe ruDbox zal aan gaan en\nnaar dit kanaal schakelen op de aangegeven tijd. +timer.eventtimed.title Uitzending markeren +timerbar.channelswitch markeren +timerbar.recordevent Opnemen +timerlist.alarmtime Alarm tijd +timerlist.apids Audio PIDs +timerlist.bouquetselect kies bouquet +timerlist.channel Kanaal +timerlist.channelselect kies kanaal +timerlist.delete Wis +timerlist.menumodify Pas timer aan +timerlist.menunew Nieuwe timer +timerlist.message Bericht +timerlist.moderadio Radio kanalen +timerlist.modeselect Mode kiezen +timerlist.modetv TV kanalen +timerlist.modify Pas aan +timerlist.name Timer lijst +timerlist.new Nieuwe timer +timerlist.program.unknown Onbekend programma +timerlist.reload Herladen +timerlist.repeat Herhaal +timerlist.repeat.biweekly twee wekelijks +timerlist.repeat.byeventdescription zie timer +timerlist.repeat.daily dagelijks +timerlist.repeat.fourweekly vier wekelijks +timerlist.repeat.friday Vrij +timerlist.repeat.monday Maan +timerlist.repeat.monthly maandelijks +timerlist.repeat.once eens +timerlist.repeat.saturday Zat +timerlist.repeat.sunday Zon +timerlist.repeat.thursday Don +timerlist.repeat.tuesday Dins +timerlist.repeat.unknown onbekend +timerlist.repeat.wednesday Woens +timerlist.repeat.weekdays door de week +timerlist.repeat.weekly wekelijks +timerlist.save Save timer +timerlist.standby SB mode +timerlist.standby.off Leave standby +timerlist.standby.on Enter standby +timerlist.stoptime Stop tijd +timerlist.type Timer type +timerlist.type.nextprogram volgend programma +timerlist.type.record Neemop +timerlist.type.remind Herinner +timerlist.type.shutdown Shutdown +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Onbekend +timerlist.type.zapto Zap naar +timerlist.weekdays Dagen van de week +timerlist.weekdays.hint_1 Maan Dins Woens Don Vrij Zat Zon +timerlist.weekdays.hint_2 'X'=timer '-' geen timer +timersettings.record_safety_time_after Opname stop tijd correctie +timersettings.record_safety_time_after.hint_1 Correctie tijd in min. (00=uit). Deze tijd +timersettings.record_safety_time_after.hint_2 word opgeteld bij de stop tijd van elke opname timer. +timersettings.record_safety_time_before Opname start tijd correctie +timersettings.record_safety_time_before.hint_1 Correctie tijd in min. (00=uit). Deze tijd +timersettings.record_safety_time_before.hint_2 word afgetrokken van elke opname timer. +timersettings.separator Timer instellingen +timing.chanlist Kanaallijst +timing.epg Epg +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Tijd in sec. Na deze tijd zal de +timing.hint_2 infobar wegfaden. +timing.infobar Infobar +timing.menu Menu +timing.numericzap Nummer Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode check +ucodecheck.ucode UCode +ucodes.failure ATTENTIE, µCodes NIeT gevonden!\n\nAub uploaden via FTP (of DBox-BootManager),\nen herstart je ruDbox! +videomenu.csync sync correctie +videomenu.head Video Instellingen +videomenu.rgb_centering RGB centrering +videomenu.screensetup Scherm aanpassen +videomenu.vcrswitch Schakel Scart automatisch +videomenu.videoformat Formaat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect autodetecteer +videomenu.videosignal VideoOutput +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Zap naar timer in een minute +extra.auto Auto +extra.valid Valid +extra.all Alles +extra.new_keys Nieuwe keys +extra.all_keys Alle keys +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo Algo pids +extra.gbox_emu Emu +extra.gbox_softcam Softcam +extra.gbox_net Netwerk +extra.gbox_mix Mixed +extra.gbox_info Gbox Info +extra.sep_extra Extras +extra.save_settings Sla Instellingen op +extra.extra_menu Addons +extra.ecm_info Ecm Info +extra.pid_info Pid Info +extra.emm_info New Keys +extra.emu_type Emu Type +extra.camd Camd +extra.restart_camd Herstart camd +extra.mg_settings Mgcamd instellingen +extra.netmode Netmode +extra.autoupdate Autoupdate +extra.show_ecm Toon Ecm +extra.show_emm Toon Emm +extra.keyupdate Key update +extra.osd OSD +extra.keyfolder Key dir +extra.hash_pids Hash pids +extra.gbox_settings Gbox instellingen +extra.gbox_mode Mode +extra.newcamd_settings Newcamd instellingen +extra.show_cw Toon CW +extra.debug_ecm Debug Ecm +extra.disable_cam Disable CAM +extra.show_cat Toon CAT +extra.show_pmt Toon PMT +extra.update_pmt Update PMT +extra.report_emm Meld verkeerd EMM sig. +extra.debug_emm Debug Emm +extra.show_allca Toon alle ca sys. +extra.reload_keys Herlaad keys bij zap +extra.reload_cfg Herlaad config bij zap +extra.cw_delay Default CW delay +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Toon PAT +extra.dvbsnoop_cat Toon CAT +extra.dvbsnoop_tsdt Toon TSDT +extra.dvbsnoop_nit Toon NIT +extra.dvbsnoop_sdt Toon SDT +extra.dvbsnoop_eit Toon EIT +extra.dvbsnoop_rst Toon RST +extra.dvbsnoop_tdt Toon TDT +extra.dvbsnoop_sit Toon SIT +extra.dvbsnoop_pid Toon manual PID +extra.dvbsnoop_scan Scan PIDs +extra.dvbsnoop_band Toon pid bandbreedte +extra.logview Bekijk Log +extra.ru ruDREAM +extra.english Engels +extra.dboxinfo Box Info +extra.scan_mode Scan mode +extra.start_tostandby Start tot standby +extra.rotor_swap Swap rotor oost/west +extra.use_log Log to /tmp/log +extra.spts_mode SPTS mode +extra.hw_sect Hardware Secties +extra.logo Logo nummer +extra.scan_full vol +extra.scan_fast snel +extra.hdd_slow langzaam +extra.hdd_middle Middel +extra.hdd_fast Snel +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Sleep time +extra.hdd_noise Lawaai +extra.hdd_activate Activeer instellingen +extra.hdd_fs Filesysteem +extra.hdd_format Formateer HDD +extra.hdd_check Check filesysteem +extra.hdd_settings HDD instellingen +extra.clear_log Clear Log +extra.zap_cycle Zap cyclus +extra.sms_channel sms-kanaal +extra.manual_scan Manuele scan +extra.tp_freq Frequentie +extra.tp_rate Symbol rate +extra.tp_pol Polarisatie +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Tijd zone +extra.zapit_menu Zapit opties +extra.zapit_make_bouquet Maak bouquet +extra.zapit_save_last_chan Sla laatste kanaal op +extra.zapit_motor_speed Motor snelheid (10 = 1 graad/sec) +extra.zapit_fe_timeout Timeout melodie +extra.add_to_bouquet voeg to aan bouquet +extra.key_list_start begin +extra.key_list_end einde +extra.chadded het aktuele kanaal is aanhet geselecteerde buoquet toegevoegd.....\n +extra.chalreadyinbq het aktuele kanaal is reeds in het geselecteerde bouquet....\n +extra.menu_left_exit "Left" = terug in menu +extra.zapit_write_names Schrijf Kanaal namen +extra.update_dir Directory for updates +extra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +audioplayer.reading_files reading files +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Onderbreek Scan +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/english.locale b/data/locale/english.locale new file mode 100644 index 000000000..7bef3513e --- /dev/null +++ b/data/locale/english.locale @@ -0,0 +1,1349 @@ +EPGMenu.epgplus Eventlist overview +EPGMenu.eventinfo Details current program +EPGMenu.eventlist Eventlist current programm +EPGMenu.head EPG - Program Information +EPGMenu.streaminfo technical information +EPGPlus.actions Actions +EPGPlus.bybouquet_mode by bouquet +EPGPlus.bypage_mode by page +EPGPlus.change_font_size font size +EPGPlus.change_font_style font style +EPGPlus.change_size size +EPGPlus.channelentry_font channel font +EPGPlus.channelentry_separationlineheight separation line height +EPGPlus.channelentry_width width +EPGPlus.channelevententry_font event font +EPGPlus.edit_fonts edit fonts +EPGPlus.edit_sizes edit sizes +EPGPlus.font_style_bold style bold +EPGPlus.font_style_italic style italic +EPGPlus.font_style_regular style regular +EPGPlus.footer_fontbouquetchannelname footer bouquet +EPGPlus.footer_fontbuttons footer buttons +EPGPlus.footer_fonteventdescription footer event +EPGPlus.footer_fonteventshortdescription footer event short +EPGPlus.head Eventlist Overview (EPG Plus) +EPGPlus.header_font header font +EPGPlus.horgap1_height height1 +EPGPlus.horgap2_height height2 +EPGPlus.next_bouquet next bouquet +EPGPlus.options options +EPGPlus.page_down page down +EPGPlus.page_up page up +EPGPlus.prev_bouquet prev bouquet +EPGPlus.record Record +EPGPlus.refresh_epg Refresh +EPGPlus.remind Schedule +EPGPlus.reset_settings reset settings +EPGPlus.save_settings save settings +EPGPlus.scroll_mode Scroll Mode +EPGPlus.select_font_name font name +EPGPlus.settings settings +EPGPlus.slider_width slider width +EPGPlus.stretch_mode Stretch Mode +EPGPlus.swap_mode swap mode +EPGPlus.timeline_fontdate font date +EPGPlus.timeline_fonttime font time +EPGPlus.vergap1_width width1 +EPGPlus.vergap2_width width2 +EPGPlus.view_mode view mode +GENRE.ARTS.0 arts / culture +GENRE.ARTS.1 performing arts +GENRE.ARTS.10 arts/culture magazines +GENRE.ARTS.11 fashion +GENRE.ARTS.2 fine arts +GENRE.ARTS.3 religion +GENRE.ARTS.4 popular culture/traditional arts +GENRE.ARTS.5 literature +GENRE.ARTS.6 film/cinema +GENRE.ARTS.7 experimental film/video +GENRE.ARTS.8 broadcasting/press +GENRE.ARTS.9 new media +GENRE.CHILDRENs_PROGRAMMES.0 children / juvenile program +GENRE.CHILDRENs_PROGRAMMES.1 pre-school children's programmes +GENRE.CHILDRENs_PROGRAMMES.2 entertainment programmes for 6 to 14 +GENRE.CHILDRENs_PROGRAMMES.3 entertainment programmes for 10 to 16 +GENRE.CHILDRENs_PROGRAMMES.4 informational/educational/school programmes +GENRE.CHILDRENs_PROGRAMMES.5 cartoons/puppets +GENRE.DOCUS_MAGAZINES.0 documentation / magazine +GENRE.DOCUS_MAGAZINES.1 nature/animals/environment +GENRE.DOCUS_MAGAZINES.2 technology/natural sciences +GENRE.DOCUS_MAGAZINES.3 medicine/physiology/psychology +GENRE.DOCUS_MAGAZINES.4 foreign countries/expeditions +GENRE.DOCUS_MAGAZINES.5 social/spiritual sciences +GENRE.DOCUS_MAGAZINES.6 further education +GENRE.DOCUS_MAGAZINES.7 languages +GENRE.MOVIE.0 movie/drama +GENRE.MOVIE.1 detective/thriller +GENRE.MOVIE.2 adventure/western/war +GENRE.MOVIE.3 science fiction/fantasy/horror +GENRE.MOVIE.4 comedy +GENRE.MOVIE.5 soap/melodrama/folkloric +GENRE.MOVIE.6 romance +GENRE.MOVIE.7 serious/classical/religious/historical movie/drama +GENRE.MOVIE.8 adult movie/drama +GENRE.MUSIC_DANCE.0 music / ballet / dance +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 serious music/classical music +GENRE.MUSIC_DANCE.3 folk/traditional music +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 news +GENRE.NEWS.1 news/weather report +GENRE.NEWS.2 news magazine +GENRE.NEWS.3 documentary +GENRE.NEWS.4 discussion/interview/debate +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 game show/quiz/contest +GENRE.SHOW.2 variety show +GENRE.SHOW.3 talk show +GENRE.SOCIAL_POLITICAL.0 social & politic events / business +GENRE.SOCIAL_POLITICAL.1 magazines/reports/documentary +GENRE.SOCIAL_POLITICAL.2 economics/social advisory +GENRE.SOCIAL_POLITICAL.3 remarkable people +GENRE.SPORTS.0 sports +GENRE.SPORTS.1 special events (Olympic Games,World Cup etc.) +GENRE.SPORTS.10 equestrian +GENRE.SPORTS.11 martial sports +GENRE.SPORTS.2 sports magazines +GENRE.SPORTS.3 football/soccer +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 team sports (excluding football) +GENRE.SPORTS.6 athletics +GENRE.SPORTS.7 motor sports +GENRE.SPORTS.8 water sports +GENRE.SPORTS.9 winter sports +GENRE.TRAVEL_HOBBIES.0 travel & recreation +GENRE.TRAVEL_HOBBIES.1 tourism/travel +GENRE.TRAVEL_HOBBIES.2 handicraft +GENRE.TRAVEL_HOBBIES.3 motoring +GENRE.TRAVEL_HOBBIES.4 fitness & health +GENRE.TRAVEL_HOBBIES.5 cooking +GENRE.TRAVEL_HOBBIES.6 advertisement/shopping +GENRE.TRAVEL_HOBBIES.7 gardening +GENRE.UNKNOWN unknown +apidselector.head Select language +audiomenu.PCMOffset Volume Decrease PCM +audiomenu.analogout Analog Output +audiomenu.avs avs +audiomenu.avs_control Volume Control avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Audio Settings +audiomenu.lirc lirc +audiomenu.monoleft mono left +audiomenu.monoright mono right +audiomenu.ost ost +audiomenu.stereo stereo +audiomenu.hdmi_dd Encoded DD on HDMI +audiomenu.spdif_dd Encoded DD on SPDIF +audiomenu.avsync A/V sync +audiomenu.avsync_am Audio master +audioplayer.add Add +audioplayer.artist_title Artist, Title +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id search by ID +audioplayer.button_select_title_by_name search by name +audioplayer.defdir start dir. +audioplayer.delete Delete +audioplayer.deleteall delete all +audioplayer.display_order display order +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward fast forw. +audioplayer.follow auto select current +audioplayer.head Audio Playlist +audioplayer.highprio High decode priority +audioplayer.jump_backwards jump backwards +audioplayer.jump_dialog_hint1 Please enter jump target +audioplayer.jump_dialog_hint2 (relative, in seconds) +audioplayer.reading_files reading files +audioplayer.jump_dialog_title Enter jump target +audioplayer.jump_forwards jump forwards +audioplayer.keylevel key level +audioplayer.name Audioplayer +audioplayer.pause pause +audioplayer.play Play +audioplayer.playing Current Track +audioplayer.playlist_fileerror_msg File could not be created: +audioplayer.playlist_fileoverwrite_msg Do you want to overwrite this file: +audioplayer.playlist_fileoverwrite_title Overwrite? +audioplayer.playlist_name filename of the play list +audioplayer.playlist_name_hint1 Please enter the filename of the playlist +audioplayer.playlist_name_hint2 The extension .m3u will be added automatically +audioplayer.repeat_on enable repeat mode +audioplayer.rewind rewind +audioplayer.save_playlist save play list +audioplayer.screensaver_timeout screensaver timeout (min, 0=off) +audioplayer.select_title_by_name search title by name (SMS) +audioplayer.show_playlist Show Playlist +audioplayer.shuffle shuffle +audioplayer.stop Stop +audioplayer.title_artist Title, Artist +audioplayerpicsettings.general audioplayer / picviewer +bookmarkmanager.delete delete +bookmarkmanager.name bookmarks +bookmarkmanager.rename rename +bookmarkmanager.select select +bouqueteditor.add Add +bouqueteditor.bouquetname Name of bouquets +bouqueteditor.delete Delete +bouqueteditor.discardingchanges Discarding changes. Please be patient. +bouqueteditor.hide Hide +bouqueteditor.lock Lock +bouqueteditor.move Move +bouqueteditor.name bouquet editor +bouqueteditor.newbouquetname New name of bouquets +bouqueteditor.rename Rename +bouqueteditor.return ready +bouqueteditor.savechanges? Do you want to save the changes? +bouqueteditor.savingchanges Saving changes. Please wait ... +bouqueteditor.switch add/remove +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider cable provider +channellist.epgtext_align_left left +channellist.epgtext_align_right right +channellist.head All Services +channellist.nonefound No channels were found!\nPlease execute a scan\n(MENU-key -> service) +channellist.since since %02d:%02d +channellist.start starts %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blue +colorchooser.green green +colorchooser.red red +colormenu.background Background +colormenu.fade Fade menus +colormenu.font Font size +colormenu.gtx_alpha Transparency (GTX) +colormenu.head Color Settings +colormenu.menucolors Menucolor +colormenu.statusbar Infobar +colormenu.textcolor Textcolor +colormenu.themeselect select theme +colormenu.timing OSD Timing +colormenusetup.head Menu Colors +colormenusetup.menucontent Window-Content +colormenusetup.menucontent_inactive Window-Content inactive +colormenusetup.menucontent_selected Window-Content selected +colormenusetup.menuhead Menu Header +colorstatusbar.head Infobar +colorstatusbar.text Infobar +colorthememenu.classic_theme Classic Theme +colorthememenu.dblue_theme DarkBlue Theme +colorthememenu.dvb2k_theme DVB2000 Theme +colorthememenu.head Theme Selector +colorthememenu.neutrino_theme Neutrino Theme +colorthememenu.gray Gray Theme +date.Apr Apr +date.Aug Aug +date.Dec Dec +date.Feb Feb +date.Fri Fri +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mar +date.May May +date.Mon Mon +date.Nov Nov +date.Oct Oct +date.Sat Sat +date.Sep Sep +date.Sun Sun +date.Thu Thu +date.Tue Tue +date.Wed Wed +epglist.noevents EPG is not available... +epgviewer.More_Screenings More Screenings on this Channel +epgviewer.nodetailed No detailed informations available +epgviewer.notfound no epg found +eventfinder.head Search in EPG +eventfinder.keyword Keyword +eventfinder.start_search Start Search +eventfinder.search_within_list Search within +eventfinder.search_within_epg Search within +eventfinder.search Search +eventfinder.searching Search for keyword in EPG... +eventlistbar.channelswitch schedule +eventlistbar.eventsort sorting +eventlistbar.recordevent record +favorites.addchannel The current channel will be added \nto the bouquet "My Favorites". \n This will take a few seconds... +favorites.bouquetname My Favorites +favorites.bqcreated Bouquet "My Favorites" has been created...\n +favorites.chadded The current channel has been added to your favorites...\n +favorites.chalreadyinbq The current channel is already in your favorites...\n +favorites.finalhint \nUse the bouqueteditor to modify your favorites.\n +favorites.menueadd add channel to favorites +favorites.nobouquets Favorites are available with activated Bouquets only. +filebrowser.delete Delete +filebrowser.denydirectoryleave Absolute start directory +filebrowser.dodelete1 Delete +filebrowser.dodelete2 ? +filebrowser.filter.active Filter on +filebrowser.filter.inactive Filter off +filebrowser.head Filebrowser +filebrowser.mark Mark +filebrowser.nextpage Next Page +filebrowser.prevpage Prev. Page +filebrowser.scan Scaning folder +filebrowser.select Select +filebrowser.showrights Show file rights +filebrowser.sort.date (date) +filebrowser.sort.name (filename) +filebrowser.sort.namedirsfirst (filename2) +filebrowser.sort.size (Size) +filebrowser.sort.type (type) +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash reading +flashupdate.cantopenfile can't open file +flashupdate.cantopenmtd can't open mtd-device +flashupdate.checkupdate search for new version +flashupdate.currentreleasecycle Release cycle +flashupdate.currentversion_sep Current version +flashupdate.currentversiondate Date +flashupdate.currentversionsnapshot ImageType +flashupdate.currentversiontime Time +flashupdate.erasefailed erasure of flash failed +flashupdate.erasing erasing flash +flashupdate.experimentalimage The image you have selected is an untested version, this means\nyour box maybe fail to boot after update.\n\nDo you really want to update to this version? +flashupdate.expertfunctions Expert-functions +flashupdate.fileis0bytes the filesize is 0 Bytes +flashupdate.fileselector File-Selector +flashupdate.flashreadyreboot The image was successfully flashed.\nThe box will be rebooted now. +flashupdate.getinfofile getting versioninfo +flashupdate.getinfofileerror can't get versioninfo +flashupdate.getupdatefile getting update +flashupdate.getupdatefileerror can't get update +flashupdate.globalprogress Global Progress: +flashupdate.head Software Update +flashupdate.md5check checking image +flashupdate.md5sumerror image has errors +flashupdate.msgbox Found the following new file:\nDate: %s, %s\nBaseImage: %s\nType: %s\n\nDo you want to download and install this version now? +flashupdate.msgbox_manual Found the following new image:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nDo you want to install this version now? +flashupdate.mtdselector Partition-Selector +flashupdate.programmingflash programming flash +flashupdate.proxypassword Password +flashupdate.proxypassword_hint1 enter the proxyserver password +flashupdate.proxypassword_hint2 a empty entry means no proxy-auth +flashupdate.proxyserver Hostname +flashupdate.proxyserver_hint1 enter proxyserver name or ip, use host:port +flashupdate.proxyserver_hint2 a empty entry means no proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Username +flashupdate.proxyusername_hint1 enter the proxyserver username +flashupdate.proxyusername_hint2 a empty entry means no proxy-auth +flashupdate.readflash Read whole image +flashupdate.readflashmtd Read one partition +flashupdate.ready ready +flashupdate.reallyflashmtd Do you really want to flash?\n\nIf a error occurs or the image is not\nvalid, the box will not boot after flashing.\n\nImagename: %s\nTarget: %s +flashupdate.savesuccess The image was successfully saved \nunder %s. +flashupdate.selectimage Available Images/Files +flashupdate.squashfs.noversion SquashFS version checks are currently only supported when updating over the web.\nAre you sure that you wish to install this image? +flashupdate.titlereadflash Reading Flash +flashupdate.titlewriteflash Writing Flash +flashupdate.updatemode Updatemode +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manual (ftp) +flashupdate.url_file config file +flashupdate.versioncheck checking version +flashupdate.writeflash Write whole image +flashupdate.writeflashmtd Write one partition +flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Channellist +fontmenu.epg EPG +fontmenu.eventlist Eventlist +fontmenu.gamelist Gamelist +fontmenu.head Fontsize settings +fontmenu.infobar Infobar +fontsize.channel_num_zap direct selection +fontsize.channellist Channellist +fontsize.channellist_descr Description +fontsize.channellist_number Number +fontsize.epg_date EPG Date +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Title +fontsize.eventlist_datetime date / time +fontsize.eventlist_itemlarge large +fontsize.eventlist_itemsmall small +fontsize.eventlist_title Title +fontsize.filebrowser_item filebrowser item +fontsize.gamelist_itemlarge large +fontsize.gamelist_itemsmall small +fontsize.hint Initialising font,\nplease wait... +fontsize.infobar_channame Channel name +fontsize.infobar_info info +fontsize.infobar_number Number +fontsize.infobar_small small +fontsize.menu Menutext +fontsize.menu_info Menu Info +fontsize.menu_title Menu Title +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG not loaded.... +infoviewer.epgwait waiting for EPG... +infoviewer.eventlist Event-List +infoviewer.motor_moving Antenna positioning +infoviewer.next next +infoviewer.now now +infoviewer.nocurrent No info for current program available +infoviewer.noepg EPG not available +infoviewer.notavailable Channel not available +infoviewer.selecttime Timeselection +infoviewer.streaminfo Features +infoviewer.subchan_disp_pos Subchannel display +infoviewer.subservice Subservices +infoviewer.waittime Waiting for time... +ipsetup.hint_1 Use 0..9, or use Up/Down, +ipsetup.hint_2 OK saves, LAME! aborts +keybindingmenu.RC Key repeat-blocker +keybindingmenu.addrecord add record timer +keybindingmenu.addremind add zapto timer +keybindingmenu.allchannels_on_ok all-services +keybindingmenu.bouquetchannels_on_ok current bouquet +keybindingmenu.bouquetdown bouquet back +keybindingmenu.bouquethandling Bouquet-control +keybindingmenu.bouquetlist_on_ok bouquet-list +keybindingmenu.bouquetup next bouquet +keybindingmenu.cancel close channellist +keybindingmenu.channeldown channel down +keybindingmenu.channellist Channellist +keybindingmenu.channelup channel up +keybindingmenu.head Keybinding Setup +keybindingmenu.lastchannel Quick Zap +keybindingmenu.modechange Modechange +keybindingmenu.pagedown page down +keybindingmenu.pageup page up +keybindingmenu.quickzap Quickzap +keybindingmenu.repeatblock repeat delay +keybindingmenu.repeatblockgeneric generic delay +keybindingmenu.sort change sort order +keybindingmenu.subchanneldown subchannel down +keybindingmenu.subchannelup subchannel up +keybindingmenu.tvradiomode TV/Radio-mode +keybindingmenu.zaphistory Zapping History Bouquet +keychooser.head Setup new Key +keychooser.text1 Please press the new key +keychooser.text2 wait a few seconds for abort +keychoosermenu.currentkey current key +keychoosermenu.setnew setup new key +keychoosermenu.setnone no key +languagesetup.head Language Setup +languagesetup.select Language +lcdcontroler.brightness normal Brightness +lcdcontroler.brightnessstandby Standby Brightness +lcdcontroler.contrast Contrast +lcdmenu.autodimm Auto dimm +lcdmenu.head VFD Settings +lcdmenu.inverse Invert +lcdmenu.lcdcontroler Contrast / Brightness +lcdmenu.power Power +lcdmenu.statusline status line +lcdmenu.statusline.both volume / playtime +lcdmenu.statusline.playtime playtime +lcdmenu.statusline.volume volume +mainmenu.audioplayer Audioplayer +mainmenu.games Games +mainmenu.head Main Menu +mainmenu.movieplayer Movieplayer +mainmenu.pausesectionsd Read EPG +mainmenu.pictureviewer Picture viewer +mainmenu.radiomode Radio-Mode +mainmenu.reboot Reboot +mainmenu.recording Recording +mainmenu.recording_start start +mainmenu.recording_stop stop +mainmenu.scartmode Scart-Mode +mainmenu.scripts Scripts +mainmenu.service Service +mainmenu.settings Settings +mainmenu.shutdown Shutdown +mainmenu.sleeptimer SleepTimer +mainmenu.tvmode TV-Mode +mainsettings.audio Audio +mainsettings.osd OSD settings +mainsettings.head Settings +mainsettings.keybinding Key Setup +mainsettings.language Language +mainsettings.lcd VFD-Display +mainsettings.misc Misc settings +mainsettings.network Network +mainsettings.recording Recording +mainsettings.savesettingsnow save settings now +mainsettings.savesettingsnow_hint Saving settings,\nplease wait... +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back back +messagebox.back Back +messagebox.cancel Cancel +messagebox.discard Discard changes? +messagebox.error Error +messagebox.info Information +messagebox.no No +messagebox.yes Yes +miscsettings.bootinfo Show infos at start +miscsettings.bootmenu Show boot menu +miscsettings.channellist Channellist +miscsettings.channellist_epgtext_align Programtext Align +miscsettings.driver_boot driver and boot options +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general General +miscsettings.head Misc settings +miscsettings.hwsections use hardware sections +miscsettings.infobar_sat_display Satellite display on infobar +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +miscsettings.pmtupdate enable pmt update +miscsettings.shutdown_count switch off after +miscsettings.shutdown_count_hint1 time (in minutes) to switch from standby +miscsettings.shutdown_count_hint2 to deep standby (0 = off). +miscsettings.shutdown_real Enable standby +miscsettings.shutdown_real_rcdelay Delayed shutdown +miscsettings.sptsmode use spts mode +motorcontrol.head Satellite finder +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmarkname +movieplayer.bookmarkname_hint1 Enter a name for your new bookmark +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffering... +movieplayer.defdir start dir. +movieplayer.defplugin Start-Plugin +movieplayer.dvdplayback DVD +movieplayer.fileplayback File play +movieplayer.goto Jump to ... +movieplayer.goto.h1 = -> absolute jump +movieplayer.goto.h2 +,- -> relative jump +movieplayer.head Movieplayer +movieplayer.nostreamingserver The streaming server could not be reached. +movieplayer.pesplayback Play PES (Experimental) +movieplayer.pleasewait Please wait.\nConnecting to the streaming server... +movieplayer.toomanybookmarks There are too many bookmarks.\nYou need to delete one of them first. +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.tsplayback Play TS +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +movieplayer.wrongvlcversion This feature is not support by your current version of VLC +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway default gateway +networkmenu.head Network Settings +networkmenu.ipaddress IP address +networkmenu.mount NFS/CIFS/FTPFS +networkmenu.nameserver name server +networkmenu.netmask netmask +networkmenu.ntpenable Syncronisation via +networkmenu.ntprefresh NTP/DVB-Refresh +networkmenu.ntprefresh_hint1 NTP/DVB-Time-Sync in minutes +networkmenu.ntprefresh_hint2 need reboot or epg-reset +networkmenu.ntpserver NTP-Server +networkmenu.ntpserver_hint1 NTP-Server example: ntp1.ptb.de +networkmenu.ntpserver_hint2 need reboot or epg-reset +networkmenu.ntptitle Time-Syncronisation +networkmenu.setupnow setup network now +networkmenu.setuponstartup setup network on startup +networkmenu.show show active network settings +networkmenu.test test network now +nfs.alreadymounted directory already mounted +nfs.automount mount on startup +nfs.dir directory/share +nfs.ip Server IP +nfs.localdir local dir +nfs.mount Mount Network volume +nfs.mount_options mount options +nfs.mountok mount successful +nfs.mounterror mount error +nfs.mounterror_notsup filesystem type not supported +nfs.mountnow mount now +nfs.mounttimeout mount error: timeout +nfs.password password +nfs.remount remount directories +nfs.type type +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount Umount Network volume +nfs.umounterror error umounting volume +nfs.username username +nfsmenu.head NFS/CIFS/FTPFS settings +nvod.percentage (%d%% over) +nvod.starting (starting in %d min) +nvodselector.directormode Direct-Mode +nvodselector.head Select starting-time +nvodselector.subservice Select Subservice +options.default Reset to defaults +options.fb framebuffer +options.null null +options.off off +options.on on +options.serial serial +parentallock.changepin change PIN code +parentallock.changepin_hint1 Enter your new youth protection pin code here! +parentallock.changetolocked on locked bouquets +parentallock.head Enter Parental Lock PIN code +parentallock.lockage lock program +parentallock.lockage12 from 12 years up +parentallock.lockage16 from 16 years up +parentallock.lockage18 from 18 years up +parentallock.lockedchannel Locked sender... +parentallock.lockedprogram Locked program (from %d years up) +parentallock.never never +parentallock.onsignal on broadcasted lock +parentallock.parentallock Youth protection +parentallock.prompt prompt for PIN +pictureviewer.defdir start dir. +pictureviewer.head Picture viewer +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +pictureviewer.resize.color_average advanced +pictureviewer.resize.none none +pictureviewer.resize.simple simple +pictureviewer.scaling scaling +pictureviewer.show show +pictureviewer.slide_time slideshow display time +pictureviewer.slideshow slideshow +pictureviewer.sortorder change sort order +pictureviewer.sortorder.date (date) +pictureviewer.sortorder.filename (filename) +ping.ok is reachable (ping) +ping.protocol is unreachable (host or protocol error) +ping.socket is unreachable (socket error) +ping.unreachable is unreachable +pinprotection.head Enter PIN code +pinprotection.wrongcode PIN-Code was wrong! Try again. +plugins.result plugin output +rclock.lockmsg Your box remote control will be locked.\n To unlock it, press \n and on your remote control. +rclock.menueadd Lock RC +rclock.title Lock Remote Control +rclock.unlockmsg Remote control reactivated. +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.defdir recording directory +recordingmenu.tsdir timeshift directory +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.file direct (file) +recordingmenu.filesettings direct recording settings +recordingmenu.head Recording Settings +recordingmenu.help Recording devices:\n--------------------------\nserver:\nusing streaming software on a PC\n\n(analog) vcr:\nusing the vcr outlet\n\ndirect (file):\ndirectly into an NFS mounted directory\nor onto an internal hard drive\nTS: use spts mode(dbox2)\nPES: do not use spts mode(dbox2)\n\n\nMax. file size:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: almost unlimited (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart do not switch to scart mode +recordingmenu.off off +recordingmenu.recording_type recording device +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.server server +recordingmenu.server_ip recording server IP +recordingmenu.server_mac MAC address +recordingmenu.server_port recording server port +recordingmenu.server_wakeup recording server WOL +recordingmenu.setupnow activate changes +recordingmenu.splitsize Max. file size (MB) +recordingmenu.stopplayback stop playback +recordingmenu.stopsectionsd stop sectionsd +recordingmenu.stream_vtxt_pid record videotext +recordingmenu.use_fdatasync write synchronous (fdatasync) +recordingmenu.use_o_sync write synchronous (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce Recording starts in a few minutes +repeatblocker.hint_1 Shortest time (in ms) to recognize 2 keystrokes +repeatblocker.hint_2 Enter 0 to switch of the blocker (red is space) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-repeats +satsetup.extended DiSEqC-Settings +satsetup.extended_motor Motor-Settings +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manual Motor-Setup +satsetup.nodiseqc no DiSEqC +satsetup.satellite Satellite +satsetup.savesettingsnow save settings now +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Cable: +scants.actsatellite Satellite: +scants.bouquet Bouquet +scants.bouquet_create create new +scants.bouquet_erase erase all +scants.bouquet_leave leave current +scants.bouquet_satellite Satellite-Bouquet +scants.bouquet_update update +scants.channel Channel: +scants.failed Transponderscan failed! +scants.finished Transponderscan finished successfully! +scants.freqdata Frequency: +scants.head Scan transponder +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Total +scants.numberoftvservices TV +scants.provider Provider: +scants.startnow start scan +scants.test Test signal +scants.transponders Transponders: +scants.select_tp Select transponder +screensetup.lowerright green = setup lower right +screensetup.upperleft red = setup upper left +servicemenu.head Service +servicemenu.reload Reload channel lists +servicemenu.reload_hint Reloading channel lists,\nplease be patient. +servicemenu.scants Servicescan +servicemenu.ucodecheck Check ucodes +servicemenu.update Software Update +settings.help Help +settings.missingoptionsconffile The neutrino-settings have been updated.\nNew Options will be set to default. +settings.noconffile No neutrino-settings found.\nUsing defaults. +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +shutdowntimer.announce Box will shutdown in 1 min.\nCancel Sutdown ? +sleeptimerbox.announce Sleeptimer in 1 min +sleeptimerbox.hint1 Shutdown time in min. (000=off) +sleeptimerbox.hint2 The STB will shutdown after this time. +sleeptimerbox.title Sleeptimer +streamfeatures.head Features +streaminfo.aratio Aspect Ratio +streaminfo.aratio_unknown Aspect Ratio: unknown +streaminfo.audiotype Audiotype +streaminfo.audiotype_unknown Audiotype: unknown +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: unknown +streaminfo.head Stream-Information +streaminfo.not_available not available +streaminfo.resolution Resolution +streaminfo.signal Receipt signal +streaming.buffer_overflow The recording was aborted,\nsince the data could not be written fast enough. +streaming.busy One or several recording processes are active.\nIf you encounter this message and no recording is active, please restart Neutrino. +streaming.dir_not_writable The recording directory is not writable.\nRecording will not work. +streaming.success The recording has ended successfully. +streaming.write_error The recording was aborted,\nsince an error occured during the writing process. +streaming.write_error_open The recording was aborted,\nbecause the target file could not be opened. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Movieplayer Settings +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Off +streamingmenu.on On +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Port +streamingmenu.streaming_audiorate Datarate Audio +streamingmenu.streaming_force_avi_rawaudio Force AC3 for AVI +streamingmenu.streaming_force_transcode_video Transcode MPG/VCD video +streamingmenu.streaming_resolution Resolution +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Directory (VLC) +streamingmenu.streaming_transcode_audio Transcode audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG video codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datarate Video +streamingserver.noconnect No connection to streamingserver.\nRecording canceled. +stringinput.caps caps / no caps +stringinput.clear clear all +timer.eventrecord.msg ... TO be DONE, or not to be done +timer.eventrecord.title Schedule Record +timer.eventtimed.msg The event is scheduled.\nThe box will power on and \nswitch to this channel at the given time. +timer.eventtimed.title Schedule Event +timerbar.channelswitch Schedule +timerbar.recordevent Record +timerlist.alarmtime Alarm time +timerlist.apids Audio PIDs +timerlist.bouquetselect choose bouquet +timerlist.channel Channel +timerlist.channelselect choose channel +timerlist.delete Delete +timerlist.menumodify Modify timer +timerlist.menunew New timer +timerlist.message Message +timerlist.moderadio Radio channels +timerlist.modeselect Mode selection +timerlist.modetv TV channels +timerlist.modify Modify +timerlist.name Timer list +timerlist.new New timer +timerlist.plugin Plugin +timerlist.program.unknown Program unknown +timerlist.recording_dir recording directory +timerlist.reload Reload +timerlist.repeat Repeat +timerlist.repeat.biweekly biweekly +timerlist.repeat.byeventdescription see timer +timerlist.repeat.daily daily +timerlist.repeat.fourweekly fourweekly +timerlist.repeat.friday Fr +timerlist.repeat.monday Mo +timerlist.repeat.monthly monthly +timerlist.repeat.once once +timerlist.repeat.saturday Sa +timerlist.repeat.sunday Su +timerlist.repeat.thursday Th +timerlist.repeat.tuesday Tu +timerlist.repeat.unknown unknown +timerlist.repeat.wednesday We +timerlist.repeat.weekdays on weekdays +timerlist.repeat.weekly weekly +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.save Save timer +timerlist.standby SB mode +timerlist.standby.off Leave standby +timerlist.standby.on Enter standby +timerlist.stoptime Stop time +timerlist.type Timer typ +timerlist.type.execplugin Execute plugin +timerlist.type.nextprogram Next program +timerlist.type.record Record +timerlist.type.remind Reminder +timerlist.type.shutdown Shutdown +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Unknown +timerlist.type.zapto Zap to +timerlist.weekdays Days of the week +timerlist.weekdays.hint_1 Mo Tu We Th Fr Sa Su +timerlist.weekdays.hint_2 'X'=timer '-' no timer +timersettings.record_safety_time_after Record stop time correction +timersettings.record_safety_time_after.hint_1 Correction time in min. (00=off). This time +timersettings.record_safety_time_after.hint_2 will added to stop time of every record timer. +timersettings.record_safety_time_before Record start time correction +timersettings.record_safety_time_before.hint_1 Correction time in min. (00=off). This time +timersettings.record_safety_time_before.hint_2 will be deducted of every record timer. +timersettings.separator Timer settings +timing.chanlist Channellist +timing.epg Epg +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Time in sec. After this time the +timing.hint_2 infobar will be faded out. +timing.infobar Infobar +timing.menu Menu +timing.numericzap Numeric Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode check +ucodecheck.ucode UCode +ucodes.failure ATTENTION, µCodes could NOT be found!\n\nPlease upload them via FTP (or DBox-BootManager),\nthen restart your box! +videomenu.csync sync correction +videomenu.head Video Settings +videomenu.rgb_centering RGB centering +videomenu.screensetup Screen Setup +videomenu.vcrswitch Switch Scart automatically +videomenu.videoformat Format +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 +videomenu.videoformat_149 14:9 +videomenu.videoformat_autodetect autodetect +videomenu.43mode 4:3 Content mode +videomenu.panscan Pan&Scan +videomenu.panscan2 14:9 Pan&Scan +videomenu.letterbox Letterbox +videomenu.fullscreen Full screen +videomenu.auto Auto +videomenu.analog_mode Analog Video Output +videomenu.analog_sd_rgb_scart RGB on scart (SD) +videomenu.analog_sd_rgb_cinch RGB on cinch (SD) +videomenu.analog_sd_yprpb_scart YPbPr on scart (SD) +videomenu.analog_sd_yprpb_cinch YPbPr on cinch (SD) +videomenu.analog_hd_rgb_scart RGB on scart (HD) +videomenu.analog_hd_rgb_cinch RGB on cinch (HD) +videomenu.analog_hd_yprpb_scart YPbPr on scart (HD) +videomenu.analog_hd_yprpb_cinch YPbPr on cinch (HD) +videomenu.scart Scart +videomenu.cinch Cinch +videomenu.dbdr MPEG2 de-block/de-ring +videomenu.dbdr_none none +videomenu.dbdr_deblock de-block +videomenu.dbdr_both de-block+de-ring +videomenu.enabled_modes VF key enabled modes +zaptotimer.announce Zapto timer in one minute +extra.english English +extra.dboxinfo Box Info +extra.scan_mode Scan mode +extra.start_tostandby Startup to standby +extra.rotor_swap Swap rotor east/west +extra.use_log Log to /tmp/log +extra.spts_mode SPTS mode +extra.hw_sect Hardware Sections +extra.logo Logo number +extra.scan_full Full +extra.scan_fast Fast +hdd_slow Slow +hdd_middle Middle +hdd_fast Fast +hdd_ext3 Ext3fs +hdd_reiser Reiserfs +hdd_1min 1 min. +hdd_5min 5 min. +hdd_10min 10 min. +hdd_20min 20 min. +hdd_30min 30 min. +hdd_60min 60 min. +hdd_sleep Sleep time +hdd_noise Noise +hdd_activate Activate settings +hdd_fs Filesystem +hdd_format Format HDD +hdd_check Check filesystem +hdd_settings HDD Settings +hdd_format_warn Are you sure to format ? You will lost all data ! +hdd_umount_warn HDD unmount failed ! +hdd_check_failed HDD check failed ! +hdd_format_failed HDD format failed ! +hdd_manage Manage +hdd_not_found No HDD found +extra.clear_log Clear Log +extra.zap_cycle Zap cycle +extra.sms_channel sms-mode channel +extra.manual_scan Manual scan +extra.tp_freq Frequency +extra.tp_rate Symbol rate +extra.tp_pol Polarization +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Timezone +extra.zapit_menu Zapit options +extra.zapit_make_bouquet Make Remaining Channels list +extra.zapit_save_last_chan Save last channel +extra.zapit_motor_speed Motor moving speed (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet Add to bouquet +extra.key_list_start home +extra.key_list_end end +extra.chadded The current channel has been added to selected bouquet....\n +extra.chalreadyinbq The current channel is already in selected bouquet....\n +extra.menu_left_exit "Left" = menu back +extra.zapit_write_names Write channel names +extra.update_dir Directory for updates +extra.zapit_fast_zap fast zap +extra.zapit_sort_names Sort ch. by name +extra.zapit_backup Backup channels to /tmp +extra.zapit_restore Restore channels from /tmp +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Abortion of channel scan +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Digital video mode +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.epg_head Epg settings +miscsettings.epg_cache EPG-Cache (Days) +miscsettings.epg_cache_hint1 How long will EPG-Data in the future cached? +miscsettings.epg_cache_hint2 Set in days. +miscsettings.epg_dir EPG save path +miscsettings.epg_extendedcache EPG Long Description (hours) +miscsettings.epg_extendedcache_hint1 How long into the future will EPG extended descriptions +miscsettings.epg_extendedcache_hint2 be Cached? (Set in hours) +miscsettings.epg_max_events Max. Events +miscsettings.epg_max_events_hint1 How many events should be stored? +miscsettings.epg_max_events_hint2 normaly 6000, 0 to disable limit +miscsettings.epg_old_events EPG remove after (std.) +miscsettings.epg_old_events_hint1 How long will EPG-Data be stored after they timed out? +miscsettings.epg_old_events_hint2 Set in hours +miscsettings.epg_save Save/Restore epg on reboot +miscsettings.virtual_zap_mode Virtual zap +shutdown.recoding_query You really want to to stop record ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart Video +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) +audioplayer.spectrum lcd a-spectrum +extra.temp_timeshift Temporary timeshift +AUDIOSelectMenue.head Audio Selection +infoviewer.languages Audio +mainmenu.clearsectionsd Clear EPG Cache +usermenu.button_blue User menu blue +usermenu.button_green User menu green +usermenu.button_red User menu red +usermenu.button_yellow User menu yellow +usermenu.head User menu +usermenu.item_bar ---- Boarder ---- +usermenu.item_epg_misc EPG functions +usermenu.item_none Nothing +usermenu.item_vtxt Videotext +usermenu.name Name +extra.fec_s2_qpsk_1_2 1/2 s2 qpsk +extra.fec_s2_qpsk_2_3 2/3 s2 qpsk +extra.fec_s2_qpsk_3_4 3/4 s2 qpsk +extra.fec_s2_qpsk_5_6 5/6 s2 qpsk +extra.fec_s2_qpsk_7_8 7/8 s2 qpsk +extra.fec_s2_qpsk_8_9 8/9 s2 qpsk +extra.fec_s2_qpsk_3_5 3/5 s2 qpsk +extra.fec_s2_qpsk_4_5 4/5 s2 qpsk +extra.fec_s2_qpsk_9_10 9/10 s2 qpsk +extra.fec_s2_8psk_1_2 1/2 s2 8psk +extra.fec_s2_8psk_2_3 3/2 s2 8psk +extra.fec_s2_8psk_3_4 3/4 s2 8psk +extra.fec_s2_8psk_5_6 5/6 s2 8psk +extra.fec_s2_8psk_7_8 7/8 s2 8psk +extra.fec_s2_8psk_8_9 8/9 s2 8psk +extra.fec_s2_8psk_3_5 3/5 s2 8psk +extra.fec_s2_8psk_4_5 4/5 s2 8psk +extra.fec_s2_8psk_9_10 9/10 s2 8psk +channellist.edit Edit +channellist.extended Extended channel list +channellist.favs Favorites +channellist.provs Providers +channellist.sats Satellites +channellist.history History +channellist.current_tp Current transponder +channellist.make_hdlist Create list of HD channels +video_mode_ok Is this video mode working ok ? +moviebrowser.edit_book_name_info2 book name info2 +satsetup.sat_setup Setup satellites input / LNB +satsetup.do_scan Include in all scan +satsetup.lofl LNB Low Offset +satsetup.lofh LNB High Offset +satsetup.lofs LNB switch Offset +satsetup.diseqc_input Diseqc input +satsetup.uncomm_input Uncommited input +satsetup.motor_pos Rotor position +satsetup.auto_scan Auto-Scan selected Satellite +satsetup.auto_scan_all Auto-Scan multiple Satellites +satsetup.manual_scan Manual frequency scan +satsetup.use_nit Use NIT +satsetup.diseqc_advanced Advanced +satsetup.diseqc_order Input order +satsetup.diseqc_com_uncom Commited/Uncommited +satsetup.diseqc_uncom_com Uncommited/Commited +satsetup.comm_input Commited input +satsetup.use_usals Use usals +satsetup.usals_repeat USALS command repeat +motorcontrol.user_menu User menu +motorcontrol.install_menu Install menu +motorcontrol.step_west Step/Drive Motor West (b,c) +motorcontrol.step_east Step/Drive Motor East (b,c) +motorcontrol.halt Halt Motor +motorcontrol.west_limit Set West (soft) Limit +motorcontrol.east_limit Set East (soft) Limit +motorcontrol.enable_limit Enable (soft) Limits +motorcontrol.disable_limit Disable (soft) Limits +motorcontrol.ref_position Goto Reference Position +motorcontrol.calc_positions (Re)-Calculate Positions +motorcontrol.pos_increase Increase Motor Position (a) +motorcontrol.pos_decrease Decrease Motor Position (a) +motorcontrol.step_drive Switch Step/Drive Mode (b) +motorcontrol.store Store Motor Position (a) +motorcontrol.step_increase Increase Step Size (c) +motorcontrol.step_decrease Decrease Step Size (c) +motorcontrol.goto Goto Motor Position (a) +motorcontrol.notdef Not used +motorcontrol.motor_pos (a) Motor Position: +motorcontrol.settings Motor Control Settings +motorcontrol.movement (b) Movement: +motorcontrol.step_mode Step Mode +motorcontrol.drive_mode Drive Mode +motorcontrol.drive_mode_auto Drive Mode/auto stop +motorcontrol.timed_mode Timed Step Mode +motorcontrol.step_size (c) Step Size: +motorcontrol.stop_moving stop on good signal(moving) +motorcontrol.stop_stopped stop on good signal(stopped) +motorcontrol.no_mode don't care +motorcontrol.msec milliseconds +motorcontrol.status Status +motorcontrol.sat_pos Satellite Position (Step Mode): +mainsettings.timezone Timezone +scrambled_channel Scrambled channel +neutrino_starting Neutrino starting... +reset_channels Delete all channels +reset_settings Reset settings to defaults +reset_all Factory reset +reset_confirm Are you sure ? +cam.settings CI Cam +cam.empty No CAM in slot +cam.inserted CAM inserted in slot +cam.removed CAM removed from slot +cam.init_ok CAM init complete +cam.waiting Waiting for CI answer +cam.timeout Timeout waiting CI menu ready +subtitles.head Subtitles +subtitles.stop Stop subtitles +favorites.copy Copy bouquet to Favorites +fan_speed CPU Fan speed +audio.srs_iq SRS TruVolume algo +audio.srs_algo Type +audio.srs_algo_light Light +audio.srs_algo_normal Normal +audio.srs_volume Reference volume +audio.srs_nmgr Noise manager +cpu.freq Cpu frequency +cpu.freq_normal Normal frequency +cpu.freq_standby Standby frequency +cpu.freq_default default frequency +audioplayer.add_ic Icecast +audioplayer.add_loc Local radio list +audioplayer.add_sc Shoutcast +audioplayer.load_radio_stations Load internet radios +audioplayer.receiving_list Receiving list, please wait +inetradio.name Internetradio +upnpbrowser.head UPnP Browser +upnpbrowser.noservers No UPnP servers found +upnpbrowser.rescan Scan again +upnpbrowser.scanning Scanning for UPnP servers diff --git a/data/locale/francais.locale b/data/locale/francais.locale new file mode 100644 index 000000000..4e10ecbad --- /dev/null +++ b/data/locale/francais.locale @@ -0,0 +1,807 @@ +EPGMenu.epgplus Visualiasation liste évènements +EPGMenu.eventinfo Détails programme en cours +EPGMenu.eventlist Liste évènements programme en cours +EPGMenu.head EPG - Information du programme +EPGMenu.streaminfo Informations techniques +EPGPlus.actions Actions +EPGPlus.bybouquet_mode Par bouquet +EPGPlus.bypage_mode par Page +EPGPlus.change_font_size Taille caractère +EPGPlus.change_font_style Taille style +EPGPlus.change_size Taille +EPGPlus.channelentry_font caractère chaine +EPGPlus.channelentry_separationlineheight Hauteur ligne de séparation +EPGPlus.channelentry_width Largeur +EPGPlus.channelevententry_font Caractère événement +EPGPlus.edit_fonts edit Caractères +EPGPlus.edit_sizes edit Tailles +EPGPlus.font_style_bold style Gras +EPGPlus.font_style_italic style Italic +EPGPlus.font_style_regular style Régulier +EPGPlus.footer_fontbouquetchannelname Bas de page bouquet +EPGPlus.footer_fontbuttons Bas de page boutons +EPGPlus.footer_fonteventdescription Bas de page évènement +EPGPlus.footer_fonteventshortdescription Bas de page évènement réduit +EPGPlus.head Eventlist Présentation (EPG Plus) +EPGPlus.header_font Caractère Entête +EPGPlus.horgap1_height Hauteur1 +EPGPlus.horgap2_height Hauteur2 +EPGPlus.next_bouquet Bouquet suivant +EPGPlus.options Options +EPGPlus.page_down Page vers le bas +EPGPlus.page_up Page vers le haut +EPGPlus.prev_bouquet Bouquet précedent +EPGPlus.record Enregistrer +EPGPlus.refresh_epg Rafraîchir +EPGPlus.remind Rappel +EPGPlus.reset_settings Reset réglages +EPGPlus.save_settings Sauvegarder Réglages +EPGPlus.scroll_mode Mode défilement +EPGPlus.select_font_name Nom de caractère +EPGPlus.settings Réglages +EPGPlus.slider_width Largeur barette +EPGPlus.stretch_mode Mode déployé +EPGPlus.swap_mode Mode échange +EPGPlus.timeline_fontdate Caractère de Date +EPGPlus.timeline_fonttime Caractère d'heure +EPGPlus.vergap1_width Largeur1 +EPGPlus.vergap2_width Largeur2 +EPGPlus.view_mode Mode visualisation +GENRE.ARTS.0 arts / culture +GENRE.ARTS.1 arts contemporains +GENRE.ARTS.10 arts/culture magazines +GENRE.ARTS.11 mode +GENRE.ARTS.2 arts fins +GENRE.ARTS.3 religions +GENRE.ARTS.4 culture populaire/arts traditionels +GENRE.ARTS.5 littérature +GENRE.ARTS.6 film/cinéma +GENRE.ARTS.7 film expérimental/vidéo +GENRE.ARTS.8 broadcasting/press +GENRE.ARTS.9 nouvelle média +GENRE.CHILDRENs_PROGRAMMES.0 enfants / programme juvénile +GENRE.CHILDRENs_PROGRAMMES.1 programme pré-scolaire +GENRE.CHILDRENs_PROGRAMMES.2 programme de loisirs pour 6 à 14 +GENRE.CHILDRENs_PROGRAMMES.3 programme de loisirs pour 10 à 16 +GENRE.CHILDRENs_PROGRAMMES.4 programme information/éducation/école +GENRE.CHILDRENs_PROGRAMMES.5 déssins animés/marionettes +GENRE.DOCUS_MAGAZINES.0 documentaire / magazine +GENRE.DOCUS_MAGAZINES.1 nature/animaux/environnement +GENRE.DOCUS_MAGAZINES.2 sciences technologie/nature +GENRE.DOCUS_MAGAZINES.3 médecine/physiologie/psychologie +GENRE.DOCUS_MAGAZINES.4 Pays étrangers/expéditions +GENRE.DOCUS_MAGAZINES.5 Sciences sociales/spirituelles +GENRE.DOCUS_MAGAZINES.6 Aurtes éducations +GENRE.DOCUS_MAGAZINES.7 languages +GENRE.MOVIE.0 films/drames +GENRE.MOVIE.1 détective/Policier +GENRE.MOVIE.2 aventure/western/guerre +GENRE.MOVIE.3 science-fiction/fantaisie/horreur +GENRE.MOVIE.4 comédie +GENRE.MOVIE.5 opéra/mélodrame/folklore +GENRE.MOVIE.6 romantique +GENRE.MOVIE.7 serieux/classique/réligieux/film historique/drame +GENRE.MOVIE.8 Film adulte/drame +GENRE.MUSIC_DANCE.0 musique / ballet / dance +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 musique sérieuse/classique +GENRE.MUSIC_DANCE.3 folk/musique traditionelle +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musique/opéra +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 informations +GENRE.NEWS.1 info/météo +GENRE.NEWS.2 magazines d'info +GENRE.NEWS.3 documentaire +GENRE.NEWS.4 discussion/interview/débat +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 game show/quiz/concours +GENRE.SHOW.2 varieté show +GENRE.SHOW.3 discours show +GENRE.SOCIAL_POLITICAL.0 évènements socials & politiques / affaires +GENRE.SOCIAL_POLITICAL.1 magazines/reportages/documentaires +GENRE.SOCIAL_POLITICAL.2 conseil économique/social +GENRE.SOCIAL_POLITICAL.3 peuple remarquable +GENRE.SPORTS.0 sports +GENRE.SPORTS.1 évenemnts spéciaux (Jeux Olympiques,Coupe du Monde etc.) +GENRE.SPORTS.10 équitation +GENRE.SPORTS.11 sports martiaux +GENRE.SPORTS.2 magazines de sports +GENRE.SPORTS.3 football +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 sports d'équipe (hors football) +GENRE.SPORTS.6 athlétisme +GENRE.SPORTS.7 sports de moteurs +GENRE.SPORTS.8 sports acquatiques +GENRE.SPORTS.9 sports d'hiver +GENRE.TRAVEL_HOBBIES.0 voyage et détente +GENRE.TRAVEL_HOBBIES.1 tourisme/voyage +GENRE.TRAVEL_HOBBIES.2 artisanet +GENRE.TRAVEL_HOBBIES.3 automobilisme +GENRE.TRAVEL_HOBBIES.4 fitness & santé +GENRE.TRAVEL_HOBBIES.5 cuisine +GENRE.TRAVEL_HOBBIES.6 publicité/shopping +GENRE.TRAVEL_HOBBIES.7 jardinage +GENRE.UNKNOWN inconnu +apids.hint_1 Entrer APIDs à streamer +apids.hint_2 en notation hex séparé par ' ' +apidselector.head Sélectionner language +audiomenu.PCMOffset diminuer le Volume PCM +audiomenu.analogout sortie Analogique +audiomenu.avs avs +audiomenu.avs_control Contrôle de Volume avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Réglages Audio +audiomenu.lirc lirc +audiomenu.monoleft mono gauche +audiomenu.monoright mono droite +audiomenu.ost ost +audiomenu.stereo stéreo +audioplayer.add Ajouter +audioplayer.artist_title Artiste, Titre +audioplayer.button_select_title_by_id chercher par ID +audioplayer.button_select_title_by_name charcher par nom +audioplayer.defdir dir. de Départ +audioplayer.delete éffacer +audioplayer.deleteall tout éffacer +audioplayer.display_order afficher ordre +audioplayer.fastforward avance rapide. +audioplayer.follow selection autode l'encours +audioplayer.head liste Liste Audio +audioplayer.highprio Pririté de décodage élevé +audioplayer.id3scan étiquette ID3 de Scanning +audioplayer.jump_backwards Saut à l'arrière +audioplayer.jump_dialog_hint1 Entrer cible pour saut SVP +audioplayer.jump_dialog_hint2 (relative, en secondes) +audioplayer.jump_dialog_title Entrer cible pour saut +audioplayer.jump_forwards Saut en avant +audioplayer.keylevel Niveau de clé +audioplayer.name Audioplayer +audioplayer.pause pause +audioplayer.play écouter +audioplayer.playing Piste en cours +audioplayer.playlist_fileerror_msg Fichier non crée: +audioplayer.playlist_fileerror_title Erreur +audioplayer.playlist_fileoverwrite_msg Voulez-vous écraser l'ancien fichier: +audioplayer.playlist_fileoverwrite_title écraser? +audioplayer.playlist_name Nom de fichier de la liste +audioplayer.playlist_name_hint1 Entrer le nom de fichier de la liste SVP +audioplayer.playlist_name_hint2 l'extension .m3u sera ajouté automatiquement +audioplayer.repeat_on Mode répétition activé +audioplayer.rewind rebibiner +audioplayer.save_playlist sauvegarder la liste +audioplayer.screensaver_timeout Ecran de sauvegarde timeout (min, 0=off) +audioplayer.select_title_by_name Chercher titre par nom (SMS) +audioplayer.show_playlist Montrer la liste +audioplayer.shuffle Pas +audioplayer.stop Stop +audioplayer.title_artist Titre, Artiste +audioplayerpicsettings.general Lecteur audio/Visualiseur de Photos +bookmarkmanager.delete Effacer +bookmarkmanager.name Marquer +bookmarkmanager.rename renommer +bookmarkmanager.select sélectionner +bouqueteditor.add Ajouter +bouqueteditor.bouquetname Nom de bouquets +bouqueteditor.delete Effacer +bouqueteditor.discardingchanges Annulation des changement. Patienter SVP... +bouqueteditor.hide Cacher +bouqueteditor.lock Bloquer +bouqueteditor.move Dépalcer +bouqueteditor.name Editeur de bouquet +bouqueteditor.newbouquetname Nouveau nom de bouquets +bouqueteditor.rename Renommer +bouqueteditor.return Prêt +bouqueteditor.savechanges? Voulez-vous sauvegarder les changements? +bouqueteditor.savingchanges Sauvegarde des changements. Pateientez SVP... +bouqueteditor.switch ajout/suppression +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider Fournisseur de service +channellist.head tout les services +channellist.nonefound Aucune chaines trouvée!\néxecuter un scan\n(DBOX-key -> service) +channellist.since depuis %02d:%02d +colorchooser.alpha alpha +colorchooser.blue bleu +colorchooser.green vert +colorchooser.red rouge +colormenu.background Arrière plan +colormenu.background_head Arrière plan +colormenu.fade Fade menus +colormenu.font Font taille +colormenu.gtx_alpha Transparence (GTX) +colormenu.head Color Régalges +colormenu.menucolors Couleur de Menu +colormenu.statusbar Barre d'info +colormenu.textcolor Couleur du texte +colormenu.textcolor_head Couleur de texte +colormenu.themeselect sélectionner le thème +colormenu.timing Temporisation OSD +colormenusetup.head Couleurs du Menu +colormenusetup.menucontent Contenu Fenêtre +colormenusetup.menucontent_inactive Contenue Fenêtre inactive +colormenusetup.menucontent_selected Contenue Fenêtre sélectinnée +colormenusetup.menuhead Menu entête +colorstatusbar.head Barre d'info +colorstatusbar.text Barre d'info +colorthememenu.classic_theme Thème Classique +colorthememenu.dblue_theme Thème bleu foncé +colorthememenu.dvb2k_theme Thème DVB2000 +colorthememenu.head Sélecteur de thème +colorthememenu.neutrino_theme Thème Neutrino +date.Apr Avr +date.Aug Aoû +date.Dec Déc +date.Feb Fév +date.Fri Ven +date.Jan Jan +date.Jul Jul +date.Jun Jui +date.Mar Mar +date.May Mai +date.Mon Lun +date.Nov Nov +date.Oct Oct +date.Sat Sam +date.Sep Sép +date.Sun Dim +date.Thu Jeu +date.Tue Mar +date.Wed Mer +epglist.head Liste Evenement - %s +epglist.noevents Pas d'EPG disponible... +epgviewer.More_Screenings Plus de diffusion sur cette chaine +epgviewer.nodetailed Pas d'informations détaillée disponible +epgviewer.notfound Pas d'EPG trouvé +eventlistbar.channelswitch Agenda +eventlistbar.eventsort Classement +eventlistbar.recordevent Enregistrement +favorites.addchannel La chaine en cours sera ajoutée \nau bouquet "Mes Favories". \n ça prendra quelques secondes... +favorites.bouquetname Mes Favories +favorites.bqcreated Le Bouquet "Mes Favories" est crée...\n +favorites.chadded La chaine en cours est ajoutée à vos favories...\n +favorites.chalreadyinbq La chaine en cours est déja dans vos favories...\n +favorites.finalhint \nUtilise l'éditeur de bouquet pour modifier vos favories.\n +favorites.menueadd Ajouter une chaine aux favories +favorites.nobouquets Les Favories sont disponibles les bouquets activés uniquement. +filebrowser.delete Effacer +filebrowser.denydirectoryleave Répertoire de démarrage réel +filebrowser.dodelete1 Effacer +filebrowser.dodelete2 ? +filebrowser.filter.active Filtre on +filebrowser.filter.inactive Filtre off +filebrowser.head Navigateur de fichiers +filebrowser.mark Marquer +filebrowser.nextpage Page suivante +filebrowser.prevpage Page précedente +filebrowser.scan Balayage de réprtoire +filebrowser.select Selectionner +filebrowser.showrights Montrer les attribues de fichier +filebrowser.sort.date (date) +filebrowser.sort.name (Nom de fichier) +filebrowser.sort.namedirsfirst (Nom de fichier2) +filebrowser.sort.size (Taille) +filebrowser.sort.type (type) +flashupdate.actionreadflash Lecture +flashupdate.cantopenfile Ouverture de fichier impossible +flashupdate.cantopenmtd Ouverture de mtd-device impossible +flashupdate.checkupdate Recherche de nouvelle version +flashupdate.currentreleasecycle Cycle de sortie +flashupdate.currentversion_sep Version courante +flashupdate.currentversiondate Date +flashupdate.currentversionsnapshot Type d'Image +flashupdate.currentversiontime heure +flashupdate.erasefailed Effacement du flash échoué +flashupdate.erasing Effacement flash +flashupdate.experimentalimage L'Image sélectionnée est une version non testée, Ca veut dire \nvotre récepteur ne plus redémarrer après la mise à jour.\n\nvoulez-vous vraiment mettre à jour avec cette version? +flashupdate.expertfunctions Fonctions Expert +flashupdate.fileis0bytes La taille du fichier est 0 Bytes +flashupdate.fileselector Sélecteur de fichier +flashupdate.flashreadyreboot L'Image est flashée avec succès.\nLe récepteur va redémarrer maintenant. +flashupdate.getinfofile Obtention de l'info version +flashupdate.getinfofileerror Impossible d'extraire l'info version +flashupdate.getupdatefile Obtention de la m.à.j (mise à jour) +flashupdate.getupdatefileerror Impossible d'extraire la m.à.j +flashupdate.globalprogress Progression globale: +flashupdate.head m.à.j du Software (Mise à jour du programme) +flashupdate.md5check Vérification de l'Image +flashupdate.md5sumerror L'Image a des erreurs +flashupdate.msgbox La nouvelle image suivante est trouvée:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nVoulez-vous télécharger et installer cette version maintenant? +flashupdate.msgbox_manual La nouvelle image suivante est trouvée:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nVoulez-vous télécharger et installer cette version maintenant? +flashupdate.mtdselector Sélecteur de partition +flashupdate.programmingflash programmation du flash +flashupdate.proxypassword Mot de passe +flashupdate.proxypassword_hint1 entrer le mot de pass du serveur proxy +flashupdate.proxypassword_hint2 Entrée vide veut dire pas de proxy-auth +flashupdate.proxyserver Nom de l'hôte +flashupdate.proxyserver_hint1 entrer le nom du serveur proxy ou bien ip, utiliser hôte:port +flashupdate.proxyserver_hint2 Entrée vide veut dire pas de proxy +flashupdate.proxyserver_sep Serveur Proxy +flashupdate.proxyusername Nom d'utilisateur +flashupdate.proxyusername_hint1 entrer le nom d'utilisateur du serveur proxy +flashupdate.proxyusername_hint2 Entrée vide veut dire pas de proxy-auth +flashupdate.readflash Lire l'image entièrement +flashupdate.readflashmtd Lire une partition +flashupdate.ready Prêt +flashupdate.reallyflashmtd Vous voulez vraiment flasher?\n\nSi une erreur survient ou l'Image est non\nvalide, le récepteur ne redémarrera pas après le flash.\n\nImagename: %s\nTarget: %s +flashupdate.savesuccess L'Image est sauvegardée avec succès \nsous %s. +flashupdate.selectimage Images disponibles +flashupdate.squashfs.noversion La vérification de la version SquashFS version ne sont supportée que lors de la m.à.j via le WEB actuellement.\nVoulez-vous vraiment installer cette Image? +flashupdate.titlereadflash Lecture du Flash +flashupdate.titlewriteflash Ecriture du Flash +flashupdate.updatemode Mode de m.à.j +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuel (ftp) +flashupdate.url_file Fichier de configuration +flashupdate.versioncheck Vérification de la version +flashupdate.writeflash Ecrire l'Image entièrement +flashupdate.writeflashmtd Ecrire une partition +flashupdate.wrongbase Votre cycle de sortie est différent, Incapable de mettre à jour\nsans avoir installé la version convenable! +fontmenu.channellist Liste de chaines +fontmenu.epg EPG +fontmenu.eventlist Liste d'évènements +fontmenu.gamelist Liste de jeux +fontmenu.head Réglage taille caractère +fontmenu.infobar Barre d'Info +fontsize.channel_num_zap Sélection directe +fontsize.channellist Liste de chaines +fontsize.channellist_descr Déscription +fontsize.channellist_number Numéro +fontsize.epg_date EPG Date +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titre +fontsize.eventlist_datetime date / heure +fontsize.eventlist_itemlarge large +fontsize.eventlist_itemsmall petit +fontsize.eventlist_title Titre +fontsize.filebrowser_item Article du navigateur de fichier +fontsize.gamelist_itemlarge large +fontsize.gamelist_itemsmall petit +fontsize.hint Initialisation caractère,\nattendre SVP... +fontsize.infobar_channame Nom de chaine +fontsize.infobar_info info +fontsize.infobar_number Numéro +fontsize.infobar_small petit +fontsize.menu Menu texte +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titre +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG non chargé.... +infoviewer.epgwait attente EPG... +infoviewer.eventlist Liste d'évènements +infoviewer.languages Audio +infoviewer.motor_moving positionnement de l'antenne +infoviewer.nocurrent Pas d'info pour le programme en cours +infoviewer.noepg Pas d'EPG disponible +infoviewer.notavailable Chaine non disponible +infoviewer.selecttime Sélection heure +infoviewer.streaminfo Options de caractéristiques +infoviewer.subservice Sous services +infoviewer.waittime Attented d'heure... +ipsetup.hint_1 Utilise 0..9, ou bien haut/bas, +ipsetup.hint_2 OK sauvegader, LAME! annuler +keybindingmenu.RC Touche repeat-blocker +keybindingmenu.addrecord Ajouter horaire d'enregistrement +keybindingmenu.addrecord_head Ajouter horaire d'enregistrement +keybindingmenu.addremind Ajouter horaire de zap vers... +keybindingmenu.addremind_head Ajouter horaire de zap vers... +keybindingmenu.allchannels_on_ok tous-services +keybindingmenu.bouquetchannels_on_ok bouquet en cours +keybindingmenu.bouquetdown bouquet arrière +keybindingmenu.bouquetdown_head Choix touche bouquet arrière +keybindingmenu.bouquethandling Contrôle de Bouquet +keybindingmenu.bouquetlist_on_ok Liste de bouquets +keybindingmenu.bouquetup next bouquet +keybindingmenu.bouquetup_head Choix touche bouquet avant +keybindingmenu.cancel Fermer liste de chaines +keybindingmenu.cancel_head Fermer liste de chaines +keybindingmenu.channeldown chaine bas +keybindingmenu.channeldown_head Choix touche chaine arrière +keybindingmenu.channellist Liste de chaines +keybindingmenu.channelup chaine avant +keybindingmenu.channelup_head Choix touche chaine avant +keybindingmenu.head Réglage des touches +keybindingmenu.lastchannel Zap rapide +keybindingmenu.lastchannel_head Zap rapide +keybindingmenu.modechange Mode changement +keybindingmenu.pagedown page bas +keybindingmenu.pagedown_head Choix touche page bas +keybindingmenu.pageup page haut +keybindingmenu.pageup_head Choix touche page haut +keybindingmenu.quickzap zap rapide +keybindingmenu.repeatblock délai de répétition +keybindingmenu.repeatblockgeneric délai générique +keybindingmenu.sort changer l'ordre de classement +keybindingmenu.sort_head changer l'ordre de classement +keybindingmenu.subchanneldown sous-chaine bas +keybindingmenu.subchanneldown_head touche sous-chaine bas +keybindingmenu.subchannelup sous-chaine haut +keybindingmenu.subchannelup_head touche sous-chaine haut +keybindingmenu.tvradiomode Mode TV/Radio +keybindingmenu.tvradiomode_head Mode TV/Radio +keybindingmenu.zaphistory Zapping Bouquet Historique +keybindingmenu.zaphistory_head Zapping Bouquet historique +keychooser.head Setup nouvelle touche +keychooser.text1 Appuyer sur la nouvelle touche SVP +keychooser.text2 Attendez quelques secondes pour quitter +keychoosermenu.currentkey touche en cours +keychoosermenu.setnew réglage nouvelle touche +keychoosermenu.setnone Pas de touche +languagesetup.head Réglage Langue +languagesetup.select Langue +lcdcontroler.brightness Brillance normale +lcdcontroler.brightnessstandby Brillance de veille +lcdcontroler.contrast Contraste +lcdcontroler.head Réglage LCD +lcdmenu.autodimm Estompage Auto +lcdmenu.head LCD Réglages +lcdmenu.inverse Inverse +lcdmenu.lcdcontroler Contraste / Brillance +lcdmenu.power Alimentation +lcdmenu.statusline Ligne d'état +lcdmenu.statusline.both Volume / temps écoulé +lcdmenu.statusline.playtime Temps écoulé +lcdmenu.statusline.volume Volume +mainmenu.audioplayer Lecteur Audio +mainmenu.games Jeux +mainmenu.head MENU PRINCIPAL +mainmenu.movieplayer Lecteur Vidéo +mainmenu.pausesectionsd Lire EPG +mainmenu.pictureviewer Visionneur de photos +mainmenu.radiomode Mode Radio +mainmenu.recording Enregistrement +mainmenu.recording_start Début +mainmenu.recording_stop stop +mainmenu.scartmode Mode Péritel +mainmenu.service Service +mainmenu.settings Réglages +mainmenu.shutdown Eteindre +mainmenu.sleeptimer Veille +mainmenu.tvmode Mode TV +mainsettings.audio Audio +mainsettings.colors Coleurs / thèmes / Caractères +mainsettings.head Réglages +mainsettings.keybinding Réglage touche +mainsettings.language Langue +mainsettings.lcd Ecran à Cristaux Liquides (LCD) +mainsettings.misc Réglage divers +mainsettings.network Réseau +mainsettings.recording Enregistrement +mainsettings.savesettingsnow Sauvegarder réglage maintenant +mainsettings.savesettingsnow_hint Sauvegarde de réglage,\nPatientez SVP... +mainsettings.streaming Lecteur vidéo +mainsettings.video Vidéo +menu.back Retour +messagebox.back Retour +messagebox.cancel Annuler +messagebox.discard Annuler les changemnts? +messagebox.error Erreur +messagebox.info Information +messagebox.no Non +messagebox.yes Oui +miscsettings.bootinfo Montrer les infos au démarrage +miscsettings.bootmenu Montrer le menu démarrage +miscsettings.driver_boot Options de driver et Démarrage +miscsettings.fb_destination Console de démarrage Expert! +miscsettings.general Général +miscsettings.head Réglages divers +miscsettings.hwsections Utiliser les sections hardware +miscsettings.infobar_sat_display Affichage du satellite dans la barre d'infos +miscsettings.pmtupdate enable m.à.j pmt +miscsettings.shutdown_real Permettre Veille +miscsettings.shutdown_real_rcdelay Arrêt retardé +miscsettings.sptsmode utiliser le mode spts +miscsettings.startbhdriver Charger les drivers du Mode BH +motorcontrol.head Réglage moteur +movieplayer.bookmark Signet +movieplayer.bookmarkname Nom du signet +movieplayer.bookmarkname_hint1 Entrer un nom pour votre nouveau signet +movieplayer.bookmarkname_hint2 +movieplayer.buffering (Tampons)Buffering... +movieplayer.defdir dir. de démarrage +movieplayer.dvdplayback DVD +movieplayer.fileplayback Fichier via VLC +movieplayer.goto Saut vers ... +movieplayer.goto.h1 = -> Saut absolu +movieplayer.goto.h2 +,- -> Saut relatif +movieplayer.head Lecteur vidéo +movieplayer.nostreamingserver Le serveur Streaming n'est pas accessible. +movieplayer.pesplayback Lecture PES (Expérimental) +movieplayer.pleasewait Patienter SVP.\nConnection au serveur streaming... +movieplayer.toomanybookmarks Il y'a plusieurs signets.\nVous avez à éffacer l'un d'eux avant. +movieplayer.tshelp Rouge/LAME(Exit)! Stop\nVERT Sélection piste audio\nJAUNE Pause/Continu\nBLEU Créer signet\nRécepteur montre progression\n1 approx. 1 minute arrière\n3 saut approx. 1 minute\n4 approx. 5 minutes arrière\n6 saut approx. 5 minutes\n7 approx. 10 minutes arrière\n9 skip approx. 10 minutes\nHelp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tsplayback Lecture TS +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp Rouge/LAME(Exit)! Stop\nVERT Resync\nJAUNE Pause/Continu\nBLEU Créer signet\nRécepteur montre Progression\n1 approx. 1 minute arrière\n3 saut approx. 1 minute\n4 approx. 5 minutes arrière\n6 saut approx. 5 minutes\n7 approx. 10 minutes arrière\n9 saut approx. 10 minutes\nHelp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.wrongvlcversion Cette option n'est pas supportée par votre version courante de VLC +networkmenu.broadcast Transmisson +networkmenu.dhcp DHCP +networkmenu.gateway Entrée par défaut +networkmenu.head Réglage réseau +networkmenu.ipaddress Adresse IP +networkmenu.mount NFS/CIFS +networkmenu.nameserver Nom du serveur +networkmenu.netmask Masque du net +networkmenu.setupnow Régler le réseau maintenant +networkmenu.setuponstartup Régler le réseau au démarrage +networkmenu.show montrer le régalge du réseau actif +networkmenu.test tester le réseau maintenant +nfs.alreadymounted repértoire déja monté +nfs.automount monter au démarrage +nfs.dir Répertoire/paratge +nfs.ip IP du sereveur NFS/CIFS +nfs.localdir Répertoire Local de NFS/CIFS +nfs.mount Monter Volume NFS/CIFS +nfs.mount_options Options de montage NFS/CIFS +nfs.mounterror erreur montage NFS/CIFS +nfs.mounterror_notsup Type de système de fichier non supporté pour NFS/CIFS +nfs.mountnow monter maintenant NFS/CIFS +nfs.mounttimeout erreur montage NFS/CIFS: Dépassement du temps alloué +nfs.password Mot de passe CIFS +nfs.remount répertoire de remontage NFS/CIFS +nfs.type type +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount éffacer volume montage NFS/CIFS(Unmount) +nfs.umounterror erreur éffacement montage volume NFS/CIFS +nfs.username Nom d'utilisateur CIFS +nfsmenu.head Réglages NFS/CIFS +nvod.percentage (%d%% fini) +nvod.starting (démarrage dans %d min) +nvodselector.head Sélectionner heure démarrage +nvodselector.subservice Sélectionner Sous-service +options.default Reset à par défaut +options.fb framebuffer +options.null null +options.off off +options.on on +options.serial série +parentallock.changepin changer PIN code +parentallock.changepin_hint1 Entrer votre code de protection(PIN) ici! +parentallock.changetolocked pour les bouquets bloquer +parentallock.head Entrer code parental (PIN) +parentallock.lockage Bloquer le programme +parentallock.lockage12 pour 12 ans et plus +parentallock.lockage16 pour 16 ans et plus +parentallock.lockage18 pour 18 ans et plus +parentallock.lockedchannel Expéditeur bloqué... +parentallock.lockedprogram Programme bloqué (pour %d ans et plus) +parentallock.never jamais +parentallock.onsignal pour fournisseur bloquer +parentallock.parentallock Protection parentale +parentallock.prompt ponctuellemnt pour PIN +pictureviewer.defdir Répertoire de démarrage. +pictureviewer.head Visualiseur de photos +pictureviewer.resize.color_average avancé +pictureviewer.resize.none aucun +pictureviewer.resize.simple simple +pictureviewer.scaling réajustement +pictureviewer.show montrer +pictureviewer.slide_time temps de défilement +pictureviewer.slideshow défilement +pictureviewer.sortorder changer l'ordre de tri +pictureviewer.sortorder.date (date) +pictureviewer.sortorder.filename (Nom de fichier) +ping.ok est joignable (ping) +ping.protocol est injoignable (erreur hôte ou protocole) +ping.socket est injoignable (erreur socket) +ping.unreachable est injoignable +pinprotection.head Entrer code PIN +pinprotection.wrongcode Le code PIN est faux! Essayer encore. +rclock.lockmsg votre télécommande Récepteur sera bloquée.\n pour débloquer, Appuyer \n et sur la Télécommande. +rclock.menueadd Bloquer Télécommande (RC) +rclock.title Bloquer Télécommande (RC) +rclock.unlockmsg Télécommande réactivée. +recordingmenu.defdir Répertoire d'enregistrement +recordingmenu.file Directe (fichier) +recordingmenu.filesettingsseparator Enregistrement directe +recordingmenu.head Réglages d'enregistrement +recordingmenu.help Dispositifs d'enregistrement:\n--------------------------\nserveur:\nutilisation du programme de streaming sur PC\n\n(analogique) vcr:\nutilisation de la sortie vcr\n\ndirecte (fichier):\ndirectement dans le répertoire NFS mounté\nsur le dique dur interne(HDD)\nTS: utiliser mode spts (dbox2)\nPES: ne pas utiliser le mode spts(dbox2)\n\n\nTaille fichier Max.:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: Presque illimité (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart Ne pas balancer vers le mode Péritel +recordingmenu.off off +recordingmenu.recording_type Dispositif d'enregistrement +recordingmenu.server serveur +recordingmenu.server_ip IP serveur d'enregistrement +recordingmenu.server_mac Adresse MAC +recordingmenu.server_port PORT serveur d'enregistrement +recordingmenu.server_wakeup WOL serveur d'enregistrement +recordingmenu.setupnow activater les changements +recordingmenu.splitsize Max. Taille fichier (MB) +recordingmenu.stopplayback stop playback +recordingmenu.stopsectionsd stop section sd +recordingmenu.stream_all_audio_pids enregistrer tout les PIDS audio +recordingmenu.stream_vtxt_pid enregistrer le videotext +recordingmenu.use_o_sync écrire synchrone (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce L'enregistrement démarre dans quelques minutes +repeatblocker.hint_1 Durée test court (in ms) pour reconnaitre 2 keystrokes +repeatblocker.hint_2 Entrer 0 pour éteindre le blocker (rouge est espace) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat Répetitions DiSEqC +satsetup.extended Réglages DiSEqC +satsetup.extended_motor Réglages Moteur +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Réglages Moteur Manuel +satsetup.nodiseqc Pas de DiSEqC +satsetup.satellite Satellite +satsetup.savesettingsnow sauvegarder réglages maintenant +satsetup.smatvremote mise au point SMATV +scants.actcable Cable: +scants.actsatellite Satellite: +scants.bouquet Bouquet +scants.bouquet_create créer nouveau +scants.bouquet_erase Tout éffacer +scants.bouquet_leave Laisser l'actuel +scants.bouquet_satellite Satellite-Bouquet +scants.bouquet_update mettre à jour +scants.channel Chaine: +scants.failed Balayage du Transpondeur échoué! +scants.finished Balayage du Transpondeur réussi! +scants.freqdata Fréquence: +scants.head Balayer le transpondeur +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Total +scants.numberoftvservices TV +scants.provider Provider(Fournisseur): +scants.startnow commencer le Balayage +scants.transponders Transpondeurs: +screensetup.lowerright Vert = réglage du bas à droite +screensetup.upperleft Rouge = réglage du haut à gauche +servicemenu.head Service +servicemenu.reload Recharger listes de chaines +servicemenu.reload_hint Rechargement listes de chaines,\nPatientez SVP. +servicemenu.scants Balayage du service +servicemenu.ucodecheck Verifier ucodes +servicemenu.update Mise à jour du Software +settings.help Aide +settings.missingoptionsconffile Les réglages NEUTRINO sont mis à jour.\nDes nouvelles Options seront mis par défaut. +settings.noconffile Pas de réglages NEUTRINO trouvés.\nUtilisation de celles par défaut. +shutdowntimer.announce Le récepteur sera arrêté dans 1 min.\nAnnuler arrêt? +sleeptimerbox.announce Veille dans in 1 min +sleeptimerbox.hint1 Arrêt dans in min. (000=off) +sleeptimerbox.hint2 Le récepteur sera arrêté après ce temps. +sleeptimerbox.title Veille +streamfeatures.head Caractéristiques +streaminfo.aratio Aspect de proportion +streaminfo.aratio_unknown Aspect de proportion: inconnu +streaminfo.audiotype Type Audio +streaminfo.audiotype_unknown Type Audio: inconnu +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: inconnu +streaminfo.head Information Stream +streaminfo.not_available Non disponible +streaminfo.resolution Résolution +streaminfo.signal Receipt signal +streaming.buffer_overflow L'enregistrement est abondonné,\nTant que les données ne peuvent être écrites assez rapidement. +streaming.busy Un ou plusieurs processus d'enregistrement sont actifs.\nSi vous rencontrez ce message et aucun enregistrement n'est actif, Redémarrer NEUTRINO SVP. +streaming.dir_not_writable Le répertoire d'enregistrement n'est pas autorisé en écriture.\nL'enregistrement ne fonctionnera pas. +streaming.success L'enregistrement est effectué avec succès. +streaming.write_error L'enregistrement est abondonné,\nTant qu'une erreur est survenue lors du processus d'écriture. +streaming.write_error_open L'enregistrement est abondonné,\nParceque le fichier cible ne peut être ouvert. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Réglages Lecteur vidéo +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Off +streamingmenu.on On +streamingmenu.server_ip IP Serveur Streaming +streamingmenu.server_port PORT Serveur Streaming +streamingmenu.streaming_audiorate Taux de données Audio +streamingmenu.streaming_force_avi_rawaudio Forcer AC3 pour AVI +streamingmenu.streaming_force_transcode_video Transcoder MPG/VCD vidéo +streamingmenu.streaming_resolution Résolution +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Répertoire (VLC) +streamingmenu.streaming_transcode_audio Transcoder audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG vidéo codec +streamingmenu.streaming_type Serveur Streaming +streamingmenu.streaming_videorate Taux de données Vidéo +streamingserver.noconnect Pas de connection au serveur streaming.\nEnregistrement annulé. +stringinput.caps caps / Pas de caps +stringinput.clear Tout éffacer +timer.eventrecord.msg ... à exécuter, ou bien non +timer.eventrecord.title Programmer enregistrement +timer.eventtimed.msg L'évènement est programmé.\nLe récepteur démarrera et \basculera vers cette chaine à l'heure programmée. +timer.eventtimed.title Programme Evènement +timerbar.channelswitch programmer +timerbar.recordevent Enregistrer +timerlist.alarmtime Réveil +timerlist.apids PIDs Audio +timerlist.bouquetselect Choisir bouquet +timerlist.channel Chaine +timerlist.channelselect choisir chaine +timerlist.delete éffacer +timerlist.menumodify Modifier horaire +timerlist.menunew Nouvel horaire +timerlist.message Message +timerlist.moderadio Chaines Radio +timerlist.modeselect Mode sélection +timerlist.modetv Chaines TV +timerlist.modify Modifier +timerlist.name Liste horaire +timerlist.new nouvel horaire +timerlist.program.unknown Programme inconnu +timerlist.reload Recharger +timerlist.repeat Repéter +timerlist.repeat.biweekly bi-hebdomadaire +timerlist.repeat.byeventdescription Voir Horaire +timerlist.repeat.daily Quotidien +timerlist.repeat.fourweekly Quatre fois/semaine +timerlist.repeat.friday Vendredi +timerlist.repeat.monday Lundi +timerlist.repeat.monthly mensuellement +timerlist.repeat.once une fois +timerlist.repeat.saturday Samedi +timerlist.repeat.sunday Dimanche +timerlist.repeat.thursday Jeudi +timerlist.repeat.tuesday Mardi +timerlist.repeat.unknown inconnu +timerlist.repeat.wednesday Mercredi +timerlist.repeat.weekdays Jours de la semaine +timerlist.repeat.weekly Hebdomadaire +timerlist.save Sauvegarder horaire +timerlist.standby Mode SB +timerlist.standby.off Quitter Veille +timerlist.standby.on Entrer en Veille +timerlist.stoptime heure Arrêt +timerlist.type Type d'horaire +timerlist.type.nextprogram Prochain programme +timerlist.type.record Enregistrer +timerlist.type.remind Rappeler +timerlist.type.shutdown Arrêt +timerlist.type.sleeptimer Veille programmée +timerlist.type.standby Veille +timerlist.type.unknown inconnu +timerlist.type.zapto Zapper vers +timerlist.weekdays Jours de la semaine +timerlist.weekdays.hint_1 Lun Mar Mar Jeu Ven Sam Dim +timerlist.weekdays.hint_2 'X'=Horaire '-' Pas d'horaire +timersettings.record_safety_time_after Correction du temps d'arrêt d'enregistrement +timersettings.record_safety_time_after.hint_1 Temps de Correction en min. (00=off). Ce temps +timersettings.record_safety_time_after.hint_2 sera ajouté à chaque horaire d'enregistrement. +timersettings.record_safety_time_before Correction du temps de démarrage d'enregistrement +timersettings.record_safety_time_before.hint_1 Temps de Correction en min. (00=off). Ce temps +timersettings.record_safety_time_before.hint_2 sera déduit de chaque horaire d'enregistrement. +timersettings.separator Réglages d'horaire +timing.chanlist Liste chaine +timing.epg Epg +timing.filebrowser Navigateur de fichier +timing.head Temps d'éclipse OSD +timing.hint_1 Temps en sec. Après ce temps +timing.hint_2 la barre d'infos disparaîtra . +timing.infobar Barre d'infos +timing.menu Menu +timing.numericzap Zap Numerique +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head vérification UCode +ucodecheck.ucode UCode +ucodes.failure ATTENTION, µCodes ne sont pas trouvés!\n\nTélécharger les via FTP (ou bien Récepteur-BootManager),\nensuite redémarrer votre récepteur! +videomenu.csync Correction sync +videomenu.head Réglages Vidéo +videomenu.rgb_centering Centrage RGB +videomenu.screensetup Réglage écran +videomenu.vcrswitch Balance Péritel automatique +videomenu.videoformat Format +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect Détection automatique +videomenu.videosignal Sortie Vidéo +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Vidéo +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Zapper vers programmeur dans une minute diff --git a/data/locale/french.locale b/data/locale/french.locale new file mode 100644 index 000000000..3f388b9ed --- /dev/null +++ b/data/locale/french.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus Visualiasation liste évènements +EPGMenu.eventinfo Détails programme en cours +EPGMenu.eventlist Liste évènements programme en cours +EPGMenu.head EPG - Information du programme +EPGMenu.streaminfo Informations techniques +EPGPlus.actions Actions +EPGPlus.bybouquet_mode Par bouquet +EPGPlus.bypage_mode par Page +EPGPlus.change_font_size Taille caractère +EPGPlus.change_font_style Taille style +EPGPlus.change_size Taille +EPGPlus.channelentry_font caractère chaine +EPGPlus.channelentry_separationlineheight Hauteur ligne de séparation +EPGPlus.channelentry_width Largeur +EPGPlus.channelevententry_font Caractère événement +EPGPlus.edit_fonts edit Caractères +EPGPlus.edit_sizes edit Tailles +EPGPlus.font_style_bold style Gras +EPGPlus.font_style_italic style Italic +EPGPlus.font_style_regular style Régulier +EPGPlus.footer_fontbouquetchannelname Bas de page bouquet +EPGPlus.footer_fontbuttons Bas de page boutons +EPGPlus.footer_fonteventdescription Bas de page évènement +EPGPlus.footer_fonteventshortdescription Bas de page évènement réduit +EPGPlus.head Eventlist Présentation (EPG Plus) +EPGPlus.header_font Caractère Entête +EPGPlus.horgap1_height Hauteur1 +EPGPlus.horgap2_height Hauteur2 +EPGPlus.next_bouquet Bouquet suivant +EPGPlus.options Options +EPGPlus.page_down Page vers le bas +EPGPlus.page_up Page vers le haut +EPGPlus.prev_bouquet Bouquet précedent +EPGPlus.record Enregistrer +EPGPlus.refresh_epg Rafraîchir +EPGPlus.remind Rappel +EPGPlus.reset_settings Reset réglages +EPGPlus.save_settings Sauvegarder Réglages +EPGPlus.scroll_mode Mode défilement +EPGPlus.select_font_name Nom de caractère +EPGPlus.settings Réglages +EPGPlus.slider_width Largeur barette +EPGPlus.stretch_mode Mode déployé +EPGPlus.swap_mode Mode échange +EPGPlus.timeline_fontdate Caractère de Date +EPGPlus.timeline_fonttime Caractère d'heure +EPGPlus.vergap1_width Largeur1 +EPGPlus.vergap2_width Largeur2 +EPGPlus.view_mode Mode visualisation +GENRE.ARTS.0 arts / culture +GENRE.ARTS.1 arts contemporains +GENRE.ARTS.10 arts/culture magazines +GENRE.ARTS.11 mode +GENRE.ARTS.2 arts fins +GENRE.ARTS.3 religions +GENRE.ARTS.4 culture populaire/arts traditionels +GENRE.ARTS.5 littérature +GENRE.ARTS.6 film/cinéma +GENRE.ARTS.7 film expérimental/vidéo +GENRE.ARTS.8 broadcasting/press +GENRE.ARTS.9 nouvelle média +GENRE.CHILDRENs_PROGRAMMES.0 enfants / programme juvénile +GENRE.CHILDRENs_PROGRAMMES.1 programme pré-scolaire +GENRE.CHILDRENs_PROGRAMMES.2 programme de loisirs pour 6 à 14 +GENRE.CHILDRENs_PROGRAMMES.3 programme de loisirs pour 10 à 16 +GENRE.CHILDRENs_PROGRAMMES.4 programme information/éducation/école +GENRE.CHILDRENs_PROGRAMMES.5 déssins animés/marionettes +GENRE.DOCUS_MAGAZINES.0 documentaire / magazine +GENRE.DOCUS_MAGAZINES.1 nature/animaux/environnement +GENRE.DOCUS_MAGAZINES.2 sciences technologie/nature +GENRE.DOCUS_MAGAZINES.3 médecine/physiologie/psychologie +GENRE.DOCUS_MAGAZINES.4 Pays étrangers/expéditions +GENRE.DOCUS_MAGAZINES.5 Sciences sociales/spirituelles +GENRE.DOCUS_MAGAZINES.6 Aurtes éducations +GENRE.DOCUS_MAGAZINES.7 languages +GENRE.MOVIE.0 films/drames +GENRE.MOVIE.1 détective/Policier +GENRE.MOVIE.2 aventure/western/guerre +GENRE.MOVIE.3 science-fiction/fantaisie/horreur +GENRE.MOVIE.4 comédie +GENRE.MOVIE.5 opéra/mélodrame/folklore +GENRE.MOVIE.6 romantique +GENRE.MOVIE.7 serieux/classique/réligieux/film historique/drame +GENRE.MOVIE.8 Film adulte/drame +GENRE.MUSIC_DANCE.0 musique / ballet / dance +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 musique sérieuse/classique +GENRE.MUSIC_DANCE.3 folk/musique traditionelle +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musique/opéra +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 informations +GENRE.NEWS.1 info/météo +GENRE.NEWS.2 magazines d'info +GENRE.NEWS.3 documentaire +GENRE.NEWS.4 discussion/interview/débat +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 game show/quiz/concours +GENRE.SHOW.2 varieté show +GENRE.SHOW.3 discours show +GENRE.SOCIAL_POLITICAL.0 évènements socials & politiques / affaires +GENRE.SOCIAL_POLITICAL.1 magazines/reportages/documentaires +GENRE.SOCIAL_POLITICAL.2 conseil économique/social +GENRE.SOCIAL_POLITICAL.3 peuple remarquable +GENRE.SPORTS.0 sports +GENRE.SPORTS.1 évenemnts spéciaux (Jeux Olympiques,Coupe du Monde etc.) +GENRE.SPORTS.10 équitation +GENRE.SPORTS.11 sports martiaux +GENRE.SPORTS.2 magazines de sports +GENRE.SPORTS.3 football +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 sports d'équipe (hors football) +GENRE.SPORTS.6 athlétisme +GENRE.SPORTS.7 sports de moteurs +GENRE.SPORTS.8 sports acquatiques +GENRE.SPORTS.9 sports d'hiver +GENRE.TRAVEL_HOBBIES.0 voyage et détente +GENRE.TRAVEL_HOBBIES.1 tourisme/voyage +GENRE.TRAVEL_HOBBIES.2 artisanet +GENRE.TRAVEL_HOBBIES.3 automobilisme +GENRE.TRAVEL_HOBBIES.4 fitness & santé +GENRE.TRAVEL_HOBBIES.5 cuisine +GENRE.TRAVEL_HOBBIES.6 publicité/shopping +GENRE.TRAVEL_HOBBIES.7 jardinage +GENRE.UNKNOWN inconnu +apids.hint_1 Entrer APIDs à streamer +apids.hint_2 en notation hex séparé par ' ' +apidselector.head Sélectionner language +audiomenu.PCMOffset diminuer le Volume PCM +audiomenu.analogout sortie Analogique +audiomenu.avs avs +audiomenu.avs_control Contrôle de Volume avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Réglages Audio +audiomenu.lirc lirc +audiomenu.monoleft mono gauche +audiomenu.monoright mono droite +audiomenu.ost ost +audiomenu.stereo stéreo +audioplayer.add Ajouter +audioplayer.artist_title Artiste, Titre +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id chercher par ID +audioplayer.button_select_title_by_name charcher par nom +audioplayer.defdir dir. de Départ +audioplayer.delete éffacer +audioplayer.deleteall tout éffacer +audioplayer.display_order afficher ordre +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward avance rapide. +audioplayer.follow selection autode l'encours +audioplayer.head liste Liste Audio +audioplayer.highprio Pririté de décodage élevé +audioplayer.id3scan étiquette ID3 de Scanning +audioplayer.jump_backwards Saut à l'arrière +audioplayer.jump_dialog_hint1 Entrer cible pour saut SVP +audioplayer.jump_dialog_hint2 (relative, en secondes) +audioplayer.jump_dialog_title Entrer cible pour saut +audioplayer.jump_forwards Saut en avant +audioplayer.keylevel Niveau de clé +audioplayer.name Audioplayer +audioplayer.pause pause +audioplayer.play écouter +audioplayer.playing Piste en cours +audioplayer.playlist_fileerror_msg Fichier non crée: +audioplayer.playlist_fileerror_title Erreur +audioplayer.playlist_fileoverwrite_msg Voulez-vous écraser l'ancien fichier: +audioplayer.playlist_fileoverwrite_title écraser? +audioplayer.playlist_name Nom de fichier de la liste +audioplayer.playlist_name_hint1 Entrer le nom de fichier de la liste SVP +audioplayer.playlist_name_hint2 l'extension .m3u sera ajouté automatiquement +audioplayer.reading_files reading files +audioplayer.repeat_on Mode répétition activé +audioplayer.rewind rebibiner +audioplayer.save_playlist sauvegarder la liste +audioplayer.screensaver_timeout Ecran de sauvegarde timeout (min, 0=off) +audioplayer.select_title_by_name Chercher titre par nom (SMS) +audioplayer.show_playlist Montrer la liste +audioplayer.shuffle Pas +audioplayer.stop Stop +audioplayer.title_artist Titre, Artiste +audioplayerpicsettings.general Lecteur audio/Visualiseur de Photos +bookmarkmanager.delete Effacer +bookmarkmanager.name Marquer +bookmarkmanager.rename renommer +bookmarkmanager.select sélectionner +bouqueteditor.add Ajouter +bouqueteditor.bouquetname Nom de bouquets +bouqueteditor.delete Effacer +bouqueteditor.discardingchanges Annulation des changement. Patienter SVP... +bouqueteditor.hide Cacher +bouqueteditor.lock Bloquer +bouqueteditor.move Dépalcer +bouqueteditor.name Editeur de bouquet +bouqueteditor.newbouquetname Nouveau nom de bouquets +bouqueteditor.rename Renommer +bouqueteditor.return Prêt +bouqueteditor.savechanges? Voulez-vous sauvegarder les changements? +bouqueteditor.savingchanges Sauvegarde des changements. Pateientez SVP... +bouqueteditor.switch ajout/suppression +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider Fournisseur de service +channellist.head tout les services +channellist.nonefound Aucune chaines trouvée!\néxecuter un scan\n(DBOX-key -> service) +channellist.since depuis %02d:%02d +colorchooser.alpha alpha +colorchooser.blue bleu +colorchooser.green vert +colorchooser.red rouge +colormenu.background Arrière plan +colormenu.fade Fade menus +colormenu.font Font taille +colormenu.gtx_alpha Transparence (GTX) +colormenu.head Color Régalges +colormenu.menucolors Couleur de Menu +colormenu.statusbar Barre d'info +colormenu.textcolor Couleur du texte +colormenu.themeselect sélectionner le thème +colormenu.timing Temporisation OSD +colormenusetup.head Couleurs du Menu +colormenusetup.menucontent Contenu Fenêtre +colormenusetup.menucontent_inactive Contenue Fenêtre inactive +colormenusetup.menucontent_selected Contenue Fenêtre sélectinnée +colormenusetup.menuhead Menu entête +colorstatusbar.head Barre d'info +colorstatusbar.text Barre d'info +colorthememenu.classic_theme Thème Classique +colorthememenu.dblue_theme Thème bleu foncé +colorthememenu.dvb2k_theme Thème DVB2000 +colorthememenu.head Sélecteur de thème +colorthememenu.neutrino_theme Thème Neutrino +date.Apr Avr +date.Aug Aoû +date.Dec Déc +date.Feb Fév +date.Fri Ven +date.Jan Jan +date.Jul Jul +date.Jun Jui +date.Mar Mar +date.May Mai +date.Mon Lun +date.Nov Nov +date.Oct Oct +date.Sat Sam +date.Sep Sép +date.Sun Dim +date.Thu Jeu +date.Tue Mar +date.Wed Mer +epglist.head Liste Evenement - %s +epglist.noevents Pas d'EPG disponible... +epgviewer.More_Screenings Plus de diffusion sur cette chaine +epgviewer.nodetailed Pas d'informations détaillée disponible +epgviewer.notfound Pas d'EPG trouvé +eventlistbar.channelswitch Agenda +eventlistbar.eventsort Classement +eventlistbar.recordevent Enregistrement +favorites.addchannel La chaine en cours sera ajoutée \nau bouquet "Mes Favories". \n ça prendra quelques secondes... +favorites.bouquetname Mes Favories +favorites.bqcreated Le Bouquet "Mes Favories" est crée...\n +favorites.chadded La chaine en cours est ajoutée à vos favories...\n +favorites.chalreadyinbq La chaine en cours est déja dans vos favories...\n +favorites.finalhint \nUtilise l'éditeur de bouquet pour modifier vos favories.\n +favorites.menueadd Ajouter une chaine aux favories +favorites.nobouquets Les Favories sont disponibles les bouquets activés uniquement. +filebrowser.delete Effacer +filebrowser.denydirectoryleave Répertoire de démarrage réel +filebrowser.dodelete1 Effacer +filebrowser.dodelete2 ? +filebrowser.filter.active Filtre on +filebrowser.filter.inactive Filtre off +filebrowser.head Navigateur de fichiers +filebrowser.mark Marquer +filebrowser.nextpage Page suivante +filebrowser.prevpage Page précedente +filebrowser.scan Balayage de réprtoire +filebrowser.select Selectionner +filebrowser.showrights Montrer les attribues de fichier +filebrowser.sort.date (date) +filebrowser.sort.name (Nom de fichier) +filebrowser.sort.namedirsfirst (Nom de fichier2) +filebrowser.sort.size (Taille) +filebrowser.sort.type (type) +flashupdate.actionreadflash Lecture +flashupdate.cantopenfile Ouverture de fichier impossible +flashupdate.cantopenmtd Ouverture de mtd-device impossible +flashupdate.checkupdate Recherche de nouvelle version +flashupdate.currentreleasecycle Cycle de sortie +flashupdate.currentversion_sep Version courante +flashupdate.currentversiondate Date +flashupdate.currentversionsnapshot Type d'Image +flashupdate.currentversiontime heure +flashupdate.erasefailed Effacement du flash échoué +flashupdate.erasing Effacement flash +flashupdate.experimentalimage L'Image sélectionnée est une version non testée, Ca veut dire \nvotre récepteur ne plus redémarrer après la mise à jour.\n\nvoulez-vous vraiment mettre à jour avec cette version? +flashupdate.expertfunctions Fonctions Expert +flashupdate.fileis0bytes La taille du fichier est 0 Bytes +flashupdate.fileselector Sélecteur de fichier +flashupdate.flashreadyreboot L'Image est flashée avec succès.\nLe récepteur va redémarrer maintenant. +flashupdate.getinfofile Obtention de l'info version +flashupdate.getinfofileerror Impossible d'extraire l'info version +flashupdate.getupdatefile Obtention de la m.à.j (mise à jour) +flashupdate.getupdatefileerror Impossible d'extraire la m.à.j +flashupdate.globalprogress Progression globale: +flashupdate.head m.à.j du Software (Mise à jour du programme) +flashupdate.md5check Vérification de l'Image +flashupdate.md5sumerror L'Image a des erreurs +flashupdate.msgbox La nouvelle file suivante est trouvée:\nDate: %s, %s\nBaseImage: %s\nType: %s\n\nVoulez-vous télécharger et installer cette version maintenant? +flashupdate.msgbox_manual La nouvelle image suivante est trouvée:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nVoulez-vous télécharger et installer cette version maintenant? +flashupdate.mtdselector Sélecteur de partition +flashupdate.programmingflash programmation du flash +flashupdate.proxypassword Mot de passe +flashupdate.proxypassword_hint1 entrer le mot de pass du serveur proxy +flashupdate.proxypassword_hint2 Entrée vide veut dire pas de proxy-auth +flashupdate.proxyserver Nom de l'hôte +flashupdate.proxyserver_hint1 entrer le nom du serveur proxy ou bien ip, utiliser hôte:port +flashupdate.proxyserver_hint2 Entrée vide veut dire pas de proxy +flashupdate.proxyserver_sep Serveur Proxy +flashupdate.proxyusername Nom d'utilisateur +flashupdate.proxyusername_hint1 entrer le nom d'utilisateur du serveur proxy +flashupdate.proxyusername_hint2 Entrée vide veut dire pas de proxy-auth +flashupdate.readflash Lire l'image entièrement +flashupdate.readflashmtd Lire une partition +flashupdate.ready Prêt +flashupdate.reallyflashmtd Vous voulez vraiment flasher?\n\nSi une erreur survient ou l'Image est non\nvalide, le récepteur ne redémarrera pas après le flash.\n\nImagename: %s\nTarget: %s +flashupdate.savesuccess L'Image est sauvegardée avec succès \nsous %s. +flashupdate.selectimage Images/Files disponibles +flashupdate.squashfs.noversion La vérification de la version SquashFS version ne sont supportée que lors de la m.à.j via le WEB actuellement.\nVoulez-vous vraiment installer cette Image? +flashupdate.titlereadflash Lecture du Flash +flashupdate.titlewriteflash Ecriture du Flash +flashupdate.updatemode Mode de m.à.j +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuel (ftp) +flashupdate.url_file Fichier de configuration +flashupdate.versioncheck Vérification de la version +flashupdate.writeflash Ecrire l'Image entièrement +flashupdate.writeflashmtd Ecrire une partition +flashupdate.wrongbase flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Liste de chaines +fontmenu.epg EPG +fontmenu.eventlist Liste d'évènements +fontmenu.gamelist Liste de jeux +fontmenu.head Réglage taille caractère +fontmenu.infobar Barre d'Info +fontsize.channel_num_zap Sélection directe +fontsize.channellist Liste de chaines +fontsize.channellist_descr Déscription +fontsize.channellist_number Numéro +fontsize.epg_date EPG Date +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titre +fontsize.eventlist_datetime date / heure +fontsize.eventlist_itemlarge large +fontsize.eventlist_itemsmall petit +fontsize.eventlist_title Titre +fontsize.filebrowser_item Article du navigateur de fichier +fontsize.gamelist_itemlarge large +fontsize.gamelist_itemsmall petit +fontsize.hint Initialisation caractère,\nattendre SVP... +fontsize.infobar_channame Nom de chaine +fontsize.infobar_info info +fontsize.infobar_number Numéro +fontsize.infobar_small petit +fontsize.menu Menu texte +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titre +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG non chargé.... +infoviewer.epgwait attente EPG... +infoviewer.eventlist Liste d'évènements +infoviewer.languages Audio +infoviewer.motor_moving positionnement de l'antenne +infoviewer.nocurrent Pas d'info pour le programme en cours +infoviewer.noepg Pas d'EPG disponible +infoviewer.notavailable Chaine non disponible +infoviewer.selecttime Sélection heure +infoviewer.streaminfo Options de caractéristiques +infoviewer.subservice Sous services +infoviewer.waittime Attented d'heure... +ipsetup.hint_1 Utilise 0..9, ou bien haut/bas, +ipsetup.hint_2 OK sauvegader, HOME annuler +keybindingmenu.RC Touche repeat-blocker +keybindingmenu.addrecord Ajouter horaire d'enregistrement +keybindingmenu.addremind Ajouter horaire de zap vers... +keybindingmenu.allchannels_on_ok tous-services +keybindingmenu.bouquetchannels_on_ok bouquet en cours +keybindingmenu.bouquetdown bouquet arrière +keybindingmenu.bouquethandling Contrôle de Bouquet +keybindingmenu.bouquetlist_on_ok Liste de bouquets +keybindingmenu.bouquetup next bouquet +keybindingmenu.cancel Fermer liste de chaines +keybindingmenu.channeldown chaine bas +keybindingmenu.channellist Liste de chaines +keybindingmenu.channelup chaine avant +keybindingmenu.head Réglage des touches +keybindingmenu.lastchannel Zap rapide +keybindingmenu.modechange Mode changement +keybindingmenu.pagedown page bas +keybindingmenu.pageup page haut +keybindingmenu.quickzap zap rapide +keybindingmenu.repeatblock délai de répétition +keybindingmenu.repeatblockgeneric délai générique +keybindingmenu.sort changer l'ordre de classement +keybindingmenu.subchanneldown sous-chaine bas +keybindingmenu.subchannelup sous-chaine haut +keybindingmenu.tvradiomode Mode TV/Radio +keybindingmenu.zaphistory Zapping Bouquet Historique +keychooser.head Setup nouvelle touche +keychooser.text1 Appuyer sur la nouvelle touche SVP +keychooser.text2 Attendez quelques secondes pour quitter +keychoosermenu.currentkey touche en cours +keychoosermenu.setnew réglage nouvelle touche +keychoosermenu.setnone Pas de touche +languagesetup.head Réglage Langue +languagesetup.select Langue +lcdcontroler.brightness Brillance normale +lcdcontroler.brightnessstandby Brillance de veille +lcdcontroler.contrast Contraste +lcdcontroler.head Réglage LCD +lcdmenu.autodimm Estompage Auto +lcdmenu.head LCD Réglages +lcdmenu.inverse Inverse +lcdmenu.lcdcontroler Contraste / Brillance +lcdmenu.power Alimentation +lcdmenu.statusline Ligne d'état +lcdmenu.statusline.both Volume / temps écoulé +lcdmenu.statusline.playtime Temps écoulé +lcdmenu.statusline.volume Volume +mainmenu.audioplayer Lecteur Audio +mainmenu.games Jeux +mainmenu.head MENU PRINCIPAL +mainmenu.movieplayer Lecteur Vidéo +mainmenu.pausesectionsd Lire EPG +mainmenu.pictureviewer Visionneur de photos +mainmenu.radiomode Mode Radio +mainmenu.recording Enregistrement +mainmenu.recording_start Début +mainmenu.recording_stop stop +mainmenu.scartmode Mode Péritel +mainmenu.service Service +mainmenu.settings Réglages +mainmenu.shutdown Eteindre +mainmenu.sleeptimer Veille +mainmenu.tvmode Mode TV +mainsettings.audio Audio +mainsettings.colors Coleurs / thèmes / Caractères +mainsettings.head Réglages +mainsettings.keybinding Réglage touche +mainsettings.language Langue +mainsettings.lcd Ecran à Cristaux Liquides (LCD) +mainsettings.misc Réglage divers +mainsettings.network Réseau +mainsettings.recording Enregistrement +mainsettings.savesettingsnow Sauvegarder réglage maintenant +mainsettings.savesettingsnow_hint Sauvegarde de réglage,\nPatientez SVP... +mainsettings.streaming Lecteur vidéo +mainsettings.video Vidéo +menu.back Retour +messagebox.back Retour +messagebox.cancel Annuler +messagebox.discard Annuler les changemnts? +messagebox.error Erreur +messagebox.info Information +messagebox.no Non +messagebox.yes Oui +miscsettings.bootinfo Montrer les infos au démarrage +miscsettings.bootmenu Montrer le menu démarrage +miscsettings.driver_boot Options de driver et Démarrage +miscsettings.fb_destination Console de démarrage Expert! +miscsettings.general Général +miscsettings.head Réglages divers +miscsettings.hwsections Utiliser les sections hardware +miscsettings.infobar_sat_display Affichage du satellite dans la barre d'infos +miscsettings.pmtupdate enable m.à.j pmt +miscsettings.shutdown_real Permettre Veille +miscsettings.shutdown_real_rcdelay Arrêt retardé +miscsettings.sptsmode utiliser le mode spts +miscsettings.startbhdriver Charger les drivers du Mode BH +motorcontrol.head Réglage moteur +movieplayer.bookmark Signet +movieplayer.bookmarkname Nom du signet +movieplayer.bookmarkname_hint1 Entrer un nom pour votre nouveau signet +movieplayer.bookmarkname_hint2 +movieplayer.buffering (Tampons)Buffering... +movieplayer.defdir dir. de démarrage +movieplayer.dvdplayback DVD +movieplayer.fileplayback Fichier via VLC +movieplayer.goto Saut vers ... +movieplayer.goto.h1 = -> Saut absolu +movieplayer.goto.h2 +,- -> Saut relatif +movieplayer.head Lecteur vidéo +movieplayer.nostreamingserver Le serveur Streaming n'est pas accessible. +movieplayer.pesplayback Lecture PES (Expérimental) +movieplayer.pleasewait Patienter SVP.\nConnection au serveur streaming... +movieplayer.toomanybookmarks Il y'a plusieurs signets.\nVous avez à éffacer l'un d'eux avant. +movieplayer.tsplayback Lecture TS +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion Cette option n'est pas supportée par votre version courante de VLC +networkmenu.broadcast Transmisson +networkmenu.dhcp DHCP +networkmenu.gateway Entrée par défaut +networkmenu.head Réglage réseau +networkmenu.ipaddress Adresse IP +networkmenu.mount NFS/CIFS +networkmenu.nameserver Nom du serveur +networkmenu.netmask Masque du net +networkmenu.setupnow Régler le réseau maintenant +networkmenu.setuponstartup Régler le réseau au démarrage +networkmenu.show montrer le régalge du réseau actif +networkmenu.test tester le réseau maintenant +nfs.alreadymounted repértoire déja monté +nfs.automount monter au démarrage +nfs.dir Répertoire/paratge +nfs.ip IP du sereveur NFS/CIFS +nfs.localdir Répertoire Local de NFS/CIFS +nfs.mount Monter Volume NFS/CIFS +nfs.mount_options Options de montage NFS/CIFS +nfs.mounterror erreur montage NFS/CIFS +nfs.mounterror_notsup Type de système de fichier non supporté pour NFS/CIFS +nfs.mountnow monter maintenant NFS/CIFS +nfs.mounttimeout erreur montage NFS/CIFS: Dépassement du temps alloué +nfs.password Mot de passe CIFS +nfs.remount répertoire de remontage NFS/CIFS +nfs.type type +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount éffacer volume montage NFS/CIFS(Unmount) +nfs.umounterror erreur éffacement montage volume NFS/CIFS +nfs.username Nom d'utilisateur CIFS +nfsmenu.head Réglages NFS/CIFS +nvod.percentage (%d%% fini) +nvod.starting (démarrage dans %d min) +nvodselector.directormode Direct +nvodselector.head Sélectionner heure démarrage +nvodselector.subservice Sélectionner Sous-service +options.default Reset à par défaut +options.fb framebuffer +options.null null +options.off off +options.on on +options.serial série +parentallock.changepin changer PIN code +parentallock.changepin_hint1 Entrer votre code de protection(PIN) ici! +parentallock.changetolocked pour les bouquets bloquer +parentallock.head Entrer code parental (PIN) +parentallock.lockage Bloquer le programme +parentallock.lockage12 pour 12 ans et plus +parentallock.lockage16 pour 16 ans et plus +parentallock.lockage18 pour 18 ans et plus +parentallock.lockedchannel Expéditeur bloqué... +parentallock.lockedprogram Programme bloqué (pour %d ans et plus) +parentallock.never jamais +parentallock.onsignal pour fournisseur bloquer +parentallock.parentallock Protection parentale +parentallock.prompt ponctuellemnt pour PIN +pictureviewer.defdir Répertoire de démarrage. +pictureviewer.head Visualiseur de photos +pictureviewer.resize.color_average avancé +pictureviewer.resize.none aucun +pictureviewer.resize.simple simple +pictureviewer.scaling réajustement +pictureviewer.show montrer +pictureviewer.slide_time temps de défilement +pictureviewer.slideshow défilement +pictureviewer.sortorder changer l’ordre de tri +pictureviewer.sortorder.date (date) +pictureviewer.sortorder.filename (Nom de fichier) +ping.ok est joignable (ping) +ping.protocol est injoignable (erreur hôte ou protocole) +ping.socket est injoignable (erreur socket) +ping.unreachable est injoignable +pinprotection.head Entrer code PIN +pinprotection.wrongcode Le code PIN est faux! Essayer encore. +rclock.lockmsg votre télécommande Récepteur sera bloquée.\n pour débloquer, Appuyer \n et sur la Télécommande. +rclock.menueadd Bloquer Télécommande (RC) +rclock.title Bloquer Télécommande (RC) +rclock.unlockmsg Télécommande réactivée. +recordingmenu.defdir Répertoire d’enregistrement +recordingmenu.file Directe (fichier) +recordingmenu.head Réglages d’enregistrement +recordingmenu.help Dispositifs d’enregistrement:\n--------------------------\nserveur:\nutilisation du programme de streaming sur PC\n\n(analogique) vcr:\nutilisation de la sortie vcr\n\ndirecte (fichier):\ndirectement dans le répertoire NFS mounté\nsur le dique dur interne(HDD)\nTS: utiliser mode spts (dbox2)\nPES: ne pas utiliser le mode spts(dbox2)\n\n\nTaille fichier Max.:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: Presque illimité (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart Ne pas balancer vers le mode Péritel +recordingmenu.off off +recordingmenu.recording_type Dispositif d’enregistrement +recordingmenu.server serveur +recordingmenu.server_ip IP serveur d’enregistrement +recordingmenu.server_mac Adresse MAC +recordingmenu.server_port PORT serveur d’enregistrement +recordingmenu.server_wakeup WOL serveur d’enregistrement +recordingmenu.setupnow activater les changements +recordingmenu.splitsize Max. Taille fichier (MB) +recordingmenu.stopplayback stop playback +recordingmenu.stopsectionsd stop section sd +recordingmenu.stream_vtxt_pid enregistrer le videotext +recordingmenu.use_o_sync écrire synchrone (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce L’enregistrement démarre dans quelques minutes +repeatblocker.hint_1 Durée test court (in ms) pour reconnaitre 2 keystrokes +repeatblocker.hint_2 Entrer 0 pour éteindre le blocker (rouge est espace) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat Répetitions DiSEqC +satsetup.extended Réglages DiSEqC +satsetup.extended_motor Réglages Moteur +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Réglages Moteur Manuel +satsetup.nodiseqc Pas de DiSEqC +satsetup.satellite Satellite +satsetup.savesettingsnow sauvegarder réglages maintenant +satsetup.smatvremote mise au point SMATV +scants.actcable Cable: +scants.actsatellite Satellite: +scants.bouquet Bouquet +scants.bouquet_create créer nouveau +scants.bouquet_erase Tout éffacer +scants.bouquet_leave Laisser l’actuel +scants.bouquet_satellite Satellite-Bouquet +scants.bouquet_update mettre à jour +scants.channel Chaine: +scants.failed Balayage du Transpondeur échoué! +scants.finished Balayage du Transpondeur réussi! +scants.freqdata Fréquence: +scants.head Balayer le transpondeur +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Total +scants.numberoftvservices TV +scants.provider Provider(Fournisseur): +scants.startnow commencer le Balayage +scants.transponders Transpondeurs: +screensetup.lowerright Vert = réglage du bas à droite +screensetup.upperleft Rouge = réglage du haut à gauche +servicemenu.head Service +servicemenu.reload Recharger listes de chaines +servicemenu.reload_hint Rechargement listes de chaines,\nPatientez SVP. +servicemenu.scants Balayage du service +servicemenu.ucodecheck Verifier ucodes +servicemenu.update Mise à jour du Software +settings.help Aide +settings.missingoptionsconffile Les réglages NEUTRINO sont mis à jour.\nDes nouvelles Options seront mis par défaut. +settings.noconffile Pas de réglages NEUTRINO trouvés.\nUtilisation de celles par défaut. +shutdowntimer.announce Le récepteur sera arrêté dans 1 min.\nAnnuler arrêt? +sleeptimerbox.announce Veille dans in 1 min +sleeptimerbox.hint1 Arrêt dans in min. (000=off) +sleeptimerbox.hint2 Le récepteur sera arrêté après ce temps. +sleeptimerbox.title Veille +streamfeatures.head Caractéristiques +streaminfo.aratio Aspect de proportion +streaminfo.aratio_unknown Aspect de proportion: inconnu +streaminfo.audiotype Type Audio +streaminfo.audiotype_unknown Type Audio: inconnu +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: inconnu +streaminfo.head Information Stream +streaminfo.not_available Non disponible +streaminfo.resolution Résolution +streaminfo.signal Receipt signal +streaming.buffer_overflow L’enregistrement est abondonné,\nTant que les données ne peuvent être écrites assez rapidement. +streaming.busy Un ou plusieurs processus d’enregistrement sont actifs.\nSi vous rencontrez ce message et aucun enregistrement n’est actif, Redémarrer NEUTRINO SVP. +streaming.dir_not_writable Le répertoire d’enregistrement n’est pas autorisé en écriture.\nL’enregistrement ne fonctionnera pas. +streaming.success L’enregistrement est effectué avec succès. +streaming.write_error L’enregistrement est abondonné,\nTant qu’une erreur est survenue lors du processus d’écriture. +streaming.write_error_open L’enregistrement est abondonné,\nParceque le fichier cible ne peut être ouvert. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Réglages Lecteur vidéo +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Off +streamingmenu.on On +streamingmenu.server_ip IP Serveur Streaming +streamingmenu.server_port PORT Serveur Streaming +streamingmenu.streaming_audiorate Taux de données Audio +streamingmenu.streaming_force_avi_rawaudio Forcer AC3 pour AVI +streamingmenu.streaming_force_transcode_video Transcoder MPG/VCD vidéo +streamingmenu.streaming_resolution Résolution +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Répertoire (VLC) +streamingmenu.streaming_transcode_audio Transcoder audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG vidéo codec +streamingmenu.streaming_type Serveur Streaming +streamingmenu.streaming_videorate Taux de données Vidéo +streamingserver.noconnect Pas de connection au serveur streaming.\nEnregistrement annulé. +stringinput.caps caps / Pas de caps +stringinput.clear Tout éffacer +timer.eventrecord.msg ... à exécuter, ou bien non +timer.eventrecord.title Programmer enregistrement +timer.eventtimed.msg L’évènement est programmé.\nLe récepteur démarrera et \basculera vers cette chaine à l’heure programmée. +timer.eventtimed.title Programme Evènement +timerbar.channelswitch programmer +timerbar.recordevent Enregistrer +timerlist.alarmtime Réveil +timerlist.apids PIDs Audio +timerlist.bouquetselect Choisir bouquet +timerlist.channel Chaine +timerlist.channelselect choisir chaine +timerlist.delete éffacer +timerlist.menumodify Modifier horaire +timerlist.menunew Nouvel horaire +timerlist.message Message +timerlist.moderadio Chaines Radio +timerlist.modeselect Mode sélection +timerlist.modetv Chaines TV +timerlist.modify Modifier +timerlist.name Liste horaire +timerlist.new nouvel horaire +timerlist.program.unknown Programme inconnu +timerlist.reload Recharger +timerlist.repeat Repéter +timerlist.repeat.biweekly bi-hebdomadaire +timerlist.repeat.byeventdescription Voir Horaire +timerlist.repeat.daily Quotidien +timerlist.repeat.fourweekly Quatre fois/semaine +timerlist.repeat.friday Vendredi +timerlist.repeat.monday Lundi +timerlist.repeat.monthly mensuellement +timerlist.repeat.once une fois +timerlist.repeat.saturday Samedi +timerlist.repeat.sunday Dimanche +timerlist.repeat.thursday Jeudi +timerlist.repeat.tuesday Mardi +timerlist.repeat.unknown inconnu +timerlist.repeat.wednesday Mercredi +timerlist.repeat.weekdays Jours de la semaine +timerlist.repeat.weekly Hebdomadaire +timerlist.save Sauvegarder horaire +timerlist.standby Mode SB +timerlist.standby.off Quitter Veille +timerlist.standby.on Entrer en Veille +timerlist.stoptime heure Arrêt +timerlist.type Type d’horaire +timerlist.type.nextprogram Prochain programme +timerlist.type.record Enregistrer +timerlist.type.remind Rappeler +timerlist.type.shutdown Arrêt +timerlist.type.sleeptimer Veille programmée +timerlist.type.standby Veille +timerlist.type.unknown inconnu +timerlist.type.zapto Zapper vers +timerlist.weekdays Jours de la semaine +timerlist.weekdays.hint_1 Lun Mar Mar Jeu Ven Sam Dim +timerlist.weekdays.hint_2 'X'=Horaire '-' Pas d’horaire +timersettings.record_safety_time_after Correction du temps d’arrêt d’enregistrement +timersettings.record_safety_time_after.hint_1 Temps de Correction en min. (00=off). Ce temps +timersettings.record_safety_time_after.hint_2 sera ajouté à chaque horaire d’enregistrement. +timersettings.record_safety_time_before Correction du temps de démarrage d’enregistrement +timersettings.record_safety_time_before.hint_1 Temps de Correction en min. (00=off). Ce temps +timersettings.record_safety_time_before.hint_2 sera déduit de chaque horaire d’enregistrement. +timersettings.separator Réglages d’horaire +timing.chanlist Liste chaine +timing.epg Epg +timing.filebrowser Navigateur de fichier +timing.head Temps d’éclipse OSD +timing.hint_1 Temps en sec. Après ce temps +timing.hint_2 la barre d’infos disparaîtra . +timing.infobar Barre d’infos +timing.menu Menu +timing.numericzap Zap Numerique +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head vérification UCode +ucodecheck.ucode UCode +ucodes.failure ATTENTION, µCodes ne sont pas trouvés!\n\nTélécharger les via FTP (ou bien Récepteur-BootManager),\nensuite redémarrer votre récepteur! +videomenu.csync Correction sync +videomenu.head Réglages Vidéo +videomenu.rgb_centering Centrage RGB +videomenu.screensetup Réglage écran +videomenu.vcrswitch Balance Péritel automatique +videomenu.videoformat Format +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect Détection automatique +videomenu.videosignal Sortie Vidéo +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Vidéo +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Zapper vers programmeur dans une minute +extra.auto Auto +extra.valid Valider +extra.all tout +extra.new_keys Nouvelles clés(keys) +extra.all_keys Toutes les clés +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo Algo pids +extra.gbox_emu Emu +extra.gbox_softcam Softcam +extra.gbox_net Réseau +extra.gbox_mix Mélange +extra.gbox_info Gbox Info +extra.sep_extra Extras +extra.save_settings Sauvegarder les réglages +extra.extra_menu Addons +extra.ecm_info Ecm Info +extra.pid_info Pid Info +extra.emm_info Nouvelles clés(Keys) +extra.emu_type Type d’Emu +extra.camd Camd +extra.restart_camd Redémarrer camd +extra.mg_settings Réglages Mgcamd +extra.netmode Mode réseau +extra.autoupdate m.à.j Automatique +extra.show_ecm Montrer Ecm +extra.show_emm Montrer Emm +extra.keyupdate m.à.j des clés(keys) +extra.osd OSD(affichage) +extra.keyfolder Répertoire des clés(keys) +extra.hash_pids Hash pids +extra.gbox_settings Réglage Gbox +extra.gbox_mode Mode +extra.newcamd_settings Réglage Newcamd +extra.show_cw Montrer CW +extra.debug_ecm Débguer Ecm +extra.disable_cam Désctiver CAM +extra.show_cat Montrer CAT +extra.show_pmt Montrer PMT +extra.update_pmt m.à.j PMT +extra.report_emm Report wrong EMM sig. +extra.debug_emm Déboguer Emm +extra.show_allca Montrer tout ca sys. +extra.reload_keys Recharger les clés après un Zap +extra.reload_cfg Recharger config après un zap +extra.cw_delay Délai CW par défaut +extra.dvbsnoop DVB Infos Stream +extra.dvbsnoop_pat Montrer PAT +extra.dvbsnoop_cat Montrer CAT +extra.dvbsnoop_tsdt Montrer TSDT +extra.dvbsnoop_nit Montrer NIT +extra.dvbsnoop_sdt Montrer SDT +extra.dvbsnoop_eit Montrer EIT +extra.dvbsnoop_rst Montrer RST +extra.dvbsnoop_tdt Montrer TDT +extra.dvbsnoop_sit Montrer SIT +extra.dvbsnoop_pid Montrer PID manuelle +extra.dvbsnoop_scan Balayage PIDs +extra.dvbsnoop_band Montrer Largeur bande pid +extra.logview Montrer Log +extra.ru ruDREAM +extra.english Anglais +extra.dboxinfo Infos Récepteur +extra.scan_mode Mode Balayage +extra.start_tostandby Démarrage vers veille +extra.rotor_swap Echange Rotor Est/Ouest +extra.use_log Log vers /tmp/log +extra.spts_mode Mode SPTS +extra.hw_sect Section Hardware (matériel) +extra.logo Numéro Logo +extra.scan_full Plein +extra.scan_fast Rapide +extra.hdd_slow Lent +extra.hdd_middle Moyen +extra.hdd_fast Rapide +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Temps vers Veille +extra.hdd_noise Bruit +extra.hdd_activate Activater Réglages +extra.hdd_fs Système de fichiers +extra.hdd_format Formater Disque dur(HDD) +extra.hdd_check Vérifier système fichiers +extra.hdd_settings Réglages Disque Dur(HDD) +extra.clear_log Effacer Log +extra.zap_cycle Cycle de Zap +extra.sms_channel Chaine Mode sms +extra.manual_scan Balayage Manuelle +extra.tp_freq Fréquence +extra.tp_rate Taux de Symbole +extra.tp_pol Polarisation +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Zone horaire(TimeZone) +extra.zapit_menu Options Zapit +extra.zapit_make_bouquet Construire liste chaines restantes +extra.zapit_save_last_chan Sauvegarder dernière chaine +extra.zapit_motor_speed Vitesse du Moteur (10 = 1deg/sec) +extra.zapit_fe_timeout Régler temps de sortie(timeout) +extra.add_to_bouquet Ajouter au bouquet +extra.key_list_start Retour départ +extra.key_list_end Fin +extra.chadded La chaine actuelle est ajoutée au bouquet selectionné....\n +extra.chalreadyinbq La chaine actuelle existe déja dans le bouquet selectionné....\n +extra.menu_left_exit "Left" = menu back +extra.zapit_write_names Write channel names +extra.update_dir Directory for updates +extra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Abondonner le balayage +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/hungarian.locale b/data/locale/hungarian.locale new file mode 100644 index 000000000..b49c7cc1c --- /dev/null +++ b/data/locale/hungarian.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus Műsorok áttekintése +EPGMenu.eventinfo Műsor leírása +EPGMenu.eventlist Csatorna műsora +EPGMenu.head Elektronikus műsorfüzet +EPGMenu.streaminfo Technikai információk +EPGPlus.actions műveletek +EPGPlus.bybouquet_mode csomag szerint +EPGPlus.bypage_mode oldal szerint +EPGPlus.change_font_size Betűméret +EPGPlus.change_font_style Betűstílus +EPGPlus.change_size Méret +EPGPlus.channelentry_font Csatorna +EPGPlus.channelentry_separationlineheight Elválasztó-vonalmagasság +EPGPlus.channelentry_width Szélesség +EPGPlus.channelevententry_font Műsor +EPGPlus.edit_fonts Betűk szerkesztése +EPGPlus.edit_sizes Méretek szerkesztése +EPGPlus.font_style_bold kövér +EPGPlus.font_style_italic dÅ‘lt +EPGPlus.font_style_regular sima +EPGPlus.footer_fontbouquetchannelname Csomag lábjegyzet +EPGPlus.footer_fontbuttons Csomag gombok +EPGPlus.footer_fonteventdescription Műsor lábjegyzet +EPGPlus.footer_fonteventshortdescription Rövid leírás lábjegyzet +EPGPlus.head Műsorok áttekintése (EPG Plus) +EPGPlus.header_font Fejléc +EPGPlus.horgap1_height Magasság1 +EPGPlus.horgap2_height Magasság2 +EPGPlus.next_bouquet KövetkezÅ‘ csomag +EPGPlus.options opciók +EPGPlus.page_down Oldalt le +EPGPlus.page_up Oldalt fel +EPGPlus.prev_bouquet elÅ‘zÅ‘ csomag +EPGPlus.record felvétel +EPGPlus.refresh_epg Frissítés +EPGPlus.remind IdÅ‘zítés +EPGPlus.reset_settings Alapértelmezés +EPGPlus.save_settings Beállítások mentése +EPGPlus.scroll_mode görgetési mód +EPGPlus.select_font_name Betűtípus +EPGPlus.settings Beállítások +EPGPlus.slider_width Csúszkaszélesség +EPGPlus.stretch_mode nyújtási mód +EPGPlus.swap_mode Mód váltás +EPGPlus.timeline_fontdate Dátum +EPGPlus.timeline_fonttime IdÅ‘ +EPGPlus.vergap1_width Szélesség1 +EPGPlus.vergap2_width Szélesség2 +EPGPlus.view_mode Nézet +GENRE.ARTS.0 mûvészet / kultúra +GENRE.ARTS.1 performing arts +GENRE.ARTS.10 mûvészet/kultúra magazin +GENRE.ARTS.11 divat +GENRE.ARTS.2 szépmûvészet +GENRE.ARTS.3 vallás +GENRE.ARTS.4 popular culture/traditional arts +GENRE.ARTS.5 irodalom +GENRE.ARTS.6 film/mozi +GENRE.ARTS.7 kísérleti film/videó +GENRE.ARTS.8 broadcasting/press +GENRE.ARTS.9 new media +GENRE.CHILDRENs_PROGRAMMES.0 gyermek/ifjúsági mûsor +GENRE.CHILDRENs_PROGRAMMES.1 pre-school children's programmes +GENRE.CHILDRENs_PROGRAMMES.2 entertainment programmes for 6 to 14 +GENRE.CHILDRENs_PROGRAMMES.3 entertainment programmes for 10 to 16 +GENRE.CHILDRENs_PROGRAMMES.4 informational/educational/school programmes +GENRE.CHILDRENs_PROGRAMMES.5 báb/rajzfilm +GENRE.DOCUS_MAGAZINES.0 documentation / magazine +GENRE.DOCUS_MAGAZINES.1 természet/állatok/környezet +GENRE.DOCUS_MAGAZINES.2 technológia/természettudomány +GENRE.DOCUS_MAGAZINES.3 gyógyszer/élettan/lélektan +GENRE.DOCUS_MAGAZINES.4 külföldi országok/expedíció +GENRE.DOCUS_MAGAZINES.5 társadalmi/szellemi tudományok +GENRE.DOCUS_MAGAZINES.6 további oktatás +GENRE.DOCUS_MAGAZINES.7 nyelvek +GENRE.MOVIE.0 film/dráma +GENRE.MOVIE.1 detektív/thriller +GENRE.MOVIE.2 kaland/western/háború +GENRE.MOVIE.3 sci-fi/fantázia/horror +GENRE.MOVIE.4 vígjáték +GENRE.MOVIE.5 szappanopera/melódráma/népi +GENRE.MOVIE.6 romatika +GENRE.MOVIE.7 komoly/klasszikus/vallási/történelmi film/dráma +GENRE.MOVIE.8 felnõtt film/dráma +GENRE.MUSIC_DANCE.0 zene/balett/tánc +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 komolyzene/kalsszikus +GENRE.MUSIC_DANCE.3 népzene +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 balett +GENRE.NEWS.0 hírek +GENRE.NEWS.1 hírek/idõjárásjelentés +GENRE.NEWS.2 hírmûsor +GENRE.NEWS.3 ismeretterjesztõ +GENRE.NEWS.4 megbeszélés/interjú/vita +GENRE.SHOW.0 show/játék +GENRE.SHOW.1 játék/kvíz/verseny +GENRE.SHOW.2 varieté +GENRE.SHOW.3 talk show +GENRE.SOCIAL_POLITICAL.0 social & politic events / business +GENRE.SOCIAL_POLITICAL.1 magazines/reports/documentary +GENRE.SOCIAL_POLITICAL.2 economics/social advisory +GENRE.SOCIAL_POLITICAL.3 remarkable people +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 különleges esemény (Olimpiai Játékok, Világkupa, stb.) +GENRE.SPORTS.10 lovaspóló +GENRE.SPORTS.11 küzdõsportok +GENRE.SPORTS.2 sportmagazin +GENRE.SPORTS.3 labdarúgás +GENRE.SPORTS.4 tenisz/squash +GENRE.SPORTS.5 csapatjáték (kivéve labdarúgás) +GENRE.SPORTS.6 atlétika +GENRE.SPORTS.7 motorsport +GENRE.SPORTS.8 vízisport +GENRE.SPORTS.9 téli sportok +GENRE.TRAVEL_HOBBIES.0 utazás és szórakozás +GENRE.TRAVEL_HOBBIES.1 utazás és turizmus +GENRE.TRAVEL_HOBBIES.2 kézimunka +GENRE.TRAVEL_HOBBIES.3 motoring +GENRE.TRAVEL_HOBBIES.4 fitnessz és egészség +GENRE.TRAVEL_HOBBIES.5 sütés/fõzés +GENRE.TRAVEL_HOBBIES.6 hirdetés/bevásárlás +GENRE.TRAVEL_HOBBIES.7 kertgazdálkodás +GENRE.UNKNOWN ismeretlen +apids.hint_1 Ãœsse be a streamelni kívánt APID-eket, +apids.hint_2 HEX-ben, szóközzel elválasztva! +apidselector.head Nyelvválasztás +audiomenu.PCMOffset PCM hangerÅ‘-csökkentés +audiomenu.analogout Analóg kimenet +audiomenu.avs avs +audiomenu.avs_control HangerÅ‘-szabalyzás (avs/ost) +audiomenu.dolbydigital Dolby Digital +audiomenu.head Hangbeállítások +audiomenu.lirc lirc +audiomenu.monoleft bal monó +audiomenu.monoright jobb monó +audiomenu.ost ost +audiomenu.stereo sztereó +audioplayer.add hozzáadás +audioplayer.artist_title ElÅ‘adó, Cím +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id search by ID +audioplayer.button_select_title_by_name név szerinti keresés +audioplayer.defdir Kiindulási könyvtár +audioplayer.delete törlés +audioplayer.deleteall összes törlése +audioplayer.display_order kijelzési sorrend +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward gyors elÅ‘re +audioplayer.follow auto select current +audioplayer.head Audio Lejátszási lista +audioplayer.highprio magas dekódolási prioritás +audioplayer.id3scan ID3 TAG-ek beolvasása +audioplayer.jump_backwards ugrás vissza +audioplayer.jump_dialog_hint1 Kérem adja meg az ugrási célt! +audioplayer.jump_dialog_hint2 (viszonylagos, másodpercben) +audioplayer.jump_dialog_title Adja meg az ugrási célt! +audioplayer.jump_forwards ugrás elÅ‘re +audioplayer.keylevel key level +audioplayer.name Hanglejátszó +audioplayer.pause Pillanatállj +audioplayer.play Lejátszás +audioplayer.playing Current Track +audioplayer.playlist_fileerror_msg A fájlt nem lehet létrehozni: +audioplayer.playlist_fileerror_title Hiba +audioplayer.playlist_fileoverwrite_msg Kívánja felülírni ezt a fájlt? +audioplayer.playlist_fileoverwrite_title Felülírja? +audioplayer.playlist_name lejátszási lista fájlneve +audioplayer.playlist_name_hint1 Kérem adja meg a lejátszási lista fájlnevét! +audioplayer.playlist_name_hint2 Az .m3u kiterjeszés automatikusan hozzáadódik +audioplayer.reading_files reading files +audioplayer.repeat_on ismétlési mód bekapcsolása +audioplayer.rewind visszatekerés +audioplayer.save_playlist lejátszási lista mentése +audioplayer.screensaver_timeout képernyÅ‘védÅ‘ késleltetése (percben, 0=ki) +audioplayer.select_title_by_name cím névszerinti keresése (SMS) +audioplayer.show_playlist lejátszási lista mutatása +audioplayer.shuffle véletlenszerű lejátszás +audioplayer.stop Ãllj +audioplayer.title_artist cím, elÅ‘adó +audioplayerpicsettings.general Hanglejátszó / KépnézegetÅ‘ +bookmarkmanager.delete törlés +bookmarkmanager.name KönyvjelzÅ‘k +bookmarkmanager.rename átnevezés +bookmarkmanager.select kiválasztás +bouqueteditor.add hozzáadás +bouqueteditor.bouquetname Csomagok nevei +bouqueteditor.delete törlés +bouqueteditor.discardingchanges Vátozások elvetése. Kérem várjon! +bouqueteditor.hide rejtés +bouqueteditor.lock zárolás +bouqueteditor.move mozgatás +bouqueteditor.name CsomagszerkesztÅ‘ +bouqueteditor.newbouquetname Új csomag neve +bouqueteditor.rename átnevezés +bouqueteditor.return vissza +bouqueteditor.savechanges? Kívánja menteni a változásokat? +bouqueteditor.savingchanges Változások mentése. Kérem várjon! +bouqueteditor.switch hozzáadás/eltávolítás +bouqueteditor.switchmode TV/Rádió +bouquetlist.head Csomagok +cablesetup.provider Kábelszolgáltató +channellist.head Összes csatorna +channellist.nonefound A cstornalista üres.\nKérem futtasson egy keresést! +channellist.since %02d:%02d óta +colorchooser.alpha alpha +colorchooser.blue kék +colorchooser.green zöld +colorchooser.red piros +colormenu.background Háttér +colormenu.fade Menük úsztatása +colormenu.font Betűméret +colormenu.gtx_alpha ÃttetszÅ‘ség +colormenu.head Színbeállítások +colormenu.menucolors Menüszínek +colormenu.statusbar Infósor +colormenu.textcolor Betűszín +colormenu.themeselect Témaválasztás +colormenu.timing OSD idÅ‘tartam +colormenusetup.head Menüszínek +colormenusetup.menucontent ablaktartalom +colormenusetup.menucontent_inactive inaktív ablaktartalom +colormenusetup.menucontent_selected kijelölt ablaktartalom +colormenusetup.menuhead Menü fejléc +colorstatusbar.head Infósor +colorstatusbar.text infósor +colorthememenu.classic_theme Klasszikus +colorthememenu.dblue_theme DarkBlue +colorthememenu.dvb2k_theme DVB2000 +colorthememenu.head Témaválasztás +colorthememenu.neutrino_theme Neutrino +date.Apr április +date.Aug augusztus +date.Dec december +date.Feb február +date.Fri péntek +date.Jan január +date.Jul július +date.Jun június +date.Mar március +date.May május +date.Mon hétfÅ‘ +date.Nov november +date.Oct október +date.Sat szombat +date.Sep szeptember +date.Sun vasárnap +date.Thu csütörtök +date.Tue kedd +date.Wed szerda +epglist.head Műsorlista - %s +epglist.noevents Információ nem elérhetÅ‘... +epgviewer.More_Screenings További adások ezen a csatornán: +epgviewer.nodetailed Nincs bÅ‘vebb információ +epgviewer.notfound Műsorfüzet nem elérhetÅ‘ +eventlistbar.channelswitch idÅ‘zítés +eventlistbar.eventsort rendezés +eventlistbar.recordevent felvétel +favorites.addchannel A kiválasztott csatorna hozzáadódik a kedvencekhez.\nEz pár másodpercet vesz igénybe. +favorites.bouquetname Kedvencek +favorites.bqcreated A kedvencek csomag létrejött... +favorites.chadded A kiválasztott csatorna hozzáadódott a kedvencekhez... +favorites.chalreadyinbq A kiválasztott csatorna már benne van a kedvencekben... +favorites.finalhint \nKérem használja a csomagszerkesztÅ‘t a kedvencek módosításához!\n +favorites.menueadd csatorna hozzáadása a kedvencekhez +favorites.nobouquets A kedvencek csak az aktív csomagokra érvényesek. +filebrowser.delete törlés +filebrowser.denydirectoryleave Abszolút kiindulási könyvtár +filebrowser.dodelete1 Törlés +filebrowser.dodelete2 ? +filebrowser.filter.active szűrÅ‘ be +filebrowser.filter.inactive szűrÅ‘ ki +filebrowser.head FájlböngészÅ‘ +filebrowser.mark kijelölés +filebrowser.nextpage következÅ‘ oldal +filebrowser.prevpage elÅ‘zÅ‘ oldal +filebrowser.scan Könyvtár beolvasása +filebrowser.select kiválasztás +filebrowser.showrights Fájljogok kijelzése +filebrowser.sort.date (dátum) +filebrowser.sort.name (fájlnév) +filebrowser.sort.namedirsfirst (fájlnév2) +filebrowser.sort.size (méret) +filebrowser.sort.type (típus) +flashupdate.actionreadflash olvasás +flashupdate.cantopenfile a fájl nem nyitható +flashupdate.cantopenmtd az MTD eszköz nem nyitható +flashupdate.checkupdate új verzió keresése +flashupdate.currentreleasecycle kibocsátási ciklus +flashupdate.currentversion_sep aktuális verzió +flashupdate.currentversiondate dátum +flashupdate.currentversionsnapshot image típusa +flashupdate.currentversiontime idÅ‘ +flashupdate.erasefailed flash törlése sikertelen +flashupdate.erasing Flash törlése +flashupdate.experimentalimage A kiválasztott image kipróbálatlan verzió!\nEz azt jelentheti, hogy a készülék nem indul el a frissítés után.\nBiztos, hogy szeretne frissíteni erre a verzióra? +flashupdate.expertfunctions Haladó funkciók +flashupdate.fileis0bytes A fájlméret 0 Byte +flashupdate.fileselector Fájlválasztó +flashupdate.flashreadyreboot A frissítés sikeresen befejezÅ‘dött.\nA beltéri egység most újraindul. +flashupdate.getinfofile verzióinformáció lekérdezése +flashupdate.getinfofileerror verzióinformáció lekérdezése sikertelen +flashupdate.getupdatefile frissítés letöltése +flashupdate.getupdatefileerror frissítés letöltése sikertelen +flashupdate.globalprogress Összesen: +flashupdate.head Szoftverfrissítés +flashupdate.md5check image ellenÅ‘rzése +flashupdate.md5sumerror az image hibás +flashupdate.msgbox A következÅ‘ image elérhetÅ‘:\ndátum: %s, %s\nalap image: %s\nimage típus: %s\n\nKívánja letölteni és installálni ezt a verziót? +flashupdate.msgbox_manual A következÅ‘ image elérhetÅ‘:\ndátum: %s, %s\nalap image: %s\nimage típus: %s\n\nKívánja installálni ezt a verziót? +flashupdate.mtdselector Partícióválasztó +flashupdate.programmingflash Flash írása +flashupdate.proxypassword jelszó +flashupdate.proxypassword_hint1 írja be a proxy jelszavát! +flashupdate.proxypassword_hint2 üres bejegyzés esetén nincs hitelesítés +flashupdate.proxyserver hostnév +flashupdate.proxyserver_hint1 írja be a proxy nevét vagy IP címét és a portot! (host:port) +flashupdate.proxyserver_hint2 üres bejegyzés esetén nincs proxy +flashupdate.proxyserver_sep Proxy szerver +flashupdate.proxyusername felhasználónév +flashupdate.proxyusername_hint1 írja be a proxy felhasználónevet +flashupdate.proxyusername_hint2 üres bejegyzés esetén nincs hitelesítés +flashupdate.readflash Teljes image kiolvasása +flashupdate.readflashmtd Egy partíció kiolvasása +flashupdate.ready Kész +flashupdate.reallyflashmtd Biztos benne, hogy programoz?\n\nHa hiba történik, vagy az image\nnem érvényes, a készülék nem fog elindulni a frissítés után!\n\Image neve: %s\nCél: %s +flashupdate.savesuccess Az image sikeresen el lett mentve\n%s néven. +flashupdate.selectimage ElérhetÅ‘ image-ek +flashupdate.squashfs.noversion SquashFS version checks are currently only supported when updating over the web.\nAre you sure that you wish to install this image? +flashupdate.titlereadflash Flash olvasása +flashupdate.titlewriteflash Flash írása +flashupdate.updatemode Frissítési mód +flashupdate.updatemode_internet Internet +flashupdate.updatemode_manual Kézi (FTP) +flashupdate.url_file Konfigurációs fájl +flashupdate.versioncheck Verzió ellenÅ‘rzése +flashupdate.writeflash Teljes image írása +flashupdate.writeflashmtd Egy partíció írása +flashupdate.wrongbase Your Release cycle differs, unable to update\nwithout having installed the right version! +fontmenu.channellist Csatornalista +fontmenu.epg EPG +fontmenu.eventlist Műsorfüzet +fontmenu.gamelist Játéklista +fontmenu.head Betűméretek beállítása +fontmenu.infobar Infósor +fontsize.channel_num_zap közvetlen kiválasztás +fontsize.channellist Csatornalista +fontsize.channellist_descr Leírás +fontsize.channellist_number Szám +fontsize.epg_date EPG dátum +fontsize.epg_info1 EPG infó 1 +fontsize.epg_info2 EPG infó 2 +fontsize.epg_title EPG cím +fontsize.eventlist_datetime dátum / idÅ‘ +fontsize.eventlist_itemlarge nagy +fontsize.eventlist_itemsmall kicsi +fontsize.eventlist_title cím +fontsize.filebrowser_item fájlböngészÅ‘ elem +fontsize.gamelist_itemlarge nagy +fontsize.gamelist_itemsmall kicsi +fontsize.hint Betűtípus inicializálása,\nkérem várjon! +fontsize.infobar_channame csatorna neve +fontsize.infobar_info infó +fontsize.infobar_number szám +fontsize.infobar_small kicsi +fontsize.menu menüszöveg +fontsize.menu_info menü infó +fontsize.menu_title menü címsor +gtxalpha.alpha1 alpha 1 +gtxalpha.alpha2 alpha 2 +infoviewer.epgnotload EPG nincs betöltve.... +infoviewer.epgwait Várakozás a műsorinformációra... +infoviewer.eventlist műsorlista +infoviewer.languages hang +infoviewer.motor_moving Motor pozícionálása +infoviewer.nocurrent Nincs elérhetÅ‘ információ a műsorról +infoviewer.noepg Műsorinformáció nem elérhetÅ‘ +infoviewer.notavailable Csatorna nem elérhetÅ‘ +infoviewer.selecttime idÅ‘választás +infoviewer.streaminfo extrák +infoviewer.subservice alcsatornák +infoviewer.waittime Várakozás az idÅ‘re... +ipsetup.hint_1 0..9, vagy fel/le, +ipsetup.hint_2 OK: mentés, LAME/EXIT: mégsem +keybindingmenu.RC Gombnyomás isméltésgátló +keybindingmenu.addrecord Felvételi idÅ‘zítÅ‘ hozzáadása +keybindingmenu.addremind Csatornaváltás-idÅ‘zítÅ‘ hozzáadása +keybindingmenu.allchannels_on_ok minden csatorna +keybindingmenu.bouquetchannels_on_ok aktuális csomag +keybindingmenu.bouquetdown Csomagot vissza +keybindingmenu.bouquethandling Csomagkezelés +keybindingmenu.bouquetlist_on_ok Csomaglista +keybindingmenu.bouquetup KövetkezÅ‘ csomag +keybindingmenu.cancel Csatornalista bezárása +keybindingmenu.channeldown Csatonát le +keybindingmenu.channellist Csatornalista +keybindingmenu.channelup Csatornát fel +keybindingmenu.head TávvezérlÅ‘ +keybindingmenu.lastchannel Gyorsváltás +keybindingmenu.modechange Módválasztás +keybindingmenu.pagedown Oldalt le +keybindingmenu.pageup Oldalt fel +keybindingmenu.quickzap Gyorsváltás +keybindingmenu.repeatblock Ismétlési késleltetés +keybindingmenu.repeatblockgeneric Ãltalános késleltetés +keybindingmenu.sort Rendezési sorrend +keybindingmenu.subchanneldown Alcsatornát le +keybindingmenu.subchannelup Alcsatornát fel +keybindingmenu.tvradiomode TV/Radió mód +keybindingmenu.zaphistory ElÅ‘zÅ‘ csatornák +keychooser.head Új gomb beállítása +keychooser.text1 Kérem nyomja meg a kívánt gombot! +keychooser.text2 Várjon, ha mégsem. +keychoosermenu.currentkey Aktuális gomb +keychoosermenu.setnew Új gomb beállítása +keychoosermenu.setnone Nincs gomb +languagesetup.head Nyelv +languagesetup.select Nyelv +lcdcontroler.brightness Normál fényerÅ‘ +lcdcontroler.brightnessstandby Készenléti fényerÅ‘ +lcdcontroler.contrast kontraszt +lcdcontroler.head KijelzÅ‘ +lcdmenu.autodimm automatikus halványítás +lcdmenu.head LCD beállítások +lcdmenu.inverse Inverz +lcdmenu.lcdcontroler Kontraszt / fényerÅ‘ +lcdmenu.power Világítás +lcdmenu.statusline Megjelenítés +lcdmenu.statusline.both hangerÅ‘ / eltelt idÅ‘ +lcdmenu.statusline.playtime eltelt idÅ‘ +lcdmenu.statusline.volume hangerÅ‘ +mainmenu.audioplayer Hanglejátszó +mainmenu.games Játékok +mainmenu.head FÅ‘menü +mainmenu.movieplayer Videólejátszó +mainmenu.pausesectionsd EPG olvasása +mainmenu.pictureviewer KépnézegetÅ‘ +mainmenu.radiomode Rádió mód +mainmenu.recording Felvétel +mainmenu.recording_start start +mainmenu.recording_stop stop +mainmenu.scartmode SCART mód +mainmenu.service Szolgáltatások +mainmenu.settings Beállítások +mainmenu.shutdown Kikapcsolás +mainmenu.sleeptimer Elalváskapcsoló +mainmenu.tvmode TV mód +mainsettings.audio Hangbeállítások +mainsettings.colors Színek / témák / betűtípusok +mainsettings.head Beállítások +mainsettings.keybinding TávvezérlÅ‘ +mainsettings.language Nyelv +mainsettings.lcd KijelzÅ‘ +mainsettings.misc Egyéb beállítások +mainsettings.network Hálózat +mainsettings.recording Felvétel +mainsettings.savesettingsnow Beállítások mentése +mainsettings.savesettingsnow_hint Mentés folyamatban,\nkérem várjon... +mainsettings.streaming Videólejátszó +mainsettings.video Képbeállítások +menu.back Vissza +messagebox.back Vissza +messagebox.cancel mégsem +messagebox.discard Elveti a változásokat? +messagebox.error Hiba +messagebox.info Információ +messagebox.no nem +messagebox.yes igen +miscsettings.bootinfo infók mutatása indításkor +miscsettings.bootmenu indítási menü bekapcsoláskor +miscsettings.driver_boot Meghajtó és indítási beállítások +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general Ãltalános +miscsettings.head Egyéb beállítások +miscsettings.hwsections hardverszekciók használata +miscsettings.infobar_sat_display Műhold neve az infósoron +miscsettings.pmtupdate PMT frissítés engedélyezése +miscsettings.shutdown_real Készenlét engedélyezése +miscsettings.shutdown_real_rcdelay Késleltetett kikapcsolás +miscsettings.sptsmode SPTS mód használata +miscsettings.startbhdriver BH módú meghajtók betöltése +motorcontrol.head Motorbeállítások +movieplayer.bookmark könyvjelzÅ‘k +movieplayer.bookmarkname könyvjelzÅ‘ neve +movieplayer.bookmarkname_hint1 Kérem adjon nevet az új könyvjelzÅ‘nek! +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffering... +movieplayer.defdir kiindulási könyvtár +movieplayer.dvdplayback DVD +movieplayer.fileplayback Fájl VLC-n keresztül +movieplayer.goto Ugrás... +movieplayer.goto.h1 = -> abszolút ugrás +movieplayer.goto.h2 +,- -> relatív ugrás +movieplayer.head Videólejátszó +movieplayer.nostreamingserver A streamingszerver nem elérhetÅ‘. +movieplayer.pesplayback PES lejátszása (kísérleti) +movieplayer.pleasewait Kérem várjon!\nCsatlakozás a streamingszerverhez... +movieplayer.toomanybookmarks Túl sok könyvjelzÅ‘.\nElÅ‘bb törölni kell egyet. +movieplayer.tsplayback TS lejátszása +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion Ez a funkció nem elérhetÅ‘ a VLC jelenlegi verziójával. +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway Ãtjáró +networkmenu.head Hálózat +networkmenu.ipaddress IP cím +networkmenu.mount NFS/CIFS +networkmenu.nameserver DNS +networkmenu.netmask Netmaszk +networkmenu.setupnow Beállítások aktualizálása +networkmenu.setuponstartup Hálózat inicializálása indításkor +networkmenu.show Jelenlegi beállítások mutatása +networkmenu.test Hálozat tesztelése +nfs.alreadymounted directory already mounted +nfs.automount mount on startup +nfs.dir directory/share +nfs.ip NFS/CIFS Server IP +nfs.localdir local dir +nfs.mount Mount NFS/CIFS volume +nfs.mount_options mount options +nfs.mounterror mount error +nfs.mounterror_notsup filesystem type not supported +nfs.mountnow mount now +nfs.mounttimeout mount error: timeout +nfs.password CIFS password +nfs.remount remount directories +nfs.type type +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount Unmount NFS/CIFS volume +nfs.umounterror error umounting volume +nfs.username CIFS username +nfsmenu.head NFS/CIFS settings +nvod.percentage (%d%% over) +nvod.starting (kezdés %d pecr múlva) +nvodselector.directormode közvetlen elérési mód +nvodselector.head Kezdési idÅ‘pont kiválasztása +nvodselector.subservice Alcsatorna kiválasztása +options.default Alapértelmezett értékek +options.fb képernyÅ‘ +options.null null +options.off ki +options.on be +options.serial serial +parentallock.changepin PIN megváltoztatása +parentallock.changepin_hint1 Ide üsse be az új PIN kódot! +parentallock.changetolocked on locked bouquets +parentallock.head Ãœsse be a PIN kódot! +parentallock.lockage Program zárolása +parentallock.lockage12 12 évtÅ‘l +parentallock.lockage16 16 évtÅ‘l +parentallock.lockage18 18 évtÅ‘l +parentallock.lockedchannel Zárolt csatorna... +parentallock.lockedprogram Zárolt műsor (%d évtÅ‘l) +parentallock.never soha +parentallock.onsignal műsor szerint +parentallock.parentallock Gyermekzár +parentallock.prompt PIN kód kérése +pictureviewer.defdir kiindulási könyvtár +pictureviewer.head KépnézegetÅ‘ +pictureviewer.resize.color_average fejlett +pictureviewer.resize.none nincs +pictureviewer.resize.simple egyszerű +pictureviewer.scaling méretezés +pictureviewer.show mutatás +pictureviewer.slide_time slideshow display time +pictureviewer.slideshow vetítés +pictureviewer.sortorder rendezési sorrend +pictureviewer.sortorder.date (dátum) +pictureviewer.sortorder.filename (fájlnév) +ping.ok elérhetÅ‘ (ping) +ping.protocol nem elérhetÅ‘ (host vagy protokoll hiba) +ping.socket nem elérhetÅ‘ (socket hiba) +ping.unreachable nem elérhetÅ‘ +pinprotection.head Ãœsse be a PIN kódot +pinprotection.wrongcode A PIN kód hibás! Próbálja újra! +rclock.lockmsg A távvezérlÅ‘ zárolva lesz.\nA nyitáshoz nyomja meg a PIROS gombot,\nmajd a MENU/DBOX gombot! +rclock.menueadd TávvezérlÅ‘-zár +rclock.title TávvezérlÅ‘ zárolása +rclock.unlockmsg TávvezérlÅ‘ aktiválva +recordingmenu.defdir felvételi könyvtár +recordingmenu.file közvetlen (fájl) +recordingmenu.head Felvétel +recordingmenu.help Felvételi eszközök:\n--------------------------\nszerver:\nstreaming szoftver használata a PC-n\n\n(analóg) képmagnó:\nképmagnó használata\n\nközvetlen (fájl):\nközvetlen felvétel NFS könyvtárba,\nvagy beépített merevlemezre\n\n\nMax. fájlméret:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: elég nagy :) (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart ne kapcsoljon SCART módba +recordingmenu.off ki +recordingmenu.recording_type Felvételi eszköz +recordingmenu.server Szerver +recordingmenu.server_ip Felvételi szerver IP címe +recordingmenu.server_mac MAC cím +recordingmenu.server_port Felvételi szerver portja +recordingmenu.server_wakeup Felvételi szerver WOL +recordingmenu.setupnow Beállítások aktualizálása +recordingmenu.splitsize Max. fájlméret (MB) +recordingmenu.stopplayback Lejátszás megállítása +recordingmenu.stopsectionsd SectionSD megállítása +recordingmenu.stream_vtxt_pid Teletext rögzítése +recordingmenu.use_o_sync Szinkron írás (O_SYNC) +recordingmenu.vcr Képmagnó +recordtimer.announce A felvétel néhány percen belül kezdÅ‘dik +repeatblocker.hint_1 A legrövidebb idÅ‘ (ms-ban) két gombnyomás felismerésére +repeatblocker.hint_2 0 érték kikapcsolja a blokkolót (PIROS gomb: szóköz) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-ismétlések +satsetup.extended DiSEqC beállítások +satsetup.extended_motor Motorbeállítások +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Kézi beállítás +satsetup.nodiseqc nincs DiSEqC +satsetup.satellite Műhold +satsetup.savesettingsnow Beállítások mentése +satsetup.smatvremote SMATV távhangolás +scants.actcable Kábel: +scants.actsatellite Műhold: +scants.bouquet Csomagok kezelése +scants.bouquet_create újak készítése +scants.bouquet_erase összes törlése +scants.bouquet_leave jelenlegiek megtartása +scants.bouquet_satellite műhold-csomagok +scants.bouquet_update frissítés +scants.channel Csatorna: +scants.failed A transzponderkeresés sikertelen! +scants.finished A transponderkeresés sikereresen befejezÅ‘dött! +scants.freqdata Frekvencia: +scants.head Transzponderkeresés +scants.numberofdataservices Adat +scants.numberofradioservices Rádio +scants.numberoftotalservices Összesen +scants.numberoftvservices TV +scants.provider Szolgáltató: +scants.startnow Keresés indítása +scants.transponders Transzponder: +screensetup.lowerright ZÖLD = jobb alsó +screensetup.upperleft PIROS = bal felsÅ‘ +servicemenu.head Szolgáltatások +servicemenu.reload Csatornalista újratöltése +servicemenu.reload_hint Újratöltés. Kérem várjon! +servicemenu.scants Csatornakeresés +servicemenu.ucodecheck Mikrokódok ellenÅ‘rzése +servicemenu.update Szoftverfrissítés +settings.help Segítség +settings.missingoptionsconffile A konfigurációs fájl hiányzik.\nAz összes érték alapértelmezettre lesz állítva. +settings.noconffile A beállítások hiányoznak.\nAlapértékek használata. +shutdowntimer.announce A készülék 1 percen belül kikapcsol.\nMegszakítja a folyamatot? +sleeptimerbox.announce Elalváskapcsoló 1 perc múlva +sleeptimerbox.hint1 Kikapcsolási idÅ‘ percben. (000=ki) +sleeptimerbox.hint2 A készülék ennyi idÅ‘ múlva fog kikapcsolni. +sleeptimerbox.title Elalváskapcsoló +streamfeatures.head Features +streaminfo.aratio képarány +streaminfo.aratio_unknown képarány: ismeretlen +streaminfo.audiotype hangtípus +streaminfo.audiotype_unknown hangtípus: ismeretlen +streaminfo.bitrate bitráta +streaminfo.framerate képkocka-ráta +streaminfo.framerate_unknown képkocka-ráta: ismeretlen +streaminfo.head Adatfolyam-információk +streaminfo.not_available nem elérhetÅ‘ +streaminfo.resolution felbontás +streaminfo.signal Vett jel +streaming.buffer_overflow A felvétel meg lett szakítva,\nmert az adatot nem lehetett eléggé gyorsan írni. +streaming.busy One or several recording processes are active.\nIf you encounter this message and no recording is active, please restart Neutrino. +streaming.dir_not_writable A felvételi könyvtár nem írható.\nA felvétel nem fog működni. +streaming.success A felvétel sikeresen befejezÅ‘dött. +streaming.write_error A felvétel meg lett szakítva,\nmert hiba történt az írásnál. +streaming.write_error_open A felvétel meg lett szakítva,\nmert a célfájlt nem lehet megnyitni. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Videólejátszó +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off ki +streamingmenu.on be +streamingmenu.server_ip streaming szerver IP címe +streamingmenu.server_port streaming szerver portja +streamingmenu.streaming_audiorate hang bitráta +streamingmenu.streaming_force_avi_rawaudio Force AC3 for AVI +streamingmenu.streaming_force_transcode_video MPG/VCD videó áttömörítése +streamingmenu.streaming_resolution Felbontás +streamingmenu.streaming_server_cddrive DVD meghajtó +streamingmenu.streaming_server_startdir könyvtár (VLC) +streamingmenu.streaming_transcode_audio Hang áttömörítése (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG videókodek +streamingmenu.streaming_type Streaming szerver +streamingmenu.streaming_videorate videó adatráta +streamingserver.noconnect Nincs kapcsolat a streaming szerverrel.\nFelvétel megszakítva. +stringinput.caps nagybetű / kisbetű +stringinput.clear összes törlése +timer.eventrecord.msg ... TO be DONE, or not to be done +timer.eventrecord.title Felvétel idÅ‘zítés +timer.eventtimed.msg A műsor idÅ‘zítve van.\nA készülék be fog kapcsolni és\nát fog kapcsolni a megadott csatornára a megadott idÅ‘ben. +timer.eventtimed.title Műsor idÅ‘zítés +timerbar.channelswitch IdÅ‘zítés +timerbar.recordevent Felvétel +timerlist.alarmtime Művelet idÅ‘pontja +timerlist.apids Hang PID-ek +timerlist.bouquetselect Csomagok +timerlist.channel Csatorna +timerlist.channelselect Csatornák +timerlist.delete törlés +timerlist.menumodify módosítás +timerlist.menunew új idÅ‘zítÅ‘ +timerlist.message üzenet +timerlist.moderadio Rádiócsatornák +timerlist.modeselect Ãœzemmód választás +timerlist.modetv Tévécsatornák +timerlist.modify módosítás +timerlist.name IdÅ‘zítÅ‘lista +timerlist.new új idÅ‘zítÅ‘ +timerlist.program.unknown ismeretlen műsor +timerlist.reload újratöltés +timerlist.repeat ismétlés +timerlist.repeat.biweekly kéthetente +timerlist.repeat.byeventdescription lásd az idÅ‘zítÅ‘t +timerlist.repeat.daily naponta +timerlist.repeat.fourweekly négyhetente +timerlist.repeat.friday P +timerlist.repeat.monday H +timerlist.repeat.monthly havonta +timerlist.repeat.once egyszer +timerlist.repeat.saturday SZ +timerlist.repeat.sunday V +timerlist.repeat.thursday CS +timerlist.repeat.tuesday K +timerlist.repeat.unknown ismeretlen +timerlist.repeat.wednesday SZ +timerlist.repeat.weekdays hét adott napjain +timerlist.repeat.weekly hetente +timerlist.save IdÅ‘zítÅ‘ mentése +timerlist.standby készenléti üzemmód +timerlist.standby.off készenlét elhagyása +timerlist.standby.on készenlétbe menetel +timerlist.stoptime Megállás idÅ‘pontja +timerlist.type IdÅ‘zítÅ‘ típusa +timerlist.type.nextprogram KövetkezÅ‘ műsor +timerlist.type.record felvétel +timerlist.type.remind emlékeztetÅ‘ +timerlist.type.shutdown kikapcsolás +timerlist.type.sleeptimer elalváskapcsoló +timerlist.type.standby készenlét +timerlist.type.unknown ismeretlen +timerlist.type.zapto csatornaváltás +timerlist.weekdays A hét napjai +timerlist.weekdays.hint_1 H K SZ CS P SZ V +timerlist.weekdays.hint_2 igen:X, nem:- +timersettings.record_safety_time_after Megállás idejének korrekciója +timersettings.record_safety_time_after.hint_1 Correction time in min. (00=off). This time +timersettings.record_safety_time_after.hint_2 will added to stop time of every record timer. +timersettings.record_safety_time_before Record start time correction +timersettings.record_safety_time_before.hint_1 Correction time in min. (00=off). This time +timersettings.record_safety_time_before.hint_2 will be deducted of every record timer. +timersettings.separator IdÅ‘zítÅ‘ beállítások +timing.chanlist Csatornalista +timing.epg EPG +timing.filebrowser FájlböngészÅ‘ +timing.head OSD idÅ‘tartam +timing.hint_1 Time in sec. After this time the +timing.hint_2 infobar will be faded out. +timing.infobar Infósor +timing.menu Menü +timing.numericzap Numerikus váltás +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head Mikrokód ellenÅ‘rzés +ucodecheck.ucode Mikrokód +ucodes.failure Figyelem! A mikrokódok hiányoznak!\n\nKérem töltse fel Å‘ket a készülékbe,\nmajd indítson újra! +videomenu.csync Szinkronkorrekció +videomenu.head Képbeállítások +videomenu.rgb_centering RGB középpont +videomenu.screensetup KépernyÅ‘-beállítás +videomenu.vcrswitch SCART automatikus kapcsolása +videomenu.videoformat Formátum +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect automatikus +videomenu.videosignal Videójel +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Csatornaváltás 1 perc múlva +extra.auto automatikus +extra.valid érvényes +extra.all összes +extra.new_keys új kulcsok +extra.all_keys minden kulcs +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo csak ismerteket +extra.gbox_emu emu +extra.gbox_softcam softcam +extra.gbox_net hálózat +extra.gbox_mix kevert +extra.gbox_info GBOX infó +extra.sep_extra Extrák +extra.save_settings Beállítások mentése +extra.extra_menu Kiegészítések +extra.ecm_info ECM infó +extra.pid_info PID infó +extra.emm_info Új kulcsok +extra.emu_type Emu típusa +extra.camd CAMD +extra.restart_camd CAMD újraindítása +extra.mg_settings Mgcamd beállítások +extra.netmode netmód +extra.autoupdate autoupdate +extra.show_ecm ECM kijelzése +extra.show_emm EMM kijelzése +extra.keyupdate kulcsfrissítés +extra.osd OSD +extra.keyfolder Kulcsok könyvtára +extra.hash_pids PID-ek ellenÅ‘rzése +extra.gbox_settings GBOX Settings +extra.gbox_mode Ãœzemmód +extra.newcamd_settings Newcamd beállítások +extra.show_cw CW kijelzése +extra.debug_ecm ECM debug +extra.disable_cam CAM letiltása +extra.show_cat Show CAT +extra.show_pmt show PMT +extra.update_pmt Update PMT +extra.report_emm Report wrong EMM sig. +extra.debug_emm Debug Emm +extra.show_allca Show all ca sys. +extra.reload_keys Reload keys on zap +extra.reload_cfg Reload config on zap +extra.cw_delay Default CW delay +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Show PAT +extra.dvbsnoop_cat Show CAT +extra.dvbsnoop_tsdt Show TSDT +extra.dvbsnoop_nit Show NIT +extra.dvbsnoop_sdt Show SDT +extra.dvbsnoop_eit Show EIT +extra.dvbsnoop_rst Show RST +extra.dvbsnoop_tdt Show TDT +extra.dvbsnoop_sit Show SIT +extra.dvbsnoop_pid Show manual PID +extra.dvbsnoop_scan Scan PIDs +extra.dvbsnoop_band Show pid bandwidth +extra.logview View Log +extra.ru ruDREAM +extra.english Automatikus angol nyelv +extra.dboxinfo Készülék infó +extra.scan_mode Keresési mód +extra.start_tostandby Készenlét bekapcsolás után +extra.rotor_swap Kelet/Nyugat felcserélése +extra.use_log Naplózás a /tmp/log -ba +extra.spts_mode SPTS mód +extra.hw_sect Hardwareszekciók +extra.logo Logó száma +extra.scan_full teljes +extra.scan_fast gyors +extra.hdd_slow lassú +extra.hdd_middle közepes +extra.hdd_fast gyors +extra.hdd_ext3 Ext3FS +extra.hdd_reiser ReiserFS +extra.hdd_1min 1 perc +extra.hdd_5min 5 perc +extra.hdd_10min 10 perc +extra.hdd_20min 20 perc +extra.hdd_30min 30 perc +extra.hdd_60min 60 perc +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Elalvási idÅ‘ +extra.hdd_noise Teljesítmény/zaj +extra.hdd_activate Beállítások aktualizálása +extra.hdd_fs Fájlrendszer +extra.hdd_format Merevlemez formázása +extra.hdd_check Fájlrendszer ellenÅ‘rzése +extra.hdd_settings Merevlemez-beállítások +extra.clear_log Napló törlése +extra.zap_cycle Körbejárás +extra.sms_channel SMS csatornaváltás +extra.manual_scan Kézi keresés +extra.tp_freq Frekvencia +extra.tp_rate Symbol Rate +extra.tp_pol Polarizáció +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone IdÅ‘zóna +extra.zapit_menu Zapit beállítások +extra.zapit_make_bouquet Csomagokon kívüli csatornák +extra.zapit_save_last_chan Utolsó pozíció mentése +extra.zapit_motor_speed Forgató sebessége +extra.zapit_fe_timeout Hanglolásra várakozás +extra.add_to_bouquet másolás csomagba +extra.key_list_start eleje +extra.key_list_end vége +extra.chadded A csatorna hozzá lett adva a kiválasztott csomaghoz...\n +extra.chalreadyinbq A csatorna már benne van a kiválasztott csomagban...\n +extra.menu_left_exit "balra" = visszalépés a menüben +extra.zapit_write_names Csatornanevek mentése +extra.update_dir Könyvtár a frissítésekhezextra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +extra.cache Cache +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Keresés megszakítása +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/italiano.locale b/data/locale/italiano.locale new file mode 100644 index 000000000..3bd4a0937 --- /dev/null +++ b/data/locale/italiano.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus Lista Programmi +EPGMenu.eventinfo Dettagli programma corrente +EPGMenu.eventlist Lista programmi canale corrente +EPGMenu.head EPG - Informazioni programmi (EPG Plus) +EPGMenu.streaminfo informazioni tecniche +EPGPlus.actions Azioni +EPGPlus.bybouquet_mode per bouquet +EPGPlus.bypage_mode per pagina +EPGPlus.change_font_size dimensioni caratteri +EPGPlus.change_font_style stile caratteri +EPGPlus.change_size dimensioni +EPGPlus.channelentry_font caratteri dei canali +EPGPlus.channelentry_separationlineheight separazione linea superiore +EPGPlus.channelentry_width width +EPGPlus.channelevententry_font event font +EPGPlus.edit_fonts edita caratteri +EPGPlus.edit_sizes edita dimensioni +EPGPlus.font_style_bold style bold +EPGPlus.font_style_italic style italic +EPGPlus.font_style_regular style regular +EPGPlus.footer_fontbouquetchannelname footer bouquet +EPGPlus.footer_fontbuttons footer buttons +EPGPlus.footer_fonteventdescription footer event +EPGPlus.footer_fonteventshortdescription footer event short +EPGPlus.head Informazioni programmi (EPG Plus) +EPGPlus.header_font header dei caratteri +EPGPlus.horgap1_height height1 +EPGPlus.horgap2_height height2 +EPGPlus.next_bouquet bouquet succes. +EPGPlus.options opzioni +EPGPlus.page_down pagina in basso +EPGPlus.page_up pagina in alto +EPGPlus.prev_bouquet bouquet preced. +EPGPlus.record Registrare +EPGPlus.refresh_epg Attualizzare +EPGPlus.remind Ricordare +EPGPlus.reset_settings reset settings +EPGPlus.save_settings salva settings +EPGPlus.scroll_mode Modo Scroll +EPGPlus.select_font_name font name +EPGPlus.settings Impostazioni +EPGPlus.slider_width slider width +EPGPlus.stretch_mode Stretch Mode +EPGPlus.swap_mode selezione +EPGPlus.timeline_fontdate font date +EPGPlus.timeline_fonttime font time +EPGPlus.vergap1_width width1 +EPGPlus.vergap2_width width2 +EPGPlus.view_mode visione +GENRE.ARTS.0 arte / cultura +GENRE.ARTS.1 performing arts +GENRE.ARTS.10 arte/cultura magazines +GENRE.ARTS.11 moda +GENRE.ARTS.2 fine arts +GENRE.ARTS.3 religione +GENRE.ARTS.4 cultura popolare/arti tradizionali +GENRE.ARTS.5 letteratura +GENRE.ARTS.6 film/cinema +GENRE.ARTS.7 cinema sperimentale/video +GENRE.ARTS.8 broadcasting/stampa +GENRE.ARTS.9 new media +GENRE.CHILDRENs_PROGRAMMES.0 ragazzi/ programmi per ragazzi +GENRE.CHILDRENs_PROGRAMMES.1 programmi ragazzi prescolarizzazione +GENRE.CHILDRENs_PROGRAMMES.2 intrattenimento 6 - 14 anni +GENRE.CHILDRENs_PROGRAMMES.3 intrattenimento adolescenti +GENRE.CHILDRENs_PROGRAMMES.4 informazione +GENRE.CHILDRENs_PROGRAMMES.5 cartoni animati +GENRE.DOCUS_MAGAZINES.0 documentari / magazine +GENRE.DOCUS_MAGAZINES.1 natura/animali +GENRE.DOCUS_MAGAZINES.2 scienze +GENRE.DOCUS_MAGAZINES.3 medicina/filosofia/psicologia +GENRE.DOCUS_MAGAZINES.4 viaggi/spedizioni +GENRE.DOCUS_MAGAZINES.5 sciene sociospirituali +GENRE.DOCUS_MAGAZINES.6 educazione +GENRE.DOCUS_MAGAZINES.7 lingue +GENRE.MOVIE.0 film tv/drammi +GENRE.MOVIE.1 thriller +GENRE.MOVIE.2 avventura/western/guerra +GENRE.MOVIE.3 fantascienza +GENRE.MOVIE.4 commedia +GENRE.MOVIE.5 melodrammi +GENRE.MOVIE.6 romantici +GENRE.MOVIE.7 classici +GENRE.MOVIE.8 film per adulti +GENRE.MUSIC_DANCE.0 musica/danza/balletto +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 classica +GENRE.MUSIC_DANCE.3 musica folcloristica +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 balletto +GENRE.NEWS.0 news +GENRE.NEWS.1 news/weather report +GENRE.NEWS.2 news magazine +GENRE.NEWS.3 documentari +GENRE.NEWS.4 discussioni/interviste/dibattiti +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 game show/quiz/contest +GENRE.SHOW.2 variet +GENRE.SHOW.3 talk show +GENRE.SOCIAL_POLITICAL.0 social & politic events / business +GENRE.SOCIAL_POLITICAL.1 magazines/reports/documentary +GENRE.SOCIAL_POLITICAL.2 economics/social advisory +GENRE.SOCIAL_POLITICAL.3 remarkable people +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 eventi speciali (Giochi olimpici,Campionati del mondo etc.) +GENRE.SPORTS.10 equitazione +GENRE.SPORTS.11 sport combattimento +GENRE.SPORTS.2 sports magazines +GENRE.SPORTS.3 calcio/footeball americano +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 sport di squadra (eccetto il calcio) +GENRE.SPORTS.6 atletica +GENRE.SPORTS.7 sports motoristici +GENRE.SPORTS.8 sports acquatici +GENRE.SPORTS.9 winter sports +GENRE.TRAVEL_HOBBIES.0 viaggi e ricreazione +GENRE.TRAVEL_HOBBIES.1 turismo/viaggi +GENRE.TRAVEL_HOBBIES.2 handicraft +GENRE.TRAVEL_HOBBIES.3 motoring +GENRE.TRAVEL_HOBBIES.4 fitness & health +GENRE.TRAVEL_HOBBIES.5 cucina +GENRE.TRAVEL_HOBBIES.6 shopping +GENRE.TRAVEL_HOBBIES.7 giardinaggio +GENRE.UNKNOWN sconosciuto +apids.hint_1 Inserisci APIDs +apids.hint_2 in hex notation seperated by ' ' +apidselector.head Seleziona la lingua +audiomenu.PCMOffset Diminuisci il Volume PCM +audiomenu.analogout Uscita Analogica +audiomenu.avs avs +audiomenu.avs_control Volume Control avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Audio Settings +audiomenu.lirc lirc +audiomenu.monoleft mono sinistro +audiomenu.monoright mono destro +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add aggiungi +audioplayer.artist_title Artista, Titolo +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id search by ID +audioplayer.button_select_title_by_name search by name +audioplayer.defdir Start-Directory. +audioplayer.delete rimuovi +audioplayer.deleteall Cancella tutti +audioplayer.display_order Visore +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward Avanti svelto +audioplayer.follow Selezionare Track attuale +audioplayer.head Audio Playlist +audioplayer.highprio High decode priority +audioplayer.id3scan leggo ID3-Tags +audioplayer.jump_backwards jump backwards +audioplayer.jump_dialog_hint1 Please enter jump target +audioplayer.jump_dialog_hint2 (relative, in seconds) +audioplayer.jump_dialog_title Enter jump target +audioplayer.jump_forwards jump forwards +audioplayer.keylevel Cambio-Tasti +audioplayer.name Audioplayer +audioplayer.pause Pausa +audioplayer.play play +audioplayer.playing Brano attuale +audioplayer.playlist_fileerror_msg File could not be created: +audioplayer.playlist_fileerror_title Errore +audioplayer.playlist_fileoverwrite_msg Vuoi sovrascrivere questo file: +audioplayer.playlist_fileoverwrite_title Sovrascrivere? +audioplayer.playlist_name filename of the play list +audioplayer.playlist_name_hint1 Please enter the filename of the playlist +audioplayer.playlist_name_hint2 The extension .m3u will be added automatically +audioplayer.reading_files reading files +audioplayer.repeat_on enable repeat mode +audioplayer.rewind Indietro svelto +audioplayer.save_playlist save play list +audioplayer.select_title_by_name search title by name (SMS) +audioplayer.screensaver_timeout Salvaschermo_Timeout +audioplayer.show_playlist Show Playlist +audioplayer.shuffle shuffle +audioplayer.stop stop +audioplayer.title_artist Titolo, Artista +audioplayerpicsettings.general audioplayer / picviewer +bookmarkmanager.delete cancella +bookmarkmanager.name bookmarks +bookmarkmanager.rename cambia nome +bookmarkmanager.select seleziona +bouqueteditor.add Aggiungi +bouqueteditor.bouquetname Nome del bouquets +bouqueteditor.delete Cancella +bouqueteditor.discardingchanges Non salvare i cambiamenti. Attendi .. +bouqueteditor.hide Nascondi +bouqueteditor.lock Blocca +bouqueteditor.move Sposta +bouqueteditor.name gestione bouquets +bouqueteditor.newbouquetname Nuovo nome del bouquets +bouqueteditor.rename Rinomina +bouqueteditor.return terminato +bouqueteditor.savechanges? vuoi salvare le modifiche? +bouqueteditor.savingchanges Salva le modifiche.Attendi ... +bouqueteditor.switch aggiungere/rimuovere +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider providers via cavo +channellist.head Tutti i canali +channellist.nonefound Non ci sono canali!\n\nEseguire una Scansione\n(DBOX-Tasto -> Servizio) +channellist.since since %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blu +colorchooser.green verde +colorchooser.red rosso +colormenu.background Colori di fondo +colormenu.fade Fader +colormenu.font Dimensioni dei caratteri +colormenu.gtx_alpha Transparenza (GTX) +colormenu.head Impostazioni dei colori +colormenu.menucolors Colori Menu +colormenu.statusbar Barra d'informazione +colormenu.textcolor Colori Testo +colormenu.themeselect seleziona tema +colormenu.timing OSD Timing +colormenusetup.head Colori Menu +colormenusetup.menucontent Contenuto Menu +colormenusetup.menucontent_inactive Contenuto Menu disattivato +colormenusetup.menucontent_selected Contenuto Menu attivato +colormenusetup.menuhead Menu Header +colorstatusbar.head Barra d'informazione +colorstatusbar.text Barra d'informazione +colorthememenu.classic_theme Tema Classico +colorthememenu.dblue_theme Tema nerazzurro +colorthememenu.dvb2k_theme DVB2000 Tema +colorthememenu.head Seleziona tema +colorthememenu.neutrino_theme Tema Neutrino +date.Apr Apr +date.Aug Ago +date.Dec Dic +date.Feb Feb +date.Fri Ven +date.Jan Gen +date.Jul Lug +date.Jun Giu +date.Mar Mar +date.May Mag +date.Mon Lun +date.Nov Nov +date.Oct Ott +date.Sat Sab +date.Sep Set +date.Sun Dom +date.Thu Gio +date.Tue Mar +date.Wed Mer +epglist.head Lista programmi - %s +epglist.noevents nessuna informazione EPG... +epgviewer.More_Screenings Programmi successivi su questo canale +epgviewer.nodetailed Nessuna informazione dettagliata +epgviewer.notfound Epg non presente +eventlistbar.channelswitch schedule +eventlistbar.eventsort esci +eventlistbar.recordevent registra +favorites.addchannel Questo canale sara aggiunto al Bouquet \n\n"Miei Favoriti" . \nOperazione necessita di un po di tempo..attendere +favorites.bouquetname Miei Favoriti +favorites.bqcreated Bouquet "Miei Favoriti" e stato creato...\n +favorites.chadded Il Canale attuale e stato aggiunto nei Favoriti...\n +favorites.chalreadyinbq Il Canale attuale e gia fra i Favoriti...\n +favorites.finalhint \nUsa bouqueteditor per modificare i favoriti.\n +favorites.menueadd aggiungi canale nei favoriti +favorites.nobouquets I favoriti sono selezionabili solo fra i bouquets attivi +filebrowser.delete Cancella +filebrowser.denydirectoryleave Absolute start directory +filebrowser.dodelete1 Cancella +filebrowser.dodelete2 ? +filebrowser.filter.active Filtro Attivo +filebrowser.filter.inactive Filtro Disattivo +filebrowser.head Filebrowser +filebrowser.mark seleziona +filebrowser.nextpage Pagina successiva +filebrowser.prevpage Pagina precedente +filebrowser.scan cerca nelle cartelle +filebrowser.select Seleziona +filebrowser.showrights Caratteristiche del file +filebrowser.sort.date (data) +filebrowser.sort.name (nome del file) +filebrowser.sort.namedirsfirst (nome del file2) +filebrowser.sort.size (Grandezza) +filebrowser.sort.type (tipo) +flashupdate.actionreadflash leggo flash +flashupdate.cantopenfile impossibile aprire il file +flashupdate.cantopenmtd impossibile aprire mtd-device +flashupdate.checkupdate ricerca nuove versioni +flashupdate.currentreleasecycle Release cycle +flashupdate.currentversion_sep Versione installata +flashupdate.currentversiondate Data +flashupdate.currentversionsnapshot ImagineTipo +flashupdate.currentversiontime Orario +flashupdate.erasefailed cancellazione flash fallita +flashupdate.erasing cancella flash +flashupdate.experimentalimage hai scelto un firmware sperimentale non testato +flashupdate.expertfunctions funzioni avanzate +flashupdate.fileis0bytes la dimensione del file e 0 Bytes +flashupdate.fileselector Seleziona file +flashupdate.flashreadyreboot immagine installata con successo il ruDbox si riavvia +flashupdate.getinfofile Carica Info Versione +flashupdate.getinfofileerror impossibile caricare info versione +flashupdate.getupdatefile carica aggiornamento +flashupdate.getupdatefileerror impossibile caricare aggiornamento +flashupdate.globalprogress Stato del sistema: +flashupdate.head Aggiornamento Software +flashupdate.md5check controllo immagine +flashupdate.md5sumerror immagine contiene errori +flashupdate.msgbox Sono state trovate queste file:\nDate: %s, %s\nBaseImage: %s\nType: %s\n\nDo you want to download and install this version now? +flashupdate.msgbox_manual Trovata una nuova img:\nDate: %s, %s\nBaseImage: %s\nImageType: %s\n\nDo you want to install this version now? +flashupdate.mtdselector Scelta della partizione +flashupdate.programmingflash programma flash +flashupdate.proxypassword Password +flashupdate.proxypassword_hint1 inserisci la password del server proxy +flashupdate.proxypassword_hint2 a empty entry means no proxy +flashupdate.proxyserver Nome del proxy +flashupdate.proxyserver_hint1 inserisci nome del proxy o IP, usa host:port +flashupdate.proxyserver_hint2 nessun nome inserito no proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Username +flashupdate.proxyusername_hint1 iserisci proxyserver username +flashupdate.proxyusername_hint2 nessun nome inserito nessuna identificazione +flashupdate.readflash Leggi tutta img su flash +flashupdate.readflashmtd leggi singole parti +flashupdate.ready terminato +flashupdate.reallyflashmtd Vuoi veramente flashare il db?\n\in caso d'errore\nil ruDbox potr non riavviarsi.\n\nImagename: %s\nTarget: %s +flashupdate.savesuccess L'immagine stata salvata con successo con\nquesto nome %s. +flashupdate.selectimage Immagini/Files disponibili +flashupdate.squashfs.noversion SquashFS version checks are currently only supported when updating over the web.\nAre you sure that you wish to install this image? +flashupdate.titlereadflash Leggi Flash +flashupdate.titlewriteflash Scrivi Flash +flashupdate.updatemode Modalita aggiornamento +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manuale (ftp) +flashupdate.url_file configurazione +flashupdate.versioncheck controlla versione +flashupdate.writeflash Scrivi l immagine completa +flashupdate.writeflashmtd Scrivi singole parti +flashupdate.wrongbase flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Lista Canali +fontmenu.epg Titolo EPG +fontmenu.eventlist Lista programmi +fontmenu.gamelist Lista giochi +fontmenu.head Impostazioni dei caratteri +fontmenu.infobar Barra d informazione +fontsize.channel_num_zap selezione diretta +fontsize.channellist Lista Canali +fontsize.channellist_descr Descrizione +fontsize.channellist_number Numero +fontsize.epg_date EPG Data +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title Titolo EPG +fontsize.eventlist_datetime lista programmi data / ora +fontsize.eventlist_itemlarge larga +fontsize.eventlist_itemsmall stretta +fontsize.eventlist_title Lista eventi +fontsize.filebrowser_item vedi filebrowser +fontsize.gamelist_itemlarge largo +fontsize.gamelist_itemsmall piccolo +fontsize.hint Inizializzazione caratteri,\nattendere... +fontsize.infobar_channame Nome dei canali +fontsize.infobar_info informazioni barra d informazione +fontsize.infobar_number Numeri barra d informazione +fontsize.infobar_small piccolo +fontsize.menu testo menu +fontsize.menu_info Menu Info +fontsize.menu_title Titolo menu +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG non caricata.... +infoviewer.epgwait attendi per EPG... +infoviewer.eventlist Lista programmi +infoviewer.languages Audio +infoviewer.motor_moving Posizionamento antenna +infoviewer.nocurrent Non disponibili informazioni su questo programma +infoviewer.noepg EPG non presente +infoviewer.notavailable Canale non presente +infoviewer.selecttime Orario di inizio +infoviewer.streaminfo Informazioni +infoviewer.subservice Sottoservizi +infoviewer.waittime Attendo orario... +ipsetup.hint_1 Usare il 0..9 per Alto/Basso +ipsetup.hint_2 OK salva, HOME indietro +keybindingmenu.RC Telecomando +keybindingmenu.addrecord Aggiungi-Timer di Registrazione +keybindingmenu.addremind Aggiungi-Timer per cambio canale +keybindingmenu.allchannels_on_ok Lista canali +keybindingmenu.bouquetchannels_on_ok Bouquet-Canali +keybindingmenu.bouquetdown bouquet precedente +keybindingmenu.bouquethandling Tasto-OK-per +keybindingmenu.bouquetlist_on_ok Lista Bouquet +keybindingmenu.bouquetup bouquet successivo +keybindingmenu.cancel chiudi lista canali +keybindingmenu.channeldown canale in basso +keybindingmenu.channellist Lista Canali +keybindingmenu.channelup canale in alto +keybindingmenu.head Impostazione Tasti +keybindingmenu.lastchannel Quick Zap +keybindingmenu.modechange Modalita di cambio +keybindingmenu.pagedown pagina in basso +keybindingmenu.pageup pagina in alto +keybindingmenu.quickzap Cambio veloce +keybindingmenu.repeatblock Ritardo ripetizione +keybindingmenu.repeatblockgeneric Ritardo di ripetizione iniziale +keybindingmenu.sort cambia posti +keybindingmenu.subchanneldown sottocanale indietro +keybindingmenu.subchannelup sottocanale avanti +keybindingmenu.tvradiomode Modo TV/Radio +keybindingmenu.zaphistory Zapping History Bouquet +keychooser.head Impostazione nuovo Tasto +keychooser.text1 Premere il tasto +keychooser.text2 per interrompere aspettare +keychoosermenu.currentkey tasto attuale +keychoosermenu.setnew imposta nuovo tasto +keychoosermenu.setnone nessun tasto +languagesetup.head Impostazioni lingua +languagesetup.select Lingua +lcdcontroler.brightness luminosita normale +lcdcontroler.brightnessstandby luminosita Standby +lcdcontroler.contrast Contrasto +lcdcontroler.head Imostazioni LCD +lcdmenu.autodimm Auto dimm +lcdmenu.head LCD Settings +lcdmenu.inverse Inversione +lcdmenu.lcdcontroler Contrasto / Luminosita +lcdmenu.power Power +lcdmenu.statusline linea di stato +lcdmenu.statusline.both volume / tempo trascorso +lcdmenu.statusline.playtime tempo trascorso +lcdmenu.statusline.volume volume +mainmenu.audioplayer Audioplayer +mainmenu.games Giochi +mainmenu.head Menu Principale +mainmenu.movieplayer Movieplayer +mainmenu.pausesectionsd Leggi EPG +mainmenu.pictureviewer Lettore immagini +mainmenu.radiomode Modo-Radio +mainmenu.recording Registrazione +mainmenu.recording_start avvia +mainmenu.recording_stop interrompi +mainmenu.scartmode Entrata Scart +mainmenu.service Menu di servizio +mainmenu.settings Impostazioni +mainmenu.shutdown Spegnere +mainmenu.sleeptimer Timer spegnimento +mainmenu.tvmode Modo-TV +mainsettings.audio Audio +mainsettings.colors Colori / temi /caratteri +mainsettings.head Impostazioni +mainsettings.keybinding Impostazioni tasti +mainsettings.language Lingua +mainsettings.lcd Display LCD +mainsettings.misc Altre Impostazioni +mainsettings.network Network +mainsettings.recording Impostazioni di Registrazione +mainsettings.savesettingsnow Salva impostazioni +mainsettings.savesettingsnow_hint le impostazioni vengono salvate\nattendi... +mainsettings.streaming Movieplayer +mainsettings.video Video +menu.back indietro +messagebox.back Indietro +messagebox.cancel Cancella +messagebox.discard rifiuto i cambiamenti? +messagebox.error Errore +messagebox.info Informazioni +messagebox.no No +messagebox.yes Si +miscsettings.bootinfo Mostra informazioni all avvio +miscsettings.bootmenu Show boot menu +miscsettings.driver_boot driver and boot options +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general Generale +miscsettings.head Altre impostazioni +miscsettings.hwsections use hardware sections +miscsettings.infobar_sat_display Satellite display on infobar +miscsettings.pmtupdate enable pmt update +miscsettings.shutdown_real Modo standby +miscsettings.shutdown_real_rcdelay spegnimento ritardato +miscsettings.sptsmode use spts mode +miscsettings.startbhdriver carica BH-Mode drivers +motorcontrol.head Impostazioni Motore +movieplayer.bookmark Bookmarks +movieplayer.bookmarkname Bookmarkname +movieplayer.bookmarkname_hint1 Inserire nome del nuovo bookmark +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffering... +movieplayer.defdir start dir. +movieplayer.dvdplayback DVD +movieplayer.fileplayback File via VLC +movieplayer.goto salta a ... +movieplayer.goto.h1 = -> salto assoluto +movieplayer.goto.h2 +,- -> salto relativo +movieplayer.head Movieplayer +movieplayer.nostreamingserver The streaming server could not be reached. +movieplayer.pesplayback Play PES (Experimental) +movieplayer.pleasewait Attendi.\nConnesiione al streaming server... +movieplayer.toomanybookmarks There are too many bookmarks.\nYou need to delete one of them first. +movieplayer.tsplayback Play TS +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion This feature is not support by your current version of VLC +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway gateway predefinito +networkmenu.head Impostazioni Rete +networkmenu.ipaddress indirizzo IP +networkmenu.mount NFS/CIFS +networkmenu.nameserver name server +networkmenu.netmask netmask +networkmenu.setupnow attiva rete +networkmenu.setuponstartup attiva rete all avvio +networkmenu.show mostra impostazioni rete attive +networkmenu.test testa rete +nfs.alreadymounted directory gia caricata +nfs.automount carica all avvio +nfs.dir directory/share +nfs.ip NFS/CIFS Server IP +nfs.localdir cartella locale +nfs.mount Mount NFS/CIFS volume +nfs.mount_options opzioni di caricamento +nfs.mounterror mount error: carica impostazioni predefinite +nfs.mounterror_notsup tipo di filesystem non supportato +nfs.mountnow carica adesso +nfs.mounttimeout mount error: timeout +nfs.password CIFS password +nfs.remount ricarica directories +nfs.type tipo +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount Umount NFS/CIFS volume +nfs.umounterror error scarica volume +nfs.username CIFS username +nfsmenu.head NFS/CIFS impostazioni +nvod.percentage (%d%% over) +nvod.starting (starting in %d min) +nvodselector.directormode Modo-diretto +nvodselector.head Seleziona orario inizio +nvodselector.subservice Seleziona sottoservizio +options.default Resetta alle impostazioni preimpostate +options.fb framebuffer +options.null null +options.off spento +options.on acceso +options.serial seriale +parentallock.changepin cambia codice parentale +parentallock.changepin_hint1 Inserisci il nuovo codice parentale! +parentallock.changetolocked bouquets bloccati +parentallock.head Inserisci codice parentale PIN code +parentallock.lockage blocca programmma +parentallock.lockage12 da 12 anni in su +parentallock.lockage16 da 16 anni in su +parentallock.lockage18 da 18 anni in su +parentallock.lockedchannel Locked sender... +parentallock.lockedprogram Programma bloccato (da %d anni in su) +parentallock.never mai +parentallock.onsignal blocco al segnale +parentallock.parentallock Blocco parentale +parentallock.prompt inserimento PIN +pictureviewer.defdir start dir. +pictureviewer.head Lettore immagini +pictureviewer.resize.color_average avanzate +pictureviewer.resize.none nessuno +pictureviewer.resize.simple semplice +pictureviewer.scaling ingrandimento +pictureviewer.show vedi +pictureviewer.slide_time durata sul display +pictureviewer.slideshow durata +pictureviewer.sortorder cambia ordine selezione +pictureviewer.sortorder.date (data) +pictureviewer.sortorder.filename (nomefile) +ping.ok irraggiungibile (ping) +ping.protocol irraggiungibile (host o protocollo errore) +ping.socket irraggiungibile (socket errore) +ping.unreachable irraggiungibile +pinprotection.head Inserisci PIN code +pinprotection.wrongcode PIN-Code sbagliato! Prova ancora. +rclock.lockmsg Blocco telecomando ruDbox.\n per sbloccarlo, premi \n e sul telecomando. +rclock.menueadd Blocca telecomando +rclock.title Blocco telecomando +rclock.unlockmsg Telecomando riattivato. +recordingmenu.defdir cartella di registrazione +recordingmenu.file diretta su hdd del db +recordingmenu.head Impostazioni di registrazione +recordingmenu.help Recording devices:\n--------------------------\nserver:\nusing streaming software on a PC\n\n(analog) vcr:\nusing the vcr outlet\n\ndirect (file):\ndirectly into an NFS mounted directory\nor onto an internal hard drive\nTS: use spts mode(dbox2)\nPES: do not use spts mode(dbox2)\n\n\nMax. file size:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: almost unlimited (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart non passare in modo-scart +recordingmenu.off spento +recordingmenu.recording_type modalita registrazione +recordingmenu.server server +recordingmenu.server_ip IP server di registrazione +recordingmenu.server_mac MAC address +recordingmenu.server_port porta server registrazione +recordingmenu.server_wakeup server registrazione WOL +recordingmenu.setupnow salva cambiamenti +recordingmenu.splitsize dimensione massima file (MB) +recordingmenu.stopplayback stop riproduzione +recordingmenu.stopsectionsd stop sectionsd +recordingmenu.stream_vtxt_pid record videotext +recordingmenu.use_o_sync write synchronous (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce Registrazione parte fra alcuni minuti +repeatblocker.hint_1 Shortest time (in ms) to recognize 2 keystrokes +repeatblocker.hint_2 Enter 0 to switch of the blocker (red is space) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-ripetizioni +satsetup.extended DiSEqC-Impostazioni +satsetup.extended_motor Motore-Impostazioni +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Impostazione manuale Motore +satsetup.nodiseqc no DiSEqC +satsetup.satellite Satellite +satsetup.savesettingsnow salva impostazioni +satsetup.smatvremote SMATV Tuning remoto +scants.actcable Cavo: +scants.actsatellite Satellite: +scants.bouquet Bouquet +scants.bouquet_create crea nuovi +scants.bouquet_erase cancella tutti +scants.bouquet_leave non modificare +scants.bouquet_satellite bouquets satellite +scants.bouquet_update aggiorna +scants.channel Canali: +scants.failed Scansione transponder fallita! +scants.finished Scansione transponder terminata con successo! +scants.freqdata Frequenza: +scants.head Scansiona transponder +scants.numberofdataservices Dati +scants.numberofradioservices Radio +scants.numberoftotalservices Totali +scants.numberoftvservices TV +scants.provider Provider: +scants.startnow inizia scansione +scants.transponders Transponders: +screensetup.lowerright verde = margine basso destro +screensetup.upperleft rosso = margine alto sinistro +servicemenu.head Menu di servizio +servicemenu.reload Ricarica Lista Canali +servicemenu.reload_hint Ricarica Lista Canali,\nattendi... +servicemenu.scants Scansione canali +servicemenu.ucodecheck Check ucodes +servicemenu.update Aggiornamento Software +settings.help Help +settings.missingoptionsconffile le impostazioni di neutrino sono state aggiornate.\nNuove impostazioni settate come predefinite. +settings.noconffile No neutrino-impostazioni.\nUsing defaults. +shutdowntimer.announce ruDBox will spento in 1 min.\nCancello Spegnimento ? +sleeptimerbox.announce Sleeptimer in 1 min +sleeptimerbox.hint1 Orario di spegnimento in min. (000=off) +sleeptimerbox.hint2 ruDbox si spegne trascorso questo tempo. +sleeptimerbox.title Timer spegnimento +streamfeatures.head Caratteristiche +streaminfo.aratio Formato +streaminfo.aratio_unknown Formato: sconosciuto +streaminfo.audiotype Tipo di audio +streaminfo.audiotype_unknown Tipo di audio: sconosciuto +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: sconosciuto +streaminfo.head Informazioni Stream +streaminfo.not_available non disponibili +streaminfo.resolution Risoluzione +streaminfo.signal receipt signal +streaming.buffer_overflow The recording was aborted,\nsince the data could not be written fast enough. +streaming.busy One or several recording processes are active.\nIf you encounter this message and no recording is active, please restart Neutrino. +streaming.dir_not_writable The recording directory is not writable.\nRecording will not work. +streaming.success The recording has ended successfully. +streaming.write_error The recording was aborted,\nsince an error occured during the writing process. +streaming.write_error_open The recording was aborted,\nbecause the target file could not be opened. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Movieplayer Settings +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Spento +streamingmenu.on Acceso +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Porta +streamingmenu.streaming_audiorate Datarate Audio +streamingmenu.streaming_force_avi_rawaudio Force AC3 for AVI +streamingmenu.streaming_force_transcode_video Transcodifica MPG/VCD video +streamingmenu.streaming_resolution Risoluzione +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Directory (VLC) +streamingmenu.streaming_transcode_audio Transcodifica audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG video codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datarate Video +streamingserver.noconnect Nessuna connessione con streamingserver.\nRegistrazione cancellata. +stringinput.caps caps / no caps +stringinput.clear cancella tutto +timer.eventrecord.msg ... TO be DONE, or not to be done +timer.eventrecord.title Schedule Record +timer.eventtimed.msg The event is scheduled.\nThe ruDbox will power on and \nswitch to this channel at the given time. +timer.eventtimed.title Schedule Event +timerbar.channelswitch Schedule +timerbar.recordevent Record +timerlist.alarmtime Alarm time +timerlist.apids Audio PIDs +timerlist.bouquetselect scegli bouquet +timerlist.channel Canale +timerlist.channelselect scegli canale +timerlist.delete Cancella +timerlist.menumodify Modifica timer registrazione +timerlist.menunew Nuovo timer +timerlist.message Messaggi +timerlist.moderadio Canali radio +timerlist.modeselect Modo di selezione +timerlist.modetv Canali TV +timerlist.modify Modifica +timerlist.name lista Timer +timerlist.new Nuovo timer +timerlist.program.unknown Programma sconosciuto +timerlist.reload Riprendi +timerlist.repeat Ripeti +timerlist.repeat.biweekly 2 volte a settimana +timerlist.repeat.byeventdescription vedi timer +timerlist.repeat.daily giornaliera +timerlist.repeat.fourweekly 4 volte a settimana +timerlist.repeat.friday Venerdi +timerlist.repeat.monday Lunedi +timerlist.repeat.monthly Ogni Mese +timerlist.repeat.once una sola volta +timerlist.repeat.saturday Sabato +timerlist.repeat.sunday Domenica +timerlist.repeat.thursday Giovedi +timerlist.repeat.tuesday Maredi +timerlist.repeat.unknown sconoscito +timerlist.repeat.wednesday Mercoledi +timerlist.repeat.weekdays ad inizio Settimana +timerlist.repeat.weekly settimanalmente +timerlist.save Salva +timerlist.standby Standby +timerlist.standby.off Poni db in standby +timerlist.standby.on Riaccendi da standby +timerlist.stoptime Orario per fermarsi +timerlist.type Tipo di Timer +timerlist.type.nextprogram Programma successsivo +timerlist.type.record Registrazione +timerlist.type.remind Ricordami +timerlist.type.shutdown Spegnere +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Sconosciuto +timerlist.type.zapto Cambia +timerlist.weekdays Giorni della settimana +timerlist.weekdays.hint_1 LU MA ME GI VE SA DO +timerlist.weekdays.hint_2 'X'=timer '-' nessun timer +timersettings.record_safety_time_after Correzione fine registrazione +timersettings.record_safety_time_after.hint_1 Correzione in min. (00=off). This time +timersettings.record_safety_time_after.hint_2 che vengono aggiunti. +timersettings.record_safety_time_before Correzione inizio registrazione +timersettings.record_safety_time_before.hint_1 Correzione in min. (00=off). This time +timersettings.record_safety_time_before.hint_2 Che vengono tolti al Timer. +timersettings.separator Impostazione Timer +timing.chanlist Lista Canali +timing.epg Epg +timing.filebrowser Filebrowser +timing.head OSD Timeouts +timing.hint_1 Tempo in sec in cui OSD appare +timing.hint_2 sullo schermo. +timing.infobar Barra Informazione +timing.menu Menu +timing.numericzap Numeric Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode controllo +ucodecheck.ucode UCode +ucodes.failure ATTENTION, µCodes non trovati!\n\nCaricali via FTP ,\npoi riavvia ruDbox! +videomenu.csync correzione sync. +videomenu.head Impostazioni Video +videomenu.rgb_centering RGB-Posizionamento +videomenu.screensetup Impostazioni Margine Video +videomenu.vcrswitch Entrata-Scart automatica +videomenu.videoformat Formato +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect automatico +videomenu.videosignal Tipo di segnale Video +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce In un Minuto verra cambiato il Canale +extra.auto Auto +extra.valid Validi +extra.all Tutti +extra.new_keys New keys +extra.all_keys All keys +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo Algo pids +extra.gbox_emu Emu +extra.gbox_softcam Softcam +extra.gbox_net Network +extra.gbox_mix Mixed +extra.gbox_info Gbox Info +extra.sep_extra Extras +extra.save_settings Salva +extra.extra_menu Impostazioni Emu +extra.ecm_info Ecm Info +extra.pid_info Pid Info +extra.emm_info New Keys +extra.emu_type Emu Type +extra.camd Camd +extra.restart_camd Restart camd +extra.mg_settings Mgcamd settings +extra.netmode Netmode +extra.autoupdate Autoupdate +extra.show_ecm Show Ecm +extra.show_emm Show Emm +extra.keyupdate Key update +extra.osd OSD +extra.keyfolder Key dir +extra.hash_pids Hash pids +extra.gbox_settings Gbox Settings +extra.gbox_mode Mode +extra.newcamd_settings Newcamd Settings +extra.show_cw Show CW +extra.debug_ecm Debug Ecm +extra.disable_cam Disable CAM +extra.show_cat Show CAT +extra.show_pmt show PMT +extra.update_pmt Update PMT +extra.report_emm Report wrong EMM sig. +extra.debug_emm Debug Emm +extra.show_allca Show all ca sys. +extra.reload_keys Reload keys on zap +extra.reload_cfg Reload config on zap +extra.cw_delay Default CW delay +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Show PAT +extra.dvbsnoop_cat Show CAT +extra.dvbsnoop_tsdt Show TSDT +extra.dvbsnoop_nit Show NIT +extra.dvbsnoop_sdt Show SDT +extra.dvbsnoop_eit Show EIT +extra.dvbsnoop_rst Show RST +extra.dvbsnoop_tdt Show TDT +extra.dvbsnoop_sit Show SIT +extra.dvbsnoop_pid Show manual PID +extra.dvbsnoop_scan Scan PIDs +extra.dvbsnoop_band Show pid bandwidth +extra.logview View Log +extra.ru ruDREAM +extra.english English +extra.dboxinfo Box Info +extra.scan_mode Scan mode +extra.start_tostandby Startup to standby +extra.rotor_swap Swap rotor east/west +extra.use_log Log to /tmp/log +extra.spts_mode SPTS mode +extra.hw_sect Hardware Sections +extra.logo Logo number +extra.scan_full Full +extra.scan_fast Fast +extra.hdd_slow Slow +extra.hdd_middle Middle +extra.hdd_fast Fast +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Sleep time +extra.hdd_noise Noise +extra.hdd_activate Activate settings +extra.hdd_fs Filesystem +extra.hdd_format Format HDD +extra.hdd_check Check filesystem +extra.hdd_settings HDD Settings +extra.clear_log Clear Log +extra.zap_cycle Zap cycle +extra.sms_channel sms-mode channel +extra.manual_scan Manual scan +extra.tp_freq Frequency +extra.tp_rate Symbol rate +extra.tp_pol Polarization +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Timezone +extra.zapit_menu Zapit options +extra.zapit_make_bouquet Make Remaining Channels list +extra.zapit_save_last_chan Save last channel +extra.zapit_motor_speed Motor moving speed (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet Add to bouquet +extra.key_list_start home +extra.key_list_end end +extra.chadded The current channel has been added to selected bouquet....\n +extra.chalreadyinbq The current channel is already in selected bouquet....\n +extra.menu_left_exit "Left" = menu back +extra.zapit_write_names Write channel names +extra.update_dir Directory for updates +extra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Abortion of channel scan +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/nederlands.locale b/data/locale/nederlands.locale new file mode 100644 index 000000000..b19d04ee0 --- /dev/null +++ b/data/locale/nederlands.locale @@ -0,0 +1,771 @@ +EPGMenu.epgplus Preview overzicht +EPGMenu.eventinfo Info over uitzending +EPGMenu.eventlist Preview programma +EPGMenu.head EPG - Programma Informatie +EPGMenu.streaminfo technische Informatie +EPGPlus.actions Aktie's +EPGPlus.change_font_size Fonttype veranderen +EPGPlus.change_font_style Fontstijl veranderen +EPGPlus.change_size Grootte veranderen +EPGPlus.channelentry_font Kanaal +EPGPlus.channelentry_separationlineheight Hoogteverschil tussen 2 kanaalnamen +EPGPlus.channelentry_width Breedte van de kanaallnamen +EPGPlus.channelevententry_font Uitzendingen +EPGPlus.edit_fonts Fonts instellen +EPGPlus.edit_sizes Grootte instellen +EPGPlus.font_style_bold Vet +EPGPlus.font_style_italic Cursief +EPGPlus.font_style_regular Normaal +EPGPlus.footer_fontbouquetchannelname Kies Boeket- of Kanaalnaam +EPGPlus.footer_fontbuttons Knoppen +EPGPlus.footer_fonteventdescription Titel 1 kies uitzending +EPGPlus.footer_fonteventshortdescription Titel 2 kies uitzending +EPGPlus.head Preview (EPG Plus) +EPGPlus.header_font Menu +EPGPlus.next_bouquet Volgend Boeket +EPGPlus.options Optie's +EPGPlus.page_down Vorige pagina. +EPGPlus.page_up Volgende pagina. +EPGPlus.prev_bouquet Vorig Boeket. +EPGPlus.record Opname +EPGPlus.refresh_epg Aktualiseren +EPGPlus.remind Herinneren +EPGPlus.reset_settings Resetten van instellingen. +EPGPlus.save_settings Instellingen bewaren +EPGPlus.scroll_mode Scroll Mode +EPGPlus.select_font_name Lettertype kiezen +EPGPlus.settings Instellingen +EPGPlus.slider_width Breedte van de sliders +EPGPlus.stretch_mode Stretch Mode +EPGPlus.swap_mode Bladeren +EPGPlus.timeline_fontdate Tijdschaal datum +EPGPlus.timeline_fonttime Tijdschaal Tijd +EPGPlus.view_mode Mode +GENRE.ARTS.0 Kunst/ cultuur +GENRE.ARTS.1 Beeldende kunst +GENRE.ARTS.10 kunst/culturele magazines +GENRE.ARTS.11 mode +GENRE.ARTS.2 fijne kunst +GENRE.ARTS.3 religie +GENRE.ARTS.4 populair cultuur/traditionele kunst +GENRE.ARTS.5 literatuur +GENRE.ARTS.6 film/cinema +GENRE.ARTS.7 experimentele film/video +GENRE.ARTS.8 uitzendingen/pers +GENRE.ARTS.9 nieuwe media +GENRE.CHILDRENs_PROGRAMMES.0 Kinderprogramma/ animatie +GENRE.CHILDRENs_PROGRAMMES.1 Kleuterprogramma +GENRE.CHILDRENs_PROGRAMMES.2 Ontspanningsprogramma (leeftijd: 6 tot 14) +GENRE.CHILDRENs_PROGRAMMES.3 Ontspanningsprogramma (leeftijd: 10 tot 16) +GENRE.CHILDRENs_PROGRAMMES.4 informationeel/educationeel/schoolprogramma +GENRE.CHILDRENs_PROGRAMMES.5 tekenfilms/animatie +GENRE.DOCUS_MAGAZINES.0 documentatie/magazine +GENRE.DOCUS_MAGAZINES.1 natuur/dieren/omgeving +GENRE.DOCUS_MAGAZINES.2 technologie/natuurkunde +GENRE.DOCUS_MAGAZINES.3 Geneeskunde/psychologie/ +GENRE.DOCUS_MAGAZINES.4 Vreemde landen/expeditie's +GENRE.DOCUS_MAGAZINES.5 sociale- en spirituele wetenschappen +GENRE.DOCUS_MAGAZINES.6 andere opvoedingen +GENRE.DOCUS_MAGAZINES.7 Talen +GENRE.MOVIE.0 film/drama +GENRE.MOVIE.1 detectieve/thriller +GENRE.MOVIE.2 aventuren/western/oorlog +GENRE.MOVIE.3 science-fiction/fantasy/horror +GENRE.MOVIE.4 comedy +GENRE.MOVIE.5 soap/melodrama/folkloristisch +GENRE.MOVIE.6 romantiek +GENRE.MOVIE.7 ernstig/klassiek/religieus/historische film/drama +GENRE.MOVIE.8 +18 / drama +GENRE.MUSIC_DANCE.0 muziek / ballet / dance +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 klassieke music +GENRE.MUSIC_DANCE.3 folk/traditionele muziek +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 nieuws +GENRE.NEWS.1 nieuws/weerbulletin +GENRE.NEWS.2 nieuwsmagazine +GENRE.NEWS.3 documentaire +GENRE.NEWS.4 discussie/interview/debat +GENRE.SHOW.0 Show / Spelshow +GENRE.SHOW.1 game show/quiz/contest +GENRE.SHOW.2 varieteit show +GENRE.SHOW.3 talk show +GENRE.SOCIAL_POLITICAL.0 sociale en politieke evenementen/zaken +GENRE.SOCIAL_POLITICAL.1 magazines/verslagen/documentaire +GENRE.SOCIAL_POLITICAL.2 economische /sociale adviezen +GENRE.SOCIAL_POLITICAL.3 Opmerkzame personen +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 speciale evenementen (Olympische spelen,Wereldbeker, etc.) +GENRE.SPORTS.10 paardensport +GENRE.SPORTS.11 gevechtsporten +GENRE.SPORTS.2 sportmagazine +GENRE.SPORTS.3 Voetbal +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 teamsporten +GENRE.SPORTS.6 atletiek +GENRE.SPORTS.7 motorsport +GENRE.SPORTS.8 watersport +GENRE.SPORTS.9 wintersport +GENRE.TRAVEL_HOBBIES.0 reizen en ontspanning +GENRE.TRAVEL_HOBBIES.1 tourisme/reizen +GENRE.TRAVEL_HOBBIES.2 werk +GENRE.TRAVEL_HOBBIES.3 autotoerisme +GENRE.TRAVEL_HOBBIES.4 fitness & gezondheid +GENRE.TRAVEL_HOBBIES.5 koken +GENRE.TRAVEL_HOBBIES.6 adverteren/shopping +GENRE.TRAVEL_HOBBIES.7 tuinieren +GENRE.UNKNOWN Onbekend +apids.hint_1 Geef de te streamen APIDs in +apids.hint_2 in hex-weergave scheiden met ' ' +apidselector.head Kies taal +audiomenu.PCMOffset Volume Decrease PCM +audiomenu.analogout Analoge uitgang +audiomenu.avs avs +audiomenu.avs_control Volume Control avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Geluidsinstellingen +audiomenu.lirc lirc +audiomenu.monoleft mono links +audiomenu.monoright mono rechts +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add Voeg toe +audioplayer.artist_title Artiest, Titel +audioplayer.defdir Startdirectory +audioplayer.delete Wis +audioplayer.deleteall Wis alles +audioplayer.display_order display order +audioplayer.fastforward Vooruitspoelen +audioplayer.follow auto Kies huidig +audioplayer.head Audio Speellijst +audioplayer.highprio Hoge decoderingsprioriteit +audioplayer.id3scan Scannen van ID3 tags +audioplayer.keylevel key level +audioplayer.name Audioplayer +audioplayer.pause pauze +audioplayer.play Play +audioplayer.playing Huidig nummer +audioplayer.rewind terugspoelen +audioplayer.screensaver_timeout screensaver timeout (min, 0=uit) +audioplayer.shuffle shuffle +audioplayer.stop Stop +audioplayer.title_artist Titel, Artiest +audioplayerpicsettings.general Audioplayer/ beeldviewer +bookmarkmanager.delete Verwijderen +bookmarkmanager.name Favorieten +bookmarkmanager.rename Hernoemen +bookmarkmanager.select Kiezen +bouqueteditor.add Voeg toe +bouqueteditor.bouquetname Naam van het boeket +bouqueteditor.delete Wis +bouqueteditor.discardingchanges Wegzetten van de wijzigingen, even geduld a.u.b... +bouqueteditor.hide Verberg +bouqueteditor.lock Slot +bouqueteditor.move Verzet +bouqueteditor.name Boeket-editor +bouqueteditor.newbouquetname Nieuwe naam van het boeket +bouqueteditor.rename Hernoem +bouqueteditor.return Klaar +bouqueteditor.savechanges? Wilt u de wijzigingen bewaren? +bouqueteditor.savingchanges Bewaren van wijzigingen, even geduld... +bouqueteditor.switch Toevoegen/verwijderen +bouqueteditor.switchmode TV/Radio +bouquetlist.head Boeket +cablesetup.provider Kabelprovider +channellist.head Alle Services +channellist.nonefound Geen kanalen gevonden!\nVoer aub een scan uit\n(dbox-toets -> service) +channellist.since sinds %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blauw +colorchooser.green groen +colorchooser.red rood +colormenu.background Achtergrond +colormenu.background_head Achtergrond +colormenu.fade Fade menu's +colormenu.font lettergrootte +colormenu.gtx_alpha Transparantheid (GTX) +colormenu.head Kleur Settings +colormenu.menucolors Menu kleuren +colormenu.statusbar Infobar +colormenu.textcolor Tekstkleur +colormenu.textcolor_head Tekstkleur +colormenu.themeselect selecteer onderwerp +colormenu.timing OSD Tijd +colormenusetup.head Menu Kleuren +colormenusetup.menucontent Vensterinhoud +colormenusetup.menucontent_inactive Vensterinhoud niet actief +colormenusetup.menucontent_selected Vensterinhoud geactiveerd +colormenusetup.menuhead Menu bovenkant +colorstatusbar.head Infobar +colorstatusbar.text Infobar +colorthememenu.classic_theme Classic thema +colorthememenu.dblue_theme Donkerblauw thema +colorthememenu.dvb2k_theme DVB2000 thema +colorthememenu.head Themakeuze +colorthememenu.neutrino_theme Neutrino thema +date.Apr April +date.Aug Augustus +date.Dec December +date.Feb Februari +date.Fri vrijdag +date.Jan Januari +date.Jul Juli +date.Jun Juni +date.Mar Maart +date.May Mei +date.Mon maandag +date.Nov November +date.Oct Oktober +date.Sat zaterdag +date.Sep September +date.Sun zondag +date.Thu donderdag +date.Tue dinsdag +date.Wed woensdag +epglist.head Event-list - %s +epglist.noevents EPG is niet beschikbaar +epgviewer.More_Screenings Meerdere Screenings op dit kanaal +epgviewer.nodetailed Geen details beschikbaar +epgviewer.notfound Geen EPG gevonden +eventlistbar.channelswitch Planning +eventlistbar.eventsort Sorteren +eventlistbar.recordevent Opnemen +favorites.addchannel Huidig kanaal zal toegevoegd worden\naan het boeket "mijn favorieten".\nDit zal enkele seconden durens... +favorites.bouquetname Mijn favorieten +favorites.bqcreated Bouquet "Mijn favorieten" is gemaakt...\n +favorites.chadded Huidig kanaal is aan de favorieten toegevoegd...\n +favorites.chalreadyinbq Huidig kanaal staat reeds bij de favorieten...\n +favorites.finalhint \nGebruik de boeketeditor om de favorieten aan te passen.\n +favorites.menueadd Voeg kanaal aan favorieten toe. +favorites.nobouquets Favorieten zijn alleen beschikbaar met geactiveerde boeketten. +filebrowser.delete Verwijderen +filebrowser.dodelete1 Moet +filebrowser.dodelete2 gewist worden? +filebrowser.filter.active Filter aan +filebrowser.filter.inactive Filter uit +filebrowser.head Browser +filebrowser.mark Markeer +filebrowser.nextpage Volgende pagina +filebrowser.prevpage Vorige pagina +filebrowser.scan Folders doorzoeken +filebrowser.select Kies +filebrowser.showrights Toon rechten +filebrowser.sort.date (datum) +filebrowser.sort.name (bestandsname) +filebrowser.sort.namedirsfirst (Dateiname2) +filebrowser.sort.size (Grootte) +filebrowser.sort.type (Type) +flashupdate.actionreadflash lezen +flashupdate.cantopenfile Kan bestand niet openen +flashupdate.cantopenmtd Kan het mtd-device niet openen +flashupdate.checkupdate Zoek nieuwe versie +flashupdate.currentreleasecycle Release cycle +flashupdate.currentversion_sep Huidige versie +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot Type Image +flashupdate.currentversiontime Tijd +flashupdate.erasefailed Wissen van de flash mislukt +flashupdate.erasing Wissen van de flash +flashupdate.experimentalimage De gekozen image is nog niet getest.D.w.z.dat\nuw ontvanger misschien niet opstart na de update.\n\nWilt u echt deze versie updaten? +flashupdate.expertfunctions Expert-functie's +flashupdate.fileis0bytes De grootte van het bestand is 0 Byte +flashupdate.fileselector File kiezen +flashupdate.flashreadyreboot De image is succesvol geflashed.\nDe DBox zal nu herstarten. +flashupdate.getinfofile Haalt info versie op +flashupdate.getinfofileerror Geen info van de versie gevonden +flashupdate.getupdatefile Krijgt een update +flashupdate.getupdatefileerror Krijgt geen update +flashupdate.globalprogress Globale vooruitgang: +flashupdate.head Software Update +flashupdate.md5check controleer image +flashupdate.md5sumerror Image heeft fouten +flashupdate.msgbox De volgende nieuwe image is gevonden:\nDatum: %s, %s\nBaseImage: %s\nImageType: %s\n\nWilt u de nieuwe image nu downloaden en installeren? +flashupdate.msgbox_manual De volgende nieuwe image is gevonden:\nDatum: %s, %s\nBaseImage: %s\nImageType: %s\n\nWilt u deze versie nu installeren? +flashupdate.mtdselector Partitie kiezen +flashupdate.programmingflash Programmeren van de flash +flashupdate.proxypassword Paswoord +flashupdate.proxypassword_hint1 Geef het paswoord voor de proxyserver +flashupdate.proxypassword_hint2 Leeg betekent geen proxy +flashupdate.proxyserver Hostnaam +flashupdate.proxyserver_hint1 Geef naam of IP proxyserver, Gebruik host: poort +flashupdate.proxyserver_hint2 Geen invoer = geen proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Gebruikersnaam +flashupdate.proxyusername_hint1 Geef de gebruikersnaam van de proxyserver +flashupdate.proxyusername_hint2 Geen invoer= geen proxy-auth +flashupdate.readflash Lees de hele image +flashupdate.readflashmtd Lees een partitie +flashupdate.ready Klaar +flashupdate.reallyflashmtd Wilt u echt flashen?\n\nAls er een fout optreed is de image niet\ngeldig, De Dbox zal niet starten na het flashen.\n\nImagename: %s\nTarget: %s +flashupdate.savesuccess De image is succesvol bewaard \nonder %s. +flashupdate.selectimage Beschikbare Images +flashupdate.squashfs.noversion Versiecontrole van SquashFS gaat momenteel enkel via webupdating +flashupdate.titlereadflash Lezen Flash +flashupdate.titlewriteflash Writing Flash +flashupdate.updatemode Updatemode +flashupdate.updatemode_internet Internet +flashupdate.updatemode_manual Manueel (ftp) +flashupdate.url_file config file +flashupdate.versioncheck Controleer versie +flashupdate.writeflash Schrijf de hele image +flashupdate.writeflashmtd Schrijf een partitie +flashupdate.wrongbase Your Release cycle differs, unable to update\nwithout having installed the right version! +fontmenu.channellist kanalenlijst +fontmenu.epg EPG +fontmenu.eventlist Eventlist +fontmenu.gamelist Spellijst +fontmenu.head Settings lettergrootte +fontmenu.infobar Infobar +fontsize.channel_num_zap Directe selectie +fontsize.channellist Kanalenlijst +fontsize.channellist_descr Omschrijving +fontsize.channellist_number Nummer +fontsize.epg_date EPG datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titel +fontsize.eventlist_datetime datum/tijd +fontsize.eventlist_itemlarge groot +fontsize.eventlist_itemsmall klein +fontsize.eventlist_title Titel +fontsize.filebrowser_item filebrowser item +fontsize.gamelist_itemlarge groot +fontsize.gamelist_itemsmall klein +fontsize.hint lettertype instellen,\ngeduld aub... +fontsize.infobar_channame Kanaalnaam +fontsize.infobar_info info +fontsize.infobar_number Nummer +fontsize.infobar_small klein +fontsize.menu Menutekst +fontsize.menu_info Menu Info +fontsize.menu_title Menu Titel +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG niet geladen.... +infoviewer.epgwait wachten op EPG... +infoviewer.eventlist Event-Lijst +infoviewer.languages Geluid +infoviewer.motor_moving Positionering van de antenne +infoviewer.nocurrent Geen info beschikbaar voor dit programma +infoviewer.noepg EPG is niet beschikbaar +infoviewer.notavailable Kanaal is niet beschikbaar +infoviewer.selecttime Tijdskeuze +infoviewer.streaminfo Kenmerken +infoviewer.subservice Subservices +infoviewer.waittime Wacht op de tijd... +ipsetup.hint_1 Gebruik 0 tot 9, of Use 0..9, of gebruik Up/Down, +ipsetup.hint_2 OK bewaar, Home afbreken +keybindingmenu.RC Key herhaal-blocker +keybindingmenu.addrecord Voeg record timer toe +keybindingmenu.addrecord_head Voeg record timer toe +keybindingmenu.addremind add zapto timer +keybindingmenu.addremind_head add zapto timer +keybindingmenu.allchannels_on_ok Alle services +keybindingmenu.bouquetchannels_on_ok Huidig boeket +keybindingmenu.bouquetdown vorig boeket +keybindingmenu.bouquetdown_head keysetup vorig boeket back +keybindingmenu.bouquethandling Boeket-control +keybindingmenu.bouquetlist_on_ok Boeketlijst +keybindingmenu.bouquetup Volgend boeket +keybindingmenu.bouquetup_head keysetup Volgend boeket +keybindingmenu.cancel Sluit kanalenlijst +keybindingmenu.cancel_head Kanalenlijst gesloten +keybindingmenu.channeldown Kanaal omlaag +keybindingmenu.channeldown_head Keysetup channel down +keybindingmenu.channellist Kanaallijst +keybindingmenu.channelup kanaal omhoog +keybindingmenu.channelup_head Keysetup channel up +keybindingmenu.head Keybinding Setup +keybindingmenu.modechange Modechange +keybindingmenu.pagedown Volgende pagina +keybindingmenu.pagedown_head Keysetup page down +keybindingmenu.pageup Vorige pagina +keybindingmenu.pageup_head Keysetup page up +keybindingmenu.quickzap Quickzap +keybindingmenu.repeatblock Herhaal delay +keybindingmenu.repeatblockgeneric generic delay +keybindingmenu.sort Verander sorteervolgorde +keybindingmenu.sort_head Verander sorteervolgorde +keybindingmenu.subchanneldown subchannel omlaag +keybindingmenu.subchanneldown_head key subkanaal omlaag +keybindingmenu.subchannelup subkanaal omhoog +keybindingmenu.subchannelup_head key subkanaal omhoog +keybindingmenu.tvradiomode TV/Radio-mode +keybindingmenu.tvradiomode_head TV/Radio-mode +keychooser.head Setup nieuwe Key +keychooser.text1 Geef de nieuwe key in +keychooser.text2 Wacht enkele sec. om af te breken +keychoosermenu.currentkey Huidige key +keychoosermenu.setnew setup nieuwe key +keychoosermenu.setnone Geen key +languagesetup.head Setup Taal +languagesetup.select Taalkeuze +lcdcontroler.brightness normale helderheid +lcdcontroler.brightnessstandby Standby helderheid +lcdcontroler.contrast Contrast +lcdcontroler.head Display-instellingen +lcdmenu.autodimm Auto dimm +lcdmenu.head LCD Settings +lcdmenu.inverse Invert +lcdmenu.lcdcontroler Contrast / Helderheid +lcdmenu.power Voeding +lcdmenu.statusline status lijn +lcdmenu.statusline.both Volume/Speelduur +lcdmenu.statusline.playtime Speelduur +lcdmenu.statusline.volume Volume +mainmenu.audioplayer Audioplayer +mainmenu.games Spelletjes +mainmenu.head Hoofdmenu +mainmenu.movieplayer Filmspeler +mainmenu.pausesectionsd Lees EPG +mainmenu.pictureviewer Weergeven van afbeeldingen +mainmenu.radiomode Radio-Mode +mainmenu.recording Opnemen +mainmenu.recording_start Start opname +mainmenu.recording_stop Stop opname +mainmenu.scartmode Scart-Mode +mainmenu.service Service +mainmenu.settings Instellingen +mainmenu.shutdown Uitschakelen +mainmenu.sleeptimer SleepTimer +mainmenu.tvmode TV-Mode +mainsettings.audio Geluid +mainsettings.colors Kleuren/lettertype +mainsettings.head Settings +mainsettings.keybinding Key Setup +mainsettings.language Taal +mainsettings.lcd LCD-display +mainsettings.misc Overige settings +mainsettings.network Netwerk +mainsettings.recording Opnemen +mainsettings.savesettingsnow Bewaar de settings nu +mainsettings.savesettingsnow_hint Even geduld, settings worden bewaard +mainsettings.streaming Filmspeler +mainsettings.video Beeld +menu.back Terug +messagebox.back terug +messagebox.cancel Annuleren +messagebox.discard Bewaren van de wijzigingen? +messagebox.error Fout +messagebox.info Informatie +messagebox.no Neen +messagebox.yes Ja +miscsettings.bootinfo Toon info bij starten +miscsettings.bootmenu Info tonen bij opstarten +miscsettings.driver_boot Bootoptie's +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general Algemeen +miscsettings.head Overige settings +miscsettings.hwsections Gebruik hardware secties +miscsettings.infobar_sat_display Satelliet display in infobar +miscsettings.shutdown_real standby +miscsettings.shutdown_real_rcdelay vertraagde shutdown +miscsettings.sptsmode Gebruik spts-mode +miscsettings.startbhdriver load BH-Mode drivers +motorcontrol.head Motor-Setup +movieplayer.bookmark Favorieten +movieplayer.bookmarkname Bookmark Naam +movieplayer.bookmarkname_hint1 Geef de naam voor de +movieplayer.bookmarkname_hint2 nieuwe favoriet in +movieplayer.buffering Bufferen... +movieplayer.defdir startdirectory +movieplayer.dvdplayback DVD +movieplayer.fileplayback Bestand via VLC +movieplayer.goto Ga naar... +movieplayer.goto.h1 = -> absolute sprong +movieplayer.goto.h2 +,- -> relatieve sprong +movieplayer.head Filmplayer +movieplayer.nostreamingserver De streamserver kon niet gevonden worden +movieplayer.pesplayback PES Afspelen (Experiment) +movieplayer.pleasewait Even geduld aub.\nBezig met verbinden met streamserver... +movieplayer.toomanybookmarks U heeft te veel favorieten.\nEr moet een andere favoriet gewist worden. +movieplayer.tshelp Rood/Home Stop\nGROEN Kies audio track\nGEEL Pause/Continue\nBLUE Create bookmark\nd-box Show progress\n1 approx. 1 minute back\n3 skip approx. 1 minute\n4 approx. 5 minuten terug\n6 sla over ongeveer 5 minuten\n7 ongeveer 10 minuten terug\n9 sla ongeveer 10 minutes\nHelp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tsplayback Speel TS +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp ROOD/Home Stop\nGROEN Resync\nGEEL Pause/verder\nBLAUW Favoriet instellen\n1 ca. 1 Minuut terug\n3 ca. 1 Minuut vooruit\n4 ca. 5 Minuten terug\n6 ca. 5 Minuten vooruit\n7 ca. 10 Minuten terug\n9 ca. 10 Minuten vooruit\nHelp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.wrongvlcversion Functie is niet mogelijk met de gebruikte VLC-versie +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway Standaard Gateway +networkmenu.head Netwerkinstellingen +networkmenu.ipaddress IP-Addres +networkmenu.mount NFS/CIFS +networkmenu.nameserver Nameserver +networkmenu.netmask Netmasker +networkmenu.setupnow Netwerk nu activeren +networkmenu.setuponstartup Netwerk activeren bij opstarten +networkmenu.show Toon actieve netwerksettings +networkmenu.test Test nu het netwerk +nfs.alreadymounted directory reeds gemount +nfs.automount mount bij starten +nfs.dir directory/share +nfs.ip NFS/CIFS Server IP +nfs.localdir plaatselijke directory +nfs.mount Mount NFS/CIFS volume +nfs.mount_options mountoptie's +nfs.mounterror mountfout +nfs.mounterror_notsup Type filesysteem niet ondersteund +nfs.mountnow direct mounten +nfs.mounttimeout mountfout: timeout +nfs.password paswoord Common Internet File System (CIFS) +nfs.remount Lijst opnieuw mounten +nfs.type type +nfs.type_cifs Common Internet File System (CIFS) +nfs.type_nfs Network File System (NFS) +nfs.umount Umount NFS/CIFS volume +nfs.umounterror umountingfout: volume +nfs.username Gebruikersnaam Common Internet File System (CIFS) +nfsmenu.head NFS/CIFS instellingen +nvod.percentage (%d%% over) +nvod.starting (start binnen %d minuten) +nvodselector.directormode Direct-Mode +nvodselector.head Kies starttijd +nvodselector.subservice Kies Subservice +options.default Reset naar standaard +options.fb Framebuffer +options.null Null +options.off Uit +options.on Aan +options.serial Serieel +parentallock.changepin verander PIN code +parentallock.changepin_hint1 Geef hier de nieuwe PIN-code in! +parentallock.changetolocked on locked bouquets +parentallock.head Voer PIN-code v.h. kinderslot in +parentallock.lockage Programma op slot +parentallock.lockage12 Voor jonger dan 12j +parentallock.lockage16 Voor jonger dan 16j +parentallock.lockage18 Voor jonger dan 18j +parentallock.lockedprogram Gesloten programma (vanaf %d jaar en hoger) +parentallock.never nooit +parentallock.onsignal Bij uitzenden op slot +parentallock.parentallock Kinderslot +parentallock.prompt prompt voor PIN +pictureviewer.defdir startdirectory +pictureviewer.head Weergave van afbeeldingen +pictureviewer.resize.color_average Geavanceerd +pictureviewer.resize.none Geen +pictureviewer.resize.simple Eenvoudig +pictureviewer.scaling schaal +pictureviewer.show Toon +pictureviewer.slide_time toon tijd van slideshow +pictureviewer.slideshow slideshow +pictureviewer.sortorder verander volgorde +pictureviewer.sortorder.date (datum) +pictureviewer.sortorder.filename (bestandsnaam) +ping.ok Bereikbaar door Ping +ping.protocol Niet bereikbaar (fout bij host of protocol) +ping.socket Niet bereikbaar (socketfout) +ping.unreachable Onbereikbaar door Ping +pinprotection.head Voer PIN-code in +pinprotection.wrongcode Foute PIN-Code! Probeer opnieuw +rclock.lockmsg Uw afstandsbediening van de DBox2 zal vergrendeld worden.\nOm de DBox2 vrij te geven druk op \nen op uw afstandsbediening. +rclock.menueadd Vergrendelen afstandsbediening +rclock.title Vergrendel afstandsbediening +rclock.unlockmsg Afstandsbediening in werking. +recordingmenu.defdir Opnamefolder +recordingmenu.file Direct (file) +recordingmenu.filesettingsseparator Direct opnemen +recordingmenu.head Opnameinstellingen +recordingmenu.no_scart Verander niet naar Scart-mode +recordingmenu.off Uit +recordingmenu.recording_type Opname type +recordingmenu.server server +recordingmenu.server_ip Opname server IP +recordingmenu.server_mac MAC adres +recordingmenu.server_port Opname server port +recordingmenu.server_wakeup recording server WOL +recordingmenu.setupnow Activeer veranderingen +recordingmenu.splitsize Max. grootte (MB) +recordingmenu.stopplayback stop playback +recordingmenu.stopsectionsd stop sectionsd +recordingmenu.stream_all_audio_pids Alle audiopids opnemen +recordingmenu.vcr vcr +recordtimer.announce Opname start in enkele minuten +repeatblocker.hint_1 Kortste tijd (in ms) tussen 2 drukken op de knop +repeatblocker.hint_2 Druk op 0 om de blocker uit te zetten (rood is spatie) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-herhaling +satsetup.extended DiSEqC-Settings +satsetup.extended_motor Motorsettings +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manuele Setup van de motor +satsetup.nodiseqc Geen DiSEqC +satsetup.satellite Satelliet +satsetup.savesettingsnow Sla de settings op +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Kabel: +scants.actsatellite Satelliet: +scants.bouquet Boeket +scants.bouquet_create Maak een nieuw +scants.bouquet_erase Wis alles +scants.bouquet_leave Verlaat huidig +scants.bouquet_satellite Satellietboeket +scants.bouquet_update update +scants.channel Kanaal: +scants.failed Faling transponderscan! +scants.finished Transponderscan succesvol! +scants.freqdata Frequentie +scants.head Scan transponder +scants.numberofdataservices Data: +scants.numberofradioservices Radio: +scants.numberoftotalservices Totaal: +scants.numberoftvservices TV: +scants.provider Provider: +scants.startnow Start scan +scants.transponders Transponders: +screensetup.lowerright groen = setup lower right +screensetup.upperleft rood = setup upper left +servicemenu.head Service +servicemenu.reload Herlaad Kanalenlijst +servicemenu.reload_hint Even geduld, herladen van kanalenlijst +servicemenu.scants Service scan +servicemenu.ucodecheck Controle ucodes +servicemenu.update Software Update +settings.missingoptionsconffile De neutrino-settings zijn geupdate.\nNew Opties worden standaard gezet. +settings.noconffile Geen neutrino-settings gevonden.\nStandaart in gebruik. +shutdowntimer.announce Box zal uitschakelen in 1 min.\nUitschakelen aflassen? +sleeptimerbox.announce Sleeptimer in 1 min +sleeptimerbox.hint1 Schakel uit in ...minuten. (000=uit) +sleeptimerbox.hint2 dbox2 zal uitgaan na deze tijd. +sleeptimerbox.title Sleeptimer +streamfeatures.head Features +streaminfo.aratio Aspect Ratio +streaminfo.aratio_unknown Aspect Ratio: onbekend +streaminfo.audiotype Audiotype +streaminfo.audiotype_unknown Audiotype: onbekend +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: onbekend +streaminfo.head Stream-Informatie +streaminfo.not_available Niet beschikbaar +streaminfo.resolution Resolutie +streaming.buffer_overflow De opname is afgebroken \nomdat doelmap niet kon geopend worden. +streaming.busy Een of meerdere opnames zijn bezig.\nAls U deze oodschap krijgt en er is geen opname\nbezig herstart dan Neutrino. +streaming.dir_not_writable De opnamefolder is niet bereikbaar.\nOpnemen zal niet gaan. +streaming.success De opname is met succes beëindigd +streaming.write_error De opname is afgebroken \nomdat er een schrijffout opgetreden is. +streaming.write_error_open De opname is afgebroken \nomdat de doelmap niet kon geopend worden. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Instellingen filmspeler +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Uit +streamingmenu.on Aan +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Poort +streamingmenu.streaming_audiorate Datarate Geluid +streamingmenu.streaming_force_avi_rawaudio AC3 met avi +streamingmenu.streaming_force_transcode_video Transcod. ook mpg/vcd Video +streamingmenu.streaming_resolution Resolutie +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Directory (VLC) +streamingmenu.streaming_transcode_audio Transcod. Audio (dvd/vcd/mpg) +streamingmenu.streaming_transcode_video_codec MPEG Video Codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datarate Beeld +streamingserver.noconnect Geen verbinding met de streamserver.\nOpname afgelast. +stringinput.caps caps/geen caps +stringinput.clear Wis alles +timer.eventrecord.msg OK, niet OK +timer.eventrecord.title Opname plannen +timer.eventtimed.msg Dit programma is opgeslagen.\ndbox2 zal opstarten en \nschakelt naar dit programma op het gegeven tijdstip. +timer.eventtimed.title Programmeren +timerbar.channelswitch Overzicht +timerbar.recordevent Opnemen +timerlist.alarmtime Alarm time +timerlist.apids Audio PIDs +timerlist.bouquetselect Kies boeket +timerlist.channel Kanaal +timerlist.channelselect Kies kanaal +timerlist.delete Wis +timerlist.menumodify Pas timer aan +timerlist.menunew nieuwe timer +timerlist.message Boodschap +timerlist.moderadio Radio-kanalen +timerlist.modeselect Mode selection +timerlist.modetv TV-kanalen +timerlist.modify Modify +timerlist.name Timer list +timerlist.new Nieuwe timer +timerlist.program.unknown Programma onbekend +timerlist.reload Herlaad +timerlist.repeat Herhaal +timerlist.repeat.biweekly Tweewekelijks +timerlist.repeat.byeventdescription Bekijk programmatie +timerlist.repeat.daily Dagelijks +timerlist.repeat.fourweekly Vierweekelijks +timerlist.repeat.friday Vr +timerlist.repeat.monday Ma +timerlist.repeat.monthly Maandelijks +timerlist.repeat.once Eenmalig +timerlist.repeat.saturday Za +timerlist.repeat.sunday Zo +timerlist.repeat.thursday Do +timerlist.repeat.tuesday Di +timerlist.repeat.unknown Onbekend +timerlist.repeat.wednesday Woe +timerlist.repeat.weekdays Op weekdagen +timerlist.repeat.weekly weekelijks +timerlist.save Bewaar timer +timerlist.standby SB mode +timerlist.standby.off Standby laten +timerlist.standby.on Enter standby +timerlist.stoptime Stop time +timerlist.type Timer typ +timerlist.type.nextprogram Volgend programma +timerlist.type.record Opnemen +timerlist.type.remind Herinnering +timerlist.type.shutdown Uitschakelen +timerlist.type.sleeptimer Sleeptimer +timerlist.type.standby Standby +timerlist.type.unknown Onbekend +timerlist.type.zapto Zap naar +timerlist.weekdays Weekdagen +timerlist.weekdays.hint_1 Ma Di Wo Do Vr Za Zo +timerlist.weekdays.hint_2 'X'=timer '-' geen timer +timersettings.record_safety_time_after Stop opname tijdscorrectie +timersettings.record_safety_time_after.hint_1 Correctie van tijd in min. (00=uit). Deze tijd +timersettings.record_safety_time_after.hint_2 wordt toegevoegd aan iedere stoptijd van de timer. +timersettings.record_safety_time_before Start opname tijdscorrectie +timersettings.record_safety_time_before.hint_1 Correctie van tijd in min. (00=uit). Deze tijd +timersettings.record_safety_time_before.hint_2 will be deducted of every record timer. +timersettings.separator Timer instellingen +timing.chanlist Kanalenlijst +timing.epg Epg +timing.filebrowser Browser voor bestanden +timing.head OSD Timeouts +timing.hint_1 Tijd in sec. Na deze tijd zal de +timing.hint_2 infobar verdwijnen. +timing.infobar Infobar +timing.menu Menu +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Alpha-Cam +ucodecheck.head Controle UCode +ucodecheck.ucode UCode +ucodes.failure Opgelet, µCodes konden niet gevonden worden!\n\nLoad ze up via FTP (of via de DBox-BootManager),\nnadien de DBox herstarten! +videomenu.csync Sync.-correctie +videomenu.head Videoinstellingen +videomenu.rgb_centering RGB-centering +videomenu.screensetup Scherm Setup +videomenu.vcrswitch Switch Scart automatisch +videomenu.videoformat Formaat +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect autodetect +videomenu.videosignal Video uit +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Zapto timer in 1 minuut. diff --git a/data/locale/polski.locale b/data/locale/polski.locale new file mode 100644 index 000000000..c3fb6eb3d --- /dev/null +++ b/data/locale/polski.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus EPG Plus +EPGMenu.eventinfo Opis programu +EPGMenu.eventlist Lista programów +EPGMenu.head EPG +EPGMenu.streaminfo Informacje o strumieniu +EPGPlus.actions akcje +EPGPlus.bybouquet_mode bukietami +EPGPlus.bypage_mode stronami +EPGPlus.change_font_size Rozmiar +EPGPlus.change_font_style Styl +EPGPlus.change_size Podaj wielkość +EPGPlus.channelentry_font Nazwa kanaÅ‚u +EPGPlus.channelentry_separationlineheight Wysokość separatorów +EPGPlus.channelentry_width Szerokość kolumny z kanaÅ‚ami +EPGPlus.channelevententry_font TytuÅ‚ programu +EPGPlus.edit_fonts Edycja czcionek +EPGPlus.edit_sizes Edycja rozmiarów +EPGPlus.font_style_bold pogrubiony +EPGPlus.font_style_italic kursywa +EPGPlus.font_style_regular normalny +EPGPlus.footer_fontbouquetchannelname Pasek: Bukiet +EPGPlus.footer_fontbuttons Pasek: Funkcje +EPGPlus.footer_fonteventdescription Pasek: TytuÅ‚ programu +EPGPlus.footer_fonteventshortdescription Pasek: Opis programu +EPGPlus.head EPG Plus +EPGPlus.header_font Nagłówek +EPGPlus.horgap1_height Wysokość górnego odstÄ™pu +EPGPlus.horgap2_height Wysokość dolnego odstÄ™pu +EPGPlus.next_bouquet nast.bukiet +EPGPlus.options opcje +EPGPlus.page_down poprz.strona +EPGPlus.page_up nast.strona +EPGPlus.prev_bouquet poprz.bukiet +EPGPlus.record nagraj +EPGPlus.refresh_epg odÅ›wież +EPGPlus.remind zapamiÄ™taj +EPGPlus.reset_settings Przywróć ustawienia domyÅ›lne +EPGPlus.save_settings Zapisz ustawienia +EPGPlus.scroll_mode przewijanie +EPGPlus.select_font_name Wybór czcionki +EPGPlus.settings Ustawienia +EPGPlus.slider_width Szerokość suwaka +EPGPlus.stretch_mode rozciÄ…ganie +EPGPlus.swap_mode Rodzaj przewijania +EPGPlus.timeline_fontdate Data +EPGPlus.timeline_fonttime Czas +EPGPlus.vergap1_width Szerokość lewego odstÄ™pu +EPGPlus.vergap2_width Szerokość prawego odstÄ™pu +EPGPlus.view_mode Tryb wyÅ›wietlania +GENRE.ARTS.0 sztuka/kultura +GENRE.ARTS.1 sztuki plastyczne +GENRE.ARTS.10 magazyn artystyczny/kulturalny +GENRE.ARTS.11 moda +GENRE.ARTS.2 sztuki piÄ™kne +GENRE.ARTS.3 religia +GENRE.ARTS.4 sztuka ludowa +GENRE.ARTS.5 literatura +GENRE.ARTS.6 film/kino +GENRE.ARTS.7 film eksperymentalny +GENRE.ARTS.8 radio/prasa +GENRE.ARTS.9 nowe media +GENRE.CHILDRENs_PROGRAMMES.0 program dla dzieci / mÅ‚odzieży +GENRE.CHILDRENs_PROGRAMMES.1 program dla dzieci w wieku przedszkolnym +GENRE.CHILDRENs_PROGRAMMES.2 program rozrywkowy dla osób w wieku 6 – 14 lat +GENRE.CHILDRENs_PROGRAMMES.3 program rozrywkowy dla osób w wieku 10 – 16 lat +GENRE.CHILDRENs_PROGRAMMES.4 program informacyjny i edukacyjny +GENRE.CHILDRENs_PROGRAMMES.5 film rysunkowy +GENRE.DOCUS_MAGAZINES.0 dokument/magazyn +GENRE.DOCUS_MAGAZINES.1 przyroda/zwierzÄ™ta/Å›rodowisko +GENRE.DOCUS_MAGAZINES.2 nauki przyrodnicze i technika +GENRE.DOCUS_MAGAZINES.3 medycyna/fizjologia/psychologia +GENRE.DOCUS_MAGAZINES.4 zagranica/wyprawy +GENRE.DOCUS_MAGAZINES.5 nauki spoÅ‚eczne i humanistyczne +GENRE.DOCUS_MAGAZINES.6 edukacja +GENRE.DOCUS_MAGAZINES.7 jÄ™zyki +GENRE.MOVIE.0 film fabularny/dramat +GENRE.MOVIE.1 film kryminalny/dreszczowiec +GENRE.MOVIE.2 film przygodowy/western/film wojenny +GENRE.MOVIE.3 fantastyka naukowa/fantasy/horror +GENRE.MOVIE.4 komedia +GENRE.MOVIE.5 opera mydlana/melodramat/folklor +GENRE.MOVIE.6 film romantyczny +GENRE.MOVIE.7 film/dramat poważny/klasyczny/religijny/historyczny +GENRE.MOVIE.8 film/dramat dla dorosÅ‚ych +GENRE.MUSIC_DANCE.0 muzyka/balet/taniec +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 muzyka poważna/klasyczna +GENRE.MUSIC_DANCE.3 muzyka ludowa +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musical/opera +GENRE.MUSIC_DANCE.6 balet +GENRE.NEWS.0 wiadomoÅ›ci +GENRE.NEWS.1 wiadomoÅ›ci/pogoda +GENRE.NEWS.2 magazyn informacyjny +GENRE.NEWS.3 dokument +GENRE.NEWS.4 dyskusja/wywiad/debata +GENRE.SHOW.0 widowisko / teleturniej +GENRE.SHOW.1 teleturniej/kwiz +GENRE.SHOW.2 rewia +GENRE.SHOW.3 talk-show +GENRE.SOCIAL_POLITICAL.0 wydarzenia spoÅ‚eczne i polityczne / gospodarka +GENRE.SOCIAL_POLITICAL.1 magazyn / reportaż / dokumentacja +GENRE.SOCIAL_POLITICAL.2 gospodarka i sprawy spoÅ‚eczne +GENRE.SOCIAL_POLITICAL.3 wyjÄ…tkowi ludzie +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 wydarzenia specjalne (olimpiady, mistrzostwa Å›wiata itp.) +GENRE.SPORTS.10 jazda konna +GENRE.SPORTS.11 sztuki walki +GENRE.SPORTS.2 programy sportowe +GENRE.SPORTS.3 piÅ‚ka nożna +GENRE.SPORTS.4 tenis +GENRE.SPORTS.5 sporty drużynowe +GENRE.SPORTS.6 lekka atletyka +GENRE.SPORTS.7 sporty motorowe +GENRE.SPORTS.8 sporty wodne +GENRE.SPORTS.9 sporty zimowe +GENRE.TRAVEL_HOBBIES.0 podróże & rekreacja +GENRE.TRAVEL_HOBBIES.1 podróże & turystyka +GENRE.TRAVEL_HOBBIES.2 rzemiosÅ‚o +GENRE.TRAVEL_HOBBIES.3 motocykle +GENRE.TRAVEL_HOBBIES.4 kondycja fizyczna i zdrowie +GENRE.TRAVEL_HOBBIES.5 gotowanie +GENRE.TRAVEL_HOBBIES.6 zakupy +GENRE.TRAVEL_HOBBIES.7 ogród +GENRE.UNKNOWN nieznana kategoria +apids.hint_1 Wpisz APIDy, które majÄ… byç strumieniowane, +apids.hint_2 oddzielone znakiem ' ' w formie szesnastkowej +apidselector.head Wybór jÄ™zyka +audiomenu.PCMOffset Zmniejszenie gÅ‚oÅ›noÅ›ci PCM +audiomenu.analogout WyjÅ›cie analogowe +audiomenu.avs avs +audiomenu.avs_control Tryb sterowania gÅ‚oÅ›noÅ›ciÄ… +audiomenu.dolbydigital Dolby Digital +audiomenu.head Ustawienia dźwiÄ™ku +audiomenu.lirc lirc +audiomenu.monoleft mono lewy kanaÅ‚ +audiomenu.monoright mono prawy kanaÅ‚ +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add dodaj +audioplayer.artist_title wykonawca, tytuÅ‚ +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id wybór wg ID +audioplayer.button_select_title_by_name wybór wg nazwy +audioplayer.defdir Katalog startowy +audioplayer.delete usuÅ„ +audioplayer.deleteall usuÅ„ wszystko +audioplayer.display_order Kolejność +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward do przodu +audioplayer.follow Wybór bieżącego utworu +audioplayer.head Lista odtwarzania Mp3 +audioplayer.highprio Wysoki priorytet dekodowania +audioplayer.id3scan wczytaj informacje ID3 +audioplayer.jump_backwards skocz do tyÅ‚u +audioplayer.jump_dialog_hint1 wzglÄ™dnie, +audioplayer.jump_dialog_hint2 w sekundach. +audioplayer.jump_dialog_title Podaj wartość +audioplayer.jump_forwards skocz do przodu +audioplayer.keylevel zmieÅ„ funkcje klawiszy +audioplayer.name Odtwarzacz MP3 +audioplayer.pause pauza +audioplayer.play odtwarzaj +audioplayer.playing bieżący utwór +audioplayer.playlist_fileerror_msg File could not be created: +audioplayer.playlist_fileerror_title Error +audioplayer.playlist_fileoverwrite_msg Do you want to overwrite this file: +audioplayer.playlist_fileoverwrite_title Overwrite? +audioplayer.playlist_name filename of the play list +audioplayer.playlist_name_hint1 Please enter the filename of the playlist +audioplayer.playlist_name_hint2 The extension .m3u will be added automatically +audioplayer.reading_files reading files +audioplayer.repeat_on enable repeat mode +audioplayer.rewind do tyÅ‚u +audioplayer.save_playlist zapisz listÄ™ +audioplayer.screensaver_timeout Wygaszacz ekranu (min, 0=wyÅ‚) +audioplayer.select_title_by_name search title by name (SMS) +audioplayer.show_playlist Show Playlist +audioplayer.shuffle odtw. losowo +audioplayer.stop zatrzymaj +audioplayer.title_artist tytuÅ‚, wykonawca +audioplayerpicsettings.general Odtw. MP3 / przeglÄ…darka obr. +bookmarkmanager.delete usuÅ„ +bookmarkmanager.name ZakÅ‚adki +bookmarkmanager.rename zmieÅ„ +bookmarkmanager.select wybierz +bouqueteditor.add dodaj +bouqueteditor.bouquetname Nazwa bukietu +bouqueteditor.delete usuÅ„ +bouqueteditor.discardingchanges Zmiany zostanÄ… odrzucone. ProszÄ™ czekaç ... +bouqueteditor.hide ukryj +bouqueteditor.lock blokuj +bouqueteditor.move przenieÅ› +bouqueteditor.name ZarzÄ…dzanie bukietami +bouqueteditor.newbouquetname Nowa nazwa bukietu +bouqueteditor.rename zmieÅ„ nazwÄ™ +bouqueteditor.return Gotowe +bouqueteditor.savechanges? Czy chcesz zapisaç zmiany? +bouqueteditor.savingchanges Trwa zapisywanie zmian. ProszÄ™ czekaç ... +bouqueteditor.switch dodaj/usuÅ„ kanaÅ‚ +bouqueteditor.switchmode TV/radio +bouquetlist.head Bukiety +cablesetup.provider Dostawca telewizji kablowej +channellist.head Wszystkie kanaÅ‚y +channellist.nonefound Brak listy kanałów!\nUruchom wyszukiwanie kanałów\n(DBOX -> UsÅ‚ugi) +channellist.since od %02d:%02d +colorchooser.alpha ALPHA +colorchooser.blue BLUE +colorchooser.green GREEN +colorchooser.red RED +colormenu.background Kolor tÅ‚a +colormenu.fade Zanikanie menu +colormenu.font Wielkość czcionki +colormenu.gtx_alpha Przezroczystość (GTX) +colormenu.head Ustawienia kolorów +colormenu.menucolors Kolory menu +colormenu.statusbar Pasek stanu +colormenu.textcolor Kolor tekstu +colormenu.themeselect Wybór tematów +colormenu.timing Czasy wyÅ›wietlania OSD +colormenusetup.head Kolory menu +colormenusetup.menucontent Zawartość okna +colormenusetup.menucontent_inactive Nieaktywna zawartość okna +colormenusetup.menucontent_selected Wybrana zawartość okna +colormenusetup.menuhead Pasek tytuÅ‚u +colorstatusbar.head Kolory paska stanu +colorstatusbar.text Pasek stanu +colorthememenu.classic_theme klasyczna +colorthememenu.dblue_theme DarkBlue +colorthememenu.dvb2k_theme DVB2000 +colorthememenu.head Wybór tematów +colorthememenu.neutrino_theme Neutrino +date.Apr Kwi +date.Aug Sie +date.Dec Gru +date.Feb Lut +date.Fri Pt +date.Jan Sty +date.Jul Lip +date.Jun Cze +date.Mar Mar +date.May Maj +date.Mon Pn +date.Nov Lis +date.Oct Paz +date.Sat So +date.Sep Wrz +date.Sun Ni +date.Thu Cz +date.Tue Wt +date.Wed Sr +epglist.head Lista programów - %s +epglist.noevents Informacje EPG sÄ… niedostÄ™pne +epgviewer.More_Screenings Kolejne emisje na tym kanale +epgviewer.nodetailed Brak szczegółowych informacji +epgviewer.notfound Nie znaleziono informacji programowych (EPG) +eventlistbar.channelswitch przeÅ‚Ä…cz +eventlistbar.eventsort sortuj +eventlistbar.recordevent nagraj +favorites.addchannel Bieżący kanaÅ‚ zostanie dodany do bukietu \n"Ulubione". \nProszÄ™ czekaç... +favorites.bouquetname Ulubione +favorites.bqcreated Utworzono kanaÅ‚y ulubione...\n +favorites.chadded Bieżący kanaÅ‚ zostaÅ‚ dodany do ulubionych...\n +favorites.chalreadyinbq Bieżący kanaÅ‚ znajduje siÄ™ już w ulubionych...\n +favorites.finalhint \nUżyj edytora bukietów, aby dokonaç\nzmian w ulubionych.\n +favorites.menueadd Dodaj kanaÅ‚ do ulubionych +favorites.nobouquets Funkcja jest dostÄ™pna wyÅ‚Ä…cznie przy wÅ‚Ä…czonych bukietach. +filebrowser.delete usuÅ„ +filebrowser.denydirectoryleave Montuj jako główny katalog +filebrowser.dodelete1 usuÅ„ +filebrowser.dodelete2 ? +filebrowser.filter.active wÅ‚Ä…cz filtr +filebrowser.filter.inactive wyÅ‚Ä…cz filtr +filebrowser.head PrzeglÄ…darka plików +filebrowser.mark zaznacz +filebrowser.nextpage nast.strona +filebrowser.prevpage poprz.strona +filebrowser.scan Trwa wyszukiwanie... +filebrowser.select wybierz +filebrowser.showrights Pokaż uprawnienia plików +filebrowser.sort.date (data) +filebrowser.sort.name (pliki) +filebrowser.sort.namedirsfirst (kat,pliki) +filebrowser.sort.size (rozmiar) +filebrowser.sort.type (typ) +flashupdate.actionreadflash reading +flashupdate.cantopenfile Nie można otworzyç pliku +flashupdate.cantopenmtd Nie można otworzyç urzÄ…dzenia MTD +flashupdate.checkupdate Szukaj nowej wersji +flashupdate.currentreleasecycle Cykl wersji +flashupdate.currentversion_sep Zainstalowana wersja +flashupdate.currentversiondate Data +flashupdate.currentversionsnapshot Typ obrazu +flashupdate.currentversiontime Godzina +flashupdate.erasefailed Kasowanie pamiÄ™ci nie powiodÅ‚o siÄ™ +flashupdate.erasing Kasuj pamięç flash +flashupdate.experimentalimage Wybrano obraz stanu. Należy wziąç pod uwagÄ™, że wersja ta nie\nzostaÅ‚a sprawdzona, a tuner może nie dziaÅ‚aç po wykonaniu\naktualizacji.\n\nCzy na pewno chcesz zainstalowaç tÄ™ wersjÄ™? +flashupdate.expertfunctions Funkcje zaawansowane +flashupdate.fileis0bytes Wielkość pliku wynosi 0 bajtów +flashupdate.fileselector Wybór pliku +flashupdate.flashreadyreboot Programowanie obrazu powiodÅ‚o siÄ™.\nBox zostanie uruchomiony ponownie. +flashupdate.getinfofile Trwa sprawdzanie wersji +flashupdate.getinfofileerror Nie można sprawdziç wersji +flashupdate.getupdatefile ZaÅ‚aduj aktualizacjÄ™ +flashupdate.getupdatefileerror Nie można zaÅ‚adowaç aktualizacji +flashupdate.globalprogress PostÄ™p caÅ‚kowity: +flashupdate.head Aktualizacja +flashupdate.md5check Kontrola obrazu +flashupdate.md5sumerror NieprawidÅ‚owy obraz +flashupdate.msgbox Znaleziono nowy file:\nData: %s, %s\nObraz podstawowy: %s\nTyp: %s\n\nCzy chcesz teraz pobraç i zainstalowaç\nten obraz? +flashupdate.msgbox_manual Znaleziono nowy obrazu:\nData: %s, %s\nObraz podstawowy: %s\nTyp obrazu: %s\n\nCzy chcesz teraz zainstalowaç\nten obraz? +flashupdate.mtdselector Wybór partycji +flashupdate.programmingflash Programowanie pamiÄ™ci flash +flashupdate.proxypassword HasÅ‚o +flashupdate.proxypassword_hint1 Wpisz hasÅ‚o serwera proxy +flashupdate.proxypassword_hint2 Pusty wpis oznacza brak hasÅ‚a +flashupdate.proxyserver Nazwa serwera proxy +flashupdate.proxyserver_hint1 Wpisz nazwÄ™ serwera proxy lub IP (host:port) +flashupdate.proxyserver_hint2 Pusty wpis oznacza brak serwera proxy +flashupdate.proxyserver_sep Serwer proxy +flashupdate.proxyusername Nazwa użytkownika +flashupdate.proxyusername_hint1 Wpisz nazwÄ™ użytkownika serwera proxy +flashupdate.proxyusername_hint2 Pusty wpis oznacza brak autoryzacji +flashupdate.readflash odczytaj caÅ‚y obraz z pamiÄ™ci flash +flashupdate.readflashmtd odczytaj pojedynczÄ… partycjÄ™ +flashupdate.ready Gotowe. +flashupdate.reallyflashmtd Czy na pewno chcesz wykonaç programowanie?\n\nW przypadku wystÄ…pienia bÅ‚Ä™du lub nieprawidÅ‚owego obrazu\n box nie wystartuje.\n\nNazwa obrazu: %s\nCel: %s +flashupdate.savesuccess Obraz zostaÅ‚ zapisany pomyÅ›lnie\npod nazwÄ… %s. +flashupdate.selectimage DostÄ™pne obrazy/files +flashupdate.squashfs.noversion SquashFS version checks are currently only supported when updating over the web.\nAre you sure that you wish to install this image? +flashupdate.titlereadflash Odczyt pamiÄ™ci flash +flashupdate.titlewriteflash Zapis pamiÄ™ci flash +flashupdate.updatemode Tryb aktualizacji +flashupdate.updatemode_internet Internet +flashupdate.updatemode_manual rÄ™czny (ftp) +flashupdate.url_file Plik konfiguracyjny +flashupdate.versioncheck Kontrola wersji +flashupdate.writeflash wgraj caÅ‚y obraz do pamiÄ™ci flash +flashupdate.writeflashmtd wgraj pojedynczÄ… partycjÄ™ +flashupdate.wrongbase flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Lista kanałów +fontmenu.epg EPG +fontmenu.eventlist Lista programów +fontmenu.gamelist Lista gier +fontmenu.head Ustawienia wielkoÅ›ci czcionki +fontmenu.infobar Pasek informacyjny +fontsize.channel_num_zap Bez funkcji +fontsize.channellist Lista kanałów +fontsize.channellist_descr Opis +fontsize.channellist_number Numer +fontsize.epg_date Data EPG +fontsize.epg_info1 Informacja EPG 1 +fontsize.epg_info2 Informacja EPG 2 +fontsize.epg_title TytuÅ‚ EPG +fontsize.eventlist_datetime Data / godzina listy programów +fontsize.eventlist_itemlarge Duża lista programów +fontsize.eventlist_itemsmall MaÅ‚a lista programów +fontsize.eventlist_title TytuÅ‚ listy programów +fontsize.filebrowser_item Wpis przeglÄ…darki plików +fontsize.gamelist_itemlarge duża +fontsize.gamelist_itemsmall maÅ‚a +fontsize.hint Trwa inicjalizowanie czcionki,\nproszÄ™ czekaç... +fontsize.infobar_channame Nazwa kanaÅ‚u +fontsize.infobar_info Informacja na pasku +fontsize.infobar_number KanaÅ‚ na pasku informacyjnym +fontsize.infobar_small MaÅ‚y pasek informacyjny +fontsize.menu Tekst menu +fontsize.menu_info Menu informacyjne +fontsize.menu_title TytuÅ‚ menu +gtxalpha.alpha1 1 +gtxalpha.alpha2 2 +infoviewer.epgnotload Informacje nie zostaÅ‚y jeszcze odczytane... +infoviewer.epgwait Trwa odczytywanie informacji EPG... +infoviewer.eventlist prog +infoviewer.languages jÄ™zyk +infoviewer.motor_moving Ustawienie anteny +infoviewer.nocurrent Brak informacji o bieżącym programie +infoviewer.noepg Informacje EPG sÄ… niedostÄ™pne +infoviewer.notavailable KanaÅ‚ (obecnie) niedostÄ™pny. +infoviewer.selecttime Czasy rozpoczÄ™cia +infoviewer.streaminfo funkcje +infoviewer.subservice PodusÅ‚ugi +infoviewer.waittime Trwa odczytywanie godziny... +ipsetup.hint_1 Wpisz 0...9 lub naciÅ›nij klawisz góra/dół +ipsetup.hint_2 OK = zapis, HOME = anuluj +keybindingmenu.RC Ustawienia pilota +keybindingmenu.addrecord Dodaj nagrywanie +keybindingmenu.addremind Dodaj zmianÄ™ kanaÅ‚u +keybindingmenu.allchannels_on_ok lista kanałów +keybindingmenu.bouquetchannels_on_ok bieżący bukiet +keybindingmenu.bouquetdown Poprzedni bukiet +keybindingmenu.bouquethandling Klawisz OK +keybindingmenu.bouquetlist_on_ok lista bukietów +keybindingmenu.bouquetup NastÄ™pny bukiet +keybindingmenu.cancel Zamknij listÄ™ kanałów +keybindingmenu.channeldown NastÄ™pny kanaÅ‚ +keybindingmenu.channellist Lista kanałów +keybindingmenu.channelup Poprzedni kanaÅ‚ +keybindingmenu.head Konfiguracja klawiszy +keybindingmenu.lastchannel Quick Zap +keybindingmenu.modechange Zmiana trybu +keybindingmenu.pagedown NastÄ™pna strona +keybindingmenu.pageup Poprzednia strona +keybindingmenu.quickzap Szybkie przeÅ‚Ä…czanie kanałów +keybindingmenu.repeatblock Opóźnienie powtarzania +keybindingmenu.repeatblockgeneric Opóźnienie poczÄ…tkowe +keybindingmenu.sort ZmieÅ„ kolejność sortowania +keybindingmenu.subchanneldown Poprzedni podkanaÅ‚ +keybindingmenu.subchannelup NastÄ™pny podkanaÅ‚ +keybindingmenu.tvradiomode TV / Radio +keybindingmenu.zaphistory Zapping History Bouquet +keychooser.head Ustaw nowy klawisz +keychooser.text1 NaciÅ›nij klawisz +keychooser.text2 Czekaj, aby anulowaç... +keychoosermenu.currentkey bieżący klawisz +keychoosermenu.setnew ustaw nowy klawisz +keychoosermenu.setnone brak klawisza +languagesetup.head Ustawienia jÄ™zykowe +languagesetup.select JÄ™zyk +lcdcontroler.brightness Jasność +lcdcontroler.brightnessstandby Tryb gotowoÅ›ci +lcdcontroler.contrast Kontrast +lcdcontroler.head Ustawienia LCD +lcdmenu.autodimm Automatyczne przyciemnianie +lcdmenu.head Ustawienia LCD +lcdmenu.inverse Inwersja +lcdmenu.lcdcontroler Kontrast / Jasność +lcdmenu.power Zasilanie +lcdmenu.statusline Pasek stanu +lcdmenu.statusline.both gÅ‚oÅ›ność / czas +lcdmenu.statusline.playtime czas +lcdmenu.statusline.volume gÅ‚oÅ›ność +mainmenu.audioplayer Odtwarzacz MP3 +mainmenu.games Gry +mainmenu.head Menu główne +mainmenu.movieplayer Odtwarzacz filmów +mainmenu.pausesectionsd Czytaj informacje EPG +mainmenu.pictureviewer PrzeglÄ…darka obrazów +mainmenu.radiomode Radio +mainmenu.recording Nagrywanie +mainmenu.recording_start START +mainmenu.recording_stop STOP +mainmenu.scartmode EurozÅ‚Ä…cze +mainmenu.service UsÅ‚ugi +mainmenu.settings Konfiguracja +mainmenu.shutdown WyÅ‚Ä…cz +mainmenu.sleeptimer WyÅ‚Ä…cznik czasowy +mainmenu.tvmode TV +mainsettings.audio DźwiÄ™k +mainsettings.colors Kolory / tematy / czcionki +mainsettings.head Konfiguracja +mainsettings.keybinding Konfiguracja klawiszy +mainsettings.language JÄ™zyk +mainsettings.lcd WyÅ›wietlacz LCD +mainsettings.misc Inne ustawienia +mainsettings.network Sieç +mainsettings.recording Nagrywanie +mainsettings.savesettingsnow zapisz ustawienia +mainsettings.savesettingsnow_hint Trwa zapisywanie ustawieÅ„,\nproszÄ™ czekaç... +mainsettings.streaming Odtwarzacz filmów +mainsettings.video Obraz +menu.back wstecz +messagebox.back Wróç +messagebox.cancel Anuluj +messagebox.discard Czy chcesz odrzuciç zmiany? +messagebox.error BÅ‚Ä…d +messagebox.info Informacja +messagebox.no NIE +messagebox.yes TAK +miscsettings.bootinfo Informacje przy starcie +miscsettings.bootmenu Show boot menu +miscsettings.driver_boot Ustawienia zaawansowane +miscsettings.fb_destination Konsola startowa +miscsettings.general Ustawienia ogólne +miscsettings.head Inne ustawienia +miscsettings.hwsections użyj hardware sections +miscsettings.infobar_sat_display WyÅ›wietlanie nazwy satelity +miscsettings.pmtupdate enable pmt update +miscsettings.shutdown_real Tryb gotowoÅ›ci +miscsettings.shutdown_real_rcdelay Opóźnione wyÅ‚Ä…czenie +miscsettings.sptsmode użyj trybu SPTS +miscsettings.startbhdriver zaÅ‚aduj sterowniki BH +motorcontrol.head Konfiguracja siÅ‚ownika +movieplayer.bookmark ZakÅ‚adki +movieplayer.bookmarkname Nazwa zakÅ‚adki +movieplayer.bookmarkname_hint1 Podaj nazwÄ™ nowej zakÅ‚adki +movieplayer.bookmarkname_hint2 +movieplayer.buffering Trwa buforowanie... +movieplayer.defdir Katalog startowy +movieplayer.dvdplayback DVD +movieplayer.fileplayback Plik przez VLC +movieplayer.goto Skocz do ... +movieplayer.goto.h1 = -> skok bezwzglÄ™dny +movieplayer.goto.h2 +,- -> skok wzglÄ™dny +movieplayer.head Odtwarzacz filmów +movieplayer.nostreamingserver NawiÄ…zanie poÅ‚Ä…czenia z serwerem\nstrumieniowania nie powiodÅ‚o siÄ™. +movieplayer.pesplayback Odtwarzaj PES (experimental) +movieplayer.pleasewait ProszÄ™ czekaç. Trwa nawiÄ…zywanie\npoÅ‚Ä…czenia z serwerem strumieniowania. +movieplayer.toomanybookmarks Za dużo zakÅ‚adek.\nAby dodaç kolejnÄ… musisz jednÄ… usunąç. +movieplayer.tsplayback Odtwarzaj TS +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion Ta opcja nie jest wspierana przez twojÄ… wersjÄ™ VLC +networkmenu.broadcast RozgÅ‚aszanie +networkmenu.dhcp Pobierz z DHCP +networkmenu.gateway Brama domyÅ›lna +networkmenu.head Konfiguracja sieci +networkmenu.ipaddress Adres IP +networkmenu.mount Zasoby sieciowe +networkmenu.nameserver Serwer nazw +networkmenu.netmask Maska podsieci +networkmenu.setupnow Zastosuj ustawienia +networkmenu.setuponstartup Zastosuj podczas uruchamiania +networkmenu.show Pokaż bieżące ustawienia +networkmenu.test Testuj ustawienia sieci +nfs.alreadymounted Katalog jest już podÅ‚Ä…czony. +nfs.automount PodÅ‚Ä…cz przy uruchamianiu +nfs.dir Katalog/zasób współużytkowany +nfs.ip IP serwera NFS/CIFS +nfs.localdir Patalog lokalny +nfs.mount PodÅ‚Ä…cz katalog +nfs.mount_options Parametry +nfs.mounterror BÅ‚Ä…d podÅ‚Ä…czenia +nfs.mounterror_notsup NieobsÅ‚ugiwany system plików. +nfs.mountnow PodÅ‚Ä…cz teraz +nfs.mounttimeout BÅ‚Ä…d podÅ‚Ä…czenia: upÅ‚ynÄ…Å‚ limit czasu +nfs.password HasÅ‚o CIFS +nfs.remount OdÅ›wież +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount OdÅ‚Ä…cz katalog +nfs.umounterror BÅ‚Ä…d przy odÅ‚Ä…czaniu +nfs.username Nazwa użytkownika CIFS +nfsmenu.head Ustawienia NFS/CIFS +nvod.percentage (minęło: %d%% min) +nvod.starting (rozpoczÄ™cie za %d min) +nvodselector.directormode Widok +nvodselector.head Wybierz czas rozpoczÄ™cia +nvodselector.subservice Perspektywy +options.default przywróç wartoÅ›ci domyÅ›lne +options.fb ekran +options.null brak +options.off NIE +options.on TAK +options.serial serial +parentallock.changepin Zmiana kodu PIN +parentallock.changepin_hint1 Wpisz nowy kod PIN! +parentallock.changetolocked zablokowane bukiety +parentallock.head Blokada rodzicielska +parentallock.lockage Blokuj programy +parentallock.lockage12 od 12-tu lat +parentallock.lockage16 od 16-tu lat +parentallock.lockage18 od 18-tu lat +parentallock.lockedchannel KanaÑ– zablokowany... +parentallock.lockedprogram Program zablokowany (od lat %d) +parentallock.never nigdy +parentallock.onsignal po wysÅ‚aniu blokady +parentallock.parentallock Blokada rodzicielska +parentallock.prompt żądanie kodu PIN +pictureviewer.defdir Katalog startowy +pictureviewer.head PrzeglÄ…darka obrazów +pictureviewer.resize.color_average zaawansowane +pictureviewer.resize.none brak +pictureviewer.resize.simple proste +pictureviewer.scaling Rodzaj skalowania +pictureviewer.show WyÅ›wietl +pictureviewer.slide_time Czas wyÅ›wietlania pokazu slajdów +pictureviewer.slideshow Pokaz slajdów +pictureviewer.sortorder ZmieÅ„ kolejność sortowania +pictureviewer.sortorder.date (data) +pictureviewer.sortorder.filename (nazwa pliku) +ping.ok jest dostÄ™pny (ping) +ping.protocol jest niedostÄ™pny (bÅ‚Ä…d hosta lub protokoÅ‚u) +ping.socket jest niedostÄ™pny (bÅ‚Ä…d gniazda) +ping.unreachable jest niedostÄ™pny +pinprotection.head Wpisz kod PIN +pinprotection.wrongcode Wpisz ponownie kod PIN! +rclock.lockmsg sterowanie boxem zostanie zablokowane\n\naby je przywróciç naciÅ›nij [RED], a nastÄ™pnie [DBOX] +rclock.menueadd Blokada sterowania +rclock.title Blokada sterowania +rclock.unlockmsg sterowanie zostaÅ‚o przywrócone +recordingmenu.defdir Nagrywaj do katalogu +recordingmenu.file dysk +recordingmenu.head Ustawienia nagrywania +recordingmenu.help UrzÄ…dzenia nagrywajÄ…ce:\n--------------------------------------\nserwer:\nwykorzystuje serwer strumieniowania\n\nmagnetowid:\nwykorzystuje gniazdo magnetowidu (drugi scart)\n\ndysk:\nwykorzystuje zasób sieciowy (NFS)\nlub wewnÄ™trzny dysk twardy\nTS: używaj trybu SPTS(Dbox2)\nPES: nie używaj trybu SPTS(Dbox2)\n\n\nMaksymalny rozmiar pliku:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: bez ograniczeÅ„ (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB)\n\nDla NFS (UDP) zaleca siÄ™ zapis synchroniczny +recordingmenu.no_scart Nie przeÅ‚Ä…czaj w tryb SCART +recordingmenu.off wyÅ‚Ä…czone +recordingmenu.recording_type UrzÄ…dzenie nagrywajÄ…ce +recordingmenu.server serwer +recordingmenu.server_ip Adres IP serwera +recordingmenu.server_mac Adres Mac serwera (WoL) +recordingmenu.server_port Port serwera +recordingmenu.server_wakeup Budzenie serwera (WoL) +recordingmenu.setupnow Zastosuj ustawienia +recordingmenu.splitsize Dziel plik na części (MB) +recordingmenu.stopplayback Zatrzymaj playback +recordingmenu.stopsectionsd Zatrzymaj sectionsd +recordingmenu.stream_vtxt_pid Nagrywaj teletext +recordingmenu.use_o_sync Zapis synchroniczny (O_SYNC) +recordingmenu.vcr magnetowid +recordtimer.announce Za kilka minut rozpocznie siÄ™ nagrywanie. +repeatblocker.hint_1 Czas opóźnienia miÄ™dzy 2 naciÅ›niÄ™ciami klawiszy +repeatblocker.hint_2 0 = wyÅ‚Ä…czenie blokady (przycisk czerwony) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat Liczba powtórzeÅ„ DiSEqC +satsetup.extended Konfiguracja DiSEqC +satsetup.extended_motor Konfiguracja siÅ‚ownika +satsetup.minidiseqc mini-DiSEqC +satsetup.motorcontrol RÄ™czna konfiguracja siÅ‚ownika +satsetup.nodiseqc brak DiSEqC +satsetup.satellite Satelita +satsetup.savesettingsnow zapisz ustawienia +satsetup.smatvremote strojenie zdalne SMATV +scants.actcable Kabel: +scants.actsatellite Satelita: +scants.bouquet Bukiety +scants.bouquet_create utwórz nowe +scants.bouquet_erase usuÅ„ +scants.bouquet_leave bez zmian +scants.bouquet_satellite bukiet pozycji sat. +scants.bouquet_update aktualizuj +scants.channel KanaÅ‚: +scants.failed Wyszukiwanie kanałów nie powiodÅ‚o siÄ™! +scants.finished Wyszukiwanie kanałów powiodÅ‚o siÄ™! +scants.freqdata CzÄ™stotliwość: +scants.head Wyszukiwanie kanałów +scants.numberofdataservices Dane +scants.numberofradioservices Radio +scants.numberoftotalservices Ogółem +scants.numberoftvservices TV +scants.provider Dostawca: +scants.startnow Uruchom wyszukiwanie +scants.transponders Transponder: +screensetup.lowerright zielony = dolna, prawa kraw. +screensetup.upperleft czerwony = górna, lewa kraw. +servicemenu.head UsÅ‚ugi +servicemenu.reload PrzeÅ‚aduj listy kanałów +servicemenu.reload_hint Trwa przeÅ‚adowywanie list kanałów, \nproszÄ™ czekaç... +servicemenu.scants Wyszukaj kanaÅ‚y +servicemenu.ucodecheck Sprawdź wersje uCodes +servicemenu.update Aktualizuj oprogramowanie +settings.help Pomoc +settings.missingoptionsconffile Ustawienia Neutrino zostaÅ‚y rozszerzone.\nDla nowych opcji zostanÄ… okreÅ›lone wartoÅ›ci domyÅ›lne. +settings.noconffile Nie można odnaleźç ustawieÅ„ Neutrino.\nZostanÄ… użyte wartoÅ›ci domyÅ›lne. +shutdowntimer.announce Tuner zostanie wyÅ‚Ä…czony za minutÄ™.\nCzy chcesz anulowaç proces? +sleeptimerbox.announce Tuner zostanie wyÅ‚Ä…czony za minutÄ™ +sleeptimerbox.hint1 Czas do wyÅ‚Ä…czenia w min. (000=wyÅ‚) +sleeptimerbox.hint2 Po upÅ‚ywie tego czasu, tuner zostanie wyÅ‚Ä…czony +sleeptimerbox.title WyÅ‚Ä…cznik czasowy +streamfeatures.head Funkcje +streaminfo.aratio Format obrazu +streaminfo.aratio_unknown Format obrazu: nieznany +streaminfo.audiotype Format dźwiÄ™ku +streaminfo.audiotype_unknown Audiotype: unknown +streaminfo.bitrate Szybkość transmisji +streaminfo.framerate Szybkość klatek +streaminfo.framerate_unknown Szybkość klatek: nieznana +streaminfo.head Stream-Information +streaminfo.not_available niedostÄ™pny +streaminfo.resolution test +streaminfo.signal Parametry sygnaÅ‚u +streaming.buffer_overflow Nagrywanie zostaÅ‚o przerwane\nz powodu zbyt dużych opóźnieÅ„ w zapisie. +streaming.busy Proces nagrywania wciąż trwa.\nJeÅ›li komunikat ten siÄ™ powtarza i nie jesteÅ› w trakcie nagrywania,\nzrestartuj Neutrino. +streaming.dir_not_writable Brak możliwoÅ›ci zapisu do katalogu nagrywania.\nNagrywanie nie bÄ™dzie dziaÅ‚aç. +streaming.success Nagrywanie zakoÅ„czone sukcesem. +streaming.write_error Nagrywanie zostaÅ‚o przerwane\nz powodu bÅ‚Ä™du w trakcie zapisu. +streaming.write_error_open Nagrywanie zostaÅ‚o przerwane\nz powodu bÅ‚Ä™du otwarcia pliku do zapisu. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Ustawienia odtwarzacza filmów +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off wyÅ‚Ä…czony +streamingmenu.on wÅ‚Ä…czony +streamingmenu.server_ip IP serwera strumieniowania +streamingmenu.server_port Port serwera strumieniowania +streamingmenu.streaming_audiorate Szybkość transmisji dźwiÄ™ku +streamingmenu.streaming_force_avi_rawaudio WymuÅ› AC3 dla AVI +streamingmenu.streaming_force_transcode_video Transkoduj obraz (MPG/VCD) +streamingmenu.streaming_resolution Rozdzielczość +streamingmenu.streaming_server_cddrive NapÄ™d DVD +streamingmenu.streaming_server_startdir Katalog (VLC) +streamingmenu.streaming_transcode_audio Transkoduj dźwiÄ™k (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec Kodowanie obrazu +streamingmenu.streaming_type Serwer strumieniowania +streamingmenu.streaming_videorate Szybkość transmisji obrazu +streamingserver.noconnect Brak poÅ‚Ä…czenia z serwerem strumieniowym.\nNagrywanie zostanie przerwane. +stringinput.caps wielkie/maÅ‚e litery +stringinput.clear usuÅ„ wszystko +timer.eventrecord.msg Program zostanie nagrany. +timer.eventrecord.title zapamiÄ™taj nagrywanie +timer.eventtimed.msg Program zostaÅ‚ zapamiÄ™tany. +timer.eventtimed.title zapamiÄ™taj program +timerbar.channelswitch przeÅ‚Ä…cz +timerbar.recordevent nagraj +timerlist.alarmtime Godzina alarmu +timerlist.apids PIDy audio +timerlist.bouquetselect wybierz bukiet +timerlist.channel kanaÅ‚ +timerlist.channelselect wybierz kanaÅ‚ +timerlist.delete usuÅ„ +timerlist.menumodify edytuj zegar +timerlist.menunew nowy zegar +timerlist.message Wiadomość +timerlist.moderadio KanaÅ‚y radiowe +timerlist.modeselect wybierz tryb +timerlist.modetv KanaÅ‚y TV +timerlist.modify edytuj +timerlist.name Lista zegarów +timerlist.new nowy zegar +timerlist.program.unknown Nieznany program +timerlist.reload aktualizuj +timerlist.repeat Powtórzenie +timerlist.repeat.biweekly dwa razy w tygodniu +timerlist.repeat.byeventdescription patrz zegar +timerlist.repeat.daily codziennie +timerlist.repeat.fourweekly cztery razy w tygodniu +timerlist.repeat.friday Pt +timerlist.repeat.monday Po +timerlist.repeat.monthly raz w miesiÄ…cu +timerlist.repeat.once jednorazowo +timerlist.repeat.saturday So +timerlist.repeat.sunday Nd +timerlist.repeat.thursday Cz +timerlist.repeat.tuesday Wt +timerlist.repeat.unknown nieznane +timerlist.repeat.wednesday Sr +timerlist.repeat.weekdays w dni powszednie +timerlist.repeat.weekly dni tygodnia +timerlist.save zapisz zegar +timerlist.standby Tryb stanu +timerlist.standby.off ze stanu gotowoÅ›ci +timerlist.standby.on w stan gotowoÅ›ci +timerlist.stoptime godzina zatrzymania +timerlist.type Typ zegara +timerlist.type.nextprogram Kolejny program +timerlist.type.record nagrywanie +timerlist.type.remind przypomnienie +timerlist.type.shutdown zamkniÄ™cie systemu +timerlist.type.sleeptimer wyÅ‚Ä…cznik czasowy +timerlist.type.standby tryb gotowoÅ›ci +timerlist.type.unknown nieznane +timerlist.type.zapto przeÅ‚Ä…cz +timerlist.weekdays w dni powszednie +timerlist.weekdays.hint_1 Pn Wt Sr Cz Pt So Ni +timerlist.weekdays.hint_2 'X'=zegar '-' brak zegara +timersettings.record_safety_time_after Korekta koÅ„ca nagrywania +timersettings.record_safety_time_after.hint_1 Czas w min. (00=wyÅ‚), który zostanie dodany +timersettings.record_safety_time_after.hint_2 po zakoÅ„czeniu danego zegara +timersettings.record_safety_time_before Korekta poczÄ…tku nagrywania +timersettings.record_safety_time_before.hint_1 Czas w min. (00=wyÅ‚), który zostanie odjÄ™ty +timersettings.record_safety_time_before.hint_2 przy uruchamianiu danego zegara +timersettings.separator Ustawienia zegara +timing.chanlist Lista kanałów +timing.epg Epg +timing.filebrowser PrzeglÄ…darka plików +timing.head Czasy wyÅ›wietlania OSD +timing.hint_1 Czas wyÅ›wietlania OSD +timing.hint_2 na ekranie TV (w sekundach) +timing.infobar Pasek informacyjny +timing.menu Menu +timing.numericzap Numeric Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head Wersje uCodes +ucodecheck.ucode uCode +ucodes.failure Uwaga, uCodes NIE zostaÅ‚y znalezione!\n\nWyÅ›lij pliki przez FTP (lub za pomocÄ…\nprogramu DBox-BootManager) i uruchom ponownie tuner box! +videomenu.csync korekcja synchronizacji +videomenu.head Ustawienia obrazu +videomenu.rgb_centering wyÅ›rodkowanie RGB +videomenu.screensetup Ustaw widocznÄ… część obrazu +videomenu.vcrswitch PrzeÅ‚Ä…czanie EurozÅ‚Ä…cza +videomenu.videoformat Format obrazu +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect automatyczny +videomenu.videosignal SygnaÅ‚ wideo +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Za minutÄ™ zostanie zmieniony kanaÅ‚. +extra.auto automatycznie +extra.valid wÅ‚aÅ›ciwe +extra.all wszystkie +extra.new_keys tylko nowe klucze +extra.all_keys wszystkie klucze +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo znanych algorytmów +extra.gbox_emu emulator +extra.gbox_softcam softcam +extra.gbox_net klient sieciowy +extra.gbox_mix emu+softcam+klient +extra.gbox_info Informacje gbox +extra.sep_extra Inne +extra.save_settings zapisz ustawienia +extra.extra_menu Dodatki +extra.ecm_info Pokaż ECM +extra.pid_info Pokaż PID +extra.emm_info Pokaż EMM +extra.emu_type Emulacja +extra.camd Typ emulatora +extra.restart_camd Uruchom ponownie emulator +extra.mg_settings Ustawienia mgcamd +extra.netmode Typ klienta sieciowego +extra.autoupdate Aktualizuj klucze +extra.show_ecm Pokazuj ECM +extra.show_emm Pokazuj EMM +extra.keyupdate Tryb aktualizacji +extra.osd Informacje ekranowe +extra.keyfolder Katalog z kluczami +extra.hash_pids Przetwarzaj PIDy +extra.gbox_settings Ustawienia gbox +extra.gbox_mode Tryb +extra.newcamd_settings Ustawienia newcamd +extra.show_cw Pokazuj CW +extra.debug_ecm Analizuj ECM +extra.disable_cam WyÅ‚Ä…cz CAM +extra.show_cat Pokazuj CAT +extra.show_pmt Pokazuj PMT +extra.update_pmt OdÅ›wieżaj PMT +extra.report_emm Raportuj zÅ‚e sygnatury EMM +extra.debug_emm Analizuj Emm +extra.show_allca Pokazuj wszystkie CA +extra.reload_keys PrzeÅ‚aduj klucze przy zm.kanaÅ‚u +extra.reload_cfg PrzeÅ‚aduj konfiguracje przy zm.kanaÅ‚u +extra.cw_delay Standardowe opóznienie zapisu CW +extra.dvbsnoop Informacje o strumieniu DVB +extra.dvbsnoop_pat Pokaż PAT +extra.dvbsnoop_cat Pokaż CAT +extra.dvbsnoop_tsdt Pokaż TSDT +extra.dvbsnoop_nit Pokaż NIT +extra.dvbsnoop_sdt Pokaż SDT +extra.dvbsnoop_eit Pokaż EIT +extra.dvbsnoop_rst Pokaż RST +extra.dvbsnoop_tdt Pokaż TDT +extra.dvbsnoop_sit Pokaż SIT +extra.dvbsnoop_pid Wybierz PID +extra.dvbsnoop_scan Skanuj PIDy +extra.dvbsnoop_band Pokaż szybkość przesyÅ‚u danych +extra.logview Pokaż /tmp/log +extra.ru ruDREAM +extra.english Wybieranie angielskiej Å›cieżki +extra.dboxinfo Informacje o systemie +extra.scan_mode Tryb wyszukiwania +extra.start_tostandby Uruchom w trybie czuwania +extra.rotor_swap ZamieÅ„ kierunki obrotnicy +extra.use_log Loguj do /tmp/log +extra.spts_mode Tryb SPTS +extra.hw_sect SprzÄ™towe przetwarzanie sekcji +extra.logo Logo startowe +extra.scan_full peÅ‚ne +extra.scan_fast szybkie +extra.hdd_slow Slow +extra.hdd_middle Middle +extra.hdd_fast Fast +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Sleep time +extra.hdd_noise Noise +extra.hdd_activate Activate settings +extra.hdd_fs Filesystem +extra.hdd_format Format HDD +extra.hdd_check Check filesystem +extra.hdd_settings HDD Settings +extra.clear_log Clear Log +extra.zap_cycle WyÅ‚Ä…cz historiÄ™ programów +extra.sms_channel sms-mode channel +extra.manual_scan Manual scan +extra.tp_freq Frequency +extra.tp_rate Symbol rate +extra.tp_pol Polarization +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Timezone +extra.zapit_menu Zapit options +extra.zapit_make_bouquet Make Remaining Channels list +extra.zapit_save_last_chan Save last channel +extra.zapit_motor_speed Motor moving speed (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet Add to bouquet +extra.key_list_start home +extra.key_list_end end +extra.chadded The current channel has been added to selected bouquet....\n +extra.chalreadyinbq The current channel is already in selected bouquet....\n +extra.menu_left_exit "Left" = menu back +extra.zapit_write_names Write channel names +extra.update_dir Directory for updates +extra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Abortion of channel scan +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/portugues.locale b/data/locale/portugues.locale new file mode 100644 index 000000000..84b177252 --- /dev/null +++ b/data/locale/portugues.locale @@ -0,0 +1,1237 @@ +EPGMenu.epgplus Ver EPG global +EPGMenu.eventinfo EPG actual +EPGMenu.eventlist EPG do canal actual +EPGMenu.head EPG - Guia de Programação +EPGMenu.streaminfo Informação +EPGPlus.actions Acções +EPGPlus.bybouquet_mode por bouquet +EPGPlus.bypage_mode tudo (ordem numérica) +EPGPlus.change_font_size tamanho da letra +EPGPlus.change_font_style estilo da letra +EPGPlus.change_size tamanho +EPGPlus.channelentry_font letera do canal +EPGPlus.channelentry_separationlineheight linha de separação em cima +EPGPlus.channelentry_width largura +EPGPlus.channelevententry_font letra do evento +EPGPlus.edit_fonts editar letra +EPGPlus.edit_sizes editar os tamanhos +EPGPlus.font_style_bold tipo bold +EPGPlus.font_style_italic tipo itálico +EPGPlus.font_style_regular tipo regular +EPGPlus.footer_fontbouquetchannelname bouquet de fundo +EPGPlus.footer_fontbuttons butões de fundo +EPGPlus.footer_fonteventdescription eventos de fundo +EPGPlus.footer_fonteventshortdescription pequena descrição de eventos de fundo +EPGPlus.head EPG Plus +EPGPlus.header_font letra do cabeçalho +EPGPlus.horgap1_height altura1 +EPGPlus.horgap2_height altura2 +EPGPlus.next_bouquet próximo bouquet +EPGPlus.options Opções +EPGPlus.page_down Para baixo +EPGPlus.page_up Para cima +EPGPlus.prev_bouquet bouquet anterior +EPGPlus.record Gravar +EPGPlus.refresh_epg Actualizar +EPGPlus.remind Programar gravação +EPGPlus.reset_settings repor as definições de origem +EPGPlus.save_settings guardar as alterações +EPGPlus.scroll_mode Modo rolante +EPGPlus.select_font_name nome da letra +EPGPlus.settings configurações +EPGPlus.slider_width largura de slider de EPG +EPGPlus.stretch_mode Modo expandido +EPGPlus.swap_mode mostra +EPGPlus.timeline_fontdate letra da data +EPGPlus.timeline_fonttime letra do tempo +EPGPlus.vergap1_width largura1 +EPGPlus.vergap2_width largura2 +EPGPlus.view_mode modo ver +GENRE.ARTS.0 arte / cultura +GENRE.ARTS.1 performing arts +GENRE.ARTS.10 arte/revistas de cultura +GENRE.ARTS.11 moda +GENRE.ARTS.2 fine arts +GENRE.ARTS.3 religião +GENRE.ARTS.4 cultura popular/artes tradicionais +GENRE.ARTS.5 literatura +GENRE.ARTS.6 cinema +GENRE.ARTS.7 experimental filmes/vídeo +GENRE.ARTS.8 notícias/imprensa +GENRE.ARTS.9 novos media +GENRE.CHILDRENs_PROGRAMMES.0 crianças / programas juvenis +GENRE.CHILDRENs_PROGRAMMES.1 programas pré-escolares +GENRE.CHILDRENs_PROGRAMMES.2 programas de entretenimento para os 6 aos 14 +GENRE.CHILDRENs_PROGRAMMES.3 programas de entretenimento para os 10 aos 16 +GENRE.CHILDRENs_PROGRAMMES.4 informação/educação/programas escolares +GENRE.CHILDRENs_PROGRAMMES.5 desenhos animados +GENRE.DOCUS_MAGAZINES.0 documentários +GENRE.DOCUS_MAGAZINES.1 natureza/animais/meio ambiente +GENRE.DOCUS_MAGAZINES.2 tecnologia/ciências naturaias +GENRE.DOCUS_MAGAZINES.3 medicina/psicologia +GENRE.DOCUS_MAGAZINES.4 países estrangeiros/expedições +GENRE.DOCUS_MAGAZINES.5 sociais/ciências espirituais +GENRE.DOCUS_MAGAZINES.6 educação +GENRE.DOCUS_MAGAZINES.7 linguagens +GENRE.MOVIE.0 filmes/drama +GENRE.MOVIE.1 detectives/thriller +GENRE.MOVIE.2 aventura/western/guerra +GENRE.MOVIE.3 ficção cientifica/fantasia/terror +GENRE.MOVIE.4 comedia +GENRE.MOVIE.5 soap/melodrama/folkloric +GENRE.MOVIE.6 romantico +GENRE.MOVIE.7 séries/clássico/religioso/filmes históricos/drama +GENRE.MOVIE.8 filmes xx's/drama +GENRE.MUSIC_DANCE.0 musica / ballet / dança +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 serious music/musica clássica +GENRE.MUSIC_DANCE.3 folclore/musica tradicional +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musicais/opera +GENRE.MUSIC_DANCE.6 ballet +GENRE.NEWS.0 notícias +GENRE.NEWS.1 notícias/notícias do tempo +GENRE.NEWS.2 telejornal +GENRE.NEWS.3 documentário +GENRE.NEWS.4 debate/entrevista/debate +GENRE.SHOW.0 espectáculos / Jogos de TV +GENRE.SHOW.1 jogos tv/questionários/respostas +GENRE.SHOW.2 espectáculos variados +GENRE.SHOW.3 talk show +GENRE.SOCIAL_POLITICAL.0 eventos políticos e sociais / negócios +GENRE.SOCIAL_POLITICAL.1 revista/reportagens/documentários +GENRE.SOCIAL_POLITICAL.2 economia/conselhos sociais +GENRE.SOCIAL_POLITICAL.3 pessoas de nome +GENRE.SPORTS.0 desporto +GENRE.SPORTS.1 desportos especiais (Jogos Olímpicos, Supertaça Cândido Oliveira, etc...) +GENRE.SPORTS.10 equestre +GENRE.SPORTS.11 desportos marciais +GENRE.SPORTS.2 revistas desportivas +GENRE.SPORTS.3 futebol +GENRE.SPORTS.4 ténis/squash +GENRE.SPORTS.5 desportos colectivos (excluindo o futebol) +GENRE.SPORTS.6 atletismo +GENRE.SPORTS.7 desportos motorizados +GENRE.SPORTS.8 desportos aquáticos +GENRE.SPORTS.9 desportos de Inverno +GENRE.TRAVEL_HOBBIES.0 viagens e recreio +GENRE.TRAVEL_HOBBIES.1 turismo/viagens +GENRE.TRAVEL_HOBBIES.2 handicraft +GENRE.TRAVEL_HOBBIES.3 motores +GENRE.TRAVEL_HOBBIES.4 desporto e saúde +GENRE.TRAVEL_HOBBIES.5 cozinha +GENRE.TRAVEL_HOBBIES.6 shopping +GENRE.TRAVEL_HOBBIES.7 jardinagem +GENRE.UNKNOWN desconhecido +apids.hint_1 Adicionar APIDs para serem "streamed" +apids.hint_2 em notação haxadecimal separada por ' ' +apidselector.head Seleccionar língua +audiomenu.PCMOffset Descer o volume usando PCM +audiomenu.analogout Saída Analógica +audiomenu.avs avs +audiomenu.avs_control Volume Controlo avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Audio Configurações +audiomenu.lirc lirc +audiomenu.monoleft mono esquerdo +audiomenu.monoright mono direito +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add Adicionar +audioplayer.artist_title Artista, titulo +audioplayer.building_search_index building search index +audioplayer.button_select_title_by_id procurar por ID +audioplayer.button_select_title_by_name procurar por nome +audioplayer.defdir directório de início +audioplayer.delete Apagar +audioplayer.deleteall Apagar todos +audioplayer.display_order ordem de exibição +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward avanço rápido +audioplayer.follow auto seleccionar a actual +audioplayer.head Lista de Ãudio +audioplayer.highprio alta prioridade na descodificação +audioplayer.id3scan Procurando etiquetas ID3 +audioplayer.jump_backwards Salto para trás +audioplayer.jump_dialog_hint1 Insira destino do salto +audioplayer.jump_dialog_hint2 Em Segundos +audioplayer.jump_dialog_title Destino do salto +audioplayer.jump_forwards Salto para frente +audioplayer.keylevel key level +audioplayer.name Reprodutor de Ãudio +audioplayer.pause pausa +audioplayer.play Reproduzir +audioplayer.playing Faixa em execução +audioplayer.playlist_fileerror_msg Ficheiro não pode ser criado: +audioplayer.playlist_fileerror_title Erro +audioplayer.playlist_fileoverwrite_msg Deseja sobrepor este ficheiro? +audioplayer.playlist_fileoverwrite_title Sobrepor? +audioplayer.playlist_name nome da lista +audioplayer.playlist_name_hint1 Por favor introduzir o nome da lista +audioplayer.playlist_name_hint2 A extensão .m3u será automaticamente adicionada +audioplayer.reading_files reading files +audioplayer.repeat_on activar modo repetição +audioplayer.rewind Modo continuo +audioplayer.save_playlist guardar lista em execução +audioplayer.screensaver_timeout definir tempo para a protecção de ecrã (min, 0=off) +audioplayer.select_title_by_name procurar titúlo por nome (SMS) +audioplayer.show_playlist mostar lista +audioplayer.shuffle Modo aleatório +audioplayer.stop Parar +audioplayer.title_artist Titulo, Artista +audioplayerpicsettings.general Reprodutor de áudio / Visualizar fotografias +bookmarkmanager.delete apagar +bookmarkmanager.name nome favoritos +bookmarkmanager.rename renomear +bookmarkmanager.select seleccionar +bouqueteditor.add Adicionar +bouqueteditor.bouquetname Nome do bouquet +bouqueteditor.delete Apagar +bouqueteditor.discardingchanges A ignorar alterações. Por favor aguardar… +bouqueteditor.hide Esconder +bouqueteditor.lock Bloquear +bouqueteditor.move Mover +bouqueteditor.name Editor de Bouquets +bouqueteditor.newbouquetname Novo nome do bouquet +bouqueteditor.rename Renomear +bouqueteditor.return pronto +bouqueteditor.savechanges? deseja guardar as alterações? +bouqueteditor.savingchanges A guardar alterações. Por favor aguardar... +bouqueteditor.switch adicionar/remover +bouqueteditor.switchmode TV/Rádio +bouquetlist.head Bouquets +cablesetup.provider operador de cabo +channellist.head Todos os serviços +channellist.nonefound Não foram encontrados canais!\nPor favor realize uma busca\n(tecla-DBOX -> serviços) +channellist.since desde %02d:%02d +colorchooser.alpha alpha +colorchooser.blue azul +colorchooser.green verde +colorchooser.red vermelho +colormenu.background Fundo +colormenu.fade Menus escondidos +colormenu.font Tamanho da letra +colormenu.gtx_alpha Transparência (GTX) +colormenu.head Configurar cor +colormenu.menucolors Menu cor +colormenu.statusbar Barra de Informação +colormenu.textcolor Cor do Texto +colormenu.themeselect Seleccionar tema +colormenu.timing Tempo de OSD +colormenusetup.head Menu Cores +colormenusetup.menucontent Janela actual +colormenusetup.menucontent_inactive Janela inactiva +colormenusetup.menucontent_selected Janela seleccionada +colormenusetup.menuhead Título do Menu +colorstatusbar.head Barra de Informação +colorstatusbar.text Barra de Informação +colorthememenu.classic_theme Tema Classico +colorthememenu.dblue_theme Tema AzulEscuro +colorthememenu.dvb2k_theme Tema DVB2000 +colorthememenu.head Selecionar Tema +colorthememenu.neutrino_theme Tema Neutrino +date.Apr Abr +date.Aug Ago +date.Dec Dez +date.Feb Feb +date.Fri Sex +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mar +date.May Mai +date.Mon Seg +date.Nov Nov +date.Oct Out +date.Sat Sab +date.Sep Sep +date.Sun Dom +date.Thu Qui +date.Tue Ter +date.Wed Qua +epglist.head Lista de eventos - %s +epglist.noevents EPG não disponível... +epgviewer.More_Screenings Repetições neste canal +epgviewer.nodetailed Informação detalhada não disponível +epgviewer.notfound epg não encontrada +eventlistbar.channelswitch Programar gravação +eventlistbar.eventsort dispor +eventlistbar.recordevent gravar +favorites.addchannel O actual canal será adicionado \npara o bouquet "Os meus favoritos". \n Irá demorar alguns segundos... +favorites.bouquetname Meus favoritos +favorites.bqcreated Bouquet "Meus favoritos" criado com sucesso...\n +favorites.chadded O actual canal foi adicionado aos seus favoritos...\n +favorites.chalreadyinbq O actual canal já pertencia aos seus favoritos...\n +favorites.finalhint \nUse o bouqueteditor para modificar os seus favoritos.\n +favorites.menueadd Adicionar canal aos favoritos +favorites.nobouquets Favorites are available with activated Bouquets only. +filebrowser.delete Apagar +filebrowser.denydirectoryleave Usar apenas directório seleccionado +filebrowser.dodelete1 Apagar +filebrowser.dodelete2 ? +filebrowser.filter.active Activar filtro +filebrowser.filter.inactive Mostrar todos +filebrowser.head Modo Ficheiro +filebrowser.mark Marca +filebrowser.nextpage Página seguinte +filebrowser.prevpage Página anterior +filebrowser.scan Procurar ficheiro +filebrowser.select Seleccionar +filebrowser.showrights Mostrar atributos do ficheiro +filebrowser.sort.date (data) +filebrowser.sort.name (nome) +filebrowser.sort.namedirsfirst (nome2) +filebrowser.sort.size (tamanho) +filebrowser.sort.type (tipo) +flashupdate.actionreadflash a ler +flashupdate.cantopenfile não é possível abrir o ficheiro +flashupdate.cantopenmtd não é possível abrir a mtd-device +flashupdate.checkupdate procura de uma nova versão +flashupdate.currentreleasecycle Release cycle +flashupdate.currentversion_sep Versão actual +flashupdate.currentversiondate Data +flashupdate.currentversionsnapshot Tipo de imagem +flashupdate.currentversiontime Hora +flashupdate.erasefailed a apagar a flash falhou +flashupdate.erasing a apagar a flash +flashupdate.experimentalimage A imagem que seleccionou é uma versão não testada, isso significa que\no seu receptor pode não funcionar correctamente depois de a instalar.\n\ndeseja realmente actualizar para esta versão? +flashupdate.expertfunctions Menu esperto +flashupdate.fileis0bytes o tamanho do ficheiro é de 0 Bytes +flashupdate.fileselector File-Selector +flashupdate.flashreadyreboot A imagem foi escrita com sucesso.\nO receptor reiniciará de imediato. +flashupdate.getinfofile recolhendo informação sobre a versão +flashupdate.getinfofileerror não é possível verificar informação sobre a versão +flashupdate.getupdatefile recebendo a actualização +flashupdate.getupdatefileerror não é possível realizar a actualização +flashupdate.globalprogress Progresso: +flashupdate.head Actualização de Software +flashupdate.md5check verificando a integridade da imagem +flashupdate.md5sumerror imagem contem erros! +flashupdate.msgbox Encontrada a seguinte nova file:\nData: %s, %s\nBaseada na imagem: %s\nTipo: %s\n\nDeseja realizar download e instalar esta versão agora? +flashupdate.msgbox_manual Encontrada a seguinte nova imagem:\nData: %s, %s\nImagem de base: %s\nTipo de imagem: %s\n\nDeseja instalar esta versão agora? +flashupdate.mtdselector Partition-Selector +flashupdate.programmingflash a programar a flash +flashupdate.proxypassword Password +flashupdate.proxypassword_hint1 enter the proxyserver password +flashupdate.proxypassword_hint2 uma entrada vazia significa não haver proxy-auth +flashupdate.proxyserver Hostname +flashupdate.proxyserver_hint1 enter proxyserver name or ip, use host:port +flashupdate.proxyserver_hint2 uma entrada vazia significa não haver proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Username +flashupdate.proxyusername_hint1 introduza o proxyserver username +flashupdate.proxyusername_hint2 uma entrada vazia significa não haver proxy-auth +flashupdate.readflash Ler a imagem toda +flashupdate.readflashmtd Ler uma partição +flashupdate.ready pronto +flashupdate.reallyflashmtd Tem a certeza que deseja fazer gravar a imagem?\n\nSe ocorre algum erro ou a imagem não é\nválida, o receptor pode não reiniciar após o processo.\n\nNome da Imagem: %s\nLocalização: %s +flashupdate.savesuccess Imagem guardada com sucesso \nsobre %s. +flashupdate.selectimage Imagens/Files Disponíveis +flashupdate.squashfs.noversion A versão de SquashFS presente é actualmente apenas suportada quando actualizada via web.\nTem a certeza que deseja instalar esta imagem? +flashupdate.titlereadflash A ler Flash +flashupdate.titlewriteflash A escrever a Flash +flashupdate.updatemode Modo Update +flashupdate.updatemode_internet internet +flashupdate.updatemode_manual manual (ftp) +flashupdate.url_file localização da imagem +flashupdate.versioncheck verificar versão +flashupdate.writeflash Escrever a imagem toda +flashupdate.writeflashmtd Escrever uma partição +flashupdate.wrongbase flashupdate.wrongbase Your Release cycle differs.\nTo continue? +fontmenu.channellist Lista de canais +fontmenu.epg EPG +fontmenu.eventlist Lista de Eventos +fontmenu.gamelist Lista de Jogos +fontmenu.head Configurações do Tamanho da fonte +fontmenu.infobar Barra de informação +fontsize.channel_num_zap selecção directa +fontsize.channellist Lista de canais +fontsize.channellist_descr Descrição +fontsize.channellist_number Número +fontsize.epg_date EPG Data +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titulo +fontsize.eventlist_datetime data / tempo +fontsize.eventlist_itemlarge largo +fontsize.eventlist_itemsmall pequeno +fontsize.eventlist_title Titúlo +fontsize.filebrowser_item Item de modo ficheiro +fontsize.gamelist_itemlarge larga +fontsize.gamelist_itemsmall pequena +fontsize.hint A Iniciar letra,\npor favor aguarde... +fontsize.infobar_channame Nome do canal +fontsize.infobar_info info +fontsize.infobar_number Numero +fontsize.infobar_small pequeno +fontsize.menu Menu texto +fontsize.menu_info Menu Informações +fontsize.menu_title Menu Titulo +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG não carregada.... +infoviewer.epgwait À espera do EPG... +infoviewer.eventlist EPG actual +infoviewer.languages Audio +infoviewer.motor_moving Movendo a antena +infoviewer.nocurrent Sem informação disponível para o seguinte programa +infoviewer.noepg EPG não disponível +infoviewer.notavailable Canal não disponível +infoviewer.selecttime Timeselection +infoviewer.streaminfo Características +infoviewer.subservice Sub serviços +infoviewer.waittime Esperando pela hora... +ipsetup.hint_1 Use 0..9, ou use Cima/Baixo, +ipsetup.hint_2 OK guardar, HOME cancelar +keybindingmenu.RC Intervalo de repetição para a mesma tecla +keybindingmenu.addrecord adicionar programação de gravação +keybindingmenu.addremind adicionar programação de evento +keybindingmenu.allchannels_on_ok todos os serviços +keybindingmenu.bouquetchannels_on_ok bouquet actual +keybindingmenu.bouquetdown bouquet anterior +keybindingmenu.bouquethandling Bouquet-control +keybindingmenu.bouquetlist_on_ok lista de bouquets +keybindingmenu.bouquetup bouquet seguinte +keybindingmenu.cancel fechar lista de canais +keybindingmenu.channeldown canal anterior +keybindingmenu.channellist Lista de canais +keybindingmenu.channelup canal seguinte +keybindingmenu.head Configurar teclas +keybindingmenu.lastchannel Zapping Rápido +keybindingmenu.modechange Modo edição +keybindingmenu.pagedown página anterior +keybindingmenu.pageup página seguinte +keybindingmenu.quickzap Zapping rápido +keybindingmenu.repeatblock Intervalo da repetição de tecla +keybindingmenu.repeatblockgeneric Intervalo entre teclas +keybindingmenu.sort alterar ordem de exposição +keybindingmenu.subchanneldown canal anterior +keybindingmenu.subchannelup canal seguinte +keybindingmenu.tvradiomode Modo TV/Rádio +keybindingmenu.zaphistory Zapping Bouquet - Histórico +keychooser.head Configurar nova tecla +keychooser.text1 por favor premir a nova tecla +keychooser.text2 esperar alguns segundos para abortar +keychoosermenu.currentkey tecla actual +keychoosermenu.setnew Configurar nova tecla +keychoosermenu.setnone sem tecla definida +languagesetup.head Configurar língua +languagesetup.select Língua +lcdcontroler.brightness Brilho normal +lcdcontroler.brightnessstandby Brilho em Standby +lcdcontroler.contrast Contraste +lcdcontroler.head LCD Configurações +lcdmenu.autodimm Auto dimm +lcdmenu.head LCD Configurações +lcdmenu.inverse Inverter +lcdmenu.lcdcontroler Contraste / Brilho +lcdmenu.power Lidado +lcdmenu.statusline Linha de estado +lcdmenu.statusline.both volume / tempo decorrido +lcdmenu.statusline.playtime tempo decorrido +lcdmenu.statusline.volume volume +mainmenu.audioplayer Reprodutor de Aúdio +mainmenu.games Jogos +mainmenu.head Menu +mainmenu.movieplayer Reprodutor de Vídeo +mainmenu.pausesectionsd Ler EPG +mainmenu.pictureviewer Ver Fotos +mainmenu.radiomode Modo Rádio +mainmenu.recording Gravação +mainmenu.recording_start Iniciar +mainmenu.recording_stop Parar +mainmenu.scartmode Modo Scart +mainmenu.service Serviços +mainmenu.settings Configurações +mainmenu.shutdown Desligar +mainmenu.sleeptimer Desligar por temporizador +mainmenu.tvmode Modo TV +mainsettings.audio Aúdio +mainsettings.colors Cores / temas / letra +mainsettings.head Configurações +mainsettings.keybinding Configurar teclas +mainsettings.language Língua +mainsettings.lcd LC-Display +mainsettings.misc Outras configurações +mainsettings.network Rede +mainsettings.recording Gravação +mainsettings.savesettingsnow Guardar alterações agora +mainsettings.savesettingsnow_hint A guardar alterações,\npor favor aguarde... +mainsettings.streaming Reprodutor de Vídeo +mainsettings.video Video +menu.back Voltar +messagebox.back Atrás +messagebox.cancel Cancelar +messagebox.discard Ignorar alterações? +messagebox.error Erro +messagebox.info Informação +messagebox.no Não +messagebox.yes Sim +miscsettings.bootinfo Mostrar informação ao arrancar +miscsettings.bootmenu Mostrar boot +miscsettings.driver_boot Driver e boot - opções +miscsettings.fb_destination Expertos! Boot-Console +miscsettings.general Geral +miscsettings.head Outras configurações +miscsettings.hwsections Secções de hardware usado +miscsettings.infobar_sat_display Mostrar satélite na barra de informações +miscsettings.pmtupdate activar actualização do pmt +miscsettings.shutdown_real Activar standby +miscsettings.shutdown_real_rcdelay Desligar com telecomando +miscsettings.sptsmode use spts mode +miscsettings.startbhdriver carregar BH-Mode drivers +motorcontrol.head Definições do Motor +movieplayer.bookmark Favoritos +movieplayer.bookmarkname Nome da lista +movieplayer.bookmarkname_hint1 Introduzir um nome para a nova lista +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffering... +movieplayer.defdir directório de início +movieplayer.dvdplayback DVD +movieplayer.fileplayback Ficheiro via VLC +movieplayer.goto Saltar para ... +movieplayer.goto.h1 = -> salto absoluto +movieplayer.goto.h2 +,- -> salto relativo +movieplayer.head Movieplayer +movieplayer.nostreamingserver The streaming server não pode ser encontrado. +movieplayer.pesplayback Play PES (Experimental) +movieplayer.pleasewait Por favor aguarde.\nA ligar ao streaming server... +movieplayer.toomanybookmarks Tem demasiadas lista de favoritos.\nNecessita primeiro apagar alguma(s) dela(s). +movieplayer.tsplayback Play TS +movieplayer.vcdplayback (S)VCD +movieplayer.wrongvlcversion esta característica não é suportada pela corrente versão de VLC +networkmenu.broadcast Transmissor +networkmenu.dhcp DHCP +networkmenu.gateway Gateway +networkmenu.head Definições de Rede +networkmenu.ipaddress Endereço IP +networkmenu.mount NFS/CIFS +networkmenu.nameserver Name server +networkmenu.netmask Netmask +networkmenu.setupnow configurar a rede +networkmenu.setuponstartup ligar a rede no arranque +networkmenu.show mostrar as definições das ligações activas +networkmenu.test testar ligação agora +nfs.alreadymounted directório já montado +nfs.automount montar no arranque +nfs.dir directório/partilha +nfs.ip NFS/CIFS Server IP +nfs.localdir directório local +nfs.mount Montar unidade de NFS/CIFS +nfs.mount_options opções de montagem +nfs.mounterror erro ao montar +nfs.mounterror_notsup sitema de ficheiros não suportado +nfs.mountnow montar agora +nfs.mounttimeout erro ao montar: timeout +nfs.password CIFS password +nfs.remount Remontar directórios +nfs.type tipo +nfs.type_cifs CIFS +nfs.type_nfs NFS +nfs.umount Desmontar unidade NFS/CIFS +nfs.umounterror erro ao "desmontar" unidade +nfs.username CIFS username +nfsmenu.head NFS/CIFS configurações +nvod.percentage (%d%% para) +nvod.starting (a começar em %d min) +nvodselector.directormode Modo directo +nvodselector.head Seleccionar hora de início +nvodselector.subservice Seleccionar Sub serviço +options.default Restaurar configurações +options.fb framebuffer +options.null nulo +options.off off +options.on on +options.serial serie +parentallock.changepin Alterar código PIN +parentallock.changepin_hint1 Introduza aqui o seu novo código pin! +parentallock.changetolocked nos bouquets bloqueados +parentallock.head Enter Código PIN de protecção +parentallock.lockage Bloquear programa +parentallock.lockage12 de 12 anos para cima +parentallock.lockage16 de 16 anos para cima +parentallock.lockage18 de 18 anos para cima +parentallock.lockedchannel Bloquear emissor... +parentallock.lockedprogram Bloquear programa (de %d anos para cima) +parentallock.never nunca +parentallock.onsignal bloquear ao entrar no canal +parentallock.parentallock Acesso condicional +parentallock.prompt Janela para introduzir PIN +pictureviewer.defdir directório de início +pictureviewer.head Visualizar fotos +pictureviewer.resize.color_average avançado +pictureviewer.resize.none nada +pictureviewer.resize.simple simples +pictureviewer.scaling dimensionamento +pictureviewer.show mostar +pictureviewer.slide_time intervalo entre fotos +pictureviewer.slideshow Slideshow +pictureviewer.sortorder alterar ordem de disposição +pictureviewer.sortorder.date (data) +pictureviewer.sortorder.filename (Nome do ficheiro) +ping.ok localizavél (ping) +ping.protocol não localizável! (host ou protocol error) +ping.socket não localizável! (socket error) +ping.unreachable não localizável! +pinprotection.head Introduzir código PIN +pinprotection.wrongcode Código PIN errado! Tende de novo. +rclock.lockmsg O comando da sua box será bloqueado.\n Para desbloquear a sua box, prima \n e no seu telecomando. +rclock.menueadd Bloquear RC +rclock.title Bloquear Telecomando +rclock.unlockmsg Comando da box reactivado. +recordingmenu.defdir Directório a Gravar +recordingmenu.file directo (ficheiro) +recordingmenu.head Configurações de gravação +recordingmenu.help Modo de Gravação:\nserver: usando software de transmissão de dados num PC\nVcr: usando vcr \ndirect (ficheiro): directamente num directório de NFS montado \nou num Dísco Rígido Interno\n\nMax. Tamanho do Ficheiro:\nNFS V2: 2 GB (2048 MB)\nNFS V3: Não limitado (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart Não alternar para scart mode +recordingmenu.off off +recordingmenu.recording_type recording device +recordingmenu.server server +recordingmenu.server_ip recording server IP +recordingmenu.server_mac MAC address +recordingmenu.server_port recording server port +recordingmenu.server_wakeup recording server WOL +recordingmenu.setupnow Activar alterações +recordingmenu.splitsize Max. Tamanho do ficheiro (MB) +recordingmenu.stopplayback parar playback +recordingmenu.stopsectionsd parar sectionsd +recordingmenu.stream_vtxt_pid Gravar teletextp +recordingmenu.use_o_sync Escrever synchronous (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce Recording starts in a few minutes +repeatblocker.hint_1 O mais curto espaço de tempo (in ms) para reconhecer 2 "keystrokes" +repeatblocker.hint_2 Introduzir 0 para desligar o blocker (vermelho é espaço) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-repetições +satsetup.extended DiSEqC-Configurações +satsetup.extended_motor Motor-Configurações +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Configuração Manual Motor +satsetup.nodiseqc sem DiSEqC +satsetup.satellite Satelite +satsetup.savesettingsnow Guardar agora configurações +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Cabo: +scants.actsatellite Satelite: +scants.bouquet Bouquet +scants.bouquet_create criar novo +scants.bouquet_erase apagar tudo +scants.bouquet_leave deixar os bouquets actuais +scants.bouquet_satellite Satelite-Bouquet +scants.bouquet_update actualização +scants.channel Channel: +scants.failed Busca de Transpondedor Falhou! +scants.finished Busca de Transpondedor concluiu com sucesso! +scants.freqdata Frequencia: +scants.head Busca transpondedor +scants.numberofdataservices Data +scants.numberofradioservices Rádio +scants.numberoftotalservices Total +scants.numberoftvservices TV +scants.provider Operador: +scants.startnow Começar Busca +scants.transponders Transpondedor: +screensetup.lowerright verde = configurar baixo direita +screensetup.upperleft vermelho = configurar cima esquerda +servicemenu.head Canais +servicemenu.reload Actualizar lista de canais +servicemenu.reload_hint A actualizar Lista de canais,\npor favor esperar... +servicemenu.scants Busca de Canais +servicemenu.ucodecheck Verificar ucodes +servicemenu.update Actualização de Software +settings.help Ajuda +settings.missingoptionsconffile As configurações de neutrino foram actualizadas.\nNovas opções estão agora seleccionadas. +settings.noconffile Não foram encontradas as configurações anteriores de neutrino.\nA usar as predefinidas. +shutdowntimer.announce A box irá desligar em 1 min.\nCancelar Desligar ? +sleeptimerbox.announce A desligar em 1 min +sleeptimerbox.hint1 Desligar em min. (000=off) +sleeptimerbox.hint2 O receptor vai desligar após este tempo. +sleeptimerbox.title Desligar por temporizador +streamfeatures.head Extra Menu +streaminfo.aratio Formato de ecrã +streaminfo.aratio_unknown Formato desconhecido +streaminfo.audiotype Formato Ãudio +streaminfo.audiotype_unknown Formato áudio desconhecido +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: desconhecido +streaminfo.head Stream-Information +streaminfo.not_available Não disponivél +streaminfo.resolution Resolutução +streaminfo.signal Sinal recebido +streaming.buffer_overflow A gravação abortou,\napós a informação não poder ser escrita de forma tão rápida. +streaming.busy Um processo de gravação está activo.\nSe visualizar esta mensagem e não tiver nenhuma gravação activa, por favor reiniciar a gravação. +streaming.dir_not_writable Não tem permissões para gravar nesse directório.\nA gravação não irá funcionar. +streaming.success Gravação concluída com sucesso. +streaming.write_error A gravação abortou,\napós ocorrer um erro no processo de escrita. +streaming.write_error_open A gravação abortou,\nporque o ficheiro de destino não pode ser aberto. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Configuração do Reprodutor de Vídeo +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Desligar +streamingmenu.on Ligar +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Porta +streamingmenu.streaming_audiorate Datarate Audio +streamingmenu.streaming_force_avi_rawaudio Forçar AC3 para filmes AVI +streamingmenu.streaming_force_transcode_video Transcode MPG/VCD vídeo +streamingmenu.streaming_resolution Resolução +streamingmenu.streaming_server_cddrive DVD Drive +streamingmenu.streaming_server_startdir Directório (VLC) +streamingmenu.streaming_transcode_audio Transcode áudio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG vídeo codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datarate Video +streamingserver.noconnect Não há ligação ao streamingserver.\nGravação Parada. +stringinput.caps maiúsculas / minúsculas +stringinput.clear limpar tudo +timer.eventrecord.msg ... Realizar ou não??? +timer.eventrecord.title Lista de Gravações +timer.eventtimed.msg O evento está programado.\nA box será ligada e \npermanecerá no canal durante a gravação. +timer.eventtimed.title Eventos programados +timerbar.channelswitch Programar gravação +timerbar.recordevent Gravar +timerlist.alarmtime Alarme +timerlist.apids Audio PIDs +timerlist.bouquetselect seleccionar bouquet +timerlist.channel Canal +timerlist.channelselect seleccionar canal +timerlist.delete Apagar +timerlist.menumodify Alterar Temporizador +timerlist.menunew Nova Programação +timerlist.message Mensagem +timerlist.moderadio Canais de Rádio +timerlist.modeselect Modo selecção +timerlist.modetv Canais de TV +timerlist.modify Modificar +timerlist.name Temporizador - Programar acontecimentos +timerlist.new Nova programação +timerlist.program.unknown Programa Desconhecido +timerlist.reload Recarregar +timerlist.repeat Repetir +timerlist.repeat.biweekly Bissemanal +timerlist.repeat.byeventdescription Ver descrição da gravação +timerlist.repeat.daily Diário +timerlist.repeat.fourweekly 4 em 4 semanas +timerlist.repeat.friday Sex +timerlist.repeat.monday Seg +timerlist.repeat.monthly Mensal +timerlist.repeat.once Apenas 1 vez +timerlist.repeat.saturday Sab +timerlist.repeat.sunday Dom +timerlist.repeat.thursday Qui +timerlist.repeat.tuesday Ter +timerlist.repeat.unknown desconhecido +timerlist.repeat.wednesday Qua +timerlist.repeat.weekdays nos dias de semana +timerlist.repeat.weekly Semanal +timerlist.save Guardar programação +timerlist.standby Modo a deixar o receptor +timerlist.standby.off Deixar standby +timerlist.standby.on Activar standby +timerlist.stoptime Parar Tempo +timerlist.type Tipo de função +timerlist.type.nextprogram Próximo programa +timerlist.type.record Gravar +timerlist.type.remind Lembrar +timerlist.type.shutdown Desligar +timerlist.type.sleeptimer desligar +timerlist.type.standby Ir para Standby +timerlist.type.unknown Desconhecido +timerlist.type.zapto Ir para +timerlist.weekdays Dias da Semana +timerlist.weekdays.hint_1 Seg Ter Qua Qui Sex Sáb Dom +timerlist.weekdays.hint_2 'X'=tempo '-' no timer +timersettings.record_safety_time_after Correcção da hora para final de gravação +timersettings.record_safety_time_after.hint_1 Correcção da duração em min. (00=off). Este tempo +timersettings.record_safety_time_after.hint_2 será adicionado ao tempo de parar gravação para todas as programações. +timersettings.record_safety_time_before Correcção da hora de início de gravação +timersettings.record_safety_time_before.hint_1 Correcção da hora de início de gravação em min. (00=off). Este tempo +timersettings.record_safety_time_before.hint_2 será incrementado ao início de todas as gravações programadas. +timersettings.separator Ajustamentos da programação de gravações +timing.chanlist Lista de Canais +timing.epg Epg +timing.filebrowser Modo Ficheiros +timing.head Duração de OSD +timing.hint_1 Time in sec. Após +timing.hint_2 a barra de informação irá desaparecer +timing.infobar Barra de informação +timing.menu Menu +timing.numericzap Numeric Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head Procurar UCode +ucodecheck.ucode UCode +ucodes.failure ATENÇÃO, µCodes NÃO PODE SER ENCONTRADO!\n\nPor favor fazer upload via FTP,\ne depois reiniciar a vossa box! +videomenu.csync Correcção sincronismo +videomenu.head Configurações Vídeo +videomenu.rgb_centering Centrar RGB +videomenu.screensetup Configurar o ecrã +videomenu.vcrswitch Alternar automáticamente com saída Scart +videomenu.videoformat Formato +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect Auto-detectar +videomenu.videosignal Saída de Vídeo +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Ir para temporizador em um minuto +extra.auto Auto +extra.valid Validas +extra.all Todas +extra.new_keys Novas Chaves +extra.all_keys Todas as Chaves +extra.vartmp /var/tmp +extra.varkeys /var/keys +extra.algo Algo pids +extra.gbox_emu Emu +extra.gbox_softcam Softcam +extra.gbox_net Rede +extra.gbox_mix Misto +extra.gbox_info Gbox Info +extra.sep_extra Extras +extra.save_settings Guardar Configurações +extra.extra_menu EMU Menu +extra.ecm_info Ecm Info +extra.pid_info Pid Info +extra.emm_info Novas chaves +extra.emu_type Tipo de Emu +extra.camd Camd +extra.restart_camd Reiniciar Emulador +extra.mg_settings Mgcamd settings +extra.netmode Netmode +extra.autoupdate Autoupdate +extra.show_ecm Mostrar Ecm +extra.show_emm Mostrar Emm +extra.keyupdate Key update +extra.osd OSD +extra.keyfolder Key dir +extra.hash_pids Hash pids +extra.gbox_settings Gbox Configurações +extra.gbox_mode Modo +extra.newcamd_settings Newcamd Configurações +extra.show_cw Mostrar CW +extra.debug_ecm Debug Ecm +extra.disable_cam Não utilizar CAM +extra.show_cat Mostar CAT +extra.show_pmt Mostar PMT +extra.update_pmt Update PMT +extra.report_emm Report wrong EMM sig. +extra.debug_emm Debug Emm +extra.show_allca Mostrar todos ca sys. +extra.reload_keys Recordar keys no zapping +extra.reload_cfg Recordar configurações no zapping +extra.cw_delay Repor CW delay de origem +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Mostrar PAT +extra.dvbsnoop_cat Mostrar CAT +extra.dvbsnoop_tsdt Mostrar TSDT +extra.dvbsnoop_nit Mostrar NIT +extra.dvbsnoop_sdt Mostrar SDT +extra.dvbsnoop_eit Mostrar EIT +extra.dvbsnoop_rst Mostrar RST +extra.dvbsnoop_tdt Mostrar TDT +extra.dvbsnoop_sit Mostrar SIT +extra.dvbsnoop_pid Mostrar PID manual +extra.dvbsnoop_scan Scanear PIDs +extra.dvbsnoop_band Mostar PID de Largura de Banda +extra.logview Ver Log +extra.ru ruDREAM +extra.english Inglês +extra.dboxinfo Dbox Informações +extra.scan_mode Busca de Canais +extra.start_tostandby Arrancar para standby +extra.rotor_swap Mover motor este/oeste +extra.use_log Loggear para /tmp/log +extra.spts_mode Modo SPTS +extra.hw_sect Hardware Secções de Hardware +extra.logo Número do logo +extra.scan_full Exaustivo +extra.scan_fast Rápido +extra.hdd_slow Lento +extra.hdd_middle Intermédio +extra.hdd_fast Rápido +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Desligar HDD em +extra.hdd_noise Ruído +extra.hdd_activate Activar configurações +extra.hdd_fs Sistema de Ficheiros +extra.hdd_format Formatar HDD +extra.hdd_check Verificar Sistema de Ficheiros +extra.hdd_settings HDD Configurações +extra.clear_log Limpar Log +extra.zap_cycle Zapping na lista de canais +extra.sms_channel sms-mode channel +extra.manual_scan Manual scan +extra.tp_freq Frequencia +extra.tp_rate Symbol rate +extra.tp_pol Polarização +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Zona horária +extra.zapit_menu Zapit options +extra.zapit_make_bouquet Realizar lista com canais restantes +extra.zapit_save_last_chan Guardar o ultimo canal +extra.zapit_motor_speed Velocidade do movimento do motor (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet Adicionar ao Bouquet +extra.key_list_start início +extra.key_list_end final +extra.chadded O canal actual foi adicionado ao bouquet selecionado....\n +extra.chalreadyinbq O canal actual já estava no bouquet selecionado....\n +extra.menu_left_exit "Left" = menu anterior +extra.zapit_write_names Escrever nome do canal +extra.update_dir Directory for updates +extra.cache Cache +extra.debug Debug +extra.zapit_backup Backup channels to /tmp +extra.zapit_delete Delete channels +extra.zapit_fast_zap fast zap +extra.zapit_restore Restore channels from /tmp +extra.zapit_sort_names Sort ch. by name +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +infoviewer.subchan_disp_pos Subchannel display +mainmenu.scripts Scripts +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +movieplayer.defplugin Start-Plugin +movieplayer.tshelp1 Stop +movieplayer.tshelp10 approx. 10 minutes back +movieplayer.tshelp11 skip approx. 10 minutes +movieplayer.tshelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Select audio track +movieplayer.tshelp3 Pause/Continue +movieplayer.tshelp4 Create bookmark +movieplayer.tshelp5 Show progress +movieplayer.tshelp6 approx. 1 minute back +movieplayer.tshelp7 skip approx. 1 minute +movieplayer.tshelp8 approx. 5 minutes back +movieplayer.tshelp9 skip approx. 5 minutes +movieplayer.vlchelp1 Stop +movieplayer.vlchelp10 approx. 10 minutes back +movieplayer.vlchelp11 skip approx. 10 minutes +movieplayer.vlchelp12 Help: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Continue +movieplayer.vlchelp4 Create bookmark +movieplayer.vlchelp5 Show progress +movieplayer.vlchelp6 approx. 1 minute back +movieplayer.vlchelp7 skip approx. 1 minute +movieplayer.vlchelp8 approx. 5 minutes back +movieplayer.vlchelp9 skip approx. 5 minutes +nfs.mountok mount successful +nfs.type_lufs FTPFS +pictureviewer.help1 menu mode +pictureviewer.help10 show mode +pictureviewer.help11 reread image +pictureviewer.help12 previous image +pictureviewer.help13 next image +pictureviewer.help14 Zoom out +pictureviewer.help15 Zoom in +pictureviewer.help16 scroll up +pictureviewer.help17 scroll left +pictureviewer.help18 scroll right +pictureviewer.help19 scroll down +pictureviewer.help2 display image +pictureviewer.help20 change sort order +pictureviewer.help21 reread image (no scaling) +pictureviewer.help22 exit +pictureviewer.help3 change sort order +pictureviewer.help4 do not scale picture +pictureviewer.help5 diashow mode +pictureviewer.help6 previous image +pictureviewer.help7 next image +pictureviewer.help8 change sort order +pictureviewer.help9 exit +plugins.result plugin output +recordingmenu.choose_direct_rec_dir choose dir on imm. recording +recordingmenu.epg_for_filename long filenames (with EPG data) +recordingmenu.filesettings direct recording settings +recordingmenu.ringbuffers no. of ringbuffers +recordingmenu.use_fdatasync write synchronous (fdatasync) +settings.pos_bottom_left bottom left +settings.pos_bottom_right bottom right +settings.pos_top_left oben top left +settings.pos_top_right oben top right +timerlist.plugin Plugin +timerlist.recording_dir recording directory +timerlist.repeatcount repeats +timerlist.repeatcount.help1 amount of timer repeats +timerlist.repeatcount.help2 0 for unlimited repeats +timerlist.type.execplugin Execute plugin +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Cancelar Busca +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.cs Cardserver +extra.restart_cs Restart cardserver +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/russkij.locale b/data/locale/russkij.locale new file mode 100644 index 000000000..b8cffdc68 --- /dev/null +++ b/data/locale/russkij.locale @@ -0,0 +1,1186 @@ +EPGMenu.epgplus Телегид по вÑем каналам +EPGMenu.eventinfo Передачи канала подробно +EPGMenu.eventlist Телегид канала +EPGMenu.head Телегид-Информ +EPGMenu.streaminfo техничеÑÐºÐ°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÐºÐ°Ð½Ð°Ð»Ð° +EPGPlus.actions ДейÑÑ‚Ð²Ð¸Ñ +EPGPlus.bybouquet_mode по пакетам +EPGPlus.bypage_mode по Ñтраницам +EPGPlus.change_font_size Размер шрифта +EPGPlus.change_font_style Вид шрифта +EPGPlus.change_size Изменить размер +EPGPlus.channelentry_font Шрифт Ð´Ð»Ñ ÐšÐ°Ð½Ð°Ð»Ð° +EPGPlus.channelentry_separationlineheight Ð’Ñ‹Ñота Ñтрок +EPGPlus.channelentry_width Ширина Ñчейки канала +EPGPlus.channelevententry_font Шрифт Ð´Ð»Ñ ÐŸÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ +EPGPlus.edit_fonts УÑтановка шрифтов +EPGPlus.edit_sizes УÑтановка размеров +EPGPlus.font_style_bold Жирный +EPGPlus.font_style_italic КурÑив +EPGPlus.font_style_regular Обычный +EPGPlus.footer_fontbouquetchannelname Шрифт инф.канала в ÑноÑке +EPGPlus.footer_fontbuttons Шрифт подпиÑей кнопок +EPGPlus.footer_fonteventdescription Шрифт Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ в ÑноÑке +EPGPlus.footer_fonteventshortdescription Шрифт инф.передачи в ÑноÑке +EPGPlus.head Телегид по вÑем каналам (проÑмотр/обзор) +EPGPlus.header_font Шрифт Заголовка +EPGPlus.horgap1_height Ð’Ñ‹Ñота верх.Ð¿Ð¾Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð° +EPGPlus.horgap2_height Ð’Ñ‹Ñота ниж.Ð¿Ð¾Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð° +EPGPlus.next_bouquet Пакет вперед +EPGPlus.options Опции +EPGPlus.page_down вниз Ñтраница +EPGPlus.page_up вверх Ñтраница +EPGPlus.prev_bouquet Пакет обратно +EPGPlus.record запиÑÑŒ +EPGPlus.refresh_epg обновить +EPGPlus.remind запомнить +EPGPlus.reset_settings ÐаÑтройки по умолчанию +EPGPlus.save_settings Сохранить наÑтройки +EPGPlus.scroll_mode Scroll Mode +EPGPlus.select_font_name Выбор шрифта +EPGPlus.settings ÐаÑтройки +EPGPlus.slider_width Ширина Ð¿Ð¾Ð»Ñ Ð²Ñех программ +EPGPlus.stretch_mode Stretch Mode +EPGPlus.swap_mode режим обмена +EPGPlus.timeline_fontdate Шрифт даты +EPGPlus.timeline_fonttime Шрифт времени +EPGPlus.vergap1_width Ширина прав.Ð¿Ð¾Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð° +EPGPlus.vergap2_width Ширина лев.Ð¿Ð¾Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð° +EPGPlus.view_mode режим проÑмотра +GENRE.ARTS.0 иÑкуÑÑтво / культура +GENRE.ARTS.1 иÑполнительное иÑкуÑÑтво (танец, пьеÑа Ñ‚.д.) +GENRE.ARTS.10 иÑкуÑÑтво/журнал культуры +GENRE.ARTS.11 мода +GENRE.ARTS.2 изобразительное иÑкуÑÑтво +GENRE.ARTS.3 Ñ€ÐµÐ»Ð¸Ð³Ð¸Ñ +GENRE.ARTS.4 популÑÑ€Ð½Ð°Ñ ÐºÑƒÐ»ÑŒÑ‚ÑƒÑ€Ð°/традиционные иÑкуÑÑтва +GENRE.ARTS.5 литература +GENRE.ARTS.6 кино/фильм +GENRE.ARTS.7 ÑкÑпериментальное кино/видео +GENRE.ARTS.8 транÑлÑции/преÑÑа +GENRE.ARTS.9 новые Ñ‚ÐµÑ‡ÐµÐ½Ð¸Ñ +GENRE.CHILDRENs_PROGRAMMES.0 дети/передачи Ð´Ð»Ñ Ð´ÐµÑ‚ÐµÐ¹ +GENRE.CHILDRENs_PROGRAMMES.1 программа Ð´Ð»Ñ Ð´Ð¾ÑˆÐºÐ¾Ð»ÑŒÐ½Ð¸ÐºÐ¾Ð² +GENRE.CHILDRENs_PROGRAMMES.2 Ñ€Ð°Ð·Ð²Ð»ÐµÐºÐ°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° от 6 до 14 +GENRE.CHILDRENs_PROGRAMMES.3 Ñ€Ð°Ð·Ð²Ð»ÐµÐºÐ°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° от 10 до 16 +GENRE.CHILDRENs_PROGRAMMES.4 информационные/образовательные/школьные программы +GENRE.CHILDRENs_PROGRAMMES.5 мультфильмы/кукольные фильмы +GENRE.DOCUS_MAGAZINES.0 документальное кино / тележурналы +GENRE.DOCUS_MAGAZINES.1 природа/животные/Ð¾ÐºÑ€ÑƒÐ¶Ð°ÑŽÑ‰Ð°Ñ Ñреда +GENRE.DOCUS_MAGAZINES.2 технологии/еÑтеÑтвенные науки +GENRE.DOCUS_MAGAZINES.3 медицина/пÑихологиÑ/Ñ„Ð¸Ð·Ð¸Ð¾Ð»Ð¾Ð³Ð¸Ñ +GENRE.DOCUS_MAGAZINES.4 зарубежные Ñтраны/ÑкÑпедиции +GENRE.DOCUS_MAGAZINES.5 общеÑтво/духовные науки +GENRE.DOCUS_MAGAZINES.6 дополнительное образование +GENRE.DOCUS_MAGAZINES.7 Ñзыки +GENRE.MOVIE.0 кино/драма +GENRE.MOVIE.1 детектив/триллер +GENRE.MOVIE.2 приключениÑ/веÑтерн/война +GENRE.MOVIE.3 Ð½Ð°ÑƒÑ‡Ð½Ð°Ñ Ñ„Ð°Ð½Ñ‚Ð°Ñтика/Ñ„Ñнтези/ÑƒÐ¶Ð°Ñ +GENRE.MOVIE.4 комедии +GENRE.MOVIE.5 мыло/мелодрама/фольклор +GENRE.MOVIE.6 романтика +GENRE.MOVIE.7 Ñерьезный/клаÑÑичеÑкий/религиозный/иÑторичеÑкий фильм/драма +GENRE.MOVIE.8 кино Ð´Ð»Ñ Ð²Ð·Ñ€Ð¾Ñлых/драма +GENRE.MUSIC_DANCE.0 музыка / балет / танцы +GENRE.MUSIC_DANCE.1 рок/поп +GENRE.MUSIC_DANCE.2 ÑÐµÑ€ÑŒÐµÐ·Ð½Ð°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ° /клаÑÑичеÑÐºÐ°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ° +GENRE.MUSIC_DANCE.3 фолк/Ñ‚Ñ€Ð°Ð´Ð¸Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ° +GENRE.MUSIC_DANCE.4 джаз +GENRE.MUSIC_DANCE.5 музыка/опера +GENRE.MUSIC_DANCE.6 балет +GENRE.NEWS.0 новоÑти +GENRE.NEWS.1 новоÑти/погода +GENRE.NEWS.2 лента новоÑтей +GENRE.NEWS.3 документальные +GENRE.NEWS.4 диÑкуÑÑии/интервью/дебаты +GENRE.SHOW.0 Шоу / Игровое шоу +GENRE.SHOW.1 игровое шоу/викторина/ÐºÐ¾Ð½ÐºÑƒÑ€Ñ +GENRE.SHOW.2 варьете +GENRE.SHOW.3 разговорное шоу +GENRE.SOCIAL_POLITICAL.0 Ñоциальные & политичеÑкие ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ / Ð±Ð¸Ð·Ð½ÐµÑ +GENRE.SOCIAL_POLITICAL.1 журналы/отчеты/документалиÑтика +GENRE.SOCIAL_POLITICAL.2 Ñкономика/общеÑтвенные конÑультации +GENRE.SOCIAL_POLITICAL.3 замечательные люди +GENRE.SPORTS.0 Ñпорт +GENRE.SPORTS.1 Ñпециальные (ОлимпийÑкие игры, Мировые чемпионаты и Ñ‚.д.) +GENRE.SPORTS.10 конный Ñпорт +GENRE.SPORTS.11 военный Ñпорт +GENRE.SPORTS.2 Ñпортивные журналы +GENRE.SPORTS.3 футбол +GENRE.SPORTS.4 тенниÑ/Ñквош +GENRE.SPORTS.5 командный Ñпорт (иÑÐºÐ»ÑŽÑ‡Ð°Ñ Ñ„ÑƒÑ‚Ð±Ð¾Ð») +GENRE.SPORTS.6 атлетика +GENRE.SPORTS.7 мотоÑпорт +GENRE.SPORTS.8 водный Ñпорт +GENRE.SPORTS.9 зимний Ñпорт +GENRE.TRAVEL_HOBBIES.0 путешеÑÑ‚Ð²Ð¸Ñ Ð¸ отдых +GENRE.TRAVEL_HOBBIES.1 туризм/путешеÑÑ‚Ð²Ð¸Ñ +GENRE.TRAVEL_HOBBIES.2 ремеÑло +GENRE.TRAVEL_HOBBIES.3 автомобильный Ñпорт +GENRE.TRAVEL_HOBBIES.4 здоровье +GENRE.TRAVEL_HOBBIES.5 ÐºÑƒÑ…Ð½Ñ +GENRE.TRAVEL_HOBBIES.6 реклама/покупки +GENRE.TRAVEL_HOBBIES.7 ÑадоводÑтво +GENRE.UNKNOWN определить Ð½ÐµÐ»ÑŒÐ·Ñ +apids.hint_1 Введите APIDs потока +apids.hint_2 в hex, разделÑйте ' ' +apidselector.head Выбор Ñзыка +audiomenu.PCMOffset Уменьшение громкоÑти PCM +audiomenu.analogout Ðналоговый выход +audiomenu.avs avs +audiomenu.avs_control Управление громкоÑтью avs/ost +audiomenu.dolbydigital Dolby Digital +audiomenu.head Ðудио ÐаÑтройки +audiomenu.lirc lirc +audiomenu.monoleft левое моно +audiomenu.monoright правое моно +audiomenu.ost ost +audiomenu.stereo Ñтерео +audioplayer.add Добавить +audioplayer.artist_title ÐртиÑÑ‚, Ðазвание +audioplayer.building_search_index поÑтроение индекÑа поиÑка +audioplayer.button_select_title_by_id ПоиÑк по ID +audioplayer.button_select_title_by_name ПоиÑк по имени +audioplayer.defdir Каталог по умолчанию +audioplayer.delete Удалить +audioplayer.deleteall Удалить вÑе +audioplayer.display_order ПорÑдок Ñортировки +audioplayer.enable_sc_metadata enable shoutcast meta data parsing +audioplayer.fastforward Вперед +audioplayer.follow Выбор текущей +audioplayer.head Ðудио-плейлиÑÑ‚ +audioplayer.highprio Задать выÑокий приоритет +audioplayer.id3scan Сканировать ID3-теги +audioplayer.jump_backwards Шаг назад +audioplayer.jump_dialog_hint1 Выбор направление отката +audioplayer.jump_dialog_hint2 (в Ñекундах) +audioplayer.jump_dialog_title Выбор направление отката +audioplayer.jump_forwards Шаг вперёд +audioplayer.keylevel Другие кнопки ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ +audioplayer.name Ðудиоплеер +audioplayer.pause Пауза +audioplayer.play Играть +audioplayer.playing Выбранный трек +audioplayer.playlist_fileerror_msg Файл Ñоздать не удалоÑÑŒ: +audioplayer.playlist_fileerror_title Ошибочка +audioplayer.playlist_fileoverwrite_msg Этот файл будет перезапиÑан: +audioplayer.playlist_fileoverwrite_title ПерезапиÑать? +audioplayer.playlist_name Ð˜Ð¼Ñ Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° Ñ Ð¿Ð»ÐµÐ¹-лиÑтом +audioplayer.playlist_name_hint1 Введите Ð¸Ð¼Ñ Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° плей-лиÑта +audioplayer.playlist_name_hint2 РаÑширение .m3u добавитÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки +audioplayer.reading_files чтение файлов +audioplayer.repeat_on Разрешить повтор +audioplayer.rewind Обратно +audioplayer.save_playlist Сохранить лиÑÑ‚ +audioplayer.screensaver_timeout Включить Screensaver (мин, 0=откл.) +audioplayer.select_title_by_name ПоиÑк Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ имени (SMS) +audioplayer.show_playlist Показать Плей-лиÑÑ‚ +audioplayer.shuffle Смена порÑдка +audioplayer.stop ОÑтановить +audioplayer.title_artist Ðазвание, ÐртиÑÑ‚ +audioplayerpicsettings.general Ðудиоплеер и Картинки +bookmarkmanager.delete Удалить +bookmarkmanager.name Закладки +bookmarkmanager.rename Переименовать +bookmarkmanager.select Выбрать +bouqueteditor.add Добавить +bouqueteditor.bouquetname Ð˜Ð¼Ñ Ð¿Ð°ÐºÐµÑ‚Ð° +bouqueteditor.delete Удалить +bouqueteditor.discardingchanges Отмена изменений. /nподождите пожалуйÑта... +bouqueteditor.hide СпрÑтать +bouqueteditor.lock Блокировать +bouqueteditor.move ПеремеÑтить +bouqueteditor.name Редактор пакетов +bouqueteditor.newbouquetname Ðовое Ð¸Ð¼Ñ Ð¿Ð°ÐºÐµÑ‚Ð° +bouqueteditor.rename Переименовать +bouqueteditor.return готов +bouqueteditor.savechanges? Сохранить изменениÑ? +bouqueteditor.savingchanges Сохранение изменений. /nподождите пожалуйÑта... +bouqueteditor.switch добавить/удалить +bouqueteditor.switchmode ТВ/Радио +bouquetlist.head Пакеты +cablesetup.provider кабельный провайдер +channellist.head Ð’Ñе ÑервиÑÑ‹ +channellist.nonefound Ðет ни одного канала! \nПожалуйÑта выполните Ñканирование\n(кнопка DREAM -> СервиÑÑ‹) +channellist.since начало в %02d:%02d +colorchooser.alpha alpha +colorchooser.blue Ñиний +colorchooser.green зеленый +colorchooser.red краÑный +colormenu.background Фон +colormenu.fade Fade-режим Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ +colormenu.font Размер шрифта +colormenu.gtx_alpha ПрозрачноÑÑ‚ÑŒ (GTX) +colormenu.head ÐаÑтройки цвета +colormenu.menucolors Выбор цветов в меню +colormenu.statusbar Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ +colormenu.textcolor Цвет текÑта +colormenu.themeselect Выбор темы +colormenu.timing Ð’Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° OSD +colormenusetup.head Выбор цветов в меню +colormenusetup.menucontent Содержимое окна +colormenusetup.menucontent_inactive Содержимое окна неактивное +colormenusetup.menucontent_selected Содержимое окна выбранное +colormenusetup.menuhead Заголовок меню +colorstatusbar.head Инфопанель +colorstatusbar.text Инфопанель +colorthememenu.classic_theme Classic +colorthememenu.dblue_theme DarkBlue +colorthememenu.dvb2k_theme DVB2000 +colorthememenu.head Выбор темы +colorthememenu.neutrino_theme Neutrino +date.Apr Ðпр +date.Aug Ðвг +date.Dec Дек +date.Feb Фев +date.Fri Птн +date.Jan Янв +date.Jul Июл +date.Jun Июн +date.Mar Мар +date.May Май +date.Mon Пнд +date.Nov ÐÐ¾Ñ +date.Oct Окт +date.Sat Сбт +date.Sep Сен +date.Sun Ð’Ñк +date.Thu Чтв +date.Tue Ð’Ñ‚Ñ€ +date.Wed Срд +epglist.head Телегид - %s +epglist.noevents Программа передач отÑутÑтвует... +epgviewer.More_Screenings Дата/Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° и повтора на Ñтом канале +epgviewer.nodetailed ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ñ‚ÑутÑтвует +epgviewer.notfound Ðет доÑтупа к программе передач +eventlistbar.channelswitch вид Ñортировки +eventlistbar.eventsort Ñортировать +eventlistbar.recordevent запиÑать +favorites.addchannel Этот канал будет добавлен \nв пакет каналов "Избранное". \n Это займет неÑколько Ñекунд... +favorites.bouquetname Избранное +favorites.bqcreated Пакет каналов "Избранное" Ñоздан...\n +favorites.chadded Этот канал добавлен в ÑпиÑок избранных...\n +favorites.chalreadyinbq Этот канал уже еÑÑ‚ÑŒ в ÑпиÑке избранных...\n +favorites.finalhint \nВоÑпользуйтеÑÑŒ редактором пакетов\n Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑпиÑка ваших избранных каналов.\n +favorites.menueadd Добавить канал в избранное +favorites.nobouquets Избранное доÑтупно только из активных пакетов. +filebrowser.delete удалить +filebrowser.denydirectoryleave Absolute start directory +filebrowser.dodelete1 удалÑем раз +filebrowser.dodelete2 удалÑем два +filebrowser.filter.active фильтр вкл +filebrowser.filter.inactive фильтр выкл +filebrowser.head Проводник по каталогам +filebrowser.mark Пометить +filebrowser.nextpage След. Ñтраница +filebrowser.prevpage Пред. Ñтраница +filebrowser.scan Сканирование каталога +filebrowser.select Выбор +filebrowser.showrights Показывать права файлов +filebrowser.sort.date Сорт.(дата) +filebrowser.sort.name Сорт.(имÑ) +filebrowser.sort.namedirsfirst Сорт.(иначе) +filebrowser.sort.size Сорт.(размер) +filebrowser.sort.type Сорт.(тип) +filesystem.is.utf8 file system +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash чтение +flashupdate.cantopenfile не могу открыть файл +flashupdate.cantopenmtd не могу открыть уÑтройÑтво mtd +flashupdate.checkupdate поиÑк новой верÑии +flashupdate.currentreleasecycle ВерÑÐ¸Ñ Ñ€ÐµÐ»Ð¸Ð·Ð° +flashupdate.currentversion_sep Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ +flashupdate.currentversiondate Дата +flashupdate.currentversionsnapshot Тип прошивки +flashupdate.currentversiontime Ð’Ñ€ÐµÐ¼Ñ +flashupdate.erasefailed не могу Ñтереть флешь +flashupdate.erasing Ñтирание флешь +flashupdate.experimentalimage Ð’Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð°Ñ Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ° не проверена и ваш\nтюнер возможно не загрузитÑÑ Ð¿Ð¾Ñле обновлениÑ.\n\nÐ’Ñ‹ дейÑтвительно хотите обновить Ñтой верÑией? +flashupdate.expertfunctions ЭкÑпертные функции +flashupdate.fileis0bytes размер файла 0 байт +flashupdate.fileselector Выбор файла +flashupdate.flashreadyreboot Прошивка уÑпешно запиÑана.\nТюнер ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ³Ñ€ÑƒÐ·Ð¸Ñ‚ÑÑ. +flashupdate.getinfofile Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²ÐµÑ€Ñии +flashupdate.getinfofileerror не могу определить верÑию +flashupdate.getupdatefile обновление +flashupdate.getupdatefileerror не могу обновить +flashupdate.globalprogress Общий прогреÑÑ: +flashupdate.head Обновление Ñофта +flashupdate.md5check проверка прошивки +flashupdate.md5sumerror прошивка без ошибок +flashupdate.msgbox Ðайдена ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð½Ð¾Ð²Ð°Ñ Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ°:\nДата: %s, %s\nбазовый имидж: %s\nтип имиджа: %s\n\nХотите загрузить и уÑтановить Ñту верÑию ÑейчаÑ? +flashupdate.msgbox_manual Ðайден новый файл:\nДата: %s, %s\nБазовый имидж: %s\nТип: %s\n\n Хотите загрузить и уÑтановить Ñту верÑию ÑейчаÑ? +flashupdate.mtdselector Выбор раздела +flashupdate.programmingflash программирование флешь +flashupdate.proxypassword Пароль +flashupdate.proxypassword_hint1 введите пароль прокÑи-Ñервера +flashupdate.proxypassword_hint2 пуÑтое поле означает, что Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð½ÐµÑ‚ +flashupdate.proxyserver ХоÑÑ‚ +flashupdate.proxyserver_hint1 введите прокÑи-Ñервер или IP-Ð°Ð´Ñ€ÐµÑ Ð² виде host:port +flashupdate.proxyserver_hint2 пуÑтое поле означает, что нет прокÑи-Ñервера +flashupdate.proxyserver_sep ПрокÑи-Ñервер +flashupdate.proxyusername Ð˜Ð¼Ñ +flashupdate.proxyusername_hint1 введите Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐºÑи-Ñервера +flashupdate.proxyusername_hint2 пуÑтое поле означает, что нет авт.прокÑи +flashupdate.readflash Сохранить прошивку полноÑтью +flashupdate.readflashmtd Сохранить один из разделов +flashupdate.ready готов +flashupdate.reallyflashmtd Уверены что хотите запиÑать?\n\nЕÑли произойдет ошибка или прошивка не\nÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ñ‚ÑŽÐ½ÐµÑ€ не перегрузитÑÑ.\n\nÐ¸Ð¼Ñ Ð¸Ð¼Ð¸Ð´Ð¶Ð°: %s\nЦель: %s +flashupdate.savesuccess Прошивка уÑпешно Ñохранена\nв %s. +flashupdate.selectimage ДоÑтупные прошивки и файлы +flashupdate.squashfs.noversion Проверка обновлений Ð´Ð»Ñ SquashFS-верÑии возможна только по интернету.\n Ð’Ñ‹ уверены в Ñвоем желании уÑтановить Ñту прошивку? +flashupdate.titlereadflash Чтение флешь +flashupdate.titlewriteflash ЗапиÑываю флешь +flashupdate.updatemode Режим Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ +flashupdate.updatemode_internet интернет +flashupdate.updatemode_manual вручную (ftp) +flashupdate.url_file файл конфигурации +flashupdate.versioncheck проверка верÑии +flashupdate.writeflash ЗапиÑать прошивку полноÑтью +flashupdate.writeflashmtd ЗапиÑать один из разделов +flashupdate.wrongbase Ваша верÑÐ¸Ñ Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ¸ отличаетÑÑ Ð¾Ñ‚ выбранной Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸.\nЗагрузить в любом Ñлучае? +fontmenu.channellist СпиÑок каналов +fontmenu.epg EPG +fontmenu.eventlist СпиÑок Ñобытий +fontmenu.gamelist СпиÑок игр +fontmenu.head ÐаÑтройки размера шрифтов +fontmenu.infobar Инфопанель +fontsize.channel_num_zap прÑмой выбор +fontsize.channellist СпиÑок каналов +fontsize.channellist_descr ОпиÑание +fontsize.channellist_number Ðомер +fontsize.epg_date EPG дата +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG заголовок +fontsize.eventlist_datetime Дата / Ð’Ñ€ÐµÐ¼Ñ +fontsize.eventlist_itemlarge Большой +fontsize.eventlist_itemsmall Маленький +fontsize.eventlist_title Заголовок +fontsize.filebrowser_item Содержимое проводника +fontsize.gamelist_itemlarge Большой +fontsize.gamelist_itemsmall Маленький +fontsize.hint Фонт инициализации, \nждем... +fontsize.infobar_channame Ð˜Ð¼Ñ ÐºÐ°Ð½Ð°Ð»Ð° +fontsize.infobar_info Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ +fontsize.infobar_number Ðомер +fontsize.infobar_small Маленький +fontsize.menu ТекÑÑ‚ меню +fontsize.menu_info Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ ÐœÐµÐ½ÑŽ +fontsize.menu_title Заголовок меню +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +infoviewer.epgnotload EPG не загружено.... +infoviewer.epgwait попытка получить программу передач... +infoviewer.eventlist Телегид +infoviewer.languages Ðудио +infoviewer.motor_moving позиционирование антенны +infoviewer.nocurrent Ðет информации о текущей передаче +infoviewer.noepg Ðет доÑтупа к программе передач +infoviewer.notavailable Канал не доÑтупен +infoviewer.selecttime Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð° +infoviewer.streaminfo ХарактериÑтики +infoviewer.subchan_disp_pos Subchannel display +infoviewer.subservice Ð”Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ +infoviewer.waittime Попытка получить текущее времÑ... +ipsetup.hint_1 ИÑпользуйте 0..9, или Up/Down, +ipsetup.hint_2 OK - Ñохранено, LAME! - Отмена +keybindingmenu.RC Ð”Ð»Ñ ÐšÐ½Ð¾Ð¿Ð¾Ðº: повтор-блокирование +keybindingmenu.addrecord Добавить таймер запиÑи +keybindingmenu.addremind Добавить таймер Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ +keybindingmenu.allchannels_on_ok Ð’Ñе ÑервиÑÑ‹ +keybindingmenu.bouquetchannels_on_ok Текущий пакет +keybindingmenu.bouquetdown Предыдущий пакет +keybindingmenu.bouquethandling Пакет-контроль +keybindingmenu.bouquetlist_on_ok СпиÑок пакетов +keybindingmenu.bouquetup Следующий пакет +keybindingmenu.cancel Закрыть ÑпиÑок каналов +keybindingmenu.channeldown Канал вниз +keybindingmenu.channellist СпиÑок каналов +keybindingmenu.channelup Канал вверх +keybindingmenu.head ÐаÑтройка кнопок +keybindingmenu.lastchannel ВернутьÑÑ Ð½Ð° канал +keybindingmenu.modechange Изменение режима +keybindingmenu.pagedown Страница вниз +keybindingmenu.pageup Страница вверх +keybindingmenu.quickzap Quickzap +keybindingmenu.repeatblock Задержка повтора +keybindingmenu.repeatblockgeneric ÐžÐ±Ñ‰Ð°Ñ Ð·Ð°Ð´ÐµÑ€Ð¶ÐºÐ° +keybindingmenu.sort Изменить порÑдок Ñортировки +keybindingmenu.subchanneldown Подканалы вниз +keybindingmenu.subchannelup Подканалы вверх +keybindingmenu.tvradiomode ТВ/Радио-режим +keybindingmenu.zaphistory СпиÑок переключений +keychooser.head ÐаÑтроить новые кнопки +keychooser.text1 Ðажмите новую кнопку +keychooser.text2 подождете неÑколько Ñекунд Ð´Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° +keychoosermenu.currentkey Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° +keychoosermenu.setnew наÑтроить новые кнопки +keychoosermenu.setnone нет кнопки +languagesetup.head ÐаÑтройка Ñзыка +languagesetup.select Язык +lcdcontroler.brightness ЯркоÑÑ‚ÑŒ +lcdcontroler.brightnessstandby ЯркоÑÑ‚ÑŒ деж.режима +lcdcontroler.contrast КонтраÑтноÑÑ‚ÑŒ +lcdcontroler.head ЖК ÐаÑтройки +lcdmenu.autodimm Ðвто dimm +lcdmenu.head ЖК ÐаÑтройки +lcdmenu.inverse Инвертировать изображение +lcdmenu.lcdcontroler КонтраÑтноÑÑ‚ÑŒ / ЯркоÑÑ‚ÑŒ +lcdmenu.power Питание +lcdmenu.statusline Ð’ Ñтроке ÑтатуÑа +lcdmenu.statusline.both громкоÑÑ‚ÑŒ / Ð²Ñ€ÐµÐ¼Ñ +lcdmenu.statusline.playtime Ð²Ñ€ÐµÐ¼Ñ +lcdmenu.statusline.volume громкоÑÑ‚ÑŒ +mainmenu.audioplayer Ðудиоплеер +mainmenu.games Игры +mainmenu.head Меню ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ +mainmenu.movieplayer Видеоплеер +mainmenu.pausesectionsd Чтение EPG +mainmenu.pictureviewer ПроÑмотр картинок +mainmenu.radiomode Радио-каналы +mainmenu.recording ЗапиÑÑŒ +mainmenu.recording_start Ñтарт +mainmenu.recording_stop Ñтоп +mainmenu.scartmode Scart-режим +mainmenu.scripts Скрипты +mainmenu.service СервиÑÑ‹ +mainmenu.settings ÐаÑтройки +mainmenu.shutdown Выключить +mainmenu.sleeptimer Таймер дежурного режима +mainmenu.tvmode ТВ-каналы +mainsettings.audio Ðудио +mainsettings.colors Цвета / Темы / Шрифты +mainsettings.head ÐаÑтройки +mainsettings.keybinding ÐаÑтройки пульта ДУ +mainsettings.language Языки +mainsettings.lcd ЖК-диÑплей +mainsettings.misc Прочие наÑтройки +mainsettings.network Сеть +mainsettings.recording ЗапиÑÑŒ +mainsettings.savesettingsnow Ñохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ +mainsettings.savesettingsnow_hint Сохранение Ñделанных изменений.\nвыполнÑетÑÑ... +mainsettings.streaming Видеоплеер +mainsettings.video Видео +menu.back назад +messagebox.back Ðазад +messagebox.cancel Отменить +messagebox.discard Отменить изменениÑ? +messagebox.error Ошибка +messagebox.info Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ +messagebox.no Ðет +messagebox.yes Да +miscsettings.bootinfo Показывать информацию при Ñтарте +miscsettings.bootmenu Показывать меню загрузки +miscsettings.driver_boot Загрузка ÑиÑтемы +miscsettings.fb_destination Expert! Boot-Console +miscsettings.general ОÑновное +miscsettings.head Прочие наÑтройки +miscsettings.hwsections HW-Sections иÑпользовать +miscsettings.infobar_sat_display Ðазвание Ñпутника на инфопанели +miscsettings.noaviawatchdog enable AVIA watchdog +miscsettings.noenxwatchdog enable eNX watchdog +miscsettings.pmtupdate enable pmt update +miscsettings.shutdown_real Включить дежурный режим +miscsettings.shutdown_real_rcdelay Задержка Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ +miscsettings.sptsmode SPTS-Mode иÑпользовать +miscsettings.startbhdriver загрузить драйвера BH-Режима +motorcontrol.head ÐаÑтройки мотора +movieplayer.bookmark Закладки +movieplayer.bookmarkname Ðазвание закладки +movieplayer.bookmarkname_hint1 Введите название +movieplayer.bookmarkname_hint2 Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð¹ закладки +movieplayer.buffering БуферизациÑ... +movieplayer.defdir Каталог по умолчанию +movieplayer.defplugin Старт плагина +movieplayer.dvdplayback ПроÑмотр DVD (VLC) +movieplayer.fileplayback ПроÑмотр файлов (VLC) +movieplayer.goto Прыгнуть в ... +movieplayer.goto.h1 = -> абÑолютный прыжок +movieplayer.goto.h2 +,- -> отноÑительный прыжок +movieplayer.head Видеоплеер +movieplayer.nostreamingserver Стрим-Ñервер недоÑтупен. +movieplayer.pesplayback ПроÑмотр PES-файлов +movieplayer.pleasewait Подождите пожалуйÑта.\nСоединение Ñо Ñтрим-Ñервером... +movieplayer.toomanybookmarks Слишком много закладок.\n Удалите одну из уже ÑущеÑтвующих. +movieplayer.tshelp1 Ñтоп +movieplayer.tshelp10 назад на 10 мин. +movieplayer.tshelp11 вперед на 10 мин. +movieplayer.tshelp12 подробнее Ñмотри на http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 выбор звук.дорожки +movieplayer.tshelp3 пауза/продолжить +movieplayer.tshelp4 добавить закладку +movieplayer.tshelp5 общее Ð²Ñ€ÐµÐ¼Ñ +movieplayer.tshelp6 назад на 1 мин. +movieplayer.tshelp7 вперед на 1 мин. +movieplayer.tshelp8 назад на 5 мин. +movieplayer.tshelp9 вперед на 5 мин. +movieplayer.tsplayback ПроÑмотр TS-файлов +movieplayer.vcdplayback ПроÑмотр (S)VCD (VLC) +movieplayer.vlchelp1 Ñтоп +movieplayer.vlchelp10 назад на 10 мин. +movieplayer.vlchelp11 вперед на 10 мин. +movieplayer.vlchelp12 подробнее Ñмотри на: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 реÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ð·Ð¸Ñ +movieplayer.vlchelp3 пауза/продолжить +movieplayer.vlchelp4 добавить закладку +movieplayer.vlchelp5 общее Ð²Ñ€ÐµÐ¼Ñ +movieplayer.vlchelp6 назад на 1 мин. +movieplayer.vlchelp7 вперед на 1 мин. +movieplayer.vlchelp8 назад на 5 мин. +movieplayer.vlchelp9 вперед на 5 мин. +movieplayer.wrongvlcversion Эта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ поддерживаетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑией VLC +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway ОÑновной шлюз +networkmenu.head ÐаÑтройки Ñети +networkmenu.ipaddress IP-Ð°Ð´Ñ€ÐµÑ +networkmenu.mount NFS/CIFS/FTPFS +networkmenu.nameserver DNS-Ñервер +networkmenu.netmask МаÑка подÑети +networkmenu.setupnow применить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ +networkmenu.setuponstartup ÐаÑтроить Ñеть при Ñтарте +networkmenu.show ИÑпользуемые наÑтройки +networkmenu.test ТеÑÑ‚ Ñетевого ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ +nfs.alreadymounted Уже подключали +nfs.automount Подключать при включении +nfs.dir каталог/обший +nfs.ip IP-Ð°Ð´Ñ€ÐµÑ Ñервера +nfs.localdir Локальный каталог +nfs.mount Подключить Ñетевой раздел +nfs.mount_options Параметры Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ +nfs.mountok подключение ÑоÑтоÑлоÑÑŒ +nfs.mounterror Ошибка при подключении +nfs.mounterror_notsup Тип файловой ÑиÑтемы не поддерживаетÑÑ +nfs.mountnow Подключить ÑÐµÐ¹Ñ‡Ð°Ñ +nfs.mounttimeout Ошибка процеÑÑа подключениÑ: таймаут +nfs.password пароль +nfs.remount Повторить подключение +nfs.type тип +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount Отключить Ñетевой раздел +nfs.umounterror Ошибка Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð° +nfs.username Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ +nfsmenu.head NFS/CIFS/FTPFS наÑтройки +nvod.percentage (%d%% над) +nvod.starting (Старт через %d минут) +nvodselector.directormode Директ режим +nvodselector.head Выбор времени Ñтарта +nvodselector.subservice Выбор доп.ÑервиÑа +options.default ÐаÑтройки по умолчанию +options.fb framebuffer +options.null null +options.off выкл. +options.on вкл. +options.serial serial +parentallock.changepin Изменить PIN-код +parentallock.changepin_hint1 Введите новый PIN-код. +parentallock.changetolocked на пакет +parentallock.head Введите PIN-код +parentallock.lockage Передачи +parentallock.lockage12 до 12 лет +parentallock.lockage16 до 16 лет +parentallock.lockage18 до 18 лет +parentallock.lockedchannel УÑтановлена защита на проÑмотр... +parentallock.lockedprogram УÑтановлена зашита на проÑмотр (Ñ %d лет по) +parentallock.never не иÑпользовать +parentallock.onsignal на вÑе +parentallock.parentallock ВозраÑÑ‚Ð½Ð°Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ð° +parentallock.prompt Зашита PIN-кодом +pictureviewer.defdir Каталог по умолчанию +pictureviewer.head ПроÑмотр картинок +pictureviewer.help1 Режим меню +pictureviewer.help10 режим проÑмотра +pictureviewer.help11 перезагрузить картинку +pictureviewer.help12 Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ° +pictureviewer.help13 ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ° +pictureviewer.help14 уменьшить +pictureviewer.help15 увеличить +pictureviewer.help16 прокрутка вверх +pictureviewer.help17 прокрутка влево +pictureviewer.help18 прокрутка вправо +pictureviewer.help19 прокрутка вниз +pictureviewer.help2 показ картинки +pictureviewer.help20 Ñмена порÑдка Ñортировки +pictureviewer.help21 в оригнальный размер +pictureviewer.help22 выход +pictureviewer.help3 Ñмена порÑдка Ñортировки +pictureviewer.help4 не менÑÑ‚ÑŒ размеры +pictureviewer.help5 режим Ñлайдов +pictureviewer.help6 Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ° +pictureviewer.help7 ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ° +pictureviewer.help8 Ñмена порÑдка Ñортировки +pictureviewer.help9 выход +pictureviewer.resize.color_average раÑширенное +pictureviewer.resize.none не иÑпользовать +pictureviewer.resize.simple проÑтое +pictureviewer.scaling МаÑштабирование +pictureviewer.show Показать +pictureviewer.slide_time Задержка в режиме Ñлайдов +pictureviewer.slideshow Режим Ñлайдов +pictureviewer.sortorder ПорÑдок Ñортировки +pictureviewer.sortorder.date (дата) +pictureviewer.sortorder.filename (Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°) +ping.ok доÑтупен (ping) +ping.protocol недоÑтупен (ошибка Ñервера или протокола) +ping.socket недоÑтупен (ошибка Ñокета) +ping.unreachable недоÑтупен +pinprotection.head Введите PIN-код +pinprotection.wrongcode Ðеверный PIN-код! Повторите ввод. +plugins.result вывод плагина +rclock.lockmsg Ваш пульт ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½.\n Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ, нажмите <КРÐСÐУЮ> \n и на вашем пульте управлениÑ. +rclock.menueadd Заблокировать Пульт ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ +rclock.title Заблокировать Пульт ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ +rclock.unlockmsg Пульт ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½. +recordingmenu.choose_direct_rec_dir куда веÑти запиÑÑŒ +recordingmenu.defdir Каталог Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи +recordingmenu.epg_for_filename Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ о программе +recordingmenu.file direkt (в файл) +recordingmenu.filesettings наÑтройки direkt(в файл) +recordingmenu.head ÐаÑтройки запиÑи +recordingmenu.help Режимы запиÑи:\n--------------------------\nÑервер:\nиÑпользуетÑÑ Ñофт уÑтановленный на ПК\n\n(analog) vcr:\nиÑпользуетÑÑ Ð²Ñ‹Ñ…Ð¾Ð´ Ð´Ð»Ñ Ð²Ð¸Ð´ÐµÐ¾Ð¼Ð°Ð³Ð½Ð¸Ñ‚Ð¾Ñ„Ð¾Ð½Ð°\n\ndirect (в файл):\nзапиÑÑŒ в предварительно монтированный NFS-каталог\nили в каталоги внутреннего жеÑткого диÑка\nTS: иÑпользует spts-режим (Ð´Ð»Ñ Dbox2)\nPES: spts-режим должен быть выключен (Ð´Ð»Ñ Dbox2)\n\n\nМакÑ.размер файла:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: без ограничений (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart не переключатьÑÑ Ð½Ð° SCART +recordingmenu.off отключить +recordingmenu.recording_type Режим запиÑи +recordingmenu.ringbuffers значение Ð´Ð»Ñ ringbuffers +recordingmenu.server Ñервер +recordingmenu.server_ip IP-Ð°Ð´Ñ€ÐµÑ Ñервера запиÑи +recordingmenu.server_mac MAC-Ð°Ð´Ñ€ÐµÑ +recordingmenu.server_port Порт Ñервера запиÑи +recordingmenu.server_wakeup WOL Ñервера запиÑи +recordingmenu.setupnow применить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ +recordingmenu.splitsize МакÑ.размер файла (MB) +recordingmenu.stopplayback ОÑтановить воÑпроизведение +recordingmenu.stopsectionsd ОÑтановить sectionsd +recordingmenu.stream_vtxt_pid ВидеотекÑÑ‚ +recordingmenu.use_fdatasync write synchronous (fdatasync) +recordingmenu.use_o_sync Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ (O_SYNC) +recordingmenu.vcr vcr +recordtimer.announce ЗапиÑÑŒ начинаетÑÑ Ñ‡ÐµÑ€ÐµÐ· неÑколько минут +repeatblocker.hint_1 Ð’Ñ€ÐµÐ¼Ñ Ð² ms Ð´Ð»Ñ Ñ€Ð°ÑÐ¿Ð¾Ð·Ð½Ð°Ð²Ð°Ð½Ð¸Ñ 2-го Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ +repeatblocker.hint_2 Ðажмите 0 Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ (краÑÐ½Ð°Ñ - пробел) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-повторы +satsetup.extended ÐаÑтройки DiSEqC +satsetup.extended_motor ÐаÑтройки мотора +satsetup.minidiseqc мини-DiSEqC +satsetup.motorcontrol Ð ÑƒÑ‡Ð½Ð°Ñ Ð½Ð°Ñтройка мотора +satsetup.nodiseqc без DiSEqC +satsetup.satellite Спутники +satsetup.savesettingsnow Ñохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ +satsetup.smatvremote SMATV Remote Tuning +scants.actcable Кабельные: +scants.actsatellite Спутники: +scants.bouquet Пакеты +scants.bouquet_create Ñоздать новый +scants.bouquet_erase Ñтереть вÑе +scants.bouquet_leave иÑпользовать текущий +scants.bouquet_satellite пакет Ñпутника +scants.bouquet_update обновить +scants.channel Каналы: +scants.failed Ðеудачное Ñканирование транÑпондера! +scants.finished Сканирование транÑпондеров завершено! +scants.freqdata ЧаÑтота: +scants.head Сканирование транÑпондеров +scants.numberofdataservices Данные +scants.numberofradioservices Радио +scants.numberoftotalservices Ð’Ñего +scants.numberoftvservices ТВ +scants.provider Провайдеры: +scants.startnow Ðачать Ñканирование +scants.transponders ТранÑпондеры: +screensetup.lowerright green = наÑтроить Ñнизу Ñправа +screensetup.upperleft red = наÑтроить Ñверху Ñлева +servicemenu.head СервиÑÑ‹ +servicemenu.reload Перегрузить ÑпиÑок каналов +servicemenu.reload_hint Перезагрузка ÑпиÑка каналов,\nвыполнÑетÑÑ... +servicemenu.scants ПоиÑк каналов +servicemenu.ucodecheck Check ucodes +servicemenu.update Обновление прошивки +settings.help ПодÑказка +settings.missingoptionsconffile ÐаÑтройки ÑиÑтемы обновлены.\nÐовые уÑтановки заменÑÑ‚ наÑтройки по умолчанию. +settings.noconffile ÐаÑтройки ÑиÑтемы отÑутÑтвуют.\nИÑпользуютÑÑ Ð½Ð°Ñтройки по умолчанию. +settings.pos_bottom_left низ лево +settings.pos_bottom_right низ право +settings.pos_top_left верх лево +settings.pos_top_right верх право +shutdowntimer.announce Dbox будет выключен через 1 минуту.\nОтменить выключение? +sleeptimerbox.announce Отключение через 1 минуту +sleeptimerbox.hint1 Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð² минутах. (000=откл) +sleeptimerbox.hint2 Ваш Dbox будет выключен через указанное времÑ. +sleeptimerbox.title Таймер перехода в дежурный режим +streamfeatures.head Меню дополнений +streaminfo.aratio Aspect Ratio +streaminfo.aratio_unknown Aspect Ratio: неизвеÑтный +streaminfo.audiotype Audiotype +streaminfo.audiotype_unknown Audiotype: неизвеÑтный +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: неизвеÑтный +streaminfo.head Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ потоке +streaminfo.not_available не предÑтавлен +streaminfo.resolution Resolution +streaminfo.signal Ñигнал +streaming.buffer_overflow ЗапиÑÑŒ прервана,\n ÑкороÑÑ‚ÑŒ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… низкаÑ. +streaming.busy Идет запиÑÑŒ!\n ЕÑли запиÑÑŒ не включена, а Ð’Ñ‹ получили Ñто Ñообщение, то пожалуйÑта перегрузите Neutrino. +streaming.dir_not_writable Ðет доÑтупа в папку Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи.\nЗапиÑÑŒ не возможна. +streaming.success ЗапиÑÑŒ прошла уÑпешно. +streaming.write_error ЗапиÑÑŒ прервана,\n ошибка в процеÑÑе Ñамой запиÑи. +streaming.write_error_open ЗапиÑÑŒ прервана,\n файл Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи не может быть открыт. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head ÐаÑтройки видеоплеера +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off отключен +streamingmenu.on включен +streamingmenu.server_ip IP-Ð°Ð´Ñ€ÐµÑ Ñтрим-Ñервера +streamingmenu.server_port Порт Ñтрим-Ñервера +streamingmenu.streaming_audiorate Ðудио-поток (kbs) +streamingmenu.streaming_force_avi_rawaudio AC3 Ð´Ð»Ñ AVI +streamingmenu.streaming_force_transcode_video ТранÑкод видео (MPG/VCD) +streamingmenu.streaming_resolution Размер кадра +streamingmenu.streaming_server_cddrive DVD-привод +streamingmenu.streaming_server_startdir Каталог (VLC) +streamingmenu.streaming_transcode_audio ТранÑкод аудио (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec Видео-кодек MPEG +streamingmenu.streaming_type Стрим-Ñервер +streamingmenu.streaming_videorate Видео-поток (kbs) +streamingserver.noconnect Ðет ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñо Ñтрим-Ñервером.\nПринÑтие данных оÑтановлено. +stringinput.caps большие / маленькие +stringinput.clear очиÑтить вÑе +timer.eventrecord.msg ... Быть или не быть? ;-) +timer.eventrecord.title РаÑпиÑание запиÑи +timer.eventtimed.msg Событие запланировано.\nВаш Dbox будет включен и \nпереключитÑÑ Ð½Ð° Ñтот канал в заданное времÑ. +timer.eventtimed.title РаÑпиÑание Ñобытий +timerbar.channelswitch РаÑпиÑание +timerbar.recordevent ЗапиÑÑŒ +timerlist.alarmtime Ð’Ñ€ÐµÐ¼Ñ ALARM +timerlist.apids Ðудио PIDs +timerlist.bouquetselect Выбор пакета +timerlist.channel Каналы +timerlist.channelselect Выбор канала +timerlist.delete Удалить +timerlist.menumodify Изменить таймер +timerlist.menunew Ðовый таймер +timerlist.message Сообщение +timerlist.moderadio Радио каналы +timerlist.modeselect Тип канала +timerlist.modetv ТВ каналы +timerlist.modify Изменить +timerlist.name СпиÑок таймеров +timerlist.new Ðовый таймер +timerlist.plugin Плагин +timerlist.program.unknown ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° +timerlist.recording_dir каталог Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи +timerlist.reload Перегрузить +timerlist.repeat Повторение +timerlist.repeat.biweekly раз в 2 недели +timerlist.repeat.byeventdescription see timer +timerlist.repeat.daily ежедневно +timerlist.repeat.fourweekly раз в 4 недели +timerlist.repeat.friday Птн +timerlist.repeat.monday Пон +timerlist.repeat.monthly ежемеÑÑчно +timerlist.repeat.once одноразово +timerlist.repeat.saturday Сбт +timerlist.repeat.sunday Ð’Ñкр +timerlist.repeat.thursday Чтв +timerlist.repeat.tuesday Ð’Ñ‚Ñ€ +timerlist.repeat.unknown Хрен знает что +timerlist.repeat.wednesday Срд +timerlist.repeat.weekdays по днÑм недели +timerlist.repeat.weekly еженедельно +timerlist.repeatcount повторы +timerlist.repeatcount.help1 чиÑло повторов +timerlist.repeatcount.help2 0 - повтор навÑегда +timerlist.save Сохранить таймер +timerlist.standby Тип Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ +timerlist.standby.off oÑтавить в дежур.режиме +timerlist.standby.on вывеÑти из дежур.режима +timerlist.stoptime Ð’Ñ€ÐµÐ¼Ñ Ð¾Ñтановки +timerlist.type Режим таймера +timerlist.type.execplugin ЗапуÑтить плагин +timerlist.type.nextprogram Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° +timerlist.type.record запиÑÑŒ +timerlist.type.remind напоминание +timerlist.type.shutdown полное выкл. +timerlist.type.sleeptimer выкл. в дежур.режим +timerlist.type.standby вкл. из дежур.режима +timerlist.type.unknown определить Ð½ÐµÐ»ÑŒÐ·Ñ +timerlist.type.zapto включить на канал +timerlist.weekdays дни недели +timerlist.weekdays.hint_1 Пон Ð’Ñ‚Ñ€ Срд Чтв Птн Сбт Ð’Ñкр +timerlist.weekdays.hint_2 'X'=таймер '-' нет таймера +timersettings.record_safety_time_after ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð¾Ñтановки запиÑи +timersettings.record_safety_time_after.hint_1 ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ в минутах. (00=выкл). Это Ð²Ñ€ÐµÐ¼Ñ +timersettings.record_safety_time_after.hint_2 будет добавлÑетÑÑ Ðº каждому таймеру запиÑи. +timersettings.record_safety_time_before ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ñтарта запиÑи +timersettings.record_safety_time_before.hint_1 ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ в минутах. (00=выкл). Это Ð²Ñ€ÐµÐ¼Ñ +timersettings.record_safety_time_before.hint_2 будет отниматьÑÑ Ð¾Ñ‚ каждого таймера запиÑи. +timersettings.separator ÐаÑтройки таймера +timing.chanlist СпиÑок каналов +timing.epg Epg +timing.filebrowser Проводник по каталогам +timing.head Ð’Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° OSD +timing.hint_1 Ð’Ñ€ÐµÐ¼Ñ Ð² Ñекундах. ПоÑле Ñтого +timing.hint_2 инфопанель будет прозрачной. +timing.infobar Инфопанель +timing.menu Меню +timing.numericzap При наборе цифрами Ñ Ð¿ÑƒÐ»ÑŒÑ‚Ð° +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head проверка UCode +ucodecheck.ucode UCode +ucodes.failure Ð’ÐИМÐÐИЕ, , mCodes нет в наличии!\nПожалуйÑта загрузите их через FTP(или Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ DBox-BootManager),\nзатем перегрузите Ваш Dbox! +videomenu.csync ÐºÐ¾Ñ€ÐµÐºÑ†Ð¸Ñ Ñинхронизации +videomenu.head ÐаÑтройки видео-Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +videomenu.rgb_centering Центровка RGB +videomenu.screensetup ÐаÑтройки Ñкрана +videomenu.vcrswitch Ðвто-включение на SCART +videomenu.videoformat Формат +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect авто-формат +videomenu.videosignal Видео-выход +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zaptotimer.announce Таймер Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÐºÐ°Ð½Ð°Ð»Ð° Ñработает через 1 минуту +extra.dvbsnoop DVB Stream Info +extra.dvbsnoop_pat Show PAT +extra.dvbsnoop_cat Show CAT +extra.dvbsnoop_tsdt Show TSDT +extra.dvbsnoop_nit Show NIT +extra.dvbsnoop_sdt Show SDT +extra.dvbsnoop_eit Show EIT +extra.dvbsnoop_rst Show RST +extra.dvbsnoop_tdt Show TDT +extra.dvbsnoop_sit Show SIT +extra.dvbsnoop_pid Show manual PID +extra.dvbsnoop_scan Scan PIDs +extra.dvbsnoop_band Show pid bandwidth +extra.logview View Log +extra.english English +extra.dboxinfo Box Info +extra.scan_mode Scan mode +extra.start_tostandby Startup to standby +extra.rotor_swap Swap rotor east/west +extra.use_log Log to /tmp/log +extra.spts_mode SPTS mode +extra.hw_sect Hardware Sections +extra.logo Logo number +extra.scan_full Full +extra.scan_fast Fast +extra.hdd_slow Slow +extra.hdd_middle Middle +extra.hdd_fast Fast +extra.hdd_ext3 Ext3fs +extra.hdd_reiser Reiserfs +extra.hdd_1min 1 min. +extra.hdd_5min 5 min. +extra.hdd_10min 10 min. +extra.hdd_20min 20 min. +extra.hdd_30min 30 min. +extra.hdd_60min 60 min. +extra.logo1 1 +extra.logo2 2 +extra.logo3 3 +extra.hdd_sleep Sleep time +extra.hdd_noise Noise +extra.hdd_activate Activate settings +extra.hdd_fs Filesystem +extra.hdd_format Format HDD +extra.hdd_check Check filesystem +extra.hdd_settings HDD Settings +extra.clear_log Clear Log +extra.zap_cycle Zap cycle +extra.sms_channel sms-mode channel +extra.manual_scan Manual scan +extra.tp_freq Frequency +extra.tp_rate Symbol rate +extra.tp_pol Polarization +extra.tp.pol_v V +extra.tp_pol_h H +extra.tp_fec FEC +extra.fec_1_2 1/2 +extra.fec_2_3 2/3 +extra.fec_3_4 3/4 +extra.fec_5_6 5/6 +extra.fec_7_8 7/8 +extra.timezone Timezone +extra.zapit_menu Параметры Zapit +extra.zapit_make_bouquet Make Remaining Channels list +extra.zapit_save_last_chan Save last channel +extra.zapit_motor_speed Motor moving speed (10 = 1deg/sec) +extra.zapit_fe_timeout Tune timeout +extra.add_to_bouquet Канал в пакет +extra.chadded Канал добавлен в выбранный пакет....\n +extra.chalreadyinbq Канал уже ÑущеÑтвует в выбранном пакете....\n +extra.menu_left_exit Left = выход из меню +extra.zapit_write_names Имена каналов в bouquets.xml +extra.update_dir Каталог Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ð¹ +extra.zapit_fast_zap fast zap +extra.cache Cache +extra.debug Debug +extra.zapit_sort_names Sort ch. by name +extra.zapit_delete Delete channels +extra.zapit_backup Backup channels to /tmp +extra.zapit_restore Restore channels from /tmp +pictureviewer.decode_server_ip decode server ip +pictureviewer.decode_server_port decode server port +scants.abort_body Should the search really be aborted? +scants.abort_header Abortion of channel scan +recordingmenu.record_in_spts_mode switch to spts mode before recording +servicemenu.getplugins Reload plugins +servicemenu.getplugins_hint Reloading plugins,\nplease be patient. +zapit.scantype scan for services +zapit.scantype.all all services +zapit.scantype.radio only radio +zapit.scantype.tv only tv +zapit.scantype.tvradio tv & radio +colorthememenu.red_theme Simply Red +lcdmenu.dim_brightness Brightness after dim timeout +lcdmenu.dim_time Dim timeout +timerlist.overlapping_timer Timer conflict. Create the timer anyway? +extra.audio_run_player Audio key start player +mpkey.rewind Rewind +mpkey.forward Forward +mpkey.pause Pause +mpkey.stop Stop +mpkey.play Play +mpkey.audio Audio track +mpkey.time Show time +mpkey.bookmark save bookmark +mpkey.plugin Run plugin +extra.key_click Key click +extra.loadcolors Load colors from +extra.savecolors Save colors as +extra.loadkeys Load keys from +extra.savekeys Save keys as +extra.loadconfig Load settings from +extra.saveconfig Save settings as +videomenu.videomode Video system +videomenu.videomode_ntsc NTSC +videomenu.videomode_pal PAL +videomenu.videomode_palm PAL-M +extra.key_timeshift Timeshift +extra.key_plugin One touch plugin +extra.timeshift_pause Timeshift pause +rfmod.rfmod RF modulator +rfmod.carrier Subcarrier Frequency +rfmod.carrier4500 4.5 +rfmod.carrier5500 5.5 +rfmod.carrier6000 6.0 +rfmod.carrier6500 6.5 +rfmod.enable Enable sound +rfmod.channel Channel +rfmod.finetune Fine tune +rfmod.standby Enable modulator +rfmod.test Test pattern +extra.zapit_scanpids Scan/Use pids +extra.zapit_rezap_time Re-Zap on emu switch time +extra.zapit_hvoltage High (13.5/18.5) voltage +extra.key_unlock Unlock key +extra.mg_netoptions Network options +extra.mg_ecm_timeout Network ecm timeout +extra.mg_reconnect Shares reconnect +extra.mg_priority Share priority +extra.mg_osd_options Osd options +extra.mg_reread Reread files on zap +servicemenu.imageinfo Image info +imageinfo.creator Creator: +imageinfo.date Date: +imageinfo.dokumentation Docs: +imageinfo.forum Forum: +imageinfo.head Image info: +imageinfo.homepage Home page: +imageinfo.image Image: +imageinfo.license License: +imageinfo.version Version: +extra.cache_txt Cache teletext +extra.use_gotoxx Use gotoXX +extra.latitude Latitude +extra.longitude Longitude +extra.ladirection LaDirection +extra.lodirection LoDirection +extra.south South +extra.north North +extra.east East +extra.west West +epgextended.actors Actors +epgextended.director Director +epgextended.guests Guests +epgextended.original_title Original Title +epgextended.presenter Presenter +epgextended.year_of_production Year of Production +recordingmenu.save_in_channeldir Save in channel dir +servicemenu.restart Soft restart +servicemenu.restart_failed Restart failed +servicemenu.restart_hint Restarting, please wait +servicemenu.restart_refused_recording Cant restart, recording in progress +extra.tp_mod Modulation +extra.tp_mod_16 QAM/16 +extra.tp_mod_32 QAM/32 +extra.tp_mod_64 QAM/64 +extra.tp_mod_128 QAM/128 +extra.tp_mod_256 QAM/256 +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_no_temp no (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +options.on.without_messages Without msg +extra.zapit_scan_sdt Scan SDT for updates +extra.zapit_sdt_changed Channels changed, reload settings. +miscsettings.virtual_zap_mode Virtual zap +miscsettings.epg_head Epg settings +miscsettings.epg_cache_days EPG-Cache (days) +miscsettings.epg_old_hours Events old after (hours) +miscsettings.epg_dir Dir for epg cache +miscsettings.epg_save Save/Restore epg on reboot +shutdown.recoding_query Box in record mode, you really want to shutdown ? +recordingmenu.apids default audio streams +recordingmenu.apids_ac3 record AC3 streams +recordingmenu.apids_alt record alternative streams +recordingmenu.apids_std record standard stream +recordingmenu.stream_pmt_pid record PMT +recordingmenu.zap_on_announce zap on recording announce +timerlist.apids_dflt record default audio streams +videomenu.osd OSD +videomenu.tv-scart TV Scart +videomenu.vcr-scart VCR Scart +videomenu.vcrsignal VCR Output Signal Type +videomenu.vcrsignal_composite CVBS +videomenu.vcrsignal_svideo S-Video +moviebrowser.option_browser Browser Options +moviebrowser.book_clear_all Clear all +moviebrowser.menu_save Save changes +moviebrowser.menu_save_all Start update of movie info files +moviebrowser.info_head_update Save changes in all movie info files +moviebrowser.update_if_dest_empty_only Copy if destination is empty only +moviebrowser.serie_auto_create Serie auto create +moviebrowser.load_default Load default settings +moviebrowser.browser_row_head Row settings +moviebrowser.browser_row_nr Number of rows +moviebrowser.browser_row_item Row item +moviebrowser.browser_row_width Row width +moviebrowser.reload_at_start Reload movie info at start +moviebrowser.remount_at_start Remount at start +moviebrowser.dir_head Additional paths +moviebrowser.dir Path +moviebrowser.use_dir Use directory +moviebrowser.use_rec_dir Use record directory +moviebrowser.use_movie_dir Use movie directory +moviebrowser.hide_series Hide series +moviebrowser.last_record_max_items Number of lines last record +moviebrowser.last_play_max_items Number of lines last play +moviebrowser.browser_frame_high Browser hight [Pixel] +extra.auto_timeshift Auto-record, sec (0 = disable) +extra.auto_delete Auto-delete +extra.auto_to_record Move timeshift to records +extra.record_time Fast/timeshift record time(hours) diff --git a/data/locale/suomi.locale b/data/locale/suomi.locale new file mode 100644 index 000000000..39a797918 --- /dev/null +++ b/data/locale/suomi.locale @@ -0,0 +1,1049 @@ +AUDIOSelectMenue.head Kielivalinta +EPGMenu.epgplus EPG ohjelmaopas +EPGMenu.eventinfo Ohjelman kuvaus +EPGMenu.eventlist Kanavan ohjelmat +EPGMenu.head Ohjelmatiedot +EPGMenu.streaminfo Teknistä tietoa +EPGPlus.actions Toiminnot +EPGPlus.bybouquet_mode Kanavaniputtain +EPGPlus.bypage_mode Sivu kerrallaan +EPGPlus.change_font_size Muuta kirjasimen kokoa +EPGPlus.change_font_style Muuta kirjasinta +EPGPlus.change_size Muuta kokoa +EPGPlus.channelentry_font Kanavat +EPGPlus.channelentry_separationlineheight riviväli +EPGPlus.channelentry_width Kanavanimien leveys +EPGPlus.channelevententry_font Ohjelma +EPGPlus.edit_fonts Muokkaa kirjasinta +EPGPlus.edit_sizes Muokkaa kokoa +EPGPlus.font_style_bold lihavointi +EPGPlus.font_style_italic kursivointi +EPGPlus.font_style_regular normaali +EPGPlus.footer_fontbouquetchannelname Valitse kanavanipun/kanavan nimi +EPGPlus.footer_fontbuttons Näppäimet +EPGPlus.footer_fonteventdescription Ohjelman kuvaus +EPGPlus.footer_fonteventshortdescription Ohjelman kuvaus lyhyesti +EPGPlus.head EPG ohjelmaopas +EPGPlus.header_font Header +EPGPlus.horgap1_height height hor. gap 1 +EPGPlus.horgap2_height height hor. gap 2 +EPGPlus.next_bouquet Seuraava nippu +EPGPlus.options Asetukset +EPGPlus.page_down Sivu ylös +EPGPlus.page_up Sivu alas +EPGPlus.prev_bouquet Edellinen nippu +EPGPlus.record Ajasta tallennus +EPGPlus.refresh_epg Päivitä EPG-tiedot +EPGPlus.remind Ajasta kanavanvaihto +EPGPlus.reset_settings Palauta asetukset +EPGPlus.save_settings Tallenna asetukset +EPGPlus.scroll_mode Vieritys +EPGPlus.select_font_name Kirjasimen valinta +EPGPlus.settings Asetukset +EPGPlus.slider_width slider width +EPGPlus.stretch_mode Venytetty +EPGPlus.swap_mode Vaihtotila +EPGPlus.timeline_fontdate Pvm kirjasin +EPGPlus.timeline_fonttime Aika Kirjasin +EPGPlus.vergap1_width width ver. gap 1 +EPGPlus.vergap2_width width ver. gap 2 +EPGPlus.view_mode Näyttötila +GENRE.ARTS.0 taide / kulttuuri +GENRE.ARTS.1 esittävä taide +GENRE.ARTS.10 kulttuurimakasiini +GENRE.ARTS.11 muoti +GENRE.ARTS.2 korkeakulttuuri +GENRE.ARTS.3 uskonto +GENRE.ARTS.4 populaarikulttuuri +GENRE.ARTS.5 kirjallisuus +GENRE.ARTS.6 elokuva +GENRE.ARTS.7 kokeellinen elokuva +GENRE.ARTS.8 uutiset / lehdistö +GENRE.ARTS.9 uusmedia +GENRE.CHILDRENs_PROGRAMMES.0 lasten ja nuorten ohjelmaa +GENRE.CHILDRENs_PROGRAMMES.1 esikouluikäisten ohjelmaa +GENRE.CHILDRENs_PROGRAMMES.2 viihdettä 6 - 14 vuotiaille +GENRE.CHILDRENs_PROGRAMMES.3 viihdettä 10 - 16 vuotiaille +GENRE.CHILDRENs_PROGRAMMES.4 opetusohjelmaa +GENRE.CHILDRENs_PROGRAMMES.5 piirros- / nukkeanimaatio +GENRE.DOCUS_MAGAZINES.0 dokumentti ja makasiini +GENRE.DOCUS_MAGAZINES.1 luonto +GENRE.DOCUS_MAGAZINES.2 teknologia / luonnontiede +GENRE.DOCUS_MAGAZINES.3 lääketiede / fysiologia / psykologia +GENRE.DOCUS_MAGAZINES.4 vieraat kulttuurit/tutkimusmatkailu +GENRE.DOCUS_MAGAZINES.5 yhteiskunta ja hengellinen +GENRE.DOCUS_MAGAZINES.6 koulutus +GENRE.DOCUS_MAGAZINES.7 kielet +GENRE.MOVIE.0 draama +GENRE.MOVIE.1 dekkari/trilleri +GENRE.MOVIE.2 seikkailu/lännenelokuva/sota +GENRE.MOVIE.3 tieteis/fantasia/kauhu +GENRE.MOVIE.4 komedia +GENRE.MOVIE.5 sarjat/melodraama +GENRE.MOVIE.6 romantiikka +GENRE.MOVIE.7 historiallinen elokuva/draama +GENRE.MOVIE.8 aikuisviihde/draama +GENRE.MUSIC_DANCE.0 musiikki / tanssi +GENRE.MUSIC_DANCE.1 rock / pop +GENRE.MUSIC_DANCE.2 klassinen musiikki +GENRE.MUSIC_DANCE.3 kansanmusiikki +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musikaali / ooppera +GENRE.MUSIC_DANCE.6 baletti +GENRE.NEWS.0 uutiset +GENRE.NEWS.1 uutiset / sääennuste +GENRE.NEWS.2 ajankohtaisohjelma +GENRE.NEWS.3 dokumentti +GENRE.NEWS.4 keskustelu / haastattelu +GENRE.SHOW.0 viihde / visailu +GENRE.SHOW.1 visailu +GENRE.SHOW.2 viihdeohjelma +GENRE.SHOW.3 keskusteluohjelma +GENRE.SOCIAL_POLITICAL.0 yhteiskunta, politiikka ja talous +GENRE.SOCIAL_POLITICAL.1 raportit ja makasiinit +GENRE.SOCIAL_POLITICAL.2 talousneuvontaa +GENRE.SOCIAL_POLITICAL.3 merkkihenkilöitä +GENRE.SPORTS.0 urheilu +GENRE.SPORTS.1 erikoistapahtumat +GENRE.SPORTS.10 hevosurheilu +GENRE.SPORTS.11 kamppailulajit +GENRE.SPORTS.2 urheilumakasiini +GENRE.SPORTS.3 jalkapallo +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 joukkuelajit +GENRE.SPORTS.6 yleisurheilu +GENRE.SPORTS.7 moottoriurheilu +GENRE.SPORTS.8 vesiurheilu +GENRE.SPORTS.9 talviurheilu +GENRE.TRAVEL_HOBBIES.0 harrasteet, viihde ja virkistys +GENRE.TRAVEL_HOBBIES.1 matkailu ja turismi +GENRE.TRAVEL_HOBBIES.2 käsityöt +GENRE.TRAVEL_HOBBIES.3 autoilu +GENRE.TRAVEL_HOBBIES.4 kuntoilu ja terveys +GENRE.TRAVEL_HOBBIES.5 ruoka +GENRE.TRAVEL_HOBBIES.6 ostos-tv +GENRE.TRAVEL_HOBBIES.7 puutarha +GENRE.UNKNOWN tuntematon +apids.hint_1 Syötä streamattavat audio PIDit +apids.hint_2 heksalukuina välilyönneillä erotettuna +apidselector.head Valitse kieli +audiomenu.PCMOffset PCM äänen vaimennus +audiomenu.analogout Analoginen lähtö +audiomenu.audio_left_right_selectable Stereo/mono-valinta +audiomenu.audiochannel_up_down_enable Valitse ääniraita nuolilla +audiomenu.avs avs +audiomenu.avs_control Äänenvoimakkuuden säätö +audiomenu.dolbydigital Suosi AC3/DD-ääniraitaa +audiomenu.head Ääniasetukset +audiomenu.lirc lirc +audiomenu.monoleft mono vasen +audiomenu.monoright mono oikea +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add Lisää +audioplayer.artist_title Artisti, Kappale +audioplayer.building_search_index Luon hakutietokantaa +audioplayer.button_select_title_by_id etsi ID:llä +audioplayer.button_select_title_by_name etsi nimellä +audioplayer.defdir Aloitus hakemisto/suunta +audioplayer.delete poista kaikki +audioplayer.deleteall poista kaikki +audioplayer.display_order järjestys +audioplayer.enable_sc_metadata Näytä shoutcastin metadata +audioplayer.fastforward kelaus eteenpäin +audioplayer.follow valitse automaattisesti nykyinen +audioplayer.head Soittolista +audioplayer.highprio Korkea purun prioriteetti +audioplayer.id3scan ID3 tietojen haku +audioplayer.jump_backwards hyppää taaksepäin +audioplayer.jump_dialog_hint1 Syötä hyppykohta +audioplayer.jump_dialog_hint2 (suhteellinen, sekunneissa) +audioplayer.jump_dialog_title Syötä hyppykohta +audioplayer.jump_forwards Hyppy eteenpäin +audioplayer.keylevel näppäin taso +audioplayer.name Musiikkisoitin +audioplayer.pause Tauko +audioplayer.play Pääte .m3u lisätään automaattisesti +audioplayer.playing Current Track +audioplayer.playlist_fileerror_msg Tiedoston luonti epäonnistui: +audioplayer.playlist_fileerror_title Virhe +audioplayer.playlist_fileoverwrite_msg Haluatko ylikirjoittaa tiedoston: +audioplayer.playlist_fileoverwrite_title Ylikirjoita? +audioplayer.playlist_name Pääte .m3u lisätään automaattisesti +audioplayer.playlist_name_hint1 Syötä soittolistan nimi +audioplayer.playlist_name_hint2 Pääte .m3u lisätään automaattisesti +audioplayer.reading_files Luetaan metadataa +audioplayer.repeat_on Toisto päälle +audioplayer.rewind kelaus taaksep. +audioplayer.save_playlist tallenna soittolista +audioplayer.screensaver_timeout ruudunsäästäjän viive (min, 0=pois) +audioplayer.select_title_by_name Etsi nimellä (SMS) +audioplayer.show_playlist Näytä soittolista +audioplayer.shuffle satunnaissoitto +audioplayer.stop Pysäytä +audioplayer.title_artist Kappale, Artisti +audioplayerpicsettings.general mp3-soitin / kuvankatselu +bookmarkmanager.delete poista +bookmarkmanager.name kirjanmerkit +bookmarkmanager.rename uudelleen nimeä +bookmarkmanager.select valitse +bouqueteditor.add Lisää +bouqueteditor.bouquetname Kanavanippujen nimet +bouqueteditor.delete Poista +bouqueteditor.discardingchanges Kumotaan muutokset. Odota hetki. +bouqueteditor.hide Piilota +bouqueteditor.lock Lukitse +bouqueteditor.move Siirrä +bouqueteditor.name Kanavaniput +bouqueteditor.newbouquetname Uusi nimi kanavanipulle +bouqueteditor.rename Nimeä +bouqueteditor.return Valmis +bouqueteditor.savechanges? Tallennetaanko muutokset? +bouqueteditor.savingchanges Tallennetaan muutoksia. Odota hetki. +bouqueteditor.switch TV/Radio +bouqueteditor.switchmode TV/Radio +bouquetlist.head Kanavaniput +cablesetup.provider Verkko +channellist.head Kaikki palvelut +channellist.nonefound Kanavia ei löytynyt!\nTee kanavahaku\n(dbox-näppäin -> asetukset) +channellist.since alkaen %02d:%02d +colorchooser.alpha alpha +colorchooser.blue sininen +colorchooser.green vihreä +colorchooser.red punainen +colormenu.background Tausta +colormenu.background_head Tausta +colormenu.fade Fade valikot +colormenu.font Kirjasinkoot +colormenu.gtx_alpha Läpinäkyvyys +colormenu.head Käyttöliittymän ulkoasu +colormenu.menucolors Valikon värit +colormenu.statusbar Infopalkin värit +colormenu.textcolor Tekstin väri +colormenu.textcolor_head Tekstin väri +colormenu.themeselect Teeman valinta +colormenu.timing OSD-ajoitukset +colormenusetup.head Valikon värit +colormenusetup.menucontent Valitun ikkunan sisältö +colormenusetup.menucontent_inactive Passiivisen ikkunan sisältö +colormenusetup.menucontent_selected Valitun ikkunan sisältö +colormenusetup.menuhead Valikon otsikko +colorstatusbar.head Infopalkki +colorstatusbar.text Infopalkki +colorthememenu.classic_theme Perinteinen +colorthememenu.dblue_theme DarkBlue +colorthememenu.dvb2k_theme DVB2000 +colorthememenu.head Teeman valinta +colorthememenu.neutrino_theme Neutrino +date.Apr Huhti +date.Aug Elo +date.Dec Joulu +date.Feb Helmi +date.Fri Pe +date.Jan Tammi +date.Jul Heinä +date.Jun Kesä +date.Mar Maalis +date.May Touko +date.Mon Ma +date.Nov Marras +date.Oct Loka +date.Sat La +date.Sep Syys +date.Sun Su +date.Thu To +date.Tue Ti +date.Wed Ke +epgextended.actors Näyttelijät +epgextended.director Ohjaaja +epgextended.guests Vieraat +epgextended.original_title Alkuperäinen nimi +epgextended.presenter Esittäjä +epgextended.year_of_production Tuotantovuosi +epglist.head Ohjelmalista - %s +epglist.noevents EPG ei ole saatavilla... +epgviewer.More_Screenings Muita näytöksiä tällä kanavalla +epgviewer.nodetailed Lisäkuvausta ei ole saatavilla +epgviewer.notfound EPG:tä ei löytynyt +eventlistbar.channelswitch Ajasta kanavan vaihto +eventlistbar.eventsort Lajittele +eventlistbar.recordevent Tallenna +experimentalsettings Kokeiluasetuksia +experimentalsettings.head Kokeiluasetuksia +favorites.addchannel Nykyinen kanava lisätään "Suosikit" kanavanippuun.\nOdota hetki... +favorites.bouquetname Suosikit +favorites.bqcreated Kanavanippu "Suosikit" luotu...\n +favorites.chadded Kanava lisätty suosikkeihisi...\n +favorites.chalreadyinbq Kanava on jo suosikeissasi...\n +favorites.finalhint \nKäytä kanavanippueditoria suosikkiesi muokkaamiseen.\n +favorites.menueadd Lisää kanava suosikkeihin +favorites.nobouquets Suosikit ovat käytettävissä vain aktiivisista kanavanipuista. +filebrowser.delete Poista +filebrowser.denydirectoryleave Rajaa aloitushakemistoon +filebrowser.dodelete1 Poista +filebrowser.dodelete2 ? +filebrowser.filter.active Suodatus päälle +filebrowser.filter.inactive Suodatus pois +filebrowser.head Tiedostoselain +filebrowser.mark Merkitse +filebrowser.nextpage Seur. sivu +filebrowser.prevpage Edell. sivu +filebrowser.scan Selaan kansiota +filebrowser.select Valitse +filebrowser.showrights Näytä tiedosto-oikeudet +filebrowser.sort.date (pvm) +filebrowser.sort.name (nimi) +filebrowser.sort.namedirsfirst (hak/tied) +filebrowser.sort.size (koko) +filebrowser.sort.type (tyyppi) +filesystem.is.utf8 Tiedostonimien merkistö +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash Luetaan +flashupdate.cantopenfile Tiedostoa ei voida avata +flashupdate.cantopenmtd MTD-laitetta ei voida käyttää +flashupdate.checkupdate Etsi uutta versiota +flashupdate.currentreleasecycle Versio +flashupdate.currentversion_sep Nykyinen versio +flashupdate.currentversiondate Päiväys +flashupdate.currentversionsnapshot Tyyppi +flashupdate.currentversiontime Aika +flashupdate.erasefailed Flashin tyhjennys epäonnistui +flashupdate.erasing Flash-muistin tyhjennys +flashupdate.experimentalimage Ohjelmisto jonka valitsit on testaamaton. Tämä tarkoittaa\n, että laitteesi ei ehkä käynnisty päivityksen jälkeen.\n\nOletko varma, että haluat päivittää tähän versioon? +flashupdate.expertfunctions Erikoistoiminnot +flashupdate.fileis0bytes Tiedosto on tyhjä (0 tavua) +flashupdate.fileselector Valitse tiedosto +flashupdate.flashreadyreboot Ohjelmistopäivitys onnistui.\nDBox täytyy käynnistää uudelleen. +flashupdate.getinfofile Versiotiedot +flashupdate.getinfofileerror Versiotietoja ei voitu lukea +flashupdate.getupdatefile Päivityksen haku +flashupdate.getupdatefileerror Päivityksen hakeminen epäonnistui +flashupdate.globalprogress Kokonaiskesto: +flashupdate.head Ohjelmistopäivitys +flashupdate.md5check Ohjelmiston tarkastus +flashupdate.md5sumerror Virheellinen ohjelmisto +flashupdate.msgbox Uusi ohjelmistoversio löydetty:\nPvm: %s, %s\nOhjelmistopohja: %s\nTyyppi: %s\n\nHaluatko asentaa tämän ohjelmistoversion? +flashupdate.msgbox_manual Uusi ohjelmistoversio löydetty:\nPvm: %s, %s\nOhjelmistopohja: %s\nTyyppi: %s\n\nHaluatko asentaa tämän ohjelmistoversion? +flashupdate.mtdselector Valitse osio +flashupdate.programmingflash Flash-muistin kirjoitus +flashupdate.proxypassword Salasana +flashupdate.proxypassword_hint1 Anna välityspalvelimen salasana. +flashupdate.proxypassword_hint2 Jätä tyhjäksi, jos salasanaa ei tarvita. +flashupdate.proxyserver Osoite ja portti +flashupdate.proxyserver_hint1 Anna palvelimen nimi tai IP-osoite, muodossa palvelin:portti +flashupdate.proxyserver_hint2 Jätä tyhjäksi mikäli välityspalvelinta ei käytetä. +flashupdate.proxyserver_sep Välityspalvelin (proxy) +flashupdate.proxyusername Käyttäjätunnus +flashupdate.proxyusername_hint1 Anna välityspalvelimen käyttäjätunnus. +flashupdate.proxyusername_hint2 Jätä tyhjäksi mikäli käyttätunnusta ei tarvita. +flashupdate.readflash Lue koko flash-muisti +flashupdate.readflashmtd Lue yksi osio +flashupdate.ready Valmis +flashupdate.reallyflashmtd Haluatko varmasti päivittää?\n\nMikäli päivitys epäonnistuu tai ohjelmisto\non viallinen, laite ei enää käynnisty uudelleen.\n\nOhjelmisto: %s\nKohde: %s +flashupdate.savesuccess Ohjelmisto tallennettu polkuun %s. +flashupdate.selectimage Saatavilla olevat ohjelmistot +flashupdate.squashfs.noversion SquashFS-versiotarkastus toimii vain\nwebin kautta päivitettäessä.\n\nHaluatko jatkaa tarkastamatta versiota? +flashupdate.titlereadflash Flash-muistin luku +flashupdate.titlewriteflash Flash-muistin kirjoitus +flashupdate.updatemode Päivitystapa +flashupdate.updatemode_internet Internet +flashupdate.updatemode_manual Manuaalinen +flashupdate.url_file Päivitysasetukset +flashupdate.versioncheck Version tarkastus +flashupdate.writeflash Kirjoita koko flash-muisti +flashupdate.writeflashmtd Kirjoita yksi osio +flashupdate.wrongbase Ohjelmistosi perusversio eroaa uudesta. Päivitys ei\nole mahdollista ilman koko ohjelmiston vaihtoa. +fontmenu.channellist Kanavalista +fontmenu.epg EPG +fontmenu.eventlist Ohjelmalista +fontmenu.gamelist Pelit +fontmenu.head Kirjasinkoot +fontmenu.infobar Infopalkki +fontsize.channel_num_zap Kanavan suoravalinta +fontsize.channellist Kanavalista +fontsize.channellist_descr Kuvaus +fontsize.channellist_number Numero +fontsize.epg_date EPG päiväys +fontsize.epg_info1 EPG info 1 +fontsize.epg_info2 EPG info 2 +fontsize.epg_title EPG otsikko +fontsize.eventlist_datetime Päiväys / aika +fontsize.eventlist_itemlarge Suuri +fontsize.eventlist_itemsmall Pieni +fontsize.eventlist_title Otsikko +fontsize.filebrowser_item Tiedostoselain +fontsize.gamelist_itemlarge Suuri +fontsize.gamelist_itemsmall Pieni +fontsize.hint Alustan kirjasinta,\nodota hetki... +fontsize.imageinfo_info Ohjelmistotiedot +fontsize.imageinfo_small Ohjelmiston lisenssitiedot +fontsize.infobar_channame Kanavan nimi +fontsize.infobar_info Info +fontsize.infobar_number Numero +fontsize.infobar_small Pieni +fontsize.menu Valikkoteksti +fontsize.menu_info Valikkotiedot +fontsize.menu_title Valikko-otsikko +gtxalpha.alpha1 Otsikko +gtxalpha.alpha2 Valikko +imageinfo.creator Koonnut: +imageinfo.date Päiväys: +imageinfo.dokumentation Ohjeet: +imageinfo.forum Keskustelu: +imageinfo.head Ohjelmiston tiedot +imageinfo.homepage Kotisivu: +imageinfo.image Nimi: +imageinfo.license Lisenssi: +imageinfo.version Versio: +infoviewer.epgnotload EPG ei vielä valmis... +infoviewer.epgwait odotetaan EPG:tä... +infoviewer.eventlist Ohjelmaopas +infoviewer.languages Ääni +infoviewer.motor_moving Moottori kääntyy.. +infoviewer.nocurrent Ohjelmasta ei ole tietoja saatavilla +infoviewer.noepg EPG ei ole käytettävissä +infoviewer.notavailable Kanava ei ole käytettävissä +infoviewer.selecttime Aikavalinta +infoviewer.streaminfo Lisätoim. +infoviewer.subchan_disp_pos Alikanavan näyttö +infoviewer.subservice Subservices +infoviewer.waittime Waiting for time... +ipsetup.hint_1 Käytä 0..9, tai Nuoli ylös/alas, +ipsetup.hint_2 OK tallentaa, Home keskeyttää +keybindingmenu.RC Kaukosäätimen näppäintoisto +keybindingmenu.addrecord - Ajastintallennus +keybindingmenu.addrecord_head Ajastintallennuksen näppäin +keybindingmenu.addremind - Kanavanvaihtoajastus +keybindingmenu.addremind_head Kanavanvaihtoajastimen näppäin +keybindingmenu.allchannels_on_ok Kaikki palvelut +keybindingmenu.bouquetchannels_on_ok Kanavanipuittain +keybindingmenu.bouquetdown - Edellinen kanavanippu +keybindingmenu.bouquetdown_head Edellisen kanavanipun näppäin +keybindingmenu.bouquethandling Kanavaselaus +keybindingmenu.bouquetlist_on_ok Kanavanippu-lista +keybindingmenu.bouquetup - Seuraava kanavanippu +keybindingmenu.bouquetup_head Seuraavan kanavanipun näppäin +keybindingmenu.cancel - Paluunäppäin +keybindingmenu.cancel_head Paluunäppäin +keybindingmenu.channeldown - Edellinen kanava +keybindingmenu.channeldown_head Edellisen kanavan näppäin +keybindingmenu.channellist Kanavalista +keybindingmenu.channelup - Seuraava kanava +keybindingmenu.channelup_head Seuraavan kanavan näppäin +keybindingmenu.head Näppäinasetukset +keybindingmenu.lastchannel - Paluu aiemmalle kanavalle +keybindingmenu.lastchannel_head Paluu Aiemmalle kanavalle +keybindingmenu.modechange Tilanvaihto +keybindingmenu.pagedown - Seuraava sivu +keybindingmenu.pagedown_head Seuraavan sivun näppäin +keybindingmenu.pageup - Edellinen sivu +keybindingmenu.pageup_head Edellisen sivun näppäin +keybindingmenu.quickzap Pikavaihto +keybindingmenu.repeatblock Toistoviive +keybindingmenu.repeatblockgeneric Yleisviive +keybindingmenu.sort - Lajittelujärjestys +keybindingmenu.sort_head Lajittelujärjestyksen näppän +keybindingmenu.subchanneldown - Edellinen alikanava +keybindingmenu.subchanneldown_head Edellisen alikanavan näppäin +keybindingmenu.subchannelup - Seuraava alikanava +keybindingmenu.subchannelup_head Seuraavan alikanavan näppäin +keybindingmenu.tvradiomode - TV/Radio +keybindingmenu.tvradiomode_head TV/Radio valintanäppäin +keybindingmenu.zaphistory - Kanavahistoria +keybindingmenu.zaphistory_head Kanavahistorian näppäin +keychooser.head Valitse uusi näppäin +keychooser.text1 Paina näppäintä +keychooser.text2 Odota hetki keskeyttääksesi +keychoosermenu.currentkey Nykyinen näppäin +keychoosermenu.setnew Aseta uusi näppäin +keychoosermenu.setnone Ei näppäintä +languagesetup.head Kielivalikko +languagesetup.select Kieli +lcdcontroler.brightness Kirkkaus +lcdcontroler.brightnessstandby - valmiustilassa +lcdcontroler.contrast Kontrasti +lcdcontroler.head LCD asetukset +lcdmenu.autodimm Automaattinen himmennys +lcdmenu.dim_brightness Himmennetty kirkkaus +lcdmenu.dim_time Viive ennen himmennystä +lcdmenu.head LCD-näytön asetukset +lcdmenu.inverse Käänteinen +lcdmenu.lcdcontroler Kontrasti / Kirkkaus +lcdmenu.power Näyttö +lcdmenu.statusline Näytä +lcdmenu.statusline.both äänenvoim. / aikajana +lcdmenu.statusline.playtime aikajana +lcdmenu.statusline.volume äänenvoimakkuus +mainmenu.audioplayer Musiikkisoitin +mainmenu.games Pelit +mainmenu.head Päävalikko +mainmenu.movieplayer Mediasoitin +mainmenu.pausesectionsd Lue EPG +mainmenu.pictureviewer Kuvankatselu +mainmenu.radiomode Radio +mainmenu.reboot Käynnistä uudelleen +mainmenu.recording Tallennus +mainmenu.recording_start Käynnistä +mainmenu.recording_stop Pysäytä +mainmenu.scartmode Scart +mainmenu.scripts Lisäkomennot +mainmenu.service Huoltovalikko +mainmenu.settings Asetukset +mainmenu.shutdown Sammuta +mainmenu.sleeptimer Uniajastin +mainmenu.tvmode TV +mainsettings.audio Ääni +mainsettings.colors Käyttöliittymän ulkoasu +mainsettings.head Asetukset +mainsettings.keybinding Näppäinasetukset +mainsettings.language Kieli +mainsettings.lcd LCD-näyttö +mainsettings.misc Lisäasetukset +mainsettings.network Verkko +mainsettings.recording Tallennus +mainsettings.savesettingsnow Tallenna asetukset +mainsettings.savesettingsnow_hint Asetuksien tallennus,\nodota hetki... +mainsettings.streaming Mediasoitin +mainsettings.video Kuva +menu.back Palaa +messagebox.back Palaa +messagebox.cancel Peruuta +messagebox.discard Peruuta muutokset? +messagebox.error Virhe +messagebox.info Info +messagebox.no Ei +messagebox.yes Kyllä +miscsettings.bootinfo Näytä tiedot käynnistyksessä +miscsettings.bootmenu Näytä käynnistysvalikko +miscsettings.cabledriver Lataa Nokian kaapeliajuri +miscsettings.driver_boot Ajurit ja käynnistys +miscsettings.fb_destination Konsoli +miscsettings.general Yleiset +miscsettings.head Sekalaiset asetukset +miscsettings.hwsections Käytä hwsections:ia +miscsettings.infobar_sat_display Näytä satelliitin nimi +miscsettings.noaviawatchdog Käytä AVIA valvontaa +miscsettings.noenxwatchdog Käytä eNX valvontaa +miscsettings.pmtupdate Käytä PMT päivitystä +miscsettings.shutdown_count Automaattinen sammutus +miscsettings.shutdown_count_hint1 Aika minuutteina jonka jälkeen laite sammuu +miscsettings.shutdown_count_hint2 automaattisesti valmiustilasta (0 = pois). +miscsettings.shutdown_real Käytä valmiustilaa +miscsettings.shutdown_real_rcdelay Sammutusviive +miscsettings.sptsmode Käytä spts-tilaa +miscsettings.startbhdriver Lataa BH-tilan ajurit +miscsettings.tuxtxt_cache Teksti-tv:n välimuisti +motorcontrol.head Kääntömoottorin asetukset +moviebrowser.book_head Bookmarks +moviebrowser.book_lastmoviestop Last play stop: +moviebrowser.book_movieend Movie end: +moviebrowser.book_moviestart Movie start: +moviebrowser.book_name Name: +moviebrowser.book_new New Bookmark +moviebrowser.book_position Position: +moviebrowser.book_type Jump (<0 back , >0 for): +moviebrowser.book_type_backward Repeat +moviebrowser.book_type_forward jump over +moviebrowser.edit_book Bookmark change +moviebrowser.edit_book_name_info1 Enter new Bookmark name +moviebrowser.edit_book_name_info2 +moviebrowser.edit_book_pos_info1 Enter new Position (s) +moviebrowser.edit_book_pos_info2 +moviebrowser.edit_book_type_info1 Enter new jump length (s) +moviebrowser.edit_book_type_info2 <0 back , >0 for, 0: none +moviebrowser.edit_serie Enter name of serie +moviebrowser.error_no_movies No movies found +moviebrowser.foot_filter Filter: +moviebrowser.foot_play Start movie +moviebrowser.foot_sort Sort: +moviebrowser.head TS MovieBrowser +moviebrowser.head_filter Filter movies by category: +moviebrowser.head_playlist Last played: +moviebrowser.head_recordlist Last recorded: +moviebrowser.hint_jumpbackward Jump back in 5 s\n '0' to cancel +moviebrowser.hint_jumpforward Jump forward in 5 s\n '0' to cancel +moviebrowser.hint_movieend Filmende in 5 s\n '0' zum weitersehen +moviebrowser.hint_newbook_backward New jump back\n 'blue' for endposition +moviebrowser.hint_newbook_forward New jump forward\n 'blue' for endposition +moviebrowser.info_audio Audio +moviebrowser.info_channel Channel +moviebrowser.info_filename Name +moviebrowser.info_genre_major Genre +moviebrowser.info_genre_minor Genre +moviebrowser.info_head Film Informationen +moviebrowser.info_info1 Info 1 +moviebrowser.info_info2 Info 2 +moviebrowser.info_length Length (Min) +moviebrowser.info_parental_lockage Parental Lock age +moviebrowser.info_parental_lockage_0year always +moviebrowser.info_parental_lockage_12year 12 years +moviebrowser.info_parental_lockage_16year 16 years +moviebrowser.info_parental_lockage_18year 18 years +moviebrowser.info_parental_lockage_6year 6 years +moviebrowser.info_parental_lockage_always never +moviebrowser.info_path Path +moviebrowser.info_prevplaydate Last play date +moviebrowser.info_prodcountry Country +moviebrowser.info_prodyear Year +moviebrowser.info_quality Quality +moviebrowser.info_recorddate Record date +moviebrowser.info_serie Serie +moviebrowser.info_size File size (MB) +moviebrowser.info_title Titel +moviebrowser.info_videoformat Picture +moviebrowser.menu_directories_head Paths +moviebrowser.menu_help_head Help +moviebrowser.menu_main_bookmarks Bookmarks +moviebrowser.menu_main_head Settings +moviebrowser.menu_main_movieinfo Movie info +moviebrowser.menu_main_saveandback save and back +moviebrowser.menu_nfs_head NFS settings +moviebrowser.menu_parental_lock_activated activated +moviebrowser.menu_parental_lock_activated_no no +moviebrowser.menu_parental_lock_activated_yes yes +moviebrowser.menu_parental_lock_activated_yes_temp yes (temporary) +moviebrowser.menu_parental_lock_head Parental Lock +moviebrowser.menu_parental_lock_rate_head Lock movies from +moviebrowser.scan_for_movies Scan for Movies ... +moviebrowser.serie_existingname Existing series +moviebrowser.serie_head Serie +moviebrowser.serie_name Change name +moviebrowser.short_audio Audio +moviebrowser.short_book Book +moviebrowser.short_channel Channel +moviebrowser.short_country Country +moviebrowser.short_filename Name +moviebrowser.short_format Format +moviebrowser.short_genre_major Genre +moviebrowser.short_genre_minor Genre +moviebrowser.short_info1 Info 1 +moviebrowser.short_info2 Info 2 +moviebrowser.short_length Min +moviebrowser.short_parental_lockage Age +moviebrowser.short_path Path +moviebrowser.short_prevplaydate Last +moviebrowser.short_prodyear Year +moviebrowser.short_quality * (quality) +moviebrowser.short_recorddate Date +moviebrowser.short_serie Serie +moviebrowser.short_size MB +moviebrowser.short_title Title +moviebrowser.start_head Start movie from: +moviebrowser.start_record_start Movie start +movieplayer.bookmark Kirjanmerkit +movieplayer.bookmarkname Kirjanmerkki +movieplayer.bookmarkname_hint1 Syötä nimi uudelle kirjanmerkille +movieplayer.bookmarkname_hint2 +movieplayer.buffering Puskuroin... +movieplayer.defdir Oletushakemisto +movieplayer.defplugin Oletuslaajennos +movieplayer.dvdplayback DVD +movieplayer.fileplayback Toisto VLC:n kautta +movieplayer.goto +,- -> suhteellinen hyppy +movieplayer.goto.h1 = -> absoluuttinen hyppy +movieplayer.goto.h2 +,- -> suhteellinen hyppy +movieplayer.head Mediasoitin +movieplayer.nostreamingserver Videon lähettäjäpalvelimeen ei saatu yhteyttä. +movieplayer.pleasewait Odota hetki.\nYhdistän videopalvelimeen... +movieplayer.toomanybookmarks Liian monta kirjanmerkkiä.\nPoista vähintään yksi ensin. +movieplayer.tshelp1 Ohjeet: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp10 n. 10 minuuttia taaksepäin +movieplayer.tshelp11 n. 10 minuuttia eteenpäin +movieplayer.tshelp12 Ohjeet: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Valitse ääniraita +movieplayer.tshelp3 Tauko/Jatka +movieplayer.tshelp4 Luo kirjanmerkki +movieplayer.tshelp5 Näytä eteneminen +movieplayer.tshelp6 n. minuutti taaksepäin +movieplayer.tshelp7 n. minuutti eteenpäin +movieplayer.tshelp8 n. 5 minuuttia taaksepäin +movieplayer.tshelp9 n. 5 minuuttia eteenpäin +movieplayer.tsplayback Toista tiedosto +movieplayer.tsplayback_pc Toista tiedosto (PIN-koodattu) +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp1 Ohjeet: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp10 n. 10 minuuttia taaksepäin +movieplayer.vlchelp11 n. 10 minuuttia eteenpäin +movieplayer.vlchelp12 Ohjeet: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Tahdista +movieplayer.vlchelp3 Tauko/Jatka +movieplayer.vlchelp4 Luo kirjanmerkki +movieplayer.vlchelp5 Näytä eteneminen +movieplayer.vlchelp6 n. minuutti taaksepäin +movieplayer.vlchelp7 n. minuutti eteenpäin +movieplayer.vlchelp8 n. 5 minuuttia taaksepäin +movieplayer.vlchelp9 n. 5 minuuttia eteenpäin +movieplayer.wrongvlcversion Tämä toiminto ei ole käytössä tässä VLC versiossa +networkmenu.broadcast Broadcast-osoite +networkmenu.dhcp DHCP +networkmenu.gateway Yhdyskäytävä +networkmenu.head Verkkoasetukset +networkmenu.ipaddress IP-osoite +networkmenu.mount Verkkolevyt +networkmenu.nameserver Nimipalvelin +networkmenu.netmask Aliverkon peite +networkmenu.setupnow Ota asetukset käyttöön +networkmenu.setuponstartup Verkko käynnistyksessä +networkmenu.show Näytä nykyiset asetukset +networkmenu.test Testaa verkkoasetukset +nfs.alreadymounted Hakemisto on jo liitetty +nfs.automount Liitä käynnistyksessä +nfs.dir Palvelimen hakemisto +nfs.ip Palvelimen IP-osoite +nfs.localdir Paikallinen hakemisto +nfs.mount Verkkolevyjen asetukset +nfs.mount_options Liitosasetukset +nfs.mounterror Tuntematon tiedostojärjestelmä +nfs.mounterror_notsup Tuntematon tiedostojärjestelmä +nfs.mountnow Liitä nyt +nfs.mountok Liitos onnistui +nfs.mounttimeout Liitos epäonnistui: aikakatkaisu +nfs.password Salasana +nfs.remount Liitä verkkolevyt uudelleen +nfs.type Verkkolevyn tyyppi +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount Vapauta verkkolevy +nfs.umounterror Virhe vapauttaessa verkkolevyä +nfs.username Käyttäjätunnus +nfsmenu.head Verkkolevyjen asetukset +nvod.percentage (%d%% jäljellä) +nvod.starting (Käynnistys %d minuutin kuluttua) +nvodselector.directormode Suora-Tila +nvodselector.head Valitse aloitusajankohta +nvodselector.subservice Valitse palvelu +options.default Palauta oletukset +options.fb Framebuffer +options.null (null) +options.off Pois +options.on Päällä +options.on.without_messages Hiljaisesti +options.serial Sarjaportti +parentallock.changepin PIN-koodin vaihto +parentallock.changepin_hint1 Syötä lapsilukon uusi PIN-koodi. +parentallock.changetolocked lukituille kanavanipuille +parentallock.head Syötä lapsilukon PIN-koodi +parentallock.lockage Lukitse +parentallock.lockage12 alle 12 vuotiailta +parentallock.lockage16 alle 16 vuotiailta +parentallock.lockage18 alle 18 vuotiailta +parentallock.lockedchannel Lukittu kanava... +parentallock.lockedprogram Lukittu ohjelma (alle %d vuotiailta) +parentallock.never ei koskaan +parentallock.onsignal lähetyksen mukaan +parentallock.parentallock Lapsilukko +parentallock.prompt PIN-koodin kysely +pictureviewer.decode_server_ip palvelimen IP-osoite +pictureviewer.decode_server_port palvelimen portti +pictureviewer.defdir Aloitushakemisto +pictureviewer.head Kuvankatselu +pictureviewer.help1 siirry alas +pictureviewer.help10 Katselu tila +pictureviewer.help11 lue uudelleen kuva +pictureviewer.help12 edellinen kuva +pictureviewer.help13 seuraava kuva +pictureviewer.help14 loitonna +pictureviewer.help15 lähennä +pictureviewer.help16 siirry ylös +pictureviewer.help17 siirry vasemmalle +pictureviewer.help18 siirry oikealle +pictureviewer.help19 siirry alas +pictureviewer.help2 poistu +pictureviewer.help20 vaihda järjestystä +pictureviewer.help21 lue kuva uudelleen (ei skaalausta) +pictureviewer.help22 poistu +pictureviewer.help3 muuta järjestystä +pictureviewer.help4 älä muuta kuvan kokoa +pictureviewer.help5 diaesitys +pictureviewer.help6 edellinen kuva +pictureviewer.help7 seuraava kuva +pictureviewer.help8 muuta järjestystä +pictureviewer.help9 poistu +pictureviewer.resize.color_average advanced +pictureviewer.resize.none ei mitään +pictureviewer.resize.simple yksinkertainen +pictureviewer.scaling skaalaus +pictureviewer.show näytä +pictureviewer.slide_time diaesityksessä kuvan näyttöaika +pictureviewer.slideshow diaesitys +pictureviewer.sortorder (tiedostonimi) +pictureviewer.sortorder.date (pvm) +pictureviewer.sortorder.filename (tiedostonimi) +ping.ok tavoitettavissa (ping) +ping.protocol ei tavoitettavissa (host or protocol error) +ping.socket ei tavoitettavissa (socket error) +ping.unreachable ei tavoitettavissa +pinprotection.head Syötä PIN-koodi +pinprotection.wrongcode Virheellinen PIN-koodi. Yritä uudelleen. +plugins.result Info +rclock.lockmsg Kaukosäädin on lukittu.\nAvaa painamalla ensin punaista- ja sitten DBox-näppäintä. +rclock.menueadd Lukitse kaukosäädin +rclock.title Kaukosäätimen lukitus +rclock.unlockmsg Lukitus avattu. +recordingmenu.apids Tallennuksen äänet +recordingmenu.apids_ac3 Tallenna AC3 ääni +recordingmenu.apids_alt Tallenna muut äänet +recordingmenu.apids_std Tallenna oletusääni +recordingmenu.choose_direct_rec_dir Hakemiston valinta välittömässä tall. +recordingmenu.defdir Tallennushakemisto +recordingmenu.dir_permissions Hakemisto-oikeudet +recordingmenu.dir_permissions_hint Hakemiston oikeudet unix-tyyliin. Yleensä 755. +recordingmenu.epg_for_filename Pitkät tiedostonimet (sis. EPG-tiedot) +recordingmenu.file Tiedosto +recordingmenu.filename_template Tiedostojen nimeäminen +recordingmenu.filename_template_hint %c=kanava, %i=ohjelman nimi,%d=päiväys, %t=aika +recordingmenu.filesettings Tiedostotallennuksen asetukset +recordingmenu.head Tallennusasetukset +recordingmenu.help Tallennustavat:\n- Palvelin:\n-- Tallennus käyttäen PC:ssä olevaa erillistä ohjelmistoa\n- Videonauhuri:\n-- Tallennus VCR-scart:iin kytketylle videonauhurille\n\n- Tiedosto:\n-- Tallennus suoraan tiedostoon\n -- verkkolevylle tai sisäiselle kiintolevylle\nTallennus spts-tilassa:\n- Päällä = TS\n- Pois = PES\n\nTiedostojärjestelmien suurimmat tiedostokoot:\n- NFS V2: 2 GB (2048 MB)\n- NFS V3: lähes rajoittamaton (0 MB)\n- FAT: 2 GB (2048 MB)\n- FAT32: 4 GB (4096 MB) +recordingmenu.no_scart Älä vaihda scart-tilaan +recordingmenu.off Pois +recordingmenu.record_in_spts_mode Tallennus spts-tilassa +recordingmenu.recording_type Tallennustapa +recordingmenu.ringbuffers Rengaspuskureiden määrä +recordingmenu.server Palvelin (stream) +recordingmenu.server_ip Palvelimen IP-osoite +recordingmenu.server_mac Palvelimen MAC +recordingmenu.server_port Palvelimen portti +recordingmenu.server_wakeup Palvelimen Wake-On-Lan +recordingmenu.setupnow Ota asetukset käyttöön +recordingmenu.splitsize Suurin tiedostokoko (MB) +recordingmenu.stopplayback Pysäytä toisto +recordingmenu.stopsectionsd Pysäytä sectionsd +recordingmenu.stream_pmt_pid Tallenna PMT +recordingmenu.stream_vtxt_pid Tallenna teksti-tv +recordingmenu.use_fdatasync Tallenna synkronisesti (fdatasync) +recordingmenu.use_o_sync Tallenna asynkronisesti (O_SYNC) +recordingmenu.vcr Videonauhuri +recordingmenu.zap_on_announce Kanavanvaihtoennakko +recordtimer.announce Tallennus alkaa muutaman minuutin kuluttua +repeatblocker.hint_1 Pienin aikaväli (ms) 2 näppäinpainalluksen tunnistamiseen +repeatblocker.hint_2 Syötä 0 vaihtaaksesi kursori (punainen = välilyönti) +satsetup.diseqc DiSEqC-toisto +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-toisto +satsetup.extended Moottorin asetukset +satsetup.extended_motor Moottorin asetukset +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manaalinen moottorin asennus +satsetup.nodiseqc no DiSEqC +satsetup.satellite Satelliitti +satsetup.savesettingsnow Tallenna asetukset +satsetup.smatvremote SMATV Remote Tuning +scantp.fec Virheenkorjaus +scantp.fec_1_2 1/2 +scantp.fec_2_3 2/3 +scantp.fec_3_4 3/4 +scantp.fec_5_6 5/6 +scantp.fec_7_8 7/8 +scantp.freq Taajuus +scantp.pol Polariteetti +scantp.pol_h Vaaka +scantp.pol_v Pysty +scantp.rate Symbolinopeus +scantp.scan Manuaalinen haku +scantp.scanmode Nopea haku +scants.abort_body Keskeytetäänkö haku? +scants.abort_header Kanavien haku keskeytetty +scants.actcable Kaapeli: +scants.actsatellite Sateliitti: +scants.bouquet Kanavaniput +scants.bouquet_create luo uudet +scants.bouquet_erase poista kaikki +scants.bouquet_leave jätä nykyiset +scants.bouquet_satellite satelliittinippu +scants.bouquet_update päivitä +scants.channel Kanava: +scants.failed Lähetinhaku epäonnistui! +scants.finished Lähetinhaku valmis! +scants.freqdata Taajuus: +scants.head Skannaa lähetin +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Yhteensä +scants.numberoftvservices TV +scants.provider Palveluntarjoaja: +scants.startnow Aloita haku +scants.transponders lähettimet: +screensetup.lowerright vihreä = säädä alakulmaa +screensetup.upperleft punainen = säädä yläkulmaa +sectionsd.scanmode Autom. kanavapäivitys +servicemenu.getplugins Lue laajennoslista uudelleen +servicemenu.getplugins_hint Ladataan laajennoksia,\nodota hetki. +servicemenu.head Huoltovalikko +servicemenu.imageinfo Ohjelmiston tiedot +servicemenu.reload Lue kanavalista uudelleen +servicemenu.reload_hint Ladataan kanavalistoja,\nodota hetki. +servicemenu.restart Käynnistä Neutrino uudelleen +servicemenu.restart_failed Neutrinon uudelleenkäynnistys epäonnistui. +servicemenu.restart_hint Uudelleenkäynnistetään Neutrino... +servicemenu.restart_refused_recording Tallennus käynnissä. Uudelleenkäynnistys estetty. +servicemenu.scants Kanavahaku +servicemenu.ucodecheck µCodejen tiedot +servicemenu.update Ohjelmistopäivitys +settings.help Ohje +settings.missingoptionsconffile Neutrinon asetukset päivitetty.\nUudet asetukset on alustettu oletusarvoihin. +settings.noconffile Neutrinon asetuksia ei löytynyt.\nKaikki asetukset on alustettu oletusarvoihin. +settings.pos_bottom_left vasen ala +settings.pos_bottom_right oikea ala +settings.pos_top_left vasen ylä +settings.pos_top_right oikea ylä +shutdown.recoding_query Tallennus käynnissä. Haluatko varmasti sammuttaa? +shutdowntimer.announce Sammutus minuutin kuluttua.\nPeru sammutus? +sleeptimerbox.announce Uniajastimessa minuutti jäljellä. +sleeptimerbox.hint1 Sammutusaika minuutteina (000=ei sammutusta). +sleeptimerbox.hint2 Laite sammuu itsestään asetetun ajan kuluttua. +sleeptimerbox.title Uniajastin +streamfeatures.head Lisätoiminnot +streaminfo.aratio Kuvasuhde +streaminfo.aratio_unknown Tuntematon kuvasuhde +streaminfo.audiotype Ääniraita +streaminfo.audiotype_unknown Tuntematon ääniraita +streaminfo.bitrate Bittinopeus +streaminfo.framerate Kuvataajuus +streaminfo.framerate_unknown Tuntematon kuvataajuus +streaminfo.head Lähetteen tiedot +streaminfo.not_available ei saatavilla +streaminfo.resolution Resoluutio +streaminfo.signal Signaalitasot +streaming.buffer_overflow Tallennus keskeytyi,\nkoska videota ei pystytty tallentamaan tarpeeksi nopeasti. +streaming.busy Yksi tai useampi tallennus käynnissä.\nMikäli olet varma, että tallennusta ei ole, käynnistä laite uudelleen. +streaming.dir_not_writable Tallennushakemistoon ei voida kirjoittaa\nTallennus ei toimi. +streaming.success Tallennus onnistui. +streaming.write_error Tallennus keskeytyi,\nkoska tallennustiedostoa ei voitu luoda. +streaming.write_error_open Tallennus keskeytyi,\nkoska tallennustiedostoa ei voitu luoda. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Mediasoittimen asetukset +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Pois +streamingmenu.on Päällä +streamingmenu.server_ip Palvelimen IP-osoite +streamingmenu.server_port Palvelimen portti +streamingmenu.streaming_audiorate Äänen datanopeus +streamingmenu.streaming_force_avi_rawaudio Pakota AC3 AVI:lle +streamingmenu.streaming_force_transcode_video Muunna video (MPG/VCD) +streamingmenu.streaming_resolution Resoluutio +streamingmenu.streaming_server_cddrive DVD-aseman polku +streamingmenu.streaming_server_startdir Aloituspolku +streamingmenu.streaming_transcode_audio Muunna audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG video +streamingmenu.streaming_type Palvelin (stream) +streamingmenu.streaming_videorate Kuvan datanopeus +streamingserver.noconnect Tallennuspalvelimelle ei saatu yhteyttä.\nTallennus keskeytetty. +stringinput.caps ABC / abc +stringinput.clear Tyhjennä kaikki +timer.eventrecord.msg Oletko varma? +timer.eventrecord.title Ajasta tallennus +timer.eventtimed.msg Tapahtuma ajastettu.\nLaite käynnistyy ja vaihtaa\nkanavalle tähän aikaan. +timer.eventtimed.title Ajasta tapahtuma +timerbar.channelswitch Ajasta +timerbar.recordevent Tallenna +timerlist.alarmtime Alkuaika +timerlist.apids Audio PIDit +timerlist.bouquetselect Valitse kanavanippu +timerlist.channel Valitse kanava +timerlist.channelselect Valitse kanava +timerlist.delete Poista +timerlist.menumodify Muokkaa ajastusta +timerlist.menunew Uusi ajastus +timerlist.message Viesti +timerlist.moderadio Radiokanavat +timerlist.modeselect Tilan valinta +timerlist.modetv TV-kanavat +timerlist.modify Muokkaa +timerlist.name Ajastukset +timerlist.new Uusi ajastus +timerlist.overlapping_timer Päällekkäisyys ajastuksissa. Jatketaanko? +timerlist.plugin Laajennos +timerlist.program.unknown Tuntematon ohjelma +timerlist.recording_dir Tallennushakemisto +timerlist.reload Lataa uudelleen +timerlist.repeat Toisto +timerlist.repeat.biweekly joka toinen viikko +timerlist.repeat.byeventdescription kuvaksen mukaan +timerlist.repeat.daily päivittäin +timerlist.repeat.fourweekly 4 viikon välein +timerlist.repeat.friday Pe +timerlist.repeat.monday Ma +timerlist.repeat.monthly kuukausittain +timerlist.repeat.once kerran +timerlist.repeat.saturday La +timerlist.repeat.sunday Su +timerlist.repeat.thursday To +timerlist.repeat.tuesday Ti +timerlist.repeat.unknown ei tiedossa +timerlist.repeat.wednesday Ke +timerlist.repeat.weekdays arkisin +timerlist.repeat.weekly viikoittain +timerlist.repeatcount Toistokerrat +timerlist.repeatcount.help1 Toistokertojen lukumäärä. +timerlist.repeatcount.help2 0 = rajoittamattomasti. +timerlist.save Tallenna ajastus +timerlist.standby Tila +timerlist.standby.off Poistu valmiustilasta +timerlist.standby.on Siirry valmiustilaan +timerlist.stoptime Päättymisaika +timerlist.type Toiminne +timerlist.type.execplugin Suorita laajennos +timerlist.type.nextprogram Seuraava ohjelma +timerlist.type.record Tallennus +timerlist.type.remind Muistutus +timerlist.type.shutdown Sammutus +timerlist.type.sleeptimer Uniajastin +timerlist.type.standby Valmiustila +timerlist.type.unknown Tuntematon +timerlist.type.zapto Kanavanvaihto +timerlist.weekdays 'X'=ajastus '-' ei ajastusta +timerlist.weekdays.hint_1 Ma Ti Ke To Pe La Su +timerlist.weekdays.hint_2 'X'=ajastus '-' ei ajastusta +timersettings.record_safety_time_after Ajastuksen pidennys +timersettings.record_safety_time_after.hint_1 Pidennysaika minuutteina (00=pois). Jokaista +timersettings.record_safety_time_after.hint_2 tallennusta jatketaan tämän verran yliajalle. +timersettings.record_safety_time_before Ajastuksen ennakko +timersettings.record_safety_time_before.hint_1 Ennakkoaika minuutteina (00=pois). Jokainen +timersettings.record_safety_time_before.hint_2 tallennus aloitetaan tämän verran etuajassa. +timersettings.separator Ajastusasetukset +timing.chanlist Kanavalista +timing.epg EPG ohjelmaopas +timing.filebrowser Tiedostoselain +timing.head OSD-ajoitukset +timing.hint_1 Aika sekunteina. Infopalkkia/valikkoa +timing.hint_2 näytetään tämä aika. +timing.infobar Infopalkki +timing.menu Valikko +timing.numericzap Kanavan valinta numerolla +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head µCodejen tiedot +ucodecheck.ucode µCode +ucodes.failure VAROITUS: µCodeja ei löytynyt!\n\nOle hyvä ja siirrä ne FTP:llä (tai DBox-BootManagerilla) laitteeseen,\nja käynnistä se uudelleen! +videomenu.csync Tahdistuskorjaus +videomenu.head Kuva-asetukset +videomenu.rgb_centering RGB keskitys +videomenu.screensetup OSD-näytön kohdistus +videomenu.vcrswitch SCART:in automaattiohjaus +videomenu.videoformat Kuvasuhde +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect automaattinen +videomenu.videosignal Videolähtö +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zapit.scantype Haettavat palvelut +zapit.scantype.all kaikki palvelut +zapit.scantype.radio vain radiokanavat +zapit.scantype.tv vain TV-kanavat +zapit.scantype.tvradio TV- ja radiokanavat +zaptotimer.announce Kanava vaihtuu ajastetulle kanavalle minuutin kuluttua diff --git a/data/locale/svenska.locale b/data/locale/svenska.locale new file mode 100644 index 000000000..99883ea33 --- /dev/null +++ b/data/locale/svenska.locale @@ -0,0 +1,954 @@ +AUDIOSelectMenue.head Ljudval +EPGMenu.epgplus Händelsevy +EPGMenu.eventinfo Detaljer om nuvarande program +EPGMenu.eventlist Händelsevy nuvarande program +EPGMenu.head EPG - Program Information +EPGMenu.streaminfo Teknisk information +EPGPlus.actions Ã…tgärder +EPGPlus.bybouquet_mode av bouquet +EPGPlus.bypage_mode av sida +EPGPlus.change_font_size Byt textstorlek +EPGPlus.change_font_style Byt textutseende +EPGPlus.change_size Byt storlek +EPGPlus.channelentry_font Kanaler +EPGPlus.channelentry_separationlineheight höjden av linje mellan poster +EPGPlus.channelentry_width längd av kanalnamn +EPGPlus.channelevententry_font Program +EPGPlus.edit_fonts Edit text +EPGPlus.edit_sizes Edit storlekar +EPGPlus.font_style_bold fet +EPGPlus.font_style_italic kursiv +EPGPlus.font_style_regular vanlig +EPGPlus.footer_fontbouquetchannelname Valt bouquet-/kanalnamn +EPGPlus.footer_fontbuttons Knappar +EPGPlus.footer_fonteventdescription Title 1 valt Prog. +EPGPlus.footer_fonteventshortdescription Title 2 valt Prog. +EPGPlus.head Händelselistvy (EPG Plus) +EPGPlus.header_font Huvud +EPGPlus.horgap1_height höjd hor. gap 1 +EPGPlus.horgap2_height höjd hor. gap 2 +EPGPlus.next_bouquet Nästa bouquet +EPGPlus.options Optioner +EPGPlus.page_down Sida ner +EPGPlus.page_up Sida upp +EPGPlus.prev_bouquet FöregÃ¥ende bouquet +EPGPlus.record Spela in +EPGPlus.refresh_epg Uppdatera +EPGPlus.remind Schema +EPGPlus.reset_settings Ã…terställ inställningar +EPGPlus.save_settings Spara inställningar +EPGPlus.scroll_mode Scroll läge +EPGPlus.select_font_name Textval +EPGPlus.settings Inställningar +EPGPlus.slider_width rullist bredd +EPGPlus.stretch_mode Utdragningsläge +EPGPlus.swap_mode hoppa-över läge +EPGPlus.timeline_fontdate Tidsskala Datum +EPGPlus.timeline_fonttime Tidsskala Tid +EPGPlus.vergap1_width längd ver. gap 1 +EPGPlus.vergap2_width längd ver. gap 2 +EPGPlus.view_mode Läge +GENRE.ARTS.0 konst / kultur +GENRE.ARTS.1 konstnärlig uppvisning +GENRE.ARTS.10 konst/kultur magasin +GENRE.ARTS.11 mode +GENRE.ARTS.2 fin konst +GENRE.ARTS.3 religion +GENRE.ARTS.4 populär kultur/traditionell konst +GENRE.ARTS.5 litteratur +GENRE.ARTS.6 film/bio +GENRE.ARTS.7 experimentell film/video +GENRE.ARTS.8 broadcasting/press +GENRE.ARTS.9 ny media +GENRE.CHILDRENs_PROGRAMMES.0 barn / ungdomsprogram +GENRE.CHILDRENs_PROGRAMMES.1 förskola barnprogram +GENRE.CHILDRENs_PROGRAMMES.2 nöjesprogram frÃ¥n 6 till 14 +GENRE.CHILDRENs_PROGRAMMES.3 nöjesprogram för 10 till 16 +GENRE.CHILDRENs_PROGRAMMES.4 information/utbildning/skolprogram +GENRE.CHILDRENs_PROGRAMMES.5 tecknad film/dockteater +GENRE.DOCUS_MAGAZINES.0 dokumentär / magasin +GENRE.DOCUS_MAGAZINES.1 natur/djur/miljö +GENRE.DOCUS_MAGAZINES.2 teknologi/naturkunskap +GENRE.DOCUS_MAGAZINES.3 medicin/psykologi +GENRE.DOCUS_MAGAZINES.4 utrikes/expeditioner +GENRE.DOCUS_MAGAZINES.5 social/andlig +GENRE.DOCUS_MAGAZINES.6 vidareutbildning +GENRE.DOCUS_MAGAZINES.7 sprÃ¥k +GENRE.MOVIE.0 film/drama +GENRE.MOVIE.1 detektiv/thriller +GENRE.MOVIE.2 äventyr/western/krig +GENRE.MOVIE.3 science fiction/fantasi/skräck +GENRE.MOVIE.4 komedi +GENRE.MOVIE.5 sÃ¥pa/melodrama/folkhistoria +GENRE.MOVIE.6 romans +GENRE.MOVIE.7 seriöst/klassisk/religiös/historisk film/drama +GENRE.MOVIE.8 vuxen film/drama +GENRE.MUSIC_DANCE.0 musik / balett / dans +GENRE.MUSIC_DANCE.1 rock/pop +GENRE.MUSIC_DANCE.2 seriös music/klassisk musik +GENRE.MUSIC_DANCE.3 folk/traditionell musik +GENRE.MUSIC_DANCE.4 jazz +GENRE.MUSIC_DANCE.5 musikal/opera +GENRE.MUSIC_DANCE.6 balett +GENRE.NEWS.0 nyheter +GENRE.NEWS.1 nyheter/väderrapport +GENRE.NEWS.2 nyhetsmagasin +GENRE.NEWS.3 dokumentär +GENRE.NEWS.4 diskussion/intervju/debatt +GENRE.SHOW.0 Show / Gameshow +GENRE.SHOW.1 game show/frÃ¥gesport/tävling +GENRE.SHOW.2 variteshow +GENRE.SHOW.3 pratshow +GENRE.SOCIAL_POLITICAL.0 social & politiska händelser / affärer +GENRE.SOCIAL_POLITICAL.1 magasin/rapporter/dokumentärer +GENRE.SOCIAL_POLITICAL.2 ekonomi/sociala frÃ¥gor +GENRE.SOCIAL_POLITICAL.3 enastÃ¥ende människor +GENRE.SPORTS.0 sport +GENRE.SPORTS.1 special evenemang (Olympiska Spelen, Världsmästerskap etc.) +GENRE.SPORTS.10 hästsport +GENRE.SPORTS.11 kampsport +GENRE.SPORTS.2 sport magasin +GENRE.SPORTS.3 football/fotboll +GENRE.SPORTS.4 tennis/squash +GENRE.SPORTS.5 lagsporter (exklusive fotboll) +GENRE.SPORTS.6 friidrott +GENRE.SPORTS.7 motorsport +GENRE.SPORTS.8 vattensport +GENRE.SPORTS.9 vintersport +GENRE.TRAVEL_HOBBIES.0 resor & rekreation +GENRE.TRAVEL_HOBBIES.1 turism/resor +GENRE.TRAVEL_HOBBIES.2 hantverk +GENRE.TRAVEL_HOBBIES.3 motorer +GENRE.TRAVEL_HOBBIES.4 fitness & hälsa +GENRE.TRAVEL_HOBBIES.5 matlagning +GENRE.TRAVEL_HOBBIES.6 reklam/shopping +GENRE.TRAVEL_HOBBIES.7 trädgÃ¥rd +GENRE.UNKNOWN unknown +apids.hint_1 Ange APIDs som ska streamas +apids.hint_2 i hexadec separerade med ' ' +apidselector.head Välj sprÃ¥k +audiomenu.PCMOffset Sänk volymen (PCM) +audiomenu.analogout Analog utgÃ¥ng +audiomenu.audio_left_right_selectable väljbar i Ljudmenu +audiomenu.audiochannel_up_down_enable Ljudval med höger/vänster +audiomenu.avs avs +audiomenu.avs_control Volymkontroll +audiomenu.dolbydigital Dolby Digital som default +audiomenu.head Ljudinställningar +audiomenu.lirc lirc +audiomenu.monoleft mono vänster +audiomenu.monoright mono höger +audiomenu.ost ost +audiomenu.stereo stereo +audioplayer.add Lägg till +audioplayer.artist_title Artist, Titel +audioplayer.building_search_index bygger sökindex +audioplayer.button_select_title_by_id sök efter ID +audioplayer.button_select_title_by_name sök efter namn +audioplayer.defdir start mapp +audioplayer.delete Radera +audioplayer.deleteall radera alla +audioplayer.display_order visa efter +audioplayer.enable_sc_metadata aktivera shoutcast meta data läsning +audioplayer.fastforward snabbt framÃ¥t +audioplayer.follow auto välj nuvarande +audioplayer.head Ljud spellista +audioplayer.highprio Hög dekodningsprioritet +audioplayer.id3scan Söker ID3 taggar +audioplayer.jump_backwards hoppa bakÃ¥t +audioplayer.jump_dialog_hint1 Ange destinationsmÃ¥l +audioplayer.jump_dialog_hint2 (relativt, i sekunder) +audioplayer.jump_dialog_title Ange destinationsmÃ¥l +audioplayer.jump_forwards hoppa framÃ¥t +audioplayer.keylevel nyckelnivÃ¥ +audioplayer.name Ljudspelare +audioplayer.pause pause +audioplayer.play Spela +audioplayer.playing Nuvarande spÃ¥r +audioplayer.playlist_fileerror_msg Filen kunde inte skapas: +audioplayer.playlist_fileerror_title Fel +audioplayer.playlist_fileoverwrite_msg Vill du skriva över denna fil: +audioplayer.playlist_fileoverwrite_title Skriv över? +audioplayer.playlist_name filnamn pÃ¥ spellistan +audioplayer.playlist_name_hint1 Ange filnamnet pÃ¥ spellistan +audioplayer.playlist_name_hint2 Suffixet .m3u kommer att sättas automatiskt +audioplayer.reading_files läser meta data +audioplayer.repeat_on aktivera upprepningsläge +audioplayer.rewind spela bakÃ¥t +audioplayer.save_playlist spara spellistan +audioplayer.screensaver_timeout timeout skärmsparare (min, 0=av) +audioplayer.select_title_by_name sök titel via namn (SMS) +audioplayer.show_playlist Visa spellistan +audioplayer.shuffle kasta om +audioplayer.stop Stopp +audioplayer.title_artist Titel, Artist +audioplayerpicsettings.general musikspelare / bildvisare +bookmarkmanager.delete radera +bookmarkmanager.name bokmärken +bookmarkmanager.rename byt namn +bookmarkmanager.select välj +bouqueteditor.add Lägg till +bouqueteditor.bouquetname Namn pÃ¥ bouquets +bouqueteditor.delete Radera +bouqueteditor.discardingchanges Glömmer ändringarna. Var god vänta. +bouqueteditor.hide Göm +bouqueteditor.lock LÃ¥s +bouqueteditor.move Flytta +bouqueteditor.name ändra bouquet +bouqueteditor.newbouquetname Nytt namn pÃ¥ bouquets +bouqueteditor.rename Byt namn +bouqueteditor.return klar +bouqueteditor.savechanges? Vill du spara ändringarna? +bouqueteditor.savingchanges Sparar ändringar. Vänta ... +bouqueteditor.switch lägg till/ta bort +bouqueteditor.switchmode TV/Radio +bouquetlist.head Bouquets +cablesetup.provider kabel operatör +channellist.head Alla tjänster +channellist.nonefound Inga kanaler hittades!\nVänligen gör en sökning\n(dbox-knapp -> tjänster) +channellist.since sedan %02d:%02d +colorchooser.alpha alpha +colorchooser.blue blÃ¥ +colorchooser.green grön +colorchooser.red röd +colormenu.background Bakgrund +colormenu.background_head Bakgrund +colormenu.fade Tona ner menyer +colormenu.font Font storlek +colormenu.gtx_alpha Genomskinlighet (GTX) +colormenu.head Färginställningar +colormenu.menucolors Menyfärger +colormenu.statusbar Infobalk +colormenu.textcolor Textfärg +colormenu.textcolor_head Textfärg +colormenu.themeselect välj tema +colormenu.timing OSD Tajming +colormenusetup.head Menyfärger +colormenusetup.menucontent FönsterinnehÃ¥ll +colormenusetup.menucontent_inactive FönsterinnehÃ¥ll inaktivt +colormenusetup.menucontent_selected FönsterinnehÃ¥ll vald +colormenusetup.menuhead Menytopp +colorstatusbar.head Infobalk +colorstatusbar.text Infobalk +colorthememenu.classic_theme Klassiskt tema +colorthememenu.dblue_theme DarkBlue tema +colorthememenu.dvb2k_theme DVB2000 tema +colorthememenu.head Temaväljaren +colorthememenu.neutrino_theme Neutrino tema +date.Apr Apr +date.Aug Aug +date.Dec Dec +date.Feb Feb +date.Fri Fre +date.Jan Jan +date.Jul Jul +date.Jun Jun +date.Mar Mar +date.May Maj +date.Mon MÃ¥n +date.Nov Nov +date.Oct Okt +date.Sat Lör +date.Sep Sep +date.Sun Sön +date.Thu Tor +date.Tue Tis +date.Wed Ons +epgextended.actors SkÃ¥despelare +epgextended.director Regisör +epgextended.guests Gäster +epgextended.original_title Originaltitel +epgextended.presenter Presentatör +epgextended.year_of_production Producerad Ã¥r +epglist.head Händelselista - %s +epglist.noevents EPG är inte tillgänglig... +epgviewer.More_Screenings Fler visningar pÃ¥ denna kanal +epgviewer.nodetailed Ingen detaljerad information tillgänglig +epgviewer.notfound ingen EPG hittades +eventlistbar.channelswitch schema +eventlistbar.eventsort sortering +eventlistbar.recordevent spela in +experimentalsettings Experimentella inställningar +experimentalsettings.head Experimentella inställningar +favorites.addchannel Nuvarande kanal kommer att läggas\ntill i bouqueten "Mina favoriter".\nDetta tar nÃ¥gra sekunder... +favorites.bouquetname Mina favoriter +favorites.bqcreated Bouquet "Mina favoriter" har skapats...\n +favorites.chadded Nuvarande kanal har lagt till i dina favoriter...\n +favorites.chalreadyinbq Nuvarande kanal är redan i dina favoriter...\n +favorites.finalhint \nAnvänd bouqueteditorn för att ändra dina favoriter.\n +favorites.menueadd lägg till kanal till favoriterna +favorites.nobouquets Favoriter är endast tillgängliga med aktiverade Bouquets. +filebrowser.delete Radera +filebrowser.denydirectoryleave Absoluta startmappen +filebrowser.dodelete1 Radera +filebrowser.dodelete2 ? +filebrowser.filter.active Filter pÃ¥ +filebrowser.filter.inactive Filter av +filebrowser.head Filöversikt +filebrowser.mark Markera +filebrowser.nextpage Nästa sida +filebrowser.prevpage Före. sida +filebrowser.scan Söker mapp.. +filebrowser.select Välj +filebrowser.showrights Visa filrättigheter +filebrowser.sort.date (datum) +filebrowser.sort.name (filnamn) +filebrowser.sort.namedirsfirst (filnamn2) +filebrowser.sort.size (Storlek) +filebrowser.sort.type (typ) +filesystem.is.utf8 filsystem +filesystem.is.utf8.option.iso8859.1 ISO-8859-1 +filesystem.is.utf8.option.utf8 UTF-8 +flashupdate.actionreadflash läser +flashupdate.cantopenfile kan inte öppna fil +flashupdate.cantopenmtd kan inte öppna mtd-enhet +flashupdate.checkupdate söker efter ny version +flashupdate.currentreleasecycle Lanseringscykel +flashupdate.currentversion_sep Nuvarande version +flashupdate.currentversiondate Datum +flashupdate.currentversionsnapshot Bildtyp +flashupdate.currentversiontime Tid +flashupdate.erasefailed radering av flash misslyckades +flashupdate.erasing raderar flash +flashupdate.experimentalimage Den image du valt är en otestad version, detta betyder\natt din box kanske inte startar efter uppdateringen.\n\nVill du verkligen uppdatera till denna version? +flashupdate.expertfunctions Expertfunktioner +flashupdate.fileis0bytes filstorlek är 0 Bytes +flashupdate.fileselector Filväljare +flashupdate.flashreadyreboot Flashning av image lyckades.\nDin DBox kommer att startas om nu. +flashupdate.getinfofile hämtar versionsinfo +flashupdate.getinfofileerror kan inte hämta versionsinfo +flashupdate.getupdatefile hämtar uppdatering +flashupdate.getupdatefileerror kan inte hämta uppdatering +flashupdate.globalprogress Global progress: +flashupdate.head Uppdatering av mjukvara +flashupdate.md5check undersöker image +flashupdate.md5sumerror image innehÃ¥ller fel +flashupdate.msgbox Hittade följande ny image:\nDatum: %s, %s\nBasimage: %s\nImagetyp: %s\n\nVill du hämta och installera denna version nu? +flashupdate.msgbox_manual Hittade följande ny image:\nDatum: %s, %s\nBasimage: %s\nImagetyp: %s\n\nVill du installera denna version nu? +flashupdate.mtdselector Partitionsväljare +flashupdate.programmingflash programmerar flash +flashupdate.proxypassword Lösenord +flashupdate.proxypassword_hint1 ange lösenord för proxyserver +flashupdate.proxypassword_hint2 +flashupdate.proxyserver Server +flashupdate.proxyserver_hint1 ange proxyservers namn och IP, använd namn:port +flashupdate.proxyserver_hint2 lämna raden tom om du inte har en proxy +flashupdate.proxyserver_sep Proxyserver +flashupdate.proxyusername Användarnamn +flashupdate.proxyusername_hint1 ange användarnamn för proxyserver +flashupdate.proxyusername_hint2 lämna raden tom om du inte använder proxy-auth +flashupdate.readflash Läs hela imagen +flashupdate.readflashmtd Läs en partition +flashupdate.ready klar +flashupdate.reallyflashmtd Vill du verkligen flasha?\n\nOm ett fel inträffar eller om imagen är invalid\n kan boxen inte startas efter flashning.\n\Imagenamn: %s\nMÃ¥l: %s +flashupdate.savesuccess Imagen sparades under\n%s. +flashupdate.selectimage Tillgängliga images +flashupdate.squashfs.noversion Kontroll av SquashFS version är endast möjlig när uppdateringen görs via webben.\nÄr du säker att du vill installera denna image? +flashupdate.titlereadflash Läser Flash +flashupdate.titlewriteflash Skriver Flash +flashupdate.updatemode Uppdateringsläge +flashupdate.updatemode_internet Internet +flashupdate.updatemode_manual manuell (ftp) +flashupdate.url_file konfigfil +flashupdate.versioncheck undersöker version +flashupdate.writeflash Skriv hela imagen +flashupdate.writeflashmtd Skriv en partition +flashupdate.wrongbase Din lanseringscykel skiljer sig, kan inte uppdatera\nutan att ha den rätta versionen installerad! +fontmenu.channellist Kanallista +fontmenu.epg EPG +fontmenu.eventlist Händelslista +fontmenu.gamelist Spellista +fontmenu.head Inställning för fontstorlek +fontmenu.infobar Infobalk +fontsize.channel_num_zap direktval +fontsize.channellist Kanallista +fontsize.channellist_descr Beskrivning +fontsize.channellist_number Nummer +fontsize.epg_date EPG Datum +fontsize.epg_info1 EPG Info 1 +fontsize.epg_info2 EPG Info 2 +fontsize.epg_title EPG Titel +fontsize.eventlist_datetime datum / tid +fontsize.eventlist_itemlarge stor +fontsize.eventlist_itemsmall liten +fontsize.eventlist_title Titel +fontsize.filebrowser_item filväljare post +fontsize.gamelist_itemlarge stor +fontsize.gamelist_itemsmall liten +fontsize.hint Initialiserar font,\nvar god vänta... +fontsize.imageinfo_info bildinformation +fontsize.imageinfo_small bildinformation licens +fontsize.infobar_channame Kanalnamn +fontsize.infobar_info info +fontsize.infobar_number Nummer +fontsize.infobar_small liten +fontsize.menu Menytext +fontsize.menu_info Meny Info +fontsize.menu_title Meny Titel +gtxalpha.alpha1 Alpha 1 +gtxalpha.alpha2 Alpha 2 +imageinfo.creator Skapare: +imageinfo.date Datum: +imageinfo.dokumentation Dokumentation: +imageinfo.forum Forum: +imageinfo.head Bildinformation +imageinfo.homepage Hemsida: +imageinfo.image Bild: +imageinfo.license Licens: +imageinfo.version Version: +infoviewer.epgnotload EPG inte laddad.... +infoviewer.epgwait vänta pÃ¥ EPG... +infoviewer.eventlist Händelselista +infoviewer.languages Ljud +infoviewer.motor_moving Antennposition +infoviewer.nocurrent Ingen info för nuvarande program tillgänglig +infoviewer.noepg EPG inte tillgänglig +infoviewer.notavailable Kanal inte tillgänglig +infoviewer.selecttime Tidsval +infoviewer.streaminfo InnehÃ¥ller +infoviewer.subchan_disp_pos Underkanal visning +infoviewer.subservice Underkanaler +infoviewer.waittime Väntar pÃ¥ tid... +ipsetup.hint_1 Använd 0..9, eller använd Up/Ned, +ipsetup.hint_2 OK sparar, Home avbryter +keybindingmenu.RC Blockera knapprepetering +keybindingmenu.addrecord lägg till inspelningstimer +keybindingmenu.addrecord_head lägg till inspelningstimer +keybindingmenu.addremind lägg till zaptotimer +keybindingmenu.addremind_head lägg till zaptotimer +keybindingmenu.allchannels_on_ok alla-kanaler +keybindingmenu.bouquetchannels_on_ok nuvarande bouquet +keybindingmenu.bouquetdown bouquet tillbaka +keybindingmenu.bouquetdown_head knappkonfig bouquet tillbaka +keybindingmenu.bouquethandling Bouquetkontroll +keybindingmenu.bouquetlist_on_ok bouquetlista +keybindingmenu.bouquetup next bouquet +keybindingmenu.bouquetup_head knappkonfig bouquet framÃ¥t +keybindingmenu.cancel stäng kanallista +keybindingmenu.cancel_head stäng kanallista +keybindingmenu.channeldown kanal ned +keybindingmenu.channeldown_head Knappkonfig kanal ned +keybindingmenu.channellist Kanallista +keybindingmenu.channelup kanal upp +keybindingmenu.channelup_head Knappkonfig kanal upp +keybindingmenu.head Knappkonfiguration +keybindingmenu.lastchannel Snabbzap +keybindingmenu.lastchannel_head Snabbzap +keybindingmenu.modechange Ändra läge +keybindingmenu.pagedown sida ned +keybindingmenu.pagedown_head Knappkonfig sida ned +keybindingmenu.pageup sida upp +keybindingmenu.pageup_head Knappkonfig sida upp +keybindingmenu.quickzap Snabbzap +keybindingmenu.repeatblock upprepningsfördröjning +keybindingmenu.repeatblockgeneric allmän fördröjning +keybindingmenu.sort ändra sortering +keybindingmenu.sort_head ändra sortering +keybindingmenu.subchanneldown underkanal ned +keybindingmenu.subchanneldown_head knapp underkanal ned +keybindingmenu.subchannelup underkanal upp +keybindingmenu.subchannelup_head knapp underkanal upp +keybindingmenu.tvradiomode TV/Radio-läge +keybindingmenu.tvradiomode_head TV/Radio-läge +keybindingmenu.zaphistory Zappningshistoria Bouquet +keybindingmenu.zaphistory_head Zappningshistoria Bouquet +keychooser.head Ange ny nyckel +keychooser.text1 Vänligen tryck den nya knappen +keychooser.text2 vänta ett par sekunder för avbryt +keychoosermenu.currentkey nuvarande knapp +keychoosermenu.setnew ange ny knapp +keychoosermenu.setnone ingen knapp +languagesetup.head SprÃ¥kinställningar +languagesetup.select SprÃ¥k +lcdcontroler.brightness normal ljusstyrka +lcdcontroler.brightnessstandby Ljusstyrka viloläge +lcdcontroler.contrast Kontrast +lcdcontroler.head LCD Inställning +lcdmenu.autodimm Auto dim +lcdmenu.dim_brightness Ljusstyrka efter dim timeout +lcdmenu.dim_time Dim timeout +lcdmenu.head LCD Inställning +lcdmenu.inverse Invertera +lcdmenu.lcdcontroler Kontrast / Ljusstyrka +lcdmenu.power Ström +lcdmenu.statusline statuslinje +lcdmenu.statusline.both volym / speltid +lcdmenu.statusline.playtime speltid +lcdmenu.statusline.volume volym +mainmenu.audioplayer Ljudspelare +mainmenu.games Spel +mainmenu.head Huvudmeny +mainmenu.movieplayer Filmspelare +mainmenu.pausesectionsd Läs EPG +mainmenu.pictureviewer Bildvisare +mainmenu.radiomode Radioläge +mainmenu.reboot Nystart +mainmenu.recording Inspelning +mainmenu.recording_start start +mainmenu.recording_stop stopp +mainmenu.scartmode Scart-läge +mainmenu.scripts Skript +mainmenu.service Service +mainmenu.settings Inställningar +mainmenu.shutdown Avstängning +mainmenu.sleeptimer Sovtimer +mainmenu.tvmode TV-läge +mainsettings.audio Ljud +mainsettings.colors Färger / teman / font +mainsettings.head Inställningar +mainsettings.keybinding Knappkonfiguration +mainsettings.language SprÃ¥k +mainsettings.lcd LCD-skärm +mainsettings.misc Extra inställningar +mainsettings.network Nätverk +mainsettings.recording Inspelning +mainsettings.savesettingsnow spara inställningar nu +mainsettings.savesettingsnow_hint Sparar inställningar,\nvänta... +mainsettings.streaming Filmspelare +mainsettings.video Video +menu.back tillbaka +messagebox.back tillbaka +messagebox.cancel Avbryt +messagebox.discard Kasta ändringar? +messagebox.error Fel +messagebox.info Information +messagebox.no Nej +messagebox.yes Ja +miscsettings.bootinfo Visa info vid start +miscsettings.bootmenu Visa bootmeny +miscsettings.driver_boot drivare och boot optioner +miscsettings.fb_destination Expert! Bootkonsol +miscsettings.general Generellt +miscsettings.head Extra inställningar +miscsettings.hwsections använd hÃ¥rdvarusektioner +miscsettings.infobar_sat_display Satellitinfo pÃ¥ Infobalken +miscsettings.noaviawatchdog aktivera AVIA watchdog +miscsettings.noenxwatchdog aktivera eNX watchdog +miscsettings.pmtupdate aktivera pmt uppdatering +miscsettings.shutdown_count stäng av efter +miscsettings.shutdown_count_hint1 tid (i minuter) för att växla frÃ¥n viloläge +miscsettings.shutdown_count_hint2 till djupt viloläge (0 = av). +miscsettings.shutdown_real Aktivera vilovälge +miscsettings.shutdown_real_rcdelay Fördröjd avstängning +miscsettings.sptsmode använd spts läge +miscsettings.startbhdriver ladda BH-Mode drivare +miscsettings.tuxtxt_cache mellanlagring av TextTV-sidor +motorcontrol.head Motorinstallation +movieplayer.bookmark Bokmärken +movieplayer.bookmarkname Bokmärkesnamn +movieplayer.bookmarkname_hint1 Ange ett namn för ditt nya bokmärke +movieplayer.bookmarkname_hint2 +movieplayer.buffering Buffrar... +movieplayer.defdir start mapp +movieplayer.defplugin Startplugin +movieplayer.dvdplayback DVD +movieplayer.fileplayback Fil via VLC +movieplayer.goto Hoppa till ... +movieplayer.goto.h1 = -> absolut hopp +movieplayer.goto.h2 +,- -> relativt hopp +movieplayer.head Filmspelare +movieplayer.nostreamingserver Streamingservern kan inte nÃ¥s. +movieplayer.pleasewait Var god vänta.\nKopplar upp mot streamingservern... +movieplayer.toomanybookmarks Det finns för mÃ¥nga bokmärken.\nDu mÃ¥ste ta bort en av dom först. +movieplayer.tshelp1 Stopp +movieplayer.tshelp10 ungefär 10 min tillbaks +movieplayer.tshelp11 hoppa ungefär 10 min +movieplayer.tshelp12 Hjälp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.tshelp2 Välj ljudspÃ¥r +movieplayer.tshelp3 Pause/Fortsätt +movieplayer.tshelp4 Skapa bokmärke +movieplayer.tshelp5 Visa progress +movieplayer.tshelp6 ungefär 1 min tillbaks +movieplayer.tshelp7 hoppa ungefär 1 min +movieplayer.tshelp8 ungefär 5 min tillbaks +movieplayer.tshelp9 hoppa ungefär 5 min +movieplayer.tsplayback Spela TS +movieplayer.tsplayback_pc Spela TS med PIN-lÃ¥s +movieplayer.vcdplayback (S)VCD +movieplayer.vlchelp1 Stopp +movieplayer.vlchelp10 ungefär 10 min tillbaks +movieplayer.vlchelp11 hoppa ungefär 10 min +movieplayer.vlchelp12 Hjälp: http://www.giggo.de/dbox2/movieplayer.html\n +movieplayer.vlchelp2 Resync +movieplayer.vlchelp3 Pause/Fortsätt +movieplayer.vlchelp4 Skapa bokmärke +movieplayer.vlchelp5 Visa progress +movieplayer.vlchelp6 ungefär 1 min tillbaks +movieplayer.vlchelp7 hoppa ungefär 1 min +movieplayer.vlchelp8 ungefär 5 min tillbaks +movieplayer.vlchelp9 hoppa ungefär 5 min +movieplayer.wrongvlcversion Denna funktion finns inte i den nuvarande versionen av VLC +networkmenu.broadcast Broadcast +networkmenu.dhcp DHCP +networkmenu.gateway Default Gateway +networkmenu.head Nätverksinställningar +networkmenu.ipaddress IP-address +networkmenu.mount NFS/CIFS/FTPFS +networkmenu.nameserver DNS server +networkmenu.netmask Subnetmask +networkmenu.setupnow Start/Ã…terstarta nätverk nu +networkmenu.setuponstartup Starta nätverk vid uppstart +networkmenu.show Visa aktiva nätverksinställningar +networkmenu.test Testa nätverket nu +nfs.alreadymounted Mapp redan monterad +nfs.automount Montera vid uppstart +nfs.dir Mapp/Utdelning +nfs.ip Server IP +nfs.localdir Lokal mapp +nfs.mount Montera nätverksvolym +nfs.mount_options Monteringsoptioner +nfs.mounterror Monteringsfel +nfs.mounterror_notsup Filsystemstypen stöds inte +nfs.mountnow Montera nu +nfs.mountok Montering lyckades +nfs.mounttimeout Monteringsfel: timeout +nfs.password Lösenord +nfs.remount Ã…termontera mappar +nfs.type Typ +nfs.type_cifs CIFS +nfs.type_lufs FTPFS +nfs.type_nfs NFS +nfs.umount Avmontera nätverksvolym +nfs.umounterror Fel vid avmontering +nfs.username Användare +nfsmenu.head NFS/CIFS/FTPFS inställning +nvod.percentage (%d%% över) +nvod.starting (startar om %d min) +nvodselector.directormode Direktläge +nvodselector.head Välj starttid +nvodselector.subservice Välj underprogram +options.default Ã…terställ inställningar +options.fb framebuffer +options.null noll +options.off av +options.on pÃ¥ +options.on.without_messages (tyst) pÃ¥ +options.serial seriell +parentallock.changepin ändra PIN-kod +parentallock.changepin_hint1 Ange PIN-kod för förädralÃ¥set! +parentallock.changetolocked är lÃ¥sta bouquets +parentallock.head Ange PIN-kod för förädralÃ¥s +parentallock.lockage lÃ¥s kanal +parentallock.lockage12 frÃ¥n 12 Ã¥r uppÃ¥t +parentallock.lockage16 frÃ¥n 16 Ã¥r uppÃ¥t +parentallock.lockage18 frÃ¥n 18 Ã¥r uppÃ¥t +parentallock.lockedchannel LÃ¥st sändare... +parentallock.lockedprogram LÃ¥st program (frÃ¥n %d Ã¥r och uppÃ¥t) +parentallock.never aldrig +parentallock.onsignal pÃ¥ broadcastat lÃ¥s +parentallock.parentallock FöräldralÃ¥s +parentallock.prompt frÃ¥ga efter PIN-kod +pictureviewer.decode_server_ip dekoda server ip +pictureviewer.decode_server_port dekoda server port +pictureviewer.defdir start mapp +pictureviewer.head Bildvisare +pictureviewer.help1 menyläge +pictureviewer.help10 visningsläge +pictureviewer.help11 läs in bild igen +pictureviewer.help12 föregÃ¥ende bild +pictureviewer.help13 nästa bild +pictureviewer.help14 Zoom ut +pictureviewer.help15 Zoom in +pictureviewer.help16 skrolla upp +pictureviewer.help17 skrolla vänster +pictureviewer.help18 skrolla höger +pictureviewer.help19 skrolla ned +pictureviewer.help2 visa bild +pictureviewer.help20 ändra sortering +pictureviewer.help21 läs in bild igen (ingen skalning) +pictureviewer.help22 avsluta +pictureviewer.help3 ändra sortering +pictureviewer.help4 skala inte bild +pictureviewer.help5 diavisningläge +pictureviewer.help6 föregÃ¥ende bild +pictureviewer.help7 nästa bild +pictureviewer.help8 ändra sortering +pictureviewer.help9 avsluta +pictureviewer.resize.color_average avancerad +pictureviewer.resize.none ingen +pictureviewer.resize.simple enkel +pictureviewer.scaling skalning +pictureviewer.show visa +pictureviewer.slide_time slideshow visningstid +pictureviewer.slideshow slideshow +pictureviewer.sortorder ändra sortering +pictureviewer.sortorder.date (datum) +pictureviewer.sortorder.filename (filnamn) +ping.ok är nÃ¥bar (ping) +ping.protocol är inte nÃ¥bar (host eller protokollfel) +ping.socket är inte nÃ¥bar (socketfel) +ping.unreachable är inte nÃ¥bar +pinprotection.head Ange PIN-kod +pinprotection.wrongcode Felaktig PIN-kod! Försök igen. +plugins.result plugin utskrift +rclock.lockmsg Din DBOX2 fjärrkontroll kommer att lÃ¥sas.\nFör att lÃ¥sa upp den, tryck \noch pÃ¥ din fjärrkontroll. +rclock.menueadd LÃ¥s FK +rclock.title LÃ¥s fjärrkontroll +rclock.unlockmsg Fjärrkontroll aktiverad. +recordingmenu.apids Förinställning för tonspÃ¥r +recordingmenu.apids_ac3 Spela in AC3 tonspÃ¥r +recordingmenu.apids_alt Spela in alternativt tonspÃ¥r +recordingmenu.apids_std Spela in standard tonspÃ¥r +recordingmenu.choose_direct_rec_dir välj mapp vid direkt inspel. +recordingmenu.defdir inspelningsmapp +recordingmenu.dir_permissions Mapprättigheter +recordingmenu.dir_permissions_hint Rättigheter i 'chmod'-stil +recordingmenu.epg_for_filename lÃ¥nga filnamn (med EPG data) +recordingmenu.file direkt (fil) +recordingmenu.filename_template Schablon för filnamn +recordingmenu.filename_template_hint %c=Kanal, %i=Titel, %d=Datum, %t=Tid +recordingmenu.filesettings direktinspelning inställning +recordingmenu.head Inspelningsinställningar +recordingmenu.help Inspelningsenheter:\n--------------------------\nserver:\nmed en streaming programvara pÃ¥ en PC\n\n(analog) vcr:\nmed ett VCR uttag\n\ndirekt (fil):\ndirekt till en NFS-monterad mapp\neller till en intern hÃ¥rddisk\nTS: använd spts-läge\nPES: använd inte spts-läget\n\n\nMax. filstorlek:\n---------------------\nNFS V2: 2 GB (2048 MB)\nNFS V3: sÃ¥ stor hÃ¥rddisken är (0 MB)\nFAT: 2 GB (2048 MB)\nFAT32: 4 GB (4096 MB) +recordingmenu.no_scart växla inte till SCART-läge +recordingmenu.off av +recordingmenu.record_in_spts_mode växla till spts-läge före inspelning +recordingmenu.recording_type inspelningsenhet +recordingmenu.ringbuffers antal ringbuffrar +recordingmenu.server server +recordingmenu.server_ip inspelningsserver IP +recordingmenu.server_mac MAC address +recordingmenu.server_port inspelningsserver port +recordingmenu.server_wakeup inspelningsserver WoL +recordingmenu.setupnow aktivera ändringar +recordingmenu.splitsize Max. filstorlek (MB) +recordingmenu.stopplayback stoppa avspelning +recordingmenu.stopsectionsd stoppa sectionsd +recordingmenu.stream_pmt_pid spela in PMT +recordingmenu.stream_vtxt_pid spela in TextTV +recordingmenu.use_fdatasync skriv synkront (fdatasync) +recordingmenu.use_o_sync skriv synkront (O_SYNC) +recordingmenu.vcr vcr +recordingmenu.zap_on_announce Slä om vid meddelande +recordtimer.announce Inspelning startar om nÃ¥gra minuter +repeatblocker.hint_1 Kortast tid (i ms) att registrera 2 knapptryck +repeatblocker.hint_2 Tryck 0 för att stänga av blockeraren (röd är mellanslag) +satsetup.diseqc DiSEqC +satsetup.diseqc10 DiSEqC 1.0 +satsetup.diseqc11 DiSEqC 1.1 +satsetup.diseqc12 DiSEqC 1.2 +satsetup.diseqcrepeat DiSEqC-upprepningar +satsetup.extended DiSEqC-inställningar +satsetup.extended_motor Motorinställningar +satsetup.minidiseqc Mini-DiSEqC +satsetup.motorcontrol Manuell Motorinställning +satsetup.nodiseqc ingen DiSEqC +satsetup.satellite Satellit +satsetup.savesettingsnow spara inställningar nu +satsetup.smatvremote SMATV Fjärrtuning +scantp.fec FEC +scantp.fec_1_2 1/2 +scantp.fec_2_3 2/3 +scantp.fec_3_4 3/4 +scantp.fec_5_6 5/6 +scantp.fec_7_8 7/8 +scantp.freq Frekvens +scantp.pol Polarisation +scantp.pol_h H +scantp.pol_v V +scantp.rate Symbolhastighet +scantp.scan Manuell sökning +scantp.scanmode Snabbsökning +scants.abort_body Ska sökningen verkligen avbrytas? +scants.abort_header Avbrytning av kanalsök +scants.actcable Kabel: +scants.actsatellite Satellit: +scants.bouquet Bouquet +scants.bouquet_create skapa ny +scants.bouquet_erase radera alla +scants.bouquet_leave lämna nuvarande +scants.bouquet_satellite Satellit-Bouquet +scants.bouquet_update uppdatera +scants.channel Kanal: +scants.failed Transpondersökning misslyckades! +scants.finished Transpondersökning genomförd! +scants.freqdata Frekvens: +scants.head Sök transponder +scants.numberofdataservices Data +scants.numberofradioservices Radio +scants.numberoftotalservices Total +scants.numberoftvservices TV +scants.provider Operatör: +scants.startnow starta sökning +scants.transponders Transpondrar: +screensetup.lowerright grön = inställning nedre höger +screensetup.upperleft röd = inställning övre vänster +sectionsd.scanmode sektionsscan +servicemenu.getplugins Läs in plugins +servicemenu.getplugins_hint Läser in plugins,\nvar god vänta. +servicemenu.head Service +servicemenu.imageinfo Bildinformation +servicemenu.reload Läs in kanallistor +servicemenu.reload_hint Läser in kanallistor,\nvar god vänta. +servicemenu.restart Starta om Neutrino +servicemenu.restart_failed Fel vid omstart av Neutrino. +servicemenu.restart_hint Startar om Neutrino... +servicemenu.restart_refused_recording Inspelning görs. Omstart inte möjlig. +servicemenu.scants Programsökning +servicemenu.ucodecheck Kontrollera ucodes +servicemenu.update Mjukvaruuppdatering +settings.help Hjälp +settings.missingoptionsconffile Neutrinoinställningarna har uppdaterats.\nNya inställningar kommer att sättas till default. +settings.noconffile Inga Neutrino-inställningar hittades.\nAnvänder default. +settings.pos_bottom_left vänster botten +settings.pos_bottom_right höger botten +settings.pos_top_left vänster topp +settings.pos_top_right höger topp +shutdown.recoding_query dBox spelar in just nu. Verkligen stänga av? +shutdowntimer.announce Box kommer att stängas av om 1 min.\nAvbryt avstängning? +sleeptimerbox.announce Sovtimer om 1 min +sleeptimerbox.hint1 Avstängningstid i min. (000=av) +sleeptimerbox.hint2 Din dbox2 kommer att stängas av efter denna tid. +sleeptimerbox.title Sovtimer +streamfeatures.head Funktioner: +streaminfo.aratio TV-format +streaminfo.aratio_unknown TV-format: okänt +streaminfo.audiotype Ljudtyp +streaminfo.audiotype_unknown Ljudtyp: okänt +streaminfo.bitrate Bitrate +streaminfo.framerate Framerate +streaminfo.framerate_unknown Framerate: okänt +streaminfo.head Stream-Information +streaminfo.not_available inte tillgänglig +streaminfo.resolution Upplösning +streaminfo.signal mottagningssignal +streaming.buffer_overflow Inspelning avbröts eftersom\ndata inte kunde skrivas tillräckligt snabbt. +streaming.busy En eller flera inspelningsprocesser är aktiva.\nOm du ser detta meddelande och inga inspelningar är aktiva, starta om Neutrino. +streaming.dir_not_writable Inspelningsmappen är inte skrivbar.\nInspelningar kommer inte att fungera. +streaming.success Inspelningen gick bra. +streaming.write_error Inspelningen avbröts,\neftersom ett fel inträffade under skrivning. +streaming.write_error_open Inspelningen avbröts,\nför att mÃ¥lfilen inte kunde öppnas. +streamingmenu.352x288 352x288 +streamingmenu.352x576 352x576 +streamingmenu.480x576 480x576 +streamingmenu.704x576 704x576 +streamingmenu.head Filmspelare inställningar +streamingmenu.mpeg1 MPEG1 +streamingmenu.mpeg2 MPEG2 +streamingmenu.off Av +streamingmenu.on PÃ¥ +streamingmenu.server_ip Streamingserver IP +streamingmenu.server_port Streamingserver Port +streamingmenu.streaming_audiorate Datarate Ljud +streamingmenu.streaming_force_avi_rawaudio Tvinga AC3 för AVI +streamingmenu.streaming_force_transcode_video Transcode MPG/VCD video +streamingmenu.streaming_resolution Upplösning +streamingmenu.streaming_server_cddrive DVD-enhet +streamingmenu.streaming_server_startdir Mapp (VLC) +streamingmenu.streaming_transcode_audio Transcode audio (DVD/VCD/MPG) +streamingmenu.streaming_transcode_video_codec MPEG video codec +streamingmenu.streaming_type Streamingserver +streamingmenu.streaming_videorate Datarate Video +streamingserver.noconnect Ingen anslutning till streamingserver.\nInspelning avbruten. +stringinput.caps caps / inga caps +stringinput.clear rensa alla +timer.eventrecord.msg ... FÖR att GÖRAS, eller inte göras +timer.eventrecord.title Schemalägg inspelning +timer.eventtimed.msg Händelsen är schemalagd.\nDin dbox2 kommer att startas och\nbyta till denna kanal pÃ¥ angiven tid. +timer.eventtimed.title Schemalägg händelse +timerbar.channelswitch Schema +timerbar.recordevent Spela in +timerlist.alarmtime Alarmtid +timerlist.apids Ljud PIDs +timerlist.apids_dflt spela in förinstÖllt tonspÃ¥r +timerlist.bouquetselect välj bouquet +timerlist.channel Kanal +timerlist.channelselect välj kanal +timerlist.delete Radera +timerlist.menumodify Ändra timer +timerlist.menunew Ny timer +timerlist.message Meddelande +timerlist.moderadio Radiokanaler +timerlist.modeselect Lägesval +timerlist.modetv TV kanaler +timerlist.modify Modifiera +timerlist.name Timerlista +timerlist.new Ny timer +timerlist.overlapping_timer Timer konflikt. Skapa timern ändÃ¥? +timerlist.plugin Plugin +timerlist.program.unknown Program okänt +timerlist.recording_dir inspelningsmapp +timerlist.reload Läs om +timerlist.repeat Upprepa +timerlist.repeat.biweekly veckomässigt +timerlist.repeat.byeventdescription se timer +timerlist.repeat.daily dagligen +timerlist.repeat.fourweekly var fjärde vecka +timerlist.repeat.friday Fr +timerlist.repeat.monday MÃ¥ +timerlist.repeat.monthly mÃ¥ntlig +timerlist.repeat.once en gÃ¥ng +timerlist.repeat.saturday Lö +timerlist.repeat.sunday Sö +timerlist.repeat.thursday To +timerlist.repeat.tuesday Ti +timerlist.repeat.unknown okänd +timerlist.repeat.wednesday On +timerlist.repeat.weekdays pÃ¥ vardagar +timerlist.repeat.weekly veckomässigt +timerlist.repeatcount upprepningar +timerlist.repeatcount.help1 antal timerupprepningar +timerlist.repeatcount.help2 0 för oändliga upprepningar +timerlist.save Spara timer +timerlist.standby SB läge +timerlist.standby.off Lämna viloläge +timerlist.standby.on GÃ¥ in i viloläge +timerlist.stoptime Stopptid +timerlist.type Timertyp +timerlist.type.execplugin Starta plugin +timerlist.type.nextprogram Nästa program +timerlist.type.record Spela in +timerlist.type.remind PÃ¥minnelse +timerlist.type.shutdown Stäng av +timerlist.type.sleeptimer Sovtimer +timerlist.type.standby Viloläge +timerlist.type.unknown Okänt +timerlist.type.zapto Zappa till +timerlist.weekdays Dagar i veckan +timerlist.weekdays.hint_1 MÃ¥ Ti On To Fr Lö Sö +timerlist.weekdays.hint_2 'X'=timer '-' ingen timer +timersettings.record_safety_time_after Justera stopptid för inspelning +timersettings.record_safety_time_after.hint_1 Justeringstid i min. (00=av). Denna tid +timersettings.record_safety_time_after.hint_2 kommer att läggas pÃ¥ stopptiden för varje inspelningstimer. +timersettings.record_safety_time_before Justering av starttid för inspelning +timersettings.record_safety_time_before.hint_1 Justeringstid i min. (00=av). Denna tid +timersettings.record_safety_time_before.hint_2 kommer att dras av för varje inspelningstimer. +timersettings.separator Timerinställningar +timing.chanlist Kanallista +timing.epg Epg +timing.filebrowser Filväljaren +timing.head OSD Timeouter +timing.hint_1 Tid i sek. Efter denna tid kommer +timing.hint_2 Infobalken att försvinna sakta. +timing.infobar Infobalk +timing.infobar_radio Infobalk/Radio +timing.menu Meny +timing.numericzap Numerisk Zap +ucodecheck.avia500 Avia 500 +ucodecheck.avia600 Avia 600 +ucodecheck.cam-alpha Cam-Alpha +ucodecheck.head UCode kontroll +ucodecheck.ucode UCode +ucodes.failure VARNING, µkoder kunde INTE hittas!\n\nVänligen ladda upp dom via FTP (eller Dbox-BootManager),\nstarta sedan om din DBox! +videomenu.csync sync justering +videomenu.head Videoinställningar +videomenu.rgb_centering RGB centrering +videomenu.screensetup Skärminställning +videomenu.vcrswitch Växla SCART automatiskt +videomenu.videoformat Format +videomenu.videoformat_169 16:9 +videomenu.videoformat_43 4:3 (LB) +videomenu.videoformat_431 4:3 (PS) +videomenu.videoformat_autodetect autodetektera +videomenu.videosignal VideoutgÃ¥ng +videomenu.videosignal_composite CVBS +videomenu.videosignal_rgb RGB + CVBS +videomenu.videosignal_svideo S-Video +videomenu.videosignal_yuv_c YUV + CVBS +videomenu.videosignal_yuv_v YUV + VBS +zapit.scantype sök efter program +zapit.scantype.all alla program +zapit.scantype.radio bara radio +zapit.scantype.tv bara tv +zapit.scantype.tvradio tv & radio +zaptotimer.announce Zaptotimer om en minut diff --git a/doc/ir.html b/doc/ir.html new file mode 100644 index 000000000..bf986f3e2 --- /dev/null +++ b/doc/ir.html @@ -0,0 +1,74 @@ + + + + Neutrino Timer Doku + + + +
    +

    Neutrino LIRC Unterstützung

    +
    +Konfiguration des lirc siehe +
    +http://www.dbox2.info/doku/lirc/lirc_doku.html

    +Syntax der Lirc-Kommando Dateien:
    +Es können in einer Kommando-Datei beliebig viele Befehle der Form
    +

    <device> <command> (<duration>)
    +vorkommen.

    +<device> = Name der Fernbedienung in der lirc.conf Datei
    +<command> = Name der Taste einer Fernbedienung in lirc.conf
    +<duration> = Dauer, die das Signal gesendet werden soll, in ms (optional). +Wenn nicht angegeben wird das Signal genau ein mal gesendet. +Werte zwischen 50ms und 500ms sind ein guter Anfang
    +

    +Folgende Neutrino-Aktionen können zusätzlich zum VCR Support mit IR-Signalen +versehen werden:
    +(ist die entsprechende Kommando-Datei vorhanden, werden die Kommandos gesendet, +existiert die Datei nicht, passiert nichts.) +

    +Sleeptimer:
    +Wenn der Sleeptimer die Box in den Standby bzw. Deep-Standby schickt, +werden die Lirc-Kommandos in der Datei sleep.lirc ausgeführt. +Dies dient zur Abschaltung des TVs / Verstärkers/...
    +Bsp. /var/tuxbox/config/lirc/sleep.lirc: +

    +tv power 500
    +amplifier power 500
    +
    +Standby on/off:
    +Wir die Box in den standby geschickt (egal, ob per Fernbedienung, timer, +web,...) so wird die Kommando-Datei sbon.lirc ausgeführt. Wacht sie +aus dem Standby auf wird die Kommando-Datei sboff.lirc ausgeführt.
    +Achtung , bei Deep-Standby funktioniert nur sbon.lirc !
    +Bsp. /var/tuxbox/config/lirc/sbon.lirc: +
    +tv power 500
    +amplifier power 500
    +
    +Bsp. /var/tuxbox/config/lirc/sboff.lirc: +
    +tv 1 250
    +tv av 250
    +amplifier power 500
    +
    +Volume +/-:
    +Bei erhöhen der Lautstärke wird die Kommando-Datei volplus.lirc bei +erniedrigen der Lautstärke volminus ausgeführt. Dies ist für Leute +interessant, die Audio nicht über den Fernseher, sondern digital über einen +Verstärker abspielen. Hier kann jetzt auch über die DBox-Fernbedienung die +Lautstärke verändert werden.
    +Dabei ist zu beachten, dass die Dauer, die die Kommados gesendet werden, +relativ kurz sein sollte, da sonst der Neutrino-Programmablauf sehr +unflüssig wirkt. Ich arbeite hier mit Werten zwischen 50 und 100 ms.
    +Bsp. /var/tuxbox/config/lirc/volplus.lirc: +
    +amplifier vol_inc 60
    +
    +Bsp. /var/tuxbox/config/lirc/volminus.lirc: +
    +amplifier vol_dec 60
    +
    + + + diff --git a/doc/picviewer.html b/doc/picviewer.html new file mode 100644 index 000000000..0e5767b3a --- /dev/null +++ b/doc/picviewer.html @@ -0,0 +1,39 @@ + + + + Neutrino Pictureviewer Doku + + + +
    +

    Neutrino Pictureviewer

    +
    +Tasten im Menü-Modus:
    +siehe Beschriftung...
    +OK-Taste zeigt ein Bild an.
    +0: Bild in voller Grösse unskaliert einlesen (kann lange dauern...)
    +

    +Tasten im Diashow-Modus:
    +Pfeil links : Bild zurück
    +Pfeil rechts : Bild weiter
    +Home: Diashow-Modus verlassen(kann etwas dauern...)
    +

    +Tasten im Anzeige-Modus:
    +Pfeil links : Bild zurück
    +Pfeil rechts : Bild weiter
    +Home: Anzeige-Modus verlassen
    +1: Zoom out
    +3: Zoom in
    +2,4,6,8: Scroll hoch,links,rechts,runter
    +0: Bild in voller Grösse unskaliert einlesen (kann lange dauern...)
    +OK: Bild neu einlesen +

    +Anmerkung zum Zoomen:
    +Beim Zoomen von Bildern stösst die Dbox sehr schnell an ihre Speichergrenzen, +da unkomprimierte Bilder sehr viel Speicher benötigen, die Dbox aber nicht +gerade üppig mit selbigem ausgestattet ist. Sollte im Anzeigemodus +nicht mehr gescrollt oder gezoomt werden können, so ist der Speicher voll. +In diesem Falle entweder zum nächsten Bild schalten (Pfeil rechts) oder den +Anzeigemodus beenden (home). + diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 000000000..a0fc874fb --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = \ + connection \ + libeventserver \ + controldclient \ + sectionsdclient \ + timerdclient \ + libconfigfile \ + libmd5sum \ + libnet \ + xmltree \ + libtuxtxt \ + libdvbsub \ + libupnpclient diff --git a/lib/connection/Makefile.am b/lib/connection/Makefile.am new file mode 100644 index 000000000..cd4b786d6 --- /dev/null +++ b/lib/connection/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libtuxbox-connection.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtuxbox_connection_a_SOURCES = basicclient.cpp basicserver.cpp basicsocket.cpp messagetools.cpp diff --git a/lib/connection/basicclient.cpp b/lib/connection/basicclient.cpp new file mode 100644 index 000000000..cbccbff84 --- /dev/null +++ b/lib/connection/basicclient.cpp @@ -0,0 +1,164 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicclient.cpp,v 1.17 2004/04/08 07:19:00 thegoodguy Exp $ + * + * Basic Client Class - The Tuxbox Project + * + * (C) 2002-2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "basicclient.h" +#include "basicmessage.h" +#include "basicsocket.h" + +#include +#include +#include +#include +#include + +#define TIMEOUT_SEC 60 +#define TIMEOUT_USEC 0 +#define MAX_TIMEOUT_SEC 300 +#define MAX_TIMEOUT_USEC 0 + +CBasicClient::CBasicClient() +{ + sock_fd = -1; +} + +bool CBasicClient::open_connection() +{ + close_connection(); + + struct sockaddr_un servaddr; + int clilen; + + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + strcpy(servaddr.sun_path, getSocketName()); // no length check !!! + clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); + + if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + printf("[CBasicClient] socket failed.\n"); + perror(getSocketName()); + sock_fd = -1; + return false; + } + + if (connect(sock_fd, (struct sockaddr*) &servaddr, clilen) < 0) + { + printf("[CBasicClient] connect failed.\n"); + perror(getSocketName()); + close_connection(); + return false; + } + return true; +} + +void CBasicClient::close_connection() +{ + if (sock_fd != -1) + { + close(sock_fd); + sock_fd = -1; + } +} + +bool CBasicClient::send_data(const char* data, const size_t size) +{ + timeval timeout; + + if (sock_fd == -1) + return false; + + timeout.tv_sec = TIMEOUT_SEC; + timeout.tv_usec = TIMEOUT_USEC; + + if (::send_data(sock_fd, data, size, timeout) == false) + { + printf("[CBasicClient] send failed: %s\n", getSocketName()); + close_connection(); + return false; + } + return true; +} + +bool CBasicClient::send_string(const char* data) +{ + uint8_t send_length; + size_t length = strlen(data); + if (length > 255) + { + printf("[CBasicClient] string too long - sending only first 255 characters: %s\n", data); + send_length = 255; + } + else + { + send_length = length; + } + return (send_data((char *)&send_length, sizeof(send_length)) && + send_data(data, send_length)); +} + +bool CBasicClient::receive_data(char* data, const size_t size, bool use_max_timeout) +{ + timeval timeout; + + if (sock_fd == -1) + return false; + + if (use_max_timeout) + { + timeout.tv_sec = MAX_TIMEOUT_SEC; + timeout.tv_usec = MAX_TIMEOUT_USEC; + } + else + { + timeout.tv_sec = TIMEOUT_SEC; + timeout.tv_usec = TIMEOUT_USEC; + } + + if (::receive_data(sock_fd, data, size, timeout) == false) + { + printf("[CBasicClient] receive failed: %s\n", getSocketName()); + close_connection(); + return false; + } + return true; +} + +bool CBasicClient::send(const unsigned char command, const char* data, const unsigned int size) +{ + CBasicMessage::Header msgHead; + msgHead.version = getVersion(); + msgHead.cmd = command; + + open_connection(); // if the return value is false, the next send_data call will return false, too + + if (!send_data((char*)&msgHead, sizeof(msgHead))) + return false; + + if (size != 0) + return send_data(data, size); + + return true; +} + diff --git a/lib/connection/basicclient.h b/lib/connection/basicclient.h new file mode 100644 index 000000000..7b82de108 --- /dev/null +++ b/lib/connection/basicclient.h @@ -0,0 +1,51 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicclient.h,v 1.7 2004/04/08 07:19:00 thegoodguy Exp $ + * + * Basic Client Class - The Tuxbox Project + * + * (C) 2002-2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __basicclient__ +#define __basicclient__ + +#include +#include + +class CBasicClient +{ + private: + int sock_fd; + + protected: + virtual const unsigned char getVersion () const = 0; + virtual const char * getSocketName() const = 0; + + bool open_connection(); + bool send_data(const char * data, const size_t size); + bool send_string(const char * data); + bool receive_data(char* data, const size_t size, bool use_max_timeout = false); + bool send(const unsigned char command, const char* data = NULL, const unsigned int size = 0); + void close_connection(); + + CBasicClient(); +}; + +#endif diff --git a/lib/connection/basicmessage.h b/lib/connection/basicmessage.h new file mode 100644 index 000000000..ab3228731 --- /dev/null +++ b/lib/connection/basicmessage.h @@ -0,0 +1,43 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicmessage.h,v 1.2 2002/10/18 11:56:56 thegoodguy Exp $ + * + * types used for clientlib <-> daemon communication - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __basicmessage_h__ +#define __basicmessage_h__ + + +class CBasicMessage +{ + public: + + typedef unsigned char t_version; + typedef unsigned char t_cmd; + + struct Header + { + t_version version; + t_cmd cmd; + }; +}; + + +#endif /* __basicmessage_h__ */ diff --git a/lib/connection/basicserver.cpp b/lib/connection/basicserver.cpp new file mode 100644 index 000000000..9156c1514 --- /dev/null +++ b/lib/connection/basicserver.cpp @@ -0,0 +1,172 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicserver.cpp,v 1.11 2004/04/08 08:00:55 thegoodguy Exp $ + * + * Basic Server Class Class - The Tuxbox Project + * + * (C) 2002 - 2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "basicserver.h" +#include "basicsocket.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define RECEIVE_TIMEOUT_IN_SECONDS 60 +#define SEND_TIMEOUT_IN_SECONDS 60 + +bool CBasicServer::receive_data(int fd, void * data, const size_t size) +{ + timeval timeout; + timeout.tv_sec = RECEIVE_TIMEOUT_IN_SECONDS; + timeout.tv_usec = 0; + return ::receive_data(fd, data, size, timeout); +} + +bool CBasicServer::send_data(int fd, const void * data, const size_t size) +{ + timeval timeout; + timeout.tv_sec = SEND_TIMEOUT_IN_SECONDS; + timeout.tv_usec = 0; + return ::send_data(fd, data, size, timeout); +} + +void CBasicServer::delete_string(char * data) +{ + free((void *)data); +} + +char * CBasicServer::receive_string(int fd) +{ + uint8_t length; + + if (receive_data(fd, &length, sizeof(length))) + { + char * data = (char *)malloc(((size_t)length) + 1); + if (receive_data(fd, data, length)) + { + data[length] = 0; /* add terminating 0 */ + return data; + } + else + { + delete_string(data); + return NULL; + } + } + return NULL; +} + +bool CBasicServer::prepare(const char* socketname) +{ + struct sockaddr_un servaddr; + int clilen; + + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + strcpy(servaddr.sun_path, socketname); + clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); + + unlink(socketname); + + if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + printf("[CBasicServer] socket failed.\n"); + perror(socketname); + return false; + } + if (bind(sock_fd, (struct sockaddr*) &servaddr, clilen) < 0) + { + printf("[CBasicServer] bind failed.\n"); + perror(socketname); + return false; + } + +#define N_connection_requests_queued 5 + + if (listen(sock_fd, N_connection_requests_queued) != 0) + { + printf("[CBasicServer] listen failed.\n"); + perror(socketname); + return false; + } + + name = socketname; + + return true; +} + +bool CBasicServer::parse(bool (parse_command)(CBasicMessage::Header &rmsg, int connfd), const CBasicMessage::t_version version) +{ + int conn_fd; + struct sockaddr_un servaddr; + int clilen = sizeof(servaddr); + bool parse_another_command = true; + + CBasicMessage::Header rmsg; + conn_fd = accept(sock_fd, (struct sockaddr*) &servaddr, (socklen_t*) &clilen); + memset(&rmsg, 0, sizeof(rmsg)); + read(conn_fd, &rmsg, sizeof(rmsg)); + + if (rmsg.version == version) + parse_another_command = parse_command(rmsg, conn_fd); + else + printf("[%s] Command ignored: cmd version %d received - server cmd version is %d\n", name.c_str(), rmsg.version, version); + + close(conn_fd); + + return parse_another_command; +} + +bool CBasicServer::run(bool (parse_command)(CBasicMessage::Header &rmsg, int connfd), const CBasicMessage::t_version version, bool non_blocking) +{ + if (non_blocking) { + struct pollfd pfd; + + pfd.fd = sock_fd; + pfd.events = (POLLIN | POLLPRI); + + if (poll(&pfd, 1, 0) > 0) + return parse(parse_command, version); + else + return true; + } + else { + while(parse(parse_command, version)); + + stop(); + + return false; + } +} + +void CBasicServer::stop(void) +{ + close(sock_fd); + unlink(name.c_str()); +} + diff --git a/lib/connection/basicserver.h b/lib/connection/basicserver.h new file mode 100644 index 000000000..848ab9857 --- /dev/null +++ b/lib/connection/basicserver.h @@ -0,0 +1,60 @@ +#ifndef __basicserver__ +#define __basicserver__ + +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicserver.h,v 1.7 2004/04/08 07:19:00 thegoodguy Exp $ + * + * Basic Server Class Class - The Tuxbox Project + * + * (C) 2002 - 2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include "basicmessage.h" + +class CBasicServer +{ + private: + int sock_fd; + std::string name; + + // used by run + bool parse(bool (parse_command)(CBasicMessage::Header &rmsg, int connfd), const CBasicMessage::t_version version); + + public: + static bool receive_data (int fd, void * data, const size_t size); + static bool send_data (int fd, const void * data, const size_t size); + static char * receive_string(int fd); + static void delete_string (char * data); + + bool prepare(const char* socketname); + + // run the server socket + // if set to non-blocking, it will leave the socket open but + // will return immediately without parsing a command if no data + // is sent by a client + bool run(bool (parse_command)(CBasicMessage::Header &rmsg, int connfd), const CBasicMessage::t_version version, bool non_blocking = false); + + // manual stop, can and should only be used in non-blocking mode + void stop(void); +}; + +#endif diff --git a/lib/connection/basicsocket.cpp b/lib/connection/basicsocket.cpp new file mode 100644 index 000000000..3ae8fd889 --- /dev/null +++ b/lib/connection/basicsocket.cpp @@ -0,0 +1,140 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicsocket.cpp,v 1.2 2003/02/24 21:14:15 thegoodguy Exp $ + * + * Basic Socket Class - The Tuxbox Project + * + * (C) 2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "basicsocket.h" + +#include +#include +#include +#include +#include + +bool send_data(int fd, const void * data, const size_t size, const timeval timeout) +{ + fd_set writefds; + timeval tv; + const void * buffer; + size_t n; + int rc; + + n = size; + + while (n > 0) + { + buffer = (void *)((char *)data + (size - n)); + rc = ::send(fd, buffer, n, MSG_DONTWAIT | MSG_NOSIGNAL); + + if (rc == -1) + { + perror("[basicsocket] send_data"); +char * buf = (char *) data; +printf("send_data: errno %d data %X\n", errno, buf[0]); + //if (errno == EPIPE) + if (errno == EPIPE || errno == ESPIPE) + return false; + + FD_ZERO(&writefds); + FD_SET(fd, &writefds); + + tv = timeout; + + rc = select(fd + 1, NULL, &writefds, NULL, &tv); + + if (rc == 0) + { + printf("[basicsocket] send timed out.\n"); + return false; + } + if (rc == -1) + { + perror("[basicsocket] send_data select"); + return false; + } + } + else + n -= rc; + } + return true; +} + + +bool receive_data(int fd, void * data, const size_t size, const timeval timeout) +{ + fd_set readfds; + timeval tv; + void * buffer; + size_t n; + int rc; + + n = size; + + while (n > 0) + { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + tv = timeout; + + rc = select(fd + 1, &readfds, NULL, NULL, &tv); + + if (rc == 0) + { + printf("[basicsocket] receive timed out.\n"); + return false; + } + if (rc == -1) + { + perror("[basicsocket] receive_data select"); + return false; + } + buffer = (void *)((char *)data + (size - n)); + rc = ::recv(fd, buffer, n, MSG_DONTWAIT | MSG_NOSIGNAL); + + if ((rc == 0) || (rc == -1)) + { + if (rc == -1) + { + perror("[basicsocket] receive_data"); + + //if (errno == EPIPE) + if (errno == EPIPE || errno == ESPIPE) + return false; + } + else + { + /* + * silently return false + * + * printf("[basicsocket] no more data\n"); + */ + return false; + } + + } + else + n -= rc; + } + return true; +} diff --git a/lib/connection/basicsocket.h b/lib/connection/basicsocket.h new file mode 100644 index 000000000..f236d3d71 --- /dev/null +++ b/lib/connection/basicsocket.h @@ -0,0 +1,35 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/basicsocket.h,v 1.2 2003/02/24 21:14:15 thegoodguy Exp $ + * + * Basic Socket Class - The Tuxbox Project + * + * (C) 2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __basicsocket__ +#define __basicsocket__ + +#include +#include + +bool send_data(int fd, const void * data, const size_t size, const timeval timeout); +bool receive_data(int fd, void * data, const size_t size, const timeval timeout); + +#endif diff --git a/lib/connection/messagetools.cpp b/lib/connection/messagetools.cpp new file mode 100644 index 000000000..cf140aa71 --- /dev/null +++ b/lib/connection/messagetools.cpp @@ -0,0 +1,96 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/messagetools.cpp,v 1.1 2002/12/07 13:37:06 thegoodguy Exp $ + * + * tools used in clientlib <-> daemon communication - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "messagetools.h" + +size_t write_length_field (unsigned char * buffer, unsigned int length) +{ + if (length < 128) + { + buffer[0] = length; + return 1; + } + else + { + unsigned int pos = 0; + unsigned int shiftby = 8; + unsigned char length_field_size = 1; + + while ((length >> shiftby) != 0) + { + length_field_size++; + shiftby += 8; + } + + buffer[pos++] = ((1 << 7) | length_field_size); + + while (shiftby != 0) + { + shiftby -= 8; + buffer[pos++] = length >> shiftby; + } + return pos; + } + +} + +unsigned int parse_length_field (const unsigned char * buffer) +{ + unsigned char size_indicator = (buffer[0] >> 7) & 0x01; + unsigned int length_value = 0; + + if (size_indicator == 0) + { + length_value = buffer[0] & 0x7F; + } + + else if (size_indicator == 1) + { + unsigned char length_field_size = buffer[0] & 0x7F; + unsigned int i; + + for (i = 0; i < length_field_size; i++) + { + length_value = (length_value << 8) | buffer[i + 1]; + } + } + + return length_value; +} + +size_t get_length_field_size (const unsigned int length) +{ + if (length < 0x80) + return 0x01; + + if (length < 0x100) + return 0x02; + + if (length < 0x10000) + return 0x03; + + if (length < 0x1000000) + return 0x04; + + return 0x05; +} diff --git a/lib/connection/messagetools.h b/lib/connection/messagetools.h new file mode 100644 index 000000000..09241138d --- /dev/null +++ b/lib/connection/messagetools.h @@ -0,0 +1,34 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libconnection/messagetools.h,v 1.1 2002/12/07 13:37:06 thegoodguy Exp $ + * + * tools used in clientlib <-> daemon communication - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __messagetools_h__ +#define __messagetools_h__ + +#include + +size_t write_length_field (unsigned char * buffer, const unsigned int length); +size_t get_length_field_size (const unsigned int length); +unsigned int parse_length_field (const unsigned char * buffer); + + +#endif /* __messagetools_h__ */ diff --git a/lib/controldclient/Makefile.am b/lib/controldclient/Makefile.am new file mode 100644 index 000000000..56165105b --- /dev/null +++ b/lib/controldclient/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +lib_LTLIBRARIES = libcontroldclient.la + +libcontroldclient_la_SOURCES = controldclient.cpp diff --git a/lib/controldclient/controldMsg.h b/lib/controldclient/controldMsg.h new file mode 100644 index 000000000..e68d6f112 --- /dev/null +++ b/lib/controldclient/controldMsg.h @@ -0,0 +1,172 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __controldMsg__ +#define __controldMsg__ + +#include +#include + +#define CONTROLD_UDS_NAME "/tmp/controld.sock" + + +class CControldMsg : public CBasicMessage +{ + + public: + + static const CBasicMessage::t_version ACTVERSION = 2; + + enum commands + { + CMD_SHUTDOWN = 1, + CMD_SAVECONFIG, + + CMD_SETVOLUME, + CMD_GETVOLUME, + + CMD_SETVOLUME_AVS, + CMD_GETVOLUME_AVS, + + CMD_SETMUTE, + CMD_GETMUTESTATUS, + + CMD_SETVIDEOFORMAT, + CMD_GETVIDEOFORMAT, + + CMD_SETVIDEOOUTPUT, + CMD_GETVIDEOOUTPUT, + + CMD_SETVCROUTPUT, + CMD_GETVCROUTPUT, + + CMD_SETBOXTYPE, + CMD_GETBOXTYPE, + + CMD_SETSCARTMODE, + CMD_GETSCARTMODE, + + CMD_GETASPECTRATIO, + + CMD_SETVIDEOPOWERDOWN, + CMD_GETVIDEOPOWERDOWN, + + CMD_REGISTEREVENT, + CMD_UNREGISTEREVENT, + + CMD_EVENT, + + CMD_SETCSYNC, + CMD_GETCSYNC, + CMD_SETVIDEOMODE, + CMD_GETVIDEOMODE + + }; + + struct commandVolume + { + unsigned char volume; + CControld::volume_type type; + }; + + struct commandMute + { + bool mute; + CControld::volume_type type; + }; + + struct commandVideoFormat + { + unsigned char format; + }; + + struct commandVideoOutput + { + unsigned char output; + }; + + struct commandVCROutput + { + unsigned char vcr_output; + }; + + struct commandBoxType + { + CControld::tuxbox_maker_t boxtype; + }; + + struct commandScartMode + { + unsigned char mode; + }; + + struct commandVideoPowerSave + { + bool powerdown; + }; + + //response structures + + struct responseVideoFormat + { + unsigned char format; + }; + + struct responseAspectRatio + { + unsigned char aspectRatio; + }; + + struct responseVideoOutput + { + unsigned char output; + }; + + struct responseVCROutput + { + unsigned char vcr_output; + }; + + struct responseBoxType + { + CControld::tuxbox_maker_t boxtype; + }; + + struct responseScartMode + { + unsigned char mode; + }; + + struct commandCsync + { + unsigned char csync; + }; + + struct responseVideoPowerSave + { + bool videoPowerSave; + }; + +}; + +#endif diff --git a/lib/controldclient/controldclient.cpp b/lib/controldclient/controldclient.cpp new file mode 100644 index 000000000..89bdfd9ca --- /dev/null +++ b/lib/controldclient/controldclient.cpp @@ -0,0 +1,316 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include + + +const unsigned char CControldClient::getVersion () const +{ + return CControldMsg::ACTVERSION; +} + +const char * CControldClient::getSocketName() const +{ + return CONTROLD_UDS_NAME; +} + +void CControldClient::shutdown() +{ + send(CControldMsg::CMD_SHUTDOWN); + close_connection(); +} + +void CControldClient::setBoxType(CControld::tuxbox_maker_t type) +{ + CControldMsg::commandBoxType msg2; + + msg2.boxtype = type; + + send(CControldMsg::CMD_SETBOXTYPE, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +CControld::tuxbox_maker_t CControldClient::getBoxType() +{ + CControldMsg::responseBoxType rmsg; + + send(CControldMsg::CMD_GETBOXTYPE); + + if (!receive_data((char*)&rmsg, sizeof(rmsg))) + rmsg.boxtype = CControld::TUXBOX_MAKER_UNKNOWN; + + close_connection(); + + return rmsg.boxtype; +} + +void CControldClient::setScartMode(bool mode) +{ + CControldMsg::commandScartMode msg2; + + msg2.mode = mode; + + send(CControldMsg::CMD_SETSCARTMODE, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +char CControldClient::getScartMode() +{ + CControldMsg::responseScartMode rmsg; + send(CControldMsg::CMD_GETSCARTMODE); + receive_data((char*)&rmsg, sizeof(rmsg)); + close_connection(); + + return rmsg.mode; +} + +void CControldClient::setVolume(const char volume, const CControld::volume_type volume_type) +{ + CControldMsg::commandVolume msg2; + msg2.type = volume_type; + msg2.volume = volume; + send(CControldMsg::CMD_SETVOLUME, (char*)&msg2, sizeof(msg2)); + close_connection(); +} + +char CControldClient::getVolume(const CControld::volume_type volume_type) +{ + CControldMsg::commandVolume rmsg; + rmsg.type = volume_type; + send(CControldMsg::CMD_GETVOLUME, (char*)&rmsg, sizeof(rmsg)); + receive_data((char*)&rmsg, sizeof(rmsg)); + close_connection(); + + return rmsg.volume; +} + +void CControldClient::setVideoFormat(char format) +{ + CControldMsg::commandVideoFormat msg2; + + msg2.format = format; + + send(CControldMsg::CMD_SETVIDEOFORMAT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +char CControldClient::getAspectRatio() +{ + CControldMsg::responseAspectRatio rmsg; + + send(CControldMsg::CMD_GETASPECTRATIO); + + receive_data((char*)&rmsg, sizeof(rmsg)); + + close_connection(); + + return rmsg.aspectRatio; +} + +char CControldClient::getVideoFormat() +{ + CControldMsg::responseVideoFormat rmsg; + + send(CControldMsg::CMD_GETVIDEOFORMAT); + + bool success = receive_data((char*)&rmsg, sizeof(rmsg)); + + close_connection(); + + return success ? rmsg.format : 2; /* default value is 2 (cf. controld.cpp) */ +} + +void CControldClient::setVideoOutput(char output) +{ + CControldMsg::commandVideoOutput msg2; + + msg2.output = output; + + send(CControldMsg::CMD_SETVIDEOOUTPUT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +void CControldClient::setVCROutput(char output) +{ + CControldMsg::commandVCROutput msg2; + + msg2.vcr_output = output; + + send(CControldMsg::CMD_SETVCROUTPUT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +char CControldClient::getVideoOutput() +{ + CControldMsg::responseVideoOutput rmsg; + + send(CControldMsg::CMD_GETVIDEOOUTPUT); + + bool success = receive_data((char*)&rmsg, sizeof(rmsg)); + + close_connection(); + + return success ? rmsg.output : 1; /* default value is 1 (cf. controld.cpp) */ +} + +char CControldClient::getVCROutput() +{ + CControldMsg::responseVCROutput rmsg; + + send(CControldMsg::CMD_GETVCROUTPUT); + + bool success = receive_data((char*)&rmsg, sizeof(rmsg)); + + close_connection(); + + return success ? rmsg.vcr_output : 1; /* default value is 1 (cf. controld.cpp) */ +} + +void CControldClient::Mute(const CControld::volume_type volume_type) +{ + setMute(true,volume_type); +} + +void CControldClient::UnMute(const CControld::volume_type volume_type) +{ + setMute(false,volume_type); +} + +void CControldClient::setMute(const bool mute, const CControld::volume_type volume_type) +{ + CControldMsg::commandMute msg; + msg.mute = mute; + msg.type = volume_type; + send(CControldMsg::CMD_SETMUTE, (char*)&msg, sizeof(msg)); + close_connection(); +} + +bool CControldClient::getMute(const CControld::volume_type volume_type) +{ + CControldMsg::commandMute rmsg; + rmsg.type = volume_type; + send(CControldMsg::CMD_GETMUTESTATUS, (char*)&rmsg, sizeof(rmsg)); + receive_data((char*)&rmsg, sizeof(rmsg)); + close_connection(); + return rmsg.mute; +} + +void CControldClient::registerEvent(unsigned int eventID, unsigned int clientID, const char * const udsName) +{ + CEventServer::commandRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + strcpy(msg2.udsName, udsName); + + send(CControldMsg::CMD_REGISTEREVENT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +void CControldClient::unRegisterEvent(unsigned int eventID, unsigned int clientID) +{ + CEventServer::commandUnRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + + send(CControldMsg::CMD_UNREGISTEREVENT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +void CControldClient::videoPowerDown(bool powerdown) +{ + CControldMsg::commandVideoPowerSave msg2; + + msg2.powerdown = powerdown; + + send(CControldMsg::CMD_SETVIDEOPOWERDOWN, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +bool CControldClient::getVideoPowerDown() +{ + CControldMsg::responseVideoPowerSave msg; + send(CControldMsg::CMD_GETVIDEOPOWERDOWN); + receive_data((char*) &msg, sizeof(msg)); + close_connection(); + return msg.videoPowerSave; +} + +void CControldClient::saveSettings() +{ + send(CControldMsg::CMD_SAVECONFIG); + close_connection(); +} + +void CControldClient::setRGBCsync(char val) +{ + CControldMsg::commandCsync msg; + msg.csync = val; + send(CControldMsg::CMD_SETCSYNC, (char*) &msg, sizeof(msg)); + close_connection(); +} + +char CControldClient::getRGBCsync() +{ + CControldMsg::commandCsync msg; + send(CControldMsg::CMD_GETCSYNC); + receive_data((char*) &msg, sizeof(msg)); + close_connection(); + return msg.csync; +} +char CControldClient::getVideoMode() +{ + CControldMsg::responseVideoFormat rmsg; + send(CControldMsg::CMD_GETVIDEOMODE); + bool success = receive_data((char*)&rmsg, sizeof(rmsg)); + close_connection(); + return success ? rmsg.format : 1; /* default value is 2 (cf. controld.cpp) */ +} +void CControldClient::setVideoMode(char format) +{ + CControldMsg::commandVideoFormat msg2; + msg2.format = format; + send(CControldMsg::CMD_SETVIDEOMODE, (char*)&msg2, sizeof(msg2)); + close_connection(); +} + diff --git a/lib/controldclient/controldclient.h b/lib/controldclient/controldclient.h new file mode 100644 index 000000000..8df78261a --- /dev/null +++ b/lib/controldclient/controldclient.h @@ -0,0 +1,196 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __controldclient__ +#define __controldclient__ + +#include +#include + + +#define VCR_STATUS_OFF 0 +#define VCR_STATUS_ON 1 +#define VCR_STATUS_16_9 2 + +class CControldClient:private CBasicClient +{ + private: + virtual const unsigned char getVersion () const; + virtual const char * getSocketName() const; + + public: + + enum events + { + EVT_VOLUMECHANGED, + EVT_MUTECHANGED, + EVT_MODECHANGED, + EVT_VCRCHANGED + }; + + //VideoFormat + static const char VIDEOFORMAT_AUTO = 0; + static const char VIDEOFORMAT_16_9 = 1; + static const char VIDEOFORMAT_4_3 = 2; + static const char VIDEOFORMAT_4_3_PS = 3; + + //VideoOutput + static const char VIDEOOUTPUT_COMPOSITE = 0; + static const char VIDEOOUTPUT_RGB = 1; + static const char VIDEOOUTPUT_SVIDEO = 2; + static const char VIDEOOUTPUT_YUV_VBS = 3; + static const char VIDEOOUTPUT_YUV_CVBS = 4; + + //mute + static const bool VOLUME_MUTE = true; + static const bool VOLUME_UNMUTE = false; + + //scartmode + static const char SCARTMODE_ON = 1; + static const char SCARTMODE_OFF = 0; + + /* + setVolume(volume_type) : Setzten der Lautstärke + Parameter: 0..100 - 0=leise 100=laut + volume_type : device AVS/OST/UNKOWN(=last used) + */ + void setVolume(const char volume, const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + char getVolume(const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + + /* + setMute(bool, volume_type) : setzen von Mute + Parameter: mute == true : ton aus + mute == false : ton an + volume_type : device AVS/OST/UNKOWN(=last used) + */ + void setMute(const bool mute, const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + bool getMute(const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + + /* + Mute(volume_type) : Ton ausschalten + Parameter: volume_type : device AVS/OST/UNKOWN(=last used) + */ + void Mute(const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + + /* + UnMute(bool) : Ton wieder einschalten + Parameter: volume_type : device AVS/OST/UNKOWN(=last used) + + */ + void UnMute(const CControld::volume_type volume_type = CControld::TYPE_UNKNOWN); + + + /* + setVideoFormat(char) : Setzten des Bildformates ( 4:3 / 16:9 ) + Parameter: VIDEOFORMAT_AUTO = auto + VIDEOFORMAT_4_3 = 4:3 + VIDEOFORMAT_16_9 = 16:9 + */ + void setVideoFormat(char); + char getVideoFormat(); + /* + getAspectRatio : Aktueller Wert aus dem Bitstream + 2: 4:3 + 3: 16:9 + 4: 2:2.1 + */ + char getAspectRatio(); + + /* + setVideoOutput(char) : Setzten des Videooutputs ( composite (= cvbs) / svideo / rgb+cvbs / yuv+vbs / yuv+cvbs ) + Parameter: VIDEOOUTPUT_COMPOSITE = cvbs (composite) video + VIDEOOUTPUT_SVIDEO = svideo + VIDEOOUTPUT_RGB = rgb+cvbs + VIDEOOUTPUT_YUV_VBS = yuv+vbs + VIDEOOUTPUT_YUV_CVBS = yuv+cvbs + */ + void setVideoOutput(char); + char getVideoOutput(); + + /* + setVCROutput(char) : Setzten des Videooutputs für VCR ( composite / svideo ) + Parameter: VIDEOOUTPUT_COMPOSITE = cvbs + VIDEOOUTPUT_SVIDEO = svideo + */ + void setVCROutput(char); + char getVCROutput(); + + /* + setBoxType(CControldClient::tuxbox_vendor_t) : Setzten des Boxentyps ( nokia / sagem / philips ) + */ + void setBoxType(const CControld::tuxbox_maker_t); + CControld::tuxbox_maker_t getBoxType(); + + /* + setScartMode(char) : Scartmode ( an / aus ) + Parameter: SCARTMODE_ON = auf scartinput schalten + SCARTMODE_OFF = wieder dvb anzeigen + + + */ + void setScartMode(bool); + char getScartMode(); + + + /* + die dBox in standby bringen + */ + void videoPowerDown(bool); + + /* + standby abfragen + */ + bool getVideoPowerDown(); + + /* + die Dbox herunterfahren + */ + void shutdown(); + + + /* + ein beliebiges Event anmelden + */ + void registerEvent(unsigned int eventID, unsigned int clientID, const char * const udsName); + + /* + ein beliebiges Event abmelden + */ + void unRegisterEvent(unsigned int eventID, unsigned int clientID); + + void saveSettings(); + + /* + setzen der sync correction im RGB mode des saa7126 (csync) 0=aus, 31=max + */ + + void setRGBCsync(char csync); + /* + lesen der sync correction im RGB mode des saa7126 (csync) 0=aus, 31=max + */ + char getRGBCsync(); + void setVideoMode(char); + char getVideoMode(); +}; + +#endif diff --git a/lib/controldclient/controldtypes.h b/lib/controldclient/controldtypes.h new file mode 100644 index 000000000..1025b0494 --- /dev/null +++ b/lib/controldclient/controldtypes.h @@ -0,0 +1,62 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __controldtypes__ +#define __controldtypes__ + +class CControld +{ +public: + + //BoxType /* cf. driver/include/tuxbox/tuxbox_info.h */ + typedef enum tuxbox_maker + { + TUXBOX_MAKER_UNKNOWN = 0, + TUXBOX_MAKER_NOKIA = 1, + TUXBOX_MAKER_PHILIPS = 2, + TUXBOX_MAKER_SAGEM = 3, + TUXBOX_MAKER_DREAM_MM = 4, + TUXBOX_MAKER_TECHNOTREND = 5 + } tuxbox_maker_t; + + enum volume_type + { + TYPE_OST=0, + TYPE_AVS=1, + TYPE_LIRC=2, + TYPE_UNKNOWN + }; + + const static int no_video_formats = 5; + + typedef enum video_format + { + FORMAT_CVBS = 0, + FORMAT_RGB = 1, + FORMAT_SVIDEO = 2, + FORMAT_YUV_VBS = 3, + FORMAT_YUV_CVBS = 4 + } t_video_format; + +}; +#endif diff --git a/lib/libconfigfile/Makefile.am b/lib/libconfigfile/Makefile.am new file mode 100644 index 000000000..201efcd63 --- /dev/null +++ b/lib/libconfigfile/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libtuxbox-configfile.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtuxbox_configfile_a_SOURCES = configfile.cpp diff --git a/lib/libconfigfile/configfile.cpp b/lib/libconfigfile/configfile.cpp new file mode 100644 index 000000000..0b28f9f40 --- /dev/null +++ b/lib/libconfigfile/configfile.cpp @@ -0,0 +1,415 @@ +/* + * $Id: configfile.cpp,v 1.19 2003/11/23 19:16:03 obi Exp $ + * + * configuration object for the d-box 2 linux project + * + * Copyright (C) 2001, 2002 Andreas Oberritter , + * thegoodguy + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "configfile.h" + +#include +#include +#include +#include +#include + +#include +#include + +CConfigFile::CConfigFile(const char p_delimiter, const bool p_saveDefaults) +{ + modifiedFlag = false; + unknownKeyQueryedFlag = false; + delimiter = p_delimiter; + saveDefaults = p_saveDefaults; +} + +void CConfigFile::clear() +{ + configData.clear(); +} + +// +// public file operation methods +// +const bool CConfigFile::loadConfig(const char * const filename) +{ + std::ifstream configFile(filename); + + if (configFile != NULL) + { + std::string s; + clear(); + modifiedFlag = false; + + for (int linenr = 1; ; linenr++) + { + getline(configFile, s); + if (configFile.fail()) + break; + + std::string::size_type i = s.find('='); + if (i != std::string::npos) + { + std::string::size_type j = s.find('#'); + if (j == std::string::npos) + j = s.length(); + configData[s.substr(0, i)] = s.substr(i + 1, j - (i + 1)); + } + } + configFile.close(); + return true; + } + else + { + std::cerr << "[ConfigFile] Unable to open file " << filename << " for reading." << std::endl; + return false; + } +} + +const bool CConfigFile::loadConfig(const std::string & filename) +{ + return loadConfig(filename.c_str()); +} + +const bool CConfigFile::saveConfig(const char * const filename) +{ + std::ofstream configFile(filename); + + if (configFile != NULL) + { + for (ConfigDataMap::const_iterator it = configData.begin(); it != configData.end(); it++) + { + configFile << it->first << "=" << it->second << std::endl; + } + + configFile.close(); + sync(); + + chmod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return true; + } + else + { + std::cerr << "[ConfigFile] Unable to open file " << filename << " for writing." << std::endl; + return false; + } +} + +const bool CConfigFile::saveConfig(const std::string & filename) +{ + return saveConfig(filename.c_str()); +} + + + +// +// private "store" methods +// +void CConfigFile::storeBool(const std::string & key, const bool val) +{ + if (val == true) + configData[key] = std::string("true"); + else + configData[key] = std::string("false"); +} + +void CConfigFile::storeInt32(const std::string & key, const int32_t val) +{ + std::stringstream s; + s << val; + s >> configData[key]; +} + +void CConfigFile::storeInt64(const std::string & key, const int64_t val) +{ + std::stringstream s; + s << val; + s >> configData[key]; +} + +void CConfigFile::storeString(const std::string & key, const std::string & val) +{ + configData[key] = val; +} + + + +// +// public "get" methods +// +bool CConfigFile::getBool(const char * const key, const bool defaultVal) +{ + return getBool(std::string(key), defaultVal); +} + +bool CConfigFile::getBool(const std::string & key, const bool defaultVal) +{ + if (configData.find(key) == configData.end()) + { + if (saveDefaults) { + unknownKeyQueryedFlag = true; + storeBool(key, defaultVal); + } + else { + return defaultVal; + } + } + + return !((configData[key] == "false") || (configData[key] == "0")); +} + +int32_t CConfigFile::getInt32(const char * const key, const int32_t defaultVal) +{ + return getInt32(std::string(key), defaultVal); +} + +int32_t CConfigFile::getInt32(const std::string & key, const int32_t defaultVal) +{ + if (configData.find(key) == configData.end()) + { + if (saveDefaults) { + unknownKeyQueryedFlag = true; + storeInt32(key, defaultVal); + } + else { + return defaultVal; + } + } + + return atoi(configData[key].c_str()); +} + +int64_t CConfigFile::getInt64(const char * const key, const int64_t defaultVal) +{ + return getInt64(std::string(key), defaultVal); +} + +int64_t CConfigFile::getInt64(const std::string & key, const int64_t defaultVal) +{ + if (configData.find(key) == configData.end()) + { + if (saveDefaults) { + unknownKeyQueryedFlag = true; + storeInt64(key, defaultVal); + } + else { + return defaultVal; + } + } + + return atoll(configData[key].c_str()); +} + +std::string CConfigFile::getString(const char * const key, const std::string & defaultVal) +{ + return getString(std::string(key), defaultVal); +} + +std::string CConfigFile::getString(const std::string & key, const std::string & defaultVal) +{ + if (configData.find(key) == configData.end()) + { + if (saveDefaults) { + unknownKeyQueryedFlag = true; + storeString(key, defaultVal); + } + else { + return defaultVal; + } + } + + return configData[key]; +} + +std::vector CConfigFile::getInt32Vector(const std::string & key) +{ + std::string val = configData[key]; + std::vector vec; + uint16_t length = 0; + uint16_t pos = 0; + uint16_t i; + + for (i = 0; i < val.length(); i++) + { + if (val[i] == delimiter) + { + vec.push_back(atoi(val.substr(pos, length).c_str())); + pos = i + 1; + length = 0; + } + else + { + length++; + } + } + + if (length == 0) + unknownKeyQueryedFlag = true; + else + vec.push_back(atoi(val.substr(pos, length).c_str())); + + return vec; +} + +std::vector CConfigFile::getStringVector(const std::string & key) +{ + std::string val = configData[key]; + std::vector vec; + uint16_t length = 0; + uint16_t pos = 0; + uint16_t i; + + for (i = 0; i < val.length(); i++) + { + if (val[i] == delimiter) + { + vec.push_back(val.substr(pos, length)); + pos = i + 1; + length = 0; + } + else + { + length++; + } + } + + if (length == 0) + unknownKeyQueryedFlag = true; + else + vec.push_back(val.substr(pos, length)); + + return vec; +} + + + +// +// public "set" methods +// +void CConfigFile::setBool(const char * const key, const bool val) +{ + setBool(std::string(key), val); +} + +void CConfigFile::setBool(const std::string & key, const bool val) +{ + bool tmpUnknownKeyQueryedFlag = unknownKeyQueryedFlag; + unknownKeyQueryedFlag = false; + bool oldVal = getBool(key); + + if ((oldVal != val) || (unknownKeyQueryedFlag)) + { + modifiedFlag = true; + storeBool(key, val); + } + + unknownKeyQueryedFlag = tmpUnknownKeyQueryedFlag; +} + +void CConfigFile::setInt32(const char * const key, int32_t val) +{ + setInt32(std::string(key), val); +} + +void CConfigFile::setInt32(const std::string & key, int32_t val) +{ + bool tmpUnknownKeyQueryedFlag = unknownKeyQueryedFlag; + unknownKeyQueryedFlag = false; + int32_t oldVal = getInt32(key); + + if ((oldVal != val) || (unknownKeyQueryedFlag)) + { + modifiedFlag = true; + storeInt32(key, val); + } + + unknownKeyQueryedFlag = tmpUnknownKeyQueryedFlag; +} + +void CConfigFile::setInt64(const char * const key, const int64_t val) +{ + setInt64(std::string(key), val); +} + +void CConfigFile::setInt64(const std::string & key, const int64_t val) +{ + bool tmpUnknownKeyQueryedFlag = unknownKeyQueryedFlag; + unknownKeyQueryedFlag = false; + int64_t oldVal = getInt64(key); + + if ((oldVal != val) || (unknownKeyQueryedFlag)) + { + modifiedFlag = true; + storeInt64(key, val); + } + + unknownKeyQueryedFlag = tmpUnknownKeyQueryedFlag; +} + +void CConfigFile::setString(const char * const key, const std::string & val) +{ + setString(std::string(key), val); +} + +void CConfigFile::setString(const std::string & key, const std::string & val) +{ + bool tmpUnknownKeyQueryedFlag = unknownKeyQueryedFlag; + unknownKeyQueryedFlag = false; + std::string oldVal = getString(key); + + if ((oldVal != val) || (unknownKeyQueryedFlag)) + { + modifiedFlag = true; + storeString(key, val); + } + + unknownKeyQueryedFlag = tmpUnknownKeyQueryedFlag; +} + +void CConfigFile::setInt32Vector(const std::string & key, const std::vector vec) +{ + std::stringstream s; + + for (std::vector::const_iterator it = vec.begin(); ; ) + { + s << (*it); + it++; + if (it == vec.end()) + break; + s << delimiter; + } + s >> configData[key]; +} + +void CConfigFile::setStringVector(const std::string & key, const std::vector vec) +{ + configData[key] = ""; + + for (std::vector::const_iterator it = vec.begin(); ; ) + { + configData[key] += *it; + it++; + if (it == vec.end()) + break; + configData[key] += delimiter; + } +} diff --git a/lib/libconfigfile/configfile.h b/lib/libconfigfile/configfile.h new file mode 100644 index 000000000..0142d4544 --- /dev/null +++ b/lib/libconfigfile/configfile.h @@ -0,0 +1,111 @@ +/* + * $Id: configfile.h,v 1.11 2003/09/25 18:52:27 thegoodguy Exp $ + * + * Copyright (C) 2001, 2002 Andreas Oberritter , + * thegoodguy + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __configfile_h__ +#define __configfile_h__ + + +#include + +#include +#include +#include + + +class CConfigFile +{ + private: + + typedef std::map ConfigDataMap; + + ConfigDataMap configData; + char delimiter; + bool saveDefaults; + bool modifiedFlag; + bool unknownKeyQueryedFlag; + + void storeBool(const std::string & key, const bool val); + void storeInt32(const std::string & key, const int32_t val); + void storeInt64(const std::string & key, const int64_t val); + void storeString(const std::string & key, const std::string & val); + + public: + CConfigFile(const char p_delimiter, const bool p_saveDefaults = true); + + const bool loadConfig(const char * const filename); + const bool loadConfig(const std::string & filename); + + const bool saveConfig(const char * const filename); + const bool saveConfig(const std::string & filename); + + void clear(); + + // + // strings + // + std::string getString(const char * const key, const std::string & defaultVal = ""); + std::string getString(const std::string & key, const std::string & defaultVal = ""); + void setString(const char * const key, const std::string & val); + void setString(const std::string & key, const std::string & val); + + // + // 32, 64 bit int + // + int32_t getInt32(const char * const key, const int32_t defaultVal = 0); + int32_t getInt32(const std::string & key, const int32_t defaultVal = 0); + void setInt32(const char * const key, const int32_t val); + void setInt32(const std::string & key, const int32_t val); + + int64_t getInt64(const char * const key, const int64_t defaultVal = 0); + int64_t getInt64(const std::string & key, const int64_t defaultVal = 0); + void setInt64(const char * const key, const int64_t val); + void setInt64(const std::string & key, const int64_t val); + + // + // boolean + // + bool getBool(const char * const key, const bool defaultVal = false); + bool getBool(const std::string & key, const bool defaultVal = false); + void setBool(const char * const key, const bool val); + void setBool(const std::string & key, const bool val); + + // + // vectors + // + std::vector getStringVector(const std::string & key); + void setStringVector(const std::string & key, const std::vector vec); + + std::vector getInt32Vector(const std::string & key); + void setInt32Vector(const std::string & key, const std::vector vec); + + // + // flags + // + const bool getModifiedFlag() const { return modifiedFlag; } + void setModifiedFlag(const bool val) { modifiedFlag = val; } + + const bool getUnknownKeyQueryedFlag() const { return unknownKeyQueryedFlag; } + void setUnknownKeyQueryedFlag(const bool val) { unknownKeyQueryedFlag = val; } + +}; + +#endif /* __configfile_h__ */ diff --git a/lib/libcoolstream/.svnignore b/lib/libcoolstream/.svnignore new file mode 100644 index 000000000..3694ff0e6 --- /dev/null +++ b/lib/libcoolstream/.svnignore @@ -0,0 +1,5 @@ +Makefile.in +.libs +.deps +*.la +*.lo diff --git a/lib/libcoolstream/audio_cs.h b/lib/libcoolstream/audio_cs.h new file mode 100644 index 000000000..b2dea13e7 --- /dev/null +++ b/lib/libcoolstream/audio_cs.h @@ -0,0 +1,115 @@ +/* public header file */ + +#ifndef _AUDIO_CS_H_ +#define _AUDIO_CS_H_ + +typedef enum +{ + AUDIO_SYNC_WITH_PTS, + AUDIO_NO_SYNC, + AUDIO_SYNC_AUDIO_MASTER +} AUDIO_SYNC_MODE; + +typedef enum +{ + AUDIO_FMT_AUTO = 0, + AUDIO_FMT_MPEG, + AUDIO_FMT_MP3, + AUDIO_FMT_DOLBY_DIGITAL, + AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL, + AUDIO_FMT_AAC, + AUDIO_FMT_AAC_PLUS, + AUDIO_FMT_DD_PLUS, + AUDIO_FMT_DTS, + AUDIO_FMT_AVS, + AUDIO_FMT_MLP, + AUDIO_FMT_WMA, + AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP +} AUDIO_FORMAT; + +#ifndef CS_AUDIO_PDATA +#define CS_AUDIO_PDATA void +#endif + +#include "cs_types.h" + +class cAudio +{ + private: + CS_AUDIO_PDATA * privateData; + unsigned int cEncodedDataOnSPDIF, cEncodedDataOnHDMI; + bool Muted; + + AUDIO_FORMAT StreamType; + AUDIO_SYNC_MODE SyncMode; + bool started; + unsigned int uAudioPTSDelay; + unsigned int uAudioDolbyPTSDelay, uAudioMpegPTSDelay; + bool receivedDelay; + + /* internal methods */ + int setBypassMode(int disable); + int LipsyncAdjust(void); + int atten; + int volume; + + bool clip_started; + bool hdmiDD; + bool spdifDD; + bool hasMuteScheduled; + public: + /* construct & destruct */ + cAudio(void * hBuffer, void * encHD, void * encSD); + ~cAudio(void); + + void * GetHandle(); + void * GetDSP(); + void HandleAudioMessage(int Event, void *pData); + void HandlePcmMessage(int Event, void *pData); + /* shut up */ + int mute(void); + int unmute(void); + int SetMute(int enable); + + /* bypass audio to external decoder */ + int enableBypass(void); + int disableBypass(void); + + /* volume, min = 0, max = 255 */ + int setVolume(unsigned int left, unsigned int right); + int getVolume(void) { return volume;} + + /* start and stop audio */ + int Start(void); + int Stop(void); + bool Pause(bool Pcm = true); + bool Resume(bool Pcm = true); + void SetStreamType(AUDIO_FORMAT type) { StreamType = type; }; + AUDIO_FORMAT GetStreamType(void) { return StreamType; } + bool ReceivedAudioDelay(void) { return receivedDelay; } + void SetReceivedAudioDelay(bool set = false) { receivedDelay = set; } + unsigned int GetAudioDelay(void) { return (StreamType == AUDIO_FMT_DOLBY_DIGITAL) ? uAudioDolbyPTSDelay : uAudioMpegPTSDelay; } + void SetSyncMode(AVSYNC_TYPE Mode); + + /* stream source */ + int getSource(void); + int setSource(int source); + int Flush(void); + + /* select channels */ + int setChannel(int channel); + int getChannel(void); + int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian); + int WriteClip(unsigned char * buffer, int size); + int StopClip(); + void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode); + void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level); + bool IsHdmiDDSupported(); + void SetHdmiDD(bool enable); + void SetSpdifDD(bool enable); + void ScheduleMute(bool On); + +}; + +#endif + diff --git a/lib/libcoolstream/cs_types.h b/lib/libcoolstream/cs_types.h new file mode 100644 index 000000000..da046341e --- /dev/null +++ b/lib/libcoolstream/cs_types.h @@ -0,0 +1,11 @@ +#ifndef __CS_TYPES +#define __CS_TYPES + +typedef enum +{ + AVSYNC_DISABLED, + AVSYNC_ENABLED, + AVSYNC_AUDIO_IS_MASTER, +} AVSYNC_TYPE; + +#endif diff --git a/lib/libcoolstream/dmx_cs.h b/lib/libcoolstream/dmx_cs.h new file mode 100644 index 000000000..35108ca29 --- /dev/null +++ b/lib/libcoolstream/dmx_cs.h @@ -0,0 +1,78 @@ +/*******************************************************************************/ +/* */ +/* libcoolstream/cszapper/demux.h */ +/* ZAP interface for neutrino frontend */ +/* */ +/* (C) 2008 CoolStream International */ +/* */ +/*******************************************************************************/ +#ifndef __DEMUX_CS_H +#define __DEMUX_CS_H + +#include + +#define DEMUX_POLL_TIMEOUT 0 // timeout in ms +#define MAX_FILTER_LENGTH 16 // maximum number of filters +#ifndef DMX_FILTER_SIZE +#define DMX_FILTER_SIZE MAX_FILTER_LENGTH +#endif +#define MAX_DMX_UNITS 4 //DMX_NUM_TSS_INPUTS_REVB + +#ifndef CS_DMX_PDATA +#define CS_DMX_PDATA void +#endif + +#include "cs_types.h" + +typedef enum +{ + DMX_VIDEO_CHANNEL = 1, + DMX_AUDIO_CHANNEL, + DMX_PES_CHANNEL, + DMX_PSI_CHANNEL, + DMX_PIP_CHANNEL, + DMX_TP_CHANNEL, + DMX_PCR_ONLY_CHANNEL, +} DMX_CHANNEL_TYPE; + +class cDemux +{ + private: + int timeout; + unsigned short pid; + unsigned char tid[MAX_FILTER_LENGTH], mask[MAX_FILTER_LENGTH]; + bool nb; // non block + pthread_cond_t read_cond; + pthread_mutex_t mutex; + AVSYNC_TYPE SyncMode; + int uLength; + bool enabled; + int unit; + + DMX_CHANNEL_TYPE type; + CS_DMX_PDATA * privateData; + public: + + bool Open(DMX_CHANNEL_TYPE pes_type, void * hVideoBuffer = NULL, int uBufferSize = 8192); + void Close(void); + bool Start(void); + bool Stop(void); + int Read(unsigned char *buff, int len, int Timeout = 0); + void SignalRead(int len); + unsigned short GetPID(void) { return pid; } + const unsigned char *GetFilterTID(void) { return tid; } + const unsigned char *GetFilterMask(void) { return mask; } + bool sectionFilter(unsigned short Pid, const unsigned char * const Tid, const unsigned char * const Mask, int len, int Timeout = DEMUX_POLL_TIMEOUT, const unsigned char * const nMask = NULL); + bool pesFilter(const unsigned short Pid); + void SetSyncMode(AVSYNC_TYPE mode); + void * getBuffer(); + void * getChannel(); + const DMX_CHANNEL_TYPE getChannelType(void); + void addPid(unsigned short Pid); + void getSTC(int64_t * STC); + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H diff --git a/lib/libcoolstream/dvb-ci.h b/lib/libcoolstream/dvb-ci.h new file mode 100644 index 000000000..483ae2c4a --- /dev/null +++ b/lib/libcoolstream/dvb-ci.h @@ -0,0 +1,62 @@ +#ifndef __DVBCI_H +#define __DVBCI_H + +#ifdef __cplusplus +extern "C" { +#endif +void CI_MenuAnswer(unsigned char bSlotIndex,unsigned char choice); +void CI_Answer(unsigned char bSlotIndex,unsigned char *pBuffer,unsigned char nLength); +void CI_CloseMMI(unsigned char bSlotIndex); +void CI_EnterMenu(unsigned char bSlotIndex); +#ifdef __cplusplus +} +#endif + +#define MAX_MMI_ITEMS 20 +#define MAX_MMI_TEXT_LEN 255 +#define MAX_MMI_CHOICE_TEXT_LEN 255 + +typedef struct +{ + int slot; + int choice_nb; + char title[MAX_MMI_TEXT_LEN]; + char subtitle[MAX_MMI_TEXT_LEN]; + char bottom[MAX_MMI_TEXT_LEN]; + char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN]; +} MMI_MENU_LIST_INFO; + +typedef struct +{ + int slot; + int blind; + int answerlen; + char enguiryText[MAX_MMI_TEXT_LEN]; + +} MMI_ENGUIRY_INFO; + +typedef void (*SEND_MSG_HOOK) (unsigned int msg, unsigned int data); + +class cDvbCi { + private: + int slots; + bool init; + int pmtlen; + unsigned char * pmtbuf; + void SendPMT(); + pthread_mutex_t ciMutex; + public: + bool Init(void); + bool SendPMT(unsigned char *data, int len); + bool SendDateTime(void); + // + cDvbCi(int Slots); + ~cDvbCi(); + static cDvbCi * getInstance(); + SEND_MSG_HOOK SendMessage; + void SetHook(SEND_MSG_HOOK _SendMessage) { SendMessage = _SendMessage; }; + bool CamPresent(int slot); + bool GetName(int slot, char * name); +}; + +#endif //__DVBCI_H diff --git a/lib/libcoolstream/init_cs.h b/lib/libcoolstream/init_cs.h new file mode 100644 index 000000000..17412fd69 --- /dev/null +++ b/lib/libcoolstream/init_cs.h @@ -0,0 +1,5 @@ +#ifndef __INIT_CS_H +#define __INIT_CS_H +void init_cs_api(); +void shutdown_cs_api(); +#endif diff --git a/lib/libcoolstream/playback_cs.h b/lib/libcoolstream/playback_cs.h new file mode 100644 index 000000000..71bad086c --- /dev/null +++ b/lib/libcoolstream/playback_cs.h @@ -0,0 +1,68 @@ +/*******************************************************************************/ +/* */ +/* libcoolstream/cszapper/demux.h */ +/* ZAP interface for neutrino frontend */ +/* */ +/* (C) 2008 CoolStream International */ +/* */ +/*******************************************************************************/ +#ifndef __PLAYBACK_CS_H +#define __PLAYBACK_CS_H + +#include + +#ifndef CS_PLAYBACK_PDATA +#define CS_PLAYBACK_PDATA void +#endif + +typedef enum { + PLAYMODE_TS = 0, + PLAYMODE_FILE, +} playmode_t; + +class cPlayback +{ + private: + int timeout; + pthread_cond_t read_cond; + pthread_mutex_t mutex; + CS_PLAYBACK_PDATA * privateData; + bool enabled; + bool paused; + bool playing; + int unit; + int nPlaybackFD; + int video_type; + int nPlaybackSpeed; + int mSpeed; + playmode_t playMode; + // + void Attach(void); + void Detach(void); + bool SetAVDemuxChannel(bool On, bool Video = true, bool Audio = true); + public: + void PlaybackNotify (int Event, void *pData, void *pTag); + void DMNotify(int Event, void *pTsBuf, void *Tag); + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3); + bool Stop(void); + bool SetAPid(unsigned short pid, bool ac3); + bool SetSpeed(int speed); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); + bool GetOffset(off64_t &offset); + bool SetPosition(int position, bool absolute = false); + bool IsPlaying(void) const { return playing; } + bool IsEnabled(void) const { return enabled; } + void * GetHandle(void); + void * GetDmHandle(void); + int GetCurrPlaybackSpeed(void) const { return nPlaybackSpeed; } + void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); + // + cPlayback(int num = 0); + ~cPlayback(); + +}; + +#endif diff --git a/lib/libcoolstream/pwrmngr.h b/lib/libcoolstream/pwrmngr.h new file mode 100644 index 000000000..0adbb267d --- /dev/null +++ b/lib/libcoolstream/pwrmngr.h @@ -0,0 +1,53 @@ +#ifndef __PWRMNGR_H__ +#define __PWRMNGR_H__ + +// -- cCpuFreqManager ---------------------------------------------------------- + +class cCpuFreqManager { +private: + unsigned long startCpuFreq; + unsigned long delta; +public: + void Up(void); + void Down(void); + void Reset(void); + // + bool SetCpuFreq(unsigned long CpuFreq); + bool SetDelta(unsigned long Delta); + unsigned long GetCpuFreq(void); + unsigned long GetDelta(void); + // + cCpuFreqManager(void); + +}; + +// -- cPowerManageger ---------------------------------------------------------- + +typedef enum +{ + PWR_INIT = 1, + PWR_FULL_ACTIVE, /* all devices/clocks up */ + PWR_ACTIVE_STANDBY, + PWR_PASSIVE_STANDBY, + PWR_INVALID +} PWR_STATE; + +class cPowerManager { +private: + bool init; + bool opened; + PWR_STATE powerState; + // + static void ApplicationCallback(void *hHandle, void *pUserData, signed long eEvent, void *pEventData, void *pTag) {} + bool SetState(PWR_STATE PowerState); +public: + bool Open(void); + void Close(void); + // + bool SetStandby(bool Active, bool Passive); + // + cPowerManager(void); + virtual ~cPowerManager(); +}; + +#endif // __PWRMNGR_H__ diff --git a/lib/libcoolstream/record_cs.h b/lib/libcoolstream/record_cs.h new file mode 100644 index 000000000..72b5c66f2 --- /dev/null +++ b/lib/libcoolstream/record_cs.h @@ -0,0 +1,38 @@ +/*******************************************************************************/ +/* */ +/* libcoolstream/cszapper/demux.h */ +/* ZAP interface for neutrino frontend */ +/* */ +/* (C) 2008 CoolStream International */ +/* */ +/*******************************************************************************/ +#ifndef __RECORD_CS_H +#define __RECORD_CS_H + +#include + +#ifndef CS_RECORD_PDATA +#define CS_RECORD_PDATA void +#endif + +class cRecord +{ + private: + CS_RECORD_PDATA * privateData; + bool enabled; + int num_apids; + int unit; + int nRecordFD; + + public: + cRecord(int num = 0); + ~cRecord(); + + bool Open(int numpids); + void Close(void); + bool Start(int fd, unsigned short vpid, unsigned short * apids, int numpids); + bool Stop(void); + void RecordNotify(int Event, void *pData); +}; + +#endif diff --git a/lib/libcoolstream/video_cs.h b/lib/libcoolstream/video_cs.h new file mode 100644 index 000000000..a667ac1c4 --- /dev/null +++ b/lib/libcoolstream/video_cs.h @@ -0,0 +1,203 @@ +#ifndef _VIDEO_CS_H +#define _VIDEO_CS_H + +#include +#include + +#include "cs_types.h" + +typedef enum { + ANALOG_SD_RGB_CINCH = 0x00, + ANALOG_SD_YPRPB_CINCH, + ANALOG_HD_RGB_CINCH, + ANALOG_HD_YPRPB_CINCH, + ANALOG_SD_RGB_SCART = 0x10, + ANALOG_SD_YPRPB_SCART, + ANALOG_HD_RGB_SCART, + ANALOG_HD_YPRPB_SCART, + ANALOG_SCART_MASK = 0x10, +} analog_mode_t; + +typedef enum { + VIDEO_FORMAT_MPEG2 = 0, + VIDEO_FORMAT_MPEG4, + VIDEO_FORMAT_VC1, + VIDEO_FORMAT_JPEG, + VIDEO_FORMAT_GIF, + VIDEO_FORMAT_PNG +} VIDEO_FORMAT; + +typedef enum { + VIDEO_SD = 0, + VIDEO_HD, + VIDEO_120x60i, + VIDEO_320x240i, + VIDEO_1440x800i, + VIDEO_360x288i +} VIDEO_DEFINITION; + +typedef enum { + VIDEO_FRAME_RATE_23_976 = 0, + VIDEO_FRAME_RATE_24, + VIDEO_FRAME_RATE_25, + VIDEO_FRAME_RATE_29_97, + VIDEO_FRAME_RATE_30, + VIDEO_FRAME_RATE_50, + VIDEO_FRAME_RATE_59_94, + VIDEO_FRAME_RATE_60 +} VIDEO_FRAME_RATE; + +typedef enum { + DISPLAY_AR_1_1, + DISPLAY_AR_4_3, + DISPLAY_AR_14_9, + DISPLAY_AR_16_9, + DISPLAY_AR_20_9, + DISPLAY_AR_RAW, +} DISPLAY_AR; + +typedef enum { + DISPLAY_AR_MODE_PANSCAN = 0, + DISPLAY_AR_MODE_LETTERBOX, + DISPLAY_AR_MODE_NONE, + DISPLAY_AR_MODE_PANSCAN2 +} DISPLAY_AR_MODE; + +typedef enum { + VIDEO_DB_DR_NEITHER = 0, + VIDEO_DB_ON, + VIDEO_DB_DR_BOTH +} VIDEO_DB_DR; + +typedef enum { + VIDEO_PLAY_STILL = 0, + VIDEO_PLAY_CLIP, + VIDEO_PLAY_TRICK, + VIDEO_PLAY_MOTION, + VIDEO_PLAY_MOTION_NO_SYNC +} VIDEO_PLAY_MODE; + +typedef enum { + VIDEO_STD_NTSC, + VIDEO_STD_SECAM, + VIDEO_STD_PAL, + VIDEO_STD_480P, + VIDEO_STD_576P, + VIDEO_STD_720P60, + VIDEO_STD_1080I60, + VIDEO_STD_720P50, + VIDEO_STD_1080I50, + VIDEO_STD_1080P30, + VIDEO_STD_1080P24, + VIDEO_STD_1080P25, + VIDEO_STD_AUTO +} VIDEO_STD; + +#ifndef CS_VIDEO_PDATA +#define CS_VIDEO_PDATA void +#endif + +class cVideo +{ + private: + CS_VIDEO_PDATA * privateData; + VIDEO_FORMAT StreamType; + VIDEO_DEFINITION VideoDefinition; + DISPLAY_AR DisplayAR; + VIDEO_PLAY_MODE SyncMode; + DISPLAY_AR_MODE ARMode; + VIDEO_DB_DR eDbDr; + DISPLAY_AR PictureAR; + VIDEO_FRAME_RATE FrameRate; + bool interlaced; + + unsigned int uDRMDisplayDelay; + unsigned int uVideoPTSDelay; + int StcPts; + bool started; + unsigned int bStandby; + bool blank; + bool playing; + bool auto_format; + int uFormatIndex; + int pic_width, pic_height; + int jpeg_width, jpeg_height; + int video_width, video_height; + + bool receivedDRMDelay; + bool receivedVideoDelay; + int cfd; // control driver fd + analog_mode_t analog_mode_cinch; + analog_mode_t analog_mode_scart; + + vfd_icon mode_icon; + int SelectAutoFormat(); + void ScalePic(); + public: + /* constructor & destructor */ + cVideo(int mode, void * hChannel, void * hBuffer); + ~cVideo(void); + + void * GetDRM(void); + void * GetTVEnc(); + void * GetTVEncSD(); + void * GetHandle(); + + void SetAudioHandle(void * handle); + /* aspect ratio */ + int getAspectRatio(void); + void getPictureInfo(int &width, int &height, int &rate); + int setAspectRatio(int aspect, int mode); + + /* cropping mode */ + int getCroppingMode(void); + int setCroppingMode(void); + + /* stream source */ + int getSource(void); + int setSource(void); + int GetStreamType(void) { return StreamType; }; + + /* blank on freeze */ + int getBlank(void); + int setBlank(int enable); + + /* get play state */ + int getPlayState(void); + void SetDRMDelay(unsigned int delay) { uDRMDisplayDelay = delay;}; + void SetVideoDelay(unsigned int delay) { uVideoPTSDelay = delay;}; + void HandleDRMMessage(int Event, void *pData); + void HandleVideoMessage(void * hHandle, int Event, void *pData); + VIDEO_DEFINITION GetVideoDef(void) { return VideoDefinition; } + + /* change video play state */ + int Start(void * PcrChannel, unsigned short PcrPid, unsigned short VideoPid, void * hChannel = NULL); + int Stop(bool blank = true); + bool Pause(void); + bool Resume(void); + int LipsyncAdjust(); + int64_t GetPTS(void); + + int Flush(void); + + /* set video_system */ + int SetVideoSystem(int video_system, bool remember = true); + int SetStreamType(VIDEO_FORMAT type); + void SetSyncMode(AVSYNC_TYPE mode); + void ShowPicture(const char * fname); + void StopPicture(); + void Standby(unsigned int bOn); + void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600); + void setContrast(int val); + bool ReceivedDRMDelay(void) { return receivedDRMDelay; } + bool ReceivedVideoDelay(void) { return receivedVideoDelay; } + void SetReceivedDRMDelay(bool Received) { receivedDRMDelay = Received; } + void SetReceivedVideoDelay(bool Received) { receivedVideoDelay = Received; } + void SetFastBlank(bool onoff); + void SetTVAV(bool onoff); + void SetWideScreen(bool onoff); + void SetVideoMode(analog_mode_t mode); + void SetDBDR(int dbdr); +}; + +#endif diff --git a/lib/libdvbsub/Debug.cpp b/lib/libdvbsub/Debug.cpp new file mode 100644 index 000000000..f2df3b672 --- /dev/null +++ b/lib/libdvbsub/Debug.cpp @@ -0,0 +1,41 @@ +#include +#include +#include "Debug.hpp" + +Debug::Debug() : + file_(""), level_(0), fp_(stdout) +{ +} + +Debug::~Debug() +{ + if (fp_ && fp_ != stdout) { + fclose(fp_); + } +} + +void Debug::set_level(int level) +{ + level_ = level; +} + +FILE* Debug::set_file(char* file) +{ + FILE* fp = fopen(file, "a"); + if (!fp) { + return NULL; + } + fp_ = fp; + return fp_; +} + +void Debug::print(int level, const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + // if (level < level_) { + vfprintf(fp_, fmt, argp); + // } + va_end(argp); +} + diff --git a/lib/libdvbsub/Debug.hpp b/lib/libdvbsub/Debug.hpp new file mode 100644 index 000000000..01a446101 --- /dev/null +++ b/lib/libdvbsub/Debug.hpp @@ -0,0 +1,28 @@ +#ifndef DEBUG_HPP_ +#define DEBUG_HPP_ + +#include + +class Debug { +public: + Debug(); + ~Debug(); + + void set_level(int level); + FILE* set_file(char* file); + void print(int level, const char *fmt, ...); + + static const int ERROR = 0; + static const int INFO = 1; + static const int VERBOSE = 2; + +private: + char* file_; + int level_; + FILE* fp_; + +}; + +extern Debug sub_debug; + +#endif diff --git a/lib/libdvbsub/Makefile.am b/lib/libdvbsub/Makefile.am new file mode 100644 index 000000000..8b19a9925 --- /dev/null +++ b/lib/libdvbsub/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/lib/libcoolstream + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libdvbsub.a + +libdvbsub_a_SOURCES = dvbsub.cpp dvbsubtitle.cpp \ + tools.cpp osd.cpp PacketQueue.cpp helpers.cpp Debug.cpp diff --git a/lib/libdvbsub/PacketQueue.cpp b/lib/libdvbsub/PacketQueue.cpp new file mode 100644 index 000000000..8fc6a8455 --- /dev/null +++ b/lib/libdvbsub/PacketQueue.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "Debug.hpp" +#include "PacketQueue.hpp" + +PacketQueue::PacketQueue() +{ + pthread_mutex_init(&mutex, NULL); +} + +PacketQueue::~PacketQueue() +{ + while (queue.begin() != queue.end()) { + delete[] queue.front(); + queue.pop_front(); + } +} + +void PacketQueue::push(uint8_t* data) +{ + pthread_mutex_lock(&mutex); + + queue.push_back(data); + + pthread_mutex_unlock(&mutex); +} + +uint8_t* PacketQueue::pop() +{ + uint8_t* retval; + + pthread_mutex_lock(&mutex); + + retval = queue.front(); + queue.pop_front(); + + pthread_mutex_unlock(&mutex); + + return retval; +} + +size_t PacketQueue::size() +{ + return queue.size(); +} + diff --git a/lib/libdvbsub/PacketQueue.hpp b/lib/libdvbsub/PacketQueue.hpp new file mode 100644 index 000000000..f948ecdb2 --- /dev/null +++ b/lib/libdvbsub/PacketQueue.hpp @@ -0,0 +1,20 @@ +#ifndef PACKET_QUEUE_H_ +#define PACKET_QUEUE_H_ +#include +#include +#include + +class PacketQueue { +public: + PacketQueue(); + ~PacketQueue(); + void push(uint8_t* data); + uint8_t* pop(); + size_t size(); + +private: + std::list queue; + pthread_mutex_t mutex; +}; + +#endif diff --git a/lib/libdvbsub/dvbsub.cpp b/lib/libdvbsub/dvbsub.cpp new file mode 100644 index 000000000..0e57b7f0e --- /dev/null +++ b/lib/libdvbsub/dvbsub.cpp @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "Debug.hpp" +#include "PacketQueue.hpp" +#include "semaphore.h" +#include "reader_thread.hpp" +#include "dvbsub_thread.hpp" +#include "helpers.hpp" +#include "dvbsubtitle.h" + +#define Log2File printf +#define RECVBUFFER_STEPSIZE 1024 + +enum {NOERROR, NETWORK, DENIED, NOSERVICE, BOXTYPE, THREAD, ABOUT}; +enum {GET_VOLUME, SET_VOLUME, SET_MUTE, SET_CHANNEL}; + +Debug sub_debug; +static PacketQueue packet_queue; +//sem_t event_semaphore; + +static pthread_t threadReader; +static pthread_t threadDvbsub; + +static pthread_cond_t readerCond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t readerMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t packetCond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t packetMutex = PTHREAD_MUTEX_INITIALIZER; + +static int reader_running; +static int dvbsub_running; +static int dvbsub_paused = true; +static int dvbsub_pid; +static int dvbsub_stopped; + +cDvbSubtitleConverter *dvbSubtitleConverter; + +int dvbsub_init() { + int trc; + + sub_debug.set_level(42); + + reader_running = true; + // reader-Thread starten + trc = pthread_create(&threadReader, 0, reader_thread, (void *) NULL); + if (trc) { + fprintf(stderr, "[dvb-sub] failed to create reader-thread (rc=%d)\n", trc); + reader_running = false; + return -1; + } + + dvbsub_running = true; + // subtitle decoder-Thread starten + trc = pthread_create(&threadDvbsub, 0, dvbsub_thread, NULL); + if (trc) { + fprintf(stderr, "[dvb-sub] failed to create dvbsub-thread (rc=%d)\n", trc); + dvbsub_running = false; + return -1; + } + + return(0); +} + +int dvbsub_pause() +{ + if(reader_running) { + if(dvbSubtitleConverter) { + dvbSubtitleConverter->Pause(true); + } + dvbsub_paused = true; + printf("[dvb-sub] paused\n"); + } + + return 0; +} + +int dvbsub_start(int pid) +{ + if(!dvbsub_paused && (pid == 0)) { + return 0; + } + + + if(pid) { + if(pid != dvbsub_pid) + dvbsub_pause(); + dvbsub_pid = pid; + } + + while(!dvbsub_stopped) + usleep(10); + + if(dvbsub_pid > 0) { + dvbsub_paused = false; + dvbSubtitleConverter->Pause(false); + pthread_mutex_lock(&readerMutex); + pthread_cond_broadcast(&readerCond); + pthread_mutex_unlock(&readerMutex); + printf("[dvb-sub] started with pid 0x%x\n", pid); + } + + return 0; +} + +int dvbsub_stop() +{ + dvbsub_pid = 0; + if(reader_running) { + dvbsub_pause(); + } + + return 0; +} + +int dvbsub_getpid() +{ + return dvbsub_pid; +} + +int dvbsub_close() +{ + if(threadReader) { + dvbsub_pause(); + reader_running = false; + pthread_mutex_lock(&readerMutex); + pthread_cond_broadcast(&readerCond); + pthread_mutex_unlock(&readerMutex); + pthread_join(threadReader, NULL); + threadReader = NULL; + } + if(threadDvbsub) { + dvbsub_running = false; + pthread_join(threadDvbsub, NULL); + threadDvbsub = NULL; + } + printf("[dvb-sub] stopped\n"); + + return 0; +} + +static cDemux * dmx; + +void* reader_thread(void *arg) +{ + uint8_t tmp[16]; /* actually 6 should be enough */ + int count; + int len; + uint16_t packlen; + uint8_t* buf; + + dmx = new cDemux(0); + dmx->Open(DMX_PES_CHANNEL, NULL, 64*1024); + + while (reader_running) { + if(dvbsub_paused) { + sub_debug.print(Debug::VERBOSE, "%s stopped\n", __FUNCTION__); + dmx->Stop(); + dvbsub_stopped = 1; + pthread_mutex_lock(&readerMutex ); + int ret = pthread_cond_wait(&readerCond, &readerMutex); + pthread_mutex_unlock(&readerMutex); + if (ret) { + sub_debug.print(Debug::VERBOSE, "pthread_cond_timedwait fails with %d\n", ret); + } + if(reader_running) { + dmx->pesFilter(dvbsub_pid); + dmx->Start(); + sub_debug.print(Debug::VERBOSE, "%s started: pid 0x%x\n", __FUNCTION__, dvbsub_pid); + dvbsub_stopped = 0; + } else + break; + } + + /* Wait for pes sync */ + len = 0; + count = 0; + int tosync = 0; + + while (!dvbsub_paused) { + //len = read(fd, &tmp[count], 3-count); + len = dmx->Read(&tmp[count], 3-count, 1000); + //printf("reader: fd = %d, len = %d\n", fd, len); + if(len <= 0) + continue; + else if (len == (3-count)) { + if ( (tmp[0] == 0x00) && + (tmp[1] == 0x00) && + (tmp[2] == 0x01)) { + count = 3; + break; + } + tosync += len; + } else { + count += len; + tosync += len; + continue; + } + tmp[0] = tmp[1]; + tmp[1] = tmp[2]; + count = 2; + } // while (!dvbsub_paused) + if(tosync) + printf("[subtitles] sync after %d bytes\n", tosync); + /* read stream id & length */ + while ((count < 6) && !dvbsub_paused) { + //printf("try to read 6-count = %d\n", 6-count); + //len = read(fd, &tmp[count], 6-count); + len = dmx->Read(&tmp[count], 6-count, 1000); + if (len < 0) { + continue; + } else { + count += len; + } + } + + packlen = getbits(tmp, 4*8, 16) + 6; + + buf = new uint8_t[packlen]; + + /* TODO: Throws an exception on out of memory */ + + /* copy tmp[0..5] => buf[0..5] */ + *(uint32_t*)buf = *(uint32_t*)tmp; + ((uint16_t*)buf)[2] = ((uint16_t*)tmp)[2]; + + /* read rest of the packet */ + while((count < packlen) && !dvbsub_paused) { + //len = read(fd, buf+count, packlen-count); + len = dmx->Read(buf+count, packlen-count, 1000); + if (len < 0) { + continue; + } else { + count += len; + } + } + if(!dvbsub_paused) { + printf("[subtitles] adding packet, len %d\n", count); + /* Packet now in memory */ + packet_queue.push(buf); + /* TODO: allocation exception */ + // wake up dvb thread + pthread_mutex_lock(&packetMutex); + pthread_cond_broadcast(&packetCond); + pthread_mutex_unlock(&packetMutex); + } else { + delete[] buf; + buf=NULL; + } + } + + dmx->Stop(); + delete dmx; + dmx = NULL; + + sub_debug.print(Debug::VERBOSE, "%s shutdown\n", __FUNCTION__); + pthread_exit(NULL); +} + +void* dvbsub_thread(void* arg) +{ + struct timespec restartWait; + struct timeval now; + + sub_debug.print(Debug::VERBOSE, "%s started\n", __FUNCTION__); + if (!dvbSubtitleConverter) + dvbSubtitleConverter = new cDvbSubtitleConverter; + + while(dvbsub_running) { + uint8_t* packet; + int64_t pts; + int pts_dts_flag; + int dataoffset; + int packlen; + + gettimeofday(&now, NULL); + TIMEVAL_TO_TIMESPEC(&now, &restartWait); + restartWait.tv_sec += 1; + + int ret = 0; + if(packet_queue.size() == 0) { + pthread_mutex_lock( &packetMutex ); + ret = pthread_cond_timedwait( &packetCond, &packetMutex, &restartWait ); + pthread_mutex_unlock( &packetMutex ); + } + if (ret == ETIMEDOUT) + { + dvbSubtitleConverter->Action(); + continue; + } + else if (ret == EINTR) + { + sub_debug.print(Debug::VERBOSE, "pthread_cond_timedwait fails with %s\n", strerror(errno)); + } + sub_debug.print(Debug::VERBOSE, "\nPES: Wakeup, queue size %d\n", packet_queue.size()); + if(dvbsub_paused) { + do { + packet = packet_queue.pop(); + if(packet) + delete[] packet; + } while(packet); + continue; + } + packet = packet_queue.pop(); + if (!packet) { + sub_debug.print(Debug::VERBOSE, "Error no packet found\n"); + continue; + } + packlen = (packet[4] << 8 | packet[5]) + 6; + + /* Get PTS */ + pts_dts_flag = getbits(packet, 7*8, 2); + if ((pts_dts_flag == 2) || (pts_dts_flag == 3)) { + pts = (uint64_t)getbits(packet, 9*8+4, 3) << 30; /* PTS[32..30] */ + pts |= getbits(packet, 10*8, 15) << 15; /* PTS[29..15] */ + pts |= getbits(packet, 12*8, 15); /* PTS[14..0] */ + } else { + pts = 0; + } + + dataoffset = packet[8] + 8 + 1; + if (packet[dataoffset] != 0x20) { + sub_debug.print(Debug::VERBOSE, "Not a dvb subtitle packet, discard it (len %d)\n", packlen); + for(int i = 0; i < packlen; i++) + printf("%02X ", packet[i]); + printf("\n"); + goto next_round; + } + + sub_debug.print(Debug::VERBOSE, "PES packet: len %d PTS=%Ld (%02d:%02d:%02d.%d)\n", + packlen, pts, (int)(pts/324000000), (int)((pts/5400000)%60), + (int)((pts/90000)%60), (int)(pts%90000)); + + if (packlen <= dataoffset + 3) { + sub_debug.print(Debug::INFO, "Packet too short, discard\n"); + goto next_round; + } + + if (packet[dataoffset + 2] == 0x0f) { + dvbSubtitleConverter->Convert(&packet[dataoffset + 2], + packlen - (dataoffset + 2), pts); + } else { + sub_debug.print(Debug::INFO, "End_of_PES is missing\n"); + } + dvbSubtitleConverter->Action(); + +next_round: + delete[] packet; + } + + delete dvbSubtitleConverter; + + sub_debug.print(Debug::VERBOSE, "%s shutdown\n", __FUNCTION__); + pthread_exit(NULL); +} + +void dvbsub_get_stc(int64_t * STC) +{ + if(dmx) + dmx->getSTC(STC); +} diff --git a/lib/libdvbsub/dvbsub_thread.hpp b/lib/libdvbsub/dvbsub_thread.hpp new file mode 100644 index 000000000..f4d240ab1 --- /dev/null +++ b/lib/libdvbsub/dvbsub_thread.hpp @@ -0,0 +1,8 @@ +#ifndef DVBSUB_THREAD_HPP_ +#define DVBSUB_THREAD_HPP_ + +#include + +void* dvbsub_thread(void* arg); + +#endif diff --git a/lib/libdvbsub/dvbsubtitle.cpp b/lib/libdvbsub/dvbsubtitle.cpp new file mode 100644 index 000000000..09c18b127 --- /dev/null +++ b/lib/libdvbsub/dvbsubtitle.cpp @@ -0,0 +1,1200 @@ +/* + * dvbsubtitle.c: DVB subtitles + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * Original author: Marco Schlüßler + * With some input from the "subtitle plugin" by Pekka Virtanen + * + * $Id: dvbsubtitle.cpp,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $ + * dvbsubtitle for HD1 ported by Coolstream LTD + */ + +#include "dvbsubtitle.h" + +extern "C" { +#include +#include +#include +#include +} +#include "driver/framebuffer.h" + +#define FB "/dev/fb/0" +extern int fb_fd; +//struct fb_fix_screeninfo fix_screeninfo; +//struct fb_var_screeninfo var_screeninfo; + +#define PAGE_COMPOSITION_SEGMENT 0x10 +#define REGION_COMPOSITION_SEGMENT 0x11 +#define CLUT_DEFINITION_SEGMENT 0x12 +#define OBJECT_DATA_SEGMENT 0x13 +#define END_OF_DISPLAY_SET_SEGMENT 0x80 + +// Set these to 'true' for debug output: +#if 1 +static bool DebugConverter = true; +static bool DebugSegments = false; +static bool DebugPages = false; +static bool DebugRegions = false; +static bool DebugObjects = false; +static bool DebugCluts = false; +#else +static bool DebugConverter = true; +static bool DebugSegments = true; +static bool DebugPages = true; +static bool DebugRegions = true; +static bool DebugObjects = true; +static bool DebugCluts = true; +#endif + +#define dbgconverter(a...) if (DebugConverter) fprintf(stderr, a) +#define dbgsegments(a...) if (DebugSegments) fprintf(stderr, a) +#define dbgpages(a...) if (DebugPages) fprintf(stderr, a) +#define dbgregions(a...) if (DebugRegions) fprintf(stderr, a) +#define dbgobjects(a...) if (DebugObjects) fprintf(stderr, a) +#define dbgcluts(a...) if (DebugCluts) fprintf(stderr, a) + +int SubtitleFgTransparency = 0; +int SubtitleBgTransparency = 0; + +// --- cSubtitleClut --------------------------------------------------------- + +class cSubtitleClut : public cListObject { +private: + int clutId; + int version; + cPalette palette2; + cPalette palette4; + cPalette palette8; +public: + cSubtitleClut(int ClutId); + int ClutId(void) { return clutId; } + int Version(void) { return version; } + void SetVersion(int Version) { version = Version; } + void SetColor(int Bpp, int Index, tColor Color); + const cPalette *GetPalette(int Bpp); + }; + +cSubtitleClut::cSubtitleClut(int ClutId) +:palette2(2) +,palette4(4) +,palette8(8) +{ + clutId = ClutId; + version = -1; +} + +void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color) +{ + switch (Bpp) { + case 2: palette2.SetColor(Index, Color); break; + case 4: palette4.SetColor(Index, Color); break; + case 8: palette8.SetColor(Index, Color); break; + default: esyslog("ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)", Bpp, Index, Color); + } +} + +const cPalette *cSubtitleClut::GetPalette(int Bpp) +{ + switch (Bpp) { + case 2: return &palette2; + case 4: return &palette4; + case 8: return &palette8; + default: esyslog("ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)", Bpp); + } + return &palette8; +} + +// --- cSubtitleObject ------------------------------------------------------- + +class cSubtitleObject : public cListObject { +private: + int objectId; + int version; + int codingMethod; + bool nonModifyingColorFlag; + int nibblePos; + uchar backgroundColor; + uchar foregroundColor; + int providerFlag; + int px; + int py; + cBitmap *bitmap; + void DrawLine(int x, int y, tIndex Index, int Length); + uchar Get2Bits(const uchar *Data, int &Index); + uchar Get4Bits(const uchar *Data, int &Index); + bool Decode2BppCodeString(const uchar *Data, int &Index, int&x, int y); + bool Decode4BppCodeString(const uchar *Data, int &Index, int&x, int y); + bool Decode8BppCodeString(const uchar *Data, int &Index, int&x, int y); +public: + cSubtitleObject(int ObjectId, cBitmap *Bitmap); + int ObjectId(void) { return objectId; } + int Version(void) { return version; } + int CodingMethod(void) { return codingMethod; } + bool NonModifyingColorFlag(void) { return nonModifyingColorFlag; } + void DecodeSubBlock(const uchar *Data, int Length, bool Even); + void SetVersion(int Version) { version = Version; } + void SetBackgroundColor(uchar BackgroundColor) { backgroundColor = BackgroundColor; } + void SetForegroundColor(uchar ForegroundColor) { foregroundColor = ForegroundColor; } + void SetNonModifyingColorFlag(bool NonModifyingColorFlag) { nonModifyingColorFlag = NonModifyingColorFlag; } + void SetCodingMethod(int CodingMethod) { codingMethod = CodingMethod; } + void SetPosition(int x, int y) { px = x; py = y; } + void SetProviderFlag(int ProviderFlag) { providerFlag = ProviderFlag; } + }; + +cSubtitleObject::cSubtitleObject(int ObjectId, cBitmap *Bitmap) +{ + objectId = ObjectId; + version = -1; + codingMethod = -1; + nonModifyingColorFlag = false; + nibblePos = 0; + backgroundColor = 0; + foregroundColor = 0; + providerFlag = -1; + px = py = 0; + bitmap = Bitmap; +} + +void cSubtitleObject::DecodeSubBlock(const uchar *Data, int Length, bool Even) +{ + int x = 0; + int y = Even ? 0 : 1; + for (int index = 0; index < Length; ) { + switch (Data[index++]) { + case 0x10: { + nibblePos = 8; + while (Decode2BppCodeString(Data, index, x, y) && index < Length) + ; + if (!nibblePos) + index++; + break; + } + case 0x11: { + nibblePos = 4; + while (Decode4BppCodeString(Data, index, x, y) && index < Length) + ; + if (!nibblePos) + index++; + break; + } + case 0x12: + while (Decode8BppCodeString(Data, index, x, y) && index < Length) + ; + break; + case 0x20: //TODO + dbgobjects("sub block 2 to 4 map"); + index += 4; + break; + case 0x21: //TODO + dbgobjects("sub block 2 to 8 map"); + index += 4; + break; + case 0x22: //TODO + dbgobjects("sub block 4 to 8 map"); + index += 16; + break; + case 0xF0: + x = 0; + y += 2; + break; + } + } +} + +void cSubtitleObject::DrawLine(int x, int y, tIndex Index, int Length) +{ + if (nonModifyingColorFlag && Index == 1) + return; + x += px; + y += py; + for (int pos = x; pos < x + Length; pos++) + bitmap->SetIndex(pos, y, Index); +} + +uchar cSubtitleObject::Get2Bits(const uchar *Data, int &Index) +{ + uchar result = Data[Index]; + if (!nibblePos) { + Index++; + nibblePos = 8; + } + nibblePos -= 2; + return (result >> nibblePos) & 0x03; +} + +uchar cSubtitleObject::Get4Bits(const uchar *Data, int &Index) +{ + uchar result = Data[Index]; + if (!nibblePos) { + Index++; + nibblePos = 4; + } + else { + result >>= 4; + nibblePos -= 4; + } + return result & 0x0F; +} + +bool cSubtitleObject::Decode2BppCodeString(const uchar *Data, int &Index, int &x, int y) +{ + int rl = 0; + int color = 0; + uchar code = Get2Bits(Data, Index); + if (code) { + color = code; + rl = 1; + } + else { + code = Get2Bits(Data, Index); + if (code & 2) { // switch_1 + rl = ((code & 1) << 2) + Get2Bits(Data, Index) + 3; + color = Get2Bits(Data, Index); + } + else if (code & 1) + rl = 1; //color 0 + else { + code = Get2Bits(Data, Index); + switch (code & 0x3) { //switch_3 + case 0: + return false; + case 1: + rl = 2; //color 0 + break; + case 2: + rl = (Get2Bits(Data, Index) << 2) + Get2Bits(Data, Index) + 12; + color = Get2Bits(Data, Index); + break; + case 3: + rl = (Get2Bits(Data, Index) << 6) + (Get2Bits(Data, Index) << 4) + (Get2Bits(Data, Index) << 2) + Get2Bits(Data, Index) + 29; + color = Get2Bits(Data, Index); + break; + } + } + } + DrawLine(x, y, color, rl); + x += rl; + return true; +} + +bool cSubtitleObject::Decode4BppCodeString(const uchar *Data, int &Index, int &x, int y) +{ + int rl = 0; + int color = 0; + uchar code = Get4Bits(Data, Index); + if (code) { + color = code; + rl = 1; + } + else { + code = Get4Bits(Data, Index); + if (code & 8) { // switch_1 + if (code & 4) { //switch_2 + switch (code & 3) { //switch_3 + case 0: // color 0 + rl = 1; + break; + case 1: // color 0 + rl = 2; + break; + case 2: + rl = Get4Bits(Data, Index) + 9; + color = Get4Bits(Data, Index); + break; + case 3: + rl = (Get4Bits(Data, Index) << 4) + Get4Bits(Data, Index) + 25; + color = Get4Bits(Data, Index); + break; + } + } + else { + rl = (code & 3) + 4; + color = Get4Bits(Data, Index); + } + } + else { // color 0 + if (!code) + return false; + rl = code + 2; + } + } + DrawLine(x, y, color, rl); + x += rl; + return true; +} + +bool cSubtitleObject::Decode8BppCodeString(const uchar *Data, int &Index, int &x, int y) +{ + int rl = 0; + int color = 0; + uchar code = Data[Index++]; + if (code) { + color = code; + rl = 1; + } + else { + code = Data[Index++]; + rl = code & 0x63; + if (code & 0x80) + color = Data[Index++]; + else if (!rl) + return false; //else color 0 + } + DrawLine(x, y, color, rl); + x += rl; + return true; +} + +// --- cSubtitleRegion ------------------------------------------------------- + +class cSubtitleRegion : public cListObject, public cBitmap { +private: + int regionId; + int version; + int clutId; + int horizontalAddress; + int verticalAddress; + int level; + cList objects; +public: + cSubtitleRegion(int RegionId); + int RegionId(void) { return regionId; } + int Version(void) { return version; } + int ClutId(void) { return clutId; } + int Level(void) { return level; } + int Depth(void) { return Bpp(); } + void FillRegion(tIndex Index); + cSubtitleObject *GetObjectById(int ObjectId, bool New = false); + int HorizontalAddress(void) { return horizontalAddress; } + int VerticalAddress(void) { return verticalAddress; } + void SetVersion(int Version) { version = Version; } + void SetClutId(int ClutId) { clutId = ClutId; } + void SetLevel(int Level); + void SetDepth(int Depth); + void SetHorizontalAddress(int HorizontalAddress) { horizontalAddress = HorizontalAddress; } + void SetVerticalAddress(int VerticalAddress) { verticalAddress = VerticalAddress; } + }; + +cSubtitleRegion::cSubtitleRegion(int RegionId) +:cBitmap(1, 1, 4) +{ + regionId = RegionId; + version = -1; + clutId = -1; + horizontalAddress = 0; + verticalAddress = 0; + level = 0; +} + +void cSubtitleRegion::FillRegion(tIndex Index) +{ + dbgregions("FillRegion %d\n", Index); + for (int y = 0; y < Height(); y++) { + for (int x = 0; x < Width(); x++) + SetIndex(x, y, Index); + } +} + +cSubtitleObject *cSubtitleRegion::GetObjectById(int ObjectId, bool New) +{ + cSubtitleObject *result = NULL; + for (cSubtitleObject *so = objects.First(); so; so = objects.Next(so)) { + if (so->ObjectId() == ObjectId) + result = so; + } + if (!result && New) { + result = new cSubtitleObject(ObjectId, this); + objects.Add(result); + } + return result; +} + +void cSubtitleRegion::SetLevel(int Level) +{ + if (Level > 0 && Level < 4) + level = 1 << Level; +} + +void cSubtitleRegion::SetDepth(int Depth) +{ + if (Depth > 0 && Depth < 4) + SetBpp(1 << Depth); +} + +// --- cDvbSubtitlePage ------------------------------------------------------ + +class cDvbSubtitlePage : public cListObject { +private: + int pageId; + int version; + int state; + int64_t pts; + int timeout; + cList cluts; +public: + cList regions; + cDvbSubtitlePage(int PageId); + virtual ~cDvbSubtitlePage(); + int PageId(void) { return pageId; } + int Version(void) { return version; } + int State(void) { return state; } + tArea *GetAreas(void); + cSubtitleClut *GetClutById(int ClutId, bool New = false); + cSubtitleObject *GetObjectById(int ObjectId); + cSubtitleRegion *GetRegionById(int RegionId, bool New = false); + int64_t Pts(void) const { return pts; } + int Timeout(void) { return timeout; } + void SetVersion(int Version) { version = Version; } + void SetPts(int64_t Pts) { pts = Pts; } + void SetState(int State); + void SetTimeout(int Timeout) { timeout = Timeout; } + void UpdateRegionPalette(cSubtitleClut *Clut); +}; + +cDvbSubtitlePage::cDvbSubtitlePage(int PageId) +{ + pageId = PageId; + version = -1; + state = -1; + pts = 0; + timeout = 0; +} + +cDvbSubtitlePage::~cDvbSubtitlePage() +{ +} + +tArea *cDvbSubtitlePage::GetAreas(void) +{ + if (regions.Count() > 0) { + tArea *Areas = new tArea[regions.Count()]; + tArea *a = Areas; + for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) { + a->x1 = sr->HorizontalAddress(); + a->y1 = sr->VerticalAddress(); + a->x2 = sr->HorizontalAddress() + sr->Width() - 1; + a->y2 = sr->VerticalAddress() + sr->Height() - 1; + a->bpp = sr->Bpp(); + while ((a->Width() & 3) != 0) + a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work + a++; + } + return Areas; + } + return NULL; +} + +cSubtitleClut *cDvbSubtitlePage::GetClutById(int ClutId, bool New) +{ + cSubtitleClut *result = NULL; + for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) { + if (sc->ClutId() == ClutId) + result = sc; + } + if (!result && New) { + result = new cSubtitleClut(ClutId); + cluts.Add(result); + } + return result; +} + +cSubtitleRegion *cDvbSubtitlePage::GetRegionById(int RegionId, bool New) +{ + cSubtitleRegion *result = NULL; + for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) { + if (sr->RegionId() == RegionId) + result = sr; + } + if (!result && New) { + result = new cSubtitleRegion(RegionId); + regions.Add(result); + } + return result; +} + +cSubtitleObject *cDvbSubtitlePage::GetObjectById(int ObjectId) +{ + cSubtitleObject *result = NULL; + for (cSubtitleRegion *sr = regions.First(); sr && !result; sr = regions.Next(sr)) + result = sr->GetObjectById(ObjectId); + return result; +} + +void cDvbSubtitlePage::SetState(int State) +{ + state = State; + switch (state) { + case 0: // normal case - page update + dbgpages("page update\n"); + break; + case 1: // aquisition point - page refresh + dbgpages("page refresh\n"); + regions.Clear(); + break; + case 2: // mode change - new page + dbgpages("new Page\n"); + regions.Clear(); + cluts.Clear(); + break; + case 3: // reserved + break; + } +} + +void cDvbSubtitlePage::UpdateRegionPalette(cSubtitleClut *Clut) +{ + for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) { + if (sr->ClutId() == Clut->ClutId()) + sr->Replace(*Clut->GetPalette(sr->Bpp())); + } +} + +// --- cDvbSubtitleAssembler ------------------------------------------------- + +class cDvbSubtitleAssembler { +private: + uchar *data; + int length; + int pos; + int size; + bool Realloc(int Size); +public: + cDvbSubtitleAssembler(void); + virtual ~cDvbSubtitleAssembler(); + void Reset(void); + unsigned char *Get(int &Length); + void Put(const uchar *Data, int Length); + }; + +cDvbSubtitleAssembler::cDvbSubtitleAssembler(void) +{ + data = NULL; + size = 0; + Reset(); +} + +cDvbSubtitleAssembler::~cDvbSubtitleAssembler() +{ + free(data); +} + +void cDvbSubtitleAssembler::Reset(void) +{ + length = 0; + pos = 0; +} + +bool cDvbSubtitleAssembler::Realloc(int Size) +{ + if (Size > size) { + size = max(Size, 2048); + data = (uchar *)realloc(data, size); + if (!data) { + esyslog("ERROR: can't allocate memory for subtitle assembler"); + length = 0; + size = 0; + return false; + } + } + return true; +} + +unsigned char *cDvbSubtitleAssembler::Get(int &Length) +{ + if (length > pos + 5) { + Length = (data[pos + 4] << 8) + data[pos + 5] + 6; + if (length >= pos + Length) { + unsigned char *result = data + pos; + pos += Length; + return result; + } + } + return NULL; +} + +void cDvbSubtitleAssembler::Put(const uchar *Data, int Length) +{ + if (Length && Realloc(length + Length)) { + memcpy(data + length, Data, Length); + length += Length; + } +} + +// --- cDvbSubtitleBitmaps --------------------------------------------------- + +class cDvbSubtitleBitmaps : public cListObject { +private: + int64_t pts; + int timeout; + tArea *areas; + int numAreas; + cVector bitmaps; +// int min_x, min_y, max_x, max_y; +public: + cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas); + ~cDvbSubtitleBitmaps(); + int64_t Pts(void) { return pts; } + int Timeout(void) { return timeout; } + void AddBitmap(cBitmap *Bitmap); + void Draw(); + void Clear(void); + }; + +cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas) +{ + pts = Pts; + timeout = Timeout; + areas = Areas; + numAreas = NumAreas; +// max_x = max_y = 0; +// min_x = min_y = 0xFFFF; + //dbgconverter("cDvbSubtitleBitmaps::new: PTS: %lld\n", pts); +} + +cDvbSubtitleBitmaps::~cDvbSubtitleBitmaps() +{ + //dbgconverter("cDvbSubtitleBitmaps::delete: PTS: %lld\n", pts); + delete[] areas; + for (int i = 0; i < bitmaps.Size(); i++) + delete bitmaps[i]; +} + +void cDvbSubtitleBitmaps::AddBitmap(cBitmap *Bitmap) +{ + bitmaps.Append(Bitmap); + //dbgconverter("cDvbSubtitleBitmaps::AddBitmap size %d PTS: %lld\n", bitmaps.Size(), pts); +} + +static int min_x = 0xFFFF, min_y = 0xFFFF; +static int max_x = 0, max_y = 0; + +void cDvbSubtitleBitmaps::Clear() +{ + dbgconverter("cDvbSubtitleBitmaps::Draw: clear x=% d y= %d, w= %d, h= %d\n", min_x, min_y, max_x-min_x, max_y-min_y); + if(max_x && max_y) { + CFrameBuffer::getInstance()->paintBackgroundBoxRel (min_x, min_y, max_x-min_x, max_y-min_y); + max_x = max_y = 0; + } +} + +void cDvbSubtitleBitmaps::Draw() +{ + static tColor save_colors[MAXNUMCOLORS]; + + int stride = CFrameBuffer::getInstance()->getScreenWidth(true); + int wd = CFrameBuffer::getInstance()->getScreenWidth(); + int xs = CFrameBuffer::getInstance()->getScreenX(); + int yend = CFrameBuffer::getInstance()->getScreenY() + CFrameBuffer::getInstance()->getScreenHeight(); + uint32_t *sublfb = CFrameBuffer::getInstance()->getFrameBufferPointer(); + + dbgconverter("cDvbSubtitleBitmaps::Draw: %d bitmaps, x= %d, width= %d end=%d stride %d\n", bitmaps.Size(), xs, wd, yend, stride); + + if(bitmaps.Size()) + Clear(); //FIXME should we clear for new bitmaps set ? + + for (int i = 0; i < bitmaps.Size(); i++) { + + int NumColors; + const tColor *Colors = bitmaps[i]->Colors(NumColors); + if (Colors) { + memcpy(save_colors, Colors, sizeof(tColor)*NumColors); + } + /* center on screen */ + int xoff = xs + (wd - bitmaps[i]->Width())/2; + /* move to screen bottom */ + int yoff = (yend - (576-bitmaps[i]->Y0()))*stride; + int ys = yend - (576-bitmaps[i]->Y0()); + + dbgconverter("cDvbSubtitleBitmaps::Draw %d colors= %d at %d,%d (x=%d y=%d) size %dx%d\n", + i, NumColors, bitmaps[i]->X0(), bitmaps[i]->Y0(), xoff, ys, bitmaps[i]->Width(), bitmaps[i]->Height()); + + for (int y2 = 0; y2 < bitmaps[i]->Height(); y2++) { + int y = y2*stride + yoff; + for (int x2 = 0; x2 < bitmaps[i]->Width(); x2++) + { + int idx = *(bitmaps[i]->Data(x2, y2)); + *(sublfb + xoff + x2 + y) = save_colors[idx]; + } + } + if(min_x > xoff) + min_x = xoff; + if(min_y > ys) + min_y = ys; + if(max_x < (xoff + bitmaps[i]->Width())) + max_x = xoff + bitmaps[i]->Width(); + if(max_y < (ys+bitmaps[i]->Height())) + max_y = ys+bitmaps[i]->Height(); + } + if(bitmaps.Size()) + dbgconverter("cDvbSubtitleBitmaps::Draw: finish, min/max screen: x=% d y= %d, w= %d, h= %d\n", min_x, min_y, max_x-min_x, max_y-min_y); +} + +// --- cDvbSubtitleConverter ------------------------------------------------- + +int cDvbSubtitleConverter::setupLevel = 0; + +cDvbSubtitleConverter::cDvbSubtitleConverter(void) +//:cThread("subtitleConverter") +{ + dvbSubtitleAssembler = new cDvbSubtitleAssembler; + pages = new cList; + bitmaps = new cList; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); + pthread_mutex_init(&mutex, &attr); + running = false; +// Start(); +} + +cDvbSubtitleConverter::~cDvbSubtitleConverter() +{ +// Cancel(3); + delete dvbSubtitleAssembler; + delete bitmaps; + delete pages; +} + +void cDvbSubtitleConverter::Lock(void) +{ + pthread_mutex_lock(&mutex); +} + +void cDvbSubtitleConverter::Unlock(void) +{ + pthread_mutex_unlock(&mutex); +} + +void cDvbSubtitleConverter::Pause(bool pause) +{ + dbgconverter("cDvbSubtitleConverter::Pause: %s\n", pause ? "pause" : "resume"); + if(pause) { + if(!running) + return; + Lock(); + running = false; + Clear(); + Unlock(); + Reset(); + } else { + running = true; + } +} + +void cDvbSubtitleConverter::Clear(void) +{ + if(max_x && max_y) { + dbgconverter("cDvbSubtitleConverter::Draw: clear x=% d y= %d, w= %d, h= %d\n", min_x, min_y, max_x-min_x, max_y-min_y); + CFrameBuffer::getInstance()->paintBackgroundBoxRel (min_x, min_y, max_x-min_x, max_y-min_y); + max_x = max_y = 0; + } +} + +void cDvbSubtitleConverter::SetupChanged(void) +{ + setupLevel++; +} + +void cDvbSubtitleConverter::Reset(void) +{ + dbgconverter("Converter reset -----------------------\n"); + dvbSubtitleAssembler->Reset(); + Lock(); + pages->Clear(); + bitmaps->Clear(); + Unlock(); +} + +int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length, int64_t pts) +{ + if (Data && Length > 8) { + int PayloadOffset = 0;//PesPayloadOffset(Data); + int SubstreamHeaderLength = 4; + bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00; + + // Compatibility mode for old subtitles plugin: +#if 0 + if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) { + PayloadOffset--; + SubstreamHeaderLength = 1; + ResetSubtitleAssembler = Data[8] >= 5; + } +#endif + + if (Length > PayloadOffset + SubstreamHeaderLength) { +// int64_t pts = 0;//PesGetPts(Data); + //if (pts) + // dbgconverter("Converter PTS: %lld\n", pts); + const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header + int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header + if (ResetSubtitleAssembler) + dvbSubtitleAssembler->Reset(); + + if (length > 3) { + if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) + dvbSubtitleAssembler->Put(data + 2, length - 2); + else + dvbSubtitleAssembler->Put(data, length); + + int Count; + while (true) { + unsigned char *b = dvbSubtitleAssembler->Get(Count); + if (b && b[0] == 0x0F) { + if (ExtractSegment(b, Count, pts) == -1) + break; + } + else { + break; + } + } + } + } + return Length; + } + return 0; +} + +int cDvbSubtitleConverter::Convert(const uchar *Data, int Length, int64_t pts) +{ + if (Data && Length > 8) { + int PayloadOffset = 0;//PesPayloadOffset(Data); + if (Length > PayloadOffset) { +// int64_t pts = 0;//PesGetPts(Data); + //if (pts) + // dbgconverter("Converter PTS: %lld\n", pts); + const uchar *data = Data + PayloadOffset; + int length = Length - PayloadOffset; + if (length > 3) { + if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) { + data += 2; + length -= 2; + } + const uchar *b = data; + while (length > 0) { + if (b[0] == 0x0F) { + int n = ExtractSegment(b, length, pts); + if (n < 0) + break; + b += n; + length -= n; + } + else + break; + } + } + } + return Length; + } + return 0; +} + +#define LimitTo32Bit(n) (n & 0x00000000FFFFFFFFL) +#define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms + +void dvbsub_get_stc(int64_t * STC); + +void cDvbSubtitleConverter::Action(void) +{ + static cTimeMs Timeout(0xFFFF*1000); + int WaitMs = 100; + + if(!running) + return; + + Lock(); + if (cDvbSubtitleBitmaps *sb = bitmaps->First()) { + int64_t STC; + dvbsub_get_stc(&STC); + int64_t Delta = 0; + + Delta = sb->Pts() - STC; + Delta /= 90; // STC and PTS are in 1/90000s + dbgconverter("cDvbSubtitleConverter::Action: PTS: %lld STC: %lld (%lld) timeout: %d\n", sb->Pts(), STC, Delta, sb->Timeout()); + + if(Delta > 1800) { + Unlock(); + usleep((Delta-500)*1000); + Lock(); + if(!running) { + Unlock(); + return; + } +#if 1 // debug + dvbsub_get_stc(&STC); + Delta = sb->Pts() - STC; + dbgconverter("cDvbSubtitleConverter::Action: PTS: %lld STC: %lld (%lld) timeout: %d after sleep\n", sb->Pts(), STC, Delta/90, sb->Timeout()); +#endif + } + Delta = 0; + if (Delta <= MAXDELTA) { + if (Delta <= 0) { + dbgconverter("cDvbSubtitleConverter::Action: Got %d bitmaps, showing #%d\n", bitmaps->Count(), sb->Index() + 1); + if (running) { + sb->Draw(); + Timeout.Set(sb->Timeout() * 100);//max: was 1000 and timeout seems in 1/10 of sec ?? + } + bitmaps->Del(sb, true); + } + else if (Delta < WaitMs) + WaitMs = Delta; + } + else + bitmaps->Del(sb, true); + } else { + //printf("cDvbSubtitleConverter::Action: timeout elapsed %lld\n", Timeout.Elapsed()); + if (Timeout.TimedOut()) { + //printf("************************************ cDvbSubtitleConverter::Action: we timed out\n"); + Timeout.Set(0xFFFF*1000); + Clear(); + } + } + Unlock(); +} + +tColor cDvbSubtitleConverter::yuv2rgb(int Y, int Cb, int Cr) +{ + int Ey, Epb, Epr; + int Eg, Eb, Er; + + Ey = (Y - 16); + Epb = (Cb - 128); + Epr = (Cr - 128); + /* ITU-R 709 */ + Er = max(min(((298 * Ey + 460 * Epr) / 256), 255), 0); + Eg = max(min(((298 * Ey - 55 * Epb - 137 * Epr) / 256), 255), 0); + Eb = max(min(((298 * Ey + 543 * Epb ) / 256), 255), 0); + + return (Er << 16) | (Eg << 8) | Eb; +} + +bool cDvbSubtitleConverter::AssertOsd(void) +{ + return 1;//osd || (osd = cOsdProvider::NewOsd(0, Setup.SubtitleOffset, OSD_LEVEL_SUBTITLES)); +} + +int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts) +{ + if (Length > 5 && Data[0] == 0x0F) { + int segmentLength = (Data[4] << 8) + Data[5] + 6; + if (segmentLength > Length) + return -1; + int segmentType = Data[1]; + int pageId = (Data[2] << 8) + Data[3]; + cDvbSubtitlePage *page = NULL; +// LOCK_THREAD; + for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) { + if (sp->PageId() == pageId) { + page = sp; + break; + } + } + if (!page) { + page = new cDvbSubtitlePage(pageId); + pages->Add(page); + dbgpages("Create SubtitlePage %d (total pages = %d)\n", pageId, pages->Count()); + } + if (Pts) + page->SetPts(Pts); + switch (segmentType) { + case PAGE_COMPOSITION_SEGMENT: { + dbgsegments("PAGE_COMPOSITION_SEGMENT\n"); + int pageVersion = (Data[6 + 1] & 0xF0) >> 4; + if (pageVersion == page->Version()) + break; // no update + page->SetVersion(pageVersion); + page->SetTimeout(Data[6]); + page->SetState((Data[6 + 1] & 0x0C) >> 2); + page->regions.Clear(); + dbgpages("Update page id %d version %d pts %lld timeout %d state %d\n", pageId, page->Version(), page->Pts(), page->Timeout(), page->State()); + for (int i = 6 + 2; i < segmentLength; i += 6) { + cSubtitleRegion *region = page->GetRegionById(Data[i], true); + region->SetHorizontalAddress((Data[i + 2] << 8) + Data[i + 3]); + region->SetVerticalAddress((Data[i + 4] << 8) + Data[i + 5]); + } + break; + } + case REGION_COMPOSITION_SEGMENT: { + dbgsegments("REGION_COMPOSITION_SEGMENT\n"); + cSubtitleRegion *region = page->GetRegionById(Data[6]); + if (!region) + break; + int regionVersion = (Data[6 + 1] & 0xF0) >> 4; + if (regionVersion == region->Version()) + break; // no update + region->SetVersion(regionVersion); + bool regionFillFlag = (Data[6 + 1] & 0x08) >> 3; + int regionWidth = (Data[6 + 2] << 8) | Data[6 + 3]; + int regionHeight = (Data[6 + 4] << 8) | Data[6 + 5]; + region->SetSize(regionWidth, regionHeight); + region->SetLevel((Data[6 + 6] & 0xE0) >> 5); + region->SetDepth((Data[6 + 6] & 0x1C) >> 2); + region->SetClutId(Data[6 + 7]); + dbgregions("Region pageId %d id %d version %d fill %d width %d height %d level %d depth %d clutId %d\n", pageId, region->RegionId(), region->Version(), regionFillFlag, regionWidth, regionHeight, region->Level(), region->Depth(), region->ClutId()); + if (regionFillFlag) { + switch (region->Bpp()) { + case 2: region->FillRegion((Data[6 + 9] & 0x0C) >> 2); break; + case 4: region->FillRegion((Data[6 + 9] & 0xF0) >> 4); break; + case 8: region->FillRegion(Data[6 + 8]); break; + } + } + for (int i = 6 + 10; i < segmentLength; i += 6) { + cSubtitleObject *object = region->GetObjectById((Data[i] << 8) | Data[i + 1], true); + int objectType = (Data[i + 2] & 0xC0) >> 6; + object->SetCodingMethod(objectType); + object->SetProviderFlag((Data[i + 2] & 0x30) >> 4); + int objectHorizontalPosition = ((Data[i + 2] & 0x0F) << 8) | Data[i + 3]; + int objectVerticalPosition = ((Data[i + 4] & 0x0F) << 8) | Data[i + 5]; + object->SetPosition(objectHorizontalPosition, objectVerticalPosition); + if (objectType == 0x01 || objectType == 0x02) { + object->SetForegroundColor(Data[i + 6]); + object->SetBackgroundColor(Data[i + 7]); + i += 2; + } + } + break; + } + case CLUT_DEFINITION_SEGMENT: { + dbgsegments("CLUT_DEFINITION_SEGMENT\n"); + cSubtitleClut *clut = page->GetClutById(Data[6], true); + int clutVersion = (Data[6 + 1] & 0xF0) >> 4; + if (clutVersion == clut->Version()) + break; // no update + clut->SetVersion(clutVersion); + dbgcluts("Clut pageId %d id %d version %d\n", pageId, clut->ClutId(), clut->Version()); + for (int i = 6 + 2; i < segmentLength; i += 2) { + uchar clutEntryId = Data[i]; + bool fullRangeFlag = Data[i + 1] & 1; + uchar yval; + uchar crval; + uchar cbval; + uchar tval; + if (fullRangeFlag) { + yval = Data[i + 2]; + crval = Data[i + 3]; + cbval = Data[i + 4]; + tval = Data[i + 5]; + } + else { + yval = Data[i + 2] & 0xFC; + crval = (Data[i + 2] & 0x03) << 6; + crval |= (Data[i + 3] & 0xC0) >> 2; + cbval = (Data[i + 3] & 0x3C) << 2; + tval = (Data[i + 3] & 0x03) << 6; + } + tColor value = 0; + if (yval) { + value = yuv2rgb(yval, cbval, crval); + value |= ((10 - (clutEntryId ? SubtitleFgTransparency : SubtitleBgTransparency)) * (255 - tval) / 10) << 24; + } + int EntryFlags = Data[i + 1]; + dbgcluts("%2d %d %d %d %08X\n", clutEntryId, (EntryFlags & 0x80) ? 2 : 0, (EntryFlags & 0x40) ? 4 : 0, (EntryFlags & 0x20) ? 8 : 0, value); + if ((EntryFlags & 0x80) != 0) + clut->SetColor(2, clutEntryId, value); + if ((EntryFlags & 0x40) != 0) + clut->SetColor(4, clutEntryId, value); + if ((EntryFlags & 0x20) != 0) + clut->SetColor(8, clutEntryId, value); + i += fullRangeFlag ? 4 : 2; + } + dbgcluts("\n"); + page->UpdateRegionPalette(clut); + break; + } + case OBJECT_DATA_SEGMENT: { + dbgsegments("OBJECT_DATA_SEGMENT\n"); + cSubtitleObject *object = page->GetObjectById((Data[6] << 8) | Data[6 + 1]); + if (!object) + break; + int objectVersion = (Data[6 + 2] & 0xF0) >> 4; + if (objectVersion == object->Version()) + break; // no update + object->SetVersion(objectVersion); + int codingMethod = (Data[6 + 2] & 0x0C) >> 2; + object->SetNonModifyingColorFlag(Data[6 + 2] & 0x01); + dbgobjects("Object pageId %d id %d version %d method %d modify %d\n", pageId, object->ObjectId(), object->Version(), object->CodingMethod(), object->NonModifyingColorFlag()); + if (codingMethod == 0) { // coding of pixels + int i = 6 + 3; + int topFieldLength = (Data[i] << 8) | Data[i + 1]; + int bottomFieldLength = (Data[i + 2] << 8) | Data[i + 3]; + object->DecodeSubBlock(Data + i + 4, topFieldLength, true); + if (bottomFieldLength) + object->DecodeSubBlock(Data + i + 4 + topFieldLength, bottomFieldLength, false); + else + object->DecodeSubBlock(Data + i + 4, topFieldLength, false); + } + else if (codingMethod == 1) { // coded as a string of characters + //TODO implement textual subtitles + } + break; + } + case END_OF_DISPLAY_SET_SEGMENT: { + dbgsegments("END_OF_DISPLAY_SET_SEGMENT\n"); + FinishPage(page); + } + break; + default: + dbgsegments("*** unknown segment type: %02X\n", segmentType); + } + return segmentLength; + } + return -1; +} + +void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page) +{ + if (!AssertOsd()) + return; + tArea *Areas = Page->GetAreas(); + int NumAreas = Page->regions.Count(); +#if 0 + int Bpp = 8; + bool Reduced = false; + + while (osd->CanHandleAreas(Areas, NumAreas) != oeOk) { + int HalfBpp = Bpp / 2; + if (HalfBpp >= 2) { + for (int i = 0; i < NumAreas; i++) { + if (Areas[i].bpp >= Bpp) { + Areas[i].bpp = HalfBpp; + Reduced = true; + } + } + Bpp = HalfBpp; + } + else + return; // unable to draw bitmaps + } + + if (Reduced) { + for (int i = 0; i < NumAreas; i++) { + cSubtitleRegion *sr = Page->regions.Get(i); + if (sr->Bpp() != Areas[i].bpp) { + if (sr->Level() <= Areas[i].bpp) { + //TODO this is untested - didn't have any such subtitle stream + cSubtitleClut *Clut = Page->GetClutById(sr->ClutId()); + if (Clut) { + dbgregions("reduce region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp); + sr->ReduceBpp(*Clut->GetPalette(sr->Bpp())); + } + } + else { + dbgregions("condense region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp); + sr->ShrinkBpp(Areas[i].bpp); + } + } + } + } +#endif + cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->Pts(), Page->Timeout(), Areas, NumAreas); + bitmaps->Add(Bitmaps); + for (cSubtitleRegion *sr = Page->regions.First(); sr; sr = Page->regions.Next(sr)) { + int posX = sr->HorizontalAddress(); + int posY = sr->VerticalAddress(); + cBitmap *bm = new cBitmap(sr->Width(), sr->Height(), sr->Bpp(), posX, posY); + bm->DrawBitmap(posX, posY, *sr); + Bitmaps->AddBitmap(bm); + } +} diff --git a/lib/libdvbsub/dvbsubtitle.h b/lib/libdvbsub/dvbsubtitle.h new file mode 100644 index 000000000..c6672492a --- /dev/null +++ b/lib/libdvbsub/dvbsubtitle.h @@ -0,0 +1,51 @@ +/* + * dvbsubtitle.h: DVB subtitles + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * Original author: Marco Schlüßler + * + * $Id: dvbsubtitle.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $ + */ + +#ifndef __DVBSUBTITLE_H +#define __DVBSUBTITLE_H + +#include "osd.h" +#include "tools.h" + +class cDvbSubtitlePage; +class cDvbSubtitleAssembler; // for legacy PES recordings +class cDvbSubtitleBitmaps; + +class cDvbSubtitleConverter /*: public cThread */{ +private: + static int setupLevel; + cDvbSubtitleAssembler *dvbSubtitleAssembler; +// cOsd *osd; + cList *pages; + cList *bitmaps; + tColor yuv2rgb(int Y, int Cb, int Cr); + bool AssertOsd(void); + int ExtractSegment(const uchar *Data, int Length, int64_t Pts); + void FinishPage(cDvbSubtitlePage *Page); + bool running; + pthread_mutex_t mutex; +public: + cDvbSubtitleConverter(void); + virtual ~cDvbSubtitleConverter(); + void Action(void); + void Reset(void); + void Clear(void); + void Pause(bool pause); + void Lock(); + void Unlock(); + int ConvertFragments(const uchar *Data, int Length, int64_t pts); // for legacy PES recordings + int Convert(const uchar *Data, int Length, int64_t pts); + static void SetupChanged(void); + bool Running() { return running; }; +}; + + +#endif //__DVBSUBTITLE_H diff --git a/lib/libdvbsub/helpers.cpp b/lib/libdvbsub/helpers.cpp new file mode 100644 index 000000000..bed1d1a86 --- /dev/null +++ b/lib/libdvbsub/helpers.cpp @@ -0,0 +1,50 @@ +#include + +#include "helpers.hpp" + +/* Max 24 bit */ +uint32_t getbits(const uint8_t* buf, uint32_t offset, uint8_t len) +{ + const uint8_t* a = buf + (offset / 8); + uint32_t retval = 0; + uint32_t mask = 1; + + retval = ((*(a)<<8) | *(a+1)); + mask <<= len; + + if (len > 8) { + retval <<= 8; + retval |= *(a+2); + len -= 8; + } + if (len > 8) { + retval <<= 8; + retval |= *(a+3); + len -= 8; + } + if (len > 8) { + unsigned long long tmp = retval << 8; + tmp |= *(a+4); + tmp >>= ((8-(offset%8)) + (8-(len))); + return tmp & (mask -1); + } + + retval >>= ((8-(offset%8)) + (8-len)); + return retval & (mask -1); +} + +#if 0 +void hexdump(uint8_t* buf) +{ + int i; + for (i = 0 ; i < 16 ; i++) { + debug.print(Debug::DEBUG, "%02x ", buf[i]); + } + debug.print(Debug::DEBUG, "\n"); + for (i = 16 ; i < 32 ; i++) { + debug.print(Debug::DEBUG, "%02x ", buf[i]); + } + debug.print(Debug::DEBUG, "\n"); +} + +#endif diff --git a/lib/libdvbsub/helpers.hpp b/lib/libdvbsub/helpers.hpp new file mode 100644 index 000000000..3ad8e134a --- /dev/null +++ b/lib/libdvbsub/helpers.hpp @@ -0,0 +1,14 @@ +#ifndef HELPERS_H_ +#define HELPERS_H_ + +#include + +uint32_t getbits(const uint8_t* buf, uint32_t offset, uint8_t len); +void hexdump(uint8_t* buf); + +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#endif diff --git a/lib/libdvbsub/osd.cpp b/lib/libdvbsub/osd.cpp new file mode 100644 index 000000000..3607e6901 --- /dev/null +++ b/lib/libdvbsub/osd.cpp @@ -0,0 +1,287 @@ +/* + * osd.c: Abstract On Screen Display layer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: osd.cpp,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $ + */ + +#include "dvbsubtitle.h" +//#include "device.h" + +#define PAGE_COMPOSITION_SEGMENT 0x10 +#define REGION_COMPOSITION_SEGMENT 0x11 +#define CLUT_DEFINITION_SEGMENT 0x12 +#define OBJECT_DATA_SEGMENT 0x13 +#define END_OF_DISPLAY_SET_SEGMENT 0x80 + +// --- cPalette -------------------------------------------------------------- + +cPalette::cPalette(int Bpp) +{ + SetBpp(Bpp); + SetAntiAliasGranularity(10, 10); +} + +void cPalette::SetAntiAliasGranularity(uint FixedColors, uint BlendColors) +{ + if (FixedColors >= MAXNUMCOLORS || BlendColors == 0) + antiAliasGranularity = MAXNUMCOLORS - 1; + else { + int ColorsForBlending = MAXNUMCOLORS - FixedColors; + int ColorsPerBlend = ColorsForBlending / BlendColors + 2; // +2 = the full foreground and background colors, which are amoung the fixed colors + antiAliasGranularity = double(MAXNUMCOLORS - 1) / (ColorsPerBlend - 1); + } +} + +void cPalette::Reset(void) +{ + numColors = 0; + modified = false; +} + +int cPalette::Index(tColor Color) +{ + // Check if color is already defined: + for (int i = 0; i < numColors; i++) { + if (color[i] == Color) + return i; + } + // No exact color, try a close one: + int i = ClosestColor(Color, 4); + if (i >= 0) + return i; + // No close one, try to define a new one: + if (numColors < maxColors) { + color[numColors++] = Color; + modified = true; + return numColors - 1; + } + // Out of colors, so any close color must do: + return ClosestColor(Color); +} + +void cPalette::SetBpp(int Bpp) +{ + bpp = Bpp; + maxColors = 1 << bpp; + Reset(); +} + +void cPalette::SetColor(int Index, tColor Color) +{ + if (Index < maxColors) { + if (numColors <= Index) { + numColors = Index + 1; + modified = true; + } + else + modified |= color[Index] != Color; + color[Index] = Color; + } +} + +const tColor *cPalette::Colors(int &NumColors) const +{ + NumColors = numColors; + return numColors ? color : NULL; +} + +void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg) +{ + for (int i = 0; i < Palette.numColors; i++) { + tColor Color = Palette.color[i]; + if (ColorFg || ColorBg) { + switch (i) { + case 0: Color = ColorBg; break; + case 1: Color = ColorFg; break; + } + } + int n = Index(Color); + if (Indexes) + (*Indexes)[i] = n; + } +} + +void cPalette::Replace(const cPalette &Palette) +{ + for (int i = 0; i < Palette.numColors; i++) + SetColor(i, Palette.color[i]); + numColors = Palette.numColors; + antiAliasGranularity = Palette.antiAliasGranularity; +} + +tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const +{ + if (antiAliasGranularity > 0) + Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity); + int Af = (ColorFg & 0xFF000000) >> 24; + int Rf = (ColorFg & 0x00FF0000) >> 16; + int Gf = (ColorFg & 0x0000FF00) >> 8; + int Bf = (ColorFg & 0x000000FF); + int Ab = (ColorBg & 0xFF000000) >> 24; + int Rb = (ColorBg & 0x00FF0000) >> 16; + int Gb = (ColorBg & 0x0000FF00) >> 8; + int Bb = (ColorBg & 0x000000FF); + int A = (Ab + (Af - Ab) * Level / 0xFF) & 0xFF; + int R = (Rb + (Rf - Rb) * Level / 0xFF) & 0xFF; + int G = (Gb + (Gf - Gb) * Level / 0xFF) & 0xFF; + int B = (Bb + (Bf - Bb) * Level / 0xFF) & 0xFF; + return (A << 24) | (R << 16) | (G << 8) | B; +} + +int cPalette::ClosestColor(tColor Color, int MaxDiff) const +{ + int n = 0; + int d = INT_MAX; + int A1 = (Color & 0xFF000000) >> 24; + int R1 = (Color & 0x00FF0000) >> 16; + int G1 = (Color & 0x0000FF00) >> 8; + int B1 = (Color & 0x000000FF); + for (int i = 0; i < numColors; i++) { + int A2 = (color[i] & 0xFF000000) >> 24; + int R2 = (color[i] & 0x00FF0000) >> 16; + int G2 = (color[i] & 0x0000FF00) >> 8; + int B2 = (color[i] & 0x000000FF); + int diff = (abs(A1 - A2) << 1) + (abs(R1 - R2) << 1) + (abs(G1 - G2) << 1) + (abs(B1 - B2) << 1); + if (diff < d) { + d = diff; + n = i; + } + } + return d <= MaxDiff ? n : -1; +} + +cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0) +:cPalette(Bpp) +{ + bitmap = NULL; + x0 = X0; + y0 = Y0; + SetSize(Width, Height); +} + +cBitmap::~cBitmap() +{ + free(bitmap); +} + +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; + } + } + } +} + +void cBitmap::SetSize(int Width, int Height) +{ + if (bitmap && Width == width && Height == height) + return; + width = Width; + height = Height; + free(bitmap); + bitmap = NULL; + dirtyX1 = 0; + dirtyY1 = 0; + dirtyX2 = width - 1; + dirtyY2 = height - 1; + if (width > 0 && height > 0) { + bitmap = MALLOC(tIndex, width * height); + if (bitmap) + memset(bitmap, 0x00, width * height); + else + esyslog("ERROR: can't allocate bitmap!"); + } + else + esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height); +} + +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)) + Reset(); + x -= x0; + y -= y0; + if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) { + 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]); + } + } + } + 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])]); + } + } + } + } +} + + +bool cBitmap::Contains(int x, int y) const +{ + x -= x0; + y -= y0; + return 0 <= x && x < width && 0 <= y && y < height; +} + +bool cBitmap::Covers(int x1, int y1, int x2, int y2) const +{ + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + return x1 <= 0 && y1 <= 0 && x2 >= width - 1 && y2 >= height - 1; +} + +bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const +{ + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height); +} + +bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2) +{ + if (dirtyX2 >= 0) { + x1 = dirtyX1; + y1 = dirtyY1; + x2 = dirtyX2; + y2 = dirtyY2; + return true; + } + return false; +} + +void cBitmap::Clean(void) +{ + dirtyX1 = width; + dirtyY1 = height; + dirtyX2 = -1; + dirtyY2 = -1; +} + +const tIndex *cBitmap::Data(int x, int y) +{ + return &bitmap[y * width + x]; +} + diff --git a/lib/libdvbsub/osd.h b/lib/libdvbsub/osd.h new file mode 100644 index 000000000..9ac555e05 --- /dev/null +++ b/lib/libdvbsub/osd.h @@ -0,0 +1,147 @@ +/* + * osd.h: Abstract On Screen Display layer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * Original author: Marco Schlüßler + * + * $Id: osd.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $ + */ + +#ifndef __OSD_H +#define __OSD_H + +#include "tools.h" +#include + +#define MAXNUMCOLORS 256 + +typedef uint32_t tColor; +typedef unsigned char tIndex; + +class cPalette { +private: + tColor color[MAXNUMCOLORS]; + int bpp; + int maxColors, numColors; + bool modified; + double antiAliasGranularity; +protected: + typedef tIndex tIndexes[MAXNUMCOLORS]; +public: + cPalette(int Bpp = 8); + ///< Initializes the palette with the given color depth. + void SetAntiAliasGranularity(uint FixedColors, uint BlendColors); + ///< Allows the system to optimize utilization of the limited color + ///< palette entries when generating blended colors for anti-aliasing. + ///< FixedColors is the maximum number of colors used, and BlendColors + ///< is the maximum number of foreground/background color combinations + ///< used with anti-aliasing. If this function is not called with + ///< useful values, the palette may be filled up with many shades of + ///< a single color combination, and may not be able to serve all + ///< requested colors. By default the palette assumes there will be + ///< 10 fixed colors and 10 color combinations. + int Bpp(void) const { return bpp; } + void Reset(void); + ///< Resets the palette, making it contain no colors. + int Index(tColor Color); + ///< Returns the index of the given Color (the first color has index 0). + ///< If Color is not yet contained in this palette, it will be added if + ///< there is a free slot. If the color can't be added to this palette, + ///< the closest existing color will be returned. + tColor Color(int Index) const { return Index < maxColors ? color[Index] : 0; } + ///< Returns the color at the given Index. If Index is outside the valid + ///< range, 0 will be returned. + void SetBpp(int Bpp); + ///< Sets the color depth of this palette to the given value. + ///< The palette contents will be reset, so that it contains no colors. + void SetColor(int Index, tColor Color); + ///< Sets the palette entry at Index to Color. If Index is larger than + ///< the number of currently used entries in this palette, the entries + ///< in between will have undefined values. + const tColor *Colors(int &NumColors) const; + ///< Returns a pointer to the complete color table and stores the + ///< number of valid entries in NumColors. If no colors have been + ///< stored yet, NumColors will be set to 0 and the function will + ///< return NULL. + void Take(const cPalette &Palette, tIndexes *Indexes = NULL, tColor ColorFg = 0, tColor ColorBg = 0); + ///< Takes the colors from the given Palette and adds them to this palette, + ///< using existing entries if possible. If Indexes is given, it will be + ///< filled with the index values that each color of Palette has in this + ///< palette. If either of ColorFg or ColorBg is not zero, the first color + ///< in Palette will be taken as ColorBg, and the second color will become + ///< ColorFg. + void Replace(const cPalette &Palette); + ///< Replaces the colors of this palette with the colors from the given + ///< palette. + tColor Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const; + ///< Determines a color that consists of a linear blend between ColorFg + ///< and ColorBg. If Level is 0, the result is ColorBg, if it is 255, + ///< the result is ColorFg. If SetAntiAliasGranularity() has been called previously, + ///< Level will be mapped to a limited range of levels that allow to make best + ///< use of the palette entries. + int ClosestColor(tColor Color, int MaxDiff = INT_MAX) const; + ///< Returns the index of a color in this palette that is closest to the given + ///< Color. MaxDiff can be used to control the maximum allowed color difference. + ///< If no color with a maximum difference of MaxDiff can be found, -1 will + ///< be returned. With the default value of INT_MAX, there will always be + ///< a valid color index returned, but the color may be completely different. +}; + +class cBitmap : public cPalette { +private: + tIndex *bitmap; + int x0, y0; + int width, height; + int dirtyX1, dirtyY1, dirtyX2, dirtyY2; +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). + ///< X0 and Y0 define the offset at which this bitmap will be located on the OSD. + ///< All coordinates given in the other functions will be relative to + ///< this offset (unless specified otherwise). + cBitmap(const char *FileName); + ///< Creates a bitmap and loads an XPM image from the given file. + virtual ~cBitmap(); + int X0(void) const { return x0; } + int Y0(void) const { return y0; } + int Width(void) const { return width; } + int Height(void) const { return height; } + void SetSize(int Width, int Height); + ///< Sets the size of this bitmap to the given values. Any previous + ///< contents of the bitmap will be lost. If Width and Height are the same + ///< as the current values, nothing will happen and the bitmap remains + ///< unchanged. + void SetIndex(int x, int y, tIndex Index); + ///< Sets the index at the given coordinates to Index. + ///< Coordinates are relative to the bitmap's origin. + void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false, bool Overlay = false); + ///< Sets the pixels in this bitmap with the data from the given + ///< Bitmap, putting the upper left corner of the Bitmap at (x, y). + ///< If ColorFg or ColorBg is given, the first palette entry of the Bitmap + ///< will be mapped to ColorBg and the second palette entry will be mapped to + ///< ColorFg (palette indexes are defined so that 0 is the background and + ///< 1 is the foreground color). ReplacePalette controls whether the target + ///< area shall have its palette replaced with the one from Bitmap. + ///< If Overlay is true, any pixel in Bitmap that has color index 0 will + ///< not overwrite the corresponding pixel in the target area. + const tIndex *Data(int x, int y); + ///< Returns the address of the index byte at the given coordinates. + + bool Contains(int x, int y) const; + bool Covers(int x1, int y1, int x2, int y2) const; + bool Intersects(int x1, int y1, int x2, int y2) const; + bool Dirty(int &x1, int &y1, int &x2, int &y2); + void Clean(void); +}; + +struct tArea { + int x1, y1, x2, y2; + int bpp; + int Width(void) const { return x2 - x1 + 1; } + int Height(void) const { return y2 - y1 + 1; } + bool Intersects(const tArea &Area) const { return !(x2 < Area.x1 || x1 > Area.x2 || y2 < Area.y1 || y1 > Area.y2); } +}; + +#endif //__OSD_H diff --git a/lib/libdvbsub/reader_thread.hpp b/lib/libdvbsub/reader_thread.hpp new file mode 100644 index 000000000..15c1b9f5b --- /dev/null +++ b/lib/libdvbsub/reader_thread.hpp @@ -0,0 +1,6 @@ +#ifndef READER_THREAD_HPP_ +#define READER_THREAD_HPP_ + +void* reader_thread(void *arg); + +#endif diff --git a/lib/libdvbsub/tools.cpp b/lib/libdvbsub/tools.cpp new file mode 100644 index 000000000..bd432bd16 --- /dev/null +++ b/lib/libdvbsub/tools.cpp @@ -0,0 +1,267 @@ +/* + * tools.c: Various tools + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * vdr: Id: tools.c 2.0 2008/03/05 17:23:47 kls Exp + */ + +#include "tools.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int SysLogLevel = 3; + +#define MAXSYSLOGBUF 256 + +// --- cTimeMs --------------------------------------------------------------- + +cTimeMs::cTimeMs(int Ms) +{ + Set(Ms); +} + +uint64_t cTimeMs::Now(void) +{ +#if 0 && _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) +#define MIN_RESOLUTION 5 // ms + static bool initialized = false; + static bool monotonic = false; + struct timespec tp; + if (!initialized) { + // check if monotonic timer is available and provides enough accurate resolution: + if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) { + long Resolution = tp.tv_nsec; + // require a minimum resolution: + if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution); + monotonic = true; + } + else + esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed"); + } + else + dsyslog("cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec); + } + else + esyslog("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed"); + initialized = true; + } + if (monotonic) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000; + esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed"); + monotonic = false; + // fall back to gettimeofday() + } +#else +# warning Posix monotonic clock not available +#endif + struct timeval t; + if (gettimeofday(&t, NULL) == 0) + return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000; + return 0; +} + +void cTimeMs::Set(int Ms) +{ + begin = Now() + Ms; +} + +bool cTimeMs::TimedOut(void) +{ + return Now() >= begin; +} + +uint64_t cTimeMs::Elapsed(void) +{ + return Now() - begin; +} + +// --- cListObject ----------------------------------------------------------- + +cListObject::cListObject(void) +{ + prev = next = NULL; +} + +cListObject::~cListObject() +{ +} + +void cListObject::Append(cListObject *Object) +{ + next = Object; + Object->prev = this; +} + +void cListObject::Insert(cListObject *Object) +{ + prev = Object; + Object->next = this; +} + +void cListObject::Unlink(void) +{ + if (next) + next->prev = prev; + if (prev) + prev->next = next; + next = prev = NULL; +} + +int cListObject::Index(void) const +{ + cListObject *p = prev; + int i = 0; + + while (p) { + i++; + p = p->prev; + } + return i; +} + +// --- cListBase ------------------------------------------------------------- + +cListBase::cListBase(void) +{ + objects = lastObject = NULL; + count = 0; +} + +cListBase::~cListBase() +{ + Clear(); +} + +void cListBase::Add(cListObject *Object, cListObject *After) +{ + if (After && After != lastObject) { + After->Next()->Insert(Object); + After->Append(Object); + } + else { + if (lastObject) + lastObject->Append(Object); + else + objects = Object; + lastObject = Object; + } + count++; +} + +void cListBase::Ins(cListObject *Object, cListObject *Before) +{ + if (Before && Before != objects) { + Before->Prev()->Append(Object); + Before->Insert(Object); + } + else { + if (objects) + objects->Insert(Object); + else + lastObject = Object; + objects = Object; + } + count++; +} + +void cListBase::Del(cListObject *Object, bool DeleteObject) +{ + if (Object == objects) + objects = Object->Next(); + if (Object == lastObject) + lastObject = Object->Prev(); + Object->Unlink(); + if (DeleteObject) { + delete Object; + count--; + } +} + +void cListBase::Move(int From, int To) +{ + Move(Get(From), Get(To)); +} + +void cListBase::Move(cListObject *From, cListObject *To) +{ + if (From && To) { + if (From->Index() < To->Index()) + To = To->Next(); + if (From == objects) + objects = From->Next(); + if (From == lastObject) + lastObject = From->Prev(); + From->Unlink(); + if (To) { + if (To->Prev()) + To->Prev()->Append(From); + From->Append(To); + } + else { + lastObject->Append(From); + lastObject = From; + } + if (!From->Prev()) + objects = From; + } +} + +void cListBase::Clear(void) +{ + while (objects) { + cListObject *object = objects->Next(); + delete objects; + objects = object; + } + objects = lastObject = NULL; + count = 0; +} + +cListObject *cListBase::Get(int Index) const +{ + if (Index < 0) + return NULL; + cListObject *object = objects; + while (object && Index-- > 0) + object = object->Next(); + return object; +} + +static int CompareListObjects(const void *a, const void *b) +{ + const cListObject *la = *(const cListObject **)a; + const cListObject *lb = *(const cListObject **)b; + return la->Compare(*lb); +} + +void cListBase::Sort(void) +{ + int n = Count(); + cListObject *a[n]; + cListObject *object = objects; + int i = 0; + while (object && i < n) { + a[i++] = object; + object = object->Next(); + } + qsort(a, n, sizeof(cListObject *), CompareListObjects); + objects = lastObject = NULL; + for (i = 0; i < n; i++) { + a[i]->Unlink(); + count--; + Add(a[i]); + } +} diff --git a/lib/libdvbsub/tools.h b/lib/libdvbsub/tools.h new file mode 100644 index 000000000..0e509790a --- /dev/null +++ b/lib/libdvbsub/tools.h @@ -0,0 +1,193 @@ +/* + * tools.h: Various tools + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: tools.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $ + */ + +#ifndef __TOOLS_H +#define __TOOLS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char uchar; + +extern int SysLogLevel; + +#if 0 +#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR, a) : void() ) +#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_ERR, a) : void() ) +#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_ERR, a) : void() ) +#else +#define esyslog printf +#define isyslog printf +#define dsyslog printf +#endif + +#define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__) +#define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s) + +#define SECSINDAY 86400 + +#define KILOBYTE(n) ((n) * 1024) +#define MEGABYTE(n) ((n) * 1024 * 1024) + +#define MALLOC(type, size) (type *)malloc(sizeof(type) * (size)) + +#define DELETENULL(p) (delete (p), p = NULL) + +#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls +#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR) + +#ifndef __STL_CONFIG_H // in case some plugin needs to use the STL +template inline T min(T a, T b) { return a <= b ? a : b; } +template inline T max(T a, T b) { return a >= b ? a : b; } +template inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; } +template inline void swap(T &a, T &b) { T t = a; a = b; b = t; } +#endif + +class cTimeMs { +private: + uint64_t begin; +public: + cTimeMs(int Ms = 0); + ///< Creates a timer with ms resolution and an initial timeout of Ms. + static uint64_t Now(void); + void Set(int Ms = 0); + bool TimedOut(void); + uint64_t Elapsed(void); + }; + +class cListObject { +private: + cListObject *prev, *next; +public: + cListObject(void); + virtual ~cListObject(); + virtual int Compare(const cListObject &ListObject) const { return 0; } + ///< Must return 0 if this object is equal to ListObject, a positive value + ///< if it is "greater", and a negative value if it is "smaller". + void Append(cListObject *Object); + void Insert(cListObject *Object); + void Unlink(void); + int Index(void) const; + cListObject *Prev(void) const { return prev; } + cListObject *Next(void) const { return next; } + }; + +class cListBase { +protected: + cListObject *objects, *lastObject; + cListBase(void); + int count; +public: + virtual ~cListBase(); + void Add(cListObject *Object, cListObject *After = NULL); + void Ins(cListObject *Object, cListObject *Before = NULL); + void Del(cListObject *Object, bool DeleteObject = true); + virtual void Move(int From, int To); + void Move(cListObject *From, cListObject *To); + virtual void Clear(void); + cListObject *Get(int Index) const; + int Count(void) const { return count; } + void Sort(void); + }; + +template class cList : public cListBase { +public: + T *Get(int Index) const { return (T *)cListBase::Get(Index); } + T *First(void) const { return (T *)objects; } + T *Last(void) const { return (T *)lastObject; } + T *Prev(const T *object) const { return (T *)object->cListObject::Prev(); } // need to call cListObject's members to + T *Next(const T *object) const { return (T *)object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists" + }; + +template class cVector { +private: + mutable int allocated; + mutable int size; + mutable T *data; + cVector(const cVector &Vector) {} // don't copy... + cVector &operator=(const cVector &Vector) { return *this; } // ...or assign this! + void Realloc(int Index) const + { + if (++Index > allocated) { + data = (T *)realloc(data, Index * sizeof(T)); + for (int i = allocated; i < Index; i++) + data[i] = T(0); + allocated = Index; + } + } +public: + cVector(int Allocated = 10) + { + allocated = 0; + size = 0; + data = NULL; + Realloc(Allocated); + } + virtual ~cVector() { free(data); } + T& At(int Index) const + { + Realloc(Index); + if (Index >= size) + size = Index + 1; + return data[Index]; + } + const T& operator[](int Index) const + { + return At(Index); + } + T& operator[](int Index) + { + return At(Index); + } + int Size(void) const { return size; } + virtual void Insert(T Data, int Before = 0) + { + if (Before < size) { + Realloc(size); + memmove(&data[Before + 1], &data[Before], (size - Before) * sizeof(T)); + size++; + data[Before] = Data; + } + else + Append(Data); + } + virtual void Append(T Data) + { + if (size >= allocated) + Realloc(allocated * 4 / 2); // increase size by 50% + data[size++] = Data; + } + virtual void Remove(int Index) + { + if (Index < size - 1) + memmove(&data[Index], &data[Index + 1], (size - Index) * sizeof(T)); + size--; + } + virtual void Clear(void) + { + size = 0; + } + void Sort(__compar_fn_t Compare) + { + qsort(data, size, sizeof(T), Compare); + } + }; + +#endif //__TOOLS_H diff --git a/lib/libeventserver/Makefile.am b/lib/libeventserver/Makefile.am new file mode 100644 index 000000000..d7ae11165 --- /dev/null +++ b/lib/libeventserver/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libtuxbox-eventserver.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtuxbox_eventserver_a_SOURCES = eventserver.cpp diff --git a/lib/libeventserver/eventserver.cpp b/lib/libeventserver/eventserver.cpp new file mode 100644 index 000000000..0ce98e244 --- /dev/null +++ b/lib/libeventserver/eventserver.cpp @@ -0,0 +1,115 @@ +/* + + $Header: /cvs/tuxbox/apps/misc/libs/libeventserver/eventserver.cpp,v 1.12 2003/03/14 06:25:49 obi Exp $ + + Event-Server - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include "eventserver.h" + +void CEventServer::registerEvent2(const unsigned int eventID, const unsigned int ClientID, const std::string udsName) +{ + strcpy(eventData[eventID][ClientID].udsName, udsName.c_str()); +} + +void CEventServer::registerEvent(const int fd) +{ + commandRegisterEvent msg; + + int readresult= read(fd, &msg, sizeof(msg)); + if ( readresult<= 0 ) + perror("[eventserver]: read"); +// printf("[eventserver]: read from %d %x bytes %d/%d\n", fd, errno, readresult, sizeof(msg)); +// printf("[eventserver]: registered event (%d) to: %d - %s\n", msg.eventID, msg.clientID, msg.udsName); + registerEvent2(msg.eventID, msg.clientID, msg.udsName); +} + +void CEventServer::unRegisterEvent2(const unsigned int eventID, const unsigned int ClientID) +{ + eventData[eventID].erase( ClientID ); +} + +void CEventServer::unRegisterEvent(const int fd) +{ + commandUnRegisterEvent msg; + read(fd, &msg, sizeof(msg)); + unRegisterEvent2(msg.eventID, msg.clientID); +} + +void CEventServer::sendEvent(const unsigned int eventID, const initiators initiatorID, const void* eventbody, const unsigned int eventbodysize) +{ + eventClientMap notifyClients = eventData[eventID]; + + for(eventClientMap::iterator pos = notifyClients.begin(); pos != notifyClients.end(); pos++) + { + //allen clients ein event schicken + eventClient client = pos->second; + sendEvent2Client(eventID, initiatorID, &client, eventbody, eventbodysize); + } +} + + +bool CEventServer::sendEvent2Client(const unsigned int eventID, const initiators initiatorID, const eventClient* ClientData, const void* eventbody, const unsigned int eventbodysize) +{ + struct sockaddr_un servaddr; + int clilen, sock_fd; + + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + strcpy(servaddr.sun_path, ClientData->udsName); + clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); + + if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror("[eventserver]: socket"); + return false; + } + + if(connect(sock_fd, (struct sockaddr*) &servaddr, clilen) <0 ) + { + char errmsg[128]; + snprintf(errmsg, 128, "[eventserver]: connect (%s)", ClientData->udsName); + perror(errmsg); + close(sock_fd); + return false; + } + + eventHead head; + head.eventID = eventID; + head.initiatorID = initiatorID; + head.dataSize = eventbodysize; + int written = write(sock_fd, &head, sizeof(head)); +// printf ("[eventserver]: sent 0x%x - following eventbody= %d\n", written, eventbodysize ); + + if(eventbodysize!=0) + { + written = write(sock_fd, eventbody, eventbodysize); +// printf ("[eventserver]: eventbody sent 0x%x - peventbody= %x eventbody= %x\n", written, (unsigned)eventbody, *(unsigned*)eventbody ); + } + close(sock_fd); + return true; +} diff --git a/lib/libeventserver/eventserver.h b/lib/libeventserver/eventserver.h new file mode 100644 index 000000000..f68233fe9 --- /dev/null +++ b/lib/libeventserver/eventserver.h @@ -0,0 +1,95 @@ +/* + + $Header: /cvs/tuxbox/apps/misc/libs/libeventserver/eventserver.h,v 1.14 2004/05/06 15:06:30 thegoodguy Exp $ + + Event-Server - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libevent__ +#define __libevent__ + +#include +#include + + +class CEventServer +{ + public: + + enum initiators + { + INITID_CONTROLD, + INITID_SECTIONSD, + INITID_ZAPIT, + INITID_TIMERD, + INITID_HTTPD, + INITID_NEUTRINO, + INITID_GENERIC_INPUT_EVENT_PROVIDER + }; + + + struct commandRegisterEvent + { + unsigned int eventID; + unsigned int clientID; + char udsName[50]; + }; + + struct commandUnRegisterEvent + { + unsigned int eventID; + unsigned int clientID; + }; + + struct eventHead + { + unsigned int eventID; + unsigned int initiatorID; + unsigned int dataSize; + }; + + void registerEvent2(const unsigned int eventID, const unsigned int ClientID, const std::string udsName); + void registerEvent(const int fd); + void unRegisterEvent2(const unsigned int eventID, const unsigned int ClientID); + void unRegisterEvent(const int fd); + void sendEvent(const unsigned int eventID, const initiators initiatorID, const void* eventbody = NULL, const unsigned int eventbodysize = 0); + + protected: + + struct eventClient + { + char udsName[50]; + }; + + //key: ClientID // Map is a Sorted Associative Container + typedef std::map eventClientMap; // -> clients with lower ClientID receive events first + + //key: eventID + std::map eventData; + + bool sendEvent2Client(const unsigned int eventID, const initiators initiatorID, const eventClient* ClientData, const void* eventbody = NULL, const unsigned int eventbodysize = 0); + +}; + + +#endif diff --git a/lib/libmd5sum/Makefile.am b/lib/libmd5sum/Makefile.am new file mode 100644 index 000000000..632a9debe --- /dev/null +++ b/lib/libmd5sum/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libtuxbox-md5sum.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtuxbox_md5sum_a_SOURCES = libmd5sum.c md5.c diff --git a/lib/libmd5sum/getline.h b/lib/libmd5sum/getline.h new file mode 100644 index 000000000..991184c75 --- /dev/null +++ b/lib/libmd5sum/getline.h @@ -0,0 +1,38 @@ +/* Copyright (C) 1995, 1997, 1999 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef GETLINE_H_ +# define GETLINE_H_ 1 + +# include + +# ifndef PARAMS +# if defined PROTOTYPES || (defined __STDC__ && __STDC__) +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +# endif + +# if __GLIBC__ < 2 +int +getline PARAMS ((char **_lineptr, size_t *_n, FILE *_stream)); + +int +getdelim PARAMS ((char **_lineptr, size_t *_n, int _delimiter, FILE *_stream)); +# endif + +#endif /* not GETLINE_H_ */ diff --git a/lib/libmd5sum/libmd5sum.c b/lib/libmd5sum/libmd5sum.c new file mode 100644 index 000000000..85878045a --- /dev/null +++ b/lib/libmd5sum/libmd5sum.c @@ -0,0 +1,87 @@ +#include "libmd5sum.h" +#include + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "md5.h" +#include "getline.h" +#include +#include "error.h" +#include + +#define STREQ(a, b) (strcmp ((a), (b)) == 0) + +static int have_read_stdin; + +#if O_BINARY +# define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT) +# define TEXT1TO1 "rb" +# define TEXTCNVT "r" +#else +# if defined VMS +# define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT) +# define TEXT1TO1 "rb", "ctx=stm" +# define TEXTCNVT "r", "ctx=stm" +# else +# if UNIX || __UNIX__ || unix || __unix__ || _POSIX_VERSION +# define OPENOPTS(BINARY) "r" +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine system type." +# endif +# endif +#endif + +int md5_file (const char *filename, int binary, unsigned char *md5_result) +{ + FILE *fp; + int err; + + (void)binary; + + if (STREQ (filename, "-")) + { + have_read_stdin = 1; + fp = stdin; +#if O_BINARY + /* If we need binary reads from a pipe or redirected stdin, we need + to switch it to BINARY mode here, since stdin is already open. */ + if (binary) + SET_BINARY (fileno (stdin)); +#endif + } + else + { + /* OPENOPTS is a macro. It varies with the system. + Some systems distinguish between internal and + external text representations. */ + + fp = fopen (filename, OPENOPTS (binary)); + if (fp == NULL) + { + error (0, errno, "%s", filename); + return 1; + } + } + + err = md5_stream (fp, md5_result); + if (err) + { + error (0, errno, "%s", filename); + if (fp != stdin) + fclose (fp); + return 1; + } + + if (fp != stdin && fclose (fp) == EOF) + { + error (0, errno, "%s", filename); + return 1; + } + + return 0; +} diff --git a/lib/libmd5sum/libmd5sum.h b/lib/libmd5sum/libmd5sum.h new file mode 100644 index 000000000..6a604903d --- /dev/null +++ b/lib/libmd5sum/libmd5sum.h @@ -0,0 +1,45 @@ +/* + md5sum lib - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + + MD5-functions Written by Ulrich Drepper . +*/ + +#ifndef __libmd5sum__ +#define __libmd5sum__ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +int md5_file (const char *filename, int binary, unsigned char *md5_result); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libmd5sum/md5.c b/lib/libmd5sum/md5.c new file mode 100644 index 000000000..f69e22685 --- /dev/null +++ b/lib/libmd5sum/md5.c @@ -0,0 +1,413 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Ulrich Drepper , 1995. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include "md5.h" + +#ifdef HAVE_ENDIAN_H +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (ctx, resbuf) + struct md5_ctx *ctx; + void *resbuf; +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (stream, resblock) + FILE *stream; + void *resblock; +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (left_over + add > 64) + { + md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx); + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + (left_over + add) & 63); + ctx->buflen = (left_over + add) & 63; + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + memcpy (ctx->buffer, buffer, len); + ctx->buflen = len; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/lib/libmd5sum/md5.h b/lib/libmd5sum/md5.h new file mode 100644 index 000000000..ad97efc32 --- /dev/null +++ b/lib/libmd5sum/md5.h @@ -0,0 +1,146 @@ +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include + +#if defined HAVE_LIMITS_H || _LIBC +# include +#endif + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include +typedef u_int32_t md5_uint32; +#else +# if defined __STDC__ && __STDC__ +# define UINT_MAX_32_BITS 4294967295U +# else +# define UINT_MAX_32_BITS 0xFFFFFFFF +# endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have ) have 64+-bit integral types. */ + +# ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +# endif + +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +#endif + +#undef __P +#if defined (__STDC__) && __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void md5_init_ctx __P ((struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void md5_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int md5_stream __P ((FILE *stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock)); + +#endif diff --git a/lib/libmd5sum/sys2.h b/lib/libmd5sum/sys2.h new file mode 100644 index 000000000..13201bb16 --- /dev/null +++ b/lib/libmd5sum/sys2.h @@ -0,0 +1,347 @@ +/* WARNING -- this file is temporary. It is shared between the + sh-utils, fileutils, and textutils packages. Once I find a little + more time, I'll merge the remaining things in system.h and everything + in this file will go back there. */ + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#ifndef __GNUC__ +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + # pragma alloca +# else +# ifdef _WIN32 +# include +# include +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#ifdef __DJGPP__ + /* We need the declaration of setmode. */ +# include + /* We need the declaration of __djgpp_set_ctrl_c. */ +# include +#endif + +#include + +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." + + Bruno Haible adds: + + "... Furthermore, isupper(c) etc. have an undefined result if c is + outside the range -1 <= c <= 255. One is tempted to write isupper(c) + with c being of type `char', but this is wrong if c is an 8-bit + character >= 128 which gets sign-extended to a negative value. + The macro ISUPPER protects against this as well." */ + +#if STDC_HEADERS || (!defined (isascii) && !HAVE_ISASCII) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) +#endif + +#ifdef isblank +# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c)) +#else +# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c)) +#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c)) +#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) +#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c)) +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) + +#if STDC_HEADERS +# define TOLOWER(Ch) tolower (Ch) +# define TOUPPER(Ch) toupper (Ch) +#else +# define TOLOWER(Ch) (ISUPPER (Ch) ? tolower (Ch) : (Ch)) +# define TOUPPER(Ch) (ISLOWER (Ch) ? toupper (Ch) : (Ch)) +#endif + +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#ifndef PARAMS +# if PROTOTYPES +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +/* Take care of NLS matters. */ + +#if HAVE_LOCALE_H +# include +#endif +#if !HAVE_SETLOCALE +# define setlocale(Category, Locale) /* empty */ +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# undef bindtextdomain +# define bindtextdomain(Domain, Directory) /* empty */ +# undef textdomain +# define textdomain(Domain) /* empty */ +# define _(Text) Text +#endif +#define N_(Text) Text + +#define STREQ(a, b) (strcmp ((a), (b)) == 0) + +#ifndef HAVE_DECL_FREE +void free (); +#endif + +//#ifndef HAVE_DECL_MALLOC +//char *malloc (); +//#endif + +//#ifndef HAVE_DECL_MEMCHR +//char *memchr (); +//#endif + +#ifndef HAVE_DECL_REALLOC +char *realloc (); +#endif + +#ifndef HAVE_DECL_STPCPY +# ifndef stpcpy +char *stpcpy (); +# endif +#endif + +#ifndef HAVE_DECL_STRSTR +char *strstr (); +#endif + +#ifndef HAVE_DECL_GETENV +char *getenv (); +#endif + +#ifndef HAVE_DECL_LSEEK +off_t lseek (); +#endif + +#if ! defined HAVE_MEMPCPY && ! defined mempcpy +/* Be CAREFUL that there are no side effects in N. */ +# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) +#endif + +/* These are wrappers for functions/macros from GNU libc. + The standard I/O functions are thread-safe. These *_unlocked ones + are more efficient but not thread-safe. That they're not thread-safe + is fine since all these applications are single threaded. */ + +#ifdef HAVE_CLEARERR_UNLOCKED +# undef clearerr +# define clearerr(S) clearerr_unlocked (S) +#endif + +#ifdef HAVE_FEOF_UNLOCKED +# undef feof +# define feof(S) feof_unlocked (S) +#endif + +#ifdef HAVE_FERROR_UNLOCKED +# undef ferror +# define ferror(S) ferror_unlocked (S) +#endif + +#ifdef HAVE_FFLUSH_UNLOCKED +# undef fflush +# define fflush(S) fflush_unlocked (S) +#endif + +#ifdef HAVE_FPUTC_UNLOCKED +# undef fputc +# define fputc(C, S) fputc_unlocked (C, S) +#endif + +#ifdef HAVE_FREAD_UNLOCKED +# undef fread +# define fread(P, Z, N, S) fread_unlocked (P, Z, N, S) +#endif + +#ifdef HAVE_FWRITE_UNLOCKED +# undef fwrite +# define fwrite(P, Z, N, S) fwrite_unlocked (P, Z, N, S) +#endif + +#ifdef HAVE_GETC_UNLOCKED +# undef getc +# define getc(S) getc_unlocked (S) +#endif + +#ifdef HAVE_GETCHAR_UNLOCKED +# undef getchar +# define getchar(S) getchar_unlocked (S) +#endif + +#ifdef HAVE_PUTC_UNLOCKED +# undef putc +# define putc(C, S) putc_unlocked (C, S) +#endif + +#ifdef HAVE_PUTCHAR_UNLOCKED +# undef putchar +# define putchar(C) putchar_unlocked (C) +#endif + +#define SAME_INODE(Stat_buf_1, Stat_buf_2) \ + ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \ + && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev) + +#define DOT_OR_DOTDOT(Basename) \ + (Basename[0] == '.' && (Basename[1] == '\0' \ + || (Basename[1] == '.' && Basename[2] == '\0'))) + +#if SETVBUF_REVERSED +# define SETVBUF(Stream, Buffer, Type, Size) \ + setvbuf (Stream, Type, Buffer, Size) +#else +# define SETVBUF(Stream, Buffer, Type, Size) \ + setvbuf (Stream, Buffer, Type, Size) +#endif + +char *base_name PARAMS ((char const *)); + +/* Factor out some of the common --help and --version processing code. */ + +#define GETOPT_HELP_CHAR 250 +#define GETOPT_VERSION_CHAR 251 + +#define GETOPT_HELP_OPTION_DECL \ + "help", no_argument, 0, GETOPT_HELP_CHAR +#define GETOPT_VERSION_OPTION_DECL \ + "version", no_argument, 0, GETOPT_VERSION_CHAR + +#define case_GETOPT_HELP_CHAR \ + case GETOPT_HELP_CHAR: \ + usage (EXIT_SUCCESS); \ + break; + +#define case_GETOPT_VERSION_CHAR(Program_name, Authors) \ + case GETOPT_VERSION_CHAR: \ + version_etc (stdout, Program_name, GNU_PACKAGE, VERSION, Authors); \ + close_stdout (); \ + exit (EXIT_SUCCESS); \ + break; + +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +/* The extra casts work around common compiler bugs. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) +/* The outer cast is needed to work around a bug in Cray C 5.0.3.0. + It is necessary at least when t == time_t. */ +#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \ + ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0)) +#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t))) + +#ifndef CHAR_MIN +# define CHAR_MIN TYPE_MINIMUM (char) +#endif + +#ifndef CHAR_MAX +# define CHAR_MAX TYPE_MAXIMUM (char) +#endif + +#ifndef SCHAR_MIN +# define SCHAR_MIN (-1 - SCHAR_MAX) +#endif + +#ifndef SCHAR_MAX +# define SCHAR_MAX (CHAR_MAX == UCHAR_MAX ? CHAR_MAX / 2 : CHAR_MAX) +#endif + +#ifndef UCHAR_MAX +# define UCHAR_MAX TYPE_MAXIMUM (unsigned char) +#endif + +#ifndef SHRT_MIN +# define SHRT_MIN TYPE_MINIMUM (short int) +#endif + +#ifndef SHRT_MAX +# define SHRT_MAX TYPE_MAXIMUM (short int) +#endif + +#ifndef INT_MAX +# define INT_MAX TYPE_MAXIMUM (int) +#endif + +#ifndef UINT_MAX +# define UINT_MAX TYPE_MAXIMUM (unsigned int) +#endif + +#ifndef LONG_MAX +# define LONG_MAX TYPE_MAXIMUM (long) +#endif + +#ifndef ULONG_MAX +# define ULONG_MAX TYPE_MAXIMUM (unsigned long) +#endif + +#ifndef UID_T_MAX +# define UID_T_MAX TYPE_MAXIMUM (uid_t) +#endif + +#ifndef GID_T_MAX +# define GID_T_MAX TYPE_MAXIMUM (gid_t) +#endif + +#ifndef PID_T_MAX +# define PID_T_MAX TYPE_MAXIMUM (pid_t) +#endif diff --git a/lib/libmd5sum/system.h b/lib/libmd5sum/system.h new file mode 100644 index 000000000..5082e6a62 --- /dev/null +++ b/lib/libmd5sum/system.h @@ -0,0 +1,245 @@ +/* system-dependent definitions for fileutils, textutils, and sh-utils packages. + Copyright (C) 89, 1991-1998, 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Include sys/types.h before this file. */ + +#include + +#ifdef STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISLNK +# undef S_ISMPB +# undef S_ISMPC +# undef S_ISNWK +# undef S_ISREG +# undef S_ISSOCK +#endif /* STAT_MACROS_BROKEN. */ + +#ifndef S_IFMT +# define S_IFMT 0170000 +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +#ifndef S_IEXEC +# define S_IEXEC S_IXUSR +#endif + +#ifndef S_IXUSR +# define S_IXUSR S_IEXEC +#endif +#ifndef S_IXGRP +# define S_IXGRP (S_IEXEC >> 3) +#endif +#ifndef S_IXOTH +# define S_IXOTH (S_IEXEC >> 6) +#endif +#ifndef S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +#if !defined(HAVE_MKFIFO) +# define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0)) +#endif + +#ifdef HAVE_SYS_PARAM_H +# include +#endif + +/* should be included before any preprocessor test + of _POSIX_VERSION. */ +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + + +#if HAVE_LIMITS_H +/* limits.h must come before pathmax.h because limits.h on some systems + undefs PATH_MAX, whereas pathmax.h sets PATH_MAX. */ +# include +#endif + +/* Don't use bcopy! Use memmove if source and destination may overlap, + memcpy otherwise. */ + +#ifdef HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#else +# include +#endif + +#include +#ifndef errno +extern int errno; +#endif + +#if HAVE_STDLIB_H +# define getopt system_getopt +# include +# undef getopt +#endif + +/* The following test is to work around the gross typo in + systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE + is defined to 0, not 1. */ +#if !EXIT_FAILURE +# undef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif + +#ifdef HAVE_FCNTL_H +# include +#else +# include +#endif + +#if !defined (SEEK_SET) +# define SEEK_SET 0 +# define SEEK_CUR 1 +# define SEEK_END 2 +#endif +#ifndef F_OK +# define F_OK 0 +# define X_OK 1 +# define W_OK 2 +# define R_OK 4 +#endif + +/* For systems that distinguish between text and binary I/O. + O_BINARY is usually declared in fcntl.h */ +#if !defined O_BINARY && defined _O_BINARY + /* For MSC-compatible compilers. */ +# define O_BINARY _O_BINARY +# define O_TEXT _O_TEXT +#endif +#if O_BINARY +# ifndef __DJGPP__ +# define setmode _setmode +# define fileno(_fp) _fileno (_fp) +# endif /* not DJGPP */ +# define SET_BINARY(_f) do {if (!isatty(_f)) setmode (_f, O_BINARY);} while (0) +# define SET_BINARY2(_f1, _f2) \ + do { \ + if (!isatty (_f1)) \ + { \ + setmode (_f1, O_BINARY); \ + if (!isatty (_f2)) \ + setmode (_f2, O_BINARY); \ + } \ + } while(0) +#else +# define SET_BINARY(f) (void)0 +# define SET_BINARY2(f1,f2) (void)0 +# define O_BINARY 0 +# define O_TEXT 0 +#endif /* O_BINARY */ + +#ifdef HAVE_DIRENT_H +# include +# define NLENGTH(direct) (strlen((direct)->d_name)) +#else /* not HAVE_DIRENT_H */ +# define dirent direct +# define NLENGTH(direct) ((direct)->d_namlen) +# ifdef HAVE_SYS_NDIR_H +# include +# endif /* HAVE_SYS_NDIR_H */ +# ifdef HAVE_SYS_DIR_H +# include +# endif /* HAVE_SYS_DIR_H */ +# ifdef HAVE_NDIR_H +# include +# endif /* HAVE_NDIR_H */ +#endif /* HAVE_DIRENT_H */ + +#ifdef CLOSEDIR_VOID +/* Fake a return value. */ +# define CLOSEDIR(d) (closedir (d), 0) +#else +# define CLOSEDIR(d) closedir (d) +#endif + +/* Get or fake the disk device blocksize. + Usually defined by sys/param.h (if at all). */ +#if !defined(DEV_BSIZE) && defined(BSIZE) +# define DEV_BSIZE BSIZE +#endif +#if !defined(DEV_BSIZE) && defined(BBSIZE) /* SGI */ +# define DEV_BSIZE BBSIZE +#endif +#ifndef DEV_BSIZE +# define DEV_BSIZE 4096 +#endif + +/* Extract or fake data from a `struct stat'. + ST_BLKSIZE: Optimal I/O blocksize for the file, in bytes. */ +#ifndef HAVE_ST_BLKSIZE +# define ST_BLKSIZE(statbuf) DEV_BSIZE +#else /* HAVE_ST_BLKSIZE */ +/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ +# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ + ? (statbuf).st_blksize : DEV_BSIZE) +#endif /* HAVE_ST_BLKSIZE */ + +#include "sys2.h" diff --git a/lib/libnet/Makefile.am b/lib/libnet/Makefile.am new file mode 100644 index 000000000..33472a037 --- /dev/null +++ b/lib/libnet/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libtuxbox-net.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtuxbox_net_a_SOURCES = libnet.c network_interfaces.cpp diff --git a/lib/libnet/libnet.c b/lib/libnet/libnet.c new file mode 100644 index 000000000..6db6baf67 --- /dev/null +++ b/lib/libnet/libnet.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void scanip( char *str, unsigned char *to ) +{ + int val; + int c; + char *sp; + int b=4; + + for( sp=str; b; b-- ) + { + val=0; + for(; (*sp != 0) && (*sp != '.'); sp++) + { + c = *sp - '0'; + if (( c < 0 ) || ( c>=10)) + break; + val=(val*10)+c; + } + *to=val; + to++; + if ( !*sp ) + break; + sp++; + } +} + +int netSetIP( char *dev, char *ip, char *mask, char *brdcast ) +{ + int fd; + struct ifreq req; + int rc=-1; + unsigned char adr_ip[4]; + unsigned char adr_mask[4]; + unsigned char adr_brdcast[4]; + struct sockaddr_in addr; + + fd=socket(AF_INET,SOCK_DGRAM,0); + if ( !fd ) + return -1; + + scanip( ip, adr_ip ); + scanip( mask, adr_mask ); + scanip( brdcast, adr_brdcast ); + + /* init structures */ + bzero(&req,sizeof(req)); + strcpy(req.ifr_name,dev); + + bzero(&addr,sizeof(addr)); + addr.sin_family = AF_INET; + + addr.sin_addr.s_addr = *((unsigned long *) adr_ip); + memcpy(&req.ifr_addr,&addr,sizeof(addr)); + if( ioctl(fd,SIOCSIFADDR,&req) < 0 ) + goto abbruch; + + addr.sin_addr.s_addr = *((unsigned long *) adr_mask); + memcpy(&req.ifr_addr,&addr,sizeof(addr)); + if( ioctl(fd,SIOCSIFNETMASK,&req) < 0 ) + goto abbruch; + + addr.sin_addr.s_addr = *((unsigned long *) adr_brdcast); + memcpy(&req.ifr_addr,&addr,sizeof(addr)); + if( ioctl(fd,SIOCSIFBRDADDR,&req) < 0 ) + goto abbruch; + + rc = 0; +abbruch: + close(fd); + + return rc; +} + +void netGetIP( char *dev, char *ip, char *mask, char *brdcast ) +{ + int fd; + struct ifreq req; + struct sockaddr_in *saddr; + unsigned char *addr; + + *ip=0; + *mask=0; + *brdcast=0; + + fd=socket(AF_INET,SOCK_DGRAM,0); + if ( !fd ) + return; + + + bzero(&req,sizeof(req)); + strcpy(req.ifr_name,dev); + saddr = (struct sockaddr_in *) &req.ifr_addr; + addr= (unsigned char*) &saddr->sin_addr.s_addr; + + if( ioctl(fd,SIOCGIFADDR,&req) == 0 ) + sprintf(ip,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]); + + if( ioctl(fd,SIOCGIFNETMASK,&req) == 0 ) + sprintf(mask,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]); + + if( ioctl(fd,SIOCGIFBRDADDR,&req) == 0 ) + sprintf(brdcast,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]); + + close(fd); + return; +} + +void netSetDefaultRoute( char *gw ) +{ + struct rtentry re; + struct sockaddr_in *in_addr; + unsigned char *addr; + int fd; + unsigned char adr_gw[4]; + + scanip( gw, adr_gw ); + + memset(&re,0,sizeof(struct rtentry)); + + in_addr = (struct sockaddr_in *)&re.rt_dst; + in_addr->sin_family = AF_INET; + + in_addr = (struct sockaddr_in *)&re.rt_gateway; + in_addr->sin_family = AF_INET; + addr=(unsigned char*)&in_addr->sin_addr.s_addr; + + in_addr = (struct sockaddr_in *)&re.rt_genmask; + in_addr->sin_family = AF_INET; + + fd=socket(AF_INET,SOCK_DGRAM,0); + if ( fd < 0 ) + return; + + re.rt_flags = RTF_GATEWAY | RTF_UP; + memcpy(addr,adr_gw,4); + + ioctl(fd,SIOCADDRT,&re); + + close(fd); + return; +} + +void netGetDefaultRoute( char *ip ) +{ + FILE *fp; + char interface[9]; + unsigned char destination[4]; + unsigned char gateway[4]; + char zeile[256]; + + *ip = 0 ; + fp = fopen("/proc/net/route","r"); + if (fp == NULL) + return; + fgets(zeile,sizeof(zeile),fp); + while(fgets(zeile,sizeof(zeile),fp)) + { + sscanf(zeile,"%8s %x %x",interface,(unsigned *) destination,(unsigned *) gateway); + if (*(unsigned *)destination == 0) + { + sprintf(ip,"%d.%d.%d.%d",gateway[0],gateway[1],gateway[2],gateway[3]); + break; + } + } + fclose(fp); +} + +static char dombuf[256]; +static char hostbuf[256]; +static char domis=0; +static char hostis=0; + +char *netGetDomainname( void ) +{ + if (!domis) + getdomainname( dombuf, 256 ); + domis=1; + return dombuf; +} + +void netSetDomainname( char *dom ) +{ + strcpy(dombuf,dom); + domis=1; + setdomainname(dombuf,strlen(dombuf)+1); +} + +char *netGetHostname( void ) +{ + if (!hostis) + gethostname( hostbuf, 256 ); + hostis=1; + return hostbuf; +} + +void netSetHostname( char *host ) +{ + strcpy(hostbuf,host); + hostis=1; + sethostname(hostbuf,strlen(hostbuf)+1); +} + +void netSetNameserver(const char * const ip) +{ + FILE *fp; + + fp = fopen("/etc/resolv.conf","w"); + if (!fp) + return; + +#if 0 + char *dom; + dom=netGetDomainname(); + if (dom && strlen(dom)>2) + fprintf(fp,"search %s\n",dom); +#endif + fprintf(fp, "# generated by neutrino\n"); + if ((ip != NULL) && (strlen(ip) > 0)) + fprintf(fp,"nameserver %s\n",ip); + fclose(fp); +} + +void netGetNameserver( char *ip ) +{ + FILE *fp; + char zeile[256]; + char *index; + unsigned zaehler; + + *ip = 0; + fp = fopen("/etc/resolv.conf","r"); + if (!fp) + return; + + while (fgets(zeile,sizeof(zeile),fp)) + { + if (!strncasecmp(zeile,"nameserver",10)) + { + index = zeile + 10; + while ( (*index == ' ') || (*index == '\t') ) + index++; + zaehler = 0; + while ( (zaehler < 15) && ( ((*index >= '0') && (*index <= '9')) || (*index == '.'))) + ip[zaehler++] = *(index++); + ip[zaehler] = 0; + break; + } + } + fclose(fp); +} diff --git a/lib/libnet/libnet.h b/lib/libnet/libnet.h new file mode 100644 index 000000000..a1ddad998 --- /dev/null +++ b/lib/libnet/libnet.h @@ -0,0 +1,27 @@ +#ifndef __libnet__ +#define __libnet__ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +extern int netSetIP( char *dev, char *ip, char *mask, char *brdcast ); +extern void netGetIP( char *dev, char *ip, char *mask, char *brdcast ); +extern void netSetDefaultRoute( char *gw ); +extern void netGetDefaultRoute( char *ip ); +extern char *netGetDomainname( void ); +extern void netSetDomainname( char *dom ); +extern char *netGetHostname( void ); +extern void netSetHostname( char *host ); +extern void netSetNameserver(const char *ip); +extern void netGetNameserver( char *ip ); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/libnet/network_interfaces.cpp b/lib/libnet/network_interfaces.cpp new file mode 100644 index 000000000..e3f57c0e3 --- /dev/null +++ b/lib/libnet/network_interfaces.cpp @@ -0,0 +1,373 @@ +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libnet/network_interfaces.cpp,v 1.6 2003/03/20 15:32:52 thegoodguy Exp $ + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +/* + * Known bugs: + * ----------- + * + * - extension of lines across multiple lines using \ is not supported + * + */ + +bool read_file(const std::string filename, std::list &line) +{ + std::string s; + std::ifstream in(filename.c_str()); + + line.clear(); + + if (!in.is_open()) + return false; + + while (getline(in, s)) + line.push_back(s); + + return true; +} + +bool write_file(const std::string filename, const std::list line) +{ + std::ofstream out(filename.c_str()); + + if (!out.is_open()) + return false; + + for (std::list::const_iterator it = line.begin(); it != line.end(); it++) + out << (*it) << std::endl; + + return true; +} + +std::list::iterator add_attributes(const std::map attribute, std::list &line, std::list::iterator here) +{ + for (std::map::const_iterator it = attribute.begin(); it != attribute.end(); it++) + { + std::ostringstream out; + out << '\t' << (it -> first) << ' ' << (it -> second); + line.insert(here, out.str()); + } + return here; +} + +std::string remove_interface_from_line(const std::string interface, const std::string line) +{ + std::string s; + std::istringstream in(line.c_str()); + std::ostringstream out; + bool contains_at_least_one_interface = false; + + if (in >> s) + { + out << s; /* auto */ + + while (in >> s) + { + if (s != interface) + { + out << ' ' << s; + contains_at_least_one_interface = true; + } + } + } + + return (contains_at_least_one_interface ? out.str() : ""); +} + +bool write_interface(const std::string filename, const std::string name, const bool automatic_start, const std::string family, const std::string method, const std::map attribute) +{ + std::string s; + std::list line; + bool found = false; + + read_file(filename, line); /* ignore return value */ + + for (std::list::iterator it = line.begin(); it != line.end(); ) + { + { + std::istringstream in((*it).c_str()); + + if (!(in >> s)) + { + it++; + continue; + } + + if (s != std::string("iface")) + { + if (s == std::string("auto")) + { + bool advance = true; + while (in >> s) + { + if (s == std::string(name)) + { + *it = remove_interface_from_line(name, *it); + if ((*it).empty()) + { + it = line.erase(it); /* erase advances it */ + advance = false; + } + break; + } + } + if (advance) + it++; + } + else + it++; + continue; + } + + if (!(in >> s)) + { + it++; + continue; + } + + if (s != std::string(name)) + { + it++; + continue; + } + } + + found = true; + + /* replace line */ + std::ostringstream out; + out << "iface " << name << ' ' << family << ' ' << method; + (*it) = out.str(); + + if (automatic_start) + line.insert(it, "auto " + name); + + /* add attributes */ + it++; + it = add_attributes(attribute, line, it); + + /* remove current attributes */ + while (it != line.end()) + { + std::istringstream in((*it).c_str()); + + if (!(in >> s)) /* retain empty lines */ + { + it++; + continue; + } + + if (s[0] == '#') /* retain comments */ + { + it++; + continue; + } + + if (s == std::string("iface")) + break; + + if (s == std::string("auto")) + break; + + if (s == std::string("mapping")) + break; + + it = line.erase(it); + } + } + + if (!found) + { + std::ostringstream out; + + if (automatic_start) + line.push_back("auto " + name); + + out << "iface " << name << ' ' << family << ' ' << method; + line.push_back(out.str()); + add_attributes(attribute, line, line.end()); + } + + return write_file(filename, line); +} + +bool read_interface(const std::string filename, const std::string name, bool &automatic_start, std::string &family, std::string &method, std::map &attribute) +{ + std::string s; + std::string t; + std::ifstream in(filename.c_str()); + bool advance = true; + + automatic_start = false; + attribute.clear(); + + if (!in.is_open()) + return false; + + while (true) + { + if (advance) + { + if (!getline(in, s)) + break; + } + else + advance = true; + + { + std::istringstream in(s.c_str()); + + if (!(in >> s)) + continue; + + if (s != std::string("iface")) + { + if (s == std::string("auto")) + { + while (in >> s) + { + if (s == std::string(name)) + { + automatic_start = true; + break; + } + } + } + continue; + } + + if (!(in >> s)) + continue; + + if (s != std::string(name)) + continue; + + if (!(in >> s)) + continue; + + if (!(in >> t)) + continue; + + family = s; + method = t; + } + + while (true) + { + if (!getline(in, s)) + return true; + + std::istringstream in(s.c_str()); + + if (!(in >> t)) /* ignore empty lines */ + continue; + + if (t[0] == '#') /* ignore comments */ + continue; + + if (t == std::string("iface")) + break; + + if (t == std::string("auto")) + break; + + if (t == std::string("mapping")) + break; + + if (!(in >> s)) + continue; + + attribute[t] = s; + } + advance = false; + } + + return true; +} + +bool getInetAttributes(const std::string name, bool &automatic_start, std::string &address, std::string &netmask, std::string &broadcast, std::string &gateway) +{ + std::string family; + std::string method; + std::map attribute; + + if (!read_interface("/etc/network/interfaces", name, automatic_start, family, method, attribute)) + return false; + + if (family != "inet") + return false; + + if (method != "static") + return false; + + address = ""; + netmask = ""; + broadcast = ""; + gateway = ""; + + for (std::map::const_iterator it = attribute.begin(); it != attribute.end(); it++) + { + if ((*it).first == "address") + address = (*it).second; + if ((*it).first == "netmask") + netmask = (*it).second; + if ((*it).first == "broadcast") + broadcast = (*it).second; + if ((*it).first == "gateway") + gateway = (*it).second; + } + return true; +} + +bool addLoopbackDevice(const std::string name, const bool automatic_start) +{ + std::map attribute; + + return write_interface("/etc/network/interfaces", name, automatic_start, "inet", "loopback", attribute); +} + +bool setStaticAttributes(const std::string name, const bool automatic_start, const std::string address, const std::string netmask, const std::string broadcast, const std::string gateway) +{ + std::map attribute; + + attribute["address"] = address; + attribute["netmask"] = netmask; + + if (!broadcast.empty()) + attribute["broadcast"] = broadcast; + + if (!gateway.empty()) + attribute["gateway"] = gateway; + + return write_interface("/etc/network/interfaces", name, automatic_start, "inet", "static", attribute); +} + +bool setDhcpAttributes(const std::string name, const bool automatic_start) +{ + std::map attribute; + + return write_interface("/etc/network/interfaces", name, automatic_start, "inet", "dhcp", attribute); +} diff --git a/lib/libnet/network_interfaces.h b/lib/libnet/network_interfaces.h new file mode 100644 index 000000000..56bbe43a3 --- /dev/null +++ b/lib/libnet/network_interfaces.h @@ -0,0 +1,35 @@ +#ifndef __network_interfaces_h__ +#define __network_interfaces_h__ + +/* + * $Header: /cvs/tuxbox/apps/misc/libs/libnet/network_interfaces.h,v 1.4 2003/03/20 14:07:32 thegoodguy Exp $ + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +bool getInetAttributes(const std::string name, bool &automatic_start, std::string &address, std::string &netmask, std::string &broadcast, std::string &gateway); + +bool addLoopbackDevice(const std::string name, const bool automatic_start); + +bool setStaticAttributes(const std::string name, const bool automatic_start, const std::string address, const std::string netmask, const std::string broadcast, const std::string gateway); + +bool setDhcpAttributes(const std::string name, const bool automatic_start); + +#endif /* __network_interfaces_h__ */ diff --git a/lib/libtuxtxt/Makefile.am b/lib/libtuxtxt/Makefile.am new file mode 100644 index 000000000..c4d95e4ef --- /dev/null +++ b/lib/libtuxtxt/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libcoolstream + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libtuxtxt.a + +libtuxtxt_a_SOURCES = libtuxtxt.cpp tuxtxt.cpp diff --git a/lib/libtuxtxt/libtuxtxt.cpp b/lib/libtuxtxt/libtuxtxt.cpp new file mode 100644 index 000000000..14b5b4758 --- /dev/null +++ b/lib/libtuxtxt/libtuxtxt.cpp @@ -0,0 +1,95 @@ +/****************************************************************************** + * <<< TuxTxt - Teletext Plugin >>> * + * * + * (c) Thomas "LazyT" Loewe 2002-2003 (LazyT@gmx.net) * + * * + * TOP-Text Support 2004 by Roland Meier * + * Info entnommen aus videotext-0.6.19991029, * + * Copyright (c) 1994-96 Martin Buck * + * * + ******************************************************************************/ + +#define TUXTXT_DEBUG 0 + +#include +#include + +#include "tuxtxt_def.h" +#include "tuxtxt_common.h" + +/****************************************************************************** + * Initialize * + ******************************************************************************/ + +static int tuxtxt_initialized=0; + +int tuxtxt_init() +{ + if ( tuxtxt_initialized ) + return 0; + + tuxtxt_initialized=1; + + /* init data */ + memset(&tuxtxt_cache.astCachetable, 0, sizeof(tuxtxt_cache.astCachetable)); + memset(&tuxtxt_cache.astP29, 0, sizeof(tuxtxt_cache.astP29)); + + tuxtxt_clear_cache(); + tuxtxt_cache.receiving = 0; + tuxtxt_cache.thread_starting = 0; + tuxtxt_cache.vtxtpid = -1; + tuxtxt_cache.thread_id = 0; + tuxtxt_cache.dmx = -1; + return 1;//tuxtxt_init_demuxer(); +} + +/****************************************************************************** + * Interface to caller * + ******************************************************************************/ + +int tuxtxt_stop() +{ + if (!tuxtxt_cache.receiving) return 1; + tuxtxt_cache.receiving = 0; + + return tuxtxt_stop_thread(); +} + +void tuxtxt_start(int tpid) +{ + if (tuxtxt_cache.vtxtpid != tpid) + { + tuxtxt_stop(); + tuxtxt_clear_cache(); + tuxtxt_cache.page = 0x100; + tuxtxt_cache.vtxtpid = tpid; + tuxtxt_start_thread(); + } + else if (!tuxtxt_cache.thread_starting && !tuxtxt_cache.receiving) + { + tuxtxt_start_thread(); + } +} + +void tuxtxt_close() +{ +#if TUXTXT_DEBUG + printf ("libtuxtxt: cleaning up\n"); +#endif + tuxtxt_stop(); +#if 0 + if (tuxtxt_cache.dmx != -1) + close(tuxtxt_cache.dmx); +#endif + tuxtxt_cache.dmx = -1; + tuxtxt_clear_cache(); + tuxtxt_initialized=0; +} + +/* Local Variables: */ +/* indent-tabs-mode:t */ +/* tab-width:3 */ +/* c-basic-offset:3 */ +/* comment-column:0 */ +/* fill-column:120 */ +/* End: */ diff --git a/lib/libtuxtxt/tuxtxt.cpp b/lib/libtuxtxt/tuxtxt.cpp new file mode 100644 index 000000000..1a309dadf --- /dev/null +++ b/lib/libtuxtxt/tuxtxt.cpp @@ -0,0 +1,6176 @@ +/****************************************************************************** + * <<< TuxTxt - Teletext Plugin >>> * + * * + * (c) Thomas "LazyT" Loewe 2002-2003 (LazyT@gmx.net) * + * * + * continued 2004-2005 by Roland Meier * + * and DBLuelle * + * * + * ported 2006 to Dreambox 7025 / 32Bit framebuffer * + * by Seddi * + * * + * ported 2009 to HD1 by Coolstream LTD * + * * + ******************************************************************************/ + +#include "tuxtxt.h" +#include + +void FillRect(int x, int y, int w, int h, int color) +{ + unsigned char *p = lfb + x*4 + y * fix_screeninfo.line_length; +#if 1 + unsigned int col = bgra[color][3] << 24 | bgra[color][2] << 16 | bgra[color][1] << 8 | bgra[color][0]; + + if (w > 0) + for (int count = 0; count < h; count++) { + unsigned int * dest0 = (unsigned int *)p; + for (int i = 0; i < w; i++) + *(dest0++) = col; + p += fix_screeninfo.line_length; + } +#else + int xtmp; + if (w > 0) + for ( ; h > 0 ; h--) + { + for (xtmp=0; xtmp<=w; xtmp++) + { + memcpy(p+xtmp*4,bgra[color],4); + } + p += fix_screeninfo.line_length; + } +#endif +} + +void FillBorder(int color) +{ + int ys = (var_screeninfo.yres-var_screeninfo.yoffset); + FillRect(0 , ys ,StartX ,var_screeninfo.yres ,color); + FillRect(StartX, ys ,displaywidth,StartY ,color); + FillRect(StartX, ys+StartY+25*fontheight,displaywidth,var_screeninfo.yres-(StartY+25*fontheight),color); + + if (screenmode == 0 ) + FillRect(StartX+displaywidth, ys,var_screeninfo.xres-(StartX+displaywidth),var_screeninfo.yres ,color); +} + +int getIndexOfPageInHotlist() +{ + int i; + for (i = 0; i <= maxhotlist; i++) + { + if (tuxtxt_cache.page == hotlist[i]) + return i; + } + return -1; +} + +void gethotlist() +{ + FILE *hl; + char line[100]; + + hotlistchanged = 0; + maxhotlist = -1; + sprintf(line, CONFIGDIR "/tuxtxt/hotlist%d.conf", tuxtxt_cache.vtxtpid); +#if TUXTXT_DEBUG + printf("TuxTxt = 0x100 && hotlist[maxhotlist+1] <= 0x899) + { +#if TUXTXT_DEBUG + printf(" %03x", hotlist[maxhotlist+1]); +#endif + maxhotlist++; + continue; + } + } +#if TUXTXT_DEBUG + else + printf(" ?%s?", line); +#endif + } while (maxhotlist < (int) (sizeof(hotlist)/sizeof(hotlist[0])-1)); + fclose(hl); + } +#if TUXTXT_DEBUG + printf(">\n"); +#endif + if (maxhotlist < 0) /* hotlist incorrect or not found */ + { + hotlist[0] = 0x100; /* create one */ + hotlist[1] = 0x303; + maxhotlist = 1; + } +} + +void savehotlist() +{ + FILE *hl; + char line[100]; + int i; + + hotlistchanged = 0; + sprintf(line, CONFIGDIR "/tuxtxt/hotlist%d.conf", tuxtxt_cache.vtxtpid); +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif +} + +#define number2char(c) ((c) + (((c) <= 9) ? '0' : ('A' - 10))) +/* print hex-number into string, s points to last digit, caller has to provide enough space, no termination */ +void hex2str(char *s, unsigned int n) +{ + do { + char c = (n & 0xF); + *s-- = number2char(c); + n >>= 4; + } while (n); +} + + +int toptext_getnext(int startpage, int up, int findgroup) +{ + int current, nextgrp, nextblk; + + int stoppage = (tuxtxt_is_dec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode + nextgrp = nextblk = 0; + current = startpage; + + do { + if (up) + tuxtxt_next_dec(¤t); + else + tuxtxt_prev_dec(¤t); + + if (!tuxtxt_cache.bttok || tuxtxt_cache.basictop[current]) /* only if existent */ + { + if (findgroup) + { + if (tuxtxt_cache.basictop[current] >= 6 && tuxtxt_cache.basictop[current] <= 7) + return current; + if (!nextgrp && (current&0x00F) == 0) + nextgrp = current; + } + if (tuxtxt_cache.basictop[current] >= 2 && tuxtxt_cache.basictop[current] <= 5) /* always find block */ + return current; + + if (!nextblk && (current&0x0FF) == 0) + nextblk = current; + } + } while (current != stoppage); + + if (nextgrp) + return nextgrp; + else if (nextblk) + return nextblk; + else + return startpage; +} + +void RenderClearMenuLineBB(char *p, tstPageAttr *attrcol, tstPageAttr *attr) +{ + int col; + + PosX = TOPMENUSTARTX; + RenderCharBB(' ', attrcol); /* indicator for navigation keys */ +#if 0 + RenderCharBB(' ', attr); /* separator */ +#endif + for(col = 0; col < TOPMENUCHARS; col++) + { + RenderCharBB(*p++, attr); + } + PosY += fontheight; + memset(p-TOPMENUCHARS, ' ', TOPMENUCHARS); /* init with spaces */ +} + +void ClearBB(int color) +{ + FillRect(0,(var_screeninfo.yres-var_screeninfo.yoffset),fix_screeninfo.line_length,var_screeninfo.yres, color); +} + +void ClearFB(int color) +{ + memset(lfb,0, var_screeninfo.yres*fix_screeninfo.line_length); + //FillRect(0,var_screeninfo.yoffset,fix_screeninfo.line_length,var_screeninfo.yres,color); +} + +void ClearB(int color) +{ + FillRect(0,0,fix_screeninfo.line_length,var_screeninfo.yres*2,color); +} + + +int GetCurFontWidth() +{ + int mx = (displaywidth)%(40-nofirst); // # of unused pixels + int abx = (mx == 0 ? displaywidth+1 : (displaywidth)/(mx+1));// distance between 'inserted' pixels + int nx= abx+1-((PosX-sx) % (abx+1)); // # of pixels to next insert + return fontwidth+(((PosX+fontwidth+1-sx) <= displaywidth && nx <= fontwidth+1) ? 1 : 0); +} + +void SetPosX(int column) +{ + PosX = StartX; + int i; + for (i = 0; i < column-nofirst; i++) + PosX += GetCurFontWidth(); +} + +void setfontwidth(int newwidth) +{ + if (fontwidth != newwidth) + { + int i; + fontwidth = newwidth; + if (usettf) + typettf.width = (FT_UShort) fontwidth * TTFWidthFactor16 / 16; + else + { + if (newwidth < 11) + newwidth = 21; + else if (newwidth < 14) + newwidth = 22; + else + newwidth = 23; + typettf.width = typettf.height = (FT_UShort) newwidth; + } + for (i = 0; i <= 12; i++) + axdrcs[i] = (fontwidth * i + 6) / 12; + } +} + + +void setcolors(unsigned short *pcolormap, int offset, int number) +{ + int i,trans_tmp; + int j = offset; /* index in global color table */ + + trans_tmp=25-trans_mode; + + bgra[transp2][3]=((trans_tmp+7)<<11 | 0x7FF)>>8; + + for (i = 0; i < number; i++) + { + int r = (pcolormap[i] << 12 & 0xF000) >> 8; + int g = (pcolormap[i] << 8 & 0xF000) >> 8; + int b = (pcolormap[i] << 4 & 0xF000) >> 8; + + r = (r * (0x3f+(color_mode<<3))) >> 8; + g = (g * (0x3f+(color_mode<<3))) >> 8; + b = (b * (0x3f+(color_mode<<3))) >> 8; + + bgra[j][2]=r; + bgra[j][1]=g; + bgra[j][0]=b; + + j++; + } +} + + +/* hexdump of page contents to stdout for debugging */ +void dump_page() +{ + int r, c; + char *p; + unsigned char pagedata[23*40]; + + if (!tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]) + return; + tuxtxt_decompress_page(tuxtxt_cache.page,tuxtxt_cache.subpage,pagedata); + for (r=1; r < 24; r++) + { + p = (char *) pagedata+40*(r-1); + for (c=0; c < 40; c++) + printf(" %02x", *p++); + printf("\n"); + p = (char *) page_char + 40*r; + for (c=0; c < 40; c++) + printf(" %c", *p++); + printf("\n"); + } +} + + +/* get object data */ +/* in: absolute triplet number (0..506, start at packet 3 byte 1) */ +/* in: pointer to cache struct of page data */ +/* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */ +int iTripletNumber2Data(int iONr, tstCachedPage *pstCachedPage, unsigned char* pagedata) +{ + if (iONr > 506 || 0 == pstCachedPage) + return -1; + + unsigned char *p; + int packet = (iONr / 13) + 3; + int packetoffset = 3 * (iONr % 13); + + if (packet <= 23) + p = pagedata + 40*(packet-1) + packetoffset + 1; + else if (packet <= 25) + { + if (0 == pstCachedPage->pageinfo.p24) + return -1; + p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1; + } + else + { + int descode = packet - 26; + if (0 == pstCachedPage->pageinfo.ext) + return -1; + if (0 == pstCachedPage->pageinfo.ext->p26[descode]) + return -1; + p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */ + } + return deh24(p); +} + +#define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40)) + +/* dump interpreted object data to stdout */ +/* in: 18 bit object data */ +/* out: termination info, >0 if end of object */ +void eval_object(int iONr, tstCachedPage *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + tObjType ObjType, unsigned char* pagedata) +{ + int iOData; + int iONr1 = iONr + 1; /* don't terminate after first triplet */ + unsigned char drcssubp=0, gdrcssubp=0; + signed char endcol = -1; /* last column to which to extend attribute changes */ + tstPageAttr attrPassive = atrtable[ATR_PassiveDefault]; /* current attribute for passive objects */ + + do + { + iOData = iTripletNumber2Data(iONr, pstCachedPage,pagedata); /* get triplet data, next triplet */ + if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */ + break; +#if TUXTXT_DEBUG + if (dumpl25) + printf(" t%03d ", iONr); +#endif + if (endcol < 0) + { + if (ObjType == OBJ_ACTIVE) + { + endcol = 40; + } + else if (ObjType == OBJ_ADAPTIVE) /* search end of line */ + { + int i; + for (i = iONr; i <= 506; i++) + { + int iTempOData = iTripletNumber2Data(i, pstCachedPage,pagedata); /* get triplet data, next triplet */ + int iAddress = (iTempOData ) & 0x3f; + int iMode = (iTempOData >> 6) & 0x1f; + //int iData = (iTempOData >> 11) & 0x7f; + if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */ + (iAddress >= 40 /* new row: row address and */ + && (iMode == 0x01 || /* Full Row Color or */ + iMode == 0x04 || /* Set Active Position */ + (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */ + iMode == 0x17))) /* Object Termination */ + break; + if (iAddress < 40 && iMode != 0x06) + endcol = iAddress; + } +#if TUXTXT_DEBUG + if (dumpl25) + printf(" endcol %02d", endcol); +#endif + } + } + iONr++; + } + while (0 == eval_triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata) + || iONr1 == iONr); /* repeat until termination reached */ +} + +void eval_NumberedObject(int p, int s, int packet, int triplet, int high, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0) +{ + if (!packet || 0 == tuxtxt_cache.astCachetable[p][s]) + return; + unsigned char pagedata[23*40]; + tuxtxt_decompress_page(p, s,pagedata); + + + int idata = deh24(pagedata + 40*(packet-1) + 1 + 3*triplet); + int iONr; + + if (idata < 0) /* hamming error: ignore triplet */ + return; + if (high) + iONr = idata >> 9; /* triplet number of odd object data */ + else + iONr = idata & 0x1ff; /* triplet number of even object data */ + if (iONr <= 506) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf("P%xT%x%c %8s %c#%03d@%03d\n", packet, triplet, "LH"[!!high], /* pointer pos, type, number, data pos */ + ObjectType[triplet % 3], "PCD"[triplet % 3], 8*packet + 2*(triplet-1)/3, iONr); + +#endif + eval_object(iONr, tuxtxt_cache.astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata); + } +} + +int eval_triplet(int iOData, tstCachedPage *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + unsigned char *drcssubp, unsigned char *gdrcssubp, + signed char *endcol, tstPageAttr *attrPassive, unsigned char* pagedata) +{ + int iAddress = (iOData ) & 0x3f; + int iMode = (iOData >> 6) & 0x1f; + int iData = (iOData >> 11) & 0x7f; + + if (iAddress < 40) /* column addresses */ + { + int offset; /* offset to page_char and page_atrb */ + + if (iMode != 0x06) + *pAPx = iAddress; /* new Active Column */ + offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to page_char and page_atrb */ +#if TUXTXT_DEBUG + if (dumpl25) + printf(" M%02xC%02xD%02x %d r:%d ch:%02x", iMode, iAddress, iData, *endcol,*pAPy0 + *pAPy,page_char[offset]); +#endif + + switch (iMode) + { + case 0x00: + if (0 == (iData>>5)) + { + int newcolor = iData & 0x1f; + if (*endcol < 0) /* passive object */ + attrPassive->fg = newcolor; + else if (*endcol == 40) /* active object */ + { + tstPageAttr *p = &page_atrb[offset]; + int oldcolor = (p)->fg; /* current color (set-after) */ + int c = *pAPx0 + *pAPx; /* current column absolute */ + do + { + p->fg = newcolor; + p++; + c++; + } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */ + } + else /* adaptive object */ + { + tstPageAttr *p = &page_atrb[offset]; + int c = *pAPx; /* current column relative to object origin */ + do + { + p->fg = newcolor; + p++; + c++; + } while (c <= *endcol); + } +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d FGCol T%x#%x", iAddress, (iData>>3)&0x03, iData&0x07); +#endif + } + break; + case 0x01: + if (iData >= 0x20) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d BlockMosaic G1 #%02x", iAddress, iData); +#endif + page_char[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G1C; /* FIXME: separated? */ + page_atrb[offset] = *attrPassive; + } + else if (page_atrb[offset].charset != C_G1S) + page_atrb[offset].charset = C_G1C; /* FIXME: separated? */ + } + break; + case 0x02: + case 0x0b: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d G3 #%02x f%db%d", iAddress, iData,attrPassive->fg, attrPassive->bg); +#endif + page_char[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G3; + page_atrb[offset] = *attrPassive; + } + else + page_atrb[offset].charset = C_G3; + break; + case 0x03: + if (0 == (iData>>5)) + { + int newcolor = iData & 0x1f; + if (*endcol < 0) /* passive object */ + attrPassive->bg = newcolor; + else if (*endcol == 40) /* active object */ + { + tstPageAttr *p = &page_atrb[offset]; + int oldcolor = (p)->bg; /* current color (set-after) */ + int c = *pAPx0 + *pAPx; /* current column absolute */ + do + { + p->bg = newcolor; + if (newcolor == black) + p->IgnoreAtBlackBgSubst = 1; + p++; + c++; + } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */ + } + else /* adaptive object */ + { + tstPageAttr *p = &page_atrb[offset]; + int c = *pAPx; /* current column relative to object origin */ + do + { + p->bg = newcolor; + if (newcolor == black) + p->IgnoreAtBlackBgSubst = 1; + p++; + c++; + } while (c <= *endcol); + } +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d BGCol T%x#%x", iAddress, (iData>>3)&0x03, iData&0x07); +#endif + } + break; + case 0x06: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" PDC"); +#endif + /* ignore */ + break; + case 0x07: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d Flash M%xP%x", iAddress, iData & 0x03, (iData >> 2) & 0x07); +#endif + if ((iData & 0x60) != 0) break; // reserved data field + if (*endcol < 0) /* passive object */ + { + attrPassive->flashing=iData & 0x1f; + page_atrb[offset] = *attrPassive; + } + else + page_atrb[offset].flashing=iData & 0x1f; + break; + case 0x08: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d G0+G2 set #%02x (p105)", iAddress, iData); +#endif + if (*endcol < 0) /* passive object */ + { + attrPassive->setG0G2=iData & 0x3f; + page_atrb[offset] = *attrPassive; + } + else + page_atrb[offset].setG0G2=iData & 0x3f; + break; + case 0x09: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d G0 #%02x '%c'", iAddress, iData, iData); +#endif + page_char[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G0P; /* FIXME: secondary? */ + attrPassive->setX26 = 1; + page_atrb[offset] = *attrPassive; + } + else + { + page_atrb[offset].charset = C_G0P; /* FIXME: secondary? */ + page_atrb[offset].setX26 = 1; + } + break; +// case 0x0b: (see 0x02) + case 0x0c: + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d Attribute%s%s%s%s%s%s", iAddress, + (iData & 0x40) ? " DoubleWidth" : "", + (iData & 0x20) ? " UnderlineSep" : "", + (iData & 0x10) ? " InvColour" : "", + (iData & 0x04) ? " Conceal" : "", + (iData & 0x02) ? " Boxing" : "", + (iData & 0x01) ? " DoubleHeight" : ""); +#endif + int conc = (iData & 0x04); + int inv = (iData & 0x10); + int dw = (iData & 0x40 ?1:0); + int dh = (iData & 0x01 ?1:0); + int sep = (iData & 0x20); + int bw = (iData & 0x02 ?1:0); + if (*endcol < 0) /* passive object */ + { + if (conc) + { + attrPassive->concealed = 1; + attrPassive->fg = attrPassive->bg; + } + attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0); + attrPassive->doubleh = dh; + attrPassive->doublew = dw; + attrPassive->boxwin = bw; + if (bw) attrPassive->IgnoreAtBlackBgSubst = 0; + if (sep) + { + if (attrPassive->charset == C_G1C) + attrPassive->charset = C_G1S; + else + attrPassive->underline = 1; + } + else + { + if (attrPassive->charset == C_G1S) + attrPassive->charset = C_G1C; + else + attrPassive->underline = 0; + } + } + else + { + + int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */ + int c1 = offset; + tstPageAttr *p = &page_atrb[offset]; + do + { + p->inverted = (inv ? 1- p->inverted : 0); + if (conc) + { + p->concealed = 1; + p->fg = p->bg; + } + if (sep) + { + if (p->charset == C_G1C) + p->charset = C_G1S; + else + p->underline = 1; + } + else + { + if (p->charset == C_G1S) + p->charset = C_G1C; + else + p->underline = 0; + } + p->doublew = dw; + p->doubleh = dh; + p->boxwin = bw; + if (bw) p->IgnoreAtBlackBgSubst = 0; + p++; + c++; + c1++; + } while (c < *endcol); + } + break; + } + case 0x0d: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d %cDRCS #%02x", iAddress, (iData & 0x40) ? ' ' : 'G', iData & 0x3f); +#endif + page_char[offset] = iData & 0x3f; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); + page_atrb[offset] = *attrPassive; + } + else + page_atrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); + break; + case 0x0f: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d G2 #%02x", iAddress, iData); +#endif + page_char[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G2; + page_atrb[offset] = *attrPassive; + } + else + page_atrb[offset].charset = C_G2; + break; + default: + if (iMode == 0x10 && iData == 0x2a) + iData = '@'; + if (iMode >= 0x10) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" ,%02d G0 #%02x %c +diacr. %x", iAddress, iData, iData, iMode & 0x0f); +#endif + page_char[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G0P; + attrPassive->diacrit = iMode & 0x0f; + attrPassive->setX26 = 1; + page_atrb[offset] = *attrPassive; + } + else + { + page_atrb[offset].charset = C_G0P; + page_atrb[offset].diacrit = iMode & 0x0f; + page_atrb[offset].setX26 = 1; + } + } + break; /* unsupported or not yet implemented mode: ignore */ + } /* switch (iMode) */ + } + else /* ================= (iAddress >= 40): row addresses ====================== */ + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" M%02xR%02xD%02x", iMode, iAddress, iData); +#endif + switch (iMode) + { + case 0x00: + if (0 == (iData>>5)) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" FScrCol T%x#%x", (iData>>3)&0x03, iData&0x07); +#endif + FullScrColor = iData & 0x1f; + } + break; + case 0x01: + if (*endcol == 40) /* active object */ + { + *pAPy = RowAddress2Row(iAddress); /* new Active Row */ + + int color = iData & 0x1f; + int row = *pAPy0 + *pAPy; + int maxrow; +#if TUXTXT_DEBUG + if (dumpl25) + { + printf(" AP=%d,0", RowAddress2Row(iAddress)); + if (0 == (iData>>5)) + printf(" FRowCol T%x#%x", (iData>>3)&0x03, iData&0x07); + else if (3 == (iData>>5)) + printf(" FRowCol++ T%x#%x", (iData>>3)&0x03, iData&0x07); + } +#endif + if (row <= 24 && 0 == (iData>>5)) + maxrow = row; + else if (3 == (iData>>5)) + maxrow = 24; + else + maxrow = -1; + for (; row <= maxrow; row++) + FullRowColor[row] = color; + *endcol = -1; + } + break; + case 0x04: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" AP=%d,%d", RowAddress2Row(iAddress), iData); +#endif + *pAPy = RowAddress2Row(iAddress); /* new Active Row */ + if (iData < 40) + *pAPx = iData; /* new Active Column */ + *endcol = -1; /* FIXME: check if row changed? */ + break; + case 0x07: +#if TUXTXT_DEBUG + if (dumpl25) + { + if (iAddress == 0x3f) + printf(" AP=0,0"); + if (0 == (iData>>5)) + printf(" Address Display R0 FRowCol T%x#%x", (iData>>3)&0x03, iData&0x07); + else if (3 == (iData>>5)) + printf(" Address Display R0->24 FRowCol T%x#%x", (iData>>3)&0x03, iData&0x07); + } +#endif + if (iAddress == 0x3f) + { + *pAPx = *pAPy = 0; /* new Active Position 0,0 */ + if (*endcol == 40) /* active object */ + { + int color = iData & 0x1f; + int row = *pAPy0; // + *pAPy; + int maxrow; + + if (row <= 24 && 0 == (iData>>5)) + maxrow = row; + else if (3 == (iData>>5)) + maxrow = 24; + else + maxrow = -1; + for (; row <= maxrow; row++) + FullRowColor[row] = color; + } + *endcol = -1; + } + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" PDC"); +#endif + /* ignore */ + break; + case 0x10: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" AP=%d,%d temp. Origin Modifier", iAddress - 40, iData); +#endif + tAPy = iAddress - 40; + tAPx = iData; + break; + case 0x11: + case 0x12: + case 0x13: + if (iAddress & 0x10) /* POP or GPOP */ + { + unsigned char APx = 0, APy = 0; + unsigned char APx0 = *pAPx0 + *pAPx + tAPx, APy0 = *pAPy0 + *pAPy + tAPy; + int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03); + int packet = (iAddress & 0x03) + 1; + int subp = iData & 0x0f; + int high = (iData >> 4) & 0x01; + + + if (APx0 < 40) /* not in side panel */ + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" Object Invocation %5s %8s S%xP%xT%x%c %c#%03d\n---", + ObjectSource[(iAddress >> 3) & 0x03], ObjectType[iMode & 0x03], + subp, + packet, + triplet, + "LH"[high], /* low/high */ + "PCD"[triplet % 3], + 8*packet + 2*(triplet-1)/3 + 1); +#endif + eval_NumberedObject((iAddress & 0x08) ? gpop : pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0); +#if TUXTXT_DEBUG + if (dumpl25) + printf("---"); +#endif + } +#if TUXTXT_DEBUG + else if (dumpl25) + printf("Object Invocation for Side Panel - ignored"); +#endif + } + else if (iAddress & 0x08) /* local: eval invoked object */ + { + unsigned char APx = 0, APy = 0; + unsigned char APx0 = *pAPx0 + *pAPx + tAPx, APy0 = *pAPy0 + *pAPy + tAPy; + int descode = ((iAddress & 0x01) << 3) | (iData >> 4); + int triplet = iData & 0x0f; + + if (APx0 < 40) /* not in side panel */ + { +#if TUXTXT_DEBUG + if (dumpl25) + printf(" local Object Invocation %5s %8s D%xT%x:\n---", + ObjectSource[(iAddress >> 3) & 0x03], ObjectType[iMode & 0x03], descode, triplet); +#endif + eval_object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata); +#if TUXTXT_DEBUG + if (dumpl25) + printf("---"); +#endif + } +#if TUXTXT_DEBUG + else if (dumpl25) + printf("local Object Invocation for Side Panel - ignored"); +#endif + } + break; + case 0x15: + case 0x16: + case 0x17: + if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */ + break; /* ignore */ +#if TUXTXT_DEBUG + if (dumpl25) + { + printf(" Object Definition %8s", ObjectType[iMode & 0x03]); + { /* *POP */ + int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03); + int packet = (iAddress & 0x03) + 1; + printf(" S%xP%xT%x%c %c#%03d", + iData & 0x0f, /* subpage */ + packet, + triplet, + "LH"[(iData >> 4) & 0x01], /* low/high */ + "PCD"[triplet % 3], + 8*packet + 2*(triplet-1)/3 + 1); + } + { /* local */ + int descode = ((iAddress & 0x03) << 3) | (iData >> 4); + int triplet = iData & 0x0f; + printf(" D%xT%x", descode, triplet); + } + putchar('\n'); + } +#endif + tAPx = tAPy = 0; + *endcol = -1; + return 0xFF; /* termination by object definition */ + break; + case 0x18: + if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */ + break; /* ignore */ +#if TUXTXT_DEBUG + if (dumpl25) + printf(" %cDRCS S%x", (iData & 0x40) ? ' ' : 'G', iData & 0x0f); /* subpage */ +#endif + if (iData & 0x40) + *drcssubp = iData & 0x0f; + else + *gdrcssubp = iData & 0x0f; + break; + case 0x1f: +#if TUXTXT_DEBUG + if (dumpl25) + printf(" Termination Marker %x\n", iData); /* subpage */ +#endif + tAPx = tAPy = 0; + *endcol = -1; + return 0x80 | iData; /* explicit termination */ + break; + default: + break; /* unsupported or not yet implemented mode: ignore */ + } /* switch (iMode) */ + } /* (iAddress >= 40): row addresses */ +#if TUXTXT_DEBUG + if (dumpl25 && iAddress < 40) + putchar('\n'); +#endif + if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */ + tAPx = tAPy = 0; + + + return 0; /* normal exit, no termination */ +} +int setnational(unsigned char sec) +{ + switch (sec) + { + case 0x08: + return NAT_PL; //polish + case 0x16: + case 0x36: + return NAT_TR; //turkish + case 0x1d: + return NAT_SR; //serbian, croatian, slovenian + case 0x20: + case 0x24: + case 0x25: + return NAT_RU; // cyrillic + case 0x22: + return NAT_ET; // estonian + case 0x23: + return NAT_LV; // latvian, lithuanian + case 0x37: + return NAT_GR; // greek + case 0x47: + case 0x57: + // TODO : arabic + break; + case 0x55: + // TODO : hebrew + break; + + } + return countryconversiontable[sec & 0x07]; +} + +/* evaluate level 2.5 information */ +void eval_l25() +{ + memset(FullRowColor, 0, sizeof(FullRowColor)); + FullScrColor = black; + + if (!tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]) + return; + +#if TUXTXT_DEBUG + if (dumpl25) + printf("=== %03x/%02x %d/%d===\n", tuxtxt_cache.page, tuxtxt_cache.subpage,tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.nationalvalid,tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.national); +#endif + +#if 0 //TUXTXT_DEBUG I don't think we need this any longer because this code is unreachable (HexPages are handled before eval_l25() is called) + if (pageinfo->function == FUNC_MOT) /* magazine organization table */ + { + int i; + unsigned char *p = pagedata; + + printf("(G)POP/(G)DRCS-associations:\n"); + printf(" 0011223344556677889900112233445566778899"); + for (i = 0; i < 200; i++) + { + if (p[i] == 0xff) + break; + if (0 == (i % 40)) + printf("\n%x ", i / 20); + putchar(number2char(p[i])); + } + putchar('\n'); + + p = pagedata + 18*40; + for (i = 0; i < 80; i += 10) + { + short pop = ((p[i] << 8) | (p[i+1] << 4) | p[i+2]) & 0x7ff; + unsigned char type = p[i+5] & 0x03; /* 1st default object */ + unsigned char triplet = 3 * ((p[i+7] >> 1) & 0x03) + type; + unsigned char packet = (p[i+7] & 0x08) >> 3; + unsigned char subp = p[i+6]; + if (pop < 0x100) + pop += 0x800; + printf("POP #%x %03x/%x p27prio:%x", i/10, pop, p[i+3], p[i] & 0x08); + printf(" DefObj S%xP%xT%x%c %c#%03d", tuxtxt_cache.subpage, packet, triplet, "LH"[p[i+7] & 0x01], + "-CDP"[type], 8*packet + 2*(triplet-1)/3 + 1); + type = (p[i+5] >> 2) & 0x03; /* 2nd default object */ + triplet = 3 * ((p[i+9] >> 1) & 0x03) + type; + packet = (p[i+9] & 0x08) >> 3; + subp = p[i+8]; + printf(", S%xP%xT%x%c %c#%03d", tuxtxt_cache.subpage, packet, triplet, "LH"[p[i+9] & 0x01], + "-CDP"[type], 8*packet + 2*(triplet-1)/3 + 1); + if ((p[i+4] & 0x01) == 0) + printf(" Sidep.:%c%c BgSubst.:%x", (p[i+4] & 0x02) ? 'L' : '-', (p[i+4] & 0x04) ? 'R' : '-', p[i+4] >> 3); + putchar('\n'); + if ((pop & 0xff) != 0xff && tuxtxt_cache.astCachetable[pop][0]) /* link valid && linked page cached */ + { + tstPageinfo *pageinfo_link = &(tuxtxt_cache.astCachetable[pop][0]->pageinfo); + if (!i) + pageinfo_link->function = FUNC_GPOP; + else + pageinfo_link->function = FUNC_POP; + } + } + + p = pagedata + 20*40; + for (i = 0; i < 4*8; i += 4) + { + short drcs = ((p[i] << 8) | (p[i+1] << 4) | p[i+2]) & 0x7ff; + if (drcs < 0x100) + drcs += 0x800; + printf("DRCS #%x %03x/%x p27prio:%x\n", i/4, drcs, p[i+3], p[i] & 0x08); + if ((drcs & 0xff) != 0xff && tuxtxt_cache.astCachetable[drcs][0]) /* link valid && linked page cached */ + { + tstPageinfo *pageinfo_link = &(tuxtxt_cache.astCachetable[drcs][0]->pageinfo); + if (!i) + pageinfo_link->function = FUNC_GDRCS; + else + pageinfo_link->function = FUNC_DRCS; + } + } + } /* function == FUNC_MOT */ + +// else if (pageinfo->function == FUNC_GPOP || pageinfo->function == FUNC_POP) /* object definitions */ + else if (!tuxtxt_is_dec(tuxtxt_cache.page) && pageinfo->function != FUNC_GDRCS && pageinfo->function != FUNC_DRCS) /* in case the function is not assigned properly */ + { + unsigned char APx0, APy0, APx, APy; + int packet; + + pop = gpop = tuxtxt_cache.page; + + for (packet = 1; packet <= 4; packet++) + { + unsigned char *ptriplet = pagedata + 40*(packet-1); + int idata = dehamming[*ptriplet]; + int triplet; + + if (idata == 0xff || 0 == (idata & 1)) /* hamming error or no pointer data: ignore packet */ + continue; + for (triplet = 1; triplet <= 12; triplet++) + { + APx0 = APy0 = APx = APy = tAPx = tAPy = 0; + eval_NumberedObject(tuxtxt_cache.page, tuxtxt_cache.subpage, packet, triplet, 0, &APx, &APy, &APx0, &APy0); + APx0 = APy0 = APx = APy = tAPx = tAPy = 0; + eval_NumberedObject(tuxtxt_cache.page, tuxtxt_cache.subpage, packet, triplet, 1, &APx, &APy, &APx0, &APy0); + } /* for triplet */ + } /* for packet */ + } /* function == FUNC_*POP */ +#endif /* TUXTXT_DEBUG */ + + /* normal page */ + if (tuxtxt_is_dec(tuxtxt_cache.page)) + { + unsigned char APx0, APy0, APx, APy; + tstPageinfo *pi = &(tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo); + tstCachedPage *pmot = tuxtxt_cache.astCachetable[(tuxtxt_cache.page & 0xf00) | 0xfe][0]; + unsigned short *colortable = 0; + int p26Received = 0; + int BlackBgSubst = 0; + int ColorTableRemapping = 0; + + + pop = gpop = drcs = gdrcs = 0; + + + if (pi->ext) + { + tstExtData *e = pi->ext; + + if (e->p26[0]) + p26Received = 1; + + if (e->p27) + { + tstp27 *p27 = e->p27; + if (p27[0].l25) + gpop = p27[0].page; + if (p27[1].l25) + pop = p27[1].page; + if (p27[2].l25) + gdrcs = p27[2].page; + if (p27[3].l25) + drcs = p27[3].page; + } + + if (e->p28Received) + { + colortable = e->bgr; + BlackBgSubst = e->BlackBgSubst; + ColorTableRemapping = e->ColorTableRemapping; + memset(FullRowColor, e->DefRowColor, sizeof(FullRowColor)); + FullScrColor = e->DefScreenColor; + national_subset = setnational(e->DefaultCharset); + national_subset_secondary = setnational(e->SecondCharset); +#if TUXTXT_DEBUG + if (dumpl25) + { + int c; /* color */ + printf("p28/0: DefCharset %02x Sec %02x SidePanel %c%c%x DefScrCol %02x DefRowCol %02x BlBgSubst %x Map %x\n CBGR", + e->DefaultCharset, + e->SecondCharset, + e->LSP ? (e->SPL25 ? 'L' : 'l') : '-', /* left panel (small: only in level 3.5) */ + e->RSP ? (e->SPL25 ? 'R' : 'r') : '-', /* right panel (small: only in level 3.5) */ + e->LSPColumns, + e->DefScreenColor, + e->DefRowColor, + e->BlackBgSubst, + e->ColorTableRemapping); + for (c = 0; c < 16; c++) + printf(" %x%03x", c, e->bgr[c]); + putchar('\n'); + } +#endif + } /* e->p28Received */ + } + + if (!colortable && tuxtxt_cache.astP29[tuxtxt_cache.page >> 8]) + { + tstExtData *e = tuxtxt_cache.astP29[tuxtxt_cache.page >> 8]; + colortable = e->bgr; + BlackBgSubst = e->BlackBgSubst; + ColorTableRemapping = e->ColorTableRemapping; + memset(FullRowColor, e->DefRowColor, sizeof(FullRowColor)); + FullScrColor = e->DefScreenColor; + national_subset = setnational(e->DefaultCharset); + national_subset_secondary = setnational(e->SecondCharset); +#if TUXTXT_DEBUG + if (dumpl25) + { + int c; /* color */ + printf("p29/0: DefCharset %02x Sec %02x SidePanel %c%c%x DefScrCol %02x DefRowCol %02x BlBgSubst %x Map %x\n CBGR", + e->DefaultCharset, + e->SecondCharset, + e->LSP ? (e->SPL25 ? 'L' : 'l') : '-', /* left panel (small: only in level 3.5) */ + e->RSP ? (e->SPL25 ? 'R' : 'r') : '-', /* right panel (small: only in level 3.5) */ + e->LSPColumns, + e->DefScreenColor, + e->DefRowColor, + e->BlackBgSubst, + e->ColorTableRemapping); + for (c = 0; c < 16; c++) + printf(" %x%03x", c, e->bgr[c]); + putchar('\n'); + } +#endif + + } + + if (ColorTableRemapping) + { + int i; + for (i = 0; i < 25*40; i++) + { + page_atrb[i].fg += MapTblFG[ColorTableRemapping - 1]; + if (!BlackBgSubst || page_atrb[i].bg != black || page_atrb[i].IgnoreAtBlackBgSubst) + page_atrb[i].bg += MapTblBG[ColorTableRemapping - 1]; + } + } + + /* determine ?pop/?drcs from MOT */ + if (pmot) + { + unsigned char pmot_data[23*40]; + tuxtxt_decompress_page((tuxtxt_cache.page & 0xf00) | 0xfe,0,pmot_data); + + unsigned char *p = pmot_data; /* start of link data */ + int o = 2 * (((tuxtxt_cache.page & 0xf0) >> 4) * 10 + (tuxtxt_cache.page & 0x0f)); /* offset of links for current page */ + int opop = p[o] & 0x07; /* index of POP link */ + int odrcs = p[o+1] & 0x07; /* index of DRCS link */ + unsigned char obj[3*4*4]; // types* objects * (triplet,packet,subp,high) + unsigned char type,ct, tstart = 4*4; + memset(obj,0,sizeof(obj)); + + + if (p[o] & 0x08) /* GPOP data used */ + { + if (!gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff; + if ((gpop & 0xff) == 0xff) + gpop = 0; + else + { + if (gpop < 0x100) + gpop += 0x800; + if (!p26Received) + { + ct=2; + while (ct) + { + ct--; + type = (p[18*40+5] >> 2*ct) & 0x03; + + if (type == 0) continue; + obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet + obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet + obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp + obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high + +#if TUXTXT_DEBUG + if (dumpl25) + printf("GPOP DefObj%d S%xP%xT%x%c %c#%03d\n" + ,2-ct + , obj[(type-1)*(tstart)+ct*4+2] + , obj[(type-1)*(tstart)+ct*4+1] + , obj[(type-1)*(tstart)+ct*4] + , "LH"[obj[(type-1)*(tstart)+ct*4+3]] + , "-CDP"[type] + , 8*(obj[(type-1)*(tstart)+ct*4+1]-1) + 2*(obj[(type-1)*(tstart)+ct*4]-1)/3 + 1); +#endif + } + } + } + } + } + if (opop) /* POP data used */ + { + opop = 18*40 + 10*opop; /* offset to POP link */ + if (!pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff; + if ((pop & 0xff) == 0xff) + pop = 0; + else + { + if (pop < 0x100) + pop += 0x800; + if (!p26Received) + { + ct=2; + while (ct) + { + ct--; + type = (p[opop+5] >> 2*ct) & 0x03; + + if (type == 0) continue; + obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet + obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet + obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp + obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high +#if TUXTXT_DEBUG + if (dumpl25) + printf("POP DefObj%d S%xP%xT%x%c %c#%03d\n" + , 2-ct + , obj[(type-1)*(tstart)+(ct+2)*4+2] + , obj[(type-1)*(tstart)+(ct+2)*4+1] + , obj[(type-1)*(tstart)+(ct+2)*4] + , "LH"[obj[(type-1)*(tstart)+(ct+2)*4+3]] + , "-CDP"[type], 8*(obj[(type-1)*(tstart)+(ct+2)*4+1]-1) + 2*(obj[(type-1)*(tstart)+(ct+2)*4]-1)/3 + 1); +#endif + } + } + } + } + } + // eval default objects in correct order + for (ct = 0; ct < 12; ct++) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf("eval DefObjs : %d S%xP%xT%x%c %c#%03d\n" + , ct + , obj[ct*4+2] + , obj[ct*4+1] + , obj[ct*4] + , "LH"[obj[ct*4+3]] + , "-CDP"[obj[ct*4 % 3]] + , 8*(obj[ct*4+1]-1) + 2*(obj[ct*4]-1)/3 + 1); +#endif + if (obj[ct*4] != 0) + { + APx0 = APy0 = APx = APy = tAPx = tAPy = 0; + eval_NumberedObject(ct % 4 > 1 ? pop : gpop, obj[ct*4+2], obj[ct*4+1], obj[ct*4], obj[ct*4+3], &APx, &APy, &APx0, &APy0); + } + } + + if (p[o+1] & 0x08) /* GDRCS data used */ + { + if (!gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff; + if ((gdrcs & 0xff) == 0xff) + gdrcs = 0; + else if (gdrcs < 0x100) + gdrcs += 0x800; + } + } + if (odrcs) /* DRCS data used */ + { + odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */ + if (!drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff; + if ((drcs & 0xff) == 0xff) + drcs = 0; + else if (drcs < 0x100) + drcs += 0x800; + } + } + if (tuxtxt_cache.astCachetable[gpop][0]) + tuxtxt_cache.astCachetable[gpop][0]->pageinfo.function = FUNC_GPOP; + if (tuxtxt_cache.astCachetable[pop][0]) + tuxtxt_cache.astCachetable[pop][0]->pageinfo.function = FUNC_POP; + if (tuxtxt_cache.astCachetable[gdrcs][0]) + tuxtxt_cache.astCachetable[gdrcs][0]->pageinfo.function = FUNC_GDRCS; + if (tuxtxt_cache.astCachetable[drcs][0]) + tuxtxt_cache.astCachetable[drcs][0]->pageinfo.function = FUNC_DRCS; + } /* if mot */ + +#if TUXTXT_DEBUG + if (dumpl25) + printf("gpop %03x pop %03x gdrcs %03x drcs %03x p28/0: Func %x Natvalid %x Nat %x Box %x\n", + gpop, pop, gdrcs, drcs, + pi->function, pi->nationalvalid, pi->national, pi->boxed); +#endif + + /* evaluate local extension data from p26 */ + if (p26Received) + { +#if TUXTXT_DEBUG + if (dumpl25) + printf("p26/x:\n"); +#endif + APx0 = APy0 = APx = APy = tAPx = tAPy = 0; + eval_object(13 * (23-2 + 2), tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage], &APx, &APy, &APx0, &APy0, OBJ_ACTIVE, &page_char[40]); /* 1st triplet p26/0 */ + } + + { + int r, c; + int o = 0; + + + for (r = 0; r < 25; r++) + for (c = 0; c < 39; c++) + { + if (BlackBgSubst && page_atrb[o].bg == black && !(page_atrb[o].IgnoreAtBlackBgSubst)) + { + if (FullRowColor[r] == 0x08) + page_atrb[o].bg = FullScrColor; + else + page_atrb[o].bg = FullRowColor[r]; + } + o++; + } + } + + if (!hintmode) + { + int i; + for (i = 0; i < 25*40; i++) + { + if (page_atrb[i].concealed) page_atrb[i].fg = page_atrb[i].bg; + } + } + + if (boxed || transpmode) +// FullScrColor = transp; + FillBorder(transp); + else + FillBorder(FullScrColor); + if (colortable) /* as late as possible to shorten the time the old page is displayed with the new colors */ + setcolors(colortable, 16, 16); /* set colors for CLUTs 2+3 */ + } /* is_dec(page) */ + +} + +/****************************************************************************** + * main loop * + ******************************************************************************/ + +int tuxtx_main(int _rc, void * _fb, int pid, int x, int y, int w, int h) +{ + char cvs_revision[] = "$Revision: 1.95 $"; + +//printf("to init tuxtxt\n");fflush(stdout); +#if !TUXTXT_CFG_STANDALONE + int initialized = tuxtxt_init(); + if ( initialized ) + tuxtxt_cache.page = 0x100; +#endif + + /* show versioninfo */ + sscanf(cvs_revision, "%*s %s", versioninfo); + printf("TuxTxt %s\n", versioninfo); + printf("for 32bpp framebuffer\n"); + + if ((fb=open("/dev/fb/0", O_RDWR)) == -1) + { + perror("TuxTxt "); + return 0; + } + + rc = _rc; + lfb = (unsigned char *) _fb; + + tuxtxt_cache.vtxtpid = pid; + + if(tuxtxt_cache.vtxtpid == 0) + printf("[tuxtxt] No PID given, so scanning for PIDs ...\n\n"); + else + printf("[tuxtxt] using PID %x\n", tuxtxt_cache.vtxtpid); + + fcntl(rc, F_SETFL, fcntl(rc, F_GETFL) | O_EXCL | O_NONBLOCK); + + /* get fixed screeninfo */ + if (ioctl(fb, FBIOGET_FSCREENINFO, &fix_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } + + /* get variable screeninfo */ + if (ioctl(fb, FBIOGET_VSCREENINFO, &var_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } + + /* set variable screeninfo for double buffering */ + var_screeninfo.yoffset = 0; +#if 0 + sx = 80; + sy = 40; + ex = var_screeninfo.xres - sx; + ey = var_screeninfo.yres - sy; +#endif + sx = x + 10; + sy = y + 10; + ex = x + w - 10; + ey = y + h - 10; + /* initialisations */ + transpmode = 0; + + if (Init() == 0) + return 0; + + //transpmode = 1; + /* main loop */ + do { + if (GetRCCode() == 1) + { + if (transpmode == 2) /* TV mode */ + { + switch (RCCode) + { +//#if TUXTXT_DEBUG /* FIXME */ + case RC_OK: + if (showhex) + { + dump_page(); /* hexdump of page contents to stdout for debugging */ + } + continue; /* otherwise ignore key */ +//#endif /* TUXTXT_DEBUG */ + case RC_UP: + case RC_DOWN: + case RC_0: + case RC_1: + case RC_2: + case RC_3: + case RC_4: + case RC_5: + case RC_6: + case RC_7: + case RC_8: + case RC_9: + case RC_GREEN: + case RC_YELLOW: + case RC_BLUE: + case RC_PLUS: + case RC_MINUS: + case RC_DBOX: + case RC_STANDBY: + transpmode = 1; /* switch to normal mode */ + SwitchTranspMode(); + break; /* and evaluate key */ + + case RC_MUTE: /* regular toggle to transparent */ + case RC_TEXT: + break; + + case RC_HELP: /* switch to scart input and back */ + { + continue; /* otherwise ignore exit key */ + } + default: + continue; /* ignore all other keys */ + } + } + + switch (RCCode) + { + case RC_UP: + GetNextPageOne(!swapupdown); + break; + case RC_DOWN: + GetNextPageOne(swapupdown); + break; + case RC_RIGHT: + if (boxed) + { + subtitledelay++; + // display subtitledelay + PosY = StartY; + char ns[10]; + SetPosX(1); + sprintf(ns,"+%d ",subtitledelay); + RenderCharFB(ns[0],&atrtable[ATR_WB]); + RenderCharFB(ns[1],&atrtable[ATR_WB]); + RenderCharFB(ns[2],&atrtable[ATR_WB]); + RenderCharFB(ns[4],&atrtable[ATR_WB]); + } + else + GetNextSubPage(1); + break; + case RC_LEFT: + if (boxed) + { + subtitledelay--; + if (subtitledelay < 0) subtitledelay = 0; + // display subtitledelay + PosY = StartY; + char ns[10]; + SetPosX(1); + sprintf(ns,"+%d ",subtitledelay); + RenderCharFB(ns[0],&atrtable[ATR_WB]); + RenderCharFB(ns[1],&atrtable[ATR_WB]); + RenderCharFB(ns[2],&atrtable[ATR_WB]); + RenderCharFB(ns[4],&atrtable[ATR_WB]); + } + else + GetNextSubPage(-1); + break; + case RC_OK: + if (tuxtxt_cache.subpagetable[tuxtxt_cache.page] == 0xFF) + continue; + PageCatching(); + break; + + case RC_0: + case RC_1: + case RC_2: + case RC_3: + case RC_4: + case RC_5: + case RC_6: + case RC_7: + case RC_8: + case RC_9: + PageInput(RCCode - RC_0); + break; + case RC_RED: ColorKey(prev_100); break; + case RC_GREEN: ColorKey(prev_10); break; + case RC_YELLOW: ColorKey(next_10); break; + case RC_BLUE: ColorKey(next_100); break; + case RC_PLUS: SwitchZoomMode(); break; + case RC_MINUS: SwitchScreenMode(-1);prevscreenmode = screenmode; break; + case RC_MUTE: SwitchTranspMode(); break; + case RC_TEXT: + if(transpmode == 1) + RCCode = RC_HOME; + SwitchTranspMode(); + break; + case RC_HELP: SwitchHintMode(); break; + case RC_DBOX: ConfigMenu(0); break; + case RC_HOME: + break; + } + } + + /* update page or timestring and lcd */ + RenderPage(); + } while ((RCCode != RC_HOME) && (RCCode != RC_STANDBY)); + + /* exit */ + CleanUp(); + +#if 0 + close(rc); + close(lcd); +#endif + close(fb); + +#if 1 + if ( initialized ) + tuxtxt_close(); +#endif + + printf("Tuxtxt: plugin ended\n"); + return 1; +} + +/****************************************************************************** + * MyFaceRequester + ******************************************************************************/ + +FT_Error MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface) +{ + FT_Error result; + + result = FT_New_Face(library, (char *) face_id, 0, aface); + +#if TUXTXT_DEBUG + if (!result) + printf("TuxTxt \n", (char*)face_id); + else + printf("TuxTxt \n", (char*)face_id); +#endif + + return result; +} + +/****************************************************************************** + * Init * + ******************************************************************************/ + +int Init() +{ + int error, i; + unsigned char magazine; + + /* init data */ + + + //page_atrb[32] = transp<<4 | transp; + inputcounter = 2; + + for (magazine = 1; magazine < 9; magazine++) + { + tuxtxt_cache.current_page [magazine] = -1; + tuxtxt_cache.current_subpage [magazine] = -1; + } +#if TUXTXT_CFG_STANDALONE +/* init data */ + memset(&tuxtxt_cache.astCachetable, 0, sizeof(tuxtxt_cache.astCachetable)); + memset(&tuxtxt_cache.subpagetable, 0xFF, sizeof(tuxtxt_cache.subpagetable)); + memset(&tuxtxt_cache.astP29, 0, sizeof(tuxtxt_cache.astP29)); + + memset(&tuxtxt_cache.basictop, 0, sizeof(tuxtxt_cache.basictop)); + memset(&tuxtxt_cache.adip, 0, sizeof(tuxtxt_cache.adip)); + memset(&tuxtxt_cache.flofpages, 0 , sizeof(tuxtxt_cache.flofpages)); + tuxtxt_cache.maxadippg = -1; + tuxtxt_cache.bttok = 0; + maxhotlist = -1; + + //page_atrb[32] = transp<<4 | transp; + inputcounter = 2; + tuxtxt_cache.cached_pages = 0; + + tuxtxt_cache.page_receiving = -1; + tuxtxt_cache.page = 0x100; +#endif + lastpage = tuxtxt_cache.page; + prev_100 = 0x100; + prev_10 = 0x100; + next_100 = 0x100; + next_10 = 0x100; + tuxtxt_cache.subpage = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + if (tuxtxt_cache.subpage == 0xff) + tuxtxt_cache.subpage = 0; + + tuxtxt_cache.pageupdate = 0; + + tuxtxt_cache.zap_subpage_manual = 0; + + subtitledelay = 0; + delaystarted = 0; + + /* init lcd */ + UpdateLCD(); + + /* config defaults */ + screenmode = 0; + screen_mode1 = 0; + screen_mode2 = 0; + color_mode = 10; + trans_mode = 10; + menulanguage = 0; /* german */ + national_subset = 0;/* default */ + auto_national = 1; + swapupdown = 0; + showhex = 0; + showflof = 1; + show39 = 1; + showl25 = 1; + dumpl25 = 0; + usettf = 0; + TTFWidthFactor16 = 28; + TTFHeightFactor16 = 16; + TTFShiftX = 0; + TTFShiftY = 0; + + /* load config */ + if ((conf = fopen(TUXTXTCONF, "rt")) == 0) + { + perror("TuxTxt "); + } + else + { + while(1) + { + char line[100]; + int ival; + + if (!fgets(line, sizeof(line), conf)) + break; + + if (1 == sscanf(line, "ScreenMode16x9Normal %i", &ival)) + screen_mode1 = ival & 1; + else if (1 == sscanf(line, "ScreenMode16x9Divided %i", &ival)) + screen_mode2 = ival & 1; + else if (1 == sscanf(line, "Brightness %i", &ival)) + color_mode = ival; + else if (1 == sscanf(line, "AutoNational %i", &ival)) + auto_national = ival & 1; + else if (1 == sscanf(line, "NationalSubset %i", &ival)) + { + if (ival >= 0 && ival <= (int) MAX_NATIONAL_SUBSET) + national_subset = ival; + } + else if (1 == sscanf(line, "MenuLanguage %i", &ival)) + { + if (ival >= 0 && ival <= MAXMENULANGUAGE) + menulanguage = ival; + } + else if (1 == sscanf(line, "SwapUpDown %i", &ival)) + swapupdown = ival & 1; + else if (1 == sscanf(line, "ShowHexPages %i", &ival)) + showhex = ival & 1; + else if (1 == sscanf(line, "Transparency %i", &ival)) + trans_mode = ival; + else if (1 == sscanf(line, "TTFWidthFactor16 %i", &ival)) + TTFWidthFactor16 = ival; + else if (1 == sscanf(line, "TTFHeightFactor16 %i", &ival)) + TTFHeightFactor16 = ival; + else if (1 == sscanf(line, "TTFShiftX %i", &ival)) + TTFShiftX = ival; + else if (1 == sscanf(line, "TTFShiftY %i", &ival)) + TTFShiftY = ival; + else if (1 == sscanf(line, "Screenmode %i", &ival)) + screenmode = ival; + else if (1 == sscanf(line, "ShowFLOF %i", &ival)) + showflof = ival & 1; + else if (1 == sscanf(line, "Show39 %i", &ival)) + show39 = ival & 1; + else if (1 == sscanf(line, "ShowLevel2p5 %i", &ival)) + showl25 = ival & 1; + else if (1 == sscanf(line, "DumpLevel2p5 %i", &ival)) + dumpl25 = ival & 1; + else if (1 == sscanf(line, "UseTTF %i", &ival)) + usettf = ival & 1; +#if 0 + else if (1 == sscanf(line, "StartX %i", &ival)) + sx = ival; + else if (1 == sscanf(line, "EndX %i", &ival)) + ex = ival; + else if (1 == sscanf(line, "StartY %i", &ival)) + sy = ival; + else if (1 == sscanf(line, "EndY %i", &ival)) + ey = ival; +#endif + } + fclose(conf); + } + saveconfig = 0; + savedscreenmode = screenmode; + national_subset_secondary = NAT_DEFAULT; + + + /* init fontlibrary */ + if ((error = FT_Init_FreeType(&library))) + { + printf("TuxTxt ", error); + return 0; + } + + if ((error = FTC_Manager_New(library, 7, 2, 0, &MyFaceRequester, NULL, &manager))) + { + FT_Done_FreeType(library); + printf("TuxTxt \n", error); + return 0; + } + + if ((error = FTC_SBitCache_New(manager, &cache))) + { + FTC_Manager_Done(manager); + FT_Done_FreeType(library); + printf("TuxTxt \n", error); + return 0; + } + + fontwidth = 0; /* initialize at first setting */ + + + /* calculate font dimensions */ + displaywidth = (ex-sx); + fontheight = (ey-sy) / 25; //21; + fontwidth_normal = (ex-sx) / 40; + setfontwidth(fontwidth_normal); + fontwidth_topmenumain = (TV43STARTX-sx) / 40; + fontwidth_topmenusmall = (ex- TOPMENUSTARTX) / TOPMENUCHARS; + fontwidth_small = (TV169FULLSTARTX-sx) / 40; + ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */ + ymosaic[1] = (fontheight + 1) / 3; + ymosaic[2] = (fontheight * 2 + 1) / 3; + ymosaic[3] = fontheight; + { + int i; + for (i = 0; i <= 10; i++) + aydrcs[i] = (fontheight * i + 5) / 10; + } + + //FIXME center + /* center screen */ + StartX = sx; //+ (((ex-sx) - 40*fontwidth) / 2); + StartY = sy + (((ey-sy) - 25*fontheight) / 2); + + if (usettf) + { + typettf.face_id = (FTC_FaceID) TUXTXTTTFVAR; + typettf.height = (FT_UShort) fontheight * TTFHeightFactor16 / 16; + } + else + { + typettf.face_id = (FTC_FaceID) TUXTXTOTBVAR; + typettf.width = (FT_UShort) 23; + typettf.height = (FT_UShort) 23; + } + + typettf.flags = FT_LOAD_MONOCHROME; + + if ((error = FTC_Manager_LookupFace(manager, typettf.face_id, &face))) + { + typettf.face_id = (void *) (usettf ? (FTC_FaceID) TUXTXTTTF : TUXTXTOTB); + if ((error = FTC_Manager_LookupFace(manager, typettf.face_id, &face))) + { + printf("TuxTxt \n", error); + FTC_Manager_Done(manager); + FT_Done_FreeType(library); + return 0; + } + } + ascender = (usettf ? fontheight * face->ascender / face->units_per_EM : 16); +#if TUXTXT_DEBUG + printf("TuxTxt \n", + fontheight, fontwidth, fontwidth_small, fontwidth_topmenumain, fontwidth_topmenusmall, + ymosaic[0], ymosaic[1], ymosaic[2], StartX, StartY, ascender); +#endif + +#if 0 + /* get fixed screeninfo */ + if (ioctl(fb, FBIOGET_FSCREENINFO, &fix_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } + + /* get variable screeninfo */ + if (ioctl(fb, FBIOGET_VSCREENINFO, &var_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } + + /* set variable screeninfo for double buffering */ + var_screeninfo.yoffset = 0; +#endif +#if 0 + var_screeninfo.yres_virtual = 2*var_screeninfo.yres; + var_screeninfo.xres_virtual = var_screeninfo.xres; + + if (ioctl(fb, FBIOPUT_VSCREENINFO, &var_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } + + if (ioctl(fb, FBIOGET_VSCREENINFO, &var_screeninfo) == -1) + { + perror("TuxTxt "); + return 0; + } +#endif +#if TUXTXT_DEBUG + printf("TuxTxt \n", + var_screeninfo.xres, var_screeninfo.yres, + var_screeninfo.xres_virtual, var_screeninfo.yres_virtual, + var_screeninfo.yoffset); +#endif + + + /* set new colormap */ + setcolors((unsigned short *)defaultcolors, 0, SIZECOLTABLE); +#if 0 + /* map framebuffer into memory */ + lfb = (unsigned char*)mmap(0, fix_screeninfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0); + + if (!lfb) + { + perror("TuxTxt "); + return 0; + } +#endif + ClearBB(transp); /* initialize backbuffer */ + for (i = 0; i < 40 * 25; i++) + { + page_char[i] = ' '; + page_atrb[i].fg = transp; + page_atrb[i].bg = transp; + page_atrb[i].charset = C_G0P; + page_atrb[i].doubleh = 0; + page_atrb[i].doublew = 0; + page_atrb[i].IgnoreAtBlackBgSubst = 0; + } + /* if no vtxtpid for current service, search PIDs */ + if (tuxtxt_cache.vtxtpid == 0) + { + /* get all vtxt-pids */ + getpidsdone = -1; /* don't kill thread */ + if (GetTeletextPIDs() == 0) + { + FTC_Manager_Done(manager); + FT_Done_FreeType(library); + //munmap(lfb, fix_screeninfo.smem_len); + return 0; + } + + if (auto_national) + national_subset = pid_table[0].national_subset; + if (pids_found > 1) + ConfigMenu(1); + else + { + tuxtxt_cache.vtxtpid = pid_table[0].vtxt_pid; + current_service = 0; + RenderMessage(ShowServiceName); + } + } + else + { + SDT_ready = 0; + getpidsdone = 0; +// tuxtxt_cache.pageupdate = 1; /* force display of message page not found (but not twice) */ + + } +#if TUXTXT_CFG_STANDALONE + tuxtxt_init_demuxer(); + tuxtxt_start_thread(); +#else + tuxtxt_start(tuxtxt_cache.vtxtpid); +#endif + fcntl(rc, F_SETFL, O_NONBLOCK); + gethotlist(); + SwitchScreenMode(screenmode); + prevscreenmode = screenmode; + + printf("TuxTxt: init ok\n"); + + /* init successfull */ + return 1; +} + +/****************************************************************************** + * Cleanup * + ******************************************************************************/ + +void CleanUp() +{ + int curscreenmode = screenmode; + + /* hide and close pig */ + if (screenmode) + SwitchScreenMode(0); /* turn off divided screen */ + //close(pig); + +#if TUXTXT_CFG_STANDALONE + tuxtxt_stop_thread(); + tuxtxt_clear_cache(); + if (tuxtxt_cache.dmx != -1) + close(tuxtxt_cache.dmx); + tuxtxt_cache.dmx = -1; +#else + //tuxtxt_stop(); +#endif + + memset(lfb,0, var_screeninfo.yres*fix_screeninfo.line_length); + + /* close freetype */ + FTC_Manager_Done(manager); + FT_Done_FreeType(library); + + if (hotlistchanged) + savehotlist(); + + /* save config */ + if (saveconfig || curscreenmode != savedscreenmode) + { + if ((conf = fopen(TUXTXTCONF, "wt")) == 0) + { + perror("TuxTxt "); + } + else + { + printf("TuxTxt \n"); + fprintf(conf, "ScreenMode16x9Normal %d\n", screen_mode1); + fprintf(conf, "ScreenMode16x9Divided %d\n", screen_mode2); + fprintf(conf, "Brightness %d\n", color_mode); + fprintf(conf, "MenuLanguage %d\n", menulanguage); + fprintf(conf, "AutoNational %d\n", auto_national); + fprintf(conf, "NationalSubset %d\n", national_subset); + fprintf(conf, "SwapUpDown %d\n", swapupdown); + fprintf(conf, "ShowHexPages %d\n", showhex); + fprintf(conf, "Transparency 0x%X\n", trans_mode); + fprintf(conf, "TTFWidthFactor16 %d\n", TTFWidthFactor16); + fprintf(conf, "TTFHeightFactor16 %d\n", TTFHeightFactor16); + fprintf(conf, "TTFShiftX %d\n", TTFShiftX); + fprintf(conf, "TTFShiftY %d\n", TTFShiftY); + fprintf(conf, "Screenmode %d\n", curscreenmode); + fprintf(conf, "ShowFLOF %d\n", showflof); + fprintf(conf, "Show39 %d\n", show39); + fprintf(conf, "ShowLevel2p5 %d\n", showl25); + fprintf(conf, "DumpLevel2p5 %d\n", dumpl25); + fprintf(conf, "UseTTF %d\n", usettf); +#if 0 + fprintf(conf, "StartX %d\n", sx); + fprintf(conf, "EndX %d\n", ex); + fprintf(conf, "StartY %d\n", sy); + fprintf(conf, "EndY %d\n", ey); +#endif + fclose(conf); + } + } +} +/****************************************************************************** + * GetTeletextPIDs * + ******************************************************************************/ +int GetTeletextPIDs() +{ + int pat_scan, pmt_scan, sdt_scan, desc_scan, pid_test, byte, diff, first_sdt_sec; + + unsigned char bufPAT[1024]; + unsigned char bufSDT[1024]; + unsigned char bufPMT[1024]; + + /* show infobar */ + RenderMessage(ShowInfoBar); + + unsigned char filter[DMX_FILTER_SIZE]; + unsigned char mask[DMX_FILTER_SIZE]; + int res; + + cDemux * dmx = new cDemux(1); + dmx->Open(DMX_PSI_CHANNEL); + + memset(filter, 0x00, DMX_FILTER_SIZE); + memset(mask, 0x00, DMX_FILTER_SIZE); + + //filter[0] = 0x00; + //mask[0] = 0xFF; + mask[0] = 0xFF; + mask[4] = 0xFF; + + dmx->sectionFilter(0, filter, mask, 1); + res = dmx->Read(bufPAT, sizeof(bufPAT)); + dmx->Stop(); + if(res <= 0) { + printf("TuxTxt failed"); + delete dmx; + return 0; + } + + /* scan each PMT for vtxt-pid */ + pids_found = 0; + + for (pat_scan = 0x0A; pat_scan < 0x0A + (((bufPAT[0x01]<<8 | bufPAT[0x02]) & 0x0FFF) - 9); pat_scan += 4) + { +#if TUXTXT_DEBUG + printf("PAT liefert:%04x, %04x \n",((bufPAT[pat_scan - 2]<<8) | (bufPAT[pat_scan - 1])),(bufPAT[pat_scan]<<8 | bufPAT[pat_scan+1]) & 0x1FFF); +#endif + if (((bufPAT[pat_scan - 2]<<8) | (bufPAT[pat_scan - 1])) == 0) + continue; + + int pid = (bufPAT[pat_scan]<<8 | bufPAT[pat_scan+1]) & 0x1FFF; + filter[0] = 0x02; + mask[0] = 0xFF; + + dmx->sectionFilter(pid, filter, mask, 1); + res = dmx->Read(bufPMT, sizeof(bufPMT)); + dmx->Stop(); + if(res <= 0) { + perror("TuxTxt "); + continue; + } + + for (pmt_scan = 0x0C + ((bufPMT[0x0A]<<8 | bufPMT[0x0B]) & 0x0FFF); + pmt_scan < (((bufPMT[0x01]<<8 | bufPMT[0x02]) & 0x0FFF) - 7); + pmt_scan += 5 + bufPMT[pmt_scan + 4]) + { + if (bufPMT[pmt_scan] == 6) + { + for (desc_scan = pmt_scan + 5; + desc_scan < pmt_scan + ((bufPMT[pmt_scan + 3]<<8 | bufPMT[pmt_scan + 4]) & 0x0FFF) + 5; + desc_scan += 2 + bufPMT[desc_scan + 1]) + { + if (bufPMT[desc_scan] == 0x56) + { + char country_code[4]; + + for (pid_test = 0; pid_test < pids_found; pid_test++) + if (pid_table[pid_test].vtxt_pid == ((bufPMT[pmt_scan + 1]<<8 | bufPMT[pmt_scan + 2]) & 0x1FFF)) + goto skip_pid; + + pid_table[pids_found].vtxt_pid = (bufPMT[pmt_scan + 1]<<8 | bufPMT[pmt_scan + 2]) & 0x1FFF; + pid_table[pids_found].service_id = bufPMT[0x03]<<8 | bufPMT[0x04]; + if (bufPMT[desc_scan + 1] == 5) + { + country_code[0] = bufPMT[desc_scan + 2] | 0x20; + country_code[1] = bufPMT[desc_scan + 3] | 0x20; + country_code[2] = bufPMT[desc_scan + 4] | 0x20; + country_code[3] = 0; + pid_table[pids_found].national_subset = GetNationalSubset(country_code); + } + else + { + country_code[0] = 0; + pid_table[pids_found].national_subset = NAT_DEFAULT; /* use default charset */ + } + +#if TUXTXT_DEBUG + printf("TuxTxt \n", + pid_table[pids_found].service_id, + country_code, + pid_table[pids_found].national_subset, + (pid_table[pids_found].vtxt_pid == tuxtxt_cache.vtxtpid) ? " * " : "" + ); +#endif + + pids_found++; +skip_pid: + ; + } + } + } + } + } + + /* check for teletext */ + if (pids_found == 0) + { + printf("TuxTxt \n"); + + RenderMessage(NoServicesFound); + sleep(3); + delete dmx; + return 0; + } + + /* read SDT to get servicenames */ + SDT_ready = 0; + + filter[0] = 0x42; + mask[0] = 0xFF; + + dmx->sectionFilter(0x0011, filter, mask, 1); + + first_sdt_sec = -1; + while (1) + { + res = dmx->Read(bufSDT, sizeof(bufSDT)); + if(res <= 0) { + perror("TuxTxt "); + delete dmx; + RenderMessage(ShowServiceName); + return 1; + } + + if (first_sdt_sec == bufSDT[6]) + break; + + if (first_sdt_sec == -1) + first_sdt_sec = bufSDT[6]; + + /* scan SDT to get servicenames */ + for (sdt_scan = 0x0B; sdt_scan < ((bufSDT[1]<<8 | bufSDT[2]) & 0x0FFF) - 7; sdt_scan += 5 + ((bufSDT[sdt_scan + 3]<<8 | bufSDT[sdt_scan + 4]) & 0x0FFF)) + { + for (pid_test = 0; pid_test < pids_found; pid_test++) + { + if ((bufSDT[sdt_scan]<<8 | bufSDT[sdt_scan + 1]) == pid_table[pid_test].service_id && bufSDT[sdt_scan + 5] == 0x48) + { + diff = 0; + pid_table[pid_test].service_name_len = bufSDT[sdt_scan+9 + bufSDT[sdt_scan+8]]; + + for (byte = 0; byte < pid_table[pid_test].service_name_len; byte++) + { + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'Ä') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x5B; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'ä') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x7B; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'Ö') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x5C; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'ö') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x7C; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'Ü') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x5D; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'ü') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x7D; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] == (unsigned char)'ß') + bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] = 0x7E; + if (bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] >= 0x80 && bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte] <= 0x9F) + diff--; + else + pid_table[pid_test].service_name[byte + diff] = bufSDT[sdt_scan+10 + bufSDT[sdt_scan + 8] + byte]; + } + + pid_table[pid_test].service_name_len += diff; + } + } + } + } + delete dmx; + SDT_ready = 1; + + /* show current servicename */ + current_service = 0; + + if (tuxtxt_cache.vtxtpid != 0) + { + while (pid_table[current_service].vtxt_pid != tuxtxt_cache.vtxtpid && current_service < pids_found) + current_service++; + + if (auto_national && current_service < pids_found) + national_subset = pid_table[current_service].national_subset; + RenderMessage(ShowServiceName); + } + + getpidsdone = 1; + + RenderCharLCD(pids_found/10, 7, 44); + RenderCharLCD(pids_found%10, 19, 44); + + return 1; +} + +/****************************************************************************** + * GetNationalSubset * + ******************************************************************************/ + +int GetNationalSubset(char *cc) +{ + if (memcmp(cc, "cze", 3) == 0 || memcmp(cc, "ces", 3) == 0 || + memcmp(cc, "slo", 3) == 0 || memcmp(cc, "slk", 3) == 0) + return 0; + if (memcmp(cc, "eng", 3) == 0) + return 1; + if (memcmp(cc, "est", 3) == 0) + return 2; + if (memcmp(cc, "fre", 3) == 0 || memcmp(cc, "fra", 3) == 0) + return 3; + if (memcmp(cc, "ger", 3) == 0 || memcmp(cc, "deu", 3) == 0) + return 4; + if (memcmp(cc, "ita", 3) == 0) + return 5; + if (memcmp(cc, "lav", 3) == 0 || memcmp(cc, "lit", 3) == 0) + return 6; + if (memcmp(cc, "pol", 3) == 0) + return 7; + if (memcmp(cc, "spa", 3) == 0 || memcmp(cc, "por", 3) == 0) + return 8; + if (memcmp(cc, "rum", 3) == 0 || memcmp(cc, "ron", 3) == 0) + return 9; + if (memcmp(cc, "scc", 3) == 0 || memcmp(cc, "srp", 3) == 0 || + memcmp(cc, "scr", 3) == 0 || memcmp(cc, "hrv", 3) == 0 || + memcmp(cc, "slv", 3) == 0) + return 10; + if (memcmp(cc, "swe", 3) == 0 || + memcmp(cc, "dan", 3) == 0 || + memcmp(cc, "nor", 3) == 0 || + memcmp(cc, "fin", 3) == 0 || + memcmp(cc, "hun", 3) == 0) + return 11; + if (memcmp(cc, "tur", 3) == 0) + return 12; + if (memcmp(cc, "rus", 3) == 0 || + memcmp(cc, "bul", 3) == 0 || + memcmp(cc, "ser", 3) == 0 || + memcmp(cc, "cro", 3) == 0 || + memcmp(cc, "ukr", 3) == 0) + return NAT_RU; + if (memcmp(cc, "gre", 3) == 0) + return NAT_GR; + + return NAT_DEFAULT; /* use default charset */ +} + +/****************************************************************************** + * ConfigMenu * + ******************************************************************************/ +#if TUXTXT_DEBUG +void charpage() +{ + PosY = StartY; + PosX = StartX; + char cachefill[100]; + int fullsize =0,hexcount = 0, col, p,sp; + int escpage = 0; + tstCachedPage* pg; + ClearFB(black); + + int zipsize = 0; + for (p = 0; p < 0x900; p++) + { + for (sp = 0; sp < 0x80; sp++) + { + pg = tuxtxt_cache.astCachetable[p][sp]; + if (pg) + { + + fullsize+=23*40; + zipsize += tuxtxt_get_zipsize(p,sp); + } + } + } + + + memset(cachefill,' ',40); + sprintf(cachefill,"f:%d z:%d h:%d c:%d %03x",fullsize, zipsize, hexcount, tuxtxt_cache.cached_pages, escpage); + + for (col = 0; col < 40; col++) + { + RenderCharFB(cachefill[col], &atrtable[ATR_WB]); + } + tstPageAttr atr; + memcpy(&atr,&atrtable[ATR_WB],sizeof(tstPageAttr)); + int row; + atr.charset = C_G0P; + PosY = StartY+fontheight; + for (row = 0; row < 16; row++) + { + PosY+= fontheight; + SetPosX(1); + for (col=0; col < 6; col++) + { + RenderCharFB(col*16+row+0x20, &atr); + } + } + atr.setX26 = 1; + PosY = StartY+fontheight; + for (row = 0; row < 16; row++) + { + PosY+= fontheight; + SetPosX(10); + for (col=0; col < 6; col++) + { + RenderCharFB(col*16+row+0x20, &atr); + } + } + PosY = StartY+fontheight; + atr.charset = C_G2; + atr.setX26 = 0; + for (row = 0; row < 16; row++) + { + PosY+= fontheight; + SetPosX(20); + for (col=0; col < 6; col++) + { + RenderCharFB(col*16+row+0x20, &atr); + } + } + atr.charset = C_G3; + PosY = StartY+fontheight; + for (row = 0; row < 16; row++) + { + PosY+= fontheight; + SetPosX(30); + for (col=0; col < 6; col++) + { + RenderCharFB(col*16+row+0x20, &atr); + } + } + do + { + GetRCCode(); + } + while (RCCode != RC_OK && RCCode != RC_HOME); +} +#endif +void Menu_HighlightLine(char *menu, int line, int high) +{ + char hilitline[] = "0111111111111111111111111111102"; + int itext = Menu_Width*line; /* index start menuline */ + int byte; + int national_subset_bak = national_subset; + + PosX = Menu_StartX; + PosY = Menu_StartY + line*fontheight; + if (line == MenuLine[M_NAT]) + national_subset = national_subset_bak; + else + national_subset = menusubset[menulanguage]; + + for (byte = 0; byte < Menu_Width; byte++) + RenderCharFB(menu[itext + byte], + high ? + &atrtable[hilitline[byte] - '0' + ATR_MENUHIL0] : + &atrtable[menuatr[itext + byte] - '0' + ATR_MENU0]); + national_subset = national_subset_bak; +} + +void Menu_UpdateHotlist(char *menu, int hotindex, int menuitem) +{ + int i, j, k; + tstPageAttr *attr; + + PosX = Menu_StartX + 6*fontwidth; + PosY = Menu_StartY + (MenuLine[M_HOT]+1)*fontheight; + j = Menu_Width*(MenuLine[M_HOT]+1) + 6; /* start index in menu */ + + for (i = 0; i <= maxhotlist+1; i++) + { + if (i == maxhotlist+1) /* clear last+1 entry in case it was deleted */ + { + attr = &atrtable[ATR_MENU5]; + memset(&menu[j], ' ', 3); + } + else + { + if (i == hotindex) + attr = &atrtable[ATR_MENU1]; + else + attr = &atrtable[ATR_MENU5]; + hex2str(&menu[j+2], hotlist[i]); + } + + for (k = 0; k < 3; k++) + RenderCharFB(menu[j+k], attr); + + if (i == 4) + { + PosX = Menu_StartX + 6*fontwidth; + PosY += fontheight; + j += 2*Menu_Width - 4*4; + } + else + { + j += 4; /* one space distance */ + PosX += fontwidth; + } + } + + hex2str(&menu[Menu_Width*MenuLine[M_HOT] + hotlistpagecolumn[menulanguage]], (hotindex >= 0) ? hotlist[hotindex] : tuxtxt_cache.page); + memcpy(&menu[Menu_Width*MenuLine[M_HOT] + hotlisttextcolumn[menulanguage]], &hotlisttext[menulanguage][(hotindex >= 0) ? 5 : 0], 5); + PosX = Menu_StartX + 20*fontwidth; + PosY = Menu_StartY + MenuLine[M_HOT]*fontheight; + + Menu_HighlightLine(menu, MenuLine[M_HOT], (menuitem == M_HOT) ? 1 : 0); +} + +void Menu_Init(char *menu, int current_pid, int menuitem, int hotindex) +{ + int byte, line; + int national_subset_bak = national_subset; + + memcpy(menu, configmenu[menulanguage], Menu_Height*Menu_Width); + + if (getpidsdone) + { + memset(&menu[MenuLine[M_PID]*Menu_Width+3], 0x20,24); + if (SDT_ready) + memcpy(&menu[MenuLine[M_PID]*Menu_Width+3+(24-pid_table[current_pid].service_name_len)/2], &pid_table[current_pid].service_name, pid_table[current_pid].service_name_len); + else + hex2str(&menu[MenuLine[M_PID]*Menu_Width + 13 + 3], tuxtxt_cache.vtxtpid); + } + if (!getpidsdone || current_pid == 0 || pids_found == 1) + menu[MenuLine[M_PID]*Menu_Width + 1] = ' '; + + if (!getpidsdone || current_pid == pids_found - 1 || pids_found == 1) + menu[MenuLine[M_PID]*Menu_Width + 28] = ' '; + + + /* set 16:9 modi, colors & national subset */ + memcpy(&menu[Menu_Width*MenuLine[M_SC1] + Menu_Width - 5], &configonoff[menulanguage][screen_mode1 ? 3 : 0], 3); + memcpy(&menu[Menu_Width*MenuLine[M_SC2] + Menu_Width - 5], &configonoff[menulanguage][screen_mode2 ? 3 : 0], 3); + + menu[MenuLine[M_COL]*Menu_Width + 1] = (color_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_COL]*Menu_Width + 28] = (color_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3 ], 0x7f,color_mode); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3+color_mode ], 0x20,24-color_mode); +// memcpy(&menu[Menu_Width*MenuLine[M_COL] + Menu_Width - 5], &configonoff[menulanguage][color_mode ? 3 : 0], 3); + menu[MenuLine[M_TRA]*Menu_Width + 1] = (trans_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_TRA]*Menu_Width + 28] = (trans_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3 ], 0x7f,trans_mode); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3+trans_mode ], 0x20,24-trans_mode); + + memcpy(&menu[Menu_Width*MenuLine[M_AUN] + Menu_Width - 5], &configonoff[menulanguage][auto_national ? 3 : 0], 3); + if (national_subset != NAT_DE) + memcpy(&menu[Menu_Width*MenuLine[M_NAT] + 2], &countrystring[national_subset*COUNTRYSTRING_WIDTH], COUNTRYSTRING_WIDTH); + if (national_subset == 0 || auto_national) + menu[MenuLine[M_NAT]*Menu_Width + 1] = ' '; + if (national_subset == MAX_NATIONAL_SUBSET || auto_national) + menu[MenuLine[M_NAT]*Menu_Width + 28] = ' '; + if (showhex) + menu[MenuLine[M_PID]*Menu_Width + 27] = '?'; + /* render menu */ + PosY = Menu_StartY; + for (line = 0; line < Menu_Height; line++) + { + PosX = Menu_StartX; + if (line == MenuLine[M_NAT]) + national_subset = national_subset_bak; + else + national_subset = menusubset[menulanguage]; + + if (line == Menu_Height-2) + memcpy(&menu[line*Menu_Width + 21], versioninfo, 4); + + for (byte = 0; byte < Menu_Width; byte++) + RenderCharFB(menu[line*Menu_Width + byte], &atrtable[menuatr[line*Menu_Width + byte] - '0' + ATR_MENU0]); + + PosY += fontheight; + } + national_subset = national_subset_bak; + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + Menu_UpdateHotlist(menu, hotindex, menuitem); +} + +void ConfigMenu(int Init) +{ +printf("[tuxtxt] Menu\n"); + int val, menuitem = M_Start; + int current_pid = 0; + int hotindex; + int oldscreenmode, oldtrans = 0; + int i; + int national_subset_bak = national_subset; + char menu[Menu_Height*Menu_Width]; + + if (auto_national && tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage] && + tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.nationalvalid) + national_subset = countryconversiontable[tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.national]; + + if (getpidsdone) + { + /* set current vtxt */ + if (tuxtxt_cache.vtxtpid == 0) + tuxtxt_cache.vtxtpid = pid_table[0].vtxt_pid; + else + while(pid_table[current_pid].vtxt_pid != tuxtxt_cache.vtxtpid && current_pid < pids_found) + current_pid++; + } + + /* reset to normal mode */ + if (zoommode) + zoommode = 0; + + if (transpmode) + { + oldtrans = transpmode; + transpmode = 0; + ClearBB(black); + } + + oldscreenmode = screenmode; + if (screenmode) + SwitchScreenMode(0); /* turn off divided screen */ + + hotindex = getIndexOfPageInHotlist(); + + /* clear framebuffer */ + ClearFB(transp); + clearbbcolor = black; + Menu_Init(menu, current_pid, menuitem, hotindex); + + /* set blocking mode */ + val = fcntl(rc, F_GETFL); + fcntl(rc, F_SETFL, val &~ O_NONBLOCK); + + /* loop */ + do { + if (GetRCCode() == 1) + { + + if ( +#if (RC_1 > 0) + RCCode >= RC_1 && /* generates a warning... */ +#endif + RCCode <= RC_1+M_MaxDirect) /* direct access */ + { + Menu_HighlightLine(menu, MenuLine[menuitem], 0); + menuitem = RCCode-RC_1; + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + + if (menuitem != M_PID) /* just select */ + RCCode = RC_OK; + } + + switch (RCCode) + { + case RC_UP: + Menu_HighlightLine(menu, MenuLine[menuitem], 0); + if (--menuitem < 0) + menuitem = M_Number-1; + if (auto_national && (menuitem == M_NAT)) + menuitem--; + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + break; + + case RC_DOWN: + Menu_HighlightLine(menu, MenuLine[menuitem], 0); + if (++menuitem > M_Number-1) + menuitem = 0; + if (auto_national && (menuitem == M_NAT)) + menuitem++; + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + break; + + case RC_LEFT: + switch (menuitem) + { + case M_COL: + saveconfig = 1; + color_mode--; + if (color_mode < 1) color_mode = 1; + menu[MenuLine[M_COL]*Menu_Width + 1] = (color_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_COL]*Menu_Width + 28] = (color_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3 ], 0x7f,color_mode); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3+color_mode ], 0x20,24-color_mode); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + setcolors((unsigned short *)defaultcolors, 0, SIZECOLTABLE); + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + case M_TRA: + saveconfig = 1; + trans_mode--; + if (trans_mode < 1) trans_mode = 1; + menu[MenuLine[M_TRA]*Menu_Width + 1] = (trans_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_TRA]*Menu_Width + 28] = (trans_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3 ], 0x7f,trans_mode); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3+trans_mode ], 0x20,24-trans_mode); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + setcolors((unsigned short *)defaultcolors, 0, SIZECOLTABLE); + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + case M_PID: + { + if (!getpidsdone) + { + GetTeletextPIDs(); + ClearFB(transp); + /* set current vtxt */ + if (tuxtxt_cache.vtxtpid == 0) + tuxtxt_cache.vtxtpid = pid_table[0].vtxt_pid; + else + while(pid_table[current_pid].vtxt_pid != tuxtxt_cache.vtxtpid && current_pid < pids_found) + current_pid++; + Menu_Init(menu, current_pid, menuitem, hotindex); + } + if (current_pid > 0) + { + current_pid--; + + memset(&menu[MenuLine[M_PID]*Menu_Width + 3], ' ', 24); + + if (SDT_ready) + { + memcpy(&menu[MenuLine[M_PID]*Menu_Width+3+(24-pid_table[current_pid].service_name_len)/2], + &pid_table[current_pid].service_name, + pid_table[current_pid].service_name_len); + } + else + hex2str(&menu[MenuLine[M_PID]*Menu_Width + 13 + 3], tuxtxt_cache.vtxtpid); + + if (pids_found > 1) + { + if (current_pid == 0) + { + menu[MenuLine[M_PID]*Menu_Width + 1] = ' '; + menu[MenuLine[M_PID]*Menu_Width + 28] = 'î'; + } + else + { + menu[MenuLine[M_PID]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_PID]*Menu_Width + 28] = 'î'; + } + } + + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + + if (auto_national) + { + national_subset = pid_table[current_pid].national_subset; + + memcpy(&menu[Menu_Width*MenuLine[M_NAT] + 2], &countrystring[national_subset*COUNTRYSTRING_WIDTH], COUNTRYSTRING_WIDTH); + Menu_HighlightLine(menu, MenuLine[M_NAT], 0); + } + } + break; + } + + case M_NAT: + saveconfig = 1; + if (national_subset > 0) + { + national_subset--; + + if (national_subset == 0) + { + menu[MenuLine[M_NAT]*Menu_Width + 1] = ' '; + menu[MenuLine[M_NAT]*Menu_Width + 28] = 'î'; + } + else + { + menu[MenuLine[M_NAT]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_NAT]*Menu_Width + 28] = 'î'; + } + + Menu_Init(menu, current_pid, menuitem, hotindex); + } + break; + + case M_HOT: /* move towards top of hotlist */ + if (hotindex <= 0) /* if not found, start at end */ + hotindex = maxhotlist; + else + hotindex--; + Menu_UpdateHotlist(menu, hotindex, menuitem); + break; + + case M_LNG: + saveconfig = 1; + if (--menulanguage < 0) + menulanguage = MAXMENULANGUAGE; + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + } /* switch menuitem */ + break; /* RC_LEFT */ + + case RC_RIGHT: + switch (menuitem) + { + case M_COL: + saveconfig = 1; + color_mode++; + if (color_mode > 24) color_mode = 24; + menu[MenuLine[M_COL]*Menu_Width + 1] = (color_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_COL]*Menu_Width + 28] = (color_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3 ], 0x7f,color_mode); + memset(&menu[Menu_Width*MenuLine[M_COL] + 3+color_mode ], 0x20,24-color_mode); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + setcolors((unsigned short *)defaultcolors, 0, SIZECOLTABLE); + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + case M_TRA: + saveconfig = 1; + trans_mode++; + if (trans_mode > 24) trans_mode = 24; + menu[MenuLine[M_TRA]*Menu_Width + 1] = (trans_mode == 1 ? ' ' : 'í'); + menu[MenuLine[M_TRA]*Menu_Width + 28] = (trans_mode == 24 ? ' ' : 'î'); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3 ], 0x7f,trans_mode); + memset(&menu[Menu_Width*MenuLine[M_TRA] + 3+trans_mode ], 0x20,24-trans_mode); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + setcolors((unsigned short *)defaultcolors, 0, SIZECOLTABLE); + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + case M_PID: + if (!getpidsdone) + { + GetTeletextPIDs(); + ClearFB(transp); + /* set current vtxt */ + if (tuxtxt_cache.vtxtpid == 0) + tuxtxt_cache.vtxtpid = pid_table[0].vtxt_pid; + else + while(pid_table[current_pid].vtxt_pid != tuxtxt_cache.vtxtpid && current_pid < pids_found) + current_pid++; + Menu_Init(menu, current_pid, menuitem, hotindex); + } + if (current_pid < pids_found - 1) + { + current_pid++; + + memset(&menu[MenuLine[M_PID]*Menu_Width + 3], ' ', 24); + + if (SDT_ready) + memcpy(&menu[MenuLine[M_PID]*Menu_Width + 3 + + (24-pid_table[current_pid].service_name_len)/2], + &pid_table[current_pid].service_name, + pid_table[current_pid].service_name_len); + else + hex2str(&menu[MenuLine[M_PID]*Menu_Width + 13 + 3], pid_table[current_pid].vtxt_pid); + + if (pids_found > 1) + { + if (current_pid == pids_found - 1) + { + menu[MenuLine[M_PID]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_PID]*Menu_Width + 28] = ' '; + } + else + { + menu[MenuLine[M_PID]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_PID]*Menu_Width + 28] = 'î'; + } + } + + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + + if (auto_national) + { + if (getpidsdone) + national_subset = pid_table[current_pid].national_subset; + memcpy(&menu[Menu_Width*MenuLine[M_NAT] + 2], &countrystring[national_subset*COUNTRYSTRING_WIDTH], COUNTRYSTRING_WIDTH); + Menu_HighlightLine(menu, MenuLine[M_NAT], 0); + } + } + break; + + case M_NAT: + saveconfig = 1; + if (national_subset < (int) MAX_NATIONAL_SUBSET) + { + national_subset++; + + if (national_subset == (int) MAX_NATIONAL_SUBSET) + { + menu[MenuLine[M_NAT]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_NAT]*Menu_Width + 28] = ' '; + } + else + { + menu[MenuLine[M_NAT]*Menu_Width + 1] = 'í'; + menu[MenuLine[M_NAT]*Menu_Width + 28] = 'î'; + } + + Menu_Init(menu, current_pid, menuitem, hotindex); + } + break; + + case M_HOT: /* select hotindex */ + if ((int)hotindex >= maxhotlist) /* if not found, start at 0 */ + hotindex = 0; + else + hotindex++; + Menu_UpdateHotlist(menu, hotindex, menuitem); + break; + + case M_LNG: + saveconfig = 1; + if (++menulanguage > MAXMENULANGUAGE) + menulanguage = 0; + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + } + break; /* RC_RIGHT */ + + case RC_PLUS: + switch (menuitem) + { + case M_HOT: /* move towards end of hotlist */ + { + if (hotindex<0) /* not found: add page at end */ + { + if (maxhotlist < (int) (sizeof(hotlist)/sizeof(hotlist[0])-1)) /* only if still room left */ + { + hotindex = ++maxhotlist; + hotlist[hotindex] = tuxtxt_cache.page; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + else /* found */ + { + if (hotindex < maxhotlist) /* not already at end */ + { + int temp = hotlist[hotindex]; + hotlist[hotindex] = hotlist[hotindex+1]; + hotlist[hotindex+1] = temp; + hotindex++; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + } + break; + } + break; /* RC_PLUS */ + + case RC_MINUS: + switch (menuitem) + { + case M_HOT: /* move towards top of hotlist */ + { + if (hotindex<0) /* not found: add page at top */ + { + if (maxhotlist < (int) (sizeof(hotlist)/sizeof(hotlist[0])-1)) /* only if still room left */ + { + for (hotindex = maxhotlist; hotindex >= 0; hotindex--) /* move rest of list */ + { + hotlist[hotindex+1] = hotlist[hotindex]; + } + maxhotlist++; + hotindex = 0; + hotlist[hotindex] = tuxtxt_cache.page; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + else /* found */ + { + if (hotindex > 0) /* not already at front */ + { + int temp = hotlist[hotindex]; + hotlist[hotindex] = hotlist[hotindex-1]; + hotlist[hotindex-1] = temp; + hotindex--; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + } + break; + } + break; /* RC_MINUS */ + + case RC_HELP: + switch (menuitem) + { + case M_HOT: /* current page is added to / removed from hotlist */ + { + if (hotindex<0) /* not found: add page */ + { + if (maxhotlist < (int) (sizeof(hotlist)/sizeof(hotlist[0])-1)) /* only if still room left */ + { + hotlist[++maxhotlist] = tuxtxt_cache.page; + hotindex = maxhotlist; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + else /* found: remove */ + { + if (maxhotlist > 0) /* don't empty completely */ + { + int i; + + for (i=hotindex; i maxhotlist) + hotindex = maxhotlist; + hotlistchanged = 1; + Menu_UpdateHotlist(menu, hotindex, menuitem); + } + } + } + break; + case M_PID: + showhex ^= 1; + menu[MenuLine[M_PID]*Menu_Width + 27] = (showhex ? '?' : ' '); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + break; +#if TUXTXT_DEBUG + case M_LNG: + charpage(); + ClearFB(transp); + Menu_Init(menu, current_pid, menuitem, hotindex); + break; +#endif + } + break; /* RC_MUTE */ + + case RC_OK: + switch (menuitem) + { + case M_PID: + if (!getpidsdone) + { + GetTeletextPIDs(); + ClearFB(transp); + /* set current vtxt */ + if (tuxtxt_cache.vtxtpid == 0) + tuxtxt_cache.vtxtpid = pid_table[0].vtxt_pid; + else + while(pid_table[current_pid].vtxt_pid != tuxtxt_cache.vtxtpid && current_pid < pids_found) + current_pid++; + Menu_Init(menu, current_pid, menuitem, hotindex); + } + else if (pids_found > 1) + { + if (hotlistchanged) + savehotlist(); + + if (Init || tuxtxt_cache.vtxtpid != pid_table[current_pid].vtxt_pid) + { +#if TUXTXT_CFG_STANDALONE + tuxtxt_stop_thread(); + tuxtxt_clear_cache(); +#else + tuxtxt_stop(); + if (Init) + tuxtxt_cache.vtxtpid = 0; // force clear cache +#endif + /* reset data */ + + + //page_atrb[32] = transp<<4 | transp; + inputcounter = 2; + + + tuxtxt_cache.page = 0x100; + lastpage = 0x100; + prev_100 = 0x100; + prev_10 = 0x100; + next_100 = 0x100; + next_10 = 0x100; + tuxtxt_cache.subpage = 0; + + tuxtxt_cache.pageupdate = 0; + tuxtxt_cache.zap_subpage_manual = 0; + hintmode = 0; + memset(page_char,' ',40 * 25); + + for (i = 0; i < 40*25; i++) + { + page_atrb[i].fg = transp; + page_atrb[i].bg = transp; + } + ClearFB(transp); + + + /* start demuxer with new vtxtpid */ + if (auto_national) + national_subset = pid_table[current_pid].national_subset; + +#if TUXTXT_CFG_STANDALONE + tuxtxt_cache.vtxtpid = pid_table[current_pid].vtxt_pid; + tuxtxt_start_thread(); +#else + tuxtxt_start(pid_table[current_pid].vtxt_pid); +#endif + } +// tuxtxt_cache.pageupdate = 1; + + ClearBB(black); + gethotlist(); + + /* show new teletext */ + current_service = current_pid; +// RenderMessage(ShowServiceName); + + fcntl(rc, F_SETFL, O_NONBLOCK); + RCCode = -1; + if (oldscreenmode) + SwitchScreenMode(oldscreenmode); /* restore divided screen */ +printf("[tuxtxt] Menu return from M_PID\n"); + transpmode = oldtrans; + return; + } + break; + + case M_SC1: + saveconfig = 1; + screen_mode1++; + screen_mode1 &= 1; + + memcpy(&menu[Menu_Width*MenuLine[M_SC1] + Menu_Width - 5], &configonoff[menulanguage][screen_mode1 ? 3 : 0], 3); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + + break; + + case M_SC2: + saveconfig = 1; + screen_mode2++; + screen_mode2 &= 1; + + memcpy(&menu[Menu_Width*MenuLine[M_SC2] + Menu_Width - 5], &configonoff[menulanguage][screen_mode2 ? 3 : 0], 3); + Menu_HighlightLine(menu, MenuLine[menuitem], 1); + break; + + + case M_AUN: + saveconfig = 1; + auto_national++; + auto_national &= 1; + if (auto_national) + { + if (getpidsdone) + national_subset = pid_table[current_pid].national_subset; + else + { + if (tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage] && + tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.nationalvalid) + national_subset = countryconversiontable[tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]->pageinfo.national]; + else + national_subset = national_subset_bak; + } + + } + Menu_Init(menu, current_pid, menuitem, hotindex); + break; + case M_HOT: /* show selected page */ + { + if (hotindex >= 0) /* not found: ignore */ + { + lastpage = tuxtxt_cache.page; + tuxtxt_cache.page = hotlist[hotindex]; + tuxtxt_cache.subpage = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + inputcounter = 2; + tuxtxt_cache.pageupdate = 1; + RCCode = RC_HOME; /* leave menu */ + } + } + break; + } /* RC_OK */ + break; + } + } + UpdateLCD(); /* update number of cached pages */ + } while ((RCCode != RC_HOME) && (RCCode != RC_DBOX) && (RCCode != RC_MUTE)); + + /* reset to nonblocking mode */ + fcntl(rc, F_SETFL, O_NONBLOCK); + tuxtxt_cache.pageupdate = 1; + RCCode = -1; + if (oldscreenmode) + SwitchScreenMode(oldscreenmode); /* restore divided screen */ + transpmode = oldtrans; +printf("[tuxtxt] Menu return\n"); +} + +/****************************************************************************** + * PageInput * + ******************************************************************************/ + +void PageInput(int Number) +{ + int zoom = 0; + + /* clear temp_page */ + if (inputcounter == 2) + temp_page = 0; + + /* check for 0 & 9 on first position */ + if (Number == 0 && inputcounter == 2) + { + /* set page */ + temp_page = lastpage; /* 0 toggles to last page as in program switching */ + inputcounter = -1; + } + else if (Number == 9 && inputcounter == 2) + { + /* set page */ + temp_page = getIndexOfPageInHotlist(); /* 9 toggles through hotlist */ + + if (temp_page<0 || temp_page==maxhotlist) /* from any (other) page go to first page in hotlist */ + temp_page = (maxhotlist >= 0) ? hotlist[0] : 0x100; + else + temp_page = hotlist[temp_page+1]; + + inputcounter = -1; + } + + /* show pageinput */ + if (zoommode == 2) + { + zoommode = 1; + CopyBB2FB(); + } + + if (zoommode == 1) + zoom = 1<<10; + + PosY = StartY; + + switch (inputcounter) + { + case 2: + SetPosX(1); + RenderCharFB(Number | '0', &atrtable[ATR_WB]); + RenderCharFB('-', &atrtable[ATR_WB]); + RenderCharFB('-', &atrtable[ATR_WB]); + break; + + case 1: + SetPosX(2); + RenderCharFB(Number | '0', &atrtable[ATR_WB]); + break; + + case 0: + SetPosX(3); + RenderCharFB(Number | '0', &atrtable[ATR_WB]); + break; + } + + /* generate pagenumber */ + temp_page |= Number << inputcounter*4; + + inputcounter--; + + if (inputcounter < 0) + { + /* disable subpage zapping */ + tuxtxt_cache.zap_subpage_manual = 0; + + /* reset input */ + inputcounter = 2; + + /* set new page */ + lastpage = tuxtxt_cache.page; + + tuxtxt_cache.page = temp_page; + hintmode = 0; + + /* check cache */ + int subp = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + if (subp != 0xFF) + { + tuxtxt_cache.subpage = subp; + tuxtxt_cache.pageupdate = 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage); +#endif + } + else + { + tuxtxt_cache.subpage = 0; +// RenderMessage(PageNotFound); +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page); +#endif + } + } +} + +/****************************************************************************** + * GetNextPageOne * + ******************************************************************************/ + +void GetNextPageOne(int up) +{ + /* disable subpage zapping */ + tuxtxt_cache.zap_subpage_manual = 0; + + /* abort pageinput */ + inputcounter = 2; + + /* find next cached page */ + lastpage = tuxtxt_cache.page; + + int subp; + do { + if (up) + tuxtxt_next_dec(&tuxtxt_cache.page); + else + tuxtxt_prev_dec(&tuxtxt_cache.page); + subp = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + } while (subp == 0xFF && tuxtxt_cache.page != lastpage); + + /* update page */ + if (tuxtxt_cache.page != lastpage) + { + if (zoommode == 2) + zoommode = 1; + + tuxtxt_cache.subpage = subp; + hintmode = 0; + tuxtxt_cache.pageupdate = 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage); +#endif + } +} + +/****************************************************************************** + * GetNextSubPage * + ******************************************************************************/ +void GetNextSubPage(int offset) +{ + int loop; + + /* abort pageinput */ + inputcounter = 2; + + for (loop = tuxtxt_cache.subpage + offset; loop != tuxtxt_cache.subpage; loop += offset) + { + if (loop < 0) + loop = 0x79; + else if (loop > 0x79) + loop = 0; + if (loop == tuxtxt_cache.subpage) + break; + + if (tuxtxt_cache.astCachetable[tuxtxt_cache.page][loop]) + { + /* enable manual subpage zapping */ + tuxtxt_cache.zap_subpage_manual = 1; + + /* update page */ + if (zoommode == 2) /* if zoomed to lower half */ + zoommode = 1; /* activate upper half */ + + tuxtxt_cache.subpage = loop; + hintmode = 0; + tuxtxt_cache.pageupdate = 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage); +#endif + return; + } + } + +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif +} +/****************************************************************************** + * ColorKey * + ******************************************************************************/ + +void ColorKey(int target) +{ + if (!target) + return; + if (zoommode == 2) + zoommode = 1; + lastpage = tuxtxt_cache.page; + tuxtxt_cache.page = target; + tuxtxt_cache.subpage = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + inputcounter = 2; + hintmode = 0; + tuxtxt_cache.pageupdate = 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page); +#endif +} + +/****************************************************************************** + * PageCatching * + ******************************************************************************/ + +void PageCatching() +{ + int val, byte; + int oldzoommode = zoommode; + + pagecatching = 1; + + /* abort pageinput */ + inputcounter = 2; + + /* show info line */ + zoommode = 0; + PosX = StartX; + PosY = StartY + 24*fontheight; + for (byte = 0; byte < 40-nofirst; byte++) + RenderCharFB(catchmenutext[menulanguage][byte], &atrtable[catchmenutext[menulanguage][byte+40] - '0' + ATR_CATCHMENU0]); + zoommode = oldzoommode; + + /* check for pagenumber(s) */ + catch_row = 1; + catch_col = 0; + catched_page = 0; + pc_old_row = pc_old_col = 0; /* no inverted page number to restore yet */ + CatchNextPage(0, 1); + + if (!catched_page) + { + pagecatching = 0; + tuxtxt_cache.pageupdate = 1; + return; + } + + /* set blocking mode */ + val = fcntl(rc, F_GETFL); + fcntl(rc, F_SETFL, val &~ O_NONBLOCK); + + /* loop */ + do { + GetRCCode(); + + switch (RCCode) + { + case RC_LEFT: + CatchNextPage(0, -1); + break; + case RC_RIGHT: + CatchNextPage(0, 1); + break; + case RC_UP: + CatchNextPage(-1, -1); + break; + case RC_DOWN: + CatchNextPage(1, 1); + break; + case RC_0: + case RC_1: + case RC_2: + case RC_3: + case RC_4: + case RC_5: + case RC_6: + case RC_7: + case RC_8: + case RC_9: + case RC_RED: + case RC_GREEN: + case RC_YELLOW: + case RC_BLUE: + case RC_PLUS: + case RC_MINUS: + case RC_DBOX: + case RC_HOME: + case RC_HELP: + case RC_MUTE: + fcntl(rc, F_SETFL, O_NONBLOCK); + tuxtxt_cache.pageupdate = 1; + pagecatching = 0; + RCCode = -1; + return; + } + UpdateLCD(); + } while (RCCode != RC_OK); + + /* set new page */ + if (zoommode == 2) + zoommode = 1; + + lastpage = tuxtxt_cache.page; + tuxtxt_cache.page = catched_page; + hintmode = 0; + tuxtxt_cache.pageupdate = 1; + pagecatching = 0; + + int subp = tuxtxt_cache.subpagetable[tuxtxt_cache.page]; + if (subp != 0xFF) + tuxtxt_cache.subpage = subp; + else + tuxtxt_cache.subpage = 0; + + /* reset to nonblocking mode */ + fcntl(rc, F_SETFL, O_NONBLOCK); +} + +/****************************************************************************** + * CatchNextPage * + ******************************************************************************/ + +void CatchNextPage(int firstlineinc, int inc) +{ + int tmp_page, allowwrap = 1; /* allow first wrap around */ + + /* catch next page */ + for(;;) + { + unsigned char *p = &(page_char[catch_row*40 + catch_col]); + tstPageAttr a = page_atrb[catch_row*40 + catch_col]; + + if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */ + (a.fg != a.bg) && /* not hidden */ + (*p >= '1' && *p <= '8' && /* valid page number */ + *(p+1) >= '0' && *(p+1) <= '9' && + *(p+2) >= '0' && *(p+2) <= '9') && + (catch_row == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */ + (catch_row == 37 || (*(p+3) < '0' || *(p+3) > '9'))) + { + tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0'); + +#if 0 + if (tmp_page != catched_page) /* confusing to skip identical page numbers - I want to reach what I aim to */ +#endif + { + catched_page = tmp_page; + RenderCatchedPage(); + catch_col += inc; /* FIXME: limit */ +#if TUXTXT_DEBUG + printf("TuxTxt 0) + { + catch_row++; + catch_col = 0; + firstlineinc = 0; + } + else if (firstlineinc < 0) + { + catch_row--; + catch_col = 37; + firstlineinc = 0; + } + else + catch_col += inc; + + if (catch_col > 37) + { + catch_row++; + catch_col = 0; + } + else if (catch_col < 0) + { + catch_row--; + catch_col = 37; + } + + if (catch_row > 23) + { + if (allowwrap) + { + allowwrap = 0; + catch_row = 1; + catch_col = 0; + } + else + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + return; + } + } + else if (catch_row < 1) + { + if (allowwrap) + { + allowwrap = 0; + catch_row = 23; + catch_col =37; + } + else + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + return; + } + } + } +} + + +/****************************************************************************** + * RenderCatchedPage * + ******************************************************************************/ + +void RenderCatchedPage() +{ + int zoom = 0; + + /* handle zoom */ + if (zoommode) + zoom = 1<<10; + + if (pc_old_row || pc_old_col) /* not at first call */ + { + /* restore pagenumber */ + SetPosX(pc_old_col); + + if (zoommode == 2) + PosY = StartY + (pc_old_row-12)*fontheight*((zoom>>10)+1); + else + PosY = StartY + pc_old_row*fontheight*((zoom>>10)+1); + + RenderCharFB(page_char[pc_old_row*40 + pc_old_col ], &page_atrb[pc_old_row*40 + pc_old_col ]); + RenderCharFB(page_char[pc_old_row*40 + pc_old_col + 1], &page_atrb[pc_old_row*40 + pc_old_col + 1]); + RenderCharFB(page_char[pc_old_row*40 + pc_old_col + 2], &page_atrb[pc_old_row*40 + pc_old_col + 2]); + } + + pc_old_row = catch_row; + pc_old_col = catch_col; + + /* mark pagenumber */ + if (zoommode == 1 && catch_row > 11) + { + zoommode = 2; + CopyBB2FB(); + } + else if (zoommode == 2 && catch_row < 12) + { + zoommode = 1; + CopyBB2FB(); + } + SetPosX(catch_col); + + + if (zoommode == 2) + PosY = StartY + (catch_row-12)*fontheight*((zoom>>10)+1); + else + PosY = StartY + catch_row*fontheight*((zoom>>10)+1); + + tstPageAttr a0 = page_atrb[catch_row*40 + catch_col ]; + tstPageAttr a1 = page_atrb[catch_row*40 + catch_col + 1]; + tstPageAttr a2 = page_atrb[catch_row*40 + catch_col + 2]; + int t; + + /* exchange colors */ + t = a0.fg; a0.fg = a0.bg; a0.bg = t; + t = a1.fg; a1.fg = a1.bg; a1.bg = t; + t = a2.fg; a2.fg = a2.bg; a2.bg = t; + + RenderCharFB(page_char[catch_row*40 + catch_col ], &a0); + RenderCharFB(page_char[catch_row*40 + catch_col + 1], &a1); + RenderCharFB(page_char[catch_row*40 + catch_col + 2], &a2); +} + +/****************************************************************************** + * SwitchZoomMode * + ******************************************************************************/ + +void SwitchZoomMode() +{ + if (tuxtxt_cache.subpagetable[tuxtxt_cache.page] != 0xFF) + { + /* toggle mode */ + zoommode++; + + if (zoommode == 3) + zoommode = 0; + +#if TUXTXT_DEBUG + printf("TuxTxt \n", zoommode); +#endif + /* update page */ + tuxtxt_cache.pageupdate = 1; /* FIXME */ + } +} + +/****************************************************************************** + * SwitchScreenMode * + ******************************************************************************/ + +void SwitchScreenMode(int newscreenmode) +{ + + //struct v4l2_format format; + + /* reset transparency mode */ + if (transpmode) + transpmode = 0; + //transpmode = 1; //NEW + + if (newscreenmode < 0) /* toggle mode */ + screenmode++; + else /* set directly */ + screenmode = newscreenmode; +// if ((screenmode > (screen_mode2 ? 2 : 1)) || (screenmode < 0)) + if ((screenmode > 2) || (screenmode < 0)) + screenmode = 0; + +#if TUXTXT_DEBUG + printf("TuxTxt \n", screenmode); +#endif + + /* update page */ + tuxtxt_cache.pageupdate = 1; + + /* clear back buffer */ + clearbbcolor = screenmode?transp:FullScrColor; + ClearBB(clearbbcolor); + + /* set mode */ + if (screenmode) /* split */ + { + ClearFB(clearbbcolor); + + int fw, fh, tx, ty, tw, th; + + if (screenmode==1) /* split with topmenu */ + { + fw = fontwidth_topmenumain; + fh = fontheight; + tw = TV43WIDTH; + displaywidth= (TV43STARTX -sx); + StartX = sx; //+ (((ex-sx) - (40*fw+2+tw)) / 2); /* center screen */ + tx = TV43STARTX; + ty = TV43STARTY; + th = TV43HEIGHT; + } + else /* 2: split with full height tv picture */ + { + fw = fontwidth_small; + fh = fontheight; + tx = TV169FULLSTARTX; + ty = TV169FULLSTARTY; + tw = TV169FULLWIDTH; + th = TV169FULLHEIGHT; + displaywidth= (TV169FULLSTARTX-sx); + } + + setfontwidth(fw); + +#if 0 + int sm = 0; + ioctl(pig, VIDIOC_OVERLAY, &sm); + sm = 1; + ioctl(pig, VIDIOC_G_FMT, &format); + format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + format.fmt.win.w.left = tx; + format.fmt.win.w.top = ty; + format.fmt.win.w.width = tw; + format.fmt.win.w.height = th; + ioctl(pig, VIDIOC_S_FMT, &format); + ioctl(pig, VIDIOC_OVERLAY, &sm); +#endif + } + else /* not split */ + { +#if 0 + ioctl(pig, VIDIOC_OVERLAY, &screenmode); +#endif + + setfontwidth(fontwidth_normal); + displaywidth= (ex-sx); + StartX = sx; //+ (ex-sx - 40*fontwidth) / 2; /* center screen */ + } +} + +/****************************************************************************** + * SwitchTranspMode * + ******************************************************************************/ + +void SwitchTranspMode() +{ + if (screenmode) + { + prevscreenmode = screenmode; + SwitchScreenMode(0); /* turn off divided screen */ + } + /* toggle mode */ +#if 0 // transparent first + if (transpmode == 2) + transpmode = 0; + else + transpmode++; /* backward to immediately switch to TV-screen */ +#endif + /* transpmode: 0 normal, 1 transparent, 2 off */ + transpmode ++; + if(transpmode > 2) + transpmode = 0; + +#if TUXTXT_DEBUG + printf("TuxTxt \n", transpmode); +#endif + + /* set mode */ + if (!transpmode) /* normal text-only */ + { + ClearBB(FullScrColor); + tuxtxt_cache.pageupdate = 1; + } + else if (transpmode == 1) /* semi-transparent BG with FG text */ + { + ClearBB(transp); + tuxtxt_cache.pageupdate = 1; + } + else /* TV mode */ + { + ClearFB(transp); + clearbbcolor = FullScrColor; + } +} + +/****************************************************************************** + * SwitchHintMode * + ******************************************************************************/ + +void SwitchHintMode() +{ + /* toggle mode */ + hintmode ^= 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", hintmode); +#endif + + if (!hintmode) /* toggle evaluation of level 2.5 information by explicitly switching off hintmode */ + { + showl25 ^= 1; +#if TUXTXT_DEBUG + printf("TuxTxt \n", showl25); +#endif + } + /* update page */ + tuxtxt_cache.pageupdate = 1; +} + +void RenderDRCS( //FIX ME + unsigned char *s, /* pointer to char data, parity undecoded */ + unsigned char *d, /* pointer to frame buffer of top left pixel */ + unsigned char *ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ + unsigned char fgcolor, unsigned char bgcolor) +{ + int bit, x, y, ltmp; + unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */ + + for (y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */ + { + unsigned char c1 = deparity[*s++]; + unsigned char c2 = deparity[*s++]; + int h = ay[y+1] - ay[y]; + + if (!h) + continue; + if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */ + return; + for (bit = 0x20, x = 0; + bit; + bit >>= 1, x++) /* bit mask (MSB left), column counter */ + { + int i, f1, f2; + + f1 = (c1 & bit) ? fgcolor : bgcolor; + f2 = (c2 & bit) ? fgcolor : bgcolor; + + for (i = 0; i < h; i++) + { + if (ax[x+1] > ax[x]) + { +// memset(d + ax[x], f1, ax[x+1] - ax[x]); + for (ltmp=0 ; ltmp <= (ax[x+1]-ax[x]); ltmp++) + { + memcpy(d + ax[x]*4 +ltmp*4,bgra[f1],4); + } + } + if (ax[x+7] > ax[x+6]) + { +// memset(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */ + for (ltmp=0 ; ltmp <= (ax[x+7]-ax[x+6]); ltmp++) + { + memcpy(d + ax[x+6]*4 +ltmp*4,bgra[f2],4); + } + + } + d += fix_screeninfo.line_length; + } + d -= h * fix_screeninfo.line_length; + } + d += h * fix_screeninfo.line_length; + } +} + + +void DrawVLine(int x, int y, int l, int color) +{ + unsigned char *p = lfb + x*4 + y * fix_screeninfo.line_length; + + for ( ; l > 0 ; l--) + { + memcpy(p,bgra[color],4); + p += fix_screeninfo.line_length; + } +} + +void DrawHLine(int x, int y, int l, int color) +{ + int ltmp; + if (l > 0) + { + for (ltmp=0; ltmp <= l; ltmp++) + { + memcpy(lfb + x*4 + ltmp*4 + y * fix_screeninfo.line_length, bgra[color], 4); + } + } +} + +void FillRectMosaicSeparated(int x, int y, int w, int h, int fgcolor, int bgcolor, int set) +{ + FillRect(x, y, w, h, bgcolor); + if (set) + { + FillRect(x+1, y+1, w-2, h-2, fgcolor); + } +} + +void FillTrapez(int x0, int y0, int l0, int xoffset1, int h, int l1, int color) +{ + unsigned char *p = lfb + x0*4 + y0 * fix_screeninfo.line_length; + int xoffset, l; + int yoffset; + int ltmp; + + for (yoffset = 0; yoffset < h; yoffset++) + { + l = l0 + ((l1-l0) * yoffset + h/2) / h; + xoffset = (xoffset1 * yoffset + h/2) / h; + if (l > 0) + { + for (ltmp=0; ltmp <= l; ltmp++) + { + memcpy(p + xoffset*4 +ltmp*4, bgra[color], 4); + } + } + p += fix_screeninfo.line_length; + } +} +void FlipHorz(int x, int y, int w, int h) +{ + unsigned char buf[w*4]; + unsigned char *p = lfb + x*4 + y * fix_screeninfo.line_length; + int w1,h1; + + for (h1 = 0 ; h1 < h ; h1++) + { + memcpy(buf,p,w*4); + for (w1 = 0 ; w1 < w ; w1++) + { + memcpy(p+w1*4,buf+((w-w1)*4)-4,4); + } + p += fix_screeninfo.line_length; + } +} +void FlipVert(int x, int y, int w, int h) +{ + unsigned char buf[w*4]; + unsigned char *p = lfb + x*4 + y * fix_screeninfo.line_length, *p1, *p2; + int h1; + + for (h1 = 0 ; h1 < h/2 ; h1++) + { + p1 = (p+(h1*fix_screeninfo.line_length)); + p2 = (p+(h-(h1+1))*fix_screeninfo.line_length); + memcpy(buf,p1,w*4); + memcpy(p1,p2,w*4); + memcpy(p2,buf,w*4); + } +} + +int ShapeCoord(int param, int curfontwidth, int curfontheight) +{ + switch (param) + { + case S_W13: + return curfontwidth/3; + case S_W12: + return curfontwidth/2; + case S_W23: + return curfontwidth*2/3; + case S_W11: + return curfontwidth; + case S_WM3: + return curfontwidth-3; + case S_H13: + return curfontheight/3; + case S_H12: + return curfontheight/2; + case S_H23: + return curfontheight*2/3; + case S_H11: + return curfontheight; + default: + return param; + } +} + +void DrawShape(int x, int y, int shapenumber, int curfontwidth, int curfontheight, int fgcolor, int bgcolor, int clear) +{ + if (shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear)) + return; + + unsigned char *p = aShapes[shapenumber - 0x20]; + + if (*p == S_INV) + { + int t = fgcolor; + fgcolor = bgcolor; + bgcolor = t; + p++; + } + + if (clear) + FillRect(x, y, curfontwidth, fontheight, bgcolor); + while (*p != S_END) + switch (*p++) + { + case S_FHL: + { + int offset = ShapeCoord(*p++, curfontwidth, curfontheight); + DrawHLine(x, y + offset, curfontwidth, fgcolor); + break; + } + case S_FVL: + { + int offset = ShapeCoord(*p++, curfontwidth, curfontheight); + DrawVLine(x + offset, y, fontheight, fgcolor); + break; + } + case S_FLH: + FlipHorz(x,y,curfontwidth, fontheight); + break; + case S_FLV: + FlipVert(x,y,curfontwidth, fontheight); + break; + case S_BOX: + { + int xo = ShapeCoord(*p++, curfontwidth, curfontheight); + int yo = ShapeCoord(*p++, curfontwidth, curfontheight); + int w = ShapeCoord(*p++, curfontwidth, curfontheight); + int h = ShapeCoord(*p++, curfontwidth, curfontheight); + FillRect(x + xo, y + yo, w, h, fgcolor); + break; + } + case S_TRA: + { + int x0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int y0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int l0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int x1 = ShapeCoord(*p++, curfontwidth, curfontheight); + int y1 = ShapeCoord(*p++, curfontwidth, curfontheight); + int l1 = ShapeCoord(*p++, curfontwidth, curfontheight); + FillTrapez(x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor); + break; + } + case S_BTR: + { + int x0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int y0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int l0 = ShapeCoord(*p++, curfontwidth, curfontheight); + int x1 = ShapeCoord(*p++, curfontwidth, curfontheight); + int y1 = ShapeCoord(*p++, curfontwidth, curfontheight); + int l1 = ShapeCoord(*p++, curfontwidth, curfontheight); + FillTrapez(x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor); + break; + } + case S_LNK: + { + DrawShape(x, y, ShapeCoord(*p, curfontwidth, curfontheight), curfontwidth, curfontheight, fgcolor, bgcolor, 0); + //p = aShapes[ShapeCoord(*p, curfontwidth, curfontheight) - 0x20]; + break; + } + default: + break; + } +} + + +/****************************************************************************** + * RenderChar * + ******************************************************************************/ + +void RenderChar(int Char, tstPageAttr *Attribute, int zoom, int yoffset) +{ + int Row, Pitch, Bit; + int error, glyph; + int bgcolor, fgcolor; + int factor, xfactor; + int national_subset_local = national_subset; + unsigned char *sbitbuffer; + + int curfontwidth = GetCurFontWidth(); + + if (Attribute->setX26) + { + national_subset_local = 0; // no national subset + } + + // G0+G2 set designation + if (Attribute->setG0G2 != 0x3f) + { + switch (Attribute->setG0G2) + { + case 0x20 : + case 0x24 : + case 0x25 : + national_subset_local = NAT_RU; + break; + case 0x37: + national_subset_local = NAT_GR; + break; + //TODO: arabic and hebrew + default: + national_subset_local = countryconversiontable[Attribute->setG0G2 & 0x07]; + break; + + } + + } + if (Attribute->charset == C_G0S) // use secondary charset + national_subset_local = national_subset_secondary; + if (zoom && Attribute->doubleh) + factor = 4; + else if (zoom || Attribute->doubleh) + factor = 2; + else + factor = 1; + + if (Attribute->doublew) + { + int t = curfontwidth; + PosX += t; + curfontwidth += GetCurFontWidth(); + PosX -= t; + xfactor = 2; + } + else + xfactor = 1; + + if (Char == 0xFF) /* skip doubleheight chars in lower line */ + { + PosX += curfontwidth; + return; + } + + /* get colors */ + if (Attribute->inverted) + { + int t = Attribute->fg; + Attribute->fg = Attribute->bg; + Attribute->bg = t; + } + fgcolor = Attribute->fg; + if (transpmode == 1 && PosY < StartY + 24*fontheight) + { + if (fgcolor == transp) /* outside boxed elements (subtitles, news) completely transparent */ + bgcolor = transp; + else + bgcolor = transp2; + } + else + bgcolor = Attribute->bg; + + /* handle mosaic */ + if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) && + ((Char&0xA0) == 0x20)) + { + int w1 = (curfontwidth / 2 ) *xfactor; + int w2 = (curfontwidth - w1) *xfactor; + int y; + + Char = (Char & 0x1f) | ((Char & 0x40) >> 1); + if (Attribute->charset == C_G1S) /* separated mosaic */ + for (y = 0; y < 3; y++) + { + FillRectMosaicSeparated(PosX, PosY + yoffset + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01); + FillRectMosaicSeparated(PosX + w1, PosY + yoffset + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02); + Char >>= 2; + } + else + for (y = 0; y < 3; y++) + { + FillRect(PosX, PosY + yoffset + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor); + FillRect(PosX + w1, PosY + yoffset + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor); + Char >>= 2; + } + + PosX += curfontwidth; + return; + } + + if (Attribute->charset == C_G3) + { + if (Char < 0x20 || Char > 0x7d) + { + Char = 0x20; + } + else + { + if (*aShapes[Char - 0x20] == S_CHR) + { + unsigned char *p = aShapes[Char - 0x20]; + Char = (*(p+1) <<8) + (*(p+2)); + } + else if (*aShapes[Char - 0x20] == S_ADT) + { + int x,y,f,c; + unsigned char* p = lfb + PosX*4 + (PosY+yoffset)* fix_screeninfo.line_length; + for (y=0; ycharset >= C_OFFSET_DRCS) + { + + tstCachedPage *pcache = tuxtxt_cache.astCachetable[(Attribute->charset & 0x10) ? drcs : gdrcs][Attribute->charset & 0x0f]; + if (pcache) + { + unsigned char drcs_data[23*40]; + tuxtxt_decompress_page((Attribute->charset & 0x10) ? drcs : gdrcs,Attribute->charset & 0x0f,drcs_data); + unsigned char *p; + if (Char < 23*2) + p = drcs_data + 20*Char; + else if (pcache->pageinfo.p24) + p = pcache->pageinfo.p24 + 20*(Char - 23*2); + else + { + FillRect(PosX, PosY + yoffset, curfontwidth, factor*fontheight, bgcolor); + PosX += curfontwidth; + return; + } + axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */ + RenderDRCS(p, + lfb + PosX*4 + (yoffset + PosY) * fix_screeninfo.line_length, + axdrcs, fgcolor, bgcolor); + } + else + FillRect(PosX, PosY + yoffset, curfontwidth, factor*fontheight, bgcolor); + PosX += curfontwidth; + return; + } + else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F) + { + if (national_subset_local == NAT_GR) + Char = G2table[2][Char-0x20]; + else if (national_subset_local == NAT_RU) + Char = G2table[1][Char-0x20]; + else + Char = G2table[0][Char-0x20]; + + if (Char == 0x7F) + { + FillRect(PosX, PosY + yoffset, curfontwidth, factor*ascender, fgcolor); + FillRect(PosX, PosY + yoffset + factor*ascender, curfontwidth, factor*(fontheight-ascender), bgcolor); + PosX += curfontwidth; + return; + } + } + else if (national_subset_local == NAT_GR && Char >= 0x40 && Char <= 0x7E) /* remap complete areas for greek */ + Char += 0x390 - 0x40; + else if (national_subset_local == NAT_GR && Char == 0x3c) + Char = '«'; + else if (national_subset_local == NAT_GR && Char == 0x3e) + Char = '»'; + else if (national_subset_local == NAT_GR && Char >= 0x23 && Char <= 0x24) + Char = nationaltable23[NAT_DE][Char-0x23]; /* #$ as in german option */ + else if (national_subset_local == NAT_RU && Char >= 0x40 && Char <= 0x7E) /* remap complete areas for cyrillic */ + Char = G0tablecyrillic[Char-0x20]; + else + { + /* load char */ + switch (Char) + { + case 0x00: + case 0x20: + FillRect(PosX, PosY + yoffset, curfontwidth, factor*fontheight, bgcolor); + PosX += curfontwidth; + return; + case 0x23: + case 0x24: + Char = nationaltable23[national_subset_local][Char-0x23]; + break; + case 0x40: + Char = nationaltable40[national_subset_local]; + break; + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + Char = nationaltable5b[national_subset_local][Char-0x5B]; + break; + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + Char = nationaltable7b[national_subset_local][Char-0x7B]; + break; + case 0x7F: + FillRect(PosX, PosY + yoffset, curfontwidth, factor*ascender, fgcolor); + FillRect(PosX, PosY + yoffset + factor*ascender, curfontwidth, factor*(fontheight-ascender), bgcolor); + PosX += curfontwidth; + return; + case 0xE0: /* |- */ + DrawHLine(PosX, PosY + yoffset, curfontwidth, fgcolor); + DrawVLine(PosX, PosY + yoffset +1, fontheight -1, fgcolor); + FillRect(PosX +1, PosY + yoffset +1, curfontwidth-1, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE1: /* - */ + DrawHLine(PosX, PosY + yoffset, curfontwidth, fgcolor); + FillRect(PosX, PosY + yoffset +1, curfontwidth, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE2: /* -| */ + DrawHLine(PosX, PosY + yoffset, curfontwidth, fgcolor); + DrawVLine(PosX + curfontwidth -1, PosY + yoffset +1, fontheight -1, fgcolor); + FillRect(PosX, PosY + yoffset +1, curfontwidth-1, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE3: /* | */ + DrawVLine(PosX, PosY + yoffset, fontheight, fgcolor); + FillRect(PosX +1, PosY + yoffset, curfontwidth -1, fontheight, bgcolor); + PosX += curfontwidth; + return; + case 0xE4: /* | */ + DrawVLine(PosX + curfontwidth -1, PosY + yoffset, fontheight, fgcolor); + FillRect(PosX, PosY + yoffset, curfontwidth -1, fontheight, bgcolor); + PosX += curfontwidth; + return; + case 0xE5: /* |_ */ + DrawHLine(PosX, PosY + yoffset + fontheight -1, curfontwidth, fgcolor); + DrawVLine(PosX, PosY + yoffset, fontheight -1, fgcolor); + FillRect(PosX +1, PosY + yoffset, curfontwidth-1, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE6: /* _ */ + DrawHLine(PosX, PosY + yoffset + fontheight -1, curfontwidth, fgcolor); + FillRect(PosX, PosY + yoffset, curfontwidth, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE7: /* _| */ + DrawHLine(PosX, PosY + yoffset + fontheight -1, curfontwidth, fgcolor); + DrawVLine(PosX + curfontwidth -1, PosY + yoffset, fontheight -1, fgcolor); + FillRect(PosX, PosY + yoffset, curfontwidth-1, fontheight-1, bgcolor); + PosX += curfontwidth; + return; + case 0xE8: /* Ii */ + FillRect(PosX +1, PosY + yoffset, curfontwidth -1, fontheight, bgcolor); + for (Row=0; Row < curfontwidth/2; Row++) + DrawVLine(PosX + Row, PosY + yoffset + Row, fontheight - Row, fgcolor); + PosX += curfontwidth; + return; + case 0xE9: /* II */ + FillRect(PosX, PosY + yoffset, curfontwidth/2, fontheight, fgcolor); + FillRect(PosX + curfontwidth/2, PosY + yoffset, (curfontwidth+1)/2, fontheight, bgcolor); + PosX += curfontwidth; + return; + case 0xEA: /* ° */ + FillRect(PosX, PosY + yoffset, curfontwidth, fontheight, bgcolor); + FillRect(PosX, PosY + yoffset, curfontwidth/2, curfontwidth/2, fgcolor); + PosX += curfontwidth; + return; + case 0xEB: /* ¬ */ + FillRect(PosX, PosY + yoffset +1, curfontwidth, fontheight -1, bgcolor); + for (Row=0; Row < curfontwidth/2; Row++) + DrawHLine(PosX + Row, PosY + yoffset + Row, curfontwidth - Row, fgcolor); + PosX += curfontwidth; + return; + case 0xEC: /* -- */ + FillRect(PosX, PosY + yoffset, curfontwidth, curfontwidth/2, fgcolor); + FillRect(PosX, PosY + yoffset + curfontwidth/2, curfontwidth, fontheight - curfontwidth/2, bgcolor); + PosX += curfontwidth; + return; + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + Char = arrowtable[Char - 0xED]; + break; + default: + break; + } + } + if (Char <= 0x20) + { +#if 0//TUXTXT_DEBUG + printf("TuxTxt found control char: %x \"%c\" \n", Char, Char); +#endif + FillRect(PosX, PosY + yoffset, curfontwidth, factor*fontheight, bgcolor); + PosX += curfontwidth; + return; + } + + + if (!(glyph = FT_Get_Char_Index(face, Char))) + { +#if 0// TUXTXT_DEBUG + printf("TuxTxt c%x a%x g%x w%d h%d x%d y%d\n", + error, Char, (int) Attribute, glyph, curfontwidth, fontheight, PosX, PosY); +#endif + FillRect(PosX, PosY + yoffset, curfontwidth, fontheight, bgcolor); + PosX += curfontwidth; + return; + } + + /* render char */ + sbitbuffer = sbit->buffer; + char localbuffer[1000]; // should be enough to store one character-bitmap... + // add diacritical marks + if (Attribute->diacrit) + { + FTC_SBit sbit_diacrit; + + if (national_subset_local == NAT_GR) + Char = G2table[2][0x20+ Attribute->diacrit]; + else if (national_subset_local == NAT_RU) + Char = G2table[1][0x20+ Attribute->diacrit]; + else + Char = G2table[0][0x20+ Attribute->diacrit]; + if ((glyph = FT_Get_Char_Index(face, Char))) + { + if ((error = FTC_SBitCache_Lookup(cache, &typettf, glyph, &sbit_diacrit, NULL)) == 0) + + { + sbitbuffer = (unsigned char*) localbuffer; + memcpy(sbitbuffer,sbit->buffer,sbit->pitch*sbit->height); + + for (Row = 0; Row < sbit->height; Row++) + { + for (Pitch = 0; Pitch < sbit->pitch; Pitch++) + { + if (sbit_diacrit->pitch > Pitch && sbit_diacrit->height > Row) + sbitbuffer[Row*sbit->pitch+Pitch] |= sbit_diacrit->buffer[Row*sbit->pitch+Pitch]; + } + } + } + } + } + + unsigned char *p; + int f; /* running counter for zoom factor */ + + Row = factor * (ascender - sbit->top + TTFShiftY); + FillRect(PosX, PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */ + + if (ascender - sbit->top + TTFShiftY + sbit->height > fontheight) + sbit->height = fontheight - ascender + sbit->top - TTFShiftY; /* limit char height to defined/calculated fontheight */ + + + p = lfb + PosX*4 + (yoffset + PosY + Row) * fix_screeninfo.line_length; /* running pointer into framebuffer */ + for (Row = sbit->height; Row; Row--) /* row counts up, but down may be a little faster :) */ + { + int pixtodo = (usettf ? sbit->width : curfontwidth); + char *pstart = (char*) p; + + for (Bit = xfactor * (sbit->left + TTFShiftX); Bit > 0; Bit--) /* fill left margin */ + { + for (f = factor-1; f >= 0; f--) + memcpy((p + f*fix_screeninfo.line_length),bgra[bgcolor],4);/*bgcolor*/ + p+=4; + if (!usettf) + pixtodo--; + } + + for (Pitch = sbit->pitch; Pitch; Pitch--) + { + for (Bit = 0x80; Bit; Bit >>= 1) + { + int color; + + if (--pixtodo < 0) + break; + + if (*sbitbuffer & Bit) /* bit set -> foreground */ + color = fgcolor; + else /* bit not set -> background */ + color = bgcolor; + + for (f = factor-1; f >= 0; f--) + memcpy((p + f*fix_screeninfo.line_length),bgra[color],4); + p+=4; + + if (xfactor > 1) /* double width */ + { + for (f = factor-1; f >= 0; f--) + memcpy((p + f*fix_screeninfo.line_length),bgra[color],4); + p+=4; + + if (!usettf) + pixtodo--; + } + } + sbitbuffer++; + } + for (Bit = (usettf ? (curfontwidth - xfactor*(sbit->width + sbit->left + TTFShiftX)) : pixtodo); + Bit > 0; Bit--) /* fill rest of char width */ + { + for (f = factor-1; f >= 0; f--) + memcpy((p + f*fix_screeninfo.line_length),bgra[bgcolor],4); + p+=4; + } + + p = (unsigned char*) pstart + factor*fix_screeninfo.line_length; + } + + Row = ascender - sbit->top + sbit->height + TTFShiftY; + FillRect(PosX, PosY + yoffset + Row*factor, curfontwidth, (fontheight - Row) * factor, bgcolor); /* fill lower margin */ + if (Attribute->underline) + FillRect(PosX, PosY + yoffset + (fontheight-2)* factor, curfontwidth,2*factor, fgcolor); /* underline char */ + + PosX += curfontwidth; +} + +/****************************************************************************** + * RenderCharFB * + ******************************************************************************/ + +void RenderCharFB(int Char, tstPageAttr *Attribute) +{ + RenderChar(Char, Attribute, zoommode, var_screeninfo.yoffset); +} + +/****************************************************************************** + * RenderCharBB * + ******************************************************************************/ + +void RenderCharBB(int Char, tstPageAttr *Attribute) +{ + RenderChar(Char, Attribute, 0, var_screeninfo.yres-var_screeninfo.yoffset); +} + +/****************************************************************************** + * RenderCharLCD * + ******************************************************************************/ + +void RenderCharLCD(int Digit, int XPos, int YPos) +{ +#if 0 + int x, y; + + /* render digit to lcd backbuffer */ + for (y = 0; y < 15; y++) + { + for (x = 0; x < 10; x++) + { + if (lcd_digits[Digit*15*10 + x + y*10]) + lcd_backbuffer[XPos + x + ((YPos+y)/8)*120] |= 1 << ((YPos+y)%8); + else + lcd_backbuffer[XPos + x + ((YPos+y)/8)*120] &= ~(1 << ((YPos+y)%8)); + } + } +#endif +} + +#if 0 +void RenderCharLCDsmall(int Char, int XPos, int YPos) +{ + int old_width = fontwidth; + int old_height = fontheight; + setfontwidth(fontwidth_small_lcd); + typettf.font.pix_height = fontheight = fontwidth_small_lcd; + RenderChar(Char, 0, 0, -(YPos<<8 | XPos)); + setfontwidth(old_width); + typettf.font.pix_height = fontheight = old_height; +} +#endif + +/****************************************************************************** + * RenderMessage * + ******************************************************************************/ + +void RenderMessage(int Message) +{ + int byte; + int fbcolor, timecolor, menuatr; + int pagecolumn; + const char *msg; + + +/* 00000000001111111111222222222233333333334 */ +/* 01234567890123456789012345678901234567890 */ + char message_1[] = "àááááááá www.tuxtxt.com x.xx áááááááâè"; + char message_2[] = "ã äé"; +/* char message_3[] = "ã suche nach Teletext-Anbietern äé"; */ + char message_4[] = "ã äé"; + char message_5[] = "åæææææææææææææææææææææææææææææææææææçé"; + char message_6[] = "ëììììììììììììììììììììììììììììììììììììê"; + +/* char message_7[] = "ã kein Teletext auf dem Transponder äé"; */ +/* char message_8[] = "ã warte auf Empfang von Seite 100 äé"; */ +/* char message_9[] = "ã Seite 100 existiert nicht! äé"; */ + + memcpy(&message_1[24], versioninfo, 4); + /* reset zoom */ + zoommode = 0; + + /* set colors */ + fbcolor = transp; + timecolor = transp<<4 | transp; + menuatr = ATR_MSG0; + + /* clear framebuffer */ + ClearFB(fbcolor); + + /* hide header */ + page_atrb[32].fg = transp; + page_atrb[32].bg = transp; + + + /* set pagenumber */ + if (Message == ShowServiceName) + { + pagecolumn = message8pagecolumn[menulanguage]; + msg = message_8[menulanguage]; + memcpy(&message_4, msg, sizeof(message_4)); + hex2str(message_4+pagecolumn, tuxtxt_cache.page); + + if (SDT_ready) + memcpy(&message_2[2 + (35 - pid_table[current_service].service_name_len)/2], + &pid_table[current_service].service_name, pid_table[current_service].service_name_len); + else if (Message == ShowServiceName) + hex2str(&message_2[17+3], tuxtxt_cache.vtxtpid); + + msg = &message_3_blank[0]; + } + else if (Message == NoServicesFound) + msg = &message_7[menulanguage][0]; + else + msg = &message_3[menulanguage][0]; + + /* render infobar */ + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*16; + for (byte = 0; byte < 37; byte++) + RenderCharFB(message_1[byte], &atrtable[menuatr + ((byte >= 9 && byte <= 27) ? 1 : 0)]); + RenderCharFB(message_1[37], &atrtable[menuatr + 2]); + + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*17; + RenderCharFB(message_2[0], &atrtable[menuatr + 0]); + for (byte = 1; byte < 36; byte++) + RenderCharFB(message_2[byte], &atrtable[menuatr + 3]); + RenderCharFB(message_2[36], &atrtable[menuatr + 0]); + RenderCharFB(message_2[37], &atrtable[menuatr + 2]); + + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*18; + RenderCharFB(msg[0], &atrtable[menuatr + 0]); + for (byte = 1; byte < 36; byte++) + RenderCharFB(msg[byte], &atrtable[menuatr + 3]); + RenderCharFB(msg[36], &atrtable[menuatr + 0]); + RenderCharFB(msg[37], &atrtable[menuatr + 2]); + + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*19; + RenderCharFB(message_4[0], &atrtable[menuatr + 0]); + for (byte = 1; byte < 36; byte++) + RenderCharFB(message_4[byte], &atrtable[menuatr + 3]); + RenderCharFB(message_4[36], &atrtable[menuatr + 0]); + RenderCharFB(message_4[37], &atrtable[menuatr + 2]); + + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*20; + for (byte = 0; byte < 37; byte++) + RenderCharFB(message_5[byte], &atrtable[menuatr + 0]); + RenderCharFB(message_5[37], &atrtable[menuatr + 2]); + + PosX = StartX + fontwidth+5; + PosY = StartY + fontheight*21; + for (byte = 0; byte < 38; byte++) + RenderCharFB(message_6[byte], &atrtable[menuatr + 2]); +} + +/****************************************************************************** + * RenderPage * + ******************************************************************************/ + +void DoFlashing(int startrow) +{ + int row, col; + /* get national subset */ + if (auto_national && + national_subset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ + pageinfo && pageinfo->nationalvalid) /* individual subset according to page header */ + { + national_subset = countryconversiontable[pageinfo->national]; + } + /* Flashing */ + tstPageAttr flashattr; + char flashchar; + struct timeval tv; + gettimeofday(&tv,NULL); + long flashphase = (tv.tv_usec / 1000) % 1000; + int srow = startrow; + int erow = 24; + int factor=1; + switch (zoommode) + { + case 1: erow = 12; factor=2;break; + case 2: srow = 12; factor=2;break; + } + PosY = StartY + startrow*fontheight*factor; + for (row = srow; row < erow; row++) + { + int index = row * 40; + int dhset = 0; + int incflash = 3; + int decflash = 2; + PosX = StartX; + for (col = nofirst; col < 40; col++) + { + if (page_atrb[index + col].flashing && page_char[index + col] > 0x20 && page_char[index + col]!= 0xff ) + { + SetPosX(col); + flashchar = page_char[index + col]; + int doflash = 0; + memcpy(&flashattr,&page_atrb[index + col],sizeof(tstPageAttr)); + switch (flashattr.flashing &0x1c) // Flash Rate + { + case 0x00 : // 1 Hz + if (flashphase>500) doflash = 1; + break; + case 0x04 : // 2 Hz Phase 1 + if (flashphase<250) doflash = 1; + break; + case 0x08 : // 2 Hz Phase 2 + if (flashphase>=250 && flashphase<500) doflash = 1; + break; + case 0x0c : // 2 Hz Phase 3 + if (flashphase>=500 && flashphase<750) doflash = 1; + break; + case 0x10 : // incremental flash + incflash++; + if (incflash>3) incflash = 1; + switch (incflash) + { + case 1: if (flashphase<250) doflash = 1; break; + case 2: if (flashphase>=250 && flashphase<500) doflash = 1;break; + case 3: if (flashphase>=500 && flashphase<750) doflash = 1; + } + break; + case 0x11 : // decremental flash + decflash--; + if (decflash<1) decflash = 3; + switch (decflash) + { + case 1: if (flashphase<250) doflash = 1; break; + case 2: if (flashphase>=250 && flashphase<500) doflash = 1;break; + case 3: if (flashphase>=500 && flashphase<750) doflash = 1; + } + break; + + } + + switch (flashattr.flashing &0x03) // Flash Mode + { + case 0x01 : // normal Flashing + if (doflash) flashattr.fg = flashattr.bg; + break; + case 0x02 : // inverted Flashing + doflash = 1-doflash; + if (doflash) flashattr.fg = flashattr.bg; + break; + case 0x03 : // color Flashing + if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8); + break; + + } + RenderCharFB(flashchar,&flashattr); + if (flashattr.doublew) col++; + if (flashattr.doubleh) dhset = 1; + } + } + if (dhset) + { + row++; + PosY += fontheight*factor; + } + PosY += fontheight*factor; + } + +} +void RenderPage() +{ + int row, col, byte, startrow = 0;; + int national_subset_bak = national_subset; + + + /* update lcd */ + UpdateLCD(); + + if (transpmode != 2 && delaystarted) + { + struct timeval tv; + gettimeofday(&tv,NULL); + if (tv.tv_sec - tv_delay.tv_sec < subtitledelay) + return; + } + + + /* update page or timestring */ + if (transpmode != 2 && tuxtxt_cache.pageupdate && tuxtxt_cache.page_receiving != tuxtxt_cache.page && inputcounter == 2) + { + if (boxed && subtitledelay) + { + if (!delaystarted) + { + gettimeofday(&tv_delay,NULL); + delaystarted = 1; + return; + } + else + delaystarted = 0; + + } + + /* reset update flag */ + tuxtxt_cache.pageupdate = 0; + + + /* decode page */ + if (tuxtxt_cache.subpagetable[tuxtxt_cache.page] != 0xFF) + DecodePage(); + else + startrow = 1; + if (boxed) + { + if (screenmode != 0) + SwitchScreenMode(0); /* turn off divided screen */ + } + else + { + if (screenmode != prevscreenmode && !transpmode) + SwitchScreenMode(prevscreenmode); + } + + /* display first column? */ + nofirst = show39; + for (row = 1; row < 24; row++) + { + byte = page_char[row*40]; + if (byte != ' ' && byte != 0x00 && byte != 0xFF && + page_atrb[row*40].fg != page_atrb[row*40].bg) + { + nofirst = 0; + break; + } + } + fontwidth_normal = (ex-sx) / (40-nofirst); + setfontwidth(fontwidth_normal); + fontwidth_topmenumain = (TV43STARTX-sx) / (40-nofirst); + fontwidth_topmenusmall = (ex- TOPMENUSTARTX) / TOPMENUCHARS; + fontwidth_small = (TV169FULLSTARTX-sx) / (40-nofirst); + switch(screenmode) + { + case 0: setfontwidth(fontwidth_normal) ; displaywidth= (ex -sx);break; + case 1: setfontwidth(fontwidth_topmenumain); displaywidth= (TV43STARTX -sx);break; + case 2: setfontwidth(fontwidth_small) ; displaywidth= (TV169FULLSTARTX-sx);break; + } + if (transpmode || (boxed && !screenmode)) + { + FillBorder(transp);//ClearBB(transp); + clearbbcolor = transp; + } + + /* get national subset */ + if (auto_national && + national_subset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ + pageinfo && pageinfo->nationalvalid) /* individual subset according to page header */ + { + national_subset = countryconversiontable[pageinfo->national]; +#if TUXTXT_DEBUG + printf("p%03x b%d n%d v%d i%d\n", tuxtxt_cache.page,national_subset_bak, national_subset, pageinfo->nationalvalid, pageinfo->national); +#endif + } + /* render page */ + PosY = StartY + startrow*fontheight; + for (row = startrow; row < 24; row++) + { + int index = row * 40; + + PosX = StartX; + for (col = nofirst; col < 40; col++) + { + RenderCharBB(page_char[index + col], &page_atrb[index + col]); + + if (page_atrb[index + col].doubleh && page_char[index + col] != 0xff) /* disable lower char in case of doubleh setting in l25 objects */ + page_char[index + col + 40] = 0xff; + if (page_atrb[index + col].doublew) /* skip next column if double width */ + { + col++; + if (page_atrb[index + col-1].doubleh && page_char[index + col] != 0xff) /* disable lower char in case of doubleh setting in l25 objects */ + page_char[index + col + 40] = 0xff; + } + } + PosY += fontheight; + } + DoFlashing(startrow); + national_subset = national_subset_bak; + + /* update framebuffer */ + CopyBB2FB(); + } + else if (transpmode != 2) + { + if (zoommode != 2) + { + PosY = StartY; + if (tuxtxt_cache.subpagetable[tuxtxt_cache.page] == 0xff) + { + page_atrb[32].fg = yellow; + page_atrb[32].bg = menu1; + int showpage = tuxtxt_cache.page_receiving; + int showsubpage = tuxtxt_cache.subpagetable[showpage]; + if (showsubpage!=0xff) + { + + tstCachedPage *pCachedPage; + pCachedPage = tuxtxt_cache.astCachetable[showpage][showsubpage]; + if (pCachedPage && tuxtxt_is_dec(showpage)) + { + PosX = StartX; + if (inputcounter == 2) + { + if (tuxtxt_cache.bttok && !tuxtxt_cache.basictop[tuxtxt_cache.page]) /* page non-existent according to TOP (continue search anyway) */ + { + page_atrb[0].fg = white; + page_atrb[0].bg = red; + } + else + { + page_atrb[0].fg = yellow; + page_atrb[0].bg = menu1; + } + hex2str((char*) page_char+3, tuxtxt_cache.page); + for (col = nofirst; col < 7; col++) // selected page + { + RenderCharFB(page_char[col], &page_atrb[0]); + } + RenderCharFB(page_char[col], &page_atrb[32]); + } + else + SetPosX(8); + + memcpy(&page_char[8], pCachedPage->p0, 24); /* header line without timestring */ + for (col = 0; col < 24; col++) + { + RenderCharFB(pCachedPage->p0[col], &page_atrb[32]); + } + } + } + } + /* update timestring */ + SetPosX(32); + for (byte = 0; byte < 8; byte++) + { + if (!page_atrb[32+byte].flashing) + RenderCharFB(tuxtxt_cache.timestring[byte], &page_atrb[32]); + else + { + SetPosX(33+byte); + page_char[32+byte] = page_char[32+byte]; + } + + + } + } + DoFlashing(startrow); + national_subset = national_subset_bak; + } + else if (transpmode == 2 && tuxtxt_cache.pageupdate == 2) + { +#if TUXTXT_DEBUG + printf("received Update flag for page %03x\n",tuxtxt_cache.page); +#endif + // display pagenr. when page has been updated while in transparency mode + PosY = StartY; + + char ns[3]; + SetPosX(1); + hex2str(ns+2,tuxtxt_cache.page); + + RenderCharFB(ns[0],&atrtable[ATR_WB]); + RenderCharFB(ns[1],&atrtable[ATR_WB]); + RenderCharFB(ns[2],&atrtable[ATR_WB]); + + tuxtxt_cache.pageupdate=0; + } +} + +/****************************************************************************** + * CreateLine25 * + ******************************************************************************/ + +void showlink(int column, int linkpage) +{ + unsigned char *p, line[] = " >??? "; + int oldfontwidth = fontwidth; + int yoffset; + +#if 0 + if (var_screeninfo.yoffset) + yoffset = 0; + else + yoffset = var_screeninfo.yres; +#endif + yoffset = var_screeninfo.yres; //NEW + + int abx = ((displaywidth)%(40-nofirst) == 0 ? displaywidth+1 : (displaywidth)/(((displaywidth)%(40-nofirst)))+1);// distance between 'inserted' pixels + int width = displaywidth /4; + + PosY = StartY + 24*fontheight; + + if (boxed) + { + PosX = StartX + column*width; + FillRect(PosX, PosY+yoffset, displaywidth, fontheight, transp); + return; + } + + if (tuxtxt_cache.adip[linkpage][0]) + { + PosX = StartX + column*width; + int l = strlen((char*) tuxtxt_cache.adip[linkpage]); + + if (l > 9) /* smaller font, if no space for one half space at front and end */ + setfontwidth(oldfontwidth * 10 / (l+1)); + FillRect(PosX, PosY+yoffset, width+(displaywidth%4), fontheight, atrtable[ATR_L250 + column].bg); + PosX += ((width) - (l*fontwidth+l*fontwidth/abx))/2; /* center */ + for (p = tuxtxt_cache.adip[linkpage]; *p; p++) + RenderCharBB(*p, &atrtable[ATR_L250 + column]); + setfontwidth(oldfontwidth); + } + else /* display number */ + { + PosX = StartX + column*width; + FillRect(PosX, PosY+yoffset, displaywidth+sx-PosX, fontheight, atrtable[ATR_L250 + column].bg); + if (linkpage < tuxtxt_cache.page) + { + line[6] = '<'; + hex2str((char*) line + 5, linkpage); + } + else + hex2str((char*) line + 6, linkpage); + for (p = line; p < line+9; p++) + RenderCharBB(*p, &atrtable[ATR_L250 + column]); + } +} + +void CreateLine25() +{ + + if (!tuxtxt_cache.bttok) + /* btt completely received and not yet decoded */ + tuxtxt_decode_btt(); + if (tuxtxt_cache.maxadippg >= 0) + tuxtxt_decode_adip(); + + if (!showhex && showflof && + (tuxtxt_cache.flofpages[tuxtxt_cache.page][0] || tuxtxt_cache.flofpages[tuxtxt_cache.page][1] || tuxtxt_cache.flofpages[tuxtxt_cache.page][2] || tuxtxt_cache.flofpages[tuxtxt_cache.page][3])) // FLOF-Navigation present + { + int i; + + prev_100 = tuxtxt_cache.flofpages[tuxtxt_cache.page][0]; + prev_10 = tuxtxt_cache.flofpages[tuxtxt_cache.page][1]; + next_10 = tuxtxt_cache.flofpages[tuxtxt_cache.page][2]; + next_100 = tuxtxt_cache.flofpages[tuxtxt_cache.page][3]; + + PosY = StartY + 24*fontheight; + PosX = StartX; + for (i=nofirst; i<40; i++) + RenderCharBB(page_char[24*40 + i], &page_atrb[24*40 + i]); + } + else + { + /* normal: blk-1, grp+1, grp+2, blk+1 */ + /* hex: hex+1, blk-1, grp+1, blk+1 */ + if (showhex) + { + /* arguments: startpage, up, findgroup */ + prev_100 = tuxtxt_next_hex(tuxtxt_cache.page); + prev_10 = toptext_getnext(tuxtxt_cache.page, 0, 0); + next_10 = toptext_getnext(tuxtxt_cache.page, 1, 1); + } + else + { + prev_100 = toptext_getnext(tuxtxt_cache.page, 0, 0); + prev_10 = toptext_getnext(tuxtxt_cache.page, 1, 1); + next_10 = toptext_getnext(prev_10, 1, 1); + } + next_100 = toptext_getnext(next_10, 1, 0); + showlink(0, prev_100); + showlink(1, prev_10); + showlink(2, next_10); + showlink(3, next_100); + } + + if (//tuxtxt_cache.bttok && + screenmode == 1) /* TOP-Info present, divided screen -> create TOP overview */ + { + char line[TOPMENUCHARS]; + int current; + int prev10done, next10done, next100done, indent; + tstPageAttr *attrcol, *attr; /* color attribute for navigation keys and text */ + + int olddisplaywidth = displaywidth; + displaywidth = 1000*(40-nofirst); // disable pixelinsert; + setfontwidth(fontwidth_topmenusmall); + + PosY = TOPMENUSTARTY; + memset(line, ' ', TOPMENUCHARS); /* init with spaces */ + + memcpy(line+TOPMENUINDENTBLK, tuxtxt_cache.adip[prev_100], 12); + hex2str(&line[TOPMENUINDENTDEF+12+TOPMENUSPC+2], prev_100); + RenderClearMenuLineBB(line, &atrtable[ATR_L250], &atrtable[ATR_TOPMENU2]); + +/* 1: blk-1, grp-1, grp+1, blk+1 */ +/* 2: blk-1, grp+1, grp+2, blk+1 */ +#if (LINE25MODE == 1) + current = prev_10 - 1; +#else + current = tuxtxt_cache.page - 1; +#endif + + prev10done = next10done = next100done = 0; + while (PosY <= (TOPMENUENDY-fontheight)) + { + attr = 0; + attrcol = &atrtable[ATR_WB]; + if (!next100done && (PosY > (TOPMENUENDY - 2*fontheight))) /* last line */ + { + attrcol = &atrtable[ATR_L253]; + current = next_100; + } + else if (!next10done && (PosY > (TOPMENUENDY - 3*fontheight))) /* line before */ + { + attrcol = &atrtable[ATR_L252]; + current = next_10; + } + else if (!prev10done && (PosY > (TOPMENUENDY - 4*fontheight))) /* line before */ + { + attrcol = &atrtable[ATR_L251]; + current = prev_10; + } + else do + { + tuxtxt_next_dec(¤t); + if (current == prev_10) + { + attrcol = &atrtable[ATR_L251]; + prev10done = 1; + break; + } + else if (current == next_10) + { + attrcol = &atrtable[ATR_L252]; + next10done = 1; + break; + } + else if (current == next_100) + { + attrcol = &atrtable[ATR_L253]; + next100done = 1; + break; + } + else if (current == tuxtxt_cache.page) + { + attr = &atrtable[ATR_TOPMENU0]; + break; + } + } while (tuxtxt_cache.adip[current][0] == 0 && (tuxtxt_cache.basictop[current] < 2 || tuxtxt_cache.basictop[current] > 7)); + + if (!tuxtxt_cache.bttok || (tuxtxt_cache.basictop[current] >= 2 && tuxtxt_cache.basictop[current] <= 5)) /* block (also for FLOF) */ + { + indent = TOPMENUINDENTBLK; + if (!attr) + attr = &atrtable[tuxtxt_cache.basictop[current] <=3 ? ATR_TOPMENU1 : ATR_TOPMENU2]; /* green for program block */ + } + else if (tuxtxt_cache.basictop[current] >= 6 && tuxtxt_cache.basictop[current] <= 7) /* group */ + { + indent = TOPMENUINDENTGRP; + if (!attr) + attr = &atrtable[ATR_TOPMENU3]; + } + else + { + indent = TOPMENUINDENTDEF; + if (!attr) + attr = &atrtable[ATR_WB]; + } + memcpy(line+indent, tuxtxt_cache.adip[current], 12); + hex2str(&line[TOPMENUINDENTDEF+12+TOPMENUSPC+2], current); + RenderClearMenuLineBB(line, attrcol, attr); + } + displaywidth = olddisplaywidth; + setfontwidth(fontwidth_topmenumain); + } +} + +/****************************************************************************** + * CopyBB2FB * + ******************************************************************************/ + +void CopyBB2FB() +{ + unsigned char *src, *dst, *topsrc; + int fillcolor, i, screenwidth, swtmp; + +//printf("[tuxtxt] CopyBB2FB: zoommode %d\n", zoommode); + + /* line 25 */ + if (!pagecatching) + CreateLine25(); + /* copy backbuffer to framebuffer */ + if (!zoommode) + { + memcpy(lfb, lfb+fix_screeninfo.line_length * var_screeninfo.yres, fix_screeninfo.line_length*var_screeninfo.yres); +#if 0 + /* if yoffset != 0, we had active page 1, and activate 0 */ + /* else active was page 0, activate page 1 */ + if (var_screeninfo.yoffset) + var_screeninfo.yoffset = 0; + else + var_screeninfo.yoffset = var_screeninfo.yres; + + if (ioctl(fb, FBIOPAN_DISPLAY, &var_screeninfo) == -1) + perror("TuxTxt "); +#endif + + /* adapt background of backbuffer if changed */ + if (StartX > 0 && *lfb != *(lfb + fix_screeninfo.line_length * var_screeninfo.yres)) + FillBorder(*(lfb + fix_screeninfo.line_length * var_screeninfo.yoffset)); +// ClearBB(*(lfb + var_screeninfo.xres * var_screeninfo.yoffset)); + + if (clearbbcolor >= 0) + { +// ClearBB(clearbbcolor); + clearbbcolor = -1; + } + return; + } + + src = dst = topsrc = lfb + StartY*fix_screeninfo.line_length; + + if (var_screeninfo.yoffset) + dst += fix_screeninfo.line_length * var_screeninfo.yres; + else + { + src += fix_screeninfo.line_length * var_screeninfo.yres; + topsrc += fix_screeninfo.line_length * var_screeninfo.yres; + } + /* copy line25 in normal height */ + if (!pagecatching ) + memcpy(dst+(24*fontheight)*fix_screeninfo.line_length, src + (24*fontheight)*fix_screeninfo.line_length, fix_screeninfo.line_length*fontheight); + + if (transpmode) + fillcolor = transp; + else + fillcolor = FullScrColor; + + if (zoommode == 2) + src += 12*fontheight*fix_screeninfo.line_length; + + /* copy topmenu in normal height (since PIG also keeps dimensions) */ + if (screenmode == 1) + { + unsigned char *topdst = dst; + + screenwidth = ( TV43STARTX ) * 4; + + topsrc += screenwidth; + topdst += screenwidth; + for (i=0; i < 24*fontheight; i++) + { + memcpy(topdst, topsrc,ex-screenwidth); + topdst += fix_screeninfo.line_length; + topsrc += fix_screeninfo.line_length; + } + } + else if (screenmode == 2) + screenwidth = ( TV169FULLSTARTX ) * 4; + else + screenwidth = fix_screeninfo.line_length;//var_screeninfo.xres; + + for (i = StartY; i>0;i--) + { + for (swtmp=0; swtmp<=screenwidth; swtmp++) + { + memcpy(dst - i*fix_screeninfo.line_length+swtmp*4, bgra[fillcolor], 4); + } + } + + for (i = 12*fontheight; i; i--) + { + memcpy(dst, src, screenwidth); + dst += fix_screeninfo.line_length; + memcpy(dst, src, screenwidth); + dst += fix_screeninfo.line_length; + src += fix_screeninfo.line_length; + } + + for (i = var_screeninfo.yres - StartY - 25*fontheight; i >= 0;i--) + { + for (swtmp=0; swtmp<= screenwidth;swtmp++) + { + memcpy(dst + fix_screeninfo.line_length*(fontheight+i)+swtmp*4, bgra[fillcolor], 4); + } + } +} + +/****************************************************************************** + * UpdateLCD * + ******************************************************************************/ + +void UpdateLCD() +{ +#if 0 + static int init_lcd = 1, old_cached_pages = -1, old_page = -1, old_subpage = -1, old_subpage_max = -1, old_hintmode = -1; + int x, y, subpage_max = 0, update_lcd = 0; + + if (lcd == -1) return; // for Dreamboxes without LCD-Display (5xxx) + /* init or update lcd */ + if (init_lcd) + { + init_lcd = 0; + + for (y = 0; y < 64; y++) + { + int lcdbase = (y/8)*120; + int lcdmask = 1 << (y%8); + + for (x = 0; x < 120; ) + { + int rommask; + int rombyte = lcd_layout[x/8 + y*120/8]; + + for (rommask = 0x80; rommask; rommask >>= 1) + { + if (rombyte & rommask) + lcd_backbuffer[x + lcdbase] |= lcdmask; + else + lcd_backbuffer[x + lcdbase] &= ~lcdmask; + x++; + } + } + } + + write(lcd, &lcd_backbuffer, sizeof(lcd_backbuffer)); + + for (y = 16; y < 56; y += 8) /* clear rectangle in backbuffer */ + for (x = 1; x < 118; x++) + lcd_backbuffer[x + (y/8)*120] = 0; + + for (x = 3; x <= 116; x++) + lcd_backbuffer[x + (39/8)*120] |= 1 << (39%8); + + for (y = 42; y <= 60; y++) + lcd_backbuffer[35 + (y/8)*120] |= 1 << (y%8); + + for (y = 42; y <= 60; y++) + lcd_backbuffer[60 + (y/8)*120] |= 1 << (y%8); + + RenderCharLCD(10, 43, 20); + RenderCharLCD(11, 79, 20); + + return; + } + else + { + int p; + + if (inputcounter == 2) + p = tuxtxt_cache.page; + else + p = temp_page + (0xDD >> 4*(1-inputcounter)); /* partial pageinput (filled with spaces) */ + + /* page */ + if (old_page != p) + { + RenderCharLCD(p>>8, 7, 20); + RenderCharLCD((p&0x0F0)>>4, 19, 20); + RenderCharLCD(p&0x00F, 31, 20); + + old_page = p; + update_lcd = 1; + } + + /* current subpage */ + if (old_subpage != tuxtxt_cache.subpage) + { + if (!tuxtxt_cache.subpage) + { + RenderCharLCD(0, 55, 20); + RenderCharLCD(1, 67, 20); + } + else + { + if (tuxtxt_cache.subpage >= 0xFF) + tuxtxt_cache.subpage = 1; + else if (tuxtxt_cache.subpage > 99) + tuxtxt_cache.subpage = 0; + + RenderCharLCD(tuxtxt_cache.subpage>>4, 55, 20); + RenderCharLCD(tuxtxt_cache.subpage&0x0F, 67, 20); + } + + old_subpage = tuxtxt_cache.subpage; + update_lcd = 1; + } + + /* max subpage */ + for (x = 0; x <= 0x79; x++) + { + if (tuxtxt_cache.astCachetable[tuxtxt_cache.page][x]) + subpage_max = x; + } + + if (old_subpage_max != subpage_max) + { + if (!subpage_max) + { + RenderCharLCD(0, 91, 20); + RenderCharLCD(1, 103, 20); + } + else + { + RenderCharLCD(subpage_max>>4, 91, 20); + RenderCharLCD(subpage_max&0x0F, 103, 20); + } + + old_subpage_max = subpage_max; + update_lcd = 1; + } + + /* cachestatus */ + if (old_cached_pages != tuxtxt_cache.cached_pages) + { + #if 0 + int s; + int p = tuxtxt_cache.cached_pages; + for (s=107; s >= 107-4*fontwidth_small_lcd; s -= fontwidth_small_lcd) + { + int c = p % 10; + if (p) + RenderCharLCDsmall('0'+c, s, 44); + else + RenderCharLCDsmall(' ', s, 44); + p /= 10; + } + #else + RenderCharLCD(tuxtxt_cache.cached_pages/1000, 67, 44); + RenderCharLCD(tuxtxt_cache.cached_pages%1000/100, 79, 44); + RenderCharLCD(tuxtxt_cache.cached_pages%100/10, 91, 44); + RenderCharLCD(tuxtxt_cache.cached_pages%10, 103, 44); + #endif + + old_cached_pages = tuxtxt_cache.cached_pages; + update_lcd = 1; + } + + /* mode */ + if (old_hintmode != hintmode) + { + if (hintmode) + RenderCharLCD(12, 43, 44); + else + RenderCharLCD(13, 43, 44); + + old_hintmode = hintmode; + update_lcd = 1; + } + } + + if (update_lcd) + write(lcd, &lcd_backbuffer, sizeof(lcd_backbuffer)); +#endif +} + +/****************************************************************************** + * DecodePage * + ******************************************************************************/ + +void DecodePage() +{ + int row, col; + int hold, dhset; + int foreground, background, doubleheight, doublewidth, charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin; + unsigned char held_mosaic, *p; + tstCachedPage *pCachedPage; + + /* copy page to decode buffer */ + if (tuxtxt_cache.subpagetable[tuxtxt_cache.page] == 0xff) /* not cached: do nothing */ + return; + if (tuxtxt_cache.zap_subpage_manual) + pCachedPage = tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpage]; + else + pCachedPage = tuxtxt_cache.astCachetable[tuxtxt_cache.page][tuxtxt_cache.subpagetable[tuxtxt_cache.page]]; + if (!pCachedPage) /* not cached: do nothing */ + return; + + tuxtxt_decompress_page(tuxtxt_cache.page,tuxtxt_cache.subpage,&page_char[40]); + + memcpy(&page_char[8], pCachedPage->p0, 24); /* header line without timestring */ + + pageinfo = &(pCachedPage->pageinfo); + if (pageinfo->p24) + memcpy(&page_char[24*40], pageinfo->p24, 40); /* line 25 for FLOF */ + + /* copy timestring */ + memcpy(&page_char[32], &tuxtxt_cache.timestring, 8); + + /* check for newsflash & subtitle */ + if (pageinfo->boxed && tuxtxt_is_dec(tuxtxt_cache.page)) + boxed = 1; + else + boxed = 0; + + + /* modify header */ + if (boxed) + memset(&page_char, ' ', 40); + else + { + memset(page_char, ' ', 8); + hex2str((char*) page_char+3, tuxtxt_cache.page); + if (tuxtxt_cache.subpage) + { + *(page_char+4) ='/'; + *(page_char+5) ='0'; + hex2str((char*) page_char+6, tuxtxt_cache.subpage); + } + + } + + if (!tuxtxt_is_dec(tuxtxt_cache.page)) + { + if (pageinfo->function == FUNC_MOT) /* magazine organization table */ + { +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage, pageinfo->function); +#endif + ClearBB(black); + for (col = 0; col < 24*40; col++) + page_atrb[col] = atrtable[ATR_WB]; + for (col = 40; col < 24*40; col++) + page_char[col] = number2char(page_char[col]); + boxed = 0; + return ; /* don't interpret irregular pages */ + } + else if (pageinfo->function == FUNC_GPOP || pageinfo->function == FUNC_POP) /* object definitions */ + { +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage, pageinfo->function); +#endif + ClearBB(black); + for (col = 0; col < 24*40; col++) + page_atrb[col] = atrtable[ATR_WB]; + p = page_char + 40; + for (row = 1; row < 12; row++) + { + *p++ = number2char(row); /* first column: number (0-9, A-..) */ + for (col = 1; col < 40; col += 3) + { + int d = deh24(p); + if (d < 0) + { + memcpy(p, "???", 3); + p += 3; + } + else + { + *p++ = number2char((d >> 6) & 0x1f); /* mode */ + *p++ = number2char(d & 0x3f); /* address */ + *p++ = number2char((d >> 11) & 0x7f); /* data */ + } + } + } + boxed = 0; + return ; /* don't interpret irregular pages */ + } + else if (pageinfo->function == FUNC_GDRCS || pageinfo->function == FUNC_DRCS) /* character definitions */ + { + #define DRCSROWS 8 + #define DRCSCOLS (48/DRCSROWS) + #define DRCSZOOMX 3 + #define DRCSZOOMY 5 + #define DRCSXSPC (12*DRCSZOOMX + 2) + #define DRCSYSPC (10*DRCSZOOMY + 2) + + unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ + DRCSZOOMX * 0, + DRCSZOOMX * 1, + DRCSZOOMX * 2, + DRCSZOOMX * 3, + DRCSZOOMX * 4, + DRCSZOOMX * 5, + DRCSZOOMX * 6, + DRCSZOOMX * 7, + DRCSZOOMX * 8, + DRCSZOOMX * 9, + DRCSZOOMX * 10, + DRCSZOOMX * 11, + DRCSZOOMX * 12, + DRCSZOOMY * 0, + DRCSZOOMY * 1, + DRCSZOOMY * 2, + DRCSZOOMY * 3, + DRCSZOOMY * 4, + DRCSZOOMY * 5, + DRCSZOOMY * 6, + DRCSZOOMY * 7, + DRCSZOOMY * 8, + DRCSZOOMY * 9, + DRCSZOOMY * 10 + }; +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page, tuxtxt_cache.subpage, pageinfo->function); +#endif + ClearBB(black); + for (col = 0; col < 24*40; col++) + page_atrb[col] = atrtable[ATR_WB]; + + for (row = 0; row < DRCSROWS; row++) + for (col = 0; col < DRCSCOLS; col++) + RenderDRCS( + page_char + 20 * (DRCSCOLS * row + col + 2), + lfb + + (StartY + fontheight + DRCSYSPC * row + var_screeninfo.yres - var_screeninfo.yoffset) * fix_screeninfo.line_length + + (StartX + DRCSXSPC * col)*4, + ax, white, black); + + memset(page_char + 40, 0xff, 24*40); /* don't render any char below row 0 */ + boxed = 0; + return ; /* don't interpret irregular pages */ + } + else + { + int i; + int h, parityerror = 0; + + for (i = 0; i < 8; i++) + page_atrb[i] = atrtable[ATR_WB]; + + /* decode parity/hamming */ + for (i = 40; i < (int) sizeof(page_char); i++) + { + page_atrb[i] = atrtable[ATR_WB]; + p = page_char + i; + h = dehamming[*p]; + if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */ + hex2str((char *) p, h); /* first try dehamming */ + else + { + if (*p == ' ' || deparity[*p] != ' ') /* correct parity */ + *p &= 127; + else + { + parityerror = 1; + if (h != 0xFF) /* first parity error: try dehamming */ + hex2str((char *) p, h); + else + *p = ' '; + } + } + } + if (parityerror) + { + boxed = 0; + return ; /* don't interpret irregular pages */ + } + } + } + + /* decode */ + for (row = 0; + row < ((showflof && pageinfo->p24) ? 25 : 24); + row++) + { + /* start-of-row default conditions */ + foreground = white; + background = black; + doubleheight = 0; + doublewidth = 0; + charset = C_G0P; + mosaictype = 0; + concealed = 0; + flashmode = 0; + hold = 0; + boxwin = 0; + held_mosaic = ' '; + dhset = 0; + IgnoreAtBlackBgSubst = 0; + + if (boxed && memchr(&page_char[row*40], start_box, 40) == 0) + { + foreground = transp; + background = transp; + } + + for (col = 0; col < 40; col++) + { + int index = row*40 + col; + + page_atrb[index].fg = foreground; + page_atrb[index].bg = background; + page_atrb[index].charset = charset; + page_atrb[index].doubleh = doubleheight; + page_atrb[index].doublew = (col < 39 ? doublewidth : 0); + page_atrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + page_atrb[index].concealed = concealed; + page_atrb[index].flashing = flashmode; + page_atrb[index].boxwin = boxwin; + page_atrb[index].inverted = 0; // only relevant for Level 2.5 + page_atrb[index].underline = 0; // only relevant for Level 2.5 + page_atrb[index].diacrit = 0; // only relevant for Level 2.5 + page_atrb[index].setX26 = 0; // only relevant for Level 2.5 + page_atrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5 + + if (page_char[index] < ' ') + { + switch (page_char[index]) + { + case alpha_black: + case alpha_red: + case alpha_green: + case alpha_yellow: + case alpha_blue: + case alpha_magenta: + case alpha_cyan: + case alpha_white: + concealed = 0; + foreground = page_char[index] - alpha_black + black; + if (col == 0 && page_char[index] == alpha_white) + page_atrb[index].fg = black; // indicate level 1 color change on column 0; (hack) + charset = C_G0P; + break; + + case flash: + flashmode = 1; + break; + case steady: + flashmode = 0; + page_atrb[index].flashing = 0; + break; + case end_box: + boxwin = 0; + IgnoreAtBlackBgSubst = 0; +/* if (boxed) + { + foreground = transp; + background = transp; + IgnoreAtBlackBgSubst = 0; + } +*/ + break; + + case start_box: + if (!boxwin) + { + boxwin = 1; + //background = 0x08; + } +/* if (boxed) + { + int rowstart = row * 40; + if (col > 0) + memset(&page_char[rowstart], ' ', col); + for (clear = 0; clear < col; clear++) + { + page_atrb[rowstart + clear].fg = page_atrb[rowstart + clear].bg = transp; + page_atrb[rowstart + clear].IgnoreAtBlackBgSubst = 0; + } + } +*/ + break; + + case normal_size: + doubleheight = 0; + doublewidth = 0; + page_atrb[index].doubleh = doubleheight; + page_atrb[index].doublew = doublewidth; + break; + + case double_height: + if (row < 23) + { + doubleheight = 1; + dhset = 1; + } + doublewidth = 0; + + break; + + case double_width: + if (col < 39) + doublewidth = 1; + doubleheight = 0; + break; + + case double_size: + if (row < 23) + { + doubleheight = 1; + dhset = 1; + } + if (col < 39) + doublewidth = 1; + break; + + case mosaic_black: + case mosaic_red: + case mosaic_green: + case mosaic_yellow: + case mosaic_blue: + case mosaic_magenta: + case mosaic_cyan: + case mosaic_white: + concealed = 0; + foreground = page_char[index] - mosaic_black + black; + charset = mosaictype ? C_G1S : C_G1C; + break; + + case conceal: + page_atrb[index].concealed = 1; + concealed = 1; + if (!hintmode) + { + foreground = background; + page_atrb[index].fg = foreground; + } + break; + + case contiguous_mosaic: + mosaictype = 0; + if (charset == C_G1S) + { + charset = C_G1C; + page_atrb[index].charset = charset; + } + break; + + case separated_mosaic: + mosaictype = 1; + if (charset == C_G1C) + { + charset = C_G1S; + page_atrb[index].charset = charset; + } + break; + + case esc: + if (charset == C_G0P) + charset = C_G0S; + else if (charset == C_G0S) + charset = C_G0P; + break; + + case black_background: + background = black; + IgnoreAtBlackBgSubst = 0; + page_atrb[index].bg = background; + page_atrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + break; + + case new_background: + background = foreground; + if (background == black) + IgnoreAtBlackBgSubst = 1; + else + IgnoreAtBlackBgSubst = 0; + page_atrb[index].bg = background; + page_atrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + break; + + case hold_mosaic: + hold = 1; + break; + + case release_mosaic: + hold = 2; + break; + } + + /* handle spacing attributes */ + if (hold && (page_atrb[index].charset == C_G1C || page_atrb[index].charset == C_G1S)) + page_char[index] = held_mosaic; + else + page_char[index] = ' '; + + if (hold == 2) + hold = 0; + } + else /* char >= ' ' */ + { + /* set new held-mosaic char */ + if ((charset == C_G1C || charset == C_G1S) && + ((page_char[index]&0xA0) == 0x20)) + held_mosaic = page_char[index]; + if (page_atrb[index].doubleh) + page_char[index + 40] = 0xFF; + + } + if (!(charset == C_G1C || charset == C_G1S)) + held_mosaic = ' '; /* forget if outside mosaic */ + + } /* for col */ + + /* skip row if doubleheight */ + if (row < 23 && dhset) + { + for (col = 0; col < 40; col++) + { + int index = row*40 + col; + page_atrb[index+40].bg = page_atrb[index].bg; + page_atrb[index+40].fg = white; + if (!page_atrb[index].doubleh) + page_char[index+40] = ' '; + page_atrb[index+40].flashing = 0; + page_atrb[index+40].charset = C_G0P; + page_atrb[index+40].doubleh = 0; + page_atrb[index+40].doublew = 0; + page_atrb[index+40].IgnoreAtBlackBgSubst = 0; + page_atrb[index+40].concealed = 0; + page_atrb[index+40].flashing = 0; + page_atrb[index+40].boxwin = page_atrb[index].boxwin; + } + row++; + } + } /* for row */ + FullScrColor = black; + + if (showl25) + eval_l25(); + + + /* handle Black Background Color Substitution and transparency (CLUT1#0) */ + { + int r, c; + int o = 0; + char bitmask ; + + + + for (r = 0; r < 25; r++) + { + for (c = 0; c < 40; c++) + { + bitmask = (page_atrb[o].bg == 0x08 ? 0x08 : 0x00) | (FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (page_atrb[o].boxwin <<1) | boxed; + switch (bitmask) + { + case 0x08: + case 0x0b: + if (FullRowColor[r] == 0x08) + page_atrb[o].bg = FullScrColor; + else + page_atrb[o].bg = FullRowColor[r]; + break; + case 0x01: + case 0x05: + case 0x09: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + page_atrb[o].bg = transp; + break; + } + bitmask = (page_atrb[o].fg == 0x08 ? 0x08 : 0x00) | (FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (page_atrb[o].boxwin <<1) | boxed; + switch (bitmask) + { + case 0x08: + case 0x0b: + if (FullRowColor[r] == 0x08) + page_atrb[o].fg = FullScrColor; + else + page_atrb[o].fg = FullRowColor[r]; + break; + case 0x01: + case 0x05: + case 0x09: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + page_atrb[o].fg = transp; + break; + } + o++; + } + } + } + return ; +} + +/****************************************************************************** + * GetRCCode * + ******************************************************************************/ + +int GetRCCode() +{ + struct input_event ev; + static __u16 rc_last_key = KEY_RESERVED; + + int val = fcntl(rc, F_GETFL); + if(!(val & O_NONBLOCK)) + printf("[tuxtxt] GetRCCode in blocking mode.\n"); + + /* get code */ + if (read(rc, &ev, sizeof(ev)) == sizeof(ev)) + { + if (ev.value) + { + if (ev.code != rc_last_key) + { + rc_last_key = ev.code; + switch (ev.code) + { + case KEY_UP: RCCode = RC_UP; break; + case KEY_DOWN: RCCode = RC_DOWN; break; + case KEY_LEFT: RCCode = RC_LEFT; break; + case KEY_RIGHT: RCCode = RC_RIGHT; break; + case KEY_OK: RCCode = RC_OK; break; + case KEY_0: RCCode = RC_0; break; + case KEY_1: RCCode = RC_1; break; + case KEY_2: RCCode = RC_2; break; + case KEY_3: RCCode = RC_3; break; + case KEY_4: RCCode = RC_4; break; + case KEY_5: RCCode = RC_5; break; + case KEY_6: RCCode = RC_6; break; + case KEY_7: RCCode = RC_7; break; + case KEY_8: RCCode = RC_8; break; + case KEY_9: RCCode = RC_9; break; + case KEY_RED: RCCode = RC_RED; break; + case KEY_GREEN: RCCode = RC_GREEN; break; + case KEY_YELLOW: RCCode = RC_YELLOW; break; + case KEY_BLUE: RCCode = RC_BLUE; break; + case KEY_VOLUMEUP: RCCode = RC_PLUS; break; + case KEY_VOLUMEDOWN: RCCode = RC_MINUS; break; + case KEY_MUTE: RCCode = RC_MUTE; break; + case KEY_TEXT: RCCode = RC_TEXT; break; + //case KEY_HELP: RCCode = RC_HELP; break; + case KEY_INFO: RCCode = RC_HELP; break; + case KEY_MENU: RCCode = RC_DBOX; break; + case KEY_EXIT: RCCode = RC_HOME; break; + case KEY_POWER: RCCode = RC_STANDBY; break; + } +printf("[tuxtxt] new key, code %X\n", RCCode); + return 1; + } + } + else + { + RCCode = -1; + rc_last_key = KEY_RESERVED; + } + } + + RCCode = -1; + usleep(1000000/100); + + return 0; +} +/* Local Variables: */ +/* indent-tabs-mode:t */ +/* tab-width:3 */ +/* c-basic-offset:3 */ +/* comment-column:0 */ +/* fill-column:120 */ +/* End: */ diff --git a/lib/libtuxtxt/tuxtxt.h b/lib/libtuxtxt/tuxtxt.h new file mode 100644 index 000000000..725256dbc --- /dev/null +++ b/lib/libtuxtxt/tuxtxt.h @@ -0,0 +1,1506 @@ +#ifndef __tuxtxt_h__ +#define __tuxtxt_h__ + +/****************************************************************************** + * <<< TuxTxt - Teletext Plugin >>> * + * * + * (c) Thomas "LazyT" Loewe 2002-2003 (LazyT@gmx.net) * + * * + * continued 2004-2005 by Roland Meier * + * and DBLuelle * + * * + * ported 2006 to Dreambox 7025 / 32Bit framebuffer * + * by Seddi * + * * + ******************************************************************************/ + +#define TUXTXT_CFG_STANDALONE 0 // 1:plugin only 0:use library +#define TUXTXT_DEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "tuxtxt_def.h" + +#include +#include FT_FREETYPE_H +#include FT_CACHE_H +#include FT_CACHE_SMALL_BITMAPS_H + +/* devices */ + +#if TUXTXT_CFG_STANDALONE +#include "tuxtxt_common.h" +#else +/* variables and functions from libtuxtxt */ +extern tuxtxt_cache_struct tuxtxt_cache; +extern int tuxtxt_init(); +extern void tuxtxt_close(); +extern void tuxtxt_start(int tpid); // Start caching +extern int tuxtxt_stop(); // Stop caching +extern void tuxtxt_next_dec(int *i); /* skip to next decimal */ +extern void tuxtxt_prev_dec(int *i); /* counting down */ +extern int tuxtxt_is_dec(int i); +extern int tuxtxt_next_hex(int i); +extern void tuxtxt_decode_btt(); +extern void tuxtxt_decode_adip(); /* additional information table */ +extern void tuxtxt_compress_page(int p, int sp, unsigned char* buffer); +extern void tuxtxt_decompress_page(int p, int sp, unsigned char* buffer); +#if TUXTXT_DEBUG +extern int tuxtxt_get_zipsize(int p, int sp); +#endif +#endif + + +#define TUXTXTCONF CONFIGDIR "/tuxtxt/tuxtxt2.conf" + +/* fonts */ +#define TUXTXTTTF FONTDIR "/tuxtxt.ttf" +#define TUXTXTOTB FONTDIR "/tuxtxt.otb" +/* alternative fontdir */ +#define TUXTXTTTFVAR "/var/tuxtxt/tuxtxt.ttf" +#define TUXTXTOTBVAR "/var/tuxtxt/tuxtxt.otb" + +int TTFWidthFactor16, TTFHeightFactor16, TTFShiftX, TTFShiftY; /* parameters for adapting to various TTF fonts */ +int fontheight, fontwidth, fontwidth_normal, fontwidth_small, fontwidth_topmenumain, fontwidth_topmenusmall, ascender; +int ymosaic[4]; +int displaywidth; +#define fontwidth_small_lcd 8 + +#define TV43STARTX (ex - 146) //(StartX + 2 + (40-nofirst)*fontwidth_topmenumain + (40*fontwidth_topmenumain/abx)) +#define TV169FULLSTARTX (sx+ 8*40) //(sx +(ex +1 - sx)/2) +#define TVENDX ex +#define TVENDY (StartY + 25*fontheight) +#define TV43WIDTH 144 /* 120 */ +#define TV43HEIGHT 116 /* 96 */ +#define TV43STARTY (TVENDY - TV43HEIGHT) +#define TV169FULLSTARTY sy +#define TV169FULLWIDTH (ex - sx)/2 +#define TV169FULLHEIGHT (ey - sy) + +#define TOPMENUSTARTX TV43STARTX+2 +#define TOPMENUENDX TVENDX +#define TOPMENUSTARTY StartY +#define TOPMENUENDY TV43STARTY + +#define TOPMENULINEWIDTH ((TOPMENUENDX-TOPMENU43STARTX+fontwidth_topmenusmall-1)/fontwidth_topmenusmall) +#define TOPMENUINDENTBLK 0 +#define TOPMENUINDENTGRP 1 +#define TOPMENUINDENTDEF 2 +#define TOPMENUSPC 0 +#define TOPMENUCHARS (TOPMENUINDENTDEF+12+TOPMENUSPC+4) + +#define FLOFSIZE 4 + +/* spacing attributes */ +#define alpha_black 0x00 +#define alpha_red 0x01 +#define alpha_green 0x02 +#define alpha_yellow 0x03 +#define alpha_blue 0x04 +#define alpha_magenta 0x05 +#define alpha_cyan 0x06 +#define alpha_white 0x07 +#define flash 0x08 +#define steady 0x09 +#define end_box 0x0A +#define start_box 0x0B +#define normal_size 0x0C +#define double_height 0x0D +#define double_width 0x0E +#define double_size 0x0F +#define mosaic_black 0x10 +#define mosaic_red 0x11 +#define mosaic_green 0x12 +#define mosaic_yellow 0x13 +#define mosaic_blue 0x14 +#define mosaic_magenta 0x15 +#define mosaic_cyan 0x16 +#define mosaic_white 0x17 +#define conceal 0x18 +#define contiguous_mosaic 0x19 +#define separated_mosaic 0x1A +#define esc 0x1B +#define black_background 0x1C +#define new_background 0x1D +#define hold_mosaic 0x1E +#define release_mosaic 0x1F + +/* rc codes */ +#define RC_0 0x00 +#define RC_1 0x01 +#define RC_2 0x02 +#define RC_3 0x03 +#define RC_4 0x04 +#define RC_5 0x05 +#define RC_6 0x06 +#define RC_7 0x07 +#define RC_8 0x08 +#define RC_9 0x09 +#define RC_RIGHT 0x0A +#define RC_LEFT 0x0B +#define RC_UP 0x0C +#define RC_DOWN 0x0D +#define RC_OK 0x0E +#define RC_MUTE 0x0F +#define RC_STANDBY 0x10 +#define RC_GREEN 0x11 +#define RC_YELLOW 0x12 +#define RC_RED 0x13 +#define RC_BLUE 0x14 +#define RC_PLUS 0x15 +#define RC_MINUS 0x16 +#define RC_HELP 0x17 +#define RC_DBOX 0x18 +#define RC_TEXT 0x19 +#define RC_HOME 0x1F + +typedef enum /* object type */ +{ + OBJ_PASSIVE, + OBJ_ACTIVE, + OBJ_ADAPTIVE +} tObjType; + +const char *ObjectSource[] = +{ + "(illegal)", + "Local", + "POP", + "GPOP" +}; +const char *ObjectType[] = +{ + "Passive", + "Active", + "Adaptive", + "Passive" +}; + +/* messages */ +#define ShowInfoBar 0 +//#define PageNotFound 1 +#define ShowServiceName 2 +#define NoServicesFound 3 + +/* framebuffer stuff */ +static unsigned char *lfb = 0; +struct fb_var_screeninfo var_screeninfo; +struct fb_fix_screeninfo fix_screeninfo; + +/* freetype stuff */ +FT_Library library; +FTC_Manager manager; +static FTC_SBitCache cache; +FTC_SBit sbit; + +#define FONTTYPE FTC_ImageTypeRec + +FT_Face face; +FONTTYPE typettf; + + +// G2 Charset (0 = Latin, 1 = Cyrillic, 2 = Greek) +const unsigned short int G2table[3][6*16] = +{ + { ' ' ,'¡' ,'¢' ,'£' ,'$' ,'¥' ,'#' ,'§' ,'¤' ,'\'','\"','«' ,8592,8594,8595,8593, + '°' ,'±' ,'²' ,'³' ,'x' ,'µ' ,'¶' ,'·' ,'÷' ,'\'','\"','»' ,'¼' ,'½' ,'¾' ,'¿' , + ' ' ,'`' ,'´' ,710 ,732 ,'¯' ,728 ,729 ,733 ,716 ,730 ,719 ,'_' ,698 ,718 ,711 , + '­' ,'¹' ,'®' ,'©' ,8482,9834,8364,8240,945 ,' ' ,' ' ,' ' ,8539,8540,8541,8542, + 937 ,'Æ' ,272 ,'ª' ,294 ,' ' ,306 ,319 ,321 ,'Ø' ,338 ,'º' ,'Þ' ,358 ,330 ,329 , + 1082,'æ' ,273 ,'ð' ,295 ,305 ,307 ,320 ,322 ,'ø' ,339 ,'ß' ,'þ' ,359 ,951 ,0x7F}, + { ' ' ,'¡' ,'¢' ,'£' ,'$' ,'¥' ,' ' ,'§' ,' ' ,'\'','\"','«' ,8592,8594,8595,8593, + '°' ,'±' ,'²' ,'³' ,'x' ,'µ' ,'¶' ,'·' ,'÷' ,'\'','\"','»' ,'¼' ,'½' ,'¾' ,'¿' , + ' ' ,'`' ,'´' ,710 ,732 ,'¯' ,728 ,729 ,733 ,716 ,730 ,719 ,'_' ,698 ,718 ,711 , + '­' ,'¹' ,'®' ,'©' ,8482,9834,8364,8240,945 ,321 ,322 ,'ß' ,8539,8540,8541,8542, + 'D' ,'E' ,'F' ,'G' ,'I' ,'J' ,'K' ,'L' ,'N' ,'Q' ,'R' ,'S' ,'U' ,'V' ,'W' ,'Z' , + 'd' ,'e' ,'f' ,'g' ,'i' ,'j' ,'k' ,'l' ,'n' ,'q' ,'r' ,'s' ,'u' ,'v' ,'w' ,'z' }, + { ' ' ,'a' ,'b' ,'£' ,'e' ,'h' ,'i' ,'§' ,':' ,'\'','\"','k' ,8592,8594,8595,8593, + '°' ,'±' ,'²' ,'³' ,'x' ,'m' ,'n' ,'p' ,'÷' ,'\'','\"','t' ,'¼' ,'½' ,'¾' ,'x' , + ' ' ,'`' ,'´' ,710 ,732 ,'¯' ,728 ,729 ,733 ,716 ,730 ,719 ,'_' ,698 ,718 ,711 , + '?' ,'¹' ,'®' ,'©' ,8482,9834,8364,8240,945 ,906 ,910 ,911 ,8539,8540,8541,8542, + 'C' ,'D' ,'F' ,'G' ,'J' ,'L' ,'Q' ,'R' ,'S' ,'U' ,'V' ,'W' ,'Y' ,'Z' ,902 ,905 , + 'c' ,'d' ,'f' ,'g' ,'j' ,'l' ,'q' ,'r' ,'s' ,'u' ,'v' ,'w' ,'y' ,'z' ,904 ,0x7F} +}; +// cyrillic G0 Charset +// TODO: different maps for serbian/russian/ukrainian +const unsigned short int G0tablecyrillic[6*16] = +{ + ' ' ,'!' ,'\"','#' ,'$' ,'%' ,'&' ,'\'','(' ,')' ,'*' ,'+' ,',' ,'-' ,'.' ,'/' , + '0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7' ,'8' ,'9' ,':' ,';' ,'<' ,'=' ,'>' ,'?' , + 1063,1040,1041,1062,1044,1045,1060,1043,1061,1048,1032,1050,1051,1052,1053,1054, + 1055,1036,1056,1057,1058,1059,1042,1027,1033,1034,1047,1035,1046,1026,1064,1119, + 1095,1072,1073,1094,1076,1077,1092,1075,1093,1080,1112,1082,1083,1084,1085,1086, + 1087,1116,1088,1089,1090,1091,1074,1107,1113,1114,1079,1115,1078,1106,1096,0x7F +}; + +const unsigned short int nationaltable23[14][2] = +{ + { '#', '¤' }, /* 0 */ + { '#', 367 }, /* 1 CS/SK */ + { '£', '$' }, /* 2 EN */ + { '#', 'õ' }, /* 3 ET */ + { 'é', 'ï' }, /* 4 FR */ + { '#', '$' }, /* 5 DE */ + { '£', '$' }, /* 6 IT */ + { '#', '$' }, /* 7 LV/LT */ + { '#', 329 }, /* 8 PL */ + { 'ç', '$' }, /* 9 PT/ES */ + { '#', '¤' }, /* A RO */ + { '#', 'Ë' }, /* B SR/HR/SL */ + { '#', '¤' }, /* C SV/FI/HU */ + { '£', 287 }, /* D TR ? */ +}; +const unsigned short int nationaltable40[14] = +{ + '@', /* 0 */ + 269, /* 1 CS/SK */ + '@', /* 2 EN */ + 352, /* 3 ET */ + 'à', /* 4 FR */ + '§', /* 5 DE */ + 'é', /* 6 IT */ + 352, /* 7 LV/LT */ + 261, /* 8 PL */ + '¡', /* 9 PT/ES */ + 354, /* A RO */ + 268, /* B SR/HR/SL */ + 'É', /* C SV/FI/HU */ + 304, /* D TR */ +}; +const unsigned short int nationaltable5b[14][6] = +{ + { '[','\\', ']', '^', '_', '`' }, /* 0 */ + { 357, 382, 'ý', 'í', 345, 'é' }, /* 1 CS/SK */ + {8592, '½',8594,8593, '#', 822 }, /* 2 EN */ + { 'Ä', 'Ö', 381, 'Ü', 'Õ', 353 }, /* 3 ET */ + { 'ë', 'ê', 'ù', 'î', '#', 'è' }, /* 4 FR */ + { 'Ä', 'Ö', 'Ü', '^', '_', '°' }, /* 5 DE */ + { '°', 'ç',8594,8593, '#', 'ù' }, /* 6 IT */ + { 'é', 553, 381, 269, 363, 353 }, /* 7 LV/LT */ + { 437, 346, 321, 263, 'ó', 281 }, /* 8 PL */ + { 'á', 'é', 'í', 'ó', 'ú', '¿' }, /* 9 PT/ES */ + { 'Â', 350, 461, 'Î', 305, 355 }, /* A RO */ + { 262, 381, 272, 352, 'ë', 269 }, /* B SR/HR/SL */ + { 'Ä', 'Ö', 'Å', 'Ü', '_', 'é' }, /* C SV/FI/HU */ + { 350, 'Ö', 'Ç', 'Ü', 486, 305 }, /* D TR */ +}; +const unsigned short int nationaltable7b[14][4] = +{ + { '{', '|', '}', '~' }, /* 0 */ + { 'á', 283, 'ú', 353 }, /* 1 CS/SK */ + { '¼',8214, '¾', '÷' }, /* 2 EN */ + { 'ä', 'ö', 382, 'ü' }, /* 3 ET */ + { 'â', 'ô', 'û', 'ç' }, /* 4 FR */ + { 'ä', 'ö', 'ü', 'ß' }, /* 5 DE */ + { 'à', 'ò', 'è', 'ì' }, /* 6 IT */ + { 261, 371, 382, 303 }, /* 7 LV/LT */ + { 380, 347, 322, 378 }, /* 8 PL */ + { 'ü', 'ñ', 'è', 'à' }, /* 9 PT/ES */ + { 'â', 351, 462, 'î' }, /* A RO */ + { 263, 382, 273, 353 }, /* B SR/HR/SL */ + { 'ä', 'ö', 'å', 'ü' }, /* C SV/FI/HU */ + { 351, 'ö', 231, 'ü' }, /* D TR */ +}; + +const unsigned short int arrowtable[] = +{ + 8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592 +}; + +/* national subsets */ +const char countrystring[] = +" (#$@[\\]^_`{|}~) " /* 0 no subset specified */ +" CS/SK (#$@[\\]^_`{|}~) " /* 1 czech, slovak */ +" EN (#$@[\\]^_`{|}~) " /* 2 english */ +" ET (#$@[\\]^_`{|}~) " /* 3 estonian */ +" FR (#$@[\\]^_`{|}~) " /* 4 french */ +" DE (#$@[\\]^_`{|}~) " /* 5 german */ +" IT (#$@[\\]^_`{|}~) " /* 6 italian */ +" LV/LT (#$@[\\]^_`{|}~) " /* 7 latvian, lithuanian */ +" PL (#$@[\\]^_`{|}~) " /* 8 polish */ +" PT/ES (#$@[\\]^_`{|}~) " /* 9 portuguese, spanish */ +" RO (#$@[\\]^_`{|}~) " /* 10 romanian */ +" SR/HR/SL (#$@[\\]^_`{|}~) " /* 11 serbian, croatian, slovenian */ +" SV/FI/HU (#$@[\\]^_`{|}~) " /* 12 swedish, finnish, hungarian */ +" TR (#$@[\\]^_`{|}~) " /* 13 turkish */ +" RU/BUL/SER/CRO/UKR (cyr) " /* 14 cyrillic */ +" EK " /* 15 greek */ +; +#define COUNTRYSTRING_WIDTH 26 +#define MAX_NATIONAL_SUBSET (sizeof(countrystring) / COUNTRYSTRING_WIDTH - 1) + +enum +{ + NAT_DEFAULT = 0, + NAT_CZ = 1, + NAT_UK = 2, + NAT_ET = 3, + NAT_FR = 4, + NAT_DE = 5, + NAT_IT = 6, + NAT_LV = 7, + NAT_PL = 8, + NAT_SP = 9, + NAT_RO = 10, + NAT_SR = 11, + NAT_SW = 12, + NAT_TR = 13, + NAT_MAX_FROM_HEADER = 13, + NAT_RU = 14, + NAT_GR = 15 +}; + +const unsigned char countryconversiontable[] = { NAT_UK, NAT_DE, NAT_SW, NAT_IT, NAT_FR, NAT_SP, NAT_CZ, NAT_RO}; + + +/* some data */ +char versioninfo[16]; +int hotlist[10]; +int maxhotlist; + +int pig, rc, fb, lcd; +int sx, ex, sy, ey; +int PosX, PosY, StartX, StartY; +int lastpage; +int inputcounter; +int zoommode, screenmode, transpmode, hintmode, boxed, nofirst, savedscreenmode, showflof, show39, showl25, prevscreenmode; +char dumpl25; +int catch_row, catch_col, catched_page, pagecatching; +int prev_100, prev_10, next_10, next_100; +int screen_mode1, screen_mode2, color_mode, trans_mode, national_subset, national_subset_secondary, auto_national, swapupdown, showhex, menulanguage; +int pids_found, current_service, getpidsdone; +int SDT_ready; +int pc_old_row, pc_old_col; /* for page catching */ +int temp_page; /* for page input */ +char saveconfig, hotlistchanged; +signed char clearbbcolor = -1; +int usettf; +short pop, gpop, drcs, gdrcs; +unsigned char tAPx, tAPy; /* temporary offset to Active Position for objects */ +unsigned char axdrcs[12+1+10+1]; +#define aydrcs (axdrcs + 12+1) +unsigned char FullRowColor[25]; +unsigned char FullScrColor; +tstPageinfo *pageinfo = 0;/* pointer to cached info of last decoded page */ +const char * fncmodes[] = {"12", "6"}; +const char * saamodes[] = {"4:3_full_format", "16:9_full_format"}; + +struct timeval tv_delay; +int subtitledelay, delaystarted; +FILE *conf; + + +unsigned short RCCode; + +struct _pid_table +{ + int vtxt_pid; + int service_id; + int service_name_len; + char service_name[24]; + int national_subset; +}pid_table[128]; + +unsigned char restoreaudio = 0; +/* 0 Nokia, 1 Philips, 2 Sagem */ +/* typ_vcr/dvb: v1 a1 v2 a2 v3 a3 (vcr_only: fblk) */ + +/* language dependent texts */ +#define MAXMENULANGUAGE 8 /* 0 deutsch, 1 englisch, 2 französisch, 3 niederländisch, 4 griechisch, 5 italienisch, 6 polnisch, 7 schwedisch, 8 suomi */ +const int menusubset[] = { NAT_DE , NAT_UK , NAT_FR , NAT_UK , NAT_GR , NAT_IT , NAT_PL , NAT_SW, NAT_SW }; + + +#define Menu_StartX (StartX + fontwidth*9/2) +#define Menu_StartY (StartY + fontheight) +#define Menu_Height 24 +#define Menu_Width 31 + +const char MenuLine[] = +{ + 3,8,11,12,15,17,19,20,21 +}; + +enum +{ + M_HOT=0, + M_PID, + M_SC1, + M_SC2, + M_COL, + M_TRA, + M_AUN, + M_NAT, + M_LNG, + M_Number +}; + +#define M_Start M_HOT +#define M_MaxDirect M_AUN + +const char hotlistpagecolumn[] = /* last(!) column of page to show in each language */ +{ + 22, 26, 28, 27, 28, 27, 28, 21, 20 +}; +const char hotlisttextcolumn[] = +{ + 24, 14, 14, 15, 14, 15, 14, 23, 22 +}; +const char hotlisttext[][2*6] = +{ + { "dazu entf." }, + { " add rem. " }, + { "ajoutenlev" }, + { "toev.verw." }, + { "pq|shava_q" }, + { "agg. elim." }, + { "dodajkasuj" }, + { "ny bort " }, + { "lis{{pois " }, +}; + +const char configonoff[][2*4] = +{ + { "ausein" }, + { "offon " }, + { "desact" }, + { "uitaan" }, + { "emeape" }, + { "offon " }, + { "wy}w} " }, + { "p} av " }, + { "EI ON " }, +}; +const char menuatr[Menu_Height*(Menu_Width+1)] = +{ + "0000000000000000000000000000002" + "0111111111111111111111111111102" + "0000000000000000000000000000002" + "3144444444444444444444444444432" + "3556655555555555555555555555532" + "3555555555555555555555555555532" + "3333333333333333333333333333332" + "3144444444444444444444444444432" + "3555555555555555555555555555532" + "3333333333333333333333333333332" + "3444444444444444444444444444432" + "3155555555555555555555555555532" + "3155555555555555555555555555532" + "3333333333333333333333333333332" + "3144444444444444444444444444432" + "3555555555555555555555555555532" + "3144444444444444444444444444432" + "3555555555555555555555555555532" + "3144444444444444444444444444432" + "3555555555555555555555555555532" + "3555555555555555555555555555532" + "3555555555555555555555555555532" + "3334444444444444444444444443332" + "2222222222222222222222222222222" +}; +const char configmenu[][Menu_Height*(Menu_Width+1)] = +{ + { +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + "àááááááááááááááááááááááááááááâè" + "ã Konfigurationsmen} äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Favoriten: Seite 111 dazu äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Teletext-Auswahl äé" + "ãí suchen îäé" + "ã äé" + "ã Bildschirmformat äé" + "ã3 Standard-Modus 16:9 äé" + "ã4 TextBild-Modus 16:9 äé" + "ã äé" + "ã5 Helligkeit äé" + "ãí îäé" + "ã6 Transparenz äé" + "ãí îäé" + "ã7 nationaler Zeichensatz äé" + "ãautomatische Erkennung äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Sprache/Language deutsch îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Configuration menu äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Favorites: add page 111 äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Teletext selection äé" + "ãí search îäé" + "ã äé" + "ã Screen format äé" + "ã3 Standard mode 16:9 äé" + "ã4 Text/TV mode 16:9 äé" + "ã äé" + "ã5 Brightness äé" + "ãí îäé" + "ã6 Transparency äé" + "ãí îäé" + "ã7 national characterset äé" + "ã automatic recognition äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Sprache/language english îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Menu de configuration äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Favorites: ajout. page 111äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Selection de teletext äé" + "ãí recherche îäé" + "ã äé" + "ã Format de l'#cran äé" + "ã3 Mode standard 16:9 äé" + "ã4 Texte/TV 16:9 äé" + "ã äé" + "ã5 Clarte äé" + "ãí îäé" + "ã6 Transparence äé" + "ãí îäé" + "ã7 police nationale äé" + "ãreconn. automatique äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Sprache/language francaisîäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Configuratiemenu äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Favorieten: toev. pag 111 äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Teletekst-selectie äé" + "ãí zoeken îäé" + "ã äé" + "ã Beeldschermformaat äé" + "ã3 Standaardmode 16:9 äé" + "ã4 Tekst/TV mode 16:9 äé" + "ã äé" + "ã5 Helderheid äé" + "ãí îäé" + "ã6 Transparantie äé" + "ãí îäé" + "ã7 nationale tekenset äé" + "ãautomatische herkenning äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Sprache/Language nederl. îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Lemo} quhl_seym äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Vaboq_: pqo_h. sek. 111äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Epikoc^ Teket]nt äé" + "ãí Amaf^tgsg îäé" + "ã äé" + "ã Loqv^ oh|mgr äé" + "ã3 Tq|por pq|tupor 16:9 äé" + "ã4 Tq|por eij. jeil. 16:9 äé" + "ã äé" + "ã5 Kalpq|tgta äé" + "ãí îäé" + "ã6 Diav\\meia äé" + "ãí îäé" + "ã7 Ehmij^ tuposeiq\\ äé" + "ãaut|latg amacm~qisg äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Ck~ssa/Language ekkgmij\\ îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Menu di configurazione äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Preferiti: agg. pag.111 äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Selezione televideo äé" + "ãí ricerca îäé" + "ã äé" + "ã Formato schermo äé" + "ã3 Modo standard 16:9 äé" + "ã4 Text/Mod.TV 16:9 äé" + "ã äé" + "ã5 Luminosit{ äé" + "ãí îäé" + "ã6 Trasparenza äé" + "ãí îäé" + "ã7 nazionalita'caratteri äé" + "ã riconoscimento automatico äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Lingua/Language Italiana îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Konfiguracja äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Ulubione : kasuj str. 111äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Wyb_r telegazety äé" + "ãí szukaj îäé" + "ã äé" + "ã Format obrazu äé" + "ã3 Tryb standard 16:9 äé" + "ã4 Telegazeta/TV 16:9 äé" + "ã äé" + "ã5 Jasno|^ äé" + "ãí îäé" + "ã6 Prze~roczysto|^ äé" + "ãí îäé" + "ã7 Znaki charakterystyczne äé" + "ã automatyczne rozpozn. äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí J`zyk/Language polski îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Konfigurationsmeny äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Favoriter: sida 111 ny äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 TextTV v{ljaren äé" + "ãí s|k îäé" + "ã äé" + "ã TV- format äé" + "ã3 Standard l{ge 16:9 äé" + "ã4 Text/Bild l{ge 16:9 äé" + "ã äé" + "ã5 Ljusstyrka äé" + "ãí îäé" + "ã6 Genomskinlighet äé" + "ãí îäé" + "ã7nationell teckenupps{ttningäé" + "ã automatisk igenk{nning äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Sprache/language svenska îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +/* 0000000000111111111122222222223 */ +/* 0123456789012345678901234567890 */ + { + "àááááááááááááááááááááááááááááâè" + "ã Asetusvalikko äé" + "åææææææææææææææææææææææææææææçé" + "ã1 Suosikit: sivu 111 lis{{ äé" + "ãíîñò äé" + "ã+-? äé" + "ã äé" + "ã2 Tekstikanavan valinta äé" + "ãí search îäé" + "ã äé" + "ã N{ytt|tila äé" + "ã3 Vakiotila 16:9 äé" + "ã4 Teksti/TV 16:9 äé" + "ã äé" + "ã5 Kirkkaus äé" + "ãí îäé" + "ã6 L{pin{kyvyys äé" + "ãí îäé" + "ã7 kansallinen merkist| äé" + "ã automaattinen tunnistus äé" + "ãí DE (#$@[\\]^_`{|}~) îäé" + "ãí Kieli suomi îäé" + "åæ www.tuxtxt.net x.xx æçé" + "ëìììììììììììììììììììììììììììììê" + }, +}; + +const char catchmenutext[][81] = +{ + { " íïðî w{hlen ñò anzeigen " + "0000000011110000000000110000000000000000" }, + { " íïðî select ñò show " + "0000000011110000000000110000000000000000" }, + { " íïðî selectionner ñò montrer " + "0011110000000000000000110000000000000000" }, + { " íïðî kiezen ñò tonen " + "0000000011110000000000110000000000000000" }, + { " íïðî epikoc^ ñò pqobok^ " + "0000000011110000000000110000000000000000" }, + { " íïðîseleziona ñò mostra " + "0000000011110000000000110000000000000000" }, + { " íïðî wybiez ñò wyswietl " + "0000000011110000000000110000000000000000" }, + { " íïðî v{lj ñò visa " + "0000000011110000000000110000000000000000" }, + { " íïðî valitse ñò n{yt{ " + "0000000011110000000000110000000000000000" }, +}; + +const char message_3[][39] = +{ + { "ã suche nach Teletext-Anbietern äé" }, + { "ã searching for teletext Services äé" }, + { "ã recherche des services teletext äé" }, + { "ã zoeken naar teletekst aanbieders äé" }, + { "ã amaf^tgsg voq]ym Teket]nt äé" }, + { "ã attesa opzioni televideo äé" }, + { "ã poszukiwanie sygna}u telegazety äé" }, + { "ã s|ker efter TextTV tj{nster äé" }, + { "ã etsit{{n Teksti-TV -palvelua äé" }, +}; +const char message_3_blank[] = "ã äé"; +const char message_7[][39] = +{ + { "ã kein Teletext auf dem Transponder äé" }, + { "ã no teletext on the transponder äé" }, + { "ã pas de teletext sur le transponderäé" }, + { "ã geen teletekst op de transponder äé" }, + { "ã jal]la Teket]nt ston amaletadot^ äé" }, + { "ã nessun televideo sul trasponder äé" }, + { "ã brak sygna}u na transponderze äé" }, + { "ã ingen TextTV p} denna transponder äé" }, + { "ã Ei Teksti-TV:t{ l{hettimell{ äé" }, +}; +const char message_8[][39] = +{ +/* 00000000001111111111222222222233333333334 */ +/* 01234567890123456789012345678901234567890 */ + { "ã warte auf Empfang von Seite 100 äé" }, + { "ã waiting for reception of page 100 äé" }, + { "ã attentre la réception de page 100 äé" }, + { "ãwachten op ontvangst van pagina 100äé" }, + { "ã amal]my k^xg sek_dar 100 äé" }, + { "ã attesa ricezione pagina 100 äé" }, + { "ã oczekiwanie na stron` 100 äé" }, + { "ã v{ntar p} mottagning av sida 100 äé" }, + { "ã Odotetaan sivua 100 äé" }, +}; +const char message8pagecolumn[] = /* last(!) column of page to show in each language */ +{ + 33, 34, 34, 35, 29, 30, 30, 34, 34 +}; + +enum /* options for charset */ +{ + C_G0P = 0, /* primary G0 */ + C_G0S, /* secondary G0 */ + C_G1C, /* G1 contiguous */ + C_G1S, /* G1 separate */ + C_G2, + C_G3, + C_OFFSET_DRCS = 32 + /* 32..47: 32+subpage# GDRCS (offset/20 in page_char) */ + /* 48..63: 48+subpage# DRCS (offset/20 in page_char) */ +}; + +/* struct for page attributes */ +typedef struct +{ + unsigned char fg :6; /* foreground color */ + unsigned char bg :6; /* background color */ + unsigned char charset :6; /* see enum above */ + unsigned char doubleh :1; /* double height */ + unsigned char doublew :1; /* double width */ + /* ignore at Black Background Color Substitution */ + /* black background set by New Background ($1d) instead of start-of-row default or Black Backgr. ($1c) */ + /* or black background set by level 2.5 extensions */ + unsigned char IgnoreAtBlackBgSubst:1; + unsigned char concealed:1; /* concealed information */ + unsigned char inverted :1; /* colors inverted */ + unsigned char flashing :5; /* flash mode */ + unsigned char diacrit :4; /* diacritical mark */ + unsigned char underline:1; /* Text underlined */ + unsigned char boxwin :1; /* Text boxed/windowed */ + unsigned char setX26 :1; /* Char is set by packet X/26 (no national subset used) */ + unsigned char setG0G2 :7; /* G0+G2 set designation */ +} tstPageAttr; + +enum /* indices in atrtable */ +{ + ATR_WB, /* white on black */ + ATR_PassiveDefault, /* Default for passive objects: white on black, ignore at Black Background Color Substitution */ + ATR_L250, /* line25 */ + ATR_L251, /* line25 */ + ATR_L252, /* line25 */ + ATR_L253, /* line25 */ + ATR_TOPMENU0, /* topmenu */ + ATR_TOPMENU1, /* topmenu */ + ATR_TOPMENU2, /* topmenu */ + ATR_TOPMENU3, /* topmenu */ + ATR_MSG0, /* message */ + ATR_MSG1, /* message */ + ATR_MSG2, /* message */ + ATR_MSG3, /* message */ + ATR_MSGDRM0, /* message */ + ATR_MSGDRM1, /* message */ + ATR_MSGDRM2, /* message */ + ATR_MSGDRM3, /* message */ + ATR_MENUHIL0, /* hilit menu line */ + ATR_MENUHIL1, /* hilit menu line */ + ATR_MENUHIL2, /* hilit menu line */ + ATR_MENU0, /* menu line */ + ATR_MENU1, /* menu line */ + ATR_MENU2, /* menu line */ + ATR_MENU3, /* menu line */ + ATR_MENU4, /* menu line */ + ATR_MENU5, /* menu line */ + ATR_MENU6, /* menu line */ + ATR_CATCHMENU0, /* catch menu line */ + ATR_CATCHMENU1 /* catch menu line */ +}; + +/* define color names */ +enum +{ + black = 0, + red, /* 1 */ + green, /* 2 */ + yellow, /* 3 */ + blue, /* 4 */ + magenta, /* 5 */ + cyan, /* 6 */ + white, /* 7 */ + menu1 = (4*8), + menu2, + menu3, + transp, + transp2, + SIZECOLTABLE +}; + +//const (avoid warnings :<) +tstPageAttr atrtable[] = +{ + { white , black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */ + { white , black , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */ + { white , red , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */ + { black , green , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */ + { black , yellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */ + { white , blue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */ + { magenta, black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */ + { green , black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */ + { yellow , black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */ + { cyan , black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */ + { menu2 , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */ + { yellow , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */ + { menu2 , transp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */ + { white , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */ + { menu2 , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */ + { yellow , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */ + { menu2 , black , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */ + { white , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */ + { menu1 , blue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */ + { white , blue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */ + { menu2 , transp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b › */ + { menu2 , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab « */ + { yellow , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 ¤ */ + { menu2 , transp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b › */ + { menu2 , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb Ë */ + { cyan , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 Ç */ + { white , menu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 È */ + { white , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ¨ */ + { yellow , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 ¤ */ + { white , menu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f} /* ATR_CATCHMENU1 a8 ¨ */ +}; +/* buffers */ +unsigned char lcd_backbuffer[120*64 / 8]; +unsigned char page_char[40 * 25]; +tstPageAttr page_atrb[40 * 25]; + +//unsigned short page_atrb[40 * 25]; /* ?????:h:cc:bbbb:ffff -> ?=reserved, h=double height, c=charset (0:G0 / 1:G1c / 2:G1s), b=background, f=foreground */ + + +/* colormap */ +const unsigned short defaultcolors[] = /* 0x0bgr */ +{ + 0x000, 0x00f, 0x0f0, 0x0ff, 0xf00, 0xf0f, 0xff0, 0xfff, + 0x000, 0x007, 0x070, 0x077, 0x700, 0x707, 0x770, 0x777, + 0x50f, 0x07f, 0x7f0, 0xbff, 0xac0, 0x005, 0x256, 0x77c, + 0x333, 0x77f, 0x7f7, 0x7ff, 0xf77, 0xf7f, 0xff7, 0xddd, + 0x420, 0x210, 0x420, 0x000, 0x000 +}; + +/* 32bit colortable */ +unsigned char bgra[][5] = { +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xFF", +"\0\0\0\xFF", "\0\0\0\xFF", "\0\0\0\xC0", "\0\0\0\x00", +"\0\0\0\x33" }; + +/* old 8bit color table */ +unsigned short rd0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x00<<8, 0x00<<8, 0x00<<8, 0, 0 }; +unsigned short gn0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x20<<8, 0x10<<8, 0x20<<8, 0, 0 }; +unsigned short bl0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x40<<8, 0x20<<8, 0x40<<8, 0, 0 }; +unsigned short tr0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x0000 , 0x0000 , 0x0A00 , 0xFFFF, 0x3000 }; +struct fb_cmap colormap_0 = {0, SIZECOLTABLE, rd0, gn0, bl0, tr0}; + +/* tables for color table remapping, first entry (no remapping) skipped, offsets for color index */ +const unsigned char MapTblFG[] = { 0, 0, 8, 8, 16, 16, 16 }; +const unsigned char MapTblBG[] = { 8, 16, 8, 16, 8, 16, 24 }; + + +/* shapes */ +enum +{ + S_END = 0, + S_FHL, /* full horizontal line: y-offset */ + S_FVL, /* full vertical line: x-offset */ + S_BOX, /* rectangle: x-offset, y-offset, width, height */ + S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */ + S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */ + S_INV, /* invert */ + S_LNK, /* call other shape: shapenumber */ + S_CHR, /* Character from freetype hibyte, lowbyte */ + S_ADT, /* Character 2F alternating raster */ + S_FLH, /* flip horizontal */ + S_FLV /* flip vertical */ +}; + +/* shape coordinates */ +enum +{ + S_W13 = 5, /* width*1/3 */ + S_W12, /* width*1/2 */ + S_W23, /* width*2/3 */ + S_W11, /* width */ + S_WM3, /* width-3 */ + S_H13, /* height*1/3 */ + S_H12, /* height*1/2 */ + S_H23, /* height*2/3 */ + S_H11, /* height */ + S_NrShCoord +}; + +/* G3 characters */ +unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END }; +unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END }; +unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END }; +unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END }; +unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END }; +unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END }; +unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END }; +unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END }; +unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END }; +unsigned char aG3_2f[] = { S_ADT }; +unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END }; +unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END }; +unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END }; +unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END }; +unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END }; +unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END }; +unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END }; +unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END }; +unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END }; +unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END }; +unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END }; +unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END }; +unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END }; +unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END }; +unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END }; +unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END }; +unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END }; +unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END }; +unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END }; +unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END }; +unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END }; +unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END }; +unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END }; +unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END }; +unsigned char aG3_48[] = { S_TRA, 0, 0, S_W23, 0, S_H23, 0, S_BTR, 0, 0, S_W13, 0, S_H13, 0, S_END }; +unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END }; +unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END }; +unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END }; +unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END }; +unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 }; +unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF }; +unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB }; +unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END }; +unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END }; +unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END }; +unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END }; +unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END }; +unsigned char aG3_55[] = { S_LNK, 0x7e, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_END }; +unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END}; +unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END}; +unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END}; +unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END}; +unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END}; +unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92}; +unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90}; +unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91}; +unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93}; +unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END }; +unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END }; +unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END }; +unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END }; +unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END }; +unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END }; +unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END }; +unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END }; +unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END }; +unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END }; +unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END }; +unsigned char aG3_6b[] = { S_BOX, 0, 0, S_W11, S_H13, S_TRA, 0, S_H13, S_W11, 0, S_H23, 1, S_END }; +unsigned char aG3_6c[] = { S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_FLV, S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_BOX, 0, S_H12, S_W12,1, S_END }; +unsigned char aG3_6d[] = { S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_FLH, S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_END }; +unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END }; +unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END }; +unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END }; +unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END }; +unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END }; +unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END }; +unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END }; +unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END }; +unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END }; +unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END }; +unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END }; +unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END }; +unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END }; +unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END }; +unsigned char aG3_7e[] = { S_BOX, S_W12, 0, 2, S_H12, S_FLH, S_BOX, S_W12, 0, 2, S_H12, S_END };// help char, not printed directly (only by S_LNK) + +unsigned char *aShapes[] = +{ + aG3_20, aG3_21, aG3_22, aG3_23, aG3_24, aG3_25, aG3_26, aG3_27, aG3_28, aG3_29, aG3_2a, aG3_2b, aG3_2c, aG3_2d, aG3_2e, aG3_2f, + aG3_30, aG3_31, aG3_32, aG3_33, aG3_34, aG3_35, aG3_36, aG3_37, aG3_38, aG3_39, aG3_3a, aG3_3b, aG3_3c, aG3_3d, aG3_3e, aG3_3f, + aG3_40, aG3_41, aG3_42, aG3_43, aG3_44, aG3_45, aG3_46, aG3_47, aG3_48, aG3_49, aG3_4a, aG3_4b, aG3_4c, aG3_4d, aG3_4e, aG3_4f, + aG3_50, aG3_51, aG3_52, aG3_53, aG3_54, aG3_55, aG3_56, aG3_57, aG3_58, aG3_59, aG3_5a, aG3_5b, aG3_5c, aG3_5d, aG3_5e, aG3_5f, + aG3_60, aG3_61, aG3_62, aG3_63, aG3_64, aG3_65, aG3_66, aG3_67, aG3_68, aG3_69, aG3_6a, aG3_6b, aG3_6c, aG3_6d, aG3_6e, aG3_6f, + aG3_70, aG3_71, aG3_72, aG3_73, aG3_74, aG3_75, aG3_76, aG3_77, aG3_78, aG3_79, aG3_7a, aG3_7b, aG3_7c, aG3_7d, aG3_7e +}; + + + + +/* lcd layout */ +const char lcd_layout[] = +{ +#define ____ 0x0 +#define ___X 0x1 +#define __X_ 0x2 +#define __XX 0x3 +#define _X__ 0x4 +#define _X_X 0x5 +#define _XX_ 0x6 +#define _XXX 0x7 +#define X___ 0x8 +#define X__X 0x9 +#define X_X_ 0xA +#define X_XX 0xB +#define XX__ 0xC +#define XX_X 0xD +#define XXX_ 0xE +#define XXXX 0xF + +#define i <<4| + + ____ i _XXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXX_ i ____, + ___X i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i X___, + __XX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XX__ i ____,XXX_ i _X__,_XXX i __X_,__XX i X___,___X i XX__,X___ i XXX_,____ i _XXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XX__, + _XXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,X_XX i XXXX,_X_X i X_XX,X_X_ i XX_X,XX_X i _XXX,XXX_ i X_XX,_XXX i _X_X,XXXX i X_XX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXX_, + _XXX i XXXX,X___ i ____,____ i ____,____ i __XX,X_XX i XXXX,_X_X i X_XX,X_X_ i XX_X,XX_X i _XXX,XXX_ i X_XX,_XXX i _X_X,XXXX i X_XX,X___ i ____,____ i ____,____ i ___X,XXXX i XXX_, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,X_XX i XXXX,_X_X i X_XX,X_X_ i XX_X,XX_X i _XXX,XXX_ i X_XX,_XXX i _X_X,XXXX i X_XX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XX__ i XX__,XX_X i X_XX,X_X_ i XXXX,XX_X i X__X,X__X i X_XX,XXXX i _XX_,_XX_ i _XXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XX__,____ i ____,____ i ____,____ i __XX,XXX_ i XX_X,XX_X i X_XX,X_XX i _XXX,__XX i XX_X,X_XX i XX_X,XX__ i XXXX,_XX_ i XXXX,X___ i ____,____ i ____,____ i ____,__XX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXX_ i XX_X,XX_X i X_XX,X_X_ i XXXX,XX_X i XX_X,X_XX i X_XX,XXXX i _XXX,_XX_ i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXX_ i XX_X,XX_X i X_XX,X_X_ i XX_X,XX_X i XX_X,X_XX i X_XX,_XXX i _XXX,_XX_ i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXX_ i ____,____ i ____,____ i ____,____ i __XX,XXX_ i XX_X,XX_X i XXXX,X_X_ i XX_X,XX_X i XX_X,X_XX i X_XX,_XXX i _XXX,_XX_ i XXXX,X___ i ____,____ i ____,____ i ____,____ i _XXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXX_ i XX_X,XX_X i XXXX,X_X_ i XX_X,XX_X i XX_X,X_XX i X_XX,_XXX i _XXX,_XX_ i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i __XX,XXX_ i ____,_XXX i __X_,__XX i XXX_,_XXX i XX__,X___ i XXXX,X__X i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i XX__,_XXX i XXX_,__XX i ___X,XXXX i X___,XX__ i _XXX,XXX_ i __XX,____ i ___X,X___ i XXXX,XX__ i _XX_,__XX i XXXX,___X i XXXX,X___ i XX_X,XX__ i _XXX,XXX_ i __XX,XXXX i ___X, + X__X i __X_,X___ i ___X,_X__ i X_X_,____ i _X_X,__X_ i X___,___X i _X__,X___ i __X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i ____,_X_X i __X_,__X_ i X___,___X i _X__,____ i X__X, + X__X i __X_,X___ i ___X,_X__ i X_X_,____ i _X_X,__X_ i X___,___X i _X__,X___ i __X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i ____,_X_X i __X_,__X_ i X___,___X i _X__,____ i X__X, + X__X i __X_,X___ i ___X,_X__ i X_X_,____ i _X_X,__X_ i X___,___X i _X__,X___ i __X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i ____,_X_X i __X_,__X_ i X___,___X i _X__,____ i X__X, + X__X i __X_,X__X i ___X,_X__ i X__X,X__X i X__X,__X_ i X__X,___X i _X__,X___ i __X_,_X_X i __XX,XX__ i X__X,_X__ i XXXX,__X_ i _X__,_X_X i __X_,__X_ i X__X,___X i _X__,XXXX i ___X, + X__X i __X_,X__X i ___X,_X__ i X___,X__X i ___X,__X_ i X___,___X i _X__,X___ i __X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i ___X,X__X i __X_,__X_ i X__X,___X i _X__,X___ i X__X, + X__X i __X_,X__X i ___X,_X__ i X___,X__X i ___X,__X_ i X___,___X i _X__,X___ i __X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i ____,_X_X i __X_,__X_ i X__X,___X i _X__,X___ i X__X, + X__X i __X_,X__X i ___X,_X__ i X___,X__X i ___X,__X_ i X__X,___X i _X__,XXXX i __X_,_X__ i XXX_,__X_ i X__X,_X__ i XXXX,__X_ i _X__,_X_X i __X_,__X_ i X__X,___X i _X__,X___ i X__X, + X__X i __X_,X__X i ___X,_X__ i X___,X__X i ___X,__X_ i X__X,___X i _X__,____ i X_X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i _X__,_X_X i ____,__X_ i X__X,___X i _X__,____ i X__X, + X__X i __X_,X__X i ___X,_X__ i X___,X__X i ___X,__X_ i X__X,___X i _X__,____ i X_X_,_X_X i ____,__X_ i X__X,_X__ i ____,X_X_ i _X__,_X_X i ____,__X_ i X__X,___X i _X__,____ i X__X, + X___ i XX__,XXX_ i XXXX,__XX i ____,_XX_ i ____,XX__ i _XX_,XXX_ i __XX,XXXX i ___X,X___ i XXXX,XX__ i _XX_,__XX i XXXX,___X i X_XX,X___ i XXXX,XX__ i _XX_,XXX_ i __XX,XXXX i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,XXXX i XXXX,X___ i XXXX,XX__ i _XXX,XXXX i XX__,_XXX i XXX_,__XX i XXXX,___X i XXXX,X___ i ____,__XX i XXXX,___X i X___,XXXX i XX__,_XXX i XXX_,__XX i XXXX,____ i ___X, + X___ i ___X,____ i ____,_X_X i ____,__X_ i X___,____ i __X_,X___ i ___X,_X__ i ____,X_X_ i ____,_X__ i ____,_X__ i ____,X_X_ i _X_X,____ i __X_,X___ i ___X,_X__ i ____,X___ i ___X, + X___ i ___X,____ i ____,_X_X i ____,__X_ i X___,____ i __X_,X___ i ___X,_X__ i ____,X_X_ i ____,_X__ i ____,_X__ i ____,X_X_ i _X_X,____ i __X_,X___ i ___X,_X__ i ____,X___ i ___X, + X___ i ___X,____ i ____,_X_X i ____,__X_ i X___,____ i __X_,X___ i ___X,_X__ i ____,X_X_ i ____,_X__ i ____,_X__ i XX__,X_X_ i _X_X,____ i __X_,X___ i ___X,_X__ i ____,X___ i ___X, + X___ i ___X,__X_ i __X_,_X_X i __X_,__X_ i X__X,___X i __X_,X__X i XXX_,_X__ i X___,X__X i X__X,X___ i ____,_X__ i ____,X_X_ i _X__,XX__ i XX__,_XX_ i _XX_,_X__ i XXXX,____ i ___X, + X___ i ___X,__X_ i __X_,_X_X i __X_,__X_ i X__X,___X i __X_,X___ i ___X,_X__ i X___,X___ i X__X,____ i ____,_X__ i __XX,__X_ i _X__,_X__ i X___,__X_ i _X__,_X__ i ____,X___ i ___X, + X___ i ___X,__X_ i __X_,_X_X i __X_,__X_ i X__X,___X i __X_,X___ i ___X,_X__ i X___,X___ i X__X,____ i ____,_X__ i ____,X_X_ i _X__,_X__ i X___,__X_ i _X__,_X__ i ____,X___ i ___X, + X___ i ___X,__X_ i __X_,_X_X i __X_,__X_ i X__X,___X i __X_,X__X i XXX_,_X__ i X___,X___ i X__X,____ i ____,_X__ i XX__,X_X_ i _X__,_X__ i X___,__X_ i _X__,_X__ i XXXX,____ i ___X, + X___ i ___X,__X_ i __X_,_X_X i ____,__X_ i X__X,___X i __X_,X___ i ___X,_X__ i X___,X___ i X__X,____ i ____,_X__ i ____,X_X_ i _X__,_X__ i X___,__X_ i _X__,_X__ i ____,X___ i ___X, + X___ i ___X,__X_ i __X_,_X_X i ____,__X_ i X__X,___X i __X_,X___ i ___X,_X__ i X___,X___ i X__X,____ i ____,_X__ i ____,X_X_ i _X__,_X__ i X___,__X_ i _X__,_X__ i ____,X___ i ___X, + X___ i ____,XX_X i XX_X,X___ i XXXX,XX__ i _XX_,XXX_ i XX__,_XXX i XXX_,__XX i _XXX,____ i _XX_,____ i ____,__XX i XXXX,___X i X___,__XX i ____,___X i X___,__XX i XXXX,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + X___ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ___X, + _X__ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i __X_, + _X__ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i __X_, + __X_ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i _X__, + ___X i X___,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,____ i ____,___X i X___, + ____ i _XXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXXX i XXXX,XXX_ i ____, + +#undef i +}; + +/* lcd digits */ +const char lcd_digits[] = +{ + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,0,0,1,1,1,1,0,0,0, + 0,0,1,1,0,0,1,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,1,0,0,1,1,0,0, + 0,0,0,1,1,1,1,0,0,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,1,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,1,1,1,1,0, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,1,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,1,1,1,0,1,1,1,1,0, + 1,1,0,1,1,1,0,0,1,1, + 1,0,0,0,1,0,0,0,0,1, + 1,0,0,0,1,0,0,0,0,1, + 1,0,0,0,1,0,0,0,0,1, + 1,0,0,0,1,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,1,0,0,1,1, + 0,0,0,0,0,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,1,1,1,1,0, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,1,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,1,1,1,1,0, + 1,0,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,0,0,0,0,1, + 0,0,0,0,1,1,0,0,1,1, + 0,0,0,0,0,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,0,0,0,0,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,1,1,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,1,1, + 0,1,1,1,1,1,1,1,1,0, + + /* 10: - */ + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,1,1,1,1,0,0, + 0,1,0,0,0,0,0,0,1,0, + 0,1,0,0,0,0,0,0,1,0, + 0,1,0,0,0,0,0,0,1,0, + 0,0,1,1,1,1,1,1,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + + /* 11: / */ + 0,0,0,0,1,1,1,1,0,0, + 0,0,0,1,0,0,0,0,1,0, + 0,0,0,1,0,0,0,0,1,0, + 0,0,0,1,0,0,0,0,1,0, + 0,0,0,1,0,0,0,0,1,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,1,0,0, + 0,1,0,0,0,0,1,0,0,0, + 0,1,0,0,0,0,1,0,0,0, + 0,1,0,0,0,0,1,0,0,0, + 0,1,0,0,0,0,1,0,0,0, + 0,0,1,1,1,1,0,0,0,0, + + /* 12: ? */ + 0,1,1,1,1,1,1,1,1,0, + 1,1,0,0,0,0,0,0,1,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 0,1,1,1,1,1,0,0,0,1, + 1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1, + 1,1,1,1,1,1,1,1,1,0, + 1,0,0,0,0,1,0,0,0,0, + 1,0,0,0,0,1,0,0,0,0, + 1,0,0,0,0,1,0,0,0,0, + 1,1,0,0,1,1,0,0,0,0, + 0,1,1,1,1,0,0,0,0,0, + + /* 13: " " */ + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, +}; + +/* functions */ +void ConfigMenu(int Init); +void CleanUp(); +void PageInput(int Number); +void ColorKey(int); +void PageCatching(); +void CatchNextPage(int, int); +void GetNextPageOne(int up); +void GetNextSubPage(int offset); +void SwitchZoomMode(); +void SwitchScreenMode(int newscreenmode); +void SwitchTranspMode(); +void SwitchHintMode(); +void CreateLine25(); +void CopyBB2FB(); +void RenderCatchedPage(); +void RenderCharFB(int Char, tstPageAttr *Attribute); +void RenderCharBB(int Char, tstPageAttr *Attribute); +void RenderCharLCD(int Digit, int XPos, int YPos); +void RenderMessage(int Message); +void RenderPage(); +void DecodePage(); +void UpdateLCD(); +int Init(); +int GetNationalSubset(char *country_code); +int GetTeletextPIDs(); +int GetRCCode(); +int eval_triplet(int iOData, tstCachedPage *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + unsigned char *drcssubp, unsigned char *gdrcssubp, + signed char *endcol, tstPageAttr *attrPassive, unsigned char* pagedata); +void eval_object(int iONr, tstCachedPage *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + tObjType ObjType, unsigned char* pagedata); + + +#endif diff --git a/lib/libtuxtxt/tuxtxt_common.h b/lib/libtuxtxt/tuxtxt_common.h new file mode 100644 index 000000000..d7a346ef7 --- /dev/null +++ b/lib/libtuxtxt/tuxtxt_common.h @@ -0,0 +1,1098 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#if TUXTXT_COMPRESS == 1 +#include +#endif + +#include + +tuxtxt_cache_struct tuxtxt_cache; +static pthread_mutex_t tuxtxt_cache_lock = PTHREAD_MUTEX_INITIALIZER; +int tuxtxt_get_zipsize(int p,int sp) +{ + tstCachedPage* pg = tuxtxt_cache.astCachetable[p][sp]; + if (!pg) return 0; +#if TUXTXT_COMPRESS == 1 + return pg->ziplen; +#elif TUXTXT_COMPRESS == 2 + pthread_mutex_lock(&tuxtxt_cache_lock); + int zipsize = 0,i,j; + for (i = 0; i < 23*5; i++) + for (j = 0; j < 8; j++) + zipsize += pg->bitmask[i]>>j & 0x01; + + zipsize+=23*5;//bitmask + pthread_mutex_unlock(&tuxtxt_cache_lock); + return zipsize; +#else + return 23*40; +#endif +} +void tuxtxt_compress_page(int p, int sp, unsigned char* buffer) +{ + pthread_mutex_lock(&tuxtxt_cache_lock); + tstCachedPage* pg = tuxtxt_cache.astCachetable[p][sp]; + if (!pg) + { + printf("tuxtxt: trying to compress a not allocated page!!\n"); + pthread_mutex_unlock(&tuxtxt_cache_lock); + return; + } + +#if TUXTXT_COMPRESS == 1 + unsigned char pagecompressed[23*40]; + uLongf comprlen = 23*40; + if (compress2(pagecompressed,&comprlen,buffer,23*40,Z_BEST_SPEED) == Z_OK) + { + if (pg->pData) free(pg->pData);//realloc(pg->pData,j); realloc scheint nicht richtig zu funktionieren? + pg->pData = malloc(comprlen); + pg->ziplen = 0; + if (pg->pData) + { + pg->ziplen = comprlen; + memcpy(pg->pData,pagecompressed,comprlen); + } + } +#elif TUXTXT_COMPRESS == 2 + int i,j=0; + unsigned char cbuf[23*40]; + memset(pg->bitmask,0,sizeof(pg->bitmask)); + for (i = 0; i < 23*40; i++) + { + if (i && buffer[i] == buffer[i-1]) + continue; + pg->bitmask[i>>3] |= 0x80>>(i&0x07); + cbuf[j++]=buffer[i]; + } + if (pg->pData) free(pg->pData);//realloc(pg->pData,j); realloc scheint nicht richtig zu funktionieren? + pg->pData = (unsigned char*)malloc(j); + if (pg->pData) + { + memcpy(pg->pData,cbuf,j); + } + else + memset(pg->bitmask,0,sizeof(pg->bitmask)); + +#else + //if (pg->pData) + memcpy(pg->data,buffer,23*40); +#endif + pthread_mutex_unlock(&tuxtxt_cache_lock); + +} +void tuxtxt_decompress_page(int p, int sp, unsigned char* buffer) +{ + pthread_mutex_lock(&tuxtxt_cache_lock); + tstCachedPage* pg = tuxtxt_cache.astCachetable[p][sp]; + memset(buffer,' ',23*40); + if (!pg) + { + printf("tuxtxt: trying to decompress a not allocated page!!\n"); + pthread_mutex_unlock(&tuxtxt_cache_lock); + return; + } +#if TUXTXT_COMPRESS == 1 + if (pg->pData) + { + if (pg->ziplen) + { + uLongf comprlen = 23*40; + uncompress(buffer,&comprlen,pg->pData,pg->ziplen); + } + +#elif TUXTXT_COMPRESS == 2 + if (pg->pData) + { + int i,j=0; + char c=0x20; + for (i = 0; i < 23*40; i++) + { + if (pg->bitmask[i>>3] & 0x80>>(i&0x07)) + c = pg->pData[j++]; + buffer[i] = c; + } +#else + { + memcpy(buffer,pg->data,23*40); +#endif + } + pthread_mutex_unlock(&tuxtxt_cache_lock); +} +void tuxtxt_next_dec(int *i) /* skip to next decimal */ +{ + (*i)++; + + if ((*i & 0x0F) > 0x09) + *i += 0x06; + + if ((*i & 0xF0) > 0x90) + *i += 0x60; + + if (*i > 0x899) + *i = 0x100; +} + +void tuxtxt_prev_dec(int *i) /* counting down */ +{ + (*i)--; + + if ((*i & 0x0F) > 0x09) + *i -= 0x06; + + if ((*i & 0xF0) > 0x90) + *i -= 0x60; + + if (*i < 0x100) + *i = 0x899; +} + +int tuxtxt_is_dec(int i) +{ + return ((i & 0x00F) <= 9) && ((i & 0x0F0) <= 0x90); +} + +int tuxtxt_next_hex(int i) /* return next existing non-decimal page number */ +{ + int startpage = i; + if (startpage < 0x100) + startpage = 0x100; + + do + { + i++; + if (i > 0x8FF) + i = 0x100; + if (i == startpage) + break; + } while ((tuxtxt_cache.subpagetable[i] == 0xFF) || tuxtxt_is_dec(i)); + return i; +} +/* + * TOP-Text + * Info entnommen aus videotext-0.6.19991029, + * Copyright (c) 1994-96 Martin Buck + */ +void tuxtxt_decode_btt() +{ + /* basic top table */ + int i, current, b1, b2, b3, b4; + unsigned char btt[23*40]; + + if (tuxtxt_cache.subpagetable[0x1f0] == 0xff || 0 == tuxtxt_cache.astCachetable[0x1f0][tuxtxt_cache.subpagetable[0x1f0]]) /* not yet received */ + return; + tuxtxt_decompress_page(0x1f0,tuxtxt_cache.subpagetable[0x1f0],btt); + if (btt[799] == ' ') /* not completely received or error */ + return; + + current = 0x100; + for (i = 0; i < 800; i++) + { + b1 = btt[i]; + if (b1 == ' ') + b1 = 0; + else + { + b1 = dehamming[b1]; + if (b1 == 0xFF) /* hamming error in btt */ + { + btt[799] = ' '; /* mark btt as not received */ + return; + } + } + tuxtxt_cache.basictop[current] = b1; + tuxtxt_next_dec(¤t); + } + /* page linking table */ + tuxtxt_cache.maxadippg = -1; /* rebuild table of adip pages */ + for (i = 0; i < 10; i++) + { + b1 = dehamming[btt[800 + 8*i +0]]; + + if (b1 == 0xE) + continue; /* unused */ + else if (b1 == 0xF) + break; /* end */ + + b4 = dehamming[btt[800 + 8*i +7]]; + + if (b4 != 2) /* only adip, ignore multipage (1) */ + continue; + + b2 = dehamming[btt[800 + 8*i +1]]; + b3 = dehamming[btt[800 + 8*i +2]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) + { + printf("TuxTxt \n", i); + btt[799] = ' '; /* mark btt as not received */ + return; + } + + b1 = b1<<8 | b2<<4 | b3; /* page number */ + tuxtxt_cache.adippg[++tuxtxt_cache.maxadippg] = b1; + } +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + tuxtxt_cache.bttok = 1; +} + +void tuxtxt_decode_adip() /* additional information table */ +{ + int i, p, j, b1, b2, b3, charfound; + unsigned char padip[23*40]; + + for (i = 0; i <= tuxtxt_cache.maxadippg; i++) + { + p = tuxtxt_cache.adippg[i]; + if (!p || tuxtxt_cache.subpagetable[p] == 0xff || 0 == tuxtxt_cache.astCachetable[p][tuxtxt_cache.subpagetable[p]]) /* not cached (avoid segfault) */ + continue; + + tuxtxt_decompress_page(p,tuxtxt_cache.subpagetable[p],padip); + for (j = 0; j < 44; j++) + { + b1 = dehamming[padip[20*j+0]]; + if (b1 == 0xE) + continue; /* unused */ + + if (b1 == 0xF) + break; /* end */ + + b2 = dehamming[padip[20*j+1]]; + b3 = dehamming[padip[20*j+2]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) + { + printf("TuxTxt \n", p, j, + padip[20*j+0], + padip[20*j+1], + padip[20*j+2], + b1, b2, b3 + ); + return; + } + + if (b1>8 || b2>9 || b3>9) /* ignore extries with invalid or hex page numbers */ + { + continue; + } + + b1 = b1<<8 | b2<<4 | b3; /* page number */ + charfound = 0; /* flag: no printable char found */ + + for (b2 = 11; b2 >= 0; b2--) + { + b3 = deparity[padip[20*j + 8 + b2]]; + if (b3 < ' ') + b3 = ' '; + + if (b3 == ' ' && !charfound) + tuxtxt_cache.adip[b1][b2] = '\0'; + else + { + tuxtxt_cache.adip[b1][b2] = b3; + charfound = 1; + } + } + } /* next link j */ + tuxtxt_cache.adippg[i] = 0; /* completely decoded: clear entry */ +#if TUXTXT_DEBUG + printf("TuxTxt \n", p); +#endif + } /* next adip page i */ + + while (!tuxtxt_cache.adippg[tuxtxt_cache.maxadippg] && (tuxtxt_cache.maxadippg >= 0)) /* and shrink table */ + tuxtxt_cache.maxadippg--; +} +/****************************************************************************** + * GetSubPage * + ******************************************************************************/ +int tuxtxt_GetSubPage(int page, int subpage, int offset) +{ + int loop; + + + for (loop = subpage + offset; loop != subpage; loop += offset) + { + if (loop < 0) + loop = 0x79; + else if (loop > 0x79) + loop = 0; + if (loop == subpage) + break; + + if (tuxtxt_cache.astCachetable[page][loop]) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n", page, subpage); +#endif + return loop; + } + } + +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + return subpage; +} + +/****************************************************************************** + * clear_cache * + ******************************************************************************/ + +void tuxtxt_clear_cache() +{ + pthread_mutex_lock(&tuxtxt_cache_lock); + int clear_page, clear_subpage, d26; + tuxtxt_cache.maxadippg = -1; + tuxtxt_cache.bttok = 0; + tuxtxt_cache.cached_pages = 0; + tuxtxt_cache.page_receiving = -1; + tuxtxt_cache.vtxtpid = -1; + memset(&tuxtxt_cache.subpagetable, 0xFF, sizeof(tuxtxt_cache.subpagetable)); + memset(&tuxtxt_cache.basictop, 0, sizeof(tuxtxt_cache.basictop)); + memset(&tuxtxt_cache.adip, 0, sizeof(tuxtxt_cache.adip)); + memset(&tuxtxt_cache.flofpages, 0 , sizeof(tuxtxt_cache.flofpages)); + memset(&tuxtxt_cache.timestring, 0x20, 8); + unsigned char magazine; + for (magazine = 1; magazine < 9; magazine++) + { + tuxtxt_cache.current_page [magazine] = -1; + tuxtxt_cache.current_subpage [magazine] = -1; + } + + for (clear_page = 0; clear_page < 0x900; clear_page++) + for (clear_subpage = 0; clear_subpage < 0x80; clear_subpage++) + if (tuxtxt_cache.astCachetable[clear_page][clear_subpage]) + { + tstPageinfo *p = &(tuxtxt_cache.astCachetable[clear_page][clear_subpage]->pageinfo); + if (p->p24) + free(p->p24); + if (p->ext) + { + if (p->ext->p27) + free(p->ext->p27); + for (d26=0; d26 < 16; d26++) + if (p->ext->p26[d26]) + free(p->ext->p26[d26]); + free(p->ext); + } +#if TUXTXT_COMPRESS >0 + if (tuxtxt_cache.astCachetable[clear_page][clear_subpage]->pData) + free(tuxtxt_cache.astCachetable[clear_page][clear_subpage]->pData); +#endif + free(tuxtxt_cache.astCachetable[clear_page][clear_subpage]); + tuxtxt_cache.astCachetable[clear_page][clear_subpage] = 0; + } + for (clear_page = 0; clear_page < 9; clear_page++) + { + if (tuxtxt_cache.astP29[clear_page]) + { + if (tuxtxt_cache.astP29[clear_page]->p27) + free(tuxtxt_cache.astP29[clear_page]->p27); + for (d26=0; d26 < 16; d26++) + if (tuxtxt_cache.astP29[clear_page]->p26[d26]) + free(tuxtxt_cache.astP29[clear_page]->p26[d26]); + free(tuxtxt_cache.astP29[clear_page]); + tuxtxt_cache.astP29[clear_page] = 0; + } + tuxtxt_cache.current_page [clear_page] = -1; + tuxtxt_cache.current_subpage [clear_page] = -1; + } + memset(&tuxtxt_cache.astCachetable, 0, sizeof(tuxtxt_cache.astCachetable)); + memset(&tuxtxt_cache.astP29, 0, sizeof(tuxtxt_cache.astP29)); +#if TUXTXT_DEBUG + printf("TuxTxt cache cleared\n"); +#endif + pthread_mutex_unlock(&tuxtxt_cache_lock); +} +/****************************************************************************** + * init_demuxer * + ******************************************************************************/ +static cDemux * dmx = NULL; +int tuxtxt_init_demuxer() +{ + + if(dmx == NULL) { + dmx = new cDemux(0); + dmx->Open(DMX_PES_CHANNEL, NULL, 2* 3008 * 62 /*64*1024*/); + } +#if TUXTXT_DEBUG + printf("TuxTxt: initialized\n"); +#endif + /* init successfull */ + + return 1; +} +/****************************************************************************** + * CacheThread support functions * + ******************************************************************************/ + +void tuxtxt_decode_p2829(unsigned char *vtxt_row, tstExtData **ptExtData) +{ + int bitsleft, colorindex; + unsigned char *p; + int t1 = deh24(&vtxt_row[7-4]); + int t2 = deh24(&vtxt_row[10-4]); + + if (t1 < 0 || t2 < 0) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + return; + } + + if (!(*ptExtData)) + (*ptExtData) = (tstExtData*)calloc(1, sizeof(tstExtData)); + if (!(*ptExtData)) + return; + + (*ptExtData)->p28Received = 1; + (*ptExtData)->DefaultCharset = (t1>>7) & 0x7f; + (*ptExtData)->SecondCharset = ((t1>>14) & 0x0f) | ((t2<<4) & 0x70); + (*ptExtData)->LSP = !!(t2 & 0x08); + (*ptExtData)->RSP = !!(t2 & 0x10); + (*ptExtData)->SPL25 = !!(t2 & 0x20); + (*ptExtData)->LSPColumns = (t2>>6) & 0x0f; + + bitsleft = 8; /* # of bits not evaluated in val */ + t2 >>= 10; /* current data */ + p = &vtxt_row[13-4]; /* pointer to next data triplet */ + for (colorindex = 0; colorindex < 16; colorindex++) + { + if (bitsleft < 12) + { + t2 |= deh24(p) << bitsleft; + if (t2 < 0) /* hamming error */ + break; + p += 3; + bitsleft += 18; + } + (*ptExtData)->bgr[colorindex] = t2 & 0x0fff; + bitsleft -= 12; + t2 >>= 12; + } + if (t2 < 0 || bitsleft != 14) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n", t2, bitsleft); +#endif + (*ptExtData)->p28Received = 0; + return; + } + (*ptExtData)->DefScreenColor = t2 & 0x1f; + t2 >>= 5; + (*ptExtData)->DefRowColor = t2 & 0x1f; + (*ptExtData)->BlackBgSubst = !!(t2 & 0x20); + t2 >>= 6; + (*ptExtData)->ColorTableRemapping = t2 & 0x07; +} + +void tuxtxt_erase_page(int magazine) +{ + pthread_mutex_lock(&tuxtxt_cache_lock); + tstCachedPage* pg = tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]; + if (pg) + { + memset(&(pg->pageinfo), 0, sizeof(tstPageinfo)); /* struct pageinfo */ + memset(pg->p0, ' ', 24); +#if TUXTXT_COMPRESS == 1 + if (pg->pData) {free(pg->pData); pg->pData = NULL;} +#elif TUXTXT_COMPRESS == 2 + memset(pg->bitmask, 0, 23*5); +#else + memset(pg->data, ' ', 23*40); +#endif + } + pthread_mutex_unlock(&tuxtxt_cache_lock); +} + +void tuxtxt_allocate_cache(int magazine) +{ + /* check cachetable and allocate memory if needed */ + if (tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]] == 0) + { + + tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]] = (tstCachedPage*) malloc(sizeof(tstCachedPage)); + if (tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]] ) + { +#if TUXTXT_COMPRESS >0 + tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->pData = 0; +#endif + tuxtxt_erase_page(magazine); + tuxtxt_cache.cached_pages++; + } + } +} +/****************************************************************************** + * CacheThread * + ******************************************************************************/ +//#define TUXTXT_DEBUG 1 //FIXME +static int stop_cache = 0; +void *tuxtxt_CacheThread(void *arg) +{ + const unsigned char rev_lut[32] = { + 0x00,0x08,0x04,0x0c, /* upper nibble */ + 0x02,0x0a,0x06,0x0e, + 0x01,0x09,0x05,0x0d, + 0x03,0x0b,0x07,0x0f, + 0x00,0x80,0x40,0xc0, /* lower nibble */ + 0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0, + 0x30,0xb0,0x70,0xf0 }; + unsigned char pes_packet[184*20]; + unsigned char vtxt_row[42]; + int line, byte/*, bit*/; + int b1, b2, b3, b4; + int packet_number; + int doupdate=0; + unsigned char magazine = 0xff; + unsigned char pagedata[9][23*40]; + tstPageinfo *pageinfo_thread; + + printf("TuxTxt running thread...(%03x)\n",tuxtxt_cache.vtxtpid); + tuxtxt_cache.receiving = 1; + nice(3); + while (!stop_cache) + { + /* check stopsignal */ + pthread_testcancel(); + + if (!tuxtxt_cache.receiving) continue; + + /* read packet */ + ssize_t readcnt; + + readcnt = dmx->Read(pes_packet, sizeof (pes_packet), 1000); + //if (readcnt != sizeof(pes_packet)) + if ((readcnt <= 0) || (readcnt % 184)) + { +#if TUXTXT_DEBUG + if(readcnt > 0) + printf ("TuxTxt: readerror: %d\n", readcnt); +#endif + continue; + } + + /* analyze it */ + for (line = 0; line < readcnt/0x2e /*4*/; line++) + { + unsigned char *vtx_rowbyte = &pes_packet[line*0x2e]; + if ((vtx_rowbyte[1] == 0x2C) && (vtx_rowbyte[0] == 0x02 || vtx_rowbyte[0] == 0x03)) + { + /* clear rowbuffer */ + /* convert row from lsb to msb (begin with magazin number) */ + for (byte = 4; byte < 46; byte++) + { + unsigned char upper,lower; + upper = (vtx_rowbyte[byte] >> 4) & 0xf; + lower = vtx_rowbyte[byte] & 0xf; + vtxt_row[byte-4] = (rev_lut[upper]) | (rev_lut[lower+16]); + } + + /* get packet number */ + b1 = dehamming[vtxt_row[0]]; + b2 = dehamming[vtxt_row[1]]; + + if (b1 == 0xFF || b2 == 0xFF) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + + b1 &= 8; + + packet_number = b1>>3 | b2<<1; + + /* get magazine number */ + magazine = dehamming[vtxt_row[0]] & 7; + if (!magazine) magazine = 8; + + if (packet_number == 0 && tuxtxt_cache.current_page[magazine] != -1 && tuxtxt_cache.current_subpage[magazine] != -1) + tuxtxt_compress_page(tuxtxt_cache.current_page[magazine],tuxtxt_cache.current_subpage[magazine],pagedata[magazine]); + +//printf("********************** receiving packet %d page %03x subpage %02x\n",packet_number, tuxtxt_cache.current_page[magazine],tuxtxt_cache.current_subpage[magazine]);//FIXME + + /* analyze row */ + if (packet_number == 0) + { + /* get pagenumber */ + b2 = dehamming[vtxt_row[3]]; + b3 = dehamming[vtxt_row[2]]; + + if (b2 == 0xFF || b3 == 0xFF) + { + tuxtxt_cache.current_page[magazine] = tuxtxt_cache.page_receiving = -1; +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + + tuxtxt_cache.current_page[magazine] = tuxtxt_cache.page_receiving = magazine<<8 | b2<<4 | b3; + + if (b2 == 0x0f && b3 == 0x0f) + { + tuxtxt_cache.current_subpage[magazine] = -1; /* ?ff: ignore data transmissions */ + continue; + } + + /* get subpagenumber */ + b1 = dehamming[vtxt_row[7]]; + b2 = dehamming[vtxt_row[6]]; + b3 = dehamming[vtxt_row[5]]; + b4 = dehamming[vtxt_row[4]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF || b4 == 0xFF) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + tuxtxt_cache.current_subpage[magazine] = -1; + continue; + } + + b1 &= 3; + b3 &= 7; + + if (tuxtxt_is_dec(tuxtxt_cache.page_receiving)) /* ignore other subpage bits for hex pages */ + { +#if 0 /* ? */ + if (b1 != 0 || b2 != 0) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n", tuxtxt_cache.page_receiving, b1, b2, b3, b4); +#endif + tuxtxt_cache.current_subpage[magazine] = -1; + continue; + } + else +#endif + tuxtxt_cache.current_subpage[magazine] = b3<<4 | b4; + } + else + tuxtxt_cache.current_subpage[magazine] = b4; /* max 16 subpages for hex pages */ + + /* store current subpage for this page */ + tuxtxt_cache.subpagetable[tuxtxt_cache.current_page[magazine]] = tuxtxt_cache.current_subpage[magazine]; + + tuxtxt_allocate_cache(magazine); + tuxtxt_decompress_page(tuxtxt_cache.current_page[magazine],tuxtxt_cache.current_subpage[magazine],pagedata[magazine]); + pageinfo_thread = &(tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->pageinfo); + + if ((tuxtxt_cache.page_receiving & 0xff) == 0xfe) /* ?fe: magazine organization table (MOT) */ + pageinfo_thread->function = FUNC_MOT; + + /* check controlbits */ + if (dehamming[vtxt_row[5]] & 8) /* C4 -> erase page */ + { +#if TUXTXT_COMPRESS == 1 + tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->ziplen = 0; +#elif TUXTXT_COMPRESS == 2 + memset(tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->bitmask, 0, 23*5); +#else + memset(tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->data, ' ', 23*40); +#endif + memset(pagedata[magazine],' ', 23*40); + } + if (dehamming[vtxt_row[9]] & 8) /* C8 -> update page */ + doupdate = tuxtxt_cache.page_receiving; + + pageinfo_thread->boxed = !!(dehamming[vtxt_row[7]] & 0x0c); + + /* get country control bits */ + b1 = dehamming[vtxt_row[9]]; + if (b1 == 0xFF) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + } + else + { + pageinfo_thread->nationalvalid = 1; + pageinfo_thread->national = rev_lut[b1] & 0x07; + } + + /* check parity, copy line 0 to cache (start and end 8 bytes are not needed and used otherwise) */ + unsigned char *p = tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->p0; + for (byte = 10; byte < 42-8; byte++) + *p++ = deparity[vtxt_row[byte]]; + + if (!tuxtxt_is_dec(tuxtxt_cache.page_receiving)) + continue; /* valid hex page number: just copy headline, ignore timestring */ + + /* copy timestring */ + p = tuxtxt_cache.timestring; + for (; byte < 42; byte++) + *p++ = deparity[vtxt_row[byte]]; + } /* (packet_number == 0) */ + else if (packet_number == 29 && dehamming[vtxt_row[2]]== 0) /* packet 29/0 replaces 28/0 for a whole magazine */ + { + tuxtxt_decode_p2829(vtxt_row, &(tuxtxt_cache.astP29[magazine])); + } + else if (tuxtxt_cache.current_page[magazine] != -1 && tuxtxt_cache.current_subpage[magazine] != -1) + /* packet>0, 0 has been correctly received, buffer allocated */ + { + pageinfo_thread = &(tuxtxt_cache.astCachetable[tuxtxt_cache.current_page[magazine]][tuxtxt_cache.current_subpage[magazine]]->pageinfo); + /* pointer to current info struct */ + + if (packet_number <= 25) + { + unsigned char *p = NULL; + if (packet_number < 24) + p = pagedata[magazine] + 40*(packet_number-1); + else + { + if (!(pageinfo_thread->p24)) + pageinfo_thread->p24 = (unsigned char*) calloc(2, 40); + if (pageinfo_thread->p24) + p = pageinfo_thread->p24 + (packet_number - 24) * 40; + } + if (p) + { + if (tuxtxt_is_dec(tuxtxt_cache.current_page[magazine])) + for (byte = 2; byte < 42; byte++) + *p++ = deparity[vtxt_row[byte]]; /* check/remove parity bit */ + else if ((tuxtxt_cache.current_page[magazine] & 0xff) == 0xfe) + for (byte = 2; byte < 42; byte++) + *p++ = dehamming[vtxt_row[byte]]; /* decode hamming 8/4 */ + else /* other hex page: no parity check, just copy */ + memcpy(p, &vtxt_row[2], 40); + } + } + else if (packet_number == 27) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + + if (descode == 0xff) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + if (descode == 0) // reading FLOF-Pagelinks + { + b1 = dehamming[vtxt_row[0]]; + if (b1 != 0xff) + { + b1 &= 7; + + for (byte = 0; byte < FLOFSIZE; byte++) + { + b2 = dehamming[vtxt_row[4+byte*6]]; + b3 = dehamming[vtxt_row[3+byte*6]]; + + if (b2 != 0xff && b3 != 0xff) + { + b4 = ((b1 ^ (dehamming[vtxt_row[8+byte*6]]>>1)) & 6) | + ((b1 ^ (dehamming[vtxt_row[6+byte*6]]>>3)) & 1); + if (b4 == 0) + b4 = 8; + if (b2 <= 9 && b3 <= 9) + tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine] ][byte] = b4<<8 | b2<<4 | b3; + } + } + + /* copy last 2 links to adip for TOP-Index */ + if (pageinfo_thread->p24) /* packet 24 received */ + { + int a, a1, e=39, l=3; + char *p = (char*) pageinfo_thread->p24; + do + { + for (; + l >= 2 && 0 == tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine]][l]; + l--) + ; /* find used linkindex */ + for (; + e >= 1 && !isalnum(p[e]); + e--) + ; /* find end */ + for (a = a1 = e - 1; + a >= 0 && p[a] >= ' '; + a--) /* find start */ + if (p[a] > ' ') + a1 = a; /* first non-space */ + if (a >= 0 && l >= 2) + { + strncpy((char *) tuxtxt_cache.adip[tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine]][l]], + &p[a1], + 12); + if (e-a1 < 11) + tuxtxt_cache.adip[tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine]][l]][e-a1+1] = '\0'; +#if 0 //TUXTXT_DEBUG + printf(" %03x/%02x %d %d %d %d %03x %s\n", + tuxtxt_cache.current_page[magazine], tuxtxt_cache.current_subpage[magazine], + l, a, a1, e, + tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine]][l], + tuxtxt_cache.adip[tuxtxt_cache.flofpages[tuxtxt_cache.current_page[magazine]][l]] + ); +#endif + } + e = a - 1; + l--; + } while (l >= 2); + } + } + } + else if (descode == 4) /* level 2.5 links (ignore level 3.5 links of /4 and /5) */ + { + int i; + tstp27 *p; + + if (!pageinfo_thread->ext) + pageinfo_thread->ext = (tstExtData*) calloc(1, sizeof(tstExtData)); + if (!pageinfo_thread->ext) + continue; + if (!(pageinfo_thread->ext->p27)) + pageinfo_thread->ext->p27 = (tstp27*) calloc(4, sizeof(tstp27)); + if (!(pageinfo_thread->ext->p27)) + continue; + p = pageinfo_thread->ext->p27; + for (i = 0; i < 4; i++) + { + int d1 = deh24(&vtxt_row[6*i + 3]); + int d2 = deh24(&vtxt_row[6*i + 6]); + if (d1 < 0 || d2 < 0) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + p->local = i & 0x01; + p->drcs = !!(i & 0x02); + p->l25 = !!(d1 & 0x04); + p->l35 = !!(d1 & 0x08); + p->page = + (((d1 & 0x000003c0) >> 6) | + ((d1 & 0x0003c000) >> (14-4)) | + ((d1 & 0x00003800) >> (11-8))) ^ + (dehamming[vtxt_row[0]] << 8); + if (p->page < 0x100) + p->page += 0x800; + p->subpage = d2 >> 2; + if ((p->page & 0xff) == 0xff) + p->page = 0; + else if (p->page > 0x899) + { + // workaround for crash on RTL + printf("[TuxTxt] page > 0x899 ... ignore!!!!!!\n"); + continue; + } + else if (tuxtxt_cache.astCachetable[p->page][0]) /* link valid && linked page cached */ + { + tstPageinfo *pageinfo_link = &(tuxtxt_cache.astCachetable[p->page][0]->pageinfo); + if (p->local) + pageinfo_link->function = p->drcs ? FUNC_DRCS : FUNC_POP; + else + pageinfo_link->function = p->drcs ? FUNC_GDRCS : FUNC_GPOP; + } + p++; /* */ + } + } + } + + else if (packet_number == 26) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + + if (descode == 0xff) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + if (!pageinfo_thread->ext) + pageinfo_thread->ext = (tstExtData*) calloc(1, sizeof(tstExtData)); + if (!pageinfo_thread->ext) + continue; + if (!(pageinfo_thread->ext->p26[descode])) + pageinfo_thread->ext->p26[descode] = (unsigned char*) malloc(13 * 3); + if (pageinfo_thread->ext->p26[descode]) + memcpy(pageinfo_thread->ext->p26[descode], &vtxt_row[3], 13 * 3); +#if 0//TUXTXT_DEBUG + int i, t, m; + + printf("P%03x/%02x %02d/%x", + tuxtxt_cache.current_page[magazine], tuxtxt_cache.current_subpage[magazine], + packet_number, dehamming[vtxt_row[2]]); + for (i=7-4; i <= 45-4; i+=3) /* dump all triplets */ + { + t = deh24(&vtxt_row[i]); /* mode/adr/data */ + m = (t>>6) & 0x1f; + printf(" M%02xA%02xD%03x", m, t & 0x3f, (t>>11) & 0x7f); + if (m == 0x1f) /* terminator */ + break; + } + putchar('\n'); +#endif + } + else if (packet_number == 28) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + + if (descode == 0xff) + { +#if TUXTXT_DEBUG + printf("TuxTxt \n"); +#endif + continue; + } + if (descode != 2) + { + int t1 = deh24(&vtxt_row[7-4]); + pageinfo_thread->function = t1 & 0x0f; + if (!pageinfo_thread->nationalvalid) + { + pageinfo_thread->nationalvalid = 1; + pageinfo_thread->national = (t1>>4) & 0x07; + } + } + + switch (descode) /* designation code */ + { + case 0: /* basic level 1 page */ + { + tuxtxt_decode_p2829(vtxt_row, &(pageinfo_thread->ext)); + break; + } + case 1: /* G0/G1 designation for older decoders, level 3.5: DCLUT4/16, colors for multicolored bitmaps */ + { + break; /* ignore */ + } + case 2: /* page key */ + { + break; /* ignore */ + } + case 3: /* types of PTUs in DRCS */ + { + break; /* TODO */ + } + case 4: /* CLUTs 0/1, only level 3.5 */ + { + break; /* ignore */ + } + default: + { + break; /* invalid, ignore */ + } + } /* switch designation code */ + } + else if (packet_number == 30) + { +#if 0//TUXTXT_DEBUG + int i; + + printf("p%03x/%02x %02d/%x ", + tuxtxt_cache.current_page[magazine], tuxtxt_cache.current_subpage[magazine], + packet_number, dehamming[vtxt_row[2]]); + for (i=26-4; i <= 45-4; i++) /* station ID */ + putchar(deparity[vtxt_row[i]]); + putchar('\n'); +#endif + } + } + /* set update flag */ + if (tuxtxt_cache.current_page[magazine] == tuxtxt_cache.page && tuxtxt_cache.current_subpage[magazine] != -1) + { + tuxtxt_compress_page(tuxtxt_cache.current_page[magazine],tuxtxt_cache.current_subpage[magazine],pagedata[magazine]); + tuxtxt_cache.pageupdate = 1+(doupdate == tuxtxt_cache.page ? 1: 0); + doupdate=0; + if (!tuxtxt_cache.zap_subpage_manual) + tuxtxt_cache.subpage = tuxtxt_cache.current_subpage[magazine]; + } + } +#if 0 + else + printf("line %d row %X %X, continue\n", line, vtx_rowbyte[0], vtx_rowbyte[1]); +#endif + } + } + return 0; +} +/****************************************************************************** + * start_thread * + ******************************************************************************/ +int tuxtxt_start_thread() +{ + if (tuxtxt_cache.vtxtpid == -1) return 0; + + + tuxtxt_cache.thread_starting = 1; + tuxtxt_init_demuxer(); + + dmx->pesFilter(tuxtxt_cache.vtxtpid); + dmx->Start(); + stop_cache = 0; + + /* create decode-thread */ + if (pthread_create(&tuxtxt_cache.thread_id, NULL, tuxtxt_CacheThread, NULL) != 0) + { + perror("TuxTxt "); + tuxtxt_cache.thread_starting = 0; + return 0; + } +#if 1//TUXTXT_DEBUG + printf("TuxTxt service started %x\n", tuxtxt_cache.vtxtpid); +#endif + tuxtxt_cache.receiving = 1; + tuxtxt_cache.thread_starting = 0; + return 1; +} +/****************************************************************************** + * stop_thread * + ******************************************************************************/ + +int tuxtxt_stop_thread() +{ + /* stop decode-thread */ + if (tuxtxt_cache.thread_id != 0) + { +#if 0 + if (pthread_cancel(tuxtxt_cache.thread_id) != 0) + { + perror("TuxTxt "); + return 0; + } +#endif + stop_cache = 1; + if (pthread_join(tuxtxt_cache.thread_id, &tuxtxt_cache.thread_result) != 0) + { + perror("TuxTxt "); + return 0; + } + tuxtxt_cache.thread_id = 0; + } + if(dmx) { + dmx->Stop(); + delete dmx; + dmx = NULL; + } +#if 0 + if (tuxtxt_cache.dmx != -1) + { + //ioctl(tuxtxt_cache.dmx, DMX_STOP); + +// close(tuxtxt_cache.dmx); + } +// tuxtxt_cache.dmx = -1; +#endif +#if 1//TUXTXT_DEBUG + printf("TuxTxt stopped service %x\n", tuxtxt_cache.vtxtpid); +#endif + return 1; +} diff --git a/lib/libtuxtxt/tuxtxt_def.h b/lib/libtuxtxt/tuxtxt_def.h new file mode 100644 index 000000000..255db1f74 --- /dev/null +++ b/lib/libtuxtxt/tuxtxt_def.h @@ -0,0 +1,370 @@ +/****************************************************************************** + * definitions for plugin and lib * + ******************************************************************************/ +#define TUXTXT_COMPRESS 2 // compress page data: 0 no compression, 1 with zlib, 2 with own algorithm + +#include + +#define FLOFSIZE 4 + +#define PAGESIZE (40*25) + + +enum /* page function */ +{ + FUNC_LOP = 0, /* Basic Level 1 Teletext page (LOP) */ + FUNC_DATA, /* Data broadcasting page coded according to EN 300 708 [2] clause 4 */ + FUNC_GPOP, /* Global Object definition page (GPOP) - (see clause 10.5.1) */ + FUNC_POP, /* Normal Object definition page (POP) - (see clause 10.5.1) */ + FUNC_GDRCS, /* Global DRCS downloading page (GDRCS) - (see clause 10.5.2) */ + FUNC_DRCS, /* Normal DRCS downloading page (DRCS) - (see clause 10.5.2) */ + FUNC_MOT, /* Magazine Organization table (MOT) - (see clause 10.6) */ + FUNC_MIP, /* Magazine Inventory page (MIP) - (see clause 11.3) */ + FUNC_BTT, /* Basic TOP table (BTT) } */ + FUNC_AIT, /* Additional Information Table (AIT) } (see clause 11.2) */ + FUNC_MPT, /* Multi-page table (MPT) } */ + FUNC_MPTEX, /* Multi-page extension table (MPT-EX) } */ + FUNC_TRIGGER /* Page contain trigger messages defined according to [8] */ +}; + + +/* struct for (G)POP/(G)DRCS links for level 2.5, allocated at reception of p27/4 or /5, initialized with 0 after allocation */ +typedef struct +{ + short page; /* linked page number */ + unsigned short subpage; /* 1 bit for each needed (1) subpage */ + unsigned char l25:1; /* 1: page required at level 2.5 */ + unsigned char l35:1; /* 1: page required at level 3.5 */ + unsigned char drcs:1; /* 1: link to (G)DRCS, 0: (G)POP */ + unsigned char local:1; /* 1: global (G*), 0: local */ +} tstp27; + +/* struct for extension data for level 2.5, allocated at reception, initialized with 0 after allocation */ +typedef struct +{ + unsigned char *p26[16]; /* array of pointers to max. 16 designation codes of packet 26 */ + tstp27 *p27; /* array of 4 structs for (G)POP/(G)DRCS links for level 2.5 */ + unsigned short bgr[16]; /* CLUT 2+3, 2*8 colors, 0x0bgr */ + unsigned char DefaultCharset:7; /* default G0/G2 charset + national option */ + unsigned char LSP:1; /* 1: left side panel to be displayed */ + unsigned char SecondCharset:7; /* second G0 charset */ + unsigned char RSP:1; /* 1: right side panel to be displayed */ + unsigned char DefScreenColor:5; /* default screen color (above and below lines 0..24) */ + unsigned char ColorTableRemapping:3; /* 1: index in table of CLUTs to use */ + unsigned char DefRowColor:5; /* default row color (left and right to lines 0..24) */ + unsigned char BlackBgSubst:1; /* 1: substitute black background (as result of start-of-line or 1c, not 00/10+1d) */ + unsigned char SPL25:1; /* 1: side panel required at level 2.5 */ + unsigned char p28Received:1; /* 1: extension data valid (p28/0 received) */ + unsigned char LSPColumns:4; /* number of columns in left side panel, 0->16, rsp=16-lsp */ +} tstExtData; + + +/* struct for pageinfo, max. 16 Bytes, at beginning of each cached page buffer, initialized with 0 after allocation */ +typedef struct +{ + unsigned char *p24; /* pointer to lines 25+26 (packets 24+25) (2*40 bytes) for FLOF or level 2.5 data */ + tstExtData *ext; /* pointer to array[16] of data for level 2.5 */ + unsigned char boxed :1; /* p0: boxed (newsflash or subtitle) */ + unsigned char nationalvalid :1; /* p0: national option character subset is valid (no biterror detected) */ + unsigned char national :3; /* p0: national option character subset */ + unsigned char function :3; /* p28/0: page function */ +} tstPageinfo; + +/* one cached page: struct for pageinfo, 24 lines page data */ +typedef struct +{ + tstPageinfo pageinfo; + unsigned char p0[24]; /* packet 0: center of headline */ +#if TUXTXT_COMPRESS == 1 + unsigned char * pData;/* packet 1-23 */ + unsigned short ziplen; +#elif TUXTXT_COMPRESS == 2 + unsigned char * pData;/* packet 1-23 */ + unsigned char bitmask[23*5]; +#else + unsigned char data[23*40]; /* packet 1-23 */ +#endif +} tstCachedPage; + +/* main data structure */ +typedef struct +{ + short flofpages[0x900][FLOFSIZE]; + unsigned char adip[0x900][13]; + unsigned char subpagetable[0x900]; + int dmx; + int vtxtpid; + int cached_pages, page, subpage, pageupdate,page_receiving, current_page[9], current_subpage[9]; + int receiving, thread_starting, zap_subpage_manual; + char bttok; + int adippg[10]; + int maxadippg; + unsigned char basictop[0x900]; + + unsigned char timestring[8]; + /* cachetable for packets 29 (one for each magazine) */ + tstExtData *astP29[9]; + /* cachetable */ + tstCachedPage *astCachetable[0x900][0x80]; + + pthread_t thread_id; + void *thread_result; +} tuxtxt_cache_struct; + +/* hamming table */ +const unsigned char dehamming[] = +{ + 0x01, 0xFF, 0x01, 0x01, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x02, 0x01, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, + 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x06, 0xFF, 0xFF, 0x0B, 0xFF, 0x00, 0x03, 0xFF, + 0xFF, 0x0C, 0x01, 0xFF, 0x04, 0xFF, 0xFF, 0x07, 0x06, 0xFF, 0xFF, 0x07, 0xFF, 0x07, 0x07, 0x07, + 0x06, 0xFF, 0xFF, 0x05, 0xFF, 0x00, 0x0D, 0xFF, 0x06, 0x06, 0x06, 0xFF, 0x06, 0xFF, 0xFF, 0x07, + 0xFF, 0x02, 0x01, 0xFF, 0x04, 0xFF, 0xFF, 0x09, 0x02, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0x03, 0xFF, + 0x08, 0xFF, 0xFF, 0x05, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x02, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0x03, + 0x04, 0xFF, 0xFF, 0x05, 0x04, 0x04, 0x04, 0xFF, 0xFF, 0x02, 0x0F, 0xFF, 0x04, 0xFF, 0xFF, 0x07, + 0xFF, 0x05, 0x05, 0x05, 0x04, 0xFF, 0xFF, 0x05, 0x06, 0xFF, 0xFF, 0x05, 0xFF, 0x0E, 0x03, 0xFF, + 0xFF, 0x0C, 0x01, 0xFF, 0x0A, 0xFF, 0xFF, 0x09, 0x0A, 0xFF, 0xFF, 0x0B, 0x0A, 0x0A, 0x0A, 0xFF, + 0x08, 0xFF, 0xFF, 0x0B, 0xFF, 0x00, 0x0D, 0xFF, 0xFF, 0x0B, 0x0B, 0x0B, 0x0A, 0xFF, 0xFF, 0x0B, + 0x0C, 0x0C, 0xFF, 0x0C, 0xFF, 0x0C, 0x0D, 0xFF, 0xFF, 0x0C, 0x0F, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, + 0xFF, 0x0C, 0x0D, 0xFF, 0x0D, 0xFF, 0x0D, 0x0D, 0x06, 0xFF, 0xFF, 0x0B, 0xFF, 0x0E, 0x0D, 0xFF, + 0x08, 0xFF, 0xFF, 0x09, 0xFF, 0x09, 0x09, 0x09, 0xFF, 0x02, 0x0F, 0xFF, 0x0A, 0xFF, 0xFF, 0x09, + 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0xFF, 0x09, 0x08, 0xFF, 0xFF, 0x0B, 0xFF, 0x0E, 0x03, 0xFF, + 0xFF, 0x0C, 0x0F, 0xFF, 0x04, 0xFF, 0xFF, 0x09, 0x0F, 0xFF, 0x0F, 0x0F, 0xFF, 0x0E, 0x0F, 0xFF, + 0x08, 0xFF, 0xFF, 0x05, 0xFF, 0x0E, 0x0D, 0xFF, 0xFF, 0x0E, 0x0F, 0xFF, 0x0E, 0x0E, 0xFF, 0x0E +}; + +/* odd parity table, error=0x20 (space) */ +const unsigned char deparity[] = +{ + ' ' , 0x01, 0x02, ' ' , 0x04, ' ' , ' ' , 0x07, 0x08, ' ' , ' ' , 0x0b, ' ' , 0x0d, 0x0e, ' ' , + 0x10, ' ' , ' ' , 0x13, ' ' , 0x15, 0x16, ' ' , ' ' , 0x19, 0x1a, ' ' , 0x1c, ' ' , ' ' , 0x1f, + 0x20, ' ' , ' ' , 0x23, ' ' , 0x25, 0x26, ' ' , ' ' , 0x29, 0x2a, ' ' , 0x2c, ' ' , ' ' , 0x2f, + ' ' , 0x31, 0x32, ' ' , 0x34, ' ' , ' ' , 0x37, 0x38, ' ' , ' ' , 0x3b, ' ' , 0x3d, 0x3e, ' ' , + 0x40, ' ' , ' ' , 0x43, ' ' , 0x45, 0x46, ' ' , ' ' , 0x49, 0x4a, ' ' , 0x4c, ' ' , ' ' , 0x4f, + ' ' , 0x51, 0x52, ' ' , 0x54, ' ' , ' ' , 0x57, 0x58, ' ' , ' ' , 0x5b, ' ' , 0x5d, 0x5e, ' ' , + ' ' , 0x61, 0x62, ' ' , 0x64, ' ' , ' ' , 0x67, 0x68, ' ' , ' ' , 0x6b, ' ' , 0x6d, 0x6e, ' ' , + 0x70, ' ' , ' ' , 0x73, ' ' , 0x75, 0x76, ' ' , ' ' , 0x79, 0x7a, ' ' , 0x7c, ' ' , ' ' , 0x7f, + 0x00, ' ' , ' ' , 0x03, ' ' , 0x05, 0x06, ' ' , ' ' , 0x09, 0x0a, ' ' , 0x0c, ' ' , ' ' , 0x0f, + ' ' , 0x11, 0x12, ' ' , 0x14, ' ' , ' ' , 0x17, 0x18, ' ' , ' ' , 0x1b, ' ' , 0x1d, 0x1e, ' ' , + ' ' , 0x21, 0x22, ' ' , 0x24, ' ' , ' ' , 0x27, 0x28, ' ' , ' ' , 0x2b, ' ' , 0x2d, 0x2e, ' ' , + 0x30, ' ' , ' ' , 0x33, ' ' , 0x35, 0x36, ' ' , ' ' , 0x39, 0x3a, ' ' , 0x3c, ' ' , ' ' , 0x3f, + ' ' , 0x41, 0x42, ' ' , 0x44, ' ' , ' ' , 0x47, 0x48, ' ' , ' ' , 0x4b, ' ' , 0x4d, 0x4e, ' ' , + 0x50, ' ' , ' ' , 0x53, ' ' , 0x55, 0x56, ' ' , ' ' , 0x59, 0x5a, ' ' , 0x5c, ' ' , ' ' , 0x5f, + 0x60, ' ' , ' ' , 0x63, ' ' , 0x65, 0x66, ' ' , ' ' , 0x69, 0x6a, ' ' , 0x6c, ' ' , ' ' , 0x6f, + ' ' , 0x71, 0x72, ' ' , 0x74, ' ' , ' ' , 0x77, 0x78, ' ' , ' ' , 0x7b, ' ' , 0x7d, 0x7e, ' ' , +}; + +#if 1 /* lookup-table algorithm for decoding Hamming 24/18, credits to: */ +/* + * libzvbi - Error correction functions + * + * Copyright (C) 2001 Michael H. Schimek + * + * Based on code from AleVT 1.5.1 + * Copyright (C) 1998, 1999 Edgar Toernig + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * [AleVT] + * + * This table generates the parity checks for hamm24/18 decoding. + * Bit 0 is for test A, 1 for B, ... + * + * Thanks to R. Gancarz for this fine table *g* + */ +const unsigned char hamm24par[3][256] = { + { + /* Parities of first byte */ + 0, 33, 34, 3, 35, 2, 1, 32, 36, 5, 6, 39, 7, 38, 37, 4, + 37, 4, 7, 38, 6, 39, 36, 5, 1, 32, 35, 2, 34, 3, 0, 33, + 38, 7, 4, 37, 5, 36, 39, 6, 2, 35, 32, 1, 33, 0, 3, 34, + 3, 34, 33, 0, 32, 1, 2, 35, 39, 6, 5, 36, 4, 37, 38, 7, + 39, 6, 5, 36, 4, 37, 38, 7, 3, 34, 33, 0, 32, 1, 2, 35, + 2, 35, 32, 1, 33, 0, 3, 34, 38, 7, 4, 37, 5, 36, 39, 6, + 1, 32, 35, 2, 34, 3, 0, 33, 37, 4, 7, 38, 6, 39, 36, 5, + 36, 5, 6, 39, 7, 38, 37, 4, 0, 33, 34, 3, 35, 2, 1, 32, + 40, 9, 10, 43, 11, 42, 41, 8, 12, 45, 46, 15, 47, 14, 13, 44, + 13, 44, 47, 14, 46, 15, 12, 45, 41, 8, 11, 42, 10, 43, 40, 9, + 14, 47, 44, 13, 45, 12, 15, 46, 42, 11, 8, 41, 9, 40, 43, 10, + 43, 10, 9, 40, 8, 41, 42, 11, 15, 46, 45, 12, 44, 13, 14, 47, + 15, 46, 45, 12, 44, 13, 14, 47, 43, 10, 9, 40, 8, 41, 42, 11, + 42, 11, 8, 41, 9, 40, 43, 10, 14, 47, 44, 13, 45, 12, 15, 46, + 41, 8, 11, 42, 10, 43, 40, 9, 13, 44, 47, 14, 46, 15, 12, 45, + 12, 45, 46, 15, 47, 14, 13, 44, 40, 9, 10, 43, 11, 42, 41, 8 + }, { + /* Parities of second byte */ + 0, 41, 42, 3, 43, 2, 1, 40, 44, 5, 6, 47, 7, 46, 45, 4, + 45, 4, 7, 46, 6, 47, 44, 5, 1, 40, 43, 2, 42, 3, 0, 41, + 46, 7, 4, 45, 5, 44, 47, 6, 2, 43, 40, 1, 41, 0, 3, 42, + 3, 42, 41, 0, 40, 1, 2, 43, 47, 6, 5, 44, 4, 45, 46, 7, + 47, 6, 5, 44, 4, 45, 46, 7, 3, 42, 41, 0, 40, 1, 2, 43, + 2, 43, 40, 1, 41, 0, 3, 42, 46, 7, 4, 45, 5, 44, 47, 6, + 1, 40, 43, 2, 42, 3, 0, 41, 45, 4, 7, 46, 6, 47, 44, 5, + 44, 5, 6, 47, 7, 46, 45, 4, 0, 41, 42, 3, 43, 2, 1, 40, + 48, 25, 26, 51, 27, 50, 49, 24, 28, 53, 54, 31, 55, 30, 29, 52, + 29, 52, 55, 30, 54, 31, 28, 53, 49, 24, 27, 50, 26, 51, 48, 25, + 30, 55, 52, 29, 53, 28, 31, 54, 50, 27, 24, 49, 25, 48, 51, 26, + 51, 26, 25, 48, 24, 49, 50, 27, 31, 54, 53, 28, 52, 29, 30, 55, + 31, 54, 53, 28, 52, 29, 30, 55, 51, 26, 25, 48, 24, 49, 50, 27, + 50, 27, 24, 49, 25, 48, 51, 26, 30, 55, 52, 29, 53, 28, 31, 54, + 49, 24, 27, 50, 26, 51, 48, 25, 29, 52, 55, 30, 54, 31, 28, 53, + 28, 53, 54, 31, 55, 30, 29, 52, 48, 25, 26, 51, 27, 50, 49, 24 + }, { + /* Parities of third byte */ + 63, 14, 13, 60, 12, 61, 62, 15, 11, 58, 57, 8, 56, 9, 10, 59, + 10, 59, 56, 9, 57, 8, 11, 58, 62, 15, 12, 61, 13, 60, 63, 14, + 9, 56, 59, 10, 58, 11, 8, 57, 61, 12, 15, 62, 14, 63, 60, 13, + 60, 13, 14, 63, 15, 62, 61, 12, 8, 57, 58, 11, 59, 10, 9, 56, + 8, 57, 58, 11, 59, 10, 9, 56, 60, 13, 14, 63, 15, 62, 61, 12, + 61, 12, 15, 62, 14, 63, 60, 13, 9, 56, 59, 10, 58, 11, 8, 57, + 62, 15, 12, 61, 13, 60, 63, 14, 10, 59, 56, 9, 57, 8, 11, 58, + 11, 58, 57, 8, 56, 9, 10, 59, 63, 14, 13, 60, 12, 61, 62, 15, + 31, 46, 45, 28, 44, 29, 30, 47, 43, 26, 25, 40, 24, 41, 42, 27, + 42, 27, 24, 41, 25, 40, 43, 26, 30, 47, 44, 29, 45, 28, 31, 46, + 41, 24, 27, 42, 26, 43, 40, 25, 29, 44, 47, 30, 46, 31, 28, 45, + 28, 45, 46, 31, 47, 30, 29, 44, 40, 25, 26, 43, 27, 42, 41, 24, + 40, 25, 26, 43, 27, 42, 41, 24, 28, 45, 46, 31, 47, 30, 29, 44, + 29, 44, 47, 30, 46, 31, 28, 45, 41, 24, 27, 42, 26, 43, 40, 25, + 30, 47, 44, 29, 45, 28, 31, 46, 42, 27, 24, 41, 25, 40, 43, 26, + 43, 26, 25, 40, 24, 41, 42, 27, 31, 46, 45, 28, 44, 29, 30, 47 + } +}; + +/* + * [AleVT] + * + * Table to extract the lower 4 bit from hamm24/18 encoded bytes + */ +const unsigned char hamm24val[256] = { + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 15, 15, 15, 15, 14, 14, 14, 14, 15, 15, 15, 15, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 15, 15, 15, 15, 14, 14, 14, 14, 15, 15, 15, 15 +}; + +const signed char hamm24err[64] = { + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/* + * [AleVT] + * + * Mapping from parity checks made by table hamm24par to faulty bit + * in the decoded 18 bit word. + */ +const unsigned int hamm24cor[64] = { + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00001, 0x00000, 0x00002, 0x00004, 0x00008, + 0x00000, 0x00010, 0x00020, 0x00040, 0x00080, 0x00100, 0x00200, 0x00400, + 0x00000, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000, 0x10000, 0x20000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, +}; + +/** + * @internal + * @param p Pointer to a byte triplet, bytes in transmission order, + * lsb first transmitted. + * + * This function decodes a Hamming 24/18 protected byte triplet + * as specified in ETS 300 706 8.3. + * + * @return + * Triplet data bits D18 [msb] ... D1 [lsb] or a negative + * value if the triplet contained incorrectable errors. + */ +static signed int deh24(unsigned char *p) +{ + int e = hamm24par[0][p[0]] + ^ hamm24par[1][p[1]] + ^ hamm24par[2][p[2]]; + + int x = hamm24val[p[0]] + + (p[1] & 127) * 16 + + (p[2] & 127) * 2048; + + return (x ^ hamm24cor[e]) | hamm24err[e]; +} + +#else /* my (rm) slower but smaller solution without lookup tables */ + +/* calc parity */ +int parity(int c) +{ + int n = 0; + for (; c; c &= (c-1)) /* reset least significant set bit */ + n ^= 1; + return n; +} + +#if 0 /* just for testing */ +/* encode hamming 24/18 */ +unsigned int ham24(unsigned int val) +{ + val = ((val & 0x000001) << 2) | + ((val & 0x00000e) << 3) | + ((val & 0x0007f0) << 4) | + ((val & 0x03f800) << 5); + val |= parity(val & 0x555554); + val |= parity(val & 0x666664) << 1; + val |= parity(val & 0x787870) << 3; + val |= parity(val & 0x007f00) << 7; + val |= parity(val & 0x7f0000) << 15; + val |= parity(val) << 23; + return val; +} +#endif + +/* decode hamming 24/18, error=-1 */ +signed int deh24(unsigned char *ph24) +{ + int h24 = *ph24 | (*(ph24+1)<<8) | (*(ph24+2)<<16); + int a = parity(h24 & 0x555555); + int f = parity(h24 & 0xaaaaaa) ^ a; + a |= (parity(h24 & 0x666666) << 1) | + (parity(h24 & 0x787878) << 2) | + (parity(h24 & 0x007f80) << 3) | + (parity(h24 & 0x7f8000) << 4); + if (a != 0x1f) + { + if (f) /* 2 biterrors */ + return -1; + else /* correct 1 biterror */ + h24 ^= (1 << ((a ^ 0x1f)-1)); + } + return + ((h24 & 0x000004) >> 2) | + ((h24 & 0x000070) >> 3) | + ((h24 & 0x007f00) >> 4) | + ((h24 & 0x7f0000) >> 5); +} +#endif /* table or serial */ diff --git a/lib/libupnpclient/Makefile.am b/lib/libupnpclient/Makefile.am new file mode 100644 index 000000000..87168ef65 --- /dev/null +++ b/lib/libupnpclient/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libtuxbox-upnpclient.a + +AM_CPPFLAGS = -fno-rtti + +INCLUDES = \ + -I$(top_srcdir)/lib/xmltree + +libtuxbox_upnpclient_a_SOURCES = UPNPSocket.cpp UPNPDevice.cpp UPNPService.cpp diff --git a/lib/libupnpclient/UPNPDevice.cpp b/lib/libupnpclient/UPNPDevice.cpp new file mode 100644 index 000000000..86afef8d2 --- /dev/null +++ b/lib/libupnpclient/UPNPDevice.cpp @@ -0,0 +1,465 @@ +/*********************************************************************************** + * UPNPDevice.cpp + * + * Copyright (c) 2007 Jochen Friedrich + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "upnpclient.h" +#include + +struct ToLower +{ + char operator() (char c) const { return std::tolower(c); } +}; + +static void trim(std::string &buf) +{ + std::string::size_type pos1, pos2; + + pos1 = buf.find_first_not_of(" \t\n\r"); + pos2 = buf.find_last_not_of(" \t\n\r"); + if ((pos1 == std::string::npos) || (pos2 == std::string::npos)) + buf=""; + else + buf = buf.substr(pos1, pos2 - pos1 + 1); +} + +bool CUPnPDevice::check_response(std::string header, std::string& charset, std::string& rcode) +{ + std::string::size_type pos; + std::string content, attrib, chars; + + charset="ISO-8859-1"; + + pos = header.find("\r"); + if (pos == std::string::npos) + return false; + + rcode = header.substr(0, pos); + if (rcode.substr(0,5) != "HTTP/") + return false; + + pos = rcode.find(" "); + if (pos == std::string::npos) + return false; + + rcode.erase(0, pos); + trim(rcode); + + pos = rcode.find(" "); + if (pos != std::string::npos) + { + rcode.erase(pos); + trim(rcode); + } + + std::transform(header.begin(), header.end(), header.begin(), ToLower()); + + pos = header.find("content-type:"); + if (pos == std::string::npos) + return false; + + content = header.substr(pos); + content.erase(0,13); + pos = content.find("\n"); + if (pos != std::string::npos) + content.erase(pos); + + trim(content); + + pos=content.find(";"); + if (pos!=std::string::npos) + { + attrib=content.substr(pos+1); + content.erase(pos); + trim(content); + } + if (content != "text/xml") + return false; + + trim(attrib); + if (attrib.substr(0,8) == "charset=") + { + attrib.erase(0,8); + pos = attrib.find("\""); + if (pos!=std::string::npos) + { + attrib.erase(0,pos+1); + pos = attrib.find("\""); + if (pos==std::string::npos) + return false; + chars=attrib.substr(0,pos); + } + else + chars=attrib; + if (!strcasecmp(chars.c_str(), "utf-8")) + charset = "UTF-8"; + } + return true; +} + +CUPnPDevice::CUPnPDevice(std::string url) +{ + std::string result, head, body, charset, urlbase; + std::string curl, eurl, name, mimetype, iurl, rcode; + std::string::size_type pos; + XMLTreeNode *root, *device, *service, *node, *snode, *icon; + int width = 0; + int height = 0; + int depth = 0; + bool servicefound = false; + + descurl = url; + urlbase = url.substr(7); + pos = urlbase.find("/"); + if (pos != std::string::npos) + urlbase=url.substr(0,pos+7); + else + urlbase=url; + + result = HTTP(url); + + pos = result.find("\r\n\r\n"); + + if (pos == std::string::npos) + throw std::runtime_error(std::string("no desc body")); + + head = result.substr(0,pos); + body = result.substr(pos+4); + + if (body == "") + throw std::runtime_error(std::string("desc body empty")); + + if (!check_response(head, charset, rcode)) + throw std::runtime_error(std::string("protocol error")); + + if (rcode != "200") + throw std::runtime_error(std::string("description url returned ") + rcode); + XMLTreeParser parser(charset.c_str()); + parser.Parse(body.c_str(), body.size(), 1); + root = parser.RootNode(); + if (!root) + throw std::runtime_error(std::string("XML: no root node")); + + if (strcmp(root->GetType(),"root")) + throw std::runtime_error(std::string("XML: no root")); + + for (node = root->GetChild(); node; node=node->GetNext()) + { + if (!strcmp(node->GetType(),"URLBase")) + { + urlbase = std::string(node->GetData()); + if ((urlbase.length() > 0) && (urlbase[urlbase.length()-1] == '/')) + urlbase.erase(urlbase.length()-1); + } + + } + + node = root->GetChild(); + if (!node) + throw std::runtime_error(std::string("XML: no root child")); + + while (strcmp(node->GetType(),"device")) + { + node = node->GetNext(); + if (!node) + throw std::runtime_error(std::string("XML: no device")); + } + device = node; + + for (node=device->GetChild(); node; node=node->GetNext()) + { + if (!strcmp(node->GetType(),"deviceType")) + devicetype = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"friendlyName")) + friendlyname = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"manufacturer")) + manufacturer = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"manufacturerURL")) + manufacturerurl = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"modelDescription")) + modeldescription = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"modelName")) + modelname = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"modelNumber")) + modelnumber = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"modelURL")) + modelurl = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"serialNumber")) + serialnumber = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"UDN")) + udn = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"UPC")) + upc = std::string(node->GetData()?node->GetData():""); + + if (!strcmp(node->GetType(),"iconList")) + { + for (icon=node->GetChild(); icon; icon=icon->GetNext()) + { + bool foundm = false; + bool foundw = false; + bool foundh = false; + bool foundd = false; + bool foundu = false; + + if (strcmp(icon->GetType(),"icon")) + throw std::runtime_error(std::string("XML: no icon")); + for (snode=icon->GetChild(); snode; snode=snode->GetNext()) + { + if (!strcmp(snode->GetType(),"mimetype")) + { + mimetype=std::string(snode->GetData()?snode->GetData():""); + foundm = true; + } + if (!strcmp(snode->GetType(),"width")) + { + width=snode->GetData()?atoi(snode->GetData()):0; + foundw = true; + } + if (!strcmp(snode->GetType(),"height")) + { + height=snode->GetData()?atoi(snode->GetData()):0; + foundh = true; + } + if (!strcmp(snode->GetType(),"depth")) + { + depth=snode->GetData()?atoi(snode->GetData()):0; + foundd = true; + } + if (!strcmp(snode->GetType(),"url")) + { + url=std::string(snode->GetData()?snode->GetData():""); + foundu = true; + } + } + if (!foundm) + throw std::runtime_error(std::string("XML: icon without mime")); + if (!foundw) + throw std::runtime_error(std::string("XML: icon without width")); + if (!foundh) + throw std::runtime_error(std::string("XML: icon without height")); + if (!foundd) + throw std::runtime_error(std::string("XML: icon without depth")); + if (!foundu) + throw std::runtime_error(std::string("XML: icon without url")); + UPnPIcon e={mimetype, url, width, height, depth}; + icons.push_back(e); + } + } + if (!strcmp(node->GetType(),"serviceList")) + { + servicefound = true; + for (service=node->GetChild(); service; service=service->GetNext()) + { + bool foundc = false; + bool founde = false; + bool foundn = false; + + if (strcmp(service->GetType(),"service")) + throw std::runtime_error(std::string("XML: no service")); + for (snode=service->GetChild(); snode; snode=snode->GetNext()) + { + if (!strcmp(snode->GetType(),"serviceType")) + { + name=std::string(snode->GetData()?snode->GetData():""); + foundn = true; + } + if (!strcmp(snode->GetType(),"eventSubURL")) + { + char *p; + p = snode->GetData(); + if (!p) + eurl=urlbase + "/"; + else if (p[0]=='/') + eurl=urlbase + std::string(p); + else + eurl=urlbase + "/" + std::string(p); + founde = true; + } + if (!strcmp(snode->GetType(),"controlURL")) + { + char *p; + p = snode->GetData(); + if (!p) + curl=urlbase + "/"; + else if (p[0]=='/') + curl=urlbase + std::string(p); + else + curl=urlbase + "/" + std::string(p); + foundc = true; + } + } + if (!foundn) + throw std::runtime_error(std::string("XML: no service type")); + if (!founde) + throw std::runtime_error(std::string("XML: no event url")); + if (!foundc) + throw std::runtime_error(std::string("XML: no control url")); + //try + { + services.push_back(CUPnPService(this, curl, eurl, name)); + } + //catch (std::runtime_error error) + //{ + // std::cout << "error " << error.what() << "\n"; + //} + } + } + } + if (!servicefound) + throw std::runtime_error(std::string("XML: no service list")); +} + +CUPnPDevice::~CUPnPDevice() +{ +} + +std::string CUPnPDevice::HTTP(std::string url, std::string post, std::string action) +{ + std::string portname; + std::string hostname; + std::string path; + int port, t_socket, result, received; + std::stringstream command, reply; + std::string commandstr, line; + struct sockaddr_in socktcp; + struct hostent *hp; + std::string::size_type pos1, pos2; + char buf[4096]; + + if (url.substr(0,7) != "http://") + return ""; + + url.erase(0,7); + + pos1 = url.find(":", 0); + pos2 = url.find("/", 0); + + if (pos2 == std::string::npos) + return ""; + if ((pos1 == std::string::npos) || (pos1 > pos2)) + { + hostname = url.substr(0,pos2); + port=80; + } + else + { + hostname = url.substr(0,pos1); + portname = url.substr(pos1+1, pos2-pos1-1); + port = atoi(portname.c_str()); + } + path = url.substr(pos2); + + if (post.size()) + command << "POST " << path << " HTTP/1.0\r\n"; + else + command << "GET " << path << " HTTP/1.0\r\n"; + + command << "Host: " << hostname << ":" << port << "\r\n"; + command << "User-Agent: TuxBox\r\n"; + command << "Accept: text/xml\r\n"; + command << "Connection: Close\r\n"; + + if (post.size()) + { + command << "Content-Length: " << post.size() << "\r\n"; + command << "Content-Type: text/xml\r\n"; + } + + if (action.size()) + command << "SOAPAction: \"" << action << "\"\r\n"; + + command << "\r\n"; + + if (post.size()) + command << post; + + t_socket = socket(PF_INET, SOCK_STREAM, 0); + if (!t_socket) + throw std::runtime_error(std::string("create TCP socket")); + + hp = gethostbyname(hostname.c_str()); + if (!hp) + throw std::runtime_error(std::string("resolve name")); + + memset(&socktcp, 0, sizeof(struct sockaddr_in)); + socktcp.sin_family = AF_INET; + socktcp.sin_port = htons(port); + memcpy((char *)&socktcp.sin_addr, hp->h_addr, hp->h_length); + + if (connect(t_socket, (struct sockaddr*) &socktcp, sizeof(struct sockaddr_in))) + { + close(t_socket); + throw std::runtime_error(std::string("connect")); + } + + commandstr = command.str(); + result = send(t_socket, commandstr.c_str(), commandstr.size(), 0); + while ((received = recv(t_socket, buf, sizeof(buf)-1, 0)) > 0) + { + buf[received] = 0; + reply << buf; + } + return reply.str(); +} + +std::list CUPnPDevice::SendSOAP(std::string servicename, std::string action, std::list attribs) +{ + std::list::iterator i; + std::list empty; + + for (i=services.begin(); i != services.end(); i++) + { + if (i->servicename == servicename) + return i->SendSOAP(action, attribs); + } + return empty; +} + +std::list CUPnPDevice::GetServices(void) +{ + std::list::iterator i; + std::list result; + + for (i=services.begin(); i != services.end(); i++) + result.push_back(i->servicename); + return result; +} + diff --git a/lib/libupnpclient/UPNPService.cpp b/lib/libupnpclient/UPNPService.cpp new file mode 100644 index 000000000..7944a8aa1 --- /dev/null +++ b/lib/libupnpclient/UPNPService.cpp @@ -0,0 +1,172 @@ +/*********************************************************************************** + * UPNPService.cpp + * + * Copyright (c) 2007 Jochen Friedrich + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "upnpclient.h" + +CUPnPService::CUPnPService(CUPnPDevice* dev, std::string curl, std::string eurl, std::string name) +{ + device = dev; + controlurl = curl; + eventurl = eurl; + servicename = name; +} + +CUPnPService::~CUPnPService() +{ +} + +std::list CUPnPService::SendSOAP(std::string action, std::list attribs) +{ + std::stringstream post; + std::string soapaction; + std::string result, head, body, charset, envelope, soapbody, soapresponse, rcode; + std::list::iterator i; + std::list results; + XMLTreeNode *root, *node, *snode; + std::string::size_type pos; + + post << "\r\n" + "" + ""; + post << ""; + } else { + post << ">"; + for (i = attribs.begin(); i != attribs.end(); i++) + { + if (i->second == "") + post << "<" << i->first << "/>"; + else + { + post << "<" << i->first << ">"; + post << i->second; + post << "first << ">"; + } + } + post << ""; + } + post << "\r\n"; + soapaction = servicename + "#" + action; + result = device->HTTP(controlurl, post.str(), soapaction); + + pos = result.find("\r\n\r\n"); + + if (pos == std::string::npos) + throw std::runtime_error(std::string("no soap body")); + + head = result.substr(0,pos); + body = result.substr(pos+4); + + if (!device->check_response(head, charset, rcode)) + throw std::runtime_error(std::string("protocol error")); + + XMLTreeParser parser(charset.c_str()); + parser.Parse(body.c_str(), body.size(), 1); + root = parser.RootNode(); + if (!root) + throw std::runtime_error(std::string("XML: no root node")); + + envelope=std::string(root->GetType()); + pos = envelope.find(":"); + if (pos !=std::string::npos) + envelope.erase(0,pos+1); + + if (envelope != "Envelope") + throw std::runtime_error(std::string("XML: no envelope")); + + node = root->GetChild(); + if (!node) + throw std::runtime_error(std::string("XML: no envelope child")); + + soapbody=std::string(node->GetType()); + pos = soapbody.find(":"); + if (pos !=std::string::npos) + soapbody.erase(0,pos+1); + + if (soapbody != "Body") + throw std::runtime_error(std::string("XML: no soap body")); + + node = node->GetChild(); + if (!node) + throw std::runtime_error(std::string("XML: no soap body child")); + + soapresponse=std::string(node->GetType()); + pos = soapresponse.find(":"); + if (pos !=std::string::npos) + soapresponse.erase(0,pos+1); + + if (rcode != "200") + { + std::string faultstring, upnpcode, upnpdesc, errstr; + if (soapresponse != "Fault") + throw std::runtime_error(std::string("XML: http error without soap fault: ")+rcode); + for (node=node->GetChild(); node; node=node->GetNext()) + { + if (!strcmp(node->GetType(),"detail")) + { + snode=node->GetChild(); + if (snode) + for (snode=snode->GetChild(); snode; snode=snode->GetNext()) + { + errstr=snode->GetType(); + pos = errstr.find(":"); + if (pos !=std::string::npos) + errstr.erase(0,pos+1); + if (errstr=="errorCode") + upnpcode=std::string(snode->GetData()?snode->GetData():""); + if (errstr=="errorDescription") + upnpdesc=std::string(snode->GetData()?snode->GetData():""); + } + + } + if (!strcmp(node->GetType(),"faultstring")) + faultstring=std::string(node->GetData()?node->GetData():""); + } + if (faultstring != "") + throw std::runtime_error(faultstring + " " + upnpcode + " " + upnpdesc); + else + throw std::runtime_error(std::string("XML: http error with unknown soap: ")+rcode); + } + if (soapresponse != action + "Response") + throw std::runtime_error(std::string("XML: no soap response")); + + for (node=node->GetChild(); node; node=node->GetNext()) + results.push_back(UPnPAttribute(node->GetType(), node->GetData())); + + return results; +} diff --git a/lib/libupnpclient/UPNPSocket.cpp b/lib/libupnpclient/UPNPSocket.cpp new file mode 100644 index 000000000..b56e269f5 --- /dev/null +++ b/lib/libupnpclient/UPNPSocket.cpp @@ -0,0 +1,159 @@ +/*********************************************************************************** + * UPNPSocket.cpp + * + * Copyright (c) 2007 Jochen Friedrich + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "upnpclient.h" + +CUPnPSocket::CUPnPSocket() +{ + struct sockaddr_in sockudp; + int opt=1; + + m_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (!m_socket) + throw std::runtime_error(std::string("create UDP socket")); + + memset(&sockudp, 0, sizeof(struct sockaddr_in)); + sockudp.sin_family = AF_INET; + sockudp.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) + { + close(m_socket); + throw std::runtime_error(std::string("socket option")); + } + + if (bind(m_socket, (struct sockaddr*) &sockudp, sizeof(struct sockaddr_in))) + { + close(m_socket); + throw std::runtime_error(std::string("bind")); + } +} + +CUPnPSocket::~CUPnPSocket() +{ + close(m_socket); +} + +void CUPnPSocket::SetTTL(int ttl) +{ + int result; + + result = setsockopt(m_socket, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); + if (!result) + throw std::runtime_error(std::string("set ttl")); +} + +std::vector CUPnPSocket::Discover(std::string service) +{ + struct sockaddr_in sockudp; + std::stringstream command, reply; + std::string commandstr, line; + std::vector devices; + int result; + std::string::size_type pos; + struct pollfd fds[1]; + char bufr[1536]; + + memset(&sockudp, 0, sizeof(struct sockaddr_in)); + sockudp.sin_family = AF_INET; + sockudp.sin_port = htons(MULTICAST_PORT); + sockudp.sin_addr.s_addr = inet_addr(MULTICAST_IP); + + command << "M-SEARCH * HTTP/1.1\r\n" << + "HOST: " << MULTICAST_IP << ":" << MULTICAST_PORT << "\r\n" << + "ST: " << service << "\r\n" << + "MAN: \"ssdp:discover\"\r\n" << + "MX: 3\r\n" << + "\r\n"; + + commandstr = command.str(); + result = sendto(m_socket, commandstr.c_str(), commandstr.size(), 0, (struct sockaddr*) &sockudp, sizeof(struct sockaddr_in)); + + if (result < 0) { + throw std::runtime_error(std::string(strerror(errno))); + } +// result = sendto(m_socket, commandstr.c_str(), commandstr.size(), 0, (struct sockaddr*) &sockudp, sizeof(struct sockaddr_in)); + + fds[0].fd = m_socket; + fds[0].events = POLLIN; + + for(;;) + { + result = poll(fds, 1, 4000); + if (result < 0) + throw std::runtime_error(std::string("poll")); + if (result == 0) + return devices; + + result = recv(m_socket, bufr, sizeof(bufr), 0); + if (result < 0) + throw std::runtime_error(std::string("recv")); + + bufr[result]=0; + + reply.clear(); + reply.str(std::string(bufr)); + + while (!reply.eof()) + { + getline(reply, line); + pos=line.find("\r", 0); + if (pos!=std::string::npos) + line.erase(pos); + std::string location = line.substr(0,9); + if (!strcasecmp(location.c_str(), "location:")) + { + line.erase(0, 9); + while ((line.length() > 0 )&& ((line[0] == ' ') || (line[0] == '\t'))) + line.erase(0, 1); + if (line.substr(0,7) == "http://") + { + std::vector::iterator i; + for (i=devices.begin(); i != devices.end(); i++) + if (line == i->descurl) + goto found; + try + { + devices.push_back(CUPnPDevice(line)); + } + catch (std::runtime_error error) + { + std::cout << "error " << error.what() << "\n"; + } + } + found: ; + } + } + } +} diff --git a/lib/libupnpclient/upnpclient.h b/lib/libupnpclient/upnpclient.h new file mode 100644 index 000000000..952f5cb9b --- /dev/null +++ b/lib/libupnpclient/upnpclient.h @@ -0,0 +1,119 @@ +/*********************************************************************************** + * upnpclient.h + * + * Copyright (c) 2007 Jochen Friedrich + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***********************************************************************************/ + +#ifndef __UPNP_H +#define __UPNP_H + +#include +#include +#include +#include + +#define MULTICAST_PORT 1900 +#define MULTICAST_IP "239.255.255.250" + +typedef std::pair UPnPAttribute; + +struct UPnPIcon +{ + std::string mimetype; + std::string url; + int width; + int heigth; + int depth; +}; + +class CUPnPService; + +/****************************************** + * Class: CUPnPDevice + ******************************************/ + +class CUPnPDevice +{ + friend class CUPnPService; + friend class CUPnPSocket; + +private: + std::string descurl; + std::list services; + std::string HTTP(std::string url,std::string post="", std::string action=""); + bool check_response(std::string header, std::string& charset, std::string& rcode); + std::list icons; + +public: + std::string friendlyname; + std::string devicetype; + std::string manufacturer; + std::string manufacturerurl; + std::string modeldescription; + std::string modelname; + std::string modelnumber; + std::string modelurl; + std::string serialnumber; + std::string udn; + std::string upc; + std::list SendSOAP(std::string service, std::string action, std::list attribs); + std::list GetServices(void); + std::list GetIcons() const { return icons; }; + + CUPnPDevice(std::string url); + ~CUPnPDevice(); +}; + +/****************************************** + * Class: CUPnPService + ******************************************/ + +class CUPnPService +{ + +public: + std::string controlurl; + std::string eventurl; + std::string servicename; + CUPnPDevice *device; + + CUPnPService(CUPnPDevice *dev, std::string curl, std::string eurl, std::string service); + ~CUPnPService(); + std::list SendSOAP(std::string action, std::list attribs); +}; + +/****************************************** + * Class: CUPnPSocket + ******************************************/ + +class CUPnPSocket +{ + +public: + CUPnPSocket(); + ~CUPnPSocket(); + + void SetTTL(int ttl); + std::vector Discover(std::string service); + +private: + int m_socket; + +}; + +#endif diff --git a/lib/sectionsdclient/Makefile.am b/lib/sectionsdclient/Makefile.am new file mode 100644 index 000000000..40b7fde20 --- /dev/null +++ b/lib/sectionsdclient/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver + +noinst_LIBRARIES = libsectionsdclient.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libsectionsdclient_a_SOURCES = sectionsdclient.cpp + +bin_PROGRAMS = sectionsdcontrol + +sectionsdcontrol_SOURCES = sectionsdcontrol.cpp +sectionsdcontrol_LDADD = \ + $(top_builddir)/lib/sectionsdclient/libsectionsdclient.a \ + $(top_builddir)/lib/connection/libtuxbox-connection.a diff --git a/lib/sectionsdclient/sectionsdMsg.h b/lib/sectionsdclient/sectionsdMsg.h new file mode 100644 index 000000000..e7123fb3d --- /dev/null +++ b/lib/sectionsdclient/sectionsdMsg.h @@ -0,0 +1,375 @@ +#ifndef SECTIONSDMSG_H +#define SECTIONSDMSG_H +// +// $Id: sectionsdMsg.h,v 1.16 2007/01/07 23:34:55 guenther Exp $ +// +// sectionsdMsg.h (header file with msg-definitions for sectionsd) +// (dbox-II-project) +// +// Copyright (C) 2001 by fnbrd, +// 2002 by thegoodguy +// +// Homepage: http://dbox2.elxsi.de +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + + +#include +#include /* t_channel_id */ + + +#define SECTIONSD_UDS_NAME "/tmp/sectionsd.sock" + + +struct sectionsd +{ + static const CBasicMessage::t_version ACTVERSION = 5; + + struct msgRequestHeader + { + unsigned char version; + unsigned char command; + unsigned short stuff_bytes; + unsigned int dataLength; + } __attribute__ ((packed)) ; + + struct msgResponseHeader + { + unsigned int dataLength; + } __attribute__ ((packed)) ; + + enum commands + { + /* actualEPGchannelName=0,*/ + dummy1, + dummy3, //actualEventListTVshort, // commandEventListTV + /*currentNextInformation,*/ + dummy2, + dumpStatusinformation, // commandDumpStatusInformation + /*allEventsChannelName,*/ + allEventsChannelIDSearch, // commandAllEventsChannelIDSearch + dummy4, // setHoursToCache, + dummy5, // setHoursExtendedCache, + dummy6, // setEventsAreOldInMinutes, + dummy7, // dumpAllServices, // commandDumpAllServices + dummy8, // actualEventListRadioshort, // commandEventListRadio + dummy9, // getNextEPG, // commandGetNextEPG + dummy10,// getNextShort, // commandGetNextShort + pauseScanning, // commandPauseScanning // for the grabbers ;) + getIsScanningActive, // commandGetIsScanningActive + actualEPGchannelID, // commandActualEPGchannelID + actualEventListTVshortIDs, // commandEventListTVids + actualEventListRadioShortIDs, // commandEventListRadioIDs + currentNextInformationID, // commandCurrentNextInfoChannelID + epgEPGid, // commandEPGepgID + epgEPGidShort, // commandEPGepgIDshort + ComponentTagsUniqueKey, // commandComponentTagsUniqueKey + allEventsChannelID_, // commandAllEventsChannelID + timesNVODservice, // commandTimesNVODservice + getEPGPrevNext, // commandGetEPGPrevNext + getIsTimeSet, // commandGetIsTimeSet + serviceChanged, // commandserviceChanged + LinkageDescriptorsUniqueKey, // commandLinkageDescriptorsUniqueKey + pauseSorting, // commandPauseSorting + CMD_registerEvents, // commandRegisterEventClient + CMD_unregisterEvents, // commandUnRegisterEventClient +#ifdef ENABLE_PPT + setPrivatePid, // commandSetPrivatePid +#else + dummy11_setPrivatePid, +#endif + setSectionsdScanMode, // commandSetSectionsdScanMode + freeMemory, // commandFreeMemory + readSIfromXML, // commandReadSIfromXML + writeSI2XML, // commandWriteSI2XML + + LoadLanguages, // commandLoadLanguages + SaveLanguages, // commandSaveLanguages + SetLanguages, // commandSetLanguages + GetLanguages, // commandGetLanguages + SetLanguageMode, // commandSetLanguageMode + GetLanguageMode, // commandGetLanguageMode + setConfig, // commandSetConfig + Restart, // commandRestart + ping, + + numberOfCommands // <- no actual command, end of command marker + }; + + struct commandGetEPGid + { + unsigned long long eventid; + time_t starttime; + } __attribute__ ((packed)) ; + + struct commandSetServiceChanged + { + t_channel_id channel_id; + bool requestEvent; + }; + + struct responseIsTimeSet + { + bool IsTimeSet; + }; + + struct commandSetConfig + { + int scanMode; + int epg_cache; + int epg_old_events; + unsigned int epg_max_events; + int network_ntprefresh; + int network_ntpenable; + int epg_extendedcache; +// std::string network_ntpserver; +// std::string epg_dir; + }; + +}; + +// +// Description of Commands: +// +// If a command is recognize then sectionsd will always send a response. +// When requested datas are not found the data length of the response is 0. +// +// actualEPGchannelName: +// data of request: +// is channel name with trailing 0 (c-string) +// data of response: +// is a string (c-string) describing the EPG (for +// compatibility with old epgd) +// +// actualEventListTVshort: +// data of request: +// - +// data of response: +// is a string (c-string) with all cached actual events, +// 3 lines per service, first line unique-event-key, second line service name, third line event name +// +// currentNextInformation: +// data of request: +// is channel name with trailing 0 (c-string) +// data of response: +// is a string (c-string) describing the current/next EPGs +// +// dumpStatusinformation: +// data of request: +// - +// data of response: +// is a string (c-string) describing current status of sectionsd +// +// allEventsChannelName: +// data of request: +// is channel name with trailing 0 (c-string) +// data of response: +// is a string (c-string) describing the cached events for the requestet channel +// 1 line per event, format: uniqueEventKey DD.MM HH:MM durationInMinutes Event name +// +// setHoursToCache +// data of request: +// unsigned short (hours to cache) +// data of response: +// - +// +// setEventsAreOldInMinutes +// data of request: +// unsigned short (minutes after events are old (after their end time)) +// data of response: +// - +// +// dumpAllServicesinformation: +// data of request: +// - +// data of response: +// is a string (c-string) with all cached services +// 3 lines per service +// 1. line: unique-service-key, service-ID, service-type, eitScheduleFlag (bool), +// eitPresentFollowingFlag (bool), runningStatus (bool), +// freeCAmode (bool), number of nvod services +// 2. line: service name +// 3. line: provider name +// +// actualEventListRadioshort: +// data of request: +// - +// data of response: +// is a string (c-string) with all cached actual events, +// 3 lines per service, first line unique-event-key, second line service name, third line event name +// +// getNextEPG: +// data of request: +// 8 bytes (long long in 32 bit world) with unique key (binary) of the event for wich the next should be delivered +// 4 bytes with start time (ctime) of the above event +// data of response: +// is a string (c-string) describing the EPG: +// unique key (long long, hex) 0xff name 0xff text 0xff extended text 0xff start time GMT (ctime, hex ) 0xff duration (seconds, hex) +// +// getNextShort: +// data of request: +// 8 bytes (long long in 32 bit world) with unique key (binary) of the event for wich the next should be delivered +// 4 bytes with start time (ctime) of the above event +// data of response: +// is a string (c-string) describing the Event in short terms: +// 1. line unique key (long long, hex), 2. line name, 3. line start time GMT (ctime, hex ), 4 line duration (seconds, hex) +// +// pauseScanning: +// data of request: +// int (1 = pause, 0 = continue) +// data of response: +// - +// +// actualEPGchannelID: +// data of request: +// is channel ID +// data of response: +// is a string (c-string) describing the EPG: +// unique key (long long, hex) 0xff name 0xff text 0xff extended text 0xff start time GMT (ctime, hex ) 0xff duration (seconds, hex) 0xff +// +// actualEventListTVshortIDs: +// data of request: +// - +// data of response: +// for every service: +// 1. unique-service-key (4 bytes) +// 2. unique-event-key (8 bytes) +// 3. event name (c-string with 0) +// +// actualEventListRadioShortIDs: +// data of request: +// - +// data of response: +// 1. unique-service-key (4 bytes) +// 2. unique-event-key (8 bytes) +// 3. event name (c-string with 0) +// +// currentNextInformationID: +// data of request: +// 4 byte channel ID (4 byte, onid<<16+sid) +// 1 byte number of Events (noch nicht implementiert) +// data of response: +// is a string (c-string) describing the current/next EPGs +// every event: +// 1. 8 bytes unique key (unsigned long long), +// 2. struct sectionsdTime (8 bytes) +// 3. name (c-string with 0) +// +// epgEPGid: +// data of request: +// unique epg ID (8 byte) +// time_t starttime GMT (4 bytes) +// data of response: +// is a string (c-string) describing the EPG: +// name 0xff text 0xff extended text 0xff start time GMT (ctime, hex ) 0xff duration (seconds, hex) 0xff +// +// epgEPGidShort: +// data of request: +// unique epg ID (8 byte) +// data of response: +// is a string (c-string) describing the EPG: +// name 0xff text 0xff extended text 0xff +// +// CurrentComponentTags +// - gets all ComponentDescriptor-Tags for the currently running Event +// +// data of request: +// is channel ID (4 byte, onid<<16+sid) +// data of response: +// for each component-descriptor (zB %02hhx %02hhx %02hhx\n%s\n) +// componentTag +// componentType +// streamContent +// component.c_str +// +// allEventsChannelID: +// data of request: +// is channel ID +// data of response: +// is a string (c-string) describing the cached events for the requestet channel +// 1 line per event, format: uniqueEventKey DD.MM HH:MM durationInMinutes Event name +// +// timesNVODservice +// data of request: +// is channel ID +// data of response: +// for every (sub-)channel +// channelID (4 byte, onid<<16+sid) +// transportStreamID (2 byte) +// start time (4 bytes ctime) +// duration (4 bytes unsigned) +// +// getEPGPrevNext +// data of request: +// 8 bytes (long long in 32 bit world) with unique key (binary) of the event for wich the next should be delivered +// 4 bytes with start time (ctime) of the above event +// data of response: +// is a string (c-string) describing the EPG: +// unique key (long long, hex) 0xff start time GMT (ctime, hex ) for previous event +// unique key (long long, hex) 0xff start time GMT (ctime, hex ) for next event + +// +// LoadLanguages +// data of request: +// none +// data of response: +// success status (sizeof(bool)) +// +// SaveLanguages +// data of request: +// none +// data of response: +// success status (sizeof(bool)) +// +// SetLanguages +// data of request: +// n * 3 bytes (iso_639_2 language codes) +// data of response: +// success status (sizeof(bool)) +// +// GetLanguages +// data of request: +// none +// data of response: +// n * 3 bytes (iso_639_2 language codes) +// +// SetLanguageMode +// data of request: +// mode (sizeof(CSectionsdClient::SIlanguageMode_t), +// ALL, FIRST_FIRST, FIRST_ALL, ALL_FIRST or ALL_ALL) +// data of response: +// success status (sizeof(bool)) +// +// GetLanguageMode +// data of request: +// none +// data of response: +// mode (see above) +// +// setConfig +// data of request: +// int scanMode; -> updating of services and bouquets -> saved in auto_scanning +// int epg_cache; -> in days -> saved in secondsToCache +// int epg_old_events; -> in hours -> saved in oldEventsAre +// unsigned int epg_max_events; -> #of saved events -> saved in max_events +// int network_ntprefresh; -> time refresh intervall -> saved in ntprefresh +// int network_ntpenable; -> time refresh via ntp server -> saved in ntpenable +// std::string network_ntpserver; -> ntpserver -> saved in ntpserver +// std::string epg_dir; -> epg_dir to im/export the epg xml files -> saved in epg_file +// data of response: +// - + +#endif // SECTIONSDMSG_H diff --git a/lib/sectionsdclient/sectionsdclient.cpp b/lib/sectionsdclient/sectionsdclient.cpp new file mode 100644 index 000000000..9f73160b1 --- /dev/null +++ b/lib/sectionsdclient/sectionsdclient.cpp @@ -0,0 +1,803 @@ +/* + Client-Interface für zapit - DBoxII-Project + + $Id: sectionsdclient.cpp,v 1.53 2007/01/12 22:57:57 houdini Exp $ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include + +#include +#include + + +const unsigned char CSectionsdClient::getVersion () const +{ + return sectionsd::ACTVERSION; +} + +const char * CSectionsdClient::getSocketName() const +{ + return SECTIONSD_UDS_NAME; +} + +int CSectionsdClient::readResponse(char* data,unsigned int size) +{ + struct sectionsd::msgResponseHeader responseHeader; + if (!receive_data((char*)&responseHeader, sizeof(responseHeader))) + return 0; + + if ( data != NULL ) + { + if ( responseHeader.dataLength != (unsigned)size ) + return -1; + else + return receive_data(data, size); + } + else + return responseHeader.dataLength; +} + + +bool CSectionsdClient::send(const unsigned char command, const char* data, const unsigned int size) +{ + sectionsd::msgRequestHeader msgHead; + + msgHead.version = getVersion(); + msgHead.command = command; + msgHead.dataLength = size; + + open_connection(); // if the return value is false, the next send_data call will return false, too + + if (!send_data((char*)&msgHead, sizeof(msgHead))) + return false; + + if (size != 0) + return send_data(data, size); + + return true; +} + +void CSectionsdClient::registerEvent(const unsigned int eventID, const unsigned int clientID, const char * const udsName) +{ + CEventServer::commandRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + + strcpy(msg2.udsName, udsName); + + send(sectionsd::CMD_registerEvents, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +void CSectionsdClient::unRegisterEvent(const unsigned int eventID, const unsigned int clientID) +{ + CEventServer::commandUnRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + + send(sectionsd::CMD_unregisterEvents, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} + +bool CSectionsdClient::getIsTimeSet() +{ + sectionsd::responseIsTimeSet rmsg; + + if (send(sectionsd::getIsTimeSet)) + { + readResponse((char*)&rmsg, sizeof(rmsg)); + close_connection(); + + return rmsg.IsTimeSet; + } + else + { + close_connection(); + return false; + } +} + + +#if 0 +void CSectionsdClient::setEventsAreOldInMinutes(const unsigned short minutes) +{ + send(sectionsd::setEventsAreOldInMinutes, (char*)&minutes, sizeof(minutes)); + + readResponse(); + close_connection(); +} +#endif + +void CSectionsdClient::setPauseSorting(const bool doPause) +{ + int PauseIt = (doPause) ? 1 : 0; + + send(sectionsd::pauseSorting, (char*)&PauseIt, sizeof(PauseIt)); + + readResponse(); + close_connection(); +} + +void CSectionsdClient::setPauseScanning(const bool doPause) +{ + int PauseIt = (doPause) ? 1 : 0; + + send(sectionsd::pauseScanning, (char*)&PauseIt, sizeof(PauseIt)); + + readResponse(); + close_connection(); +} + +bool CSectionsdClient::getIsScanningActive() +{ + int scanning; + + if (send(sectionsd::getIsScanningActive)) + { + readResponse((char*)&scanning, sizeof(scanning)); + close_connection(); + + return scanning; + } + else + { + close_connection(); + return false; + } +} + +void CSectionsdClient::setServiceChanged(const t_channel_id channel_id, const bool requestEvent) +{ + sectionsd::commandSetServiceChanged msg; + + msg.channel_id = channel_id; + msg.requestEvent = requestEvent; + + send(sectionsd::serviceChanged, (char *)&msg, sizeof(msg)); + + readResponse(); + close_connection(); +} + + +bool CSectionsdClient::getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags) +{ + if (send(sectionsd::ComponentTagsUniqueKey, (char*)&uniqueKey, sizeof(uniqueKey))) + { + tags.clear(); + + int nBufSize = readResponse(); + + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + char* dp = pData; + + int count= *(int *) pData; + dp+= sizeof(int); + + CSectionsdClient::responseGetComponentTags response; + for (int i= 0; i 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + + char* dp = pData; + + while(dp < pData + nBufSize) + { + CChannelEvent aEvent; + + aEvent.eventID = *((event_id_t *) dp); + dp+=sizeof(aEvent.eventID); + + aEvent.startTime = *((time_t *) dp); + dp+=sizeof(aEvent.startTime); + + aEvent.duration = *((unsigned *) dp); + dp+=sizeof(aEvent.duration); + + aEvent.description= dp; + dp+=strlen(dp)+1; + + aEvent.text= dp; + dp+=strlen(dp)+1; + + eList.push_back(aEvent); + } + delete[] pData; + } + } + close_connection(); + return eList; +} + +//GU:EPG +/* This function does initiate a search for a keyword in all EPG Event of the Channel channel_id in sectionsd. + The parameter search_typ does specify the search mode + 0: none -> all EPG events of the channel are returned + 1: keyword search in EPG Title + 2: keyword search in EPG short description (INFO1) + 3: keyword search in EPG description (INFO2) + In case of a match, the EPG event is added to the Eventlist eList. + */ +bool CSectionsdClient::getEventsServiceKeySearchAdd(CChannelEventList& eList,const t_channel_id channel_id,char search_typ,std::string& search_text) +{ + int nBufSize=0; + + nBufSize += sizeof(t_channel_id); + nBufSize += sizeof(char); + nBufSize += search_text.size()+1; + + char* pSData = new char[nBufSize]; + char* pSData_ptr = pSData; + + *(t_channel_id*)pSData_ptr = channel_id; + pSData_ptr += sizeof(t_channel_id); + *pSData_ptr = search_typ; + pSData_ptr += sizeof(char); + strcpy(pSData_ptr,search_text.c_str()); + + if (send(sectionsd::allEventsChannelIDSearch, pSData, nBufSize)) + { + int nBufSize = readResponse(); + + if( nBufSize > 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + + char* dp = pData; + +// int a = eList.size(); + + while(dp < pData + nBufSize) + { + CChannelEvent aEvent; + + aEvent.eventID = *((event_id_t *) dp); + dp+=sizeof(aEvent.eventID); + + aEvent.startTime = *((time_t *) dp); + dp+=sizeof(aEvent.startTime); + + aEvent.duration = *((unsigned *) dp); + dp+=sizeof(aEvent.duration); + + aEvent.description= dp; + dp+=aEvent.description.length()+1; + + aEvent.text= dp; + dp+=aEvent.text.length()+1; + + eList.push_back(aEvent); + } +// int b = eList.size() -a; + delete[] pData; + } + } + delete[] pSData; + + close_connection(); + return true; +} + +CChannelEventList CSectionsdClient::getEventsServiceKey(const t_channel_id channel_id) +{ + CChannelEventList eList; + + if (send(sectionsd::allEventsChannelID_, (char*)&channel_id, sizeof(channel_id))) + { + int nBufSize = readResponse(); + + if( nBufSize > 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + + char* dp = pData; + + while(dp < pData + nBufSize) + { + CChannelEvent aEvent; + + aEvent.eventID = *((event_id_t *) dp); + dp+=sizeof(aEvent.eventID); + + aEvent.startTime = *((time_t *) dp); + dp+=sizeof(aEvent.startTime); + + aEvent.duration = *((unsigned *) dp); + dp+=sizeof(aEvent.duration); + + aEvent.description= dp; + dp+=strlen(dp)+1; + + aEvent.text= dp; + dp+=strlen(dp)+1; + + eList.push_back(aEvent); + } + delete[] pData; + } + } + + close_connection(); + return eList; +} +void showhexdumpa (char *label, unsigned char * from, int len) +{ + int i, j, k; + char buf[128]; + char abuf[128]; + unsigned char fl, ol; + + fl = len / 16; + ol = len % 16; + if (label) { + time_t tt = time (0); + printf("\n%s -- %s", label, ctime (&tt)); + printf("----------------------------------------------------\n"); + } + + for (i = 0; i < fl; i++) { + j = i * 16; + sprintf (buf, "%03X: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", i * 16, from[j + 0], from[j + 1], from[j + 2], from[j + 3], from[j + 4], from[j + 5], from[j + 6], from[j + 7], from[j + 8], from[j + 9], from[j + 10], from[j + 11], from[j + 12], from[j + 13], from[j + 14], from[j + 15]); + printf ("%s ", buf); + for (k = 0; k < 16; k++) { + abuf[k] = (from[j + k] >= 0x20 && from[j + k] <= 0x7b) ? from[j + k] : 0x2E; + } + abuf[16] = 0; + printf("%s\n", abuf); + } + if (ol) { + j = fl * 16; + sprintf (buf, "%03X: ", j); + for (i = 0; i < ol; i++) { + sprintf (&buf[5 + i * 3], "%02x ", from[j + i]); + abuf[i] = (from[j + i] >= 0x20 && from[j + i] <= 0x7b) ? from[j + i] : 0x2E; + } + abuf[ol] = 0; + for (i = ol; i < 16; i++) + sprintf (&buf[5 + i * 3], " "); + printf ("%s ", buf); + printf ("%s\n", abuf); + } + printf ("\n"); +} + +// 21.07.2005 - rainerk +// Convert line-terminated extended events to vector of strings +char * CSectionsdClient::parseExtendedEvents(char * dp, CEPGData * epgdata) { + char * pItemDescriptions = dp, * pItemDescriptionStart; + dp+=strlen(dp)+1; + char * pItems = dp, * pItemStart; + dp+=strlen(dp)+1; + /* Clear vector since epgdata seems to be reused */ + epgdata->itemDescriptions.clear(); + while (*pItemDescriptions) { + pItemDescriptionStart = pItemDescriptions; + while (*pItemDescriptions && '\n' != *pItemDescriptions) { + pItemDescriptions++; + } + char pp = *pItemDescriptions; + *pItemDescriptions = 0; + epgdata->itemDescriptions.push_back(pItemDescriptionStart); +/*printf("CSectionsdClient::parseExtendedEvents: desc %s\n", pItemDescriptionStart);*/ + if(!pp) + break; + pItemDescriptions++; + } + /* Clear vector since epgdata seems to be reused */ + epgdata->items.clear(); + while (*pItems) { + pItemStart = pItems; + while (*pItems && '\n' != *pItems) { + pItems++; + } + char pp = *pItemDescriptions; + *pItems = 0; + epgdata->items.push_back(pItemStart); +/*printf("CSectionsdClient::parseExtendedEvents: item %s\n", pItemStart);*/ + if(!pp) + break; + pItems++; + } + return dp; +} + +bool CSectionsdClient::getActualEPGServiceKey(const t_channel_id channel_id, CEPGData * epgdata) +{ + epgdata->title = ""; + + if (send(sectionsd::actualEPGchannelID, (char*)&channel_id, sizeof(channel_id))) + { + int nBufSize = readResponse(); + if( nBufSize > 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + close_connection(); + + char* dp = pData; + + epgdata->eventID = *((event_id_t *)dp); + dp+= sizeof(epgdata->eventID); + + epgdata->title = dp; + dp+=strlen(dp)+1; + epgdata->info1 = dp; + dp+=strlen(dp)+1; + epgdata->info2 = dp; + dp+=strlen(dp)+1; + // 21.07.2005 - rainerk + // Convert line-terminated extended events to vector of strings +//showhexdumpa("Data:", (unsigned char *)pData, nBufSize); + dp = parseExtendedEvents(dp, epgdata); + + // *dp is the length, dp+1 is the chararray[] + epgdata->contentClassification = std::string(dp+1, *dp); + dp+=*dp+1; + epgdata->userClassification = std::string(dp+1, *dp); + dp+=*dp+1; + + epgdata->fsk = *dp++; + + epgdata->epg_times.startzeit = ((CSectionsdClient::sectionsdTime *) dp)->startzeit; + epgdata->epg_times.dauer = ((CSectionsdClient::sectionsdTime *) dp)->dauer; + dp+= sizeof(CSectionsdClient::sectionsdTime); + + delete[] pData; + return true; + } + else + printf("no response from sectionsd\n"); + } + + close_connection(); + + return false; +} + + +bool CSectionsdClient::getEPGid(const event_id_t eventid, const time_t starttime, CEPGData * epgdata) +{ + sectionsd::commandGetEPGid msg; + + msg.eventid = eventid; + msg.starttime = starttime; + + if (send(sectionsd::epgEPGid, (char *)&msg, sizeof(msg))) + { + int nBufSize = readResponse(); + if (nBufSize > 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + close_connection(); + + char* dp = pData; + + epgdata->eventID = *((event_id_t *)dp); + dp+= sizeof(epgdata->eventID); + + epgdata->title = dp; + dp+=strlen(dp)+1; + epgdata->info1 = dp; + dp+=strlen(dp)+1; + epgdata->info2 = dp; + dp+=strlen(dp)+1; + // 21.07.2005 - rainerk + // Convert line-terminated extended events to vector of strings + dp = parseExtendedEvents(dp, epgdata); + + // *dp is the length, dp+1 is the chararray[] + epgdata->contentClassification = std::string(dp+1, *dp); + dp+=*dp+1; + epgdata->userClassification = std::string(dp+1, *dp); + dp+=*dp+1; + + epgdata->fsk = *dp++; + + epgdata->epg_times.startzeit = ((CSectionsdClient::sectionsdTime *) dp)->startzeit; + epgdata->epg_times.dauer = ((CSectionsdClient::sectionsdTime *) dp)->dauer; + dp+= sizeof(CSectionsdClient::sectionsdTime); + + delete[] pData; + return true; + } + else + printf("no response from sectionsd\n"); + } + + close_connection(); + + return false; +} + + +bool CSectionsdClient::getEPGidShort(const event_id_t eventid, CShortEPGData * epgdata) +{ + if (send(sectionsd::epgEPGidShort, (char*)&eventid, sizeof(eventid))) + { + int nBufSize = readResponse(); + if( nBufSize > 0) + { + char* pData = new char[nBufSize]; + receive_data(pData, nBufSize); + + close_connection(); + + char* dp = pData; + + for(int i = 0; i < nBufSize;i++) + if(((unsigned char)pData[i]) == 0xff) + pData[i] = 0; + + epgdata->title = dp; + dp+=strlen(dp)+1; + epgdata->info1 = dp; + dp+=strlen(dp)+1; + epgdata->info2 = dp; + dp+=strlen(dp)+1; +// printf("titel: %s\n",epgdata->title.c_str()); + + + delete[] pData; + return true; + } + else + printf("no response from sectionsd\n"); + } + + close_connection(); + + return false; +} +#ifdef ENABLE_PPT +void CSectionsdClient::setPrivatePid(const unsigned short pid) +{ + send(sectionsd::setPrivatePid, (char*)&pid, sizeof(pid)); + + readResponse(); + close_connection(); +} +#endif +#if 0 +void CSectionsdClient::setSectionsdScanMode(const int scanMode) +{ + send(sectionsd::setSectionsdScanMode, (char*)&scanMode, sizeof(scanMode)); + + readResponse(); + close_connection(); +} +#endif + +void CSectionsdClient::freeMemory() +{ + send(sectionsd::freeMemory); + + readResponse(); + close_connection(); +} + +void CSectionsdClient::readSIfromXML(const char * epgxmlname) +{ + send(sectionsd::readSIfromXML, (char*) epgxmlname, strlen(epgxmlname)); + + readResponse(); + close_connection(); +} + +void CSectionsdClient::writeSI2XML(const char * epgxmlname) +{ + send(sectionsd::writeSI2XML, (char*) epgxmlname, strlen(epgxmlname)); + + readResponse(); + close_connection(); +} + +void CSectionsdClient::setConfig(const epg_config config) +{ + sectionsd::commandSetConfig *msg; + char* pData = new char[sizeof(sectionsd::commandSetConfig) + config.network_ntpserver.length() + 1 + config.epg_dir.length() + 1]; + msg = (sectionsd::commandSetConfig *)pData; + + msg->scanMode = config.scanMode; + msg->epg_cache = config.epg_cache; + msg->epg_old_events = config.epg_old_events; + msg->epg_max_events = config.epg_max_events; + msg->network_ntprefresh = config.network_ntprefresh; + msg->network_ntpenable = config.network_ntpenable; + msg->epg_extendedcache = config.epg_extendedcache; +// config.network_ntpserver: + strcpy(&pData[sizeof(sectionsd::commandSetConfig)], config.network_ntpserver.c_str()); +// config.epg_dir: + strcpy(&pData[sizeof(sectionsd::commandSetConfig) + config.network_ntpserver.length() + 1], config.epg_dir.c_str()); + + send(sectionsd::setConfig, (char*)pData, sizeof(sectionsd::commandSetConfig) + config.network_ntpserver.length() + 1 + config.epg_dir.length() + 1); + readResponse(); + close_connection(); + delete[] pData; +} + +void CSectionsdClient::dumpStatus() +{ + send(sectionsd::dumpStatusinformation); + close_connection(); +} diff --git a/lib/sectionsdclient/sectionsdclient.h b/lib/sectionsdclient/sectionsdclient.h new file mode 100644 index 000000000..458a2f764 --- /dev/null +++ b/lib/sectionsdclient/sectionsdclient.h @@ -0,0 +1,251 @@ +#ifndef __sectionsdclient__ +#define __sectionsdclient__ +/* + Client-Interface für zapit - DBoxII-Project + + $Id: sectionsdclient.h,v 1.42 2007/01/12 22:57:57 houdini Exp $ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include + +#include + +class CShortEPGData +{ + public: + std::string title; + std::string info1; + std::string info2; + + CShortEPGData() + { + title = ""; + info1 = ""; + info2 = ""; + }; +}; + +class CEPGData; + +class CChannelEvent +{ + public: + t_channel_id get_channel_id(void) const { return GET_CHANNEL_ID_FROM_EVENT_ID(eventID); } + event_id_t eventID; + std::string description; + std::string text; + time_t startTime; + unsigned duration; +}; + +typedef std::vector CChannelEventList; + +class CSectionsdClient : private CBasicClient +{ + private: + virtual const unsigned char getVersion () const; + virtual const char * getSocketName() const; + + int readResponse(char* data = NULL, unsigned int size = 0); + bool send(const unsigned char command, const char* data = NULL, const unsigned int size = 0); + char * parseExtendedEvents(char * dp, CEPGData * epgdata); + + public: + enum SIlanguageMode_t { + ALL, + FIRST_FIRST, + FIRST_ALL, + ALL_FIRST, + ALL_ALL, + LANGUAGE_MODE_OFF + }; + + enum events + { + EVT_TIMESET, + EVT_GOT_CN_EPG, + EVT_SERVICES_UPDATE, + EVT_BOUQUETS_UPDATE, + EVT_WRITE_SI_FINISHED + }; + + struct epgflags { + enum + { + has_anything = 0x01, + has_later = 0x02, + has_current = 0x04, + not_broadcast = 0x08, + has_next = 0x10, + has_no_current= 0x20, + current_has_linkagedescriptors= 0x40 + }; + }; + + struct responseGetComponentTags + { + std::string component; // Text aus dem Component Descriptor + unsigned char componentType; // Component Descriptor + unsigned char componentTag; // Component Descriptor + unsigned char streamContent; // Component Descriptor + }; + typedef std::vector ComponentTagList; + + struct responseGetLinkageDescriptors + { + std::string name; + t_transport_stream_id transportStreamId; + t_original_network_id originalNetworkId; + t_service_id serviceId; + }; + typedef std::vector LinkageDescriptorList; + + struct sectionsdTime + { + time_t startzeit; + unsigned dauer; + } __attribute__ ((packed)) ; + + struct responseGetNVODTimes + { + t_service_id service_id; + t_original_network_id original_network_id; + t_transport_stream_id transport_stream_id; + CSectionsdClient::sectionsdTime zeit; + }; + typedef std::vector NVODTimesList; + + struct responseGetCurrentNextInfoChannelID + { + event_id_t current_uniqueKey; + CSectionsdClient::sectionsdTime current_zeit; + std::string current_name; + char current_fsk; + event_id_t next_uniqueKey; + CSectionsdClient::sectionsdTime next_zeit; + std::string next_name; + unsigned flags; + }; + + struct CurrentNextInfo : public responseGetCurrentNextInfoChannelID + {}; + + typedef struct + { + int scanMode; + int epg_cache; + int epg_old_events; + int epg_max_events; + int network_ntprefresh; + int network_ntpenable; + int epg_extendedcache; + std::string network_ntpserver; + std::string epg_dir; + } epg_config; + + bool getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags); + + bool getLinkageDescriptorsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::LinkageDescriptorList& descriptors); + + bool getNVODTimesServiceKey(const t_channel_id channel_id, CSectionsdClient::NVODTimesList& nvod_list); + + bool getCurrentNextServiceKey(const t_channel_id channel_id, CSectionsdClient::responseGetCurrentNextInfoChannelID& current_next); + + bool getIsTimeSet(); + +// void setEventsAreOldInMinutes(const unsigned short minutes); + + void setPauseScanning(const bool doPause); + + bool getIsScanningActive(); + + void setPauseSorting(const bool doPause); + + void setServiceChanged(const t_channel_id channel_id, const bool requestEvent); + + CChannelEventList getChannelEvents(const bool tv_mode = true, t_channel_id* = NULL, int size = 0); + + CChannelEventList getEventsServiceKey(const t_channel_id channel_id); + + bool getEventsServiceKeySearchAdd(CChannelEventList& evtlist,const t_channel_id channel_id,char m_search_typ,std::string& m_search_text); + + bool getEPGid(const event_id_t eventid, const time_t starttime, CEPGData * epgdata); + + bool getActualEPGServiceKey(const t_channel_id channel_id, CEPGData * epgdata); + + bool getEPGidShort(const event_id_t eventid, CShortEPGData * epgdata); + + void setPrivatePid(const unsigned short pid); + +// void setSectionsdScanMode(const int scanMode); + + void freeMemory(); + + void readSIfromXML(const char * epgxmlname); + + void writeSI2XML(const char * epgxmlname); + + /* + ein beliebiges Event anmelden + */ + void registerEvent(const unsigned int eventID, const unsigned int clientID, const char * const udsName); + + /* + ein beliebiges Event abmelden + */ + void unRegisterEvent(const unsigned int eventID, const unsigned int clientID); + + void setConfig(const epg_config config); + void dumpStatus(void); + +}; + +class CEPGData +{ + public: + unsigned long long eventID; + CSectionsdClient::sectionsdTime epg_times; + std::string title; + std::string info1; + std::string info2; + // 21.07.2005 - extended event data + std::vector itemDescriptions; + std::vector items; + char fsk; + std::string contentClassification; + std::string userClassification; + + CEPGData() + { + eventID = 0; + title = ""; + info1 = ""; + info2 = ""; + fsk = 0; + contentClassification = ""; + userClassification = ""; + }; + +}; + +#endif diff --git a/lib/sectionsdclient/sectionsdcontrol.cpp b/lib/sectionsdclient/sectionsdcontrol.cpp new file mode 100644 index 000000000..fb40c867b --- /dev/null +++ b/lib/sectionsdclient/sectionsdcontrol.cpp @@ -0,0 +1,75 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/lib/sectionsdclient/sectionsdcontrol.cpp,v 1.2 2003/06/18 12:19:22 alexw Exp $ + * + * Sectionsd command line interface - The Tuxbox Project + * + * (C) 2003 by thegoodguy + * + * License: GPL + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include + +CSectionsdClient client; + +int main(int argc, char** argv) +{ + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "--pause")) + { + printf("setPauseScanning true\n"); + client.setPauseScanning(true); + } + else + if (!strcmp(argv[i], "--nopause")) + { + printf("setPauseScanning false\n"); + client.setPauseScanning(false); + } + else + if (!strcmp(argv[i], "--state")) + printf("Scanning is active: %s\n", client.getIsScanningActive()?"true":"false"); + else + if (!strcmp(argv[i], "--saveepg")) { + if(argv[i+1]) { + char path[255]; + sprintf(path, "%s/", argv[i+1]); + printf("writing epg cache to %s...\n", path); + client.writeSI2XML(path); + } + } + else + if (!strcmp(argv[i], "--readepg")) { + if(argv[i+1]) { + char path[255]; + sprintf(path, "%s/", argv[i+1]); + printf("Reading epg cache from %s....\n", path); + client.readSIfromXML(path); + } + } + else + if (!strcmp(argv[i], "--dump")) { + client.dumpStatus(); + } + } + + return 0; +} diff --git a/lib/sectionsdclient/sectionsdtypes.h b/lib/sectionsdclient/sectionsdtypes.h new file mode 100644 index 000000000..54f07fecb --- /dev/null +++ b/lib/sectionsdclient/sectionsdtypes.h @@ -0,0 +1,33 @@ +/* + * $Id: sectionsdtypes.h,v 1.1 2004/02/13 14:36:19 thegoodguy Exp $ + * + * sectionsd's types which are used by the clientlib - d-box2 linux project + * + * (C) 2004 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __sectionsdtypes_h__ +#define __sectionsdtypes_h__ + +#include /* t_channel_id, t_service_id, t_original_network_id, t_transport_stream_id; */ + +typedef uint64_t event_id_t; +#define CREATE_EVENT_ID(channel_id,event_nr) ((((event_id_t)channel_id) << 16) | event_nr) +#define GET_CHANNEL_ID_FROM_EVENT_ID(event_id) ((t_channel_id)((event_id) >> 16)) + +#endif /* __sectionsdtypes_h__ */ diff --git a/lib/timerdclient/Makefile.am b/lib/timerdclient/Makefile.am new file mode 100644 index 000000000..ead4c2c73 --- /dev/null +++ b/lib/timerdclient/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libtimerdclient.a + +libtimerdclient_a_SOURCES = timerdclient.cpp diff --git a/lib/timerdclient/timerdclient.cpp b/lib/timerdclient/timerdclient.cpp new file mode 100644 index 000000000..3348b32f6 --- /dev/null +++ b/lib/timerdclient/timerdclient.cpp @@ -0,0 +1,483 @@ +/* + Timer Daemon - DBoxII-Project + + Copyright (C) 2002 Dirk Szymanski 'Dirch' + + $Id: timerdclient.cpp,v 1.55 2007/10/09 20:46:05 guenther Exp $ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include +#include +#include + + +const unsigned char CTimerdClient::getVersion () const +{ + return CTimerdMsg::ACTVERSION; +} + +const char * CTimerdClient::getSocketName() const +{ + return TIMERD_UDS_NAME; +} + +//------------------------------------------------------------------------- + +void CTimerdClient::registerEvent(unsigned int eventID, unsigned int clientID, const char * const udsName) +{ + CEventServer::commandRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + + strcpy(msg2.udsName, udsName); + + send(CTimerdMsg::CMD_REGISTEREVENT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} +//------------------------------------------------------------------------- + +void CTimerdClient::unRegisterEvent(unsigned int eventID, unsigned int clientID) +{ + CEventServer::commandUnRegisterEvent msg2; + + msg2.eventID = eventID; + msg2.clientID = clientID; + + send(CTimerdMsg::CMD_UNREGISTEREVENT, (char*)&msg2, sizeof(msg2)); + + close_connection(); +} +//------------------------------------------------------------------------- + +int CTimerdClient::setSleeptimer(time_t announcetime, time_t alarmtime, int timerid) +{ + int timerID; + + if(timerid == 0) + timerID = getSleeptimerID(); + else + timerID = timerid; + + if(timerID != 0) + { + modifyTimerEvent(timerID, announcetime, alarmtime, 0); + } + else + { + timerID = addTimerEvent(CTimerd::TIMER_SLEEPTIMER,NULL,announcetime,alarmtime,0); + } + + return timerID; +} +//------------------------------------------------------------------------- + +int CTimerdClient::getSleeptimerID() +{ + send(CTimerdMsg::CMD_GETSLEEPTIMER); + CTimerdMsg::responseGetSleeptimer response; + if(!receive_data((char*)&response, sizeof(CTimerdMsg::responseGetSleeptimer))) + response.eventID =0; + close_connection(); + return response.eventID; +} +//------------------------------------------------------------------------- + +int CTimerdClient::getSleepTimerRemaining() +{ + int timerID; + if((timerID = getSleeptimerID()) != 0) + { + CTimerd::responseGetTimer timer; + getTimer( timer, timerID); + int min=(((timer.alarmTime + 1 - time(NULL)) / 60)+1); //aufrunden auf n�chst gr��erere Min. + if(min <1) + min=1; + return min; + } + else + return 0; +} +//------------------------------------------------------------------------- + +void CTimerdClient::getTimerList(CTimerd::TimerList &timerlist) +{ + CTimerdMsg::generalInteger responseInteger; + CTimerd::responseGetTimer response; + + send(CTimerdMsg::CMD_GETTIMERLIST); + + timerlist.clear(); + + if (CBasicClient::receive_data((char* )&responseInteger, sizeof(responseInteger))) + { + while (responseInteger.number-- > 0) + { + if (CBasicClient::receive_data((char*)&response, sizeof(response))) + if (response.eventState != CTimerd::TIMERSTATE_TERMINATED) + timerlist.push_back(response); + }; + } + + close_connection(); +} +//------------------------------------------------------------------------- + +void CTimerdClient::getTimer( CTimerd::responseGetTimer &timer, unsigned timerID) +{ + send(CTimerdMsg::CMD_GETTIMER, (char*)&timerID, sizeof(timerID)); + + CTimerd::responseGetTimer response; + receive_data((char*)&response, sizeof(CTimerd::responseGetTimer)); + timer = response; + close_connection(); +} +//------------------------------------------------------------------------- + + +bool CTimerdClient::modifyTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount) +{ + return modifyTimerEvent(eventid,announcetime,alarmtime,stoptime,evrepeat,repeatcount,NULL); +} +//------------------------------------------------------------------------- + +bool CTimerdClient::modifyTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount, void *data, + int datalen) +{ + // set new time values for event eventid + + CTimerdMsg::commandModifyTimer msgModifyTimer; + msgModifyTimer.eventID = eventid; + msgModifyTimer.announceTime = announcetime; + msgModifyTimer.alarmTime = alarmtime; + msgModifyTimer.stopTime = stoptime; + msgModifyTimer.eventRepeat = evrepeat; + msgModifyTimer.repeatCount = repeatcount; + send(CTimerdMsg::CMD_MODIFYTIMER, (char*) &msgModifyTimer, sizeof(msgModifyTimer)); + + if (data && datalen) + send_data((char*)data,datalen); + + CTimerdMsg::responseStatus response; + receive_data((char*)&response, sizeof(response)); + + close_connection(); + return true; +} +//------------------------------------------------------------------------- + +bool CTimerdClient::modifyRecordTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount, + const char * const recordingdir) +{ + CTimerdMsg::commandRecordDir rdir; + strncpy(rdir.recDir,recordingdir,RECORD_DIR_MAXLEN-1); + return modifyTimerEvent(eventid,announcetime,alarmtime,stoptime,evrepeat,repeatcount,&rdir,sizeof(rdir)); +} +//------------------------------------------------------------------------- + +bool CTimerdClient::rescheduleTimerEvent(int eventid, time_t diff) +{ + rescheduleTimerEvent(eventid,diff,diff,diff); + return true; +} +//------------------------------------------------------------------------- + +bool CTimerdClient::rescheduleTimerEvent(int eventid, time_t announcediff, time_t alarmdiff, time_t stopdiff) +{ + CTimerdMsg::commandModifyTimer msgModifyTimer; + msgModifyTimer.eventID = eventid; + msgModifyTimer.announceTime = announcediff; + msgModifyTimer.alarmTime = alarmdiff; + msgModifyTimer.stopTime = stopdiff; + + send(CTimerdMsg::CMD_RESCHEDULETIMER, (char*) &msgModifyTimer, sizeof(msgModifyTimer)); + + CTimerdMsg::responseStatus response; + receive_data((char*)&response, sizeof(response)); + + close_connection(); + return response.status; +} +//------------------------------------------------------------------------- + +/* +int CTimerdClient::addTimerEvent( CTimerEventTypes evType, void* data , int min, int hour, int day, int month, CTimerd::CTimerEventRepeat evrepeat) +{ + time_t actTime_t; + time(&actTime_t); + struct tm* actTime = localtime(&actTime_t); + + actTime->tm_min = min; + actTime->tm_hour = hour; + + if (day > 0) + actTime->tm_mday = day; + if (month > 0) + actTime->tm_mon = month -1; + + addTimerEvent(evType,true,data,0,mktime(actTime),0); +} +*/ +//------------------------------------------------------------------------- +int CTimerdClient::addTimerEvent( CTimerd::CTimerEventTypes evType, void* data, time_t announcetime, time_t alarmtime,time_t stoptime, + CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount,bool forceadd) +{ + + if (!forceadd) + { + //printf("[CTimerdClient] checking for overlapping timers\n"); + CTimerd::TimerList overlappingTimer; + overlappingTimer = getOverlappingTimers(alarmtime, stoptime); + if (overlappingTimer.size() > 0) + { + // timerd starts eventID at 0 so we can return -1 + return -1; + } + } + + CTimerd::TransferEventInfo tei; + CTimerd::TransferRecordingInfo tri; + CTimerdMsg::commandAddTimer msgAddTimer; + msgAddTimer.alarmTime = alarmtime; + msgAddTimer.announceTime = announcetime; + msgAddTimer.stopTime = stoptime; + msgAddTimer.eventType = evType; + msgAddTimer.eventRepeat = evrepeat; + msgAddTimer.repeatCount = repeatcount; + int length; + if( evType == CTimerd::TIMER_SHUTDOWN || evType == CTimerd::TIMER_SLEEPTIMER ) + { + length = 0; + } + else if(evType == CTimerd::TIMER_NEXTPROGRAM || evType == CTimerd::TIMER_ZAPTO || + evType == CTimerd::TIMER_IMMEDIATE_RECORD ) + { + CTimerd::EventInfo *ei=static_cast(data); + tei.apids = ei->apids; + tei.channel_id = ei->channel_id; + tei.epg_starttime = ei->epg_starttime; + tei.epgID = ei->epgID; + tei.recordingSafety = ei->recordingSafety; + length = sizeof( CTimerd::TransferEventInfo); + data = &tei; + } + else if(evType == CTimerd::TIMER_RECORD) + { + CTimerd::RecordingInfo *ri=static_cast(data); + tri.apids = ri->apids; + tri.channel_id = ri->channel_id; + tri.epg_starttime = ri->epg_starttime; + tri.epgID = ri->epgID; + tri.recordingSafety = ri->recordingSafety; + strncpy(tri.recordingDir, ri->recordingDir, RECORD_DIR_MAXLEN-1); + length = sizeof( CTimerd::TransferRecordingInfo); + data = &tri; + } + else if(evType == CTimerd::TIMER_STANDBY) + { + length = sizeof(CTimerdMsg::commandSetStandby); + } + else if(evType == CTimerd::TIMER_REMIND) + { + length = sizeof(CTimerdMsg::commandRemind); + } + else if(evType == CTimerd::TIMER_EXEC_PLUGIN) + { + length = sizeof(CTimerdMsg::commandExecPlugin); + } + else + { + length = 0; + } + + send(CTimerdMsg::CMD_ADDTIMER, (char*)&msgAddTimer, sizeof(msgAddTimer)); + + if((data != NULL) && (length > 0)) + send_data((char*)data, length); + + CTimerdMsg::responseAddTimer response; + receive_data((char*)&response, sizeof(response)); + close_connection(); + + return( response.eventID); +} +//------------------------------------------------------------------------- + +void CTimerdClient::removeTimerEvent( int evId) +{ + CTimerdMsg::commandRemoveTimer msgRemoveTimer; + + msgRemoveTimer.eventID = evId; + + send(CTimerdMsg::CMD_REMOVETIMER, (char*) &msgRemoveTimer, sizeof(msgRemoveTimer)); + + close_connection(); +} +//------------------------------------------------------------------------- + +bool CTimerdClient::isTimerdAvailable() +{ + if(!send(CTimerdMsg::CMD_TIMERDAVAILABLE)) + return false; + + CTimerdMsg::responseAvailable response; + bool ret=receive_data((char*)&response, sizeof(response)); + close_connection(); + return ret; +} +//------------------------------------------------------------------------- + +CTimerd::TimerList CTimerdClient::getOverlappingTimers(time_t& startTime, time_t& stopTime) +{ + CTimerd::TimerList timerlist; + CTimerd::TimerList overlapping; + int timerPre; + int timerPost; + + getTimerList(timerlist); + getRecordingSafety(timerPre,timerPost); + + for (CTimerd::TimerList::iterator it = timerlist.begin(); + it != timerlist.end();it++) + { + if(it->stopTime != 0 && stopTime != 0) + { + // Check if both timers have start and end. In this case do not show conflict, if endtime is the same than the starttime of the following timer + if ((stopTime+timerPost > it->alarmTime) && (startTime-timerPre < it->stopTime)) + { + overlapping.push_back(*it); + } + } + else + { + if (!((stopTime < it->announceTime) || (startTime > it->stopTime))) + { + overlapping.push_back(*it); + } + } + } + return overlapping; +} + +//------------------------------------------------------------------------- + +bool CTimerdClient::shutdown() +{ + send(CTimerdMsg::CMD_SHUTDOWN); + + CTimerdMsg::responseStatus response; + receive_data((char*)&response, sizeof(response)); + + close_connection(); + return response.status; +} +//------------------------------------------------------------------------- +void CTimerdClient::modifyTimerAPid(int eventid, unsigned char apids) +{ + CTimerdMsg::commandSetAPid data; + data.eventID=eventid; + data.apids = apids; + send(CTimerdMsg::CMD_SETAPID, (char*) &data, sizeof(data)); + close_connection(); +} + +//------------------------------------------------------------------------- +void CTimerdClient::setRecordingSafety(int pre, int post) +{ + CTimerdMsg::commandRecordingSafety data; + data.pre = pre; + data.post = post; + send(CTimerdMsg::CMD_SETRECSAFETY, (char*) &data, sizeof(data)); + close_connection(); +} + +//------------------------------------------------------------------------- +void CTimerdClient::getRecordingSafety(int &pre, int &post) +{ + send(CTimerdMsg::CMD_GETRECSAFETY); + CTimerdMsg::commandRecordingSafety data; + + bool success = receive_data((char*)&data, sizeof(data)); + close_connection(); + if (success) + { + pre = data.pre; + post = data.post; + } + else + { + /* fill with default values (cf. timermanager.cpp) */ + pre = 0; + post = 0; + } +} + +//------------------------------------------------------------------------- +//void CTimerdClient::getWeekdaysFromStr(int *rep, const char* str) +void CTimerdClient::getWeekdaysFromStr(CTimerd::CTimerEventRepeat *eventRepeat, const char* str) +{ + int rep = (int) *eventRepeat; + if(rep >= (int)CTimerd::TIMERREPEAT_WEEKDAYS) + { + for(int n=0;n<7;n++) + { + if(str[n]=='X' || str[n]=='x') + { + rep |= (1 << (n+9)); + } + else + { + rep &= (~(1 << (n+9))); + } + } + } + *eventRepeat = (CTimerd::CTimerEventRepeat) rep; +} +//------------------------------------------------------------------------- +void CTimerdClient::setWeekdaysToStr(CTimerd::CTimerEventRepeat rep, char* str) +{ + if(rep >= CTimerd::TIMERREPEAT_WEEKDAYS) + { + for(int n=0;n<7;n++) + { + if(rep & (1 << (n+9))) + str[n]='X'; + else + str[n]='-'; + } + str[7]=0; + } + else + strcpy(str,"-------"); +} +//------------------------------------------------------------------------- +void CTimerdClient::stopTimerEvent( int evId) +{ + CTimerdMsg::commandRemoveTimer msgRemoveTimer; + + msgRemoveTimer.eventID = evId; + + send(CTimerdMsg::CMD_STOPTIMER, (char*) &msgRemoveTimer, sizeof(msgRemoveTimer)); + + close_connection(); +} + diff --git a/lib/timerdclient/timerdclient.h b/lib/timerdclient/timerdclient.h new file mode 100644 index 000000000..debc061fc --- /dev/null +++ b/lib/timerdclient/timerdclient.h @@ -0,0 +1,175 @@ +/* + Timer-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + $Id: timerdclient.h,v 1.47 2006/02/14 22:38:28 zwen Exp $ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __timerdclient__ +#define __timerdclient__ + +#include +#include + +#include + +#include + +class CTimerdClient:private CBasicClient +{ + private: + virtual const unsigned char getVersion () const; + virtual const char * getSocketName() const; + + public: + enum events + { + EVT_SHUTDOWN = 1, + EVT_ANNOUNCE_SHUTDOWN, + EVT_ZAPTO, + EVT_ANNOUNCE_ZAPTO, + EVT_NEXTPROGRAM, + EVT_ANNOUNCE_NEXTPROGRAM, + EVT_STANDBY_ON, + EVT_STANDBY_OFF, + EVT_RECORD_START, + EVT_RECORD_STOP, + EVT_ANNOUNCE_RECORD, + EVT_ANNOUNCE_SLEEPTIMER, + EVT_SLEEPTIMER, + EVT_REMIND, + EVT_EXEC_PLUGIN + }; + + void registerEvent(unsigned int eventID, unsigned int clientID, const char * const udsName); + void unRegisterEvent(unsigned int eventID, unsigned int clientID); + + bool isTimerdAvailable(); // check if timerd is running + + CTimerd::TimerList getOverlappingTimers(time_t& announcetime, time_t& stoptime); + + int addTimerEvent( CTimerd::CTimerEventTypes evType, void* data, time_t alarmtime,time_t announcetime = 0, time_t stoptime = 0, + CTimerd::CTimerEventRepeat evrepeat = CTimerd::TIMERREPEAT_ONCE, uint32_t repeatcount = 0, bool forceadd=true); + + void removeTimerEvent( int evId); // remove timer event + void stopTimerEvent( int evId); // set timer state to stoped (rescedule on demand) + + void getTimerList( CTimerd::TimerList &timerlist); // returns the list of all timers + void getTimer( CTimerd::responseGetTimer &timer, unsigned timerID); // returns specified timer + + // modify existing timer event + bool modifyTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat = CTimerd::TIMERREPEAT_ONCE, uint32_t repeatcount=0); + bool modifyTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount, + void *data,int datalen=0); + + bool modifyRecordTimerEvent(int eventid, time_t announcetime, time_t alarmtime, time_t stoptime, CTimerd::CTimerEventRepeat evrepeat, uint32_t repeatcount, const char * const recordingdir); + + void modifyTimerAPid(int eventid, unsigned char apids); + + // set existing sleeptimer to new times or create new sleeptimer with these times + int setSleeptimer(time_t announcetime, time_t alarmtime, int timerid = 0); + + // returns the id of sleeptimer, 0 of no sleeptimer exists + int getSleeptimerID(); + // returns remaining mins, -1 if no sleeptimer exists + int getSleepTimerRemaining(); + + + // add diff to existing timer event + bool rescheduleTimerEvent(int eventid, time_t diff); + + // add diff to existing timer event + bool rescheduleTimerEvent(int eventid, time_t announcediff, time_t alarmdiff, time_t stoptime); + + // adds new sleeptimer event + int addSleepTimerEvent(time_t announcetime,time_t alarmtime) // sleeptimer setzen + {return addTimerEvent(CTimerd::TIMER_SLEEPTIMER, NULL, announcetime, alarmtime, 0);}; + + // adds new shutdown timer event + int addShutdownTimerEvent(time_t alarmtime, time_t announcetime = 0, time_t stoptime = 0) + {return addTimerEvent(CTimerd::TIMER_SHUTDOWN, NULL, announcetime, alarmtime, stoptime);}; + + // adds new record timer event + int addRecordTimerEvent(const t_channel_id channel_id, time_t alarmtime, time_t stoptime, + unsigned long long epgID=0, time_t epg_starttime=0, time_t announcetime = 0, + unsigned char apids=TIMERD_APIDS_STD, bool safety=false,std::string recDir="", bool forceAdd=true) + { + CTimerd::RecordingInfo eventInfo; + eventInfo.channel_id = channel_id; + eventInfo.epgID = epgID; + eventInfo.epg_starttime = epg_starttime; + eventInfo.apids = apids; + eventInfo.recordingSafety = safety; + strncpy(eventInfo.recordingDir, recDir.c_str(), RECORD_DIR_MAXLEN); + return addTimerEvent(CTimerd::TIMER_RECORD, &eventInfo, announcetime, alarmtime, stoptime,CTimerd::TIMERREPEAT_ONCE, 0,forceAdd); + }; + + int addImmediateRecordTimerEvent(const t_channel_id channel_id, time_t alarmtime, time_t stoptime, + unsigned long long epgID=0, time_t epg_starttime=0,unsigned char apids=TIMERD_APIDS_STD) + { + CTimerd::EventInfo eventInfo; + eventInfo.channel_id = channel_id; + eventInfo.epgID = epgID; + eventInfo.epg_starttime = epg_starttime; + eventInfo.apids = apids; + eventInfo.recordingSafety = false; + return addTimerEvent(CTimerd::TIMER_IMMEDIATE_RECORD, &eventInfo, 0, alarmtime, stoptime); + }; + + // adds new standby timer event + int addStandbyTimerEvent(bool standby_on,time_t alarmtime, time_t announcetime = 0, time_t stoptime = 0) + {return addTimerEvent(CTimerd::TIMER_STANDBY, &standby_on, announcetime, alarmtime, stoptime);}; + + // adds new zapto timer event + int addZaptoTimerEvent(const t_channel_id channel_id, time_t alarmtime, time_t announcetime = 0, + time_t stoptime = 0, unsigned long long epgID=0, time_t epg_starttime=0, + unsigned char apids=TIMERD_APIDS_STD) + { + CTimerd::EventInfo eventInfo; + eventInfo.channel_id = channel_id; + eventInfo.epgID = epgID; + eventInfo.epg_starttime = epg_starttime; + eventInfo.apids = apids; + return addTimerEvent(CTimerd::TIMER_ZAPTO, &eventInfo, announcetime, alarmtime, stoptime); + }; + + int addNextProgramTimerEvent(CTimerd::EventInfo eventInfo,time_t alarmtime, time_t announcetime = 0, time_t stoptime = 0) + { + // mal auf verdacht eingebaut + // keine ahnung ob / was hier noch fehlt + return addTimerEvent(CTimerd::TIMER_NEXTPROGRAM, &eventInfo, alarmtime, announcetime, stoptime); + }; + + // Exit timerd and programm wakeup + bool shutdown(); + + // set and get recording safety time (in sec) + void setRecordingSafety(int pre, int post); + void getRecordingSafety(int &pre, int &post); + + // Convert String of O and X to repeat type and vice versa + //void getWeekdaysFromStr(int *rep, const char* str); + void getWeekdaysFromStr(CTimerd::CTimerEventRepeat *rep, const char* str); + void setWeekdaysToStr(CTimerd::CTimerEventRepeat rep, char* str); +}; + +#endif diff --git a/lib/timerdclient/timerdmsg.h b/lib/timerdclient/timerdmsg.h new file mode 100644 index 000000000..9efb9d61f --- /dev/null +++ b/lib/timerdclient/timerdmsg.h @@ -0,0 +1,155 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/lib/timerdclient/timerdmsg.h,v 1.11 2006/02/14 22:38:28 zwen Exp $ + * + * types used for clientlib <-> timerd communication - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __timerdmsg_h__ +#define __timerdmsg_h__ + + +#include + +#include "timerdtypes.h" // REMINDER_MESSAGE_MAXLEN + + +#define TIMERD_UDS_NAME "/tmp/timerd.sock" + + +class CTimerdMsg : public CBasicMessage +{ + + public: + + static const CBasicMessage::t_version ACTVERSION = 2; + + enum commands + { + CMD_ADDTIMER = 1, + CMD_REMOVETIMER, + CMD_GETTIMER, + CMD_GETTIMERLIST, + CMD_MODIFYTIMER, + CMD_GETSLEEPTIMER, + CMD_RESCHEDULETIMER, + + CMD_REGISTEREVENT, + CMD_UNREGISTEREVENT, + CMD_TIMERDAVAILABLE, + CMD_SHUTDOWN, + CMD_SETAPID, + CMD_GETRECSAFETY, + CMD_SETRECSAFETY, + CMD_STOPTIMER + }; + + + struct commandAddTimer + { + CTimerd::CTimerEventTypes eventType; + CTimerd::CTimerEventRepeat eventRepeat; + time_t alarmTime; + time_t announceTime; + time_t stopTime; + uint32_t repeatCount; + }; + + struct commandGetTimer + { + int eventID; + }; + + struct commandModifyTimer + { + int eventID; + time_t announceTime; + time_t alarmTime; + time_t stopTime; + CTimerd::CTimerEventRepeat eventRepeat; + uint32_t repeatCount; + }; + + + struct commandRemind + { + char message[REMINDER_MESSAGE_MAXLEN]; + }; + + struct commandExecPlugin + { + char name[EXEC_PLUGIN_NAME_MAXLEN]; + }; + + struct commandRecordDir + { + char recDir[RECORD_DIR_MAXLEN]; + }; + + struct commandSetAPid + { + int eventID; + unsigned char apids; + }; + + struct commandRemoveTimer + { + int eventID; + }; + + struct commandSetStandby + { + bool standby_on; + }; + + struct commandRecordingSafety + { + int pre; + int post; + }; + + + struct generalInteger + { + int number; + }; + + struct responseAddTimer + { + int eventID; + }; + + struct responseAvailable + { + bool available; + }; + + struct responseGetSleeptimer + { + int eventID; + }; + + struct responseStatus + { + bool status; + }; + +}; + +#endif /* __timerdmsg_h__ */ diff --git a/lib/timerdclient/timerdtypes.h b/lib/timerdclient/timerdtypes.h new file mode 100644 index 000000000..2bf9ce6a7 --- /dev/null +++ b/lib/timerdclient/timerdtypes.h @@ -0,0 +1,168 @@ +/* + Timer-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + $Id: timerdtypes.h,v 1.20 2006/02/28 21:51:01 zwen Exp $ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __timerdtypes__ +#define __timerdtypes__ + +#include +#include + +#include + +#define REMINDER_MESSAGE_MAXLEN 31 +#define EXEC_PLUGIN_NAME_MAXLEN 31 +#define RECORD_DIR_MAXLEN 100 +#define EPG_TITLE_MAXLEN 50 + +#define TIMERD_APIDS_CONF 0x00 +#define TIMERD_APIDS_STD 0x01 +#define TIMERD_APIDS_ALT 0x02 +#define TIMERD_APIDS_AC3 0x04 +#define TIMERD_APIDS_ALL 0xFF + +class CTimerd +{ + public: + enum CTimerEventRepeat + { + TIMERREPEAT_ONCE = 0, + TIMERREPEAT_DAILY, + TIMERREPEAT_WEEKLY, + TIMERREPEAT_BIWEEKLY, + TIMERREPEAT_FOURWEEKLY, + TIMERREPEAT_MONTHLY, + TIMERREPEAT_BYEVENTDESCRIPTION, + TIMERREPEAT_WEEKDAYS = 0x100 // Bits 9-15 specify weekdays (9=mo,10=di,...) + }; + + enum CTimerEventTypes + { + TIMER_SHUTDOWN = 1, + TIMER_NEXTPROGRAM, + TIMER_ZAPTO, + TIMER_STANDBY, + TIMER_RECORD, + TIMER_REMIND, + TIMER_SLEEPTIMER, + TIMER_EXEC_PLUGIN, + TIMER_IMMEDIATE_RECORD + }; + + enum CTimerEventStates + { + TIMERSTATE_SCHEDULED, + TIMERSTATE_PREANNOUNCE, + TIMERSTATE_ISRUNNING, + TIMERSTATE_HASFINISHED, + TIMERSTATE_TERMINATED + }; + + struct EventInfo + { + event_id_t epgID; + time_t epg_starttime; + t_channel_id channel_id; + unsigned char apids; + bool recordingSafety; + }; + + struct TransferEventInfo + { + event_id_t epgID; + time_t epg_starttime; + t_channel_id channel_id; + unsigned char apids; + bool recordingSafety; + }; + + struct TransferRecordingInfo : TransferEventInfo + { + char recordingDir[RECORD_DIR_MAXLEN]; + char epgTitle[EPG_TITLE_MAXLEN]; + + }; + + class RecordingInfo : public EventInfo + { + public: + RecordingInfo(){}; + RecordingInfo(EventInfo& e) + { + apids = e.apids; + channel_id = e.channel_id; + epgID = e.epgID; + epg_starttime = e.epg_starttime; + recordingSafety = e.recordingSafety; + }; + RecordingInfo& operator = (EventInfo& e) + { + apids = e.apids; + channel_id = e.channel_id; + epgID = e.epgID; + epg_starttime = e.epg_starttime; + recordingSafety = e.recordingSafety; + return *this; + } + unsigned char apids; + int eventID; + char recordingDir[RECORD_DIR_MAXLEN]; + char epgTitle[EPG_TITLE_MAXLEN]; + }; + + struct RecordingStopInfo + { + int eventID; + }; + + struct responseGetTimer + { + int eventID; + CTimerEventTypes eventType; + CTimerEventStates eventState; + CTimerEventRepeat eventRepeat; + uint32_t repeatCount; + time_t alarmTime; + time_t announceTime; + time_t stopTime; + t_channel_id channel_id; //only filled if applicable + event_id_t epgID; //only filled if applicable + time_t epg_starttime; //only filled if applicable + unsigned char apids; //only filled if applicable + bool standby_on; //only filled if applicable + char message[REMINDER_MESSAGE_MAXLEN]; //only filled if applicable + char pluginName[EXEC_PLUGIN_NAME_MAXLEN]; //only filled if applicable + char recordingDir[RECORD_DIR_MAXLEN]; //only filled if applicable + char epgTitle[EPG_TITLE_MAXLEN]; //only filled if applicable + + bool operator< (const responseGetTimer& a) const + { + return this->alarmTime < a.alarmTime ; + } + }; + + typedef std::vector TimerList; +}; +#endif diff --git a/lib/xmltree/Makefile.am b/lib/xmltree/Makefile.am new file mode 100644 index 000000000..045af2a79 --- /dev/null +++ b/lib/xmltree/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = -I$(srcdir)/xmltok + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libtuxbox-xmltree.a + +libtuxbox_xmltree_a_SOURCES = \ + hashtab.c xmlparse.cpp xmlrole.c xmltok.c xmltree.cpp xmlinterface.cpp diff --git a/lib/xmltree/asciitab.h b/lib/xmltree/asciitab.h new file mode 100644 index 000000000..f7d78da4f --- /dev/null +++ b/lib/xmltree/asciitab.h @@ -0,0 +1,52 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_NMSTRT, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/lib/xmltree/hashtab.c b/lib/xmltree/hashtab.c new file mode 100644 index 000000000..9e164a959 --- /dev/null +++ b/lib/xmltree/hashtab.c @@ -0,0 +1,134 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +csompliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#include +#include + +#include "xmldef.h" +#include "hashtab.h" + +#ifdef XML_UNICODE +#define keycmp wcscmp +#else +#define keycmp strcmp +#endif + +#define INIT_SIZE 64 + +static +unsigned long hash(KEY s) +{ + unsigned long h = 0; + while (*s) + h = (h << 5) + h + (unsigned char)*s++; + return h; +} + +NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + if (!createSize) + return 0; + table->v = (NAMED**)calloc(INIT_SIZE, sizeof(NAMED *)); + if (!table->v) + return 0; + table->size = INIT_SIZE; + table->usedLim = INIT_SIZE / 2; + i = hash(name) & (table->size - 1); + } + else { + unsigned long h = hash(name); + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) { + if (keycmp(name, table->v[i]->name) == 0) + return table->v[i]; + } + if (!createSize) + return 0; + if (table->used == table->usedLim) { + /* check for overflow */ + size_t newSize = table->size * 2; + NAMED **newV = (NAMED**)calloc(newSize, sizeof(NAMED *)); + if (!newV) + return 0; + for (i = 0; i < table->size; i++) + if (table->v[i]) { + size_t j; + for (j = hash(table->v[i]->name) & (newSize - 1); + newV[j]; + j == 0 ? j = newSize - 1 : --j) + ; + newV[j] = table->v[i]; + } + free(table->v); + table->v = newV; + table->size = newSize; + table->usedLim = newSize/2; + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) + ; + } + } + table->v[i] = (NAMED*)calloc(1, createSize); + if (!table->v[i]) + return 0; + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +void hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + NAMED *p = table->v[i]; + if (p) + free(p); + } + free(table->v); +} + +void hashTableInit(HASH_TABLE *p) +{ + p->size = 0; + p->usedLim = 0; + p->used = 0; + p->v = 0; +} + +void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +NAMED *hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return 0; +} + diff --git a/lib/xmltree/hashtab.h b/lib/xmltree/hashtab.h new file mode 100644 index 000000000..27de67086 --- /dev/null +++ b/lib/xmltree/hashtab.h @@ -0,0 +1,59 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE +typedef const wchar_t *KEY; +#else +typedef const char *KEY; +#endif + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + size_t size; + size_t used; + size_t usedLim; +} HASH_TABLE; + +NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize); +void hashTableInit(HASH_TABLE *); +void hashTableDestroy(HASH_TABLE *); + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +NAMED *hashTableIterNext(HASH_TABLE_ITER *); + +#ifdef __cplusplus +} +#endif diff --git a/lib/xmltree/iasctab.h b/lib/xmltree/iasctab.h new file mode 100644 index 000000000..d89407477 --- /dev/null +++ b/lib/xmltree/iasctab.h @@ -0,0 +1,53 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_NMSTRT, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/lib/xmltree/xmlinterface.cpp b/lib/xmltree/xmlinterface.cpp new file mode 100644 index 000000000..82d262534 --- /dev/null +++ b/lib/xmltree/xmlinterface.cpp @@ -0,0 +1,178 @@ +/* + * $Header: /cvs/tuxbox/apps/dvb/zapit/src/xmlinterface.cpp,v 1.25 2004/04/07 19:33:21 thegoodguy Exp $ + * + * xmlinterface for zapit - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#include + +#ifdef USE_LIBXML +#include +#include +#include +#else /* USE_LIBXML */ +#include +#endif /* USE_LIBXML */ + + +unsigned long xmlGetNumericAttribute(const xmlNodePtr node, char *name, const int base) +{ + char *ptr = xmlGetAttribute(node, name); + + if (!ptr) + return 0; + + return strtoul(ptr, 0, base); +} + +long xmlGetSignedNumericAttribute(const xmlNodePtr node, char *name, const int base) +{ + char *ptr = xmlGetAttribute(node, name); + + if (!ptr) + return 0; + + return strtol(ptr, 0, base); +} + +xmlNodePtr xmlGetNextOccurence(xmlNodePtr cur, const char * s) +{ + while ((cur != NULL) && (strcmp(xmlGetName(cur), s) != 0)) + cur = cur->xmlNextNode; + return cur; +} + + +std::string Unicode_Character_to_UTF8(const int character) +{ +#ifdef USE_LIBXML + xmlChar buf[5]; + int length = xmlCopyChar(4, buf, character); + return std::string((char*)buf, length); +#else /* USE_LIBXML */ + char buf[XML_UTF8_ENCODE_MAX]; + int length = XmlUtf8Encode(character, buf); + return std::string(buf, length); +#endif /* USE_LIBXML */ +} + +#ifdef USE_LIBXML +xmlDocPtr parseXmlFile(const char * filename) +{ + xmlDocPtr doc; + xmlNodePtr cur; + + doc = xmlParseFile(filename); + + if (doc == NULL) + { + WARN("Error parsing \"%s\"", filename); + return NULL; + } + else + { + cur = xmlDocGetRootElement(doc); + if (cur == NULL) + { + WARN("Empty document\n"); + xmlFreeDoc(doc); + return NULL; + } + else + return doc; + } +} +#else /* USE_LIBXML */ +xmlDocPtr parseXml(const char * data) +{ + XMLTreeParser* tree_parser; + + tree_parser = new XMLTreeParser(NULL); + + if (!tree_parser->Parse(data, strlen(data), true)) + { + printf("Error parsing XML Data: %s at line %d\n", + tree_parser->ErrorString(tree_parser->GetErrorCode()), + tree_parser->GetCurrentLineNumber()); + + delete tree_parser; + return NULL; + } + + if (!tree_parser->RootNode()) + { + printf("Error: No Root Node\n"); + delete tree_parser; + return NULL; + } + return tree_parser; +} + +xmlDocPtr parseXmlFile(const char * filename) +{ + char buffer[2048]; + XMLTreeParser* tree_parser; + size_t done; + size_t length; + FILE* xml_file; + + xml_file = fopen(filename, "r"); + + if (xml_file == NULL) + { + perror(filename); + return NULL; + } + + tree_parser = new XMLTreeParser(NULL); + + do + { + length = fread(buffer, 1, sizeof(buffer), xml_file); + done = length < sizeof(buffer); + + if (!tree_parser->Parse(buffer, length, done)) + { + printf("Error parsing \"%s\": %s at line %d\n", + filename, + tree_parser->ErrorString(tree_parser->GetErrorCode()), + tree_parser->GetCurrentLineNumber()); + + fclose(xml_file); + delete tree_parser; + return NULL; + } + } + while (!done); + + fclose(xml_file); + + if (!tree_parser->RootNode()) + { + delete tree_parser; + return NULL; + } + return tree_parser; +} +#endif /* USE_LIBXML */ diff --git a/lib/xmltree/xmlinterface.h b/lib/xmltree/xmlinterface.h new file mode 100644 index 000000000..c1faddd46 --- /dev/null +++ b/lib/xmltree/xmlinterface.h @@ -0,0 +1,93 @@ +/* + * $Header: /cvs/tuxbox/apps/dvb/zapit/include/zapit/xmlinterface.h,v 1.21 2004/04/07 19:33:21 thegoodguy Exp $ + * + * xmlinterface for zapit - d-box2 linux project + * + * (C) 2002 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __xmlinterface_h__ +#define __xmlinterface_h__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef USE_LIBXML +#include +#define xmlNextNode next +inline char* xmlGetAttribute (xmlNodePtr cur, const char * s) { return (char *)xmlGetProp(cur, (const xmlChar *)s); }; +inline char* xmlGetName (xmlNodePtr cur) { return (char *)(cur->name); }; + +#else /* use libxmltree */ +#include +typedef XMLTreeParser* xmlDocPtr; +typedef XMLTreeNode* xmlNodePtr; +#define xmlChildrenNode GetChild() +#define xmlNextNode GetNext() +inline xmlNodePtr xmlDocGetRootElement(xmlDocPtr doc) { return doc->RootNode(); }; +inline void xmlFreeDoc (xmlDocPtr doc) { delete doc; }; +inline char* xmlGetAttribute (xmlNodePtr cur, char * s) { return cur->GetAttributeValue(s); }; +inline char* xmlGetName (xmlNodePtr cur) { return cur->GetType(); }; +inline char* xmlGetData (xmlNodePtr cur) { return cur->GetData(); }; +#endif /* USE_LIBXML */ + + +unsigned long xmlGetNumericAttribute (const xmlNodePtr node, char *name, const int base); +long xmlGetSignedNumericAttribute (const xmlNodePtr node, char *name, const int base); +xmlNodePtr xmlGetNextOccurence (xmlNodePtr cur, const char * s); + +std::string Unicode_Character_to_UTF8(const int character); + +inline std::string convert_UTF8_To_UTF8_XML(const char * s) +{ + std::string r; + + while ((*s) != 0) + { + /* cf. http://www.w3.org/TR/xhtml1/dtds.html */ + switch (*s) + { + case '<': + r += "<"; + break; + case '>': + r += ">"; + break; + case '&': + r += "&"; + break; + case '\"': + r += """; + break; + case '\'': + r += "'"; + break; + default: + r += *s; + } + s++; + } + return r; +} +xmlDocPtr parseXml(const char *data); +xmlDocPtr parseXmlFile(const char * filename); + +#endif /* __xmlinterface_h__ */ diff --git a/lib/xmltree/xmlparse.cpp b/lib/xmltree/xmlparse.cpp new file mode 100644 index 000000000..666976bb0 --- /dev/null +++ b/lib/xmltree/xmlparse.cpp @@ -0,0 +1,2734 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): + + Fabian Giesen - converted this monster to C++ (at least the core) + +ChangeLog: + + $Log: xmlparse.cpp,v $ + Revision 1.3 2003/03/14 05:13:04 obi + compileable with -W -Werror + + Revision 1.2 2003/02/26 22:21:24 obi + Mismatched free() / delete / delete [] + + Revision 1.1 2002/01/18 20:22:39 tmbinc + initial checkin + + Revision 1.1.1.1 2001/10/07 13:01:18 tmbinc + Import of ezap2-200110070 + + Revision 1.1 1999/01/11 23:01:59 cvs + Another try to commit this. + +*/ + +#include +#include +#include + +#include "xmldef.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlEncode XmlUtf16Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) L ## x +#else +#define XML_T(x) x +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +#include "xmlparse.h" + +static int dtdInit(DTD *); +static void dtdDestroy(DTD *); +static int dtdCopy(DTD *newDtd, const DTD *oldDtd); + +static void poolInit(STRING_POOL *); +static void poolClear(STRING_POOL *); +static void poolDestroy(STRING_POOL *); +static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end); +static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end); +static int poolGrow(STRING_POOL *pool); +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) (((pool)->ptr == (pool)->end && !poolGrow(pool)) ? 0 : ((*((pool)->ptr)++ = c), 1)) + +XML_Parser::XML_Parser(const XML_Char *encodingName) +{ + processor=prologInitProcessor; + XmlPrologStateInit(&prologState); + + buffer=0; + bufferPtr=0; + bufferEnd=0; + parseEndByteIndex=0; + parseEndPtr=0; + bufferLim=0; + declElementType=0; + declAttributeId=0; + declEntity=0; + declNotationName=0; + declNotationPublicId=0; + + startElementHandler=0; + endElementHandler=0; + characterDataHandler=0; + processingInstructionHandler=0; + defaultHandler=0; + unparsedEntityDeclHandler=0; + notationDeclHandler=0; + externalEntityRefHandler=0; + unknownEncodingHandler=0; + + memset(&position, 0, sizeof(POSITION)); + + errorCode=XML_ERROR_NONE; + eventPtr=0; + eventEndPtr=0; + positionPtr=0; + tagLevel=0; + tagStack=0; + freeTagList=0; + attsSize=INIT_ATTS_SIZE; + atts=new ATTRIBUTE[attsSize]; + dataBuf=new XML_Char[INIT_DATA_BUF_SIZE]; + groupSize=0; + groupConnector=0; + hadExternalDoctype=0; + unknownEncodingMem=0; + unknownEncodingRelease=0; + unknownEncodingData=0; + unknownEncodingHandlerData=0; + + poolInit(&tempPool); + poolInit(&temp2Pool); + + protocolEncodingName=encodingName ? poolCopyString(&tempPool, encodingName) : 0; + + if (!dtdInit(&dtd) || !atts || !dataBuf || (encodingName && !protocolEncodingName)) + { + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); + + if (atts) delete[] atts; + if (dataBuf) delete[] dataBuf; + + return; + }; + + dataBufEnd=dataBuf+INIT_DATA_BUF_SIZE; + XmlInitEncoding(&initEncoding, &encoding, 0); +}; + +XML_Parser *XML_Parser::ExternalEntityParserCreate(const XML_Char *openEntityNames, const XML_Char *encodingName) +{ + XML_Parser *parser; + + parser=new XML_Parser(encodingName); + if (!parser) return 0; + + if (!dtdCopy(&parser->dtd, &dtd) || !parser->setOpenEntityNames(openEntityNames)) + { + delete parser; + return 0; + }; + + parser->processor=externalEntityInitProcessor; + return parser; +}; + +XML_Parser::~XML_Parser() +{ + for (;;) + { + TAG *p; + + if (!tagStack) + { + if (!freeTagList) break; + + tagStack=freeTagList; + freeTagList=0; + }; + + p=tagStack; + tagStack=tagStack->parent; + + delete[] p->buf; + delete p; + }; + + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); + dtdDestroy(&dtd); + + delete[] atts; + free(groupConnector); + free(buffer); + delete[] dataBuf; + free(unknownEncodingMem); + + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); +}; + +int XML_Parser::SetBase(const XML_Char *p) +{ + if (p) + { + p=poolCopyString(&dtd.pool, p); + if (!p) return 0; + + dtd.base=p; + } + else + dtd.base=0; + + return 1; +}; + +const XML_Char *XML_Parser::GetBase() +{ + return dtd.base; +}; + +int XML_Parser::Parse(const char *s, int len, int isFinal) +{ + if (!len) + { + if (!isFinal) return 1; + + errorCode=processor(this, bufferPtr, parseEndPtr=bufferEnd, 0); + if (errorCode==XML_ERROR_NONE) return 1; + + eventEndPtr=eventPtr; + return 0; + } + else if (bufferPtr==bufferEnd) + { + const char *end; + int nLeftOver; + + parseEndByteIndex+=len; + positionPtr=s; + + if (isFinal) + { + errorCode=processor(this, s, parseEndPtr=s+len, 0); + if (errorCode==XML_ERROR_NONE) return 1; + + eventEndPtr=eventPtr; + return 0; + }; + + errorCode=processor(this, s, parseEndPtr=s+len, &end); + if (errorCode!=XML_ERROR_NONE) + { + eventEndPtr=eventPtr; + return 0; + }; + + XmlUpdatePosition(encoding, positionPtr, end, &position); + + nLeftOver=s+len-end; + if (nLeftOver) + { + if (!buffer || nLeftOver>bufferLim-buffer) + { + buffer=buffer==0 ? (char *) malloc(len*2) : (char *) realloc(buffer, len*2); + + if (!buffer) + { + errorCode=XML_ERROR_NO_MEMORY; + eventPtr=eventEndPtr=0; + return 0; + }; + + bufferLim=buffer+len*2; + }; + + memcpy(buffer, end, nLeftOver); + + bufferPtr=buffer; + bufferEnd=buffer+nLeftOver; + }; + + return 1; + } + else + { + memcpy(GetBuffer(len), s, len); + return ParseBuffer(len, isFinal); + }; +}; + +int XML_Parser::ParseBuffer(int len, int isFinal) +{ + const char *start = bufferPtr; + + positionPtr=start; + bufferEnd+=len; + parseEndByteIndex+=len; + + errorCode=processor(this, start, parseEndPtr=bufferEnd, + isFinal ? (const char **) 0: &bufferPtr); + + if (errorCode==XML_ERROR_NONE) + { + if (!isFinal) + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + + return 1; + } + else + { + eventEndPtr=eventPtr; + return 0; + }; +}; + +void *XML_Parser::GetBuffer(int len) +{ + if (len>bufferLim-bufferEnd) + { + int neededSize=len+(bufferEnd-bufferPtr); + + if (neededSize<=bufferLim-buffer) + { + memmove(buffer, bufferPtr, bufferEnd-bufferPtr); + bufferEnd=buffer+(bufferEnd-bufferPtr); + bufferPtr=buffer; + } + else + { + char *newBuf; + int bufferSize=bufferLim-bufferPtr; + + if (!bufferSize) + bufferSize=INIT_BUFFER_SIZE; + + do + { + bufferSize*=2; + } while (bufferSize 0 && (unsigned)code < sizeof(message)/sizeof(message[0])) + return message[code]; + + return 0; +}; + +enum XML_Error contentProcessor(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + return p->doContent(0, p->encoding, start, end, endPtr); +}; + +enum XML_Error externalEntityInitProcessor(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + enum XML_Error result = p->initializeEncoding(); + if (result != XML_ERROR_NONE) + return result; + + p->processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(p, start, end, endPtr); +} + +enum XML_Error externalEntityInitProcessor2(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + const char *next; + + int tok=XmlContentTok(p->encoding, start, end, &next); + switch (tok) + { + case XML_TOK_BOM: + start=next; + break; + + case XML_TOK_PARTIAL: + if (endPtr) + { + *endPtr=start; + return XML_ERROR_NONE; + }; + + p->eventPtr=start; + return XML_ERROR_UNCLOSED_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + if (endPtr) + { + *endPtr=start; + return XML_ERROR_NONE; + }; + + p->eventPtr=start; + return XML_ERROR_PARTIAL_CHAR; + }; + + p->processor=externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +enum XML_Error externalEntityInitProcessor3(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + const char *next; + + int tok=XmlContentTok(p->encoding, start, end, &next); + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result=p->processXmlDecl(1, start, next); + + if (result!=XML_ERROR_NONE) + return result; + + start=next; + }; + + break; + + case XML_TOK_PARTIAL: + if (endPtr) + { + *endPtr=start; + return XML_ERROR_NONE; + }; + + p->eventPtr=start; + return XML_ERROR_UNCLOSED_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + if (endPtr) + { + *endPtr=start; + return XML_ERROR_NONE; + }; + + p->eventPtr=start; + return XML_ERROR_PARTIAL_CHAR; + } + + p->processor=externalEntityContentProcessor; + p->tagLevel=1; + + return p->doContent(1, p->encoding, start, end, endPtr); +} + +enum XML_Error externalEntityContentProcessor(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + return p->doContent(1, p->encoding, start, end, endPtr); +} + +enum XML_Error XML_Parser::doContent(int startTagLevel, const ENCODING *enc, const char *s, const char *end, const char **nextPtr) +{ + const ENCODING *internalEnc=XmlGetInternalEncoding(); + const char *dummy; + const char **eventPP; + const char **eventEndPP; + + if (enc==encoding) + { + eventPP=&eventPtr; + *eventPP=s; + eventEndPP=&eventEndPtr; + } + else + eventPP=eventEndPP=&dummy; + + for (;;) + { + const char *next; + int tok=XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + + switch (tok) { + case XML_TOK_TRAILING_CR: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + } + + *eventEndPP=end; + + if (characterDataHandler) + { + XML_Char c=XML_T('\n'); + CharacterDataHandler(&c, 1); + } + else + if (defaultHandler) + reportDefault(enc, s, end); + + if (!startTagLevel) return XML_ERROR_NO_ELEMENTS; + if (tagLevel!=startTagLevel) return XML_ERROR_ASYNC_ENTITY; + + return XML_ERROR_NONE; + + case XML_TOK_NONE: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + if (startTagLevel > 0) + { + if (tagLevel!=startTagLevel) return XML_ERROR_ASYNC_ENTITY; + + return XML_ERROR_NONE; + }; + + return XML_ERROR_NO_ELEMENTS; + + case XML_TOK_INVALID: + *eventPP=next; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_PARTIAL: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + return XML_ERROR_UNCLOSED_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + return XML_ERROR_PARTIAL_CHAR; + + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch=XmlPredefinedEntityName(enc, + s+enc->minBytesPerChar, + next-enc->minBytesPerChar); + + if (ch) + { + if (characterDataHandler) + CharacterDataHandler(&ch, 1); + else if (defaultHandler) + reportDefault(enc, s, next); + + break; + }; + + name=poolStoreString(&dtd.pool, enc, s+enc->minBytesPerChar, + next-enc->minBytesPerChar); + + if (!name) return XML_ERROR_NO_MEMORY; + + entity=(ENTITY *) lookup(&dtd.generalEntities, name, 0); + poolDiscard(&dtd.pool); + + if (!entity) + { + if (dtd.complete || dtd.standalone) + return XML_ERROR_UNDEFINED_ENTITY; + + if (defaultHandler) + reportDefault(enc, s, next); + + break; + }; + + if (entity->open) return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) return XML_ERROR_BINARY_ENTITY_REF; + + if (entity) + { + if (entity->textPtr) + { + enum XML_Error result; + + if (defaultHandler) + { + reportDefault(enc, s, next); + break; + }; + + /* Protect against the possibility that somebody sets + the defaultHandler from inside another handler. */ + + *eventEndPP=*eventPP; + entity->open=1; + + result=doContent(tagLevel, internalEnc, + (char *) entity->textPtr, + (char *) (entity->textPtr+entity->textLen), + 0); + + entity->open = 0; + + if (result) return result; + } + else if (externalEntityRefHandler) + { + const XML_Char *openEntityNames; + + entity->open=1; + openEntityNames=getOpenEntityNames(); + entity->open=0; + + if (!openEntityNames) return XML_ERROR_NO_MEMORY; + if (!ExternalEntityRefHandler(openEntityNames, dtd.base, entity->systemId, entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(enc, s, next); + }; + break; + } + + case XML_TOK_START_TAG_WITH_ATTS: + if (!startElementHandler) + { + enum XML_Error result=storeAtts(enc, 0, s); + + if (result) return result; + }; + + // fall through + + case XML_TOK_START_TAG_NO_ATTS: + { + TAG *tag; + + if (freeTagList) + { + tag=freeTagList; + freeTagList=freeTagList->parent; + } + else + { + tag=new TAG; + if (!tag) return XML_ERROR_NO_MEMORY; + + tag->buf=new char[INIT_TAG_BUF_SIZE]; + if (!tag->buf) return XML_ERROR_NO_MEMORY; + + tag->bufEnd=tag->buf+INIT_TAG_BUF_SIZE; + } + + tag->parent=tagStack; + tagStack=tag; + + tag->rawName=s+enc->minBytesPerChar; + tag->rawNameLength=XmlNameLength(enc, tag->rawName); + + if (nextPtr) + { + if (tag->rawNameLength>tag->bufEnd-tag->buf) + { + int bufSize=ROUND_UP(tag->rawNameLength*4, sizeof(XML_Char)); + + tag->buf=(char *) realloc(tag->buf, bufSize); + if (!tag->buf) return XML_ERROR_NO_MEMORY; + + tag->bufEnd=tag->buf+bufSize; + }; + + memcpy(tag->buf, tag->rawName, tag->rawNameLength); + tag->rawName=tag->buf; + }; + + tagLevel++; + + if (startElementHandler) + { + enum XML_Error result; + XML_Char *toPtr; + + for (;;) + { + const char *rawNameEnd=tag->rawName+tag->rawNameLength; + const char *fromPtr=tag->rawName; + int bufSize; + + if (nextPtr) + toPtr=(XML_Char *) (tag->buf+ROUND_UP(tag->rawNameLength, sizeof(XML_Char))); + else + toPtr=(XML_Char *) tag->buf; + + tag->name=toPtr; + + XmlConvert(enc, &fromPtr, rawNameEnd, + (ICHAR **) &toPtr, (ICHAR *) tag->bufEnd-1); + + if (fromPtr==rawNameEnd) break; + + bufSize=(tag->bufEnd-tag->buf) << 1; + tag->buf=(char *) realloc(tag->buf, bufSize); + + if (!tag->buf) return XML_ERROR_NO_MEMORY; + + tag->bufEnd=tag->buf+bufSize; + + if (nextPtr) tag->rawName=tag->buf; + }; + + *toPtr=XML_T('\0'); + + result=storeAtts(enc, tag->name, s); + if (result) return result; + + StartElementHandler(tag->name, (const XML_Char **) atts); + poolClear(&tempPool); + } + else + { + tag->name=0; + + if (defaultHandler) reportDefault(enc, s, next); + }; + + break; + } + + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + + if (!startElementHandler) + { + enum XML_Error result=storeAtts(enc, 0, s); + + if (result) return result; + }; + + // fall through */ + + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + if (startElementHandler || endElementHandler) + { + const char *rawName=s+enc->minBytesPerChar; + const XML_Char *name=poolStoreString(&tempPool, enc, rawName, + rawName + + XmlNameLength(enc, rawName)); + + if (!name) return XML_ERROR_NO_MEMORY; + + poolFinish(&tempPool); + + if (startElementHandler) + { + enum XML_Error result=storeAtts(enc, name, s); + + if (result) return result; + + StartElementHandler(name, (const XML_Char **) atts); + }; + + if (endElementHandler) + { + if (startElementHandler) *eventPP=*eventEndPP; + + EndElementHandler(name); + }; + + poolClear(&tempPool); + } + else if (defaultHandler) + reportDefault(enc, s, next); + + if (!tagLevel) + return epilogProcessor(this, next, end, nextPtr); + + break; + + case XML_TOK_END_TAG: + if (tagLevel==startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else + { + int len; + const char *rawName; + + TAG *tag=tagStack; + tagStack=tag->parent; + tag->parent=freeTagList; + freeTagList=tag; + + rawName=s+enc->minBytesPerChar*2; + len=XmlNameLength(enc, rawName); + + if (len!=tag->rawNameLength || memcmp(tag->rawName, rawName, len)!=0) + { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + }; + + --tagLevel; + + if (endElementHandler) + { + if (tag->name) + EndElementHandler(tag->name); + else + { + const XML_Char *name=poolStoreString(&tempPool, enc, rawName, + rawName+len); + + if (!name) return XML_ERROR_NO_MEMORY; + + EndElementHandler(name); + poolClear(&tempPool); + }; + } + else if (defaultHandler) + reportDefault(enc, s, next); + + if (!tagLevel) + return epilogProcessor(this, next, end, nextPtr); + }; + + break; + + case XML_TOK_CHAR_REF: + { + int n=XmlCharRefNumber(enc, s); + + if (n<0) return XML_ERROR_BAD_CHAR_REF; + + if (characterDataHandler) + { + XML_Char buf[XML_ENCODE_MAX]; + CharacterDataHandler(buf, XmlEncode(n, (ICHAR *) buf)); + } + else if (defaultHandler) + reportDefault(enc, s, next); + } + + break; + + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) + { + XML_Char c=XML_T('\n'); + CharacterDataHandler(&c, 1); + } + else if (defaultHandler) + reportDefault(enc, s, next); + + break; + + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + + if (characterDataHandler) + CharacterDataHandler(dataBuf, 0); + else if (defaultHandler) + reportDefault(enc, s, next); + + result=doCdataSection(enc, &next, end, nextPtr); + + if (!next) + { + processor=cdataSectionProcessor; + return result; + }; + }; + + break; + + case XML_TOK_TRAILING_RSQB: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + if (characterDataHandler) + { + if (MUST_CONVERT(enc, s)) + { + ICHAR *dataPtr=(ICHAR *) dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *) dataBufEnd); + + CharacterDataHandler(dataBuf, dataPtr - (ICHAR *) dataBuf); + } + else + CharacterDataHandler((XML_Char *) s, (XML_Char *) end-(XML_Char *) s); + } + else if (defaultHandler) + reportDefault(enc, s, end); + + if (!startTagLevel) + { + *eventPP=end; + return XML_ERROR_NO_ELEMENTS; + }; + + if (tagLevel!=startTagLevel) + { + *eventPP=end; + return XML_ERROR_ASYNC_ENTITY; + }; + + return XML_ERROR_NONE; + + case XML_TOK_DATA_CHARS: + if (characterDataHandler) + { + if (MUST_CONVERT(enc, s)) + { + for (;;) + { + ICHAR *dataPtr=(ICHAR *) dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *) dataBufEnd); + + *eventEndPP = s; + CharacterDataHandler(dataBuf, dataPtr-(ICHAR *) dataBuf); + + if (s==next) break; + *eventPP = s; + }; + } + else + CharacterDataHandler((XML_Char *) s, (XML_Char *) next-(XML_Char *) s); + } + else if (defaultHandler) + reportDefault(enc, s, next); + + break; + + case XML_TOK_PI: + if (!reportProcessingInstruction(enc, s, next)) + return XML_ERROR_NO_MEMORY; + + break; + + default: + if (defaultHandler) + reportDefault(enc, s, next); + + break; + }; + + *eventPP=s=next; + }; + + // not reached +}; + +/* If tagName is non-null, build a real list of attributes, +otherwise just check the attributes for well-formedness. */ + +enum XML_Error XML_Parser::storeAtts(const ENCODING *enc, const XML_Char *tagName, const char *s) +{ + ELEMENT_TYPE *elementType=0; + int nDefaultAtts=0; + const XML_Char **appAtts; + int i; + int n; + + if (tagName) + { + elementType=(ELEMENT_TYPE *) lookup(&dtd.elementTypes, tagName, 0); + + if (elementType) + nDefaultAtts=elementType->nDefaultAtts; + } + + n=XmlGetAttributes(enc, s, attsSize, atts); + + if (n+nDefaultAtts>attsSize) + { + int oldAttsSize=attsSize; + + attsSize=n+nDefaultAtts+INIT_ATTS_SIZE; + + atts=(ATTRIBUTE *) realloc((void *) atts, attsSize*sizeof(ATTRIBUTE)); + + if (!atts) return XML_ERROR_NO_MEMORY; + + if (n>oldAttsSize) XmlGetAttributes(enc, s, n, atts); + }; + + appAtts=(const XML_Char **) atts; + + for (i=0; iname)[-1]) + { + if (enc==encoding) eventPtr=atts[i].name; + + return XML_ERROR_DUPLICATE_ATTRIBUTE; + }; + + (attId->name)[-1]=1; + + appAtts[i<<1]=attId->name; + + if (!atts[i].normalized) + { + enum XML_Error result; + int isCdata=1; + + if (attId->maybeTokenized) + { + for (int j=0; jdefaultAtts[j].id) + { + isCdata=elementType->defaultAtts[j].isCdata; + break; + }; + }; + }; + + result=storeAttributeValue(enc, isCdata, atts[i].valuePtr, + atts[i].valueEnd, &tempPool); + + if (result) return result; + + if (tagName) + { + appAtts[(i<<1)+1]=poolStart(&tempPool); + poolFinish(&tempPool); + } + else + poolDiscard(&tempPool); + } + else if (tagName) + { + appAtts[(i<<1)+1]=poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd); + + if (appAtts[(i<<1)+1]==0) return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + }; + }; + + if (tagName) + { + for (int j=0; jdefaultAtts+j; + + if (!(da->id->name)[-1] && da->value) + { + (da->id->name)[-1]=1; + appAtts[i<<1]=da->id->name; + appAtts[(i<<1)+1]=da->value; + i++; + }; + }; + appAtts[i<<1]=0; + }; + + while (i-->0) ((XML_Char *)appAtts[i<<1])[-1]=0; + + return XML_ERROR_NONE; +}; + +/* The idea here is to avoid using stack for each CDATA section when +the whole file is parsed with one call. */ + +enum XML_Error cdataSectionProcessor(void *parser, const char *start, const char *end, const char **endPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + enum XML_Error result=p->doCdataSection(p->encoding, &start, end, endPtr); + + if (start) + { + p->processor=contentProcessor; + return contentProcessor(parser, start, end, endPtr); + }; + + return result; +}; + +/* startPtr gets set to non-null is the section is closed, and to null if +the section is not yet closed. */ + +enum XML_Error XML_Parser::doCdataSection(const ENCODING *enc, const char **startPtr, const char *end, const char **nextPtr) +{ + const char *s=*startPtr; + const char *dummy; + const char **eventPP; + const char **eventEndPP; + + if (enc==encoding) + { + eventPP=&eventPtr; + *eventPP=s; + eventEndPP=&eventEndPtr; + } + else + eventPP=eventEndPP=&dummy; + + *startPtr=0; + + for (;;) + { + const char *next; + + int tok=XmlCdataSectionTok(enc, s, end, &next); + + *eventEndPP = next; + + switch (tok) + { + case XML_TOK_CDATA_SECT_CLOSE: + if (characterDataHandler) + CharacterDataHandler(dataBuf, 0); + else if (defaultHandler) + reportDefault(enc, s, next); + + *startPtr = next; + return XML_ERROR_NONE; + + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) + { + XML_Char c=XML_T('\n'); + CharacterDataHandler(&c, 1); + } + else if (defaultHandler) + reportDefault(enc, s, next); + + break; + + case XML_TOK_DATA_CHARS: + if (characterDataHandler) + { + if (MUST_CONVERT(enc, s)) + { + for (;;) + { + ICHAR *dataPtr=(ICHAR *) dataBuf; + + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *) dataBufEnd); + + *eventEndPP=next; + CharacterDataHandler(dataBuf, dataPtr-(ICHAR *) dataBuf); + + if (s==next) break; + *eventPP=s; + }; + } + else + CharacterDataHandler((XML_Char *) s, (XML_Char *) next-(XML_Char *) s); + } + else if (defaultHandler) + reportDefault(enc, s, next); + + break; + + case XML_TOK_INVALID: + *eventPP=next; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + return XML_ERROR_PARTIAL_CHAR; + + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + } + + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + abort(); + }; + + *eventPP=s=next; + }; + + // not reached +}; + +enum XML_Error XML_Parser::initializeEncoding() +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + + if (!protocolEncodingName) + s=0; + else + { + for (int i=0; protocolEncodingName[i]; i++) + { + if (i==sizeof(encodingBuf)-1 + || protocolEncodingName[i]>=0x80 + || protocolEncodingName[i]<0) + { + encodingBuf[0]='\0'; + break; + }; + + encodingBuf[i]=(char) protocolEncodingName[i]; + }; + + encodingBuf[i]='\0'; + s=encodingBuf; + }; +#else + s=protocolEncodingName; +#endif + + if (XmlInitEncoding(&initEncoding, &encoding, s)) return XML_ERROR_NONE; + + return handleUnknownEncoding(protocolEncodingName); +}; + +enum XML_Error XML_Parser::processXmlDecl(int isGeneralTextEntity, const char *s, const char *next) +{ + const char *encodingName=0; + const ENCODING *newEncoding=0; + const char *version; + int standalone=-1; + + if (!XmlParseXmlDecl(isGeneralTextEntity, encoding, s, next, &eventPtr, + &version, &encodingName, &newEncoding, &standalone)) + return XML_ERROR_SYNTAX; + + if (!isGeneralTextEntity && standalone==1) dtd.standalone=1; + + if (defaultHandler) reportDefault(encoding, s, next); + + if (!protocolEncodingName) + { + if (newEncoding) + { + if (newEncoding->minBytesPerChar!=encoding->minBytesPerChar) + { + eventPtr=encodingName; + return XML_ERROR_INCORRECT_ENCODING; + }; + + encoding=newEncoding; + } + else if (encodingName) + { + enum XML_Error result; + const XML_Char *s=poolStoreString(&tempPool, encoding, encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + + if (!s) return XML_ERROR_NO_MEMORY; + + result=handleUnknownEncoding(s); + + poolDiscard(&tempPool); + + if (result==XML_ERROR_UNKNOWN_ENCODING) eventPtr=encodingName; + return result; + }; + }; + + return XML_ERROR_NONE; +}; + +enum XML_Error XML_Parser::handleUnknownEncoding(const XML_Char *encodingName) +{ + if (unknownEncodingHandler) + { + XML_Encoding info; + + for (int i=0; i<256; i++) info.map[i]=-1; + + info.convert=0; + info.data=0; + info.release=0; + + if (UnknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) + { + ENCODING *enc; + unknownEncodingMem=malloc(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) + { + if (info.release) info.release(info.data); + return XML_ERROR_NO_MEMORY; + }; + + enc=XmlInitUnknownEncoding(unknownEncodingMem, info.map, info.convert, + info.data); + + if (enc) + { + unknownEncodingData=info.data; + unknownEncodingRelease=info.release; + encoding=enc; + return XML_ERROR_NONE; + }; + }; + + if (info.release) info.release(info.data); + }; + + return XML_ERROR_UNKNOWN_ENCODING; +}; + +enum XML_Error prologInitProcessor(void *parser, const char *s, const char *end, const char **nextPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + enum XML_Error result=p->initializeEncoding(); + + if (result!=XML_ERROR_NONE) return result; + + p->processor=prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +enum XML_Error prologProcessor(void *parser, const char *s, const char *end, const char **nextPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + for (;;) + { + const char *next; + + int tok=XmlPrologTok(p->encoding, s, end, &next); + + if (tok<=0) + { + if (nextPtr!=0 && tok!=XML_TOK_INVALID) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + switch (tok) + { + case XML_TOK_INVALID: + p->eventPtr=next; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_NONE: + return XML_ERROR_NO_ELEMENTS; + + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + + case XML_TOK_TRAILING_CR: + p->eventPtr=s+p->encoding->minBytesPerChar; + return XML_ERROR_NO_ELEMENTS; + + default: + abort(); + }; + }; + + switch (XmlTokenRole(&p->prologState, tok, s, next, p->encoding)) + { + case XML_ROLE_XML_DECL: + { + enum XML_Error result=p->processXmlDecl(0, s, next); + + if (result!=XML_ERROR_NONE) return result; + } + break; + + case XML_ROLE_DOCTYPE_SYSTEM_ID: + p->hadExternalDoctype=1; + break; + + case XML_ROLE_DOCTYPE_PUBLIC_ID: + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(p->encoding, s, next, &p->eventPtr)) + return XML_ERROR_SYNTAX; + + if (p->declEntity) + { + XML_Char *tem=poolStoreString(&p->dtd.pool, p->encoding, + s+p->encoding->minBytesPerChar, + next-p->encoding->minBytesPerChar); + + if (!tem) return XML_ERROR_NO_MEMORY; + + p->normalizePublicId(tem); + p->declEntity->publicId=tem; + poolFinish(&p->dtd.pool); + } + + break; + + case XML_ROLE_INSTANCE_START: + p->processor=contentProcessor; + + if (p->hadExternalDoctype) p->dtd.complete=0; + + return contentProcessor(parser, s, end, nextPtr); + + case XML_ROLE_ATTLIST_ELEMENT_NAME: + { + const XML_Char *name=poolStoreString(&p->dtd.pool, p->encoding, s, next); + + if (!name) return XML_ERROR_NO_MEMORY; + + p->declElementType=(ELEMENT_TYPE *) lookup(&p->dtd.elementTypes, name, sizeof(ELEMENT_TYPE)); + + if (!p->declElementType) return XML_ERROR_NO_MEMORY; + + if (p->declElementType->name!=name) + poolDiscard(&p->dtd.pool); + else + poolFinish(&p->dtd.pool); + + break; + }; + + case XML_ROLE_ATTRIBUTE_NAME: + p->declAttributeId=p->getAttributeId(p->encoding, s, next); + + if (!p->declAttributeId) return XML_ERROR_NO_MEMORY; + + p->declAttributeIsCdata=0; + + break; + + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + p->declAttributeIsCdata=1; + break; + + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (p->dtd.complete && + !p->defineAttribute(p->declElementType, p->declAttributeId, p->declAttributeIsCdata, 0)) + return XML_ERROR_NO_MEMORY; + + break; + + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + { + const XML_Char *attVal; + enum XML_Error result=p->storeAttributeValue(p->encoding, p->declAttributeIsCdata, + s+p->encoding->minBytesPerChar, + next-p->encoding->minBytesPerChar, &p->dtd.pool); + + if (result) return result; + + attVal=poolStart(&p->dtd.pool); + poolFinish(&p->dtd.pool); + + if (p->dtd.complete && + !p->defineAttribute(p->declElementType, p->declAttributeId, p->declAttributeIsCdata, attVal)) + return XML_ERROR_NO_MEMORY; + + break; + }; + + case XML_ROLE_ENTITY_VALUE: + { + enum XML_Error result=p->storeEntityValue(s, next); + if (result!=XML_ERROR_NONE) return result; + }; + + break; + + case XML_ROLE_ENTITY_SYSTEM_ID: + if (p->declEntity) + { + p->declEntity->systemId=poolStoreString(&p->dtd.pool, p->encoding, + s+p->encoding->minBytesPerChar, + next-p->encoding->minBytesPerChar); + + if (!p->declEntity->systemId) return XML_ERROR_NO_MEMORY; + + p->declEntity->base=p->dtd.base; + poolFinish(&p->dtd.pool); + }; + + break; + + case XML_ROLE_ENTITY_NOTATION_NAME: + if (p->declEntity) + { + p->declEntity->notation=poolStoreString(&p->dtd.pool, p->encoding, s, next); + + if (!p->declEntity->notation) return XML_ERROR_NO_MEMORY; + + poolFinish(&p->dtd.pool); + + if (p->unparsedEntityDeclHandler) + { + p->eventPtr=p->eventEndPtr=s; + + p->UnparsedEntityDeclHandler(p->declEntity->name, p->declEntity->base, + p->declEntity->systemId, p->declEntity->publicId, + p->declEntity->notation); + }; + }; + + break; + + case XML_ROLE_GENERAL_ENTITY_NAME: + { + const XML_Char *name; + + if (XmlPredefinedEntityName(p->encoding, s, next)) + { + p->declEntity=0; + break; + }; + + name=poolStoreString(&p->dtd.pool, p->encoding, s, next); + + if (!name) return XML_ERROR_NO_MEMORY; + + if (p->dtd.complete) + { + p->declEntity=(ENTITY *) lookup(&p->dtd.generalEntities, name, sizeof(ENTITY)); + + if (!p->declEntity) return XML_ERROR_NO_MEMORY; + + if (p->declEntity->name!=name) + { + poolDiscard(&p->dtd.pool); + p->declEntity=0; + } + else + poolFinish(&p->dtd.pool); + } + else + { + poolDiscard(&p->dtd.pool); + p->declEntity=0; + }; + }; + + break; + + case XML_ROLE_PARAM_ENTITY_NAME: + p->declEntity=0; + break; + + case XML_ROLE_NOTATION_NAME: + p->declNotationPublicId=0; + p->declNotationName=0; + + if (p->notationDeclHandler) + { + p->declNotationName=poolStoreString(&p->tempPool, p->encoding, s, next); + + if (!p->declNotationName) return XML_ERROR_NO_MEMORY; + poolFinish(&p->tempPool); + }; + + break; + + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(p->encoding, s, next, &p->eventPtr)) + return XML_ERROR_SYNTAX; + + if (p->declNotationName) + { + XML_Char *tem=poolStoreString(&p->tempPool, p->encoding, + s+p->encoding->minBytesPerChar, + next-p->encoding->minBytesPerChar); + + if (!tem) return XML_ERROR_NO_MEMORY; + + p->normalizePublicId(tem); + p->declNotationPublicId=tem; + poolFinish(&p->tempPool); + }; + + break; + + case XML_ROLE_NOTATION_SYSTEM_ID: + if (p->declNotationName && p->notationDeclHandler) + { + const XML_Char *systemId=poolStoreString(&p->tempPool, p->encoding, + s+p->encoding->minBytesPerChar, + next-p->encoding->minBytesPerChar); + + if (!systemId) return XML_ERROR_NO_MEMORY; + + p->eventPtr=p->eventEndPtr=s; + + p->NotationDeclHandler(p->declNotationName, p->dtd.base, systemId, + p->declNotationPublicId); + }; + + poolClear(&p->tempPool); + break; + + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (p->declNotationPublicId && p->notationDeclHandler) + { + p->eventPtr=p->eventEndPtr=s; + + p->NotationDeclHandler(p->declNotationName, p->dtd.base, 0, + p->declNotationPublicId); + }; + + poolClear(&p->tempPool); + break; + + case XML_ROLE_ERROR: + p->eventPtr=s; + + switch (tok) + { + case XML_TOK_PARAM_ENTITY_REF: + return XML_ERROR_PARAM_ENTITY_REF; + + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + + default: + return XML_ERROR_SYNTAX; + }; + + case XML_ROLE_GROUP_OPEN: + if (p->prologState.level>=p->groupSize) + { + if (p->groupSize) + p->groupConnector=(char *) realloc(p->groupConnector, p->groupSize*=2); + else + p->groupConnector=(char *) malloc(p->groupSize=32); + + if (!p->groupConnector) return XML_ERROR_NO_MEMORY; + }; + + p->groupConnector[p->prologState.level]=0; + break; + + case XML_ROLE_GROUP_SEQUENCE: + if (p->groupConnector[p->prologState.level]=='|') + { + p->eventPtr=s; + return XML_ERROR_SYNTAX; + }; + + p->groupConnector[p->prologState.level]=','; + break; + + case XML_ROLE_GROUP_CHOICE: + if (p->groupConnector[p->prologState.level]==',') + { + p->eventPtr=s; + return XML_ERROR_SYNTAX; + }; + + p->groupConnector[p->prologState.level]='|'; + break; + + case XML_ROLE_PARAM_ENTITY_REF: + p->dtd.complete=0; + break; + + case XML_ROLE_NONE: + switch (tok) + { + case XML_TOK_PI: + p->eventPtr=s; + p->eventEndPtr=next; + + if (!p->reportProcessingInstruction(p->encoding, s, next)) + return XML_ERROR_NO_MEMORY; + + break; + }; + + break; + }; + + if (p->defaultHandler) + { + switch (tok) + { + case XML_TOK_PI: + case XML_TOK_BOM: + case XML_TOK_XML_DECL: + break; + + default: + p->eventPtr=s; + p->eventEndPtr=next; + p->reportDefault(p->encoding, s, next); + }; + }; + + s=next; + }; + + // not reached +}; + +enum XML_Error epilogProcessor(void *parser, const char *s, const char *end, const char **nextPtr) +{ + XML_Parser *p=(XML_Parser *) parser; + + p->processor=epilogProcessor; + p->eventPtr=s; + + for (;;) + { + const char *next; + + int tok=XmlPrologTok(p->encoding, s, end, &next); + p->eventEndPtr=next; + + switch (tok) { + case XML_TOK_TRAILING_CR: + if (p->defaultHandler) + { + p->eventEndPtr=end; + p->reportDefault(p->encoding, s, end); + }; + + // fall through + + case XML_TOK_NONE: + if (nextPtr) *nextPtr=end; + return XML_ERROR_NONE; + + case XML_TOK_PROLOG_S: + case XML_TOK_COMMENT: + if (p->defaultHandler) p->reportDefault(p->encoding, s, next); + break; + + case XML_TOK_PI: + if (!p->reportProcessingInstruction(p->encoding, s, next)) + return XML_ERROR_NO_MEMORY; + + break; + + case XML_TOK_INVALID: + p->eventPtr=next; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_PARTIAL: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + return XML_ERROR_UNCLOSED_TOKEN; + + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) + { + *nextPtr=s; + return XML_ERROR_NONE; + }; + + return XML_ERROR_PARTIAL_CHAR; + + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + }; + + p->eventPtr=s=next; + }; +}; + +enum XML_Error errorProcessor(void *parser, const char* /*s*/, const char* /*end*/, const char** /*nextPtr*/) +{ + XML_Parser *p=(XML_Parser *) parser; + + return p->errorCode; +}; + +enum XML_Error XML_Parser::storeAttributeValue(const ENCODING *enc, int isCdata, const char *ptr, const char *end, STRING_POOL *pool) +{ + enum XML_Error result=appendAttributeValue(enc, isCdata, ptr, end, pool); + + if (result) return result; + + if (!isCdata && poolLength(pool) && poolLastChar(pool) == XML_T(' ')) + poolChop(pool); + + if (!poolAppendChar(pool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; + + return XML_ERROR_NONE; +}; + +enum XML_Error XML_Parser::appendAttributeValue(const ENCODING *enc, int isCdata, const char *ptr, const char *end, STRING_POOL *pool) +{ + const ENCODING *internalEnc=XmlGetInternalEncoding(); + + for (;;) + { + const char *next; + int tok=XmlAttributeValueTok(enc, ptr, end, &next); + + switch (tok) + { + case XML_TOK_NONE: + return XML_ERROR_NONE; + + case XML_TOK_INVALID: + if (enc==encoding) eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_PARTIAL: + if (enc==encoding) eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n=XmlCharRefNumber(enc, ptr); + + if (n<0) + { + if (enc==encoding) eventPtr=ptr; + return XML_ERROR_BAD_CHAR_REF; + }; + + if (!isCdata && n==0x20 && (poolLength(pool)==0 || + poolLastChar(pool)==XML_T(' '))) break; + + n=XmlEncode(n, (ICHAR *) buf); + + if (!n) + { + if (enc==encoding) eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + }; + + for (i=0; iminBytesPerChar; + // fall through + + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool)==0 || poolLastChar(pool)==XML_T(' '))) + break; + + if (!poolAppendChar(pool, XML_T(' '))) return XML_ERROR_NO_MEMORY; + break; + + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch=XmlPredefinedEntityName(enc, + ptr+enc->minBytesPerChar, + next-enc->minBytesPerChar); + + if (ch) + { + if (!poolAppendChar(pool, ch)) return XML_ERROR_NO_MEMORY; + break; + }; + + name=poolStoreString(&temp2Pool, enc, + ptr+enc->minBytesPerChar, + next-enc->minBytesPerChar); + + if (!name) return XML_ERROR_NO_MEMORY; + + entity=(ENTITY *) lookup(&dtd.generalEntities, name, 0); + + poolDiscard(&temp2Pool); + + if (!entity) + { + if (dtd.complete) + { + if (enc==encoding) eventPtr=ptr; + return XML_ERROR_UNDEFINED_ENTITY; + }; + } + else if (entity->open) + { + if (enc==encoding) eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + else if (entity->notation) + { + if (enc==encoding) eventPtr=ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + else if (!entity->textPtr) + { + if (enc==encoding) eventPtr=ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else + { + enum XML_Error result; + const XML_Char *textEnd=entity->textPtr+entity->textLen; + + entity->open=1; + result=appendAttributeValue(internalEnc, isCdata, (char *) entity->textPtr, (char *) textEnd, pool); + entity->open = 0; + + if (result) return result; + }; + }; + break; + + default: + abort(); + }; + + ptr=next; + }; + + // not reached +}; + +enum XML_Error XML_Parser::storeEntityValue(const char *entityTextPtr, const char *entityTextEnd) +{ +// const ENCODING *internalEnc=XmlGetInternalEncoding(); + STRING_POOL *pool=&(dtd.pool); + + entityTextPtr+=encoding->minBytesPerChar; + entityTextEnd-=encoding->minBytesPerChar; + + for (;;) + { + const char *next; + + int tok=XmlEntityValueTok(encoding, entityTextPtr, entityTextEnd, &next); + + switch (tok) + { + case XML_TOK_PARAM_ENTITY_REF: + eventPtr=entityTextPtr; + return XML_ERROR_SYNTAX; + + case XML_TOK_NONE: + if (declEntity) + { + declEntity->textPtr=pool->start; + declEntity->textLen=pool->ptr-pool->start; + poolFinish(pool); + } + else + poolDiscard(pool); + + return XML_ERROR_NONE; + + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, encoding, entityTextPtr, next)) + return XML_ERROR_NO_MEMORY; + break; + + case XML_TOK_TRAILING_CR: + next=entityTextPtr+encoding->minBytesPerChar; + // fall through + + case XML_TOK_DATA_NEWLINE: + if (pool->end==pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + + *(pool->ptr)++=XML_T('\n'); + break; + + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n=XmlCharRefNumber(encoding, entityTextPtr); + + if (n<0) + { + eventPtr=entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + }; + + n=XmlEncode(n, (ICHAR *) buf); + + if (!n) + { + eventPtr=entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + }; + + for (i=0; iend==pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + + *(pool->ptr)++=buf[i]; + }; + }; + + break; + + case XML_TOK_PARTIAL: + eventPtr=entityTextPtr; + return XML_ERROR_INVALID_TOKEN; + + case XML_TOK_INVALID: + eventPtr=next; + return XML_ERROR_INVALID_TOKEN; + + default: + abort(); + }; + + entityTextPtr=next; + }; + + // not reached +}; + +static void normalizeLines(XML_Char *s) +{ + XML_Char *p; + + for (;; s++) + { + if (*s==XML_T('\0')) return; + if (*s==XML_T('\r')) break; + }; + + p=s; + + do + { + if (*s==XML_T('\r')) + { + *p++=XML_T('\n'); + if (*++s==XML_T('\n')) s++; + } + else + *p++ = *s++; + } while (*s); + + *p=XML_T('\0'); +}; + +int XML_Parser::reportProcessingInstruction(const ENCODING *enc, const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + + if (!processingInstructionHandler) + { + if (defaultHandler) reportDefault(enc, start, end); + return 1; + }; + + start+=enc->minBytesPerChar*2; + tem=start+XmlNameLength(enc, start); + + target=poolStoreString(&tempPool, enc, start, tem); + if (!target) return 0; + + poolFinish(&tempPool); + + data=poolStoreString(&tempPool, enc, XmlSkipS(enc, tem), end-enc->minBytesPerChar*2); + if (!data) return 0; + + normalizeLines(data); + + ProcessingInstructionHandler(target, data); + + poolClear(&tempPool); + + return 1; +}; + +void XML_Parser::reportDefault(const ENCODING *enc, const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) + { + for (;;) + { + ICHAR *dataPtr=(ICHAR *) dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *) dataBufEnd); + + if (s==end) + { + DefaultHandler(dataBuf, dataPtr-(ICHAR *) dataBuf); + break; + }; + + if (enc==encoding) + { + eventEndPtr=s; + DefaultHandler(dataBuf, dataPtr-(ICHAR *) dataBuf); + eventPtr=s; + } + else + DefaultHandler(dataBuf, dataPtr-(ICHAR *) dataBuf); + }; + } + else + DefaultHandler((XML_Char *) s, (XML_Char *) end-(XML_Char *) s); +}; + +int XML_Parser::defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, const XML_Char *value) +{ + DEFAULT_ATTRIBUTE *att; + + if (type->nDefaultAtts==type->allocDefaultAtts) + { + if (type->allocDefaultAtts==0) + { + type->allocDefaultAtts=8; + type->defaultAtts=new DEFAULT_ATTRIBUTE[type->allocDefaultAtts]; + } + else + { + type->allocDefaultAtts*=2; + type->defaultAtts=(DEFAULT_ATTRIBUTE *) realloc(type->defaultAtts, type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE)); + }; + + if (!type->defaultAtts) return 0; + }; + + att=type->defaultAtts+type->nDefaultAtts; + + att->id=attId; + att->value=value; + att->isCdata=isCdata; + + if (!isCdata) attId->maybeTokenized=1; + + type->nDefaultAtts+=1; + + return 1; +}; + +ATTRIBUTE_ID *XML_Parser::getAttributeId(const ENCODING *enc, const char *start, const char *end) +{ + ATTRIBUTE_ID *id; + const XML_Char *name; + + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) return 0; + + name=poolStoreString(&dtd.pool, enc, start, end); + if (!name) return 0; + ++name; + + id=(ATTRIBUTE_ID *) lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) return 0; + + if (id->name!=name) + poolDiscard(&dtd.pool); + else + poolFinish(&dtd.pool); + + return id; +}; + +const XML_Char *XML_Parser::getOpenEntityNames() +{ + HASH_TABLE_ITER iter; + + hashTableIterInit(&iter, &(dtd.generalEntities)); + + for (;;) + { + const XML_Char *s; + ENTITY *e=(ENTITY *) hashTableIterNext(&iter); + + if (!e) break; + if (!e->open) continue; + + if (poolLength(&tempPool)>0 && !poolAppendChar(&tempPool, XML_T(' '))) + return 0; + + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + }; + + if (!poolAppendChar(&tempPool, XML_T('\0'))) return 0; + + return tempPool.start; +}; + +int XML_Parser::setOpenEntityNames(const XML_Char *openEntityNames) +{ + const XML_Char *s=openEntityNames; + + while (*openEntityNames!=XML_T('\0')) + { + if (*s==XML_T(' ') || *s==XML_T('\0')) + { + ENTITY *e; + + if (!poolAppendChar(&tempPool, XML_T('\0'))) return 0; + + e=(ENTITY *)lookup(&dtd.generalEntities, poolStart(&tempPool), 0); + + if (e) e->open = 1; + if (*s == XML_T(' ')) s++; + + openEntityNames=s; + poolDiscard(&tempPool); + } + else + { + if (!poolAppendChar(&tempPool, *s)) return 0; + s++; + }; + }; + + return 1; +}; + + +void XML_Parser::normalizePublicId(XML_Char *publicId) +{ + XML_Char *p=publicId; + XML_Char *s; + + for (s=publicId; *s; s++) + { + switch (*s) + { + case XML_T(' '): + case XML_T('\r'): + case XML_T('\n'): + if (p!=publicId && p[-1]!=XML_T(' ')) *p++=XML_T(' '); + break; + + default: + *p++=*s; + }; + }; + + if (p!=publicId && p[-1]==XML_T(' ')) --p; + *p=XML_T('\0'); +}; + +static int dtdInit(DTD *p) +{ + poolInit(&(p->pool)); + + hashTableInit(&(p->generalEntities)); + hashTableInit(&(p->elementTypes)); + hashTableInit(&(p->attributeIds)); + + p->complete=1; + p->standalone=0; + p->base=0; + + return 1; +}; + +static void dtdDestroy(DTD *p) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + + for (;;) + { + ELEMENT_TYPE *e=(ELEMENT_TYPE *) hashTableIterNext(&iter); + + if (!e) break; + if (e->allocDefaultAtts!=0) free(e->defaultAtts); + }; + + hashTableDestroy(&(p->generalEntities)); + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + + poolDestroy(&(p->pool)); +}; + +/* Do a deep copy of the DTD. Return 0 for out of memory; non-zero otherwise. + The new DTD has already been initialized. */ + +static int dtdCopy(DTD *newDtd, const DTD *oldDtd) +{ + HASH_TABLE_ITER iter; + + if (oldDtd->base) + { + const XML_Char *tem=poolCopyString(&(newDtd->pool), oldDtd->base); + if (!tem) return 0; + newDtd->base=tem; + }; + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + // Copy the attribute id table. + + for (;;) + { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA=(ATTRIBUTE_ID *) hashTableIterNext(&iter); + + if (!oldA) break; + + // Remember to allocate the scratch byte before the name. + + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) return 0; + + name=poolCopyString(&(newDtd->pool), oldA->name); + + if (!name) return 0; + ++name; + + newA=(ATTRIBUTE_ID *) lookup(&(newDtd->attributeIds), name, sizeof(ATTRIBUTE_ID)); + + if (!newA) return 0; + newA->maybeTokenized=oldA->maybeTokenized; + } + + // Copy the element type table. + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) + { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE=(ELEMENT_TYPE *)hashTableIterNext(&iter); + + if (!oldE) break; + name=poolCopyString(&(newDtd->pool), oldE->name); + if (!name) return 0; + + newE=(ELEMENT_TYPE *) lookup(&(newDtd->elementTypes), name, sizeof(ELEMENT_TYPE)); + if (!newE) return 0; + + newE->defaultAtts=new DEFAULT_ATTRIBUTE[oldE->nDefaultAtts]; + if (!newE->defaultAtts) return 0; + + newE->allocDefaultAtts=newE->nDefaultAtts=oldE->nDefaultAtts; + + for (i=0; inDefaultAtts; i++) + { + newE->defaultAtts[i].id=(ATTRIBUTE_ID *) lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata=oldE->defaultAtts[i].isCdata; + + if (oldE->defaultAtts[i].value) + { + newE->defaultAtts[i].value=poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) return 0; + } + else + newE->defaultAtts[i].value=0; + }; + }; + + // Copy the entity table. + + hashTableIterInit(&iter, &(oldDtd->generalEntities)); + + for (;;) + { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE=(ENTITY *)hashTableIterNext(&iter); + + if (!oldE) break; + + name=poolCopyString(&(newDtd->pool), oldE->name); + if (!name) return 0; + + newE=(ENTITY *) lookup(&(newDtd->generalEntities), name, sizeof(ENTITY)); + if (!newE) return 0; + + if (oldE->systemId) + { + const XML_Char *tem=poolCopyString(&(newDtd->pool), oldE->systemId); + if (!tem) return 0; + newE->systemId = tem; + + if (oldE->base) + { + if (oldE->base==oldDtd->base) newE->base=newDtd->base; + + tem=poolCopyString(&(newDtd->pool), oldE->base); + if (!tem) return 0; + + newE->base=tem; + }; + } + else + { + const XML_Char *tem=poolCopyStringN(&(newDtd->pool), oldE->textPtr, oldE->textLen); + if (!tem) return 0; + newE->textPtr=tem; + newE->textLen=oldE->textLen; + }; + + if (oldE->notation) + { + const XML_Char *tem=poolCopyString(&(newDtd->pool), oldE->notation); + if (!tem) return 0; + newE->notation=tem; + }; + }; + + newDtd->complete=oldDtd->complete; + newDtd->standalone=oldDtd->standalone; + + return 1; +}; + +static void poolInit(STRING_POOL *pool) +{ + pool->blocks=0; + pool->freeBlocks=0; + pool->start=0; + pool->ptr=0; + pool->end=0; +}; + +static void poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else + { + BLOCK *p=pool->blocks; + while (p) + { + BLOCK *tem=p->next; + p->next=pool->freeBlocks; + pool->freeBlocks=p; + p=tem; + }; + }; + + pool->blocks=0; + pool->start=0; + pool->ptr=0; + pool->end=0; +}; + +void poolDestroy(STRING_POOL *pool) +{ + BLOCK *p=pool->blocks; + + while (p) + { + BLOCK *tem=p->next; + free(p); + p=tem; + }; + + pool->blocks=0; + p=pool->freeBlocks; + + while (p) + { + BLOCK *tem=p->next; + free(p); + p=tem; + }; + + pool->freeBlocks=0; + pool->ptr=0; + pool->start=0; + pool->end=0; +}; + +static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) return 0; + + for (;;) + { + XmlConvert(enc, &ptr, end, (ICHAR **) &(pool->ptr), (ICHAR *) pool->end); + + if (ptr == end) break; + if (!poolGrow(pool)) return 0; + }; + + return pool->start; +}; + +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do + { + if (!poolAppendChar(pool, *s)) return 0; + } while (*s++); + + s=pool->start; + poolFinish(pool); + + return s; +}; + +static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) return 0; + + for (; n > 0; --n, s++) + { + if (!poolAppendChar(pool, *s)) return 0; + }; + + s=pool->start; + poolFinish(pool); + return s; +}; + +static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) return 0; + + if (pool->ptr==pool->end && !poolGrow(pool)) return 0; + *(pool->ptr)++=0; + + return pool->start; +}; + +static int poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) + { + if (pool->start==0) + { + pool->blocks=pool->freeBlocks; + pool->freeBlocks=pool->freeBlocks->next; + pool->blocks->next=0; + pool->start=pool->blocks->s; + pool->end=pool->start+pool->blocks->size; + pool->ptr=pool->start; + + return 1; + }; + + if (pool->end-pool->startfreeBlocks->size) + { + BLOCK *tem=pool->freeBlocks->next; + + pool->freeBlocks->next=pool->blocks; + pool->blocks=pool->freeBlocks; + pool->freeBlocks=tem; + + memcpy(pool->blocks->s, pool->start, (pool->end-pool->start)*sizeof(XML_Char)); + + pool->ptr=pool->blocks->s+(pool->ptr-pool->start); + pool->start=pool->blocks->s; + pool->end=pool->start+pool->blocks->size; + + return 1; + }; + }; + + if (pool->blocks && pool->start==pool->blocks->s) + { + int blockSize=(pool->end-pool->start)*2; + + pool->blocks=(BLOCK *) realloc(pool->blocks, offsetof(BLOCK, s)+blockSize*sizeof(XML_Char)); + + if (!pool->blocks) return 0; + + pool->blocks->size=blockSize; + pool->ptr=pool->blocks->s+(pool->ptr-pool->start); + pool->start=pool->blocks->s; + pool->end=pool->start+blockSize; + } + else + { + BLOCK *tem; + + int blockSize=pool->end-pool->start; + + if (blockSizesize=blockSize; + tem->next=pool->blocks; + + pool->blocks=tem; + + memcpy(tem->s, pool->start, (pool->ptr-pool->start)*sizeof(XML_Char)); + + pool->ptr=tem->s+(pool->ptr-pool->start); + pool->start=tem->s; + pool->end=tem->s+blockSize; + }; + + return 1; +}; diff --git a/lib/xmltree/xmlparse.h b/lib/xmltree/xmlparse.h new file mode 100644 index 000000000..50b4966ce --- /dev/null +++ b/lib/xmltree/xmlparse.h @@ -0,0 +1,317 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): + + Fabian Giesen - converted this monster to C++ (at least the core) + +ChangeLog: + + $Log: xmlparse.h,v $ + Revision 1.2 2003/03/14 05:13:04 obi + compileable with -W -Werror + + Revision 1.1 2002/01/18 20:22:39 tmbinc + initial checkin + + Revision 1.1.1.1 2001/10/07 13:01:16 tmbinc + Import of ezap2-200110070 + + Revision 1.2 1999/01/24 16:00:05 cvs + First release of the XMLTree API (ryg) + + Revision 1.1 1999/01/11 23:01:58 cvs + Another try to commit this. + +*/ + +#ifndef XmlParse_INCLUDED +#define XmlParse_INCLUDED 1 + +#ifndef XMLPARSEAPI +#define XMLPARSEAPI /* as nothing */ +#endif + +#ifdef XML_UNICODE_WCHAR_T + +/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t +uses Unicode. */ +/* Information is UTF-16 encoded as wchar_ts */ + +#ifndef XML_UNICODE +#define XML_UNICODE +#endif + +#include +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; + +#else /* not XML_UNICODE_WCHAR_T */ + +#ifdef XML_UNICODE + +/* Information is UTF-16 encoded as unsigned shorts */ +typedef unsigned short XML_Char; +typedef char XML_LChar; + +#else /* not XML_UNICODE */ + +/* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; + +#endif /* not XML_UNICODE */ + +#endif /* not XML_UNICODE_WCHAR_T */ + +#include "xmltok.h" +#include "xmlrole.h" +#include "hashtab.h" + +typedef struct { + int map[256]; + void *data; + int (*convert)(void *data, const char *s); + void (*release)(void *data); +} XML_Encoding; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING +}; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +typedef struct tag { + struct tag *parent; + const char *rawName; + int rawNameLength; + const XML_Char *name; + char *buf; + char *bufEnd; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + char open; +} ENTITY; + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether +an attribute has been specified. */ +typedef struct { + XML_Char *name; + char maybeTokenized; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + char isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + const XML_Char *name; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + STRING_POOL pool; + int complete; + int standalone; + const XML_Char *base; +} DTD; + +typedef enum XML_Error Processor(void *parser, + const char *start, + const char *end, + const char **endPtr); + +extern Processor prologProcessor; +extern Processor prologInitProcessor; +extern Processor contentProcessor; +extern Processor cdataSectionProcessor; +extern Processor epilogProcessor; +extern Processor errorProcessor; +extern Processor externalEntityInitProcessor; +extern Processor externalEntityInitProcessor2; +extern Processor externalEntityInitProcessor3; +extern Processor externalEntityContentProcessor; + + +class XML_Parser +{ + friend Processor prologProcessor; + friend Processor prologInitProcessor; + friend Processor contentProcessor; + friend Processor cdataSectionProcessor; + friend Processor epilogProcessor; + friend Processor errorProcessor; + friend Processor externalEntityInitProcessor; + friend Processor externalEntityInitProcessor2; + friend Processor externalEntityInitProcessor3; + friend Processor externalEntityContentProcessor; + + protected: + char *buffer; + /* first character to be parsed */ + const char *bufferPtr; + /* past last character to be parsed */ + char *bufferEnd; + /* allocated end of buffer */ + const char *bufferLim; + long parseEndByteIndex; + const char *parseEndPtr; + XML_Char *dataBuf; + XML_Char *dataBufEnd; + const ENCODING *encoding; + INIT_ENCODING initEncoding; + const XML_Char *protocolEncodingName; + void *unknownEncodingMem; + void *unknownEncodingData; + void *unknownEncodingHandlerData; + void (*unknownEncodingRelease)(void *); + PROLOG_STATE prologState; + Processor *processor; + enum XML_Error errorCode; + const char *eventPtr; + const char *eventEndPtr; + const char *positionPtr; + int tagLevel; + ENTITY *declEntity; + const XML_Char *declNotationName; + const XML_Char *declNotationPublicId; + ELEMENT_TYPE *declElementType; + ATTRIBUTE_ID *declAttributeId; + char declAttributeIsCdata; + DTD dtd; + TAG *tagStack; + TAG *freeTagList; + int attsSize; + ATTRIBUTE *atts; + POSITION position; + STRING_POOL tempPool; + STRING_POOL temp2Pool; + char *groupConnector; + unsigned groupSize; + int hadExternalDoctype; + + int startElementHandler, endElementHandler, characterDataHandler; + int processingInstructionHandler, defaultHandler; + int unparsedEntityDeclHandler, notationDeclHandler; + int externalEntityRefHandler, unknownEncodingHandler; + + virtual enum XML_Error handleUnknownEncoding(const XML_Char *encodingName); + virtual enum XML_Error processXmlDecl(int isGeneralTextEntity, const char *, const char *); + virtual enum XML_Error initializeEncoding(); + virtual enum XML_Error doContent(int startTagLevel, const ENCODING *enc, const char *start, const char *end, const char **endPtr); + virtual enum XML_Error doCdataSection(const ENCODING *, const char **startPtr, const char *end, const char **nextPtr); + virtual enum XML_Error storeAtts(const ENCODING *, const XML_Char *tagName, const char *s); + virtual int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, const XML_Char *dfltValue); + virtual enum XML_Error storeAttributeValue(const ENCODING *, int isCdata, const char *, const char *, STRING_POOL *); + virtual enum XML_Error appendAttributeValue(const ENCODING *, int isCdata, const char *, const char *, STRING_POOL *); + virtual ATTRIBUTE_ID *getAttributeId(const ENCODING *enc, const char *start, const char *end); + virtual enum XML_Error storeEntityValue(const char *start, const char *end); + virtual int reportProcessingInstruction(const ENCODING *enc, const char *start, const char *end); + virtual void reportDefault(const ENCODING *enc, const char *start, const char *end); + + virtual const XML_Char *getOpenEntityNames(); + virtual int setOpenEntityNames(const XML_Char *openEntityNames); + virtual void normalizePublicId(XML_Char *s); + + virtual void StartElementHandler(const XML_Char* /*name*/, const XML_Char** /*atts*/) {}; + virtual void EndElementHandler(const XML_Char* /*name*/) {}; + virtual void CharacterDataHandler(const XML_Char* /*s*/, int /*len*/) {}; + virtual void ProcessingInstructionHandler(const XML_Char* /*target*/, const XML_Char* /*data*/) {}; + virtual void DefaultHandler(const XML_Char* /*s*/, int /*len*/) {}; + virtual void UnparsedEntityDeclHandler(const XML_Char* /*entityName*/, const XML_Char* /*base*/, const XML_Char* /*systemId*/, const XML_Char* /*publicId*/, const XML_Char* /*notationName*/) {}; + virtual void NotationDeclHandler(const XML_Char* /*notationName*/, const XML_Char* /*base*/, const XML_Char* /*systemId*/, const XML_Char* /*publicId*/) {}; + virtual int ExternalEntityRefHandler(const XML_Char* /*openEntityNames*/, const XML_Char* /*base*/, const XML_Char* /*systemId*/, const XML_Char* /*publicId*/) {return 0;}; + virtual int UnknownEncodingHandler(void* /*encodingHandlerData*/, const XML_Char* /*name*/, XML_Encoding* /*info*/) {return 0;}; + + virtual void DefaultCurrent(); + + public: + XML_Parser(const XML_Char *encoding); + virtual ~XML_Parser(); + + virtual int SetBase(const XML_Char *base); + virtual const XML_Char *GetBase(); + + virtual int Parse(const char *s, int len, int isFinal); + virtual void *GetBuffer(int len); + virtual int ParseBuffer(int len, int isFinal); + virtual XML_Parser *ExternalEntityParserCreate(const XML_Char *openEntityNames, const XML_Char *encoding); + + virtual enum XML_Error GetErrorCode(); + + virtual int GetCurrentLineNumber(); + virtual int GetCurrentColumnNumber(); + virtual long GetCurrentByteIndex(); + + virtual const XML_LChar *ErrorString(int code); +}; + +#endif /* not XmlParse_INCLUDED */ diff --git a/lib/xmltree/xmlrole.c b/lib/xmltree/xmlrole.c new file mode 100644 index 000000000..27d5c41ce --- /dev/null +++ b/lib/xmltree/xmlrole.c @@ -0,0 +1,1269 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#include "xmldef.h" +#include "xmlrole.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +typedef int PROLOG_HANDLER(struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, + declClose, + error; + +static +int syntaxError(PROLOG_STATE *); + +static +int prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_COMMENT: + state->handler = prolog1; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "DOCTYPE")) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "DOCTYPE")) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return syntaxError(state); +} + +static +int doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = doctype3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = doctype2; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return syntaxError(state); +} + +static +int doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return syntaxError(state); +} + +static +int internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ENTITY")) { + state->handler = entity0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ATTLIST")) { + state->handler = attlist0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ELEMENT")) { + state->handler = element0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "NOTATION")) { + state->handler = notation0; + return XML_ROLE_NONE; + } + break; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return syntaxError(state); +} + +static +int entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return syntaxError(state); +} + +static +int entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = entity4; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = entity3; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return syntaxError(state); +} + +static +int entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return syntaxError(state); +} + + +static +int entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "NDATA")) { + state->handler = entity6; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = declClose; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return syntaxError(state); +} + +static +int entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = entity9; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = entity8; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return syntaxError(state); +} + +static +int entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return syntaxError(state); +} + +static +int notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = notation3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = notation2; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return syntaxError(state); +} + +static +int attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + default: + return syntaxError(state); + } +} + +static +int attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + { + static const char *types[] = { + "CDATA", + "ID", + "IDREF", + "IDREFS", + "ENTITY", + "ENTITIES", + "NMTOKEN", + "NMTOKENS", + }; + + int i; + + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + + if (XmlNameMatchesAscii(enc, ptr, "NOTATION")) { + state->handler = attlist5; + return XML_ROLE_NONE; + } + + return syntaxError(state); + } + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +static +int attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + default: + return syntaxError(state); + } +} + +static +int attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +static +int attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + + +static +int attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + default: + return syntaxError(state); + } +} + +static +int attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +/* default value */ +static +int attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), "IMPLIED")) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), "REQUIRED")) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), "FIXED")) { + state->handler = attlist9; + return XML_ROLE_NONE; + } + return syntaxError(state); + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + default: + return syntaxError(state); + } +} + +static +int attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + default: + return syntaxError(state); + } +} + +static +int element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + default: + return syntaxError(state); + } +} + +static +int element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "EMPTY")) { + state->handler = declClose; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, "ANY")) { + state->handler = declClose; + return XML_ROLE_CONTENT_ANY; + } + return syntaxError(state); + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + default: + return syntaxError(state); + } +} + +static +int element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)end; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), "PCDATA")) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + return syntaxError(state); + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + default: + return syntaxError(state); + } +} + +static +int element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +static +int element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + default: + return syntaxError(state); + } +} + +static +int element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +static +int element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + default: + return syntaxError(state); + } +} + +static +int element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + default: + return syntaxError(state); + } + +} + +static +int declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + default: + return syntaxError(state); + } +} + +#if 0 + +static +int ignore(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)ptr; + (void)end; + (void)enc; + + switch (tok) { + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return 0; + default: + return XML_ROLE_NONE; + } +} +#endif + +static +int error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + (void)state; + (void)tok; + (void)ptr; + (void)end; + (void)enc; + return XML_ROLE_NONE; +} + +static +int syntaxError(PROLOG_STATE *state) +{ + state->handler = error; + return XML_ROLE_ERROR; +} + +void XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +} diff --git a/lib/xmltree/xmlrole.h b/lib/xmltree/xmlrole.h new file mode 100644 index 000000000..ecbcc26df --- /dev/null +++ b/lib/xmltree/xmlrole.h @@ -0,0 +1,101 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (*handler)(struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; +} PROLOG_STATE; + +void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *); + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/lib/xmltree/xmltimpl.c b/lib/xmltree/xmltimpl.c new file mode 100644 index 000000000..173b2c57a --- /dev/null +++ b/lib/xmltree/xmltimpl.c @@ -0,0 +1,1655 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC; \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC; \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following "')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_COMMENT; + } + break; + default: + ptr += MINBPC; + break; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC)) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC; + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr) +{ + (void)enc; + + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'x': + break; + case 'X': + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC; + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'm': + break; + case 'M': + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC; + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'l': + break; + case 'L': + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following "')) { + *nextTokPtr = ptr + MINBPC; + return tok; + } + break; + default: + ptr += MINBPC; + break; + } + } + return XML_TOK_PARTIAL; + case BT_QUEST: + if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr + MINBPC; + return tok; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + + +static +int PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + (void)enc; + + int i; + /* CDATA[ */ + if (end - ptr < 6 * MINBPC) + return XML_TOK_PARTIAL; + for (i = 0; i < 6; i++, ptr += MINBPC) { + if (!CHAR_MATCHES(enc, ptr, "CDATA["[i])) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + *nextTokPtr = ptr; + return XML_TOK_CDATA_SECT_OPEN; +} + +static +int PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; +#if MINBPC > 1 + { + size_t n = end - ptr; + if (n & (MINBPC - 1)) { + n &= ~(MINBPC - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } +#endif + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ']')) + break; + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + ptr -= MINBPC; + break; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC; + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC; + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC; + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_EMPTY_ELEMENT_WITH_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; + } + break; + } + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "<" */ + +static +int PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_EXCL: + if ((ptr += MINBPC) == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_MINUS: + return PREFIX(scanComment)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_LSQB: + return PREFIX(scanCdataSection)(enc, ptr + MINBPC, end, nextTokPtr); + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_SOL: + return PREFIX(scanEndTag)(enc, ptr + MINBPC, end, nextTokPtr); + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* we have a start-tag */ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_CR: case BT_LF: + { + ptr += MINBPC; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: + goto gt; + case BT_SOL: + goto sol; + case BT_S: case BT_CR: case BT_LF: + ptr += MINBPC; + continue; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr); + } + return XML_TOK_PARTIAL; + } + case BT_GT: + gt: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_START_TAG_NO_ATTS; + case BT_SOL: + sol: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_EMPTY_ELEMENT_NO_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; +#if MINBPC > 1 + { + size_t n = end - ptr; + if (n & (MINBPC - 1)) { + n &= ~(MINBPC - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } +#endif + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_CR: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC; + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ']')) + break; + ptr += MINBPC; + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, '>')) { + ptr -= MINBPC; + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC; + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC, ']')) { + ptr += MINBPC; + break; + } + if (ptr + 2*MINBPC != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC, '>')) { + ptr += MINBPC; + break; + } + *nextTokPtr = ptr + 2*MINBPC; + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC; + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static +int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC; + if (t != open) + break; + if (ptr == end) + return XML_TOK_PARTIAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC; + break; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; +#if MINBPC > 1 + { + size_t n = end - ptr; + if (n & (MINBPC - 1)) { + n &= ~(MINBPC - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } +#endif + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC, end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC, end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC; + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC == end) + return XML_TOK_TRAILING_CR; + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC; + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC, end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ']')) { + if (ptr + MINBPC == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC, '>')) { + *nextTokPtr = ptr + 2*MINBPC; + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC; + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC; + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC, end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC; + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + tok = XML_TOK_NMTOKEN; + ptr += MINBPC; + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC; + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC; + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; + case BT_PLUS: + if (tok != XML_TOK_NAME) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok != XML_TOK_NAME) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok != XML_TOK_NAME) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC; + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC, end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC; + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC; + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC; + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC; + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static +int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC, end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) + return PREFIX(scanPercent)(enc, ptr + MINBPC, end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC; + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC; + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC; + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static +int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC; + end -= MINBPC; + for (; ptr != end; ptr += MINBPC) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, '\t')) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty element tag. +Returns the number of attributes. Pointers to the first attsMax attributes +are stored in atts. */ + +static +int PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open=0; + + for (ptr += MINBPC;; ptr += MINBPC) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC; + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts++].valueEnd = ptr; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC; + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts++].valueEnd = ptr; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ' ' + || BYTE_TO_ASCII(enc, ptr + MINBPC) == ' ' + || BYTE_TYPE(enc, ptr + MINBPC) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static +int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + (void)enc; + /* skip &# */ + ptr += 2*MINBPC; + if (CHAR_MATCHES(enc, ptr, 'x')) { + for (ptr += MINBPC; !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + result <<= 4; + result |= (c - '0'); + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + result <<= 4; + result += 10 + (c - 'A'); + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + result <<= 4; + result += 10 + (c - 'a'); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - '0'); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static +int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end) +{ + (void)enc; + switch (end - ptr) { + case 2 * MINBPC: + if (CHAR_MATCHES(enc, ptr + MINBPC, 't')) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'l': + return '<'; + case 'g': + return '>'; + } + } + break; + case 3 * MINBPC: + if (CHAR_MATCHES(enc, ptr, 'a')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'm')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'p')) + return '&'; + } + } + break; + case 4 * MINBPC: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'q': + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'u')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'o')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 't')) + return '"'; + } + } + break; + case 'a': + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'p')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 'o')) { + ptr += MINBPC; + if (CHAR_MATCHES(enc, ptr, 's')) + return '\''; + } + } + break; + } + } + return 0; +} + +static +int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; +#if MINBPC > 1 + if (*ptr2++ != *ptr1++) + return 0; +#if MINBPC > 2 + if (*ptr2++ != *ptr1++) + return 0; +#if MINBPC > 3 + if (*ptr2++ != *ptr1++) + return 0; +#endif +#endif +#endif + break; + default: +#if MINBPC == 1 + if (*ptr1 == *ptr2) + return 1; +#endif + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static +int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC, ptr2++) { + if (!CHAR_MATCHES(end, ptr1, *ptr2)) + return 0; + } + switch (BYTE_TYPE(enc, ptr1)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } +} + +static +int PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC; + break; + default: + return ptr - start; + } + } +} + +static +const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC; + break; + default: + return ptr; + } + } +} + +static +void PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (unsigned)-1; + pos->lineNumber++; + ptr += MINBPC; + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC; + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC; + pos->columnNumber = (unsigned)-1; + break; + default: + ptr += MINBPC; + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES diff --git a/lib/xmltree/xmltimpl.h b/lib/xmltree/xmltimpl.h new file mode 100644 index 000000000..3b0444ad2 --- /dev/null +++ b/lib/xmltree/xmltimpl.h @@ -0,0 +1,60 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/lib/xmltree/xmltok.c b/lib/xmltree/xmltok.c new file mode 100644 index 000000000..4634a0356 --- /dev/null +++ b/lib/xmltree/xmltok.c @@ -0,0 +1,1164 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#include "xmldef.h" +#include "xmltok.h" +#include "nametab.h" + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), PREFIX(cdataSectionTok) }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits +between the bottom 5 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits +between the bottom 4, 6 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +#define UTF8_INVALID3(p) \ + ((*p) == 0xED \ + ? (((p)[1] & 0x20) != 0) \ + : ((*p) == 0xEF \ + ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \ + : 0)) + +#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0) + +static +int isNever(const ENCODING *enc, const char *p) +{ + (void)enc; + (void)p; + return 0; +} + +static +int utf8_isName2(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static +int utf8_isName3(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static +int utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static +int utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +#define utf8_isInvalid2 isNever + +static +int utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_INVALID3((const unsigned char *)p); +} + +static +int utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + (void)enc; + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; + int (*isName2)(const ENCODING *, const char *); + int (*isName3)(const ENCODING *, const char *); + int (*isName4)(const ENCODING *, const char *); + int (*isNmstrt2)(const ENCODING *, const char *); + int (*isNmstrt3)(const ENCODING *, const char *); + int (*isNmstrt4)(const ENCODING *, const char *); + int (*isInvalid2)(const ENCODING *, const char *); + int (*isInvalid3)(const ENCODING *, const char *); + int (*isInvalid4)(const ENCODING *, const char *); +}; + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int checkCharRefNumber(int); + +#include "xmltimpl.h" + +/* minimum bytes per character */ +#define MINBPC 1 +#define BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) +#define BYTE_TO_ASCII(enc, p) (*p) + +#define IS_NAME_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p)) + +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) + +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) + +#define PREFIX(ident) normal_ ## ident +#include "xmltimpl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static +void utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + (void)enc; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static +void utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f); + from += 2; + break; + case BT_LEAD3: + *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + break; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } + *fromP = from; + *toP = to; +} + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + NORMAL_VTABLE(utf8_) +}; + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasctab.h" +#include "utf8tab.h" + }, + NORMAL_VTABLE(utf8_) +}; + +static +void latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + (void)enc; + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = ((c >> 6) | UTF8_cval2); + *(*toP)++ = ((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static +void latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + (void)enc; + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +static +void ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + (void)enc; + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#undef PREFIX + +static int unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8 \ +static \ +void PREFIX(toUtf8)(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + (void)enc; \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16 \ +static \ +void PREFIX(toUtf16)(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + (void)enc; \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define PREFIX(ident) little2_ ## ident +#define MINBPC 2 +#define BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define IS_NAME_CHAR(enc, p, n) (0) +#define IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#include "xmltimpl.c" + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8 +DEFINE_UTF16_TO_UTF16 + +#undef SET2 +#undef GET_LO +#undef GET_HI +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTE_ORDER == 12 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#if BYTE_ORDER != 21 + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#include "iasctab.h" +#include "latin1tab.h" + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#endif + +#undef PREFIX + +#define PREFIX(ident) big2_ ## ident +#define MINBPC 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#include "xmltimpl.c" + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8 +DEFINE_UTF16_TO_UTF16 + +#undef SET2 +#undef GET_LO +#undef GET_HI +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTE_ORDER == 21 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#if BYTE_ORDER != 12 + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#include "iasctab.h" +#include "latin1tab.h" + }, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#endif + +#undef PREFIX + +static +int streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if ('a' <= c1 && c1 <= 'z') + c1 += 'A' - 'a'; + if ('a' <= c2 && c2 <= 'z') + c2 += 'A' - 'a'; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static +int initScan(const ENCODING *enc, int state, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = ((const INIT_ENCODING *)enc)->encPtr; + if (ptr + 1 == end) { + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0x003C: + *encPtr = &big2_encoding.enc; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFEFF: + *nextTokPtr = ptr + 2; + *encPtr = &big2_encoding.enc; + return XML_TOK_BOM; + case 0x3C00: + *encPtr = &little2_encoding.enc; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + *nextTokPtr = ptr + 2; + *encPtr = &little2_encoding.enc; + return XML_TOK_BOM; + } + } + *encPtr = &utf8_encoding.enc; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + +static +int initScanProlog(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static +int initScanContent(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +static +void initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + (void)enc; + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +const ENCODING *XmlGetUtf8InternalEncoding() +{ + return &internal_utf8_encoding.enc; +} + +const ENCODING *XmlGetUtf16InternalEncoding() +{ +#if BYTE_ORDER == 12 + return &internal_little2_encoding.enc; +#elif BYTE_ORDER == 21 + return &internal_big2_encoding.enc; +#else + const short n = 1; + return *(const char *)&n ? &internal_little2_encoding.enc : &internal_big2_encoding.enc; +#endif +} + +int XmlInitEncoding(INIT_ENCODING *p, const ENCODING **encPtr, const char *name) +{ + if (name) { + if (streqci(name, "ISO-8859-1")) { + *encPtr = &latin1_encoding.enc; + return 1; + } + if (streqci(name, "UTF-8")) { + *encPtr = &utf8_encoding.enc; + return 1; + } + if (streqci(name, "US-ASCII")) { + *encPtr = &ascii_encoding.enc; + return 1; + } + if (!streqci(name, "UTF-16")) + return 0; + } + p->initEnc.scanners[XML_PROLOG_STATE] = initScanProlog; + p->initEnc.scanners[XML_CONTENT_STATE] = initScanContent; + p->initEnc.updatePosition = initUpdatePosition; + p->initEnc.minBytesPerChar = 1; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static +int toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static +int isSpace(int c) +{ + switch (c) { + case ' ': + case '\r': + case '\n': + case '\t': + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space +or there's an S followed by name=val. */ +static +int parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = 0; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = 0; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == '=') + break; + if (isSpace(c)) { + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != '=') { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != '"' && c != '\'') { + *nextTokPtr = ptr; + return 0; + } + open = c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9') + && c != '.' + && c != '-' + && c != '_') { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static +const ENCODING *findEncoding(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + for (i = 0; buf[i]; i++) { + if ('a' <= buf[i] && buf[i] <= 'z') + buf[i] += 'A' - 'a'; + } + if (streqci(buf, "UTF-8")) + return &utf8_encoding.enc; + if (streqci(buf, "ISO-8859-1")) + return &latin1_encoding.enc; + if (streqci(buf, "US-ASCII")) + return &ascii_encoding.enc; + if (streqci(buf, "UTF-16")) { +// static const unsigned short n = 1; + if (enc->minBytesPerChar == 2) + return enc; + return &big2_encoding.enc; + } + return 0; +} + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = 0; + const char *name = 0; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr) || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, "version")) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (XmlNameMatchesAscii(enc, name, "encoding")) { + int c = toAscii(enc, val, end); + if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = findEncoding(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, "standalone") || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, "yes")) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, "no")) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static +int checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = ((c >> 6) | UTF8_cval2); + buf[1] = ((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = ((c >> 12) | UTF8_cval3); + buf[1] = (((c >> 6) & 0x3f) | 0x80); + buf[2] = ((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = ((c >> 18) | UTF8_cval4); + buf[1] = (((c >> 12) & 0x3f) | 0x80); + buf[2] = (((c >> 6) & 0x3f) | 0x80); + buf[3] = ((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (charNum >> 10) + 0xD800; + buf[1] = (charNum & 0x3FF) + 0xDC00; + return 2; + } + return 0; +} + +typedef struct unknown_encoding { + struct normal_encoding normal; + int (*convert)(void *userData, const char *p); + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +} unknown_encoding; + +int XmlSizeOfUnknownEncoding() +{ + return sizeof(struct unknown_encoding); +} + +static +int unknown_isName(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static +int unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static +int unknown_isInvalid(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static +void unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static +void unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) { + unsigned short c + = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short)((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData) +{ + int i; + struct unknown_encoding *e = (unknown_encoding*)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = BT_LEAD2 - (c + 2); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = c == 0 ? 0xFFFF : c; + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} diff --git a/lib/xmltree/xmltok.h b/lib/xmltree/xmltok.h new file mode 100644 index 000000000..6d0b91dff --- /dev/null +++ b/lib/xmltree/xmltok.h @@ -0,0 +1,276 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998 +James Clark. All Rights Reserved. + +Contributor(s): +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMLTOKAPI +#define XMLTOKAPI /* as nothing */ +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of + illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */ + +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +#define XML_N_STATES 3 +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + unsigned long lineNumber; + unsigned long columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +struct encoding { + int (*scanners[XML_N_STATES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*sameName)(const ENCODING *, + const char *, const char *); + int (*nameMatchesAscii)(const ENCODING *, + const char *, const char *); + int (*nameLength)(const ENCODING *, const char *); + const char *(*skipS)(const ENCODING *, const char *); + int (*getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts); + int (*charRefNumber)(const ENCODING *enc, const char *ptr); + int (*predefinedEntityName)(const ENCODING *, const char *, const char *); + void (*updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr); + void (*utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (*utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* +Scan the string starting at ptr until the end of the next complete token, +but do not scan past eptr. Return an integer giving the type of token. + +Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + +Return XML_TOK_PARTIAL when the string does not contain a complete token; +nextTokPtr will not be set. + +Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr +will be set to point to the character which made the token invalid. + +Otherwise the string starts with a valid token; nextTokPtr will be set to point +to the character following the end of that token. + +Each data character counts as a single token, but adjacent data characters +may be returned together. Similarly for characters in the prolog outside +literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +/* This is used for performing a 2nd-level tokenization on +the content of a literal that has already been returned by XmlTok. */ + +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding(); +const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding(); +int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf); +int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf); + +int XMLTOKAPI XmlSizeOfUnknownEncoding(); +ENCODING XMLTOKAPI * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData); + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/lib/xmltree/xmltok/latin1tab.h b/lib/xmltree/xmltok/latin1tab.h new file mode 100644 index 000000000..48609aa8f --- /dev/null +++ b/lib/xmltree/xmltok/latin1tab.h @@ -0,0 +1,62 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/lib/xmltree/xmltok/nametab.h b/lib/xmltree/xmltok/nametab.h new file mode 100644 index 000000000..b05e62c77 --- /dev/null +++ b/lib/xmltree/xmltok/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/lib/xmltree/xmltok/utf8tab.h b/lib/xmltree/xmltok/utf8tab.h new file mode 100644 index 000000000..a38fe624e --- /dev/null +++ b/lib/xmltree/xmltok/utf8tab.h @@ -0,0 +1,63 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/lib/xmltree/xmltok/xmldef.h b/lib/xmltree/xmltok/xmldef.h new file mode 100644 index 000000000..49ce9ed63 --- /dev/null +++ b/lib/xmltree/xmltok/xmldef.h @@ -0,0 +1,63 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +#include + +#ifdef XML_WINLIB + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#include + +#define malloc(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define calloc(x, y) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x)*(y)) +#define free(x) HeapFree(GetProcessHeap(), 0, (x)) +#define realloc(x, y) HeapReAlloc(GetProcessHeap(), 0, x, y) +#define abort() /* as nothing */ + +#else /* not XML_WINLIB */ + +#include + +#endif /* not XML_WINLIB */ + +/* This file can be used for any definitions needed in +particular environments. */ + +#ifdef MOZILLA + +#include "nspr.h" +#define malloc(x) PR_Malloc(x) +#define realloc(x, y) PR_Realloc((x), (y)) +#define calloc(x, y) PR_Calloc((x),(y)) +#define free(x) PR_Free(x) +#define int int32 + +#endif /* MOZILLA */ diff --git a/lib/xmltree/xmltree.cpp b/lib/xmltree/xmltree.cpp new file mode 100644 index 000000000..3d03fad4c --- /dev/null +++ b/lib/xmltree/xmltree.cpp @@ -0,0 +1,446 @@ +/* xmltree.cpp + * + * XMLTree API implementation + * + * $Id: xmltree.cpp,v 1.2 2003/03/14 05:13:04 obi Exp $ + * + * Changelog: + * $Log: xmltree.cpp,v $ + * Revision 1.2 2003/03/14 05:13:04 obi + * compileable with -W -Werror + * + * Revision 1.1 2002/01/18 20:22:39 tmbinc + * initial checkin + * + * Revision 1.1.1.1 2001/10/07 13:01:18 tmbinc + * Import of ezap2-200110070 + * + * Revision 1.4 1999/02/26 13:17:21 cvs + * - thousands of bugs fixed (really) + * - modifications in the XML part + * - added a new object for testing, cpiwButton (usable already, see + * win32/ts.cpp) + * - modified ts.cpp to create a pseudo-GUI again (for testing the + * buttons) + * - ts now exits on a click in the windows + * - ts has no titlebar anymore (will soon by replace by a special + * widget) + * + * (ryg) + * + * Revision 1.3 1999/01/25 18:05:27 cvs + * ARG! tmb, next time you look bevor you say shit, okay? (ryg) + * + * Revision 1.1 1999/01/24 16:01:14 cvs + * First release of the XMLTree API (ryg) + * + */ + +#define stricmp strcasecmp + +#include +#include +#include "xmltree.h" + +/* XMLAttribute class */ + +XMLAttribute::XMLAttribute() +{ + name=value=0; + next=prev=0; +} + +XMLAttribute::XMLAttribute(char *nam, char *val) +{ + name=value=0; + next=prev=0; + + SetName(nam); + SetValue(val); +} + +XMLAttribute::XMLAttribute(XMLAttribute *prv, char *nam, char *val) +{ + name=value=0; + + SetName(nam); + SetValue(val); + SetPrevious(prv); + + next=0; +} + +XMLAttribute::XMLAttribute(XMLAttribute *prv, char *nam, char *val, XMLAttribute *nxt) +{ + name=value=0; + + SetName(nam); + SetValue(val); + + SetPrevious(prv); + SetNext(nxt); +} + +XMLAttribute::~XMLAttribute() +{ + if (name) { free(name); name=0; }; + if (value) { free(value); value=0; }; + + next=prev=0; +} + +void XMLAttribute::SetName(char *nam) +{ + if (name) free(name); + + name=strdup(nam); +} + +void XMLAttribute::SetValue(char *val) +{ + if (value) free(value); + + value=strdup(val); +} + +void XMLAttribute::SetNext(XMLAttribute *nxt) +{ + next=nxt; +} + +void XMLAttribute::SetPrevious(XMLAttribute *prv) +{ + prev=prv; +} + +/* XMLTreeNode class */ + +XMLTreeNode::XMLTreeNode(XMLTreeNode *prnt) +{ + attributes=0; + next=child=0; + type=data=0; + dataSize=0; + + SetMatchingMode(prnt?prnt->mmode:MATCH_CASE); + SetParent(prnt); +} + +XMLTreeNode::XMLTreeNode(XMLTreeNode *prnt, char *typ) +{ + attributes=0; + next=child=0; + type=data=0; + dataSize=0; + + SetType(typ); + SetMatchingMode(prnt?prnt->mmode:MATCH_CASE); + SetParent(prnt); +} + +XMLTreeNode::XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize) +{ + attributes=0; + next=child=0; + type=0; + + SetType(typ); + SetData(dta, dtaSize); + SetMatchingMode(prnt?prnt->mmode:MATCH_CASE); + SetParent(prnt); +} + +XMLTreeNode::XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize, XMLTreeNode *cld) +{ + attributes=0; + next=0; + type=0; + + SetType(typ); + SetData(dta, dtaSize); + SetChild(cld); + SetMatchingMode(prnt?prnt->mmode:MATCH_CASE); + SetParent(prnt); +} + +XMLTreeNode::XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize, XMLTreeNode *cld, XMLTreeNode *nxt) +{ + attributes=0; + type=0; + + SetType(typ); + SetData(dta, dtaSize); + SetChild(cld); + SetNext(nxt); + SetMatchingMode(prnt?prnt->mmode:MATCH_CASE); + SetParent(prnt); +} + +XMLTreeNode::~XMLTreeNode() +{ + DeleteAttributes(); + DeleteChildren(); + + if (type) { free(type); type=0; }; + if (data) { delete[] data; dataSize=0; }; + + next=0; +} + +XMLAttribute *XMLTreeNode::GetAttribute(char *name) const +{ + XMLAttribute *a; + + a=attributes; + + while (a) + { + if (a->GetName()) + { + switch (mmode) + { + case MATCH_CASE: + if (!strcmp(a->GetName(), name)) return a; + + case MATCH_NOCASE: + if (!stricmp(a->GetName(), name)) return a; + } + } + + a=a->GetNext(); + } + + return 0; +} + +char *XMLTreeNode::GetAttributeValue(char *name) const +{ + XMLAttribute *a; + + a=GetAttribute(name); + if (a) return a->GetValue(); + + return 0; +} + +void XMLTreeNode::AddNode(XMLTreeNode *node, addmode mode) +{ + XMLTreeNode *n=0; + + if (!node) return; + node->next=0; + + switch (mode) + { + case ADD_NEIGHBOUR: + n=this; + + case ADD_CHILD: + n=child; + } + + if (n) + { + while (n->next) n=n->next; + + n->next=node; + } + else + child=node; +} + +XMLTreeNode *XMLTreeNode::AddNode(addmode mode) +{ + XMLTreeNode *n=new XMLTreeNode(this); + + AddNode(n, mode); + + return n; +} + +void XMLTreeNode::SetNext(XMLTreeNode *nxt) +{ + next=nxt; +} + +void XMLTreeNode::SetChild(XMLTreeNode *cld) +{ + child=cld; +} + +void XMLTreeNode::SetParent(XMLTreeNode *prnt) +{ + parent=prnt; +} + +void XMLTreeNode::SetAttribute(char *name, char *value) +{ + XMLAttribute *a; + + a=GetAttribute(name); + + if (a) + a->SetValue(value); + else + { + a=attributes; + + if (a) + { + while (a->GetNext()) a=a->GetNext(); + + a->SetNext(new XMLAttribute(a, name, value, 0)); + } + else + { + a=attributes=new XMLAttribute(0, name, value, 0); + } + } +} + +void XMLTreeNode::SetType(char *typ) +{ + if (type) free(type); + + type=strdup(typ); +} + +void XMLTreeNode::SetData(char *dat, unsigned int datSize) +{ + if (data) delete[] data; + + dataSize=datSize; + data=new char[dataSize+1]; + data[dataSize]=0; + + memcpy(data, dat, datSize); +} + +void XMLTreeNode::AppendData(char *dat, unsigned int datSize) +{ + if (!data) + SetData(dat, datSize); + else + { + char *tmp; + + tmp=new char[dataSize+datSize]; + memcpy(tmp, data, dataSize); + memcpy(tmp+dataSize, dat, datSize); + + SetData(tmp, dataSize+datSize); + + delete[] tmp; + } +} + +void XMLTreeNode::SetPDataOff(int pdo) +{ + pdataOff=pdo; +} + +void XMLTreeNode::SetMatchingMode(matchmode mode) +{ + mmode=mode; +}; + +void XMLTreeNode::DeleteAttribute(char *name) +{ + XMLAttribute *a, *next, *prev; + + a=GetAttribute(name); + if (!a) return; + + next=a->GetNext(); + prev=a->GetPrevious(); + + if (prev) prev->SetNext(next); + if (next) next->SetPrevious(prev); + + delete a; +} + +void XMLTreeNode::DeleteAttributes() +{ + XMLAttribute *a, *b; + + a=attributes; + + while (a) + { + b=a->GetNext(); delete a; + a=b; + } + + attributes=0; +} + +void XMLTreeNode::DeleteChildren() +{ + XMLTreeNode *a, *b; + + if (!child) return; + a=child; + + while (a) + { + b=a->next; delete a; + a=b; + } + + child=0; +} + +void XMLTreeParser::StartElementHandler(const XML_Char *name, const XML_Char **atts) +{ + XMLTreeNode *n; + const XML_Char **a=atts; + + n=new XMLTreeNode(current, (char *) name); + + if (a) + { + while (*a) { n->SetAttribute((char *) a[0], (char *) a[1]); a+=2; }; + } + + if (current) + { + n->SetPDataOff(current->GetDataSize()); + current->AddNode(n, XMLTreeNode::ADD_CHILD); + current=n; + } + else + { + root=current=n; + } +} + +void XMLTreeParser::EndElementHandler(const XML_Char* /*name*/) +{ + if (current) + { + if (current->GetParent()) current=current->GetParent(); + } +} + +void XMLTreeParser::CharacterDataHandler(const XML_Char *s, int len) +{ + if (current) + current->AppendData((char *) s, len); +} + +XMLTreeParser::XMLTreeParser(const XML_Char *encoding): XML_Parser(encoding) +{ + root=current=0; + + startElementHandler=endElementHandler=characterDataHandler=1; +} + +XMLTreeParser::~XMLTreeParser() +{ + if (root) + { + delete root; + root=current=0; + } +} diff --git a/lib/xmltree/xmltree.h b/lib/xmltree/xmltree.h new file mode 100644 index 000000000..4e9abdf53 --- /dev/null +++ b/lib/xmltree/xmltree.h @@ -0,0 +1,156 @@ +/* xmltree.h + * + * XMLTree API header + * + * $Id: xmltree.h,v 1.1 2002/01/18 20:22:39 tmbinc Exp $ + * + * Changelog: + * $Log: xmltree.h,v $ + * Revision 1.1 2002/01/18 20:22:39 tmbinc + * initial checkin + * + * Revision 1.1.1.1 2001/10/07 13:01:15 tmbinc + * Import of ezap2-200110070 + * + * Revision 1.4 1999/02/26 13:17:22 cvs + * - thousands of bugs fixed (really) + * - modifications in the XML part + * - added a new object for testing, cpiwButton (usable already, see + * win32/ts.cpp) + * - modified ts.cpp to create a pseudo-GUI again (for testing the + * buttons) + * - ts now exits on a click in the windows + * - ts has no titlebar anymore (will soon by replace by a special + * widget) + * + * (ryg) + * + * Revision 1.3 1999/01/25 18:05:27 cvs + * ARG! tmb, next time you look bevor you say shit, okay? (ryg) + * + * Revision 1.1 1999/01/24 16:01:15 cvs + * First release of the XMLTree API (ryg) + * + */ + +#ifndef __XMLTREE_H +#define __XMLTREE_H + +#include "xmlparse.h" + +class XMLAttribute +{ + private: + char *name, *value; + XMLAttribute *next, *prev; + + public: + XMLAttribute(); + XMLAttribute(char *nam, char *val); + XMLAttribute(XMLAttribute *prv, char *nam, char *val); + XMLAttribute(XMLAttribute *prv, char *nam, char *val, XMLAttribute *nxt); + ~XMLAttribute(); + + char *GetName() { return name; } + char *GetValue() { return value; } + XMLAttribute *GetNext() { return next; } + XMLAttribute *GetPrevious() { return prev; } + + void SetName(char *nam); + void SetValue(char *val); + void SetNext(XMLAttribute *nxt); + void SetPrevious(XMLAttribute *prv); +}; + +class XMLTreeNode +{ + public: + // matching mode: case insensitive or case sensitive? + + enum matchmode + { + MATCH_CASE = 0, + MATCH_NOCASE = 1 + }; + + private: + XMLAttribute *attributes; + XMLTreeNode *next; + XMLTreeNode *child; + XMLTreeNode *parent; + + char *type; + char *data; + unsigned int dataSize; + unsigned int pdataOff; + + matchmode mmode; + + public: + XMLTreeNode(XMLTreeNode *prnt); + XMLTreeNode(XMLTreeNode *prnt, char *typ); + XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize); + XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize, XMLTreeNode *cld); + XMLTreeNode(XMLTreeNode *prnt, char *typ, char *dta, unsigned int dtaSize, XMLTreeNode *cld, XMLTreeNode *nxt); + ~XMLTreeNode(); + + // add modes: add the node as child or neighbour? + + enum addmode + { + ADD_NEIGHBOUR = 0, + ADD_CHILD = 1 + }; + + XMLTreeNode *GetNext() const { return next; } + XMLTreeNode *GetChild() const { return child; } + XMLTreeNode *GetParent() const { return parent; }; + + XMLAttribute *GetAttributes() const { return attributes; } + XMLAttribute *GetAttribute(char *name) const; + char *GetAttributeValue(char *name) const; + + matchmode GetMatchingMode() const { return mmode; } + + char *GetType() const { return type; } + char *GetData() const { return data; } + unsigned int GetDataSize() const { return dataSize; } + unsigned int GetPDataOff() const { return pdataOff; } + + void AddNode(XMLTreeNode *node, addmode mode); + XMLTreeNode *AddNode(addmode mode); + + void SetNext(XMLTreeNode *nxt); + void SetChild(XMLTreeNode *cld); + void SetParent(XMLTreeNode *prnt); + + void SetAttribute(char *name, char *value); + void SetType(char *typ); + void SetData(char *dat, unsigned int datSize); + void AppendData(char *dat, unsigned int datSize); + void SetPDataOff(int pdo); + + void SetMatchingMode(matchmode mode); + + void DeleteAttribute(char *name); + void DeleteAttributes(); + void DeleteChildren(); +}; + +class XMLTreeParser:public XML_Parser +{ + protected: + XMLTreeNode *root, *current; + + virtual void StartElementHandler(const XML_Char *name, const XML_Char **atts); + virtual void EndElementHandler(const XML_Char *name); + virtual void CharacterDataHandler(const XML_Char *s, int len); + + public: + XMLTreeParser(const XML_Char *encoding); + virtual ~XMLTreeParser(); + + XMLTreeNode *RootNode() { return root; }; +}; + +#endif // __XMLTREE_H diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..ad613693e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,71 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +SUBDIRS = zapit gui daemonc driver system sectionsd timerd nhttpd + +INCLUDES = \ + -I$(top_srcdir)/daemons \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libnet \ + -I$(top_srcdir)/lib/xmltree \ + -I$(top_srcdir)/lib/libmd5sum \ + -I$(top_srcdir)/lib/libcoolstream \ + -I$(top_srcdir)/lib/libupnpclient \ + @FREETYPE_CFLAGS@ \ + @LIBCS_CFLAGS@ + +bin_PROGRAMS = neutrino + +neutrino_SOURCES = neutrino_menue.cpp neutrino.cpp + +neutrino_LDADD = \ + daemonc/libneutrino_daemonc.a \ + gui/bedit/libneutrino_gui_bedit.a \ + gui/libtimerlist.a \ + gui/libneutrino_gui.a \ + gui/widget/libneutrino_gui_widget.a \ + gui/widget/libneutrino_gui_widget2.a \ + driver/pictureviewer/libneutrino_pictureviewer.a \ + system/libneutrino_system.a \ + driver/libneutrino_driver.a \ + gui/movieinfo.o \ + driver/audiodec/libneutrino_driver_audiodec.a \ + driver/libneutrino_driver_netfile.a \ + gui/libneutrino_gui2.a \ + sectionsd/libsectionsd.a \ + timerd/libtimerd.a \ + zapit/src/libzapit.a \ + nhttpd/libnhttpd.a \ + nhttpd/tuxboxapi/libnhttpd_tuxboxapi.a \ + nhttpd/yhttpd_mods/libyhttpdmods.a \ + nhttpd/yhttpd_core/libyhttpd.a \ + $(top_builddir)/lib/controldclient/libcontroldclient.la \ + $(top_builddir)/lib/sectionsdclient/libsectionsdclient.a \ + $(top_builddir)/lib/timerdclient/libtimerdclient.a \ + $(top_builddir)/src/zapit/lib/libzapitclient.a \ + $(top_builddir)/lib/libconfigfile/libtuxbox-configfile.a \ + $(top_builddir)/lib/connection/libtuxbox-connection.a \ + $(top_builddir)/lib/libeventserver/libtuxbox-eventserver.a \ + $(top_builddir)/lib/xmltree/libtuxbox-xmltree.a \ + $(top_builddir)/lib/libnet/libtuxbox-net.a \ + $(top_builddir)/lib/libmd5sum/libtuxbox-md5sum.a \ + $(top_builddir)/lib/libtuxtxt/libtuxtxt.a \ + $(top_builddir)/lib/libdvbsub/libdvbsub.a \ + $(top_builddir)/lib/libupnpclient/libtuxbox-upnpclient.a \ + @CURL_LIBS@ \ + @FREETYPE_LIBS@ \ + @PNG_LIBS@ \ + @MAD_LIBS@ \ + @ID3TAG_LIBS@ \ + @LIBCS_LIBS@ \ + @AVFORMAT_LIBS@ \ + @AVUTIL_LIBS@ \ + -lvorbisfile -lvorbis -logg \ + -lungif \ + -ljpeg \ + -lcoolstream \ + -lnxp \ + -lrt -lpthread diff --git a/src/daemonc/Makefile.am b/src/daemonc/Makefile.am new file mode 100644 index 000000000..3bd3d0ec9 --- /dev/null +++ b/src/daemonc/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/daemons \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libnet \ + -I$(top_srcdir)/lib/xmltree \ + @FREETYPE_CFLAGS@ \ + -I$(top_srcdir)/lib + +noinst_LIBRARIES = libneutrino_daemonc.a + +libneutrino_daemonc_a_SOURCES = remotecontrol.cpp diff --git a/src/daemonc/remotecontrol.cpp b/src/daemonc/remotecontrol.cpp new file mode 100644 index 000000000..7ab245b90 --- /dev/null +++ b/src/daemonc/remotecontrol.cpp @@ -0,0 +1,692 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include + +#ifndef TUXTXT_CFG_STANDALONE +extern int tuxtxt_init(); +extern void tuxtxt_start(int tpid); +extern int tuxtxt_stop(); +extern void tuxtxt_close(); +extern void dvbsub_pause(bool pause); +#endif +//FIXME: auto-timeshift +extern bool autoshift; +extern uint32_t shift_timer; +extern uint32_t scrambled_timer; +extern t_channel_id live_channel_id; //zapit + +bool sectionsd_getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags); +bool sectionsd_getLinkageDescriptorsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::LinkageDescriptorList& descriptors); +bool sectionsd_getNVODTimesServiceKey(const t_channel_id uniqueServiceKey, CSectionsdClient::NVODTimesList& nvod_list); +void sectionsd_setPrivatePid(unsigned short pid); + +CSubService::CSubService(const t_original_network_id anoriginal_network_id, const t_service_id aservice_id, const t_transport_stream_id atransport_stream_id, const std::string &asubservice_name) +{ + service.original_network_id = anoriginal_network_id; + service.service_id = aservice_id; + service.transport_stream_id = atransport_stream_id; + startzeit = 0; + dauer = 0; + subservice_name = asubservice_name; +} + +CSubService::CSubService(const t_original_network_id anoriginal_network_id, const t_service_id aservice_id, const t_transport_stream_id atransport_stream_id, const time_t astartzeit, const unsigned adauer) +{ + service.original_network_id = anoriginal_network_id; + service.service_id = aservice_id; + service.transport_stream_id = atransport_stream_id; + startzeit = astartzeit; + dauer = adauer; + subservice_name = ""; +} + +t_channel_id CSubService::getChannelID(void) const +{ + return CREATE_CHANNEL_ID_FROM_SERVICE_ORIGINALNETWORK_TRANSPORTSTREAM_ID(service.service_id, service.original_network_id, service.transport_stream_id); +} + + +CRemoteControl::CRemoteControl() +{ + current_channel_id = live_channel_id; + current_sub_channel_id = 0; + current_channel_name = ""; + + zap_completion_timeout = 0; + + current_EPGid = 0; + next_EPGid = 0; + memset(¤t_PIDs.PIDs, 0, sizeof(current_PIDs.PIDs) ); + has_ac3 = false; + selected_subchannel = -1; + needs_nvods = false; + director_mode = 0; + current_programm_timer = 0; + is_video_started = true; +} + +#include +#include +extern tallchans allchans; +extern CBouquetManager *g_bouquetManager; + +int CRemoteControl::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data) +{ +//printf("[neutrino] MSG %x\n", msg); + if ( zap_completion_timeout != 0 ) { + if ((msg == NeutrinoMessages::EVT_ZAP_COMPLETE) || (msg == NeutrinoMessages::EVT_ZAP_FAILED ) || + (msg == NeutrinoMessages::EVT_ZAP_ISNVOD )) { +//printf("[neutrino] timeout EVT_ZAP current %llx data %llx\n", current_channel_id, *(t_channel_id *)data); + if ((*(t_channel_id *)data) != current_channel_id) { + g_InfoViewer->chanready = 0; + g_Zapit->zapTo_serviceID_NOWAIT(current_channel_id ); + g_Sectionsd->setServiceChanged(current_channel_id &0xFFFFFFFFFFFFULL, false); + + zap_completion_timeout = getcurrenttime() + 2 * (long long) 1000000; + + return messages_return::handled; + } + else { + zap_completion_timeout = 0; + g_InfoViewer->chanready = 1; + } + if ((!is_video_started) && (g_settings.parentallock_prompt != PARENTALLOCK_PROMPT_NEVER)) + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, 0x100, false ); + } + } else { + if ((msg == NeutrinoMessages::EVT_ZAP_COMPLETE) || (msg == NeutrinoMessages::EVT_ZAP_FAILED ) || + (msg == NeutrinoMessages::EVT_ZAP_ISNVOD )) + { +//printf("[neutrino] EVT_ZAP current %llx data %llx\n", current_channel_id, *(t_channel_id *)data); + g_InfoViewer->chanready = 1; + // warte auf keine Meldung vom ZAPIT -> jemand anderer hat das zappen ausgelöst... + if ((*(t_channel_id *)data) != current_channel_id) { + t_channel_id new_id = *(t_channel_id *)data; + tallchans_iterator cit = allchans.find(new_id); + if ( cit != allchans.end() ) + current_channel_name = cit->second.getName(); + + CVFD::getInstance()->showServicename(current_channel_name); // UTF-8 + current_channel_id = new_id; + is_video_started= true; + + current_EPGid = 0; + next_EPGid = 0; + + memset(¤t_PIDs.PIDs, 0, sizeof(current_PIDs.PIDs) ); + + current_PIDs.APIDs.clear(); + has_ac3 = false; + + subChannels.clear(); + selected_subchannel = -1; + director_mode = 0; + needs_nvods = (msg == NeutrinoMessages:: EVT_ZAP_ISNVOD); + + g_Sectionsd->setServiceChanged( current_channel_id&0xFFFFFFFFFFFFULL, true ); + CNeutrinoApp::getInstance()->channelList->adjustToChannelID(current_channel_id); + if ( g_InfoViewer->is_visible ) + g_RCInput->postMsg( NeutrinoMessages::SHOW_INFOBAR , 0 ); + } + if ((!is_video_started) && (g_settings.parentallock_prompt != PARENTALLOCK_PROMPT_NEVER)) + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, 0x100, false ); + } + else + if ((msg == NeutrinoMessages::EVT_ZAP_SUB_COMPLETE) || (msg == NeutrinoMessages:: EVT_ZAP_SUB_FAILED )) { +//printf("[neutrino] EVT_ZAP_SUB current %llx data %llx\n", current_sub_channel_id, *(t_channel_id *)data); + if ((*(t_channel_id *)data) != current_sub_channel_id) + { + current_sub_channel_id = *(t_channel_id *)data; + + for(unsigned int i = 0; i < subChannels.size(); i++) + if (subChannels[i].getChannelID() == (*(t_channel_id *)data)) + { + selected_subchannel = i; + break; + } + } + } + } + + if ( msg == NeutrinoMessages::EVT_CURRENTEPG ) { + CSectionsdClient::CurrentNextInfo* info_CN = (CSectionsdClient::CurrentNextInfo*) data; + +//printf("[neutrino] got EVT_CURRENTEPG, uniqueKey %llx chid %llx flags %x\n", info_CN->current_uniqueKey, current_channel_id, info_CN->flags); +//printf("[neutrino] comparing: uniqueKey %llx chid %llx\n", info_CN->current_uniqueKey >> 16, current_channel_id & 0xFFFFFFFFFFFFULL); + if ( ( info_CN->current_uniqueKey >> 16) == (current_channel_id&0xFFFFFFFFFFFFULL)) + { +//printf("[neutrino] channel match\n"); + //CURRENT-EPG für den aktuellen Kanal bekommen!; + + if ( info_CN->current_uniqueKey != current_EPGid ) + { +//printf("[neutrino] info_CN->current_uniqueKey != current_EPGid\n"); + if ( current_EPGid != 0 ) + { + // ist nur ein neues Programm, kein neuer Kanal + + // PIDs neu holen + g_Zapit->getPIDS( current_PIDs ); + + // APID Bearbeitung neu anstossen + has_unresolved_ctags = true; + } + + current_EPGid= info_CN->current_uniqueKey; + + if ( has_unresolved_ctags ) + processAPIDnames(); + + if ( info_CN->flags & CSectionsdClient::epgflags::current_has_linkagedescriptors ) { +//printf("[neutrino] info_CN->flags have current_has_linkaged\n"); + subChannels.clear(); + getSubChannels(); + } + + if ( needs_nvods ) + getNVODs(); + + if ( current_programm_timer != 0 ) + g_RCInput->killTimer( current_programm_timer ); + + time_t end_program= info_CN->current_zeit.startzeit+ info_CN->current_zeit.dauer; + current_programm_timer = g_RCInput->addTimer( &end_program ); + + // is_video_started is only false if channel is locked + if (((!is_video_started) && (info_CN->current_fsk == 0)) || ((!is_video_started) && (g_settings.parentallock_prompt == PARENTALLOCK_PROMPT_CHANGETOLOCKED))) + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, 0x100, false ); + else + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, info_CN->current_fsk, false ); + } + } + return messages_return::handled; + } + else if ( msg == NeutrinoMessages::EVT_NEXTEPG ) + { + CSectionsdClient::CurrentNextInfo* info_CN = (CSectionsdClient::CurrentNextInfo*) data; + + if ( ( info_CN->next_uniqueKey >> 16) == (current_channel_id&0xFFFFFFFFFFFFULL) ) + { + // next-EPG für den aktuellen Kanal bekommen, current ist leider net da?!; + if ( info_CN->next_uniqueKey != next_EPGid ) + { + next_EPGid= info_CN->next_uniqueKey; + + // timer setzen + + if ( current_programm_timer != 0 ) + g_RCInput->killTimer( current_programm_timer ); + + time_t end_program= info_CN->next_zeit.startzeit; + current_programm_timer = g_RCInput->addTimer( &end_program ); + } + } + if ( !is_video_started ) + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, 0x100, false ); + + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_NOEPG_YET) + { + if ((*(t_channel_id *)data) == (current_channel_id&0xFFFFFFFFFFFFULL)) + { + if ( !is_video_started ) + g_RCInput->postMsg( NeutrinoMessages::EVT_PROGRAMLOCKSTATUS, 0x100, false ); + } + return messages_return::handled; + } + else if ((msg == NeutrinoMessages::EVT_ZAP_COMPLETE)|| + (msg == NeutrinoMessages::EVT_ZAP_SUB_COMPLETE)) { + + if ((*(t_channel_id *)data) == ((msg == NeutrinoMessages::EVT_ZAP_COMPLETE) ? current_channel_id : current_sub_channel_id)) + { + CVFD::getInstance()->showServicename(current_channel_name); // UTF-8 + g_Zapit->getPIDS( current_PIDs ); + //g_Sectionsd->setPrivatePid( current_PIDs.PIDs.privatepid ); + sectionsd_setPrivatePid( current_PIDs.PIDs.privatepid ); + //tuxtxt +#if 1 + tuxtxt_stop(); + if(g_settings.cacheTXT) { + printf("TuxTXT pid: %X\n", current_PIDs.PIDs.vtxtpid); + if(current_PIDs.PIDs.vtxtpid != 0) + tuxtxt_start(current_PIDs.PIDs.vtxtpid); + } +#endif + t_channel_id * p = new t_channel_id; + *p = current_channel_id; + g_RCInput->postMsg(NeutrinoMessages::EVT_ZAP_GOTPIDS, (const neutrino_msg_data_t)p, false); // data is pointer to allocated memory + + processAPIDnames(); + } + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_ZAP_ISNVOD) + { + if ((*(t_channel_id *)data) == current_channel_id) + { + needs_nvods = true; + CVFD::getInstance()->showServicename(std::string("[") + current_channel_name + ']'); // UTF-8 + if ( current_EPGid != 0) + { + getNVODs(); + if (subChannels.empty()) + g_Sectionsd->setServiceChanged( current_channel_id&0xFFFFFFFFFFFFULL, true ); + } + else + // EVENT anfordern! + g_Sectionsd->setServiceChanged( current_channel_id&0xFFFFFFFFFFFFULL, true ); + + } + return messages_return::handled; + } + else if ( ( msg == NeutrinoMessages::EVT_TIMER ) && ( data == current_programm_timer ) ) + { + //printf("new program !\n"); + + t_channel_id * p = new t_channel_id; + *p = current_channel_id; + g_RCInput->postMsg(NeutrinoMessages::EVT_NEXTPROGRAM, (const neutrino_msg_data_t)p, false); // data is pointer to allocated memory + + return messages_return::handled; + } + //else if (msg == NeutrinoMessages::EVT_ZAP_FAILED || msg == NeutrinoMessages::EVT_ZAP_SUB_FAILED) + //return messages_return::handled; + else + return messages_return::unhandled; +} + +void CRemoteControl::getSubChannels() +{ +//printf("[neutrino] getSubChannels, current_EPGid %llx\n", current_EPGid); + if ( subChannels.size() == 0 ) + { + CSectionsdClient::LinkageDescriptorList linkedServices; + //if ( g_Sectionsd->getLinkageDescriptorsUniqueKey( current_EPGid, linkedServices ) ) + if ( sectionsd_getLinkageDescriptorsUniqueKey( current_EPGid, linkedServices ) ) + { + if ( linkedServices.size()> 1 ) + { + are_subchannels = true; +//printf("CRemoteControl::getSubChannels linkedServices.size %d\n", linkedServices.size()); + for (unsigned int i=0; i< linkedServices.size(); i++) + { +//printf("CRemoteControl::getSubChannels %s\n", linkedServices[i].name.c_str()); + subChannels.push_back(CSubService( + linkedServices[i].originalNetworkId, + linkedServices[i].serviceId, + linkedServices[i].transportStreamId, + linkedServices[i].name)); + if (subChannels[i].getChannelID() == (current_channel_id&0xFFFFFFFFFFFFULL)) + selected_subchannel = i; + } + copySubChannelsToZapit(); + + t_channel_id * p = new t_channel_id; + *p = current_channel_id; + g_RCInput->postMsg(NeutrinoMessages::EVT_ZAP_GOT_SUBSERVICES, (const neutrino_msg_data_t)p, false); // data is pointer to allocated memory + } + } + } +} + +void CRemoteControl::getNVODs() +{ +//printf("[neutrino] getNVODs, current_EPGid %llx\n", current_EPGid); + if ( subChannels.size() == 0 ) + { + CSectionsdClient::NVODTimesList NVODs; + //if ( g_Sectionsd->getNVODTimesServiceKey( current_channel_id & 0xFFFFFFFFFFFFULL, NVODs ) ) + if ( sectionsd_getNVODTimesServiceKey( current_channel_id & 0xFFFFFFFFFFFFULL, NVODs ) ) + { + are_subchannels = false; +//printf("CRemoteControl::getNVODs NVODs.size %d\n", NVODs.size()); + for (unsigned int i=0; i< NVODs.size(); i++) + { + if ( NVODs[i].zeit.dauer> 0 ) + { + CSubService newService( + NVODs[i].original_network_id, + NVODs[i].service_id, + NVODs[i].transport_stream_id, + NVODs[i].zeit.startzeit, + NVODs[i].zeit.dauer); + + CSubServiceListSorted::iterator e= subChannels.begin(); + for(; e!=subChannels.end(); ++e) + { + if ( e->startzeit > newService.startzeit ) + break; + } + subChannels.insert( e, newService ); + } + + } + + copySubChannelsToZapit(); + + t_channel_id * p = new t_channel_id; + *p = current_channel_id; + g_RCInput->postMsg(NeutrinoMessages::EVT_ZAP_GOT_SUBSERVICES, (const neutrino_msg_data_t)p, false); // data is pointer to allocated memory + + if ( selected_subchannel == -1 ) + { + // beim ersten Holen letzten NVOD-Kanal setzen! + setSubChannel( subChannels.size()- 1 ); + } + else + { + // sollte nur passieren, wenn die aktuelle Sendung vorbei ist?! + selected_subchannel = -1; + } + } + } +} + +void CRemoteControl::processAPIDnames() +{ + has_unresolved_ctags= false; + has_ac3 = false; + + for(unsigned int count=0; count< current_PIDs.APIDs.size(); count++) + { + if ( current_PIDs.APIDs[count].component_tag != 0xFF ) + { + has_unresolved_ctags= true; + } + if ( strlen( current_PIDs.APIDs[count].desc ) == 3 ) + { + // unaufgeloeste Sprache... + strcpy( current_PIDs.APIDs[count].desc, getISO639Description( current_PIDs.APIDs[count].desc ) ); + } + + if ( current_PIDs.APIDs[count].is_ac3 ) + { + strncat(current_PIDs.APIDs[count].desc, " (AC3)", 25); + has_ac3 = true; + } +//printf("Neutrino: have apid name= %s pid= %X english= %d\n", current_PIDs.APIDs[count].desc, current_PIDs.APIDs[count].pid, g_settings.audio_english); + } + + if ( has_unresolved_ctags ) + { + if ( current_EPGid != 0 ) + { + CSectionsdClient::ComponentTagList tags; + //if ( g_Sectionsd->getComponentTagsUniqueKey( current_EPGid, tags ) ) + if ( sectionsd_getComponentTagsUniqueKey( current_EPGid, tags ) ) + { + has_unresolved_ctags = false; + has_ac3 = false; + + for (unsigned int i=0; i< tags.size(); i++) + { + for (unsigned int j=0; j< current_PIDs.APIDs.size(); j++) + { + if ( current_PIDs.APIDs[j].component_tag == tags[i].componentTag ) + { + // workaround for buggy ZDF ctags / or buggy sectionsd/drivers , who knows... + if(!tags[i].component.empty()) + { + strncpy(current_PIDs.APIDs[j].desc, tags[i].component.c_str(), 25); + if (current_PIDs.APIDs[j].is_ac3) + strncat(current_PIDs.APIDs[j].desc, " (AC3)", 25); + } + current_PIDs.APIDs[j].component_tag = -1; + break; + } + } + } + + CZapitClient::APIDList::iterator e = current_PIDs.APIDs.begin(); + while ( e != current_PIDs.APIDs.end() ) + { + if ( e->is_ac3 ) + { + has_ac3 = true; + } + e++; + } + + if ( ( g_settings.audio_english == 0) && (g_settings.audio_DolbyDigital == 1)) + { + for (unsigned int j=0; j< current_PIDs.APIDs.size(); j++) + if ( current_PIDs.APIDs[j].is_ac3 ) + { + setAPID( j ); + break; + } + } + if ( current_PIDs.PIDs.selected_apid >= current_PIDs.APIDs.size() ) + { + setAPID( 0 ); + } + } + } + } + if ( (g_settings.audio_english == 1) && (current_PIDs.APIDs.size() > 1) ) + { + for (unsigned int j=0; j< current_PIDs.APIDs.size(); j++) + { + if ( strstr(current_PIDs.APIDs[j].desc, "eng") || strstr(current_PIDs.APIDs[j].desc, "Tonoption 2") || strstr(current_PIDs.APIDs[j].desc, "Eng") || strstr(current_PIDs.APIDs[j].desc, "ENG")) + { + printf("Neutrino: set apid name= %s pid= %X\n", current_PIDs.APIDs[j].desc, current_PIDs.APIDs[j].pid); + setAPID( j ); + break; + } + } + } + + t_channel_id * p = new t_channel_id; + *p = current_channel_id; + g_RCInput->postMsg(NeutrinoMessages::EVT_ZAP_GOTAPIDS, (const neutrino_msg_data_t)p, false); // data is pointer to allocated memory +} + + +void CRemoteControl::copySubChannelsToZapit(void) +{ + CZapitClient::subServiceList zapitList; + + for (CSubServiceListSorted::const_iterator e = subChannels.begin(); e != subChannels.end(); e++) + zapitList.push_back(e->getAsZapitSubService()); + + g_Zapit->setSubServices(zapitList); +} + + +void CRemoteControl::setAPID( uint32_t APID ) +{ + if ((current_PIDs.PIDs.selected_apid == APID ) || + (APID >= current_PIDs.APIDs.size())) + return; + + current_PIDs.PIDs.selected_apid = APID; + g_Zapit->setAudioChannel( APID ); +} + +static const std::string empty_string; + +const std::string & CRemoteControl::setSubChannel(const int numSub, const bool force_zap) +{ + if ((numSub < 0) || (numSub >= (int)subChannels.size())) + return empty_string; + + if ((selected_subchannel == numSub ) && (!force_zap)) + return empty_string; + + selected_subchannel = numSub; + current_sub_channel_id = subChannels[numSub].getChannelID(); + g_InfoViewer->chanready = 0; + if(scrambled_timer) { + g_RCInput->killTimer(scrambled_timer); + scrambled_timer = 0; + } + + g_Zapit->zapTo_subServiceID_NOWAIT( current_sub_channel_id ); + // Houdini: to restart reading the private EPG when switching to a new option + g_Sectionsd->setServiceChanged( current_sub_channel_id , true ); + + return subChannels[numSub].subservice_name; +} + +const std::string & CRemoteControl::subChannelUp(void) +{ + //return setSubChannel((subChannels.size() == 0) ? -1 : (int)((selected_subchannel + 1) % subChannels.size())); + // if there are any NVOD/subchannels switch these else switch audio channel (if any) + if (subChannels.size() > 0 || !g_settings.audiochannel_up_down_enable) + { + return setSubChannel((subChannels.size() == 0) ? -1 : (int)((selected_subchannel + 1) % subChannels.size())); + } + else + { + if (current_PIDs.APIDs.size() > 0) + { + setAPID((current_PIDs.PIDs.selected_apid + 1) % current_PIDs.APIDs.size()); + } + return (empty_string); + } +} + +const std::string & CRemoteControl::subChannelDown(void) +{ + //return setSubChannel((selected_subchannel <= 0) ? (subChannels.size() - 1) : (selected_subchannel - 1)); + // if there are any NVOD/subchannels switch these else switch audio channel (if any) + if (subChannels.size() > 0 || !g_settings.audiochannel_up_down_enable) + { + return setSubChannel((selected_subchannel <= 0) ? (subChannels.size() - 1) : (selected_subchannel - 1)); + } + else + { + if (current_PIDs.APIDs.size() > 0) + { + if (current_PIDs.PIDs.selected_apid <= 0) + setAPID(current_PIDs.APIDs.size() - 1); + else + setAPID((current_PIDs.PIDs.selected_apid - 1)); + } + return (empty_string); + } +} + +void stopAutoRecord(); +extern int abort_zapit; +void CRemoteControl::zapTo_ChannelID(const t_channel_id channel_id, const std::string & channame, const bool start_video) // UTF-8 +{ + current_channel_id = channel_id; + current_channel_name = channame; +//printf("zapTo_ChannelID: start_video: %d\n", start_video); + if (start_video) + startvideo(); + else + stopvideo(); + + current_sub_channel_id = 0; + current_EPGid = 0; + next_EPGid = 0; + + memset(¤t_PIDs.PIDs, 0, sizeof(current_PIDs.PIDs) ); + + current_PIDs.APIDs.clear(); + has_ac3 = false; + + subChannels.clear(); + selected_subchannel = -1; + needs_nvods = false; + director_mode = 0; + + unsigned long long now = getcurrenttime(); + if ( zap_completion_timeout < now ) + { + g_InfoViewer->chanready = 0; + if(autoshift) { + stopAutoRecord(); + CNeutrinoApp::getInstance ()->recordingstatus = 0; + } + if(scrambled_timer) { + g_RCInput->killTimer(scrambled_timer); + scrambled_timer = 0; + } + //dvbsub_pause(true); + abort_zapit = 1; + g_Zapit->zapTo_serviceID_NOWAIT(channel_id); + g_Sectionsd->setServiceChanged( current_channel_id&0xFFFFFFFFFFFFULL, false ); + abort_zapit = 0; + + zap_completion_timeout = now + 2 * (long long) 1000000; + if ( current_programm_timer != 0 ) + { + g_RCInput->killTimer( current_programm_timer ); + current_programm_timer = 0; + } + } +} + +void CRemoteControl::startvideo() +{ + if ( !is_video_started ) + { + is_video_started= true; + //g_Zapit->startPlayBack(); + g_Zapit->unlockPlayBack(); + } +} + +void CRemoteControl::stopvideo() +{ + if ( is_video_started ) + { + is_video_started= false; + //g_Zapit->stopPlayBack(); + g_Zapit->lockPlayBack(); + } +} + +void CRemoteControl::radioMode() +{ + g_Zapit->setMode( CZapitClient::MODE_RADIO ); +} + +void CRemoteControl::tvMode() +{ + g_Zapit->setMode( CZapitClient::MODE_TV ); +} diff --git a/src/daemonc/remotecontrol.h b/src/daemonc/remotecontrol.h new file mode 100644 index 000000000..192985fa8 --- /dev/null +++ b/src/daemonc/remotecontrol.h @@ -0,0 +1,121 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __remotecontrol__ +#define __remotecontrol__ + +#include /* neutrino_msg_t, neutrino_msg_data_t */ + +#include + +#include +#include + +struct st_rmsg +{ + unsigned char version; + unsigned char cmd; + unsigned char param; + unsigned short param2; + char param3[30]; +}; + +class CSubService +{ + private: + struct CZapitClient::commandAddSubServices service; + + public: + time_t startzeit; + unsigned dauer; + std::string subservice_name; + + CSubService(const t_original_network_id, const t_service_id, const t_transport_stream_id, const std::string &asubservice_name); + CSubService(const t_original_network_id, const t_service_id, const t_transport_stream_id, const time_t astartzeit, const unsigned adauer); + + t_channel_id getChannelID (void) const; + inline const struct CZapitClient::commandAddSubServices getAsZapitSubService(void) const { return service; } +}; + +typedef std::vector CSubServiceListSorted; + +class CRemoteControl +{ + unsigned int current_programm_timer; + unsigned long long zap_completion_timeout; + std::string current_channel_name; + t_channel_id current_sub_channel_id; + + void getNVODs(); + void getSubChannels(); + void copySubChannelsToZapit(void); + +public: + t_channel_id current_channel_id; + unsigned long long current_EPGid; + unsigned long long next_EPGid; + CZapitClient::responseGetPIDs current_PIDs; + + // APID - Details + bool has_ac3; + bool has_unresolved_ctags; + + // SubChannel/NVOD - Details + CSubServiceListSorted subChannels; + int selected_subchannel; + bool are_subchannels; + bool needs_nvods; + int director_mode; + + // Video / Parental-Lock + bool is_video_started; + + CRemoteControl(); + void zapTo_ChannelID(const t_channel_id channel_id, const std::string & channame, const bool start_video = true); // UTF-8 + void startvideo(); + void stopvideo(); + void queryAPIDs(); + void setAPID(uint32_t APID); + void processAPIDnames(); + const std::string & setSubChannel(const int numSub, const bool force_zap = false); + const std::string & subChannelUp(void); + const std::string & subChannelDown(void); + + void radioMode(); + void tvMode(); + + int handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data); + inline const std::string & getCurrentChannelName(void) const { return current_channel_name; } +}; + + +#endif diff --git a/src/driver/Makefile.am b/src/driver/Makefile.am new file mode 100644 index 000000000..a76fdf683 --- /dev/null +++ b/src/driver/Makefile.am @@ -0,0 +1,42 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +SUBDIRS = pictureviewer audiodec + +INCLUDES = \ + -I$(top_srcdir)/daemons \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libnet \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libcoolstream \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver \ + @FREETYPE_CFLAGS@ \ + -I$(top_srcdir)/lib + +noinst_LIBRARIES = libneutrino_driver.a libneutrino_driver_netfile.a + +libneutrino_driver_a_SOURCES = \ + ringbuffer.c \ + encoding.cpp \ + fontrenderer.cpp \ + framebuffer.cpp \ + fb_window.cpp \ + rcinput.cpp \ + vcrcontrol.cpp \ + audioplay.cpp \ + vfd.cpp \ + stream2file.cpp \ + screen_max.cpp \ + slotbuffer.c \ + file.cpp \ + audiometadata.cpp \ + audiofile.cpp \ + shutdown_count.cpp \ + genpsi.c \ + streamts.cpp \ + rfmod.cpp + +libneutrino_driver_netfile_a_SOURCES = netfile.cpp + diff --git a/src/driver/audiodec/Makefile.am b/src/driver/audiodec/Makefile.am new file mode 100644 index 000000000..908cd01ba --- /dev/null +++ b/src/driver/audiodec/Makefile.am @@ -0,0 +1,22 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libcoolstream \ + @FREETYPE_CFLAGS@ \ + @VORBISIDEC_CFLAGS@ + +noinst_LIBRARIES = libneutrino_driver_audiodec.a + +libneutrino_driver_audiodec_a_SOURCES = \ + basedec.cpp \ + cdrdec.cpp \ + mp3dec.cpp \ + oggdec.cpp \ + wavdec.cpp \ + tag.c \ + crc.c \ + vis.cpp diff --git a/src/driver/audiodec/basedec.cpp b/src/driver/audiodec/basedec.cpp new file mode 100644 index 000000000..6d2b0e0ef --- /dev/null +++ b/src/driver/audiodec/basedec.cpp @@ -0,0 +1,241 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 Zwen + base decoder class + Homepage: http://www.cyberphoria.org/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // for ShoutcastCallback() + +#include +#include +#include + +unsigned int CBaseDec::mSamplerate=0; + +void ShoutcastCallback(void *arg) +{ + CAudioPlayer::getInstance()->sc_callback(arg); +} + +CBaseDec::RetCode CBaseDec::DecoderBase(CAudiofile* const in, + const int OutputFd, State* const state, + time_t* const t, + unsigned int* const secondsToSkip) +{ + RetCode Status = OK; + + FILE* fp = fopen( in->Filename.c_str(), "r" ); + if ( fp == NULL ) + { + fprintf( stderr, "Error opening file %s for decoding.\n", + in->Filename.c_str() ); + Status = INTERNAL_ERR; + } + /* jump to first audio frame; audio_start_pos is only set for FILE_MP3 */ + else if ( in->MetaData.audio_start_pos && + fseek( fp, in->MetaData.audio_start_pos, SEEK_SET ) == -1 ) + { + fprintf( stderr, "fseek() failed.\n" ); + Status = INTERNAL_ERR; + } + + if ( Status == OK ) + { + if( in->FileType == CFile::STREAM_AUDIO ) + { + if ( fstatus( fp, ShoutcastCallback ) < 0 ) + { + fprintf( stderr, "Error adding shoutcast callback: %s", + err_txt ); + } + if(ftype(fp, (char *) "ogg")) + { + Status = COggDec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + else + { + Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + } + else if( in->FileType == CFile::FILE_MP3) + { + Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + else if( in->FileType == CFile::FILE_OGG ) + { + Status = COggDec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + else if( in->FileType == CFile::FILE_WAV ) + { + Status = CWavDec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + else if( in->FileType == CFile::FILE_CDR ) + { + Status = CCdrDec::getInstance()->Decoder( fp, OutputFd, state, + &in->MetaData, t, + secondsToSkip ); + } + else + { + fprintf( stderr, "DecoderBase: Supplied filetype is not " ); + fprintf( stderr, "supported by Audioplayer.\n" ); + Status = INTERNAL_ERR; + } + + if ( fclose( fp ) == EOF ) + { + fprintf( stderr, "Could not close file %s.\n", + in->Filename.c_str() ); + } + } + + return Status; +} + +bool CBaseDec::GetMetaDataBase(CAudiofile* const in, const bool nice) +{ + bool Status = true; + + if ( in->FileType == CFile::FILE_MP3 || in->FileType == CFile::FILE_OGG || + in->FileType == CFile::FILE_WAV || in->FileType == CFile::FILE_CDR ) + { + FILE* fp = fopen( in->Filename.c_str(), "r" ); + if ( fp == NULL ) + { + fprintf( stderr, "Error opening file %s for meta data reading.\n", + in->Filename.c_str() ); + Status = false; + } + else + { + if(in->FileType == CFile::FILE_MP3) + { + Status = CMP3Dec::getInstance()->GetMetaData(fp, nice, + &in->MetaData); + } + else if(in->FileType == CFile::FILE_OGG) + { + Status = COggDec::getInstance()->GetMetaData(fp, nice, + &in->MetaData); + } + else if(in->FileType == CFile::FILE_WAV) + { + Status = CWavDec::getInstance()->GetMetaData(fp, nice, + &in->MetaData); + } + else if(in->FileType == CFile::FILE_CDR) + { + Status = CCdrDec::getInstance()->GetMetaData(fp, nice, + &in->MetaData); + } + + if ( fclose( fp ) == EOF ) + { + fprintf( stderr, "Could not close file %s.\n", + in->Filename.c_str() ); + } + } + } + else + { + fprintf( stderr, "GetMetaDataBase: Filetype is not supported for " ); + fprintf( stderr, "meta data reading.\n" ); + Status = false; + } + + return Status; +} + +bool CBaseDec::SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels) +{ + bool crit_error=false; + + if (::ioctl(soundfd, SNDCTL_DSP_RESET)) + printf("reset failed\n"); + if(::ioctl(soundfd, SNDCTL_DSP_SETFMT, &fmt)) + printf("setfmt failed\n"); + if(::ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels)) + printf("channel set failed\n"); + if (dsp_speed != mSamplerate) + { + // mute audio to reduce pops when changing samplerate (avia_reset) + //bool was_muted = avs_mute(true); + if (::ioctl(soundfd, SNDCTL_DSP_SPEED, &dsp_speed)) + { + printf("speed set failed\n"); + crit_error=true; + } + else + { +#if 0 + unsigned int rs = 0; + ::ioctl(soundfd, SNDCTL_DSP_SPEED, &rs); + mSamplerate = dsp_speed; + // disable iec aka digi out (avia reset enables it again) + //g_Zapit->IecOff(); +#endif + } + //usleep(400000); + //if (!was_muted) + // avs_mute(false); + } +//printf("Debug: SNDCTL_DSP_RESET %d / SNDCTL_DSP_SPEED %d / SNDCTL_DSP_CHANNELS %d / SNDCTL_DSP_SETFMT %d\n", +// SNDCTL_DSP_RESET, SNDCTL_DSP_SPEED, SNDCTL_DSP_CHANNELS, SNDCTL_DSP_SETFMT); + return crit_error; +} + +bool CBaseDec::avs_mute(bool mute) +{ + return true; +} + +void CBaseDec::Init() +{ + mSamplerate=0; +} + diff --git a/src/driver/audiodec/basedec.h b/src/driver/audiodec/basedec.h new file mode 100644 index 000000000..5715e3e7f --- /dev/null +++ b/src/driver/audiodec/basedec.h @@ -0,0 +1,63 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 Zwen + + Decoder base class + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __BASE_DEC__ +#define __BASE_DEC__ + +#include +#include +#include + +class CBaseDec +{ +public: + enum State {STOP = 0, STOP_REQ, PLAY, PAUSE, FF, REV}; + enum RetCode { OK = 0, READ_ERR, WRITE_ERR, DSPSET_ERR, DATA_ERR, INTERNAL_ERR }; + + // the follwing two methods have to be implemented for new decoders + //@param secondsToSkip: a value of 0 indicates that normal FF/REV operation was requested + // a value > 0 indicates that *one* jump forwards (FF) or backwards (REV) was requested + virtual RetCode Decoder(FILE *, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const)=0; + virtual bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m)=0; + + static RetCode DecoderBase(CAudiofile* const in, const int OutputFd, + State* const state, time_t* const t, + unsigned int* const secondsToSkip); + static bool GetMetaDataBase(CAudiofile* const in, const bool nice); + static void Init(); + + CBaseDec(){}; + static bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels); +private: + static bool avs_mute(bool mute); + unsigned static int mSamplerate; +}; + + +#endif + diff --git a/src/driver/audiodec/cdrdec.cpp b/src/driver/audiodec/cdrdec.cpp new file mode 100644 index 000000000..1d483d4ee --- /dev/null +++ b/src/driver/audiodec/cdrdec.cpp @@ -0,0 +1,65 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 thegoodguy + + Homepage: http://www.dbox2.info/ + + Kommentar: + + cdr audio decoder + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +CCdrDec* CCdrDec::getInstance() +{ + static CCdrDec* CdrDec = NULL; + if (CdrDec == NULL) + { + CdrDec = new CCdrDec(); + } + return CdrDec; +} + +bool CCdrDec::SetMetaData(FILE* in, CAudioMetaData* m) +{ + header_size = 0; + + fseek(in, 0, SEEK_END); + int filesize = ftell(in); + + m->type = CAudioMetaData::CDR; + m->bitrate = 44100 * 2 * 2 * 8; + m->samplerate = 44100; + mBitsPerSample = 16; + mChannels = 2; + m->total_time = filesize / (44100 * 2 * 2); + m->type_info = "CDR / 2 channels / 16 bit"; + m->changed=true; + return true; +} + + diff --git a/src/driver/audiodec/cdrdec.h b/src/driver/audiodec/cdrdec.h new file mode 100644 index 000000000..65217aa3e --- /dev/null +++ b/src/driver/audiodec/cdrdec.h @@ -0,0 +1,43 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 thegoodguy + + cdr audio decoder + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __CDR_DEC__ +#define __CDR_DEC__ + +#include + +class CCdrDec : public CWavDec +{ + public: + static CCdrDec* getInstance(); + protected: + virtual bool SetMetaData(FILE* in, CAudioMetaData* m); +}; + +#endif + diff --git a/src/driver/audiodec/crc.c b/src/driver/audiodec/crc.c new file mode 100644 index 000000000..8495224e9 --- /dev/null +++ b/src/driver/audiodec/crc.c @@ -0,0 +1,109 @@ +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: crc.c,v 1.1 2004/08/28 14:11:58 rasc Exp $ + */ + +#ifdef INCLUDE_UNUSED_STUFF +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +//# include "global.h" + +# include "crc.h" + +/* + * It would be nice to use the code in libmad to compute all CRC-16 values, + * but unfortunately the LAME tag uses reflected CRCs whereas MPEG audio + * frames do not. Therefore we are forced to write yet another CRC routine. + */ + +static +unsigned short const crc_table[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 +}; + +/* + * NAME: crc->compute() + * DESCRIPTION: calculate reflected CRC-16 value (polynomial 0x8005) + */ +unsigned short crc_compute(char const *data, unsigned int length, + unsigned short init) +{ + register unsigned int crc; + + for (crc = init; length >= 8; length -= 8) { + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + } + + switch (length) { + case 7: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 6: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 5: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 4: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 3: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 2: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 1: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8); + case 0: break; + } + + return crc; +} +#endif /* INCLUDE_UNUSED_STUFF */ diff --git a/src/driver/audiodec/crc.h b/src/driver/audiodec/crc.h new file mode 100644 index 000000000..e882668de --- /dev/null +++ b/src/driver/audiodec/crc.h @@ -0,0 +1,29 @@ +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: crc.h,v 1.1 2004/08/28 14:11:58 rasc Exp $ + */ + +#ifdef INCLUDE_UNUSED_STUFF +# ifndef __CRC_H__ +# define __CRC_H__ + +unsigned short crc_compute(char const *, unsigned int, unsigned short); + +# endif /* __CRC_H__ */ +#endif /* INCLUDE_UNUSED_STUFF */ diff --git a/src/driver/audiodec/fft.c b/src/driver/audiodec/fft.c new file mode 100644 index 000000000..ba6ace9e1 --- /dev/null +++ b/src/driver/audiodec/fft.c @@ -0,0 +1,274 @@ +/* fft.c: Iterative implementation of a FFT + * Copyright (C) 1999 Richard Boulton + * Convolution stuff by Ralph Loader + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * TODO + * Remove compiling in of FFT_BUFFER_SIZE? (Might slow things down, but would + * be nice to be able to change size at runtime.) + * Finish making / checking thread-safety. + * More optimisations. + */ + +#include "fft.h" + +#include +#include +#ifndef PI + #ifdef M_PI + #define PI M_PI + #else + #define PI 3.14159265358979323846 /* pi */ + #endif +#endif + +/* ########### */ +/* # Structs # */ +/* ########### */ + +struct _struct_fft_state { + /* Temporary data stores to perform FFT in. */ + float real[FFT_BUFFER_SIZE]; + float imag[FFT_BUFFER_SIZE]; +}; + +/* ############################# */ +/* # Local function prototypes # */ +/* ############################# */ + +static void fft_prepare(const sound_sample *input, float * re, float * im); +static void fft_calculate(float * re, float * im); +static void fft_output(const float *re, const float *im, float *output); +static int reverseBits(unsigned int initial); + +/* #################### */ +/* # Global variables # */ +/* #################### */ + +/* Table to speed up bit reverse copy */ +static unsigned int bitReverse[FFT_BUFFER_SIZE]; + +/* The next two tables could be made to use less space in memory, since they + * overlap hugely, but hey. */ +static float sintable[FFT_BUFFER_SIZE / 2]; +static float costable[FFT_BUFFER_SIZE / 2]; + +/* ############################## */ +/* # Externally called routines # */ +/* ############################## */ + +/* --------- */ +/* FFT stuff */ +/* --------- */ + +/* + * Initialisation routine - sets up tables and space to work in. + * Returns a pointer to internal state, to be used when performing calls. + * On error, returns NULL. + * The pointer should be freed when it is finished with, by fft_close(). + */ +fft_state *fft_init(void) { + fft_state *state; + unsigned int i; + + state = (fft_state *) malloc (sizeof(fft_state)); + if(!state) return NULL; + + for(i = 0; i < FFT_BUFFER_SIZE; i++) { + bitReverse[i] = reverseBits(i); + } + for(i = 0; i < FFT_BUFFER_SIZE / 2; i++) { + float j = 2 * PI * i / FFT_BUFFER_SIZE; + costable[i] = cos(j); + sintable[i] = sin(j); + } + + return state; +} + +/* + * Do all the steps of the FFT, taking as input sound data (as described in + * sound.h) and returning the intensities of each frequency as floats in the + * range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2 + * + * FIXME - the above range assumes no frequencies present have an amplitude + * larger than that of the sample variation. But this is false: we could have + * a wave such that its maximums are always between samples, and it's just + * inside the representable range at the places samples get taken. + * Question: what _is_ the maximum value possible. Twice that value? Root + * two times that value? Hmmm. Think it depends on the frequency, too. + * + * The input array is assumed to have FFT_BUFFER_SIZE elements, + * and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements. + * state is a (non-NULL) pointer returned by fft_init. + */ +void fft_perform(const sound_sample *input, float *output, fft_state *state) { + /* Convert data from sound format to be ready for FFT */ + fft_prepare(input, state->real, state->imag); + + /* Do the actual FFT */ + fft_calculate(state->real, state->imag); + + /* Convert the FFT output into intensities */ + fft_output(state->real, state->imag, output); +} + +/* + * Free the state. + */ +void fft_close(fft_state *state) { + if(state) free(state); +} + +/* ########################### */ +/* # Locally called routines # */ +/* ########################### */ + +/* + * Prepare data to perform an FFT on + */ +static void fft_prepare(const sound_sample *input, float * re, float * im) { + unsigned int i; + float *realptr = re; + float *imagptr = im; + + /* Get input, in reverse bit order */ + for(i = 0; i < FFT_BUFFER_SIZE; i++) { + *realptr++ = input[bitReverse[i]]; + *imagptr++ = 0; + } +} + +/* + * Take result of an FFT and calculate the intensities of each frequency + * Note: only produces half as many data points as the input had. + * This is roughly a consequence of the Nyquist sampling theorm thingy. + * (FIXME - make this comment better, and helpful.) + * + * The two divisions by 4 are also a consequence of this: the contributions + * returned for each frequency are split into two parts, one at i in the + * table, and the other at FFT_BUFFER_SIZE - i, except for i = 0 and + * FFT_BUFFER_SIZE which would otherwise get float (and then 4* when squared) + * the contributions. + */ +static void fft_output(const float * re, const float * im, float *output) { + float *outputptr = output; + const float *realptr = re; + const float *imagptr = im; + float *endptr = output + FFT_BUFFER_SIZE / 2; + +#ifdef DEBUG + unsigned int i, j; +#endif + + while(outputptr <= endptr) { + *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr); + outputptr++; realptr++; imagptr++; + } + /* Do divisions to keep the constant and highest frequency terms in scale + * with the other terms. */ + *output /= 4; + *endptr /= 4; + +#ifdef DEBUG + printf("Recalculated input:\n"); + for(i = 0; i < FFT_BUFFER_SIZE; i++) { + float val_real = 0; + float val_imag = 0; + for(j = 0; j < FFT_BUFFER_SIZE; j++) { + float fact_real = cos(- 2 * j * i * PI / FFT_BUFFER_SIZE); + float fact_imag = sin(- 2 * j * i * PI / FFT_BUFFER_SIZE); + val_real += fact_real * re[j] - fact_imag * im[j]; + val_imag += fact_real * im[j] + fact_imag * re[j]; + } + printf("%5d = %8f + i * %8f\n", i, + val_real / FFT_BUFFER_SIZE, + val_imag / FFT_BUFFER_SIZE); + } + printf("\n"); +#endif +} + +/* + * Actually perform the FFT + */ +static void fft_calculate(float * re, float * im) { + unsigned int i, j, k; + unsigned int exchanges; + float fact_real, fact_imag; + float tmp_real, tmp_imag; + unsigned int factfact; + + /* Set up some variables to reduce calculation in the loops */ + exchanges = 1; + factfact = FFT_BUFFER_SIZE / 2; + + /* Loop through the divide and conquer steps */ + for(i = FFT_BUFFER_SIZE_LOG; i != 0; i--) { + /* In this step, we have 2 ^ (i - 1) exchange groups, each with + * 2 ^ (FFT_BUFFER_SIZE_LOG - i) exchanges + */ + /* Loop through the exchanges in a group */ + for(j = 0; j != exchanges; j++) { + /* Work out factor for this exchange + * factor ^ (exchanges) = -1 + * So, real = cos(j * PI / exchanges), + * imag = sin(j * PI / exchanges) + */ + fact_real = costable[j * factfact]; + fact_imag = sintable[j * factfact]; + + /* Loop through all the exchange groups */ + for(k = j; k < FFT_BUFFER_SIZE; k += exchanges << 1) { + int k1 = k + exchanges; + /* newval[k] := val[k] + factor * val[k1] + * newval[k1] := val[k] - factor * val[k1] + **/ +#ifdef DEBUG + printf("%d %d %d\n", i,j,k); + printf("Exchange %d with %d\n", k, k1); + printf("Factor %9f + i * %8f\n", fact_real, fact_imag); +#endif + /* FIXME - potential scope for more optimization here? */ + tmp_real = fact_real * re[k1] - fact_imag * im[k1]; + tmp_imag = fact_real * im[k1] + fact_imag * re[k1]; + re[k1] = re[k] - tmp_real; + im[k1] = im[k] - tmp_imag; + re[k] += tmp_real; + im[k] += tmp_imag; +#ifdef DEBUG + for(k1 = 0; k1 < FFT_BUFFER_SIZE; k1++) { + printf("%5d = %8f + i * %8f\n", k1, real[k1], imag[k1]); + } +#endif + } + } + exchanges <<= 1; + factfact >>= 1; + } +} + +static int reverseBits(unsigned int initial) { + unsigned int reversed = 0, loop; + for(loop = 0; loop < FFT_BUFFER_SIZE_LOG; loop++) { + reversed <<= 1; + reversed += (initial & 1); + initial >>= 1; + } + return reversed; +} diff --git a/src/driver/audiodec/fft.h b/src/driver/audiodec/fft.h new file mode 100644 index 000000000..9816ac478 --- /dev/null +++ b/src/driver/audiodec/fft.h @@ -0,0 +1,43 @@ +/* fft.h: Header for iterative implementation of a FFT + * Copyright (C) 1999 Richard Boulton + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FFT_H_ +#define _FFT_H_ + +#define FFT_BUFFER_SIZE_LOG 9 + +#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG) + +/* sound sample - should be an signed 16 bit value */ +typedef short int sound_sample; + +#ifdef __cplusplus +extern "C" { +#endif + +/* FFT library */ +typedef struct _struct_fft_state fft_state; +fft_state *fft_init (void); +void fft_perform (const sound_sample *input, float *output, fft_state *state); +void fft_close (fft_state *state); + +#ifdef __cplusplus +} +#endif + +#endif /* _FFT_H_ */ diff --git a/src/driver/audiodec/int_fft.c b/src/driver/audiodec/int_fft.c new file mode 100644 index 000000000..1909606d3 --- /dev/null +++ b/src/driver/audiodec/int_fft.c @@ -0,0 +1,528 @@ +/* fix_fft.c - Fixed-point Fast Fourier Transform */ +/* + fix_fft() perform FFT or inverse FFT + window() applies a Hanning window to the (time) input + fix_loud() calculates the loudness of the signal, for + each freq point. Result is an integer array, + units are dB (values will be negative). + iscale() scale an integer value by (numer/denom). + fix_mpy() perform fixed-point multiplication. + Sinewave[1024] sinewave normalized to 32767 (= 1.0). + Loudampl[100] Amplitudes for lopudnesses from 0 to -99 dB. + Low_pass Low-pass filter, cutoff at sample_freq / 4. + + + All data are fixed-point short integers, in which + -32768 to +32768 represent -1.0 to +1.0. Integer arithmetic + is used for speed, instead of the more natural floating-point. + + For the forward FFT (time -> freq), fixed scaling is + performed to prevent arithmetic overflow, and to map a 0dB + sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq + coefficients; the one in the lower half is reported as 0dB + by fix_loud(). The return value is always 0. + + For the inverse FFT (freq -> time), fixed scaling cannot be + done, as two 0dB coefficients would sum to a peak amplitude of + 64K, overflowing the 32k range of the fixed-point integers. + Thus, the fix_fft() routine performs variable scaling, and + returns a value which is the number of bits LEFT by which + the output must be shifted to get the actual amplitude + (i.e. if fix_fft() returns 3, each value of fr[] and fi[] + must be multiplied by 8 (2**3) for proper scaling. + Clearly, this cannot be done within the fixed-point short + integers. In practice, if the result is to be used as a + filter, the scale_shift can usually be ignored, as the + result will be approximately correctly normalized as is. + + + TURBO C, any memory model; uses inline assembly for speed + and for carefully-scaled arithmetic. + + Written by: Tom Roberts 11/8/89 + Made portable: Malcolm Slaney 12/15/94 malcolm@interval.com + + Timing on a Macintosh PowerBook 180.... (using Symantec C6.0) + fix_fft (1024 points) 8 ticks + fft (1024 points - Using SANE) 112 Ticks + fft (1024 points - Using FPU) 11 + +*/ + +/* FIX_MPY() - fixed-point multiplication macro. + This macro is a statement, not an expression (uses asm). + BEWARE: make sure _DX is not clobbered by evaluating (A) or DEST. + args are all of type fixed. + Scaling ensures that 32767*32767 = 32767. */ +#define dosFIX_MPY(DEST,A,B) { \ + _DX = (B); \ + _AX = (A); \ + asm imul dx; \ + asm add ax,ax; \ + asm adc dx,dx; \ + DEST = _DX; } + +#define FIX_MPY(DEST,A,B) DEST = ((long)(A) * (long)(B))>>15 + +#define N_WAVE 1024 /* dimension of Sinewave[] */ +#define LOG2_N_WAVE 10 /* log2(N_WAVE) */ +#define N_LOUD 100 /* dimension of Loudampl[] */ +#ifndef fixed +#define fixed short +#endif + +extern fixed Sinewave[N_WAVE]; /* placed at end of this file for clarity */ +extern fixed Loudampl[N_LOUD]; +int db_from_ampl(fixed re, fixed im); +fixed fix_mpy(fixed a, fixed b); + +/* + fix_fft() - perform fast Fourier transform. + + if n>0 FFT is done, if n<0 inverse FFT is done + fr[n],fi[n] are real,imaginary arrays, INPUT AND RESULT. + size of data = 2**m + set inverse to 0=dft, 1=idft +*/ +int fix_fft(fixed fr[], fixed fi[], int m, int inverse) +{ + int mr,nn,i,j,l,k,istep, n, scale, shift; + fixed qr,qi,tr,ti,wr,wi; + + n = 1< N_WAVE) + return -1; + + mr = 0; + nn = n - 1; + scale = 0; + + /* decimation in time - re-order data */ + for(m=1; m<=nn; ++m) { + l = n; + do { + l >>= 1; + } while(mr+l > nn); + mr = (mr & (l-1)) + l; + + if(mr <= m) continue; + tr = fr[m]; + fr[m] = fr[mr]; + fr[mr] = tr; + ti = fi[m]; + fi[m] = fi[mr]; + fi[mr] = ti; + } + + l = 1; + k = LOG2_N_WAVE-1; + while(l < n) { + if(inverse) { + /* variable scaling, depending upon data */ + shift = 0; + for(i=0; i 16383 || m > 16383) { + shift = 1; + break; + } + } + if(shift) + ++scale; + } else { + /* fixed scaling, for proper normalization - + there will be log2(n) passes, so this + results in an overall factor of 1/n, + distributed to maximize arithmetic accuracy. */ + shift = 1; + } + /* it may not be obvious, but the shift will be performed + on each data point exactly once, during this pass. */ + istep = l << 1; + for(m=0; m>= 1; + wi >>= 1; + } + for(i=m; i>= 1; + qi >>= 1; + } + fr[j] = qr - tr; + fi[j] = qi - ti; + fr[i] = qr + tr; + fi[i] = qi + ti; + } + } + --k; + l = istep; + } + + return scale; +} + + +/* window() - apply a Hanning window */ +void window(fixed fr[], int n) +{ + int i,j,k; + + j = N_WAVE/n; + n >>= 1; + for(i=0,k=N_WAVE/4; i>1)); + n <<= 1; + for(k-=j; i>1)); +} + +/* fix_loud() - compute loudness of freq-spectrum components. + n should be ntot/2, where ntot was passed to fix_fft(); + 6 dB is added to account for the omitted alias components. + scale_shift should be the result of fix_fft(), if the time-series + was obtained from an inverse FFT, 0 otherwise. + loud[] is the loudness, in dB wrt 32767; will be +10 to -N_LOUD. +*/ +void fix_loud(fixed loud[], fixed fr[], fixed fi[], int n, int scale_shift) +{ + int i, max; + + max = 0; + if(scale_shift > 0) + max = 10; + scale_shift = (scale_shift+1) * 6; + + for(i=0; i max) + loud[i] = max; + } +} + +/* db_from_ampl() - find loudness (in dB) from + the complex amplitude. +*/ +int db_from_ampl(fixed re, fixed im) +{ + static long loud2[N_LOUD] = {0}; + long v; + int i; + + if(loud2[0] == 0) { + loud2[0] = (long)Loudampl[0] * (long)Loudampl[0]; + for(i=1; i>4; + off &= 0x000F; + pa = MK_FP(seg,off); + */ + sum = 0L; + while(n--) { + a = *pa++; + b = *pb++; + FIX_MPY(a,a,b); + sum += a; + } + + if(sum > 0x7FFF) + sum = 0x7FFF; + else if(sum < -0x7FFF) + sum = -0x7FFF; + + return (fixed)sum; +#ifdef DOS + /* ASSUMES hpa is already normalized so FP_OFF(hpa) < 16 */ + asm push ds + asm lds si,hpa + asm les di,pb + asm xor bx,bx + + asm xor cx,cx + +loop: /* intermediate values can overflow by a factor of 2 without + causing an error; the final value must not overflow! */ + asm lodsw +. + asm imul word ptr es:[di] + asm add bx,ax + asm adc cx,dx + asm jo overflow + asm add di,2 + asm dec word ptr n + asm jg loop + + asm add bx,bx + asm adc cx,cx + asm jo overflow + + asm pop ds + return _CX; + +overflow: + asm mov cx,7FFFH + asm adc cx,0 + + asm pop ds + return _CX; +#endif + +} + + +#if N_WAVE != 1024 + ERROR: N_WAVE != 1024 +#endif +fixed Sinewave[1024] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, + 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, + 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, + 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, + 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, + 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, + 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, + 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, + 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, + 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, + 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, + 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, + 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, + 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, + 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, + 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, + 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, + 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, + 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, + 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, + 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, + 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, + 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, + 30851, 30918, 30984, 31049, + 31113, 31175, 31236, 31297, + 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, + 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, + 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, + 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, + 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, + 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, + 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, + 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, + 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, + 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, + 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, + 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, + 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, + 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, + 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, + 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, + 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, + 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, + 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, + 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, + 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, + 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, + 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, + 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, + 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, + 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, + 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, + 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, + 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, + 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, + 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, + 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, + 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, + 0, -201, -402, -603, -804, -1005, -1206, -1406, + -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, + -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, + -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, + -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, + -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, + -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, + -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, + -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, + -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, + -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, + -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, + -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, + -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, + -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, + -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, + -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, + -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, + -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, + -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, + -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, + -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, + -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, + -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, + -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, + -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, + -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, + -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, + -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, + -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, + -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, + -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, + -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, + -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, + -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, + -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, + -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, + -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, + -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, + -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, + -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, + -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, + -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, + -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, + -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, + -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, + -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, + -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, + -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, + -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, + -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, + -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, + -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, + -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, + -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, + -1607, -1406, -1206, -1005, -804, -603, -402, -201, +}; + +#if N_LOUD != 100 + ERROR: N_LOUD != 100 +#endif +fixed Loudampl[100] = { + 32767, 29203, 26027, 23197, 20674, 18426, 16422, 14636, + 13044, 11626, 10361, 9234, 8230, 7335, 6537, 5826, + 5193, 4628, 4125, 3676, 3276, 2920, 2602, 2319, + 2067, 1842, 1642, 1463, 1304, 1162, 1036, 923, + 823, 733, 653, 582, 519, 462, 412, 367, + 327, 292, 260, 231, 206, 184, 164, 146, + 130, 116, 103, 92, 82, 73, 65, 58, + 51, 46, 41, 36, 32, 29, 26, 23, + 20, 18, 16, 14, 13, 11, 10, 9, + 8, 7, 6, 5, 5, 4, 4, 3, + 3, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +#ifdef MAIN + +#include +#include + +#define M 4 +#define N (1< + (C) 2002,2003,2004 Zwen + + libmad MP3 low-level core + Homepage: http://www.cyberphoria.org/ + + Kommentar: + + based on + ************************************ + *** madlld -- Mad low-level *** v 1.0p1, 2002-01-08 + *** demonstration/decoder *** (c) 2001, 2002 Bertrand Petit + ************************************ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +/**************************************************************************** + * Includes * + ****************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +extern cAudio * audioDecoder; + +//#define SPECTRUM + +#ifdef SPECTRUM +void sanalyzer_render_freq(short data[1024]); +#endif +/* libid3tag extension: This is neccessary in order to call fclose + on the file. Normally libid3tag closes the file implicit. + For the netfile extension to work properly netfiles fclose must be called. + To close an id3 file (usually by calling id3_file_close) without fclosing it, + call following i3_finish_file function. It's just a copy of libid3tags + finish_file function. */ +extern "C" +{ +void id3_tag_addref(struct id3_tag *); +void id3_tag_delref(struct id3_tag *); +struct filetag { + struct id3_tag *tag; + unsigned long location; + id3_length_t length; +}; +struct id3_file { + FILE *iofile; + enum id3_file_mode mode; + char *path; + int flags; + struct id3_tag *primary; + unsigned int ntags; + struct filetag *tags; +}; +void id3_finish_file(struct id3_file* file); +} + +// Frames to skip in ff/rev mode +#define FRAMES_TO_SKIP 50 // 75 +// nr of frames to play after skipping in rev/ff mode +#define FRAMES_TO_PLAY 5 + +#define ProgName "CMP3Dec" + +/**************************************************************************** + * Global variables. * + ****************************************************************************/ + +/**************************************************************************** + * Return an error string associated with a mad error code. * + ****************************************************************************/ +/* Mad version 0.14.2b introduced the mad_stream_errorstr() function. + * For previous library versions a replacement is provided below. + */ +#if (MAD_VERSION_MAJOR>=1) || \ + ((MAD_VERSION_MAJOR==0) && \ + (((MAD_VERSION_MINOR==14) && \ + (MAD_VERSION_PATCH>=2)) || \ + (MAD_VERSION_MINOR>14))) +#define MadErrorString(x) mad_stream_errorstr(x) +#else +const char *CMP3Dec::MadErrorString(const struct mad_stream *Stream) +{ + switch(Stream->error) + { + /* Generic unrecoverable errors. */ + case MAD_ERROR_BUFLEN: + return("input buffer too small (or EOF)"); + case MAD_ERROR_BUFPTR: + return("invalid (null) buffer pointer"); + case MAD_ERROR_NOMEM: + return("not enough memory"); + + /* Frame header related unrecoverable errors. */ + case MAD_ERROR_LOSTSYNC: + return("lost synchronization"); + case MAD_ERROR_BADLAYER: + return("reserved header layer value"); + case MAD_ERROR_BADBITRATE: + return("forbidden bitrate value"); + case MAD_ERROR_BADSAMPLERATE: + return("reserved sample frequency value"); + case MAD_ERROR_BADEMPHASIS: + return("reserved emphasis value"); + + /* Recoverable errors */ + case MAD_ERROR_BADCRC: + return("CRC check failed"); + case MAD_ERROR_BADBITALLOC: + return("forbidden bit allocation value"); + case MAD_ERROR_BADSCALEFACTOR: + return("bad scalefactor index"); + case MAD_ERROR_BADFRAMELEN: + return("bad frame length"); + case MAD_ERROR_BADBIGVALUES: + return("bad big_values count"); + case MAD_ERROR_BADBLOCKTYPE: + return("reserved block_type"); + case MAD_ERROR_BADSCFSI: + return("bad scalefactor selection info"); + case MAD_ERROR_BADDATAPTR: + return("bad main_data_begin pointer"); + case MAD_ERROR_BADPART3LEN: + return("bad audio data length"); + case MAD_ERROR_BADHUFFTABLE: + return("bad Huffman table select"); + case MAD_ERROR_BADHUFFDATA: + return("Huffman data overrun"); + case MAD_ERROR_BADSTEREO: + return("incompatible block_type for JS"); + + /* Unknown error. This swich may be out of sync with libmad's + * defined error codes. + */ + default: + return("Unknown error code"); + } +} +#endif + +/**************************************************************************** + * Converts a sample from mad's fixed point number format to a signed * + * short (16 bits). * + ****************************************************************************/ +#if 1 +struct audio_dither { + mad_fixed_t error[3]; + mad_fixed_t random; +}; +static struct audio_dither left_dither, right_dither; +static inline +unsigned long prng(unsigned long state) +{ + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; +} + +inline signed short CMP3Dec::MadFixedToSShort(const mad_fixed_t Fixed, bool left) +{ + unsigned int scalebits; + mad_fixed_t output, mask, random; + struct audio_dither *dither = left ? &left_dither : &right_dither; + unsigned int bits = 16; + mad_fixed_t sample = Fixed; + + enum { + MIN = -MAD_F_ONE, + MAX = MAD_F_ONE - 1 + }; + + /* noise shape */ + sample += dither->error[0] - dither->error[1] + dither->error[2]; + + dither->error[2] = dither->error[1]; + dither->error[1] = dither->error[0] / 2; + + /* bias */ + output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1)); + + scalebits = MAD_F_FRACBITS + 1 - bits; + mask = (1L << scalebits) - 1; + + /* dither */ + random = prng(dither->random); + output += (random & mask) - (dither->random & mask); + + dither->random = random; + + /* clip */ +#if 0 + if (output >= MAD_F_ONE) + output = 32767; + else if (output < -MAD_F_ONE) + output = -32768; +#endif + + if (output > MAX) + output = MAX; + else if (output < MIN) + output = MIN; + + /* quantize */ + output &= ~mask; + + /* error feedback */ + dither->error[0] = sample - output; + + /* scale */ + return (signed short) (output >> scalebits); +} +#else + +inline signed short CMP3Dec::MadFixedToSShort(const mad_fixed_t Fixed) +{ + /* A fixed point number is formed of the following bit pattern: + * + * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF + * MSB LSB + * S ==> Sign (0 is positive, 1 is negative) + * W ==> Whole part bits + * F ==> Fractional part bits + * + * This pattern contains MAD_F_FRACBITS fractional bits, one + * should alway use this macro when working on the bits of a fixed + * point number. It is not guaranteed to be constant over the + * different platforms supported by libmad. + * + * The unsigned short value is formed by the least significant + * whole part bit, followed by the 15 most significant fractional + * part bits. Warning: this is a quick and dirty way to compute + * the 16-bit number, madplay includes much better algorithms. + */ + if (Fixed >= MAD_F_ONE) + return 32767; + else if (Fixed < -MAD_F_ONE) + return -32768; + + return (signed short)(Fixed >> (MAD_F_FRACBITS + 1 - 16)); +} +#endif +/**************************************************************************** + * Print human readable informations about an audio MPEG frame. * + ****************************************************************************/ +void CMP3Dec::CreateInfo(CAudioMetaData* m, int FrameNumber) +{ + if ( !m ) + return; + + if ( !m->hasInfoOrXingTag ) + { + m->total_time = m->avg_bitrate != 0 ? + static_cast( round( static_cast( m->filesize ) + / m->avg_bitrate ) ) + : 0; + } + + if ( FrameNumber == 1 ) + { + using namespace std; + string Layer, Mode; + + /* Convert the layer number to it's printed representation. */ + switch(m->layer) + { + case MAD_LAYER_I: + Layer="I"; + break; + case MAD_LAYER_II: + Layer="II"; + break; + case MAD_LAYER_III: + Layer="III"; + break; + default: + Layer="?"; + break; + } + + /* Convert the audio mode to it's printed representation. */ + switch(m->mode) + { + case MAD_MODE_SINGLE_CHANNEL: + Mode="single channel"; + break; + case MAD_MODE_DUAL_CHANNEL: + Mode="dual channel"; + break; + case MAD_MODE_JOINT_STEREO: + Mode="joint stereo"; + break; + case MAD_MODE_STEREO: + Mode="normal stereo"; + break; + default: + Mode="unkn. mode"; + break; + } + +#ifdef INCLUDE_UNUSED_STUFF + const char *Emphasis, *Vbr; + + /* Convert the emphasis to it's printed representation. */ + switch(m->emphasis) + { + case MAD_EMPHASIS_NONE: + Emphasis="no"; + break; + case MAD_EMPHASIS_50_15_US: + Emphasis="50/15 us"; + break; + case MAD_EMPHASIS_CCITT_J_17: + Emphasis="CCITT J.17"; + break; + default: + Emphasis="(unexpected emphasis value)"; + break; + } + + if(m->vbr) + Vbr="VBR "; + else + Vbr=""; +#endif /* INCLUDE_UNUSED_STUFF */ + + m->type_info = string("MPEG Layer ") + Layer + string(" / ") + Mode; + } + + m->changed = true; +} + +/**************************************************************************** + * Main decoding loop. This is where mad is used. * + ****************************************************************************/ +#define INPUT_BUFFER_SIZE (2*8192) //(5*8192) /* enough to skip big id3 tags */ +#define OUTPUT_BUFFER_SIZE 8192 +#define SPECTRUM_CNT 4 +CBaseDec::RetCode CMP3Dec::Decoder(FILE *InputFp, const int OutputFd, + State* const state, + CAudioMetaData* meta_data, + time_t* const time_played, + unsigned int* const secondsToSkip) +{ + struct mad_stream Stream; + struct mad_frame Frame; + struct mad_synth Synth; + mad_timer_t Timer; + unsigned char InputBuffer[INPUT_BUFFER_SIZE], + OutputBuffer[OUTPUT_BUFFER_SIZE], + *OutputPtr=OutputBuffer; + const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE; + RetCode Status=OK; + int ret; + unsigned long FrameCount=0; + +#ifdef SPECTRUM + bool update_lcd = false; + int i = 0, j = 0; + signed short data[1024]; + if(g_settings.spectrum) + CVFD::getInstance ()->Lock (); + int scnt = 0; +#endif + signed short ll, rr; + + /* First the structures used by libmad must be initialized. */ + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + mad_timer_reset(&Timer); + + // used as kind of mutex for single jumps in the file + // to make sure the amount of frames is calculated + // before jumping (without this state/secondsToSkip could change + // anytime within the loop) + bool jumpDone=false; + + /* Decoding options can here be set in the options field of the + * Stream structure. + */ + + /* This is the decoding loop. */ + do + { + int secondsToJump = *secondsToSkip; + if(*state==PAUSE) + { + // in pause mode do nothing + usleep(100000); + continue; + } + /* The input bucket must be filled if it becomes empty or if + * it's the first execution of the loop. + */ + if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN) + { + size_t ReadSize, + Remaining; + unsigned char *ReadStart; + + /* {1} libmad may not consume all bytes of the input + * buffer. If the last frame in the buffer is not wholly + * contained by it, then that frame's start is pointed by + * the next_frame member of the Stream structure. This + * common situation occurs when mad_frame_decode() fails, + * sets the stream error code to MAD_ERROR_BUFLEN, and + * sets the next_frame pointer to a non NULL value. (See + * also the comment marked {2} bellow.) + * + * When this occurs, the remaining unused bytes must be + * put back at the beginning of the buffer and taken in + * account before refilling the buffer. This means that + * the input buffer must be large enough to hold a whole + * frame at the highest observable bit-rate (currently 448 + * kb/s). XXX=XXX Is 2016 bytes the size of the largest + * frame? (448000*(1152/32000))/8 + */ + if(Stream.next_frame!=NULL) + { + Remaining=Stream.bufend-Stream.next_frame; + memmove(InputBuffer,Stream.next_frame,Remaining); + ReadStart=InputBuffer+Remaining; + ReadSize=INPUT_BUFFER_SIZE-Remaining; + } + else + ReadSize=INPUT_BUFFER_SIZE, + ReadStart=InputBuffer, + Remaining=0; + + /* Fill-in the buffer. If an error occurs print a message + * and leave the decoding loop. If the end of stream is + * reached we also leave the loop but the return status is + * left untouched. + */ + ReadSize=fread(ReadStart,1,ReadSize,InputFp); + if(ReadSize<=0) + { + if(ferror(InputFp)) + { + fprintf(stderr,"%s: read error on bitstream (%s)\n", + ProgName,strerror(errno)); + Status=READ_ERR; + } + if(feof(InputFp)) + fprintf(stderr,"%s: end of input stream\n",ProgName); + break; + } + + /* Pipe the new buffer content to libmad's stream decoder + * facility. */ + mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining); + Stream.error=(mad_error)0; + } + + long actFramesToSkip=FRAMES_TO_SKIP; + // Calculate the amount of frames within the custom period + if(((*state==FF) || (*state==REV)) && ((secondsToJump)!=0)) + { + jumpDone=false; + + // what is 1152? grabbed it from the documentation above {1} ;) + actFramesToSkip=secondsToJump * meta_data->samplerate/1152; + //printf("secsToSkip: %d, framesToSkip: %ld\n", secondsToJump,actFramesToSkip); + } + + /* Decode the next mpeg frame. The streams is read from the + * buffer, its constituents are break down and stored the the + * Frame structure, ready for examination/alteration or PCM + * synthesis. Decoding options are carried in the Frame + * structure from the Stream structure. + * + * Error handling: mad_frame_decode() returns a non zero value + * when an error occurs. The error condition can be checked in + * the error member of the Stream structure. A mad error is + * recoverable or fatal, the error status is checked with the + * MAD_RECOVERABLE macro. + * + * {2} When a fatal error is encountered all decoding + * activities shall be stopped, except when a MAD_ERROR_BUFLEN + * is signaled. This condition means that the + * mad_frame_decode() function needs more input to achieve + * it's work. One shall refill the buffer and repeat the + * mad_frame_decode() call. Some bytes may be left unused at + * the end of the buffer if those bytes forms an incomplete + * frame. Before refilling, the remainign bytes must be moved + * to the begining of the buffer and used for input for the +q * next mad_frame_decode() invocation. (See the comments marked + * {1} earlier for more details.) + * + * Recoverable errors are caused by malformed bit-streams, in + * this case one can call again mad_frame_decode() in order to + * skip the faulty part and re-sync to the next frame. + */ + // decode 'FRAMES_TO_PLAY' frames each 'FRAMES_TO_SKIP' frames in ff/rev mode + if( (*state!=FF && + *state!=REV) || + FrameCount % actFramesToSkip < FRAMES_TO_PLAY ) + ret=mad_frame_decode(&Frame,&Stream); + else if(*state==FF) // in FF mode just decode the header, this sets bufferptr to next frame and also gives stats about the frame for totals + if (secondsToJump != 0 && !jumpDone) + { + jumpDone=true; + // jump forwards + long bytesForward = (Stream.bufend - Stream.this_frame) + ((ftell(InputFp)+Stream.this_frame-Stream.bufend) / FrameCount)*(actFramesToSkip + FRAMES_TO_PLAY); + //printf("jump forwards by %d secs and %ld bytes",secondsToJump, bytesForward); + + if (fseek(InputFp, bytesForward, SEEK_CUR)!=0) + { + // Reached end, do nothing + } + else + { + // Calculate timer + mad_timer_t m; + mad_timer_set(&m, 0, 32 * MAD_NSBSAMPLES(&Frame.header) *(actFramesToSkip + FRAMES_TO_PLAY), Frame.header.samplerate); + Timer.seconds += m.seconds; + if((Timer.fraction + m.fraction)*MAD_TIMER_RESOLUTION>=1) + { + Timer.seconds++; + Timer.fraction-= m.fraction; + } + else + Timer.fraction+= m.fraction; + // in case we calculated wrong... + if(Timer.seconds < 0) + { + Timer.seconds=0; + Timer.fraction=0; + } + *time_played=Timer.seconds; + FrameCount+=actFramesToSkip + FRAMES_TO_PLAY; + } + Stream.buffer=NULL; + Stream.next_frame=NULL; + // if a custom value was set we only jump once + *state=PLAY; + continue; + } else + { + ret=mad_header_decode(&Frame.header,&Stream); + } + else + { //REV + // Jump back + long bytesBack = (Stream.bufend - Stream.this_frame) + ((ftell(InputFp)+Stream.this_frame-Stream.bufend) / FrameCount)*(actFramesToSkip + FRAMES_TO_PLAY); + + if (secondsToJump!=0) + { + jumpDone=true; + //printf("jumping backwards by %d secs and %ld bytes\n",secondsToJump, bytesBack); + } + if (fseek(InputFp, -1*(bytesBack), SEEK_CUR)!=0) + { + // Reached beginning + fseek(InputFp, 0, SEEK_SET); + Timer.fraction=0; + Timer.seconds=0; + FrameCount=0; + *state=PLAY; + } + else + { + // Calculate timer + mad_timer_t m; + mad_timer_set(&m, 0, 32 * MAD_NSBSAMPLES(&Frame.header) *(actFramesToSkip + FRAMES_TO_PLAY), Frame.header.samplerate); + Timer.seconds -= m.seconds; + if(Timer.fraction < m.fraction) + { + Timer.seconds--; + Timer.fraction+= MAD_TIMER_RESOLUTION - m.fraction; + } + else + Timer.fraction-= m.fraction; + // in case we calculated wrong... + if(Timer.seconds < 0) + { + Timer.seconds=0; + Timer.fraction=0; + } + *time_played=Timer.seconds; + FrameCount-=actFramesToSkip + FRAMES_TO_PLAY; + } + Stream.buffer=NULL; + Stream.next_frame=NULL; + // if a custom value was set we only jump once + if (secondsToJump != 0) { + *state=PLAY; + } + continue; + } + + if(ret) + { + if(MAD_RECOVERABLE(Stream.error)) + { + // no errrors in FF mode + if(*state!=FF && + *state!=REV) + { + fprintf(stderr,"%s: recoverable frame level error (%s)\n", + ProgName,MadErrorString(&Stream)); + fflush(stderr); + } + continue; + } + else + if(Stream.error==MAD_ERROR_BUFLEN) + continue; + else + { + fprintf(stderr,"%s: unrecoverable frame level error (%s).\n", + ProgName,MadErrorString(&Stream)); + Status=DATA_ERR; + break; + } + } + + /* On first frame set DSP & save header info + * The first frame is representative of the entire + * stream. + */ + FrameCount++; + if (FrameCount == 1) + { +#if 0 + if (SetDSP(OutputFd, AFMT_S16_NE, Frame.header.samplerate, 2)) + { + Status=DSPSET_ERR; + break; + } +#endif + audioDecoder->PrepareClipPlay(2, Frame.header.samplerate, 16, 1); + + if ( !meta_data ) + { + meta_data = new CAudioMetaData; + } + meta_data->samplerate = Frame.header.samplerate; + meta_data->bitrate = Frame.header.bitrate; + meta_data->mode = Frame.header.mode; + meta_data->layer = Frame.header.layer; + CreateInfo( meta_data, FrameCount ); + } + else + { + if ( meta_data->bitrate != Frame.header.bitrate ) + { + /* bitrate of actual frame */ + meta_data->vbr = true; + meta_data->bitrate = Frame.header.bitrate; + + /* approximate average bitrate */ + meta_data->avg_bitrate -= + meta_data->avg_bitrate / FrameCount; + meta_data->avg_bitrate += + Frame.header.bitrate / FrameCount; + CreateInfo( meta_data, FrameCount ); + } + } + + // if played time was modified from outside, take this value... + if(*time_played!=Timer.seconds) + { + mad_timer_reset(&Timer); + Timer.seconds = *time_played; + } + + /* Accounting. The computed frame duration is in the frame + * header structure. It is expressed as a fixed point number + * whole data type is mad_timer_t. It is different from the + * samples fixed point format and unlike it, it can't directly + * be added or substracted. The timer module provides several + * functions to operate on such numbers. Be careful there, as + * some functions of mad's timer module receive some of their + * mad_timer_t arguments by value! + */ + mad_timer_add(&Timer,Frame.header.duration); + //mad_timer_string(Timer,m_timePlayed,"%lu:%02lu", + // MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0); + *time_played = Timer.seconds; + + + // decode 5 frames each 75 frames in ff mode + if( *state!=FF || FrameCount % actFramesToSkip < FRAMES_TO_PLAY) + { + + /* Once decoded the frame is synthesized to PCM samples. No errors + * are reported by mad_synth_frame(); + */ + mad_synth_frame(&Synth, &Frame); + + + /* Synthesized samples must be converted from mad's fixed + * point number to the consumer format. Here we use signed + * 16 bit native endian integers on two channels. Integer samples + * are temporarily stored in a buffer that is flushed when + * full. + */ + + if (MAD_NCHANNELS(&Frame.header) == 2) + { + mad_fixed_t * leftchannel = Synth.pcm.samples[0]; + mad_fixed_t * rightchannel = Synth.pcm.samples[1]; + + while (Synth.pcm.length-- > 0) + { + ll = MadFixedToSShort(*(leftchannel++), true); + rr = MadFixedToSShort(*(rightchannel++), false); + *((signed short *)OutputPtr) = ll; + *(((signed short *)OutputPtr) + 1) = rr; + + OutputPtr += 4; +#ifdef SPECTRUM +#define DDIFF 0 + if(g_settings.spectrum) { +#if HAVE_DBOX2 + if(scnt == SPECTRUM_CNT-2) +#endif + { + j += 4; + if((i < 512) && (j > DDIFF)) { +//if(i == 0) printf("Filling on j = %d\n", j); + int tmp = (ll+rr)/2; + data[i] = (signed short)tmp; + i++; + } + } + } +#endif + + /* Flush the buffer if it is full. */ + if (OutputPtr == OutputBufferEnd) + { + //if (write(OutputFd, OutputBuffer, OUTPUT_BUFFER_SIZE) != OUTPUT_BUFFER_SIZE) + if(audioDecoder->WriteClip(OutputBuffer, OUTPUT_BUFFER_SIZE) != OUTPUT_BUFFER_SIZE) + { + fprintf(stderr,"%s: PCM write error in stereo (%s).\n", ProgName, strerror(errno)); + Status = WRITE_ERR; + break; + } + + OutputPtr = OutputBuffer; +#ifdef SPECTRUM + if(g_settings.spectrum) { + i = 0; + j = 0; + update_lcd = true; + +//gettimeofday (&tv2, NULL); +//int ms = (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000; +//if(ms)printf("Write takes %dms\n", ms); + //sanalyzer_render_vu(data); + scnt++; +#if HAVE_DBOX2 + if(scnt >= SPECTRUM_CNT) +#endif + { + scnt = 0; + sanalyzer_render_freq(data); + } + } +#endif + } + } + } + else + { + mad_fixed_t * leftchannel = Synth.pcm.samples[0]; + + while (Synth.pcm.length-- > 0) + { + /* Left channel => copy to right channel */ + + //*(((signed short *)OutputPtr) + 1) = *((signed short *)OutputPtr) = MadFixedToSShort(*(leftchannel++)); + ll = MadFixedToSShort(*(leftchannel++), false); + rr = ll; + *(((signed short *)OutputPtr) + 1) = *((signed short *)OutputPtr) = ll; + + OutputPtr += 4; +#ifdef SPECTRUM +#define DDIFF 1024 + if(g_settings.spectrum) { +#if HAVE_DBOX2 + if(scnt == SPECTRUM_CNT-2) +#endif + { + j += 4; + if((i < 512) && (j > DDIFF)) { +//if(i == 0) printf("Filling on j = %d\n", j); + int tmp = (ll+rr)/2; + data[i] = (signed short)tmp; + i++; + } + } + } +#endif + + /* Flush the buffer if it is full. */ + if (OutputPtr == OutputBufferEnd) + { + //if (write(OutputFd, OutputBuffer, OUTPUT_BUFFER_SIZE) != OUTPUT_BUFFER_SIZE) + if(audioDecoder->WriteClip(OutputBuffer, OUTPUT_BUFFER_SIZE) != OUTPUT_BUFFER_SIZE) + { + fprintf(stderr,"%s: PCM write error in mono (%s).\n", ProgName, strerror(errno)); + Status = WRITE_ERR; + break; + } + + OutputPtr = OutputBuffer; +#ifdef SPECTRUM + if(g_settings.spectrum) { + i = 0; + j = 0; + update_lcd = true; + +//gettimeofday (&tv2, NULL); +//int ms = (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000; +//if(ms)printf("Write takes %dms\n", ms); +#if HAVE_DBOX2 + if(scnt >= SPECTRUM_CNT) +#endif + { + sanalyzer_render_freq(data); + } + } +#endif + } + } + } + } + + // if a custom value was set we only jump once + if ((*state==FF || *state==REV) && secondsToJump != 0 && !jumpDone) { + jumpDone=true; + *state=PLAY; + } + + }while(*state!=STOP_REQ); + + /* Mad is no longer used, the structures that were initialized must + * now be cleared. + */ + mad_synth_finish(&Synth); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); + + /* If the output buffer is not empty and no error occured during + * the last write, then flush it. + */ + if(OutputPtr!=OutputBuffer && Status!=WRITE_ERR) + { + ssize_t BufferSize=OutputPtr-OutputBuffer; + + //if(write(OutputFd, OutputBuffer, BufferSize)!=BufferSize) + if(audioDecoder->WriteClip(OutputBuffer, BufferSize) != BufferSize) + { + fprintf(stderr,"%s: PCM write error at the end (%s).\n", ProgName,strerror(errno)); + Status=WRITE_ERR; + } + } + audioDecoder->StopClip(); + CVFD::getInstance ()->Unlock (); + /* Accounting report if no error occured. */ + if(Status==OK) + { + /* The duration timer is converted to a human readable string + * with the versatile but still constrained mad_timer_string() + * function, in a fashion not unlike strftime(). The main + * difference is that the timer is break-down into several + * values according some of it's arguments. The units and + * fracunits arguments specify the intended conversion to be + * executed. + * + * The conversion unit (MAD_UNIT_MINUTES in our example) also + * specify the order and kind of conversion specifications + * that can be used in the format string. + * + * It is best to examine mad's timer.c source-code for details + * of the available units, fraction of units, their meanings, + * the format arguments, etc. + */ + // mad_timer_string(Timer,m_timePlayed,"%lu:%02lu", + // MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0); + *time_played = Timer.seconds; + +// fprintf(stderr,"%s: %lu frames decoded (%s).\n", +// ProgName,FrameCount,Buffer); + } + + /* That's the end of the world (in the H. G. Wells way). */ + return Status; +} + +CMP3Dec* CMP3Dec::getInstance() +{ + static CMP3Dec* MP3Dec = NULL; + if(MP3Dec == NULL) + { + MP3Dec = new CMP3Dec(); + } + return MP3Dec; +} + +bool CMP3Dec::GetMetaData(FILE* in, const bool nice, CAudioMetaData* const m) +{ + bool res; + if ( in && m ) + { + res = GetMP3Info(in, nice, m); + GetID3(in, m); + } + else + { + res = false; + } + + return res; +} + +/* + * Scans MP3 header for Xing, Info and Lame tag. Returns -1 on failure, + * >= 0 on success. The returned value specifies the location of the + * first audio frame. + * + * @author Christian Schlotter + * @date 2004 + * + * Based on scan_header() from Robert Leslie's "MAD Plug-in for Winamp". + */ +#define BUFFER_SIZE (5*8192) // big enough to skip id3 tags containing jpegs +long CMP3Dec::scanHeader( FILE* input, struct mad_header* const header, + struct tag* const ftag, const bool nice ) +{ + struct mad_stream stream; + struct mad_frame frame; + unsigned char buffer[BUFFER_SIZE]; + unsigned int buflen = 0; + int count = 0; + short refillCount = 4; /* buffer may be refilled refillCount times */ + long filePos = 0; /* return value */ + + mad_stream_init( &stream ); + mad_frame_init( &frame ); + + if ( ftag ) + tag_init( ftag ); + + while ( true ) + { + if ( buflen < sizeof(buffer) ) + { + if ( nice ) + usleep( 15000 ); + + filePos = ftell( input ); /* remember where reading started */ + if ( filePos == -1 ) + { + perror( "ftell()" ); + } + + /* fill buffer */ + int readbytes = fread( buffer+buflen, 1, sizeof(buffer)-buflen, + input ); + if ( readbytes <= 0 ) + { + if ( readbytes == -1 ) + filePos = -1; + break; + } + + buflen += readbytes; + } + + mad_stream_buffer( &stream, buffer, buflen ); + + while ( true ) + { + const unsigned char* const actualFrame = stream.this_frame; + if ( mad_frame_decode( &frame, &stream ) == -1 ) + { + if ( !MAD_RECOVERABLE( stream.error ) ) + break; + + /* check if id3 tag is in the way */ + long tagsize = + id3_tag_query( stream.this_frame, + stream.bufend - stream.this_frame ); + + if ( tagsize > 0 ) /* id3 tag recognized */ + { + mad_stream_skip( &stream, tagsize ); + continue; + } + else if ( mad_stream_sync( &stream ) != -1 ) /* try to sync */ + { + continue; + } + else /* syncing attempt failed */ + { + /* we have to set some limit here, otherwise we would scan + junk files completely */ + if ( refillCount-- ) + { + stream.error = MAD_ERROR_BUFLEN; + } + break; + } + } + + if ( count++ || ( ftag && tag_parse(ftag, &stream) == -1 ) ) + { + filePos += actualFrame - buffer; /* start of audio data */ + break; + } + } + + if ( count || stream.error != MAD_ERROR_BUFLEN ) + break; + + if ( refillCount-- ) + { + memmove( buffer, stream.next_frame, + buflen = &buffer[buflen] - stream.next_frame ); + } + else + { + break; + } + } + + if ( count ) + { + if ( header ) + { + *header = frame.header; + } + } + else + { + filePos = -1; + } + + mad_frame_finish( &frame ); + mad_stream_finish( &stream ); + + return filePos; +} + +/* + * Function retrieves MP3 metadata. Returns false on failure, true on success. + * + * Inspired by get_fileinfo() from Robert Leslie's "MAD Plug-in for Winamp" and + * decode_filter() from Robert Leslie's "madplay". + */ +bool CMP3Dec::GetMP3Info( FILE* input, const bool nice, + CAudioMetaData* const meta ) +{ + struct mad_header header; + struct tag ftag; + mad_header_init( &header ); + tag_init( &ftag ); + bool result = true; + + if ( ( meta->audio_start_pos = scanHeader(input, &header, &ftag, nice) ) + != -1 ) + { + meta->type = CAudioMetaData::MP3; + meta->bitrate = header.bitrate; + meta->layer = header.layer; + meta->mode = header.mode; + meta->samplerate = header.samplerate; + + if ( fseek( input, 0, SEEK_END ) ) + { + perror( "fseek()" ); + result = false; + } + /* this is still not 100% accurate, because it does not take + id3 tags at the end of the file in account */ + meta->filesize = ( ftell( input ) - meta->audio_start_pos ) * 8; + + /* valid Xing vbr tag present? */ + if ( ( ftag.flags & TAG_XING ) && + ( ftag.xing.flags & TAG_XING_FRAMES ) ) + { + meta->hasInfoOrXingTag = true; + mad_timer_t timer = header.duration; + mad_timer_multiply( &timer, ftag.xing.frames ); + + meta->total_time = mad_timer_count( timer, MAD_UNITS_SECONDS ); + } + else /* no valid Xing vbr tag present */ + { + meta->total_time = header.bitrate != 0 + ? meta->filesize / header.bitrate : 0; + } + + /* vbr file */ + if ( ftag.flags & TAG_VBR ) + { + meta->vbr = true; + meta->avg_bitrate = meta->total_time != 0 + ? static_cast( round( static_cast(meta->filesize) + / meta->total_time ) ) + : 0; + } + else /* we do not know wether the file is vbr or not */ + { + meta->vbr = false; + meta->avg_bitrate = header.bitrate; + + } + } + else /* scanning failed */ + { + result = false; + } + + if ( !result ) + { + meta->clear(); + } + + return result; +} + + +//------------------------------------------------------------------------ +void CMP3Dec::GetID3(FILE* in, CAudioMetaData* const m) +{ + unsigned int i; + struct id3_frame const *frame; + id3_ucs4_t const *ucs4; + id3_utf8_t *utf8; + char const spaces[] = " "; + + struct + { + char const *id; + char const *name; + } const info[] = + { + { ID3_FRAME_TITLE, "Title"}, + { "TIT3", 0}, /* Subtitle */ + { "TCOP", 0,}, /* Copyright */ + { "TPRO", 0,}, /* Produced */ + { "TCOM", "Composer"}, + { ID3_FRAME_ARTIST, "Artist"}, + { "TPE2", "Orchestra"}, + { "TPE3", "Conductor"}, + { "TEXT", "Lyricist"}, + { ID3_FRAME_ALBUM, "Album"}, + { ID3_FRAME_YEAR, "Year"}, + { ID3_FRAME_TRACK, "Track"}, + { "TPUB", "Publisher"}, + { ID3_FRAME_GENRE, "Genre"}, + { "TRSN", "Station"}, + { "TENC", "Encoder"} + }; + + /* text information */ + + struct id3_file *id3file = id3_file_fdopen(fileno(in), ID3_FILE_MODE_READONLY); + if(id3file == 0) + printf("error open id3 file\n"); + else + { + id3_tag *tag=id3_file_tag(id3file); + if(tag) + { + for(i = 0; i < sizeof(info) / sizeof(info[0]); ++i) + { + union id3_field const *field; + unsigned int nstrings, namelen, j; + char const *name; + + frame = id3_tag_findframe(tag, info[i].id, 0); + if(frame == 0) + continue; + + field = &frame->fields[1]; + nstrings = id3_field_getnstrings(field); + + name = info[i].name; + namelen = name ? strlen(name) : 0; + assert(namelen < sizeof(spaces)); + + for(j = 0; j < nstrings; ++j) + { + ucs4 = id3_field_getstrings(field, j); + assert(ucs4); + + if(strcmp(info[i].id, ID3_FRAME_GENRE) == 0) + ucs4 = id3_genre_name(ucs4); + + utf8 = id3_ucs4_utf8duplicate(ucs4); + if (utf8 == NULL) + goto fail; + + if (j == 0 && name) + { + if(strcmp(name,"Title") == 0) + m->title = (char *) utf8; + if(strcmp(name,"Artist") == 0) + m->artist = (char *) utf8; + if(strcmp(name,"Year") == 0) + m->date = (char *) utf8; + if(strcmp(name,"Album") == 0) + m->album = (char *) utf8; + if(strcmp(name,"Genre") == 0) + m->genre = (char *) utf8; + } + else + { + if(strcmp(info[i].id, "TCOP") == 0 || strcmp(info[i].id, "TPRO") == 0) + { + //printf("%s %s %s\n", spaces, (info[i].id[1] == 'C') ? ("Copyright (C)") : ("Produced (P)"), latin1); + } + //else + //printf("%s %s\n", spaces, latin1); + } + + free(utf8); + } + } + +#ifdef INCLUDE_UNUSED_STUFF + /* comments */ + + i = 0; + while((frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, i++))) + { + id3_utf8_t *ptr, *newline; + int first = 1; + + ucs4 = id3_field_getstring(&frame->fields[2]); + assert(ucs4); + + if(*ucs4) + continue; + + ucs4 = id3_field_getfullstring(&frame->fields[3]); + assert(ucs4); + + utf8 = id3_ucs4_utf8duplicate(ucs4); + if (utf8 == 0) + goto fail; + + ptr = utf8; + while(*ptr) + { + newline = (id3_utf8_t *) strchr((char*)ptr, '\n'); + if(newline) + *newline = 0; + + if(strlen((char *)ptr) > 66) + { + id3_utf8_t *linebreak; + + linebreak = ptr + 66; + + while(linebreak > ptr && *linebreak != ' ') + --linebreak; + + if(*linebreak == ' ') + { + if(newline) + *newline = '\n'; + + newline = linebreak; + *newline = 0; + } + } + + if(first) + { + char const *name; + unsigned int namelen; + + name = "Comment"; + namelen = strlen(name); + assert(namelen < sizeof(spaces)); + mp3->Comment = (char *) ptr; + //printf("%s%s: %s\n", &spaces[namelen], name, ptr); + first = 0; + } + else + //printf("%s %s\n", spaces, ptr); + + ptr += strlen((char *) ptr) + (newline ? 1 : 0); + } + + free(utf8); + break; + } +#endif + id3_tag_delete(tag); + } + else + printf("error open id3 tag\n"); + + id3_finish_file(id3file); + } + if(0) + { + fail: + printf("id3: not enough memory to display tag\n"); + } +} + +// this is a copy of static libid3tag function "finish_file" +// which cannot be called from outside +void id3_finish_file(struct id3_file* file) +{ + unsigned int i; + + if (file->path) + free(file->path); + + if (file->primary) { + id3_tag_delref(file->primary); + id3_tag_delete(file->primary); + } + + for (i = 0; i < file->ntags; ++i) { + struct id3_tag *tag; + + tag = file->tags[i].tag; + if (tag) { + id3_tag_delref(tag); + id3_tag_delete(tag); + } + } + + if (file->tags) + free(file->tags); + + free(file); +} + diff --git a/src/driver/audiodec/mp3dec.h b/src/driver/audiodec/mp3dec.h new file mode 100644 index 000000000..b18924a9d --- /dev/null +++ b/src/driver/audiodec/mp3dec.h @@ -0,0 +1,81 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2002 Bjoern Kalkbrenner + Copyright (C) 2002,2003 Dirch + Copyright (C) 2002,2003,2004 Zwen + + libmad MP3 low-level core + Homepage: http://www.dbox2.info/ + + Kommentar: + + based on + ************************************ + *** madlld -- Mad low-level *** v 1.0p1, 2002-01-08 + *** demonstration/decoder *** (c) 2001, 2002 Bertrand Petit + ************************************ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __MP3_DEC__ +#define __MP3_DEC__ + +#include +#include +#include +#include +#include +#include +#include +extern "C" +{ +#include "driver/audiodec/tag.h" +} + +class CMP3Dec : public CBaseDec +{ +private: +#if !((MAD_VERSION_MAJOR>=1) || \ + ((MAD_VERSION_MAJOR==0) && \ + (((MAD_VERSION_MINOR==14) && \ + (MAD_VERSION_PATCH>=2)) || \ + (MAD_VERSION_MINOR>14)))) + const char* MadErrorString(const struct mad_stream *Stream); +#endif + signed short MadFixedToSShort(const mad_fixed_t Fixed, bool left = false); + void CreateInfo(CAudioMetaData* const, const int); + bool GetMP3Info(FILE*, const bool, CAudioMetaData* const); + void GetID3(FILE*, CAudioMetaData* const); + long scanHeader( FILE*, struct mad_header* const, struct tag* const, + const bool ); + +public: + static CMP3Dec* getInstance(); + virtual RetCode Decoder(FILE *InputFp, const int OutputFd, + State* const state, CAudioMetaData* m, + time_t* const t, unsigned int* const secondsToSkip); + bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* const m); + CMP3Dec(){}; + +}; + + +#endif + diff --git a/src/driver/audiodec/oggdec.cpp b/src/driver/audiodec/oggdec.cpp new file mode 100644 index 000000000..6757a7749 --- /dev/null +++ b/src/driver/audiodec/oggdec.cpp @@ -0,0 +1,348 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 Sania, Zwen + + Homepage: http://www.dbox2.info/ + + Kommentar: + + ogg vorbis audio decoder + uses tremor libvorbisidec + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +extern cAudio * audioDecoder; + +#define ProgName "OggDec" +// nr of msecs to skip in ff/rev mode +#define MSECS_TO_SKIP 3000 +// nr of msecs to play in ff/rev mode +#define MSECS_TO_PLAY 200 + +/* at first, define our own callback functions used in */ +/* tremor to access the data. These functions are simple mappers */ +size_t ogg_read(void *buf, size_t size, size_t nmemb, void *data) +{ + return fread(buf, size, nmemb, (FILE*)data); +} + +int ogg_seek(void *data, ogg_int64_t offset, int whence) +{ + return fseek((FILE*)data, (long)offset, whence); +} + +int ogg_close(void *data) +{ + return 0; +} + +long ogg_tell(void *data) +{ + return ftell((FILE*)data); +} + +#define PCMBUFFER 4096 //4096 max for libtremor +#define MAX_OUTPUT_SAMPLES 2048 /* AVIA_GT_PCM_MAX_SAMPLES-1 */ + +CBaseDec::RetCode COggDec::Decoder(FILE *in, const int OutputFd, State* const state, CAudioMetaData* meta_data, time_t* const time_played, unsigned int* const secondsToSkip) +{ + OggVorbis_File vf; + int bitstream, rval; + ogg_int64_t jumptime=0; + Status=OK; + mOutputFd = OutputFd; + mState = state; + mTimePlayed=time_played; + + if (!Open(in, &vf)) + { + Status=DATA_ERR; + return Status; + } + + SetMetaData(&vf, meta_data); + + audioDecoder->PrepareClipPlay(ov_info(&vf,0)->channels, ov_info(&vf,0)->rate, 16, 1); + + /* up and away ... */ + mSlotSize = MAX_OUTPUT_SAMPLES * 2 * ov_info(&vf,0)->channels; + for(int i = 0 ; i < DECODE_SLOTS ; i++) + { + if ((mPcmSlots[i] = (char*) malloc(mSlotSize)) == NULL) + { + for (int j = i - 1; j >= 0; j--) + { + free(mPcmSlots[j]); + } + Status=INTERNAL_ERR; + return Status; + } + mSlotTime[i]=0; + } + mReadSlot=mWriteSlot=0; + + pthread_t OutputThread; + if (pthread_create (&OutputThread, 0, OutputDsp, (void *) this) != 0 ) + { + for(int i = 0 ; i < DECODE_SLOTS ; i++) + free(mPcmSlots[i]); + Status=INTERNAL_ERR; + return Status; + } + + int bytes; + State oldstate=*state; + do + { + // clear buffer on state change + if(oldstate!=*state) + { + if(*state!=PAUSE && (*state!=PLAY || oldstate!=PAUSE)) + { + mWriteSlot=mReadSlot=0; + oldstate=*state; + } + } + while((mWriteSlot+1)%DECODE_SLOTS == mReadSlot) + { + usleep(10000); + } + bytes=0; + if(mSeekable) +#ifdef DBOX + mSlotTime[mWriteSlot] = ov_time_tell(&vf); +#else + mSlotTime[mWriteSlot] = (ogg_int64_t)(1000 * ov_time_tell(&vf)); +#endif + do + { +#ifdef DBOX + rval = ov_read(&vf, mPcmSlots[mWriteSlot]+bytes, mSlotSize-bytes, &bitstream); +#else + rval = ov_read(&vf, mPcmSlots[mWriteSlot]+bytes, mSlotSize-bytes, 0, 2, 1, &bitstream); +#endif + bytes+=rval; +//printf("Ogg: read buf 0x%x size %d / %d done %d\n", mPcmSlots[mWriteSlot]+bytes, rval, mSlotSize, bytes); + } while (rval > 0 && bytes !=mSlotSize); +//printf("\n"); + int actMSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip * 1000 : MSECS_TO_SKIP; + if((*state==FF || *state==REV) && mSeekable ) + { + if((std::abs((long int)( mSlotTime[mWriteSlot]-jumptime))) > MSECS_TO_PLAY) + { + if(*state==FF) + { + ov_time_seek_page(&vf, mSlotTime[mWriteSlot] + actMSecsToSkip); + jumptime=mSlotTime[mWriteSlot]+actMSecsToSkip; + } + else + { + if(mSlotTime[mWriteSlot] < actMSecsToSkip) + { + ov_time_seek(&vf, 0); + *state=PLAY; + } + else + { + ov_time_seek_page(&vf, mSlotTime[mWriteSlot] - actMSecsToSkip); + jumptime=mSlotTime[mWriteSlot]-actMSecsToSkip; + } + } + } + if (*secondsToSkip != 0) { + *state=PLAY; + } + } + if(bytes == mSlotSize) + mWriteSlot=(mWriteSlot+1) % DECODE_SLOTS; + } while (rval != 0 && *state!=STOP_REQ && Status==OK); + + //printf("COggDec::Decoder: read loop stop, rval %d state %d status %d\n", rval, *state, Status); + // let buffer run dry + while(rval==0 && *state!=STOP_REQ && Status==OK && mReadSlot != mWriteSlot) + usleep(100000); + + //pthread_cancel(OutputThread); + //printf("COggDec::Decoder: OutputThread join\n"); + Status = WRITE_ERR; + pthread_join(OutputThread, NULL); + //printf("COggDec::Decoder: OutputThread join done\n"); + audioDecoder->StopClip(); + for(int i = 0 ; i < DECODE_SLOTS ; i++) + free(mPcmSlots[i]); + + /* clean up the junk from the party */ + ov_clear(&vf); + + /* and drive home ;) */ + return Status; +} + +void* COggDec::OutputDsp(void * arg) +{ + COggDec* dec = (COggDec*) arg; + while(dec->Status == OK/*true*/) + { + while(dec->mReadSlot==dec->mWriteSlot || *dec->mState==PAUSE) + { + if(dec->Status != OK) + return NULL; + usleep(10000); + } + //if (write(dec->mOutputFd, dec->mPcmSlots[dec->mReadSlot], dec->mSlotSize) != dec->mSlotSize) + if (audioDecoder->WriteClip((unsigned char *)dec->mPcmSlots[dec->mReadSlot], dec->mSlotSize) != dec->mSlotSize) + { + fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno)); + dec->Status=WRITE_ERR; + break; + } + *dec->mTimePlayed = (int)(dec->mSlotTime[dec->mReadSlot]/1000); + dec->mReadSlot=(dec->mReadSlot+1)%DECODE_SLOTS; + } + return NULL; +} + +bool COggDec::GetMetaData(FILE *in, const bool nice, CAudioMetaData* m) +{ + OggVorbis_File vf; + if (!Open(in, &vf)) + { + return false; + } + SetMetaData(&vf, m); + ov_clear(&vf); + return true; +} + +COggDec* COggDec::getInstance() +{ + static COggDec* OggDec = NULL; + if(OggDec == NULL) + { + OggDec = new COggDec(); + } + return OggDec; +} + +void COggDec::ParseUserComments(vorbis_comment* vc, CAudioMetaData* m) +{ + for(int i=0; i < vc->comments ; i++) + { + char* search; + if((search=strstr(vc->user_comments[i],"Artist"))!=NULL || + (search=strstr(vc->user_comments[i],"ARTIST"))!=NULL) + m->artist = search+7; + else if((search=strstr(vc->user_comments[i],"Album"))!=NULL || + (search=strstr(vc->user_comments[i],"ALBUM"))!=NULL) + m->album = search+6; + else if((search=strstr(vc->user_comments[i],"Title"))!=NULL || + (search=strstr(vc->user_comments[i],"TITLE"))!=NULL) + m->title = search+6; + else if((search=strstr(vc->user_comments[i],"Genre"))!=NULL || + (search=strstr(vc->user_comments[i],"GENRE"))!=NULL) + m->genre = search+6; + else if((search=strstr(vc->user_comments[i],"Date"))!=NULL || + (search=strstr(vc->user_comments[i],"DATE"))!=NULL) + m->date = search+5; + else if((search=strstr(vc->user_comments[i],"TrackNumber"))!=NULL || + (search=strstr(vc->user_comments[i],"TRACKNUMBER"))!=NULL) + m->track = search+12; + } +} + + +void COggDec::SetMetaData(OggVorbis_File* vf, CAudioMetaData* m) +{ + /* Set Metadata */ + m->type = CAudioMetaData::OGG; + m->bitrate = ov_info(vf,0)->bitrate_nominal; + m->samplerate = ov_info(vf,0)->rate; + if(mSeekable) +#ifdef DBOX + m->total_time = (time_t) ov_time_total(vf, 0) / 1000; +#else + m->total_time = (time_t) ov_time_total(vf, 0); +#endif + std::stringstream ss; + ss << "OGG V." << ov_info(vf,0)->version << " / " << ov_info(vf,0)->channels << "channel(s)"; + m->type_info = ss.str(); + ParseUserComments(ov_comment(vf, 0), m); + m->changed=true; +} + +bool COggDec::Open(FILE* in, OggVorbis_File* vf) +{ + int rval; + ov_callbacks cb; + /* we need to use our own functions, because we have */ + /* the netfile layer hooked in here. If we would not */ + /* provide callbacks, the tremor lib and the netfile */ + /* layer would clash and steal each other the data */ + /* from the stream ! */ + + cb.read_func = ogg_read; + cb.seek_func = ogg_seek; + cb.close_func = ogg_close; + cb.tell_func = ogg_tell; + + /* test the dope ... */ + //rval = ov_test_callbacks((void*)in, vf, NULL, 0, cb); + rval = ov_open_callbacks(in, vf, NULL, 0, cb); + + /* and tell our friends about the quality of the stuff */ + // initialize the sound device here + + if(rval<0) + { + switch(rval) + { + /* err_txt from netfile.cpp */ + case OV_EREAD: sprintf(err_txt, "media read error"); break; + case OV_ENOTVORBIS: sprintf(err_txt, "no vorbis stream"); break; + case OV_EVERSION: sprintf(err_txt, "incompatible vorbis version"); break; + case OV_EBADHEADER: sprintf(err_txt, "invalid bvorbis bitstream header"); break; + case OV_EFAULT: sprintf(err_txt, "internal logic fault (tremor)"); break; + default: sprintf(err_txt, "unknown error, code: %d", rval); + } + fprintf(stderr,"%s: %s\n", ProgName, err_txt); + return false; + } + + /* finish the opening and ignite the joint */ + //ov_test_open(vf); + + if(ov_seekable(vf)) + mSeekable = true; + else + mSeekable = false; + + return true; +} diff --git a/src/driver/audiodec/oggdec.h b/src/driver/audiodec/oggdec.h new file mode 100644 index 000000000..2860b4b7e --- /dev/null +++ b/src/driver/audiodec/oggdec.h @@ -0,0 +1,78 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2002,2003,2004 Sania,Zwen + + ogg vorbis audio decoder + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __OGG_DEC__ +#define __OGG_DEC__ + +//#define DBOX + +#include +#include +#include +#include +#include +#include +#include +#ifdef DBOX +#include +#include +#else +#include +#include +#endif + +#define DECODE_SLOTS 30 + +class COggDec : public CBaseDec +{ + +public: + static COggDec* getInstance(); + virtual RetCode Decoder(FILE *, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const); + bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m); + COggDec(){}; +private: + void ParseUserComments(vorbis_comment*, CAudioMetaData*); + bool Open(FILE* , OggVorbis_File*); + void SetMetaData(OggVorbis_File*, CAudioMetaData*); + RetCode Status; + char* mPcmSlots[DECODE_SLOTS]; + ogg_int64_t mSlotTime[DECODE_SLOTS]; + int mWriteSlot; + int mReadSlot; + int mSlotSize; + int mOutputFd; + State* mState; + bool mSeekable; + time_t* mTimePlayed; + static void* OutputDsp(void *); +}; + + +#endif + diff --git a/src/driver/audiodec/tag.c b/src/driver/audiodec/tag.c new file mode 100644 index 000000000..1aeefd20a --- /dev/null +++ b/src/driver/audiodec/tag.c @@ -0,0 +1,331 @@ +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: tag.c,v 1.1 2004/08/28 14:11:58 rasc Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +//# include "global.h" + +# include + +#ifdef INCLUDE_UNUSED_STUFF +# include +#endif /* INCLUDE_UNUSED_STUFF */ +# include + +# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g') +# define INFO_MAGIC (('I' << 24) | ('n' << 16) | ('f' << 8) | 'o') +# define LAME_MAGIC (('L' << 24) | ('A' << 16) | ('M' << 8) | 'E') + +/* + * NAME: tag->init() + * DESCRIPTION: initialize tag structure + */ +void tag_init(struct tag *tag) +{ + tag->flags = 0; +#ifdef INCLUDE_UNUSED_STUFF + tag->encoder[0] = 0; +#endif /* INCLUDE_UNUSED_STUFF */ +} + +/* + * NAME: parse_xing() + * DESCRIPTION: parse a Xing VBR tag + */ +static +int parse_xing(struct tag_xing *xing, + struct mad_bitptr *ptr, unsigned int *bitlen) +{ + if (*bitlen < 32) + goto fail; + + xing->flags = mad_bit_read(ptr, 32); + *bitlen -= 32; + + if (xing->flags & TAG_XING_FRAMES) { + if (*bitlen < 32) + goto fail; + + xing->frames = mad_bit_read(ptr, 32); + *bitlen -= 32; + } + + if (xing->flags & TAG_XING_BYTES) { + if (*bitlen < 32) + goto fail; + + xing->bytes = mad_bit_read(ptr, 32); + *bitlen -= 32; + } + + if (xing->flags & TAG_XING_TOC) { + int i; + + if (*bitlen < 800) + goto fail; + + for (i = 0; i < 100; ++i) + xing->toc[i] = mad_bit_read(ptr, 8); + + *bitlen -= 800; + } + + if (xing->flags & TAG_XING_SCALE) { + if (*bitlen < 32) + goto fail; + + xing->scale = mad_bit_read(ptr, 32); + *bitlen -= 32; + } + + return 0; + + fail: + xing->flags = 0; + return -1; +} + +#ifdef INCLUDE_UNUSED_STUFF +/* + * NAME: tag->parse_rgain() + * DESCRIPTION: parse a 16-bit Replay Gain field + */ +void tag_parse_rgain(struct tag_rgain *rgain, struct mad_bitptr *ptr) +{ + int negative; + + rgain->name = mad_bit_read(ptr, 3); + rgain->originator = mad_bit_read(ptr, 3); + + negative = mad_bit_read(ptr, 1); + rgain->adjustment = mad_bit_read(ptr, 9); + + if (negative) + rgain->adjustment = -rgain->adjustment; +} + +/* + * NAME: parse_lame() + * DESCRIPTION: parse a LAME tag + */ +static +int parse_lame(struct tag_lame *lame, + struct mad_bitptr *ptr, unsigned int *bitlen, + unsigned short crc) +{ + struct mad_bitptr save = *ptr; + + if (*bitlen < 36 * 8) + goto fail; + + /* bytes $9A-$A4: Encoder short VersionString */ + + mad_bit_skip(ptr, 9 * 8); + + /* byte $A5: Info Tag revision + VBR method */ + + lame->revision = mad_bit_read(ptr, 4); + if (lame->revision == 15) + goto fail; + + lame->vbr_method = mad_bit_read(ptr, 4); + + /* byte $A6: Lowpass filter value (Hz) */ + + lame->lowpass_filter = mad_bit_read(ptr, 8) * 100; + + /* bytes $A7-$AA: 32 bit "Peak signal amplitude" */ + + lame->peak = mad_bit_read(ptr, 32) << 5; + + /* bytes $AB-$AC: 16 bit "Radio Replay Gain" */ + + tag_parse_rgain(&lame->replay_gain[0], ptr); + + /* bytes $AD-$AE: 16 bit "Audiophile Replay Gain" */ + + tag_parse_rgain(&lame->replay_gain[1], ptr); + + /* byte $AF: Encoding flags + ATH Type */ + + lame->flags = mad_bit_read(ptr, 4); + lame->ath_type = mad_bit_read(ptr, 4); + + /* byte $B0: if ABR {specified bitrate} else {minimal bitrate} */ + + lame->bitrate = mad_bit_read(ptr, 8); + + /* bytes $B1-$B3: Encoder delays */ + + lame->start_delay = mad_bit_read(ptr, 12); + lame->end_padding = mad_bit_read(ptr, 12); + + /* byte $B4: Misc */ + + lame->source_samplerate = mad_bit_read(ptr, 2); + + if (mad_bit_read(ptr, 1)) + lame->flags |= TAG_LAME_UNWISE; + + lame->stereo_mode = mad_bit_read(ptr, 3); + lame->noise_shaping = mad_bit_read(ptr, 2); + + /* byte $B5: MP3 Gain */ + + lame->gain = mad_bit_read(ptr, 8); + + /* bytes $B6-B7: Preset and surround info */ + + mad_bit_skip(ptr, 2); + + lame->surround = mad_bit_read(ptr, 3); + lame->preset = mad_bit_read(ptr, 11); + + /* bytes $B8-$BB: MusicLength */ + + lame->music_length = mad_bit_read(ptr, 32); + + /* bytes $BC-$BD: MusicCRC */ + + lame->music_crc = mad_bit_read(ptr, 16); + + /* bytes $BE-$BF: CRC-16 of Info Tag */ + + if (mad_bit_read(ptr, 16) != crc) + goto fail; + + *bitlen -= 36 * 8; + + return 0; + + fail: + *ptr = save; + return -1; +} +#endif /* INCLUDE_UNUSED_STUFF */ + +/* + * NAME: tag->parse() + * DESCRIPTION: parse Xing/LAME tag(s) + */ +int tag_parse(struct tag *tag, struct mad_stream const *stream) +{ + struct mad_bitptr ptr = stream->anc_ptr; + struct mad_bitptr start = ptr; + unsigned int bitlen = stream->anc_bitlen; + unsigned long magic; +#ifdef INCLUDE_UNUSED_STUFF + int i; +#endif /* INCLUDE_UNUSED_STUFF */ + + if (bitlen < 32) + return -1; + + magic = mad_bit_read(&ptr, 32); + bitlen -= 32; + + if (magic != XING_MAGIC + && magic != INFO_MAGIC + && magic != LAME_MAGIC) + { + /* + * Due to an unfortunate historical accident, a Xing VBR tag may be + * misplaced in a stream with CRC protection. We check for this by + * assuming the tag began two octets prior and the high bits of the + * following flags field are always zero. + */ + + if (magic != ((XING_MAGIC << 16) & 0xffffffffL) + && magic != ((INFO_MAGIC << 16) & 0xffffffffL)) + return -1; + + magic >>= 16; + + /* backtrack the bit pointer */ + + ptr = start; + mad_bit_skip(&ptr, 16); + bitlen += 16; + } + + if ((magic & 0x0000ffffL) == (XING_MAGIC & 0x0000ffffL)) + tag->flags |= TAG_VBR; + + /* Xing tag */ + + if (magic == LAME_MAGIC) { + ptr = start; + bitlen += 32; + } + else if (parse_xing(&tag->xing, &ptr, &bitlen) == 0) + tag->flags |= TAG_XING; + +#ifdef INCLUDE_UNUSED_STUFF + /* encoder string */ + + if (bitlen >= 20 * 8) { + start = ptr; + + for (i = 0; i < 20; ++i) { + tag->encoder[i] = mad_bit_read(&ptr, 8); + + if (tag->encoder[i] == 0) + break; + + /* keep only printable ASCII chars */ + + if (tag->encoder[i] < 0x20 || tag->encoder[i] >= 0x7f) { + tag->encoder[i] = 0; + break; + } + } + + tag->encoder[20] = 0; + ptr = start; + } + + /* LAME tag */ + + if (stream->next_frame - stream->this_frame >= 192 && + parse_lame(&tag->lame, &ptr, &bitlen, + crc_compute(stream->this_frame, 190, 0x0000)) == 0) { + tag->flags |= TAG_LAME; + tag->encoder[9] = 0; + } + else { + for (i = 0; i < 20; ++i) { + if (tag->encoder[i] == 0) + break; + + /* stop at padding chars */ + + if (tag->encoder[i] == 0x55) { + tag->encoder[i] = 0; + break; + } + } + } +#endif /* INCLUDE_UNUSED_STUFF */ + + return 0; +} diff --git a/src/driver/audiodec/tag.h b/src/driver/audiodec/tag.h new file mode 100644 index 000000000..6dc05ceea --- /dev/null +++ b/src/driver/audiodec/tag.h @@ -0,0 +1,183 @@ +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: tag.h,v 1.1 2004/08/28 14:11:58 rasc Exp $ + */ + +# ifndef __TAG_H__ +# define __TAG_H__ + +# include + +enum { + TAG_XING = 0x0001, + TAG_LAME = 0x0002, + TAG_VBR = 0x0100 +}; + +enum { + TAG_XING_FRAMES = 0x00000001L, + TAG_XING_BYTES = 0x00000002L, + TAG_XING_TOC = 0x00000004L, + TAG_XING_SCALE = 0x00000008L +}; + +#ifdef INCLUDE_UNUSED_STUFF +enum { + TAG_LAME_NSPSYTUNE = 0x01, + TAG_LAME_NSSAFEJOINT = 0x02, + TAG_LAME_NOGAP_NEXT = 0x04, + TAG_LAME_NOGAP_PREV = 0x08, + TAG_LAME_UNWISE = 0x10 +}; + +enum tag_lame_vbr { + TAG_LAME_VBR_CONSTANT = 1, + TAG_LAME_VBR_ABR = 2, + TAG_LAME_VBR_METHOD1 = 3, + TAG_LAME_VBR_METHOD2 = 4, + TAG_LAME_VBR_METHOD3 = 5, + TAG_LAME_VBR_METHOD4 = 6, + TAG_LAME_VBR_CONSTANT2PASS = 8, + TAG_LAME_VBR_ABR2PASS = 9 +}; + +enum tag_lame_source { + TAG_LAME_SOURCE_32LOWER = 0x00, + TAG_LAME_SOURCE_44_1 = 0x01, + TAG_LAME_SOURCE_48 = 0x02, + TAG_LAME_SOURCE_HIGHER48 = 0x03 +}; + +enum tag_lame_mode { + TAG_LAME_MODE_MONO = 0x00, + TAG_LAME_MODE_STEREO = 0x01, + TAG_LAME_MODE_DUAL = 0x02, + TAG_LAME_MODE_JOINT = 0x03, + TAG_LAME_MODE_FORCE = 0x04, + TAG_LAME_MODE_AUTO = 0x05, + TAG_LAME_MODE_INTENSITY = 0x06, + TAG_LAME_MODE_UNDEFINED = 0x07 +}; + +enum tag_lame_surround { + TAG_LAME_SURROUND_NONE = 0, + TAG_LAME_SURROUND_DPL = 1, + TAG_LAME_SURROUND_DPL2 = 2, + TAG_LAME_SURROUND_AMBISONIC = 3 +}; + +enum tag_lame_preset { + TAG_LAME_PRESET_NONE = 0, + TAG_LAME_PRESET_V9 = 410, + TAG_LAME_PRESET_V8 = 420, + TAG_LAME_PRESET_V7 = 430, + TAG_LAME_PRESET_V6 = 440, + TAG_LAME_PRESET_V5 = 450, + TAG_LAME_PRESET_V4 = 460, + TAG_LAME_PRESET_V3 = 470, + TAG_LAME_PRESET_V2 = 480, + TAG_LAME_PRESET_V1 = 490, + TAG_LAME_PRESET_V0 = 500, + TAG_LAME_PRESET_R3MIX = 1000, + TAG_LAME_PRESET_STANDARD = 1001, + TAG_LAME_PRESET_EXTREME = 1002, + TAG_LAME_PRESET_INSANE = 1003, + TAG_LAME_PRESET_STANDARD_FAST = 1004, + TAG_LAME_PRESET_EXTREME_FAST = 1005, + TAG_LAME_PRESET_MEDIUM = 1006, + TAG_LAME_PRESET_MEDIUM_FAST = 1007 +}; +#endif /* INCLUDE_UNUSED_STUFF */ + +struct tag_xing { + long flags; /* valid fields (see below) */ + unsigned long frames; /* total number of frames */ + unsigned long bytes; /* total number of bytes */ + unsigned char toc[100]; /* 100-point seek table */ + long scale; /* VBR quality indicator (0 best - 100 worst) */ +}; + +#ifdef INCLUDE_UNUSED_STUFF +enum tag_rgain_name { + TAG_RGAIN_NAME_NOT_SET = 0x0, + TAG_RGAIN_NAME_RADIO = 0x1, + TAG_RGAIN_NAME_AUDIOPHILE = 0x2 +}; + +enum tag_rgain_originator { + TAG_RGAIN_ORIGINATOR_UNSPECIFIED = 0x0, + TAG_RGAIN_ORIGINATOR_PRESET = 0x1, + TAG_RGAIN_ORIGINATOR_USER = 0x2, + TAG_RGAIN_ORIGINATOR_AUTOMATIC = 0x3 +}; + +struct tag_rgain { + enum tag_rgain_name name; + enum tag_rgain_originator originator; + signed short adjustment; +}; + +struct tag_lame { + unsigned char revision; + unsigned char flags; + + enum tag_lame_vbr vbr_method; + unsigned short lowpass_filter; + + mad_fixed_t peak; + struct tag_rgain replay_gain[2]; + + unsigned char ath_type; + unsigned char bitrate; + + unsigned short start_delay; + unsigned short end_padding; + + enum tag_lame_source source_samplerate; + enum tag_lame_mode stereo_mode; + unsigned char noise_shaping; + + signed char gain; + enum tag_lame_surround surround; + enum tag_lame_preset preset; + + unsigned long music_length; + unsigned short music_crc; +}; +#endif /* INCLUDE_UNUSED_STUFF */ + +struct tag { + int flags; + struct tag_xing xing; +#ifdef INCLUDE_UNUSED_STUFF + struct tag_lame lame; + char encoder[21]; +#endif /* INCLUDE_UNUSED_STUFF */ +}; + +void tag_init(struct tag *); + +# define tag_finish(tag) /* nothing */ + +int tag_parse(struct tag *, struct mad_stream const *); +#ifdef INCLUDE_UNUSED_STUFF +void tag_parse_rgain(struct tag_rgain *, struct mad_bitptr *); +#endif /* INCLUDE_UNUSED_STUFF */ + +# endif /* __TAG_H__ */ diff --git a/src/driver/audiodec/vis.cpp b/src/driver/audiodec/vis.cpp new file mode 100644 index 000000000..79c3600e6 --- /dev/null +++ b/src/driver/audiodec/vis.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "int_fft.c" +typedef signed short gint16; +typedef int gint; +typedef float gfloat; +typedef double gdouble; + +#define NUM_BANDS 16 +#define WIDTH (120/NUM_BANDS) +#define HEIGHT 64 +#define FALL 2 +#define FALLOFF 1 + +static int bar_heights[NUM_BANDS]; +static int falloffs[NUM_BANDS]; +static gdouble scale = 0; + +static gint xscale128[] = { 0, 1, 2, 3, 4, 6, 8, 9, 10, 14, 20, 27, 37, 50, 67, 94, 127 }; +static gint xscale256[] = { 0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 187, 255 }; +static gint xscale512[] = { 0, 2, 4, 6, 10, 14, 20, 28, 40, 56, 80, 108, 148, 202, 274, 374, 510 }; + +//#define DEBUG +#if HAVE_DBOX2 +#define SAMPLES 256 +#define LOG 8 +#define xscale xscale128 +#else +#define SAMPLES 512 +#define LOG 9 +#define xscale xscale256 +#endif + +static void do_fft(gint16 out_data[], gint16 in_data[]) +{ + gint16 im[SAMPLES]; + + memset(im, 0, sizeof(im)); + window(in_data, SAMPLES); + fix_fft(in_data, im, LOG, 0); + fix_loud(out_data, in_data, im, SAMPLES/2, 0); +} + +static int threshold = -60; +void sanalyzer_render_freq (gint16 in_data[]) +{ +#if 0 + gint i, c; + gint y; + gint16 freq_data[1024]; + static int fl = 0; + +#ifdef DEBUG +struct timeval tv1, tv2; +gettimeofday (&tv1, NULL); +#endif + do_fft (freq_data, in_data); + + CVFD::getInstance ()->Clear (); +#ifdef DEBUG +//gettimeofday (&tv1, NULL); +#endif + for (i = 0; i < NUM_BANDS; i++) { + y = 0; + +#if 0 // using max value + for (c = xscale[i]; c < xscale[i + 1]; c++) { + if(freq_data[c] > threshold) { + int val = freq_data[c]-threshold; + if (val > y) + y = val; + } + } +#else + int val = 0; + int cnt = 0; + for (c = xscale[i]; c < xscale[i + 1]; c++) { + if(freq_data[c] > threshold) { + val += freq_data[c]-threshold; + cnt++; + } + } + if(cnt) y = val/cnt; +#endif + + if (y > HEIGHT - 1) + y = HEIGHT - 1; + + if (y > bar_heights[i]) + bar_heights[i] = y; + else if (bar_heights[i] > FALL) + bar_heights[i] -= FALL; + else + bar_heights[i] = 0; + + if (y > falloffs[i]) + falloffs[i] = y; + else if (falloffs[i] > FALLOFF) { + fl ++; + if(fl > 2) { + fl = 0; + falloffs[i] -= FALLOFF; + } + } + else + falloffs[i] = 0; + CVFD::getInstance ()->drawBar (i*WIDTH, 64-falloffs[i]-5, WIDTH, 2); + y = bar_heights[i]; + CVFD::getInstance ()->drawBar (i*WIDTH, 64-y, WIDTH, y); + } + +#ifdef DEBUG +gettimeofday (&tv2, NULL); +printf("Calc takes %dns\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec)); +#endif + CVFD::getInstance ()->Update (); +#endif +} +#if 0 +void sanalyzer_render_vu (gint16 in_data[2][512]) +{ + long delay_value; + int a = 0,b = 0, c; + float max = 32767.50; + float db_min = (float) -91; + float channel_data[2] = { 0.0, 0.0}; + static int olda = 0, oldb = 0; +return; +#ifdef DEBUG +//struct timeval tv1, tv2; +//gettimeofday (&tv1, NULL); +#endif + for (c = 0; c < 512; c += 2) { + channel_data[0] += (float) (in_data[0][c] * in_data[0][c]); + channel_data[1] += (float) (in_data[1][c] * in_data[1][c]); + } + channel_data[0] = 20 * log10 (sqrt (channel_data[0] / 256.0) / max); + channel_data[1] = 20 * log10 (sqrt (channel_data[1] / 256.0) / max); + + a = channel_data[0] + 91; + b = channel_data[1] + 91; +//printf ("channel0: %d\n", a); +//printf ("channel1: %d\n", b); +#ifdef DEBUG +//gettimeofday (&tv2, NULL); +//printf("Calc takes %dms\n", (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000); +#endif + if(a != olda || b != oldb) { + CVFD::getInstance ()->Clear (); + CVFD::getInstance ()->drawBar (0, 8, a, 20); + CVFD::getInstance ()->drawBar (0, 36, b, 20); +#ifdef DEBUG +//gettimeofday (&tv1, NULL); +#endif + CVFD::getInstance ()->Update (); +#ifdef DEBUG +//gettimeofday (&tv2, NULL); +//int ms = (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000; +//if(ms) printf("Update takes %dms\n", ms); +#endif + } + return; +} +#endif diff --git a/src/driver/audiodec/wavdec.cpp b/src/driver/audiodec/wavdec.cpp new file mode 100644 index 000000000..552d12987 --- /dev/null +++ b/src/driver/audiodec/wavdec.cpp @@ -0,0 +1,213 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 Zwen + + Homepage: http://www.dbox2.info/ + + Kommentar: + + wav audio decoder + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ProgName "WavDec" +// nr of msecs to skip in ff/rev mode +#define MSECS_TO_SKIP 3000 +// nr of msecs to play in ff/rev mode +#define MSECS_TO_PLAY 200 + +struct WavHeader +{ + char ChunkID[4]; + int ChunkSize; + char Format[4]; + char Subchunk1ID[4]; + int Subchunk1Size; + short AudioFormat; + short NumChannels; + int SampleRate; + int ByteRate; + short BlockAlign; + short BitsPerSample; + char Subchunk2ID[4]; + int Subchunk2Size; +} __attribute__ ((packed)); + +int endianTest=1; +#define Swap32IfBE(l) \ + (*(char *)&endianTest ? (l) : \ + ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24))) +#define Swap16IfBE(l) \ + (*(char *)&endianTest ? (l) : \ + ((((l) & 0xff00) >> 8) | \ + (((l) & 0x00ff) << 8))) + +#define MAX_OUTPUT_SAMPLES 1022 /* AVIA_GT_PCM_MAX_SAMPLES-1 */ + +CBaseDec::RetCode CWavDec::Decoder(FILE *in, int OutputFd, State* state, CAudioMetaData* meta_data, time_t* time_played, unsigned int* secondsToSkip) +{ + char* buffer; + RetCode Status=OK; + + if (!SetMetaData(in, meta_data)) + { + Status=DATA_ERR; + return Status; + } + fseek(in, header_size, SEEK_SET); + int fmt; + switch(mBitsPerSample) + { + case 8 : fmt = AFMT_U8; + break; + case 16 : fmt = header_size == 0 ? AFMT_S16_BE : AFMT_S16_LE; + break; + default: + printf("%s: wrong bits per sample (%d)\n", ProgName, mBitsPerSample); + Status=DATA_ERR; + return Status; + } + + if (SetDSP(OutputFd, fmt, meta_data->samplerate , mChannels)) + { + Status=DSPSET_ERR; + return Status; + } + int actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000; + unsigned int oldSecsToSkip = *secondsToSkip; + int jumppos=0; + int bytes; + int bytes_to_play = (int) (1.0 * MSECS_TO_PLAY / 1000 * meta_data->bitrate / 8); + int bytes_to_skip = (int) (1.0 * actSecsToSkip * meta_data->bitrate / 8); + int buffersize = MAX_OUTPUT_SAMPLES * mChannels * mBitsPerSample / 8; + buffer = (char*) malloc (buffersize); + do + { + while(*state==PAUSE) + usleep(10000); + + if(*state==FF || *state==REV) + { + if (oldSecsToSkip != *secondsToSkip) + { + actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000; + bytes_to_skip = (int) (1.0 * actSecsToSkip * meta_data->bitrate / 8); + oldSecsToSkip = *secondsToSkip; + } + //printf("skipping %d secs and %d bytes\n",actSecsToSkip,bytes_to_skip); + if(std::abs(ftell(in)-jumppos) > bytes_to_play) + { + if(*state==FF) + { + fseek(in, bytes_to_skip, SEEK_CUR); + jumppos=ftell(in); + } + else + { + if(ftell(in) < bytes_to_skip) + { + fseek(in, header_size, SEEK_SET); + *state=PLAY; + } + else + { + fseek(in, -bytes_to_skip, SEEK_CUR); + jumppos=ftell(in); + } + } + } + // if a custom value was set we only jump once + if (*secondsToSkip != 0) { + *state=PLAY; + } + } + + bytes = fread(buffer, 1, buffersize, in); + if (write(OutputFd, buffer, bytes) != bytes) + { + fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno)); + Status=WRITE_ERR; + } + *time_played = (meta_data->bitrate!=0) ? (ftell(in)-header_size)*8/meta_data->bitrate : 0; + } while (bytes > 0 && *state!=STOP_REQ && Status==OK); + free(buffer); + return Status; +} + +bool CWavDec::GetMetaData(FILE *in, const bool nice, CAudioMetaData* m) +{ + return SetMetaData(in, m); +} + +CWavDec* CWavDec::getInstance() +{ + static CWavDec* WavDec = NULL; + if(WavDec == NULL) + { + WavDec = new CWavDec(); + } + return WavDec; +} + +bool CWavDec::SetMetaData(FILE* in, CAudioMetaData* m) +{ + /* Set Metadata */ + struct WavHeader wh; + + header_size = 44; + + fseek(in, 0, SEEK_END); + int filesize = ftell(in); + fseek(in, 0, SEEK_SET); + if(fread(&wh, sizeof(wh), 1, in)!=1) + return false; + if(memcmp(wh.ChunkID, "RIFF", 4)!=0 || + memcmp(wh.Format, "WAVE", 4)!=0 || + Swap16IfBE(wh.AudioFormat) != 1) + { + printf("%s: wrong format (header)\n", ProgName); + return false; + } + m->type = CAudioMetaData::WAV; + m->bitrate = Swap32IfBE(wh.ByteRate)*8; + m->samplerate = Swap32IfBE(wh.SampleRate); + mBitsPerSample = Swap16IfBE(wh.BitsPerSample); + mChannels = Swap16IfBE(wh.NumChannels); + m->total_time = (m->bitrate!=0) ? (filesize-header_size)*8 / m->bitrate : 0; + std::stringstream ss; + ss << "Riff/Wave / " << mChannels << "channel(s) / " << mBitsPerSample << "bit"; + m->type_info = ss.str(); + m->changed=true; + return true; +} diff --git a/src/driver/audiodec/wavdec.h b/src/driver/audiodec/wavdec.h new file mode 100644 index 000000000..5a9a9fadb --- /dev/null +++ b/src/driver/audiodec/wavdec.h @@ -0,0 +1,54 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2004 Zwen + + wav audio decoder + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __WAV_DEC__ +#define __WAV_DEC__ + +#include +#include + +class CWavDec : public CBaseDec +{ + +public: + static CWavDec* getInstance(); + virtual RetCode Decoder(FILE *,int , State* , CAudioMetaData* m, time_t* t, unsigned int* secondsToSkip); + bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m); + CWavDec(){}; + +protected: + virtual bool SetMetaData(FILE* in, CAudioMetaData* m); + + int mBitsPerSample; + int mChannels; + int header_size; +}; + + +#endif + diff --git a/src/driver/audiofile.cpp b/src/driver/audiofile.cpp new file mode 100644 index 000000000..d271eae2c --- /dev/null +++ b/src/driver/audiofile.cpp @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: non-nil; c-basic-offset: 4 -*- */ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* default constructor */ +CAudiofile::CAudiofile() + : MetaData(), Filename(), FileType( CFile::FILE_UNKNOWN ) +{ +} + +/* constructor */ +CAudiofile::CAudiofile( std::string name, CFile::FileType type ) + : MetaData(), Filename( name ), FileType( type ) +{ +} + +/* copy constructor */ +CAudiofile::CAudiofile( const CAudiofile& src ) + : MetaData( src.MetaData ), Filename( src.Filename ), + FileType( src.FileType ) +{ +} + +/* assignment operator */ +void CAudiofile::operator=( const CAudiofile& src ) +{ + MetaData = src.MetaData; + Filename = src.Filename; + FileType = src.FileType; +} + +void CAudiofile::clear() +{ + MetaData.clear(); + Filename.clear(); + FileType = CFile::FILE_UNKNOWN; +} diff --git a/src/driver/audiofile.h b/src/driver/audiofile.h new file mode 100644 index 000000000..f662418d8 --- /dev/null +++ b/src/driver/audiofile.h @@ -0,0 +1,58 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __AUDIOFILE_H__ +#define __AUDIOFILE_H__ + +#include +#include +#include "driver/audiometadata.h" +#include "driver/file.h" + +class CAudiofile +{ + public: + /* constructors */ + CAudiofile(); + CAudiofile( std::string name, CFile::FileType type ); + CAudiofile( const CAudiofile& src ); + + void operator=( const CAudiofile& src ); + void clear(); + + CAudioMetaData MetaData; + std::string Filename; + CFile::FileType FileType; +}; + +typedef std::vector CPlayList; + +#endif /* __AUDIOFILE_H__ */ diff --git a/src/driver/audiometadata.cpp b/src/driver/audiometadata.cpp new file mode 100644 index 000000000..600c4f32b --- /dev/null +++ b/src/driver/audiometadata.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: non-nil; c-basic-offset: 4 -*- */ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2002 Bjoern Kalkbrenner + Copyright (C) 2002,2003 Dirch + Copyright (C) 2002,2003,2004 Zwen + + libmad MP3 low-level core + Homepage: http://www.dbox2.info/ + + Kommentar: + + based on + ************************************ + *** madlld -- Mad low-level *** v 1.0p1, 2002-01-08 + *** demonstration/decoder *** (c) 2001, 2002 Bertrand Petit + ************************************ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +// constructor +CAudioMetaData::CAudioMetaData() +{ + clear(); +} + +// copy constructor +CAudioMetaData::CAudioMetaData( const CAudioMetaData& src ) + : type( src.type ), type_info( src.type_info ), + filesize( src.filesize ), bitrate( src.bitrate ), + avg_bitrate( src.avg_bitrate ), samplerate( src.samplerate ), + layer( src.layer ), mode( src.mode ), total_time( src.total_time ), + audio_start_pos( src.audio_start_pos ), vbr( src.vbr ), + hasInfoOrXingTag( src.hasInfoOrXingTag ), artist( src.artist ), + title( src.title ), album( src.album ), sc_station( src.sc_station ), + date( src.date ), genre( src.genre ), track( src.track ), + changed( src.changed ) +{ +} + +// assignment operator +void CAudioMetaData::operator=( const CAudioMetaData& src ) +{ + // self assignment check + if ( &src == this ) + return; + + type = src.type; + type_info = src.type_info; + filesize = src.filesize; + bitrate = src.bitrate; + avg_bitrate = src.avg_bitrate; + samplerate = src.samplerate; + layer = src.layer; + mode = src.mode; + total_time = src.total_time; + audio_start_pos = src.audio_start_pos; + vbr = src.vbr; + hasInfoOrXingTag = src.hasInfoOrXingTag; + artist = src.artist; + title = src.title; + album = src.album; + date = src.date; + genre = src.genre; + track = src.track; + sc_station = src.sc_station; + changed = src.changed; +} + +void CAudioMetaData::clear() +{ + type=NONE; + type_info.clear(); + filesize=0; + bitrate=0; + avg_bitrate=0; + samplerate=0; + total_time=0; + audio_start_pos=0; + vbr=false; + hasInfoOrXingTag=false; + artist.clear(); + title.clear(); + album.clear(); + sc_station.clear(); + date.clear(); + genre.clear(); + track.clear(); + changed=false; +} diff --git a/src/driver/audiometadata.h b/src/driver/audiometadata.h new file mode 100644 index 000000000..7cd6272b5 --- /dev/null +++ b/src/driver/audiometadata.h @@ -0,0 +1,88 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2002 Bjoern Kalkbrenner + Copyright (C) 2002,2003 Dirch + Copyright (C) 2002,2003,2004 Zwen + + libmad MP3 low-level core + Homepage: http://www.dbox2.info/ + + Kommentar: + + based on + ************************************ + *** madlld -- Mad low-level *** v 1.0p1, 2002-01-08 + *** demonstration/decoder *** (c) 2001, 2002 Bertrand Petit + ************************************ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __AUDIO_METADATA__ +#define __AUDIO_METADATA__ + +#include +#include + +class CAudioMetaData +{ +public: + // constructor + CAudioMetaData(); + // copy constructor + CAudioMetaData( const CAudioMetaData& src ); + // assignment operator + void operator=( const CAudioMetaData& src ); + void clear(); + + enum AudioType + { + NONE, + CDR, + MP3, + OGG, + WAV + }; + AudioType type; + std::string type_info; + + long filesize; /* filesize in bits (for mp3: without leading id3 tag) */ + + unsigned int bitrate; /* overall bitrate, vbr file: current bitrate */ + unsigned int avg_bitrate; /* average bitrate in case of vbr file */ + unsigned int samplerate; + enum mad_layer layer; + enum mad_mode mode; + time_t total_time; + long audio_start_pos; /* position of first audio frame */ + bool vbr; + /* if the variable hasInfoOrXingTag is true, this means the values of + VBR and Duration are correct and should not be changed by the + decoder */ + bool hasInfoOrXingTag; + + std::string artist; + std::string title; + std::string album; + std::string sc_station; + std::string date; + std::string genre; + std::string track; + bool changed; +}; +#endif /* __AUDIO_METADATA__ */ diff --git a/src/driver/audioplay.cpp b/src/driver/audioplay.cpp new file mode 100644 index 000000000..73b352520 --- /dev/null +++ b/src/driver/audioplay.cpp @@ -0,0 +1,223 @@ +/* + Neutrino-GUI - DBoxII-Project + + audioplayer + Copyright (C) 2002 Bjoern Kalkbrenner + Copyright (C) 2002,2003 Dirch + Copyright (C) 2002,2003,2004 Zwen + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define DBOX + +/**************************************************************************** + * Includes * + ****************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif +#include "global.h" +#include +#include +#include + +#include +#include +#include + +void CAudioPlayer::stop() +{ + state = CBaseDec::STOP_REQ; + if(thrPlay) + pthread_join(thrPlay,NULL); + thrPlay = 0; +} +void CAudioPlayer::pause() +{ + if(state==CBaseDec::PLAY || state==CBaseDec::FF || state==CBaseDec::REV) + state=CBaseDec::PAUSE; + else if(state==CBaseDec::PAUSE) + state=CBaseDec::PLAY; +} +void CAudioPlayer::ff(unsigned int seconds) +{ + m_SecondsToSkip = seconds; + if(state==CBaseDec::PLAY || state==CBaseDec::PAUSE || state==CBaseDec::REV) + state=CBaseDec::FF; + else if(state==CBaseDec::FF) + state=CBaseDec::PLAY; +} +void CAudioPlayer::rev(unsigned int seconds) +{ + m_SecondsToSkip = seconds; + if(state==CBaseDec::PLAY || state==CBaseDec::PAUSE || state==CBaseDec::FF) + state=CBaseDec::REV; + else if(state==CBaseDec::REV) + state=CBaseDec::PLAY; +} +CAudioPlayer* CAudioPlayer::getInstance() +{ + static CAudioPlayer* AudioPlayer = NULL; + if(AudioPlayer == NULL) + { + AudioPlayer = new CAudioPlayer(); + } + return AudioPlayer; +} + +void* CAudioPlayer::PlayThread( void* dummy ) +{ + int soundfd = -1; + g_RCInput->close_click(); + /* Decode stdin to stdout. */ + CBaseDec::RetCode Status = + CBaseDec::DecoderBase( &getInstance()->m_Audiofile, soundfd, + &getInstance()->state, + &getInstance()->m_played_time, + &getInstance()->m_SecondsToSkip ); + + if (Status != CBaseDec::OK) + { + fprintf( stderr, "Error during decoding: %s.\n", + ( Status == CBaseDec::READ_ERR ) ? "READ_ERR" : + ( Status == CBaseDec::WRITE_ERR ) ? "WRITE_ERR" : + ( Status == CBaseDec::DSPSET_ERR ) ? "DSPSET_ERR" : + ( Status == CBaseDec::DATA_ERR ) ? "DATA_ERR" : + ( Status == CBaseDec::INTERNAL_ERR ) ? "INTERNAL_ERR" : + "unknown" ); + } + + g_RCInput->open_click(); + + getInstance()->state = CBaseDec::STOP; + pthread_exit(0); + return NULL; +} + +bool CAudioPlayer::play(const CAudiofile* file, const bool highPrio) +{ + if (state != CBaseDec::STOP) + stop(); + getInstance()->clearFileData(); + + /* + transfer information from CAudiofile to member variable, + so that it does not have to be gathered again + + this assignment is important, otherwise the player would + crash if the file currently played was deleted from the + playlist + */ + m_Audiofile = *file; + + state = CBaseDec::PLAY; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + if(highPrio) + { + struct sched_param param; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + param.sched_priority=1; + pthread_attr_setschedparam(&attr, ¶m); + usleep(100000); // give the event thread some time to handle his stuff + // without this sleep there were duplicated events... + } + + bool ret = true; +#warning fixme: There must be a way to call the playing thread without arguments. (NULL did not work for me) + if (pthread_create (&thrPlay, &attr, PlayThread, (void*)&ret) != 0 ) + { + perror("audioplay: pthread_create(PlayThread)"); + ret = false; + } + + pthread_attr_destroy(&attr); + return ret; +} + +CAudioPlayer::CAudioPlayer() +{ + init(); +} + +void CAudioPlayer::init() +{ + CBaseDec::Init(); + state = CBaseDec::STOP; + thrPlay = 0; +} + +void CAudioPlayer::sc_callback(void *arg) +{ + bool changed=false; + CSTATE *stat = (CSTATE*)arg; + if(m_Audiofile.MetaData.artist != stat->artist) + { + m_Audiofile.MetaData.artist = stat->artist; + changed=true; + } + if (m_Audiofile.MetaData.title != stat->title) + { + m_Audiofile.MetaData.title = stat->title; + changed=true; + } + if (m_Audiofile.MetaData.sc_station != stat->station) + { + m_Audiofile.MetaData.sc_station = stat->station; + changed=true; + } + if (m_Audiofile.MetaData.genre != stat->genre) + { + m_Audiofile.MetaData.genre = stat->genre; + changed=true; + } + if(changed) + { + m_played_time = 0; + } + m_sc_buffered = stat->buffered; + m_Audiofile.MetaData.changed = changed; + //printf("Callback %s %s %s %d\n",stat->artist, stat->title, stat->station, stat->buffered); +} + +void CAudioPlayer::clearFileData() +{ + m_Audiofile.clear(); + m_played_time=0; + m_sc_buffered=0; +} + +CAudioMetaData CAudioPlayer::getMetaData() +{ + CAudioMetaData m = m_Audiofile.MetaData; + m_Audiofile.MetaData.changed=false; + return m; +} + +bool CAudioPlayer::hasMetaDataChanged() +{ + return m_Audiofile.MetaData.changed; +} + +bool CAudioPlayer::readMetaData(CAudiofile* const file, const bool nice) +{ + return CBaseDec::GetMetaDataBase(file, nice); +} + diff --git a/src/driver/audioplay.h b/src/driver/audioplay.h new file mode 100644 index 000000000..e56516a97 --- /dev/null +++ b/src/driver/audioplay.h @@ -0,0 +1,77 @@ +/* + Neutrino-GUI - DBoxII-Project + + Homepage: http://www.dbox2.info/ + + Kommentar: + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __AUDIO_PLAY__ +#define __AUDIO_PLAY__ + +#include +#include +#include +#include +#include + +class CAudioPlayer +{ +private: + time_t m_played_time; + int m_sc_buffered; + FILE *soundfd; + pthread_t thrPlay; + FILE *fp; + CBaseDec::State state; + static void* PlayThread(void*); + void clearFileData(); + unsigned int m_SecondsToSkip; + +protected: + CAudiofile m_Audiofile; + bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels); + +public: + static CAudioPlayer* getInstance(); + bool play(const CAudiofile*, const bool highPrio=false); + void stop(); + void pause(); + void init(); + void ff(unsigned int seconds=0); + void rev(unsigned int seconds=0); + CAudioMetaData getMetaData(); + bool hasMetaDataChanged(); + bool readMetaData(CAudiofile* const, const bool); + time_t getTimePlayed(){return m_played_time;} + time_t getTimeTotal(){return m_Audiofile.MetaData.total_time;} + int getScBuffered(){return m_sc_buffered;} + void sc_callback(void *arg); // see comment in .cpp + CBaseDec::State getState(){return state;} + + CAudioPlayer(); + ~CAudioPlayer(); + + +}; + + +#endif + diff --git a/src/driver/aviaext.cpp b/src/driver/aviaext.cpp new file mode 100644 index 000000000..8873ad96b --- /dev/null +++ b/src/driver/aviaext.cpp @@ -0,0 +1,170 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2004 Zwen@tuxbox.org + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#if HAVE_DVB_API_VERSION >= 3 +#include +#include + +#define AVIAEXT_DEV "/dev/dbox/aviaEXT" + +CAViAExt* CAViAExt::getInstance() +{ + static CAViAExt* AViAExt = NULL; + if(AViAExt == NULL) + { + AViAExt = new CAViAExt(); + } + return AViAExt; +} + +void CAViAExt::iecOn() +{ + int res,fd; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return; + } + res = ioctl(fd, AVIA_EXT_IEC_SET, 1); + if (res<0) + perror("aviaext: ioctl"); + close(fd); +} + +void CAViAExt::iecOff() +{ + int res,fd; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return; + } + res = ioctl(fd, AVIA_EXT_IEC_SET, 0); + if (res<0) + perror("aviaext: ioctl"); + close(fd); +} + +int CAViAExt::iecState() +{ + int res,fd; + unsigned int param; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return -1; + } + res = ioctl(fd, AVIA_EXT_IEC_GET, ¶m); + + close(fd); + + if (res<0) + { + perror("aviaext: ioctl"); + return -1; + } + return param; +} + +void CAViAExt::playbackSPTS() +{ + int res,fd; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return; + } + res = ioctl(fd, AVIA_EXT_AVIA_PLAYBACK_MODE_SET, 1); + if (res<0) + perror("aviaext: ioctl"); + close(fd); +} + +void CAViAExt::playbackPES() +{ + int res,fd; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return; + } + res = ioctl(fd, AVIA_EXT_AVIA_PLAYBACK_MODE_SET, 0); + if (res<0) + perror("aviaext: ioctl"); + close(fd); +} + +int CAViAExt::playbackState() +{ + int res,fd; + unsigned int param; + + if ((fd = open(AVIAEXT_DEV,O_RDWR))<0) + { + if (errno==ENOENT) + fprintf (stderr,"%s does not exist, did you forget to load the aviaEXT module?\n",AVIAEXT_DEV); + else + perror ("aviaext: error opening /dev/dbox/aviaEXT"); + return -1; + } + res = ioctl(fd, AVIA_EXT_AVIA_PLAYBACK_MODE_GET, ¶m); + + close(fd); + + if (res<0) + { + perror("aviaext: ioctl"); + return -1; + } + return param; +} +#endif diff --git a/src/driver/bigclock.cpp b/src/driver/bigclock.cpp new file mode 100644 index 000000000..e5264f59b --- /dev/null +++ b/src/driver/bigclock.cpp @@ -0,0 +1,125 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#include "bigclock.h" + + +void doppelpunkt(CLCDDisplay* display, int pos) +{ + int i,j; + + for (i=1;i<=4;i++) + for (j=1;j<=4;j++) + display->draw_point(i+10+pos*24-3,j+23,CLCDDisplay::PIXEL_ON); + for (i=1;i<=4;i++) + for (j=1;j<=4;j++) + display->draw_point(i+10+pos*24-3,j+37,CLCDDisplay::PIXEL_ON); +} + + +void balken(CLCDDisplay* display, int pos, int balken) +{ + int i,j; + + if (balken==3||balken==6||balken==7) + { + //horizontaler balken + if (balken == 6) for (i=1;i<=14;i++) for (j=1;j<=4;j++) display->draw_point(i+5+pos*24-3,j+14,CLCDDisplay::PIXEL_ON); + if (balken == 7) for (i=1;i<=8;i++) for (j=1;j<=4;j++) display->draw_point(i+8+pos*24-3,j+30,CLCDDisplay::PIXEL_ON); + } if (balken == 3) for (i=1;i<=14;i++) for (j=1;j<=4;j++) display->draw_point(i+5+pos*24-3,j+46,CLCDDisplay::PIXEL_ON); + else + { + //vertikaler balken + if (balken == 1) for (i=1;i<=4;i++) for (j=1;j<=13;j++) display->draw_point(i+19+pos*24-3,j+18,CLCDDisplay::PIXEL_ON); + if (balken == 2) for (i=1;i<=4;i++) for (j=1;j<=13;j++) display->draw_point(i+19+pos*24-3,j+33,CLCDDisplay::PIXEL_ON); + if (balken == 5) for (i=1;i<=4;i++) for (j=1;j<=13;j++) display->draw_point(i+1+pos*24-3,j+18,CLCDDisplay::PIXEL_ON); + if (balken == 4) for (i=1;i<=4;i++) for (j=1;j<=13;j++) display->draw_point(i+1+pos*24-3,j+33,CLCDDisplay::PIXEL_ON); + + } +} + +void showBigClock(CLCDDisplay* display, int h, int m) +{ + + //stunden einer + if (h==0||h==10||h==20) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,3); balken(display, 1,4); balken(display, 1,5); balken(display, 1,6);} + if (h==1||h==11||h==21) + { balken(display, 1,1); balken(display, 1,2);} + if (h==2||h==12||h==22) + { balken(display, 1,1); balken(display, 1,7); balken(display, 1,3); balken(display, 1,4); balken(display, 1,6);} + if (h==3||h==13||h==23) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,3); balken(display, 1,7); balken(display, 1,6);} + if (h==4||h==14) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,7); balken(display, 1,5);} + if (h==5||h==15) + { balken(display, 1,2); balken(display, 1,3); balken(display, 1,5); balken(display, 1,6); balken(display, 1,7);} + if (h==6||h==16) + { balken(display, 1,7); balken(display, 1,2); balken(display, 1,3); balken(display, 1,4); balken(display, 1,5); balken(display, 1,6);} + if (h==7||h==17) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,6);} + if (h==8||h==18) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,3); balken(display, 1,4); balken(display, 1,5); balken(display, 1,6); balken(display, 1,7);} + if (h==9||h==19) + { balken(display, 1,1); balken(display, 1,2); balken(display, 1,3); balken(display, 1,7); balken(display, 1,5); balken(display, 1,6);} + //stunden zehner + if (h>9&&h<20){ balken(display, 0,1); balken(display, 0,2);} + if (h>19) { balken(display, 0,1); balken(display, 0,7); balken(display, 0,3); balken(display, 0,4); balken(display, 0,6);} + + + //minuten einer + if (m==0||m==10||m==20||m==30||m==40||m==50) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,3); balken(display, 4,4); balken(display, 4,5); balken(display, 4,6);} + if (m==1||m==11||m==21||m==31||m==41||m==51) + { balken(display, 4,1); balken(display, 4,2);} + if (m==2||m==12||m==22||m==32||m==42||m==52) + { balken(display, 4,1); balken(display, 4,7); balken(display, 4,3); balken(display, 4,4); balken(display, 4,6);} + if (m==3||m==13||m==23||m==33||m==43||m==53) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,3); balken(display, 4,7); balken(display, 4,6);} + if (m==4||m==14||m==24||m==34||m==44||m==54) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,7); balken(display, 4,5);} + if (m==5||m==15||m==25||m==35||m==45||m==55) + { balken(display, 4,2); balken(display, 4,3); balken(display, 4,5); balken(display, 4,6); balken(display, 4,7);} + if (m==6||m==16||m==26||m==36||m==46||m==56) + { balken(display, 4,7); balken(display, 4,2); balken(display, 4,3); balken(display, 4,4); balken(display, 4,5); balken(display, 4,6);} + if (m==7||m==17||m==27||m==37||m==47||m==57) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,6);} + if (m==8||m==18||m==28||m==38||m==48||m==58) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,3); balken(display, 4,4); balken(display, 4,5); balken(display, 4,6); balken(display, 4,7);} + if (m==9||m==19||m==29||m==39||m==49||m==59) + { balken(display, 4,1); balken(display, 4,2); balken(display, 4,3); balken(display, 4,7); balken(display, 4,5); balken(display, 4,6);} + + //minuten zehner + if (m<10) + { balken(display, 3,1); balken(display, 3,2); balken(display, 3,3); balken(display, 3,4); balken(display, 3,5); balken(display, 3,6);} + if (m>9&&m<20){ balken(display, 3,1); balken(display, 3,2);} + if (m>19&&m<30){ balken(display, 3,1); balken(display, 3,7); balken(display, 3,3); balken(display, 3,4); balken(display, 3,6);} + if (m>29&&m<40){ balken(display, 3,1); balken(display, 3,2); balken(display, 3,3); balken(display, 3,7); balken(display, 3,6);} + if (m>39&&m<50){ balken(display, 3,1); balken(display, 3,2); balken(display, 3,7); balken(display, 3,5);} + if (m>=50) { balken(display, 3,2); balken(display, 3,3); balken(display, 3,5); balken(display, 3,6); balken(display, 3,7);} + + doppelpunkt(display, 2); + } diff --git a/src/driver/bigclock.h b/src/driver/bigclock.h new file mode 100644 index 000000000..91735f93c --- /dev/null +++ b/src/driver/bigclock.h @@ -0,0 +1,35 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __bigclock__ +#define __bigclock__ + +#include + + +void showBigClock(CLCDDisplay* display, int h,int m); + + +#endif diff --git a/src/driver/capture.cpp b/src/driver/capture.cpp new file mode 100644 index 000000000..35c580b42 --- /dev/null +++ b/src/driver/capture.cpp @@ -0,0 +1,308 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + + + + +#include +#include +#include +#include +#include +#include +#include + +#include "capture.h" + + +#include +#warning "experimental..." + + +// +// -- TV Picture Capture +// -- This module is a class to provide a capture abstraction layer +// -- +// -- 2003-12 rasc +// + + + +// +// -- Constructor +// + +CCAPTURE::CCAPTURE() +{ + fd = -1; +} + + +CCAPTURE::CCAPTURE(int capture_nr) +{ + fd = -1; + fd = captureopen(capture_nr); +} + + +CCAPTURE::CCAPTURE(int capture_nr, int x, int y, int w, int h) +{ + fd = -1; + fd = captureopen (capture_nr); + set_coord (x,y, w,h); +} + + + + +CCAPTURE::~CCAPTURE() +{ + captureclose (); // cleanup anyway!! +} + + +// +// -- open Capture device +// -- return >=0: ok +// + +int CCAPTURE::captureopen (int cap_nr) +{ + char *capturedevs[] = { + CAPTURE_DEV "0" // CAPTURE device 0 + // CAPTURE_DEV "1", // CAPTURE device 1 + // CAPTURE_DEV "2", // CAPTURE device 2 + // CAPTURE_DEV "3" // CAPTURE device 3 + }; + + + if ( (cap_nr>0) || (cap_nr < (int)(sizeof (capturedevs)/sizeof(char *))) ) { + + if (fd < 0) { + fd = open( capturedevs[cap_nr], O_RDWR ); + if (fd >= 0) { + cx = cy = cw = ch = 0; + out_w = out_h = 0; + stride = 0; + } else perror (capturedevs[cap_nr]); + return fd; + } + + } + + return -1; +} + + +// +// -- close Capture Device +// + +void CCAPTURE::captureclose () +{ + if (fd >=0 ) { + close (fd); + fd = -1; + cx = cy = cw = ch = 0; + out_w = out_h = 0; + stride = 0; + } + return; +} + + + + +/* + -- Coordination routines... + */ + +void CCAPTURE::set_coord (int x, int y, int w, int h) +{ + if (( x != cx ) || ( y != cy )) { + cx = x; + cy = y; + cw = w; + ch = h; + _set_window (cx,cy,cw,ch); + } +} + + +void CCAPTURE::set_xy (int x, int y) +{ + if (( x != cx ) || ( y != cy )) { + cx = x; + cy = y; + _set_window (cx,cy,cw,ch); + } +} + + + +void CCAPTURE::set_size (int w, int h) +{ + if (( w != cw ) || ( h != ch )) { + cw = w; + ch = h; + _set_window (cx,cy,cw,ch); + } +} + + + + +// +// -- set Capture Position +// + +void CCAPTURE::_set_window (int x, int y, int w, int h) +{ +#ifdef __v4l_capture + + struct v4l2_crop crop; + + crop.c.left = x; + crop.c.top = y; + crop.c.width = w; + crop.c.height = h; + if( ioctl(fd, VIDIOC_S_CROP, &crop) < 0) + perror ("error VIDIOC_S_CROP"); + +#else + + capture_set_input_pos(fd, x, y); + capture_set_input_size(fd, w, h); + +#endif +} + + + +void CCAPTURE::set_output_size (int w, int h) +{ +#ifdef __v4l_capture + + struct v4l2_format format; + + out_w = w; + out_h = h; + format.fmt.pix.width = w; + format.fmt.pix.height = h; + + if (ioctl(fd, VIDIOC_S_FMT, &format) < 0) + perror ("error VIDIOC_S_FMT"); + +#else + + out_w = w; + out_h = h; + capture_set_output_size(fd, w, h); + +#endif +} + + +// +// -- capture a frame +// -- if buffer == NULL, routine will alloc memory! +// -- destroy will not free this allocated memory! +// -- otherwise buffer has to be large enough! +// + +u_char *CCAPTURE::readframe (u_char *buf) +{ +#ifdef __v4l_capture + + struct v4l2_format vid; + int n; + u_char *b; + + if (fd >= 0) { + // get capture information + vid.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(fd, VIDIOC_G_FMT, &vid) < 0) + perror ("Error getting info with VIDIOC_G_FMT"); + + printf ("reported bytesperline: %d, height: %d, sizeimage: %d\n", + vid.fmt.pix.bytesperline, + vid.fmt.pix.height, + vid.fmt.pix.sizeimage); + + b = (buf != NULL) + ? buf + : (u_char *) malloc (vid.fmt.pix.bytesperline * + vid.fmt.pix.height); + + + n = read (fd, b, vid.fmt.pix.bytesperline*vid.fmt.pix.height); + if (n < 0) { + perror ("Error reading capture buffer"); + if (!buf) free (b); + return (u_char *)NULL; + } + + return b; + } + + return (u_char *) NULL; + + +#else + + u_char *b; + int stride; + int n; + + if (fd >= 0) { + stride = capture_start(fd); + + b = (buf != NULL) + ? buf + : (u_char *) malloc (stride * out_h); + + n = read(fd, b, stride * out_h); + capture_stop(fd); + if (n < 0) { + perror ("Error reading capture buffer"); + if (!buf) free (b); + return (u_char *)NULL; + } + + + return b; + } + + return (u_char *) NULL; + +#endif +} + + + + + + + + + + diff --git a/src/driver/capture.h b/src/driver/capture.h new file mode 100644 index 000000000..53775739e --- /dev/null +++ b/src/driver/capture.h @@ -0,0 +1,81 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __CAPTURE_CONTROL__ +#define __CAPTURE_CONTROL__ + + +#define __v4l_capture + + +using namespace std; + + +// +// -- Picture Capture Control +// -- 2003-12 rasc +// + +#ifdef __v4l_capture + +#include +#define CAPTURE_DEV "/dev/v4l/video" // CaptureNr will be appended! + +#else + +#include +#define CAPTURE_DEV "/dev/dbox/capture" // CaptureNr will be appended! + +#endif + + +class CCAPTURE +{ + public: + CCAPTURE (); + CCAPTURE (int capture_nr); // incl. open + CCAPTURE (int capture_nr, int x, int y, int w, int h); // open + set_coord + ~CCAPTURE (); + + int captureopen (int capture_nr); + void captureclose (void); + void set_coord (int x, int y, int w, int h); + void set_xy (int x, int y); + void set_size (int w, int h); + void set_output_size (int w, int h); + u_char *readframe (u_char *buf); + + private: + void _set_window (int x, int y, int w, int h); + + int fd; // io descriptor + int cx, cy, cw, ch; // capture size + int out_w, out_h; // capture output size + int stride; + +}; + + + +#endif + + diff --git a/src/driver/encoding.cpp b/src/driver/encoding.cpp new file mode 100644 index 000000000..519b10d89 --- /dev/null +++ b/src/driver/encoding.cpp @@ -0,0 +1,44 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/encoding.cpp,v 1.2 2003/09/27 11:48:09 thegoodguy Exp $ + * + * conversion of character encodings - d-box2 linux project + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +std::string Latin1_to_UTF8(const std::string & s) +{ + std::string r; + + for (std::string::const_iterator it = s.begin(); it != s.end(); it++) + { + unsigned char c = *it; + if (c < 0x80) + r += c; + else + { + unsigned char d = 0xc0 | (c >> 6); + r += d; + d = 0x80 | (c & 0x3f); + r += d; + } + } + return r; +} diff --git a/src/driver/encoding.h b/src/driver/encoding.h new file mode 100644 index 000000000..9564a8733 --- /dev/null +++ b/src/driver/encoding.h @@ -0,0 +1,31 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/encoding.h,v 1.2 2003/09/27 11:48:09 thegoodguy Exp $ + * + * conversion of character encodings - d-box2 linux project + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __neutrino__encoding_h__ +#define __neutrino__encoding_h__ + +#include + +std::string Latin1_to_UTF8(const std::string & s); + +#endif /* __neutrino__encoding_h__ */ diff --git a/src/driver/fb_window.cpp b/src/driver/fb_window.cpp new file mode 100644 index 000000000..d0d47afd5 --- /dev/null +++ b/src/driver/fb_window.cpp @@ -0,0 +1,82 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/fb_window.cpp,v 1.6 2004/03/14 22:28:13 thegoodguy Exp $ + * + * abstract fb_window class - d-box2 linux project + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +class CPrivateData +{ + public: + CFrameBuffer * frameBuffer; + fb_pixel_t * Background; +}; + +CFBWindow::CFBWindow(const int _x, const int _y, const int _dx, const int _dy) +{ + x = _x ; + y = _y ; + dx = _dx; + dy = _dy; + + private_data = (void *) new CPrivateData; + ((CPrivateData *)private_data)->frameBuffer = CFrameBuffer::getInstance(); + ((CPrivateData *)private_data)->Background = new fb_pixel_t [_dx * _dy]; + if (((CPrivateData *)private_data)->Background != NULL) + ((CPrivateData *)private_data)->frameBuffer->SaveScreen(_x, _y, _dx, _dy, (fb_pixel_t *)((CPrivateData *)private_data)->Background); + +} + +CFBWindow::~CFBWindow(void) +{ + if (private_data != NULL) + { + if (((CPrivateData *)private_data)->Background != NULL) + ((CPrivateData *)private_data)->frameBuffer->RestoreScreen(x, y, dx, dy, (fb_pixel_t *)((CPrivateData *)private_data)->Background); + + delete ((CPrivateData *)private_data)->Background; + delete ((CPrivateData *)private_data); + private_data = NULL; + } +} + +void CFBWindow::paintBoxRel(const int _x, const int _y, const int _dx, const int _dy, const color_t _col, int radius, int type) +{ + ((CPrivateData *)private_data)->frameBuffer->paintBoxRel(x + _x, y + _y, _dx, _dy, _col, radius, type); +} + +bool CFBWindow::paintIcon(const char * const _filename, const int _x, const int _y, const color_t _offset) +{ + ((CPrivateData *)private_data)->frameBuffer->paintIcon(_filename, x + _x, y + _y, _offset); + return 0; +} + +void CFBWindow::RenderString(const font_t _font, const int _x, const int _y, const int _width, const char * const _text, const color_t _color, const int _boxheight, const bool _utf8_encoded) +{ + ((Font *)_font)->RenderString(x + _x, y + _y, _width, _text, _color, _boxheight, _utf8_encoded); +} diff --git a/src/driver/fb_window.h b/src/driver/fb_window.h new file mode 100644 index 000000000..36110386d --- /dev/null +++ b/src/driver/fb_window.h @@ -0,0 +1,52 @@ +#ifndef __fb_window_h__ +#define __fb_window_h__ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/fb_window.h,v 1.3 2004/03/13 12:45:41 thegoodguy Exp $ + * + * abstract fb_window class - d-box2 linux project + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +class CFBWindow +{ + public: +#ifdef FB_USE_PALETTE + typedef unsigned short color_t; +#else + typedef unsigned int color_t; +#endif + typedef void * font_t; + typedef void * private_data_t; + + private: + private_data_t private_data; + + public: + int x, y; /* upper left corner */ + int dx, dy; /* dimension */ + + CFBWindow(const int _x, const int _y, const int _dx, const int _dy); + ~CFBWindow(); + + void paintBoxRel(const int _x, const int _y, const int _dx, const int _dy, const color_t _col, int radius = 0, int type = 0); + bool paintIcon(const char * const _filename, const int _x, const int _y, const color_t _offset = 1); + void RenderString(const font_t _font, const int _x, const int _y, const int _width, const char * const _text, const color_t _color, const int _boxheight = 0, const bool _utf8_encoded = false); +}; + +#endif /* __fb_window_h__ */ diff --git a/src/driver/file.cpp b/src/driver/file.cpp new file mode 100644 index 000000000..01723fe7c --- /dev/null +++ b/src/driver/file.cpp @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: non-nil; c-basic-offset: 4 -*- */ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +/* ATTENTION: the array file_extension_list MUST BE SORTED ASCENDING (cf. sort, man bsearch) - otherwise bsearch will not work correctly! */ +const char * const file_extension_list[] = +{ + "asf", "avi", "bmp", "cdr", "crw", + "flac", "gif", "imu", "jpeg", "jpg", + "m2a", "m3u", "mkv", "mp2", "mp3", + "mpa", "ogg", "png", "sh", "txt", + "url", "wav", "xml" +}; +/* ATTENTION: the array file_extension_list MUST BE SORTED ASCENDING (cf. sort, man bsearch) - otherwise bsearch will not work correctly! */ + +const CFile::FileType file_type_list[] = +{ + CFile::FILE_ASF , CFile::FILE_AVI , CFile::FILE_PICTURE , CFile::FILE_CDR , CFile::FILE_PICTURE , + CFile::FILE_FLAC , CFile::FILE_PICTURE , CFile::STREAM_PICTURE, CFile::FILE_PICTURE , CFile::FILE_PICTURE , + CFile::FILE_MP3 , CFile::FILE_PLAYLIST , CFile::FILE_MKV , CFile::FILE_MP3 , CFile::FILE_MP3 , + CFile::FILE_MP3 , CFile::FILE_OGG , CFile::FILE_PICTURE , CFile::FILE_TEXT , CFile::FILE_TEXT , + CFile::STREAM_AUDIO , CFile::FILE_WAV , CFile::FILE_XML +}; + +int mycasecmp(const void * a, const void * b) +{ + return strcasecmp(*(const char * *)a, *(const char * *)b); +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ + +CFile::CFile() + : Size( 0 ), Mode( 0 ), Marked( false ), Time( 0 ) +{ +} + +CFile::FileType CFile::getType(void) const +{ + if(S_ISDIR(Mode)) + return FILE_DIR; + + std::string::size_type ext_pos = Name.rfind('.'); + + if (ext_pos != std::string::npos) + { + const char * key = &(Name.c_str()[ext_pos + 1]); + + void * result = ::bsearch(&key, file_extension_list, sizeof(file_extension_list) / sizeof(const char *), sizeof(const char *), mycasecmp); + + if (result != NULL) + return file_type_list[(const char * *)result - (const char * *)&file_extension_list]; + } + return FILE_UNKNOWN; +} + +//------------------------------------------------------------------------ + +std::string CFile::getFileName(void) const // return name.extension or folder name without trailing / +{ + std::string::size_type namepos = Name.rfind('/'); + + return (namepos == std::string::npos) ? Name : Name.substr(namepos + 1); +} + +//------------------------------------------------------------------------ + +std::string CFile::getPath(void) const // return complete path including trailing / +{ + int pos = 0; + + return ((pos = Name.rfind('/')) > 1) ? Name.substr(0, pos + 1) : "/"; +} diff --git a/src/driver/file.h b/src/driver/file.h new file mode 100644 index 000000000..6d62e8e46 --- /dev/null +++ b/src/driver/file.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: non-nil; c-basic-offset: 4 -*- */ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __FILE_H__ +#define __FILE_H__ + +#include /* make sure off_t has size 8 + in __USE_FILE_OFFSET64 mode */ + +#ifndef __USE_FILE_OFFSET64 +#error not using 64 bit file offsets +#endif /* __USE_FILE__OFFSET64 */ + +#include + + +#include +#include + +class CFile +{ +public: + enum FileType + { + FILE_UNKNOWN = 0, + FILE_AVI, + FILE_ASF, + FILE_DIR, + FILE_TEXT, + FILE_CDR, + FILE_MP3, + FILE_MKV, + FILE_OGG, + FILE_WAV, + FILE_FLAC, + FILE_XML, + FILE_PLAYLIST, + STREAM_AUDIO, + FILE_PICTURE, + STREAM_PICTURE + }; + + FileType getType(void) const; + std::string getFileName(void) const; + std::string getPath(void) const; + + CFile(); + off_t Size; + std::string Name; + std::string Url; + mode_t Mode; + bool Marked; + time_t Time; +}; + +typedef std::vector CFileList; + +#endif /* __FILE_H__ */ diff --git a/src/driver/fontrenderer.cpp b/src/driver/fontrenderer.cpp new file mode 100644 index 000000000..f79f78349 --- /dev/null +++ b/src/driver/fontrenderer.cpp @@ -0,0 +1,630 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Copyright (C) 2003 thegoodguy + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +// this method is recommended for FreeType >2.0.x: +#include +#include FT_FREETYPE_H + +#include + +#include + +//#undef USE_NEVIS_GXA // since OSD problem gone with new driver, until GXA will be faster//reenabled GXA-luc + +FT_Error FBFontRenderClass::myFTC_Face_Requester(FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face* aface) +{ + return ((FBFontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface); +} + + +FBFontRenderClass::FBFontRenderClass(const int xr, const int yr) +{ + dprintf(DEBUG_DEBUG, "[FONT] initializing core...\n"); + if (FT_Init_FreeType(&library)) + { + dprintf(DEBUG_NORMAL, "[FONT] initializing core failed.\n"); + return; + } + + font = NULL; + + xres = xr; + yres = yr; + + int maxbytes= 4 *1024*1024; + dprintf(DEBUG_INFO, "[FONT] Intializing font cache, using max. %dMB...\n", maxbytes/1024/1024); + fflush(stdout); + if (FTC_Manager_New(library, 10, 20, maxbytes, myFTC_Face_Requester, this, &cacheManager)) + { + dprintf(DEBUG_NORMAL, "[FONT] manager failed!\n"); + return; + } + if (!cacheManager) + { + dprintf(DEBUG_NORMAL, "[FONT] error.\n"); + return; + } + if (FTC_SBitCache_New(cacheManager, &sbitsCache)) + { + dprintf(DEBUG_NORMAL, "[FONT] sbit failed!\n"); + return; + } +/* if (FTC_ImageCache_New(cacheManager, &imageCache)) + { + printf(" imagecache failed!\n"); + } +*/ + pthread_mutex_init( &render_mutex, NULL ); +} + +FBFontRenderClass::~FBFontRenderClass() +{ + fontListEntry * g; + + for (fontListEntry * f = font; f; f = g) + { + g = f->next; + delete f; + } + + FTC_Manager_Done(cacheManager); + FT_Done_FreeType(library); +} + +FT_Error FBFontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface) +{ + fontListEntry *font=(fontListEntry *)face_id; + if (!font) + return -1; + dprintf(DEBUG_DEBUG, "[FONT] FTC_Face_Requester (%s/%s)\n", font->family, font->style); + + int error; + if ((error=FT_New_Face(library, font->filename, 0, aface))) + { + dprintf(DEBUG_NORMAL, "[FONT] FTC_Face_Requester (%s/%s) failed: %i\n", font->family, font->style, error); + return error; + } + + if (strcmp(font->style, (*aface)->style_name) != 0) + { + FT_Matrix matrix; // Italics + + matrix.xx = 1 * 0x10000; + matrix.xy = (0x10000 >> 3); + matrix.yx = 0 * 0x10000; + matrix.yy = 1 * 0x10000; + + FT_Set_Transform(*aface, &matrix, NULL); + } + return 0; +} + +FTC_FaceID FBFontRenderClass::getFaceID(const char * const family, const char * const style) +{ + for (fontListEntry *f=font; f; f=f->next) + { + if ((!strcmp(f->family, family)) && (!strcmp(f->style, style))) + return (FTC_FaceID)f; + } + if (strncmp(style, "Bold ", 5) == 0) + { + for (fontListEntry *f=font; f; f=f->next) + { + if ((!strcmp(f->family, family)) && (!strcmp(f->style, &(style[5])))) + return (FTC_FaceID)f; + } + } + for (fontListEntry *f=font; f; f=f->next) + { + if (!strcmp(f->family, family)) + { + if (f->next) // the first font always seems to be italic, skip if possible + continue; + return (FTC_FaceID)f; + } + } + return 0; +} + +FT_Error FBFontRenderClass::getGlyphBitmap(FTC_ImageTypeRec *font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + return FTC_SBitCache_Lookup(sbitsCache, font, glyph_index, sbit, NULL); +} + +FT_Error FBFontRenderClass::getGlyphBitmap(FTC_ScalerRec *sc, FT_ULong glyph_index, FTC_SBit *sbit) +{ + return FTC_SBitCache_LookupScaler(sbitsCache, sc, FT_LOAD_DEFAULT, glyph_index, sbit, NULL); +} + +const char * const FBFontRenderClass::AddFont(const char * const filename, const bool make_italics) +{ + fflush(stdout); + int error; + fontListEntry *n=new fontListEntry; + + FT_Face face; + if ((error=FT_New_Face(library, filename, 0, &face))) + { + dprintf(DEBUG_NORMAL, "[FONT] adding font %s, failed: %i\n", filename, error); + delete n;//Memory leak: n + return NULL; + } + n->filename = strdup(filename); + n->family = strdup(face->family_name); + n->style = strdup(make_italics ? "Italic" : face->style_name); + FT_Done_Face(face); + n->next=font; + dprintf(DEBUG_DEBUG, "[FONT] adding font %s... family %s, style %s ok\n", filename, n->family, n->style); + font=n; + return n->style; +} + +FBFontRenderClass::fontListEntry::~fontListEntry() +{ + delete[] filename; + delete[] family; + delete[] style; +} + +Font *FBFontRenderClass::getFont(const char * const family, const char * const style, int size) +{ + FTC_FaceID id = getFaceID(family, style); + if (!id) { + dprintf(DEBUG_DEBUG, "[FONT] getFont: family %s, style %s failed!\n", family, style); + return 0; + } + dprintf(DEBUG_DEBUG, "[FONT] getFont: family %s, style %s ok\n", family, style); + return new Font(this, id, size, (strcmp(((fontListEntry *)id)->style, style) == 0) ? Font::Regular : Font::Embolden); +} + +std::string FBFontRenderClass::getFamily(const char * const filename) const +{ + for (fontListEntry *f=font; f; f=f->next) + { + if (!strcmp(f->filename, filename)) + return f->family; + } + + return ""; +} + +Font::Font(FBFontRenderClass *render, FTC_FaceID faceid, const int isize, const fontmodifier _stylemodifier) +{ + stylemodifier = _stylemodifier; + + frameBuffer = CFrameBuffer::getInstance(); + renderer = render; + font.face_id = faceid; + font.width = isize; + font.height = isize; + //font.image_type = ftc_image_grays; + //font.image_type |= ftc_image_flag_autohinted; + font.flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT; + + scaler.face_id = font.face_id; + scaler.width = isize * 64; + scaler.height = isize * 64; + scaler.pixel = false; + scaler.x_res = render->xres; + scaler.y_res = render->yres; + + setSize(isize); +} + +FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit) +{ + return renderer->getGlyphBitmap(&scaler, glyph_index, sbit); +} + +int Font::setSize(int isize) +{ + int temp = font.width; + font.width = font.height = isize; + scaler.width = isize * 64; + scaler.height = isize * 64; + + if (FTC_Manager_LookupSize(renderer->cacheManager, &scaler, &size)<0) + { + dprintf(DEBUG_NORMAL, "FTC_Manager_Lookup_Size failed!\n"); + return 0; + } + face = size->face; +#if 0 +//FIXME test + + ascender = face->ascender; + descender = face->descender; + height = face->height; + lower = -descender+(-(descender>>1))+1; +return 0; +#endif + // hack begin (this is a hack to get correct font metrics, didn't find any other way which gave correct values) + FTC_SBit glyph; + int index; + + index=FT_Get_Char_Index(face, 'M'); // "M" gives us ascender + getGlyphBitmap(index, &glyph); + int tM=glyph->top; + fontwidth = glyph->width; + + index=FT_Get_Char_Index(face, 'g'); // "g" gives us descender + getGlyphBitmap(index, &glyph); + int hg=glyph->height; + int tg=glyph->top; + + ascender=tM; + descender=tg-hg; //this is a negative value! + int halflinegap= -(descender>>1); // |descender/2| - we use descender as linegap, half at top, half at bottom + upper = halflinegap+ascender+3; // we add 3 at top + lower = -descender+halflinegap+1; // we add 1 at bottom + height=upper+lower; // this is total height == distance of lines + // hack end + + //printf("glyph: hM=%d tM=%d hg=%d tg=%d ascender=%d descender=%d height=%d linegap/2=%d upper=%d lower=%d\n", + // hM,tM,hg,tg,ascender,descender,height,halflinegap,upper,lower); + //printf("font metrics: height=%ld\n", (size->metrics.height+32) >> 6); + return temp; +} + +int Font::getHeight(void) +{ + return height; +} + +int UTF8ToUnicode(const char * &text, const bool utf8_encoded) // returns -1 on error +{ + int unicode_value; +//printf("%c ", (unsigned char)(*text)); + if (utf8_encoded && ((((unsigned char)(*text)) & 0x80) != 0)) + { + int remaining_unicode_length; + if ((((unsigned char)(*text)) & 0xf8) == 0xf0) + { + unicode_value = ((unsigned char)(*text)) & 0x07; + remaining_unicode_length = 3; + } + else if ((((unsigned char)(*text)) & 0xf0) == 0xe0) + { + unicode_value = ((unsigned char)(*text)) & 0x0f; + remaining_unicode_length = 2; + } + else if ((((unsigned char)(*text)) & 0xe0) == 0xc0) + { + unicode_value = ((unsigned char)(*text)) & 0x1f; + remaining_unicode_length = 1; + } + else // cf.: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return -1; // corrupted character or a character with > 4 bytes utf-8 representation + + for (int i = 0; i < remaining_unicode_length; i++) + { + text++; + if (((*text) & 0xc0) != 0x80) + { + remaining_unicode_length = -1; + return -1; // incomplete or corrupted character + } + unicode_value <<= 6; + unicode_value |= ((unsigned char)(*text)) & 0x3f; + } + if (remaining_unicode_length == -1) + return -1; // incomplete or corrupted character + } + else + unicode_value = (unsigned char)(*text); + + return unicode_value; +} + +void Font::RenderString(int x, int y, const int width, const char *text, const unsigned char color, const int boxheight, const bool utf8_encoded) +{ + if (!frameBuffer->getActive()) + return; + + pthread_mutex_lock( &renderer->render_mutex ); + + if (FTC_Manager_LookupSize(renderer->cacheManager, &scaler, &size)<0) + { + dprintf(DEBUG_NORMAL, "FTC_Manager_Lookup_Size failed!\n"); + return; + } + face = size->face; + + int use_kerning=FT_HAS_KERNING(face); + + int left=x; + int step_y=height; + + // ----------------------------------- box upper end (this is NOT a font metric, this is our method for y centering) + // + // ** -------------------------------- y=baseline-upper + // || + // |u --------------------*----------- y=baseline+ascender + // |p * * + // hp * * + // ee * * * * + // ir * * ******* + // g| * * * * + // h| * * * * + // t* -------**--------*-----*-------- y=baseline + // |l * + // |o * + // |w -----**------------------------- y=baseline+descender // descender is a NEGATIVE value + // |r + // ** -------------------------------- y=baseline+lower == YCALLER + // + // ----------------------------------- box lower end (this is NOT a font metric, this is our method for y centering) + + // height = ascender + -1*descender + linegap // descender is negative! + + // now we adjust the given y value (which is the line marked as YCALLER) to be the baseline after adjustment: + y -= lower; + // y coordinate now gives font baseline which is used for drawing + + // caution: this only works if we print a single line of text + // if we have multiple lines, don't use boxheight or specify boxheight==0. + // if boxheight is !=0, we further adjust y, so that text is y-centered in the box + if(boxheight) + { + if(boxheight>step_y) // this is a real box (bigger than text) + y -= ((boxheight-step_y)>>1); + else if(boxheight<0) // this normally would be wrong, so we just use it to define a "border" + y += (boxheight>>1); // half of border value at lower end, half at upper end + } + + int lastindex=0; // 0 == missing glyph (never has kerning values) + FT_Vector kerning; + int pen1=-1; // "pen" positions for kerning, pen2 is "x" + + static fb_pixel_t oldbgcolor = 0, oldfgcolor = 0; + static fb_pixel_t colors[256]; + + fb_pixel_t bgcolor = frameBuffer->realcolor[color]; + fb_pixel_t fgcolor = frameBuffer->realcolor[(((((int)color) + 2) | 7) - 2)]; + + if((oldbgcolor != bgcolor) || (oldfgcolor != fgcolor)) { + + oldbgcolor = bgcolor; + oldfgcolor = fgcolor; + t_fb_var_screeninfo * screeninfo = frameBuffer->getScreenInfo(); + int rl = screeninfo->red.length; + int ro = screeninfo->red.offset; + int gl = screeninfo->green.length; + int go = screeninfo->green.offset; + int bl = screeninfo->blue.length; + int bo = screeninfo->blue.offset; + int tl = screeninfo->transp.length; + int to = screeninfo->transp.offset; + int fgr = (((int)fgcolor >> ro) & ((1 << rl) - 1)); + int fgg = (((int)fgcolor >> go) & ((1 << gl) - 1)); + int fgb = (((int)fgcolor >> bo) & ((1 << bl) - 1)); + int fgt = (((int)fgcolor >> to) & ((1 << tl) - 1)); + int deltar = (((int)bgcolor >> ro) & ((1 << rl) - 1)) - fgr; + int deltag = (((int)bgcolor >> go) & ((1 << gl) - 1)) - fgg; + int deltab = (((int)bgcolor >> bo) & ((1 << bl) - 1)) - fgb; + int deltat = (((int)bgcolor >> to) & ((1 << tl) - 1)) - fgt; + + + for (int i = 0; i < 256; i++) { + colors[255 - i] = + ((((fgr + deltar * i / 255) & ((1 << rl) - 1)) << ro) | + (((fgg + deltag * i / 255) & ((1 << gl) - 1)) << go) | + (((fgb + deltab * i / 255) & ((1 << bl) - 1)) << bo) | + (((fgt + deltat * i / 255) & ((1 << tl) - 1)) << to)); + } + } + + int spread_by = 0; + if (stylemodifier == Font::Embolden) + { + spread_by = (fontwidth / 6) - 1; + if (spread_by < 1) + spread_by = 1; + } + + for (; *text; text++) + { + FTC_SBit glyph; + if (*text=='\n') + { + x = left; + y += step_y; + } + + int unicode_value = UTF8ToUnicode(text, utf8_encoded); + + if (unicode_value == -1) + break; + + int index = FT_Get_Char_Index(face, unicode_value); + + if (!index) + continue; + if (getGlyphBitmap(index, &glyph)) + { + dprintf(DEBUG_NORMAL, "failed to get glyph bitmap.\n"); + continue; + } + + // width clip + if (x + glyph->xadvance + spread_by > left + width) + break; + + //kerning + if(use_kerning) + { + FT_Get_Kerning(face,lastindex,index,0,&kerning); + //x+=(kerning.x+32)>>6; // kerning! + x+=(kerning.x)>>6; // kerning! + } + + #ifndef USE_NEVIS_GXA + int stride = frameBuffer->getStride(); + uint8_t * d = ((uint8_t *)frameBuffer->getFrameBufferPointer()) + (x + glyph->left) * sizeof(fb_pixel_t) + stride * (y - glyph->top); + #endif + uint8_t * s = glyph->buffer; + int w = glyph->width; + int h = glyph->height; + int pitch = glyph->pitch; + + for (int ay=0; aypaintPixel(x + glyph->left + ax, y - glyph->top + ay, colors[*s++]); + #else + *td++= colors[*s++]; + #endif + } + else + { + int start, end; + int color = -1; + + if (ax < w) + start = 0; + else + start = ax - w + 1; + + if (ax < spread_by) + end = ax + 1; + else + end = spread_by + 1; + + for (int i = start; i < end; i++) + if (color < *(s - i)) + color = *(s - i); + #ifdef USE_NEVIS_GXA + frameBuffer->paintPixel(x + glyph->left + ax, y - glyph->top + ay, colors[color]); + #else + *td++= colors[color]; + #endif + s++; + } + } + s += pitch- ax; + #ifndef USE_NEVIS_GXA + d += stride; + #endif + } + x+=glyph->xadvance+1; + //x+=glyph->xadvance; + if(pen1>x) + x=pen1; + pen1=x; + lastindex=index; + } +//printf("RenderStat: %d %d %d \n", renderer->cacheManager->num_nodes, renderer->cacheManager->num_bytes, renderer->cacheManager->max_bytes); + pthread_mutex_unlock( &renderer->render_mutex ); +} + +void Font::RenderString(int x, int y, const int width, const std::string & text, const unsigned char color, const int boxheight, const bool utf8_encoded) +{ + RenderString(x, y, width, text.c_str(), color, boxheight, utf8_encoded); +} + +int Font::getRenderWidth(const char *text, const bool utf8_encoded) +{ + pthread_mutex_lock( &renderer->render_mutex ); + + if (FTC_Manager_LookupSize(renderer->cacheManager, &scaler, &size)<0) + { + dprintf(DEBUG_NORMAL, "FTC_Manager_Lookup_Size failed!\n"); + return -1; + } + face = size->face; + + int use_kerning=FT_HAS_KERNING(face); + int x=0; + int lastindex=0; // 0==missing glyph (never has kerning) + FT_Vector kerning; + int pen1=-1; // "pen" positions for kerning, pen2 is "x" + for (; *text; text++) + { + FTC_SBit glyph; + + int unicode_value = UTF8ToUnicode(text, utf8_encoded); + + if (unicode_value == -1) + break; + + int index=FT_Get_Char_Index(face, unicode_value); + + if (!index) + continue; + if (getGlyphBitmap(index, &glyph)) + { + dprintf(DEBUG_NORMAL, "failed to get glyph bitmap.\n"); + continue; + } + //kerning + if(use_kerning) + { + FT_Get_Kerning(face,lastindex,index,0,&kerning); + x+=(kerning.x+32)>>6; // kerning! + } + + x+=glyph->xadvance+1; + if(pen1>x) + x=pen1; + pen1=x; + lastindex=index; + } + + if (stylemodifier == Font::Embolden) + { + int spread_by = (fontwidth / 6) - 1; + if (spread_by < 1) + spread_by = 1; + + x += spread_by; + } + + pthread_mutex_unlock( &renderer->render_mutex ); + + return x; +} + +int Font::getRenderWidth(const std::string & text, const bool utf8_encoded) +{ + return getRenderWidth(text.c_str(), utf8_encoded); +} diff --git a/src/driver/fontrenderer.h b/src/driver/fontrenderer.h new file mode 100644 index 000000000..17c225f87 --- /dev/null +++ b/src/driver/fontrenderer.h @@ -0,0 +1,124 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Copyright (C) 2003 thegoodguy + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __FONTRENDERER__ +#define __FONTRENDERER__ + +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_CACHE_H + +#include FT_CACHE_IMAGE_H +#include FT_CACHE_SMALL_BITMAPS_H + +#include "framebuffer.h" + + +class FBFontRenderClass; +class Font +{ + CFrameBuffer *frameBuffer; + FTC_ImageTypeRec font; + FBFontRenderClass *renderer; + FT_Face face; + FT_Size size; + FTC_ScalerRec scaler; + + FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit); + + // these are HACKED values, because the font metrics were unusable. + int height,ascender,descender,upper,lower; + int fontwidth; + + public: + enum fontmodifier + { + Regular, + Embolden + }; + fontmodifier stylemodifier; + + void RenderString(int x, int y, const int width, const char * text, const unsigned char color, const int boxheight = 0, const bool utf8_encoded = false); + void RenderString(int x, int y, const int width, const std::string & text, const unsigned char color, const int boxheight = 0, const bool utf8_encoded = false); + + int getRenderWidth(const char * text, const bool utf8_encoded = false); + int getRenderWidth(const std::string & text, const bool utf8_encoded = false); + int getHeight(void); + int getSize(){return font.width;} + int setSize(int isize); + + Font(FBFontRenderClass *render, FTC_FaceID faceid, const int isize, const fontmodifier _stylemodifier); + ~Font(){} +}; + +class FBFontRenderClass +{ + struct fontListEntry + { + char *filename, *style, *family; + fontListEntry *next; + ~fontListEntry(); + } + *font; + + FT_Library library; + FTC_Manager cacheManager; /* the cache manager */ + FTC_ImageCache imageCache; /* the glyph image cache */ + FTC_SBitCache sbitsCache; /* the glyph small bitmaps cache */ + + FTC_FaceID getFaceID(const char * const family, const char * const style); + FT_Error getGlyphBitmap(FTC_ImageTypeRec *font, FT_ULong glyph_index, FTC_SBit *sbit); + FT_Error getGlyphBitmap(FTC_ScalerRec *sc, FT_ULong glyph_index, FTC_SBit *sbit); + + int xres; /* the screen resolution in dpi */ + int yres; /* defaults to 72 dpi */ + + public: + pthread_mutex_t render_mutex; + + FT_Error FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface); + + + static FT_Error myFTC_Face_Requester(FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face* aface); + + //FT_Face getFace(const char *family, const char *style); + Font *getFont(const char * const family, const char * const style, int size); + + std::string getFamily(const char * const filename) const; + + const char * const AddFont(const char * const filename, const bool make_italics = false); + + FBFontRenderClass(const int xres = 72, const int yres = 72); + ~FBFontRenderClass(); + + friend class Font; +}; + +#endif diff --git a/src/driver/framebuffer.cpp b/src/driver/framebuffer.cpp new file mode 100644 index 000000000..b57ec4723 --- /dev/null +++ b/src/driver/framebuffer.cpp @@ -0,0 +1,1409 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + 2003 thegoodguy + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +//#include +#include +extern cVideo * videoDecoder; + +extern CPictureViewer * g_PicViewer; +#define BACKGROUNDIMAGEWIDTH 720 + +//#undef USE_NEVIS_GXA //FIXME +/*******************************************************************************/ +#ifdef USE_NEVIS_GXA + +#ifdef GXA_FG_COLOR_REG +#undef GXA_FG_COLOR_REG +#endif +#ifdef GXA_BG_COLOR_REG +#undef GXA_BG_COLOR_REG +#endif +#ifdef GXA_LINE_CONTROL_REG +#undef GXA_LINE_CONTROL_REG +#endif +#ifdef GXA_DEPTH_REG +#undef GXA_DEPTH_REG +#endif +#ifdef GXA_CONTENT_ID_REG +#undef GXA_CONTENT_ID_REG +#endif + +#define GXA_POINT(x, y) (((y) & 0x0FFF) << 16) | ((x) & 0x0FFF) +#define GXA_SRC_BMP_SEL(x) (x << 8) +#define GXA_DST_BMP_SEL(x) (x << 5) +#define GXA_PARAM_COUNT(x) (x << 2) + +#define GXA_FG_COLOR_REG 0x0020 +#define GXA_BG_COLOR_REG 0x0024 +#define GXA_LINE_CONTROL_REG 0x0038 +#define GXA_BMP2_TYPE_REG 0x0050 +#define GXA_BMP2_ADDR_REG 0x0054 +#define GXA_DEPTH_REG 0x00F4 +#define GXA_CONTENT_ID_REG 0x0144 + +#define GXA_CMD_BLT 0x00010800 +#define GXA_CMD_NOT_ALPHA 0x00011000 +#define GXA_CMD_NOT_TEXT 0x00018000 + +/* +static unsigned int _read_gxa(volatile unsigned char *base_addr, unsigned int offset) +{ + return *(volatile unsigned int *)(base_addr + offset); +} +*/ +static void _write_gxa(volatile unsigned char *base_addr, unsigned int offset, unsigned int value) +{ + while( (*(volatile unsigned int *)(base_addr + GXA_DEPTH_REG)) & 0x40000000); + *(volatile unsigned int *)(base_addr + offset) = value; +} + +#endif /* USE_NEVIS_GXA */ + +/*******************************************************************************/ + +static uint8_t * virtual_fb = NULL; +inline unsigned int make16color(uint16_t r, uint16_t g, uint16_t b, uint16_t t, + uint32_t rl = 0, uint32_t ro = 0, + uint32_t gl = 0, uint32_t go = 0, + uint32_t bl = 0, uint32_t bo = 0, + uint32_t tl = 0, uint32_t to = 0) +{ + return ((t << 24) & 0xFF000000) | ((r << 8) & 0xFF0000) | ((g << 0) & 0xFF00) | (b >> 8 & 0xFF); +} + +CFrameBuffer::CFrameBuffer() +: active ( true ) +{ + iconBasePath = ""; + available = 0; + cmap.start = 0; + cmap.len = 256; + cmap.red = red; + cmap.green = green; + cmap.blue = blue; + cmap.transp = trans; + backgroundColor = 0; + useBackgroundPaint = false; + background = NULL; + backupBackground = NULL; + backgroundFilename = ""; + fd = 0; + tty = 0; +//FIXME: test + memset(red, 0, 256*sizeof(__u16)); + memset(green, 0, 256*sizeof(__u16)); + memset(blue, 0, 256*sizeof(__u16)); + memset(trans, 0, 256*sizeof(__u16)); +} + +CFrameBuffer* CFrameBuffer::getInstance() +{ + static CFrameBuffer* frameBuffer = NULL; + + if(!frameBuffer) { + frameBuffer = new CFrameBuffer(); + printf("[neutrino] frameBuffer Instance created\n"); + } else { + //printf("[neutrino] frameBuffer Instace requested\n"); + } + return frameBuffer; +} + +void CFrameBuffer::init(const char * const fbDevice) +{ + fd = open(fbDevice, O_RDWR); + if(!fd) fd = open(fbDevice, O_RDWR); + + if (fd<0) { + perror(fbDevice); + goto nolfb; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo)<0) { + perror("FBIOGET_VSCREENINFO"); + goto nolfb; + } + + memcpy(&oldscreen, &screeninfo, sizeof(screeninfo)); + + fb_fix_screeninfo fix; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) { + perror("FBIOGET_FSCREENINFO"); + goto nolfb; + } + + available=fix.smem_len; + printf("%dk video mem\n", available/1024); + lfb=(fb_pixel_t*)mmap(0, available, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + + if (!lfb) { + perror("mmap"); + goto nolfb; + } + +#ifdef USE_NEVIS_GXA + /* Open 7dev/mem for HW-register access */ + devmem_fd = open("/dev/mem", O_RDWR | O_SYNC); + if (devmem_fd < 0) { + perror("Unable to open /dev/mem"); + goto nolfb; + } + + /* mmap the GXA's base address */ + gxa_base = (volatile unsigned char*) mmap(0, 0x00040000, PROT_READ | PROT_WRITE, MAP_SHARED, devmem_fd, 0xE0600000); + if (gxa_base == (void*) -1){ + perror("Unable to mmap /dev/mem"); + goto nolfb; + } + + /* tell the GXA where the framebuffer to draw on starts */ + smem_start = (unsigned int) fix.smem_start; + _write_gxa(gxa_base, GXA_BMP2_TYPE_REG, (3 << 16) | screeninfo.xres); + _write_gxa(gxa_base, GXA_BMP2_ADDR_REG, (unsigned int) fix.smem_start); + _write_gxa(gxa_base, GXA_CONTENT_ID_REG, 0); +#endif + +#if 0 + if ((tty=open("/dev/vc/0", O_RDWR))<0) { + perror("open (tty)"); + goto nolfb; + } + + struct sigaction act; + + memset(&act,0,sizeof(act)); + act.sa_handler = switch_signal; + sigemptyset(&act.sa_mask); + sigaction(SIGUSR1,&act,NULL); + sigaction(SIGUSR2,&act,NULL); + + struct vt_mode mode; + + if (-1 == ioctl(tty,KDGETMODE, &kd_mode)) { + perror("ioctl KDGETMODE"); + goto nolfb; + } + + if (-1 == ioctl(tty,VT_GETMODE, &vt_mode)) { + perror("ioctl VT_GETMODE"); + goto nolfb; + } + + if (-1 == ioctl(tty,VT_GETMODE, &mode)) { + perror("ioctl VT_GETMODE"); + goto nolfb; + } + + mode.mode = VT_PROCESS; + mode.waitv = 0; + mode.relsig = SIGUSR1; + mode.acqsig = SIGUSR2; + + if (-1 == ioctl(tty,VT_SETMODE, &mode)) { + perror("ioctl VT_SETMODE"); + goto nolfb; + } + + if (-1 == ioctl(tty,KDSETMODE, KD_GRAPHICS)) { + perror("ioctl KDSETMODE"); + goto nolfb; + } +#endif + + return; + +nolfb: + printf("framebuffer not available.\n"); + lfb=0; +} + + +CFrameBuffer::~CFrameBuffer() +{ + if (background) { + delete[] background; + } + + if (backupBackground) { + delete[] backupBackground; + } + +#if 0 +#ifdef RETURN_FROM_GRAPHICS_MODE + if (-1 == ioctl(tty,KDSETMODE, kd_mode)) + perror("ioctl KDSETMODE"); +#endif + + if (-1 == ioctl(tty,VT_SETMODE, &vt_mode)) + perror("ioctl VT_SETMODE"); + + if (available) + ioctl(fd, FBIOPUT_VSCREENINFO, &oldscreen); +#endif + if (lfb) + munmap(lfb, available); + + if (virtual_fb) + delete[] virtual_fb; + close(fd); + close(tty); +} + +int CFrameBuffer::getFileHandle() const +{ + return fd; +} + +unsigned int CFrameBuffer::getStride() const +{ + return stride; +} + +unsigned int CFrameBuffer::getScreenWidth(bool real) +{ + if(real) + return xRes; + else + return g_settings.screen_EndX - g_settings.screen_StartX; +} + +unsigned int CFrameBuffer::getScreenHeight(bool real) +{ + if(real) + return yRes; + else + return g_settings.screen_EndY - g_settings.screen_StartY; +} + +unsigned int CFrameBuffer::getScreenX() +{ + return g_settings.screen_StartX; +} + +unsigned int CFrameBuffer::getScreenY() +{ + return g_settings.screen_StartY; +} + +fb_pixel_t * CFrameBuffer::getFrameBufferPointer() const +{ + if (active || (virtual_fb == NULL)) + return lfb; + else + return (fb_pixel_t *) virtual_fb; +} + +bool CFrameBuffer::getActive() const +{ + return (active || (virtual_fb != NULL)); +} + +void CFrameBuffer::setActive(bool enable) +{ + active = enable; +} + +t_fb_var_screeninfo *CFrameBuffer::getScreenInfo() +{ + return &screeninfo; +} + +int CFrameBuffer::setMode(unsigned int nxRes, unsigned int nyRes, unsigned int nbpp) +{ + if (!available&&!active) + return -1; + +#if 0 + screeninfo.xres_virtual=screeninfo.xres=nxRes; + screeninfo.yres_virtual=screeninfo.yres=nyRes; + screeninfo.height=0; + screeninfo.width=0; + screeninfo.xoffset=screeninfo.yoffset=0; + screeninfo.bits_per_pixel=nbpp; + + if (ioctl(fd, FBIOPUT_VSCREENINFO, &screeninfo)<0) { + perror("FBIOPUT_VSCREENINFO"); + } + + if(1) { + printf("SetMode: %dbits, red %d:%d green %d:%d blue %d:%d transp %d:%d\n", + screeninfo.bits_per_pixel, screeninfo.red.length, screeninfo.red.offset, screeninfo.green.length, screeninfo.green.offset, screeninfo.blue.length, screeninfo.blue.offset, screeninfo.transp.length, screeninfo.transp.offset); + } + if ((screeninfo.xres!=nxRes) && (screeninfo.yres!=nyRes) && (screeninfo.bits_per_pixel!=nbpp)) + { + printf("SetMode failed: wanted: %dx%dx%d, got %dx%dx%d\n", + nxRes, nyRes, nbpp, + screeninfo.xres, screeninfo.yres, screeninfo.bits_per_pixel); + return -1; + } +#endif + + xRes = screeninfo.xres; + yRes = screeninfo.yres; + bpp = screeninfo.bits_per_pixel; + fb_fix_screeninfo fix; + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) { + perror("FBIOGET_FSCREENINFO"); + return -1; + } + + stride = fix.line_length; +printf("FB: %dx%dx%d line lenght %d\n", xRes, yRes, bpp, stride); + + memset(getFrameBufferPointer(), 0, stride * yRes); + if (ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK) < 0) { + printf("screen unblanking failed\n"); + } + return 0; +} + +void CFrameBuffer::setTransparency( int tr ) +{ +} + +void CFrameBuffer::setBlendLevel(int blev1, int blev2) +{ + unsigned short value = blev1; +#if 0 + if (ioctl(fd, FBIO_CHANGEOPACITY, &value) < 0) + printf("FBIO_CHANGEOPACITY failed.\n"); +#endif +} + +void CFrameBuffer::setAlphaFade(int in, int num, int tr) +{ + for (int i=0; i>16)*level; + *g= ((rgb2&0x00FF00)>>8 )*level; + *b= ((rgb2&0x0000FF) )*level; + *r+=((rgb1&0xFF0000)>>16)*(255-level); + *g+=((rgb1&0x00FF00)>>8 )*(255-level); + *b+=((rgb1&0x0000FF) )*(255-level); +} + +void CFrameBuffer::paletteGenFade(int in, __u32 rgb1, __u32 rgb2, int num, int tr) +{ + for (int i=0; i>8; + cmap.green[i] =(rgb&0x00FF00) ; + cmap.blue[i] =(rgb&0x0000FF)<<8; + cmap.transp[i] = tr; +} + +void CFrameBuffer::paletteSet(struct fb_cmap *map) +{ + if (!active) + return; + + if(map == NULL) + map = &cmap; + + if(bpp == 8) { +//printf("Set palette for %dbit\n", bpp); + ioctl(fd, FBIOPUTCMAP, map); + } + uint32_t rl, ro, gl, go, bl, bo, tl, to; + rl = screeninfo.red.length; + ro = screeninfo.red.offset; + gl = screeninfo.green.length; + go = screeninfo.green.offset; + bl = screeninfo.blue.length; + bo = screeninfo.blue.offset; + tl = screeninfo.transp.length; + to = screeninfo.transp.offset; + for (int i = 0; i < 256; i++) { + realcolor[i] = make16color(cmap.red[i], cmap.green[i], cmap.blue[i], cmap.transp[i], + rl, ro, gl, go, bl, bo, tl, to); + } +} + +#if 0 +void CFrameBuffer::paintBoxRel(const int x, const int y, const int dx, const int dy, const fb_pixel_t col) +{ + if (!getActive()) + return; + + uint8_t * pos = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + for (int count = 0; count < dy; count++) { + fb_pixel_t * dest = (fb_pixel_t *)pos; + for (int i = 0; i < dx; i++) + *(dest++) = col; + pos += stride; + } +} +#endif + +void CFrameBuffer::paintBoxRel(const int x, const int y, const int dx, const int dy, const fb_pixel_t col, int radius, int type) +{ + /* draw a filled rectangle (with additional round corners) */ + + if (!getActive()) + return; + +#ifdef USE_NEVIS_GXA + /* this table contains the x coordinates for a quarter circle (the bottom right quarter) with fixed + radius of 540 px which is the half of the max HD graphics size of 1080 px. So with that table we + ca draw boxes with round corners and als circles by just setting dx = dy = radius (max 540). */ + int q_circle[541] = { + 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, + 540, 540, 540, 540, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, + 539, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 537, 537, 537, 537, 537, 537, 537, + 537, 537, 536, 536, 536, 536, 536, 536, 536, 536, 535, 535, 535, 535, 535, 535, 535, 535, 534, 534, + 534, 534, 534, 534, 533, 533, 533, 533, 533, 533, 532, 532, 532, 532, 532, 532, 531, 531, 531, 531, + 531, 531, 530, 530, 530, 530, 529, 529, 529, 529, 529, 529, 528, 528, 528, 528, 527, 527, 527, 527, + 527, 526, 526, 526, 526, 525, 525, 525, 525, 524, 524, 524, 524, 523, 523, 523, 523, 522, 522, 522, + 522, 521, 521, 521, 521, 520, 520, 520, 519, 519, 519, 518, 518, 518, 518, 517, 517, 517, 516, 516, + 516, 515, 515, 515, 515, 514, 514, 514, 513, 513, 513, 512, 512, 512, 511, 511, 511, 510, 510, 510, + 509, 509, 508, 508, 508, 507, 507, 507, 506, 506, 506, 505, 505, 504, 504, 504, 503, 503, 502, 502, + 502, 501, 501, 500, 500, 499, 499, 499, 498, 498, 498, 497, 497, 496, 496, 496, 495, 495, 494, 494, + 493, 493, 492, 492, 491, 491, 490, 490, 490, 489, 489, 488, 488, 487, 487, 486, 486, 485, 485, 484, + 484, 483, 483, 482, 482, 481, 481, 480, 480, 479, 479, 478, 478, 477, 477, 476, 476, 475, 475, 474, + 473, 473, 472, 472, 471, 471, 470, 470, 469, 468, 468, 467, 466, 466, 465, 465, 464, 464, 463, 462, + 462, 461, 460, 460, 459, 459, 458, 458, 457, 456, 455, 455, 454, 454, 453, 452, 452, 451, 450, 450, + 449, 449, 448, 447, 446, 446, 445, 445, 444, 443, 442, 441, 441, 440, 440, 439, 438, 437, 436, 436, + 435, 435, 434, 433, 432, 431, 431, 430, 429, 428, 427, 427, 426, 425, 425, 424, 423, 422, 421, 421, + 420, 419, 418, 417, 416, 416, 415, 414, 413, 412, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, + 403, 402, 401, 400, 399, 398, 397, 397, 395, 394, 393, 393, 392, 391, 390, 389, 388, 387, 386, 385, + 384, 383, 382, 381, 380, 379, 378, 377, 376, 375, 374, 373, 372, 371, 369, 368, 367, 367, 365, 364, + 363, 362, 361, 360, 358, 357, 356, 355, 354, 353, 352, 351, 350, 348, 347, 346, 345, 343, 342, 341, + 340, 339, 337, 336, 335, 334, 332, 331, 329, 328, 327, 326, 324, 323, 322, 321, 319, 317, 316, 315, + 314, 312, 310, 309, 308, 307, 305, 303, 302, 301, 299, 297, 296, 294, 293, 291, 289, 288, 287, 285, + 283, 281, 280, 278, 277, 275, 273, 271, 270, 268, 267, 265, 263, 261, 259, 258, 256, 254, 252, 250, + 248, 246, 244, 242, 240, 238, 236, 234, 232, 230, 228, 225, 223, 221, 219, 217, 215, 212, 210, 207, + 204, 202, 200, 197, 195, 192, 190, 187, 184, 181, 179, 176, 173, 170, 167, 164, 160, 157, 154, 150, + 147, 144, 140, 136, 132, 128, 124, 120, 115, 111, 105, 101, 95, 89, 83, 77, 69, 61, 52, 40, + 23}; + + int line = 0; + unsigned int cmd = GXA_CMD_NOT_TEXT | GXA_SRC_BMP_SEL(2) | GXA_DST_BMP_SEL(2) | GXA_PARAM_COUNT(2) | GXA_CMD_NOT_ALPHA; + + _write_gxa(gxa_base, GXA_FG_COLOR_REG, (unsigned int) col); /* setup the drawing color */ + _write_gxa(gxa_base, GXA_LINE_CONTROL_REG, 0x00000404); /* X is major axis, skip last pixel */ + + if ((type) && (radius)) + { + #define MUL 32768 /* just an multiplicator for all math to reduce rounding errors */ + int ofs, scf, scl; + + /* limit the radius */ + if (radius > dx) + radius = dx; + if (radius > dy) + radius = dy; + if (radius > 540) + radius = 540; + + scf = (540 * MUL) / radius; + + while (line < dy) + { + ofs = 0; + + if ((line < radius) && (type & 1)) + { + /* uper round corners */ + scl = scf * (radius - line) / MUL; + if ((scf * (radius - line) % MUL) >= (MUL / 2)) /* round up */ + scl++; + ofs = radius - (q_circle[scl] * MUL / scf); + } + else if ((line >= (dy - radius)) && (type & 2)) + { + /* lower round corners */ + scl = scf * (radius - (dy - (line + 1))) / MUL; + if ((scf * (radius - (dy - (line + 1))) % MUL) >= (MUL / 2)) /* round up */ + scl++; + ofs = radius - (q_circle[scl] * MUL / scf); + } + _write_gxa(gxa_base, cmd, GXA_POINT(x + dx - ofs, y + line)); /* endig point */ + _write_gxa(gxa_base, cmd, GXA_POINT(x + ofs, y + line)); /* start point */ + + line++; + } + } + else + { + while (line < dy) + { + _write_gxa(gxa_base, cmd, GXA_POINT(x + dx, y + line)); /* endig point */ + _write_gxa(gxa_base, cmd, GXA_POINT(x, y + line)); /* start point */ + line++; + } + } + +#else + int F,R=radius,sx,sy,dxx=dx,dyy=dy,rx,ry,wx,wy; + + if (!getActive()) + return; + + uint8_t *pos = ((uint8_t *)getFrameBufferPointer()) + x*sizeof(fb_pixel_t) + stride*y; + uint8_t *pos0 = 0, *pos1 = 0, *pos2 = 0, *pos3 = 0; + + fb_pixel_t *dest0, *dest1; + + if(R) { + if(--dyy<=0) { + //if(dyy <= 0) + dyy=1; + } + + if(R==1 || R>(dxx/2) || R>(dyy/2)) { + R=dxx/10; + F=dyy/10; + if(R>F) { + if(R>(dyy/3)) { + R=dyy/3; + } + } else { + R=F; + if(R>(dxx/3)) { + R=dxx/3; + } + } + } + sx=0; + sy=R; + F=1-R; + + rx=R-sx; + ry=R-sy; + + if(type & 1) { + pos1=pos+(ry*stride); // top 1 + pos2=pos+(rx*stride); // top 2 + } + if(type & 2) { + pos0=pos+((dyy-ry)*stride); // bottom 1 + pos3=pos+((dyy-rx)*stride); // bottom 2 + } + while (sx <= sy) { + rx=R-sx; + ry=R-sy; + wx=rx<<1; + wy=ry<<1; + dest0=(fb_pixel_t *)(pos0+rx*sizeof(fb_pixel_t)); + dest1=(fb_pixel_t *)(pos1+rx*sizeof(fb_pixel_t)); + for (int i=0; i<(dxx-wx); i++) { + if(type & 2) + *(dest0++)=col; //bottom 1 + if(type & 1) + *(dest1++)=col; // top 1 + } + dest0=(fb_pixel_t *)(pos2+ry*sizeof(fb_pixel_t)); + dest1=(fb_pixel_t *)(pos3+ry*sizeof(fb_pixel_t)); + for (int i=0; i<(dxx-wy); i++) { + if(type & 1) + *(dest0++)=col; // top 2 + if(type & 2) + *(dest1++)=col; //bottom 2 + } + sx++; + pos2-=stride; + pos3+=stride; + if (F<0) { + F+=(sx<<1)-1; + } else { + F+=((sx-sy)<<1); + sy--; + pos0-=stride; + pos1+=stride; + } + } + if(type & 1) + pos+=R*stride; + } + + int start = R; + int end = dyy - R; + if(!(type & 1)) + start = 0; + if(!(type & 2)) + end = dyy+ (R ? 1 : 0); + +#if 0 + fb_fillrect fillrect; + fillrect.dx = x; + fillrect.dy = y+start; + fillrect.width = dx; + fillrect.height = end; + fillrect.color = col; + fillrect.rop = ROP_COPY; + ioctl(fd, FBIO_FILL_RECT, &fillrect); +#else + for (int count= start; count < end; count++) { + dest0=(fb_pixel_t *)pos; + for (int i=0; iDisplayImage(newname, x, y, 0, 0); + } + struct rawHeader header; + uint16_t width, height; + int fd; +#if 0 // no need if we have whole / as r/w + std::string iconBasePath1 = "/var/share/icons/"; + fd = open((iconBasePath1 + filename).c_str(), O_RDONLY); + if (fd == -1) + fd = open((iconBasePath + filename).c_str(), O_RDONLY); +#endif + fd = open((iconBasePath + filename).c_str(), O_RDONLY); + + if (fd == -1) { + printf("paintIcon: error while loading icon: %s%s\n", iconBasePath.c_str(), filename.c_str()); + return false; + } + + read(fd, &header, sizeof(struct rawHeader)); + + width = (header.width_hi << 8) | header.width_lo; + height = (header.height_hi << 8) | header.height_lo; + + unsigned char pixbuf[768]; + uint8_t * d = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + fb_pixel_t * d2; + for (int count=0; count> 1 ); + unsigned char *pixpos = &pixbuf[0]; + d2 = (fb_pixel_t *) d; + for (int count2=0; count2> 1; count2 ++ ) { + unsigned char compressed = *pixpos; + unsigned char pix1 = (compressed & 0xf0) >> 4; + unsigned char pix2 = (compressed & 0x0f); + if (pix1 != header.transp) { + paintPixel(d2, pix1 + offset); + } + d2++; + if (pix2 != header.transp) { + paintPixel(d2, pix2 + offset); + } + d2++; + pixpos++; + } + d += stride; + } + + close(fd); + return true; +} + +bool CFrameBuffer::paintIcon(const char * const filename, const int x, const int y, const unsigned char offset) +{ +//printf("%s(%s, %d, %d, %d)\n", __FUNCTION__, filename, x, y, offset); + return paintIcon(std::string(filename), x, y, offset); +} + +void CFrameBuffer::loadPal(const std::string & filename, const unsigned char offset, const unsigned char endidx) +{ + if (!getActive()) + return; + +//printf("%s()\n", __FUNCTION__); + + struct rgbData rgbdata; + int fd; + + fd = open((iconBasePath + filename).c_str(), O_RDONLY); + + if (fd == -1) { + printf("error while loading palette: %s%s\n", iconBasePath.c_str(), filename.c_str()); + return; + } + + int pos = 0; + int readb = read(fd, &rgbdata, sizeof(rgbdata) ); + while(readb) { + __u32 rgb = (rgbdata.r<<16) | (rgbdata.g<<8) | (rgbdata.b); + int colpos = offset+pos; + if( colpos>endidx) + break; + + paletteSetColor(colpos, rgb, 0xFF); + readb = read(fd, &rgbdata, sizeof(rgbdata) ); + pos++; + } + paletteSet(&cmap); + close(fd); +} + +void CFrameBuffer::paintPixel(const int x, const int y, const fb_pixel_t col) +{ + if (!getActive()) + return; + + #ifdef USE_NEVIS_GXA + paintHLineRel(x, 1, y, col); + #else + fb_pixel_t * pos = getFrameBufferPointer(); + pos += (stride / sizeof(fb_pixel_t)) * y; + pos += x; + + *pos = col; + #endif +} + +void CFrameBuffer::paintLine(int xa, int ya, int xb, int yb, const fb_pixel_t col) +{ + if (!getActive()) + return; + +//printf("%s(%d, %d, %d, %d, %.8X)\n", __FUNCTION__, xa, ya, xb, yb, col); + + int dx = abs (xa - xb); + int dy = abs (ya - yb); + int x; + int y; + int End; + int step; + + if ( dx > dy ) + { + int p = 2 * dy - dx; + int twoDy = 2 * dy; + int twoDyDx = 2 * (dy-dx); + + if ( xa > xb ) + { + x = xb; + y = yb; + End = xa; + step = ya < yb ? -1 : 1; + } + else + { + x = xa; + y = ya; + End = xb; + step = yb < ya ? -1 : 1; + } + + paintPixel (x, y, col); + + while( x < End ) + { + x++; + if ( p < 0 ) + p += twoDy; + else + { + y += step; + p += twoDyDx; + } + paintPixel (x, y, col); + } + } + else + { + int p = 2 * dx - dy; + int twoDx = 2 * dx; + int twoDxDy = 2 * (dx-dy); + + if ( ya > yb ) + { + x = xb; + y = yb; + End = ya; + step = xa < xb ? -1 : 1; + } + else + { + x = xa; + y = ya; + End = yb; + step = xb < xa ? -1 : 1; + } + + paintPixel (x, y, col); + + while( y < End ) + { + y++; + if ( p < 0 ) + p += twoDx; + else + { + x += step; + p += twoDxDy; + } + paintPixel (x, y, col); + } + } +} + +void CFrameBuffer::setBackgroundColor(const fb_pixel_t color) +{ + backgroundColor = color; +} + +bool CFrameBuffer::loadPictureToMem(const std::string & filename, const uint16_t width, const uint16_t height, const uint16_t stride, fb_pixel_t * memp) +{ + struct rawHeader header; + int fd; + +//printf("%s(%d, %d, memp)\n", __FUNCTION__, width, height); + + fd = open((iconBasePath + filename).c_str(), O_RDONLY ); + + if (fd == -1) + { + printf("error while loading icon: %s%s\n", iconBasePath.c_str(), filename.c_str()); + return false; + } + + read(fd, &header, sizeof(struct rawHeader)); + + if ((width != ((header.width_hi << 8) | header.width_lo)) || + (height != ((header.height_hi << 8) | header.height_lo))) + { + printf("error while loading icon: %s - invalid resolution = %hux%hu\n", filename.c_str(), width, height); + return false; + } + + if ((stride == 0) || (stride == width * sizeof(fb_pixel_t))) + read(fd, memp, height * width * sizeof(fb_pixel_t)); + else + for (int i = 0; i < height; i++) + read(fd, ((uint8_t *)memp) + i * stride, width * sizeof(fb_pixel_t)); + + close(fd); + return true; +} + +bool CFrameBuffer::loadPicture2Mem(const std::string & filename, fb_pixel_t * memp) +{ + return loadPictureToMem(filename, BACKGROUNDIMAGEWIDTH, 576, 0, memp); +} + +bool CFrameBuffer::loadPicture2FrameBuffer(const std::string & filename) +{ + if (!getActive()) + return false; + + return loadPictureToMem(filename, BACKGROUNDIMAGEWIDTH, 576, getStride(), getFrameBufferPointer()); +} + +bool CFrameBuffer::savePictureFromMem(const std::string & filename, const fb_pixel_t * const memp) +{ + struct rawHeader header; + uint16_t width, height; + int fd; + + width = BACKGROUNDIMAGEWIDTH; + height = 576; + + header.width_lo = width & 0xFF; + header.width_hi = width >> 8; + header.height_lo = height & 0xFF; + header.height_hi = height >> 8; + header.transp = 0; + + fd = open((iconBasePath + filename).c_str(), O_WRONLY | O_CREAT); + + if (fd==-1) + { + printf("error while saving icon: %s%s", iconBasePath.c_str(), filename.c_str() ); + return false; + } + + write(fd, &header, sizeof(struct rawHeader)); + + write(fd, memp, width * height * sizeof(fb_pixel_t)); + + close(fd); + return true; +} + +bool CFrameBuffer::loadBackground(const std::string & filename, const unsigned char offset) +{ + if ((backgroundFilename == filename) && (background)) + return true; + + if (background) + delete[] background; + + background = new fb_pixel_t[BACKGROUNDIMAGEWIDTH * 576]; + + if (!loadPictureToMem(filename, BACKGROUNDIMAGEWIDTH, 576, 0, background)) + { + delete[] background; + background=0; + return false; + } + + if (offset != 0)//pic-offset + { + fb_pixel_t * bpos = background; + int pos = BACKGROUNDIMAGEWIDTH * 576; + while (pos > 0) + { + *bpos += offset; + bpos++; + pos--; + } + } + + fb_pixel_t * dest = background + BACKGROUNDIMAGEWIDTH * 576; + uint8_t * src = ((uint8_t * )background)+ BACKGROUNDIMAGEWIDTH * 576; + for (int i = 576 - 1; i >= 0; i--) + for (int j = BACKGROUNDIMAGEWIDTH - 1; j >= 0; j--) + { + dest--; + src--; + paintPixel(dest, *src); + } + backgroundFilename = filename; + + return true; +} + +bool CFrameBuffer::loadBackgroundPic(const std::string & filename, bool show) +{ + if ((backgroundFilename == filename) && (background)) + return true; + +//printf("loadBackgroundPic: %s\n", filename.c_str()); + if (background) + delete[] background; + + background = g_PicViewer->getImage(iconBasePath + filename, BACKGROUNDIMAGEWIDTH, 576); + + if (background == NULL) { + background=0; + return false; + } + + backgroundFilename = filename; + if(show) { + useBackgroundPaint = true; + paintBackground(); + } + return true; +} + +void CFrameBuffer::useBackground(bool ub) +{ + useBackgroundPaint = ub; + if(!useBackgroundPaint) { + delete[] background; + background=0; + } +} + +bool CFrameBuffer::getuseBackground(void) +{ + return useBackgroundPaint; +} + +void CFrameBuffer::saveBackgroundImage(void) +{ + if (backupBackground != NULL) + delete[] backupBackground; + + backupBackground = background; + //useBackground(false); // <- necessary since no background is available + useBackgroundPaint = false; + background = NULL; +} + +void CFrameBuffer::restoreBackgroundImage(void) +{ + fb_pixel_t * tmp = background; + + if (backupBackground != NULL) + { + background = backupBackground; + backupBackground = NULL; + } + else + useBackground(false); // <- necessary since no background is available + + if (tmp != NULL) + delete[] tmp; +} + +void CFrameBuffer::paintBackgroundBoxRel(int x, int y, int dx, int dy) +{ + if (!getActive()) + return; + + if(!useBackgroundPaint) + { + paintBoxRel(x, y, dx, dy, backgroundColor); + } + else + { + uint8_t * fbpos = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + fb_pixel_t * bkpos = background + x + BACKGROUNDIMAGEWIDTH * y; + for(int count = 0;count < dy; count++) + { + memcpy(fbpos, bkpos, dx * sizeof(fb_pixel_t)); + fbpos += stride; + bkpos += BACKGROUNDIMAGEWIDTH; + } + } +} + +void CFrameBuffer::paintBackground() +{ + if (!getActive()) + return; + + if (useBackgroundPaint && (background != NULL)) + { + for (int i = 0; i < 576; i++) + memcpy(((uint8_t *)getFrameBufferPointer()) + i * stride, (background + i * BACKGROUNDIMAGEWIDTH), BACKGROUNDIMAGEWIDTH * sizeof(fb_pixel_t)); + } + else + { + paintBoxRel(0, 0, xRes, yRes, backgroundColor); + } +} + +void CFrameBuffer::SaveScreen(int x, int y, int dx, int dy, fb_pixel_t * const memp) +{ + if (!getActive()) + return; + + uint8_t * pos = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + fb_pixel_t * bkpos = memp; + for (int count = 0; count < dy; count++) { + fb_pixel_t * dest = (fb_pixel_t *)pos; + for (int i = 0; i < dx; i++) + //*(dest++) = col; + *(bkpos++) = *(dest++); + pos += stride; + } +#if 0 //FIXME test to flush cache + if (ioctl(fd, 1, FB_BLANK_UNBLANK) < 0); +#endif + //RestoreScreen(x, y, dx, dy, memp); //FIXME +#if 0 + uint8_t * fbpos = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + fb_pixel_t * bkpos = memp; + for (int count = 0; count < dy; count++) + { + memcpy(bkpos, fbpos, dx * sizeof(fb_pixel_t)); + fbpos += stride; + bkpos += dx; + } +#endif + +} + +void CFrameBuffer::RestoreScreen(int x, int y, int dx, int dy, fb_pixel_t * const memp) +{ + if (!getActive()) + return; + + uint8_t * fbpos = ((uint8_t *)getFrameBufferPointer()) + x * sizeof(fb_pixel_t) + stride * y; + fb_pixel_t * bkpos = memp; + for (int count = 0; count < dy; count++) + { + memcpy(fbpos, bkpos, dx * sizeof(fb_pixel_t)); + fbpos += stride; + bkpos += dx; + } +} + +void CFrameBuffer::switch_signal (int signal) +{ + CFrameBuffer * thiz = CFrameBuffer::getInstance(); + if (signal == SIGUSR1) { + if (virtual_fb != NULL) + delete[] virtual_fb; + virtual_fb = new uint8_t[thiz->stride * thiz->yRes]; + thiz->active = false; + if (virtual_fb != NULL) + memcpy(virtual_fb, thiz->lfb, thiz->stride * thiz->yRes); + ioctl(thiz->tty, VT_RELDISP, 1); + printf ("release display\n"); + } + else if (signal == SIGUSR2) { + ioctl(thiz->tty, VT_RELDISP, VT_ACKACQ); + thiz->active = true; + printf ("acquire display\n"); + thiz->paletteSet(NULL); + if (virtual_fb != NULL) + memcpy(thiz->lfb, virtual_fb, thiz->stride * thiz->yRes); + else + memset(thiz->lfb, 0, thiz->stride * thiz->yRes); + } +} + +void CFrameBuffer::ClearFrameBuffer() +{ + int tr; + + tr = 0xFF; + //paletteSetColor(0, 0xFFFFFF, 0xFF); + //Windows Colors + paletteSetColor(0x1, 0x010101, tr); + paletteSetColor(0x2, 0x800000, tr); + paletteSetColor(0x3, 0x008000, tr); + paletteSetColor(0x4, 0x808000, tr); + paletteSetColor(0x5, 0x000080, tr); + paletteSetColor(0x6, 0x800080, tr); + paletteSetColor(0x7, 0x008080, tr); + paletteSetColor(0x8, 0xA0A0A0, tr); + paletteSetColor(0x9, 0x505050, tr); + paletteSetColor(0xA, 0xFF0000, tr); + paletteSetColor(0xB, 0x00FF00, tr); + paletteSetColor(0xC, 0xFFFF00, tr); + paletteSetColor(0xD, 0x0000FF, tr); + paletteSetColor(0xE, 0xFF00FF, tr); + paletteSetColor(0xF, 0x00FFFF, tr); + paletteSetColor(0x10, 0xFFFFFF, tr); + paletteSetColor(0x11, 0x000000, tr); + useBackground(false); + + paintBackground(); + + //background + paletteSetColor(COL_BACKGROUND, 0x000000, 0xffff); + + paletteSet(); +} + +void CFrameBuffer::showFrame(const std::string & filename) +{ + std::string varpath = "/var/tuxbox/config/neutrino/icons/"; + if(!access((varpath + filename).c_str(), F_OK)) + videoDecoder->ShowPicture((varpath + filename).c_str()); + else + videoDecoder->ShowPicture((iconBasePath + filename).c_str()); +} diff --git a/src/driver/framebuffer.h b/src/driver/framebuffer.h new file mode 100644 index 000000000..e76f75f12 --- /dev/null +++ b/src/driver/framebuffer.h @@ -0,0 +1,198 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __framebuffer__ +#define __framebuffer__ +#include + +#include +#include + +#include +#include + +#define fb_pixel_t uint32_t + +typedef struct fb_var_screeninfo t_fb_var_screeninfo; + +#if 0 +#define CORNER_TOP_LEFT 0x1 +#define CORNER_TOP_RIGHT 0x2 +#define CORNER_TOP 0x3 +#define CORNER_BOTTOM_RIGHT 0x4 +#define CORNER_RIGHT 0x6 +#define CORNER_BOTTOM_LEFT 0x8 +#define CORNER_LEFT 0x9 +#define CORNER_BOTTOM 0xC +#endif + +#define CORNER_TOP 0x1 +#define CORNER_BOTTOM 0x2 +#define CORNER_BOTH 0x3 + +/** Ausführung als Singleton */ +class CFrameBuffer +{ + private: + + CFrameBuffer(); + + struct rgbData + { + uint8_t r; + uint8_t g; + uint8_t b; + } __attribute__ ((packed)); + + struct rawHeader + { + uint8_t width_lo; + uint8_t width_hi; + uint8_t height_lo; + uint8_t height_hi; + uint8_t transp; + } __attribute__ ((packed)); + + std::string iconBasePath; + + int fd, tty; + fb_pixel_t * lfb; + int available; + fb_pixel_t * background; + fb_pixel_t * backupBackground; + fb_pixel_t backgroundColor; + std::string backgroundFilename; + bool useBackgroundPaint; + unsigned int xRes, yRes, stride, bpp; + t_fb_var_screeninfo screeninfo, oldscreen; + fb_cmap cmap; + __u16 red[256], green[256], blue[256], trans[256]; + + void paletteFade(int i, __u32 rgb1, __u32 rgb2, int level); + + int kd_mode; + struct vt_mode vt_mode; + bool active; + static void switch_signal (int); + + #ifdef USE_NEVIS_GXA + int devmem_fd; /* to access the GXA register we use /dev/mem */ + unsigned int smem_start; /* as aquired from the fbdev, the framebuffers physical start address */ + volatile uint8_t *gxa_base; /* base address for the GXA's register access */ + #endif /* USE_NEVIS_GXA */ + public: +#ifndef FB_USE_PALETTE + fb_pixel_t realcolor[256]; +#endif + + ~CFrameBuffer(); + + static CFrameBuffer* getInstance(); + + void init(const char * const fbDevice = "/dev/fb/0"); + int setMode(unsigned int xRes, unsigned int yRes, unsigned int bpp); + + + int getFileHandle() const; //only used for plugins (games) !! + t_fb_var_screeninfo *getScreenInfo(); + + fb_pixel_t * getFrameBufferPointer() const; // pointer to framebuffer + unsigned int getStride() const; // size of a single line in the framebuffer (in bytes) + unsigned int getScreenWidth(bool real = false); + unsigned int getScreenHeight(bool real = false); + unsigned int getScreenX(); + unsigned int getScreenY(); + + bool getActive() const; // is framebuffer active? + void setActive(bool enable); // is framebuffer active? + + void setTransparency( int tr = 0 ); + void setBlendLevel(int blev1, int blev2); + + //Palette stuff + void setAlphaFade(int in, int num, int tr); + void paletteGenFade(int in, __u32 rgb1, __u32 rgb2, int num, int tr=0); + void paletteSetColor(int i, __u32 rgb, int tr); + void paletteSet(struct fb_cmap *map = NULL); + + //paint functions + inline void paintPixel(fb_pixel_t * const dest, const uint8_t color) const + { +#ifdef FB_USE_PALETTE + *dest = color; +#else + *dest = realcolor[color]; +#endif + }; + void paintPixel(int x, int y, const fb_pixel_t col); + + void paintBoxRel(const int x, const int y, const int dx, const int dy, const fb_pixel_t col, int radius = 0, int type = 0); + inline void paintBox(int xa, int ya, int xb, int yb, const fb_pixel_t col) { paintBoxRel(xa, ya, xb - xa, yb - ya, col); } + inline void paintBox(int xa, int ya, int xb, int yb, const fb_pixel_t col, int radius, int type) { paintBoxRel(xa, ya, xb - xa, yb - ya, col, radius, type); } + + void paintLine(int xa, int ya, int xb, int yb, const fb_pixel_t col); + + void paintVLine(int x, int ya, int yb, const fb_pixel_t col); + void paintVLineRel(int x, int y, int dy, const fb_pixel_t col); + + void paintHLine(int xa, int xb, int y, const fb_pixel_t col); + void paintHLineRel(int x, int dx, int y, const fb_pixel_t col); + + + void setIconBasePath(const std::string & iconPath); + + bool paintIcon (const char * const filename, const int x, const int y, const unsigned char offset = 1); + bool paintIcon (const std::string & filename, const int x, const int y, const unsigned char offset = 1); + bool paintIcon8(const std::string & filename, const int x, const int y, const unsigned char offset = 0); + void loadPal (const std::string & filename, const unsigned char offset = 0, const unsigned char endidx = 255); + + bool loadPicture2Mem (const std::string & filename, fb_pixel_t * const memp); + bool loadPicture2FrameBuffer(const std::string & filename); + bool loadPictureToMem (const std::string & filename, const uint16_t width, const uint16_t height, const uint16_t stride, fb_pixel_t * const memp); + bool savePictureFromMem (const std::string & filename, const fb_pixel_t * const memp); + + int getBackgroundColor() { return backgroundColor;} + void setBackgroundColor(const fb_pixel_t color); + bool loadBackground(const std::string & filename, const unsigned char col = 0); + void useBackground(bool); + bool getuseBackground(void); + + void saveBackgroundImage(void); // <- implies useBackground(false); + void restoreBackgroundImage(void); + + void paintBackgroundBoxRel(int x, int y, int dx, int dy); + inline void paintBackgroundBox(int xa, int ya, int xb, int yb) { paintBackgroundBoxRel(xa, ya, xb - xa, yb - ya); } + + void paintBackground(); + + void SaveScreen(int x, int y, int dx, int dy, fb_pixel_t * const memp); + void RestoreScreen(int x, int y, int dx, int dy, fb_pixel_t * const memp); + + void ClearFrameBuffer(); + void showFrame(const std::string & filename); + bool loadBackgroundPic(const std::string & filename, bool show = true); +}; + + +#endif diff --git a/src/driver/genpsi.c b/src/driver/genpsi.c new file mode 100644 index 000000000..0a0ee923d --- /dev/null +++ b/src/driver/genpsi.c @@ -0,0 +1,282 @@ +/* +$Id: genpsi.c,v 1.2 2006/01/16 12:45:54 sat_man Exp $ + + Copyright (c) 2004 gmo18t, Germany. All rights reserved. + + aktuelle Versionen gibt es hier: + $Source: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/genpsi.c,v $ + + 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., 675 Mass Ave, Cambridge MA 02139, USA. + + Mit diesem Programm koennen Neutrino TS Streams für das Abspielen unter Enigma gepatched werden + */ +//#include +#include +#include +#include + +#define SIZE_TS_PKT 188 +#define OFS_HDR_2 5 +#define OFS_PMT_DATA 13 +#define OFS_STREAM_TAB 17 +#define SIZE_STREAM_TAB_ROW 5 +#define OFS_ENIGMA_TAB 31 +#define SIZE_ENIGMA_TAB_ROW 4 + +#define ES_TYPE_MPEG12 0x02 +#define ES_TYPE_AVC 0x1b +#define ES_TYPE_MPA 0x03 +#define ES_TYPE_AC3 0x81 + +typedef struct +{ + short nba; + uint16_t vpid; + uint8_t vtype; + uint16_t apid[10]; + short isAC3[10]; +} T_AV_PIDS; + +T_AV_PIDS avPids; + +static const uint32_t 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 +}; + +uint32_t calc_crc32psi(uint8_t *dst, const uint8_t *src, uint32_t len) +{ + uint32_t i; + uint32_t crc = 0xffffffff; + + for (i=0; i> 24) ^ *src++) & 0xff]; + + if (dst) + { + dst[0] = (crc >> 24) & 0xff; + dst[1] = (crc >> 16) & 0xff; + dst[2] = (crc >> 8) & 0xff; + dst[3] = (crc) & 0xff; + } + + return crc; +} + +void transfer_pids(uint16_t pid,uint16_t pidart,short isAC3) +{ + switch(pidart) + { + case EN_TYPE_VIDEO: + avPids.vpid=pid; + avPids.vtype = ES_TYPE_MPEG12; + break; + case EN_TYPE_AVC: + avPids.vpid=pid; + avPids.vtype = ES_TYPE_AVC; + break; + case EN_TYPE_AUDIO: + avPids.apid[avPids.nba]=pid; + avPids.isAC3[avPids.nba]=isAC3; + avPids.nba++; + break; + case EN_TYPE_TELTEX: + break; + + default: + break; + } +} +//-- special enigma stream description packet for -- +//-- at least 1 video, 1 audo and 1 PCR-Pid stream -- +//------------------------------------------------------------------------------------ +static uint8_t pkt_enigma[] = +{ + 0x47, 0x40, 0x1F, 0x10, 0x00, + 0x7F, 0x80, 0x24, + 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x6D, 0x66, 0x30, 0x19, + 0x80, 0x13, 'N','E','U','T','R','I','N','O','N','G', // tag(8), len(8), text(10) -> NG hihi ;) + 0x00, 0x02, 0x00, 0x6e, // cVPID(8), len(8), PID(16) + 0x01, 0x03, 0x00, 0x78, 0x00, // cAPID(8), len(8), PID(16), ac3flag(8) +// 0x02, 0x02, 0x00, 0x82,// cTPID(8), len(8), ... + 0x03, 0x02, 0x00, 0x6e // cPCRPID(8), ... +}; +//-- PAT packet for at least 1 PMT -- +//---------------------------------------------------------- +static uint8_t pkt_pat[] = +{ + 0x47, 0x40, 0x00, 0x10, 0x00, // HEADER-1 + 0x00, 0xB0, 0x0D, // HEADER-2 + 0x04, 0x37, 0xE9, 0x00, 0x00, // HEADER-3 sid + 0x6D, 0x66, 0xEF, 0xFF, // PAT-DATA - PMT (PID=0xFFF) entry +}; + +//-- PMT packet for at least 1 video and 1 audio stream -- +//-------------------------------------------------------- +static uint8_t pkt_pmt[] = +{ + 0x47, 0x4F, 0xFF, 0x10, 0x00, // HEADER-1 + 0x02, 0xB0, 0x17, // HEADER-2 + 0x6D, 0x66, 0xE9, 0x00, 0x00, // HEADER-3 + 0xE0, 0x00, 0xF0, 0x00, // PMT-DATA + 0x02, 0xE0, 0x00, 0xF0, 0x00, // (video stream 1) + 0x03, 0xE0, 0x00, 0xF0, 0x00 // (audio stream 1) +}; + + +//== setup a new TS packet with format == +//== predefined with a template == +//======================================= +#define COPY_TEMPLATE(dst, src) copy_template(dst, src, sizeof(src)) + +static int copy_template(uint8_t *dst, uint8_t *src, int len) +{ +//-- reset buffer -- + memset(dst, 0xFF, SIZE_TS_PKT); +//-- copy PMT template -- + memcpy(dst, src, len); + + return len; +} +int genpsi(int fd2) +{ + int bytes = 0; + uint8_t pkt[SIZE_TS_PKT]; + int i, data_len, patch_len, ofs; + +//-- copy "Enigma"-template -- + data_len = COPY_TEMPLATE(pkt, pkt_enigma); + +//-- adjust len dependent to number of audio streams -- + data_len += ((SIZE_ENIGMA_TAB_ROW+1) * (avPids.nba-1)); + + patch_len = data_len - OFS_HDR_2 + 1; + pkt[OFS_HDR_2+1] |= (patch_len>>8); + pkt[OFS_HDR_2+2] = (patch_len & 0xFF); +//-- write row with desc. for video stream -- + ofs = OFS_ENIGMA_TAB; + pkt[ofs] = EN_TYPE_VIDEO; + pkt[ofs+1] = 0x02; + pkt[ofs+2] = (avPids.vpid>>8); + pkt[ofs+3] = (avPids.vpid & 0xFF); +//-- for each audio stream, write row with desc. -- + ofs += SIZE_ENIGMA_TAB_ROW; + for (i=0; i>8); + pkt[ofs+3] = (avPids.apid[i] & 0xFF); + pkt[ofs+4] = (avPids.isAC3[i]==1)? 0x01 : 0x00; + + ofs += (SIZE_ENIGMA_TAB_ROW + 1); + } +//-- write row with desc. for pcr stream (eq. video) -- + pkt[ofs] = EN_TYPE_PCR; + pkt[ofs+1] = 0x02; + pkt[ofs+2] = (avPids.vpid>>8); + pkt[ofs+3] = (avPids.vpid & 0xFF); + +//-- calculate CRC -- + calc_crc32psi(&pkt[data_len], &pkt[OFS_HDR_2], data_len-OFS_HDR_2 ); +//-- write TS packet -- + bytes += write(fd2, pkt, SIZE_TS_PKT); +//-- (II) build PAT -- + data_len = COPY_TEMPLATE(pkt, pkt_pat); +//-- calculate CRC -- + calc_crc32psi(&pkt[data_len], &pkt[OFS_HDR_2], data_len-OFS_HDR_2 ); +//-- write TS packet -- + bytes += write(fd2, pkt, SIZE_TS_PKT); + +//-- (III) build PMT -- + data_len = COPY_TEMPLATE(pkt, pkt_pmt); +//-- adjust len dependent to count of audio streams -- + data_len += (SIZE_STREAM_TAB_ROW * (avPids.nba-1)); + patch_len = data_len - OFS_HDR_2 + 1; + pkt[OFS_HDR_2+1] |= (patch_len>>8); + pkt[OFS_HDR_2+2] = (patch_len & 0xFF); +//-- patch pcr PID -- + ofs = OFS_PMT_DATA; + pkt[ofs] |= (avPids.vpid>>8); + pkt[ofs+1] = (avPids.vpid & 0xFF); +//-- write row with desc. for ES video stream -- + ofs = OFS_STREAM_TAB; + pkt[ofs] = avPids.vtype; + pkt[ofs+1] = 0xE0 | (avPids.vpid>>8); + pkt[ofs+2] = (avPids.vpid & 0xFF); + pkt[ofs+3] = 0xF0; + pkt[ofs+4] = 0x00; + +//-- for each ES audio stream, write row with desc. -- + for (i=0; i>8); + pkt[ofs+2] = (avPids.apid[i] & 0xFF); + pkt[ofs+3] = 0xF0; + pkt[ofs+4] = 0x00; + } +//-- calculate CRC -- + calc_crc32psi(&pkt[data_len], &pkt[OFS_HDR_2], data_len-OFS_HDR_2 ); +//-- write TS packet -- + bytes += write(fd2, pkt, SIZE_TS_PKT); +//-- finish -- + avPids.vpid=0; + avPids.nba=0; + return 1; +} diff --git a/src/driver/genpsi.h b/src/driver/genpsi.h new file mode 100644 index 000000000..113eb883b --- /dev/null +++ b/src/driver/genpsi.h @@ -0,0 +1,38 @@ +/* +$Id: genpsi.h,v 1.1 2005/08/15 14:47:52 metallica Exp $ + + Copyright (c) 2004 gmo18t, Germany. All rights reserved. + + aktuelle Versionen gibt es hier: + $Source: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/genpsi.h,v $ + + 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., 675 Mass Ave, Cambridge MA 02139, USA. + + Mit diesem Programm koennen Neutrino TS Streams für das Abspielen unter Enigma gepatched werden + */ +#ifndef __genpsi_h__ +#define __genpsi_h__ +#include + +int genpsi(int fd2); +void transfer_pids(uint16_t pid,uint16_t pidart,short isAC3); + +#define EN_TYPE_VIDEO 0x00 +#define EN_TYPE_AUDIO 0x01 +#define EN_TYPE_TELTEX 0x02 +#define EN_TYPE_PCR 0x03 +#define EN_TYPE_AVC 0x04 + +#endif diff --git a/src/driver/netfile.cpp b/src/driver/netfile.cpp new file mode 100644 index 000000000..dde135483 --- /dev/null +++ b/src/driver/netfile.cpp @@ -0,0 +1,2023 @@ +/******************************************************************************\ +| Neutrino-GUI - DBoxII-Project +| +| Copyright (C) 2004 by Sanaia +| netfile - remote file access mapper +| +| +| description: +| netfile - a URL capable wrapper for the fopen() call +| +| usage: +| include 'netfile.h' in your code as *LAST* file after all other +| include files and add netfile.c to your sources. That's it. The +| include file maps the common fopen() call onto a URL capable +| version that handles files hosted on http servers like local ones. +| +| +| examples: +| +| automatic redirection example: +| +| fd = fopen("remotefile.url", "r"); +| +| This is a pretty straight forward implementation that should +| even work with applications which refuse to accept filenames +| starting with protocol headers (i.e. 'http://'). If the +| given file ends with '.url', then the file is opened and read +| and its content is assumed to be a valid URL which opened then. +| This works for all protocols provided by this code, e.g. 'http://', +| 'icy://' and 'scast://' +| All restrictions apply for the protocols themself as follows. +| +| +| http example: +| +| fd = fopen("http://find.me:666/somewhere/foo.bar", "r"); +| +| This opens the specified file on a webserver (read only). +| NOTE: the stream itself is bidirectional, you can write +| into it (e.g. commands for communication with the server), +| but this is neither supported nor recommended. The result +| of such an action is rather undefined. You *CAN NOT* make +| any changes to the file of the webserver ! +| +| +| shoutcast example: +| +| fd = fopen("scast://666", "r"); +| +| This opens a shoutcast sation; all you need to know is the +| station number. The query of the shoutcast directory and the +| lookup of a working server is done automatically. The stream +| is opened read-only. +| I recommend this for official shoutcast stations. +| +| shoutcast example: +| +| fd = fopen("icy://find.me:666/funky/station/", "r"); +| or +| fd = fopen("icy://username:password@find.me:666/station", "r"); +| +| This is a low level mechanism that can be used for all +| shoutcast servers, but it mainly is intended to be used for +| private radio stations which are not listed in the official +| shoutcast directory. The stream is opened read-only. +| The second format contains a basic authentication string before '@' +| of username:passwort to be sent to the server. +| +| file access modes, selectable by the fopen 'access type': +| +| "r" read only, stream caching enabled +| "rc" read only, stream caching disabled (compatibility mode) +| +| NOTE: All network accesses are only possible read only. Although some +| protocols could allow write access to the remote resource, this +| is not (and rather unlikely to be anytime) implemented here. +| All remote accesses are made through a FIFO caching mechanism that +| uses read-ahead caching (as far as possible). The fopen() call starts +| a background fetching thread that fills the cache up to the ceiling. +| +| +| License: GPL +| +| 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., 675 Mass Ave, Cambridge, MA 02139, USA. +| +\******************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "netfile.h" +#include "global.h" +#include +#include +#include +/* +TODO: + - ICECAST support + - follow redirection errors (server error codes 302, 301) (done for HTTP, 2005-05-16 ChakaZulu) + - support for automatic playlist processing (shoutcast pls files) + +known bugs: + - HTTP POST requests - are implemented, but don't work with shoutcast.com !? +*/ + +#define STATIC /**/ + +#ifdef fopen + #undef fopen +#endif +#ifdef fclose + #undef fclose +#endif +#ifdef fread + #undef fread +#endif +#ifdef ftell + #undef ftell +#endif +#ifdef rewind + #undef rewind +#endif +#ifdef fseek + #undef fseek +#endif + + + + +typedef struct +{ + const unsigned char mask[4]; + const unsigned char mode[4]; + const char *type; +} magic_t; + +magic_t known_magic[] = +{ + {{0xFF, 0xFF, 0xFF, 0x00}, {'I' , 'D' , '3' , 0x00}, "audio/mpeg"}, + {{0xFF, 0xFF, 0xFF, 0x00}, {'O' , 'g' , 'g' , 0x00}, "audio/ogg" }, + {{0xFF, 0xFE, 0x00, 0x00}, {0xFF, 0xFA, 0x00, 0x00}, "audio/mpeg"} +}; + +#if 0 +#warning if a magic is contained in a file (for instance in .cdr) there is no way to play it correctly with neutrino +#warning the third magic is pretty short - hence disabled by default +/* 1111 1111 1111 1010 0000 0000 0000 0000 + AAAA AAAA AAAB BCC + where A: frame sync + B: MPEG audio ID (11 = MPEG Version 1) + C: Layer description (01 = Layer III) + see http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm */ + +#define known_magic_count (sizeof(known_magic) / sizeof(magic_t)) +#else +#define known_magic_count 2 +#endif + +#define is_redirect(a) ((a == 301) || (a == 302)) + +char err_txt[2048]; /* human readable error message */ +char redirect_url[2048]; /* new url if we've been redirected (HTTP 301/302) */ +static int debug = 0; /* print debugging output or not */ +static char logfile[255]; /* redirect errors from stderr */ +static int retry_num = 2 /*10*/; /* number of retries for failed connections */ +static int enable_metadata = 0; /* allow shoutcast meta data streaming */ +static int got_opts = 0; /* is set to 1 if getOpts() was executed */ +static int cache_size = 196608; /* default cache size; can be overridden at */ + /* runtime with an option in the options file */ + +STATIC STREAM_CACHE cache[CACHEENTMAX]; +STATIC STREAM_TYPE stream_type[CACHEENTMAX]; + +static int ConnectToServer(char *hostname, int port); +static int parse_response(URL *url, void *, CSTATE*); +static int request_file(URL *url); +static void readln(int, char *); +static int getCacheSlot(FILE *fd); +static int push(FILE *fd, char *buf, long len); +static int pop(FILE *fd, char *buf, long len); +static void CacheFillThread(void *url); +static void ShoutCAST_MetaFilter(STREAM_FILTER *); +static void ShoutCAST_DestroyFilter(void *a); +static STREAM_FILTER *ShoutCAST_InitFilter(int); +static void ShoutCAST_ParseMetaData(char *, CSTATE *); + +static void getOpts(void); + +static void parseURL_url(URL& url); + +static int base64_encode(char *dest, const char *src); + +/***************************************/ +/* this is a simple options parser */ + +void getOpts() +{ + char *dirs[] = { (char *) "/var/etc", (char *) ".", NULL }; + char buf[4096], *ptr; + int i; + FILE *fd = NULL; + +#ifndef RADIOBOX //a not generic thing in a generic library + /* options which can be set from within neutrino */ + enable_metadata = g_settings.audioplayer_enable_sc_metadata; +#endif /* RADIOBOX */ + + if (got_opts) /* prevent reading in options multiple times */ + return; + else + got_opts = 1; + + for(i=0; dirs[i] != NULL; i++) + { + sprintf(buf, "%s/.netfile", dirs[i]); + fd = fopen(buf, "r"); + if(fd) break; + } + + if(!fd) + return; + fread(buf, sizeof(char), 4095, fd); + fclose(fd); + + if(strstr(buf, "debug=1")) + debug = 1; + + if((ptr = strstr(buf, "cachesize="))) + cache_size = atoi(strchr(ptr, '=') + 1); + + if((ptr = strstr(buf, "retries="))) + retry_num = atoi(strchr(ptr, '=') + 1); + + if((ptr = strstr(buf, "logfile="))) + { + strcpy(logfile, strchr(ptr, '=') + 1); + CRLFCut(logfile); + freopen(logfile, "w", stderr); + } +} + +/***************************************/ +/* networking functions */ + +int ConnectToServer(char *hostname, int port) +{ + struct hostent *host; + struct sockaddr_in sock; + int fd, addr; + struct pollfd pfd; + + dprintf(stderr, "looking up hostname: %s\n", hostname); + + host = gethostbyname(hostname); + + if(host == NULL) + { + herror(err_txt); + return -1; + } + + addr = htonl(*(int *)host->h_addr); + + dprintf(stderr, "connecting to %s [%d.%d.%d.%d], port %d\n", host->h_name, + (addr & 0xff000000) >> 24, + (addr & 0x00ff0000) >> 16, + (addr & 0x0000ff00) >> 8, + (addr & 0x000000ff), port); + + fd = socket(AF_INET, SOCK_STREAM, 0); + + if(fd == -1) + { + strcpy(err_txt, strerror(errno)); + return -1; + } + + memset(&sock, 0, sizeof(sock)); + memmove((char*)&sock.sin_addr, host->h_addr, host->h_length); + + sock.sin_family = AF_INET; + sock.sin_port = htons(port); + + int flgs = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flgs | O_NONBLOCK); + + if( connect(fd, (struct sockaddr *)&sock, sizeof(sock)) == -1 ) + { + if(errno != EINPROGRESS) { + close(fd); + strcpy(err_txt, strerror(errno)); + dprintf(stderr, "error connecting to %s: %s\n", hostname, err_txt); + return -1; + } + } + dprintf(stderr, "connected to %s\n", hostname); + pfd.fd = fd; + //pfd.events = POLLIN | POLLPRI; + pfd.events = POLLOUT | POLLERR | POLLHUP; + pfd.revents = 0; + + int ret = poll(&pfd, 1, 5000); + if(ret != 1) { + strcpy(err_txt, strerror(errno)); + dprintf(stderr, "error connecting to %s: %s\n", hostname, err_txt); + close(fd); + return -1; + } + if ((pfd.revents & POLLOUT) == POLLOUT) { + fcntl(fd, F_SETFL, flgs &~ O_NONBLOCK); + return fd; + } + + return fd; +} + +/*********************************************/ +/* request a file from the HTTP server */ +/* the network stream must be opened already */ +/* and the URL structure be initialized */ +/* properly ! */ + +int request_file(URL *url) +{ + char str[255], *ptr; + int slot; + ID3 id3; + + /* get the cache slot for this stream. A negative return value */ + /* indicates that no cache has been set up for this stream */ + slot = getCacheSlot(url->stream); + + /* if the filename contains a line break, then use everything */ + /* up to the line break as file name and everything beyond as */ + /* entity to be sent with the request */ + ptr = strchr(url->file, '\n'); + if(ptr) + { + strcpy(url->entity, ptr + 1); + *ptr = 0; + } + switch(url->proto_version) + { + /* send a HTTP/1.0 request */ + case HTTP10: { + snprintf(str, sizeof(str)-1, "GET http://%s:%d%s\n", url->host, url->port, url->file); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + break; + + /* send a HTTP/1.1 request */ + case HTTP11: { + int meta_int; + CSTATE tmp; + + snprintf(str, sizeof(str)-1, "%s %s HTTP/1.1\r\n", (url->entity[0]) ? "POST" : "GET", url->file); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + snprintf(str, sizeof(str)-1, "Host: %s:%d\r\n", url->host, url->port); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + snprintf(str, sizeof(str)-1, "User-Agent: WinampMPEG/5.52\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if (url->logindata[0]) + { + sprintf(str, "Authorization: Basic %s\r\n", url->logindata); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + + snprintf(str, sizeof(str)-1, "Accept: */*\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if(enable_metadata) + { + snprintf(str, sizeof(str)-1, "Icy-MetaData: 1\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + + /* if we have a entity, announce it to the server */ + if(url->entity[0]) + { + snprintf(str, sizeof(str)-1, "Content-Length: %d\r\n", strlen(url->entity)); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + + snprintf(str, sizeof(str)-1, "Connection: close\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + // empty line -> end of HTTP request + snprintf(str, sizeof(str)-1, "\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if( (meta_int = parse_response(url, &id3, &tmp)) < 0) + return -1; + + if(meta_int) + { + /* hook in the filter function if there is meta */ + /* data present in the stream */ + cache[slot].filter_arg = ShoutCAST_InitFilter(meta_int); + cache[slot].filter = ShoutCAST_MetaFilter; + + /* this is a *really bad* way to pass along the argument, */ + /* since we convert the integer into a pointer instead of */ + /* passing along a pointer/reference !*/ + if(cache[slot].filter_arg->state) + memmove(cache[slot].filter_arg->state, &tmp, sizeof(CSTATE)); + } + + /* push the created ID3 header into the stream cache */ + push(url->stream, (char*)&id3, id3.len); +#if 0 + rval = parse_response(url, NULL, NULL); + dprintf(stderr, "server response parser: return value = %d\n", rval); + + /* if the header indicated a zero length document or an */ + /* error, then close the cache, if there is any */ + /* 25.04.05 ChakaZulu: zero length can be a stream so let's try playing */ + if((slot >= 0) && (rval < 0)) + cache[slot].closed = 1; + + /* return on error */ + if( rval < 0 ) + return rval; +#endif + } + break; + + /* send a SHOUTCAST request */ + case SHOUTCAST:{ + int meta_int; + CSTATE tmp; + + sprintf(str, "GET %s HTTP/1.0\r\n", url->file); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + sprintf(str, "Host: %s\r\n", url->host); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if(enable_metadata) + { + sprintf(str, "icy-metadata: 1\r\n"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + + sprintf(str, "User-Agent: %s\r\n", "RealPlayer/4.0"); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if (url->logindata[0]) + { + sprintf(str, "Authorization: Basic %s\r\n", url->logindata); + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + } + + sprintf(str, "\r\n"); /* end of headers to send */ + dprintf(stderr, "> %s", str); + send(url->fd, str, strlen(str), 0); + + if( (meta_int = parse_response(url, &id3, &tmp)) < 0) + return -1; + + if(meta_int) + { + /* hook in the filter function if there is meta */ + /* data present in the stream */ + cache[slot].filter_arg = ShoutCAST_InitFilter(meta_int); + cache[slot].filter = ShoutCAST_MetaFilter; + + /* this is a *really bad* way to pass along the argument, */ + /* since we convert the integer into a pointer instead of */ + /* passing along a pointer/reference !*/ + if(cache[slot].filter_arg->state) + memmove(cache[slot].filter_arg->state, &tmp, sizeof(CSTATE)); + } + + /* push the created ID3 header into the stream cache */ + push(url->stream, (char*)&id3, id3.len); + } + break; + } /* end switch(url->proto_version) */ + + /* now it get's nasty ;) */ + /* create a thread that continously feeds the cache */ + /* with the data it fetches from the network stream */ + /* but *ONLY* if there is a cache slot for this stream at all ! */ + /* HINT: in compatibility mode no cache is configured */ + + if(slot >= 0) + { + pthread_attr_init(&cache[slot].attr); + pthread_create( + &cache[slot].fill_thread, + &cache[slot].attr, + (void *(*)(void*))&CacheFillThread, (void*)&cache[slot]); + dprintf(stderr, "request_file: slot %d fill_thread 0x%x\n", slot, (int)cache[slot].fill_thread); + } + + /* now we do not care any longer about fetching new data,*/ + /* but we can not be shure that the cache is filled with */ + /* enough stuff ! */ + return 0; +} + +/***************************************/ +/* parse the server response (header) */ +/* and translate possible errors into */ +/* local syserror numbers */ + +#define getHeaderVal(a,b) { \ + char *_ptr; \ + _ptr = strstr(header, a); \ + if(_ptr) \ + { \ + _ptr = strchr(_ptr, ':'); \ + for(; !isalnum(*_ptr); _ptr++); \ + b = atoi(_ptr); \ + } else b = -1; } + +#define getHeaderStr(a,b) { \ + char *_ptr; \ + _ptr = strstr(header, a); \ + if(_ptr) \ + { \ + unsigned int i; \ + _ptr = strchr(_ptr, ':'); \ + for(_ptr++; isspace(*_ptr); _ptr++); \ + for (i=0; (_ptr[i]!='\n') && (_ptr[i]!='\r') && (_ptr[i]!='\0') && (ifd; + ID3 *id3 = (ID3*)opt; + + memset(header, 0, 2048); + ptr = header; + + /* extract the http header from the stream */ + do + { + recv(fd, ptr, 1, 0); + + /* skip all 'CR' symbols */ + if(*ptr != 13) + { + lastchr = chr; chr = *ptr++; hlen++; + } + + } while((hlen < 2048) && ( ! ((chr == 10) && (lastchr == 10)))); + + *ptr = 0; + + dprintf(stderr, "----------\n%s----------\n", header); + + /* parse the header fields */ + ptr = strstr(header, "HTTP/1.1"); + + if(!ptr) + ptr = strstr(header, "HTTP/1.0"); + + if(!ptr) + ptr = strstr(header, "ICY"); + + /* no valid HTTP/1.1 or SHOUTCAST response */ + if(!ptr) + return -1; + + response = atoi(strchr(ptr, ' ') + 1); + + switch(response) + { + case 200: errno = 0; + break; + + case 301: /* 'file moved' error */ + case 302: /* 'file not found' error */ + errno = ENOENT; + strcpy(err_txt, ptr); + getHeaderStr("Location", redirect_url); + return -1 * response; + break; + + case 404: /* 'file not found' error */ + errno = ENOENT; + strcpy(err_txt, ptr); + return -1; + break; + + default: errno = ENOPROTOOPT; + dprintf(stderr, "unknown server response code: %d\n", response); + return -1; + } + + /* use 'audio/mpeg' as default type, in case we are not told otherwise */ + strcpy(str, "audio/mpeg"); + getHeaderStr("Content-Type:", str); + f_type(url->stream, str); + dprintf(stderr, "type %s\n", str); + + /* if we got a content length from the server, i.e. rval >= 0 */ + /* then return now with the content length as return value */ + getHeaderVal("Content-Length:", rval); + // dprintf(stderr, "file size: %d\n", rval); + if(rval >= 0) + return rval; + + /* yet another hack: this is only implemented to be able to fetch */ + /* the playlists from shoutcast */ + if(strstr(header, "Transfer-Encoding: chunked")) + { + readln(fd, str); + sscanf(str, "%x", &rval); + return rval; + } + + /* no content length indication from the server ? Then treat it as stream */ + getHeaderVal("icy-metaint:", meta_interval); + if(meta_interval < 0) + meta_interval = 0; + + if (state != NULL) { + getHeaderStr("icy-genre:", state->genre); + getHeaderStr("icy-name:", state->station); + getHeaderStr("icy-url:", state->station_url); + getHeaderVal("icy-br:", state->bitrate); + } + /********************* dirty hack **********************/ + /* we parse the stream header sent by the server and */ + /* build based on this information an arteficial id3 */ + /* header that is pushed into the streamcache before */ + /* any data from the stream is fed into the cache. This */ + /* makes the stream look like an MP3 and we have the */ + /* station information in the display of the player :)) */ + +#define SSIZE(a) (\ + (((a) & 0x0000007f) << 0) | (((a) & 0x00003f80) << 1) | \ + (((a) & 0x001fc000) << 2) | (((a) & 0xfe000000) << 3)) + +#define FRAME(b,c) {\ + strcpy(id3frame.id, (b)); \ + strcpy(id3frame.base, (c)); \ + id3frame.size = strlen(id3frame.base); \ + fcnt = 11 + id3frame.size; } + + if(id3) + { + int cnt = 0, fcnt = 0; + ID3_frame id3frame; + uint32_t sz; + char station[2048], desc[2048]; + + memmove(id3->magic, "ID3", 3); + id3->version[0] = 3; + id3->version[1] = 0; + + ptr = strstr(header, "icy-name:"); + if(ptr) + { + ptr = strchr(ptr, ':') + 1; + for(; ((*ptr == '-') || (*ptr == ' ')); ptr++){}; + strcpy(station, ptr); + *strchr(station, '\n') = 0; + + ptr = strchr(station, '-'); + if(ptr) + { + *ptr = 0; + for(ptr++; ((*ptr == '-') || (*ptr == ' ')); ptr++){}; + strcpy(desc, ptr); + } + + FRAME("TPE1", station); + id3frame.size = SSIZE(id3frame.size + 1); + memmove(id3->base + cnt, &id3frame, fcnt); + cnt += fcnt; + + FRAME("TALB", desc); + id3frame.size = SSIZE(id3frame.size + 1); + memmove(id3->base + cnt, &id3frame, fcnt); + cnt += fcnt; + } + + ptr = strstr(header, "icy-genre:"); + if(ptr) + { + ptr = strchr(ptr, ':') + 1; + for(; ((*ptr == '-') || (*ptr == ' ')); ptr++){}; + strcpy(str, ptr); + *strchr(str, '\n') = 0; + + FRAME("TIT2", str); + id3frame.size = SSIZE(id3frame.size + 1); + memmove(id3->base + cnt, &id3frame, fcnt); + cnt += fcnt; + } + + FRAME("COMM", "dbox streamconverter"); + id3frame.size = SSIZE(id3frame.size + 1); + memmove(id3->base + cnt, &id3frame, fcnt); + cnt += fcnt; + + sz = 14 + cnt - 10; + + id3->size[0] = (sz & 0xfe000000) >> 21; + id3->size[1] = (sz & 0x001fc000) >> 14; + id3->size[2] = (sz & 0x00003f80) >> 7; + id3->size[3] = (sz & 0x0000007f) >> 0; + + id3->len = 14 + cnt; + } + + return meta_interval; +} + +/******* wrapper functions for the f*() calls ************************/ + +#define transport(a,b,c) \ + if(strstr(url.url, a)) \ + { \ + url.access_mode = b; \ + url.proto_version = c; \ + } + +FILE *f_open(const char *filename, const char *acctype) +{ + URL url; + FILE *fd; + int /*i,*/ compatibility_mode = 0; + char *ptr = NULL, buf[4096], type[10]; + + if(acctype) + strcpy(type, acctype); + + /* read some options from the options-file */ + getOpts(); + + /* test if compatibility mode has been requested. */ + /* e.g. "rbc" = read, binary, without stream caching */ + if(strchr(type, 'c')) + { + compatibility_mode = 1; + *strchr(type, 'c') = 0; + } + + dprintf(stderr, "f_open: %s %s\n", (compatibility_mode) ? "(compatibility mode)" : "", filename); + + /* set default protocol and port */ + memset(&url, 0, sizeof(URL)); + url.proto_version = HTTP11; + url.port = 80; + strcpy(url.url, filename); + + /* remove leading spaces */ + for (ptr = url.url; (ptr != NULL) && ((*ptr == ' ') || (*ptr == ' ')); ptr++){} ; + + if(ptr != url.url) + strcpy(url.url, ptr); + + /* did we get an URL file as argument ? If so, then open */ + /* this file, read out the url and open the url instead */ +#ifndef DISABLE_URLFILES + if( strstr(filename, ".url") || strstr(filename, ".imu")) + { + fd = fopen(filename, "r"); + + if(fd) + { + fread(buf, sizeof(char), 4095, fd); + fclose(fd); + + ptr = strstr(buf, "://"); + + if(!ptr) + { + dprintf(stderr, "Ups! File doesn't seem to contain any URL !\nbuffer:%s\n", buf); + return NULL; + } + + ptr = strchr(buf, '\n'); + if(ptr) + *ptr = 0; + + sprintf(url.url, "%s", buf); + } + else + return NULL; + } +#endif + + /* now lets see what we have ... */ + parseURL_url(url); + + dprintf(stderr, "URL to open: %s, access mode %s%s\n", + url.url, + (url.access_mode == MODE_HTTP) ? "HTTP" : + (url.access_mode == MODE_SCAST) ? "SHOUTCAST" : + (url.access_mode == MODE_ICAST) ? "ICECAST" : + (url.access_mode == MODE_PLS) ? "PLAYLIST" : + "FILE", + (url.access_mode != MODE_FILE) ? ( + (url.proto_version == HTTP10) ? "/1.0" : + (url.proto_version == HTTP11) ? "/1.1" : + (url.proto_version == SHOUTCAST) ? "/SHOUTCAST" : + "") : "" ); + + dprintf(stderr, "FILE to open: %s, access mode: %d\n", url.file, url.access_mode); + + switch(url.access_mode) + { + case MODE_HTTP: { + int follow_url = 1; // used for redirects + int redirects = 0; + *redirect_url = '\0'; + while (follow_url) + { + + int retries = retry_num; + + do + { + url.fd = ConnectToServer(url.host, url.port); + retries--; + } while( url.fd < 0 && retries > 0); + + /* if the stream could not be opened, then indicate */ + /* an 'No such device or address' error */ + if(url.fd < 0) + { + fd = NULL; + errno = ENXIO; + printf("netfile: could not connect to server %s:%d\n",url.host, url.port); + return fd; + } + else + { + fd = fdopen(url.fd, "r+"); + url.stream = fd; + + if(!fd) + { + perror(err_txt); + } + else + { + /* in compatibility mode we must not use our own stream cache */ + /* because the application makes use of their own f*() calls */ + /* which we can not replace by our own functions and thus they'll */ + /* interfere with each other. All we can do is to open the stream */ + /* and return a valid stream descriptor */ + if(!compatibility_mode) + { + int i; + /* look for a free cache slot */ + for(i=0; ((i 0); + + + /* extract the servers from the received playlist */ + if(!ptr) + { + dprintf(stderr, "Ups! Playlist doesn't seem to contain any URL !\nbuffer:%s\n", buf); + sprintf(err_txt, "Ups! Playlist doesn't seem to contain any URL !"); + return NULL; + } + + for(int i=0; ((ptr != NULL) && (i<25)); ptr = strstr(ptr, "http://") ) + { + strncpy(servers[i], ptr, 1023); + ptr2 = strchr(servers[i], '\n'); + if(ptr2) *ptr2 = 0; + // change ptr so that next strstr searches in buf and not in servers[i] + ptr = strchr(ptr,'\n'); + if (ptr != NULL) + ptr++; + dprintf(stderr, "server[%d]: %s\n", i, servers[i]); + i++; + } + + /* try to connect to all servers until we find one that */ + /* is willing to serve us */ + { + int i; + for(i=0, fd = NULL; ((fd == NULL) && (i<25)); i++) + { + const char* const chptr = strstr(servers[i], "://"); + if(chptr) + { + sprintf(url.url, "icy%s", chptr); + fd = f_open(url.url, "r"); + } + } + } + } + break; + + case MODE_FILE: + default: + { + unsigned char magic[4] = {0, 0, 0, 0}; + + fd = fopen(url.file, type); + if (fd == NULL) + return NULL; + fread(&magic, 4, 1, fd); + rewind(fd); + + /* first stage: try to determine the filetype from the file */ + /* magic, if there is any */ + for (int i = 0; i < known_magic_count; i++) + { + if (((*(uint32_t *)&(magic[0])) & *(uint32_t *)&(known_magic[i].mask[0])) == *(uint32_t *)&(known_magic[i].mode[0])) + { + f_type(fd, (char *)known_magic[i].type); + goto magic_found; + } + } + + /* stage two: try to determine the filetype from the file name */ + /* a smarter solution would be to get this info from /etc/mime.types */ + + ptr = strrchr(url.file , '.'); +#warning what about filenames without dots? + + if (ptr++) + { + if( strcasecmp(ptr, "cdr") == 0) f_type(fd, (char *) "audio/cdr"); + if( strcasecmp(ptr, "wav") == 0) f_type(fd, (char *) "audio/wave"); + if( strcasecmp(ptr, "aif") == 0) f_type(fd, (char *) "audio/aifc"); + if( strcasecmp(ptr, "snd") == 0) f_type(fd, (char *) "audio/snd"); + + /* they should be obsolete now due to the file magic detection */ + if( strcasecmp(ptr, "ogg") == 0) f_type(fd, (char *) "audio/ogg"); + if( strcasecmp(ptr, "mp3") == 0) f_type(fd, (char *) "audio/mpeg"); + if( strcasecmp(ptr, "mp2") == 0) f_type(fd, (char *) "audio/mpeg"); + if( strcasecmp(ptr, "mpa") == 0) f_type(fd, (char *) "audio/mpeg"); + } +magic_found: + ; + } + break; + } /* endswitch */ + + return fd; +} + +int f_close(FILE *stream) +{ + int i, rval; + + dprintf(stderr, "f_close: stream 0x%x\n", stream); + /* at first, lookup the stream in the stream type table and remove it */ + for(i=0 ; (idestructor) + { + dprintf(stderr, "f_close: calling stream filter destructor\n"); + cache[i].filter_arg->destructor(cache[i].filter_arg); + free(cache[i].filter_arg); + } + + dprintf(stderr, "f_close: destroying mutexes\n"); + pthread_mutex_destroy(&cache[i].cache_lock); + pthread_mutex_destroy(&cache[i].readable); + pthread_mutex_destroy(&cache[i].writeable); + + /* completely blank out all data */ + memset(&cache[i], 0, sizeof(STREAM_CACHE)); + } + else + rval = fclose(stream); + + return rval; +} + +long f_tell(FILE *stream) +{ + int i; + long rval; + + /* lookup the stream ID in the cache table */ + i = getCacheSlot(stream); + + if(i < 0) + return( ftell(stream) ); + + if(cache[i].fd == stream) + rval = cache[i].total_bytes_delivered; + else + rval = ftell(stream); + + return rval; +} + +void f_rewind(FILE *stream) +{ + int i; + + /* lookup the stream ID in the cache table */ + i = getCacheSlot(stream); + + if(i < 0) + { + rewind(stream); + return; + } + + if(cache[i].fd == stream) + { + /* nothing to do */ + } + else + rewind(stream); +} + +int f_seek(FILE *stream, long offset, int whence) +{ + int i, rval; + + /* lookup the stream ID in the cache table */ + i = getCacheSlot(stream); + + if(i < 0) + return( fseek(stream, offset, whence) ); + + if(cache[i].fd == stream) + { + dprintf(stderr, "WARNING: program tries to seek on a stream !! offset %d whence %d\n", (int) offset, whence); + rval = -1; + } + else + rval = fseek(stream, offset, whence); + + return rval; +} + +size_t f_read (void *ptr, size_t size, size_t nitems, FILE *stream) +{ + int i, rval; + + /* lookup the stream ID in the cache table */ + i = getCacheSlot(stream); + + if(i < 0) + return( fread(ptr, size, nitems, stream) ); + + if(cache[i].fd == stream) { + rval = pop(stream, (char*)ptr, size * nitems); + } + else + rval = fread(ptr, size, nitems, stream); + + return rval; +} + +char *f_type(FILE *stream, char *type) +{ + int i; + + /* lookup the stream in the stream type table */ + for(i=0 ; (i (cache[i].csize - cache[i].filled)) ? (cache[i].csize - cache[i].filled) : (len - rval); + + if(cache[i].wptr < cache[i].rptr) + { + amt[0] = cache[i].rptr - cache[i].wptr; + amt[1] = 0; + } + else + { + amt[0] = cache[i].ceiling - cache[i].wptr; + amt[1] = cache[i].rptr - cache[i].cache; + } + + for(j=0; j<2; j++) + { + if(amt[j] > blen) + amt[j] = blen; + + if(amt[j]) + { + memmove(cache[i].wptr, buf, amt[j]); + cache[i].wptr = cache[i].cache + + (((int)(cache[i].wptr - cache[i].cache) + amt[j]) % cache[i].csize); + + buf += amt[j]; /* adjust the target buffer pointer */ + rval += amt[j]; /* increase the 'total bytes' counter */ + blen -= amt[j]; /* decrease the block length counter */ + cache[i].filled += amt[j]; + } + } + + // dprintf(stderr, "push: %d/%d bytes written [filled: %d of %d], stream: %x\n", amt[0] + amt[1], rval, cache[i].filled, CACHESIZE, fd); + + pthread_mutex_unlock( &cache[i].cache_lock ); + + /* unlock the cache for read access, if it */ + /* contains some data */ + if(cache[i].filled){ + pthread_mutex_unlock( &cache[i].readable ); + } + } + + /* if there is still space in the cache, unlock */ + /* it again for further writing by anyone */ + if(cache[i].csize - cache[i].filled){ + pthread_mutex_unlock( & cache[i].writeable ); + } + else + dprintf(stderr, "push: buffer overrun; cache full - leaving cache locked\n"); + + } while(rval < len); + + dprintf(stderr, "push: exitstate: [filled: %3.1f %%], stream: %x\r", 100.0 * (float)cache[i].filled / (float)cache[i].csize, (int) fd); + + return rval; +} + +int pop(FILE *fd, char *buf, long len) +{ + int rval = 0, i, j; + int blen = 0; + + i = getCacheSlot(fd); + + if(i < 0) + return -1; + + dprintf(stderr, "pop: %d bytes requested [filled: %d of %d], stream: %x buf 0x%x\n", + (int) len, (int) cache[i].filled, (int) CACHESIZE, (int)fd, (int) buf); + + if(cache[i].fd == fd) + { + do + { + if(cache[i].closed && (!cache[i].filled) ) + return 0; + + if((!cache[i].filled) && feof(fd)){ + return 0; + } + + /* try to obtain read permissions for the cache */ + /* this will block if the cache is empty */ +#if 0 //FIXME no way to stop if connection dead ? + pthread_mutex_lock( & cache[i].readable ); +#else + while(true) { + int lret = pthread_mutex_trylock(&cache[i].readable); + if((lret == 0) || (CAudioPlayer::getInstance()->getState() == CBaseDec::STOP_REQ)) + break; + usleep(100); + } +#endif + if(cache[i].filled) + { + int amt[2]; + + /* prevent any modification to the cache by other threads, */ + /* mainly by the push() function, while we read data from it */ + pthread_mutex_lock( &cache[i].cache_lock ); + + /* block transfer length: get either what's there or */ + /* only as much as we need */ + blen = ((len - rval) > cache[i].filled) ? cache[i].filled : (len - rval); + + if(cache[i].rptr < cache[i].wptr) + { + amt[0] = cache[i].wptr - cache[i].rptr; + amt[1] = 0; + } + else + { + amt[0] = cache[i].ceiling - cache[i].rptr; + amt[1] = cache[i].wptr - cache[i].cache; + } + + for(j=0; j<2; j++) + { + if(amt[j] > blen) amt[j] = blen; + + if(amt[j]) + { + dprintf(stderr, "pop(): rptr: 0x%08x, buf: 0x%08x, amt[%d]=%d, blen=%d, len=%d, rval=%d\n", + (int) cache[i].rptr, (int) buf, j, amt[j], blen, (int) len, rval); + + memmove(buf, cache[i].rptr, amt[j]); + + cache[i].rptr = cache[i].cache + + (((int)(cache[i].rptr - cache[i].cache) + amt[j]) % cache[i].csize); + + buf += amt[j]; /* adjust the target buffer pointer */ + rval += amt[j]; /* increase the 'total bytes' counter */ + blen -= amt[j]; /* decrease the block length counter */ + cache[i].filled -= amt[j]; + } + } + + dprintf(stderr, "pop: %d/%d/%d bytes read [filled: %d of %d], stream: %x\n", amt[0] + amt[1], rval, (int) len, (int) cache[i].filled, (int) CACHESIZE, (int) fd); + + /* if the cache is closed and empty, then */ + /* force the end condition to be met */ + if(cache[i].closed && (! cache[i].filled)) + break;//len = rval; + + /* allow write access again */ + pthread_mutex_unlock( &cache[i].cache_lock ); + + /* unlock the cache for write access, if it */ + /* has some free space */ + if(cache[i].csize - cache[i].filled){ + pthread_mutex_unlock( &cache[i].writeable ); + } + } + + /* if there is still data in the cache, unlock */ + /* it again for further reading by anyone */ + if(cache[i].filled) + { + pthread_mutex_unlock( & cache[i].readable ); + } + else + dprintf(stderr, "pop: buffer underrun; cache empty - leaving cache locked\n"); + } while((rval < len) && (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP_REQ)); + } + else + { + dprintf(stderr, "pop: no cache present for stream %0x\n", (int) fd); + rval = -1; + } + +// dprintf(stderr, "pop: exitstate: [filled: %3.1f %%], stream: %x\n", 100.0 * (float)cache[i].filled / (float)cache[i].csize, fd); + + cache[i].total_bytes_delivered += rval; + + if(cache[i].filter_arg) + if(cache[i].filter_arg->state) + cache[i].filter_arg->state->buffered = 65536L * (long long)cache[i].filled / (long long)CACHESIZE; + + return rval; +} + +void CacheFillThread(void *c) +{ + char *buf; + STREAM_CACHE *scache = (STREAM_CACHE*)c; + int rval, datalen; + + if(scache->closed) + return; + + dprintf(stderr, "CacheFillThread: thread started, using stream %8x\n", (int) scache->fd); + + buf = (char*)malloc(CACHEBTRANS); + + if(!buf) + { + dprintf(stderr, "CacheFillThread: fatal error ! Could not allocate memory. Terminating.\n"); + exit(-1); + } + + /* endless loop; read a block of data from the stream */ + /* and push it into the cache */ + do + { + struct pollfd pfd; + + /* has a f_close() call in an other thread already closed the cache ? */ + datalen = CACHEBTRANS; + + pfd.fd = fileno(scache->fd); + pfd.events = POLLIN | POLLPRI; + pfd.revents = 0; + + int ret = poll(&pfd, 1, 1000); + + if (ret > 0 && (pfd.revents & POLLIN) == POLLIN) { +#if 0 //FIXME: fread blocks i/o on dead connection + rval = fread(buf, 1, datalen, scache->fd); + if ((rval == 0) && feof(scache->fd)) + break; /* exit cache fill thread if eof and nothing to push */ +#else + rval = read(fileno(scache->fd), buf, datalen); + if (rval == 0) + break; /* exit cache fill thread if eof and nothing to push */ +#endif + /* if there is a filter function set up for this stream, then */ + /* we need to call it with the propper arguments */ + if(scache->filter) + { + scache->filter_arg->buf = buf; + scache->filter_arg->len = &rval; + scache->filter(scache->filter_arg); + datalen = rval; + } + + if( push(scache->fd, buf, rval) < 0) + break; + } + } + while(!scache->closed); + //while( (rval == datalen) && (!scache->closed) ); + + /* close the cache if the stream disrupted */ + scache->closed = 1; + pthread_mutex_unlock( &scache->writeable ); + pthread_mutex_unlock( &scache->readable ); + + /* ... and exit this thread. */ + dprintf(stderr, "CacheFillThread: thread exited, stream %8x \n", (int) scache->fd); + + free(buf); + pthread_exit(0); +} + +/**************************** stream filter ******************************/ + +int f_status(FILE *stream, void (*cb)(void*)) +{ + int i, rval = -1; + + if(!stream) + { + strcpy(err_txt, "NULL pointer as stream id\n"); + return -1; + } + + /* lookup the stream ID in the cache table */ + i = getCacheSlot(stream); + + if(cache[i].fd == stream) + { + /* hook the users function into the steam filter */ + if(cache[i].filter_arg) + { + if(cache[i].filter_arg->state) + { + cache[i].filter_arg->state->cb = cb; + rval = 0; + } + else + strcpy(err_txt, "no cache[].filter_arg->state hook\n"); + } + else + strcpy(err_txt, "no cache[].filter_arg hook\n"); + } + + return rval; +} + +/* parse the meta data block and copy all relevant */ +/* information into the CSTATE structure */ +void ShoutCAST_ParseMetaData(char *md, CSTATE *state) +{ + #define SKIP(a) for(;(a && !isalnum(*a)); ++a); + char *ptr; + + /* abort if we were submitted a NULL pointer */ + if((!md) || (!state)) + return; + + dprintf(stderr, "ShoutCAST_ParseMetaData(%x : %s, %x)\n", (int) md, md, (int) state); + + ptr = strstr(md, "StreamTitle="); + + if(ptr) + { + /* look if there is a dash or a comma that separates artist and title */ + ptr = strstr(md, " - "); + + if(!ptr) + ptr = strstr(md, ", "); + + + /* no separator, simply copy everything into the 'title' field */ + if(!ptr) + { + ptr = strchr(md, '='); + strncpy(state->title, ptr + 2, 4096); + ptr = strchr(state->title, ';'); + if(ptr) + *(ptr - 1) = 0; + state->artist[0] = 0; + } + else + { + SKIP(ptr); + strcpy(state->title, ptr); + ptr = strchr(state->title, ';'); + if(ptr) + *(ptr - 1) = 0; + + ptr = strstr(md, "StreamTitle="); + ptr = strchr(ptr, '\''); + strncpy(state->artist, ptr + 1, 4096); + ptr = strstr(state->artist, " - "); + if(!ptr) + ptr = strstr(state->artist, ", "); + + if(ptr) + *ptr = 0; + } + state->state = RUNNING; + } +} + +void ShoutCAST_DestroyFilter(void *a) +{ + STREAM_FILTER *arg = (STREAM_FILTER*)a; + + if(arg->state) + free(arg->state), arg->state = NULL; + if(arg->user) + free(arg->user), arg->user = NULL; +} + +STREAM_FILTER *ShoutCAST_InitFilter(int meta_int) +{ + STREAM_FILTER *arg; + + arg = (STREAM_FILTER*)calloc(1, sizeof(STREAM_FILTER)); + + /* allocate our private data space, hook it into the */ + /* stream filter structure and initialize the variables */ + if(!arg->user) + { + arg->user = calloc(1, sizeof(FILTERDATA)); + arg->state = (CSTATE*)calloc(1, sizeof(CSTATE)); + ((FILTERDATA*)arg->user)->meta_int = meta_int; + arg->destructor = ShoutCAST_DestroyFilter; + } + + return arg; +} + +void ShoutCAST_MetaFilter(STREAM_FILTER *arg) +{ + FILTERDATA *filterdata = (FILTERDATA*)arg->user; + int meta_int = filterdata->meta_int; + int len = *arg->len; + char*buf = (char*)arg->buf; + int meta_start; + + /* bug trap */ + if(!arg) + return; + +#if 0 + dprintf(stderr, "filter : cnt : %d\n", filterdata->cnt); + dprintf(stderr, "filter : len : %d\n", filterdata->len); + dprintf(stderr, "filter : stored : %d\n", filterdata->stored); + dprintf(stderr, "filter : cnt + len: %d\n", filterdata->cnt + len); + dprintf(stderr, "filter : meta_int : %d\n", filterdata->meta_int); +#endif + /* not yet all meta data has been processed */ + if(filterdata->stored < filterdata->len) + { + int bsize = (filterdata->len + 1) - filterdata->stored; + + /* if there is some meta data, extract it */ + /* there can be zero size blocks too */ + if(filterdata->len) + { + dprintf(stderr, "filter : ********* partitioned metadata block part 2 ******\n"); + + memmove(filterdata->meta_data + filterdata->stored, buf, bsize); + + ShoutCAST_ParseMetaData(filterdata->meta_data, arg->state); + + /* call the users callback function */ + if(arg->state->cb) + arg->state->cb(arg->state); + + //dprintf(stderr, "filter : metadata : \n\n\n----------\n%s\n----------\n\n\n", filterdata->meta_data); + + /* remove the metadata and it's size indicator from the buffer */ + memmove(buf, buf + bsize, len - bsize); + + *arg->len -= bsize; + filterdata->cnt = len - bsize; + filterdata->stored = 0; + filterdata->len = 0; + } + return; + } + + if((filterdata->cnt < meta_int) && ((filterdata->cnt + len) <= meta_int)) + { + /* do nothing; leave the data block and the length variable */ + /* untouched, update our private counter and return */ + filterdata->cnt += len; + return; + } + + /* does a meta data block start in the current block ? */ + if((filterdata->cnt <= meta_int) && ((filterdata->cnt + len) > meta_int)) + { + meta_start = meta_int - filterdata->cnt; + + /* the first byte of the meta data block tells us how long it is */ + filterdata->len = 16 * (int)buf[meta_start]; + + dprintf(stderr, "filter : ---> metadata %d bytes @ %d\n", filterdata->len, meta_start); + + /****************************************************************/ + /* case A: the meta data is completely within the current block */ + /****************************************************************/ + if((meta_start + filterdata->len) <= len) + { + int b = meta_start + filterdata->len + 1; + + /* if there is some meta data, extract it; */ + /* there can be zero size blocks too */ + if(filterdata->len) + { + memset(filterdata->meta_data, 0, 4096); + memmove(filterdata->meta_data, buf + meta_start, filterdata->len); + + ShoutCAST_ParseMetaData(filterdata->meta_data, arg->state); + + /* call the users callback function */ + if(arg->state->cb) + arg->state->cb(arg->state); + + //dprintf(stderr, "filter : metadata : \n\n\n----------\n%s\n----------\n\n\n", filterdata->meta_data); + } + + /* remove the metadata and it's size indicator from the buffer */ + memmove(buf + meta_start, buf + b, len - b ); + + /* adjust the buffersize */ + *arg->len -= filterdata->len + 1; + filterdata->cnt = len - b; + filterdata->stored = 0; + filterdata->len = 0; + } + + /************************************************************************/ + /* case B: the meta data is partitioned and continues in the next block */ + /************************************************************************/ + else + { + dprintf(stderr, "filter : ********* partitioned metadata block part 1 ******\n"); + + /* if there is some meta data, extract it */ + /* there can be zero size blocks too */ + if(filterdata->len) + { + memset(filterdata->meta_data, 0, 4096); + filterdata->stored = len - meta_start; + memmove(filterdata->meta_data, buf + meta_start, filterdata->stored); + } + + *arg->len = meta_start; + filterdata->cnt = 0; + } + } +} + +/**************************** utility functions ******************************/ +void parseURL_url(URL& url) { + /* now lets see what we have ... */ + char buffer[2048]; +// printf("parseURL_url: %s\n", url.url); + char *ptr = strstr(url.url, "://"); + if (!ptr) + { + url.access_mode = MODE_FILE; + strcpy(url.file, url.url); + url.host[0] = 0; + url.port = 0; + url.logindata[0] = 0; + } + else + { + strcpy(url.host, ptr + 3); + + /* select the appropriate transport modes */ + transport("http", MODE_HTTP, HTTP11); + transport("icy", MODE_HTTP, SHOUTCAST); + transport("scast", MODE_SCAST, SHOUTCAST); + //findme + transport("icast", MODE_ICAST, SHOUTCAST); + + /* if we fetch a playlist file, then set the access mode */ + /* that it will be parsed and processed automatically. If */ + /* it does not fail, then the call returns with an opened stream */ + +/* this currently results in an endless loop due to recursive calls of f_open() + if((url.access_mode == HTTP11) && (strstr(url.url, ".pls"))) + { + url.access_mode = MODE_PLS; + url.proto_version = SHOUTCAST; + } +*/ + + /* extract the file path from the url */ + ptr = strchr(ptr + 3, '/'); + if(ptr) + strcpy(url.file, ptr); + else + strcpy(url.file, "/"); + + /* extract the host part from the url */ + ptr = strchr(url.host, '/'); + if(ptr) + *ptr = 0; + + if ((ptr = strchr(url.host, '@'))) + { + *ptr = 0; + base64_encode(buffer, url.host); + strcpy(url.logindata, buffer); + + strcpy(buffer, ptr + 1); + strcpy(url.host, buffer); + } + else + url.logindata[0] = 0; + + ptr = strrchr(url.host, ':'); + + if(ptr) + { + url.port = atoi(ptr + 1); + *ptr = 0; + } + } +} + +int base64_encode(char *dest, const char *src) +{ + int retval = 1; + int src_len, src_pos; + char symbols[65]; + char mypart[3]; + char mybits[24]; + int part_pos; + int null_bytes = 0; + + if (dest != NULL && src != NULL && (src_len = strlen(src)) > 0) + { + strcpy(symbols,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + char buffer[src_len*2]; + char *buf_ptr = buffer; + + for (src_pos = 0; src_pos < src_len; src_pos +=3) + { + for (part_pos = 0; part_pos < 3; part_pos++) + { + if (src[src_pos+part_pos] != 0) + mypart[part_pos] = src[src_pos+part_pos]; + else + { + null_bytes = 3 - part_pos; + mypart[part_pos] = 0; + if (part_pos < 2) /* here: part_pos == 1, cannot be 0 */ + mypart[part_pos+1] = 0; + break; + } + } + for (part_pos = 0; part_pos < 24; part_pos++) + mybits[part_pos] = ( mypart[part_pos/8] >> (7 - (part_pos%8)) ) & 0x1; + for (part_pos = 0; part_pos < 24; part_pos+=6) + { + *buf_ptr = (mybits[part_pos] << 5) | + (mybits[part_pos+1] << 4) | (mybits[part_pos+2] << 3) | (mybits[part_pos+3] << 2) | + (mybits[part_pos+4] << 1) | mybits[part_pos+5]; + buf_ptr++; + } + } + for (part_pos = 0 ; part_pos < buf_ptr - buffer - null_bytes; part_pos++) + dest[part_pos] = symbols[(int)buffer[part_pos]]; + for (part_pos = buf_ptr - buffer - null_bytes ; part_pos < buf_ptr - buffer; part_pos++) + dest[part_pos] = '='; + } + else + retval = 0; + + return retval; +} + diff --git a/src/driver/netfile.h b/src/driver/netfile.h new file mode 100644 index 000000000..97a89e1d5 --- /dev/null +++ b/src/driver/netfile.h @@ -0,0 +1,214 @@ +/******************************************************************/ +/* netfile - a URL capable wrapper for the fopen() call */ +/* */ +/* usage: include 'netfile.h' in your code as *LAST* file */ +/* after all other include files and add netfile.c to your */ +/* sources. That's it. The include file maps the common */ +/* fopen() call onto a URL capable version that handles */ +/* files hosted on http servers like local ones. */ +/* */ +/* http example: */ +/* fd = fopen("http://find.me:666/somewhere/foo.bar", "r"); */ +/* */ +/* shoutcast example: */ +/* fd = fopen("icy://find.me:666/funky/station/", "r"); */ +/* */ +/* shoutcast example: (opens the shoutcast station 666 directly) */ +/* fd = fopen("scast://666", "r"); */ +/* */ +/* NOTE: only read access is implemented ! */ +/******************************************************************/ +#ifndef NETFILE_H +#define NETFILE_H 1 + +/* whether we should open an *.url file as plain file or the url */ +/* contained in it instead */ +//#define DISABLE_URLFILES 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define dprintf if(debug) fprintf + +/* file access modes */ +#define ACC_RO 0 +#define ACC_RW 1 +#define ACC_UD 2 /* -> this is used for the 'r+' mode */ +#define ACC_WO 3 + +#define MODE_FILE 0 +#define MODE_HTTP 1 +#define MODE_SCAST 2 /* pseudo transfer mode; is actually HTTP/SHOUTCAST */ +#define MODE_ICAST 3 /* pseudo transfer mode; is actually HTTP/SHOUTCAST */ +#define MODE_PLS 4 /* pseudo transfer mode; is actually HTTP/SHOUTCAST */ + +#define HTTP10 0 +#define HTTP11 1 +#define SHOUTCAST 2 + +#define MAX_REDIRECTS 5 /* follow this amount of redirects */ + +#define CONNECTING 1 /* not used */ +#define BUFFERING 2 /* not used */ +#define RUNNING 3 + +/* map all fopen() calls onto out f_open() function */ +#define fopen f_open +#define fclose f_close +#define fread f_read +#define ftell f_tell +#define rewind f_rewind +#define fseek f_seek +#define fstatus f_status +#define ftype f_type + +extern FILE *f_open(const char *, const char *); +extern int f_close(FILE *); +extern size_t f_read (void *, size_t, size_t, FILE *); +extern long f_tell(FILE *); +extern void f_rewind(FILE *); +extern int f_seek(FILE *, long, int); +extern int f_status(FILE *, void (*)(void*)); +extern char *f_type(FILE*, char*); + +extern char err_txt[2048]; + +#define CACHESIZE cache_size +#define CACHEENTMAX 20 /* at most 20 caches are available */ +#define CACHEBTRANS 1024 /* blocksize for the stream-to-cache transfer */ + +typedef struct +{ + int access_mode; /* access mode; FILE or HTTP */ + int proto_version; /* 0= 1.0; 1 = 1.1; 2 = shoutcast */ + char url[2048]; /* universal resource locator */ + char host[2048]; + int port; + char file[2048]; + char entity[2048]; /* data to send on POST requests */ + int fd; /* filedescriptor of the file*/ + FILE *stream; /* streamdescriptor */ + char logindata[2048]; /* base64 encoded auhtentication string of "username:password" */ +} URL; + +typedef struct +{ + void (*cb)(void *); /* user provided callback function */ + void *user; /* user date hook point */ + int state; /* CONNECTING, BUFFERING, RUNNING */ + int bitrate; + int buffered; /* "waterlevel" in the cache; 0 ... 65535 */ + char station_url[1024]; /*station url */ + char station[1024]; /* station name */ + char genre[4096]; /* station genre */ + char artist[4096]; /* artist currently playing */ + char title[4096]; /* title currently playing */ +} CSTATE; + +typedef struct +{ + void *buf; /* start of the buffer */ + int *len; /* pointer to a variable containing the length of the buffer */ + void *arg; /* pointer to some arguments for the filter function */ + void *user; /* here the filter function can hook in */ + /* some private data */ + void (*destructor)(void*); /* stream filter destructor */ + + CSTATE *state; +} STREAM_FILTER; + +typedef struct +{ + int cnt; /* counter */ + int len; /* meta block length */ + int stored; /* number of bytes already stored */ + int meta_int; /* meta data intervall */ + char meta_data[4096]; /* meta blocks cam be at most 4096 bytes */ +} FILTERDATA; + +typedef struct +{ + FILE *fd; /* stream ID */ + + int acc_mode; /* ACC_RO, ACC_RW, ACC_UD (unused yet) */ + + char *cache; /* cache buffer */ + char *ceiling; /* cache ceiling */ + int csize; /* cache size */ + char *wptr; /* next write position */ + char *rptr; /* next read position */ + long filled; + int closed; /* flag; closed = 1 if supply thread */ + /* has been terminated due to a */ + /* disrupted incoming stream or an EOF */ + long total_bytes_delivered; + + pthread_t fill_thread; + pthread_attr_t attr; + pthread_mutex_t cache_lock; + pthread_mutexattr_t cache_lock_attr; + + pthread_mutex_t readable; + pthread_mutexattr_t readable_attr; + + pthread_mutex_t writeable; + pthread_mutexattr_t writeable_attr; + + void (*filter)(STREAM_FILTER*); /* stream filter function */ + + STREAM_FILTER *filter_arg; /* place to hook in a pointer to the arguments */ +} STREAM_CACHE; + +typedef struct +{ + FILE *stream; + char type[65]; +} STREAM_TYPE; + +typedef struct +{ + char magic[3]; /* "ID3" */ + char version[2]; /* version of the tag */ + char flags; + char size[4]; + char base[1024]; + int len; +} ID3; + +typedef struct +{ + char id[4]; + uint32_t size; + char flags[3]; + char base[1024]; +} ID3_frame; + +#define CRLFCut(a) \ +{ \ + char *_ptr; \ + _ptr = strchr(a, '\r'); \ + if(_ptr) *_ptr = 0; \ + else { \ + _ptr = strchr(a, '\n'); \ + if(_ptr) *_ptr = 0; } \ +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/driver/pictureviewer/Makefile.am b/src/driver/pictureviewer/Makefile.am new file mode 100644 index 000000000..5c28f9a4f --- /dev/null +++ b/src/driver/pictureviewer/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libconfigfile \ + @FREETYPE_CFLAGS@ \ + -I$(top_srcdir)/lib + +noinst_LIBRARIES = libneutrino_pictureviewer.a + +libneutrino_pictureviewer_a_SOURCES = \ + pictureviewer.cpp fb_display.cpp \ + png.cpp bmp.cpp resize.cpp jpeg.cpp \ + gif.cpp crw.cpp make15color_errdiff.cpp diff --git a/src/driver/pictureviewer/bmp.cpp b/src/driver/pictureviewer/bmp.cpp new file mode 100644 index 000000000..84ba4be2a --- /dev/null +++ b/src/driver/pictureviewer/bmp.cpp @@ -0,0 +1,263 @@ +#include "config.h" +#ifdef FBV_SUPPORT_BMP +#include "pictureviewer.h" +#include + +#include +#include +#include +#include + +#define BMP_TORASTER_OFFSET 10 +#define BMP_SIZE_OFFSET 18 +#define BMP_BPP_OFFSET 28 +#define BMP_RLE_OFFSET 30 +#define BMP_COLOR_OFFSET 54 + +#define fill4B(a) ( ( 4 - ( (a) % 4 ) ) & 0x03) + +struct color { + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +int fh_bmp_id(const char *name) +{ + int fd; + char id[2]; + + fd = open(name, O_RDONLY); + if (fd == -1) { +// dbout("fh_bmp_id {\n"); + return(0); + } + + read(fd, id, 2); + close(fd); + if ( id[0]=='B' && id[1]=='M' ) { + return(1); + } + return(0); +} + +void fetch_pallete(int fd, struct color pallete[], int count) +{ +// dbout("fetch_palette {\n"); + unsigned char buff[4]; + int i; + + lseek(fd, BMP_COLOR_OFFSET, SEEK_SET); + for (i=0; i0) + bytes++; + unsigned char* tbuffer = (unsigned char*) malloc(bytes); + if(tbuffer==NULL) + { + printf("Error: malloc\n"); + return (FH_ERROR_MALLOC); + } + for (i=0; i>4; + c2 = tbuffer[j] & 0x0f; + *wr_buffer++ = pallete[c1].red; + *wr_buffer++ = pallete[c1].green; + *wr_buffer++ = pallete[c1].blue; + *wr_buffer++ = pallete[c2].red; + *wr_buffer++ = pallete[c2].green; + *wr_buffer++ = pallete[c2].blue; + } + if (x%2) { + c1 = tbuffer[j]>>4; + *wr_buffer++ = pallete[c1].red; + *wr_buffer++ = pallete[c1].green; + *wr_buffer++ = pallete[c1].blue; + } + if (skip) { + read(fd, buff, skip); + } + wr_buffer -= x*6; /* backoff 2 lines - x*2 *3 */ + } + free(tbuffer); + } + break; + case 8: /* 8bit palletized */ + { + skip = fill4B(x); + fetch_pallete(fd, pallete, 256); + lseek(fd, raster, SEEK_SET); + unsigned char* tbuffer = (unsigned char*) malloc(x); + if(tbuffer==NULL) + { + printf("Error: malloc\n"); + return (FH_ERROR_MALLOC); + } + for (i=0; i +#include +#ifdef FBV_SUPPORT_CRW + #include + #include + #include +#include +#include + +extern "C" { +#include +} + #include + #include "pictureviewer.h" + +/* + Get a 2-byte integer, making no assumptions about CPU byte order. + Nor should we assume that the compiler evaluates left-to-right. + */ +unsigned short fget2 (FILE *f,int order) +{ + unsigned char a, b; + + a = fgetc(f); + b = fgetc(f); + if (order == 0x4949) /* "II" means little-endian */ + return a + (b << 8); + else /* "MM" means big-endian */ + return (a << 8) + b; +} + +/* + Same for a 4-byte integer. + */ +int fget4 (FILE *f,int order) +{ + unsigned char a, b, c, d; + + a = fgetc(f); + b = fgetc(f); + c = fgetc(f); + d = fgetc(f); + if (order == 0x4949) + return a + (b << 8) + (c << 16) + (d << 24); + else + return (a << 24) + (b << 16) + (c << 8) + d; +} + +int fh_crw_parsedirs(FILE *fh, int pos, int length, int order) +{ + fseek(fh,pos+length-4,SEEK_SET); + int off=pos+fget4(fh,order); + fseek(fh,off,SEEK_SET); + int nEntrys=fget2(fh,order); + + for (int i = 0; i < nEntrys; i++) + { + int type = fget2(fh,order); + int len = fget4(fh,order); + int roff = fget4(fh,order); + + switch(type) + { + case 0x2005: // Image + break; + case 0x2007: // Thumbnail + fseek(fh,pos+roff,SEEK_SET); + return 1; + break; + case 0x0810: // Owner + break; + case 0x0816: // Filename + break; + case 0x0817: // Thumbname + break; + case 0x580b: // SerNo. + break; + case 0x0805: // comment, if subdir 0x300a. "EOS 300D DIGITAL CMOS RAW" if subdir 0x2804 + break; + case 0x080a: // vendor \0 name of the camera + break; + case 0x080b: // firmware + break; + case 0x0815: + break; + case 0x180e: // time + break; + case 0x102a: // White balance + break; + case 0x1031: // size of the image + break; + case 0x1835: // decoder table + break; + default: + if (type >> 8 == 0x28 || type >> 8 == 0x30) // Subdirs + { + if (fh_crw_parsedirs(fh,pos+roff,len,order)==1) + { + return 1; + } + } + } + } + return 0; +} + +void fh_crw_find_jpeg_thumbnail(FILE *fh) +{ + char header[26]; + int order=0; + long fsize=0; + long hlength=0; + + fseek (fh, 0, SEEK_SET); + fread (header, 1, 26, fh); + fseek (fh, 0, SEEK_END); + fsize = ftell(fh); + fseek (fh, 0, SEEK_SET); + + order=*((unsigned short*)header); + + if (order == 0x4949) + hlength = header[2] + (header[3] << 8) + (header[4] << 16) + (header[5] << 24); + else + hlength = (header[2] << 24) + (header[3] << 16) + (header[4] << 8) + header[5]; + + fh_crw_parsedirs(fh,hlength,fsize-hlength,order); +} + +struct r_crw_jpeg_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf envbuffer; +}; + +int fh_crw_id(const char *name) +{ +// dbout("fh_crw_id {\n"); + int fd; + unsigned char id[14]; + fd=open(name,O_RDONLY); if(fd==-1) return(0); + read(fd,id,14); + close(fd); +// dbout("fh_crw_id }\n"); + if(id[6]=='H' && id[7]=='E' && id[8]=='A' && id[9]=='P' && + id[10]=='C' && id[11]=='C' && id[12]=='D' && id[13]=='R') return(1); + + return(0); +} + + +void crw_cb_error_exit(j_common_ptr cinfo) +{ +// dbout("crw_cb_error_exit {\n"); + struct r_crw_jpeg_error_mgr *mptr; + mptr=(struct r_crw_jpeg_error_mgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(mptr->envbuffer,1); +// dbout("crw_cb_error_exit }\n"); +} + +int fh_crw_load(const char *filename,unsigned char **buffer,int* xp,int* yp) +{ +// dbout("fh_crw_load (%d/%d) {\n",x,y); + struct jpeg_decompress_struct cinfo; + struct jpeg_decompress_struct *ciptr; + struct r_crw_jpeg_error_mgr emgr; + unsigned char *bp; + int px,py,c,x=*xp; + FILE *fh; + JSAMPLE *lb; + + ciptr=&cinfo; + if(!(fh=fopen(filename,"rb"))) return(FH_ERROR_FILE); + ciptr->err=jpeg_std_error(&emgr.pub); + emgr.pub.error_exit=crw_cb_error_exit; + if(setjmp(emgr.envbuffer)==1) + { + // FATAL ERROR - Free the object and return... + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_crw_load } - FATAL ERROR\n"); + return(FH_ERROR_FORMAT); + } + + jpeg_create_decompress(ciptr); + fh_crw_find_jpeg_thumbnail(fh); + jpeg_stdio_src(ciptr,fh); + jpeg_read_header(ciptr,TRUE); + ciptr->out_color_space=JCS_RGB; + if(x==(int)ciptr->image_width) + ciptr->scale_denom=1; + else if(abs(x*2 - ciptr->image_width) < 2) + ciptr->scale_denom=2; + else if(abs(x*4 - ciptr->image_width) < 4) + ciptr->scale_denom=4; + else if(abs(x*8 - ciptr->image_width) < 8) + ciptr->scale_denom=8; + else + ciptr->scale_denom=1; + + jpeg_start_decompress(ciptr); + + px=ciptr->output_width; py=ciptr->output_height; + c=ciptr->output_components; + + + if(c==3) + { + lb=(JSAMPLE*)(*ciptr->mem->alloc_small)((j_common_ptr) ciptr,JPOOL_PERMANENT,c*px); + bp=*buffer; + while(ciptr->output_scanline < ciptr->output_height) + { + jpeg_read_scanlines(ciptr, &lb, 1); + memcpy(bp,lb,px*c); + bp+=px*c; + } + + } + jpeg_finish_decompress(ciptr); + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_crw_load }\n"); + return(FH_ERROR_OK); +} + +int fh_crw_getsize(const char *filename,int *x,int *y, int wanted_width, int wanted_height) +{ +// dbout("fh_crw_getsize {\n"); + struct jpeg_decompress_struct cinfo; + struct jpeg_decompress_struct *ciptr; + struct r_crw_jpeg_error_mgr emgr; + + int px,py,c; + FILE *fh; + + ciptr=&cinfo; + if(!(fh=fopen(filename,"rb"))) return(FH_ERROR_FILE); + + ciptr->err=jpeg_std_error(&emgr.pub); + emgr.pub.error_exit=crw_cb_error_exit; + if(setjmp(emgr.envbuffer)==1) + { + // FATAL ERROR - Free the object and return... + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_crw_getsize } - FATAL ERROR\n"); + return(FH_ERROR_FORMAT); + } + + jpeg_create_decompress(ciptr); + fh_crw_find_jpeg_thumbnail(fh); + jpeg_stdio_src(ciptr,fh); + jpeg_read_header(ciptr,TRUE); + ciptr->out_color_space=JCS_RGB; + // should be more flexible... + if((int)ciptr->image_width/8 >= wanted_width || + (int)ciptr->image_height/8 >= wanted_height) + ciptr->scale_denom=8; + else if((int)ciptr->image_width/4 >= wanted_width || + (int)ciptr->image_height/4 >= wanted_height) + ciptr->scale_denom=4; + else if((int)ciptr->image_width/2 >= wanted_width || + (int)ciptr->image_height/2 >= wanted_height) + ciptr->scale_denom=2; + else + ciptr->scale_denom=1; + + jpeg_start_decompress(ciptr); + px=ciptr->output_width; py=ciptr->output_height; + c=ciptr->output_components; + *x=px; *y=py; +// jpeg_finish_decompress(ciptr); + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_crw_getsize }\n"); + return(FH_ERROR_OK); +} + +#endif diff --git a/src/driver/pictureviewer/fb_display.cpp b/src/driver/pictureviewer/fb_display.cpp new file mode 100644 index 000000000..0022c2be7 --- /dev/null +++ b/src/driver/pictureviewer/fb_display.cpp @@ -0,0 +1,349 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fb_display.h" +#include "pictureviewer.h" +#include "driver/framebuffer.h" +/* + * FrameBuffer Image Display Function + * (c) smoku/2000 + * + */ + +/* Public Use Functions: + * + * extern void fb_display(unsigned char *rgbbuff, + * int x_size, int y_size, + * int x_pan, int y_pan, + * int x_offs, int y_offs); + * + * extern void getCurrentRes(int *x,int *y); + * + */ + +static unsigned short red[256], green[256], blue[256]; +static struct fb_cmap map332 = {0, 256, red, green, blue, NULL}; +static unsigned short red_b[256], green_b[256], blue_b[256]; +static struct fb_cmap map_back = {0, 256, red_b, green_b, blue_b, NULL}; + +static unsigned char *lfb = 0; + +int openFB(const char *name); +//void closeFB(int fh); +//void getVarScreenInfo(int fh, struct fb_var_screeninfo *var); +//void setVarScreenInfo(int fh, struct fb_var_screeninfo *var); +void getFixScreenInfo(struct fb_fix_screeninfo *fix); +void set332map(); +void blit2FB(void *fbbuff, + unsigned int pic_xs, unsigned int pic_ys, + unsigned int scr_xs, unsigned int scr_ys, + unsigned int xp, unsigned int yp, + unsigned int xoffs, unsigned int yoffs, + int cpp); +void clearFB(int bpp,int cpp); +inline unsigned short make16color(uint32_t r, uint32_t g, + uint32_t b, uint32_t rl, + uint32_t ro, uint32_t gl, + uint32_t go, uint32_t bl, + uint32_t bo, uint32_t tl, + uint32_t to); + +void fb_display(unsigned char *rgbbuff, int x_size, int y_size, int x_pan, int y_pan, int x_offs, int y_offs, bool clearfb, int transp) +{ + struct fb_var_screeninfo *var; + unsigned short *fbbuff = NULL; + int bp = 0; + if(rgbbuff==NULL) + return; + /* read current video mode */ + var = CFrameBuffer::getInstance()->getScreenInfo(); + lfb = (unsigned char *)CFrameBuffer::getInstance()->getFrameBufferPointer(); + + /* correct panning */ + if(x_pan > x_size - (int)var->xres) x_pan = 0; + if(y_pan > y_size - (int)var->yres) y_pan = 0; + /* correct offset */ + if(x_offs + x_size > (int)var->xres) x_offs = 0; + if(y_offs + y_size > (int)var->yres) y_offs = 0; + +//printf("fb_display: bits_per_pixel: %d\n", var->bits_per_pixel); +//printf("fb_display: var->xres %d var->yres %d x_size %d y_size %d\n", var->xres, var->yres, x_size, y_size); + /* blit buffer 2 fb */ + fbbuff = (unsigned short *) convertRGB2FB(rgbbuff, x_size, y_size, var->bits_per_pixel, &bp, transp); + if(fbbuff==NULL) + return; + /* ClearFB if image is smaller */ + //if(x_size < (int)var->xres || y_size < (int)var->yres) + if(clearfb) + clearFB(var->bits_per_pixel, bp); + blit2FB(fbbuff, x_size, y_size, var->xres, var->yres, x_pan, y_pan, x_offs, y_offs, bp); + free(fbbuff); +} + +void getCurrentRes(int *x, int *y) +{ + struct fb_var_screeninfo *var; + var = CFrameBuffer::getInstance()->getScreenInfo(); + *x = var->xres; + *y = var->yres; +//printf("getCurrentRes: %dx%d\n", var->xres, var->yres); +} + +void make332map(struct fb_cmap *map) +{ + int rs, gs, bs, i; + int r = 8, g = 8, b = 4; + + map->red = red; + map->green = green; + map->blue = blue; + + rs = 256 / (r - 1); + gs = 256 / (g - 1); + bs = 256 / (b - 1); + + for (i = 0; i < 256; i++) { + map->red[i] = (rs * ((i / (g * b)) % r)) * 255; + map->green[i] = (gs * ((i / b) % g)) * 255; + map->blue[i] = (bs * ((i) % b)) * 255; + } +} +/* +void set8map(int fh, struct fb_cmap *map) +{ + if (ioctl(fh, FBIOPUTCMAP, map) < 0) { + fprintf(stderr, "Error putting colormap"); + exit(1); + } +} + +void get8map(int fh, struct fb_cmap *map) +{ + if (ioctl(fh, FBIOGETCMAP, map) < 0) { + fprintf(stderr, "Error getting colormap"); + exit(1); + } +} +*/ +void set332map() +{ + make332map(&map332); + CFrameBuffer::getInstance()->paletteSet(&map332); +} + +void blit2FB(void *fbbuff, + unsigned int pic_xs, unsigned int pic_ys, + unsigned int scr_xs, unsigned int scr_ys, + unsigned int xp, unsigned int yp, + unsigned int xoffs, unsigned int yoffs, + int cpp) +{ + int i, xc, yc; + unsigned char *cp; unsigned short *sp; unsigned int *ip; + ip = (unsigned int *) fbbuff; + sp = (unsigned short *) ip; + cp = (unsigned char *) sp; + + xc = (pic_xs > scr_xs) ? scr_xs : pic_xs; + yc = (pic_ys > scr_ys) ? scr_ys : pic_ys; + + unsigned int stride = CFrameBuffer::getInstance()->getStride(); + + switch(cpp){ + case 1: + set332map(); + for(i = 0; i < yc; i++){ + memcpy(lfb+(i+yoffs)*stride+xoffs*cpp, cp + (i+yp)*pic_xs+xp,xc*cpp); + } + break; + case 2: + for(i = 0; i < yc; i++){ + memcpy(lfb+(i+yoffs)*stride+xoffs*cpp, sp + (i+yp)*pic_xs+xp, xc*cpp); + } + break; + case 4: + for(i = 0; i < yc; i++){ + memcpy(lfb+(i+yoffs)*stride+xoffs*cpp, ip + (i+yp)*pic_xs+xp, xc*cpp); + } + break; + } +} + +void clearFB(int bpp, int cpp) +{ + int x,y; + getCurrentRes(&x,&y); + unsigned int stride = CFrameBuffer::getInstance()->getStride(); + +//printf("clearFB: stride %d y %d cpp %d bpp %d total %d\n", stride, y, cpp, bpp, stride*y*cpp); + switch(cpp){ + case 2: + { + uint32_t rl, ro, gl, go, bl, bo, tl, to; + unsigned int i; + struct fb_var_screeninfo *var; + var = CFrameBuffer::getInstance()->getScreenInfo(); + rl = (var->red).length; + ro = (var->red).offset; + gl = (var->green).length; + go = (var->green).offset; + bl = (var->blue).length; + bo = (var->blue).offset; + tl = (var->transp).length; + to = (var->transp).offset; + short black=make16color(0,0,0, rl, ro, gl, go, bl, bo, tl, to); + unsigned short *s_fbbuff = (unsigned short *) malloc(y*stride/2 * sizeof(unsigned short)); + if(s_fbbuff==NULL) + { + printf("Error: malloc\n"); + return; + } + + for(i = 0; i < y*stride/2; i++) + s_fbbuff[i] = black; + memcpy(lfb, s_fbbuff, y*stride); + free(s_fbbuff); + } + break; + case 4: + { + uint32_t col = 0xFF000000; + uint32_t * dest = (uint32_t *) lfb; + for(unsigned int i = 0; i < stride*y/4; i ++) + dest[i] = col; + } + break; + default: + //memset(lfb, 0xFF, stride*y); + memset(lfb, 0, stride*y); + break; + } +} + +inline unsigned char make8color(unsigned char r, unsigned char g, unsigned char b) +{ + return ( + (((r >> 5) & 7) << 5) | + (((g >> 5) & 7) << 2) | + ((b >> 6) & 3) ); +} + +inline unsigned short make15color(unsigned char r, unsigned char g, unsigned char b) +{ + return ( + (((b >> 3) & 31) << 10) | + (((g >> 3) & 31) << 5) | + ((r >> 3) & 31) ); +} + +inline unsigned short make16color(uint32_t r, uint32_t g, uint32_t b, + uint32_t rl, uint32_t ro, + uint32_t gl, uint32_t go, + uint32_t bl, uint32_t bo, + uint32_t tl, uint32_t to) +{ + return ( + // ((0xFF >> (8 - tl)) << to) | + ((r >> (8 - rl)) << ro) | + ((g >> (8 - gl)) << go) | + ((b >> (8 - bl)) << bo)); +} + +void* convertRGB2FB(unsigned char *rgbbuff, unsigned long x, unsigned long y, int bpp, int *cpp, int transp) +{ + unsigned long i; + void *fbbuff = NULL; + unsigned char *c_fbbuff; + unsigned short *s_fbbuff; + unsigned int *i_fbbuff; + unsigned long count = x*y; + uint32_t rl, ro, gl, go, bl, bo, tl, to; + + struct fb_var_screeninfo *var; + var = CFrameBuffer::getInstance()->getScreenInfo(); + rl = (var->red).length; + ro = (var->red).offset; + gl = (var->green).length; + go = (var->green).offset; + bl = (var->blue).length; + bo = (var->blue).offset; + tl = (var->transp).length; + to = (var->transp).offset; + + switch(bpp) + { + case 8: + *cpp = 1; + c_fbbuff = (unsigned char *) malloc(count * sizeof(unsigned char)); + if(c_fbbuff==NULL) + { + printf("Error: malloc\n"); + return NULL; + } + for(i = 0; i < count; i++) + c_fbbuff[i] = make8color(rgbbuff[i*3], rgbbuff[i*3+1], rgbbuff[i*3+2]); + fbbuff = (void *) c_fbbuff; + break; + case 15: + *cpp = 2; +#if HAVE_DVB_API_VERSION < 3 + s_fbbuff = (unsigned short *) malloc(count * sizeof(unsigned short)); + if(s_fbbuff==NULL) + { + printf("Error: malloc\n"); + return NULL; + } + for(i = 0; i < count ; i++) + s_fbbuff[i] = make15color(rgbbuff[i*3], rgbbuff[i*3+1], rgbbuff[i*3+2]); +#else + s_fbbuff = (unsigned short*) make15color_errdiff(rgbbuff, x, y); +#endif + fbbuff = (void *) s_fbbuff; + break; + case 16: + *cpp = 2; +#if HAVE_DVB_API_VERSION < 3 + s_fbbuff = (unsigned short *) malloc(count * sizeof(unsigned short)); + if(s_fbbuff==NULL) + { + printf("Error: malloc\n"); + return NULL; + } + for(i = 0; i < count ; i++) + s_fbbuff[i]=make16color(rgbbuff[i*3], rgbbuff[i*3+1], rgbbuff[i*3+2], rl, ro, gl, go, bl, bo, tl, to); +#else + s_fbbuff = (unsigned short*) make15color_errdiff(rgbbuff, x, y); +#endif + fbbuff = (void *) s_fbbuff; + break; + case 24: + case 32: + *cpp = 4; + i_fbbuff = (unsigned int *) malloc(count * sizeof(unsigned int)); + if(i_fbbuff==NULL) + { + printf("Error: malloc\n"); + return NULL; + } + // red rgbbuff[i*3] green rgbbuff[i*3+1] blue rgbbuff[i*3+2] + for(i = 0; i < count ; i++) + i_fbbuff[i] = (transp << 24) | ((rgbbuff[i*3] << 16) & 0xFF0000) | ((rgbbuff[i*3+1] << 8) & 0xFF00) | (rgbbuff[i*3+2] & 0xFF); + //i_fbbuff[i] = (transp << 24) | ((rgbbuff[i*3+2] << 16) & 0xFF0000) | ((rgbbuff[i*3+1] << 8) & 0xFF00) | (rgbbuff[i*3] & 0xFF); + fbbuff = (void *) i_fbbuff; + break; + default: + fprintf(stderr, "Unsupported video mode! You've got: %dbpp\n", bpp); + exit(1); + } + return fbbuff; +} diff --git a/src/driver/pictureviewer/fb_display.h b/src/driver/pictureviewer/fb_display.h new file mode 100644 index 000000000..914f08e4b --- /dev/null +++ b/src/driver/pictureviewer/fb_display.h @@ -0,0 +1,8 @@ +#ifndef __pictureviewer_fbdisplay__ +#define __pictureviewer_fbdisplay__ +void* convertRGB2FB(unsigned char *rgbbuff, unsigned long x, unsigned long y, int bpp, int *cpp, int transp = 0xFF); +extern void fb_display(unsigned char *rgbbuff, int x_size, int y_size, int x_pan, int y_pan, int x_offs, int y_offs, bool clearfb = true, int transp = 0xFF); +extern void getCurrentRes(int *x,int *y); +unsigned char * make15color_errdiff(unsigned char * orgin,int x, int y); +#endif + diff --git a/src/driver/pictureviewer/gif.cpp b/src/driver/pictureviewer/gif.cpp new file mode 100644 index 000000000..9d3d5cc94 --- /dev/null +++ b/src/driver/pictureviewer/gif.cpp @@ -0,0 +1,160 @@ +#include "config.h" + +#include +#include + +#ifdef FBV_SUPPORT_GIF + #include "pictureviewer.h" + #include + #include + #include + #include + #include +extern "C" { +#include +} + #include + #define min(a,b) ((a) < (b) ? (a) : (b)) + #define gflush return(FH_ERROR_FILE); + #define grflush { DGifCloseFile(gft); return(FH_ERROR_FORMAT); } + #define mgrflush { free(lb); free(slb); DGifCloseFile(gft); return(FH_ERROR_FORMAT); } + #define agflush return(FH_ERROR_FORMAT); + #define agrflush { DGifCloseFile(gft); return(FH_ERROR_FORMAT); } + + +int fh_gif_id(const char *name) +{ + int fd; + char id[4]; + fd=open(name,O_RDONLY); if(fd==-1) return(0); + read(fd,id,4); + close(fd); + if(id[0]=='G' && id[1]=='I' && id[2]=='F') return(1); + return(0); +} + +inline void m_rend_gif_decodecolormap(unsigned char *cmb,unsigned char *rgbb,ColorMapObject *cm,int s,int l) +{ + GifColorType *cmentry; + int i; + for(i=0;iColors[cmb[i]]; + *(rgbb++)=cmentry->Red; + *(rgbb++)=cmentry->Green; + *(rgbb++)=cmentry->Blue; + } +} +int fh_gif_load(const char *name,unsigned char **buffer,int* xp,int* yp) +{ + int px,py,i,ibxs; + int j; + unsigned char *fbptr; + unsigned char *lb; + unsigned char *slb; + GifFileType *gft; + GifByteType *extension; + int extcode; + GifRecordType rt; + ColorMapObject *cmap; + int cmaps; + + gft=DGifOpenFileName(name); + if(gft==NULL) gflush; + do + { + if(DGifGetRecordType(gft,&rt) == GIF_ERROR) grflush; + switch(rt) + { + case IMAGE_DESC_RECORD_TYPE: + + if(DGifGetImageDesc(gft)==GIF_ERROR) grflush; + px=gft->Image.Width; + py=gft->Image.Height; + lb=(unsigned char*)malloc(px*3); + slb=(unsigned char*) malloc(px); +// printf("reading...\n"); + if(lb!=NULL && slb!=NULL) + { + cmap=(gft->Image.ColorMap ? gft->Image.ColorMap : gft->SColorMap); + cmaps=cmap->ColorCount; + + ibxs=ibxs*3; + fbptr=*buffer; + if(!(gft->Image.Interlace)) + { + for(i=0;iImage.Width; + py=gft->Image.Height; + *x=px; *y=py; + DGifCloseFile(gft); + return(FH_ERROR_OK); + break; + case EXTENSION_RECORD_TYPE: + if(DGifGetExtension(gft,&extcode,&extension)==GIF_ERROR) grflush; + while(extension!=NULL) + if(DGifGetExtensionNext(gft,&extension)==GIF_ERROR) grflush; + break; + default: + break; + } + } + while( rt!= TERMINATE_RECORD_TYPE ); + DGifCloseFile(gft); + return(FH_ERROR_FORMAT); +} +#endif diff --git a/src/driver/pictureviewer/jpeg.cpp b/src/driver/pictureviewer/jpeg.cpp new file mode 100644 index 000000000..52a1badaf --- /dev/null +++ b/src/driver/pictureviewer/jpeg.cpp @@ -0,0 +1,310 @@ +#include "../../../config.h" +#include "config.h" +#ifdef FBV_SUPPORT_JPEG + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +#include + +#include +#include "pictureviewer.h" +#include "picv_client_server.h" + +#define MIN(a,b) ((a)>(b)?(b):(a)) + +struct r_jpeg_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf envbuffer; +}; + + +int fh_jpeg_id(const char *name) +{ +// dbout("fh_jpeg_id {\n"); + int fd; + unsigned char id[10]; + fd=open(name,O_RDONLY); if(fd==-1) return(0); + read(fd,id,10); + close(fd); +// dbout("fh_jpeg_id }\n"); + if(id[6]=='J' && id[7]=='F' && id[8]=='I' && id[9]=='F') return(1); + if(id[0]==0xff && id[1]==0xd8 && id[2]==0xff) return(1); + return(0); +} + + +void jpeg_cb_error_exit(j_common_ptr cinfo) +{ +// dbout("jpeg_cd_error_exit {\n"); + struct r_jpeg_error_mgr *mptr; + mptr=(struct r_jpeg_error_mgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(mptr->envbuffer,1); +// dbout("jpeg_cd_error_exit }\n"); +} + +#define BUFFERLEN 8192 +int fh_jpeg_load_via_server(const char *filename,unsigned char *buffer,int x,int y) +{ + struct sockaddr_in si_other; + int s, slen=sizeof(si_other); + int bytes, port; + char path[PICV_CLIENT_SERVER_PATHLEN]; + struct pic_data pd; + + strncpy(path, filename, PICV_CLIENT_SERVER_PATHLEN-1); + path[PICV_CLIENT_SERVER_PATHLEN - 1] = 0; + + dbout("fh_jpeg_load_via_server (%s/%d/%d) {\n",basename(filename),x,y); + if ((s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) + { + perror("socket:"); + return(FH_ERROR_FILE); + } + port=atoi(g_settings.picviewer_decode_server_port); + printf("Server %s [%d]\n",g_settings.picviewer_decode_server_ip.c_str(),port); + + memset((char *) &si_other, sizeof(si_other), 0); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(port); + if (inet_aton(g_settings.picviewer_decode_server_ip.c_str(), + &si_other.sin_addr)==0) + { + fprintf(stderr, "inet_aton() failed\n"); + return(FH_ERROR_FILE); + } + if (connect(s, (struct sockaddr *) &si_other, slen)==-1) + { + perror("connect()"); + return(FH_ERROR_FILE); + } + if (send(s, path, PICV_CLIENT_SERVER_PATHLEN, 0)==-1) + { + perror("send()"); + return(FH_ERROR_FILE); + } + pd.width=htonl(x); + pd.height=htonl(y); + if (send(s, &pd, sizeof(pd), 0)==-1) + { + perror("send()"); + return(FH_ERROR_FILE); + } + if (recv(s, &pd, sizeof(pd), 0) < (int)sizeof(pd)) + { + perror("recv pic desc"); + return(FH_ERROR_FILE); + } + if ((int)ntohl(pd.width) != x || (int)ntohl(pd.height) != y) + { + fprintf(stderr,"ugh, decoded pic has wrong size [%d/%d]<>[%d/%d]\n", ntohl(pd.width), ntohl(pd.height), x, y); + return(FH_ERROR_FILE); + } + unsigned char buf2[BUFFERLEN]; + unsigned char* workptr=buffer; + int rest = x*y*2; + while (rest > 0) + { + bytes=recv(s, buf2, MIN(BUFFERLEN,rest), 0); + if(bytes % 2 ==1) + bytes+=recv(s, buf2+bytes, 1, 0); + + for(int i=0 ; i < bytes ; i+=2) + { + *workptr = (buf2[i] >> 2) << 3; + workptr++; + *workptr = (buf2[i] << 6) | ((buf2[i+1] >> 5) << 3); + workptr++; + *workptr = (buf2[i+1] << 3); + workptr++; + } + rest-=bytes; + } + dbout("fh_jpeg_load_via_server }\n"); + return(FH_ERROR_OK); +} +int fh_jpeg_load_local(const char *filename,unsigned char **buffer,int* x,int* y) +{ + //dbout("fh_jpeg_load_local (%s/%d/%d) {\n",basename(filename),*x,*y); + struct jpeg_decompress_struct cinfo; + struct jpeg_decompress_struct *ciptr; + struct r_jpeg_error_mgr emgr; + unsigned char *bp; + int px,py,c; + FILE *fh; + JSAMPLE *lb; + + ciptr=&cinfo; + if(!(fh=fopen(filename,"rb"))) return(FH_ERROR_FILE); + ciptr->err=jpeg_std_error(&emgr.pub); + emgr.pub.error_exit=jpeg_cb_error_exit; + if(setjmp(emgr.envbuffer)==1) + { + // FATAL ERROR - Free the object and return... + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_jpeg_load } - FATAL ERROR\n"); + return(FH_ERROR_FORMAT); + } + + jpeg_create_decompress(ciptr); + jpeg_stdio_src(ciptr,fh); + jpeg_read_header(ciptr,TRUE); + ciptr->out_color_space=JCS_RGB; + ciptr->dct_method=JDCT_FASTEST; + if(*x==(int)ciptr->image_width) + ciptr->scale_denom=1; + else if(abs(*x*2 - ciptr->image_width) < 2) + ciptr->scale_denom=2; + else if(abs(*x*4 - ciptr->image_width) < 4) + ciptr->scale_denom=4; + else if(abs(*x*8 - ciptr->image_width) < 8) + ciptr->scale_denom=8; + else + ciptr->scale_denom=1; + + jpeg_start_decompress(ciptr); + + px=ciptr->output_width; py=ciptr->output_height; + c=ciptr->output_components; + if(px > *x || py > *y) + { + // pic act larger, e.g. because of not responding jpeg server + free(*buffer); + *buffer = (unsigned char*) malloc(px*py*3); + *x = px; + *y = py; + } + + if(c==3) + { + lb=(JSAMPLE*)(*ciptr->mem->alloc_small)((j_common_ptr) ciptr,JPOOL_PERMANENT,c*px); + bp=*buffer; + while(ciptr->output_scanline < ciptr->output_height) + { + jpeg_read_scanlines(ciptr, &lb, 1); + memcpy(bp,lb,px*c); + bp+=px*c; + } + + } + jpeg_finish_decompress(ciptr); + jpeg_destroy_decompress(ciptr); + fclose(fh); + //dbout("fh_jpeg_load_local }\n"); + return(FH_ERROR_OK); +} + +int fh_jpeg_load(const char *filename,unsigned char **buffer,int* x,int* y) +{ + int ret=FH_ERROR_FILE; +#if HAVE_DVB_API_VERSION >= 3 + if(!g_settings.picviewer_decode_server_ip.empty()) + ret=fh_jpeg_load_via_server(filename, *buffer, *x, *y); + if(ret!=FH_ERROR_OK) +#endif + ret=fh_jpeg_load_local(filename, buffer, x, y); + return ret; +} + +int fh_jpeg_getsize(const char *filename,int *x,int *y, int wanted_width, int wanted_height) +{ +// dbout("fh_jpeg_getsize {\n"); + struct jpeg_decompress_struct cinfo; + struct jpeg_decompress_struct *ciptr; + struct r_jpeg_error_mgr emgr; + + int px,py,c; + FILE *fh; + ciptr=&cinfo; + if(!(fh=fopen(filename,"rb"))) return(FH_ERROR_FILE); + + ciptr->err=jpeg_std_error(&emgr.pub); + emgr.pub.error_exit=jpeg_cb_error_exit; + if(setjmp(emgr.envbuffer)==1) + { + // FATAL ERROR - Free the object and return... + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_jpeg_getsize } - FATAL ERROR\n"); + return(FH_ERROR_FORMAT); + } + + jpeg_create_decompress(ciptr); + jpeg_stdio_src(ciptr,fh); + jpeg_read_header(ciptr,TRUE); + ciptr->out_color_space=JCS_RGB; + // should be more flexible... + if((int)ciptr->image_width/8 >= wanted_width || + (int)ciptr->image_height/8 >= wanted_height) + ciptr->scale_denom=8; + else if((int)ciptr->image_width/4 >= wanted_width || + (int)ciptr->image_height/4 >= wanted_height) + ciptr->scale_denom=4; + else if((int)ciptr->image_width/2 >= wanted_width || + (int)ciptr->image_height/2 >= wanted_height) + ciptr->scale_denom=2; + else + ciptr->scale_denom=1; + + jpeg_start_decompress(ciptr); + px=ciptr->output_width; py=ciptr->output_height; + c=ciptr->output_components; +#if HAVE_DVB_API_VERSION >= 3 + if(!g_settings.picviewer_decode_server_ip.empty()) + { + // jpeg server resizes pic to desired size + if( px > wanted_width || py > wanted_height) + { + if( (CPictureViewer::m_aspect_ratio_correction*py*wanted_width/px) <= wanted_height) + { + *x=wanted_width; + *y=(int)(CPictureViewer::m_aspect_ratio_correction*py*wanted_width/px); + } + else + { + *x=(int)((1.0/CPictureViewer::m_aspect_ratio_correction)*px*wanted_height/py); + *y=wanted_height; + } + } + else if(CPictureViewer::m_aspect_ratio_correction >=1) + { + *x=px; + *y=(int)(py/CPictureViewer::m_aspect_ratio_correction); + } + else + { + *x=(int)(px/CPictureViewer::m_aspect_ratio_correction); + *y=py; + } + + } + else +#endif + { + *x=px; + *y=py; + } +// jpeg_finish_decompress(ciptr); + jpeg_destroy_decompress(ciptr); + fclose(fh); +// dbout("fh_jpeg_getsize }\n"); + return(FH_ERROR_OK); +} +#endif diff --git a/src/driver/pictureviewer/make15color_errdiff.cpp b/src/driver/pictureviewer/make15color_errdiff.cpp new file mode 100644 index 000000000..e00ae47ee --- /dev/null +++ b/src/driver/pictureviewer/make15color_errdiff.cpp @@ -0,0 +1,155 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2005 Zwen + + Kommentar: + This is an implementation of the floyd steinberg error diffusion algorithm + adapted for 24bit to 15bit color reduction. + + For a description of the base alorithm see e.g.: + http://www.informatik.fh-muenchen.de/~schieder/graphik-01-02/slide0264.html + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include "pictureviewer.h" + +void c32_15(unsigned char r, unsigned char g , unsigned char b , unsigned char* d) +{ + *d = ((r >> 1) & 0x7C) | (g >> 6); + *(d+1) = ((g << 2) & 0xE0) | (b >> 3); +} + +#define FS_CALC_ERROR_COMMON(color, index) \ + p1 = p2 = (p_src[index] + (this_line_error_##color[ix]>>4)); \ + if(p1>255)p1=255; if(p1<0)p1=0; \ + color = (p1 & 0xF8) | 0x4; \ + error = p2 - color; \ + +#define FS_CALC_ERROR_RIGHT(color, index) \ + FS_CALC_ERROR_COMMON(color,index) \ + this_line_error_##color[ix+1] += (error * 7); \ + next_line_error_##color[ix-1] += (error * 3); \ + next_line_error_##color[ix] += (error * 5); \ + next_line_error_##color[ix+1] += error; + +#define FS_CALC_ERROR_LEFT(color, index) \ + FS_CALC_ERROR_COMMON(color,index) \ + this_line_error_##color[ix-1] += (error * 7); \ + next_line_error_##color[ix+1] += (error * 3); \ + next_line_error_##color[ix] += (error * 5); \ + next_line_error_##color[ix-1] += error; + +unsigned char * make15color_errdiff(unsigned char * src,int width, int height) +{ + int odd_line=1; + int ix,iy, error, p1, p2; + unsigned char r,g,b; + unsigned char *p_src, *p_dst; + unsigned char *dst = (unsigned char*) malloc(width*height*2); + int *this_line_error_r; + int *this_line_error_g; + int *this_line_error_b; + int *next_line_error_r; + int *next_line_error_g; + int *next_line_error_b; + int *save_error; + int *error1_r = (int*) malloc((width+2)*sizeof(int)); + int *error1_g = (int*) malloc((width+2)*sizeof(int)); + int *error1_b = (int*) malloc((width+2)*sizeof(int)); + int *error2_r = (int*) malloc((width+2)*sizeof(int)); + int *error2_g = (int*) malloc((width+2)*sizeof(int)); + int *error2_b = (int*) malloc((width+2)*sizeof(int)); + + dbout("Start error diffusion\n"); + + this_line_error_r = error1_r; + this_line_error_g = error1_g; + this_line_error_b = error1_b; + next_line_error_r = error2_r; + next_line_error_g = error2_g; + next_line_error_b = error2_b; + memset (this_line_error_r, 0 , (width+2) * sizeof(int)); + memset (this_line_error_g, 0 , (width+2) * sizeof(int)); + memset (this_line_error_b, 0 , (width+2) * sizeof(int)); + memset (next_line_error_r, 0 , (width+2) * sizeof(int)); + memset (next_line_error_g, 0 , (width+2) * sizeof(int)); + memset (next_line_error_b, 0 , (width+2) * sizeof(int)); + p_src = src; + p_dst = dst; + + for(iy=0 ; iy < height ; iy++) + { + save_error = this_line_error_r; + this_line_error_r = next_line_error_r; + next_line_error_r = save_error; + save_error = this_line_error_g; + this_line_error_g = next_line_error_g; + next_line_error_g = save_error; + save_error = this_line_error_b; + this_line_error_b = next_line_error_b; + next_line_error_b = save_error; + memset (next_line_error_r, 0 , (width+2) * sizeof(int)); + memset (next_line_error_g, 0 , (width+2) * sizeof(int)); + memset (next_line_error_b, 0 , (width+2) * sizeof(int)); + + if(odd_line) + { + for(ix=1 ; ix <= width ; ix++) + { + FS_CALC_ERROR_RIGHT(r,0); + FS_CALC_ERROR_RIGHT(g,1); + FS_CALC_ERROR_RIGHT(b,2); + c32_15(r,g,b,p_dst); + p_src+=3; + p_dst+=2; + } + odd_line=0; + } + else + { + p_src+=(width-1)*3; + p_dst+=(width-1)*2; + for(ix=width ; ix >= 1 ; ix--) + { + FS_CALC_ERROR_LEFT(r,0); + FS_CALC_ERROR_LEFT(g,1); + FS_CALC_ERROR_LEFT(b,2); + c32_15(r,g,b,p_dst); + p_src-=3; + p_dst-=2; + } + p_src+=width*3; + p_dst+=width*2; + odd_line=1; + } + } + free(error1_r); + free(error1_g); + free(error1_b); + free(error2_r); + free(error2_g); + free(error2_b); + dbout("End error diffusion\n"); + return dst; +} + diff --git a/src/driver/pictureviewer/pictureviewer.cpp b/src/driver/pictureviewer/pictureviewer.cpp new file mode 100644 index 000000000..e7069ba11 --- /dev/null +++ b/src/driver/pictureviewer/pictureviewer.cpp @@ -0,0 +1,540 @@ +#include +#include +#include "pictureviewer.h" +#include "config.h" +#include "fb_display.h" +#include "driver/framebuffer.h" + + +#include +#include +#include + +/* resize.cpp */ +extern unsigned char *simple_resize (unsigned char *orgin, int ox, int oy, int dx, int dy); +extern unsigned char *color_average_resize (unsigned char *orgin, int ox, int oy, int dx, int dy); + + +#ifdef FBV_SUPPORT_GIF +extern int fh_gif_getsize (const char *, int *, int *, int, int); +extern int fh_gif_load (const char *, unsigned char **, int *, int *); +extern int fh_gif_id (const char *); +#endif +#ifdef FBV_SUPPORT_JPEG +extern int fh_jpeg_getsize (const char *, int *, int *, int, int); +extern int fh_jpeg_load (const char *, unsigned char **, int *, int *); +extern int fh_jpeg_id (const char *); +#endif +#ifdef FBV_SUPPORT_PNG +extern int fh_png_getsize (const char *, int *, int *, int, int); +extern int fh_png_load (const char *, unsigned char **, int *, int *); +extern int fh_png_id (const char *); +#endif +#ifdef FBV_SUPPORT_BMP +extern int fh_bmp_getsize (const char *, int *, int *, int, int); +extern int fh_bmp_load (const char *, unsigned char **, int *, int *); +extern int fh_bmp_id (const char *); +#endif +#ifdef FBV_SUPPORT_CRW +extern int fh_crw_getsize (const char *, int *, int *, int, int); +extern int fh_crw_load (const char *, unsigned char **, int *, int *); +extern int fh_crw_id (const char *); +#endif + +double CPictureViewer::m_aspect_ratio_correction; + +void CPictureViewer::add_format (int (*picsize) (const char *, int *, int *, int, int), int (*picread) (const char *, unsigned char **, int *, int *), int (*id) (const char *)) +{ + CFormathandler *fhn; + fhn = (CFormathandler *) malloc (sizeof (CFormathandler)); + fhn->get_size = picsize; + fhn->get_pic = picread; + fhn->id_pic = id; + fhn->next = fh_root; + fh_root = fhn; +} + +void CPictureViewer::init_handlers (void) +{ +#ifdef FBV_SUPPORT_GIF + add_format (fh_gif_getsize, fh_gif_load, fh_gif_id); +#endif +#ifdef FBV_SUPPORT_JPEG + add_format (fh_jpeg_getsize, fh_jpeg_load, fh_jpeg_id); +#endif +#ifdef FBV_SUPPORT_PNG + add_format (fh_png_getsize, fh_png_load, fh_png_id); +#endif +#ifdef FBV_SUPPORT_BMP + add_format (fh_bmp_getsize, fh_bmp_load, fh_bmp_id); +#endif +#ifdef FBV_SUPPORT_CRW + add_format (fh_crw_getsize, fh_crw_load, fh_crw_id); +#endif +} + +CPictureViewer::CFormathandler * CPictureViewer::fh_getsize (const char *name, int *x, int *y, int width_wanted, int height_wanted) +{ + CFormathandler *fh; + for (fh = fh_root; fh != NULL; fh = fh->next) { + if (fh->id_pic (name)) + if (fh->get_size (name, x, y, width_wanted, height_wanted) == FH_ERROR_OK) + return (fh); + } + return (NULL); +} + +bool CPictureViewer::DecodeImage (const std::string & name, bool showBusySign, bool unscaled) +{ +// dbout("DecodeImage {\n"); + if (name == m_NextPic_Name) { +// dbout("DecodeImage }\n"); + return true; + } + + int x, y, xs, ys, imx, imy; + getCurrentRes (&xs, &ys); + + // Show red block for "next ready" in view state + if (showBusySign) + showBusy (m_startx + 3, m_starty + 3, 10, 0xff, 00, 00); + + CFormathandler *fh; + if (unscaled) + fh = fh_getsize (name.c_str (), &x, &y, INT_MAX, INT_MAX); + else + fh = fh_getsize (name.c_str (), &x, &y, m_endx - m_startx, m_endy - m_starty); + if (fh) { + if (m_NextPic_Buffer != NULL) { + free (m_NextPic_Buffer); + } + m_NextPic_Buffer = (unsigned char *) malloc (x * y * 3); + if (m_NextPic_Buffer == NULL) { + printf ("Error: malloc\n"); + return false; + } +// dbout("---Decoding Start(%d/%d)\n",x,y); + if (fh->get_pic (name.c_str (), &m_NextPic_Buffer, &x, &y) == FH_ERROR_OK) { +// dbout("---Decoding Done\n"); + if ((x > (m_endx - m_startx) || y > (m_endy - m_starty)) && m_scaling != NONE && !unscaled) { + if ((m_aspect_ratio_correction * y * (m_endx - m_startx) / x) <= (m_endy - m_starty)) { + imx = (m_endx - m_startx); + imy = (int) (m_aspect_ratio_correction * y * (m_endx - m_startx) / x); + } else { + imx = (int) ((1.0 / m_aspect_ratio_correction) * x * (m_endy - m_starty) / y); + imy = (m_endy - m_starty); + } + if (m_scaling == SIMPLE) + m_NextPic_Buffer = simple_resize (m_NextPic_Buffer, x, y, imx, imy); + else + m_NextPic_Buffer = color_average_resize (m_NextPic_Buffer, x, y, imx, imy); + x = imx; + y = imy; + } + m_NextPic_X = x; + m_NextPic_Y = y; + if (x < (m_endx - m_startx)) + m_NextPic_XPos = (m_endx - m_startx - x) / 2 + m_startx; + else + m_NextPic_XPos = m_startx; + if (y < (m_endy - m_starty)) + m_NextPic_YPos = (m_endy - m_starty - y) / 2 + m_starty; + else + m_NextPic_YPos = m_starty; + if (x > (m_endx - m_startx)) + m_NextPic_XPan = (x - (m_endx - m_startx)) / 2; + else + m_NextPic_XPan = 0; + if (y > (m_endy - m_starty)) + m_NextPic_YPan = (y - (m_endy - m_starty)) / 2; + else + m_NextPic_YPan = 0; + } else { + printf ("Unable to read file !\n"); + free (m_NextPic_Buffer); + m_NextPic_Buffer = (unsigned char *) malloc (3); + if (m_NextPic_Buffer == NULL) { + printf ("Error: malloc\n"); + return false; + } + memset (m_NextPic_Buffer, 0, 3); + m_NextPic_X = 1; + m_NextPic_Y = 1; + m_NextPic_XPos = 0; + m_NextPic_YPos = 0; + m_NextPic_XPan = 0; + m_NextPic_YPan = 0; + } + } else { + printf ("Unable to read file or format not recognized!\n"); + if (m_NextPic_Buffer != NULL) { + free (m_NextPic_Buffer); + } + m_NextPic_Buffer = (unsigned char *) malloc (3); + if (m_NextPic_Buffer == NULL) { + printf ("Error: malloc\n"); + return false; + } + memset (m_NextPic_Buffer, 0, 3); + m_NextPic_X = 1; + m_NextPic_Y = 1; + m_NextPic_XPos = 0; + m_NextPic_YPos = 0; + m_NextPic_XPan = 0; + m_NextPic_YPan = 0; + } + m_NextPic_Name = name; + hideBusy (); +// dbout("DecodeImage }\n"); + return (m_NextPic_Buffer != NULL); +} + +void CPictureViewer::SetVisible (int startx, int endx, int starty, int endy) +{ + m_startx = startx; + m_endx = endx; + m_starty = starty; + m_endy = endy; +} + + +bool CPictureViewer::ShowImage (const std::string & filename, bool unscaled) +{ +// dbout("Show Image {\n"); + // Wird eh ueberschrieben ,also schonmal freigeben... (wenig speicher) + if (m_CurrentPic_Buffer != NULL) { + free (m_CurrentPic_Buffer); + m_CurrentPic_Buffer = NULL; + } + DecodeImage (filename, true, unscaled); + DisplayNextImage (); +// dbout("Show Image }\n"); + return true; +} + +bool CPictureViewer::DisplayNextImage () +{ +// dbout("DisplayNextImage {\n"); + if (m_CurrentPic_Buffer != NULL) { + free (m_CurrentPic_Buffer); + m_CurrentPic_Buffer = NULL; + } + if (m_NextPic_Buffer != NULL) + fb_display (m_NextPic_Buffer, m_NextPic_X, m_NextPic_Y, m_NextPic_XPan, m_NextPic_YPan, m_NextPic_XPos, m_NextPic_YPos); +// dbout("DisplayNextImage fb_disp done\n"); + m_CurrentPic_Buffer = m_NextPic_Buffer; + m_NextPic_Buffer = NULL; + m_CurrentPic_Name = m_NextPic_Name; + m_CurrentPic_X = m_NextPic_X; + m_CurrentPic_Y = m_NextPic_Y; + m_CurrentPic_XPos = m_NextPic_XPos; + m_CurrentPic_YPos = m_NextPic_YPos; + m_CurrentPic_XPan = m_NextPic_XPan; + m_CurrentPic_YPan = m_NextPic_YPan; +// dbout("DisplayNextImage }\n"); + return true; +} + +void CPictureViewer::Zoom (float factor) +{ +// dbout("Zoom %f\n",factor); + showBusy (m_startx + 3, m_starty + 3, 10, 0xff, 0xff, 00); + + int oldx = m_CurrentPic_X; + int oldy = m_CurrentPic_Y; + unsigned char *oldBuf = m_CurrentPic_Buffer; + m_CurrentPic_X = (int) (factor * m_CurrentPic_X); + m_CurrentPic_Y = (int) (factor * m_CurrentPic_Y); + + if (m_scaling == COLOR) + m_CurrentPic_Buffer = color_average_resize (m_CurrentPic_Buffer, oldx, oldy, m_CurrentPic_X, m_CurrentPic_Y); + else + m_CurrentPic_Buffer = simple_resize (m_CurrentPic_Buffer, oldx, oldy, m_CurrentPic_X, m_CurrentPic_Y); + + if (m_CurrentPic_Buffer == oldBuf) { + // resize failed + hideBusy (); + return; + } + + if (m_CurrentPic_X < (m_endx - m_startx)) + m_CurrentPic_XPos = (m_endx - m_startx - m_CurrentPic_X) / 2 + m_startx; + else + m_CurrentPic_XPos = m_startx; + if (m_CurrentPic_Y < (m_endy - m_starty)) + m_CurrentPic_YPos = (m_endy - m_starty - m_CurrentPic_Y) / 2 + m_starty; + else + m_CurrentPic_YPos = m_starty; + if (m_CurrentPic_X > (m_endx - m_startx)) + m_CurrentPic_XPan = (m_CurrentPic_X - (m_endx - m_startx)) / 2; + else + m_CurrentPic_XPan = 0; + if (m_CurrentPic_Y > (m_endy - m_starty)) + m_CurrentPic_YPan = (m_CurrentPic_Y - (m_endy - m_starty)) / 2; + else + m_CurrentPic_YPan = 0; + fb_display (m_CurrentPic_Buffer, m_CurrentPic_X, m_CurrentPic_Y, m_CurrentPic_XPan, m_CurrentPic_YPan, m_CurrentPic_XPos, m_CurrentPic_YPos); +} + +void CPictureViewer::Move (int dx, int dy) +{ +// dbout("Move %d %d\n",dx,dy); + showBusy (m_startx + 3, m_starty + 3, 10, 0x00, 0xff, 00); + + int xs, ys; + getCurrentRes (&xs, &ys); + m_CurrentPic_XPan += dx; + if (m_CurrentPic_XPan + xs >= m_CurrentPic_X) + m_CurrentPic_XPan = m_CurrentPic_X - xs - 1; + if (m_CurrentPic_XPan < 0) + m_CurrentPic_XPan = 0; + + m_CurrentPic_YPan += dy; + if (m_CurrentPic_YPan + ys >= m_CurrentPic_Y) + m_CurrentPic_YPan = m_CurrentPic_Y - ys - 1; + if (m_CurrentPic_YPan < 0) + m_CurrentPic_YPan = 0; + + if (m_CurrentPic_X < (m_endx - m_startx)) + m_CurrentPic_XPos = (m_endx - m_startx - m_CurrentPic_X) / 2 + m_startx; + else + m_CurrentPic_XPos = m_startx; + if (m_CurrentPic_Y < (m_endy - m_starty)) + m_CurrentPic_YPos = (m_endy - m_starty - m_CurrentPic_Y) / 2 + m_starty; + else + m_CurrentPic_YPos = m_starty; +// dbout("Display x(%d) y(%d) xpan(%d) ypan(%d) xpos(%d) ypos(%d)\n",m_CurrentPic_X, m_CurrentPic_Y, +// m_CurrentPic_XPan, m_CurrentPic_YPan, m_CurrentPic_XPos, m_CurrentPic_YPos); + + fb_display (m_CurrentPic_Buffer, m_CurrentPic_X, m_CurrentPic_Y, m_CurrentPic_XPan, m_CurrentPic_YPan, m_CurrentPic_XPos, m_CurrentPic_YPos); +} + +CPictureViewer::CPictureViewer () +{ + fh_root = NULL; + m_scaling = COLOR; + //m_aspect = 4.0 / 3; + m_aspect = 16.0 / 9; + m_CurrentPic_Name = ""; + m_CurrentPic_Buffer = NULL; + m_CurrentPic_X = 0; + m_CurrentPic_Y = 0; + m_CurrentPic_XPos = 0; + m_CurrentPic_YPos = 0; + m_CurrentPic_XPan = 0; + m_CurrentPic_YPan = 0; + m_NextPic_Name = ""; + m_NextPic_Buffer = NULL; + m_NextPic_X = 0; + m_NextPic_Y = 0; + m_NextPic_XPos = 0; + m_NextPic_YPos = 0; + m_NextPic_XPan = 0; + m_NextPic_YPan = 0; + int xs, ys; + getCurrentRes (&xs, &ys); + m_startx = 0; + m_endx = xs - 1; + m_starty = 0; + m_endy = ys - 1; + m_aspect_ratio_correction = m_aspect / ((double) xs / ys); + + m_busy_buffer = NULL; + + init_handlers (); +} + +void CPictureViewer::showBusy (int sx, int sy, int width, char r, char g, char b) +{ +// dbout("Show Busy{\n"); + unsigned char rgb_buffer[3]; + unsigned char *fb_buffer; + unsigned char *busy_buffer_wrk; + int cpp; + struct fb_var_screeninfo *var; + var = CFrameBuffer::getInstance ()->getScreenInfo (); + + rgb_buffer[0] = r; + rgb_buffer[1] = g; + rgb_buffer[2] = b; + + fb_buffer = (unsigned char *) convertRGB2FB (rgb_buffer, 1, 1, var->bits_per_pixel, &cpp); + if (fb_buffer == NULL) { + printf ("Error: malloc\n"); + return; + } + if (m_busy_buffer != NULL) { + free (m_busy_buffer); + m_busy_buffer = NULL; + } + m_busy_buffer = (unsigned char *) malloc (width * width * cpp); + if (m_busy_buffer == NULL) { + printf ("Error: malloc\n"); + return; + } + busy_buffer_wrk = m_busy_buffer; + unsigned char *fb = (unsigned char *) CFrameBuffer::getInstance ()->getFrameBufferPointer (); + unsigned int stride = CFrameBuffer::getInstance ()->getStride (); + + for (int y = sy; y < sy + width; y++) { + for (int x = sx; x < sx + width; x++) { + memcpy (busy_buffer_wrk, fb + y * stride + x * cpp, cpp); + busy_buffer_wrk += cpp; + memcpy (fb + y * stride + x * cpp, fb_buffer, cpp); + } + } + m_busy_x = sx; + m_busy_y = sy; + m_busy_width = width; + m_busy_cpp = cpp; + free (fb_buffer); +// dbout("Show Busy}\n"); +} + +void CPictureViewer::hideBusy () +{ +// dbout("Hide Busy{\n"); + if (m_busy_buffer != NULL) { + unsigned char *fb = (unsigned char *) CFrameBuffer::getInstance ()->getFrameBufferPointer (); + unsigned int stride = CFrameBuffer::getInstance ()->getStride (); + unsigned char *busy_buffer_wrk = m_busy_buffer; + + for (int y = m_busy_y; y < m_busy_y + m_busy_width; y++) { + for (int x = m_busy_x; x < m_busy_x + m_busy_width; x++) { + memcpy (fb + y * stride + x * m_busy_cpp, busy_buffer_wrk, m_busy_cpp); + busy_buffer_wrk += m_busy_cpp; + } + } + free (m_busy_buffer); + m_busy_buffer = NULL; + } +// dbout("Hide Busy}\n"); +} +void CPictureViewer::Cleanup () +{ + if (m_busy_buffer != NULL) { + free (m_busy_buffer); + m_busy_buffer = NULL; + } + if (m_NextPic_Buffer != NULL) { + free (m_NextPic_Buffer); + m_NextPic_Buffer = NULL; + } + if (m_CurrentPic_Buffer != NULL) { + free (m_CurrentPic_Buffer); + m_CurrentPic_Buffer = NULL; + } +} +#define LOGO_DIR1 "/share/tuxbox/neutrino/icons/logo" +#define LOGO_DIR2 "/var/share/icons/logo" +#define LOGO_FMT ".jpg" + +bool CPictureViewer::DisplayLogo (uint64_t channel_id, int posx, int posy, int width, int height) +{ + char fname[255]; + bool ret = false; + sprintf(fname, "%s/%llx.jpg", LOGO_DIR2, channel_id & 0xFFFFFFFFFFFFULL); +printf("logo file: %s\n", fname); + if(access(fname, F_OK)) + sprintf(fname, "%s/%llx.gif", LOGO_DIR2, channel_id & 0xFFFFFFFFFFFFULL); + if(!access(fname, F_OK)) { + ret = DisplayImage(fname, posx, posy, width, height); + } + return ret; +} + +bool CPictureViewer::DisplayImage (const std::string & name, int posx, int posy, int width, int height) +{ + int x, y; + CFormathandler *fh; + bool ret = false; + + if (m_NextPic_Name != name || m_NextPic_X != width || m_NextPic_Y != height) { + + //getCurrentRes (&xs, &ys); + + fh = fh_getsize (name.c_str (), &x, &y, INT_MAX, INT_MAX); + if (fh) { + if (m_NextPic_Buffer != NULL) + free (m_NextPic_Buffer); + + m_NextPic_Buffer = (unsigned char *) malloc (x * y * 3); + if (m_NextPic_Buffer == NULL) { + printf ("Error: malloc\n"); + return false; + } + if (fh->get_pic (name.c_str (), &m_NextPic_Buffer, &x, &y) == FH_ERROR_OK) { +//printf("DisplayImage: decoded %s, %d x %d to x=%d y=%d\n", name.c_str (), x, y, posx, posy); + //FIXME m_aspect_ratio_correction ? + if(width && height) { + if(x != width || y != height) { + //m_NextPic_Buffer = simple_resize (m_NextPic_Buffer, x, y, imx, imy); + m_NextPic_Buffer = color_average_resize (m_NextPic_Buffer, x, y, width, height); + x = width; + y = height; + } + posx += (width-x)/2; + posy += (height-y)/2; + } +//printf("DisplayImage: display %s, %d x %d to x=%d y=%d\n", name.c_str (), x, y, posx, posy); + fb_display (m_NextPic_Buffer, x, y, 0, 0, posx, posy, false, convertSetupAlpha2Alpha(g_settings.infobar_alpha)); + m_NextPic_X = x; + m_NextPic_Y = y; + m_NextPic_Name = name; + ret = true; + } else { + printf ("Error decoding file %s\n", name.c_str ()); + free (m_NextPic_Buffer); + m_NextPic_Buffer = NULL; + m_NextPic_Name = ""; + } + } else { + printf("Error open file %s\n", name.c_str ()); + } + } else if(m_NextPic_Buffer) { + m_NextPic_XPos = posx; + m_NextPic_YPos = posy; + fb_display (m_NextPic_Buffer, m_NextPic_X, m_NextPic_Y, 0, 0, m_NextPic_XPos, m_NextPic_YPos, false, convertSetupAlpha2Alpha(g_settings.infobar_alpha)); + ret = true; + } + return ret; +} + +fb_pixel_t * CPictureViewer::getImage (const std::string & name, int width, int height) +{ + int x, y; + CFormathandler *fh; + unsigned char * buffer; + fb_pixel_t * ret = NULL; + + fh = fh_getsize (name.c_str (), &x, &y, INT_MAX, INT_MAX); + if (fh) { + + buffer = (unsigned char *) malloc (x * y * 3); + if (buffer == NULL) { + printf ("Error: malloc\n"); + return false; + } + if (fh->get_pic (name.c_str (), &buffer, &x, &y) == FH_ERROR_OK) { +printf("getImage: decoded %s, %d x %d \n", name.c_str (), x, y); + if(x != width || y != height) + { + buffer = color_average_resize (buffer, x, y, width, height); + x = width; + y = height; + } + struct fb_var_screeninfo *var = CFrameBuffer::getInstance()->getScreenInfo(); + int bp; + ret = (fb_pixel_t *) convertRGB2FB(buffer, x, y, var->bits_per_pixel, &bp, 0); + free(buffer); + //fb_display (buffer, x, y, 0, 0, posx, posy, false, convertSetupAlpha2Alpha(g_settings.infobar_alpha)); + } else { + printf ("Error decoding file %s\n", name.c_str ()); + free (buffer); + buffer = NULL; + } + } else + printf("Error open file %s\n", name.c_str ()); + + return ret; +} diff --git a/src/driver/pictureviewer/pictureviewer.h b/src/driver/pictureviewer/pictureviewer.h new file mode 100644 index 000000000..03c1c0673 --- /dev/null +++ b/src/driver/pictureviewer/pictureviewer.h @@ -0,0 +1,114 @@ +#ifndef __pictureviewer__ +#define __pictureviewer__ + +/* + pictureviewer - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include /* printf */ +#include /* gettimeofday */ +#include "driver/framebuffer.h" +class CPictureViewer +{ + struct cformathandler + { + struct cformathandler *next; + int (*get_size)(const char *,int *,int*, int, int); + int (*get_pic)(const char *,unsigned char **,int* ,int*); + int (*id_pic)(const char *); + }; + typedef struct cformathandler CFormathandler; + + public: + enum ScalingMode + { + NONE=0, + SIMPLE=1, + COLOR=2 + }; + CPictureViewer(); + ~CPictureViewer(){Cleanup();}; + bool ShowImage(const std::string & filename, bool unscaled=false); + bool DecodeImage(const std::string & name, bool showBusySign=false, bool unscaled=false); + bool DisplayNextImage(); + void SetScaling(ScalingMode s){m_scaling=s;} + void SetAspectRatio(float aspect_ratio) {m_aspect=aspect_ratio;} + void showBusy(int sx, int sy, int width, char r, char g, char b); + void hideBusy(); + void Zoom(float factor); + void Move(int dx, int dy); + void Cleanup(); + void SetVisible(int startx, int endx, int starty, int endy); + static double m_aspect_ratio_correction; + bool DisplayImage (const std::string & name, int posx, int posy, int width, int height); + bool DisplayLogo (uint64_t channel_id, int posx, int posy, int width, int height); + fb_pixel_t * getImage (const std::string & name, int width, int height); + + private: + CFormathandler *fh_root; + ScalingMode m_scaling; + float m_aspect; + std::string m_NextPic_Name; + unsigned char* m_NextPic_Buffer; + int m_NextPic_X; + int m_NextPic_Y; + int m_NextPic_XPos; + int m_NextPic_YPos; + int m_NextPic_XPan; + int m_NextPic_YPan; + std::string m_CurrentPic_Name; + unsigned char* m_CurrentPic_Buffer; + int m_CurrentPic_X; + int m_CurrentPic_Y; + int m_CurrentPic_XPos; + int m_CurrentPic_YPos; + int m_CurrentPic_XPan; + int m_CurrentPic_YPan; + + unsigned char* m_busy_buffer; + int m_busy_x; + int m_busy_y; + int m_busy_width; + int m_busy_cpp; + + int m_startx; + int m_starty; + int m_endx; + int m_endy; + + CFormathandler * fh_getsize(const char *name,int *x,int *y, int width_wanted, int height_wanted); + void init_handlers(void); + void add_format(int (*picsize)(const char *,int *,int*,int,int),int (*picread)(const char *,unsigned char **,int*,int*), int (*id)(const char*)); +}; + + +#define FH_ERROR_OK 0 +#define FH_ERROR_FILE 1 /* read/access error */ +#define FH_ERROR_FORMAT 2 /* file format error */ +#define FH_ERROR_MALLOC 3 /* error during malloc */ + +#define dbout(fmt, args...) {struct timeval tv; gettimeofday(&tv,NULL); \ + printf( "PV[%ld|%02ld] " fmt, (long)tv.tv_sec, (long)tv.tv_usec/10000, ## args);} +#endif diff --git a/src/driver/pictureviewer/picv_client_server.h b/src/driver/pictureviewer/picv_client_server.h new file mode 100644 index 000000000..c23cfee42 --- /dev/null +++ b/src/driver/pictureviewer/picv_client_server.h @@ -0,0 +1,37 @@ +/* + + structs/defines for client server communication + between neutrino picviewer and jpeg decode server + + (c) Zwen@tuxbox.org + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef picv_client_server__ +#define picv_client_server__ + +#define PICV_CLIENT_SERVER_PATHLEN 1000 + +struct pic_data +{ + uint32_t width; + uint32_t height; + uint32_t bpp; /* unused atm */ +}; + +#endif diff --git a/src/driver/pictureviewer/png.cpp b/src/driver/pictureviewer/png.cpp new file mode 100644 index 000000000..539b0b7b7 --- /dev/null +++ b/src/driver/pictureviewer/png.cpp @@ -0,0 +1,158 @@ +#include "config.h" + +#ifdef FBV_SUPPORT_PNG + #include + #include + #include +#include +#include + + #include "pictureviewer.h" + + #define PNG_BYTES_TO_CHECK 4 + #define min(x,y) ((x) < (y) ? (x) : (y)) + +int fh_png_id(const char *name) +{ + int fd; + char id[4]; + fd=open(name,O_RDONLY); if(fd==-1) return(0); + read(fd,id,4); + close(fd); + if(id[1]=='P' && id[2]=='N' && id[3]=='G') return(1); + return(0); +} + + +int fh_png_load(const char *name,unsigned char **buffer,int* xp,int* yp) +{ + static const png_color_16 my_background = {0, 0, 0, 0, 0}; + + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + unsigned int i; + int bit_depth, color_type, interlace_type; + int number_passes,pass; + png_byte * fbptr; + FILE * fh; + + if(!(fh=fopen(name,"rb"))) return(FH_ERROR_FILE); + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); + if(png_ptr == NULL) { + fclose(fh); + return(FH_ERROR_FORMAT); + } + info_ptr = png_create_info_struct(png_ptr); + if(info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fh); + return(FH_ERROR_FORMAT); + } + + if(setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fh); + return(FH_ERROR_FORMAT); + } + + png_init_io(png_ptr,fh); + + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,&interlace_type, NULL, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_palette_to_rgb(png_ptr); + png_set_background(png_ptr, (png_color_16*)&my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + /* other possibility for png_set_background: use png_get_bKGD */ + } + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(png_ptr); + png_set_background(png_ptr, (png_color_16*)&my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + + if (bit_depth < 8) + png_set_packing(png_ptr); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + /* on Intel PC ?: + if (bit_depth == 16) + png_set_swap(png_ptr); + */ + + number_passes = png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr,info_ptr); + + if (width * 3 != png_get_rowbytes(png_ptr, info_ptr)) + { + printf("[png.cpp]: Error processing %s - please report (including image).\n", name); + fclose(fh); + return(FH_ERROR_FORMAT); + } + + for(pass = 0; pass < number_passes; pass++) + { + fbptr = (png_byte *)(*buffer); + for (i = 0; i < height; i++, fbptr += width * 3) + { + png_read_row(png_ptr, fbptr, NULL); + } + } + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fh); + return(FH_ERROR_OK); +} + +int fh_png_getsize(const char *name,int *x,int *y, int wanted_width, int wanted_height) +{ + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + FILE *fh; + + if(!(fh=fopen(name,"rb"))) return(FH_ERROR_FILE); + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); + if(png_ptr == NULL) { + fclose(fh); + return(FH_ERROR_FORMAT); + } + info_ptr = png_create_info_struct(png_ptr); + if(info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fh); + return(FH_ERROR_FORMAT); + } + + if(setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fh); + return(FH_ERROR_FORMAT); + } + + png_init_io(png_ptr,fh); + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,&interlace_type, NULL, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + *x=width; + *y=height; + fclose(fh); + return(FH_ERROR_OK); +} +#endif diff --git a/src/driver/pictureviewer/resize.cpp b/src/driver/pictureviewer/resize.cpp new file mode 100644 index 000000000..662385564 --- /dev/null +++ b/src/driver/pictureviewer/resize.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "pictureviewer.h" + +unsigned char * simple_resize(unsigned char * orgin,int ox,int oy,int dx,int dy) +{ +// dbout("simple_resize{\n"); + unsigned char *cr,*p,*l; + int i,j,k,ip; + cr=(unsigned char*) malloc(dx*dy*3); + if(cr==NULL) + { + printf("Error: malloc\n"); +// dbout("simple_resize}\n"); + return(orgin); + } + l=cr; + + for(j=0;j=ox) + xb_v[i]=ox-1; + } + for(j=0;j=oy) yb=oy-1; + for(i=0;i +#include +#include +#include +#include +#include +#include + +#include "pig.h" + + + + +// Video4Linux API for PIG +// + +// +// -- Picture in Graphics Control +// -- This module is a Class to provide a PIG abstraction layer +// -- +// -- 2002-11 rasc +// -- 2003-06 rasc V4L Api +// + + + +// +// -- Constructor +// + +CPIG::CPIG() +{ + fd = -1; + status = CLOSED; +} + + +CPIG::CPIG(int pig_nr) +{ + fd = -1; + status = CLOSED; + fd = pigopen (pig_nr); +} + + +CPIG::CPIG(int pig_nr, int x, int y, int w, int h) +{ + fd = -1; + status = CLOSED; + fd = pigopen (pig_nr); + set_coord (x,y, w,h); + +} + +CPIG::~CPIG() +{ + pigclose (); // cleanup anyway!! + +} + + +// +// -- open PIG #nr +// -- return >=0: ok +// + +int CPIG::pigopen (int pig_nr) +{ + + char *pigdevs[] = { + PIG_DEV "0" // PIG device 0 + // PIG_DEV "1", // PIG device 1 + // PIG_DEV "2", // PIG device ... + // PIG_DEV "3" + }; + + + if ( (pig_nr>0) || (pig_nr < (int)(sizeof (pigdevs)/sizeof(char *))) ) { + + if (fd < 0) { + fd = open( pigdevs[pig_nr], O_RDWR ); + if (fd >= 0) { + status = HIDE; + px = py = pw = ph = 0; + } + return fd; + } + + } + + return -1; +} + + +// +// -- close PIG +// + +void CPIG::pigclose () +{ + if (fd >=0 ) { + close (fd); + fd = -1; + status = CLOSED; + px = py = pw = ph = 0; + } + return; +} + + +// +// -- set PIG Position +// -- routines should be self explaining +// + +void CPIG::_set_window (int x, int y, int w, int h) +{ +#if HAVE_DVB_API_VERSION >= 3 + // -- Modul interne Routine + struct v4l2_crop crop; + struct v4l2_format coord; + int err; + + crop.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + err = ioctl(fd, VIDIOC_G_CROP, &crop); + + // take whole input frame + crop.c.left = 0; + crop.c.top = 0; + crop.c.width = 720; + crop.c.height = 576; + err = ioctl(fd, VIDIOC_S_CROP, &crop); + + coord.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + err = ioctl(fd, VIDIOC_G_FMT, &coord); + + // fit into small window + coord.fmt.win.w.left = x; + coord.fmt.win.w.top = y; + coord.fmt.win.w.width = w; + coord.fmt.win.w.height = h; + + err = ioctl(fd, VIDIOC_S_FMT, &coord); +#if 0 // old + struct v4l2_format coord; + int err; + + ioctl(fd, VIDIOC_G_FMT, &coord); + coord.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + + coord.fmt.win.w.left = x; + coord.fmt.win.w.top = y; + coord.fmt.win.w.width = w; + coord.fmt.win.w.height = h; + + int pigmode = 0; + err = ioctl(fd, VIDIOC_OVERLAY, &pigmode); +printf("pig::window VIDIOC_OVERLAY %d\n", err); + + err = ioctl(fd, VIDIOC_S_FMT, &coord); +printf("pig::window VIDIOC_S_FMT %d\n", err); +#endif +#else + avia_pig_set_pos(fd,x,y); + avia_pig_set_size(fd, w, h); + avia_pig_set_stack(fd,2); +#endif +} + + +void CPIG::set_coord (int x, int y, int w, int h) +{ + + if (( x != px ) || ( y != py )) { + px = x; + py = y; + pw = w; + ph = h; + _set_window (px,py,pw,ph); + } + +} + + +void CPIG::set_xy (int x, int y) +{ + + if (( x != px ) || ( y != py )) { + px = x; + py = y; + _set_window (px,py,pw,ph); + } + +} + + +void CPIG::set_size (int w, int h) +{ + + if (( w != pw ) || ( h != ph )) { + pw = w; + ph = h; + _set_window (px,py,pw,ph); + } + +} + + +// $$$ ???? what this for? + +//void CPIG::set_source (int x, int y) +//{ +// +// if (fd >= 0) { +// +// if (( x != px ) || ( y != py )) { +// // avia_pig_set_source(fd,x,y); +// } +// +// } +// +//} + + +// +// -- routine set's stack position of PIG on display +// + +//void CPIG::set_stackpos (int pos) +// +//{ +// if (fd >= 0) { +// avia_pig_set_stack(fd,pos); +// stackpos = pos; +// } +//} + + +// +// -- Show PIG or hide PIG +// + +void CPIG::show (int x, int y, int w, int h) +{ + set_coord (x,y, w,h); + show (); +} + +void CPIG::show (void) +{ + if ( fd >= 0 ) { +#if HAVE_DVB_API_VERSION >= 3 + int pigmode = 1; + int err; + err = ioctl(fd, VIDIOC_OVERLAY, &pigmode); +//printf("pig::show err %d\n", err); +#else + avia_pig_show(fd); +#endif + status = SHOW; + } +} + +void CPIG::hide (void) +{ + if ( fd >= 0 ) { +#if HAVE_DVB_API_VERSION >= 3 + int pigmode = 0; + int err; + err = ioctl(fd, VIDIOC_OVERLAY, &pigmode); +#else + avia_pig_hide(fd); +#endif + status = HIDE; + } +} + + +CPIG::PigStatus CPIG::getStatus(void) +{ + return status; + +} + + + + + +// +// ToDo's: +// -- Capability unavail check/status +// -- Cropping for resizing (possible zoom/scaling?) +// -- Capturing +// + diff --git a/src/driver/pig.h b/src/driver/pig.h new file mode 100644 index 000000000..48f044420 --- /dev/null +++ b/src/driver/pig.h @@ -0,0 +1,83 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __PIG_CONTROL__ +#define __PIG_CONTROL__ +#include +using namespace std; + + +// +// -- Picture in Graphics Control +// -- 2002-11 rasc +// -- 2003-06 rasc V4L API +// + + + +#if HAVE_DVB_API_VERSION >= 3 +#include + +#define PIG_DEV "/dev/v4l/video" // PigNr will be appended! +#else +#include +#define PIG_DEV "/dev/dbox/pig" +#endif + + +class CPIG +{ + public: + CPIG (); + CPIG (int pig_nr); // incl. open + CPIG (int pig_nr, int x, int y, int w, int h); // open + set_coord + ~CPIG (); + + int pigopen (int pig_nr); + void pigclose (void); + void set_coord (int x, int y, int w, int h); + void set_xy (int x, int y); + void set_size (int w, int h); +// void set_source(int x, int y); +// void set_stackpos (int pos); + void show (void); + void show (int x, int y, int w, int h); + void hide (void); + + enum PigStatus { CLOSED, HIDE, SHOW }; + PigStatus getStatus(void); + + private: + void _set_window (int x, int y, int w, int h); + + int fd; // io descriptor + int px, py, pw, ph; // pig frame + int stackpos; // Order (Framebuffer, PIGs) + PigStatus status; // on display? + + +}; + + +#endif + + diff --git a/src/driver/rcinput.cpp b/src/driver/rcinput.cpp new file mode 100644 index 000000000..b05893e3e --- /dev/null +++ b/src/driver/rcinput.cpp @@ -0,0 +1,1487 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + 2003 thegoodguy + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +//#define RCDEBUG +#include +#include +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL +#include +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ +#include +#include + +#include +#include +#include + +#include + +#include +#include + +//const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/dev/input/nevis_ir", "/dev/input/event0"}; +const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/dev/input/nevis_ir"}; +typedef struct input_event t_input_event; + +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL +static struct termio orig_termio; +static bool saved_orig_termio = false; +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + +/************************************************************************** +* Constructor - opens rc-input device and starts threads +* +**************************************************************************/ +CRCInput::CRCInput() +{ + timerid= 1; + + // pipe for internal event-queue + // ----------------------------- + if (pipe(fd_pipe_high_priority) < 0) + { + perror("fd_pipe_high_priority"); + exit(-1); + } + + fcntl(fd_pipe_high_priority[0], F_SETFL, O_NONBLOCK ); + fcntl(fd_pipe_high_priority[1], F_SETFL, O_NONBLOCK ); + + if (pipe(fd_pipe_low_priority) < 0) + { + perror("fd_pipe_low_priority"); + exit(-1); + } + + fcntl(fd_pipe_low_priority[0], F_SETFL, O_NONBLOCK ); + fcntl(fd_pipe_low_priority[1], F_SETFL, O_NONBLOCK ); + + + // open event-library + // ----------------------------- + fd_event = 0; + + //network-setup + struct sockaddr_un servaddr; + int clilen; + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + strcpy(servaddr.sun_path, NEUTRINO_UDS_NAME); + clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); + unlink(NEUTRINO_UDS_NAME); + + //network-setup + if ((fd_event = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror("[neutrino] socket\n"); + } + + if ( bind(fd_event, (struct sockaddr*) &servaddr, clilen) <0 ) + { + perror("[neutrino] bind failed...\n"); + exit(-1); + } + + + if (listen(fd_event, 15) !=0) + { + perror("[neutrino] listen failed...\n"); + exit( -1 ); + } + + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) + { + fd_rc[i] = -1; + } + clickfd = -1; + repeat_block = repeat_block_generic = 0; + open(); + rc_last_key = KEY_MAX; +} + +void CRCInput::open() +{ + close(); + + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) + { + if ((fd_rc[i] = ::open(RC_EVENT_DEVICE[i], O_RDONLY)) == -1) + perror(RC_EVENT_DEVICE[i]); + else + { + fcntl(fd_rc[i], F_SETFL, O_NONBLOCK); + } +printf("CRCInput::open: %s fd %d\n", RC_EVENT_DEVICE[i], fd_rc[i]); + } + + //+++++++++++++++++++++++++++++++++++++++ +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL + fd_keyb = STDIN_FILENO; +#else + fd_keyb = 0; +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + /* + ::open("/dev/dbox/rc0", O_RDONLY); + if (fd_keyb<0) + { + perror("/dev/stdin"); + exit(-1); + } + */ +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL + ::fcntl(fd_keyb, F_SETFL, O_NONBLOCK); + + struct termio new_termio; + + ::ioctl(STDIN_FILENO, TCGETA, &orig_termio); + + saved_orig_termio = true; + + new_termio = orig_termio; + new_termio.c_lflag &= ~ICANON; + // new_termio.c_lflag &= ~(ICANON|ECHO); + new_termio.c_cc[VMIN ] = 1; + new_termio.c_cc[VTIME] = 0; + + ::ioctl(STDIN_FILENO, TCSETA, &new_termio); + +#else + //fcntl(fd_keyb, F_SETFL, O_NONBLOCK ); + + //+++++++++++++++++++++++++++++++++++++++ +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + + open_click(); + calculateMaxFd(); +} + +void CRCInput::close() +{ + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) { + if (fd_rc[i] != -1) { + ::close(fd_rc[i]); + fd_rc[i] = -1; + } + } +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL + if (saved_orig_termio) + { + ::ioctl(STDIN_FILENO, TCSETA, &orig_termio); + printf("Original terminal settings restored.\n"); + } +#else +/* + if(fd_keyb) + { + ::close(fd_keyb); + } +*/ +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + calculateMaxFd(); +} + +void CRCInput::calculateMaxFd() +{ + fd_max = fd_event; + + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) + if (fd_rc[i] > fd_max) + fd_max = fd_rc[i]; + + if(fd_pipe_high_priority[0] > fd_max) + fd_max = fd_pipe_high_priority[0]; + if(fd_pipe_low_priority[0] > fd_max) + fd_max = fd_pipe_low_priority[0]; +} + +/************************************************************************** +* Destructor - close the input-device +* +**************************************************************************/ +CRCInput::~CRCInput() +{ + close(); + + if(fd_pipe_high_priority[0]) + ::close(fd_pipe_high_priority[0]); + if(fd_pipe_high_priority[1]) + ::close(fd_pipe_high_priority[1]); + + if(fd_pipe_low_priority[0]) + ::close(fd_pipe_low_priority[0]); + if(fd_pipe_low_priority[1]) + ::close(fd_pipe_low_priority[1]); + + if(fd_event) + ::close(fd_event); + close_click(); +} + +/************************************************************************** +* stopInput - stop reading rcin for plugins +* +**************************************************************************/ +void CRCInput::stopInput() +{ + close(); +} + +/************************************************************************** +* restartInput - restart reading rcin after calling plugins +* +**************************************************************************/ +void CRCInput::restartInput() +{ + close(); + open(); +} + +int CRCInput::messageLoop( bool anyKeyCancels, int timeout ) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + bool doLoop = true; + + if ( timeout == -1 ) + timeout = g_settings.timing[SNeutrinoSettings::TIMING_MENU]; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd( timeout == 0 ? 0xFFFF : timeout); + + while (doLoop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ( ( msg == CRCInput::RC_timeout ) || + ( msg == CRCInput::RC_home ) || + ( msg == CRCInput::RC_ok ) ) + doLoop = false; + else if((msg == CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) { + } + else + { + int mr = CNeutrinoApp::getInstance()->handleMsg( msg, data ); + + if ( mr & messages_return::cancel_all ) + { + res = menu_return::RETURN_EXIT_ALL; + doLoop = false; + } + else if ( mr & messages_return::unhandled ) + { + if ((msg <= CRCInput::RC_MaxRC) && + (data == 0)) /* <- button pressed */ + { + if ( anyKeyCancels ) + doLoop = false; + else + timeoutEnd = CRCInput::calcTimeoutEnd( timeout ); + } + } + } + + + } + return res; +} + + +int CRCInput::addTimer(unsigned long long Interval, bool oneshot, bool correct_time ) +{ + struct timeval tv; + + gettimeofday( &tv, NULL ); + unsigned long long timeNow = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + + timer _newtimer; + if (!oneshot) + _newtimer.interval = Interval; + else + _newtimer.interval = 0; + + _newtimer.id = timerid++; + if ( correct_time ) + _newtimer.times_out = timeNow+ Interval; + else + _newtimer.times_out = Interval; + + _newtimer.correct_time = correct_time; + +//printf("adding timer %d (0x%llx, 0x%llx)\n", _newtimer.id, _newtimer.times_out, Interval); + + std::vector::iterator e; + for ( e= timers.begin(); e!= timers.end(); ++e ) + if ( e->times_out> _newtimer.times_out ) + break; + + timers.insert(e, _newtimer); + return _newtimer.id; +} + +int CRCInput::addTimer(struct timeval Timeout) +{ + unsigned long long timesout = (unsigned long long) Timeout.tv_usec + (unsigned long long)((unsigned long long) Timeout.tv_sec * (unsigned long long) 1000000); + return addTimer( timesout, true, false ); +} + +int CRCInput::addTimer(const time_t *Timeout) +{ + return addTimer( (unsigned long long)*Timeout* (unsigned long long) 1000000, true, false ); +} + +void CRCInput::killTimer(uint32_t id) +{ +//printf("killing timer %d\n", id); + std::vector::iterator e; + for ( e= timers.begin(); e!= timers.end(); ++e ) + if ( e->id == id ) + { + timers.erase(e); + break; + } +} + +int CRCInput::checkTimers() +{ + struct timeval tv; + int _id = 0; + + gettimeofday( &tv, NULL ); + unsigned long long timeNow = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + + + std::vector::iterator e; + for ( e= timers.begin(); e!= timers.end(); ++e ) + if ( e->times_out< timeNow+ 2000 ) + { +//printf("timeout timer %d %llx %llx\n",e->id,e->times_out,timeNow ); + _id = e->id; + if ( e->interval != 0 ) + { + timer _newtimer; + _newtimer.id = e->id; + _newtimer.interval = e->interval; + _newtimer.correct_time = e->correct_time; + if ( _newtimer.correct_time ) + _newtimer.times_out = timeNow + e->interval; + else + _newtimer.times_out = e->times_out + e->interval; + + timers.erase(e); + for ( e= timers.begin(); e!= timers.end(); ++e ) + if ( e->times_out> _newtimer.times_out ) + break; + + timers.insert(e, _newtimer); + } + else + timers.erase(e); + + break; + } +// else +// printf("skipped timer %d %llx %llx\n",e->id,e->times_out, timeNow ); +//printf("checkTimers: return %d\n", _id); + return _id; +} + + + +long long CRCInput::calcTimeoutEnd(const int timeout_in_seconds) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec + (unsigned long long)timeout_in_seconds) * (unsigned long long) 1000000; +} + +long long CRCInput::calcTimeoutEnd_MS(const int timeout_in_milliseconds) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + unsigned long long timeNow = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + + return ( timeNow + timeout_in_milliseconds * 1000 ); +} + + +void CRCInput::getMsgAbsoluteTimeout(neutrino_msg_t * msg, neutrino_msg_data_t * data, unsigned long long *TimeoutEnd, bool bAllowRepeatLR) +{ + struct timeval tv; + + gettimeofday( &tv, NULL ); + unsigned long long timeNow = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + + unsigned long long diff; + + if ( *TimeoutEnd < timeNow+ 100 ) + diff = 100; // Minimum Differenz... + else + diff = ( *TimeoutEnd - timeNow ); +//printf("CRCInput::getMsgAbsoluteTimeout diff %llx TimeoutEnd %llx now %llx\n", diff, *TimeoutEnd, timeNow); + getMsg_us( msg, data, diff, bAllowRepeatLR ); + + if ( *msg == NeutrinoMessages::EVT_TIMESET ) + { + // recalculate timeout.... + //unsigned long long ta= *TimeoutEnd; + *TimeoutEnd= *TimeoutEnd + *(long long*) *data; + + //printf("[getMsgAbsoluteTimeout]: EVT_TIMESET - recalculate timeout\n%llx/%llx - %llx/%llx\n", timeNow, *(long long*) *data, *TimeoutEnd, ta ); + } +} + +void CRCInput::getMsg(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR) +{ + getMsg_us(msg, data, (unsigned long long) Timeout * 100 * 1000, bAllowRepeatLR); +} + +void CRCInput::getMsg_ms(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR) +{ + getMsg_us(msg, data, (unsigned long long) Timeout * 1000, bAllowRepeatLR); +} + +#define ENABLE_REPEAT_CHECK +void CRCInput::getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, unsigned long long Timeout, bool bAllowRepeatLR) +{ + static unsigned long long last_keypress = 0ULL; + unsigned long long getKeyBegin; + + //static __u16 rc_last_key = KEY_MAX; + static __u16 rc_last_repeat_key = KEY_MAX; + + struct timeval tv, tvselect; + unsigned long long InitialTimeout = Timeout; + long long targetTimeout; + + int timer_id; + fd_set rfds; + t_input_event ev; + + *data = 0; + + // wiederholung reinmachen - dass wirklich die ganze zeit bis timeout gewartet wird! + gettimeofday( &tv, NULL ); + getKeyBegin = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + + while(1) { + timer_id = 0; + if ( timers.size()> 0 ) + { + gettimeofday( &tv, NULL ); + unsigned long long t_n= (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + if ( timers[0].times_out< t_n ) + { + timer_id = checkTimers(); + *msg = NeutrinoMessages::EVT_TIMER; + *data = timer_id; + return; + } + else + { + targetTimeout = timers[0].times_out - t_n; + if ( (unsigned long long) targetTimeout> Timeout) + targetTimeout= Timeout; + else + timer_id = timers[0].id; + } + } + else + targetTimeout= Timeout; + + tvselect.tv_sec = targetTimeout/1000000; + tvselect.tv_usec = targetTimeout%1000000; + + FD_ZERO(&rfds); + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) + { + if (fd_rc[i] != -1) + FD_SET(fd_rc[i], &rfds); + } +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL + if (true) +#else + if (fd_keyb> 0) +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + FD_SET(fd_keyb, &rfds); + + FD_SET(fd_event, &rfds); + FD_SET(fd_pipe_high_priority[0], &rfds); + FD_SET(fd_pipe_low_priority[0], &rfds); + + int status = select(fd_max+1, &rfds, NULL, NULL, &tvselect); + + if ( status == -1 ) + { + perror("[neutrino - getMsg_us]: select returned "); + // in case of an error return timeout...?! + *msg = RC_timeout; + *data = 0; + return; + } + else if ( status == 0 ) // Timeout! + { + if ( timer_id != 0 ) + { + timer_id = checkTimers(); + if ( timer_id != 0 ) + { + *msg = NeutrinoMessages::EVT_TIMER; + *data = timer_id; + return; + } + else + continue; + } + else + { + *msg = RC_timeout; + *data = 0; + return; + } + } + + if(FD_ISSET(fd_pipe_high_priority[0], &rfds)) + { + struct event buf; + + read(fd_pipe_high_priority[0], &buf, sizeof(buf)); + + *msg = buf.msg; + *data = buf.data; + + // printf("got event from high-pri pipe %x %x\n", *msg, *data ); + + return; + } + + +#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL + if (FD_ISSET(fd_keyb, &rfds)) + { + int trkey; + char key = 0; + read(fd_keyb, &key, sizeof(key)); + + switch(key) + { + case 27: // <- Esc + trkey = KEY_HOME; + break; + case 10: // <- Return + case 'o': + trkey = KEY_OK; + break; + case 'p': + trkey = KEY_POWER; + break; + case 's': + trkey = KEY_SETUP; + break; + case 'h': + trkey = KEY_HELP; + break; + case 'i': + trkey = KEY_UP; + break; + case 'm': + trkey = KEY_DOWN; + break; + case 'j': + trkey = KEY_LEFT; + break; + case 'k': + trkey = KEY_RIGHT; + break; + case 'r': + trkey = KEY_RED; + break; + case 'g': + trkey = KEY_GREEN; + break; + case 'y': + trkey = KEY_YELLOW; + break; + case 'b': + trkey = KEY_BLUE; + break; + case '0': + trkey = RC_0; + break; + case '1': + trkey = RC_1; + break; + case '2': + trkey = RC_2; + break; + case '3': + trkey = RC_3; + break; + case '4': + trkey = RC_4; + break; + case '5': + trkey = RC_5; + break; + case '6': + trkey = RC_6; + break; + case '7': + trkey = RC_7; + break; + case '8': + trkey = RC_8; + break; + case '9': + trkey = RC_9; + break; + case '+': + trkey = RC_plus; + break; + case '-': + trkey = RC_minus; + break; + case 'a': + trkey = KEY_A; + break; + case 'u': + trkey = KEY_U; + break; + case '/': + trkey = KEY_SLASH; + break; + case '\\': + trkey = KEY_BACKSLASH; + break; + default: + trkey = RC_nokey; + } + if (trkey != RC_nokey) + { + *msg = trkey; + *data = 0; /* <- button pressed */ + return; + } + } +#else + /* + if(FD_ISSET(fd_keyb, &rfds)) + { + char key = 0; + read(fd_keyb, &key, sizeof(key)); + printf("keyboard: %d\n", rc_key); + } + */ +#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ + + if(FD_ISSET(fd_event, &rfds)) { + //printf("[neutrino] event - accept!\n"); + socklen_t clilen; + struct sockaddr_in cliaddr; + clilen = sizeof(cliaddr); + int fd_eventclient = accept(fd_event, (struct sockaddr *) &cliaddr, &clilen); + + *msg = RC_nokey; + //printf("[neutrino] network event - read!\n"); + CEventServer::eventHead emsg; + int read_bytes= recv(fd_eventclient, &emsg, sizeof(emsg), MSG_WAITALL); + //printf("[neutrino] event read %d bytes - following %d bytes\n", read_bytes, emsg.dataSize ); + if ( read_bytes == sizeof(emsg) ) { + bool dont_delete_p = false; + + unsigned char* p; + p= new unsigned char[ emsg.dataSize + 1 ]; + if ( p!=NULL ) + { + read_bytes= recv(fd_eventclient, p, emsg.dataSize, MSG_WAITALL); + //printf("[neutrino] eventbody read %d bytes - initiator %x\n", read_bytes, emsg.initiatorID ); + + if ( emsg.initiatorID == CEventServer::INITID_CONTROLD ) + { + switch(emsg.eventID) + { + case CControldClient::EVT_VOLUMECHANGED : + *msg = NeutrinoMessages::EVT_VOLCHANGED; + *data = 0; + break; + case CControldClient::EVT_MUTECHANGED : + *msg = NeutrinoMessages::EVT_MUTECHANGED; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CControldClient::EVT_VCRCHANGED : + *msg = NeutrinoMessages::EVT_VCRCHANGED; + *data = *(int*) p; + break; + case CControldClient::EVT_MODECHANGED : + *msg = NeutrinoMessages::EVT_MODECHANGED; + *data = *(int*) p; + break; + default: + printf("[neutrino] event INITID_CONTROLD - unknown eventID 0x%x\n", emsg.eventID ); + } + } + else if ( emsg.initiatorID == CEventServer::INITID_HTTPD ) + { + switch(emsg.eventID) + { + case NeutrinoMessages::SHUTDOWN : + *msg = NeutrinoMessages::SHUTDOWN; + *data = 0; + break; + case NeutrinoMessages::EVT_POPUP : + *msg = NeutrinoMessages::EVT_POPUP; + *data = (unsigned) p; + dont_delete_p = true; + break; + case NeutrinoMessages::EVT_EXTMSG : + *msg = NeutrinoMessages::EVT_EXTMSG; + *data = (unsigned) p; + dont_delete_p = true; + break; + case NeutrinoMessages::CHANGEMODE : // Change + *msg = NeutrinoMessages::CHANGEMODE; + *data = *(unsigned*) p; + break; + case NeutrinoMessages::STANDBY_TOGGLE : + *msg = NeutrinoMessages::STANDBY_TOGGLE; + *data = 0; + break; + case NeutrinoMessages::STANDBY_ON : + *msg = NeutrinoMessages::STANDBY_ON; + *data = 0; + break; + case NeutrinoMessages::STANDBY_OFF : + *msg = NeutrinoMessages::STANDBY_OFF; + *data = 0; + break; + case NeutrinoMessages::EVT_START_PLUGIN : + *msg = NeutrinoMessages::EVT_START_PLUGIN; + *data = (unsigned) p; + dont_delete_p = true; + break; + case NeutrinoMessages::LOCK_RC : + *msg = NeutrinoMessages::LOCK_RC; + *data = 0; + break; + case NeutrinoMessages::UNLOCK_RC : + *msg = NeutrinoMessages::UNLOCK_RC; + *data = 0; + break; + default: + printf("[neutrino] event INITID_HTTPD - unknown eventID 0x%x\n", emsg.eventID ); + } + } + else if ( emsg.initiatorID == CEventServer::INITID_SECTIONSD ) + { + //printf("[neutrino] event - from SECTIONSD %x %x\n", emsg.eventID, *(unsigned*) p); + switch(emsg.eventID) + { + case CSectionsdClient::EVT_TIMESET: + { + struct timeval tv; + gettimeofday( &tv, NULL ); + long long timeOld = (long long) tv.tv_usec + (long long)((long long) tv.tv_sec * (long long) 1000000); + + //printf("[neutrino] event TIMESET from SECTIONSD %x %x\n", emsg.eventID, *(unsigned*) p); + //g_Sectionsd->registerEvent(CSectionsdClient::EVT_TIMESET, 222, NEUTRINO_UDS_NAME); + stime((time_t*) p); + + gettimeofday( &tv, NULL ); + long long timeNew = (long long) tv.tv_usec + (long long)((long long) tv.tv_sec * (long long) 1000000); + + delete[] p;//new [] delete [] + p= new unsigned char[ sizeof(long long) ]; + *(long long*) p = timeNew - timeOld; + + if ((long long)last_keypress > *(long long*)p) + last_keypress += *(long long *)p; + + // Timer anpassen + for(std::vector::iterator e = timers.begin(); e != timers.end(); ++e) + if (e->correct_time) + e->times_out+= *(long long*) p; + + *msg = NeutrinoMessages::EVT_TIMESET; + *data = (neutrino_msg_data_t) p; + dont_delete_p = true; + } + break; + case CSectionsdClient::EVT_GOT_CN_EPG: +printf("[neutrino] CSectionsdClient::EVT_GOT_CN_EPG\n"); + *msg = NeutrinoMessages::EVT_CURRENTNEXT_EPG; + *data = (neutrino_msg_data_t) p; + dont_delete_p = true; + break; + case CSectionsdClient::EVT_SERVICES_UPDATE: + *msg = NeutrinoMessages::EVT_SERVICES_UPD; + *data = 0; + break; + case CSectionsdClient::EVT_BOUQUETS_UPDATE: + break; + case CSectionsdClient::EVT_WRITE_SI_FINISHED: + *msg = NeutrinoMessages::EVT_SI_FINISHED; + *data = 0; + break; + default: + printf("[neutrino] event INITID_SECTIONSD - unknown eventID 0x%x\n", emsg.eventID ); + } + } + else if ( emsg.initiatorID == CEventServer::INITID_ZAPIT ) + { + //printf("[neutrino] event - from ZAPIT %x %x\n", emsg.eventID, *(unsigned*) p); + switch(emsg.eventID) + { + case CZapitClient::EVT_RECORDMODE_ACTIVATED: + *msg = NeutrinoMessages::EVT_RECORDMODE; + *data = true; + break; + case CZapitClient::EVT_RECORDMODE_DEACTIVATED: + *msg = NeutrinoMessages::EVT_RECORDMODE; + *data = false; + break; + case CZapitClient::EVT_ZAP_COMPLETE: + *msg = NeutrinoMessages::EVT_ZAP_COMPLETE; + break; + case CZapitClient::EVT_ZAP_FAILED: + *msg = NeutrinoMessages::EVT_ZAP_FAILED; + break; + case CZapitClient::EVT_ZAP_SUB_FAILED: + *msg = NeutrinoMessages::EVT_ZAP_SUB_FAILED; + break; + case CZapitClient::EVT_ZAP_COMPLETE_IS_NVOD: + *msg = NeutrinoMessages::EVT_ZAP_ISNVOD; + break; + case CZapitClient::EVT_ZAP_SUB_COMPLETE: + *msg = NeutrinoMessages::EVT_ZAP_SUB_COMPLETE; + break; + case CZapitClient::EVT_SCAN_COMPLETE: + *msg = NeutrinoMessages::EVT_SCAN_COMPLETE; + *data = 0; + break; + case CZapitClient::EVT_SCAN_NUM_TRANSPONDERS: + *msg = NeutrinoMessages::EVT_SCAN_NUM_TRANSPONDERS; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS: + *msg = NeutrinoMessages::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_REPORT_FREQUENCY: + *msg = NeutrinoMessages::EVT_SCAN_REPORT_FREQUENCY; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_FOUND_A_CHAN: + *msg = NeutrinoMessages::EVT_SCAN_FOUND_A_CHAN; + break; + case CZapitClient::EVT_SCAN_SERVICENAME: + *msg = NeutrinoMessages::EVT_SCAN_SERVICENAME; + break; + case CZapitClient::EVT_SCAN_FOUND_TV_CHAN: + *msg = NeutrinoMessages::EVT_SCAN_FOUND_TV_CHAN; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_FOUND_RADIO_CHAN: + *msg = NeutrinoMessages::EVT_SCAN_FOUND_RADIO_CHAN; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_FOUND_DATA_CHAN: + *msg = NeutrinoMessages::EVT_SCAN_FOUND_DATA_CHAN; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_REPORT_FREQUENCYP: + *msg = NeutrinoMessages::EVT_SCAN_REPORT_FREQUENCYP; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_NUM_CHANNELS: + *msg = NeutrinoMessages::EVT_SCAN_NUM_CHANNELS; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_PROVIDER: + *msg = NeutrinoMessages::EVT_SCAN_PROVIDER; + break; + case CZapitClient::EVT_SCAN_SATELLITE: + *msg = NeutrinoMessages::EVT_SCAN_SATELLITE; + break; + case CZapitClient::EVT_BOUQUETS_CHANGED: + *msg = NeutrinoMessages::EVT_BOUQUETSCHANGED; + *data = 0; + break; + case CZapitClient::EVT_SERVICES_CHANGED: + *msg = NeutrinoMessages::EVT_SERVICESCHANGED; + *data = 0; + break; + case CZapitClient::EVT_ZAP_CA_CLEAR: + *msg = NeutrinoMessages::EVT_ZAP_CA_CLEAR; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_ZAP_CA_LOCK: + *msg = NeutrinoMessages::EVT_ZAP_CA_LOCK; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_ZAP_CA_FTA: + *msg = NeutrinoMessages::EVT_ZAP_CA_FTA; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_ZAP_CA_ID : + *msg = NeutrinoMessages::EVT_ZAP_CA_ID; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SCAN_FAILED: + *msg = NeutrinoMessages::EVT_SCAN_FAILED; + *data = 0; + break; + case CZapitClient::EVT_ZAP_MOTOR: + *msg = NeutrinoMessages::EVT_ZAP_MOTOR; + *data = *(unsigned*) p; + break; + case CZapitClient::EVT_SDT_CHANGED: + *msg = NeutrinoMessages::EVT_SERVICES_UPD; + *data = 0; + break; + default: + printf("[neutrino] event INITID_ZAPIT - unknown eventID 0x%x\n", emsg.eventID ); + } + if (((*msg) >= CRCInput::RC_WithData) && ((*msg) < CRCInput::RC_WithData + 0x10000000)) + { + *data = (neutrino_msg_data_t) p; + dont_delete_p = true; + } + } + else if ( emsg.initiatorID == CEventServer::INITID_TIMERD ) + { + /* + if (emsg.eventID==CTimerdClient::EVT_ANNOUNCE_NEXTPROGRAM) + { + } + + if (emsg.eventID==CTimerdClient::EVT_NEXTPROGRAM) + { + *msg = NeutrinoMessages::EVT_NEXTPROGRAM; + *data = (neutrino_msg_data_t) p; + dont_delete_p = true; + } + */ + switch(emsg.eventID) + { + case CTimerdClient::EVT_ANNOUNCE_RECORD : + *msg = NeutrinoMessages::ANNOUNCE_RECORD; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CTimerdClient::EVT_ANNOUNCE_ZAPTO : + *msg = NeutrinoMessages::ANNOUNCE_ZAPTO; + *data = 0; + break; + case CTimerdClient::EVT_ANNOUNCE_SHUTDOWN : + *msg = NeutrinoMessages::ANNOUNCE_SHUTDOWN; + *data = 0; + break; + case CTimerdClient::EVT_ANNOUNCE_SLEEPTIMER : + *msg = NeutrinoMessages::ANNOUNCE_SLEEPTIMER; + *data = 0; + break; + case CTimerdClient::EVT_SLEEPTIMER : + *msg = NeutrinoMessages::SLEEPTIMER; + *data = 0; + break; + case CTimerdClient::EVT_RECORD_START : + *msg = NeutrinoMessages::RECORD_START; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CTimerdClient::EVT_RECORD_STOP : + *msg = NeutrinoMessages::RECORD_STOP; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CTimerdClient::EVT_ZAPTO : + *msg = NeutrinoMessages::ZAPTO; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CTimerdClient::EVT_SHUTDOWN : + *msg = NeutrinoMessages::SHUTDOWN; + *data = 0; + break; + case CTimerdClient::EVT_STANDBY_ON : + *msg = NeutrinoMessages::STANDBY_ON; + *data = 0; + break; + case CTimerdClient::EVT_STANDBY_OFF : + *msg = NeutrinoMessages::STANDBY_OFF; + *data = 0; + break; + case CTimerdClient::EVT_REMIND : + *msg = NeutrinoMessages::REMIND; + *data = (unsigned) p; + dont_delete_p = true; + break; + case CTimerdClient::EVT_EXEC_PLUGIN : + *msg = NeutrinoMessages::EVT_START_PLUGIN; + *data = (unsigned) p; + dont_delete_p = true; + break; + + default : + printf("[neutrino] event INITID_TIMERD - unknown eventID 0x%x\n", emsg.eventID ); + + } + } + else if (emsg.initiatorID == CEventServer::INITID_NEUTRINO) + { + if ((emsg.eventID == NeutrinoMessages::EVT_RECORDING_ENDED) && + (read_bytes == sizeof(stream2file_status2_t))) + { + *msg = NeutrinoMessages::EVT_RECORDING_ENDED; + *data = (neutrino_msg_data_t) p; + dont_delete_p = true; + } + } + else if (emsg.initiatorID == CEventServer::INITID_GENERIC_INPUT_EVENT_PROVIDER) + { + if (read_bytes == sizeof(int)) + { + *msg = *(int *)p; + *data = emsg.eventID; + } + } + else + printf("[neutrino] event - unknown initiatorID 0x%x\n", emsg.initiatorID); + if ( !dont_delete_p ) + { + delete[] p;//new [] delete [] + p= NULL; + } + } + } + else + { + printf("[neutrino] event - read failed!\n"); + } + + ::close(fd_eventclient); + + if ( *msg != RC_nokey ) + { + // raus hier :) + //printf("[neutrino] event 0x%x\n", *msg); + return; + } + } + + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) { + if ((fd_rc[i] != -1) && (FD_ISSET(fd_rc[i], &rfds))) { + int ret; + ret = read(fd_rc[i], &ev, sizeof(t_input_event)); + + if(ret != sizeof(t_input_event)) + continue; + printf("key: %04x value %d, translate: %04x -%s-\n", ev.code, ev.value, translate(ev.code, i), getKeyName(translate(ev.code, i)).c_str()); + uint32_t trkey = translate(ev.code, i); + + if (trkey == RC_nokey) + continue; + if (ev.value) { +#ifdef RCDEBUG + printf("got keydown native key: %04x %04x, translate: %04x -%s-\n", ev.code, ev.code&0x1f, translate(ev.code, 0), getKeyName(translate(ev.code, 0)).c_str()); + printf("rc_last_key %04x rc_last_repeat_key %04x\n\n", rc_last_key, rc_last_repeat_key); +#endif + unsigned long long now_pressed; + bool keyok = true; + + tv = ev.time; + now_pressed = (unsigned long long) tv.tv_usec + (unsigned long long)((unsigned long long) tv.tv_sec * (unsigned long long) 1000000); + if (ev.code == rc_last_key) { + /* only allow selected keys to be repeated */ + /* (why?) */ + if((trkey == RC_up) || (trkey == RC_down ) || + (trkey == RC_plus ) || (trkey == RC_minus ) || + (trkey == RC_page_down ) || (trkey == RC_page_up ) || + //(trkey == RC_standby) || + ((bAllowRepeatLR) && ((trkey == RC_left ) || + (trkey == RC_right)))) + { +#ifdef ENABLE_REPEAT_CHECK + if (rc_last_repeat_key != ev.code) { + if ((now_pressed > last_keypress + repeat_block) || + /* accept all keys after time discontinuity: */ + (now_pressed < last_keypress)) + rc_last_repeat_key = ev.code; + else + keyok = false; + } +#endif + } + else + keyok = false; + } + else + rc_last_repeat_key = KEY_MAX; + + rc_last_key = ev.code; + + if (keyok) { +#ifdef ENABLE_REPEAT_CHECK + if ((now_pressed > last_keypress + repeat_block_generic) || + /* accept all keys after time discontinuity: */ + (now_pressed < last_keypress)) +#endif + { + last_keypress = now_pressed; + + *msg = trkey; + *data = 0; /* <- button pressed */ + if(g_settings.key_click) + play_click(); + return; + } + } /*if keyok */ + } /* if (ev.value) */ + else { + // clear rc_last_key on keyup event +#ifdef RCDEBUG + printf("got keyup native key: %04x %04x, translate: %04x -%s-\n", ev.code, ev.code&0x1f, translate(ev.code, 0), getKeyName(translate(ev.code, 0)).c_str() ); +#endif + rc_last_key = KEY_MAX; + if (trkey == RC_standby) { + *msg = RC_standby; + *data = 1; /* <- button released */ + return; + } + } + }/* if FDSET */ + } /* for NUMBER_OF_EVENT_DEVICES */ + + if(FD_ISSET(fd_pipe_low_priority[0], &rfds)) + { + struct event buf; + + read(fd_pipe_low_priority[0], &buf, sizeof(buf)); + + *msg = buf.msg; + *data = buf.data; + + // printf("got event from low-pri pipe %x %x\n", *msg, *data ); + + return; + } + + if ( InitialTimeout == 0 ) + { + //nicht warten wenn kein key da ist + *msg = RC_timeout; + *data = 0; + return; + } + else + { + //timeout neu kalkulieren + gettimeofday( &tv, NULL ); + long long getKeyNow = (long long) tv.tv_usec + (long long)((long long) tv.tv_sec * (long long) 1000000); + long long diff = (getKeyNow - getKeyBegin); + if( Timeout <= (unsigned long long) diff ) + { + *msg = RC_timeout; + *data = 0; + return; + } + else + Timeout -= diff; + } + } +} + +void CRCInput::postMsg(const neutrino_msg_t msg, const neutrino_msg_data_t data, const bool Priority) +{ +// printf("postMsg %x %x %d\n", msg, data, Priority ); + + struct event buf; + buf.msg = msg; + buf.data = data; + + if (Priority) + write(fd_pipe_high_priority[1], &buf, sizeof(buf)); + else + write(fd_pipe_low_priority[1], &buf, sizeof(buf)); +} + + +void CRCInput::clearRCMsg() +{ + t_input_event ev; + + for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) + { + if (fd_rc[i] != -1) + { + while (read(fd_rc[i], &ev, sizeof(t_input_event)) == sizeof(t_input_event)) + ; + } + } + rc_last_key = KEY_MAX; +} + +/************************************************************************** +* isNumeric - test if key is 0..9 +* +**************************************************************************/ +bool CRCInput::isNumeric(const neutrino_msg_t key) +{ + return ((key == RC_0) || ((key >= RC_1) && (key <= RC_9))); +} + +/************************************************************************** +* getNumericValue - return numeric value of the key or -1 +* +**************************************************************************/ +int CRCInput::getNumericValue(const neutrino_msg_t key) +{ + return ((key == RC_0) ? (int)0 : (((key >= RC_1) && (key <= RC_9)) ? (int)(key - RC_1 + 1) : (int)-1)); +} + +/************************************************************************** +* convertDigitToKey - return key representing digit or RC_nokey +* +**************************************************************************/ +static const unsigned int digit_to_key[10] = {CRCInput::RC_0, CRCInput::RC_1, CRCInput::RC_2, CRCInput::RC_3, CRCInput::RC_4, CRCInput::RC_5, CRCInput::RC_6, CRCInput::RC_7, CRCInput::RC_8, CRCInput::RC_9}; + +unsigned int CRCInput::convertDigitToKey(const unsigned int digit) +{ + return (digit < 10) ? digit_to_key[digit] : RC_nokey; +} + +/************************************************************************** +* getUnicodeValue - return unicode value of the key or -1 +* +**************************************************************************/ +#define UNICODE_VALUE_SIZE 58 +static const int unicode_value[UNICODE_VALUE_SIZE] = {-1 , -1 , '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', -1 , -1 , + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', -1 , -1 , 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', -1 /* FIXME */, -1 /* FIXME */, -1 , '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', -1, -1, -1, ' '}; + +int CRCInput::getUnicodeValue(const neutrino_msg_t key) +{ + if (key < UNICODE_VALUE_SIZE) + return unicode_value[key]; + else + return -1; +} + +/************************************************************************** +* transforms the rc-key to const char * +* +**************************************************************************/ +const char * CRCInput::getSpecialKeyName(const unsigned int key) +{ + switch(key) + { + case RC_standby: + return "standby"; + case RC_home: +#if HAVE_DVB_API_VERSION == 1 + return "lame"; +#else + return "home"; +#endif + case RC_setup: +#if HAVE_DVB_API_VERSION == 1 + return "dream"; +#else + return "setup"; +#endif + case RC_red: + return "red button"; + case RC_green: + return "green button"; + case RC_yellow: + return "yellow button"; + case RC_blue: + return "blue button"; + case RC_page_up: + return "page up"; + case RC_page_down: + return "page down"; + case RC_up: + return "cursor up"; + case RC_down: + return "cursor down"; + case RC_left: + return "cursor left"; + case RC_right: + return "cursor right"; + case RC_ok: + return "ok"; + case RC_plus: + return "vol. inc"; + case RC_minus: + return "vol. dec"; + case RC_spkr: + return "mute"; + case RC_help: + return "help"; + case RC_info: + return "info"; + case RC_topleft: + return "topleft"; + case RC_topright: + return "topright"; + case RC_audio: + return "audio"; + case RC_video: + return "video"; + case RC_tv: + return "tv"; + case RC_radio: + return "radio"; + case RC_text: + return "text"; +#if 0 + case RC_shift_red: + return "shift-red"; + case RC_shift_green: + return "shift-green"; + case RC_shift_yellow: + return "shift-yellow"; + case RC_shift_blue: + return "shift-blue"; + case RC_shift_tv: + return "shift-tv"; + case RC_shift_radio: + return "shift-radio"; +#endif + case RC_epg: + return "epg"; + case RC_recall: + return "recall"; + case RC_favorites: + return "favorites"; + case RC_sat: + return "sat"; + case RC_sat2: + return "sat2"; + case RC_timeout: + return "timeout"; + case RC_play: + return "play"; + case RC_stop: + return "stop"; + case RC_forward: + return "forward"; + case RC_rewind: + return "rewind"; + case RC_timeshift: + return "timeshift"; + case RC_mode: + return "mode"; + case RC_record: + return "record"; + case RC_pause: + return "pause"; + case RC_games: + return "games"; + case RC_next: + return "next"; + case RC_prev: + return "prev"; + case RC_nokey: + return "none"; + default: + printf("unknown key: %d (0x%x) \n", key, key); + return "unknown"; + } +} + +std::string CRCInput::getKeyName(const unsigned int key) +{ + int unicode_value = getUnicodeValue(key); + if (unicode_value == -1) + return getSpecialKeyName(key); + else + { + char tmp[2]; + tmp[0] = unicode_value; + tmp[1] = 0; + return std::string(tmp); + } +} + +/************************************************************************** +* transforms the rc-key to generic - internal use only! +* +**************************************************************************/ +int CRCInput::translate(int code, int num) +{ + if(code == 0x100) code = RC_up; + else if(code == 0x101) code = RC_down; + if ((code >= 0) && (code <= KEY_MAX)) + return code; + else + return RC_nokey; +} + +void CRCInput::close_click() +{ +} + +void CRCInput::open_click() +{ +} + +void CRCInput::reset_dsp(int rate) +{ +} + +void CRCInput::set_dsp() +{ +} + +void CRCInput::play_click() +{ +} diff --git a/src/driver/rcinput.h b/src/driver/rcinput.h new file mode 100644 index 000000000..e0d830b0a --- /dev/null +++ b/src/driver/rcinput.h @@ -0,0 +1,247 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __MOD_rcinput__ +#define __MOD_rcinput__ + +#include +#include +#include + +#ifndef KEY_OK +#define KEY_OK 0x160 +#endif + +#ifndef KEY_RED +#define KEY_RED 0x18e +#endif + +#ifndef KEY_GREEN +#define KEY_GREEN 0x18f +#endif + +#ifndef KEY_YELLOW +#define KEY_YELLOW 0x190 +#endif + +#ifndef KEY_BLUE +#define KEY_BLUE 0x191 +#endif + +/* SAGEM remote controls have the following additional keys */ + +#ifndef KEY_TOPLEFT +#define KEY_TOPLEFT 0x1a2 +#endif + +#ifndef KEY_TOPRIGHT +#define KEY_TOPRIGHT 0x1a3 +#endif + +#ifndef KEY_BOTTOMLEFT +#define KEY_BOTTOMLEFT 0x1a4 +#endif + +#ifndef KEY_BOTTOMRIGHT +#define KEY_BOTTOMRIGHT 0x1a5 +#endif + +typedef uint32_t neutrino_msg_t; +typedef uint32_t neutrino_msg_data_t; + +#define NEUTRINO_UDS_NAME "/tmp/neutrino.sock" + + +class CRCInput +{ + private: + struct event + { + neutrino_msg_t msg; + neutrino_msg_data_t data; + }; + + struct timer + { + uint id; + unsigned long long interval; + unsigned long long times_out; + bool correct_time; + }; + + uint32_t timerid; + std::vector timers; + + int fd_pipe_high_priority[2]; + int fd_pipe_low_priority[2]; + int fd_gamerc; +#define NUMBER_OF_EVENT_DEVICES 1 + int fd_rc[NUMBER_OF_EVENT_DEVICES]; + int fd_keyb; + int fd_event; + + int fd_max; + int clickfd; + __u16 rc_last_key; + void set_dsp(); + + void open(); + void close(); + int translate(int code, int num); + + void calculateMaxFd(void); + + int checkTimers(); + + public: + //rc-code definitions + static const neutrino_msg_t RC_Repeat = 0x0400; + static const neutrino_msg_t RC_Release = 0x0800; + static const neutrino_msg_t RC_MaxRC = KEY_MAX | RC_Repeat | RC_Release; /* /include/linux/input.h: #define KEY_MAX 0x1ff */ + static const neutrino_msg_t RC_KeyBoard = 0x4000; + static const neutrino_msg_t RC_Events = 0x80000000; + static const neutrino_msg_t RC_Messages = 0x90000000; + static const neutrino_msg_t RC_WithData = 0xA0000000; + enum + { + RC_0 = KEY_0, /* /include/linux/input.h: #define KEY_0 11 */ + RC_1 = KEY_1, /* /include/linux/input.h: #define KEY_1 2 */ + RC_2 = KEY_2, /* /include/linux/input.h: #define KEY_2 3 */ + RC_3 = KEY_3, /* /include/linux/input.h: #define KEY_3 4 */ + RC_4 = KEY_4, /* /include/linux/input.h: #define KEY_4 5 */ + RC_5 = KEY_5, /* /include/linux/input.h: #define KEY_5 6 */ + RC_6 = KEY_6, /* /include/linux/input.h: #define KEY_6 7 */ + RC_7 = KEY_7, /* /include/linux/input.h: #define KEY_7 8 */ + RC_8 = KEY_8, /* /include/linux/input.h: #define KEY_8 9 */ + RC_9 = KEY_9, /* /include/linux/input.h: #define KEY_9 10 */ + RC_backspace = KEY_BACKSPACE, /* /include/linux/input.h: #define KEY_BACKSPACE 14 */ + RC_up = KEY_UP, /* /include/linux/input.h: #define KEY_UP 103 */ + RC_left = KEY_LEFT, /* /include/linux/input.h: #define KEY_LEFT 105 */ + RC_right = KEY_RIGHT, /* /include/linux/input.h: #define KEY_RIGHT 106 */ + RC_down = KEY_DOWN, /* /include/linux/input.h: #define KEY_DOWN 108 */ + RC_spkr = KEY_MUTE, /* /include/linux/input.h: #define KEY_MUTE 113 */ + RC_minus = KEY_VOLUMEDOWN, /* /include/linux/input.h: #define KEY_VOLUMEDOWN 114 */ + RC_plus = KEY_VOLUMEUP, /* /include/linux/input.h: #define KEY_VOLUMEUP 115 */ + RC_standby = KEY_POWER, /* /include/linux/input.h: #define KEY_POWER 116 */ + RC_help = KEY_HELP, /* /include/linux/input.h: #define KEY_HELP 138 */ + RC_home = KEY_EXIT, /* /include/linux/input.h: #define KEY_HOME 102 */ + RC_setup = KEY_MENU, /* /include/linux/input.h: #define KEY_SETUP 141 */ + RC_topleft = KEY_TOPLEFT, + RC_topright = KEY_TOPRIGHT, + RC_page_up = KEY_PAGEUP, /* /include/linux/input.h: #define KEY_PAGEUP 104 */ + RC_page_down = KEY_PAGEDOWN, /* /include/linux/input.h: #define KEY_PAGEDOWN 109 */ + RC_ok = KEY_OK, /* /include/linux/input.h: #define KEY_OK 0x160 */ /* in patched input.h */ + RC_red = KEY_RED, /* /include/linux/input.h: #define KEY_RED 0x18e */ /* in patched input.h */ + RC_green = KEY_GREEN, /* /include/linux/input.h: #define KEY_GREEN 0x18f */ /* in patched input.h */ + RC_yellow = KEY_YELLOW, /* /include/linux/input.h: #define KEY_YELLOW 0x190 */ /* in patched input.h */ + RC_blue = KEY_BLUE, /* /include/linux/input.h: #define KEY_BLUE 0x191 */ /* in patched input.h */ + RC_top_left = KEY_TOPLEFT, /* /include/linux/input.h: #define KEY_TOPLEFT 0x1a2 */ /* in patched input.h */ + RC_top_right = KEY_TOPRIGHT, /* /include/linux/input.h: #define KEY_TOPRIGHT 0x1a3 */ /* in patched input.h */ + RC_bottom_left = KEY_BOTTOMLEFT, /* /include/linux/input.h: #define KEY_BOTTOMLEFT 0x1a4 */ /* in patched input.h */ + RC_bottom_right = KEY_BOTTOMRIGHT, /* /include/linux/input.h: #define KEY_BOTTOMRIGHT 0x1a5 */ /* in patched input.h */ + + RC_audio = KEY_AUDIO, + RC_video = KEY_VIDEO, + RC_tv = KEY_TV, + RC_radio = KEY_RADIO, + RC_text = KEY_TEXT, + RC_info = KEY_INFO, + RC_epg = KEY_EPG, + RC_recall = KEY_LAST, + RC_favorites = KEY_FAVORITES, + RC_sat = KEY_SAT, + RC_sat2 = KEY_SAT2, + RC_record = KEY_RECORD, + RC_play = KEY_PLAY, + RC_pause = KEY_PAUSE, + RC_forward = KEY_FORWARD, + RC_rewind = KEY_REWIND, + RC_stop = KEY_STOP, + RC_timeshift = KEY_TIME, + RC_mode = KEY_MODE, + RC_games = KEY_GAMES, + RC_next = KEY_NEXT, + RC_prev = KEY_PREVIOUS, + + RC_timeout = 0xFFFFFFFF, + RC_nokey = 0xFFFFFFFE + }; + + inline int getFileHandle(void) /* used for plugins (i.e. games) only */ + { +#if HAVE_DVB_API_VERSION == 1 + return fd_gamerc; +#else + return fd_rc[0]; +#endif + } + void stopInput(); + void restartInput(); + + unsigned long long repeat_block; + unsigned long long repeat_block_generic; + CRCInput(); //constructor - opens rc-device and starts needed threads + ~CRCInput(); //destructor - closes rc-device + + + static bool isNumeric(const neutrino_msg_t key); + static int getNumericValue(const neutrino_msg_t key); + static unsigned int convertDigitToKey(const unsigned int digit); + static int getUnicodeValue(const neutrino_msg_t key); + + static const char * getSpecialKeyName(const unsigned int key); + static std::string getKeyName(const unsigned int key); + + int addTimer(unsigned long long Interval, bool oneshot= true, bool correct_time= true ); + int addTimer(struct timeval Timeout); + int addTimer(const time_t *Timeout); + + void killTimer(uint32_t id); + + static long long calcTimeoutEnd_MS(const int timeout_in_milliseconds); + static long long calcTimeoutEnd(const int timeout_in_seconds); + + void getMsgAbsoluteTimeout(neutrino_msg_t * msg, neutrino_msg_data_t * data, unsigned long long *TimeoutEnd, bool bAllowRepeatLR= false); + void getMsg(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR= false); //get message, timeout in 1/10 secs :) + void getMsg_ms(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR= false); //get message, timeout in msecs :) + void getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, unsigned long long Timeout, bool bAllowRepeatLR= false);//get message, timeout in µsecs :) + void postMsg(const neutrino_msg_t msg, const neutrino_msg_data_t data, const bool Priority = true); // push message back into buffer + void clearRCMsg(); + + int messageLoop( bool anyKeyCancels = false, int timeout= -1 ); + void open_click(); + void close_click(); + void play_click(); + void reset_dsp(int rate); +}; + + +#endif diff --git a/src/driver/rfmod.cpp b/src/driver/rfmod.cpp new file mode 100644 index 000000000..5cbe6f071 --- /dev/null +++ b/src/driver/rfmod.cpp @@ -0,0 +1,134 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#define RFMOD_DEV "/dev/rfmod0" +#define IOCTL_SET_CHANNEL 0 +#define IOCTL_SET_TESTMODE 1 +#define IOCTL_SET_SOUNDENABLE 2 +#define IOCTL_SET_SOUNDSUBCARRIER 3 +#define IOCTL_SET_FINETUNE 4 +#define IOCTL_SET_STANDBY 5 + +#define C0 3 +#define C1 2 +#define FL 1 +#define FM 0 + +RFmod::RFmod() +{ + rfmodfd=open(RFMOD_DEV, O_RDWR); +} + +void RFmod::init() +{ + soundsubcarrier=g_settings.rf_subcarrier; + soundenable=g_settings.rf_soundenable; + channel = g_settings.rf_channel; + finetune = g_settings.rf_finetune; + standby = g_settings.rf_standby; + + setSoundSubCarrier(soundsubcarrier); + setSoundEnable(soundenable); + setChannel(channel); + setFinetune(finetune); + setStandby(standby); + setTestPattern(0); +} + +RFmod::~RFmod() +{ + if (rfmodfd>=0) + close(rfmodfd); +} + +int RFmod::setSoundEnable(int val) +{ + soundenable = val; +//printf("RF sound: %d\n", val); + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_SOUNDENABLE,&soundenable); + + return 0; +} + +int RFmod::setStandby(int val) +{ + standby = val; +//printf("RF standby: %d\n", val); + + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_STANDBY,&standby); + + return 0; +} + +int RFmod::setChannel(int val) +{ + channel = val; +//printf("RF channel: %d\n", val); + + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_CHANNEL,&channel); + + return 0; +} + +int RFmod::setFinetune(int val) +{ + finetune = val; +//printf("RF finetune: %d\n", val); + + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_FINETUNE,&finetune); + + return 0; +} + +int RFmod::setTestPattern(int val) +{ +//printf("RF test: %d\n", val); + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_TESTMODE,&val); + + return 0; +} + + +int RFmod::setSoundSubCarrier(int freq) //freq in KHz +{ +//printf("RF carrier: %d\n", freq); + soundsubcarrier=freq; +/* + switch(freq) + { + case 4500: + sfd=0; + break; + case 5500: + sfd=1; + break; + case 6000: + sfd=2; + break; + case 6500: + sfd=3; + break; + default: + return -1; + } +*/ + if(rfmodfd > 0) + ioctl(rfmodfd,IOCTL_SET_SOUNDSUBCARRIER,&soundsubcarrier); + + return 0; +} diff --git a/src/driver/rfmod.h b/src/driver/rfmod.h new file mode 100644 index 000000000..31b669077 --- /dev/null +++ b/src/driver/rfmod.h @@ -0,0 +1,23 @@ +#ifndef __erfmod_h +#define __erfmod_h + + +class RFmod +{ + int channel,soundsubcarrier,soundenable,finetune,standby; + +public: + RFmod(); + ~RFmod(); + + int rfmodfd; + void init(); + + int setChannel(int channel); + int setSoundSubCarrier(int val); + int setSoundEnable(int val); + int setStandby(int val); + int setFinetune(int val); + int setTestPattern(int val); +}; +#endif diff --git a/src/driver/ringbuffer.c b/src/driver/ringbuffer.c new file mode 100755 index 000000000..b15120c49 --- /dev/null +++ b/src/driver/ringbuffer.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2000 Paul Davis + * Copyright (C) 2003 Rohan Drape + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. + * This is safe for the case of one read thread and one write thread. + */ + +#include +#include +#include +#include +#include "ringbuffer.h" + + +/* Create a new ringbuffer to hold at least `sz' bytes of data. The + * actual buffer size is rounded up to the next power of two. + */ +ringbuffer_t * ringbuffer_create (int sz) +{ + int power_of_two; + ringbuffer_t *rb; + + rb = malloc (sizeof (ringbuffer_t)); + + for(power_of_two = 1; 1 << power_of_two < sz; power_of_two++) + ; + + rb->size = 1 << power_of_two; + rb->size_mask = rb->size-1; + rb->write_ptr = 0; + rb->read_ptr = 0; + rb->buf = malloc (rb->size); + rb->mlocked = 0; +printf("RING buffer size %d\n", rb->size); fflush(stdout); + return rb; +} + + +/* Free all data associated with the ringbuffer `rb'. + */ +void ringbuffer_free (ringbuffer_t * rb) +{ + if (rb->mlocked) + munlock (rb->buf, rb->size); + + free (rb->buf); +} + +/* Lock the data block of `rb' using the system call 'mlock'. */ +int ringbuffer_mlock (ringbuffer_t * rb) +{ + if (mlock (rb->buf, rb->size)) + return -1; + + rb->mlocked = 1; + return 0; +} + +/* Reset the read and write pointers to zero. This is not thread + * safe. + */ +void ringbuffer_reset (ringbuffer_t * rb) +{ + rb->read_ptr = 0; + rb->write_ptr = 0; +} + +/* Return the number of bytes available for reading. This is the + * number of bytes in front of the read pointer and behind the write + * pointer. + */ +size_t ringbuffer_read_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return w - r; + else + return (w - r + rb->size) & rb->size_mask; +} + +/* Return the number of bytes available for writing. This is the + * number of bytes in front of the write pointer and behind the read + * pointer. + */ +size_t ringbuffer_write_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + return (r - w) - 1; + else + return rb->size - 1; +} + +/* The copying data reader. Copy at most `cnt' bytes from `rb' to + * `dest'. Returns the actual number of bytes copied. + */ +size_t ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + if ((free_cnt = ringbuffer_read_space (rb)) == 0) + return 0; + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) + { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[rb->read_ptr]), n1); + rb->read_ptr += n1; + rb->read_ptr &= rb->size_mask; + + if (n2) + { + memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); + rb->read_ptr += n2; + rb->read_ptr &= rb->size_mask; + } + + return to_read; +} + +/* The copying data writer. Copy at most `cnt' bytes to `rb' from + * `src'. Returns the actual number of bytes copied. + */ +size_t ringbuffer_write (ringbuffer_t * rb, char *src, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + if ((free_cnt = ringbuffer_write_space (rb)) == 0) + return 0; + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->write_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_write; + n2 = 0; + } + + memcpy (&(rb->buf[rb->write_ptr]), src, n1); + rb->write_ptr += n1; + rb->write_ptr &= rb->size_mask; + + if (n2) + { + memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); + rb->write_ptr += n2; + rb->write_ptr &= rb->size_mask; + } + + return to_write; +} + +/* Advance the read pointer `cnt' places. + */ +void ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->read_ptr += cnt; + rb->read_ptr &= rb->size_mask; +} + +/* Advance the write pointer `cnt' places. + */ +void ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->write_ptr += cnt; + rb->write_ptr &= rb->size_mask; +} + +/* The non-copying data reader. `vec' is an array of two places. Set + * the values at `vec' to hold the current readable data at `rb'. If + * the readable data is in one segment the second segment has zero + * length. + */ +void ringbuffer_get_read_vector (ringbuffer_t * rb, ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = w - r; + else + free_cnt = (w - r + rb->size) & rb->size_mask; + + cnt2 = r + free_cnt; + + if (cnt2 > rb->size) + { + /* Two part vector: the rest of the buffer after the current write + * ptr, plus some from the start of the buffer. + */ + vec[0].buf = &(rb->buf[r]); + vec[0].len = rb->size - r; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + /* Single part vector: just the rest of the buffer */ + vec[0].buf = &(rb->buf[r]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +/* The non-copying data writer. `vec' is an array of two places. Set + * the values at `vec' to hold the current writeable data at `rb'. If + * the writeable data is in one segment the second segment has zero + * length. + */ +void ringbuffer_get_write_vector (ringbuffer_t * rb, ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + free_cnt = (r - w) - 1; + else + free_cnt = rb->size - 1; +//free_cnt = free_cnt / 188 * 188; + cnt2 = w + free_cnt; + + if (cnt2 > rb->size) + { + /* Two part vector: the rest of the buffer after the current write + * ptr, plus some from the start of the buffer. + */ + vec[0].buf = &(rb->buf[w]); + vec[0].len = rb->size - w; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + vec[0].buf = &(rb->buf[w]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} diff --git a/src/driver/ringbuffer.h b/src/driver/ringbuffer.h new file mode 100755 index 000000000..f86b0eb08 --- /dev/null +++ b/src/driver/ringbuffer.h @@ -0,0 +1,40 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H + +#include + +typedef struct +{ + char *buf; + size_t len; +} ringbuffer_data_t; + +typedef struct +{ + char *buf; + volatile size_t write_ptr; + volatile size_t read_ptr; + size_t size; + size_t size_mask; + int mlocked; +} ringbuffer_t; + +ringbuffer_t *ringbuffer_create(int sz); +void ringbuffer_free(ringbuffer_t *rb); + +int ringbuffer_mlock(ringbuffer_t *rb); +void ringbuffer_reset(ringbuffer_t *rb); + +void ringbuffer_write_advance(ringbuffer_t *rb, size_t cnt); +void ringbuffer_read_advance(ringbuffer_t *rb, size_t cnt); + +size_t ringbuffer_write_space(ringbuffer_t *rb); +size_t ringbuffer_read_space(ringbuffer_t *rb); + +size_t ringbuffer_read(ringbuffer_t *rb, char *dest, size_t cnt); +size_t ringbuffer_write(ringbuffer_t *rb, char *src, size_t cnt); + +void ringbuffer_get_read_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); +void ringbuffer_get_write_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); + +#endif diff --git a/src/driver/screen_max.cpp b/src/driver/screen_max.cpp new file mode 100644 index 000000000..80dbb0060 --- /dev/null +++ b/src/driver/screen_max.cpp @@ -0,0 +1,51 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/screen_max.cpp,v 1.3 2004/03/17 22:56:08 rasc Exp $ + * + * -- some odd module to calc max. screen usage of an menue + * -- this should be somewhere else (neutrino needs redesign) + * + * (C) 2004 by rasc + */ + +#include +#include "global.h" +#include "driver/screen_max.h" + +// -- this is a simple odd class provided for 'static' usage +// -- to calculate max. usage of a preferred menue size (x,y) +// -- this is due to 16:9 TV zoom functions, which are cutting menues. +// -- so using this function will make menues to obey max. screen settings. +// +// usage: e.g. try to paint menue h: 500 w, 400 (but screen is limited +// to (450 x 420)), functions will return 450 and 400 +// the _add factor is for boundary... +// +// 16:9 Zoom-Mode on a Thomson TV set will e.g. be 625x415 +// +// 2004-03-17 rasc + +int w_max (int w_size, int w_add) +{ + int dw; + int ret; + + dw = (g_settings.screen_EndX - g_settings.screen_StartX); + + ret = w_size; + if (dw <= (w_size + w_add) ) ret = dw - w_add; + + return ret; +} + +int h_max (int h_size, int h_add) +{ + int dh; + int ret; + + dh = (g_settings.screen_EndY - g_settings.screen_StartY); + + ret = h_size; + if (dh <= (h_size + h_add) ) ret = dh - h_add; + + return ret; +} diff --git a/src/driver/screen_max.h b/src/driver/screen_max.h new file mode 100644 index 000000000..8b60d0570 --- /dev/null +++ b/src/driver/screen_max.h @@ -0,0 +1,33 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/screen_max.h,v 1.1 2004/03/17 21:30:51 rasc Exp $ + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef __SCREEN_MAX__ +#define __SCREEN_MAX__ + + +int w_max (int w_size, int w_add); +int h_max (int h_size, int h_add); + + + +#endif + + diff --git a/src/driver/showframe.c b/src/driver/showframe.c new file mode 100644 index 000000000..a00afd1b0 --- /dev/null +++ b/src/driver/showframe.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIDEO_DEV "/dev/dvb/card0/video0" +#define VIDEO_SET_AUTOFLUSH _IOW('o', 2, int) + +void showframe(char * fname) +{ + int file, size; + char *buffer; + + file=open(fname, O_RDONLY); + if (file < 0) + return; + size=lseek(file, 0, SEEK_END); + lseek(file, 0, SEEK_SET); + if (size < 0) + { + close(file); + return; + } + buffer = malloc(size); + read(file, buffer, size); + close(file); + displayIFrame(buffer, size); + free(buffer); + return; +} +static int displayIFrame(const char *frame, int len) +{ + int fdv, fdvideo, i; + fdv=open("/dev/video", O_WRONLY); + if (fdv < 0) { + printf("cant open /dev/video\n"); + return -1; + } + + fdvideo = open(VIDEO_DEV, O_RDWR); + if (fdvideo < 0) { + colose(fdv);//Resource leak: fdv + printf("cant open %s\n", VIDEO_DEV); + return -1; + } + if (ioctl(fdvideo, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY ) <0) + printf("VIDEO_SELECT_SOURCE failed \n"); + if (ioctl(fdvideo, VIDEO_CLEAR_BUFFER) <0 ) + printf("VIDEO_CLEAR_BUFFER failed \n"); + if (ioctl(fdvideo, VIDEO_PLAY) < 0 ) + printf("VIDEO_PLAY failed\n"); + + for (i=0; i < 2; i++ ) + write(fdv, frame, len); + + unsigned char buf[128]; + memset(&buf, 0, 128); + write(fdv, buf, 128); + + if ( ioctl(fdv, VIDEO_SET_AUTOFLUSH, 0) < 0 ) + printf("VIDEO_SET_AUTOFLUSH off failed\n"); + + if (ioctl(fdvideo, VIDEO_SET_BLANK, 0) < 0 ) + printf("VIDEO_SET_BLANK failed\n"); + + close(fdvideo); + if ( ioctl(fdv, VIDEO_SET_AUTOFLUSH, 1) < 0 ) + printf("VIDEO_SET_AUTOFLUSH on failed\n"); + + close(fdv); + return 0; +} + diff --git a/src/driver/shutdown_count.cpp b/src/driver/shutdown_count.cpp new file mode 100644 index 000000000..c6fd2e398 --- /dev/null +++ b/src/driver/shutdown_count.cpp @@ -0,0 +1,97 @@ +/* + Shutdown Counter - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include + + +SHTDCNT::SHTDCNT() + : configfile('\t') +{ +} + +SHTDCNT* SHTDCNT::getInstance() +{ + static SHTDCNT* shtdcnt = NULL; + if(shtdcnt == NULL) + { + shtdcnt = new SHTDCNT(); + } + return shtdcnt; +} + +void* SHTDCNT::TimeThread(void *) +{ + while(1) + { + sleep(1); + SHTDCNT::getInstance()->shutdown_counter(); + } + return NULL; +} + +void SHTDCNT::init() +{ + if (pthread_create (&thrTime, NULL, TimeThread, NULL) != 0 ) + { + perror("[SHTDCNT]: pthread_create(TimeThread)"); + return ; + } +} + +void SHTDCNT::shutdown_counter() +{ + if (atoi(g_settings.shutdown_count) > 0) + { + if ((CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_standby) && (!CNeutrinoApp::getInstance ()->recordingstatus)) + { + if (shutdown_cnt > 0 ) + { + shutdown_cnt = shutdown_cnt - 1; + printf("[SHTDCNT] shutdown counter: %d sec to shutdown\n", shutdown_cnt); + } + else + { + // send shutdown message + printf("[SHTDCNT] shutdown counter send NeutrinoMessages::SHUTDOWN\n"); + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::SHUTDOWN, 0); + } + } + else + { + // reset counter + shutdown_cnt = atoi(g_settings.shutdown_count) * 60; + } + } +} diff --git a/src/driver/shutdown_count.h b/src/driver/shutdown_count.h new file mode 100644 index 000000000..2bf7efee3 --- /dev/null +++ b/src/driver/shutdown_count.h @@ -0,0 +1,53 @@ +/* + Shutdown Counter - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __shutdown_count__ +#define __shutdown_count__ + +#include +#include + +class SHTDCNT +{ + private: + + pthread_t thrTime; + unsigned int shutdown_cnt; + CConfigFile configfile; + + void shutdown_counter(); + SHTDCNT(); + + static void* TimeThread(void*); + + public: + + void setlcdparameter(void); + + static SHTDCNT* getInstance(); + void init(); +}; + +#endif diff --git a/src/driver/slotbuffer.c b/src/driver/slotbuffer.c new file mode 100644 index 000000000..3a59da1b9 --- /dev/null +++ b/src/driver/slotbuffer.c @@ -0,0 +1,107 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/slotbuffer.c,v 1.1 2004/06/03 09:51:54 thegoodguy Exp $ + * + * (C) 2004 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Note: this slotbuffer "wastes" one slot */ + +#include "slotbuffer.h" +#include + +slotbuffer_t * slotbuffer_construct(const size_t size) +{ + slotbuffer_t * slotbuffer; + + slotbuffer = malloc(sizeof(slotbuffer_t)); + + slotbuffer->buffer = malloc(size); + slotbuffer->size = size; + slotbuffer->read_pointer = 0; + slotbuffer->write_pointer = 0; + + return slotbuffer; +} + +void slotbuffer_destruct(slotbuffer_t * const slotbuffer) +{ + free(slotbuffer->buffer); + free(slotbuffer); +} + +size_t slotbuffer_remaining_read_space(slotbuffer_t * const slotbuffer) +{ + ssize_t delta; + + delta = slotbuffer->write_pointer; + delta -= slotbuffer->read_pointer; + + return (delta >= 0) ? delta : (delta + slotbuffer->size); +} + +size_t slotbuffer_remaining_write_space(slotbuffer_t * const slotbuffer) +{ + ssize_t delta; + + delta = slotbuffer->read_pointer; + delta -= slotbuffer->write_pointer; + + return ((delta > 0) ? delta : (delta + slotbuffer->size)) - 1; +} + +size_t slotbuffer_remaining_continuous_read_space(slotbuffer_t * const slotbuffer) +{ + ssize_t delta; + size_t read_pointer; + + delta = slotbuffer->write_pointer; + read_pointer = slotbuffer->read_pointer; + delta -= read_pointer; + + return (delta >= 0) ? delta : (slotbuffer->size - read_pointer); +} + +size_t slotbuffer_remaining_continuous_write_space(slotbuffer_t * const slotbuffer) +{ + ssize_t delta; + size_t write_pointer; + + delta = slotbuffer->read_pointer; + + if (delta == 0) + return (slotbuffer->size - slotbuffer->write_pointer - 1); + + write_pointer = slotbuffer->write_pointer; + delta -= write_pointer; + + return (delta > 0) ? (delta - 1) : (slotbuffer->size - write_pointer); +} + +void slotbuffer_advance_read_pointer(slotbuffer_t * const slotbuffer, const size_t delta) +{ + slotbuffer->read_pointer += delta; + if (slotbuffer->read_pointer >= slotbuffer->size) + slotbuffer->read_pointer = 0; +} + +void slotbuffer_advance_write_pointer(slotbuffer_t * const slotbuffer, const size_t delta) +{ + slotbuffer->write_pointer += delta; + if (slotbuffer->write_pointer >= slotbuffer->size) + slotbuffer->write_pointer = 0; +} diff --git a/src/driver/slotbuffer.h b/src/driver/slotbuffer.h new file mode 100644 index 000000000..07266f62d --- /dev/null +++ b/src/driver/slotbuffer.h @@ -0,0 +1,44 @@ +#ifndef __slotbuffer_h__ +#define __slotbuffer_h__ + +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/driver/slotbuffer.h,v 1.1 2004/06/03 09:51:54 thegoodguy Exp $ + * + * (C) 2004 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +typedef struct +{ + unsigned char * buffer; + size_t size; + volatile size_t read_pointer; + volatile size_t write_pointer; +} slotbuffer_t; + +slotbuffer_t * slotbuffer_construct (size_t const size ); +void slotbuffer_destruct (slotbuffer_t * const slotbuffer ); +size_t slotbuffer_remaining_read_space (slotbuffer_t * const slotbuffer ); +size_t slotbuffer_remaining_write_space (slotbuffer_t * const slotbuffer ); +size_t slotbuffer_remaining_continuous_read_space (slotbuffer_t * const slotbuffer ); +size_t slotbuffer_remaining_continuous_write_space(slotbuffer_t * const slotbuffer ); +void slotbuffer_advance_read_pointer (slotbuffer_t * const slotbuffer, size_t const delta); +void slotbuffer_advance_write_pointer (slotbuffer_t * const slotbuffer, size_t const delta); + +#endif diff --git a/src/driver/stream2file.cpp b/src/driver/stream2file.cpp new file mode 100644 index 000000000..75074e962 --- /dev/null +++ b/src/driver/stream2file.cpp @@ -0,0 +1,185 @@ +/* + * $Id: stream2file.cpp,v 1.19 2005/01/12 20:40:22 chakazulu Exp $ + * + * streaming to file/disc + * + * Copyright (C) 2004 Axel Buehning , + * thegoodguy + * + * based on code which is + * Copyright (C) 2001 TripleDES + * Copyright (C) 2000, 2001 Marcus Metzler + * Copyright (C) 2002 Andreas Oberritter + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern "C" { +#include +} + +static cRecord * record = NULL; +extern CZapitChannel *channel; +extern CCam *cam0; + +extern bool autoshift; +extern bool autoshift_delete; + +#define MAXPIDS 64 +#define FILENAMEBUFFERSIZE 1024 + +static stream2file_status_t exit_flag = STREAM2FILE_STATUS_IDLE; + +char rec_filename[FILENAMEBUFFERSIZE]; + +stream2file_error_msg_t start_recording(const char * const filename, + const char * const info, const unsigned short vpid, const unsigned short * const pids, + const unsigned int numpids) +{ + int fd; + char buf[FILENAMEBUFFERSIZE]; + struct statfs s; + + if(autoshift) + sprintf(rec_filename, "%s_temp", filename); + else + sprintf(rec_filename, "%s", filename); + + // write stream information (should wakeup the disk from standby, too) + sprintf(buf, "%s.xml", rec_filename); + + char * dir = strdup(buf); + int ret = statfs(dirname(dir), &s); + free(dir); + if((ret != 0) || (s.f_type == 0x72b6) || (s.f_type == 0x24051905)) { + return STREAM2FILE_INVALID_DIRECTORY; + } + if ((fd = open(buf, O_SYNC | O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) { + write(fd, info, strlen(info)); + fdatasync(fd); + close(fd); + } else { + return STREAM2FILE_INVALID_DIRECTORY; + } + + exit_flag = STREAM2FILE_STATUS_RUNNING; + + sprintf(buf, "%s.ts", rec_filename); + + printf("Record start: file %s vpid %x apid %x\n", buf, vpid, pids[0]); + + fd = open(buf, O_CREAT | O_RDWR | O_LARGEFILE | O_TRUNC , S_IRWXO | S_IRWXG | S_IRWXU); + if(fd < 0) { + perror(buf); + return STREAM2FILE_INVALID_DIRECTORY; + } + genpsi(fd); + + if(!record) + record = new cRecord(2); + + record->Open(numpids); + + if(!record->Start(fd, (unsigned short ) vpid, (unsigned short *) pids, numpids)) { + record->Stop(); + delete record; + record = NULL; + return STREAM2FILE_INVALID_DIRECTORY; + } + + if(channel) + cam0->setCaPmt(channel->getCaPmt(), 0, 5, true); // demux 0 + 2, update + + CVFD::getInstance()->ShowIcon(VFD_ICON_CAM1, true); + + return STREAM2FILE_OK; +} + +stream2file_error_msg_t stop_recording(const char * const info) +{ + char buf[FILENAMEBUFFERSIZE]; + char buf1[FILENAMEBUFFERSIZE]; + int fd; + stream2file_error_msg_t ret; + + //if(!autoshift || autoshift_delete) + { + sprintf(buf, "%s.xml", rec_filename); + if ((fd = open(buf, O_SYNC | O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) { + write(fd, info, strlen(info)); + fdatasync(fd); + close(fd); + } + } + + if(record) { + record->Stop(); + delete record; + record = NULL; + } + + if (exit_flag == STREAM2FILE_STATUS_RUNNING) { + exit_flag = STREAM2FILE_STATUS_IDLE; + ret = STREAM2FILE_OK; + } + else + ret = STREAM2FILE_RECORDING_THREADS_FAILED; + + if((autoshift && g_settings.auto_delete) || autoshift_delete) { + sprintf(buf, "rm -f %s.ts &", rec_filename); + sprintf(buf1, "%s.xml", rec_filename); + autoshift_delete = false; + system(buf); + unlink(buf1); + } + CVFD::getInstance()->ShowIcon(VFD_ICON_CAM1, false); + + rec_filename[0] = 0; + return ret; +} + diff --git a/src/driver/stream2file.h b/src/driver/stream2file.h new file mode 100644 index 000000000..0708a11aa --- /dev/null +++ b/src/driver/stream2file.h @@ -0,0 +1,59 @@ +/* + * $Id: stream2file.h,v 1.10 2005/01/12 20:40:22 chakazulu Exp $ + * + * (C) 2004 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __stream2file_h__ +#define __stream2file_h__ + +enum stream2file_error_msg_t +{ + STREAM2FILE_OK = 0, + STREAM2FILE_BUSY = -1, + STREAM2FILE_INVALID_DIRECTORY = -2, + STREAM2FILE_INVALID_PID = -3, + STREAM2FILE_PES_FILTER_FAILURE = -4, + STREAM2FILE_DVR_OPEN_FAILURE = -5, + STREAM2FILE_RECORDING_THREADS_FAILED = -6, +}; + +enum stream2file_status_t +{ + STREAM2FILE_STATUS_RUNNING = 0, + STREAM2FILE_STATUS_IDLE = 1, + STREAM2FILE_STATUS_BUFFER_OVERFLOW = -1, + STREAM2FILE_STATUS_WRITE_OPEN_FAILURE = -2, + STREAM2FILE_STATUS_WRITE_FAILURE = -3, + STREAM2FILE_STATUS_READ_FAILURE = -4 +}; + +struct stream2file_status2_t +{ + stream2file_status_t status; + char dir[100]; +}; + +stream2file_error_msg_t start_recording(const char * const filename, + const char * const info, + const unsigned short vpid, + const unsigned short * const apids, + const unsigned int numpids); +stream2file_error_msg_t stop_recording(const char * const info); + +#endif diff --git a/src/driver/streamts.cpp b/src/driver/streamts.cpp new file mode 100644 index 000000000..6382e6280 --- /dev/null +++ b/src/driver/streamts.cpp @@ -0,0 +1,487 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +#include + +#include +#include +#include + +extern CZapitChannel *channel; +extern CCam *cam0; + +#define TS_SIZE 188 +//#define IN_SIZE (2048 * TS_SIZE) +#define IN_SIZE (TS_SIZE * 362) + +#define DMX_BUFFER_SIZE (3008 * 62) + +/* maximum number of pes pids */ +#define MAXPIDS 64 + +/* tcp packet data size */ +//#define PACKET_SIZE 1448 +#define PACKET_SIZE 7*TS_SIZE + +//unsigned char * buf; + +int demuxfd[MAXPIDS]; + +static unsigned char exit_flag = 0; +static unsigned int writebuf_size = 0; +static unsigned char writebuf[PACKET_SIZE]; + +static int +sync_byte_offset (const unsigned char * buf, const unsigned int len) +{ + + unsigned int i; + + for (i = 0; i < len; i++) + if (buf[i] == 0x47) + return i; + + return -1; +} + +void packet_stdout (int fd, unsigned char * buf, int count, void * p) +{ + + unsigned int size; + unsigned char * bp; + ssize_t written; + +//printf("packet_stdout count %d\n", count); + /* ensure, that there is always at least one complete + * packet inside of the send buffer */ + while (writebuf_size + count >= PACKET_SIZE) { + + /* how many bytes are to be sent from the input buffer? */ + size = PACKET_SIZE - writebuf_size; + + /* send buffer is not empty, so copy from + input buffer to get a complete packet */ + if (writebuf_size) { + memcpy(writebuf + writebuf_size, buf, size); + bp = writebuf; + } + + /* if send buffer is empty, then do not memcopy, + but send directly from input buffer */ + else { + bp = buf; + } + + /* write the packet, count the amount of really written bytes */ + written = write(fd, bp, PACKET_SIZE); + + /* exit on error */ + if (written == -1) { + perror("write"); + exit_flag = 1; + return; + } + + /* if the packet could not be written completely, then + * how many bytes must be stored in the send buffer + * until the next packet is to be sent? */ + writebuf_size = PACKET_SIZE - written; + + /* move all bytes of the packet which were not sent + * to the beginning of the send buffer */ + if (writebuf_size) + memmove(writebuf, bp + written, writebuf_size); + + /* * advance in the input buffer */ + buf += size; + + /* * decrease the todo size */ + count -= size; + } + + /* if there are still some bytes left in the input buffer, + * then store them in the send buffer and increase send + * buffer size */ + if (count) { + memcpy(writebuf + writebuf_size, buf, count); + writebuf_size += count; + } +} + +int open_incoming_port (int port) +{ + struct sockaddr_in socketAddr; + int socketOptActive = 1; + int handle; + + if (!port) + return -1; + + if ((handle = socket (AF_INET, SOCK_STREAM, 0)) < 0) + { + fprintf (stderr, "network port %u open: ", port); + perror ("socket"); + return -1; + } + + if (setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const void *)&socketOptActive, sizeof (int)) < 0) + { + fprintf (stderr, "network port %u open: error setsockopt\n", port); + close (handle); + return -1; + } + + socketAddr.sin_family = AF_INET; + socketAddr.sin_port = htons (port); + socketAddr.sin_addr.s_addr = htonl (INADDR_ANY); + + if (bind (handle, (struct sockaddr *) &socketAddr, sizeof (socketAddr)) < 0) + { + fprintf (stderr, "network port %u open: ", port); + perror ("bind"); + close (handle); + return -1; + } + + if (listen (handle, 5) < 0) + { + fprintf (stderr, "network port %u open: ", port); + perror ("listen"); + close (handle); + return -1; + } + return handle; +} + +void * streamts_live_thread(void *data); +int streamts_stop; + +void streamts_main_thread(void *data) +{ + struct sockaddr_in servaddr; + int clilen; + + struct pollfd pfd[128]; + int poll_cnt, tcnt; + int listenfd; + int connfd = -1; + int pollres; + int i; + pthread_t st = 0; + + printf("Starting STREAM thread keeper, tid %ld\n", syscall(__NR_gettid)); + + listenfd = open_incoming_port(31339); + if(listenfd < 0) { + printf("Open incoming port failed\n"); + return; + } + printf("listenfd %d\n", listenfd); + + clilen = sizeof (servaddr); + pfd[0].fd = listenfd; + pfd[0].events = (POLLIN | POLLPRI); + pfd[0].revents = 0; + tcnt = 1; + streamts_stop = 0; + + while (!streamts_stop) { + poll_cnt = tcnt; +//printf("polling, count= %d\n", poll_cnt); + pollres = poll (pfd, poll_cnt, 1000); + if (pollres < 0) { + perror("poll"); + continue; + } + if(pollres == 0) + continue; + for (i = poll_cnt - 1; i >= 0; i--) { + if (pfd[i].revents & (POLLIN | POLLPRI | POLLHUP | POLLRDHUP)) { + printf("fd %d has events %x\n", pfd[i].fd, pfd[i].revents); + if (pfd[i].fd == listenfd) { + if(connfd >= 0) + close(connfd); + connfd = accept (listenfd, (struct sockaddr *) &servaddr, (socklen_t *) & clilen); + printf("new connection, fd %d\n", connfd); + if(connfd < 0) { + perror("accept"); + continue; + } + if(st != 0) { + printf("New connection, stopping stream thread\n"); + exit_flag = 1; + pthread_join(st, NULL); + tcnt --; + } + pfd[tcnt].fd = connfd; + pfd[tcnt].events = POLLRDHUP | POLLHUP; + pfd[tcnt].revents = 0; + tcnt++; + exit_flag = 0; + pthread_create (&st, NULL, streamts_live_thread, (void *) connfd); + } else { + if (pfd[i].revents & (POLLHUP | POLLRDHUP)) { + connfd = -1; + printf("Client disconnected, stopping stream thread\n"); + exit_flag = 1; + if(st) + pthread_join(st, NULL); + st = 0; + tcnt --; + } + } + } + } + } + printf("Stopping STREAM thread keeper\n"); + close(listenfd); + if(st != 0) { + printf("Stopping stream thread\n"); + exit_flag = 1; + pthread_join(st, NULL); + close(connfd); + } + return; +} + +void * streamts_live_thread(void *data) +{ + unsigned char * buf; + int pid; + int pids[MAXPIDS]; + char cbuf[512]; + char *bp; + int fd = (int) data; + FILE * fp; + unsigned char demuxfd_count = 0; + + printf("Starting LIVE STREAM thread, fd %d\n", fd); + fp = fdopen(fd, "r+"); + if(fp == NULL) { + perror("fdopen"); + return 0; + } + + writebuf_size = 0; + + cbuf[0] = 0; + bp = &cbuf[0]; + + /* read one line */ + while (bp - &cbuf[0] < (int) sizeof(cbuf)) { + unsigned char c; + int res = read(fd, &c, 1); + if(res < 0) { + perror("read"); + return 0; + } + if ((*bp++ = c) == '\n') + break; + } + + *bp++ = 0; + bp = &cbuf[0]; + + printf("stream: got %s\n", cbuf); + + /* send response to http client */ + if (!strncmp(cbuf, "GET /", 5)) { + fprintf(fp, "HTTP/1.1 200 OK\r\nServer: streamts (%s)\r\n\r\n", "ts" /*&argv[1][1]*/); + fflush(fp); + bp += 5; + } else { + printf("Received garbage\n"); + return 0; + } + + /* parse stdin / url path, start dmx filters */ + do { + int res = sscanf(bp, "%x", &pid); + if(res == 1) { + printf("New pid: 0x%x\n", pid); + pids[demuxfd_count++] = pid; + } + } + while ((bp = strchr(bp, ',')) && (bp++) && (demuxfd_count < MAXPIDS)); + + if(demuxfd_count == 0) { + printf("No pids!\n"); + return 0; + } + + buf = (unsigned char *) malloc(IN_SIZE); + if (buf == NULL) { + perror("malloc"); + return 0; + } + + cDemux * dmx = new cDemux(1); + + dmx->Open(DMX_TP_CHANNEL, NULL, DMX_BUFFER_SIZE); + + dmx->pesFilter(pids[0]); + for(int i = 1; i < demuxfd_count; i++) + dmx->addPid(pids[i]); + + dmx->Start(); + + if(channel) + cam0->setCaPmt(channel->getCaPmt(), 0, 3, true); // demux 0 + 1, update + + size_t pos; + ssize_t r; + ssize_t todo; + int offset; + + while (!exit_flag) { + todo = IN_SIZE; + pos = 0; + + while ((!exit_flag) && (todo)) { + r = dmx->Read(buf+pos, todo, 100); + if (r > 0) { +//printf("Read: %d\n", r); + pos += r; + todo -= r; + } else + usleep(1000); + } + if(!exit_flag) { + //packet_stdout(fd, buf, IN_SIZE, NULL); + /* make sure to start with a ts header */ + offset = sync_byte_offset(buf, IN_SIZE); + + if (offset == -1) + continue; + + packet_stdout(fd, buf + offset, IN_SIZE - offset, NULL); + } + } + + printf("Exiting LIVE STREAM thread, fd %d\n", fd); + if(channel) + cam0->setCaPmt(channel->getCaPmt(), 0, 1, true); // demux 0, update + + delete dmx; + free(buf); + close(fd); + return 0; +} + +void streamts_file_thread(void *data) +{ + int dvrfd; + unsigned char * buf; + char cbuf[512]; + char *bp; + unsigned char mode = 0; + char tsfile[IN_SIZE]; + int tsfilelen = 0; + int fileslice = 0; + int i = 0; + int fd = (int) data; + + buf = (unsigned char *) malloc(IN_SIZE); + + if (buf == NULL) { + perror("malloc"); + return; + } + + bp = &cbuf[0]; + + /* read one line */ + while (bp - &cbuf[0] < IN_SIZE) { + unsigned char c; + read(fd, &c, 1); + if ((*bp++ = c) == '\n') + break; + } + + *bp++ = 0; + bp = &cbuf[0]; + + /* send response to http client */ + if (!strncmp(cbuf, "GET /", 5)) { + printf("HTTP/1.1 200 OK\r\nServer: streamts (%s)\r\n\r\n", "ts" /*&argv[1][1]*/); + fflush(stdout); + bp += 5; + } + + /* ts filename */ + int j = 0; + i = 0; + while (i < (int) strlen(bp) - 3) + { + if ((bp[i] == '.') && (bp[i + 1] == 't') && (bp[i + 2] == 's')) + { + tsfile[j] = bp[i]; + tsfile[j + 1] = bp[i + 1]; + tsfile[j + 2] = bp[i + 2]; + tsfile[j + 3] = '\0'; + break; + } + else + if ((bp[i] == '%') && (bp[i + 1] == '2') && (bp[i + 2] == '0')) + { + tsfile[j++] = ' '; + i += 3; + } + else + tsfile[j++] = bp[i++]; + } + tsfilelen = strlen(tsfile); + /* open ts file */ + if ((dvrfd = open(tsfile, O_RDONLY)) < 0) { + free(buf); + return; + } + + size_t pos; + ssize_t r; + + while (!exit_flag) { + /* always read IN_SIZE bytes */ + for (pos = 0; pos < IN_SIZE; pos += r) { + r = read(dvrfd, buf + pos, IN_SIZE - pos); + if (r == -1) { + /* Error */ + exit_flag = 1; + break; + } else if (r == 0) { + /* End of file */ + if (mode == 3) { + close(dvrfd); + sprintf(&tsfile[tsfilelen], ".%03d", ++fileslice); + dvrfd = open(tsfile, O_RDONLY); + } + if ((dvrfd == -1) || (mode != 3)) { + exit_flag = 1; + break; + } + } + } + packet_stdout(fd, buf, pos, NULL); + } + close(dvrfd); + free(buf); + + return; +} diff --git a/src/driver/vcrcontrol.cpp b/src/driver/vcrcontrol.cpp new file mode 100644 index 000000000..e9223bae7 --- /dev/null +++ b/src/driver/vcrcontrol.cpp @@ -0,0 +1,941 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ +int was_record = 0; +extern bool autoshift; +extern bool autoshift_delete; + +#define SA struct sockaddr +#define SAI struct sockaddr_in +extern "C" { +#include +} +CMovieInfo * g_cMovieInfo; +MI_MOVIE_INFO * g_movieInfo; +t_channel_id rec_channel_id; +int safe_mkdir(char * path); + +static CVCRControl vcrControl; + +CVCRControl * CVCRControl::getInstance() +{ + return &vcrControl; +} + +//------------------------------------------------------------------------- +CVCRControl::CVCRControl() +{ + Device = NULL; +} + +//------------------------------------------------------------------------- +CVCRControl::~CVCRControl() +{ + unregisterDevice(); +} + +//------------------------------------------------------------------------- +void CVCRControl::unregisterDevice() +{ + if (Device) + { + delete Device; + Device = NULL; + } +} + +//------------------------------------------------------------------------- +void CVCRControl::registerDevice(CDevice * const device) +{ + unregisterDevice(); + + Device = device; +} + +//------------------------------------------------------------------------- +bool CVCRControl::Record(const CTimerd::RecordingInfo * const eventinfo) +{ + int mode = g_Zapit->isChannelTVChannel(eventinfo->channel_id) ? NeutrinoMessages::mode_tv : NeutrinoMessages::mode_radio; + + return Device->Record(eventinfo->channel_id, mode, eventinfo->epgID, eventinfo->epgTitle, eventinfo->apids, eventinfo->epg_starttime); +} + +//------------------------------------------------------------------------- +void CVCRControl::CDevice::getAPIDs(const unsigned char ap, APIDList & apid_list) +{ +// (strstr(g_RemoteControl->current_PIDs.APIDs[i].desc, "(AC3)") == NULL)) + unsigned char apids=ap; + if (apids == TIMERD_APIDS_CONF) + apids = g_settings.recording_audio_pids_default; + apid_list.clear(); + CZapitClient::responseGetPIDs allpids; + g_Zapit->getPIDS(allpids); + // assume smallest apid ist std apid + if (apids & TIMERD_APIDS_STD) + { + uint32_t apid_min=UINT_MAX; + uint32_t apid_min_idx=0; + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].pid < apid_min && !allpids.APIDs[i].is_ac3) + { + apid_min = allpids.APIDs[i].pid; + apid_min_idx = i; + } + } + if (apid_min != UINT_MAX) + { + APIDDesc a = {apid_min, apid_min_idx, false}; + apid_list.push_back(a); + } + } + if (apids & TIMERD_APIDS_ALT) + { + uint32_t apid_min=UINT_MAX; + uint32_t apid_min_idx=0; + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].pid < apid_min && !allpids.APIDs[i].is_ac3) + { + apid_min = allpids.APIDs[i].pid; + apid_min_idx = i; + } + } + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].pid != apid_min && !allpids.APIDs[i].is_ac3) + { + APIDDesc a = {allpids.APIDs[i].pid, i, false}; + apid_list.push_back(a); + } + } + } + if (apids & TIMERD_APIDS_AC3) + { + bool ac3_found=false; + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].is_ac3) + { + APIDDesc a = {allpids.APIDs[i].pid, i, true}; + apid_list.push_back(a); + ac3_found=true; + } + } + // add non ac3 apid if ac3 not found + if (!(apids & TIMERD_APIDS_STD) && !ac3_found) + { + uint32_t apid_min=UINT_MAX; + uint32_t apid_min_idx=0; + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].pid < apid_min && !allpids.APIDs[i].is_ac3) + { + apid_min = allpids.APIDs[i].pid; + apid_min_idx = i; + } + } + if (apid_min != UINT_MAX) + { + APIDDesc a = {apid_min, apid_min_idx, false}; + apid_list.push_back(a); + } + } + } + // no apid selected use standard + if (apid_list.empty() && !allpids.APIDs.empty()) + { + uint32_t apid_min=UINT_MAX; + uint32_t apid_min_idx=0; + for(unsigned int i = 0; i < allpids.APIDs.size(); i++) + { + if (allpids.APIDs[i].pid < apid_min && !allpids.APIDs[i].is_ac3) + { + apid_min = allpids.APIDs[i].pid; + apid_min_idx = i; + } + } + if (apid_min != UINT_MAX) + { + APIDDesc a = {apid_min, apid_min_idx, false}; + apid_list.push_back(a); + } + for(APIDList::iterator it = apid_list.begin(); it != apid_list.end(); it++) + printf("Record APID 0x%X %d\n",it->apid, it->ac3); + + } +} + +//------------------------------------------------------------------------- +bool CVCRControl::CVCRDevice::Stop() +{ + deviceState = CMD_VCR_STOP; + + if(last_mode != NeutrinoMessages::mode_scart) + { + g_RCInput->postMsg( NeutrinoMessages::VCR_OFF, 0 ); + g_RCInput->postMsg( NeutrinoMessages::CHANGEMODE , last_mode); + } + return true; +} + +//------------------------------------------------------------------------- +bool CVCRControl::CVCRDevice::Record(const t_channel_id channel_id, int mode, const event_id_t epgid, const std::string& epgTitle, unsigned char apids, const time_t epg_time) +{ + printf("Record channel_id: " PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS " epg: %llx, apids 0x%X mode \n", + channel_id, epgid, apids); + // leave menu (if in any) + g_RCInput->postMsg( CRCInput::RC_timeout, 0 ); + + last_mode = CNeutrinoApp::getInstance()->getMode(); + if(mode != last_mode) { + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , mode | NeutrinoMessages::norezap ); + } + + if(channel_id != 0) // wenn ein channel angegeben ist + { + if(g_Zapit->getCurrentServiceID() != channel_id) // und momentan noch nicht getuned ist + { + g_Zapit->zapTo_serviceID(channel_id); // dann umschalten + } + } + if(! (apids & TIMERD_APIDS_STD)) // nicht std apid + { + APIDList apid_list; + getAPIDs(apids,apid_list); + if(!apid_list.empty()) + { + if(!apid_list.begin()->ac3) + g_Zapit->setAudioChannel(apid_list.begin()->index); + else + g_Zapit->setAudioChannel(0); //sonst apid 0, also auf jeden fall ac3 aus ! + } + else + g_Zapit->setAudioChannel(0); //sonst apid 0, also auf jeden fall ac3 aus ! + } + else + g_Zapit->setAudioChannel(0); //sonst apid 0, also auf jeden fall ac3 aus ! + + if(SwitchToScart) + { + // Auf Scart schalten + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::VCR_ON, 0 ); + // Das ganze nochmal in die queue, da obiges RC_timeout erst in der naechsten ev. loop ausgeführt wird + // und dann das menu widget das display falsch rücksetzt + g_RCInput->postMsg( NeutrinoMessages::VCR_ON, 0 ); + } + + deviceState = CMD_VCR_RECORD; + // Send IR + return true; +} + +//------------------------------------------------------------------------- +bool CVCRControl::CVCRDevice::Pause() +{ + return true; +} + +//------------------------------------------------------------------------- +bool CVCRControl::CVCRDevice::Resume() +{ + return true; +} + +//------------------------------------------------------------------------- +void CVCRControl::CFileAndServerDevice::RestoreNeutrino(void) +{ +//printf("RestoreNeutrino\n");fflush(stdout); + /* after this zapit send EVT_RECORDMODE_DEACTIVATED, so neutrino getting NeutrinoMessages::EVT_RECORDMODE */ + g_Zapit->setRecordMode( false ); + if (!g_Zapit->isPlayBackActive() && (CNeutrinoApp::getInstance()->getMode() != NeutrinoMessages::mode_standby)) + g_Zapit->startPlayBack(); + // alten mode wieder herstellen (ausser wen zwischenzeitlich auf oder aus sb geschalten wurde) + if(CNeutrinoApp::getInstance()->getMode() != last_mode && + CNeutrinoApp::getInstance()->getMode() != NeutrinoMessages::mode_standby && + last_mode != NeutrinoMessages::mode_standby) + if(!autoshift) g_RCInput->postMsg( NeutrinoMessages::CHANGEMODE , last_mode); + + if(last_mode == NeutrinoMessages::mode_standby && + CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_standby ) + { + //Wenn vorher und jetzt standby, dann die zapit wieder auf sb schalten + //g_Zapit->setStandby(true); + //was_record = 1; + } + if((last_mode != NeutrinoMessages::mode_standby) && StopSectionsd) + g_Sectionsd->setPauseScanning(false); +} + +void CVCRControl::CFileAndServerDevice::CutBackNeutrino(const t_channel_id channel_id, const int mode) +{ + rec_channel_id = channel_id; +//printf("CutBackNeutrino\n");fflush(stdout); + g_Zapit->setStandby(false); + last_mode = CNeutrinoApp::getInstance()->getMode(); + if(last_mode == NeutrinoMessages::mode_standby) + was_record = 1; + if (channel_id != 0) { + if (mode != last_mode && (last_mode != NeutrinoMessages::mode_standby || mode != CNeutrinoApp::getInstance()->getLastMode())) { + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , mode | NeutrinoMessages::norezap ); + // Wenn wir im Standby waren, dann brauchen wir fürs streamen nicht aufwachen... + if(last_mode == NeutrinoMessages::mode_standby) + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , NeutrinoMessages::mode_standby); + } + if(g_Zapit->getCurrentServiceID() != channel_id) { + g_Zapit->zapTo_serviceID(channel_id); + } + } + if(StopSectionsd) // wenn sectionsd gestoppt werden soll + g_Sectionsd->setPauseScanning(true); // sectionsd stoppen + + /* after this zapit send EVT_RECORDMODE_ACTIVATED, so neutrino getting NeutrinoMessages::EVT_RECORDMODE */ + g_Zapit->setRecordMode( true ); + if((last_mode == NeutrinoMessages::mode_standby) || (StopPlayBack && g_Zapit->isPlayBackActive())) + g_Zapit->stopPlayBack(); +} + +bool sectionsd_getEPGidShort(event_id_t epgID, CShortEPGData * epgdata); +bool sectionsd_getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata); + +std::string CVCRControl::CFileAndServerDevice::getCommandString(const CVCRCommand command, const t_channel_id channel_id, const event_id_t epgid, const std::string& epgTitle, unsigned char apids) +{ + char tmp[40]; + std::string apids_selected; + const char * extCommand; + std::string info1, info2; + + std::string extMessage = "\n\n\n\t\n" + "\t\t"; + + CZapitClient::responseGetPIDs pids; + g_Zapit->getPIDS (pids); + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo (); + + APIDList apid_list; + getAPIDs(apids,apid_list); + apids_selected=""; + for(APIDList::iterator it = apid_list.begin(); it != apid_list.end(); it++) + { + if(it != apid_list.begin()) + apids_selected += " "; + sprintf(tmp, "%u", it->apid); + apids_selected += tmp; + } + + std::string tmpstring = g_Zapit->getChannelName(channel_id); + if (tmpstring.empty()) + extMessage += "unknown"; + else + extMessage += ZapitTools::UTF8_to_UTF8XML(tmpstring.c_str()); + + extMessage += "\n\t\t"; + + tmpstring = "not available"; + if (epgid != 0) + { + CShortEPGData epgdata; + //if (g_Sectionsd->getEPGidShort(epgid, &epgdata)) { + if(sectionsd_getEPGidShort(epgid, &epgdata)) { +//#warning fixme sectionsd should deliver data in UTF-8 format + tmpstring = epgdata.title; + info1 = epgdata.info1; + info2 = epgdata.info2; + } + } else if (!epgTitle.empty()) { + tmpstring = epgTitle; + } + extMessage += ZapitTools::UTF8_to_UTF8XML(tmpstring.c_str()); + + extMessage += "\n\t\t"; + + sprintf(tmp, PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS, channel_id); + extMessage += tmp; + + extMessage += "\n\t\t"; + extMessage += ZapitTools::UTF8_to_UTF8XML(info1.c_str()); + extMessage += "\n\t\t"; + extMessage += ZapitTools::UTF8_to_UTF8XML(info2.c_str()); + extMessage += "\n\t\t"; + sprintf(tmp, "%llu", epgid); + extMessage += tmp; + extMessage += "\n\t\t"; + sprintf(tmp, "%d", g_Zapit->getMode()); + extMessage += tmp; + extMessage += "\n\t\t"; + sprintf(tmp, "%u", si.vpid); + extMessage += tmp; + extMessage += "\n\t\t\n"; + // super hack :-), der einfachste weg an die apid descriptions ranzukommen + g_RemoteControl->current_PIDs = pids; + g_RemoteControl->processAPIDnames(); + + for(unsigned int i= 0; i< pids.APIDs.size(); i++) + { + extMessage += "\t\t\t\n" + "\t\t"; + sprintf(tmp, "%u", si.vtxtpid); + extMessage += tmp; + extMessage += + "\n" + "\t\n" + "\n"; + + return extMessage; +} + +bool CVCRControl::CFileDevice::Stop() +{ + std::string extMessage = " "; + time_t end_time = time(0); +//printf("[direct] Stop recording, g_movieInfo %lx\n", g_movieInfo); fflush(stdout); +//FIXME why not save info if shift ? + //if(!autoshift || autoshift_delete) + { + // g_movieInfo->length = (end_time - start_time) / 60; + g_movieInfo->length = (int) round((double) (end_time - start_time) / (double) 60); +//printf("[direct] stop recording 1\n"); fflush(stdout); + g_cMovieInfo->encodeMovieInfoXml(&extMessage, g_movieInfo); +//printf("[direct] stop recording 2\n"); fflush(stdout); + } + bool return_value = (::stop_recording(extMessage.c_str()) == STREAM2FILE_OK); +//printf("[direct] stop recording 3\n"); fflush(stdout); + + RestoreNeutrino(); + + deviceState = CMD_VCR_STOP; + + g_movieInfo->audioPids.clear(); + delete g_movieInfo; + g_movieInfo = NULL; + delete g_cMovieInfo; + g_cMovieInfo = NULL; + + return return_value; +} + +std::string ext_channel_name; +bool CVCRControl::CFileDevice::Record(const t_channel_id channel_id, int mode, const event_id_t epgid, const std::string& epgTitle, unsigned char apids, const time_t epg_time) +{ +printf("Record channel_id: " PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS " epg: %llx, apids 0x%X mode %d\n", + channel_id, epgid, apids, mode); + + CutBackNeutrino(channel_id, mode); + +#define MAXPIDS 64 + unsigned short pids[MAXPIDS]; + unsigned int numpids; + unsigned int pos; + + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo(); + numpids = 0; + + if (si.vpid != 0) + transfer_pids(si.vpid, si.vtype ? EN_TYPE_AVC : EN_TYPE_VIDEO, 0); + + APIDList apid_list; + getAPIDs(apids,apid_list); + for(APIDList::iterator it = apid_list.begin(); it != apid_list.end(); it++) { + pids[numpids++] = it->apid; + transfer_pids(it->apid, EN_TYPE_AUDIO, it->ac3 ? 1 : 0); + } +#if 0 // FIXME : why this needed ? + if(!apid_list.empty()) + g_Zapit->setAudioChannel(apid_list.begin()->index); +#endif + CZapitClient::responseGetPIDs allpids; + g_Zapit->getPIDS(allpids); + + if ((StreamVTxtPid) && (si.vtxtpid != 0)) { + pids[numpids++] = si.vtxtpid; + } + if ((StreamPmtPid) && (si.pmtpid != 0)) { + pids[numpids++] = si.pmtpid; + } + char filename[512]; // UTF-8 + + // Create filename for recording + pos = Directory.size(); + strcpy(filename, Directory.c_str()); + + if ((pos == 0) || (filename[pos - 1] != '/')) { + filename[pos] = '/'; + pos++; + filename[pos] = '\0'; + } + pos = strlen(filename); +#if 0 + time_t t = time(NULL); + strftime(&(filename[pos]), sizeof(filename) - pos - 1, "%Y%m%d_%H%M%S", localtime(&t)); + strcat(filename, "_"); + pos = strlen(filename); +#endif + ext_channel_name = g_Zapit->getChannelName(channel_id); + if (!(ext_channel_name.empty())) + { + strcpy(&(filename[pos]), UTF8_TO_FILESYSTEM_ENCODING(ext_channel_name.c_str())); + char * p_act = &(filename[pos]); + do { + p_act += strcspn(p_act, "/ \"%&-\t`'´!,:;"); + if (*p_act) { + *p_act++ = '_'; + } + } while (*p_act); + if (!autoshift && g_settings.recording_save_in_channeldir) { + struct stat statInfo; + int res = stat(filename,&statInfo); + if (res == -1) { + if (errno == ENOENT) { + //res = mkdir(filename,0755); + res = safe_mkdir(filename); + if (res == 0) { + strcat(filename,"/"); + } else { + perror("[vcrcontrol] mkdir"); + } + + } else { + perror("[vcrcontrol] stat"); + } + } else { + // directory exists + strcat(filename,"/"); + } + + } else + strcat(filename, "_"); + } + + pos = strlen(filename); + if (g_settings.recording_epg_for_filename) { + if(epgid != 0) { + CShortEPGData epgdata; + //if (g_Sectionsd->getEPGidShort(epgid, &epgdata)) + if(sectionsd_getEPGidShort(epgid, &epgdata)) + { + if (!(epgdata.title.empty())) + { + strcpy(&(filename[pos]), epgdata.title.c_str()); + char * p_act = &(filename[pos]); + do { + p_act += strcspn(p_act, "/ \"%&-\t`'~<>!,:;?^°$\\=*#@¤|"); + if (*p_act) { + *p_act++ = '_'; + } + } while (*p_act); + } + } + } else if (!epgTitle.empty()) { + strcpy(&(filename[pos]), epgTitle.c_str()); + char * p_act = &(filename[pos]); + do { + p_act += strcspn(p_act, "/ \"%&-\t`'~<>!,:;?^°$\\=*#@¤|"); + if (*p_act) { + *p_act++ = '_'; + } + } while (*p_act); + } + } +#if 1 + pos = strlen(filename); + time_t t = time(NULL); + strftime(&(filename[pos]), sizeof(filename) - pos - 1, "%Y%m%d_%H%M%S", localtime(&t)); + //pos = strlen(filename); +#endif + + start_time = time(0); + stream2file_error_msg_t error_msg = ::start_recording(filename, + getMovieInfoString(CMD_VCR_RECORD, channel_id, epgid, epgTitle, apid_list, epg_time).c_str(), + si.vpid, pids, numpids); + + if (error_msg == STREAM2FILE_OK) { + deviceState = CMD_VCR_RECORD; + return true; + } + else { + RestoreNeutrino(); + + printf("[vcrcontrol] stream2file error code: %d\n", error_msg); +#warning FIXME: Use better error message + DisplayErrorMessage(g_Locale->getText( + error_msg == STREAM2FILE_BUSY ? LOCALE_STREAMING_BUSY : + error_msg == STREAM2FILE_INVALID_DIRECTORY ? LOCALE_STREAMING_DIR_NOT_WRITABLE : + LOCALE_STREAMINGSERVER_NOCONNECT + )); // UTF-8 + + return false; + } +} + +bool sectionsd_getActualEPGServiceKey(const t_channel_id uniqueServiceKey, CEPGData * epgdata); + +void CVCRControl::Screenshot(const t_channel_id channel_id, char * fname) +{ + char filename[512]; // UTF-8 + char cmd[512]; + std::string channel_name; + CEPGData epgData; + event_id_t epgid = 0; + unsigned int pos; + + if(!fname) { + if(safe_mkdir((char *) "/hdd/screenshots/")) + return; + + strcpy(filename, "/hdd/screenshots/"); + + pos = strlen(filename); + channel_name = g_Zapit->getChannelName(channel_id); + if (!(channel_name.empty())) { + strcpy(&(filename[pos]), UTF8_TO_FILESYSTEM_ENCODING(channel_name.c_str())); + char * p_act = &(filename[pos]); + do { + p_act += strcspn(p_act, "/ \"%&-\t`'´!,:;"); + if (*p_act) { + *p_act++ = '_'; + } + } while (*p_act); + strcat(filename, "_"); + } + pos = strlen(filename); + + //if (g_Sectionsd->getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData)) + if(sectionsd_getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData)); + epgid = epgData.eventID; + if(epgid != 0) { + CShortEPGData epgdata; + //if (g_Sectionsd->getEPGidShort(epgid, &epgdata)) { + if(sectionsd_getEPGidShort(epgid, &epgdata)) { + if (!(epgdata.title.empty())) { + strcpy(&(filename[pos]), epgdata.title.c_str()); + char * p_act = &(filename[pos]); + do { + p_act += strcspn(p_act, "/ \"%&-\t`'~<>!,:;?^°$\\=*#@¤|"); + if (*p_act) { + *p_act++ = '_'; + } + } while (*p_act); + } + } + } + pos = strlen(filename); + time_t t = time(NULL); + strftime(&(filename[pos]), sizeof(filename) - pos - 1, "%Y%m%d_%H%M%S", localtime(&t)); + strcat(filename, ".bmp"); + } else + strcpy(filename, fname); + + sprintf(cmd, "grab -v %s", filename); +printf("Executing %s\n", cmd); + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, "Saving screenshot.."); + hintBox->paint(); + system(cmd); + hintBox->hide(); + delete hintBox; +} + +//------------------------------------------------------------------------- +bool CVCRControl::CServerDevice::Stop() +{ + printf("Stop\n"); + + bool return_value = sendCommand(CMD_VCR_STOP); + RestoreNeutrino(); + + return return_value; +} + +//------------------------------------------------------------------------- +bool CVCRControl::CServerDevice::Record(const t_channel_id channel_id, int mode, const event_id_t epgid, const std::string& epgTitle, unsigned char apids, const time_t epg_time) +{ + printf("Record channel_id: " + PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS + " epg: %llx, apids 0x%X mode %d\n", + channel_id, + epgid, + apids, + mode); + + CutBackNeutrino(channel_id, mode); + if(!sendCommand(CMD_VCR_RECORD, channel_id, epgid, epgTitle, apids)) + { + RestoreNeutrino(); + + DisplayErrorMessage(g_Locale->getText(LOCALE_STREAMINGSERVER_NOCONNECT)); + + return false; + } + else { + ext_channel_name = g_Zapit->getChannelName(channel_id); + return true; + } +} + + +//------------------------------------------------------------------------- +void CVCRControl::CServerDevice::serverDisconnect() +{ + close(sock_fd); +} + +//------------------------------------------------------------------------- +bool CVCRControl::CServerDevice::sendCommand(CVCRCommand command, const t_channel_id channel_id, const event_id_t epgid, const std::string& epgTitle, unsigned char apids) +{ + printf("Send command: %d channel_id: " + PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS + " epgid: %llx\n", + command, + channel_id, + epgid); + if(serverConnect()) + { + std::string extMessage = getCommandString(command, channel_id, epgid, epgTitle, apids); + + printf("sending to vcr-client:\n\n%s\n", extMessage.c_str()); + write(sock_fd, extMessage.c_str() , extMessage.length() ); + + serverDisconnect(); + + deviceState = command; + return true; + } + else + return false; + +} + +//------------------------------------------------------------------------- +bool CVCRControl::CServerDevice::serverConnect() +{ + + printf("connect to server: %s:%d\n",ServerAddress.c_str(),ServerPort); + + sock_fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + SAI servaddr; + memset(&servaddr,0,sizeof(SAI)); + servaddr.sin_family=AF_INET; + servaddr.sin_port=htons(ServerPort); + inet_pton(AF_INET, ServerAddress.c_str(), &servaddr.sin_addr); + + + if(connect(sock_fd, (SA *)&servaddr, sizeof(servaddr))==-1) + { + perror("[cvcr] - cannot connect to streamingserver\n"); + return false; + } + + return true; +} + +extern unsigned short g_vpid; +extern unsigned short g_vtype; +extern unsigned short g_apids[10]; +extern unsigned short g_ac3flags[10]; +extern unsigned short g_numpida; +extern unsigned int g_currentapid, g_currentac3; + +//------------------------------------------------------------------------- +std::string CVCRControl::CFileAndServerDevice::getMovieInfoString(const CVCRCommand command, const t_channel_id channel_id, const event_id_t epgid, const std::string& epgTitle, APIDList apid_list, const time_t epg_time) +{ + std::string extMessage; + std::string apids10; + std::string info1, info2; + if(!g_cMovieInfo) + g_cMovieInfo = new CMovieInfo(); + if(!g_movieInfo) + g_movieInfo = new MI_MOVIE_INFO(); + + g_cMovieInfo->clearMovieInfo(g_movieInfo); + + CZapitClient::responseGetPIDs pids; + g_Zapit->getPIDS (pids); + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo (); + + std::string tmpstring = g_Zapit->getChannelName(channel_id); + if (tmpstring.empty()) + g_movieInfo->epgChannel = "unknown"; + else + g_movieInfo->epgChannel = ZapitTools::UTF8_to_UTF8XML(tmpstring.c_str()); + + tmpstring = "not available"; + if (epgid != 0) { + CEPGData epgdata; + //if (g_Sectionsd->getEPGid(epgid, epg_time, &epgdata)) { + if (sectionsd_getEPGid(epgid, epg_time, &epgdata)) { + tmpstring = epgdata.title; + info1 = epgdata.info1; + info2 = epgdata.info2; + + g_movieInfo->parentalLockAge = epgdata.fsk; + if(epgdata.contentClassification.size() > 0 ) + g_movieInfo->genreMajor = epgdata.contentClassification[0]; + + g_movieInfo->length = epgdata.epg_times.dauer / 60; + + printf("fsk:%d, Genre:%d, Dauer: %d\r\n",g_movieInfo->parentalLockAge,g_movieInfo->genreMajor,g_movieInfo->length); + } + } else if (!epgTitle.empty()) { + tmpstring = epgTitle; + } + g_movieInfo->epgTitle = ZapitTools::UTF8_to_UTF8XML(tmpstring.c_str()); + g_movieInfo->epgId = channel_id; + g_movieInfo->epgInfo1 = ZapitTools::UTF8_to_UTF8XML(info1.c_str()); + g_movieInfo->epgInfo2 = ZapitTools::UTF8_to_UTF8XML(info2.c_str()); + g_movieInfo->epgEpgId = epgid ; + g_movieInfo->epgMode = g_Zapit->getMode(); + g_movieInfo->epgVideoPid = si.vpid; + g_movieInfo->VideoType = si.vtype; + + g_vpid = si.vpid; + g_vtype = si.vtype; + g_currentapid = si.apid; + memset(g_apids, 0, sizeof(unsigned short)*10); + memset(g_ac3flags, 0, sizeof(unsigned short)*10); + g_numpida = 0; + + EPG_AUDIO_PIDS audio_pids; + // super hack :-), der einfachste weg an die apid descriptions ranzukommen + g_RemoteControl->current_PIDs = pids; + g_RemoteControl->processAPIDnames(); + + APIDList::iterator it; + for(unsigned int i= 0; i< pids.APIDs.size(); i++) { + for(it = apid_list.begin(); it != apid_list.end(); it++) { + if(pids.APIDs[i].pid == it->apid) { + audio_pids.epgAudioPid = pids.APIDs[i].pid; + audio_pids.epgAudioPidName = ZapitTools::UTF8_to_UTF8XML(g_RemoteControl->current_PIDs.APIDs[i].desc); + audio_pids.atype = pids.APIDs[i].is_ac3; + audio_pids.selected = (audio_pids.epgAudioPid == (int) g_currentapid) ? 1 : 0; + g_movieInfo->audioPids.push_back(audio_pids); + + if(pids.APIDs[i].is_ac3) + g_ac3flags[i] = 1; + + g_apids[i] = pids.APIDs[i].pid; + if(g_apids[i] == g_currentapid) + g_currentac3 = pids.APIDs[i].is_ac3; + g_numpida++; + } + } + } + //FIXME sometimes no apid in xml ?? + if(g_movieInfo->audioPids.empty() && pids.APIDs.size()) { + int i = 0; + audio_pids.epgAudioPid = pids.APIDs[i].pid; + audio_pids.epgAudioPidName = ZapitTools::UTF8_to_UTF8XML(g_RemoteControl->current_PIDs.APIDs[i].desc); + audio_pids.atype = pids.APIDs[i].is_ac3; + audio_pids.selected = 1; + g_movieInfo->audioPids.push_back(audio_pids); + } + g_movieInfo->epgVTXPID = si.vtxtpid; + + g_cMovieInfo->encodeMovieInfoXml(&extMessage, g_movieInfo); + + //g_movieInfo->audioPids.clear(); + + return extMessage; +} diff --git a/src/driver/vcrcontrol.h b/src/driver/vcrcontrol.h new file mode 100644 index 000000000..8b219f70c --- /dev/null +++ b/src/driver/vcrcontrol.h @@ -0,0 +1,221 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef __vcrcontrol__ +#define __vcrcontrol__ + +#include +//#include +#include + +#include +#include + +#include + + +class CVCRControl +{ + public: + typedef enum CVCRStates + { + CMD_VCR_UNKNOWN = 0, + CMD_VCR_RECORD = 1, + CMD_VCR_STOP = 2, + CMD_VCR_PAUSE = 3, + CMD_VCR_RESUME = 4, + CMD_VCR_AVAILABLE = 5 + } CVCRCommand; + + enum CVCRDevices + { + DEVICE_VCR, + DEVICE_SERVER, + DEVICE_FILE + }; + + class CDevice // basisklasse für die devices + { + public: + int sock_fd; + int last_mode; + time_t start_time; + virtual CVCRDevices getDeviceType(void) const = 0; + CVCRStates deviceState; + virtual bool Stop() = 0; + virtual bool Record(const t_channel_id channel_id = 0, int mode=1, const event_id_t epgid = 0, const std::string& epgTitle = "", unsigned char apids = 0, const time_t epg_time=0) = 0; // epg_time added for .xml (MovieBrowser) + virtual bool Pause() = 0; + virtual bool Resume() = 0; + virtual bool IsAvailable() = 0; + CDevice() { deviceState = CMD_VCR_STOP; }; + virtual ~CDevice(){}; + typedef struct { + unsigned short apid; + unsigned int index; + bool ac3; + } APIDDesc; + typedef std::list APIDList; + virtual void getAPIDs(const unsigned char apids, APIDList & apid_list); + }; + + class CVCRDevice : public CDevice // VCR per IR + { + public: + bool SwitchToScart; + + virtual CVCRDevices getDeviceType(void) const + { + return DEVICE_VCR; + }; + virtual bool Stop(); + virtual bool Record(const t_channel_id channel_id = 0, int mode=1, const event_id_t epgid = 0, const std::string& epgTitle = "", unsigned char apids = 0, const time_t epg_time=0); // epg_time added for .xml (MovieBrowser) + virtual bool Pause(); + virtual bool Resume(); + virtual bool IsAvailable() { return true; }; + CVCRDevice(bool switchtoscart) { SwitchToScart = switchtoscart; }; + virtual ~CVCRDevice(){}; + }; + + class CFileAndServerDevice : public CDevice + { + protected: + void RestoreNeutrino(void); + void CutBackNeutrino(const t_channel_id channel_id, const int mode); + std::string getCommandString(const CVCRCommand command, const t_channel_id channel_id, const event_id_t epgid, const std::string& epgTitle, unsigned char apids); + std::string getMovieInfoString(const CVCRCommand command, const t_channel_id channel_id,const event_id_t epgid, const std::string& epgTitle, APIDList apid_list, const time_t epg_time); + + public: + bool StopPlayBack; + bool StopSectionsd; + + virtual bool Pause() + { + return false; + }; + + virtual bool Resume() + { + return false; + }; + + virtual bool IsAvailable() + { + return true; + }; + }; + + class CFileDevice : public CFileAndServerDevice + { + public: + std::string Directory; + unsigned int SplitSize; + bool Use_O_Sync; + bool Use_Fdatasync; + bool StreamVTxtPid; + bool StreamPmtPid; + unsigned int RingBuffers; + + virtual CVCRDevices getDeviceType(void) const + { + return DEVICE_FILE; + }; + + virtual bool Stop(); + virtual bool Record(const t_channel_id channel_id = 0, int mode=1, const event_id_t epgid = 0, const std::string& epgTitle = "", unsigned char apids = 0, const time_t epg_time=0); // epg_time added for .xml (MovieBrowser) + + CFileDevice(const bool stopplayback, const bool stopsectionsd, const char * const directory, const unsigned int splitsize, const bool use_o_sync, const bool use_fdatasync, const bool stream_vtxt_pid, const bool stream_pmt_pid, const unsigned int ringbuffers) + { + StopPlayBack = stopplayback; + StopSectionsd = stopsectionsd; + Directory = directory; + SplitSize = splitsize; + Use_O_Sync = use_o_sync; + Use_Fdatasync = use_fdatasync; + StreamVTxtPid = stream_vtxt_pid; + StreamPmtPid = stream_pmt_pid; + RingBuffers = ringbuffers; + }; + virtual ~CFileDevice() + { + }; + }; + + class CServerDevice : public CFileAndServerDevice // externer Streamingserver per tcp + { + private: + bool serverConnect(); + void serverDisconnect(); + + bool sendCommand(CVCRCommand command, const t_channel_id channel_id = 0, const event_id_t epgid = 0, const std::string& epgTitle = "", unsigned char apids = 0); + + public: + std::string ServerAddress; + unsigned int ServerPort; + + virtual CVCRDevices getDeviceType(void) const + { + return DEVICE_SERVER; + }; + + virtual bool Stop(); + virtual bool Record(const t_channel_id channel_id = 0, int mode=1, const event_id_t epgid = 0, const std::string& epgTitle = "", unsigned char apids = 0, const time_t epg_time=0); // epg_time added for .xml (MovieBrowser) + + CServerDevice(const bool stopplayback, const bool stopsectionsd, const char * const serveraddress, const unsigned int serverport) + { + StopPlayBack = stopplayback; + StopSectionsd = stopsectionsd; + ServerAddress = serveraddress; + ServerPort = serverport; + }; + virtual ~CServerDevice(){}; + }; + + public: + CVCRControl(); + ~CVCRControl(); + static CVCRControl * getInstance(); + + CDevice * Device; + + void registerDevice(CDevice * const device); + void unregisterDevice(); + + inline bool isDeviceRegistered(void) const { return (Device != NULL); }; + + inline CVCRStates getDeviceState(void) const { return Device->deviceState; }; + bool Stop(){return Device->Stop();}; + bool Record(const CTimerd::RecordingInfo * const eventinfo); + bool Pause(){return Device->Pause();}; + bool Resume(){return Device->Resume();}; + void Screenshot(const t_channel_id channel_id, char * fname = NULL); +}; + + +#endif diff --git a/src/driver/vfd.cpp b/src/driver/vfd.cpp new file mode 100644 index 000000000..7b73bf61e --- /dev/null +++ b/src/driver/vfd.cpp @@ -0,0 +1,899 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ + +CVFD::CVFD() +{ +#ifdef VFD_UPDATE + m_fileList = NULL; + m_fileListPos = 0; + m_fileListHeader = ""; + m_infoBoxText = ""; + m_infoBoxAutoNewline = 0; + m_progressShowEscape = 0; + m_progressHeaderGlobal = ""; + m_progressHeaderLocal = ""; + m_progressGlobal = 0; + m_progressLocal = 0; +#endif // VFD_UPDATE + + has_lcd = 1; + fd = open("/dev/display", O_RDONLY); + if(fd < 0) { + perror("/dev/display"); + has_lcd = 0; + } + text[0] = 0; + clearClock = 0; +} + +CVFD::~CVFD() +{ + if(fd > 0) + close(fd); +} + +CVFD* CVFD::getInstance() +{ + static CVFD* lcdd = NULL; + if(lcdd == NULL) { + lcdd = new CVFD(); + } + return lcdd; +} + +void CVFD::count_down() { + if (timeout_cnt > 0) { + timeout_cnt--; + if (timeout_cnt == 0) { + if (atoi(g_settings.lcd_setting_dim_brightness) > 0) { + // save lcd brightness, setBrightness() changes global setting + int b = g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS]; + setBrightness(atoi(g_settings.lcd_setting_dim_brightness)); + g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS] = b; + } else { + setPower(0); + } + } + } +} + +void CVFD::wake_up() { + if (atoi(g_settings.lcd_setting_dim_time) > 0) { + timeout_cnt = atoi(g_settings.lcd_setting_dim_time); + atoi(g_settings.lcd_setting_dim_brightness) > 0 ? + setBrightness(g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS]) : setPower(1); + } + else + setPower(1); +} + +void* CVFD::TimeThread(void *) +{ + while(1) { + sleep(1); + struct stat buf; + if (stat("/tmp/vfd.locked", &buf) == -1) { + CVFD::getInstance()->showTime(); + CVFD::getInstance()->count_down(); + } else + CVFD::getInstance()->wake_up(); + } + return NULL; +} + +void CVFD::init(const char * fontfile, const char * fontname) +{ + //InitNewClock(); /FIXME + + brightness = -1; + setMode(MODE_TVRADIO); + + if (pthread_create (&thrTime, NULL, TimeThread, NULL) != 0 ) { + perror("[lcdd]: pthread_create(TimeThread)"); + return ; + } +} + +void CVFD::setlcdparameter(int dimm, const int power) +{ + if(!has_lcd) return; + + if(dimm < 0) + dimm = 0; + else if(dimm > 15) + dimm = 15; + + if(!power) + dimm = 0; + + if(brightness == dimm) + return; + + brightness = dimm; + +printf("CVFD::setlcdparameter dimm %d power %d\n", dimm, power); + int ret = ioctl(fd, IOC_VFD_SET_BRIGHT, dimm); + if(ret < 0) + perror("IOC_VFD_SET_BRIGHT"); +} + +void CVFD::setlcdparameter(void) +{ + if(!has_lcd) return; + last_toggle_state_power = g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER]; + setlcdparameter((mode == MODE_STANDBY) ? g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] : g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS], + last_toggle_state_power); +} + +void CVFD::showServicename(const std::string & name) // UTF-8 +{ + if(!has_lcd) return; + +printf("CVFD::showServicename: %s\n", name.c_str()); + servicename = name; + if (mode != MODE_TVRADIO) + return; + + ShowText((char *) name.c_str()); + wake_up(); +} + +void CVFD::showTime(bool force) +{ + if(!has_lcd) + return; + + if (showclock) { + if (mode == MODE_STANDBY) { + char timestr[21]; + struct timeb tm; + struct tm * t; + static int hour = 0, minute = 0; + + ftime(&tm); + t = localtime(&tm.time); + if(force || ((hour != t->tm_hour) || (minute != t->tm_min))) { + hour = t->tm_hour; + minute = t->tm_min; + strftime(timestr, 20, "%H:%M", t); + ShowText((char *) timestr); + } + } + } + + if (CNeutrinoApp::getInstance ()->recordingstatus) { + if(clearClock) { + clearClock = 0; + ShowIcon(VFD_ICON_CAM1, false); + } else { + clearClock = 1; + ShowIcon(VFD_ICON_CAM1, true); + } + } else if(clearClock) { // in case icon ON after record stopped + clearClock = 0; + ShowIcon(VFD_ICON_CAM1, false); + } +} + +void CVFD::showRCLock(int duration) +{ +} + +void CVFD::showVolume(const char vol, const bool perform_update) +{ + static int oldpp = 0; + if(!has_lcd) return; + + ShowIcon(VFD_ICON_MUTE, muted); + if(vol == volume) + return; + + volume = vol; + wake_up(); + ShowIcon(VFD_ICON_FRAME, true); + + if ((mode == MODE_TVRADIO) && g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME]) { + int pp = (int) round((double) vol * (double) 8 / (double) 100); + if(pp > 8) pp = 8; + + if(oldpp != pp) { +printf("CVFD::showVolume: %d, bar %d\n", (int) vol, pp); + int i; + int j = 0x00000200; + for(i = 0; i < pp; i++) { + ShowIcon((vfd_icon) j, true); + j /= 2; + } + for(;i < 8; i++) { + ShowIcon((vfd_icon) j, false); + j /= 2; + } + oldpp = pp; + } + } +} + +void CVFD::showPercentOver(const unsigned char perc, const bool perform_update) +{ + if(!has_lcd) return; + + if ((mode == MODE_TVRADIO) && !(g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME])) { + //if (g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME] == 0) + { + ShowIcon(VFD_ICON_FRAME, true); + int pp; + if(perc == 255) + pp = 0; + else + pp = (int) round((double) perc * (double) 8 / (double) 100); +printf("CVFD::showPercentOver: %d, bar %d\n", (int) perc, pp); + if(pp > 8) pp = 8; + if(pp != percentOver) { + int i; + int j = 0x00000200; + for(i = 0; i < pp; i++) { + ShowIcon((vfd_icon) j, true); + j /= 2; + } + for(;i < 8; i++) { + ShowIcon((vfd_icon) j, false); + j /= 2; + } + percentOver = pp; + } + } + } +} + +void CVFD::showMenuText(const int position, const char * text, const int highlight, const bool utf_encoded) +{ + if(!has_lcd) return; + if (mode != MODE_MENU_UTF8) + return; + + ShowText((char *) text); + wake_up(); +} + +void CVFD::showAudioTrack(const std::string & artist, const std::string & title, const std::string & album) +{ + if(!has_lcd) return; + if (mode != MODE_AUDIO) + return; +printf("CVFD::showAudioTrack: %s\n", title.c_str()); + ShowText((char *) title.c_str()); + wake_up(); + +#ifdef HAVE_LCD + fonts.menu->RenderString(0,22, 125, artist.c_str() , CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 + fonts.menu->RenderString(0,35, 125, album.c_str() , CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 + fonts.menu->RenderString(0,48, 125, title.c_str() , CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 +#endif +} + +void CVFD::showAudioPlayMode(AUDIOMODES m) +{ + if(!has_lcd) return; + switch(m) { + case AUDIO_MODE_PLAY: + ShowIcon(VFD_ICON_PLAY, true); + ShowIcon(VFD_ICON_PAUSE, false); + break; + case AUDIO_MODE_STOP: + ShowIcon(VFD_ICON_PLAY, false); + ShowIcon(VFD_ICON_PAUSE, false); + break; + case AUDIO_MODE_PAUSE: + ShowIcon(VFD_ICON_PLAY, false); + ShowIcon(VFD_ICON_PAUSE, true); + break; + case AUDIO_MODE_FF: + break; + case AUDIO_MODE_REV: + break; + } + wake_up(); +} + +void CVFD::showAudioProgress(const char perc, bool isMuted) +{ + if(!has_lcd) return; +#if 0 // what is this ? FIXME + if (mode == MODE_AUDIO) { + int dp = int( perc/100.0*61.0+12.0); + if(isMuted) { + if(dp > 12) { + display.draw_line(12, 56, dp-1, 56, CLCDDisplay::PIXEL_OFF); + display.draw_line(12, 58, dp-1, 58, CLCDDisplay::PIXEL_OFF); + } + else + display.draw_line (12,55,72,59, CLCDDisplay::PIXEL_ON); + } + } +#endif +} + +void CVFD::setMode(const MODES m, const char * const title) +{ + if(!has_lcd) return; + + if(mode == MODE_AUDIO) + ShowIcon(VFD_ICON_MP3, false); +#if 0 + else if(mode == MODE_STANDBY) { + ShowIcon(VFD_ICON_COL1, false); + ShowIcon(VFD_ICON_COL2, false); + } +#endif + + if(strlen(title)) + ShowText((char *) title); + mode = m; + setlcdparameter(); + + switch (m) { + case MODE_TVRADIO: + switch (g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME]) + { + case 0: + showPercentOver(percentOver, false); + break; + case 1: + showVolume(volume, false); + break; +#if 0 + case 2: + showVolume(volume, false); + showPercentOver(percentOver, false); + break; +#endif + } + showServicename(servicename); + showclock = true; + //showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + case MODE_AUDIO: + { + ShowIcon(VFD_ICON_MP3, true); + showAudioPlayMode(AUDIO_MODE_STOP); + showVolume(volume, false); + showclock = true; + //showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + } + case MODE_SCART: + showVolume(volume, false); + showclock = true; + //showTime(); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + break; + case MODE_MENU_UTF8: + showclock = false; + //fonts.menutitle->RenderString(0,28, 140, title, CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 + break; + case MODE_SHUTDOWN: + showclock = false; + break; + case MODE_STANDBY: +#if 0 + ShowIcon(VFD_ICON_COL1, true); + ShowIcon(VFD_ICON_COL2, true); +#endif + showclock = true; + showTime(true); /* "showclock = true;" implies that "showTime();" does a "displayUpdate();" */ + /* "showTime()" clears the whole lcd in MODE_STANDBY */ + break; +#ifdef VFD_UPDATE + case MODE_FILEBROWSER: + showclock = true; + display.draw_fill_rect(-1, -1, 120, 64, CLCDDisplay::PIXEL_OFF); // clear lcd + showFilelist(); + break; + case MODE_PROGRESSBAR: + showclock = false; + display.load_screen(&(background[BACKGROUND_SETUP])); + showProgressBar(); + break; + case MODE_PROGRESSBAR2: + showclock = false; + display.load_screen(&(background[BACKGROUND_SETUP])); + showProgressBar2(); + break; + case MODE_INFOBOX: + showclock = false; + showInfoBox(); + break; +#endif // VFD_UPDATE + } + wake_up(); +} + +void CVFD::setBrightness(int bright) +{ + if(!has_lcd) return; + + g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS] = bright; + setlcdparameter(); +} + +int CVFD::getBrightness() +{ + //FIXME for old neutrino.conf + if(g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS] > 15) + g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS] = 15; + + return g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS]; +} + +void CVFD::setBrightnessStandby(int bright) +{ + if(!has_lcd) return; + + g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] = bright; + setlcdparameter(); +} + +int CVFD::getBrightnessStandby() +{ + //FIXME for old neutrino.conf + if(g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] > 15) + g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] = 15; + return g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS]; +} + +void CVFD::setPower(int power) +{ + if(!has_lcd) return; +// old + //g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER] = power; + //setlcdparameter(); +} + +int CVFD::getPower() +{ + return g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER]; +} + +void CVFD::togglePower(void) +{ + if(!has_lcd) return; + + last_toggle_state_power = 1 - last_toggle_state_power; + setlcdparameter((mode == MODE_STANDBY) ? g_settings.lcd_setting[SNeutrinoSettings::LCD_STANDBY_BRIGHTNESS] : g_settings.lcd_setting[SNeutrinoSettings::LCD_BRIGHTNESS], + last_toggle_state_power); +} + +void CVFD::setMuted(bool mu) +{ + if(!has_lcd) return; + muted = mu; + showVolume(volume); +} + +void CVFD::resume() +{ + if(!has_lcd) return; +} + +void CVFD::pause() +{ + if(!has_lcd) return; +} + +void CVFD::Lock() +{ + if(!has_lcd) return; + creat("/tmp/vfd.locked", 0); +} + +void CVFD::Unlock() +{ + if(!has_lcd) return; + unlink("/tmp/vfd.locked"); +} + +void CVFD::Clear() +{ + if(!has_lcd) return; + int ret = ioctl(fd, IOC_VFD_CLEAR_ALL, 0); + if(ret < 0) + perror("IOC_VFD_SET_TEXT"); +} + +void CVFD::ShowIcon(vfd_icon icon, bool show) +{ +//printf("CVFD::ShowIcon %s %x\n", show ? "show" : "hide", (int) icon); + int ret = ioctl(fd, show ? IOC_VFD_SET_ICON : IOC_VFD_CLEAR_ICON, icon); + if(ret < 0) + perror(show ? "IOC_VFD_SET_ICON" : "IOC_VFD_CLEAR_ICON"); +} + +void CVFD::ShowText(char * str) +{ + int len = strlen(str); + int i, ret; + +printf("CVFD::ShowText: [%s]\n", str); + for(i = len-1; i > 0; i--) { + if(str[i] == ' ') + str[i] = 0; + else + break; + } + + if(!strcmp(str, text) || len > 255) + return; + + strcpy(text, str); + +//printf("****************************** CVFD::ShowText: %s\n", str); + //FIXME !! + ret = ioctl(fd, IOC_VFD_SET_TEXT, len ? str : NULL); + if(ret < 0) + perror("IOC_VFD_SET_TEXT"); +} + +#ifdef VFD_UPDATE +/*****************************************************************************************/ +// showInfoBox +/*****************************************************************************************/ +#define LCD_WIDTH 120 +#define LCD_HEIGTH 64 + +#define EPG_INFO_FONT_HEIGHT 9 +#define EPG_INFO_SHADOW_WIDTH 1 +#define EPG_INFO_LINE_WIDTH 1 +#define EPG_INFO_BORDER_WIDTH 2 + +#define EPG_INFO_WINDOW_POS 4 +#define EPG_INFO_LINE_POS EPG_INFO_WINDOW_POS + EPG_INFO_SHADOW_WIDTH +#define EPG_INFO_BORDER_POS EPG_INFO_WINDOW_POS + EPG_INFO_SHADOW_WIDTH + EPG_INFO_LINE_WIDTH +#define EPG_INFO_TEXT_POS EPG_INFO_WINDOW_POS + EPG_INFO_SHADOW_WIDTH + EPG_INFO_LINE_WIDTH + EPG_INFO_BORDER_WIDTH + +#define EPG_INFO_TEXT_WIDTH LCD_WIDTH - (2*EPG_INFO_WINDOW_POS) + +// timer 0: OFF, timer>0 time to show in seconds, timer>=999 endless +void CVFD::showInfoBox(const char * const title, const char * const text ,int autoNewline,int timer) +{ +#ifdef HAVE_LCD + if(!has_lcd) return; + //printf("[lcdd] Info: \n"); + if(text != NULL) + m_infoBoxText = text; + if(text != NULL) + m_infoBoxTitle = title; + if(timer != -1) + m_infoBoxTimer = timer; + if(autoNewline != -1) + m_infoBoxAutoNewline = autoNewline; + + //printf("[lcdd] Info: %s,%s,%d,%d\n",m_infoBoxTitle.c_str(),m_infoBoxText.c_str(),m_infoBoxAutoNewline,m_infoBoxTimer); + if( mode == MODE_INFOBOX && + !m_infoBoxText.empty()) + { + // paint empty box + display.draw_fill_rect (EPG_INFO_WINDOW_POS, EPG_INFO_WINDOW_POS, LCD_WIDTH-EPG_INFO_WINDOW_POS+1, LCD_HEIGTH-EPG_INFO_WINDOW_POS+1, CLCDDisplay::PIXEL_OFF); + display.draw_fill_rect (EPG_INFO_LINE_POS, EPG_INFO_LINE_POS, LCD_WIDTH-EPG_INFO_LINE_POS-1, LCD_HEIGTH-EPG_INFO_LINE_POS-1, CLCDDisplay::PIXEL_ON); + display.draw_fill_rect (EPG_INFO_BORDER_POS, EPG_INFO_BORDER_POS, LCD_WIDTH-EPG_INFO_BORDER_POS-3, LCD_HEIGTH-EPG_INFO_BORDER_POS-3, CLCDDisplay::PIXEL_OFF); + + // paint title + if(!m_infoBoxTitle.empty()) + { + int width = fonts.menu->getRenderWidth(m_infoBoxTitle.c_str(),true); + if(width > 100) + width = 100; + int start_pos = (120-width) /2; + display.draw_fill_rect (start_pos, EPG_INFO_WINDOW_POS-4, start_pos+width+5, EPG_INFO_WINDOW_POS+10, CLCDDisplay::PIXEL_OFF); + fonts.menu->RenderString(start_pos+4,EPG_INFO_WINDOW_POS+5, width+5, m_infoBoxTitle.c_str(), CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 + } + + // paint info + std::string text_line; + int line; + int pos = 0; + int length = m_infoBoxText.size(); + for(line = 0; line < 5; line++) + { + text_line.clear(); + while ( m_infoBoxText[pos] != '\n' && + ((fonts.menu->getRenderWidth(text_line.c_str(), true) < EPG_INFO_TEXT_WIDTH-10) || !m_infoBoxAutoNewline )&& + (pos < length)) // UTF-8 + { + if ( m_infoBoxText[pos] >= ' ' && m_infoBoxText[pos] <= '~' ) // any char between ASCII(32) and ASCII (126) + text_line += m_infoBoxText[pos]; + pos++; + } + //printf("[lcdd] line %d:'%s'\r\n",line,text_line.c_str()); + fonts.menu->RenderString(EPG_INFO_TEXT_POS+1,EPG_INFO_TEXT_POS+(line*EPG_INFO_FONT_HEIGHT)+EPG_INFO_FONT_HEIGHT+3, EPG_INFO_TEXT_WIDTH, text_line.c_str(), CLCDDisplay::PIXEL_ON, 0, true); // UTF-8 + if ( m_infoBoxText[pos] == '\n' ) + pos++; // remove new line + } + displayUpdate(); + } +#endif +} + +/*****************************************************************************************/ +//showFilelist +/*****************************************************************************************/ +#define BAR_POS_X 114 +#define BAR_POS_Y 10 +#define BAR_POS_WIDTH 6 +#define BAR_POS_HEIGTH 40 + +void CVFD::showFilelist(int flist_pos,CFileList* flist,const char * const mainDir) +{ +#ifdef HAVE_LCD + if(!has_lcd) return; + //printf("[lcdd] FileList\n"); + if(flist != NULL) + m_fileList = flist; + if(flist_pos != -1) + m_fileListPos = flist_pos; + if(mainDir != NULL) + m_fileListHeader = mainDir; + + if (mode == MODE_FILEBROWSER && + m_fileList != NULL && + m_fileList->size() > 0) + { + + printf("[lcdd] FileList:OK\n"); + int size = m_fileList->size(); + + display.draw_fill_rect(-1, -1, 120, 52, CLCDDisplay::PIXEL_OFF); // clear lcd + + if(m_fileListPos > size) + m_fileListPos = size-1; + + int width = fonts.menu->getRenderWidth(m_fileListHeader.c_str(), true); + if(width>110) + width=110; + fonts.menu->RenderString((120-width)/2, 11, width+5, m_fileListHeader.c_str(), CLCDDisplay::PIXEL_ON); + + //printf("list%d,%d\r\n",m_fileListPos,(*m_fileList)[m_fileListPos].Marked); + std::string text; + int marked; + if(m_fileListPos > 0) + { + if ( (*m_fileList)[m_fileListPos-1].Marked == false ) + { + text =""; + marked = CLCDDisplay::PIXEL_ON; + } + else + { + text ="*"; + marked = CLCDDisplay::PIXEL_INV; + } + text += (*m_fileList)[m_fileListPos-1].getFileName(); + fonts.menu->RenderString(1, 12+12, BAR_POS_X+5, text.c_str(), marked); + } + if(m_fileListPos < size) + { + if ((*m_fileList)[m_fileListPos-0].Marked == false ) + { + text =""; + marked = CLCDDisplay::PIXEL_ON; + } + else + { + text ="*"; + marked = CLCDDisplay::PIXEL_INV; + } + text += (*m_fileList)[m_fileListPos-0].getFileName(); + fonts.time->RenderString(1, 12+12+14, BAR_POS_X+5, text.c_str(), marked); + } + if(m_fileListPos < size-1) + { + if ((*m_fileList)[m_fileListPos+1].Marked == false ) + { + text =""; + marked = CLCDDisplay::PIXEL_ON; + } + else + { + text ="*"; + marked = CLCDDisplay::PIXEL_INV; + } + text += (*m_fileList)[m_fileListPos+1].getFileName(); + fonts.menu->RenderString(1, 12+12+14+12, BAR_POS_X+5, text.c_str(), marked); + } + // paint marker + int pages = (((size-1)/3 )+1); + int marker_length = (BAR_POS_HEIGTH-2) / pages; + if(marker_length <4) + marker_length=4;// not smaller than 4 pixel + int marker_offset = ((BAR_POS_HEIGTH-2-marker_length) * m_fileListPos) /size ; + //printf("%d,%d,%d\r\n",pages,marker_length,marker_offset); + + display.draw_fill_rect (BAR_POS_X, BAR_POS_Y, BAR_POS_X+BAR_POS_WIDTH, BAR_POS_Y+BAR_POS_HEIGTH, CLCDDisplay::PIXEL_ON); + display.draw_fill_rect (BAR_POS_X+1, BAR_POS_Y+1, BAR_POS_X+BAR_POS_WIDTH-1, BAR_POS_Y+BAR_POS_HEIGTH-1, CLCDDisplay::PIXEL_OFF); + display.draw_fill_rect (BAR_POS_X+1, BAR_POS_Y+1+marker_offset, BAR_POS_X+BAR_POS_WIDTH-1, BAR_POS_Y+1+marker_offset+marker_length, CLCDDisplay::PIXEL_ON); + + displayUpdate(); + } +#endif +} + +/*****************************************************************************************/ +//showProgressBar +/*****************************************************************************************/ +#define PROG_GLOB_POS_X 10 +#define PROG_GLOB_POS_Y 30 +#define PROG_GLOB_POS_WIDTH 100 +#define PROG_GLOB_POS_HEIGTH 20 +void CVFD::showProgressBar(int global, const char * const text,int show_escape,int timer) +{ +#ifdef HAVE_LCD + if(!has_lcd) return; + if(text != NULL) + m_progressHeaderGlobal = text; + + if(timer != -1) + m_infoBoxTimer = timer; + + if(global >= 0) + { + if(global > 100) + m_progressGlobal =100; + else + m_progressGlobal = global; + } + + if(show_escape != -1) + m_progressShowEscape = show_escape; + + if (mode == MODE_PROGRESSBAR) + { + //printf("[lcdd] prog:%s,%d,%d\n",m_progressHeaderGlobal.c_str(),m_progressGlobal,m_progressShowEscape); + // Clear Display + display.draw_fill_rect (0,12,120,64, CLCDDisplay::PIXEL_OFF); + + // paint progress header + int width = fonts.menu->getRenderWidth(m_progressHeaderGlobal.c_str(),true); + if(width > 100) + width = 100; + int start_pos = (120-width) /2; + fonts.menu->RenderString(start_pos, 12+12, width+10, m_progressHeaderGlobal.c_str(), CLCDDisplay::PIXEL_ON,0,true); + + // paint global bar + int marker_length = (PROG_GLOB_POS_WIDTH * m_progressGlobal)/100; + + display.draw_fill_rect (PROG_GLOB_POS_X, PROG_GLOB_POS_Y, PROG_GLOB_POS_X+PROG_GLOB_POS_WIDTH, PROG_GLOB_POS_Y+PROG_GLOB_POS_HEIGTH, CLCDDisplay::PIXEL_ON); + display.draw_fill_rect (PROG_GLOB_POS_X+1+marker_length, PROG_GLOB_POS_Y+1, PROG_GLOB_POS_X+PROG_GLOB_POS_WIDTH-1, PROG_GLOB_POS_Y+PROG_GLOB_POS_HEIGTH-1, CLCDDisplay::PIXEL_OFF); + + // paint foot + if(m_progressShowEscape == true) + { + fonts.menu->RenderString(90, 64, 40, "Home", CLCDDisplay::PIXEL_ON); + } + displayUpdate(); + } +#endif +} + +/*****************************************************************************************/ +// showProgressBar2 +/*****************************************************************************************/ +#define PROG2_GLOB_POS_X 10 +#define PROG2_GLOB_POS_Y 24 +#define PROG2_GLOB_POS_WIDTH 100 +#define PROG2_GLOB_POS_HEIGTH 10 + +#define PROG2_LOCAL_POS_X 10 +#define PROG2_LOCAL_POS_Y 37 +#define PROG2_LOCAL_POS_WIDTH PROG2_GLOB_POS_WIDTH +#define PROG2_LOCAL_POS_HEIGTH PROG2_GLOB_POS_HEIGTH + +void CVFD::showProgressBar2(int local,const char * const text_local ,int global ,const char * const text_global ,int show_escape ) +{ +#ifdef HAVE_LCD + if(!has_lcd) return; + //printf("[lcdd] prog2\n"); + if(text_local != NULL) + m_progressHeaderLocal = text_local; + + if(text_global != NULL) + m_progressHeaderGlobal = text_global; + + if(global >= 0) + { + if(global > 100) + m_progressGlobal =100; + else + m_progressGlobal = global; + } + if(local >= 0) + { + if(local > 100) + m_progressLocal =100; + else + m_progressLocal = local; + } + if(show_escape != -1) + m_progressShowEscape = show_escape; + + if (mode == MODE_PROGRESSBAR2) + { + + //printf("[lcdd] prog2:%s,%d,%d\n",m_progressHeaderGlobal.c_str(),m_progressGlobal,m_progressShowEscape); + // Clear Display + display.draw_fill_rect (0,12,120,64, CLCDDisplay::PIXEL_OFF); + + // paint global header + int width = fonts.menu->getRenderWidth(m_progressHeaderGlobal.c_str(),true); + if(width > 100) + width = 100; + int start_pos = (120-width) /2; + fonts.menu->RenderString(start_pos, PROG2_GLOB_POS_Y-3, width+10, m_progressHeaderGlobal.c_str(), CLCDDisplay::PIXEL_ON,0,true); + + // paint global bar + int marker_length = (PROG2_GLOB_POS_WIDTH * m_progressGlobal)/100; + + display.draw_fill_rect (PROG2_GLOB_POS_X, PROG2_GLOB_POS_Y, PROG2_GLOB_POS_X+PROG2_GLOB_POS_WIDTH, PROG2_GLOB_POS_Y+PROG2_GLOB_POS_HEIGTH, CLCDDisplay::PIXEL_ON); + display.draw_fill_rect (PROG2_GLOB_POS_X+1+marker_length, PROG2_GLOB_POS_Y+1, PROG2_GLOB_POS_X+PROG2_GLOB_POS_WIDTH-1, PROG2_GLOB_POS_Y+PROG2_GLOB_POS_HEIGTH-1, CLCDDisplay::PIXEL_OFF); + + // paint local header + width = fonts.menu->getRenderWidth(m_progressHeaderLocal.c_str(),true); + if(width > 100) + width = 100; + start_pos = (120-width) /2; + fonts.menu->RenderString(start_pos, PROG2_LOCAL_POS_Y + PROG2_LOCAL_POS_HEIGTH +10 , width+10, m_progressHeaderLocal.c_str(), CLCDDisplay::PIXEL_ON,0,true); + // paint local bar + marker_length = (PROG2_LOCAL_POS_WIDTH * m_progressLocal)/100; + + display.draw_fill_rect (PROG2_LOCAL_POS_X, PROG2_LOCAL_POS_Y, PROG2_LOCAL_POS_X+PROG2_LOCAL_POS_WIDTH, PROG2_LOCAL_POS_Y+PROG2_LOCAL_POS_HEIGTH, CLCDDisplay::PIXEL_ON); + display.draw_fill_rect (PROG2_LOCAL_POS_X+1+marker_length, PROG2_LOCAL_POS_Y+1, PROG2_LOCAL_POS_X+PROG2_LOCAL_POS_WIDTH-1, PROG2_LOCAL_POS_Y+PROG2_LOCAL_POS_HEIGTH-1, CLCDDisplay::PIXEL_OFF); + // paint foot + if(m_progressShowEscape == true) + { + fonts.menu->RenderString(90, 64, 40, "Home", CLCDDisplay::PIXEL_ON); + } + displayUpdate(); + } +#endif +} +/*****************************************************************************************/ +#endif // VFD_UPDATE + + diff --git a/src/driver/vfd.h b/src/driver/vfd.h new file mode 100644 index 000000000..91de14913 --- /dev/null +++ b/src/driver/vfd.h @@ -0,0 +1,172 @@ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __vfd__ +#define __vfd__ + +//#define VFD_UPDATE + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef VFD_UPDATE +// TODO Why is USE_FILE_OFFSET64 not defined, if file.h is included here???? +#ifndef __USE_FILE_OFFSET64 +#define __USE_FILE_OFFSET64 1 +#endif +#include "driver/file.h" +#endif // LCD_UPDATE + +#include +#include +#include + +class CVFD +{ + public: + + enum MODES + { + MODE_TVRADIO, + MODE_SCART, + MODE_SHUTDOWN, + MODE_STANDBY, + MODE_MENU_UTF8, + MODE_AUDIO +#ifdef VFD_UPDATE + , MODE_FILEBROWSER, + MODE_PROGRESSBAR, + MODE_PROGRESSBAR2, + MODE_INFOBOX +#endif // LCD_UPDATE + + }; + enum AUDIOMODES + { + AUDIO_MODE_PLAY, + AUDIO_MODE_STOP, + AUDIO_MODE_FF, + AUDIO_MODE_PAUSE, + AUDIO_MODE_REV + }; + + + private: + MODES mode; + + std::string servicename; + char volume; + unsigned char percentOver; + bool muted; + bool showclock; + pthread_t thrTime; + int last_toggle_state_power; + bool clearClock; + unsigned int timeout_cnt; + int fd; + int brightness; + char text[256]; + + void wake_up(); + void count_down(); + + CVFD(); + + static void* TimeThread(void*); + void setlcdparameter(int dimm, int power); + public: + + ~CVFD(); + bool has_lcd; + void setlcdparameter(void); + + static CVFD* getInstance(); + void init(const char * fontfile, const char * fontname); + + void setMode(const MODES m, const char * const title = ""); + + void showServicename(const std::string & name); // UTF-8 + void showTime(bool force = false); + /** blocks for duration seconds */ + void showRCLock(int duration = 2); + void showVolume(const char vol, const bool perform_update = true); + void showPercentOver(const unsigned char perc, const bool perform_update = true); + void showMenuText(const int position, const char * text, const int highlight = -1, const bool utf_encoded = false); + void showAudioTrack(const std::string & artist, const std::string & title, const std::string & album); + void showAudioPlayMode(AUDIOMODES m=AUDIO_MODE_PLAY); + void showAudioProgress(const char perc, bool isMuted); + void setBrightness(int); + int getBrightness(); + + void setBrightnessStandby(int); + int getBrightnessStandby(); + + void setPower(int); + int getPower(); + + void togglePower(void); + + void setInverse(int); + int getInverse(); + + void setMuted(bool); + + void resume(); + void pause(); + void Lock(); + void Unlock(); + void Clear(); + void ShowIcon(vfd_icon icon, bool show); + void ShowText(char * str); +#ifdef LCD_UPDATE + private: + CFileList* m_fileList; + int m_fileListPos; + std::string m_fileListHeader; + + std::string m_infoBoxText; + std::string m_infoBoxTitle; + int m_infoBoxTimer; // for later use + bool m_infoBoxAutoNewline; + + bool m_progressShowEscape; + std::string m_progressHeaderGlobal; + std::string m_progressHeaderLocal; + int m_progressGlobal; + int m_progressLocal; + public: + MODES getMode(void){return mode;}; + + void showFilelist(int flist_pos = -1,CFileList* flist = NULL,const char * const mainDir=NULL); + void showInfoBox(const char * const title = NULL,const char * const text = NULL,int autoNewline = -1,int timer = -1); + void showProgressBar(int global = -1,const char * const text = NULL,int show_escape = -1,int timer = -1); + void showProgressBar2(int local = -1,const char * const text_local = NULL,int global = -1,const char * const text_global = NULL,int show_escape = -1); +#endif // LCD_UPDATE + +}; + + +#endif diff --git a/src/driver/wav.h b/src/driver/wav.h new file mode 100644 index 000000000..1b108b746 --- /dev/null +++ b/src/driver/wav.h @@ -0,0 +1,65 @@ +static unsigned char click[] = { +0xE5, 0xF5, 0xFF, 0xF7, 0x18, 0xFA, 0x7F, 0xFB, 0x98, 0xFD, 0xFF, 0xFE, 0x17, 0x01, 0x31, 0x03, +0x4A, 0x05, 0xB1, 0x06, 0xCA, 0x08, 0xCA, 0x0F, 0xCA, 0x16, 0x97, 0x12, 0x17, 0x0F, 0xFE, 0x13, +0xE4, 0x18, 0x7E, 0x17, 0x17, 0x16, 0x64, 0x15, 0x64, 0x15, 0xE4, 0x18, 0x17, 0x1D, 0xCA, 0x1D, +0x7E, 0x1E, 0xCA, 0x24, 0x17, 0x2B, 0xE4, 0x26, 0x64, 0x23, 0x31, 0x2D, 0xFE, 0x36, 0x7E, 0x33, +0xB1, 0x30, 0x17, 0x32, 0x7E, 0x33, 0x64, 0x2A, 0x4A, 0x21, 0x64, 0x15, 0x7E, 0x09, 0x97, 0x04, +0xB2, 0xFF, 0xB2, 0xF1, 0xB2, 0xE3, 0xFF, 0xE2, 0x4B, 0xE2, 0x32, 0xD9, 0xCB, 0xD0, 0x7F, 0xD1, +0xE5, 0xD2, 0x18, 0xD0, 0x4B, 0xCD, 0x65, 0xC8, 0x7F, 0xC3, 0xCB, 0xC2, 0x18, 0xC2, 0x18, 0xC2, +0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0xCB, 0xC2, +0x7F, 0xC3, 0x18, 0xC9, 0xB2, 0xCE, 0xFF, 0xD4, 0x4B, 0xDB, 0x4B, 0xE2, 0x4B, 0xE9, 0xE5, 0xEE, +0x32, 0xF5, 0x7F, 0xFB, 0xCA, 0x01, 0x17, 0x08, 0x17, 0x0F, 0xFE, 0x13, 0x97, 0x19, 0xCA, 0x1D, +0xB1, 0x22, 0x31, 0x26, 0xB1, 0x29, 0xCA, 0x2B, 0x97, 0x2E, 0x4A, 0x2F, 0xB1, 0x30, 0xFE, 0x2F, +0xFE, 0x2F, 0xE4, 0x2D, 0x7E, 0x2C, 0xFE, 0x28, 0x31, 0x26, 0xFE, 0x21, 0xCA, 0x1D, 0x31, 0x18, +0x4A, 0x13, 0xB1, 0x0D, 0x17, 0x08, 0xCA, 0x01, 0x7F, 0xFB, 0x32, 0xF5, 0x98, 0xEF, 0x4B, 0xE9, +0xB2, 0xE3, 0x18, 0xDE, 0x7F, 0xD8, 0x98, 0xD3, 0x65, 0xCF, 0x32, 0xCB, 0xB2, 0xC7, 0xE5, 0xC4, +0xCB, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x7F, 0xC3, 0x98, 0xC5, 0x65, 0xC8, +0xE5, 0xCB, 0x18, 0xD0, 0xFF, 0xD4, 0x98, 0xDA, 0x32, 0xE0, 0x7F, 0xE6, 0x7F, 0xED, 0x7F, 0xF4, +0x32, 0xFC, 0xE4, 0x03, 0x97, 0x0B, 0xE4, 0x11, 0x31, 0x18, 0x31, 0x1F, 0x31, 0x26, 0x7E, 0x2C, +0x7E, 0x33, 0xB1, 0x37, 0x97, 0x3C, 0x4A, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, +0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xFE, 0x3D, 0xCA, 0x39, +0x97, 0x35, 0xFE, 0x2F, 0x17, 0x2B, 0xCA, 0x24, 0x7E, 0x1E, 0x7E, 0x17, 0x7E, 0x10, 0x7E, 0x09, +0x31, 0x03, 0xE5, 0xFC, 0x4B, 0xF7, 0xFF, 0xF0, 0x65, 0xEB, 0x7F, 0xE6, 0x98, 0xE1, 0xCB, 0xDE, +0xFF, 0xDB, 0x32, 0xD9, 0x65, 0xD6, 0xFF, 0xD4, 0x4B, 0xD4, 0xFF, 0xD4, 0x65, 0xD6, 0x7F, 0xD8, +0x98, 0xDA, 0x65, 0xDD, 0xE5, 0xE0, 0x18, 0xE5, 0x4B, 0xE9, 0xE5, 0xEE, 0x32, 0xF5, 0xCB, 0xFA, +0x64, 0x00, 0xB1, 0x06, 0xFE, 0x0C, 0x4A, 0x13, 0x4A, 0x1A, 0xE4, 0x1F, 0x7E, 0x25, 0xB1, 0x29, +0x97, 0x2E, 0x17, 0x32, 0x4A, 0x36, 0x17, 0x39, 0x97, 0x3C, 0x97, 0x3C, 0x4A, 0x3D, 0x4A, 0x3D, +0x4A, 0x3D, 0x31, 0x3B, 0xCA, 0x39, 0x4A, 0x36, 0x7E, 0x33, 0x97, 0x2E, 0xB1, 0x29, 0x17, 0x24, +0x31, 0x1F, 0x31, 0x18, 0xE4, 0x11, 0xE4, 0x0A, 0xE4, 0x03, 0xE5, 0xFC, 0xE5, 0xF5, 0x98, 0xEF, +0x4B, 0xE9, 0xFF, 0xE2, 0x65, 0xDD, 0xCB, 0xD7, 0xE5, 0xD2, 0xB2, 0xCE, 0x32, 0xCB, 0x65, 0xC8, +0x4B, 0xC6, 0xE5, 0xC4, 0x7F, 0xC3, 0x32, 0xC4, 0xE5, 0xC4, 0xB2, 0xC7, 0x7F, 0xCA, 0xFF, 0xCD, +0x7F, 0xD1, 0x65, 0xD6, 0x4B, 0xDB, 0x98, 0xE1, 0xE5, 0xE7, 0x32, 0xEE, 0x32, 0xF5, 0x7F, 0xFB, +0x7E, 0x02, 0xCA, 0x08, 0xCA, 0x0F, 0x17, 0x16, 0x17, 0x1D, 0xFE, 0x21, 0xE4, 0x26, 0x17, 0x2B, +0x4A, 0x2F, 0x17, 0x32, 0x97, 0x35, 0x4A, 0x36, 0xB1, 0x37, 0xFE, 0x36, 0x4A, 0x36, 0x31, 0x34, +0x17, 0x32, 0x97, 0x2E, 0x17, 0x2B, 0x7E, 0x25, 0x97, 0x20, 0x4A, 0x1A, 0xB1, 0x14, 0x4A, 0x0C, +0x97, 0x04, 0x4B, 0xFE, 0xB2, 0xF8, 0x4B, 0xF0, 0xE5, 0xE7, 0x98, 0xE1, 0xFF, 0xDB, 0x65, 0xD6, +0xCB, 0xD0, 0x4B, 0xCD, 0x7F, 0xCA, 0x65, 0xC8, 0x4B, 0xC6, 0xE5, 0xC4, 0x7F, 0xC3, 0x98, 0xC5, +0x65, 0xC8, 0xCB, 0xC9, 0xE5, 0xCB, 0x18, 0xD0, 0xFF, 0xD4, 0xE5, 0xD9, 0x7F, 0xDF, 0x18, 0xE5, +0x65, 0xEB, 0xB2, 0xF1, 0xB2, 0xF8, 0xFF, 0xFE, 0xFE, 0x05, 0xFE, 0x0C, 0xFE, 0x13, 0x97, 0x19, +0xE4, 0x1F, 0x7E, 0x25, 0x17, 0x2B, 0x97, 0x2E, 0xCA, 0x32, 0x97, 0x35, 0x17, 0x39, 0xCA, 0x39, +0x7E, 0x3A, 0xCA, 0x39, 0x17, 0x39, 0xFE, 0x36, 0xE4, 0x34, 0xFE, 0x2F, 0x17, 0x2B, 0xCA, 0x24, +0x31, 0x1F, 0x7E, 0x17, 0x7E, 0x10, 0x17, 0x08, 0x64, 0x00, 0xFF, 0xF7, 0x98, 0xEF, 0x32, 0xE7, +0x7F, 0xDF, 0xCB, 0xD7, 0x18, 0xD0, 0xCB, 0xC9, 0x32, 0xC4, 0xCB, 0xC2, 0x18, 0xC2, 0x18, 0xC2, +0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x98, 0xC5, 0xCB, 0xC9, 0x18, 0xD0, +0x65, 0xD6, 0x18, 0xDE, 0xCB, 0xE5, 0x7F, 0xED, 0x32, 0xF5, 0xE5, 0xFC, 0x4A, 0x05, 0x97, 0x0B, +0x97, 0x12, 0x31, 0x18, 0x7E, 0x1E, 0xB1, 0x22, 0xE4, 0x26, 0xB1, 0x29, 0x7E, 0x2C, 0x31, 0x2D, +0x97, 0x2E, 0xE4, 0x2D, 0xE4, 0x2D, 0xCA, 0x2B, 0xB1, 0x29, 0x31, 0x26, 0x64, 0x23, 0x7E, 0x1E, +0x4A, 0x1A, 0xB1, 0x14, 0x17, 0x0F, 0xCA, 0x08, 0x7E, 0x02, 0x32, 0xFC, 0x98, 0xF6, 0xE5, 0xEE, +0xE5, 0xE7, 0xE5, 0xE0, 0x98, 0xDA, 0x65, 0xD6, 0x32, 0xD2, 0xFF, 0xCD, 0xCB, 0xC9, 0x98, 0xC5, +0x18, 0xC2, 0x18, 0xC2, 0x18, 0xC2, 0x32, 0xC4, 0xFF, 0xC6, 0xCB, 0xC9, 0x98, 0xCC, 0x32, 0xD2, +0x7F, 0xD8, 0xE5, 0xE0, 0xFF, 0xE9, 0x65, 0xF2, 0xCB, 0xFA, 0x31, 0x03, 0x97, 0x0B, 0xFE, 0x13, +0x64, 0x1C, 0x64, 0x23, 0x17, 0x2B, 0x97, 0x2E, 0x17, 0x32, 0x31, 0x34, 0x4A, 0x36, 0x4A, 0x36, +0x4A, 0x36, 0xCA, 0x32, 0x4A, 0x2F, 0x64, 0x2A, 0x7E, 0x25, 0xE4, 0x1F, 0x4A, 0x1A, 0x4A, 0x13, +0x4A, 0x0C, 0x4A, 0x05, 0x4B, 0xFE, 0xFF, 0xF7, 0x65, 0xF2, 0xCB, 0xEC, 0x32, 0xE7, 0xFF, 0xE2, +0xCB, 0xDE, 0x4B, 0xDB, 0xCB, 0xD7, 0xFF, 0xD4, 0xE5, 0xD2, 0x7F, 0xD1, 0x18, 0xD0, 0x18, 0xD0, +0xCB, 0xD0, 0x32, 0xD2, 0x98, 0xD3, 0x65, 0xD6, 0x32, 0xD9, 0x18, 0xDE, 0xFF, 0xE2, 0x4B, 0xE9, +0x98, 0xEF, 0x98, 0xF6, 0x4B, 0xFE, 0xB1, 0x06, 0x17, 0x0F, 0x7E, 0x17, 0xE4, 0x1F, 0x31, 0x26, +0x31, 0x2D, 0x64, 0x31, 0x4A, 0x36, 0x64, 0x38, 0x7E, 0x3A, 0x17, 0x39, 0x64, 0x38, 0x7E, 0x33, +0x97, 0x2E, 0x97, 0x27, 0x4A, 0x21, 0x97, 0x19, 0xE4, 0x11, 0xCA, 0x08, 0xB2, 0xFF, 0x4B, 0xF7, +0xE5, 0xEE, 0xE5, 0xE7, 0x98, 0xE1, 0xB2, 0xDC, 0xCB, 0xD7, 0xB2, 0xD5, 0x98, 0xD3, 0x98, 0xD3, +0x98, 0xD3, 0xFF, 0xD4, 0x65, 0xD6, 0x32, 0xD9, 0xFF, 0xDB, 0x7F, 0xDF, 0xFF, 0xE2, 0x7F, 0xE6, +0xB2, 0xEA, 0xE5, 0xEE, 0xCB, 0xF3, 0xB2, 0xF8, 0x98, 0xFD, 0x7E, 0x02, 0x17, 0x08, 0xB1, 0x0D, +0xFE, 0x13, 0x4A, 0x1A, 0x97, 0x20, 0x7E, 0x25, 0x17, 0x2B, 0x4A, 0x2F, 0x31, 0x34, 0x4A, 0x36, +0x64, 0x38, 0xFE, 0x36, 0x4A, 0x36, 0x17, 0x32, 0xE4, 0x2D, 0xE4, 0x26, 0xE4, 0x1F, 0x17, 0x16, +0x4A, 0x0C, 0xCA, 0x01, 0xFF, 0xF7, 0x32, 0xEE +}; diff --git a/src/global.h b/src/global.h new file mode 100644 index 000000000..1b510558f --- /dev/null +++ b/src/global.h @@ -0,0 +1,90 @@ +#ifndef __neutrino_global_h__ +#define __neutrino_global_h__ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#include + +#include +#include +#include + +#include "driver/fontrenderer.h" +#include "driver/rcinput.h" +#include "driver/vfd.h" +#include "driver/rfmod.h" + +#include "system/localize.h" +#include "system/settings.h" + +#include "gui/epgview.h" +#include "gui/infoviewer.h" +#include "gui/eventlist.h" + +#ifndef NEUTRINO_CPP + #define NEUTRINO_CPP extern +#endif + +#define NEUTRINO_SETTINGS_FILE CONFIGDIR "/neutrino.conf" + +#define NEUTRINO_RECORDING_TIMER_SCRIPT CONFIGDIR "/recording.timer" +#define NEUTRINO_RECORDING_START_SCRIPT CONFIGDIR "/recording.start" +#define NEUTRINO_RECORDING_ENDED_SCRIPT CONFIGDIR "/recording.end" +#define NEUTRINO_ENTER_STANDBY_SCRIPT CONFIGDIR "/standby.on" +#define NEUTRINO_LEAVE_STANDBY_SCRIPT CONFIGDIR "/standby.off" + +#define NEUTRINO_SCAN_SETTINGS_FILE CONFIGDIR "/scan.conf" +#define NEUTRINO_PARENTALLOCKED_FILE DATADIR "/neutrino/.plocked" + +NEUTRINO_CPP SNeutrinoSettings g_settings; +NEUTRINO_CPP SglobalInfo g_info; + +NEUTRINO_CPP CControldClient *g_Controld; +NEUTRINO_CPP CZapitClient *g_Zapit; +NEUTRINO_CPP CSectionsdClient *g_Sectionsd; +NEUTRINO_CPP CTimerdClient *g_Timerd; + +NEUTRINO_CPP FBFontRenderClass *g_fontRenderer; + +NEUTRINO_CPP Font * g_Font[FONT_TYPE_COUNT]; +NEUTRINO_CPP Font * g_SignalFont; + +NEUTRINO_CPP CRCInput *g_RCInput; + +NEUTRINO_CPP CEpgData *g_EpgData; +NEUTRINO_CPP CInfoViewer *g_InfoViewer; +NEUTRINO_CPP EventList *g_EventList; + +NEUTRINO_CPP CLocaleManager *g_Locale; +NEUTRINO_CPP RFmod *g_RFmod; + +#endif /* __neutrino_global_h__ */ diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am new file mode 100644 index 000000000..c41ccff4a --- /dev/null +++ b/src/gui/Makefile.am @@ -0,0 +1,37 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions +#AM_CPPFLAGS = -fno-rtti + +SUBDIRS = widget bedit + +INCLUDES = \ + -I$(top_srcdir)/daemons \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libnet \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/xmltree \ + -I$(top_srcdir)/lib/libcoolstream \ + -I$(top_srcdir)/lib/libupnpclient \ + @CURL_CFLAGS@ \ + @FREETYPE_CFLAGS@ \ + -I$(top_srcdir)/lib + +noinst_LIBRARIES = libtimerlist.a libneutrino_gui.a libneutrino_gui2.a + +libneutrino_gui_a_SOURCES = \ + bouquetlist.cpp channellist.cpp eventlist.cpp pluginlist.cpp \ + favorites.cpp sleeptimer.cpp screensetup.cpp \ + epgview.cpp infoviewer.cpp scan.cpp update.cpp \ + filebrowser.cpp audioplayer.cpp nfs.cpp pictureviewer.cpp \ + movieplayer.cpp rc_lock.cpp bookmarkmanager.cpp \ + timeosd.cpp epgplus.cpp epg_menu.cpp \ + streaminfo2.cpp dboxinfo.cpp \ + plugins.cpp imageinfo.cpp audio_select.cpp moviebrowser.cpp movieinfo.cpp + +libneutrino_gui2_a_SOURCES = \ + color.cpp alphasetup.cpp motorcontrol.cpp scale.cpp hdd_menu.cpp cam_menu.cpp infoclock.cpp upnpbrowser.cpp + +libtimerlist_a_SOURCES = \ + timerlist.cpp + diff --git a/src/gui/alphasetup.cpp b/src/gui/alphasetup.cpp new file mode 100644 index 000000000..621cb213a --- /dev/null +++ b/src/gui/alphasetup.cpp @@ -0,0 +1,272 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ALPHA_SETUP_ICON_DESELECTED "volumeslider2.raw" +#define ALPHA_SETUP_ICON_ALPHA1_SELECTED "volumeslider2red.raw" +#define ALPHA_SETUP_ICON_ALPHA2_SELECTED "volumeslider2green.raw" + + +CAlphaSetup::CAlphaSetup(const neutrino_locale_t Name, unsigned char* Alpha1, unsigned char* Alpha2, CChangeObserver* Observer) +{ + frameBuffer = CFrameBuffer::getInstance(); + observer = Observer; + name = Name; + + + alpha1 = Alpha1; + alpha2 = Alpha2; + //frameBuffer->setBlendLevel(*alpha1, *alpha2); //FIXME until blend works +} + +int CAlphaSetup::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + width = w_max(360, 0); + + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + + height = hheight+ mheight*3; + + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height) >> 1); + + int res = menu_return::RETURN_REPAINT; + if (parent) + { + parent->hide(); + } + unsigned char alpha1_alt= *alpha1; + unsigned char alpha2_alt= *alpha2; + + frameBuffer->setBlendLevel(*alpha1, *alpha2); + paint(); + + int selected = 0; + int max = 1; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + switch ( msg ) + { +#if 0 + case CRCInput::RC_down: + { + if(selected 0) + { + paintSlider(x + 10, y + hheight , alpha1, LOCALE_GTXALPHA_ALPHA1, ALPHA_SETUP_ICON_DESELECTED , false); + paintSlider(x + 10, y + hheight + mheight, alpha2, LOCALE_GTXALPHA_ALPHA2, ALPHA_SETUP_ICON_DESELECTED , false); + + selected--; + + switch (selected) + { + case 0: + paintSlider(x + 10, y + hheight , alpha1, LOCALE_GTXALPHA_ALPHA1, ALPHA_SETUP_ICON_ALPHA1_SELECTED, true ); + break; + case 1: + paintSlider(x + 10, y + hheight + mheight, alpha2, LOCALE_GTXALPHA_ALPHA2, ALPHA_SETUP_ICON_ALPHA2_SELECTED, true ); + break; + } + } + break; + } +#endif + case CRCInput::RC_right: + { + switch (selected) + { + case 0: + if (*alpha1 < 255) { + *alpha1+= 0x10; + paintSlider(x + 10, y + hheight , alpha1, LOCALE_GTXALPHA_ALPHA1, ALPHA_SETUP_ICON_ALPHA1_SELECTED, true ); + frameBuffer->setBlendLevel(*alpha1, *alpha2); + } + break; + case 1: + if (*alpha2 < 255) { + *alpha2 += 0x10; + paintSlider(x + 10, y + hheight + mheight, alpha2, LOCALE_GTXALPHA_ALPHA2, ALPHA_SETUP_ICON_ALPHA2_SELECTED, true ); + frameBuffer->setBlendLevel(*alpha1, *alpha2); + } + break; + } + break; + } + case CRCInput::RC_left: + { + switch (selected) + { + case 0: + if (*alpha1 >= 0x10) { + *alpha1 -= 0x10; + paintSlider(x + 10, y + hheight , alpha1, LOCALE_GTXALPHA_ALPHA1, ALPHA_SETUP_ICON_ALPHA1_SELECTED, true ); + frameBuffer->setBlendLevel(*alpha1, *alpha2); + } + break; + case 1: + if (*alpha2 >= 0x10) { + *alpha2 -= 0x10; + paintSlider(x + 10, y + hheight + mheight, alpha2, LOCALE_GTXALPHA_ALPHA2, ALPHA_SETUP_ICON_ALPHA2_SELECTED, true ); + frameBuffer->setBlendLevel(*alpha1, *alpha2); + } + break; + } + break; + } + case CRCInput::RC_favorites: + case CRCInput::RC_sat: + break; + case CRCInput::RC_home: + if ((*alpha1 != alpha1_alt) || (*alpha2 != alpha2_alt)) + { + if (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel) + { + break; + } + } + + // sonst abbruch... + *alpha1 = alpha1_alt; + *alpha2 = alpha2_alt; + + case CRCInput::RC_timeout: + case CRCInput::RC_ok: + loop = false; + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + hide(); + + if(observer) + observer->changeNotify(name, NULL); + + return res; +} + +void CAlphaSetup::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CAlphaSetup::paint() +{ + frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0); + + paintSlider(x + 10, y + hheight , alpha1, LOCALE_GTXALPHA_ALPHA1, ALPHA_SETUP_ICON_ALPHA1_SELECTED, true ); + //paintSlider(x + 10, y + hheight + mheight, alpha2, LOCALE_GTXALPHA_ALPHA2, ALPHA_SETUP_ICON_DESELECTED , false); +} + +void CAlphaSetup::paintSlider(const int x, const int y, const unsigned char * const spos, const neutrino_locale_t text, const char * const iconname, const bool selected) // UTF-8 +{ + if (!spos) + return; + + int sspos = (*spos)*100/255; + + frameBuffer->paintBoxRel(x+70,y,120,mheight, COL_MENUCONTENT_PLUS_0); + + frameBuffer->paintIcon("volumebody.raw", x + 70 , y + 2 + mheight / 4); + frameBuffer->paintIcon(iconname , x + 73 + sspos, y + mheight / 4); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x,y+mheight, width, g_Locale->getText(text), COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/alphasetup.h b/src/gui/alphasetup.h new file mode 100644 index 000000000..3631dadaa --- /dev/null +++ b/src/gui/alphasetup.h @@ -0,0 +1,78 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __alphasetup__ +#define __alphasetup__ + + +#include "widget/menue.h" + +#include +#include + +#include + + +class CAlphaSetup : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + unsigned char *alpha1; + unsigned char *alpha2; + + + neutrino_locale_t name; + + CChangeObserver* observer; + + void paint(); + void setAlpha(); + void paintSlider(const int x, const int y, const unsigned char * const spos, const neutrino_locale_t text, const char * const iconname, const bool selected); + + public: + + CAlphaSetup(const neutrino_locale_t Name, unsigned char* Alpha1, unsigned char* Alpha2, CChangeObserver* Observer = NULL); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif + diff --git a/src/gui/audio_select.cpp b/src/gui/audio_select.cpp new file mode 100644 index 000000000..4eef30449 --- /dev/null +++ b/src/gui/audio_select.cpp @@ -0,0 +1,131 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include +#include +#include +#include + +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ +extern CAPIDChangeExec * APIDChanger; +extern CAudioSetupNotifier * audioSetupNotifier; +int dvbsub_getpid(); + +#include + +// +// -- AUDIO Selector Menue Handler Class +// -- to be used for calls from Menue +// -- (2005-08-31 rasc) +// + +// -- this is a copy from neutrino.cpp!! +#define AUDIOMENU_ANALOGOUT_OPTION_COUNT 3 +const CMenuOptionChooser::keyval AUDIOMENU_ANALOGOUT_OPTIONS[AUDIOMENU_ANALOGOUT_OPTION_COUNT] = +{ + { 0, LOCALE_AUDIOMENU_STEREO }, + { 1, LOCALE_AUDIOMENU_MONOLEFT }, + { 2, LOCALE_AUDIOMENU_MONORIGHT } +}; + +int CAudioSelectMenuHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + + + if (parent) { + parent->hide(); + } + + doMenu (); + return res; +} + +int CAudioSelectMenuHandler::doMenu () +{ + CMenuWidget AudioSelector(LOCALE_APIDSELECTOR_HEAD, "audio.raw", 360); + unsigned int count; + CSubtitleChangeExec SubtitleChanger; + + for(count=0; count < g_RemoteControl->current_PIDs.APIDs.size(); count++ ) { + char apid[5]; + sprintf(apid, "%d", count); + AudioSelector.addItem(new CMenuForwarderNonLocalized( + g_RemoteControl->current_PIDs.APIDs[count].desc, true, NULL, + APIDChanger, apid, CRCInput::convertDigitToKey(count + 1)), + (count == g_RemoteControl->current_PIDs.PIDs.selected_apid)); + } + + // -- setup menue for to Dual Channel Stereo + AudioSelector.addItem(GenericMenuSeparatorLine); + + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_AUDIOMENU_ANALOGOUT, + &g_settings.audio_AnalogMode, + AUDIOMENU_ANALOGOUT_OPTIONS, AUDIOMENU_ANALOGOUT_OPTION_COUNT, + true, audioSetupNotifier, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED); + + AudioSelector.addItem( oj ); + + CChannelList *channelList = CNeutrinoApp::getInstance ()->channelList; + int curnum = channelList->getActiveChannelNumber(); + CZapitChannel * cc = channelList->getChannel(curnum); + + bool sep_added = false; + if(cc) { + for (int i = 0 ; i < (int)cc->getSubtitleCount() ; ++i) { + CZapitAbsSub* s = cc->getChannelSub(i); + CZapitDVBSub* sd = reinterpret_cast(s); + CZapitTTXSub* st = reinterpret_cast(s); + printf("[neutrino] adding subtitle %s pid %x\n", sd->ISO639_language_code.c_str(), sd->pId); + if (s->thisSubType == CZapitAbsSub::DVB) { + if(!sep_added) { + sep_added = true; + AudioSelector.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_SUBTITLES_HEAD)); + } + char spid[5]; + sprintf(spid, "%d", sd->pId); + AudioSelector.addItem(new CMenuForwarderNonLocalized(sd->ISO639_language_code.c_str(), + sd->pId != dvbsub_getpid(), NULL, &SubtitleChanger, spid, CRCInput::convertDigitToKey(++count))); + } + } + if(sep_added) { + AudioSelector.addItem(new CMenuForwarder(LOCALE_SUBTITLES_STOP, true, NULL, &SubtitleChanger, "off")); + } + + } + + return AudioSelector.exec(NULL, ""); +} diff --git a/src/gui/audio_select.h b/src/gui/audio_select.h new file mode 100644 index 000000000..f2700a4f6 --- /dev/null +++ b/src/gui/audio_select.h @@ -0,0 +1,47 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __audio_selector__ +#define __audio_selector__ + +// +// -- Audio Channel Selector Menue +// -- 2005-08-31 rasc +// + +#include "widget/menue.h" + + + +using namespace std; + +class CAudioSelectMenuHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + int doMenu(); + +}; + + +#endif + diff --git a/src/gui/audioplayer.cpp b/src/gui/audioplayer.cpp new file mode 100644 index 000000000..f0bdc5721 --- /dev/null +++ b/src/gui/audioplayer.cpp @@ -0,0 +1,2882 @@ +/* + $Id: audioplayer.cpp,v 1.80 2009/10/18 14:38:03 dbt Exp $ + Neutrino-GUI - DBoxII-Project + + AudioPlayer by Dirch,Zwen + + (C) 2002-2008 the tuxbox project contributors + (C) 2008 Novell, Inc. Author: Stefan Seyfried + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef ENABLE_GUI_MOUNT +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#if 0 +#ifdef ENABLE_LIRC +#include +#endif + +#if HAVE_DVB_API_VERSION >= 3 +#include +#include +#include +#define ADAP "/dev/dvb/adapter0" +#define ADEC ADAP "/audio0" +#define VDEC ADAP "/video0" +#define DMX ADAP "/demux0" +#define DVR ADAP "/dvr0" +#endif +#endif + +#include +extern cVideo * videoDecoder; + +#ifdef ConnectLineBox_Width +#undef ConnectLineBox_Width +#endif +#define ConnectLineBox_Width 16 + +#define AUDIOPLAYERGUI_SMSKEY_TIMEOUT 1000 +#define SHOW_FILE_LOAD_LIMIT 50 + +//#define AUDIOPLAYER_TIME_DEBUG + +// check if files to be added are already in the playlist +#define AUDIOPLAYER_CHECK_FOR_DUPLICATES +#define AUDIOPLAYER_START_SCRIPT CONFIGDIR "/audioplayer.start" +#define AUDIOPLAYER_END_SCRIPT CONFIGDIR "/audioplayer.end" +#define DEFAULT_RADIOSTATIONS_XMLFILE CONFIGDIR "/radio-stations.xml" + +const long int GET_PLAYLIST_TIMEOUT = 10; +const char RADIO_STATION_XML_FILE[] = {DEFAULT_RADIOSTATIONS_XMLFILE}; + +const std::string icecasturl = "http://dir.xiph.org/yp.xml"; +const long int GET_ICECAST_TIMEOUT = 90; // list is about 500kB! + +CAudiofileExt::CAudiofileExt() +: CAudiofile(), firstChar('\0') +{ +} + +CAudiofileExt::CAudiofileExt(std::string name, CFile::FileType type) +: CAudiofile(name, type), firstChar('\0') +{ +} + +CAudiofileExt::CAudiofileExt(const CAudiofileExt& src) +: CAudiofile(src), firstChar(src.firstChar) +{ +} + +void CAudiofileExt::operator=(const CAudiofileExt& src) +{ + if (&src == this) + return; + CAudiofile::operator=(src); + firstChar = src.firstChar; +} + + +//------------------------------------------------------------------------ + + + +#include +#include +#include + +struct MemoryStruct { + char *memory; + size_t size; +}; + +static void *myrealloc(void *ptr, size_t size) +{ + /* There might be a realloc() out there that doesn't like reallocing + NULL pointers, so we take care of it here */ + if(ptr) + return realloc(ptr, size); + else + return malloc(size); +} + +static size_t +WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)data; + + mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1); + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} + +// we borrow this from filebrowser +extern size_t CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data); + +CAudioPlayerGui::CAudioPlayerGui(bool inetmode) +{ + m_frameBuffer = CFrameBuffer::getInstance(); + m_visible = false; + m_inetmode = false; //inetmode;//FIXME + + Init(); +} + +void CAudioPlayerGui::Init(void) +{ + stimer = 0; + m_selected = 0; + m_metainfo.clear(); + + m_select_title_by_name = g_settings.audioplayer_select_title_by_name==1; + + if(strlen(g_settings.network_nfs_audioplayerdir)!=0) + m_Path = g_settings.network_nfs_audioplayerdir; + else + m_Path = "/"; + + audiofilefilter.Clear(); + if (m_inetmode) { + audiofilefilter.addFilter("url"); + audiofilefilter.addFilter("xml"); + audiofilefilter.addFilter("m3u"); + audiofilefilter.addFilter("pls"); + } else { + audiofilefilter.addFilter("cdr"); + audiofilefilter.addFilter("mp3"); + audiofilefilter.addFilter("m2a"); + audiofilefilter.addFilter("mpa"); + audiofilefilter.addFilter("mp2"); + audiofilefilter.addFilter("m3u"); + audiofilefilter.addFilter("ogg"); + audiofilefilter.addFilter("wav"); +#ifdef ENABLE_FLAC + audiofilefilter.addFilter("flac"); +#endif + } + m_SMSKeyInput.setTimeout(AUDIOPLAYERGUI_SMSKEY_TIMEOUT); +} + +//------------------------------------------------------------------------ + +CAudioPlayerGui::~CAudioPlayerGui() +{ + m_playlist.clear(); + m_radiolist.clear(); + m_filelist.clear(); + m_title2Pos.clear(); + g_Zapit->setStandby (false); + g_Sectionsd->setPauseScanning (false); +} + +//------------------------------------------------------------------------ +int CAudioPlayerGui::exec(CMenuTarget* parent, const std::string &) +{ + CAudioPlayer::getInstance()->init(); + m_state = CAudioPlayerGui::STOP; + + m_show_playlist = g_settings.audioplayer_show_playlist==1; + + if (m_select_title_by_name != (g_settings.audioplayer_select_title_by_name==1)) + { + if ((g_settings.audioplayer_select_title_by_name == 1) + && m_playlistHasChanged) + { + buildSearchTree(); + } + m_select_title_by_name = g_settings.audioplayer_select_title_by_name; + } + + if (m_playlist.empty()) + m_current = -1; + else + m_current = 0; + + m_selected = 0; + //m_width = 710; + //if((g_settings.screen_EndX - g_settings.screen_StartX) < m_width+ConnectLineBox_Width) + m_width=(g_settings.screen_EndX - g_settings.screen_StartX) - ConnectLineBox_Width - 5; + //m_height = 570; + //if((g_settings.screen_EndY - g_settings.screen_StartY) < m_height) + m_height = (g_settings.screen_EndY - g_settings.screen_StartY - 5); + m_sheight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight(); + m_buttonHeight = std::min(25, m_sheight); + m_theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + m_fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + m_title_height = m_fheight*2 + 20 + m_sheight + 4; + m_info_height = m_fheight*2; + m_listmaxshow = (m_height - m_info_height - m_title_height - m_theight - 2*m_buttonHeight) / (m_fheight); + m_height = m_theight + m_info_height + m_title_height + 2*m_buttonHeight + m_listmaxshow * m_fheight; // recalc height + + m_x = (((g_settings.screen_EndX - g_settings.screen_StartX) - (m_width + ConnectLineBox_Width)) / 2) + + g_settings.screen_StartX + ConnectLineBox_Width; + m_y = (((g_settings.screen_EndY- g_settings.screen_StartY) - m_height)/ 2) + g_settings.screen_StartY; + m_idletime=time(NULL); + m_screensaver=false; + + if(parent) + { + parent->hide(); + } +#if 0 + if(g_settings.video_Format != g_settings.video_backgroundFormat) + g_Controld->setVideoFormat(g_settings.video_backgroundFormat); +#endif + bool usedBackground = m_frameBuffer->getuseBackground(); + if (usedBackground) + m_frameBuffer->saveBackgroundImage(); + + + // set zapit in standby mode + g_Zapit->stopPlayBack(); + + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/mp3.jpg"); + + // tell neutrino we're in audio mode + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , NeutrinoMessages::mode_audio ); +#if 0 + // remember last mode + CZapitClient::responseGetLastChannel firstchannel; + g_Zapit->getLastChannel(firstchannel.channelNumber, firstchannel.mode); + if ((firstchannel.mode == 'r') ? + (CNeutrinoApp::getInstance()->zapto_radio_on_init_done) : + (CNeutrinoApp::getInstance()->zapto_tv_on_init_done)) + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode() | NeutrinoMessages::norezap); + else + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode()); +#endif + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode()); + // Stop sectionsd + g_Sectionsd->setPauseScanning(true); + + puts("[audioplayer.cpp] executing " AUDIOPLAYER_START_SCRIPT "."); + if (system(AUDIOPLAYER_START_SCRIPT) != 0) + perror("Datei " AUDIOPLAYER_START_SCRIPT " fehlt.Bitte erstellen, wenn gebraucht.\nFile " AUDIOPLAYER_START_SCRIPT " not found. Please create if needed.\n"); + + show(); + + // Restore previous background + if (usedBackground) + m_frameBuffer->restoreBackgroundImage(); + m_frameBuffer->useBackground(usedBackground); + m_frameBuffer->paintBackground(); + + // Restore last mode + //t_channel_id channel_id=CNeutrinoApp::getInstance()->channelList->getActiveChannel_ChannelID(); + //g_Zapit->zapTo_serviceID(channel_id); + //g_Zapit->setStandby(false); + + puts("[audioplayer.cpp] executing " AUDIOPLAYER_END_SCRIPT "."); + if (system(AUDIOPLAYER_END_SCRIPT) != 0) + perror("Datei " AUDIOPLAYER_END_SCRIPT " fehlt. Bitte erstellen, wenn gebraucht.\nFile " AUDIOPLAYER_END_SCRIPT " not found. Please create if needed.\n"); + + // Start Sectionsd + g_Sectionsd->setPauseScanning(false); + videoDecoder->StopPicture(); + + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , m_LastMode ); + g_RCInput->postMsg( NeutrinoMessages::SHOW_INFOBAR, 0 ); + + // always exit all + return menu_return::RETURN_EXIT_ALL; +} + +//------------------------------------------------------------------------ + +int CAudioPlayerGui::show() +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int pic_index = 0; + + int ret = -1; + + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + + bool loop = true; + bool update = true; + bool clear_before_update = false; + m_key_level = 0; + + while(loop) + { + if(!m_screensaver) + { + updateMetaData(); + } + updateTimes(); + + if(CNeutrinoApp::getInstance()->getMode() != NeutrinoMessages::mode_audio) + { + // stop if mode was changed in another thread + loop = false; + } + if ((m_state != CAudioPlayerGui::STOP) && + (CAudioPlayer::getInstance()->getState() == CBaseDec::STOP) && + (!m_playlist.empty())) + { + if(m_curr_audiofile.FileType != CFile::STREAM_AUDIO) + playNext(); + } + + if (update) + { + if(clear_before_update) + { + hide(); + clear_before_update = false; + } + update = false; + paint(); + } + g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display + + if( msg == CRCInput::RC_timeout || msg == NeutrinoMessages::EVT_TIMER) + { + int timeout = time(NULL) - m_idletime; + int screensaver_timeout = atoi(g_settings.audioplayer_screensaver); + if(screensaver_timeout !=0 && timeout > screensaver_timeout*60 && !m_screensaver) + screensaver(true); + + if(msg == NeutrinoMessages::EVT_TIMER && data == stimer) { + if(m_screensaver) { + char fname[255]; + + sprintf(fname, "%s/mp3-%d.jpg", DATADIR "/neutrino/icons", pic_index); + + int ret = access(fname, F_OK); + printf("CAudioPlayerGui::show: new pic %s: %s\n", fname, ret ? "not found" : "found"); + if(ret == 0) { + pic_index++; + videoDecoder->StopPicture(); + videoDecoder->ShowPicture(fname); + } else if(pic_index) { + pic_index = 0; + videoDecoder->StopPicture(); + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/mp3.jpg"); + } + } else + pic_index = 0; + } + + } + else + { + m_idletime = time(NULL); + if(m_screensaver) + { + screensaver(false); + } + } + + if( msg == CRCInput::RC_tv) + { + if(m_inetmode) { + m_inetmode = false; + m_radiolist = m_playlist; + m_playlist = m_filelist; + } else { + m_inetmode = true; + m_filelist = m_playlist; + m_playlist = m_radiolist; + } + Init(); + clear_before_update = true; + update = true; + } + else if (msg == CRCInput::RC_home) + { + if (m_state != CAudioPlayerGui::STOP) + stop(); + else + loop=false; + } + else if (msg == CRCInput::RC_left) + { + if (m_key_level == 1) + { + playPrev(); + } + else + { + if (!m_show_playlist) + { + m_current--; + if (m_current < 0) + m_current = m_playlist.size()-1; + update = true; + } + else if (!m_playlist.empty()) + { + if (m_selected < m_listmaxshow) + { + m_selected = m_playlist.size()-1; + } + else + m_selected -= m_listmaxshow; + m_liststart = (m_selected / m_listmaxshow) * m_listmaxshow; + update = true; + } + } + + } + else if (msg == CRCInput::RC_right) + { + if (m_key_level == 1) + { + playNext(); + } + else + { + if (!m_show_playlist) + { + m_current++; + if (m_current >= (int)m_playlist.size()) + m_current = 0; + update = true; + } + else if (!m_playlist.empty()) + { + m_selected += m_listmaxshow; + if (m_selected >= m_playlist.size()) { + if (((m_playlist.size() / m_listmaxshow) + 1) * m_listmaxshow == m_playlist.size() + m_listmaxshow) + m_selected = 0; + else + m_selected = m_selected < (((m_playlist.size() / m_listmaxshow) + 1) * m_listmaxshow) ? (m_playlist.size()-1) : 0; + } + m_liststart = (m_selected / m_listmaxshow) * m_listmaxshow; + update = true; + } + } + } + else if((msg &~ CRCInput::RC_Repeat) == CRCInput::RC_up || (msg &~ CRCInput::RC_Repeat) == CRCInput::RC_page_up) + { + if(m_show_playlist && !m_playlist.empty() ) + { + int step = 0; + int prevselected = m_selected; + + step = msg == CRCInput::RC_page_up ? m_listmaxshow : 1; + m_selected -= step; + if((prevselected-step) < 0) + m_selected = m_playlist.size()-1; +#if 0 + if(m_selected == 0) + { + m_selected = m_playlist.size()-1; + } + else + { + m_selected--; + } +#endif + paintItem(prevselected - m_liststart); + unsigned int oldliststart = m_liststart; + m_liststart = (m_selected/m_listmaxshow)*m_listmaxshow; + if(oldliststart != m_liststart) + { + update = true; + } + else + { + paintItem(m_selected - m_liststart); + } + } + } + else if((msg &~ CRCInput::RC_Repeat) == CRCInput::RC_down || (msg &~ CRCInput::RC_Repeat) == CRCInput::RC_page_down) + { + if(m_show_playlist && !m_playlist.empty() ) + { + int prevselected = m_selected; + int step = msg == CRCInput::RC_page_down ? m_listmaxshow : 1; + m_selected += step; + + if(m_selected >= m_playlist.size()) { + if (((m_playlist.size() / m_listmaxshow) + 1) * m_listmaxshow == m_playlist.size() + m_listmaxshow) // last page has full entries + m_selected = 0; + else + m_selected = ((step == m_listmaxshow) && (m_selected < (((m_playlist.size() / m_listmaxshow)+1) * m_listmaxshow))) ? (m_playlist.size() - 1) : 0; + } + //m_selected = (m_selected + 1) % m_playlist.size(); + + paintItem(prevselected - m_liststart); + unsigned int oldliststart = m_liststart; + m_liststart = (m_selected/m_listmaxshow)*m_listmaxshow; + if(oldliststart != m_liststart) + { + update = true; + } + else + { + paintItem(m_selected - m_liststart); + } + } + } + else if (msg == CRCInput::RC_ok || msg == CRCInput::RC_play) + { + if (!m_playlist.empty()) { + if (!m_show_playlist) + play(m_current); + else + play(m_selected); + } + } + else if (msg == CRCInput::RC_red) + { + if(m_key_level == 0) + { + if (!m_playlist.empty()) + { + //xx CPlayList::iterator p = m_playlist.begin()+selected; + removeFromPlaylist(m_selected); + if((int)m_selected == m_current) + { + m_current--; + //stop(); // Stop if song is deleted, next song will be startet automat. + } + if(m_selected >= m_playlist.size()) + m_selected = m_playlist.size() == 0 ? m_playlist.size() : m_playlist.size() - 1; + update = true; + } + } + else if(m_key_level == 1) + { + stop(); + } + else + { + // key_level==2 + } + + } + else if (msg == CRCInput::RC_stop) + { + if(m_key_level == 1) + stop(); + } + else if(msg == CRCInput::RC_green) + { + if (m_key_level == 0) + { + openFilebrowser(); + update=true; + } + else if (m_key_level == 1) + { + if(m_curr_audiofile.FileType != CFile::STREAM_AUDIO) + rev(); + } + else + { // key_level == 2 + + if(m_state == CAudioPlayerGui::STOP) + { + if (!m_playlist.empty()) + { + savePlaylist(); + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + update = true; + } + } + else + { + // keylevel 2 can only be reached if the currently played file + // is no stream, so we do not have to test for this case + int seconds=0; + CIntInput secondsInput(LOCALE_AUDIOPLAYER_JUMP_DIALOG_TITLE, + seconds, + 5, + LOCALE_AUDIOPLAYER_JUMP_DIALOG_HINT1, + LOCALE_AUDIOPLAYER_JUMP_DIALOG_HINT2); + int res = secondsInput.exec(NULL,""); + if (seconds != 0 && res!= menu_return::RETURN_EXIT_ALL) + rev(seconds); + update=true; + } + } + } + else if(msg == CRCInput::RC_yellow) + { + if(m_key_level == 0) + { + if (!m_playlist.empty()) + { + //stop(); + clearPlaylist(); + clear_before_update = true; + update = true; + } + } + else if(m_key_level == 1) + { + pause(); + } + else + { // key_level==2 + m_select_title_by_name =! m_select_title_by_name; + if (m_select_title_by_name && m_playlistHasChanged) + buildSearchTree(); + paint(); + } + } + else if(msg == CRCInput::RC_blue) + { + if (m_key_level == 0) + { + if (m_inetmode) { + static int old_select = 0; + char cnt[5]; + CMenuWidget InputSelector(LOCALE_AUDIOPLAYER_LOAD_RADIO_STATIONS, "audio.raw", 400); + int count = 0; + int select = -1; + CMenuSelectorTarget *InetRadioInputChanger = new CMenuSelectorTarget(&select); + // -- setup menue for inetradio input + sprintf(cnt, "%d", count); + InputSelector.addItem(new CMenuForwarder( + LOCALE_AUDIOPLAYER_ADD_LOC, true, NULL, InetRadioInputChanger, + cnt, CRCInput::convertDigitToKey(count + 1)), old_select == count); + + sprintf(cnt, "%d", ++count); + InputSelector.addItem(new CMenuForwarder( + LOCALE_AUDIOPLAYER_ADD_SC, true, NULL, InetRadioInputChanger, + cnt, CRCInput::convertDigitToKey(count + 1)), old_select == count); + + sprintf(cnt, "%d", ++count); + InputSelector.addItem(new CMenuForwarder( + LOCALE_AUDIOPLAYER_ADD_IC, true, NULL, InetRadioInputChanger, + cnt, CRCInput::convertDigitToKey(count + 1)), old_select == count); + + //InputSelector.addItem(GenericMenuSeparator); + hide(); + InputSelector.exec(NULL, ""); + if(select >= 0) + old_select = select; + switch (select) { + case 0: scanXmlFile(RADIO_STATION_XML_FILE); + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + break; + case 1: openSCbrowser(); + break; + case 2: readDir_ic(); + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + break; + default: break; + } + update=true; + } + else if ( shufflePlaylist() ) + { + update = true; + } + } + else if (m_key_level == 1) + { + if(m_curr_audiofile.FileType != CFile::STREAM_AUDIO) + ff(); + } + else // key_level == 2 + { + if (m_state != CAudioPlayerGui::STOP) + { + // keylevel 2 can only be reached if the currently played file + // is no stream, so we do not have to test for this case + int seconds=0; + CIntInput secondsInput(LOCALE_AUDIOPLAYER_JUMP_DIALOG_TITLE, + seconds, + 5, + LOCALE_AUDIOPLAYER_JUMP_DIALOG_HINT1, + LOCALE_AUDIOPLAYER_JUMP_DIALOG_HINT2); + int res = secondsInput.exec(NULL,""); + if (seconds != 0 && res!= menu_return::RETURN_EXIT_ALL) + ff(seconds); + update = true; + } + } + } + else if(msg == CRCInput::RC_help) + { + if (m_key_level == 2) + m_key_level = 0; + else + m_key_level++; + + if (m_state != CAudioPlayerGui::STOP) + { + // jumping in streams not supported + if (m_key_level == 2 && + m_curr_audiofile.FileType == CFile::STREAM_AUDIO) + { + m_key_level = 0; + } + } + // there are only two keylevels in the "STOP"-case + else if(m_key_level == 1) + { + m_key_level = 2; + } + paintFoot(); + } + else if(msg == CRCInput::RC_0) + { + if(m_current >= 0) + { + m_selected = m_current; + update = true; + } + } + else if (CRCInput::isNumeric(msg) && !(m_playlist.empty())) + { //numeric zap or SMS zap + if (m_select_title_by_name) + { + //printf("select by name\n"); + unsigned char smsKey = 0; + do + { + smsKey = m_SMSKeyInput.handleMsg(msg); + //printf(" new key: %c", smsKey); + g_RCInput->getMsg_ms(&msg, &data, AUDIOPLAYERGUI_SMSKEY_TIMEOUT - 200); + + +/* show a hint box with current char (too slow at the moment?)*/ +#if 1 + char selectedKey[1]; + sprintf(selectedKey,"%c",smsKey); + int x1=(g_settings.screen_EndX- g_settings.screen_StartX)/2 + g_settings.screen_StartX-50; + int y1=(g_settings.screen_EndY- g_settings.screen_StartY)/2 + g_settings.screen_StartY; + int h = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getHeight(); + int w = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getRenderWidth(selectedKey); + m_frameBuffer->paintBoxRel(x1 - 7, y1 - h - 5, w + 14, h + 10, COL_MENUCONTENT_PLUS_6); + m_frameBuffer->paintBoxRel(x1 - 4, y1 - h - 3, w + 8, h + 6, COL_MENUCONTENTSELECTED_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP] + ->RenderString(x1,y1,w+1,selectedKey,COL_MENUCONTENTSELECTED,0); +#endif + + } while (CRCInput::isNumeric(msg) && !(m_playlist.empty())); + + if (msg == CRCInput::RC_timeout + || msg == CRCInput::RC_nokey) + { + //printf("selected key: %c\n",smsKey); + selectTitle(smsKey); + update = true; + } + m_SMSKeyInput.resetOldKey(); + } + else + { + //printf("numeric zap\n"); + int val = 0; + if (getNumericInput(msg,val)) + m_selected = std::min((int)m_playlist.size(), val) - 1; + update = true; + } + + } + +#ifdef ENABLE_GUI_MOUNT + else if((msg == CRCInput::RC_setup) && !m_inetmode) + { + CNFSSmallMenu nfsMenu; + nfsMenu.exec(this, ""); + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + update = true; + //pushback key if... + //g_RCInput->postMsg( msg, data ); + //loop = false; + } +#endif + else if(msg == NeutrinoMessages::CHANGEMODE) + { + if((data & NeutrinoMessages::mode_mask) != NeutrinoMessages::mode_audio) + { + loop = false; + m_LastMode=data; + } + } + else if(msg == NeutrinoMessages::RECORD_START || + msg == NeutrinoMessages::ZAPTO || + msg == NeutrinoMessages::STANDBY_ON || + msg == NeutrinoMessages::SHUTDOWN || + msg == NeutrinoMessages::SLEEPTIMER) + { + // Exit for Record/Zapto Timers + loop = false; + g_RCInput->postMsg(msg, data); + } + else if(msg == NeutrinoMessages::EVT_TIMER) + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + } + // update mute icon + //paintHead(); + //paintLCD(); + } + } + hide(); + + if(m_state != CAudioPlayerGui::STOP) + stop(); + + return ret; +} + +bool CAudioPlayerGui::playNext(bool allow_rotate) +{ + bool result = false; + + if (!(m_playlist.empty())) + { + int next = getNext(); + if(next >= 0) + play(next); + else if(allow_rotate) + play(0); + else + stop(); + result = true; + } + + return(result); +} + +bool CAudioPlayerGui::playPrev(bool allow_rotate) +{ + bool result = false; + + if (!(m_playlist.empty())) + { + if(m_current == -1) + stop(); + else if(m_current-1 > 0) + play(m_current-1); + else if(allow_rotate) + play(m_playlist.size()-1); + else + play(0); + result = true; + } + + return(result); +} + +bool CAudioPlayerGui::clearPlaylist(void) +{ + bool result = false; + + if (!(m_playlist.empty())) + { + m_playlist.clear(); + m_current = -1; + m_selected = 0; + m_title2Pos.clear(); + result = true; + } + return(result); +} + +bool CAudioPlayerGui::shufflePlaylist(void) +{ + RandomNumber rnd; + bool result = false; + if (!(m_playlist.empty())) + { + if (m_current > 0) + { + std::swap(m_playlist[0], m_playlist[m_current]); + m_current = 0; + } + + std::random_shuffle((m_current != 0) ? m_playlist.begin() : m_playlist.begin() + 1, m_playlist.end(), rnd); + if (m_select_title_by_name) + { + buildSearchTree(); + } + m_playlistHasChanged = true; + m_selected = 0; + + result = true; + } + return(result); +} + +void CAudioPlayerGui::addUrl2Playlist(const char *url, const char *name, const time_t bitrate) { + CAudiofileExt mp3( url, CFile::STREAM_AUDIO ); +// tmp = tmp.substr(0,tmp.length()-4); //remove .url +//printf("[addUrl2Playlist], name = %s, url = %s\n", name, url); + if (name != NULL) { + mp3.MetaData.title = name; + } else { + std::string tmp = mp3.Filename.substr(mp3.Filename.rfind('/')+1); + mp3.MetaData.title = tmp; + } + if (bitrate) + mp3.MetaData.total_time = bitrate; + else + mp3.MetaData.total_time = 0; + + if (url[0] != '#') { + addToPlaylist(mp3); + } +} + +void CAudioPlayerGui::processPlaylistUrl(const char *url, const char *name, const time_t tim) { + CURL *curl_handle; + struct MemoryStruct chunk; + +printf("CAudioPlayerGui::processPlaylistUrl (%s, %s)\n", url, name); + chunk.memory=NULL; /* we expect realloc(NULL, size) to work */ + chunk.size = 0; /* no data at this point */ + + curl_global_init(CURL_GLOBAL_ALL); + + /* init the curl session */ + curl_handle = curl_easy_init(); + + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + + /* some servers don't like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + /* don't use signal for timeout */ + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, (long)1); + + /* set timeout to 10 seconds */ + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, GET_PLAYLIST_TIMEOUT); + + /* get it! */ + curl_easy_perform(curl_handle); + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + /* + * Now, our chunk.memory points to a memory block that is chunk.size + * bytes big and contains the remote file. + * + * Do something nice with it! + * + * You should be aware of the fact that at this point we might have an + * allocated data block, and nothing has yet deallocated that data. So when + * you're done with it, you should free() it as a nice application. + */ + + long res_code; + if (curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE/*CURLINFO_RESPONSE_CODE*/, &res_code ) == CURLE_OK) { + if (200 == res_code) { + //printf("\nchunk = %s\n", chunk.memory); + std::istringstream iss; + iss.str (std::string(chunk.memory, chunk.size)); + char line[512]; + char *ptr; + while (iss.rdstate() == std::ifstream::goodbit) { + iss.getline(line, 512); + if (line[0] != '#') { + //printf("chunk: line = %s\n", line); + ptr = strstr(line, "http://"); + if (ptr != NULL) { + char *tmp; + // strip \n and \r characters from url + tmp = strchr(line, '\r'); + if (tmp != NULL) + *tmp = '\0'; + tmp = strchr(line, '\n'); + if (tmp != NULL) + *tmp = '\0'; + addUrl2Playlist(ptr, name, tim); + } + } + } + } + } + + if(chunk.memory) + free(chunk.memory); + + /* we're done with libcurl, so clean it up */ + curl_global_cleanup(); +} + + +void CAudioPlayerGui::readDir_ic(void) +{ + std::string answer=""; + std::cout << "[readDir_ic] IC URL: " << icecasturl << std::endl; + CURL *curl_handle; + CURLcode httpres; + /* init the curl session */ + curl_handle = curl_easy_init(); + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, icecasturl.c_str()); + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, CurlWriteToString); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_FILE, (void *)&answer); + /* Generate error if http error >= 400 occurs */ + curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1); + /* set timeout to 30 seconds */ + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, GET_ICECAST_TIMEOUT); + + /* error handling */ + char error[CURL_ERROR_SIZE]; + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error); + /* get it! */ + CHintBox *scanBox = new CHintBox(LOCALE_AUDIOPLAYER_ADD_IC, g_Locale->getText(LOCALE_AUDIOPLAYER_RECEIVING_LIST)); // UTF-8 + scanBox->paint(); + + httpres = curl_easy_perform(curl_handle); + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + //std::cout << "Answer:" << std::endl << "----------------" << std::endl << answer << std::endl; + + if (!answer.empty() && httpres == 0) + { + xmlDocPtr answer_parser = parseXml(answer.c_str()); + scanBox->hide(); + scanXmlData(answer_parser, "server_name", "listen_url", "bitrate", true); + } + else + scanBox->hide(); + + delete scanBox; +} + +void CAudioPlayerGui::scanXmlFile(std::string filename) +{ + xmlDocPtr answer_parser = parseXmlFile(filename.c_str()); + scanXmlData(answer_parser, "name", "url"); +} + +void CAudioPlayerGui::scanXmlData(xmlDocPtr answer_parser, const char *nametag, const char *urltag, + const char *bitratetag, bool usechild) +{ +#define IC_typetag "server_type" + + if (answer_parser != NULL) { + xmlNodePtr element = xmlDocGetRootElement(answer_parser); + element = element->xmlChildrenNode; + xmlNodePtr element_tmp = element; + if (element == NULL) { + printf("[openFilebrowser] No valid XML File.\n"); + } else { + CProgressWindow progress; + long maxProgress = 1; + // count # of entries + while (element) { + maxProgress++; + element = element->xmlNextNode; + } + element = element_tmp; + long listPos = -1; + progress.setTitle(LOCALE_AUDIOPLAYER_LOAD_RADIO_STATIONS); + progress.exec(this, ""); + neutrino_msg_t msg; + neutrino_msg_data_t data; + g_RCInput->getMsg(&msg, &data, 0); + while (element && msg != CRCInput::RC_home) { + char *ptr = NULL; + char *name = NULL; + char *url = NULL; + char *type = NULL; + time_t bitrate = 0; + bool skip = true; + listPos++; + // show status + int global = 100*listPos / maxProgress; + progress.showGlobalStatus(global); + #ifdef LCD_UPDATE + CVFD::getInstance()->showProgressBar(global, "read xmldata..."); + CVFD::getInstance()->setMode(CVFD::MODE_PROGRESSBAR); + #endif // LCD_UPDATE + + if (usechild) { + xmlNodePtr child = element->xmlChildrenNode; + while (child) { + if (strcmp(xmlGetName(child), nametag) == 0) + name = xmlGetData(child); + else if (strcmp(xmlGetName(child), urltag) == 0) + url = xmlGetData(child); + else if (strcmp(xmlGetName(child), IC_typetag) == 0) + type = xmlGetData(child); + else if (bitratetag && strcmp(xmlGetName(child), bitratetag) == 0) { + ptr = xmlGetData(child); + if (ptr) + bitrate = atoi(ptr); + } + child = child->xmlNextNode; + } + if (strcmp("audio/mpeg", type) == 0) skip = false; + else if (strcmp("application/ogg", type) == 0) skip = false; + else if (strcmp("mp3", type) == 0) skip = false; + else if (strcmp("application/mp3", type) == 0) skip = false; + } else { + url = xmlGetAttribute(element, (char *) urltag); + name = xmlGetAttribute(element, (char *) nametag); + if (bitratetag) { + ptr = xmlGetAttribute(element, (char *) bitratetag); + if (ptr) + bitrate = atoi(ptr); + } + skip = false; + } + + if ((url != NULL) && !skip) { + progress.showStatusMessageUTF(url); + //printf("Processing %s, %s\n", url, name); + if (strstr(url, ".m3u") || strstr(url, ".pls")) + processPlaylistUrl(url, name); + else + addUrl2Playlist(url, name, bitrate); + } + element = element->xmlNextNode; + g_RCInput->getMsg(&msg, &data, 0); + + } + progress.hide(); + } + xmlFreeDoc(answer_parser); + } + else + printf("[scanXmlData] answer_parser == NULL"); +} + +bool CAudioPlayerGui::openFilebrowser(void) +{ + bool result = false; + CFileBrowser filebrowser((g_settings.filebrowser_denydirectoryleave) + ? g_settings.network_nfs_audioplayerdir : ""); + + filebrowser.Multi_Select = true; + filebrowser.Dirs_Selectable = true; + filebrowser.Filter = &audiofilefilter; + + hide(); + + if (filebrowser.exec(m_Path.c_str())) + { +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval start; + gettimeofday(&start,NULL); +#endif + CProgressWindow progress; + long maxProgress = (filebrowser.getSelectedFiles().size() > 1) ? filebrowser.getSelectedFiles().size() - 1 : 1; + long currentProgress = -1; + if (maxProgress > SHOW_FILE_LOAD_LIMIT) + { + progress.setTitle(LOCALE_AUDIOPLAYER_READING_FILES); + progress.exec(this,""); + } + + m_Path = filebrowser.getCurrentDir(); + CFileList::const_iterator files = filebrowser.getSelectedFiles().begin(); + for(; files != filebrowser.getSelectedFiles().end();files++) + { + if (maxProgress > SHOW_FILE_LOAD_LIMIT) + { + currentProgress++; + // show status + int global = 100*currentProgress/maxProgress; + progress.showGlobalStatus(global); + progress.showStatusMessageUTF(files->Name); + #ifdef LCD_UPDATE + CVFD::getInstance()->showProgressBar(global, "read metadata..."); + CVFD::getInstance()->setMode(CVFD::MODE_PROGRESSBAR); + #endif // LCD_UPDATE + } + if ((files->getType() == CFile::FILE_CDR) + || (files->getType() == CFile::FILE_OGG) + || (files->getType() == CFile::FILE_MP3) + || (files->getType() == CFile::FILE_WAV) +#ifdef ENABLE_FLAC + || (files->getType() == CFile::FILE_FLAC) +#endif + ) + { + CAudiofileExt audiofile(files->Name, + files->getType()); + addToPlaylist(audiofile); + } + if(files->getType() == CFile::STREAM_AUDIO) + { + std::string filename = files->Name; + FILE *fd = fopen(filename.c_str(), "r"); + if(fd) + { + char buf[512]; + unsigned int len = fread(buf, sizeof(char), 512, fd); + fclose(fd); + + if (len && (strstr(buf, ".m3u") || strstr(buf, ".pls"))) + { + printf("m3u/pls Playlist found: %s\n", buf); + filename = buf; + processPlaylistUrl(buf); + } + else + { + addUrl2Playlist(filename.c_str()); + } + } + } + else if(files->getType() == CFile::FILE_PLAYLIST) + { + std::string sPath = files->Name.substr(0, files->Name.rfind('/')); + std::ifstream infile; + char cLine[256]; + infile.open(files->Name.c_str(), std::ifstream::in); + while (infile.good()) + { + infile.getline(cLine, 255); + // remove CR + if(cLine[strlen(cLine)-1]=='\r') + cLine[strlen(cLine)-1]=0; + if(strlen(cLine) > 0 && cLine[0]!='#') + { + char *url = strstr(cLine, "http://"); + if (url != NULL) { + if (strstr(url, ".m3u") || strstr(url, ".pls")) + processPlaylistUrl(url); + } else if ((url = strstr(cLine, "icy://")) != NULL) { + addUrl2Playlist(url); + } else if ((url = strstr(cLine, "scast:://")) != NULL) { + addUrl2Playlist(url); + } + else + { + std::string filename = sPath + '/' + cLine; + + std::string::size_type pos; + while((pos=filename.find('\\'))!=std::string::npos) + filename[pos]='/'; + + std::ifstream testfile; + testfile.open(filename.c_str(), std::ifstream::in); + if(testfile.good()) + { +#ifdef AUDIOPLAYER_CHECK_FOR_DUPLICATES + // Check for duplicates and remove (new entry has higher prio) + // this really needs some time :( + for (unsigned long i=0;igetType() == CFile::FILE_XML) + { + if (!files->Name.empty()) + { + scanXmlFile(files->Name); + } + } + } + if (m_select_title_by_name) + { + buildSearchTree(); + } +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval end; + gettimeofday(&end,NULL); + printf("adding %ld files took: ",maxProgress+1); + printTimevalDiff(start,end); +#endif + result = true; + } + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + // if playlist is turned off -> start playing immediately + if (!m_show_playlist && !m_playlist.empty()) + play(m_selected); + + return ( result); +} + +//------------------------------------------------------------------------ +#define SC_BASE_DIR "http://www.shoutcast.com" +#define SC_INIT_DIR "/sbin/newxml.phtml" +bool CAudioPlayerGui::openSCbrowser(void) +{ + bool result = false; + CFileBrowser filebrowser(SC_BASE_DIR, CFileBrowser::ModeSC); + + filebrowser.Multi_Select = true; + filebrowser.Dirs_Selectable = true; + filebrowser.Filter = NULL;//&audiofilefilter; + + hide(); + + if (filebrowser.exec(SC_INIT_DIR)) + { +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval start; + gettimeofday(&start,NULL); +#endif + CProgressWindow progress; + long maxProgress = (filebrowser.getSelectedFiles().size() > 1) ? filebrowser.getSelectedFiles().size() - 1: 1; + long currentProgress = -1; + //if (maxProgress > SHOW_FILE_LOAD_LIMIT) + { + progress.setTitle(LOCALE_AUDIOPLAYER_READING_FILES); + progress.exec(this,""); + } + + m_Path = filebrowser.getCurrentDir(); + CFileList::const_iterator files = filebrowser.getSelectedFiles().begin(); + neutrino_msg_t msg; + neutrino_msg_data_t data; + g_RCInput->getMsg(&msg, &data, 0); + for(; (files != filebrowser.getSelectedFiles().end()) && (msg != CRCInput::RC_home);files++) + { + //if (maxProgress > SHOW_FILE_LOAD_LIMIT) + { + currentProgress++; + // show progress + int global = 100*currentProgress/maxProgress; + progress.showGlobalStatus(global); + progress.showStatusMessageUTF(files->Name); + #ifdef LCD_UPDATE + CVFD::getInstance()->showProgressBar(global, "read metadata..."); + CVFD::getInstance()->setMode(CVFD::MODE_PROGRESSBAR); + #endif // LCD_UPDATE + } + //printf("processPlaylistUrl(%s, %s)\n", files->Url.c_str(), files->Name.c_str()); + processPlaylistUrl(files->Url.c_str(), files->Name.c_str(), files->Time); + g_RCInput->getMsg(&msg, &data, 0); + } + if (m_select_title_by_name) + { + buildSearchTree(); + } +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval end; + gettimeofday(&end,NULL); + printf("adding %ld files took: ",maxProgress+1); + printTimevalDiff(start,end); +#endif + result = true; + } + CVFD::getInstance()->setMode(CVFD::MODE_AUDIO); + paintLCD(); + // if playlist is turned off -> start playing immediately + if (!m_show_playlist && !m_playlist.empty()) + play(m_selected); + + return ( result); +} + +//------------------------------------------------------------------------ + + +void CAudioPlayerGui::hide() +{ + // printf("hide(){\n"); + if(m_visible) + { + m_frameBuffer->paintBackgroundBoxRel(m_x - ConnectLineBox_Width-1, m_y + m_title_height - 1, + m_width + ConnectLineBox_Width+2, m_height + 2 - m_title_height); + clearItemID3DetailsLine(); + m_frameBuffer->paintBackgroundBoxRel(m_x, m_y, m_width, m_title_height); + m_visible = false; + } +} + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::paintItem(int pos) +{ + if (!m_show_playlist) + return; + + int ypos = m_y + m_title_height + m_theight + pos*m_fheight; + int c_rad_small; + uint8_t color; + fb_pixel_t bgcolor; + + if ((pos + m_liststart) == m_selected) + { + if ((pos + m_liststart) == (unsigned)m_current) + { + color = COL_MENUCONTENTSELECTED + 2; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_2; + } + else + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + paintItemID3DetailsLine(pos); + c_rad_small = RADIUS_SMALL; + } + else + { + if (((pos + m_liststart) < m_playlist.size()) && (pos & 1)) + { + if ((pos + m_liststart) == (unsigned)m_current) + { + color = COL_MENUCONTENTDARK + 2; + bgcolor = COL_MENUCONTENTDARK_PLUS_2; + } + else + { + color = COL_MENUCONTENTDARK; + bgcolor = COL_MENUCONTENTDARK_PLUS_0; + } + } + else + { + if ((pos + m_liststart) == (unsigned)m_current) + { + color = COL_MENUCONTENT + 2; + bgcolor = COL_MENUCONTENT_PLUS_2; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + } + c_rad_small = 0; + } + + m_frameBuffer->paintBoxRel(m_x, ypos, m_width - 15, m_fheight, bgcolor, c_rad_small); + + if ((pos + m_liststart) < m_playlist.size()) + { + if (m_playlist[pos + m_liststart].FileType != CFile::STREAM_AUDIO && + !m_playlist[pos + m_liststart].MetaData.bitrate) + { + // id3tag noch nicht geholt + GetMetaData(m_playlist[pos + m_liststart]); + if(m_state != CAudioPlayerGui::STOP && !g_settings.audioplayer_highprio) + usleep(100*1000); + } + char sNr[20]; + sprintf(sNr, "%2d : ", pos + m_liststart + 1); + std::string tmp = sNr; + getFileInfoToDisplay(tmp, m_playlist[pos + m_liststart]); + + char dura[9]; + if (m_inetmode) + snprintf(dura, 8, "%ldk", m_playlist[pos + m_liststart].MetaData.total_time); + else + snprintf(dura, 8, "%ld:%02ld", m_playlist[pos + m_liststart].MetaData.total_time / 60, + m_playlist[pos + m_liststart].MetaData.total_time % 60); + int w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(dura) + 5; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + 10, ypos + m_fheight, m_width - 30 - w, + tmp, color, m_fheight, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + m_width - 15 - w, ypos + m_fheight, + w, dura, color, m_fheight); + if ((pos + m_liststart) == m_selected) + { + if (m_state == CAudioPlayerGui::STOP) + { + CVFD::getInstance()->showAudioTrack(m_playlist[pos + m_liststart].MetaData.artist, + m_playlist[pos + m_liststart].MetaData.title, + m_playlist[pos + m_liststart].MetaData.album); + } + } + } +} + +//--------------playlist---------------------------------------------------------- + +void CAudioPlayerGui::paintHead() +{ + if (!m_show_playlist) + return; + + int c_rad_mid = RADIUS_MID; + std::string strCaption; + if (m_inetmode) + strCaption = g_Locale->getText(LOCALE_INETRADIO_NAME); + else + strCaption = g_Locale->getText(LOCALE_AUDIOPLAYER_HEAD); + m_frameBuffer->paintBoxRel(m_x, m_y + m_title_height, m_width, m_theight, COL_MENUHEAD_PLUS_0, c_rad_mid, CORNER_TOP); + m_frameBuffer->paintIcon("mp3.raw",m_x + 7, m_y + m_title_height + 10); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(m_x + 35, m_y + m_theight + m_title_height + 0, + m_width - 45, strCaption, COL_MENUHEAD, 0, true); // UTF-8 + int ypos = m_y + m_title_height; + if(m_theight > 26) + ypos = (m_theight - 26) / 2 + m_y + m_title_height; +#ifdef ENABLE_GUI_MOUNT + if (!m_inetmode) + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, m_x + m_width - 30, ypos); +#endif +#if 1 + if( CNeutrinoApp::getInstance()->isMuted() ) + { + int xpos = m_x + m_width - 75; + ypos = m_y + m_title_height; + if(m_theight > 32) + ypos = (m_theight - 32) / 2 + m_y + m_title_height; + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_MUTE, xpos, ypos); + } +#endif +} + +//------------------------------------------------------------------------ +const struct button_label AudioPlayerButtons[][4] = +{ + { + { NEUTRINO_ICON_BUTTON_RED , LOCALE_AUDIOPLAYER_STOP }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_REWIND }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_PAUSE }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_AUDIOPLAYER_FASTFORWARD }, + }, + { + { NEUTRINO_ICON_BUTTON_RED , LOCALE_AUDIOPLAYER_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_ADD }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_DELETEALL }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_AUDIOPLAYER_SHUFFLE }, + }, + { + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_JUMP_BACKWARDS }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_AUDIOPLAYER_JUMP_FORWARDS }, + }, + { + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_JUMP_BACKWARDS }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_AUDIOPLAYER_JUMP_FORWARDS }, + }, + { + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_SAVE_PLAYLIST }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_BUTTON_SELECT_TITLE_BY_ID }, + }, + { + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_SAVE_PLAYLIST }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_BUTTON_SELECT_TITLE_BY_NAME }, + }, + { + { NEUTRINO_ICON_BUTTON_RED , LOCALE_AUDIOPLAYER_STOP }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_PAUSE }, + }, + { + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_ADD }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_INETRADIO_NAME }, + }, + { + { NEUTRINO_ICON_BUTTON_RED , LOCALE_AUDIOPLAYER_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_ADD }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_DELETEALL }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_INETRADIO_NAME }, + }, +}; +//------------------------------------------------------------------------ + +void CAudioPlayerGui::paintFoot() +{ + // printf("paintFoot{\n"); + int top; + int c_rad_mid = RADIUS_MID; + if (m_show_playlist) + top = m_y + (m_height - m_info_height - 2 * m_buttonHeight); + else + top = m_y + (m_height - 2 * m_buttonHeight); + + int ButtonWidth = (m_width - 20) / 4; + int ButtonWidth2 = (m_width - 50) / 2; + m_frameBuffer->paintBoxRel(m_x, top, m_width, 2 * m_buttonHeight, COL_INFOBAR_SHADOW_PLUS_1, c_rad_mid, CORNER_BOTTOM); + m_frameBuffer->paintHLine(m_x, m_x + m_width, top, COL_INFOBAR_SHADOW_PLUS_1); + + if (!m_playlist.empty()) + { + // play + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, m_x + 1 * ButtonWidth2 + 25, top + m_buttonHeight - 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL] + ->RenderString(m_x + 1 * ButtonWidth2 + 53, top + m_buttonHeight + 24 - 4, ButtonWidth2 - 28, + g_Locale->getText(LOCALE_AUDIOPLAYER_PLAY), COL_INFOBAR /*_SHADOW_PLUS_1*/, 0, true); // UTF-8 + // keylevel switch + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, m_x + 0 * ButtonWidth + 25, top + m_buttonHeight - 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL] + ->RenderString(m_x + 0 * ButtonWidth + 53 , top + m_buttonHeight + 24 - 4, ButtonWidth2 - 28, + g_Locale->getText(LOCALE_AUDIOPLAYER_KEYLEVEL), COL_INFOBAR /*_SHADOW_PLUS_1*/, 0, true); // UTF-8 + } + + if (m_key_level == 0) + { + if (m_playlist.empty()) { + if (m_inetmode) + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 2, AudioPlayerButtons[7]); + else + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 1, &(AudioPlayerButtons[7][0])); + } else + if (m_inetmode) + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 4, AudioPlayerButtons[8]); + else + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 4, AudioPlayerButtons[1]); + } + else if (m_key_level == 1) + { + if (m_curr_audiofile.FileType != CFile::STREAM_AUDIO) + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, m_x + 10, top + 4, ButtonWidth, 4, AudioPlayerButtons[0]); + } + else + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, m_x + 10, top + 4, ButtonWidth*2, 2, AudioPlayerButtons[6]); + } + } + else + { // key_level == 2 + if (m_state == CAudioPlayerGui::STOP) + { + if (m_select_title_by_name) + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + ButtonWidth + 10, top + 4, ButtonWidth, 2, AudioPlayerButtons[5]); + } + else + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + ButtonWidth + 10, top + 4, ButtonWidth, 2, AudioPlayerButtons[4]); + } + } + else + { + if (m_select_title_by_name) + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + ButtonWidth + 10, top + 4, ButtonWidth*2, 2, AudioPlayerButtons[3]); + } + else + { + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + ButtonWidth + 10, top + 4, ButtonWidth*2, 2, AudioPlayerButtons[2]); + } + } + } +} +//------------------------------------------------------------------------ +void CAudioPlayerGui::paintInfo() +{ + int c_rad_mid = RADIUS_MID; + if(m_state == CAudioPlayerGui::STOP && m_show_playlist) + m_frameBuffer->paintBackgroundBoxRel(m_x, m_y, m_width, m_title_height); + else + { + if (!m_show_playlist) + { + // no playlist -> smaller Info-Box + m_frameBuffer->paintBoxRel(m_x, m_y, m_width, m_title_height - 10 - m_fheight, COL_MENUCONTENT_PLUS_6, c_rad_mid); + m_frameBuffer->paintBoxRel(m_x + 2, m_y + 2 , m_width - 4, m_title_height - 14 - m_fheight, COL_MENUCONTENTSELECTED_PLUS_0, c_rad_mid); + } + else + { + m_frameBuffer->paintBoxRel(m_x, m_y, m_width, m_title_height - 10, COL_MENUCONTENT_PLUS_6, c_rad_mid); + m_frameBuffer->paintBoxRel(m_x + 2, m_y + 2 , m_width - 4, m_title_height - 14, COL_MENUCONTENTSELECTED_PLUS_0, c_rad_mid); + } + + // first line (Track number) + std::string tmp; + if (m_inetmode) { + tmp = m_curr_audiofile.MetaData.album; + } else { + char sNr[20]; + sprintf(sNr, ": %2d", m_current + 1); + tmp = g_Locale->getText(LOCALE_AUDIOPLAYER_PLAYING); + tmp += sNr ; + } + + int w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + int xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 1*m_fheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + // second line (Artist/Title...) + if (m_curr_audiofile.FileType != CFile::STREAM_AUDIO && + !m_curr_audiofile.MetaData.bitrate) + { + GetMetaData(m_curr_audiofile); + } + + if (m_curr_audiofile.MetaData.title.empty()) + tmp = m_curr_audiofile.MetaData.artist; + else if (m_curr_audiofile.MetaData.artist.empty()) + tmp = m_curr_audiofile.MetaData.title; + else if (g_settings.audioplayer_display == TITLE_ARTIST) + { + tmp = m_curr_audiofile.MetaData.title; + tmp += " / "; + tmp += m_curr_audiofile.MetaData.artist; + } + else //if(g_settings.audioplayer_display == ARTIST_TITLE) + { + tmp = m_curr_audiofile.MetaData.artist; + tmp += " / "; + tmp += m_curr_audiofile.MetaData.title; + } + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + xstart=(m_width-w)/2; + if(xstart < 10) + xstart=10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x+xstart, m_y +4+ 2*m_fheight, m_width- 20, tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + // reset so fields get painted always + m_metainfo.clear(); + m_time_total = 0; + m_time_played = 0; + updateMetaData(); + + updateTimes(true); + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::paint() +{ + if (m_show_playlist) + { + m_liststart = (m_selected / m_listmaxshow) * m_listmaxshow; + paintHead(); + for (unsigned int count=0;countpaintBoxRel(m_x + m_width - 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc = ((m_playlist.size() - 1) / m_listmaxshow) + 1; + int sbs = (m_selected / m_listmaxshow); + + m_frameBuffer->paintBoxRel(m_x + m_width - 13, ypos + 2 + sbs*(sb-4)/sbc , 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3, RADIUS_SMALL); + } + + paintFoot(); + paintInfo(); + m_visible = true; + +} + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::clearItemID3DetailsLine () +{ + paintItemID3DetailsLine(-1); +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::paintItemID3DetailsLine (int pos) +{ + int xpos = m_x - ConnectLineBox_Width; + int ypos1 = m_y + m_title_height + m_theight+ 0 + pos*m_fheight; + int ypos2 = m_y + (m_height - m_info_height); + int ypos1a = ypos1 + (m_fheight / 2) - 2; + int ypos2a = ypos2 + (m_info_height / 2) - 2; + fb_pixel_t col1 = COL_MENUCONTENT_PLUS_6; + fb_pixel_t col2 = COL_MENUCONTENT_PLUS_1; + int c_rad_small = RADIUS_SMALL; + + + // Clear + m_frameBuffer->paintBackgroundBoxRel(xpos - 1, m_y + m_title_height, ConnectLineBox_Width + 1, + m_height - m_title_height); + + // paint Line if detail info (and not valid list pos) + if (!m_playlist.empty() && (pos >= 0)) + { + // 1. col thick line + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 4, ypos1, 4, m_fheight, col2 /*, c_rad_small, CORNER_LEFT*/);//FIXME + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 3, ypos1, 8, m_fheight, col1 /*, c_rad_small, CORNER_LEFT*/); // item marker + + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 4, ypos2, 4, m_info_height, col1); + + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 16, ypos1a, 4, ypos2a - ypos1a, col1); + + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 16, ypos1a, 12, 4, col1); + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 16, ypos2a, 12, 4, col1); + + // 2. col small line + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 4, ypos2, 1, m_info_height, col2); + + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 16, ypos1a, 1, ypos2a - ypos1a + 4, col2); + + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 16, ypos1a, 12, 1, col2); + m_frameBuffer->paintBoxRel(xpos + ConnectLineBox_Width - 12, ypos2a, 8, 1, col2); + + // -- small Frame around infobox + m_frameBuffer->paintBoxRel(m_x, ypos2 , 2 , m_info_height , col1); + m_frameBuffer->paintBoxRel(m_x + m_width - 2, ypos2 , 2 , m_info_height , col1); + m_frameBuffer->paintBoxRel(m_x, ypos2 , m_width -2 , 2 , col1); + m_frameBuffer->paintBoxRel(m_x, ypos2 + m_info_height -2, m_width -2 , 2 , col1); + // m_frameBuffer->paintBoxRel(m_x, ypos2, m_width, m_info_height, col1); + + // paint id3 infobox + m_frameBuffer->paintBoxRel(m_x + 2, ypos2 + 2 , m_width - 4, m_info_height - 4, COL_MENUCONTENTDARK_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + 10, ypos2 + 2 + 1*m_fheight, m_width- 80, + m_playlist[m_selected].MetaData.title, COL_MENUCONTENTDARK, 0, true); // UTF-8 + std::string tmp; + if (m_playlist[m_selected].MetaData.genre.empty()) + tmp = m_playlist[m_selected].MetaData.date; + else if (m_playlist[m_selected].MetaData.date.empty()) + tmp = m_playlist[m_selected].MetaData.genre; + else + { + tmp = m_playlist[m_selected].MetaData.genre; + tmp += " / "; + tmp += m_playlist[m_selected].MetaData.date; + } + int w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true) + 10; // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + m_width - w - 5, ypos2 + 2 + 1*m_fheight, + w, tmp, COL_MENUCONTENTDARK, 0, true); // UTF-8 + tmp = m_playlist[m_selected].MetaData.artist; + if (!(m_playlist[m_selected].MetaData.album.empty())) + { + tmp += " ("; + tmp += m_playlist[m_selected].MetaData.album; + tmp += ')'; + } + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + 10, ypos2 + 2*m_fheight - 2, m_width - 20, + tmp, COL_MENUCONTENTDARK, 0, true); // UTF-8 + } + else + { + m_frameBuffer->paintBackgroundBoxRel(m_x, ypos2, m_width, m_info_height); + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::stop() +{ + m_state = CAudioPlayerGui::STOP; + m_current = 0; + //LCD + paintLCD(); + //Display + paintInfo(); + m_key_level = 0; + paintFoot(); + + if(CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) + CAudioPlayer::getInstance()->stop(); +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::pause() +{ + if(m_state == CAudioPlayerGui::PLAY + || m_state == CAudioPlayerGui::FF + || m_state == CAudioPlayerGui::REV) + { + m_state = CAudioPlayerGui::PAUSE; + CAudioPlayer::getInstance()->pause(); + } + else if(m_state == CAudioPlayerGui::PAUSE) + { + m_state = CAudioPlayerGui::PLAY; + CAudioPlayer::getInstance()->pause(); + } + paintLCD(); +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::ff(unsigned int seconds) +{ + if(m_state == CAudioPlayerGui::FF) + { + m_state = CAudioPlayerGui::PLAY; + CAudioPlayer::getInstance()->ff(seconds); + } + else if(m_state == CAudioPlayerGui::PLAY + || m_state == CAudioPlayerGui::PAUSE + || m_state == CAudioPlayerGui::REV) + { + m_state = CAudioPlayerGui::FF; + CAudioPlayer::getInstance()->ff(seconds); + } + paintLCD(); +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::rev(unsigned int seconds) +{ + if(m_state == CAudioPlayerGui::REV) + { + m_state = CAudioPlayerGui::PLAY; + CAudioPlayer::getInstance()->rev(seconds); + } + else if(m_state == CAudioPlayerGui::PLAY + || m_state == CAudioPlayerGui::PAUSE + || m_state == CAudioPlayerGui::FF) + { + m_state = CAudioPlayerGui::REV; + CAudioPlayer::getInstance()->rev(seconds); + } + paintLCD(); +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::play(unsigned int pos) +{ + //printf("AudioPlaylist: play %d/%d\n",pos,playlist.size()); + unsigned int old_current = m_current; + unsigned int old_selected = m_selected; + + m_current = pos; + if(g_settings.audioplayer_follow) + m_selected = pos; + + if(m_selected - m_liststart >= m_listmaxshow && g_settings.audioplayer_follow) + { + m_liststart = m_selected; + if(!m_screensaver) + paint(); + } + else if(m_liststart < m_selected && g_settings.audioplayer_follow) + { + m_liststart = m_selected - m_listmaxshow + 1; + if(!m_screensaver) + paint(); + } + else + { + if(old_current >= m_liststart && old_current - m_liststart < m_listmaxshow) + { + if(!m_screensaver) + paintItem(old_current - m_liststart); + } + if(pos >= m_liststart && pos - m_liststart < m_listmaxshow) + { + if(!m_screensaver) + paintItem(pos - m_liststart); + } + if(g_settings.audioplayer_follow) + { + if(old_selected >= m_liststart && old_selected - m_liststart < m_listmaxshow) + if(!m_screensaver) + paintItem(old_selected - m_liststart); + } + } + + if (m_playlist[pos].FileType != CFile::STREAM_AUDIO && + !m_playlist[pos].MetaData.bitrate) + { + // id3tag noch nicht geholt + //printf("play: need getMetaData\n"); + GetMetaData(m_playlist[pos]); + } + m_metainfo.clear(); + m_time_played = 0; + m_time_total = m_playlist[m_current].MetaData.total_time; + m_state = CAudioPlayerGui::PLAY; + m_curr_audiofile = m_playlist[m_current]; + // Play + CAudioPlayer::getInstance()->play(&m_curr_audiofile, g_settings.audioplayer_highprio == 1); + //LCD + paintLCD(); + // Display + if(!m_screensaver) + paintInfo(); + m_key_level = 1; + if(!m_screensaver) + paintFoot(); +} +//------------------------------------------------------------------------ + +int CAudioPlayerGui::getNext() +{ + int ret= m_current + 1; + if(m_playlist.empty()) + return -1; + if((unsigned)ret >= m_playlist.size()) + { + if (g_settings.audioplayer_repeat_on == 1) + ret = 0; + else + ret = -1; + } + return ret; +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::updateMetaData() +{ + bool updateMeta = false; + bool updateLcd = false; + bool updateScreen = false; + + if(m_state == CAudioPlayerGui::STOP || !m_show_playlist) + return; + + if( CAudioPlayer::getInstance()->hasMetaDataChanged() + || m_metainfo.empty() ) + { + const CAudioMetaData meta = + CAudioPlayer::getInstance()->getMetaData(); + + std::stringstream info; + info.precision(3); + + if ( meta.bitrate > 0 ) + { + info << " / "; + if ( meta.vbr ) + { + info << "VBR "; + } + info << meta.bitrate/1000 << "kbps"; + } + + if ( meta.samplerate > 0 ) + { + info << " / " << meta.samplerate/1000 << "." << (meta.samplerate/100)%10 <<"kHz"; + } + + m_metainfo = meta.type_info + info.str(); + updateMeta = true; + + if (!meta.artist.empty() && + meta.artist != m_curr_audiofile.MetaData.artist) + { + m_curr_audiofile.MetaData.artist = meta.artist; + updateScreen = true; + updateLcd = true; + } + if (!meta.title.empty() && + meta.title != m_curr_audiofile.MetaData.title) + { + m_curr_audiofile.MetaData.title = meta.title; + updateScreen = true; + updateLcd = true; + } + if (!meta.sc_station.empty() && + meta.sc_station != m_curr_audiofile.MetaData.album) + { + m_curr_audiofile.MetaData.album = meta.sc_station; + updateLcd = true; + } + } + //if (CAudioPlayer::getInstance()->getScBuffered() != 0) + if (CAudioPlayer::getInstance()->hasMetaDataChanged() != 0) + { + updateLcd = true; + } +//printf("CAudioPlayerGui::updateMetaData: updateLcd %d\n", updateLcd); + if(updateLcd) + paintLCD(); + if(updateScreen) + paintInfo(); + if(updateMeta || updateScreen) + { + m_frameBuffer->paintBoxRel(m_x + 10, m_y + 4 + 2*m_fheight, m_width - 20, + m_sheight, COL_MENUCONTENTSELECTED_PLUS_0); + int xstart = ((m_width - 20 - g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(m_metainfo))/2)+10; + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL] + ->RenderString(m_x + xstart, m_y + 4 + 2*m_fheight + m_sheight, + m_width- 2*xstart, m_metainfo, COL_MENUCONTENTSELECTED); + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::updateTimes(const bool force) +{ + if (m_state != CAudioPlayerGui::STOP) + { + bool updateTotal = force; + bool updatePlayed = force; + + if (m_time_total != CAudioPlayer::getInstance()->getTimeTotal()) + { + m_time_total = CAudioPlayer::getInstance()->getTimeTotal(); + if (m_curr_audiofile.MetaData.total_time != CAudioPlayer::getInstance()->getTimeTotal()) + { + m_curr_audiofile.MetaData.total_time = CAudioPlayer::getInstance()->getTimeTotal(); + if(m_current >= 0) + m_playlist[m_current].MetaData.total_time = CAudioPlayer::getInstance()->getTimeTotal(); + } + updateTotal = true; + } + if ((m_time_played != CAudioPlayer::getInstance()->getTimePlayed())) + { + m_time_played = CAudioPlayer::getInstance()->getTimePlayed(); + updatePlayed = true; + } + if(!m_screensaver) + { + char tot_time[11]; + snprintf(tot_time, 10, " / %ld:%02ld", m_time_total / 60, m_time_total % 60); + char tmp_time[8]; + snprintf(tmp_time, 7, "%ld:00", m_time_total / 60); + char play_time[8]; + snprintf(play_time, 7, "%ld:%02ld", m_time_played / 60, m_time_played % 60); + + int w1 = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tot_time); + int w2 = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp_time); + + if (updateTotal) + { + m_frameBuffer->paintBoxRel(m_x + m_width - w1 - 10, m_y + 4, w1 + 4, + m_fheight, COL_MENUCONTENTSELECTED_PLUS_0); + if(m_time_total > 0) + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + m_width - w1 - 10, m_y + 4 + m_fheight, + w1, tot_time, COL_MENUCONTENTSELECTED); + } + if (updatePlayed || (m_state == CAudioPlayerGui::PAUSE)) + { + //m_frameBuffer->paintBoxRel(m_x + m_width - w1 - w2 - 15, m_y + 4, w2 + 4, m_fheight, + m_frameBuffer->paintBoxRel(m_x + m_width - w1 - w2 - 16, m_y + 4, w2 + 5, m_fheight, + COL_MENUCONTENTSELECTED_PLUS_0); + struct timeval tv; + gettimeofday(&tv, NULL); + if ((m_state != CAudioPlayerGui::PAUSE) || (tv.tv_sec & 1)) + { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + m_width - w1 - w2 - 11, m_y + 4 + m_fheight, + w2+4, play_time, COL_MENUCONTENTSELECTED); + } + } + } + if((updatePlayed || updateTotal) && m_time_total != 0) + { + CVFD::getInstance()->showAudioProgress(100 * m_time_played / m_time_total, CNeutrinoApp::getInstance()->isMuted()); + } + } +} + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::paintLCD() +{ + switch(m_state) + { + case CAudioPlayerGui::STOP: + CVFD::getInstance()->showAudioPlayMode(CVFD::AUDIO_MODE_STOP); + CVFD::getInstance()->showAudioProgress(0, CNeutrinoApp::getInstance()->isMuted()); + break; + case CAudioPlayerGui::PLAY: + CVFD::getInstance()->showAudioPlayMode(CVFD::AUDIO_MODE_PLAY); + + CVFD::getInstance()->showAudioTrack(m_curr_audiofile.MetaData.artist, m_curr_audiofile.MetaData.title, + m_curr_audiofile.MetaData.album); + if(m_curr_audiofile.FileType != CFile::STREAM_AUDIO && m_time_total != 0) + CVFD::getInstance()->showAudioProgress(100 * m_time_played / m_time_total, CNeutrinoApp::getInstance()->isMuted()); + +#ifdef INCLUDE_UNUSED_STUFF + else + CVFD::getInstance()->showAudioProgress(100 * CAudioPlayer::getInstance()->getScBuffered() / 65536, CNeutrinoApp::getInstance()->isMuted()); +#endif /* INCLUDE_UNUSED_STUFF */ + break; + case CAudioPlayerGui::PAUSE: + CVFD::getInstance()->showAudioPlayMode(CVFD::AUDIO_MODE_PAUSE); + CVFD::getInstance()->showAudioTrack(m_curr_audiofile.MetaData.artist, m_curr_audiofile.MetaData.title, + m_curr_audiofile.MetaData.album); + break; + case CAudioPlayerGui::FF: + CVFD::getInstance()->showAudioPlayMode(CVFD::AUDIO_MODE_FF); + CVFD::getInstance()->showAudioTrack(m_curr_audiofile.MetaData.artist, m_curr_audiofile.MetaData.title, + m_curr_audiofile.MetaData.album); + break; + case CAudioPlayerGui::REV: + CVFD::getInstance()->showAudioPlayMode(CVFD::AUDIO_MODE_REV); + CVFD::getInstance()->showAudioTrack(m_curr_audiofile.MetaData.artist, m_curr_audiofile.MetaData.title, + m_curr_audiofile.MetaData.album); + break; + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::screensaver(bool on) +{ + if(on) + { + m_screensaver = true; + m_frameBuffer->ClearFrameBuffer(); + stimer = g_RCInput->addTimer(10*1000*1000, false); + } + else + { + if(stimer) + g_RCInput->killTimer(stimer); + stimer = 0; + m_screensaver = false; +#if 0 + m_frameBuffer->loadPal("radiomode.pal", 18, COL_MAXFREE); + m_frameBuffer->useBackground(m_frameBuffer->loadBackground("radiomode.raw"));// set useBackground true or false + m_frameBuffer->paintBackground(); +#endif + paint(); + m_idletime = time(NULL); + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::GetMetaData(CAudiofileExt &File) +{ + // printf("GetMetaData\n"); + bool ret = 1; + + if (CFile::STREAM_AUDIO != File.FileType) + ret = CAudioPlayer::getInstance()->readMetaData( &File, + m_state != CAudioPlayerGui::STOP && + !g_settings.audioplayer_highprio); + + if (!ret || (File.MetaData.artist.empty() && File.MetaData.title.empty() )) + { + //Set from Filename + std::string tmp = File.Filename.substr(File.Filename.rfind('/') + 1); + tmp = tmp.substr(0,tmp.length()-4); //remove extension (.mp3) + std::string::size_type i = tmp.rfind(" - "); + if(i != std::string::npos) + { // Trennzeichen " - " gefunden + File.MetaData.artist = tmp.substr(0, i); + File.MetaData.title = tmp.substr(i + 3); + } + else + { + i = tmp.rfind('-'); + if(i != std::string::npos) + { //Trennzeichen "-" + File.MetaData.artist = tmp.substr(0, i); + File.MetaData.title = tmp.substr(i + 1); + } + else + File.MetaData.title = tmp; + } + File.MetaData.artist = FILESYSTEM_ENCODING_TO_UTF8_STRING(File.MetaData.artist); + File.MetaData.title = FILESYSTEM_ENCODING_TO_UTF8_STRING(File.MetaData.title ); + } +} +//------------------------------------------------------------------------ + +bool CAudioPlayerGui::getNumericInput(neutrino_msg_t& msg, int& val) { + + neutrino_msg_data_t data; + int x1 = (g_settings.screen_EndX - g_settings.screen_StartX) / 2 + g_settings.screen_StartX - 50; + int y1 = (g_settings.screen_EndY - g_settings.screen_StartY) / 2 + g_settings.screen_StartY; + char str[11]; + do + { + val = val * 10 + CRCInput::getNumericValue(msg); + sprintf(str, "%d", val); + int w = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getRenderWidth(str); + int h = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getHeight(); + m_frameBuffer->paintBoxRel(x1 - 7, y1 - h - 5, w + 14, h + 10, COL_MENUCONTENT_PLUS_6); + m_frameBuffer->paintBoxRel(x1 - 4, y1 - h - 3, w + 8, h + 6, COL_MENUCONTENTSELECTED_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->RenderString(x1, y1, w + 1, str, COL_MENUCONTENTSELECTED, 0); + while (true) + { + g_RCInput->getMsg(&msg, &data, 100); + if (msg > CRCInput::RC_MaxRC && msg != CRCInput::RC_timeout) + { // not a key event + CNeutrinoApp::getInstance()->handleMsg(msg, data); + continue; + } + if (msg & (CRCInput::RC_Repeat|CRCInput::RC_Release)) // repeat / release + continue; + break; + } + } while (g_RCInput->isNumeric(msg) && val < 1000000); + return (msg == CRCInput::RC_ok); +} + +//------------------------------------------------------------------------ + + +void CAudioPlayerGui::getFileInfoToDisplay(std::string &info, CAudiofileExt &file) +{ + std::string fileInfo; + std::string artist; + std::string title; + + if (!m_inetmode) { + artist ="Artist?"; + title = "Title?"; + } + + if (!file.MetaData.bitrate) + { + GetMetaData(file); + } + + if (!file.MetaData.artist.empty()) + artist = file.MetaData.artist; + + if (!file.MetaData.title.empty()) + title = file.MetaData.title; + + if(g_settings.audioplayer_display == TITLE_ARTIST) + { + fileInfo += title; + if (!title.empty() && !artist.empty()) fileInfo += ", "; + fileInfo += artist; + } + else //if(g_settings.audioplayer_display == ARTIST_TITLE) + { + fileInfo += artist; + if (!title.empty() && !artist.empty()) fileInfo += ", "; + fileInfo += title; + } + + if (!file.MetaData.album.empty()) + { + fileInfo += " ("; + fileInfo += file.MetaData.album; + fileInfo += ')'; + } + if (fileInfo.empty()) + { + fileInfo += "Unknown"; + } + file.firstChar = tolower(fileInfo[0]); + info += fileInfo; +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::addToPlaylist(CAudiofileExt &file) +{ + //printf("add2Playlist: %s\n", file.Filename.c_str()); + if (m_select_title_by_name) + { + if (!file.MetaData.bitrate) + { + std::string t = ""; + getFileInfoToDisplay(t,file); + } + } + m_playlist.push_back(file); + m_playlistHasChanged = true; +} + +//------------------------------------------------------------------------ +void CAudioPlayerGui::removeFromPlaylist(long pos) +{ + unsigned char firstChar = ' '; + if (m_select_title_by_name) + { + // must be called before m_playlist.erase() + firstChar = getFirstChar(m_playlist[pos]); + } + + m_playlist.erase(m_playlist.begin() + pos); + m_playlistHasChanged = true; + + if (m_select_title_by_name) + { + +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval start; + gettimeofday(&start,NULL); +#endif + //printf("searching for key: %c val: %ld\n",firstChar,pos); + + CTitle2Pos::iterator item = m_title2Pos.find(firstChar); + if (item != m_title2Pos.end()) + { + //const CPosList::size_type del = + item->second.erase(pos); + + // delete empty entries + if (item->second.size() == 0) + { + m_title2Pos.erase(item); + } + } + else + { + printf("could not find key: %c pos: %ld\n",firstChar,pos); + } + // decrease position information for all titles with a position + // behind item to delete + long p = 0; + for (CTitle2Pos::iterator title=m_title2Pos.begin(); + title!=m_title2Pos.end();title++) + { + CPosList newList; + for (CPosList::iterator posIt=title->second.begin(); + posIt!=title->second.end();posIt++) + { + p = *(posIt); + if (*posIt > pos) + p--; + // old list is sorted so we can give a hint to insert at the end + newList.insert(newList.end(), p); + } + //title->second.clear(); + title->second = newList; + } +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval end; + gettimeofday(&end,NULL); + printf("delete took: "); + printTimevalDiff(start,end); +#endif + + } +} + + + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::selectTitle(unsigned char selectionChar) +{ + unsigned long i; + + //printf("fastLookup: key %c\n",selectionChar); + CTitle2Pos::iterator it = m_title2Pos.find(selectionChar); + if (it!=m_title2Pos.end()) + { + // search for the next greater id + // if nothing found take the first + CPosList::iterator posIt = it->second.upper_bound(m_selected); + if (posIt != it->second.end()) + { + i = *posIt; + //printf("upper bound i: %ld\n",i); + } + else + { + if (it->second.size() > 0) + { + i = *(it->second.begin()); + //printf("using begin i: %ld\n",i); + } + else + { + //printf("no title with that key\n"); + return; + } + } + } + else + { + //printf("no title with that key\n"); + return; + } + + int prevselected = m_selected; + m_selected = i; + + paintItem(prevselected - m_liststart); + unsigned int oldliststart = m_liststart; + m_liststart = (m_selected / m_listmaxshow)*m_listmaxshow; + //printf("before paint\n"); + if(oldliststart != m_liststart) + { + paint(); + } + else + { + paintItem(m_selected - m_liststart); + } +} +//------------------------------------------------------------------------ + +void CAudioPlayerGui::printSearchTree() +{ + for (CTitle2Pos::iterator it=m_title2Pos.begin(); + it!=m_title2Pos.end();it++) + { + printf("key: %c\n",it->first); + long pos=-1; + for (CPosList::iterator it2=it->second.begin(); + it2!=it->second.end();it2++) + { + pos++; + printf(" val: %ld ",*it2); + if (pos % 5 == 4) + printf("\n"); + } + printf("\n"); + } +} + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::buildSearchTree() +{ + + // printf("before\n"); + // printSearchTree(); + +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval start; + gettimeofday(&start,NULL); +#endif + + CProgressWindow progress; + progress.setTitle(LOCALE_AUDIOPLAYER_BUILDING_SEARCH_INDEX); + progress.exec(this, ""); + + long maxProgress = (m_playlist.size() > 1) ? m_playlist.size() - 1 : 1; + + m_title2Pos.clear(); + long listPos = -1; + + for (CAudioPlayList::iterator it=m_playlist.begin(); + it!=m_playlist.end();it++) + { + // if (m_state == CAudioPlayerGui::PLAY) + // usleep(10*1000); + listPos++; + progress.showGlobalStatus(100*listPos / maxProgress); + //std::string info; + progress.showStatusMessageUTF(it->Filename); + unsigned char firstChar = getFirstChar(*it); + const std::pair item = + m_title2Pos.insert(CTitle2PosItem(firstChar,CPosList())); + item.first->second.insert(listPos); + } + progress.hide(); + m_playlistHasChanged = false; + +#ifdef AUDIOPLAYER_TIME_DEBUG + timeval end; + gettimeofday(&end,NULL); + printf("searchtree took: "); + printTimevalDiff(start,end); +#endif + // printf("after:\n"); + //printSearchTree(); +} + +//------------------------------------------------------------------------ + +unsigned char CAudioPlayerGui::getFirstChar(CAudiofileExt &file) +{ + if (file.firstChar == '\0') + { + std::string info; + getFileInfoToDisplay(info,file); + } + //printf("getFirstChar: %c\n",file.firstChar); + return file.firstChar; +} + + +//------------------------------------------------------------------------ +#ifdef AUDIOPLAYER_TIME_DEBUG +void CAudioPlayerGui::printTimevalDiff(timeval &start, timeval &end) +{ + + long secs = end.tv_sec - start.tv_sec; + long usecs = end.tv_usec -start.tv_usec; + if (usecs < 0) + { + usecs = 1000000 + usecs; + secs--; + } + printf("%ld:%ld\n",secs,usecs); +} +#endif + +//------------------------------------------------------------------------ + +void CAudioPlayerGui::savePlaylist() +{ + const char *path; + + // .m3u playlist + // http://hanna.pyxidis.org/tech/m3u.html + + CFileBrowser browser; + browser.Multi_Select = false; + browser.Dir_Mode = true; + CFileFilter dirFilter; + dirFilter.addFilter("m3u"); + browser.Filter = &dirFilter; + // select preferred directory if exists + if (strlen(g_settings.network_nfs_audioplayerdir) != 0) + path = g_settings.network_nfs_audioplayerdir; + else + path = "/"; + + // let user select target directory + this->hide(); + if (browser.exec(path)) + { + // refresh view + this->paint(); + CFile *file = browser.getSelectedFile(); + std::string absPlaylistDir = file->getPath(); + + // add a trailing slash if necessary + if ((absPlaylistDir.empty()) || ((*(absPlaylistDir.rbegin()) != '/'))) + { + absPlaylistDir += '/'; + } + absPlaylistDir += file->getFileName(); + + const int filenamesize = 30; + char filename[filenamesize + 1] = ""; + + if (file->getType() == CFile::FILE_PLAYLIST) + { + // file is playlist so we should ask if we can overwrite it + std::string name = file->getPath(); + name += '/'; + name += file->getFileName(); + bool overwrite = askToOverwriteFile(name); + if (!overwrite) + { + return; + } + snprintf(filename, name.size(), "%s", name.c_str()); + } + else if (file->getType() == CFile::FILE_DIR) + { + // query for filename + this->hide(); + CStringInputSMS filenameInput(LOCALE_AUDIOPLAYER_PLAYLIST_NAME, + filename, + filenamesize - 1, + LOCALE_AUDIOPLAYER_PLAYLIST_NAME_HINT1, + LOCALE_AUDIOPLAYER_PLAYLIST_NAME_HINT2, + "abcdefghijklmnopqrstuvwxyz0123456789-.,:!?/ "); + filenameInput.exec(NULL, ""); + // refresh view + this->paint(); + std::string name = absPlaylistDir; + name += '/'; + name += filename; + name += ".m3u"; + std::ifstream input(name.c_str()); + + // test if file exists and query for overwriting it or not + if (input.is_open()) + { + bool overwrite = askToOverwriteFile(name); + if (!overwrite) + { + return; + } + } + input.close(); + } + else + { + std::cout << "CAudioPlayerGui: neither .m3u nor directory selected, abort" << std::endl; + return; + } + std::string absPlaylistFilename = absPlaylistDir; + absPlaylistFilename += '/'; + absPlaylistFilename += filename; + absPlaylistFilename += ".m3u"; + std::ofstream playlistFile(absPlaylistFilename.c_str()); + std::cout << "CAudioPlayerGui: writing playlist to " << absPlaylistFilename << std::endl; + if (!playlistFile) + { + // an error occured + const int msgsize = 255; + char msg[msgsize] = ""; + snprintf(msg, + msgsize, + "%s\n%s", + g_Locale->getText(LOCALE_AUDIOPLAYER_PLAYLIST_FILEERROR_MSG), + absPlaylistFilename.c_str()); + + DisplayErrorMessage(msg); + // refresh view + this->paint(); + std::cout << "CAudioPlayerGui: could not create play list file " + << absPlaylistFilename << std::endl; + return; + } + // writing .m3u file + playlistFile << "#EXTM3U" << std::endl; + + CAudioPlayList::const_iterator it; + for (it = m_playlist.begin();it!=m_playlist.end();it++) + { + playlistFile << "#EXTINF:" << it->MetaData.total_time << "," + << it->MetaData.artist << " - " << it->MetaData.title << std::endl; + if (m_inetmode) + playlistFile << it->Filename << std::endl; + else + playlistFile << absPath2Rel(absPlaylistDir, it->Filename) << std::endl; + } + playlistFile.close(); + } + this->paint(); +} +//------------------------------------------------------------------------ + +bool CAudioPlayerGui::askToOverwriteFile(const std::string& filename) { + + char msg[filename.length() + 127]; + snprintf(msg, + filename.length() + 126, + "%s\n%s", + g_Locale->getText(LOCALE_AUDIOPLAYER_PLAYLIST_FILEOVERWRITE_MSG), + filename.c_str()); + bool res = (ShowMsgUTF(LOCALE_AUDIOPLAYER_PLAYLIST_FILEOVERWRITE_TITLE, + msg,CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo) + == CMessageBox::mbrYes); + this->paint(); + return res; +} +//------------------------------------------------------------------------ + +std::string CAudioPlayerGui::absPath2Rel(const std::string& fromDir, + const std::string& absFilename) { + std::string res = ""; + + int length = fromDir.length() < absFilename.length() ? fromDir.length() : absFilename.length(); + int lastSlash = 0; + // find common prefix for both paths + // fromDir: /foo/bar/angle/1 (length: 16) + // absFilename: /foo/bar/devil/2/fire.mp3 (length: 19) + // -> /foo/bar/ is prefix, lastSlash will be 8 + for (int i=0;i + +#include +#include +#include +#include +#include + +typedef std::set CPosList; + +typedef std::map CTitle2Pos; +typedef std::pair CTitle2PosItem; + +class CAudiofileExt : public CAudiofile +{ +public: + + CAudiofileExt(); + + CAudiofileExt(std::string name, CFile::FileType type); + + CAudiofileExt(const CAudiofileExt& src); + + void operator=(const CAudiofileExt& src); + + + char firstChar; +}; + +typedef std::vector CAudioPlayList; + +class RandomNumber +{ + public: + RandomNumber() + { + srand(time(0)); + } + + int operator()(int n) + { + return ((long long)n * rand() / RAND_MAX); + } +}; + +class CAudioPlayerGui : public CMenuTarget +{ + public: + enum State + { + PLAY=0, + STOP, + PAUSE, + FF, + REV + }; + + enum DisplayOrder {ARTIST_TITLE = 0, TITLE_ARTIST=1}; + + private: + void Init(void); + CFrameBuffer * m_frameBuffer; + unsigned int m_selected; + int m_current; + unsigned int m_liststart; + unsigned int m_listmaxshow; + int m_fheight; // Fonthoehe Playlist-Inhalt + int m_theight; // Fonthoehe Playlist-Titel + int m_sheight; // Fonthoehe MP Info + int m_buttonHeight; + int m_title_height; + int m_info_height; + int m_key_level; + bool m_visible; + State m_state; + time_t m_time_total; + time_t m_time_played; + std::string m_metainfo; + bool m_select_title_by_name; + bool m_show_playlist; + + bool m_playlistHasChanged; + + CAudioPlayList m_playlist; + CAudioPlayList m_radiolist; + CAudioPlayList m_filelist; + CTitle2Pos m_title2Pos; + CAudiofileExt m_curr_audiofile; + std::string m_Path; + + int m_width; + int m_height; + int m_x; + int m_y; + int m_title_w; + + int m_LastMode; + int m_idletime; + bool m_screensaver; + bool m_vol_ost; + bool m_inetmode; + uint32_t stimer; + + SMSKeyInput m_SMSKeyInput; + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void paintInfo(); + void paintLCD(); + void hide(); + + void get_id3(CAudiofileExt * audiofile); + void get_mp3info(CAudiofileExt * audiofile); + CFileFilter audiofilefilter; + void paintItemID3DetailsLine (int pos); + void clearItemID3DetailsLine (); + void play(unsigned int pos); + void stop(); + void pause(); + void ff(unsigned int seconds=0); + void rev(unsigned int seconds=0); + int getNext(); + void GetMetaData(CAudiofileExt &File); + void updateMetaData(); + void updateTimes(const bool force = false); + void showMetaData(); + void screensaver(bool on); + bool getNumericInput(neutrino_msg_t& msg,int& val); + + void addToPlaylist(CAudiofileExt &file); + void removeFromPlaylist(long pos); + + /** + * Adds an url (shoutcast, ...) to the to the audioplayer playlist + */ + void addUrl2Playlist(const char *url, const char *name = NULL, const time_t bitrate = 0); + + /** + * Adds a url which points to an .m3u format (playlist, ...) to the audioplayer playlist + */ + void processPlaylistUrl(const char *url, const char *name = NULL, const time_t bitrate = 0); + + /** + * Loads a given XML file of internet audiostreams or playlists and processes them + */ + void scanXmlFile(std::string filename); + + /** + * Processes a loaded XML file/data of internet audiostreams or playlists + */ + void scanXmlData(xmlDocPtr answer_parser, const char *nametag, const char *urltag, const char *bitratetag = NULL, bool usechild = false); + + /** + * Reads the icecast directory (XML file) and calls scanXmlData + */ + void readDir_ic(void); + + void selectTitle(unsigned char selectionChar); + /** + * Appends the file information to the given string. + * @param fileInfo a string where the file information will be appended + * @param file the file to return the information for + */ + void getFileInfoToDisplay(std::string& fileInfo, CAudiofileExt &file); + + void printSearchTree(); + + void buildSearchTree(); + + unsigned char getFirstChar(CAudiofileExt &file); + + void printTimevalDiff(timeval &start, timeval &end); + + /** + * Saves the current playlist into a .m3u playlist file. + */ + void savePlaylist(); + + /** + * Converts an absolute filename to a relative one + * as seen from a file in fromDir. + * Example: + * absFilename: /mnt/audio/A/abc.mp3 + * fromDir: /mnt/audio/B + * => ../A/abc.mp3 will be returned + * @param fromDir the directory from where we want to + * access the file + * @param absFilename the file we want to access in a + * relative way from fromDir (given as an absolute path) + * @return the location of absFilename as seen from fromDir + * (relative path) + */ + std::string absPath2Rel(const std::string& fromDir, + const std::string& absFilename); + + /** + * Asks the user if the file filename should be overwritten or not + * @param filename the name of the file + * @return true if file should be overwritten, false otherwise + */ + bool askToOverwriteFile(const std::string& filename); + bool openFilebrowser(void); + bool openSCbrowser(void); + bool clearPlaylist(void); + bool shufflePlaylist(void); + bool playNext(bool allow_rotate = false); + bool playPrev(bool allow_rotate = false); + + public: + CAudioPlayerGui(bool inetmode = false); + ~CAudioPlayerGui(); + int show(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/bedit/Makefile.am b/src/gui/bedit/Makefile.am new file mode 100644 index 000000000..8d78d480e --- /dev/null +++ b/src/gui/bedit/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/daemons \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/xmltree \ + @FREETYPE_CFLAGS@ \ + -I$(top_srcdir)/lib + + +noinst_LIBRARIES = libneutrino_gui_bedit.a + +libneutrino_gui_bedit_a_SOURCES = \ + bouqueteditor_bouquets.cpp \ + bouqueteditor_channels.cpp \ + bouqueteditor_chanselect.cpp + diff --git a/src/gui/bedit/bouqueteditor_bouquets.cpp b/src/gui/bedit/bouqueteditor_bouquets.cpp new file mode 100644 index 000000000..797caf4c6 --- /dev/null +++ b/src/gui/bedit/bouqueteditor_bouquets.cpp @@ -0,0 +1,579 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +extern CBouquetManager *g_bouquetManager; + +#define ROUND_RADIUS 9 + +CBEBouquetWidget::CBEBouquetWidget() +{ + frameBuffer = CFrameBuffer::getInstance(); + + ButtonHeight = 25; + + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + + selected = 0; + liststart = 0; + state = beDefault; + blueFunction = beRename; + Bouquets = &g_bouquetManager->Bouquets; +} + +void CBEBouquetWidget::paintItem(int pos) +{ + uint8_t color; + fb_pixel_t bgcolor; + int ypos = y+ theight+0 + pos*fheight; + unsigned int current = liststart + pos; + + if (current == selected) { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, ROUND_RADIUS, 3); + } else { + bool has_channels = true; + if(current < Bouquets->size()) + has_channels = ((*Bouquets)[current]->tvChannels.size() > 0) || ((*Bouquets)[current]->radioChannels.size() > 0); + color = has_channels ? COL_MENUCONTENT : COL_MENUCONTENTINACTIVE; + bgcolor = has_channels ? COL_MENUCONTENT_PLUS_0 : COL_MENUCONTENTINACTIVE_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor); + } + + if(current < Bouquets->size()) { + if ((current == selected) && (state == beMoving)) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_YELLOW, x + 8, ypos+4); + + if ((*Bouquets)[current]->bLocked) + frameBuffer->paintIcon("lock.raw", x + 7, ypos); + + if ((*Bouquets)[current]->bHidden) + frameBuffer->paintIcon("hidden.raw", x + 37, ypos); + + //g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+68, ypos+ fheight, width-68, (*Bouquets)[current]->Name, color, 0, true); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+68, ypos+ fheight, width-68, (*Bouquets)[current]->bFav ? g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME) : (*Bouquets)[current]->Name, color, 0, true); + } +} + +void CBEBouquetWidget::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((Bouquets->size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); +} + +void CBEBouquetWidget::paintHead() +{ + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+theight+0, width, g_Locale->getText(LOCALE_BOUQUETLIST_HEAD), COL_MENUHEAD, 0, true); // UTF-8 +} + +const struct button_label CBEBouquetWidgetButtons[3] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_BOUQUETEDITOR_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_BOUQUETEDITOR_ADD }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_BOUQUETEDITOR_MOVE } +}; + +void CBEBouquetWidget::paintFoot() +{ + struct button_label Button[4]; + Button[0] = CBEBouquetWidgetButtons[0]; + Button[1] = CBEBouquetWidgetButtons[1]; + Button[2] = CBEBouquetWidgetButtons[2]; + Button[3].button = NEUTRINO_ICON_BUTTON_BLUE; + + frameBuffer->paintBoxRel(x,y+height, width,ButtonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + //frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + switch( blueFunction) + { + case beRename: + Button[3].locale = LOCALE_BOUQUETEDITOR_RENAME; + break; + case beHide: + Button[3].locale = LOCALE_BOUQUETEDITOR_HIDE; + break; + case beLock: + Button[3].locale = LOCALE_BOUQUETEDITOR_LOCK; + break; + } + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 5, y + height + 4, (width - 28 - 10) / 4, 4, Button); + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, x+width - 28, y+height); +} + +void CBEBouquetWidget::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height+ButtonHeight); +} + +int CBEBouquetWidget::exec(CMenuTarget* parent, const std::string & actionKey) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + if (parent) + parent->hide(); + + width = w_max (500, 0); + height = h_max (440, 50); + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + Bouquets = &g_bouquetManager->Bouquets; + paintHead(); + paint(); + paintFoot(); + + bouquetsChanged = false; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + if ((msg == CRCInput::RC_timeout) || + (msg == (neutrino_msg_t)g_settings.key_channelList_cancel)) + { + if (state == beDefault) + { + if (bouquetsChanged) + { + int result = ShowLocalizedMessage(LOCALE_BOUQUETEDITOR_NAME, LOCALE_BOUQUETEDITOR_SAVECHANGES, CMessageBox::mbrYes, CMessageBox::mbAll); + + switch( result ) + { + case CMessageBox::mbrYes : + loop=false; + saveChanges(); + break; + case CMessageBox::mbrNo : + loop=false; + discardChanges(); + break; + case CMessageBox::mbrCancel : + paintHead(); + paint(); + paintFoot(); + break; + } + } + else + { + loop = false; + } + } + else if (state == beMoving) + { + cancelMoveBouquet(); + } + } + // + // -- For more convenience: include browsing of list (paging) (rasc, 2002-04-02) + // -- The keys should be configurable. Problem is: red/green key, which is the + // -- default in neutrino is used as a function key here... so use left/right + // + else if (msg==CRCInput::RC_up || msg==(neutrino_msg_t)g_settings.key_channelList_pageup) + { + if (!(Bouquets->empty())) + { + int step = 0; + int prev_selected = selected; + + step = (msg == (neutrino_msg_t)g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + { + selected = Bouquets->size()-1; + } + + if (state == beDefault) + { + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if (state == beMoving) + { + internalMoveBouquet(prev_selected, selected); + } + } + } + else if (msg==CRCInput::RC_down || msg==(neutrino_msg_t)g_settings.key_channelList_pagedown) + { + unsigned int step = 0; + int prev_selected = selected; + + step = (msg == (neutrino_msg_t)g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= Bouquets->size()) + { + if (((Bouquets->size() / listmaxshow) + 1) * listmaxshow == Bouquets->size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((Bouquets->size() / listmaxshow) + 1) * listmaxshow))) ? (Bouquets->size() - 1) : 0; + } + + if (state == beDefault) + { + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if (state == beMoving) + { + internalMoveBouquet(prev_selected, selected); + } + } + else if(msg==CRCInput::RC_red) + { + if (state == beDefault) + deleteBouquet(); + } + else if(msg==CRCInput::RC_green) + { + if (state == beDefault) + addBouquet(); + } + else if(msg==CRCInput::RC_yellow) + { + if (selected < Bouquets->size()) /* Bouquets->size() might be 0 */ + { + liststart = (selected/listmaxshow)*listmaxshow; + if (state == beDefault) + beginMoveBouquet(); + paintItem(selected - liststart); + } + } + else if(msg==CRCInput::RC_blue) + { + if (selected < Bouquets->size()) /* Bouquets->size() might be 0 */ + { + if (state == beDefault) + switch (blueFunction) + { + case beRename: + renameBouquet(); + break; + case beHide: + switchHideBouquet(); + break; + case beLock: + switchLockBouquet(); + break; + } + } + } + else if(msg==CRCInput::RC_setup) + { + if (state == beDefault) + switch (blueFunction) + { + case beRename: + blueFunction = beHide; + break; + case beHide: + blueFunction = beLock; + break; + case beLock: + blueFunction = beRename; + break; + } + paintFoot(); + } + else if(msg==CRCInput::RC_ok) + { + if (state == beDefault) + { + if (selected < Bouquets->size()) /* Bouquets->size() might be 0 */ + { + //CBEChannelWidget* channelWidget = new CBEChannelWidget((*Bouquets)[selected]->Name, selected); + CBEChannelWidget* channelWidget = new CBEChannelWidget((*Bouquets)[selected]->bFav ? g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME) : (*Bouquets)[selected]->Name, selected); + channelWidget->exec( this, ""); + if (channelWidget->hasChanged()) + bouquetsChanged = true; + delete channelWidget; + paintHead(); + paint(); + paintFoot(); + } + } + else if (state == beMoving) + { + finishMoveBouquet(); + } + } + else if( CRCInput::isNumeric(msg) ) + { + if (state == beDefault) + { + //kein pushback - wenn man versehentlich wo draufkommt is die edit-arbeit umsonst + //selected = oldselected; + //g_RCInput->postMsg( msg, data ); + //loop=false; + } + else if (state == beMoving) + { + cancelMoveBouquet(); + } + } + else + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + // kein canceling... + } + } + hide(); + return res; +} + +void CBEBouquetWidget::deleteBouquet() +{ + if (selected >= Bouquets->size()) /* Bouquets->size() might be 0 */ + return; + + if (ShowMsgUTF(LOCALE_FILEBROWSER_DELETE, (*Bouquets)[selected]->bFav ? g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME) : (*Bouquets)[selected]->Name, CMessageBox::mbrNo, CMessageBox::mbYes|CMessageBox::mbNo)!=CMessageBox::mbrYes) + return; + + //g_Zapit->deleteBouquet(selected); + //Bouquets.clear(); + //g_Zapit->getBouquets(Bouquets, true, true); + g_bouquetManager->deleteBouquet(selected); + Bouquets = &g_bouquetManager->Bouquets; + if (selected >= Bouquets->size()) + selected = Bouquets->empty() ? 0 : (Bouquets->size() - 1); + bouquetsChanged = true; + paint(); +} + +void CBEBouquetWidget::addBouquet() +{ + std::string newName = inputName("", LOCALE_BOUQUETEDITOR_BOUQUETNAME); + if (!(newName.empty())) + { + //g_Zapit->addBouquet(ZapitTools::Latin1_to_UTF8(newName.c_str()).c_str()); + //Bouquets.clear(); + //g_Zapit->getBouquets(Bouquets, true, true); + g_bouquetManager->addBouquet(newName, true); + Bouquets = &g_bouquetManager->Bouquets; + selected = Bouquets->empty() ? 0 : (Bouquets->size() - 1); + bouquetsChanged = true; + } + paintHead(); + paint(); + paintFoot(); +} + +void CBEBouquetWidget::beginMoveBouquet() +{ + state = beMoving; + origPosition = selected; + newPosition = selected; +} + +void CBEBouquetWidget::finishMoveBouquet() +{ + state = beDefault; + if (newPosition != origPosition) + { + //g_Zapit->moveBouquet(origPosition, newPosition); + //Bouquets.clear(); + //g_Zapit->getBouquets(Bouquets, true, true); + Bouquets = &g_bouquetManager->Bouquets; + bouquetsChanged = true; + } + paint(); +} + +void CBEBouquetWidget::cancelMoveBouquet() +{ + state = beDefault; + internalMoveBouquet( newPosition, origPosition); + bouquetsChanged = false; +} + +void CBEBouquetWidget::internalMoveBouquet( unsigned int fromPosition, unsigned int toPosition) +{ + if ( (int) toPosition == -1 ) return; + if ( toPosition == Bouquets->size()) return; + + //g_Zapit->moveBouquet(fromPosition, toPosition); + g_bouquetManager->moveBouquet(fromPosition, toPosition); + Bouquets = &g_bouquetManager->Bouquets; + bouquetsChanged = true; +#if 0 + CZapitClient::responseGetBouquets Bouquet = Bouquets[fromPosition]; + if (fromPosition < toPosition) + { + for (unsigned int i=fromPosition; i toPosition) + { + for (unsigned int i=fromPosition; i>toPosition; i--) + Bouquets[i] = Bouquets[i-1]; + } + Bouquets[toPosition] = Bouquet; +#endif + selected = toPosition; + newPosition = toPosition; + paint(); +} + +void CBEBouquetWidget::renameBouquet() +{ + if ((*Bouquets)[selected]->bFav) + return; + + std::string newName = inputName((*Bouquets)[selected]->Name.c_str(), LOCALE_BOUQUETEDITOR_NEWBOUQUETNAME); + if (newName != (*Bouquets)[selected]->Name) + { + //g_Zapit->renameBouquet(selected, ZapitTools::Latin1_to_UTF8(newName.c_str()).c_str()); + //Bouquets.clear(); + //g_Zapit->getBouquets(Bouquets, true, true); + + g_bouquetManager->Bouquets[selected]->Name = newName; + g_bouquetManager->Bouquets[selected]->bUser = true; + //Bouquets = &g_bouquetManager->Bouquets; + bouquetsChanged = true; + } + paintHead(); + paint(); + paintFoot(); +} + +void CBEBouquetWidget::switchHideBouquet() +{ + bouquetsChanged = true; + (*Bouquets)[selected]->bHidden = !(*Bouquets)[selected]->bHidden; + //g_Zapit->setBouquetHidden(selected, Bouquets[selected].hidden); + paint(); +} + +void CBEBouquetWidget::switchLockBouquet() +{ + bouquetsChanged = true; + (*Bouquets)[selected]->bLocked = !(*Bouquets)[selected]->bLocked; + //g_Zapit->setBouquetLock(selected, Bouquets[selected].locked); + paint(); +} + +std::string CBEBouquetWidget::inputName(const char * const defaultName, const neutrino_locale_t caption) +{ + char Name[30]; + + strncpy(Name, defaultName, 30); + + CStringInputSMS * nameInput = new CStringInputSMS(caption, Name, 29, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.,:|!?/ "); + nameInput->exec(this, ""); + delete nameInput; + + return std::string(Name); +} + +void CBEBouquetWidget::saveChanges() +{ + CHintBox* hintBox= new CHintBox(LOCALE_BOUQUETEDITOR_NAME, g_Locale->getText(LOCALE_BOUQUETEDITOR_SAVINGCHANGES), 480); // UTF-8 + hintBox->paint(); + g_Zapit->saveBouquets(); + hintBox->hide(); + delete hintBox; +} + +void CBEBouquetWidget::discardChanges() +{ + CHintBox* hintBox= new CHintBox(LOCALE_BOUQUETEDITOR_NAME, g_Locale->getText(LOCALE_BOUQUETEDITOR_DISCARDINGCHANGES), 480); // UTF-8 + hintBox->paint(); + g_Zapit->restoreBouquets(); + hintBox->hide(); + delete hintBox; +} diff --git a/src/gui/bedit/bouqueteditor_bouquets.h b/src/gui/bedit/bouqueteditor_bouquets.h new file mode 100644 index 000000000..4a0cd2ac4 --- /dev/null +++ b/src/gui/bedit/bouqueteditor_bouquets.h @@ -0,0 +1,121 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __bouqueteditor_bouquets__ +#define __bouqueteditor_bouquets__ + +#include +#include + +#include +#include +#include + +#include + +/* class for handling when bouquets changed. */ +/* This class should be a temporarily work around */ +/* and should be replaced by standard neutrino event handlers */ +/* (libevent) */ +class CBouquetEditorEvents +{ +public: + virtual void onBouquetsChanged() {}; +}; + +class CBEBouquetWidget : public CMenuWidget +{ + + private: + + CFrameBuffer *frameBuffer; + + enum + { + beDefault, + beMoving + } state; + + enum + { + beRename, + beHide, + beLock + } blueFunction; + + unsigned int selected; + unsigned int origPosition; + unsigned int newPosition; + + unsigned int liststart; + unsigned int listmaxshow; + int fheight; // Fonthoehe Bouquetlist-Inhalt + int theight; // Fonthoehe Bouquetlist-Titel + + int ButtonHeight; + //std::string name; + bool bouquetsChanged; + int width; + int height; + int x; + int y; + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void hide(); + + void deleteBouquet(); + void addBouquet(); + void beginMoveBouquet(); + void finishMoveBouquet(); + void cancelMoveBouquet(); + void internalMoveBouquet( unsigned int fromPosition, unsigned int toPosition); + void renameBouquet(); + void switchHideBouquet(); + void switchLockBouquet(); + + void saveChanges(); + void discardChanges(); + + std::string inputName(const char * const defaultName, const neutrino_locale_t caption); + + public: + CBEBouquetWidget(); + + //CZapitClient::BouquetList Bouquets; + BouquetList * Bouquets; + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/bedit/bouqueteditor_channels.cpp b/src/gui/bedit/bouqueteditor_channels.cpp new file mode 100644 index 000000000..e0e48abe9 --- /dev/null +++ b/src/gui/bedit/bouqueteditor_channels.cpp @@ -0,0 +1,417 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +extern tallchans allchans; +extern CBouquetManager *g_bouquetManager; + +#define ROUND_RADIUS 9 + +CBEChannelWidget::CBEChannelWidget(const std::string & Caption, unsigned int Bouquet) +{ + frameBuffer = CFrameBuffer::getInstance(); + selected = 0; + ButtonHeight = 25; + + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + + liststart = 0; + state = beDefault; + caption = Caption; + bouquet = Bouquet; + mode = CZapitClient::MODE_TV; +} + +void CBEChannelWidget::paintItem(int pos) +{ + uint8_t color; + fb_pixel_t bgcolor; + int ypos = y+ theight+0 + pos*fheight; + unsigned int current = liststart + pos; + + if(current == selected) { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, ROUND_RADIUS, 3); + } else { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor); + } + + if ((current == selected) && (state == beMoving)) + { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_YELLOW, x + 8, ypos+4); + } + if(current < Channels->size()) + { + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 5+ numwidth+ 10, ypos+ fheight, width- numwidth- 20- 15, (*Channels)[current]->getName(), color, 0, true); + } +} + +void CBEChannelWidget::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + int lastnum = liststart + listmaxshow; + + if(lastnum<10) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("0"); + else if(lastnum<100) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("00"); + else if(lastnum<1000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("000"); + else if(lastnum<10000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("0000"); + else // if(lastnum<100000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("00000"); + + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((Channels->size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); +} + +void CBEChannelWidget::paintHead() +{ + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+theight+0, width, caption.c_str() , COL_MENUHEAD, 0, true); +} + +const struct button_label CBEChannelWidgetButtons[4] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_BOUQUETEDITOR_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_BOUQUETEDITOR_ADD }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_BOUQUETEDITOR_MOVE }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_BOUQUETEDITOR_SWITCHMODE } +}; + +void CBEChannelWidget::paintFoot() +{ + frameBuffer->paintBoxRel(x,y+height, width,ButtonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + //frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + height + 4, (width - 20) / 4, 4, CBEChannelWidgetButtons); +} + +void CBEChannelWidget::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height+ButtonHeight); +} + +int CBEChannelWidget::exec(CMenuTarget* parent, const std::string & actionKey) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + if (parent) + parent->hide(); + + + width = w_max (500, 0); + height = h_max (440, 50); + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + Channels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + paintHead(); + paint(); + paintFoot(); + + channelsChanged = false; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + if ((msg == CRCInput::RC_timeout) || + (msg == (neutrino_msg_t)g_settings.key_channelList_cancel)) + { + if (state == beDefault) + { + loop = false; + } + else if (state == beMoving) + { + cancelMoveChannel(); + } + } + else if (msg==CRCInput::RC_up || msg==(neutrino_msg_t)g_settings.key_channelList_pageup) + { + if (!(Channels->empty())) + { + int step = 0; + int prev_selected = selected; + + step = (msg==(neutrino_msg_t)g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + { + selected = Channels->size() - 1; + } + + if (state == beDefault) + { + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if (state == beMoving) + { + internalMoveChannel(prev_selected, selected); + } + } + } + else if (msg==CRCInput::RC_down || msg==(neutrino_msg_t)g_settings.key_channelList_pagedown) + { + unsigned int step = 0; + int prev_selected = selected; + + step = (msg==(neutrino_msg_t)g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= Channels->size()) + { + if (((Channels->size() / listmaxshow) + 1) * listmaxshow == Channels->size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((Channels->size() / listmaxshow) + 1) * listmaxshow))) ? (Channels->size() - 1) : 0; + } + + if (state == beDefault) + { + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if (state == beMoving) + { + internalMoveChannel(prev_selected, selected); + } + } + else if(msg==CRCInput::RC_red) + { + if (state == beDefault) + deleteChannel(); + } + else if(msg==CRCInput::RC_green) + { + if (state == beDefault) + addChannel(); + } + else if(msg==CRCInput::RC_blue) + { + if (state == beDefault) + { + if (mode == CZapitClient::MODE_TV) + mode = CZapitClient::MODE_RADIO; + else + mode = CZapitClient::MODE_TV; + + Channels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + + selected = 0; + paint(); + } + } + else if(msg==CRCInput::RC_yellow) + { + liststart = (selected/listmaxshow)*listmaxshow; + if (state == beDefault) + beginMoveChannel(); + paintItem(selected - liststart); + } + else if(msg==CRCInput::RC_ok) + { + if (state == beDefault) + { + if (selected < Channels->size()) /* Channels.size() might be 0 */ + g_Zapit->zapTo_serviceID((*Channels)[selected]->channel_id); + + } else if (state == beMoving) { + finishMoveChannel(); + } + } + else if( CRCInput::isNumeric(msg) ) + { + if (state == beDefault) + { + //kein pushback - wenn man versehentlich wo draufkommt is die edit-arbeit umsonst + //selected = oldselected; + //g_RCInput->postMsg( msg, data ); + //loop=false; + } + else if (state == beMoving) + { + cancelMoveChannel(); + } + } + else if((msg == CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) { + } + else + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + } + hide(); + return res; +} + +void CBEChannelWidget::deleteChannel() +{ + if (selected >= Channels->size()) /* Channels.size() might be 0 */ + return; + + if (ShowMsgUTF(LOCALE_FILEBROWSER_DELETE, (*Channels)[selected]->getName(), CMessageBox::mbrNo, CMessageBox::mbYes|CMessageBox::mbNo)!=CMessageBox::mbrYes) + return; + + g_bouquetManager->Bouquets[bouquet]->removeService((*Channels)[selected]->channel_id); + + Channels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + + if (selected >= Channels->size()) + selected = Channels->empty() ? 0 : (Channels->size() - 1); + channelsChanged = true; + paint(); +} + +void CBEChannelWidget::addChannel() +{ + CBEChannelSelectWidget* channelSelectWidget = new CBEChannelSelectWidget(caption, bouquet, mode); + + channelSelectWidget->exec(this, ""); + if (channelSelectWidget->hasChanged()) + { + channelsChanged = true; + Channels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + } + delete channelSelectWidget; + paintHead(); + paint(); + paintFoot(); +} + +void CBEChannelWidget::beginMoveChannel() +{ + state = beMoving; + origPosition = selected; + newPosition = selected; +} + +void CBEChannelWidget::finishMoveChannel() +{ + state = beDefault; + paint(); +} + +void CBEChannelWidget::cancelMoveChannel() +{ + state = beDefault; + internalMoveChannel( newPosition, origPosition); + channelsChanged = false; +} + +void CBEChannelWidget::internalMoveChannel( unsigned int fromPosition, unsigned int toPosition) +{ + if ( (int) toPosition == -1 ) return; + if ( toPosition == Channels->size()) return; + + g_bouquetManager->Bouquets[bouquet]->moveService(fromPosition, toPosition, + mode == CZapitClient::MODE_TV ? 1 : 2); + + channelsChanged = true; + Channels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + + selected = toPosition; + newPosition = toPosition; + paint(); +} + +bool CBEChannelWidget::hasChanged() +{ + return (channelsChanged); +} diff --git a/src/gui/bedit/bouqueteditor_channels.h b/src/gui/bedit/bouqueteditor_channels.h new file mode 100644 index 000000000..8dbc27f53 --- /dev/null +++ b/src/gui/bedit/bouqueteditor_channels.h @@ -0,0 +1,103 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __bouqueteditor_channels__ +#define __bouqueteditor_channels__ + +#include +#include + +#include + +#include +#include +#include + +class CBEChannelWidget : public CMenuWidget +{ + + private: + + CFrameBuffer *frameBuffer; + + enum state_ + { + beDefault, + beMoving + } state; + + unsigned int selected; + unsigned int origPosition; + unsigned int newPosition; + + unsigned int liststart; + unsigned int listmaxshow; + unsigned int numwidth; + int fheight; + int theight; + + int ButtonHeight; + std::string caption; + bool channelsChanged; + + CZapitClient::channelsMode mode; + + unsigned int bouquet; + + int width; + int height; + int x; + int y; + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void hide(); + + void deleteChannel(); + void addChannel(); + void beginMoveChannel(); + void finishMoveChannel(); + void cancelMoveChannel(); + void internalMoveChannel( unsigned int fromPosition, unsigned int toPosition); + + public: + CBEChannelWidget( const std::string & Caption, unsigned int Bouquet); + + //CZapitClient::BouquetChannelList Channels; + ZapitChannelList * Channels; + int exec(CMenuTarget* parent, const std::string & actionKey); + bool hasChanged(); +}; + +#endif diff --git a/src/gui/bedit/bouqueteditor_chanselect.cpp b/src/gui/bedit/bouqueteditor_chanselect.cpp new file mode 100644 index 000000000..663155f8f --- /dev/null +++ b/src/gui/bedit/bouqueteditor_chanselect.cpp @@ -0,0 +1,176 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define ROUND_RADIUS 9 + +extern tallchans allchans; +extern CBouquetManager *g_bouquetManager; +void addChannelToBouquet(const unsigned int bouquet, const t_channel_id channel_id); + +CBEChannelSelectWidget::CBEChannelSelectWidget(const std::string & Caption, unsigned int Bouquet, CZapitClient::channelsMode Mode) + :CListBox(Caption.c_str()) +{ + bouquet = Bouquet; + mode = Mode; + ButtonHeight = 25; + + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + + liststart = 0; +} + +uint CBEChannelSelectWidget::getItemCount() +{ + return Channels.size(); +} + +bool CBEChannelSelectWidget::isChannelInBouquet( int index) +{ + for (unsigned int i=0; i< bouquetChannels->size(); i++) + { + if ((*bouquetChannels)[i]->channel_id == Channels[index]->channel_id) + return true; + } + return false; +} + +bool CBEChannelSelectWidget::hasChanged() +{ + return modified; +} + +void CBEChannelSelectWidget::paintItem(uint32_t itemNr, int paintNr, bool selected) +{ + int ypos = y+ theight + paintNr*fheight; + + uint8_t color; + fb_pixel_t bgcolor; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, ROUND_RADIUS, 3); + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor); + } + + + if(itemNr < getItemCount()) + { + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x + 8 + NEUTRINO_ICON_BUTTON_GREEN_WIDTH + 8, ypos+ fheight, width - (8 + NEUTRINO_ICON_BUTTON_GREEN_WIDTH + 8 + 8), Channels[itemNr]->getName(), color, 0, true); + + if( isChannelInBouquet(itemNr)) + { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, x+8, ypos+4); + } + else + { + frameBuffer->paintBoxRel(x+8, ypos+4, NEUTRINO_ICON_BUTTON_GREEN_WIDTH, fheight-4, bgcolor); + } + } +} + +void CBEChannelSelectWidget::onOkKeyPressed() +{ + setModified(); + if (isChannelInBouquet(selected)) + g_bouquetManager->Bouquets[bouquet]->removeService(Channels[selected]->channel_id); + else + addChannelToBouquet( bouquet, Channels[selected]->channel_id); + + bouquetChannels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + + paintItem( selected, selected - liststart, false); + g_RCInput->postMsg( CRCInput::RC_down, 0 ); +} + +int CBEChannelSelectWidget::exec(CMenuTarget* parent, const std::string & actionKey) +{ + width = w_max (500, 0); + height = h_max (440, 50); + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + bouquetChannels = mode == CZapitClient::MODE_TV ? &(g_bouquetManager->Bouquets[bouquet]->tvChannels) : &(g_bouquetManager->Bouquets[bouquet]->radioChannels); + + Channels.clear(); + if (mode == CZapitClient::MODE_RADIO) { + for (tallchans_iterator it = allchans.begin(); it != allchans.end(); it++) + if (it->second.getServiceType() == ST_DIGITAL_RADIO_SOUND_SERVICE) + Channels.push_back(&(it->second)); + } else { + for (tallchans_iterator it = allchans.begin(); it != allchans.end(); it++) + if (it->second.getServiceType() != ST_DIGITAL_RADIO_SOUND_SERVICE) + Channels.push_back(&(it->second)); + } + sort(Channels.begin(), Channels.end(), CmpChannelByChName()); + + return CListBox::exec(parent, actionKey); +} + + +void CBEChannelSelectWidget::paintFoot() +{ + int ButtonWidth = width / 3; + frameBuffer->paintBoxRel(x,y+height, width,ButtonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + //frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x+width- 3* ButtonWidth+ 8, y+height+1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width- 3* ButtonWidth+ 38, y+height+24 - 2, width, g_Locale->getText(LOCALE_BOUQUETEDITOR_SWITCH), COL_INFOBAR, 0, true); // UTF-8 + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HOME, x+width - ButtonWidth+ 8, y+height+1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width - ButtonWidth+ 38, y+height+24 - 2, width, g_Locale->getText(LOCALE_BOUQUETEDITOR_RETURN), COL_INFOBAR, 0, true); // UTF-8 +} diff --git a/src/gui/bedit/bouqueteditor_chanselect.h b/src/gui/bedit/bouqueteditor_chanselect.h new file mode 100644 index 000000000..fa1f11098 --- /dev/null +++ b/src/gui/bedit/bouqueteditor_chanselect.h @@ -0,0 +1,70 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __bouqueteditor_chanselect__ +#define __bouqueteditor_chanselect__ + +#include +#include + +#include + +#include +#include +#include + +class CBEChannelSelectWidget : public CListBox +{ + private: + + unsigned int bouquet; + CZapitClient::channelsMode mode; + bool isChannelInBouquet( int index); + + uint getItemCount(); + void paintItem(uint32_t itemNr, int paintNr, bool selected); + void paintFoot(); + void onOkKeyPressed(); + + public: + //CZapitClient::BouquetChannelList Channels; // list of all channels + //CZapitClient::BouquetChannelList bouquetChannels; // list of chans that are currently selected + ZapitChannelList Channels; + ZapitChannelList * bouquetChannels; + + CBEChannelSelectWidget(const std::string & Caption, unsigned int Bouquet, CZapitClient::channelsMode Mode); + int exec(CMenuTarget* parent, const std::string & actionKey); + bool hasChanged(); + +}; + +#endif + diff --git a/src/gui/bookmarkmanager.cpp b/src/gui/bookmarkmanager.cpp new file mode 100644 index 000000000..d71dfdc9e --- /dev/null +++ b/src/gui/bookmarkmanager.cpp @@ -0,0 +1,489 @@ +/* + Neutrino-GUI - DBoxII-Project + + Part of Movieplayer (c) 2003, 2004 by gagga + Based on code by Zwen. Thanks. + + $Id: bookmarkmanager.cpp,v 1.12 2004/05/20 07:38:34 thegoodguy Exp $ + + Homepage: http://www.giggo.de/dbox2/movieplayer.html + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define info_height 60 + + +CBookmark::CBookmark(const std::string & inName, const std::string & inUrl, const std::string & inTime) +{ + name = inName; + url = inUrl; + time = inTime; +} + +//------------------------------ + +int CBookmarkManager::addBookmark (CBookmark inBookmark) { + if (bookmarks.size() < MAXBOOKMARKS) + { + bookmarks.push_back(inBookmark); + printf("CBookmarkManager: addBookmark: %s %s\n", inBookmark.getName(), inBookmark.getTime()); + bookmarksmodified = true; + return 0; + } + // TODO:show dialog to delete old bookmark + return -1; +} + +//------------------------------------------------------------------------ +inline int CBookmarkManager::createBookmark (const std::string & name, const std::string & url, const std::string & time) { + return addBookmark(CBookmark(name, url, time)); +} + +int CBookmarkManager::createBookmark (const std::string & url, const std::string & time) { + char bookmarkname[26]=""; + CStringInputSMS bookmarkname_input(LOCALE_MOVIEPLAYER_BOOKMARKNAME, bookmarkname, 25, LOCALE_MOVIEPLAYER_BOOKMARKNAME_HINT1, LOCALE_MOVIEPLAYER_BOOKMARKNAME_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789-_"); + bookmarkname_input.exec(NULL, ""); + // TODO: return -1 if no name was entered + if(!strlen(bookmarkname)) return -1; + return createBookmark(std::string(bookmarkname), url, time); +} + +//------------------------------------------------------------------------ + +void CBookmarkManager::removeBookmark (unsigned int index) { + std::vector::iterator p = bookmarks.begin()+index; + bookmarks.erase(p); + bookmarksmodified=true; +} + +//------------------------------------------------------------------------ + +void CBookmarkManager::renameBookmark (unsigned int index) { + if (index >= bookmarks.size()) + return; + + CBookmark & theBookmark = bookmarks[index]; + char bookmarkname[26]; + strncpy (bookmarkname, theBookmark.getName(), 25); + CStringInputSMS bookmarkname_input(LOCALE_MOVIEPLAYER_BOOKMARKNAME, bookmarkname, 25, LOCALE_MOVIEPLAYER_BOOKMARKNAME_HINT1, LOCALE_MOVIEPLAYER_BOOKMARKNAME_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789-_"); + bookmarkname_input.exec(NULL, ""); + + if (strcmp(theBookmark.getName(), bookmarkname) != 0) + { + theBookmark.setName(std::string(bookmarkname)); + bookmarksmodified=true; + } +} + +#define BOOKMARKSTRINGLENGTH (10 + 1) +#define BOOKMARKSTRINGMODIFICATIONPOINT 8 +const char * const BOOKMARKSTRING = "bookmark0."; + +//------------------------------------------------------------------------ +void CBookmarkManager::readBookmarkFile() { + if (bookmarkfile.loadConfig(BOOKMARKFILE)) + { + char bookmarkstring[BOOKMARKSTRINGLENGTH]; + strcpy(bookmarkstring, BOOKMARKSTRING); + + bookmarksmodified = false; + bookmarks.clear(); + + unsigned int bookmarkcount = bookmarkfile.getInt32("bookmarkcount", 0); + + if (bookmarkcount > MAXBOOKMARKS) + bookmarkcount = MAXBOOKMARKS; + + while (bookmarkcount-- > 0) + { + std::string tmp = bookmarkstring; + tmp += "name"; + std::string bookmarkname = bookmarkfile.getString(tmp, ""); + tmp = bookmarkstring; + tmp += "url"; + std::string bookmarkurl = bookmarkfile.getString(tmp, ""); + tmp = bookmarkstring; + tmp += "time"; + std::string bookmarktime = bookmarkfile.getString(tmp, ""); + + bookmarks.push_back(CBookmark(bookmarkname, bookmarkurl, bookmarktime)); + + bookmarkstring[BOOKMARKSTRINGMODIFICATIONPOINT]++; + } + } + else + bookmarkfile.clear(); +} + +//------------------------------------------------------------------------ +void CBookmarkManager::writeBookmarkFile() { + char bookmarkstring[BOOKMARKSTRINGLENGTH]; + strcpy(bookmarkstring, BOOKMARKSTRING); + + printf("CBookmarkManager: Writing bookmark file\n"); + + for (std::vector::const_iterator it = bookmarks.begin(); it != bookmarks.end(); it++) + { + std::string tmp = bookmarkstring; + tmp += "name"; + bookmarkfile.setString(tmp, it->getName()); + tmp = bookmarkstring; + tmp += "url"; + bookmarkfile.setString(tmp, it->getUrl()); + tmp = bookmarkstring; + tmp += "time"; + bookmarkfile.setString(tmp, it->getTime()); + + bookmarkstring[BOOKMARKSTRINGMODIFICATIONPOINT]++; + } + bookmarkfile.setInt32("bookmarkcount", bookmarks.size()); + bookmarkfile.saveConfig(BOOKMARKFILE); +} + +//------------------------------------------------------------------------ + +CBookmarkManager::CBookmarkManager() : bookmarkfile ('\t') +{ + readBookmarkFile(); +} + +//------------------------------------------------------------------------ + +CBookmarkManager::~CBookmarkManager () { + flush(); +} + +//------------------------------------------------------------------------ + +int CBookmarkManager::getBookmarkCount(void) const { + return bookmarks.size(); +} + +//------------------------------------------------------------------------ + +int CBookmarkManager::getMaxBookmarkCount(void) const { + return MAXBOOKMARKS; +} + +//------------------------------------------------------------------------ + +void CBookmarkManager::flush() { + if (bookmarksmodified) { + writeBookmarkFile(); + } +} + +//------------------------------------------------------------------------ + +const CBookmark * CBookmarkManager::getBookmark(CMenuTarget* parent) +{ + if(parent) + { + parent->hide(); + } + + frameBuffer = CFrameBuffer::getInstance(); + visible = false; + selected = 0; + // Max + width = 720; + if(g_settings.screen_EndX-g_settings.screen_StartX < width) + width=g_settings.screen_EndX-g_settings.screen_StartX-10; + buttonHeight = 25; + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-( height+ info_height) ) / 2) + g_settings.screen_StartY; + listmaxshow = (height-theight-0)/(fheight*2); + liststart = 0; + + height = (g_settings.screen_EndY-g_settings.screen_StartY)-(info_height+50); + listmaxshow = (height-theight-0)/(fheight*2); + height = theight+0+listmaxshow*fheight*2; // recalc height + if(bookmarks.size() < listmaxshow) + { + listmaxshow=bookmarks.size(); + height = theight+0+listmaxshow*fheight*2; // recalc height + } + if(selected==bookmarks.size() && !(bookmarks.empty())) + { + selected=bookmarks.size()-1; + liststart = (selected/listmaxshow)*listmaxshow; + } + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-( height+ info_height) ) / 2) + g_settings.screen_StartY; + + + int res = -1; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + uint32_t msg; uint32_t data; + + bool loop=true; + bool update=true; + while(loop) + { + if(update) + { + hide(); + update=false; + paint(); + } + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + if( ( msg == CRCInput::RC_timeout ) || + ( msg == CRCInput::RC_home) ) + { //Exit after timeout or cancel key + res = -1; + loop=false; + } + else if ((msg == CRCInput::RC_up) && !(bookmarks.empty())) + { + int prevselected=selected; + if(selected==0) + { + selected = bookmarks.size()-1; + } + else + selected--; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if ((msg == CRCInput::RC_down) && !(bookmarks.empty())) + { + int prevselected=selected; + selected = (selected+1)%bookmarks.size(); + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if ((msg == CRCInput::RC_ok) && !(bookmarks.empty())) + { + res = selected; + loop=false; + } + else if((msg == CRCInput::RC_red) && !(bookmarks.empty())) + { + removeBookmark(selected); + update=true; + } + else if((msg==CRCInput::RC_yellow) && !(bookmarks.empty())) + { + renameBookmark(selected); + update=true; + } + else if((msg==CRCInput::RC_blue)||(msg==CRCInput::RC_green)|| + (CRCInput::isNumeric(msg)) ) + { + //Ignore + } + else if(msg==CRCInput::RC_setup) + { + res=-1; + loop=false; + } + else if( msg == CRCInput::RC_help ) + { + // TODO Add Help + } + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + hide(); + + if ((res >=0) && (((unsigned int)res) < bookmarks.size())) + return &bookmarks[res]; + else + return NULL; +} + +//------------------------------------------------------------------------ +void CBookmarkManager::paintItem(int pos) +{ + int ypos = y+ theight+0 + pos*fheight*2; + + uint8_t color; + fb_pixel_t bgcolor; + + if (pos & 1) + { + color = COL_MENUCONTENTDARK; + bgcolor = COL_MENUCONTENTDARK_PLUS_0; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + if (liststart + pos == selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + + int real_width=width; + if(bookmarks.size()>listmaxshow) + { + real_width-=15; //scrollbar + } + + frameBuffer->paintBoxRel(x,ypos, real_width, 2*fheight, bgcolor); + if(liststart+posRenderString(x+10,ypos+fheight, real_width-10, theBookmark.getName(), color, fheight, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,ypos+2*fheight, real_width-10, theBookmark.getUrl(), color, fheight, true); // UTF-8 + + // LCD Display + if(liststart+pos==selected) + { + CVFD::getInstance()->showMenuText(0, theBookmark.getName(), -1, true); // UTF-8 + CVFD::getInstance()->showMenuText(1, theBookmark.getUrl(), -1, true); // UTF-8 + } + } +} + +//------------------------------------------------------------------------ + +void CBookmarkManager::hide() +{ + if(visible) + { + frameBuffer->paintBackgroundBoxRel(x, y, width, height+ info_height+ 5); + visible = false; + } +} + +//------------------------------------------------------------------------ +void CBookmarkManager::paintHead() +{ + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0); + frameBuffer->paintIcon("bookmarkmanager.raw",x+5,y+4); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+35,y+theight+0, width- 45, g_Locale->getText(LOCALE_BOOKMARKMANAGER_NAME), COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 ); +} + +const struct button_label BookmarkmanagerButtons[2] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_BOOKMARKMANAGER_DELETE }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_BOOKMARKMANAGER_RENAME } +}; + +//------------------------------------------------------------------------ +void CBookmarkManager::paintFoot() +{ + int ButtonWidth = (width - 20) / 4; + frameBuffer->paintBoxRel(x,y+height, width,buttonHeight, COL_MENUHEAD_PLUS_0); + frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + if (bookmarks.empty()) { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x+width- 1* ButtonWidth + 10, y+height); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width-1 * ButtonWidth + 38, y+height+24 - 2, ButtonWidth- 28, g_Locale->getText(LOCALE_BOOKMARKMANAGER_SELECT), COL_INFOBAR, 0, true); // UTF-8 + } + else + { + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + height + 4, ButtonWidth, 2, BookmarkmanagerButtons); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x+width- 1* ButtonWidth + 10, y+height); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width-1 * ButtonWidth + 38, y+height+24 - 2, ButtonWidth- 28, g_Locale->getText(LOCALE_BOOKMARKMANAGER_SELECT), COL_INFOBAR, 0, true); // UTF-8 + } +} + +//------------------------------------------------------------------------ +void CBookmarkManager::paint() +{ + unsigned int page_nr = (listmaxshow == 0) ? 0 : (selected / listmaxshow); + liststart = page_nr * listmaxshow; + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_BOOKMARKMANAGER_NAME)); + + paintHead(); + + for(unsigned int count=0;countlistmaxshow) + { + int ypos = y+ theight; + int sb = 2*fheight* listmaxshow; + frameBuffer->paintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((bookmarks.size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(page_nr * sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); + } + + paintFoot(); + visible = true; +} + diff --git a/src/gui/bookmarkmanager.h b/src/gui/bookmarkmanager.h new file mode 100644 index 000000000..b46a8442d --- /dev/null +++ b/src/gui/bookmarkmanager.h @@ -0,0 +1,115 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2003,2004 gagga + Homepage: http://www.giggo.de/dbox + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __bookmarkmanager__ +#define __bookmarkmanager__ + +#include +#include +#include + +#include +#include + +#include +#include + +#define MAXBOOKMARKS 10 +#define BOOKMARKFILE "/var/tuxbox/config/bookmarks" + +class CBookmark +{ + private: + std::string name; + std::string url; + std::string time; + public: + CBookmark(const std::string & name, const std::string & url, const std::string & time); + const char * const getName(void) const { return name.c_str(); }; + const char * const getUrl (void) const { return url .c_str(); }; + const char * const getTime(void) const { return time.c_str(); }; + inline void setName(const std::string & new_name) { name = new_name; }; + inline void setUrl (const std::string & new_url ) { url = new_url ; }; + inline void setTime(const std::string & new_time) { time = new_time; }; +}; + +//----------------------------------------- + +class CBookmarkManager +{ + private: + std::vector bookmarks; + CConfigFile bookmarkfile; + + CFrameBuffer *frameBuffer; + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + int fheight; // Fonthoehe Timerlist-Inhalt + int theight; // Fonthoehe Timerlist-Titel + int buttonHeight; + bool visible; + int width; + int height; + int x; + int y; + + + //int bookmarkCount; + bool bookmarksmodified; + void readBookmarkFile(); + void writeBookmarkFile(); + CBookmark getBookmark(); + int addBookmark(CBookmark inBookmark); + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void hide(); + + + + public: + CBookmarkManager(); + ~CBookmarkManager(); + int createBookmark(const std::string & name, const std::string & url, const std::string & time); + int createBookmark(const std::string & url, const std::string & time); + void removeBookmark(unsigned int index); + void renameBookmark(unsigned int index); + int getBookmarkCount(void) const; + int getMaxBookmarkCount(void) const; + void flush(); + + const CBookmark * getBookmark(CMenuTarget* parent); +}; + +#endif diff --git a/src/gui/bouquetlist.cpp b/src/gui/bouquetlist.cpp new file mode 100644 index 000000000..11514d9b2 --- /dev/null +++ b/src/gui/bouquetlist.cpp @@ -0,0 +1,549 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define ROUND_RADIUS 9 +extern CBouquetManager *g_bouquetManager; + +CBouquetList::CBouquetList(const char * const Name) +{ + frameBuffer = CFrameBuffer::getInstance(); + selected = 0; + liststart = 0; + if(Name == NULL) + name = g_Locale->getText(LOCALE_BOUQUETLIST_HEAD); + else + name = Name; + +} + +CBouquetList::~CBouquetList() +{ + for (std::vector::iterator it = Bouquets.begin(); it != Bouquets.end(); it++) { + delete (*it); + } + Bouquets.clear(); +} + +CBouquet* CBouquetList::addBouquet(CZapitBouquet * zapitBouquet) +{ + CBouquet* tmp = addBouquet(zapitBouquet->Name.c_str(), -1, zapitBouquet->bLocked); + tmp->zapitBouquet = zapitBouquet; + return tmp; +} + +CBouquet* CBouquetList::addBouquet(const char * const name, int BouquetKey, bool locked) +{ + if ( BouquetKey==-1 ) + BouquetKey= Bouquets.size(); + + CBouquet* tmp = new CBouquet( BouquetKey, name, locked ); + Bouquets.push_back(tmp); + return(tmp); +} + +void CBouquetList::deleteBouquet(CBouquet*bouquet) +{ + if (bouquet != NULL) { + std::vector::iterator it = find(Bouquets.begin(), Bouquets.end(), bouquet); + + if (it != Bouquets.end()) { + Bouquets.erase(it); + delete bouquet; + } + } +} + +int CBouquetList::getActiveBouquetNumber() +{ + return selected; +} + +void CBouquetList::adjustToChannel( int nChannelNr) +{ + for (uint32_t i=0; ichannelList->hasChannel(nChannelNr); + if (nChannelPos > -1) { + selected = i; + Bouquets[i]->channelList->setSelected(nChannelPos); + return; + } + } +} + +void CBouquetList::adjustToChannelID(t_channel_id channel_id) +{ + if(selected < Bouquets.size()) { + int nChannelPos = Bouquets[selected]->channelList->hasChannelID(channel_id); + if(nChannelPos > -1) { +printf("CBouquetList::adjustToChannelID to %llx -> not needed\n", channel_id); + Bouquets[selected]->channelList->setSelected(nChannelPos); + return; + } + } +printf("CBouquetList::adjustToChannelID to %llx\n", channel_id); + for (uint32_t i=0; i < Bouquets.size(); i++) { + if(i == selected) + continue; + int nChannelPos = Bouquets[i]->channelList->hasChannelID(channel_id); + if (nChannelPos > -1) { + selected = i; + Bouquets[i]->channelList->setSelected(nChannelPos); + return; + } + } +} +/* used in channellist to switch bouquets up/down */ +int CBouquetList::showChannelList( int nBouquet) +{ + if (nBouquet == -1) + nBouquet = selected; + + int nNewChannel = Bouquets[nBouquet]->channelList->exec(); + if (nNewChannel > -1) { + selected = nBouquet; + nNewChannel = -2; + } + return nNewChannel; +} +/* bShowChannelList default to false , return seems not checked anywhere */ +int CBouquetList::activateBouquet( int id, bool bShowChannelList) +{ + int res = -1; + + if(id < (int) Bouquets.size()) + selected = id; + + if (bShowChannelList) { + res = Bouquets[selected]->channelList->exec(); + if(res > -1) + res = -2; + } + return res; +} + +int CBouquetList::exec( bool bShowChannelList) +{ + /* select bouquet to show */ + int res = show(bShowChannelList); +//printf("Bouquet-exec: res %d bShowChannelList %d\n", res, bShowChannelList); fflush(stdout); + + if(!bShowChannelList) + return res; + /* if >= 0, call activateBouquet to show channel list */ + if ( res > -1) { + return activateBouquet(selected, bShowChannelList); + } + return res; +} + +int CBouquetList::doMenu() +{ + int i = 0; + int select = -1; + static int old_selected = 0; + int ret = menu_return::RETURN_NONE; + signed int bouquet_id; + char cnt[5]; + CZapitBouquet * tmp, * zapitBouquet; + ZapitChannelList* channels; + + if(!Bouquets.size() || g_settings.minimode) + return 0; + + zapitBouquet = Bouquets[selected]->zapitBouquet; + /* zapitBouquet not NULL only on real bouquets, not on virtual SAT or HD */ + if(!zapitBouquet) + return 0; + + CMenuWidget* menu = new CMenuWidget(LOCALE_CHANNELLIST_EDIT, NEUTRINO_ICON_SETTINGS); + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&select); + + sprintf(cnt, "%d", i); + if(!zapitBouquet->bUser) { + menu->addItem(new CMenuForwarder(LOCALE_FAVORITES_COPY, true, NULL, selector, cnt, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE), old_selected == i ++); + ret = menu->exec(NULL, ""); + delete menu; + delete selector; + printf("CBouquetList::doMenu: %d selected\n", select); + if(select >= 0) { + old_selected = select; + switch(select) { + case 0: + hide(); + bouquet_id = g_bouquetManager->existsUBouquet(Bouquets[selected]->channelList->getName()); + if(bouquet_id < 0) { + tmp = g_bouquetManager->addBouquet(Bouquets[selected]->channelList->getName(), true); + } else + tmp = g_bouquetManager->Bouquets[bouquet_id]; + + channels = &zapitBouquet->tvChannels; + for(int i = 0; i < (int) channels->size(); i++) + tmp->addService((*channels)[i]); + return 1; + break; + default: + break; + } + } + } else { + menu->addItem(new CMenuForwarder(LOCALE_BOUQUETEDITOR_DELETE, true, NULL, selector, cnt, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE), old_selected == i ++); + ret = menu->exec(NULL, ""); + delete menu; + delete selector; + printf("CBouquetList::doMenu: %d selected\n", select); + if(select >= 0) { + old_selected = select; + switch(select) { + case 0: + hide(); + bouquet_id = g_bouquetManager->existsUBouquet(Bouquets[selected]->channelList->getName()); + if(bouquet_id >= 0) { + g_bouquetManager->deleteBouquet(bouquet_id); + return 1; + } + break; + default: + break; + } + } + } + return 0; +} + +/* bShowChannelList default to true, returns new bouquet or -1/-2 */ +int CBouquetList::show(bool bShowChannelList) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + int res = -1; + + //if(Bouquets.size()==0) + // return res; + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, ""); + + width = w_max (500, 0); + height = h_max (440, 40); + + buttonHeight = 7 + std::min(16, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight()); + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + listmaxshow = (height - theight - buttonHeight)/fheight; + height = theight + buttonHeight + listmaxshow * fheight; // recalc height + + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + int maxpos= 1; + int i= Bouquets.size(); + while ((i= i/10)!=0) + maxpos++; + + paintHead(); + paint(); + + int oldselected = selected; + int firstselected = selected+ 1; + int zapOnExit = false; + + unsigned int chn= 0; + int pos= maxpos; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + bool loop=true; + while (loop) { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + if ((msg == CRCInput::RC_timeout ) || + (msg == (neutrino_msg_t)g_settings.key_channelList_cancel)) + { + selected = oldselected; + loop=false; + } + else if(msg == CRCInput::RC_red || msg == CRCInput::RC_favorites) { + CNeutrinoApp::getInstance()->SetChannelMode(LIST_MODE_FAV); + hide(); + return -3; + } else if(msg == CRCInput::RC_green) { + CNeutrinoApp::getInstance()->SetChannelMode(LIST_MODE_PROV); + hide(); + return -3; + } else if(msg == CRCInput::RC_yellow || msg == CRCInput::RC_sat) { + CNeutrinoApp::getInstance()->SetChannelMode(LIST_MODE_SAT); + hide(); + return -3; + } else if(msg == CRCInput::RC_blue) { + CNeutrinoApp::getInstance()->SetChannelMode(LIST_MODE_ALL); + hide(); + return -3; + } + else if(Bouquets.size() == 0) + continue; //FIXME msgs not forwarded to neutrino !! + else if ( msg == CRCInput::RC_setup) { + int ret = doMenu(); + if(ret) { + res = -4; + loop = false; + } else + paint(); + } + else if ( msg == (neutrino_msg_t) g_settings.key_list_start ) { + selected=0; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + } + else if ( msg == (neutrino_msg_t) g_settings.key_list_end ) { + selected=Bouquets.size()-1; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + } + else if (msg == CRCInput::RC_up || (int) msg == g_settings.key_channelList_pageup) + { + int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + selected = Bouquets.size()-1; + + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + } + else if (msg == CRCInput::RC_down || (int) msg == g_settings.key_channelList_pagedown) + { + unsigned int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= Bouquets.size()) { + if (((Bouquets.size() / listmaxshow) + 1) * listmaxshow == Bouquets.size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((Bouquets.size() / listmaxshow) + 1) * listmaxshow))) ? (Bouquets.size() - 1) : 0; + } + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + } + + else if ( msg == CRCInput::RC_ok ) { + if(!bShowChannelList || Bouquets[selected]->channelList->getSize() > 0) { + zapOnExit = true; + loop=false; + } + } + else if (CRCInput::isNumeric(msg)) { + if (pos == maxpos) { + if (msg == CRCInput::RC_0) { + chn = firstselected; + pos = maxpos; + } else { + chn = CRCInput::getNumericValue(msg); + pos = 1; + } + } else { + chn = chn * 10 + CRCInput::getNumericValue(msg); + pos++; + } + + if (chn > Bouquets.size()) { + chn = firstselected; + pos = maxpos; + } + + int prevselected=selected; + selected = (chn - 1) % Bouquets.size(); // is % necessary (i.e. can firstselected be > Bouquets.size()) ? + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) { + paint(); + } else { + paintItem(selected - liststart); + } + + } else { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) { + loop = false; + res = -2; + } + }; + } + hide(); + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + if(zapOnExit) { + return (selected); + } else { + return (res); + } +} + +void CBouquetList::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height+10); +} + +void CBouquetList::paintItem(int pos) +{ + int ypos = y+ theight+0 + pos*fheight; + uint8_t color; + fb_pixel_t bgcolor; + bool iscurrent = true; + int npos = liststart + pos; + const char * name = NULL; + + if(npos < (int) Bouquets.size()) + name = (Bouquets[npos]->zapitBouquet && Bouquets[npos]->zapitBouquet->bFav) ? g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME) : Bouquets[npos]->channelList->getName(); + + if (npos == (int) selected) { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + frameBuffer->paintBoxRel(x, ypos, width- 15, fheight, bgcolor, ROUND_RADIUS, 3); + if(npos < (int) Bouquets.size()) + CVFD::getInstance()->showMenuText(0, name, -1, true); + } else { + if(npos < (int) Bouquets.size()) + iscurrent = Bouquets[npos]->channelList->getSize() > 0; + color = iscurrent ? COL_MENUCONTENT : COL_MENUCONTENTINACTIVE; + bgcolor = iscurrent ? COL_MENUCONTENT_PLUS_0 : COL_MENUCONTENTINACTIVE_PLUS_0; + frameBuffer->paintBoxRel(x, ypos, width- 15, fheight, bgcolor); + } + + if(npos < (int) Bouquets.size()) { + char tmp[10]; + sprintf((char*) tmp, "%d", npos+ 1); + + int numpos = x+5+numwidth- g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth(tmp); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(numpos,ypos+fheight, numwidth+5, tmp, color, fheight); + + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 5+ numwidth+ 10, ypos+ fheight, width- numwidth- 20- 15, name, color, 0, true); // UTF-8 + //CVFD::getInstance()->showMenuText(0, bouq->channelList->getName(), -1, true); + } +} + +const struct button_label CBouquetListButtons[4] = +{ + { NEUTRINO_ICON_BUTTON_RED, LOCALE_CHANNELLIST_FAVS}, + { NEUTRINO_ICON_BUTTON_GREEN, LOCALE_CHANNELLIST_PROVS}, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_CHANNELLIST_SATS}, + { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_CHANNELLIST_HEAD} +}; + +void CBouquetList::paintHead() +{ + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+theight+0, width, name, COL_MENUHEAD, 0, true); // UTF-8 +} + +void CBouquetList::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + int lastnum = liststart + listmaxshow; + int bsize = Bouquets.size() > 0 ? Bouquets.size() : 1; + + if(lastnum<10) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("0"); + else if(lastnum<100) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("00"); + else if(lastnum<1000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("000"); + else if(lastnum<10000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("0000"); + else // if(lastnum<100000) + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("00000"); + + //frameBuffer->paintBoxRel(x, y+theight, width, height-theight+10, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + frameBuffer->paintBoxRel(x, y+theight, width, height - theight - buttonHeight, COL_MENUCONTENT_PLUS_0); + + int ButtonWidth = (width - 20) / 4; + + frameBuffer->paintBoxRel(x, y + (height - buttonHeight), width, buttonHeight - 1, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + (height - buttonHeight) + 3, ButtonWidth, sizeof(CBouquetListButtons)/sizeof(CBouquetListButtons[0]), CBouquetListButtons); + + if(Bouquets.size()) + { + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((bsize - 1)/ listmaxshow)+ 1; + float sbh= (sb - 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); +} diff --git a/src/gui/bouquetlist.h b/src/gui/bouquetlist.h new file mode 100644 index 000000000..5f71dca09 --- /dev/null +++ b/src/gui/bouquetlist.h @@ -0,0 +1,123 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __bouquetlist__ +#define __bouquetlist__ + +#include + +#include +#include +#include + +#include +#include + + +typedef enum bouquetSwitchMode +{ + bsmBouquets, // pressing OK shows list of all Bouquets + bsmChannels, // pressing OK shows list of all channels of active bouquets + bsmAllChannels // OK shows lsit of all channels +} BouquetSwitchMode; + +class CBouquet +{ + + public: + int unique_key; + bool bLocked; + CChannelList* channelList; + CZapitBouquet * zapitBouquet; + + CBouquet(const int Unique_key, const char * const Name, const bool locked) + { + zapitBouquet = NULL; + unique_key = Unique_key; + bLocked = locked; + channelList = new CChannelList(Name); + } + + ~CBouquet() + { + delete channelList; + } +}; + + +class CBouquetList +{ + private: + CFrameBuffer *frameBuffer; + + std::string name; + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + unsigned int numwidth; + unsigned int maxpos; + int fheight; // Fonthoehe Bouquetlist-Inhalt + int theight; // Fonthoehe Bouquetlist-Titel + int buttonHeight; + + int width; + int height; + int x; + int y; + + void paintItem(int pos); + void paint(); + void paintHead(); + void hide(); + int doMenu(); + + public: + CBouquetList(const char * const Name = NULL); + ~CBouquetList(); + + std::vector Bouquets; + + CChannelList* orgChannelList; + CBouquet* addBouquet(const char * const name, int BouquetKey=-1, bool locked=false ); + CBouquet* addBouquet(CZapitBouquet * zapitBouquet); + void deleteBouquet(CBouquet* bouquet); + int getActiveBouquetNumber(); + int activateBouquet(int id, bool bShowChannelList); + int show(bool bShowChannelList = true); + int showChannelList(int nBouquet = -1); + void adjustToChannel(int nChannelNr); + void adjustToChannelID(t_channel_id channel_id); + int exec( bool bShowChannelList); +}; + + +#endif diff --git a/src/gui/cam_menu.cpp b/src/gui/cam_menu.cpp new file mode 100644 index 000000000..26caddc48 --- /dev/null +++ b/src/gui/cam_menu.cpp @@ -0,0 +1,402 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gui/widget/menue.h" +#include "gui/widget/stringinput.h" +#include "gui/widget/messagebox.h" +#include "gui/widget/hintbox.h" +#include "gui/widget/progresswindow.h" + +#include "system/setting_helpers.h" +#include "system/settings.h" +#include "system/debug.h" + +#include +#include +#include +#include + +void CCAMMenuHandler::init(void) +{ + hintBox = NULL; + ci = cDvbCi::getInstance(); +} + +int CCAMMenuHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ +printf("CCAMMenuHandler::exec: actionkey %s\n", actionkey.c_str()); + if (parent) + parent->hide(); + + if(actionkey == "cam1") { + return doMenu(0); + } else if(actionkey == "cam2") { + return doMenu(1); + } + + return doMainMenu (); +} + +int CCAMMenuHandler::doMainMenu () +{ + int ret; + char name1[255], name2[255]; + //cDvbCiSlot *one, *two; + char str1[255]; + char str2[255]; + + //one = ci->GetSlot(0); + //two = ci->GetSlot(1); + + CMenuWidget* cammenu = new CMenuWidget(LOCALE_CAM_SETTINGS, NEUTRINO_ICON_SETTINGS); + cammenu->addItem( GenericMenuBack ); + cammenu->addItem( GenericMenuSeparatorLine ); + + CMenuWidget * tempMenu; + if(ci->CamPresent(0)) { + ci->GetName(0, name1); +printf("CCAMMenuHandler::doMenu cam1 name %s\n", name1); + cammenu->addItem(new CMenuForwarderNonLocalized(name1, true, NULL, this, "cam1", CRCInput::RC_1)); + } else { + sprintf(str1, "%s 1", g_Locale->getText(LOCALE_CAM_EMPTY)); + tempMenu = new CMenuWidget(str1, NEUTRINO_ICON_SETTINGS); + cammenu->addItem(new CMenuForwarderNonLocalized(str1, false, NULL, tempMenu)); + } + + if(ci->CamPresent(1)) { + ci->GetName(1, name2); +printf("CCAMMenuHandler::doMenu cam2 name %s\n", name2); + cammenu->addItem(new CMenuForwarderNonLocalized(name2, true, NULL, this, "cam2", CRCInput::RC_2)); + } else { + sprintf(str2, "%s 2", g_Locale->getText(LOCALE_CAM_EMPTY)); + tempMenu = new CMenuWidget(str2, NEUTRINO_ICON_SETTINGS); + cammenu->addItem(new CMenuForwarderNonLocalized(str2, false, NULL, tempMenu)); + } + + ret = cammenu->exec(NULL, ""); + delete cammenu; + return ret; +} + +#define CI_MSG_TIME 5 +int CCAMMenuHandler::handleMsg (const neutrino_msg_t msg, neutrino_msg_data_t data) +{ + int ret = messages_return::handled; +//printf("CCAMMenuHandler::handleMsg: msg 0x%x data 0x%x\n", msg, data); + int camret = handleCamMsg(msg, data); + if(camret < 0) { + ret = messages_return::unhandled; + } + return ret; +} + +int CCAMMenuHandler::handleCamMsg (const neutrino_msg_t msg, neutrino_msg_data_t data, bool from_menu) +{ + int ret = 0; + char str[255]; + char cnt[5]; + int i; + MMI_MENU_LIST_INFO Menu; + MMI_ENGUIRY_INFO MmiEnquiry; + int curslot; + MMI_MENU_LIST_INFO * pMenu = &Menu; + MMI_ENGUIRY_INFO * pMmiEnquiry = &MmiEnquiry; + +//printf("CCAMMenuHandler::handleCamMsg: msg 0x%x data 0x%x\n", msg, data); + + if(msg == NeutrinoMessages::EVT_CI_INSERTED) { + if(hintBox != NULL) { + hintBox->hide(); + delete hintBox; + } + sprintf(str, "%s %d", g_Locale->getText(LOCACE_CAM_INSERTED), (int) data+1); + + printf("CCAMMenuHandler::handleMsg: %s\n", str); + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, str); + hintBox->paint(); + + sleep(CI_MSG_TIME); + hintBox->hide(); + delete hintBox; + hintBox = NULL; + + } else if (msg == NeutrinoMessages::EVT_CI_REMOVED) { + if(hintBox != NULL) { + hintBox->hide(); + delete hintBox; + } + sprintf(str, "%s %d", g_Locale->getText(LOCALE_CAM_REMOVED), (int) data+1); + + printf("CCAMMenuHandler::handleMsg: %s\n", str); + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, str); + + hintBox->paint(); + + sleep(CI_MSG_TIME); + hintBox->hide(); + delete hintBox; + hintBox = NULL; + } else if(msg == NeutrinoMessages::EVT_CI_INIT_OK) { + if(hintBox != NULL) { + hintBox->hide(); + delete hintBox; + } + char name[255] = "Unknown"; + //cDvbCiSlot * slot = ci->GetSlot((int) data); + //if(slot) + // slot->GetName(name); + ci->GetName((int) data, name); + sprintf(str, "%s %d: %s", g_Locale->getText(LOCALE_CAM_INIT_OK), (int) data+1, name); + + printf("CCAMMenuHandler::handleMsg: %s\n", str); + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, str); + hintBox->paint(); + sleep(CI_MSG_TIME); + hintBox->hide(); + delete hintBox; + hintBox = NULL; + } + else if(msg == NeutrinoMessages::EVT_CI_MMI_MENU || msg == NeutrinoMessages::EVT_CI_MMI_LIST) { + bool sublevel = false; + if(msg != NeutrinoMessages::EVT_CI_MMI_MENU) + sublevel = true; + + memcpy(pMenu, (MMI_MENU_LIST_INFO*) data, sizeof(MMI_MENU_LIST_INFO)); + free((void *)data); + curslot = pMenu->slot; + + printf("CCAMMenuHandler::handleCamMsg: slot %d menu ready, title %s choices %d\n", curslot, convertDVBUTF8(pMenu->title, strlen(pMenu->title), 0).c_str(), pMenu->choice_nb); + + if(hintBox) { + hintBox->hide(); + } + + int selected = -1; + if(pMenu->choice_nb) { + CMenuWidget* menu = new CMenuWidget(convertDVBUTF8(pMenu->title, strlen(pMenu->title), 0).c_str(), NEUTRINO_ICON_SETTINGS); + + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&selected); + int slen = strlen(pMenu->subtitle); + if(slen) { + char * sptr = pMenu->subtitle; + char * tptr = sptr; + int bpos = 0; + for(int i = 0; i < slen; i++) { + if((tptr[i] == 0x8A) || ((bpos > 38) && (tptr[i] == 0x20)) ) { + bpos = 0; + tptr[i] = 0; +printf("CCAMMenuHandler::handleCamMsg: subtitle: %s\n", sptr); + menu->addItem(new CMenuForwarderNonLocalized(convertDVBUTF8(sptr, strlen(sptr), 0).c_str(), false)); + sptr = &tptr[i+1]; + } + bpos++; + } + if(strlen(sptr)) { +printf("CCAMMenuHandler::handleCamMsg: subtitle: %s\n", sptr); + menu->addItem(new CMenuForwarderNonLocalized(convertDVBUTF8(sptr, strlen(sptr), 0).c_str(), false)); + } + } + for(i = 0; i < pMenu->choice_nb; i++) { + sprintf(cnt, "%d", i); + if(sublevel) + menu->addItem(new CMenuForwarderNonLocalized(convertDVBUTF8(pMenu->choice_item[i], strlen(pMenu->choice_item[i]), 0).c_str(), true, NULL, selector, cnt)); + else + menu->addItem(new CMenuForwarderNonLocalized(convertDVBUTF8(pMenu->choice_item[i], strlen(pMenu->choice_item[i]), 0).c_str(), true, NULL, selector, cnt, CRCInput::convertDigitToKey(i+1))); + } + slen = strlen(pMenu->bottom); + if(slen) { +printf("CCAMMenuHandler::handleCamMsg: bottom: %s\n", pMenu->bottom); + menu->addItem(new CMenuForwarderNonLocalized(convertDVBUTF8(pMenu->bottom, slen, 0).c_str(), false)); + } + + menu->exec(NULL, ""); + + delete menu; + delete selector; + } else { + char str[255]; + snprintf(str, 255, "%s\n%s\n%s", pMenu->title, pMenu->subtitle, pMenu->bottom); + if(hintBox) + delete hintBox; + //hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, convertDVBUTF8(pMenu->title, strlen(pMenu->title), 0).c_str()); + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, convertDVBUTF8(str, strlen(str), 0).c_str()); + hintBox->paint(); + sleep(4);//FIXME + if(!from_menu) { + delete hintBox; + hintBox = NULL; + } + return 1; + } + + if(sublevel) + return 0; + + if(selected >= 0) { + printf("CCAMMenuHandler::handleCamMsg: selected %d:%s sublevel %s\n", selected, pMenu->choice_item[i], sublevel ? "yes" : "no"); + CI_MenuAnswer(curslot, selected+1); + timeoutEnd = CRCInput::calcTimeoutEnd(10); + return 1; + } else { + return 2; + } + } + else if(msg == NeutrinoMessages::EVT_CI_MMI_REQUEST_INPUT) { + memcpy(pMmiEnquiry, (MMI_ENGUIRY_INFO*) data, sizeof(MMI_ENGUIRY_INFO)); + free((void *)data); + curslot = pMmiEnquiry->slot; + printf("CCAMMenuHandler::handleCamMsg: slot %d input request, text %s\n", curslot, convertDVBUTF8(pMmiEnquiry->enguiryText, strlen(pMmiEnquiry->enguiryText), 0).c_str()); + if(hintBox) + hintBox->hide(); + + char cPIN[pMmiEnquiry->answerlen+1]; + cPIN[0] = 0; + + CPINInput* PINInput = new CPINInput((char *) convertDVBUTF8(pMmiEnquiry->enguiryText, strlen(pMmiEnquiry->enguiryText), 0).c_str(), cPIN, 4, NONEXISTANT_LOCALE); + PINInput->exec(NULL, ""); + delete PINInput; + + printf("CCAMMenuHandler::handleCamMsg: input=[%s]\n", cPIN); + //if(cPIN[0] == 0) + // return 0; + + if((int) strlen(cPIN) != pMmiEnquiry->answerlen) { + printf("CCAMMenuHandler::handleCamMsg: wrong input len\n"); + CI_Answer(curslot, (unsigned char *) cPIN, 0); + return 0; + } else { + CI_Answer(curslot, (unsigned char *) cPIN, pMmiEnquiry->answerlen); + return 1; + } + } + else if(msg == NeutrinoMessages::EVT_CI_MMI_CLOSE) { + curslot = (int) data; + printf("CCAMMenuHandler::handleCamMsg: close request slot: %d\n", curslot); +#if 0 + if(hintBox) { + hintBox->hide(); + delete hintBox; + hintBox = NULL; + } +#endif + CI_CloseMMI(curslot); + return 0; + } + else if(msg == NeutrinoMessages::EVT_CI_MMI_TEXT) { + curslot = (int) data; + printf("CCAMMenuHandler::handleCamMsg: text\n"); + } else + ret = -1; +//printf("CCAMMenuHandler::handleCamMsg: return %d\n", ret); + return ret; +} + +int CCAMMenuHandler::doMenu (int slot) +{ + int res = menu_return::RETURN_REPAINT; + neutrino_msg_t msg; + neutrino_msg_data_t data; + bool doexit = false; + + while(!doexit) { + printf("CCAMMenuHandler::doMenu: slot %d\n", slot); + + timeoutEnd = CRCInput::calcTimeoutEnd(10); + + CI_EnterMenu(slot); + while(true) { + if(hintBox) + delete hintBox; + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_CAM_WAITING)); + hintBox->paint(); + g_RCInput->getMsgAbsoluteTimeout (&msg, &data, &timeoutEnd); + printf("CCAMMenuHandler::doMenu: msg %x data %x\n", msg, data); + if (msg == CRCInput::RC_timeout) { + if(hintBox) + delete hintBox; + + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_CAM_TIMEOUT)); + hintBox->paint(); + + printf("CCAMMenuHandler::doMenu: menu timeout\n"); + sleep(5); + delete hintBox; + hintBox = NULL; + CI_CloseMMI(slot); + return menu_return::RETURN_REPAINT; + } + /* -1 = not our event, 0 = back to top menu, 1 = continue loop, 2 = quit */ + int ret = handleCamMsg(msg, data, true); + if(ret < 0 && (msg > CRCInput::RC_Messages)) { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & ( messages_return::cancel_all | messages_return::cancel_info ) ) + { + doexit = true; + res = menu_return::RETURN_EXIT_ALL; + } + } else if (ret == 1) { + timeoutEnd = CRCInput::calcTimeoutEnd(10); + continue; + } else if (ret == 2) { + doexit = true; + break; + } else { + break; + } + } + } + CI_CloseMMI(slot); + if(hintBox) { + delete hintBox; + hintBox = NULL; + } +printf("CCAMMenuHandler::doMenu: return\n"); + return res; +} diff --git a/src/gui/cam_menu.h b/src/gui/cam_menu.h new file mode 100644 index 000000000..09d629bee --- /dev/null +++ b/src/gui/cam_menu.h @@ -0,0 +1,48 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __cam_menu__ +#define __cam_menu__ + + +#include "widget/menue.h" +#include + +using namespace std; + +class CCAMMenuHandler : public CMenuTarget +{ + private: + CHintBox * hintBox; + cDvbCi * ci; + unsigned long long timeoutEnd; + //int slot; + int doMenu(int slot); + int doMainMenu(); + int handleCamMsg (const neutrino_msg_t msg, neutrino_msg_data_t data, bool from_menu = false); + public: + void init(void); + int exec(CMenuTarget* parent, const std::string &actionkey); + int handleMsg (const neutrino_msg_t msg, neutrino_msg_data_t data); +}; +#endif + diff --git a/src/gui/ch_mosaic.cpp b/src/gui/ch_mosaic.cpp new file mode 100644 index 000000000..c630c0a98 --- /dev/null +++ b/src/gui/ch_mosaic.cpp @@ -0,0 +1,256 @@ + +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ch_mosaic.h" + +#warning "experimental..." + + +/* + -- Channel/Service Mosaic + -- Display multiple channel images on screen + -- capture used from outdoor (tmbinc) + -- 2002-11 rasc + */ + + + + +// +// -- init Channel Mosaic Handler Class +// -- to be used for calls from Menue +// + +int CChMosaicHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + CChMosaic * mosaic; + + + if (parent) { + parent->hide(); + } + + mosaic = new CChMosaic; + mosaic->doMosaic (); + delete mosaic; + + return res; +} + + + + +#define SCREEN_X 720 +#define SCREEN_Y 572 +#define RATIO(y) (((y)*100)/126) // w/h PAL ratio + + +// +// -- Channel Mosaic Class +// -- do Mosaic +// + +CChMosaic::CChMosaic() +{ + pig = new CPIG (0); + capture = new CCAPTURE (0); + current_pig_pos = 0; + + channellist = CNeutrinoApp::getInstance()->channelList; + frameBuffer = CFrameBuffer::getInstance(); +} + + +CChMosaic::~CChMosaic() +{ + delete pig; + +} + + +void CChMosaic::doMosaic() +{ +#define W (150) +#define H (120) + struct PIG_COORD coord[] = { + { 40, 10, W,H }, + {200, 10, W,H }, + {360, 10, W,H }, + {520, 10, W,H }, + { 40,150, W,H }, + {200,150, W,H }, + {360,150, W,H }, + {520,150, W,H }, + { 40,290, W,H }, + {200,290, W,H }, + {360,290, W,H }, + {520,290, W,H }, + { 40,430, W,H }, + {200,430, W,H }, + {360,430, W,H }, + {520,430, W,H } + }; + + int channel; + int i; + + + + channellist = CNeutrinoApp::getInstance()->channelList; + channel = channellist->getActiveChannelNumber(); + + frameBuffer = CFrameBuffer::getInstance(); + + + + + // $$$ mute + + + // -- paint background and - windows + paintBackground(); + for (i=0; i < (int)(sizeof(coord)/sizeof(coord[0])); i++) { + paintMiniTVBackground(coord[i].x,coord[i].y, coord[i].w, coord[i].h); + } + + + + + // experimental + for (i=0; i < (int)(sizeof(coord)/sizeof(coord[0])); i++) { + + u_char frame_buf[SCREEN_X*SCREEN_Y*2]; + int j; + + + // -- adjust pig and zap to channel, set capture size + printf ("pig: %d \n",i); + pig->show (coord[i].x,coord[i].y, coord[i].w, coord[i].h); + channellist->zapTo(channel); + capture->set_coord (coord[i].x, coord[i].y, coord[i].w, coord[i].h); +// capture->set_output_size (coord[i].w, coord[i].h); + + sleep (2); + + + // -- inner loop + // -- try 4 times (4 * 0,5 sec) to get a captured frame + for (j=0; j < 4; j++) { + + neutrino_msg_t msg; + neutrino_msg_data_t data; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(500); + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + printf ("pig inner loop: %d - %d \n",i,j); + + + // $$$ TEST + capture->readframe (frame_buf); + //if (frame_buf[10] == 0x00) continue; + + int a; + for (a=0; a<0x10; a++) { + printf("%02x ",frame_buf[a]); + } + printf ("\n"); + + if (msg == CRCInput::RC_timeout) { + printf ("pig inner loop timeout: \n"); + i = i; + } + + + // -- push other events + if ( msg > CRCInput::RC_MaxRC ) { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + + + + // zap, sleep 0.5 sec + // capture frame + + // loop 4 times frame "empty"? + // --> wait 0,5 sec, re-capture + + } + + // display frame + + // display add info (sendername, epg info) + // + + + channel ++; + } + + + + + // -- clear + clearTV(); + + // $$$ unmute + +} + + + + + +// +// -- some paint abstraction functions in this class +// + +void CChMosaic::clearTV() +{ + frameBuffer->paintBackgroundBoxRel(0,0,SCREEN_X-1,SCREEN_Y-1); +} + +void CChMosaic::paintBackground() +{ + fb_pixel_t col = 254; + frameBuffer->paintBoxRel(0, 0, SCREEN_X-1, SCREEN_Y-1, col); + //frameBuffer->paintBoxRel(0, 0, SCREEN_X-1, SCREEN_Y-1, frameBuffer->realcolor[0xFe]); +} + +void CChMosaic::paintMiniTVBackground(int x, int y, int w, int h) +{ + fb_pixel_t col = 240; + frameBuffer->paintBoxRel(x,y,w,h, col); + //frameBuffer->paintBoxRel(x,y,w,h, frameBuffer->realcolor[0xF0]); +} + + diff --git a/src/gui/ch_mosaic.h b/src/gui/ch_mosaic.h new file mode 100644 index 000000000..e2a8c8b7a --- /dev/null +++ b/src/gui/ch_mosaic.h @@ -0,0 +1,70 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __ch_mosaic__ +#define __ch_mosaic__ + +#include +#include "widget/menue.h" + +#include +#include + + +using namespace std; + +class CChMosaicHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + +}; + + +class CChMosaic +{ + public: + CChMosaic (); + ~CChMosaic (); + void doMosaic (); + + private: + void clearTV (); + void paintBackground (); + void paintMiniTVBackground(int x, int y, int w, int h); + + CPIG *pig; + CCAPTURE *capture; + int current_pig_pos; + + struct PIG_COORD { + int x,y,w,h; + }; + + CChannelList *channellist; + CFrameBuffer *frameBuffer; + +}; + + +#endif + diff --git a/src/gui/channellist.cpp b/src/gui/channellist.cpp new file mode 100644 index 000000000..ae2e7948c --- /dev/null +++ b/src/gui/channellist.cpp @@ -0,0 +1,1645 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gui/filebrowser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern CFrontend * frontend; +extern CBouquetList * bouquetList; /* neutrino.cpp */ +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ +extern SMSKeyInput * c_SMSKeyInput; +extern CPictureViewer * g_PicViewer; +extern CBouquetList * TVbouquetList; +extern CBouquetList * TVsatList; +extern CBouquetList * TVfavList; +extern CBouquetList * TVallList; +extern CBouquetList * RADIObouquetList; +extern CBouquetList * RADIOsatList; +extern CBouquetList * RADIOfavList; +extern CBouquetList * RADIOallList; + +#define PIC_W 52 +#define PIC_H 39 +#define ROUND_RADIUS 9 + +extern t_channel_id rec_channel_id; +extern bool autoshift; +int info_height = 0; +bool new_mode_active = 0; +//int list_changed = 0; +static bool pipzap; + +extern CBouquetManager *g_bouquetManager; +void sectionsd_getChannelEvents(CChannelEventList &eList, const bool tv_mode, t_channel_id *chidlist, int clen); +void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = ""); +void addChannelToBouquet(const unsigned int bouquet, const t_channel_id channel_id); + +extern int old_b_id; + +CChannelList::CChannelList(const char * const Name, bool historyMode, bool _vlist) +{ + frameBuffer = CFrameBuffer::getInstance(); + name = Name; + selected = 0; + liststart = 0; + tuned=0xfffffff; + zapProtection = NULL; + this->historyMode = historyMode; + vlist = _vlist; +//printf("************ NEW LIST %s : %x\n", name.c_str(), this);fflush(stdout); +} + +CChannelList::~CChannelList() +{ +//printf("************ DELETE LIST %s : %x\n", name.c_str(), this);fflush(stdout); + chanlist.clear(); +} + +void CChannelList::ClearList(void) +{ +//printf("************ CLEAR LIST %s : %x\n", name.c_str(), this);fflush(stdout); + chanlist.clear(); + chanlist.resize(1); +} + +void CChannelList::setSize(int newsize) +{ + chanlist.reserve(newsize); + //chanlist.resize(newsize); +} + +void CChannelList::addChannel(CZapitChannel* channel, int num) +{ +//printf("************ %s : addChannel: %s %x\n", name.c_str(), channel->getName().c_str(), channel);fflush(stdout); + if(num) + channel->number = num; + chanlist.push_back(channel); +} + +void CChannelList::putChannel(CZapitChannel* channel) +{ + int num = channel->number - 1; + if(num < 0) { + printf("CChannelList::addChannel error inserting at %d\n", num); + return; + } + if(num >= (int) chanlist.size()) { + chanlist.resize((unsigned) num + 1); + } + chanlist[num] = channel; +//printf("************ %s : me %x putChannel: %d: %s %x -> %x [0] %x\n", name.c_str(), this, num, channel->getName().c_str(), channel, chanlist[num], chanlist[0]);fflush(stdout); +} + +void CChannelList::updateEvents(void) +{ + CChannelEventList events; + + if (displayNext) { + if (chanlist.size()) { + time_t atime = time(NULL); + unsigned int count; + for (count=0; count < chanlist.size(); count++){ + //CChannelEventList events = g_Sectionsd->getEventsServiceKey(chanlist[liststart+count]->channel_id &0xFFFFFFFFFFFFULL); + sectionsd_getEventsServiceKey(chanlist[count]->channel_id &0xFFFFFFFFFFFFULL, events); + chanlist[count]->nextEvent.startTime = (long)0x7fffffff; + for ( CChannelEventList::iterator e= events.begin(); e != events.end(); ++e ) { + if (((long)(e->startTime) > atime) && + ((e->startTime) < (long)(chanlist[count]->nextEvent.startTime))) + { + chanlist[count]->nextEvent= *e; + break; //max: FIXME no sense to continue ? + } + } + } + } + } else { + t_channel_id *p_requested_channels = NULL; + int size_requested_channels = 0; + + if (chanlist.size()) { + size_requested_channels = chanlist.size()*sizeof(t_channel_id); + p_requested_channels = (t_channel_id*)malloc(size_requested_channels); + for (uint32_t count = 0; count < chanlist.size(); count++){ + p_requested_channels[count] = chanlist[count]->channel_id&0xFFFFFFFFFFFFULL; + } + + //CChannelEventList events = g_Sectionsd->getChannelEvents((CNeutrinoApp::getInstance()->getMode()) != NeutrinoMessages::mode_radio, p_requested_channels, size_requested_channels); + CChannelEventList events; + sectionsd_getChannelEvents(events, (CNeutrinoApp::getInstance()->getMode()) != NeutrinoMessages::mode_radio, p_requested_channels, size_requested_channels); + for (uint32_t count=0; count < chanlist.size(); count++) { + chanlist[count]->currentEvent = CChannelEvent(); + for ( CChannelEventList::iterator e = events.begin(); e != events.end(); ++e ) + if ((chanlist[count]->channel_id&0xFFFFFFFFFFFFULL) == e->get_channel_id()){ + chanlist[count]->currentEvent= *e; + break; + } + } + if (p_requested_channels != NULL) free(p_requested_channels); + } + } + events.clear(); +} + +struct CmpChannelBySat: public binary_function +{ + static bool comparetolower(const char a, const char b) + { + return tolower(a) < tolower(b); + }; + + bool operator() (const CZapitChannel * const c1, const CZapitChannel * const c2) + { + if(c1->getSatellitePosition() == c2->getSatellitePosition()) + return std::lexicographical_compare(c1->getName().begin(), c1->getName().end(), c2->getName().begin(), c2->getName().end(), comparetolower); + else + return c1->getSatellitePosition() < c2->getSatellitePosition(); +; + }; +}; + +struct CmpChannelByFreq: public binary_function +{ + static bool comparetolower(const char a, const char b) + { + return tolower(a) < tolower(b); + }; + + bool operator() (const CZapitChannel * const c1, const CZapitChannel * const c2) + { + if(c1->getFreqId() == c2->getFreqId()) + return std::lexicographical_compare(c1->getName().begin(), c1->getName().end(), c2->getName().begin(), c2->getName().end(), comparetolower); + else + return c1->getFreqId() < c2->getFreqId(); +; + }; +}; + +void CChannelList::SortAlpha(void) +{ + sort(chanlist.begin(), chanlist.end(), CmpChannelByChName()); +} + +void CChannelList::SortSat(void) +{ + sort(chanlist.begin(), chanlist.end(), CmpChannelBySat()); +} + +CZapitChannel* CChannelList::getChannel(int number) +{ + for (uint32_t i=0; i< chanlist.size();i++) { + if (chanlist[i]->number == number) + return chanlist[i]; + } + return(NULL); +} + +CZapitChannel* CChannelList::getChannel(t_channel_id channel_id) +{ + for (uint32_t i=0; i< chanlist.size();i++) { + if (chanlist[i]->channel_id == channel_id) + return chanlist[i]; + } + return(NULL); +} + +int CChannelList::getKey(int id) +{ + return chanlist[id]->number; +} + +static const std::string empty_string; + +const std::string & CChannelList::getActiveChannelName(void) const +{ + if (selected < chanlist.size()) + return chanlist[selected]->name; + else + return empty_string; +} + +t_satellite_position CChannelList::getActiveSatellitePosition(void) const +{ + if (selected < chanlist.size()) + return chanlist[selected]->getSatellitePosition(); + else + return 0; +} + +t_channel_id CChannelList::getActiveChannel_ChannelID(void) const +{ + if (selected < chanlist.size()) { +//printf("CChannelList::getActiveChannel_ChannelID me %x selected = %d %llx\n", (int) this, selected, chanlist[selected]->channel_id); + return chanlist[selected]->channel_id; + } else + return 0; +} + +int CChannelList::getActiveChannelNumber(void) const +{ + return (selected + 1); +} + +int CChannelList::doChannelMenu(void) +{ + int i = 0; + int select = -1; + static int old_selected = 0; + int ret = menu_return::RETURN_NONE; + signed int bouquet_id, old_bouquet_id, new_bouquet_id; + int result; + char cnt[5]; + t_channel_id channel_id; + + if(!bouquetList || g_settings.minimode) + return 0; + + CMenuWidget* menu = new CMenuWidget(LOCALE_CHANNELLIST_EDIT, NEUTRINO_ICON_SETTINGS); + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&select); + + sprintf(cnt, "%d", i); + menu->addItem(new CMenuForwarder(LOCALE_BOUQUETEDITOR_DELETE, true, NULL, selector, cnt, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED), old_selected == i++); + sprintf(cnt, "%d", i); + menu->addItem(new CMenuForwarder(LOCALE_BOUQUETEDITOR_MOVE, true, NULL, selector, cnt, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN), old_selected == i++); + sprintf(cnt, "%d", i); + menu->addItem(new CMenuForwarder(LOCALE_EXTRA_ADD_TO_BOUQUET, true, NULL, selector, cnt, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW), old_selected == i++); + sprintf(cnt, "%d", i); + menu->addItem(new CMenuForwarder(LOCALE_FAVORITES_MENUEADD, true, NULL, selector, cnt, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE), old_selected == i++); + ret = menu->exec(NULL, ""); + delete menu; + delete selector; + + if(select >= 0) { + old_selected = select; + channel_id = chanlist[selected]->channel_id; + switch(select) { + case 0: + hide(); + result = ShowMsgUTF ( LOCALE_BOUQUETEDITOR_DELETE, "Delete channel from bouquet?", CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo ); + + if(result == CMessageBox::mbrYes) { + bouquet_id = bouquetList->getActiveBouquetNumber(); + bouquet_id = g_bouquetManager->existsBouquet(bouquetList->Bouquets[bouquet_id]->channelList->getName()); + if (bouquet_id == -1) + return 0; + if(g_bouquetManager->existsChannelInBouquet(bouquet_id, channel_id)) { + g_bouquetManager->Bouquets[bouquet_id]->removeService(channel_id); + return 1; + } + } + break; + case 1: // move + old_bouquet_id = bouquetList->getActiveBouquetNumber(); + old_bouquet_id = g_bouquetManager->existsBouquet(bouquetList->Bouquets[old_bouquet_id]->channelList->getName()); + + do { + new_bouquet_id = bouquetList->exec(false); + } while(new_bouquet_id == -3); + + hide(); + if(new_bouquet_id < 0) + return 0; + new_bouquet_id = g_bouquetManager->existsBouquet(bouquetList->Bouquets[new_bouquet_id]->channelList->getName()); + if ((new_bouquet_id == -1) || (new_bouquet_id == old_bouquet_id)) + return 0; + + if(!g_bouquetManager->existsChannelInBouquet(new_bouquet_id, channel_id)) { + addChannelToBouquet(new_bouquet_id, channel_id); + } + if(g_bouquetManager->existsChannelInBouquet(old_bouquet_id, channel_id)) { + g_bouquetManager->Bouquets[old_bouquet_id]->removeService(channel_id); + } + return 1; + + break; + case 2: // add to + do { + bouquet_id = bouquetList->exec(false); + } while(bouquet_id == -3); + hide(); + if(bouquet_id < 0) + return 0; + bouquet_id = g_bouquetManager->existsBouquet(bouquetList->Bouquets[bouquet_id]->channelList->getName()); + if (bouquet_id == -1) + return 0; + if(!g_bouquetManager->existsChannelInBouquet(bouquet_id, channel_id)) { + addChannelToBouquet(bouquet_id, channel_id); + return 1; + } + break; + case 3: // add to my favorites + bouquet_id = g_bouquetManager->existsUBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + if(bouquet_id == -1) { + g_bouquetManager->addBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + bouquet_id = g_bouquetManager->existsUBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + } + if(!g_bouquetManager->existsChannelInBouquet(bouquet_id, channel_id)) { + addChannelToBouquet(bouquet_id, channel_id); + return 1; + } + + break; + default: + break; + } + } + return 0; +} + +int CChannelList::exec() +{ + displayNext = 0; // always start with current events + int nNewChannel = show(); + if ( nNewChannel > -1) { +#if 0 + if(this != CNeutrinoApp::getInstance ()->channelList) + CNeutrinoApp::getInstance ()->channelList->adjustToChannelID(chanlist[nNewChannel]->channel_id, false); + zapTo(nNewChannel); +#endif + //channelList->zapTo(bouquetList->Bouquets[bouquetList->getActiveBouquetNumber()]->channelList->getKey(nNewChannel)-1); + CNeutrinoApp::getInstance ()->channelList->zapTo(getKey(nNewChannel)-1); + } + + return nNewChannel; +} + +#define CHANNEL_SMSKEY_TIMEOUT 800 +/* return: >= 0 to zap, -1 on cancel, -3 on list mode change, -4 list edited, -2 zap but no restore old list/chan ?? */ +int CChannelList::show() +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + bool actzap = 0; + int res = -1; + + new_mode_active = 0; + pipzap = 0; + + //width = w_max (560, 0); + //height = h_max (420 + (1+3+16+3), 60); + width = w_max (((g_settings.channellist_extended)?720:660), 0); + height = h_max (450, 60); + + if (chanlist.empty()) { + return res; + } + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, name.c_str()); + + buttonHeight = 7 + std::min(16, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight()); + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + + if(theight < PIC_H) theight = PIC_H; + + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + listmaxshow = (height - theight - buttonHeight -0)/fheight; + height = theight + buttonHeight + listmaxshow * fheight; + //info_height = fheight + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getHeight() + 10; + info_height = 2*fheight + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getHeight() + 10; + + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - (height+ info_height)) / 2; + displayNext = false; + + paintHead(); + updateEvents(); + paint(); + + int oldselected = selected; + int zapOnExit = false; + bool bShowBouquetList = false; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + bool loop=true; + while (loop) { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd ); + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + if ( ( msg == CRCInput::RC_timeout ) || ( msg == (neutrino_msg_t)g_settings.key_channelList_cancel) ) { + if(!actzap) + selected = oldselected; + loop=false; + res = -1; + } + else if ((msg == CRCInput::RC_red) || (msg == CRCInput::RC_epg)) { + hide(); + + if ( g_EventList->exec(chanlist[selected]->channel_id, chanlist[selected]->name) == menu_return::RETURN_EXIT_ALL) { + res = -2; + loop = false; + } + paintHead(); + paint(); + } + else if (msg == CRCInput::RC_blue && ( bouquetList != NULL ) ) { //FIXME + bShowBouquetList = true; + loop=false; + } + else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + g_RCInput->postMsg (msg, 0); + loop = false; + res = -1; + } + else if ( msg == CRCInput::RC_setup) { + old_b_id = bouquetList->getActiveBouquetNumber(); + int ret = doChannelMenu(); + if(ret) { + res = -4; + loop = false; + } else { + old_b_id = -1; + paintHead(); + paint(); + } + + } + else if (msg == (neutrino_msg_t) g_settings.key_list_start) { + selected=0; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + if(new_mode_active) { actzap = true; zapTo(selected); } + } + else if (msg == (neutrino_msg_t) g_settings.key_list_end) { + selected=chanlist.size()-1; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + if(new_mode_active) { actzap = true; zapTo(selected); } + } + else if (msg == CRCInput::RC_up || (int) msg == g_settings.key_channelList_pageup) + { + int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + selected = chanlist.size() - 1; + + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + + if(new_mode_active) { actzap = true; zapTo(selected); } + //paintHead(); + } + else if (msg == CRCInput::RC_down || (int) msg == g_settings.key_channelList_pagedown) + { + unsigned int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= chanlist.size()) { + if (((chanlist.size() / listmaxshow) + 1) * listmaxshow == chanlist.size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((chanlist.size() / listmaxshow)+1) * listmaxshow))) ? (chanlist.size() - 1) : 0; + } + + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + + if(new_mode_active) { actzap = true; zapTo(selected); } + //paintHead(); + } + + else if ((msg == (neutrino_msg_t)g_settings.key_bouquet_up) && (bouquetList != NULL)) { + if (bouquetList->Bouquets.size() > 0) { + bool found = true; + uint32_t nNext = (bouquetList->getActiveBouquetNumber()+1) % bouquetList->Bouquets.size(); + if(bouquetList->Bouquets[nNext]->channelList->getSize() <= 0) { + found = false; + nNext = nNext < bouquetList->Bouquets.size()-1 ? nNext+1 : 0; + for(uint32_t i = nNext; i < bouquetList->Bouquets.size(); i++) { + if(bouquetList->Bouquets[i]->channelList->getSize() > 0) { + found = true; + nNext = i; + break; + } + } + } + if(found) { + bouquetList->activateBouquet(nNext, false); + res = bouquetList->showChannelList(); + loop = false; + } + } + } + else if ((msg == (neutrino_msg_t)g_settings.key_bouquet_down) && (bouquetList != NULL)) { + if (bouquetList->Bouquets.size() > 0) { + bool found = true; + int nNext = (bouquetList->getActiveBouquetNumber()+bouquetList->Bouquets.size()-1) % bouquetList->Bouquets.size(); + if(bouquetList->Bouquets[nNext]->channelList->getSize() <= 0) { + found = false; + nNext = nNext > 0 ? nNext-1 : bouquetList->Bouquets.size()-1; + for(int i = nNext; i > 0; i--) { + if(bouquetList->Bouquets[i]->channelList->getSize() > 0) { + found = true; + nNext = i; + break; + } + } + } + if(found) { + bouquetList->activateBouquet(nNext, false); + res = bouquetList->showChannelList(); + loop = false; + } + } + } + else if ( msg == CRCInput::RC_ok ) { + zapOnExit = true; + loop=false; + } + else if ( msg == CRCInput::RC_spkr ) { + new_mode_active = (new_mode_active ? 0 : 1); + paintHead(); + } + else if (CRCInput::isNumeric(msg) && (this->historyMode || g_settings.sms_channel)) { + if (this->historyMode) { //numeric zap + switch (msg) { + case CRCInput::RC_0:selected = 0;break; + case CRCInput::RC_1:selected = 1;break; + case CRCInput::RC_2:selected = 2;break; + case CRCInput::RC_3:selected = 3;break; + case CRCInput::RC_4:selected = 4;break; + case CRCInput::RC_5:selected = 5;break; + case CRCInput::RC_6:selected = 6;break; + case CRCInput::RC_7:selected = 7;break; + case CRCInput::RC_8:selected = 8;break; + case CRCInput::RC_9:selected = 9;break; + }; + zapOnExit = true; + loop = false; + } + else if(g_settings.sms_channel) { + uint32_t i; + unsigned char smsKey = 0; + c_SMSKeyInput->setTimeout(CHANNEL_SMSKEY_TIMEOUT); + + do { + smsKey = c_SMSKeyInput->handleMsg(msg); + //printf("SMS new key: %c\n", smsKey); + g_RCInput->getMsg_ms(&msg, &data, CHANNEL_SMSKEY_TIMEOUT-100); + } while ((msg >= CRCInput::RC_1) && (msg <= CRCInput::RC_9)); + + if (msg == CRCInput::RC_timeout || msg == CRCInput::RC_nokey) { + for(i = selected+1; i < chanlist.size(); i++) { + char firstCharOfTitle = chanlist[i]->name.c_str()[0]; + if(tolower(firstCharOfTitle) == smsKey) { +//printf("SMS chan found was= %d selected= %d i= %d %s\n", was_sms, selected, i, chanlist[i]->channel->name.c_str()); + break; + } + } + if(i >= chanlist.size()) { + for(i = 0; i < chanlist.size(); i++) { + char firstCharOfTitle = chanlist[i]->name.c_str()[0]; + if(tolower(firstCharOfTitle) == smsKey) { +//printf("SMS chan found was= %d selected= %d i= %d %s\n", was_sms, selected, i, chanlist[i]->channel->name.c_str()); + break; + } + } + } + if(i < chanlist.size()) { + int prevselected=selected; + selected=i; + + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) { + paint(); + } else { + paintItem(selected - liststart); + } + } + c_SMSKeyInput->resetOldKey(); + } + } + } + else if(CRCInput::isNumeric(msg)) { + //pushback key if... + selected = oldselected; + g_RCInput->postMsg( msg, data ); + loop=false; + } + else if ( msg == CRCInput::RC_yellow ) + { + displayNext = !displayNext; + paintHead(); // update button bar + updateEvents(); + paint(); + } + else if ((msg == CRCInput::RC_info) || (msg == CRCInput::RC_help)) { + hide(); + g_EpgData->show(chanlist[selected]->channel_id); + paintHead(); + paint(); + } else { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) { + loop = false; + res = - 2; + } + } + } + hide(); + if (bShowBouquetList) { + res = bouquetList->exec(true); +printf("CChannelList:: bouquetList->exec res %d\n", res); + } + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + new_mode_active = 0; + + if(NeutrinoMessages::mode_ts == CNeutrinoApp::getInstance()->getMode()) + return -1; + + if(zapOnExit) + res = selected; + +printf("CChannelList::show *********** res %d\n", res); + return(res); +} + +void CChannelList::hide() +{ + frameBuffer->paintBackgroundBoxRel(x, y, width, height+ info_height+ 5); + clearItem2DetailsLine (); +} + +bool CChannelList::showInfo(int pos, int epgpos) +{ + if((pos >= (signed int) chanlist.size()) || (pos<0)) + return false; + + CZapitChannel* chan = chanlist[pos]; + g_InfoViewer->showTitle(pos+1, chan->name, chan->getSatellitePosition(), chan->channel_id, true, epgpos); // UTF-8 + return true; +} + +int CChannelList::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data) +{ + if ( msg == NeutrinoMessages::EVT_PROGRAMLOCKSTATUS) { + // 0x100 als FSK-Status zeigt an, dass (noch) kein EPG zu einem Kanal der NICHT angezeigt + // werden sollte (vorgesperrt) da ist + // oder das bouquet des Kanals ist vorgesperrt + +//printf("program-lock-status: %d\n", data); + + if ((g_settings.parentallock_prompt == PARENTALLOCK_PROMPT_ONSIGNAL) || (g_settings.parentallock_prompt == PARENTALLOCK_PROMPT_CHANGETOLOCKED)) + { + if ( zapProtection != NULL ) + zapProtection->fsk = data; + else { + // require password if either + // CHANGETOLOCK mode and channel/bouquet is pre locked (0x100) + // ONSIGNAL mode and fsk(data) is beyond configured value + // if programm has already been unlocked, dont require pin +#if 1 + if ((data >= (neutrino_msg_data_t)g_settings.parentallock_lockage) && + ((chanlist[selected]->last_unlocked_EPGid != g_RemoteControl->current_EPGid) || (g_RemoteControl->current_EPGid == 0)) && + ((g_settings.parentallock_prompt != PARENTALLOCK_PROMPT_CHANGETOLOCKED) || (data >= 0x100))) + { + g_RemoteControl->stopvideo(); + zapProtection = new CZapProtection( g_settings.parentallock_pincode, data ); + + if ( zapProtection->check() ) + { + g_RemoteControl->startvideo(); + + // remember it for the next time + chanlist[selected]->last_unlocked_EPGid= g_RemoteControl->current_EPGid; + } + delete zapProtection; + zapProtection = NULL; + } + else + g_RemoteControl->startvideo(); +#endif + } + } + else + g_RemoteControl->startvideo(); + + return messages_return::handled; + } + else + return messages_return::unhandled; +} +/* bToo default to true */ +bool CChannelList::adjustToChannelID(const t_channel_id channel_id, bool bToo) +{ + unsigned int i; + +printf("CChannelList::adjustToChannelID me %x list size %d channel_id %llx\n", (int) this, chanlist.size(), channel_id);fflush(stdout); + for (i = 0; i < chanlist.size(); i++) { + if(chanlist[i] == NULL) { + printf("CChannelList::adjustToChannelID REPORT BUG !! ******************************** %d is NULL !!\n", i); + continue; + } + if (chanlist[i]->channel_id == channel_id) { + selected = i; + lastChList.store (selected, channel_id, false); + + tuned = i; + if (bToo && (bouquetList != NULL)) { + //bouquetList->adjustToChannel( getActiveChannelNumber()); + //bouquetList->adjustToChannelID(channel_id); +//FIXME test + if(CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_tv) { + TVbouquetList->adjustToChannelID(channel_id); + TVsatList->adjustToChannelID(channel_id); + TVfavList->adjustToChannelID(channel_id); + TVallList->adjustToChannelID(channel_id); + } + else if(CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_radio) { + RADIObouquetList->adjustToChannelID(channel_id); + RADIOsatList->adjustToChannelID(channel_id); + RADIOfavList->adjustToChannelID(channel_id); + RADIOallList->adjustToChannelID(channel_id); + } + } +//printf("CChannelList::adjustToChannelID me %x to %llx bToo %s OK: %d\n", (int) this, channel_id, bToo ? "yes" : "no", i);fflush(stdout); + return true; + } + } +//printf("CChannelList::adjustToChannelID me %x to %llx bToo %s FAILED\n", (int) this, channel_id, bToo ? "yes" : "no");fflush(stdout); + + return false; +} + +int CChannelList::hasChannel(int nChannelNr) +{ + for (uint32_t i=0;ichannel_id == channel_id) + return i; + } + return -1; +} + +// for adjusting bouquet's channel list after numzap or quickzap +void CChannelList::setSelected( int nChannelNr) +{ + selected = nChannelNr; +} + +// -- Zap to channel with channel_id +bool CChannelList::zapTo_ChannelID(const t_channel_id channel_id) +{ +printf("**************************** CChannelList::zapTo_ChannelID %llx\n", channel_id); + for (unsigned int i=0; ichannel_id == channel_id) { + zapTo (i); + return true; + } + } + return false; +} + +/* forceStoreToLastChannels defaults to false */ +void CChannelList::zapTo(int pos, bool forceStoreToLastChannels) +{ + if (chanlist.empty()) { + DisplayErrorMessage(g_Locale->getText(LOCALE_CHANNELLIST_NONEFOUND)); // UTF-8 + return; + } + if ( (pos >= (signed int) chanlist.size()) || (pos< 0) ) { + pos = 0; + } + + CZapitChannel* chan = chanlist[pos]; +printf("**************************** CChannelList::zapTo me %x %s tuned %d new %d %s -> %llx\n", (int) this, name.c_str(), tuned, pos, chan->name.c_str(), chan->channel_id); + if ( pos!=(int)tuned ) { + tuned = pos; + g_RemoteControl->zapTo_ChannelID(chan->channel_id, chan->name, !chan->bAlwaysLocked); // UTF-8 + } + if(!new_mode_active) { + selected= pos; + lastChList.store (selected, chan->channel_id, forceStoreToLastChannels); + /* remove recordModeActive from infobar */ + if(g_settings.auto_timeshift && !CNeutrinoApp::getInstance()->recordingstatus) { + g_InfoViewer->handleMsg(NeutrinoMessages::EVT_RECORDMODE, 0); + } + g_RCInput->postMsg( NeutrinoMessages::SHOW_INFOBAR, 0 ); + + if (bouquetList != NULL) { + //bouquetList->adjustToChannel( getActiveChannelNumber()); + //bouquetList->adjustToChannelID(chan->channel_id); +//FIXME test + if(CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_tv) { + TVbouquetList->adjustToChannelID(chan->channel_id); + TVsatList->adjustToChannelID(chan->channel_id); + TVfavList->adjustToChannelID(chan->channel_id); + } + else if(CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_radio) { + RADIObouquetList->adjustToChannelID(chan->channel_id); + RADIOsatList->adjustToChannelID(chan->channel_id); + RADIOfavList->adjustToChannelID(chan->channel_id); + } + } + } +} + +int CChannelList::numericZap(int key) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = -1; + + if (chanlist.empty()) { + DisplayErrorMessage(g_Locale->getText(LOCALE_CHANNELLIST_NONEFOUND)); // UTF-8 + return res; + } + + // -- quickzap "0" to last seen channel... + if (key == g_settings.key_lastchannel) { + t_channel_id channel_id = lastChList.getlast(1); + if(channel_id) { + lastChList.clear_storedelay (); // ignore store delay + zapTo_ChannelID(channel_id); + } + return res; + } + + if (key == g_settings.key_zaphistory) { + if(!autoshift && CNeutrinoApp::getInstance()->recordingstatus) { + CChannelList * orgList = bouquetList->orgChannelList; + CChannelList * channelList = new CChannelList(g_Locale->getText(LOCALE_CHANNELLIST_CURRENT_TP), false, true); + t_channel_id recid = rec_channel_id >> 16; + for ( unsigned int i = 0 ; i < orgList->chanlist.size(); i++) { + if((orgList->chanlist[i]->channel_id >> 16) == recid) { + channelList->addChannel(orgList->chanlist[i]); + } + } + if (channelList->getSize() != 0) { + channelList->adjustToChannelID(orgList->getActiveChannel_ChannelID(), false); + this->frameBuffer->paintBackground(); + res = channelList->exec(); +#if 0 + int newChannel = channelList->show() ; + + if (newChannel > -1) { //FIXME handle edit/mode change ?? + orgList->zapTo_ChannelID(channelList->chanlist[newChannel]->channel_id); + } +#endif + } + delete channelList; + return res; + } + // -- zap history bouquet, similar to "0" quickzap, but shows a menue of last channels + if (this->lastChList.size() > 1) { + CChannelList * channelList = new CChannelList(g_Locale->getText(LOCALE_CHANNELLIST_HISTORY), true, true); + + for(unsigned int i = 1 ; i < this->lastChList.size() ; ++i) { + t_channel_id channel_id = this->lastChList.getlast(i); + if(channel_id) { + CZapitChannel* channel = getChannel(channel_id); + if(channel) channelList->addChannel(channel); + } + } + if (channelList->getSize() != 0) { + this->frameBuffer->paintBackground(); + //int newChannel = channelList->exec(); + res = channelList->exec(); + } + delete channelList; + } + return res; + } + + //int ox = 300; + //int oy = 200; + int sx = 4 * g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getRenderWidth(widest_number) + 14; + int sy = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->getHeight() + 6; + + int ox = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - sx)/2; + int oy = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - sy)/2; + char valstr[10]; + int chn = CRCInput::getNumericValue(key); + int pos = 1; + int lastchan= -1; + bool doZap = true; + bool showEPG = false; + + while(1) { + if (lastchan != chn) { + sprintf((char*) &valstr, "%d", chn); + while(strlen(valstr)<4) + strcat(valstr,"-"); //"_" + + frameBuffer->paintBoxRel(ox, oy, sx, sy, COL_INFOBAR_PLUS_0); + + for (int i=3; i>=0; i--) { + valstr[i+ 1]= 0; + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP]->RenderString(ox+7+ i*((sx-14)>>2), oy+sy-3, sx, &valstr[i], COL_INFOBAR); + } + + showInfo(chn- 1); + lastchan= chn; + } + + g_RCInput->getMsg( &msg, &data, g_settings.timing[SNeutrinoSettings::TIMING_NUMERICZAP] * 10 ); + + if ( msg == CRCInput::RC_timeout ) { + if ( ( chn > (int)chanlist.size() ) || (chn == 0) ) + chn = tuned + 1; + break; + } + else if ( msg == CRCInput::RC_favorites || msg == CRCInput::RC_sat) { + } + else if (CRCInput::isNumeric(msg)) { + if (pos == 4) { + chn = 0; + pos = 1; + } else { + chn *= 10; + pos++; + } + chn += CRCInput::getNumericValue(msg); + } + else if ( msg == CRCInput::RC_ok ) { + if ( ( chn > (signed int) chanlist.size() ) || ( chn == 0 ) ) { + chn = tuned + 1; + } + break; + } + else if ( msg == (neutrino_msg_t)g_settings.key_quickzap_down ) { + if ( chn == 1 ) + chn = chanlist.size(); + else { + chn--; + + if (chn > (int)chanlist.size()) + chn = (int)chanlist.size(); + } + } + else if ( msg == (neutrino_msg_t)g_settings.key_quickzap_up ) { + chn++; + + if (chn > (int)chanlist.size()) + chn = 1; + } + else if ( ( msg == CRCInput::RC_home ) || ( msg == CRCInput::RC_left ) || ( msg == CRCInput::RC_right) ) + { + doZap = false; + break; + } + else if ( msg == CRCInput::RC_red ) { + if ( ( chn <= (signed int) chanlist.size() ) && ( chn != 0 ) ) { + doZap = false; + showEPG = true; + break; + } + } + else if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) { + doZap = false; + //res = menu_return::RETURN_EXIT_ALL; + break; + } + } + + frameBuffer->paintBackgroundBoxRel(ox, oy, sx, sy); + + chn--; + if (chn<0) + chn=0; + if ( doZap ) { + if(g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] == 0) + g_InfoViewer->killTitle(); + zapTo( chn ); + } else { + showInfo(tuned); + g_InfoViewer->killTitle(); + if ( showEPG ) + g_EventList->exec(chanlist[chn]->channel_id, chanlist[chn]->name); + } + return res; +} + +void CChannelList::virtual_zap_mode(bool up) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + if (chanlist.empty()) { + DisplayErrorMessage(g_Locale->getText(LOCALE_CHANNELLIST_NONEFOUND)); // UTF-8 + return; + } + + int chn = getActiveChannelNumber() + (up ? 1 : -1); + if (chn > (int)chanlist.size()) + chn = 1; + if (chn == 0) + chn = (int)chanlist.size(); + int lastchan= -1; + bool doZap = true; + bool showEPG = false; + int epgpos = 0; + + while(1) + { + if (lastchan != chn || (epgpos != 0)) + { + showInfo(chn- 1, epgpos); + lastchan= chn; + } + epgpos = 0; + g_RCInput->getMsg( &msg, &data, 15*10 ); // 15 seconds, not user changable + //printf("########### %u ### %u #### %u #######\n", msg, NeutrinoMessages::EVT_TIMER, CRCInput::RC_timeout); + + if ( msg == CRCInput::RC_ok ) + { + if ( ( chn > (signed int) chanlist.size() ) || ( chn == 0 ) ) + { + chn = tuned + 1; + } + break; + } + else if ( msg == CRCInput::RC_left ) + { + if ( chn == 1 ) + chn = chanlist.size(); + else + { + chn--; + + if (chn > (int)chanlist.size()) + chn = (int)chanlist.size(); + } + } + else if ( msg == CRCInput::RC_right ) + { + chn++; + + if (chn > (int)chanlist.size()) + chn = 1; + } + else if ( msg == CRCInput::RC_up ) + { + epgpos = -1; + } + else if ( msg == CRCInput::RC_down ) + { + epgpos = 1; + } + else if ( ( msg == CRCInput::RC_home ) || ( msg == CRCInput::RC_timeout ) ) + { + // Abbruch ohne Channel zu wechseln + doZap = false; + break; + } + else if ( msg == CRCInput::RC_red ) + { + // Rote Taste zeigt EPG fuer gewaehlten Kanal an + if ( ( chn <= (signed int) chanlist.size() ) && ( chn != 0 ) ) + { + doZap = false; + showEPG = true; + break; + } + } + else if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + doZap = false; + break; + } + } + g_InfoViewer->clearVirtualZapMode(); + + chn--; + if (chn<0) + chn=0; + if ( doZap ) + { + if(g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] == 0) + g_InfoViewer->killTitle(); + zapTo( chn ); + } + else + { + showInfo(tuned); + g_InfoViewer->killTitle(); + + // Rote Taste zeigt EPG fuer gewaehlten Kanal an + if ( showEPG ) + g_EventList->exec(chanlist[chn]->channel_id, chanlist[chn]->name); + } +} + +void CChannelList::quickZap(int key, bool cycle) +{ + if(chanlist.size() == 0) + return; + + int bsize = bouquetList->Bouquets.size(); + if(!cycle && bsize > 1) { + int bactive = bouquetList->getActiveBouquetNumber(); + int cactive = selected; + +printf("CChannelList::quickZap: selected %d total %d active bouquet %d total %d\n", cactive, chanlist.size(), bactive, bsize); + if ( (key==g_settings.key_quickzap_down) || (key == CRCInput::RC_left)) { + cactive--; + if(cactive < 0) { + if(bactive == 0) + bactive = bsize - 1; + else + bactive--; + bouquetList->activateBouquet(bactive, false); + cactive = bouquetList->Bouquets[bactive]->channelList->getSize() - 1; + } else + selected = cactive; + } + else if ((key==g_settings.key_quickzap_up) || (key == CRCInput::RC_right)) { + cactive++; + if(cactive >= chanlist.size()) { + bactive = (bactive + 1) % bsize; + bouquetList->activateBouquet(bactive, false); + cactive = 0; + } else + selected = cactive; + } +printf("CChannelList::quickZap: new selected %d total %d active bouquet %d total %d\n", cactive, bouquetList->Bouquets[bactive]->channelList->getSize(), bactive, bsize); + CNeutrinoApp::getInstance()->channelList->zapTo(bouquetList->Bouquets[bactive]->channelList->getKey(cactive)-1); + } else { + if ( (key==g_settings.key_quickzap_down) || (key == CRCInput::RC_left)) { + if(selected == 0) + selected = chanlist.size()-1; + else + selected--; + } + else if ((key==g_settings.key_quickzap_up) || (key == CRCInput::RC_right)) { + selected = (selected+1)%chanlist.size(); + } + + //printf("[neutrino] quick zap selected = %d bouquetList %x getActiveBouquetNumber %d orgChannelList %x\n", selected, bouquetList, bouquetList->getActiveBouquetNumber(), bouquetList->orgChannelList); + + CNeutrinoApp::getInstance()->channelList->zapTo(getKey(selected)-1); + } + g_RCInput->clearRCMsg(); //FIXME test for n.103 +} + +void CChannelList::paintDetails(int index) +{ + CChannelEvent *p_event; + if (displayNext) { + p_event = &chanlist[index]->nextEvent; + } else { + p_event = &chanlist[index]->currentEvent; + } + +#if 0 + if (chanlist[index]->currentEvent.description.empty()) { + frameBuffer->paintBackgroundBoxRel(x, y+ height, width, info_height); + } else +#endif + { + frameBuffer->paintBoxRel(x+2, y + height + 2, width-4, info_height - 4, COL_MENUCONTENTDARK_PLUS_0, ROUND_RADIUS, 3);//round + + if (!p_event->description.empty()) { + char cNoch[50]; // UTF-8 + char cSeit[50]; // UTF-8 + + struct tm *pStartZeit = localtime(&p_event->startTime); + unsigned seit = ( time(NULL) - p_event->startTime ) / 60; + if (displayNext) { + sprintf(cNoch, "(%d min)", p_event->duration / 60); + sprintf(cSeit, g_Locale->getText(LOCALE_CHANNELLIST_START), pStartZeit->tm_hour, pStartZeit->tm_min); + } else { + sprintf(cSeit, g_Locale->getText(LOCALE_CHANNELLIST_SINCE), pStartZeit->tm_hour, pStartZeit->tm_min); + int noch = (p_event->startTime + p_event->duration - time(NULL)) / 60; + if ((noch< 0) || (noch>=10000)) + noch= 0; + sprintf(cNoch, "(%d / %d min)", seit, noch); + } + int seit_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getRenderWidth(cSeit, true); // UTF-8 + int noch_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth(cNoch, true); // UTF-8 + + std::string text1= p_event->description; + std::string text2= p_event->text; + + int xstart = 10; + if (g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text1, true) > (width - 30 - seit_len) ) + { + // zu breit, Umbruch versuchen... + int pos; + do { + pos = text1.find_last_of("[ -.]+"); + if ( pos!=-1 ) + text1 = text1.substr( 0, pos ); + } while ( ( pos != -1 ) && (g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text1, true) > (width - 30 - seit_len) ) ); + + std::string text3 = p_event->description.substr(text1.length()+ 1); + + if (!(text2.empty())) + text3= text3+ " - "; + + xstart += g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text3, true); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 10, y+ height+ 5+ 2* fheight, width - 30- noch_len, text3, COL_MENUCONTENTDARK, 0, true); + } + + if (!(text2.empty())) { + while ( text2.find_first_of("[ -.+*#?=!$%&/]+") == 0 ) + text2 = text2.substr( 1 ); + text2 = text2.substr( 0, text2.find('\n') ); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString(x+ xstart, y+ height+ 5+ 2* fheight, width- xstart- 20- noch_len, text2, COL_MENUCONTENTDARK, 0, true); + } + + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 10, y+ height+ 5+ fheight, width - 30 - seit_len, text1, COL_MENUCONTENTDARK, 0, true); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString (x+ width- 10- seit_len, y+ height+ 5+ fheight , seit_len, cSeit, COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x+ width- 10- noch_len, y+ height+ 5+ 2* fheight- 2, noch_len, cNoch, COL_MENUCONTENTDARK, 0, true); // UTF-8 + } + + char buf[128]; + transponder_id_t ct = chanlist[index]->getTransponderId(); + transponder_list_t::iterator tpI = transponders.find(ct); + int len = snprintf(buf, sizeof(buf), "%d ", chanlist[index]->getFreqId()); + + if(tpI != transponders.end()) { + char * f, *s, *m; + switch(frontend->getInfo()->type) { + case FE_QPSK: + frontend->getDelSys(tpI->second.feparams.u.qpsk.fec_inner, dvbs_get_modulation(tpI->second.feparams.u.qpsk.fec_inner), f, s, m); + len += snprintf(&buf[len], sizeof(buf) - len, "%c %d %s %s %s ", tpI->second.polarization ? 'V' : 'H', tpI->second.feparams.u.qpsk.symbol_rate/1000, f, s, m); + break; + case FE_QAM: + frontend->getDelSys(tpI->second.feparams.u.qam.fec_inner, tpI->second.feparams.u.qam.modulation, f, s, m); + len += snprintf(&buf[len], sizeof(buf) - len, "%d %s %s %s ", tpI->second.feparams.u.qam.symbol_rate/1000, f, s, m); + break; + case FE_OFDM: + case FE_ATSC: + break; + } + } + + if(chanlist[index]->pname) + snprintf(&buf[len], sizeof(buf) - len, "(%s)\n", chanlist[index]->pname); + else { + sat_iterator_t sit = satellitePositions.find(chanlist[index]->getSatellitePosition()); + if(sit != satellitePositions.end()) { + int satNameWidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getRenderWidth (sit->second.name); + snprintf(&buf[len], sizeof(buf) - len, "(%s)\n", sit->second.name.c_str()); + } + } + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 10, y+ height+ 5+ 3*fheight, width - 30, buf, COL_MENUCONTENTDARK, 0, true); + } +} + +void CChannelList::clearItem2DetailsLine () +{ + paintItem2DetailsLine (-1, 0); +} + +void CChannelList::paintItem2DetailsLine (int pos, int ch_index) +{ + #define ConnectLineBox_Width 16 + + int xpos = x - ConnectLineBox_Width; + int ypos1 = y + theight+0 + pos*fheight; + int ypos2 = y + height; + int ypos1a = ypos1 + (fheight/2)-2; + int ypos2a = ypos2 + (info_height/2)-2; + fb_pixel_t col1 = COL_MENUCONTENT_PLUS_6; + fb_pixel_t col2 = COL_MENUCONTENT_PLUS_1; + + // Clear + frameBuffer->paintBackgroundBoxRel(xpos,y, ConnectLineBox_Width, height+info_height); + + // paint Line if detail info (and not valid list pos) + if (pos >= 0) { //pos >= 0 && chanlist[ch_index]->currentEvent.description != "") { + if(1) // FIXME why -> ? (!g_settings.channellist_extended) + { + int fh = fheight > 10 ? fheight - 10: 5; + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos1+5, 4, fh, col1); + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos1+5, 1, fh, col2); + + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos2+7, 4,info_height-14, col1); + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos2+7, 1,info_height-14, col2); + + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-16, ypos1a, 4,ypos2a-ypos1a, col1); + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-16, ypos1a, 1,ypos2a-ypos1a+4, col2); + + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-16, ypos1a, 12,4, col1); + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-16, ypos1a, 12,1, col2); + + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-16, ypos2a, 12,4, col1); + frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-12, ypos2a, 8,1, col2); + + frameBuffer->paintBoxRel(x, ypos2, width, info_height, col1, ROUND_RADIUS, 3); + } + } +} + +void CChannelList::paintItem(int pos) +{ + int ypos = y+ theight+0 + pos*fheight; + uint8_t color; + fb_pixel_t bgcolor; + bool iscurrent = true; + unsigned int curr = liststart + pos; + + if(!autoshift && CNeutrinoApp::getInstance()->recordingstatus && curr < chanlist.size()) { + iscurrent = (chanlist[curr]->channel_id >> 16) == (rec_channel_id >> 16); +//printf("recording %llx current %llx current = %s\n", rec_channel_id, chanlist[liststart + pos]->channel->channel_id, iscurrent? "yes" : "no"); + } + if (curr == selected) { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + paintItem2DetailsLine (pos, curr); + paintDetails(curr); + frameBuffer->paintBoxRel(x + width - 100 - PIC_W, y+(theight-PIC_H)/2, PIC_W, PIC_H, COL_MENUHEAD_PLUS_0); + g_PicViewer->DisplayLogo(chanlist[selected]->channel_id, x + width - 100 - PIC_W, y+(theight-PIC_H)/2, PIC_W, PIC_H); + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, ROUND_RADIUS, 3); + } else { + color = iscurrent ? COL_MENUCONTENT : COL_MENUCONTENTINACTIVE; + bgcolor = iscurrent ? COL_MENUCONTENT_PLUS_0 : COL_MENUCONTENTINACTIVE_PLUS_0; + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, 0, 3); + } +#if 0 + if(curr < chanlist.size()) { + CZapitChannel* chan = chanlist[liststart+pos]; + char tmp[10]; + sprintf((char*) tmp, "%d", this->historyMode ? pos : chan->number); + int numpos = x+5+numwidth- g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth(tmp); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(numpos,ypos+fheight, numwidth+5, tmp, color, fheight); + char nameAndDescription[255]; + if (!(chan->currentEvent.description.empty())) { + snprintf(nameAndDescription, sizeof(nameAndDescription), "%s - %s", + chan->name.c_str(), chan->currentEvent.description.c_str()); + } else { + snprintf(nameAndDescription, sizeof(nameAndDescription), "%s", chan->name.c_str()); + } + if (curr == selected) + CVFD::getInstance()->showMenuText(0, nameAndDescription, -1, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 5+ numwidth+ 10, ypos+ fheight, width- numwidth- 20- 15, nameAndDescription, color, 0, true); + } +#else + if(curr < chanlist.size()) { + char nameAndDescription[255]; + char tmp[10]; + CZapitChannel* chan = chanlist[curr]; + int prg_offset=0; + int title_offset=0; + uint8_t tcolor=(liststart + pos == selected) ? color : COL_MENUCONTENTINACTIVE; + int xtheight=fheight-2; + + if(g_settings.channellist_extended) + { + prg_offset=42; + title_offset=6; + } + + sprintf((char*) tmp, "%d", this->historyMode ? pos : chan->number); + + CChannelEvent *p_event=NULL; + if (displayNext) { + p_event = &chan->nextEvent; + } else { + p_event = &chan->currentEvent; + } + + if(chan->scrambled) + frameBuffer->paintIcon("ca.raw", x+width- 15 - 28, ypos + (fheight - 16)/2); + + int numpos = x+5+numwidth- g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth(tmp); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(numpos,ypos+fheight, numwidth+5, tmp, color, fheight); + + int l=0; + if (this->historyMode) + l = snprintf(nameAndDescription, sizeof(nameAndDescription), ": %d %s", chan->number, chan->name.c_str()); + else + l = snprintf(nameAndDescription, sizeof(nameAndDescription), "%s", chan->name.c_str()); + + if (!(p_event->description.empty())) { + + snprintf(nameAndDescription+l, sizeof(nameAndDescription)-l," - "); + unsigned int ch_name_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(nameAndDescription, true); + unsigned int ch_desc_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getRenderWidth(p_event->description, true); + + if ( (width- numwidth- 60- 15- prg_offset - ch_name_len)< ch_desc_len ) + ch_desc_len = (width- numwidth- 60- 15- ch_name_len - prg_offset); + if (ch_desc_len< 0) + ch_desc_len = 0; + + if(g_settings.channellist_extended){ + if(displayNext) + { + struct tm *pStartZeit = localtime(&p_event->startTime); + + sprintf((char*) tmp, "%02d:%02d", pStartZeit->tm_hour, pStartZeit->tm_min); +// g_Font[SNeutrinoSettings::FONT_TYPE_IMAGEINFO_SMALL]->RenderString(x+ 5+ numwidth+ 6, ypos+ xtheight, width- numwidth- 20- 15 -poffs, tmp, COL_MENUCONTENT, 0, true); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x+ 5+ numwidth+ 6, ypos+ xtheight, width- numwidth- 20- 15 -prg_offset, tmp, tcolor, 0, true); + } + else + { + time_t jetzt=time(NULL); + int runningPercent = 0; + + if (((jetzt - p_event->startTime + 30) / 60) < 0 ) + { + runningPercent= 0; + } + else + { + runningPercent=(jetzt-p_event->startTime) * 30 / p_event->duration; + if (runningPercent > 30) // this would lead to negative value in paintBoxRel + runningPercent = 30; // later on which can be fatal... + } + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset, ypos+fheight/4, 34, fheight/2, COL_MENUCONTENT_PLUS_3, 0);//fill passive + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset+2, ypos+2+fheight/4, 30, fheight/2-4, COL_MENUCONTENT_PLUS_1, 0);//frame(passive) + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset+2, ypos+2+fheight/4, runningPercent, fheight/2-4, COL_MENUCONTENT_PLUS_3, 0);//fill(active) + } + } + + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 5+ numwidth+ 10+prg_offset, ypos+ fheight, width- numwidth- 40- 15-prg_offset, nameAndDescription, color, 0, true); + if (g_settings.channellist_epgtext_align_right){ + // align right + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString(x + width - 20 - ch_desc_len - 28, ypos + fheight, ch_desc_len, p_event->description, (curr == selected)?COL_MENUCONTENTSELECTED:(!displayNext ? COL_MENUCONTENT : COL_MENUCONTENTINACTIVE) , 0, true); + } + else{ + // align left + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString(x+ 5+ numwidth+ 10+ ch_name_len+ 5+prg_offset, ypos+ fheight, ch_desc_len, p_event->description, (curr == selected)?COL_MENUCONTENTSELECTED:(!displayNext ? COL_MENUCONTENT : COL_MENUCONTENTINACTIVE) , 0, true); + } + } + else { + if(g_settings.channellist_extended){ + short runningPercent=0; + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset, ypos+fheight/4, 34, fheight/2, COL_MENUCONTENT_PLUS_3, 0);//fill passive + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset+2, ypos+2+fheight/4, 30, fheight/2-4, COL_MENUCONTENT_PLUS_1, 0);//frame(passive) + frameBuffer->paintBoxRel(x+ 5+ numwidth+ title_offset+2, ypos+2+fheight/4, runningPercent, fheight/2-4, COL_MENUCONTENT_PLUS_3, 0);//fill(active) + frameBuffer->paintLine(x+ 5+ numwidth+ title_offset, ypos+fheight/4+1,x+ 5+ numwidth+ title_offset+ 32, ypos+fheight/4+fheight/2-3, COL_MENUCONTENT_PLUS_3); + } + //name + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 5+ numwidth+ 10+prg_offset, ypos+ fheight, width- numwidth- 40- 15-prg_offset, nameAndDescription, color, 0, true); // UTF-8 + } + if (curr == selected) { + if (!(chan->currentEvent.description.empty())) { + snprintf(nameAndDescription, sizeof(nameAndDescription), "%s - %s", + chan->name.c_str(), p_event->description.c_str()); + CVFD::getInstance()->showMenuText(0, nameAndDescription, -1, true); // UTF-8 + } else + CVFD::getInstance()->showMenuText(0, chan->name.c_str(), -1, true); // UTF-8 + } + } +#endif +} + +#define NUM_LIST_BUTTONS 3 +struct button_label CChannelListButtons[NUM_LIST_BUTTONS] = +{ + { NEUTRINO_ICON_BUTTON_RED, LOCALE_INFOVIEWER_EVENTLIST}, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_INFOVIEWER_NEXT}, + { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_BOUQUETLIST_HEAD} +#if 0 + { NEUTRINO_ICON_BUTTON_GREEN, LOCALE_BOUQUETEDITOR_DELETE}, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_BOUQUETEDITOR_MOVE}, + { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_EXTRA_ADD_TO_BOUQUET} +#endif +}; + +#define NUM_VLIST_BUTTONS 3 +const struct button_label CChannelVListButtons[NUM_VLIST_BUTTONS] = +{ + { NEUTRINO_ICON_BUTTON_RED, LOCALE_INFOVIEWER_EVENTLIST}, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_INFOVIEWER_NEXT}, + { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_BOUQUETLIST_HEAD} + //{ NEUTRINO_ICON_BUTTON_BLUE, LOCALE_EXTRA_ADD_TO_BOUQUET} +}; + +void CChannelList::paintHead() +{ + // head + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+theight+0, width- 65, name, COL_MENUHEAD, 0, true); // UTF-8 + + int ButtonWidth = (width - 20) / 4; + + // foot + if (displayNext) { + CChannelListButtons[1].locale = LOCALE_INFOVIEWER_NOW; + } else { + CChannelListButtons[1].locale = LOCALE_INFOVIEWER_NEXT; + } + + frameBuffer->paintBoxRel(x, y + (height - buttonHeight), width, buttonHeight - 1, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); //round + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + (height - buttonHeight) + 3, ButtonWidth, + vlist ? NUM_VLIST_BUTTONS : NUM_LIST_BUTTONS, vlist ? CChannelVListButtons : CChannelListButtons); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 ); + if (bouquetList != NULL) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, x + width - 60, y + 5); // icon for bouquet list button + frameBuffer->paintIcon(new_mode_active ? NEUTRINO_ICON_BUTTON_MUTE_ZAP_ACTIVE : NEUTRINO_ICON_BUTTON_MUTE_ZAP_INACTIVE, x + width - 90, y + 5); +} + +void CChannelList::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + //FIXME do we need to find biggest chan number in list ? + numwidth = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth("0000"); + //updateEvents(); + + frameBuffer->paintBoxRel(x, y+theight, width, height-buttonHeight-theight, COL_MENUCONTENT_PLUS_0, 0, 2); + + for(unsigned int count = 0; count < listmaxshow; count++) { + paintItem(count); + } + + int ypos = y+ theight; + int sb = fheight* listmaxshow; + frameBuffer->paintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((chanlist.size()- 1)/ listmaxshow)+ 1; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ sbs*(sb-4)/sbc, 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3); + +} + +int CChannelList::getSize() const +{ + return this->chanlist.size(); +} + +int CChannelList::getSelectedChannelIndex() const +{ + return this->selected; +} diff --git a/src/gui/channellist.h b/src/gui/channellist.h new file mode 100644 index 000000000..5539cf6d8 --- /dev/null +++ b/src/gui/channellist.h @@ -0,0 +1,135 @@ +#ifndef __channellist__ +#define __channellist__ + +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +enum { + LIST_MODE_FAV, + LIST_MODE_PROV, + LIST_MODE_SAT, + LIST_MODE_ALL +}; + +class CChannelList +{ + private: + CFrameBuffer *frameBuffer; + unsigned int selected; + CLastChannel lastChList; + unsigned int liststart; + unsigned int listmaxshow; + unsigned int numwidth; + int fheight; // Fonthoehe Channellist-Inhalt + int theight; // Fonthoehe Channellist-Titel + int buttonHeight; + + std::string name; + //std::vector chanlist; + std::vector chanlist; + CZapProtection* zapProtection; + + int width; + int height; + int x; + int y; + + bool historyMode; + bool vlist; // "virtual" list, not bouquet + bool displayNext; + + void paintDetails(int index); + void clearItem2DetailsLine (); + void paintItem2DetailsLine (int pos, int ch_index); + void paintItem(int pos); + void paint(); + void paintHead(); + void hide(); + + public: + CChannelList(const char * const Name, bool historyMode = false, bool _vlist = false ); + ~CChannelList(); + //void addChannel(int key, int number, const std::string& name, const t_satellite_position satellitePosition, t_channel_id ids = 0); // UTF-8 + void addChannel(CZapitChannel* chan, int num = 0); + void putChannel(CZapitChannel* chan); + //void addChannel(CZapitChannel* channel, int key, int number, bool locked = false); + CZapitChannel* getChannel(int number); + CZapitChannel* getChannel(t_channel_id channel_id); + CZapitChannel* getChannelFromIndex( uint32_t index) { if (chanlist.size() > index) return chanlist[index]; else return NULL;}; + CZapitChannel* operator[]( uint32_t index) { if (chanlist.size() > index) return chanlist[index]; else return NULL;}; + int getKey(int); + + const char * const getName (void) const { return name.c_str(); }; + const std::string & getActiveChannelName (void) const; // UTF-8 + t_satellite_position getActiveSatellitePosition(void) const; + int getActiveChannelNumber (void) const; + t_channel_id getActiveChannel_ChannelID(void) const; + +/* CChannel * getChannelFromChannelID(const t_channel_id channel_id); */ + + void zapTo(int pos, bool forceStoreToLastChannels = false); + void virtual_zap_mode(bool up); + bool zapTo_ChannelID(const t_channel_id channel_id); + bool adjustToChannelID(const t_channel_id channel_id, bool bToo = true); + bool showInfo(int pos, int epgpos = 0); + void updateEvents(void); + int numericZap(int key); + int show(); + int exec(); + void quickZap(int key, bool cycle = false); + int hasChannel(int nChannelNr); + int hasChannelID(t_channel_id channel_id); + void setSelected( int nChannelNr); // for adjusting bouquet's channel list after numzap or quickzap + + int handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data); + + int getSize() const; + int getSelectedChannelIndex() const; + void setSize(int newsize); + unsigned int tuned; + int doChannelMenu(void); + void SortAlpha(void); + void SortSat(void); + void ClearList(void); + //friend class CZapitChannel; +}; +#endif diff --git a/src/gui/color.cpp b/src/gui/color.cpp new file mode 100644 index 000000000..d4e7e0fad --- /dev/null +++ b/src/gui/color.cpp @@ -0,0 +1,90 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +int convertSetupColor2RGB(const unsigned char r, const unsigned char g, const unsigned char b) +{ + unsigned char red = int( float(255./100.)*float(r) ); + unsigned char green = int( float(255./100.)*float(g) ); + unsigned char blue = int( float(255./100.)*float(b) ); + + return (red << 16) | (green << 8) | blue; +} + +int convertSetupAlpha2Alpha(unsigned char alpha) +{ + if(alpha == 0) return 0xFF; + else if(alpha >= 100) return 0; + unsigned char a = 100 - alpha; + int ret = int( float(0xFF/100.)*float(a) ); + return ret; +} + +void recalcColor(unsigned char &orginal, int fade) +{ + if(fade==100) + { + return; + } + int color = int( float( float(orginal) * float( float(fade) / 100.0)) ); + if(color>255) + color=255; + if(color<0) + color=0; + orginal = color; +} + +void protectColor( unsigned char &r, unsigned char &g, unsigned char &b, bool protect ) +{ + if (!protect) + return; + if ((r==0) && (g==0) && (b==0)) + { + r=1; + g=1; + b=1; + } +} + +void fadeColor(unsigned char &r, unsigned char &g, unsigned char &b, int fade, bool protect) +{ + recalcColor(r, fade); + recalcColor(g, fade); + recalcColor(b, fade); + protectColor(r,g,b, protect); +} diff --git a/src/gui/color.h b/src/gui/color.h new file mode 100644 index 000000000..d51dce641 --- /dev/null +++ b/src/gui/color.h @@ -0,0 +1,100 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __color__ +#define __color__ + +#define COL_MAXFREE 254-8*7 - 1 +#define COL_INFOBAR_SHADOW 254-8*7 +#define COL_INFOBAR 254-8*6 +#define COL_MENUHEAD 254-8*5 +#define COL_MENUCONTENT 254-8*4 +#define COL_MENUCONTENTDARK 254-8*3 +#define COL_MENUCONTENTSELECTED 254-8*2 +#define COL_MENUCONTENTINACTIVE 254-8*1 + +#define COL_BACKGROUND 255 + +#ifdef FB_USE_PALETTE +#define COL_INFOBAR_SHADOW_PLUS_0 (COL_INFOBAR_SHADOW + 0) +#define COL_INFOBAR_SHADOW_PLUS_1 (COL_INFOBAR_SHADOW + 1) +#define COL_INFOBAR_PLUS_0 (COL_INFOBAR + 0) +#define COL_INFOBAR_PLUS_1 (COL_INFOBAR + 1) +#define COL_INFOBAR_PLUS_3 (COL_INFOBAR + 3) +#define COL_INFOBAR_PLUS_7 (COL_INFOBAR + 7) +#define COL_MENUHEAD_PLUS_0 (COL_MENUHEAD + 0) +#define COL_MENUCONTENT_PLUS_0 (COL_MENUCONTENT + 0) +#define COL_MENUCONTENT_PLUS_1 (COL_MENUCONTENT + 1) +#define COL_MENUCONTENT_PLUS_2 (COL_MENUCONTENT + 2) +#define COL_MENUCONTENT_PLUS_3 (COL_MENUCONTENT + 3) +#define COL_MENUCONTENT_PLUS_4 (COL_MENUCONTENT + 4) +#define COL_MENUCONTENT_PLUS_5 (COL_MENUCONTENT + 5) +#define COL_MENUCONTENT_PLUS_6 (COL_MENUCONTENT + 6) +#define COL_MENUCONTENT_PLUS_7 (COL_MENUCONTENT + 7) +#define COL_MENUCONTENTDARK_PLUS_0 (COL_MENUCONTENTDARK + 0) +#define COL_MENUCONTENTDARK_PLUS_2 (COL_MENUCONTENTDARK + 2) +#define COL_MENUCONTENTSELECTED_PLUS_0 (COL_MENUCONTENTSELECTED + 0) +#define COL_MENUCONTENTSELECTED_PLUS_2 (COL_MENUCONTENTSELECTED + 2) +#define COL_MENUCONTENTINACTIVE_PLUS_0 (COL_MENUCONTENTINACTIVE + 0) +#define COL_BACKGROUND_PLUS_0 (COL_BACKGROUND + 0) +#else +#define COL_INFOBAR_SHADOW_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR_SHADOW + 0)]) +#define COL_INFOBAR_SHADOW_PLUS_1 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR_SHADOW + 1)]) +#define COL_INFOBAR_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR + 0)]) +#define COL_INFOBAR_PLUS_1 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR + 1)]) +#define COL_INFOBAR_PLUS_3 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR + 3)]) +#define COL_INFOBAR_PLUS_7 (CFrameBuffer::getInstance()->realcolor[(COL_INFOBAR + 7)]) +#define COL_MENUHEAD_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_MENUHEAD + 0)]) +#define COL_MENUCONTENT_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 0)]) +#define COL_MENUCONTENT_PLUS_1 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 1)]) +#define COL_MENUCONTENT_PLUS_2 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 2)]) +#define COL_MENUCONTENT_PLUS_3 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 3)]) +#define COL_MENUCONTENT_PLUS_4 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 4)]) +#define COL_MENUCONTENT_PLUS_5 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 5)]) +#define COL_MENUCONTENT_PLUS_6 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 6)]) +#define COL_MENUCONTENT_PLUS_7 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENT + 7)]) +#define COL_MENUCONTENTDARK_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENTDARK + 0)]) +#define COL_MENUCONTENTDARK_PLUS_2 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENTDARK + 2)]) +#define COL_MENUCONTENTSELECTED_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENTSELECTED + 0)]) +#define COL_MENUCONTENTSELECTED_PLUS_2 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENTSELECTED + 2)]) +#define COL_MENUCONTENTINACTIVE_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_MENUCONTENTINACTIVE + 0)]) +#define COL_BACKGROUND_PLUS_0 (CFrameBuffer::getInstance()->realcolor[(COL_BACKGROUND + 0)]) +#endif + + +int convertSetupColor2RGB(unsigned char r, unsigned char g, unsigned char b); +int convertSetupAlpha2Alpha(unsigned char alpha); + +void fadeColor(unsigned char &r, unsigned char &g, unsigned char &b, int fade, bool protect=true); + + +#endif diff --git a/src/gui/customcolor.h b/src/gui/customcolor.h new file mode 100644 index 000000000..47a6020cc --- /dev/null +++ b/src/gui/customcolor.h @@ -0,0 +1,57 @@ +#ifndef __custom_color__ +#define __custom_color__ + +#define COLOR_CUSTOM 0x0 +#ifdef FB_USE_PALETTE +/* +#define COL_WHITE (COLOR_CUSTOM + 0) +#define COL_RED (COLOR_CUSTOM + 1) +#define COL_GREEN (COLOR_CUSTOM + 2) +#define COL_BLUE (COLOR_CUSTOM + 3) +#define COL_YELLOW (COLOR_CUSTOM + 4) +#define COL_BLACK (COLOR_CUSTOM + 5) +*/ +#define COL_DARK_RED 0x02 +#define COL_DARK_GREEN 0x03 +#define COL_DARK_BLUE 0x05 +#define COL_LIGHT_GRAY 0x08 +#define COL_DARK_GRAY 0x09 +#define COL_RED 0x0A +#define COL_GREEN 0x0B +#define COL_YELLOW 0x0C +#define COL_BLUE 0x0D +#define COL_PURP 0x0E +#define COL_LIGHT_BLUE 0x0F +#define COL_WHITE 0x10 +#define COL_BLACK 0x11 +#else +#define COL_DARK_RED0 0x02 +#define COL_DARK_GREEN0 0x03 +#define COL_DARK_BLUE0 0x05 +#define COL_LIGHT_GRAY0 0x08 +#define COL_DARK_GRAY0 0x09 +#define COL_RED0 0x0A +#define COL_GREEN0 0x0B +#define COL_YELLOW0 0x0C +#define COL_BLUE0 0x0D +#define COL_PURP0 0x0E +#define COL_LIGHT_BLUE0 0x0F +#define COL_WHITE0 0x10 +#define COL_BLACK0 0x11 + +#define COL_DARK_RED (CFrameBuffer::getInstance()->realcolor[0x02]) +#define COL_DARK_GREEN (CFrameBuffer::getInstance()->realcolor[0x03]) +#define COL_DARK_BLUE (CFrameBuffer::getInstance()->realcolor[0x05]) +#define COL_LIGHT_GRAY (CFrameBuffer::getInstance()->realcolor[0x08]) +#define COL_DARK_GRAY (CFrameBuffer::getInstance()->realcolor[0x09]) +#define COL_RED (CFrameBuffer::getInstance()->realcolor[0x0A]) +#define COL_GREEN (CFrameBuffer::getInstance()->realcolor[0x0B]) +#define COL_YELLOW (CFrameBuffer::getInstance()->realcolor[0x0C]) +#define COL_BLUE (CFrameBuffer::getInstance()->realcolor[0x0D]) +#define COL_PURP (CFrameBuffer::getInstance()->realcolor[0x0E]) +#define COL_LIGHT_BLUE (CFrameBuffer::getInstance()->realcolor[0x0F]) +#define COL_WHITE (CFrameBuffer::getInstance()->realcolor[0x10]) +#define COL_BLACK (CFrameBuffer::getInstance()->realcolor[0x11]) +#endif + +#endif diff --git a/src/gui/dboxinfo.cpp b/src/gui/dboxinfo.cpp new file mode 100644 index 000000000..43e39fa8d --- /dev/null +++ b/src/gui/dboxinfo.cpp @@ -0,0 +1,207 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +static const int FSHIFT = 16; /* nr of bits of precision */ +#define FIXED_1 (1<> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) +#define ROUND_RADIUS 9 + +CDBoxInfoWidget::CDBoxInfoWidget() +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + // width = 600; + // height = hheight+13*mheight+ 10; + width = w_max (600, 0); + height = h_max (hheight+14*mheight+ 10, 0); + + + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height) / 2) + g_settings.screen_StartY; +} + + +int CDBoxInfoWidget::exec(CMenuTarget* parent, const std::string &) +{ + if (parent) + { + parent->hide(); + } + paint(); + + int res = g_RCInput->messageLoop(); + + hide(); + return res; +} + +void CDBoxInfoWidget::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} +#define FP_IOCTL_GET_LNB_CURRENT 0x100 +#define FP_IOCTL_GETID 0 +#define FP_IOCTL_UPGRADE_CTRL 0x200 + +void CDBoxInfoWidget::paint() +{ + int ypos=y; + int i = 0; + frameBuffer->paintBoxRel(x, ypos, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10, ypos+ hheight+1, width, "Uptime/Mem/Fs info", COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintBoxRel(x, ypos+ hheight, width, height- hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + ypos+= hheight + (mheight >>1); + +#if 0 + int power = 0; + int is_val = 0xdeadbabe; + int fp_ver = 0; + + int fp = open("/dev/dbox/fp0", O_RDWR); + if(fp > 0) { + fp_ver = ioctl(fp, FP_IOCTL_GETID); + if(fp_ver < 0) fp_ver = 0; + ioctl(fp, FP_IOCTL_UPGRADE_CTRL, &is_val); + if(is_val != 1) is_val = 0; + ioctl(fp, FP_IOCTL_GET_LNB_CURRENT, &power); + close(fp); + } + else is_val = 0; +#endif + char buf[256]; + FILE* fd = fopen("/proc/cpuinfo", "rt"); + if (fd==NULL) { + printf("error while opening proc-cpuinfo\n" ); + } else { + + fgets(buf,255,fd); + while(!feof(fd)) { + if(fgets(buf,255,fd)!=NULL) { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, buf, COL_MENUCONTENT); + ypos+= mheight; + i++; + if(i > 6) break; + } + } + fclose(fd); + } + +#if 0 + sprintf(buf, "FP version 1.0%d, Upgrade Available: %s, LNB current: %d\n", fp_ver, is_val == 1 ? "yes" : "no", power); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, buf, COL_MENUCONTENT); + ypos+= mheight; +#endif + + int updays, uphours, upminutes; + struct sysinfo info; + struct tm *current_time; + time_t current_secs; + char ubuf[256], sbuf[256]; + + memset(sbuf, 0, 256); + time(¤t_secs); + current_time = localtime(¤t_secs); + + sysinfo(&info); + + sprintf( ubuf, "%2d:%02d%s up ", + current_time->tm_hour%12 ? current_time->tm_hour%12 : 12, + current_time->tm_min, current_time->tm_hour > 11 ? "pm" : "am"); + strcat(sbuf, ubuf); + updays = (int) info.uptime / (60*60*24); + if (updays) { + sprintf(ubuf, "%d day%s, ", updays, (updays != 1) ? "s" : ""); + strcat(sbuf, ubuf); + } + upminutes = (int) info.uptime / 60; + uphours = (upminutes / 60) % 24; + upminutes %= 60; + if(uphours) + sprintf(ubuf,"%2d:%02d, ", uphours, upminutes); + else + sprintf(ubuf,"%d min, ", upminutes); + strcat(sbuf, ubuf); + + sprintf(ubuf, "load: %ld.%02ld, %ld.%02ld, %ld.%02ld\n", + LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]), + LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]), + LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2])); + strcat(sbuf, ubuf); + ypos+= mheight/2; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, sbuf, COL_MENUCONTENT); + ypos+= mheight; + + sprintf(ubuf, "memory total %dKb, free %dKb", (int) info.totalram/1024, (int) info.freeram/1024); + ypos+= mheight/2; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, ubuf, COL_MENUCONTENT); + ypos+= mheight; + + struct statfs s; + //long blocks_used; + //long blocks_percent_used; + + if (::statfs("/var", &s) == 0) { + //printf("/var filesystem total %ldKb, free %ldKb\n", (long)s.f_blocks*s.f_bsize/1024, (long)s.f_bfree*s.f_bsize/1024); + sprintf(ubuf, "/var filesystem total %ldKb, free %ldKb", (long)s.f_blocks*s.f_bsize/1024, (long)s.f_bfree*s.f_bsize/1024); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, ubuf, COL_MENUCONTENT); + ypos+= mheight; + } + else + perror("/var statfs"); + if (::statfs("/hdd", &s) == 0) { +//printf("STATFS: type %lX free: %ld\n", s.f_type, s.f_bfree); + if( (s.f_type != (int) 0xEF53) && (s.f_type != (int) 0x52654973) && (s.f_type != (int) 0x6969) && (s.f_type != (int) 0xFF534D42) && (s.f_type != (int) 0x517B) && (s.f_type != (int) 0x58465342) ) + return; + //printf("/hdd filesystem total %ldKb, free %ldKb\n", (long)(s.f_blocks/1024)*s.f_bsize, (long)(s.f_bfree/1024)*s.f_bsize); + sprintf(ubuf, "/hdd filesystem total %ldKb, free %ldKb", (long)(s.f_blocks/1024)*s.f_bsize, (long)(s.f_bfree/1024)*s.f_bsize); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width, ubuf, COL_MENUCONTENT); + } +} diff --git a/src/gui/dboxinfo.h b/src/gui/dboxinfo.h new file mode 100644 index 000000000..4d454e01d --- /dev/null +++ b/src/gui/dboxinfo.h @@ -0,0 +1,66 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __dboxinfo__ +#define __dboxinfo__ + +#include + +#include + +#include + + +class CDBoxInfoWidget : public CMenuTarget +{ + private: + + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + void paint(); + + public: + + CDBoxInfoWidget(); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/gui/dmx_tspidbandwidth.c b/src/gui/dmx_tspidbandwidth.c new file mode 100644 index 000000000..7f6fccb7e --- /dev/null +++ b/src/gui/dmx_tspidbandwidth.c @@ -0,0 +1,235 @@ +/* + * some definition + */ +#define TS_LEN 188 +#define TS_SYNC_BYTE 0x47 +#define TS_BUF_SIZE (TS_LEN * 2048) /* fix dmx buffer size */ + +static unsigned long timeval_to_ms(const struct timeval *tv) +{ + return (tv->tv_sec * 1000) + ((tv->tv_usec + 500) / 1000); +} + +long delta_time_ms (struct timeval *tv, struct timeval *last_tv) +{ + return timeval_to_ms(tv) - timeval_to_ms(last_tv); +} + +int ts_pidbandwidth (OPTION *opt) +{ + + u_char buf[TS_BUF_SIZE]; + struct pollfd pfd; + struct dmx_pes_filter_params flt; + int dmxfd; + struct timeval tv,last_tv, first_tv; + int pid; + unsigned long long b_total; + long b; + long packets_bad; + long packets_total; + struct { // simple struct for storing last average bandwidth + unsigned long kb_sec; + unsigned long b_sec; + } last_avg; + + + + + if (opt->ts_raw_mode) { + pid = PID_FULL_TS; + } else { + pid = opt->pid; + } + + + + indent (0); + out_nl (2,""); + out_nl (2,"---------------------------------------------------------"); + out_nl (2,"PID bandwidth statistics..."); + if (opt->ts_raw_mode) { + out (2,"Full Transponder"); + } else { + out (2,"PID: %u (0x%04x)", pid, pid); + } + if (opt->rd_packet_count != 0) { + out (2," - max packet count: %ld ", opt->rd_packet_count); + } + out_nl (2,""); + out_nl (2,"---------------------------------------------------------"); + + + + // -- open DVR device for reading + pfd.events = POLLIN | POLLPRI; + if((pfd.fd = open(opt->devDvr,O_RDONLY|O_NONBLOCK)) < 0){ + IO_error(opt->devDvr); + return -1; + } + + + + if ((dmxfd=open(opt->devDemux,O_RDWR)) < 0) { + IO_error(opt->devDemux); + close(pfd.fd); + return -1; + } + ioctl (dmxfd,DMX_SET_BUFFER_SIZE, sizeof(buf)); + + flt.pid = pid; + flt.input = DMX_IN_FRONTEND; + flt.output = DMX_OUT_TS_TAP; + flt.pes_type = DMX_PES_OTHER; + flt.flags = DMX_IMMEDIATE_START; + if (ioctl(dmxfd, DMX_SET_PES_FILTER, &flt) < 0) { + IO_error("DMX_SET_PES_FILTER"); + close(pfd.fd); + close(dmxfd); + return -1; + } + + + + gettimeofday (&first_tv, NULL); + last_tv.tv_sec = first_tv.tv_sec; + last_tv.tv_usec = first_tv.tv_usec; + + b_total = 0; + packets_total = 0; + packets_bad = 0; + + while ( !isSigAbort() ) { + int b_len, b_start; + + // -- we will poll the PID in 2 secs intervall + int timeout = 2000; + + b_len = 0; + b_start = 0; + if (poll(&pfd, 1, timeout) > 0) { + if (pfd.revents & POLLIN) { + + b_len = read(pfd.fd, buf, sizeof(buf)); + gettimeofday (&tv, NULL); + + + if (b_len >= TS_LEN) { + b_start = sync_ts (buf, b_len); + } else { + b_len = 0; + } + + b = b_len - b_start; + if (b == 0) continue; + if (b < 0) { + IO_error("read"); + continue; + } + + b_total += b; + + + + // -- calc bandwidth + + { + unsigned long long bit_s; + long d_tim_ms; + int packets; + + packets = b/TS_LEN; + packets_total += packets; + + + // output on different verbosity levels + // -- current bandwidth + d_tim_ms = delta_time_ms (&tv, &last_tv); + if (d_tim_ms <= 0) d_tim_ms = 1; // ignore usecs + + out (3, "packets read: %3d/(%ld) d_time: %2ld.%03ld s = ", + packets, packets_total, d_tim_ms / 1000UL, d_tim_ms % 1000UL); + + // -- current bandwidth in kbit/sec + // --- cast to unsigned long long so it doesn't overflow as + // --- early, add time / 2 before division for correct rounding + bit_s = (((unsigned long long)b * 8000ULL) + ((unsigned long long)d_tim_ms / 2ULL)) + / (unsigned long long)d_tim_ms; + + out (1, "%5llu.%03llu kbit/s", bit_s / 1000ULL, bit_s % 1000ULL); + + + // -- average bandwidth + d_tim_ms = delta_time_ms (&tv,&first_tv); + if (d_tim_ms <= 0) d_tim_ms = 1; // ignore usecs + + bit_s = ((b_total * 8000ULL) + ((unsigned long long)d_tim_ms / 2ULL)) + / (unsigned long long)d_tim_ms; + + last_avg.kb_sec = (unsigned long) (bit_s / 1000ULL); + last_avg.b_sec = (unsigned long) (bit_s % 1000ULL); + + out (2, " (Avrg: %5lu.%03lu kbit/s)", last_avg.kb_sec, last_avg.b_sec); + + + // -- bad packet(s) check in buffer + { + int bp; + + bp = ts_error_count (buf+b_start, b); + packets_bad += bp; + out (4, " [bad: %d]", bp); + } + + out_NL (1); + + } + + + last_tv.tv_sec = tv.tv_sec; + last_tv.tv_usec = tv.tv_usec; + + + + // count packets ? + if (opt->rd_packet_count > 0) { + opt->rd_packet_count -= b/TS_LEN; + if (opt->rd_packet_count <= 0) break; + } + + + } + } + + } + + + // -- packets stats + + out (4, "## "); + + if (opt->ts_raw_mode) { out (2,"PID: "); + } else { out (2,"PID: %u (0x%04x)", pid, pid); + } + + out (4, " bad/total packets: %ld/%ld (= %1.1Lf%%)", + packets_bad, packets_total, + (((long double) packets_bad)*100)/packets_total ); + out (4, " Avrg: %5lu.%03lu kbit/s", + last_avg.kb_sec, last_avg.b_sec); + out_NL(4); + + + + + if (ioctl(dmxfd, DMX_STOP) < 0) { + IO_error("DMX_STOP"); + } + close(dmxfd); + close(pfd.fd); + + + return 0; + +} + diff --git a/src/gui/epg_menu.cpp b/src/gui/epg_menu.cpp new file mode 100644 index 000000000..fc01605f7 --- /dev/null +++ b/src/gui/epg_menu.cpp @@ -0,0 +1,80 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include + +// +// -- EPG Menue Handler Class +// -- to be used for calls from Menue +// -- (2004-03-06 rasc) +// + +int CEPGMenuHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + + + if (parent) { + parent->hide(); + } + + doMenu (); + return res; +} + +int CEPGMenuHandler::doMenu () +{ + CMenuWidget EPGSelector(LOCALE_EPGMENU_HEAD, "features.raw", 350); + + // EPGSelector.addItem(GenericMenuSeparator); + EPGSelector.addItem(new CMenuForwarder(LOCALE_EPGMENU_EVENTLIST , true, NULL, new CEventListHandler(), NULL, CRCInput::RC_red , NEUTRINO_ICON_BUTTON_RED ), false); + EPGSelector.addItem(new CMenuForwarder(LOCALE_EPGMENU_EPGPLUS , true, NULL, new CEPGplusHandler() , NULL, CRCInput::RC_green , NEUTRINO_ICON_BUTTON_GREEN ), false); + EPGSelector.addItem(new CMenuForwarder(LOCALE_EPGMENU_EVENTINFO , true, NULL, new CEPGDataHandler() , NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW), false); + EPGSelector.addItem(new CMenuForwarder(LOCALE_EPGMENU_STREAMINFO, true, NULL, new CStreamInfo2() , NULL, CRCInput::RC_blue , NEUTRINO_ICON_BUTTON_BLUE ), false); + + EPGSelector.addItem(GenericMenuSeparator); + + return EPGSelector.exec(NULL, ""); +} + diff --git a/src/gui/epg_menu.h b/src/gui/epg_menu.h new file mode 100644 index 000000000..8c9a98add --- /dev/null +++ b/src/gui/epg_menu.h @@ -0,0 +1,43 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __epg_menu__ +#define __epg_menu__ + + +#include "widget/menue.h" + + + +using namespace std; + +class CEPGMenuHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + int doMenu(); + +}; + + +#endif + diff --git a/src/gui/epgplus.cpp b/src/gui/epgplus.cpp new file mode 100644 index 000000000..ae298cd59 --- /dev/null +++ b/src/gui/epgplus.cpp @@ -0,0 +1,1309 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Copyright (C) 2004 Martin Griep 'vivamiga' + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "bouquetlist.h" + +#include +#include +#include + +#include +#include + +#define COL_MENUCONTENT_P1 254-8*4+1 +#define COL_MENUCONTENT_P2 254-8*4+2 +extern CBouquetList *bouquetList; + +Font * fonts[EpgPlus::NumberOfFontSettings]; +int sizes[EpgPlus::NumberOfSizeSettings]; + +time_t EpgPlus::duration = 0; + +int EpgPlus::horGap1Height = 0; +int EpgPlus::horGap2Height = 0; +int EpgPlus::verGap1Width = 0; +int EpgPlus::verGap2Width = 0; + +int EpgPlus::horGap1Color = 0; +int EpgPlus::horGap2Color = 0; +int EpgPlus::verGap1Color = 0; +int EpgPlus::verGap2Color = 0; + +int EpgPlus::sliderWidth = 0; +int EpgPlus::channelsTableWidth = 0; + +static EpgPlus::FontSetting fontSettingTable[] = { + {EpgPlus::EPGPlus_header_font, (char *) "Bold", 20}, + {EpgPlus::EPGPlus_timeline_fonttime, (char *) "Bold", 16}, + {EpgPlus::EPGPlus_timeline_fontdate, (char *) "Bold", 14}, + {EpgPlus::EPGPlus_channelentry_font, (char *) "Bold", 16}, + {EpgPlus::EPGPlus_channelevententry_font, (char *) "Regular", 16}, + {EpgPlus::EPGPlus_footer_fontbouquetchannelname, (char *) "Bold", 24}, + {EpgPlus::EPGPlus_footer_fonteventdescription, (char *) "Regular", 16}, + {EpgPlus::EPGPlus_footer_fonteventshortdescription, (char *) "Regular", 16}, + {EpgPlus::EPGPlus_footer_fontbuttons, (char *) "Regular", 16}, +}; + +static EpgPlus::SizeSetting sizeSettingTable[] = { + {EpgPlus::EPGPlus_channelentry_width, 100}, + {EpgPlus::EPGPlus_channelentry_separationlineheight, 2}, + {EpgPlus::EPGPlus_slider_width, 15}, + {EpgPlus::EPGPlus_horgap1_height, 4}, + {EpgPlus::EPGPlus_horgap2_height, 4}, + {EpgPlus::EPGPlus_vergap1_width, 4}, + {EpgPlus::EPGPlus_vergap2_width, 4}, +}; + +Font *EpgPlus::Header::font = NULL; + +EpgPlus::Header::Header (CFrameBuffer * frameBuffer, int x, int y, int width) +{ + this->frameBuffer = frameBuffer; + this->x = x; + this->y = y; + this->width = width; +} + +EpgPlus::Header::~Header () +{ +} + +void EpgPlus::Header::init () +{ + font = fonts[EPGPlus_header_font]; +} + +void EpgPlus::Header::paint () +{ + this->frameBuffer->paintBoxRel (this->x, this->y, this->width, this->font->getHeight (), COL_MENUHEAD_PLUS_0); + this->font->RenderString (this->x + 10, this->y + this->font->getHeight () + , this->width - 20, g_Locale->getText (LOCALE_EPGPLUS_HEAD) , COL_MENUHEAD, 0, true); +} + +int EpgPlus::Header::getUsedHeight () +{ + return font->getHeight (); +} + +Font *EpgPlus::TimeLine::fontTime = NULL; +Font *EpgPlus::TimeLine::fontDate = NULL; + +EpgPlus::TimeLine::TimeLine (CFrameBuffer * frameBuffer, int x, int y, int width, int startX, int durationX) +{ + this->frameBuffer = frameBuffer; + this->x = x; + this->y = y; + this->width = width; + this->startX = startX; + this->durationX = durationX; +} + +void EpgPlus::TimeLine::init () +{ + fontTime = fonts[EPGPlus_timeline_fonttime]; + fontDate = fonts[EPGPlus_timeline_fontdate]; +} + +EpgPlus::TimeLine::~TimeLine () +{ +} + +void EpgPlus::TimeLine::paint (time_t startTime, int duration) +{ + this->clearMark (); + + int xPos = this->startX; + + this->currentDuration = duration; + int numberOfTicks = this->currentDuration / (60 * 60) * 2; + int tickDist = (this->durationX) / numberOfTicks; + time_t tickTime = startTime; + bool toggleColor = false; + + // display date of begin + this->frameBuffer->paintBoxRel (this->x, this->y, this->width, this->fontTime->getHeight () + , toggleColor ? COL_MENUCONTENT_PLUS_2 : COL_MENUCONTENT_PLUS_1); + + this->fontDate->RenderString (this->x + 4, this->y + this->fontDate->getHeight () + , this->width, EpgPlus::getTimeString (startTime, "%d-%b") , COL_MENUCONTENT, 0, true); // UTF-8 + + // paint ticks + for (int i = 0; i < numberOfTicks; ++i, xPos += tickDist, tickTime += duration / numberOfTicks) { + int xWidth = tickDist; + if (xPos + xWidth > this->x + width) + xWidth = this->x + width - xPos; + + this->frameBuffer->paintBoxRel (xPos, this->y, xWidth, this->fontTime->getHeight () + , toggleColor ? COL_MENUCONTENT_PLUS_1 : COL_MENUCONTENT_PLUS_2); + + std::string timeStr = EpgPlus::getTimeString (tickTime, "%H"); + + int textWidth = this->fontTime->getRenderWidth (timeStr, true); + + this->fontTime->RenderString (xPos - textWidth - 4, this->y + this->fontTime->getHeight () + , textWidth, timeStr, toggleColor ? COL_MENUCONTENT_P1 : COL_MENUCONTENT_P2, 0, true); // UTF-8 + + timeStr = EpgPlus::getTimeString (tickTime, "%M"); + textWidth = this->fontTime->getRenderWidth (timeStr, true); + this->fontTime->RenderString (xPos + 4, this->y + this->fontTime->getHeight () + , textWidth, timeStr, toggleColor ? COL_MENUCONTENT_P1 : COL_MENUCONTENT_P2, 0, true); // UTF-8 + + toggleColor = !toggleColor; + } +} + +void EpgPlus::TimeLine::paintGrid () +{ + int xPos = this->startX; + int numberOfTicks = this->currentDuration / (60 * 60) * 2; + int tickDist = (this->durationX) / numberOfTicks; + // paint ticks + for (int i = 0; i < numberOfTicks; ++i, xPos += tickDist) { + // display a line for the tick + this->frameBuffer->paintVLineRel (xPos, this->y, this->fontTime->getHeight (), COL_MENUCONTENT_PLUS_5); + } +} + +void EpgPlus::TimeLine::paintMark (time_t startTime, int duration, int x, int width) +{ + // clear old mark + this->clearMark (); + + // paint new mark + this->frameBuffer->paintBoxRel (x, this->y + this->fontTime->getHeight () + , width, this->fontTime->getHeight () , COL_MENUCONTENTSELECTED_PLUS_0); + + // display start time before mark + std::string timeStr = EpgPlus::getTimeString (startTime, "%H:%M"); + int textWidth = this->fontTime->getRenderWidth (timeStr, true); + + this->fontTime->RenderString (x - textWidth, this->y + this->fontTime->getHeight () + this->fontTime->getHeight () + , textWidth, timeStr, COL_MENUCONTENT, 0, true); // UTF-8 + + // display end time after mark + timeStr = EpgPlus::getTimeString (startTime + duration, "%H:%M"); + textWidth = fontTime->getRenderWidth (timeStr, true); + + if (x + width + textWidth < this->x + this->width) { + this->fontTime->RenderString (x + width, this->y + this->fontTime->getHeight () + this->fontTime->getHeight () + , textWidth, timeStr, COL_MENUCONTENT, 0, true); // UTF-8 + } else if (textWidth < width - 10) { + this->fontTime->RenderString (x + width - textWidth, this->y + this->fontTime->getHeight () + this->fontTime->getHeight () + , textWidth, timeStr, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + } +} + +void EpgPlus::TimeLine::clearMark () +{ + this->frameBuffer->paintBoxRel (this->x, this->y + this->fontTime->getHeight () + , this->width, this->fontTime->getHeight () , COL_MENUCONTENT_PLUS_0); +} + +int EpgPlus::TimeLine::getUsedHeight () +{ + return std::max (fontDate->getHeight (), fontTime->getHeight ()) + + fontTime->getHeight (); +} + +Font *EpgPlus::ChannelEventEntry::font = NULL; +int EpgPlus::ChannelEventEntry::separationLineHeight = 0; + +EpgPlus::ChannelEventEntry::ChannelEventEntry (const CChannelEvent * channelEvent, CFrameBuffer * frameBuffer, TimeLine * timeLine, Footer * footer, int x, int y, int width) +{ + // copy neccessary? + if (channelEvent != NULL) + this->channelEvent = *channelEvent; + + this->frameBuffer = frameBuffer; + this->timeLine = timeLine; + this->footer = footer; + this->x = x; + this->y = y; + this->width = width; +} + +void EpgPlus::ChannelEventEntry::init () +{ + font = fonts[EPGPlus_channelevententry_font]; + separationLineHeight = sizes[EPGPlus_channelentry_separationlineheight]; +} + +EpgPlus::ChannelEventEntry::~ChannelEventEntry () +{ +} + +bool EpgPlus::ChannelEventEntry::isSelected (time_t selectedTime) const +{ + return (selectedTime >= this->channelEvent.startTime) && (selectedTime < this->channelEvent.startTime + time_t (this->channelEvent.duration)); +} + +bool sectionsd_getEPGidShort(event_id_t epgID, CShortEPGData * epgdata); +void EpgPlus::ChannelEventEntry::paint (bool isSelected, bool toggleColor) +{ + this->frameBuffer->paintBoxRel (this->x, this->y, this->width, this->font->getHeight () + , this->channelEvent.description.empty ()? COL_MENUCONTENT_PLUS_0 : (isSelected ? COL_MENUCONTENTSELECTED_PLUS_0 : (toggleColor ? COL_MENUCONTENT_PLUS_1 : COL_MENUCONTENT_PLUS_2))); + + this->font->RenderString (this->x + 2, this->y + this->font->getHeight () + , this->width - 4 > 0 ? this->width - 4 : 0, this->channelEvent.description, isSelected ? COL_MENUCONTENTSELECTED : (toggleColor ? COL_MENUCONTENT_P1 : COL_MENUCONTENT_P2) + , 0, true); + + // paint the separation line + if (separationLineHeight > 0) { + this->frameBuffer->paintBoxRel (this->x, this->y + this->font->getHeight () + , this->width, this->separationLineHeight, COL_MENUCONTENT_PLUS_5); + } + + if (isSelected) { + if (this->channelEvent.description.empty ()) { // dummy channel event + this->timeLine->clearMark (); + } else { + this->timeLine->paintMark (this->channelEvent.startTime, this->channelEvent.duration, this->x, this->width); + } + + CShortEPGData shortEpgData; + + //this->footer->paintEventDetails (this->channelEvent.description, g_Sectionsd->getEPGidShort (this->channelEvent.eventID, &shortEpgData) ? shortEpgData.info1 : ""); + this->footer->paintEventDetails (this->channelEvent.description, sectionsd_getEPGidShort(this->channelEvent.eventID, &shortEpgData) ? shortEpgData.info1 : ""); + + this->timeLine->paintGrid (); + } +} + +int EpgPlus::ChannelEventEntry::getUsedHeight () +{ + return font->getHeight () + separationLineHeight; +} + +Font *EpgPlus::ChannelEntry::font = NULL; +int EpgPlus::ChannelEntry::separationLineHeight = 0; + +EpgPlus::ChannelEntry::ChannelEntry (const CZapitChannel * channel, int index, CFrameBuffer * frameBuffer, Footer * footer, CBouquetList * bouquetList, int x, int y, int width) +{ + this->channel = channel; + + if (channel != NULL) { + std::stringstream displayName; + displayName << index + 1 << " " << channel->getName (); + + this->displayName = displayName.str (); + } + + this->index = index; + + this->frameBuffer = frameBuffer; + this->footer = footer; + this->bouquetList = bouquetList; + + this->x = x; + this->y = y; + this->width = width; +} + +void EpgPlus::ChannelEntry::init () +{ + font = fonts[EPGPlus_channelentry_font]; + separationLineHeight = sizes[EPGPlus_channelentry_separationlineheight]; +} + +EpgPlus::ChannelEntry::~ChannelEntry () +{ + for (TCChannelEventEntries::iterator It = this->channelEventEntries.begin (); + It != this->channelEventEntries.end (); It++) { + delete *It; + } + this->channelEventEntries.clear(); +} + +void EpgPlus::ChannelEntry::paint (bool isSelected, time_t selectedTime) +{ + this->frameBuffer->paintBoxRel (this->x, this->y, this->width, this->font->getHeight () + , isSelected ? COL_MENUCONTENTSELECTED_PLUS_0 : COL_MENUCONTENT_PLUS_0); + + this->font->RenderString (this->x + 2, this->y + this->font->getHeight () + , this->width - 4, this->displayName, isSelected ? COL_MENUCONTENTSELECTED : COL_MENUCONTENT, 0, true); + + if (isSelected) { + for (uint32_t i = 0; i < this->bouquetList->Bouquets.size (); + ++i) { + CBouquet *bouquet = this->bouquetList->Bouquets[i]; + for (int j = 0; j < bouquet->channelList->getSize (); + ++j) { + + if ((*bouquet->channelList)[j]->number == this->channel->number) { + this->footer->setBouquetChannelName (bouquet->channelList->getName () + , this->channel->getName () + ); + + bouquet = NULL; + + break; + } + } + if (bouquet == NULL) + break; + } + } + // paint the separation line + if (separationLineHeight > 0) { + this->frameBuffer->paintBoxRel (this->x, this->y + this->font->getHeight () + , this->width, this->separationLineHeight, COL_MENUCONTENT_PLUS_5); + } + + bool toggleColor = false; + for (TCChannelEventEntries::iterator It = this->channelEventEntries.begin (); + It != this->channelEventEntries.end (); + ++It) { + (*It)->paint (isSelected && (*It)->isSelected (selectedTime), toggleColor); + + toggleColor = !toggleColor; + } +} + +int EpgPlus::ChannelEntry::getUsedHeight () +{ + return font->getHeight () + + separationLineHeight; +} + +Font *EpgPlus::Footer::fontBouquetChannelName = NULL; +Font *EpgPlus::Footer::fontEventDescription = NULL; +Font *EpgPlus::Footer::fontEventShortDescription = NULL; +Font *EpgPlus::Footer::fontButtons = NULL; +int EpgPlus::Footer::color = 0; + +EpgPlus::Footer::Footer (CFrameBuffer * frameBuffer, int x, int y, int width) +{ + this->frameBuffer = frameBuffer; + this->x = x; + this->y = y; + this->width = width; +} + +EpgPlus::Footer::~Footer () +{ +} + +void EpgPlus::Footer::init () +{ + fontBouquetChannelName = fonts[EPGPlus_footer_fontbouquetchannelname]; + fontEventDescription = fonts[EPGPlus_footer_fonteventdescription]; + fontEventShortDescription = fonts[EPGPlus_footer_fonteventshortdescription]; + fontButtons = fonts[EPGPlus_footer_fontbuttons]; +} + +void EpgPlus::Footer::setBouquetChannelName (const std::string & newBouquetName, const std::string & newChannelName) +{ + this->currentBouquetName = newBouquetName; + this->currentChannelName = newChannelName; +} + +int EpgPlus::Footer::getUsedHeight () +{ + return fontBouquetChannelName->getHeight () + fontEventDescription->getHeight () + + fontEventShortDescription->getHeight () + fontButtons->getHeight (); +} + +void EpgPlus::Footer::paintEventDetails (const std::string & description, const std::string & shortDescription) +{ + int yPos = this->y; + + int height = this->fontBouquetChannelName->getHeight (); + + // clear the region + this->frameBuffer->paintBoxRel (this->x, yPos, this->width, height, COL_MENUHEAD_PLUS_0); + + yPos += height; + + // display new text + this->fontBouquetChannelName->RenderString (this->x + 10, yPos, this->width - 20, this->currentBouquetName + " : " + this->currentChannelName, COL_MENUHEAD, 0, true); + + height = this->fontEventDescription->getHeight (); + + // clear the region + this->frameBuffer->paintBoxRel (this->x, yPos, this->width, height, COL_MENUHEAD_PLUS_0); + + yPos += height; + + // display new text + this->fontEventDescription->RenderString (this->x + 10, yPos, this->width - 20, description, COL_MENUHEAD, 0, true); + + height = this->fontEventShortDescription->getHeight (); + + // clear the region + this->frameBuffer->paintBoxRel (this->x, yPos, this->width, height, COL_MENUHEAD_PLUS_0); + + yPos += height; + + // display new text + this->fontEventShortDescription->RenderString (this->x + 10, yPos, this->width - 20, shortDescription, COL_MENUHEAD, 0, true); +} + +struct button_label buttonLabels[] = { + {NEUTRINO_ICON_BUTTON_RED, LOCALE_EPGPLUS_ACTIONS}, + {NEUTRINO_ICON_BUTTON_GREEN, LOCALE_EPGPLUS_PAGE_UP}, + {NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_EPGPLUS_PAGE_DOWN}, + {NEUTRINO_ICON_BUTTON_BLUE, LOCALE_EPGPLUS_OPTIONS} +}; + +void EpgPlus::Footer::paintButtons (button_label * buttonLabels, int numberOfButtons) +{ + int yPos = this->y + this->getUsedHeight () - this->fontButtons->getHeight (); + + int buttonWidth = (this->width - 20) / 4; + + int buttonHeight = 7 + std::min (16, this->fontButtons->getHeight ()); + + this->frameBuffer->paintBoxRel (this->x, yPos, this->width, this->fontButtons->getHeight (), COL_MENUHEAD_PLUS_0); + + ::paintButtons (this->frameBuffer, this->fontButtons, g_Locale, this->x + 10, yPos + this->fontButtons->getHeight () - buttonHeight + 3, buttonWidth, numberOfButtons, buttonLabels); + + this->frameBuffer->paintIcon (NEUTRINO_ICON_BUTTON_HELP, this->x + this->width - 30, yPos - this->fontButtons->getHeight ()); +} + +EpgPlus::EpgPlus () +{ + this->init (); +} + +EpgPlus::~EpgPlus () +{ + this->free (); +} + +void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = ""); + +void EpgPlus::createChannelEntries (int selectedChannelEntryIndex) +{ + for (TChannelEntries::iterator It = this->displayedChannelEntries.begin (); + It != this->displayedChannelEntries.end (); It++) { + delete *It; + } + this->displayedChannelEntries.clear (); + + this->selectedChannelEntry = NULL; + + if (selectedChannelEntryIndex < this->channelList->getSize ()) { + for (;;) { + if (selectedChannelEntryIndex < this->channelListStartIndex) { + this->channelListStartIndex -= this->maxNumberOfDisplayableEntries; + if (this->channelListStartIndex < 0) + this->channelListStartIndex = 0; + } else if (selectedChannelEntryIndex >= this->channelListStartIndex + this->maxNumberOfDisplayableEntries) { + this->channelListStartIndex += this->maxNumberOfDisplayableEntries; + } else + break; + } + + int yPosChannelEntry = this->channelsTableY; + int yPosEventEntry = this->eventsTableY; + + for (int i = this->channelListStartIndex; (i < this->channelListStartIndex + this->maxNumberOfDisplayableEntries) + && (i < this->channelList->getSize ()); + ++i, yPosChannelEntry += this->entryHeight, yPosEventEntry += this->entryHeight) { + + CZapitChannel * channel = (*this->channelList)[i]; + + ChannelEntry *channelEntry = new ChannelEntry (channel, i, this->frameBuffer, this->footer, this->bouquetList, this->channelsTableX + 2, yPosChannelEntry, this->channelsTableWidth); +//printf("Going to get getEventsServiceKey for %llx\n", (channel->channel_id & 0xFFFFFFFFFFFFULL)); + //CChannelEventList channelEventList = g_Sectionsd->getEventsServiceKey (channel->channel->channel_id & 0xFFFFFFFFFFFFULL); + CChannelEventList channelEventList; + sectionsd_getEventsServiceKey(channel->channel_id & 0xFFFFFFFFFFFFULL, channelEventList); +//printf("channelEventList size %d\n", channelEventList.size()); + + int xPosEventEntry = this->eventsTableX; + int widthEventEntry = 0; + time_t lastEndTime = this->startTime; + + CChannelEventList::const_iterator lastIt (channelEventList.end ()); + //for (CChannelEventList::const_iterator It = channelEventList.begin (); (It != channelEventList.end ()) && (It->startTime < (this->startTime + this->duration)); ++It) + for (CChannelEventList::const_iterator It = channelEventList.begin (); It != channelEventList.end (); ++It) + { +//if(0x2bc000b004b7ULL == (channel->channel_id & 0xFFFFFFFFFFFFULL)) printf("*** Check1 event %s event start %ld this start %ld\n", It->description.c_str(), It->startTime, (this->startTime + this->duration)); + if(!(It->startTime < (this->startTime + this->duration)) ) + continue; + if ((lastIt == channelEventList.end ()) || (lastIt->startTime != It->startTime)) { + int startTimeDiff = It->startTime - this->startTime; + int endTimeDiff = this->startTime + time_t (this->duration) - It->startTime - time_t (It->duration); +//if(0x2bc000b004b7ULL == (channel->channel_id & 0xFFFFFFFFFFFFULL)) printf("*** Check event %s\n", It->description.c_str()); + if ((startTimeDiff >= 0) && (endTimeDiff >= 0)) { + // channel event fits completely in the visible part of time line + startTimeDiff = 0; + endTimeDiff = 0; + } else if ((startTimeDiff < 0) && (endTimeDiff < 0)) { + // channel event starts and ends outside visible part of the time line but covers complete visible part + } else if ((startTimeDiff < 0) && (endTimeDiff < this->duration)) { + // channel event starts before visible part of the time line but ends in the visible part + endTimeDiff = 0; + } else if ((endTimeDiff < 0) && (startTimeDiff < this->duration)) { + // channel event ends after visible part of the time line but starts in the visible part + startTimeDiff = 0; + } else if (startTimeDiff > 0) { // channel event starts and ends after visible part of the time line => break the loop +//if(0x2bc000b004b7ULL == (channel->channel_id & 0xFFFFFFFFFFFFULL)) printf("*** break 1\n"); + break; + } else { // channel event starts and ends after visible part of the time line => ignore the channel event +//if(0x2bc000b004b7ULL == (channel->channel_id & 0xFFFFFFFFFFFFULL)) printf("*** continue 1 startTimeDiff %ld endTimeDiff %ld\n", startTimeDiff, endTimeDiff); + continue; + } + + if (lastEndTime < It->startTime) { // there is a gap between last end time and new start time => fill it with a new event entry + + CChannelEvent channelEvent; + channelEvent.startTime = lastEndTime; + channelEvent.duration = It->startTime - channelEvent.startTime; + + ChannelEventEntry *channelEventEntry = new ChannelEventEntry (&channelEvent, this->frameBuffer, this->timeLine, this->footer, this->eventsTableX + ((channelEvent.startTime - this->startTime) * this->eventsTableWidth) / this->duration, yPosEventEntry, (channelEvent.duration * this->eventsTableWidth) / this->duration + 1); + channelEntry->channelEventEntries.push_back (channelEventEntry); + } + // correct position + xPosEventEntry = this->eventsTableX + ((It->startTime - startTimeDiff - this->startTime) * this->eventsTableWidth) / this->duration; + + // correct width + widthEventEntry = ((It->duration + startTimeDiff + endTimeDiff) * this->eventsTableWidth) / this->duration + 1; + + if (widthEventEntry < 0) + widthEventEntry = 0; + + if (xPosEventEntry + widthEventEntry > this->eventsTableX + this->eventsTableWidth) + widthEventEntry = this->eventsTableX + this->eventsTableWidth - xPosEventEntry; + + ChannelEventEntry *channelEventEntry = new ChannelEventEntry (&(*It) , this->frameBuffer, this->timeLine, this->footer, xPosEventEntry, yPosEventEntry, widthEventEntry); + + channelEntry->channelEventEntries.push_back (channelEventEntry); + lastEndTime = It->startTime + It->duration; + } + lastIt = It; + } + + if (lastEndTime < this->startTime + time_t (this->duration)) { // there is a gap between last end time and end of the timeline => fill it with a new event entry + + CChannelEvent channelEvent; + channelEvent.startTime = lastEndTime; + channelEvent.duration = this->startTime + this->duration - channelEvent.startTime; + + ChannelEventEntry *channelEventEntry = new ChannelEventEntry (&channelEvent, this->frameBuffer, this->timeLine, this->footer, this->eventsTableX + ((channelEvent.startTime - this->startTime) * this->eventsTableWidth) / this->duration, yPosEventEntry, (channelEvent.duration * this->eventsTableWidth) / this->duration + 1); + channelEntry->channelEventEntries.push_back (channelEventEntry); + } + this->displayedChannelEntries.push_back (channelEntry); + } + + this->selectedChannelEntry = this->displayedChannelEntries[selectedChannelEntryIndex - this->channelListStartIndex]; + } +} + +void EpgPlus::init () +{ + frameBuffer = CFrameBuffer::getInstance (); + currentViewMode = ViewMode_Scroll; + currentSwapMode = SwapMode_ByPage; + usableScreenWidth = w_max (g_settings.screen_EndX, 4); + usableScreenHeight = h_max (g_settings.screen_EndY, 4); + + std::string FileName = std::string (g_settings.font_file); + for (size_t i = 0; i < NumberOfFontSettings; ++i) { + std::string family = g_fontRenderer->getFamily (FileName.c_str ()); + Font *font = g_fontRenderer->getFont (family.c_str (), fontSettingTable[i].style, fontSettingTable[i].size); + + if (font == NULL) + font = g_fontRenderer->getFont (family.c_str (), "Regular", fontSettingTable[i].size); + + fonts[i] = font; + } + + for (size_t i = 0; i < NumberOfSizeSettings; ++i) { + sizes[i] = sizeSettingTable[i].size; + } + + Header::init (); + TimeLine::init (); + ChannelEntry::init (); + ChannelEventEntry::init (); + Footer::init (); + + this->selectedChannelEntry = NULL; + + channelsTableWidth = sizes[EPGPlus_channelentry_width]; + sliderWidth = sizes[EPGPlus_slider_width]; + + horGap1Height = sizes[EPGPlus_horgap1_height]; + horGap2Height = sizes[EPGPlus_horgap2_height]; + verGap1Width = sizes[EPGPlus_vergap1_width]; + verGap2Width = sizes[EPGPlus_vergap2_width]; + + int headerHeight = Header::getUsedHeight (); + int timeLineHeight = TimeLine::getUsedHeight (); + this->entryHeight = ChannelEntry::getUsedHeight (); + int footerHeight = Footer::getUsedHeight (); + + this->maxNumberOfDisplayableEntries = (this->usableScreenHeight - headerHeight - timeLineHeight - horGap1Height - horGap2Height - footerHeight) / this->entryHeight; + + this->usableScreenHeight = headerHeight + timeLineHeight + horGap1Height + this->maxNumberOfDisplayableEntries * this->entryHeight + horGap2Height + footerHeight; // recalc deltaY + this->usableScreenX = (((g_settings.screen_EndX - g_settings.screen_StartX) - this->usableScreenWidth) / 2) + g_settings.screen_StartX; + this->usableScreenY = (((g_settings.screen_EndY - g_settings.screen_StartY) - this->usableScreenHeight) / 2) + g_settings.screen_StartY; + + this->headerX = this->usableScreenX; + this->headerY = this->usableScreenY; + this->headerWidth = this->usableScreenWidth; + + this->timeLineX = this->usableScreenX; + this->timeLineY = this->usableScreenY + headerHeight; + this->timeLineWidth = this->usableScreenWidth; + + this->horGap1X = this->usableScreenX; + this->horGap1Y = this->timeLineY + timeLineHeight; + this->horGap1Width = this->usableScreenWidth; + + this->footerX = usableScreenX; + this->footerY = this->usableScreenY + this->usableScreenHeight - footerHeight; + this->footerWidth = this->usableScreenWidth; + + this->horGap2X = this->usableScreenX; + this->horGap2Y = this->footerY - horGap2Height; + this->horGap2Width = this->usableScreenWidth; + + this->channelsTableX = this->usableScreenX; + this->channelsTableY = this->timeLineY + timeLineHeight + horGap1Height; + this->channelsTableHeight = this->maxNumberOfDisplayableEntries * entryHeight; + + this->verGap1X = this->channelsTableX + channelsTableWidth; + this->verGap1Y = this->channelsTableY; + this->verGap1Height = this->channelsTableHeight; + + this->eventsTableX = this->channelsTableX + channelsTableWidth + verGap1Width; + this->eventsTableY = this->channelsTableY; + this->eventsTableWidth = this->usableScreenWidth - this->channelsTableWidth - this->sliderWidth - verGap1Width - verGap2Width; + this->eventsTableHeight = this->channelsTableHeight; + + this->sliderX = this->usableScreenX + this->usableScreenWidth - this->sliderWidth; + this->sliderY = this->eventsTableY; + this->sliderHeight = this->channelsTableHeight; + + this->verGap2X = this->sliderX - verGap2Width; + this->verGap2Y = this->channelsTableY; + this->verGap2Height = this->channelsTableHeight; + + this->channelListStartIndex = 0; + this->startTime = 0; + this->duration = 2 * 60 * 60; + + this->refreshAll = false; + this->currentViewMode = ViewMode_Scroll; + this->currentSwapMode = SwapMode_ByPage; + + this->header = new Header (this->frameBuffer, this->headerX, this->headerY, this->headerWidth); + + this->timeLine = new TimeLine (this->frameBuffer, this->timeLineX, this->timeLineY, this->timeLineWidth, this->eventsTableX, this->eventsTableWidth); + + this->footer = new Footer (this->frameBuffer, this->footerX, this->footerY, this->footerWidth); +} + +void EpgPlus::free () +{ + delete this->header; + delete this->timeLine; + delete this->footer; + int i; + for (i = 0; i < NumberOfFontSettings; ++i) { + delete fonts[i]; + } +} + +int EpgPlus::exec (CChannelList * channelList, int selectedChannelIndex, CBouquetList * bouquetList) +{ + this->channelList = channelList; + this->channelListStartIndex = int (selectedChannelIndex / maxNumberOfDisplayableEntries) * maxNumberOfDisplayableEntries; + this->bouquetList = bouquetList; + + int res = menu_return::RETURN_REPAINT; + + do { + this->refreshAll = false; + this->refreshFooterButtons = false; + time_t currentTime = time (NULL); + tm tmStartTime = *localtime (¤tTime); + + tmStartTime.tm_sec = 0; + tmStartTime.tm_min = int (tmStartTime.tm_min / 15) * 15; + + this->startTime = mktime (&tmStartTime); + this->selectedTime = this->startTime; + this->firstStartTime = this->startTime; + + if (this->selectedChannelEntry != NULL) { + selectedChannelIndex = this->selectedChannelEntry->index; + } + + neutrino_msg_t msg; + neutrino_msg_data_t data; + + this->createChannelEntries (selectedChannelIndex); + + this->header->paint (); + + this->footer->paintButtons (buttonLabels, sizeof (buttonLabels) / sizeof (button_label)); + + this->paint (); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd (g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + bool loop = true; + while (loop) { + g_RCInput->getMsgAbsoluteTimeout (&msg, &data, &timeoutEnd); + + if (msg <= CRCInput::RC_MaxRC) + timeoutEnd = CRCInput::calcTimeoutEnd (g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + if ((msg == CRCInput::RC_page_down) || (msg == CRCInput::RC_yellow)) { + switch (this->currentSwapMode) { + case SwapMode_ByPage: + { + int selectedChannelEntryIndex = this->selectedChannelEntry->index; + selectedChannelEntryIndex += this->maxNumberOfDisplayableEntries; + + if (selectedChannelEntryIndex > this->channelList->getSize () - 1) + selectedChannelEntryIndex = 0; + + this->createChannelEntries (selectedChannelEntryIndex); + + this->paint (); + } + break; + case SwapMode_ByBouquet: + { + unsigned int currentBouquetNumber = bouquetList->getActiveBouquetNumber (); + + ++currentBouquetNumber; + + if (currentBouquetNumber == bouquetList->Bouquets.size ()) + currentBouquetNumber = 0; + + CBouquet *bouquet = bouquetList->Bouquets[currentBouquetNumber]; + + if (bouquet->channelList->getSize () > 0) { + // select first channel of bouquet + + bouquetList->activateBouquet (currentBouquetNumber, false); + + this->channelListStartIndex = (*bouquet->channelList)[0]->number - 1; + this->createChannelEntries (this->channelListStartIndex); + + this->paint (); + } + } + break; + } + } + //else if ( (msg == (neutrino_msg_t)g_settings.key_channelList_pageup) + else if ((msg == CRCInput::RC_page_up) || (msg == CRCInput::RC_green)) { + switch (this->currentSwapMode) { + case SwapMode_ByPage: + { + int selectedChannelEntryIndex = this->selectedChannelEntry->index; + selectedChannelEntryIndex -= this->maxNumberOfDisplayableEntries; + + if (selectedChannelEntryIndex < 0) + selectedChannelEntryIndex = this->channelList->getSize () - 1; + + this->createChannelEntries (selectedChannelEntryIndex); + + this->paint (); + } + break; + case SwapMode_ByBouquet: + { + unsigned int currentBouquetNumber = bouquetList->getActiveBouquetNumber (); + + --currentBouquetNumber; + + if (currentBouquetNumber == unsigned (-1)) + currentBouquetNumber = bouquetList->Bouquets.size () - 1; + + CBouquet *bouquet = bouquetList->Bouquets[currentBouquetNumber]; + + if (bouquet->channelList->getSize () > 0) { + // select first channel of bouquet + + bouquetList->activateBouquet (currentBouquetNumber, false); + + this->channelListStartIndex = (*bouquet->channelList)[0]->number - 1; + this->createChannelEntries (this->channelListStartIndex); + + this->paint (); + } + } + break; + } + } else if (msg == (neutrino_msg_t) CRCInput::RC_red) { + CMenuWidget menuWidgetActions (LOCALE_EPGPLUS_ACTIONS, "features.raw", 400); + if (!g_settings.minimode) + menuWidgetActions.addItem (new CMenuForwarder (LOCALE_EPGPLUS_RECORD, true, NULL, new MenuTargetAddRecordTimer (this), NULL, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED), false); + menuWidgetActions.addItem (new CMenuForwarder (LOCALE_EPGPLUS_REFRESH_EPG, true, NULL, new MenuTargetRefreshEpg (this), NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN), false); + menuWidgetActions.addItem (new CMenuForwarder (LOCALE_EPGPLUS_REMIND, true, NULL, new MenuTargetAddReminder (this), NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW), false); + + menuWidgetActions.exec (NULL, ""); + + this->refreshAll = true; + } else if (msg == (neutrino_msg_t) CRCInput::RC_blue) { + + CMenuWidget menuWidgetOptions (LOCALE_EPGPLUS_OPTIONS, "features.raw", 500); + menuWidgetOptions.addItem (new MenuOptionChooserSwitchSwapMode (this)); + menuWidgetOptions.addItem (new MenuOptionChooserSwitchViewMode (this)); + + int result = menuWidgetOptions.exec (NULL, ""); + if (result == menu_return::RETURN_REPAINT) { + this->refreshAll = true; + } else if (result == menu_return::RETURN_EXIT_ALL) { + this->refreshAll = true; + } + } else if (CRCInput::isNumeric (msg)) { //numeric zap + this->hide (); + this->channelList->numericZap (msg); + + int selectedChannelEntryIndex = this->channelList->getSelectedChannelIndex (); + if (selectedChannelEntryIndex < this->channelList->getSize ()) { + this->hide (); + this->createChannelEntries (selectedChannelEntryIndex); + + this->header->paint (); + this->footer->paintButtons (buttonLabels, sizeof (buttonLabels) / sizeof (button_label)); + this->paint (); + } + + } else if (msg == CRCInput::RC_up) { + int selectedChannelEntryIndex = this->selectedChannelEntry->index; + int prevSelectedChannelEntryIndex = selectedChannelEntryIndex; + + --selectedChannelEntryIndex; + if (selectedChannelEntryIndex < 0) { + selectedChannelEntryIndex = this->channelList->getSize () - 1; + } + + + int oldChannelListStartIndex = this->channelListStartIndex; + + this->channelListStartIndex = (selectedChannelEntryIndex / this->maxNumberOfDisplayableEntries) * this->maxNumberOfDisplayableEntries; + + if (oldChannelListStartIndex != this->channelListStartIndex) { + + this->createChannelEntries (selectedChannelEntryIndex); + + this->paint (); + } else { + this->selectedChannelEntry = this->displayedChannelEntries[selectedChannelEntryIndex - this->channelListStartIndex]; + + this->paintChannelEntry (prevSelectedChannelEntryIndex - this->channelListStartIndex); + this->paintChannelEntry (selectedChannelEntryIndex - this->channelListStartIndex); + } + } else if (msg == CRCInput::RC_down) { + int selectedChannelEntryIndex = this->selectedChannelEntry->index; + int prevSelectedChannelEntryIndex = this->selectedChannelEntry->index; + + selectedChannelEntryIndex = (selectedChannelEntryIndex + 1) % this->channelList->getSize (); + + + int oldChannelListStartIndex = this->channelListStartIndex; + this->channelListStartIndex = (selectedChannelEntryIndex / this->maxNumberOfDisplayableEntries) * this->maxNumberOfDisplayableEntries; + + if (oldChannelListStartIndex != this->channelListStartIndex) { + this->createChannelEntries (selectedChannelEntryIndex); + + this->paint (); + } else { + this->selectedChannelEntry = this->displayedChannelEntries[selectedChannelEntryIndex - this->channelListStartIndex]; + + this->paintChannelEntry (prevSelectedChannelEntryIndex - this->channelListStartIndex); + this->paintChannelEntry (this->selectedChannelEntry->index - this->channelListStartIndex); + } + + } else if ((msg == CRCInput::RC_timeout) || (msg == (neutrino_msg_t) g_settings.key_channelList_cancel)) { + loop = false; + } + + else if (msg == CRCInput::RC_left) { + switch (this->currentViewMode) { + case ViewMode_Stretch: + { + if (this->duration - 30 * 60 > 30 * 60) { + this->duration -= 30 * 60; + this->hide (); + this->refreshAll = true; + } + } + break; + case ViewMode_Scroll: + { + TCChannelEventEntries::const_iterator It = this->getSelectedEvent (); + + if ((It != this->selectedChannelEntry->channelEventEntries.begin ()) + && (It != this->selectedChannelEntry->channelEventEntries.end ()) + ) { + --It; + this->selectedTime = (*It)->channelEvent.startTime + (*It)->channelEvent.duration / 2; + if (this->selectedTime < this->startTime) + this->selectedTime = this->startTime; + + this->selectedChannelEntry->paint (true, this->selectedTime); + } else { + if (this->startTime != this->firstStartTime) { + + if (this->startTime - this->duration > this->firstStartTime) { + this->startTime -= this->duration; + } else { + this->startTime = this->firstStartTime; + } + + this->selectedTime = this->startTime + this->duration - 1; // select last event + this->createChannelEntries (this->selectedChannelEntry->index); + + this->paint (); + } + } + } + break; + } + } else if (msg == CRCInput::RC_right) { + switch (this->currentViewMode) { + case ViewMode_Stretch: + { + if (this->duration + 30 * 60 < 4 * 60 * 60) { + this->duration += 60 * 60; + this->hide (); + this->refreshAll = true; + } + } + break; + + case ViewMode_Scroll: + { + TCChannelEventEntries::const_iterator It = this->getSelectedEvent (); + + if ((It != this->selectedChannelEntry->channelEventEntries.end () - 1) + && (It != this->selectedChannelEntry->channelEventEntries.end ())) { + ++It; + + this->selectedTime = (*It)->channelEvent.startTime + (*It)->channelEvent.duration / 2; + + if (this->selectedTime > this->startTime + time_t (this->duration)) + this->selectedTime = this->startTime + this->duration; + + this->selectedChannelEntry->paint (true, this->selectedTime); + } else { + this->startTime += this->duration; + this->createChannelEntries (this->selectedChannelEntry->index); + + this->selectedTime = this->startTime; + this->createChannelEntries (this->selectedChannelEntry->index); + + this->paint (); + } + } + break; + } + } else if (msg == CRCInput::RC_ok) { + this->channelList->zapTo (this->selectedChannelEntry->index); + } else if (msg == CRCInput::RC_help || msg == CRCInput::RC_info) { + TCChannelEventEntries::const_iterator It = this->getSelectedEvent (); + + if (It != this->selectedChannelEntry->channelEventEntries.end ()) { + + if ((*It)->channelEvent.eventID != 0) { + this->hide (); + + time_t startTime = (*It)->channelEvent.startTime; + res = g_EpgData->show (this->selectedChannelEntry->channel->channel_id, (*It)->channelEvent.eventID, &startTime); + + if (res == menu_return::RETURN_EXIT_ALL) { + loop = false; + } else { + g_RCInput->getMsg (&msg, &data, 0); + + if ((msg != CRCInput::RC_red) && (msg != CRCInput::RC_timeout)) { + // RC_red schlucken + g_RCInput->postMsg (msg, data); + } + + this->header->paint (); + this->footer->paintButtons (buttonLabels, sizeof (buttonLabels) / sizeof (button_label)); + this->paint (); + } + } + } + else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + g_RCInput->postMsg (msg, 0); + res = menu_return::RETURN_EXIT_ALL; + loop = false; + } + } else { + if (CNeutrinoApp::getInstance ()->handleMsg (msg, data) & messages_return::cancel_all) { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + + if (this->refreshAll) + loop = false; + else if (this->refreshFooterButtons) + this->footer->paintButtons (buttonLabels, sizeof (buttonLabels) / sizeof (button_label)); + + } + + this->hide (); + for (TChannelEntries::iterator It = this->displayedChannelEntries.begin (); + It != this->displayedChannelEntries.end (); It++) { + delete *It; + } + this->displayedChannelEntries.clear (); + } + while (this->refreshAll); + + return res; +} + +EpgPlus::TCChannelEventEntries::const_iterator EpgPlus::getSelectedEvent () const +{ + for (TCChannelEventEntries::const_iterator It = this->selectedChannelEntry->channelEventEntries.begin (); + It != this->selectedChannelEntry->channelEventEntries.end (); + ++It) { + if ((*It)->isSelected (this->selectedTime)) { + return It; + } + } + return this->selectedChannelEntry->channelEventEntries.end (); +} + +void EpgPlus::hide () +{ + this->frameBuffer->paintBackgroundBoxRel (this->usableScreenX, this->usableScreenY, this->usableScreenWidth, this->usableScreenHeight); +} + +void EpgPlus::paintChannelEntry (int position) +{ + ChannelEntry *channelEntry = this->displayedChannelEntries[position]; + + bool currentChannelIsSelected = false; + if (this->channelListStartIndex + position == this->selectedChannelEntry->index) { + currentChannelIsSelected = true; + } + channelEntry->paint (currentChannelIsSelected, this->selectedTime); +} + +std::string EpgPlus::getTimeString (const time_t & time, const std::string & format) +{ + char tmpstr[256]; + struct tm *tmStartTime = localtime (&time); + + strftime (tmpstr, sizeof (tmpstr), format.c_str (), tmStartTime); + return tmpstr; +} + +void EpgPlus::paint () +{ + // clear + this->frameBuffer->paintBackgroundBoxRel (this->channelsTableX, this->channelsTableY, this->usableScreenWidth, this->channelsTableHeight); + + // paint the gaps + this->frameBuffer->paintBoxRel (this->horGap1X, this->horGap1Y, this->horGap1Width, horGap1Height, horGap1Color); + this->frameBuffer->paintBoxRel (this->horGap2X, this->horGap2Y, this->horGap2Width, horGap2Height, horGap2Color); + this->frameBuffer->paintBoxRel (this->verGap1X, this->verGap1Y, verGap1Width, this->verGap1Height, verGap1Color); + this->frameBuffer->paintBoxRel (this->verGap2X, this->verGap2Y, verGap2Width, this->verGap2Height, verGap2Color); + + // paint the time line + timeLine->paint (this->startTime, this->duration); + + // paint the channel entries + for (int i = 0; i < (int) this->displayedChannelEntries.size (); ++i) { + this->paintChannelEntry (i); + } + + // paint the time line grid + this->timeLine->paintGrid (); + + // paint slider + this->frameBuffer->paintBoxRel (this->sliderX, this->sliderY, this->sliderWidth, this->sliderHeight, COL_MENUCONTENT_PLUS_0); + + int tmp = ((this->channelList->getSize () - 1) / this->maxNumberOfDisplayableEntries) + 1; + float sliderKnobHeight = (sliderHeight - 4) / tmp; + int sliderKnobPosition = this->selectedChannelEntry == NULL ? 0 : (this->selectedChannelEntry->index / this->maxNumberOfDisplayableEntries); + + this->frameBuffer->paintBoxRel (this->sliderX + 2, this->sliderY + int (sliderKnobPosition * sliderKnobHeight) + , this->sliderWidth - 4, int (sliderKnobHeight) , COL_MENUCONTENT_PLUS_3); +} + +// -- EPG+ Menue Handler Class +// -- to be used for calls from Menue +// -- (2004-03-05 rasc) + +int CEPGplusHandler::exec (CMenuTarget * parent, const std::string & actionKey) +{ + int res = menu_return::RETURN_EXIT_ALL; + EpgPlus *e; + CChannelList *channelList; + + if (parent) + parent->hide (); + + e = new EpgPlus; + + channelList = CNeutrinoApp::getInstance ()->channelList; + e->exec (channelList, channelList->getSelectedChannelIndex (), bouquetList); + delete e; + + return res; +} + +EpgPlus::MenuTargetAddReminder::MenuTargetAddReminder (EpgPlus * epgPlus) { + this->epgPlus = epgPlus; +} + +int EpgPlus::MenuTargetAddReminder::exec (CMenuTarget * parent, const std::string & actionKey) +{ + TCChannelEventEntries::const_iterator It = this->epgPlus->getSelectedEvent (); + + if ((It != this->epgPlus->selectedChannelEntry->channelEventEntries.end ()) + && (!(*It)->channelEvent.description.empty ()) + ) { + if (g_Timerd->isTimerdAvailable ()) { + g_Timerd->addZaptoTimerEvent (this->epgPlus->selectedChannelEntry->channel->channel_id, (*It)->channelEvent.startTime, (*It)->channelEvent.startTime - ANNOUNCETIME, 0, (*It)->channelEvent.eventID, (*It)->channelEvent.startTime, 0); + + ShowMsgUTF (LOCALE_TIMER_EVENTTIMED_TITLE, g_Locale->getText (LOCALE_TIMER_EVENTTIMED_MSG) + , CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); // UTF-8 + } else + printf ("timerd not available\n"); + } + return menu_return::RETURN_EXIT_ALL; +} + +EpgPlus::MenuTargetAddRecordTimer::MenuTargetAddRecordTimer (EpgPlus * epgPlus) { + this->epgPlus = epgPlus; +} + +int EpgPlus::MenuTargetAddRecordTimer::exec (CMenuTarget * parent, const std::string & actionKey) +{ + TCChannelEventEntries::const_iterator It = this->epgPlus->getSelectedEvent (); + + if ((It != this->epgPlus->selectedChannelEntry->channelEventEntries.end ()) + && (!(*It)->channelEvent.description.empty ()) + ) { + if (g_Timerd->isTimerdAvailable ()) { + + g_Timerd->addRecordTimerEvent (this->epgPlus->selectedChannelEntry->channel->channel_id, (*It)->channelEvent.startTime, (*It)->channelEvent.startTime + (*It)->channelEvent.duration, (*It)->channelEvent.eventID, (*It)->channelEvent.startTime, (*It)->channelEvent.startTime - (ANNOUNCETIME + 120) + , TIMERD_APIDS_CONF, true); + ShowMsgUTF (LOCALE_TIMER_EVENTRECORD_TITLE, g_Locale->getText (LOCALE_TIMER_EVENTRECORD_MSG) + , CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); // UTF-8 + } else + printf ("timerd not available\n"); + } + return menu_return::RETURN_EXIT_ALL; +} + +EpgPlus::MenuTargetRefreshEpg::MenuTargetRefreshEpg (EpgPlus * epgPlus) { + this->epgPlus = epgPlus; +} + +int EpgPlus::MenuTargetRefreshEpg::exec (CMenuTarget * parent, const std::string & actionKey) +{ + this->epgPlus->refreshAll = true; + return menu_return::RETURN_EXIT_ALL; +} + +struct CMenuOptionChooser::keyval menuOptionChooserSwitchSwapModes[] = { + {EpgPlus::SwapMode_ByPage, LOCALE_EPGPLUS_BYPAGE_MODE}, + {EpgPlus::SwapMode_ByBouquet, LOCALE_EPGPLUS_BYBOUQUET_MODE} +}; + +EpgPlus::MenuOptionChooserSwitchSwapMode::MenuOptionChooserSwitchSwapMode (EpgPlus * epgPlus) +:CMenuOptionChooser (LOCALE_EPGPLUS_SWAP_MODE, (int *) &epgPlus->currentSwapMode, menuOptionChooserSwitchSwapModes, sizeof (menuOptionChooserSwitchSwapModes) / sizeof (CMenuOptionChooser::keyval) + , true, NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW) { + this->epgPlus = epgPlus; + this->oldSwapMode = epgPlus->currentSwapMode; + this->oldTimingMenuSettings = g_settings.timing[SNeutrinoSettings::TIMING_MENU]; +} + +EpgPlus::MenuOptionChooserSwitchSwapMode::~MenuOptionChooserSwitchSwapMode () +{ + g_settings.timing[SNeutrinoSettings::TIMING_MENU] = this->oldTimingMenuSettings; + + if (this->epgPlus->currentSwapMode != this->oldSwapMode) { + switch (this->epgPlus->currentSwapMode) { + case SwapMode_ByPage: + buttonLabels[1].locale = LOCALE_EPGPLUS_PAGE_DOWN; + buttonLabels[2].locale = LOCALE_EPGPLUS_PAGE_UP; + break; + case SwapMode_ByBouquet: + buttonLabels[1].locale = LOCALE_EPGPLUS_PREV_BOUQUET; + buttonLabels[2].locale = LOCALE_EPGPLUS_NEXT_BOUQUET; + break; + } + this->epgPlus->refreshAll = true; + } +} + +int EpgPlus::MenuOptionChooserSwitchSwapMode::exec (CMenuTarget * parent) +{ + // change time out settings temporary + g_settings.timing[SNeutrinoSettings::TIMING_MENU] = 1; + + CMenuOptionChooser::exec (parent); + + return menu_return::RETURN_REPAINT; +} + +struct CMenuOptionChooser::keyval menuOptionChooserSwitchViewModes[] = { + {EpgPlus::ViewMode_Scroll, LOCALE_EPGPLUS_STRETCH_MODE}, + {EpgPlus::ViewMode_Stretch, LOCALE_EPGPLUS_SCROLL_MODE} +}; + +EpgPlus::MenuOptionChooserSwitchViewMode::MenuOptionChooserSwitchViewMode (EpgPlus * epgPlus) +:CMenuOptionChooser (LOCALE_EPGPLUS_VIEW_MODE, (int *) &epgPlus->currentViewMode, menuOptionChooserSwitchViewModes, sizeof (menuOptionChooserSwitchViewModes) / sizeof (CMenuOptionChooser::keyval) + , true, NULL, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE) { + this->oldTimingMenuSettings = g_settings.timing[SNeutrinoSettings::TIMING_MENU]; +} + +EpgPlus::MenuOptionChooserSwitchViewMode::~MenuOptionChooserSwitchViewMode () +{ + g_settings.timing[SNeutrinoSettings::TIMING_MENU] = this->oldTimingMenuSettings; +} + +int EpgPlus::MenuOptionChooserSwitchViewMode::exec (CMenuTarget * parent) +{ + // change time out settings temporary + g_settings.timing[SNeutrinoSettings::TIMING_MENU] = 1; + + CMenuOptionChooser::exec (parent); + + return menu_return::RETURN_REPAINT; +} diff --git a/src/gui/epgplus.h b/src/gui/epgplus.h new file mode 100644 index 000000000..a4dc32660 --- /dev/null +++ b/src/gui/epgplus.h @@ -0,0 +1,556 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Copyright (C) 2004 Martin Griep 'vivamiga' + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __EPGPLUS_HPP__ +#define __EPGPLUS_HPP__ + +#include +#include +#include +#include + +#include "color.h" +#include "channellist.h" +#include "infoviewer.h" +#include "filebrowser.h" + +#include "widget/menue.h" + +#include + +class CBouquetList; +class ConfigFile; +struct button_label; + +class EpgPlus +{ +//// types, inner classes +public: + enum FontSettingID + { + EPGPlus_header_font = 0, + EPGPlus_timeline_fonttime, + EPGPlus_timeline_fontdate, + EPGPlus_channelentry_font, + EPGPlus_channelevententry_font, + EPGPlus_footer_fontbouquetchannelname, + EPGPlus_footer_fonteventdescription, + EPGPlus_footer_fonteventshortdescription, + EPGPlus_footer_fontbuttons, + NumberOfFontSettings + }; + + enum SizeSettingID + { + EPGPlus_channelentry_width = 0, + EPGPlus_channelentry_separationlineheight, + EPGPlus_slider_width, + EPGPlus_horgap1_height, + EPGPlus_horgap2_height, + EPGPlus_vergap1_width, + EPGPlus_vergap2_width, + NumberOfSizeSettings + }; + + struct FontSetting + { + FontSettingID settingID; + char* style; + int size; + }; + + struct SizeSetting + { + SizeSettingID settingID; + int size; + }; + + + enum TViewMode + { + ViewMode_Stretch, + ViewMode_Scroll, + }; + + enum TSwapMode + { + SwapMode_ByPage, + SwapMode_ByBouquet, + }; + + class Footer; + + class Header + { + //// construction / destruction + public: + Header ( CFrameBuffer* frameBuffer , int x , int y , int width); + + ~Header(); + + //// methods + public: + static void init(); + + void paint(); + + static int getUsedHeight(); + + //// attributes + public: + CFrameBuffer* frameBuffer; + + int x; + int y; + int width; + + static Font* font; + }; + + + class TimeLine + { + //// construction / destruction + public: + TimeLine ( CFrameBuffer* frameBuffer , int x , int y , int width , int startX , int durationX); + + ~TimeLine(); + + //// methods + public: + static void init(); + + void paint ( time_t startTime , int duration); + + void paintMark ( time_t startTime , int duration , int x , int width); + + void paintGrid(); + + void clearMark(); + + static int getUsedHeight(); + + //// attributes + public: + CFrameBuffer* frameBuffer; + + int currentDuration; + + int x; + int y; + int width; + + static Font* fontTime; + static Font* fontDate; + + int startX; + int durationX; + }; + + class ChannelEventEntry + { + //// construction / destruction + public: + ChannelEventEntry + ( const CChannelEvent* channelEvent + , CFrameBuffer* frameBuffer + , TimeLine* timeLine + , Footer* footer + , int x + , int y + , int width + ); + + ~ChannelEventEntry(); + + //// methods + public: + static void init(); + + bool isSelected + ( time_t selectedTime + ) const; + + void paint + ( bool isSelected + , bool toggleColor + ); + + static int getUsedHeight(); + + //// attributes + public: + CChannelEvent channelEvent; + + CFrameBuffer* frameBuffer; + TimeLine* timeLine; + Footer* footer; + + int x; + int y; + int width; + static int separationLineHeight; + + static Font* font; + }; + + typedef std::vector TCChannelEventEntries; + + class ChannelEntry + { + //// construction / destruction + public: + ChannelEntry + ( const CZapitChannel* channel + , int index + , CFrameBuffer* frameBuffer + , Footer* footer + , CBouquetList* bouquetList + , int x + , int y + , int width + ); + + ~ChannelEntry(); + + //// methods + public: + static void init(); + + void paint + ( bool isSelected + , time_t selectedTime + ); + + static int getUsedHeight(); + + //// attributes + public: + const CZapitChannel * channel; + std::string displayName; + int index; + + CFrameBuffer* frameBuffer; + Footer* footer; + CBouquetList* bouquetList; + + int x; + int y; + int width; + static int separationLineHeight; + + static Font* font; + + TCChannelEventEntries channelEventEntries; + }; + + typedef std::vector TChannelEntries; + + class Footer + { + //// construction / destruction + public: + Footer + ( CFrameBuffer* frameBuffer + , int x + , int y + , int width + ); + + ~Footer(); + + //// methods + public: + static void init(); + + void setBouquetChannelName + ( const std::string& newBouquetName + , const std::string& newChannelName + ); + + void paintEventDetails + ( const std::string& description + , const std::string& shortDescription + ); + + void paintButtons + ( button_label* buttonLabels + , int numberOfButtons + ); + + static int getUsedHeight(); + + //// attributes + public: + CFrameBuffer* frameBuffer; + + int x; + int y; + int width; + + static Font* fontBouquetChannelName; + static Font* fontEventDescription; + static Font* fontEventShortDescription; + static Font* fontButtons; + + static int color; + + std::string currentBouquetName; + std::string currentChannelName; + }; + + + class MenuTargetAddReminder : public CMenuTarget + { + public: + MenuTargetAddReminder ( EpgPlus* epgPlus); + + public: + int exec ( CMenuTarget* parent , const std::string& actionKey); + + private: + EpgPlus* epgPlus; + + }; + + class MenuTargetAddRecordTimer : public CMenuTarget + { + public: + MenuTargetAddRecordTimer ( EpgPlus* epgPlus); + + public: + int exec ( CMenuTarget* parent , const std::string& actionKey); + + private: + EpgPlus* epgPlus; + + }; + + class MenuTargetRefreshEpg : public CMenuTarget + { + public: + MenuTargetRefreshEpg ( EpgPlus* epgPlus); + + public: + int exec ( CMenuTarget* parent , const std::string& actionKey); + + private: + EpgPlus* epgPlus; + + }; + + class MenuOptionChooserSwitchSwapMode : public CMenuOptionChooser + { + public: + MenuOptionChooserSwitchSwapMode ( EpgPlus* epgPlus); + + virtual ~MenuOptionChooserSwitchSwapMode(); + + public: + int exec + ( CMenuTarget* parent); + + private: + int oldTimingMenuSettings; + TSwapMode oldSwapMode; + EpgPlus* epgPlus; + }; + + class MenuOptionChooserSwitchViewMode : public CMenuOptionChooser + { + public: + MenuOptionChooserSwitchViewMode ( EpgPlus* epgPlus); + + virtual ~MenuOptionChooserSwitchViewMode(); + + public: + int exec ( CMenuTarget* parent); + + private: + int oldTimingMenuSettings; + }; + + class MenuTargetSettings : public CMenuTarget + { + public: + MenuTargetSettings ( EpgPlus* epgPlus); + + public: + int exec ( CMenuTarget* parent , const std::string& actionKey); + + private: + EpgPlus* epgPlus; + }; + + typedef time_t DurationSetting; + +/* + struct Settings + { + Settings ( bool doInit = true); + + virtual ~Settings(); + + FontSetting* fontSettings; + SizeSetting* sizeSettings; + DurationSetting durationSetting; + }; + typedef std::map Fonts; + typedef std::map Sizes; + static Font * fonts[NumberOfFontSettings]; + static int sizes[NumberOfSizeSettings]; +*/ + + friend class EpgPlus::MenuOptionChooserSwitchSwapMode; + friend class EpgPlus::MenuOptionChooserSwitchViewMode; + friend class EpgPlus::ChannelEntry; + friend class EpgPlus::ChannelEventEntry; + +//// construction / destruction +public: + EpgPlus(); + ~EpgPlus(); + +//// methods +public: + void init(); + void free(); + + int exec ( CChannelList* channelList , int selectedChannelIndex , CBouquetList* bouquetList); + +private: + static std::string getTimeString ( const time_t& time , const std::string& format); + + TCChannelEventEntries::const_iterator getSelectedEvent() const; + + void createChannelEntries ( int selectedChannelEntryIndex); + void paint(); + void paintChannelEntry ( int position); + void hide(); + +//// properties +private: + CFrameBuffer* frameBuffer; + + TChannelEntries displayedChannelEntries; + + Header* header; + TimeLine* timeLine; + + CChannelList* channelList; + CBouquetList* bouquetList; + + Footer* footer; + + ChannelEntry* selectedChannelEntry; + time_t selectedTime; + + int channelListStartIndex; + int maxNumberOfDisplayableEntries; // maximal number of displayable entrys + + time_t startTime; + time_t firstStartTime; + static time_t duration; + + int entryHeight; + + TViewMode currentViewMode; + TSwapMode currentSwapMode; + + int headerX; + int headerY; + int headerWidth; + + int usableScreenWidth; + int usableScreenHeight; + int usableScreenX; + int usableScreenY; + + int timeLineX; + int timeLineY; + int timeLineWidth; + + int channelsTableX; + int channelsTableY; + static int channelsTableWidth; + int channelsTableHeight; + + int eventsTableX; + int eventsTableY; + int eventsTableWidth; + int eventsTableHeight; + + int sliderX; + int sliderY; + static int sliderWidth; + int sliderHeight; + static int sliderBackColor; + static int sliderKnobColor; + + int footerX; + int footerY; + int footerWidth; + + int horGap1X; + int horGap1Y; + int horGap1Width; + int horGap2X; + int horGap2Y; + int horGap2Width; + int verGap1X; + int verGap1Y; + int verGap1Height; + int verGap2X; + int verGap2Y; + int verGap2Height; + + static int horGap1Height; + static int horGap2Height; + static int verGap1Width; + static int verGap2Width; + + static int horGap1Color; + static int horGap2Color; + static int verGap1Color; + static int verGap2Color; + + bool refreshAll; + bool refreshFooterButtons; +}; + +class CEPGplusHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionKey); +}; + +#endif diff --git a/src/gui/epgview.cpp b/src/gui/epgview.cpp new file mode 100644 index 000000000..312729e31 --- /dev/null +++ b/src/gui/epgview.cpp @@ -0,0 +1,1080 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +extern CPictureViewer * g_PicViewer; +#define PIC_W 52 +#define PIC_H 39 +static CScale * timescale; + +#define ICON_LARGE_WIDTH 26 +#define ROUND_RADIUS 9 + +int findItem(std::string strItem, std::vector & vecItems) { + for (std::vector::size_type nCnt = 0; nCnt < vecItems.size(); nCnt++) { + std::string strThisItem = vecItems[nCnt]; + if (strItem == strThisItem) { + return nCnt; + } + } + return -1; +} + +// 21.07.2005 - rainerk +// Merge multiple extended event strings into one description and localize the label +// Examples: +// Actor1-ActorX -> Darsteller 1, 2, 3 +// Year of production -> Produktionsjahr +// Director -> Regisseur +// Guests -> Gäste +void reformatExtendedEvents(std::string strItem, std::string strLabel, bool bUseRange, CEPGData & epgdata) { + std::vector & vecDescriptions = epgdata.itemDescriptions; + std::vector & vecItems = epgdata.items; + // Merge multiple events into 1 (Actor1-) + if (bUseRange) { + bool bHasItems = false; + char index[3]; + // Maximum of 10 items should suffice + for (int nItem = 1; nItem < 11; nItem++) { + sprintf(index, "%d", nItem); + // Look for matching items + int nPos = findItem(std::string(strItem) + std::string(index), vecDescriptions); + if (-1 != nPos) { + std::string item = std::string(vecItems[nPos]); + vecDescriptions.erase(vecDescriptions.begin() + nPos); + vecItems.erase(vecItems.begin() + nPos); + if (false == bHasItems) { + // First item added, so create new label item + vecDescriptions.push_back(strLabel); + vecItems.push_back(item + ", "); + bHasItems = true; + } else { + vecItems.back().append(item).append(", "); + } + } + } + // Remove superfluous ", " + if (bHasItems) { + vecItems.back().resize(vecItems.back().length() - 2); + } + } else { // Single string event (e.g. Director) + // Look for matching items + int nPos = findItem(strItem, vecDescriptions); + if (-1 != nPos) { + vecDescriptions[nPos] = strLabel; + } + } +} + +CEpgData::CEpgData() +{ + bigFonts = false; + frameBuffer = CFrameBuffer::getInstance(); + timescale = new CScale(100, 12, 30, 100, 70, true); +} + +#define MAX_W 540 +#define MAX_H 320 + +void CEpgData::start() +{ + ox = w_max (MAX_W * (bigFonts ? BIG_FONT_FAKTOR : 1), 0); + oy = h_max (MAX_H * (bigFonts ? BIG_FONT_FAKTOR : 1), 0); + sx = (((g_settings.screen_EndX-g_settings.screen_StartX) -ox) / 2) + g_settings.screen_StartX; + + topheight = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_TITLE]->getHeight(); + topboxheight = topheight + 6; + + if(topboxheight < PIC_H) topboxheight = PIC_H; + + botheight = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->getHeight(); + botboxheight = botheight + 6; + medlineheight = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getHeight(); + medlinecount = (oy- botboxheight)/medlineheight; + sb = medlinecount* medlineheight; + + oy = botboxheight+medlinecount*medlineheight; // recalculate //FIXME + sy = (((g_settings.screen_EndY-g_settings.screen_StartY)-(oy- topboxheight) ) / 2) + g_settings.screen_StartY; + toph = topboxheight; +} + +void CEpgData::addTextToArray(const std::string & text) // UTF-8 +{ + //printf("line: >%s<\n", text.c_str() ); + if (text==" ") + { + emptyLineCount ++; + if(emptyLineCount<2) + { + epgText.push_back(text); + } + } + else + { + emptyLineCount = 0; + epgText.push_back(text); + } +} + +void CEpgData::processTextToArray(std::string text) // UTF-8 +{ + std::string aktLine = ""; + std::string aktWord = ""; + int aktWidth = 0; + text += ' '; + char* text_= (char*) text.c_str(); + + while(*text_!=0) + { + if ( (*text_==' ') || (*text_=='\n') || (*text_=='-') || (*text_=='.') ) + { + // Houdini: if there is a newline (especially in the Premiere Portal EPGs) do not forget to add aktWord to aktLine + // after width check, if width check failes do newline, add aktWord to next line + // and "reinsert" i.e. reloop for the \n + if(*text_!='\n') + aktWord += *text_; + + // check the wordwidth - add to this line if size ok + int aktWordWidth = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->getRenderWidth(aktWord, true); + if((aktWordWidth+aktWidth)<(ox- 20- 15)) + {//space ok, add + aktWidth += aktWordWidth; + aktLine += aktWord; + + if(*text_=='\n') + { //enter-handler + addTextToArray( aktLine ); + aktLine = ""; + aktWidth= 0; + } + aktWord = ""; + } + else + {//new line needed + addTextToArray( aktLine ); + aktLine = aktWord; + aktWidth = aktWordWidth; + aktWord = ""; + // Houdini: in this case where we skipped \n and space is too low, exec newline and rescan \n + // otherwise next word comes direct after aktLine + if(*text_=='\n') + continue; + } + } + else + { + aktWord += *text_; + } + text_++; + } + //add the rest + addTextToArray( aktLine + aktWord ); +} + +void CEpgData::showText( int startPos, int ypos ) +{ + // recalculate + medlineheight = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getHeight(); + medlinecount=(oy- botboxheight)/medlineheight; + + int textCount = epgText.size(); + int y=ypos; + + frameBuffer->paintBoxRel(sx, y, ox- 15, sb, COL_MENUCONTENT_PLUS_0); + + for(int i=startPos; iRenderString(sx+10, y+medlineheight, ox- 15- 15, epgText[i], COL_MENUCONTENT, 0, true); // UTF-8 + else + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->RenderString(sx+10, y+medlineheight, ox- 15- 15, epgText[i], COL_MENUCONTENT, 0, true); // UTF-8 + } + + frameBuffer->paintBoxRel(sx+ ox- 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((textCount- 1)/ medlinecount)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (startPos+ 1)/ medlinecount; + + frameBuffer->paintBoxRel(sx+ ox- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); +} + +#define GENRE_MOVIE_COUNT 9 +const neutrino_locale_t genre_movie[GENRE_MOVIE_COUNT] = +{ + LOCALE_GENRE_MOVIE_0, + LOCALE_GENRE_MOVIE_1, + LOCALE_GENRE_MOVIE_2, + LOCALE_GENRE_MOVIE_3, + LOCALE_GENRE_MOVIE_4, + LOCALE_GENRE_MOVIE_5, + LOCALE_GENRE_MOVIE_6, + LOCALE_GENRE_MOVIE_7, + LOCALE_GENRE_MOVIE_8 +}; +#define GENRE_NEWS_COUNT 5 +const neutrino_locale_t genre_news[GENRE_NEWS_COUNT] = +{ + LOCALE_GENRE_NEWS_0, + LOCALE_GENRE_NEWS_1, + LOCALE_GENRE_NEWS_2, + LOCALE_GENRE_NEWS_3, + LOCALE_GENRE_NEWS_4 +}; +#define GENRE_SHOW_COUNT 4 +const neutrino_locale_t genre_show[GENRE_SHOW_COUNT] = +{ + LOCALE_GENRE_SHOW_0, + LOCALE_GENRE_SHOW_1, + LOCALE_GENRE_SHOW_2, + LOCALE_GENRE_SHOW_3 +}; +#define GENRE_SPORTS_COUNT 12 +const neutrino_locale_t genre_sports[GENRE_SPORTS_COUNT] = +{ + LOCALE_GENRE_SPORTS_0, + LOCALE_GENRE_SPORTS_1, + LOCALE_GENRE_SPORTS_2, + LOCALE_GENRE_SPORTS_3, + LOCALE_GENRE_SPORTS_4, + LOCALE_GENRE_SPORTS_5, + LOCALE_GENRE_SPORTS_6, + LOCALE_GENRE_SPORTS_7, + LOCALE_GENRE_SPORTS_8, + LOCALE_GENRE_SPORTS_9, + LOCALE_GENRE_SPORTS_10, + LOCALE_GENRE_SPORTS_11 +}; +#define GENRE_CHILDRENS_PROGRAMMES_COUNT 6 +const neutrino_locale_t genre_childrens_programmes[GENRE_CHILDRENS_PROGRAMMES_COUNT] = +{ + LOCALE_GENRE_CHILDRENS_PROGRAMMES_0, + LOCALE_GENRE_CHILDRENS_PROGRAMMES_1, + LOCALE_GENRE_CHILDRENS_PROGRAMMES_2, + LOCALE_GENRE_CHILDRENS_PROGRAMMES_3, + LOCALE_GENRE_CHILDRENS_PROGRAMMES_4, + LOCALE_GENRE_CHILDRENS_PROGRAMMES_5 +}; +#define GENRE_MUSIC_DANCE_COUNT 7 +const neutrino_locale_t genre_music_dance[GENRE_MUSIC_DANCE_COUNT] = +{ + LOCALE_GENRE_MUSIC_DANCE_0, + LOCALE_GENRE_MUSIC_DANCE_1, + LOCALE_GENRE_MUSIC_DANCE_2, + LOCALE_GENRE_MUSIC_DANCE_3, + LOCALE_GENRE_MUSIC_DANCE_4, + LOCALE_GENRE_MUSIC_DANCE_5, + LOCALE_GENRE_MUSIC_DANCE_6 +}; +#define GENRE_ARTS_COUNT 12 +const neutrino_locale_t genre_arts_dance[GENRE_ARTS_COUNT] = +{ + LOCALE_GENRE_ARTS_0, + LOCALE_GENRE_ARTS_1, + LOCALE_GENRE_ARTS_2, + LOCALE_GENRE_ARTS_3, + LOCALE_GENRE_ARTS_4, + LOCALE_GENRE_ARTS_5, + LOCALE_GENRE_ARTS_6, + LOCALE_GENRE_ARTS_7, + LOCALE_GENRE_ARTS_8, + LOCALE_GENRE_ARTS_9, + LOCALE_GENRE_ARTS_10, + LOCALE_GENRE_ARTS_11 +}; +#define GENRE_SOCIAL_POLITICAL_COUNT 4 +const neutrino_locale_t genre_social_political[GENRE_SOCIAL_POLITICAL_COUNT] = +{ + LOCALE_GENRE_SOCIAL_POLITICAL_0, + LOCALE_GENRE_SOCIAL_POLITICAL_1, + LOCALE_GENRE_SOCIAL_POLITICAL_2, + LOCALE_GENRE_SOCIAL_POLITICAL_3 +}; +#define GENRE_DOCUS_MAGAZINES_COUNT 8 +const neutrino_locale_t genre_docus_magazines[GENRE_DOCUS_MAGAZINES_COUNT] = +{ + LOCALE_GENRE_DOCUS_MAGAZINES_0, + LOCALE_GENRE_DOCUS_MAGAZINES_1, + LOCALE_GENRE_DOCUS_MAGAZINES_2, + LOCALE_GENRE_DOCUS_MAGAZINES_3, + LOCALE_GENRE_DOCUS_MAGAZINES_4, + LOCALE_GENRE_DOCUS_MAGAZINES_5, + LOCALE_GENRE_DOCUS_MAGAZINES_6, + LOCALE_GENRE_DOCUS_MAGAZINES_7 +}; +#define GENRE_TRAVEL_HOBBIES_COUNT 8 +const neutrino_locale_t genre_travel_hobbies[GENRE_TRAVEL_HOBBIES_COUNT] = +{ + LOCALE_GENRE_TRAVEL_HOBBIES_0, + LOCALE_GENRE_TRAVEL_HOBBIES_1, + LOCALE_GENRE_TRAVEL_HOBBIES_2, + LOCALE_GENRE_TRAVEL_HOBBIES_3, + LOCALE_GENRE_TRAVEL_HOBBIES_4, + LOCALE_GENRE_TRAVEL_HOBBIES_5, + LOCALE_GENRE_TRAVEL_HOBBIES_6, + LOCALE_GENRE_TRAVEL_HOBBIES_7 +}; +const unsigned char genre_sub_classes[10] = +{ + GENRE_MOVIE_COUNT, + GENRE_NEWS_COUNT, + GENRE_SHOW_COUNT, + GENRE_SPORTS_COUNT, + GENRE_CHILDRENS_PROGRAMMES_COUNT, + GENRE_MUSIC_DANCE_COUNT, + GENRE_ARTS_COUNT, + GENRE_SOCIAL_POLITICAL_COUNT, + GENRE_DOCUS_MAGAZINES_COUNT, + GENRE_TRAVEL_HOBBIES_COUNT +}; +const neutrino_locale_t * genre_sub_classes_list[10] = +{ + genre_movie, + genre_news, + genre_show, + genre_sports, + genre_childrens_programmes, + genre_music_dance, + genre_arts_dance, + genre_social_political, + genre_docus_magazines, + genre_travel_hobbies +}; + +bool CEpgData::hasFollowScreenings(const t_channel_id channel_id, const std::string & title) { + time_t curtime = time(NULL); + + for (CChannelEventList::iterator e = evtlist.begin(); e != evtlist.end(); ++e ) + { + if (e->startTime > curtime && e->eventID && e->description == title) + return true; + } + return false; +} + +const char * GetGenre(const unsigned char contentClassification) // UTF-8 +{ + neutrino_locale_t res; + + //unsigned char i = (contentClassification & 0x0F0); + int i = (contentClassification & 0xF0); +//printf("GetGenre: contentClassification %X i %X bool %s\n", contentClassification, i, i < 176 ? "yes" : "no"); fflush(stdout); + if ((i >= 0x010) && (i < 0x0B0)) + { + i >>= 4; + i--; +//printf("GetGenre: i %d content %d\n", i, contentClassification & 0x0F); fflush(stdout); + res = genre_sub_classes_list[i][((contentClassification & 0x0F) < genre_sub_classes[i]) ? (contentClassification & 0x0F) : 0]; + } + else + res = LOCALE_GENRE_UNKNOWN; + return g_Locale->getText(res); +} + +static bool sortByDateTime (const CChannelEvent& a, const CChannelEvent& b) +{ + return a.startTime< b.startTime; +} + +extern char recDir[255]; +void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = ""); +bool sectionsd_getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags); + +void CEpgData::showHead(const t_channel_id channel_id) +{ + int pos; + std::string text1 = epgData.title; + std::string text2 = ""; + if (g_Font[SNeutrinoSettings::FONT_TYPE_EPG_TITLE]->getRenderWidth(text1, true) > ox - PIC_W - 5) + { + do + { + pos = text1.find_last_of("[ .]+"); + if ( pos!=-1 ) + text1 = text1.substr( 0, pos ); + } while ( ( pos != -1 ) && (g_Font[SNeutrinoSettings::FONT_TYPE_EPG_TITLE]->getRenderWidth(text1, true) > 520 - PIC_W - 5)); + text2 = epgData.title.substr(text1.length()+ 1, uint(-1) ); + } + int oldtoph= toph; + + toph = text2 != "" ? 2* topboxheight : topboxheight; + + if (oldtoph > toph) + frameBuffer->paintBackgroundBox (sx, sy- oldtoph- 1, sx+ ox, sy /*- toph*/); + + //show the epg title + frameBuffer->paintBoxRel(sx, sy- toph, ox, toph, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + bool logo_ok = false; + + logo_ok = g_PicViewer->DisplayLogo(channel_id, sx+10, sy- toph+ (toph-PIC_H)/2/*5*/, PIC_W, PIC_H); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_TITLE]->RenderString(sx+15 +(logo_ok? PIC_W+10: 0), sy- toph+ topheight+ 3, ox-15- (logo_ok ? PIC_W+5: 0), text1, COL_MENUHEAD, 0, true); + if (!(text2.empty())) + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_TITLE]->RenderString(sx+15+(logo_ok? PIC_W+10: 0), sy- toph+ 2* topheight+ 3, ox-15 - (logo_ok ? PIC_W+5: 0), text2, COL_MENUHEAD, 0, true); + +} + +int CEpgData::show(const t_channel_id channel_id, unsigned long long a_id, time_t* a_startzeit, bool doLoop ) +{ + int res = menu_return::RETURN_REPAINT; + static unsigned long long id; + static time_t startzeit; + + if(a_startzeit) + startzeit=*a_startzeit; + id=a_id; + + int height; + height = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->getHeight(); + if (doLoop) + { + //start(); + frameBuffer->paintBoxRel(g_settings.screen_StartX, g_settings.screen_StartY, 50, height+5, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(g_settings.screen_StartX+10, g_settings.screen_StartY+height, 40, "-@-", COL_INFOBAR); + if(!bigFonts && g_settings.bigFonts) { + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getSize() * BIG_FONT_FAKTOR)); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->getSize() * BIG_FONT_FAKTOR)); + } + bigFonts = g_settings.bigFonts; + start(); + } + + GetEPGData(channel_id, id, &startzeit ); + if (doLoop) + { + //evtlist = g_Sectionsd->getEventsServiceKey(channel_id&0xFFFFFFFFFFFFULL); + evtlist.clear(); + sectionsd_getEventsServiceKey(channel_id&0xFFFFFFFFFFFFULL, evtlist); + // Houdini added for Private Premiere EPG start sorted by start date/time 2005-08-15 + sort(evtlist.begin(),evtlist.end(),sortByDateTime); + frameBuffer->paintBackgroundBoxRel(g_settings.screen_StartX, g_settings.screen_StartY, 50, height+5); + } + + if (epgData.title.empty()) /* no epg info found */ + { + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_EPGVIEWER_NOTFOUND)); // UTF-8 + hide(); + return res; + } + + // 21.07.2005 - rainerk + // Only show info1 if it's not included in info2! + std::string strEpisode = ""; // Episode title in case info1 gets stripped + if (!epgData.info1.empty()) { + bool bHide = false; + if (false == epgData.info2.empty()) { + // Look for the first . in info1, usually this is the title of an episode. + std::string::size_type nPosDot = epgData.info1.find('.'); + if (std::string::npos != nPosDot) { + nPosDot += 2; // Skip dot and first blank +/* Houdini: changed for safty reason (crashes with some events at WDR regional) + if (nPosDot < epgData.info2.length()) { // Make sure we don't overrun the buffer +*/ + if (nPosDot < epgData.info2.length() && nPosDot < epgData.info1.length()) { // Make sure we don't overrun the buffer + + // Check if the stuff after the dot equals the beginning of info2 + if (0 == epgData.info2.find(epgData.info1.substr(nPosDot, epgData.info1.length() - nPosDot))) { + strEpisode = epgData.info1.substr(0, nPosDot) + "\n"; + bHide = true; + } + } + } + // Compare strings normally if not positively found to be equal before + if (false == bHide && false == (std::string::npos == epgData.info2.find(epgData.info1))) { + bHide = true; + } + } + if (false == bHide) { + processTextToArray(epgData.info1); + } + } + + info1_lines = epgText.size(); + + //scan epg-data - sort to list + if ((epgData.info2.empty()) && (info1_lines == 0)) + processTextToArray(g_Locale->getText(LOCALE_EPGVIEWER_NODETAILED)); // UTF-8 + else + processTextToArray(strEpisode + epgData.info2); + + // 21.07.2005 - rainerk + // Show extended information + if(0 != epgData.itemDescriptions.size() && 0 != epgData.items.size()) { + char line[256]; + std::vector::iterator description; + std::vector::iterator item; + processTextToArray(""); // Add a blank line +//printf("show:: items %d descriptions %d\n", epgData.items.size(), epgData.itemDescriptions.size()); + for (description = epgData.itemDescriptions.begin(), item = epgData.items.begin(); description != epgData.itemDescriptions.end(), item != epgData.items.end(); ++description, ++item) { +//printf("item:: %s: %s\n", (*(description)).c_str(), (*(item)).c_str());fflush(stdout); + sprintf(line, "%s: %s", (*(description)).c_str(), (*(item)).c_str()); + processTextToArray(line); +//printf("after processTextToArray\n"); + } + } +//printf("after epgData.itemDescriptions.size\n"); + + if (epgData.fsk > 0) + { + char _tfsk[11]; + sprintf (_tfsk, "FSK: ab %d", epgData.fsk ); + processTextToArray( _tfsk ); // UTF-8 + } + + if (epgData.contentClassification.length()> 0) + processTextToArray(GetGenre(epgData.contentClassification[0])); // UTF-8 +// processTextToArray( epgData.userClassification.c_str() ); + + + // -- display more screenings on the same channel + // -- 2002-05-03 rasc + if (hasFollowScreenings(channel_id, epgData.title)) { + processTextToArray(""); // UTF-8 + processTextToArray(std::string(g_Locale->getText(LOCALE_EPGVIEWER_MORE_SCREENINGS)) + ':'); // UTF-8 + FollowScreenings(channel_id, epgData.title); + } + + showHead(channel_id); + + //show date-time.... + frameBuffer->paintBoxRel(sx, sy+oy-botboxheight, ox, botboxheight, COL_MENUHEAD_PLUS_0); + std::string fromto; + int widthl,widthr; + fromto = epg_start; + fromto += " - "; + fromto += epg_end; + + widthl = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->getRenderWidth(fromto); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+40, sy+oy-3, widthl, fromto, COL_MENUHEAD); + widthr = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->getRenderWidth(epg_date); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+ox-40-widthr, sy+oy-3, widthr, epg_date, COL_MENUHEAD); + + int showPos = 0; + textCount = epgText.size(); + int textypos = sy; + showText(showPos, textypos); + + // show Timer Event Buttons + showTimerEventBar (true); + + //show Content&Component for Dolby & 16:9 + CSectionsdClient::ComponentTagList tags; + //if ( g_Sectionsd->getComponentTagsUniqueKey( epgData.eventID, tags ) ) + if ( sectionsd_getComponentTagsUniqueKey( epgData.eventID, tags ) ) + { + for (unsigned int i=0; i< tags.size(); i++) + { + if( tags[i].streamContent == 1 && (tags[i].componentType == 2 || tags[i].componentType == 3) ) + { + frameBuffer->paintIcon("16_9.raw" ,ox+sx-(ICON_LARGE_WIDTH+2)-(ICON_LARGE_WIDTH+2),sy + oy+5 ); + } + else if( tags[i].streamContent == 2 && tags[i].componentType == 5 ) + { + frameBuffer->paintIcon("dd.raw", ox+sx-(ICON_LARGE_WIDTH+2), sy + oy+5); + } + } + } + + //show progressbar + if ( epg_done!= -1 ) + { + int pbx = sx + 10 + widthl + 10 + ((ox-104-widthr-widthl-10-10-20)>>1); + timescale->reset(); + timescale->paint(pbx+2, sy+oy-height+2, epg_done); + } + + GetPrevNextEPGData( epgData.eventID, &epgData.epg_times.startzeit ); + if (prev_id != 0) + { + frameBuffer->paintBoxRel(sx+ 5, sy+ oy- botboxheight+ 4, botboxheight- 8, botboxheight- 8, COL_MENUCONTENT_PLUS_3); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+ 10, sy+ oy- 3, widthr, "<", COL_MENUCONTENT + 3); + } + + if (next_id != 0) + { + frameBuffer->paintBoxRel(sx+ ox- botboxheight+ 8- 5, sy+ oy- botboxheight+ 4, botboxheight- 8, botboxheight- 8, COL_MENUCONTENT_PLUS_3); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+ ox- botboxheight+ 8, sy+ oy- 3, widthr, ">", COL_MENUCONTENT + 3); + } + + if ( doLoop ) + { + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int scrollCount; + + bool loop = true; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + while(loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + scrollCount = medlinecount; + + switch ( msg ) + { + case NeutrinoMessages::EVT_TIMER: + if (data == g_InfoViewer->lcdUpdateTimer) { + GetEPGData(channel_id, id, &startzeit ); + if ( epg_done!= -1 ) { + int pbx = sx + 10 + widthl + 10 + ((ox-104-widthr-widthl-10-10-20)>>1); + timescale->reset(); + timescale->paint(pbx+2, sy+oy-height+2, epg_done); + } + } + CNeutrinoApp::getInstance()->handleMsg(msg, data); + break; + case NeutrinoMessages::EVT_CURRENTNEXT_EPG: + if (/*!id && */ ((*(t_channel_id *) data) == (channel_id & 0xFFFFFFFFFFFFULL))) { + show(channel_id); + showPos=0; + } + CNeutrinoApp::getInstance()->handleMsg(msg, data); + break; + case CRCInput::RC_left: + if (prev_id != 0) + { + frameBuffer->paintBoxRel(sx+ 5, sy+ oy- botboxheight+ 4, botboxheight- 8, botboxheight- 8, COL_MENUCONTENT_PLUS_1); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+ 10, sy+ oy- 3, widthr, "<", COL_MENUCONTENT + 1); + + show(channel_id, prev_id, &prev_zeit, false); + showPos=0; + } + break; + + case CRCInput::RC_right: + if (next_id != 0) + { + frameBuffer->paintBoxRel(sx+ ox- botboxheight+ 8- 5, sy+ oy- botboxheight+ 4, botboxheight- 8, botboxheight- 8, COL_MENUCONTENT_PLUS_1); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_DATE]->RenderString(sx+ ox- botboxheight+ 8, sy+ oy- 3, widthr, ">", COL_MENUCONTENT + 1); + + show(channel_id, next_id, &next_zeit, false); + showPos=0; + } + break; + + case CRCInput::RC_down: + if(showPos+scrollCountisTimerdAvailable()) + { + bool doRecord = true; + //char *recDir = g_settings.network_nfs_recordingdir; + strcpy(recDir, g_settings.network_nfs_recordingdir); + if (g_settings.recording_choose_direct_rec_dir == 2) { + CFileBrowser b; + b.Dir_Mode=true; + hide(); + if (b.exec(g_settings.network_nfs_recordingdir)) { + strcpy(recDir, b.getSelectedFile()->Name.c_str()); + } else + doRecord = false; + show(channel_id,epgData.eventID,&epgData.epg_times.startzeit,false); + } + else if (g_settings.recording_choose_direct_rec_dir == 1) + { + int id = -1; + CMountChooser recDirs(LOCALE_TIMERLIST_RECORDING_DIR,NEUTRINO_ICON_SETTINGS,&id,NULL,g_settings.network_nfs_recordingdir); + if (recDirs.hasItem()) + { + hide(); + recDirs.exec(NULL,""); + show(channel_id,epgData.eventID,&epgData.epg_times.startzeit,false); + } else + { + printf("no network devices available\n"); + } + if (id != -1) + strcpy(recDir, g_settings.network_nfs_local_dir[id]); + //recDir = g_settings.network_nfs_local_dir[id]; + //else + //recDir = NULL; + } + //if (recDir != NULL) + if (doRecord) + { + if (g_Timerd->addRecordTimerEvent(channel_id, + epgData.epg_times.startzeit, + epgData.epg_times.startzeit + epgData.epg_times.dauer, + epgData.eventID, epgData.epg_times.startzeit, + epgData.epg_times.startzeit - (ANNOUNCETIME + 120 ), + TIMERD_APIDS_CONF, true, recDir,false) == -1) + { + if(askUserOnTimerConflict(epgData.epg_times.startzeit - (ANNOUNCETIME + 120), + epgData.epg_times.startzeit + epgData.epg_times.dauer)) + { + g_Timerd->addRecordTimerEvent(channel_id, + epgData.epg_times.startzeit, + epgData.epg_times.startzeit + epgData.epg_times.dauer, + epgData.eventID, epgData.epg_times.startzeit, + epgData.epg_times.startzeit - (ANNOUNCETIME + 120 ), + TIMERD_APIDS_CONF, true, recDir,true); + ShowLocalizedMessage(LOCALE_TIMER_EVENTRECORD_TITLE, LOCALE_TIMER_EVENTRECORD_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + } else { + ShowLocalizedMessage(LOCALE_TIMER_EVENTRECORD_TITLE, LOCALE_TIMER_EVENTRECORD_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + } + } + else + printf("timerd not available\n"); + } + break; + + // 31.05.2002 dirch zapto timer + case CRCInput::RC_yellow: + { + //CTimerdClient timerdclient; + if(g_Timerd->isTimerdAvailable()) + { + g_Timerd->addZaptoTimerEvent(channel_id, + epgData.epg_times.startzeit, + epgData.epg_times.startzeit - ANNOUNCETIME, 0, + epgData.eventID, epgData.epg_times.startzeit, 0); + ShowLocalizedMessage(LOCALE_TIMER_EVENTTIMED_TITLE, LOCALE_TIMER_EVENTTIMED_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + else + printf("timerd not available\n"); + break; + } + + case CRCInput::RC_info: + case CRCInput::RC_help: + bigFonts = bigFonts ? false : true; + frameBuffer->paintBackgroundBox (sx, sy- toph, sx+ ox, sy+ oy); + showTimerEventBar (false); + start(); +//printf("bigFonts %d\n", bigFonts); + if(bigFonts) + { + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getSize() * BIG_FONT_FAKTOR)); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->getSize() * BIG_FONT_FAKTOR)); + }else + { + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getSize() / BIG_FONT_FAKTOR)); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->getSize() / BIG_FONT_FAKTOR)); + } + g_settings.bigFonts = bigFonts; + show(channel_id, id, &startzeit, false); + showPos=0; + break; + + case CRCInput::RC_ok: + case CRCInput::RC_timeout: + loop = false; + break; + case CRCInput::RC_favorites: + case CRCInput::RC_sat: + g_RCInput->postMsg (msg, 0); + loop = false; + break; + + default: + // konfigurierbare Keys handlen... + if (msg == (neutrino_msg_t)g_settings.key_channelList_cancel) + loop = false; + else + { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + } + hide(); + } + return res; +} + +void CEpgData::hide() +{ + // 2004-09-10 rasc (bugfix, scale large font settings back to normal) + if (bigFonts) { + bigFonts = false; + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]->getSize() / BIG_FONT_FAKTOR)); + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->setSize((int)(g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2]->getSize() / BIG_FONT_FAKTOR)); + } + + frameBuffer->paintBackgroundBox (sx, sy- toph, sx+ ox, sy+ oy); + showTimerEventBar (false); +} + +bool sectionsd_getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata); +bool sectionsd_getActualEPGServiceKey(const t_channel_id uniqueServiceKey, CEPGData * epgdata); + +void CEpgData::GetEPGData(const t_channel_id channel_id, unsigned long long id, time_t* startzeit ) +{ + epgText.clear(); + emptyLineCount = 0; + epgData.title.clear(); + + bool res; + if ( id!= 0 ) + //res = g_Sectionsd->getEPGid( id, *startzeit, &epgData ); + res = sectionsd_getEPGid(id, *startzeit, &epgData); + else + //res = g_Sectionsd->getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData ); + res = sectionsd_getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData ); + + if ( res ) + { + // If we have items, merge and localize them (e.g. actor1, actor2, ... -> Actors) + if (false == epgData.itemDescriptions.empty()) { + reformatExtendedEvents("Year of production", g_Locale->getText(LOCALE_EPGEXTENDED_YEAR_OF_PRODUCTION), false, epgData); + reformatExtendedEvents("Original title", g_Locale->getText(LOCALE_EPGEXTENDED_ORIGINAL_TITLE), false, epgData); + reformatExtendedEvents("Director", g_Locale->getText(LOCALE_EPGEXTENDED_DIRECTOR), false, epgData); + reformatExtendedEvents("Actor", g_Locale->getText(LOCALE_EPGEXTENDED_ACTORS), true, epgData); + reformatExtendedEvents("Guests", g_Locale->getText(LOCALE_EPGEXTENDED_GUESTS), false, epgData); + reformatExtendedEvents("Presenter", g_Locale->getText(LOCALE_EPGEXTENDED_PRESENTER), false, epgData); + } + + struct tm *pStartZeit = localtime(&(epgData.epg_times).startzeit); + char temp[11]; + strftime( temp, sizeof(temp), "%d.%m.%Y", pStartZeit); + epg_date= temp; + strftime( temp, sizeof(temp), "%H:%M", pStartZeit); + epg_start= temp; + + long int uiEndTime((epgData.epg_times).startzeit+ (epgData.epg_times).dauer); + struct tm *pEndeZeit = localtime((time_t*)&uiEndTime); + strftime( temp, sizeof(temp), "%H:%M", pEndeZeit); + epg_end= temp; + + epg_done= -1; + if (( time(NULL)- (epgData.epg_times).startzeit )>= 0 ) + { + unsigned nProcentagePassed=(unsigned)((float)(time(NULL)-(epgData.epg_times).startzeit)/(float)(epgData.epg_times).dauer*100.); + if (nProcentagePassed<= 100) + epg_done= nProcentagePassed; + } + } +//printf("GetEPGData:: items %d descriptions %d\n", epgData.items.size(), epgData.itemDescriptions.size()); +} + +void CEpgData::GetPrevNextEPGData( unsigned long long id, time_t* startzeit ) +{ + prev_id= 0; + next_id= 0; + unsigned int i; + + for ( i= 0; i< evtlist.size(); i++ ) + { + //printf("%d %llx/%llx - %x %x\n", i, evtlist[i].eventID, id, evtlist[i].startTime, *startzeit); + if ( ( evtlist[i].eventID == id ) && ( evtlist[i].startTime == *startzeit ) ) + { + if ( i > 0 ) + { + prev_id= evtlist[i- 1].eventID; + prev_zeit= evtlist[i- 1].startTime; + } + if ( i < ( evtlist.size()- 1 ) ) + { + next_id= evtlist[i+ 1].eventID; + next_zeit= evtlist[i+ 1].startTime; + } + break; + } + } + /* Houdini: dirty RTL double event workaround, if prev/next event has same starttime as actual event skip it */ + if ((prev_zeit == *startzeit) && ((i-1) > 0)) + { + prev_id = evtlist[i- 2].eventID; + prev_zeit = evtlist[i- 2].startTime; + } + if ((next_zeit == *startzeit) && ((i+1) < (evtlist.size()- 1))) + { + next_id = evtlist[i+ 2].eventID; + next_zeit = evtlist[i+ 2].startTime; + } + +} + +// +// -- get following screenings of this program title +// -- yek! a better class design would be more helpfull +// -- BAD THING: Cross channel screenings will not be shown +// -- $$$TODO +// -- 2002-05-03 rasc +// + +int CEpgData::FollowScreenings (const t_channel_id channel_id, const std::string & title) + +{ + CChannelEventList::iterator e; + time_t curtime; + struct tm *tmStartZeit; + std::string screening_dates,screening_nodual; + int count; + char tmpstr[256]; + + + count = 0; + screening_dates = screening_nodual = ""; + // alredy read: evtlist = g_Sectionsd->getEventsServiceKey( channel_id&0xFFFFFFFFFFFFULL ); + curtime = time(NULL); + + for ( e= evtlist.begin(); e != evtlist.end(); ++e ) + { + if (e->startTime <= curtime) continue; + if (! e->eventID) continue; + if (e->description == title) { + count++; + tmStartZeit = localtime(&(e->startTime)); + + screening_dates = " "; + + screening_dates += g_Locale->getText(CLocaleManager::getWeekday(tmStartZeit)); + screening_dates += '.'; + + strftime(tmpstr, sizeof(tmpstr), " %d.", tmStartZeit ); + screening_dates += tmpstr; + + screening_dates += g_Locale->getText(CLocaleManager::getMonth(tmStartZeit)); + + strftime(tmpstr, sizeof(tmpstr), ". %H:%M ", tmStartZeit ); + screening_dates += tmpstr; + if (screening_dates != screening_nodual){ + screening_nodual=screening_dates; + processTextToArray(screening_dates ); // UTF-8 + } + } + } + if (count == 0) + processTextToArray("---\n"); // UTF-8 + return count; +} + + +// +// -- Just display or hide TimerEventbar +// -- 2002-05-13 rasc +// + +void CEpgData::showTimerEventBar (bool show) + +{ + int x,y,w,h; + int cellwidth; // 4 cells + int h_offset, pos; + + w = ox; + h = 30; + x = sx; + y = sy + oy; + h_offset = 5; + cellwidth = w / 4; + + + frameBuffer->paintBackgroundBoxRel(x,y,w,h); + // hide only? + if (! show) return; + + frameBuffer->paintBoxRel(x,y,w,h, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2);//round + + // Button: Timer Record & Channelswitch + if (g_settings.recording_type != CNeutrinoApp::RECORDING_OFF) + { + pos = 0; + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_RED, x+8+cellwidth*pos, y+h_offset ); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+29+cellwidth*pos, y+h-h_offset, w-30, g_Locale->getText(LOCALE_TIMERBAR_RECORDEVENT), COL_INFOBAR, 0, true); // UTF-8 + } + // Button: Timer Channelswitch + pos = 2; + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_YELLOW, x+8+cellwidth*pos, y+h_offset ); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+29+cellwidth*pos, y+h-h_offset, w-30, g_Locale->getText(LOCALE_TIMERBAR_CHANNELSWITCH), COL_INFOBAR, 0, true); // UTF-8 +} + +// -- EPG Data Viewer Menu Handler Class +// -- to be used for calls from Menue +// -- (2004-03-06 rasc) + +int CEPGDataHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + CChannelList *channelList; + CEpgData *e; + + + if (parent) { + parent->hide(); + } + + e = new CEpgData; + + channelList = CNeutrinoApp::getInstance()->channelList; + e->show( channelList->getActiveChannel_ChannelID() ); + delete e; + + return res; +} diff --git a/src/gui/epgview.h b/src/gui/epgview.h new file mode 100644 index 000000000..f536429cd --- /dev/null +++ b/src/gui/epgview.h @@ -0,0 +1,108 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __epgview__ +#define __epgview__ + +#include +#include +#include +#include + +#include +#include "widget/menue.h" + +#include + +#include +#include + + +#define BIG_FONT_FAKTOR 1.5 + +class CEpgData +{ + private: + CFrameBuffer *frameBuffer; + CChannelEventList evtlist; + CEPGData epgData; + + std::string epg_date; + std::string epg_start; + std::string epg_end; + int epg_done; + bool bigFonts; + + unsigned long long prev_id; + time_t prev_zeit; + unsigned long long next_id; + time_t next_zeit; + + int ox, oy, sx, sy, toph, sb; + int emptyLineCount, info1_lines; + int textCount; + std::vector epgText; + int topheight,topboxheight; + int botheight,botboxheight; + int medlineheight,medlinecount; + + void GetEPGData(const t_channel_id channel_id, unsigned long long id, time_t* startzeit ); + void GetPrevNextEPGData( unsigned long long id, time_t* startzeit ); + void addTextToArray( const std::string & text ); + void processTextToArray(std::string text); + void showText( int startPos, int ypos ); + bool hasFollowScreenings(const t_channel_id channel_id, const std::string & title); + int FollowScreenings(const t_channel_id channel_id, const std::string & title); + void showTimerEventBar(bool show); + void showHead(const t_channel_id channel_id); + + public: + + CEpgData(); + void start( ); + int show(const t_channel_id channel_id, unsigned long long id = 0, time_t* startzeit = NULL, bool doLoop = true ); + void hide(); +}; + + + +class CEPGDataHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + +}; + + + +#endif + diff --git a/src/gui/eventlist.cpp b/src/gui/eventlist.cpp new file mode 100644 index 000000000..701d03221 --- /dev/null +++ b/src/gui/eventlist.cpp @@ -0,0 +1,1077 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "widget/hintbox.h" +#include "gui/bouquetlist.h" +#include + +extern CBouquetList * bouquetList; +extern t_channel_id live_channel_id; + +#include /* CZapitClient::Utf8_to_Latin1 */ +#include + +#include + +#include +extern CPictureViewer * g_PicViewer; +#define PIC_W 52 +#define PIC_H 39 + +#define ROUND_RADIUS 9 + +void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = ""); +bool sectionsd_getActualEPGServiceKey(const t_channel_id uniqueServiceKey, CEPGData * epgdata); +bool sectionsd_getLinkageDescriptorsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::LinkageDescriptorList& descriptors); + +// sort operators +bool sortById (const CChannelEvent& a, const CChannelEvent& b) +{ + return a.eventID < b.eventID ; +} +bool sortByDescription (const CChannelEvent& a, const CChannelEvent& b) +{ + if(a.description == b.description) + return a.eventID < b.eventID; + else + return a.description < b.description ; +} +static bool sortByDateTime (const CChannelEvent& a, const CChannelEvent& b) +{ + return a.startTime < b.startTime; +} + +EventList::EventList() +{ + frameBuffer = CFrameBuffer::getInstance(); + selected = 0; + current_event = 0; + liststart = 0; + sort_mode = 0; + + m_search_list = SEARCH_LIST_NONE; + m_search_epg_item = SEARCH_LIST_NONE; + m_search_epg_item = SEARCH_EPG_TITLE; + m_search_channel_id = 1; + m_search_bouquet_id= 1; +} + +EventList::~EventList() +{ +} + +void EventList::readEvents(const t_channel_id channel_id) +{ + //evtlist = g_Sectionsd->getEventsServiceKey(channel_id &0xFFFFFFFFFFFFULL); + evtlist.clear(); + sectionsd_getEventsServiceKey(channel_id &0xFFFFFFFFFFFFULL, evtlist); + time_t azeit=time(NULL); + + CChannelEventList::iterator e; + + if ( evtlist.size() != 0 ) { + + CEPGData epgData; + // todo: what if there are more than one events in the Portal + //if (g_Sectionsd->getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData )) + if (sectionsd_getActualEPGServiceKey(channel_id&0xFFFFFFFFFFFFULL, &epgData )) + { +// epgData.eventID; +// epgData.epg_times.startzeit; + CSectionsdClient::LinkageDescriptorList linkedServices; + //if ( g_Sectionsd->getLinkageDescriptorsUniqueKey( epgData.eventID, linkedServices ) ) + if ( sectionsd_getLinkageDescriptorsUniqueKey( epgData.eventID, linkedServices ) ) + { + if ( linkedServices.size()> 1 ) + { + CChannelEventList evtlist2; // stores the temporary eventlist of the subchannel channelid + t_channel_id channel_id2; +#if 0 + for (e=evtlist.begin(); e!=evtlist.end(); ++e ) + { + if ( e->startTime > azeit ) { + break; + } + } + // next line is to have a valid e + if (evtlist.end() == e) --e; +#endif + for (unsigned int i=0; igetEventsServiceKey(channel_id2); + evtlist2.clear(); + sectionsd_getEventsServiceKey(channel_id2 &0xFFFFFFFFFFFFULL, evtlist2); + + for (unsigned int loop=0 ; loop= azeit) /*&& + (evtlist2[loop].startTime < e->startTime + (int)e->duration)*/ ) +#endif + { + //FIXME: bad ?evtlist2[loop].sub = true; + evtlist.push_back(evtlist2[loop]); + } + } + evtlist2.clear(); + } + } + } + } + } + // Houdini added for Private Premiere EPG, start sorted by start date/time + sort(evtlist.begin(),evtlist.end(),sortByDateTime); + // Houdini: dirty workaround for RTL double events, remove them + CChannelEventList::iterator e2; + for ( e=evtlist.begin(); e!=evtlist.end(); ++e ) + { + e2 = e+1; + if ( e2!=evtlist.end() && (e->startTime == e2->startTime)) { + evtlist.erase(e2); + } + } + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + + } + + current_event = (unsigned int)-1; + for ( e=evtlist.begin(); e!=evtlist.end(); ++e ) + { + if ( e->startTime > azeit ) { + break; + } + current_event++; + } + + if ( evtlist.size() == 0 ) + { + CChannelEvent evt; + + evt.description = g_Locale->getText(LOCALE_EPGLIST_NOEVENTS); + evt.eventID = 0; + evtlist.push_back(evt); + + } + if (current_event == (unsigned int)-1) + current_event = 0; + selected= current_event; + + return; +} + + +int EventList::exec(const t_channel_id channel_id, const std::string& channelname) // UTF-8 +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + bool in_search = 0; + + width = w_max (580, 20); + height = h_max (480, 20); + + iheight = 30; // info bar height (see below, hard coded at this time) + theight = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->getHeight(); + + if(theight < PIC_H) theight = PIC_H; + + fheight1 = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMLARGE]->getHeight(); + { + int h1 = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL]->getHeight(); + int h2 = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_DATETIME]->getHeight(); + fheight2 = (h1 > h2) ? h1 : h2; + } + fheight = fheight1 + fheight2 + 2; + fwidth1 = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_DATETIME]->getRenderWidth("DDD, 00:00, "); + fwidth2 = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL]->getRenderWidth("[999 min] "); + + + listmaxshow = (height-theight-iheight-0)/fheight; + height = theight+iheight+0+listmaxshow*fheight; // recalc height + + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + int res = menu_return::RETURN_REPAINT; +//printf("EventList::exec: channel_id %llx\n", channel_id); + if(m_search_list == SEARCH_LIST_NONE) // init globals once only + { + m_search_epg_item = SEARCH_EPG_TITLE; + //m_search_keyword = ""; + m_search_list = SEARCH_LIST_CHANNEL; + //m_search_channel_id = channel_id; + m_search_bouquet_id= bouquetList->getActiveBouquetNumber(); + //m_search_source_text = ""; + } + m_search_channel_id = channel_id; + m_showChannel = false; // do not show the channel in normal mode, we just need it in search mode + + name = channelname; + sort_mode=0; + paintHead(channel_id); + readEvents(channel_id); + paint(channel_id); + showFunctionBar(true); + + int oldselected = selected; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + bool loop = true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + + if (msg == CRCInput::RC_up || (int) msg == g_settings.key_channelList_pageup) + { + int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + selected = evtlist.size() - 1; + + paintItem(prev_selected - liststart, channel_id); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + + if(oldliststart!=liststart) + paint(channel_id); + else + paintItem(selected - liststart, channel_id); + +#if 0 + if ((g_settings.key_channelList_addremind != CRCInput::RC_nokey) || + ((g_settings.recording_type != CNeutrinoApp::RECORDING_OFF) && + (g_settings.key_channelList_addrecord != CRCInput::RC_nokey))) + { + showFunctionBar(true); + } +#endif + } + else if (msg == CRCInput::RC_down || (int) msg == g_settings.key_channelList_pagedown) + { + unsigned int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= evtlist.size()) { + if (((evtlist.size() / listmaxshow) + 1) * listmaxshow == evtlist.size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((evtlist.size() / listmaxshow) + 1) * listmaxshow))) ? (evtlist.size() - 1) : 0; + } + + paintItem(prev_selected - liststart, channel_id); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(channel_id); + else + paintItem(selected - liststart, channel_id); +#if 0 + if ((g_settings.key_channelList_addremind != CRCInput::RC_nokey) || + ((g_settings.recording_type != CNeutrinoApp::RECORDING_OFF) && + (g_settings.key_channelList_addrecord != CRCInput::RC_nokey))) + { + showFunctionBar(true); + } +#endif + } + + else if (msg == (neutrino_msg_t)g_settings.key_channelList_sort) + { + unsigned long long selected_id = evtlist[selected].eventID; + if(sort_mode==0) + { + sort_mode++; + sort(evtlist.begin(),evtlist.end(),sortByDescription); + } +#if 0 + else if(sort_mode==1) + { + sort_mode++; + sort(evtlist.begin(),evtlist.end(),sortById); + } +#endif + else + { + sort_mode=0; + sort(evtlist.begin(),evtlist.end(),sortByDateTime); + } + // find selected + for ( selected=0 ; selected < evtlist.size(); selected++ ) + { + if ( evtlist[selected].eventID == selected_id ) + break; + } + oldselected=selected; + if(selected <=listmaxshow) + liststart=0; + else + liststart=(selected/listmaxshow)*listmaxshow; + hide(); + paintHead(channel_id); + paint(channel_id); + showFunctionBar(true); + + } + + // -- I commented out the following part (code is working) + // -- reason: this is a little bit confusing, because e.g. you can enter the function + // -- with RED, but pressing RED doesn't leave - it triggers a record timer instead + // -- I think it's sufficient, to press RIGHT or HELP to get movie details and then + // -- press "auto record" or "auto switch" (rasc 2003-06-28) + // --- hm, no need to comment out that part, leave the decision to the user + // --- either set addrecord timer key to "no key" and leave eventlist with red (default now), + // --- or set addrecord timer key to "red key" (zwen 2003-07-29) + + else if ((msg == (neutrino_msg_t)g_settings.key_channelList_addrecord) && !g_settings.minimode) + { + if (g_settings.recording_type != CNeutrinoApp::RECORDING_OFF) + { + int tID = -1; + CTimerd::CTimerEventTypes etype = isScheduled(channel_id, &evtlist[selected], &tID); + if(etype == CTimerd::TIMER_RECORD) { + g_Timerd->removeTimerEvent(tID); + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + paint(channel_id); + continue; + } + char *recDir = g_settings.network_nfs_recordingdir; + if (g_settings.recording_choose_direct_rec_dir) + { + int id = -1; + CMountChooser recDirs(LOCALE_TIMERLIST_RECORDING_DIR,NEUTRINO_ICON_SETTINGS,&id,NULL,g_settings.network_nfs_recordingdir); + if (recDirs.hasItem()) + { + hide(); + recDirs.exec(NULL,""); + paint(channel_id); + } else + { + printf("[CEventList] no network devices available\n"); + } + if (id != -1) + recDir = g_settings.network_nfs_local_dir[id]; + else + recDir = NULL; + } + if (recDir != NULL) + { + + //FIXME: bad ?if (g_Timerd->addRecordTimerEvent(evtlist[selected].sub ? GET_CHANNEL_ID_FROM_EVENT_ID(evtlist[selected].eventID) : channel_id, + if (g_Timerd->addRecordTimerEvent(channel_id, + evtlist[selected].startTime, + evtlist[selected].startTime + evtlist[selected].duration, + evtlist[selected].eventID, evtlist[selected].startTime, + evtlist[selected].startTime - (ANNOUNCETIME + 120), + TIMERD_APIDS_CONF, true, recDir,false) == -1) + { + if(askUserOnTimerConflict(evtlist[selected].startTime - (ANNOUNCETIME + 120), + evtlist[selected].startTime + evtlist[selected].duration)) + { + //g_Timerd->addRecordTimerEvent(evtlist[selected].sub ? GET_CHANNEL_ID_FROM_EVENT_ID(evtlist[selected].eventID) : channel_id, + g_Timerd->addRecordTimerEvent(channel_id, + evtlist[selected].startTime, + evtlist[selected].startTime + evtlist[selected].duration, + evtlist[selected].eventID, evtlist[selected].startTime, + evtlist[selected].startTime - (ANNOUNCETIME + 120), + TIMERD_APIDS_CONF, true, recDir,true); + ShowLocalizedMessage(LOCALE_TIMER_EVENTRECORD_TITLE, LOCALE_TIMER_EVENTRECORD_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + } else { + ShowLocalizedMessage(LOCALE_TIMER_EVENTRECORD_TITLE, LOCALE_TIMER_EVENTRECORD_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + } + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + paint(channel_id); + } + } + else if ( msg == (neutrino_msg_t) g_settings.key_channelList_addremind ) + { + int tID = -1; + CTimerd::CTimerEventTypes etype = isScheduled(channel_id, &evtlist[selected], &tID); + if(etype == CTimerd::TIMER_ZAPTO) { + g_Timerd->removeTimerEvent(tID); + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + paint(channel_id); + continue; + } + // FIXME g_Timerd->addZaptoTimerEvent(evtlist[selected].sub ? GET_CHANNEL_ID_FROM_EVENT_ID(evtlist[selected].eventID) : channel_id, + g_Timerd->addZaptoTimerEvent(channel_id, + evtlist[selected].startTime, + evtlist[selected].startTime - ANNOUNCETIME, 0, + evtlist[selected].eventID, evtlist[selected].startTime, 0); + ShowLocalizedMessage(LOCALE_TIMER_EVENTTIMED_TITLE, LOCALE_TIMER_EVENTTIMED_MSG, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + paint(channel_id); + } + + else if (msg == CRCInput::RC_timeout) + { + selected = oldselected; + loop = false; + } + else if (msg == (neutrino_msg_t)g_settings.key_channelList_cancel) + { + if(in_search) { + in_search = false; + name = channelname; + paintHead(channel_id); + readEvents(channel_id); + paint(channel_id); + showFunctionBar(true); + } else { + selected = oldselected; + loop = false; + } + } + else if ( msg==CRCInput::RC_left || msg==CRCInput::RC_red ) + { + loop= false; + } + else if (msg==CRCInput::RC_help || msg==CRCInput::RC_right || msg==CRCInput::RC_ok || msg==CRCInput::RC_info) + { + if ( evtlist[selected].eventID != 0 ) + { + hide(); + + //FIXME res = g_EpgData->show(evtlist[selected].sub ? GET_CHANNEL_ID_FROM_EVENT_ID(evtlist[selected].eventID) : channel_id, evtlist[selected].eventID, &evtlist[selected].startTime); + res = g_EpgData->show(channel_id, evtlist[selected].eventID, &evtlist[selected].startTime); + if ( res == menu_return::RETURN_EXIT_ALL ) + { + loop = false; + } + else + { + g_RCInput->getMsg( &msg, &data, 0 ); + + if ( ( msg != CRCInput::RC_red ) && ( msg != CRCInput::RC_timeout ) ) + { + // RC_red schlucken + g_RCInput->postMsg( msg, data ); + } + timerlist.clear(); + g_Timerd->getTimerList (timerlist); + + paintHead(channel_id); + paint(channel_id); + showFunctionBar(true); + } + } + } + else if ( msg==CRCInput::RC_green ) + { + in_search = findEvents(); + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_CHANLIST]); + } + else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + g_RCInput->postMsg (msg, 0); + res = menu_return::RETURN_EXIT_ALL; + loop = false; + } + else + { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + hide(); + + return res; +} + +void EventList::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); + showFunctionBar (false); + +} + +CTimerd::CTimerEventTypes EventList::isScheduled(t_channel_id channel_id, CChannelEvent * event, int * tID) +{ + CTimerd::TimerList::iterator timer = timerlist.begin(); + for(; timer != timerlist.end(); timer++) { + if(timer->channel_id == channel_id && (timer->eventType == CTimerd::TIMER_ZAPTO || timer->eventType == CTimerd::TIMER_RECORD)) { + if(timer->epgID == event->eventID) { + if(timer->epg_starttime == event->startTime) { + if(tID) + *tID = timer->eventID; + return timer->eventType; + } + } + } + } + return (CTimerd::CTimerEventTypes) 0; +} + +void EventList::paintItem(unsigned int pos, t_channel_id channel_id) +{ + uint8_t color; + fb_pixel_t bgcolor; + int ypos = y+ theight+0 + pos*fheight; + std::string datetime1_str, datetime2_str, duration_str; + const char * icontype = 0; + + if (liststart+pos==selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else if (liststart+pos == current_event ) + { + color = COL_MENUCONTENT + 1; + bgcolor = COL_MENUCONTENT_PLUS_1; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + frameBuffer->paintBoxRel(x, ypos, width- 15, fheight, bgcolor, color == COL_MENUCONTENTSELECTED ? ROUND_RADIUS : 0, 3); + + if(liststart+posgetText(CLocaleManager::getWeekday(tmStartZeit)); + + strftime(tmpstr, sizeof(tmpstr), ". %H:%M, ", tmStartZeit ); + datetime1_str += tmpstr; + + strftime(tmpstr, sizeof(tmpstr), " %d. ", tmStartZeit ); + datetime2_str = tmpstr; + + datetime2_str += g_Locale->getText(CLocaleManager::getMonth(tmStartZeit)); + + datetime2_str += '.'; + + if ( m_showChannel ) // show the channel if we made a event search only (which could be made through all channels ). + { + t_channel_id channel = evtlist[liststart+pos].get_channel_id(); + datetime2_str += " "; + datetime2_str += g_Zapit->getChannelName(channel); + } + + sprintf(tmpstr, "[%d min]", evtlist[liststart+pos].duration / 60 ); + duration_str = tmpstr; +#if 0 + CTimerd::TimerList::iterator timer = timerlist.begin(); + for(; timer != timerlist.end(); timer++) { + if(timer->channel_id == channel_id && (timer->eventType == CTimerd::TIMER_ZAPTO || timer->eventType == CTimerd::TIMER_RECORD)) { + if(timer->epgID == evtlist[liststart+pos].eventID) { + if(timer->epg_starttime == evtlist[liststart+pos].startTime) + icontype = timer->eventType == CTimerd::TIMER_ZAPTO ? NEUTRINO_ICON_BUTTON_YELLOW : NEUTRINO_ICON_BUTTON_RED; + break; + } + } + } +#endif + } + CTimerd::CTimerEventTypes etype = isScheduled(channel_id, &evtlist[liststart+pos]); + icontype = etype == CTimerd::TIMER_ZAPTO ? NEUTRINO_ICON_BUTTON_YELLOW : etype == CTimerd::TIMER_RECORD ? NEUTRINO_ICON_BUTTON_RED : 0; + + // 1st line + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_DATETIME]->RenderString(x+5, ypos+ fheight1+3, fwidth1+5, datetime1_str, color, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_DATETIME]->RenderString(x+5+fwidth1, ypos+ fheight1+3, width-fwidth1-10- 20, datetime2_str, color, 0, true); // UTF-8 + + int seit = ( evtlist[liststart+pos].startTime - time(NULL) ) / 60; + if ( (seit> 0) && (seit<100) && (duration_str.length()!=0) ) + { + char beginnt[100]; + sprintf((char*) &beginnt, "in %d min", seit); + int w = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL]->getRenderWidth(beginnt) + 10; + + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL]->RenderString(x+width-fwidth2-5- 20- w, ypos+ fheight1+3, fwidth2, beginnt, color); + } + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL]->RenderString(x+width-fwidth2-5- 20, ypos+ fheight1+3, fwidth2, duration_str, color, 0, true); // UTF-8 + if(icontype != 0) + frameBuffer->paintIcon(icontype, x+2, ypos + fheight - 16 - (fheight1 - 16)/2); + // 2nd line + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMLARGE]->RenderString(x+ 20, ypos+ fheight, width- 25- 20, evtlist[liststart+pos].description, color, 0, true); + } +} + +void EventList::paintHead(t_channel_id channel_id) +{ + bool logo_ok = false; + + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); +#ifndef FB_USE_PALETTE + logo_ok = g_PicViewer->DisplayLogo(channel_id, x+10, y+(theight-PIC_H)/2, PIC_W, PIC_H); +#endif + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->RenderString(x+15+(logo_ok? 5+PIC_W:0),y+theight+1, width, name.c_str(), COL_MENUHEAD, 0, true); // UTF-8 +} + +void EventList::paint(t_channel_id channel_id) +{ + liststart = (selected/listmaxshow)*listmaxshow; + + if (evtlist[0].eventID != 0) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 ); + + frameBuffer->paintBoxRel(x, y+theight, width, height-theight-iheight, COL_MENUCONTENT_PLUS_0, 0, 1); + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((evtlist.size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); + +} + +void EventList::showFunctionBar (bool show) +{ + int bx,by,bw,bh; + int cellwidth; // 4 cells + int h_offset, pos; + int bdx; + + bw = width; + bh = iheight; + bx = x; + by = y + height-iheight; + h_offset = 5; + cellwidth = bw / 4; + bdx = iheight-1; + + + frameBuffer->paintBackgroundBoxRel(bx,by,bw,bh); + // -- hide only? + if (! show) return; + + // -- frameBuffer->paintBoxRel(x,y,w,h, COL_INFOBAR_SHADOW_PLUS_1); + //frameBuffer->paintBoxRel(bx,by,bw,bh, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(bx,by,bw,bh, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + + + // -- Button: Timer Record & Channelswitch + if ((g_settings.recording_type != CNeutrinoApp::RECORDING_OFF) && + ((unsigned int) g_settings.key_channelList_addrecord != CRCInput::RC_nokey)) + { + pos = 0; +#warning FIXME: display other icons depending on g_settings.key_channelList_addrecord + if ((g_settings.key_channelList_addrecord == CRCInput::RC_red) && !g_settings.minimode) { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_RED, bx+8+cellwidth*pos, by+h_offset); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(bx+bdx+cellwidth*pos, by+bh-h_offset, bw-30, g_Locale->getText(LOCALE_EVENTLISTBAR_RECORDEVENT), COL_INFOBAR, 0, true); // UTF-8 + } + } + if (1) + { + pos = 1; + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, bx+8+cellwidth*pos, by+h_offset); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(bx+bdx+cellwidth*pos, by+bh-h_offset, bw-30, g_Locale->getText(LOCALE_EVENTFINDER_SEARCH), COL_INFOBAR, + 0, true); // UTF-8 + } + + // Button: Timer Channelswitch + if ((unsigned int) g_settings.key_channelList_addremind != CRCInput::RC_nokey) + { + pos = 2; +#warning FIXME: display other icons depending on g_settings.key_channelList_addremind + if (g_settings.key_channelList_addremind == CRCInput::RC_yellow) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_YELLOW, bx+8+cellwidth*pos, by+h_offset ); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(bx+bdx+cellwidth*pos, by+bh-h_offset, bw-30, g_Locale->getText(LOCALE_EVENTLISTBAR_CHANNELSWITCH), COL_INFOBAR, 0, true); // UTF-8 + } + + // Button: Event Re-Sort + if ((unsigned int) g_settings.key_channelList_sort != CRCInput::RC_nokey) + { + pos = 3; +#warning FIXME: display other icons depending on g_settings.key_channelList_sort value + if (g_settings.key_channelList_sort == CRCInput::RC_blue) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_BLUE, bx+8+cellwidth*pos, by+h_offset ); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(bx+bdx+cellwidth*pos, by+bh-h_offset, bw-30, g_Locale->getText(LOCALE_EVENTLISTBAR_EVENTSORT), COL_INFOBAR, 0, true); // UTF-8 + } +} + +int CEventListHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + EventList *e; + CChannelList *channelList; + + + if (parent) { + parent->hide(); + } + + e = new EventList; + + channelList = CNeutrinoApp::getInstance()->channelList; + //e->exec(channelList->getActiveChannel_ChannelID(), channelList->getActiveChannelName()); // UTF-8 + //e->exec(g_Zapit->getCurrentServiceID(), channelList->getActiveChannelName()); // UTF-8 + e->exec(live_channel_id, channelList->getActiveChannelName()); // UTF-8 + delete e; + + return res; +} + + + +/************************************************************************************************/ +int EventList::findEvents(void) +/************************************************************************************************/ +{ + int res = 0; + int event = 0; + t_channel_id channel_id; //g_Zapit->getCurrentServiceID() + + CEventFinderMenu menu( &event, + &m_search_epg_item, + &m_search_keyword, + &m_search_list, + &m_search_channel_id, + &m_search_bouquet_id + ); + hide(); + menu.exec(NULL,""); + + if(event == 1) + { + res = 1; + m_showChannel = true; // force the event list to paint the channel name + evtlist.clear(); + if(m_search_list == SEARCH_LIST_CHANNEL) + { + //g_Sectionsd->getEventsServiceKeySearchAdd(evtlist,m_search_channel_id & 0xFFFFFFFFFFFFULL,m_search_epg_item,m_search_keyword); + sectionsd_getEventsServiceKey(m_search_channel_id & 0xFFFFFFFFFFFFULL, evtlist, m_search_epg_item,m_search_keyword); + } + else if(m_search_list == SEARCH_LIST_BOUQUET) + { + int channel_nr = bouquetList->Bouquets[m_search_bouquet_id]->channelList->getSize(); + for(int channel = 0; channel < channel_nr; channel++) + { + channel_id = bouquetList->Bouquets[m_search_bouquet_id]->channelList->getChannelFromIndex(channel)->channel_id; + //g_Sectionsd->getEventsServiceKeySearchAdd(evtlist,channel_id & 0xFFFFFFFFFFFFULL,m_search_epg_item,m_search_keyword); + sectionsd_getEventsServiceKey(channel_id & 0xFFFFFFFFFFFFULL, evtlist, m_search_epg_item,m_search_keyword); + } + } + else if(m_search_list == SEARCH_LIST_ALL) + { + CHintBox box(LOCALE_TIMING_EPG,g_Locale->getText(LOCALE_EVENTFINDER_SEARCHING)); + box.paint(); + int bouquet_nr = bouquetList->Bouquets.size(); + for(int bouquet = 0; bouquet < bouquet_nr; bouquet++) + { + int channel_nr = bouquetList->Bouquets[bouquet]->channelList->getSize(); + for(int channel = 0; channel < channel_nr; channel++) + { + channel_id = bouquetList->Bouquets[bouquet]->channelList->getChannelFromIndex(channel)->channel_id; + //g_Sectionsd->getEventsServiceKeySearchAdd(evtlist,channel_id & 0xFFFFFFFFFFFFULL,m_search_epg_item,m_search_keyword); + sectionsd_getEventsServiceKey(channel_id & 0xFFFFFFFFFFFFULL,evtlist, m_search_epg_item,m_search_keyword); + } + } + box.hide(); + } + sort(evtlist.begin(),evtlist.end(),sortByDateTime); + current_event = (unsigned int)-1; + time_t azeit=time(NULL); + + CChannelEventList::iterator e; + for ( e=evtlist.begin(); e!=evtlist.end(); ++e ) + { + if ( e->startTime > azeit ) { + break; + } + current_event++; + } + if(evtlist.empty()) + { + if ( evtlist.size() == 0 ) + { + CChannelEvent evt; + //evt.description = m_search_keyword + ": " + g_Locale->getText(LOCALE_EPGVIEWER_NOTFOUND); + evt.description = g_Locale->getText(LOCALE_EPGVIEWER_NOTFOUND); + evt.eventID = 0; + evtlist.push_back(evt); + } + } + if (current_event == (unsigned int)-1) + current_event = 0; + selected= current_event; + + name = g_Locale->getText(LOCALE_EVENTFINDER_SEARCH); + name += ": '"; + name += m_search_keyword; + name += "'"; + } + paintHead(0); + paint(); + showFunctionBar(true); + return(res); +} + +/************************************************************************************************/ +/* +class CSearchNotifier : public CChangeObserver +{ + private: + CMenuItem* menuItem; + public: + CSearchNotifier( CMenuItem* i){menuItem=i}; + bool changeNotify(const neutrino_locale_t t, void * data) + { + int selected = *(int*)data; + menuItem->setActive(1); + menuItem + } +}; +*/ +/************************************************************************************************ +bool CEventFinderMenuHandler::changeNotify(const neutrino_locale_t OptionName, void *Data) +{ + if(OptionName == ) + { + } + + return true; +} +*/ + +#define SEARCH_LIST_OPTION_COUNT 3 +const CMenuOptionChooser::keyval SEARCH_LIST_OPTIONS[SEARCH_LIST_OPTION_COUNT] = +{ +// { EventList::SEARCH_LIST_NONE , LOCALE_PICTUREVIEWER_RESIZE_NONE }, + { EventList::SEARCH_LIST_CHANNEL , LOCALE_TIMERLIST_CHANNEL }, + { EventList::SEARCH_LIST_BOUQUET , LOCALE_BOUQUETLIST_HEAD }, + { EventList::SEARCH_LIST_ALL , LOCALE_CHANNELLIST_HEAD } +}; + + +#define SEARCH_EPG_OPTION_COUNT 3 +const CMenuOptionChooser::keyval SEARCH_EPG_OPTIONS[SEARCH_EPG_OPTION_COUNT] = +{ +// { EventList::SEARCH_EPG_NONE , LOCALE_PICTUREVIEWER_RESIZE_NONE }, + { EventList::SEARCH_EPG_TITLE , LOCALE_FONTSIZE_EPG_TITLE }, + { EventList::SEARCH_EPG_INFO1 , LOCALE_FONTSIZE_EPG_INFO1 }, + { EventList::SEARCH_EPG_INFO2 , LOCALE_FONTSIZE_EPG_INFO2 } +// ,{ EventList::SEARCH_EPG_GENRE , LOCALE_MOVIEBROWSER_INFO_GENRE_MAJOR } +}; + + + +/************************************************************************************************/ +CEventFinderMenu::CEventFinderMenu( int* event, + int* search_epg_item, + std::string* search_keyword, + int* search_list, + t_channel_id* search_channel_id, + t_bouquet_id* search_bouquet_id) +/************************************************************************************************/ +{ + m_event = event; + m_search_epg_item = search_epg_item; + m_search_keyword = search_keyword; + m_search_list = search_list; + m_search_channel_id = search_channel_id; + m_search_bouquet_id = search_bouquet_id; +} + + +/************************************************************************************************/ +int CEventFinderMenu::exec(CMenuTarget* parent, const std::string &actionkey) +/************************************************************************************************/ +{ + int res = menu_return::RETURN_REPAINT; + + + if(actionkey =="") + { + if(parent != NULL) + parent->hide(); + //printf("0\n"); + showMenu(); + } + else if(actionkey =="1") + { + //printf("1\n"); + *m_event = true; + res = menu_return::RETURN_EXIT_ALL; + } + else if(actionkey =="2") + { + //printf("2\n"); + /* + if(*m_search_list == EventList::SEARCH_LIST_CHANNEL) + { + mf[1]->setActive(true); + m_search_channelname = g_Zapit->getChannelName(*m_search_channel_id);; + } + else if(*m_search_list == EventList::SEARCH_LIST_BOUQUET) + { + mf[1]->setActive(true); + m_search_channelname = bouquetList->Bouquets[*m_search_bouquet_id]->channelList->getName(); + } + else if(*m_search_list == EventList::SEARCH_LIST_ALL) + { + mf[1]->setActive(false); + m_search_channelname = ""; + } + */ + } + else if(actionkey =="3") + { + //printf("3\n"); + // get channel id / bouquet id + if(*m_search_list == EventList::SEARCH_LIST_CHANNEL) + { + int nNewChannel; + int nNewBouquet; + nNewBouquet = bouquetList->show(); + //printf("new_bouquet_id %d\n",nNewBouquet); + if (nNewBouquet > -1) + { + nNewChannel = bouquetList->Bouquets[nNewBouquet]->channelList->show(); + //printf("nNewChannel %d\n",nNewChannel); + if (nNewChannel > -1) + { + *m_search_bouquet_id = nNewBouquet; + *m_search_channel_id = bouquetList->Bouquets[nNewBouquet]->channelList->getActiveChannel_ChannelID(); + m_search_channelname = g_Zapit->getChannelName(*m_search_channel_id); + } + } + } + else if(*m_search_list == EventList::SEARCH_LIST_BOUQUET) + { + int nNewBouquet; + nNewBouquet = bouquetList->show(); + //printf("new_bouquet_id %d\n",nNewBouquet); + if (nNewBouquet > -1) + { + *m_search_bouquet_id = nNewBouquet; + m_search_channelname = bouquetList->Bouquets[nNewBouquet]->channelList->getName(); + } + } + } + else if(actionkey =="4") + { + //printf("4\n"); + } + + return res; +} + +/************************************************************************************************/ +int CEventFinderMenu::showMenu(void) +/************************************************************************************************/ +{ + int res = menu_return::RETURN_REPAINT; + *m_event = false; + + if(*m_search_list == EventList::SEARCH_LIST_CHANNEL) + { + m_search_channelname = g_Zapit->getChannelName(*m_search_channel_id); + } + else if(*m_search_list == EventList::SEARCH_LIST_BOUQUET) + { + m_search_channelname = bouquetList->Bouquets[*m_search_bouquet_id]->channelList->getName(); + } + else if(*m_search_list == EventList::SEARCH_LIST_ALL) + { + m_search_channelname ==""; + } + + CStringInputSMS stringInput(LOCALE_EVENTFINDER_KEYWORD,m_search_keyword, 20, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.: "); + + CMenuForwarder* mf2 = new CMenuForwarder(LOCALE_EVENTFINDER_KEYWORD ,true, *m_search_keyword, &stringInput, NULL, CRCInput::RC_1 ); + CMenuOptionChooser* mo0 = new CMenuOptionChooser(LOCALE_EVENTFINDER_SEARCH_WITHIN_LIST , m_search_list, SEARCH_LIST_OPTIONS, SEARCH_LIST_OPTION_COUNT, true, NULL, CRCInput::RC_2); + CMenuForwarderNonLocalized* mf1 = new CMenuForwarderNonLocalized("", *m_search_list != EventList::SEARCH_LIST_ALL, m_search_channelname, this, "3", CRCInput::RC_3 ); + CMenuOptionChooser* mo1 = new CMenuOptionChooser(LOCALE_EVENTFINDER_SEARCH_WITHIN_EPG, m_search_epg_item, SEARCH_EPG_OPTIONS, SEARCH_EPG_OPTION_COUNT, true, NULL, CRCInput::RC_4); + CMenuForwarder* mf0 = new CMenuForwarder(LOCALE_EVENTFINDER_START_SEARCH, true, NULL, this, "1", CRCInput::RC_5 ); + + CMenuWidget searchMenu(LOCALE_EVENTFINDER_HEAD, "features.raw", 450); + + searchMenu.addItem(GenericMenuSeparator); + searchMenu.addItem(mf2, false); + searchMenu.addItem(GenericMenuSeparatorLine); + searchMenu.addItem(mo0, false); + searchMenu.addItem(mf1, false); + searchMenu.addItem(mo1, false); + searchMenu.addItem(GenericMenuSeparatorLine); + searchMenu.addItem(mf0, false); + + res = searchMenu.exec(NULL,""); + return(res); +} + diff --git a/src/gui/eventlist.h b/src/gui/eventlist.h new file mode 100644 index 000000000..0e3fd528d --- /dev/null +++ b/src/gui/eventlist.h @@ -0,0 +1,150 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __EVENTLIST_HPP__ +#define __EVENTLIST_HPP__ + +#include +#include + +#include +#include +#include +#include + +#include "color.h" +#include "infoviewer.h" + +#include "widget/menue.h" + +#include + + +class EventList +{ + // Eventfinder start + public: + typedef enum + { + SEARCH_EPG_NONE, + SEARCH_EPG_TITLE, + SEARCH_EPG_INFO1, + SEARCH_EPG_INFO2, + SEARCH_EPG_GENRE, + SEARCH_EPG_ALL + }SEARCH_EPG; + typedef enum + { + SEARCH_LIST_NONE, + SEARCH_LIST_CHANNEL, + SEARCH_LIST_BOUQUET, + SEARCH_LIST_ALL + }SEARCH_LIST; + private: + int m_search_epg_item; + std::string m_search_keyword; + int m_search_list; + t_channel_id m_search_channel_id; + t_bouquet_id m_search_bouquet_id; + bool m_showChannel; + + private: + int findEvents(void); + // Eventfinder end + + private: + CFrameBuffer *frameBuffer; + CChannelEventList evtlist; + CTimerd::TimerList timerlist; + void readEvents(const t_channel_id channel_id); + unsigned int selected; + unsigned int current_event; + unsigned int liststart; + unsigned int listmaxshow; + unsigned int numwidth; + int fheight; // Fonthoehe Channellist-Inhalt + int fheight1,fheight2; + int fwidth1,fwidth2; + int theight; // Fonthoehe Channellist-Titel + int iheight; // Height info bar + + int key; + std::string name; + + int width; + int height; + int x; + int y; + int sort_mode; + + void paintItem(unsigned pos, t_channel_id channel_id = 0); + void paint(t_channel_id channel_id = 0); + void paintHead(t_channel_id channel_id); + void hide(); + void showFunctionBar(bool show); + CTimerd::CTimerEventTypes isScheduled(t_channel_id channel_id, CChannelEvent * event, int * tID = NULL); + + public: + EventList(); + ~EventList(); + int exec(const t_channel_id channel_id, const std::string& channelname); // UTF-8 +}; + +class CEventListHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + +}; + +class CEventFinderMenu : public CMenuTarget +{ + private: + int* m_event; + int* m_search_epg_item; + std::string* m_search_keyword; + int* m_search_list; + std::string m_search_channelname; + t_channel_id* m_search_channel_id; + t_bouquet_id* m_search_bouquet_id; + int showMenu(void); + public: + CEventFinderMenu( int* event, + int* search_epg_item, + std::string* search_keyword, + int* search_list, + t_channel_id* search_channel_id, + t_bouquet_id* search_bouquet_id); + int exec( CMenuTarget* parent, const std::string &actionkey); + +}; +#endif diff --git a/src/gui/favorites.cpp b/src/gui/favorites.cpp new file mode 100644 index 000000000..58afb0e71 --- /dev/null +++ b/src/gui/favorites.cpp @@ -0,0 +1,169 @@ +/* + Neutrino-GUI - DBoxII-Project + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + + +#include + +extern CBouquetList * bouquetList; /* neutrino.cpp */ +extern t_channel_id live_channel_id; +void addChannelToBouquet(const unsigned int bouquet, const t_channel_id channel_id); +extern CBouquetManager *g_bouquetManager; +// +// -- Add current channel to Favorites-Bouquet +// -- Return Status (bit-Status):A +// -- 1 = Bouquet created +// -- 2 = Channel added (if not set, channel already in BQ) +// -- rasc +// + +int CFavorites::addChannelToFavorites(bool show_list) +{ + signed int bouquet_id; + t_channel_id channel_id; + const char * fav_bouquetname; + int status = 0; + + + // no bouquet-List? do nothing + if (!bouquetList) return status; + + if(show_list) + { + bouquet_id = bouquetList->exec(false); + if (bouquet_id < 0) + return bouquet_id; + printf("[favorites] bouquet name %s\n", bouquetList->Bouquets[bouquet_id]->channelList->getName()); + bouquet_id = g_Zapit->existsBouquet(bouquetList->Bouquets[bouquet_id]->channelList->getName()); + if (bouquet_id == -1) + return bouquet_id; + } + else + { + // -- check if Favorite Bouquet exists: if not, create it. + bouquet_id = g_bouquetManager->existsUBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + if (bouquet_id == -1) { + g_bouquetManager->addBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + bouquet_id = g_bouquetManager->existsUBouquet(g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME), true); + // status |= 1; + } + } + + //channel_id = g_Zapit->getCurrentServiceID(); + channel_id = live_channel_id; + + if(!g_bouquetManager->existsChannelInBouquet(bouquet_id, channel_id)) { + addChannelToBouquet(bouquet_id, channel_id); + status |= 2; + } + + // -- tell zapit to save Boquets and reinit (if changed) + if (status) + { + g_Zapit->saveBouquets(); + } + + return status; +} + +// +// -- Menue Handler Interface +// -- to fit the MenueClasses from McClean +// -- Add current channel to Favorites and display user messagebox +// + +int CFavorites::exec(CMenuTarget* parent, const std::string & actionKey) +{ + int status; + std::string str; + int res = menu_return::RETURN_EXIT_ALL; + bool show_list; + //printf("[favorites] key %s\n", actionKey.c_str()); + show_list = (actionKey == "showlist"); + if (parent) + parent->hide(); + + if (!bouquetList) { + ShowLocalizedMessage(LOCALE_FAVORITES_BOUQUETNAME, LOCALE_FAVORITES_NOBOUQUETS, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + return res; + } + + + CHintBox* hintBox = new CHintBox(LOCALE_FAVORITES_BOUQUETNAME, g_Locale->getText(LOCALE_FAVORITES_ADDCHANNEL), 380); // UTF-8 + if(!show_list) + hintBox->paint(); + + status = addChannelToFavorites(show_list); + + hintBox->hide(); + delete hintBox; + + // -- Display result + + //printf("[favorites] status %d\n", status); + if(status < 0) + return menu_return::RETURN_REPAINT; + + str = ""; + if(show_list) + { + if (status & 2) str += g_Locale->getText(LOCALE_EXTRA_CHADDED); + else str += g_Locale->getText(LOCALE_EXTRA_CHALREADYINBQ); + ShowMsgUTF(LOCALE_EXTRA_ADD_TO_BOUQUET, str, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); // UTF-8 + } + else + { + if (status & 1) str += g_Locale->getText(LOCALE_FAVORITES_BQCREATED); + if (status & 2) str += g_Locale->getText(LOCALE_FAVORITES_CHADDED); + else str += g_Locale->getText(LOCALE_FAVORITES_CHALREADYINBQ); + if (status) str += g_Locale->getText(LOCALE_FAVORITES_FINALHINT); + ShowMsgUTF(LOCALE_FAVORITES_BOUQUETNAME, str, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); // UTF-8 + } + + + + // if (status) { + // g_RCInput->postMsg( NeutrinoMessages::EVT_BOUQUETSCHANGED, 0 ); + // } + + return res; +} diff --git a/src/gui/favorites.h b/src/gui/favorites.h new file mode 100644 index 000000000..060d72d5c --- /dev/null +++ b/src/gui/favorites.h @@ -0,0 +1,47 @@ +/* + Neutrino-GUI - DBoxII-Project + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __favorites__ +#define __favorites__ + +#include + +#include + + +// +// -- Implement Favorites... +// -- Class for adding Favorite channels to a bouquet called "Neutrino__Favorites" +// -- Bouquet will be created on the first add! +// +class CFavorites : public CMenuTarget +{ + + public: + + int addChannelToFavorites (bool show_list); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/filebrowser.cpp b/src/gui/filebrowser.cpp new file mode 100644 index 000000000..665adba2e --- /dev/null +++ b/src/gui/filebrowser.cpp @@ -0,0 +1,1518 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* include before to enable 64 bit file offsets */ +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#ifdef __USE_FILE_OFFSET64 +typedef struct dirent64 dirent_struct; +#define my_alphasort alphasort64 +#define my_scandir scandir64 +typedef struct stat64 stat_struct; +#define my_stat stat64 +#define my_lstat lstat64 +#else +typedef struct dirent dirent_struct; +#define my_alphasort alphasort +#define my_scandir scandir +typedef struct stat stat_struct; +#define my_stat stat +#define my_lstat lstat +#error not using 64 bit file offsets +#endif + +#define SMSKEY_TIMEOUT 2000 +//------------------------------------------------------------------------ +size_t CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data) +{ + std::string* pStr = (std::string*) data; + pStr->append((char*) ptr, nmemb); // do only add the correct size, do not go until end of data (chunked transfer) + return size*nmemb; +} + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ + +SMSKeyInput::SMSKeyInput() +{ + resetOldKey(); + m_timeout = SMSKEY_TIMEOUT; +} +//------------------------------------------------------------------------ + +unsigned char SMSKeyInput::handleMsg(const neutrino_msg_t msg) +{ + timeval keyTime; + gettimeofday(&keyTime,NULL); + bool timeoutNotReached = (keyTime.tv_sec*1000+keyTime.tv_usec/1000 + <= m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000 + m_timeout); + +// printf("act: %ld , old: %ld (diff: %ld ) , timeout: %ld => timout= %d\n", +// keyTime.tv_sec*1000+keyTime.tv_usec/1000, +// m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000, +// keyTime.tv_sec*1000+keyTime.tv_usec/1000 - +// m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000, +// m_timeout,!timeoutNotReached); + + unsigned char key = 0; + if(msg == CRCInput::RC_1) + { + key = '1'; + } + if(msg == CRCInput::RC_2) + { + if(m_oldKey == 'a' && timeoutNotReached) + key = 'b'; + else if(m_oldKey == 'b' && timeoutNotReached) + key = 'c'; + else if(m_oldKey == 'c' && timeoutNotReached) + key = '2'; + else + key = 'a'; + } + else if(msg == CRCInput::RC_3) + { + if(m_oldKey == 'd' && timeoutNotReached) + key = 'e'; + else if(m_oldKey == 'e' && timeoutNotReached) + key = 'f'; + else if(m_oldKey == 'f' && timeoutNotReached) + key = '3'; + else + key = 'd'; + } + else if(msg == CRCInput::RC_4) + { + if(m_oldKey == 'g' && timeoutNotReached) + key = 'h'; + else if(m_oldKey == 'h' && timeoutNotReached) + key = 'i'; + else if(m_oldKey == 'i' && timeoutNotReached) + key = '4'; + else + key = 'g'; + } + else if(msg == CRCInput::RC_5) + { + if(m_oldKey == 'j' && timeoutNotReached) + key = 'k'; + else if(m_oldKey == 'k' && timeoutNotReached) + key = 'l'; + else if(m_oldKey == 'l' && timeoutNotReached) + key = '5'; + else + key = 'j'; + } + else if(msg == CRCInput::RC_6) + { + if(m_oldKey == 'm' && timeoutNotReached) + key = 'n'; + else if(m_oldKey == 'n' && timeoutNotReached) + key = 'o'; + else if(m_oldKey == 'o' && timeoutNotReached) + key = '6'; + else + key = 'm'; + } + else if(msg == CRCInput::RC_7) + { + if(m_oldKey == 'p' && timeoutNotReached) + key = 'q'; + else if(m_oldKey == 'q' && timeoutNotReached) + key = 'r'; + else if(m_oldKey == 'r' && timeoutNotReached) + key = 's'; + else if(m_oldKey == 's' && timeoutNotReached) + key = 's'; + else + key = 'p'; + } + else if(msg == CRCInput::RC_8) + { + if(m_oldKey == 't' && timeoutNotReached) + key = 'u'; + else if(m_oldKey == 'u' && timeoutNotReached) + key = 'v'; + else if(m_oldKey == 'v' && timeoutNotReached) + key = '8'; + else + key = 't'; + } + else if(msg == CRCInput::RC_9) + { + if(m_oldKey == 'w' && timeoutNotReached) + key = 'x'; + else if(m_oldKey == 'x' &&timeoutNotReached) + key = 'y'; + else if(m_oldKey == 'y' &&timeoutNotReached) + key = 'z'; + else if(m_oldKey == 'z' && timeoutNotReached) + key = '9'; + else + key = 'w'; + } + else if(msg == CRCInput::RC_0) + { + key = '0'; + } + m_oldKeyTime=keyTime; + m_oldKey=key; + return key; +} +//------------------------------------------------------------------------ + +void SMSKeyInput::resetOldKey() +{ + m_oldKeyTime.tv_sec = 0; + m_oldKeyTime.tv_usec = 0; + m_oldKey = 0; +} + +unsigned char SMSKeyInput::getOldKey() const +{ + return m_oldKey; +} + +const timeval* SMSKeyInput::getOldKeyTime() const +{ + return &m_oldKeyTime; +} + +time_t SMSKeyInput::getOldKeyTimeSec() const +{ + return m_oldKeyTime.tv_sec; +} + + +int SMSKeyInput::getTimeout() const +{ + return m_timeout; +} + +void SMSKeyInput::setTimeout(int timeout) +{ + m_timeout = timeout; +} + + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ + +bool comparetolower(const char a, const char b) +{ + return tolower(a) < tolower(b); +}; + +// sort operators +bool sortByName (const CFile& a, const CFile& b) +{ + if (std::lexicographical_compare(a.Name.begin(), a.Name.end(), b.Name.begin(), b.Name.end(), comparetolower)) + return true; + + if (std::lexicographical_compare(b.Name.begin(), b.Name.end(), a.Name.begin(), a.Name.end(), comparetolower)) + return false; + + return a.Mode < b.Mode; +/* + int result = __gnu_cxx::lexicographical_compare_3way(a.Name.begin(), a.Name.end(), b.Name.begin(), b.Name.end(), comparetolower); + + if (result == 0) + return a.Mode < b.Mode; + else + return result < 0; +*/ +} + +bool sortByNameDirsFirst(const CFile& a, const CFile& b) +// Sorts alphabetically with Directories first +{ + int typea, typeb; + typea = a.getType(); + typeb = b.getType(); + + if (typea == CFile::FILE_DIR) + if (typeb == CFile::FILE_DIR) + //both directories + return sortByName(a, b); + else + //only a is directory + return true; + else if (typeb == CFile::FILE_DIR) + //only b is directory + return false; + else + //no directory + return sortByName(a, b); +} + +bool sortByType (const CFile& a, const CFile& b) +{ + if(a.Mode == b.Mode) + return sortByName(a, b); + else + return a.Mode < b.Mode; +} + +bool sortByDate (const CFile& a, const CFile& b) +{ + if(a.getFileName()=="..") + return true; + if(b.getFileName()=="..") + return false; + return a.Time < b.Time ; +} + +bool sortBySize (const CFile& a, const CFile& b) +{ + if(a.getFileName()=="..") + return true; + if(b.getFileName()=="..") + return false; + return a.Size < b.Size; +} + +bool (* const sortBy[FILEBROWSER_NUMBER_OF_SORT_VARIANTS])(const CFile& a, const CFile& b) = +{ + &sortByName, + &sortByNameDirsFirst, + &sortByType, + &sortByDate, + &sortBySize +}; + +const neutrino_locale_t sortByNames[FILEBROWSER_NUMBER_OF_SORT_VARIANTS] = +{ + LOCALE_FILEBROWSER_SORT_NAME, + LOCALE_FILEBROWSER_SORT_NAMEDIRSFIRST, + LOCALE_FILEBROWSER_SORT_TYPE, + LOCALE_FILEBROWSER_SORT_DATE, + LOCALE_FILEBROWSER_SORT_SIZE +}; + +//------------------------------------------------------------------------ + +CFileBrowser::CFileBrowser() +{ + commonInit(); + base = ""; + m_Mode = ModeFile; +} + +CFileBrowser::CFileBrowser(const char * const _base, const tFileBrowserMode mode) +{ + commonInit(); + base = _base; + m_Mode = mode; +} + +void CFileBrowser::commonInit() +{ + frameBuffer = CFrameBuffer::getInstance(); + + Filter = NULL; + use_filter = true; + Multi_Select = false; + Dirs_Selectable = false; + Dir_Mode = false; + Hide_records = false; + selected = 0; + selections.clear(); + + x = g_settings.screen_StartX + 20; + y = g_settings.screen_StartY + 20; + + width = (g_settings.screen_EndX - g_settings.screen_StartX - 40); + height = (g_settings.screen_EndY - g_settings.screen_StartY - 40); + + theight = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getHeight(); + foheight = 30; + + liststart = 0; + listmaxshow = std::max(1,(int)(height - theight - 2 * foheight)/fheight); + + //recalc height + height = theight + listmaxshow * fheight + 2 * foheight; + + m_SMSKeyInput.setTimeout(SMSKEY_TIMEOUT); +} + +//------------------------------------------------------------------------ + + +CFileBrowser::~CFileBrowser() +{ +} + +//------------------------------------------------------------------------ + +CFile *CFileBrowser::getSelectedFile() +{ + if ((!(filelist.empty())) && (!(filelist[selected].Name.empty()))) + return &filelist[selected]; + else + return NULL; +} + +//------------------------------------------------------------------------ + +void CFileBrowser::ChangeDir(const std::string & filename, int selection) +{ + std::string newpath; + if((m_Mode != ModeSC) && (filename == "..")) + { + std::string::size_type pos = Path.substr(0,Path.length()-1).rfind('/'); + +#ifdef ENABLE_MOVIEPLAYER_VLC + bool is_vlc = (strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) == 0); +#endif + if (pos == std::string::npos) + { + newpath = Path; + } + else + { +#ifdef ENABLE_MOVIEPLAYER_VLC + if (is_vlc && (pos < strlen(VLC_URI) - 1)) + newpath = VLC_URI; + else +#endif + newpath = Path.substr(0, pos + 1); + } + +#ifdef ENABLE_MOVIEPLAYER_VLC + if (strncmp(is_vlc ? &(newpath.c_str()[strlen(VLC_URI)]) : newpath.c_str(), base.c_str(), base.length()) != 0) + return; +#endif + } + else + { + newpath=filename; + } + if(m_Mode != ModeSC && (newpath.rfind('/') != newpath.length()-1 || newpath.length() == 0)) + { + newpath += '/'; + } + filelist.clear(); + Path = newpath; + name = newpath; + CFileList allfiles; + readDir(newpath, &allfiles); + // filter + CFileList::iterator file = allfiles.begin(); + for(; file != allfiles.end() ; file++) + { + if(Filter != NULL && (!S_ISDIR(file->Mode)) && use_filter) + { + if(!Filter->matchFilter(file->Name)) + { + continue; + } + if(Hide_records) { + int ext_pos = file->Name.rfind('.'); + if( ext_pos > 0) { + std::string extension = file->Name.substr(ext_pos + 1, name.length() - ext_pos); + if(strcasecmp(extension.c_str(), "ts") == 0) { + std::string fname = file->Name.substr(0, ext_pos) + ".xml"; + if(access(fname.c_str(), F_OK) == 0) + continue; + } + } + } + } + if(Dir_Mode && (!S_ISDIR(file->Mode))) + { + continue; + } + filelist.push_back(*file); + } + // sort result + sort(filelist.begin(), filelist.end(), sortBy[g_settings.filebrowser_sortmethod]); + + selected = 0; + if ((selection != -1) && (selection < (int)filelist.size())) + selected = selection; + paintHead(); + paint(); +} + +//------------------------------------------------------------------------ +bool CFileBrowser::readDir(const std::string & dirname, CFileList* flist) +{ + bool ret; + +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) { + ret = readDir_sc(dirname, flist); + } + else +#endif +#ifdef ENABLE_MOVIEPLAYER_VLC + if (strncmp(dirname.c_str(), VLC_URI, strlen(VLC_URI)) == 0) + { + ret = readDir_vlc(dirname, flist); + } + else +#endif + { + ret = readDir_std(dirname, flist); + } + return ret; +} + +#ifdef ENABLE_MOVIEPLAYER_VLC +bool CFileBrowser::readDir_vlc(const std::string & dirname, CFileList* flist) +{ +// printf("readDir_vlc %s\n",dirname.c_str()); + std::string answer=""; + char *dir_escaped = curl_escape(dirname.substr(strlen(VLC_URI)).c_str(), 0); + std::string url = m_baseurl; + url += dir_escaped; + curl_free(dir_escaped); + std::cout << "[FileBrowser] vlc URL: " << url << std::endl; + CURL *curl_handle; + CURLcode httpres; + /* init the curl session */ + curl_handle = curl_easy_init(); + /* timeout. 15 seconds should be enough */ + curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 15); + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, CurlWriteToString); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_FILE, (void *)&answer); + /* Generate error if http error >= 400 occurs */ + curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1); + /* error handling */ + char error[CURL_ERROR_SIZE]; + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error); + /* get it! */ + httpres = curl_easy_perform(curl_handle); + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + // std::cout << "Answer:" << std::endl << "----------------" << std::endl << answer << std::endl; + + if (!answer.empty() && httpres == 0) + { + xmlDocPtr answer_parser = parseXml(answer.c_str()); + + if (answer_parser != NULL) { + xmlNodePtr element = xmlDocGetRootElement(answer_parser); + element = element->xmlChildrenNode; + char *ptr; + if (element == NULL) { + printf("[FileBrowser] vlc: Drive is not readable. Possibly no disc inserted\n"); + CFile file; + file.Mode = S_IFDIR + 0777 ; + file.Name = dirname + ".."; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + } else { + while (element) { + CFile file; + ptr = xmlGetAttribute(element, (char *) "type"); + if (strcmp(ptr, "directory")==0) + file.Mode = S_IFDIR + 0777 ; + else + file.Mode = S_IFREG + 0777 ; + + file.Name = dirname + xmlGetAttribute(element, (char *) "name"); + ptr = xmlGetAttribute(element, (char *) "size"); + if (ptr) + file.Size = atoi(ptr); + else + file.Size = 0; + file.Time = 0; + + element = element->xmlNextNode; + flist->push_back(file); + } + } + xmlFreeDoc(answer_parser); + return true; + } + } + + /* since all CURL error messages use only US-ASCII characters, when can safely print them as if they were UTF-8 encoded */ + if (httpres == 22) { + strcat(error, "\nProbably wrong vlc version\nPlease use vlc 0.8.5 or higher"); + } + DisplayErrorMessage(error); // UTF-8 + CFile file; + + file.Name = dirname + ".."; + file.Mode = S_IFDIR + 0777; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + + return false; +} +#endif /* ENABLE_MOVIEPLAYER_VLC */ + +#ifdef ENABLE_INTERNETRADIO +bool CFileBrowser::readDir_sc(const std::string & dirname, CFileList* flist) +{ +#define GET_SHOUTCAST_TIMEOUT 60 +/* how the shoutcast xml interfaces looks/works: +1st step: get http://www.shoutcast.com/sbin/newxml.phtml +example answer: + + +... + + + + += 400 occurs */ + curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1); + /* set timeout to 30 seconds */ + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, GET_SHOUTCAST_TIMEOUT); + + /* error handling */ + char error[CURL_ERROR_SIZE]; + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error); + /* get it! */ + httpres = curl_easy_perform(curl_handle); + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + //std::cout << "Answer:" << std::endl << "----------------" << std::endl << answer << std::endl; + + if (!answer.empty() && httpres == 0) + { +printf("CFileBrowser::readDir_sc: read done, size %d\n", answer.size()); + xmlDocPtr answer_parser = parseXml(answer.c_str()); + + if (answer_parser != NULL) { + char *ptr; + unsigned char xml_decode = 0; + xmlNodePtr element = xmlDocGetRootElement(answer_parser); + + if (strcmp(xmlGetName(element), "genrelist") == 0) + xml_decode = 1; + else if (strcmp(xmlGetName(element), "stationlist") == 0) + xml_decode = 2; + element = element->xmlChildrenNode; + + if (element == NULL) { + printf("[FileBrowser] SC: Directory cannot be read.\n"); + CFile file; + file.Mode = S_IFDIR + 0777 ; + file.Name = dirname + ".."; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + } else { + char * tunein_base = NULL; + + if (xml_decode == 1) { + CFile file; + file.Mode = S_IFDIR + 0777 ; + file.Name = " Top500"; // use space to have it at the beginning of the list + file.Url = "/sbin/newxml.phtml?genre=Top500"; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + } else if (xml_decode == 2) { + CFile file2; + file2.Mode = S_IFDIR + 0777 ; + file2.Name = ".."; + file2.Url = "/sbin/newxml.phtml"; + file2.Size = 0; + file2.Time = 0; + flist->push_back(file2); + } + while (element) { + CFile file; + if (xml_decode == 1) { + file.Mode = S_IFDIR + 0777 ; + file.Name = xmlGetAttribute(element, (char *) "name"); + file.Url = "/sbin/newxml.phtml?genre=" + file.Name; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + } + else if (xml_decode == 2) { + ptr = xmlGetName(element); + if (ptr != NULL) { + if (strcmp(ptr, "tunein")==0) { + ptr = xmlGetAttribute(element, (char *) "base"); + if (ptr) + tunein_base = ptr; + } else if (strcmp(ptr, "station")==0) { + ptr = xmlGetAttribute(element, (char *) "mt"); + if (ptr && (strcmp(ptr, "audio/mpeg")==0)) { + file.Mode = S_IFREG + 0777 ; + file.Name = xmlGetAttribute(element, (char *) "name"); + file.Url = base + tunein_base + (std::string)"?id=" + xmlGetAttribute(element, (char *) "id"); + //printf("adding %s (%s)\n", file.Name.c_str(), file.Url.c_str()); + ptr = xmlGetAttribute(element, (char *) "br"); + if (ptr) { + file.Size = atoi(ptr); + file.Time = atoi(ptr); + } else { + file.Size = 0; + file.Time = 0; + } + flist->push_back(file); + } + } + } + } + element = element->xmlNextNode; + } + } + xmlFreeDoc(answer_parser); + return true; + } + } + + /* since all CURL error messages use only US-ASCII characters, when can safely print them as if they were UTF-8 encoded */ + if (httpres == 22) { + strcat(error, "\nProbably wrong link."); + } +printf("CFileBrowser::readDir_sc: httpres %d error, %s\n", httpres, error); + DisplayErrorMessage(error); // UTF-8 + CFile file; + + file.Name = dirname + ".."; + file.Mode = S_IFDIR + 0777; + file.Size = 0; + file.Time = 0; + flist->push_back(file); + + return false; +} +#endif + +bool CFileBrowser::readDir_std(const std::string & dirname, CFileList* flist) +{ +// printf("readDir_std %s\n",dirname.c_str()); + stat_struct statbuf; + dirent_struct **namelist; + int n; + + n = my_scandir(dirname.c_str(), &namelist, 0, my_alphasort); + if (n < 0) + { + perror(("Filebrowser scandir: "+dirname).c_str()); + return false; + } + for(int i = 0; i < n;i++) + { + CFile file; + if(strcmp(namelist[i]->d_name,".") != 0) + { + file.Name = dirname + namelist[i]->d_name; + +// printf("file.Name: '%s', getFileName: '%s' getPath: '%s'\n",file.Name.c_str(),file.getFileName().c_str(),file.getPath().c_str()); + if(my_stat((file.Name).c_str(),&statbuf) != 0) + perror("stat error"); + else + { + file.Mode = statbuf.st_mode; + file.Size = statbuf.st_size; + file.Time = statbuf.st_mtime; + flist->push_back(file); + } + } + free(namelist[i]); + } + + free(namelist); + + return true; +} + +//------------------------------------------------------------------------ + +bool CFileBrowser::exec(const char * const dirname) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + bool res = false; + +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) { + m_baseurl = base; + } else +#endif + { + m_baseurl = "http://" + g_settings.streaming_server_ip + ':' + + g_settings.streaming_server_port + "/requests/browse.xml?dir="; + } + name = dirname; + std::replace(name.begin(), name.end(), '\\', '/'); + + paintHead(); + ChangeDir(name); + paint(); + paintFoot(); + + int oldselected = selected; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]); + + if(!CRCInput::isNumeric(msg)) + { + m_SMSKeyInput.resetOldKey(); + } + + if (msg == CRCInput::RC_yellow) + { + if ((Multi_Select) && (selected < filelist.size())) + { + if(filelist[selected].getFileName() != "..") + { + if( (S_ISDIR(filelist[selected].Mode) && Dirs_Selectable) || !S_ISDIR(filelist[selected].Mode) ) + { + filelist[selected].Marked = !filelist[selected].Marked; + paintItem(selected - liststart); + } + } + msg_repeatok = CRCInput::RC_down; // jump to next item + } + } + + if ((msg == CRCInput::RC_red) || msg == CRCInput::RC_page_down) + { + selected += listmaxshow; + if (selected >= filelist.size()) { + if (((filelist.size() / listmaxshow) + 1) * listmaxshow == filelist.size() + listmaxshow) // last page has full entries + selected = 0; + else + selected = selected < (((filelist.size() / listmaxshow) + 1) * listmaxshow) ? (filelist.size() - 1) : 0; + } + liststart = (selected / listmaxshow) * listmaxshow; + paint(); + } + else if ((msg == CRCInput::RC_green) || (msg == CRCInput::RC_page_up) ) + { + if ((int(selected)-int(listmaxshow))<0) + selected=filelist.size()-1; + else + selected -= listmaxshow; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + } + else if (msg_repeatok == CRCInput::RC_up) + { + int prevselected=selected; + if(selected==0) + { + selected = filelist.size()-1; + } + else + selected--; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if (msg_repeatok == CRCInput::RC_down) + { + if (!(filelist.empty())) + { + int prevselected=selected; + selected = (selected + 1) % filelist.size(); + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + } + } + else if ( ( msg == CRCInput::RC_timeout ) ) + { + selected = oldselected; + loop=false; + } + else if ( msg == CRCInput::RC_right ) + { + if (!(filelist.empty())) + { + if (S_ISDIR(filelist[selected].Mode)) + { +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) { + ChangeDir(filelist[selected].Url); + } else +#endif + { + if (filelist[selected].getFileName() != "..") { + selections.push_back(selected); + ChangeDir(filelist[selected].Name); + } + } + } + } + } + else if ( msg == CRCInput::RC_left ) + { +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) + { + for(unsigned int i = 0; i < filelist.size();i++) { + if (S_ISDIR(filelist[i].Mode) && filelist[i].getFileName() == "..") { + ChangeDir(filelist[i].Url); + break; + } + } + } + else +#endif + if (selections.size() > 0) + { + ChangeDir("..",selections.back()); + selections.pop_back(); + } else + { + ChangeDir(".."); + } + } + else if ( msg == CRCInput::RC_blue ) + { + if(Filter != NULL) + { + use_filter = !use_filter; + paintFoot(); + ChangeDir(Path); + } + } + else if ( msg == CRCInput::RC_home ) + { + loop = false; + } + else if ( msg == CRCInput::RC_spkr && strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) != 0) //Not in vlc mode + { + if(".." !=(filelist[selected].getFileName().substr(0,2))) // do not delete that + { + std::stringstream _msg; + _msg << g_Locale->getText(LOCALE_FILEBROWSER_DODELETE1) << " "; + if (filelist[selected].getFileName().length() > 25) + { + _msg << filelist[selected].getFileName().substr(0, 25) << "..."; + } + else + _msg << filelist[selected].getFileName(); + + _msg << " " << g_Locale->getText(LOCALE_FILEBROWSER_DODELETE2); + if (ShowMsgUTF(LOCALE_FILEBROWSER_DELETE, _msg.str(), CMessageBox::mbrNo, CMessageBox::mbYes|CMessageBox::mbNo)==CMessageBox::mbrYes) + { + recursiveDelete(filelist[selected].Name.c_str()); + if(".ts" ==(filelist[selected].getFileName().substr(filelist[selected].getFileName().length()-3,filelist[selected].getFileName().length())))//if bla.ts + { + recursiveDelete((filelist[selected].Name.substr(0,filelist[selected].Name.length()-7)+".xml").c_str());//remove bla.xml von bla.ts + } + ChangeDir(Path); + + } + } + } + else if (msg == CRCInput::RC_ok) + { + if (!(filelist.empty())) + { + if (filelist[selected].getFileName() == "..") + { +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) + ChangeDir(filelist[selected].Url); + else +#endif + { + if (selections.size() > 0) + { + ChangeDir("..",selections.back()); + selections.pop_back(); + } else + { + std::string::size_type pos = Path.substr(0,Path.length()-1).rfind('/'); + if (pos != std::string::npos) { + ChangeDir(".."); + } + else { + loop = false; + res = true; + filelist[selected].Name = "/"; + } + } + } + } + else + { + std::string filename = filelist[selected].Name; + if ( filename.length() > 1 ) + { + if((!Multi_Select) && S_ISDIR(filelist[selected].Mode) && !Dir_Mode) + { +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) + ChangeDir(filelist[selected].Url); + else +#endif + ChangeDir(filelist[selected].Name); + } + else + { + filelist[selected].Marked = true; + loop = false; + res = true; + } + } + } + } + } + else if (msg==CRCInput::RC_help) + { + if (++g_settings.filebrowser_sortmethod >= FILEBROWSER_NUMBER_OF_SORT_VARIANTS) + g_settings.filebrowser_sortmethod = 0; + + sort(filelist.begin(), filelist.end(), sortBy[g_settings.filebrowser_sortmethod]); + + paint(); + paintFoot(); + } + else if (CRCInput::isNumeric(msg_repeatok)) + { + if (!(filelist.empty())) + SMSInput(msg_repeatok); + } + else + { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + } + } + } + + hide(); + + selected_filelist.clear(); + + if(res && Multi_Select) + { + CProgressWindow * progress = new CProgressWindow(); + progress->setTitle(LOCALE_FILEBROWSER_SCAN); + progress->exec(NULL,""); + for(unsigned int i = 0; i < filelist.size();i++) + if(filelist[i].Marked) + { + if(S_ISDIR(filelist[i].Mode)) { +#ifdef ENABLE_INTERNETRADIO + if (m_Mode == ModeSC) + addRecursiveDir(&selected_filelist,filelist[i].Url, true, progress); + else +#endif + addRecursiveDir(&selected_filelist,filelist[i].Name, true, progress); + } else + selected_filelist.push_back(filelist[i]); + } + progress->hide(); + delete progress; + } + + return res; +} + +//------------------------------------------------------------------------ + +void CFileBrowser::addRecursiveDir(CFileList * re_filelist, std::string rpath, bool bRootCall, CProgressWindow * progress) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int n; + + //printf("addRecursiveDir %s\n",rpath.c_str()); + + if (bRootCall) bCancel=false; + + g_RCInput->getMsg_us(&msg, &data, 1); + if (msg==CRCInput::RC_home) + { + // home key cancel scan + bCancel=true; + } + else if (msg!=CRCInput::RC_timeout) + { + // other event, save to low priority queue + g_RCInput->postMsg( msg, data, false ); + } + if(bCancel) + return; + + if ((m_Mode != ModeSC) && ((rpath.empty()) || ((*rpath.rbegin()) != '/'))) + { + rpath += '/'; + } + + CFileList tmplist; + if(!readDir(rpath, &tmplist)) + { + perror(("Recursive scandir: "+rpath).c_str()); + } + else + { + n = tmplist.size(); + if(progress) + { + progress->showStatusMessageUTF(FILESYSTEM_ENCODING_TO_UTF8_STRING(rpath)); + } + for(int i = 0; i < n;i++) + { + if(progress) + { + progress->showGlobalStatus(100/n*i); + } + std::string basename = tmplist[i].Name.substr(tmplist[i].Name.rfind('/')+1); + if( basename != ".." ) + { + if(Filter != NULL && (!S_ISDIR(tmplist[i].Mode)) && use_filter) + { + if(!Filter->matchFilter(tmplist[i].Name)) + { + continue; + } + } + if(!S_ISDIR(tmplist[i].Mode)) + re_filelist->push_back(tmplist[i]); + else + addRecursiveDir(re_filelist,tmplist[i].Name, false, progress); + } + } + } +} + + +//------------------------------------------------------------------------ + +void CFileBrowser::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +//------------------------------------------------------------------------ + +void CFileBrowser::paintItem(unsigned int pos) +{ + int colwidth1, colwidth2, colwidth3, colwidth1_dir, colwidth2_dir; + int c_rad_small; + uint8_t color; + fb_pixel_t bgcolor; + int ypos = y+ theight+0 + pos*fheight; + CFile * actual_file = NULL; + std::string fileicon; + + colwidth2_dir = 180; + colwidth1_dir = width - 35 - colwidth2_dir - 10; + + if (liststart + pos == selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + c_rad_small = RADIUS_SMALL; +// paintFoot(); + } + else + { + color = COL_MENUCONTENT;//DARK; + bgcolor = COL_MENUCONTENT_PLUS_0;//DARK; + c_rad_small = 0; + } + + if( (liststart + pos) < filelist.size() ) + { + actual_file = &filelist[liststart+pos]; + if (actual_file->Marked) + { + color = COL_MENUCONTENTINACTIVE; //+= 2; FIXME + bgcolor = (liststart + pos == selected) ? COL_MENUCONTENTSELECTED_PLUS_2 : COL_MENUCONTENT_PLUS_2; + } + + if (g_settings.filebrowser_showrights == 0 && S_ISREG(actual_file->Mode)) + { + colwidth2 = 0; + colwidth3 = 90; + } + else + { + colwidth2 = 90; + colwidth3 = 90; + } + colwidth1 = width - 35 - colwidth2 - colwidth3 - 10; + + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, c_rad_small); + + if ( actual_file->Name.length() > 0 ) + { + if (liststart+pos==selected) + CVFD::getInstance()->showMenuText(0, FILESYSTEM_ENCODING_TO_UTF8_STRING(actual_file->getFileName()).c_str(), -1, true); // UTF-8 + + switch(actual_file->getType()) + { + case CFile::FILE_CDR: + case CFile::FILE_MP3: + case CFile::FILE_OGG: + case CFile::FILE_WAV: +#ifdef ENABLE_FLAC + case CFile::FILE_FLAC: +#endif + fileicon = "mp3.raw"; +// color = COL_MENUCONTENT; + break; + + case CFile::FILE_DIR: + fileicon = "folder.raw"; + break; + + case CFile::FILE_PICTURE: + case CFile::FILE_TEXT: + default: + fileicon = "file.raw"; + } + frameBuffer->paintIcon(fileicon, x+5 , ypos + (fheight-16) / 2 ); + + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35, ypos + fheight, colwidth1 - 10 , FILESYSTEM_ENCODING_TO_UTF8_STRING(actual_file->getFileName()), color, 0, true); // UTF-8 + + if( S_ISREG(actual_file->Mode) ) + { + if (g_settings.filebrowser_showrights != 0) + { + const char * attribute = "xwr"; + char modestring[9 + 1]; + for (int m = 8; m >= 0; m--) + { + modestring[8 - m] = (actual_file->Mode & (1 << m)) ? attribute[m % 3] : '-'; + } + modestring[9] = 0; + + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1 , ypos+ fheight, colwidth2 - 10, modestring, color, 0, true); // UTF-8 + } + +#define GIGABYTE 1073741824LL +#define MEGABYTE 1048576LL +#define KILOBYTE 1024LL + char tmpstr[256]; + const char *unit = ""; + long long factor = 0; + if (actual_file->Size >= GIGABYTE) + { + factor = GIGABYTE; + unit = "G"; + } + else if (actual_file->Size >= MEGABYTE) + { + factor = MEGABYTE; + unit = "M"; + } + else if (actual_file->Size >= KILOBYTE) + { + factor = KILOBYTE; + unit = "k"; + } + if (factor) + { + int a = actual_file->Size / factor; + int b = (actual_file->Size - a * factor) * 1000 / factor; + snprintf(tmpstr, sizeof(tmpstr), "%d.%03d%s", a, b, unit); + } + else + snprintf(tmpstr,sizeof(tmpstr),"%d", (int)actual_file->Size); + + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1 + colwidth2, ypos+ fheight, colwidth3 - 10, tmpstr, color); + } + + if( S_ISDIR(actual_file->Mode) ) + { + char timestring[18]; + time_t rawtime; + + rawtime = actual_file->Time; + strftime(timestring, 18, "%d-%m-%Y %H:%M", gmtime(&rawtime)); + + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1_dir, ypos+ fheight, colwidth2_dir - 10, timestring, color); + } + } + } + else + frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0/*DARK*/); +} + +//------------------------------------------------------------------------ + +void CFileBrowser::paintHead() +{ + char l_name[100]; + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, RADIUS_MID, CORNER_TOP); +#ifdef ENABLE_INTERNETRADIO + if(m_Mode == ModeSC) + snprintf(l_name, sizeof(l_name), "%s %s", g_Locale->getText(LOCALE_AUDIOPLAYER_ADD_SC), FILESYSTEM_ENCODING_TO_UTF8_STRING(name).c_str()); // UTF-8 + else +#endif + snprintf(l_name, sizeof(l_name), "%s %s", g_Locale->getText(LOCALE_FILEBROWSER_HEAD), FILESYSTEM_ENCODING_TO_UTF8_STRING(name).c_str()); // UTF-8 + + g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->RenderString(x+10,y+theight+1, width-11, l_name, COL_MENUHEAD, 0, true); // UTF-8 +} + +//------------------------------------------------------------------------ + +const struct button_label FileBrowserButtons[3] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_FILEBROWSER_NEXTPAGE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_FILEBROWSER_PREVPAGE }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_FILEBROWSER_MARK }, +}; +const struct button_label FileBrowserFilterButton[2] = +{ + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_FILEBROWSER_FILTER_INACTIVE }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_FILEBROWSER_FILTER_ACTIVE }, +}; + +void CFileBrowser::paintFoot() +{ + int dx = (width-20) / 4; + //Second Line (bottom, top) + int by2 = y + height - (foheight - 4); + int ty2 = by2 + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight(); + + //Background + frameBuffer->paintBoxRel(x, y + height - (2 * foheight ), width, (2 * foheight ), COL_INFOBAR_SHADOW_PLUS_1, RADIUS_MID, CORNER_BOTTOM); + + if (!(filelist.empty())) + { + int by = y + height - 2 * (foheight - 4); + + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, by, dx, Multi_Select ? 3 : 2, FileBrowserButtons); + + if(Filter != NULL) + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10 + (3 * dx), by, dx, 1, &(FileBrowserFilterButton[use_filter?0:1])); + + //OK-Button + if( (filelist[selected].getType() != CFile::FILE_UNKNOWN) || (S_ISDIR(filelist[selected].Mode)) ) + { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x +3 , by2 - 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + 35, ty2, dx - 35, g_Locale->getText(LOCALE_FILEBROWSER_SELECT), COL_INFOBAR /*_SHADOW_PLUS_1*/, 0, true); // UTF-8 + + } + + //?-Button + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x + (1 * dx), by2 - 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + 35 + (1 * dx), ty2, dx - 35, g_Locale->getText(sortByNames[g_settings.filebrowser_sortmethod]), COL_INFOBAR /*_SHADOW_PLUS_1*/, 0, true); // UTF-8 + + //Mute-Button + if (strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) != 0) { //Not in vlc mode + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_MUTE_SMALL, x + (2 * dx), by2 - 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + 35 + (2 * dx), ty2, dx - 35, g_Locale->getText(LOCALE_FILEBROWSER_DELETE), COL_INFOBAR /*_SHADOW_PLUS_1*/, 0, true); // UTF-8 + } + + if(m_SMSKeyInput.getOldKey()!=0) + { + char cKey[2]={m_SMSKeyInput.getOldKey(),0}; + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + width - 16, by2 , 16, cKey, COL_MENUHEAD, 0, true); // UTF-8 + } + } +} + +//------------------------------------------------------------------------ + +void CFileBrowser::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + +// if (filelist[0].Name.length() != 0) +// frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 ); + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_FILEBROWSER_HEAD)); + + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((filelist.size()- 1)/ listmaxshow)+ 1; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ sbs*(sb-4)/sbc, 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3, RADIUS_SMALL); +} + +//------------------------------------------------------------------------ + +void CFileBrowser::SMSInput(const neutrino_msg_t msg) +{ + unsigned char key = m_SMSKeyInput.handleMsg(msg); + + unsigned int i; + for(i=(selected+1) % filelist.size(); i != selected ; i= (i+1) % filelist.size()) + { + if(tolower(filelist[i].getFileName()[0]) == key) + { + break; + } + } + int prevselected=selected; + selected=i; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } +} + +//------------------------------------------------------------------------ + +void CFileBrowser::recursiveDelete(const char* file) +{ + stat_struct statbuf; + dirent_struct **namelist; + int n; + printf("Delete %s\n", file); + if(my_lstat(file,&statbuf) == 0) + { + if(S_ISDIR(statbuf.st_mode)) + { + n = my_scandir(file, &namelist, 0, my_alphasort); + while(n--) + { + if(strcmp(namelist[n]->d_name, ".")!=0 && strcmp(namelist[n]->d_name, "..")!=0) + { + std::string fullname = (std::string)file + "/" + namelist[n]->d_name; + recursiveDelete(fullname.c_str()); + } + free(namelist[n]); + } + free(namelist); + rmdir(file); + } + else + { + unlink(file); + } + } + else + perror(file); +} diff --git a/src/gui/filebrowser.h b/src/gui/filebrowser.h new file mode 100644 index 000000000..6a8497ce4 --- /dev/null +++ b/src/gui/filebrowser.h @@ -0,0 +1,233 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __FILEBROWSER_HPP__ +#define __FILEBROWSER_HPP__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#define ENABLE_INTERNETRADIO +#define VLC_URI "vlc://" + +/** + * Converts input of numeric keys to SMS style char input. + */ +class SMSKeyInput +{ + // time since last input + timeval m_oldKeyTime; + + // last key input + unsigned char m_oldKey; + + // keypresses within this period are taken as a sequence + int m_timeout; +public: + SMSKeyInput(); + + /** + * Returns the SMS char calculated with respect to the new input. + * @param msg the current RC input + * @return the calculated SMS char + */ + unsigned char handleMsg(const neutrino_msg_t msg); + + /** + * Resets the key history which is needed for proper calculation + * of the SMS char by #handleMsg(neutrino_msg_t) + */ + void resetOldKey(); + + /** + * @return the last key calculated by #handleMsg(neutrino_msg_t) + */ + unsigned char getOldKey() const; + + /** + * Returns time of last key push. + * resolution: usecs + */ + const timeval* getOldKeyTime() const; + + /** + * Returns time of last key push. + * resolution: seconds + */ + time_t getOldKeyTimeSec() const; + + int getTimeout() const; + + /** + * Sets the timeout. + * @param timeout keypresses within this period are taken as a + * sequence. unit: msecs + */ + void setTimeout(int timeout); +}; +//------------------------------------------------------------------------ + + +class CFileFilter +{ + std::vector Filter; +public: + void addFilter(const std::string & filter){Filter.push_back(filter);}; + bool matchFilter(const std::string & name) + { + int ext_pos = 0; + ext_pos = name.rfind('.'); + if( ext_pos > 0) + { + std::string extension; + extension = name.substr(ext_pos + 1, name.length() - ext_pos); + for(unsigned int i = 0; i < Filter.size();i++) + if(strcasecmp(Filter[i].c_str(),extension.c_str()) == 0) + return true; + } + return false; + }; + void Clear(void) { Filter.clear();}; +}; +//------------------------------------------------------------------------ + +#define FILEBROWSER_NUMBER_OF_SORT_VARIANTS 5 + +class CFileBrowser +{ + private: + CFrameBuffer *frameBuffer; + + CFileList selected_filelist; + bool readDir(const std::string & dirname, CFileList* flist); +#ifdef ENABLE_MOVIEPLAYER_VLC + bool readDir_vlc(const std::string & dirname, CFileList* flist); +#endif + bool readDir_std(const std::string & dirname, CFileList* flist); +#ifdef ENABLE_INTERNETRADIO + bool readDir_sc(const std::string & dirname, CFileList* flist); +#endif + void addRecursiveDir(CFileList * re_filelist, std::string path, bool bRootCall, CProgressWindow * progress = NULL); + void SMSInput(const neutrino_msg_t msg); + + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + std::vector selections; + + int fheight; // Fonthoehe Filelist-Inhalt + int theight; // Fonthoehe Filelist-Titel + int foheight; // Hoehe der button leiste + std::string name; + std::string base; + std::string m_baseurl; + int width; + int height; + bool use_filter; + bool bCancel; + + int x; + int y; + + SMSKeyInput m_SMSKeyInput; + + void paintItem(unsigned pos); + void paint(); + void paintHead(); + void paintFoot(); + void recursiveDelete(const char* file); + + protected: + void commonInit(); + + public: + CFileList filelist; + + typedef enum { + ModeFile, + ModeVLC, + ModeSC + } tFileBrowserMode; + + /** + * @param selection select the specified entry, ignored if selection == -1 + */ + void ChangeDir(const std::string & filename, int selection = -1); + void hide(); + + std::string Path; + bool Multi_Select; + bool Dirs_Selectable; + bool Dir_Mode; + bool Hide_records; + CFileFilter * Filter; + + CFileBrowser(); + CFileBrowser(const char * const _base, const tFileBrowserMode mode = ModeFile); + ~CFileBrowser(); + + bool exec(const char * const dirname); + CFile *getSelectedFile(); + + inline const CFileList & getSelectedFiles(void) const + { + return selected_filelist; + } + + inline const std::string & getCurrentDir(void) const + { + return Path; + } + +// size_t CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data); + private: + tFileBrowserMode m_Mode; +}; + +#endif diff --git a/src/gui/hdd_menu.cpp b/src/gui/hdd_menu.cpp new file mode 100644 index 000000000..9d587f6a6 --- /dev/null +++ b/src/gui/hdd_menu.cpp @@ -0,0 +1,458 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gui/widget/menue.h" +#include "gui/widget/stringinput.h" +#include "gui/widget/messagebox.h" +#include "gui/widget/hintbox.h" +#include "gui/widget/progresswindow.h" + +#include "system/setting_helpers.h" +#include "system/settings.h" +#include "system/debug.h" + +#include +#include + +int safe_mkdir(char * path); + +static int my_filter(const struct dirent * dent) +{ + if(dent->d_name[0] == 's' && dent->d_name[1] == 'd') + return 1; + return 0; +} + +int CHDDMenuHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + if (parent) + parent->hide(); + + return doMenu (); +} + +int CHDDMenuHandler::doMenu () +{ + FILE * f; + int fd; + struct dirent **namelist; + int ret; + + bool hdd_found = 0; + int n = scandir("/sys/block", &namelist, my_filter, alphasort); +#if 0 + if(n <= 0) { + //FIXME no hdd found message + return 0; + } +#endif + + CMenuWidget* hddmenu = new CMenuWidget(LOCALE_HDD_SETTINGS, NEUTRINO_ICON_SETTINGS); + hddmenu->addItem( GenericMenuBack ); + hddmenu->addItem( GenericMenuSeparatorLine ); + + hddmenu->addItem( new CMenuOptionChooser(LOCALE_HDD_SLEEP, &g_settings.hdd_sleep, HDD_SLEEP_OPTIONS, HDD_SLEEP_OPTION_COUNT, true)); + hddmenu->addItem( new CMenuOptionChooser(LOCALE_HDD_NOISE, &g_settings.hdd_noise, HDD_NOISE_OPTIONS, HDD_NOISE_OPTION_COUNT, true)); + + hddmenu->addItem(new CMenuForwarder(LOCALE_HDD_ACTIVATE, true, "", new CHDDDestExec())); + + //if(n > 0) + hddmenu->addItem( GenericMenuSeparatorLine ); + + for(int i = 0; i < n;i++) { + char str[256]; + char vendor[128], model[128]; + int64_t bytes; + int64_t megabytes; + int removable = 0; + + printf("HDD: checking /sys/block/%s\n", namelist[i]->d_name); + sprintf(str, "/dev/%s", namelist[i]->d_name); + fd = open(str, O_RDONLY); + if(fd < 0) { + printf("Cant open %s\n", str); + continue; + } + if (ioctl(fd, BLKGETSIZE64, &bytes)) + perror("BLKGETSIZE64"); + + megabytes = bytes/1000000; + + sprintf(str, "/sys/block/%s/device/vendor", namelist[i]->d_name); + f = fopen(str, "r"); + if(!f) { + printf("Cant open %s\n", str); + continue; + } + fscanf(f, "%s", vendor); + fclose(f); + + sprintf(str, "/sys/block/%s/device/model", namelist[i]->d_name); + f = fopen(str, "r"); + if(!f) { + printf("Cant open %s\n", str); + continue; + } + fscanf(f, "%s", model); + fclose(f); + + sprintf(str, "/sys/block/%s/removable", namelist[i]->d_name); + f = fopen(str, "r"); + if(!f) { + printf("Cant open %s\n", str); + continue; + } + fscanf(f, "%d", &removable); + fclose(f); + + sprintf(str, "%s %s (%s-%s %lld %s)\n", g_Locale->getText(LOCALE_HDD_MANAGE), namelist[i]->d_name, vendor, model, megabytes < 10000 ? megabytes : megabytes/1000, megabytes < 10000 ? "MB" : "GB"); + printf("HDD: %s\n", str); + CMenuWidget * tempMenu = new CMenuWidget(str, NEUTRINO_ICON_SETTINGS); + tempMenu->addItem( GenericMenuBack ); + tempMenu->addItem( GenericMenuSeparatorLine ); + //tempMenu->addItem( new CMenuOptionChooser(LOCALE_HDD_FS, &g_settings.hdd_fs, HDD_FILESYS_OPTIONS, HDD_FILESYS_OPTION_COUNT, true)); + tempMenu->addItem(new CMenuForwarder(LOCALE_HDD_FORMAT, true, "", new CHDDFmtExec, namelist[i]->d_name)); + tempMenu->addItem(new CMenuForwarder(LOCALE_HDD_CHECK, true, "", new CHDDChkExec, namelist[i]->d_name)); + hddmenu->addItem(new CMenuForwarderNonLocalized(str, removable ? false : true, NULL, tempMenu)); + hdd_found = 1; + } + if(!hdd_found) + hddmenu->addItem(new CMenuForwarder(LOCALE_HDD_NOT_FOUND, false)); + + ret = hddmenu->exec(NULL, ""); + delete hddmenu; + return ret; +} + +int CHDDDestExec::exec(CMenuTarget* parent, const std::string&) +{ + char cmd[100]; + + printf("CHDDDestExec: noise %d sleep %d\n", g_settings.hdd_noise, g_settings.hdd_sleep); + //FIXME: atm we can have 2 hdd, sata and usb + sprintf(cmd, "hdparm -S%d /dev/sda >/dev/null 2>/dev/null", g_settings.hdd_sleep); + system(cmd); + sprintf(cmd, "hdparm -M%d /dev/sda >/dev/null 2>/dev/null", g_settings.hdd_noise); + system(cmd); + sprintf(cmd, "hdparm -S%d /dev/sdb >/dev/null 2>/dev/null", g_settings.hdd_sleep); + system(cmd); + sprintf(cmd, "hdparm -M%d /dev/sdb >/dev/null 2>/dev/null", g_settings.hdd_noise); + system(cmd); + sprintf(cmd, "hdparm -K /dev/sda >/dev/null 2>/dev/null"); + system(cmd); + sprintf(cmd, "hdparm -K /dev/sdb >/dev/null 2>/dev/null"); + system(cmd); + return 1; +} + +static int check_and_umount(char * dev, char * path) +{ + char buffer[255]; + FILE *f = fopen("/proc/mounts", "r"); + if(f == NULL) + return -1; + while (fgets (buffer, 255, f) != NULL) { + if(strstr(buffer, dev)) { + printf("HDD: %s mounted\n", path); + fclose(f); + return umount(path); + } + } + fclose(f); + printf("HDD: %s not mounted\n", path); + return 0; +} + +int CHDDFmtExec::exec(CMenuTarget* parent, const std::string& key) +{ + char cmd[100]; + CHintBox * hintbox; + int res; + FILE * f; + char src[128], dst[128]; + CProgressWindow * progress; + bool idone; + + sprintf(src, "/dev/%s1", key.c_str()); + sprintf(dst, "/media/%s1", key.c_str()); + + printf("CHDDFmtExec: key %s\n", key.c_str()); + + res = ShowMsgUTF ( LOCALE_HDD_FORMAT, g_Locale->getText(LOCALE_HDD_FORMAT_WARN), CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo ); + if(res != CMessageBox::mbrYes) + return 0; + + bool srun = system("killall -9 smbd"); + + //res = check_and_umount(dst); + res = check_and_umount(src, dst); + printf("CHDDFmtExec: umount res %d\n", res); + + if(res) { + hintbox = new CHintBox(LOCALE_HDD_FORMAT, g_Locale->getText(LOCALE_HDD_UMOUNT_WARN)); + hintbox->paint(); + sleep(2); + delete hintbox; + goto _return; + } + + f = fopen("/proc/sys/kernel/hotplug", "w"); + if(f) { + fprintf(f, "none\n"); + fclose(f); + } + + progress = new CProgressWindow(); + progress->setTitle(LOCALE_HDD_FORMAT); + progress->exec(NULL,""); + progress->showStatusMessageUTF("Executing fdisk"); + progress->showGlobalStatus(0); + + sprintf(cmd, "/sbin/sfdisk -f -uM /dev/%s", key.c_str()); + printf("CHDDFmtExec: executing %s\n", cmd); + f=popen(cmd, "w"); + if (!f) { + hintbox = new CHintBox(LOCALE_HDD_FORMAT, g_Locale->getText(LOCALE_HDD_FORMAT_FAILED)); + hintbox->paint(); + sleep(2); + delete hintbox; + goto _remount; + } + + fprintf(f, "0,\n;\n;\n;\ny\n"); + pclose(f); + //sleep(1); + + switch(g_settings.hdd_fs) { + case 0: + sprintf(cmd, "/sbin/mkfs.ext3 -T largefile -m0 %s", src); + break; + case 1: + sprintf(cmd, "/sbin/mkreiserfs -f -f %s", src); + break; + default: + return 0; + } + + printf("CHDDFmtExec: executing %s\n", cmd); + + f=popen(cmd, "r"); + if (!f) { + hintbox = new CHintBox(LOCALE_HDD_FORMAT, g_Locale->getText(LOCALE_HDD_FORMAT_FAILED)); + hintbox->paint(); + sleep(2); + delete hintbox; + goto _remount; + } + + char buf[256]; + idone = false; + while(fgets(buf, 255, f) != NULL) + { + printf("%s", buf); + if(!idone && strncmp(buf, "Writing inode", 13)) { + idone = true; + buf[21] = 0; + progress->showGlobalStatus(20); + progress->showStatusMessageUTF(buf); + } + else if(strncmp(buf, "Creating", 8)) { + progress->showGlobalStatus(40); + progress->showStatusMessageUTF(buf); + } + else if(strncmp(buf, "Writing superblocks", 19)) { + progress->showGlobalStatus(60); + progress->showStatusMessageUTF(buf); + } + } + pclose(f); + progress->showGlobalStatus(100); + sleep(2); + + sprintf(cmd, "/sbin/tune2fs -r 0 -c 0 -i 0 %s", src); + printf("CHDDFmtExec: executing %s\n", cmd); + system(cmd); + +_remount: + progress->hide(); + delete progress; + + switch(g_settings.hdd_fs) { + case 0: + res = mount(src, dst, "ext3", 0, NULL); + break; + case 1: + res = mount(src, dst, "reiserfs", 0, NULL); + break; + default: + break; + } + f = fopen("/proc/sys/kernel/hotplug", "w"); + if(f) { + fprintf(f, "/sbin/hotplug\n"); + fclose(f); + } + + if(!res) { + sprintf(cmd, "%s/movies", dst); + safe_mkdir((char *) cmd); + sprintf(cmd, "%s/pictures", dst); + safe_mkdir((char *) cmd); + sprintf(cmd, "%s/epg", dst); + safe_mkdir((char *) cmd); + sprintf(cmd, "%s/music", dst); + safe_mkdir((char *) cmd); + sync(); + } +_return: + if(!srun) system("smbd"); + return menu_return::RETURN_REPAINT; +} + +int CHDDChkExec::exec(CMenuTarget* parent, const std::string& key) +{ + char cmd[100]; + CHintBox * hintbox; + int res; + char src[128], dst[128]; + FILE * f; + CProgressWindow * progress; + int oldpass = 0, pass, step, total; + int percent = 0, opercent = 0; + + sprintf(src, "/dev/%s1", key.c_str()); + sprintf(dst, "/media/%s1", key.c_str()); + +printf("CHDDChkExec: key %s\n", key.c_str()); + + bool srun = system("killall -9 smbd"); + + //res = check_and_umount(dst); + res = check_and_umount(src, dst); + printf("CHDDChkExec: umount res %d\n", res); + if(res) { + hintbox = new CHintBox(LOCALE_HDD_CHECK, g_Locale->getText(LOCALE_HDD_UMOUNT_WARN)); + hintbox->paint(); + sleep(2); + delete hintbox; + return menu_return::RETURN_REPAINT; + } + + switch(g_settings.hdd_fs) { + case 0: + sprintf(cmd, "/sbin/fsck.ext3 -C 1 -f -y %s", src); + break; + case 1: + sprintf(cmd, "/sbin/reiserfsck --fix-fixable %s", src); + break; + default: + return 0; + } + + printf("CHDDChkExec: Executing %s\n", cmd); + f=popen(cmd, "r"); + if(!f) { + hintbox = new CHintBox(LOCALE_HDD_CHECK, g_Locale->getText(LOCALE_HDD_CHECK_FAILED)); + hintbox->paint(); + sleep(2); + delete hintbox; + goto ret1; + } + + progress = new CProgressWindow(); + progress->setTitle(LOCALE_HDD_CHECK); + progress->exec(NULL,""); + + char buf[256]; + while(fgets(buf, 255, f) != NULL) + { + if(isdigit(buf[0])) { + sscanf(buf, "%d %d %d\n", &pass, &step, &total); + if(total == 0) + total = 1; + if(oldpass != pass) { + oldpass = pass; + progress->showGlobalStatus(pass > 0 ? (pass-1)*20: 0); + } + percent = (step * 100) / total; + if(opercent != percent) { + opercent = percent; +//printf("CHDDChkExec: pass %d : %d\n", pass, percent); + progress->showLocalStatus(percent); + } + } + else if(!strncmp(buf, "Pass", 4)) + progress->showStatusMessageUTF(buf); + } +//printf("CHDDChkExec: %s\n", buf); + pclose(f); + progress->showGlobalStatus(100); + progress->showStatusMessageUTF(buf); + sleep(2); + progress->hide(); + delete progress; + +ret1: + switch(g_settings.hdd_fs) { + case 0: + res = mount(src, dst, "ext3", 0, NULL); + break; + case 1: + res = mount(src, dst, "reiserfs", 0, NULL); + break; + default: + break; + } + printf("CHDDChkExec: mount res %d\n", res); + + if(!srun) system("smbd"); + return menu_return::RETURN_REPAINT; +} diff --git a/src/gui/hdd_menu.h b/src/gui/hdd_menu.h new file mode 100644 index 000000000..36bffde05 --- /dev/null +++ b/src/gui/hdd_menu.h @@ -0,0 +1,55 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __hdd_menu__ +#define __hdd_menu__ + + +#include "widget/menue.h" + +using namespace std; + +class CHDDDestExec : public CMenuTarget +{ +public: + int exec(CMenuTarget* parent, const std::string&); +}; +class CHDDFmtExec : public CMenuTarget +{ +public: + int exec(CMenuTarget* parent, const std::string&); +}; +class CHDDChkExec : public CMenuTarget +{ +public: + int exec(CMenuTarget* parent, const std::string&); +}; + +class CHDDMenuHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + int doMenu(); +}; + +#endif + diff --git a/src/gui/imageinfo.cpp b/src/gui/imageinfo.cpp new file mode 100644 index 000000000..9298f9eb0 --- /dev/null +++ b/src/gui/imageinfo.cpp @@ -0,0 +1,233 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define SCREEN_X 720 +#define SCREEN_Y 572 + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +extern cVideo * videoDecoder; + +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ + +CImageInfo::CImageInfo() +{ + Init(); +} + +void CImageInfo::Init(void) +{ + frameBuffer = CFrameBuffer::getInstance(); + + font_head = SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME;; + font_small = SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL; + font_info = SNeutrinoSettings::FONT_TYPE_MENU; + + hheight = g_Font[font_head]->getHeight(); + iheight = g_Font[font_info]->getHeight(); + sheight = g_Font[font_small]->getHeight(); + + max_width = frameBuffer->getScreenWidth(true); + max_height = frameBuffer->getScreenHeight(true); + + width = frameBuffer->getScreenWidth() - 10; + height = frameBuffer->getScreenHeight() - 10; + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height) / 2) + g_settings.screen_StartY; +} + +CImageInfo::~CImageInfo() +{ + videoDecoder->Pig(-1, -1, -1, -1); +} + +int CImageInfo::exec(CMenuTarget* parent, const std::string &) +{ + if (parent) + parent->hide(); + + width = frameBuffer->getScreenWidth() - 10; + height = frameBuffer->getScreenHeight() - 10; + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height) / 2) + g_settings.screen_StartY; + + paint(); + + //paint_pig( width-170, y, 215, 170); + paint_pig (width - width/3 - 10, y + 10, width/3, height/3); + + neutrino_msg_t msg; + + while (1) + { + neutrino_msg_data_t data; + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd_MS(100); + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if (msg <= CRCInput::RC_MaxRC) + { + break; + } + + if ( msg > CRCInput::RC_MaxRC && msg != CRCInput::RC_timeout) + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + } + + hide(); + + return menu_return::RETURN_REPAINT; +} + +void CImageInfo::hide() +{ + //frameBuffer->paintBackgroundBoxRel(0,0, max_width,max_height); + frameBuffer->paintBackground(); + videoDecoder->Pig(-1, -1, -1, -1); +} + +void CImageInfo::paint_pig(int x, int y, int w, int h) +{ + //frameBuffer->paintBoxRel(x,y,w,h, COL_BACKGROUND); + frameBuffer->paintBackgroundBoxRel(x,y,w,h); + videoDecoder->Pig(x, y, w, h, frameBuffer->getScreenWidth(true), frameBuffer->getScreenHeight(true)); +} + +void CImageInfo::paintLine(int xpos, int font, const char* text) +{ + char buf[100]; + sprintf((char*) buf, "%s", text); + //g_Font[font]->RenderString(xpos, ypos, width-10, buf, COL_MENUCONTENT, 0, true); + g_Font[font]->RenderString(xpos, ypos, width-10, buf, COL_INFOBAR, 0, true); +} + +void CImageInfo::paint() +{ + const char * head_string; + char imagedate[18] = ""; + int xpos = x+10; + + ypos = y+5; + + head_string = g_Locale->getText(LOCALE_IMAGEINFO_HEAD); + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, head_string); + + //frameBuffer->paintBoxRel(0, 0, max_width, max_height, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(0, 0, max_width, max_height, COL_INFOBAR_PLUS_0); + g_Font[font_head]->RenderString(xpos, ypos+ hheight+1, width, head_string, COL_MENUHEAD, 0, true); + + ypos += hheight; + ypos += (iheight >>1); + + + CConfigFile config('\t'); + config.loadConfig("/.version"); + + const char * imagename = config.getString("imagename", "Neutrino").c_str(); + const char * homepage = config.getString("homepage", "n/a").c_str(); + const char * creator = config.getString("creator", "n/a").c_str(); + const char * version = config.getString("version", "no version").c_str(); + const char * docs = config.getString("docs", "man neutrino").c_str(); + const char * forum = config.getString("forum", "http://forum.tuxbox.org").c_str(); + + static CFlashVersionInfo versionInfo(version); + const char * releaseCycle = versionInfo.getReleaseCycle(); + sprintf((char*) imagedate, "%s %s", versionInfo.getDate(), versionInfo.getTime()); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_IMAGE)); + paintLine(xpos+125, font_info, imagename); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_DATE)); + paintLine(xpos+125, font_info, imagedate); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_VERSION)); + paintLine(xpos+125, font_info, releaseCycle); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_CREATOR)); + paintLine(xpos+125, font_info, creator); + + ypos += iheight+15; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_HOMEPAGE)); + paintLine(xpos+125, font_info, homepage); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_DOKUMENTATION)); + paintLine(xpos+125, font_info, docs); + + ypos += iheight; + paintLine(xpos , font_info, g_Locale->getText(LOCALE_IMAGEINFO_FORUM)); + paintLine(xpos+125, font_info, forum); + + ypos += iheight+15; + paintLine(xpos, font_info,g_Locale->getText(LOCALE_IMAGEINFO_LICENSE)); + paintLine(xpos+125, font_small, "This program is free software; you can redistribute it and/or modify"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "it under the terms of the GNU General Public License as published by"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "the Free Software Foundation; either version 2 of the License, or"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "(at your option) any later version."); + + ypos+= sheight+10; + paintLine(xpos+125, font_small, "This program is distributed in the hope that it will be useful,"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "but WITHOUT ANY WARRANTY; without even the implied warranty of"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); + + ypos+= sheight; + paintLine(xpos+125, font_small, "See the GNU General Public License for more details."); + + ypos+= sheight+10; + paintLine(xpos+125, font_small, "You should have received a copy of the GNU General Public License"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "along with this program; if not, write to the Free Software"); + + ypos+= sheight; + paintLine(xpos+125, font_small, "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA."); +} diff --git a/src/gui/imageinfo.h b/src/gui/imageinfo.h new file mode 100644 index 000000000..f5bf95fc2 --- /dev/null +++ b/src/gui/imageinfo.h @@ -0,0 +1,69 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __imageinfo__ +#define __imageinfo__ + +#include + +#include + +#include +#include + +class CImageInfo : public CMenuTarget +{ + private: + void Init(void); + CConfigFile * configfile; + CFrameBuffer *frameBuffer; + int x; + int y; + int ypos; + int width; + int height; + int hheight,iheight,sheight; // head/info/small font height + + int max_height; // Frambuffer 0.. max + int max_width; + + int font_head; + int font_info; + int font_small; + + CPIG *pig; + + void paint(); + void paint_pig(int x, int y, int w, int h); + void paintLine(int xpos, int font, const char* text); + + public: + + CImageInfo(); + ~CImageInfo(); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + +#endif + diff --git a/src/gui/infoclock.cpp b/src/gui/infoclock.cpp new file mode 100644 index 000000000..2f9f11d9c --- /dev/null +++ b/src/gui/infoclock.cpp @@ -0,0 +1,87 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include "infoclock.h" + +#define XOFF 10 +#define YOFF 0 + +CInfoClock::CInfoClock() +{ + frameBuffer = CFrameBuffer::getInstance(); + + time_height = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getHeight(); + int t1 = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth(widest_number); + int t2 = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth(":"); + time_width = t1*6 + t2*2; + thrTimer = 0; +} + +CInfoClock::~CInfoClock() +{ + if(thrTimer) + pthread_cancel(thrTimer); + thrTimer = 0; +} + +void CInfoClock::paintTime( bool show_dot) +{ + int x = g_settings.screen_EndX - XOFF; + int y = g_settings.screen_StartY + YOFF; + + char timestr[10]; + time_t tm; + + tm = time(0); + if(show_dot) + strftime((char*) ×tr, 20, "%H:%M:%S", localtime(&tm)); + else + strftime((char*) ×tr, 20, "%H.%M:%S", localtime(&tm)); + frameBuffer->paintBoxRel(x - time_width - 15, y, time_width, time_height, COL_MENUCONTENT_PLUS_0, 7, 3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString(x - time_width- 10, y+ time_height, time_width, timestr, COL_MENUCONTENT); +} + +void* CInfoClock::TimerProc(void *arg) +{ + + bool show_dot = false; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS,0); + + CInfoClock *InfoClock = (CInfoClock*) arg; + + InfoClock->paintTime(show_dot); + while(1) { + sleep(1); + show_dot = !show_dot; + InfoClock->paintTime(show_dot); + } +} + +void CInfoClock::StartClock() +{ + if(!thrTimer) { + pthread_create (&thrTimer, NULL, TimerProc, (void*) this) ; + pthread_detach(thrTimer); + } +} + +void CInfoClock::StopClock() +{ + int x = g_settings.screen_EndX - XOFF; + int y = g_settings.screen_StartY + YOFF; + if(thrTimer) { + pthread_cancel(thrTimer); + thrTimer = 0; + frameBuffer->paintBackgroundBoxRel(x - time_width - 15, y, time_width, time_height); + } +} diff --git a/src/gui/infoclock.h b/src/gui/infoclock.h new file mode 100644 index 000000000..68fca718e --- /dev/null +++ b/src/gui/infoclock.h @@ -0,0 +1,36 @@ +#ifndef __infoclock__ +#define __infoclock__ + + +#include +#include +#include + +#include "gui/color.h" + +#include + + +class CInfoClock +{ + private: + CFrameBuffer * frameBuffer; + + pthread_t thrTimer; + int time_width; + int time_height; + void paintTime( bool show_dot); + + static void CleanUpProc(void* arg); + static void* TimerProc(void *arg); + + public: + CInfoClock(); + ~CInfoClock(); + + void StartClock(); + void StopClock(); + +}; + +#endif diff --git a/src/gui/infoviewer.cpp b/src/gui/infoviewer.cpp new file mode 100644 index 000000000..93a66287a --- /dev/null +++ b/src/gui/infoviewer.cpp @@ -0,0 +1,1316 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + +void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = ""); +void sectionsd_getCurrentNextServiceKey(t_channel_id uniqueServiceKey, CSectionsdClient::responseGetCurrentNextInfoChannelID& current_next ); + +extern CRemoteControl *g_RemoteControl; /* neutrino.cpp */ +extern CPictureViewer * g_PicViewer; +extern CFrontend * frontend; +extern cVideo * videoDecoder; +extern t_channel_id live_channel_id; //zapit + +#define COL_INFOBAR_BUTTONS (COL_INFOBAR_SHADOW + 1) +#define COL_INFOBAR_BUTTONS_BACKGROUND (COL_INFOBAR_SHADOW_PLUS_1) + +#define ICON_LARGE_WIDTH 26 +#define ICON_SMALL_WIDTH 16 +#define ICON_LARGE 30 +#define ICON_SMALL 18 +#define ICON_Y_1 18 + +#define ICON_OFFSET (2 + ICON_LARGE_WIDTH + 2 + ICON_LARGE_WIDTH + 2 + ICON_SMALL_WIDTH + 2) + +#define BOTTOM_BAR_OFFSET 0 +#define SHADOW_OFFSET 6 +#define borderwidth 4 +#define LEFT_OFFSET 5 +#define ASIZE 100 + +// in us +#define FADE_TIME 40000 + +#define LCD_UPDATE_TIME_TV_MODE (60 * 1000 * 1000) + +#define ROUND_RADIUS 7 + +int time_left_width; +int time_dot_width; +int time_width; +int time_height; +bool newfreq = true; +char old_timestr[10]; +static event_id_t last_curr_id = 0, last_next_id = 0; + +extern CZapitClient::SatelliteList satList; +static bool sortByDateTime (const CChannelEvent& a, const CChannelEvent& b) +{ + return a.startTime < b.startTime; +} + +extern int timeshift; +extern unsigned char file_prozent; +extern std::string g_file_epg; +extern std::string g_file_epg1; +extern bool autoshift; +extern uint32_t shift_timer; + +#define RED_BAR 40 +#define YELLOW_BAR 70 +#define GREEN_BAR 100 +#define BAR_BORDER 1 +#define BAR_WIDTH 72 //(68+BAR_BORDER*2) +#define BAR_HEIGHT 12 //(13 + BAR_BORDER*2) +#define TIME_BAR_HEIGHT 12 +// InfoViewer: H 63 W 27 +#define NUMBER_H 63 +#define NUMBER_W 27 +extern char act_emu_str[20]; +extern std::string ext_channel_name; +int m_CA_Status; +static CScale * timescale; +static CScale * hddscale; +static CScale * varscale; +extern bool timeset; + +CInfoViewer::CInfoViewer () +{ + Init(); +} + +void CInfoViewer::Init() +{ + frameBuffer = CFrameBuffer::getInstance (); + + BoxStartX = BoxStartY = BoxEndX = BoxEndY = 0; + recordModeActive = false; + is_visible = false; + showButtonBar = false; + //gotTime = g_Sectionsd->getIsTimeSet (); + gotTime = timeset; + CA_Status = false; + virtual_zap_mode = false; + chanready = 1; + fileplay = 0; + sigscale = new CScale(BAR_WIDTH, 8, RED_BAR, GREEN_BAR, YELLOW_BAR); + snrscale = new CScale(BAR_WIDTH, 8, RED_BAR, GREEN_BAR, YELLOW_BAR); + hddscale = new CScale(100, 4, 50, GREEN_BAR, 75, true); + varscale = new CScale(100, 4, 50, GREEN_BAR, 75, true); + timescale = new CScale(100, TIME_BAR_HEIGHT, 30, GREEN_BAR, 70, true); + channel_id = live_channel_id; +} + +void CInfoViewer::start () +{ + ChanWidth = 122; + ChanHeight = 70; + + lcdUpdateTimer = g_RCInput->addTimer (LCD_UPDATE_TIME_TV_MODE, false, true); +} + +void CInfoViewer::paintTime (bool show_dot, bool firstPaint) +{ + if (gotTime) { + int ChanNameY = BoxStartY + (ChanHeight >> 1) + 5; //oberkante schatten? + + char timestr[10]; + struct timeb tm; + + ftime (&tm); + strftime ((char *) ×tr, 20, "%H:%M", localtime (&tm.time)); + + if ((!firstPaint) && (strcmp (timestr, old_timestr) == 0)) { + if (show_dot) + frameBuffer->paintBoxRel (BoxEndX - time_width + time_left_width - LEFT_OFFSET, ChanNameY, time_dot_width, time_height / 2 + 2, COL_INFOBAR_PLUS_0); + else + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString (BoxEndX - time_width + time_left_width - LEFT_OFFSET, ChanNameY + time_height, time_dot_width, ":", COL_INFOBAR); + strcpy (old_timestr, timestr); + } else { + strcpy (old_timestr, timestr); + + if (!firstPaint) { + frameBuffer->paintBoxRel (BoxEndX - time_width - LEFT_OFFSET, ChanNameY, time_width + LEFT_OFFSET, time_height, COL_INFOBAR_PLUS_0, ROUND_RADIUS, 1); + } + + timestr[2] = 0; + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString (BoxEndX - time_width - LEFT_OFFSET, ChanNameY + time_height, time_left_width, timestr, COL_INFOBAR); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString (BoxEndX - time_left_width - LEFT_OFFSET, ChanNameY + time_height, time_left_width, ×tr[3], COL_INFOBAR); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString (BoxEndX - time_width + time_left_width - LEFT_OFFSET, ChanNameY + time_height, time_dot_width, ":", COL_INFOBAR); + if (show_dot) + frameBuffer->paintBoxRel (BoxEndX - time_left_width - time_dot_width - LEFT_OFFSET, ChanNameY, time_dot_width, time_height / 2 + 2, COL_INFOBAR_PLUS_0); + } + } +} + +void CInfoViewer::showRecordIcon (const bool show) +{ + recordModeActive = CNeutrinoApp::getInstance ()->recordingstatus || shift_timer; + if (recordModeActive) { + int ChanNameX = BoxStartX + ChanWidth + 20; + if (show) { + frameBuffer->paintIcon (autoshift ? "ats.raw" : NEUTRINO_ICON_BUTTON_RED, ChanNameX, BoxStartY + 12); + if(!autoshift && !shift_timer) { + int chanH = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight (); + frameBuffer->paintBoxRel (ChanNameX + 28 + SHADOW_OFFSET, BoxStartY + 12 + SHADOW_OFFSET, 300, 20, COL_INFOBAR_SHADOW_PLUS_0); + frameBuffer->paintBoxRel (ChanNameX + 28, BoxStartY + 12, 300, 20, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString (ChanNameX + 30, BoxStartY + 12 + chanH, 300, ext_channel_name.c_str (), COL_INFOBAR, 0, true); + } else + frameBuffer->paintBackgroundBoxRel (ChanNameX + 28, BoxStartY + 12, 300 + SHADOW_OFFSET, 20 + SHADOW_OFFSET); + } else { + frameBuffer->paintBackgroundBoxRel (ChanNameX, BoxStartY + 10, 20, 20); + } + } +} + +void CInfoViewer::showTitle (const int ChanNum, const std::string & Channel, const t_satellite_position satellitePosition, const t_channel_id new_channel_id, const bool calledFromNumZap, int epgpos) +{ + last_curr_id = last_next_id = 0; + std::string ChannelName = Channel; + bool show_dot = true; + bool fadeOut = false; + int fadeValue; + bool new_chan = false; +//printf("CInfoViewer::showTitle ************************* chan num %d name %s\n", ChanNum, Channel.c_str()); + //aspectRatio = videoDecoder->getAspectRatio() > 2 ? 1 : 0; + aspectRatio = 0; + + showButtonBar = !calledFromNumZap; + bool fadeIn = g_settings.widget_fade && (!is_visible) && showButtonBar; + + is_visible = true; + if (!calledFromNumZap && fadeIn) + fadeTimer = g_RCInput->addTimer (FADE_TIME, false); + + fileplay = (ChanNum == 0); + newfreq = true; + sigscale->reset(); snrscale->reset(); timescale->reset(); hddscale->reset(); varscale->reset(); + + InfoHeightY = NUMBER_H * 9 / 8 + 2 * g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getHeight () + 25; + InfoHeightY_Info = 40; + + time_height = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getHeight () + 5; + time_left_width = 2 * g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth (widest_number); + time_dot_width = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth (":"); + time_width = time_left_width * 2 + time_dot_width; + + BoxStartX = g_settings.screen_StartX + 10; + BoxEndX = g_settings.screen_EndX - 10; + BoxEndY = g_settings.screen_EndY - 10; + + int BoxEndInfoY = showButtonBar ? (BoxEndY - InfoHeightY_Info) : (BoxEndY); + BoxStartY = BoxEndInfoY - InfoHeightY; + + if (!gotTime) + gotTime = timeset; + + if (fadeIn) { + fadeValue = 0x10; + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } else + fadeValue = g_settings.gtx_alpha1; + + // kill linke seite + frameBuffer->paintBackgroundBox (BoxStartX, BoxStartY + ChanHeight, BoxStartX + (ChanWidth / 3), BoxStartY + ChanHeight + InfoHeightY_Info + 10); + // kill progressbar + frameBuffer->paintBackgroundBox (BoxEndX - 120, BoxStartY, BoxEndX, BoxStartY + ChanHeight); + + int col_NumBoxText; + int col_NumBox; + if (virtual_zap_mode) { + col_NumBoxText = COL_MENUHEAD; + col_NumBox = COL_MENUHEAD_PLUS_0; + if ((channel_id != new_channel_id) || (evtlist.empty())) { + evtlist.clear(); + //evtlist = g_Sectionsd->getEventsServiceKey(new_channel_id & 0xFFFFFFFFFFFFULL); + sectionsd_getEventsServiceKey(new_channel_id & 0xFFFFFFFFFFFFULL, evtlist); + if (!evtlist.empty()) + sort(evtlist.begin(),evtlist.end(), sortByDateTime); + new_chan = true; + } + } else { + col_NumBoxText = COL_INFOBAR; + col_NumBox = COL_INFOBAR_PLUS_0; + } + if (! calledFromNumZap && !(g_RemoteControl->subChannels.empty()) && (g_RemoteControl->selected_subchannel > 0)) + { + channel_id = g_RemoteControl->subChannels[g_RemoteControl->selected_subchannel].getChannelID(); + ChannelName = g_RemoteControl->subChannels[g_RemoteControl->selected_subchannel].subservice_name; + } else { + channel_id = new_channel_id; + } + + int ChanNameX = BoxStartX + ChanWidth + SHADOW_OFFSET; + int ChanNameY = BoxStartY + (ChanHeight / 2) + 5; //oberkante schatten? + ChanInfoX = BoxStartX + (ChanWidth / 3); + + asize = (BoxEndX - (2*ICON_LARGE_WIDTH + 2*ICON_SMALL_WIDTH + 4*2) - 102) - ChanInfoX; + asize = asize - (NEUTRINO_ICON_BUTTON_RED_WIDTH+6)*4; + asize = asize / 4; + + //Shadow + frameBuffer->paintBox (BoxEndX-20, ChanNameY + SHADOW_OFFSET, BoxEndX + SHADOW_OFFSET, BoxEndY, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 1); + frameBuffer->paintBox (ChanInfoX + SHADOW_OFFSET, BoxEndY -20, BoxEndX + SHADOW_OFFSET, BoxEndY + SHADOW_OFFSET, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 2); //round + + //infobox + frameBuffer->paintBoxRel (ChanNameX-10, ChanNameY, BoxEndX-ChanNameX+10, BoxEndInfoY-ChanNameY, COL_INFOBAR_PLUS_0, ROUND_RADIUS, 1); // round + + //number box + frameBuffer->paintBoxRel (BoxStartX + SHADOW_OFFSET, BoxStartY + SHADOW_OFFSET, ChanWidth, ChanHeight + 4, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 3); // round + frameBuffer->paintBoxRel (BoxStartX, BoxStartY, ChanWidth, ChanHeight + 4, COL_INFOBAR_PLUS_0, ROUND_RADIUS, 3); // round + + int ChanNumYPos = BoxStartY + ChanHeight; + if (g_settings.infobar_sat_display && satellitePosition != 0 && satellitePositions.size()) { + sat_iterator_t sit = satellitePositions.find(satellitePosition); + + if(sit != satellitePositions.end()) { + int satNameWidth = g_SignalFont->getRenderWidth (sit->second.name); + if (satNameWidth > (ChanWidth - 4)) + satNameWidth = ChanWidth - 4; + + int chanH = g_SignalFont->getHeight (); + g_SignalFont->RenderString (3 + BoxStartX + ((ChanWidth - satNameWidth) / 2), BoxStartY + chanH, satNameWidth, sit->second.name, COL_INFOBAR); + } + ChanNumYPos += 5; + } + paintTime (show_dot, true); + showRecordIcon (show_dot); + show_dot = !show_dot; + + char strChanNum[10]; + sprintf (strChanNum, "%d", ChanNum); + + int ChanNumWidth = 0; + bool logo_ok = false; + if(showButtonBar) { +#define PIC_W 52 +#define PIC_H 39 +#define PIC_X (ChanNameX + 10) +#define PIC_Y (ChanNameY + time_height - PIC_H) + logo_ok = g_PicViewer->DisplayLogo(channel_id, PIC_X, PIC_Y, PIC_W, PIC_H); + ChanNumWidth = PIC_W + 10; + } + if (ChanNum && !logo_ok) { + int ChanNumHeight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_NUMBER]->getHeight (); + ChanNumWidth = 5 + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_NUMBER]->getRenderWidth (strChanNum); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_NUMBER]->RenderString (ChanNameX + 5, ChanNameY + ChanNumHeight, ChanNumWidth, strChanNum, col_NumBoxText); + } + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->RenderString (ChanNameX + 10 + ChanNumWidth, ChanNameY + time_height, BoxEndX - (ChanNameX + 20) - time_width - LEFT_OFFSET - 5 - ChanNumWidth, ChannelName, COL_INFOBAR, 0, true); // UTF-8 + + int ChanInfoY = BoxStartY + ChanHeight + 10; + ButtonWidth = (BoxEndX - ChanInfoX - ICON_OFFSET) >> 2; + + frameBuffer->paintBox (ChanInfoX, ChanInfoY, ChanNameX, BoxEndInfoY, COL_INFOBAR_PLUS_0); + + if (showButtonBar) { + sec_timer_id = g_RCInput->addTimer (1*1000*1000, false); + if (BOTTOM_BAR_OFFSET > 0) + frameBuffer->paintBackgroundBox (ChanInfoX, BoxEndInfoY, BoxEndX, BoxEndInfoY + BOTTOM_BAR_OFFSET); + + frameBuffer->paintBox (ChanInfoX, BoxEndInfoY-2, BoxEndX, BoxEndY-20, COL_BLACK); + { // FIXME + int xcnt = (BoxEndX - ChanInfoX) / 4; + int ycnt = (BoxEndY - BoxEndInfoY - 18) / 4; + for(int i = 0; i < xcnt; i++) { + for(int j = 0; j < ycnt; j++) + frameBuffer->paintBoxRel (ChanInfoX + i*4, BoxEndInfoY + j*4, 2, 2, COL_INFOBAR_PLUS_1); + } + } + frameBuffer->paintBox (ChanInfoX, BoxEndY-20, BoxEndX, BoxEndY, COL_INFOBAR_BUTTONS_BACKGROUND, ROUND_RADIUS, 2); //round + + showSNR(); + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_BLUE, ChanInfoX + 16*3 + asize * 3 + 2*7, BoxEndY - ICON_Y_1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(ChanInfoX + 16*4 + asize * 3 + 2*8, BoxEndY+2, ButtonWidth - (2 + NEUTRINO_ICON_BUTTON_BLUE_WIDTH + 2 + 2), g_Locale->getText(LOCALE_INFOVIEWER_STREAMINFO), COL_INFOBAR_BUTTONS, 0, true); // UTF-8 + + showButton_Audio (); + showButton_SubServices (); + showIcon_CA_Status(0); + showIcon_16_9 (); + showIcon_VTXT (); + showIcon_SubT(); + } + + if (fileplay) { + show_Data (); + } else { + //info_CurrentNext = getEPG (channel_id); + sectionsd_getCurrentNextServiceKey(channel_id & 0xFFFFFFFFFFFFULL, info_CurrentNext); + if (!evtlist.empty()) { + if (new_chan) { + for ( eli=evtlist.begin(); eli!=evtlist.end(); ++eli ) { + if ((uint)eli->startTime >= info_CurrentNext.current_zeit.startzeit + info_CurrentNext.current_zeit.dauer) + break; + } + if (eli == evtlist.end()) // the end is not valid, so go back + --eli; + } + + if (epgpos != 0) { + info_CurrentNext.flags = 0; + if ((epgpos > 0) && (eli != evtlist.end())) { + ++eli; // next epg + if (eli == evtlist.end()) // the end is not valid, so go back + --eli; + } + else if ((epgpos < 0) && (eli != evtlist.begin())) { + --eli; // prev epg + } + info_CurrentNext.flags = CSectionsdClient::epgflags::has_current; + info_CurrentNext.current_uniqueKey = eli->eventID; + info_CurrentNext.current_zeit.startzeit = eli->startTime; + info_CurrentNext.current_zeit.dauer = eli->duration; + if (eli->description.empty()) + info_CurrentNext.current_name = g_Locale->getText(LOCALE_INFOVIEWER_NOEPG); + else + info_CurrentNext.current_name = eli->description; + info_CurrentNext.current_fsk = '\0'; + + if (eli != evtlist.end()) { + ++eli; + if (eli != evtlist.end()) { + info_CurrentNext.flags = CSectionsdClient::epgflags::has_current | CSectionsdClient::epgflags::has_next; + info_CurrentNext.next_uniqueKey = eli->eventID; + info_CurrentNext.next_zeit.startzeit = eli->startTime; + info_CurrentNext.next_zeit.dauer = eli->duration; + if (eli->description.empty()) + info_CurrentNext.next_name = g_Locale->getText(LOCALE_INFOVIEWER_NOEPG); + else + info_CurrentNext.next_name = eli->description; + } + --eli; + } + } + } + + if (!(info_CurrentNext.flags & (CSectionsdClient::epgflags::has_later | CSectionsdClient::epgflags::has_current | CSectionsdClient::epgflags::not_broadcast))) { + // nicht gefunden / noch nicht geladen + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (ChanNameX + 10, ChanInfoY + 2 * g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getHeight () + 5, BoxEndX - (ChanNameX + 20), g_Locale->getText (gotTime ? (showButtonBar ? LOCALE_INFOVIEWER_EPGWAIT : LOCALE_INFOVIEWER_EPGNOTLOAD) : LOCALE_INFOVIEWER_WAITTIME), COL_INFOBAR, 0, true); // UTF-8 + } else { + show_Data (); + } + } + showLcdPercentOver (); + + if ((g_RemoteControl->current_channel_id == channel_id) && !(((info_CurrentNext.flags & CSectionsdClient::epgflags::has_next) && (info_CurrentNext.flags & (CSectionsdClient::epgflags::has_current | CSectionsdClient::epgflags::has_no_current))) || (info_CurrentNext.flags & CSectionsdClient::epgflags::not_broadcast))) { + g_Sectionsd->setServiceChanged (channel_id & 0xFFFFFFFFFFFFULL, true); + } + + neutrino_msg_t msg; + neutrino_msg_data_t data; + + CNeutrinoApp *neutrino = CNeutrinoApp::getInstance (); + if (!calledFromNumZap) { + + bool hideIt = true; + virtual_zap_mode = false; + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd (g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR]); + + int res = messages_return::none; + time_t ta, tb; + + while (!(res & (messages_return::cancel_info | messages_return::cancel_all))) { + g_RCInput->getMsgAbsoluteTimeout (&msg, &data, &timeoutEnd); +#if 0 + if (!(info_CurrentNext.flags & (CSectionsdClient::epgflags::has_current))) { + if (difftime (time (&tb), ta) > 1.1) { + time (&ta); + info_CurrentNext = getEPG (channel_id); + if ((info_CurrentNext.flags & (CSectionsdClient::epgflags::has_current))) { + show_Data (); + showLcdPercentOver (); + } + } + } +#endif + if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + g_RCInput->postMsg (msg, 0); + res = messages_return::cancel_info; + } + else if (msg == CRCInput::RC_help || msg == CRCInput::RC_info) { + g_RCInput->postMsg (NeutrinoMessages::SHOW_EPG, 0); + res = messages_return::cancel_info; + } else if ((msg == NeutrinoMessages::EVT_TIMER) && (data == fadeTimer)) { + if (fadeOut) { // disappear + fadeValue -= 0x10; + if (fadeValue <= 0x10) { + fadeValue = g_settings.gtx_alpha1; + g_RCInput->killTimer (fadeTimer); + res = messages_return::cancel_info; + } else + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } else { // appears + fadeValue += 0x10; + if (fadeValue >= g_settings.gtx_alpha1) { + fadeValue = g_settings.gtx_alpha1; + g_RCInput->killTimer (fadeTimer); + fadeIn = false; + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + } else + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } + } else if ((msg == CRCInput::RC_ok) || (msg == CRCInput::RC_home) || (msg == CRCInput::RC_timeout)) { + if (fadeIn) { + g_RCInput->killTimer (fadeTimer); + fadeIn = false; + } + if ((!fadeOut) && g_settings.widget_fade) { + fadeOut = true; + fadeTimer = g_RCInput->addTimer (FADE_TIME, false); + timeoutEnd = CRCInput::calcTimeoutEnd (1); + } else { +#if 0 + if ((msg != CRCInput::RC_timeout) && (msg != CRCInput::RC_ok)) + if (!fileplay && !timeshift) + g_RCInput->postMsg (msg, data); +#endif + res = messages_return::cancel_info; + } + } else if ((msg == NeutrinoMessages::EVT_TIMER) && (data == sec_timer_id)) { + showSNR (); + paintTime (show_dot, false); + showRecordIcon (show_dot); + show_dot = !show_dot; + + showIcon_16_9(); + } else if ( g_settings.virtual_zap_mode && ((msg == CRCInput::RC_right) || msg == CRCInput::RC_left )) { + virtual_zap_mode = true; + res = messages_return::cancel_all; + hideIt = true; + } else if (!fileplay && !timeshift) { + if ((msg == (neutrino_msg_t) g_settings.key_quickzap_up) || (msg == (neutrino_msg_t) g_settings.key_quickzap_down) || (msg == CRCInput::RC_0) || (msg == NeutrinoMessages::SHOW_INFOBAR)) { + hideIt = false; + //hideIt = (g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] == 0) ? true : false; + g_RCInput->postMsg (msg, data); + res = messages_return::cancel_info; + } else if (msg == NeutrinoMessages::EVT_TIMESET) { + // Handle anyway! + neutrino->handleMsg (msg, data); + g_RCInput->postMsg (NeutrinoMessages::SHOW_INFOBAR, 0); + hideIt = false; + res = messages_return::cancel_all; + } else { + if (msg == CRCInput::RC_standby) { + g_RCInput->killTimer (sec_timer_id); + if (fadeIn || fadeOut) + g_RCInput->killTimer (fadeTimer); + } + res = neutrino->handleMsg (msg, data); + if (res & messages_return::unhandled) { + // raus hier und im Hauptfenster behandeln... + g_RCInput->postMsg (msg, data); + res = messages_return::cancel_info; + } + } + } + } + + if (hideIt) + killTitle (); + + g_RCInput->killTimer (sec_timer_id); + sec_timer_id = 0; + if (fadeIn || fadeOut) { + g_RCInput->killTimer (fadeTimer); + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + } + if (virtual_zap_mode) + CNeutrinoApp::getInstance()->channelList->virtual_zap_mode(msg == CRCInput::RC_right); + + } + aspectRatio = 0; + fileplay = 0; +} + +void CInfoViewer::showSubchan () +{ + CFrameBuffer *frameBuffer = CFrameBuffer::getInstance (); + CNeutrinoApp *neutrino = CNeutrinoApp::getInstance (); + + std::string subChannelName; // holds the name of the subchannel/audio channel + int subchannel = 0; // holds the channel index + + if (!(g_RemoteControl->subChannels.empty ())) { + // get info for nvod/subchannel + subchannel = g_RemoteControl->selected_subchannel; + if (g_RemoteControl->selected_subchannel >= 0) + subChannelName = g_RemoteControl->subChannels[g_RemoteControl->selected_subchannel].subservice_name; + } else if (g_RemoteControl->current_PIDs.APIDs.size () > 1 && g_settings.audiochannel_up_down_enable) { + // get info for audio channel + subchannel = g_RemoteControl->current_PIDs.PIDs.selected_apid; + subChannelName = g_RemoteControl->current_PIDs.APIDs[g_RemoteControl->current_PIDs.PIDs.selected_apid].desc; + } + + if (!(subChannelName.empty ())) { + char text[100]; + sprintf (text, "%d - %s", subchannel, subChannelName.c_str ()); + + int dx = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getRenderWidth (text) + 20; + int dy = 25; + + if (g_RemoteControl->director_mode) { + int w = 20 + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth (g_Locale->getText (LOCALE_NVODSELECTOR_DIRECTORMODE), true) + 20; // UTF-8 + if (w > dx) + dx = w; + dy = dy * 2; + } else + dy = dy + 5; + + int x = 0, y = 0; + if (g_settings.infobar_subchan_disp_pos == 0) { + // Rechts-Oben + x = g_settings.screen_EndX - dx - 10; + y = g_settings.screen_StartY + 10; + } else if (g_settings.infobar_subchan_disp_pos == 1) { + // Links-Oben + x = g_settings.screen_StartX + 10; + y = g_settings.screen_StartY + 10; + } else if (g_settings.infobar_subchan_disp_pos == 2) { + // Links-Unten + x = g_settings.screen_StartX + 10; + y = g_settings.screen_EndY - dy - 10; + } else if (g_settings.infobar_subchan_disp_pos == 3) { + // Rechts-Unten + x = g_settings.screen_EndX - dx - 10; + y = g_settings.screen_EndY - dy - 10; + } + + fb_pixel_t pixbuf[(dx + 2 * borderwidth) * (dy + 2 * borderwidth)]; + frameBuffer->SaveScreen (x - borderwidth, y - borderwidth, dx + 2 * borderwidth, dy + 2 * borderwidth, pixbuf); + + // clear border + frameBuffer->paintBackgroundBoxRel (x - borderwidth, y - borderwidth, dx + 2 * borderwidth, borderwidth); + frameBuffer->paintBackgroundBoxRel (x - borderwidth, y + dy, dx + 2 * borderwidth, borderwidth); + frameBuffer->paintBackgroundBoxRel (x - borderwidth, y, borderwidth, dy); + frameBuffer->paintBackgroundBoxRel (x + dx, y, borderwidth, dy); + + frameBuffer->paintBoxRel (x, y, dx, dy, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (x + 10, y + 30, dx - 20, text, COL_MENUCONTENT); + + if (g_RemoteControl->director_mode) { + frameBuffer->paintIcon (NEUTRINO_ICON_BUTTON_YELLOW, x + 8, y + dy - 20); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString (x + 30, y + dy - 2, dx - 40, g_Locale->getText (LOCALE_NVODSELECTOR_DIRECTORMODE), COL_MENUCONTENT, 0, true); // UTF-8 + } + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd (2); + int res = messages_return::none; + + neutrino_msg_t msg; + neutrino_msg_data_t data; + + while (!(res & (messages_return::cancel_info | messages_return::cancel_all))) { + g_RCInput->getMsgAbsoluteTimeout (&msg, &data, &timeoutEnd); + + if (msg == CRCInput::RC_timeout) { + res = messages_return::cancel_info; + } else { + res = neutrino->handleMsg (msg, data); + + if (res & messages_return::unhandled) { + // raus hier und im Hauptfenster behandeln... + g_RCInput->postMsg (msg, data); + res = messages_return::cancel_info; + } + } + } + frameBuffer->RestoreScreen (x - borderwidth, y - borderwidth, dx + 2 * borderwidth, dy + 2 * borderwidth, pixbuf); + } else { + g_RCInput->postMsg (NeutrinoMessages::SHOW_INFOBAR, 0); + } +} + +void CInfoViewer::showIcon_16_9 () +{ + if((aspectRatio == 0) || (aspectRatio != videoDecoder->getAspectRatio())) { + aspectRatio = videoDecoder->getAspectRatio(); + frameBuffer->paintIcon ((aspectRatio > 2) ? "16_9.raw" : "16_9_gray.raw", BoxEndX - (2*ICON_LARGE_WIDTH + 2*ICON_SMALL_WIDTH + 4*2), BoxEndY - ICON_Y_1); + } +} + +extern "C" void tuxtxt_start(int tpid); +extern "C" int tuxtxt_stop(); + +void CInfoViewer::showIcon_VTXT () const +{ + frameBuffer->paintIcon ((g_RemoteControl->current_PIDs.PIDs.vtxtpid != 0) ? "vtxt.raw" : "vtxt_gray.raw", BoxEndX - (2*ICON_SMALL_WIDTH + 2*2), BoxEndY - ICON_Y_1); +} + +void CInfoViewer::showIcon_SubT() const +{ + bool have_sub = false; + CZapitChannel * cc = CNeutrinoApp::getInstance()->channelList->getChannel(CNeutrinoApp::getInstance()->channelList->getActiveChannelNumber()); + if(cc && cc->getSubtitleCount()) + have_sub = true; + + frameBuffer->paintIcon(have_sub ? "subt.raw" : "subt_gray.raw", BoxEndX - (ICON_SMALL_WIDTH + 2), BoxEndY - ICON_Y_1); +} + +void CInfoViewer::showFailure () +{ + ShowHintUTF (LOCALE_MESSAGEBOX_ERROR, g_Locale->getText (LOCALE_INFOVIEWER_NOTAVAILABLE), 430); // UTF-8 +} + +void CInfoViewer::showMotorMoving (int duration) +{ + char text[256]; + char buffer[10]; + + sprintf (buffer, "%d", duration); + strcpy (text, g_Locale->getText (LOCALE_INFOVIEWER_MOTOR_MOVING)); + strcat (text, " ("); + strcat (text, buffer); + strcat (text, " s)"); + + ShowHintUTF (LOCALE_MESSAGEBOX_INFO, text, g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth (text, true) + 10, duration); // UTF-8 +} + +int CInfoViewer::handleMsg (const neutrino_msg_t msg, neutrino_msg_data_t data) +{ + + if ((msg == NeutrinoMessages::EVT_CURRENTNEXT_EPG) || (msg == NeutrinoMessages::EVT_NEXTPROGRAM)) { +//printf("CInfoViewer::handleMsg: NeutrinoMessages::EVT_CURRENTNEXT_EPG data %llx current %llx\n", *(t_channel_id *) data, channel_id & 0xFFFFFFFFFFFFULL); + if ((*(t_channel_id *) data) == (channel_id & 0xFFFFFFFFFFFFULL)) { + getEPG (*(t_channel_id *) data, info_CurrentNext); + if (is_visible) + show_Data (true); + showLcdPercentOver (); + } + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_TIMER) { + if (data == fadeTimer) { + // hierher kann das event nur dann kommen, wenn ein anderes Fenster im Vordergrund ist! + g_RCInput->killTimer (fadeTimer); + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + + return messages_return::handled; + } else if (data == lcdUpdateTimer) { +//printf("CInfoViewer::handleMsg: lcdUpdateTimer\n"); + if ( is_visible ) + show_Data( true ); + showLcdPercentOver (); + return messages_return::handled; + } else if (data == sec_timer_id) { + showSNR (); + return messages_return::handled; + } + } else if (msg == NeutrinoMessages::EVT_RECORDMODE) { + recordModeActive = data; + if(is_visible) showRecordIcon(true); + } else if (msg == NeutrinoMessages::EVT_ZAP_GOTAPIDS) { + if ((*(t_channel_id *) data) == channel_id) { + if (is_visible && showButtonBar) + showButton_Audio (); + } + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_GOTPIDS) { + if ((*(t_channel_id *) data) == channel_id) { + if (is_visible && showButtonBar) { + showIcon_VTXT (); + showIcon_SubT(); + showIcon_CA_Status (0); + } + } + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_GOT_SUBSERVICES) { + if ((*(t_channel_id *) data) == channel_id) { + if (is_visible && showButtonBar) + showButton_SubServices (); + } + return messages_return::handled; + } else if ((msg == NeutrinoMessages::EVT_ZAP_COMPLETE) || + (msg == NeutrinoMessages::EVT_ZAP_ISNVOD)) + { + channel_id = (*(t_channel_id *)data); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_SUB_COMPLETE) { + chanready = 1; + showSNR (); + //if ((*(t_channel_id *)data) == channel_id) + { + if (is_visible && showButtonBar && (!g_RemoteControl->are_subchannels)) + show_Data (true); + } + showLcdPercentOver (); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_SUB_FAILED) { + chanready = 1; + showSNR (); + // show failure..! + CVFD::getInstance ()->showServicename ("(" + g_RemoteControl->getCurrentChannelName () + ')'); + printf ("zap failed!\n"); + showFailure (); + CVFD::getInstance ()->showPercentOver (255); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_FAILED) { + chanready = 1; + showSNR (); + if ((*(t_channel_id *) data) == channel_id) { + // show failure..! + CVFD::getInstance ()->showServicename ("(" + g_RemoteControl->getCurrentChannelName () + ')'); + printf ("zap failed!\n"); + showFailure (); + CVFD::getInstance ()->showPercentOver (255); + } + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_MOTOR) { + chanready = 0; + showMotorMoving (data); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_MODECHANGED) { + aspectRatio = data; + if (is_visible && showButtonBar) + showIcon_16_9 (); + + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_TIMESET) { + gotTime = true; + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_CA_CLEAR) { + Set_CA_Status (false); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_CA_LOCK) { + Set_CA_Status (true); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_CA_FTA) { + Set_CA_Status (false); + return messages_return::handled; + } else if (msg == NeutrinoMessages::EVT_ZAP_CA_ID) { + chanready = 1; + Set_CA_Status (data); + showSNR (); + return messages_return::handled; + } + + return messages_return::unhandled; +} + +void CInfoViewer::showButton_SubServices () +{ + if (!(g_RemoteControl->subChannels.empty ())) { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_YELLOW, + ChanInfoX + 2 + NEUTRINO_ICON_BUTTON_RED_WIDTH + 2 + asize + 2 + NEUTRINO_ICON_BUTTON_GREEN_WIDTH + 2 + asize + 2, BoxEndY- ICON_Y_1 ); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString( + ChanInfoX + 2 + NEUTRINO_ICON_BUTTON_RED_WIDTH + 2 + asize + 2 + NEUTRINO_ICON_BUTTON_GREEN_WIDTH + 2 + asize + 2 + NEUTRINO_ICON_BUTTON_YELLOW_WIDTH + 2, + BoxEndY+2, asize, g_Locale->getText((g_RemoteControl->are_subchannels) ? LOCALE_INFOVIEWER_SUBSERVICE : LOCALE_INFOVIEWER_SELECTTIME), COL_INFOBAR_BUTTONS, 0, true); // UTF-8 + } +} + +CSectionsdClient::CurrentNextInfo CInfoViewer::getEPG (const t_channel_id for_channel_id, CSectionsdClient::CurrentNextInfo &info) +{ + static CSectionsdClient::CurrentNextInfo oldinfo; + + //g_Sectionsd->getCurrentNextServiceKey (for_channel_id & 0xFFFFFFFFFFFFULL, info); + sectionsd_getCurrentNextServiceKey(for_channel_id & 0xFFFFFFFFFFFFULL, info); + +//printf("CInfoViewer::getEPG: old uniqueKey %llx new %llx\n", oldinfo.current_uniqueKey, info.current_uniqueKey); + if (info.current_uniqueKey != oldinfo.current_uniqueKey || info.next_uniqueKey != oldinfo.next_uniqueKey) { + if (info.flags & (CSectionsdClient::epgflags::has_current | CSectionsdClient::epgflags::has_next)) { + CSectionsdClient::CurrentNextInfo * _info = new CSectionsdClient::CurrentNextInfo; + *_info = info; + neutrino_msg_t msg; + if (info.flags & CSectionsdClient::epgflags::has_current) + msg = NeutrinoMessages::EVT_CURRENTEPG; + else + msg = NeutrinoMessages::EVT_NEXTEPG; + g_RCInput->postMsg(msg, (unsigned) _info, false ); + } else { + t_channel_id *p = new t_channel_id; + *p = for_channel_id; + g_RCInput->postMsg (NeutrinoMessages::EVT_NOEPG_YET, (const neutrino_msg_data_t) p, false); // data is pointer to allocated memory + } + oldinfo = info; + } + + return info; +} + +#define get_set CNeutrinoApp::getInstance()->getScanSettings() + +void CInfoViewer::showSNR () +{ + char percent[10]; + uint16_t ssig, ssnr; + int sw, snr, sig, posx, posy; + int height, ChanNumYPos; + int barwidth = BAR_WIDTH; + + if (is_visible && !fileplay) { + if (newfreq && chanready) { + char freq[20]; + + newfreq = false; + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo (); + sprintf (freq, "%d.%d MHz %c", si.tsfrequency / 1000, si.tsfrequency % 1000, si.polarisation ? 'V' : 'H'); +#if 0 +//FIXME this sets default params for scan menu + sprintf (get_set.TP_freq, "%d", si.tsfrequency); + sprintf (get_set.TP_rate, "%d", si.rate); + get_set.TP_pol = si.polarisation; + get_set.TP_fec = si.fec; +#endif + + int chanH = g_SignalFont->getHeight (); + int satNameWidth = g_SignalFont->getRenderWidth (freq); + g_SignalFont->RenderString (3 + BoxStartX + ((ChanWidth - satNameWidth) / 2), BoxStartY + 2 * chanH - 3, satNameWidth, freq, COL_INFOBAR); + } + ssig = frontend->getSignalStrength(); + ssnr = frontend->getSignalNoiseRatio(); + + sig = (ssig & 0xFFFF) * 100 / 65535; + snr = (ssnr & 0xFFFF) * 100 / 65535; + height = g_SignalFont->getHeight () - 1; + ChanNumYPos = BoxStartY + ChanHeight + 4 - 2 * height; + + if (sigscale->getPercent() != sig) { + posx = BoxStartX + 4; + posy = ChanNumYPos + 3; + sigscale->paint(posx, posy+4, sig); + + sprintf (percent, "%d%%S", sig); + posx = posx + barwidth + 2; + sw = BoxStartX + ChanWidth - posx; + frameBuffer->paintBoxRel (posx, posy, sw, height, COL_INFOBAR_PLUS_0); + g_SignalFont->RenderString (posx, posy + height, sw, percent, COL_INFOBAR); + } + if (snrscale->getPercent() != snr) { + posx = BoxStartX + 4; + posy = ChanNumYPos + 3 + height - 2; + + snrscale->paint(posx, posy+4, snr); + + sprintf (percent, "%d%%Q", snr); + posx = posx + barwidth + 2; + sw = BoxStartX + ChanWidth - posx -4; + frameBuffer->paintBoxRel (posx, posy, sw, height-2, COL_INFOBAR_PLUS_0); + g_SignalFont->RenderString (posx, posy + height, sw, percent, COL_INFOBAR); + } + } + if(is_visible) { + struct statfs s; + int per = 0; + if (::statfs("/var", &s) == 0) { + per = (s.f_blocks - s.f_bfree) / (s.f_blocks/100); + } + varscale->paint(BoxEndX - (2*ICON_LARGE_WIDTH + 2*ICON_SMALL_WIDTH + 4*2) - 102, BoxEndY - ICON_Y_1, per); + per = 0; + //HD info + if (::statfs(g_settings.network_nfs_recordingdir, &s) == 0) { + switch(s.f_type) + { + case (int) 0xEF53: /*EXT2 & EXT3*/ + case (int) 0x6969: /*NFS*/ + case (int) 0xFF534D42: /*CIFS*/ + case (int) 0x517B: /*SMB*/ + case (int) 0x52654973: /*REISERFS*/ + case (int) 0x65735546: /*fuse for ntfs*/ + case (int) 0x58465342: /*xfs*/ + case (int) 0x4d44: /*msdos*/ + per = (s.f_blocks - s.f_bfree) / (s.f_blocks/100); + break; + default: + fprintf( stderr,"%s Unknow File system type: %i\n",g_settings.network_nfs_recordingdir ,s.f_type); + break; + } + } + + hddscale->paint(BoxEndX - (2*ICON_LARGE_WIDTH + 2*ICON_SMALL_WIDTH + 4*2) - 102, BoxEndY - ICON_Y_1 + 10, per); + } +} + +void CInfoViewer::show_Data (bool calledFromEvent) +{ + char runningStart[10]; + char runningRest[20]; + char runningPercent = 0; + static char oldrunningPercent = 255; + + char nextStart[10]; + char nextDuration[10]; + + int is_nvod = false; + int b114 = BoxEndX - 114 + 7; + int b112 = BoxEndX - 112 + 7; + + if (fileplay) { + int posy = BoxStartY + 12; + runningPercent = file_prozent; + if(runningPercent > 100) + runningPercent = 100; + + if(!calledFromEvent || (oldrunningPercent != runningPercent)) { + frameBuffer->paintBoxRel (b114+4, posy, 102, 18, COL_INFOBAR_SHADOW_PLUS_0); + frameBuffer->paintBoxRel (b112+4, posy + 2, 98, 14, COL_INFOBAR_PLUS_0); + oldrunningPercent = file_prozent; + } + timescale->paint(b112+4, posy + 2, runningPercent); + + int xStart = BoxStartX + ChanWidth; + int ChanInfoY = BoxStartY + ChanHeight + 15; //+10 + int height = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getHeight (); + int duration1TextPos = BoxEndX - 30; + + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, duration1TextPos - xStart - 5, g_file_epg, COL_INFOBAR, 0, true); + ChanInfoY += height; + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, duration1TextPos - xStart - 5, g_file_epg1, COL_INFOBAR, 0, true); + + return; + } + if (is_visible) { + + if ((g_RemoteControl->current_channel_id == channel_id) && (g_RemoteControl->subChannels.size () > 0) && (!g_RemoteControl->are_subchannels)) { + is_nvod = true; + info_CurrentNext.current_zeit.startzeit = g_RemoteControl->subChannels[g_RemoteControl->selected_subchannel].startzeit; + info_CurrentNext.current_zeit.dauer = g_RemoteControl->subChannels[g_RemoteControl->selected_subchannel].dauer; + } else { + if ((info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) && (info_CurrentNext.flags & CSectionsdClient::epgflags::has_next) && (showButtonBar)) { + if ((uint) info_CurrentNext.next_zeit.startzeit < (info_CurrentNext.current_zeit.startzeit + info_CurrentNext.current_zeit.dauer)) { + is_nvod = true; + } + } + } + + time_t jetzt = time (NULL); + + if (info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) { + int seit = (jetzt - info_CurrentNext.current_zeit.startzeit + 30) / 60; + int rest = (info_CurrentNext.current_zeit.dauer / 60) - seit; + if (seit < 0) { + runningPercent = 0; + sprintf (runningRest, "in %d min", -seit); + } else { + runningPercent = (unsigned) ((float) (jetzt - info_CurrentNext.current_zeit.startzeit) / (float) info_CurrentNext.current_zeit.dauer * 100.); + if(runningPercent > 100) + runningPercent = 100; + sprintf (runningRest, "%d / %d min", seit, rest); + } + + struct tm *pStartZeit = localtime (&info_CurrentNext.current_zeit.startzeit); + sprintf (runningStart, "%02d:%02d", pStartZeit->tm_hour, pStartZeit->tm_min); + } else + last_curr_id = 0; + + if (info_CurrentNext.flags & CSectionsdClient::epgflags::has_next) { + unsigned dauer = info_CurrentNext.next_zeit.dauer / 60; + sprintf (nextDuration, "%d min", dauer); + struct tm *pStartZeit = localtime (&info_CurrentNext.next_zeit.startzeit); + sprintf (nextStart, "%02d:%02d", pStartZeit->tm_hour, pStartZeit->tm_min); + } else + last_next_id = 0; + + int height = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getHeight () / 3; + int ChanInfoY = BoxStartY + ChanHeight + 15; //+10 + + if (showButtonBar) { + int posy = BoxStartY + 16; + int height2 = 20; + //percent + if (info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) { +//printf("CInfoViewer::show_Data: ************************************************* runningPercent %d\n", runningPercent); + if(!calledFromEvent || (oldrunningPercent != runningPercent)) { + frameBuffer->paintBoxRel (BoxEndX - 104, posy + 6, 108, 14, COL_INFOBAR_SHADOW_PLUS_0, 1, 3); + frameBuffer->paintBoxRel (BoxEndX - 108, posy + 2, 108, 14, COL_INFOBAR_PLUS_0, 1, 3); + oldrunningPercent = runningPercent; + } + timescale->paint(BoxEndX - 102, posy + 2, runningPercent); + } else { + oldrunningPercent = 255; + frameBuffer->paintBackgroundBoxRel (BoxEndX - 108, posy, 112, height2); + } + if (info_CurrentNext.flags & CSectionsdClient::epgflags::has_anything) { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_RED, ChanInfoX + 2, BoxEndY - ICON_Y_1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(ChanInfoX + (2 + NEUTRINO_ICON_BUTTON_RED_WIDTH + 2), BoxEndY+2, asize, g_Locale->getText(LOCALE_INFOVIEWER_EVENTLIST), COL_INFOBAR_BUTTONS, 0, true); // UTF-8 + } + } + + height = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getHeight (); + int xStart = BoxStartX + ChanWidth; + + //frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + + if ((info_CurrentNext.flags & CSectionsdClient::epgflags::not_broadcast) || ((calledFromEvent) && !(info_CurrentNext.flags & (CSectionsdClient::epgflags::has_next | CSectionsdClient::epgflags::has_current)))) { + // no EPG available + ChanInfoY += height; + frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (BoxStartX + ChanWidth + 20, ChanInfoY + height, BoxEndX - (BoxStartX + ChanWidth + 20), g_Locale->getText (gotTime ? LOCALE_INFOVIEWER_NOEPG : LOCALE_INFOVIEWER_WAITTIME), COL_INFOBAR, 0, true); // UTF-8 + } else { + // irgendein EPG gefunden + int duration1Width = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getRenderWidth (runningRest); + int duration1TextPos = BoxEndX - duration1Width - LEFT_OFFSET; + + int duration2Width = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->getRenderWidth (nextDuration); + int duration2TextPos = BoxEndX - duration2Width - LEFT_OFFSET; + + if ((info_CurrentNext.flags & CSectionsdClient::epgflags::has_next) && (!(info_CurrentNext.flags & CSectionsdClient::epgflags::has_current))) { + // there are later events available - yet no current + frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, BoxEndX - xStart, g_Locale->getText (LOCALE_INFOVIEWER_NOCURRENT), COL_INFOBAR, 0, true); // UTF-8 + + ChanInfoY += height; + + //info next + //frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + + if(last_next_id != info_CurrentNext.next_uniqueKey) { + frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (ChanInfoX + 10, ChanInfoY + height, 100, nextStart, COL_INFOBAR); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, duration2TextPos - xStart - 5, info_CurrentNext.next_name, COL_INFOBAR, 0, true); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (duration2TextPos, ChanInfoY + height, duration2Width, nextDuration, COL_INFOBAR); + last_next_id = info_CurrentNext.next_uniqueKey; + } + } else { + if(last_curr_id != info_CurrentNext.current_uniqueKey) { + frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (ChanInfoX + 10, ChanInfoY + height, 100, runningStart, COL_INFOBAR); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, duration1TextPos - xStart - 5, info_CurrentNext.current_name, COL_INFOBAR, 0, true); + + last_curr_id = info_CurrentNext.current_uniqueKey; + } + frameBuffer->paintBox (BoxEndX - 80, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0);//FIXME duration1TextPos not really good + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (duration1TextPos, ChanInfoY + height, duration1Width, runningRest, COL_INFOBAR); + + ChanInfoY += height; + + //info next + //frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + + if ((!is_nvod) && (info_CurrentNext.flags & CSectionsdClient::epgflags::has_next)) { + if(last_next_id != info_CurrentNext.next_uniqueKey) { + frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (ChanInfoX + 10, ChanInfoY + height, 100, nextStart, COL_INFOBAR); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (xStart, ChanInfoY + height, duration2TextPos - xStart - 5, info_CurrentNext.next_name, COL_INFOBAR, 0, true); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO]->RenderString (duration2TextPos, ChanInfoY + height, duration2Width, nextDuration, COL_INFOBAR); + last_next_id = info_CurrentNext.next_uniqueKey; + } + } //else + //frameBuffer->paintBox (ChanInfoX + 10, ChanInfoY, BoxEndX, ChanInfoY + height, COL_INFOBAR_PLUS_0);//why this... + } + } + } +} + +void CInfoViewer::showButton_Audio () +{ + // green, in case of several APIDs + uint32_t count = g_RemoteControl->current_PIDs.APIDs.size (); + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, ChanInfoX + 2 + NEUTRINO_ICON_BUTTON_RED_WIDTH + 2 + asize + 2, BoxEndY- ICON_Y_1); + + if (count > 0) { + int selected = g_RemoteControl->current_PIDs.PIDs.selected_apid; + + int sx = ChanInfoX + 2 + NEUTRINO_ICON_BUTTON_RED_WIDTH + 2 + asize + 2 + NEUTRINO_ICON_BUTTON_GREEN_WIDTH + 2; + + frameBuffer->paintBox (sx, BoxEndY-20, sx+asize, BoxEndY, COL_INFOBAR_BUTTONS_BACKGROUND); + + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(sx, BoxEndY+2, asize, + g_RemoteControl->current_PIDs.APIDs[selected].desc, COL_INFOBAR_BUTTONS, 0, true); // UTF-8 + } + const char *dd_icon; + if ((g_RemoteControl->current_PIDs.PIDs.selected_apid < count) && (g_RemoteControl->current_PIDs.APIDs[g_RemoteControl->current_PIDs.PIDs.selected_apid].is_ac3)) + dd_icon = "dd.raw"; + else if (g_RemoteControl->has_ac3) + dd_icon = "dd_avail.raw"; + else + dd_icon = "dd_gray.raw"; + + frameBuffer->paintIcon (dd_icon, BoxEndX - (ICON_LARGE_WIDTH + 2*ICON_SMALL_WIDTH + 3*2), BoxEndY - ICON_Y_1); +} + +void CInfoViewer::killTitle () +{ + if (is_visible) { + is_visible = false; + frameBuffer->paintBackgroundBox (BoxStartX, BoxStartY, BoxEndX + SHADOW_OFFSET, BoxEndY + SHADOW_OFFSET); + } +} + +void CInfoViewer::Set_CA_Status (int Status) +{ + CA_Status = Status; + m_CA_Status = Status; + if (is_visible && showButtonBar) + showIcon_CA_Status (1); +} + +void CInfoViewer::showLcdPercentOver () +{ + if (g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME] != 1) { + if (fileplay || (NeutrinoMessages::mode_ts == CNeutrinoApp::getInstance()->getMode())) { + CVFD::getInstance ()->showPercentOver (file_prozent); + return; + } + int runningPercent = -1; + time_t jetzt = time (NULL); +#if 0 + if (!(info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) || jetzt > (int) (info_CurrentNext.current_zeit.startzeit + info_CurrentNext.current_zeit.dauer)) { + info_CurrentNext = getEPG (channel_id); + } +#endif + if (info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) { + if (jetzt < info_CurrentNext.current_zeit.startzeit) + runningPercent = 0; + else + runningPercent = MIN ((unsigned) ((float) (jetzt - info_CurrentNext.current_zeit.startzeit) / (float) info_CurrentNext.current_zeit.dauer * 100.), 100); + } + CVFD::getInstance ()->showPercentOver (runningPercent); + } +} + +int CInfoViewerHandler::exec (CMenuTarget * parent, const std::string & actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + CChannelList *channelList; + CInfoViewer *i; + + if (parent) { + parent->hide (); + } + + i = new CInfoViewer; + + channelList = CNeutrinoApp::getInstance ()->channelList; + i->start (); + i->showTitle (channelList->getActiveChannelNumber (), channelList->getActiveChannelName (), channelList->getActiveSatellitePosition (), channelList->getActiveChannel_ChannelID ()); // UTF-8 + delete i; + + return res; +} + + + +#define ICON_H 16 +#define ICON_Y_2 (16 + 2 + ICON_H) +#define MAX_EW 146 + + +void CInfoViewer::paint_ca_icons(int caid, char * icon) +{ + char buf[20]; + int endx = ChanInfoX - 8 + ((BoxEndX - ChanInfoX)/4)*4; + int py = BoxEndY - InfoHeightY_Info; + int px = 0; + + switch ( caid & 0xFF00 ) { + case 0x0E00: + px = endx - 48 - 38 - 9*10 - 7*14 - 10; sprintf(buf, "%s_%s.raw", "powervu", icon); + break; + case 0x4A00: + px = endx - 48 - 38 - 8*10 - 6*14 - 10; sprintf(buf, "%s_%s.raw", "d", icon); + break; + case 0x2600: + px = endx - 48 - 38 - 7*10 - 5*14 - 10; sprintf(buf, "%s_%s.raw", "biss", icon); + break; + case 0x600: + case 0x602: + case 0x1700: + px = endx - 48 - 38 - 6*10 - 4*14 - 10; sprintf(buf, "%s_%s.raw", "ird", icon); + break; + case 0x100: + px = endx - 48 - 38 - 5*10 - 4*14; sprintf(buf, "%s_%s.raw", "seca", icon); + break; + case 0x500: + px = endx - 48 - 38 - 4*10 - 3*14; sprintf(buf, "%s_%s.raw", "via", icon); + break; + case 0x1800: + case 0x1801: + px = endx - 48 - 38 - 3*10 - 2*14; sprintf(buf, "%s_%s.raw", "nagra", icon); + break; + case 0xB00: + px = endx - 48 - 38 - 2*10 - 1*14; sprintf(buf, "%s_%s.raw", "conax", icon); + break; + case 0xD00: + px = endx - 48 - 38 - 1*10; sprintf(buf, "%s_%s.raw", "cw", icon); + break; + case 0x900: + px = endx - 48; sprintf(buf, "%s_%s.raw", "nds", icon); + break; + default: + break; + }/*case*/ + if(px) { + frameBuffer->paintIcon(buf, px, py ); + } +} + +static char * gray = (char *) "white"; +//static char * green = (char *) "green"; +static char * white = (char *) "yellow"; +extern int pmt_caids[10]; + +void CInfoViewer::showIcon_CA_Status (int notfirst) +{ +#if 0 + FILE *f; + char input[256]; + char buf[256]; + int acaid = 0; + int py = BoxEndY - InfoHeightY_Info; +#endif + int i; + int caids[] = { 0x1700, 0x0100, 0x0500, 0x1800, 0xB00, 0xD00, 0x900, 0x2600, 0x4a00, 0x0E00 }; + + if(!notfirst) { + for(i=0; i < (int)(sizeof(caids)/sizeof(int)); i++) { + paint_ca_icons(caids[i], (char *) (pmt_caids[i] ? white : gray)); + } + } +} diff --git a/src/gui/infoviewer.h b/src/gui/infoviewer.h new file mode 100644 index 000000000..2a363443e --- /dev/null +++ b/src/gui/infoviewer.h @@ -0,0 +1,137 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __infoview__ +#define __infoview__ + +#include + +#include +#include +#include +#include +#include "widget/menue.h" +#include +#include + +class CInfoViewer +{ + private: + void Init(void); + CFrameBuffer * frameBuffer; + + bool gotTime; + bool recordModeActive; +#ifndef SKIP_CA_STATUS + bool CA_Status; +#endif + + int InfoHeightY; + int InfoHeightY_Info; + bool showButtonBar; + bool fileplay; + + int BoxEndX; + int BoxEndY; + int BoxStartX; + int BoxStartY; + int ButtonWidth; + + int ChanWidth; + int ChanHeight; + int ChanInfoX; + + int asize; + + CSectionsdClient::CurrentNextInfo info_CurrentNext; + t_channel_id channel_id; + + char aspectRatio; + + uint32_t sec_timer_id; + uint32_t fadeTimer; + bool virtual_zap_mode; + CChannelEventList evtlist; + CChannelEventList::iterator eli; + + void show_Data( bool calledFromEvent = false ); + void paintTime( bool show_dot, bool firstPaint ); + + void showButton_Audio(); + void showButton_SubServices(); + + void showIcon_16_9(); +#ifndef SKIP_CA_STATUS + void showIcon_CA_Status(int); + void paint_ca_icons(int, char*); +#endif + void showIcon_VTXT() const; + void showRecordIcon(const bool show); + void showIcon_SubT() const; + + void showFailure(); + void showMotorMoving(int duration); + void showLcdPercentOver(); + void showSNR(); + CScale * snrscale, * sigscale; + + public: + bool chanready; + bool is_visible; + uint32_t lcdUpdateTimer; + + CInfoViewer(); + + void start(); + + void showTitle(const int ChanNum, const std::string & Channel, const t_satellite_position satellitePosition, const t_channel_id new_channel_id = 0, const bool calledFromNumZap = false, int epgpos = 0); // Channel must be UTF-8 encoded + void lookAheadEPG(const int ChanNum, const std::string & Channel, const t_channel_id new_channel_id = 0, const bool calledFromNumZap = false); //alpha: fix for nvod subchannel update + void killTitle(); + CSectionsdClient::CurrentNextInfo getEPG(const t_channel_id for_channel_id, CSectionsdClient::CurrentNextInfo &info); + + void showSubchan(); +#ifndef SKIP_CA_STATUS + void Set_CA_Status(int Status); +#endif + + int handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data); + void clearVirtualZapMode() {virtual_zap_mode = false;} +}; + +class CInfoViewerHandler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionkey); + int doMenu(); + +}; +#endif diff --git a/src/gui/motorcontrol.cpp b/src/gui/motorcontrol.cpp new file mode 100644 index 000000000..34510f9c5 --- /dev/null +++ b/src/gui/motorcontrol.cpp @@ -0,0 +1,705 @@ +/* + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern CFrontend * frontend; + +static int g_sig; +static int g_snr; +static int last_snr = 0; +static int moving = 0; + +#define RED_BAR 40 +#define YELLOW_BAR 70 +#define GREEN_BAR 100 + +#define BAR_BORDER 2 +#define BAR_WIDTH 100 +#define BAR_HEIGHT 16 //(13 + BAR_BORDER*2) +#define ROUND_RADIUS 9 + +#define get_set CNeutrinoApp::getInstance()->getScanSettings() +CMotorControl::CMotorControl() +{ + Init(); +} + +void CMotorControl::Init(void) +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + + satfindpid = -1; + + width = w_max(470, 0); + mheight = mheight - 2; + height = hheight + (20 * mheight) - 5; + height = h_max(height, 0); + + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + stepSize = 1; //default: 1 step + stepMode = STEP_MODE_ON; + installerMenue = false; + motorPosition = 1; + satellitePosition = 0; + stepDelay = 10; + sigscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); + snrscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); +} + +int CMotorControl::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + TP_params TP; + int wasgrow = 0; + last_snr = 0; + moving = 0; + + CZapitClient::ScanSatelliteList satList; + CZapitClient::commandSetScanSatelliteList sat; + sat_iterator_t sit; + + sigscale->reset(); + snrscale->reset(); + + bool istheend = false; + int lim_cmd; + if (!frameBuffer->getActive()) + return menu_return::RETURN_EXIT_ALL; + + if (parent) + parent->hide(); + + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + + /* send satellite list to zapit */ + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + if(!strcmp(sit->second.name.c_str(),get_set.satNameNoDiseqc)) { + sat.position = sit->first; + strncpy(sat.satName, get_set.satNameNoDiseqc, 50); + satList.push_back(sat); + break; + } + } + + g_Zapit->setScanSatelliteList( satList); + + TP.feparams.frequency = atoi(get_set.TP_freq); + TP.feparams.u.qpsk.symbol_rate = atoi(get_set.TP_rate); + TP.feparams.u.qpsk.fec_inner = (fe_code_rate_t)get_set.TP_fec; + TP.polarization = get_set.TP_pol; +#if 0 + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo (); + TP.feparams.frequency = si.tsfrequency; + TP.feparams.u.qpsk.symbol_rate = si.rate; + TP.feparams.u.qpsk.fec_inner = si.fec; + TP.polarization = si.polarisation; +#endif + + g_Zapit->stopPlayBack(); + g_Zapit->tune_TP(TP); + + paint(); + paintMenu(); + paintStatus(); + + while (!istheend) + { + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd_MS(250); + msg = CRCInput::RC_nokey; + + while (!(msg == CRCInput::RC_timeout) && (!(msg == CRCInput::RC_home))) + { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + showSNR(); +//printf("SIG: %d SNR %d last %d\n", g_sig, g_snr, last_snr); + if(moving && (stepMode == STEP_MODE_AUTO)) { + if(last_snr < g_snr) { + wasgrow = 1; + } + //if((last_snr > g_snr) && last_snr > 37000) { + if(wasgrow && (last_snr > g_snr) && last_snr > 50) { +//printf("Must stop rotor!!!\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x60, 0, 0, 0); + moving = 0; + paintStatus(); + last_snr = 0; + } else + last_snr = g_snr; + } else + wasgrow = 0; + + if (installerMenue) + { + switch(msg) + { + case CRCInput::RC_ok: + case CRCInput::RC_0: + printf("[motorcontrol] 0 key received... goto userMenue\n"); + installerMenue = false; + paintMenu(); + paintStatus(); + break; + + case CRCInput::RC_1: + case CRCInput::RC_right: + printf("[motorcontrol] left/1 key received... drive/Step motor west, stepMode: %d\n", stepMode); + motorStepWest(); + paintStatus(); + break; + + case CRCInput::RC_red: + case CRCInput::RC_2: + printf("[motorcontrol] 2 key received... halt motor\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x60, 0, 0, 0); + moving = 0; + paintStatus(); + break; + + case CRCInput::RC_3: + case CRCInput::RC_left: + printf("[motorcontrol] right/3 key received... drive/Step motor east, stepMode: %d\n", stepMode); + motorStepEast(); + paintStatus(); + break; + + case CRCInput::RC_4: + printf("[motorcontrol] 4 key received... set west (soft) limit\n"); + if(g_settings.rotor_swap) lim_cmd = 0x66; + else lim_cmd = 0x67; + g_Zapit->sendMotorCommand(0xE1, 0x31, lim_cmd, 0, 0, 0); + break; + + case CRCInput::RC_5: + printf("[motorcontrol] 5 key received... disable (soft) limits\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x63, 0, 0, 0); + break; + + case CRCInput::RC_6: + printf("[motorcontrol] 6 key received... set east (soft) limit\n"); + if(g_settings.rotor_swap) lim_cmd = 0x67; + else lim_cmd = 0x66; + g_Zapit->sendMotorCommand(0xE1, 0x31, lim_cmd, 0, 0, 0); + break; + + case CRCInput::RC_7: + printf("[motorcontrol] 7 key received... goto reference position\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x6B, 1, 0, 0); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_8: + printf("[motorcontrol] 8 key received... enable (soft) limits\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x6A, 1, 0, 0); + break; + + case CRCInput::RC_9: + printf("[motorcontrol] 9 key received... (re)-calculate positions\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x6F, 1, 0, 0); + break; + + case CRCInput::RC_plus: + case CRCInput::RC_up: + printf("[motorcontrol] up key received... increase satellite position: %d\n", ++motorPosition); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_minus: + case CRCInput::RC_down: + if (motorPosition > 1) motorPosition--; + printf("[motorcontrol] down key received... decrease satellite position: %d\n", motorPosition); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_blue: + if (++stepMode > 3) + stepMode = 0; + if (stepMode == STEP_MODE_OFF) + satellitePosition = 0; + last_snr = 0; + printf("[motorcontrol] red key received... toggle stepmode on/off: %d\n", stepMode); + paintStatus(); + break; + + default: + //printf("[motorcontrol] message received...\n"); + if ((msg >= CRCInput::RC_WithData) && (msg < CRCInput::RC_WithData + 0x10000000)) + delete (unsigned char*) data; + break; + } + } + else + { + switch(msg) + { + case CRCInput::RC_ok: + case CRCInput::RC_0: + printf("[motorcontrol] 0 key received... goto installerMenue\n"); + installerMenue = true; + paintMenu(); + paintStatus(); + break; + + case CRCInput::RC_1: + case CRCInput::RC_right: + printf("[motorcontrol] left/1 key received... drive/Step motor west, stepMode: %d\n", stepMode); + motorStepWest(); + paintStatus(); + break; + + case CRCInput::RC_red: + case CRCInput::RC_2: + printf("[motorcontrol] 2 key received... halt motor\n"); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x60, 0, 0, 0); + break; + + case CRCInput::RC_3: + case CRCInput::RC_left: + printf("[motorcontrol] right/3 key received... drive/Step motor east, stepMode: %d\n", stepMode); + motorStepEast(); + paintStatus(); + break; + + case CRCInput::RC_green: + case CRCInput::RC_5: + printf("[motorcontrol] 5 key received... store present satellite number: %d\n", motorPosition); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x6A, 1, motorPosition, 0); + break; + + case CRCInput::RC_6: + if (stepSize < 0x7F) stepSize++; + printf("[motorcontrol] 6 key received... increase Step size: %d\n", stepSize); + paintStatus(); + break; + + case CRCInput::RC_yellow: + case CRCInput::RC_7: + printf("[motorcontrol] 7 key received... goto satellite number: %d\n", motorPosition); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x6B, 1, motorPosition, 0); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_9: + if (stepSize > 1) stepSize--; + printf("[motorcontrol] 9 key received... decrease Step size: %d\n", stepSize); + paintStatus(); + break; + + case CRCInput::RC_plus: + case CRCInput::RC_up: + printf("[motorcontrol] up key received... increase satellite position: %d\n", ++motorPosition); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_minus: + case CRCInput::RC_down: + if (motorPosition > 1) motorPosition--; + printf("[motorcontrol] down key received... decrease satellite position: %d\n", motorPosition); + satellitePosition = 0; + paintStatus(); + break; + + case CRCInput::RC_blue: + if (++stepMode > 2) + stepMode = 0; + if (stepMode == STEP_MODE_OFF) + satellitePosition = 0; + printf("[motorcontrol] red key received... toggle stepmode on/off: %d\n", stepMode); + paintStatus(); + break; + + default: + //printf("[motorcontrol] message received...\n"); + if ((msg >= CRCInput::RC_WithData) && (msg < CRCInput::RC_WithData + 0x10000000)) + delete (unsigned char*) data; + break; + } + } + } + + istheend = (msg == CRCInput::RC_home); + } + + hide(); + + return menu_return::RETURN_REPAINT; +} + +void CMotorControl::motorStepWest(void) +{ + int cmd; + printf("[motorcontrol] motorStepWest\n"); + if(g_settings.rotor_swap) cmd = 0x68; + else cmd = 0x69; + switch(stepMode) + { + case STEP_MODE_ON: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, (-1 * stepSize), 0); + satellitePosition += stepSize; + break; + case STEP_MODE_TIMED: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, 40, 0); + usleep(stepSize * stepDelay * 1000); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x60, 0, 0, 0); //halt motor + satellitePosition += stepSize; + break; + case STEP_MODE_AUTO: + moving = 1; + paintStatus(); + default: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, 40, 0); + } +} + +void CMotorControl::motorStepEast(void) +{ + int cmd; + if(g_settings.rotor_swap) cmd = 0x69; + else cmd = 0x68; + printf("[motorcontrol] motorStepEast\n"); + switch(stepMode) + { + case STEP_MODE_ON: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, (-1 * stepSize), 0); + satellitePosition -= stepSize; + break; + case STEP_MODE_TIMED: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, 40, 0); + usleep(stepSize * stepDelay * 1000); + g_Zapit->sendMotorCommand(0xE0, 0x31, 0x60, 0, 0, 0); //halt motor + satellitePosition -= stepSize; + break; + case STEP_MODE_AUTO: + moving = 1; + default: + g_Zapit->sendMotorCommand(0xE0, 0x31, cmd, 1, 40, 0); + } +} + +void CMotorControl::hide() +{ + frameBuffer->paintBackgroundBoxRel(x, y, width, height + 20); + stopSatFind(); +} + +void CMotorControl::paintLine(int x, int * y, int width, char * txt) +{ + *y += mheight; + frameBuffer->paintBoxRel(x, *y - mheight, width, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, *y, width, txt, COL_MENUCONTENT, 0, true); +} + +void CMotorControl::paintLine(int x, int y, int width, char * txt) +{ + //frameBuffer->paintBoxRel(x, y - mheight, width, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, y, width, txt, COL_MENUCONTENT, 0, true); +} + +void CMotorControl::paintSeparator(int xpos, int * ypos, int width, char * txt) +{ + //int stringwidth = 0; + //int stringstartposX = 0; + int th = 10; + //*ypos += mheight; + *ypos += th; + frameBuffer->paintHLineRel(xpos, width - 20, *ypos - (th >> 1), COL_MENUCONTENT_PLUS_3); + frameBuffer->paintHLineRel(xpos, width - 20, *ypos - (th >> 1) + 1, COL_MENUCONTENT_PLUS_1); + +#if 0 + stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(txt); + stringstartposX = 0; + stringstartposX = (xpos + (width >> 1)) - (stringwidth >> 1); + frameBuffer->paintBoxRel(stringstartposX - 5, *ypos - mheight, stringwidth + 10, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposX, *ypos, stringwidth, txt, COL_MENUCONTENT); +#endif +} + +void CMotorControl::paintStatus() +{ + char buf[256]; + char buf2[256]; + + int xpos1 = x + 10; + int xpos2 = xpos1 + 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth((char *) g_Locale->getText(LOCALE_MOTORCONTROL_MOTOR_POS)); + int width2 = width - (xpos2 - xpos1) - 10; + int width1 = width - 10; + + ypos = ypos_status; + paintSeparator(xpos1, &ypos, width, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_SETTINGS)); + + paintLine(xpos1, &ypos, width1, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_MOTOR_POS)); + sprintf(buf, "%d", motorPosition); + paintLine(xpos2, ypos, width2 , buf); + + paintLine(xpos1, &ypos, width1, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_MOVEMENT)); + switch(stepMode) + { + case STEP_MODE_ON: + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_MODE)); + break; + case STEP_MODE_OFF: + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_DRIVE_MODE)); + break; + case STEP_MODE_AUTO: + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_DRIVE_MODE_AUTO)); + break; + case STEP_MODE_TIMED: + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_TIMED_MODE)); + break; + } + paintLine(xpos2, ypos, width2, buf); + + paintLine(xpos1, &ypos, width1, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_SIZE)); + switch(stepMode) + { + case STEP_MODE_ON: + sprintf(buf, "%d", stepSize); + break; + case STEP_MODE_AUTO: + if(moving) + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STOP_MOVING)); + else + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STOP_STOPPED)); + break; + case STEP_MODE_OFF: + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_NO_MODE)); + break; + case STEP_MODE_TIMED: + sprintf(buf, "%d ", stepSize * stepDelay); + strcat(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_MSEC)); + break; + } + paintLine(xpos2, ypos, width2, buf); + + paintSeparator(xpos1, &ypos, width, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STATUS)); + strcpy(buf, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_SAT_POS)); + sprintf(buf2, "%d", satellitePosition); + strcat(buf, buf2); + paintLine(xpos1, &ypos, width1, buf); + paintSeparator(xpos1, &ypos, width, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_SETTINGS)); +} + +void CMotorControl::paint() +{ + ypos = y; + frameBuffer->paintBoxRel(x, ypos, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x + 10, ypos + hheight, width, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_HEAD), COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintBoxRel(x, ypos + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + ypos += hheight + (mheight >> 1) - 10; + ypos_menue = ypos; +} + +void CMotorControl::paintMenu() +{ + ypos = ypos_menue; + + int xpos1 = x + 10; + int xpos2 = xpos1 + 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth("(7/yellow)"); + int width2 = width - (xpos2 - xpos1) - 10; + int width1 = width - 10; + + paintLine(xpos1, &ypos, width1, (char *) "(0/OK)"); + if(installerMenue) + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_USER_MENU)); + else + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_INSTALL_MENU)); + + paintLine(xpos1, &ypos, width1, (char *) "(1/right)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_WEST)); + paintLine(xpos1, &ypos, width1, (char *) "(2/red)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_HALT)); + paintLine(xpos1, &ypos, width1, (char *) "(3/left)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_EAST)); + + if (installerMenue) + { + paintLine(xpos1, &ypos, width1,(char *) "(4)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_WEST_LIMIT)); + paintLine(xpos1, &ypos, width1, (char *) "(5)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_DISABLE_LIMIT)); + paintLine(xpos1, &ypos, width1, (char *) "(6)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_EAST_LIMIT)); + paintLine(xpos1, &ypos, width1, (char *) "(7)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_REF_POSITION)); + paintLine(xpos1, &ypos, width1, (char *) "(8)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_ENABLE_LIMIT)); + paintLine(xpos1, &ypos, width1, (char *) "(9)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_CALC_POSITIONS)); + paintLine(xpos1, &ypos, width1, (char *) "(+/up)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_POS_INCREASE)); + paintLine(xpos1, &ypos, width1, (char *) "(-/down)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_POS_DECREASE)); + paintLine(xpos1, &ypos, width1,(char *) "(blue)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_DRIVE)); + } + else + { + paintLine(xpos1, &ypos, width1, (char *) "(4)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_NOTDEF)); + paintLine(xpos1, &ypos, width1, (char *) "(5/green)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STORE)); + paintLine(xpos1, &ypos, width1,(char *) "(6)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_INCREASE)); + paintLine(xpos1, &ypos, width1, (char *) "(7/yellow)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_GOTO)); + paintLine(xpos1, &ypos, width1, (char *) "(8)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_NOTDEF)); + paintLine(xpos1, &ypos, width1, (char *) "(9)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_DECREASE)); + paintLine(xpos1, &ypos, width1, (char *) "(+/up)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_POS_INCREASE)); + paintLine(xpos1, &ypos, width1, (char *) "(-/down)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_POS_DECREASE)); + paintLine(xpos1, &ypos, width1, (char *) "(blue)"); + paintLine(xpos2, ypos, width2, (char *) g_Locale->getText(LOCALE_MOTORCONTROL_STEP_DRIVE)); + } + + ypos_status = ypos; +} + +void CMotorControl::startSatFind(void) +{ +#if 0 + if (satfindpid != -1) { + kill(satfindpid, SIGKILL); + waitpid(satfindpid, 0, 0); + satfindpid = -1; + } + + switch ((satfindpid = fork())) { + case -1: + printf("[motorcontrol] fork"); + break; + case 0: + printf("[motorcontrol] starting satfind...\n"); +#if HAVE_DVB_API_VERSION >= 3 + if (execlp("/bin/satfind", "satfind", NULL) < 0) +#else + //if (execlp("/bin/satfind", "satfind", "--tune", NULL) < 0) + if (execlp("/bin/satfind", "satfind", NULL) < 0) +#endif + printf("[motorcontrol] execlp satfind failed.\n"); + break; + } /* switch */ +#endif +} + +void CMotorControl::stopSatFind(void) +{ + + if (satfindpid != -1) { + printf("[motorcontrol] killing satfind...\n"); + kill(satfindpid, SIGKILL); + waitpid(satfindpid, 0, 0); + satfindpid = -1; + } +} + +#define BARWT 10 +#define BAR_BL 2 +#define BARW (BARWT - BAR_BL) +#define BARWW (BARWT - BARW) + +void CMotorControl::showSNR() +{ + char percent[10]; + //char ber[20]; + int barwidth = 100; + uint16_t ssig, ssnr; + int sig, snr; + int bheight, posx, posy; + + int sw; + + ssig = frontend->getSignalStrength(); + ssnr = frontend->getSignalNoiseRatio(); + + snr = (ssnr & 0xFFFF) * 100 / 65535; + sig = (ssig & 0xFFFF) * 100 / 65535; + if(sig < 5) + return; + g_sig = ssig & 0xFFFF; + g_snr = snr; + + bheight = mheight - 5; + posy = y + height - mheight - 5; + + if(sigscale->getPercent() != sig) { + posx = x + 10; + sprintf(percent, "%d%% SIG", sig); + sw = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth ("100% SIG"); + + sigscale->paint(posx-1, posy, sig); + + posx = posx + barwidth + 3; + frameBuffer->paintBoxRel(posx, posy - 2, sw+4, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString (posx+2, posy + mheight, sw, percent, COL_MENUCONTENT); + } + + if(snrscale->getPercent() != snr) { + posx = x + 10 + 210; + sprintf(percent, "%d%% SNR", snr); + sw = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth ("100% SNR"); + snrscale->paint(posx-1, posy, snr); + + posx = posx + barwidth + 3; + frameBuffer->paintBoxRel(posx, posy - 2, sw+4, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString (posx+2, posy + mheight, sw, percent, COL_MENUCONTENT); + } +} diff --git a/src/gui/motorcontrol.h b/src/gui/motorcontrol.h new file mode 100644 index 000000000..5d88e5a03 --- /dev/null +++ b/src/gui/motorcontrol.h @@ -0,0 +1,90 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __motorcontrol__ +#define __motorcontrol__ + +#include +#include +#include +#include + + +#define STEP_MODE_OFF 0 +#define STEP_MODE_AUTO 1 +#define STEP_MODE_ON 2 +#define STEP_MODE_TIMED 3 + +class CMotorControl : public CMenuTarget +{ + private: + void Init(void); + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + int ypos; + int ypos_status; + int ypos_menue; + + int8_t stepSize; + int32_t stepDelay; + int stepMode; + bool installerMenue; + uint8_t motorPosition; + int32_t satellitePosition; + int satfindpid; + CScale * snrscale, * sigscale; + + void paint(); + void paintMenu(void); + void paintStatus(void); + void paintLine(int x, int * y, int width, char * txt); + void paintLine(int x, int y, int width, char * txt); + void paintSeparator(int xpos, int * ypos, int width, char * txt); + void motorStepEast(void); + void motorStepWest(void); + void startSatFind(void); + void stopSatFind(void); + void showSNR(void); + + public: + + CMotorControl(); + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/moviebrowser.cpp b/src/gui/moviebrowser.cpp new file mode 100644 index 000000000..b9a5392f7 --- /dev/null +++ b/src/gui/moviebrowser.cpp @@ -0,0 +1,4317 @@ +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + $Id: moviebrowser.cpp,v 1.10 2006/09/11 21:11:35 guenther Exp $ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: moviebrowser.cpp . + + Description: Implementation of the CMovieBrowser class + This class provides a filebrowser window to view, select and start a movies from HD. + This class does replace the Filebrowser + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' +****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "moviebrowser.h" +#include "filebrowser.h" +#include "widget/hintbox.h" +#include "widget/helpbox.h" +#include "widget/messagebox.h" +#include "widget/stringinput_ext.h" +#include +#include +#include +#include "neutrino.h" +#include +#include // for statfs +#include +#include +#include +#include +#include +#include + +extern CPictureViewer * g_PicViewer; +#define PIC_W 52 +#define PIC_H 39 +static CScale * timescale; +#define ROUND_RADIUS 9 + +#define my_scandir scandir64 +#define my_alphasort alphasort64 +typedef struct stat64 stat_struct; +typedef struct dirent64 dirent_struct; +#define my_stat stat64 +static off64_t get_full_len(char * startname); +//static off64_t truncate_movie(char * name, off64_t size, int len, int seconds); +static off64_t truncate_movie(MI_MOVIE_INFO * minfo); +static off64_t cut_movie(MI_MOVIE_INFO * minfo, CMovieInfo * cmovie); +static off64_t copy_movie(MI_MOVIE_INFO * minfo, CMovieInfo * cmovie, bool onefile); +void strReplace(std::string& orig, const char* fstr, const std::string rstr); + +#define TRACE printf +#define TRACE_1 printf + +#define VLC_URI "vlc://" + +#define NUMBER_OF_MOVIES_LAST 40 // This is the number of movies shown in last recored and last played list + +#define MESSAGEBOX_BROWSER_ROW_ITEM_COUNT 20 +const CMenuOptionChooser::keyval MESSAGEBOX_BROWSER_ROW_ITEM[MESSAGEBOX_BROWSER_ROW_ITEM_COUNT] = +{ + { MB_INFO_FILENAME, LOCALE_MOVIEBROWSER_INFO_FILENAME }, + { MB_INFO_FILEPATH, LOCALE_MOVIEBROWSER_INFO_PATH }, + { MB_INFO_TITLE, LOCALE_MOVIEBROWSER_INFO_TITLE }, + { MB_INFO_SERIE, LOCALE_MOVIEBROWSER_INFO_SERIE }, + { MB_INFO_INFO1, LOCALE_MOVIEBROWSER_INFO_INFO1 }, + { MB_INFO_MAJOR_GENRE, LOCALE_MOVIEBROWSER_INFO_GENRE_MAJOR }, + { MB_INFO_MINOR_GENRE, LOCALE_MOVIEBROWSER_INFO_GENRE_MINOR }, + { MB_INFO_PARENTAL_LOCKAGE, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE }, + { MB_INFO_CHANNEL, LOCALE_MOVIEBROWSER_INFO_CHANNEL }, + { MB_INFO_QUALITY, LOCALE_MOVIEBROWSER_INFO_QUALITY }, + { MB_INFO_PREVPLAYDATE, LOCALE_MOVIEBROWSER_INFO_PREVPLAYDATE }, + { MB_INFO_RECORDDATE, LOCALE_MOVIEBROWSER_INFO_RECORDDATE }, + { MB_INFO_PRODDATE, LOCALE_MOVIEBROWSER_INFO_PRODYEAR }, + { MB_INFO_COUNTRY, LOCALE_MOVIEBROWSER_INFO_PRODCOUNTRY }, + { MB_INFO_GEOMETRIE, LOCALE_MOVIEBROWSER_INFO_VIDEOFORMAT }, + { MB_INFO_AUDIO, LOCALE_MOVIEBROWSER_INFO_AUDIO }, + { MB_INFO_LENGTH, LOCALE_MOVIEBROWSER_INFO_LENGTH }, + { MB_INFO_SIZE, LOCALE_MOVIEBROWSER_INFO_SIZE }, + { MB_INFO_BOOKMARK, LOCALE_MOVIEBROWSER_MENU_MAIN_BOOKMARKS }, + { MB_INFO_FILENAME, LOCALE_MOVIEBROWSER_INFO_FILENAME } + }; + +#define MESSAGEBOX_YES_NO_OPTIONS_COUNT 2 +const CMenuOptionChooser::keyval MESSAGEBOX_YES_NO_OPTIONS[MESSAGEBOX_YES_NO_OPTIONS_COUNT] = +{ + { 0, LOCALE_MESSAGEBOX_NO }, + { 1, LOCALE_MESSAGEBOX_YES } +}; + +#define MESSAGEBOX_PARENTAL_LOCK_OPTIONS_COUNT 3 +const CMenuOptionChooser::keyval MESSAGEBOX_PARENTAL_LOCK_OPTIONS[MESSAGEBOX_PARENTAL_LOCK_OPTIONS_COUNT] = +{ + { 1, LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_ACTIVATED_YES }, + { 0, LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_ACTIVATED_NO }, + { 2, LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_ACTIVATED_NO_TEMP } +}; + +#define MESSAGEBOX_PARENTAL_LOCKAGE_OPTION_COUNT 6 +const CMenuOptionChooser::keyval MESSAGEBOX_PARENTAL_LOCKAGE_OPTIONS[MESSAGEBOX_PARENTAL_LOCKAGE_OPTION_COUNT] = +{ + { 0, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_0YEAR }, + { 6, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_6YEAR }, + { 12, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_12YEAR }, + { 16, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_16YEAR }, + { 18, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_18YEAR }, + { 99, LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE_ALWAYS } +}; + +#define MAX_WINDOW_WIDTH (g_settings.screen_EndX - g_settings.screen_StartX - 40) +#define MAX_WINDOW_HEIGHT (g_settings.screen_EndY - g_settings.screen_StartY - 40) + +#define MIN_WINDOW_WIDTH ((g_settings.screen_EndX - g_settings.screen_StartX)>>1) +#define MIN_WINDOW_HEIGHT 200 + +#define TITLE_BACKGROUND_COLOR ((CFBWindow::color_t)COL_MENUHEAD_PLUS_0) +#define TITLE_FONT_COLOR ((CFBWindow::color_t)COL_MENUHEAD) + +#define TEXT_FONT g_Font[SNeutrinoSettings::FONT_TYPE_MENU] +#define TITLE_FONT g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE] +#define FOOT_FONT g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL] + +#define INTER_FRAME_SPACE 4 // space between e.g. upper and lower window +#define TEXT_BORDER_WIDTH 8 + +const neutrino_locale_t m_localizedItemName[MB_INFO_MAX_NUMBER+1] = +{ + LOCALE_MOVIEBROWSER_SHORT_FILENAME, + LOCALE_MOVIEBROWSER_SHORT_PATH, + LOCALE_MOVIEBROWSER_SHORT_TITLE , + LOCALE_MOVIEBROWSER_SHORT_SERIE, + LOCALE_MOVIEBROWSER_SHORT_INFO1, + LOCALE_MOVIEBROWSER_SHORT_GENRE_MAJOR, + LOCALE_MOVIEBROWSER_SHORT_GENRE_MINOR, + LOCALE_MOVIEBROWSER_SHORT_INFO2, + LOCALE_MOVIEBROWSER_SHORT_PARENTAL_LOCKAGE , + LOCALE_MOVIEBROWSER_SHORT_CHANNEL , + LOCALE_MOVIEBROWSER_SHORT_BOOK, + LOCALE_MOVIEBROWSER_SHORT_QUALITY, + LOCALE_MOVIEBROWSER_SHORT_PREVPLAYDATE, + LOCALE_MOVIEBROWSER_SHORT_RECORDDATE, + LOCALE_MOVIEBROWSER_SHORT_PRODYEAR, + LOCALE_MOVIEBROWSER_SHORT_COUNTRY, + LOCALE_MOVIEBROWSER_SHORT_FORMAT , + LOCALE_MOVIEBROWSER_SHORT_AUDIO , + LOCALE_MOVIEBROWSER_SHORT_LENGTH, + LOCALE_MOVIEBROWSER_SHORT_SIZE, + NONEXISTANT_LOCALE +}; + +// default row size in pixel for any element +#define MB_ROW_WIDTH_FILENAME 150 +#define MB_ROW_WIDTH_FILEPATH 150 +#define MB_ROW_WIDTH_TITLE 300 +#define MB_ROW_WIDTH_SERIE 100 +#define MB_ROW_WIDTH_INFO1 100 +#define MB_ROW_WIDTH_MAJOR_GENRE 100 +#define MB_ROW_WIDTH_MINOR_GENRE 30 +#define MB_ROW_WIDTH_INFO2 30 +#define MB_ROW_WIDTH_PARENTAL_LOCKAGE 25 +#define MB_ROW_WIDTH_CHANNEL 80 +#define MB_ROW_WIDTH_BOOKMARK 50 +#define MB_ROW_WIDTH_QUALITY 25 +#define MB_ROW_WIDTH_PREVPLAYDATE 80 +#define MB_ROW_WIDTH_RECORDDATE 80 +#define MB_ROW_WIDTH_PRODDATE 50 +#define MB_ROW_WIDTH_COUNTRY 50 +#define MB_ROW_WIDTH_GEOMETRIE 50 +#define MB_ROW_WIDTH_AUDIO 50 +#define MB_ROW_WIDTH_LENGTH 40 +#define MB_ROW_WIDTH_SIZE 45 + +const int m_defaultRowWidth[MB_INFO_MAX_NUMBER+1] = +{ + MB_ROW_WIDTH_FILENAME , + MB_ROW_WIDTH_FILEPATH, + MB_ROW_WIDTH_TITLE, + MB_ROW_WIDTH_SERIE, + MB_ROW_WIDTH_INFO1, + MB_ROW_WIDTH_MAJOR_GENRE , + MB_ROW_WIDTH_MINOR_GENRE , + MB_ROW_WIDTH_INFO2 , + MB_ROW_WIDTH_PARENTAL_LOCKAGE , + MB_ROW_WIDTH_CHANNEL, + MB_ROW_WIDTH_BOOKMARK, + MB_ROW_WIDTH_QUALITY , + MB_ROW_WIDTH_PREVPLAYDATE, + MB_ROW_WIDTH_RECORDDATE , + MB_ROW_WIDTH_PRODDATE , + MB_ROW_WIDTH_COUNTRY , + MB_ROW_WIDTH_GEOMETRIE, + MB_ROW_WIDTH_AUDIO , + MB_ROW_WIDTH_LENGTH, + MB_ROW_WIDTH_SIZE, + 0 //MB_ROW_WIDTH_MAX_NUMBER +}; +static MI_MOVIE_INFO* playing_info; +//------------------------------------------------------------------------ +// sorting +//------------------------------------------------------------------------ +#define FILEBROWSER_NUMBER_OF_SORT_VARIANTS 5 + +bool sortDirection = 0; + +bool compare_to_lower(const char a, const char b) +{ + return tolower(a) < tolower(b); +}; + +// sort operators +bool sortByTitle (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if (std::lexicographical_compare(a->epgTitle.begin(), a->epgTitle.end(), b->epgTitle.begin(), b->epgTitle.end(), compare_to_lower)) + return true; + if (std::lexicographical_compare(b->epgTitle.begin(), b->epgTitle.end(), a->epgTitle.begin(), a->epgTitle.end(), compare_to_lower)) + return false; + return a->file.Time < b->file.Time; +} +bool sortByGenre (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if (std::lexicographical_compare(a->epgInfo1.begin(), a->epgInfo1.end(), b->epgInfo1.begin(), b->epgInfo1.end(), compare_to_lower)) + return true; + if (std::lexicographical_compare(b->epgInfo1.begin(), b->epgInfo1.end(), a->epgInfo1.begin(), a->epgInfo1.end(), compare_to_lower)) + return false; + return sortByTitle(a,b); +} +bool sortByChannel (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if (std::lexicographical_compare(a->epgChannel.begin(), a->epgChannel.end(), b->epgChannel.begin(), b->epgChannel.end(), compare_to_lower)) + return true; + if (std::lexicographical_compare(b->epgChannel.begin(), b->epgChannel.end(), a->epgChannel.begin(), a->epgChannel.end(), compare_to_lower)) + return false; + return sortByTitle(a,b); +} +bool sortByFileName (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if (std::lexicographical_compare(a->file.getFileName().begin(), a->file.getFileName().end(), b->file.getFileName().begin(), b->file.getFileName().end(), compare_to_lower)) + return true; + if (std::lexicographical_compare(b->file.getFileName().begin(), b->file.getFileName().end(), a->file.getFileName().begin(), a->file.getFileName().end(), compare_to_lower)) + return false; + return a->file.Time < b->file.Time; +} +bool sortByRecordDate (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->file.Time > b->file.Time ; + else + return a->file.Time < b->file.Time ; +} +bool sortBySize (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->file.Size > b->file.Size; + else + return a->file.Size < b->file.Size; +} +bool sortByAge (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->parentalLockAge > b->parentalLockAge; + else + return a->parentalLockAge < b->parentalLockAge; +} +bool sortByQuality (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->quality > b->quality; + else + return a->quality < b->quality; +} +bool sortByDir (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->dirItNr > b->dirItNr; + else + return a->dirItNr < b->dirItNr; +} + +bool sortByLastPlay (const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) +{ + if(sortDirection) + return a->dateOfLastPlay > b->dateOfLastPlay; + else + return a->dateOfLastPlay < b->dateOfLastPlay; +} + +bool (* const sortBy[MB_INFO_MAX_NUMBER+1])(const MI_MOVIE_INFO* a, const MI_MOVIE_INFO* b) = +{ + &sortByFileName , //MB_INFO_FILENAME = 0, + &sortByDir, //MB_INFO_FILEPATH = 1, + &sortByTitle, //MB_INFO_TITLE = 2, + NULL, //MB_INFO_SERIE = 3, + &sortByGenre, //MB_INFO_INFO1 = 4, + NULL, //MB_INFO_MAJOR_GENRE = 5, + NULL, //MB_INFO_MINOR_GENRE = 6, + NULL, //MB_INFO_INFO2 = 7, + &sortByAge, //MB_INFO_PARENTAL_LOCKAGE = 8, + &sortByChannel, //MB_INFO_CHANNEL = 9, + NULL, //MB_INFO_BOOKMARK = 10, + &sortByQuality, //MB_INFO_QUALITY = 11, + &sortByLastPlay, //MB_INFO_PREVPLAYDATE = 12, + &sortByRecordDate, //MB_INFO_RECORDDATE = 13, + NULL, //MB_INFO_PRODDATE = 14, + NULL, //MB_INFO_COUNTRY = 15, + NULL, //MB_INFO_GEOMETRIE = 16, + NULL, //MB_INFO_AUDIO = 17, + NULL, //MB_INFO_LENGTH = 18, + &sortBySize, //MB_INFO_SIZE = 19, + NULL //MB_INFO_MAX_NUMBER = 20 +}; +/************************************************************************ + Public API +************************************************************************/ + +CMovieBrowser::CMovieBrowser(const char* path): configfile ('\t') +{ + m_selectedDir = path; + //addDir(m_selectedDir); + CMovieBrowser(); +} + +CMovieBrowser::CMovieBrowser(): configfile ('\t') +{ + TRACE("$Id: moviebrowser.cpp,v 1.10 2006/09/11 21:11:35 guenther Exp $\r\n"); + init(); +} + +CMovieBrowser::~CMovieBrowser() +{ + //TRACE("[mb] del\r\n"); + //saveSettings(&m_settings); + hide(); + m_dir.clear(); + + m_dirNames.clear(); + for(unsigned int i=0; i < m_vMovieInfo.size(); i++) + { + m_vMovieInfo[i].audioPids.clear(); + } + m_vMovieInfo.clear(); + m_vHandleBrowserList.clear(); + m_vHandleRecordList.clear(); + m_vHandlePlayList.clear(); + m_vHandleSerienames.clear(); + + for(int i = 0; i < LF_MAX_ROWS; i++) + { + m_browserListLines.lineArray[i].clear(); + m_recordListLines.lineArray[i].clear(); + m_playListLines.lineArray[i].clear(); + m_FilterLines.lineArray[i].clear(); + } +} + +void CMovieBrowser::fileInfoStale(void) +{ + m_file_info_stale = true; + m_seriename_stale = true; + + // Also release memory buffers, since we have to reload this stuff next time anyhow + m_dirNames.clear(); + + for(unsigned int i=0; i < m_vMovieInfo.size(); i++) + { + m_vMovieInfo[i].audioPids.clear(); + } + + m_vMovieInfo.clear(); + m_vHandleBrowserList.clear(); + m_vHandleRecordList.clear(); + m_vHandlePlayList.clear(); + m_vHandleSerienames.clear(); + + for(int i = 0; i < LF_MAX_ROWS; i++) + { + m_browserListLines.lineArray[i].clear(); + m_recordListLines.lineArray[i].clear(); + m_playListLines.lineArray[i].clear(); + m_FilterLines.lineArray[i].clear(); + } +}; + +void CMovieBrowser::init(void) +{ + //TRACE("[mb]->init\r\n"); + initGlobalSettings(); + loadSettings(&m_settings); + + //restart_mb_timeout = 0; + m_file_info_stale = true; + m_seriename_stale = true; + + m_pcWindow = CFrameBuffer::getInstance(); + m_pcBrowser = NULL; + m_pcLastPlay = NULL; + m_pcLastRecord = NULL; + m_pcInfo = NULL; + + m_windowFocus = MB_FOCUS_BROWSER; + + m_pcFontFoot = FOOT_FONT; + m_pcFontTitle = TITLE_FONT; + + m_textTitle = g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD); + + m_currentStartPos = 0; + + m_movieSelectionHandler = NULL; + m_currentBrowserSelection = 0; + m_currentRecordSelection = 0; + m_currentPlaySelection = 0; + m_prevBrowserSelection = 0; + m_prevRecordSelection = 0; + m_prevPlaySelection = 0; + + m_storageType = MB_STORAGE_TYPE_NFS; + + m_parentalLock = m_settings.parentalLock; + + // check g_setting values + if(m_settings.gui >= MB_GUI_MAX_NUMBER) + m_settings.gui = MB_GUI_MOVIE_INFO; + + if(m_settings.sorting.direction >= MB_DIRECTION_MAX_NUMBER) + m_settings.sorting.direction = MB_DIRECTION_DOWN; + if(m_settings.sorting.item >= MB_INFO_MAX_NUMBER) + m_settings.sorting.item = MB_INFO_TITLE; + + if(m_settings.filter.item >= MB_INFO_MAX_NUMBER) + m_settings.filter.item = MB_INFO_MAX_NUMBER; + + if(m_settings.parentalLockAge >= MI_PARENTAL_MAX_NUMBER) + m_settings.parentalLockAge = MI_PARENTAL_OVER18; + if(m_settings.parentalLock >= MB_PARENTAL_LOCK_MAX_NUMBER) + m_settings.parentalLock = MB_PARENTAL_LOCK_OFF; + + if(m_settings.browserFrameHeight < MIN_BROWSER_FRAME_HEIGHT ) + m_settings.browserFrameHeight = MIN_BROWSER_FRAME_HEIGHT; + if(m_settings.browserFrameHeight > MAX_BROWSER_FRAME_HEIGHT) + m_settings.browserFrameHeight = MAX_BROWSER_FRAME_HEIGHT; + /***** Browser List **************/ + if(m_settings.browserRowNr == 0) + { + TRACE(" row error\r\n"); + // init browser row elements if not configured correctly by neutrino.config + m_settings.browserRowNr = 6; + m_settings.browserRowItem[0] = MB_INFO_TITLE; + m_settings.browserRowItem[1] = MB_INFO_INFO1; + m_settings.browserRowItem[2] = MB_INFO_RECORDDATE; + m_settings.browserRowItem[3] = MB_INFO_SIZE; + m_settings.browserRowItem[4] = MB_INFO_PARENTAL_LOCKAGE; + m_settings.browserRowItem[5] = MB_INFO_QUALITY; + m_settings.browserRowWidth[0] = m_defaultRowWidth[m_settings.browserRowItem[0]]; //300; + m_settings.browserRowWidth[1] = m_defaultRowWidth[m_settings.browserRowItem[1]]; //100; + m_settings.browserRowWidth[2] = m_defaultRowWidth[m_settings.browserRowItem[2]]; //80; + m_settings.browserRowWidth[3] = m_defaultRowWidth[m_settings.browserRowItem[3]]; //50; + m_settings.browserRowWidth[4] = m_defaultRowWidth[m_settings.browserRowItem[4]]; //30; + m_settings.browserRowWidth[5] = m_defaultRowWidth[m_settings.browserRowItem[5]]; //30; + } + + initFrames(); + initRows(); + //initDevelopment(); + + refreshLastPlayList(); + refreshLastRecordList(); + refreshBrowserList(); + refreshFilterList(); +#if 0 + TRACE_1("Frames\r\n\tScren:\t%3d,%3d,%3d,%3d\r\n\tMain:\t%3d,%3d,%3d,%3d\r\n\tTitle:\t%3d,%3d,%3d,%3d \r\n\tBrowsr:\t%3d,%3d,%3d,%3d \r\n\tPlay:\t%3d,%3d,%3d,%3d \r\n\tRecord:\t%3d,%3d,%3d,%3d\r\n\r\n", + g_settings.screen_StartX, + g_settings.screen_StartY, + g_settings.screen_EndX, + g_settings.screen_EndY, + m_cBoxFrame.iX, + m_cBoxFrame.iY, + m_cBoxFrame.iWidth, + m_cBoxFrame.iHeight, + m_cBoxFrameTitleRel.iX, + m_cBoxFrameTitleRel.iY, + m_cBoxFrameTitleRel.iWidth, + m_cBoxFrameTitleRel.iHeight, + m_cBoxFrameBrowserList.iX, + m_cBoxFrameBrowserList.iY, + m_cBoxFrameBrowserList.iWidth, + m_cBoxFrameBrowserList.iHeight, + m_cBoxFrameLastPlayList.iX, + m_cBoxFrameLastPlayList.iY, + m_cBoxFrameLastPlayList.iWidth, + m_cBoxFrameLastPlayList.iHeight, + m_cBoxFrameLastRecordList.iX, + m_cBoxFrameLastRecordList.iY, + m_cBoxFrameLastRecordList.iWidth, + m_cBoxFrameLastRecordList.iHeight + ); +#endif +} + +void CMovieBrowser::initGlobalSettings(void) +{ + //TRACE("[mb]->initGlobalSettings\r\n"); + + m_settings.gui = MB_GUI_MOVIE_INFO; + + m_settings.sorting.direction = MB_DIRECTION_DOWN; + m_settings.sorting.item = MB_INFO_TITLE; + + m_settings.filter.item = MB_INFO_MAX_NUMBER; + m_settings.filter.optionString = ""; + m_settings.filter.optionVar = 0; + + m_settings.parentalLockAge = MI_PARENTAL_OVER18; + m_settings.parentalLock = MB_PARENTAL_LOCK_OFF; + + for(int i = 0; i < MB_MAX_DIRS; i++) + { + m_settings.storageDir[i] = ""; + m_settings.storageDirUsed[i] = 0; + } + + /***** Browser List **************/ + m_settings.browserFrameHeight = g_settings.screen_EndY - g_settings.screen_StartY - 20 - ((g_settings.screen_EndY - g_settings.screen_StartY - 20)>>1) - (INTER_FRAME_SPACE>>1); + + m_settings.browserRowNr = 6; + m_settings.browserRowItem[0] = MB_INFO_TITLE; + m_settings.browserRowItem[1] = MB_INFO_INFO1; + m_settings.browserRowItem[2] = MB_INFO_RECORDDATE; + m_settings.browserRowItem[3] = MB_INFO_SIZE; + m_settings.browserRowItem[4] = MB_INFO_PARENTAL_LOCKAGE; + m_settings.browserRowItem[5] = MB_INFO_QUALITY; + m_settings.browserRowWidth[0] = m_defaultRowWidth[m_settings.browserRowItem[0]]; //300; + m_settings.browserRowWidth[1] = m_defaultRowWidth[m_settings.browserRowItem[1]]; //100; + m_settings.browserRowWidth[2] = m_defaultRowWidth[m_settings.browserRowItem[2]]; //80; + m_settings.browserRowWidth[3] = m_defaultRowWidth[m_settings.browserRowItem[3]]; //50; + m_settings.browserRowWidth[4] = m_defaultRowWidth[m_settings.browserRowItem[4]]; //30; + m_settings.browserRowWidth[5] = m_defaultRowWidth[m_settings.browserRowItem[5]]; //30; + + m_settings.storageDirMovieUsed = true; + m_settings.storageDirRecUsed = true; + m_settings.reload = true; + m_settings.remount = false; + m_settings.browser_serie_mode = 0; + m_settings.serie_auto_create = 0; +} + +void CMovieBrowser::initFrames(void) +{ + //TRACE("[mb]->initFrames\r\n"); + m_cBoxFrame.iX = g_settings.screen_StartX + 10; + m_cBoxFrame.iY = g_settings.screen_StartY + 10; + m_cBoxFrame.iWidth = g_settings.screen_EndX - g_settings.screen_StartX - 20; + m_cBoxFrame.iHeight = g_settings.screen_EndY - g_settings.screen_StartY - 20; + + m_cBoxFrameTitleRel.iX = 0; + m_cBoxFrameTitleRel.iY = 0; + m_cBoxFrameTitleRel.iWidth = m_cBoxFrame.iWidth; + m_cBoxFrameTitleRel.iHeight = m_pcFontTitle->getHeight(); +#ifndef FB_USE_PALETTE + if(m_cBoxFrameTitleRel.iHeight < PIC_H) m_cBoxFrameTitleRel.iHeight = PIC_H; +#endif + + m_cBoxFrameBrowserList.iX = m_cBoxFrame.iX; + m_cBoxFrameBrowserList.iY = m_cBoxFrame.iY + m_cBoxFrameTitleRel.iHeight; + m_cBoxFrameBrowserList.iWidth = m_cBoxFrame.iWidth; + m_cBoxFrameBrowserList.iHeight = m_settings.browserFrameHeight; //m_cBoxFrame.iHeight - (m_cBoxFrame.iHeight>>1) - (INTER_FRAME_SPACE>>1); + + m_cBoxFrameFootRel.iX = 0; + m_cBoxFrameFootRel.iY = m_cBoxFrame.iHeight - m_pcFontFoot->getHeight(); + m_cBoxFrameFootRel.iWidth = m_cBoxFrameBrowserList.iWidth; + m_cBoxFrameFootRel.iHeight = m_pcFontFoot->getHeight(); + + m_cBoxFrameLastPlayList.iX = m_cBoxFrameBrowserList.iX; + m_cBoxFrameLastPlayList.iY = m_cBoxFrameBrowserList.iY ; + m_cBoxFrameLastPlayList.iWidth = (m_cBoxFrameBrowserList.iWidth>>1) - (INTER_FRAME_SPACE>>1); + m_cBoxFrameLastPlayList.iHeight = m_cBoxFrameBrowserList.iHeight; + + m_cBoxFrameLastRecordList.iX = m_cBoxFrameLastPlayList.iX + m_cBoxFrameLastPlayList.iWidth + INTER_FRAME_SPACE; + m_cBoxFrameLastRecordList.iY = m_cBoxFrameLastPlayList.iY; + m_cBoxFrameLastRecordList.iWidth = m_cBoxFrame.iWidth - m_cBoxFrameLastPlayList.iWidth - INTER_FRAME_SPACE; + m_cBoxFrameLastRecordList.iHeight = m_cBoxFrameLastPlayList.iHeight; + + m_cBoxFrameInfo.iX = m_cBoxFrameBrowserList.iX; + m_cBoxFrameInfo.iY = m_cBoxFrameBrowserList.iY + m_cBoxFrameBrowserList.iHeight + INTER_FRAME_SPACE; + m_cBoxFrameInfo.iWidth = m_cBoxFrameBrowserList.iWidth; + m_cBoxFrameInfo.iHeight = m_cBoxFrame.iHeight - m_cBoxFrameBrowserList.iHeight - INTER_FRAME_SPACE - m_cBoxFrameFootRel.iHeight - m_cBoxFrameTitleRel.iHeight; + + m_cBoxFrameFilter.iX = m_cBoxFrameInfo.iX; + m_cBoxFrameFilter.iY = m_cBoxFrameInfo.iY; + m_cBoxFrameFilter.iWidth = m_cBoxFrameInfo.iWidth; + m_cBoxFrameFilter.iHeight = m_cBoxFrameInfo.iHeight; +} + +void CMovieBrowser::initRows(void) +{ + //TRACE("[mb]->initRows\r\n"); + + /***** Last Play List **************/ + m_settings.lastPlayRowNr = 2; + m_settings.lastPlayRow[0] = MB_INFO_TITLE; + m_settings.lastPlayRow[1] = MB_INFO_PREVPLAYDATE; + m_settings.lastPlayRow[2] = MB_INFO_MAX_NUMBER; + m_settings.lastPlayRow[3] = MB_INFO_MAX_NUMBER; + m_settings.lastPlayRow[4] = MB_INFO_MAX_NUMBER; + m_settings.lastPlayRow[5] = MB_INFO_MAX_NUMBER; + m_settings.lastPlayRowWidth[0] = 190; + m_settings.lastPlayRowWidth[1] = m_defaultRowWidth[m_settings.lastPlayRow[1]]; + m_settings.lastPlayRowWidth[2] = m_defaultRowWidth[m_settings.lastPlayRow[2]]; + m_settings.lastPlayRowWidth[3] = m_defaultRowWidth[m_settings.lastPlayRow[3]]; + m_settings.lastPlayRowWidth[4] = m_defaultRowWidth[m_settings.lastPlayRow[4]]; + m_settings.lastPlayRowWidth[5] = m_defaultRowWidth[m_settings.lastPlayRow[5]]; + + /***** Last Record List **************/ + m_settings.lastRecordRowNr = 2; + m_settings.lastRecordRow[0] = MB_INFO_TITLE; + m_settings.lastRecordRow[1] = MB_INFO_RECORDDATE; + m_settings.lastRecordRow[2] = MB_INFO_MAX_NUMBER; + m_settings.lastRecordRow[3] = MB_INFO_MAX_NUMBER; + m_settings.lastRecordRow[4] = MB_INFO_MAX_NUMBER; + m_settings.lastRecordRow[5] = MB_INFO_MAX_NUMBER; + m_settings.lastRecordRowWidth[0] = 190; + m_settings.lastRecordRowWidth[1] = m_defaultRowWidth[m_settings.lastRecordRow[1]]; + m_settings.lastRecordRowWidth[2] = m_defaultRowWidth[m_settings.lastRecordRow[2]]; + m_settings.lastRecordRowWidth[3] = m_defaultRowWidth[m_settings.lastRecordRow[3]]; + m_settings.lastRecordRowWidth[4] = m_defaultRowWidth[m_settings.lastRecordRow[4]]; + m_settings.lastRecordRowWidth[5] = m_defaultRowWidth[m_settings.lastRecordRow[5]]; +} + +void CMovieBrowser::initDevelopment(void) +{ + TRACE("[mb]->initDevelopment\r\n"); + std::string name; + name = "/mnt/movies/"; + //addDir(name); + name = "/mnt/record/"; + //addDir(name); + name = "/mnt/nfs/"; + //addDir(name); + +} + +void CMovieBrowser::defaultSettings(MB_SETTINGS* settings) +{ + CFile file; + file.Name = MOVIEBROWSER_SETTINGS_FILE; + delFile(file); + //configfile.clear(); + loadSettings(settings); +} + +bool CMovieBrowser::loadSettings(MB_SETTINGS* settings) +{ + bool result = true; + //TRACE("CMovieBrowser::loadSettings\r\n"); + //configfile.loadConfig(MOVIEBROWSER_SETTINGS_FILE); + if(configfile.loadConfig(MOVIEBROWSER_SETTINGS_FILE)) + { + settings->lastPlayMaxItems = configfile.getInt32("mb_lastPlayMaxItems", NUMBER_OF_MOVIES_LAST); + settings->lastRecordMaxItems = configfile.getInt32("mb_lastRecordMaxItems", NUMBER_OF_MOVIES_LAST); + settings->browser_serie_mode = configfile.getInt32("mb_browser_serie_mode", 0); + settings->serie_auto_create = configfile.getInt32("mb_serie_auto_create", 0); + + settings->gui = (MB_GUI)configfile.getInt32("mb_gui", MB_GUI_MOVIE_INFO); + + settings->sorting.item = (MB_INFO_ITEM)configfile.getInt32("mb_sorting_item", MB_INFO_TITLE); + settings->sorting.direction = (MB_DIRECTION)configfile.getInt32("mb_sorting_direction", MB_DIRECTION_UP); + + settings->filter.item = (MB_INFO_ITEM)configfile.getInt32("mb_filter_item", MB_INFO_INFO1); + settings->filter.optionString = configfile.getString("mb_filter_optionString", ""); + settings->filter.optionVar = configfile.getInt32("mb_filter_optionVar", 0); + + settings->parentalLockAge = (MI_PARENTAL_LOCKAGE)configfile.getInt32("mb_parentalLockAge", MI_PARENTAL_OVER18); + settings->parentalLock = (MB_PARENTAL_LOCK)configfile.getInt32("mb_parentalLock", MB_PARENTAL_LOCK_ACTIVE); + + settings->storageDirRecUsed = (bool)configfile.getInt32("mb_storageDir_rec", true ); + settings->storageDirMovieUsed = (bool)configfile.getInt32("mb_storageDir_movie", true ); + + settings->reload = (bool)configfile.getInt32("mb_reload", false ); + settings->remount = (bool)configfile.getInt32("mb_remount", false ); + + char cfg_key[81]; + for(int i = 0; i < MB_MAX_DIRS; i++) + { + sprintf(cfg_key, "mb_dir_%d", i); + settings->storageDir[i] = configfile.getString( cfg_key, "" ); + sprintf(cfg_key, "mb_dir_used%d", i); + settings->storageDirUsed[i] = configfile.getInt32( cfg_key,false ); + } + /* these variables are used for the listframes */ + settings->browserFrameHeight = configfile.getInt32("mb_browserFrameHeight", 250); + settings->browserRowNr = configfile.getInt32("mb_browserRowNr", 0); + for(int i = 0; i < MB_MAX_ROWS && i < settings->browserRowNr; i++) + { + sprintf(cfg_key, "mb_browserRowItem_%d", i); + settings->browserRowItem[i] = (MB_INFO_ITEM)configfile.getInt32(cfg_key, MB_INFO_MAX_NUMBER); + sprintf(cfg_key, "mb_browserRowWidth_%d", i); + settings->browserRowWidth[i] = configfile.getInt32(cfg_key, 50); + } + } + else + { + TRACE("CMovieBrowser::loadSettings failed\r\n"); + configfile.clear(); + result = false; + } + return (result); +} + +bool CMovieBrowser::saveSettings(MB_SETTINGS* settings) +{ + bool result = true; + TRACE("[mb] saveSettings\r\n"); + + configfile.setInt32("mb_lastPlayMaxItems", settings->lastPlayMaxItems); + configfile.setInt32("mb_lastRecordMaxItems", settings->lastRecordMaxItems); + configfile.setInt32("mb_browser_serie_mode", settings->browser_serie_mode); + configfile.setInt32("mb_serie_auto_create", settings->serie_auto_create); + + configfile.setInt32("mb_gui", settings->gui); + + configfile.setInt32("mb_sorting_item", settings->sorting.item); + configfile.setInt32("mb_sorting_direction", settings->sorting.direction); + + configfile.setInt32("mb_filter_item", settings->filter.item); + configfile.setString("mb_filter_optionString", settings->filter.optionString); + configfile.setInt32("mb_filter_optionVar", settings->filter.optionVar); + + configfile.setInt32("mb_storageDir_rec", settings->storageDirRecUsed ); + configfile.setInt32("mb_storageDir_movie", settings->storageDirMovieUsed ); + + configfile.setInt32("mb_parentalLockAge", settings->parentalLockAge); + configfile.setInt32("mb_parentalLock", settings->parentalLock); + + configfile.setInt32("mb_reload", settings->reload); + configfile.setInt32("mb_remount", settings->remount); + + char cfg_key[81]; + for(int i = 0; i < MB_MAX_DIRS; i++) + { + sprintf(cfg_key, "mb_dir_%d", i); + configfile.setString( cfg_key, settings->storageDir[i] ); + sprintf(cfg_key, "mb_dir_used%d", i); + configfile.setInt32( cfg_key, settings->storageDirUsed[i] ); // do not save this so far + } + /* these variables are used for the listframes */ + configfile.setInt32("mb_browserFrameHeight", settings->browserFrameHeight); + configfile.setInt32("mb_browserRowNr",settings->browserRowNr); + for(int i = 0; i < MB_MAX_ROWS && i < settings->browserRowNr; i++) + { + sprintf(cfg_key, "mb_browserRowItem_%d", i); + configfile.setInt32(cfg_key, settings->browserRowItem[i]); + sprintf(cfg_key, "mb_browserRowWidth_%d", i); + configfile.setInt32(cfg_key, settings->browserRowWidth[i]); + } + + configfile.saveConfig(MOVIEBROWSER_SETTINGS_FILE); + return (result); +} + +int CMovieBrowser::exec(CMenuTarget* parent, const std::string & actionKey) +{ + int returnval = menu_return::RETURN_REPAINT; + + if(actionKey == "loaddefault") + { + defaultSettings(&m_settings); + } + else if(actionKey == "save_options") + { + } + else if(actionKey == "show_movie_info_menu") + { + if(m_movieSelectionHandler != NULL) + showMovieInfoMenu(m_movieSelectionHandler); + } + else if(actionKey == "save_movie_info") + { + if(m_movieSelectionHandler != NULL) + m_movieInfo.saveMovieInfo( *m_movieSelectionHandler); + } + else if(actionKey == "save_movie_info_all") + { + std::vector * current_list=NULL; + + if(m_movieSelectionHandler != NULL) + { + if(m_windowFocus == MB_FOCUS_BROWSER) current_list = &m_vHandleBrowserList; + else if(m_windowFocus == MB_FOCUS_LAST_PLAY) current_list = &m_vHandlePlayList; + else if(m_windowFocus == MB_FOCUS_LAST_RECORD) current_list = &m_vHandleRecordList ; + + if(current_list != NULL) + { + CHintBox loadBox(LOCALE_MOVIEBROWSER_HEAD,g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_HEAD_UPDATE)); + loadBox.paint(); + for(unsigned int i = 0; i< current_list->size();i++) + { + if( !((*current_list)[i]->parentalLockAge != 0 && movieInfoUpdateAllIfDestEmptyOnly == true) && + movieInfoUpdateAll[MB_INFO_TITLE] ) + (*current_list)[i]->parentalLockAge = m_movieSelectionHandler->parentalLockAge; + + if( !(!(*current_list)[i]->serieName.empty() && movieInfoUpdateAllIfDestEmptyOnly == true) && + movieInfoUpdateAll[MB_INFO_SERIE] ) + (*current_list)[i]->serieName = m_movieSelectionHandler->serieName; + + if( !(!(*current_list)[i]->productionCountry.empty() && movieInfoUpdateAllIfDestEmptyOnly == true) && + movieInfoUpdateAll[MB_INFO_COUNTRY] ) + (*current_list)[i]->productionCountry = m_movieSelectionHandler->productionCountry; + + if( !((*current_list)[i]->genreMajor!=0 && movieInfoUpdateAllIfDestEmptyOnly == true) && + movieInfoUpdateAll[MB_INFO_MAJOR_GENRE] ) + (*current_list)[i]->genreMajor = m_movieSelectionHandler->genreMajor; + + if( !((*current_list)[i]->quality!=0 && movieInfoUpdateAllIfDestEmptyOnly == true) && + movieInfoUpdateAll[MB_INFO_QUALITY] ) + (*current_list)[i]->quality = m_movieSelectionHandler->quality; + + m_movieInfo.saveMovieInfo( *((*current_list)[i]) ); + } + loadBox.hide(); + } + } + } + else if(actionKey == "reload_movie_info") + { + loadMovies(); + refresh(); + } + else if(actionKey == "run") + { + if(parent) parent->hide (); + exec(NULL); + } + else if(actionKey == "book_clear_all") + { + if(m_movieSelectionHandler != NULL) + { + m_movieSelectionHandler->bookmarks.start =0; + m_movieSelectionHandler->bookmarks.end =0; + m_movieSelectionHandler->bookmarks.lastPlayStop =0; + for(int i=0; ibookmarks.user[i].name.empty(); + m_movieSelectionHandler->bookmarks.user[i].length =0; + m_movieSelectionHandler->bookmarks.user[i].pos =0; + } + } + } + return returnval; +} + +int CMovieBrowser::exec(const char* path) +{ + bool res = false; + + TRACE("[mb] start MovieBrowser\r\n"); + int timeout = -1; + int returnDefaultOnTimeout = true; + neutrino_msg_t msg; + neutrino_msg_data_t data; + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD)); + // might be removed, for development it is good to reload the settings at any startup for testing + //loadSettings(&m_settings); + //init(); // FIXME test -> crash + loadSettings(&m_settings); + initFrames(); + + // Clear all, to avoid 'jump' in screen + m_vHandleBrowserList.clear(); + m_vHandleRecordList.clear(); + m_vHandlePlayList.clear(); + + for(int i = 0; i < LF_MAX_ROWS; i++) + { + m_browserListLines.lineArray[i].clear(); + m_recordListLines.lineArray[i].clear(); + m_playListLines.lineArray[i].clear(); + } + + m_selectedDir = path; + + if(paint() == false) + return res;// paint failed due to less memory , exit + + if ( timeout == -1 ) + timeout = g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd( timeout ); + + if(m_settings.remount == true) + { + TRACE("[mb] remount\r\n"); + //umount automount dirs + for(int i = 0; i < NETWORK_NFS_NR_OF_ENTRIES; i++) + { + if(g_settings.network_nfs_automount[i]) + umount2(g_settings.network_nfs_local_dir[i],MNT_FORCE); + } + CFSMounter::automount(); + } + + refreshTitle(); + if(m_file_info_stale == true) + { + TRACE("[mb] reload\r\n"); + loadMovies(); + } + else + { + // since we cleared everything above, we have to refresh the list now. + refreshBrowserList(); + refreshLastPlayList(); + refreshLastRecordList(); + } + + // get old movie selection and set position in windows + m_currentBrowserSelection = m_prevBrowserSelection; + m_currentRecordSelection = m_prevRecordSelection; + m_currentPlaySelection = m_prevPlaySelection; + + m_pcBrowser->setSelectedLine(m_currentBrowserSelection); + m_pcLastRecord->setSelectedLine(m_currentRecordSelection); + m_pcLastPlay->setSelectedLine(m_currentPlaySelection); + + updateMovieSelection(); + //refreshMovieInfo(); + + onSetGUIWindow(m_settings.gui); + + bool loop = true; + bool result; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + result = onButtonPress(msg); + if(result == false) + { + if (msg == CRCInput::RC_timeout && returnDefaultOnTimeout) + { + TRACE("[mb] Timerevent\n"); + //if( restart_mb_timeout == 1) + // restart_mb_timeout = 0; + //else + loop = false; + } + else if(msg == CRCInput::RC_ok) + { + m_currentStartPos = 0; + + if(m_movieSelectionHandler != NULL) + { + // If there is any available bookmark, show the bookmark menu + if( m_movieSelectionHandler->bookmarks.lastPlayStop != 0 || + m_movieSelectionHandler->bookmarks.start != 0) + { + TRACE("[mb] stop: %d start:%d \r\n",m_movieSelectionHandler->bookmarks.lastPlayStop,m_movieSelectionHandler->bookmarks.start); + m_currentStartPos = showStartPosSelectionMenu(); // display start menu m_currentStartPos = + } + if(m_currentStartPos >= 0) { + playing_info = m_movieSelectionHandler; + TRACE("[mb] start pos: %d s\r\n",m_currentStartPos); + res = true; + loop = false; + } else + refresh(); + } + } + else if (msg == CRCInput::RC_home) + { + loop = false; + } + else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + //FIXME do nothing ? + } + else if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + { + TRACE("[mb]->exec: getInstance\r\n"); + //res = menu_return::RETURN_EXIT_ALL; + loop = false; + } + } + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(timeout); // calcualate next timeout + } + hide(); + //TRACE(" return %d\r\n",res); + + m_prevBrowserSelection = m_currentBrowserSelection; + m_prevRecordSelection = m_currentRecordSelection; + m_prevPlaySelection = m_currentPlaySelection; + + saveSettings(&m_settings); // might be better done in ~CMovieBrowser, but for any reason this does not work if MB is killed by neutrino shutdown + + // make stale if we should reload the next time, but not if movie has to be played + if(m_settings.reload == true && res == false) + { + TRACE("[mb] force reload next time\r\n"); + fileInfoStale(); + } + + //CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + return (res); +} + +void CMovieBrowser::hide(void) +{ + //TRACE("[mb]->Hide\r\n"); + m_pcWindow->paintBackground(); + if (m_pcFilter != NULL) + { + m_currentFilterSelection = m_pcFilter->getSelectedLine(); + delete m_pcFilter; + m_pcFilter = NULL; + } + if (m_pcBrowser != NULL) + { + m_currentBrowserSelection = m_pcBrowser->getSelectedLine(); + delete m_pcBrowser; + m_pcBrowser = NULL; + } + if (m_pcLastPlay != NULL) + { + m_currentPlaySelection = m_pcLastPlay->getSelectedLine(); + delete m_pcLastPlay; + m_pcLastPlay = NULL; + } + if (m_pcLastRecord != NULL) + { + m_currentRecordSelection = m_pcLastRecord->getSelectedLine(); + delete m_pcLastRecord; + m_pcLastRecord = NULL; + } + if (m_pcInfo != NULL) delete m_pcInfo; + //if (m_pcWindow != NULL) delete m_pcWindow; + + //m_pcWindow = NULL; + m_pcInfo = NULL; + +} + +int CMovieBrowser::paint(void) +{ + TRACE("[mb]->Paint\r\n"); + + //CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD)); + + //Font* font = TEXT_FONT; + Font* font = NULL; + + m_pcBrowser = new CListFrame(&m_browserListLines, font, CListFrame::SCROLL | CListFrame::HEADER_LINE, + &m_cBoxFrameBrowserList); + m_pcLastPlay = new CListFrame(&m_playListLines, font, CListFrame::SCROLL | CListFrame::HEADER_LINE | CListFrame::TITLE, + &m_cBoxFrameLastPlayList, g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD_PLAYLIST), + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]); + m_pcLastRecord = new CListFrame(&m_recordListLines, font, CListFrame::SCROLL | CListFrame::HEADER_LINE | CListFrame::TITLE, + &m_cBoxFrameLastRecordList, g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD_RECORDLIST), + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]); + m_pcFilter = new CListFrame(&m_FilterLines, font, CListFrame::SCROLL | CListFrame::TITLE, + &m_cBoxFrameFilter, g_Locale->getText(LOCALE_MOVIEBROWSER_HEAD_FILTER), + g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]); + m_pcInfo = new CTextBox(" ", NULL, CTextBox::SCROLL, &m_cBoxFrameInfo); + + + if( m_pcBrowser == NULL || m_pcLastPlay == NULL || + m_pcLastRecord == NULL || m_pcInfo == NULL || m_pcFilter == NULL) + { + TRACE("[mb] paint, ERROR: not enought memory to allocate windows"); + if (m_pcFilter != NULL)delete m_pcFilter; + if (m_pcBrowser != NULL)delete m_pcBrowser; + if (m_pcLastPlay != NULL) delete m_pcLastPlay; + if (m_pcLastRecord != NULL)delete m_pcLastRecord; + if (m_pcInfo != NULL) delete m_pcInfo; + + m_pcInfo = NULL; + m_pcLastPlay = NULL; + m_pcLastRecord = NULL; + m_pcBrowser = NULL; + m_pcFilter = NULL; + + return (false); + } + //onSetGUIWindow(m_settings.gui); + //refreshTitle(); + //refreshFoot(); + refreshLCD(); + //refresh(); + return (true); +} + +void CMovieBrowser::refresh(void) +{ + //TRACE("[mb]->refresh\r\n"); + + refreshTitle(); + if (m_pcBrowser != NULL && m_showBrowserFiles == true ) + m_pcBrowser->refresh(); + if (m_pcLastPlay != NULL && m_showLastPlayFiles == true ) + m_pcLastPlay->refresh(); + if (m_pcLastRecord != NULL && m_showLastRecordFiles == true) + m_pcLastRecord->refresh(); + if (m_pcInfo != NULL && m_showMovieInfo == true) + refreshMovieInfo(); + //m_pcInfo->refresh(); + if (m_pcFilter != NULL && m_showFilter == true) + m_pcFilter->refresh(); + + refreshFoot(); + refreshLCD(); +} + +std::string CMovieBrowser::getCurrentDir(void) +{ + return(m_selectedDir); +} + +CFile* CMovieBrowser::getSelectedFile(void) +{ + //TRACE("[mb]->getSelectedFile: %s\r\n",m_movieSelectionHandler->file.Name.c_str()); + + if(m_movieSelectionHandler != NULL) + return(&m_movieSelectionHandler->file); + else + return(NULL); +} + +void CMovieBrowser::refreshMovieInfo(void) +{ +//TRACE("[mb]->refreshMovieInfo m_vMovieInfo.size %d\n", m_vMovieInfo.size()); + std::string emptytext = " "; + if(m_vMovieInfo.size() <= 0) { + if(m_pcInfo != NULL) + m_pcInfo->setText(&emptytext); + return; + } + if (m_movieSelectionHandler == NULL) + { + // There is no selected element, clear LCD + m_pcInfo->setText(&emptytext); + } + else + { + bool logo_ok = false; + int divx = 720/m_cBoxFrameInfo.iHeight; + int picw = 720/divx; + int pich = 576/divx; +#ifndef FB_USE_PALETTE + std::string fname = m_movieSelectionHandler->file.Name; + strReplace(fname, ".ts", ".bmp"); +//printf("screenshot name: %s\n", fname.c_str()); + logo_ok = !access(fname.c_str(), F_OK); +#endif + m_pcInfo->setText(&m_movieSelectionHandler->epgInfo2, logo_ok ? m_cBoxFrameInfo.iWidth-picw-20: 0); + +#ifndef FB_USE_PALETTE +//printf("refreshMovieInfo: EpgId %llx id %llx y %d\n", m_movieSelectionHandler->epgEpgId, m_movieSelectionHandler->epgId, m_cBoxFrameTitleRel.iY); + int lx = m_cBoxFrame.iX+m_cBoxFrameTitleRel.iX+m_cBoxFrameTitleRel.iWidth-PIC_W-10; + int ly = m_cBoxFrameTitleRel.iY+m_cBoxFrame.iY+ (m_cBoxFrameTitleRel.iHeight-PIC_H)/2; + m_pcWindow->paintBoxRel(lx, ly, PIC_W, PIC_H, TITLE_BACKGROUND_COLOR); + g_PicViewer->DisplayLogo(m_movieSelectionHandler->epgEpgId >>16, lx, ly, PIC_W, PIC_H); + if(logo_ok) { +#if 0 + lx = m_cBoxFrameInfo.iX+m_cBoxFrameInfo.iWidth - picw -10; + ly = m_cBoxFrameInfo.iY + (m_cBoxFrameInfo.iHeight-pich)/2; + g_PicViewer->DisplayImage(fname, lx, ly, picw, pich); +#endif + lx = m_cBoxFrameInfo.iX+m_cBoxFrameInfo.iWidth - picw -10; + ly = m_cBoxFrameInfo.iY + (m_cBoxFrameInfo.iHeight-pich)/2; + m_pcWindow->paintVLineRel(lx, ly, pich, COL_WHITE); + m_pcWindow->paintVLineRel(lx+picw, ly, pich, COL_WHITE); + m_pcWindow->paintHLineRel(lx, picw, ly, COL_WHITE); + m_pcWindow->paintHLineRel(lx, picw, ly+pich, COL_WHITE); + g_PicViewer->DisplayImage(fname, lx+3, ly+3, picw-3, pich-3); + } +#endif + } +} + +void CMovieBrowser::refreshLCD(void) +{ + if(m_vMovieInfo.size() <= 0) return; + + //CVFD * lcd = CVFD::getInstance(); + if(m_movieSelectionHandler == NULL) + { + // There is no selected element, clear LCD + //lcd->showMenuText(0, " ", -1, true); // UTF-8 + //lcd->showMenuText(1, " ", -1, true); // UTF-8 + } + else + { + CVFD::getInstance()->showMenuText(0, m_movieSelectionHandler->epgTitle.c_str(), -1, true); // UTF-8 + //lcd->showMenuText(1, m_movieSelectionHandler->epgInfo1.c_str(), -1, true); // UTF-8 + } +} + +void CMovieBrowser::refreshFilterList(void) +{ + TRACE("[mb]->refreshFilterList %d\r\n",m_settings.filter.item); + + std::string string_item; + + m_FilterLines.rows = 1; + m_FilterLines.lineArray[0].clear(); + m_FilterLines.rowWidth[0] = 400; + m_FilterLines.lineHeader[0]= ""; + + if(m_vMovieInfo.size() <= 0) + return; // exit here if nothing else is to do + + if(m_settings.filter.item == MB_INFO_MAX_NUMBER) + { + // show Main List + string_item = g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_GENRE_MAJOR); + m_FilterLines.lineArray[0].push_back(string_item); + string_item = g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_INFO1); + m_FilterLines.lineArray[0].push_back(string_item); + string_item = g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_PATH); + m_FilterLines.lineArray[0].push_back(string_item); + string_item = g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_SERIE); + m_FilterLines.lineArray[0].push_back(string_item); + } + else + { + std::string tmp = g_Locale->getText(LOCALE_MENU_BACK); + m_FilterLines.lineArray[0].push_back(tmp); + + if(m_settings.filter.item == MB_INFO_FILEPATH) + { + for(unsigned int i =0 ; i < m_dirNames.size() ;i++) + { + m_FilterLines.lineArray[0].push_back(m_dirNames[i]); + } + } + else if(m_settings.filter.item == MB_INFO_INFO1) + { + for(unsigned int i = 0; i < m_vMovieInfo.size(); i++) + { + bool found = false; + for(unsigned int t = 0; t < m_FilterLines.lineArray[0].size() && found == false; t++) + { + if(strcmp(m_FilterLines.lineArray[0][t].c_str(),m_vMovieInfo[i].epgInfo1.c_str()) == 0) + found = true; + } + if(found == false) + m_FilterLines.lineArray[0].push_back(m_vMovieInfo[i].epgInfo1); + } + } + else if(m_settings.filter.item == MB_INFO_MAJOR_GENRE) + { + for(int i = 0; i < GENRE_ALL_COUNT; i++) + { + std::string tmp = g_Locale->getText(GENRE_ALL[i].value); + m_FilterLines.lineArray[0].push_back(tmp); + } + } + else if(m_settings.filter.item == MB_INFO_SERIE) + { + updateSerienames(); + for(unsigned int i = 0; i < m_vHandleSerienames.size(); i++) + { + m_FilterLines.lineArray[0].push_back(m_vHandleSerienames[i]->serieName); + } + } + } + m_pcFilter->setLines(&m_FilterLines); +} + +void CMovieBrowser::refreshLastPlayList(void) //P2 +{ + //TRACE("[mb]->refreshlastPlayList \r\n"); + std::string string_item; + + // Initialise and clear list array + m_playListLines.rows = m_settings.lastPlayRowNr; + for(int row = 0 ;row < m_settings.lastPlayRowNr; row++) + { + m_playListLines.lineArray[row].clear(); + m_playListLines.rowWidth[row] = m_settings.lastPlayRowWidth[row]; + m_playListLines.lineHeader[row]= g_Locale->getText(m_localizedItemName[m_settings.lastPlayRow[row]]); + } + m_vHandlePlayList.clear(); + + if(m_vMovieInfo.size() <= 0) { + if(m_pcLastPlay != NULL) + m_pcLastPlay->setLines(&m_playListLines); + return; // exit here if nothing else is to do + } + + MI_MOVIE_INFO* movie_handle; + // prepare Browser list for sorting and filtering + for(unsigned int file=0; file < m_vMovieInfo.size(); file++) + { + if( /*isFiltered(m_vMovieInfo[file]) == false &&*/ + isParentalLock(m_vMovieInfo[file]) == false) + { + movie_handle = &(m_vMovieInfo[file]); + m_vHandlePlayList.push_back(movie_handle); + } + } + // sort the not filtered files + onSortMovieInfoHandleList(m_vHandlePlayList,MB_INFO_PREVPLAYDATE,MB_DIRECTION_DOWN); + + for( int handle=0; handle < (int) m_vHandlePlayList.size() && handle < m_settings.lastPlayMaxItems ;handle++) + { + for(int row = 0; row < m_settings.lastPlayRowNr ;row++) + { + if ( getMovieInfoItem(*m_vHandlePlayList[handle], m_settings.lastPlayRow[row], &string_item) == false) + { + string_item = "n/a"; + if(m_settings.lastPlayRow[row] == MB_INFO_TITLE) + getMovieInfoItem(*m_vHandlePlayList[handle], MB_INFO_FILENAME, &string_item); + } + m_playListLines.lineArray[row].push_back(string_item); + } + } + m_pcLastPlay->setLines(&m_playListLines); + + m_currentPlaySelection = m_pcLastPlay->getSelectedLine(); + // update selected movie if browser is in the focus + if (m_windowFocus == MB_FOCUS_LAST_PLAY) + { + updateMovieSelection(); + } +} + +void CMovieBrowser::refreshLastRecordList(void) //P2 +{ + //TRACE("[mb]->refreshLastRecordList \r\n"); + std::string string_item; + + // Initialise and clear list array + m_recordListLines.rows = m_settings.lastRecordRowNr; + for(int row = 0 ;row < m_settings.lastRecordRowNr; row++) + { + m_recordListLines.lineArray[row].clear(); + m_recordListLines.rowWidth[row] = m_settings.lastRecordRowWidth[row]; + m_recordListLines.lineHeader[row]= g_Locale->getText(m_localizedItemName[m_settings.lastRecordRow[row]]); + } + m_vHandleRecordList.clear(); + + if(m_vMovieInfo.size() <= 0) { + if(m_pcLastRecord != NULL) + m_pcLastRecord->setLines(&m_recordListLines); + return; // exit here if nothing else is to do + } + + MI_MOVIE_INFO* movie_handle; + // prepare Browser list for sorting and filtering + for(unsigned int file=0; file < m_vMovieInfo.size() ;file++) + { + if( /*isFiltered(m_vMovieInfo[file]) == false &&*/ + isParentalLock(m_vMovieInfo[file]) == false) + { + movie_handle = &(m_vMovieInfo[file]); + m_vHandleRecordList.push_back(movie_handle); + } + } + // sort the not filtered files + onSortMovieInfoHandleList(m_vHandleRecordList,MB_INFO_RECORDDATE,MB_DIRECTION_DOWN); + + for( int handle=0; handle < (int) m_vHandleRecordList.size() && handle < m_settings.lastRecordMaxItems ;handle++) + { + for(int row = 0; row < m_settings.lastRecordRowNr ;row++) + { + if ( getMovieInfoItem(*m_vHandleRecordList[handle], m_settings.lastRecordRow[row], &string_item) == false) + { + string_item = "n/a"; + if(m_settings.lastRecordRow[row] == MB_INFO_TITLE) + getMovieInfoItem(*m_vHandleRecordList[handle], MB_INFO_FILENAME, &string_item); + } + m_recordListLines.lineArray[row].push_back(string_item); + } + } + + m_pcLastRecord->setLines(&m_recordListLines); + + m_currentRecordSelection = m_pcLastRecord->getSelectedLine(); + // update selected movie if browser is in the focus + if (m_windowFocus == MB_FOCUS_LAST_RECORD) + { + updateMovieSelection(); + } +} + +void CMovieBrowser::refreshBrowserList(void) //P1 +{ + //TRACE("[mb]->refreshBrowserList \r\n"); + std::string string_item; + + // Initialise and clear list array + m_browserListLines.rows = m_settings.browserRowNr; + for(int row = 0; row < m_settings.browserRowNr; row++) + { + m_browserListLines.lineArray[row].clear(); + m_browserListLines.rowWidth[row] = m_settings.browserRowWidth[row]; + m_browserListLines.lineHeader[row]= g_Locale->getText(m_localizedItemName[m_settings.browserRowItem[row]]); + } + m_vHandleBrowserList.clear(); + + if(m_vMovieInfo.size() <= 0) + { + m_currentBrowserSelection = 0; + m_movieSelectionHandler = NULL; + if(m_pcBrowser != NULL) + m_pcBrowser->setLines(&m_browserListLines);//FIXME last delete test + return; // exit here if nothing else is to do + } + + MI_MOVIE_INFO* movie_handle; + // prepare Browser list for sorting and filtering + for(unsigned int file=0; file < m_vMovieInfo.size(); file++) + { + if( isFiltered(m_vMovieInfo[file]) == false && + isParentalLock(m_vMovieInfo[file]) == false && + (m_settings.browser_serie_mode == 0 || m_vMovieInfo[file].serieName.empty() || m_settings.filter.item == MB_INFO_SERIE) ) + { + movie_handle = &(m_vMovieInfo[file]); + m_vHandleBrowserList.push_back(movie_handle); + } + } + // sort the not filtered files + onSortMovieInfoHandleList(m_vHandleBrowserList,m_settings.sorting.item,MB_DIRECTION_AUTO); + + for(unsigned int handle=0; handle < m_vHandleBrowserList.size() ;handle++) + { + for(int row = 0; row < m_settings.browserRowNr ;row++) + { + if ( getMovieInfoItem(*m_vHandleBrowserList[handle], m_settings.browserRowItem[row], &string_item) == false) + { + string_item = "n/a"; + if(m_settings.browserRowItem[row] == MB_INFO_TITLE) + getMovieInfoItem(*m_vHandleBrowserList[handle], MB_INFO_FILENAME, &string_item); + } + m_browserListLines.lineArray[row].push_back(string_item); + } + } + m_pcBrowser->setLines(&m_browserListLines); + + m_currentBrowserSelection = m_pcBrowser->getSelectedLine(); + // update selected movie if browser is in the focus + if (m_windowFocus == MB_FOCUS_BROWSER) + { + updateMovieSelection(); + } +} + +void CMovieBrowser::refreshBookmarkList(void) // P3 +{ + //TRACE("[mb]->refreshBookmarkList \r\n"); +} + +void CMovieBrowser::refreshTitle(void) +{ + //Paint Text Background + //TRACE("[mb]->refreshTitle : %s\r\n",m_textTitle.c_str()); + m_pcWindow->paintBoxRel(m_cBoxFrame.iX+m_cBoxFrameTitleRel.iX, m_cBoxFrame.iY+ m_cBoxFrameTitleRel.iY, + m_cBoxFrameTitleRel.iWidth, m_cBoxFrameTitleRel.iHeight, TITLE_BACKGROUND_COLOR, ROUND_RADIUS, 1); + + m_pcFontTitle->RenderString(m_cBoxFrame.iX+m_cBoxFrameTitleRel.iX + TEXT_BORDER_WIDTH, m_cBoxFrame.iY+m_cBoxFrameTitleRel.iY + m_cBoxFrameTitleRel.iHeight, m_cBoxFrameTitleRel.iWidth - (TEXT_BORDER_WIDTH << 1), m_textTitle.c_str(), TITLE_FONT_COLOR, 0, true); // UTF-8 +} + +#define ADD_FOOT_HEIGHT 4 +void CMovieBrowser::refreshFoot(void) +{ + //TRACE("[mb]->refreshButtonLine \r\n"); + int color = (CFBWindow::color_t) COL_MENUHEAD; + + std::string filter_text = g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_FILTER); + filter_text += m_settings.filter.optionString; + std::string sort_text = g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_SORT); + sort_text += g_Locale->getText(m_localizedItemName[m_settings.sorting.item]); + std::string ok_text = g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_PLAY); + + // draw the background first + m_pcWindow->paintBoxRel(m_cBoxFrame.iX+m_cBoxFrameFootRel.iX, m_cBoxFrame.iY+ m_cBoxFrameFootRel.iY, + m_cBoxFrameFootRel.iWidth, m_cBoxFrameFootRel.iHeight+ 6, + (CFBWindow::color_t)COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + + int width = m_cBoxFrameFootRel.iWidth>>2; + + int xpos1 = m_cBoxFrameFootRel.iX+10; + int width1 = width; + int xpos2 = xpos1 + width1; + int width2 = width + width; + int xpos4 = xpos2 + width2; + int width4 = width; + //int xpos4 = xpos3 + width3; + //int width4 = width; + + // draw Button blue (filter) + //xpos += ButtonWidth + ButtonSpacing; + // draw yellow (sort) + if (m_settings.gui != MB_GUI_LAST_PLAY && m_settings.gui != MB_GUI_LAST_RECORD) + { + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_RED, m_cBoxFrame.iX+xpos1, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)); + m_pcFontFoot->RenderString(m_cBoxFrame.iX+xpos1 + 20, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + m_cBoxFrameFootRel.iHeight + 4 , width1-30, sort_text.c_str(), (CFBWindow::color_t)color, 0, true); // UTF-8 + } + + if (m_settings.gui != MB_GUI_LAST_PLAY && m_settings.gui != MB_GUI_LAST_RECORD) + { + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, m_cBoxFrame.iX+xpos2, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)); + m_pcFontFoot->RenderString(m_cBoxFrame.iX+ xpos2 + 20, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + m_cBoxFrameFootRel.iHeight + 4 , width2 -30, filter_text.c_str(), (CFBWindow::color_t)color, 0, true); // UTF-8 + } + //std::string ok_text; + if(m_settings.gui == MB_GUI_FILTER && m_windowFocus == MB_FOCUS_FILTER) + { + ok_text = "select"; + } + else + { + ok_text = g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_PLAY); + } + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, m_cBoxFrame.iX+xpos4, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)); + m_pcFontFoot->RenderString(m_cBoxFrame.iX+xpos4 + 30, m_cBoxFrame.iY+m_cBoxFrameFootRel.iY + m_cBoxFrameFootRel.iHeight + 4 , width4-30, ok_text.c_str(), (CFBWindow::color_t)color, 0, true); // UTF-8 +} + +bool CMovieBrowser::onButtonPress(neutrino_msg_t msg) +{ +// TRACE("[mb]->onButtonPress %d\r\n",msg); + bool result = false; + + result = onButtonPressMainFrame(msg); + if(result == false) + { + // if Main Frame didnot process the button, the focused window may do + switch(m_windowFocus) + { + case MB_FOCUS_BROWSER: + result = onButtonPressBrowserList(msg); + break; + case MB_FOCUS_LAST_PLAY: + result = onButtonPressLastPlayList(msg); + break; + case MB_FOCUS_LAST_RECORD: + result = onButtonPressLastRecordList(msg); + break; + case MB_FOCUS_MOVIE_INFO: + result = onButtonPressMovieInfoList(msg); + break; + case MB_FOCUS_FILTER: + result = onButtonPressFilterList(msg); + break; + default: + break; + } + } + return (result); +} + +bool CMovieBrowser::onButtonPressMainFrame(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressMainFrame: %d\r\n",msg); + bool result = true; + + if (msg == CRCInput::RC_home) + { + if(m_settings.gui == MB_GUI_FILTER) + onSetGUIWindow(MB_GUI_MOVIE_INFO); + else + result = false; + } + else if (msg == CRCInput::RC_minus) + { + onSetGUIWindowPrev(); + //refreshMovieInfo(); + } + else if (msg == CRCInput::RC_plus) + { + onSetGUIWindowNext(); + //refreshMovieInfo(); + } + else if (msg == CRCInput::RC_green) + { + if(m_settings.gui == MB_GUI_MOVIE_INFO) + onSetGUIWindow(MB_GUI_FILTER); + else if(m_settings.gui == MB_GUI_FILTER) + onSetGUIWindow(MB_GUI_MOVIE_INFO); + // no effect if gui is last play or record + } + else if (msg == CRCInput::RC_yellow) + { + onSetFocusNext(); + } + else if (msg == CRCInput::RC_blue) + { + loadMovies(); + refresh(); + } + else if (msg == CRCInput::RC_red ) + { + if(m_settings.gui != MB_GUI_LAST_PLAY && m_settings.gui != MB_GUI_LAST_RECORD) + { + // sorting is not avialable for last play and record + do + { + if(m_settings.sorting.item + 1 >= MB_INFO_MAX_NUMBER) + m_settings.sorting.item = (MB_INFO_ITEM)0; + else + m_settings.sorting.item = (MB_INFO_ITEM)(m_settings.sorting.item + 1); + }while(sortBy[m_settings.sorting.item] == NULL); + + TRACE("[mb]->new sorting %d,%s\r\n",m_settings.sorting.item,g_Locale->getText(m_localizedItemName[m_settings.sorting.item])); + refreshBrowserList(); + refreshFoot(); + } + } + else if (msg == CRCInput::RC_spkr) + { + if(m_vMovieInfo.size() > 0) + { + if(m_movieSelectionHandler != NULL) + { + onDeleteFile(*m_movieSelectionHandler); + } + } + } + else if (msg == CRCInput::RC_help || msg == CRCInput::RC_info) + { + if(m_movieSelectionHandler != NULL) + { + m_movieInfo.showMovieInfo(*m_movieSelectionHandler); + //m_movieInfo.printDebugMovieInfo(*m_movieSelectionHandler); //FIXME + refresh(); + } + } + else if (msg == CRCInput::RC_setup) + { + if(m_movieSelectionHandler != NULL) + showMenu(m_movieSelectionHandler); + } + else if (msg == CRCInput::RC_text || msg == CRCInput::RC_radio) { + if(ShowMsgUTF (LOCALE_MESSAGEBOX_INFO, msg == CRCInput::RC_radio ? "Copy jumps from movie to new file ?" : "Copy jumps from movie to new files ?", CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, "Coping, please wait"); + hintBox->paint(); + sleep(1); + hintBox->hide(); + delete hintBox; + m_pcWindow->paintBackground(); // clear screen + off64_t res = copy_movie(m_movieSelectionHandler, &m_movieInfo, msg == CRCInput::RC_radio); + //g_RCInput->clearRCMsg(); + if(res == 0) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "Copy failed, is there jump bookmarks ? Or check free space.", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else + loadMovies(); + refresh(); + } + } + else if (msg == CRCInput::RC_audio) { +#if 0 + if((m_movieSelectionHandler == playing_info) && (NeutrinoMessages::mode_ts == CNeutrinoApp::getInstance()->getMode())) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "Impossible to cut playing movie.", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else +#endif + if(ShowMsgUTF (LOCALE_MESSAGEBOX_INFO, "Cut jumps from movie ?", CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, "Cutting, please wait"); + hintBox->paint(); + sleep(1); + hintBox->hide(); + delete hintBox; + m_pcWindow->paintBackground(); // clear screen + off64_t res = cut_movie(m_movieSelectionHandler, &m_movieInfo); + //g_RCInput->clearRCMsg(); + if(res == 0) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "Cut failed, is there jump bookmarks ? Or check free space.", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else { + loadMovies(); + } + refresh(); + } + } + else if (msg == CRCInput::RC_games) { + if((m_movieSelectionHandler == playing_info) && (NeutrinoMessages::mode_ts == CNeutrinoApp::getInstance()->getMode())) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "Impossible to truncate playing movie.", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else if(m_movieSelectionHandler->bookmarks.end == 0) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "No End bookmark defined!", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else { + if(ShowMsgUTF (LOCALE_MESSAGEBOX_INFO, "Truncate movie ?", CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, "Truncating, please wait"); + hintBox->paint(); + off64_t res = truncate_movie(m_movieSelectionHandler); + hintBox->hide(); + delete hintBox; + g_RCInput->clearRCMsg(); + if(res == 0) + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, "Truncate failed.", CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); + else { +//printf("New movie info: size %lld len %d\n", res, m_movieSelectionHandler->bookmarks.end/60); + m_movieInfo.saveMovieInfo( *m_movieSelectionHandler); + loadMovies(); + } + refresh(); + } + } + } else if (msg == CRCInput::RC_topleft) { + if (m_movieSelectionHandler != NULL) { + if(ShowMsgUTF (LOCALE_MESSAGEBOX_INFO, "Remove screenshot ?", CMessageBox::mbrNo, CMessageBox:: mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + std::string fname = m_movieSelectionHandler->file.Name; + strReplace(fname, ".ts", ".bmp"); + unlink(fname.c_str()); + refresh(); + } + } + } + else + { + //TRACE("[mb]->onButtonPressMainFrame none\r\n"); + result = false; + } + + return (result); +} + +bool CMovieBrowser::onButtonPressBrowserList(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressBrowserList %d\r\n",msg); + bool result = true; + + if(msg==CRCInput::RC_up) + { + m_pcBrowser->scrollLineUp(1); + } + else if (msg == CRCInput::RC_down) + { + m_pcBrowser->scrollLineDown(1); + } + else if (msg == CRCInput::RC_page_up) + { + m_pcBrowser->scrollPageUp(1); + } + else if (msg == CRCInput::RC_page_down) + { + m_pcBrowser->scrollPageDown(1); + } + else if (msg == CRCInput::RC_left) + { + m_pcBrowser->scrollPageUp(1); + } + else if (msg == CRCInput::RC_right) + { + m_pcBrowser->scrollPageDown(1); + } + else + { + // default + result = false; + } + if(result == true) + updateMovieSelection(); + + return (result); +} + +bool CMovieBrowser::onButtonPressLastPlayList(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressLastPlayList %d\r\n",msg); + bool result = true; + + if(msg==CRCInput::RC_up) + { + m_pcLastPlay->scrollLineUp(1); + } + else if (msg == CRCInput::RC_down) + { + m_pcLastPlay->scrollLineDown(1); + } + else if (msg == CRCInput::RC_left) + { + m_pcLastPlay->scrollPageUp(1); + } + else if (msg == CRCInput::RC_right) + { + m_pcLastPlay->scrollPageDown(1); + } + else + { + // default + result = false; + } + if(result == true) + updateMovieSelection(); + + return (result); +} + +bool CMovieBrowser::onButtonPressLastRecordList(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressLastRecordList %d\r\n",msg); + bool result = true; + + if(msg==CRCInput::RC_up) + { + m_pcLastRecord->scrollLineUp(1); + } + else if (msg == CRCInput::RC_down) + { + m_pcLastRecord->scrollLineDown(1); + } + else if (msg == CRCInput::RC_left) + { + m_pcLastRecord->scrollPageUp(1); + } + else if (msg == CRCInput::RC_right) + { + m_pcLastRecord->scrollPageDown(1); + } + else + { + // default + result = false; + } + + if(result == true) + updateMovieSelection(); + + return (result); +} + +bool CMovieBrowser::onButtonPressBookmarkList(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressBookmarkList %d\r\n",msg); + bool result = true; + + result = false; + return (result); +} + +bool CMovieBrowser::onButtonPressFilterList(neutrino_msg_t msg) +{ + //TRACE("[mb]->onButtonPressFilterList %d,%d\r\n",msg,m_settings.filter.item); + bool result = true; + + if(msg==CRCInput::RC_up) + { + m_pcFilter->scrollLineUp(1); + } + else if (msg == CRCInput::RC_down) + { + m_pcFilter->scrollLineDown(1); + } + else if (msg == CRCInput::RC_page_up) + { + m_pcFilter->scrollPageUp(1); + } + else if (msg == CRCInput::RC_page_down) + { + m_pcFilter->scrollPageDown(1); + } + else if (msg == CRCInput::RC_ok) + { + int selected_line = m_pcFilter->getSelectedLine(); + if(m_settings.filter.item == MB_INFO_MAX_NUMBER) + { + if(selected_line == 0) m_settings.filter.item = MB_INFO_MAJOR_GENRE; + if(selected_line == 1) m_settings.filter.item = MB_INFO_INFO1; + if(selected_line == 2) m_settings.filter.item = MB_INFO_FILEPATH; + if(selected_line == 3) m_settings.filter.item = MB_INFO_SERIE; + refreshFilterList(); + m_pcFilter->setSelectedLine(0); + } + else + { + if(selected_line == 0) + { + m_settings.filter.item = MB_INFO_MAX_NUMBER; + m_settings.filter.optionString = ""; + m_settings.filter.optionVar = 0; + refreshFilterList(); + m_pcFilter->setSelectedLine(0); + refreshBrowserList(); + refreshLastPlayList(); + refreshLastRecordList(); + refreshFoot(); + } + else + { + updateFilterSelection(); + } + } + } + else if (msg == CRCInput::RC_left) + { + m_pcFilter->scrollPageUp(1); + } + else if (msg == CRCInput::RC_right) + { + m_pcFilter->scrollPageDown(1); + } + else + { + // default + result = false; + } + + return (result); +} + +bool CMovieBrowser::onButtonPressMovieInfoList(neutrino_msg_t msg) +{ +// TRACE("[mb]->onButtonPressEPGInfoList %d\r\n",msg); + bool result = true; + + if(msg == CRCInput::RC_up) + { + m_pcInfo->scrollPageUp(1); + } + else if (msg == CRCInput::RC_down) + { + m_pcInfo->scrollPageDown(1); + } + else + { + // default + result = false; + } + + return (result); +} + +void CMovieBrowser::onDeleteFile(MI_MOVIE_INFO& movieSelectionHandler) +{ + //TRACE( "[onDeleteFile] "); + int test= movieSelectionHandler.file.Name.find(".ts"); + if(test == -1) + { + // not a TS file, return!!!!! + TRACE( "show_ts_info: not a TS file "); + } + else + { + std::string msg = g_Locale->getText(LOCALE_FILEBROWSER_DODELETE1); + msg += "\r\n "; + if (movieSelectionHandler.file.Name.length() > 40) + { + msg += movieSelectionHandler.file.Name.substr(0,40); + msg += "..."; + } + else + msg += movieSelectionHandler.file.Name; + + msg += "\r\n "; + msg += g_Locale->getText(LOCALE_FILEBROWSER_DODELETE2); + if (ShowMsgUTF(LOCALE_FILEBROWSER_DELETE, msg, CMessageBox::mbrNo, CMessageBox::mbYes|CMessageBox::mbNo)==CMessageBox::mbrYes) + { + delFile(movieSelectionHandler.file); + +#if 1 + int i = 1; + char newpath[1024]; + do { + sprintf(newpath, "%s.%03d", movieSelectionHandler.file.Name.c_str(), i); + if(access(newpath, R_OK)) { + break; + } else { + unlink(newpath); + TRACE(" delete file: %s\r\n", newpath); + } + i++; + } while(1); + std::string fname = movieSelectionHandler.file.Name; + strReplace(fname, ".ts", ".bmp"); + unlink(fname.c_str()); +#endif + + CFile file_xml = movieSelectionHandler.file; + if(m_movieInfo.convertTs2XmlName(&file_xml.Name) == true) + { + delFile(file_xml); + } + + m_vMovieInfo.erase( (std::vector::iterator)&movieSelectionHandler); + TRACE("List size: %d\n", m_vMovieInfo.size()); + //if(m_vMovieInfo.size() == 0) fileInfoStale(); + //if(m_vMovieInfo.size() == 0) onSetGUIWindow(m_settings.gui); + updateSerienames(); + refreshBrowserList(); + refreshLastPlayList(); + refreshLastRecordList(); + refreshMovieInfo(); + + //loadMovies(); // //TODO we might remove the handle from the handle list only, to avoid reload ..... + refresh(); + } + } +} + +void CMovieBrowser::onSetGUIWindow(MB_GUI gui) +{ + m_settings.gui = gui; + + if(gui == MB_GUI_MOVIE_INFO) + { + TRACE("[mb] browser info\r\n"); + // Paint these frames ... + m_showMovieInfo = true; + m_showBrowserFiles = true; + + // ... and hide these frames + m_showLastRecordFiles = false; + m_showLastPlayFiles = false; + m_showFilter = false; + + m_pcLastPlay->hide(); + m_pcLastRecord->hide(); + m_pcFilter->hide(); + m_pcBrowser->paint(); + onSetFocus(MB_FOCUS_BROWSER); + m_pcInfo->paint(); + refreshMovieInfo(); + } + else if(gui == MB_GUI_LAST_PLAY) + { + TRACE("[mb] last play \r\n"); + // Paint these frames ... + m_showLastRecordFiles = true; + m_showLastPlayFiles = true; + m_showMovieInfo = true; + + // ... and hide these frames + m_showBrowserFiles = false; + m_showFilter = false; + + m_pcBrowser->hide(); + m_pcFilter->hide(); + m_pcLastRecord->paint(); + m_pcLastPlay->paint(); + + onSetFocus(MB_FOCUS_LAST_PLAY); + m_pcInfo->paint(); + refreshMovieInfo(); + } + else if(gui == MB_GUI_LAST_RECORD) + { + TRACE("[mb] last record \r\n"); + // Paint these frames ... + m_showLastRecordFiles = true; + m_showLastPlayFiles = true; + m_showMovieInfo = true; + + // ... and hide these frames + m_showBrowserFiles = false; + m_showFilter = false; + + m_pcBrowser->hide(); + m_pcFilter->hide(); + m_pcLastRecord->paint(); + m_pcLastPlay->paint(); + + onSetFocus(MB_FOCUS_LAST_RECORD); + m_pcInfo->paint(); + refreshMovieInfo(); + } + else if(gui == MB_GUI_FILTER) + { + TRACE("[mb] filter \r\n"); + // Paint these frames ... + m_showFilter = true; + // ... and hide these frames + m_showMovieInfo = false; + m_pcInfo->hide(); + m_pcFilter->paint(); + + onSetFocus(MB_FOCUS_FILTER); + } +} + +void CMovieBrowser::onSetGUIWindowNext(void) +{ + if(m_settings.gui == MB_GUI_MOVIE_INFO ) + { + onSetGUIWindow(MB_GUI_LAST_PLAY); + } + else if(m_settings.gui == MB_GUI_LAST_PLAY) + { + onSetGUIWindow(MB_GUI_LAST_RECORD); + } + else + { + onSetGUIWindow(MB_GUI_MOVIE_INFO); + } +} + +void CMovieBrowser::onSetGUIWindowPrev(void) +{ + if(m_settings.gui == MB_GUI_MOVIE_INFO ) + { + onSetGUIWindow(MB_GUI_LAST_RECORD); + } + else if(m_settings.gui == MB_GUI_LAST_RECORD) + { + onSetGUIWindow(MB_GUI_LAST_PLAY); + } + else + { + onSetGUIWindow(MB_GUI_MOVIE_INFO); + } +} + +void CMovieBrowser::onSetFocus(MB_FOCUS new_focus) +{ + //TRACE("[mb]->onSetFocus %d \r\n",new_focus); + m_windowFocus = new_focus; + if(m_windowFocus == MB_FOCUS_BROWSER) + { + m_pcBrowser->showSelection(true); + m_pcLastRecord->showSelection(false); + m_pcLastPlay->showSelection(false); + m_pcFilter->showSelection(false); + //m_pcInfo->showSelection(false); + } + else if(m_windowFocus == MB_FOCUS_LAST_PLAY) + { + m_pcBrowser->showSelection(false); + m_pcLastRecord->showSelection(false); + m_pcLastPlay->showSelection(true); + m_pcFilter->showSelection(false); + //m_pcInfo->showSelection(false); + } + else if(m_windowFocus == MB_FOCUS_LAST_RECORD) + { + m_pcBrowser->showSelection(false); + m_pcLastRecord->showSelection(true); + m_pcLastPlay->showSelection(false); + m_pcFilter->showSelection(false); + //m_pcInfo->showSelection(false); + } + else if(m_windowFocus == MB_FOCUS_MOVIE_INFO) + { + m_pcBrowser->showSelection(false); + m_pcLastRecord->showSelection(false); + m_pcLastPlay->showSelection(false); + m_pcFilter->showSelection(false); + //m_pcInfo->showSelection(true); + } + else if(m_windowFocus == MB_FOCUS_FILTER) + { + m_pcBrowser->showSelection(false); + m_pcLastRecord->showSelection(false); + m_pcLastPlay->showSelection(false); + m_pcFilter->showSelection(true); + //m_pcInfo->showSelection(false); + } + updateMovieSelection(); + refreshFoot(); +} + +void CMovieBrowser::onSetFocusNext(void) +{ + //TRACE("[mb]->onSetFocusNext \r\n"); + + if(m_settings.gui == MB_GUI_FILTER) + { + if(m_windowFocus == MB_FOCUS_BROWSER) + { + TRACE("[mb] MB_FOCUS_FILTER\r\n"); + onSetFocus(MB_FOCUS_FILTER); + } + else + { + TRACE("[mb] MB_FOCUS_BROWSER\r\n"); + onSetFocus(MB_FOCUS_BROWSER); + } + } + else if(m_settings.gui == MB_GUI_MOVIE_INFO) + { + if(m_windowFocus == MB_FOCUS_BROWSER) + { + TRACE("[mb] MB_FOCUS_MOVIE_INFO\r\n"); + onSetFocus(MB_FOCUS_MOVIE_INFO); + m_windowFocus = MB_FOCUS_MOVIE_INFO; + } + else + { + TRACE("[mb] MB_FOCUS_BROWSER\r\n"); + onSetFocus(MB_FOCUS_BROWSER); + } + } + else if(m_settings.gui == MB_GUI_LAST_PLAY) + { + if(m_windowFocus == MB_FOCUS_MOVIE_INFO) + { + onSetFocus(MB_FOCUS_LAST_PLAY); + } + else if(m_windowFocus == MB_FOCUS_LAST_PLAY) + { + onSetFocus(MB_FOCUS_MOVIE_INFO); + } + } + else if(m_settings.gui == MB_GUI_LAST_RECORD) + { + if(m_windowFocus == MB_FOCUS_MOVIE_INFO) + { + onSetFocus(MB_FOCUS_LAST_RECORD); + } + else if(m_windowFocus == MB_FOCUS_LAST_RECORD) + { + onSetFocus(MB_FOCUS_MOVIE_INFO); + } + } +} + +bool CMovieBrowser::onSortMovieInfoHandleList(std::vector& handle_list, MB_INFO_ITEM sort_item, MB_DIRECTION direction) +{ + //TRACE("sort: %d\r\n",direction); + if(handle_list.size() <= 0) + return (false); // nothing to sort, return immedately + if(sortBy[sort_item] == NULL) + return (false); + + if(direction == MB_DIRECTION_AUTO) + { + if( sort_item == MB_INFO_QUALITY || + sort_item == MB_INFO_PARENTAL_LOCKAGE || + sort_item == MB_INFO_PREVPLAYDATE || + sort_item == MB_INFO_RECORDDATE || + sort_item == MB_INFO_PRODDATE || + sort_item == MB_INFO_SIZE) + { + sortDirection = 1; + } + else + { + sortDirection = 0; + } + } + else if(direction == MB_DIRECTION_UP) + { + sortDirection = 0; + } + else + { + sortDirection = 1; + } + + //TRACE("sort: %d\r\n",sortDirection); + sort(handle_list.begin(), handle_list.end(), sortBy[sort_item]); + + return (true); +} + +void CMovieBrowser::updateDir(void) +{ + m_dir.clear(); +#if 0 + // check if there is a movie dir and if we should use it + if(g_settings.network_nfs_moviedir[0] != 0 ) + { + std::string name = g_settings.network_nfs_moviedir; + addDir(name,&m_settings.storageDirMovieUsed); + } +#endif + // check if there is a record dir and if we should use it + if(g_settings.network_nfs_recordingdir[0] != 0 ) + { + std::string name = g_settings.network_nfs_recordingdir; + addDir(name,&m_settings.storageDirRecUsed); + } + + for(int i = 0; i < MB_MAX_DIRS; i++) + { + if(!m_settings.storageDir[i].empty()) + addDir(m_settings.storageDir[i],&m_settings.storageDirUsed[i]); + } +} + +void CMovieBrowser::loadAllTsFileNamesFromStorage(void) +{ + //TRACE("[mb]->loadAllTsFileNamesFromStorage \r\n"); + bool result; + int i,size; + + m_movieSelectionHandler = NULL; + m_dirNames.clear(); + m_vMovieInfo.clear(); + + updateDir(); + + size = m_dir.size(); + for(i=0; i < size;i++) + { + if(*m_dir[i].used == true ) + result = loadTsFileNamesFromDir(m_dir[i].name); + } + + TRACE("[mb] Dir%d, Files:%d \r\n",m_dirNames.size(),m_vMovieInfo.size()); + /* + if(m_vMovieInfo.size() == 0) + { + std::string msg = g_Locale->getText(LOCALE_MOVIEBROWSER_ERROR_NO_MOVIES); + DisplayErrorMessage(msg.c_str()); + } + */ +} + +/************************************************************************ +Note: this function is used recursive, do not add any return within the body due to the recursive counter +************************************************************************/ +bool CMovieBrowser::loadTsFileNamesFromDir(const std::string & dirname) +{ + //TRACE("[mb]->loadTsFileNamesFromDir %s\r\n",dirname.c_str()); + + static int recursive_counter = 0; // recursive counter to be used to avoid hanging + bool result = false; + int file_found_in_dir = false; + + if (recursive_counter > 10) + { + TRACE("[mb]loadTsFileNamesFromDir: return->recoursive error\r\n"); + return (false); // do not go deeper than 10 directories + } + + /* check if directory was already searched once */ + int size = m_dirNames.size(); + for(int i = 0; i < size; i++) + { + if(strcmp(m_dirNames[i].c_str(),dirname.c_str()) == 0) + { + // string is identical to previous one + TRACE("[mb]Dir already in list: %s\r\n",dirname.c_str()); + return (false); + } + } + /* !!!!!! no return statement within the body after here !!!!*/ + recursive_counter++; + + CFileList flist; + if(readDir(dirname, &flist) == true) + { + MI_MOVIE_INFO movieInfo; + //m_movieInfo.clearMovieInfo(&movieInfo); // refresh structure + for(unsigned int i = 0; i < flist.size(); i++) + { + if( S_ISDIR(flist[i].Mode)) + { + flist[i].Name += '/'; + //TRACE("[mb] Dir: '%s'\r\n",movieInfo.file.Name.c_str()); + loadTsFileNamesFromDir(flist[i].Name); + } + else + { + int test=flist[i].getFileName().find(".ts"); + if( test == -1) + { + //TRACE("[mb] other file: '%s'\r\n",movieInfo.file.Name.c_str()); + } + else + { + m_movieInfo.clearMovieInfo(&movieInfo); // refresh structure + movieInfo.file.Name = flist[i].Name; + if(m_movieInfo.loadMovieInfo(&movieInfo)) { //FIXME atm we show only ts+xml (records) here + movieInfo.file.Mode = flist[i].Mode; + //movieInfo.file.Size = flist[i].Size; + movieInfo.file.Size = get_full_len((char *)flist[i].Name.c_str()); + movieInfo.file.Time = flist[i].Time; + //TRACE(" N:%s,s:%d,t:%d\r\n",movieInfo.file.getFileName().c_str(),movieInfo.file.Size,movieInfo.file.Time); + //TRACE(" N:%s,s:%d\r\n",movieInfo.file.getFileName().c_str(),movieInfo.file.Size>>20); + //TRACE(" s:%d\r\n",movieInfo.file.getFileName().c_str(),movieInfo.file.Size); + //TRACE(" s:%llu\r\n",movieInfo.file.getFileName().c_str(),movieInfo.file.Size); + if(file_found_in_dir == false) + { + // first file in directory found, add directory to list + m_dirNames.push_back(dirname); + file_found_in_dir = true; + //TRACE("[mb] new dir: :%s\r\n",dirname); + } + movieInfo.dirItNr = m_dirNames.size()-1; + m_vMovieInfo.push_back(movieInfo); + } + } + } + } + result = true; + } + + recursive_counter--; + return (result); +} + +bool CMovieBrowser::readDir(const std::string & dirname, CFileList* flist) +{ + bool result = false; + if (strncmp(dirname.c_str(), VLC_URI, strlen(VLC_URI)) == 0) + { + result = readDir_vlc(dirname, flist); + } + else + { + result = readDir_std(dirname, flist); + } + return(result); +} + +bool CMovieBrowser::readDir_vlc(const std::string & dirname, CFileList* flist) +{ + return false; +} + +bool CMovieBrowser::readDir_std(const std::string & dirname, CFileList* flist) +{ + bool result = true; + //TRACE("readDir_std %s\n",dirname.c_str()); + stat_struct statbuf; + dirent_struct **namelist; + int n; + + n = my_scandir(dirname.c_str(), &namelist, 0, my_alphasort); + if (n < 0) + { + perror(("[mb] scandir: "+dirname).c_str()); + return false; + } + CFile file; + for(int i = 0; i < n;i++) + { + if(namelist[i]->d_name[0] != '.') + { + file.Name = dirname; + file.Name += namelist[i]->d_name; + +// printf("file.Name: '%s', getFileName: '%s' getPath: '%s'\n",file.Name.c_str(),file.getFileName().c_str(),file.getPath().c_str()); + if(my_stat((file.Name).c_str(),&statbuf) != 0) + perror("stat error"); + else + { + file.Mode = statbuf.st_mode; + file.Time = statbuf.st_mtime; + file.Size = statbuf.st_size; + flist->push_back(file); + } + } + free(namelist[i]); + } + + free(namelist); + + return(result); +} + +bool CMovieBrowser::delFile(CFile& file) +{ + bool result = false; + //only std supported yet + if(1) + { + result = delFile_std(file); + } + else + { + result = delFile_vlc(file); + } + return(result); +} + +bool CMovieBrowser::delFile_vlc(CFile& file) +{ + bool result = false; + return(result); +} + +bool CMovieBrowser::delFile_std(CFile& file) +{ + bool result = true; + unlink(file.Name.c_str()); // fix: use full path + TRACE(" delete file: %s\r\n",file.Name.c_str()); + return(result); +} + +void CMovieBrowser::updateMovieSelection(void) +{ + //TRACE("[mb]->updateMovieSelection %d\r\n",m_windowFocus); + if (m_vMovieInfo.size() == 0) return; + bool new_selection = false; + + unsigned int old_movie_selection; + if(m_windowFocus == MB_FOCUS_BROWSER) + { + if(m_vHandleBrowserList.size() == 0) + { + // There are no elements in the Filebrowser, clear all handles + m_currentBrowserSelection = 0; + m_movieSelectionHandler = NULL; + new_selection = true; + } + else + { + old_movie_selection = m_currentBrowserSelection; + m_currentBrowserSelection = m_pcBrowser->getSelectedLine(); + //TRACE(" sel1:%d\r\n",m_currentBrowserSelection); + if(m_currentBrowserSelection != old_movie_selection) + new_selection = true; + + if(m_currentBrowserSelection < m_vHandleBrowserList.size()) + m_movieSelectionHandler = m_vHandleBrowserList[m_currentBrowserSelection]; + } + } + else if(m_windowFocus == MB_FOCUS_LAST_PLAY) + { + if(m_vHandlePlayList.size() == 0) + { + // There are no elements in the Filebrowser, clear all handles + m_currentPlaySelection = 0; + m_movieSelectionHandler = NULL; + new_selection = true; + } + else + { + old_movie_selection = m_currentPlaySelection; + m_currentPlaySelection = m_pcLastPlay->getSelectedLine(); + //TRACE(" sel2:%d\r\n",m_currentPlaySelection); + if(m_currentPlaySelection != old_movie_selection) + new_selection = true; + + if(m_currentPlaySelection < m_vHandlePlayList.size()) + m_movieSelectionHandler = m_vHandlePlayList[m_currentPlaySelection]; + } + } + else if(m_windowFocus == MB_FOCUS_LAST_RECORD) + { + if(m_vHandleRecordList.size() == 0) + { + // There are no elements in the Filebrowser, clear all handles + m_currentRecordSelection = 0; + m_movieSelectionHandler = NULL; + new_selection = true; + } + else + { + old_movie_selection = m_currentRecordSelection; + m_currentRecordSelection = m_pcLastRecord->getSelectedLine(); + //TRACE(" sel3:%d\r\n",m_currentRecordSelection); + if(m_currentRecordSelection != old_movie_selection) + new_selection = true; + + if(m_currentRecordSelection < m_vHandleRecordList.size()) + m_movieSelectionHandler = m_vHandleRecordList[m_currentRecordSelection]; + } + } + + if(new_selection == true) + { + //TRACE("new\r\n"); + refreshMovieInfo(); + refreshLCD(); + } + //TRACE("\r\n"); +} + +void CMovieBrowser::updateFilterSelection(void) +{ + //TRACE("[mb]->updateFilterSelection \r\n"); + if(m_FilterLines.lineArray[0].size() == 0) return; + + bool result = true; + int selected_line = m_pcFilter->getSelectedLine(); + if(selected_line > 0) + selected_line--; + + if(m_settings.filter.item == MB_INFO_FILEPATH) + { + m_settings.filter.optionString = m_FilterLines.lineArray[0][selected_line+1]; + m_settings.filter.optionVar = selected_line; + } + else if(m_settings.filter.item == MB_INFO_INFO1) + { + m_settings.filter.optionString = m_FilterLines.lineArray[0][selected_line+1]; + } + else if(m_settings.filter.item == MB_INFO_MAJOR_GENRE) + { + m_settings.filter.optionString = g_Locale->getText(GENRE_ALL[selected_line].value); + m_settings.filter.optionVar = GENRE_ALL[selected_line].key; + } + else if(m_settings.filter.item == MB_INFO_SERIE) + { + m_settings.filter.optionString = m_FilterLines.lineArray[0][selected_line+1]; + } + else + { + result = false; + } + if(result == true) + { + refreshBrowserList(); + refreshLastPlayList(); + refreshLastRecordList(); + refreshFoot(); + } +} + +bool CMovieBrowser::addDir(std::string& dirname, int* used) +{ + if(dirname.empty()) return false; + if(dirname == "/") return false; + + MB_DIR newdir; + newdir.name = dirname; + + if(newdir.name.rfind('/') != newdir.name.length()-1 || + newdir.name.length() == 0 || + newdir.name == VLC_URI) + { + newdir.name += '/'; + } + + int size = m_dir.size(); + for(int i = 0; i < size; i++) + { + if(strcmp(m_dir[i].name.c_str(),newdir.name.c_str()) == 0) + { + // string is identical to previous one + TRACE("[mb] Dir already in list: %s\r\n",newdir.name.c_str()); + return (false); + } + } + TRACE("[mb] new Dir: %s\r\n",newdir.name.c_str()); + newdir.used = used; + m_dir.push_back(newdir); + if(*used == true) + { + m_file_info_stale = true; // we got a new Dir, search again for all movies next time + m_seriename_stale = true; + } + return (true); +} + +void CMovieBrowser::loadMovies(void) +{ + time_t time_start = time(NULL); + clock_t clock_start = clock()/10000; // CLOCKS_PER_SECOND + //clock_t clock_prev = clock_start; + clock_t clock_act = clock_start; + + TRACE("[mb] loadMovies: \n"); + + CHintBox loadBox(LOCALE_MOVIEBROWSER_HEAD,g_Locale->getText(LOCALE_MOVIEBROWSER_SCAN_FOR_MOVIES)); + loadBox.paint(); + +//clock_act = clock()/10000;TRACE("[mb] *1: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + loadAllTsFileNamesFromStorage(); // P1 +//clock_act = clock()/10000;TRACE("[mb] *2: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + //loadAllMovieInfo(); // P1 +//clock_act = clock()/10000;TRACE("[mb] *3: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + m_file_info_stale = false; + m_seriename_stale = true; // we reloded the movie info, so make sure the other list are updated later on as well + updateSerienames(); +//clock_act = clock()/10000;TRACE("[mb] *4: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + if(m_settings.serie_auto_create == 1) + { + autoFindSerie(); + } + + loadBox.hide(); + +//clock_act = clock()/10000;TRACE("[mb] *5: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + refreshBrowserList(); +//clock_act = clock()/10000;TRACE("[mb] *6: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; + refreshLastPlayList(); + refreshLastRecordList(); + refreshFilterList(); + refreshMovieInfo(); // is done by refreshBrowserList if needed +//clock_act = clock()/10000;TRACE("[mb] *7: time %9ld clock %6ld dclock %6ld*\n",(long)time(NULL),clock_act,clock_act - clock_prev);clock_prev = clock_act; +TRACE("[mb] ***Total:time %ld clock %ld***\n",(time(NULL)-time_start), clock_act-clock_start); +} + +void CMovieBrowser::loadAllMovieInfo(void) +{ + //TRACE("[mb]->loadAllMovieInfo \r\n"); + + for(unsigned int i=0; i < m_vMovieInfo.size();i++) + { + m_movieInfo.loadMovieInfo( &(m_vMovieInfo[i])); + } +} + +void CMovieBrowser::showHelp(void) +{ + CMovieHelp help; + help.exec(NULL,NULL); +} + +#define MAX_STRING 30 +void CMovieBrowser::showMovieInfoMenu(MI_MOVIE_INFO* movie_info) +{ + unsigned int i = 0; + + /********************************************************************/ + /** MovieInfo menu ******************************************************/ + + /********************************************************************/ + /** bookmark ******************************************************/ + CStringInput* pBookNameInput[MAX_NUMBER_OF_BOOKMARK_ITEMS]; + CIntInput* pBookPosIntInput[MAX_NUMBER_OF_BOOKMARK_ITEMS]; + CIntInput* pBookTypeIntInput[MAX_NUMBER_OF_BOOKMARK_ITEMS]; + CMenuWidget* pBookItemMenu[MAX_NUMBER_OF_BOOKMARK_ITEMS]; + + CIntInput bookStartIntInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, (int&)movie_info->bookmarks.start, 5, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO2); + CIntInput bookLastIntInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, (int&)movie_info->bookmarks.lastPlayStop, 5, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO2); + CIntInput bookEndIntInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, (int&)movie_info->bookmarks.end, 5, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO2); + + CMenuWidget bookmarkMenu (LOCALE_MOVIEBROWSER_BOOK_HEAD , "streaming.raw"); + + bookmarkMenu.addItem(GenericMenuSeparator); + bookmarkMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_CLEAR_ALL, true, NULL, this, "book_clear_all",CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + bookmarkMenu.addItem(GenericMenuSeparatorLine); + bookmarkMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_MOVIESTART, true, bookStartIntInput.getValue(), &bookStartIntInput)); + bookmarkMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_MOVIEEND, true, bookLastIntInput.getValue(), &bookLastIntInput)); + bookmarkMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_LASTMOVIESTOP, true, bookEndIntInput.getValue(), &bookEndIntInput)); + bookmarkMenu.addItem(GenericMenuSeparatorLine); + + for(int i =0 ; i < MI_MOVIE_BOOK_USER_MAX && i < MAX_NUMBER_OF_BOOKMARK_ITEMS; i++ ) + { + pBookNameInput[i] = new CStringInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, &movie_info->bookmarks.user[i].name, 20, LOCALE_MOVIEBROWSER_EDIT_BOOK_NAME_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_NAME_INFO2, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + pBookPosIntInput[i] = new CIntInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, (int&) movie_info->bookmarks.user[i].pos, 20, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_POS_INFO2); + pBookTypeIntInput[i] = new CIntInput (LOCALE_MOVIEBROWSER_EDIT_BOOK, (int&) movie_info->bookmarks.user[i].length, 20, LOCALE_MOVIEBROWSER_EDIT_BOOK_TYPE_INFO1, LOCALE_MOVIEBROWSER_EDIT_BOOK_TYPE_INFO2); + + pBookItemMenu[i] = new CMenuWidget(LOCALE_MOVIEBROWSER_BOOK_HEAD, "streaming.raw"); + pBookItemMenu[i]->addItem(GenericMenuSeparator); + pBookItemMenu[i]->addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_NAME, true, movie_info->bookmarks.user[i].name,pBookNameInput[i])); + pBookItemMenu[i]->addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_POSITION, true, pBookPosIntInput[i]->getValue(), pBookPosIntInput[i])); + pBookItemMenu[i]->addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_TYPE, true, pBookTypeIntInput[i]->getValue(),pBookTypeIntInput[i])); + + bookmarkMenu.addItem( new CMenuForwarderNonLocalized (movie_info->bookmarks.user[i].name.c_str(), true, pBookPosIntInput[i]->getValue(),pBookItemMenu[i])); + } + +/********************************************************************/ +/** serie******************************************************/ + CStringInput serieUserInput(LOCALE_MOVIEBROWSER_EDIT_SERIE, &movie_info->serieName, 20, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + + CMenuWidget serieMenu (LOCALE_MOVIEBROWSER_SERIE_HEAD, "streaming.raw"); + serieMenu.addItem(GenericMenuSeparator); + serieMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_SERIE_NAME, true, movie_info->serieName,&serieUserInput)); + serieMenu.addItem(GenericMenuSeparatorLine); + for(unsigned int i=0; i < m_vHandleSerienames.size(); i++) + serieMenu.addItem( new CMenuSelector(m_vHandleSerienames[i]->serieName.c_str(), true, movie_info->serieName)); + + /********************************************************************/ + /** update movie info ******************************************************/ + for(i=0; idirItNr].c_str(),BUFFER_SIZE); + snprintf(size,BUFFER_SIZE,"%5llu",movie_info->file.Size>>20); + } + + CStringInput titelUserInput(LOCALE_MOVIEBROWSER_INFO_TITLE, &movie_info->epgTitle, MAX_STRING, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + //CStringInputSMS titelUserInput(LOCALE_MOVIEBROWSER_INFO_TITLE, &movie_info->epgTitle, MAX_STRING, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + CStringInput channelUserInput(LOCALE_MOVIEBROWSER_INFO_CHANNEL, &movie_info->epgChannel, 15, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + CStringInput epgUserInput(LOCALE_MOVIEBROWSER_INFO_INFO1, &movie_info->epgInfo1, 20, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + CDateInput dateUserDateInput(LOCALE_MOVIEBROWSER_INFO_LENGTH, &movie_info->dateOfLastPlay, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + CDateInput recUserDateInput(LOCALE_MOVIEBROWSER_INFO_LENGTH, &movie_info->file.Time, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + CIntInput lengthUserIntInput(LOCALE_MOVIEBROWSER_INFO_LENGTH, (int&)movie_info->length, 3, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + CStringInput countryUserInput(LOCALE_MOVIEBROWSER_INFO_PRODCOUNTRY, &movie_info->productionCountry, 11, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ "); + CIntInput yearUserIntInput(LOCALE_MOVIEBROWSER_INFO_PRODYEAR, (int&)movie_info->productionDate, 4, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + + CMenuWidget movieInfoMenu (LOCALE_MOVIEBROWSER_INFO_HEAD, "streaming.raw",m_cBoxFrame.iWidth); + + movieInfoMenu.addItem(GenericMenuSeparator); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_MENU_SAVE, true, NULL, this, "save_movie_info", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_HEAD_UPDATE, true, NULL, &movieInfoMenuUpdate, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_HEAD, true, NULL, &bookmarkMenu, NULL, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + movieInfoMenu.addItem(GenericMenuSeparatorLine); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_TITLE, true, movie_info->epgTitle, &titelUserInput,NULL, CRCInput::RC_1, NEUTRINO_ICON_BUTTON_1)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_SERIE, true, movie_info->serieName, &serieMenu,NULL, CRCInput::RC_2, NEUTRINO_ICON_BUTTON_2)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_INFO1, (movie_info->epgInfo1.size() <= MAX_STRING) /*true*/, movie_info->epgInfo1, &epgUserInput,NULL, CRCInput::RC_3, NEUTRINO_ICON_BUTTON_3)); + movieInfoMenu.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_INFO_GENRE_MAJOR, &movie_info->genreMajor, GENRE_ALL, GENRE_ALL_COUNT, true,NULL, CRCInput::RC_4, NEUTRINO_ICON_BUTTON_4)); + movieInfoMenu.addItem(GenericMenuSeparatorLine); + movieInfoMenu.addItem( new CMenuOptionNumberChooser(LOCALE_MOVIEBROWSER_INFO_QUALITY,&movie_info->quality,true,0,3, NULL)); + movieInfoMenu.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE, &movie_info->parentalLockAge, MESSAGEBOX_PARENTAL_LOCKAGE_OPTIONS, MESSAGEBOX_PARENTAL_LOCKAGE_OPTION_COUNT, true,NULL, CRCInput::RC_6, NEUTRINO_ICON_BUTTON_6 )); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_PRODYEAR, true, yearUserIntInput.getValue(), &yearUserIntInput,NULL, CRCInput::RC_7, NEUTRINO_ICON_BUTTON_7)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_PRODCOUNTRY, true, movie_info->productionCountry, &countryUserInput,NULL, CRCInput::RC_8, NEUTRINO_ICON_BUTTON_8)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_LENGTH, true, lengthUserIntInput.getValue(), &lengthUserIntInput,NULL, CRCInput::RC_9, NEUTRINO_ICON_BUTTON_9)); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_CHANNEL, true, movie_info->epgChannel, &channelUserInput,NULL, CRCInput::RC_0, NEUTRINO_ICON_BUTTON_0));//LOCALE_TIMERLIST_CHANNEL + movieInfoMenu.addItem(GenericMenuSeparatorLine); + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_PATH, false, dirItNr)); //LOCALE_TIMERLIST_RECORDING_DIR + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_PREVPLAYDATE, false, dateUserDateInput.getValue()));//LOCALE_FLASHUPDATE_CURRENTVERSIONDATE + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_RECORDDATE, false, recUserDateInput.getValue()));//LOCALE_FLASHUPDATE_CURRENTVERSIONDATE + movieInfoMenu.addItem( new CMenuForwarder(LOCALE_MOVIEBROWSER_INFO_SIZE, false, size, NULL)); + + movieInfoMenu.exec(NULL,""); + + for(int i =0 ; i < MI_MOVIE_BOOK_USER_MAX && i < MAX_NUMBER_OF_BOOKMARK_ITEMS; i++ ) + { + delete pBookNameInput[i] ; + delete pBookPosIntInput[i] ; + delete pBookTypeIntInput[i]; + delete pBookItemMenu[i]; + } +} + +extern "C" int pinghost( const char *hostname ); +bool CMovieBrowser::showMenu(MI_MOVIE_INFO* movie_info) +{ + /* first clear screen */ + m_pcWindow->paintBackground(); + int i; +/********************************************************************/ +/** directory menu ******************************************************/ + CDirMenu dirMenu(&m_dir); + +/********************************************************************/ +/** options menu **************************************************/ + +/********************************************************************/ +/** parental lock **************************************************/ + CMenuWidget parentalMenu (LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_HEAD , "streaming.raw",450); + parentalMenu.addItem(GenericMenuSeparator); + parentalMenu.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_ACTIVATED, (int*)(&m_parentalLock), MESSAGEBOX_PARENTAL_LOCK_OPTIONS, MESSAGEBOX_PARENTAL_LOCK_OPTIONS_COUNT, true )); + parentalMenu.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_MENU_PARENTAL_LOCK_RATE_HEAD, (int*)(&m_settings.parentalLockAge), MESSAGEBOX_PARENTAL_LOCKAGE_OPTIONS, MESSAGEBOX_PARENTAL_LOCKAGE_OPTION_COUNT, true )); + + +/********************************************************************/ +/** optionsVerzeichnisse **************************************************/ + CMenuWidget optionsMenuDir (LOCALE_MOVIEBROWSER_MENU_DIRECTORIES_HEAD , "streaming.raw",450); + optionsMenuDir.addItem(GenericMenuSeparator); + optionsMenuDir.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_USE_REC_DIR, (int*)(&m_settings.storageDirRecUsed), MESSAGEBOX_YES_NO_OPTIONS, MESSAGEBOX_YES_NO_OPTIONS_COUNT, true )); + optionsMenuDir.addItem( new CMenuForwarder( LOCALE_MOVIEBROWSER_DIR , false ,g_settings.network_nfs_recordingdir)); + + optionsMenuDir.addItem( new CMenuOptionChooser(LOCALE_MOVIEBROWSER_USE_MOVIE_DIR, (int*)(&m_settings.storageDirMovieUsed), MESSAGEBOX_YES_NO_OPTIONS, MESSAGEBOX_YES_NO_OPTIONS_COUNT, true )); + optionsMenuDir.addItem( new CMenuForwarder (LOCALE_MOVIEBROWSER_DIR, false , g_settings.network_nfs_moviedir)); + optionsMenuDir.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MOVIEBROWSER_DIR_HEAD)); + + CFileChooser* dirInput[MB_MAX_DIRS]; + CMenuOptionChooser* chooser[MB_MAX_DIRS]; + COnOffNotifier* notifier[MB_MAX_DIRS]; + CMenuForwarder* forwarder[MB_MAX_DIRS]; + for(i=0; igetValue(), browserRowWidthIntInput[i])); + if(i MAX_BROWSER_FRAME_HEIGHT) + m_settings.browserFrameHeight = MAX_BROWSER_FRAME_HEIGHT; + if (m_settings.browserRowNr > MB_MAX_ROWS ) + m_settings.browserRowNr = MB_MAX_ROWS; + if (m_settings.browserRowNr < 1 ) + m_settings.browserRowNr = 1; + for(i = 0; i < m_settings.browserRowNr; i++) + { + if( m_settings.browserRowWidth[i] >500) + m_settings.browserRowWidth[i] = 500; + if( m_settings.browserRowWidth[i] < 10) + m_settings.browserRowWidth[i] = 10; + } + + if(dirMenu.isChanged()) + loadMovies(); + + updateSerienames(); + refreshBrowserList(); + refreshLastPlayList(); + refreshLastRecordList(); + refreshFilterList(); + refresh(); + + for(i=0; ishowStartPosSelectionMenu\r\n"); + int pos = -1; + int result = 0; + int menu_nr= 0; + int position[MAX_NUMBER_OF_BOOKMARK_ITEMS]; + + if(m_movieSelectionHandler == NULL) return(result); + + char start_pos[13]; snprintf(start_pos, 12,"%3d min",m_movieSelectionHandler->bookmarks.start/60); + char play_pos[13]; snprintf(play_pos, 12,"%3d min",m_movieSelectionHandler->bookmarks.lastPlayStop/60); + + char book[MI_MOVIE_BOOK_USER_MAX][20]; + + CMenuWidgetSelection startPosSelectionMenu(LOCALE_MOVIEBROWSER_START_HEAD , "streaming.raw"); + + startPosSelectionMenu.addItem(GenericMenuSeparator); + + //startPosSelectionMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_START_RECORD_START, true,NULL)); + //position[menu_nr++] = 0; + if(m_movieSelectionHandler->bookmarks.start != 0) + { + startPosSelectionMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_MOVIESTART, true, start_pos)); + position[menu_nr++] = m_movieSelectionHandler->bookmarks.start; + } + if(m_movieSelectionHandler->bookmarks.lastPlayStop != 0) + { + startPosSelectionMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_LASTMOVIESTOP, true, play_pos)); + position[menu_nr++] = m_movieSelectionHandler->bookmarks.lastPlayStop; + } + startPosSelectionMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_START_RECORD_START, true,NULL)); + position[menu_nr++] = 0; + + startPosSelectionMenu.addItem(GenericMenuSeparatorLine); + int sep_pos = menu_nr; + + for(int i =0 ; i < MI_MOVIE_BOOK_USER_MAX && menu_nr < MAX_NUMBER_OF_BOOKMARK_ITEMS; i++ ) + { + if( m_movieSelectionHandler->bookmarks.user[i].pos != 0 ) + { + if(m_movieSelectionHandler->bookmarks.user[i].length >= 0) + position[menu_nr] = m_movieSelectionHandler->bookmarks.user[i].pos; + else + position[menu_nr] = m_movieSelectionHandler->bookmarks.user[i].pos + m_movieSelectionHandler->bookmarks.user[i].length; + + snprintf(book[i], 19,"%5d min",position[menu_nr]/60); +TRACE("[mb] adding boomark menu N %d, position %d\n", menu_nr, position[menu_nr]); + startPosSelectionMenu.addItem(new CMenuForwarderNonLocalized (m_movieSelectionHandler->bookmarks.user[i].name.c_str(), true, book[i])); + menu_nr++; + } + } + + startPosSelectionMenu.exec(NULL, "12345"); + /* check what menu item was ok'd and set the appropriate play offset*/ + //result = startPosSelectionMenu.getSelected(); + result = startPosSelectionMenu.getSelectedLine(); +printf("startPosSelectionMenu result %d\n", result); + if(result < 0) + return -1; + //TRACE("[mb] selected bookmark %d\n", result); + if(result != 0 && result <= MAX_NUMBER_OF_BOOKMARK_ITEMS) + { + result--; + if(result > sep_pos) result--; + pos = position[result]; + } + TRACE("[mb] selected bookmark %d position %d \n", result, pos); + + return(pos) ; +} + +bool CMovieBrowser::isParentalLock(MI_MOVIE_INFO& movie_info) +{ + bool result = false; + if(m_parentalLock == MB_PARENTAL_LOCK_ACTIVE && m_settings.parentalLockAge <= movie_info.parentalLockAge ) + { + result = true; + } + return (result); +} + +bool CMovieBrowser::isFiltered(MI_MOVIE_INFO& movie_info) +{ + bool result = true; + + switch(m_settings.filter.item) + { + case MB_INFO_FILEPATH: + if(m_settings.filter.optionVar == movie_info.dirItNr) + result = false; + break; + case MB_INFO_INFO1: + if(strcmp(m_settings.filter.optionString.c_str(),movie_info.epgInfo1.c_str()) == 0) + result = false; + break; + case MB_INFO_MAJOR_GENRE: + if(m_settings.filter.optionVar == movie_info.genreMajor) + result = false; + break; + case MB_INFO_SERIE: + if(strcmp(m_settings.filter.optionString.c_str(),movie_info.serieName.c_str()) == 0) + result = false; + break; + break; + default: + result = false; + break; + } + return (result); +} + +bool CMovieBrowser::getMovieInfoItem(MI_MOVIE_INFO& movie_info, MB_INFO_ITEM item, std::string* item_string) +{ + #define MAX_STR_TMP 100 + char str_tmp[MAX_STR_TMP]; + bool result = true; + *item_string=""; + tm* tm_tmp; + + char text[20]; + int i=0; + int counter=0; + + switch(item) + { + case MB_INFO_FILENAME: // = 0, + *item_string = movie_info.file.getFileName(); + break; + case MB_INFO_FILEPATH: // = 1, + if(m_dirNames.size() > 0) + *item_string = m_dirNames[movie_info.dirItNr]; + break; + case MB_INFO_TITLE: // = 2, + *item_string = movie_info.epgTitle; + if(strcmp("not available",movie_info.epgTitle.c_str()) == 0) + result = false; + if(movie_info.epgTitle.empty()) + result = false; + break; + case MB_INFO_SERIE: // = 3, + *item_string = movie_info.serieName; + break; + case MB_INFO_INFO1: // = 4, + *item_string = movie_info.epgInfo1; + break; + case MB_INFO_MAJOR_GENRE: // = 5, + snprintf(str_tmp,MAX_STR_TMP,"%2d",movie_info.genreMajor); + *item_string = str_tmp; + break; + case MB_INFO_MINOR_GENRE: // = 6, + snprintf(str_tmp,MAX_STR_TMP,"%2d",movie_info.genreMinor); + *item_string = str_tmp; + break; + case MB_INFO_INFO2: // = 7, + *item_string = movie_info.epgInfo2; + break; + case MB_INFO_PARENTAL_LOCKAGE: // = 8, + snprintf(str_tmp,MAX_STR_TMP,"%2d",movie_info.parentalLockAge); + *item_string = str_tmp; + break; + case MB_INFO_CHANNEL: // = 9, + *item_string = movie_info.epgChannel; + break; + case MB_INFO_BOOKMARK: // = 10, + // we just return the number of bookmarks + for(i = 0; i < MI_MOVIE_BOOK_USER_MAX; i++) + { + if(movie_info.bookmarks.user[i].pos != 0) + counter++; + } + snprintf(text, 8,"%d",counter); + text[9] = 0; // just to make sure string is terminated + *item_string = text; + break; + case MB_INFO_QUALITY: // = 11, + snprintf(str_tmp,MAX_STR_TMP,"%d",movie_info.quality); + *item_string = str_tmp; + break; + case MB_INFO_PREVPLAYDATE: // = 12, + tm_tmp = localtime(&movie_info.dateOfLastPlay); + snprintf(str_tmp,MAX_STR_TMP,"%02d.%02d.%02d",tm_tmp->tm_mday,(tm_tmp->tm_mon)+ 1, tm_tmp->tm_year >= 100 ? tm_tmp->tm_year-100 : tm_tmp->tm_year); + *item_string = str_tmp; + break; + case MB_INFO_RECORDDATE: // = 13, + tm_tmp = localtime(&movie_info.file.Time); + snprintf(str_tmp,MAX_STR_TMP,"%02d.%02d.%02d",tm_tmp->tm_mday,(tm_tmp->tm_mon) + 1,tm_tmp->tm_year >= 100 ? tm_tmp->tm_year-100 : tm_tmp->tm_year); + *item_string = str_tmp; + break; + case MB_INFO_PRODDATE: // = 14, + snprintf(str_tmp,MAX_STR_TMP,"%d",movie_info.productionDate); + *item_string = str_tmp; + break; + case MB_INFO_COUNTRY: // = 15, + *item_string = movie_info.productionCountry; + break; + case MB_INFO_GEOMETRIE: // = 16, + result = false; + break; + case MB_INFO_AUDIO: // = 17, +#if 1 // MB_INFO_AUDIO test + // we just return the number of audiopids + char text[10]; + snprintf(text, 8,"%d",movie_info.audioPids.size()); + text[9] = 0; // just to make sure string is terminated + *item_string = text; +#else // MB_INFO_AUDIO test + for(i=0; i < movie_info.audioPids.size() && i < 10; i++) + { + if(movie_info.audioPids[i].epgAudioPidName[0].size() < 2) + { + text[counter++] = '?'; // two chars ??? -> strange name + continue; + } + + // check for Dolby Digital / AC3 Audio audiopids (less than 5.1 is not remarkable) + if( (movie_info.audioPids[i].epgAudioPidName.find("AC3") != -1 ) || + (movie_info.audioPids[i].epgAudioPidName.find("5.1") != -1 )) + { + ac3_found = true; + } + // Check for german audio pids + if( movie_info.audioPids[i].epgAudioPidName[0] == 'D' || // Deutsch + movie_info.audioPids[i].epgAudioPidName[0] == 'd' || + movie_info.audioPids[i].epgAudioPidName[0] == 'G' || // German + movie_info.audioPids[i].epgAudioPidName[0] == 'g' || + movie_info.audioPids[i].epgAudioPidName[0] == 'M' || // for Mono, mono and Stereo, stereo we assume German ;) + movie_info.audioPids[i].epgAudioPidName[0] == 'n' || + (movie_info.audioPids[i].epgAudioPidName[0] == 'S' && movie_info.audioPids[i].epgAudioPidName[1] == 't' ) || + (movie_info.audioPids[i].epgAudioPidName[0] == 's' && movie_info.audioPids[i].epgAudioPidName[1] == 't' )) + { + text[counter++] = 'D'; + continue; + } + // Check for english audio pids + if( movie_info.audioPids[i].epgAudioPidName[0] == 'E' || + movie_info.audioPids[i].epgAudioPidName[0] == 'e') + { + text[counter++] = 'E'; + continue; + } + // Check for french audio pids + if( movie_info.audioPids[i].epgAudioPidName[0] == 'F' || + movie_info.audioPids[i].epgAudioPidName[0] == 'f') + { + text[counter++] = 'F'; + continue; + } + // Check for italian audio pids + if( movie_info.audioPids[i].epgAudioPidName[0] == 'I' || + movie_info.audioPids[i].epgAudioPidName[0] == 'i') + { + text[counter++] = 'I'; + continue; + } + // Check for spanish audio pids + if( movie_info.audioPids[i].epgAudioPidName[0] == 'E' || + movie_info.audioPids[i].epgAudioPidName[0] == 'e' || + movie_info.audioPids[i].epgAudioPidName[0] == 'S' || + movie_info.audioPids[i].epgAudioPidName[0] == 's') + { + text[counter++] = 'S'; + continue; + } + text[counter++] = '?'; // We have not found any language for this pid + } + if(ac3_found == true) + { + text[counter++] = '5'; + text[counter++] = '.'; + text[counter++] = '1'; + } + text[counter] = 0; // terminate string +#endif // MB_INFO_AUDIO test + break; + case MB_INFO_LENGTH: // = 18, + snprintf(str_tmp,MAX_STR_TMP,"%4d",movie_info.length); + *item_string = str_tmp; + break; + case MB_INFO_SIZE: // = 19, + snprintf(str_tmp,MAX_STR_TMP,"%4llu",movie_info.file.Size>>20); + *item_string = str_tmp; + break; + case MB_INFO_MAX_NUMBER: // = 20 + default: + *item_string=""; + result = false; + break; + } + //TRACE(" getMovieInfoItem: %d,>%s<",item,*item_string.c_str()); + return(result); +} + +void CMovieBrowser::updateSerienames(void) +{ + if(m_seriename_stale == false) + return; + + m_vHandleSerienames.clear(); + for(unsigned int i = 0; i < m_vMovieInfo.size(); i++) + { + if(!m_vMovieInfo[i].serieName.empty()) + { + // current series name is not empty, lets see if we already have it in the list, and if not save it to the list. + bool found = false; + for(unsigned int t = 0; t < m_vHandleSerienames.size() && found == false; t++) + { + if(strcmp(m_vHandleSerienames[t]->serieName.c_str(),m_vMovieInfo[i].serieName.c_str()) == 0) + found = true; + } + if(found == false) + m_vHandleSerienames.push_back(&m_vMovieInfo[i]); + } + } + TRACE("[mb]->updateSerienames: %d\r\n",m_vHandleSerienames.size()); + // TODO sort(m_serienames.begin(), m_serienames.end(), my_alphasort); + m_seriename_stale = false; +} + +void CMovieBrowser::autoFindSerie(void) +{ + TRACE("autoFindSerie\n"); + updateSerienames(); // we have to make sure that the seriename array is up to date, otherwise this does not work + // if the array is not stale, the function is left immediately + for(unsigned int i = 0; i < m_vMovieInfo.size(); i++) + { + // For all movie infos, which do not have a seriename, we try to find one. + // We search for a movieinfo with seriename, and than we do check if the title is the same + // in case of same title, we assume both belongs to the same serie + //TRACE("%s ",m_vMovieInfo[i].serieName); + if( m_vMovieInfo[i].serieName.empty()) + { + for(unsigned int t=0; t < m_vHandleSerienames.size();t++) + { + //TRACE("%s ",m_vHandleSerienames[i].serieName); + if(m_vMovieInfo[i].epgTitle == m_vHandleSerienames[t]->epgTitle ) + { + //TRACE("x"); + m_vMovieInfo[i].serieName = m_vHandleSerienames[t]->serieName; + break; // we found a maching serie, nothing to do else, leave for(t=0) + } + } + //TRACE("\n"); + } + } +} + +CMenuSelector::CMenuSelector(const char * OptionName, const bool Active , char * OptionValue, int* ReturnInt ,int ReturnIntValue ) : CMenuItem() +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionValueString = NULL; + optionName = OptionName; + optionValue = OptionValue; + active = Active; + returnIntValue = ReturnIntValue; + returnInt = ReturnInt; +}; + +CMenuSelector::CMenuSelector(const char * OptionName, const bool Active , std::string& OptionValue, int* ReturnInt ,int ReturnIntValue ) : CMenuItem() +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionValueString = &OptionValue; + optionName = OptionName; + strncpy(buffer,OptionValue.c_str(),BUFFER_MAX); + buffer[BUFFER_MAX-1] = 0;// terminate string + optionValue = buffer; + active = Active; + returnIntValue = ReturnIntValue; + returnInt = ReturnInt; +}; + +int CMenuSelector::exec(CMenuTarget* parent) +{ + if(returnInt != NULL) + *returnInt= returnIntValue; + + if(optionValue != NULL && optionName != NULL) + { + if(optionValueString == NULL) + strcpy(optionValue,optionName); + else + *optionValueString = optionName; + } + return menu_return::RETURN_EXIT; +}; + +int CMenuSelector::paint(bool selected, bool last) +{ + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + if (!active) + { + color = COL_MENUCONTENTINACTIVE; + bgcolor = COL_MENUCONTENTINACTIVE_PLUS_0; + } + + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor); + + int stringstartposName = x + offx + 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposName, y+height,dx- (stringstartposName - x), optionName, color, 0, true); // UTF-8 + + if (selected) + CVFD::getInstance()->showMenuText(0, optionName, -1, true); // UTF-8 + + return y+height; +} + +int CMovieHelp::exec(CMenuTarget* parent, const std::string & actionKey) +{ + Helpbox helpbox; + helpbox.addLine(NEUTRINO_ICON_BUTTON_RED, "Sortierung ändern"); + helpbox.addLine(NEUTRINO_ICON_BUTTON_GREEN, "Filterfenster einblenden"); + helpbox.addLine(NEUTRINO_ICON_BUTTON_YELLOW, "Aktives Fenster wechseln"); + helpbox.addLine(NEUTRINO_ICON_BUTTON_BLUE, "Filminfos neu laden"); + helpbox.addLine(NEUTRINO_ICON_BUTTON_DBOX, "Hauptmenü"); + helpbox.addLine("+/- Ansicht wechseln"); + helpbox.addLine(""); + helpbox.addLine("Während der Filmwiedergabe:"); + helpbox.addLine(NEUTRINO_ICON_BUTTON_BLUE, " Markierungsmenu "); + helpbox.addLine(NEUTRINO_ICON_BUTTON_0, " Markierungsaktion nicht ausführen"); + helpbox.addLine(""); + helpbox.addLine("MovieBrowser $Revision: 1.10 $"); + helpbox.addLine("by Günther"); + helpbox.show(LOCALE_MESSAGEBOX_INFO); + return(0); +} +///////////////////////////////////////////////// +// MenuTargets +//////////////////////////////////////////////// +int CFileChooser::exec(CMenuTarget* parent, const std::string & actionKey) +{ + if(parent != NULL) + parent->hide(); + + CFileBrowser browser; + browser.Dir_Mode=true; + if (browser.exec(dirPath->c_str())) + { + *dirPath = browser.getSelectedFile()->Name; + short a = dirPath->compare(0,5,"/mnt/"); + short b = dirPath->compare(0,7,"/media/"); + if(a != 0 && b != 0) + *dirPath =""; // We clear the string if the selected folder is not at leaset /mnt/ or /hdd (There is no other possibility to clear this) + } + + return menu_return::RETURN_REPAINT; +} + +CDirMenu::CDirMenu(std::vector* dir_list) +{ + unsigned int i; + changed = false; + dirList = dir_list; + + if( dirList->empty()) + return; + + for(i = 0; i < MAX_DIR; i++) + dirNfsMountNr[i]=-1; + + for(i = 0; i < dirList->size() && i < MAX_DIR; i++) + { + for(int nfs = 0; nfs < NETWORK_NFS_NR_OF_ENTRIES; nfs++) + { + std::string tmp = g_settings.network_nfs_local_dir[nfs]; + int result = -1; + if(tmp.size()) + result = (*dirList)[i].name.compare( 0,tmp.size(),tmp) ; +printf("[CDirMenu] (nfs%d) %s == (mb%d) %s (%d)\n",nfs,g_settings.network_nfs_local_dir[nfs],i,(*dirList)[i].name.c_str(),result); + + if(result == 0) + { + dirNfsMountNr[i] = nfs; + break; + } + } + } +}; + +int CDirMenu::exec(CMenuTarget* parent, const std::string & actionKey) +{ + int returnval = menu_return::RETURN_REPAINT; + + if(actionKey == "") + { + if(parent) + parent->hide(); + + changed = false; + show(); + } + else if(actionKey.size() == 1) + { + printf("[CDirMenu].exec %s\n",actionKey.c_str()); + int number = atoi(actionKey.c_str()); + if(number < MAX_DIR) + { + if(dirState[number] == DIR_STATE_SERVER_DOWN) + { + std::string command = "etherwake "; + command += g_settings.network_nfs_mac[dirNfsMountNr[number]]; + printf("try to start server: %s\n",command.c_str()); + if(system(command.c_str()) != 0) + perror("etherwake failed"); + + dirOptionText[number]="STARTE SERVER"; + } + else if(dirState[number] == DIR_STATE_NOT_MOUNTED) + { + printf("[CDirMenu] try to mount %d,%d\n",number,dirNfsMountNr[number]); + CFSMounter::MountRes res; + res = CFSMounter::mount( g_settings.network_nfs_ip[dirNfsMountNr[number]].c_str(), + g_settings.network_nfs_dir[dirNfsMountNr[number]] , + g_settings.network_nfs_local_dir[dirNfsMountNr[number]] , + (CFSMounter::FSType)g_settings.network_nfs_type[dirNfsMountNr[number]] , + g_settings.network_nfs_username[dirNfsMountNr[number]] , + g_settings.network_nfs_password[dirNfsMountNr[number]] , + g_settings.network_nfs_mount_options1[dirNfsMountNr[number]] , + g_settings.network_nfs_mount_options2[dirNfsMountNr[number]] ); + if(res == CFSMounter::MRES_OK) // if mount is successful we set the state to active in any case + { + *(*dirList)[number].used = true; + } + // try to mount + updateDirState(); + changed = true; + } + else if(dirState[number] == DIR_STATE_MOUNTED) + { + if(*(*dirList)[number].used == true) + { + *(*dirList)[number].used = false; + } + else + { + *(*dirList)[number].used = true; + } + //CFSMounter::umount(g_settings.network_nfs_local_dir[dirNfsMountNr[number]]); + updateDirState(); + changed = true; + } + } + } + return returnval; +} + +void CDirMenu::updateDirState(void) +{ + unsigned int drivefree = 0; + struct statfs s; + + for(unsigned int i = 0; i < dirList->size() && i < MAX_DIR; i++) + { + dirOptionText[i]="UNBEKANNT"; + dirState[i]=DIR_STATE_UNKNOWN; + // 1st ping server +printf("updateDirState: %d: state %d nfs %d\n", i, dirState[i], dirNfsMountNr[i]); + if(dirNfsMountNr[i] != -1) + { + int retvalue = pinghost(g_settings.network_nfs_ip[dirNfsMountNr[i]].c_str()); + if (retvalue == 0)//LOCALE_PING_UNREACHABLE + { + dirOptionText[i]="Server, offline"; + dirState[i]=DIR_STATE_SERVER_DOWN; + } + else if (retvalue == 1)//LOCALE_PING_OK + { + if(CFSMounter::isMounted (g_settings.network_nfs_local_dir[dirNfsMountNr[i]]) == 0) + { + dirOptionText[i]="Not mounted"; + dirState[i]=DIR_STATE_NOT_MOUNTED; + } + else + { + dirState[i]=DIR_STATE_MOUNTED; + } + } + } + else + { + // not a nfs dir, probably IDE? we accept this so far + dirState[i]=DIR_STATE_MOUNTED; + } + if(dirState[i] == DIR_STATE_MOUNTED) + { + if(*(*dirList)[i].used == true) + { + if (statfs((*dirList)[i].name.c_str(), &s) >= 0 ) + { + drivefree = (s.f_bfree * s.f_bsize)>>30; + char tmp[20]; + snprintf(tmp, 19,"%3d GB free",drivefree); + tmp[19]=0; + dirOptionText[i]=tmp; + } + else + { + dirOptionText[i]="? GB free"; + } + } + else + { + dirOptionText[i] = "Inactive"; + } + } + } +} + + +void CDirMenu::show(void) +{ + if(dirList->empty()) + return; + + char tmp[20]; + + CMenuWidget dirMenu (LOCALE_MOVIEBROWSER_MENU_DIRECTORIES_HEAD , "streaming.raw",440); + dirMenu.addItem(GenericMenuSeparator); + + updateDirState(); + for(unsigned int i = 0; i < dirList->size() && i < MAX_DIR; i++) + { + sprintf(tmp,"%d",i); + tmp[1]=0; + dirMenu.addItem( new CMenuForwarderNonLocalized ( (*dirList)[i].name.c_str(), (dirState[i] != DIR_STATE_UNKNOWN), dirOptionText[i], this,tmp)); + } + dirMenu.exec(NULL," "); + return; + +} + +off64_t get_full_len(char * startname) +{ + off64_t fulllength=0; + struct stat64 s; + char spart[255]; + int part = 0; + + stat64(startname, &s); + do { + fulllength +=s.st_size; + sprintf(spart, "%s.%03d", startname, ++part); + } while (!stat64(spart, &s)); + return fulllength; +} + +static void reset_atime(char * path, time_t tt) +{ + struct utimbuf ut; + ut.actime = tt-1; + ut.modtime = tt-1; + utime(path, &ut); +} + +#define BUF_SIZE 1395*188 +#define SAFE_GOP 1395*188 +#define MP_TS_SIZE 262072 // ~0.5 sec +#define MINUTEOFFSET 117*262072 +#define SECONDOFFSET MP_TS_SIZE*2 +static off64_t truncate_movie(MI_MOVIE_INFO * minfo) +{ + struct stat64 s; + char spart[255]; + int part = 0, tpart = 0; + bool found = 0; + char * name = (char *) minfo->file.Name.c_str(); + off64_t size = minfo->file.Size; + int len = minfo->length; + int seconds = minfo->bookmarks.end; + off64_t minuteoffset = len ? size / len : MINUTEOFFSET; + minuteoffset = (minuteoffset / MP_TS_SIZE) * MP_TS_SIZE; + if(minuteoffset < 10000000 || minuteoffset > 90000000) + minuteoffset = MINUTEOFFSET; + off64_t secsize = minuteoffset/60; + off64_t secoffset = secsize * seconds; + off64_t newsize = secoffset; + +//printf("truncate: name %s size %lld len %d sec truncate to %d sec, new size %lld\n", name, size, len, seconds, secoffset); + sprintf(spart, "%s", name); + while (!stat64(spart, &s)) { + if(found) { +//printf("truncate: check part %d file %s - TO REMOVE\n", part, spart); + unlink(spart); + } else { +//printf("truncate: check part %d file %s - OK\n", part, spart); + if(secoffset < s.st_size) { + tpart = part; + found = 1; + } else + secoffset -= s.st_size; + } + sprintf(spart, "%s.%03d", name, ++part); + } + if(found) { + if(tpart) + sprintf(spart, "%s.%03d", name, tpart); + else + sprintf(spart, "%s", name); +printf("truncate: part %s to size %lld\n", spart, secoffset); + truncate(spart, secoffset); + minfo->file.Size = newsize; + minfo->length = minfo->bookmarks.end/60; + minfo->bookmarks.end = 0; + reset_atime(spart, minfo->file.Time); + return newsize; + } + return 0; +} + +struct mybook { + off64_t pos; + off64_t len; + bool ok; +}; +#define REAL_CUT 1 + +static int check_pes_start (unsigned char *packet) +{ + // PCKT: 47 41 91 37 07 50 3F 14 BF 04 FE B9 00 00 01 EA 00 00 8C ... + if (packet[0] == 0x47 && // sync byte 0x47 + (packet[1] & 0x40)) // pusi == 1 + { + /* good, now we have to check if it is video stream */ + unsigned char *pes = packet + 4; + if (packet[3] & 0x20) // adaptation field is present + pes += packet[4] + 1; + + if (!memcmp(pes, "\x00\x00\x01", 3) && (pes[3] & 0xF0) == 0xE0) // PES start & video type + { + //return 1; //(pes[4] << 8) | pes[5]; // PES packet len + pes += 4; + while (pes < (packet + 188 - 4)) + if (!memcmp (pes, "\x00\x00\x01\xB8", 4)) // GOP detect + return 1; + else + pes++; + } + } + return 0; +} + +int find_gop(unsigned char *buf, int r) +{ + for(int j = 0; j < r/188; j++) { + if(check_pes_start(&buf[188*j])) { + return 188*j; + } + } + return -1; +} + +off64_t fake_read(int fd, unsigned char *buf, size_t size, off64_t fsize) +{ + off64_t cur = lseek64 (fd, 0, SEEK_CUR); + + buf[0] = 0x47; + if((cur + size) > fsize) + return (fsize - cur); + else + return size; +} + +#define PSI_SIZE 188*3 +static int read_psi(char * spart, unsigned char * buf) +{ + int srcfd = open (spart, O_RDONLY | O_LARGEFILE); + if(srcfd >= 0) { + /* read psi */ + int r = read (srcfd, buf, PSI_SIZE); + close(srcfd); + if(r != PSI_SIZE) { + perror("read psi"); + return -1; + } + return 0; + } + return -1; +} + +static void save_info(CMovieInfo * cmovie, MI_MOVIE_INFO * minfo, char * dpart, off64_t spos, off64_t secsize) +{ + MI_MOVIE_INFO ninfo; + + cmovie->copy(minfo, &ninfo); + ninfo.file.Name = dpart; + ninfo.file.Size = spos; + ninfo.length = spos/secsize/60; + ninfo.bookmarks.end = 0; + ninfo.bookmarks.start = 0; + ninfo.bookmarks.lastPlayStop = 0; + for(int book_nr = 0; book_nr < MI_MOVIE_BOOK_USER_MAX; book_nr++) { + if( ninfo.bookmarks.user[book_nr].pos != 0 && ninfo.bookmarks.user[book_nr].length > 0 ) { + ninfo.bookmarks.user[book_nr].pos = 0; + ninfo.bookmarks.user[book_nr].length = 0; + } + } + cmovie->saveMovieInfo(ninfo); + cmovie->clearMovieInfo(&ninfo); + reset_atime(dpart, minfo->file.Time); +} + +static void find_new_part(char * npart, char * dpart) +{ + struct stat64 s; + int dp = 0; + sprintf(dpart, "%s_%d.ts", npart, dp); + while (!stat64(dpart, &s)) { + sprintf(dpart, "%s_%d.ts", npart, ++dp); + } +} + +int compare_book(const void *x, const void *y) +{ + struct mybook * px, * py; + int dx, dy; + px = (struct mybook*) x; + py = (struct mybook*) y; + dx = px->pos / (off64_t) 1024; + dy = py->pos / (off64_t) 1024; + int res = dx - dy; +//printf("SORT: %lld and %lld res %d\n", px->pos, py->pos, res); + return res; +} + +static int get_input(bool * stop) +{ + neutrino_msg_data_t data; + neutrino_msg_t msg; + int retval = 0; + * stop = false; + g_RCInput->getMsg(&msg, &data, 1, false); + if(msg == CRCInput::RC_home) { + if(ShowMsgUTF (LOCALE_MESSAGEBOX_INFO, "Cancel movie cut/split ?", CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + * stop = true; + } + } + if(msg != CRCInput::RC_timeout) + retval |= 1; + if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + retval |= 2; +//printf("input: msg %d (%x) ret %d\n", msg, msg, retval); + return retval; +} + +static off64_t cut_movie(MI_MOVIE_INFO * minfo, CMovieInfo * cmovie) +{ + struct mybook books[MI_MOVIE_BOOK_USER_MAX+2]; + int bcount = 0; + int dstfd, srcfd; + int part = 0; + struct stat64 s; + char spart[255]; + char dpart[255]; + char npart[255]; + unsigned char * buf; + unsigned char psi[PSI_SIZE]; + int r, i; + off64_t sdone, spos; + off64_t newsize; + time_t tt; + int percent = 0; + char * name = (char *) minfo->file.Name.c_str(); + CFile file; + MI_MOVIE_INFO ninfo; + bool need_gop = 0; + off64_t tdone = 0; + int was_cancel = 0; + int retval = 0; + time_t tt1; + off64_t bpos, bskip; + + buf = (unsigned char *) malloc(BUF_SIZE); + if(buf == 0) { + perror("malloc"); + return 0; + } + + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + if(!timescale) + timescale = new CScale(200, 15, 0, 100, 0); + int dx = 256; + int x = (((g_settings.screen_EndX- g_settings.screen_StartX)- dx) / 2) + g_settings.screen_StartX; + int y = g_settings.screen_EndY - 50; + frameBuffer->paintBoxRel (x + 40, y+12, 200, 15, COL_INFOBAR_PLUS_0); + timescale->paint(x + 41, y + 12, percent); + int len = minfo->length; + off64_t size = minfo->file.Size; + //off64_t secsize = len ? size/len/60 : 511040; + off64_t minuteoffset = len ? size / len : MINUTEOFFSET; + minuteoffset = (minuteoffset / MP_TS_SIZE) * MP_TS_SIZE; + if(minuteoffset < 5000000 || minuteoffset > 190000000) + minuteoffset = MINUTEOFFSET; + off64_t secsize = minuteoffset/60; + newsize = size; + + if(minfo->bookmarks.start != 0) { + books[bcount].pos = 0; + books[bcount].len = (minfo->bookmarks.start * secsize)/188 * 188; + if(books[bcount].len > SAFE_GOP) + books[bcount].len -= SAFE_GOP; + books[bcount].ok = 1; +printf("cut: start bookmark %d at %lld len %lld\n", bcount, books[bcount].pos, books[bcount].len); + bcount++; + } + for(int book_nr = 0; book_nr < MI_MOVIE_BOOK_USER_MAX; book_nr++) { + if( minfo->bookmarks.user[book_nr].pos != 0 && minfo->bookmarks.user[book_nr].length > 0 ) { + books[bcount].pos = (minfo->bookmarks.user[book_nr].pos * secsize)/188 * 188; + books[bcount].len = (minfo->bookmarks.user[book_nr].length * secsize)/188 * 188; + if(books[bcount].len > SAFE_GOP) + books[bcount].len -= SAFE_GOP; + books[bcount].ok = 1; +printf("cut: jump bookmark %d at %lld len %lld -> skip to %lld\n", bcount, books[bcount].pos, books[bcount].len, books[bcount].pos+books[bcount].len); + bcount++; + } + } + if(minfo->bookmarks.end != 0) { + books[bcount].pos = ((off64_t) minfo->bookmarks.end * secsize)/188 * 188; + books[bcount].len = size - books[bcount].pos; + //if(books[bcount].pos > SAFE_GOP) + // books[bcount].pos -= SAFE_GOP; + books[bcount].ok = 1; +printf("cut: end bookmark %d at %lld\n", bcount, books[bcount].pos); + bcount++; + } +printf("\n"); + if(!bcount) return 0; + qsort(books, bcount, sizeof(struct mybook), compare_book); + for(i = 0; i < bcount; i++) { + if(books[i].ok) { + printf("cut: bookmark %d at %lld len %lld -> skip to %lld\n", i, books[i].pos, books[i].len, books[i].pos+books[i].len); + newsize -= books[i].len; + off64_t curend = books[i].pos + books[i].len; + for(int j = i + 1; j < bcount; j++) { + if((books[j].pos > books[i].pos) && (books[j].pos < curend)) { + off64_t newend = books[j].pos + books[j].len; + if(newend > curend) { + printf("cut: bad bookmark %d, position %lld len %lld, ajusting..\n", j, books[j].pos, books[j].len); + books[j].pos = curend; + books[j].len = newend - curend; + } else { + printf("cut: bad bookmark %d, position %lld len %lld, skipping..\n", j, books[j].pos, books[j].len); + books[j].ok = 0; + } + } + } + } + } + sprintf(npart, "%s", name); + char * ptr = strstr(npart, ".ts"); + if(ptr) + *ptr = 0; + find_new_part(npart, dpart); +tt = time(0); +printf("\n********* new file %s expected size %lld, start time %s", dpart, newsize, ctime (&tt)); + dstfd = open (dpart, O_CREAT|O_WRONLY|O_TRUNC| O_LARGEFILE); + if(dstfd < 0) { + perror(dpart); + return 0; + } + part = 0; + i = 0; + off64_t offset = 0; + spos = 0; + sprintf(spart, "%s", name); + if(read_psi(spart, &psi[0])) { + perror(spart); + goto ret_err; + } + write(dstfd, psi, PSI_SIZE); + bpos = books[i].pos; + bskip = books[i].len; + while (!stat64(spart, &s)) { +printf("cut: open part %d file %s size %lld offset %lld book pos %lld\n", part, spart, s.st_size, offset, bpos); + srcfd = open (spart, O_RDONLY | O_LARGEFILE); + if(srcfd < 0) { + perror(spart); + goto ret_err; + } + if(offset >= s.st_size) { + offset -= s.st_size; + bpos -= s.st_size; + goto next_file; + } + lseek64 (srcfd, offset, SEEK_SET); + sdone = offset; + while(true) { + off64_t until = bpos; +printf("\ncut: reading from %lld to %lld (%lld) want gop %d\n", sdone, until, until - sdone, need_gop); + while(sdone < until) { + bool stop; + int msg = get_input(&stop); + was_cancel = msg & 2; + if(stop) { + close(srcfd); + unlink(dpart); + retval = 1; + goto ret_err; + } + if(msg) { + timescale->reset(); + frameBuffer->paintBoxRel (x + 40, y+12, 200, 15, COL_INFOBAR_PLUS_0); + } + size_t toread = (until-sdone) > BUF_SIZE ? BUF_SIZE : until - sdone; +#if REAL_CUT + r = read (srcfd, buf, toread); +#else + r = fake_read (srcfd, buf, toread, s.st_size); +#endif + if(r > 0) { + int wptr = 0; +// FIXME: TEST +if(r != BUF_SIZE) printf("****** short read ? %d\n", r); +if(buf[0] != 0x47) printf("cut: buffer not aligned at %lld\n", sdone); + if(need_gop) { + int gop = find_gop(buf, r); + if(gop >= 0) { + printf("cut: GOP found at %lld offset %d\n", (off64_t)(sdone+gop), gop); + newsize -= gop; + wptr = gop; + } else + printf("cut: GOP needed, but not found\n"); + need_gop = 0; + } + sdone += r; + spos += r - wptr; + percent = (int) ((float)(spos)/(float)(newsize)*100.); + timescale->paint(x + 41, y + 12, percent); +#if REAL_CUT + int wr = write(dstfd, &buf[wptr], r-wptr); + if(wr < (r-wptr)) { + perror(dpart); + close(srcfd); + goto ret_err; + } +#endif + } else if(sdone < s.st_size) { + /* read error ? */ + close(srcfd); + perror(spart); + goto ret_err; + } else { +printf("cut: next file -> sdone %lld spos %lld bpos %lld\n", sdone, spos, bpos); + offset = 0; + bpos -= sdone; + goto next_file; + } + } +printf("cut: current file pos %lld write pos %lld book pos %lld still to read %lld\n", sdone, spos, bpos, sdone - bpos); + need_gop = 1; + offset = bpos + bskip; + i++; + while(i < bcount) { + if(books[i].ok) + break; + else + i++; + } + if(i < bcount) { + bpos = books[i].pos; + bskip = books[i].len; + } else + bpos = size; +printf("cut: next bookmark pos: %lld abs %lld relative next file pos %lld cur file size %lld\n", bpos, bpos - tdone, offset, s.st_size); + bpos -= tdone; /* all books from 0, converting to 0 + total size skipped */ + if(offset >= s.st_size) { + offset -= s.st_size; + bpos -= s.st_size; + goto next_file; + } + lseek64 (srcfd, offset, SEEK_SET); + sdone = offset; + } +next_file: + tdone += s.st_size; + close(srcfd); + sprintf(spart, "%s.%03d", name, ++part); + } + tt1 = time(0); +printf("********* total written %lld tooks %ld secs end time %s", spos, tt1-tt, ctime (&tt1)); + + save_info(cmovie, minfo, dpart, spos, secsize); + retval = 1; + lseek64 (dstfd, 0, SEEK_SET); +ret_err: + close(dstfd); + free(buf); + if(was_cancel) + g_RCInput->postMsg(CRCInput::RC_home, 0); + return retval; +} + +static off64_t copy_movie(MI_MOVIE_INFO * minfo, CMovieInfo * cmovie, bool onefile) +{ + struct mybook books[MI_MOVIE_BOOK_USER_MAX+2]; + int bcount = 0; + int dstfd = -1, srcfd; + int part = 0; + struct stat64 s; + char spart[255]; + char dpart[255]; + char npart[255]; + unsigned char * buf; + unsigned char psi[PSI_SIZE]; + int r, i; + off64_t sdone, spos = 0, btotal = 0; + off64_t newsize; + time_t tt; + int percent = 0; + char * name = (char *) minfo->file.Name.c_str(); + CFile file; + bool need_gop = 0; + bool dst_done = 0; + bool was_cancel; + int retval = 0; + + buf = (unsigned char *) malloc(BUF_SIZE); + if(buf == 0) { + perror("malloc"); + return 0; + } + + int len = minfo->length; + off64_t size = minfo->file.Size; + off64_t minuteoffset = len ? size / len : MINUTEOFFSET; + minuteoffset = (minuteoffset / MP_TS_SIZE) * MP_TS_SIZE; + if(minuteoffset < 5000000 || minuteoffset > 190000000) + minuteoffset = MINUTEOFFSET; + off64_t secsize = minuteoffset/60; + //off64_t secsize = len ? size/len/60 : 511040; +printf("copy: len %d minute %lld second %lld\n", len, len ? size/len : 511040*60, secsize); + + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + if(!timescale) + timescale = new CScale(200, 15, 0, 100, 0); + int dx = 256; + int x = (((g_settings.screen_EndX- g_settings.screen_StartX)- dx) / 2) + g_settings.screen_StartX; + int y = g_settings.screen_EndY - 50; + frameBuffer->paintBoxRel (x + 40, y+12, 200, 15, COL_INFOBAR_PLUS_0); + timescale->paint(x + 41, y + 12, percent); + + newsize = 0; + for(int book_nr = 0; book_nr < MI_MOVIE_BOOK_USER_MAX; book_nr++) { + if( minfo->bookmarks.user[book_nr].pos != 0 && minfo->bookmarks.user[book_nr].length > 0 ) { + books[bcount].pos = (minfo->bookmarks.user[book_nr].pos * secsize)/188 * 188; + if(books[bcount].pos > SAFE_GOP) + books[bcount].pos -= SAFE_GOP; + books[bcount].len = (minfo->bookmarks.user[book_nr].length * secsize)/188 * 188; + books[bcount].ok = 1; +printf("copy: jump bookmark %d at %lld len %lld\n", bcount, books[bcount].pos, books[bcount].len); + newsize += books[bcount].len; + bcount++; + } + } + if(!bcount) return 0; + +tt = time(0); +printf("********* %d boormarks, to %s file(s), expected size to copy %lld, start time %s", bcount, onefile ? "one" : "many", newsize, ctime (&tt)); + sprintf(npart, "%s", name); + char * ptr = strstr(npart, ".ts"); + if(ptr) + *ptr = 0; + sprintf(spart, "%s", name); + srcfd = open (spart, O_RDONLY | O_LARGEFILE); + if(read_psi(spart, &psi[0])) { + perror(spart); + goto ret_err; + } + for(i = 0; i < bcount; i++) { +printf("\ncopy: processing bookmark %d at %lld len %lld\n", i, books[i].pos, books[i].len); + off64_t bpos = books[i].pos; + off64_t bskip = books[i].len; + part = 0; + sprintf(spart, "%s", name); + int sres; + while (!(sres = stat64(spart, &s))) { + if(bpos >= s.st_size) { + bpos -= s.st_size; + sprintf(spart, "%s.%03d", name, ++part); +//printf("copy: check src part %s\n", spart); + continue; + } + break; + } + if(sres != 0) { + printf("file for bookmark %d with offset %lld not found\n", i, books[i].pos); + continue; + } + if(!dst_done || !onefile) { + find_new_part(npart, dpart); + dstfd = open (dpart, O_CREAT|O_WRONLY|O_TRUNC| O_LARGEFILE); +printf("copy: new file %s fd %d\n", dpart, dstfd); + if(dstfd < 0) { + printf("failed to open %s\n", dpart); + goto ret_err;; + } + dst_done = 1; + spos = 0; + write(dstfd, psi, PSI_SIZE); + } + need_gop = 1; +next_file: + stat64(spart, &s); +printf("copy: open part %d file %s size %lld offset %lld\n", part, spart, s.st_size, bpos); + srcfd = open (spart, O_RDONLY | O_LARGEFILE); + if(srcfd < 0) { + printf("failed to open %s\n", spart); + close(dstfd); + goto ret_err; + } + lseek64 (srcfd, bpos, SEEK_SET); + sdone = bpos; + off64_t until = bpos + bskip; +printf("copy: read from %lld to %lld read size %d want gop %d\n", bpos, until, BUF_SIZE, need_gop); + while(sdone < until) { + size_t toread = (until-sdone) > BUF_SIZE ? BUF_SIZE : until - sdone; + bool stop; + int msg = get_input(&stop); + was_cancel = msg & 2; + if(stop) { + close(srcfd); + close(dstfd); + unlink(dpart); + retval = 1; + goto ret_err; + } + if(msg) { + frameBuffer->paintBoxRel (x + 40, y+12, 200, 15, COL_INFOBAR_PLUS_0); + timescale->reset(); + } +#if REAL_CUT + r = read (srcfd, buf, toread); +#else + r = fake_read (srcfd, buf, toread, s.st_size); +#endif + if(r > 0) { + int wptr = 0; +// FIXME: TEST +if(r != BUF_SIZE) printf("****** short read ? %d\n", r); +if(buf[0] != 0x47) printf("copy: buffer not aligned at %lld\n", sdone); + if(need_gop) { + int gop = find_gop(buf, r); + if(gop >= 0) { + printf("cut: GOP found at %lld offset %d\n", (off64_t)(sdone+gop), gop); + newsize -= gop; + wptr = gop; + } else + printf("cut: GOP needed, but not found\n"); + need_gop = 0; + } + sdone += r; + bskip -= r; + spos += r - wptr; + btotal += r; + percent = (int) ((float)(btotal)/(float)(newsize)*100.); + timescale->paint(x + 41, y + 12, percent); +#if REAL_CUT + int wr = write(dstfd, &buf[wptr], r-wptr); + if(wr < (r-wptr)) { + printf("write to %s failed\n", dpart); + close(srcfd); + close(dstfd); + goto ret_err; + } +#endif + } else if(sdone < s.st_size) { + /* read error ? */ + printf("%s: read failed\n", spart); + close(srcfd); + close(dstfd); + goto ret_err; + } else { +printf("copy: -> next file, file pos %lld written %lld left %lld\n", sdone, spos, bskip); + bpos = 0; + close(srcfd); + sprintf(spart, "%s.%03d", name, ++part); + goto next_file; + } + } /* while(sdone < until) */ + close(srcfd); + + if(!onefile) { + close(dstfd); + save_info(cmovie, minfo, dpart, spos, secsize); +time_t tt1 = time(0); +printf("copy: ********* %s: total written %lld took %ld secs\n", dpart, spos, tt1-tt); + } + } /* for all books */ + if(onefile) { + close(dstfd); + save_info(cmovie, minfo, dpart, spos, secsize); +time_t tt1 = time(0); +printf("copy: ********* %s: total written %lld took %ld secs\n", dpart, spos, tt1-tt); + } + retval = 1; +ret_err: + free(buf); + if(was_cancel) + g_RCInput->postMsg(CRCInput::RC_home, 0); + return retval; +} diff --git a/src/gui/moviebrowser.h b/src/gui/moviebrowser.h new file mode 100644 index 000000000..5dce329f4 --- /dev/null +++ b/src/gui/moviebrowser.h @@ -0,0 +1,709 @@ +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + $Id: moviebrowser.h,v 1.5 2006/09/11 21:11:35 guenther Exp $ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: moviebrowser.h . + + Description: implementation of the CMovieBrowser class + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + $Log: moviebrowser.h,v $ + Revision 1.5 2006/09/11 21:11:35 guenther + General menu clean up + Dir menu updated + Add options menu + In movie info menu "update all" added + Serie option added (hide serie, auto serie) + Update movie info on delete movie + Delete Background when menu is entered + Timeout updated (MB does not exit after options menu is left) + + Revision 1.4 2006/02/20 01:10:34 guenther + - temporary parental lock updated - remove 1s debug prints in movieplayer- Delete file without rescan of movies- Crash if try to scroll in list with 2 movies only- UTF8XML to UTF8 conversion in preview- Last file selection recovered- use of standard folders adjustable in config- reload and remount option in config + + Revision 1.3 2005/12/18 09:23:53 metallica + fix compil warnings + + Revision 1.2 2005/12/12 07:58:02 guenther + - fix bug on deleting CMovieBrowser - speed up parse time (20 ms per .ts file now)- update stale function- refresh directories on reload- print scan time in debug console + + +****************************************************************************/ +#ifndef MOVIEBROWSER_H_ +#define MOVIEBROWSER_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include "gui/widget/listframe.h" +#include "gui/widget/menue.h" +#include "gui/widget/textbox.h" +#include "gui/movieinfo.h" +#include "driver/file.h" +#include +#include "driver/pictureviewer/pictureviewer.h" + +#define MAX_NUMBER_OF_BOOKMARK_ITEMS MI_MOVIE_BOOK_USER_MAX // we just use the same size as used in Movie info (MAX_NUMBER_OF_BOOKMARK_ITEMS is used for the number of menu items) +#define MOVIEBROWSER_SETTINGS_FILE CONFIGDIR "/moviebrowser.conf" + +#define MIN_BROWSER_FRAME_HEIGHT 100 +#define MAX_BROWSER_FRAME_HEIGHT 400 +/* !!!! Do NOT change the order of the enum, just add items at the end !!!! */ +typedef enum +{ + MB_INFO_FILENAME = 0, + MB_INFO_FILEPATH = 1, + MB_INFO_TITLE = 2, + MB_INFO_SERIE = 3, + MB_INFO_INFO1 = 4, + MB_INFO_MAJOR_GENRE = 5, + MB_INFO_MINOR_GENRE = 6, + MB_INFO_INFO2 = 7, + MB_INFO_PARENTAL_LOCKAGE = 8, + MB_INFO_CHANNEL = 9, + MB_INFO_BOOKMARK = 10, + MB_INFO_QUALITY = 11, + MB_INFO_PREVPLAYDATE = 12, + MB_INFO_RECORDDATE = 13, + MB_INFO_PRODDATE = 14, + MB_INFO_COUNTRY = 15, + MB_INFO_GEOMETRIE = 16, + MB_INFO_AUDIO = 17, + MB_INFO_LENGTH = 18, + MB_INFO_SIZE = 19, + MB_INFO_MAX_NUMBER = 20 // MUST be allways the last item in the list +}MB_INFO_ITEM; + + + +typedef enum +{ + MB_DIRECTION_AUTO = 0, + MB_DIRECTION_UP = 1, + MB_DIRECTION_DOWN = 2, + MB_DIRECTION_MAX_NUMBER = 3 // MUST be allways the last item in the list +}MB_DIRECTION; + +typedef struct +{ + MB_INFO_ITEM item; + MB_DIRECTION direction; +}MB_SORTING; + +typedef enum +{ + MB_STORAGE_TYPE_UNDEFINED = 0, + MB_STORAGE_TYPE_NFS = 1, + MB_STORAGE_TYPE_VLC = 2, + MB_STORAGE_MAX_NUMBER = 3 // MUST be allways the last item in the list +}MB_STORAGE_TYPE; + +typedef struct +{ + MB_INFO_ITEM item; + std::string optionString; + int optionVar; +}MB_FILTER; + +typedef enum +{ + MB_FOCUS_BROWSER = 0, + MB_FOCUS_LAST_PLAY = 1, + MB_FOCUS_LAST_RECORD = 2, + MB_FOCUS_MOVIE_INFO = 3, + MB_FOCUS_FILTER = 4, + MB_FOCUS_MAX_NUMBER = 5 // MUST be allways the last item in the list +}MB_FOCUS; + +typedef enum +{ + MB_GUI_BROWSER_ONLY = 0, + MB_GUI_MOVIE_INFO = 1, + MB_GUI_LAST_PLAY = 2, + MB_GUI_LAST_RECORD = 3, + MB_GUI_FILTER = 4, + MB_GUI_MAX_NUMBER = 5 // MUST be allways the last item in the list +}MB_GUI; + + +typedef enum +{ + MB_PARENTAL_LOCK_OFF = 0, + MB_PARENTAL_LOCK_ACTIVE = 1, + MB_PARENTAL_LOCK_OFF_TMP = 2, // use this to activate the lock temporarily until next dbox start up + MB_PARENTAL_LOCK_MAX_NUMBER = 3 // MUST be allways the last item in the list +}MB_PARENTAL_LOCK; + +typedef struct +{ + std::string name; + int* used; +}MB_DIR; + +#define MB_MAX_ROWS 6 +#define MB_MAX_DIRS 5 +/* MB_SETTINGS to be stored in g_settings anytime ....*/ +typedef struct +{ + // moviebrowser + MB_GUI gui; //MB_GUI + MB_SORTING sorting; //MB_SORTING + MB_FILTER filter;//MB_FILTER + MI_PARENTAL_LOCKAGE parentalLockAge ;//MI_PARENTAL_LOCKAGE + MB_PARENTAL_LOCK parentalLock;//MB_PARENTAL_LOCK + + std::string storageDir[MB_MAX_DIRS]; + int storageDirUsed[MB_MAX_DIRS]; + int storageDirRecUsed; + int storageDirMovieUsed; + + int reload; + int remount; + + int browser_serie_mode; + int serie_auto_create; + /* these variables are used for the listframes */ + int browserFrameHeight; + int browserRowNr; + MB_INFO_ITEM browserRowItem[MB_MAX_ROWS];//MB_INFO_ITEM + int browserRowWidth[MB_MAX_ROWS]; + + // to be added to config later + int lastPlayMaxItems; + int lastPlayRowNr; + MB_INFO_ITEM lastPlayRow[MB_MAX_ROWS]; + int lastPlayRowWidth[MB_MAX_ROWS]; + + int lastRecordMaxItems; + int lastRecordRowNr; + MB_INFO_ITEM lastRecordRow[MB_MAX_ROWS]; + int lastRecordRowWidth[MB_MAX_ROWS]; +}MB_SETTINGS; + +// Priorities for Developmemt: P1: critical feature, P2: important feature, P3: for next release, P4: looks nice, lets see +class CMovieBrowser : public CMenuTarget +{ + public: // Variables ///////////////////////////////////////////////// + int Multi_Select; // for FileBrowser compatibility, not used in MovieBrowser + int Dirs_Selectable; // for FileBrowser compatibility, not used in MovieBrowser + + private: // Variables + //CFBWindow* m_pcWindow; + CFrameBuffer * m_pcWindow; + + CListFrame* m_pcBrowser; + CListFrame* m_pcLastPlay; + CListFrame* m_pcLastRecord; + CTextBox* m_pcInfo; + CListFrame* m_pcFilter; + + CBox m_cBoxFrame; + CBox m_cBoxFrameLastPlayList; + CBox m_cBoxFrameLastRecordList; + CBox m_cBoxFrameBrowserList; + CBox m_cBoxFrameInfo; + CBox m_cBoxFrameBookmarkList; + CBox m_cBoxFrameFilter; + CBox m_cBoxFrameFootRel; + CBox m_cBoxFrameTitleRel; + + LF_LINES m_browserListLines; + LF_LINES m_recordListLines; + LF_LINES m_playListLines; + LF_LINES m_FilterLines; + + std::vector m_vMovieInfo; + std::vector m_vHandleBrowserList; + std::vector m_vHandleRecordList; + std::vector m_vHandlePlayList; + std::vector m_dirNames; +// std::vector m_serienames; + std::vector m_vHandleSerienames; + + unsigned int m_currentBrowserSelection; + unsigned int m_currentRecordSelection; + unsigned int m_currentPlaySelection; + unsigned int m_currentFilterSelection; + unsigned int m_prevBrowserSelection; + unsigned int m_prevRecordSelection; + unsigned int m_prevPlaySelection; + + bool m_showBrowserFiles; + bool m_showLastRecordFiles; + bool m_showLastPlayFiles; + bool m_showMovieInfo; + bool m_showFilter; + + MI_MOVIE_INFO* m_movieSelectionHandler; + int m_currentStartPos; + std::string m_selectedDir; + MB_FOCUS m_windowFocus; + + bool m_file_info_stale; // if this bit is set, MovieBrowser shall reload all movie infos from HD + bool m_seriename_stale; + + Font* m_pcFontFoot; + Font* m_pcFontTitle; + std::string m_textTitle; + + MB_PARENTAL_LOCK m_parentalLock; + MB_STORAGE_TYPE m_storageType; + + CConfigFile configfile; + CMovieInfo m_movieInfo; + MB_SETTINGS m_settings; + std::vector m_dir; + + int movieInfoUpdateAll[MB_INFO_MAX_NUMBER]; + int movieInfoUpdateAllIfDestEmptyOnly; + + //bool restart_mb_timeout; + + public: // Functions //////////////////////////////////////////////////////////7 + CMovieBrowser(const char* path); //P1 + CMovieBrowser(); //P1 + ~CMovieBrowser(); //P1 + int exec(const char* path); //P1 + int exec(CMenuTarget* parent, const std::string & actionKey); + std::string getCurrentDir(void); //P1 for FileBrowser compatibility + CFile* getSelectedFile(void); //P1 for FileBrowser compatibility + MI_MOVIE_BOOKMARKS* getCurrentMovieBookmark(void){if(m_movieSelectionHandler == NULL) return NULL; return(&(m_movieSelectionHandler->bookmarks));}; + int getCurrentStartPos(void){return(m_currentStartPos);}; //P1 return start position in [s] + MI_MOVIE_INFO* getCurrentMovieInfo(void){return(m_movieSelectionHandler);}; //P1 return start position in [s] + void fileInfoStale(void); // call this function to force the Moviebrowser to reload all movie information from HD + + bool readDir(const std::string & dirname, CFileList* flist); + bool readDir_vlc(const std::string & dirname, CFileList* flist); + bool readDir_std(const std::string & dirname, CFileList* flist); + + bool delFile(CFile& file); + bool delFile_vlc(CFile& file); + bool delFile_std(CFile& file); + + private: //Functions + ///// MovieBrowser init /////////////// + void init(void); //P1 + void initGlobalSettings(void); //P1 + void initFrames(void); + void initDevelopment(void); //P1 for development testing only + void initRows(void); + void reinit(void); //P1 + + ///// MovieBrowser Main Window////////// + int paint(void); //P1 + void refresh(void); //P1 + void hide(void); //P1 + void refreshLastPlayList(void); //P2 + void refreshLastRecordList(void); //P2 + void refreshBrowserList(void); //P1 + void refreshFilterList(void); //P1 + void refreshMovieInfo(void); //P1 + void refreshBookmarkList(void); // P3 + void refreshFoot(void); //P2 + void refreshTitle(void); //P2 + void refreshInfo(void); // P2 + void refreshLCD(void); // P2 + + ///// Events /////////////////////////// + bool onButtonPress(neutrino_msg_t msg); // P1 + bool onButtonPressMainFrame(neutrino_msg_t msg); // P1 + bool onButtonPressBrowserList(neutrino_msg_t msg); // P1 + bool onButtonPressLastPlayList(neutrino_msg_t msg); // P2 + bool onButtonPressLastRecordList(neutrino_msg_t msg); // P2 + bool onButtonPressFilterList(neutrino_msg_t msg); // P2 + bool onButtonPressBookmarkList(neutrino_msg_t msg); // P3 + bool onButtonPressMovieInfoList(neutrino_msg_t msg); // P2 + void onSetFocus(MB_FOCUS new_focus); // P2 + void onSetFocusNext(void); // P2 + void onSetFocusPrev(void); // P2 + void onSetGUIWindow(MB_GUI gui); + void onSetGUIWindowNext(void); + void onSetGUIWindowPrev(void); + void onDeleteFile(MI_MOVIE_INFO& movieSelectionHandler); // P4 + bool onSortMovieInfoHandleList(std::vector& pv_handle_list, MB_INFO_ITEM sort_type, MB_DIRECTION direction); + + ///// parse Storage Directories ///////////// + bool addDir(std::string& dirname, int* used); + void updateDir(void); + void loadAllTsFileNamesFromStorage(void); // P1 + bool loadTsFileNamesFromDir(const std::string & dirname); // P1 + void getStorageInfo(void); // P3 + + ///// Menu //////////////////////////////////// + bool showMenu(MI_MOVIE_INFO* movie_info); // P2 + void showMovieInfoMenu(MI_MOVIE_INFO* movie_info); // P2 + int showStartPosSelectionMenu(void); // P2 + + ///// settings /////////////////////////////////// + bool loadSettings(MB_SETTINGS* settings); // P2 + bool saveSettings(MB_SETTINGS* settings); // P2 + void defaultSettings(MB_SETTINGS* settings); + + ///// EPG_DATA /XML /////////////////////////////// + void loadMovies(); + void loadAllMovieInfo(void); // P1 + void saveMovieInfo(std::string* filename, MI_MOVIE_INFO* movie_info); // P2 + + // misc + void showHelp(void); + bool isFiltered(MI_MOVIE_INFO& movie_info); + bool isParentalLock(MI_MOVIE_INFO& movie_info); + bool getMovieInfoItem(MI_MOVIE_INFO& movie_info, MB_INFO_ITEM item, std::string* item_string); + void updateMovieSelection(void); + void updateFilterSelection(void); + void updateSerienames(void); + void autoFindSerie(void); +}; + +// Class to show Moviebrowser Information, to be used by menu +class CMovieHelp : public CMenuTarget +{ + private: + + public: + CMovieHelp(){}; + ~CMovieHelp(){}; + int exec( CMenuTarget* parent, const std::string & actionKey ); +}; + +// I tried a lot to use the menu.cpp as ListBox selection, and I got three solution which are all garbage. +//Might be replaced by somebody who is familiar with this stuff . + +// CLass to verifiy a menu was selected by the user. There might be better ways to do so. +class CSelectedMenu : public CMenuTarget +{ + public: + bool selected; + CSelectedMenu(void){selected = false;}; +inline int exec(CMenuTarget* parent, const std::string & actionKey){selected = true; return menu_return::RETURN_EXIT;}; +}; + + +// This Class creates a menue item, which writes its caption to an given string (or an given int value to an given variable). +// The programm could use this class to verify, what menu was selected. +// A good listbox class might do the same. There might be better ways to do so. +#define BUFFER_MAX 20 +class CMenuSelector : public CMenuItem +{ + private: + const char * optionName; + char * optionValue; + std::string* optionValueString; + int returnIntValue; + int* returnInt; + int height; + char buffer[BUFFER_MAX]; + public: + CMenuSelector(const char * OptionName, const bool Active = true, char * OptionValue = NULL, int* ReturnInt = NULL,int ReturnIntValue = 0); + CMenuSelector(const char * OptionName, const bool Active , std::string & OptionValue, int* ReturnInt = NULL,int ReturnIntValue = 0); + int exec(CMenuTarget* parent); + int paint(bool selected, bool last = 0); + int getHeight(void) const{return height;}; + bool isSelectable(void) const { return active;} +}; + +// CLass to get the menu line selected by the user. There might be better ways to do so. +class CMenuWidgetSelection : public CMenuWidget +{ + public: + CMenuWidgetSelection(const neutrino_locale_t Name, const std::string & Icon = "", const int mwidth = 400, const int mheight = 576) : CMenuWidget( Name,Icon,mwidth, mheight){;}; + int getSelectedLine(void){return exit_pressed ? -1 : selected;}; +}; + + +class CFileChooser : public CMenuWidget +{ + private: + std::string* dirPath; + + public: + CFileChooser(std::string* path){dirPath= path;}; + int exec(CMenuTarget* parent, const std::string & actionKey); + }; + +typedef enum +{ + DIR_STATE_UNKNOWN = 0, + DIR_STATE_SERVER_DOWN = 1, + DIR_STATE_NOT_MOUNTED = 2, + DIR_STATE_MOUNTED = 3, + DIR_STATE_DISABLED = 4 +}DIR_STATE; + +#define MAX_DIR 10 +class CDirMenu : public CMenuWidget +{ + private: + std::vector* dirList; + DIR_STATE dirState[MAX_DIR]; + std::string dirOptionText[MAX_DIR]; + int dirNfsMountNr[MAX_DIR]; + bool changed; + + void updateDirState(void); + + public: + CDirMenu(std::vector* dir_list); + int exec(CMenuTarget* parent, const std::string & actionKey); + void show(void); + bool isChanged(){return changed;}; + }; + + +// EPG Genre , taken from epgview, TODO: migth be splitted in major/minor to increase handling, might be moved to CMovieInfo +#define GENRE_ALL_COUNT 76 +const CMenuOptionChooser::keyval GENRE_ALL[GENRE_ALL_COUNT] = +{ + { 0x00, LOCALE_GENRE_UNKNOWN}, + { 0x10, LOCALE_GENRE_MOVIE_0}, + { 0x11, LOCALE_GENRE_MOVIE_1}, + { 0x12, LOCALE_GENRE_MOVIE_2}, + { 0x13, LOCALE_GENRE_MOVIE_3}, + { 0x14, LOCALE_GENRE_MOVIE_4}, + { 0x15, LOCALE_GENRE_MOVIE_5}, + { 0x16, LOCALE_GENRE_MOVIE_6}, + { 0x17, LOCALE_GENRE_MOVIE_7}, + { 0x18, LOCALE_GENRE_MOVIE_8}, + { 0x20, LOCALE_GENRE_NEWS_0}, + { 0x21, LOCALE_GENRE_NEWS_1}, + { 0x22, LOCALE_GENRE_NEWS_2}, + { 0x23, LOCALE_GENRE_NEWS_3}, + { 0x24, LOCALE_GENRE_NEWS_4}, + { 0x30, LOCALE_GENRE_SHOW_0}, + { 0x31, LOCALE_GENRE_SHOW_1}, + { 0x32, LOCALE_GENRE_SHOW_2}, + { 0x33, LOCALE_GENRE_SHOW_3}, + { 0x40, LOCALE_GENRE_SPORTS_0}, + { 0x41, LOCALE_GENRE_SPORTS_1}, + { 0x42, LOCALE_GENRE_SPORTS_2}, + { 0x43, LOCALE_GENRE_SPORTS_3}, + { 0x44, LOCALE_GENRE_SPORTS_4}, + { 0x45, LOCALE_GENRE_SPORTS_5}, + { 0x46, LOCALE_GENRE_SPORTS_6}, + { 0x47, LOCALE_GENRE_SPORTS_7}, + { 0x48, LOCALE_GENRE_SPORTS_8}, + { 0x49, LOCALE_GENRE_SPORTS_9}, + { 0x4A, LOCALE_GENRE_SPORTS_10}, + { 0x4B, LOCALE_GENRE_SPORTS_11}, + { 0x50, LOCALE_GENRE_CHILDRENS_PROGRAMMES_0}, + { 0x51, LOCALE_GENRE_CHILDRENS_PROGRAMMES_1}, + { 0x52, LOCALE_GENRE_CHILDRENS_PROGRAMMES_2}, + { 0x53, LOCALE_GENRE_CHILDRENS_PROGRAMMES_3}, + { 0x54, LOCALE_GENRE_CHILDRENS_PROGRAMMES_4}, + { 0x55, LOCALE_GENRE_CHILDRENS_PROGRAMMES_5}, + { 0x60, LOCALE_GENRE_MUSIC_DANCE_0}, + { 0x61, LOCALE_GENRE_MUSIC_DANCE_1}, + { 0x62, LOCALE_GENRE_MUSIC_DANCE_2}, + { 0x63, LOCALE_GENRE_MUSIC_DANCE_3}, + { 0x64, LOCALE_GENRE_MUSIC_DANCE_4}, + { 0x65, LOCALE_GENRE_MUSIC_DANCE_5}, + { 0x66, LOCALE_GENRE_MUSIC_DANCE_6}, + { 0x70, LOCALE_GENRE_ARTS_0}, + { 0x71, LOCALE_GENRE_ARTS_1}, + { 0x72, LOCALE_GENRE_ARTS_2}, + { 0x73, LOCALE_GENRE_ARTS_3}, + { 0x74, LOCALE_GENRE_ARTS_4}, + { 0x75, LOCALE_GENRE_ARTS_5}, + { 0x76, LOCALE_GENRE_ARTS_6}, + { 0x77, LOCALE_GENRE_ARTS_7}, + { 0x78, LOCALE_GENRE_ARTS_8}, + { 0x79, LOCALE_GENRE_ARTS_9}, + { 0x7A, LOCALE_GENRE_ARTS_10}, + { 0x7B, LOCALE_GENRE_ARTS_11}, + { 0x80, LOCALE_GENRE_SOCIAL_POLITICAL_0}, + { 0x81, LOCALE_GENRE_SOCIAL_POLITICAL_1}, + { 0x82, LOCALE_GENRE_SOCIAL_POLITICAL_2}, + { 0x83, LOCALE_GENRE_SOCIAL_POLITICAL_3}, + { 0x90, LOCALE_GENRE_DOCUS_MAGAZINES_0}, + { 0x91, LOCALE_GENRE_DOCUS_MAGAZINES_1}, + { 0x92, LOCALE_GENRE_DOCUS_MAGAZINES_2}, + { 0x93, LOCALE_GENRE_DOCUS_MAGAZINES_3}, + { 0x94, LOCALE_GENRE_DOCUS_MAGAZINES_4}, + { 0x95, LOCALE_GENRE_DOCUS_MAGAZINES_5}, + { 0x96, LOCALE_GENRE_DOCUS_MAGAZINES_6}, + { 0x97, LOCALE_GENRE_DOCUS_MAGAZINES_7}, + { 0xA0, LOCALE_GENRE_TRAVEL_HOBBIES_0}, + { 0xA1, LOCALE_GENRE_TRAVEL_HOBBIES_1}, + { 0xA2, LOCALE_GENRE_TRAVEL_HOBBIES_2}, + { 0xA3, LOCALE_GENRE_TRAVEL_HOBBIES_3}, + { 0xA4, LOCALE_GENRE_TRAVEL_HOBBIES_4}, + { 0xA5, LOCALE_GENRE_TRAVEL_HOBBIES_5}, + { 0xA6, LOCALE_GENRE_TRAVEL_HOBBIES_6}, + { 0xA7, LOCALE_GENRE_TRAVEL_HOBBIES_7} +}; + +#define GENRE_MOVIE_COUNT 9 +const CMenuOptionChooser::keyval genre_movie[GENRE_MOVIE_COUNT] = +{ + { 0, LOCALE_GENRE_MOVIE_0}, + { 1, LOCALE_GENRE_MOVIE_1}, + { 2, LOCALE_GENRE_MOVIE_2}, + { 3, LOCALE_GENRE_MOVIE_3}, + { 4, LOCALE_GENRE_MOVIE_4}, + { 5, LOCALE_GENRE_MOVIE_5}, + { 6, LOCALE_GENRE_MOVIE_6}, + { 7, LOCALE_GENRE_MOVIE_7}, + { 8, LOCALE_GENRE_MOVIE_8} +}; +#define GENRE_NEWS_COUNT 5 +const CMenuOptionChooser::keyval genre_news[GENRE_NEWS_COUNT] = +{ + { 0, LOCALE_GENRE_NEWS_0}, + { 1, LOCALE_GENRE_NEWS_1}, + { 2, LOCALE_GENRE_NEWS_2}, + { 3, LOCALE_GENRE_NEWS_3}, + { 4, LOCALE_GENRE_NEWS_4} +}; +#define GENRE_SHOW_COUNT 4 +const CMenuOptionChooser::keyval genre_show[GENRE_SHOW_COUNT] = +{ + { 0, LOCALE_GENRE_SHOW_0}, + { 1, LOCALE_GENRE_SHOW_1}, + { 2, LOCALE_GENRE_SHOW_2}, + { 3, LOCALE_GENRE_SHOW_3} +}; +#define GENRE_SPORTS_COUNT 12 +const CMenuOptionChooser::keyval genre_sports[GENRE_SPORTS_COUNT] = +{ + { 0, LOCALE_GENRE_SPORTS_0}, + { 1, LOCALE_GENRE_SPORTS_1}, + { 2, LOCALE_GENRE_SPORTS_2}, + { 3, LOCALE_GENRE_SPORTS_3}, + { 4, LOCALE_GENRE_SPORTS_4}, + { 5, LOCALE_GENRE_SPORTS_5}, + { 6, LOCALE_GENRE_SPORTS_6}, + { 7, LOCALE_GENRE_SPORTS_7}, + { 8, LOCALE_GENRE_SPORTS_8}, + { 9, LOCALE_GENRE_SPORTS_9}, + {10, LOCALE_GENRE_SPORTS_10}, + {11, LOCALE_GENRE_SPORTS_11} +}; +#define GENRE_CHILDRENS_PROGRAMMES_COUNT 6 +const CMenuOptionChooser::keyval genre_childrens_programmes[GENRE_CHILDRENS_PROGRAMMES_COUNT] = +{ + { 0, LOCALE_GENRE_CHILDRENS_PROGRAMMES_0}, + { 1, LOCALE_GENRE_CHILDRENS_PROGRAMMES_1}, + { 2, LOCALE_GENRE_CHILDRENS_PROGRAMMES_2}, + { 3, LOCALE_GENRE_CHILDRENS_PROGRAMMES_3}, + { 4, LOCALE_GENRE_CHILDRENS_PROGRAMMES_4}, + { 5, LOCALE_GENRE_CHILDRENS_PROGRAMMES_5} +}; +#define GENRE_MUSIC_DANCE_COUNT 7 +const CMenuOptionChooser::keyval genre_music_dance[GENRE_MUSIC_DANCE_COUNT] = +{ + { 0, LOCALE_GENRE_MUSIC_DANCE_0}, + { 1, LOCALE_GENRE_MUSIC_DANCE_1}, + { 2, LOCALE_GENRE_MUSIC_DANCE_2}, + { 3, LOCALE_GENRE_MUSIC_DANCE_3}, + { 4, LOCALE_GENRE_MUSIC_DANCE_4}, + { 5, LOCALE_GENRE_MUSIC_DANCE_5}, + { 6, LOCALE_GENRE_MUSIC_DANCE_6} +}; +#define GENRE_ARTS_COUNT 12 +const CMenuOptionChooser::keyval genre_arts_dance[GENRE_ARTS_COUNT] = +{ + { 0, LOCALE_GENRE_ARTS_0}, + { 1, LOCALE_GENRE_ARTS_1}, + { 2, LOCALE_GENRE_ARTS_2}, + { 3, LOCALE_GENRE_ARTS_3}, + { 4, LOCALE_GENRE_ARTS_4}, + { 5, LOCALE_GENRE_ARTS_5}, + { 6, LOCALE_GENRE_ARTS_6}, + { 7, LOCALE_GENRE_ARTS_7}, + { 8, LOCALE_GENRE_ARTS_8}, + { 9, LOCALE_GENRE_ARTS_9}, + {10, LOCALE_GENRE_ARTS_10}, + {11, LOCALE_GENRE_ARTS_11} +}; +#define GENRE_SOCIAL_POLITICAL_COUNT 4 +const CMenuOptionChooser::keyval genre_social_political[GENRE_SOCIAL_POLITICAL_COUNT] = +{ + { 0, LOCALE_GENRE_SOCIAL_POLITICAL_0}, + { 1, LOCALE_GENRE_SOCIAL_POLITICAL_1}, + { 2, LOCALE_GENRE_SOCIAL_POLITICAL_2}, + { 3, LOCALE_GENRE_SOCIAL_POLITICAL_3} +}; +#define GENRE_DOCUS_MAGAZINES_COUNT 8 +const CMenuOptionChooser::keyval genre_docus_magazines[GENRE_DOCUS_MAGAZINES_COUNT] = +{ + { 0, LOCALE_GENRE_DOCUS_MAGAZINES_0}, + { 1, LOCALE_GENRE_DOCUS_MAGAZINES_1}, + { 2, LOCALE_GENRE_DOCUS_MAGAZINES_2}, + { 3, LOCALE_GENRE_DOCUS_MAGAZINES_3}, + { 4, LOCALE_GENRE_DOCUS_MAGAZINES_4}, + { 5, LOCALE_GENRE_DOCUS_MAGAZINES_5}, + { 6, LOCALE_GENRE_DOCUS_MAGAZINES_6}, + { 7, LOCALE_GENRE_DOCUS_MAGAZINES_7} +}; +#define GENRE_TRAVEL_HOBBIES_COUNT 8 +const CMenuOptionChooser::keyval genre_travel_hobbies[GENRE_TRAVEL_HOBBIES_COUNT] = +{ + { 0, LOCALE_GENRE_TRAVEL_HOBBIES_0}, + { 1, LOCALE_GENRE_TRAVEL_HOBBIES_1}, + { 2, LOCALE_GENRE_TRAVEL_HOBBIES_2}, + { 3, LOCALE_GENRE_TRAVEL_HOBBIES_3}, + { 4, LOCALE_GENRE_TRAVEL_HOBBIES_4}, + { 5, LOCALE_GENRE_TRAVEL_HOBBIES_5}, + { 6, LOCALE_GENRE_TRAVEL_HOBBIES_6}, + { 7, LOCALE_GENRE_TRAVEL_HOBBIES_7} +}; + +#define GENRE_MAJOR_COUNT 10 +const CMenuOptionChooser::keyval genre_major[GENRE_MAJOR_COUNT] = +{ + { 1, LOCALE_GENRE_MOVIE_0}, + { 2, LOCALE_GENRE_NEWS_0}, + { 3, LOCALE_GENRE_SHOW_0}, + { 4, LOCALE_GENRE_SPORTS_0}, + { 5, LOCALE_GENRE_CHILDRENS_PROGRAMMES_0}, + { 6, LOCALE_GENRE_MUSIC_DANCE_0}, + { 7, LOCALE_GENRE_ARTS_0}, + { 8, LOCALE_GENRE_SOCIAL_POLITICAL_0}, + { 9, LOCALE_GENRE_DOCUS_MAGAZINES_0}, + { 10, LOCALE_GENRE_TRAVEL_HOBBIES_0} +}; + + +#endif /*MOVIEBROWSER_H_*/ + + + + diff --git a/src/gui/movieinfo.cpp b/src/gui/movieinfo.cpp new file mode 100644 index 000000000..63b3a8062 --- /dev/null +++ b/src/gui/movieinfo.cpp @@ -0,0 +1,1042 @@ + /*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: movieinfo.cpp . + + Description: Implementation of the CMovieInfo class + This class loads, saves and shows the movie Information from the any .xml File on HD + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + + Revision History: + Date Author Change Description + Nov 2005 Günther initial start + +****************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +//#define XMLTREE_LIB +#ifdef XMLTREE_LIB +#include +#include +#endif +#define TRACE printf +#define VLC_URI "vlc://" + +/************************************************************************ + +************************************************************************/ +CMovieInfo::CMovieInfo() +{ + //TRACE("[mi] new\r\n"); +} + +CMovieInfo::~CMovieInfo() +{ + //TRACE("[mi] del\r\n"); + ; +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::convertTs2XmlName(char *char_filename, int size) +{ + bool result = false; + std::string filename = char_filename; + if (convertTs2XmlName(&filename) == true) { + strncpy(char_filename, filename.c_str(), size); + char_filename[size - 1] = 0; + result = true; + } + return (result); +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::convertTs2XmlName(std::string * filename) +{ + //TRACE("[mi]->convertTs2XmlName\r\n"); + int bytes = filename->find(".ts"); + bool result = false; + + if (bytes != -1) { + if (bytes > 3) { + if ((*filename)[bytes - 4] == '.') { + bytes = bytes - 4; + } + } + *filename = filename->substr(0, bytes) + ".xml"; + result = true; + } else // not a TS file, return!!!!! + { + //TRACE(" not a TS file "); + } + + return (result); +} + +/************************************************************************ + +************************************************************************/ +#define XML_ADD_TAG_STRING(_xml_text_,_tag_name_,_tag_content_){ \ + _xml_text_ += "\t\t<"_tag_name_">"; \ + _xml_text_ += _tag_content_; \ + _xml_text_ += "\n";} + +#define XML_ADD_TAG_UNSIGNED(_xml_text_,_tag_name_,_tag_content_){\ + _xml_text_ += "\t\t<"_tag_name_">";\ + char _tmp_[50];\ + sprintf(_tmp_, "%u", (unsigned int) _tag_content_);\ + _xml_text_ += _tmp_;\ + _xml_text_ += "\n";} + +#define XML_ADD_TAG_LONG(_xml_text_,_tag_name_,_tag_content_){\ + _xml_text_ += "\t\t<"_tag_name_">";\ + char _tmp_[50];\ + sprintf(_tmp_, "%llu", _tag_content_);\ + _xml_text_ += _tmp_;\ + _xml_text_ += "\n";} + +#define XML_GET_DATA_STRING(_node_,_tag_,_string_dest_){\ + if(!strcmp(_node_->GetType(), _tag_))\ + {\ + if(_node_->GetData() != NULL)\ + {\ + _string_dest_ = _node_->GetData();\ + }\ + }} +#define XML_GET_DATA_INT(_node_,_tag_,_int_dest_){\ + if(!strcmp(_node_->GetType(), _tag_))\ + {\ + if(_node_->GetData() != NULL)\ + {\ + _int_dest_ = atoi(_node_->GetData());\ + }\ + }} + +#define XML_GET_DATA_LONG(_node_,_tag_,_int_dest_){\ + if(!strcmp(_node_->GetType(), _tag_))\ + {\ + if(_node_->GetData() != NULL)\ + {\ + sscanf(_node_->GetData(), "%llu", &_int_dest_); \ + }\ + }} +//sscanf(_node_->GetData(), "%lld", &_int_dest_); + +bool CMovieInfo::encodeMovieInfoXml(std::string * extMessage, MI_MOVIE_INFO * movie_info) +{ + //TRACE("[mi]->encodeMovieInfoXml\r\n"); + char tmp[40]; + + *extMessage = "\n\n"; + *extMessage += "<" MI_XML_TAG_NEUTRINO " commandversion=\"1\">\n"; + *extMessage += "\t<" MI_XML_TAG_RECORD " command=\""; + *extMessage += "record"; + *extMessage += "\">\n"; + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_CHANNELNAME, movie_info->epgChannel); + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_EPGTITLE, movie_info->epgTitle); + XML_ADD_TAG_LONG(*extMessage, MI_XML_TAG_ID, movie_info->epgId); + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_INFO1, movie_info->epgInfo1); + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_INFO2, movie_info->epgInfo2); + XML_ADD_TAG_LONG(*extMessage, MI_XML_TAG_EPGID, movie_info->epgEpgId); // %llu + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_MODE, movie_info->epgMode); //%d + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_VIDEOPID, movie_info->epgVideoPid); //%u + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_VIDEOTYPE, movie_info->VideoType); //%u + if (movie_info->audioPids.size() > 0) { + //*extMessage += "\t\t<"MI_XML_TAG_AUDIOPIDS" selected=\""; + //sprintf(tmp, "%u", movie_info->audioPids[0].epgAudioPid); //pids.APIDs[i].pid); + //*extMessage += tmp; + //*extMessage += "\">\n"; + *extMessage += "\t\t<" MI_XML_TAG_AUDIOPIDS ">\n"; + + for (unsigned int i = 0; i < movie_info->audioPids.size(); i++) // pids.APIDs.size() + { + *extMessage += "\t\t\t<" MI_XML_TAG_AUDIO " " MI_XML_TAG_PID "=\""; + sprintf(tmp, "%u", movie_info->audioPids[i].epgAudioPid); //pids.APIDs[i].pid); + *extMessage += tmp; + *extMessage += "\" " MI_XML_TAG_ATYPE "=\""; + sprintf(tmp, "%u", movie_info->audioPids[i].atype); //pids.APIDs[i].pid); + *extMessage += tmp; + *extMessage += "\" " MI_XML_TAG_SELECTED "=\""; + sprintf(tmp, "%u", movie_info->audioPids[i].selected); //pids.APIDs[i].pid); + *extMessage += tmp; + *extMessage += "\" " MI_XML_TAG_NAME "=\""; + *extMessage += movie_info->audioPids[i].epgAudioPidName; // ZapitTools::UTF8_to_UTF8XML(g_RemoteControl->current_PIDs.APIDs[i].desc); + *extMessage += "\"/>\n"; + } + *extMessage += "\t\t\n"; + } + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_VTXTPID, movie_info->epgVTXPID); //%u + /***************************************************** + * new tags */ + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_GENRE_MAJOR, movie_info->genreMajor); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_GENRE_MINOR, movie_info->genreMinor); + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_SERIE_NAME, movie_info->serieName); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_LENGTH, movie_info->length); + XML_ADD_TAG_STRING(*extMessage, MI_XML_TAG_PRODUCT_COUNTRY, movie_info->productionCountry); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_PRODUCT_DATE, movie_info->productionDate); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_QUALITY, movie_info->quality); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_PARENTAL_LOCKAGE, movie_info->parentalLockAge); + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_DATE_OF_LAST_PLAY, movie_info->dateOfLastPlay); + *extMessage += "\t\t<" MI_XML_TAG_BOOKMARK ">\n"; + *extMessage += "\t"; + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_BOOKMARK_START, movie_info->bookmarks.start); + *extMessage += "\t"; + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_BOOKMARK_END, movie_info->bookmarks.end); + *extMessage += "\t"; + XML_ADD_TAG_UNSIGNED(*extMessage, MI_XML_TAG_BOOKMARK_LAST, movie_info->bookmarks.lastPlayStop); + for (int i = 0; i < MI_MOVIE_BOOK_USER_MAX; i++) { + if (movie_info->bookmarks.user[i].pos != 0 || i == 0) { + // encode any valid book, at least 1 + *extMessage += "\t\t\t<" MI_XML_TAG_BOOKMARK_USER " " MI_XML_TAG_BOOKMARK_USER_POS "=\""; + sprintf(tmp, "%d", movie_info->bookmarks.user[i].pos); //pids.APIDs[i].pid); + *extMessage += tmp; + *extMessage += "\" " MI_XML_TAG_BOOKMARK_USER_TYPE "=\""; + sprintf(tmp, "%d", movie_info->bookmarks.user[i].length); //pids.APIDs[i].pid); + *extMessage += tmp; + *extMessage += "\" " MI_XML_TAG_BOOKMARK_USER_NAME "=\""; + *extMessage += movie_info->bookmarks.user[i].name; + *extMessage += "\"/>\n"; + } + } + + *extMessage += "\t\t\n"; + /*****************************************************/ + + *extMessage += "\t\n"; + *extMessage += "\n"; + return true; +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::saveMovieInfo(MI_MOVIE_INFO & movie_info, CFile * file) +{ + //TRACE("[mi]->saveXml \r\n"); + bool result = true; + std::string text; + CFile file_xml; + + if (file == NULL) { + file_xml.Name = movie_info.file.Name; + result = convertTs2XmlName(&file_xml.Name); + } else { + file_xml.Name = file->Name; + } + TRACE("[mi] saveXml: %s\r\n", file_xml.Name.c_str()); + + if (result == true) { + result = encodeMovieInfoXml(&text, &movie_info); + if (result == true) { + result = saveFile(file_xml, text.c_str(), text.size()); // save + if (result == false) { + TRACE("[mi] saveXml: save error\r\n"); + } + } else { + TRACE("[mi] saveXml: encoding error\r\n"); + } + } else { + TRACE("[mi] saveXml: error\r\n"); + } + return (result); +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::loadMovieInfo(MI_MOVIE_INFO * movie_info, CFile * file) +{ + //TRACE("[mi]->loadMovieInfo \r\n"); + bool result = true; + CFile file_xml; + + if (file == NULL) { + // if there is no give file, we use the file name from movie info but we have to convert the ts name to xml name first + file_xml.Name = movie_info->file.Name; + result = convertTs2XmlName(&file_xml.Name); + } else { + file_xml.Name = file->Name; + } + + if (result == true) { + // load xml file in buffer + char text[6000]; + result = loadFile(file_xml, text, 6000); + if (result == true) { +#ifdef XMLTREE_LIB + result = parseXmlTree(text, movie_info); +#else /* XMLTREE_LIB */ + result = parseXmlQuickFix(text, movie_info); +#endif /* XMLTREE_LIB */ + } + } + if (movie_info->productionDate > 50 && movie_info->productionDate < 200) // backwardcompaibility + movie_info->productionDate += 1900; + + return (result); +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::parseXmlTree(char *text, MI_MOVIE_INFO * movie_info) +{ +#ifndef XMLTREE_LIB + return (false); // no XML lib available return false +#else /* XMLTREE_LIB */ + + //int helpIDtoLoad = 80; + + //XMLTreeParser *parser=new XMLTreeParser("ISO-8859-1"); + XMLTreeParser *parser = new XMLTreeParser(NULL); + + if (!parser->Parse(text, strlen(text), 1)) { + TRACE("parse error: %s at line %d \r\n", parser->ErrorString(parser->GetErrorCode()), parser->GetCurrentLineNumber()); + //fclose(in); + delete parser; + return (false); + } + + XMLTreeNode *root = parser->RootNode(); + if (!root) { + TRACE(" root error \r\n"); + return (false); + } + + if (strcmp(root->GetType(), MI_XML_TAG_NEUTRINO)) { + TRACE("not neutrino file. %s", root->GetType()); + return (false); + } + + XMLTreeNode *node = parser->RootNode(); + + for (node = node->GetChild(); node; node = node->GetNext()) { + if (!strcmp(node->GetType(), MI_XML_TAG_RECORD)) { + for (XMLTreeNode * xam1 = node->GetChild(); xam1; xam1 = xam1->GetNext()) { + XML_GET_DATA_STRING(xam1, MI_XML_TAG_CHANNELNAME, movie_info->epgChannel); + XML_GET_DATA_STRING(xam1, MI_XML_TAG_EPGTITLE, movie_info->epgTitle); + XML_GET_DATA_LONG(xam1, MI_XML_TAG_ID, movie_info->epgId); + XML_GET_DATA_STRING(xam1, MI_XML_TAG_INFO1, movie_info->epgInfo1); + XML_GET_DATA_STRING(xam1, MI_XML_TAG_INFO2, movie_info->epgInfo2); + XML_GET_DATA_LONG(xam1, MI_XML_TAG_EPGID, movie_info->epgEpgId); // %llu + XML_GET_DATA_INT(xam1, MI_XML_TAG_MODE, movie_info->epgMode); //%d + XML_GET_DATA_INT(xam1, MI_XML_TAG_VIDEOPID, movie_info->epgVideoPid); //%u + XML_GET_DATA_INT(xam1, MI_XML_TAG_VIDEOTYPE, movie_info->VideoType); //%u + if (!strcmp(xam1->GetType(), MI_XML_TAG_AUDIOPIDS)) { + for (XMLTreeNode * xam2 = xam1->GetChild(); xam2; xam2 = xam2->GetNext()) { + if (!strcmp(xam2->GetType(), MI_XML_TAG_AUDIO)) { + EPG_AUDIO_PIDS pids; + pids.epgAudioPid = atoi(xam2->GetAttributeValue((char *)MI_XML_TAG_PID)); + pids.atype = atoi(xam2->GetAttributeValue((char *)MI_XML_TAG_ATYPE)); + pids.selected = atoi(xam2->GetAttributeValue((char *)MI_XML_TAG_SELECTED)); + pids.epgAudioPidName = xam2->GetAttributeValue((char *)MI_XML_TAG_NAME); +//printf("MOVIE INFO: apid %d type %d name %s selected %d\n", pids.epgAudioPid, pids.atype, pids.epgAudioPidName.c_str(), pids.selected); + movie_info->audioPids.push_back(pids); + } + } + } + XML_GET_DATA_INT(xam1, MI_XML_TAG_VTXTPID, movie_info->epgVTXPID); //%u + /***************************************************** + * new tags */ + XML_GET_DATA_INT(xam1, MI_XML_TAG_GENRE_MAJOR, movie_info->genreMajor); + XML_GET_DATA_INT(xam1, MI_XML_TAG_GENRE_MINOR, movie_info->genreMinor); + XML_GET_DATA_STRING(xam1, MI_XML_TAG_SERIE_NAME, movie_info->serieName); + XML_GET_DATA_INT(xam1, MI_XML_TAG_LENGTH, movie_info->length); + XML_GET_DATA_STRING(xam1, MI_XML_TAG_PRODUCT_COUNTRY, movie_info->productionCountry); + //if(!strcmp(xam1->GetType(), MI_XML_TAG_PRODUCT_COUNTRY)) if(xam1->GetData() != NULL)strncpy(movie_info->productionCountry, xam1->GetData(),4); + XML_GET_DATA_INT(xam1, MI_XML_TAG_PRODUCT_DATE, movie_info->productionDate); + XML_GET_DATA_INT(xam1, MI_XML_TAG_QUALITY, movie_info->quality); + XML_GET_DATA_INT(xam1, MI_XML_TAG_PARENTAL_LOCKAGE, movie_info->parentalLockAge); + XML_GET_DATA_INT(xam1, MI_XML_TAG_DATE_OF_LAST_PLAY, movie_info->dateOfLastPlay); + + if (!strcmp(xam1->GetType(), MI_XML_TAG_BOOKMARK)) { + for (XMLTreeNode * xam2 = xam1->GetChild(); xam2; xam2 = xam2->GetNext()) { + XML_GET_DATA_INT(xam2, MI_XML_TAG_BOOKMARK_START, movie_info->bookmarks.start); + XML_GET_DATA_INT(xam2, MI_XML_TAG_BOOKMARK_END, movie_info->bookmarks.end); + XML_GET_DATA_INT(xam2, MI_XML_TAG_BOOKMARK_LAST, movie_info->bookmarks.lastPlayStop); + } + } + /*****************************************************/ + } + } + } + + delete parser; + strReplace(movie_info->epgTitle, """, "\""); + strReplace(movie_info->epgInfo1, """, "\""); + strReplace(movie_info->epgTitle, "'", "'"); + strReplace(movie_info->epgInfo1, "'", "'"); + if (movie_info->epgInfo2 == "") { + movie_info->epgInfo2 = movie_info->epgInfo1; + //movie_info->epgInfo1 = ""; + } else { + strReplace(movie_info->epgInfo2, """, "\""); + strReplace(movie_info->epgInfo2, "'", "'"); + } +#endif /* XMLTREE_LIB */ + return (true); +} + +/************************************************************************ + +************************************************************************/ +void CMovieInfo::showMovieInfo(MI_MOVIE_INFO & movie_info) +{ + std::string print_buffer; + tm *date_tm; + char date_char[100]; + // prepare print buffer + print_buffer = movie_info.epgInfo1; + print_buffer += "\n"; + print_buffer += movie_info.epgInfo2; + + if (movie_info.productionCountry.size() != 0 || movie_info.productionDate != 0) { + print_buffer += "\n"; + print_buffer += movie_info.productionCountry; + print_buffer += " "; + snprintf(date_char, 12, "%4d", movie_info.productionDate + 1900); + print_buffer += date_char; + } + + if (!movie_info.serieName.empty()) { + print_buffer += "\n\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_SERIE); + print_buffer += ": "; + print_buffer += movie_info.serieName; + } + if (!movie_info.epgChannel.empty()) { + print_buffer += "\n\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_CHANNEL); + print_buffer += ": "; + print_buffer += movie_info.epgChannel; + } + if (movie_info.quality != 0) { + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_QUALITY); + print_buffer += ": "; + snprintf(date_char, 12, "%2d", movie_info.quality); + print_buffer += date_char; + } + if (movie_info.parentalLockAge != 0) { + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_PARENTAL_LOCKAGE); + print_buffer += ": "; + snprintf(date_char, 12, "%2d", movie_info.parentalLockAge); + print_buffer += date_char; + print_buffer += " Jahre"; + } + if (movie_info.length != 0) { + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_LENGTH); + print_buffer += ": "; + snprintf(date_char, 12, "%3d", movie_info.length); + print_buffer += date_char; + } + if (movie_info.audioPids.size() != 0) { + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_AUDIO); + print_buffer += ": "; + for (unsigned int i = 0; i < movie_info.audioPids.size(); i++) { + print_buffer += movie_info.audioPids[i].epgAudioPidName; + print_buffer += ", "; + } + } + + print_buffer += "\n\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_PREVPLAYDATE); + print_buffer += ": "; + date_tm = localtime(&movie_info.dateOfLastPlay); + snprintf(date_char, 12, "%02d.%02d.%04d", date_tm->tm_mday, date_tm->tm_mon + 1, date_tm->tm_year + 1900); + print_buffer += date_char; + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_RECORDDATE); + print_buffer += ": "; + date_tm = localtime(&movie_info.file.Time); + snprintf(date_char, 12, "%02d.%02d.%04d", date_tm->tm_mday, date_tm->tm_mon + 1, date_tm->tm_year + 1900); + print_buffer += date_char; + if (movie_info.file.Size != 0) { + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_SIZE); + print_buffer += ": "; + //snprintf(date_char, 12,"%4llu",movie_info.file.Size>>20); + sprintf(date_char, "%llu", movie_info.file.Size >> 20); + print_buffer += date_char; + //print_buffer += "\n"; + } + print_buffer += "\n"; + print_buffer += g_Locale->getText(LOCALE_MOVIEBROWSER_INFO_PATH); + print_buffer += ": "; + print_buffer += movie_info.file.Name; + print_buffer += "\n"; + + ShowMsg2UTF(movie_info.epgTitle.empty()? movie_info.file.getFileName().c_str() : movie_info.epgTitle.c_str(), print_buffer.c_str(), CMsgBox::mbrBack, CMsgBox::mbBack); // UTF-8*/ + +} + +/************************************************************************ + +************************************************************************/ +void CMovieInfo::printDebugMovieInfo(MI_MOVIE_INFO & movie_info) +{ + TRACE(" FileName: %s", movie_info.file.Name.c_str()); + //TRACE(" FilePath: %s", movie_info.file.GetFilePath ); + //TRACE(" FileLength: %d", movie_info.file.GetLength ); + //TRACE(" FileStatus: %d", movie_info.file.GetStatus ); + + TRACE(" ********** Movie Data ***********\r\n"); // (date, month, year) + TRACE(" dateOfLastPlay: \t%d\r\n", (int)movie_info.dateOfLastPlay); // (date, month, year) + TRACE(" dirItNr: \t\t%d\r\n", movie_info.dirItNr); // + TRACE(" genreMajor: \t\t%d\r\n", movie_info.genreMajor); //genreMajor; + TRACE(" genreMinor: \t\t%d\r\n", movie_info.genreMinor); //genreMinor; + TRACE(" length: \t\t%d\r\n", movie_info.length); // (minutes) + TRACE(" quality: \t\t%d\r\n", movie_info.quality); // (3 stars: classics, 2 stars: very good, 1 star: good, 0 stars: OK) + TRACE(" productionCount:\t>%s<\r\n", movie_info.productionCountry.c_str()); + TRACE(" productionDate: \t%d\r\n", movie_info.productionDate); // (Year) years since 1900 + TRACE(" parentalLockAge: \t\t\t%d\r\n", movie_info.parentalLockAge); // MI_PARENTAL_LOCKAGE (0,6,12,16,18) + TRACE(" format: \t\t%d\r\n", movie_info.format); // MI_VIDEO_FORMAT(16:9, 4:3) + TRACE(" audio: \t\t%d\r\n", movie_info.audio); // MI_AUDIO (AC3, Deutsch, Englisch) + TRACE(" epgId: \t\t%d\r\n", (int)movie_info.epgId); + TRACE(" epgEpgId: \t\t%llu\r\n", movie_info.epgEpgId); + TRACE(" epgMode: \t\t%d\r\n", movie_info.epgMode); + TRACE(" epgVideoPid: \t\t%d\r\n", movie_info.epgVideoPid); + TRACE(" epgVTXPID: \t\t%d\r\n", movie_info.epgVTXPID); + TRACE(" Size: \t\t%d\r\n", (int)movie_info.file.Size >> 20); + TRACE(" Date: \t\t%d\r\n", (int)movie_info.file.Time); + + for (unsigned int i = 0; i < movie_info.audioPids.size(); i++) { + TRACE(" audioPid (%d): \t\t%d\r\n", i, movie_info.audioPids[i].epgAudioPid); + TRACE(" audioName(%d): \t\t>%s<\r\n", i, movie_info.audioPids[i].epgAudioPidName.c_str()); + } + + TRACE(" epgTitle: \t\t>%s<\r\n", movie_info.epgTitle.c_str()); + TRACE(" epgInfo1:\t\t>%s<\r\n", movie_info.epgInfo1.c_str()); //epgInfo1 + TRACE(" epgInfo2:\t\t\t>%s<\r\n", movie_info.epgInfo2.c_str()); //epgInfo2 + TRACE(" epgChannel:\t\t>%s<\r\n", movie_info.epgChannel.c_str()); + TRACE(" serieName:\t\t>%s<\r\n", movie_info.serieName.c_str()); // (name e.g. 'StarWars) + + TRACE(" bookmarks start: \t%d\r\n", movie_info.bookmarks.start); + TRACE(" bookmarks end: \t%d\r\n", movie_info.bookmarks.end); + TRACE(" bookmarks lastPlayStop: %d\r\n", movie_info.bookmarks.lastPlayStop); + + for (int i = 0; i < MI_MOVIE_BOOK_USER_MAX; i++) { + if (movie_info.bookmarks.user[i].pos != 0 || i == 0) { + TRACE(" bookmarks user, pos:%d, type:%d, name: >%s<\r\n", movie_info.bookmarks.user[i].pos, movie_info.bookmarks.user[i].length, movie_info.bookmarks.user[i].name.c_str()); + } + } +} + +/************************************************************************ + +************************************************************************/ +int find_next_char(char to_find, char *text, int start_pos, int end_pos) +{ + while (start_pos < end_pos) { + if (text[start_pos] == to_find) { + return (start_pos); + } + start_pos++; + } + return (-1); +} + +#define GET_XML_DATA_STRING(_text_,_pos_,_tag_,_dest_)\ + if(strncmp(&_text_[_pos_],_tag_,sizeof(_tag_)-1) == 0)\ + {\ + _pos_ += sizeof(_tag_) ;\ + int pos_prev = _pos_;\ + while(_pos_ < bytes && _text_[_pos_] != '<' ) _pos_++;\ + _dest_ = "";\ + _dest_.append(&_text_[pos_prev],_pos_ - pos_prev );\ + _pos_ += sizeof(_tag_);\ + continue;\ + } + +#define GET_XML_DATA_INT(_text_,_pos_,_tag_,_dest_)\ + if(strncmp(&_text_[pos],_tag_,sizeof(_tag_)-1) == 0)\ + {\ + _pos_ += sizeof(_tag_) ;\ + int pos_prev = _pos_;\ + while(_pos_ < bytes && _text_[_pos_] != '<' ) pos++;\ + _dest_ = atoi(&_text_[pos_prev]);\ + continue;\ + } +#define GET_XML_DATA_LONG(_text_,_pos_,_tag_,_dest_)\ + if(strncmp(&_text_[pos],_tag_,sizeof(_tag_)-1) == 0)\ + {\ + _pos_ += sizeof(_tag_) ;\ + int pos_prev = _pos_;\ + while(_pos_ < bytes && _text_[_pos_] != '<' ) pos++;\ + _dest_ = atoll(&_text_[pos_prev]);\ + continue;\ + } + +//void CMovieInfo::strReplace(std::string& orig, const char* fstr, const std::string rstr) +void strReplace(std::string & orig, const char *fstr, const std::string rstr) +{ +// replace all occurrence of fstr with rstr and, and returns a reference to itself + unsigned int index = 0; + unsigned int fstrlen = strlen(fstr); + int rstrlen = rstr.size(); + + while ((index = orig.find(fstr, index)) != std::string::npos) { + orig.replace(index, fstrlen, rstr); + index += rstrlen; + } +} + + /************************************************************************ +************************************************************************/ +bool CMovieInfo::parseXmlQuickFix(char *text, MI_MOVIE_INFO * movie_info) +{ +#ifndef XMLTREE_LIB + int bookmark_nr = 0; + movie_info->dateOfLastPlay = 0; //100*366*24*60*60; // (date, month, year) + //bool result = false; + + int bytes = strlen(text); + /** search ****/ + int pos = 0; + + EPG_AUDIO_PIDS audio_pids; + + while ((pos = find_next_char('<', text, pos, bytes)) != -1) { + pos++; + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_CHANNELNAME, movie_info->epgChannel) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_EPGTITLE, movie_info->epgTitle) + GET_XML_DATA_LONG(text, pos, MI_XML_TAG_ID, movie_info->epgId) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_INFO1, movie_info->epgInfo1) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_INFO2, movie_info->epgInfo2) + GET_XML_DATA_LONG(text, pos, MI_XML_TAG_EPGID, movie_info->epgEpgId) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_MODE, movie_info->epgMode) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_VIDEOPID, movie_info->epgVideoPid) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_VIDEOTYPE, movie_info->VideoType) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_NAME, movie_info->epgChannel) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_VTXTPID, movie_info->epgVTXPID) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_GENRE_MAJOR, movie_info->genreMajor) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_GENRE_MINOR, movie_info->genreMinor) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_SERIE_NAME, movie_info->serieName) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_LENGTH, movie_info->length) + GET_XML_DATA_STRING(text, pos, MI_XML_TAG_PRODUCT_COUNTRY, movie_info->productionCountry) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_PRODUCT_DATE, movie_info->productionDate) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_PARENTAL_LOCKAGE, movie_info->parentalLockAge) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_QUALITY, movie_info->quality) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_DATE_OF_LAST_PLAY, movie_info->dateOfLastPlay) + if (strncmp(&text[pos], MI_XML_TAG_AUDIOPIDS, sizeof(MI_XML_TAG_AUDIOPIDS) - 1) == 0) + pos += sizeof(MI_XML_TAG_AUDIOPIDS); + + /* parse audio pids */ + if (strncmp(&text[pos], MI_XML_TAG_AUDIO, sizeof(MI_XML_TAG_AUDIO) - 1) == 0) { + pos += sizeof(MI_XML_TAG_AUDIO); + + int pos2; + char *ptr; + + pos2 = -1; + ptr = strstr(&text[pos], MI_XML_TAG_PID); + if (ptr) + pos2 = (int)ptr - (int)&text[pos]; + //pos2 = strcspn(&text[pos],MI_XML_TAG_PID); + if (pos2 >= 0) { + pos2 += sizeof(MI_XML_TAG_PID); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') + audio_pids.epgAudioPid = atoi(&text[pos + pos2 + 1]); + } else + audio_pids.epgAudioPid = 0; + + audio_pids.atype = 0; + pos2 = -1; + ptr = strstr(&text[pos], MI_XML_TAG_ATYPE); + if (ptr) + pos2 = (int)ptr - (int)&text[pos]; + //pos2 = strcspn(&text[pos],MI_XML_TAG_ATYPE); + if (pos2 >= 0) { + pos2 += sizeof(MI_XML_TAG_ATYPE); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') + audio_pids.atype = atoi(&text[pos + pos2 + 1]); + } + + audio_pids.selected = 0; + pos2 = -1; + ptr = strstr(&text[pos], MI_XML_TAG_SELECTED); + if (ptr) + pos2 = (int)ptr - (int)&text[pos]; + //pos2 = strcspn(&text[pos],MI_XML_TAG_SELECTED); + if (pos2 >= 0) { + pos2 += sizeof(MI_XML_TAG_SELECTED); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') + audio_pids.selected = atoi(&text[pos + pos2 + 1]); + } + + audio_pids.epgAudioPidName = ""; + //pos2 = strcspn(&text[pos],MI_XML_TAG_NAME); + pos2 = -1; + ptr = strstr(&text[pos], MI_XML_TAG_NAME); + if (ptr) + pos2 = (int)ptr - (int)&text[pos]; + if (pos2 >= 0) { + pos2 += sizeof(MI_XML_TAG_PID); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') { + int pos3 = pos2 + 1; + while (text[pos + pos3] != '\"' && text[pos + pos3] != 0 && text[pos + pos3] != '/') + pos3++; + if (text[pos + pos3] == '\"') + audio_pids.epgAudioPidName.append(&text[pos + pos2 + 1], pos3 - pos2 - 1); + } + } + printf("MOVIE INFO: apid %d type %d name %s selected %d\n", audio_pids.epgAudioPid, audio_pids.atype, audio_pids.epgAudioPidName.c_str(), audio_pids.selected); + movie_info->audioPids.push_back(audio_pids); + } + /* parse bookmarks */ + GET_XML_DATA_INT(text, pos, MI_XML_TAG_BOOKMARK_START, movie_info->bookmarks.start) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_BOOKMARK_END, movie_info->bookmarks.end) + GET_XML_DATA_INT(text, pos, MI_XML_TAG_BOOKMARK_LAST, movie_info->bookmarks.lastPlayStop) + + if (bookmark_nr < MI_MOVIE_BOOK_USER_MAX) { + if (strncmp(&text[pos], MI_XML_TAG_BOOKMARK_USER, sizeof(MI_XML_TAG_BOOKMARK_USER) - 1) == 0) { + pos += sizeof(MI_XML_TAG_BOOKMARK_USER); + //int pos2 = strcspn(&text[pos],MI_XML_TAG_BOOKMARK_USER_POS); + if (strcspn(&text[pos], MI_XML_TAG_BOOKMARK_USER_POS) == 0) { + int pos2 = 0; + pos2 += sizeof(MI_XML_TAG_BOOKMARK_USER_POS); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') { + movie_info->bookmarks.user[bookmark_nr].pos = atoi(&text[pos + pos2 + 1]); + + //pos2 = strcspn(&text[pos],MI_XML_TAG_BOOKMARK_USER_TYPE); + pos++; + while (text[pos + pos2] == ' ') + pos++; + if (strcspn(&text[pos], MI_XML_TAG_BOOKMARK_USER_TYPE) == 0) { + pos2 += sizeof(MI_XML_TAG_BOOKMARK_USER_TYPE); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') { + movie_info->bookmarks.user[bookmark_nr].length = atoi(&text[pos + pos2 + 1]); + + movie_info->bookmarks.user[bookmark_nr].name = ""; + //pos2 = ; + if (strcspn(&text[pos], MI_XML_TAG_BOOKMARK_USER_NAME) == 0) { + pos2 += sizeof(MI_XML_TAG_BOOKMARK_USER_NAME); + while (text[pos + pos2] != '\"' && text[pos + pos2] != 0 && text[pos + pos2] != '/') + pos2++; + if (text[pos + pos2] == '\"') { + int pos3 = pos2 + 1; + while (text[pos + pos3] != '\"' && text[pos + pos3] != 0 && text[pos + pos3] != '/') + pos3++; + if (text[pos + pos3] == '\"') + movie_info->bookmarks.user[bookmark_nr].name.append(&text[pos + pos2 + 1], pos3 - pos2 - 1); + } + } + } + } else + movie_info->bookmarks.user[bookmark_nr].length = 0; + } + bookmark_nr++; + } else + movie_info->bookmarks.user[bookmark_nr].pos = 0; + } + } + } + + strReplace(movie_info->epgTitle, """, "\""); + strReplace(movie_info->epgInfo1, """, "\""); + strReplace(movie_info->epgTitle, "'", "'"); + strReplace(movie_info->epgInfo1, "'", "'"); + if (movie_info->epgInfo2 == "") { + movie_info->epgInfo2 = movie_info->epgInfo1; + //movie_info->epgInfo1 = ""; + } else { + strReplace(movie_info->epgInfo2, """, "\""); + strReplace(movie_info->epgInfo2, "'", "'"); + } + + return (true); +#endif + return (false); +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::addNewBookmark(MI_MOVIE_INFO * movie_info, MI_BOOKMARK & new_bookmark) +{ + TRACE("[mi] addNewBookmark\r\n"); + bool result = false; + if (movie_info != NULL) { + // search for free entry + bool loop = true; + for (int i = 0; i < MI_MOVIE_BOOK_USER_MAX && loop == true; i++) { + if (movie_info->bookmarks.user[i].pos == 0) { + // empty entry found + result = true; + loop = false; + movie_info->bookmarks.user[i].pos = new_bookmark.pos; + movie_info->bookmarks.user[i].length = new_bookmark.length; + //if(movie_info->bookmarks.user[i].name.empty()) + if (movie_info->bookmarks.user[i].name.size() == 0) { + if (new_bookmark.length == 0) + movie_info->bookmarks.user[i].name = g_Locale->getText(LOCALE_MOVIEBROWSER_BOOK_NEW); + if (new_bookmark.length < 0) + movie_info->bookmarks.user[i].name = g_Locale->getText(LOCALE_MOVIEBROWSER_BOOK_TYPE_BACKWARD); + if (new_bookmark.length > 0) + movie_info->bookmarks.user[i].name = g_Locale->getText(LOCALE_MOVIEBROWSER_BOOK_TYPE_FORWARD); + } else { + movie_info->bookmarks.user[i].name = new_bookmark.name; + } + } + } + } + return (result); +} + +/************************************************************************ + +************************************************************************/ +void CMovieInfo::clearMovieInfo(MI_MOVIE_INFO * movie_info) +{ + //TRACE("[mi]->clearMovieInfo \r\n"); + tm timePlay; + timePlay.tm_hour = 0; + timePlay.tm_min = 0; + timePlay.tm_sec = 0; + timePlay.tm_year = 100; + timePlay.tm_mday = 0; + timePlay.tm_mon = 1; + + movie_info->file.Name = ""; + movie_info->file.Size = 0; // Megabytes + movie_info->file.Time = mktime(&timePlay); + movie_info->dateOfLastPlay = mktime(&timePlay); // (date, month, year) + movie_info->dirItNr = 0; // + movie_info->genreMajor = 0; //genreMajor; + movie_info->genreMinor = 0; //genreMinor; + movie_info->length = 0; // (minutes) + movie_info->quality = 0; // (3 stars: classics, 2 stars: very good, 1 star: good, 0 stars: OK) + movie_info->productionDate = 0; // (Year) years since 1900 + movie_info->parentalLockAge = 0; // MI_PARENTAL_LOCKAGE (0,6,12,16,18) + movie_info->format = 0; // MI_VIDEO_FORMAT(16:9, 4:3) + movie_info->audio = 0; // MI_AUDIO (AC3, Deutsch, Englisch) + + movie_info->epgId = 0; + movie_info->epgEpgId = 0; + movie_info->epgMode = 0; + movie_info->epgVideoPid = 0; + movie_info->VideoType = 0; + movie_info->epgVTXPID = 0; + + movie_info->audioPids.clear(); + + movie_info->productionCountry = ""; + movie_info->epgTitle = ""; + movie_info->epgInfo1 = ""; //epgInfo1 + movie_info->epgInfo2 = ""; //epgInfo2 + movie_info->epgChannel = ""; + movie_info->serieName = ""; // (name e.g. 'StarWars) + movie_info->bookmarks.end = 0; + movie_info->bookmarks.start = 0; + movie_info->bookmarks.lastPlayStop = 0; + for (int i = 0; i < MI_MOVIE_BOOK_USER_MAX; i++) { + movie_info->bookmarks.user[i].pos = 0; + movie_info->bookmarks.user[i].length = 0; + movie_info->bookmarks.user[i].name = ""; + } +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::loadFile(CFile & file, char *buffer, int buffer_size) +{ + bool result = false; + if (strncmp(file.getFileName().c_str(), VLC_URI, strlen(VLC_URI)) == 0) { + result = loadFile_vlc(file, buffer, buffer_size); + } else { + result = loadFile_std(file, buffer, buffer_size); + } + return (result); +} + +bool CMovieInfo::loadFile_std(CFile & file, char *buffer, int buffer_size) +{ + bool result = true; + + int fd = open(file.Name.c_str(), O_RDONLY); + if (fd == -1) // cannot open file, return!!!!! + { + TRACE("[mi] loadXml: cannot open (%s)\r\n", file.getFileName().c_str()); + return false; + } + //TRACE( "show_ts_info: File found (%s)\r\n" ,filename->c_str()); + // read file content to buffer + int bytes = read(fd, buffer, buffer_size - 1); + if (bytes <= 0) // cannot read file into buffer, return!!!! + { + TRACE("[mi] loadXml: cannot read (%s)\r\n", file.getFileName().c_str()); + return false; + } + close(fd); + buffer[bytes] = 0; // terminate string + return (result); +} + +bool CMovieInfo::loadFile_vlc(CFile & file, char *buffer, int buffer_size) +{ + bool result = false; + return (result); +} + +/************************************************************************ + +************************************************************************/ +bool CMovieInfo::saveFile(const CFile & file, const char *text, const int text_size) +{ + bool result = false; + if (strncmp(file.getFileName().c_str(), VLC_URI, strlen(VLC_URI)) == 0) { + result = saveFile_vlc(file, text, text_size); + } else { + result = saveFile_std(file, text, text_size); + } + return (result); +} + +bool CMovieInfo::saveFile_std(const CFile & file, const char *text, const int text_size) +{ + bool result = false; + int fd; + if ((fd = open(file.Name.c_str(), O_SYNC | O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) { + int nr; + nr = write(fd, text, text_size); + //fdatasync(fd); + close(fd); + result = true; + //TRACE("[mi] saved (%d)\r\n",nr); + } else { + TRACE("[mi] ERROR: cannot open\r\n"); + } + return (result); +} + +bool CMovieInfo::saveFile_vlc(const CFile & file, const char *text, const int text_size) +{ + bool result = false; + return (result); +} + +/* char buf[2048]; + + int done; + do + { + unsigned int len=fread(buf, 1, sizeof(buf), in); + done=lenParse(buf, len, 1)) + { + TRACE("parse error: %s at line %d \r\n", parser->ErrorString(parser->GetErrorCode()), parser->GetCurrentLineNumber()); + fclose(in); + delete parser; + return (false); + } + } while (!done); + fclose(in); + * + * */ + +void CMovieInfo::copy(MI_MOVIE_INFO * src, MI_MOVIE_INFO * dst) +{ + //TRACE("[mi]->clearMovieInfo \r\n"); + + dst->file.Name = src->file.Name; + dst->file.Size = src->file.Size; + dst->file.Time = src->file.Time; + dst->dateOfLastPlay = src->dateOfLastPlay; + dst->dirItNr = src->dirItNr; + dst->genreMajor = src->genreMajor; + dst->genreMinor = src->genreMinor; + dst->length = src->length; + dst->quality = src->quality; + dst->productionDate = src->productionDate; + dst->parentalLockAge = src->parentalLockAge; + dst->format = src->format; + dst->audio = src->audio; + + dst->epgId = src->epgId; + dst->epgEpgId = src->epgEpgId; + dst->epgMode = src->epgMode; + dst->epgVideoPid = src->epgVideoPid; + dst->VideoType = src->VideoType; + dst->epgVTXPID = src->epgVTXPID; + + dst->productionCountry = src->productionCountry; + dst->epgTitle = src->epgTitle; + dst->epgInfo1 = src->epgInfo1; + dst->epgInfo2 = src->epgInfo2; + dst->epgChannel = src->epgChannel; + dst->serieName = src->serieName; + dst->bookmarks.end = src->bookmarks.end; + dst->bookmarks.start = src->bookmarks.start; + dst->bookmarks.lastPlayStop = src->bookmarks.lastPlayStop; + + for (int i = 0; i < MI_MOVIE_BOOK_USER_MAX; i++) { + dst->bookmarks.user[i].pos = src->bookmarks.user[i].pos; + dst->bookmarks.user[i].length = src->bookmarks.user[i].length; + dst->bookmarks.user[i].name = src->bookmarks.user[i].name; + } + + for (unsigned int i = 0; i < src->audioPids.size(); i++) { + EPG_AUDIO_PIDS audio_pids; + audio_pids.epgAudioPid = src->audioPids[i].epgAudioPid; + audio_pids.epgAudioPidName = src->audioPids[i].epgAudioPidName; + audio_pids.atype = src->audioPids[i].atype; + dst->audioPids.push_back(audio_pids); + } +} diff --git a/src/gui/movieinfo.h b/src/gui/movieinfo.h new file mode 100644 index 000000000..4fb907407 --- /dev/null +++ b/src/gui/movieinfo.h @@ -0,0 +1,204 @@ +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: movieinfo.h . + + Description: implementation of the CMovieInfo class + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + + Revision History: + Date Author Change Description + Nov 2005 Günther initial start + +****************************************************************************/ + +#ifndef MOVIEINFO_H_ +#define MOVIEINFO_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "driver/file.h" + +/************************************************************************/ +/************************************************************************/ +/***************** CMovieInfo ********************************/ +/************************************************************************/ + + +/* XML tags for xml file*/ +#define MI_XML_TAG_NEUTRINO "neutrino" +#define MI_XML_TAG_RECORD "record" + +#define MI_XML_TAG_CHANNELNAME "channelname" +#define MI_XML_TAG_EPGTITLE "epgtitle" +#define MI_XML_TAG_ID "id" +#define MI_XML_TAG_INFO1 "info1" +#define MI_XML_TAG_INFO2 "info2" +#define MI_XML_TAG_EPGID "epgid" +#define MI_XML_TAG_MODE "mode" +#define MI_XML_TAG_VIDEOPID "videopid" +#define MI_XML_TAG_VIDEOTYPE "videotype" +#define MI_XML_TAG_AUDIOPIDS "audiopids" +#define MI_XML_TAG_AUDIO "audio" +#define MI_XML_TAG_PID "pid" +#define MI_XML_TAG_NAME "name" +#define MI_XML_TAG_ATYPE "audiotype" +#define MI_XML_TAG_SELECTED "selected" +#define MI_XML_TAG_VTXTPID "vtxtpid" +#define MI_XML_TAG_GENRE_MAJOR "genremajor" +#define MI_XML_TAG_GENRE_MINOR "genreminor" +#define MI_XML_TAG_SERIE_NAME "seriename" +#define MI_XML_TAG_LENGTH "length" +#define MI_XML_TAG_PRODUCT_COUNTRY "productioncountry" +#define MI_XML_TAG_PRODUCT_DATE "productiondate" +#define MI_XML_TAG_QUALITY "qualitiy" +#define MI_XML_TAG_PARENTAL_LOCKAGE "parentallockage" +#define MI_XML_TAG_BOOKMARK "bookmark" +#define MI_XML_TAG_BOOKMARK_START "bookmarkstart" +#define MI_XML_TAG_BOOKMARK_END "bookmarkend" +#define MI_XML_TAG_BOOKMARK_LAST "bookmarklast" +#define MI_XML_TAG_BOOKMARK_USER "bookmarkuser" +#define MI_XML_TAG_BOOKMARK_USER_POS "bookmarkuserpos" +#define MI_XML_TAG_BOOKMARK_USER_TYPE "bookmarkusertype" +#define MI_XML_TAG_BOOKMARK_USER_NAME "bookmarkusername" +#define MI_XML_TAG_DATE_OF_LAST_PLAY "dateoflastplay" + + +#define MI_MAX_AUDIO_PIDS 4 // just to avoid the buffer is filled endless, might be increased later on , but 4 audio pids might be enough +#define MI_MOVIE_BOOK_USER_MAX 20 // just to avoid the buffer is filled endless, might be increased later on. Make sure to increase the bookmark menu as well + +typedef enum +{ + MI_PARENTAL_OVER0 = 0, + MI_PARENTAL_OVER6 = 6, + MI_PARENTAL_OVER12 = 12, + MI_PARENTAL_OVER16 = 16, + MI_PARENTAL_OVER18 = 18, + MI_PARENTAL_ALWAYS = 99, + MI_PARENTAL_MAX_NUMBER = 100 +}MI_PARENTAL_LOCKAGE; + +typedef struct +{ + int pos; // position in seconds from file start + int length; // bookmark type, 0: just a bookmark, < 0 jump back (seconds), > 0 jump forward (seconds) + std::string name; // bookmark name to be displayed +}MI_BOOKMARK; + +typedef struct +{ + int start; // movie start in seconds from file start + int end; // movie end in seconds from file start + int lastPlayStop; // position of last play stop in seconds from file start + MI_BOOKMARK user[MI_MOVIE_BOOK_USER_MAX]; // other user defined bookmarks +}MI_MOVIE_BOOKMARKS; + +typedef struct +{ + int atype; + int selected; + int epgAudioPid; // epg audio pid nr, usually filled by VCR + std::string epgAudioPidName; // epg audio pid name, usually filled by VCR +}EPG_AUDIO_PIDS; + +/************************************************************************/ +/************************************************************************/ + +typedef struct +{ + CFile file; // not stored in xml + std::string productionCountry; // user defined Country (not from EPG yet, but might be possible) + std::string epgTitle; // plain movie name, usually filled by EPG + std::string epgInfo1; // used for Genre (Premiere) or second title, usually filled by EPG + std::string epgInfo2; // detailed movie content, usually filled by EPG + std::string epgChannel; // Channel name, usually filled by EPG + std::string serieName; // user defines series name + + time_t dateOfLastPlay; // last play date of movie in seconds since 1970 + char dirItNr; // handle for quick directory path access only, this is not saved in xml, might be used by the owner of the movie info struct + int genreMajor; // see showEPG class for more info, usually filled by EPG + char genreMinor; // genreMinor not used so far + int length; // movie length in minutes, usually filled by EPG + int quality; // user classification (3 stars: classics, 2 stars: very good, 1 star: good, 0 stars: OK) + int productionDate; // user defined Country (not from EPG yet, but might be possible) + int parentalLockAge; // used for age rating(0:never,6,12,16,18 years,99:always), usually filled by EPG (if available) + char format; // currently not used + char audio; // currently not used + MI_MOVIE_BOOKMARKS bookmarks; // bookmark collecton for this movie + std::vector audioPids; // available AudioPids, usually filled by VCR. Note: Vectors are easy to is also using the heap (memory fragmentation), might be changed to array [MI_MAX_AUDIO_PIDS] + + uint64_t epgId; // currently not used, we just do not want to loose this info if movie info is saved backed + uint64_t epgEpgId; // off_t currently not used, we just do not want to loose this info if movie info is saved backed + int epgMode; // currently not used, we just do not want to loose this info if movie info is saved backed + int epgVideoPid; // currently not used, we just do not want to loose this info if movie info is saved backed + int VideoType; + int epgVTXPID; // currently not used, we just do not want to loose this info if movie info is saved backed +}MI_MOVIE_INFO; + + +class CMovieInfo +{ + public: // Functions + CMovieInfo(); + ~CMovieInfo(); + bool convertTs2XmlName(std::string* filename); // convert a ts file name in .xml file name + bool convertTs2XmlName(char* filename,int size); // convert a ts file name in .xml file name + bool loadMovieInfo(MI_MOVIE_INFO* movie_info, CFile* file = NULL ); // load movie information for the given .xml filename. If there is no filename, the filename (ts) from movie_info is converted to xml and used instead + bool encodeMovieInfoXml(std::string* extMessage, MI_MOVIE_INFO * movie_info); // encode the movie_info structure to xml string + bool saveMovieInfo(MI_MOVIE_INFO& movie_info, CFile* file = NULL ); // encode the movie_info structure to xml and save it to the given .xml filename. If there is no filename, the filename (ts) from movie_info is converted to xml and used instead + void showMovieInfo(MI_MOVIE_INFO& movie_info); // open a Hintbox and show the movie info + void printDebugMovieInfo(MI_MOVIE_INFO& movie_info); // print movie info on debug channel (RS232) + void clearMovieInfo(MI_MOVIE_INFO* movie_info); // Set movie info structure to initial values + bool addNewBookmark(MI_MOVIE_INFO* movie_info,MI_BOOKMARK &new_bookmark); // add a new bookmark to the given movie info. If there is no space false is returned + void copy(MI_MOVIE_INFO* src, MI_MOVIE_INFO* dst); + + private:// Functions + bool parseXmlTree (char* text, MI_MOVIE_INFO* movie_info); // this is the 'good' function, but it needs the xmllib which is not currently linked within neutrino. Might be to slow as well. If used, add bookmark parsing + bool parseXmlQuickFix(char* text, MI_MOVIE_INFO* movie_info); // OK, this is very quick an dirty. It does not waist execution time nor flash (this is QUICK). But, do not play to much with the xml files (e.g. with MS Notepad) since small changes in the structure could cause the parser to fail (this it DIRTY). + bool loadFile_std(CFile& file,char* buffer, int buffer_size); + bool loadFile_vlc(CFile& file,char* buffer, int buffer_size); + bool loadFile(CFile& file,char* buffer, int buffer_size); + bool saveFile_std(const CFile& file, const char* text, const int text_size); + bool saveFile_vlc(const CFile& file, const char* text, const int text_sizet); + bool saveFile(const CFile& file, const char* text, const int text_size); + //void CMovieInfo::strReplace(std::string& orig, const char* fstr, const std::string rstr); + private:// variables +}; + +#endif /*MOVIEINFO_H_*/ + diff --git a/src/gui/movieplayer.cpp b/src/gui/movieplayer.cpp new file mode 100644 index 000000000..52cf58d84 --- /dev/null +++ b/src/gui/movieplayer.cpp @@ -0,0 +1,1339 @@ +/* + Neutrino-GUI - DBoxII-Project + + Movieplayer (c) 2003, 2004 by gagga + Based on code by Dirch, obi and the Metzler Bros. Thanks. + + $Id: movieplayer.cpp,v 1.97 2004/07/18 00:54:52 thegoodguy Exp $ + + Homepage: http://www.giggo.de/dbox2/movieplayer.html + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +int dvbsub_start(int pid); +int dvbsub_pause(); + +extern cVideo * videoDecoder; +static cPlayback *playback; + +extern CRemoteControl *g_RemoteControl; /* neutrino.cpp */ +void strReplace(std::string & orig, const char *fstr, const std::string rstr); +#define MOVIE_HINT_BOX_TIMER 5 // time to show bookmark hints in seconds +extern CPlugins *g_PluginList; +extern CInfoClock *InfoClock; + +#define MINUTEOFFSET 117*262072 +#define MP_TS_SIZE 262072 // ~0.5 sec + +extern char rec_filename[512]; +extern off64_t glob_limit; +extern int glob_splits; + +static CMoviePlayerGui::state playstate; +static bool isBookmark; +static bool isMovieBrowser = false; +int speed = 1; +int slow = 0; +static off64_t fullposition; // cur. position including all parts played +//static off64_t fulllength = 0; // len of all parts +int startposition; +int timeshift; +off64_t minuteoffset; +off64_t secondoffset; + +int file_prozent; + +#ifndef __USE_FILE_OFFSET64 +#error not using 64 bit file offsets +#endif /* __USE_FILE__OFFSET64 */ + +int streamingrunning; +CHintBox *hintBox; +std::string startfilename; +std::string skipvalue; + +int jumpminutes = 1; +static int g_jumpseconds = 0; +int buffer_time = 0; +unsigned short g_apids[10]; +unsigned short g_ac3flags[10]; +unsigned short g_numpida = 0; +unsigned short g_vpid = 0; +unsigned short g_vtype = 0; +std::string g_language[10]; + +unsigned int g_currentapid = 0, g_currentac3 = 0, apidchanged = 0; +std::string g_file_epg; +std::string g_file_epg1; +bool showaudioselectdialog = false; + +void checkAspectRatio(int vdec, bool init); + +bool get_movie_info_apid_name(int apid, MI_MOVIE_INFO * movie_info, std::string * apidtitle) +{ + if (movie_info == NULL || apidtitle == NULL) + return false; + + for (int i = 0; i < (int)movie_info->audioPids.size(); i++) { + if (movie_info->audioPids[i].epgAudioPid == apid && !movie_info->audioPids[i].epgAudioPidName.empty()) { + *apidtitle = movie_info->audioPids[i].epgAudioPidName; + return true; + } + } + return false; +} + +int CAPIDSelectExec::exec(CMenuTarget * parent, const std::string & actionKey) +{ + apidchanged = 0; + unsigned int sel = atoi(actionKey.c_str()); + if (g_currentapid != g_apids[sel - 1]) { + g_currentapid = g_apids[sel - 1]; + g_currentac3 = g_ac3flags[sel - 1]; + apidchanged = 1; + printf("[movieplayer] apid changed to %d\n", g_apids[sel - 1]); + } + return menu_return::RETURN_EXIT; +} + +CMoviePlayerGui::CMoviePlayerGui() +{ + Init(); +} + +void CMoviePlayerGui::Init(void) +{ + stopped = false; + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_MOVIEPLAYER_PLEASEWAIT)); // UTF-8 + + frameBuffer = CFrameBuffer::getInstance(); + + if (strlen(g_settings.network_nfs_moviedir) != 0) + Path_local = g_settings.network_nfs_moviedir; + else + Path_local = "/"; + Path_vlc = "vlc://"; + Path_vlc += g_settings.streaming_server_startdir; + Path_vlc_settings = g_settings.streaming_server_startdir; + + if (g_settings.filebrowser_denydirectoryleave) + filebrowser = new CFileBrowser(Path_local.c_str()); + else + filebrowser = new CFileBrowser(); + + filebrowser->Multi_Select = false; + filebrowser->Dirs_Selectable = false; + filebrowser->Hide_records = true; + playback = new cPlayback(3); + moviebrowser = new CMovieBrowser(); + bookmarkmanager = 0; + + tsfilefilter.addFilter("ts"); + tsfilefilter.addFilter("avi"); + tsfilefilter.addFilter("mkv"); + tsfilefilter.addFilter("wav"); + tsfilefilter.addFilter("asf"); + tsfilefilter.addFilter("aiff"); + tsfilefilter.addFilter("mpg"); + tsfilefilter.addFilter("mpeg"); + tsfilefilter.addFilter("m2p"); + tsfilefilter.addFilter("mpv"); + tsfilefilter.addFilter("vob"); + tsfilefilter.addFilter("m2ts"); + + + vlcfilefilter.addFilter("mpg"); + vlcfilefilter.addFilter("mpeg"); + vlcfilefilter.addFilter("m2p"); + vlcfilefilter.addFilter("avi"); + vlcfilefilter.addFilter("vob"); + pesfilefilter.addFilter("mpv"); + filebrowser->Filter = &tsfilefilter; + rct = 0; +} + +CMoviePlayerGui::~CMoviePlayerGui() +{ +#if 0 + delete filebrowser; + if (moviebrowser) + delete moviebrowser; + delete hintBox; + if (bookmarkmanager) + delete bookmarkmanager; + g_Zapit->setStandby(false); + g_Sectionsd->setPauseScanning(false); +#endif + delete playback; +} + +void CMoviePlayerGui::cutNeutrino() +{ + if (stopped) + return; + + g_Zapit->setStandby(true); + g_Sectionsd->setPauseScanning(true); + + CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_ts); + m_LastMode = (CNeutrinoApp::getInstance()->getLastMode() | NeutrinoMessages::norezap); + + stopped = true; +} + +void CMoviePlayerGui::restoreNeutrino() +{ + if (!stopped) + return; + + g_Zapit->setStandby(false); + g_Sectionsd->setPauseScanning(false); + + CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, m_LastMode); + //CVFD::getInstance()->showServicename(g_RemoteControl->getCurrentChannelName()); + + stopped = false; +} + +int CMoviePlayerGui::exec(CMenuTarget * parent, const std::string & actionKey) +{ + printf("[movieplayer] actionKey=%s\n", actionKey.c_str()); + + if (Path_vlc_settings != g_settings.streaming_server_startdir) { + Path_vlc = "vlc://"; + Path_vlc += g_settings.streaming_server_startdir; + Path_vlc_settings = g_settings.streaming_server_startdir; + } + bookmarkmanager = new CBookmarkManager(); + + dvbsub_pause(); + + if (parent) { + parent->hide(); + } + + bool usedBackground = frameBuffer->getuseBackground(); + if (usedBackground) { + frameBuffer->saveBackgroundImage(); + frameBuffer->ClearFrameBuffer(); + } + + const CBookmark *theBookmark = NULL; + if (actionKey == "bookmarkplayback") { + isBookmark = true; + theBookmark = bookmarkmanager->getBookmark(NULL); + if (theBookmark == NULL) { + bookmarkmanager->flush(); + return menu_return::RETURN_REPAINT; + } + } + + isBookmark = false; + startfilename = ""; + startposition = 0; + + isMovieBrowser = false; + minuteoffset = MINUTEOFFSET; + secondoffset = minuteoffset / 60; +#if 0 + if (actionKey == "fileplayback") { + cutNeutrino(); + PlayStream(STREAMTYPE_FILE); + } else if (actionKey == "dvdplayback") { + cutNeutrino(); + PlayStream(STREAMTYPE_DVD); + } else if (actionKey == "vcdplayback") { + cutNeutrino(); + PlayStream(STREAMTYPE_SVCD); + } else if (actionKey == "tsplayback") { + timeshift = 0; + PlayFile(); + } else +#endif + if (actionKey == "tsmoviebrowser") { + isMovieBrowser = true;// TESTTTTTTTTTTTTTTT + timeshift = 0; + PlayFile(); + } + else if (actionKey == "fileplayback") { + isMovieBrowser = false; + timeshift = 0; + PlayFile(); + } + else if (actionKey == "timeshift") { + cutNeutrino(); + timeshift = 1; + PlayFile(); + } + else if (actionKey == "ptimeshift") { + cutNeutrino(); + timeshift = 2; + PlayFile(); + } + else if (actionKey == "rtimeshift") { + cutNeutrino(); + timeshift = 3; + PlayFile(); + } +#if 0 + else if (actionKey == "bookmarkplayback") { + isBookmark = true; + if (theBookmark != NULL) { + cutNeutrino(); + startfilename = theBookmark->getUrl(); + sscanf(theBookmark->getTime(), "%lld", &startposition); + int vlcpos = startfilename.rfind("vlc://"); + if (vlcpos == 0) { + PlayStream(STREAMTYPE_FILE); + } else { + timeshift = 0; + PlayFile(); + } + } + } +#endif + + bookmarkmanager->flush(); + // Restore previous background + if (usedBackground) { + frameBuffer->restoreBackgroundImage(); + frameBuffer->useBackground(true); + frameBuffer->paintBackground(); + } + + restoreNeutrino(); + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + //g_RCInput->postMsg(NeutrinoMessages::SHOW_INFOBAR, 0); + //dvbsub_start(0); + + if (bookmarkmanager) + delete bookmarkmanager; + if (timeshift) { + timeshift = 0; + return menu_return::RETURN_EXIT_ALL; + } + return menu_return::RETURN_REPAINT; + //return menu_return::RETURN_EXIT_ALL; +} + +void updateLcd(const std::string & sel_filename) +{ + char tmp[20]; + std::string lcd; + + switch (playstate) { + case CMoviePlayerGui::PAUSE: + lcd = "|| "; + lcd += sel_filename; + //lcd += ')'; + break; + case CMoviePlayerGui::REW: + sprintf(tmp, "%dx<< ", speed); + lcd = tmp; + lcd += sel_filename; + break; + case CMoviePlayerGui::FF: + sprintf(tmp, "%dx>> ", speed); + lcd = tmp; + lcd += sel_filename; + break; +#if 0 + case CMoviePlayerGui::JF: + sprintf(tmp, "%ds>> ", speed); + lcd = tmp; + lcd += sel_filename; + break; +#endif + default: + if (slow) { + sprintf(tmp, "%ds||> ", slow); + lcd = tmp; + } else + lcd = "> "; + lcd += sel_filename; + break; + } + + //CVFD::getInstance()->showServicename(lcd); + CVFD::getInstance()->showMenuText(0, lcd.c_str(), -1, true); +} + +extern bool has_hdd; +#define TIMESHIFT_SECONDS 3 +void CMoviePlayerGui::PlayFile(void) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + int position = 0, duration = 0; + bool timeshift_paused = false; + + std::string sel_filename; + CTimeOSD FileTime; + bool update_lcd = true, open_filebrowser = true, start_play = false, exit = false; + bool timesh = timeshift; + bool was_file = false; + bool time_forced = false; + playstate = CMoviePlayerGui::STOPPED; + bool is_file_player = false; + //unsigned short apid = 0, vpid = 0; + //int vtype = 0, atype = 0; + + if (has_hdd) + system("(rm /hdd/.wakeup; touch /hdd/.wakeup; sync) > /dev/null 2> /dev/null &"); + + timeb current_time; + CMovieInfo cMovieInfo; // funktions to save and load movie info + MI_MOVIE_INFO *p_movie_info = NULL; // movie info handle which comes from the MovieBrowser, if not NULL MoviePla yer is able to save new bookmarks + + int width = 280; + int height = 65; + int x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + //int y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + int y = frameBuffer->getScreenY() + frameBuffer->getScreenHeight() - height - 20; + + CBox boxposition(x, y, width, height); // window position for the hint boxes + + CTextBox endHintBox(g_Locale->getText(LOCALE_MOVIEBROWSER_HINT_MOVIEEND), NULL, CTextBox::CENTER /*CTextBox::AUTO_WIDTH | CTextBox::AUTO_HIGH */ , &boxposition); + CTextBox comHintBox(g_Locale->getText(LOCALE_MOVIEBROWSER_HINT_JUMPFORWARD), NULL, CTextBox::CENTER /*CTextBox::AUTO_WIDTH | CTextBox::AUTO_HIGH */ , &boxposition); + CTextBox loopHintBox(g_Locale->getText(LOCALE_MOVIEBROWSER_HINT_JUMPBACKWARD), NULL, CTextBox::CENTER /*CTextBox::AUTO_WIDTH | CTextBox::AUTO_HIGH */ , &boxposition); + CTextBox newLoopHintBox(g_Locale->getText(LOCALE_MOVIEBROWSER_HINT_NEWBOOK_BACKWARD), NULL, CTextBox::CENTER /*CTextBox::AUTO_WIDTH | CTextBox::AUTO_HIGH */ , &boxposition); + CTextBox newComHintBox(g_Locale->getText(LOCALE_MOVIEBROWSER_HINT_NEWBOOK_FORWARD), NULL, CTextBox::CENTER /*CTextBox::AUTO_WIDTH | CTextBox::AUTO_HIGH */ , &boxposition); + + bool showEndHintBox = false; // flag to check whether the box shall be painted + bool showComHintBox = false; // flag to check whether the box shall be painted + bool showLoopHintBox = false; // flag to check whether the box shall be painted + int jump_not_until = 0; // any jump shall be avoided until this time (in seconds from moviestart) + MI_BOOKMARK new_bookmark; // used for new movie info bookmarks created from the movieplayer + new_bookmark.pos = 0; // clear , since this is used as flag for bookmark activity + new_bookmark.length = 0; + + // very dirty usage of the menue, but it works and I already spent to much time with it, feel free to make it better ;-) +#define BOOKMARK_START_MENU_MAX_ITEMS 6 + CSelectedMenu cSelectedMenuBookStart[BOOKMARK_START_MENU_MAX_ITEMS]; + + CMenuWidget bookStartMenu(LOCALE_MOVIEBROWSER_BOOK_NEW, "streaming.raw"); + bookStartMenu.addItem(GenericMenuSeparator); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_HEAD, true, NULL, &cSelectedMenuBookStart[0])); + bookStartMenu.addItem(GenericMenuSeparatorLine); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_HEAD, true, NULL, &cSelectedMenuBookStart[1])); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_TYPE_FORWARD, true, NULL, &cSelectedMenuBookStart[2])); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_TYPE_BACKWARD, true, NULL, &cSelectedMenuBookStart[3])); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_MOVIESTART, true, NULL, &cSelectedMenuBookStart[4])); + bookStartMenu.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_BOOK_MOVIEEND, true, NULL, &cSelectedMenuBookStart[5])); + + rct = 0; + + go_repeat: + do { + + if (exit) { + exit = false; + printf("[movieplayer] stop\n"); + playstate = CMoviePlayerGui::STOPPED; + break; + } + + if (timesh) { + char fname[255]; + int cnt = 10 * 1000000; + while (!strlen(rec_filename)) { + usleep(1000); + cnt -= 1000; + if (!cnt) + break; + } + if (!strlen(rec_filename)) + return; + + sprintf(fname, "%s.ts", rec_filename); + filename = fname; + sel_filename = std::string(rindex(filename, '/') + 1); + printf("Timeshift: %s\n", sel_filename.c_str()); + + update_lcd = true; + start_play = true; + open_filebrowser = false; + isBookmark = false; + timesh = false; + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + FileTime.SetMode(CTimeOSD::MODE_DESC); + FileTime.show(position / 1000); + FileTime.updatePos(file_prozent); + } + + if (isBookmark) { + open_filebrowser = false; + filename = startfilename.c_str(); + sel_filename = startfilename; + update_lcd = true; + start_play = true; + isBookmark = false; + timeshift = false; + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + } + + if (isMovieBrowser == true) { + // do all moviebrowser stuff here ( like commercial jump ect.) + if (playstate == CMoviePlayerGui::PLAY) { + playback->GetPosition(position, duration); + int play_sec = position / 1000; // get current seconds from moviestart + //TRACE(" %6ds\r\n",play_sec); + + if (play_sec + 10 < jump_not_until || play_sec > jump_not_until + 10) + jump_not_until = 0; // check if !jump is stale (e.g. if user jumped forward or backward) + if (new_bookmark.pos == 0) // do bookmark activities only, if there is no new bookmark started + { + if (p_movie_info != NULL) // process bookmarks if we have any movie info + { + if (p_movie_info->bookmarks.end != 0) { + // *********** Check for stop position ******************************* + if (play_sec >= p_movie_info->bookmarks.end - MOVIE_HINT_BOX_TIMER && play_sec < p_movie_info->bookmarks.end && play_sec > jump_not_until) { + if (showEndHintBox == false) { + endHintBox.paint(); // we are 5 sec before the end postition, show warning + showEndHintBox = true; + TRACE("[mp] user stop in 5 sec...\r\n"); + } + } else { + if (showEndHintBox == true) { + endHintBox.hide(); // if we showed the warning before, hide the box again + showEndHintBox = false; + } + } + + if (play_sec >= p_movie_info->bookmarks.end && play_sec <= p_movie_info->bookmarks.end + 2 && play_sec > jump_not_until) // stop playing + { + // *********** we ARE close behind the stop position, stop playing ******************************* + TRACE("[mp] user stop\r\n"); + playstate = CMoviePlayerGui::STOPPED; + } + } + // ************* Check for bookmark jumps ******************************* + int loop = true; + showLoopHintBox = false; + showComHintBox = false; + for (int book_nr = 0; book_nr < MI_MOVIE_BOOK_USER_MAX && loop == true; book_nr++) { + if (p_movie_info->bookmarks.user[book_nr].pos != 0 && p_movie_info->bookmarks.user[book_nr].length != 0) { + // valid bookmark found, now check if we are close before or after it + if (play_sec >= p_movie_info->bookmarks.user[book_nr].pos - MOVIE_HINT_BOX_TIMER && play_sec < p_movie_info->bookmarks.user[book_nr].pos && play_sec > jump_not_until) { + if (p_movie_info->bookmarks.user[book_nr].length < 0) + showLoopHintBox = true; // we are 5 sec before , show warning + else if (p_movie_info->bookmarks.user[book_nr].length > 0) + showComHintBox = true; // we are 5 sec before, show warning + //else // TODO should we show a plain bookmark infomation as well? + } + + if (play_sec >= p_movie_info->bookmarks.user[book_nr].pos && play_sec <= p_movie_info->bookmarks.user[book_nr].pos + 2 && play_sec > jump_not_until) // + { + //for plain bookmark, the following calc shall result in 0 (no jump) + g_jumpseconds = p_movie_info->bookmarks.user[book_nr].length; + + // we are close behind the bookmark, do bookmark activity (if any) + if (p_movie_info->bookmarks.user[book_nr].length < 0) { + // if the jump back time is to less, it does sometimes cause problems (it does probably jump only 5 sec which will cause the next jump, and so on) + if (g_jumpseconds > -15) + g_jumpseconds = -15; + + g_jumpseconds = g_jumpseconds + p_movie_info->bookmarks.user[book_nr].pos; + //playstate = CMoviePlayerGui::JPOS; // bookmark is of type loop, jump backward + playback->SetPosition(g_jumpseconds * 1000); + } else if (p_movie_info->bookmarks.user[book_nr].length > 0) { + // jump at least 15 seconds + if (g_jumpseconds < 15) + g_jumpseconds = 15; + g_jumpseconds = g_jumpseconds + p_movie_info->bookmarks.user[book_nr].pos; + + //playstate = CMoviePlayerGui::JPOS; // bookmark is of type loop, jump backward + playback->SetPosition(g_jumpseconds * 1000); + } + TRACE("[mp] do jump %d sec\r\n", g_jumpseconds); + update_lcd = true; + loop = false; // do no further bookmark checks + } + } + } + // check if we shall show the commercial warning + if (showComHintBox == true) { + comHintBox.paint(); + TRACE("[mp] com jump in 5 sec...\r\n"); + } else + comHintBox.hide(); + + // check if we shall show the loop warning + if (showLoopHintBox == true) { + loopHintBox.paint(); + TRACE("[mp] loop jump in 5 sec...\r\n"); + } else + loopHintBox.hide(); + } + } + } + } // isMovieBrowser == true + + if (open_filebrowser) { + open_filebrowser = false; + timeshift = false; + FileTime.hide(); + /*clear audipopids */ + for (int i = 0; i < g_numpida; i++) { + g_apids[i] = 0; + g_ac3flags[i] = 0; + g_language[i].clear(); + } + g_numpida = 0; g_currentapid = 0; + + if (isMovieBrowser == true) { + // start the moviebrowser instead of the filebrowser + if (moviebrowser->exec(Path_local.c_str())) { + // get the current path and file name + Path_local = moviebrowser->getCurrentDir(); + CFile *file; + + if ((file = moviebrowser->getSelectedFile()) != NULL) { + CFile::FileType ftype; + ftype = file->getType(); + + if(ftype == CFile::FILE_AVI || ftype == CFile::FILE_MKV) { + is_file_player = true; // Movie player AVI/MKV + } else { + is_file_player = false; // Movie player AVI/MKV + } + filename = file->Name.c_str(); + sel_filename = file->getFileName(); + + // get the movie info handle (to be used for e.g. bookmark handling) + p_movie_info = moviebrowser->getCurrentMovieInfo(); + bool recfile = CNeutrinoApp::getInstance()->recordingstatus && !strncmp(rec_filename, filename, strlen(rec_filename)); + if (!recfile && p_movie_info->length) { + minuteoffset = file->Size / p_movie_info->length; + minuteoffset = (minuteoffset / MP_TS_SIZE) * MP_TS_SIZE; + if (minuteoffset < 5000000 || minuteoffset > 190000000) + minuteoffset = MINUTEOFFSET; + secondoffset = minuteoffset / 60; + } + + if(!p_movie_info->audioPids.empty()) { + g_currentapid = p_movie_info->audioPids[0].epgAudioPid; //FIXME + g_currentac3 = p_movie_info->audioPids[0].atype; + } + for (int i = 0; i < (int)p_movie_info->audioPids.size(); i++) { + g_apids[i] = p_movie_info->audioPids[i].epgAudioPid; + g_ac3flags[i] = p_movie_info->audioPids[i].atype; + g_numpida++; + if (p_movie_info->audioPids[i].selected) { + g_currentapid = p_movie_info->audioPids[i].epgAudioPid; //FIXME + g_currentac3 = p_movie_info->audioPids[i].atype; + //break; + } + } + g_vpid = p_movie_info->epgVideoPid; + g_vtype = p_movie_info->VideoType; + printf("CMoviePlayerGui::PlayFile: file %s apid %X atype %d vpid %x vtype %d\n", filename, g_currentapid, g_currentac3, g_vpid, g_vtype); + printf("Bytes per minute: %lld\n", minuteoffset); + // get the start position for the movie + startposition = 1000 * moviebrowser->getCurrentStartPos(); + //TRACE("[mp] start pos %llu, %d s Name: %s\r\n", startposition, moviebrowser->getCurrentStartPos(), filename); + + update_lcd = true; + start_play = true; + was_file = true; + } + } else if (playstate == CMoviePlayerGui::STOPPED) { + was_file = false; + break; + } + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + } else { + filebrowser->Filter = &tsfilefilter; + + if (filebrowser->exec(Path_local.c_str()) == true) { + Path_local = filebrowser->getCurrentDir(); + CFile *file; + if ((file = filebrowser->getSelectedFile()) != NULL) { + + CFile::FileType ftype; + ftype = file->getType(); + + is_file_player = true; +#if 0 + if(ftype == CFile::FILE_AVI || ftype == CFile::FILE_MKV || ftype == CFile::FILE_WAV || ftype == CFile::FILE_ASF) { + + is_file_player = true; // Movie player AVI/MKV + + } else { + is_file_player = false; // Movie player AVI/MKV + } +#endif + + filename = file->Name.c_str(); + update_lcd = true; + start_play = true; + was_file = true; + sel_filename = filebrowser->getSelectedFile()->getFileName(); + //playstate = CMoviePlayerGui::PLAY; + } + } else if (playstate == CMoviePlayerGui::STOPPED) { + was_file = false; + break; + } + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + } + } + + if (update_lcd) { + update_lcd = false; + if (isMovieBrowser && strlen(p_movie_info->epgTitle.c_str()) && strncmp(p_movie_info->epgTitle.c_str(), "not", 3)) + updateLcd(p_movie_info->epgTitle); + else + updateLcd(sel_filename); + } + + if (showaudioselectdialog) { + CMenuWidget APIDSelector(LOCALE_APIDSELECTOR_HEAD, "audio.raw", 300); + APIDSelector.addItem(GenericMenuSeparator); + CAPIDSelectExec *APIDChanger = new CAPIDSelectExec; + bool enabled; + bool defpid; + if(is_file_player && !g_numpida){ + playback->FindAllPids(g_apids, g_ac3flags, &g_numpida, g_language); + } + for (unsigned int count = 0; count < g_numpida; count++) { + bool name_ok; + char apidnumber[10]; + sprintf(apidnumber, "%d %X", count + 1, g_apids[count]); + enabled = true; + defpid = g_currentapid ? (g_currentapid == g_apids[count]) : (count == 0); + std::string apidtitle = "Stream "; + if(!is_file_player){ + name_ok = get_movie_info_apid_name(g_apids[count], p_movie_info, &apidtitle); + } + else if (!g_language[count].empty()){ + apidtitle = g_language[count]; + name_ok = true; + } + if (!name_ok) + apidtitle = "Stream "; + + switch(g_ac3flags[count]) + { + case 1: /*AC3,EAC3*/ + if (apidtitle.find("AC3") < 0 || is_file_player) + apidtitle.append(" (AC3)"); + break; + case 2: /*teletext*/ + apidtitle.append(" (Teletext)"); + enabled = false; + break; + case 3: /*MP2*/ + apidtitle.append(" (MP2)"); + break; + case 4: /*MP3*/ + apidtitle.append(" (MP3)"); + break; + case 5: /*AAC*/ + apidtitle.append(" (AAC)"); + break; + case 6: /*DTS*/ + apidtitle.append(" (DTS)"); + enabled = false; + break; + case 7: /*MLP*/ + apidtitle.append(" (MLP)"); + break; + default: + break; + } + if (!name_ok) + apidtitle.append(apidnumber); + + APIDSelector.addItem(new CMenuForwarderNonLocalized(apidtitle.c_str(), enabled, NULL, APIDChanger, apidnumber, CRCInput::convertDigitToKey(count + 1)), defpid); + } + + apidchanged = 0; + APIDSelector.exec(NULL, ""); + if (apidchanged) { + if (g_currentapid == 0) { + g_currentapid = g_apids[0]; + g_currentac3 = g_ac3flags[0]; + } + playback->SetAPid(g_currentapid, g_currentac3); + apidchanged = 0; + } + delete APIDChanger; + showaudioselectdialog = false; + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + update_lcd = true; + } + + if (FileTime.IsVisible() /*FIXME && playstate == CMoviePlayerGui::PLAY */ ) { + if (FileTime.GetMode() == CTimeOSD::MODE_ASC) { + FileTime.update(position / 1000); + } else { + FileTime.update((duration - position) / 1000); + } + FileTime.updatePos(file_prozent); + } + + if (start_play) { + printf("Startplay at %d seconds\n", startposition/1000); + start_play = false; + if (playstate >= CMoviePlayerGui::PLAY) { + playstate = CMoviePlayerGui::STOPPED; + playback->Close(); + } + + cutNeutrino(); + playback->Open(is_file_player ? PLAYMODE_FILE : PLAYMODE_TS); + + printf("IS FILE PLAYER: %s\n", is_file_player ? "true": "false" ); + + if(!playback->Start((char *)filename, g_vpid, g_vtype, g_currentapid, g_currentac3)) { + playback->Close(); + restoreNeutrino(); + } else { + playstate = CMoviePlayerGui::PLAY; + CVFD::getInstance()->ShowIcon(VFD_ICON_PLAY, true); + if(timeshift) { + timeshift_paused = true; + startposition = -1; + int i; + int towait = (timeshift == 1) ? TIMESHIFT_SECONDS+1 : TIMESHIFT_SECONDS; + for(i = 0; i < 500; i++) { + playback->GetPosition(position, duration); + startposition = (duration - position); + + //printf("CMoviePlayerGui::PlayFile: waiting for data, position %d duration %d (%d)\n", position, duration, towait); + if(startposition > towait*1000) + break; + //sleep(1); + usleep(20000); + } + if(timeshift == 3) { + startposition = duration; + } else { + if(g_settings.timeshift_pause) + playstate = CMoviePlayerGui::PAUSE; + if(timeshift == 1) + startposition = 0; + else + startposition = duration - TIMESHIFT_SECONDS*1000; + } + printf("******************* Timeshift %d, position %d, seek to %d seconds\n", timeshift, position, startposition/1000); + } + if(!is_file_player && startposition >= 0)//FIXME no jump for file at start yet + playback->SetPosition(startposition, true); + + if(timeshift == 3) { + playback->SetSpeed(-1); + } else if(!timeshift || !g_settings.timeshift_pause) { + playback->SetSpeed(1); + } + } + } + + g_RCInput->getMsg(&msg, &data, 10); // 1 secs.. + + if(timeshift && g_settings.timeshift_pause) { + if(!timeshift_paused && !videoDecoder->getBlank()) { + playback->SetSpeed(0); + timeshift_paused = true; + playstate = CMoviePlayerGui::PAUSE; + } + } + if ((playstate >= CMoviePlayerGui::PLAY) && (timeshift || (playstate != CMoviePlayerGui::PAUSE))) { + if(playback->GetPosition(position, duration)) { + if(duration > 100) + file_prozent = (unsigned char) (position / (duration / 100)); + playback->GetSpeed(speed); + printf("CMoviePlayerGui::PlayFile: speed %d position %d duration %d (%d, %d%%)\n", speed, position, duration, duration-position, file_prozent); + } + } + + if (msg == (neutrino_msg_t) g_settings.mpkey_plugin) { + //g_PluginList->start_plugin_by_name (g_settings.movieplayer_plugin.c_str (), pidt); + } else if (msg == (neutrino_msg_t) g_settings.mpkey_stop) { + //exit play + playstate = CMoviePlayerGui::STOPPED; + if (isMovieBrowser == true && p_movie_info != NULL) { + // if we have a movie information, try to save the stop position + ftime(¤t_time); + p_movie_info->dateOfLastPlay = current_time.time; + current_time.time = time(NULL); + p_movie_info->bookmarks.lastPlayStop = position / 1000; + + cMovieInfo.saveMovieInfo(*p_movie_info); + //p_movie_info->fileInfoStale(); //TODO: we might to tell the Moviebrowser that the movie info has changed, but this could cause long reload times when reentering the Moviebrowser + } + + if (!was_file) + exit = true; + } else if (msg == (neutrino_msg_t) g_settings.mpkey_play) { + if (playstate >= CMoviePlayerGui::PLAY) { + update_lcd = true; + playstate = CMoviePlayerGui::PLAY; + speed = 1; + playback->SetSpeed(speed); + } else if (!timeshift) { + open_filebrowser = true; + } + if (time_forced) { + time_forced = false; + if (g_settings.mode_clock) + InfoClock->StopClock(); + FileTime.hide(); + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_pause) { + update_lcd = true; + if (playstate == CMoviePlayerGui::PAUSE) { + playstate = CMoviePlayerGui::PLAY; + //CVFD::getInstance()->ShowIcon(VFD_ICON_PAUSE, false); + speed = 1; + playback->SetSpeed(speed); + } else { + playstate = CMoviePlayerGui::PAUSE; + //CVFD::getInstance()->ShowIcon(VFD_ICON_PAUSE, true); + speed = 0; + playback->SetSpeed(speed); + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_bookmark) { + // is there already a bookmark activity? + if (isMovieBrowser != true) { + if (bookmarkmanager->getBookmarkCount() < bookmarkmanager->getMaxBookmarkCount()) { + char timerstring[200]; + fprintf(stderr, "fileposition: %lld\n", fullposition); + sprintf(timerstring, "%lld", fullposition); + fprintf(stderr, "timerstring: %s\n", timerstring); + std::string bookmarktime = ""; + bookmarktime.append(timerstring); + fprintf(stderr, "bookmarktime: %s\n", bookmarktime.c_str()); + bookmarkmanager->createBookmark(filename, bookmarktime); + fprintf(stderr, "bookmark done\n"); + } else { + fprintf(stderr, "too many bookmarks\n"); + DisplayErrorMessage(g_Locale->getText(LOCALE_MOVIEPLAYER_TOOMANYBOOKMARKS)); // UTF-8 + } + } else { + int pos_sec = position / 1000; + if (newComHintBox.isPainted() == true) { + // yes, let's get the end pos of the jump forward + new_bookmark.length = pos_sec - new_bookmark.pos; + TRACE("[mp] commercial length: %d\r\n", new_bookmark.length); + if (cMovieInfo.addNewBookmark(p_movie_info, new_bookmark) == true) { + cMovieInfo.saveMovieInfo(*p_movie_info); /* save immediately in xml file */ + } + new_bookmark.pos = 0; // clear again, since this is used as flag for bookmark activity + newComHintBox.hide(); + } else if (newLoopHintBox.isPainted() == true) { + // yes, let's get the end pos of the jump backward + new_bookmark.length = new_bookmark.pos - pos_sec; + new_bookmark.pos = pos_sec; + TRACE("[mp] loop length: %d\r\n", new_bookmark.length); + if (cMovieInfo.addNewBookmark(p_movie_info, new_bookmark) == true) { + cMovieInfo.saveMovieInfo(*p_movie_info); /* save immediately in xml file */ + jump_not_until = pos_sec + 5; // avoid jumping for this time + } + new_bookmark.pos = 0; // clear again, since this is used as flag for bookmark activity + newLoopHintBox.hide(); + } else { + // no, nothing else to do, we open a new bookmark menu + new_bookmark.name = ""; // use default name + new_bookmark.pos = 0; + new_bookmark.length = 0; + + // next seems return menu_return::RETURN_EXIT, if something selected + bookStartMenu.exec(NULL, "none"); + if (cSelectedMenuBookStart[0].selected == true) { + /* Movieplayer bookmark */ + if (bookmarkmanager->getBookmarkCount() < bookmarkmanager->getMaxBookmarkCount()) { + char timerstring[200]; + fprintf(stderr, "fileposition: %lld\n", fullposition); + sprintf(timerstring, "%lld", fullposition); + fprintf(stderr, "timerstring: %s\n", timerstring); + std::string bookmarktime = ""; + bookmarktime.append(timerstring); + fprintf(stderr, "bookmarktime: %s\n", bookmarktime.c_str()); + bookmarkmanager->createBookmark(filename, bookmarktime); + } else { + fprintf(stderr, "too many bookmarks\n"); + DisplayErrorMessage(g_Locale->getText(LOCALE_MOVIEPLAYER_TOOMANYBOOKMARKS)); // UTF-8 + } + cSelectedMenuBookStart[0].selected = false; // clear for next bookmark menu + } else if (cSelectedMenuBookStart[1].selected == true) { + /* Moviebrowser plain bookmark */ + new_bookmark.pos = pos_sec; + new_bookmark.length = 0; + if (cMovieInfo.addNewBookmark(p_movie_info, new_bookmark) == true) + cMovieInfo.saveMovieInfo(*p_movie_info); /* save immediately in xml file */ + new_bookmark.pos = 0; // clear again, since this is used as flag for bookmark activity + cSelectedMenuBookStart[1].selected = false; // clear for next bookmark menu + } else if (cSelectedMenuBookStart[2].selected == true) { + /* Moviebrowser jump forward bookmark */ + new_bookmark.pos = pos_sec; + TRACE("[mp] new bookmark 1. pos: %d\r\n", new_bookmark.pos); + newComHintBox.paint(); + + cSelectedMenuBookStart[2].selected = false; // clear for next bookmark menu + } else if (cSelectedMenuBookStart[3].selected == true) { + /* Moviebrowser jump backward bookmark */ + new_bookmark.pos = pos_sec; + TRACE("[mp] new bookmark 1. pos: %d\r\n", new_bookmark.pos); + newLoopHintBox.paint(); + cSelectedMenuBookStart[3].selected = false; // clear for next bookmark menu + } else if (cSelectedMenuBookStart[4].selected == true) { + /* Moviebrowser movie start bookmark */ + p_movie_info->bookmarks.start = pos_sec; + TRACE("[mp] New movie start pos: %d\r\n", p_movie_info->bookmarks.start); + cMovieInfo.saveMovieInfo(*p_movie_info); /* save immediately in xml file */ + cSelectedMenuBookStart[4].selected = false; // clear for next bookmark menu + } else if (cSelectedMenuBookStart[5].selected == true) { + /* Moviebrowser movie end bookmark */ + p_movie_info->bookmarks.end = pos_sec; + TRACE("[mp] New movie end pos: %d\r\n", p_movie_info->bookmarks.start); + cMovieInfo.saveMovieInfo(*p_movie_info); /* save immediately in xml file */ + cSelectedMenuBookStart[5].selected = false; // clear for next bookmark menu + } + } + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_audio) { + showaudioselectdialog = true; + } else if (msg == CRCInput::RC_help) { + if (timeshift) + g_InfoViewer->showTitle(CNeutrinoApp::getInstance()->channelList->getActiveChannelNumber(), CNeutrinoApp::getInstance()->channelList->getActiveChannelName(), CNeutrinoApp::getInstance()->channelList->getActiveSatellitePosition(), CNeutrinoApp::getInstance()->channelList->getActiveChannel_ChannelID()); // UTF-8 + + else { + if (isMovieBrowser) { + g_file_epg = p_movie_info->epgTitle; + g_file_epg1 = p_movie_info->epgInfo1; + g_InfoViewer->showTitle(0, p_movie_info->epgChannel.c_str(), 0, 0); // UTF-8 + + } else { + char temp_name[255]; + char *slash = strrchr(filename, '/'); + if (slash) { + slash++; + int len = strlen(slash); + for (int i = 0; i < len; i++) { + if (slash[i] == '_') + temp_name[i] = ' '; + else + temp_name[i] = slash[i]; + } + temp_name[len] = 0; + } + g_file_epg = ""; + g_file_epg1 = ""; + g_InfoViewer->showTitle(0, temp_name, 0, 0); // UTF-8 + } + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + update_lcd = true; + //showHelpTS(); + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_time) { + if (FileTime.IsVisible()) { + if (FileTime.GetMode() == CTimeOSD::MODE_ASC) { + FileTime.SetMode(CTimeOSD::MODE_DESC); + FileTime.update((duration - position) / 1000); + FileTime.updatePos(file_prozent); + } else { + if (g_settings.mode_clock) + InfoClock->StopClock(); + FileTime.hide(); + } + } else { + if (g_settings.mode_clock) + InfoClock->StartClock(); + FileTime.SetMode(CTimeOSD::MODE_ASC); + FileTime.show(position / 1000); + FileTime.updatePos(file_prozent); + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_rewind) { // rewind + if (speed >= 0) + speed = -1; + else + speed --; + + playback->SetSpeed(speed); + playstate = CMoviePlayerGui::REW; + update_lcd = true; + if (!FileTime.IsVisible()) { + if (g_settings.mode_clock) + InfoClock->StartClock(); + FileTime.SetMode(CTimeOSD::MODE_ASC); + FileTime.show(position / 1000); + FileTime.updatePos(file_prozent); + time_forced = true; + } + } else if (msg == (neutrino_msg_t) g_settings.mpkey_forward) { // fast-forward + if (speed <= 0) + speed = 2; + else + speed ++; + + playback->SetSpeed(speed); + + update_lcd = true; + playstate = CMoviePlayerGui::FF; + + if (!FileTime.IsVisible()) { + if (g_settings.mode_clock) + InfoClock->StartClock(); + FileTime.SetMode(CTimeOSD::MODE_ASC); + FileTime.show(position / 1000); + FileTime.updatePos(file_prozent); + time_forced = true; + } + } else if (msg == CRCInput::RC_1) { // Jump Backwards 1 minute + //update_lcd = true; + playback->SetPosition(-60 * 1000); + } else if (msg == CRCInput::RC_3) { // Jump Forward 1 minute + //update_lcd = true; + playback->SetPosition(60 * 1000); + } else if (msg == CRCInput::RC_4) { // Jump Backwards 5 minutes + playback->SetPosition(-5 * 60 * 1000); + } else if (msg == CRCInput::RC_6) { // Jump Forward 5 minutes + playback->SetPosition(5 * 60 * 1000); + } else if (msg == CRCInput::RC_7) { // Jump Backwards 10 minutes + playback->SetPosition(-10 * 60 * 1000); + } else if (msg == CRCInput::RC_9) { // Jump Forward 10 minutes + playback->SetPosition(10 * 60 * 1000); + } else if (msg == CRCInput::RC_2) { // goto start + playback->SetPosition(0, true); + } else if (msg == CRCInput::RC_5) { // goto middle + playback->SetPosition(duration/2, true); + } else if (msg == CRCInput::RC_8) { // goto end + playback->SetPosition(duration - 60 * 1000, true); + } else if (msg == CRCInput::RC_page_up) { + playback->SetPosition(10 * 1000); + } else if (msg == CRCInput::RC_page_down) { + playback->SetPosition(-10 * 1000); + } else if (msg == CRCInput::RC_0) { // cancel bookmark jump + if (isMovieBrowser == true) { + if (new_bookmark.pos != 0) { + new_bookmark.pos = 0; // stop current bookmark activity, TODO: might bemoved to another key + newLoopHintBox.hide(); // hide hint box if any + newComHintBox.hide(); + } + jump_not_until = (position / 1000) + 10; // avoid bookmark jumping for the next 10 seconds, , TODO: might be moved to another key + } else if (playstate != CMoviePlayerGui::PAUSE) + playstate = CMoviePlayerGui::SOFTRESET; + } else if (msg == CRCInput::RC_up || msg == CRCInput::RC_down) { + if (msg == CRCInput::RC_up) { + if (slow == 2) + slow = 0; + if (slow > 0) + slow--; + } else if (msg == CRCInput::RC_down) { + if (slow == 0) + slow++; + slow++; + } + //set_slow (slow); + update_lcd = true; + } else if (msg == CRCInput::RC_radio) { + if (isMovieBrowser == true && p_movie_info != NULL) { + std::string fname = p_movie_info->file.Name; + strReplace(fname, ".ts", ".bmp"); + CVCRControl::getInstance()->Screenshot(0, (char *)fname.c_str()); + } + } +#if 0 + else if (msg == CRCInput::RC_shift_radio) { + if (isMovieBrowser == true && p_movie_info != NULL) { + time_t t = time(NULL); + char filename[512]; + sprintf(filename, "%s", p_movie_info->file.Name.c_str()); + int pos = strlen(filename); + strftime(&(filename[pos - 3]), sizeof(filename) - pos - 1, "%Y%m%d_%H%M%S", localtime(&t)); + strcat(filename, ".bmp"); + CVCRControl::getInstance()->Screenshot(0, filename); + } + } +#endif + else if (msg == CRCInput::RC_timeout) { + // nothing + } else if ((msg == NeutrinoMessages::ANNOUNCE_RECORD) || msg == NeutrinoMessages::RECORD_START || msg == NeutrinoMessages::ZAPTO || msg == NeutrinoMessages::STANDBY_ON || msg == NeutrinoMessages::SHUTDOWN || msg == NeutrinoMessages::SLEEPTIMER) { // Exit for Record/Zapto Timers + exit = true; + g_RCInput->postMsg(msg, data); + } else { + if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + exit = true; + else if ( msg <= CRCInput::RC_MaxRC ) { + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + update_lcd = true; + } + } + + if (exit) { + if (isMovieBrowser == true && p_movie_info != NULL) { + // if we have a movie information, try to save the stop position + ftime(¤t_time); + p_movie_info->dateOfLastPlay = current_time.time; + current_time.time = time(NULL); + p_movie_info->bookmarks.lastPlayStop = position / 1000; + + cMovieInfo.saveMovieInfo(*p_movie_info); + //p_movie_info->fileInfoStale(); //TODO: we might to tell the Moviebrowser that the movie info has changed, but this could cause long reload times when reentering the Moviebrowser + } + } + } while (playstate >= CMoviePlayerGui::PLAY); + + FileTime.hide(); + + playback->SetSpeed(1); + playback->Close(); + + CVFD::getInstance()->ShowIcon(VFD_ICON_PLAY, false); + CVFD::getInstance()->ShowIcon(VFD_ICON_PAUSE, false); + + if (was_file) { + restoreNeutrino(); + open_filebrowser = true; + start_play = true; + goto go_repeat; + } + + if (g_settings.mode_clock) + InfoClock->StartClock(); +} + +// checks if AR has changed an sets cropping mode accordingly (only video mode auto) +static short archeck; +void checkAspectRatio(int vdec, bool init) +{ + static time_t last_check = 0; + + // only necessary for auto mode, check each 5 sec. max + if (g_settings.video_Format != 0 || (!init && time(NULL) <= last_check + archeck)) + return; +#if 0 + char aspectRatio = 2; + if (init) { + aspectRatio = g_Controld->getAspectRatio(); + last_check = 0; + archeck = 1; + return; + } else { + char newRatio = g_Controld->getAspectRatio(); + if (newRatio != aspectRatio) { + printf("[movieplayer] AR change detected in auto mode, adjusting display format\n"); + video_displayformat_t vdt; + if (newRatio == 2) + vdt = VIDEO_LETTER_BOX; + else + vdt = VIDEO_CENTER_CUT_OUT; + if (ioctl(vdec, VIDEO_SET_DISPLAY_FORMAT, vdt)) + perror("[movieplayer] VIDEO_SET_DISPLAY_FORMAT"); + aspectRatio = newRatio; + archeck = 5; + } + last_check = time(NULL); + } +#endif +} + +void CMoviePlayerGui::showHelpTS() +{ + Helpbox helpbox; + helpbox.addLine(NEUTRINO_ICON_BUTTON_RED, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP1)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_GREEN, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP2)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_YELLOW, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP3)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_BLUE, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP4)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_DBOX, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP5)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_1, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP6)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_3, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP7)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_4, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP8)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_6, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP9)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_7, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP10)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_9, g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP11)); + helpbox.addLine(g_Locale->getText(LOCALE_MOVIEPLAYER_TSHELP12)); + helpbox.addLine("Version: $Revision: 1.97 $"); + helpbox.addLine("Movieplayer (c) 2003, 2004 by gagga"); + hide(); + helpbox.show(LOCALE_MESSAGEBOX_INFO); +} + +void CMoviePlayerGui::showHelpVLC() +{ + Helpbox helpbox; + helpbox.addLine(NEUTRINO_ICON_BUTTON_RED, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP1)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_GREEN, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP2)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_YELLOW, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP3)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_BLUE, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP4)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_DBOX, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP5)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_1, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP6)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_3, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP7)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_4, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP8)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_6, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP9)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_7, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP10)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_9, g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP11)); + helpbox.addLine(g_Locale->getText(LOCALE_MOVIEPLAYER_VLCHELP12)); + helpbox.addLine("Version: $Revision: 1.97 $"); + helpbox.addLine("Movieplayer (c) 2003, 2004 by gagga"); + hide(); + helpbox.show(LOCALE_MESSAGEBOX_INFO); +} diff --git a/src/gui/movieplayer.h b/src/gui/movieplayer.h new file mode 100644 index 000000000..21e65c3cd --- /dev/null +++ b/src/gui/movieplayer.h @@ -0,0 +1,117 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2003,2004 gagga + Homepage: http://www.giggo.de/dbox + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __movieplayergui__ +#define __movieplayergui__ + +#include +#include +#if HAVE_DVB_API_VERSION >= 1 +#include "driver/framebuffer.h" +#include "gui/filebrowser.h" +#include "gui/bookmarkmanager.h" +#include "gui/widget/menue.h" +#include "gui/moviebrowser.h" +#include "gui/movieinfo.h" + +extern "C" { + #include +} +#include + +#include +#include + +class CMoviePlayerGui : public CMenuTarget +{ + public: + enum state + { + STOPPED = 0, + PREPARING = 1, + STREAMERROR = 2, + PLAY = 3, + PAUSE = 4, + FF = 5, + REW = 6, + RESYNC = 7, + JPOS = 8, // jump to absolute position + JF = 9, + JB = 10, + SKIP = 11, + AUDIOSELECT = 12, + SOFTRESET = 99 + }; + + private: + void Init(void); + pthread_t rct; + CFrameBuffer * frameBuffer; + int m_LastMode; + const char *filename; + bool stopped; + + std::string Path_local; + std::string Path_vlc; + std::string Path_vlc_settings; + + CFileBrowser * filebrowser; + CMovieBrowser* moviebrowser; + + CBookmarkManager * bookmarkmanager; + + void PlayStream(int streamtype); + void PlayFile(); + void cutNeutrino(); + void restoreNeutrino(); + + CFileFilter tsfilefilter; + CFileFilter pesfilefilter; + CFileFilter vlcfilefilter; + void showHelpTS(void); + void showHelpVLC(void); + + public: + CMoviePlayerGui(); + ~CMoviePlayerGui(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +class CAPIDSelectExec : public CMenuTarget +{ + public: + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + +#endif + +#endif diff --git a/src/gui/nfs.cpp b/src/gui/nfs.cpp new file mode 100644 index 000000000..aa926700d --- /dev/null +++ b/src/gui/nfs.cpp @@ -0,0 +1,406 @@ +/* + Neutrino-GUI - DBoxII-Project + + NFSMount/Umount GUI by Zwen + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +class CNFSMountGuiNotifier : public CChangeObserver +{ +private: + CMenuForwarder *m_opt1,*m_opt2, *m_user, *m_pass; + int *m_type; +public: + CNFSMountGuiNotifier( CMenuForwarder* a3, CMenuForwarder* a4 , int* type) + { + m_user = a3; + m_pass = a4; + m_type = type; + } + bool changeNotify(const neutrino_locale_t OptionName, void *) + { + if(*m_type == (int)CFSMounter::NFS) + { + m_user->setActive (false); + m_pass->setActive (false); + } + else + { + m_user->setActive (true); + m_pass->setActive (true); + } + return true; + } +}; + +CNFSMountGui::CNFSMountGui() +{ +#warning move probing from exec() to fsmounter + m_nfs_sup = CFSMounter::FS_UNPROBED; + m_cifs_sup = CFSMounter::FS_UNPROBED; + m_lufs_sup = CFSMounter::FS_UNPROBED; + +} + + +const char * nfs_entry_printf_string[3] = +{ + "NFS %s:%s -> %s auto: %4s", + "CIFS //%s/%s -> %s auto: %4s", + "FTPFS %s/%s -> %s auto: %4s" +}; + + +int CNFSMountGui::exec( CMenuTarget* parent, const std::string & actionKey ) +{ + //printf("exec: %s\n", actionKey.c_str()); + int returnval = menu_return::RETURN_REPAINT; + + if (m_nfs_sup == CFSMounter::FS_UNPROBED) + m_nfs_sup = CFSMounter::fsSupported(CFSMounter::NFS); + + if (m_cifs_sup == CFSMounter::FS_UNPROBED) + m_cifs_sup = CFSMounter::fsSupported(CFSMounter::CIFS); + + if (m_lufs_sup == CFSMounter::FS_UNPROBED) + m_lufs_sup = CFSMounter::fsSupported(CFSMounter::LUFS); + + printf("SUPPORT: NFS: %d, CIFS: %d, LUFS: %d\n", m_nfs_sup, m_cifs_sup, m_lufs_sup); + + if (actionKey.empty()) + { + parent->hide(); + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES; i++) + { + sprintf(m_entry[i], + nfs_entry_printf_string[(g_settings.network_nfs_type[i] == (int) CFSMounter::NFS) ? 0 : ((g_settings.network_nfs_type[i] == (int) CFSMounter::CIFS) ? 1 : 2)], + g_settings.network_nfs_ip[i].c_str(), + FILESYSTEM_ENCODING_TO_UTF8(g_settings.network_nfs_dir[i]), + FILESYSTEM_ENCODING_TO_UTF8(g_settings.network_nfs_local_dir[i]), + g_Locale->getText(g_settings.network_nfs_automount[i] ? LOCALE_MESSAGEBOX_YES : LOCALE_MESSAGEBOX_NO)); + } + returnval = menu(); + } + else if(actionKey.substr(0,10)=="mountentry") + { + parent->hide(); + returnval = menuEntry(actionKey[10]-'0'); + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES; i++) + { + sprintf(m_entry[i], + nfs_entry_printf_string[(g_settings.network_nfs_type[i] == (int) CFSMounter::NFS) ? 0 : ((g_settings.network_nfs_type[i] == (int) CFSMounter::CIFS) ? 1 : 2)], + g_settings.network_nfs_ip[i].c_str(), + FILESYSTEM_ENCODING_TO_UTF8(g_settings.network_nfs_dir[i]), + FILESYSTEM_ENCODING_TO_UTF8(g_settings.network_nfs_local_dir[i]), + g_Locale->getText(g_settings.network_nfs_automount[i] ? LOCALE_MESSAGEBOX_YES : LOCALE_MESSAGEBOX_NO)); + sprintf(ISO_8859_1_entry[i],ZapitTools::UTF8_to_Latin1(m_entry[i]).c_str()); + } + } + else if(actionKey.substr(0,7)=="domount") + { + int nr=atoi(actionKey.substr(7,1).c_str()); + CFSMounter::mount(g_settings.network_nfs_ip[nr].c_str(), g_settings.network_nfs_dir[nr], + g_settings.network_nfs_local_dir[nr], (CFSMounter::FSType) g_settings.network_nfs_type[nr], + g_settings.network_nfs_username[nr], g_settings.network_nfs_password[nr], + g_settings.network_nfs_mount_options1[nr], g_settings.network_nfs_mount_options2[nr]); + // TODO show msg in case of error + returnval = menu_return::RETURN_EXIT; + } + else if(actionKey.substr(0,3)=="dir") + { + parent->hide(); + int nr=atoi(actionKey.substr(3,1).c_str()); + CFileBrowser b; + b.Dir_Mode=true; + + if (b.exec(g_settings.network_nfs_local_dir[nr])) + strcpy(g_settings.network_nfs_local_dir[nr], b.getSelectedFile()->Name.c_str()); + + returnval = menu_return::RETURN_REPAINT; + } + return returnval; +} + +int CNFSMountGui::menu() +{ + CMenuWidget mountMenuW(LOCALE_NFS_MOUNT, "network.raw", 720); + mountMenuW.addItem(GenericMenuSeparator); + mountMenuW.addItem(GenericMenuBack); + mountMenuW.addItem(GenericMenuSeparatorLine); + char s2[12]; + + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) + { + sprintf(s2,"mountentry%d",i); + sprintf(ISO_8859_1_entry[i],ZapitTools::UTF8_to_Latin1(m_entry[i]).c_str()); + CMenuForwarderNonLocalized *forwarder = new CMenuForwarderNonLocalized("", true, ISO_8859_1_entry[i], this, s2); + if (CFSMounter::isMounted(g_settings.network_nfs_local_dir[i])) + { + forwarder->iconName = NEUTRINO_ICON_MOUNTED; + } else + { + forwarder->iconName = NEUTRINO_ICON_NOT_MOUNTED; + } + mountMenuW.addItem(forwarder); + } + int ret=mountMenuW.exec(this,""); + return ret; +} + +#warning MESSAGEBOX_NO_YES_XXX is defined in neutrino.cpp, too! +#define MESSAGEBOX_NO_YES_OPTION_COUNT 2 +const CMenuOptionChooser::keyval MESSAGEBOX_NO_YES_OPTIONS[MESSAGEBOX_NO_YES_OPTION_COUNT] = +{ + { 0, LOCALE_MESSAGEBOX_NO }, + { 1, LOCALE_MESSAGEBOX_YES } +}; + +#define NFS_TYPE_OPTION_COUNT 3 +const CMenuOptionChooser::keyval NFS_TYPE_OPTIONS[NFS_TYPE_OPTION_COUNT] = +{ + { CFSMounter::NFS , LOCALE_NFS_TYPE_NFS }, + { CFSMounter::CIFS, LOCALE_NFS_TYPE_CIFS } /*, + { CFSMounter::LUFS, LOCALE_NFS_TYPE_LUFS } */ +}; + +int CNFSMountGui::menuEntry(int nr) +{ + char *dir,*local_dir, *username, *password, *options1, *options2, *mac; + int* automount; + int* type; + char cmd[9]; + char cmd2[9]; + + dir = g_settings.network_nfs_dir[nr]; + local_dir = g_settings.network_nfs_local_dir[nr]; + username = g_settings.network_nfs_username[nr]; + password = g_settings.network_nfs_password[nr]; + automount = &g_settings.network_nfs_automount[nr]; + type = &g_settings.network_nfs_type[nr]; + options1 = g_settings.network_nfs_mount_options1[nr]; + options2 = g_settings.network_nfs_mount_options2[nr]; + mac = g_settings.network_nfs_mac[nr]; + + sprintf(cmd,"domount%d",nr); + sprintf(cmd2,"dir%d",nr); + + /* rewrite fstype in new entries */ + if(strlen(local_dir)==0) + { + if(m_cifs_sup != CFSMounter::FS_UNSUPPORTED && m_nfs_sup == CFSMounter::FS_UNSUPPORTED && m_lufs_sup == CFSMounter::FS_UNSUPPORTED) + *type = (int) CFSMounter::CIFS; + + else if(m_lufs_sup != CFSMounter::FS_UNSUPPORTED && m_cifs_sup == CFSMounter::FS_UNSUPPORTED && m_nfs_sup == CFSMounter::FS_UNSUPPORTED) + *type = (int) CFSMounter::LUFS; + } + bool typeEnabled = (m_cifs_sup != CFSMounter::FS_UNSUPPORTED && m_nfs_sup != CFSMounter::FS_UNSUPPORTED && m_lufs_sup != CFSMounter::FS_UNSUPPORTED) || + (m_cifs_sup != CFSMounter::FS_UNSUPPORTED && *type != (int)CFSMounter::CIFS) || + (m_nfs_sup != CFSMounter::FS_UNSUPPORTED && *type != (int)CFSMounter::NFS) || + (m_lufs_sup != CFSMounter::FS_UNSUPPORTED && *type != (int)CFSMounter::LUFS); + + CMenuWidget mountMenuEntryW(LOCALE_NFS_MOUNT, "network.raw",720); + mountMenuEntryW.addItem(GenericMenuSeparator); + mountMenuEntryW.addItem(GenericMenuBack); + mountMenuEntryW.addItem(GenericMenuSeparatorLine); + CIPInput ipInput(LOCALE_NFS_IP, g_settings.network_nfs_ip[nr], LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CStringInputSMS dirInput(LOCALE_NFS_DIR, dir, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE,"abcdefghijklmnopqrstuvwxyz0123456789-_.,:|!?/ "); + CMenuOptionChooser *automountInput= new CMenuOptionChooser(LOCALE_NFS_AUTOMOUNT, automount, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true); + CStringInputSMS options1Input(LOCALE_NFS_MOUNT_OPTIONS, options1, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-_=.,:|!?/ "); + CMenuForwarder *options1_fwd = new CMenuForwarder(LOCALE_NFS_MOUNT_OPTIONS, true, options1, &options1Input); + CStringInputSMS options2Input(LOCALE_NFS_MOUNT_OPTIONS, options2, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-_=.,:|!?/ "); + CMenuForwarder *options2_fwd = new CMenuForwarder(LOCALE_NFS_MOUNT_OPTIONS, true, options2, &options2Input); + CStringInputSMS userInput(LOCALE_NFS_USERNAME, username, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-_.,:|!?/ "); + CMenuForwarder *username_fwd = new CMenuForwarder(LOCALE_NFS_USERNAME, (*type==CFSMounter::CIFS || CFSMounter::LUFS), username, &userInput); + CStringInputSMS passInput(LOCALE_NFS_PASSWORD, password, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-_.,:|!?/ "); + CMenuForwarder *password_fwd = new CMenuForwarder(LOCALE_NFS_PASSWORD, (*type==CFSMounter::CIFS || CFSMounter::LUFS), NULL, &passInput); + CMACInput * macInput = new CMACInput(LOCALE_RECORDINGMENU_SERVER_MAC, g_settings.network_nfs_mac[nr], LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CMenuForwarder * macInput_fwd = new CMenuForwarder(LOCALE_RECORDINGMENU_SERVER_MAC, true, g_settings.network_nfs_mac[nr], macInput); + + CNFSMountGuiNotifier notifier(username_fwd, password_fwd, type); + + mountMenuEntryW.addItem(new CMenuOptionChooser(LOCALE_NFS_TYPE, type, NFS_TYPE_OPTIONS, NFS_TYPE_OPTION_COUNT, typeEnabled, ¬ifier)); + mountMenuEntryW.addItem(new CMenuForwarder(LOCALE_NFS_IP , true, g_settings.network_nfs_ip[nr], &ipInput )); + mountMenuEntryW.addItem(new CMenuForwarder(LOCALE_NFS_DIR , true, dir , &dirInput )); + mountMenuEntryW.addItem(new CMenuForwarder(LOCALE_NFS_LOCALDIR, true, local_dir , this , cmd2)); + mountMenuEntryW.addItem(automountInput); + mountMenuEntryW.addItem(options1_fwd); + mountMenuEntryW.addItem(options2_fwd); + mountMenuEntryW.addItem(username_fwd); + mountMenuEntryW.addItem(password_fwd); + mountMenuEntryW.addItem(macInput_fwd); + mountMenuEntryW.addItem(new CMenuForwarder(LOCALE_NFS_MOUNTNOW, true, NULL , this , cmd )); + + int ret = mountMenuEntryW.exec(this,""); + return ret; +} + +int CNFSUmountGui::exec( CMenuTarget* parent, const std::string & actionKey ) +{ + // printf("ac: %s\n", actionKey.c_str()); + int returnval; + + if (actionKey.empty()) + { + parent->hide(); + returnval = menu(); + } + else if(actionKey.substr(0,8)=="doumount") + { + CFSMounter::umount((actionKey.substr(9)).c_str()); + returnval = menu_return::RETURN_EXIT; + } + else + returnval = menu_return::RETURN_REPAINT; + + return returnval; +} +int CNFSUmountGui::menu() +{ + int count = 0; + CFSMounter::MountInfos infos; + CMenuWidget umountMenu(LOCALE_NFS_UMOUNT, "network.raw",720); + umountMenu.addItem(GenericMenuSeparator); + umountMenu.addItem(GenericMenuBack); + umountMenu.addItem(GenericMenuSeparatorLine); + CFSMounter::getMountedFS(infos); + for (CFSMounter::MountInfos::const_iterator it = infos.begin(); + it != infos.end();it++) + { + if(it->type == "nfs" || it->type == "cifs" || it->type == "lufs") + { + count++; + std::string s1 = it->device; + s1 += " -> "; + s1 += it->mountPoint; + std::string s2 = "doumount "; + s2 += it->mountPoint; + CMenuForwarder *forwarder = new CMenuForwarderNonLocalized(s1.c_str(), true, NULL, this, s2.c_str()); + forwarder->iconName = NEUTRINO_ICON_MOUNTED; + umountMenu.addItem(forwarder); + } + } + if(infos.size() > 0) + return umountMenu.exec(this,""); + else + return menu_return::RETURN_REPAINT; +} + + + +int CNFSSmallMenu::exec( CMenuTarget* parent, const std::string & actionKey ) +{ + if (actionKey.empty()) + { + CMenuWidget menu(LOCALE_NFSMENU_HEAD, "network.raw"); + CNFSMountGui mountGui; + CNFSUmountGui umountGui; + menu.addItem(GenericMenuSeparator); + menu.addItem(GenericMenuBack); + menu.addItem(GenericMenuSeparatorLine); + menu.addItem(new CMenuForwarder(LOCALE_NFS_REMOUNT, true, NULL, this, "remount")); + menu.addItem(new CMenuForwarder(LOCALE_NFS_MOUNT , true, NULL, & mountGui)); + menu.addItem(new CMenuForwarder(LOCALE_NFS_UMOUNT, true, NULL, &umountGui)); + return menu.exec(parent, actionKey); + } + else if(actionKey.substr(0,7) == "remount") + { + //umount automount dirs + for(int i = 0; i < NETWORK_NFS_NR_OF_ENTRIES; i++) + { + if(g_settings.network_nfs_automount[i]) + umount2(g_settings.network_nfs_local_dir[i],MNT_FORCE); + } + CFSMounter::automount(); + return menu_return::RETURN_REPAINT; + } + return menu_return::RETURN_REPAINT; +} + +const char * mntRes2Str(CFSMounter::MountRes res) +{ + switch(res) + { + case CFSMounter::MRES_FS_NOT_SUPPORTED: + return g_Locale->getText(LOCALE_NFS_MOUNTERROR_NOTSUP); + break; + case CFSMounter::MRES_FS_ALREADY_MOUNTED: + return g_Locale->getText(LOCALE_NFS_ALREADYMOUNTED); + break; + case CFSMounter::MRES_TIMEOUT: + return g_Locale->getText(LOCALE_NFS_MOUNTTIMEOUT); + break; + case CFSMounter::MRES_UNKNOWN: + return g_Locale->getText(LOCALE_NFS_MOUNTERROR); + break; + case CFSMounter::MRES_OK: + return g_Locale->getText(LOCALE_NFS_MOUNTOK); + break; + default: + return g_Locale->getText(NONEXISTANT_LOCALE); + break; + } +} + +const char * mntRes2Str(CFSMounter::UMountRes res) +{ + switch(res) + { + case CFSMounter::UMRES_ERR: + return g_Locale->getText(LOCALE_NFS_UMOUNTERROR); + break; + case CFSMounter::UMRES_OK: + return g_Locale->getText(NONEXISTANT_LOCALE); + break; + default: + return g_Locale->getText(NONEXISTANT_LOCALE); + break; + } +} diff --git a/src/gui/nfs.h b/src/gui/nfs.h new file mode 100644 index 000000000..97726e097 --- /dev/null +++ b/src/gui/nfs.h @@ -0,0 +1,86 @@ +/* + Neutrino-GUI - DBoxII-Project + + NFS Mount/Umount GUI by Zwen + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __neutrino_nfs_gui__ +#define __neutrino_nfs_gui__ + +#include +#include +#include + + +class CNFSMountGui : public CMenuTarget +{ + + private: + int menu(); + int menuEntry(int nr); + + char m_entry[NETWORK_NFS_NR_OF_ENTRIES][200]; + char ISO_8859_1_entry[NETWORK_NFS_NR_OF_ENTRIES][200]; + + CFSMounter::FS_Support m_nfs_sup; + CFSMounter::FS_Support m_cifs_sup; + CFSMounter::FS_Support m_lufs_sup; + + public: + CNFSMountGui(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + +class CNFSUmountGui : public CMenuTarget +{ + private: + + int menu(); + + public: + CNFSUmountGui(){}; + ~CNFSUmountGui(){}; + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + +class CNFSSmallMenu : public CMenuTarget +{ + private: + + public: + CNFSSmallMenu(){}; + ~CNFSSmallMenu(){}; + int exec( CMenuTarget* parent, const std::string & actionKey ); +}; + +const char * mntRes2Str(CFSMounter::MountRes res); +const char * mntRes2Str(CFSMounter::UMountRes res); + + +#endif diff --git a/src/gui/pictureviewer.cpp b/src/gui/pictureviewer.cpp new file mode 100644 index 000000000..71129b028 --- /dev/null +++ b/src/gui/pictureviewer.cpp @@ -0,0 +1,720 @@ +/* + Neutrino-GUI - DBoxII-Project + + MP3Player by Dirch + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +// remove this +#include + +#include +#include + +#include + +#include +#include +#include + +#define ROUND_RADIUS 9 + +//------------------------------------------------------------------------ +bool comparePictureByDate (const CPicture& a, const CPicture& b) +{ + return a.Date < b.Date ; +} +//------------------------------------------------------------------------ +extern bool comparetolower(const char a, const char b); /* filebrowser.cpp */ +//------------------------------------------------------------------------ +bool comparePictureByFilename (const CPicture& a, const CPicture& b) +{ + return std::lexicographical_compare(a.Filename.begin(), a.Filename.end(), b.Filename.begin(), b.Filename.end(), comparetolower); +} +//------------------------------------------------------------------------ + +CPictureViewerGui::CPictureViewerGui() +{ + frameBuffer = CFrameBuffer::getInstance(); + + visible = false; + selected = 0; + m_sort = FILENAME; + m_viewer = new CPictureViewer(); + if(strlen(g_settings.network_nfs_picturedir)!=0) + Path = g_settings.network_nfs_picturedir; + else + Path = "/"; + + picture_filter.addFilter("png"); + picture_filter.addFilter("bmp"); + picture_filter.addFilter("jpg"); + picture_filter.addFilter("jpeg"); + picture_filter.addFilter("gif"); + picture_filter.addFilter("crw"); +} + +//------------------------------------------------------------------------ + +CPictureViewerGui::~CPictureViewerGui() +{ + playlist.clear(); + delete m_viewer; +} + +//------------------------------------------------------------------------ +int CPictureViewerGui::exec(CMenuTarget* parent, const std::string & actionKey) +{ + selected = 0; + width = 710; + height = 570; + + if((g_settings.screen_EndX- g_settings.screen_StartX) < width) + width=(g_settings.screen_EndX- g_settings.screen_StartX); + if((g_settings.screen_EndY- g_settings.screen_StartY) < height) + height=(g_settings.screen_EndY- g_settings.screen_StartY); + + sheight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight(); + buttonHeight = std::min(25, sheight); + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + listmaxshow = (height-theight-2*buttonHeight)/(fheight); + height = theight+2*buttonHeight+listmaxshow*fheight; // recalc height + + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height)/ 2) + g_settings.screen_StartY; + + m_viewer->SetScaling((CPictureViewer::ScalingMode)g_settings.picviewer_scaling); + m_viewer->SetVisible(g_settings.screen_StartX, g_settings.screen_EndX, g_settings.screen_StartY, g_settings.screen_EndY); + + if(g_settings.video_Format == 3) + m_viewer->SetAspectRatio(16.0/9); + else + m_viewer->SetAspectRatio(4.0/3); + + if(parent) + parent->hide(); + + // tell neutrino we're in pic_mode + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , NeutrinoMessages::mode_pic ); + // remember last mode + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode() | NeutrinoMessages::norezap); + + g_Sectionsd->setPauseScanning(true); + + show(); + + // free picviewer mem + m_viewer->Cleanup(); + + // Start Sectionsd + g_Sectionsd->setPauseScanning(false); + + // Restore last mode + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , m_LastMode ); + + // always exit all + return menu_return::RETURN_REPAINT; +} + +//------------------------------------------------------------------------ + +int CPictureViewerGui::show() +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = -1; + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_PICTUREVIEWER_HEAD)); + m_state=MENU; + + int timeout; + + bool loop=true; + bool update=true; + + while(loop) + { + if(update) + { + hide(); + update=false; + paint(); + } + + if(m_state!=SLIDESHOW) + timeout=50; // egal + else + { + timeout=(m_time+atoi(g_settings.picviewer_slide_time)-(long)time(NULL))*10; + if(timeout <0 ) + timeout=1; + } + g_RCInput->getMsg( &msg, &data, timeout ); + + if( msg == CRCInput::RC_home) + { //Exit after cancel key + if(m_state!=MENU) + { + endView(); + update=true; + } + else + loop=false; + } + else if (msg == CRCInput::RC_timeout) + { + if(m_state == SLIDESHOW) + { + m_time=(long)time(NULL); + unsigned int next = selected + 1; + if (next >= playlist.size()) + next = 0; + view(next); + } + } + else if (msg == CRCInput::RC_left) + { + if (!playlist.empty()) + { + if (m_state == MENU) + { + if (selected < listmaxshow) + selected=playlist.size()-1; + else + selected -= listmaxshow; + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + } + else + { + view((selected == 0) ? (playlist.size() - 1) : (selected - 1)); + } + } + } + else if (msg == CRCInput::RC_right) + { + if (!playlist.empty()) + { + if (m_state == MENU) + { + selected += listmaxshow; + if (selected >= playlist.size()) { + if (((playlist.size() / listmaxshow) + 1) * listmaxshow == playlist.size() + listmaxshow) + selected=0; + else + selected = selected < (((playlist.size() / listmaxshow) + 1) * listmaxshow) ? (playlist.size() - 1) : 0; + } + liststart = (selected/listmaxshow)*listmaxshow; + paint(); + } + else + { + unsigned int next = selected + 1; + if (next >= playlist.size()) + next = 0; + view(next); + } + } + } + else if (msg == CRCInput::RC_up) + { + if ((m_state == MENU) && (!playlist.empty())) + { + int prevselected=selected; + if(selected==0) + { + selected = playlist.size()-1; + } + else + selected--; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + update=true; + } + else + { + paintItem(selected - liststart); + } + } + } + else if (msg == CRCInput::RC_down) + { + if ((m_state == MENU) && (!playlist.empty())) + { + int prevselected=selected; + selected = (selected+1)%playlist.size(); + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + update=true; + } + else + { + paintItem(selected - liststart); + } + } + } + else if (msg == CRCInput::RC_ok) + { + if (!playlist.empty()) + view(selected); + } + else if (msg == CRCInput::RC_red) + { + if (m_state == MENU) + { + if (!playlist.empty()) + { + CViewList::iterator p = playlist.begin()+selected; + playlist.erase(p); + if (selected >= playlist.size()) + selected = playlist.size()-1; + update = true; + } + } + } + else if(msg==CRCInput::RC_green) + { + if (m_state == MENU) + { + CFileBrowser filebrowser((g_settings.filebrowser_denydirectoryleave) ? g_settings.network_nfs_picturedir : ""); + + filebrowser.Multi_Select = true; + filebrowser.Dirs_Selectable = true; + filebrowser.Filter = &picture_filter; + + hide(); + + if (filebrowser.exec(Path.c_str())) + { + Path = filebrowser.getCurrentDir(); + CFileList::const_iterator files = filebrowser.getSelectedFiles().begin(); + for(; files != filebrowser.getSelectedFiles().end();files++) + { + if(files->getType() == CFile::FILE_PICTURE) + { + CPicture pic; + pic.Filename = files->Name; + std::string tmp = files->Name.substr(files->Name.rfind('/')+1); + pic.Name = tmp.substr(0,tmp.rfind('.')); + pic.Type = tmp.substr(tmp.rfind('.')+1); + struct stat statbuf; + if(stat(pic.Filename.c_str(),&statbuf) != 0) + printf("stat error"); + pic.Date = statbuf.st_mtime; + playlist.push_back(pic); + } + else + printf("Wrong Filetype %s:%d\n",files->Name.c_str(), files->getType()); + } + if (m_sort == FILENAME) + std::sort(playlist.begin(), playlist.end(), comparePictureByFilename); + else if (m_sort == DATE) + std::sort(playlist.begin(), playlist.end(), comparePictureByDate); + } + update=true; + } + } + else if(msg==CRCInput::RC_yellow) + { + if (m_state == MENU && !playlist.empty()) + { + playlist.clear(); + selected=0; + update=true; + } + } + else if(msg==CRCInput::RC_blue) + { + if ((m_state == MENU) && (!playlist.empty())) + { + m_time=(long)time(NULL); + view(selected); + m_state=SLIDESHOW; + } else { + if(m_state == SLIDESHOW) + m_state = VIEW; + else + m_state = SLIDESHOW; + } + } + else if(msg==CRCInput::RC_help) + { + if (m_state == MENU) + { + showHelp(); + paint(); + } + } + else if( msg == CRCInput::RC_1 ) + { + if (m_state != MENU) + { + m_viewer->Zoom(2.0/3); + } + + } + else if( msg == CRCInput::RC_2 ) + { + if (m_state != MENU) + { + m_viewer->Move(0,-50); + } + } + else if( msg == CRCInput::RC_3 ) + { + if (m_state != MENU) + { + m_viewer->Zoom(1.5); + } + + } + else if( msg == CRCInput::RC_4 ) + { + if (m_state != MENU) + { + m_viewer->Move(-50,0); + } + } + else if ( msg == CRCInput::RC_5 ) + { + if(m_state==MENU) + { + if (!playlist.empty()) + { + if(m_sort==FILENAME) + { + m_sort=DATE; + std::sort(playlist.begin(),playlist.end(),comparePictureByDate); + } + else if(m_sort==DATE) + { + m_sort=FILENAME; + std::sort(playlist.begin(),playlist.end(),comparePictureByFilename); + } + update=true; + } + } + } + else if( msg == CRCInput::RC_6 ) + { + if (m_state != MENU && playlist.empty()) + { + m_viewer->Move(50,0); + } + } + else if( msg == CRCInput::RC_8 ) + { + if (m_state != MENU && playlist.empty()) + { + m_viewer->Move(0,50); + } + } + else if(msg==CRCInput::RC_0) + { + if (!playlist.empty()) + view(selected, true); + } + else if(msg==CRCInput::RC_setup) + { + if(m_state==MENU) + { + CNFSSmallMenu nfsMenu; + nfsMenu.exec(this, ""); + update=true; + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_PICTUREVIEWER_HEAD)); + } + } + else if(msg == NeutrinoMessages::CHANGEMODE) + { + if((data & NeutrinoMessages::mode_mask) !=NeutrinoMessages::mode_pic) + { + loop = false; + m_LastMode=data; + } + } + else if(msg == NeutrinoMessages::RECORD_START || + msg == NeutrinoMessages::ZAPTO || + msg == NeutrinoMessages::STANDBY_ON || + msg == NeutrinoMessages::SHUTDOWN || + msg == NeutrinoMessages::SLEEPTIMER) + { + // Exit for Record/Zapto Timers + if (m_state != MENU) + endView(); + loop = false; + g_RCInput->postMsg(msg, data); + } + else if((msg == CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) { + } + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + } + } + } + hide(); + + return(res); +} + +//------------------------------------------------------------------------ + +void CPictureViewerGui::hide() +{ + if(visible) { + frameBuffer->paintBackground(); + visible = false; + } +} + +//------------------------------------------------------------------------ + +void CPictureViewerGui::paintItem(int pos) +{ +// printf("paintItem{\n"); + int ypos = y+ theight + 0 + pos*fheight; + + uint8_t color; + fb_pixel_t bgcolor; + + if ((liststart+pos < playlist.size()) && (pos & 1) ) + { + color = COL_MENUCONTENTDARK; + bgcolor = COL_MENUCONTENTDARK_PLUS_0; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + if (liststart+pos == selected) + { + frameBuffer->paintBoxRel(x,ypos, width-15, fheight, bgcolor); + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + + frameBuffer->paintBoxRel(x,ypos, width-15, fheight, bgcolor, liststart+pos == selected ? ROUND_RADIUS : 0, 3); + if(liststart+posgetRenderWidth(timestring); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,ypos+fheight, width-30 - w, tmp, color, fheight, true); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+width-20-w,ypos+fheight, w, timestring, color, fheight); + + } +// printf("paintItem}\n"); +} + +//------------------------------------------------------------------------ + +void CPictureViewerGui::paintHead() +{ +// printf("paintHead{\n"); + std::string strCaption = g_Locale->getText(LOCALE_PICTUREVIEWER_HEAD); + frameBuffer->paintBoxRel(x,y, width,theight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + frameBuffer->paintIcon("mp3.raw",x+7,y+10); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+35,y+theight+0, width- 45, strCaption, COL_MENUHEAD, 0, true); // UTF-8 + int ypos=y+0; + if(theight > 26) + ypos = (theight-26) / 2 + y ; + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 60, ypos ); + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, x+ width- 30, ypos ); +// printf("paintHead}\n"); +} + +//------------------------------------------------------------------------ +const struct button_label PictureViewerButtons[4] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_AUDIOPLAYER_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_AUDIOPLAYER_ADD }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_DELETEALL }, + { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_PICTUREVIEWER_SLIDESHOW } +}; + +void CPictureViewerGui::paintFoot() +{ +// printf("paintFoot{\n"); + int ButtonWidth = (width-20) / 4; + int ButtonWidth2 = (width-50) / 2; + frameBuffer->paintBoxRel(x,y+(height-2*buttonHeight), width,2*buttonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + frameBuffer->paintHLine(x, x+width, y+(height-2*buttonHeight), COL_INFOBAR_SHADOW_PLUS_0); + + if (!playlist.empty()) + { + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x + 1* ButtonWidth2 + 25, y+(height-buttonHeight)-3); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + 1 * ButtonWidth2 + 53 , y+(height-buttonHeight)+24 - 4, ButtonWidth2- 28, g_Locale->getText(LOCALE_PICTUREVIEWER_SHOW), COL_INFOBAR, 0, true); // UTF-8 + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_5, x+ 0* ButtonWidth2 + 25, y+(height-buttonHeight)-3); + std::string tmp = g_Locale->getText(LOCALE_PICTUREVIEWER_SORTORDER); + tmp += ' '; + if(m_sort==FILENAME) + tmp += g_Locale->getText(LOCALE_PICTUREVIEWER_SORTORDER_DATE); + else if(m_sort==DATE) + tmp += g_Locale->getText(LOCALE_PICTUREVIEWER_SORTORDER_FILENAME); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+ 0* ButtonWidth2 +53 , y+(height-buttonHeight)+24 - 4, ButtonWidth2- 28, tmp, COL_INFOBAR, 0, true); // UTF-8 + + + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + (height - 2 * buttonHeight) + 4, ButtonWidth, 4, PictureViewerButtons); + } + else + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + ButtonWidth + 10, y + (height - 2 * buttonHeight) + 4, ButtonWidth, 1, &(PictureViewerButtons[1])); +// printf("paintFoot}\n"); +} +//------------------------------------------------------------------------ +void CPictureViewerGui::paintInfo() +{ +} +//------------------------------------------------------------------------ + +void CPictureViewerGui::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + + paintHead(); + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((playlist.size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); + + paintFoot(); + paintInfo(); + + visible = true; +} + +void CPictureViewerGui::view(unsigned int index, bool unscaled) +{ + selected=index; + + CVFD::getInstance()->showMenuText(0, playlist[index].Name.c_str()); + char timestring[19]; + strftime(timestring, 18, "%d-%m-%Y %H:%M", gmtime(&playlist[index].Date)); + //CVFD::getInstance()->showMenuText(1, timestring); //FIXME + + if(unscaled) + m_viewer->DecodeImage(playlist[index].Filename, true, unscaled); + m_viewer->ShowImage(playlist[index].Filename, unscaled); + + //Decode next + unsigned int next=selected+1; + if(next > playlist.size()-1) + next=0; + if(m_state==MENU) + m_state=VIEW; + if(m_state==VIEW) + m_viewer->DecodeImage(playlist[next].Filename,true); + else + m_viewer->DecodeImage(playlist[next].Filename,false); +} + +void CPictureViewerGui::endView() +{ + if(m_state != MENU) + m_state=MENU; +} + +void CPictureViewerGui::showHelp() +{ + Helpbox helpbox; + helpbox.addLine(g_Locale->getText(LOCALE_PICTUREVIEWER_HELP1)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_OKAY, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP2)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_5, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP3)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_0, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP4)); + helpbox.addPagebreak(); + helpbox.addLine(g_Locale->getText(LOCALE_PICTUREVIEWER_HELP5)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_LEFT, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP6)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_RIGHT, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP7)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_5, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP8)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_HOME, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP9)); + helpbox.addPagebreak(); + helpbox.addLine(g_Locale->getText(LOCALE_PICTUREVIEWER_HELP10)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_OKAY, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP11)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_LEFT, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP12)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_RIGHT, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP13)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_1, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP14)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_3, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP15)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_2, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP16)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_4, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP17)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_6, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP18)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_8, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP19)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_5, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP20)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_0, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP21)); + helpbox.addLine(NEUTRINO_ICON_BUTTON_HOME, g_Locale->getText(LOCALE_PICTUREVIEWER_HELP22)); + + helpbox.addLine("Version: $Revision: 1.57 $"); + hide(); + helpbox.show(LOCALE_MESSAGEBOX_INFO); +} diff --git a/src/gui/pictureviewer.h b/src/gui/pictureviewer.h new file mode 100644 index 000000000..a9175c2a8 --- /dev/null +++ b/src/gui/pictureviewer.h @@ -0,0 +1,120 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __pictureviewergui__ +#define __pictureviewergui__ + + +#include "driver/framebuffer.h" +#include "driver/pictureviewer/pictureviewer.h" +#include "gui/widget/menue.h" +#include "gui/filebrowser.h" + +#include + +class CPicture +{ +public: + std::string Filename; + std::string Name; + std::string Type; + time_t Date; +}; + +typedef std::vector CViewList; + + +class CPictureViewerGui : public CMenuTarget +{ + public: + enum State + { + VIEW=0, + MENU, + SLIDESHOW + }; + enum SortOrder + { + DATE=0, + FILENAME, + NAME + }; + private: + CFrameBuffer *frameBuffer; + CPictureViewer *m_viewer; + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + int fheight; // Fonthoehe Playlist-Inhalt + int theight; // Fonthoehe Playlist-Titel + int sheight; // Fonthoehe MP Info + int buttonHeight; + bool visible; + State m_state; + SortOrder m_sort; + + CViewList playlist; + std::string Path; + + int width; + int height; + int x; + int y; + int m_title_w; + long m_time; + + int m_LastMode; + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void paintInfo(); + void paintLCD(); + void hide(); + + CFileFilter picture_filter; + void view(unsigned int nr, bool unscaled=false); + void endView(); + int show(); + + void showHelp(); + + public: + CPictureViewerGui(); + ~CPictureViewerGui(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif + + diff --git a/src/gui/pluginlist.cpp b/src/gui/pluginlist.cpp new file mode 100644 index 000000000..ff30c1cdb --- /dev/null +++ b/src/gui/pluginlist.cpp @@ -0,0 +1,371 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* for alexW images with old drivers: + * #define USE_VBI_INTERFACE 1 + */ + +#ifdef USE_VBI_INTERFACE + #define AVIA_VBI_START_VTXT 1 + #define AVIA_VBI_STOP_VTXT 2 +#endif + +#include +extern CPlugins * g_PluginList; /* neutrino.cpp */ +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ + +CPluginList::CPluginList(const neutrino_locale_t Name, const uint32_t listtype) +{ + frameBuffer = CFrameBuffer::getInstance(); + name = Name; + pluginlisttype = listtype; + selected = 0; + width = 500; + if(width>(g_settings.screen_EndX-g_settings.screen_StartX)) + width=(g_settings.screen_EndX-g_settings.screen_StartX); + height = 526; + if((height+50)>(g_settings.screen_EndY-g_settings.screen_StartY)) + height=(g_settings.screen_EndY-g_settings.screen_StartY) - 50; // 2*25 pixel frei + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + // + fheight1 = g_Font[SNeutrinoSettings::FONT_TYPE_GAMELIST_ITEMLARGE]->getHeight(); + fheight2 = g_Font[SNeutrinoSettings::FONT_TYPE_GAMELIST_ITEMSMALL]->getHeight(); + fheight = fheight1 + fheight2 + 2; + // + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height) / 2) + g_settings.screen_StartY; + liststart = 0; +} + +CPluginList::~CPluginList() +{ + for(unsigned int count=0;counthide(); + } + + //scan4plugins here! + for(unsigned int count=0;countname = g_Locale->getText(LOCALE_MENU_BACK); + pluginlist.push_back(tmp); + + for(unsigned int count=0;count < (unsigned int)g_PluginList->getNumberOfPlugins();count++) + { + if ((g_PluginList->getType(count) & pluginlisttype) && !g_PluginList->isHidden(count)) + { + tmp = new pluginitem(); + tmp->number = count; + tmp->name = g_PluginList->getName(count); + tmp->desc = g_PluginList->getDescription(count); + pluginlist.push_back(tmp); + } + } + + paint(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + if ( ( msg == CRCInput::RC_timeout ) || + ( msg == (neutrino_msg_t)g_settings.key_channelList_cancel ) ) + { + loop=false; + } + else if ( msg == (neutrino_msg_t)g_settings.key_channelList_pageup ) + { + if ((int(selected)-int(listmaxshow))<0) + selected=0; + else + selected -= listmaxshow; + liststart = (selected/listmaxshow)*listmaxshow; + paintItems(); + } + else if ( msg == (neutrino_msg_t)g_settings.key_channelList_pagedown ) + { + selected+=listmaxshow; + if (selected>pluginlist.size()-1) + selected=pluginlist.size()-1; + liststart = (selected/listmaxshow)*listmaxshow; + paintItems(); + } + else if ( msg == CRCInput::RC_up ) + { + int prevselected=selected; + if(selected==0) + { + selected = pluginlist.size()-1; + } + else + selected--; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paintItems(); + } + else + { + paintItem(selected - liststart); + } + } + else if ( msg == CRCInput::RC_down ) + { + int prevselected=selected; + selected = (selected+1)%pluginlist.size(); + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paintItems(); + } + else + { + paintItem(selected - liststart); + } + } + else if ( msg == CRCInput::RC_ok ) + { + if(selected==0) + { + loop=false; + } + else + {//exec the plugin :)) + if (pluginSelected() == close) + { + loop=false; + } + } + } + else if( (msg== CRCInput::RC_red) || + (msg==CRCInput::RC_green) || + (msg==CRCInput::RC_yellow) || + (msg==CRCInput::RC_blue) || + (CRCInput::isNumeric(msg)) ) + { + g_RCInput->postMsg(msg, data); + loop=false; + } + else if ( CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + hide(); + return res; +} + +void CPluginList::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width+15,height); +} + +void CPluginList::paintItem(int pos) +{ + int ypos = (y+theight) + pos*fheight; + int itemheight = fheight; + + uint8_t color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (liststart+pos==selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + + if(liststart+pos==0) + { //back is half-height... + itemheight = (fheight / 2) + 3; + frameBuffer->paintBoxRel(x , ypos + itemheight , width , 15, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x + 10, ypos + itemheight + 5, width - 20, 1, COL_MENUCONTENT_PLUS_5); + frameBuffer->paintBoxRel(x + 10, ypos + itemheight + 6, width - 20, 1, COL_MENUCONTENT_PLUS_2); + } + else if(liststart==0) + { + ypos -= (fheight / 2) - 15; + if(pos==(int)listmaxshow-1) + frameBuffer->paintBoxRel(x,ypos+itemheight, width, (fheight / 2)-15, COL_MENUCONTENT_PLUS_0); + + } + frameBuffer->paintBoxRel(x, ypos, width, itemheight, bgcolor); + + + if(liststart+posRenderString(x+10, ypos+fheight1+3, width-20, actplugin->name, color, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_GAMELIST_ITEMSMALL]->RenderString(x+20, ypos+fheight, width-20, actplugin->desc, color, 0, true); // UTF-8 + } +} + +void CPluginList::paintHead() +{ + if(listmaxshow > pluginlist.size()+1) + frameBuffer->paintBoxRel(x,y, width,theight, COL_MENUHEAD_PLUS_0); + else + frameBuffer->paintBoxRel(x,y, width+15,theight, COL_MENUHEAD_PLUS_0); + + if(pluginlisttype == CPlugins::P_TYPE_GAME) + { + frameBuffer->paintIcon(NEUTRINO_ICON_GAMES,x+8,y+5); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+38,y+theight+1, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + } else if (pluginlisttype == CPlugins::P_TYPE_SCRIPT) + { + frameBuffer->paintIcon(NEUTRINO_ICON_SHELL,x+8,y+5); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+38,y+theight+1, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + } else + { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+8,y+theight+1, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + } +} + +void CPluginList::paint() +{ + hide(); + width = 500; + if(width>(g_settings.screen_EndX-g_settings.screen_StartX)) + width=(g_settings.screen_EndX-g_settings.screen_StartX); + height = 526; + if((height+50)>(g_settings.screen_EndY-g_settings.screen_StartY)) + height=(g_settings.screen_EndY-g_settings.screen_StartY) - 50; // 2*25 pixel frei + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + x=(((g_settings.screen_EndX- g_settings.screen_StartX)-width) / 2) + g_settings.screen_StartX; + y=(((g_settings.screen_EndY- g_settings.screen_StartY)-height) / 2) + g_settings.screen_StartY; + + liststart = (selected/listmaxshow)*listmaxshow; + + paintHead(); + paintItems(); +} + +void CPluginList::paintItems() +{ + if(listmaxshow <= pluginlist.size()+1) + { + // Scrollbar + int nrOfPages = ((pluginlist.size()-1) / listmaxshow)+1; + int currPage = (liststart/listmaxshow) +1; + float blockHeight = (height-theight-4)/nrOfPages; + frameBuffer->paintBoxRel(x+width, y+theight, 15, height-theight, COL_MENUCONTENT_PLUS_1); + frameBuffer->paintBoxRel(x+ width +2, y+theight+2+int((currPage-1)*blockHeight) , 11, int(blockHeight), COL_MENUCONTENT_PLUS_3); + } + + for(unsigned int count=0;countstartPlugin(pluginlist[selected]->number,0); + if (!g_PluginList->getScriptOutput().empty()) + { + hide(); + //ShowMsgUTF(LOCALE_PLUGINS_RESULT, Latin1_to_UTF8(g_PluginList->getScriptOutput()), CMessageBox::mbrBack,CMessageBox::mbBack,NEUTRINO_ICON_SHELL); + ShowMsgUTF(LOCALE_PLUGINS_RESULT, g_PluginList->getScriptOutput(), CMessageBox::mbrBack,CMessageBox::mbBack,NEUTRINO_ICON_SHELL); + } + paint(); + return resume; +} + +CPluginChooser::CPluginChooser(const neutrino_locale_t Name, const uint32_t listtype, char* pluginname) + : CPluginList(Name, listtype), selected_plugin(pluginname) +{ +} + +CPluginList::result_ CPluginChooser::pluginSelected() +{ + strcpy(selected_plugin,g_PluginList->getFileName(pluginlist[selected]->number)); + return CPluginList::close; +} + diff --git a/src/gui/pluginlist.h b/src/gui/pluginlist.h new file mode 100644 index 000000000..0bc4e1465 --- /dev/null +++ b/src/gui/pluginlist.h @@ -0,0 +1,113 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __pluginlist__ +#define __pluginlist__ + +#include + +#include +#include + +#include +#include + +class CPluginList : public CMenuTarget +{ + + public: + enum result_ + { + close = 0, + resume = 1, + } result; + + private: + + CFrameBuffer *frameBuffer; + + struct pluginitem + { + int number; + std::string name; // UTF-8 encoded + std::string desc; // UTF-8 encoded + }; + + unsigned int liststart; + unsigned int listmaxshow; + int key; + neutrino_locale_t name; + uint32_t pluginlisttype; + + int fheight; // Fonthoehe Channellist-Inhalt + int theight; // Fonthoehe Channellist-Titel + + int fheight1,fheight2; + + int width; + int height; + int x; + int y; + + void paintItem(int pos); + void paintItems(); + void paint(); + void paintHead(); + + protected: + + unsigned int selected; + std::vector pluginlist; + + virtual CPluginList::result_ pluginSelected(); + + public: + + CPluginList(const neutrino_locale_t Name, const uint32_t listtype); + virtual ~CPluginList(); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + +class CPluginChooser : public CPluginList +{ + private: + char* selected_plugin; + protected: + + CPluginList::result_ pluginSelected(); + + public: + CPluginChooser(const neutrino_locale_t Name, const uint32_t listtype, char* pluginname); +}; + +#endif diff --git a/src/gui/plugins.cpp b/src/gui/plugins.cpp new file mode 100644 index 000000000..8cf39feb4 --- /dev/null +++ b/src/gui/plugins.cpp @@ -0,0 +1,618 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/* for alexW images with old drivers: + * #define USE_VBI_INTERFACE 1 + */ + +#ifdef USE_VBI_INTERFACE + #define AVIA_VBI_START_VTXT 1 + #define AVIA_VBI_STOP_VTXT 2 +#endif + +#include +extern CPlugins * g_PluginList; /* neutrino.cpp */ +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ + +#define PLUGINDIR_VAR "/var/tuxbox/plugins" +#define PLUGINDIR_HDD "/hdd/tuxbox/plugins" +#define PLUGINDIR_USB "/mnt/usb/tuxbox/plugins" + +bool CPlugins::plugin_exists(const std::string & filename) +{ + return (find_plugin(filename) >= 0); +} + +int CPlugins::find_plugin(const std::string & filename) +{ + for(int i = 0; i < (int) plugin_list.size();i++) + { + if( (filename.compare(plugin_list[i].filename) == 0) || (filename.compare(plugin_list[i].filename + ".cfg") == 0) ) + return i; + } + return -1; +} + +bool CPlugins::pluginfile_exists(const std::string & filename) +{ + FILE *file = fopen(filename.c_str(),"r"); + if (file != NULL) + { + fclose(file); + return true; + } else + { + return false; + } +} + +void CPlugins::scanDir(const char *dir) +{ + struct dirent **namelist; + std::string fname; + + int number_of_files = scandir(dir, &namelist, 0, alphasort); + + for (int i = 0; i < number_of_files; i++) + { + std::string filename; + + filename = namelist[i]->d_name; + int pos = filename.find(".cfg"); + if (pos > -1) + { + plugin new_plugin; + new_plugin.filename = filename.substr(0, pos); + fname = dir; + fname += '/'; + new_plugin.cfgfile = fname.append(new_plugin.filename); + new_plugin.cfgfile.append(".cfg"); + parseCfg(&new_plugin); + bool plugin_ok = parseCfg(&new_plugin); + if (plugin_ok) { + new_plugin.pluginfile = fname; + if (new_plugin.type == CPlugins::P_TYPE_SCRIPT) + { + new_plugin.pluginfile.append(".sh"); + } else { + new_plugin.pluginfile.append(".so"); + } + // We do not check if new_plugin.pluginfile exists since .cfg in + // PLUGINDIR_VAR can overwrite settings in read only dir + // PLUGINDIR. This needs PLUGINDIR_VAR to be scanned at + // first -> .cfg in PLUGINDIR will be skipped since plugin + // already exists in the list. + // This behavior is used to make sure plugins can be disabled + // by creating a .cfg in PLUGINDIR_VAR (PLUGINDIR often is read only). + + if(!plugin_exists(new_plugin.filename)) + { + plugin_list.push_back(new_plugin); + number_of_plugins++; + } + } + } + } +} + +void CPlugins::loadPlugins() +{ + frameBuffer = CFrameBuffer::getInstance(); + number_of_plugins = 0; + plugin_list.clear(); + + scanDir(PLUGINDIR_HDD); + scanDir(PLUGINDIR_USB); + scanDir(PLUGINDIR_VAR); + scanDir(PLUGINDIR); + sort(plugin_list.begin(), plugin_list.end()); +} + +CPlugins::~CPlugins() +{ + plugin_list.clear(); +} + +bool CPlugins::parseCfg(plugin *plugin_data) +{ +// FILE *fd; + + std::ifstream inFile; + std::string line[20]; + int linecount = 0; + bool reject = false; + + inFile.open(plugin_data->cfgfile.c_str()); + + while(linecount < 20 && getline(inFile, line[linecount++])); + + plugin_data->fb = false; + plugin_data->rc = false; + plugin_data->lcd = false; + plugin_data->vtxtpid = false; + plugin_data->showpig = false; + plugin_data->needoffset = false; + plugin_data->hide = false; + plugin_data->type = CPlugins::P_TYPE_DISABLED; + + for (int i = 0; i < linecount; i++) + { + std::istringstream iss(line[i]); + std::string cmd; + std::string parm; + + getline(iss, cmd, '='); + getline(iss, parm, '='); + + if (cmd == "pluginversion") + { + plugin_data->version = atoi(parm.c_str()); + } + else if (cmd == "name") + { + plugin_data->name = parm; + } + else if (cmd == "desc") + { + plugin_data->description = parm; + } + else if (cmd == "depend") + { + plugin_data->depend = parm; + } + else if (cmd == "type") + { + plugin_data->type = getPluginType(atoi(parm.c_str())); + } + else if (cmd == "needfb") + { + plugin_data->fb = ((parm == "1")?true:false); + } + else if (cmd == "needrc") + { + plugin_data->rc = ((parm == "1")?true:false); + } + else if (cmd == "needlcd") + { + plugin_data->lcd = ((parm == "1")?true:false); + } + else if (cmd == "needvtxtpid") + { + plugin_data->vtxtpid = ((parm == "1")?true:false); + } + else if (cmd == "pigon") + { + plugin_data->showpig = ((parm == "1")?true:false); + } + else if (cmd == "needoffsets") + { + plugin_data->needoffset = ((parm == "1")?true:false); + } + else if (cmd == "hide") + { + plugin_data->hide = ((parm == "1")?true:false); + } + else if (cmd == "needenigma") + { + reject = ((parm == "1")?true:false); + } + + } + + inFile.close(); + return !reject; +} + +PluginParam * CPlugins::makeParam(const char * const id, const char * const value, PluginParam * const next) +{ + PluginParam * startparam = new PluginParam; + + startparam->next = next; + startparam->id = id; + startparam->val = strdup(value); + + return startparam; +} + +PluginParam * CPlugins::makeParam(const char * const id, const int value, PluginParam * const next) +{ + char aval[10]; + + sprintf(aval, "%d", value); + + return makeParam(id, aval, next); +} + +void CPlugins::start_plugin_by_name(const std::string & filename,int param) +{ + for(int i = 0; i < (int) plugin_list.size();i++) + { + if(filename.compare(g_PluginList->getName(i))==0) + { + startPlugin(i,param); + return; + } + } +} + +void CPlugins::startPlugin(const char * const name) +{ + int pluginnr = find_plugin(name); + if (pluginnr > -1) + startPlugin(pluginnr,0); + else + printf("[CPlugins] could not find %s\n", name); + +} + +void CPlugins::startScriptPlugin(int number) +{ + const char *script = plugin_list[number].pluginfile.c_str(); + printf("[CPlugins] executing script %s\n",script); + if (!pluginfile_exists(plugin_list[number].pluginfile)) + { + printf("[CPlugins] could not find %s,\nperhaps wrong plugin type in %s\n", + script, plugin_list[number].cfgfile.c_str()); + return; + } + + FILE *f = popen(script,"r"); + if (f != NULL) + { + char output[1024]; + while (fgets(output,1024,f)) + { + scriptOutput += output; + } + pclose(f); + } + else + { + printf("[CPlugins] can't execute %s\n",script); + } +} + +int mysystem(char * cmd, char * arg1, char * arg2); +void CPlugins::startPlugin(int number,int param) +{ + // always delete old output + delScriptOutput(); + + //bool ispip = strncmp(plugin_list[number].pluginfile.c_str(), "pip", 3) ? false : true; + bool ispip = strstr(plugin_list[number].pluginfile.c_str(), "pip") != 0; +//printf("exec: %s pip: %d\n", plugin_list[number].pluginfile.c_str(), ispip); + if(ispip && !g_RemoteControl->is_video_started) + return; + if (plugin_list[number].type == CPlugins::P_TYPE_SCRIPT) + { + startScriptPlugin(number); + return; + } + if (!pluginfile_exists(plugin_list[number].pluginfile)) + { + printf("[CPlugins] could not find %s,\nperhaps wrong plugin type in %s\n", + plugin_list[number].pluginfile.c_str(), plugin_list[number].cfgfile.c_str()); + return; + } + + PluginExec execPlugin; + char depstring[129]; + char *argv[20]; + void *libhandle[20]; + int argc = 0, i = 0, lcd_fd=-1; + char *p; + char *np; + void *handle; + char * error; + int vtpid = 0; + PluginParam * startparam = 0; + + g_RCInput->clearRCMsg(); +#if 0 + if (plugin_list[number].fb) + { + startparam = makeParam(P_ID_FBUFFER , frameBuffer->getFileHandle() , startparam); + } + if (plugin_list[number].rc) + { + startparam = makeParam(P_ID_RCINPUT , g_RCInput->getFileHandle() , startparam); + startparam = makeParam(P_ID_RCBLK_ANF, g_settings.repeat_genericblocker, startparam); + startparam = makeParam(P_ID_RCBLK_REP, g_settings.repeat_blocker , startparam); + } + else + { + g_RCInput->stopInput(); + } + if (plugin_list[number].lcd) + { + CLCD::getInstance()->pause(); + + lcd_fd = open("/dev/dbox/lcd0", O_RDWR); + + startparam = makeParam(P_ID_LCD , lcd_fd , startparam); + } + if (plugin_list[number].vtxtpid) + { + vtpid = g_RemoteControl->current_PIDs.PIDs.vtxtpid; +#ifdef USE_VBI_INTERFACE + int fd = open("/dev/dbox/vbi0", O_RDWR); + if (fd > 0) + { + ioctl(fd, AVIA_VBI_STOP_VTXT, 0); + close(fd); + } +#endif + if(param>0) + vtpid=param; + startparam = makeParam(P_ID_VTXTPID, vtpid, startparam); + } + if (plugin_list[number].needoffset) + { + startparam = makeParam(P_ID_VFORMAT , g_settings.video_Format , startparam); + startparam = makeParam(P_ID_OFF_X , g_settings.screen_StartX , startparam); + startparam = makeParam(P_ID_OFF_Y , g_settings.screen_StartY , startparam); + startparam = makeParam(P_ID_END_X , g_settings.screen_EndX , startparam); + startparam = makeParam(P_ID_END_Y , g_settings.screen_EndY , startparam); + } + + PluginParam *par = startparam; + for( ; par; par=par->next ) + { + printf("[CPlugins] (id,val):(%s,%s)\n", par->id, par->val); + } + std::string pluginname = plugin_list[number].filename; + + strcpy(depstring, plugin_list[number].depend.c_str()); + + argc=0; + if ( depstring[0] ) + { + p=depstring; + while( 1 ) + { + argv[ argc ] = p; + argc++; + np = strchr(p,','); + if ( !np ) + break; + + *np=0; + p=np+1; + if ( argc == 20 ) // mehr nicht ! + break; + } + } + for( i=0; isetPauseScanning (true); + g_Zapit->setEventMode(false); +#if 0 + //g_Controld->setEventMode(false); +#endif + if(g_Zapit->isPlayBackActive()) { + if(!CNeutrinoApp::getInstance()->recordingstatus) + g_Zapit->setRecordMode(true); + } else { + /* no playback, we playing file ? zap to channel */ + t_channel_id current_channel = g_Zapit->getCurrentServiceID(); + g_Zapit->zapTo_serviceID(current_channel); + } + } + g_RCInput->close_click(); +#ifndef FB_USE_PALETTE + if (plugin_list[number].fb) { + frameBuffer->setMode(720, 576, 8); + frameBuffer->paletteSet(); + } +#endif + execPlugin(startparam); + dlclose(handle); + printf("[CPlugins] exec done...\n"); + g_RCInput->open_click(); + } + } + //restore framebuffer... +//#if 0 +// g_RCInput->open_click(); +//#endif + + //if (!plugin_list[number].rc) + g_RCInput->restartInput(); + g_RCInput->clearRCMsg(); + + if (plugin_list[number].lcd) + { + if(lcd_fd != -1) + close(lcd_fd); + CLCD::getInstance()->resume(); + } + + if (plugin_list[number].fb) + { +#ifdef FB_USE_PALETTE + frameBuffer->paletteSet(); +#else + frameBuffer->setMode(720, 576, 8 * sizeof(fb_pixel_t)); +#endif + } + frameBuffer->paintBackgroundBox(0,0,720,576); + if(ispip) { + if(!CNeutrinoApp::getInstance()->recordingstatus) { + g_Zapit->setRecordMode(false); + } +#if 0 + //g_Controld->setEventMode(true); +#endif + g_Zapit->setEventMode(true); + g_Sectionsd->setPauseScanning (false); + } + +#ifdef USE_VBI_INTERFACE + if (plugin_list[number].vtxtpid) + { + if (vtpid != 0) + { + // versuche, den gtx/enx_vbi wieder zu starten + int fd = open("/dev/dbox/vbi0", O_RDWR); + if (fd > 0) + { + ioctl(fd, AVIA_VBI_START_VTXT, vtpid); + close(fd); + } + } + } +#endif + } + + /* unload shared libs */ + for( i=0; iid, since it is the original */ + free(par->val); + PluginParam * tmp = par; + par = par->next; + delete tmp; + } + g_RCInput->clearRCMsg(); +#else + g_RCInput->clearRCMsg(); + g_RCInput->stopInput(); + //frameBuffer->setMode(720, 576, 8 * sizeof(fb_pixel_t)); +printf("Starting %s\n", plugin_list[number].pluginfile.c_str()); + mysystem((char *) plugin_list[number].pluginfile.c_str(), NULL, NULL); + //frameBuffer->setMode(720, 576, 8 * sizeof(fb_pixel_t)); + frameBuffer->paintBackground(); + g_RCInput->restartInput(); + g_RCInput->clearRCMsg(); +#endif +} + +bool CPlugins::hasPlugin(CPlugins::p_type_t type) +{ + for (std::vector::iterator it=plugin_list.begin(); + it!=plugin_list.end();it++) + { + if (it->type == type && !it->hide) + return true; + } + return false; +} + +const std::string& CPlugins::getScriptOutput() const +{ + return scriptOutput; +} + +void CPlugins::delScriptOutput() +{ + scriptOutput.clear(); +} + +CPlugins::p_type_t CPlugins::getPluginType(int type) +{ + switch (type) + { + case PLUGIN_TYPE_DISABLED: + return P_TYPE_DISABLED; + break; + case PLUGIN_TYPE_GAME: + return P_TYPE_GAME; + break; + case PLUGIN_TYPE_TOOL: + return P_TYPE_TOOL; + break; + case PLUGIN_TYPE_SCRIPT: + return P_TYPE_SCRIPT; + break; + default: + return P_TYPE_DISABLED; + } +} + + diff --git a/src/gui/plugins.h b/src/gui/plugins.h new file mode 100644 index 000000000..532857736 --- /dev/null +++ b/src/gui/plugins.h @@ -0,0 +1,131 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __plugins__ +#define __plugins__ + +#include + +#include +#include + +#include + +#include +#include + +class CPlugins +{ + + public: + typedef enum p_type + { + P_TYPE_DISABLED = 0x1, + P_TYPE_GAME = 0x2, + P_TYPE_TOOL = 0x4, + P_TYPE_SCRIPT = 0x8, + } + p_type_t; + + + private: + + CFrameBuffer *frameBuffer; + + struct plugin + { + std::string filename; + std::string cfgfile; + std::string pluginfile; + int version; + std::string name; // UTF-8 encoded + std::string description; // UTF-8 encoded + std::string depend; + CPlugins::p_type_t type; + + bool fb; + bool rc; + bool lcd; + bool vtxtpid; + int posx, posy, sizex, sizey; + bool showpig; + bool needoffset; + bool hide; + bool operator< (const plugin& a) const + { + return this->filename < a.filename ; + } + }; + + int fb, rc, lcd, pid; + int number_of_plugins; + + std::vector plugin_list; + std::string plugin_dir; + std::string scriptOutput; + + bool parseCfg(plugin *plugin_data); + void scanDir(const char *dir); + bool plugin_exists(const std::string & filename); + int find_plugin(const std::string & filename); + bool pluginfile_exists(const std::string & filename); + CPlugins::p_type_t getPluginType(int type); + public: + + ~CPlugins(); + + void loadPlugins(); + + void setPluginDir(const std::string & dir) { plugin_dir = dir; } + + PluginParam * makeParam(const char * const id, const char * const value, PluginParam * const next); + PluginParam * makeParam(const char * const id, const int value, PluginParam * const next); + + inline int getNumberOfPlugins (void ) const { return plugin_list.size() ; } + inline const char * getName (const int number) const { return plugin_list[number].name.c_str() ; } + inline const char * getPluginFile (const int number) const { return plugin_list[number].pluginfile.c_str(); } + inline const char * getFileName (const int number) const { return plugin_list[number].filename.c_str() ; } + inline const std::string & getDescription (const int number) const { return plugin_list[number].description ; } + inline int getType (const int number) const { return plugin_list[number].type ; } + inline bool isHidden (const int number) const { return plugin_list[number].hide ; } + + void startPlugin(int number,int param); + void start_plugin_by_name(const std::string & filename,int param);// start plugins by "name=" in .cfg + void startScriptPlugin(int number); + + void startPlugin(const char * const filename); // start plugins also by name + bool hasPlugin(CPlugins::p_type_t type); + + const std::string& getScriptOutput() const; + void delScriptOutput(); +}; + +#endif diff --git a/src/gui/rc_lock.cpp b/src/gui/rc_lock.cpp new file mode 100644 index 000000000..901de331f --- /dev/null +++ b/src/gui/rc_lock.cpp @@ -0,0 +1,104 @@ +/* + Neutrino-GUI - DBoxII-Project + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include +#include +#include + +const std::string CRCLock::NO_USER_INPUT = "noUserInput"; + +// -- Menue Handler Interface +// -- Infinite Loop to lock remote control (until release lock key pressed) +// -- 2003-12-01 rasc + +int CRCLock::exec(CMenuTarget* parent, const std::string &actionKey) +{ + if (parent) + parent->hide(); + + bool no_input = (actionKey == NO_USER_INPUT); + if (ShowLocalizedMessage(LOCALE_RCLOCK_TITLE, LOCALE_RCLOCK_LOCKMSG, + CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel, + NEUTRINO_ICON_INFO,450,no_input ? 5 : -1,no_input) == CMessageBox::mbrCancel) + return menu_return::RETURN_EXIT_ALL; + + // -- Lockup Box + lockBox(); + + ShowLocalizedMessage(LOCALE_RCLOCK_TITLE, LOCALE_RCLOCK_UNLOCKMSG, CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO,450, no_input ? 5 : -1); + return menu_return::RETURN_EXIT_ALL; +} + +void CRCLock::lockBox() +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + unsigned long long timeoutEnd; + + // -- Loop until release key pressed + // -- Key sequence: within 5 secs + while (1) { + + timeoutEnd = CRCInput::calcTimeoutEnd(9999999); + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + + if (msg == NeutrinoMessages::UNLOCK_RC) + break; + + if (msg == CRCInput::RC_red) { + timeoutEnd = CRCInput::calcTimeoutEnd(5); + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + + //if (msg == CRCInput::RC_setup) break; + if (msg == (neutrino_msg_t) g_settings.key_unlock) break; + } + + if (msg == CRCInput::RC_timeout) continue; + + // -- Zwen told me: Eating only RC events would be nice + // -- so be it... + + if (msg > CRCInput::RC_MaxRC) { + CNeutrinoApp::getInstance()->handleMsg(msg, data); + } else { + CVFD::getInstance()->showRCLock(); + // Since showRCLock blocks 2secs for each key we eat all + // messages created during this time. Hopefully this ok... + g_RCInput->clearRCMsg(); + } + } + return; +} diff --git a/src/gui/rc_lock.h b/src/gui/rc_lock.h new file mode 100644 index 000000000..4e6ea758c --- /dev/null +++ b/src/gui/rc_lock.h @@ -0,0 +1,47 @@ +/* + Neutrino-GUI - DBoxII-Project + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _remoteLock_ +#define _remoteLock_ + +#include + +#include + + +// +// -- Implements RemoteControl Locking... +// -- ... usefull, if you want to protect your box against unintented zapping +// -- 2003-12-01 rasc +// +class CRCLock: public CMenuTarget +{ + +public: + static const std::string NO_USER_INPUT; + int exec(CMenuTarget* parent, const std::string & actionKey); + void lockBox(); +}; + + +#endif diff --git a/src/gui/ringbuffer.c b/src/gui/ringbuffer.c new file mode 100755 index 000000000..7e12cf76d --- /dev/null +++ b/src/gui/ringbuffer.c @@ -0,0 +1,308 @@ +/* + Copyright (C) 2000 Paul Davis + Copyright (C) 2003 Rohan Drape + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. + This is safe for the case of one read thread and one write thread. +*/ + +#include +#include +#include +#include "ringbuffer.h" + +/* Create a new ringbuffer to hold at least `sz' bytes of data. The + actual buffer size is rounded up to the next power of two. */ + +ringbuffer_t * +ringbuffer_create (int sz) +{ + int power_of_two; + ringbuffer_t *rb; + + rb = malloc (sizeof (ringbuffer_t)); + + for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + + rb->size = 1 << power_of_two; + rb->size_mask = rb->size; + rb->size_mask -= 1; + rb->write_ptr = 0; + rb->read_ptr = 0; + rb->buf = malloc (rb->size); + rb->mlocked = 0; +printf("[ringbuffer] size %d\n", rb->size); + return rb; +} + +/* Free all data associated with the ringbuffer `rb'. */ + +void +ringbuffer_free (ringbuffer_t * rb) +{ + if (rb->mlocked) { + munlock (rb->buf, rb->size); + } + free (rb->buf); + free (rb); +} + +/* Lock the data block of `rb' using the system call 'mlock'. */ + +int +ringbuffer_mlock (ringbuffer_t * rb) +{ + if (mlock (rb->buf, rb->size)) { + return -1; + } + rb->mlocked = 1; + return 0; +} + +/* Reset the read and write pointers to zero. This is not thread + safe. */ + +void +ringbuffer_reset (ringbuffer_t * rb) +{ + rb->read_ptr = 0; + rb->write_ptr = 0; +} + +/* Return the number of bytes available for reading. This is the + number of bytes in front of the read pointer and behind the write + pointer. */ + +size_t +ringbuffer_read_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + return w - r; + } else { + return (w - r + rb->size) & rb->size_mask; + } +} + +/* Return the number of bytes available for writing. This is the + number of bytes in front of the write pointer and behind the read + pointer. */ + +size_t +ringbuffer_write_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + return ((r - w + rb->size) & rb->size_mask) - 1; + } else if (w < r) { + return (r - w) - 1; + } else { + return rb->size - 1; + } +} + +/* The copying data reader. Copy at most `cnt' bytes from `rb' to + `dest'. Returns the actual number of bytes copied. */ + +size_t +ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + if ((free_cnt = ringbuffer_read_space (rb)) == 0) { + return 0; + } + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[rb->read_ptr]), n1); + rb->read_ptr += n1; + rb->read_ptr &= rb->size_mask; + + if (n2) { + memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); + rb->read_ptr += n2; + rb->read_ptr &= rb->size_mask; + } + + return to_read; +} + +/* The copying data writer. Copy at most `cnt' bytes to `rb' from + `src'. Returns the actual number of bytes copied. */ + +size_t +ringbuffer_write (ringbuffer_t * rb, char *src, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + if ((free_cnt = ringbuffer_write_space (rb)) == 0) { + return 0; + } + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->write_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_write; + n2 = 0; + } + + memcpy (&(rb->buf[rb->write_ptr]), src, n1); + rb->write_ptr += n1; + rb->write_ptr &= rb->size_mask; + + if (n2) { + memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); + rb->write_ptr += n2; + rb->write_ptr &= rb->size_mask; + } + + return to_write; +} + +/* Advance the read pointer `cnt' places. */ + +void +ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->read_ptr += cnt; + rb->read_ptr &= rb->size_mask; +} + +/* Advance the write pointer `cnt' places. */ + +void +ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->write_ptr += cnt; + rb->write_ptr &= rb->size_mask; +} + +/* The non-copying data reader. `vec' is an array of two places. Set + the values at `vec' to hold the current readable data at `rb'. If + the readable data is in one segment the second segment has zero + length. */ + +void +ringbuffer_get_read_vector (ringbuffer_t * rb, + ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + free_cnt = w - r; + } else { + free_cnt = (w - r + rb->size) & rb->size_mask; + } + + cnt2 = r + free_cnt; + + if (cnt2 > rb->size) { + + /* Two part vector: the rest of the buffer after the current write + ptr, plus some from the start of the buffer. */ + + vec[0].buf = &(rb->buf[r]); + vec[0].len = rb->size - r; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + + } else { + + /* Single part vector: just the rest of the buffer */ + + vec[0].buf = &(rb->buf[r]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +/* The non-copying data writer. `vec' is an array of two places. Set + the values at `vec' to hold the current writeable data at `rb'. If + the writeable data is in one segment the second segment has zero + length. */ + +void +ringbuffer_get_write_vector (ringbuffer_t * rb, + ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) { + free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; + } else if (w < r) { + free_cnt = (r - w) - 1; + } else { + free_cnt = rb->size - 1; + } + + cnt2 = w + free_cnt; + + if (cnt2 > rb->size) { + + /* Two part vector: the rest of the buffer after the current write + ptr, plus some from the start of the buffer. */ + + vec[0].buf = &(rb->buf[w]); + vec[0].len = rb->size - w; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } else { + vec[0].buf = &(rb->buf[w]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} diff --git a/src/gui/scale.cpp b/src/gui/scale.cpp new file mode 100644 index 000000000..432de0310 --- /dev/null +++ b/src/gui/scale.cpp @@ -0,0 +1,129 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define RED_BAR 40 +#define YELLOW_BAR 70 +#define GREEN_BAR 100 + +#define BAR_BORDER 1 +#define BARW 2 +#define BARWW 2 + +#define ITEMW 4 +#define POINT 2 + +#define RED 0xFF0000 +#define GREEN 0x00FF00 +#define YELLOW 0xFFFF00 + +inline unsigned int make16color(__u32 rgb) +{ + return 0xFF000000 | rgb; +} + +CScale::CScale (int w, int h, int r, int g, int b, bool inv) +{ + double div; +//printf("new SCALE, w %d h %d size %d\n", w, h, sizeof(CScale)); fflush(stdout); + frameBuffer = CFrameBuffer::getInstance (); + width = w; + height = h; + inverse = inv; + + div = (double) 100 / (double) width; + red = (double) r / (double) div / (double) ITEMW; + green = (double) g / (double) div / (double) ITEMW; + yellow = (double) b / (double) div / (double) ITEMW; + percent = 255; +} + +void CScale::paint (int x, int y, int pcr) +{ + int i, j, siglen; + int posx, posy; + int xpos, ypos; + int hcnt = height / ITEMW; + double div; + uint32_t rgb; + + fb_pixel_t color; + int b = 0; + + i = 0; + xpos = x; + ypos = y; +//printf("CScale::paint: old %d new %d x %d y %d\n", percent, pcr, x, y); fflush(stdout); + if (pcr != percent) { + if(percent == 255) percent = 0; + div = (double) 100 / (double) width; + siglen = (double) pcr / (double) div; + posx = xpos; + posy = ypos; + int maxi = siglen / ITEMW; + int total = width / ITEMW; + int step = 255/total; + if (pcr > percent) { + + for (i = 0; (i < red) && (i < maxi); i++) { + step = 255/red; + if(inverse) rgb = GREEN + ((unsigned char)(step*i) << 16); // adding red + else rgb = RED + ((unsigned char)(step*i) << 8); // adding green + color = make16color(rgb); + for(j = 0; j <= hcnt; j++ ) { + frameBuffer->paintBoxRel (posx + i*ITEMW, posy + j*ITEMW, POINT, POINT, color); + } + } +//printf("hcnt %d yellow %d i %d\n", hcnt, yellow, i); fflush(stdout); + for (; (i < yellow) && (i < maxi); i++) { + step = 255/yellow/2; + if(inverse) rgb = YELLOW - (((unsigned char)step*(b++)) << 8); // removing green + else rgb = YELLOW - ((unsigned char)(step*(b++)) << 16); // removing red + color = make16color(rgb); +//printf("YELLOW: or %08X diff %08X result %08X\n", YELLOW, ((unsigned char)(step*(b-1)) << 16), color); + for(j = 0; j <= hcnt; j++ ) { + frameBuffer->paintBoxRel (posx + i*ITEMW, posy + j*ITEMW, POINT, POINT, color); + } + } + for (; (i < green) && (i < maxi); i++) { + step = 255/green; + if(inverse) rgb = YELLOW - ((unsigned char) (step*(b++)) << 8); // removing green + else rgb = YELLOW - ((unsigned char) (step*(b++)) << 16); // removing red + color = make16color(rgb); + for(j = 0; j <= hcnt; j++ ) { + frameBuffer->paintBoxRel (posx + i*ITEMW, posy + j*ITEMW, POINT, POINT, color); + } + } + } + for(i = maxi; i < total; i++) { + for(j = 0; j <= hcnt; j++ ) { + frameBuffer->paintBoxRel (posx + i*ITEMW, posy + j*ITEMW, POINT, POINT, COL_INFOBAR_PLUS_3);//fill passive + } + } + percent = pcr; + } +} + +void CScale::reset () +{ + percent = 255; +} + +void CScale::hide () +{ +} diff --git a/src/gui/scale.h b/src/gui/scale.h new file mode 100644 index 000000000..97b4f5dd4 --- /dev/null +++ b/src/gui/scale.h @@ -0,0 +1,22 @@ +#ifndef __scale_ +#define __scale_ + +#include + +class CScale +{ + private: + CFrameBuffer * frameBuffer; + short width; + short height; + char percent; + short red, green, yellow; + bool inverse; + public: + CScale(int w, int h, int r, int g, int b, bool inv = false); + void paint(int x, int y, int pcr); + void hide(); + void reset(); + int getPercent() { return percent; }; +}; +#endif diff --git a/src/gui/scan.cpp b/src/gui/scan.cpp new file mode 100644 index 000000000..93cb3218c --- /dev/null +++ b/src/gui/scan.cpp @@ -0,0 +1,505 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +extern cVideo * videoDecoder; +extern CFrontend * frontend; + +#define NEUTRINO_SCAN_START_SCRIPT CONFIGDIR "/scan.start" +#define NEUTRINO_SCAN_STOP_SCRIPT CONFIGDIR "/scan.stop" +#define NEUTRINO_SCAN_SETTINGS_FILE CONFIGDIR "/scan.conf" +TP_params TP; + +#define RED_BAR 40 +#define YELLOW_BAR 70 +#define GREEN_BAR 100 +#define BAR_BORDER 2 +#define BAR_WIDTH 150 +#define BAR_HEIGHT 16//(13 + BAR_BORDER*2) + +#define ROUND_RADIUS 9 + +CScanTs::CScanTs() +{ + frameBuffer = CFrameBuffer::getInstance(); + radar = 0; + total = done = 0; + freqready = 0; + + sigscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); + snrscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); + +} + +extern int scan_pids; +#define get_set CNeutrinoApp::getInstance()->getScanSettings() +int CScanTs::exec(CMenuTarget* parent, const std::string & actionKey) +{ + diseqc_t diseqcType = NO_DISEQC; + neutrino_msg_t msg; + neutrino_msg_data_t data; + //bool manual = (get_set.scan_mode == 2); + int scan_mode = get_set.scan_mode; + sat_iterator_t sit; + bool scan_all = actionKey == "all"; + bool test = actionKey == "test"; + bool manual = (actionKey == "manual") || test; + CZapitClient::ScanSatelliteList satList; + CZapitClient::commandSetScanSatelliteList sat; + int _scan_pids = scan_pids; + + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + width = w_max(550, 0); + height = h_max(hheight + (10 * mheight), 0); //9 lines + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - height) / 2; + xpos_radar = x + 470; + ypos_radar = y + hheight + (mheight >> 1); + xpos1 = x + 10; + + if(scan_all) + scan_mode |= 0xFF00; + + sigscale->reset(); + snrscale->reset(); + + if (!frameBuffer->getActive()) + return menu_return::RETURN_EXIT_ALL; + + g_Zapit->stopPlayBack(); + frameBuffer->paintBackground(); + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/scan.jpg"); + g_Sectionsd->setPauseScanning(true); + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8); + +//printf("[neutrino] scan_mode %d TP_freq %s TP_rate %s TP_fec %d TP_pol %d\n", get_set.scan_mode, get_set.TP_freq, get_set.TP_rate, get_set.TP_fec, get_set.TP_pol); + + if(manual) { + scan_pids = true; + TP.scan_mode = get_set.scan_mode; + TP.feparams.frequency = atoi(get_set.TP_freq); + if(g_info.delivery_system == DVB_S) { + TP.feparams.u.qpsk.symbol_rate = atoi(get_set.TP_rate); + TP.feparams.u.qpsk.fec_inner = (fe_code_rate_t) get_set.TP_fec; + TP.polarization = get_set.TP_pol; + } else { + TP.feparams.u.qam.symbol_rate = atoi(get_set.TP_rate); + TP.feparams.u.qam.fec_inner = (fe_code_rate_t)get_set.TP_fec; + TP.feparams.u.qam.modulation = (fe_modulation_t) get_set.TP_mod; + } + //printf("[neutrino] freq %d rate %d fec %d pol %d\n", TP.feparams.frequency, TP.feparams.u.qpsk.symbol_rate, TP.feparams.u.qpsk.fec_inner, TP.polarization); + } + satList.clear(); + if(manual || !scan_all) { + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + if(!strcmp(sit->second.name.c_str(),get_set.satNameNoDiseqc)) { + sat.position = sit->first; + strncpy(sat.satName, get_set.satNameNoDiseqc, 50); + satList.push_back(sat); + break; + } + } + } else { + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + if(sit->second.use_in_scan) { + sat.position = sit->first; + strncpy(sat.satName, sit->second.name.c_str(), 50); + satList.push_back(sat); + } + } + } + success = false; + + if(!manual) { + g_RCInput->close_click(); + if (system(NEUTRINO_SCAN_START_SCRIPT) != 0) + perror(NEUTRINO_SCAN_START_SCRIPT " failed"); + } + + /* send diseqc type to zapit */ + diseqcType = (diseqc_t) CNeutrinoApp::getInstance()->getScanSettings().diseqcMode; + g_Zapit->setDiseqcType(diseqcType); + + /* send diseqc repeat to zapit */ + g_Zapit->setDiseqcRepeat( CNeutrinoApp::getInstance()->getScanSettings().diseqcRepeat); + g_Zapit->setScanBouquetMode( (CZapitClient::bouquetMode)CNeutrinoApp::getInstance()->getScanSettings().bouquetMode); + + /* send satellite list to zapit */ + g_Zapit->setScanSatelliteList( satList); + + /* send scantype to zapit */ + g_Zapit->setScanType((CZapitClient::scanType) CNeutrinoApp::getInstance()->getScanSettings().scanType ); + + paint(test); + /* go */ + if(test) { + int w = x + width - xpos2; + char buffer[128]; + char * f, *s, *m; + if(frontend->getInfo()->type == FE_QPSK) { + frontend->getDelSys(get_set.TP_fec, dvbs_get_modulation((fe_code_rate_t)get_set.TP_fec), f, s, m); + sprintf(buffer, "%u %c %d %s %s %s", atoi(get_set.TP_freq)/1000, get_set.TP_pol == 0 ? 'H' : 'V', atoi(get_set.TP_rate)/1000, f, s, m); + } else if(frontend->getInfo()->type == FE_QAM) { + frontend->getDelSys(get_set.TP_fec, get_set.TP_mod, f, s, m); + sprintf(buffer, "%u %d %s %s %s", atoi(get_set.TP_freq)/1000, atoi(get_set.TP_rate)/1000, f, s, m); + } + paintLine(xpos2, ypos_cur_satellite, w - 95, get_set.satNameNoDiseqc); + paintLine(xpos2, ypos_frequency, w, buffer); + success = g_Zapit->tune_TP(TP); + } else if(manual) + success = g_Zapit->scan_TP(TP); + else + success = g_Zapit->startScan(scan_mode); + + //paint(); + + /* poll for messages */ + istheend = !success; + while (!istheend) { + paintRadar(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd_MS( 250 ); + + do { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + if (test && (msg <= CRCInput::RC_MaxRC)) { + istheend = true; + msg = CRCInput::RC_timeout; + } + else if(msg == CRCInput::RC_home) { + if(manual && get_set.scan_mode) + continue; + if (ShowLocalizedMessage(LOCALE_SCANTS_ABORT_HEADER, LOCALE_SCANTS_ABORT_BODY, CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) { + g_Zapit->stopScan(); + } + } + else + msg = handleMsg(msg, data); + } + while (!(msg == CRCInput::RC_timeout)); + showSNR(); // FIXME commented until scan slowdown will be solved + } + + if(!manual) { + if (system(NEUTRINO_SCAN_STOP_SCRIPT) != 0) + perror(NEUTRINO_SCAN_STOP_SCRIPT " failed"); + g_RCInput->open_click(); + } + if(!test) { + //ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, success ? LOCALE_SCANTS_FINISHED : LOCALE_SCANTS_FAILED, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + + const char * text = g_Locale->getText(success ? LOCALE_SCANTS_FINISHED : LOCALE_SCANTS_FAILED); + //paintLine(xpos2, ypos_frequency, xpos_frequency, text); + frameBuffer->paintBoxRel(x, y, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(xpos1, y + hheight, width, text, COL_MENUHEAD, 0, true); // UTF-8 + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(0xFFFF); + do { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + if ( msg <= CRCInput::RC_MaxRC ) + msg = CRCInput::RC_timeout; + else + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } while (!(msg == CRCInput::RC_timeout)); + } + + hide(); + + scan_pids = _scan_pids; + videoDecoder->StopPicture(); + frameBuffer->ClearFrameBuffer(); + g_Sectionsd->setPauseScanning(false); + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + + return menu_return::RETURN_REPAINT; +} + +int CScanTs::handleMsg(neutrino_msg_t msg, neutrino_msg_data_t data) +{ + int w = x + width - xpos2; +//printf("CScanTs::handleMsg: x %d xpos2 %d width %d w %d\n", x, xpos2, width, w); + char buffer[128]; + char str[256]; + switch (msg) { + case NeutrinoMessages::EVT_SCAN_SATELLITE: + paintLine(xpos2, ypos_cur_satellite, w - 95, (char *)data); + break; + + case NeutrinoMessages::EVT_SCAN_NUM_TRANSPONDERS: + sprintf(buffer, "%d", data); + paintLine(xpos2, ypos_transponder, w - 95, buffer); + total = data; + snprintf(str, 255, "scan: %d/%d", done, total); + CVFD::getInstance()->showMenuText(0, str, -1, true); + break; + + case NeutrinoMessages::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS: + if (total == 0) data = 0; + done = data; + sprintf(buffer, "%d/%d", done, total); + paintLine(xpos2, ypos_transponder, w - 95, buffer); + snprintf(str, 255, "scan %d/%d", done, total); + CVFD::getInstance()->showMenuText(0, str, -1, true); + break; + + case NeutrinoMessages::EVT_SCAN_REPORT_FREQUENCY: + freqready = 1; + sprintf(buffer, "%u", data); + xpos_frequency = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(buffer, true); + paintLine(xpos2, ypos_frequency, xpos_frequency, buffer); + break; + + case NeutrinoMessages::EVT_SCAN_REPORT_FREQUENCYP: + { + int pol = data & 0xFF; + int fec = (data >> 8) & 0xFF; + int rate = data >> 16; + char * f, *s, *m; + frontend->getDelSys(fec, (fe_modulation_t)0, f, s, m); // FIXME + sprintf(buffer, " %c %d %s %s %s", pol == 0 ? 'H' : 'V', rate, f, s, m); + //(pol == 0) ? sprintf(buffer, "-H") : sprintf(buffer, "-V"); + paintLine(xpos2 + xpos_frequency, ypos_frequency, w - xpos_frequency - 80, buffer); + } + break; + + case NeutrinoMessages::EVT_SCAN_PROVIDER: + paintLine(xpos2, ypos_provider, w, (char*)data); // UTF-8 + break; + + case NeutrinoMessages::EVT_SCAN_SERVICENAME: + paintLine(xpos2, ypos_channel, w, (char *)data); // UTF-8 + break; + + case NeutrinoMessages::EVT_SCAN_NUM_CHANNELS: + sprintf(buffer, " = %d", data); + paintLine(xpos1 + 3 * 72, ypos_service_numbers + mheight, width - 3 * 72 - 10, buffer); + break; + + case NeutrinoMessages::EVT_SCAN_FOUND_TV_CHAN: + sprintf(buffer, "%d", data); + paintLine(xpos1, ypos_service_numbers + mheight, 72, buffer); + break; + + case NeutrinoMessages::EVT_SCAN_FOUND_RADIO_CHAN: + sprintf(buffer, "%d", data); + paintLine(xpos1 + 72, ypos_service_numbers + mheight, 72, buffer); + break; + + case NeutrinoMessages::EVT_SCAN_FOUND_DATA_CHAN: + sprintf(buffer, "%d", data); + paintLine(xpos1 + 2 * 72, ypos_service_numbers + mheight, 72, buffer); + break; + + case NeutrinoMessages::EVT_SCAN_COMPLETE: + case NeutrinoMessages::EVT_SCAN_FAILED: + success = (msg == NeutrinoMessages::EVT_SCAN_COMPLETE); + istheend = true; + msg = CRCInput::RC_timeout; + break; + case CRCInput::RC_plus: + case CRCInput::RC_minus: + case CRCInput::RC_left: + case CRCInput::RC_right: + CNeutrinoApp::getInstance()->setVolume(msg, true, true); + break; + default: + if ((msg >= CRCInput::RC_WithData) && (msg < CRCInput::RC_WithData + 0x10000000)) + delete (unsigned char*) data; + break; + } + return msg; +} + +void CScanTs::paintRadar(void) +{ + char filename[30]; + + sprintf(filename, "radar%d.raw", radar); + radar = (radar + 1) % 10; + frameBuffer->paintIcon8(filename, xpos_radar, ypos_radar, 17); +} + +void CScanTs::hide() +{ + //frameBuffer->loadPal("radiomode.pal", 18, COL_MAXFREE); + //frameBuffer->paintBackgroundBoxRel(0, 0, 720, 576); + frameBuffer->paintBackground(); + freqready = 0; +} + +void CScanTs::paintLineLocale(int x, int * y, int width, const neutrino_locale_t l) +{ + frameBuffer->paintBoxRel(x, *y, width, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, *y + mheight, width, g_Locale->getText(l), COL_MENUCONTENTINACTIVE, 0, true); // UTF-8 + *y += mheight; +} + +void CScanTs::paintLine(int x, int y, int w, const char * const txt) +{ +//printf("CScanTs::paintLine x %d y %d w %d width %d xpos2 %d: %s\n", x, y, w, width, xpos2, txt); + frameBuffer->paintBoxRel(x, y, w, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, y + mheight, w, txt, COL_MENUCONTENT, 0, true); // UTF-8 +} + +void CScanTs::paint(bool fortest) +{ + int ypos; + + ypos = y; + + //frameBuffer->paintBoxRel(x, ypos, width, hheight, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x, ypos, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(xpos1, ypos + hheight, width, fortest ? g_Locale->getText(LOCALE_SCANTS_TEST) : g_Locale->getText(LOCALE_SCANTS_HEAD), COL_MENUHEAD, 0, true); // UTF-8 + //frameBuffer->paintBoxRel(x, ypos + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x, ypos + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + frameBuffer->loadPal("radar.pal", 17, 37); + + ypos = y + hheight + (mheight >> 1); + + ypos_cur_satellite = ypos; + + if (g_info.delivery_system == DVB_S) + { //sat + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_ACTSATELLITE); + xpos2 = xpos1 + 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(g_Locale->getText(LOCALE_SCANTS_ACTSATELLITE), true); // UTF-8 + } + if (g_info.delivery_system == DVB_C) + { //cable + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_ACTCABLE); + xpos2 = xpos1 + 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(g_Locale->getText(LOCALE_SCANTS_ACTCABLE), true); // UTF-8 + } + + ypos_transponder = ypos; + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_TRANSPONDERS); + xpos2 = greater_xpos(xpos2, LOCALE_SCANTS_TRANSPONDERS); + + ypos_frequency = ypos; + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_FREQDATA); + xpos2 = greater_xpos(xpos2, LOCALE_SCANTS_FREQDATA); + + ypos += mheight >> 1; // 1/2 blank line + + ypos_provider = ypos; + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_PROVIDER); + xpos2 = greater_xpos(xpos2, LOCALE_SCANTS_PROVIDER); + + ypos_channel = ypos; + paintLineLocale(xpos1, &ypos, width - xpos1, LOCALE_SCANTS_CHANNEL); + xpos2 = greater_xpos(xpos2, LOCALE_SCANTS_CHANNEL); + + ypos += mheight >> 1; // 1/2 blank line + + ypos_service_numbers = ypos; paintLineLocale(xpos1 , &ypos, 72 , LOCALE_SCANTS_NUMBEROFTVSERVICES ); + ypos = ypos_service_numbers; paintLineLocale(xpos1 + 72, &ypos, 72 , LOCALE_SCANTS_NUMBEROFRADIOSERVICES); + ypos = ypos_service_numbers; paintLineLocale(xpos1 + 2 * 72, &ypos, 72 , LOCALE_SCANTS_NUMBEROFDATASERVICES ); + ypos = ypos_service_numbers; paintLineLocale(xpos1 + 3 * 72, &ypos, width - 3 * 72 - 10, LOCALE_SCANTS_NUMBEROFTOTALSERVICES); +} + +int CScanTs::greater_xpos(int xpos, const neutrino_locale_t txt) +{ + int txt_xpos = xpos1 + 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(g_Locale->getText(txt), true); // UTF-8 + if (txt_xpos > xpos) + return txt_xpos; + else + return xpos; +} + +void CScanTs::showSNR () +{ + char percent[10]; + int barwidth = 150; + uint16_t ssig, ssnr; + int sig, snr; + int posx, posy; + int sw; + + ssig = frontend->getSignalStrength(); + ssnr = frontend->getSignalNoiseRatio(); + + snr = (ssnr & 0xFFFF) * 100 / 65535; + sig = (ssig & 0xFFFF) * 100 / 65535; + + posy = y + height - mheight - 5; + + if(sigscale->getPercent() != sig) { + posx = x + 20; + sprintf(percent, "%d%% SIG", sig); + sw = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth ("100% SIG"); + + sigscale->paint(posx - 1, posy+2, sig); + + posx = posx + barwidth + 3; + sw = x + 247 - posx; + frameBuffer->paintBoxRel(posx, posy - 2, sw, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString (posx+2, posy + mheight, sw, percent, COL_MENUCONTENT); + } + if(snrscale->getPercent() != snr) { + posx = x + 20 + 260; + sprintf(percent, "%d%% SNR", snr); + sw = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth ("100% SNR"); + snrscale->paint(posx - 1, posy+2, snr); + + posx = posx + barwidth + 3; + sw = x + width - posx; + frameBuffer->paintBoxRel(posx, posy - 2, sw, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString (posx+2, posy + mheight, sw, percent, COL_MENUCONTENT); + } +} diff --git a/src/gui/scan.h b/src/gui/scan.h new file mode 100644 index 000000000..05a90d8b7 --- /dev/null +++ b/src/gui/scan.h @@ -0,0 +1,83 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __scants__ +#define __scants__ + +#include +#include +#include +#include +#include + + +class CScanTs : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight, mheight; // head/menu font height + int xpos1; //x position for first column + int xpos2; //x position for second column + int radar; + int xpos_radar; + int ypos_radar; + int ypos_cur_satellite; + int ypos_transponder; + int ypos_frequency; + int xpos_frequency; + int ypos_provider; + int ypos_channel; + int ypos_service_numbers; + bool success; + bool istheend; + uint32_t total; + uint32_t done; + CScale * snrscale, * sigscale; + + void paint(bool fortest = false); + void paintLineLocale(int x, int * y, int width, const neutrino_locale_t l); + void paintLine(int x, int y, int width, const char * const txt); + void paintRadar(void); + int handleMsg(neutrino_msg_t msg, neutrino_msg_data_t data); + int greater_xpos(int xpos, const neutrino_locale_t txt); + bool freqready; + void showSNR(); + + public: + CScanTs(); + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); +}; +#endif diff --git a/src/gui/screensetup.cpp b/src/gui/screensetup.cpp new file mode 100644 index 000000000..5c1e7679e --- /dev/null +++ b/src/gui/screensetup.cpp @@ -0,0 +1,305 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +int x=15*5; +int y=15*25; +int BoxHeight=15*4; +int BoxWidth=15*23; + +CScreenSetup::CScreenSetup() +{ + frameBuffer = CFrameBuffer::getInstance(); +} + +int CScreenSetup::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + if (parent) + { + parent->hide(); + } + + x_coord[0] = g_settings.screen_StartX; + x_coord[1] = g_settings.screen_EndX; + y_coord[0] = g_settings.screen_StartY; + y_coord[1] = g_settings.screen_EndY; + + paint(); + + selected = 0; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + switch ( msg ) + { + case CRCInput::RC_ok: + // abspeichern + g_settings.screen_StartX = x_coord[0]; + g_settings.screen_EndX = x_coord[1]; + g_settings.screen_StartY = y_coord[0]; + g_settings.screen_EndY = y_coord[1]; + loop = false; + break; + + case CRCInput::RC_home: + if ( ( ( g_settings.screen_StartX != x_coord[0] ) || + ( g_settings.screen_EndX != x_coord[1] ) || + ( g_settings.screen_StartY != y_coord[0] ) || + ( g_settings.screen_EndY != y_coord[1] ) ) && + (ShowLocalizedMessage(LOCALE_VIDEOMENU_SCREENSETUP, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + break; + + case CRCInput::RC_timeout: + loop = false; + break; + + case CRCInput::RC_red: + case CRCInput::RC_green: + { + selected = ( msg == CRCInput::RC_green ) ? 1 : 0 ; + + frameBuffer->paintBoxRel(x,y, BoxWidth,BoxHeight/2, (selected==0)? COL_MENUCONTENTSELECTED_PLUS_0:COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,y+BoxHeight/2, BoxWidth,BoxHeight/2, (selected==1)? COL_MENUCONTENTSELECTED_PLUS_0:COL_MENUCONTENT_PLUS_0); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30,y+BoxHeight/2, BoxWidth, g_Locale->getText(LOCALE_SCREENSETUP_UPPERLEFT ), (selected == 0)?COL_MENUCONTENTSELECTED:COL_MENUCONTENT, 0, true); // UTF-8 + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30,y+BoxHeight, BoxWidth, g_Locale->getText(LOCALE_SCREENSETUP_LOWERRIGHT), (selected == 1)?COL_MENUCONTENTSELECTED:COL_MENUCONTENT, 0, true); // UTF-8 + + paintIcons(); + break; + } + case CRCInput::RC_up: + { + y_coord[selected]--; + + int min = ( selected == 0 ) ? 0 : 400; + if ( y_coord[selected] < min ) + y_coord[selected] = min ; + else + paintBorder( selected ); + break; + } + case CRCInput::RC_down: + { + y_coord[selected]++; + + //int max = ( selected == 0 ) ? 200 : 575; + int max = ( selected == 0 ) ? 200 : frameBuffer->getScreenHeight(true)-1; +printf("selected %d y %d max %d\n", selected, y_coord[selected], max); + if ( y_coord[selected] > max ) + y_coord[selected] = max ; + else + paintBorder( selected ); + break; + } + case CRCInput::RC_left: + { + x_coord[selected]--; + + int min = ( selected == 0 ) ? 0 : 400; + if ( x_coord[selected] < min ) + x_coord[selected] = min ; + else + paintBorder( selected ); + break; + } + case CRCInput::RC_right: + { + x_coord[selected]++; + + //int max = ( selected == 0 ) ? 200 : 719; + int max = ( selected == 0 ) ? 200 : frameBuffer->getScreenWidth(true)-1; + if ( y_coord[selected] > max ) + y_coord[selected] = max ; + else + paintBorder( selected ); + break; + } + case CRCInput::RC_favorites: + case CRCInput::RC_sat: + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + + } + + hide(); + return res; +} + +void CScreenSetup::hide() +{ + int w = (int) frameBuffer->getScreenWidth(true); + int h = (int) frameBuffer->getScreenHeight(true); + frameBuffer->paintBackgroundBox(0, 0, w, h); +} + +void CScreenSetup::paintBorder( int selected ) +{ + if ( selected == 0 ) + paintBorderUL(); + else + paintBorderLR(); + + paintCoords(); +} + +void CScreenSetup::paintIcons() +{ + frameBuffer->paintIcon("rot.raw", x+6, y+8); + frameBuffer->paintIcon("gruen.raw", x+6, y+36 ); +} + +void CScreenSetup::paintBorderUL() +{ + frameBuffer->paintIcon( "border_ul.raw", x_coord[0], y_coord[0] ); +} + +void CScreenSetup::paintBorderLR() +{ + frameBuffer->paintIcon("border_lr.raw", x_coord[1]- 96, y_coord[1]- 96 ); +} + +void CScreenSetup::paintCoords() +{ + int w = 15*9; + int h = 15*6; + + //int x=15*19; + //int y=15*16; + int x = (frameBuffer->getScreenWidth(true) - w) / 2; + int y = (frameBuffer->getScreenHeight(true) - h) / 2; + + frameBuffer->paintBoxRel(x,y, 15*9,15*6, COL_MENUCONTENT_PLUS_0); + + char xpos[30]; + char ypos[30]; + char xepos[30]; + char yepos[30]; + + sprintf((char*) &xpos, "SX: %d",x_coord[0] ); + sprintf((char*) &ypos, "SY: %d", y_coord[0] ); + sprintf((char*) &xepos, "EX: %d", x_coord[1] ); + sprintf((char*) &yepos, "EY: %d", y_coord[1] ); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,y+30, 200, xpos, COL_MENUCONTENT); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,y+50, 200, ypos, COL_MENUCONTENT); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,y+70, 200, xepos, COL_MENUCONTENT); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,y+90, 200, yepos, COL_MENUCONTENT); +} + +inline unsigned int make16color(__u32 rgb) +{ + return 0xFF000000 | rgb; +} + +void CScreenSetup::paint() +{ + if (!frameBuffer->getActive()) + return; + + int w = (int) frameBuffer->getScreenWidth(true); + int h = (int) frameBuffer->getScreenHeight(true); + + frameBuffer->paintBox(0,0, w, h, make16color(0xA0A0A0)); + + for(int count = 0; count < h; count += 15) + frameBuffer->paintHLine(0, w-1, count, make16color(0x505050) ); + + for(int count = 0; count < w; count += 15) + frameBuffer->paintVLine(count, 0, h-1, make16color(0x505050) ); + + //frameBuffer->paintBox(0,0, 15*15,15*15, make16color(0xA0A0A0)); + //frameBuffer->paintBox(32*15+1, 23*15+1, 719,575, make16color(0xA0A0A0)); + frameBuffer->paintBox(0, 0, w/3, h/3, make16color(0xA0A0A0)); + frameBuffer->paintBox(w-w/3, h-h/3, w-1, h-1, make16color(0xA0A0A0)); +//new + //frameBuffer->paintBoxRel(225, 89, 496, 3, COL_MENUCONTENT_PLUS_0); //upper letterbox marker + //frameBuffer->paintBoxRel(0, 495, 481, 3, COL_MENUCONTENT_PLUS_0); //lower letterbox marker + + frameBuffer->paintBoxRel(x, y, BoxWidth, BoxHeight/2, COL_MENUCONTENTSELECTED_PLUS_0); //upper selected box + frameBuffer->paintBoxRel(x, y+BoxHeight/2, BoxWidth, BoxHeight/2, COL_MENUCONTENT_PLUS_0); //lower selected box + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30, y+BoxHeight/2, BoxWidth, + g_Locale->getText(LOCALE_SCREENSETUP_UPPERLEFT ), COL_MENUCONTENTSELECTED , 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30, y+BoxHeight, BoxWidth, + g_Locale->getText(LOCALE_SCREENSETUP_LOWERRIGHT), COL_MENUCONTENT, 0, true); // UTF-8 +//new end +#if 0 // old + int x=15*5; + int y=15*24; + frameBuffer->paintBoxRel(x,y, 15*23,15*4, COL_MENUCONTENT_PLUS_0); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30,y+29, 15*23, g_Locale->getText(LOCALE_SCREENSETUP_UPPERLEFT ), COL_MENUHEAD , 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+30,y+49, 15*23, g_Locale->getText(LOCALE_SCREENSETUP_LOWERRIGHT), COL_MENUCONTENT, 0, true); // UTF-8 +#endif + + paintIcons(); + paintBorderUL(); + paintBorderLR(); + paintCoords(); +} diff --git a/src/gui/screensetup.h b/src/gui/screensetup.h new file mode 100644 index 000000000..6ab3c90a3 --- /dev/null +++ b/src/gui/screensetup.h @@ -0,0 +1,66 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __screensetup__ +#define __screensetup__ + +#include + +#include + +#include + + +class CScreenSetup : public CMenuTarget +{ + private: + CFrameBuffer * frameBuffer; + int selected; + int x_coord[2]; + int y_coord[2]; + + void paint(); + void paintBorderUL(); + void paintBorderLR(); + void paintCoords(); + void paintBorder(int selected); + void paintIcons(); + + public: + CScreenSetup(); + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/gui/sleeptimer.cpp b/src/gui/sleeptimer.cpp new file mode 100644 index 000000000..749158746 --- /dev/null +++ b/src/gui/sleeptimer.cpp @@ -0,0 +1,94 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include + +#include +#include + +extern CRemoteControl * g_RemoteControl; /* neutrino.cpp */ + +#include + +int CSleepTimerWidget::exec(CMenuTarget* parent, const std::string &) +{ + int res = menu_return::RETURN_EXIT_ALL; + int shutdown_min = 0; + char value[16]; + CStringInput *inbox; + + if (parent) + parent->hide(); + + shutdown_min = g_Timerd->getSleepTimerRemaining(); // remaining shutdown time? + sprintf(value,"%03d", shutdown_min); + CSectionsdClient::CurrentNextInfo info_CurrentNext; + g_InfoViewer->getEPG(g_RemoteControl->current_channel_id, info_CurrentNext); + if ( info_CurrentNext.flags & CSectionsdClient::epgflags::has_current) { + time_t jetzt=time(NULL); + int current_epg_zeit_dauer_rest = (info_CurrentNext.current_zeit.dauer+150 - (jetzt - info_CurrentNext.current_zeit.startzeit ))/60 ; + if(shutdown_min == 0 && current_epg_zeit_dauer_rest > 0 && current_epg_zeit_dauer_rest < 1000) + { + sprintf(value,"%03d",current_epg_zeit_dauer_rest); + } + } + + inbox = new CStringInput(LOCALE_SLEEPTIMERBOX_TITLE, value, 3, LOCALE_SLEEPTIMERBOX_HINT1, LOCALE_SLEEPTIMERBOX_HINT2, "0123456789 "); + inbox->exec (NULL, ""); + inbox->hide (); + + delete inbox; + + int new_val = atoi(value); + if(shutdown_min != new_val) { + shutdown_min = new_val; + printf("sleeptimer min: %d\n",shutdown_min); + if (shutdown_min == 0) // if set to zero remove existing sleeptimer + { + if(g_Timerd->getSleeptimerID() > 0) { + g_Timerd->removeTimerEvent(g_Timerd->getSleeptimerID()); + } + } + else // set the sleeptimer to actual time + shutdown mins and announce 1 min before + g_Timerd->setSleeptimer(time(NULL) + ((shutdown_min -1) * 60),time(NULL) + shutdown_min * 60,0); + } + return res; +} diff --git a/src/gui/sleeptimer.h b/src/gui/sleeptimer.h new file mode 100644 index 000000000..8215c34d2 --- /dev/null +++ b/src/gui/sleeptimer.h @@ -0,0 +1,35 @@ +/* + Neutrino-GUI - DBoxII-Project + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __sleeptimer__ +#define __sleeptimer__ + +#include + +class CSleepTimerWidget: public CMenuTarget +{ + public: + int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/streaminfo2.cpp b/src/gui/streaminfo2.cpp new file mode 100644 index 000000000..1bde46205 --- /dev/null +++ b/src/gui/streaminfo2.cpp @@ -0,0 +1,807 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define SCREEN_X 720 +#define SCREEN_Y 572 + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern CFrontend * frontend; +extern cVideo * videoDecoder; +extern cAudio * audioDecoder; + +extern CRemoteControl *g_RemoteControl; /* neutrino.cpp */ +extern CZapitClient::SatelliteList satList; + +#if 0 +extern CPipSetup * g_Pip0; +#endif +CStreamInfo2::CStreamInfo2 () +{ + frameBuffer = CFrameBuffer::getInstance (); + + font_head = SNeutrinoSettings::FONT_TYPE_MENU_TITLE; + font_info = SNeutrinoSettings::FONT_TYPE_MENU; + font_small = SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL; + + hheight = g_Font[font_head]->getHeight (); + iheight = g_Font[font_info]->getHeight (); + sheight = g_Font[font_small]->getHeight (); + + //max_height = SCREEN_Y - 1; + //max_width = SCREEN_X - 1; + + max_width = frameBuffer->getScreenWidth(true); + max_height = frameBuffer->getScreenHeight(true); + + width = frameBuffer->getScreenWidth(); + height = frameBuffer->getScreenHeight(); + x = frameBuffer->getScreenX(); + y = frameBuffer->getScreenY(); + + //x = (((g_settings.screen_EndX - g_settings.screen_StartX) - width) / 2) + g_settings.screen_StartX; + //y = (((g_settings.screen_EndY - g_settings.screen_StartY) - height) / 2) + g_settings.screen_StartY; + + sigBox_pos = 0; + paint_mode = 0; + + b_total = 0; + bit_s = 0; + abit_s = 0; + + signal.max_sig = 0; + signal.max_snr = 0; + signal.max_ber = 0; + + signal.min_sig = 0; + signal.min_snr = 0; + signal.min_ber = 0; + + rate.short_average = 0; + rate.max_short_average = 0; + rate.min_short_average = 0; + +} + +CStreamInfo2::~CStreamInfo2 () +{ + videoDecoder->Pig(-1, -1, -1, -1); +} + +int CStreamInfo2::exec() +{ + paint(paint_mode); + doSignalStrengthLoop(); + hide(); + + return menu_return::RETURN_EXIT_ALL; +} + +int CStreamInfo2::exec (CMenuTarget * parent, const std::string &) +{ + + if (parent) + parent->hide (); + + paint (paint_mode); + doSignalStrengthLoop (); + hide (); + return menu_return::RETURN_EXIT_ALL; +} + +int CStreamInfo2::doSignalStrengthLoop () +{ +#define RED_BAR 40 +#define YELLOW_BAR 70 +#define GREEN_BAR 100 +#define BAR_WIDTH 150 +#define BAR_HEIGHT 12 + + sigscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); + snrscale = new CScale(BAR_WIDTH, BAR_HEIGHT, RED_BAR, GREEN_BAR, YELLOW_BAR); + + neutrino_msg_t msg; + unsigned long long maxb, minb, lastb, tmp_rate; + int cnt = 0,i=0; + uint16_t ssig, ssnr; + uint32_t ber; + char tmp_str[150]; + int offset_tmp = 0; + int offset = g_Font[font_info]->getRenderWidth(g_Locale->getText (LOCALE_STREAMINFO_BITRATE)); + int sw = g_Font[font_info]->getRenderWidth ("99999.999"); + int mm = g_Font[font_info]->getRenderWidth ("Max");//max min lenght + maxb = minb = lastb = 0; + ts_setup (); + while (1) { + neutrino_msg_data_t data; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd_MS (100); + g_RCInput->getMsgAbsoluteTimeout (&msg, &data, &timeoutEnd); + + ssig = frontend->getSignalStrength(); + ssnr = frontend->getSignalNoiseRatio(); + ber = frontend->getBitErrorRate(); + + signal.sig = ssig & 0xFFFF; + signal.snr = ssnr & 0xFFFF; + signal.ber = ber; + + int ret = update_rate (); + if (paint_mode == 0) { + char currate[150]; + if (cnt < 12) + cnt++; + int dheight = g_Font[font_info]->getHeight (); + int dx1 = x + 10; + int dy = y+ height - dheight - 5; + if (ret && (lastb != bit_s)) { + lastb = bit_s; + + if (maxb < bit_s) + rate.max_short_average = maxb = bit_s; + if ((cnt > 10) && ((minb == 0) || (minb > bit_s))) + rate.min_short_average = minb = bit_s; + + for(i = 0; i < 3; i++){ + switch (i) { + case 0: + tmp_rate = bit_s; + sprintf(tmp_str, "%s", g_Locale->getText(LOCALE_STREAMINFO_BITRATE)); + offset_tmp = 0; + break; + case 1: + tmp_rate = minb; + sprintf(tmp_str, "%s", "Min"); + offset_tmp = offset+5; + break; + case 2: + tmp_rate = maxb; + sprintf(tmp_str, "%s", "Max"); + offset_tmp = offset+5+mm; + break; + } + g_Font[font_info]->RenderString (dx1+offset_tmp+((sw)*i), yypos+(dheight*4), offset, tmp_str, COL_MENUCONTENTDARK, 0, true); + sprintf(currate, "%5llu.%03llu", tmp_rate / 1000ULL, tmp_rate % 1000ULL); + frameBuffer->paintBoxRel (dx1+offset+5+((sw+mm)*i), yypos+(dheight*3), sw, dheight, COL_MENUHEAD_PLUS_0); + g_Font[font_info]->RenderString (dx1+offset+10+((sw+mm)*i), yypos+(dheight*4), sw - 10, currate, COL_MENUCONTENTDARK); + } + } + if(snrscale && sigscale) + showSNR (); + } + rate.short_average = abit_s; + if (signal.max_ber < signal.ber) { + signal.max_ber = signal.ber; + } + if (signal.max_sig < signal.sig) { + signal.max_sig = signal.sig; + } + if (signal.max_snr < signal.snr) { + signal.max_snr = signal.snr; + } + + if ((signal.min_ber == 0) || (signal.min_ber > signal.ber)) { + signal.min_ber = signal.ber; + } + if ((signal.min_sig == 0) || (signal.min_sig > signal.sig)) { + signal.min_sig = signal.sig; + } + if ((signal.min_snr == 0) || (signal.min_snr > signal.snr)) { + signal.min_snr = signal.snr; + } + + paint_signal_fe(rate, signal); + signal.old_sig = signal.sig; + signal.old_snr = signal.snr; + signal.old_ber = signal.ber; + + // switch paint mode + if (msg == CRCInput::RC_red || msg == CRCInput::RC_blue || msg == CRCInput::RC_green || msg == CRCInput::RC_yellow) { + hide (); + if(sigscale) + sigscale->reset(); + if(snrscale) + snrscale->reset(); + paint_mode = ++paint_mode % 2; + paint (paint_mode); + continue; + } + // -- any key --> abort + if (msg <= CRCInput::RC_MaxRC) { + break; + } + // -- push other events + if (msg > CRCInput::RC_MaxRC && msg != CRCInput::RC_timeout) { + CNeutrinoApp::getInstance ()->handleMsg (msg, data); + } + } + if(sigscale){ + delete sigscale; + sigscale = NULL; + } + if(snrscale){ + delete snrscale; + snrscale = NULL; + } + ts_close (); + return msg; +} + +void CStreamInfo2::hide () +{ + videoDecoder->Pig(-1, -1, -1, -1); + frameBuffer->paintBackgroundBoxRel (0, 0, max_width, max_height); +} + +void CStreamInfo2::paint_pig (int x, int y, int w, int h) +{ + frameBuffer->paintBackgroundBoxRel (x, y, w, h); +printf("CStreamInfo2::paint_pig x %d y %d w %d h %d\n", x, y, w, h); + videoDecoder->Pig(x, y, w, h, frameBuffer->getScreenWidth(true), frameBuffer->getScreenHeight(true)); +} + +void CStreamInfo2::paint_signal_fe_box(int _x, int _y, int w, int h) +{ + int y1, y2; + int xd = w/4; + + g_Font[font_small]->RenderString(_x, _y+iheight+15, width-10, g_Locale->getText(LOCALE_STREAMINFO_SIGNAL), COL_MENUCONTENTDARK, 0, true); + + sigBox_x = _x; + sigBox_y = _y+iheight+15; + sigBox_w = w; + sigBox_h = h-iheight*3; + frameBuffer->paintBoxRel(sigBox_x,sigBox_y,sigBox_w+2,sigBox_h, COL_BLACK); + + y1 = _y + h + iheight + iheight+iheight-8; + y2 = _y + h - sheight+8; + + frameBuffer->paintBoxRel(_x+xd*0,y2- 12,16,2, COL_RED); //red + g_Font[font_small]->RenderString(_x+20+xd*0, y2, 50, "BER", COL_MENUCONTENTDARK, 0, true); + + frameBuffer->paintBoxRel(_x+xd*1,y2- 12,16,2,COL_BLUE); //blue + g_Font[font_small]->RenderString(_x+20+xd*1, y2, 50, "SNR", COL_MENUCONTENTDARK, 0, true); + + frameBuffer->paintBoxRel(_x+8+xd*2,y2- 12,16,2, COL_GREEN); //green + g_Font[font_small]->RenderString(_x+28+xd*2, y2, 50, "SIG", COL_MENUCONTENTDARK, 0, true); + + frameBuffer->paintBoxRel(_x+xd*3,y2- 12,16,2,COL_YELLOW); // near yellow + g_Font[font_small]->RenderString(_x+20+xd*3, y2, 50, "Bitrate", COL_MENUCONTENTDARK, 0, true); + + sig_text_y = y1 - iheight; + sig_text_ber_x = _x + xd * 0; + sig_text_snr_x = _x + 5 + xd * 1; + sig_text_sig_x = _x + 5 + xd * 2; + sig_text_rate_x = _x + 10 + xd * 3; + + int maxmin_x; // x-position of min and max + if (paint_mode == 0) { + maxmin_x = sig_text_ber_x-40; + } + else { + maxmin_x = _x + 40 + xd * 3 + 45; + } + g_Font[font_small]->RenderString(maxmin_x, y1 - sheight - sheight - sheight, 50, "max", COL_MENUCONTENTDARK, 0, true); + g_Font[font_small]->RenderString(maxmin_x, y1 - sheight, 50, "min", COL_MENUCONTENTDARK, 0, true); + + + sigBox_pos = 0; + + signal.old_sig = 1; + signal.old_snr = 1; + signal.old_ber = 1; + + feSignal s = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +// paint_signal_fe(rate, signal); +} + +void CStreamInfo2::paint_signal_fe(struct bitrate br, struct feSignal s) +{ + int x_now = sigBox_pos; + int yt = sig_text_y; + int yd; + static int old_x=0,old_y=0; + sigBox_pos = (++sigBox_pos) % sigBox_w; + + frameBuffer->paintVLine(sigBox_x+sigBox_pos, sigBox_y, sigBox_y+sigBox_h, COL_WHITE); + frameBuffer->paintVLine(sigBox_x+x_now, sigBox_y, sigBox_y+sigBox_h+1, COL_BLACK); + + long value = (long) (br.short_average / 1000ULL); + + SignalRenderStr(value, sig_text_rate_x, yt - sheight); + SignalRenderStr(br.max_short_average/ 1000ULL, sig_text_rate_x, yt - sheight - sheight); + SignalRenderStr(br.min_short_average/ 1000ULL, sig_text_rate_x, yt); + if ( g_RemoteControl->current_PIDs.PIDs.vpid > 0 ){ + yd = y_signal_fe (value, 15000, sigBox_h);// Video + Audio + } else { + yd = y_signal_fe (value, 512, sigBox_h); // Audio only + } + if ((old_x == 0 && old_y == 0) || sigBox_pos == 1) { + old_x = sigBox_x+x_now; + old_y = sigBox_y+sigBox_h-yd; + } else { + frameBuffer->paintLine(old_x, old_y, sigBox_x+x_now, sigBox_y+sigBox_h-yd, COL_YELLOW); //yellow + old_x = sigBox_x+x_now; + old_y = sigBox_y+sigBox_h-yd; + } + + if (s.ber != s.old_ber) { + SignalRenderStr(s.ber, sig_text_ber_x, yt - sheight); + SignalRenderStr(s.max_ber, sig_text_ber_x, yt - sheight - sheight); + SignalRenderStr(s.min_ber, sig_text_ber_x, yt); + } + yd = y_signal_fe (s.ber, 4000, sigBox_h); + frameBuffer->paintPixel(sigBox_x+x_now, sigBox_y+sigBox_h-yd, COL_RED); //red + + + if (s.sig != s.old_sig) { + SignalRenderStr(s.sig, sig_text_sig_x, yt - sheight); + SignalRenderStr(s.max_sig, sig_text_sig_x, yt - sheight - sheight); + SignalRenderStr(s.min_sig, sig_text_sig_x, yt); + } + yd = y_signal_fe (s.sig, 65000, sigBox_h); + frameBuffer->paintPixel(sigBox_x+x_now, sigBox_y+sigBox_h-yd, COL_GREEN); //green + + + if (s.snr != s.old_snr) { + SignalRenderStr(s.snr, sig_text_snr_x, yt - sheight); + SignalRenderStr(s.max_snr, sig_text_snr_x, yt - sheight - sheight); + SignalRenderStr(s.min_snr, sig_text_snr_x, yt); + } + yd = y_signal_fe (s.snr, 65000, sigBox_h); + frameBuffer->paintPixel(sigBox_x+x_now, sigBox_y+sigBox_h-yd, COL_BLUE); //blue +} + +// -- calc y from max_range and max_y +int CStreamInfo2::y_signal_fe (unsigned long value, unsigned long max_value, int max_y) +{ + long l; + + if (!max_value) + max_value = 1; + + l = ((long) max_y * (long) value) / (long) max_value; + if (l > max_y) + l = max_y; + + return (int) l; +} + +void CStreamInfo2::SignalRenderStr(unsigned int value, int _x, int _y) +{ + char str[30]; + + frameBuffer->paintBoxRel(_x, _y - sheight + 5, 60, sheight - 1, COL_MENUHEAD_PLUS_0); + sprintf(str,"%6u",value); + g_Font[font_small]->RenderString(_x, _y + 5, 60, str, COL_MENUCONTENTDARK, 0, true); +} + +void CStreamInfo2::paint (int mode) +{ + const char *head_string; + + width = frameBuffer->getScreenWidth(); + height = frameBuffer->getScreenHeight(); + x = frameBuffer->getScreenX(); + y = frameBuffer->getScreenY(); + int ypos = y + 5; + int xpos = x + 10; + + if (paint_mode == 0) { + + // -- tech Infos, PIG, small signal graph + head_string = g_Locale->getText (LOCALE_STREAMINFO_HEAD); + CVFD::getInstance ()->setMode (CVFD::MODE_MENU_UTF8, head_string); + + // paint backround, title pig, etc. + frameBuffer->paintBoxRel (0, 0, max_width, max_height, COL_MENUHEAD_PLUS_0); + g_Font[font_head]->RenderString (xpos, ypos + hheight + 1, width, head_string, COL_MENUHEAD, 0, true); // UTF-8 + ypos += hheight; + + // paint PIG + //paint_pig (width - 240, y + 10, 240, 190); + paint_pig (width - width/3 - 10, y + 10, width/3, height/3); + + // Info Output + //ypos += (iheight >> 1); + //ypos += iheight; + paint_techinfo (xpos, ypos); + + //paint_signal_fe_box (width - 240, (y + 190 + hheight), 240, 190); + paint_signal_fe_box (width - width/3 - 10, (y + 10 + height/3 + hheight), width/3, height/3 + hheight); + } else { + // -- small PIG, small signal graph + // -- paint backround, title pig, etc. + frameBuffer->paintBoxRel (0, 0, max_width, max_height, COL_MENUHEAD_PLUS_0); + + // -- paint large signal graph + paint_signal_fe_box (x, y, width, height-100); + + } + +} + +void CStreamInfo2::paint_techinfo(int xpos, int ypos) +{ + char buf[100], buf2[100]; + int xres, yres, aspectRatio, framerate; + // paint labels + int spaceoffset = 0,i = 0; + int array[4]={g_Font[font_info]->getRenderWidth(g_Locale->getText (LOCALE_STREAMINFO_RESOLUTION)), + g_Font[font_info]->getRenderWidth(g_Locale->getText (LOCALE_STREAMINFO_ARATIO)), + g_Font[font_info]->getRenderWidth(g_Locale->getText (LOCALE_STREAMINFO_FRAMERATE)), + g_Font[font_info]->getRenderWidth(g_Locale->getText (LOCALE_STREAMINFO_AUDIOTYPE))}; + for(i=0 ; i<4; i++) + { + if(spaceoffset < array[i]) + spaceoffset = array[i]; + } + spaceoffset+=4; + + videoDecoder->getPictureInfo(xres, yres, framerate); + aspectRatio = videoDecoder->getAspectRatio(); + //Video RESOLUTION + ypos += iheight; + sprintf ((char *) buf, "%s:",g_Locale->getText (LOCALE_STREAMINFO_RESOLUTION)); + g_Font[font_info]->RenderString (xpos, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + sprintf ((char *) buf, "%dx%d", xres, yres); + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + +#if 0 + ypos += iheight; + sprintf ((char *) buf, "%s: %d bits/sec", g_Locale->getText (LOCALE_STREAMINFO_BITRATE), (int) bitInfo[4] * 50); + g_Font[font_info]->RenderString (xpos, ypos, width*2/3 - 10, buf, COL_MENUCONTENT, 0, true); // UTF-8 + +#endif + //audio rate + ypos += iheight; + sprintf ((char *) buf, "%s:",g_Locale->getText (LOCALE_STREAMINFO_ARATIO)); + g_Font[font_info]->RenderString (xpos, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + switch (aspectRatio) { + case 1: + sprintf ((char *) buf, "4:3"); + break; + case 2: + sprintf ((char *) buf, "14:9"); + break; + case 3: + sprintf ((char *) buf, "16:9"); + break; + case 4: + sprintf ((char *) buf, "20:9"); + break; + default: + strncpy (buf, g_Locale->getText (LOCALE_STREAMINFO_ARATIO_UNKNOWN), sizeof (buf)); + } + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //Video FRAMERATE + ypos += iheight; + sprintf ((char *) buf, "%s:", g_Locale->getText (LOCALE_STREAMINFO_FRAMERATE)); + g_Font[font_info]->RenderString (xpos, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + switch (framerate) { + case 2: + sprintf ((char *) buf, "25fps"); + break; + case 5: + sprintf ((char *) buf, "50fps"); + break; + default: + strncpy (buf, g_Locale->getText (LOCALE_STREAMINFO_FRAMERATE_UNKNOWN), sizeof (buf)); + } + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //AUDIOTYPE + ypos += iheight; + int type, layer, freq, mode, bitrate; + audioDecoder->getAudioInfo(type, layer, freq, bitrate, mode); +#if 0 + const char *layernames[4] = { "res", "III", "II", "I" }; + const char *sampfreqnames[4] = { "44,1k", "48k", "32k", "res" }; + const char *modenames[4] = { "stereo", "joint_st", "dual_ch", "single_ch" }; + + sprintf ((char *) buf, "%s: %s (%s/%s) %s", g_Locale->getText (LOCALE_STREAMINFO_AUDIOTYPE), modenames[stereo], sampfreqnames[sampfreq], layernames[layer], copy ? "c" : ""); + } +#endif + const char *mpegmodes[4] = { "stereo", "joint_st", "dual_ch", "single_ch" }; + const char *ddmodes[8] = { "CH1/CH2", "C", "L/R", "L/C/R", "L/R/S", "L/C/R/S", "L/R/SL/SR", "L/C/R/SL/SR" }; + + sprintf ((char *) buf, "%s:", g_Locale->getText (LOCALE_STREAMINFO_AUDIOTYPE)); + g_Font[font_info]->RenderString (xpos, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + if(type == 0) { + sprintf ((char *) buf, "MPEG %s (%d)", mpegmodes[mode], freq); + } else { + sprintf ((char *) buf, "DD %s (%d)", ddmodes[mode], freq); + } + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //satellite + t_satellite_position satellitePosition = CNeutrinoApp::getInstance ()->channelList->getActiveSatellitePosition (); + sat_iterator_t sit = satellitePositions.find(satellitePosition); + if(sit != satellitePositions.end()) { + ypos += iheight; + sprintf ((char *) buf, "%s:",g_Locale->getText (LOCALE_SATSETUP_SATELLITE));//swiped locale + g_Font[font_info]->RenderString(xpos, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + sprintf ((char *) buf, "%s", sit->second.name.c_str()); + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + } + CChannelList *channelList = CNeutrinoApp::getInstance ()->channelList; + int curnum = channelList->getActiveChannelNumber(); + CZapitChannel * channel = channelList->getChannel(curnum); + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo (); + + //channel + ypos += iheight; + sprintf ((char *) buf, "%s:",g_Locale->getText (LOCALE_TIMERLIST_CHANNEL));//swiped locale + g_Font[font_info]->RenderString(xpos, ypos, width*2/3-10, buf , COL_MENUCONTENTDARK, 0, true); // UTF-8 + sprintf((char*) buf, "%s" ,channelList->getActiveChannelName().c_str()); + g_Font[font_info]->RenderString (xpos+spaceoffset, ypos, width*2/3 - 10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //tsfrequenz + ypos += iheight; + char * f=NULL, *s=NULL, *m=NULL; + if(frontend->getInfo()->type == FE_QPSK) { + frontend->getDelSys((fe_code_rate_t)si.fec, dvbs_get_modulation((fe_code_rate_t)si.fec), f, s, m); + sprintf ((char *) buf,"%d.%d (%c) %d %s %s %s", si.tsfrequency / 1000, si.tsfrequency % 1000, si.polarisation ? 'V' : 'H', si.rate / 1000,f,m,s=="DVB-S2"?"S2":"S1"); + g_Font[font_info]->RenderString(xpos, ypos, width*2/3-10, "Tp. Freq.:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_info]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + } + // paint labels + spaceoffset = 68; + + //onid + ypos+= sheight; + sprintf((char*) buf, "0x%04x (%i)", si.onid, si.onid); + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "ONid:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //sid + ypos+= sheight; + sprintf((char*) buf, "0x%04x (%i)", si.sid, si.sid); + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "Sid:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //tsid + ypos+= sheight; + sprintf((char*) buf, "0x%04x (%i)", si.tsid, si.tsid); + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "TSid:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //pmtpid + ypos+= sheight; + sprintf((char*) buf, "0x%04x (%i)", si.pmtpid, si.pmtpid); + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "PMTpid:", COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //vpid + ypos+= sheight; + if ( g_RemoteControl->current_PIDs.PIDs.vpid > 0 ){ + sprintf((char*) buf, "0x%04x (%i)", g_RemoteControl->current_PIDs.PIDs.vpid, g_RemoteControl->current_PIDs.PIDs.vpid ); + } else { + sprintf((char*) buf, "%s", g_Locale->getText(LOCALE_STREAMINFO_NOT_AVAILABLE)); + } + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "Vpid:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + //apid + ypos+= sheight; + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "Apid(s):" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + if (g_RemoteControl->current_PIDs.APIDs.empty()){ + sprintf((char*) buf, "%s", g_Locale->getText(LOCALE_STREAMINFO_NOT_AVAILABLE)); + } else { + unsigned int i=0,j=0,sw=spaceoffset; + for (i= 0; (icurrent_PIDs.APIDs.size()) && (i<10); i++) + { + sprintf((char*) buf, "0x%04x (%i)", g_RemoteControl->current_PIDs.APIDs[i].pid, g_RemoteControl->current_PIDs.APIDs[i].pid ); + if (i == g_RemoteControl->current_PIDs.PIDs.selected_apid){ + g_Font[font_small]->RenderString(xpos+sw, ypos, width*2/3-10, buf, COL_MENUHEAD, 0, true); // UTF-8 + } + else{ + g_Font[font_small]->RenderString(xpos+sw, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + } + sw = g_Font[font_small]->getRenderWidth(buf)+sw+10; + if (((i+1)%3 == 0) &&(g_RemoteControl->current_PIDs.APIDs.size()-1 > i)){ // if we have lots of apids, put "intermediate" line with pids + ypos+= sheight; + sw=spaceoffset; + } + } + } + + //vtxtpid + ypos += sheight; + if ( g_RemoteControl->current_PIDs.PIDs.vtxtpid == 0 ) + sprintf((char*) buf, "%s", g_Locale->getText(LOCALE_STREAMINFO_NOT_AVAILABLE)); + else + sprintf((char*) buf, "0x%04x (%i)", g_RemoteControl->current_PIDs.PIDs.vtxtpid, g_RemoteControl->current_PIDs.PIDs.vtxtpid ); + g_Font[font_small]->RenderString(xpos, ypos, width*2/3-10, "VTXTpid:" , COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[font_small]->RenderString(xpos+spaceoffset, ypos, width*2/3-10, buf, COL_MENUCONTENTDARK, 0, true); // UTF-8 + + yypos = ypos; +} + +int CStreamInfo2Handler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + int res = menu_return::RETURN_EXIT_ALL; + if (parent){ + parent->hide(); + } + CStreamInfo2 *e = new CStreamInfo2; + e->exec(); + delete e; + return res; +} + +/* + * some definition + */ +#define TS_LEN 188 +#define TS_BUF_SIZE (TS_LEN * 2048) /* fix dmx buffer size */ + +static unsigned long timeval_to_ms (const struct timeval *tv) +{ + return (tv->tv_sec * 1000) + ((tv->tv_usec + 500) / 1000); +} + +long delta_time_ms (struct timeval *tv, struct timeval *last_tv) +{ + return timeval_to_ms (tv) - timeval_to_ms (last_tv); +} + +unsigned long long b_total; +static cDemux * dmx; + +int CStreamInfo2::ts_setup () +{ + if (g_RemoteControl->current_PIDs.PIDs.vpid == 0) + return -1; + + dmx = new cDemux(1); + + dmx->Open(DMX_TP_CHANNEL, NULL, 3008 * 62); + dmx->pesFilter(g_RemoteControl->current_PIDs.PIDs.vpid); + dmx->Start(); + + gettimeofday (&first_tv, NULL); + last_tv.tv_sec = first_tv.tv_sec; + last_tv.tv_usec = first_tv.tv_usec; + b_total = 0; + return 0; +} + +int CStreamInfo2::update_rate () +{ + unsigned char buf[TS_BUF_SIZE]; + long b; + + int ret = 0; + int b_len, b_start; + int timeout = 100; + + if(!dmx) + return 0; + + b_len = 0; + b_start = 0; + + b_len = dmx->Read(buf, sizeof (buf), timeout); + //printf("ts: read %d\n", b_len); + + b = b_len; + if (b <= 0) + return 0; + + gettimeofday (&tv, NULL); + + b_total += b; + + long d_tim_ms; + + d_tim_ms = delta_time_ms (&tv, &last_tv); + if (d_tim_ms <= 0) + d_tim_ms = 1; // ignore usecs + + bit_s = (((unsigned long long) b * 8000ULL) + ((unsigned long long) d_tim_ms / 2ULL)) + / (unsigned long long) d_tim_ms; + + d_tim_ms = delta_time_ms (&tv, &first_tv); + if (d_tim_ms <= 0) + d_tim_ms = 1; // ignore usecs + + abit_s = ((b_total * 8000ULL) + ((unsigned long long) d_tim_ms / 2ULL)) + / (unsigned long long) d_tim_ms; + + last_tv.tv_sec = tv.tv_sec; + last_tv.tv_usec = tv.tv_usec; + ret = 1; + return ret; +} + +int CStreamInfo2::ts_close () +{ + if(dmx) + delete dmx; + dmx = NULL; + return 0; +} + +void CStreamInfo2::showSNR () +{ + char percent[10]; + int barwidth = 150; + uint16_t ssig, ssnr; + int sig, snr; + int posx, posy; + int sw; + + snr = (signal.snr & 0xFFFF) * 100 / 65535; + sig = (signal.sig & 0xFFFF) * 100 / 65535; + + int mheight = g_Font[font_info]->getHeight(); + if(sigscale->getPercent() != sig) { + posy = yypos + (mheight/2); + posx = x + 10; + sprintf(percent, "%d%% SIG", sig); + sw = g_Font[font_info]->getRenderWidth (percent); + sigscale->paint(posx - 1, posy, sig); + + posx = posx + barwidth + 3; + frameBuffer->paintBoxRel(posx, posy -1, sw, mheight-8, COL_MENUHEAD_PLUS_0); + g_Font[font_info]->RenderString (posx+2, posy + mheight-5, sw, percent, COL_MENUCONTENTDARK); + } + if(snrscale->getPercent() != snr) { + posy = yypos + mheight + 4; + posx = x + 10; + sprintf(percent, "%d%% SNR", snr); + sw = g_Font[font_info]->getRenderWidth (percent); + snrscale->paint(posx - 1, posy+2, snr); + + posx = posx + barwidth + 3; + frameBuffer->paintBoxRel(posx, posy - 1, sw, mheight-8, COL_MENUHEAD_PLUS_0, 0, true); + g_Font[font_info]->RenderString (posx + 2, posy + mheight-5, sw, percent, COL_MENUCONTENTDARK, 0, true); + } +} diff --git a/src/gui/streaminfo2.h b/src/gui/streaminfo2.h new file mode 100644 index 000000000..e85706659 --- /dev/null +++ b/src/gui/streaminfo2.h @@ -0,0 +1,114 @@ +/* + Neutrino-GUI - DBoxII-Project + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __streaminfo2__ +#define __streaminfo2__ + +#include + +#include +#include +#include + + +class CStreamInfo2 : public CMenuTarget +{ + private: + + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,iheight,sheight; // head/info/small font height + + int max_height; // Frambuffer 0.. max + int max_width; + + int yypos; + int paint_mode; + + int font_head; + int font_info; + int font_small; + + int sigBox_x; + int sigBox_y; + int sigBox_w; + int sigBox_h; + int sigBox_pos; + int sig_text_y; + int sig_text_ber_x; + int sig_text_sig_x; + int sig_text_snr_x; + int sig_text_rate_x; + + struct feSignal { + unsigned long ber, old_ber, max_ber, min_ber; + unsigned long sig, old_sig, max_sig, min_sig; + unsigned long snr, old_snr, max_snr, min_snr; + } signal; + + struct bitrate { + unsigned int short_average, max_short_average, min_short_average; + } rate; + + int doSignalStrengthLoop(); + + int dvrfd, dmxfd; + struct timeval tv, last_tv, first_tv; + unsigned long long bit_s; + unsigned long long abit_s; + unsigned long long b_total; + + int update_rate(); + int ts_setup(); + int ts_close(); + + void paint(int mode); + void paint_pig(int x, int y, int w, int h); + void paint_techinfo(int x, int y); + void paint_signal_fe_box(int x, int y, int w, int h); + void paint_signal_fe(struct bitrate rate, struct feSignal s); + int y_signal_fe(unsigned long value, unsigned long max_range, int max_y); + void SignalRenderStr (unsigned int value, int x, int y); + CScale *sigscale; + CScale *snrscale; + void showSNR (); + + public: + + CStreamInfo2(); + ~CStreamInfo2(); + int exec(); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; +class CStreamInfo2Handler : public CMenuTarget +{ + public: + int exec( CMenuTarget* parent, const std::string &actionKey); +}; +#endif + diff --git a/src/gui/timeosd.cpp b/src/gui/timeosd.cpp new file mode 100644 index 000000000..6f2e3c814 --- /dev/null +++ b/src/gui/timeosd.cpp @@ -0,0 +1,137 @@ +/* + Neutrino-GUI - DBoxII-Project + + Timerliste by Zwen + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +static CScale * timescale; + +#define TIMEOSD_FONT SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME +#define TIMEBARH 38 +#define BARLEN 200 +CTimeOSD::CTimeOSD() +{ + frameBuffer = CFrameBuffer::getInstance(); + visible=false; + m_mode=MODE_ASC; + GetDimensions(); + if(!timescale) + timescale = new CScale(200, 32, 40, 100, 70, true); +} + +CTimeOSD::~CTimeOSD() +{ + hide(); + if(timescale) { + delete timescale; + timescale = 0; + } +} + +void CTimeOSD::show(time_t time_show) +{ + GetDimensions(); + visible = true; + m_time_dis = time(NULL); + m_time_show = time_show; + frameBuffer->paintBoxRel(m_xstart-2, m_y, 2+BARLEN+2, TIMEBARH, COL_INFOBAR_SHADOW_PLUS_0); //border + timescale->reset(); + update(); +} + +void CTimeOSD::GetDimensions() +{ + m_xstart = g_settings.screen_StartX + 10; + m_xend = g_settings.screen_EndX - 10; + m_height = g_Font[TIMEOSD_FONT]->getHeight(); + m_y = g_settings.screen_StartY + 10; + m_width = g_Font[TIMEOSD_FONT]->getRenderWidth("00:00:00"); + if(g_settings.mode_clock) { + int x1 = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth(widest_number); + int x2 = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME]->getRenderWidth(":"); + twidth = x1*6 + x2*2; + m_xend = m_xend - twidth - 45; + } +} + +void CTimeOSD::update(time_t time_show) +{ + time_t tDisplayTime; + static time_t oldDisplayTime = 0; + char cDisplayTime[8+1]; + fb_pixel_t color1, color2; + +//printf("CTimeOSD::update time %ld\n", time_show); + if(!visible) + return; + if(m_mode == MODE_ASC) { + color1 = COL_MENUCONTENT_PLUS_0; + color2 = COL_MENUCONTENT; + } else { + color1 = COL_MENUCONTENTSELECTED_PLUS_0; + color2 = COL_MENUCONTENTSELECTED; + if(!time_show) time_show = 1; + } + if(time_show) { + m_time_show = time_show; + tDisplayTime = m_time_show; + } else { + if(m_mode == MODE_ASC) { + tDisplayTime = m_time_show + (time(NULL) - m_time_dis); + } else { + tDisplayTime = m_time_show + (m_time_dis - time(NULL)); + } + } + if(tDisplayTime < 0) + tDisplayTime=0; + if(tDisplayTime != oldDisplayTime) { + oldDisplayTime = tDisplayTime; + strftime(cDisplayTime, 9, "%T", gmtime(&tDisplayTime)); + frameBuffer->paintBoxRel(m_xend - m_width - 10, m_y, m_width + 10, m_height, color1); + g_Font[TIMEOSD_FONT]->RenderString(m_xend - m_width - 5, m_y + m_height, m_width +5, cDisplayTime, color2); + } +} + +void CTimeOSD::updatePos(short runningPercent) +{ + timescale->paint(m_xstart, m_y, runningPercent); +} + +void CTimeOSD::hide() +{ + GetDimensions(); +printf("CTimeOSD::hide: x %d y %d xend %d yend %d\n", m_xstart, m_y , m_xend - (g_settings.mode_clock ? 35 : 0), m_height + 15); + if(!visible) + return; + //frameBuffer->paintBackgroundBoxRel(m_xstart-10, m_y - 10 , m_xend - (g_settings.mode_clock ? 35 : 0), m_height + 15); + frameBuffer->paintBackgroundBoxRel(m_xstart-2, m_y , m_xend - (g_settings.mode_clock ? 35 : 0), m_height + 15); + visible=false; + timescale->reset(); +} diff --git a/src/gui/timeosd.h b/src/gui/timeosd.h new file mode 100644 index 000000000..284b07de6 --- /dev/null +++ b/src/gui/timeosd.h @@ -0,0 +1,60 @@ +/* + Neutrino-GUI - DBoxII-Project + + TimeOSD by Zwen + + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __timeosd__ +#define __timeosd__ + +#include +#include + +class CTimeOSD +{ + public: + enum mode + { + MODE_ASC, + MODE_DESC + }; + + private: + CFrameBuffer *frameBuffer; + time_t m_time_dis; + time_t m_time_show; + bool visible; + int m_xstart,m_xend,m_y,m_height, m_width, twidth; + mode m_mode; + void GetDimensions(); + + public: + CTimeOSD(); + ~CTimeOSD(); + void show(time_t time_show); + void update(time_t time_show = 0); + void updatePos(short runningPercent); + void hide(); + bool IsVisible() {return visible;} + void SetMode(mode m) { m_mode = m;} + mode GetMode() { return m_mode;} +}; +#endif diff --git a/src/gui/timerlist.cpp b/src/gui/timerlist.cpp new file mode 100644 index 000000000..e26c3ed40 --- /dev/null +++ b/src/gui/timerlist.cpp @@ -0,0 +1,1179 @@ +/* + Neutrino-GUI - DBoxII-Project + + Timerliste by Zwen + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +extern CBouquetManager *g_bouquetManager; + +#include + +#define info_height 60 +#define ROUND_RADIUS 9 + +class CTimerListNewNotifier : public CChangeObserver +{ +private: + CMenuItem* m1; + CMenuItem* m2; + CMenuItem* m3; + CMenuItem* m4; + CMenuItem* m5; + CMenuItem* m6; + char* display; + int* iType; + time_t* stopTime; +public: + CTimerListNewNotifier( int* Type, time_t* time,CMenuItem* a1, CMenuItem* a2, + CMenuItem* a3, CMenuItem* a4, CMenuItem* a5, CMenuItem* a6,char* d) + { + m1 = a1; + m2 = a2; + m3 = a3; + m4 = a4; + m5 = a5; + m6 = a6; + display=d; + iType=Type; + stopTime=time; + } + bool changeNotify(const neutrino_locale_t OptionName, void *) + { + CTimerd::CTimerEventTypes type = (CTimerd::CTimerEventTypes) *iType; + if(type == CTimerd::TIMER_RECORD) + { + *stopTime=(time(NULL)/60)*60; + struct tm *tmTime2 = localtime(stopTime); + sprintf( display, "%02d.%02d.%04d %02d:%02d", tmTime2->tm_mday, tmTime2->tm_mon+1, + tmTime2->tm_year+1900, + tmTime2->tm_hour, tmTime2->tm_min); + m1->setActive(true); + m6->setActive((g_settings.recording_type == RECORDING_FILE)); + } + else + { + *stopTime=0; + strcpy(display," "); + m1->setActive (false); + m6->setActive(false); + } + if(type == CTimerd::TIMER_RECORD || + type == CTimerd::TIMER_ZAPTO || + type == CTimerd::TIMER_NEXTPROGRAM) + { + m2->setActive(true); + } + else + { + m2->setActive(false); + } + if(type == CTimerd::TIMER_STANDBY) + m3->setActive(true); + else + m3->setActive(false); + if(type == CTimerd::TIMER_REMIND) + m4->setActive(true); + else + m4->setActive(false); + if(type == CTimerd::TIMER_EXEC_PLUGIN) + m5->setActive(true); + else + m5->setActive(false); + return true; + } +}; + +class CTimerListRepeatNotifier : public CChangeObserver +{ +private: + CMenuForwarder* m1; + CMenuForwarder* m2; + + int* iRepeat; +public: + CTimerListRepeatNotifier( int* repeat, CMenuForwarder* a1, CMenuForwarder *a2) + { + m1 = a1; + m2 = a2; + iRepeat=repeat; + } + + bool changeNotify(const neutrino_locale_t OptionName, void *) + { + if(*iRepeat >= (int)CTimerd::TIMERREPEAT_WEEKDAYS) + m1->setActive (true); + else + m1->setActive (false); + if (*iRepeat != (int)CTimerd::TIMERREPEAT_ONCE) + m2->setActive(true); + else + m2->setActive(false); + return true; + } +}; + +class CTimerListApidNotifier : public CChangeObserver +{ +private: + int* o_dflt; + int* o_std; + int* o_alt; + int* o_ac3; + CMenuItem* m_dflt; + CMenuItem* m_std; + CMenuItem* m_alt; + CMenuItem* m_ac3; +public: + CTimerListApidNotifier( int* o1, int* o2, int* o3, int* o4) + { + o_dflt=o1; + o_std=o2; + o_alt=o3; + o_ac3=o4; + } + + void setItems(CMenuItem* m1, CMenuItem* m2, CMenuItem* m3, CMenuItem* m4) + { + m_dflt=m1; + m_std=m2; + m_alt=m3; + m_ac3=m4; + } + + bool changeNotify(const neutrino_locale_t OptionName, void *) + { + if(OptionName == LOCALE_TIMERLIST_APIDS_DFLT) + { + if(*o_dflt==0) + { + m_std->setActive(true); + m_alt->setActive(true); + m_ac3->setActive(true); + } + else + { + m_std->setActive(false); + m_alt->setActive(false); + m_ac3->setActive(false); + *o_std=0; + *o_alt=0; + *o_ac3=0; + } + } + else + { + if(*o_std || *o_alt || *o_ac3) + *o_dflt=0; + } + return true; + } +}; + + +CTimerList::CTimerList() +{ + frameBuffer = CFrameBuffer::getInstance(); + visible = false; + selected = 0; + liststart = 0; + Timer = new CTimerdClient(); + skipEventID=0; + buttonHeight = 25; +} + +CTimerList::~CTimerList() +{ + timerlist.clear(); + delete Timer; +} + +int CTimerList::exec(CMenuTarget* parent, const std::string & actionKey) +{ + const char * key = actionKey.c_str(); + + if (strcmp(key, "modifytimer") == 0) + { + timerlist[selected].announceTime = timerlist[selected].alarmTime -60; + if(timerlist[selected].eventRepeat >= CTimerd::TIMERREPEAT_WEEKDAYS) + Timer->getWeekdaysFromStr(&timerlist[selected].eventRepeat, m_weekdaysStr); + if(timerlist[selected].eventType == CTimerd::TIMER_RECORD) + { + timerlist[selected].announceTime -= 120; // 2 more mins for rec timer + if (timer_apids_dflt) + timerlist[selected].apids = TIMERD_APIDS_CONF; + else + timerlist[selected].apids = (timer_apids_std * TIMERD_APIDS_STD) | (timer_apids_ac3 * TIMERD_APIDS_AC3) | + (timer_apids_alt * TIMERD_APIDS_ALT); + Timer->modifyTimerAPid(timerlist[selected].eventID,timerlist[selected].apids); + Timer->modifyRecordTimerEvent(timerlist[selected].eventID, timerlist[selected].announceTime, + timerlist[selected].alarmTime, + timerlist[selected].stopTime, timerlist[selected].eventRepeat, + timerlist[selected].repeatCount,timerlist[selected].recordingDir); + } else + { + Timer->modifyTimerEvent(timerlist[selected].eventID, timerlist[selected].announceTime, + timerlist[selected].alarmTime, + timerlist[selected].stopTime, timerlist[selected].eventRepeat, + timerlist[selected].repeatCount); + } + return menu_return::RETURN_EXIT; + } + else if (strcmp(key, "newtimer") == 0) + { + timerNew.announceTime=timerNew.alarmTime-60; + CTimerd::EventInfo eventinfo; + CTimerd::RecordingInfo recinfo; + eventinfo.epgID=0; + eventinfo.epg_starttime=0; + eventinfo.channel_id=timerNew.channel_id; + eventinfo.apids = TIMERD_APIDS_CONF; + eventinfo.recordingSafety = false; + timerNew.standby_on = (timerNew_standby_on == 1); + void *data=NULL; + if(timerNew.eventType == CTimerd::TIMER_STANDBY) + data=&(timerNew.standby_on); + else if(timerNew.eventType==CTimerd::TIMER_NEXTPROGRAM || + timerNew.eventType==CTimerd::TIMER_ZAPTO || + timerNew.eventType==CTimerd::TIMER_RECORD) + { + if (strcmp(timerNew_channel_name, "---")==0) + return menu_return::RETURN_REPAINT; + if (timerNew.eventType==CTimerd::TIMER_RECORD) + { + recinfo.epgID=0; + recinfo.epg_starttime=0; + recinfo.channel_id=timerNew.channel_id; + recinfo.apids=TIMERD_APIDS_CONF; + recinfo.recordingSafety = false; + + timerNew.announceTime-= 120; // 2 more mins for rec timer + strncpy(recinfo.recordingDir,timerNew.recordingDir,sizeof(recinfo.recordingDir)); + data = &recinfo; + } else + data= &eventinfo; + } + else if(timerNew.eventType==CTimerd::TIMER_REMIND) + data= timerNew.message; + else if (timerNew.eventType==CTimerd::TIMER_EXEC_PLUGIN) + { + if (strcmp(timerNew.pluginName, "---") == 0) + return menu_return::RETURN_REPAINT; + data= timerNew.pluginName; + } + if(timerNew.eventRepeat >= CTimerd::TIMERREPEAT_WEEKDAYS) + Timer->getWeekdaysFromStr(&timerNew.eventRepeat, m_weekdaysStr); + + if (Timer->addTimerEvent(timerNew.eventType,data,timerNew.announceTime,timerNew.alarmTime, + timerNew.stopTime,timerNew.eventRepeat,timerNew.repeatCount,false) == -1) + { + bool forceAdd = askUserOnTimerConflict(timerNew.announceTime,timerNew.stopTime); + + if (forceAdd) + { + Timer->addTimerEvent(timerNew.eventType,data,timerNew.announceTime,timerNew.alarmTime, + timerNew.stopTime, timerNew.eventRepeat,timerNew.repeatCount,true); + } + } + return menu_return::RETURN_EXIT; + } + else if (strncmp(key, "SC:", 3) == 0) + { + int delta; + sscanf(&(key[3]), + SCANF_CHANNEL_ID_TYPE + "%n", + &timerNew.channel_id, + &delta); + strncpy(timerNew_channel_name, &(key[3 + delta + 1]), 30); + g_RCInput->postMsg(CRCInput::RC_timeout, 0); // leave underlying menu also + g_RCInput->postMsg(CRCInput::RC_timeout, 0); // leave underlying menu also + return menu_return::RETURN_EXIT; + } + + if(parent) + { + parent->hide(); + } + + int ret = show(); + + return ret; +/* + if( ret > -1) + { + return menu_return::RETURN_REPAINT; + } + else if( ret == -1) + { + // -1 bedeutet nur REPAINT + return menu_return::RETURN_REPAINT; + } + else + { + // -2 bedeutet EXIT_ALL + return menu_return::RETURN_EXIT_ALL; + }*/ +} + +void CTimerList::updateEvents(void) +{ + timerlist.clear(); + Timer->getTimerList (timerlist); + //Remove last deleted event from List + CTimerd::TimerList::iterator timer = timerlist.begin(); + for(; timer != timerlist.end();timer++) + { + if(timer->eventID==skipEventID) + { + timerlist.erase(timer); + break; + } + } + sort(timerlist.begin(), timerlist.end()); + + width = w_max(720, 0); + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + + height = frameBuffer->getScreenHeight() - (info_height+50); + + listmaxshow = (height-theight-0)/(fheight*2); + height = theight+0+listmaxshow*fheight*2; // recalc height + if(timerlist.size() < listmaxshow) + { + listmaxshow=timerlist.size(); + height = theight+0+listmaxshow*fheight*2; // recalc height + } + if(selected==timerlist.size() && !(timerlist.empty())) + { + selected=timerlist.size()-1; + liststart = (selected/listmaxshow)*listmaxshow; + } + x = frameBuffer->getScreenX() + (frameBuffer->getScreenWidth() - width) / 2; + y = frameBuffer->getScreenY() + (frameBuffer->getScreenHeight() - (height+ info_height)) / 2; +} + + +int CTimerList::show() +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + bool loop=true; + bool update=true; + while(loop) + { + if(update) + { + hide(); + updateEvents(); + update=false; + paint(); + } + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + if( ( msg == CRCInput::RC_timeout ) || + ( msg == CRCInput::RC_home) ) + { //Exit after timeout or cancel key + loop=false; + } + else if ((msg == CRCInput::RC_up) && !(timerlist.empty())) + { + int prevselected=selected; + if(selected==0) + { + selected = timerlist.size()-1; + } + else + selected--; + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if ((msg == CRCInput::RC_down) && !(timerlist.empty())) + { + int prevselected=selected; + selected = (selected+1)%timerlist.size(); + paintItem(prevselected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + { + paint(); + } + else + { + paintItem(selected - liststart); + } + } + else if ((msg == CRCInput::RC_ok) && !(timerlist.empty())) + { + if (modifyTimer()==menu_return::RETURN_EXIT_ALL) + { + res=menu_return::RETURN_EXIT_ALL; + loop=false; + } + else + update=true; + } + else if((msg == CRCInput::RC_red) && !(timerlist.empty())) + { + Timer->removeTimerEvent(timerlist[selected].eventID); + skipEventID=timerlist[selected].eventID; + update=true; + } + else if(msg==CRCInput::RC_green) + { + if (newTimer()==menu_return::RETURN_EXIT_ALL) + { + res=menu_return::RETURN_EXIT_ALL; + loop=false; + } + else + update=true; + } + else if(msg==CRCInput::RC_yellow) + { + update=true; + } + else if((msg==CRCInput::RC_blue)|| + (CRCInput::isNumeric(msg)) ) + { + //pushback key if... + g_RCInput->postMsg( msg, data ); + loop=false; + } + else if(msg==CRCInput::RC_setup) + { + res=menu_return::RETURN_EXIT_ALL; + loop=false; + } + else if( msg == CRCInput::RC_help || msg == CRCInput::RC_info) + { + CTimerd::responseGetTimer* timer=&timerlist[selected]; + if(timer!=NULL) + { + if(timer->eventType == CTimerd::TIMER_RECORD || timer->eventType == CTimerd::TIMER_ZAPTO) + { + hide(); + res = g_EpgData->show(timer->channel_id, timer->epgID, &timer->epg_starttime); + if(res==menu_return::RETURN_EXIT_ALL) + loop=false; + else + paint(); + } + } + // help key + } + else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + g_RCInput->postMsg (msg, 0); + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + hide(); + + return(res); +} + +void CTimerList::hide() +{ + if(visible) + { + frameBuffer->paintBackgroundBoxRel(x, y, width, height+ info_height+ 5); + visible = false; + } +} + +bool sectionsd_getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata); +void CTimerList::paintItem(int pos) +{ + int ypos = y+ theight+0 + pos*fheight*2; + + uint8_t color; + fb_pixel_t bgcolor; + + if (pos & 1) + { + color = COL_MENUCONTENTDARK; + bgcolor = COL_MENUCONTENTDARK_PLUS_0; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + if (liststart + pos == selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + + int real_width=width; + if(timerlist.size()>listmaxshow) + { + real_width-=15; //scrollbar + } + + frameBuffer->paintBoxRel(x,ypos, real_width, 2*fheight, bgcolor); + if(liststart+posRenderString(x+10,ypos+fheight, 150, zAlarmTime, color, fheight, true); // UTF-8 + if(timer.stopTime != 0) + { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10,ypos+2*fheight, 150, zStopTime, color, fheight, true); // UTF-8 + } + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+160,ypos+fheight, (real_width-160)/2-5, convertTimerRepeat2String(timer.eventRepeat), color, fheight, true); // UTF-8 + + if (timer.eventRepeat != CTimerd::TIMERREPEAT_ONCE) + { + char srepeatcount[25] = {0}; + if (timer.repeatCount == 0) +// Unicode 8734 (hex: 221E) not available in all fonts +// sprintf(srepeatcount,"∞"); + sprintf(srepeatcount,"00"); + else + sprintf(srepeatcount,"%ux",timer.repeatCount); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+160+(real_width-300)/2,ypos+fheight, (real_width-160)/2-5, srepeatcount, color, fheight, true); // UTF-8 + } + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+160+(real_width-160)/2,ypos+fheight, (real_width-160)/2-5, convertTimerType2String(timer.eventType), color, fheight, true); // UTF-8 + std::string zAddData(""); + switch(timer.eventType) + { + case CTimerd::TIMER_NEXTPROGRAM : + case CTimerd::TIMER_ZAPTO : + case CTimerd::TIMER_RECORD : + { + zAddData = convertChannelId2String(timer.channel_id); // UTF-8 + if(timer.apids != TIMERD_APIDS_CONF) + { + std::string sep = ""; + zAddData += " ("; + if(timer.apids & TIMERD_APIDS_STD) + { + zAddData += "STD"; + sep = "/"; + } + if(timer.apids & TIMERD_APIDS_ALT) + { + zAddData += sep; + zAddData += "ALT"; + sep = "/"; + } + if(timer.apids & TIMERD_APIDS_AC3) + { + zAddData += sep; + zAddData += "AC3"; + sep = "/"; + } + zAddData += ')'; + } + if(timer.epgID!=0) + { + CEPGData epgdata; + //if (g_Sectionsd->getEPGid(timer.epgID, timer.epg_starttime, &epgdata)) + if (sectionsd_getEPGid(timer.epgID, timer.epg_starttime, &epgdata)) + { + zAddData += " : "; + zAddData += epgdata.title; + } + else if(strlen(timer.epgTitle)!=0) + { + zAddData += " : "; + zAddData += timer.epgTitle; + } + } + else if(strlen(timer.epgTitle)!=0) + { + zAddData += " : "; + zAddData += timer.epgTitle; + } + } + break; + case CTimerd::TIMER_STANDBY: + { + zAddData = g_Locale->getText(timer.standby_on ? LOCALE_TIMERLIST_STANDBY_ON : LOCALE_TIMERLIST_STANDBY_OFF); + break; + } + case CTimerd::TIMER_REMIND : + { + zAddData = timer.message; // must be UTF-8 encoded ! + } + break; + case CTimerd::TIMER_EXEC_PLUGIN : + { + zAddData = timer.pluginName; + } + break; + default:{} + } + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+160,ypos+2*fheight, real_width-165, zAddData, color, fheight, true); // UTF-8 + // LCD Display + if(liststart+pos==selected) + { + std::string line1 = convertTimerType2String(timer.eventType); // UTF-8 + std::string line2 = zAlarmTime; + switch(timer.eventType) + { + case CTimerd::TIMER_RECORD : + line2+= " -"; + line2+= zStopTime+6; + case CTimerd::TIMER_NEXTPROGRAM : + case CTimerd::TIMER_ZAPTO : + { + line1 += ' '; + line1 += convertChannelId2String(timer.channel_id); // UTF-8 + } + break; + case CTimerd::TIMER_STANDBY : + { + if(timer.standby_on) + line1+=" ON"; + else + line1+=" OFF"; + } + break; + default:; + } + CVFD::getInstance()->showMenuText(0, line1.c_str(), -1, true); // UTF-8 + //CVFD::getInstance()->showMenuText(1, line2.c_str(), -1, true); // UTF-8 + } + } +} + +void CTimerList::paintHead() +{ + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + frameBuffer->paintIcon("timer.raw",x+5,y+4); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+35,y+theight+0, width- 45, g_Locale->getText(LOCALE_TIMERLIST_NAME), COL_MENUHEAD, 0, true); // UTF-8 + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 ); +/* if (bouquetList!=NULL) + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, x+ width- 60, y+ 5 );*/ +} + +const struct button_label TimerListButtons[3] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_TIMERLIST_DELETE }, + { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_TIMERLIST_NEW }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_TIMERLIST_RELOAD } +}; + +void CTimerList::paintFoot() +{ + int ButtonWidth = (width - 20) / 4; + frameBuffer->paintBoxRel(x,y+height, width,buttonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + //frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + if (timerlist.empty()) + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + ButtonWidth + 10, y + height + 4, ButtonWidth, 2, &(TimerListButtons[1])); + else + { + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 10, y + height + 4, ButtonWidth, 3, TimerListButtons); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x+width- 1* ButtonWidth + 10, y+height); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width-1 * ButtonWidth + 38, y+height+24 - 2, ButtonWidth- 28, g_Locale->getText(LOCALE_TIMERLIST_MODIFY), COL_INFOBAR, 0, true); // UTF-8 + } +} + +void CTimerList::paint() +{ + unsigned int page_nr = (listmaxshow == 0) ? 0 : (selected / listmaxshow); + liststart = page_nr * listmaxshow; + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_TIMERLIST_NAME)); + + paintHead(); + for(unsigned int count=0;countlistmaxshow) + { + int ypos = y+ theight; + int sb = 2*fheight* listmaxshow; + frameBuffer->paintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((timerlist.size()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(page_nr * sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); + } + + paintFoot(); + visible = true; +} + +const char * CTimerList::convertTimerType2String(const CTimerd::CTimerEventTypes type) // UTF-8 +{ + switch(type) + { + case CTimerd::TIMER_SHUTDOWN : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_SHUTDOWN ); + case CTimerd::TIMER_NEXTPROGRAM : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_NEXTPROGRAM); + case CTimerd::TIMER_ZAPTO : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_ZAPTO ); + case CTimerd::TIMER_STANDBY : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_STANDBY ); + case CTimerd::TIMER_RECORD : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_RECORD ); + case CTimerd::TIMER_REMIND : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_REMIND ); + case CTimerd::TIMER_SLEEPTIMER : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_SLEEPTIMER ); + case CTimerd::TIMER_EXEC_PLUGIN : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_EXECPLUGIN ); + default : return g_Locale->getText(LOCALE_TIMERLIST_TYPE_UNKNOWN ); + } +} + +std::string CTimerList::convertTimerRepeat2String(const CTimerd::CTimerEventRepeat rep) // UTF-8 +{ + switch(rep) + { + case CTimerd::TIMERREPEAT_ONCE : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_ONCE ); + case CTimerd::TIMERREPEAT_DAILY : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_DAILY ); + case CTimerd::TIMERREPEAT_WEEKLY : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_WEEKLY ); + case CTimerd::TIMERREPEAT_BIWEEKLY : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_BIWEEKLY ); + case CTimerd::TIMERREPEAT_FOURWEEKLY : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_FOURWEEKLY ); + case CTimerd::TIMERREPEAT_MONTHLY : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_MONTHLY ); + case CTimerd::TIMERREPEAT_BYEVENTDESCRIPTION : return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_BYEVENTDESCRIPTION); + default: + if(rep >=CTimerd::TIMERREPEAT_WEEKDAYS) + { + int weekdays = (((int)rep) >> 9); + std::string weekdayStr=""; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_MONDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_TUESDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_WEDNESDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_THURSDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_FRIDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_SATURDAY); + weekdays >>= 1; + if(weekdays & 1) + weekdayStr+= g_Locale->getText(LOCALE_TIMERLIST_REPEAT_SUNDAY); + return weekdayStr; + } + else + return g_Locale->getText(LOCALE_TIMERLIST_REPEAT_UNKNOWN); + } +} + +std::string CTimerList::convertChannelId2String(const t_channel_id id) // UTF-8 +{ + //CZapitClient Zapit; + std::string name = g_Zapit->getChannelName(id); // UTF-8 + if (name.empty()) + name = g_Locale->getText(LOCALE_TIMERLIST_PROGRAM_UNKNOWN); + + return name; +} + +#define TIMERLIST_REPEAT_OPTION_COUNT 7 +const CMenuOptionChooser::keyval TIMERLIST_REPEAT_OPTIONS[TIMERLIST_REPEAT_OPTION_COUNT] = +{ + { CTimerd::TIMERREPEAT_ONCE , LOCALE_TIMERLIST_REPEAT_ONCE }, + { CTimerd::TIMERREPEAT_DAILY , LOCALE_TIMERLIST_REPEAT_DAILY }, + { CTimerd::TIMERREPEAT_WEEKLY , LOCALE_TIMERLIST_REPEAT_WEEKLY }, + { CTimerd::TIMERREPEAT_BIWEEKLY , LOCALE_TIMERLIST_REPEAT_BIWEEKLY }, + { CTimerd::TIMERREPEAT_FOURWEEKLY , LOCALE_TIMERLIST_REPEAT_FOURWEEKLY }, + { CTimerd::TIMERREPEAT_MONTHLY , LOCALE_TIMERLIST_REPEAT_MONTHLY }, + { CTimerd::TIMERREPEAT_WEEKDAYS , LOCALE_TIMERLIST_REPEAT_WEEKDAYS } +}; + +#define TIMERLIST_STANDBY_OPTION_COUNT 2 +const CMenuOptionChooser::keyval TIMERLIST_STANDBY_OPTIONS[TIMERLIST_STANDBY_OPTION_COUNT] = +{ + { 0 , LOCALE_TIMERLIST_STANDBY_OFF }, + { 1 , LOCALE_TIMERLIST_STANDBY_ON } +}; + +#if 1 +#define TIMERLIST_TYPE_OPTION_COUNT 7 +#else +#define TIMERLIST_TYPE_OPTION_COUNT 8 +#endif +const CMenuOptionChooser::keyval TIMERLIST_TYPE_OPTIONS[TIMERLIST_TYPE_OPTION_COUNT] = +{ + { CTimerd::TIMER_SHUTDOWN , LOCALE_TIMERLIST_TYPE_SHUTDOWN }, +#if 0 + { CTimerd::TIMER_NEXTPROGRAM, LOCALE_TIMERLIST_TYPE_NEXTPROGRAM }, +#endif + { CTimerd::TIMER_ZAPTO , LOCALE_TIMERLIST_TYPE_ZAPTO }, + { CTimerd::TIMER_STANDBY , LOCALE_TIMERLIST_TYPE_STANDBY }, + { CTimerd::TIMER_RECORD , LOCALE_TIMERLIST_TYPE_RECORD }, + { CTimerd::TIMER_SLEEPTIMER , LOCALE_TIMERLIST_TYPE_SLEEPTIMER }, + { CTimerd::TIMER_REMIND , LOCALE_TIMERLIST_TYPE_REMIND }, + { CTimerd::TIMER_EXEC_PLUGIN, LOCALE_TIMERLIST_TYPE_EXECPLUGIN } +}; + +#define MESSAGEBOX_NO_YES_OPTION_COUNT 2 +const CMenuOptionChooser::keyval MESSAGEBOX_NO_YES_OPTIONS[MESSAGEBOX_NO_YES_OPTION_COUNT] = +{ + { 0, LOCALE_MESSAGEBOX_NO }, + { 1, LOCALE_MESSAGEBOX_YES } +}; + +int CTimerList::modifyTimer() +{ + CTimerd::responseGetTimer* timer=&timerlist[selected]; + CMenuWidget timerSettings(LOCALE_TIMERLIST_MENUMODIFY, NEUTRINO_ICON_SETTINGS); + timerSettings.addItem(GenericMenuSeparator); + timerSettings.addItem(GenericMenuBack); + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem(new CMenuForwarder(LOCALE_TIMERLIST_SAVE, true, NULL, this, "modifytimer", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + timerSettings.addItem(GenericMenuSeparatorLine); + + char type[80]; + strcpy(type, convertTimerType2String(timer->eventType)); // UTF + CMenuForwarder *m0 = new CMenuForwarder(LOCALE_TIMERLIST_TYPE, false, type); + timerSettings.addItem( m0); + + CDateInput timerSettings_alarmTime(LOCALE_TIMERLIST_ALARMTIME, &timer->alarmTime , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CMenuForwarder *m1 = new CMenuForwarder(LOCALE_TIMERLIST_ALARMTIME, true, timerSettings_alarmTime.getValue (), &timerSettings_alarmTime ); + timerSettings.addItem( m1); + + CDateInput timerSettings_stopTime(LOCALE_TIMERLIST_STOPTIME, &timer->stopTime , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + if(timer->stopTime != 0) + { + CMenuForwarder *m2 = new CMenuForwarder(LOCALE_TIMERLIST_STOPTIME, true, timerSettings_stopTime.getValue (), &timerSettings_stopTime ); + timerSettings.addItem( m2); + } + + Timer->setWeekdaysToStr(timer->eventRepeat, m_weekdaysStr); + timer->eventRepeat = (CTimerd::CTimerEventRepeat)(((int)timer->eventRepeat) & 0x1FF); + CStringInput timerSettings_weekdays(LOCALE_TIMERLIST_WEEKDAYS, m_weekdaysStr, 7, LOCALE_TIMERLIST_WEEKDAYS_HINT_1, LOCALE_TIMERLIST_WEEKDAYS_HINT_2, "-X"); + CMenuForwarder *m4 = new CMenuForwarder(LOCALE_TIMERLIST_WEEKDAYS, ((int)timer->eventRepeat) >= (int)CTimerd::TIMERREPEAT_WEEKDAYS, m_weekdaysStr, &timerSettings_weekdays ); + CIntInput timerSettings_repeatCount(LOCALE_TIMERLIST_REPEATCOUNT, (int&)timer->repeatCount,3, LOCALE_TIMERLIST_REPEATCOUNT_HELP1, LOCALE_TIMERLIST_REPEATCOUNT_HELP2); + + CMenuForwarder *m5 = new CMenuForwarder(LOCALE_TIMERLIST_REPEATCOUNT, timer->eventRepeat != (int)CTimerd::TIMERREPEAT_ONCE ,timerSettings_repeatCount.getValue() , &timerSettings_repeatCount); + + CTimerListRepeatNotifier notifier((int *)&timer->eventRepeat,m4,m5); + CMenuOptionChooser* m3 = new CMenuOptionChooser(LOCALE_TIMERLIST_REPEAT, (int *)&timer->eventRepeat, TIMERLIST_REPEAT_OPTIONS, TIMERLIST_REPEAT_OPTION_COUNT, true, ¬ifier); + +//printf("TIMER: rec dir %s len %s\n", timer->recordingDir, strlen(timer->recordingDir)); + + if(!strlen(timer->recordingDir)) + strncpy(timer->recordingDir,g_settings.network_nfs_recordingdir,sizeof(timer->recordingDir)); + + CMountChooser recDirs(LOCALE_TIMERLIST_RECORDING_DIR,NEUTRINO_ICON_SETTINGS,NULL, + timer->recordingDir, g_settings.network_nfs_recordingdir); + if (!recDirs.hasItem()) + { + printf("[CTimerList] warning: no network devices available\n"); + } + bool recDirEnabled = recDirs.hasItem() && (timer->eventType == CTimerd::TIMER_RECORD) && (g_settings.recording_type == RECORDING_FILE); + CMenuForwarder* m6 = new CMenuForwarder(LOCALE_TIMERLIST_RECORDING_DIR,recDirEnabled,timer->recordingDir, &recDirs); + + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem(m3); + timerSettings.addItem(m4); + timerSettings.addItem(m5); + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem(m6); + + CMenuWidget timerSettings_apids(LOCALE_TIMERLIST_APIDS, NEUTRINO_ICON_SETTINGS); + CTimerListApidNotifier apid_notifier(&timer_apids_dflt, &timer_apids_std, &timer_apids_ac3, &timer_apids_alt); + timer_apids_dflt = (timer->apids == 0) ? 1 : 0 ; + timer_apids_std = (timer->apids & TIMERD_APIDS_STD) ? 1 : 0 ; + timer_apids_ac3 = (timer->apids & TIMERD_APIDS_AC3) ? 1 : 0 ; + timer_apids_alt = (timer->apids & TIMERD_APIDS_ALT) ? 1 : 0 ; + timerSettings_apids.addItem(GenericMenuSeparator); + timerSettings_apids.addItem(GenericMenuBack); + timerSettings_apids.addItem(GenericMenuSeparatorLine); + CMenuOptionChooser* ma1 = new CMenuOptionChooser(LOCALE_TIMERLIST_APIDS_DFLT, &timer_apids_dflt, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, &apid_notifier); + timerSettings_apids.addItem(ma1); + CMenuOptionChooser* ma2 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_STD, &timer_apids_std, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, &apid_notifier); + timerSettings_apids.addItem(ma2); + CMenuOptionChooser* ma3 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_ALT, &timer_apids_alt, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, &apid_notifier); + timerSettings_apids.addItem(ma3); + CMenuOptionChooser* ma4 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_AC3, &timer_apids_ac3, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, &apid_notifier); + timerSettings_apids.addItem(ma4); + apid_notifier.setItems(ma1,ma2,ma3,ma4); + if(timer->eventType == CTimerd::TIMER_RECORD) + { + timerSettings.addItem( new CMenuForwarder(LOCALE_TIMERLIST_APIDS, true, NULL, &timerSettings_apids )); + } + + return timerSettings.exec(this,""); +} + +int CTimerList::newTimer() +{ + std::vector toDelete; + // Defaults + timerNew.eventType = CTimerd::TIMER_RECORD ; + timerNew.eventRepeat = CTimerd::TIMERREPEAT_ONCE ; + timerNew.repeatCount = 0; + timerNew.alarmTime = (time(NULL)/60)*60; + timerNew.stopTime = (time(NULL)/60)*60; + timerNew.channel_id = 0; + strcpy(timerNew.message, ""); + timerNew_standby_on =false; + strncpy(timerNew.recordingDir,g_settings.network_nfs_recordingdir,sizeof(timerNew.recordingDir)); + + + CMenuWidget timerSettings(LOCALE_TIMERLIST_MENUNEW, NEUTRINO_ICON_SETTINGS); + timerSettings.addItem(GenericMenuSeparator); + timerSettings.addItem(GenericMenuBack); + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem(new CMenuForwarder(LOCALE_TIMERLIST_SAVE, true, NULL, this, "newtimer", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + timerSettings.addItem(GenericMenuSeparatorLine); + + CDateInput timerSettings_alarmTime(LOCALE_TIMERLIST_ALARMTIME, &(timerNew.alarmTime) , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CMenuForwarder *m1 = new CMenuForwarder(LOCALE_TIMERLIST_ALARMTIME, true, timerSettings_alarmTime.getValue (), &timerSettings_alarmTime ); + + CDateInput timerSettings_stopTime(LOCALE_TIMERLIST_STOPTIME, &(timerNew.stopTime) , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CMenuForwarder *m2 = new CMenuForwarder(LOCALE_TIMERLIST_STOPTIME, true, timerSettings_stopTime.getValue (), &timerSettings_stopTime ); + + CStringInput timerSettings_weekdays(LOCALE_TIMERLIST_WEEKDAYS, m_weekdaysStr, 7, LOCALE_TIMERLIST_WEEKDAYS_HINT_1, LOCALE_TIMERLIST_WEEKDAYS_HINT_2, "-X"); + CMenuForwarder *m4 = new CMenuForwarder(LOCALE_TIMERLIST_WEEKDAYS, false, m_weekdaysStr, &timerSettings_weekdays); + + CIntInput timerSettings_repeatCount(LOCALE_TIMERLIST_REPEATCOUNT, (int&)timerNew.repeatCount,3, LOCALE_TIMERLIST_REPEATCOUNT_HELP1, LOCALE_TIMERLIST_REPEATCOUNT_HELP2); + CMenuForwarder *m5 = new CMenuForwarder(LOCALE_TIMERLIST_REPEATCOUNT, false,timerSettings_repeatCount.getValue() , &timerSettings_repeatCount); + + CTimerListRepeatNotifier notifier((int *)&timerNew.eventRepeat,m4,m5); + strcpy(m_weekdaysStr,"-------"); + CMenuOptionChooser* m3 = new CMenuOptionChooser(LOCALE_TIMERLIST_REPEAT, (int *)&timerNew.eventRepeat, TIMERLIST_REPEAT_OPTIONS, TIMERLIST_REPEAT_OPTION_COUNT, true, ¬ifier); + + + CMenuWidget mctv(LOCALE_TIMERLIST_BOUQUETSELECT, NEUTRINO_ICON_SETTINGS); + CMenuWidget mcradio(LOCALE_TIMERLIST_BOUQUETSELECT, NEUTRINO_ICON_SETTINGS); + + for (int i = 0; i < (int) g_bouquetManager->Bouquets.size(); i++) { + if (!g_bouquetManager->Bouquets[i]->bHidden) { + CMenuWidget* mwtv = new CMenuWidget(LOCALE_TIMERLIST_CHANNELSELECT, NEUTRINO_ICON_SETTINGS); + toDelete.push_back(mwtv); + CMenuWidget* mwradio = new CMenuWidget(LOCALE_TIMERLIST_CHANNELSELECT, NEUTRINO_ICON_SETTINGS); + toDelete.push_back(mwradio); + + ZapitChannelList* channels = &(g_bouquetManager->Bouquets[i]->tvChannels); + for(int j = 0; j < (int) channels->size(); j++) { + char cChannelId[3+16+1+1]; + sprintf(cChannelId, "SC:" PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS ",", (*channels)[j]->channel_id); + mwtv->addItem(new CMenuForwarderNonLocalized((*channels)[j]->getName().c_str(), true, NULL, this, (std::string(cChannelId) + (*channels)[j]->getName()).c_str())); + } + if (!channels->empty()) + mctv.addItem(new CMenuForwarderNonLocalized(g_bouquetManager->Bouquets[i]->Name.c_str(), true, NULL, mwtv)); + + + channels = &(g_bouquetManager->Bouquets[i]->radioChannels); + for(int j = 0; j < (int) channels->size(); j++) { + char cChannelId[3+16+1+1]; + sprintf(cChannelId, "SC:" PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS ",", (*channels)[j]->channel_id); + mwradio->addItem(new CMenuForwarderNonLocalized((*channels)[j]->getName().c_str(), true, NULL, this, (std::string(cChannelId) + (*channels)[j]->getName()).c_str())); + } + if (!channels->empty()) + mcradio.addItem(new CMenuForwarderNonLocalized(g_bouquetManager->Bouquets[i]->Name.c_str(), true, NULL, mwtv)); + } + } + + CMenuWidget mm(LOCALE_TIMERLIST_MODESELECT, NEUTRINO_ICON_SETTINGS); + mm.addItem(new CMenuForwarder(LOCALE_TIMERLIST_MODETV, true, NULL, &mctv)); + mm.addItem(new CMenuForwarder(LOCALE_TIMERLIST_MODERADIO, true, NULL, &mcradio)); + strcpy(timerNew_channel_name,"---"); + CMenuForwarder* m6 = new CMenuForwarder(LOCALE_TIMERLIST_CHANNEL, true, timerNew_channel_name, &mm); + + CMountChooser recDirs(LOCALE_TIMERLIST_RECORDING_DIR,NEUTRINO_ICON_SETTINGS,NULL,timerNew.recordingDir,g_settings.network_nfs_recordingdir); + if (!recDirs.hasItem()) + { + printf("[CTimerList] warning: no network devices available\n"); + } + CMenuForwarder* m7 = new CMenuForwarder(LOCALE_TIMERLIST_RECORDING_DIR, recDirs.hasItem(),timerNew.recordingDir, &recDirs); + + CMenuOptionChooser* m8 = new CMenuOptionChooser(LOCALE_TIMERLIST_STANDBY, &timerNew_standby_on, TIMERLIST_STANDBY_OPTIONS, TIMERLIST_STANDBY_OPTION_COUNT, false); + + CStringInputSMS timerSettings_msg(LOCALE_TIMERLIST_MESSAGE, timerNew.message, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789-.,:!?/ "); + CMenuForwarder *m9 = new CMenuForwarder(LOCALE_TIMERLIST_MESSAGE, false, timerNew.message, &timerSettings_msg ); + + strcpy(timerNew.pluginName,"---"); + CPluginChooser plugin_chooser(LOCALE_TIMERLIST_PLUGIN, CPlugins::P_TYPE_SCRIPT | CPlugins::P_TYPE_TOOL, timerNew.pluginName); + CMenuForwarder *m10 = new CMenuForwarder(LOCALE_TIMERLIST_PLUGIN, false, timerNew.pluginName, &plugin_chooser); + + + CTimerListNewNotifier notifier2((int *)&timerNew.eventType, + &timerNew.stopTime,m2,m6,m8,m9,m10,m7, + timerSettings_stopTime.getValue()); + CMenuOptionChooser* m0 = new CMenuOptionChooser(LOCALE_TIMERLIST_TYPE, (int *)&timerNew.eventType, TIMERLIST_TYPE_OPTIONS, TIMERLIST_TYPE_OPTION_COUNT, true, ¬ifier2); + + timerSettings.addItem( m0); + timerSettings.addItem( m1); + timerSettings.addItem( m2); + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem( m3); + timerSettings.addItem( m4); + timerSettings.addItem( m5); + timerSettings.addItem(GenericMenuSeparatorLine); + timerSettings.addItem( m6); + timerSettings.addItem( m7); + timerSettings.addItem( m8); + timerSettings.addItem( m9); + timerSettings.addItem( m10); + + int ret=timerSettings.exec(this,""); + + // delete dynamic created objects + for(unsigned int count=0;countgetText(LOCALE_TIMERLIST_OVERLAPPING_TIMER); + timerbuf += "\n"; + for (CTimerd::TimerList::iterator it = overlappingTimers.begin(); + it != overlappingTimers.end();it++) + { + timerbuf += CTimerList::convertTimerType2String(it->eventType); + timerbuf += " ("; + timerbuf += CTimerList::convertChannelId2String(it->channel_id); // UTF-8 + if(it->epgID != 0) + { + CEPGData epgdata; + //if (g_Sectionsd->getEPGid(it->epgID, it->epg_starttime, &epgdata)) + if (sectionsd_getEPGid(it->epgID, it->epg_starttime, &epgdata)) + { + timerbuf += ":"; + timerbuf += epgdata.title; + } + else if(strlen(it->epgTitle)!=0) + { + timerbuf += ":"; + timerbuf += it->epgTitle; + } + } + timerbuf += ")"; + + timerbuf += ":\n"; + char at[25] = {0}; + struct tm *annTime = localtime(&(it->announceTime)); + strftime(at,20,"%d.%m. %H:%M",annTime); + timerbuf += at; + timerbuf += " - "; + + char st[25] = {0}; + struct tm *sTime = localtime(&(it->stopTime)); + strftime(st,20,"%d.%m. %H:%M",sTime); + timerbuf += st; + timerbuf += "\n"; + //printf("%d\t%d\t%d\n",it->announceTime,it->alarmTime,it->stopTime); + } + //printf("message:\n%s\n",timerbuf.c_str()); + // todo: localize message + //g_Locale->getText(TIMERLIST_OVERLAPPING_MESSAGE); + + return (ShowMsgUTF(LOCALE_MESSAGEBOX_INFO,timerbuf,CMessageBox::mbrNo,CMessageBox::mbNo|CMessageBox::mbYes) == CMessageBox::mbrYes); +} diff --git a/src/gui/timerlist.h b/src/gui/timerlist.h new file mode 100644 index 000000000..037e043c0 --- /dev/null +++ b/src/gui/timerlist.h @@ -0,0 +1,98 @@ +/* + Neutrino-GUI - DBoxII-Project + + Timerliste by Zwen + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __timerlist__ +#define __timerlist__ + +#include + +#include + +#include + +#include + + +class CTimerList : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + int fheight; // Fonthoehe Timerlist-Inhalt + int theight; // Fonthoehe Timerlist-Titel + int buttonHeight; + bool visible; + + CTimerdClient *Timer; + CTimerd::TimerList timerlist; // List of timers + CTimerd::responseGetTimer timerNew; + int timerNew_standby_on; + char timerNew_channel_name[30]; + char m_weekdaysStr[8]; + + int timer_apids_dflt; + int timer_apids_std; + int timer_apids_ac3; + int timer_apids_alt; + + int width; + int height; + int x; + int y; + + int skipEventID; + + void paintItem(int pos); + void paint(); + void paintHead(); + void paintFoot(); + void hide(); + int modifyTimer(); + int newTimer(); + + public: + CTimerList(); + ~CTimerList(); + void updateEvents(void); + int show(); + int exec(CMenuTarget* parent, const std::string & actionKey); + static const char * convertTimerType2String(const CTimerd::CTimerEventTypes type); // UTF-8 + static std::string convertTimerRepeat2String(const CTimerd::CTimerEventRepeat rep); // UTF-8 + static std::string convertChannelId2String(const t_channel_id id); // UTF-8 +}; + +bool askUserOnTimerConflict(time_t announceTime, time_t stopTime); + + +#endif diff --git a/src/gui/update.cpp b/src/gui/update.cpp new file mode 100644 index 000000000..aab010e91 --- /dev/null +++ b/src/gui/update.cpp @@ -0,0 +1,647 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#define SQUASHFS + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +extern int allow_flash; + +#define gTmpPath "/var/update/" +#define gUserAgent "neutrino/softupdater 1.0" + +#define LIST_OF_UPDATES_LOCAL_FILENAME "coolstream.list" +#define UPDATE_LOCAL_FILENAME "update.img" +#define RELEASE_CYCLE "1.0" +#define FILEBROWSER_UPDATE_FILTER "img" + +#define MTD_OF_WHOLE_IMAGE 0 +#define MTD_DEVICE_OF_UPDATE_PART "/dev/mtd2" + +CFlashUpdate::CFlashUpdate() + :CProgressWindow() +{ + setTitle(LOCALE_FLASHUPDATE_HEAD); +} + + + +class CUpdateMenuTarget : public CMenuTarget +{ + int myID; + int * myselectedID; + +public: + CUpdateMenuTarget(const int id, int * const selectedID) + { + myID = id; + myselectedID = selectedID; + } + + virtual int exec(CMenuTarget *, const std::string &) + { + *myselectedID = myID; + return menu_return::RETURN_EXIT_ALL; + } +}; + + +class CNonLocalizedMenuSeparator : public CMenuSeparator +{ + const char * the_text; + +public: + CNonLocalizedMenuSeparator(const char * text, const neutrino_locale_t Text) : CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, Text) + { + the_text = text; + } + + virtual const char * getString(void) + { + return the_text; + } +}; +//#define DEBUG +bool CFlashUpdate::selectHttpImage(void) +{ + CHTTPTool httpTool; + std::string url; + std::string name; + std::string version; + std::string md5; + std::vector updates_lists, urls, names, versions, descriptions, md5s; + char fileTypes[128]; + int selected = -1; + + httpTool.setStatusViewer(this); + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_GETINFOFILE)); // UTF-8 + + CMenuWidget SelectionWidget(LOCALE_FLASHUPDATE_SELECTIMAGE, "softupdate.raw", 600); + SelectionWidget.addItem(GenericMenuSeparator); + SelectionWidget.addItem(GenericMenuBack); + + std::ifstream urlFile(g_settings.softupdate_url_file); +#ifdef DEBUG +printf("[update] file %s\n", g_settings.softupdate_url_file); +#endif + + unsigned int i = 0; + while (urlFile >> url) + { + std::string::size_type startpos, endpos; +#ifdef DEBUG +printf("[update] url %s\n", url.c_str()); +#endif + + /* extract domain name */ + startpos = url.find("//"); + if (startpos == std::string::npos) + { + startpos = 0; + endpos = std::string::npos; + updates_lists.push_back(url.substr(startpos, endpos - startpos)); + } + else + { + //startpos += 2; + //endpos = url.find('/', startpos); + startpos = url.find('/', startpos+2)+1; + endpos = std::string::npos; + updates_lists.push_back(url.substr(startpos, endpos - startpos)); + } + //updates_lists.push_back(url.substr(startpos, endpos - startpos)); + + SelectionWidget.addItem(new CNonLocalizedMenuSeparator(updates_lists.rbegin()->c_str(), LOCALE_FLASHUPDATE_SELECTIMAGE)); + if (httpTool.downloadFile(url, gTmpPath LIST_OF_UPDATES_LOCAL_FILENAME, 20)) + { + std::ifstream in(gTmpPath LIST_OF_UPDATES_LOCAL_FILENAME); + bool enabled; + while (in >> url >> version >> md5 >> std::ws) + { + urls.push_back(url); + versions.push_back(version); + std::getline(in, name); + names.push_back(name); + //std::getline(in, md5); + md5s.push_back(md5); + enabled = true; +#ifdef DEBUG +printf("[update] url %s version %s md5 %s name %s\n", url.c_str(), version.c_str(), md5.c_str(), name.c_str()); +#endif + + CFlashVersionInfo versionInfo(versions[i]); + + if(!allow_flash && (versionInfo.snapshot < '3')) + enabled = false; + fileTypes[i] = versionInfo.snapshot; + std::string description = versionInfo.getType(); + description += ' '; + description += versionInfo.getDate(); + description += ' '; + description += versionInfo.getTime(); + + descriptions.push_back(description); /* workaround since CMenuForwarder does not store the Option String itself */ + + SelectionWidget.addItem(new CMenuForwarderNonLocalized(names[i].c_str(), enabled, descriptions[i].c_str(), new CUpdateMenuTarget(i, &selected))); + i++; + } + } + } + + hide(); + + if (urls.empty()) + { + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, g_Locale->getText(LOCALE_FLASHUPDATE_GETINFOFILEERROR)); // UTF-8 + return false; + } + + SelectionWidget.exec(NULL, ""); + + if (selected == -1) + return false; + + filename = urls[selected]; + newVersion = versions[selected]; + file_md5 = md5s[selected]; + fileType = fileTypes[selected]; +#ifdef DEBUG +printf("[update] filename %s type %c newVersion %s md5 %s\n", filename.c_str(), fileType, newVersion.c_str(), file_md5.c_str()); +#endif + + return true; +} + +bool CFlashUpdate::getUpdateImage(const std::string & version) +{ + CHTTPTool httpTool; + char * fname, dest_name[100]; + httpTool.setStatusViewer(this); + + fname = rindex(filename.c_str(), '/'); + if(fname != NULL) fname++; + else return false; + + sprintf(dest_name, "%s/%s", g_settings.update_dir, fname); + showStatusMessageUTF(std::string(g_Locale->getText(LOCALE_FLASHUPDATE_GETUPDATEFILE)) + ' ' + version); // UTF-8 + + printf("get update (url): %s - %s\n", filename.c_str(), dest_name); + return httpTool.downloadFile(filename, dest_name, 40 ); + //return httpTool.downloadFile(filename, gTmpPath UPDATE_LOCAL_FILENAME, 40 ); +} + +bool CFlashUpdate::checkVersion4Update() +{ + char msg[400]; + CFlashVersionInfo * versionInfo; + neutrino_locale_t msg_body; +#ifdef DEBUG +printf("[update] mode is %d\n", g_settings.softupdate_mode); +#endif + if(g_settings.softupdate_mode==1) //internet-update + { + if(!selectHttpImage()) + return false; + + showLocalStatus(100); + showGlobalStatus(20); + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_VERSIONCHECK)); // UTF-8 + + printf("internet version: %s\n", newVersion.c_str()); + + showLocalStatus(100); + showGlobalStatus(20); + hide(); + + msg_body = LOCALE_FLASHUPDATE_MSGBOX; +#ifdef SQUASHFS + versionInfo = new CFlashVersionInfo(newVersion);//Memory leak: versionInfo + sprintf(msg, g_Locale->getText(msg_body), versionInfo->getDate(), versionInfo->getTime(), versionInfo->getReleaseCycle(), versionInfo->getType()); + + if(fileType < '3') + { + if ((strncmp(RELEASE_CYCLE, versionInfo->getReleaseCycle(), 2) != 0) && + (ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_FLASHUPDATE_WRONGBASE), CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") != CMessageBox::mbrYes)) + { + delete versionInfo; + //ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, g_Locale->getText(LOCALE_FLASHUPDATE_WRONGBASE)); // UTF-8 + return false; + } + + if ((strcmp("Release", versionInfo->getType()) != 0) && + //(ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_FLASHUPDATE_EXPERIMENTALIMAGE), CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") != CMessageBox::mbrYes)) // UTF-8 + (ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_FLASHUPDATE_EXPERIMENTALIMAGE, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") != CMessageBox::mbrYes)) + { + delete versionInfo; + return false; + } + } + + delete versionInfo; +#endif + } + else + { + CFileBrowser UpdatesBrowser; + + CFileFilter UpdatesFilter; + if(allow_flash) UpdatesFilter.addFilter(FILEBROWSER_UPDATE_FILTER); + UpdatesFilter.addFilter("bin"); + UpdatesFilter.addFilter("txt"); + + UpdatesBrowser.Filter = &UpdatesFilter; + + CFile * CFileSelected = NULL; + if (!(UpdatesBrowser.exec(g_settings.update_dir))) + return false; + + CFileSelected = UpdatesBrowser.getSelectedFile(); + + if (CFileSelected == NULL) + return false; + + filename = CFileSelected->Name; + + FILE* fd = fopen(filename.c_str(), "r"); + if(fd) + fclose(fd); + else { + hide(); + printf("flash-file not found: %s\n", filename.c_str()); + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, g_Locale->getText(LOCALE_FLASHUPDATE_CANTOPENFILE)); // UTF-8 + return false; + } + hide(); + char * ptr = rindex(filename.c_str(), '.'); + if(ptr) { + ptr++; + if(!strcmp(ptr, "bin")) fileType = 'A'; + else if(!strcmp(ptr, "txt")) fileType = 'T'; + else if(!allow_flash) return false; + else fileType = 0; +#ifdef DEBUG + printf("[update] manual file type: %s %c\n", ptr, fileType); +#endif + } + + strcpy(msg, g_Locale->getText(LOCALE_FLASHUPDATE_SQUASHFS_NOVERSION)); + msg_body = LOCALE_FLASHUPDATE_MSGBOX_MANUAL; + } + return (ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, msg, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") == CMessageBox::mbrYes); // UTF-8 +} + +int CFlashUpdate::exec(CMenuTarget* parent, const std::string &) +{ + if(parent) + parent->hide(); + + paint(); + + if(!checkVersion4Update()) { + hide(); + return menu_return::RETURN_REPAINT; + } + +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(0,"checking",0,"Update Neutrino"); + CVFD::getInstance()->setMode(CLCD::MODE_PROGRESSBAR2); +#endif // VFD_UPDATE + + showGlobalStatus(19); + paint(); + showGlobalStatus(20); + + if(g_settings.softupdate_mode==1) //internet-update + { + char * fname = rindex(filename.c_str(), '/') +1; + char fullname[255]; + + if(!getUpdateImage(newVersion)) { + hide(); + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, g_Locale->getText(LOCALE_FLASHUPDATE_GETUPDATEFILEERROR)); // UTF-8 + return menu_return::RETURN_REPAINT; + } + sprintf(fullname, "%s/%s", g_settings.update_dir, fname); + filename = std::string(fullname); + } + + showGlobalStatus(40); + + CFlashTool ft; + ft.setMTDDevice(MTD_DEVICE_OF_UPDATE_PART); + ft.setStatusViewer(this); + + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_MD5CHECK)); // UTF-8 + if((g_settings.softupdate_mode==1) && !ft.check_md5(filename, file_md5)) { + hide(); + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, g_Locale->getText(LOCALE_FLASHUPDATE_MD5SUMERROR)); // UTF-8 + return menu_return::RETURN_REPAINT; + } + if(g_settings.softupdate_mode==1) { //internet-update + if ( ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, (fileType < '3') ? "Flash downloaded image ?" : "Install downloaded pack ?", CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") != CMessageBox::mbrYes) // UTF-8 + { + hide(); + return menu_return::RETURN_REPAINT; + } + } + + showGlobalStatus(60); + +#ifdef DEBUG + printf("[update] flash/install filename %s type %c\n", filename.c_str(), fileType); +#endif + if(fileType < '3') { + CNeutrinoApp::getInstance()->exec(NULL, "savesettings"); + sleep(2); + //flash it... +#ifdef DEBUG1 + if(1) +#else + if(!ft.program(filename, 80, 100)) +#endif + { + hide(); + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, ft.getErrorMessage().c_str()); // UTF-8 + return menu_return::RETURN_REPAINT; + } + + //status anzeigen + showGlobalStatus(100); + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_READY)); // UTF-8 + + hide(); + + // Unmount all NFS & CIFS volumes + nfs_mounted_once = false; /* needed by update.cpp to prevent removal of modules after flashing a new cramfs, since rmmod (busybox) might no longer be available */ + CFSMounter::umount(); + + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_FLASHUPDATE_FLASHREADYREBOOT)); // UTF-8 + //system("/etc/init.d/rcK"); + ft.reboot(); + sleep(20000); + } + else if(fileType == 'T') // display file contents + { + FILE* fd = fopen(filename.c_str(), "r"); + if(fd) { + char * buffer; + off_t filesize = lseek(fileno(fd), 0, SEEK_END); + lseek(fileno(fd), 0, SEEK_SET); + buffer =(char *) malloc(filesize+1); + fread(buffer, filesize, 1, fd); + fclose(fd); + buffer[filesize] = 0; + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, buffer, CMessageBox::mbrBack, CMessageBox::mbBack); // UTF-8 + free(buffer); + } + } + else // not image, install + { + char cmd[100]; + sprintf(cmd, "install.sh %s %s", g_settings.update_dir, filename.c_str()); +#ifdef DEBUG1 + printf("[update] calling %s\n", cmd); +#else + printf("[update] calling %s\n", cmd); + system(cmd); +#endif + showGlobalStatus(100); + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_FLASHUPDATE_READY)); // UTF-8 + } + hide(); + return menu_return::RETURN_REPAINT; +} + + +//-------------------------------------------------------------------------------------------------------------- + + +CFlashExpert::CFlashExpert() + :CProgressWindow() +{ + selectedMTD = -1; +} + +void CFlashExpert::readmtd(int readmtd) +{ + char tmp[10]; + sprintf(tmp, "%d", readmtd); + std::string filename = "/tmp/mtd"; + filename += tmp; + filename += ".img"; // US-ASCII (subset of UTF-8 and ISO8859-1) + + if (readmtd == -1) { + filename = "/tmp/flashimage.img"; // US-ASCII (subset of UTF-8 and ISO8859-1) + readmtd = MTD_OF_WHOLE_IMAGE; + } + setTitle(LOCALE_FLASHUPDATE_TITLEREADFLASH); + paint(); + showGlobalStatus(0); + showStatusMessageUTF((std::string(g_Locale->getText(LOCALE_FLASHUPDATE_ACTIONREADFLASH)) + " (" + CMTDInfo::getInstance()->getMTDName(readmtd) + ')')); // UTF-8 + CFlashTool ft; + ft.setStatusViewer( this ); + ft.setMTDDevice(CMTDInfo::getInstance()->getMTDFileName(readmtd)); + + if(!ft.readFromMTD(filename, 100)) { + showStatusMessageUTF(ft.getErrorMessage()); // UTF-8 + sleep(10); + } else { + showGlobalStatus(100); + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_READY)); // UTF-8 + char message[500]; + sprintf(message, g_Locale->getText(LOCALE_FLASHUPDATE_SAVESUCCESS), filename.c_str()); + sleep(1); + hide(); + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, message); + } +} + +void CFlashExpert::writemtd(const std::string & filename, int mtdNumber) +{ + char message[500]; + + sprintf(message, + g_Locale->getText(LOCALE_FLASHUPDATE_REALLYFLASHMTD), + FILESYSTEM_ENCODING_TO_UTF8_STRING(filename).c_str(), + CMTDInfo::getInstance()->getMTDName(mtdNumber).c_str()); + + if (ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, message, CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo, "softupdate.raw") != CMessageBox::mbrYes) // UTF-8 + return; +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(0,"checking",0,"Update Neutrino"); + CVFD::getInstance()->setMode(CLCD::MODE_PROGRESSBAR2); +#endif // VFD_UPDATE + + setTitle(LOCALE_FLASHUPDATE_TITLEWRITEFLASH); + paint(); + showGlobalStatus(0); + CFlashTool ft; + ft.setStatusViewer( this ); + ft.setMTDDevice( CMTDInfo::getInstance()->getMTDFileName(mtdNumber) ); + if(!ft.program( "/tmp/" + filename, 50, 100)) { + showStatusMessageUTF(ft.getErrorMessage()); // UTF-8 + sleep(10); + } else { + showGlobalStatus(100); + showStatusMessageUTF(g_Locale->getText(LOCALE_FLASHUPDATE_READY)); // UTF-8 + sleep(1); + hide(); + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_FLASHUPDATE_FLASHREADYREBOOT)); // UTF-8 + ft.reboot(); + } +} + +void CFlashExpert::showMTDSelector(const std::string & actionkey) +{ + //mtd-selector erzeugen + CMenuWidget* mtdselector = new CMenuWidget(LOCALE_FLASHUPDATE_MTDSELECTOR, "softupdate.raw"); + mtdselector->addItem(GenericMenuSeparator); + mtdselector->addItem(new CMenuForwarder(LOCALE_MESSAGEBOX_CANCEL)); + mtdselector->addItem(GenericMenuSeparatorLine); + CMTDInfo* mtdInfo =CMTDInfo::getInstance(); + for(int x=0;xgetMTDCount();x++) { + char sActionKey[20]; + sprintf(sActionKey, "%s%d", actionkey.c_str(), x); + mtdselector->addItem(new CMenuForwarderNonLocalized(mtdInfo->getMTDName(x).c_str(), true, NULL, this, sActionKey)); + } + mtdselector->exec(NULL,""); + delete mtdselector; +} + +void CFlashExpert::showFileSelector(const std::string & actionkey) +{ + CMenuWidget* fileselector = new CMenuWidget(LOCALE_FLASHUPDATE_FILESELECTOR, "softupdate.raw"); + fileselector->addItem(GenericMenuSeparator); + fileselector->addItem(new CMenuForwarder(LOCALE_MESSAGEBOX_CANCEL)); + fileselector->addItem(GenericMenuSeparatorLine); + struct dirent **namelist; + int n = scandir("/tmp", &namelist, 0, alphasort); + if (n < 0) + { + perror("no flashimages available"); + //should be available... + } + else + { + for(int count=0;countd_name; + int pos = filen.find(".img"); + if(pos!=-1) + { + fileselector->addItem(new CMenuForwarderNonLocalized(filen.c_str(), true, NULL, this, (actionkey + filen).c_str())); +#warning TODO: make sure filen is UTF-8 encoded + } + free(namelist[count]); + } + free(namelist); + } + fileselector->exec(NULL,""); + delete fileselector; +} + +int CFlashExpert::exec(CMenuTarget* parent, const std::string & actionKey) +{ + if(parent) + parent->hide(); + + if(actionKey=="readflash") { + readmtd(-1); + } + else if(actionKey=="writeflash") { + showFileSelector(""); + } + else if(actionKey=="readflashmtd") { + showMTDSelector("readmtd"); + } + else if(actionKey=="writeflashmtd") { + showMTDSelector("writemtd"); + } + else { + int iReadmtd = -1; + int iWritemtd = -1; + sscanf(actionKey.c_str(), "readmtd%d", &iReadmtd); + sscanf(actionKey.c_str(), "writemtd%d", &iWritemtd); + if(iReadmtd!=-1) { + readmtd(iReadmtd); + } + else if(iWritemtd!=-1) { + printf("mtd-write\n\n"); + selectedMTD = iWritemtd; + showFileSelector(""); + } else { + if(selectedMTD==-1) { + writemtd(actionKey, MTD_OF_WHOLE_IMAGE); + } else { + writemtd(actionKey, selectedMTD); + selectedMTD=-1; + } + } + hide(); + return menu_return::RETURN_EXIT_ALL; + } + + hide(); + return menu_return::RETURN_REPAINT; +} diff --git a/src/gui/update.h b/src/gui/update.h new file mode 100644 index 000000000..9728b1cbb --- /dev/null +++ b/src/gui/update.h @@ -0,0 +1,82 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __update__ +#define __update__ + +#include +#include +#include + +#include + +#include + +class CFlashUpdate : public CProgressWindow +{ + private: + std::string filename; + std::string file_md5; + char fileType; + + std::string installedVersion; + std::string newVersion; + + bool selectHttpImage(void); + bool getUpdateImage(const std::string & version); + bool checkVersion4Update(); + + public: + CFlashUpdate(); + int exec( CMenuTarget* parent, const std::string & actionKey ); + +}; + +class CFlashExpert : public CProgressWindow +{ + private: + int selectedMTD; + + void showMTDSelector(const std::string & actionkey); + void showFileSelector(const std::string & actionkey); + + void readmtd(int readmtd); + void writemtd(const std::string & filename, int mtdNumber); + + public: + CFlashExpert(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/gui/upnpbrowser.cpp b/src/gui/upnpbrowser.cpp new file mode 100644 index 000000000..d0ac791e7 --- /dev/null +++ b/src/gui/upnpbrowser.cpp @@ -0,0 +1,1382 @@ +/* + Neutrino-GUI - DBoxII-Project + + UPnP Browser (c) 2007 by Jochen Friedrich + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +extern cVideo * videoDecoder; + +#ifdef ConnectLineBox_Width +#undef ConnectLineBox_Width +#endif +#define ConnectLineBox_Width 15 + +const struct button_label RescanButton = {NEUTRINO_ICON_BUTTON_BLUE , LOCALE_UPNPBROWSER_RESCAN}; +const struct button_label StopButton = {NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_STOP}; +const struct button_label PUpButton = {NEUTRINO_ICON_BUTTON_RED , LOCALE_FILEBROWSER_NEXTPAGE}; +const struct button_label PDownButton = {NEUTRINO_ICON_BUTTON_GREEN , LOCALE_FILEBROWSER_PREVPAGE}; + +//------------------------------------------------------------------------ + +CUpnpBrowserGui::CUpnpBrowserGui() +{ + m_socket = new CUPnPSocket(); + m_frameBuffer = CFrameBuffer::getInstance(); + m_playing_entry_is_shown = false; +} + +//------------------------------------------------------------------------ + +CUpnpBrowserGui::~CUpnpBrowserGui() +{ + delete m_socket; +} + +//------------------------------------------------------------------------ + +int CUpnpBrowserGui::exec(CMenuTarget* parent, const std::string & /*actionKey*/) +{ + + CAudioPlayer::getInstance()->init(); + + if(parent) + { + parent->hide(); + } + + g_Zapit->stopPlayBack(); + + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/mp3.jpg"); + + // tell neutrino we're in audio mode + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , NeutrinoMessages::mode_audio ); + // remember last mode +#if 0 + CZapitClient::responseGetLastChannel firstchannel; + g_Zapit->getLastChannel(firstchannel.channelNumber, firstchannel.mode); + if ((firstchannel.mode == 'r') ? + (CNeutrinoApp::getInstance()->zapto_radio_on_init_done) : + (CNeutrinoApp::getInstance()->zapto_tv_on_init_done)) + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode() | NeutrinoMessages::norezap); + else + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode()); +#endif + m_LastMode=(CNeutrinoApp::getInstance()->getLastMode()); + + m_width=(g_settings.screen_EndX - g_settings.screen_StartX) - ConnectLineBox_Width; + m_height = (g_settings.screen_EndY - g_settings.screen_StartY); + + m_sheight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight(); + m_buttonHeight = std::min(25, m_sheight); + m_theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + m_mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + m_fheight = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getHeight(); + m_title_height = m_mheight*2 + 20 + m_sheight + 4; + m_info_height = m_mheight*2; + m_listmaxshow = (m_height - m_info_height - m_title_height - m_theight - 2*m_buttonHeight) / (m_fheight); + m_height = m_theight + m_info_height + m_title_height + 2*m_buttonHeight + m_listmaxshow * m_fheight; // recalc height + + m_x = (((g_settings.screen_EndX - g_settings.screen_StartX) - (m_width + ConnectLineBox_Width)) / 2) + + g_settings.screen_StartX + ConnectLineBox_Width; + m_y = (((g_settings.screen_EndY- g_settings.screen_StartY) - m_height)/ 2) + g_settings.screen_StartY; + + // Stop sectionsd + g_Sectionsd->setPauseScanning(true); + + m_indexdevice=0; + m_selecteddevice=0; + + selectDevice(); + + if(CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) + CAudioPlayer::getInstance()->stop(); + + //g_Zapit->setStandby(false); + + // Start Sectionsd + g_Sectionsd->setPauseScanning(false); + videoDecoder->StopPicture(); + + CNeutrinoApp::getInstance()->handleMsg( NeutrinoMessages::CHANGEMODE , m_LastMode ); + g_RCInput->postMsg( NeutrinoMessages::SHOW_INFOBAR, 0 ); + + return menu_return::RETURN_EXIT_ALL; +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::splitProtocol(std::string &protocol, std::string &prot, std::string &network, std::string &mime, std::string &additional) +{ + std::string::size_type pos; + std::string::size_type startpos = 0; + + pos = protocol.find(":", startpos); + if (pos != std::string::npos) + { + prot = protocol.substr(startpos, pos-startpos); + startpos = pos + 1; + + pos = protocol.find(":", startpos); + if (pos != std::string::npos) + { + network = protocol.substr(startpos, pos-startpos); + startpos = pos + 1; + + pos = protocol.find(":", startpos); + if (pos != std::string::npos) + { + mime = protocol.substr(startpos, pos-startpos); + startpos = pos + 1; + + pos = protocol.find(":", startpos); + if (pos != std::string::npos) + { + additional = protocol.substr(startpos, pos-startpos); + } + } + } + } +//printf("%s -> %s - %s - %s - %s\n", protocol.c_str(), prot.c_str(), network.c_str(), mime.c_str(), additional.c_str()); +} + +//------------------------------------------------------------------------ + +std::vector *CUpnpBrowserGui::decodeResult(std::string result) +{ + XMLTreeParser *parser; + XMLTreeNode *root, *node, *snode; + std::vector *entries; + + parser = new XMLTreeParser("UTF-8"); + parser->Parse(result.c_str(), result.size(), 1); + root=parser->RootNode(); + if (!root){ + delete parser; + return NULL; + } + entries = new std::vector; + + for (node=root->GetChild(); node; node=node->GetNext()) + { + bool isdir; + std::string title, artist = "", album = "", id, children; + char *type, *p; + + if (!strcmp(node->GetType(), "container")) + { + std::vector resources; + isdir=true; + for (snode=node->GetChild(); snode; snode=snode->GetNext()) + { + type=snode->GetType(); + p = strchr(type,':'); + if (p) + type=p+1; + if (!strcmp(type,"title")) + { + p=snode->GetData(); + if (!p) + p=(char *) ""; + title=std::string(p); + } + } + p = node->GetAttributeValue((char *) "id"); + if (!p) + p=(char *) ""; + id=std::string(p); + + p = node->GetAttributeValue((char *) "childCount"); + if (!p) + p=(char *) ""; + children=std::string(p); + + UPnPEntry entry={id, isdir, title, artist, album, children, resources, -1}; + entries->push_back(entry); + } + if (!strcmp(node->GetType(), "item")) + { + std::vector resources; + int preferred = -1; + std::string protocol, prot, network, mime, additional; + isdir=false; + for (snode=node->GetChild(); snode; snode=snode->GetNext()) + { + std::string duration, url, size; + unsigned int i; + type=snode->GetType(); + p = strchr(type,':'); + if (p) + type=p+1; + + if (!strcmp(type,"title")) + { + p=snode->GetData(); + if (!p) + p=(char *) ""; + title=std::string(p); + } + else if (!strcmp(type,"artist")) + { + p=snode->GetData(); + if (!p) + p=(char *) ""; + artist=std::string(p); + } + else if (!strcmp(type,"album")) + { + p=snode->GetData(); + if (!p) + p=(char *) ""; + album=std::string(p); + } + else if (!strcmp(type,"res")) + { + p = snode->GetData(); + if (!p) + p=(char *) ""; + url=std::string(p); + p = snode->GetAttributeValue((char *) "size"); + if (!p) + p=(char *) "0"; + size=std::string(p); + p = snode->GetAttributeValue((char *) "duration"); + if (!p) + p=(char *) ""; + duration=std::string(p); + p = snode->GetAttributeValue((char *) "protocolInfo"); + if (!p) + p=(char *) ""; + protocol=std::string(p); + UPnPResource resource = {url, protocol, size, duration}; + resources.push_back(resource); + } + int pref=0; + preferred=-1; + for (i=0; iGetAttributeValue((char *) "id"); + if (!p) + p=(char *) ""; + id=std::string(p); + + p = node->GetAttributeValue((char *) "childCount"); + if (!p) + p=(char *) ""; + children=std::string(p); + + UPnPEntry entry={id, isdir, title, artist, album, children, resources, preferred}; + + entries->push_back(entry); + } + } + delete parser; + return entries; +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::selectDevice() +{ + bool loop = true; + bool changed = true; + neutrino_msg_t msg; + neutrino_msg_data_t data; + + CHintBox *scanBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_UPNPBROWSER_SCANNING)); // UTF-8 + scanBox->paint(); +#if 0 + try { + m_devices = m_socket->Discover("urn:schemas-upnp-org:service:ContentDirectory:1"); + } + catch (std::runtime_error error) + { + delete scanBox; + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + return; + } +#endif + m_devices = m_socket->Discover("urn:schemas-upnp-org:service:ContentDirectory:1"); + scanBox->hide(); + + if (!m_devices.size()) + { + ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_UPNPBROWSER_NOSERVERS, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + delete scanBox; + return; + } + + while (loop) + { + if (changed) + { + paintDevice(); + changed=false; + } + + g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display + neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; + + if( msg == CRCInput::RC_timeout) + { + // nothing + } + + else if( msg == CRCInput::RC_home) + { + loop=false; + } + + else if (msg_repeatok == CRCInput::RC_up && m_selecteddevice > 0) + { + m_selecteddevice--; + if (m_selecteddevice < m_indexdevice) + m_indexdevice-=m_listmaxshow; + changed=true; + } + + else if (msg_repeatok == CRCInput::RC_down && m_selecteddevice + 1 < m_devices.size()) + { + m_selecteddevice++; + if (m_selecteddevice + 1 > m_indexdevice + m_listmaxshow) + m_indexdevice+=m_listmaxshow; + changed=true; + } + + else if( msg == CRCInput::RC_right || msg == CRCInput::RC_ok) + { + m_folderplay = false; + selectItem("0"); + changed=true; + } + + else if( msg == CRCInput::RC_blue) + { + scanBox->paint(); +#if 0 + try { + m_devices = m_socket->Discover("urn:schemas-upnp-org:service:ContentDirectory:1"); + } + catch (std::runtime_error error) + { + delete scanBox; + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + return; + } +#endif + m_devices = m_socket->Discover("urn:schemas-upnp-org:service:ContentDirectory:1"); + scanBox->hide(); + if (!m_devices.size()) + { + ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_UPNPBROWSER_NOSERVERS, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + delete scanBox; + return; + } + changed=true; + } + + else if(msg == NeutrinoMessages::RECORD_START || + msg == NeutrinoMessages::ZAPTO || + msg == NeutrinoMessages::STANDBY_ON || + msg == NeutrinoMessages::SHUTDOWN || + msg == NeutrinoMessages::SLEEPTIMER) + { + loop=false; + g_RCInput->postMsg(msg, data); + } + + else if(msg == NeutrinoMessages::EVT_TIMER) + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + loop = false; + changed=true; + } + } + delete scanBox; +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::playnext(void) +{ + while (true) + { + std::listattribs; + std::listresults; + std::list::iterator i; + std::stringstream sindex; + std::vector *entries = NULL; + bool rfound = false; + bool nfound = false; + bool tfound = false; + + sindex << m_playid; + attribs.push_back(UPnPAttribute("ObjectID", m_playfolder)); + attribs.push_back(UPnPAttribute("BrowseFlag", "BrowseDirectChildren")); + attribs.push_back(UPnPAttribute("Filter", "*")); + attribs.push_back(UPnPAttribute("StartingIndex", sindex.str())); + attribs.push_back(UPnPAttribute("RequestedCount", "1")); + attribs.push_back(UPnPAttribute("SortCriteria", "")); + +#if 0 + try + { + results=m_devices[m_selecteddevice].SendSOAP("urn:schemas-upnp-org:service:ContentDirectory:1", "Browse", attribs); + } + catch (std::runtime_error error) + { + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + m_folderplay = false; + return; + } +#endif + results=m_devices[m_selecteddevice].SendSOAP("urn:schemas-upnp-org:service:ContentDirectory:1", "Browse", attribs); + for (i=results.begin(); i!=results.end(); i++) + { + if (i->first=="NumberReturned") + { + if (atoi(i->second.c_str()) != 1) + { + m_folderplay = false; + return; + } + nfound=true; + } + if (i->first=="TotalMatches") + { + tfound=true; + } + if (i->first=="Result") + { + entries=decodeResult(i->second); + rfound=true; + } + } + m_playid++; + if ((entries != NULL) && (!(*entries)[0].isdir)) + { + int preferred=(*entries)[0].preferred; + if (preferred != -1) + { + std::string protocol, prot, network, mime, additional; + protocol=(*entries)[0].resources[preferred].protocol; + splitProtocol(protocol, prot, network, mime, additional); + if (mime == "audio/mpeg") + { + m_playing_entry = (*entries)[0]; + m_playing_entry_is_shown = false; + CAudiofile mp3((*entries)[0].resources[preferred].url, CFile::FILE_MP3); + CAudioPlayer::getInstance()->play(&mp3, g_settings.audioplayer_highprio == 1); + return; + } + else if (mime == "audio/x-vorbis+ogg") + { + m_playing_entry = (*entries)[0]; + m_playing_entry_is_shown = false; + CAudiofile mp3((*entries)[0].resources[preferred].url, CFile::FILE_OGG); + CAudioPlayer::getInstance()->play(&mp3, g_settings.audioplayer_highprio == 1); + return; + } + } + } else { + neutrino_msg_t msg; + neutrino_msg_data_t data; + g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display + + if( msg == CRCInput::RC_home) + { + m_folderplay = false; + break; + } + } + } +} + +//------------------------------------------------------------------------ + +bool CUpnpBrowserGui::selectItem(std::string id) +{ + bool loop = true; + bool endall = false; + bool changed = true; + bool rchanged = true; + neutrino_msg_t msg; + neutrino_msg_data_t data; + std::vector *entries; + unsigned int index, selected, dirnum; + + index=0; + selected=0; + dirnum=0; + entries=NULL; + + while (loop) + { + updateTimes(); + if (rchanged) + { + if (entries) + delete entries; + entries=NULL; + + std::listattribs; + std::listresults; + std::list::iterator i; + std::stringstream sindex; + std::stringstream scount; + unsigned int returned = 0; + + bool rfound = false; + bool nfound = false; + bool tfound = false; + + sindex << index; + scount << m_listmaxshow; + + attribs.push_back(UPnPAttribute("ObjectID", id)); + attribs.push_back(UPnPAttribute("BrowseFlag", "BrowseDirectChildren")); + attribs.push_back(UPnPAttribute("Filter", "*")); + attribs.push_back(UPnPAttribute("StartingIndex", sindex.str())); + attribs.push_back(UPnPAttribute("RequestedCount", scount.str())); + attribs.push_back(UPnPAttribute("SortCriteria", "")); +#if 0 + try + { + results=m_devices[m_selecteddevice].SendSOAP("urn:schemas-upnp-org:service:ContentDirectory:1", "Browse", attribs); + } + catch (std::runtime_error error) + { + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + if (entries) + delete entries; + return endall; + } +#endif + results=m_devices[m_selecteddevice].SendSOAP("urn:schemas-upnp-org:service:ContentDirectory:1", "Browse", attribs); + for (i=results.begin(); i!=results.end(); i++) + { + if (i->first=="NumberReturned") + { + returned=atoi(i->second.c_str()); + nfound=true; + } + if (i->first=="TotalMatches") + { + dirnum=atoi(i->second.c_str()); + tfound=true; + } + if (i->first=="Result") + { + entries=decodeResult(i->second); + rfound=true; + } + } + if (!entries) + return endall; + if (!nfound || !tfound || !rfound) + { + delete entries; + return endall; + } + if (returned != entries->size()) + { + delete entries; + return endall; + } + + if (returned == 0) + { + delete entries; + return endall; + } + rchanged=false; + changed=true; + } + + if (changed) + { + paintItem(entries, selected - index, dirnum - index, index); + changed=false; + } + + g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display + neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; + + if( msg == CRCInput::RC_timeout) + { + // nothing + } + else if(msg == CRCInput::RC_home) + { + loop=false; + endall=true; + } + else if(msg == CRCInput::RC_left) + { + loop=false; + } + + else if (msg_repeatok == CRCInput::RC_up && selected > 0) + { + selected--; + if (selected < index) + { + index-=m_listmaxshow; + rchanged=true; + } + changed=true; + } + + else if(msg == CRCInput::RC_green && selected > 0) + { + if (index > 0) + { + index-=m_listmaxshow; + selected-=m_listmaxshow; + rchanged=true; + } + else + selected=0; + changed=true; + } + + else if (msg_repeatok == CRCInput::RC_down && selected + 1 < dirnum) + { + selected++; + if (selected + 1 > index + m_listmaxshow) + { + index+=m_listmaxshow; + rchanged=true; + } + changed=true; + } + + else if(msg == CRCInput::RC_red && selected + 1 < dirnum) + { + if (index < ((dirnum - 1) / m_listmaxshow) * m_listmaxshow) + { + index+=m_listmaxshow; + selected+=m_listmaxshow; + if (selected + 1 >= dirnum) + selected=dirnum - 1; + rchanged=true; + } + else + selected=dirnum - 1; + changed=true; + } + + else if(msg == CRCInput::RC_right) + { + if ((*entries)[selected - index].isdir) + { + endall=selectItem((*entries)[selected - index].id); + if (endall) + loop=false; + } + changed=true; + } + else if(msg == CRCInput::RC_ok) + { + if (!(*entries)[selected - index].isdir) + { + m_folderplay = false; + int preferred=(*entries)[selected - index].preferred; + if (preferred != -1) + { + std::string protocol, prot, network, mime, additional; + protocol=(*entries)[selected - index].resources[preferred].protocol; + splitProtocol(protocol, prot, network, mime, additional); + if (mime == "audio/mpeg") + { + CAudiofile mp3((*entries)[selected - index].resources[preferred].url, CFile::FILE_MP3); + CAudioPlayer::getInstance()->play(&mp3, g_settings.audioplayer_highprio == 1); + } + else if (mime == "audio/x-vorbis+ogg") + { + CAudiofile mp3((*entries)[selected - index].resources[preferred].url, CFile::FILE_OGG); + CAudioPlayer::getInstance()->play(&mp3, g_settings.audioplayer_highprio == 1); + } + m_playing_entry = (*entries)[selected - index]; +#if 0 +// #ifdef ENABLE_PICTUREVIEWER + else if ((mime == "image/gif") || (mime == "image/jpeg")) + { + CPictureViewer *viewer = new CPictureViewer(); + bool loop=true; + viewer->SetScaling((CPictureViewer::ScalingMode)g_settings.picviewer_scaling); + viewer->SetVisible(g_settings.screen_StartX, g_settings.screen_EndX, g_settings.screen_StartY, g_settings.screen_EndY); + + if(g_settings.video_Format==1) + viewer->SetAspectRatio(16.0/9); + else if(g_settings.video_Format==0) + { + CControldClient cdc; + cdc.setVideoFormat(CControldClient::VIDEOFORMAT_4_3); + viewer->SetAspectRatio(4.0/3); + } + else + viewer->SetAspectRatio(4.0/3); + + m_frameBuffer->setMode(720, 576, 16); + m_frameBuffer->setTransparency(0); + viewer->ShowImage((*entries)[selected - index].resources[preferred].url, true); + while (loop) + { + g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display + + if( msg == CRCInput::RC_home) + loop=false; + } + m_frameBuffer->setMode(720, 576, 8 * sizeof(fb_pixel_t)); + m_frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + m_frameBuffer->ClearFrameBuffer(); + delete viewer; + } +// #endif +#endif + } + + } else { + m_folderplay = true; + m_playfolder = (*entries)[selected - index].id; + m_playid = 0; + playnext(); + } + changed=true; + } + else if( msg == CRCInput::RC_yellow) + { + if(CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) + CAudioPlayer::getInstance()->stop(); + m_folderplay = false; + } + + else if(msg == NeutrinoMessages::RECORD_START || + msg == NeutrinoMessages::ZAPTO || + msg == NeutrinoMessages::STANDBY_ON || + msg == NeutrinoMessages::SHUTDOWN || + msg == NeutrinoMessages::SLEEPTIMER) + { + loop = false; + g_RCInput->postMsg(msg, data); + } + + else if(msg == NeutrinoMessages::EVT_TIMER) + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + } + + else + { + if( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + loop = false; + changed=true; + } + + if (m_folderplay && (CAudioPlayer::getInstance()->getState() == CBaseDec::STOP)) + playnext(); + } + if (entries) + delete entries; + return endall; +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintDevicePos(unsigned int pos) +{ + int ypos = m_y + m_title_height + m_theight + pos*m_fheight; + uint8_t color; + fb_pixel_t bgcolor; + + if (pos == m_selecteddevice) + { + color = COL_MENUCONTENT + 2; + bgcolor = COL_MENUCONTENT_PLUS_2; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + m_frameBuffer->paintBoxRel(m_x, ypos, m_width - 15, m_fheight, bgcolor); + + if (pos + m_indexdevice >= m_devices.size()) + return; + + char sNr[20]; + sprintf(sNr, "%2d", pos + 1); + std::string num = sNr; + + std::string name = m_devices[pos + m_indexdevice].friendlyname; + + int w = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getRenderWidth(name) + 5; + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 10, ypos + m_fheight, m_width - 30 - w, + num, color, m_fheight, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + m_width - 15 - w, ypos + m_fheight, + w, name, color, m_fheight, true); // UTF-8 +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintItemPos(std::vector *entry, unsigned int pos, unsigned int selected) +{ + int ypos = m_y + m_title_height + m_theight + pos*m_fheight; + uint8_t color; + fb_pixel_t bgcolor; + + if (pos == selected) + { + color = COL_MENUCONTENT + 2; + bgcolor = COL_MENUCONTENT_PLUS_2; + paintDetails(entry, pos); + if ((*entry)[pos].isdir) + paintItem2DetailsLine (-1, pos); // clear it + else + paintItem2DetailsLine (pos, pos); + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + m_frameBuffer->paintBoxRel(m_x, ypos, m_width - 15, m_fheight, bgcolor); + + if (pos >= entry->size()) + return; + + int preferred=(*entry)[pos].preferred; + std::string info; + std::string fileicon; + if ((*entry)[pos].isdir) + { + info = "

    "; + fileicon = "folder.raw"; + } + else + { + if (preferred != -1) + { + info = (*entry)[pos].resources[preferred].duration; + fileicon = "mp3.raw"; + } + else + { + info = "(none)"; + fileicon = "file.raw"; + } + } + + std::string name = (*entry)[pos].title; + char tmp_time[] = "00:00:00.0"; + int w = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getRenderWidth(tmp_time); + + m_frameBuffer->paintIcon(fileicon, m_x + 5 , ypos + (m_fheight - 16) / 2); + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + m_width - 15 - w, ypos + m_fheight, + w, info, color, m_fheight); + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 30, ypos + m_fheight, m_width - 50 - w, + name, color, m_fheight, true); // UTF-8 + +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintDevice() +{ + std::string tmp; + int w, xstart, ypos, top; + int c_rad_mid = RADIUS_MID; + + // LCD + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, "Select UPnP Device"); + CVFD::getInstance()->showMenuText(0, m_devices[m_selecteddevice].friendlyname.c_str(), -1, true); + + // Info + m_frameBuffer->paintBoxRel(m_x, m_y, m_width, m_title_height - 10, COL_MENUCONTENT_PLUS_6, c_rad_mid); + m_frameBuffer->paintBoxRel(m_x + 2, m_y + 2, m_width - 4, m_title_height - 14, COL_MENUCONTENTSELECTED_PLUS_0, c_rad_mid); + + // first line + tmp = m_devices[m_selecteddevice].manufacturer + " " + + m_devices[m_selecteddevice].manufacturerurl; + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 1*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + // second line + tmp = m_devices[m_selecteddevice].modelname + " " + + m_devices[m_selecteddevice].modelnumber + " " + + m_devices[m_selecteddevice].modeldescription; + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 2*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + // third line + tmp = m_devices[m_selecteddevice].modelurl; + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 3*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + // Head + tmp = g_Locale->getText(LOCALE_UPNPBROWSER_HEAD); + m_frameBuffer->paintBoxRel(m_x, m_y + m_title_height, m_width, m_theight, COL_MENUHEAD_PLUS_0, c_rad_mid, CORNER_TOP); + m_frameBuffer->paintIcon(NEUTRINO_ICON_UPNP, m_x + 7, m_y + m_title_height + 6); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(m_x + 35, m_y + m_theight + m_title_height + 0, + m_width - 45, tmp, COL_MENUHEAD, 0, true); // UTF-8 + ypos = m_y + m_title_height; + if(m_theight > 26) + ypos = (m_theight - 26) / 2 + m_y + m_title_height; + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, m_x + m_width - 30, ypos); +#if 0 + if( CNeutrinoApp::getInstance()->isMuted() ) + { + xpos = m_x + m_width - 75; + ypos = m_y + m_title_height; + if(m_theight > 32) + ypos = (m_theight - 32) / 2 + m_y + m_title_height; + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_MUTE, xpos, ypos); + } +#endif + + // Items + for (unsigned int count=0;countpaintBoxRel(m_x + m_width - 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc = ((m_devices.size() - 1) / m_listmaxshow) + 1; + int sbs = ((m_selecteddevice) / m_listmaxshow); + + m_frameBuffer->paintBoxRel(m_x + m_width - 13, ypos + 2 + sbs*(sb-4)/sbc, 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3); + + // Foot + top = m_y + (m_height - m_info_height - 2 * m_buttonHeight); + + int ButtonWidth = (m_width - 20) / 4; + m_frameBuffer->paintBoxRel(m_x, top, m_width, 1 * m_buttonHeight, COL_MENUHEAD_PLUS_0, c_rad_mid, CORNER_BOTTOM); + m_frameBuffer->paintHLine(m_x, m_x + m_width, top, COL_INFOBAR_SHADOW_PLUS_0); + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 1, &RescanButton); + + clearItem2DetailsLine(); // clear it +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintItem(std::vector *entry, unsigned int selected, unsigned int max, unsigned int offset) +{ + std::string tmp; + std::stringstream ts; + int w, xstart, ypos, top; + int preferred=(*entry)[selected].preferred; + + // LCD + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, "Select UPnP Entry"); + CVFD::getInstance()->showMenuText(0, (*entry)[selected].title.c_str(), -1, true); + + // Info + m_frameBuffer->paintBoxRel(m_x, m_y, m_width, m_title_height - 10, COL_MENUCONTENT_PLUS_6); + m_frameBuffer->paintBoxRel(m_x + 2, m_y + 2, m_width - 4, m_title_height - 14, COL_MENUCONTENTSELECTED_PLUS_0); + + // first line + ts << "Resources: " << (*entry)[selected].resources.size() << " Selected: " << preferred+1 << " "; + tmp = ts.str(); + + if (preferred != -1) + tmp = tmp + "Duration: " + (*entry)[selected].resources[preferred].duration; + else + tmp = tmp + "No resource for Item"; + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + if (w > m_width - 20) + w = m_width - 20; + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 1*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + // second line + if ((*entry)[selected].isdir) + tmp = "Directory"; + else + { + tmp = ""; + if (preferred != -1) + { + std::string proto, network, mime, info; + splitProtocol((*entry)[selected].resources[preferred].protocol, proto, network, mime, info); + tmp = "Protocol: " + proto + ", MIME-Type: " + mime; + } + + } + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + if (w > m_width - 20) + w = m_width - 20; + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 2*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + //third line + tmp = ""; + if (!(*entry)[selected].isdir) + { + if (preferred != -1) + { + tmp = "URL: " + (*entry)[selected].resources[preferred].url; + } + + } + w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(tmp, true); // UTF-8 + if (w > m_width - 20) + w = m_width - 20; + xstart = (m_width - w) / 2; + if(xstart < 10) + xstart = 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(m_x + xstart, m_y + 4 + 3*m_mheight, m_width - 20, + tmp, COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + + + // Head + tmp = g_Locale->getText(LOCALE_UPNPBROWSER_HEAD); + m_frameBuffer->paintBoxRel(m_x, m_y + m_title_height, m_width, m_theight, COL_MENUHEAD_PLUS_0); + m_frameBuffer->paintIcon(NEUTRINO_ICON_UPNP, m_x + 7, m_y + m_title_height + 6); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(m_x + 35, m_y + m_theight + m_title_height + 0, + m_width - 45, tmp, COL_MENUHEAD, 0, true); // UTF-8 + ypos = m_y + m_title_height; + if(m_theight > 26) + ypos = (m_theight - 26) / 2 + m_y + m_title_height; + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_DBOX, m_x + m_width - 30, ypos); +#if 0 + if( CNeutrinoApp::getInstance()->isMuted() ) + { + xpos = m_x + m_width - 75; + ypos = m_y + m_title_height; + if(m_theight > 32) + ypos = (m_theight - 32) / 2 + m_y + m_title_height; + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_MUTE, xpos, ypos); + } +#endif + + // Items + for (unsigned int count=0;countpaintBoxRel(m_x + m_width - 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc = ((max + offset - 1) / m_listmaxshow) + 1; + int sbs = ((selected + offset) / m_listmaxshow); + + int sbh = 0; + if ((sbc > 0) && (sbc > sb-4)) + sbh = 2; + m_frameBuffer->paintBoxRel(m_x + m_width - 13, ypos + 2 + sbs*((sb-4)/sbc+sbh), 11, (sb-4)/sbc + sbh, COL_MENUCONTENT_PLUS_3); + + // Foot buttons + top = m_y + (m_height - m_info_height - 2 * m_buttonHeight); + int ButtonWidth = (m_width - 20) / 4; + m_frameBuffer->paintBoxRel(m_x, top, m_width, 1 * m_buttonHeight+2, COL_MENUHEAD_PLUS_0); + m_frameBuffer->paintHLine(m_x, m_x + m_width, top, COL_INFOBAR_SHADOW_PLUS_0); + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 10, top + 4, ButtonWidth, 1, &StopButton); + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + ButtonWidth + 10, top + 4, ButtonWidth, 1, &PUpButton); + ::paintButtons(m_frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, + m_x + 2 * ButtonWidth + 10, top + 4, ButtonWidth, 1, &PDownButton); + + m_frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, m_x + 3 * ButtonWidth + 10, top + 1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(m_x + 3 * ButtonWidth + 40, + top + 19 + 4, ButtonWidth - 40, + g_Locale->getText(LOCALE_AUDIOPLAYER_PLAY), COL_INFOBAR, 0, true); // UTF-8 +} + + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintDetails(std::vector *entry, unsigned int index, bool use_playing) +{ + // Foot info + int top = m_y + (m_height - m_info_height - 1 * m_buttonHeight) + 2; + + if ((!use_playing) && ((*entry)[index].isdir)) + { + m_frameBuffer->paintBackgroundBoxRel(m_x, top + 2, m_width, 2 * m_buttonHeight); + } + else + { +// char cNoch[50]; // UTF-8 +// char cSeit[50]; // UTF-8 + + if (use_playing) { + if (!m_playing_entry_is_shown) { + m_frameBuffer->paintBoxRel(m_x, top + 2, m_width-2, 2 * m_buttonHeight, COL_MENUCONTENTDARK_PLUS_0); + m_playing_entry_is_shown = true; + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 4, + top + 1 * m_buttonHeight + 4, m_x + m_width - 8, m_playing_entry.title + " - " + + m_playing_entry.artist, COL_INFOBAR, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 4, + top + 2 * m_buttonHeight + 4, m_x + m_width - 8, m_playing_entry.album, COL_INFOBAR, 0, true); // UTF-8 + } + } else { + if (entry == NULL) return; + m_frameBuffer->paintBoxRel(m_x, top + 2, m_width-2, 2 * m_buttonHeight, COL_MENUCONTENTDARK_PLUS_0); + m_playing_entry_is_shown = false; + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 4, + top + 1 * m_buttonHeight + 4, m_x + m_width - 8, (*entry)[index].title + " - " + + (*entry)[index].artist, COL_INFOBAR, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 4, + top + 2 * m_buttonHeight + 4, m_x + m_width - 8, (*entry)[index].album, COL_INFOBAR, 0, true); // UTF-8 + } +//// printf("title = %s\n", (*entry)[selected].title.c_str()); +// printf("artist = %s\n", (*entry)[selected].artist.c_str()); +// printf("album = %s\n", (*entry)[selected].album.c_str()); +// printf("children = %s\n", (*entry)[selected].children.c_str()); +// printf("id = %s\n", (*entry)[selected].id.c_str()); +#if 0 + struct tm *pStartZeit = localtime(&chanlist[index]->currentEvent.startTime); + unsigned seit = ( time(NULL) - chanlist[index]->currentEvent.startTime ) / 60; + sprintf( cSeit, g_Locale->getText(LOCALE_CHANNELLIST_SINCE), pStartZeit->tm_hour, pStartZeit->tm_min); //, seit ); + int seit_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->getRenderWidth(cSeit, true); // UTF-8 + + int noch = ( chanlist[index]->currentEvent.startTime + chanlist[index]->currentEvent.duration - time(NULL) ) / 60; + if ( (noch< 0) || (noch>=10000) ) + noch= 0; + sprintf( cNoch, "(%d / %d min)", seit, noch ); + int noch_len = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->getRenderWidth(cNoch, true); // UTF-8 + + std::string text1= chanlist[index]->currentEvent.description; + std::string text2= chanlist[index]->currentEvent.text; + + int xstart = 10; + if (g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text1) > (width - 30 - seit_len) ) + { + // zu breit, Umbruch versuchen... + int pos; + do + { + pos = text1.find_last_of("[ -.]+"); + if ( pos!=-1 ) + text1 = text1.substr( 0, pos ); + } while ( ( pos != -1 ) && (g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text1) > (width - 30 - seit_len) ) ); + + std::string text3= chanlist[index]->currentEvent.description.substr(text1.length()+ 1); + if (!(text2.empty())) + text3 += " . "; + + xstart += g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getRenderWidth(text3); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 10, y+ height+ 5+ 2* fheight, width - 30- noch_len, text3, COL_MENUCONTENTDARK); + } + + if (!(text2.empty())) + { + while ( text2.find_first_of("[ -.+*#?=!$%&/]+") == 0 ) + text2 = text2.substr( 1 ); + text2 = text2.substr( 0, text2.find('\n') ); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString(x+ xstart, y+ height+ 5+ 2* fheight, width- xstart- 20- noch_len, text2, COL_MENUCONTENTDARK); + } + + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x+ 10, y+ height+ 5+ fheight, width - 30 - seit_len, text1, COL_MENUCONTENTDARK); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR]->RenderString (x+ width- 10- seit_len, y+ height+ 5+ fheight , seit_len, cSeit, COL_MENUCONTENTDARK, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x+ width- 10- noch_len, y+ height+ 5+ 2* fheight- 2, noch_len, cNoch, COL_MENUCONTENTDARK, 0, true); // UTF-8 +#endif + + } +} + +//------------------------------------------------------------------------ + +// +// -- Decoreline to connect ChannelDisplayLine with ChannelDetail display +// -- 2002-03-17 rasc +// + +void CUpnpBrowserGui::clearItem2DetailsLine () +{ + paintItem2DetailsLine (-1, 0); +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::paintItem2DetailsLine (int pos, unsigned int /*ch_index*/) +{ + int xpos = m_x - ConnectLineBox_Width; + int ypos1 = m_y + m_title_height+0 + m_theight + pos*m_fheight; + int ypos2 = m_y + (m_height - m_info_height - 1 * m_buttonHeight) + 2; + + int ypos1a = ypos1 + (m_fheight/2)-2; + int ypos2a = ypos2 + (m_info_height/2)-2; + fb_pixel_t col1 = COL_MENUCONTENT_PLUS_6; + fb_pixel_t col2 = COL_MENUCONTENT_PLUS_1; + + // Clear + m_frameBuffer->paintBackgroundBoxRel(xpos, m_y + m_title_height, ConnectLineBox_Width, m_height+m_info_height-(m_y + m_title_height)); + if (pos < 0) { + m_frameBuffer->paintBackgroundBoxRel(m_x, m_y + (m_height - m_info_height - 1 * m_buttonHeight) + 2, m_width, m_info_height); + } + // paint Line if detail info (and not valid list pos) + if (pos >= 0) + { + // 1. col thick line + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos1, 4, m_fheight, col1); + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos2, 4, m_info_height, col1); + + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-15, ypos1a, 4, ypos2a-ypos1a, col1); + + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-15, ypos1a, 12, 4, col1); + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-15, ypos2a, 12, 4, col1); + + // 2. col small line + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos1, 1, m_fheight, col2); + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-4, ypos2, 1, m_info_height, col2); + + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-15, ypos1a, 1, ypos2a-ypos1a+4, col2); + + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-15, ypos1a, 12, 1, col2); + m_frameBuffer->paintBoxRel(xpos+ConnectLineBox_Width-12, ypos2a, 8, 1, col2); + + // -- small Frame around infobox + m_frameBuffer->paintBoxRel(m_x , ypos2, 2, m_info_height, col1); + m_frameBuffer->paintBoxRel(m_x+m_width-2, ypos2, 2, m_info_height, col1); + m_frameBuffer->paintBoxRel(m_x , ypos2, m_width-2, 2, col1); + m_frameBuffer->paintBoxRel(m_x , ypos2+m_info_height-2, m_width-2, 2, col1); + } +} + +//------------------------------------------------------------------------ + +void CUpnpBrowserGui::updateTimes(const bool force) +{ + int top; + if(CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) + { + bool updatePlayed = force; + + if ((m_time_played != CAudioPlayer::getInstance()->getTimePlayed())) + { + m_time_played = CAudioPlayer::getInstance()->getTimePlayed(); + updatePlayed = true; + } + char play_time[8]; + snprintf(play_time, 7, "%ld:%02ld", m_time_played / 60, m_time_played % 60); + char tmp_time[] = "000:00"; + int w = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(tmp_time); + + if (updatePlayed) + { + paintDetails(NULL, 0, true); + top = m_y + (m_height - m_info_height - 1 * m_buttonHeight) + m_buttonHeight + 4; + m_frameBuffer->paintBoxRel(m_x + m_width - w - 15, top + 1, w + 4, m_buttonHeight, + COL_MENUCONTENTDARK_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(m_x + m_width - w - 11, top + 1 + m_buttonHeight, w, play_time, COL_MENUHEAD); + } + } +} + diff --git a/src/gui/upnpbrowser.h b/src/gui/upnpbrowser.h new file mode 100644 index 000000000..03b6cc7b8 --- /dev/null +++ b/src/gui/upnpbrowser.h @@ -0,0 +1,115 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __upnpplayergui__ +#define __upnpplayergui__ + +#include "driver/framebuffer.h" +#include "driver/audiofile.h" +#include "driver/pictureviewer/pictureviewer.h" +#include "gui/filebrowser.h" +#include "gui/widget/menue.h" + +#include +#include +#include + +struct UPnPResource +{ + std::string url; + std::string protocol; + std::string size; + std::string duration; +}; + +struct UPnPEntry +{ + std::string id; + bool isdir; + std::string title; + std::string artist; + std::string album; + std::string children; + std::vector resources; + int preferred; +}; + +class CUpnpBrowserGui : public CMenuTarget +{ + public: + CUpnpBrowserGui(); + ~CUpnpBrowserGui(); + int exec(CMenuTarget* parent, const std::string & actionKey); + + private: + std::vector m_devices; + UPnPEntry m_playing_entry; + CUPnPSocket * m_socket; + CFrameBuffer * m_frameBuffer; + bool m_vol_ost; + int m_LastMode; + int m_width; + int m_height; + int m_x; + int m_y; + unsigned int m_listmaxshow; + unsigned int m_indexdevice; + unsigned int m_selecteddevice; + int m_fheight; // Fonthoehe Inhalt + int m_theight; // Fonthoehe Titel + int m_mheight; // Fonthoehe Info + int m_sheight; // Fonthoehe Status + int m_buttonHeight; + int m_title_height; + int m_info_height; + bool m_folderplay; + std::string m_playfolder; + int m_playid; + time_t m_time_played; + bool m_playing_entry_is_shown; + + void selectDevice(); + bool selectItem(std::string); + void paintItem(std::vector *entry, unsigned int selected, unsigned int max, unsigned int offset); + void paintDevice(); + std::vector *decodeResult(std::string); + void playnext(); + void splitProtocol(std::string &protocol, std::string &prot, std::string &network, std::string &mime, std::string &additional); + void paintItemPos (std::vector *entry, unsigned int pos, unsigned int selected); + void paintDevicePos(unsigned int pos); + void paintDetails(std::vector *entry, unsigned int index, bool use_playing = false); + void clearItem2DetailsLine (void); + void paintItem2DetailsLine (int pos,unsigned int ch_index); + + void updateTimes(const bool force = false); +}; + +#endif diff --git a/src/gui/widget/Makefile.am b/src/gui/widget/Makefile.am new file mode 100644 index 000000000..0f43cd31e --- /dev/null +++ b/src/gui/widget/Makefile.am @@ -0,0 +1,24 @@ +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/libnet \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libcoolstream \ + @FREETYPE_CFLAGS@ + +noinst_LIBRARIES = libneutrino_gui_widget.a libneutrino_gui_widget2.a + +libneutrino_gui_widget_a_SOURCES = \ + buttons.cpp \ + colorchooser.cpp keychooser.cpp \ + menue.cpp stringinput.cpp stringinput_ext.cpp \ + messagebox.cpp hintbox.cpp listbox.cpp listbox_legacy.cpp \ + rgbcsynccontroler.cpp \ + drawable.cpp hintboxext.cpp helpbox.cpp mountchooser.cpp \ + listframe.cpp msgbox.cpp textbox.cpp + +libneutrino_gui_widget2_a_SOURCES = \ + progresswindow.cpp vfdcontroler.cpp diff --git a/src/gui/widget/buttons.cpp b/src/gui/widget/buttons.cpp new file mode 100644 index 000000000..37c7c75e5 --- /dev/null +++ b/src/gui/widget/buttons.cpp @@ -0,0 +1,37 @@ +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/gui/widget/buttons.cpp,v 1.2 2004/03/14 22:20:05 thegoodguy Exp $ + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +void paintButtons(CFrameBuffer * const frameBuffer, Font * const font, const CLocaleManager * const localemanager, const int x, const int y, const unsigned int buttonwidth, const unsigned int count, const struct button_label * const content) +{ + for (unsigned int i = 0; i < count; i++) + { + frameBuffer->paintIcon(content[i].button, x + i * buttonwidth, y); + font->RenderString(x + i * buttonwidth + 20, y + 19, buttonwidth - 20, localemanager->getText(content[i].locale), COL_INFOBAR, 0, true); // UTF-8 + } +} diff --git a/src/gui/widget/buttons.h b/src/gui/widget/buttons.h new file mode 100644 index 000000000..96e241cfd --- /dev/null +++ b/src/gui/widget/buttons.h @@ -0,0 +1,37 @@ +#ifndef __gui_widget_buttons_h__ +#define __gui_widget_buttons_h__ + +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/gui/widget/buttons.h,v 1.2 2004/05/25 07:44:16 thegoodguy Exp $ + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +typedef struct button_label +{ + const char * button; + neutrino_locale_t locale; +} button_label_struct; + +void paintButtons(CFrameBuffer * const frameBuffer, Font * const font, const CLocaleManager * const localemanager, const int x, const int y, const unsigned int buttonwidth, const unsigned int count, const struct button_label * const content); + +#endif /* __gui_widget_buttons_h__ */ diff --git a/src/gui/widget/colorchooser.cpp b/src/gui/widget/colorchooser.cpp new file mode 100644 index 000000000..5065a05d7 --- /dev/null +++ b/src/gui/widget/colorchooser.cpp @@ -0,0 +1,256 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#define ROUND_RADIUS 8 + +#define VALUE_R 0 +#define VALUE_G 1 +#define VALUE_B 2 +#define VALUE_ALPHA 3 + +static const char * const iconnames[4] = { + "volumeslider2red.raw", + "volumeslider2green.raw", + "volumeslider2blue.raw", + "volumeslider2alpha.raw" +}; + +static const neutrino_locale_t colorchooser_names[4] = +{ + LOCALE_COLORCHOOSER_RED , + LOCALE_COLORCHOOSER_GREEN, + LOCALE_COLORCHOOSER_BLUE , + LOCALE_COLORCHOOSER_ALPHA +}; + +CColorChooser::CColorChooser(const neutrino_locale_t Name, unsigned char *R, unsigned char *G, unsigned char *B, unsigned char* Alpha, CChangeObserver* Observer) // UTF-8 +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + observer = Observer; + name = Name; + width = w_max(360, 0); + height = h_max(hheight+ mheight* 4, 0); + + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height)>>1); + + value[VALUE_R] = R; + value[VALUE_G] = G; + value[VALUE_B] = B; + value[VALUE_ALPHA] = Alpha; +} + +void CColorChooser::setColor() +{ + int color = convertSetupColor2RGB(*(value[VALUE_R]), *(value[VALUE_G]), *(value[VALUE_B])); + int tAlpha = (value[VALUE_ALPHA]) ? (convertSetupAlpha2Alpha(*(value[VALUE_ALPHA]))) : 0; + + if(!value[VALUE_ALPHA]) tAlpha = 0xFF; + + fb_pixel_t col = ((tAlpha << 24) & 0xFF000000) | color; + //((tAlpha << 24) & 0xFF000000) | ((color << 16) & 0x00FF0000) | (color & 0x0000FF00) | ((color >> 16) & 0xFF); + frameBuffer->paintBoxRel(x+222,y+hheight+2+5, mheight*4-4 ,mheight*4-4-10, col); +} + +int CColorChooser::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + if (parent) + parent->hide(); + + unsigned char r_alt= *value[VALUE_R]; + unsigned char g_alt= *value[VALUE_G]; + unsigned char b_alt= *value[VALUE_B]; + unsigned char a_alt = (value[VALUE_ALPHA]) ? (*(value[VALUE_ALPHA])) : 0; + + paint(); + setColor(); + + int selected = 0; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings + ::TIMING_MENU]); + + bool loop=true; + while (loop) { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd, true); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings + ::TIMING_MENU]); + + switch ( msg ) { + case CRCInput::RC_down: + { + if (selected < ((value[VALUE_ALPHA]) ? 3 : 2)) + { + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], false); + selected++; + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + } else { + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], false); + selected = 0; + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + } + break; + + } + case CRCInput::RC_up: + { + if (selected > 0) + { + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], false); + selected--; + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + } else { + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], false); + selected = ((value[VALUE_ALPHA]) ? 3 : 2); + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + } + break; + } + case CRCInput::RC_right: + { + if ((*value[selected]) < 100) + { + if ((*value[selected]) < 98) + (*value[selected]) += 2; + else + (*value[selected]) = 100; + + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + setColor(); + } + break; + } + case CRCInput::RC_left: + { + if ((*value[selected]) > 0) + { + if ((*value[selected]) > 2) + (*value[selected]) -= 2; + else + (*value[selected]) = 0; + + paintSlider(x + 10, y + hheight + mheight * selected, value[selected], colorchooser_names[selected], iconnames[selected], true); + setColor(); + } + break; + } + case CRCInput::RC_home: + if (((*value[VALUE_R] != r_alt) || (*value[VALUE_G] != g_alt) || (*value[VALUE_B] != b_alt) || ((value[VALUE_ALPHA]) && (*(value[VALUE_ALPHA]) != a_alt))) && + (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + break; + + // sonst abbruch... + *value[VALUE_R] = r_alt; + *value[VALUE_G] = g_alt; + *value[VALUE_B] = b_alt; + if (value[VALUE_ALPHA]) + *value[VALUE_ALPHA] = a_alt; + + case CRCInput::RC_sat: + case CRCInput::RC_favorites: + break; + case CRCInput::RC_timeout: + case CRCInput::RC_ok: + loop = false; + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + hide(); + + if(observer) + observer->changeNotify(name, NULL); + + return res; +} + +void CColorChooser::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CColorChooser::paint() +{ + //frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); //round + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + //frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + for (int i = 0; i < 4; i++) + paintSlider(x + 10, y + hheight + mheight * i, value[i], colorchooser_names[i], iconnames[i], (i == 0)); + + //color preview + frameBuffer->paintBoxRel(x+220,y+hheight+5, mheight*4, mheight*4-10, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x+222,y+hheight+2+5, mheight*4-4 ,mheight*4-4-10, 254); +} + +void CColorChooser::paintSlider(int x, int y, unsigned char *spos, const neutrino_locale_t text, const char * const iconname, const bool selected) +{ + if (!spos) + return; + frameBuffer->paintBoxRel(x+70,y,120,mheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintIcon("volumebody.raw",x+70,y+2+mheight/4); + frameBuffer->paintIcon(selected ? iconname : "volumeslider2.raw",x+73+(*spos),y+mheight/4); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x,y+mheight, width, g_Locale->getText(text), COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/widget/colorchooser.h b/src/gui/widget/colorchooser.h new file mode 100644 index 000000000..fda66b089 --- /dev/null +++ b/src/gui/widget/colorchooser.h @@ -0,0 +1,73 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __colorchooser__ +#define __colorchooser__ + +#include +#include +#include + +#include + +class CColorChooser : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + unsigned char * value[4]; // r, g, b, alpha + + neutrino_locale_t name; + + CChangeObserver* observer; + + void paint(); + void setColor(); + void paintSlider(int x, int y, unsigned char *spos, const neutrino_locale_t text, const char * const iconname, const bool selected); + + public: + + CColorChooser(const neutrino_locale_t Name, unsigned char *R, unsigned char *G, unsigned char *B, unsigned char* Alpha, CChangeObserver* Observer = NULL); // UTF-8 + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif + diff --git a/src/gui/widget/drawable.cpp b/src/gui/widget/drawable.cpp new file mode 100644 index 000000000..a337e635d --- /dev/null +++ b/src/gui/widget/drawable.cpp @@ -0,0 +1,150 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +Drawable::Drawable() +{ +} + +Drawable::~Drawable() +{ +} + +int Drawable::getWidth(void) +{ + return m_width; +} + +int Drawable::getHeight(void) +{ + return m_height; +} + +Drawable::DType Drawable::getType(void) +{ + return Drawable::DTYPE_DRAWABLE; +} + + +// ------------------------------------------------------------------------------ +DText::DText(std::string& text) +{ + m_text = text; + init(); +} + +DText::DText(const char *text) +{ + m_text = std::string(text); + init(); +} + +void DText::init() +{ + m_width = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(m_text, true); // UTF-8 + m_height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); +} + + +void DText::draw(CFBWindow *window, int x, int y, int width) +{ + window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU], x, y + m_height, width, + m_text.c_str(), (CFBWindow::color_t)COL_MENUCONTENT, 0, true); // UTF-8 +} + +void DText::print(void) +{ + std::cout << " text: " << m_text; +} + +// ------------------------------------------------------------------------------ +DIcon::DIcon(std::string& icon) +{ + m_icon = icon; + init(); +} + +DIcon::DIcon(const char *icon) +{ + m_icon = std::string(icon); + init(); +} + +void DIcon::init() +{ + m_height = 16; + m_width = 16; +} + +void DIcon::draw(CFBWindow *window, int x, int y, int width) +{ + window->paintIcon(m_icon.c_str(), x, y); +} + +void DIcon::print(void) +{ + std::cout << " icon: " << m_icon; +} + +// ------------------------------------------------------------------------------ +DPagebreak::DPagebreak() +{ + m_height = 0; + m_width = 0; +} + +void DPagebreak::draw(CFBWindow *window, int x, int y, int width) +{ +// window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU], +// x, y + m_height, width, "", +// (CFBWindow::color_t)COL_MENUCONTENT, 0, true); // UTF-8 +} + +void DPagebreak::print(void) +{ + std::cout << ""; +} + +Drawable::DType DPagebreak::getType(void) +{ + return Drawable::DTYPE_PAGEBREAK; +} diff --git a/src/gui/widget/drawable.h b/src/gui/widget/drawable.h new file mode 100644 index 000000000..744f4446e --- /dev/null +++ b/src/gui/widget/drawable.h @@ -0,0 +1,164 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __drawable__ +#define __drawable__ + +#include +#include + +#include +#include + +class Drawable; + +typedef std::vector > ContentLines; + + +/** + * The base class for items which can be drawn on a CFBWindow. + */ +class Drawable +{ +public: + + enum DType { + DTYPE_DRAWABLE, + DTYPE_PAGEBREAK + }; + + virtual ~Drawable(); + + /** + * Overwrite this method in subclasses to draw on the window + * + * @param window the window to draw on + * @param x x component of the top left corner + * @param y y component of the top left corner + */ + virtual void draw(CFBWindow *window, int x, int y, int width) = 0; + + virtual int getWidth(void); + + virtual int getHeight(void); + + /** + * Overwrite this method in subclasses to print some info + * about the content. Mainly used for debuging ;) + */ + virtual void print(void) = 0; + + /** + * Returns the type of this drawable. Used to distinguish between + * drawing objects and control objects like pagebreaks. + * @return the type of this drawable. + */ + virtual DType getType(); + +protected: + + Drawable(); + + int m_height; + + int m_width; + +private: + +}; + +/** + * This class draws a given string. + */ +class DText : public Drawable +{ +public: + DText(std::string& text); + + DText(const char *text); + + void init(); + + void draw(CFBWindow *window, int x, int y, int width); + + void print(); + +protected: + + std::string m_text; + +}; + + +/** + * This class draws a given icon. + */ +class DIcon : public Drawable +{ +public: + DIcon(std::string& icon); + + DIcon(const char *icon); + + void init(); + + void draw(CFBWindow *window, int x, int y, int width); + + void print(); + +protected: + + std::string m_icon; +}; + +/** + * This class is used as a control object and forces a new page + * in scrollable windows. + */ +class DPagebreak : public Drawable +{ +public: + DPagebreak(); + + void draw(CFBWindow *window, int x, int y, int width); + + void print(); + + DType getType(); + +protected: + +private: + +}; + +#endif diff --git a/src/gui/widget/helpbox.cpp b/src/gui/widget/helpbox.cpp new file mode 100644 index 000000000..05f4d56e6 --- /dev/null +++ b/src/gui/widget/helpbox.cpp @@ -0,0 +1,111 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +Helpbox::Helpbox() +{ +} + +Helpbox::~Helpbox() +{ + for (ContentLines::iterator it = m_lines.begin(); + it != m_lines.end(); it++) + { + for (std::vector::iterator it2 = it->begin(); + it2 != it->end(); it2++) + { + delete *it2; + } + } +} + +void Helpbox::show(const neutrino_locale_t Caption, const int Width, int timeout) +{ + + CMessageBox msgBox(Caption, m_lines, Width, NEUTRINO_ICON_INFO, CMessageBox::mbrBack,CMessageBox::mbBack); + msgBox.exec(timeout); +} + +void Helpbox::addLine(const char *text) +{ + std::vector v; + Drawable *d = new DText(text); + v.push_back(d); + m_lines.push_back(v); +} + +void Helpbox::addLine(std::string& text) +{ + std::vector v; + Drawable *d = new DText(text); + v.push_back(d); + m_lines.push_back(v); +} + +void Helpbox::addLine(const char *icon, const char *text) +{ + std::vector v; + Drawable *di = new DIcon(icon); + Drawable *dt = new DText(text); + v.push_back(di); + v.push_back(dt); + m_lines.push_back(v); +} + +void Helpbox::addLine(std::string& icon, std::string& text) +{ + std::vector v; + Drawable *di = new DIcon(icon); + Drawable *dt = new DText(text); + v.push_back(di); + v.push_back(dt); + m_lines.push_back(v); +} + +void Helpbox::addPagebreak(void) +{ + std::vector v; + Drawable *p = new DPagebreak(); + v.push_back(p); + m_lines.push_back(v); +} + diff --git a/src/gui/widget/helpbox.h b/src/gui/widget/helpbox.h new file mode 100644 index 000000000..a1d4d3c22 --- /dev/null +++ b/src/gui/widget/helpbox.h @@ -0,0 +1,64 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __helpbox__ +#define __helpbox__ + +#include + +#include + +#include +#include + +class Helpbox +{ +public: + + Helpbox(); + ~Helpbox(); + + void show(const neutrino_locale_t Caption, const int Width = 450, int timeout = -1); + + void addLine(std::string& text); + void addLine(const char *text); + void addLine(std::string& icon, std::string& text); + void addLine(const char *icon, const char *text); + void addPagebreak(); +protected: + +private: + + ContentLines m_lines; +}; + +#endif diff --git a/src/gui/widget/hintbox.cpp b/src/gui/widget/hintbox.cpp new file mode 100644 index 000000000..8c2c57210 --- /dev/null +++ b/src/gui/widget/hintbox.cpp @@ -0,0 +1,279 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#define borderwidth 4 + +#define ROUND_RADIUS 9 + +#define HINTBOX_MAX_HEIGHT 420 + +CHintBox::CHintBox(const neutrino_locale_t Caption, const char * const Text, const int Width, const char * const Icon) +{ + char * begin; + char * pos; + int nw; + + message = strdup(Text); + + width = Width; + + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + height = theight + fheight; + + caption = Caption; + + begin = message; + + while (true) + { + height += fheight; + if (height > HINTBOX_MAX_HEIGHT) + height -= fheight; + + line.push_back(begin); + pos = strchr(begin, '\n'); + if (pos != NULL) + { + *pos = 0; + begin = pos + 1; + } + else + break; + } + entries_per_page = ((height - theight) / fheight) - 1; + current_page = 0; + + unsigned int additional_width; + + if (entries_per_page < line.size()) + additional_width = 20 + 15; + else + additional_width = 20 + 0; + + if (Icon != NULL) + { + iconfile = Icon; + additional_width += 30; + } + else + iconfile = ""; + + nw = additional_width + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(g_Locale->getText(caption), true); // UTF-8 + + if (nw > width) + width = nw; + + for (std::vector::const_iterator it = line.begin(); it != line.end(); it++) + { + nw = additional_width + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(*it, true); // UTF-8 + if (nw > width) + width = nw; + } + window = NULL; +} + +CHintBox::~CHintBox(void) +{ + if (window != NULL) + { + delete window; + window = NULL; + } + free(message); +} + +void CHintBox::paint(void) +{ + if (window != NULL) + { + /* + * do not paint stuff twice: + * => thread safety needed by movieplayer.cpp: + * one thread calls our paint method, the other one our hide method + * => no memory leaks + */ + return; + } + + CFrameBuffer* frameBuffer = CFrameBuffer::getInstance(); + window = new CFBWindow(frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width ) >> 1), + frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - height) >> 2), + width + borderwidth, + height + borderwidth); + refresh(); +} + +void CHintBox::refresh(void) +{ + if (window == NULL) + { + return; + } + + //window->paintBoxRel(borderwidth, height, width, borderwidth, COL_INFOBAR_SHADOW_PLUS_0); + //window->paintBoxRel(width, borderwidth, borderwidth, height - borderwidth, COL_INFOBAR_SHADOW_PLUS_0); + window->paintBoxRel(width-20, borderwidth, borderwidth+20, height - borderwidth, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 1); // right + window->paintBoxRel(borderwidth, height-20, width, borderwidth+20, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 2); // bottom + + //window->paintBoxRel(0, 0, width, theight, (CFBWindow::color_t)COL_MENUHEAD_PLUS_0); + window->paintBoxRel(0, 0, width, theight, (CFBWindow::color_t)COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + + if (!iconfile.empty()) + { + window->paintIcon(iconfile.c_str(), 8, 5); + window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE], 40, theight, width - 40, g_Locale->getText(caption), (CFBWindow::color_t)COL_MENUHEAD, 0, true); // UTF-8 + } + else + window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE], 10, theight, width - 10, g_Locale->getText(caption), (CFBWindow::color_t)COL_MENUHEAD, 0, true); // UTF-8 + + //window->paintBoxRel(0, theight, width, (entries_per_page + 1) * fheight, (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0); + window->paintBoxRel(0, theight, width, (entries_per_page + 1) * fheight, (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + int count = entries_per_page; + int ypos = theight + (fheight >> 1); + + for (std::vector::const_iterator it = line.begin() + (entries_per_page * current_page); ((it != line.end()) && (count > 0)); it++, count--) + window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU], 10, (ypos += fheight), width, *it, (CFBWindow::color_t)COL_MENUCONTENT, 0, true); // UTF-8 + + if (entries_per_page < line.size()) + { + ypos = theight + (fheight >> 1); + window->paintBoxRel(width - 15, ypos , 15, entries_per_page * fheight, COL_MENUCONTENT_PLUS_1); + unsigned int marker_size = (entries_per_page * fheight) / ((line.size() + entries_per_page - 1) / entries_per_page); + window->paintBoxRel(width - 13, ypos + current_page * marker_size, 11, marker_size , COL_MENUCONTENT_PLUS_3); + } +} + +bool CHintBox::has_scrollbar(void) +{ + return (entries_per_page < line.size()); +} + +void CHintBox::scroll_up(void) +{ + if (current_page > 0) + { + current_page--; + refresh(); + } +} + +void CHintBox::scroll_down(void) +{ + if ((entries_per_page * (current_page + 1)) <= line.size()) + { + current_page++; + refresh(); + } +} + +void CHintBox::hide(void) +{ + if (window != NULL) + { + delete window; + window = NULL; + } +} + +int ShowHintUTF(const neutrino_locale_t Caption, const char * const Text, const int Width, int timeout, const char * const Icon) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + CHintBox * hintBox = new CHintBox(Caption, Text, Width, Icon); + hintBox->paint(); + + if ( timeout == -1 ) + timeout = 5; /// default timeout 5 sec + //timeout = g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR]; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd( timeout ); + + int res = messages_return::none; + + while ( ! ( res & ( messages_return::cancel_info | messages_return::cancel_all ) ) ) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if ((msg == CRCInput::RC_timeout) || + (msg == CRCInput::RC_home ) || + (msg == CRCInput::RC_ok )) + { + res = messages_return::cancel_info; + } + else if ((hintBox->has_scrollbar()) && ((msg == CRCInput::RC_up) || (msg == CRCInput::RC_down))) + { + if (msg == CRCInput::RC_up) + hintBox->scroll_up(); + else + hintBox->scroll_down(); + } + else if((msg == CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) { + } + else if((msg == CRCInput::RC_mode) || (msg == CRCInput::RC_next) || (msg == CRCInput::RC_prev)) { + res = messages_return::cancel_info; + g_RCInput->postMsg(msg, data); + } + else + { + res = CNeutrinoApp::getInstance()->handleMsg(msg, data); + if (res & messages_return::unhandled) + { + + // raus hier und darüber behandeln... + g_RCInput->postMsg(msg, data); + res = messages_return::cancel_info; + } + } + } + + hintBox->hide(); + delete hintBox; + return 1; +} + +int ShowLocalizedHint(const neutrino_locale_t Caption, const neutrino_locale_t Text, const int Width, int timeout, const char * const Icon) +{ + return ShowHintUTF(Caption, g_Locale->getText(Text),Width,timeout,Icon); +} + diff --git a/src/gui/widget/hintbox.h b/src/gui/widget/hintbox.h new file mode 100644 index 000000000..c291aa683 --- /dev/null +++ b/src/gui/widget/hintbox.h @@ -0,0 +1,82 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __hintbox__ +#define __hintbox__ + +#include +#include +#include + +#include +#include + +class CHintBox +{ + protected: + + CFBWindow * window; + + unsigned int entries_per_page; + unsigned int current_page; + + int width; + int height; + + int fheight; + int theight; + neutrino_locale_t caption; + char * message; + std::vector line; + std::string iconfile; + + void refresh(void); + + public: + // Text is UTF-8 encoded + CHintBox(const neutrino_locale_t Caption, const char * const Text, const int Width = 450, const char * const Icon = "info.raw"); + ~CHintBox(void); + + bool has_scrollbar(void); + void scroll_up(void); + void scroll_down(void); + + void paint(void); + void hide(void); +}; + +// Text is UTF-8 encoded +int ShowHintUTF(const neutrino_locale_t Caption, const char * const Text, const int Width = 450, int timeout = -1, const char * const Icon = NEUTRINO_ICON_INFO); +int ShowLocalizedHint(const neutrino_locale_t Caption, const neutrino_locale_t Text, const int Width = 450, int timeout = -1, const char * const Icon = NEUTRINO_ICON_INFO); + + +#endif diff --git a/src/gui/widget/hintboxext.cpp b/src/gui/widget/hintboxext.cpp new file mode 100644 index 000000000..2346dc5e6 --- /dev/null +++ b/src/gui/widget/hintboxext.cpp @@ -0,0 +1,314 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include + +#define ROUND_RADIUS 9 + +#define borderwidth 4 + +#define HINTBOXEXT_MAX_HEIGHT 420 + +CHintBoxExt::CHintBoxExt(const neutrino_locale_t Caption, const char * const Text, const int Width, const char * const Icon) +{ + m_message = strdup(Text); + + char *begin = m_message; + + begin = strtok(m_message, "\n"); + while (begin != NULL) + { + std::vector oneLine; + std::string s(begin); + DText *d = new DText(s); + oneLine.push_back(d); + m_lines.push_back(oneLine); + begin = strtok(NULL, "\n"); + } + init(Caption, Width, Icon); +} + + +CHintBoxExt::CHintBoxExt(const neutrino_locale_t Caption, ContentLines& lines, const int Width, const char * const Icon) +{ + m_message = NULL; + m_lines = lines; + init(Caption, Width, Icon); +} + +CHintBoxExt::~CHintBoxExt(void) +{ + if (m_window != NULL) + { + delete m_window; + m_window = NULL; + } + if (m_message != NULL) { + free(m_message); + + // content has been set using "m_message" so we are responsible to + // delete it + for (ContentLines::iterator it = m_lines.begin(); + it != m_lines.end(); it++) + { + for (std::vector::iterator it2 = it->begin(); + it2 != it->end(); it2++) + { + //(*it2)->print(); + delete *it2; + } + } + } +} + +void CHintBoxExt::init(const neutrino_locale_t Caption, const int Width, const char * const Icon) +{ + m_width = Width; + int nw = 0; + m_theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + m_fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + m_height = m_theight + m_fheight; + m_maxEntriesPerPage = 0; + + m_caption = Caption; + + int page = 0; + int line = 0; + int maxWidth = m_width > 0 ? m_width : 0; + int maxOverallHeight = 0; + m_startEntryOfPage.push_back(0); + for (ContentLines::iterator it = m_lines.begin(); it!=m_lines.end(); it++) + { + bool pagebreak = false; + int maxHeight = 0; + int lineWidth = 0; + for (std::vector::iterator item = it->begin(); + item != it->end(); item++) { + if ((*item)->getHeight() > maxHeight) + maxHeight = (*item)->getHeight(); + lineWidth += (*item)->getWidth(); + if ((*item)->getType() == Drawable::DTYPE_PAGEBREAK) + pagebreak = true; + } + if (lineWidth > maxWidth) + maxWidth = lineWidth; + m_height += maxHeight; + if (m_height > HINTBOXEXT_MAX_HEIGHT || pagebreak) { + if (m_height-maxHeight > maxOverallHeight) + maxOverallHeight = m_height - maxHeight; + m_height = m_theight + m_fheight + maxHeight; + if (pagebreak) + m_startEntryOfPage.push_back(line+1); + else + m_startEntryOfPage.push_back(line); + page++; + if (m_maxEntriesPerPage < (m_startEntryOfPage[page] - m_startEntryOfPage[page-1])) + { + m_maxEntriesPerPage = m_startEntryOfPage[page] - m_startEntryOfPage[page-1]; + } + } + line++; + } + + // if there is only one page m_height is already correct + // but m_maxEntries has not been set + if (m_startEntryOfPage.size() > 1) + { + m_height = maxOverallHeight; + } else { + m_maxEntriesPerPage = line; + } + + m_startEntryOfPage.push_back(line+1); // needed to calculate amount of items on last page + +// for (std::vector::iterator it=m_startEntryOfPage.begin(); +// it!=m_startEntryOfPage.end();it++) +// printf("startentryofpage: %d\n",*it); +// printf("pages: %d, startEntryVec: %d\n",page+1,m_startEntryOfPage.size()-1); +// printf("maxEntries: %d\n", m_maxEntriesPerPage); + + m_width = w_max(maxWidth,borderwidth); + m_currentPage = 0; + m_pages = page + 1; + unsigned int additional_width; + + if (m_startEntryOfPage.size() > 1) + additional_width = 20 + 15; + else + additional_width = 20 + 0; + + if (Icon != NULL) + { + m_iconfile = Icon; + additional_width += 30; + } + else + m_iconfile = ""; + + nw = additional_width + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(g_Locale->getText(m_caption), true); // UTF-8 + + if (nw > m_width) + m_width = nw; + + m_window = NULL; +} + +void CHintBoxExt::paint(bool toround) +{ + if (m_window != NULL) + { + /* + * do not paint stuff twice: + * => thread safety needed by movieplayer.cpp: + * one thread calls our paint method, the other one our hide method + * => no memory leaks + */ + return; + } + + CFrameBuffer* frameBuffer = CFrameBuffer::getInstance(); + m_window = new CFBWindow(frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - m_width ) >> 1), + frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - m_height) >> 2), + m_width + borderwidth, + m_height + borderwidth); + + refresh(toround); +} + +void CHintBoxExt::refresh(bool toround) +{ + if (m_window == NULL) + { + return; + } + // bottom, right shadow + //m_window->paintBoxRel(borderwidth, m_height, m_width, borderwidth, COL_INFOBAR_SHADOW_PLUS_0); + //m_window->paintBoxRel(m_width, borderwidth, borderwidth, m_height - borderwidth, COL_INFOBAR_SHADOW_PLUS_0); + m_window->paintBoxRel(m_width-20, borderwidth, borderwidth+20, m_height - borderwidth, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 1);//round + //m_window->paintBoxRel(borderwidth, m_height-20, m_width, borderwidth+20, COL_INFOBAR_SHADOW_PLUS_0, toround ? ROUND_RADIUS : 0, 2);//round + m_window->paintBoxRel(borderwidth, m_height-20, m_width, borderwidth+20, COL_INFOBAR_SHADOW_PLUS_0, ROUND_RADIUS, 2);//round + + // title + //m_window->paintBoxRel(0, 0, m_width, m_theight, (CFBWindow::color_t)COL_MENUHEAD_PLUS_0); + m_window->paintBoxRel(0, 0, m_width, m_theight, (CFBWindow::color_t)COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + + if (!m_iconfile.empty()) + { + m_window->paintIcon(m_iconfile.c_str(), 8, 5); + m_window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE], 40, m_theight, m_width - 40, g_Locale->getText(m_caption), (CFBWindow::color_t)COL_MENUHEAD, 0, true); // UTF-8 + } + else + m_window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE], 10, m_theight, m_width - 10, g_Locale->getText(m_caption), (CFBWindow::color_t)COL_MENUHEAD, 0, true); // UTF-8 + + // background of text panel + //m_window->paintBoxRel(0, m_theight, m_width, (m_maxEntriesPerPage + 1) * m_fheight, (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0); + m_window->paintBoxRel(0, m_theight, m_width, (m_maxEntriesPerPage + 1) * m_fheight, (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0, toround ? ROUND_RADIUS : 0, 2);//round + + int yPos = m_theight + (m_fheight >> 1); + +// for(std::vector::iterator it = m_startEntryOfPage.begin(); +// it != m_startEntryOfPage.end();it++) { +// printf(" %d",*it); +// } +// printf("\n current page: %d",m_currentPage); +// printf("von %d bis %d\n",m_startEntryOfPage[m_currentPage],m_startEntryOfPage[m_currentPage+1]-1); + + for (ContentLines::iterator it = m_lines.begin() + m_startEntryOfPage[m_currentPage]; + it != m_lines.begin() + m_startEntryOfPage[m_currentPage+1] + && it != m_lines.end(); it++) + { + int xPos = 10; + int maxHeight = 0; + for (std::vector::iterator d = it->begin();d!=it->end();d++) + { +// (*d)->print(); +// printf("\n"); + //(*d)->draw(m_window,xPos,yPos,m_width); + (*d)->draw(m_window,xPos,yPos,m_width-20); + xPos += (*d)->getWidth() + 20; + if ((*d)->getHeight() > maxHeight) + maxHeight = (*d)->getHeight(); + } + yPos += maxHeight; + } + + if (has_scrollbar()) + { +// yPos = m_theight + (m_fheight >> 1); + yPos = m_theight; + m_window->paintBoxRel(m_width - 15, yPos, 15, m_maxEntriesPerPage * m_fheight, COL_MENUCONTENT_PLUS_1); + unsigned int marker_size = (m_maxEntriesPerPage * m_fheight) / m_pages; + m_window->paintBoxRel(m_width - 13, yPos + m_currentPage * marker_size, 11, marker_size, COL_MENUCONTENT_PLUS_3); + } +} + +bool CHintBoxExt::has_scrollbar(void) +{ + return (m_startEntryOfPage.size() > 2); +} + +void CHintBoxExt::scroll_up(void) +{ + if (m_currentPage > 0) + { + m_currentPage--; + refresh(); + } +} + +void CHintBoxExt::scroll_down(void) +{ + if (m_currentPage +1 < m_startEntryOfPage.size()-1) + { + m_currentPage++; + refresh(); + } +} + +void CHintBoxExt::hide(void) +{ + if (m_window != NULL) + { + delete m_window; + m_window = NULL; + } +} diff --git a/src/gui/widget/hintboxext.h b/src/gui/widget/hintboxext.h new file mode 100644 index 000000000..9c19af80f --- /dev/null +++ b/src/gui/widget/hintboxext.h @@ -0,0 +1,84 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __hintboxext__ +#define __hintboxext__ + +#include +#include + +#include + +#include +#include + +class CHintBoxExt +{ + protected: + + CFBWindow * m_window; + + unsigned int m_currentPage; + std::vector m_startEntryOfPage; + int m_maxEntriesPerPage; + int m_pages; + + int m_width; + int m_height; + + int m_fheight; + int m_theight; + neutrino_locale_t m_caption; + char * m_message; + ContentLines m_lines; + std::string m_iconfile; + + void refresh(bool toround = 1); + + public: + CHintBoxExt(const neutrino_locale_t Caption, const char * const Text, const int Width, const char * const Icon); + + CHintBoxExt(const neutrino_locale_t Caption, ContentLines& lines, const int Width = 450, const char * const Icon = "info.raw"); + + ~CHintBoxExt(void); + + void init(const neutrino_locale_t Caption, const int Width, const char * const Icon); + + bool has_scrollbar(void); + void scroll_up(void); + void scroll_down(void); + + void paint(bool toround = 1); + void hide(void); +}; + +#endif diff --git a/src/gui/widget/icons.h b/src/gui/widget/icons.h new file mode 100644 index 000000000..707441e23 --- /dev/null +++ b/src/gui/widget/icons.h @@ -0,0 +1,97 @@ +#ifndef __gui_widget_icons_h__ +#define __gui_widget_icons_h__ + +/* + * $Header: /cvs/tuxbox/apps/tuxbox/neutrino/src/gui/widget/icons.h,v 1.18 2009/10/13 19:08:12 dbt Exp $ + * + * (C) 2003 by thegoodguy + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* icon files */ + +#define NEUTRINO_ICON_BUTTON_BLUE "blau.raw" +#define NEUTRINO_ICON_BUTTON_GREEN "gruen.raw" +#define NEUTRINO_ICON_BUTTON_RED "rot.raw" +#define NEUTRINO_ICON_BUTTON_YELLOW "gelb.raw" + +#define NEUTRINO_ICON_BUTTON_DBOX "dbox.raw" +#define NEUTRINO_ICON_BUTTON_DBOX_SMALL "dbox_small.raw" +#define NEUTRINO_ICON_BUTTON_HELP "help.raw" +#define NEUTRINO_ICON_BUTTON_HELP_SMALL "help_small.raw" +#define NEUTRINO_ICON_BUTTON_HOME "home.raw" +#define NEUTRINO_ICON_BUTTON_MUTE "mute.raw" +#define NEUTRINO_ICON_BUTTON_MUTE_SMALL "mute_small.raw" +#define NEUTRINO_ICON_BUTTON_POWER "power.raw" + +#define NEUTRINO_ICON_BUTTON_OKAY "ok.raw" +#define NEUTRINO_ICON_BUTTON_OKAY_SMALL "ok_small.raw" + +#define NEUTRINO_ICON_BUTTON_0 "0.raw" +#define NEUTRINO_ICON_BUTTON_1 "1.raw" +#define NEUTRINO_ICON_BUTTON_2 "2.raw" +#define NEUTRINO_ICON_BUTTON_3 "3.raw" +#define NEUTRINO_ICON_BUTTON_4 "4.raw" +#define NEUTRINO_ICON_BUTTON_5 "5.raw" +#define NEUTRINO_ICON_BUTTON_6 "6.raw" +#define NEUTRINO_ICON_BUTTON_7 "7.raw" +#define NEUTRINO_ICON_BUTTON_8 "8.raw" +#define NEUTRINO_ICON_BUTTON_9 "9.raw" + +#define NEUTRINO_ICON_BUTTON_TOP "up.raw" +#define NEUTRINO_ICON_BUTTON_TOP_SMALL "up_small.raw" +#define NEUTRINO_ICON_BUTTON_RIGHT "right.raw" +#define NEUTRINO_ICON_BUTTON_DOWN "down.raw" +#define NEUTRINO_ICON_BUTTON_DOWN_SMALL "down_small.raw" +#define NEUTRINO_ICON_BUTTON_LEFT "left.raw" + +#define NEUTRINO_ICON_BUTTON_PLUS "plus.raw" +#define NEUTRINO_ICON_BUTTON_MINUS "minus.raw" + +#define NEUTRINO_ICON_ERROR "error.raw" +#define NEUTRINO_ICON_FILE "file.raw" +#define NEUTRINO_ICON_GAMES "games.raw" +#define NEUTRINO_ICON_INFO "info.raw" +#define NEUTRINO_ICON_MOUNTED "mounted.raw" +#define NEUTRINO_ICON_NOT_MOUNTED "not_mounted.raw" +#define NEUTRINO_ICON_RECORDING "recording.raw" +#define NEUTRINO_ICON_SETTINGS "settings.raw" +#define NEUTRINO_ICON_SHELL "shell.raw" +#define NEUTRINO_ICON_UPNP "upnp.raw" +#define NEUTRINO_ICON_PROTECTING "protecting.raw" +#define NEUTRINO_ICON_EPGINFO "epginfo.raw" +#define NEUTRINO_ICON_UPDATE "softupdate.raw" +#define NEUTRINO_ICON_FEATURES "features.raw" + +#define DUMMY_ICON "dummy.raw" + + +/* icon widths */ + +#define NEUTRINO_ICON_BUTTON_BLUE_WIDTH 16 +#define NEUTRINO_ICON_BUTTON_GREEN_WIDTH 16 +#define NEUTRINO_ICON_BUTTON_RED_WIDTH 16 +#define NEUTRINO_ICON_BUTTON_YELLOW_WIDTH 16 + +#define NEUTRINO_ICON_BUTTON_MUTE_ZAP_ACTIVE "mute_zap_green.raw" +#define NEUTRINO_ICON_BUTTON_MUTE_ZAP_INACTIVE "mute_zap_gray.raw" + +/* misc */ + +#define NEUTRINO_ICON_VARPATH "/var/share/tuxbox/neutrino/icons/" //alternatively path for user-defined icons + +#endif /* __gui_widget_icons_h__ */ diff --git a/src/gui/widget/keychooser.cpp b/src/gui/widget/keychooser.cpp new file mode 100644 index 000000000..4aee5ef17 --- /dev/null +++ b/src/gui/widget/keychooser.cpp @@ -0,0 +1,169 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include + +#define ROUND_RADIUS 9 + +class CKeyValue : public CMenuSeparator +{ + std::string the_text; +public: + int keyvalue; + + CKeyValue() : CMenuSeparator(CMenuSeparator::STRING, LOCALE_KEYCHOOSERMENU_CURRENTKEY) + { + }; + + virtual const char * getString(void) + { + the_text = g_Locale->getText(LOCALE_KEYCHOOSERMENU_CURRENTKEY); + the_text += ": "; + the_text += CRCInput::getKeyName(keyvalue); + return the_text.c_str(); + }; +}; + + + +CKeyChooser::CKeyChooser(int * const Key, const neutrino_locale_t title, const std::string & Icon) : CMenuWidget(title, Icon) +{ + frameBuffer = CFrameBuffer::getInstance(); + key = Key; + keyChooser = new CKeyChooserItem(LOCALE_KEYCHOOSER_HEAD, key); + keyDeleter = new CKeyChooserItemNoKey(key); + + addItem(new CKeyValue()); + addItem(GenericMenuSeparatorLine); + addItem(GenericMenuBack); + addItem(GenericMenuSeparatorLine); + addItem(new CMenuForwarder(LOCALE_KEYCHOOSERMENU_SETNEW , true, NULL, keyChooser)); + addItem(new CMenuForwarder(LOCALE_KEYCHOOSERMENU_SETNONE, true, NULL, keyDeleter)); +} + + +CKeyChooser::~CKeyChooser() +{ + delete keyChooser; + delete keyDeleter; +} + + +void CKeyChooser::paint() +{ + (((CKeyValue *)(items[0]))->keyvalue) = *key; + + CMenuWidget::paint(); +} + +//***************************** +CKeyChooserItem::CKeyChooserItem(const neutrino_locale_t Name, int * Key) +{ + name = Name; + key = Key; + x = y = width = height = 0; +} + + +int CKeyChooserItem::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + unsigned long long timeoutEnd; + + int res = menu_return::RETURN_REPAINT; + + if (parent) + parent->hide(); + + paint(); + + g_RCInput->clearRCMsg(); + + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + get_Message: + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if (msg != CRCInput::RC_timeout) + { + if ((msg >= 0) && (msg <= CRCInput::RC_MaxRC)) + *key = msg; + else if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + res = menu_return::RETURN_EXIT_ALL; + else + goto get_Message; + } + + hide(); + return res; +} + +void CKeyChooserItem::hide() +{ + CFrameBuffer::getInstance()->paintBackgroundBoxRel(x, y, width, height); +} + +void CKeyChooserItem::paint() +{ + int hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + int mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + + width = w_max(350, 0); + height = h_max(hheight + 2 * mheight, 0); + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height) >> 1); + + //frameBuffer->paintBoxRel(x, y , width, hheight , COL_MENUHEAD_PLUS_0 ); + //frameBuffer->paintBoxRel(x, y + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x, y , width, hheight , COL_MENUHEAD_PLUS_0 , ROUND_RADIUS, 1);//round + frameBuffer->paintBoxRel(x, y + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+ 10, y+ hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + + //paint msg... + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, y+ hheight+ mheight, width, g_Locale->getText(LOCALE_KEYCHOOSER_TEXT1), COL_MENUCONTENT, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, y+ hheight+ mheight* 2, width, g_Locale->getText(LOCALE_KEYCHOOSER_TEXT2), COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/widget/keychooser.h b/src/gui/widget/keychooser.h new file mode 100644 index 000000000..b7162d3b8 --- /dev/null +++ b/src/gui/widget/keychooser.h @@ -0,0 +1,106 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __keychooser__ +#define __keychooser__ + +#include + +#include +#include +#include +#include + +#include "menue.h" + + +class CKeyChooserItem; +class CKeyChooserItemNoKey; +class CKeyChooser : public CMenuWidget +{ + private: + CFrameBuffer *frameBuffer; + int* key; + CKeyChooserItem *keyChooser; + CKeyChooserItemNoKey *keyDeleter; + + public: + CKeyChooser(int * const Key, const neutrino_locale_t title, const std::string & Icon = ""); + ~CKeyChooser(); + + void paint(); +}; + +class CKeyChooserItem : public CMenuTarget +{ + private: + + int x; + int y; + int width; + int height; + + neutrino_locale_t name; + int * key; + + void paint(); + + public: + + CKeyChooserItem(const neutrino_locale_t Name, int *Key); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + +class CKeyChooserItemNoKey : public CMenuTarget +{ + int *key; + + public: + + CKeyChooserItemNoKey(int *Key) + { + key=Key; + }; + + int exec(CMenuTarget* parent, const std::string & actionKey) + { + *key=CRCInput::RC_nokey; + return menu_return::RETURN_REPAINT; + } + +}; + + +#endif diff --git a/src/gui/widget/lcdcontroler.cpp b/src/gui/widget/lcdcontroler.cpp new file mode 100644 index 000000000..8b9b23ce6 --- /dev/null +++ b/src/gui/widget/lcdcontroler.cpp @@ -0,0 +1,336 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define BRIGHTNESSFACTOR 2.55 +#define CONTRASTFACTOR 0.63 + +#define ROUND_RADIUS 9 + +CLcdControler::CLcdControler(const neutrino_locale_t Name, CChangeObserver* Observer) +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + observer = Observer; + name = Name; + width = w_max(390, 0); + height = h_max(hheight+ mheight* 4+ +mheight/2, 0); + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height)>>1); + + contrast = CLCD::getInstance()->getContrast(); + brightness = CLCD::getInstance()->getBrightness(); + brightnessstandby = CLCD::getInstance()->getBrightnessStandby(); +} + +void CLcdControler::setLcd() +{ +// printf("contrast: %d brightness: %d brightness standby: %d\n", contrast, brightness, brightnessstandby); + CLCD::getInstance()->setBrightness(brightness); + CLCD::getInstance()->setBrightnessStandby(brightnessstandby); + CLCD::getInstance()->setContrast(contrast); +} + +int CLcdControler::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int selected, res = menu_return::RETURN_REPAINT; + unsigned int contrast_alt, brightness_alt, brightnessstandby_alt, autodimm_alt; + + if (parent) + { + parent->hide(); + } + contrast_alt = CLCD::getInstance()->getContrast(); + brightness_alt = CLCD::getInstance()->getBrightness(); + brightnessstandby_alt = CLCD::getInstance()->getBrightnessStandby(); + autodimm_alt = CLCD::getInstance()->getAutoDimm(); + selected = 0; + + setLcd(); + CLCD::getInstance()->setAutoDimm(0); // autodimm deactivated to control and see the real settings + paint(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + switch ( msg ) + { + case CRCInput::RC_down: + if(selected < 3) // max entries + { + paintSlider(x + 10, y + hheight , contrast , CONTRASTFACTOR , LOCALE_LCDCONTROLER_CONTRAST , false); + paintSlider(x + 10, y + hheight + mheight , brightness , BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS , false); + paintSlider(x + 10, y + hheight + mheight * 2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + selected++; + switch (selected) + { + case 0: + paintSlider(x+ 10, y+ hheight, contrast, CONTRASTFACTOR, LOCALE_LCDCONTROLER_CONTRAST, true); + break; + case 1: + paintSlider(x+ 10, y+ hheight+ mheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + break; + case 2: + paintSlider(x+ 10, y+ hheight+ mheight* 2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + CLCD::getInstance()->setMode(CLCD::MODE_STANDBY); + break; + case 3: + frameBuffer->paintBoxRel(x, y+hheight+mheight*3+mheight/2, width, mheight, COL_MENUCONTENTSELECTED_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*4+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + break; + } + } + break; + + case CRCInput::RC_up: + if (selected > 0) + { + paintSlider(x + 10, y + hheight , contrast , CONTRASTFACTOR , LOCALE_LCDCONTROLER_CONTRAST , false); + paintSlider(x + 10, y + hheight + mheight , brightness , BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS , false); + paintSlider(x + 10, y + hheight + mheight * 2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + selected--; + switch (selected) + { + case 0: + paintSlider(x+ 10, y+ hheight, contrast, CONTRASTFACTOR, LOCALE_LCDCONTROLER_CONTRAST, true); + break; + case 1: + paintSlider(x+ 10, y+ hheight+ mheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + CLCD::getInstance()->setMode(CLCD::MODE_TVRADIO); + break; + case 2: + paintSlider(x+10, y+hheight+mheight*2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + CLCD::getInstance()->setMode(CLCD::MODE_STANDBY); + frameBuffer->paintBoxRel(x, y+hheight+mheight*3+mheight/2, width, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*4+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENT, 0, true); // UTF-8 + break; + case 3: + break; + } + } + break; + + case CRCInput::RC_right: + switch (selected) + { + case 0: + if (contrast < 63) + { + int val = lrint(::log(contrast+1)); + + if (contrast + val < 63) + contrast += val; + else + contrast = 63; + + paintSlider(x+10, y+hheight, contrast, CONTRASTFACTOR, LOCALE_LCDCONTROLER_CONTRAST, true); + setLcd(); + } + break; + case 1: + if (brightness < 255) + { + if (brightness < 250) + brightness += 5; + else + brightness = 255; + + paintSlider(x+10, y+hheight+mheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + setLcd(); + } + break; + case 2: + if (brightnessstandby < 255) + { + if (brightnessstandby < 250) + brightnessstandby += 5; + else + brightnessstandby = 255; + + paintSlider(x+10, y+hheight+mheight*2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + setLcd(); + } + break; + } + break; + + case CRCInput::RC_left: + switch (selected) + { + case 0: + if (contrast > 0) + { + contrast -= lrint(::log(contrast)); + + paintSlider(x+10, y+hheight, contrast, CONTRASTFACTOR, LOCALE_LCDCONTROLER_CONTRAST, true); + setLcd(); + } + break; + case 1: + if (brightness > 0) + { + if (brightness > 5) + brightness -= 5; + else + brightness = 0; + + paintSlider(x+10, y+hheight+mheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + setLcd(); + } + break; + case 2: + if (brightnessstandby > 0) + { + if (brightnessstandby > 5) + brightnessstandby -= 5; + else + brightnessstandby = 0; + + paintSlider(x+10, y+hheight+mheight*2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + setLcd(); + } + break; + } + break; + + case CRCInput::RC_home: + if ( ( (contrast != contrast_alt) || (brightness != brightness_alt) || (brightnessstandby != brightnessstandby_alt) ) && + (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + break; + + // sonst abbruch... + contrast = contrast_alt; + brightness = brightness_alt; + brightnessstandby = brightnessstandby_alt; + setLcd(); + loop = false; + break; + + case CRCInput::RC_ok: + if (selected==3) // default Werte benutzen + { + brightness = DEFAULT_LCD_BRIGHTNESS; + brightnessstandby = DEFAULT_LCD_STANDBYBRIGHTNESS; + contrast = DEFAULT_LCD_CONTRAST; + selected = 0; + setLcd(); + paint(); + break; + } + + case CRCInput::RC_timeout: + loop = false; + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + CLCD::getInstance()->setAutoDimm(autodimm_alt); + hide(); + + if(observer) + observer->changeNotify(name, NULL); + + return res; +} + +void CLcdControler::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CLcdControler::paint() +{ + CLCD::getInstance()->setMode(CLCD::MODE_TVRADIO); + + //frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0); + //frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + + paintSlider(x+10, y+hheight, contrast, CONTRASTFACTOR, LOCALE_LCDCONTROLER_CONTRAST, true); + paintSlider(x+10, y+hheight+mheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, false); + paintSlider(x+10, y+hheight+mheight*2, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + + frameBuffer->paintHLineRel(x+10, width-20, y+hheight+mheight*3+mheight/4, COL_MENUCONTENT_PLUS_3); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*4+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENT, 0, true); // UTF-8 +} + +void CLcdControler::paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected) +{ + int startx = 200; + char wert[5]; + + frameBuffer->paintBoxRel(x + startx, y, 120, mheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintIcon("volumebody.raw", x + startx, y+2+mheight/4); + frameBuffer->paintIcon(selected ? "volumeslider2blue.raw" : "volumeslider2.raw", (int)(x + (startx+3)+(spos / factor)), y+mheight/4); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, y+mheight, width, g_Locale->getText(text), COL_MENUCONTENT, 0, true); // UTF-8 + sprintf(wert, "%3d", spos); // UTF-8 encoded + frameBuffer->paintBoxRel(x + startx + 120 + 10, y, 50, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x + startx + 120 + 10, y+mheight, width, wert, COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/widget/lcdcontroler.h b/src/gui/widget/lcdcontroler.h new file mode 100644 index 000000000..2c7c80c79 --- /dev/null +++ b/src/gui/widget/lcdcontroler.h @@ -0,0 +1,75 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __lcdcontroler__ +#define __lcdcontroler__ + +#include +#include + +#include "menue.h" + +#include + +class CLcdControler : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + unsigned char contrast; + unsigned char brightness; + unsigned char brightnessstandby; + + neutrino_locale_t name; + + CChangeObserver* observer; + + void paint(); + void setLcd(); + void paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected); + + public: + + CLcdControler(const neutrino_locale_t Name, CChangeObserver* Observer = NULL); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/gui/widget/listbox.cpp b/src/gui/widget/listbox.cpp new file mode 100644 index 000000000..93d5162b5 --- /dev/null +++ b/src/gui/widget/listbox.cpp @@ -0,0 +1,255 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#define ROUND_RADIUS 9 + +CListBox::CListBox(const char * const Caption) +{ + frameBuffer = CFrameBuffer::getInstance(); + caption = Caption; + liststart = 0; + selected = 0; + width = 400; + height = 420; + ButtonHeight = 25; + modified = false; + theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + fheight = g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->getHeight(); + listmaxshow = (height-theight-0)/fheight; + height = theight+0+listmaxshow*fheight; // recalc height + + x=frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width) / 2); + y=frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - height) / 2); +} + +void CListBox::setModified(void) +{ + modified = true; +} + +void CListBox::paint() +{ + liststart = (selected/listmaxshow)*listmaxshow; + + for(unsigned int count=0;countpaintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1); + + int sbc= ((getItemCount()- 1)/ listmaxshow)+ 1; + float sbh= (sb- 4)/ sbc; + int sbs= (selected/listmaxshow); + + frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ int(sbs* sbh) , 11, int(sbh), COL_MENUCONTENT_PLUS_3); +} + +void CListBox::paintHead() +{ + //frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+theight+0, width, caption.c_str() , COL_MENUHEAD, 0, true); +} + +void CListBox::paintFoot() +{ + int ButtonWidth = width / 4; + //frameBuffer->paintBoxRel(x,y+height, width,ButtonHeight, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x,y+height, width,ButtonHeight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2);//round + frameBuffer->paintHLine(x, x+width, y, COL_INFOBAR_SHADOW_PLUS_0); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_OKAY, x+width- 4* ButtonWidth+ 8, y+height+1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width- 4* ButtonWidth+ 38, y+height+24 - 2, width, "edit", COL_INFOBAR); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, x+width- 3* ButtonWidth+ 8, y+height+4); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width- 3* ButtonWidth+ 29, y+height+24 - 2, width- 26, "add", COL_INFOBAR); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_RED, x+width- 2* ButtonWidth+ 8, y+height+4); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width- 2* ButtonWidth+ 29, y+height+24 - 2, width- 26, "remove", COL_INFOBAR); + + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HOME, x+width - ButtonWidth+ 8, y+height+1); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x+width - ButtonWidth+ 38, y+height+24 - 2, width, "ready", COL_INFOBAR); +} + +void CListBox::paintItem(int pos) +{ + paintItem(liststart+pos, pos, (liststart+pos==selected) ); +} + +void CListBox::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height+ButtonHeight); +} + +unsigned int CListBox::getItemCount() +{ + return 10; +} + +int CListBox::getItemHeight() +{ + return fheight; +} + +void CListBox::paintItem(unsigned int itemNr, int paintNr, bool selected) +{ + int ypos = y+ theight + paintNr*getItemHeight(); + + uint8_t color; + fb_pixel_t bgcolor; + + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + frameBuffer->paintBoxRel(x,ypos, width- 15, getItemHeight(), bgcolor); + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST]->RenderString(x + 10, ypos+ fheight, width-20, "demo", color); +} + +int CListBox::exec(CMenuTarget* parent, const std::string & actionKey) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + selected=0; + + if (parent) + { + parent->hide(); + } + + paintHead(); + paint(); + paintFoot(); + + bool loop=true; + modified = false; + while (loop) + { + g_RCInput->getMsg(&msg, &data, g_settings.timing[SNeutrinoSettings::TIMING_EPG]); + + if (( msg == (neutrino_msg_t)g_settings.key_channelList_cancel) || + ( msg == CRCInput::RC_home)) + { + loop = false; + } + else if (msg == CRCInput::RC_up || (int) msg == g_settings.key_channelList_pageup) + { + if(getItemCount()!=0) { + int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pageup) ? listmaxshow : 1; // browse or step 1 + selected -= step; + if((prev_selected-step) < 0) // because of uint + selected = getItemCount() - 1; + + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + } + } + else if (msg == CRCInput::RC_down || (int) msg == g_settings.key_channelList_pagedown) + { + if(getItemCount()!=0) { + unsigned int step = 0; + int prev_selected = selected; + + step = ((int) msg == g_settings.key_channelList_pagedown) ? listmaxshow : 1; // browse or step 1 + selected += step; + + if(selected >= getItemCount()) { + if (((getItemCount() / listmaxshow) + 1) * listmaxshow == getItemCount() + listmaxshow) // last page has full entries + selected = 0; + else + selected = ((step == listmaxshow) && (selected < (((getItemCount() / listmaxshow) + 1) * listmaxshow))) ? (getItemCount() - 1) : 0; + } + + paintItem(prev_selected - liststart); + unsigned int oldliststart = liststart; + liststart = (selected/listmaxshow)*listmaxshow; + if(oldliststart!=liststart) + paint(); + else + paintItem(selected - liststart); + } + } + else if( msg ==CRCInput::RC_ok) + { + onOkKeyPressed(); + } + else if ( msg ==CRCInput::RC_red) + { + onRedKeyPressed(); + } + else if ( msg ==CRCInput::RC_green) + { + onGreenKeyPressed(); + } + else if ( msg ==CRCInput::RC_yellow) + { + onYellowKeyPressed(); + } + else if ( msg ==CRCInput::RC_blue) + { + onBlueKeyPressed(); + } + else if ((msg ==CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) + { + } + else + { + CNeutrinoApp::getInstance()->handleMsg( msg, data ); + // kein canceling... + } + } + + hide(); + return res; +} diff --git a/src/gui/widget/listbox.h b/src/gui/widget/listbox.h new file mode 100644 index 000000000..8c1565a09 --- /dev/null +++ b/src/gui/widget/listbox.h @@ -0,0 +1,89 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __listbox__ +#define __listbox__ + +#include "menue.h" + +#include + +#include + +class CListBox : public CMenuWidget +{ + protected: + CFrameBuffer* frameBuffer; + bool modified; + std::string caption; + int width; + int height; + int x; + int y; + + int fheight; + int theight; + + unsigned int selected; + unsigned int liststart; + unsigned int listmaxshow; + + int ButtonHeight; + + //---------------------------- + + virtual void paintItem(int pos); + virtual void paint(); + virtual void paintHead(); + virtual void paintFoot(); + virtual void hide(); + + + //------hier Methoden überschreiben------- + + //------Fernbedienungsevents-------------- + virtual void onRedKeyPressed(){}; + virtual void onGreenKeyPressed(){}; + virtual void onYellowKeyPressed(){}; + virtual void onBlueKeyPressed(){}; + virtual void onOkKeyPressed(){}; + virtual void onOtherKeyPressed( int key ){}; + + //------gibt die Anzahl der Listenitems--- + virtual unsigned int getItemCount(); + + //------malen der Items------------------- + virtual int getItemHeight(); + virtual void paintItem(uint32_t itemNr, int paintNr, bool selected); + + //------Benutzung von setModified--------- + void setModified(void); + + public: + CListBox(const char * const Caption); + virtual int exec(CMenuTarget* parent, const std::string & actionKey); +}; + + +#endif diff --git a/src/gui/widget/listbox_legacy.cpp b/src/gui/widget/listbox_legacy.cpp new file mode 100644 index 000000000..0c00ea0fe --- /dev/null +++ b/src/gui/widget/listbox_legacy.cpp @@ -0,0 +1,58 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2003 thegoodguy + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "listbox_legacy.h" + +#include + +CListBoxExt::CListBoxExt(void) : CListBox("") +{ + saveBoxCaption = NONEXISTANT_LOCALE; + saveBoxText = NULL; +} + +void CListBoxExt::setTitle(const char * const title) +{ + caption = title ? title : ""; +} + +void CListBoxExt::hide() +{ + //want2save? + if ((modified) && (saveBoxCaption != NONEXISTANT_LOCALE) && (saveBoxText != NULL)) + { + if (ShowMsgUTF(saveBoxCaption, saveBoxText, CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo) == CMessageBox::mbrYes) // UTF-8 + onSaveData(); + } + + CListBox::hide(); +} + +void CListBoxExt::setSaveDialogText(const neutrino_locale_t title, const char * const text) +{ + saveBoxCaption = title; + saveBoxText = text; +} diff --git a/src/gui/widget/listbox_legacy.h b/src/gui/widget/listbox_legacy.h new file mode 100644 index 000000000..14b729374 --- /dev/null +++ b/src/gui/widget/listbox_legacy.h @@ -0,0 +1,46 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2003 thegoodguy + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __listbox_legacy__ +#define __listbox_legacy__ + +#include "listbox.h" + +class CListBoxExt : public CListBox +{ + protected: + neutrino_locale_t saveBoxCaption; + const char * saveBoxText; + + void setTitle(const char * const title); + + virtual void hide(); + void setSaveDialogText(const neutrino_locale_t title, const char * const text); + virtual void onSaveData(){}; + + public: + CListBoxExt(void); +}; + + +#endif diff --git a/src/gui/widget/listframe.cpp b/src/gui/widget/listframe.cpp new file mode 100644 index 000000000..5130c43e0 --- /dev/null +++ b/src/gui/widget/listframe.cpp @@ -0,0 +1,679 @@ +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: listframe.cpp . + + Description: Implementation of the CListFrame class + This class provides a plain Listbox with numerous rows and lines. + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Günther initial implementation + +****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define ROUND_RADIUS 9 + +#include "stdlib.h" +#include "listframe.h" +#include + +#define TEXT_BORDER_WIDTH 8 +#define ROW_BORDER_WIDTH 4 +#define SCROLL_FRAME_WIDTH 10 +#define SCROLL_MARKER_BORDER 2 + +#define MAX_WINDOW_WIDTH (frameBuffer->getScreenWidth() - 40) +#define MAX_WINDOW_HEIGHT (frameBuffer->getScreenHeight() - 40) + +#define MIN_WINDOW_WIDTH (frameBuffer->getScreenWidth() >> 1) +#define MIN_WINDOW_HEIGHT 40 + +#define TITLE_BACKGROUND_COLOR ((CFBWindow::color_t)COL_MENUHEAD_PLUS_0) +#define HEADER_LIST_BACKGROUND_COLOR ((CFBWindow::color_t)COL_MENUCONTENT_PLUS_0) +#define LIST_BACKGROUND_COLOR ((CFBWindow::color_t)COL_MENUCONTENT_PLUS_0) +//#define LIST_BACKGROUND_COLOR_SELECTED ((CFBWindow::color_t)COL_MENUCONTENT_PLUS_1) +#define LIST_BACKGROUND_COLOR_SELECTED ((CFBWindow::color_t)COL_MENUCONTENTSELECTED_PLUS_0) + +#define TITLE_FONT_COLOR COL_MENUHEAD +#define HEADER_LIST_FONT_COLOR COL_MENUCONTENT +#define LIST_FONT_COLOR COL_MENUCONTENT +//#define LIST_FONT_COLOR_SELECTED COL_MENUCONTENT +#define LIST_FONT_COLOR_SELECTED COL_MENUCONTENTSELECTED + +#define FONT_LIST g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO2] +#define FONT_HEADER_LIST g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1] +#define FONT_TITLE g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CListFrame::CListFrame( LF_LINES* lines, Font* font_text, const int mode, + const CBox* position, const char* textTitle, Font* font_title) +{ + //TRACE("[CListFrame] new\r\n"); + initVar(); + + if(lines != NULL) { + m_pLines = lines; + m_nNrOfRows = lines->rows; + if(m_nNrOfRows > LF_MAX_ROWS) + m_nNrOfRows = LF_MAX_ROWS; + } + if(font_text != NULL) m_pcFontList = font_text; + if(font_text != NULL) m_pcFontHeaderList = font_text; + if(position != NULL) { + m_cFrame = *position; + m_nMaxHeight = m_cFrame.iHeight; + m_nMaxWidth = m_cFrame.iWidth; + } + + m_nMode = mode; + +#if 0 + TRACE(" Mode: "); + if(mode & SCROLL) TRACE("SCROLL "); + if(mode & AUTO_WIDTH) TRACE("AUTO_WIDTH "); + if(mode & AUTO_HIGH) TRACE("AUTO_HIGH"); + TRACE("\r\n"); +#endif + + if(font_title != NULL) + m_pcFontTitle = font_title; + + if( textTitle != NULL) + m_textTitle = textTitle; + + m_nFontListHeight = m_pcFontList->getHeight(); + m_nFontHeaderListHeight = m_pcFontHeaderList->getHeight(); + m_nFontTitleHeight = m_pcFontTitle->getHeight(); + + //TRACE(" CListFrame::m_nFontTextHeight: %d\t\r\n",m_nFontListHeight); + + /* Initialise the window frames first */ + initFramesRel(); + + // than refresh text line array + onNewLineArray(); +} + +CListFrame::CListFrame( LF_LINES* lines) +{ + //TRACE("[CListFrame] new\r\n"); + initVar(); + + if(lines != NULL) + { + m_pLines = lines; + m_nNrOfRows = lines->rows; + if(m_nNrOfRows > LF_MAX_ROWS) + m_nNrOfRows = LF_MAX_ROWS; + } + + /* Initialise the window frames first */ + initFramesRel(); + + // than refresh text line array + onNewLineArray(); +} + +CListFrame::CListFrame() +{ + //TRACE("[CListFrame] new\r\n"); + initVar(); + initFramesRel(); + + m_pLines = NULL; +} + +CListFrame::~CListFrame() +{ + //TRACE("[CListFrame] del\r\n"); + hide(); +} + +void CListFrame::initVar(void) +{ + frameBuffer = CFrameBuffer::getInstance(); + //TRACE("[CListFrame]->InitVar\r\n"); + m_nMode = SCROLL; + + m_showSelection = true; + + m_pcFontList = FONT_LIST ; + m_nFontListHeight = m_pcFontList->getHeight(); + + m_pcFontHeaderList = FONT_HEADER_LIST ; + m_nFontHeaderListHeight = m_pcFontHeaderList->getHeight(); + + m_pcFontTitle = FONT_TITLE; + m_textTitle = "MovieBrowser"; + m_nFontTitleHeight = m_pcFontTitle->getHeight(); + + m_nNrOfPages = 1; + m_nNrOfLines = 0; + m_nNrOfRows = 1; + m_nLinesPerPage = 0; + m_nCurrentLine = 0; + m_nCurrentPage = 0; + m_nSelectedLine = 0; + + m_cFrame.iX = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - MIN_WINDOW_WIDTH) >>1); + m_cFrame.iWidth = MIN_WINDOW_WIDTH; + m_cFrame.iY = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - MIN_WINDOW_HEIGHT) >>1); + m_cFrame.iHeight = MIN_WINDOW_HEIGHT; + + m_nMaxHeight = MAX_WINDOW_HEIGHT; + m_nMaxWidth = MAX_WINDOW_WIDTH; + frameBuffer = NULL; +} + +void CListFrame::reSizeMainFrameWidth(int textWidth) +{ + //TRACE("[CListFrame]->ReSizeMainFrameWidth: %d, current: %d\r\n",textWidth,m_cFrameListRel.iWidth); + + int iNewWindowWidth = textWidth + m_cFrameScrollRel.iWidth + 2*TEXT_BORDER_WIDTH; + + if( iNewWindowWidth > m_nMaxWidth) iNewWindowWidth = m_nMaxWidth; + if((unsigned int) iNewWindowWidth < MIN_WINDOW_WIDTH) iNewWindowWidth = MIN_WINDOW_WIDTH; + + m_cFrame.iWidth = iNewWindowWidth; + + /* Re-Init the children frames due to new main window */ + initFramesRel(); +} + +void CListFrame::reSizeMainFrameHeight(int textHeight) +{ + //TRACE("[CListFrame]->ReSizeMainFrameHeight: %d, current: %d\r\n",textHeight,m_cFrameListRel.iHeight); + + int iNewWindowHeight = textHeight + 2*TEXT_BORDER_WIDTH; + + if( iNewWindowHeight > m_nMaxHeight) iNewWindowHeight = m_nMaxHeight; + if( iNewWindowHeight < MIN_WINDOW_HEIGHT) iNewWindowHeight = MIN_WINDOW_HEIGHT; + + m_cFrame.iHeight = iNewWindowHeight; + + /* Re-Init the children frames due to new main window */ + initFramesRel(); +} + +void CListFrame::initFramesRel(void) +{ + //TRACE("-[CListFrame]->InitFramesRel\r\n"); + + if(m_nMode & TITLE) + { + m_cFrameTitleRel.iX = 0; + m_cFrameTitleRel.iY = 0; + m_cFrameTitleRel.iHeight = m_nFontTitleHeight +2 ; + m_cFrameTitleRel.iWidth = m_cFrame.iWidth ; + } + else + { + m_cFrameTitleRel.iX = 0; + m_cFrameTitleRel.iY = 0; + m_cFrameTitleRel.iHeight= 0; + m_cFrameTitleRel.iWidth = 0; + } + + if(m_nMode & HEADER_LINE) + { + m_cFrameHeaderListRel.iX = 0; + m_cFrameHeaderListRel.iY = 0 + m_cFrameTitleRel.iHeight; + m_cFrameHeaderListRel.iHeight = m_nFontHeaderListHeight ; + } + else + { + m_cFrameHeaderListRel.iX = 0; + m_cFrameHeaderListRel.iY = 0; + m_cFrameHeaderListRel.iHeight = 0; + m_cFrameHeaderListRel.iWidth = 0; + } + + m_cFrameListRel.iX = 0; + m_cFrameListRel.iY = m_cFrameHeaderListRel.iHeight + m_cFrameTitleRel.iHeight; + m_cFrameListRel.iHeight = m_cFrame.iHeight - m_cFrameHeaderListRel.iHeight - m_cFrameTitleRel.iHeight; + + if(m_nMode & SCROLL) + { + m_cFrameScrollRel.iX = m_cFrame.iWidth - SCROLL_FRAME_WIDTH; + m_cFrameScrollRel.iY = m_cFrameTitleRel.iHeight; + m_cFrameScrollRel.iWidth = SCROLL_FRAME_WIDTH; + m_cFrameScrollRel.iHeight = m_cFrameListRel.iHeight + m_cFrameHeaderListRel.iHeight; + } + else + { + m_cFrameScrollRel.iX = 0; + m_cFrameScrollRel.iY = 0; + m_cFrameScrollRel.iHeight = 0; + m_cFrameScrollRel.iWidth = 0; + } + + m_cFrameListRel.iWidth = m_cFrame.iWidth - m_cFrameScrollRel.iWidth; + + if(m_nMode & HEADER_LINE) + { + m_cFrameHeaderListRel.iWidth = m_cFrame.iWidth - m_cFrameScrollRel.iWidth; + } + + m_nLinesPerPage = (m_cFrameListRel.iHeight - (2*TEXT_BORDER_WIDTH)) / m_nFontListHeight; +} + +void CListFrame::onNewLineArray(void) +{ + //TRACE("[CListFrame]->onNewLineArray \r\n"); + int maxTextWidth = 0; + + maxTextWidth = 300; // TODO + m_nNrOfLines = m_pLines->lineArray[0].size(); + if(m_nNrOfLines > 0 ) + { + /* check if we have to recalculate the window frame size, due to auto width and auto height */ + if( m_nMode & AUTO_WIDTH) + { + reSizeMainFrameWidth(maxTextWidth); + } + + if(m_nMode & AUTO_HIGH) + { + reSizeMainFrameHeight(m_nNrOfLines * m_nFontListHeight); + } + m_nLinesPerPage = (m_cFrameListRel.iHeight - (2*TEXT_BORDER_WIDTH)) / m_nFontListHeight; + + if(m_nLinesPerPage <= 0) + m_nLinesPerPage = 1; + + m_nNrOfPages = ((m_nNrOfLines-1) / m_nLinesPerPage) + 1; + + if(m_nCurrentLine >= m_nNrOfLines) + { + m_nCurrentPage = m_nNrOfPages - 1; + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + } + if(m_nSelectedLine >= m_nNrOfLines) + { + m_nSelectedLine = m_nCurrentLine; + } + } + else + { + m_nNrOfLines = 0; + m_nCurrentLine = 0; + m_nSelectedLine = 0; + m_nLinesPerPage = 1; + m_nNrOfPages = 0; + } + +// TRACE_1(" m_nNrOfPages: %d\r\n",m_nNrOfPages); +// TRACE_1(" m_nNrOfLines: %d\r\n",m_nNrOfLines); +// TRACE_1(" maxTextWidth: %d\r\n",maxTextWidth); +// TRACE_1(" m_nLinesPerPage: %d\r\n",m_nLinesPerPage); +// TRACE_1(" m_nFontTextHeight:%d\r\n",m_nFontListHeight); + //TRACE_1(" m_nCurrentPage: %d\r\n",m_nCurrentPage); + //TRACE_1(" m_nCurrentLine: %d\r\n",m_nCurrentLine); +} + +void CListFrame::refreshTitle(void) +{ + //TRACE("[CListFrame]->refreshHeaderList \r\n"); + if( frameBuffer == NULL) return; + + frameBuffer->paintBoxRel(m_cFrameTitleRel.iX+m_cFrame.iX, m_cFrameTitleRel.iY+m_cFrame.iY, + m_cFrameTitleRel.iWidth, m_cFrameTitleRel.iHeight, TITLE_BACKGROUND_COLOR); + + m_pcFontTitle->RenderString(m_cFrameTitleRel.iX + TEXT_BORDER_WIDTH + m_cFrame.iX, + m_cFrameTitleRel.iY + m_cFrameTitleRel.iHeight + m_cFrame.iY, + m_cFrameTitleRel.iWidth - (TEXT_BORDER_WIDTH << 1), + m_textTitle.c_str(), TITLE_FONT_COLOR, 0, true); // UTF-8 +} + +void CListFrame::refreshScroll(void) +{ + //TRACE("[CListFrame]->refreshScroll\r\n"); + if( frameBuffer == NULL) return; + if(!(m_nMode & SCROLL)) return; + + if (m_nNrOfPages > 1) + { + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX+m_cFrame.iX, m_cFrameScrollRel.iY+m_cFrame.iY, + m_cFrameScrollRel.iWidth, m_cFrameScrollRel.iHeight, COL_MENUCONTENT_PLUS_1); + unsigned int marker_size = m_cFrameScrollRel.iHeight / m_nNrOfPages; + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX + SCROLL_MARKER_BORDER+m_cFrame.iX, + m_cFrameScrollRel.iY + m_nCurrentPage * marker_size +m_cFrame.iY, + m_cFrameScrollRel.iWidth - (2*SCROLL_MARKER_BORDER), + marker_size, COL_MENUCONTENT_PLUS_3); + } + else + { + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX+m_cFrame.iX, m_cFrameScrollRel.iY+m_cFrame.iY, + m_cFrameScrollRel.iWidth, m_cFrameScrollRel.iHeight, COL_MENUCONTENT_PLUS_0); + } +} + +void CListFrame::refreshList(void) +{ + //TRACE("[CListFrame]->refreshList: %d\r\n",m_nCurrentLine); + if( frameBuffer == NULL) return; + frameBuffer->paintBoxRel(m_cFrameListRel.iX+m_cFrame.iX, m_cFrameListRel.iY+m_cFrame.iY, + m_cFrameListRel.iWidth, m_cFrameListRel.iHeight, LIST_BACKGROUND_COLOR); + + if( m_nNrOfLines <= 0) + return; + + int y = m_cFrameListRel.iY + TEXT_BORDER_WIDTH ; + for(int line = m_nCurrentLine; + line < m_nNrOfLines && line < m_nCurrentLine + m_nLinesPerPage; + line++) + { + int color = LIST_FONT_COLOR; + // draw line + if(line == m_nSelectedLine && m_showSelection == true) + { + color = LIST_FONT_COLOR_SELECTED; + frameBuffer->paintBoxRel(m_cFrameListRel.iX+m_cFrame.iX, + y+m_cFrame.iY, m_cFrameListRel.iWidth, + m_nFontListHeight, LIST_BACKGROUND_COLOR_SELECTED, + ROUND_RADIUS, 3); + } + int width; + int x = m_cFrameListRel.iX + TEXT_BORDER_WIDTH; + y += m_nFontListHeight; + for(int row = 0; row < m_pLines->rows; row++) + { + width = m_pLines->rowWidth[row] ; + if(width > m_cFrameListRel.iWidth - x + m_cFrameListRel.iX - TEXT_BORDER_WIDTH) + { + width = m_cFrameListRel.iWidth - x + m_cFrameListRel.iX - TEXT_BORDER_WIDTH; + //TRACE(" normalize width to %d , x:%d \r\n",width,x); + } + m_pcFontList->RenderString(x+m_cFrame.iX, y+m_cFrame.iY, + width, m_pLines->lineArray[row][line].c_str(), + color, 0, true); // UTF-8 + x += m_pLines->rowWidth[row] + ROW_BORDER_WIDTH; + } + } +} + +void CListFrame::refreshLine(int line) +{ + if( frameBuffer == NULL) return; + if( m_nNrOfLines <= 0) return; + + if((line < m_nCurrentLine) && (line > m_nCurrentLine + m_nLinesPerPage)) + return; + + uint8_t color; + int rel_line = line - m_nCurrentLine; + int y = m_cFrameListRel.iY + TEXT_BORDER_WIDTH + (rel_line*m_nFontListHeight); + + if(line == m_nSelectedLine && m_showSelection == true) + { + color = LIST_FONT_COLOR_SELECTED; + frameBuffer->paintBoxRel(m_cFrameListRel.iX+m_cFrame.iX, y+m_cFrame.iY, + m_cFrameListRel.iWidth, m_nFontListHeight, LIST_BACKGROUND_COLOR_SELECTED, + ROUND_RADIUS, 3); + } + else + { + color = LIST_FONT_COLOR; + frameBuffer->paintBoxRel(m_cFrameListRel.iX+m_cFrame.iX, y+m_cFrame.iY, + m_cFrameListRel.iWidth, m_nFontListHeight, LIST_BACKGROUND_COLOR); + } + int width; + int x = m_cFrameListRel.iX + TEXT_BORDER_WIDTH; + y += m_nFontListHeight; + for(int row = 0; row < m_pLines->rows; row++) + { + width = m_pLines->rowWidth[row] ; + if(width > m_cFrameListRel.iWidth - x + m_cFrameListRel.iX - 2*TEXT_BORDER_WIDTH) + { + width = m_cFrameListRel.iWidth - x + m_cFrameListRel.iX - 2*TEXT_BORDER_WIDTH; + //TRACE(" normalize to %d,x:%d\r\n",width,x); + } + m_pcFontList->RenderString(x+m_cFrame.iX, y+m_cFrame.iY, + width, m_pLines->lineArray[row][line].c_str(), + color, 0, true); // UTF-8 + x += m_pLines->rowWidth[row] + ROW_BORDER_WIDTH; + } +} + +void CListFrame::refreshHeaderList(void) +{ + //TRACE("[CListFrame]->refreshHeaderList \r\n"); + if( frameBuffer == NULL) return; + if(!(m_nMode & HEADER_LINE))return; + + frameBuffer->paintBoxRel(m_cFrameHeaderListRel.iX+m_cFrame.iX, m_cFrameHeaderListRel.iY+m_cFrame.iY, + m_cFrameHeaderListRel.iWidth, m_cFrameHeaderListRel.iHeight, HEADER_LIST_BACKGROUND_COLOR); + + int width; + int x = m_cFrameHeaderListRel.iX + TEXT_BORDER_WIDTH; + int y = m_cFrameHeaderListRel.iY + m_nFontHeaderListHeight + 2; + bool loop = true; + for(int row = 0; row < m_pLines->rows && loop == true; row++) + { + width = m_pLines->rowWidth[row] ; + if(width > m_cFrameHeaderListRel.iWidth - x + m_cFrameHeaderListRel.iX - 2*TEXT_BORDER_WIDTH) + { + width = m_cFrameHeaderListRel.iWidth - x + m_cFrameHeaderListRel.iX - 2*TEXT_BORDER_WIDTH; + //TRACE(" normalize width to %d , x:%d \r\n",width,x); + loop = false; + } + m_pcFontHeaderList->RenderString(x+m_cFrame.iX, y+m_cFrame.iY, + width, m_pLines->lineHeader[row].c_str(), + HEADER_LIST_FONT_COLOR, 0, true); // UTF-8 + x += m_pLines->rowWidth[row] + ROW_BORDER_WIDTH; + } +} + +void CListFrame::scrollLineDown(const int lines) +{ + //TRACE("[CListFrame]->scrollLineDown \r\n"); + + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 1) return; + + if(m_nSelectedLine < m_nNrOfLines - 1) { + m_nSelectedLine++; + // check if the cursor moves out of the window + if(m_nSelectedLine - m_nCurrentLine > m_nLinesPerPage-1) { + // yes, scroll to next page + //TRACE("[CListFrame] m_nSelectedLine: %d, \r\n",m_nSelectedLine); + scrollPageDown(1); + } else { + refreshLine(m_nSelectedLine-lines); + refreshLine(m_nSelectedLine); + } + } else { + m_nCurrentPage = 0; + m_nCurrentLine = m_nSelectedLine = 0; + refreshList(); + } + +} + +void CListFrame::scrollLineUp(const int lines) +{ + //TRACE("[CListFrame]->scrollLineUp \r\n"); + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 1) return; + + if(m_nSelectedLine > 0) { + m_nSelectedLine--; + // check if the cursor moves out of the window + if(m_nSelectedLine < m_nCurrentLine ) { + // yes, scroll to next page + //TRACE("[CListFrame] m_nSelectedLine: %d, \r\n",m_nSelectedLine); + scrollPageUp(1); + } else { + refreshLine(m_nSelectedLine+lines); + refreshLine(m_nSelectedLine); + } + } else if(m_nSelectedLine == 0) { + //m_nCurrentPage = m_nNrOfPages - 1; + //m_nCurrentLine = m_nSelectedLine = m_nNrOfLines - 1; + //refresh(); + setSelectedLine(m_nNrOfLines - 1); + } +} + +void CListFrame::scrollPageDown(const int pages) +{ + //TRACE("[CListFrame]->ScrollPageDown \r\n"); + + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 1) return; + + if(m_nCurrentPage + pages < m_nNrOfPages) + { + m_nCurrentPage += pages; + } + else + { + m_nCurrentPage = m_nNrOfPages - 1; + } + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + if(m_nSelectedLine < m_nCurrentLine || m_nSelectedLine -m_nCurrentLine >= m_nLinesPerPage ) + { + m_nSelectedLine = m_nCurrentLine; + } + //TRACE("[CListFrame] m_nCurrentLine: %d, m_nCurrentPage: %d \r\n",m_nCurrentLine,m_nCurrentPage); + refresh(); +}; + +void CListFrame::scrollPageUp(const int pages) +{ + //TRACE("[CListFrame]->ScrollPageUp \r\n"); + + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 1) return; + + if(m_nCurrentPage - pages > 0) + { + m_nCurrentPage -= pages; + } + else + { + m_nCurrentPage = 0; + } + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + if(m_nSelectedLine < m_nCurrentLine || m_nSelectedLine - m_nCurrentLine >= m_nLinesPerPage ) + { + m_nSelectedLine = m_nCurrentLine; + } + //TRACE("[CListFrame] m_nCurrentLine: %d, m_nCurrentPage: %d \r\n",m_nCurrentLine,m_nCurrentPage); + refresh(); +} + +void CListFrame::refresh(void) +{ + //TRACE("[CListFrame]->Refresh\r\n"); + if( frameBuffer == NULL) return; + + refreshTitle(); + refreshScroll(); + refreshHeaderList(); + refreshList(); +} + +bool CListFrame::setLines(LF_LINES* lines) +{ + if(lines == NULL) + return(false); + //TRACE("[CListFrame]->setLines \r\n"); + + m_pLines = lines; + m_nNrOfRows = lines->rows; + + if(m_nNrOfRows > LF_MAX_ROWS) + m_nNrOfRows = LF_MAX_ROWS; + onNewLineArray(); + refresh(); + + return(true); +} + +bool CListFrame::setTitle(char* title) +{ + //TRACE("[CListFrame]->setTitle \r\n"); + if(!(m_nMode & TITLE)) return(false); + if(title == NULL) return(false); + + m_textTitle = title; + refreshTitle(); + + return(true); +} + +bool CListFrame::setSelectedLine(int selection) +{ + //TRACE("[CListFrame]->setSelectedLine %d \r\n",selection); + bool result = false; + if(selection >= 0 && selection < m_nNrOfLines) + { + m_nSelectedLine = selection; + m_nCurrentPage = selection / m_nLinesPerPage; + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + refreshList(); + refreshScroll(); //NEW + result = true; + //TRACE(" selected line: %d,%d,%d \r\n",m_nSelectedLine,m_nCurrentPage,m_nCurrentLine); + } + + return (result); +} + +void CListFrame::hide(void) +{ + if(frameBuffer == NULL) return; + TRACE("[CListFrame]->hide %s\n", m_textTitle.c_str()); + + frameBuffer->paintBackgroundBoxRel(m_cFrame.iX, m_cFrame.iY, m_cFrame.iWidth, m_cFrame.iHeight); + frameBuffer = NULL; +} + +void CListFrame::paint(void) +{ + TRACE("[CListFrame]->paint %s\n", m_textTitle.c_str()); + frameBuffer = CFrameBuffer::getInstance(); + refresh(); +} diff --git a/src/gui/widget/listframe.h b/src/gui/widget/listframe.h new file mode 100644 index 000000000..bbabc1b59 --- /dev/null +++ b/src/gui/widget/listframe.h @@ -0,0 +1,164 @@ + +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: listframe.h . + + Description: interface of the CTextBox class + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Günther initial implementation +****************************************************************************/ + +#if !defined(LISTFRAME_H_) +#define LISTFRAME_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#define LF_MAX_ROWS 6 +typedef struct +{ + int rows; + std::string lineHeader[LF_MAX_ROWS]; + std::vector lineArray[LF_MAX_ROWS]; + int rowWidth[LF_MAX_ROWS]; +}LF_LINES; + +class CListFrame +{ + private: + /* Functions */ + void onNewLineArray(void); + void initVar(void); + void initFramesRel(void); + void refreshTitle(void); + void refreshScroll(void); + void refreshList(void); + void refreshHeaderList(void); + void reSizeMainFrameWidth(int maxTextWidth); + void reSizeMainFrameHeight(int maxTextHeight); + + /* Variables */ + LF_LINES* m_pLines; + + CBox m_cFrame; + CBox m_cFrameTitleRel; + CBox m_cFrameListRel; + CBox m_cFrameScrollRel; + CBox m_cFrameHeaderListRel; + + int m_nMaxHeight; + int m_nMaxWidth; + + int m_nMode; + + int m_nNrOfPages; + int m_nNrOfLines; + int m_nNrOfRows; + int m_nMaxLineWidth; + int m_nLinesPerPage; + int m_nCurrentLine; + int m_nCurrentPage; + int m_nSelectedLine; + + bool m_showSelection; + + Font* m_pcFontTitle; + std::string m_textTitle; + int m_nFontTitleHeight; + + Font* m_pcFontList; + int m_nFontListHeight; + + Font* m_pcFontHeaderList; + int m_nFontHeaderListHeight; + + CFrameBuffer * frameBuffer; + public: + /* Constructor */ + CListFrame(); + CListFrame( LF_LINES* lines); + CListFrame( LF_LINES* lines, + Font* font_text, + const int mode, + const CBox* position, + const char* textTitle = NULL, + Font* font_title = NULL); + + virtual ~CListFrame(); + + /* Functions */ + void refresh(void); + void refreshLine(int line); + void scrollPageDown(const int pages); + void scrollPageUp(const int pages); + void scrollLineDown(const int lines); + void scrollLineUp(const int lines); + bool setLines(LF_LINES* lines); + bool setTitle(char* title); + bool setSelectedLine(int selection); + void hide(void); + void paint(void); + +inline CBox getWindowsPos(void) {return(m_cFrame);}; +inline int getMaxLineWidth(void) {return(m_nMaxLineWidth);}; +inline int getSelectedLine(void) {return(m_nSelectedLine);}; +inline int getLines(void) {return(m_nNrOfLines);}; +inline int getPages(void) {return(m_nNrOfPages);}; +inline void showSelection(bool show) {m_showSelection = show;refreshLine(m_nSelectedLine);}; +inline void movePosition(int x, int y){m_cFrame.iX = x; m_cFrame.iY = y;}; + + + /* Variables */ + typedef enum mode_ + { + AUTO_WIDTH = 0x01, + AUTO_HIGH = 0x02, + SCROLL = 0x04, + TITLE = 0x08, + HEADER_LINE = 0x80 + }mode; +}; + +#endif /*LISTFRAME_H_*/ diff --git a/src/gui/widget/menue.cpp b/src/gui/widget/menue.cpp new file mode 100644 index 000000000..983b77dec --- /dev/null +++ b/src/gui/widget/menue.cpp @@ -0,0 +1,1331 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + + +#include + +#define ROUND_RADIUS 9 + +/* the following generic menu items are integrated into multiple menus at the same time */ +CMenuSeparator CGenericMenuSeparator; +CMenuSeparator CGenericMenuSeparatorLine(CMenuSeparator::LINE); +CMenuForwarder CGenericMenuBack(LOCALE_MENU_BACK); +CMenuSeparator * const GenericMenuSeparator = &CGenericMenuSeparator; +CMenuSeparator * const GenericMenuSeparatorLine = &CGenericMenuSeparatorLine; +CMenuForwarder * const GenericMenuBack = &CGenericMenuBack; + +void CMenuItem::init(const int X, const int Y, const int DX, const int OFFX) +{ + x = X; + y = Y; + dx = DX; + offx = OFFX; +} + +void CMenuItem::setActive(const bool Active) +{ + active = Active; + if (x != -1) + paint(); +} + +CMenuWidget::CMenuWidget() +{ + nameString = g_Locale->getText(NONEXISTANT_LOCALE); + name = NONEXISTANT_LOCALE; + iconfile = ""; + selected = -1; + iconOffset = 0; + offx = offy = 0; +} + +CMenuWidget::CMenuWidget(const neutrino_locale_t Name, const std::string & Icon, const int mwidth, const int mheight) +{ + name = Name; + nameString = g_Locale->getText(NONEXISTANT_LOCALE); + + Init(Icon, mwidth, mheight); +} + +CMenuWidget::CMenuWidget(const char* Name, const std::string & Icon, const int mwidth, const int mheight) +{ + name = NONEXISTANT_LOCALE; + nameString = Name; + + Init(Icon, mwidth, mheight); +} + +void CMenuWidget::Init(const std::string & Icon, const int mwidth, const int mheight) +{ + frameBuffer = CFrameBuffer::getInstance(); + iconfile = Icon; + selected = -1; + width = mwidth; + if(width > (int) frameBuffer->getScreenWidth()) + width = frameBuffer->getScreenWidth(); + height = mheight; + wanted_height=mheight; + current_page=0; + offx = offy = 0; +} + +void CMenuWidget::move(int xoff, int yoff) +{ + offx = xoff; + offy = yoff; +} + +CMenuWidget::~CMenuWidget() +{ + for(unsigned int count=0;counthide(); + + + bool fadeIn = g_settings.widget_fade; + bool fadeOut = false; + int fadeValue; + uint32_t fadeTimer = 0; + if ( fadeIn ) { + fadeValue = 0x10; + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } + else + fadeValue = g_settings.gtx_alpha1; + + paint(); + int retval = menu_return::RETURN_REPAINT; + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + if ( fadeIn ) + fadeTimer = g_RCInput->addTimer( FADE_TIME, false ); + do { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd); + + if ( msg <= CRCInput::RC_MaxRC ) { + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + } + int handled= false; + + for (unsigned int i= 0; i< items.size(); i++) { + CMenuItem* titem = items[i]; + if ((titem->directKey != CRCInput::RC_nokey) && (titem->directKey == msg)) { + if (titem->isSelectable()) { + items[selected]->paint( false, (i == items.size()-1) ); + selected= i; + msg= CRCInput::RC_ok; + } else { + // swallow-key... + handled= true; + } + break; + } + } + + if (!handled) { + switch (msg) { + case (NeutrinoMessages::EVT_TIMER): + if(data == fadeTimer) { + if (fadeOut) { // disappear + fadeValue -= 0x10; + if (fadeValue <= 0x10) { + fadeValue = g_settings.gtx_alpha1; + g_RCInput->killTimer (fadeTimer); + msg = CRCInput::RC_timeout; + } else + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } else { // appears + fadeValue += 0x10; + if (fadeValue >= g_settings.gtx_alpha1) { + fadeValue = g_settings.gtx_alpha1; + g_RCInput->killTimer (fadeTimer); + fadeIn = false; + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + } else + frameBuffer->setBlendLevel(fadeValue, fadeValue); + } + + + } else { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) { + retval = menu_return::RETURN_EXIT_ALL; + msg = CRCInput::RC_timeout; + } + } + break; + case (CRCInput::RC_page_up) : + case (CRCInput::RC_page_down) : + if(msg==CRCInput::RC_page_up) { + if(current_page) { + pos = (int) page_start[current_page] - 1; + for (unsigned int count=pos ; count > 0; count--) { + CMenuItem* item = items[pos]; + if ( item->isSelectable() ) { + if ((pos < (int)page_start[current_page + 1]) && (pos >= (int)page_start[current_page])) { + items[selected]->paint( false, (pos == (int) items.size()-1) ); + item->paint( true , (count == items.size()-1)); + selected = pos; + } else { + selected=pos; + paintItems(); + } + break; + } + pos--; + } + } else { + pos = 0; + for (unsigned int count=0; count < items.size(); count++) { + CMenuItem* item = items[pos]; + if ( item->isSelectable() ) { + if ((pos < (int)page_start[current_page + 1]) && (pos >= (int)page_start[current_page])) { + items[selected]->paint( false, (count == items.size()-1) ); + item->paint( true, (count == items.size()-1) ); + selected = pos; + } else { + selected=pos; + paintItems(); + } + break; + } + pos++; + } + } + } + else if(msg==CRCInput::RC_page_down) { + pos = (int) page_start[current_page + 1];// - 1; + if(pos >= (int) items.size()) + pos = items.size()-1; + for (unsigned int count=pos ; count < items.size(); count++) { + CMenuItem* item = items[pos]; + if ( item->isSelectable() ) { + if ((pos < (int)page_start[current_page + 1]) && (pos >= (int)page_start[current_page])) { + items[selected]->paint( false ); + item->paint( true ); + selected = pos; + } else { + selected=pos; + paintItems(); + } + break; + } + pos++; + } + } + break; + case (CRCInput::RC_up) : + case (CRCInput::RC_down) : + { + //search next / prev selectable item + for (unsigned int count=1; count< items.size(); count++) { + if (msg==CRCInput::RC_up) { + pos = selected - count; + if ( pos < 0 ) + pos += items.size(); + } + else if(msg==CRCInput::RC_down) { + pos = (selected+ count)%items.size(); + } + + CMenuItem* item = items[pos]; + + if ( item->isSelectable() ) { + if ((pos < (int)page_start[current_page + 1]) && (pos >= (int)page_start[current_page])) + { // Item is currently on screen + //clear prev. selected + items[selected]->paint( false, (selected == (int) (page_start[current_page + 1]-1)) ); + //select new + item->paint( true ); + selected = pos; + } else { + selected=pos; + paintItems(); + } + break; + } + } + } + break; + case (CRCInput::RC_left): + if(!(items[selected]->can_arrow) || g_settings.menu_left_exit) { + msg = CRCInput::RC_timeout; + break; + } + case (CRCInput::RC_right): + case (CRCInput::RC_ok): + { + if(hasItem()) { + //exec this item... + CMenuItem* item = items[selected]; + item->msg = msg; + if ( fadeIn ) { + g_RCInput->killTimer(fadeTimer); + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + fadeIn = false; + } + int rv = item->exec( this ); + switch ( rv ) { + case menu_return::RETURN_EXIT_ALL: + retval = menu_return::RETURN_EXIT_ALL; + case menu_return::RETURN_EXIT: + msg = CRCInput::RC_timeout; + break; + case menu_return::RETURN_REPAINT: + paint(); + break; + } + } else + msg = CRCInput::RC_timeout; + } + break; + + case (CRCInput::RC_home): + exit_pressed = true; + msg = CRCInput::RC_timeout; + break; + case (CRCInput::RC_timeout): + break; + + case (CRCInput::RC_sat): + case (CRCInput::RC_favorites): + g_RCInput->postMsg (msg, 0); + //close any menue on dbox-key + case (CRCInput::RC_setup): + { + msg = CRCInput::RC_timeout; + retval = menu_return::RETURN_EXIT_ALL; + } + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) { + retval = menu_return::RETURN_EXIT_ALL; + msg = CRCInput::RC_timeout; + } + } + if(msg == CRCInput::RC_timeout) { + if ( fadeIn ) { + g_RCInput->killTimer(fadeTimer); + fadeIn = false; + } + if ((!fadeOut) && g_settings.widget_fade) { + fadeOut = true; + fadeTimer = g_RCInput->addTimer( FADE_TIME, false ); + timeoutEnd = CRCInput::calcTimeoutEnd( 1 ); + msg = 0; + continue; + } + } + + if ( msg <= CRCInput::RC_MaxRC ) + { + // recalculate timeout für RC-Tasten + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + } + } + } + while ( msg!=CRCInput::RC_timeout ); + hide(); + + if ( fadeIn || fadeOut ) { + g_RCInput->killTimer(fadeTimer); + frameBuffer->setBlendLevel(g_settings.gtx_alpha1, g_settings.gtx_alpha2); + } + + if(!parent) + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + + for (unsigned int count = 0; count < items.size(); count++) { + CMenuItem* item = items[count]; + item->init(-1, 0, 0, 0); + } + return retval; +} + +void CMenuWidget::hide() +{ + frameBuffer->paintBackgroundBoxRel(x, y, width+15,height+10 ); +} + +void CMenuWidget::paint() +{ + const char * l_name; + if(name == NONEXISTANT_LOCALE) + l_name = nameString.c_str(); + else + l_name = g_Locale->getText(name); + + CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8 /*, l_name*/); //FIXME menu name + + height=wanted_height; + + if(height > ((int)frameBuffer->getScreenHeight() - 10)) + height = frameBuffer->getScreenHeight() - 10; + + int neededWidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(l_name, true); // UTF-8 + if (neededWidth > width-48) { + width= neededWidth+ 49; + if(width > (int)frameBuffer->getScreenWidth()) + width = frameBuffer->getScreenWidth(); + } + int hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + int itemHeightTotal=0; + int heightCurrPage=0; + page_start.clear(); + page_start.push_back(0); + total_pages=1; + for (unsigned int i= 0; i< items.size(); i++) { + int item_height=items[i]->getHeight(); + itemHeightTotal+=item_height; + heightCurrPage+=item_height; + if(heightCurrPage > (height-hheight)) { + page_start.push_back(i); + total_pages++; + heightCurrPage=item_height; + } + } + page_start.push_back(items.size()); + + iconOffset= 0; + for (unsigned int i= 0; i< items.size(); i++) { + if ((!(items[i]->iconName.empty())) || CRCInput::isNumeric(items[i]->directKey)) + { + iconOffset = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + break; + } + } + + // shrink menu if less items + if(hheight+itemHeightTotal < height) + height=hheight+itemHeightTotal; + + x = offx + frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width ) >> 1 ); + y = offy + frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - height) >> 1 ); + + int sb_width; + if(total_pages > 1) + sb_width=15; + else + sb_width=0; + + //frameBuffer->paintBoxRel(x,y, width+sb_width,hheight, COL_MENUHEAD_PLUS_0); + frameBuffer->paintBoxRel(x, y, width+sb_width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); //FIXME rounded + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+38,y+hheight+1, width-40, l_name, COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintIcon(iconfile, x + 8, y + 5); + + item_start_y = y+hheight; + paintItems(); +} + +void CMenuWidget::paintItems() +{ + int item_height=height-(item_start_y-y); + + //Item not currently on screen + if (selected >= 0) + { + while(selected < (int)page_start[current_page]) + current_page--; + while(selected >= (int)page_start[current_page + 1]) + current_page++; + } + + // Scrollbar + if(total_pages>1) + { + int sbh= ((item_height-4) / total_pages); + // items box + frameBuffer->paintBoxRel(x,item_start_y, width+15,item_height+10, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + // scrollbar + frameBuffer->paintBoxRel(x+ width,item_start_y, 15, item_height, COL_MENUCONTENT_PLUS_1); + frameBuffer->paintBoxRel(x+ width +2, item_start_y+ 2+ current_page* sbh, 11, sbh, COL_MENUCONTENT_PLUS_3); + } else + frameBuffer->paintBoxRel(x,item_start_y, width,item_height, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//FIXME round + + int ypos=item_start_y; + for (unsigned int count = 0; count < items.size(); count++) { + CMenuItem* item = items[count]; + + if ((count >= page_start[current_page]) && (count < page_start[current_page + 1])) { + item->init(x, ypos, width, iconOffset); + if( (item->isSelectable()) && (selected==-1) ) { + ypos = item->paint(true); + selected = count; + } else { + //ypos = item->paint(selected==((signed int) count), (count == items.size()-1)); + ypos = item->paint(selected==((signed int) count), (count == (page_start[current_page + 1]-1))); + } + } else { + /* x = -1 is a marker which prevents the item from being painted on setActive changes */ + item->init(-1, 0, 0, 0); + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------- +CMenuOptionNumberChooser::CMenuOptionNumberChooser(const neutrino_locale_t name, int * const OptionValue, const bool Active, const int min_value, const int max_value, CChangeObserver * const Observ, const int print_offset, const int special_value, const neutrino_locale_t special_value_name, const char * non_localized_name) +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionName = name; + active = Active; + optionValue = OptionValue; + + lower_bound = min_value; + upper_bound = max_value; + + display_offset = print_offset; + + localized_value = special_value; + localized_value_name = special_value_name; + + optionString = non_localized_name; + can_arrow = true; + observ = Observ; +} + +int CMenuOptionNumberChooser::exec(CMenuTarget*) +{ + if(msg == CRCInput::RC_left) { + if (((*optionValue) > upper_bound) || ((*optionValue) <= lower_bound)) + *optionValue = upper_bound; + else + (*optionValue)--; + } else { + if (((*optionValue) >= upper_bound) || ((*optionValue) < lower_bound)) + *optionValue = lower_bound; + else + (*optionValue)++; + } + paint(true); + if(observ) + observ->changeNotify(optionName, optionValue); + + return menu_return::RETURN_NONE; +} + +int CMenuOptionNumberChooser::paint(bool selected, bool last) +{ + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + if (!active) + { + color = COL_MENUCONTENTINACTIVE; + bgcolor = COL_MENUCONTENTINACTIVE_PLUS_0; + } + + if(selected) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 3); //FIXME + else if(last) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 2); //FIXME + else + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor); + + const char * l_option; + char option_value[11]; + + if ((localized_value_name == NONEXISTANT_LOCALE) || ((*optionValue) != localized_value)) + { + sprintf(option_value, "%d", ((*optionValue) + display_offset)); + l_option = option_value; + } + else + l_option = g_Locale->getText(localized_value_name); + + int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(l_option, true); // UTF-8 + int stringstartposName = x + offx + 10; + int stringstartposOption = x + dx - stringwidth - 10; //+ offx + + const char * l_optionName = (optionString != NULL) ? optionString : g_Locale->getText(optionName); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposName, y+height,dx- (stringstartposName - x), l_optionName, color, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposOption, y+height,dx- (stringstartposOption - x), l_option, color, 0, true); // UTF-8 + + if (selected) + { + char str[256]; + snprintf(str, 255, "%s %s", l_optionName, l_option); + CVFD::getInstance()->showMenuText(0, str, -1, true); + //CVFD::getInstance()->showMenuText(0, l_optionName, -1, true); // UTF-8 + //CVFD::getInstance()->showMenuText(1, l_option, -1, true); // UTF-8 + } + + return y+height; +} + +CMenuOptionChooser::CMenuOptionChooser(const neutrino_locale_t OptionName, int * const OptionValue, const struct keyval * const Options, const unsigned Number_Of_Options, const bool Active, CChangeObserver * const Observ, const neutrino_msg_t DirectKey, const std::string & IconName, bool Pulldown) +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionNameString = g_Locale->getText(OptionName); + optionName = OptionName; + options = Options; + active = Active; + optionValue = OptionValue; + number_of_options = Number_Of_Options; + observ = Observ; + directKey = DirectKey; + iconName = IconName; + can_arrow = true; + pulldown = Pulldown; +} + +CMenuOptionChooser::CMenuOptionChooser(const char* OptionName, int * const OptionValue, const struct keyval * const Options, const unsigned Number_Of_Options, const bool Active, CChangeObserver * const Observ, const neutrino_msg_t DirectKey, const std::string & IconName, bool Pulldown) +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionNameString = OptionName; + optionName = NONEXISTANT_LOCALE; + options = Options; + active = Active; + optionValue = OptionValue; + number_of_options = Number_Of_Options; + observ = Observ; + directKey = DirectKey; + iconName = IconName; + can_arrow = true; + pulldown = Pulldown; +} + +void CMenuOptionChooser::setOptionValue(const int newvalue) +{ + *optionValue = newvalue; +} + +int CMenuOptionChooser::getOptionValue(void) const +{ + return *optionValue; +} + + +int CMenuOptionChooser::exec(CMenuTarget*) +{ + bool wantsRepaint = false; + int ret = menu_return::RETURN_NONE; + + if((msg == CRCInput::RC_ok) && pulldown) { + int select = -1; + char cnt[5]; + CMenuWidget* menu = new CMenuWidget(optionNameString.c_str(), NEUTRINO_ICON_SETTINGS); + menu->move(20, 0); + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&select); + for(unsigned int count = 0; count < number_of_options; count++) { + bool selected = false; + const char * l_option; + if (options[count].key == (*optionValue)) + selected = true; + + if(options[count].valname != 0) + l_option = options[count].valname; + else + l_option = g_Locale->getText(options[count].value); + sprintf(cnt, "%d", count); + menu->addItem(new CMenuForwarderNonLocalized(l_option, true, NULL, selector, cnt), selected); + } + menu->exec(NULL, ""); + ret = menu_return::RETURN_REPAINT;; + if(select >= 0) { + *optionValue = options[select].key; + } + delete menu; + delete selector; + } else { + for(unsigned int count = 0; count < number_of_options; count++) { + if (options[count].key == (*optionValue)) { + if(msg == CRCInput::RC_left) { + if(count > 0) + *optionValue = options[(count-1) % number_of_options].key; + else + *optionValue = options[number_of_options-1].key; + } else + *optionValue = options[(count+1) % number_of_options].key; + break; + } + } + } + paint(true); + if(observ) + wantsRepaint = observ->changeNotify(optionName, optionValue); + + if ( wantsRepaint ) + ret = menu_return::RETURN_REPAINT; + + return ret; +} + +int CMenuOptionChooser::paint( bool selected , bool last) +{ + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + if (!active) + { + color = COL_MENUCONTENTINACTIVE; + bgcolor = COL_MENUCONTENTINACTIVE_PLUS_0; + } + + if(selected) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 3); //FIXME + else if(last) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 2); //FIXME + else + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor); + + neutrino_locale_t option = NONEXISTANT_LOCALE; + const char * l_option = NULL; + + for(unsigned int count = 0 ; count < number_of_options; count++) { + if (options[count].key == *optionValue) { + option = options[count].value; + if(options[count].valname != 0) + l_option = options[count].valname; + else + l_option = g_Locale->getText(option); + break; + } + } + if(l_option == NULL) { + *optionValue = options[0].key; + option = options[0].value; + if(options[0].valname != 0) + l_option = options[0].valname; + else + l_option = g_Locale->getText(option); + } + + if (!(iconName.empty())) + { + frameBuffer->paintIcon(iconName, x + 10, y + ((height - 20) >> 1)); + } + else if (CRCInput::isNumeric(directKey)) + { +#if 0 + std::string newicon = CRCInput::getKeyName(directKey) + ".raw"; + //printf("MENU: newicon %s\n", newicon); + if(!frameBuffer->paintIcon(newicon, x + 10, y + ((height - 20) >> 1))) +#endif + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x + 15, y+ height, height, CRCInput::getKeyName(directKey), color, height); + } + + + + int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(l_option, true); // UTF-8 + int stringstartposName = x + offx + 10; + int stringstartposOption = x + dx - stringwidth - 10; //+ offx + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposName, y+height,dx- (stringstartposName - x), optionNameString.c_str(), color, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposOption, y+height,dx- (stringstartposOption - x), l_option, color, 0, true); // UTF-8 + + if (selected) + { + char str[256]; + snprintf(str, 255, "%s %s", optionNameString.c_str(), l_option); + CVFD::getInstance()->showMenuText(0, str, -1, true); + //CVFD::getInstance()->showMenuText(0, optionNameString.c_str(), -1, true); // UTF-8 + //CVFD::getInstance()->showMenuText(1, l_option, -1, true); // UTF-8 + } + + return y+height; +} + + +//------------------------------------------------------------------------------------------------------------------------------- + +CMenuOptionStringChooser::CMenuOptionStringChooser(const neutrino_locale_t OptionName, char* OptionValue, bool Active, CChangeObserver* Observ, const neutrino_msg_t DirectKey, const std::string & IconName, bool Pulldown) +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionName = OptionName; + active = Active; + optionValue = OptionValue; + observ = Observ; + + directKey = DirectKey; + iconName = IconName; + can_arrow = true; + pulldown = Pulldown; +} + + +CMenuOptionStringChooser::~CMenuOptionStringChooser() +{ + options.clear(); +} + +void CMenuOptionStringChooser::addOption(const char * const value) +{ + options.push_back(std::string(value)); +} + +int CMenuOptionStringChooser::exec(CMenuTarget* parent) +{ + bool wantsRepaint = false; + int ret = menu_return::RETURN_NONE; + + if (parent) + parent->hide(); + + if((!parent || msg == CRCInput::RC_ok) && pulldown) { + int select = -1; + char cnt[5]; + + CMenuWidget* menu = new CMenuWidget(optionName, NEUTRINO_ICON_SETTINGS); + //if(parent) menu->move(20, 0); + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&select); + for(unsigned int count = 0; count < options.size(); count++) { + bool selected = false; + if (strcmp(options[count].c_str(), optionValue) == 0) + selected = true; + sprintf(cnt, "%d", count); + menu->addItem(new CMenuForwarderNonLocalized(options[count].c_str(), true, NULL, selector, cnt), selected); + } + menu->exec(NULL, ""); + ret = menu_return::RETURN_REPAINT; + if(select >= 0) + strcpy(optionValue, options[select].c_str()); + delete menu; + delete selector; + } else { + //select next value + for(unsigned int count = 0; count < options.size(); count++) { + if (strcmp(options[count].c_str(), optionValue) == 0) { + if(msg == CRCInput::RC_left) { + if(count > 0) + strcpy(optionValue, options[(count - 1) % options.size()].c_str()); + else + strcpy(optionValue, options[options.size() - 1].c_str()); + } else + strcpy(optionValue, options[(count + 1) % options.size()].c_str()); + wantsRepaint = true; + break; + } + } + } + + if(parent) + paint(true); + if(observ) { + wantsRepaint = observ->changeNotify(optionName, optionValue); + } + if (wantsRepaint) + ret = menu_return::RETURN_REPAINT; + + return ret; +} + +int CMenuOptionStringChooser::paint( bool selected, bool last ) +{ + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + if (!active) { + color = COL_MENUCONTENTINACTIVE; + bgcolor = COL_MENUCONTENTINACTIVE_PLUS_0; + } + + if(selected) + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 3); //FIXME + else if(last) + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 2); //FIXME + else + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor); + + + const char * l_optionName = g_Locale->getText(optionName); + int optionwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(l_optionName, true); + //int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(optionValue, true); + int stringstartposName = x + offx + 10; + //int stringstartposOption = x + dx - stringwidth - 10; //+ offx + int stringstartposOption = x + offx + 10 + 10 + optionwidth; + + if (!(iconName.empty())) + { + CFrameBuffer::getInstance()->paintIcon(iconName, x + 10, y + ((height - 20) >> 1)); + } + else if (CRCInput::isNumeric(directKey)) + { + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x + 15, y+ height, height, CRCInput::getKeyName(directKey), color, height); + } + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposName, y+height, dx- (stringstartposName - x), l_optionName, color, 0, true); // UTF-8 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposOption, y+height, dx- (stringstartposOption - x), optionValue, color, 0, true); + + if (selected) + { + char str[256]; + snprintf(str, 255, "%s %s", l_optionName, optionValue); + CVFD::getInstance()->showMenuText(0, str, -1, true); + //CVFD::getInstance()->showMenuText(0, l_optionName, -1, true); // UTF-8 + //CVFD::getInstance()->showMenuText(1, optionValue); + } + + return y+height; +} + +//------------------------------------------------------------------------------------------------------------------------------- + +CMenuOptionLanguageChooser::CMenuOptionLanguageChooser(char* OptionValue, CChangeObserver* Observ, const char * const IconName) +{ + height = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + optionValue = OptionValue; + observ = Observ; + + directKey = CRCInput::RC_nokey; + iconName = IconName ? IconName : ""; +} + + +CMenuOptionLanguageChooser::~CMenuOptionLanguageChooser() +{ + options.clear(); +} + +void CMenuOptionLanguageChooser::addOption(const char * const value) +{ + options.push_back(std::string(value)); +} + +int CMenuOptionLanguageChooser::exec(CMenuTarget*) +{ + bool wantsRepaint = false; + + //select value + for(unsigned int count = 0; count < options.size(); count++) + { + if (strcmp(options[count].c_str(), optionValue) == 0) + { + strcpy(g_settings.language, options[(count + 1) % options.size()].c_str()); + break; + } + } + + paint(true); + if(observ) + { + wantsRepaint = observ->changeNotify(LOCALE_LANGUAGESETUP_SELECT, optionValue); + } + return menu_return::RETURN_EXIT; + if ( wantsRepaint ) + return menu_return::RETURN_REPAINT; + else + return menu_return::RETURN_NONE; +} + +int CMenuOptionLanguageChooser::paint( bool selected, bool last ) +{ + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + + if(selected) + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 3); //FIXME + else if(last) + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 2); //FIXME + else + CFrameBuffer::getInstance()->paintBoxRel(x, y, dx, height, bgcolor); + + if (!(iconName.empty())) + { + CFrameBuffer::getInstance()->paintIcon(iconName, x + 10, y + ((height - 11) >> 1)); + } + + //int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(optionValue); + int stringstartposOption = x + offx + 10; + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposOption, y+height,dx- (stringstartposOption - x), optionValue, color); + + if (selected) + { + CVFD::getInstance()->showMenuText(1, optionValue); + } + + return y+height; +} + + + +//------------------------------------------------------------------------------------------------------------------------------- +CMenuForwarder::CMenuForwarder(const neutrino_locale_t Text, const bool Active, const char * const Option, CMenuTarget* Target, const char * const ActionKey, neutrino_msg_t DirectKey, const char * const IconName) +{ + option = Option; + option_string = NULL; + text=Text; + active = Active; + jumpTarget = Target; + actionKey = ActionKey ? ActionKey : ""; + directKey = DirectKey; + iconName = IconName ? IconName : ""; +} + +CMenuForwarder::CMenuForwarder(const neutrino_locale_t Text, const bool Active, const std::string &Option, CMenuTarget* Target, const char * const ActionKey, neutrino_msg_t DirectKey, const char * const IconName) +{ + option = NULL; + option_string = &Option; + text=Text; + active = Active; + jumpTarget = Target; + actionKey = ActionKey ? ActionKey : ""; + directKey = DirectKey; + iconName = IconName ? IconName : ""; +} + +int CMenuForwarder::getHeight(void) const +{ + return g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); +} + +int CMenuForwarder::getWidth(void) const +{ + int tw = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(g_Locale->getText(text), true); + const char * option_text = NULL; + + if (option) + option_text = option; + else if (option_string) + option_text = option_string->c_str(); + + + if (option_text != NULL) + tw += 10 + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(option_text, true); + + return tw; +} + +int CMenuForwarder::exec(CMenuTarget* parent) +{ + if(jumpTarget) + { + return jumpTarget->exec(parent, actionKey); + } + else + { + return menu_return::RETURN_EXIT; + } +} + +const char * CMenuForwarder::getOption(void) +{ + if (option) + return option; + else + if (option_string) + return option_string->c_str(); + else + return NULL; +} + +const char * CMenuForwarder::getName(void) +{ + return g_Locale->getText(text); +} + +int CMenuForwarder::paint(bool selected, bool last) +{ + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + int height = getHeight(); + const char * l_text = getName(); + int stringstartposX = x + offx + 10; + + const char * option_text = getOption(); + + if (selected) + { + char str[256]; + + if (option_text != NULL) { + snprintf(str, 255, "%s %s", l_text, option_text); + CVFD::getInstance()->showMenuText(0, str, -1, true); + } else + CVFD::getInstance()->showMenuText(0, l_text, -1, true); + } + + unsigned char color = COL_MENUCONTENT; + fb_pixel_t bgcolor = COL_MENUCONTENT_PLUS_0; + if (selected) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else if (!active) + { + color = COL_MENUCONTENTINACTIVE; + bgcolor = COL_MENUCONTENTINACTIVE_PLUS_0; + } + + if(selected) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 3); //FIXME + else if(last) + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor, ROUND_RADIUS, 2); //FIXME + else + frameBuffer->paintBoxRel(x, y, dx, height, bgcolor); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposX, y+ height, dx- (stringstartposX - x), l_text, color, 0, true); // UTF-8 + + if (!iconName.empty()) + { + frameBuffer->paintIcon(iconName, x + 10, y+ ((height- 20)>>1) ); + } + else if (CRCInput::isNumeric(directKey)) + { +#if 0 +printf("MENU: key %s\n", CRCInput::getKeyName(directKey).c_str()); fflush(stdout); + std::string newicon = CRCInput::getKeyName(directKey) + ".raw"; +printf("MENU: newicon %s\n", newicon.c_str()); fflush(stdout); + if(!frameBuffer->paintIcon(newicon, x + 10, y + ((height - 20) >> 1))) +#endif + g_Font[SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER]->RenderString(x + 15, y+ height, height, CRCInput::getKeyName(directKey), color, height); + } + + if (option_text != NULL) + { + int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(option_text, true); + int stringstartposOption = std::max(stringstartposX + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(l_text, true) + 10, + x + dx - stringwidth - 10); //+ offx + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposOption, y+height,dx- (stringstartposOption- x), option_text, color, 0, true); + } + + return y+ height; +} + + +//------------------------------------------------------------------------------------------------------------------------------- +const char * CMenuForwarderNonLocalized::getName(void) +{ + return the_text.c_str(); +} + +CMenuForwarderNonLocalized::CMenuForwarderNonLocalized(const char * const Text, const bool Active, const char * const Option, CMenuTarget* Target, const char * const ActionKey, neutrino_msg_t DirectKey, const char * const IconName) : CMenuForwarder(NONEXISTANT_LOCALE, Active, Option, Target, ActionKey, DirectKey, IconName) +{ + the_text = Text; +} + +CMenuForwarderNonLocalized::CMenuForwarderNonLocalized(const char * const Text, const bool Active, const std::string &Option, CMenuTarget* Target, const char * const ActionKey, neutrino_msg_t DirectKey, const char * const IconName) : CMenuForwarder(NONEXISTANT_LOCALE, Active, Option, Target, ActionKey, DirectKey, IconName) +{ + the_text = Text; +} + +//------------------------------------------------------------------------------------------------------------------------------- +CMenuSeparator::CMenuSeparator(const int Type, const neutrino_locale_t Text) +{ + directKey = CRCInput::RC_nokey; + iconName = ""; + type = Type; + text = Text; +} + + +int CMenuSeparator::getHeight(void) const +{ + return (text == NONEXISTANT_LOCALE) ? 10 : g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); +} + +int CMenuSeparator::getWidth(void) const +{ + return 0; +} + +const char * CMenuSeparator::getString(void) +{ + return g_Locale->getText(text); +} + +int CMenuSeparator::paint(bool selected, bool last) +{ + int height; + CFrameBuffer * frameBuffer = CFrameBuffer::getInstance(); + height = getHeight(); + + frameBuffer->paintBoxRel(x,y, dx, height, COL_MENUCONTENT_PLUS_0); + if ((type & LINE)) + { + frameBuffer->paintHLineRel(x+10,dx-20,y+(height>>1), COL_MENUCONTENT_PLUS_3); + frameBuffer->paintHLineRel(x+10,dx-20,y+(height>>1)+1, COL_MENUCONTENT_PLUS_1); + } + if ((type & STRING)) + { + + if (text != NONEXISTANT_LOCALE) + { + int stringstartposX; + + const char * l_text = getString(); + int stringwidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(l_text, true); // UTF-8 + + /* if no alignment is specified, align centered */ + if (type & ALIGN_LEFT) + stringstartposX = x + 20; + else if (type & ALIGN_RIGHT) + stringstartposX = x + dx - stringwidth - 20; + else /* ALIGN_CENTER */ + stringstartposX = x + (dx >> 1) - (stringwidth >> 1); + + frameBuffer->paintBoxRel(stringstartposX-5, y, stringwidth+10, height, COL_MENUCONTENT_PLUS_0); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(stringstartposX, y+height,dx- (stringstartposX- x) , l_text, COL_MENUCONTENTINACTIVE, 0, true); // UTF-8 + + if (selected) + { + CVFD::getInstance()->showMenuText(0, l_text, -1, true); // UTF-8 + //CVFD::getInstance()->showMenuText(1, "", -1, true); // UTF-8 + } + } + } + return y+ height; +} + +bool CPINProtection::check() +{ + char cPIN[4]; + neutrino_locale_t hint = NONEXISTANT_LOCALE; + do + { + cPIN[0] = 0; + CPINInput* PINInput = new CPINInput(LOCALE_PINPROTECTION_HEAD, cPIN, 4, hint); + PINInput->exec( getParent(), ""); + delete PINInput; + hint = LOCALE_PINPROTECTION_WRONGCODE; + } while ((strncmp(cPIN,validPIN,4) != 0) && (cPIN[0] != 0)); + return ( strncmp(cPIN,validPIN,4) == 0); +} + + +bool CZapProtection::check() +{ + + int res; + char cPIN[5]; + neutrino_locale_t hint2 = NONEXISTANT_LOCALE; + do + { + cPIN[0] = 0; + + CPLPINInput* PINInput = new CPLPINInput(LOCALE_PARENTALLOCK_HEAD, cPIN, 4, hint2, fsk); + + res = PINInput->exec(getParent(), ""); + delete PINInput; + + hint2 = LOCALE_PINPROTECTION_WRONGCODE; + } while ( (strncmp(cPIN,validPIN,4) != 0) && + (cPIN[0] != 0) && + ( res == menu_return::RETURN_REPAINT ) && + ( fsk >= g_settings.parentallock_lockage ) ); + return ( ( strncmp(cPIN,validPIN,4) == 0 ) || + ( fsk < g_settings.parentallock_lockage ) ); +} + +int CLockedMenuForwarder::exec(CMenuTarget* parent) +{ + Parent = parent; + if( (g_settings.parentallock_prompt != PARENTALLOCK_PROMPT_NEVER) || AlwaysAsk ) + if (!check()) + { + Parent = NULL; + return menu_return::RETURN_REPAINT; + } + + Parent = NULL; + return CMenuForwarder::exec(parent); +} + +int CMenuSelectorTarget::exec(CMenuTarget* parent, const std::string & actionKey) +{ + if (actionKey != "") + *m_select = atoi(actionKey.c_str()); + else + *m_select = -1; + return menu_return::RETURN_EXIT; +} + diff --git a/src/gui/widget/menue.h b/src/gui/widget/menue.h new file mode 100644 index 000000000..c03c696b6 --- /dev/null +++ b/src/gui/widget/menue.h @@ -0,0 +1,399 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __MENU__ +#define __MENU__ + +#include +#include +#include + +#include +#include + +struct menu_return +{ + enum + { + RETURN_NONE = 0, + RETURN_REPAINT = 1, + RETURN_EXIT = 2, + RETURN_EXIT_ALL = 4 + }; +}; + +class CChangeObserver +{ + public: + virtual ~CChangeObserver(){} + virtual bool changeNotify(const neutrino_locale_t OptionName, void *Data) + { + return false; + } +}; + +class CMenuTarget +{ + public: + + CMenuTarget(){} + virtual ~CMenuTarget(){} + virtual void hide(){} + virtual int exec(CMenuTarget* parent, const std::string & actionKey) = 0; +}; + + +class CMenuItem +{ + protected: + int x, y, dx, offx; + public: + bool active; + neutrino_msg_t directKey; + neutrino_msg_t msg; + bool can_arrow; + std::string iconName; + + CMenuItem() + { + x = -1; + directKey = CRCInput::RC_nokey; + iconName = ""; + can_arrow = false; + } + virtual ~CMenuItem(){} + + virtual void init(const int X, const int Y, const int DX, const int OFFX); + + virtual int paint (bool selected = false, bool last = false) = 0; + + virtual int getHeight(void) const = 0; + virtual int getWidth(void) const + { + return 0; + } + + virtual bool isSelectable(void) const + { + return false; + } + + virtual int exec(CMenuTarget* parent) + { + return 0; + } + virtual void setActive(const bool Active); +}; + +class CMenuSeparator : public CMenuItem +{ + int type; + + public: + neutrino_locale_t text; + + enum + { + EMPTY = 0, + LINE = 1, + STRING = 2, + ALIGN_CENTER = 4, + ALIGN_LEFT = 8, + ALIGN_RIGHT = 16 + }; + + + CMenuSeparator(const int Type = 0, const neutrino_locale_t Text = NONEXISTANT_LOCALE); + + int paint(bool selected=false, bool last = false); + int getHeight(void) const; + int getWidth(void) const; + + virtual const char * getString(void); +}; + +class CMenuForwarder : public CMenuItem +{ + const char * option; + const std::string * option_string; + CMenuTarget * jumpTarget; + std::string actionKey; + + protected: + neutrino_locale_t text; + + virtual const char * getOption(void); + virtual const char * getName(void); + + public: + + CMenuForwarder(const neutrino_locale_t Text, const bool Active=true, const char * const Option=NULL, CMenuTarget* Target=NULL, const char * const ActionKey = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const char * const IconName = NULL); + CMenuForwarder(const neutrino_locale_t Text, const bool Active, const std::string &Option, CMenuTarget* Target=NULL, const char * const ActionKey = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const char * const IconName = NULL); + int paint(bool selected=false, bool last = false); + int getHeight(void) const; + int getWidth(void) const; + int exec(CMenuTarget* parent); + bool isSelectable(void) const + { + return active; + } +}; + +class CMenuForwarderNonLocalized : public CMenuForwarder +{ + protected: + std::string the_text; + virtual const char * getName(void); + public: + // Text must be UTF-8 encoded: + CMenuForwarderNonLocalized(const char * const Text, const bool Active=true, const char * const Option=NULL, CMenuTarget* Target=NULL, const char * const ActionKey = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const char * const IconName = NULL); + CMenuForwarderNonLocalized(const char * const Text, const bool Active, const std::string &Option, CMenuTarget* Target=NULL, const char * const ActionKey = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const char * const IconName = NULL); + +}; + +class CAbstractMenuOptionChooser : public CMenuItem +{ + protected: + neutrino_locale_t optionName; + int height; + int * optionValue; + + int getHeight(void) const + { + return height; + } + + bool isSelectable(void) const + { + return active; + } +}; + +class CMenuOptionNumberChooser : public CAbstractMenuOptionChooser +{ + const char * optionString; + + int lower_bound; + int upper_bound; + + int display_offset; + + int localized_value; + neutrino_locale_t localized_value_name; + private: + CChangeObserver * observ; + + public: + CMenuOptionNumberChooser(const neutrino_locale_t name, int * const OptionValue, const bool Active, const int min_value, const int max_value, CChangeObserver * const Observ = NULL, const int print_offset = 0, const int special_value = 0, const neutrino_locale_t special_value_name = NONEXISTANT_LOCALE, const char * non_localized_name = NULL); + + int paint(bool selected, bool last = false); + + int exec(CMenuTarget* parent); +}; + +class CMenuOptionChooser : public CAbstractMenuOptionChooser +{ + public: + struct keyval + { + const int key; + const neutrino_locale_t value; + const char * valname; + }; + + private: + const struct keyval * options; + unsigned number_of_options; + CChangeObserver * observ; + std::string optionNameString; + bool pulldown; + + public: + CMenuOptionChooser(const neutrino_locale_t OptionName, int * const OptionValue, const struct keyval * const Options, const unsigned Number_Of_Options, const bool Active = false, CChangeObserver * const Observ = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const std::string & IconName= "", bool Pulldown = false); + CMenuOptionChooser(const char* OptionName, int * const OptionValue, const struct keyval * const Options, const unsigned Number_Of_Options, const bool Active = false, CChangeObserver * const Observ = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const std::string & IconName= "", bool Pulldown = false); + + void setOptionValue(const int newvalue); + int getOptionValue(void) const; + + int paint(bool selected, bool last = 0); + + int exec(CMenuTarget* parent); +}; + +class CMenuOptionStringChooser : public CMenuItem +{ + neutrino_locale_t optionName; + int height; + char * optionValue; + std::vector options; + CChangeObserver * observ; + bool pulldown; + + public: + CMenuOptionStringChooser(const neutrino_locale_t OptionName, char* OptionValue, bool Active = false, CChangeObserver* Observ = NULL, const neutrino_msg_t DirectKey = CRCInput::RC_nokey, const std::string & IconName= "", bool Pulldown = false); + ~CMenuOptionStringChooser(); + + void addOption(const char * value); + int paint(bool selected, bool last = 0); + int getHeight(void) const + { + return height; + } + bool isSelectable(void) const + { + return active; + } + + int exec(CMenuTarget* parent); +}; + +class CMenuOptionLanguageChooser : public CMenuItem +{ + int height; + char * optionValue; + std::vector options; + CChangeObserver * observ; + + public: + CMenuOptionLanguageChooser(char* OptionValue, CChangeObserver* Observ = NULL, const char * const IconName = NULL); + ~CMenuOptionLanguageChooser(); + + void addOption(const char * value); + int paint(bool selected, bool last = 0); + int getHeight(void) const + { + return height; + } + bool isSelectable(void) const + { + return true; + } + + int exec(CMenuTarget* parent); +}; + +class CMenuWidget : public CMenuTarget +{ + protected: + std::string nameString; + neutrino_locale_t name; + CFrameBuffer *frameBuffer; + std::vector items; + std::vector page_start; + std::string iconfile; + + int width; + int height; + int wanted_height; + int x; + int y; + int offx, offy; + int selected; + int iconOffset; + unsigned int item_start_y; + unsigned int current_page; + unsigned int total_pages; + bool exit_pressed; + void Init(const std::string & Icon, const int mwidth, const int mheight); + virtual void paintItems(); + + public: + CMenuWidget(); + CMenuWidget(const char* Name, const std::string & Icon = "", const int mwidth = 400, const int mheight = 576); + CMenuWidget(const neutrino_locale_t Name, const std::string & Icon = "", const int mwidth = 400, const int mheight = 576); + ~CMenuWidget(); + + virtual void addItem(CMenuItem* menuItem, const bool defaultselected = false); + bool hasItem(); + virtual void paint(); + virtual void hide(); + virtual int exec(CMenuTarget* parent, const std::string & actionKey); + void setSelected(unsigned int _new) { if(_new <= items.size()) selected = _new; }; + int getSelected() { return selected; }; + void move(int xoff, int yoff); + int getSelectedLine(void){return exit_pressed ? -1 : selected;}; +}; + +class CPINProtection +{ + protected: + char* validPIN; + bool check(); + virtual CMenuTarget* getParent() = 0; + public: + CPINProtection( char* validpin){ validPIN = validpin;}; +}; + +class CZapProtection : public CPINProtection +{ + protected: + virtual CMenuTarget* getParent() { return( NULL);}; + public: + int fsk; + + CZapProtection( char* validpin, int FSK ) : CPINProtection(validpin){ fsk= FSK; }; + bool check(); +}; + +class CLockedMenuForwarder : public CMenuForwarder, public CPINProtection +{ + CMenuTarget* Parent; + bool AlwaysAsk; + + protected: + virtual CMenuTarget* getParent(){ return Parent;}; + public: + CLockedMenuForwarder(const neutrino_locale_t Text, char* validPIN, bool alwaysAsk=false, const bool Active=true, char *Option=NULL, + CMenuTarget* Target=NULL, const char * const ActionKey = NULL, + neutrino_msg_t DirectKey = CRCInput::RC_nokey, const char * const IconName = NULL) + + : CMenuForwarder(Text, Active, Option, Target, ActionKey, DirectKey, IconName) , + CPINProtection( validPIN){AlwaysAsk = alwaysAsk;}; + + virtual int exec(CMenuTarget* parent); +}; + +class CMenuSelectorTarget : public CMenuTarget +{ + public: + CMenuSelectorTarget(int *select) {m_select = select;}; + int exec(CMenuTarget* parent, const std::string & actionKey); + + private: + int *m_select; +}; + +extern CMenuSeparator * const GenericMenuSeparator; +extern CMenuSeparator * const GenericMenuSeparatorLine; +extern CMenuForwarder * const GenericMenuBack; + +#endif diff --git a/src/gui/widget/messagebox.cpp b/src/gui/widget/messagebox.cpp new file mode 100644 index 000000000..d08d8de7b --- /dev/null +++ b/src/gui/widget/messagebox.cpp @@ -0,0 +1,301 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#define ROUND_RADIUS 9 + +CMessageBox::CMessageBox(const neutrino_locale_t Caption, const char * const Text, const int Width, const char * const Icon, const CMessageBox::result_ Default, const uint32_t ShowButtons) : CHintBoxExt(Caption, Text, Width, Icon) +{ + returnDefaultOnTimeout = false; + + m_height += (m_fheight << 1); + + result = Default; + + showbuttons = ShowButtons; + + int MaxButtonTextWidth = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(g_Locale->getText(LOCALE_MESSAGEBOX_CANCEL), true); // UTF-8 + int ButtonWidth = 20 + 33 + MaxButtonTextWidth + 5; + int num = 0; + if (showbuttons & mbYes) + num++; + if (showbuttons & mbNo) + num++; + if (showbuttons & (mbCancel | mbBack)) + num++; + int new_width = 15 + num*ButtonWidth; + if(new_width > m_width) + m_width = new_width; +} + +CMessageBox::CMessageBox(const neutrino_locale_t Caption, ContentLines& Lines, const int Width, const char * const Icon, const CMessageBox::result_ Default, const uint32_t ShowButtons) : CHintBoxExt(Caption, Lines, Width, Icon) +{ + returnDefaultOnTimeout = false; + + m_height += (m_fheight << 1); + + result = Default; + + showbuttons = ShowButtons; + int MaxButtonTextWidth = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(g_Locale->getText(LOCALE_MESSAGEBOX_CANCEL), true); // UTF-8 + int ButtonWidth = 20 + 33 + MaxButtonTextWidth + 5; + int num = 0; + if (showbuttons & mbYes) + num++; + if (showbuttons & mbNo) + num++; + if (showbuttons & (mbCancel | mbBack)) + num++; + int new_width = 15 + num*ButtonWidth; + if(new_width > m_width) + m_width = new_width; +} + +void CMessageBox::returnDefaultValueOnTimeout(bool returnDefault) +{ + returnDefaultOnTimeout = returnDefault; +} + +void CMessageBox::paintButtons() +{ + uint8_t color; + fb_pixel_t bgcolor; + + //m_window->paintBoxRel(0, m_height - (m_fheight << 1), m_width, (m_fheight << 1), (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0); + m_window->paintBoxRel(0, m_height - (m_fheight << 1), m_width, (m_fheight << 1), (CFBWindow::color_t)COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + //irgendwann alle vergleichen - aber cancel ist sicher der längste + int MaxButtonTextWidth = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(g_Locale->getText(LOCALE_MESSAGEBOX_CANCEL), true); // UTF-8 + + int ButtonWidth = 20 + 33 + MaxButtonTextWidth; + +// int ButtonSpacing = 40; +// int startpos = (m_width - ((ButtonWidth*3)+(ButtonSpacing*2))) / 2; + + int ButtonSpacing = (m_width - 20 - (ButtonWidth * 3)) / 2; + if(ButtonSpacing <= 5) ButtonSpacing = 5; + + int xpos = 10; + + if (showbuttons & mbYes) + { + if (result == mbrYes) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + //m_window->paintBoxRel(xpos, m_height - m_fheight - 20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor); + m_window->paintBoxRel(xpos, m_height - m_fheight - 20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor, ROUND_RADIUS, 3);//round + m_window->paintIcon(NEUTRINO_ICON_BUTTON_RED, xpos + 14, m_height - m_fheight - 15); + m_window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], xpos + 43, m_height-m_fheight+4, ButtonWidth- 53, g_Locale->getText(LOCALE_MESSAGEBOX_YES), (CFBWindow::color_t)color, 0, true); // UTF-8 + xpos += ButtonWidth + ButtonSpacing; + } + + + if (showbuttons & mbNo) + { + if (result == mbrNo) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + + //m_window->paintBoxRel(xpos, m_height-m_fheight-20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor); + m_window->paintBoxRel(xpos, m_height-m_fheight-20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor, ROUND_RADIUS, 3);//round + m_window->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, xpos+14, m_height-m_fheight-15); + m_window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], xpos + 43, m_height-m_fheight+4, ButtonWidth- 53, g_Locale->getText(LOCALE_MESSAGEBOX_NO), (CFBWindow::color_t)color, 0, true); // UTF-8 + xpos += ButtonWidth + ButtonSpacing; + } + + + if (showbuttons & (mbCancel | mbBack)) + { + if (result >= mbrCancel) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + + //m_window->paintBoxRel(xpos, m_height-m_fheight-20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor); + m_window->paintBoxRel(xpos, m_height-m_fheight-20, ButtonWidth, m_fheight, (CFBWindow::color_t)bgcolor, ROUND_RADIUS, 3);//round + m_window->paintIcon(NEUTRINO_ICON_BUTTON_HOME, xpos+10, m_height-m_fheight-19); + m_window->RenderString(g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], xpos + 43, m_height-m_fheight+4, ButtonWidth- 53, g_Locale->getText((showbuttons & mbCancel) ? LOCALE_MESSAGEBOX_CANCEL : LOCALE_MESSAGEBOX_BACK), (CFBWindow::color_t)color, 0, true); // UTF-8 + } +} + +int CMessageBox::exec(int timeout) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + CHintBoxExt::paint(0); + + if (m_window == NULL) + { + return res; /* out of memory */ + } + + paintButtons(); + + if ( timeout == -1 ) + timeout = g_settings.timing[SNeutrinoSettings::TIMING_EPG]; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd( timeout ); + + bool loop=true; + while (loop) + { + + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + if (msg == CRCInput::RC_timeout && returnDefaultOnTimeout) + { + // return default + loop = false; + } + else if (((msg == CRCInput::RC_timeout) || + (msg == (neutrino_msg_t)g_settings.key_channelList_cancel)) && + (showbuttons & (mbCancel | mbBack))) + { + result = (showbuttons & mbCancel) ? mbrCancel : mbrBack; + loop = false; + } + else if ((msg == CRCInput::RC_green) && (showbuttons & mbNo)) + { + result = mbrNo; + loop = false; + } + else if ((msg == CRCInput::RC_red) && (showbuttons & mbYes)) + { + result = mbrYes; + loop = false; + } + else if(msg==CRCInput::RC_right) + { + bool ok = false; + while (!ok) + { + result = (CMessageBox::result_)((result + 1) & 3); + ok = showbuttons & (1 << result); + } + + paintButtons(); + } + else if (has_scrollbar() && ((msg == CRCInput::RC_up) || (msg == CRCInput::RC_down))) + { + if (msg == CRCInput::RC_up) + scroll_up(); + else + scroll_down(); + } + else if(msg==CRCInput::RC_left) + { + bool ok = false; + while (!ok) + { + result = (CMessageBox::result_)((result - 1) & 3); + ok = showbuttons & (1 << result); + } + + paintButtons(); + + } + else if(msg == CRCInput::RC_ok) + { + loop = false; + } + else if((msg == CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) + { + } + else if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + { + res = menu_return::RETURN_EXIT_ALL; + loop = false; + } + + } + + hide(); + + return res; +} + +int ShowMsgUTF(const neutrino_locale_t Caption, const char * const Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon, const int Width, const int timeout, bool returnDefaultOnTimeout) +{ + CMessageBox* messageBox = new CMessageBox(Caption, Text, Width, Icon, Default, ShowButtons); + messageBox->returnDefaultValueOnTimeout(returnDefaultOnTimeout); + messageBox->exec(timeout); + int res = messageBox->result; + delete messageBox; + + return res; +} + +int ShowLocalizedMessage(const neutrino_locale_t Caption, const neutrino_locale_t Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon, const int Width, const int timeout, bool returnDefaultOnTimeout) +{ + return ShowMsgUTF(Caption, g_Locale->getText(Text), Default, ShowButtons, Icon, Width, timeout,returnDefaultOnTimeout); +} + +int ShowMsgUTF(const neutrino_locale_t Caption, const std::string & Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon, const int Width, const int timeout, bool returnDefaultOnTimeout) +{ + return ShowMsgUTF(Caption, Text.c_str(), Default, ShowButtons, Icon, Width, timeout,returnDefaultOnTimeout); +} + +void DisplayErrorMessage(const char * const ErrorMsg) +{ + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, ErrorMsg, CMessageBox::mbrCancel, CMessageBox::mbCancel, "error.raw"); +} diff --git a/src/gui/widget/messagebox.h b/src/gui/widget/messagebox.h new file mode 100644 index 000000000..a5bf8390d --- /dev/null +++ b/src/gui/widget/messagebox.h @@ -0,0 +1,84 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __messagebox__ +#define __messagebox__ + +#include + +#include + + +class CMessageBox : public CHintBoxExt +{ + private: + + int showbuttons; + bool returnDefaultOnTimeout; + + void paintButtons(); + + public: + enum result_ + { + mbrYes = 0, + mbrNo = 1, + mbrCancel = 2, + mbrBack = 3 + } result; + + enum buttons_ + { + mbYes= 0x01, + mbNo = 0x02, + mbCancel = 0x04, + mbAll = 0x07, + mbBack = 0x08 + } buttons; + + // Text & Caption are always UTF-8 encoded + CMessageBox(const neutrino_locale_t Caption, const char * const Text, const int Width = 500, const char * const Icon = NULL, const CMessageBox::result_ Default = mbrYes, const uint32_t ShowButtons = mbAll); + + CMessageBox(const neutrino_locale_t Caption, ContentLines& Lines, const int Width = 500, const char * const Icon = NULL, const CMessageBox::result_ Default = mbrYes, const uint32_t ShowButtons = mbAll); + + int exec(int timeout = -1); + void returnDefaultValueOnTimeout(bool returnDefault); +}; + +// Text is always UTF-8 encoded +int ShowLocalizedMessage(const neutrino_locale_t Caption, const neutrino_locale_t Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon = NULL, const int Width = 450, const int timeout = -1, bool returnDefaultOnTimeout = false); +int ShowMsgUTF(const neutrino_locale_t Caption, const char * const Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon = NULL, const int Width = 450, const int timeout = -1, bool returnDefaultOnTimeout = false); // UTF-8 +int ShowMsgUTF(const neutrino_locale_t Caption, const std::string & Text, const CMessageBox::result_ Default, const uint32_t ShowButtons, const char * const Icon = NULL, const int Width = 450, const int timeout = -1, bool returnDefaultOnTimeout = false); // UTF-8 + +void DisplayErrorMessage(const char * const ErrorMsg); // UTF-8 + +#endif diff --git a/src/gui/widget/mountchooser.cpp b/src/gui/widget/mountchooser.cpp new file mode 100644 index 000000000..d71a7988d --- /dev/null +++ b/src/gui/widget/mountchooser.cpp @@ -0,0 +1,98 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include + +CMountChooser::CMountChooser(const neutrino_locale_t Name, const std::string & Icon, int * chosenIndex, char * chosenLocalDir, const char * const selectedLocalDir, const int mwidth, const int mheight) + : CMenuWidget(Name, Icon,mwidth,mheight), index(chosenIndex), localDir(chosenLocalDir) +{ + char indexStr[2]; + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) + { + if (g_settings.network_nfs_local_dir[i] != NULL && + strcmp(g_settings.network_nfs_local_dir[i],"") != 0 && + (strstr(g_settings.network_nfs_mount_options1[i],"rw") != NULL || + strstr(g_settings.network_nfs_mount_options2[i],"rw") != NULL)) + { + std::string s(g_settings.network_nfs_local_dir[i]); + s += " ("; + s += g_settings.network_nfs_ip[i]; + s += ":"; + s += g_settings.network_nfs_dir[i]; + s +=")"; + snprintf(indexStr,2,"%d",i); + addItem(new CMenuForwarderNonLocalized(s.c_str(),true,NULL,this,(std::string("MID:") + std::string(indexStr)).c_str()), + (strcmp(selectedLocalDir,g_settings.network_nfs_local_dir[i]) == 0)); + } + } +} + + +int CMountChooser::exec(CMenuTarget* parent, const std::string & actionKey) +{ + + const char * key = actionKey.c_str(); + if (strncmp(key, "MID:",4) == 0) + { + int mount_id = -1; + sscanf(&key[4],"%d",&mount_id); + if ((mount_id > -1) && (mount_id < NETWORK_NFS_NR_OF_ENTRIES)) + { + if (index) + *index = mount_id; + if (localDir) + strcpy(localDir,g_settings.network_nfs_local_dir[mount_id]); + } + hide(); + return menu_return::RETURN_EXIT; + } else + { + return CMenuWidget::exec(parent, actionKey); + } +} + +void CMountChooser::setSelectedItem(int selection) +{ + selected = selection; +} + + + diff --git a/src/gui/widget/mountchooser.h b/src/gui/widget/mountchooser.h new file mode 100644 index 000000000..da7a030d5 --- /dev/null +++ b/src/gui/widget/mountchooser.h @@ -0,0 +1,67 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __mountchooser__ +#define __mountchooser__ + +#include +#include +#include + +#include + +/* +class CMountChooserTarget : CMenuTarget +{ +public: + + int CMountChooser::exec(CMenuTarget* parent, const std::string & actionKey); +}; +*/ + +class CMountChooser : public CMenuWidget +{ + private: + int * index; + char * localDir; + + public: + + CMountChooser(const neutrino_locale_t Name, const std::string & Icon = "", int * chosenIndex = NULL, char * chosenLocalDir = NULL, const char * const selectedLocalDir = "", const int mwidth = 400, const int mheight = 576); + int exec(CMenuTarget* parent, const std::string & actionKey); + void setSelectedItem(int selection); + +}; + + +#endif + diff --git a/src/gui/widget/msgbox.cpp b/src/gui/widget/msgbox.cpp new file mode 100644 index 000000000..df93882dd --- /dev/null +++ b/src/gui/widget/msgbox.cpp @@ -0,0 +1,860 @@ +/* + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: msgbox.cpp: . + + Description: Implementation of the CMsgBox class + This class provides a message box using CTextBox. + + Date: Nov 2005 + + Author: Gnther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Gnther initial implementation +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "msgbox.h" + +#include +#include + +#define WINDOW_FRAME_BORDER_WIDTH 4 +#define ADD_FOOT_HEIGHT 20 +#define TEXT_BORDER_WIDTH 8 +#define TITLE_ICON_WIDTH (40 - TEXT_BORDER_WIDTH) + +#define MAX_WINDOW_WIDTH (g_settings.screen_EndX - g_settings.screen_StartX ) +#define MAX_WINDOW_HEIGHT (g_settings.screen_EndY - g_settings.screen_StartY - 40) + +#define MIN_WINDOW_WIDTH (MAX_WINDOW_WIDTH>>1) +#define MIN_WINDOW_HEIGHT 40 + +#define DEFAULT_TITLE_FONT g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE] +#define DEFAULT_FOOT_FONT g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL] + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// Function Name: CMsgBox +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +CMsgBox::CMsgBox( const char * text, + Font* fontText, + const int _mode, + const CBox* position, + const char * title, + Font* fontTitle, + const char * icon, + int return_button , + const result_ default_result) +{ + //TRACE("->CMsgBox::CMsgBox\r\n"); + initVar(); + + if(title != NULL) m_cTitle = title; + if(fontTitle != NULL) m_pcFontTitle = fontTitle; + if(icon != NULL) m_cIcon = icon; + if(position != NULL) m_cBoxFrame = *position; + m_nMode = _mode; + //TRACE(" CMsgBox::cText: %d ,m_cTitle %d,m_nMode %d\t\r\n",strlen(text),m_cTitle.size(),m_nMode); + + if(m_nMode & BORDER) + m_nWindowFrameBorderWidth = WINDOW_FRAME_BORDER_WIDTH; + else + m_nWindowFrameBorderWidth = 0; + + //TRACE(" Mode: "); + //if(_mode & BORDER) TRACE("BORDER "); + //if(_mode & TITLE) TRACE("TITLE "); + //if(_mode & FOOT) TRACE("FOOT "); + //if(_mode & CENTER) TRACE("CENTER"); + //TRACE("\r\n"); + + //TRACE_1(" m_nWindowFrameBorderWidth: \t%d\r\n",m_nWindowFrameBorderWidth); + + + /* Initialise the window frames first */ + initFramesRel(); + + m_pcTextBox = new CTextBox( text, + fontText, + _mode, + &m_cBoxFrameText); + + if(_mode & AUTO_WIDTH || _mode & AUTO_HIGH) + { + /* window might changed in size ...*/ + m_cBoxFrameText = m_pcTextBox->getWindowsPos(); + + m_cBoxFrame.iWidth = m_cBoxFrameText.iWidth + m_nWindowFrameBorderWidth; + m_cBoxFrame.iHeight = m_cBoxFrameText.iHeight + m_cBoxFrameFootRel.iHeight + m_cBoxFrameTitleRel.iHeight + m_nWindowFrameBorderWidth; + + initFramesRel(); + } + + if(_mode & CENTER) + { + m_cBoxFrame.iX = g_settings.screen_StartX + ((g_settings.screen_EndX - g_settings.screen_StartX - m_cBoxFrame.iWidth) >>1); + m_cBoxFrame.iY = g_settings.screen_StartY + ((g_settings.screen_EndY - g_settings.screen_StartY - m_cBoxFrame.iHeight) >>2); + } + + m_nResult = default_result; + m_nFootButtons = return_button; +} + +////////////////////////////////////////////////////////////////////// +// Function Name: CMsgBox +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +CMsgBox::CMsgBox(const char * text) +{ + initVar(); + + m_pcTextBox = new CTextBox( text); + /* Initialise the window frames first */ + initFramesRel(); +} + +////////////////////////////////////////////////////////////////////// +// Function Name: CMsgBox +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +CMsgBox::CMsgBox() +{ + m_pcTextBox = NULL; + + initVar(); + initFramesRel(); +} + +////////////////////////////////////////////////////////////////////// +// Function Name: ~CMsgBox +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +CMsgBox::~CMsgBox() +{ + if (m_pcWindow != NULL) + { + delete m_pcWindow; + m_pcWindow = NULL; + } + + if (m_pcTextBox != NULL) + { + delete m_pcTextBox; + m_pcTextBox = NULL; + } + +} + +////////////////////////////////////////////////////////////////////// +// Function Name: InitVar +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::initVar(void) +{ + //TRACE("->CMsgBox::InitVar\r\n"); + m_cTitle = ""; + m_cIcon = ""; + m_nMode = SCROLL | TITLE | BORDER ; + + // set the title varianles + m_pcFontTitle = DEFAULT_TITLE_FONT; + m_nFontTitleHeight = m_pcFontTitle->getHeight(); + + // set the foot variables + m_pcFontFoot = DEFAULT_FOOT_FONT; + m_nFontFootHeight = m_pcFontFoot->getHeight(); + m_nFootButtons = 0; + + // set the main frame border width + if(m_nMode & BORDER) + m_nWindowFrameBorderWidth = WINDOW_FRAME_BORDER_WIDTH; + else + m_nWindowFrameBorderWidth = 0; + + // set the main frame to default + m_cBoxFrame.iX = g_settings.screen_StartX + ((g_settings.screen_EndX - g_settings.screen_StartX - MIN_WINDOW_WIDTH) >>1); + m_cBoxFrame.iWidth = MIN_WINDOW_WIDTH; + m_cBoxFrame.iY = g_settings.screen_StartY + ((g_settings.screen_EndY - g_settings.screen_StartY - MIN_WINDOW_HEIGHT) >>2); + m_cBoxFrame.iHeight = MIN_WINDOW_HEIGHT; + + m_pcWindow = NULL; + + //TRACE_1(" m_nWindowFrameBorderWidth: \t%d\r\n",m_nWindowFrameBorderWidth); +} + +////////////////////////////////////////////////////////////////////// +// Function Name: InitFramesRel +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::initFramesRel(void) +{ + //TRACE("->CMsgBox::InitFramesRel\r\n"); + // init the title frame + if(m_nMode & TITLE) + { + m_cBoxFrameTitleRel.iX = 0; + m_cBoxFrameTitleRel.iY = 0; + m_cBoxFrameTitleRel.iWidth = m_cBoxFrame.iWidth - m_nWindowFrameBorderWidth; + m_cBoxFrameTitleRel.iHeight = m_nFontTitleHeight + 2; + } + else + { + m_cBoxFrameTitleRel.iX = 0; + m_cBoxFrameTitleRel.iY = 0; + m_cBoxFrameTitleRel.iHeight = 0; + m_cBoxFrameTitleRel.iWidth = 0; + } + + // init the foot frame + if(m_nMode & FOOT) + { + m_cBoxFrameFootRel.iX = 0; + m_cBoxFrameFootRel.iY = m_cBoxFrame.iHeight - m_nFontFootHeight - m_nWindowFrameBorderWidth - ADD_FOOT_HEIGHT; + m_cBoxFrameFootRel.iWidth = m_cBoxFrame.iWidth - m_nWindowFrameBorderWidth; + m_cBoxFrameFootRel.iHeight = m_nFontFootHeight + ADD_FOOT_HEIGHT; + } + else + { + m_cBoxFrameFootRel.iX = 0; + m_cBoxFrameFootRel.iY = 0; + m_cBoxFrameFootRel.iHeight = 0; + m_cBoxFrameFootRel.iWidth = 0; + } + + // init the text frame + m_cBoxFrameText.iY = m_cBoxFrame.iY + m_cBoxFrameTitleRel.iY + m_cBoxFrameTitleRel.iHeight; + m_cBoxFrameText.iX = m_cBoxFrame.iX + m_cBoxFrameTitleRel.iX; + m_cBoxFrameText.iHeight = m_cBoxFrame.iHeight - m_cBoxFrameTitleRel.iHeight - m_cBoxFrameFootRel.iHeight - m_nWindowFrameBorderWidth; + m_cBoxFrameText.iWidth = m_cBoxFrame.iWidth - m_nWindowFrameBorderWidth; +#if 0 + TRACE_1("Frames\r\n\tScren:\t%3d,%3d,%3d,%3d\r\n\tMain:\t%3d,%3d,%3d,%3d\r\n\tText:\t%3d,%3d,%3d,%3d \r\n\tTitle:\t%3d,%3d,%3d,%3d \r\n\tFoot:\t%3d,%3d,%3d,%3d\r\n\r\n", + g_settings.screen_StartX, + g_settings.screen_StartY, + g_settings.screen_EndX, + g_settings.screen_EndY, + m_cBoxFrame.iX, + m_cBoxFrame.iY, + m_cBoxFrame.iWidth, + m_cBoxFrame.iHeight, + m_cBoxFrameText.iX, + m_cBoxFrameText.iY, + m_cBoxFrameText.iWidth, + m_cBoxFrameText.iHeight, + m_cBoxFrameTitleRel.iX, + m_cBoxFrameTitleRel.iY, + m_cBoxFrameTitleRel.iWidth, + m_cBoxFrameTitleRel.iHeight, + m_cBoxFrameFootRel.iX, + m_cBoxFrameFootRel.iY, + m_cBoxFrameFootRel.iWidth, + m_cBoxFrameFootRel.iHeight + ); +#endif +} + +////////////////////////////////////////////////////////////////////// +// Function Name: RefreshFoot +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::refreshFoot(void) +{ + int color,bgcolor; + if( !(m_nMode & FOOT)) return; + + // draw the background first + m_pcWindow->paintBoxRel( m_cBoxFrameFootRel.iX+m_cBoxFrame.iX, + m_cBoxFrameFootRel.iY+m_cBoxFrame.iY, + m_cBoxFrameFootRel.iWidth, + m_cBoxFrameFootRel.iHeight, + (CFBWindow::color_t)COL_MENUHEAD_PLUS_0); + + //const char* text; + + int MaxButtonTextWidth = m_pcFontFoot->getRenderWidth(g_Locale->getText(LOCALE_MESSAGEBOX_CANCEL), true); // UTF-8 + int ButtonWidth = 20 + 33 + MaxButtonTextWidth; + int ButtonSpacing = (m_cBoxFrameFootRel.iWidth - 20- (ButtonWidth*3) ) / 2; + int xpos = m_cBoxFrameFootRel.iX; + + // draw Button mbYes + if (m_nFootButtons & mbYes) + { + if (m_nResult == mbrYes) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + m_pcWindow->paintBoxRel(xpos+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth, m_nFontFootHeight + 4, (CFBWindow::color_t)bgcolor); + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_RED, xpos + 14+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY); + /*m_pcWindow->RenderString(*/ + m_pcFontFoot->RenderString(xpos + 43+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + m_nFontFootHeight + 4 + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth - 53, g_Locale->getText(LOCALE_MESSAGEBOX_YES), (CFBWindow::color_t)color, 0, true); // UTF-8 + } + + xpos += ButtonWidth + ButtonSpacing; + + // draw Button mbNo + if (m_nFootButtons & mbNo) + { + if (m_nResult == mbrNo) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + + m_pcWindow->paintBoxRel(xpos+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth, m_nFontFootHeight + 4, (CFBWindow::color_t)bgcolor); + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_GREEN, xpos + 14+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY); + /*m_pcWindow->RenderString(*/ + m_pcFontFoot->RenderString(xpos + 43+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + m_nFontFootHeight + 4 + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth- 53, g_Locale->getText(LOCALE_MESSAGEBOX_NO), (CFBWindow::color_t)color, 0, true); // UTF-8 + } + + xpos += ButtonWidth + ButtonSpacing; + + // draw Button mbCancel + if (m_nFootButtons & (mbCancel | mbBack)) + { + if (m_nResult >= mbrCancel) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_INFOBAR_SHADOW; + bgcolor = COL_INFOBAR_SHADOW_PLUS_0; + } + + m_pcWindow->paintBoxRel(xpos+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth, m_nFontFootHeight + 4, (CFBWindow::color_t)bgcolor); + m_pcWindow->paintIcon(NEUTRINO_ICON_BUTTON_HOME, xpos+10+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY); + /*m_pcWindow->RenderString(*/ + m_pcFontFoot->RenderString(xpos + 43+m_cBoxFrame.iX, m_cBoxFrameFootRel.iY + m_nFontFootHeight + 2 + (ADD_FOOT_HEIGHT>>1)+m_cBoxFrame.iY, ButtonWidth- 53, g_Locale->getText((m_nFootButtons & mbCancel) ? LOCALE_MESSAGEBOX_CANCEL : LOCALE_MESSAGEBOX_BACK), (CFBWindow::color_t)color, 0, true); // UTF-8 + } +} + +////////////////////////////////////////////////////////////////////// +// Function Name: RefreshTitle +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::refreshTitle(void) +{ + // first check if title is configured + if( !(m_nMode & TITLE)) return; + + // draw the background + m_pcWindow->paintBoxRel( m_cBoxFrameTitleRel.iX+m_cBoxFrame.iX, + m_cBoxFrameTitleRel.iY+m_cBoxFrame.iY, + m_cBoxFrameTitleRel.iWidth, + m_cBoxFrameTitleRel.iHeight, + (CFBWindow::color_t)COL_MENUHEAD_PLUS_0); + + if (!m_cIcon.empty()) + { + // draw icon and title text + m_pcWindow->paintIcon(m_cIcon.c_str(), m_cBoxFrameTitleRel.iX + 8+m_cBoxFrame.iX, m_cBoxFrameTitleRel.iY + 5+m_cBoxFrame.iY); + /*m_pcWindow->RenderString(*/ + m_pcFontTitle->RenderString( + m_cBoxFrameTitleRel.iX + TITLE_ICON_WIDTH + TEXT_BORDER_WIDTH+m_cBoxFrame.iX, + m_cBoxFrameTitleRel.iHeight+3+m_cBoxFrame.iY, + m_cBoxFrameTitleRel.iWidth - TITLE_ICON_WIDTH + TEXT_BORDER_WIDTH, + m_cTitle.c_str(), + (CFBWindow::color_t)COL_MENUHEAD, + 0, + true); // UTF-8 + } + else + { + // no icon available, just draw the title text + /*m_pcWindow->RenderString(*/ + m_pcFontTitle->RenderString( + m_cBoxFrameTitleRel.iX + TEXT_BORDER_WIDTH+m_cBoxFrame.iX, + m_cBoxFrameTitleRel.iHeight+3+m_cBoxFrame.iY, + m_cBoxFrameTitleRel.iWidth - TEXT_BORDER_WIDTH, + m_cTitle.c_str(), + (CFBWindow::color_t)COL_MENUHEAD, + 0, + true); // UTF-8 + } +} + +////////////////////////////////////////////////////////////////////// +// Function Name: RefreshBorder +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::refreshBorder(void) +{ + if( !(m_nMode & BORDER && m_nWindowFrameBorderWidth > 0)) return; + + //draw bottom shadow + m_pcWindow->paintBoxRel( m_nWindowFrameBorderWidth+m_cBoxFrame.iX, + m_cBoxFrame.iHeight - m_nWindowFrameBorderWidth+m_cBoxFrame.iY, + m_cBoxFrame.iWidth - m_nWindowFrameBorderWidth, + m_nWindowFrameBorderWidth, + COL_INFOBAR_SHADOW_PLUS_0); + + //draw right shadow + m_pcWindow->paintBoxRel( m_cBoxFrame.iWidth - m_nWindowFrameBorderWidth+m_cBoxFrame.iX, + m_nWindowFrameBorderWidth+m_cBoxFrame.iY, + m_nWindowFrameBorderWidth, + m_cBoxFrame.iHeight - m_nWindowFrameBorderWidth, + COL_INFOBAR_SHADOW_PLUS_0); +} + +////////////////////////////////////////////////////////////////////// +// global Functions +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////// +// Function Name: Hide +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +bool CMsgBox::hide(void) +{ + //TRACE("->CMsgBox::Hide\r\n"); + + if (m_pcWindow == NULL) + { + TRACE(" return -> window does not exists\r\n"); + return (false); + } + if(m_pcTextBox != NULL) + { + m_pcTextBox->hide(); + } + + // delete window + // delete m_pcWindow; + m_pcWindow->paintBackgroundBoxRel(m_cBoxFrame.iX, m_cBoxFrame.iY, m_cBoxFrame.iWidth, m_cBoxFrame.iHeight); + m_pcWindow = NULL; + return (true); +} + +////////////////////////////////////////////////////////////////////// +// Function Name: ScrollPageDown +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::scrollPageDown(const int pages) +{ + // send scroll up event to text box if there is one + if(m_pcTextBox != NULL) + { + m_pcTextBox->scrollPageDown(pages); + } + +}; + +////////////////////////////////////////////////////////////////////// +// Function Name: ScrollPageUp +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::scrollPageUp(const int pages) +{ + // send scroll up event to text box if there is one + if(m_pcTextBox != NULL) + { + m_pcTextBox->scrollPageUp(pages); + } +}; + +////////////////////////////////////////////////////////////////////// +// Function Name: Paint +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +bool CMsgBox::paint(void) +{ + /* + * do not paint stuff twice: + * => thread safety needed by movieplayer.cpp: + * one thread calls our paint method, the other one our hide method + * => no memory leaks + */ + //TRACE("->CMsgBox::Paint\r\n"); + + if (m_pcWindow != NULL) + { + TRACE(" return -> window already exists\r\n"); + return (false); + } + + // create new window + //m_pcWindow = new CFBWindow( m_cBoxFrame.iX, m_cBoxFrame.iY, m_cBoxFrame.iWidth, m_cBoxFrame.iHeight); + m_pcWindow = CFrameBuffer::getInstance(); + if(m_pcTextBox != NULL) + { + m_pcTextBox->paint(); + } + refresh(); + return (true); +} + +////////////////////////////////////////////////////////////////////// +// Function Name: Refresh +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +void CMsgBox::refresh(void) +{ + //TRACE("->CMsgBox::Refresh\r\n"); + + if (m_pcWindow == NULL) + { + TRACE(" return -> no window\r\n"); + return; + } + + //re-draw message box window + refreshTitle(); + refreshFoot(); + refreshBorder(); + + // rep-draw textbox if there is one + if(m_pcTextBox != NULL) + { + //m_pcTextBox->refresh(); + } +} + +////////////////////////////////////////////////////////////////////// +// Function Name: Exec +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +int CMsgBox::exec( int timeout, int returnDefaultOnTimeout) +{ + //TRACE("->CMsgBox::exec\r\n"); +#ifdef VC + int res = 1; + +#else + neutrino_msg_t msg; + neutrino_msg_data_t data; + int return_button = m_nFootButtons; + int res = menu_return::RETURN_REPAINT; + + // show message box + paint(); + if (m_pcWindow == NULL) + { + return res; /* out of memory */ + } + + if ( timeout == -1 ) + timeout = g_settings.timing[SNeutrinoSettings::TIMING_EPG]; + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd( timeout ); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); + + if (msg == CRCInput::RC_timeout && returnDefaultOnTimeout) + { + // return default + loop = false; + } + else if ( ((msg == CRCInput::RC_timeout) || + (msg == (neutrino_msg_t)g_settings.key_channelList_cancel)) && + (return_button & (mbCancel | mbBack))) + { + m_nResult = (return_button & mbCancel) ? mbrCancel : mbrBack; + loop = false; + } + else if ((msg == CRCInput::RC_green) && (return_button & mbNo)) + { + m_nResult = mbrNo; + loop = false; + } + else if ((msg == CRCInput::RC_red) && (return_button & mbYes)) + { + m_nResult = mbrYes; + loop = false; + } + else if(msg==CRCInput::RC_right) + { + bool ok = false; + while (!ok) + { + m_nResult = (CMsgBox::result_)((m_nResult + 1) & 3); + ok = m_nFootButtons & (1 << m_nResult); + } + + refreshFoot(); + } + else if (msg == CRCInput::RC_up ) + { + scrollPageUp(1); + } + else if (msg == CRCInput::RC_down) + { + scrollPageDown(1); + } + else if(msg==CRCInput::RC_left) + { + bool ok = false; + while (!ok) + { + m_nResult = (CMsgBox::result_)((m_nResult - 1) & 3); + ok = return_button & (1 << m_nResult); + } + + refreshFoot(); + } + else if(msg == CRCInput::RC_ok) + { + loop = false; + } + else if ((msg ==CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) + { + } + else if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) + { + res = menu_return::RETURN_EXIT_ALL; + loop = false; + } + + } + + hide(); +#endif //VC + + return res; +} + +////////////////////////////////////////////////////////////////////// +// Function Name: SetText +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +bool CMsgBox::setText(const std::string* newText) +{ + bool result = false; + // update text in textbox if there is one + if(m_pcTextBox != NULL && newText != NULL) + { + result = m_pcTextBox->setText(newText); + if(m_nMode & AUTO_WIDTH || m_nMode & AUTO_HIGH) + { + /* window might changed in size ...*/ + m_cBoxFrameText = m_pcTextBox->getWindowsPos(); + + m_cBoxFrame.iWidth = m_cBoxFrameText.iWidth + m_nWindowFrameBorderWidth; + m_cBoxFrame.iHeight = m_cBoxFrameText.iHeight + m_cBoxFrameFootRel.iHeight + m_cBoxFrameTitleRel.iHeight + m_nWindowFrameBorderWidth; + + initFramesRel(); + + // since the frames size has changed, we have to recenter the window again */ + if(m_nMode & CENTER) + { + m_cBoxFrame.iX = g_settings.screen_StartX + ((g_settings.screen_EndX - g_settings.screen_StartX - m_cBoxFrame.iWidth) >>1); + m_cBoxFrame.iY = g_settings.screen_StartY + ((g_settings.screen_EndY - g_settings.screen_StartY - m_cBoxFrame.iHeight) >>1); + } + } + } + + return(result); +}; + + +////////////////////////////////////////////////////////////////////// +// Function Name: SetText +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +int CMsgBox::result(void) +{ + return m_nResult; +} + +////////////////////////////////////////////////////////////////////// +// Function Name: ShowMsg2UTF +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +int ShowMsg2UTF( const neutrino_locale_t Caption, + const char * const Text, + const CMsgBox::result_ Default, + const uint32_t ShowButtons, + const char * const Icon, + const int Width, + const int timeout, + bool returnDefaultOnTimeout) +{ + //TRACE("->CMsgBox::ShowTextUTF \r\n"); + + int result = ShowMsg2UTF( g_Locale->getText(Caption), + Text, + Default, + ShowButtons, + Icon, + Width, + timeout, + returnDefaultOnTimeout); + + return (result); + +} + +////////////////////////////////////////////////////////////////////// +// Function Name: ShowMsg2UTF +// Description: +// Parameters: +// Data IN/OUT: +// Return: +// Notes: +////////////////////////////////////////////////////////////////////// +int ShowMsg2UTF( const char * const Title, + const char * const Text, + const CMsgBox::result_ Default, + const uint32_t ShowButtons, + const char * const Icon, + const int Width, + const int timeout, + bool returnDefaultOnTimeout) +{ + int mode = CMsgBox::SCROLL | + CMsgBox::TITLE | + CMsgBox::FOOT | + CMsgBox::BORDER;// | + //CMsgBox::NO_AUTO_LINEBREAK | + //CMsgBox::CENTER | + //CMsgBox::AUTO_WIDTH | + //CMsgBox::AUTO_HIGH; + CBox position ( g_settings.screen_StartX+30, + g_settings.screen_StartY+30, + g_settings.screen_EndX - g_settings.screen_StartX-60, + g_settings.screen_EndY - g_settings.screen_StartY-60); + + //TRACE("\r\n->ShowTextUTF %s\r\n",Text); + CMsgBox* msgBox = new CMsgBox( Text, + g_Font[SNeutrinoSettings::FONT_TYPE_MENU], + mode, + &position, + Title, + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE], + Icon, + ShowButtons, + Default); + + msgBox->exec( timeout, returnDefaultOnTimeout); + + int res = msgBox->result(); + + delete msgBox; + + return res; +} diff --git a/src/gui/widget/msgbox.h b/src/gui/widget/msgbox.h new file mode 100644 index 000000000..85905a4f0 --- /dev/null +++ b/src/gui/widget/msgbox.h @@ -0,0 +1,171 @@ +/* + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: msgbox.h . + + Description: interface of the CMsgBox class + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Günther initial implementation + +*/ + +#if !defined(MSGBOX_H) +#define MSGBOX_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "textbox.h" + +#include +#include + +class CMsgBox +{ + public: + /* enum definition */ + enum result_ + { + mbrYes = 0, + mbrNo = 1, + mbrCancel = 2, + mbrBack = 3 + }; + enum buttons_ + { + mbYes = 0x01, + mbNo = 0x02, + mbCancel = 0x04, + mbAll = 0x07, + mbBack = 0x08 + }; + enum mode_ + { + AUTO_WIDTH = 0x01, + AUTO_HIGH = 0x02, + SCROLL = 0x04, + TITLE = 0x08, + FOOT = 0x10, + BORDER = 0x20, + CENTER = 0x40, + NO_AUTO_LINEBREAK= 0x80 + }mode; + + private: + /* Functions */ + void initVar(void); + void initFramesRel(void); + void refreshFoot(void); + void refreshTitle(void); + void refreshText(void); + void refreshBorder(void); + + /* Variables */ + std::string m_cIcon; + std::string m_cTitle; + + CBox m_cBoxFrame; + CBox m_cBoxFrameText; + CBox m_cBoxFrameTitleRel; + CBox m_cBoxFrameFootRel; + + int m_nMode; + + int m_nWindowFrameBorderWidth; + + Font* m_pcFontTitle; + int m_nFontTitleHeight; + + Font* m_pcFontFoot; + int m_nFontFootHeight; + int m_nFootButtons; + + CTextBox* m_pcTextBox; + //CFBWindow* m_pcWindow; + CFrameBuffer * m_pcWindow; + + result_ m_nResult; + public: + /* Constructor */ + virtual ~CMsgBox(); + CMsgBox(); + CMsgBox(const char * text); + CMsgBox( const char * text, + Font* fontText, + const int mode, + const CBox* position, + const char * title, + Font* fontTitle, + const char * icon, + int return_button = mbCancel, + const result_ default_result = mbrCancel); + + /* Functions */ + bool paint(void); + bool hide(void); + int exec(int timeout,int returnDefaultOnTimeout = false); + void refresh(void); + void scrollPageDown(const int pages); + void scrollPageUp(const int pages); + int result(void); + + bool setText(const std::string* newText); +}; + +extern int ShowMsg2UTF( const neutrino_locale_t Caption, + const char * const Text, + const CMsgBox::result_ Default, + const uint32_t ShowButtons, + const char * const Icon = NULL, + const int Width = 450, + const int timeout = -1, + bool returnDefaultOnTimeout = false); // UTF-8 + +extern int ShowMsg2UTF( const char * const Title, + const char * const Text, + const CMsgBox::result_ Default, + const uint32_t ShowButtons, + const char * const Icon = NULL, + const int Width = 450, + const int timeout = -1, + bool returnDefaultOnTimeout = false); // UTF-8 + + +#endif diff --git a/src/gui/widget/progressstatus.h b/src/gui/widget/progressstatus.h new file mode 100644 index 000000000..1e2358ffb --- /dev/null +++ b/src/gui/widget/progressstatus.h @@ -0,0 +1,39 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA +*/ + + +#ifndef __progressstatus__ +#define __progressstatus__ + +#include + +class CProgress_StatusViewer +{ + public: + virtual void showGlobalStatus(const unsigned int prog) = 0; + virtual unsigned int getGlobalStatus(void) = 0; + virtual void showLocalStatus(const unsigned int prog) = 0; + virtual void showStatusMessageUTF(const std::string & text) = 0; +}; + +#endif diff --git a/src/gui/widget/progresswindow.cpp b/src/gui/widget/progresswindow.cpp new file mode 100644 index 000000000..24679f154 --- /dev/null +++ b/src/gui/widget/progresswindow.cpp @@ -0,0 +1,178 @@ +/* + $Id: progresswindow.cpp,v 1.16 2007/02/25 21:32:58 guenther Exp $ + + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "progresswindow.h" + +#include +#include + +#include +#include +#include + +#include + +#define ROUND_RADIUS 9 +CProgressWindow::CProgressWindow() +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + width = w_max (420, 0); + height = h_max(hheight+5*mheight, 20); + + global_progress = local_progress = 101; + statusText = ""; + + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width ) >> 1 ); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - height) >>1 ); + + caption = NONEXISTANT_LOCALE; +} + +void CProgressWindow::setTitle(const neutrino_locale_t title) +{ + caption = title; + +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(-1,NULL,-1,g_Locale->getText(caption)); // set global text in VFD +#endif // VFD_UPDATE +} + + +void CProgressWindow::showGlobalStatus(const unsigned int prog) +{ + if (global_progress == prog) + return; + + global_progress = prog; + + int pos = x + 10; + + if(global_progress != 0) + { + if (global_progress > 100) + global_progress = 100; + + pos += int( float(width-20)/100.0 * global_progress); + //vordergrund + frameBuffer->paintBox(x+10, globalstatusY,pos, globalstatusY+10, COL_MENUCONTENT_PLUS_7); + } + //hintergrund + frameBuffer->paintBox(pos, globalstatusY, x+width-10, globalstatusY+10, COL_MENUCONTENT_PLUS_2); + +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(-1,NULL,global_progress); +#endif // VFD_UPDATE +} + +void CProgressWindow::showLocalStatus(const unsigned int prog) +{ + if (local_progress == prog) + return; + + local_progress = prog; + + int pos = x + 10; + + if (local_progress != 0) + { + if (local_progress > 100) + local_progress = 100; + + pos += int( float(width-20)/100.0 * local_progress); + //vordergrund + frameBuffer->paintBox(x+10, localstatusY,pos, localstatusY+10, COL_MENUCONTENT_PLUS_7); + } + //hintergrund + frameBuffer->paintBox(pos, localstatusY, x+width-10, localstatusY+10, COL_MENUCONTENT_PLUS_2); + +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(local_progress); +#endif // VFD_UPDATE +} + +void CProgressWindow::showStatusMessageUTF(const std::string & text) +{ + statusText = text; + frameBuffer->paintBox(x, statusTextY-mheight, x+width, statusTextY, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, statusTextY, width-20, text, COL_MENUCONTENT, 0, true); // UTF-8 + +#ifdef VFD_UPDATE + CVFD::getInstance()->showProgressBar2(-1,text.c_str()); // set local text in VFD +#endif // VFD_UPDATE +} + + +unsigned int CProgressWindow::getGlobalStatus(void) +{ + return global_progress; +} + + +void CProgressWindow::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CProgressWindow::paint() +{ + int ypos=y; + frameBuffer->paintBoxRel(x, ypos, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + if (caption != NONEXISTANT_LOCALE) + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10, ypos+ hheight, width- 10, g_Locale->getText(caption), COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintBoxRel(x, ypos+ hheight, width, height- hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + ypos+= hheight + (mheight >>1); + statusTextY = ypos+mheight; + showStatusMessageUTF(statusText); + + ypos+= mheight; + localstatusY = ypos+ mheight-20; + showLocalStatus(0); + ypos+= mheight+10; + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+ 10, ypos+ mheight, width- 10, g_Locale->getText(LOCALE_FLASHUPDATE_GLOBALPROGRESS), COL_MENUCONTENT, 0, true); // UTF-8 + ypos+= mheight; + + globalstatusY = ypos+ mheight-20; + ypos+= mheight >>1; + showGlobalStatus(global_progress); +} + +int CProgressWindow::exec(CMenuTarget* parent, const std::string & actionKey) +{ + if(parent) + { + parent->hide(); + } + paint(); + + return menu_return::RETURN_REPAINT; +} diff --git a/src/gui/widget/progresswindow.h b/src/gui/widget/progresswindow.h new file mode 100644 index 000000000..667920087 --- /dev/null +++ b/src/gui/widget/progresswindow.h @@ -0,0 +1,77 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __progresswindow__ +#define __progresswindow__ + +#include +#include + +#include "progressstatus.h" +#include "menue.h" + +#include + +class CProgressWindow : public CMenuTarget, public CProgress_StatusViewer +{ + protected: + + CFrameBuffer *frameBuffer; + neutrino_locale_t caption; + + int x; + int y; + int width; + int height; + int hheight; // head font height + int mheight; // menu font height + unsigned int global_progress; + unsigned int local_progress; + int globalstatusX; + int globalstatusY; + int localstatusY; + int statusTextY; + std::string statusText; + + + //---------------------------- + + virtual void paint(); + + public: + + CProgressWindow(); + void setTitle(const neutrino_locale_t title); + virtual void hide(); + + virtual int exec( CMenuTarget* parent, const std::string & actionKey ); + + virtual void showGlobalStatus(const unsigned int prog); + virtual unsigned int getGlobalStatus(void); + virtual void showLocalStatus(const unsigned int prog); + virtual void showStatusMessageUTF(const std::string & text); // UTF-8 +}; + + +#endif diff --git a/src/gui/widget/rgbcsynccontroler.cpp b/src/gui/widget/rgbcsynccontroler.cpp new file mode 100644 index 000000000..f74a48d49 --- /dev/null +++ b/src/gui/widget/rgbcsynccontroler.cpp @@ -0,0 +1,189 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +extern cVideo * videoDecoder; + + +#define CSYNCFACTOR 0.31 + + +CRGBCSyncControler::CRGBCSyncControler(const neutrino_locale_t Name, unsigned char* Csync, CChangeObserver* Observer) +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + observer = Observer; + name = Name; + width = w_max(390, 0); + height = h_max(hheight+ mheight* 1+ +mheight/2, 0); + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height)>>1); + csync=Csync; +} + +void CRGBCSyncControler::setCSync() +{ +// printf("contrast: %d brightness: %d brightness standby: %d\n", contrast, brightness, brightnessstandby); + g_Controld->setRGBCsync(*csync); +} + +int CRGBCSyncControler::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + unsigned char csync_alt; + + if (parent) + { + parent->hide(); + } + csync_alt = *csync; + + setCSync(); + paint(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU]); + + switch ( msg ) + { + case CRCInput::RC_right: + if (*csync < 31) + { + (*csync)++; + paintSlider(x+10, y+hheight, *csync, CSYNCFACTOR, LOCALE_VIDEOMENU_CSYNC, true); + setCSync(); + } + break; + + case CRCInput::RC_left: + if (*csync > 0) + { + (*csync)--; + paintSlider(x+10, y+hheight, *csync, CSYNCFACTOR, LOCALE_VIDEOMENU_CSYNC, true); + setCSync(); + } + break; + + case CRCInput::RC_home: + if ( ( (*csync != csync_alt) ) && + (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + break; + + // sonst abbruch... + *csync = csync_alt; + setCSync(); + loop = false; + break; + + case CRCInput::RC_ok: + loop = false; + break; + + case CRCInput::RC_timeout: + loop = false; + break; + + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + hide(); + + if(observer) + observer->changeNotify(name, NULL); + + return res; +} + +void CRGBCSyncControler::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CRGBCSyncControler::paint() +{ + frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0); + + paintSlider(x+10, y+hheight, *csync, CSYNCFACTOR, LOCALE_VIDEOMENU_CSYNC, true); + +// frameBuffer->paintHLineRel(x+10, width-20, y+hheight+mheight*3+mheight/4, COL_MENUCONTENT_PLUS_3); +} + +void CRGBCSyncControler::paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected) +{ + int startx = 200; + char wert[5]; + + frameBuffer->paintBoxRel(x + startx, y, 120, mheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintIcon("volumebody.raw", x + startx, y+2+mheight/4); + frameBuffer->paintIcon(selected ? "volumeslider2blue.raw" : "volumeslider2.raw", (int)(x + (startx+3)+(spos / factor)), y+mheight/4); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, y+mheight, width, g_Locale->getText(text), COL_MENUCONTENT, 0, true); // UTF-8 + sprintf(wert, "%3d", spos); // UTF-8 encoded + frameBuffer->paintBoxRel(x + startx + 120 + 10, y, 50, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x + startx + 120 + 10, y+mheight, width, wert, COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/widget/rgbcsynccontroler.h b/src/gui/widget/rgbcsynccontroler.h new file mode 100644 index 000000000..ea3010482 --- /dev/null +++ b/src/gui/widget/rgbcsynccontroler.h @@ -0,0 +1,73 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __rgbcsynccontroler__ +#define __rgbcsynccontroler__ + +#include +#include + +#include "menue.h" + +#include + +class CRGBCSyncControler : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + unsigned char* csync; + + neutrino_locale_t name; + + CChangeObserver* observer; + + void paint(); + void setCSync(); + void paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected); + + public: + + CRGBCSyncControler(const neutrino_locale_t Name, unsigned char* Csync, CChangeObserver* Observer=NULL); // UTF-8 + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/gui/widget/stringinput.cpp b/src/gui/widget/stringinput.cpp new file mode 100644 index 000000000..947bd6da3 --- /dev/null +++ b/src/gui/widget/stringinput.cpp @@ -0,0 +1,841 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#define ROUND_RADIUS 9 + +CStringInput::CStringInput(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon) +{ + frameBuffer = CFrameBuffer::getInstance(); + name = Name; + head = NULL; + value = Value; + valueString = NULL; + size = Size; + + hint_1 = Hint_1; + hint_2 = Hint_2; + validchars = Valid_Chars; + iconfile = Icon ? Icon : ""; + + observ = Observ; + init(); +} + +CStringInput::CStringInput(const neutrino_locale_t Name, std::string* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon) +{ + frameBuffer = CFrameBuffer::getInstance(); + name = Name; + head = NULL; + value = new char[Size+1]; + value[Size] = '\0'; + strncpy(value,Value->c_str(),Size); + valueString = Value; + size = Size; + + hint_1 = Hint_1; + hint_2 = Hint_2; + validchars = Valid_Chars; + iconfile = Icon ? Icon : ""; + + observ = Observ; + init(); +} + +CStringInput::CStringInput(char * Head, char* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon) +{ + frameBuffer = CFrameBuffer::getInstance(); + head = strdup(Head); + value = Value; + valueString = NULL; + size = Size; + + hint_1 = Hint_1; + hint_2 = Hint_2; + validchars = Valid_Chars; + iconfile = Icon ? Icon : ""; + + observ = Observ; + init(); +} + +CStringInput::~CStringInput() +{ + if (valueString != NULL) + { + delete[] value; + } + if(head) { + free(head); + } +} + +void CStringInput::init() +{ + width = (size*20)+40; + + if (width<420) + width = 420; + + int neededWidth; + if(head) { + neededWidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(head, true); // UTF-8 + } + else + neededWidth = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(g_Locale->getText(name), true); // UTF-8 + + if (!(iconfile.empty())) + neededWidth += 28; + if (neededWidth+20> width) + width = neededWidth+20; + + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + iheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->getHeight(); + + height = hheight+ mheight+ 50; + if (hint_1 != NONEXISTANT_LOCALE) + { + height += iheight; + if (hint_2 != NONEXISTANT_LOCALE) + height += iheight; + } + + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width)>>1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height)>>1); + selected = 0; +} + +void CStringInput::NormalKeyPressed(const neutrino_msg_t key) +{ + if (CRCInput::isNumeric(key)) + { + value[selected] = validchars[CRCInput::getNumericValue(key)]; + + if (selected < (size - 1)) + { + selected++; + paintChar(selected - 1); + } + + paintChar(selected); + } +} + +void CStringInput::keyBackspacePressed(void) +{ + if (selected > 0) + { + selected--; + for (int i = selected; i < size - 1; i++) + { + value[i] = value[i + 1]; + paintChar(i); + } + value[size - 1] = ' '; + paintChar(size - 1); + } +} + + +void CStringInput::keyRedPressed() +{ + if (index(validchars, ' ') != NULL) + { + value[selected] = ' '; + + if (selected < (size - 1)) + { + selected++; + paintChar(selected - 1); + } + + paintChar(selected); + } +} + +void CStringInput::keyYellowPressed() +{ + selected=0; + for(int i=0 ; i < size ; i++) + { + value[i]=' '; + paintChar(i); + } +} + +void CStringInput::keyBluePressed() +{ + if (((value[selected] | 32) >= 'a') && ((value[selected] | 32) <= 'z')) + { + char newValue = value[selected] ^ 32; + if (index(validchars, newValue) != NULL) + { + value[selected] = newValue; + paintChar(selected); + } + } +} + +void CStringInput::keyUpPressed() +{ + int npos = 0; + for(int count=0;count<(int)strlen(validchars);count++) + if(value[selected]==validchars[count]) + npos = count; + npos++; + if(npos>=(int)strlen(validchars)) + npos = 0; + value[selected]=validchars[npos]; + paintChar(selected); +} + +void CStringInput::keyDownPressed() +{ + int npos = 0; + for(int count=0;count<(int)strlen(validchars);count++) + if(value[selected]==validchars[count]) + npos = count; + npos--; + if(npos<0) + npos = strlen(validchars)-1; + value[selected]=validchars[npos]; + paintChar(selected); +} + +void CStringInput::keyLeftPressed() +{ + int old = selected; + if(selected>0) { + selected--; + } else { + selected = size - 1; + } + paintChar(old); + paintChar(selected); +} + +void CStringInput::keyRightPressed() +{ + int old = selected; + if (selected < (size - 1)) { + selected++; + } else + selected = 0; + paintChar(old); + paintChar(selected); +} + +void CStringInput::keyMinusPressed() +{ + int item = selected; + while (item < (size -1)) + { + value[item] = value[item+1]; + paintChar(item); + item++; + } + value[item] = ' '; + paintChar(item); +} + +void CStringInput::keyPlusPressed() +{ + int item = size -1; + while (item > selected) + { + value[item] = value[item-1]; + paintChar(item); + item--; + } + value[item] = ' '; + paintChar(item); +} + +int CStringInput::exec( CMenuTarget* parent, const std::string & ) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + int res = menu_return::RETURN_REPAINT; + + char oldval[size+1], dispval[size+1]; + oldval[size] = 0; + dispval[size] = 0; + + if (parent) + parent->hide(); + + for(int count=strlen(value)-1;countshowMenuText(1, value, selected+1); + strncpy(dispval, value, size); + } + + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + if (msg==CRCInput::RC_left) + { + keyLeftPressed(); + } + else if (msg==CRCInput::RC_right) + { + keyRightPressed(); + } + else if (CRCInput::getUnicodeValue(msg) != -1) + { + NormalKeyPressed(msg); + } + else if (msg==CRCInput::RC_backspace) + { + keyBackspacePressed(); + } + else if (msg==CRCInput::RC_red) + { + keyRedPressed(); + } + else if (msg==CRCInput::RC_yellow) + { + keyYellowPressed(); + } + else if ( (msg==CRCInput::RC_green) && (index(validchars, '.') != NULL)) + { + value[selected] = '.'; + + if (selected < (size - 1)) + { + selected++; + paintChar(selected - 1); + } + + paintChar(selected); + } + else if (msg== CRCInput::RC_blue) + { + keyBluePressed(); + } + else if (msg==CRCInput::RC_up) + { + keyUpPressed(); + } + else if (msg==CRCInput::RC_down) + { + keyDownPressed(); + } else if (msg==CRCInput::RC_plus) + { + keyPlusPressed(); + } else if (msg==CRCInput::RC_minus) + { + keyMinusPressed(); + } + else if (msg==CRCInput::RC_ok) + { + loop=false; + } + else if ( (msg==CRCInput::RC_home) || (msg==CRCInput::RC_timeout) ) + { + if ( ( strcmp(value, oldval) != 0) && + (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + continue; + + strncpy(value, oldval, size); + loop=false; + } + else if ((msg ==CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) + { + } + else + { + int r = handleOthers( msg, data ); + if (r & (messages_return::cancel_all | messages_return::cancel_info)) + { + res = (r & messages_return::cancel_all) ? menu_return::RETURN_EXIT_ALL : menu_return::RETURN_EXIT; + loop = false; + } + else if ( r & messages_return::unhandled ) + { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + } + + hide(); + + for(int count=size-1;count>=0;count--) + { + if((value[count]==' ') || (value[count]==0)) + { + value[count]=0; + } + else + break; + } + value[size]=0; + + if (valueString != NULL) + { + *valueString = value; + } + + if ( (observ) && (msg==CRCInput::RC_ok) ) + { + observ->changeNotify(name, value); + } + + return res; +} + +int CStringInput::handleOthers(const neutrino_msg_t msg, const neutrino_msg_data_t data) +{ + return messages_return::unhandled; +} + +void CStringInput::hide() +{ + frameBuffer->paintBackgroundBoxRel(x, y, width, height); +} + +const char * CStringInput::getHint1(void) +{ + return g_Locale->getText(hint_1); +} + +void CStringInput::paint() +{ + int iconoffset; + + //frameBuffer->paintBoxRel(x, y, width, hheight, COL_MENUHEAD_PLUS_0); + //frameBuffer->paintBoxRel(x, y + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintBoxRel(x, y, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); //round + frameBuffer->paintBoxRel(x, y + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + if (!(iconfile.empty())) + { + frameBuffer->paintIcon(iconfile, x + 8, y + 5); + iconoffset = 28; + } + else + iconoffset = 0; + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+ 10+ iconoffset, y+ hheight, width- 10- iconoffset, head ? head : g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + + if (hint_1 != NONEXISTANT_LOCALE) + { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ 20, y+ hheight+ mheight+ iheight+ 40, width- 20, getHint1(), COL_MENUCONTENT, 0, true); // UTF-8 + if (hint_2 != NONEXISTANT_LOCALE) + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ 20, y+ hheight+ mheight+ iheight* 2+ 40, width- 20, g_Locale->getText(hint_2), COL_MENUCONTENT, 0, true); // UTF-8 + } + + for (int count=0;countpaintBoxRel(xpos, ypos, xs, ys, COL_MENUCONTENT_PLUS_4); + frameBuffer->paintBoxRel(xpos+ 1, ypos+ 1, xs- 2, ys- 2, bgcolor); + + int xfpos = xpos + ((xs- g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(ch))>>1); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(xfpos,ypos+ys, width, ch, color); +} + +void CStringInput::paintChar(int pos) +{ + if(pos<(int)strlen(value)) + paintChar(pos, value[pos]); +} + +CStringInputSMS::CStringInputSMS(const neutrino_locale_t Name, std::string* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon) + : CStringInput(Name, Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon) +{ + initSMS(Valid_Chars); +} + +CStringInputSMS::CStringInputSMS(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon) + : CStringInput(Name, Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon) + { + initSMS(Valid_Chars); +} + +void CStringInputSMS::initSMS(const char * const Valid_Chars) +{ + last_digit = -1; // no key pressed yet + const char CharList[10][11] = { "0 -_/()<>=", // 9 characters + "1+.,:!?\\", + "abc2ä", + "def3", + "ghi4", + "jkl5", + "mno6ö", + "pqrs7ß", + "tuv8ü", + "wxyz9" }; + + for (int i = 0; i < 10; i++) + { + int j = 0; + for (int k = 0; k < (int) strlen(CharList[i]); k++) + if (strchr(Valid_Chars, CharList[i][k]) != NULL) + Chars[i][j++] = CharList[i][k]; + if (j == 0) + Chars[i][j++] = ' '; // prevent empty char lists + arraySizes[i] = j; + } + + height+=260; + y = ((500-height)>>1); +} + + +void CStringInputSMS::NormalKeyPressed(const neutrino_msg_t key) +{ + if (CRCInput::isNumeric(key)) + { + int numericvalue = CRCInput::getNumericValue(key); + if (last_digit != numericvalue) + { + if ((last_digit != -1) && // there is a last key + (selected < (size- 1))) // we can shift the cursor one field to the right + { + selected++; + paintChar(selected - 1); + } + keyCounter = 0; + } + else + keyCounter = (keyCounter + 1) % arraySizes[numericvalue]; + value[selected] = Chars[numericvalue][keyCounter]; + last_digit = numericvalue; + paintChar(selected); + } + else + { + value[selected] = (char)CRCInput::getUnicodeValue(key); + keyRedPressed(); /* to lower, paintChar */ + keyRightPressed(); /* last_digit = -1, move to next position */ + } +} + +void CStringInputSMS::keyBackspacePressed(void) +{ + last_digit = -1; + CStringInput::keyBackspacePressed(); +} + +void CStringInputSMS::keyRedPressed() // switch between lower & uppercase +{ + if (((value[selected] | 32) >= 'a') && ((value[selected] | 32) <= 'z')) + value[selected] ^= 32; + + paintChar(selected); +} + +void CStringInputSMS::keyYellowPressed() // clear all +{ + last_digit = -1; + CStringInput::keyYellowPressed(); +} + +void CStringInputSMS::keyUpPressed() +{ + last_digit = -1; + + if (selected > 0) + { + int lastselected = selected; + selected = 0; + paintChar(lastselected); + paintChar(selected); + } +} + +void CStringInputSMS::keyDownPressed() +{ + last_digit = -1; + + int lastselected = selected; + + selected = size - 1; + + while (value[selected] == ' ') + { + selected--; + if (selected < 0) + break; + } + + if (selected < (size - 1)) + selected++; + + paintChar(lastselected); + paintChar(selected); +} + +void CStringInputSMS::keyLeftPressed() +{ + last_digit = -1; + CStringInput::keyLeftPressed(); +} + +void CStringInputSMS::keyRightPressed() +{ + last_digit = -1; + CStringInput::keyRightPressed(); +} + +const struct button_label CStringInputSMSButtons[2] = +{ + { NEUTRINO_ICON_BUTTON_RED , LOCALE_STRINGINPUT_CAPS }, +// { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_XXX }, + { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_STRINGINPUT_CLEAR } +// { NEUTRINO_ICON_BUTTON_BLUE , LOCALE_XXX } +}; + +void CStringInputSMS::paint() +{ + CStringInput::paint(); + + frameBuffer->paintIcon("numericpad.raw", x+20+140, y+ hheight+ mheight+ iheight* 3+ 30, COL_MENUCONTENT); + + frameBuffer->paintBoxRel(x,y+height-25, width,25, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 2); + frameBuffer->paintHLine(x, x+width, y+height-25, COL_INFOBAR_SHADOW_PLUS_0); + + ::paintButtons(frameBuffer, g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL], g_Locale, x + 8, y+height-25+1, 230, 2, CStringInputSMSButtons); +} + +void CPINInput::paintChar(int pos) +{ + CStringInput::paintChar(pos, (value[pos] == ' ') ? ' ' : '*'); +} + +int CPINInput::exec( CMenuTarget* parent, const std::string & ) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int res = menu_return::RETURN_REPAINT; + + if (parent) + parent->hide(); + + for(int count=strlen(value)-1;countgetMsg( &msg, &data, 300 ); + + if (msg==CRCInput::RC_left) + { + keyLeftPressed(); + } + else if (msg==CRCInput::RC_right) + { + keyRightPressed(); + } + else if (CRCInput::isNumeric(msg)) + { + int old_selected = selected; + NormalKeyPressed(msg); + if ( old_selected == ( size- 1 ) ) + loop=false; + } + else if ( (msg==CRCInput::RC_up) || + (msg==CRCInput::RC_down) ) + { + g_RCInput->postMsg( msg, data ); + res = menu_return::RETURN_EXIT; + loop=false; + } + else if ( (msg==CRCInput::RC_home) || (msg==CRCInput::RC_timeout) || (msg==CRCInput::RC_ok) ) + { + loop=false; + } + else + { + int r = handleOthers(msg, data); + if (r & (messages_return::cancel_all | messages_return::cancel_info)) + { + res = (r & messages_return::cancel_all) ? menu_return::RETURN_EXIT_ALL : menu_return::RETURN_EXIT; + loop = false; + } + else if ( r & messages_return::unhandled ) + { + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & ( messages_return::cancel_all | messages_return::cancel_info ) ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + } + + hide(); + + for(int count=size-1;count>=0;count--) + { + if((value[count]==' ') || (value[count]==0)) + { + value[count]=0; + } + else + break; + } + value[size]=0; + + if ( (observ) && (msg==CRCInput::RC_ok) ) + { + observ->changeNotify(name, value); + } + + return res; +} + +int CPLPINInput::handleOthers(neutrino_msg_t msg, neutrino_msg_data_t data) +{ + int res = messages_return::unhandled; + + if ( msg == NeutrinoMessages::EVT_PROGRAMLOCKSTATUS ) + { + // trotzdem handlen + CNeutrinoApp::getInstance()->handleMsg(msg, data); + + if (data != (neutrino_msg_data_t) fsk) + res = messages_return::cancel_info; + } + + return res; +} + +const char * CPLPINInput::getHint1(void) +{ + if (fsk == 0x100) + { + hint_1 = LOCALE_PARENTALLOCK_LOCKEDCHANNEL; + return CStringInput::getHint1(); + } + else + { + sprintf(hint, g_Locale->getText(LOCALE_PARENTALLOCK_LOCKEDPROGRAM), fsk); + return hint; + } +} + +#define borderwidth 4 + +int CPLPINInput::exec( CMenuTarget* parent, const std::string & ) +{ + fb_pixel_t * pixbuf = new fb_pixel_t[(width+ 2* borderwidth) * (height+ 2* borderwidth)]; + + if (pixbuf != NULL) + frameBuffer->SaveScreen(x- borderwidth, y- borderwidth, width+ 2* borderwidth, height+ 2* borderwidth, pixbuf); + + // clear border + frameBuffer->paintBackgroundBoxRel(x- borderwidth, y- borderwidth, width+ 2* borderwidth, borderwidth); + frameBuffer->paintBackgroundBoxRel(x- borderwidth, y+ height, width+ 2* borderwidth, borderwidth); + frameBuffer->paintBackgroundBoxRel(x- borderwidth, y, borderwidth, height); + frameBuffer->paintBackgroundBoxRel(x+ width, y, borderwidth, height); + + int res = CPINInput::exec ( parent, "" ); + + if (pixbuf != NULL) + { + frameBuffer->RestoreScreen(x- borderwidth, y- borderwidth, width+ 2* borderwidth, height+ 2* borderwidth, pixbuf); + delete[] pixbuf;//Mismatching allocation and deallocation: pixbuf + } + + return ( res ); +} diff --git a/src/gui/widget/stringinput.h b/src/gui/widget/stringinput.h new file mode 100644 index 000000000..832ad19c0 --- /dev/null +++ b/src/gui/widget/stringinput.h @@ -0,0 +1,163 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __stringinput__ +#define __stringinput__ + +#include "menue.h" + +#include +#include + +#include + +class CStringInput : public CMenuTarget +{ + protected: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight; // head font height + int mheight; // menu font height + int iheight; + + char * head; + neutrino_locale_t name; + neutrino_locale_t hint_1, hint_2; + std::string iconfile; + const char * validchars; + char * value; + std::string *valueString; + int size; + int selected; + CChangeObserver * observ; + + virtual void init(); + virtual const char * getHint1(void); + + virtual void paint(); + virtual void paintChar(int pos, char c); + virtual void paintChar(int pos); + + virtual void NormalKeyPressed(const neutrino_msg_t key); + virtual void keyBackspacePressed(void); + virtual void keyRedPressed(); + virtual void keyYellowPressed(); + virtual void keyBluePressed(); + virtual void keyUpPressed(); + virtual void keyDownPressed(); + virtual void keyLeftPressed(); + virtual void keyRightPressed(); + virtual void keyPlusPressed(); + virtual void keyMinusPressed(); + + virtual int handleOthers(const neutrino_msg_t msg, const neutrino_msg_data_t data); + + public: + + CStringInput(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1 = NONEXISTANT_LOCALE, const neutrino_locale_t Hint_2 = NONEXISTANT_LOCALE, const char * const Valid_Chars= (const char *) "0123456789. ", CChangeObserver* Observ = NULL, const char * const Icon = NULL); + CStringInput(char * Head, char* Value, int Size, const neutrino_locale_t Hint_1 = NONEXISTANT_LOCALE, const neutrino_locale_t Hint_2 = NONEXISTANT_LOCALE, const char * const Valid_Chars= (const char *) "0123456789. ", CChangeObserver* Observ = NULL, const char * const Icon = NULL); + CStringInput(const neutrino_locale_t Name, std::string* Value, int Size, const neutrino_locale_t Hint_1 = NONEXISTANT_LOCALE, const neutrino_locale_t Hint_2 = NONEXISTANT_LOCALE, const char * const Valid_Chars= (const char *) "0123456789. ", CChangeObserver* Observ = NULL, const char * const Icon = NULL); + ~CStringInput(); + + void hide(); + int exec( CMenuTarget* parent, const std::string & actionKey ); + +}; + +class CStringInputSMS : public CStringInput +{ + bool capsMode; + int arraySizes[10]; + char Chars[10][9]; // maximal 9 character in one CharList entry! + + int keyCounter; + int last_digit; + + virtual void NormalKeyPressed(const neutrino_msg_t key); + virtual void keyBackspacePressed(void); + virtual void keyRedPressed(); + virtual void keyYellowPressed(); + virtual void keyUpPressed(); + virtual void keyDownPressed(); + virtual void keyLeftPressed(); + virtual void keyRightPressed(); + + virtual void paint(); + void initSMS(const char * const Valid_Chars); + + public: + CStringInputSMS(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ = NULL, const char * const Icon = NULL); + CStringInputSMS(const neutrino_locale_t Name, std::string* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ = NULL, const char * const Icon = NULL); +}; + +class CPINInput : public CStringInput +{ + protected: + virtual void paintChar(int pos); + public: + CPINInput(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1 = NONEXISTANT_LOCALE, const neutrino_locale_t Hint_2 = NONEXISTANT_LOCALE, const char * const Valid_Chars= (const char *)"0123456789", CChangeObserver* Observ = NULL) + : CStringInput(Name, (char *)Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, (char *)"lock.raw") {}; + CPINInput(char * Head, char* Value, int Size, const neutrino_locale_t Hint_1 = NONEXISTANT_LOCALE, const neutrino_locale_t Hint_2 = NONEXISTANT_LOCALE, const char * const Valid_Chars= (const char *)"0123456789", CChangeObserver* Observ = NULL) + : CStringInput(Head, (char *)Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, (char *)"lock.raw") {}; + + int exec( CMenuTarget* parent, const std::string & actionKey ); +}; + +class CPLPINInput : public CPINInput +{ + protected: + int fsk; + char hint[100]; + + virtual int handleOthers(const neutrino_msg_t msg, const neutrino_msg_data_t data); + + virtual const char * getHint1(void); + + public: + CPLPINInput(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_2, int FSK ) + : CPINInput(Name, (char *)Value, Size, NONEXISTANT_LOCALE, Hint_2) { fsk = FSK; }; + + int exec( CMenuTarget* parent, const std::string & actionKey ); +}; + +class CPINChangeWidget : public CStringInput +{ + public: + CPINChangeWidget(const neutrino_locale_t Name, char* Value, int Size, const neutrino_locale_t Hint_1, const char * const Valid_Chars= (const char *) "0123456789", CChangeObserver* Observ = NULL) + : CStringInput(Name, (char *)Value, Size, Hint_1, NONEXISTANT_LOCALE, Valid_Chars, Observ){}; +}; + + +#endif diff --git a/src/gui/widget/stringinput_ext.cpp b/src/gui/widget/stringinput_ext.cpp new file mode 100644 index 000000000..4e294c31d --- /dev/null +++ b/src/gui/widget/stringinput_ext.cpp @@ -0,0 +1,668 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include + +#include + +#include +#include + +#define ROUND_RADIUS 9 + +CExtendedInput::CExtendedInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ, bool* Cancel) +{ + name = Name; + value = Value; + cancel = Cancel; + + hint_1 = Hint_1; + hint_2 = Hint_2; + + observ = Observ; + Init(); +} + +void CExtendedInput::Init(void) +{ + + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + iheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->getHeight(); + + width = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(g_Locale->getText(name), true) + 20; // UTF-8 + height = hheight+ mheight+ 20; + + if (hint_1 != NONEXISTANT_LOCALE) + height += iheight; + if (hint_2 != NONEXISTANT_LOCALE) + height += iheight; + + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth() - width)>>1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight() - height)>>1); +} + +CExtendedInput::~CExtendedInput() +{ + for (std::vector::iterator it = inputFields.begin(); it < inputFields.end(); it++) + delete *it; +} + +void CExtendedInput::addInputField( CExtendedInput_Item* fld ) +{ + inputFields.push_back(fld); +} + + +void CExtendedInput::calculateDialog() +{ + int ix = 0; + int iy = 0; + int maxX = 0; + int maxY = 0; + + selectedChar = -1; + for(unsigned int i=0; iinit( ix, iy); + inputFields[i]->setDataPointer( &value[i] ); + if ((selectedChar==-1) && (inputFields[i]->isSelectable())) + { + selectedChar = i; + } + maxX = ix > maxX ? ix : maxX; + maxY = iy > maxY ? iy : maxY; + } + + width = width > maxX+40 ? width : maxX+40; + height = height > maxY + hheight + mheight ? height : maxY + hheight + mheight; + + hintPosY = height -10; + + if (hint_1 != NONEXISTANT_LOCALE) + height += iheight; + if (hint_2 != NONEXISTANT_LOCALE) + height += iheight; + + x = ((frameBuffer->getScreenWidth() - width)>>1); + y = ((frameBuffer->getScreenHeight() - height)>>1); + + hintPosY += y; +} + + +int CExtendedInput::exec( CMenuTarget* parent, const std::string & ) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + onBeforeExec(); + int res = menu_return::RETURN_REPAINT; + char oldval[inputFields.size()+10], dispval[inputFields.size()+10]; + + if (parent) + { + parent->hide(); + } + + strcpy(oldval, value); + paint(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + bool loop=true; + while (loop) + { + if ( strcmp(value, dispval) != 0) + { + CVFD::getInstance()->showMenuText(1, value, selectedChar+1); + strcpy(dispval, value); + } + + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd, true ); + + if (msg==CRCInput::RC_left) { + bool found = false; + int oldSelectedChar = selectedChar; + if(selectedChar > 0) { + for(int i=selectedChar-1; i>=0;i--) { + if (inputFields[i]->isSelectable()) { + found = true; + selectedChar = i; + break; + } + } + } else { + for(int i = inputFields.size() - 1; i >= 0; i--) { + if(inputFields[i]->isSelectable()) { + found = true; + selectedChar = i; + break; + } + } + } + if(found) { + inputFields[oldSelectedChar]->paint( x+20, y+hheight +20, false ); + inputFields[selectedChar]->paint( x+20, y+hheight +20, true ); + CVFD::getInstance()->showMenuText(1, value, selectedChar+1); + } + } else if (msg==CRCInput::RC_right) { + bool found = false; + int oldSelectedChar = selectedChar; + if(selectedChar < (int) inputFields.size()-1) { + for(unsigned int i = selectedChar+1; i < inputFields.size();i++) { + if (inputFields[i]->isSelectable()) { + found = true; + selectedChar = i; + break; + } + } + } + if(!found) { + for(int i = 0; i < (int) inputFields.size(); i++) { +//printf("old %d sel %d size %d i %d\n", oldSelectedChar, selectedChar, inputFields.size(), i); + if(inputFields[i]->isSelectable()) { + found = true; + selectedChar = i; + break; + } + } + } + if(found) { + inputFields[oldSelectedChar]->paint( x+20, y+hheight +20, false ); + inputFields[selectedChar]->paint( x+20, y+hheight +20, true ); + CVFD::getInstance()->showMenuText(1, value, selectedChar+1); + } + } + else if ( (CRCInput::getUnicodeValue(msg) != -1) || (msg == CRCInput::RC_red) || (msg == CRCInput::RC_green) || (msg == CRCInput::RC_blue) || (msg == CRCInput::RC_yellow) + || (msg == CRCInput::RC_up) || (msg == CRCInput::RC_down)) + { + inputFields[selectedChar]->keyPressed(msg); + inputFields[selectedChar]->paint( x+20, y+hheight +20, true ); + } + else if (msg==CRCInput::RC_ok) + { + loop=false; + if(cancel != NULL) + *cancel = false; + } + else if ( (msg==CRCInput::RC_home) || (msg==CRCInput::RC_timeout) ) + { + if(strcmp(value, oldval)!= 0){ + int erg = ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbNo | CMessageBox::mbYes | CMessageBox::mbCancel); + if(erg==CMessageBox::mbrYes){ + strcpy(value, oldval); + loop=false; + if(cancel != NULL) + *cancel = true; + } + else if(erg==CMessageBox::mbrNo){ + loop=false; + if(cancel != NULL) + *cancel = false; + } + else if(erg==CMessageBox::mbrCancel){ + } + } else { + //keine änderungen - beenden ok + loop=false; + if(cancel != NULL) + *cancel = true; + } + } + else if ((msg ==CRCInput::RC_sat) || (msg == CRCInput::RC_favorites)) + { + } + else if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + + hide(); + + onAfterExec(); + + if ((observ) && (msg == CRCInput::RC_ok)) + { + observ->changeNotify(name, value); + } + + return res; +} + +void CExtendedInput::hide() +{ + frameBuffer->paintBackgroundBoxRel(x, y, width, height); +} + +void CExtendedInput::paint() +{ + frameBuffer->paintBoxRel(x, y, width, hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1); + frameBuffer->paintBoxRel(x, y + hheight, width, height - hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+ 10, y+ hheight, width- 10, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + + if (hint_1 != NONEXISTANT_LOCALE) + { + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ 20, hintPosY, width- 20, g_Locale->getText(hint_1), COL_MENUCONTENT, 0, true); // UTF-8 + if (hint_2 != NONEXISTANT_LOCALE) + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ 20, hintPosY + iheight, width- 20, g_Locale->getText(hint_2), COL_MENUCONTENT, 0, true); // UTF-8 + } + + for(unsigned int i=0; ipaint( x+20, y+hheight +20, (i== (unsigned int) selectedChar) ); + } + + +} + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + +CExtendedInput_Item_Char::CExtendedInput_Item_Char(const std::string & Chars, bool Selectable ) +{ + frameBuffer = CFrameBuffer::getInstance(); + idx = 20; + idy = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + allowedChars = Chars; + selectable = Selectable; +} + +void CExtendedInput_Item_Char::init(int &x, int &y) +{ + ix = x; + iy = y; + x += idx; +} + +void CExtendedInput_Item_Char::setAllowedChars( const std::string & ac ) +{ + allowedChars = ac; +} + +void CExtendedInput_Item_Char::paint(int x, int y, bool focusGained ) +{ + int startx = ix + x; + int starty = iy + y; + + uint8_t color; + fb_pixel_t bgcolor; + + if (focusGained) + { + color = COL_MENUCONTENTSELECTED; + bgcolor = COL_MENUCONTENTSELECTED_PLUS_0; + } + else + { + color = COL_MENUCONTENT; + bgcolor = COL_MENUCONTENT_PLUS_0; + } + + frameBuffer->paintBoxRel( startx, starty, idx, idy, COL_MENUCONTENT_PLUS_4); + frameBuffer->paintBoxRel( startx+1, starty+1, idx-2, idy-2, bgcolor); + + char text[2]; + text[0] = *data; + text[1] = 0; + int xfpos = startx + 1 + ((idx- g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth( text ))>>1); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(xfpos,starty+idy, idx, text, color); +} + +bool CExtendedInput_Item_Char::isAllowedChar( char ch ) +{ + return ( (int) allowedChars.find(ch) != -1); +} + +int CExtendedInput_Item_Char::getCharID( char ch ) +{ + return allowedChars.find(ch); +} + +void CExtendedInput_Item_Char::keyPressed(const int key) +{ + int value = CRCInput::getUnicodeValue(key); + if (value != -1) + { + if (isAllowedChar((char)value)) + { + *data = (char)value; + g_RCInput->postMsg( CRCInput::RC_right, 0 ); + } + } + else + { + unsigned int pos = getCharID( *data ); + if (key==CRCInput::RC_up) + { + if(pos0) + { + *data = allowedChars[pos-1]; + } + else + { + *data = allowedChars[allowedChars.size()-1]; + } + } + } +} + +//-----------------------------#################################------------------------------------------------------- + +CIPInput::CIPInput(const neutrino_locale_t Name, std::string & Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ) + : CExtendedInput(Name, IP, Hint_1, Hint_2, Observ) +{ + ip = &Value; + frameBuffer = CFrameBuffer::getInstance(); + addInputField( new CExtendedInput_Item_Char("012") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("012") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("012") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("012") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_newLiner(30) ); + calculateDialog(); +} + +void CIPInput::onBeforeExec() +{ + if (ip->empty()) + { + strcpy(value, "000.000.000.000"); + //printf("[neutrino] value-before(2): %s\n", value); + return; + } + unsigned char _ip[4]; + sscanf(ip->c_str(), "%hhu.%hhu.%hhu.%hhu", &_ip[0], &_ip[1], &_ip[2], &_ip[3]); + sprintf( value, "%03hhu.%03hhu.%03hhu.%03hhu", _ip[0], _ip[1], _ip[2], _ip[3]); +} + +void CIPInput::onAfterExec() +{ + int _ip[4]; + sscanf( value, "%3d.%3d.%3d.%3d", &_ip[0], &_ip[1], &_ip[2], &_ip[3] ); + sprintf( value, "%d.%d.%d.%d", _ip[0], _ip[1], _ip[2], _ip[3]); + if(strcmp(value,"0.0.0.0")==0) + { + (*ip) = ""; + } + else + (*ip) = value; +} + +//-----------------------------#################################------------------------------------------------------- +CDateInput::CDateInput(const neutrino_locale_t Name, time_t* Time, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ) + : CExtendedInput(Name, (char *) "", Hint_1, Hint_2, Observ) +{ + time=Time; + value= new char[20]; + struct tm *tmTime = localtime(time); + sprintf( value, "%02d.%02d.%04d %02d:%02d", tmTime->tm_mday, tmTime->tm_mon+1, + tmTime->tm_year+1900, + tmTime->tm_hour, tmTime->tm_min); + + frameBuffer = CFrameBuffer::getInstance(); + addInputField( new CExtendedInput_Item_Char("0123") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char(".",false) ); + addInputField( new CExtendedInput_Item_Char("01") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char(".",false) ); + addInputField( new CExtendedInput_Item_Char("2",false) ); + addInputField( new CExtendedInput_Item_Char("0",false) ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("012") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char(":",false) ); + addInputField( new CExtendedInput_Item_Char("012345") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_newLiner(30) ); + calculateDialog(); +} +CDateInput::~CDateInput() +{ + delete value; +} +void CDateInput::onBeforeExec() +{ + struct tm *tmTime = localtime(time); + sprintf( value, "%02d.%02d.%04d %02d:%02d", tmTime->tm_mday, tmTime->tm_mon+1, + tmTime->tm_year+1900, + tmTime->tm_hour, tmTime->tm_min); + dst = tmTime->tm_isdst; +} + +void CDateInput::onAfterExec() +{ + struct tm tmTime; + sscanf( value, "%02d.%02d.%04d %02d:%02d", &tmTime.tm_mday, &tmTime.tm_mon, + &tmTime.tm_year, + &tmTime.tm_hour, &tmTime.tm_min); + tmTime.tm_mon-=1; + tmTime.tm_year-=1900; + tmTime.tm_sec=0; + tmTime.tm_isdst=dst; + + if(tmTime.tm_year>129) + tmTime.tm_year=129; + if(tmTime.tm_year<0) + tmTime.tm_year=0; + if(tmTime.tm_mon>11) + tmTime.tm_mon=11; + if(tmTime.tm_mon<0) + tmTime.tm_mon=0; + if(tmTime.tm_mday>31) //-> eine etwas laxe pruefung, aber mktime biegt das wieder grade + tmTime.tm_mday=31; + if(tmTime.tm_mday<1) + tmTime.tm_mday=1; + if(tmTime.tm_hour>23) + tmTime.tm_hour=23; + if(tmTime.tm_hour<0) + tmTime.tm_hour=0; + if(tmTime.tm_min>59) + tmTime.tm_min=59; + if(tmTime.tm_min<0) + tmTime.tm_min=0; + if(tmTime.tm_sec>59) + tmTime.tm_sec=59; + if(tmTime.tm_sec<0) + tmTime.tm_sec=0; + *time=mktime(&tmTime); + struct tm *tmTime2 = localtime(time); + sprintf( value, "%02d.%02d.%04d %02d:%02d", tmTime2->tm_mday, tmTime2->tm_mon+1, + tmTime2->tm_year+1900, + tmTime2->tm_hour, tmTime2->tm_min); +} +//-----------------------------#################################------------------------------------------------------- + +CMACInput::CMACInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ) + : CExtendedInput(Name, Value, Hint_1, Hint_2, Observ) +{ + frameBuffer = CFrameBuffer::getInstance(); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_Char("0123456789ABCDEF") ); + addInputField( new CExtendedInput_Item_newLiner(30) ); + calculateDialog(); +} + +void CMACInput::onBeforeExec() +{ + if (value[0] == 0) /* strcmp(value, "") == 0 */ + { + strcpy(value, "00:00:00:00:00:00"); + printf("[neutrino] value-before(2): %s\n", value); + return; + } + int _mac[6]; + sscanf( value, "%x:%x:%x:%x:%x:%x", &_mac[0], &_mac[1], &_mac[2], &_mac[3], &_mac[4], &_mac[5] ); + sprintf( value, "%02x:%02x:%02x:%02x:%02x:%02x", _mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]); +} + +void CMACInput::onAfterExec() +{ + int _mac[6]; + sscanf( value, "%x:%x:%x:%x:%x:%x", &_mac[0], &_mac[1], &_mac[2], &_mac[3], &_mac[4], &_mac[5] ); + sprintf( value, "%02x:%02x:%02x:%02x:%02x:%02x", _mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]); + if(strcmp(value,"00:00:00:00:00:00")==0) + value[0] = 0; /* strcpy(value, ""); */ +} + +//-----------------------------#################################------------------------------------------------------- + +CTimeInput::CTimeInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ, bool* Cancel) + : CExtendedInput(Name, Value, Hint_1, Hint_2, Observ, Cancel) +{ + frameBuffer = CFrameBuffer::getInstance(); + addInputField( new CExtendedInput_Item_Char("=+-") ); + addInputField( new CExtendedInput_Item_Spacer(20) ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char(":",false) ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char(":",false) ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_Char("0123456789") ); + addInputField( new CExtendedInput_Item_newLiner(30) ); + calculateDialog(); +} + +void CTimeInput::onBeforeExec() +{ + strcpy(value, "= 00:00:00"); +} + +void CTimeInput::onAfterExec() +{ + char tmp[10+1]; + strcpy(tmp, value); + strcpy(value+1, tmp+2); +} +//-----------------------------#################################------------------------------------------------------- + +CIntInput::CIntInput(const neutrino_locale_t Name, int& Value, const unsigned int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ) + : CExtendedInput(Name, myValueStringInput, Hint_1, Hint_2, Observ) +{ + myValue = &Value; + + if (Size +#include + +#include +#include + +class CExtendedInput_Item; +class CExtendedInput : public CMenuTarget +{ + protected: + void Init(void); + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hintPosY; + int hheight; // head font height + int mheight; // menu font height + int iheight; + + std::vector inputFields; + int selectedChar; + + neutrino_locale_t name; + neutrino_locale_t hint_1; + neutrino_locale_t hint_2; + + char* value; + CChangeObserver* observ; + bool* cancel; + + virtual void paint(); + virtual void onBeforeExec(){}; + virtual void onAfterExec(){}; + + public: + + CExtendedInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL, bool* cancel = NULL); + ~CExtendedInput(); + + void hide(); + int exec( CMenuTarget* parent, const std::string & actionKey ); + void calculateDialog(); + + void addInputField( CExtendedInput_Item* ); +}; + + +class CExtendedInput_Item +{ + protected: + CFrameBuffer *frameBuffer; + int ix, iy, idx, idy; + char* data; + + public: + + virtual void setDataPointer(char* Data){data=Data;}; + virtual void init(int &x, int &y){}; + virtual void paint(int x, int y, bool focusGained){}; + virtual bool isSelectable(){return true;}; + + virtual void keyPressed( int key ){}; +}; + +class CExtendedInput_Item_Spacer : public CExtendedInput_Item +{ + protected: + int mSpacingX; + int mSpacingY; + public: + CExtendedInput_Item_Spacer(){}; + CExtendedInput_Item_Spacer(int spaceX, int spaceY=0){mSpacingX=spaceX;mSpacingY=spaceY;}; + virtual void init(int &x, int &y){x+=mSpacingX;y+=mSpacingY;}; + virtual bool isSelectable(){return false;}; +}; + +class CExtendedInput_Item_newLiner : public CExtendedInput_Item +{ + protected: + int mSpacingY; + public: + CExtendedInput_Item_newLiner(){}; + CExtendedInput_Item_newLiner(int spaceY){mSpacingY=spaceY;}; + virtual void init(int &x, int &y){x=0;y+=mSpacingY;}; + virtual bool isSelectable(){return false;}; +}; + + +class CExtendedInput_Item_Char : public CExtendedInput_Item +{ + protected: + std::string allowedChars; + bool selectable; + + bool isAllowedChar( char ); + int getCharID( char ); + + public: + CExtendedInput_Item_Char(const std::string & Chars="", bool Selectable=true ); + virtual ~CExtendedInput_Item_Char(){}; + void setAllowedChars( const std::string & ); + virtual void init(int &x, int &y); + virtual void paint(int x, int y, bool focusGained); + + virtual void keyPressed( int key ); + virtual bool isSelectable(){return selectable;}; +}; + +//---------------------------------------------------------------------------------------------------- + +class CIPInput : public CExtendedInput +{ + char IP[16]; + std::string * ip; + + protected: + virtual void onBeforeExec(); + virtual void onAfterExec(); + + public: + CIPInput(const neutrino_locale_t Name, std::string & Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL); +}; + +//---------------------------------------------------------------------------------------------------- + +class CDateInput : public CExtendedInput +{ + private: + time_t* time; + int dst; + + protected: + virtual void onBeforeExec(); + virtual void onAfterExec(); + + public: + CDateInput(const neutrino_locale_t Name, time_t* Time, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL); + ~CDateInput(); + char* getValue() {return value;} +}; + +//---------------------------------------------------------------------------------------------------- + +class CMACInput : public CExtendedInput +{ + protected: + virtual void onBeforeExec(); + virtual void onAfterExec(); + + public: + CMACInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL); +}; + +//---------------------------------------------------------------------------------------------------- + +class CTimeInput : public CExtendedInput +{ + protected: + virtual void onBeforeExec(); + virtual void onAfterExec(); + + public: + CTimeInput(const neutrino_locale_t Name, char* Value, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL, bool* cancel=NULL); +}; + +//---------------------------------------------------------------------------------------------------- + +class CIntInput : public CExtendedInput +{ +#define MAX_CINTINPUT_SIZE 16 + + char myValueStringInput[MAX_CINTINPUT_SIZE]; + char myValueStringOutput[MAX_CINTINPUT_SIZE]; + + int* myValue; + unsigned int m_size; + protected: + virtual void onBeforeExec(); + virtual void onAfterExec(); + + public: + /** + *@param Size how many digits can be entered + */ + CIntInput(const neutrino_locale_t Name, int& Value, const unsigned int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, CChangeObserver* Observ = NULL); + char* getValue() { + return myValueStringOutput; + } +}; + + + +#endif diff --git a/src/gui/widget/textbox.cpp b/src/gui/widget/textbox.cpp new file mode 100644 index 000000000..4463091c3 --- /dev/null +++ b/src/gui/widget/textbox.cpp @@ -0,0 +1,533 @@ +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: textbox.cpp: . + + Description: implementation of the CTextBox class + This class provides a plain textbox with selectable features: + - Foot, Title + - Scroll bar + - Frame shadow + - Auto line break + - fixed position or auto width and auto height (later not tested yet) + - Center Text + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Günther initial implementation + +****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "textbox.h" +#include + +#define TEXT_BORDER_WIDTH 8 +#define SCROLL_FRAME_WIDTH 10 +#define SCROLL_MARKER_BORDER 2 + +#define MAX_WINDOW_WIDTH (g_settings.screen_EndX - g_settings.screen_StartX - 40) +#define MAX_WINDOW_HEIGHT (g_settings.screen_EndY - g_settings.screen_StartY - 40) + +#define MIN_WINDOW_WIDTH ((g_settings.screen_EndX - g_settings.screen_StartX)>>1) +#define MIN_WINDOW_HEIGHT 40 + +CTextBox::CTextBox(const char * text, Font* font_text, const int mode, + const CBox* position, CFBWindow::color_t textBackgroundColor) +{ + //TRACE("[CTextBox] new\r\n"); + initVar(); + + frameBuffer = NULL; + max_width = 0; + + if(text != NULL) m_cText = text; + if(font_text != NULL) m_pcFontText = font_text; + if(position != NULL) + { + m_cFrame = *position; + m_nMaxHeight = m_cFrame.iHeight; + m_nMaxWidth = m_cFrame.iWidth; + } + + m_nMode = mode; + + /* in case of auto line break, we do no support auto width yet */ + if( !(mode & NO_AUTO_LINEBREAK)) + { + m_nMode = m_nMode & ~AUTO_WIDTH; /* delete any AUTO_WIDTH*/ + } + +#if 0 + TRACE(" Mode: "); + if(mode & SCROLL) TRACE("SCROLL "); + if(mode & NO_AUTO_LINEBREAK) TRACE("NO_AUTO_LINEBREAK "); + if(mode & AUTO_WIDTH) TRACE("AUTO_WIDTH "); + if(mode & AUTO_HIGH) TRACE("AUTO_HIGH"); + TRACE("\r\n"); + +#endif + //TRACE(" CTextBox::m_cText: %d, m_nMode %d\t\r\n",m_cText.size(),m_nMode); + + m_textBackgroundColor = textBackgroundColor; + m_nFontTextHeight = m_pcFontText->getHeight(); + //TRACE(" CTextBox::m_nFontTextHeight: %d\t\r\n",m_nFontTextHeight); + + /* Initialise the window frames first */ + initFramesRel(); + + // than refresh text line array + refreshTextLineArray(); +} + +CTextBox::CTextBox(const char * text) +{ + //TRACE("[CTextBox] new\r\n"); + initVar(); + + frameBuffer = NULL; + if(text != NULL) m_cText = *text; + + /* Initialise the window frames first */ + initFramesRel(); + + // than refresh text line array + refreshTextLineArray(); +} + +CTextBox::CTextBox() +{ + //TRACE("[CTextBox] new\r\n"); + initVar(); + initFramesRel(); + + frameBuffer = NULL; +} + +CTextBox::~CTextBox() +{ + //TRACE("[CTextBox] del\r\n"); + m_cLineArray.clear(); + hide(); + +} + +void CTextBox::initVar(void) +{ + //TRACE("[CTextBox]->InitVar\r\n"); + m_cText = ""; + m_nMode = SCROLL; + + m_pcFontText = NULL; + m_pcFontText = g_Font[SNeutrinoSettings::FONT_TYPE_EPG_INFO1]; + m_nFontTextHeight = m_pcFontText->getHeight(); + + m_nNrOfPages = 1; + m_nNrOfLines = 0; + m_nLinesPerPage = 0; + m_nCurrentLine = 0; + m_nCurrentPage = 0; + + m_cFrame.iX = g_settings.screen_StartX + ((g_settings.screen_EndX - g_settings.screen_StartX - MIN_WINDOW_WIDTH) >>1); + m_cFrame.iWidth = MIN_WINDOW_WIDTH; + m_cFrame.iY = g_settings.screen_StartY + ((g_settings.screen_EndY - g_settings.screen_StartY - MIN_WINDOW_HEIGHT) >>1); + m_cFrame.iHeight = MIN_WINDOW_HEIGHT; + + m_nMaxHeight = MAX_WINDOW_HEIGHT; + m_nMaxWidth = MAX_WINDOW_WIDTH; + + m_textBackgroundColor = COL_MENUCONTENT_PLUS_0; + + m_cLineArray.clear(); +} + +void CTextBox::reSizeMainFrameWidth(int textWidth) +{ + //TRACE("[CTextBox]->ReSizeMainFrameWidth: %d, current: %d\r\n",textWidth,m_cFrameTextRel.iWidth); + + int iNewWindowWidth = textWidth + m_cFrameScrollRel.iWidth + 2*TEXT_BORDER_WIDTH; + + if( iNewWindowWidth > m_nMaxWidth) iNewWindowWidth = m_nMaxWidth; + if( iNewWindowWidth < MIN_WINDOW_WIDTH) iNewWindowWidth = MIN_WINDOW_WIDTH; + + m_cFrame.iWidth = iNewWindowWidth; + + /* Re-Init the children frames due to new main window */ + initFramesRel(); +} + +void CTextBox::reSizeMainFrameHeight(int textHeight) +{ + TRACE("[CTextBox]->ReSizeMainFrameHeight: %d, current: %d\r\n",textHeight,m_cFrameTextRel.iHeight); + + int iNewWindowHeight = textHeight + + 2*TEXT_BORDER_WIDTH; + + if( iNewWindowHeight > m_nMaxHeight) iNewWindowHeight = m_nMaxHeight; + if( iNewWindowHeight < MIN_WINDOW_HEIGHT) iNewWindowHeight = MIN_WINDOW_HEIGHT; + + m_cFrame.iHeight = iNewWindowHeight; + + /* Re-Init the children frames due to new main window */ + initFramesRel(); +} + +void CTextBox::initFramesRel(void) +{ + //TRACE("[CTextBox]->InitFramesRel\r\n"); + + m_cFrameTextRel.iX = 0; + m_cFrameTextRel.iY = 0; + m_cFrameTextRel.iHeight = m_cFrame.iHeight ; + + if(m_nMode & SCROLL) + { + m_cFrameScrollRel.iX = m_cFrame.iWidth - SCROLL_FRAME_WIDTH; + m_cFrameScrollRel.iY = m_cFrameTextRel.iY; + m_cFrameScrollRel.iWidth = SCROLL_FRAME_WIDTH; + m_cFrameScrollRel.iHeight = m_cFrameTextRel.iHeight; + } + else + { + m_cFrameScrollRel.iX = 0; + m_cFrameScrollRel.iY = 0; + m_cFrameScrollRel.iHeight = 0; + m_cFrameScrollRel.iWidth = 0; + } + + m_cFrameTextRel.iWidth = m_cFrame.iWidth - m_cFrameScrollRel.iWidth; + + m_nLinesPerPage = (m_cFrameTextRel.iHeight - (2*TEXT_BORDER_WIDTH)) / m_nFontTextHeight; + +#if 0 + TRACE_1("Frames\r\n\tScren:\t%3d,%3d,%3d,%3d\r\n\tMain:\t%3d,%3d,%3d,%3d\r\n\tText:\t%3d,%3d,%3d,%3d \r\n\tScroll:\t%3d,%3d,%3d,%3d \r\n", + g_settings.screen_StartX, + g_settings.screen_StartY, + g_settings.screen_EndX, + g_settings.screen_EndY, + m_cFrame.iX, + m_cFrame.iY, + m_cFrame.iWidth, + m_cFrame.iHeight, + m_cFrameTextRel.iX, + m_cFrameTextRel.iY, + m_cFrameTextRel.iWidth, + m_cFrameTextRel.iHeight, + m_cFrameScrollRel.iX, + m_cFrameScrollRel.iY, + m_cFrameScrollRel.iWidth, + m_cFrameScrollRel.iHeight + ); +#endif +} + +void CTextBox::refreshTextLineArray(void) +{ + //TRACE("[CTextBox]->RefreshLineArray \r\n"); + int loop = true; + int pos_prev = 0; + int pos = 0; + int aktWidth = 0; + int aktWordWidth = 0; + int lineBreakWidth; + int maxTextWidth = 0; + + m_nNrOfNewLine = 0; + + std::string aktLine = ""; + std::string aktWord = ""; + + /* clear current line vector */ + m_cLineArray.clear(); + m_nNrOfLines = 0; + + if( m_nMode & AUTO_WIDTH) + { + /* In case of autowidth, we calculate the max allowed width of the textbox */ + lineBreakWidth = MAX_WINDOW_WIDTH - m_cFrameScrollRel.iWidth - 2*TEXT_BORDER_WIDTH; + } + else + { + /* If not autowidth, we just take the actuall textframe width */ + lineBreakWidth = m_cFrameTextRel.iWidth - 2*TEXT_BORDER_WIDTH; + } + if(max_width) + lineBreakWidth = max_width; + //printf("TextBox: lineBreakWidth %d\n", lineBreakWidth); + int TextChars = m_cText.size(); + // do not parse, if text is empty + if(TextChars > 0) + { + while(loop) + { + if(m_nMode & NO_AUTO_LINEBREAK) + { + pos = m_cText.find_first_of("\n",pos_prev); + } + else + { + pos = m_cText.find_first_of("\n-. ",pos_prev); + } + + //TRACE_1(" pos: %d pos_prev: %d\r\n",pos,pos_prev); + + if(pos == -1) + { + pos = TextChars+1; + loop = false; // note, this is not 100% correct. if the last characters does not fit in one line, the characters after are cut + //TRACE_1(" Textend found\r\n"); + } + + aktWord = m_cText.substr(pos_prev, pos - pos_prev + 1); + aktWordWidth = m_pcFontText->getRenderWidth(aktWord, true); + pos_prev = pos + 1; + //if(aktWord.find(""") == ) + if(1) + { + //TRACE_1(" aktWord: >%s< pos:%d\r\n",aktWord.c_str(),pos); + + if( aktWidth + aktWordWidth > lineBreakWidth && + !(m_nMode & NO_AUTO_LINEBREAK)) + { + /* we need a new line before we can continue */ + m_cLineArray.push_back(aktLine); + //TRACE_1(" end line: %s\r\n", aktLine.c_str()); + m_nNrOfLines++; + aktLine = ""; + aktWidth = 0; + + if(pos_prev >= TextChars) loop = false; + } + + aktLine += aktWord; + aktWidth += aktWordWidth; + if (aktWidth > maxTextWidth) maxTextWidth = aktWidth; + //TRACE_1(" aktLine : %s\r\n",aktLine.c_str()); + //TRACE_1(" aktWidth: %d aktWordWidth:%d\r\n",aktWidth,aktWordWidth); + + if( m_cText[pos] == '\n' || + loop == false) + { + // current line ends with an carriage return, make new line + if (m_cText[pos] == '\n') + aktLine.erase(aktLine.size() - 1,1); + m_cLineArray.push_back(aktLine); + m_nNrOfLines++; + aktLine = ""; + aktWidth = 0; + m_nNrOfNewLine++; + if(pos_prev >= TextChars) loop = false; + } + } + } + + + /* check if we have to recalculate the window frame size, due to auto width and auto height */ + if( m_nMode & AUTO_WIDTH) + { + reSizeMainFrameWidth(maxTextWidth); + } + + if(m_nMode & AUTO_HIGH) + { + reSizeMainFrameHeight(m_nNrOfLines * m_nFontTextHeight); + } + + m_nLinesPerPage = (m_cFrameTextRel.iHeight - (2*TEXT_BORDER_WIDTH)) / m_nFontTextHeight; + m_nNrOfPages = ((m_nNrOfLines-1) / m_nLinesPerPage) + 1; + + if(m_nCurrentPage >= m_nNrOfPages) + { + m_nCurrentPage = m_nNrOfPages - 1; + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + } + } + else + { + m_nNrOfPages = 0; + m_nNrOfLines = 0; + m_nCurrentPage = 0; + m_nCurrentLine = 0; + m_nLinesPerPage = 1; + } +#if 0 + TRACE_1(" m_nNrOfPages: %d\r\n",m_nNrOfPages); + TRACE_1(" m_nNrOfLines: %d\r\n",m_nNrOfLines); + TRACE_1(" m_nNrOfNewLine: %d\r\n",m_nNrOfNewLine); + TRACE_1(" maxTextWidth: %d\r\n",maxTextWidth); + TRACE_1(" m_nLinesPerPage: %d\r\n",m_nLinesPerPage); + TRACE_1(" m_nFontTextHeight:%d\r\n",m_nFontTextHeight); + TRACE_1(" m_nCurrentPage: %d\r\n",m_nCurrentPage); + TRACE_1(" m_nCurrentLine: %d\r\n",m_nCurrentLine); +#endif +} + +void CTextBox::refreshScroll(void) +{ + if( !(m_nMode & SCROLL)) return; + if( frameBuffer == NULL) return; + + if (m_nNrOfPages > 1) + { + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX+m_cFrame.iX, m_cFrameScrollRel.iY+m_cFrame.iY, + m_cFrameScrollRel.iWidth, m_cFrameScrollRel.iHeight, + COL_MENUCONTENT_PLUS_1); + unsigned int marker_size = m_cFrameScrollRel.iHeight / m_nNrOfPages; + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX + SCROLL_MARKER_BORDER+m_cFrame.iX, + m_cFrameScrollRel.iY + m_nCurrentPage * marker_size+m_cFrame.iY, + m_cFrameScrollRel.iWidth - 2*SCROLL_MARKER_BORDER, + marker_size, COL_MENUCONTENT_PLUS_3); + } + else + { + frameBuffer->paintBoxRel(m_cFrameScrollRel.iX+m_cFrame.iX, m_cFrameScrollRel.iY+m_cFrame.iY, + m_cFrameScrollRel.iWidth, m_cFrameScrollRel.iHeight, + m_textBackgroundColor); + } +} + +void CTextBox::refreshText(void) +{ + if( frameBuffer == NULL) return; + //TRACE(" CTextBox::refreshText: %d,%s\r\n",m_nCurrentLine,m_cLineArray[m_nCurrentLine].c_str()); + //Paint Text Background + frameBuffer->paintBoxRel(m_cFrameTextRel.iX+m_cFrame.iX, m_cFrameTextRel.iY+m_cFrame.iY, + m_cFrameTextRel.iWidth, m_cFrameTextRel.iHeight, m_textBackgroundColor); + + if( m_nNrOfLines <= 0) return; + int y = m_cFrameTextRel.iY + TEXT_BORDER_WIDTH; + int i; + int x_center = 0; + + for(i = m_nCurrentLine; i < m_nNrOfLines && i < m_nCurrentLine + m_nLinesPerPage; i++) + { + y += m_nFontTextHeight; + + if( m_nMode & CENTER ) + { + x_center = (m_cFrameTextRel.iWidth - m_pcFontText->getRenderWidth(m_cLineArray[i], true))>>1; + } + + m_pcFontText->RenderString(m_cFrameTextRel.iX + TEXT_BORDER_WIDTH + x_center+m_cFrame.iX, + y+m_cFrame.iY, m_cFrameTextRel.iWidth, m_cLineArray[i].c_str(), + COL_MENUCONTENT, 0, true); // UTF-8 + } +} + +void CTextBox::scrollPageDown(const int pages) +{ + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 0) return; + TRACE("[CTextBox]->ScrollPageDown \r\n"); + + + if(m_nCurrentPage + pages < m_nNrOfPages) + { + m_nCurrentPage += pages; + } + else + { + m_nCurrentPage = m_nNrOfPages - 1; + } + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + refresh(); +} + +void CTextBox::scrollPageUp(const int pages) +{ + if( !(m_nMode & SCROLL)) return; + if( m_nNrOfLines <= 0) return; + TRACE("[CTextBox]->ScrollPageUp \r\n"); + + + if(m_nCurrentPage - pages > 0) + { + m_nCurrentPage -= pages; + } + else + { + m_nCurrentPage = 0; + } + m_nCurrentLine = m_nCurrentPage * m_nLinesPerPage; + refresh(); +} + +void CTextBox::refresh(void) +{ + if( frameBuffer == NULL) return; + //TRACE("[CTextBox]->Refresh\r\n"); +//printf("setText::refresh!\n"); + + //Paint text + refreshScroll(); + refreshText(); +} + +bool CTextBox::setText(const std::string* newText, int _max_width) +{ + //TRACE("[CTextBox]->SetText \r\n"); + bool result = false; + max_width = _max_width; +//printf("setText: _max_width %d max_width %d\n", _max_width, max_width); + if (newText != NULL) + { + m_cText = *newText; + //m_cText = *newText + "\n"; //FIXME test + refreshTextLineArray(); + refresh(); + result = true; + } + return(result); +}; + +void CTextBox::paint (void) +{ + if(frameBuffer != NULL) return; + //TRACE("[CTextBox]->paint \r\n"); + frameBuffer = CFrameBuffer::getInstance(); + refresh(); +} + +void CTextBox::hide (void) +{ + if(frameBuffer == NULL) return; + //TRACE("[CTextBox]->hide \r\n"); + frameBuffer->paintBackgroundBoxRel(m_cFrame.iX, m_cFrame.iY, m_cFrame.iWidth, m_cFrame.iHeight); + frameBuffer = NULL; +} diff --git a/src/gui/widget/textbox.h b/src/gui/widget/textbox.h new file mode 100644 index 000000000..7d70e7113 --- /dev/null +++ b/src/gui/widget/textbox.h @@ -0,0 +1,168 @@ + +/*************************************************************************** + Neutrino-GUI - DBoxII-Project + + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + *********************************************************** + + Module Name: textbox.h . + + Description: interface of the CTextBox class + + Date: Nov 2005 + + Author: Günther@tuxbox.berlios.org + based on code of Steffen Hehn 'McClean' + + Revision History: + Date Author Change Description + Nov 2005 Günther initial implementation + +****************************************************************************/ + +#if !defined(TEXTBOX_H) +#define TEXTBOX_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TRACE printf +#define TRACE_1 printf + +class CBox +{ + private: + + public: + /* Constructor */ + inline CBox(){;}; + inline CBox( const int _iX, const int _iY, const int _iWidth, const int _iHeight){iX=_iX; iY=_iY; iWidth=_iWidth; iHeight=_iHeight;}; + inline ~CBox(){;}; + /* Functions */ + /* Variables */ + int iX; + int iY; + int iWidth; + int iHeight; +}; + +class CTextBox +{ + private: + /* Functions */ + void refreshTextLineArray(void); + void initVar(void); + void initFramesRel(void); + void refreshScroll(void); + void refreshText(void); + void reSizeMainFrameWidth(int maxTextWidth); + void reSizeMainFrameHeight(int maxTextHeight); + + /* Variables */ + std::string m_cText; + std::vector m_cLineArray; + + bool m_showTextFrame; + + CBox m_cFrame; + CBox m_cFrameTextRel; + CBox m_cFrameScrollRel; + + int m_nMaxHeight; + int m_nMaxWidth; + + int m_nMode; + + int m_nNrOfPages; + int m_nNrOfLines; + int m_nNrOfNewLine; + int m_nMaxLineWidth; + int m_nLinesPerPage; + int m_nCurrentLine; + int m_nCurrentPage; + + Font* m_pcFontText; + int m_nFontTextHeight; + CFBWindow::color_t m_textBackgroundColor; + + CFrameBuffer * frameBuffer; + int max_width; + public: + /* Constructor */ + CTextBox(); + CTextBox( const char * text); + CTextBox( const char * text, + Font* font_text, + const int mode, + const CBox* position, + CFBWindow::color_t textBackgroundColor = COL_MENUCONTENT_PLUS_0); + + virtual ~CTextBox(); + + /* Functions */ + void refresh(void); + void scrollPageDown(const int pages); + void scrollPageUp(const int pages); + bool setText(const std::string* newText, int _max_width = 0); +inline bool isPainted(void){if( frameBuffer == NULL) return (false); else return (true);}; + +inline CBox getWindowsPos(void) {return(m_cFrame);}; +inline int getMaxLineWidth(void) {return(m_nMaxLineWidth);}; +inline int getLines(void) {return(m_nNrOfLines);}; +inline int getPages(void) {return(m_nNrOfPages);}; +inline void movePosition(int x, int y){m_cFrame.iX = x; m_cFrame.iY = y;}; + + void paint (void); + void hide (void); + + + /* Variables */ + typedef enum mode_ + { + AUTO_WIDTH = 0x01, + AUTO_HIGH = 0x02, + SCROLL = 0x04, + CENTER = 0x40, + NO_AUTO_LINEBREAK = 0x80 + }mode; +}; + + + +#endif // !defined(AFX_TEXTBOX_H__208DED01_ABEC_491C_A632_5B21057DC5D8__INCLUDED_) diff --git a/src/gui/widget/vfdcontroler.cpp b/src/gui/widget/vfdcontroler.cpp new file mode 100644 index 000000000..f0a75a797 --- /dev/null +++ b/src/gui/widget/vfdcontroler.cpp @@ -0,0 +1,270 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define BRIGHTNESSFACTOR 0.15 // 0 - 15 + +#define ROUND_RADIUS 9 + +CVfdControler::CVfdControler(const neutrino_locale_t Name, CChangeObserver* Observer) +{ + frameBuffer = CFrameBuffer::getInstance(); + hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); + mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); + observer = Observer; + name = Name; + width = w_max(390, 0); + height = h_max(hheight+ mheight* 3+ +mheight/2, 0); + x = frameBuffer->getScreenX() + ((frameBuffer->getScreenWidth()-width) >> 1); + y = frameBuffer->getScreenY() + ((frameBuffer->getScreenHeight()-height)>>1); + + brightness = CVFD::getInstance()->getBrightness(); + brightnessstandby = CVFD::getInstance()->getBrightnessStandby(); +} + +void CVfdControler::setVfd() +{ + CVFD::getInstance()->setBrightness(brightness); + CVFD::getInstance()->setBrightnessStandby(brightnessstandby); +} + +int CVfdControler::exec(CMenuTarget* parent, const std::string &) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + int selected, res = menu_return::RETURN_REPAINT; + unsigned int brightness_alt, brightnessstandby_alt; + + if (parent) + { + parent->hide(); + } + brightness_alt = CVFD::getInstance()->getBrightness(); + brightnessstandby_alt = CVFD::getInstance()->getBrightnessStandby(); + selected = 0; + + setVfd(); + paint(); + + unsigned long long timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + bool loop=true; + while (loop) + { + g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd, true ); + + if ( msg <= CRCInput::RC_MaxRC ) + timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings +::TIMING_MENU]); + + switch ( msg ) + { + case CRCInput::RC_down: + if(selected < 2) // max entries + { + paintSlider(x + 10, y + hheight , brightness , BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS , false); + paintSlider(x + 10, y + hheight + mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + selected++; + switch (selected) { + case 0: + paintSlider(x+ 10, y+ hheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + break; + case 1: + paintSlider(x+ 10, y+ hheight+ mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + CVFD::getInstance()->setMode(CVFD::MODE_STANDBY); + break; + case 2: + frameBuffer->paintBoxRel(x, y+hheight+mheight*2+mheight/2, width, mheight, COL_MENUCONTENTSELECTED_PLUS_0, ROUND_RADIUS, 3); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*3+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENTSELECTED, 0, true); // UTF-8 + break; + } + } + break; + + case CRCInput::RC_up: + if (selected > 0) { + paintSlider(x + 10, y + hheight , brightness , BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS , false); + paintSlider(x + 10, y + hheight + mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + selected--; + switch (selected) { + case 0: + paintSlider(x+ 10, y+ hheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + break; + case 1: + paintSlider(x+10, y+hheight+mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + CVFD::getInstance()->setMode(CVFD::MODE_STANDBY); + frameBuffer->paintBoxRel(x, y+hheight+mheight*2+mheight/2, width, mheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*3+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENT, 0, true); // UTF-8 + break; + case 2: + break; + } + } + break; + + case CRCInput::RC_right: + switch (selected) { + case 0: + if (brightness < 15) { + brightness ++; + paintSlider(x+10, y+hheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + setVfd(); + } + break; + case 1: + if (brightnessstandby < 15) { + brightnessstandby ++; + paintSlider(x+10, y+hheight+mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + setVfd(); + } + break; + } + break; + + case CRCInput::RC_left: + switch (selected) { + case 0: + if (brightness > 0) { + brightness--; + paintSlider(x+10, y+hheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + setVfd(); + } + break; + case 1: + if (brightnessstandby > 0) { + brightnessstandby--; + paintSlider(x+10, y+hheight+mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, true); + setVfd(); + } + break; + } + break; + + case CRCInput::RC_home: + if ( ((brightness != brightness_alt) || (brightnessstandby != brightnessstandby_alt) ) && + (ShowLocalizedMessage(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::mbrCancel)) + break; + + brightness = brightness_alt; + brightnessstandby = brightnessstandby_alt; + setVfd(); + loop = false; + break; + + case CRCInput::RC_ok: + if (selected==2) { + brightness = DEFAULT_VFD_BRIGHTNESS; + brightnessstandby = DEFAULT_VFD_STANDBYBRIGHTNESS; + selected = 0; + setVfd(); + paint(); + break; + } + + case CRCInput::RC_timeout: + loop = false; + break; + + case CRCInput::RC_sat: + case CRCInput::RC_favorites: + break; + default: + if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all ) + { + loop = false; + res = menu_return::RETURN_EXIT_ALL; + } + } + } + + hide(); + + if(observer) + observer->changeNotify(name, NULL); + + return res; +} + +void CVfdControler::hide() +{ + frameBuffer->paintBackgroundBoxRel(x,y, width,height); +} + +void CVfdControler::paint() +{ + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + + frameBuffer->paintBoxRel(x,y, width,hheight, COL_MENUHEAD_PLUS_0, ROUND_RADIUS, 1);//round + frameBuffer->paintBoxRel(x,y+hheight, width,height-hheight, COL_MENUCONTENT_PLUS_0, ROUND_RADIUS, 2);//round + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->RenderString(x+10,y+hheight, width, g_Locale->getText(name), COL_MENUHEAD, 0, true); // UTF-8 + + paintSlider(x+10, y+hheight, brightness, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESS, true); + paintSlider(x+10, y+hheight+mheight, brightnessstandby, BRIGHTNESSFACTOR, LOCALE_LCDCONTROLER_BRIGHTNESSSTANDBY, false); + + frameBuffer->paintHLineRel(x+10, width-20, y+hheight+mheight*2+mheight/4, COL_MENUCONTENT_PLUS_3); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x+10, y+hheight+mheight*3+mheight/2, width, g_Locale->getText(LOCALE_OPTIONS_DEFAULT), COL_MENUCONTENT, 0, true); // UTF-8 +} + +void CVfdControler::paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected) +{ + int startx = 200; + char wert[5]; + + frameBuffer->paintBoxRel(x + startx, y, 120, mheight, COL_MENUCONTENT_PLUS_0); + frameBuffer->paintIcon("volumebody.raw", x + startx, y+2+mheight/4); + frameBuffer->paintIcon(selected ? "volumeslider2blue.raw" : "volumeslider2.raw", (int)(x + (startx+3)+(spos / factor)), y+mheight/4); + + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x, y+mheight, width, g_Locale->getText(text), COL_MENUCONTENT, 0, true); // UTF-8 + sprintf(wert, "%3d", spos); // UTF-8 encoded + frameBuffer->paintBoxRel(x + startx + 120 + 10, y, 50, mheight, COL_MENUCONTENT_PLUS_0); + g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(x + startx + 120 + 10, y+mheight, width, wert, COL_MENUCONTENT, 0, true); // UTF-8 +} diff --git a/src/gui/widget/vfdcontroler.h b/src/gui/widget/vfdcontroler.h new file mode 100644 index 000000000..bb17a11d1 --- /dev/null +++ b/src/gui/widget/vfdcontroler.h @@ -0,0 +1,74 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __vfdcontroler__ +#define __vfdcontroler__ + +#include +#include + +#include "menue.h" + +#include + +class CVfdControler : public CMenuTarget +{ + private: + CFrameBuffer *frameBuffer; + int x; + int y; + int width; + int height; + int hheight,mheight; // head/menu font height + + unsigned char brightness; + unsigned char brightnessstandby; + + neutrino_locale_t name; + + CChangeObserver* observer; + + void paint(); + void setVfd(); + void paintSlider(int x, int y, unsigned int spos, float factor, const neutrino_locale_t text, bool selected); + + public: + + CVfdControler(const neutrino_locale_t Name, CChangeObserver* Observer = NULL); + + void hide(); + int exec(CMenuTarget* parent, const std::string & actionKey); + +}; + + +#endif diff --git a/src/lcddisplay/lcddisplay.h b/src/lcddisplay/lcddisplay.h new file mode 100644 index 000000000..1904076c8 --- /dev/null +++ b/src/lcddisplay/lcddisplay.h @@ -0,0 +1,77 @@ +#ifndef __lcddisplay__ +#define __lcddisplay__ +/* + LCD-Daemon - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + baseroutines by Shadow_ + Homepage: http://dbox.cyberphoria.org/ + + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include + +typedef unsigned char raw_display_t[LCD_ROWS*8][LCD_COLS]; + +class CLCDDisplay +{ + private: + raw_display_t raw; + unsigned char lcd[LCD_ROWS][LCD_COLS]; + int fd, paused; + std::string iconBasePath; + bool available; + + public: + enum + { + PIXEL_ON = LCD_PIXEL_ON, + PIXEL_OFF = LCD_PIXEL_OFF, + PIXEL_INV = LCD_PIXEL_INV + }; + + CLCDDisplay(); + ~CLCDDisplay(); + + void pause(); + void resume(); + + void convert_data(); + void setIconBasePath(std::string bp){iconBasePath=bp;}; + bool isAvailable(); + + void update(); + + void draw_point(const int x, const int y, const int state); + void draw_line(const int x1, const int y1, const int x2, const int y2, const int state); + void draw_fill_rect (int left,int top,int right,int bottom,int state); + void draw_rectangle (int left,int top, int right, int bottom, int linestate,int fillstate); + void draw_polygon(int num_vertices, int *vertices, int state); + + bool paintIcon(std::string filename, int x, int y, bool invert); + void dump_screen(raw_display_t *screen); + void load_screen(const raw_display_t * const screen); + bool load_png(const char * const filename); +}; + + +#endif diff --git a/src/mymenu.h b/src/mymenu.h new file mode 100644 index 000000000..d528f633c --- /dev/null +++ b/src/mymenu.h @@ -0,0 +1,63 @@ +#define SCANMODE_OPTION_COUNT 3 +const CMenuOptionChooser::keyval SCANMODE_OPTIONS[SCANMODE_OPTION_COUNT] = +{ + { 0, LOCALE_EXTRA_SCAN_FULL}, + { 1, LOCALE_EXTRA_SCAN_FAST}, + { 2, LOCALE_EXTRA_TPSCAN} +}; + +#define HDD_NOISE_OPTION_COUNT 4 +const CMenuOptionChooser::keyval HDD_NOISE_OPTIONS[HDD_NOISE_OPTION_COUNT] = +{ + {0, LOCALE_OPTIONS_OFF}, + {128, LOCALE_HDD_SLOW}, + {190, LOCALE_HDD_MIDDLE}, + {254, LOCALE_HDD_FAST} +}; +#define HDD_FILESYS_OPTION_COUNT 3 +const CMenuOptionChooser::keyval HDD_FILESYS_OPTIONS[HDD_FILESYS_OPTION_COUNT] = +{ + {0, LOCALE_HDD_EXT3}, + {1, LOCALE_HDD_REISER}, + {2, LOCALE_OPTIONS_OFF} +}; +#define HDD_SLEEP_OPTION_COUNT 7 +const CMenuOptionChooser::keyval HDD_SLEEP_OPTIONS[HDD_SLEEP_OPTION_COUNT] = +{ + {0, LOCALE_OPTIONS_OFF}, + {12, LOCALE_HDD_1MIN}, + {60, LOCALE_HDD_5MIN}, + {120, LOCALE_HDD_10MIN}, + {240, LOCALE_HDD_20MIN}, + {241, LOCALE_HDD_30MIN}, + {242, LOCALE_HDD_60MIN} +}; +#if 0 +#define LOGO_OPTION_COUNT 3 +const CMenuOptionChooser::keyval LOGO_OPTIONS[LOGO_OPTION_COUNT] = +{ + {1, LOCALE_EXTRA_LOGO1}, + {2, LOCALE_EXTRA_LOGO2}, + {3, LOCALE_EXTRA_LOGO3} +}; +#endif +#define RF_CARRIER_OPTION_COUNT 4 +const CMenuOptionChooser::keyval RF_CARRIER_OPTIONS[RF_CARRIER_OPTION_COUNT] = +{ + {0, LOCALE_RF_CARRIER_4500}, + {1, LOCALE_RF_CARRIER_5500}, + {2, LOCALE_RF_CARRIER_6000}, + {3, LOCALE_RF_CARRIER_6500} +}; +#define OPTIONS_SOUTH0_NORTH1_OPTION_COUNT 2 +const CMenuOptionChooser::keyval OPTIONS_SOUTH0_NORTH1_OPTIONS[OPTIONS_SOUTH0_NORTH1_OPTION_COUNT] = +{ + {0, LOCALE_EXTRA_SOUTH}, + {1, LOCALE_EXTRA_NORTH} +}; +#define OPTIONS_EAST0_WEST1_OPTION_COUNT 2 +const CMenuOptionChooser::keyval OPTIONS_EAST0_WEST1_OPTIONS[OPTIONS_EAST0_WEST1_OPTION_COUNT] = +{ + {0, LOCALE_EXTRA_EAST}, + {1, LOCALE_EXTRA_WEST} +}; diff --git a/src/neutrino.cpp b/src/neutrino.cpp new file mode 100644 index 000000000..c3ef00f0d --- /dev/null +++ b/src/neutrino.cpp @@ -0,0 +1,4711 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + and some other guys + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define NEUTRINO_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +#include "global.h" +#include "neutrino.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gui/widget/colorchooser.h" +#include "gui/widget/menue.h" +#include "gui/widget/messagebox.h" +#include "gui/widget/hintbox.h" +#include "gui/widget/icons.h" +#include "gui/widget/lcdcontroler.h" +#include "gui/widget/vfdcontroler.h" +#include "gui/widget/rgbcsynccontroler.h" +#include "gui/widget/keychooser.h" +#include "gui/widget/stringinput.h" +#include "gui/widget/stringinput_ext.h" +#include "gui/widget/mountchooser.h" + +#include "gui/color.h" +#include "gui/customcolor.h" + +#include "gui/bedit/bouqueteditor_bouquets.h" +#include "gui/bouquetlist.h" +#include "gui/eventlist.h" +#include "gui/channellist.h" +#include "gui/screensetup.h" +#include "gui/pluginlist.h" +#include "gui/plugins.h" +#include "gui/infoviewer.h" +#include "gui/epgview.h" +#include "gui/epg_menu.h" +#include "gui/update.h" +#include "gui/scan.h" +#include "gui/favorites.h" +#include "gui/sleeptimer.h" +#include "gui/rc_lock.h" +#include "gui/timerlist.h" +#include "gui/alphasetup.h" +#include "gui/audioplayer.h" +#include "gui/imageinfo.h" +#include "gui/movieplayer.h" +#include "gui/nfs.h" +#include "gui/pictureviewer.h" +#include "gui/motorcontrol.h" +#include "gui/filebrowser.h" +#include "gui/scale.h" +#include "gui/cam_menu.h" +#include "gui/hdd_menu.h" +#include "gui/upnpbrowser.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +int old_b_id = -1; +CHintBox * reloadhintBox = 0; +uint32_t sec_timer; +int rf_dummy; +bool has_hdd; +#include "gui/infoclock.h" +#include "gui/dboxinfo.h" +CInfoClock *InfoClock; +int allow_flash = 1; +extern int was_record; +extern char current_timezone[50]; +Zapit_config zapitCfg; +char zapit_lat[20]; +char zapit_long[20]; +bool autoshift = false; +bool autoshift_delete = false; +uint32_t shift_timer; +uint32_t scrambled_timer; +char recDir[255]; +char timeshiftDir[255]; + +extern int tuxtxt_init(); +extern void tuxtxt_start(int tpid); +extern int tuxtxt_stop(); +extern void tuxtxt_close(); + +int dvbsub_init(); +int dvbsub_stop(); +int dvbsub_close(); +int dvbsub_start(int pid); +int dvbsub_pause(); + +//char current_volume; +extern int list_changed; + +static CScale * g_volscale; +//NEW +static pthread_t timer_thread; +void * timerd_main_thread(void *data); + +extern int streamts_stop; +void * streamts_main_thread(void *data); +static pthread_t stream_thread ; + +extern int zapit_ready; +static pthread_t zapit_thread ; +void * zapit_main_thread(void *data); +extern t_channel_id live_channel_id; //zapit +void setZapitConfig(Zapit_config * Cfg); +void getZapitConfig(Zapit_config *Cfg); + +void * nhttpd_main_thread(void *data); +static pthread_t nhttpd_thread ; + +//#define DISABLE_SECTIONSD //FIXME +extern int sectionsd_stop; +static pthread_t sections_thread; +void * sectionsd_main_thread(void *data); +//extern int sectionsd_scanning; // sectionsd.cpp +extern bool timeset; // sectionsd + +extern cVideo * videoDecoder; +extern cAudio * audioDecoder; +cPowerManager *powerManager; +cCpuFreqManager * cpuFreq; + +int prev_video_mode; + +int xres = 72; +int yres = 72; // TODO: no globals for that. + +void stop_daemons(bool stopall = true); +// uncomment if you want to have a "test" menue entry (rasc) + +#if HAVE_DVB_API_VERSION >= 3 +//#define __EXPERIMENTAL_CODE__ +#endif + +#ifdef __EXPERIMENTAL_CODE__ +#include "gui/ch_mosaic.h" +#endif + +#include "gui/audio_select.h" + +CAPIDChangeExec * APIDChanger; +CAudioSetupNotifier * audioSetupNotifier; +CBouquetList * bouquetList; // current list + +CBouquetList * TVbouquetList; +CBouquetList * TVsatList; +CBouquetList * TVfavList; +CBouquetList * TVallList; + +CBouquetList * RADIObouquetList; +CBouquetList * RADIOsatList; +CBouquetList * RADIOfavList; +CBouquetList * RADIOallList; + +CPlugins * g_PluginList; +CRemoteControl * g_RemoteControl; +SMSKeyInput * c_SMSKeyInput; +CMoviePlayerGui* moviePlayerGui; +CAudioSelectMenuHandler *audio_menu; +CPictureViewer * g_PicViewer; +CCAMMenuHandler * g_CamHandler; + +//int g_ChannelMode = LIST_MODE_PROV; + +// Globale Variablen - to use import global.h + +// I don't like globals, I would have hidden them in classes, +// but if you wanna do it so... ;) +bool parentallocked = false; +//static bool waitforshutdown = false; +static char **global_argv; + +//static CTimingSettingsNotifier timingsettingsnotifier; +CFontSizeNotifier fontsizenotifier; +CFanControlNotifier * funNotifier; + +extern const char * locale_real_names[]; /* #include */ +// USERMENU +const char* usermenu_button_def[SNeutrinoSettings::BUTTON_MAX]={"red","green","yellow","blue"}; + +CZapitClient::SatelliteList satList; + +CVCRControl::CDevice * recordingdevice = NULL; + +#if 0 +#define NEUTRINO_SETTINGS_FILE CONFIGDIR "/neutrino.conf" + +#define NEUTRINO_RECORDING_TIMER_SCRIPT CONFIGDIR "/recording.timer" +#define NEUTRINO_RECORDING_START_SCRIPT CONFIGDIR "/recording.start" +#define NEUTRINO_RECORDING_ENDED_SCRIPT CONFIGDIR "/recording.end" +#define NEUTRINO_ENTER_STANDBY_SCRIPT CONFIGDIR "/standby.on" +#define NEUTRINO_LEAVE_STANDBY_SCRIPT CONFIGDIR "/standby.off" + +#define NEUTRINO_SCAN_SETTINGS_FILE CONFIGDIR "/scan.conf" +#define NEUTRINO_PARENTALLOCKED_FILE DATADIR "/neutrino/.plocked" +#define NEUTRINO_SCAN_START_SCRIPT CONFIGDIR "/recording.start" +#endif + +int safe_mkdir(char * path) +{ + struct statfs s; + int ret = 0; + if(!strncmp(path, "/hdd", 4)) { + ret = statfs("/hdd", &s); + if((ret != 0) || (s.f_type == 0x72b6)) + ret = -1; + else + mkdir(path, 0755); + } else + mkdir(path, 0755); + return ret; +} + +static int check_dir(const char * newdir) +{ + if(strncmp(newdir, "/media/sda1/", 12) && strncmp(newdir, "/media/sdb1/", 12) && strncmp(newdir, "/mnt/", 5) && strncmp(newdir, "/tmp/", 5) && strncmp(newdir, "/media/", 7)) { + return 1; + } + + return 0; +} + +static void initGlobals(void) +{ + g_fontRenderer = NULL; + + g_RCInput = NULL; + //g_Controld = new CControldClient; + g_Timerd = NULL; + //g_Zapit = new CZapitClient; + g_RemoteControl = NULL; + + g_EpgData = NULL; + g_InfoViewer = NULL; + g_EventList = NULL; + + g_Locale = new CLocaleManager; + g_PluginList = NULL; + InfoClock = NULL; + g_CamHandler = NULL; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ CNeutrinoApp - Constructor, initialize g_fontRenderer + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +CNeutrinoApp::CNeutrinoApp() +: configfile('\t') +{ + standby_pressed_at.tv_sec = 0; + + frameBuffer = CFrameBuffer::getInstance(); + frameBuffer->setIconBasePath(DATADIR "/neutrino/icons/"); + + SetupFrameBuffer(); + + mode = mode_unknown; + channelList = NULL; + TVchannelList = NULL; + RADIOchannelList = NULL; + nextRecordingInfo = NULL; + skipShutdownTimer=false; + current_muted = 0; +} + +/*------------------------------------------------------------------------------------- +- CNeutrinoApp - Destructor - +-------------------------------------------------------------------------------------*/ +CNeutrinoApp::~CNeutrinoApp() +{ + if (channelList) + delete channelList; +} + +CNeutrinoApp* CNeutrinoApp::getInstance() +{ + static CNeutrinoApp* neutrinoApp = NULL; + + if(!neutrinoApp) { + neutrinoApp = new CNeutrinoApp(); + dprintf(DEBUG_DEBUG, "NeutrinoApp Instance created\n"); + } + return neutrinoApp; +} + + +/************************************************************************************** +* CNeutrinoApp - setup Color Sheme (Neutrino) * +**************************************************************************************/ +void CNeutrinoApp::setupColors_neutrino() +{ + g_settings.menu_Head_alpha = 0x00; + g_settings.menu_Head_red = 0x00; + g_settings.menu_Head_green = 0x0A; + g_settings.menu_Head_blue = 0x19; + + g_settings.menu_Head_Text_alpha = 0x00; + g_settings.menu_Head_Text_red = 0x5f; + g_settings.menu_Head_Text_green = 0x46; + g_settings.menu_Head_Text_blue = 0x00; + + g_settings.menu_Content_alpha = 0x14; + g_settings.menu_Content_red = 0x00; + g_settings.menu_Content_green = 0x0f; + g_settings.menu_Content_blue = 0x23; + + g_settings.menu_Content_Text_alpha = 0x00; + g_settings.menu_Content_Text_red = 0x64; + g_settings.menu_Content_Text_green = 0x64; + g_settings.menu_Content_Text_blue = 0x64; + + g_settings.menu_Content_Selected_alpha = 0x14; + g_settings.menu_Content_Selected_red = 0x19; + g_settings.menu_Content_Selected_green = 0x37; + g_settings.menu_Content_Selected_blue = 0x64; + + g_settings.menu_Content_Selected_Text_alpha = 0x00; + g_settings.menu_Content_Selected_Text_red = 0x00; + g_settings.menu_Content_Selected_Text_green = 0x00; + g_settings.menu_Content_Selected_Text_blue = 0x00; + + g_settings.menu_Content_inactive_alpha = 0x14; + g_settings.menu_Content_inactive_red = 0x00; + g_settings.menu_Content_inactive_green = 0x0f; + g_settings.menu_Content_inactive_blue = 0x23; + + g_settings.menu_Content_inactive_Text_alpha = 0x00; + g_settings.menu_Content_inactive_Text_red = 55; + g_settings.menu_Content_inactive_Text_green = 70; + g_settings.menu_Content_inactive_Text_blue = 85; + + g_settings.infobar_alpha = 0x14; + g_settings.infobar_red = 0x00; + g_settings.infobar_green = 0x0e; + g_settings.infobar_blue = 0x23; + + g_settings.infobar_Text_alpha = 0x00; + g_settings.infobar_Text_red = 0x64; + g_settings.infobar_Text_green = 0x64; + g_settings.infobar_Text_blue = 0x64; +} + +/************************************************************************************** +* * +* CNeutrinoApp - setup Color Sheme (classic) * +* * +**************************************************************************************/ +void CNeutrinoApp::setupColors_classic() +{ + g_settings.menu_Head_alpha = 20; + g_settings.menu_Head_red = 5; + g_settings.menu_Head_green = 10; + g_settings.menu_Head_blue = 60; + + g_settings.menu_Head_Text_alpha = 0; + g_settings.menu_Head_Text_red = 100; + g_settings.menu_Head_Text_green = 100; + g_settings.menu_Head_Text_blue = 100; + + g_settings.menu_Content_alpha = 20; + g_settings.menu_Content_red = 50; + g_settings.menu_Content_green = 50; + g_settings.menu_Content_blue = 50; + + g_settings.menu_Content_Text_alpha = 0; + g_settings.menu_Content_Text_red = 100; + g_settings.menu_Content_Text_green = 100; + g_settings.menu_Content_Text_blue = 100; + + g_settings.menu_Content_Selected_alpha = 20; + g_settings.menu_Content_Selected_red = 5; + g_settings.menu_Content_Selected_green = 10; + g_settings.menu_Content_Selected_blue = 60; + + g_settings.menu_Content_Selected_Text_alpha = 0; + g_settings.menu_Content_Selected_Text_red = 100; + g_settings.menu_Content_Selected_Text_green = 100; + g_settings.menu_Content_Selected_Text_blue = 100; + + g_settings.menu_Content_inactive_alpha = 20; + g_settings.menu_Content_inactive_red = 50; + g_settings.menu_Content_inactive_green = 50; + g_settings.menu_Content_inactive_blue = 50; + + g_settings.menu_Content_inactive_Text_alpha = 0; + g_settings.menu_Content_inactive_Text_red = 80; + g_settings.menu_Content_inactive_Text_green = 80; + g_settings.menu_Content_inactive_Text_blue = 80; + + g_settings.infobar_alpha = 20; + g_settings.infobar_red = 5; + g_settings.infobar_green = 10; + g_settings.infobar_blue = 60; + + g_settings.infobar_Text_alpha = 0; + g_settings.infobar_Text_red = 100; + g_settings.infobar_Text_green = 100; + g_settings.infobar_Text_blue = 100; +} +void CNeutrinoApp::setupColors_ru() +{ + + g_settings.infobar_Text_alpha=0; + g_settings.infobar_Text_blue=100; + g_settings.infobar_Text_green=100; + g_settings.infobar_Text_red=100; + + g_settings.infobar_alpha=0; + g_settings.infobar_blue=40; + g_settings.infobar_green=29; + g_settings.infobar_red=25; + + g_settings.menu_Content_Selected_Text_alpha=0; + g_settings.menu_Content_Selected_Text_blue=0; + g_settings.menu_Content_Selected_Text_green=0; + g_settings.menu_Content_Selected_Text_red=0; + + g_settings.menu_Content_Selected_alpha=0; + g_settings.menu_Content_Selected_blue=70; + g_settings.menu_Content_Selected_green=65; + g_settings.menu_Content_Selected_red=65; + + g_settings.menu_Content_Text_alpha=0; + g_settings.menu_Content_Text_blue=100; + g_settings.menu_Content_Text_green=100; + g_settings.menu_Content_Text_red=100; + + g_settings.menu_Content_alpha=0; + g_settings.menu_Content_blue=40; + g_settings.menu_Content_green=30; + g_settings.menu_Content_red=25; +; + g_settings.menu_Content_inactive_Text_alpha=0; + g_settings.menu_Content_inactive_Text_blue=100; + g_settings.menu_Content_inactive_Text_green=100; + g_settings.menu_Content_inactive_Text_red=100; + + g_settings.menu_Content_inactive_alpha=0; + g_settings.menu_Content_inactive_blue=40; + g_settings.menu_Content_inactive_green=30; + g_settings.menu_Content_inactive_red=25; + + g_settings.menu_Head_Text_alpha=0; + g_settings.menu_Head_Text_blue=0; + g_settings.menu_Head_Text_green=70; + g_settings.menu_Head_Text_red=95; + + g_settings.menu_Head_alpha=0; + g_settings.menu_Head_blue=30; + g_settings.menu_Head_green=20; + g_settings.menu_Head_red=15; +} +void CNeutrinoApp::setupColors_red() +{ + g_settings.infobar_Text_alpha=0; + g_settings.infobar_Text_blue=100; + g_settings.infobar_Text_green=100; + g_settings.infobar_Text_red=100; + + g_settings.infobar_alpha=20; + g_settings.infobar_blue=0; + g_settings.infobar_green=14; + g_settings.infobar_red=40; + + g_settings.menu_Content_Selected_Text_alpha=0; + g_settings.menu_Content_Selected_Text_blue=0; + g_settings.menu_Content_Selected_Text_green=0; + g_settings.menu_Content_Selected_Text_red=0; + + g_settings.menu_Content_Selected_alpha=20; + g_settings.menu_Content_Selected_blue=100; + g_settings.menu_Content_Selected_green=100; + g_settings.menu_Content_Selected_red=100; + + g_settings.menu_Content_Text_alpha=0; + g_settings.menu_Content_Text_blue=100; + g_settings.menu_Content_Text_green=100; + g_settings.menu_Content_Text_red=100; + + g_settings.menu_Content_inactive_Text_alpha=0; + g_settings.menu_Content_inactive_Text_blue=35; + g_settings.menu_Content_inactive_Text_green=85; + g_settings.menu_Content_inactive_Text_red=85; + + g_settings.menu_Content_inactive_alpha=20; + g_settings.menu_Content_inactive_blue=0; + g_settings.menu_Content_inactive_green=15; + g_settings.menu_Content_inactive_red=35; + + g_settings.menu_Content_alpha=20; + g_settings.menu_Content_blue=0; + g_settings.menu_Content_green=15; + g_settings.menu_Content_red=40; + + g_settings.menu_Head_Text_alpha=0; + g_settings.menu_Head_Text_blue=0; + g_settings.menu_Head_Text_green=70; + g_settings.menu_Head_Text_red=95; + + g_settings.menu_Head_alpha=0; + g_settings.menu_Head_blue=0; + g_settings.menu_Head_green=10; + g_settings.menu_Head_red=40; +} + +/************************************************************************************** +* * +* CNeutrinoApp - setup Color Sheme (darkblue) * +* * +**************************************************************************************/ +void CNeutrinoApp::setupColors_dblue() +{ + g_settings.menu_Head_alpha = 0; + g_settings.menu_Head_red = 0; + g_settings.menu_Head_green = 0; + g_settings.menu_Head_blue = 50; + + g_settings.menu_Head_Text_alpha = 0; + g_settings.menu_Head_Text_red = 95; + g_settings.menu_Head_Text_green = 100; + g_settings.menu_Head_Text_blue = 100; + + g_settings.menu_Content_alpha = 20; + g_settings.menu_Content_red = 0; + g_settings.menu_Content_green = 0; + g_settings.menu_Content_blue = 20; + + g_settings.menu_Content_Text_alpha = 0; + g_settings.menu_Content_Text_red = 100; + g_settings.menu_Content_Text_green = 100; + g_settings.menu_Content_Text_blue = 100; + + g_settings.menu_Content_Selected_alpha = 15; + g_settings.menu_Content_Selected_red = 0; + g_settings.menu_Content_Selected_green = 65; + g_settings.menu_Content_Selected_blue = 0; + + g_settings.menu_Content_Selected_Text_alpha = 0; + g_settings.menu_Content_Selected_Text_red = 0; + g_settings.menu_Content_Selected_Text_green = 0; + g_settings.menu_Content_Selected_Text_blue = 0; + + g_settings.menu_Content_inactive_alpha = 20; + g_settings.menu_Content_inactive_red = 0; + g_settings.menu_Content_inactive_green = 0; + g_settings.menu_Content_inactive_blue = 15; + + g_settings.menu_Content_inactive_Text_alpha = 0; + g_settings.menu_Content_inactive_Text_red = 55; + g_settings.menu_Content_inactive_Text_green = 70; + g_settings.menu_Content_inactive_Text_blue = 85; + + g_settings.infobar_alpha = 20; + g_settings.infobar_red = 0; + g_settings.infobar_green = 0; + g_settings.infobar_blue = 20; + + g_settings.infobar_Text_alpha = 0; + g_settings.infobar_Text_red = 100; + g_settings.infobar_Text_green = 100; + g_settings.infobar_Text_blue = 100; +} + +/************************************************************************************** +* * +* CNeutrinoApp - setup Color Sheme (dvb2000) * +* * +**************************************************************************************/ +void CNeutrinoApp::setupColors_dvb2k() +{ + g_settings.menu_Head_alpha = 0; + g_settings.menu_Head_red = 25; + g_settings.menu_Head_green = 25; + g_settings.menu_Head_blue = 25; + + g_settings.menu_Head_Text_alpha = 0; + g_settings.menu_Head_Text_red = 100; + g_settings.menu_Head_Text_green = 100; + g_settings.menu_Head_Text_blue = 0; + + g_settings.menu_Content_alpha = 0; + g_settings.menu_Content_red = 0; + g_settings.menu_Content_green = 20; + g_settings.menu_Content_blue = 0; + + g_settings.menu_Content_Text_alpha = 0; + g_settings.menu_Content_Text_red = 100; + g_settings.menu_Content_Text_green = 100; + g_settings.menu_Content_Text_blue = 100; + + g_settings.menu_Content_Selected_alpha = 0; + g_settings.menu_Content_Selected_red = 100; + g_settings.menu_Content_Selected_green = 100; + g_settings.menu_Content_Selected_blue = 100; + + g_settings.menu_Content_Selected_Text_alpha = 0; + g_settings.menu_Content_Selected_Text_red = 0; + g_settings.menu_Content_Selected_Text_green = 0; + g_settings.menu_Content_Selected_Text_blue = 0; + + g_settings.menu_Content_inactive_alpha = 20; + g_settings.menu_Content_inactive_red = 0; + g_settings.menu_Content_inactive_green = 25; + g_settings.menu_Content_inactive_blue = 0; + + g_settings.menu_Content_inactive_Text_alpha = 0; + g_settings.menu_Content_inactive_Text_red = 100; + g_settings.menu_Content_inactive_Text_green = 100; + g_settings.menu_Content_inactive_Text_blue = 0; + + g_settings.infobar_alpha = 5; + g_settings.infobar_red = 0; + g_settings.infobar_green = 19; + g_settings.infobar_blue = 0; + + g_settings.infobar_Text_alpha = 0; + g_settings.infobar_Text_red = 100; + g_settings.infobar_Text_green = 100; + g_settings.infobar_Text_blue = 100; +} + +#define FONT_STYLE_REGULAR 0 +#define FONT_STYLE_BOLD 1 +#define FONT_STYLE_ITALIC 2 + +extern font_sizes_groups_struct font_sizes_groups[]; +extern font_sizes_struct neutrino_font[]; + +const font_sizes_struct signal_font = {LOCALE_FONTSIZE_INFOBAR_SMALL , 14, FONT_STYLE_REGULAR, 1}; + +typedef struct lcd_setting_t +{ + const char * const name; + const unsigned int default_value; +} lcd_setting_struct_t; + +const lcd_setting_struct_t lcd_setting[LCD_SETTING_COUNT] = +{ + {"lcd_brightness" , DEFAULT_VFD_BRIGHTNESS }, + {"lcd_standbybrightness", DEFAULT_VFD_STANDBYBRIGHTNESS}, + {"lcd_contrast" , DEFAULT_LCD_CONTRAST }, + {"lcd_power" , DEFAULT_LCD_POWER }, + {"lcd_inverse" , DEFAULT_LCD_INVERSE }, + {"lcd_show_volume" , DEFAULT_LCD_SHOW_VOLUME }, + {"lcd_autodimm" , DEFAULT_LCD_AUTODIMM } +}; + +/************************************************************************************** +* CNeutrinoApp - loadSetup, load the application-settings * +**************************************************************************************/ +int CNeutrinoApp::loadSetup(const char * fname) +{ + char cfg_key[81]; + int erg = 0; + + configfile.clear(); + //settings laden - und dabei Defaults setzen! + if(!configfile.loadConfig(fname)) { + //file existiert nicht + erg = 1; + } + std::ifstream checkParentallocked(NEUTRINO_PARENTALLOCKED_FILE); + if(checkParentallocked) { + parentallocked = true; + checkParentallocked.close(); + } + //video + g_settings.video_Mode = configfile.getInt32("video_Mode", 7); // default 720p/50HZ + prev_video_mode = g_settings.video_Mode; + g_settings.analog_mode1 = configfile.getInt32("analog_mode1", 0); // default RGB + g_settings.analog_mode2 = configfile.getInt32("analog_mode2", 0); // default RGB + + g_settings.video_Format = configfile.getInt32("video_Format", 3); + g_settings.video_43mode = configfile.getInt32("video_43mode", 0); + g_settings.current_volume = configfile.getInt32("current_volume", 100); + g_settings.channel_mode = configfile.getInt32("channel_mode", LIST_MODE_PROV); + g_settings.video_csync = configfile.getInt32( "video_csync", 0 ); + + g_settings.fan_speed = configfile.getInt32( "fan_speed", 1); + if(g_settings.fan_speed < 1) g_settings.fan_speed = 1;//FIXME disable OFF + + g_settings.srs_enable = configfile.getInt32( "srs_enable", 0); + g_settings.srs_algo = configfile.getInt32( "srs_algo", 1); + g_settings.srs_ref_volume = configfile.getInt32( "srs_ref_volume", 40);//FIXME + g_settings.srs_nmgr_enable = configfile.getInt32( "srs_nmgr_enable", 0); + g_settings.hdmi_dd = configfile.getInt32( "hdmi_dd", 0); + g_settings.spdif_dd = configfile.getInt32( "spdif_dd", 1); + g_settings.avsync = configfile.getInt32( "avsync", 1); + g_settings.clockrec = configfile.getInt32( "clockrec", 1); + g_settings.video_dbdr = configfile.getInt32("video_dbdr", 2); + + for(int i = 0; i < VIDEOMENU_VIDEOMODE_OPTION_COUNT; i++) { + sprintf(cfg_key, "enabled_video_mode_%d", i); + g_settings.enabled_video_modes[i] = configfile.getInt32(cfg_key, 1); + } + g_settings.cpufreq = configfile.getInt32("cpufreq", 0); + g_settings.standby_cpufreq = configfile.getInt32("standby_cpufreq", 100); + g_settings.rounded_corners = 1; //FIXME +//FIXME + g_settings.cpufreq = 0; + g_settings.standby_cpufreq = 50; + + g_settings.make_hd_list = configfile.getInt32("make_hd_list", 1); + //fb-alphawerte für gtx + g_settings.gtx_alpha1 = configfile.getInt32( "gtx_alpha1", 255); + g_settings.gtx_alpha2 = configfile.getInt32( "gtx_alpha2", 1); + + //misc + g_settings.power_standby = configfile.getInt32( "power_standby", 0); + g_settings.rotor_swap = configfile.getInt32( "rotor_swap", 0); + g_settings.emlog = configfile.getInt32( "emlog", 0); + + g_settings.hdd_fs = configfile.getInt32( "hdd_fs", 0); + g_settings.hdd_sleep = configfile.getInt32( "hdd_sleep", 120); + g_settings.hdd_noise = configfile.getInt32( "hdd_noise", 254); + g_settings.shutdown_real = configfile.getBool("shutdown_real" , false ); + g_settings.shutdown_real_rcdelay = configfile.getBool("shutdown_real_rcdelay", false ); + strcpy(g_settings.shutdown_count, configfile.getString("shutdown_count","0").c_str()); + g_settings.infobar_sat_display = configfile.getBool("infobar_sat_display" , true ); + g_settings.infobar_subchan_disp_pos = configfile.getInt32("infobar_subchan_disp_pos" , 0 ); + + //audio + g_settings.audio_AnalogMode = configfile.getInt32( "audio_AnalogMode", 0 ); + g_settings.audio_DolbyDigital = configfile.getBool("audio_DolbyDigital" , false); + + g_settings.audio_avs_Control = false; + g_settings.audio_english = configfile.getInt32( "audio_english", 0 ); + g_settings.zap_cycle = configfile.getInt32( "zap_cycle", 1 ); + g_settings.sms_channel = configfile.getInt32( "sms_channel", 0 ); + strcpy( g_settings.audio_PCMOffset, configfile.getString( "audio_PCMOffset", "0" ).c_str() ); + + //vcr + g_settings.vcr_AutoSwitch = configfile.getBool("vcr_AutoSwitch" , true ); + + //language + strcpy(g_settings.language, configfile.getString("language", "").c_str()); + strcpy(g_settings.timezone, configfile.getString("timezone", "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Vienna").c_str()); + //epg dir + g_settings.epg_cache = configfile.getString("epg_cache_time", "14"); + g_settings.epg_extendedcache = configfile.getString("epg_extendedcache_time", "360"); + g_settings.epg_old_events = configfile.getString("epg_old_events", "1"); + g_settings.epg_max_events = configfile.getString("epg_max_events", "30000"); + g_settings.epg_dir = configfile.getString("epg_dir", "/media/sda1/epg"); + // NTP-Server for sectionsd + g_settings.network_ntpserver = configfile.getString("network_ntpserver", "time.fu-berlin.de"); + g_settings.network_ntprefresh = configfile.getString("network_ntprefresh", "30" ); + g_settings.network_ntpenable = configfile.getBool("network_ntpenable", false); + + g_settings.epg_save = configfile.getBool("epg_save", false); + + //widget settings + //FIXME not work yet g_settings.widget_fade = configfile.getBool("widget_fade" , false ); + g_settings.widget_fade = false; + + //colors (neutrino defaultcolors) + g_settings.menu_Head_alpha = configfile.getInt32( "menu_Head_alpha", 0x00 ); + g_settings.menu_Head_red = configfile.getInt32( "menu_Head_red", 0x00 ); + g_settings.menu_Head_green = configfile.getInt32( "menu_Head_green", 0x0A ); + g_settings.menu_Head_blue = configfile.getInt32( "menu_Head_blue", 0x19 ); + + g_settings.menu_Head_Text_alpha = configfile.getInt32( "menu_Head_Text_alpha", 0x00 ); + g_settings.menu_Head_Text_red = configfile.getInt32( "menu_Head_Text_red", 0x5f ); + g_settings.menu_Head_Text_green = configfile.getInt32( "menu_Head_Text_green", 0x46 ); + g_settings.menu_Head_Text_blue = configfile.getInt32( "menu_Head_Text_blue", 0x00 ); + + g_settings.menu_Content_alpha = configfile.getInt32( "menu_Content_alpha", 0x14 ); + g_settings.menu_Content_red = configfile.getInt32( "menu_Content_red", 0x00 ); + g_settings.menu_Content_green = configfile.getInt32( "menu_Content_green", 0x0f ); + g_settings.menu_Content_blue = configfile.getInt32( "menu_Content_blue", 0x23 ); + g_settings.menu_Content_Text_alpha = configfile.getInt32( "menu_Content_Text_alpha", 0x00 ); + g_settings.menu_Content_Text_red = configfile.getInt32( "menu_Content_Text_red", 0x64 ); + g_settings.menu_Content_Text_green = configfile.getInt32( "menu_Content_Text_green", 0x64 ); + g_settings.menu_Content_Text_blue = configfile.getInt32( "menu_Content_Text_blue", 0x64 ); + + g_settings.menu_Content_Selected_alpha = configfile.getInt32( "menu_Content_Selected_alpha", 0x14 ); + g_settings.menu_Content_Selected_red = configfile.getInt32( "menu_Content_Selected_red", 0x19 ); + g_settings.menu_Content_Selected_green = configfile.getInt32( "menu_Content_Selected_green", 0x37 ); + g_settings.menu_Content_Selected_blue = configfile.getInt32( "menu_Content_Selected_blue", 0x64 ); + + g_settings.menu_Content_Selected_Text_alpha = configfile.getInt32( "menu_Content_Selected_Text_alpha", 0x00 ); + g_settings.menu_Content_Selected_Text_red = configfile.getInt32( "menu_Content_Selected_Text_red", 0x00 ); + g_settings.menu_Content_Selected_Text_green = configfile.getInt32( "menu_Content_Selected_Text_green", 0x00 ); + g_settings.menu_Content_Selected_Text_blue = configfile.getInt32( "menu_Content_Selected_Text_blue", 0x00 ); + + g_settings.menu_Content_inactive_alpha = configfile.getInt32( "menu_Content_inactive_alpha", 0x14 ); + g_settings.menu_Content_inactive_red = configfile.getInt32( "menu_Content_inactive_red", 0x00 ); + g_settings.menu_Content_inactive_green = configfile.getInt32( "menu_Content_inactive_green", 0x0f ); + g_settings.menu_Content_inactive_blue = configfile.getInt32( "menu_Content_inactive_blue", 0x23 ); + + g_settings.menu_Content_inactive_Text_alpha = configfile.getInt32( "menu_Content_inactive_Text_alpha", 0x00 ); + g_settings.menu_Content_inactive_Text_red = configfile.getInt32( "menu_Content_inactive_Text_red", 55 ); + g_settings.menu_Content_inactive_Text_green = configfile.getInt32( "menu_Content_inactive_Text_green", 70 ); + g_settings.menu_Content_inactive_Text_blue = configfile.getInt32( "menu_Content_inactive_Text_blue", 85 ); + + g_settings.infobar_alpha = configfile.getInt32( "infobar_alpha", 0x14 ); + g_settings.infobar_red = configfile.getInt32( "infobar_red", 0x00 ); + g_settings.infobar_green = configfile.getInt32( "infobar_green", 0x0e ); + g_settings.infobar_blue = configfile.getInt32( "infobar_blue", 0x23 ); + + g_settings.infobar_Text_alpha = configfile.getInt32( "infobar_Text_alpha", 0x00 ); + g_settings.infobar_Text_red = configfile.getInt32( "infobar_Text_red", 0x64 ); + g_settings.infobar_Text_green = configfile.getInt32( "infobar_Text_green", 0x64 ); + g_settings.infobar_Text_blue = configfile.getInt32( "infobar_Text_blue", 0x64 ); + + //network + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) { + sprintf(cfg_key, "network_nfs_ip_%d", i); + g_settings.network_nfs_ip[i] = configfile.getString(cfg_key, ""); + sprintf(cfg_key, "network_nfs_dir_%d", i); + strcpy( g_settings.network_nfs_dir[i], configfile.getString( cfg_key, "" ).c_str() ); + sprintf(cfg_key, "network_nfs_local_dir_%d", i); + strcpy( g_settings.network_nfs_local_dir[i], configfile.getString( cfg_key, "" ).c_str() ); + sprintf(cfg_key, "network_nfs_automount_%d", i); + g_settings.network_nfs_automount[i] = configfile.getInt32( cfg_key, 0); + sprintf(cfg_key, "network_nfs_type_%d", i); + g_settings.network_nfs_type[i] = configfile.getInt32( cfg_key, 0); + sprintf(cfg_key,"network_nfs_username_%d", i); + strcpy( g_settings.network_nfs_username[i], configfile.getString( cfg_key, "" ).c_str() ); + sprintf(cfg_key, "network_nfs_password_%d", i); + strcpy( g_settings.network_nfs_password[i], configfile.getString( cfg_key, "" ).c_str() ); + sprintf(cfg_key, "network_nfs_mount_options1_%d", i); + strcpy( g_settings.network_nfs_mount_options1[i], configfile.getString( cfg_key, "ro,soft,udp" ).c_str() ); + sprintf(cfg_key, "network_nfs_mount_options2_%d", i); + strcpy( g_settings.network_nfs_mount_options2[i], configfile.getString( cfg_key, "nolock,rsize=8192,wsize=8192" ).c_str() ); + sprintf(cfg_key, "network_nfs_mac_%d", i); + strcpy( g_settings.network_nfs_mac[i], configfile.getString( cfg_key, "11:22:33:44:55:66").c_str() ); + } + strcpy( g_settings.network_nfs_audioplayerdir, configfile.getString( "network_nfs_audioplayerdir", "/media/sda1/music" ).c_str() ); + strcpy( g_settings.network_nfs_picturedir, configfile.getString( "network_nfs_picturedir", "/media/sda1/pictures" ).c_str() ); + strcpy( g_settings.network_nfs_moviedir, configfile.getString( "network_nfs_moviedir", "/media/sda1/movies" ).c_str() ); + strcpy( g_settings.network_nfs_recordingdir, configfile.getString( "network_nfs_recordingdir", "/media/sda1/movies" ).c_str() ); + strcpy( g_settings.timeshiftdir, configfile.getString( "timeshiftdir", "" ).c_str() ); + + g_settings.temp_timeshift = configfile.getInt32( "temp_timeshift", 0 ); + g_settings.auto_timeshift = configfile.getInt32( "auto_timeshift", 0 ); + g_settings.auto_delete = configfile.getInt32( "auto_delete", 1 ); + + if(strlen(g_settings.timeshiftdir) == 0) { + sprintf(timeshiftDir, "%s/.timeshift", g_settings.network_nfs_recordingdir); + safe_mkdir(timeshiftDir); + } else { + if(strcmp(g_settings.timeshiftdir, g_settings.network_nfs_recordingdir)) + strncpy(timeshiftDir, g_settings.timeshiftdir, sizeof(timeshiftDir)); + else + sprintf(timeshiftDir, "%s/.timeshift", g_settings.network_nfs_recordingdir); + } +printf("***************************** rec dir %s timeshift dir %s\n", g_settings.network_nfs_recordingdir, timeshiftDir); + + if(g_settings.auto_delete) { + if(strcmp(g_settings.timeshiftdir, g_settings.network_nfs_recordingdir)) { + char buf[512]; + sprintf(buf, "rm -f %s/*_temp.ts %s/*_temp.xml &", timeshiftDir, timeshiftDir); + system(buf); + } + } + g_settings.record_hours = configfile.getInt32( "record_hours", 4 ); + g_settings.filesystem_is_utf8 = configfile.getBool("filesystem_is_utf8" , true ); + + //recording (server + vcr) + g_settings.recording_type = configfile.getInt32("recording_type", RECORDING_FILE); + g_settings.recording_stopplayback = configfile.getBool("recording_stopplayback" , false); + g_settings.recording_stopsectionsd = configfile.getBool("recording_stopsectionsd" , true ); + g_settings.recording_server_ip = configfile.getString("recording_server_ip", "127.0.0.1"); + strcpy( g_settings.recording_server_port, configfile.getString( "recording_server_port", "4000").c_str() ); + g_settings.recording_server_wakeup = configfile.getInt32( "recording_server_wakeup", 0 ); + strcpy( g_settings.recording_server_mac, configfile.getString( "recording_server_mac", "11:22:33:44:55:66").c_str() ); + g_settings.recording_vcr_no_scart = configfile.getInt32( "recording_vcr_no_scart", false); + strcpy( g_settings.recording_splitsize, configfile.getString( "recording_splitsize", "2048").c_str() ); + g_settings.recording_use_o_sync = configfile.getBool("recordingmenu.use_o_sync" , false); + g_settings.recording_use_fdatasync = configfile.getBool("recordingmenu.use_fdatasync" , false); + g_settings.recording_audio_pids_default = configfile.getInt32("recording_audio_pids_default", TIMERD_APIDS_STD | TIMERD_APIDS_AC3); + g_settings.recording_zap_on_announce = configfile.getBool("recording_zap_on_announce" , false); + + g_settings.recording_stream_vtxt_pid = configfile.getBool("recordingmenu.stream_vtxt_pid" , false); + g_settings.recording_stream_pmt_pid = configfile.getBool("recordingmenu.stream_pmt_pid" , false); + strcpy( g_settings.recording_ringbuffers, configfile.getString( "recordingmenu.ringbuffers", "20").c_str() ); + g_settings.recording_choose_direct_rec_dir = configfile.getInt32( "recording_choose_direct_rec_dir", 0 ); + g_settings.recording_epg_for_filename = configfile.getBool("recording_epg_for_filename" , true); + g_settings.recording_save_in_channeldir = configfile.getBool("recording_save_in_channeldir" , false); + g_settings.recording_in_spts_mode = true; + //streaming (server) + g_settings.streaming_type = configfile.getInt32( "streaming_type", 0 ); + g_settings.streaming_server_ip = configfile.getString("streaming_server_ip", "10.10.10.10"); + strcpy( g_settings.streaming_server_port, configfile.getString( "streaming_server_port", "8080").c_str() ); + strcpy( g_settings.streaming_server_cddrive, configfile.getString("streaming_server_cddrive", "D:").c_str() ); + strcpy( g_settings.streaming_videorate, configfile.getString("streaming_videorate", "1000").c_str() ); + strcpy( g_settings.streaming_audiorate, configfile.getString("streaming_audiorate", "192").c_str() ); + strcpy( g_settings.streaming_server_startdir, configfile.getString("streaming_server_startdir", "C:/Movies").c_str() ); + g_settings.streaming_transcode_audio = configfile.getInt32( "streaming_transcode_audio", 0 ); + g_settings.streaming_force_transcode_video = configfile.getInt32( "streaming_force_transcode_video", 0 ); + g_settings.streaming_transcode_video_codec = configfile.getInt32( "streaming_transcode_video_codec", 0 ); + g_settings.streaming_force_avi_rawaudio = configfile.getInt32( "streaming_force_avi_rawaudio", 0 ); + g_settings.streaming_resolution = configfile.getInt32( "streaming_resolution", 0 ); + + // default plugin for movieplayer + g_settings.movieplayer_plugin = configfile.getString( "movieplayer_plugin", "Teletext" ); + g_settings.onekey_plugin = configfile.getString( "onekey_plugin", "noplugin" ); + + //rc-key configuration + g_settings.key_tvradio_mode = configfile.getInt32( "key_tvradio_mode", CRCInput::RC_nokey ); + + g_settings.key_channelList_pageup = configfile.getInt32( "key_channelList_pageup", CRCInput::RC_page_up ); + g_settings.key_channelList_pagedown = configfile.getInt32( "key_channelList_pagedown", CRCInput::RC_page_down ); + g_settings.key_channelList_cancel = configfile.getInt32( "key_channelList_cancel", CRCInput::RC_home ); + g_settings.key_channelList_sort = configfile.getInt32( "key_channelList_sort", CRCInput::RC_blue ); + g_settings.key_channelList_addrecord = configfile.getInt32( "key_channelList_addrecord", CRCInput::RC_nokey ); + g_settings.key_channelList_addremind = configfile.getInt32( "key_channelList_addremind", CRCInput::RC_nokey ); + + g_settings.key_list_start = configfile.getInt32( "key_list_start", CRCInput::RC_nokey ); + g_settings.key_list_end = configfile.getInt32( "key_list_end", CRCInput::RC_nokey ); + g_settings.menu_left_exit = configfile.getInt32( "menu_left_exit", 0 ); + + g_settings.audio_run_player = configfile.getInt32( "audio_run_player", 1 ); + g_settings.key_click = configfile.getInt32( "key_click", 1 ); + g_settings.timeshift_pause = configfile.getInt32( "timeshift_pause", 1 ); + g_settings.mpkey_rewind = configfile.getInt32( "mpkey.rewind", CRCInput::RC_rewind ); + g_settings.mpkey_forward = configfile.getInt32( "mpkey.forward", CRCInput::RC_forward ); + g_settings.mpkey_pause = configfile.getInt32( "mpkey.pause", CRCInput::RC_pause ); + g_settings.mpkey_stop = configfile.getInt32( "mpkey.stop", CRCInput::RC_stop ); + g_settings.mpkey_play = configfile.getInt32( "mpkey.play", CRCInput::RC_play ); + g_settings.mpkey_audio = configfile.getInt32( "mpkey.audio", CRCInput::RC_green ); + g_settings.mpkey_time = configfile.getInt32( "mpkey.time", CRCInput::RC_setup ); + g_settings.mpkey_bookmark = configfile.getInt32( "mpkey.bookmark", CRCInput::RC_blue ); + g_settings.mpkey_plugin = configfile.getInt32( "mpkey.plugin", CRCInput::RC_red ); + g_settings.key_timeshift = configfile.getInt32( "key_timeshift", CRCInput::RC_pause ); + g_settings.key_plugin = configfile.getInt32( "key_plugin", CRCInput::RC_nokey ); + g_settings.key_unlock = configfile.getInt32( "key_unlock", CRCInput::RC_setup ); +//printf("get: key_unlock =============== %d\n", g_settings.key_unlock); + +//rfmod + g_settings.rf_subcarrier = configfile.getInt32( "rf_subcarrier", 1); + g_settings.rf_soundenable = configfile.getInt32( "rf_soundenable", 0); + g_settings.rf_channel = configfile.getInt32( "rf_channel", 36); + g_settings.rf_finetune = configfile.getInt32( "rf_finetune", 0); + g_settings.rf_standby = configfile.getInt32( "rf_standby", 0); + + g_settings.key_quickzap_up = configfile.getInt32( "key_quickzap_up", CRCInput::RC_up ); + g_settings.key_quickzap_down = configfile.getInt32( "key_quickzap_down", CRCInput::RC_down ); + g_settings.key_subchannel_up = configfile.getInt32( "key_subchannel_up", CRCInput::RC_right ); + g_settings.key_subchannel_down = configfile.getInt32( "key_subchannel_down", CRCInput::RC_left ); + g_settings.key_zaphistory = configfile.getInt32( "key_zaphistory", CRCInput::RC_home ); + g_settings.key_lastchannel = configfile.getInt32( "key_lastchannel", CRCInput::RC_0 ); + g_settings.cacheTXT = configfile.getInt32( "cacheTXT", 0); + g_settings.minimode = configfile.getInt32( "minimode", 0); + g_settings.mode_clock = configfile.getInt32( "mode_clock", 0); + g_settings.virtual_zap_mode = configfile.getBool("virtual_zap_mode" , false); + g_settings.spectrum = configfile.getBool("spectrum" , false); + g_settings.channellist_epgtext_align_right = configfile.getBool("channellist_epgtext_align_right" , false); + g_settings.channellist_extended = configfile.getBool("channellist_extended" , true); + //screen configuration + g_settings.screen_StartX = configfile.getInt32( "screen_StartX", 37 ); + g_settings.screen_StartY = configfile.getInt32( "screen_StartY", 23 ); + g_settings.screen_EndX = configfile.getInt32( "screen_EndX", 668 ); + g_settings.screen_EndY = configfile.getInt32( "screen_EndY", 555 ); + g_settings.screen_width = configfile.getInt32("screen_width", 0); + g_settings.screen_height = configfile.getInt32("screen_height", 0); + + g_settings.bigFonts = configfile.getInt32("bigFonts", 0); + + strcpy(g_settings.repeat_blocker, configfile.getString("repeat_blocker", "150").c_str()); + strcpy(g_settings.repeat_genericblocker, configfile.getString("repeat_genericblocker", "100").c_str()); + g_settings.key_bouquet_up = configfile.getInt32( "key_bouquet_up", CRCInput::RC_right); + g_settings.key_bouquet_down = configfile.getInt32( "key_bouquet_down", CRCInput::RC_left); + g_settings.audiochannel_up_down_enable = configfile.getBool("audiochannel_up_down_enable", false); + + //Software-update + g_settings.softupdate_mode = configfile.getInt32( "softupdate_mode", 1 ); + + strcpy(g_settings.softupdate_url_file, configfile.getString("softupdate_url_file", "/var/etc/update.urls").c_str()); + strcpy(g_settings.softupdate_proxyserver, configfile.getString("softupdate_proxyserver", "" ).c_str()); + strcpy(g_settings.softupdate_proxyusername, configfile.getString("softupdate_proxyusername", "" ).c_str()); + strcpy(g_settings.softupdate_proxypassword, configfile.getString("softupdate_proxypassword", "" ).c_str()); +// + strcpy( g_settings.font_file, configfile.getString( "font_file", FONTDIR"/neutrino.ttf" ).c_str() ); + strcpy( g_settings.update_dir, configfile.getString( "update_dir", "/tmp" ).c_str() ); + //BouquetHandling + g_settings.bouquetlist_mode = configfile.getInt32( "bouquetlist_mode", 0 ); + + // parentallock + if (!parentallocked) { + g_settings.parentallock_prompt = configfile.getInt32( "parentallock_prompt", 0 ); + g_settings.parentallock_lockage = configfile.getInt32( "parentallock_lockage", 12 ); + } else { + g_settings.parentallock_prompt = 3; + g_settings.parentallock_lockage = 18; + } + strcpy( g_settings.parentallock_pincode, configfile.getString( "parentallock_pincode", "0000" ).c_str() ); + + for (int i = 0; i < TIMING_SETTING_COUNT; i++) + g_settings.timing[i] = configfile.getInt32(locale_real_names[timing_setting_name[i]], default_timing[i]); + + for (int i = 0; i < LCD_SETTING_COUNT; i++) + g_settings.lcd_setting[i] = configfile.getInt32(lcd_setting[i].name, lcd_setting[i].default_value); + strcpy(g_settings.lcd_setting_dim_time, configfile.getString("lcd_dim_time","0").c_str()); + strcpy(g_settings.lcd_setting_dim_brightness, configfile.getString("lcd_dim_brightness","0").c_str()); + + //Picture-Viewer + strcpy( g_settings.picviewer_slide_time, configfile.getString( "picviewer_slide_time", "10" ).c_str() ); + g_settings.picviewer_scaling = configfile.getInt32("picviewer_scaling", 1 /*(int)CPictureViewer::SIMPLE*/); + g_settings.picviewer_decode_server_ip = configfile.getString("picviewer_decode_server_ip", ""); + + //Audio-Player + g_settings.audioplayer_display = configfile.getInt32("audioplayer_display",(int)CAudioPlayerGui::ARTIST_TITLE); + g_settings.audioplayer_follow = configfile.getInt32("audioplayer_follow",0); + strcpy( g_settings.audioplayer_screensaver, configfile.getString( "audioplayer_screensaver", "1" ).c_str() ); + g_settings.audioplayer_highprio = configfile.getInt32("audioplayer_highprio",0); + g_settings.audioplayer_select_title_by_name = configfile.getInt32("audioplayer_select_title_by_name",0); + g_settings.audioplayer_repeat_on = configfile.getInt32("audioplayer_repeat_on",0); + g_settings.audioplayer_show_playlist = configfile.getInt32("audioplayer_show_playlist",1); + g_settings.audioplayer_enable_sc_metadata = configfile.getInt32("audioplayer_enable_sc_metadata",1); + + //Filebrowser + g_settings.filebrowser_showrights = configfile.getInt32("filebrowser_showrights", 1); + g_settings.filebrowser_sortmethod = configfile.getInt32("filebrowser_sortmethod", 0); + if ((g_settings.filebrowser_sortmethod < 0) || (g_settings.filebrowser_sortmethod >= FILEBROWSER_NUMBER_OF_SORT_VARIANTS)) + g_settings.filebrowser_sortmethod = 0; + g_settings.filebrowser_denydirectoryleave = configfile.getBool("filebrowser_denydirectoryleave", false); + + // USERMENU -> in system/settings.h + //------------------------------------------- + // this is as the current neutrino usermen + const char* usermenu_default[SNeutrinoSettings::BUTTON_MAX]={ + "2,3,4,13", // RED + "6", // GREEN + "7", // YELLOW + "12,10,11,14,15" // BLUE + }; + char txt1[81]; + std::string txt2; + const char* txt2ptr; + for(int button = 0; button < SNeutrinoSettings::BUTTON_MAX; button++) + { + snprintf(txt1,80,"usermenu_tv_%s_text",usermenu_button_def[button]); + txt1[80] = 0; // terminate for sure + g_settings.usermenu_text[button] = configfile.getString(txt1, "" ); + + snprintf(txt1,80,"usermenu_tv_%s",usermenu_button_def[button]); + txt2 = configfile.getString(txt1,usermenu_default[button]); + txt2ptr = txt2.c_str(); + for( int pos = 0; pos < SNeutrinoSettings::ITEM_MAX; pos++) + { + // find next comma or end of string - if it's not the first round + if(pos != 0) + { + while(*txt2ptr != 0 && *txt2ptr != ',') + txt2ptr++; + if(*txt2ptr != 0) + txt2ptr++; + } + if(*txt2ptr != 0) + { + g_settings.usermenu[button][pos] = atoi(txt2ptr); // there is still a string + if(g_settings.usermenu[button][pos] >= SNeutrinoSettings::ITEM_MAX) + g_settings.usermenu[button][pos] = 0; + } + else + g_settings.usermenu[button][pos] = 0; // string empty, fill up with 0 + + } + } + + if(configfile.getUnknownKeyQueryedFlag() && (erg==0)) { + erg = 2; + } + +#if 0 + // uboot config file + if(fromflash) { + g_settings.uboot_console = 0; + g_settings.uboot_lcd_inverse = -1; + g_settings.uboot_lcd_contrast = -1; + + FILE* fd = fopen("/var/tuxbox/boot/boot.conf", "r"); + if(fd) { + char buffer[100]; + + while(fgets(buffer, 99, fd) != NULL) { + if(strncmp(buffer,"console=",8) == 0) { + if(strncmp(&buffer[8], "null", 4)==0) + g_settings.uboot_console = 0; + else if(strncmp(&buffer[8], "ttyS0", 5)==0) + g_settings.uboot_console = 1; + else if(strncmp(&buffer[8], "ttyCPM0", 5)==0) + g_settings.uboot_console = 1; + else if(strncmp(&buffer[8], "tty", 3)==0) + g_settings.uboot_console = 2; + } + else if(strncmp(buffer,"lcd_inverse=", 12) == 0) { + g_settings.uboot_lcd_inverse = atoi(&buffer[12]); + } + else if(strncmp(buffer,"lcd_contrast=", 13) == 0) { + g_settings.uboot_lcd_contrast = atoi(&buffer[13]); + } + else + printf("unknown entry found in boot.conf\n"); + } + + fclose(fd); + } + g_settings.uboot_console_bak = g_settings.uboot_console; + } +#endif +#define DEFAULT_X_OFF 85 +#define DEFAULT_Y_OFF 34 + if((g_settings.screen_width != (int) frameBuffer->getScreenWidth(true)) + || (g_settings.screen_height != (int) frameBuffer->getScreenHeight(true))) { + g_settings.screen_StartX = DEFAULT_X_OFF; + g_settings.screen_StartY = DEFAULT_Y_OFF; + g_settings.screen_EndX = frameBuffer->getScreenWidth(true) - DEFAULT_X_OFF; + g_settings.screen_EndY = frameBuffer->getScreenHeight(true) - DEFAULT_Y_OFF; + g_settings.screen_width = frameBuffer->getScreenWidth(true); + g_settings.screen_height = frameBuffer->getScreenHeight(true); + } + if(erg) + configfile.setModifiedFlag(true); + return erg; +} + +/************************************************************************************** +* CNeutrinoApp - saveSetup, save the application-settings * +**************************************************************************************/ +void CNeutrinoApp::saveSetup(const char * fname) +{ + char cfg_key[81]; + //uboot; write config only on changes +#if 0 + if (fromflash && + ((g_settings.uboot_console_bak != g_settings.uboot_console) || + (g_settings.uboot_lcd_inverse != g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE]) || + (g_settings.uboot_lcd_contrast != g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST]))) + { + bool newkernel = 0; + FILE* fd = fopen("/proc/version", "r"); + if(fd != NULL) { + char buf[128]; + fgets(buf, 127, fd); + fclose(fd); + if(strstr(buf, "version 2.6")) + newkernel = 1; +//printf("new: %d kernel:: %s\n", newkernel, buf); + } + fd = fopen("/var/tuxbox/boot/boot.conf", "w"); + + if(fd != NULL) { + const char * buffer; + g_settings.uboot_console_bak = g_settings.uboot_console; + g_settings.uboot_lcd_inverse = g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE]; + g_settings.uboot_lcd_contrast = g_settings.lcd_setting[SNeutrinoSettings::LCD_CONTRAST]; + + switch(g_settings.uboot_console) { + case 1: + buffer = newkernel ? "ttyCPM0" : "ttyS0"; + break; + case 2: + buffer = "tty"; + break; + default: + buffer = "null"; + break; + } + fprintf(fd, "console=%s\n" "lcd_inverse=%d\n" "lcd_contrast=%d\n", buffer, g_settings.uboot_lcd_inverse, g_settings.uboot_lcd_contrast); + fclose(fd); + } else { + dprintf(DEBUG_NORMAL, "unable to write file /var/tuxbox/boot/boot.conf\n"); + } + } +#endif + //scan settings + if(!scanSettings.saveSettings(NEUTRINO_SCAN_SETTINGS_FILE)) { + dprintf(DEBUG_NORMAL, "error while saving scan-settings!\n"); + } + + //video + configfile.setInt32( "video_Mode", g_settings.video_Mode ); + configfile.setInt32( "analog_mode1", g_settings.analog_mode1 ); + configfile.setInt32( "analog_mode2", g_settings.analog_mode2 ); + configfile.setInt32( "video_Format", g_settings.video_Format ); + configfile.setInt32( "video_43mode", g_settings.video_43mode ); + configfile.setInt32( "current_volume", g_settings.current_volume ); + configfile.setInt32( "channel_mode", g_settings.channel_mode ); + configfile.setInt32( "video_csync", g_settings.video_csync ); + + configfile.setInt32( "fan_speed", g_settings.fan_speed); + + configfile.setInt32( "srs_enable", g_settings.srs_enable); + configfile.setInt32( "srs_algo", g_settings.srs_algo); + configfile.setInt32( "srs_ref_volume", g_settings.srs_ref_volume); + configfile.setInt32( "srs_nmgr_enable", g_settings.srs_nmgr_enable); + configfile.setInt32( "hdmi_dd", g_settings.hdmi_dd); + configfile.setInt32( "spdif_dd", g_settings.spdif_dd); + configfile.setInt32( "avsync", g_settings.avsync); + configfile.setInt32( "clockrec", g_settings.clockrec); + configfile.setInt32( "video_dbdr", g_settings.video_dbdr); + for(int i = 0; i < VIDEOMENU_VIDEOMODE_OPTION_COUNT; i++) { + sprintf(cfg_key, "enabled_video_mode_%d", i); + configfile.setInt32(cfg_key, g_settings.enabled_video_modes[i]); + } + configfile.setInt32( "cpufreq", g_settings.cpufreq); + configfile.setInt32( "standby_cpufreq", g_settings.standby_cpufreq); + + configfile.setInt32( "make_hd_list", g_settings.make_hd_list); + //fb-alphawerte für gtx + configfile.setInt32( "gtx_alpha1", g_settings.gtx_alpha1 ); + configfile.setInt32( "gtx_alpha2", g_settings.gtx_alpha2 ); + + //misc + configfile.setInt32( "power_standby", g_settings.power_standby); + configfile.setInt32( "rotor_swap", g_settings.rotor_swap); + configfile.setInt32( "emlog", g_settings.emlog); + configfile.setInt32( "zap_cycle", g_settings.zap_cycle ); + configfile.setInt32( "sms_channel", g_settings.sms_channel ); + configfile.setInt32( "hdd_fs", g_settings.hdd_fs); + configfile.setInt32( "hdd_sleep", g_settings.hdd_sleep); + configfile.setInt32( "hdd_noise", g_settings.hdd_noise); + configfile.setBool("shutdown_real" , g_settings.shutdown_real ); + configfile.setBool("shutdown_real_rcdelay", g_settings.shutdown_real_rcdelay); + configfile.setString("shutdown_count" , g_settings.shutdown_count); + configfile.setBool("infobar_sat_display" , g_settings.infobar_sat_display ); + configfile.setInt32("infobar_subchan_disp_pos" , g_settings.infobar_subchan_disp_pos ); + + //audio + configfile.setInt32( "audio_AnalogMode", g_settings.audio_AnalogMode ); + configfile.setBool("audio_DolbyDigital" , g_settings.audio_DolbyDigital ); + configfile.setInt32( "audio_avs_Control", g_settings.audio_avs_Control ); + configfile.setInt32( "audio_english", g_settings.audio_english ); + configfile.setString( "audio_PCMOffset", g_settings.audio_PCMOffset ); + + //vcr + configfile.setBool("vcr_AutoSwitch" , g_settings.vcr_AutoSwitch ); + + //language + configfile.setString("language", g_settings.language); + configfile.setString("timezone", g_settings.timezone); + // epg + configfile.setBool("epg_save", g_settings.epg_save); + configfile.setString("epg_cache_time" ,g_settings.epg_cache ); + configfile.setString("epg_extendedcache_time" ,g_settings.epg_extendedcache); + configfile.setString("epg_old_events" ,g_settings.epg_old_events ); + configfile.setString("epg_max_events" ,g_settings.epg_max_events ); + configfile.setString("epg_dir" ,g_settings.epg_dir); + + // NTP-Server for sectionsd + configfile.setString( "network_ntpserver", g_settings.network_ntpserver); + configfile.setString( "network_ntprefresh", g_settings.network_ntprefresh); + configfile.setBool( "network_ntpenable", g_settings.network_ntpenable); + + //widget settings + configfile.setBool("widget_fade" , g_settings.widget_fade ); + + //colors + configfile.setInt32( "menu_Head_alpha", g_settings.menu_Head_alpha ); + configfile.setInt32( "menu_Head_red", g_settings.menu_Head_red ); + configfile.setInt32( "menu_Head_green", g_settings.menu_Head_green ); + configfile.setInt32( "menu_Head_blue", g_settings.menu_Head_blue ); + + configfile.setInt32( "menu_Head_Text_alpha", g_settings.menu_Head_Text_alpha ); + configfile.setInt32( "menu_Head_Text_red", g_settings.menu_Head_Text_red ); + configfile.setInt32( "menu_Head_Text_green", g_settings.menu_Head_Text_green ); + configfile.setInt32( "menu_Head_Text_blue", g_settings.menu_Head_Text_blue ); + + configfile.setInt32( "menu_Content_alpha", g_settings.menu_Content_alpha ); + configfile.setInt32( "menu_Content_red", g_settings.menu_Content_red ); + configfile.setInt32( "menu_Content_green", g_settings.menu_Content_green ); + configfile.setInt32( "menu_Content_blue", g_settings.menu_Content_blue ); + + configfile.setInt32( "menu_Content_Text_alpha", g_settings.menu_Content_Text_alpha ); + configfile.setInt32( "menu_Content_Text_red", g_settings.menu_Content_Text_red ); + configfile.setInt32( "menu_Content_Text_green", g_settings.menu_Content_Text_green ); + configfile.setInt32( "menu_Content_Text_blue", g_settings.menu_Content_Text_blue ); + + configfile.setInt32( "menu_Content_Selected_alpha", g_settings.menu_Content_Selected_alpha ); + configfile.setInt32( "menu_Content_Selected_red", g_settings.menu_Content_Selected_red ); + configfile.setInt32( "menu_Content_Selected_green", g_settings.menu_Content_Selected_green ); + configfile.setInt32( "menu_Content_Selected_blue", g_settings.menu_Content_Selected_blue ); + + configfile.setInt32( "menu_Content_Selected_Text_alpha", g_settings.menu_Content_Selected_Text_alpha ); + configfile.setInt32( "menu_Content_Selected_Text_red", g_settings.menu_Content_Selected_Text_red ); + configfile.setInt32( "menu_Content_Selected_Text_green", g_settings.menu_Content_Selected_Text_green ); + configfile.setInt32( "menu_Content_Selected_Text_blue", g_settings.menu_Content_Selected_Text_blue ); + + configfile.setInt32( "menu_Content_inactive_alpha", g_settings.menu_Content_inactive_alpha ); + configfile.setInt32( "menu_Content_inactive_red", g_settings.menu_Content_inactive_red ); + configfile.setInt32( "menu_Content_inactive_green", g_settings.menu_Content_inactive_green ); + configfile.setInt32( "menu_Content_inactive_blue", g_settings.menu_Content_inactive_blue ); + + configfile.setInt32( "menu_Content_inactive_Text_alpha", g_settings.menu_Content_inactive_Text_alpha ); + configfile.setInt32( "menu_Content_inactive_Text_red", g_settings.menu_Content_inactive_Text_red ); + configfile.setInt32( "menu_Content_inactive_Text_green", g_settings.menu_Content_inactive_Text_green ); + configfile.setInt32( "menu_Content_inactive_Text_blue", g_settings.menu_Content_inactive_Text_blue ); + + configfile.setInt32( "infobar_alpha", g_settings.infobar_alpha ); + configfile.setInt32( "infobar_red", g_settings.infobar_red ); + configfile.setInt32( "infobar_green", g_settings.infobar_green ); + configfile.setInt32( "infobar_blue", g_settings.infobar_blue ); + + configfile.setInt32( "infobar_Text_alpha", g_settings.infobar_Text_alpha ); + configfile.setInt32( "infobar_Text_red", g_settings.infobar_Text_red ); + configfile.setInt32( "infobar_Text_green", g_settings.infobar_Text_green ); + configfile.setInt32( "infobar_Text_blue", g_settings.infobar_Text_blue ); + + //network + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) { + sprintf(cfg_key, "network_nfs_ip_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_ip[i] ); + sprintf(cfg_key, "network_nfs_dir_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_dir[i] ); + sprintf(cfg_key, "network_nfs_local_dir_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_local_dir[i] ); + sprintf(cfg_key, "network_nfs_automount_%d", i); + configfile.setInt32( cfg_key, g_settings.network_nfs_automount[i]); + sprintf(cfg_key, "network_nfs_type_%d", i); + configfile.setInt32( cfg_key, g_settings.network_nfs_type[i]); + sprintf(cfg_key,"network_nfs_username_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_username[i] ); + sprintf(cfg_key, "network_nfs_password_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_password[i] ); + sprintf(cfg_key, "network_nfs_mount_options1_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_mount_options1[i]); + sprintf(cfg_key, "network_nfs_mount_options2_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_mount_options2[i]); + sprintf(cfg_key, "network_nfs_mac_%d", i); + configfile.setString( cfg_key, g_settings.network_nfs_mac[i]); + } + configfile.setString( "network_nfs_audioplayerdir", g_settings.network_nfs_audioplayerdir); + configfile.setString( "network_nfs_picturedir", g_settings.network_nfs_picturedir); + configfile.setString( "network_nfs_moviedir", g_settings.network_nfs_moviedir); + configfile.setString( "network_nfs_recordingdir", g_settings.network_nfs_recordingdir); + configfile.setString( "timeshiftdir", g_settings.timeshiftdir); + configfile.setBool ("filesystem_is_utf8" , g_settings.filesystem_is_utf8 ); + + //recording (server + vcr) + configfile.setInt32 ("recording_type", g_settings.recording_type); + configfile.setBool ("recording_stopplayback" , g_settings.recording_stopplayback ); + configfile.setBool ("recording_stopsectionsd" , g_settings.recording_stopsectionsd ); + configfile.setString("recording_server_ip", g_settings.recording_server_ip); + configfile.setString("recording_server_port", g_settings.recording_server_port); + configfile.setInt32 ("recording_server_wakeup", g_settings.recording_server_wakeup); + configfile.setString("recording_server_mac", g_settings.recording_server_mac); + configfile.setInt32 ("recording_vcr_no_scart", g_settings.recording_vcr_no_scart); + configfile.setString("recording_splitsize", g_settings.recording_splitsize); + configfile.setBool ("recordingmenu.use_o_sync" , g_settings.recording_use_o_sync ); + configfile.setBool ("recordingmenu.use_fdatasync" , g_settings.recording_use_fdatasync ); + + configfile.setInt32 ("recording_audio_pids_default" , g_settings.recording_audio_pids_default); + configfile.setBool ("recording_zap_on_announce" , g_settings.recording_zap_on_announce ); + + configfile.setBool ("recordingmenu.stream_vtxt_pid" , g_settings.recording_stream_vtxt_pid ); + configfile.setBool ("recordingmenu.stream_pmt_pid" , g_settings.recording_stream_pmt_pid ); + configfile.setString("recordingmenu.ringbuffers" , g_settings.recording_ringbuffers); + configfile.setInt32 ("recording_choose_direct_rec_dir" , g_settings.recording_choose_direct_rec_dir); + configfile.setBool ("recording_epg_for_filename" , g_settings.recording_epg_for_filename ); + configfile.setBool ("recording_save_in_channeldir" , g_settings.recording_save_in_channeldir ); + configfile.setBool ("recording_in_spts_mode" , g_settings.recording_in_spts_mode ); + + //streaming + configfile.setInt32 ( "streaming_type", g_settings.streaming_type ); + configfile.setString( "streaming_server_ip", g_settings.streaming_server_ip ); + configfile.setString( "streaming_server_port", g_settings.streaming_server_port ); + configfile.setString( "streaming_server_cddrive", g_settings.streaming_server_cddrive ); + configfile.setString ( "streaming_videorate", g_settings.streaming_videorate ); + configfile.setString ( "streaming_audiorate", g_settings.streaming_audiorate ); + configfile.setString( "streaming_server_startdir", g_settings.streaming_server_startdir ); + configfile.setInt32 ( "streaming_transcode_audio", g_settings.streaming_transcode_audio ); + configfile.setInt32 ( "streaming_force_avi_rawaudio", g_settings.streaming_force_avi_rawaudio ); + configfile.setInt32 ( "streaming_force_transcode_video", g_settings.streaming_force_transcode_video ); + configfile.setInt32 ( "streaming_transcode_video_codec", g_settings.streaming_transcode_video_codec ); + configfile.setInt32 ( "streaming_resolution", g_settings.streaming_resolution ); + + // default plugin for movieplayer + configfile.setString ( "movieplayer_plugin", g_settings.movieplayer_plugin ); + configfile.setString ( "onekey_plugin", g_settings.onekey_plugin ); + + configfile.setInt32 ( "streaming_resolution", g_settings.streaming_resolution ); + + configfile.setInt32( "rf_subcarrier", g_settings.rf_subcarrier); + configfile.setInt32( "rf_soundenable", g_settings.rf_soundenable); + configfile.setInt32( "rf_channel", g_settings.rf_channel); + configfile.setInt32( "rf_finetune", g_settings.rf_finetune); + configfile.setInt32( "rf_standby", g_settings.rf_standby); + + //rc-key configuration + configfile.setInt32( "key_tvradio_mode", g_settings.key_tvradio_mode ); + + configfile.setInt32( "key_channelList_pageup", g_settings.key_channelList_pageup ); + configfile.setInt32( "key_channelList_pagedown", g_settings.key_channelList_pagedown ); + configfile.setInt32( "key_channelList_cancel", g_settings.key_channelList_cancel ); + configfile.setInt32( "key_channelList_sort", g_settings.key_channelList_sort ); + configfile.setInt32( "key_channelList_addrecord", g_settings.key_channelList_addrecord ); + configfile.setInt32( "key_channelList_addremind", g_settings.key_channelList_addremind ); + + configfile.setInt32( "key_quickzap_up", g_settings.key_quickzap_up ); + configfile.setInt32( "key_quickzap_down", g_settings.key_quickzap_down ); + configfile.setInt32( "key_bouquet_up", g_settings.key_bouquet_up ); + configfile.setInt32( "key_bouquet_down", g_settings.key_bouquet_down ); + configfile.setInt32( "key_subchannel_up", g_settings.key_subchannel_up ); + configfile.setInt32( "key_subchannel_down", g_settings.key_subchannel_down ); + configfile.setInt32( "key_zaphistory", g_settings.key_zaphistory ); + configfile.setInt32( "key_lastchannel", g_settings.key_lastchannel ); + + configfile.setInt32( "key_list_start", g_settings.key_list_start ); + configfile.setInt32( "key_list_end", g_settings.key_list_end ); + configfile.setInt32( "menu_left_exit", g_settings.menu_left_exit ); + configfile.setInt32( "audio_run_player", g_settings.audio_run_player ); + configfile.setInt32( "key_click", g_settings.key_click ); + configfile.setInt32( "timeshift_pause", g_settings.timeshift_pause ); + configfile.setInt32( "temp_timeshift", g_settings.temp_timeshift ); + configfile.setInt32( "auto_timeshift", g_settings.auto_timeshift ); + configfile.setInt32( "auto_delete", g_settings.auto_delete ); + configfile.setInt32( "record_hours", g_settings.record_hours ); + configfile.setInt32( "mpkey.rewind", g_settings.mpkey_rewind ); + configfile.setInt32( "mpkey.forward", g_settings.mpkey_forward ); + configfile.setInt32( "mpkey.pause", g_settings.mpkey_pause ); + configfile.setInt32( "mpkey.stop", g_settings.mpkey_stop ); + configfile.setInt32( "mpkey.play", g_settings.mpkey_play ); + configfile.setInt32( "mpkey.audio", g_settings.mpkey_audio ); + configfile.setInt32( "mpkey.time", g_settings.mpkey_time ); + configfile.setInt32( "mpkey.bookmark", g_settings.mpkey_bookmark ); + configfile.setInt32( "mpkey.plugin", g_settings.mpkey_plugin ); + configfile.setInt32( "key_timeshift", g_settings.key_timeshift ); + configfile.setInt32( "key_plugin", g_settings.key_plugin ); + configfile.setInt32( "key_unlock", g_settings.key_unlock ); +//printf("set: key_unlock =============== %d\n", g_settings.key_unlock); + configfile.setInt32( "cacheTXT", g_settings.cacheTXT ); + configfile.setInt32( "minimode", g_settings.minimode ); + configfile.setInt32( "mode_clock", g_settings.mode_clock ); + configfile.setBool("virtual_zap_mode", g_settings.virtual_zap_mode); + configfile.setBool("spectrum", g_settings.spectrum); + configfile.setBool("channellist_epgtext_align_right", g_settings.channellist_epgtext_align_right); + configfile.setBool("channellist_extended" , g_settings.channellist_extended); + configfile.setString( "repeat_blocker", g_settings.repeat_blocker ); + configfile.setString( "repeat_genericblocker", g_settings.repeat_genericblocker ); + configfile.setBool ( "audiochannel_up_down_enable", g_settings.audiochannel_up_down_enable ); + + //screen configuration + configfile.setInt32( "screen_StartX", g_settings.screen_StartX ); + configfile.setInt32( "screen_StartY", g_settings.screen_StartY ); + configfile.setInt32( "screen_EndX", g_settings.screen_EndX ); + configfile.setInt32( "screen_EndY", g_settings.screen_EndY ); + configfile.setInt32( "screen_width", g_settings.screen_width); + configfile.setInt32( "screen_height", g_settings.screen_height); + + //Software-update + configfile.setInt32 ("softupdate_mode" , g_settings.softupdate_mode ); + configfile.setString("softupdate_url_file" , g_settings.softupdate_url_file ); +#if 1 //FIXME why was commented ?? + configfile.setString("softupdate_proxyserver" , g_settings.softupdate_proxyserver ); + configfile.setString("softupdate_proxyusername" , g_settings.softupdate_proxyusername ); + configfile.setString("softupdate_proxypassword" , g_settings.softupdate_proxypassword ); +#endif + configfile.setString("update_dir", g_settings.update_dir); + configfile.setString("font_file", g_settings.font_file); + //BouquetHandling + configfile.setInt32( "bouquetlist_mode", g_settings.bouquetlist_mode ); + + //parentallock + configfile.setInt32( "parentallock_prompt", g_settings.parentallock_prompt ); + configfile.setInt32( "parentallock_lockage", g_settings.parentallock_lockage ); + configfile.setString( "parentallock_pincode", g_settings.parentallock_pincode ); + + //timing + for (int i = 0; i < TIMING_SETTING_COUNT; i++) + configfile.setInt32(locale_real_names[timing_setting_name[i]], g_settings.timing[i]); + + for (int i = 0; i < LCD_SETTING_COUNT; i++) + configfile.setInt32(lcd_setting[i].name, g_settings.lcd_setting[i]); + configfile.setString("lcd_dim_time", g_settings.lcd_setting_dim_time); + configfile.setString("lcd_dim_brightness", g_settings.lcd_setting_dim_brightness); + + //Picture-Viewer + configfile.setString( "picviewer_slide_time", g_settings.picviewer_slide_time ); + configfile.setInt32( "picviewer_scaling", g_settings.picviewer_scaling ); + configfile.setString( "picviewer_decode_server_ip", g_settings.picviewer_decode_server_ip ); + configfile.setString( "picviewer_decode_server_port", g_settings.picviewer_decode_server_port); + + //Audio-Player + configfile.setInt32( "audioplayer_display", g_settings.audioplayer_display ); + configfile.setInt32( "audioplayer_follow", g_settings.audioplayer_follow ); + configfile.setString( "audioplayer_screensaver", g_settings.audioplayer_screensaver ); + configfile.setInt32( "audioplayer_highprio", g_settings.audioplayer_highprio ); + configfile.setInt32( "audioplayer_select_title_by_name", g_settings.audioplayer_select_title_by_name ); + configfile.setInt32( "audioplayer_repeat_on", g_settings.audioplayer_repeat_on ); + configfile.setInt32( "audioplayer_show_playlist", g_settings.audioplayer_show_playlist ); + configfile.setInt32( "audioplayer_enable_sc_metadata", g_settings.audioplayer_enable_sc_metadata ); + + //Filebrowser + configfile.setInt32("filebrowser_showrights", g_settings.filebrowser_showrights); + configfile.setInt32("filebrowser_sortmethod", g_settings.filebrowser_sortmethod); + configfile.setBool("filebrowser_denydirectoryleave", g_settings.filebrowser_denydirectoryleave); + + // USERMENU + //--------------------------------------- + char txt1[81]; + char txt2[81]; + for(int button = 0; button < SNeutrinoSettings::BUTTON_MAX; button++) { + snprintf(txt1,80,"usermenu_tv_%s_text",usermenu_button_def[button]); + txt1[80] = 0; // terminate for sure + configfile.setString(txt1,g_settings.usermenu_text[button]); + + char* txt2ptr = txt2; + snprintf(txt1,80,"usermenu_tv_%s",usermenu_button_def[button]); + for(int pos = 0; pos < SNeutrinoSettings::ITEM_MAX; pos++) { + if( g_settings.usermenu[button][pos] != 0) { + if(pos != 0) + *txt2ptr++ = ','; + txt2ptr += snprintf(txt2ptr,80,"%d",g_settings.usermenu[button][pos]); + } + } + configfile.setString(txt1,txt2); + } + + configfile.setInt32("bigFonts", g_settings.bigFonts); +#if 0 + configfile.setInt32("pip_x", g_settings.pip_x); + configfile.setInt32("pip_y", g_settings.pip_y); + configfile.setInt32("pip_width", g_settings.pip_width); + configfile.setInt32("pip_height", g_settings.pip_height); +#endif + if(strcmp(fname, NEUTRINO_SETTINGS_FILE)) + configfile.saveConfig(fname); + + else if (configfile.getModifiedFlag()) + { + configfile.saveConfig(fname); + configfile.setModifiedFlag(false); + } +} + +/************************************************************************************** +* CNeutrinoApp - firstChannel, get the initial channel * +**************************************************************************************/ +void CNeutrinoApp::firstChannel() +{ + g_Zapit->getLastChannel(firstchannel.channelNumber, firstchannel.mode); +} + +/************************************************************************************** +* CNeutrinoApp - channelsInit, get the Channellist from daemon * +**************************************************************************************/ +#include +#include + +#define TIMER_START() \ + struct timeval tv, tv2; \ + unsigned int msec; \ + gettimeofday(&tv, NULL) + +#define TIMER_STOP(label) \ + gettimeofday(&tv2, NULL); \ + msec = ((tv2.tv_sec - tv.tv_sec) * 1000) + ((tv2.tv_usec - tv.tv_usec) / 1000); \ + printf("%s: %u msec\n", label, msec) + +extern tallchans allchans; +extern CBouquetManager *g_bouquetManager; + +void CNeutrinoApp::channelsInit(bool bOnly) +{ + printf("[neutrino] Creating channels lists...\n"); + TIMER_START(); + +#if 1 + if(!reloadhintBox) + reloadhintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_SERVICEMENU_RELOAD_HINT)); + reloadhintBox->paint(); +#endif + + const char * fav_bouquetname = g_Locale->getText(LOCALE_FAVORITES_BOUQUETNAME); + if(g_bouquetManager->existsUBouquet(fav_bouquetname, true) == -1) + g_bouquetManager->addBouquet(fav_bouquetname, true, true); + + if(TVallList) delete TVallList; + if(RADIOallList) delete RADIOallList; + if(TVbouquetList) delete TVbouquetList; + if(TVsatList) delete TVsatList; + if(TVfavList) delete TVfavList; + if(RADIObouquetList) delete RADIObouquetList; + if(RADIOsatList) delete RADIOsatList; + if(RADIOfavList) delete RADIOfavList; + + if(TVchannelList) delete TVchannelList; + if(RADIOchannelList) delete RADIOchannelList; + + TVchannelList = new CChannelList(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + RADIOchannelList = new CChannelList(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + + TVbouquetList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_PROVS)); + TVbouquetList->orgChannelList = TVchannelList; + TVsatList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_SATS)); + TVsatList->orgChannelList = TVchannelList; + TVfavList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_FAVS)); + TVfavList->orgChannelList = TVchannelList; + + RADIObouquetList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_PROVS)); + RADIObouquetList->orgChannelList = RADIOchannelList; + RADIOsatList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_SATS)); + RADIOsatList->orgChannelList = RADIOchannelList; + RADIOfavList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_FAVS)); + RADIOfavList->orgChannelList = RADIOchannelList; + + uint32_t i; + i = 1; + + //CBouquet* tmp = TVfavList->addBouquet("HD");//FIXME locale + CBouquet* hdBouquet; + if(g_settings.make_hd_list) + hdBouquet = new CBouquet(0, (char *) "HD", 0); + + int tvi = 0, ri = 0, hi = 0; + for (tallchans_iterator it = allchans.begin(); it != allchans.end(); it++) { + if (it->second.getServiceType() == ST_DIGITAL_TELEVISION_SERVICE) { + TVchannelList->putChannel(&(it->second)); + tvi++; +#if 1 + if(g_settings.make_hd_list) + if(it->second.isHD()) { + //printf("HD channel: %d %s sid %x\n", it->second.getSatellitePosition(), it->second.getName().c_str(), it->second.getServiceId()); + hdBouquet->channelList->addChannel(&(it->second)); + hi++; + } +#endif + } + else if (it->second.getServiceType() == ST_DIGITAL_RADIO_SOUND_SERVICE) { + RADIOchannelList->putChannel(&(it->second)); + ri++; + } + } + if(hi) + hdBouquet->channelList->SortSat(); + + printf("[neutrino] got %d TV (%d is HD) and %d RADIO channels\n", tvi, hi, ri); fflush(stdout); + + CBouquet* tmp; + + TVallList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + tmp = TVallList->addBouquet(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + *(tmp->channelList) = *TVchannelList; + tmp->channelList->SortAlpha(); + TVallList->orgChannelList = TVchannelList; + + RADIOallList = new CBouquetList(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + tmp = RADIOallList->addBouquet(g_Locale->getText(LOCALE_CHANNELLIST_HEAD)); + *(tmp->channelList) = *RADIOchannelList; + tmp->channelList->SortAlpha(); + RADIOallList->orgChannelList = RADIOchannelList; + + int bnum; + sat_iterator_t sit; + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + tvi = 0, ri = 0; + CBouquet* tmp1 = TVsatList->addBouquet(sit->second.name.c_str()); + CBouquet* tmp2 = RADIOsatList->addBouquet(sit->second.name.c_str()); + + for (tallchans_iterator it = allchans.begin(); it != allchans.end(); it++) { + if(it->second.getSatellitePosition() == sit->first) { + if (it->second.getServiceType() == ST_DIGITAL_TELEVISION_SERVICE) { + tmp1->channelList->addChannel(&(it->second)); + tvi++; + } + else if (it->second.getServiceType() == ST_DIGITAL_RADIO_SOUND_SERVICE) { + tmp2->channelList->addChannel(&(it->second)); + ri++; + } + } + } + if(tvi) + tmp1->channelList->SortAlpha(); + else + TVsatList->deleteBouquet(tmp1); + if(ri) + tmp2->channelList->SortAlpha(); + else + RADIOsatList->deleteBouquet(tmp2); + if(tvi || ri) + printf("[neutrino] created %s bouquet with %d TV and %d RADIO channels\n", sit->second.name.c_str(), tvi, ri); + } + + bnum = 0; + for (i = 0; i < g_bouquetManager->Bouquets.size(); i++) { + //if (!g_bouquetManager->Bouquets[i]->bHidden && (g_bouquetManager->Bouquets[i]->bUser || !g_bouquetManager->Bouquets[i]->tvChannels.empty() )) + if (!g_bouquetManager->Bouquets[i]->bHidden && !g_bouquetManager->Bouquets[i]->tvChannels.empty()) + { + CBouquet* tmp; + if(g_bouquetManager->Bouquets[i]->bUser) + tmp = TVfavList->addBouquet(g_bouquetManager->Bouquets[i]); + else + tmp = TVbouquetList->addBouquet(g_bouquetManager->Bouquets[i]); + + ZapitChannelList* channels = &(g_bouquetManager->Bouquets[i]->tvChannels); + tmp->channelList->setSize(channels->size()); + for(int j = 0; j < (int) channels->size(); j++) { + tmp->channelList->addChannel((*channels)[j]); + } + bnum++; + } + } + printf("[neutrino] got %d TV bouquets\n", bnum); fflush(stdout); + + if(g_settings.make_hd_list) + TVfavList->Bouquets.push_back(hdBouquet); + + bnum = 0; + for (i = 0; i < g_bouquetManager->Bouquets.size(); i++) { + //if (!g_bouquetManager->Bouquets[i]->bHidden && (g_bouquetManager->Bouquets[i]->bUser || !g_bouquetManager->Bouquets[i]->radioChannels.empty() )) + if (!g_bouquetManager->Bouquets[i]->bHidden && !g_bouquetManager->Bouquets[i]->radioChannels.empty() ) + { + CBouquet* tmp; + if(g_bouquetManager->Bouquets[i]->bUser) + tmp = RADIOfavList->addBouquet(g_bouquetManager->Bouquets[i]->Name.c_str(), i, g_bouquetManager->Bouquets[i]->bLocked); + else + tmp = RADIObouquetList->addBouquet(g_bouquetManager->Bouquets[i]->Name.c_str(), i, g_bouquetManager->Bouquets[i]->bLocked); + + ZapitChannelList* channels = &(g_bouquetManager->Bouquets[i]->radioChannels); + tmp->channelList->setSize(channels->size()); + for(int j = 0; j < (int) channels->size(); j++) { + tmp->channelList->addChannel((*channels)[j]); + } + bnum++; + } + } + printf("[neutrino] got %d RADIO bouquets\n", bnum); fflush(stdout); + TIMER_STOP("[neutrino] took"); + + SetChannelMode(g_settings.channel_mode); + + dprintf(DEBUG_DEBUG, "\nAll bouquets-channels received\n"); +#ifdef DEBUG + struct mallinfo myinfo = mallinfo(); + printf("[neutrino] total memory allocated by malloc, in bytes: %d (%dkb), chunks %d\n", + myinfo.arena, myinfo.arena / 1024, myinfo.uordblks); +#endif + + reloadhintBox->hide(); +} + +void CNeutrinoApp::SetChannelMode(int newmode) +{ +printf("CNeutrinoApp::SetChannelMode %d\n", newmode); + if(mode == mode_radio) + channelList = RADIOchannelList; + else + channelList = TVchannelList; + + switch(newmode) { + case LIST_MODE_FAV: + if(mode == mode_radio) { + bouquetList = RADIOfavList; + } else { + bouquetList = TVfavList; + } + break; + case LIST_MODE_SAT: + if(mode == mode_radio) { + bouquetList = RADIOsatList; + } else { + bouquetList = TVsatList; + } + break; + case LIST_MODE_ALL: + if(mode == mode_radio) { + bouquetList = RADIOallList; + } else { + bouquetList = TVallList; + } + break; + default: + case LIST_MODE_PROV: + if(mode == mode_radio) { + bouquetList = RADIObouquetList; + } else { + bouquetList = TVbouquetList; + } + break; + } + g_settings.channel_mode = newmode; +} + +/************************************************************************************** +* CNeutrinoApp - run, the main runloop * +**************************************************************************************/ +extern int cnxt_debug; +extern int sections_debug; +extern int zapit_debug; + +void CNeutrinoApp::CmdParser(int argc, char **argv) +{ + global_argv = new char *[argc+1]; + for (int i = 0; i < argc; i++) + global_argv[i] = argv[i]; + global_argv[argc] = NULL; + + softupdate = false; + fromflash = false; + + font.name = NULL; + + for(int x=1; xinit(); + if(frameBuffer->setMode(720, 576, 8 * sizeof(fb_pixel_t))) { + dprintf(DEBUG_NORMAL, "Error while setting framebuffer mode\n"); + exit(-1); + } + //make 1..8 transparent for dummy painting + //for(int count =0;count<256;count++) + for(int count =0;count<8;count++) + frameBuffer->paletteSetColor(count, 0x000000, 0xffff); + frameBuffer->paletteSet(); + frameBuffer->ClearFrameBuffer(); +} + +/************************************************************************************** +* CNeutrinoApp - setup fonts * +**************************************************************************************/ +#if 0 +const neutrino_font_descr_struct predefined_font[2] = +{ + {"Micron" , {FONTDIR "/micron.ttf" , FONTDIR "/micron_bold.ttf", FONTDIR "/micron_italic.ttf"}, 0}, + {"MD King KhammuRabi", {FONTDIR "/md_khmurabi_10.ttf", NULL , NULL }, 0} +}; +const char* predefined_lcd_font[2][6] = +{ + {FONTDIR "/12.pcf.gz", "Fix12", FONTDIR "/14B.pcf.gz", "Fix14", FONTDIR "/15B.pcf.gz", "Fix15"}, + {FONTDIR "/md_khmurabi_10.ttf", "MD King KhammuRabi", NULL, NULL, NULL, NULL} +}; +#endif + +void CNeutrinoApp::SetupFonts() +{ + const char * style[3]; + + if (g_fontRenderer != NULL) + delete g_fontRenderer; + + g_fontRenderer = new FBFontRenderClass(xres, yres); + + if(font.filename != NULL) + free((void *)font.filename); + + printf("[neutrino] settings font file %s\n", g_settings.font_file); + + if(access(g_settings.font_file, F_OK)) { + font.filename = strdup(FONTDIR"/neutrino.ttf"); + strcpy(g_settings.font_file, font.filename); + } + else + font.filename = strdup(g_settings.font_file); + + style[0] = g_fontRenderer->AddFont(font.filename); + + if(font.name != NULL) + free((void *)font.name); + + font.name = strdup(g_fontRenderer->getFamily(font.filename).c_str()); + + printf("[neutrino] font family %s\n", font.name); + + style[1] = "Bold Regular"; + + g_fontRenderer->AddFont(font.filename, true); // make italics + style[2] = "Italic"; + + for (int i = 0; i < FONT_TYPE_COUNT; i++) + { + if(g_Font[i]) delete g_Font[i]; + g_Font[i] = g_fontRenderer->getFont(font.name, style[neutrino_font[i].style], configfile.getInt32(locale_real_names[neutrino_font[i].name], neutrino_font[i].defaultsize) + neutrino_font[i].size_offset * font.size_offset); + } + g_SignalFont = g_fontRenderer->getFont(font.name, style[signal_font.style], signal_font.defaultsize + signal_font.size_offset * font.size_offset); +} + +/************************************************************************************** +* CNeutrinoApp - setup the menu timouts * +**************************************************************************************/ +void CNeutrinoApp::SetupTiming() +{ + for (int i = 0; i < TIMING_SETTING_COUNT; i++) + sprintf(g_settings.timing_string[i], "%d", g_settings.timing[i]); +} + +CAudioPlayerGui * audioPlayer; + +bool sectionsd_getActualEPGServiceKey(const t_channel_id uniqueServiceKey, CEPGData * epgdata); +bool sectionsd_getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata); + +int startAutoRecord(bool addTimer) +{ + CTimerd::RecordingInfo eventinfo; + if(CNeutrinoApp::getInstance()->recordingstatus || !CVCRControl::getInstance()->isDeviceRegistered() || (g_settings.recording_type != RECORDING_FILE)) + return 0; + + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo(); + + //eventinfo.channel_id = g_Zapit->getCurrentServiceID(); + eventinfo.channel_id = live_channel_id; + CEPGData epgData; + //if (g_Sectionsd->getActualEPGServiceKey(g_RemoteControl->current_channel_id&0xFFFFFFFFFFFFULL, &epgData )) + if (sectionsd_getActualEPGServiceKey(live_channel_id&0xFFFFFFFFFFFFULL, &epgData )) + { + eventinfo.epgID = epgData.eventID; + eventinfo.epg_starttime = epgData.epg_times.startzeit; + strncpy(eventinfo.epgTitle, epgData.title.c_str(), EPG_TITLE_MAXLEN-1); + eventinfo.epgTitle[EPG_TITLE_MAXLEN-1]=0; + } + else { + eventinfo.epgID = 0; + eventinfo.epg_starttime = 0; + strcpy(eventinfo.epgTitle, ""); + } + eventinfo.apids = TIMERD_APIDS_CONF; +printf("*********************************** startAutoRecord: dir %s\n", timeshiftDir); + (static_cast(recordingdevice))->Directory = timeshiftDir; + + autoshift = 1; + CNeutrinoApp::getInstance()->recordingstatus = 1; + if(CVCRControl::getInstance()->Record(&eventinfo)==false) { + CNeutrinoApp::getInstance()->recordingstatus = 0; + autoshift = 0; + } + else if (addTimer) { + time_t now = time(NULL); + CNeutrinoApp::getInstance()->recording_id = g_Timerd->addImmediateRecordTimerEvent(eventinfo.channel_id, now, now+g_settings.record_hours*60*60, eventinfo.epgID, eventinfo.epg_starttime, eventinfo.apids); + } +//printf("startAutoRecord: recording_id = %d\n", CNeutrinoApp::getInstance()->recording_id); + return 0; +} +void stopAutoRecord() +{ + if(autoshift && CNeutrinoApp::getInstance()->recordingstatus) { +printf("stopAutoRecord: autoshift, recordingstatus %d, stopping ...\n", CNeutrinoApp::getInstance()->recordingstatus); + CVCRControl::getInstance()->Stop(); + autoshift = false; + if(CNeutrinoApp::getInstance()->recording_id) { +//printf("stopAutoRecord: recording_id = %d\n", CNeutrinoApp::getInstance()->recording_id); + g_Timerd->stopTimerEvent(CNeutrinoApp::getInstance()->recording_id); + CNeutrinoApp::getInstance()->recording_id = 0; + } + } else if(shift_timer) { + g_RCInput->killTimer (shift_timer); + shift_timer = 0; + } +} + +bool CNeutrinoApp::doGuiRecord(char * preselectedDir, bool addTimer) +{ + CTimerd::RecordingInfo eventinfo; + bool refreshGui = false; + if(CVCRControl::getInstance()->isDeviceRegistered()) { + if(autoshift) { + stopAutoRecord(); + } + if(recordingstatus == 1) { + puts("[neutrino.cpp] executing " NEUTRINO_RECORDING_START_SCRIPT "."); + if (system(NEUTRINO_RECORDING_START_SCRIPT) != 0) + perror(NEUTRINO_RECORDING_START_SCRIPT " failed"); + + CZapitClient::CCurrentServiceInfo si = g_Zapit->getCurrentServiceInfo(); + + //eventinfo.channel_id = g_Zapit->getCurrentServiceID(); + eventinfo.channel_id = live_channel_id; + CEPGData epgData; + //if (g_Sectionsd->getActualEPGServiceKey(g_RemoteControl->current_channel_id&0xFFFFFFFFFFFFULL, &epgData )) + if (sectionsd_getActualEPGServiceKey(live_channel_id&0xFFFFFFFFFFFFULL, &epgData )) + { + eventinfo.epgID = epgData.eventID; + eventinfo.epg_starttime = epgData.epg_times.startzeit; + strncpy(eventinfo.epgTitle, epgData.title.c_str(), EPG_TITLE_MAXLEN-1); + eventinfo.epgTitle[EPG_TITLE_MAXLEN-1]=0; + } + else { + eventinfo.epgID = 0; + eventinfo.epg_starttime = 0; + strcpy(eventinfo.epgTitle, ""); + } + eventinfo.apids = TIMERD_APIDS_CONF; + bool doRecord = true; + if (g_settings.recording_type == RECORDING_FILE) { + strcpy(recDir, (preselectedDir != NULL) ? preselectedDir : g_settings.network_nfs_recordingdir); + // data == NULL -> called after stream problem so do not show a dialog again + // but which dir has been chosen? + if(preselectedDir == NULL && (g_settings.recording_choose_direct_rec_dir == 2)) { + CFileBrowser b; + b.Dir_Mode=true; + refreshGui = true; + if (b.exec(g_settings.network_nfs_recordingdir)) { + strcpy(recDir, b.getSelectedFile()->Name.c_str()); + } + else doRecord = false; + } + else if(preselectedDir == NULL && (g_settings.recording_choose_direct_rec_dir == 1)) { + int userDecision = -1; + + CMountChooser recDirs(LOCALE_TIMERLIST_RECORDING_DIR,NEUTRINO_ICON_SETTINGS,&userDecision,NULL,g_settings.network_nfs_recordingdir); + if (recDirs.hasItem()) { + recDirs.exec(NULL,""); + refreshGui = true; + if (userDecision != -1) { + //recDir = g_settings.network_nfs_local_dir[userDecision]; + strcpy(recDir, g_settings.network_nfs_local_dir[userDecision]); + if (!CFSMounter::isMounted(g_settings.network_nfs_local_dir[userDecision])) + { + CFSMounter::MountRes mres = + CFSMounter::mount(g_settings.network_nfs_ip[userDecision].c_str(), + g_settings.network_nfs_dir[userDecision], + g_settings.network_nfs_local_dir[userDecision], + (CFSMounter::FSType) g_settings.network_nfs_type[userDecision], + g_settings.network_nfs_username[userDecision], + g_settings.network_nfs_password[userDecision], + g_settings.network_nfs_mount_options1[userDecision], + g_settings.network_nfs_mount_options2[userDecision]); + if (mres != CFSMounter::MRES_OK) { + //recDir = g_settings.network_nfs_local_dir[userDecision]; + //strcpy(recDir, g_settings.network_nfs_local_dir[userDecision]); + doRecord = false; + const char * merr = mntRes2Str(mres); + int msglen = strlen(merr) + strlen(g_settings.network_nfs_local_dir[userDecision]) + 7; + char msg[msglen]; + strcpy(msg,merr); + strcat(msg,"\nDir: "); + strcat(msg,g_settings.network_nfs_local_dir[userDecision]); + + ShowMsgUTF(LOCALE_MESSAGEBOX_ERROR, msg, + CMessageBox::mbrBack, CMessageBox::mbBack,NEUTRINO_ICON_ERROR, 450, 10); // UTF-8 + } + } + } else { + doRecord = false; + } + } else { + printf("[neutrino.cpp] no network devices available\n"); + doRecord = false; + } + } + (static_cast(recordingdevice))->Directory = recDir; +printf("CNeutrinoApp::doGuiRecord: start to dir %s\n", recDir); + } + if(!doRecord || (CVCRControl::getInstance()->Record(&eventinfo)==false)) { + recordingstatus=0; + if(doRecord) + return true;// try to refresh gui if record was not ok ? + return refreshGui; + } + else if (addTimer) { + time_t now = time(NULL); + recording_id = g_Timerd->addImmediateRecordTimerEvent(eventinfo.channel_id, now, now+g_settings.record_hours*60*60, eventinfo.epgID, eventinfo.epg_starttime, eventinfo.apids); + } + } else { + g_Timerd->stopTimerEvent(recording_id); + startNextRecording(); + } + return refreshGui; + } + else + puts("[neutrino.cpp] no recording devices"); + return false; +} + +#define LCD_UPDATE_TIME_RADIO_MODE (6 * 1000 * 1000) +#define LCD_UPDATE_TIME_TV_MODE (60 * 1000 * 1000) + +void CNeutrinoApp::SendSectionsdConfig(void) +{ + CSectionsdClient::epg_config config; + config.scanMode = scanSettings.scanSectionsd; + config.epg_cache = atoi(g_settings.epg_cache.c_str()); + config.epg_old_events = atoi(g_settings.epg_old_events.c_str()); + config.epg_max_events = atoi(g_settings.epg_max_events.c_str()); + config.epg_extendedcache = atoi(g_settings.epg_extendedcache.c_str()); + config.epg_dir = g_settings.epg_dir; + config.network_ntpserver = g_settings.network_ntpserver; + config.network_ntprefresh = atoi(g_settings.network_ntprefresh.c_str()); + config.network_ntpenable = g_settings.network_ntpenable; + g_Sectionsd->setConfig(config); +} + +void CNeutrinoApp::InitZapper() +{ + struct stat my_stat; + + g_InfoViewer->start(); + SendSectionsdConfig(); + if (g_settings.epg_save){ + if(stat(g_settings.epg_dir.c_str(), &my_stat) == 0) + g_Sectionsd->readSIfromXML(g_settings.epg_dir.c_str()); + } + firstChannel(); + channelsInit(); + + if(firstchannel.mode == 't') { + tvMode(false); + } else { + g_RCInput->killTimer(g_InfoViewer->lcdUpdateTimer); + g_InfoViewer->lcdUpdateTimer = g_RCInput->addTimer( LCD_UPDATE_TIME_RADIO_MODE, false ); + radioMode(false); + } + if(g_settings.cacheTXT) + tuxtxt_init(); + if(channelList->getSize() && live_channel_id) { + channelList->adjustToChannelID(live_channel_id); + CVFD::getInstance ()->showServicename(channelList->getActiveChannelName()); + g_Sectionsd->setPauseScanning(false); + g_Sectionsd->setServiceChanged(live_channel_id&0xFFFFFFFFFFFFULL, true ); + g_Zapit->getPIDS(g_RemoteControl->current_PIDs); + if(g_settings.cacheTXT) + if(g_RemoteControl->current_PIDs.PIDs.vtxtpid != 0) + tuxtxt_start(g_RemoteControl->current_PIDs.PIDs.vtxtpid); + g_RCInput->postMsg(NeutrinoMessages::SHOW_INFOBAR, 0); + } +} + +void CNeutrinoApp::setupRecordingDevice(void) +{ + if (g_settings.recording_type == RECORDING_SERVER) + { + unsigned int port; + sscanf(g_settings.recording_server_port, "%u", &port); + + recordingdevice = new CVCRControl::CServerDevice(g_settings.recording_stopplayback, g_settings.recording_stopsectionsd, g_settings.recording_server_ip.c_str(), port); + CVCRControl::getInstance()->registerDevice(recordingdevice); + } + else if (g_settings.recording_type == RECORDING_FILE) + { + unsigned int splitsize, ringbuffers; + sscanf(g_settings.recording_splitsize, "%u", &splitsize); + sscanf(g_settings.recording_ringbuffers, "%u", &ringbuffers); + + recordingdevice = new CVCRControl::CFileDevice(g_settings.recording_stopplayback, g_settings.recording_stopsectionsd, g_settings.network_nfs_recordingdir, splitsize, g_settings.recording_use_o_sync, g_settings.recording_use_fdatasync, g_settings.recording_stream_vtxt_pid, g_settings.recording_stream_pmt_pid, ringbuffers); + + CVCRControl::getInstance()->registerDevice(recordingdevice); + } + else if(g_settings.recording_type == RECORDING_VCR) + { + recordingdevice = new CVCRControl::CVCRDevice((g_settings.recording_vcr_no_scart == 0)); + + CVCRControl::getInstance()->registerDevice(recordingdevice); + } + else + { + if (CVCRControl::getInstance()->isDeviceRegistered()) + CVCRControl::getInstance()->unregisterDevice(); + } +} + +//CMenuWidget moviePlayer (LOCALE_MOVIEPLAYER_HEAD , "streaming.raw" ); +CMenuWidget * gmoviePlayer; +#if 0 +CPipSetup * g_Pip0; +#endif +#include "videosettings.h" +extern CVideoSettings * videoSettings; +extern CMenuOptionStringChooser* tzSelect; + +void CISendMessage(uint32_t msg, uint32_t data) +{ + g_RCInput->postMsg(msg, data); +} + +int CNeutrinoApp::run(int argc, char **argv) +{ + CmdParser(argc, argv); + + CHintBox * hintBox; + + int loadSettingsErg = loadSetup(NEUTRINO_SETTINGS_FILE); + + bool display_language_selection; + CLocaleManager::loadLocale_ret_t loadLocale_ret = g_Locale->loadLocale(g_settings.language); + if (loadLocale_ret == CLocaleManager::NO_SUCH_LOCALE) + { + strcpy(g_settings.language, "english"); + loadLocale_ret = g_Locale->loadLocale(g_settings.language); + display_language_selection = true; + } + else + display_language_selection = false; + + SetupFonts(); + SetupTiming(); + colorSetupNotifier = new CColorSetupNotifier; + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_NEUTRINO_STARTING)); + hintBox->paint(); + CVFD::getInstance()->init(font.filename, font.name); + + CVFD::getInstance()->Clear(); + CVFD::getInstance()->ShowText((char *) g_Locale->getText(LOCALE_NEUTRINO_STARTING)); + + init_cs_api(); + + pthread_create (&zapit_thread, NULL, zapit_main_thread, (void *) g_settings.video_Mode); + audioSetupNotifier = new CAudioSetupNotifier; + + while(!zapit_ready) + usleep(0); + printf("zapit ready\n\n"); + + audioDecoder->SetSRS(g_settings.srs_enable, g_settings.srs_nmgr_enable, g_settings.srs_algo, g_settings.srs_ref_volume); + audioDecoder->setVolume(g_settings.current_volume, g_settings.current_volume); + audioDecoder->SetHdmiDD(g_settings.hdmi_dd ? true : false); + audioDecoder->SetSpdifDD(g_settings.spdif_dd ? true : false); + videoDecoder->SetDBDR(g_settings.video_dbdr); + audioSetupNotifier->changeNotify(LOCALE_AUDIOMENU_AVSYNC, NULL); + + if(display_language_selection) + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/start.jpg"); + + powerManager = new cPowerManager; + + if (powerManager) { + if (!powerManager->Open()) + printf("opening powermanager failed\n"); + } + + cpuFreq = new cCpuFreqManager(); + cpuFreq->SetCpuFreq(g_settings.cpufreq * 1000 * 1000); + + + dvbsub_init(); + + pthread_create (&timer_thread, NULL, timerd_main_thread, (void *) NULL); + pthread_create (&nhttpd_thread, NULL, nhttpd_main_thread, (void *) NULL); + + pthread_create (&stream_thread, NULL, streamts_main_thread, (void *) NULL); + + hintBox->hide(); //FIXME + hintBox->paint(); + +#ifndef DISABLE_SECTIONSD + pthread_create (§ions_thread, NULL, sectionsd_main_thread, (void *) NULL); +#endif + g_Zapit = new CZapitClient; + + if (!scanSettings.loadSettings(NEUTRINO_SCAN_SETTINGS_FILE, (g_info.delivery_system = g_Zapit->getDeliverySystem()))) { + dprintf(DEBUG_NORMAL, "Loading of scan settings failed. Using defaults.\n"); + } + + + CVFD::getInstance()->showVolume(g_settings.current_volume); + CVFD::getInstance()->setMuted(current_muted); + + InfoClock = new CInfoClock(); + if(g_settings.mode_clock) + InfoClock->StartClock(); + + g_RCInput = new CRCInput; + + g_Sectionsd = new CSectionsdClient; + //g_Sectionsd->setServiceChanged(live_channel_id &0xFFFFFFFFFFFFULL, false); + g_Timerd = new CTimerdClient; + + g_RemoteControl = new CRemoteControl; + g_EpgData = new CEpgData; + g_InfoViewer = new CInfoViewer; + g_EventList = new EventList; + g_volscale = new CScale(200, 15, 50, 100, 80, true); + g_CamHandler = new CCAMMenuHandler(); + g_CamHandler->init(); + + audio_menu = new CAudioSelectMenuHandler; + + g_PluginList = new CPlugins; + g_PluginList->setPluginDir(PLUGINDIR); + g_PicViewer = new CPictureViewer(); + // mount shares before scanning for plugins + CFSMounter::automount(); + //load Pluginlist before main menu (only show script menu if at least one script is available + g_PluginList->loadPlugins(); + + APIDChanger = new CAPIDChangeExec; + NVODChanger = new CNVODChangeExec; + StreamFeaturesChanger = new CStreamFeaturesChangeExec; + MoviePluginChanger = new CMoviePluginChangeExec; + OnekeyPluginChanger = new COnekeyPluginChangeExec; + MyIPChanger = new CIPChangeNotifier; + ConsoleDestinationChanger = new CConsoleDestChangeNotifier; + rcLock = new CRCLock(); + //USERMENU + Timerlist = new CTimerList; + + // setup recording device + if (g_settings.recording_type != RECORDING_OFF) + setupRecordingDevice(); + + dprintf( DEBUG_NORMAL, "menue setup\n"); + + c_SMSKeyInput = new SMSKeyInput(); + //Main settings + CMenuWidget mainMenu (LOCALE_MAINMENU_HEAD , "mainmenue.raw" ); + CMenuWidget mainSettings (LOCALE_MAINSETTINGS_HEAD , NEUTRINO_ICON_SETTINGS); + CMenuWidget languageSettings (LOCALE_LANGUAGESETUP_HEAD , "language.raw" ); + CMenuWidget audioSettings (LOCALE_AUDIOMENU_HEAD , "audio.raw" ); + CMenuWidget parentallockSettings(LOCALE_PARENTALLOCK_PARENTALLOCK , "lock.raw" , 500); + CMenuWidget networkSettings (LOCALE_NETWORKMENU_HEAD , "network.raw" ); + CMenuWidget recordingSettings (LOCALE_RECORDINGMENU_HEAD , "recording.raw" ); + CMenuWidget streamingSettings (LOCALE_STREAMINGMENU_HEAD , "streaming.raw" ); + //CMenuWidget colorSettings (LOCALE_COLORMENU_HEAD , "colors.raw" ); + CMenuWidget colorSettings (LOCALE_MAINSETTINGS_OSD , "colors.raw" ); + CMenuWidget fontSettings (LOCALE_FONTMENU_HEAD , "colors.raw" ); + CMenuWidget lcdSettings (LOCALE_LCDMENU_HEAD , "lcd.raw" ); + //CMenuWidget keySettings (LOCALE_KEYBINDINGMENU_HEAD , "keybinding.raw" , 400); + CMenuWidget keySettings (LOCALE_MAINSETTINGS_KEYBINDING , "keybinding.raw" , 400); + CMenuWidget miscSettings (LOCALE_MISCSETTINGS_HEAD , NEUTRINO_ICON_SETTINGS); + CMenuWidget audioplPicSettings (LOCALE_AUDIOPLAYERPICSETTINGS_GENERAL, NEUTRINO_ICON_SETTINGS); + CMenuWidget scanSettings (LOCALE_SERVICEMENU_SCANTS , NEUTRINO_ICON_SETTINGS); + CMenuWidget service (LOCALE_SERVICEMENU_HEAD , NEUTRINO_ICON_SETTINGS); + CMenuWidget moviePlayer (LOCALE_MOVIEPLAYER_HEAD , "streaming.raw" ); + gmoviePlayer = &moviePlayer; + + InitMainMenu(mainMenu, mainSettings, audioSettings, parentallockSettings, networkSettings, recordingSettings, + colorSettings, lcdSettings, keySettings, languageSettings, miscSettings, + service, fontSettings, audioplPicSettings, streamingSettings, moviePlayer); + + InitServiceSettings(service, scanSettings); + InitLanguageSettings(languageSettings); + InitAudioplPicSettings(audioplPicSettings); + InitMiscSettings(miscSettings); + InitAudioSettings(audioSettings, audioSetupNotifier); + InitParentalLockSettings(parentallockSettings); + InitScanSettings(scanSettings); + + dprintf( DEBUG_NORMAL, "registering as event client\n"); +#if 0 + g_Controld->registerEvent(CControldClient::EVT_MUTECHANGED, 222, NEUTRINO_UDS_NAME); + g_Controld->registerEvent(CControldClient::EVT_VOLUMECHANGED, 222, NEUTRINO_UDS_NAME); + g_Controld->registerEvent(CControldClient::EVT_MODECHANGED, 222, NEUTRINO_UDS_NAME); + g_Controld->registerEvent(CControldClient::EVT_VCRCHANGED, 222, NEUTRINO_UDS_NAME); +#endif + + g_Sectionsd->registerEvent(CSectionsdClient::EVT_TIMESET, 222, NEUTRINO_UDS_NAME); + g_Sectionsd->registerEvent(CSectionsdClient::EVT_GOT_CN_EPG, 222, NEUTRINO_UDS_NAME); + g_Sectionsd->registerEvent(CSectionsdClient::EVT_SERVICES_UPDATE, 222, NEUTRINO_UDS_NAME); + g_Sectionsd->registerEvent(CSectionsdClient::EVT_BOUQUETS_UPDATE, 222, NEUTRINO_UDS_NAME); + g_Sectionsd->registerEvent(CSectionsdClient::EVT_WRITE_SI_FINISHED, 222, NEUTRINO_UDS_NAME); + +#define ZAPIT_EVENT_COUNT 29 + const CZapitClient::events zapit_event[ZAPIT_EVENT_COUNT] = + { + CZapitClient::EVT_ZAP_COMPLETE, + CZapitClient::EVT_ZAP_COMPLETE_IS_NVOD, + CZapitClient::EVT_ZAP_FAILED, + CZapitClient::EVT_ZAP_SUB_COMPLETE, + CZapitClient::EVT_ZAP_SUB_FAILED, + CZapitClient::EVT_ZAP_MOTOR, + CZapitClient::EVT_ZAP_CA_CLEAR, + CZapitClient::EVT_ZAP_CA_LOCK, + CZapitClient::EVT_ZAP_CA_FTA, + CZapitClient::EVT_ZAP_CA_ID, + CZapitClient::EVT_RECORDMODE_ACTIVATED, + CZapitClient::EVT_RECORDMODE_DEACTIVATED, + CZapitClient::EVT_SCAN_COMPLETE, + CZapitClient::EVT_SCAN_FAILED, + CZapitClient::EVT_SCAN_NUM_TRANSPONDERS, + CZapitClient::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS, + CZapitClient::EVT_SCAN_REPORT_FREQUENCY, + CZapitClient::EVT_SCAN_REPORT_FREQUENCYP, + CZapitClient::EVT_SCAN_SATELLITE, + CZapitClient::EVT_SCAN_NUM_CHANNELS, + CZapitClient::EVT_SCAN_PROVIDER, + CZapitClient::EVT_BOUQUETS_CHANGED, + CZapitClient::EVT_SERVICES_CHANGED, + CZapitClient::EVT_SCAN_SERVICENAME, + CZapitClient::EVT_SCAN_FOUND_A_CHAN, + CZapitClient::EVT_SCAN_FOUND_TV_CHAN, + CZapitClient::EVT_SCAN_FOUND_RADIO_CHAN, + CZapitClient::EVT_SCAN_FOUND_DATA_CHAN, + CZapitClient::EVT_SDT_CHANGED + }; + + for (int i = 0; i < ZAPIT_EVENT_COUNT; i++) + g_Zapit->registerEvent(zapit_event[i], 222, NEUTRINO_UDS_NAME); + + g_Timerd->registerEvent(CTimerdClient::EVT_ANNOUNCE_SHUTDOWN, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_SHUTDOWN, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_ANNOUNCE_NEXTPROGRAM, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_NEXTPROGRAM, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_STANDBY_ON, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_STANDBY_OFF, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_ANNOUNCE_RECORD, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_RECORD_START, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_RECORD_STOP, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_ANNOUNCE_ZAPTO, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_ZAPTO, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_SLEEPTIMER, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_ANNOUNCE_SLEEPTIMER, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_REMIND, 222, NEUTRINO_UDS_NAME); + g_Timerd->registerEvent(CTimerdClient::EVT_EXEC_PLUGIN, 222, NEUTRINO_UDS_NAME); + + InitNetworkSettings(networkSettings); + InitKeySettings(keySettings); + InitFontSettings(fontSettings); + InitColorSettings(colorSettings, fontSettings); + + if (display_language_selection) { + hintBox->hide(); + int ret = languageSettings.exec(NULL, ""); + + if(ret != menu_return::RETURN_EXIT_ALL) + videoSettings->exec(NULL, ""); + if(ret != menu_return::RETURN_EXIT_ALL) + colorSettings.exec(NULL, ""); + if(ret != menu_return::RETURN_EXIT_ALL) + if(tzSelect) + tzSelect->exec(NULL); + if(ret != menu_return::RETURN_EXIT_ALL) + networkSettings.exec(NULL, ""); + if(ret != menu_return::RETURN_EXIT_ALL) + scanSettings.exec(NULL, ""); + + videoDecoder->StopPicture(); + } + + if(loadSettingsErg) { + hintBox->hide(); + dprintf(DEBUG_INFO, "config file or options missing\n"); + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, loadSettingsErg == 1 ? g_Locale->getText(LOCALE_SETTINGS_NOCONFFILE) + : g_Locale->getText(LOCALE_SETTINGS_MISSINGOPTIONSCONFFILE)); + configfile.setModifiedFlag(true); + saveSetup(NEUTRINO_SETTINGS_FILE); + } +#if 1 // FIXME + system("mkdir /media/sda1 2> /dev/null"); + system("mount /media/sda1 2> /dev/null"); + system("mkdir /media/sdb1 2> /dev/null"); + system("mount /media/sdb1 2> /dev/null"); + CHDDDestExec * hdd = new CHDDDestExec(); + hdd->exec(NULL, ""); +#endif + + InitZapper(); + InitRecordingSettings(recordingSettings); + InitStreamingSettings(streamingSettings); + InitLcdSettings(lcdSettings); + + AudioMute( current_muted, true); + + SHTDCNT::getInstance()->init(); + + hintBox->hide(); + delete hintBox; + + cDvbCi::getInstance()->SetHook(CISendMessage); + RealRun(mainMenu); + + ExitRun(true); + + return 0; +} + +int tuxtx_main(int _rc, void * _fb, int pid, int x, int y, int w, int h); + +void CNeutrinoApp::quickZap(int msg) +{ + if((bouquetList != NULL) && !(bouquetList->Bouquets.empty())) + bouquetList->Bouquets[bouquetList->getActiveBouquetNumber()]->channelList->quickZap(msg, g_settings.zap_cycle); + else + channelList->quickZap(msg); +} + +void CNeutrinoApp::RealRun(CMenuWidget &mainMenu) +{ + neutrino_msg_t msg; + neutrino_msg_data_t data; + + dprintf(DEBUG_NORMAL, "initialized everything\n"); + + g_PluginList->startPlugin("startup.cfg"); + if (!g_PluginList->getScriptOutput().empty()) { + ShowMsgUTF(LOCALE_PLUGINS_RESULT, g_PluginList->getScriptOutput(), CMessageBox::mbrBack,CMessageBox::mbBack,NEUTRINO_ICON_SHELL); + } + g_RCInput->clearRCMsg(); + if(g_settings.power_standby) + standbyMode(true); + + while( true ) { + g_RCInput->getMsg(&msg, &data, 100); // 10 secs.. + + if( ( mode == mode_tv ) || ( ( mode == mode_radio ) ) ) { + if( (msg == NeutrinoMessages::SHOW_EPG) /* || (msg == CRCInput::RC_info) */ ) { + //g_EpgData->show( g_Zapit->getCurrentServiceID() ); + g_EpgData->show(live_channel_id); + } + else if( msg == CRCInput::RC_epg ) { + g_EventList->exec(live_channel_id, channelList->getActiveChannelName()); + } + else if( msg == CRCInput::RC_text) { + g_RCInput->clearRCMsg(); + if(g_settings.mode_clock) + InfoClock->StopClock(); + + tuxtx_main(g_RCInput->getFileHandle(), frameBuffer->getFrameBufferPointer(), g_RemoteControl->current_PIDs.PIDs.vtxtpid, + frameBuffer->getScreenX(), frameBuffer->getScreenY(), frameBuffer->getScreenWidth(), frameBuffer->getScreenHeight()); + + frameBuffer->paintBackground(); + //if(!g_settings.cacheTXT) + // tuxtxt_stop(); + g_RCInput->clearRCMsg(); + AudioMute(current_muted, true); + if(g_settings.mode_clock) + InfoClock->StartClock(); + } + else if( msg == CRCInput::RC_setup ) { + if(!g_settings.minimode) { + dvbsub_pause(); + if(g_settings.mode_clock) + InfoClock->StopClock(); + mainMenu.exec(NULL, ""); + // restore mute symbol + AudioMute(current_muted, true); + if(g_settings.mode_clock) + InfoClock->StartClock(); + dvbsub_start(0); + saveSetup(NEUTRINO_SETTINGS_FILE); + } + } + else if( msg == (neutrino_msg_t) g_settings.key_tvradio_mode ) { + if( mode == mode_tv ) + radioMode(); + else if( mode == mode_radio ) + tvMode(); + } + else if( ((msg == CRCInput::RC_tv) || (msg == CRCInput::RC_radio)) && ((neutrino_msg_t)g_settings.key_tvradio_mode == CRCInput::RC_nokey)) { + if(mode == mode_radio ) + tvMode(); + else if(mode == mode_tv) + radioMode(); + } + else if( ( msg == (neutrino_msg_t) g_settings.key_quickzap_up ) || ( msg == (neutrino_msg_t) g_settings.key_quickzap_down ) ) + { + //quickzap + quickZap(msg); + } + else if( msg == (neutrino_msg_t) g_settings.key_subchannel_up ) { + if(g_RemoteControl->subChannels.size() > 0) { + g_RemoteControl->subChannelUp(); + g_InfoViewer->showSubchan(); + } else + quickZap( msg ); + } + else if( msg == (neutrino_msg_t) g_settings.key_subchannel_down ) { + if(g_RemoteControl->subChannels.size()> 0) { + g_RemoteControl->subChannelDown(); + g_InfoViewer->showSubchan(); + } else + quickZap( msg ); + } + else if( msg == (neutrino_msg_t) g_settings.key_zaphistory ) { + // Zap-History "Bouquet" + if(g_settings.mode_clock && g_settings.key_zaphistory == CRCInput::RC_home) { + InfoClock->StopClock(); + g_settings.mode_clock=false; + } else { + dvbsub_pause(); + int res = channelList->numericZap( msg ); + if(res < 0) + dvbsub_start(0); + } + } + else if( msg == (neutrino_msg_t) g_settings.key_lastchannel ) { + // Quick Zap + channelList->numericZap( msg ); + } + else if( msg == (neutrino_msg_t) g_settings.key_plugin ) { + g_PluginList->start_plugin_by_name(g_settings.onekey_plugin.c_str(), 0); + } + else if( (msg == (neutrino_msg_t) g_settings.key_timeshift) || msg == CRCInput::RC_rewind) { + if((g_settings.recording_type == RECORDING_FILE) && !g_settings.minimode) { +printf("[neutrino] timeshift try, recordingstatus %d, rec dir %s, timeshift dir %s temp timeshift %d ...\n", recordingstatus, g_settings.network_nfs_recordingdir, timeshiftDir, g_settings.temp_timeshift); + std::string tmode; + if(msg == CRCInput::RC_rewind) + tmode = "rtimeshift"; // rewind + else if(recordingstatus && msg == (neutrino_msg_t) g_settings.key_timeshift) + tmode = "ptimeshift"; // already recording, pause + else + tmode = "timeshift"; // record just started + + if(g_RemoteControl->is_video_started) { + if(recordingstatus) { + //dvbsub_pause(); + moviePlayerGui->exec(NULL, tmode); + //dvbsub_start(0); + } else if(msg != CRCInput::RC_rewind) { + //dvbsub_pause(); + if(g_settings.temp_timeshift) { + startAutoRecord(true); + } else { + recordingstatus = 1; + doGuiRecord(g_settings.network_nfs_recordingdir, true); + } + if(recordingstatus) { + //dvbsub_pause(); + moviePlayerGui->exec(NULL, tmode); + //dvbsub_start(0); + } + } + } + } + } + else if( msg == CRCInput::RC_record || msg == CRCInput::RC_stop ) { +printf("[neutrino] direct record\n"); + if(recordingstatus) { + if(ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_SHUTDOWN_RECODING_QUERY, + CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbNo, NULL, 450, 30, true) == CMessageBox::mbrYes) + g_Timerd->stopTimerEvent(recording_id); + } else if(msg != CRCInput::RC_stop ) { + recordingstatus = 1; + doGuiRecord(g_settings.network_nfs_recordingdir, true); + } + } + else if( msg == CRCInput::RC_red ) { + showUserMenu(SNeutrinoSettings::BUTTON_RED); + } + else if( (msg == CRCInput::RC_green) || ((msg == CRCInput::RC_audio) && !g_settings.audio_run_player) ) + { + showUserMenu(SNeutrinoSettings::BUTTON_GREEN); + } + else if( msg == CRCInput::RC_yellow ) { // NVODs + showUserMenu(SNeutrinoSettings::BUTTON_YELLOW); + } + else if( msg == CRCInput::RC_blue ) { + showUserMenu(SNeutrinoSettings::BUTTON_BLUE); + } + else if( (msg == CRCInput::RC_audio) && g_settings.audio_run_player) { + dvbsub_pause(); + audioPlayer->exec(NULL, ""); + dvbsub_start(0); + } + else if( msg == CRCInput::RC_video || msg == CRCInput::RC_play ) { + bool show = true; + if ((g_settings.parentallock_prompt == PARENTALLOCK_PROMPT_ONSIGNAL) || (g_settings.parentallock_prompt == PARENTALLOCK_PROMPT_CHANGETOLOCKED)) { + CZapProtection* zapProtection = new CZapProtection( g_settings.parentallock_pincode, 0x100 ); + show = zapProtection->check(); + delete zapProtection; + } + if(show) { + //dvbsub_pause(); + if( mode == mode_radio ) + videoDecoder->StopPicture(); + moviePlayerGui->exec(NULL, "tsmoviebrowser"); + if( mode == mode_radio ) + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/radiomode.jpg"); + //dvbsub_start(0); + } + } + else if (CRCInput::isNumeric(msg) && g_RemoteControl->director_mode ) { + g_RemoteControl->setSubChannel(CRCInput::getNumericValue(msg)); + g_InfoViewer->showSubchan(); + } + else if (CRCInput::isNumeric(msg)) { + channelList->numericZap( msg ); + } + else if( ( msg == CRCInput::RC_help ) || ( msg == CRCInput::RC_info) || + ( msg == NeutrinoMessages::SHOW_INFOBAR ) ) + { + bool show_info = ((msg != NeutrinoMessages::SHOW_INFOBAR) || (g_InfoViewer->is_visible || g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] != 0)); + // turn on LCD display + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + + // show Infoviewer + if(show_info && channelList->getSize()) { + dvbsub_pause(); + g_InfoViewer->showTitle(channelList->getActiveChannelNumber(), channelList->getActiveChannelName(), channelList->getActiveSatellitePosition(), channelList->getActiveChannel_ChannelID()); // UTF-8 + dvbsub_start(0); + } + } +#if 0 + else if( msg == CRCInput::RC_shift_blue ) { + if(CVFD::getInstance()->has_lcd) { + standbyMode( true ); + //this->rcLock->exec(NULL,CRCLock::NO_USER_INPUT); + this->rcLock->lockBox(); + standbyMode( false ); + } + } + else if( msg == CRCInput::RC_shift_tv ) { + if(CVFD::getInstance()->has_lcd) { // FIXME hack for 5xxx dreams + scartMode( true ); + //this->rcLock->exec(NULL,CRCLock::NO_USER_INPUT); + this->rcLock->lockBox(); + scartMode( false ); + } + } + else if( msg == CRCInput::RC_shift_red) { + if(CVFD::getInstance()->has_lcd) { // FIXME hack for 5xxx dreams + g_settings.minimode = !g_settings.minimode; + saveSetup(NEUTRINO_SETTINGS_FILE); + } + } else if( msg == CRCInput::RC_shift_radio) { + CVCRControl::getInstance()->Screenshot(g_RemoteControl->current_channel_id); + } +#endif + else { + if (msg == CRCInput::RC_home) { + if(g_settings.mode_clock && g_settings.key_zaphistory == CRCInput::RC_home) { + InfoClock->StopClock(); + g_settings.mode_clock=false; + } + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + } + handleMsg(msg, data); + } + } + else { + // mode == mode_scart + if( msg == CRCInput::RC_home ) { + if( mode == mode_scart ) { + //wenn VCR Aufnahme dann stoppen + if(CVCRControl::getInstance()->isDeviceRegistered()) { + if ((CVCRControl::getInstance()->Device->getDeviceType() == CVCRControl::DEVICE_VCR) && + (CVCRControl::getInstance()->getDeviceState() == CVCRControl::CMD_VCR_RECORD || + CVCRControl::getInstance()->getDeviceState() == CVCRControl::CMD_VCR_PAUSE)) + { + CVCRControl::getInstance()->Stop(); + recordingstatus=0; + startNextRecording(); + } + } + // Scart-Mode verlassen + scartMode( false ); + } + } + else { + handleMsg(msg, data); + } + } + } +} + +int CNeutrinoApp::handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data) +{ + int res = 0; +//printf("[neutrino] handleMsg %X data %X\n", msg, data); fflush(stdout); + + if(msg == NeutrinoMessages::EVT_ZAP_COMPLETE) { + g_Zapit->getAudioMode(&g_settings.audio_AnalogMode); + if(g_settings.audio_AnalogMode < 0 || g_settings.audio_AnalogMode > 2) + g_settings.audio_AnalogMode = 0; +#if 0 // per-channel auto volume save/restore + unsigned int volume; + g_Zapit->getVolume(&volume, &volume); + current_volume = 100 - volume*100/63; + printf("zapit volume %d new current %d mode %d\n", volume, current_volume, g_settings.audio_AnalogMode); + setvol(current_volume,(g_settings.audio_avs_Control)); +#endif + if(shift_timer) { + g_RCInput->killTimer (shift_timer); + shift_timer = 0; + } + if (!recordingstatus && g_settings.auto_timeshift) { + int delay = g_settings.auto_timeshift; + shift_timer = g_RCInput->addTimer( delay*1000*1000, true ); + g_InfoViewer->handleMsg(NeutrinoMessages::EVT_RECORDMODE, 1); + } + + if(scrambled_timer) { + g_RCInput->killTimer(scrambled_timer); + scrambled_timer = 0; + } + scrambled_timer = g_RCInput->addTimer(10*1000*1000, true); + } + if ((msg == NeutrinoMessages::EVT_TIMER)) { + if(data == shift_timer) { + shift_timer = 0; + startAutoRecord(true); + return messages_return::handled; + } else if(data == scrambled_timer) { + scrambled_timer = 0; + if(videoDecoder->getBlank() && videoDecoder->getPlayState()) { + const char * text = g_Locale->getText(LOCALE_SCRAMBLED_CHANNEL); + ShowHintUTF (LOCALE_MESSAGEBOX_INFO, text, g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth (text, true) + 10, 5); + } + return messages_return::handled; + + } + } + + res = res | g_RemoteControl->handleMsg(msg, data); + res = res | g_InfoViewer->handleMsg(msg, data); + res = res | channelList->handleMsg(msg, data); + + //printf("[neutrino] handleMsg %X unhandled by others\n", msg); fflush(stdout); + if( res != messages_return::unhandled ) { + if( ( msg>= CRCInput::RC_WithData ) && ( msg< CRCInput::RC_WithData+ 0x10000000 ) ) + delete (unsigned char*) data; + return( res & ( 0xFFFFFFFF - messages_return::unhandled ) ); + } + + /* we assume g_CamHandler free/delete data if needed */ + res = g_CamHandler->handleMsg(msg, data); + if( res != messages_return::unhandled ) { + return(res & (0xFFFFFFFF - messages_return::unhandled)); + } + + /* ================================== KEYS ================================================ */ + if( msg == CRCInput::RC_ok || msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) { + if( (mode == mode_tv) || (mode == mode_radio) || (mode == mode_ts)) { + if(g_settings.mode_clock) + InfoClock->StopClock(); + + dvbsub_pause(); + + int nNewChannel = -1; + int old_num = 0; + int old_b = bouquetList->getActiveBouquetNumber(); + int old_mode = g_settings.channel_mode; + printf("************************* ZAP START: bouquetList %x size %d old_b %d\n", (int) bouquetList, bouquetList->Bouquets.size(), old_b);fflush(stdout); + + if(bouquetList->Bouquets.size()) { + old_num = bouquetList->Bouquets[old_b]->channelList->getActiveChannelNumber(); + } + + //if(msg == CRCInput::RC_ok && bouquetList->Bouquets.size()) + if(msg == CRCInput::RC_ok) + { + if(bouquetList->Bouquets.size() && bouquetList->Bouquets[old_b]->channelList->getSize() > 0) + nNewChannel = bouquetList->Bouquets[old_b]->channelList->exec();//with ZAP! + else + nNewChannel = bouquetList->exec(true); + } else if(msg == CRCInput::RC_sat) { + SetChannelMode(LIST_MODE_SAT); + nNewChannel = bouquetList->exec(true); + } else if(msg == CRCInput::RC_favorites) { + SetChannelMode(LIST_MODE_FAV); + nNewChannel = bouquetList->exec(true); + } +_repeat: + printf("************************* ZAP RES: nNewChannel %d\n", nNewChannel);fflush(stdout); + if(nNewChannel == -1) { // restore orig. bouquet and selected channel on cancel + SetChannelMode(old_mode); + bouquetList->activateBouquet(old_b, false); + if(bouquetList->Bouquets.size()) + bouquetList->Bouquets[old_b]->channelList->setSelected(old_num-1); + } + else if(nNewChannel == -3) { // list mode changed + printf("************************* ZAP NEW MODE: bouquetList %x size %d\n", (int) bouquetList, bouquetList->Bouquets.size());fflush(stdout); + nNewChannel = bouquetList->exec(true); + goto _repeat; + } + else if(nNewChannel == -4) { + if(old_b_id < 0) old_b_id = old_b; + g_Zapit->saveBouquets(); + } + + if(g_settings.mode_clock) + InfoClock->StartClock(); + if(mode == mode_tv) + dvbsub_start(0); + return messages_return::handled; + } + } + else if (msg == CRCInput::RC_standby) { + if (data == 0) { + neutrino_msg_t new_msg; + + /* Note: pressing the power button on the dbox (not the remote control) over 1 second */ + /* shuts down the system even if !g_settings.shutdown_real_rcdelay (see below) */ + gettimeofday(&standby_pressed_at, NULL); + + if ((mode != mode_standby) && (g_settings.shutdown_real)) { + new_msg = NeutrinoMessages::SHUTDOWN; + } + else { + new_msg = (mode == mode_standby) ? NeutrinoMessages::STANDBY_OFF : NeutrinoMessages::STANDBY_ON; + //printf("standby: new msg %X\n", new_msg); + if ((g_settings.shutdown_real_rcdelay)) { + neutrino_msg_t msg; + neutrino_msg_data_t data; + struct timeval endtime; + time_t seconds; + + int timeout = 0; + int timeout1 = 0; + + sscanf(g_settings.repeat_blocker, "%d", &timeout); + sscanf(g_settings.repeat_genericblocker, "%d", &timeout1); + + if (timeout1 > timeout) + timeout = timeout1; + + timeout += 500; + //printf("standby: timeout %d\n", timeout); + + while(true) { + g_RCInput->getMsg_ms(&msg, &data, timeout); + + //printf("standby: input msg %X\n", msg); + if (msg == CRCInput::RC_timeout) + break; + + gettimeofday(&endtime, NULL); + seconds = endtime.tv_sec - standby_pressed_at.tv_sec; + if (endtime.tv_usec < standby_pressed_at.tv_usec) + seconds--; + //printf("standby: input seconds %d\n", seconds); + if (seconds >= 1) { + if (msg == CRCInput::RC_standby) + new_msg = NeutrinoMessages::SHUTDOWN; + break; + } + } + } + } + g_RCInput->postMsg(new_msg, 0); + return messages_return::cancel_all | messages_return::handled; + } + else /* data == 1: KEY_POWER released */ + if (standby_pressed_at.tv_sec != 0) /* check if we received a KEY_POWER pressed event before */ + { /* required for correct handling of KEY_POWER events of */ + /* the power button on the dbox (not the remote control) */ + struct timeval endtime; + gettimeofday(&endtime, NULL); + time_t seconds = endtime.tv_sec - standby_pressed_at.tv_sec; + if (endtime.tv_usec < standby_pressed_at.tv_usec) + seconds--; + if (seconds >= 1) { + g_RCInput->postMsg(NeutrinoMessages::SHUTDOWN, 0); + return messages_return::cancel_all | messages_return::handled; + } + } + } + else if ((msg == CRCInput::RC_plus) || (msg == CRCInput::RC_minus)) + { + setVolume(msg, (mode != mode_scart)); + return messages_return::handled; + } +#if 0 + else if ((msg == CRCInput::RC_left) || (msg == CRCInput::RC_right)) // FIXME temp, until new remote + { + setVolume(msg, (mode != mode_scart)); + return messages_return::handled; + } +#endif + else if( msg == CRCInput::RC_spkr ) { + if( mode == mode_standby ) { + //switch lcd off/on + CVFD::getInstance()->togglePower(); + } + else { + //mute + AudioMute( !current_muted, true); + } + return messages_return::handled; + } + else if( msg == CRCInput::RC_mode ) { + videoSettings->nextMode(); + return messages_return::handled; + } + else if( msg == CRCInput::RC_next ) { + videoSettings->next43Mode(); + return messages_return::handled; + } + else if( msg == CRCInput::RC_prev ) { + videoSettings->SwitchFormat(); + return messages_return::handled; + } + /* ================================== MESSAGES ================================================ */ + else if (msg == NeutrinoMessages::EVT_VOLCHANGED) { + //setVolume(msg, false, true); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::EVT_VCRCHANGED ) { + if (g_settings.vcr_AutoSwitch) { + if( data != VCR_STATUS_OFF ) + g_RCInput->postMsg( NeutrinoMessages::VCR_ON, 0 ); + else + g_RCInput->postMsg( NeutrinoMessages::VCR_OFF, 0 ); + } + return messages_return::handled | messages_return::cancel_info; + } + else if( msg == NeutrinoMessages::EVT_MUTECHANGED ) { + CControldMsg::commandMute* cmd = (CControldMsg::commandMute*) data; + if(cmd->type == (CControld::volume_type)g_settings.audio_avs_Control) + AudioMute( cmd->mute, true ); + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if( msg == NeutrinoMessages::EVT_SERVICESCHANGED ) { + channelsInit(); + channelList->adjustToChannelID(live_channel_id);//FIXME what if deleted ? + if(old_b_id >= 0) { + bouquetList->activateBouquet(old_b_id, false); + old_b_id = -1; + g_RCInput->postMsg(CRCInput::RC_ok, 0); + } + } + else if( msg == NeutrinoMessages::EVT_BOUQUETSCHANGED ) { + channelsInit(); + channelList->adjustToChannelID(live_channel_id);//FIXME what if deleted ? + return messages_return::handled; + } + else if( msg == NeutrinoMessages::EVT_RECORDMODE ) { + /* sent by rcinput, then got msg from zapit about record activated/deactivated */ + dprintf(DEBUG_DEBUG, "neutino - recordmode %s\n", ( data ) ? "on":"off" ); + if(!recordingstatus && was_record && (!data)) { + g_Zapit->setStandby(true); + was_record = 0; + if( mode == mode_standby ) + cpuFreq->SetCpuFreq(g_settings.standby_cpufreq * 1000 * 1000); + } + if( mode == mode_standby && data ) + was_record = 1; + recordingstatus = data; + if( ( !g_InfoViewer->is_visible ) && data && !autoshift) + g_RCInput->postMsg( NeutrinoMessages::SHOW_INFOBAR, 0 ); + + return messages_return::handled; + } + else if (msg == NeutrinoMessages::RECORD_START) { + if( mode == mode_standby ) + cpuFreq->SetCpuFreq(g_settings.cpufreq * 1000 * 1000); + + if(autoshift) { + stopAutoRecord(); + recordingstatus = 0; + } + puts("[neutrino.cpp] executing " NEUTRINO_RECORDING_START_SCRIPT "."); + if (system(NEUTRINO_RECORDING_START_SCRIPT) != 0) + perror(NEUTRINO_RECORDING_START_SCRIPT " failed"); + /* set nextRecordingInfo to current event (replace other scheduled recording if available) */ + /* Note: CTimerd::RecordingInfo is a class! + * => typecast to avoid destructor call */ + + if (nextRecordingInfo != NULL) + delete[] (unsigned char *) nextRecordingInfo; + + nextRecordingInfo = (CTimerd::RecordingInfo *) data; + startNextRecording(); + + return messages_return::handled | messages_return::cancel_all; + } + else if( msg == NeutrinoMessages::RECORD_STOP) { + if(((CTimerd::RecordingStopInfo*)data)->eventID == recording_id) + { // passendes stop zur Aufnahme + //CVCRControl * vcr_control = CVCRControl::getInstance(); + if (CVCRControl::getInstance()->isDeviceRegistered()) { + if ((CVCRControl::getInstance()->getDeviceState() == CVCRControl::CMD_VCR_RECORD) || + (CVCRControl::getInstance()->getDeviceState() == CVCRControl::CMD_VCR_PAUSE )) + { + CVCRControl::getInstance()->Stop(); + recordingstatus = 0; + autoshift = 0; + } + else + printf("falscher state\n"); + } + else + puts("[neutrino.cpp] no recording devices"); + + startNextRecording(); + + if (recordingstatus == 0) { + puts("[neutrino.cpp] executing " NEUTRINO_RECORDING_ENDED_SCRIPT "."); + if (system(NEUTRINO_RECORDING_ENDED_SCRIPT) != 0) + perror(NEUTRINO_RECORDING_ENDED_SCRIPT " failed"); + if( mode == mode_standby ) + cpuFreq->SetCpuFreq(g_settings.standby_cpufreq * 1000 * 1000); + } + } + else if(nextRecordingInfo!=NULL) { + if(((CTimerd::RecordingStopInfo*)data)->eventID == nextRecordingInfo->eventID) { + /* Note: CTimerd::RecordingInfo is a class! + * => typecast to avoid destructor call */ + delete[] (unsigned char *) nextRecordingInfo; + nextRecordingInfo=NULL; + } + } + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if( msg == NeutrinoMessages::ZAPTO) { + CTimerd::EventInfo * eventinfo; + eventinfo = (CTimerd::EventInfo *) data; + if(recordingstatus==0) { + bool isTVMode = g_Zapit->isChannelTVChannel(eventinfo->channel_id); + + dvbsub_stop(); + + if ((!isTVMode) && (mode != mode_radio)) { + radioMode(false); + } + else if (isTVMode && (mode != mode_tv)) { + tvMode(false); + } + channelList->zapTo_ChannelID(eventinfo->channel_id); + } + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if( msg == NeutrinoMessages::ANNOUNCE_ZAPTO) { + if( mode == mode_standby ) { + standbyMode( false ); + } + if( mode != mode_scart ) { + std::string name = g_Locale->getText(LOCALE_ZAPTOTIMER_ANNOUNCE); + + CTimerd::TimerList tmpTimerList; + CTimerdClient tmpTimerdClient; + + tmpTimerList.clear(); + tmpTimerdClient.getTimerList( tmpTimerList ); + + if(tmpTimerList.size() > 0) { + sort( tmpTimerList.begin(), tmpTimerList.end() ); + + CTimerd::responseGetTimer &timer = tmpTimerList[0]; + + CZapitClient Zapit; + name += "\n"; + + std::string zAddData = Zapit.getChannelName( timer.channel_id ); // UTF-8 + if( zAddData.empty()) { + zAddData = g_Locale->getText(LOCALE_TIMERLIST_PROGRAM_UNKNOWN); + } + + if(timer.epgID!=0) { + CEPGData epgdata; + zAddData += " :\n"; + //if (g_Sectionsd->getEPGid(timer.epgID, timer.epg_starttime, &epgdata)) + if (sectionsd_getEPGid(timer.epgID, timer.epg_starttime, &epgdata)) { + zAddData += epgdata.title; + } + else if(strlen(timer.epgTitle)!=0) { + zAddData += timer.epgTitle; + } + } + else if(strlen(timer.epgTitle)!=0) { + zAddData += timer.epgTitle; + } + + name += zAddData; + } + ShowHintUTF( LOCALE_MESSAGEBOX_INFO, name.c_str() ); + } + + return messages_return::handled; + } + else if( msg == NeutrinoMessages::ANNOUNCE_RECORD) { + system(NEUTRINO_RECORDING_TIMER_SCRIPT); + if( g_settings.recording_server_wakeup ) { + std::string command = "etherwake "; + command += g_settings.recording_server_mac; + if(system(command.c_str()) != 0) + perror("etherwake failed"); + } + if (g_settings.recording_type == RECORDING_FILE) { + char * recDir = ((CTimerd::RecordingInfo*)data)->recordingDir; + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) { + if (strcmp(g_settings.network_nfs_local_dir[i],recDir) == 0) { + printf("[neutrino] waking up %s (%s)\n",g_settings.network_nfs_ip[i].c_str(),recDir); + std::string command = "etherwake "; + command += g_settings.network_nfs_mac[i]; + if(system(command.c_str()) != 0) + perror("etherwake failed"); + break; + } + } + if(autoshift) { + stopAutoRecord(); + recordingstatus = 0; + } + if(has_hdd) { + system("(rm /media/sda1/.wakeup; touch /media/sda1/.wakeup; sync) > /dev/null 2> /dev/null &"); // wakeup hdd + } + } + if( g_settings.recording_zap_on_announce ) { + if(recordingstatus==0) { + dvbsub_stop(); //FIXME if same channel ? + t_channel_id channel_id=((CTimerd::RecordingInfo*)data)->channel_id; + g_Zapit->zapTo_serviceID_NOWAIT(channel_id); + } + } + delete[] (unsigned char*) data; + if( mode != mode_scart ) + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_RECORDTIMER_ANNOUNCE)); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::ANNOUNCE_SLEEPTIMER) { + if( mode != mode_scart ) + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_SLEEPTIMERBOX_ANNOUNCE)); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::SLEEPTIMER) { + if(g_settings.shutdown_real) + ExitRun(true); + else + standbyMode( true ); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::STANDBY_TOGGLE ) { + standbyMode( !(mode & mode_standby) ); + g_RCInput->clearRCMsg(); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::STANDBY_ON ) { + if( mode != mode_standby ) { + standbyMode( true ); + } + g_RCInput->clearRCMsg(); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::STANDBY_OFF ) { + if( mode == mode_standby ) { + standbyMode( false ); + } + g_RCInput->clearRCMsg(); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::ANNOUNCE_SHUTDOWN) { + if( mode != mode_scart ) + skipShutdownTimer = (ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_SHUTDOWNTIMER_ANNOUNCE, CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo, NULL, 450, 5) == CMessageBox::mbrYes); + } + else if( msg == NeutrinoMessages::SHUTDOWN ) { + if(!skipShutdownTimer) { + ExitRun(true); + } + else { + skipShutdownTimer=false; + } + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_POPUP) { + if (mode != mode_scart) + ShowHintUTF(LOCALE_MESSAGEBOX_INFO, (const char *) data); // UTF-8 + delete (unsigned char*) data; + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_EXTMSG) { + if (mode != mode_scart) + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, (const char *) data, CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO); // UTF-8 + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_RECORDING_ENDED) { + if (mode != mode_scart) { + neutrino_locale_t msgbody; + if ((* (stream2file_status2_t *) data).status == STREAM2FILE_STATUS_BUFFER_OVERFLOW) + msgbody = LOCALE_STREAMING_BUFFER_OVERFLOW; + else if ((* (stream2file_status2_t *) data).status == STREAM2FILE_STATUS_WRITE_OPEN_FAILURE) + msgbody = LOCALE_STREAMING_WRITE_ERROR_OPEN; + else if ((* (stream2file_status2_t *) data).status == STREAM2FILE_STATUS_WRITE_FAILURE) + msgbody = LOCALE_STREAMING_WRITE_ERROR; + else + goto skip_message; + + /* use a short timeout of only 5 seconds in case it was only a temporary network problem + * in case of STREAM2FILE_STATUS_IDLE we might even have to immediately start the next recording */ + ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, msgbody, CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO, 450, 5); + +skip_message: + ; + } + if ((* (stream2file_status2_t *) data).status != STREAM2FILE_STATUS_IDLE) { + /* + * note that changeNotify does not distinguish between LOCALE_MAINMENU_RECORDING_START and LOCALE_MAINMENU_RECORDING_STOP + * instead it checks the state of the variable recordingstatus + */ + /* restart recording */ + //FIXME doGuiRecord((*(stream2file_status2_t *) data).dir); + //changeNotify(LOCALE_MAINMENU_RECORDING_START, data); + } + + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if( msg == NeutrinoMessages::REMIND) { + std::string text = (char*)data; + std::string::size_type pos; + while((pos=text.find('/'))!= std::string::npos) + { + text[pos] = '\n'; + } + if( mode != mode_scart ) + ShowMsgUTF(LOCALE_TIMERLIST_TYPE_REMIND, text, CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO); // UTF-8 + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if (msg == NeutrinoMessages::LOCK_RC) { + this->rcLock->exec(NULL,CRCLock::NO_USER_INPUT); + return messages_return::handled; + } + else if( msg == NeutrinoMessages::CHANGEMODE ) { + int newmode = data & mode_mask; + + if((data & mode_mask)== mode_radio) { + if( mode != mode_radio ) { + if((data & norezap)==norezap) + radioMode(false); + else + radioMode(true); + } + } + if((data & mode_mask)== mode_tv) { + if( mode != mode_tv ) { + if((data & norezap)==norezap) + tvMode(false); + else + tvMode(true); + } + } + if((data &mode_mask)== mode_standby) { + if(mode != mode_standby) + standbyMode( true ); + } + if((data &mode_mask)== mode_audio) { + lastMode=mode; + mode=mode_audio; + } + if((data &mode_mask)== mode_pic) { + lastMode=mode; + mode=mode_pic; + } + if((data &mode_mask)== mode_ts) { + if(mode == mode_radio) + videoDecoder->StopPicture(); + lastMode=mode; + mode=mode_ts; + } + } + else if( msg == NeutrinoMessages::VCR_ON ) { + if( mode != mode_scart ) { + scartMode( true ); + } + else + CVFD::getInstance()->setMode(CVFD::MODE_SCART); + } + + else if( msg == NeutrinoMessages::VCR_OFF ) { + if( mode == mode_scart ) { + scartMode( false ); + } + } + else if (msg == NeutrinoMessages::EVT_START_PLUGIN) { + g_PluginList->startPlugin((const char *)data); + if (!g_PluginList->getScriptOutput().empty()) { + ShowMsgUTF(LOCALE_PLUGINS_RESULT, g_PluginList->getScriptOutput(), CMessageBox::mbrBack,CMessageBox::mbBack,NEUTRINO_ICON_SHELL); + } + + delete[] (unsigned char*) data; + return messages_return::handled; + } + else if (msg == NeutrinoMessages::EVT_SERVICES_UPD) { + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_ZAPIT_SDTCHANGED), + CMessageBox::mbrBack,CMessageBox::mbBack, NEUTRINO_ICON_INFO); + } + if ((msg >= CRCInput::RC_WithData) && (msg < CRCInput::RC_WithData + 0x10000000)) + delete [] (unsigned char*) data; + + return messages_return::unhandled; +} + +void CNeutrinoApp::ExitRun(const bool write_si, int retcode) +{ + if (!recordingstatus || + ShowLocalizedMessage(LOCALE_MESSAGEBOX_INFO, LOCALE_SHUTDOWN_RECODING_QUERY, CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo, NULL, 450, 30, true) == CMessageBox::mbrYes) + { + if(recordingstatus) { + CVCRControl::getInstance()->Stop(); + g_Timerd->stopTimerEvent(recording_id); + } + CVFD::getInstance()->setMode(CVFD::MODE_SHUTDOWN); + + dprintf(DEBUG_INFO, "exit\n"); + g_Zapit->stopPlayBack(); + + frameBuffer->paintBackground(); + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/shutdown.jpg"); + + networkConfig.automatic_start = (network_automatic_start == 1); + networkConfig.commitConfig(); + saveSetup(NEUTRINO_SETTINGS_FILE); + + if(g_settings.epg_save /* && timeset && g_Sectionsd->getIsTimeSet ()*/) { + saveEpg(); + } + + if(retcode) { + neutrino_msg_t msg; + neutrino_msg_data_t data; + + printf("entering off state\n"); + mode = mode_off; + + stop_daemons(false); + + system("/bin/sync"); + system("/bin/umount -a"); + cpuFreq->SetCpuFreq(g_settings.standby_cpufreq * 1000 * 1000); + powerManager->SetStandby(true, true); + int fspeed = 0; + funNotifier->changeNotify(NONEXISTANT_LOCALE, (void *) &fspeed); + + if (powerManager) { + powerManager->Close(); + delete powerManager; + } + + delete moviePlayerGui; + shutdown_cs_api(); + + system("/etc/init.d/rcK"); + CVFD::getInstance()->ShowIcon(VFD_ICON_CAM1, true); + InfoClock->StopClock(); + + g_RCInput->clearRCMsg(); + while( true ) { + g_RCInput->getMsg(&msg, &data, 10000); + if( msg == CRCInput::RC_standby ) { + printf("Power key, going to reboot...\n"); + sleep(2); + reboot(LINUX_REBOOT_CMD_RESTART); + } else if( ( msg == NeutrinoMessages::ANNOUNCE_RECORD) || ( msg == NeutrinoMessages::ANNOUNCE_ZAPTO) ) { + printf("Zap/record timer, going to reboot...\n"); + sleep(2); + reboot(LINUX_REBOOT_CMD_RESTART); + } + } + } else { + if (g_RCInput != NULL) + delete g_RCInput; + + stop_daemons(); + + int fspeed = 0; + funNotifier->changeNotify(NONEXISTANT_LOCALE, (void *) &fspeed); + exit(retcode); + } + } +} +void CNeutrinoApp::saveEpg() +{ + struct stat my_stat; + if(stat(g_settings.epg_dir.c_str(), &my_stat) == 0){ + printf("Saving EPG to %s....\n", g_settings.epg_dir.c_str()); + neutrino_msg_t msg; + neutrino_msg_data_t data; + g_Sectionsd->writeSI2XML(g_settings.epg_dir.c_str()); + while( true ) { + g_RCInput->getMsg(&msg, &data, 300); // 30 secs.. + if (( msg == CRCInput::RC_timeout ) || (msg == NeutrinoMessages::EVT_SI_FINISHED)) { + //printf("Msg %x timeout %d EVT_SI_FINISHED %x\n", msg, CRCInput::RC_timeout, NeutrinoMessages::EVT_SI_FINISHED); + break; + } + } + } +} +void CNeutrinoApp::AudioMute( int newValue, bool isEvent ) +{ + //printf("MUTE: val %d current %d event %d\n", newValue, current_muted, isEvent); + int dx = 40; + int dy = 40; + int x = g_settings.screen_EndX-dx; + int y = g_settings.screen_StartY; + + CVFD::getInstance()->setMuted(newValue); + current_muted = newValue; + +printf("AudioMute: current %d new %d isEvent: %d\n", current_muted, newValue, isEvent); + //if( !isEvent ) + g_Zapit->muteAudio(current_muted); + + if( isEvent && ( mode != mode_scart ) && ( mode != mode_audio) && ( mode != mode_pic)) + { + if( current_muted ) { + frameBuffer->paintBoxRel(x, y, dx, dy, COL_INFOBAR_PLUS_0); + frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_MUTE, x+5, y+5); + } + else + frameBuffer->paintBackgroundBoxRel(x, y, dx, dy); + } +} + +void CNeutrinoApp::setvol(int vol, int avs) +{ + audioDecoder->setVolume(vol, vol); +} + +void CNeutrinoApp::setVolume(const neutrino_msg_t key, const bool bDoPaint, bool nowait) +{ + neutrino_msg_t msg = key; + + int dx = 256; + int dy = 40; +#if 0 // orig + int x = (((g_settings.screen_EndX- g_settings.screen_StartX)- dx) / 2) + g_settings.screen_StartX; + int y = g_settings.screen_EndY - 100; +#else + + int x = frameBuffer->getScreenX() + 10; + int y = frameBuffer->getScreenY() + 0; +#endif + int vol = g_settings.current_volume; + + fb_pixel_t * pixbuf = NULL; + + if(bDoPaint) { + pixbuf = new fb_pixel_t[dx * dy]; + if(pixbuf!= NULL) + frameBuffer->SaveScreen(x, y, dx, dy, pixbuf); + + frameBuffer->paintIcon("volume.raw",x,y, COL_INFOBAR); + frameBuffer->paintBoxRel (x + 40, y+12, 200, 15, COL_INFOBAR_PLUS_0); + g_volscale->reset(); + g_volscale->paint(x + 41, y + 12, g_settings.current_volume); + } + + neutrino_msg_data_t data; + + unsigned long long timeoutEnd; + + do { + if (msg <= CRCInput::RC_MaxRC) { + if (msg == CRCInput::RC_plus || msg == CRCInput::RC_right) { //FIXME + if (g_settings.current_volume < 100 - 2) + g_settings.current_volume += 2; + else + g_settings.current_volume = 100; + } + else if (msg == CRCInput::RC_minus || msg == CRCInput::RC_left) { //FIXME + if (g_settings.current_volume > 2) + g_settings.current_volume -= 2; + else + g_settings.current_volume = 0; + } + else { + g_RCInput->postMsg(msg, data); + break; + } + + setvol(g_settings.current_volume,(g_settings.audio_avs_Control)); + //timeoutEnd = CRCInput::calcTimeoutEnd(nowait ? 1 : g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] / 2); + timeoutEnd = CRCInput::calcTimeoutEnd(nowait ? 1 : 3); + } + else if (msg == NeutrinoMessages::EVT_VOLCHANGED) { + //current_volume = g_Controld->getVolume((CControld::volume_type)g_settings.audio_avs_Control);//FIXME +//printf("setVolume EVT_VOLCHANGED %d\n", current_volume); + //timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] / 2); + timeoutEnd = CRCInput::calcTimeoutEnd(3); + } + else if (handleMsg(msg, data) & messages_return::unhandled) { + g_RCInput->postMsg(msg, data); + break; + } + + if (bDoPaint) { + if(vol != g_settings.current_volume) { + vol = g_settings.current_volume; + g_volscale->paint(x + 41, y + 12, g_settings.current_volume); + } + } + + CVFD::getInstance()->showVolume(g_settings.current_volume); + if (msg != CRCInput::RC_timeout) { + g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd ); + } + } while (msg != CRCInput::RC_timeout); + + //frameBuffer->paintBackgroundBoxRel(x, y, dx, dy); //FIXME osd bug + + if( (bDoPaint) && (pixbuf!= NULL) ) { + frameBuffer->RestoreScreen(x, y, dx, dy, pixbuf); + delete [] pixbuf; + } +} + +void CNeutrinoApp::tvMode( bool rezap ) +{ + if(mode==mode_radio ) { + videoDecoder->StopPicture(); + g_RCInput->killTimer(g_InfoViewer->lcdUpdateTimer); + g_InfoViewer->lcdUpdateTimer = g_RCInput->addTimer( LCD_UPDATE_TIME_TV_MODE, false ); + CVFD::getInstance()->ShowIcon(VFD_ICON_RADIO, false); + if(!rezap) + dvbsub_start(0); + } + + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + CVFD::getInstance()->ShowIcon(VFD_ICON_TV, true); + + if( mode == mode_tv ) { + if(g_settings.mode_clock) { + InfoClock->StopClock(); + g_settings.mode_clock=false; + } else { + InfoClock->StartClock(); + g_settings.mode_clock=true; + } + return; + } else if( mode == mode_scart ) { + //g_Controld->setScartMode( 0 ); + } else if( mode == mode_standby ) { + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + videoDecoder->Standby(false); + } + + bool stopauto = (mode != mode_ts); + mode = mode_tv; + if(stopauto && autoshift) { +//printf("standby on: autoshift ! stopping ...\n"); + stopAutoRecord(); + recordingstatus = 0; + } + + frameBuffer->useBackground(false); + frameBuffer->paintBackground(); + + g_RemoteControl->tvMode(); + SetChannelMode(g_settings.channel_mode);//FIXME needed ? + if( rezap ) { + firstChannel(); + channelList->tuned = 0xfffffff;; + channelList->zapTo( firstchannel.channelNumber -1 ); + } +#ifdef USEACTIONLOG + g_ActionLog->println("mode: tv"); +#endif +} + +void CNeutrinoApp::scartMode( bool bOnOff ) +{ + //printf( ( bOnOff ) ? "mode: scart on\n" : "mode: scart off\n" ); + + if( bOnOff ) { + // SCART AN + frameBuffer->useBackground(false); + frameBuffer->paintBackground(); + + //g_Controld->setScartMode( 1 ); + CVFD::getInstance()->setMode(CVFD::MODE_SCART); + lastMode = mode; + mode = mode_scart; + } else { + // SCART AUS + //g_Controld->setScartMode( 0 ); + + mode = mode_unknown; + //re-set mode + if( lastMode == mode_radio ) { + radioMode( false ); + } + else if( lastMode == mode_tv ) { + tvMode( false ); + } + else if( lastMode == mode_standby ) { + standbyMode( true ); + } + } +} + +void CNeutrinoApp::standbyMode( bool bOnOff ) +{ + static bool wasshift = false; +//printf("********* CNeutrinoApp::standbyMode, was_record %d bOnOff %d\n", was_record, bOnOff); + //printf( ( bOnOff ) ? "mode: standby on\n" : "mode: standby off\n" ); + if( bOnOff ) { + puts("[neutrino.cpp] executing " NEUTRINO_ENTER_STANDBY_SCRIPT "."); + if (system(NEUTRINO_ENTER_STANDBY_SCRIPT) != 0) + perror(NEUTRINO_ENTER_STANDBY_SCRIPT " failed"); + + if(autoshift) { + stopAutoRecord(); + wasshift = true; + recordingstatus = 0; + } + if( mode == mode_scart ) { + //g_Controld->setScartMode( 0 ); + } + dvbsub_pause(); + + frameBuffer->useBackground(false); + frameBuffer->paintBackground(); + + CVFD::getInstance()->Clear(); + CVFD::getInstance()->setMode(CVFD::MODE_STANDBY); + + videoDecoder->Standby(true); + if(recordingstatus) was_record = 1; + if(!was_record) { + g_Zapit->setStandby(true); + system("hdparm -y /dev/ide/host0/bus0/target0/lun0/disc >/dev/null 2>/dev/null"); + } else + g_Zapit->stopPlayBack(); + + g_Sectionsd->setPauseScanning(true); + if(g_settings.epg_save) { + saveEpg(); + } + if(g_settings.mode_clock) + InfoClock->StopClock(); + + + lastMode = mode; + mode = mode_standby; + int fspeed = 1; + funNotifier->changeNotify(NONEXISTANT_LOCALE, (void *) &fspeed); + + frameBuffer->setActive(false); + // Active standby on + powerManager->SetStandby(true, false); + if(!was_record) + cpuFreq->SetCpuFreq(g_settings.standby_cpufreq * 1000 * 1000); + } else { + // Active standby off + cpuFreq->SetCpuFreq(g_settings.cpufreq * 1000 * 1000); + + powerManager->SetStandby(false, false); + frameBuffer->setActive(true); + + funNotifier->changeNotify(NONEXISTANT_LOCALE, (void*) &g_settings.fan_speed); + puts("[neutrino.cpp] executing " NEUTRINO_LEAVE_STANDBY_SCRIPT "."); + if (system(NEUTRINO_LEAVE_STANDBY_SCRIPT) != 0) + perror(NEUTRINO_LEAVE_STANDBY_SCRIPT " failed"); + + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + g_Zapit->setStandby(false); + if(was_record) + g_Zapit->startPlayBack(); + if(recordingstatus) was_record = 0; + + videoDecoder->Standby(false); + g_Sectionsd->setPauseScanning(false); + + if(g_settings.mode_clock) + InfoClock->StartClock(); + + mode = mode_unknown; + if( lastMode == mode_radio ) { + radioMode( false ); + } else { + tvMode( false ); + } + AudioMute(current_muted, false ); + if((mode == mode_tv) && wasshift) { + startAutoRecord(true); + } + wasshift = false; + dvbsub_start(0); + } +} + +void CNeutrinoApp::radioMode( bool rezap) +{ +printf("radioMode: rezap %s\n", rezap ? "yes" : "no"); + if(mode==mode_tv ) { + g_RCInput->killTimer(g_InfoViewer->lcdUpdateTimer); + g_InfoViewer->lcdUpdateTimer = g_RCInput->addTimer( LCD_UPDATE_TIME_RADIO_MODE, false ); + CVFD::getInstance()->ShowIcon(VFD_ICON_TV, false); + dvbsub_pause(); + } + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + CVFD::getInstance()->ShowIcon(VFD_ICON_RADIO, true); + + if( mode==mode_radio ) { + if(g_settings.mode_clock) { + InfoClock->StopClock(); + g_settings.mode_clock=false; + } else { + InfoClock->StartClock(); + g_settings.mode_clock=true; + } + return; + } + else if( mode == mode_scart ) { + //g_Controld->setScartMode( 0 ); + } + else if( mode == mode_standby ) { + CVFD::getInstance()->setMode(CVFD::MODE_TVRADIO); + videoDecoder->Standby(false); + } + mode = mode_radio; + if(autoshift) { +//printf("standby on: autoshift ! stopping ...\n"); + stopAutoRecord(); + recordingstatus = 0; + } + + g_RemoteControl->radioMode(); + SetChannelMode(g_settings.channel_mode);//FIXME needed? + if( rezap ) { + firstChannel(); + channelList->tuned = 0xfffffff;; + channelList->zapTo( firstchannel.channelNumber -1 ); + } + videoDecoder->ShowPicture(DATADIR "/neutrino/icons/radiomode.jpg"); +} + +void CNeutrinoApp::startNextRecording() +{ + if ((recordingstatus == 0) && (nextRecordingInfo != NULL)) { + bool doRecord = true; + if (CVCRControl::getInstance()->isDeviceRegistered()) { + recording_id = nextRecordingInfo->eventID; + if (g_settings.recording_type == RECORDING_FILE) { + char *recDir = strlen(nextRecordingInfo->recordingDir) > 0 ? + nextRecordingInfo->recordingDir : g_settings.network_nfs_recordingdir; + if (!CFSMounter::isMounted(recDir)) { + doRecord = false; + for(int i=0 ; i < NETWORK_NFS_NR_OF_ENTRIES ; i++) { + if (strcmp(g_settings.network_nfs_local_dir[i],recDir) == 0) { + CFSMounter::MountRes mres = + CFSMounter::mount(g_settings.network_nfs_ip[i].c_str(), g_settings.network_nfs_dir[i], + g_settings.network_nfs_local_dir[i], (CFSMounter::FSType) g_settings.network_nfs_type[i], + g_settings.network_nfs_username[i], g_settings.network_nfs_password[i], + g_settings.network_nfs_mount_options1[i], g_settings.network_nfs_mount_options2[i]); + if (mres == CFSMounter::MRES_OK) { + doRecord = true; + } else { + const char * merr = mntRes2Str(mres); + int msglen = strlen(merr) + strlen(nextRecordingInfo->recordingDir) + 7; + char msg[msglen]; + strcpy(msg,merr); + strcat(msg,"\nDir: "); + strcat(msg,nextRecordingInfo->recordingDir); + + ShowHintUTF(LOCALE_MESSAGEBOX_ERROR, msg); // UTF-8 + doRecord = false; + } + break; + } + } + if (!doRecord) { + // recording dir does not seem to exist in config anymore + // or an error occured while mounting + // -> try default dir + recDir = g_settings.network_nfs_recordingdir; + doRecord = true; + } + } + (static_cast(recordingdevice))->Directory = std::string(recDir); +printf("CNeutrinoApp::startNextRecording: start to dir %s\n", recDir); + } + if(doRecord && CVCRControl::getInstance()->Record(nextRecordingInfo)) + recordingstatus = 1; + else + recordingstatus = 0; + } + else + puts("[neutrino.cpp] no recording devices"); + + /* Note: CTimerd::RecordingInfo is a class! + * What a brilliant idea to send classes via the eventserver! + * => typecast to avoid destructor call + */ + delete [](unsigned char *)nextRecordingInfo; + + nextRecordingInfo = NULL; + } +} +/************************************************************************************** +* CNeutrinoApp - exec, menuitem callback (shutdown) * +**************************************************************************************/ +void SaveMotorPositions(); +int CNeutrinoApp::exec(CMenuTarget* parent, const std::string & actionKey) +{ + // printf("ac: %s\n", actionKey.c_str()); + int returnval = menu_return::RETURN_REPAINT; + + if(actionKey == "help_recording") { + ShowLocalizedMessage(LOCALE_SETTINGS_HELP, LOCALE_RECORDINGMENU_HELP, CMessageBox::mbrBack, CMessageBox::mbBack); + } + else if(actionKey=="shutdown") { + ExitRun(true, 1); + } + else if(actionKey=="reboot") + { + FILE *f = fopen("/tmp/.reboot", "w"); + fclose(f); + ExitRun(true); + unlink("/tmp/.reboot"); + returnval = menu_return::RETURN_NONE; + } + else if(actionKey=="tv") { + tvMode(); + returnval = menu_return::RETURN_EXIT_ALL; + } + else if(actionKey=="radio") { + radioMode(); + returnval = menu_return::RETURN_EXIT_ALL; + } + else if(actionKey=="scart") { + g_RCInput->postMsg( NeutrinoMessages::VCR_ON, 0 ); + returnval = menu_return::RETURN_EXIT_ALL; + } + else if(actionKey=="network") { + networkConfig.automatic_start = (network_automatic_start == 1); + networkConfig.stopNetwork(); + networkConfig.commitConfig(); + networkConfig.startNetwork(); + } + else if(actionKey=="networktest") { + dprintf(DEBUG_INFO, "doing network test...\n"); + testNetworkSettings(networkConfig.address.c_str(), networkConfig.netmask.c_str(), networkConfig.broadcast.c_str(), networkConfig.gateway.c_str(), networkConfig.nameserver.c_str(), networkConfig.inet_static); + } + else if(actionKey=="networkshow") { + dprintf(DEBUG_INFO, "showing current network settings...\n"); + showCurrentNetworkSettings(); + } + else if (actionKey=="theme_neutrino") { + setupColors_neutrino(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if (actionKey=="theme_classic") { + setupColors_classic(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if (actionKey=="theme_dblue") { + setupColors_dblue(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if (actionKey=="theme_dvb2k") { + setupColors_dvb2k(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if(actionKey=="theme_ru") { + setupColors_ru(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if(actionKey=="theme_red") { + setupColors_red(); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if(actionKey=="savescansettings") { + SaveMotorPositions(); + } + else if(actionKey=="savesettings") { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_MAINSETTINGS_SAVESETTINGSNOW_HINT)); // UTF-8 + hintBox->paint(); + + networkConfig.automatic_start = (network_automatic_start == 1); + networkConfig.commitConfig(); + saveSetup(NEUTRINO_SETTINGS_FILE); + SaveMotorPositions(); + + zapitCfg.gotoXXLatitude = strtod(zapit_lat, NULL); + zapitCfg.gotoXXLongitude = strtod(zapit_long, NULL); + + setZapitConfig(&zapitCfg); + if(g_settings.cacheTXT) { + tuxtxt_init(); + } else + tuxtxt_close(); + + //g_Sectionsd->setEventsAreOldInMinutes((unsigned short) (g_settings.epg_old_hours*60)); + //g_Sectionsd->setHoursToCache((unsigned short) (g_settings.epg_cache_days*24)); + + hintBox->hide(); + delete hintBox; + } + else if(actionKey=="recording") { + setupRecordingDevice(); + } + else if(actionKey=="reloadchannels") { + //reloadhintBox->paint(); + g_Zapit->reinitChannels(); +#if 0 + system(mode == mode_radio + ? "wget -q -O /dev/null http://127.0.0.1/control/setmode?radio > /dev/null 2>&1" + : "wget -q -O /dev/null http://127.0.0.1/control/setmode?tv > /dev/null 2>&1"); +#endif + //reloadhintBox->hide(); + + } + else if(actionKey=="reloadplugins") { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_SERVICEMENU_GETPLUGINS_HINT)); + hintBox->paint(); + + g_PluginList->loadPlugins(); + + hintBox->hide(); + delete hintBox; + } + else if(actionKey=="restart") { + if (recordingstatus) + DisplayErrorMessage(g_Locale->getText(LOCALE_SERVICEMENU_RESTART_REFUSED_RECORDING)); + else { + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_SERVICEMENU_RESTART_HINT)); + hintBox->paint(); + + stop_daemons(); + + delete g_RCInput; + delete g_Sectionsd; + delete g_Timerd; + delete g_RemoteControl; + delete g_fontRenderer; + delete g_Zapit; + delete CVFD::getInstance(); + + networkConfig.automatic_start = (network_automatic_start == 1); + networkConfig.commitConfig(); + saveSetup(NEUTRINO_SETTINGS_FILE); + delete frameBuffer; + execvp(global_argv[0], global_argv); // no return if successful + exit(1); + } + } + else if(strncmp(actionKey.c_str(), "fontsize.d", 10) == 0) { + for (int i = 0; i < 6; i++) { + if (actionKey == font_sizes_groups[i].actionkey) + for (unsigned int j = 0; j < font_sizes_groups[i].count; j++) { + SNeutrinoSettings::FONT_TYPES k = font_sizes_groups[i].content[j]; + configfile.setInt32(locale_real_names[neutrino_font[k].name], neutrino_font[k].defaultsize); + } + } + fontsizenotifier.changeNotify(NONEXISTANT_LOCALE, NULL); + } + else if(actionKey=="osd.def") { + for (int i = 0; i < TIMING_SETTING_COUNT; i++) + g_settings.timing[i] = default_timing[i]; + + SetupTiming(); + } + else if(actionKey == "audioplayerdir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.network_nfs_audioplayerdir)) + strncpy(g_settings.network_nfs_audioplayerdir, b.getSelectedFile()->Name.c_str(), sizeof(g_settings.network_nfs_audioplayerdir)-1); + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "picturedir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.network_nfs_picturedir)) + strncpy(g_settings.network_nfs_picturedir, b.getSelectedFile()->Name.c_str(), sizeof(g_settings.network_nfs_picturedir)-1); + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "moviedir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.network_nfs_moviedir)) + strncpy(g_settings.network_nfs_moviedir, b.getSelectedFile()->Name.c_str(), sizeof(g_settings.network_nfs_moviedir)-1); + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "recordingdir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.network_nfs_recordingdir)) { + const char * newdir = b.getSelectedFile()->Name.c_str(); +printf("New recordingdir: selected %s\n", newdir); + if(check_dir(newdir)) + printf("Wrong/unsupported recording dir %s\n", newdir); + else { + strncpy(g_settings.network_nfs_recordingdir, b.getSelectedFile()->Name.c_str(), sizeof(g_settings.network_nfs_recordingdir)-1); +printf("New recordingdir: %s (timeshift %s)\n", g_settings.network_nfs_recordingdir, g_settings.timeshiftdir); + if(strlen(g_settings.timeshiftdir) == 0) { + sprintf(timeshiftDir, "%s/.timeshift", g_settings.network_nfs_recordingdir); + safe_mkdir(timeshiftDir); +printf("New timeshift dir: %s\n", timeshiftDir); + } + } + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "timeshiftdir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.timeshiftdir)) { + const char * newdir = b.getSelectedFile()->Name.c_str(); +printf("New timeshift: selected %s\n", newdir); + if(check_dir(newdir)) + printf("Wrong/unsupported recording dir %s\n", newdir); + else { +printf("New timeshift dir: old %s (record %s)\n", g_settings.timeshiftdir, g_settings.network_nfs_recordingdir); + if(strcmp(newdir, g_settings.network_nfs_recordingdir)) { +printf("New timeshift != rec dir\n"); + strncpy(g_settings.timeshiftdir, b.getSelectedFile()->Name.c_str(), sizeof(g_settings.timeshiftdir)-1); + strcpy(timeshiftDir, g_settings.timeshiftdir); + } else { + sprintf(timeshiftDir, "%s/.timeshift", g_settings.network_nfs_recordingdir); + strcpy(g_settings.timeshiftdir, newdir); + safe_mkdir(timeshiftDir); +printf("New timeshift == rec dir\n"); + } +printf("New timeshift dir: %s\n", timeshiftDir); + } + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "update_dir") { + parent->hide(); + CFileBrowser fileBrowser; + fileBrowser.Dir_Mode = true; + if (fileBrowser.exec(g_settings.update_dir) == true) { + const char * newdir = fileBrowser.getSelectedFile()->Name.c_str(); + if(check_dir(newdir)) + printf("Wrong/unsupported recording dir %s\n", newdir); + else { + strcpy(g_settings.update_dir, fileBrowser.getSelectedFile()->Name.c_str()); + printf("[neutrino] new update dir %s\n", fileBrowser.getSelectedFile()->Name.c_str()); + } + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "epgdir") { + parent->hide(); + CFileBrowser b; + b.Dir_Mode=true; + if (b.exec(g_settings.epg_dir.c_str())) { + const char * newdir = b.getSelectedFile()->Name.c_str(); + if(check_dir(newdir)) + printf("Wrong/unsupported epg dir %s\n", newdir); + else { + g_settings.epg_dir = b.getSelectedFile()->Name; + SendSectionsdConfig(); + } + } + + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "select_font") { + parent->hide(); + CFileBrowser fileBrowser; + CFileFilter fileFilter; + fileFilter.addFilter("ttf"); + fileBrowser.Filter = &fileFilter; + if (fileBrowser.exec(FONTDIR) == true) { + strcpy(g_settings.font_file, fileBrowser.getSelectedFile()->Name.c_str()); + printf("[neutrino] new font file %s\n", fileBrowser.getSelectedFile()->Name.c_str()); + SetupFonts(); + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "movieplugin") { + parent->hide(); + CMenuWidget MoviePluginSelector(LOCALE_MOVIEPLAYER_DEFPLUGIN, "features.raw", 350); + MoviePluginSelector.addItem(GenericMenuSeparator); + + char id[5]; + int cnt = 0; + int enabled_count = 0; + for(unsigned int count=0;count < (unsigned int) g_PluginList->getNumberOfPlugins();count++) { + if (g_PluginList->getType(count)== CPlugins::P_TYPE_TOOL && !g_PluginList->isHidden(count)) { + // zB vtxt-plugins + sprintf(id, "%d", count); + enabled_count++; + MoviePluginSelector.addItem(new CMenuForwarderNonLocalized(g_PluginList->getName(count), true, NULL, MoviePluginChanger, id, CRCInput::convertDigitToKey(count)), (cnt == 0)); + cnt++; + } + } + + MoviePluginSelector.exec(NULL, ""); + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "onekeyplugin") { + parent->hide(); + CMenuWidget MoviePluginSelector(LOCALE_EXTRA_KEY_PLUGIN, "features.raw", 350); + MoviePluginSelector.addItem(GenericMenuSeparator); + + char id[5]; + int cnt = 0; + int enabled_count = 0; + for(unsigned int count=0;count < (unsigned int) g_PluginList->getNumberOfPlugins();count++) { + if (g_PluginList->getType(count)== CPlugins::P_TYPE_TOOL && !g_PluginList->isHidden(count)) { + // zB vtxt-plugins + sprintf(id, "%d", count); + enabled_count++; + MoviePluginSelector.addItem(new CMenuForwarderNonLocalized(g_PluginList->getName(count), true, NULL, OnekeyPluginChanger, id, CRCInput::convertDigitToKey(count)), (cnt == 0)); + cnt++; + } + } + + MoviePluginSelector.exec(NULL, ""); + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "loadconfig") { + parent->hide(); + CFileBrowser fileBrowser; + CFileFilter fileFilter; + fileFilter.addFilter("conf"); + fileBrowser.Filter = &fileFilter; + if (fileBrowser.exec("/var/tuxbox/config") == true) { + loadSetup(fileBrowser.getSelectedFile()->Name.c_str()); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + CVFD::getInstance()->setlcdparameter(); + printf("[neutrino] new settings: %s\n", fileBrowser.getSelectedFile()->Name.c_str()); + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "saveconfig") { + parent->hide(); + CFileBrowser fileBrowser; + fileBrowser.Dir_Mode = true; + if (fileBrowser.exec("/var/tuxbox") == true) { + char fname[256] = "neutrino.conf", sname[256]; + CStringInputSMS * sms = new CStringInputSMS(LOCALE_EXTRA_SAVECONFIG, fname, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789. "); + sms->exec(NULL, ""); + sprintf(sname, "%s/%s", fileBrowser.getSelectedFile()->Name.c_str(), fname); + printf("[neutrino] save settings: %s\n", sname); + saveSetup(sname); + delete sms; + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "loadcolors") { + parent->hide(); + CFileBrowser fileBrowser; + CFileFilter fileFilter; + fileFilter.addFilter("conf"); + fileBrowser.Filter = &fileFilter; + if (fileBrowser.exec("/var/tuxbox/config") == true) { + loadColors(fileBrowser.getSelectedFile()->Name.c_str()); + //printf("[neutrino] new colors: %s\n", fileBrowser.getSelectedFile()->Name.c_str()); + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "savecolors") { + parent->hide(); + CFileBrowser fileBrowser; + fileBrowser.Dir_Mode = true; + if (fileBrowser.exec("/var/tuxbox") == true) { + char fname[256] = "colors.conf", sname[256]; + CStringInputSMS * sms = new CStringInputSMS(LOCALE_EXTRA_SAVECOLORS, fname, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789. "); + sms->exec(NULL, ""); + sprintf(sname, "%s/%s", fileBrowser.getSelectedFile()->Name.c_str(), fname); + printf("[neutrino] save colors: %s\n", sname); + saveColors(sname); + delete sms; + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "loadkeys") { + parent->hide(); + CFileBrowser fileBrowser; + CFileFilter fileFilter; + fileFilter.addFilter("conf"); + fileBrowser.Filter = &fileFilter; + if (fileBrowser.exec("/var/tuxbox/config") == true) { + loadKeys(fileBrowser.getSelectedFile()->Name.c_str()); + printf("[neutrino] new keys: %s\n", fileBrowser.getSelectedFile()->Name.c_str()); + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "savekeys") { + parent->hide(); + CFileBrowser fileBrowser; + fileBrowser.Dir_Mode = true; + if (fileBrowser.exec("/var/tuxbox") == true) { + char fname[256] = "keys.conf", sname[256]; + CStringInputSMS * sms = new CStringInputSMS(LOCALE_EXTRA_SAVEKEYS, fname, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789. "); + sms->exec(NULL, ""); + sprintf(sname, "%s/%s", fileBrowser.getSelectedFile()->Name.c_str(), fname); + printf("[neutrino] save keys: %s\n", sname); + saveKeys(sname); + delete sms; + } + return menu_return::RETURN_REPAINT; + } + else if(actionKey == "autolink") { + if(autoshift) { + char buf[512]; + autoshift = false; + sprintf(buf, "ln %s/* %s", timeshiftDir, g_settings.network_nfs_recordingdir); + system(buf); + autoshift_delete = true; + //sprintf(buf, "rm -f %s/* &", timeshiftDir); + //system(buf); + } + returnval = menu_return::RETURN_EXIT_ALL; + } + else if(actionKey == "clearSectionsd") + { + g_Sectionsd->freeMemory(); + } + + return returnval; +} + +/************************************************************************************** +* changeNotify - features menu recording start / stop * +**************************************************************************************/ +bool CNeutrinoApp::changeNotify(const neutrino_locale_t OptionName, void *data) +{ + if ((ARE_LOCALES_EQUAL(OptionName, LOCALE_MAINMENU_RECORDING_START)) || (ARE_LOCALES_EQUAL(OptionName, LOCALE_MAINMENU_RECORDING))) + { + if(g_RemoteControl->is_video_started) { + bool res = doGuiRecord(NULL,ARE_LOCALES_EQUAL(OptionName, LOCALE_MAINMENU_RECORDING)); + return res; + } + else { + recordingstatus = 0; + return false; + } + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_LANGUAGESETUP_SELECT)) { + g_Locale->loadLocale(g_settings.language); + return true; + } + return false; +} + +/************************************************************************************** +* Main programm - no function here * +**************************************************************************************/ +void stop_daemons(bool stopall) +{ + dvbsub_close(); + tuxtxt_stop(); + tuxtxt_close(); + + printf("httpd shutdown\n"); + pthread_cancel(nhttpd_thread); + pthread_join(nhttpd_thread, NULL); + printf("httpd shutdown done\n"); + streamts_stop = 1; + pthread_join(stream_thread, NULL); + sectionsd_stop = 1; + if(stopall) { + printf("timerd shutdown\n"); + g_Timerd->shutdown(); + pthread_join(timer_thread, NULL); + printf("timerd shutdown done\n"); + } +#ifndef DISABLE_SECTIONSD + printf("sectionsd shutdown\n"); + pthread_join(sections_thread, NULL); + printf("sectionsd shutdown done\n"); +#endif + printf("zapit shutdown\n"); + g_Zapit->shutdown(); + pthread_join(zapit_thread, NULL); + printf("zapit shutdown done\n"); + CVFD::getInstance()->Clear(); + if(stopall) { + delete moviePlayerGui; + if (powerManager) { + powerManager->Close(); + delete powerManager; + } + shutdown_cs_api(); + } +} + +void sighandler (int signum) +{ + signal (signum, SIG_IGN); + switch (signum) { + case SIGTERM: + case SIGINT: + stop_daemons(); + _exit(0); + default: + break; + } +} + +int main(int argc, char **argv) +{ + setDebugLevel(DEBUG_NORMAL); + signal(SIGTERM, sighandler); + signal(SIGINT, sighandler); + signal(SIGHUP, SIG_IGN); + + for(int i = 3; i < 256; i++) + close(i); + + tzset(); + initGlobals(); + + return CNeutrinoApp::getInstance()->run(argc, argv); +} + +void CNeutrinoApp::loadColors(const char * fname) +{ + bool res; + CConfigFile tconfig(',', true); + + printf("CNeutrinoApp::loadColors: %s\n", fname); + + res = tconfig.loadConfig(fname); + if(!res) return; + //colors (neutrino defaultcolors) + g_settings.menu_Head_alpha = tconfig.getInt32( "menu_Head_alpha", 0x00 ); + g_settings.menu_Head_red = tconfig.getInt32( "menu_Head_red", 0x00 ); + g_settings.menu_Head_green = tconfig.getInt32( "menu_Head_green", 0x0A ); + g_settings.menu_Head_blue = tconfig.getInt32( "menu_Head_blue", 0x19 ); + + g_settings.menu_Head_Text_alpha = tconfig.getInt32( "menu_Head_Text_alpha", 0x00 ); + g_settings.menu_Head_Text_red = tconfig.getInt32( "menu_Head_Text_red", 0x5f ); + g_settings.menu_Head_Text_green = tconfig.getInt32( "menu_Head_Text_green", 0x46 ); + g_settings.menu_Head_Text_blue = tconfig.getInt32( "menu_Head_Text_blue", 0x00 ); + + g_settings.menu_Content_alpha = tconfig.getInt32( "menu_Content_alpha", 0x14 ); + g_settings.menu_Content_red = tconfig.getInt32( "menu_Content_red", 0x00 ); + g_settings.menu_Content_green = tconfig.getInt32( "menu_Content_green", 0x0f ); + g_settings.menu_Content_blue = tconfig.getInt32( "menu_Content_blue", 0x23 ); + + g_settings.menu_Content_Text_alpha = tconfig.getInt32( "menu_Content_Text_alpha", 0x00 ); + g_settings.menu_Content_Text_red = tconfig.getInt32( "menu_Content_Text_red", 0x64 ); + g_settings.menu_Content_Text_green = tconfig.getInt32( "menu_Content_Text_green", 0x64 ); + g_settings.menu_Content_Text_blue = tconfig.getInt32( "menu_Content_Text_blue", 0x64 ); + + g_settings.menu_Content_Selected_alpha = tconfig.getInt32( "menu_Content_Selected_alpha", 0x14 ); + g_settings.menu_Content_Selected_red = tconfig.getInt32( "menu_Content_Selected_red", 0x19 ); + g_settings.menu_Content_Selected_green = tconfig.getInt32( "menu_Content_Selected_green", 0x37 ); + g_settings.menu_Content_Selected_blue = tconfig.getInt32( "menu_Content_Selected_blue", 0x64 ); + + g_settings.menu_Content_Selected_Text_alpha = tconfig.getInt32( "menu_Content_Selected_Text_alpha", 0x00 ); + g_settings.menu_Content_Selected_Text_red = tconfig.getInt32( "menu_Content_Selected_Text_red", 0x00 ); + g_settings.menu_Content_Selected_Text_green = tconfig.getInt32( "menu_Content_Selected_Text_green", 0x00 ); + g_settings.menu_Content_Selected_Text_blue = tconfig.getInt32( "menu_Content_Selected_Text_blue", 0x00 ); + + g_settings.menu_Content_inactive_alpha = tconfig.getInt32( "menu_Content_inactive_alpha", 0x14 ); + g_settings.menu_Content_inactive_red = tconfig.getInt32( "menu_Content_inactive_red", 0x00 ); + g_settings.menu_Content_inactive_green = tconfig.getInt32( "menu_Content_inactive_green", 0x0f ); + g_settings.menu_Content_inactive_blue = tconfig.getInt32( "menu_Content_inactive_blue", 0x23 ); + + g_settings.menu_Content_inactive_Text_alpha = tconfig.getInt32( "menu_Content_inactive_Text_alpha", 0x00 ); + g_settings.menu_Content_inactive_Text_red = tconfig.getInt32( "menu_Content_inactive_Text_red", 55 ); + g_settings.menu_Content_inactive_Text_green = tconfig.getInt32( "menu_Content_inactive_Text_green", 70 ); + g_settings.menu_Content_inactive_Text_blue = tconfig.getInt32( "menu_Content_inactive_Text_blue", 85 ); + + g_settings.infobar_alpha = tconfig.getInt32( "infobar_alpha", 0x14 ); + g_settings.infobar_red = tconfig.getInt32( "infobar_red", 0x00 ); + g_settings.infobar_green = tconfig.getInt32( "infobar_green", 0x0e ); + g_settings.infobar_blue = tconfig.getInt32( "infobar_blue", 0x23 ); + + g_settings.infobar_Text_alpha = tconfig.getInt32( "infobar_Text_alpha", 0x00 ); + g_settings.infobar_Text_red = tconfig.getInt32( "infobar_Text_red", 0x64 ); + g_settings.infobar_Text_green = tconfig.getInt32( "infobar_Text_green", 0x64 ); + g_settings.infobar_Text_blue = tconfig.getInt32( "infobar_Text_blue", 0x64 ); + for (int i = 0; i < LCD_SETTING_COUNT; i++) + g_settings.lcd_setting[i] = tconfig.getInt32(lcd_setting[i].name, lcd_setting[i].default_value); + strcpy(g_settings.lcd_setting_dim_time, tconfig.getString("lcd_dim_time","0").c_str()); + strcpy(g_settings.lcd_setting_dim_brightness, tconfig.getString("lcd_dim_brightness","0").c_str()); + + strcpy( g_settings.font_file, configfile.getString( "font_file", "/share/fonts/neutrino.ttf" ).c_str() ); + colorSetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + SetupFonts(); + CVFD::getInstance()->setlcdparameter(); +} + +void CNeutrinoApp::saveColors(const char * fname) +{ + //bool res; + CConfigFile tconfig(',', true); + + //colors + tconfig.setInt32( "menu_Head_alpha", g_settings.menu_Head_alpha ); + tconfig.setInt32( "menu_Head_red", g_settings.menu_Head_red ); + tconfig.setInt32( "menu_Head_green", g_settings.menu_Head_green ); + tconfig.setInt32( "menu_Head_blue", g_settings.menu_Head_blue ); + + tconfig.setInt32( "menu_Head_Text_alpha", g_settings.menu_Head_Text_alpha ); + tconfig.setInt32( "menu_Head_Text_red", g_settings.menu_Head_Text_red ); + tconfig.setInt32( "menu_Head_Text_green", g_settings.menu_Head_Text_green ); + tconfig.setInt32( "menu_Head_Text_blue", g_settings.menu_Head_Text_blue ); + + tconfig.setInt32( "menu_Content_alpha", g_settings.menu_Content_alpha ); + tconfig.setInt32( "menu_Content_red", g_settings.menu_Content_red ); + tconfig.setInt32( "menu_Content_green", g_settings.menu_Content_green ); + tconfig.setInt32( "menu_Content_blue", g_settings.menu_Content_blue ); + + tconfig.setInt32( "menu_Content_Text_alpha", g_settings.menu_Content_Text_alpha ); + tconfig.setInt32( "menu_Content_Text_red", g_settings.menu_Content_Text_red ); + tconfig.setInt32( "menu_Content_Text_green", g_settings.menu_Content_Text_green ); + tconfig.setInt32( "menu_Content_Text_blue", g_settings.menu_Content_Text_blue ); + + tconfig.setInt32( "menu_Content_Selected_alpha", g_settings.menu_Content_Selected_alpha ); + tconfig.setInt32( "menu_Content_Selected_red", g_settings.menu_Content_Selected_red ); + tconfig.setInt32( "menu_Content_Selected_green", g_settings.menu_Content_Selected_green ); + tconfig.setInt32( "menu_Content_Selected_blue", g_settings.menu_Content_Selected_blue ); + + tconfig.setInt32( "menu_Content_Selected_Text_alpha", g_settings.menu_Content_Selected_Text_alpha ); + tconfig.setInt32( "menu_Content_Selected_Text_red", g_settings.menu_Content_Selected_Text_red ); + tconfig.setInt32( "menu_Content_Selected_Text_green", g_settings.menu_Content_Selected_Text_green ); + tconfig.setInt32( "menu_Content_Selected_Text_blue", g_settings.menu_Content_Selected_Text_blue ); + + tconfig.setInt32( "menu_Content_inactive_alpha", g_settings.menu_Content_inactive_alpha ); + tconfig.setInt32( "menu_Content_inactive_red", g_settings.menu_Content_inactive_red ); + tconfig.setInt32( "menu_Content_inactive_green", g_settings.menu_Content_inactive_green ); + tconfig.setInt32( "menu_Content_inactive_blue", g_settings.menu_Content_inactive_blue ); + + tconfig.setInt32( "menu_Content_inactive_Text_alpha", g_settings.menu_Content_inactive_Text_alpha ); + tconfig.setInt32( "menu_Content_inactive_Text_red", g_settings.menu_Content_inactive_Text_red ); + tconfig.setInt32( "menu_Content_inactive_Text_green", g_settings.menu_Content_inactive_Text_green ); + tconfig.setInt32( "menu_Content_inactive_Text_blue", g_settings.menu_Content_inactive_Text_blue ); + + tconfig.setInt32( "infobar_alpha", g_settings.infobar_alpha ); + tconfig.setInt32( "infobar_red", g_settings.infobar_red ); + tconfig.setInt32( "infobar_green", g_settings.infobar_green ); + tconfig.setInt32( "infobar_blue", g_settings.infobar_blue ); + + tconfig.setInt32( "infobar_Text_alpha", g_settings.infobar_Text_alpha ); + tconfig.setInt32( "infobar_Text_red", g_settings.infobar_Text_red ); + tconfig.setInt32( "infobar_Text_green", g_settings.infobar_Text_green ); + tconfig.setInt32( "infobar_Text_blue", g_settings.infobar_Text_blue ); + for (int i = 0; i < LCD_SETTING_COUNT; i++) + tconfig.setInt32(lcd_setting[i].name, g_settings.lcd_setting[i]); + tconfig.setString("lcd_dim_time", g_settings.lcd_setting_dim_time); + tconfig.setString("lcd_dim_brightness", g_settings.lcd_setting_dim_brightness); + + tconfig.setString("font_file", g_settings.font_file); + tconfig.saveConfig(fname); +} + +void CNeutrinoApp::loadKeys(const char * fname) +{ + bool res; + CConfigFile tconfig(',', true); + + res = tconfig.loadConfig(fname); + if(!res) return; + //rc-key configuration + g_settings.key_tvradio_mode = tconfig.getInt32( "key_tvradio_mode", CRCInput::RC_nokey ); + + g_settings.key_channelList_pageup = tconfig.getInt32( "key_channelList_pageup", CRCInput::RC_minus ); + g_settings.key_channelList_pagedown = tconfig.getInt32( "key_channelList_pagedown", CRCInput::RC_plus ); + g_settings.key_channelList_cancel = tconfig.getInt32( "key_channelList_cancel", CRCInput::RC_home ); + g_settings.key_channelList_sort = tconfig.getInt32( "key_channelList_sort", CRCInput::RC_blue ); + g_settings.key_channelList_addrecord = tconfig.getInt32( "key_channelList_addrecord", CRCInput::RC_nokey ); + g_settings.key_channelList_addremind = tconfig.getInt32( "key_channelList_addremind", CRCInput::RC_nokey ); + + g_settings.key_list_start = tconfig.getInt32( "key_list_start", CRCInput::RC_nokey ); + g_settings.key_list_end = tconfig.getInt32( "key_list_end", CRCInput::RC_nokey ); + g_settings.menu_left_exit = tconfig.getInt32( "menu_left_exit", 0 ); + + g_settings.audio_run_player = tconfig.getInt32( "audio_run_player", 1 ); + g_settings.key_click = tconfig.getInt32( "key_click", 0 ); + g_settings.mpkey_rewind = tconfig.getInt32( "mpkey.rewind", CRCInput::RC_left ); + g_settings.mpkey_forward = tconfig.getInt32( "mpkey.forward", CRCInput::RC_right ); + g_settings.mpkey_pause = tconfig.getInt32( "mpkey.pause", CRCInput::RC_yellow ); + g_settings.mpkey_stop = tconfig.getInt32( "mpkey.stop", CRCInput::RC_home ); + g_settings.mpkey_play = tconfig.getInt32( "mpkey.play", CRCInput::RC_ok ); + g_settings.mpkey_audio = tconfig.getInt32( "mpkey.audio", CRCInput::RC_green ); + g_settings.mpkey_time = tconfig.getInt32( "mpkey.time", CRCInput::RC_setup ); + g_settings.mpkey_bookmark = tconfig.getInt32( "mpkey.bookmark", CRCInput::RC_blue ); + g_settings.mpkey_plugin = tconfig.getInt32( "mpkey.plugin", CRCInput::RC_red ); + g_settings.key_timeshift = configfile.getInt32( "key_timeshift", CRCInput::RC_nokey ); + g_settings.key_plugin = configfile.getInt32( "key_plugin", CRCInput::RC_nokey ); + + g_settings.key_quickzap_up = tconfig.getInt32( "key_quickzap_up", CRCInput::RC_up ); + g_settings.key_quickzap_down = tconfig.getInt32( "key_quickzap_down", CRCInput::RC_down ); + g_settings.key_subchannel_up = tconfig.getInt32( "key_subchannel_up", CRCInput::RC_right ); + g_settings.key_subchannel_down = tconfig.getInt32( "key_subchannel_down", CRCInput::RC_left ); + g_settings.key_zaphistory = tconfig.getInt32( "key_zaphistory", CRCInput::RC_home ); + g_settings.key_lastchannel = tconfig.getInt32( "key_lastchannel", CRCInput::RC_0 ); + + g_settings.key_bouquet_up = tconfig.getInt32( "key_bouquet_up", CRCInput::RC_right); + g_settings.key_bouquet_down = tconfig.getInt32( "key_bouquet_down", CRCInput::RC_left); + strcpy(g_settings.repeat_blocker, tconfig.getString("repeat_blocker", "300").c_str()); + strcpy(g_settings.repeat_genericblocker, tconfig.getString("repeat_genericblocker", "100").c_str()); +} + +void CNeutrinoApp::saveKeys(const char * fname) +{ + CConfigFile tconfig(',', true); + //rc-key configuration + tconfig.setInt32( "key_tvradio_mode", g_settings.key_tvradio_mode ); + + tconfig.setInt32( "key_channelList_pageup", g_settings.key_channelList_pageup ); + tconfig.setInt32( "key_channelList_pagedown", g_settings.key_channelList_pagedown ); + tconfig.setInt32( "key_channelList_cancel", g_settings.key_channelList_cancel ); + tconfig.setInt32( "key_channelList_sort", g_settings.key_channelList_sort ); + tconfig.setInt32( "key_channelList_addrecord", g_settings.key_channelList_addrecord ); + tconfig.setInt32( "key_channelList_addremind", g_settings.key_channelList_addremind ); + + tconfig.setInt32( "key_quickzap_up", g_settings.key_quickzap_up ); + tconfig.setInt32( "key_quickzap_down", g_settings.key_quickzap_down ); + tconfig.setInt32( "key_bouquet_up", g_settings.key_bouquet_up ); + tconfig.setInt32( "key_bouquet_down", g_settings.key_bouquet_down ); + tconfig.setInt32( "key_subchannel_up", g_settings.key_subchannel_up ); + tconfig.setInt32( "key_subchannel_down", g_settings.key_subchannel_down ); + tconfig.setInt32( "key_zaphistory", g_settings.key_zaphistory ); + tconfig.setInt32( "key_lastchannel", g_settings.key_lastchannel ); + + tconfig.setInt32( "key_list_start", g_settings.key_list_start ); + tconfig.setInt32( "key_list_end", g_settings.key_list_end ); + tconfig.setInt32( "menu_left_exit", g_settings.menu_left_exit ); + tconfig.setInt32( "audio_run_player", g_settings.audio_run_player ); + tconfig.setInt32( "key_click", g_settings.key_click ); + tconfig.setInt32( "mpkey.rewind", g_settings.mpkey_rewind ); + tconfig.setInt32( "mpkey.forward", g_settings.mpkey_forward ); + tconfig.setInt32( "mpkey.pause", g_settings.mpkey_pause ); + tconfig.setInt32( "mpkey.stop", g_settings.mpkey_stop ); + tconfig.setInt32( "mpkey.play", g_settings.mpkey_play ); + tconfig.setInt32( "mpkey.audio", g_settings.mpkey_audio ); + tconfig.setInt32( "mpkey.time", g_settings.mpkey_time ); + tconfig.setInt32( "mpkey.bookmark", g_settings.mpkey_bookmark ); + tconfig.setInt32( "mpkey.plugin", g_settings.mpkey_plugin ); + tconfig.setInt32( "key_timeshift", g_settings.key_timeshift ); + tconfig.setInt32( "key_plugin", g_settings.key_plugin ); + tconfig.setInt32( "key_unlock", g_settings.key_unlock ); + + tconfig.setString( "repeat_blocker", g_settings.repeat_blocker ); + tconfig.setString( "repeat_genericblocker", g_settings.repeat_genericblocker ); + tconfig.saveConfig(fname); +} diff --git a/src/neutrino.h b/src/neutrino.h new file mode 100644 index 000000000..31336e47a --- /dev/null +++ b/src/neutrino.h @@ -0,0 +1,255 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __neutrino__ +#define __neutrino__ + +#include + +#include +#include +#include +#include +#include +#include +#include /* CChannelList */ +#include +#include /* st_rmsg */ + +#include + +#include + +#define widest_number "2" + +#define ANNOUNCETIME (1 * 60) + +/************************************************************************************** +* * +* CNeutrinoApp - main run-class * +* * +**************************************************************************************/ + +typedef struct neutrino_font_descr +{ + const char * name; + const char * filename; + int size_offset; +} neutrino_font_descr_struct; + +typedef struct font_sizes +{ + const neutrino_locale_t name; + const unsigned int defaultsize; + const unsigned int style; + const unsigned int size_offset; +} font_sizes_struct; + +typedef struct font_sizes_groups +{ + const neutrino_locale_t groupname; + const unsigned int count; + const SNeutrinoSettings::FONT_TYPES * const content; + const char * const actionkey; +} font_sizes_groups_struct; + + +class CNeutrinoApp : public CMenuTarget, CChangeObserver +{ + public: + enum + { + RECORDING_OFF = 0, + RECORDING_SERVER = 1, + RECORDING_VCR = 2, + RECORDING_FILE = 3 + }; + + private: + CFrameBuffer * frameBuffer; + + enum + { + mode_unknown = -1, + mode_tv = 1, + mode_radio = 2, + mode_scart = 3, + mode_standby = 4, + mode_audio = 5, + mode_pic = 6, + mode_ts = 7, + mode_off = 8, + mode_mask = 0xFF, + norezap = 0x100 + }; + + CConfigFile configfile; + CScanSettings scanSettings; + int network_dhcp; + int network_automatic_start; + + neutrino_font_descr_struct font; + + int mode; + int lastMode; + bool softupdate; + bool fromflash; + CTimerd::RecordingInfo* nextRecordingInfo; + //bool record_mode; + + struct timeval standby_pressed_at; + + CZapitClient::responseGetLastChannel firstchannel; + st_rmsg sendmessage; + + int current_muted; + + bool skipShutdownTimer; + + CColorSetupNotifier *colorSetupNotifier; + CKeySetupNotifier *keySetupNotifier; + CNVODChangeExec *NVODChanger; + CStreamFeaturesChangeExec *StreamFeaturesChanger; + CMoviePluginChangeExec *MoviePluginChanger; + COnekeyPluginChangeExec *OnekeyPluginChanger; + CIPChangeNotifier *MyIPChanger; +// CVCRControl *vcrControl; + CConsoleDestChangeNotifier *ConsoleDestinationChanger; + CRCLock *rcLock; + // USERMENU + CTimerList *Timerlist; + + bool showUserMenu(int button); + bool getNVODMenu(CMenuWidget* menu); + + void firstChannel(); + void setupColors_red(); + void setupColors_ru(); + void setupColors_dvb2000(); + void setupColors_classic(); + void setupColors_neutrino(); + void setupColors_dblue(); + void setupColors_dvb2k(); + void setupNetwork( bool force= false ); + void setupNFS(); + void setupRecordingDevice(void); + + void startNextRecording(); + + void tvMode( bool rezap = true ); + void radioMode( bool rezap = true ); + void scartMode( bool bOnOff ); + void standbyMode( bool bOnOff ); + void AudioMute( int newValue, bool isEvent= false ); + void setvol(int vol, int avs); + void saveEpg(); + + void ExitRun(const bool write_si = true, int retcode = 0); + void RealRun(CMenuWidget &mainSettings); + void InitZapper(); + void InitKeySettings(CMenuWidget &); + void InitServiceSettings(CMenuWidget &, CMenuWidget &); + void InitColorSettingsMenuColors(CMenuWidget &); + void InitAudioSettings(CMenuWidget &audioSettings, CAudioSetupNotifier* audioSetupNotifier); + void InitColorSettings(CMenuWidget &, CMenuWidget &); + void InitLanguageSettings(CMenuWidget &); + void InitColorThemesSettings(CMenuWidget &); + void InitColorSettingsStatusBarColors(CMenuWidget &colorSettings_menuColors); + void InitColorSettingsTiming(CMenuWidget &colorSettings_timing); + void InitLcdSettings(CMenuWidget &lcdSettings); + void InitNetworkSettings(CMenuWidget &networkSettings); + void AddFontSettingItem(CMenuWidget &fontSettings, const SNeutrinoSettings::FONT_TYPES number_of_fontsize_entry); + void InitFontSettings(CMenuWidget &fontSettings); + void InitRecordingSettings(CMenuWidget &recordingSettings); + void InitStreamingSettings(CMenuWidget &streamingSettings); + void InitScreenSettings(CMenuWidget &); + void InitAudioplPicSettings(CMenuWidget &); + void InitMiscSettings(CMenuWidget &); + void InitScanSettings(CMenuWidget &); + void InitParentalLockSettings(CMenuWidget &); + void InitMainMenu(CMenuWidget &mainMenu, CMenuWidget &mainSettings, CMenuWidget &audioSettings, + CMenuWidget &parentallockSettings, CMenuWidget &networkSettings1, CMenuWidget &networkSettings2, + CMenuWidget &colorSettings, CMenuWidget &lcdSettings, CMenuWidget &keySettings, + CMenuWidget &languageSettings, CMenuWidget &miscSettings, CMenuWidget &service, CMenuWidget &fontSettings, + CMenuWidget &audioplPicSettings, CMenuWidget &streamingSettings, CMenuWidget &moviePlayer); + void SetupFrameBuffer(); + void SelectAPID(); + void SelectNVOD(); + void CmdParser(int argc, char **argv); + void ShowStreamFeatures(); + void InitSCSettings(CMenuWidget &); + bool doGuiRecord(char * preselectedDir, bool addTimer = false); + void saveColors(const char * fname); + void loadKeys(const char * fname); + void saveKeys(const char * fname); + CNeutrinoApp(); + + public: + void saveSetup(const char * fname); + int loadSetup(const char * fname); + void loadColors(const char * fname); + void SetupTiming(); + void SetupFonts(); + + void setVolume(const neutrino_msg_t key, const bool bDoPaint = true, bool nowait = false); + ~CNeutrinoApp(); + CScanSettings& getScanSettings(){ return scanSettings;}; + + CChannelList *TVchannelList; + CChannelList *RADIOchannelList; + CChannelList *channelList; + CNetworkConfig networkConfig; + + static CNeutrinoApp* getInstance(); + + void channelsInit(bool bOnly = false); + int run(int argc, char **argv); + + //callback stuff only.... + int exec(CMenuTarget* parent, const std::string & actionKey); + + //onchange + bool changeNotify(const neutrino_locale_t OptionName, void *); + + int handleMsg(const neutrino_msg_t msg, neutrino_msg_data_t data); + + int getMode() { return mode; } + int getLastMode() { return lastMode; } + bool isMuted() { return current_muted; } + int recordingstatus; + int recording_id; + void SendSectionsdConfig(void); + int GetChannelMode(void) { return g_settings.channel_mode; }; + void SetChannelMode(int mode); + void quickZap(int msg); +}; +#endif diff --git a/src/neutrinoMessages.h b/src/neutrinoMessages.h new file mode 100644 index 000000000..2b636028e --- /dev/null +++ b/src/neutrinoMessages.h @@ -0,0 +1,168 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef __neutrinoMessages__ +#define __neutrinoMessages__ + +#include "driver/rcinput.h" + + +struct messages_return +{ + enum + { + none = 0x00, + handled = 0x01, + unhandled = 0x02, + cancel_all = 0x04, + cancel_info = 0x08 + }; +}; + +struct NeutrinoMessages { + enum + { + SHOW_EPG = CRCInput::RC_Messages + 1, + SHOW_INFOBAR = CRCInput::RC_Messages + 2, + VCR_ON = CRCInput::RC_Messages + 3, + VCR_OFF = CRCInput::RC_Messages + 4, + STANDBY_ON = CRCInput::RC_Messages + 5, + STANDBY_OFF = CRCInput::RC_Messages + 6, + STANDBY_TOGGLE = CRCInput::RC_Messages + 7, + SHUTDOWN = CRCInput::RC_Messages + 8, + ANNOUNCE_SHUTDOWN = CRCInput::RC_Messages + 9, + ANNOUNCE_ZAPTO = CRCInput::RC_Messages + 10, + ZAPTO = CRCInput::RC_Messages + 11, + ANNOUNCE_RECORD = CRCInput::RC_Messages + 12, + RECORD_START = CRCInput::RC_Messages + 13, + RECORD_STOP = CRCInput::RC_Messages + 14, + ANNOUNCE_SLEEPTIMER = CRCInput::RC_Messages + 15, + SLEEPTIMER = CRCInput::RC_Messages + 16, + CHANGEMODE = CRCInput::RC_Messages + 17, + REMIND = CRCInput::RC_Messages + 18, + LOCK_RC = CRCInput::RC_Messages + 19, + UNLOCK_RC = CRCInput::RC_Messages + 20, + + EVT_VOLCHANGED = CRCInput::RC_Events + 1, + EVT_MUTECHANGED = CRCInput::RC_Events + 2, + EVT_VCRCHANGED = CRCInput::RC_Events + 3, + EVT_MODECHANGED = CRCInput::RC_Events + 4, + EVT_BOUQUETSCHANGED = CRCInput::RC_Events + 6, + EVT_SERVICESCHANGED = CRCInput::RC_Events + 7, + EVT_SCAN_COMPLETE = CRCInput::RC_Events + 16, + EVT_SCAN_NUM_TRANSPONDERS = CRCInput::RC_Events + 17, + EVT_SCAN_NUM_CHANNELS = CRCInput::RC_Events + 18, + EVT_SHUTDOWN = CRCInput::RC_Events + 19, + EVT_TIMER = CRCInput::RC_Events + 20, + EVT_PROGRAMLOCKSTATUS = CRCInput::RC_Events + 22, + EVT_RECORDMODE = CRCInput::RC_Events + 24, +#ifndef SKIP_CA_STATUS + EVT_ZAP_CA_ID = CRCInput::RC_Events + 50, + EVT_ZAP_CA_CLEAR = CRCInput::RC_Events + 26, + EVT_ZAP_CA_LOCK = CRCInput::RC_Events + 27, + EVT_ZAP_CA_FTA = CRCInput::RC_Events + 28, +#endif + EVT_SCAN_FAILED = CRCInput::RC_Events + 29, + EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS = CRCInput::RC_Events + 30, + EVT_SCAN_REPORT_FREQUENCY = CRCInput::RC_Events + 31, + EVT_SCAN_FOUND_RADIO_CHAN = CRCInput::RC_Events + 32, + EVT_SCAN_FOUND_DATA_CHAN = CRCInput::RC_Events + 33, + EVT_SCAN_FOUND_TV_CHAN = CRCInput::RC_Events + 34, + EVT_SCAN_REPORT_FREQUENCYP = CRCInput::RC_Events + 36, + EVT_ZAP_MOTOR = CRCInput::RC_Events + 37, + /* sectionsd */ + EVT_SERVICES_UPD = CRCInput::RC_Events + 38, + EVT_SI_FINISHED = CRCInput::RC_Events + 39, + + /* NEVER CHANGE THIS */ + EVT_CI_INSERTED = CRCInput::RC_Events + 60, /* data = slot num */ + EVT_CI_REMOVED = CRCInput::RC_Events + 61, /* data = slot num */ + EVT_CI_INIT_OK = CRCInput::RC_Events + 62, /* data = slot num */ + EVT_CI_MMI_MENU = CRCInput::RC_Events + 63, + EVT_CI_MMI_LIST = CRCInput::RC_Events + 64, + EVT_CI_MMI_TEXT = CRCInput::RC_Events + 65, + EVT_CI_MMI_REQUEST_INPUT = CRCInput::RC_Events + 66, + EVT_CI_MMI_CLOSE = CRCInput::RC_Events + 67, + /* END */ + + EVT_CURRENTEPG = CRCInput::RC_WithData + 1, + EVT_NEXTEPG = CRCInput::RC_WithData + 2, + EVT_POPUP = CRCInput::RC_WithData + 3, + EVT_EXTMSG = CRCInput::RC_WithData + 4, + EVT_START_PLUGIN = CRCInput::RC_WithData + 5, + + /* sectionsd */ + EVT_CURRENTNEXT_EPG = CRCInput::RC_WithData + 6, /* data: (t_channel_id *) */ + EVT_TIMESET = CRCInput::RC_WithData + 7, /* data: (long long *) */ + + /* "sectionsd" events triggered by neutrino */ + EVT_NOEPG_YET = CRCInput::RC_WithData + 8, /* data: (t_channel_id *) */ + + /* "timerd" events triggered by neutrino */ + EVT_NEXTPROGRAM = CRCInput::RC_WithData + 9, /* data: (t_channel_id *) */ + + /* zapit */ + EVT_SCAN_FOUND_A_CHAN = CRCInput::RC_WithData + 10, + EVT_SCAN_PROVIDER = CRCInput::RC_WithData + 11, + EVT_SCAN_SATELLITE = CRCInput::RC_WithData + 12, + EVT_SCAN_SERVICENAME = CRCInput::RC_WithData + 13, + EVT_ZAP_COMPLETE = CRCInput::RC_WithData + 14, /* data: (t_channel_id *) */ + EVT_ZAP_FAILED = CRCInput::RC_WithData + 15, /* data: (t_channel_id *) */ + EVT_ZAP_ISNVOD = CRCInput::RC_WithData + 16, /* data: (t_channel_id *) */ + EVT_ZAP_SUB_COMPLETE = CRCInput::RC_WithData + 17, /* data: (t_channel_id *) */ + EVT_ZAP_SUB_FAILED = CRCInput::RC_WithData + 18, /* data: (t_channel_id *) */ + + /* "zapit" events triggered by neutrino */ + EVT_ZAP_GOT_SUBSERVICES = CRCInput::RC_WithData + 19, /* data: (t_channel_id *) */ + EVT_ZAP_GOTAPIDS = CRCInput::RC_WithData + 20, /* data: (t_channel_id *) */ + EVT_ZAP_GOTPIDS = CRCInput::RC_WithData + 21, /* data: (t_channel_id *) */ + + /* neutrino */ + EVT_RECORDING_ENDED = CRCInput::RC_WithData + 22 + }; + enum + { + mode_unknown = -1, + mode_tv = 1, + mode_radio = 2, + mode_scart = 3, + mode_standby = 4, + mode_audio = 5, + mode_pic = 6, + mode_ts = 7, + mode_mask = 0xFF, + norezap = 0x100 + }; +}; + + +#endif diff --git a/src/neutrino_menue.cpp b/src/neutrino_menue.cpp new file mode 100644 index 000000000..a89e04c1a --- /dev/null +++ b/src/neutrino_menue.cpp @@ -0,0 +1,3019 @@ +/* + Neutrino-GUI - DBoxII-Project + + Copyright (C) 2001 Steffen Hehn 'McClean' + and some other guys + Homepage: http://dbox.cyberphoria.org/ + + Kommentar: + + Diese GUI wurde von Grund auf neu programmiert und sollte nun vom + Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert + auf der Client-Server Idee, diese GUI ist also von der direkten DBox- + Steuerung getrennt. Diese wird dann von Daemons uebernommen. + + + License: GPL + + 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., 675 Mass Ave, Cambridge, MA 02139, USA +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +#include "global.h" +#include "neutrino.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gui/widget/colorchooser.h" +#include "gui/widget/menue.h" +#include "gui/widget/messagebox.h" +#include "gui/widget/hintbox.h" +#include "gui/widget/icons.h" +#include "gui/widget/lcdcontroler.h" +#include "gui/widget/vfdcontroler.h" +#include "gui/widget/rgbcsynccontroler.h" +#include "gui/widget/keychooser.h" +#include "gui/widget/stringinput.h" +#include "gui/widget/stringinput_ext.h" +#include "gui/widget/mountchooser.h" + +#include "gui/color.h" +#include "gui/customcolor.h" + +#include "gui/bedit/bouqueteditor_bouquets.h" +#include "gui/bouquetlist.h" +#include "gui/eventlist.h" +#include "gui/channellist.h" +#include "gui/screensetup.h" +#include "gui/pluginlist.h" +#include "gui/plugins.h" +#include "gui/infoviewer.h" +#include "gui/epgview.h" +#include "gui/epg_menu.h" +#include "gui/update.h" +#include "gui/scan.h" +#include "gui/favorites.h" +#include "gui/sleeptimer.h" +#include "gui/rc_lock.h" +#include "gui/timerlist.h" +#include "gui/alphasetup.h" +#include "gui/audioplayer.h" +#include "gui/imageinfo.h" +#include "gui/movieplayer.h" +#include "gui/nfs.h" +#include "gui/pictureviewer.h" +#include "gui/motorcontrol.h" +#include "gui/filebrowser.h" +#include "gui/scale.h" +#include "gui/upnpbrowser.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "gui/infoclock.h" +#include "mymenu.h" +#include "gui/dboxinfo.h" +#include "gui/hdd_menu.h" +#include "gui/audio_select.h" +#include "gui/cam_menu.h" + +#include +#include + +//#define TEST_MENU + +extern CFrontend * frontend; +extern CAudioPlayerGui * audioPlayer; +extern CMoviePlayerGui* moviePlayerGui; +extern CPlugins * g_PluginList; +extern bool has_hdd; +extern CZapitClient::SatelliteList satList; +extern Zapit_config zapitCfg; +extern char zapit_lat[20]; +extern char zapit_long[20]; +extern char current_timezone[50]; +extern cVideo *videoDecoder; +extern cAudio *audioDecoder; +extern int prev_video_mode; +extern bool parentallocked; +extern const char * locale_real_names[]; +extern CFontSizeNotifier fontsizenotifier; +extern CFanControlNotifier * funNotifier; +extern CRemoteControl * g_RemoteControl; +extern CCAMMenuHandler * g_CamHandler; +extern bool autoshift; +extern unsigned int system_rev; + +//extern int sectionsd_scanning; + +static CTimingSettingsNotifier timingsettingsnotifier; + +#define get_set CNeutrinoApp::getInstance()->getScanSettings() + +int safe_mkdir(char * path); + +#define OPTIONS_OFF0_ON1_OPTION_COUNT 2 +const CMenuOptionChooser::keyval OPTIONS_OFF0_ON1_OPTIONS[OPTIONS_OFF0_ON1_OPTION_COUNT] = +{ + { 0, LOCALE_OPTIONS_OFF }, + { 1, LOCALE_OPTIONS_ON } +}; + +#define OPTIONS_OFF1_ON0_OPTION_COUNT 2 +const CMenuOptionChooser::keyval OPTIONS_OFF1_ON0_OPTIONS[OPTIONS_OFF1_ON0_OPTION_COUNT] = +{ + { 1, LOCALE_OPTIONS_OFF }, + { 0, LOCALE_OPTIONS_ON } +}; + +#define VIDEOMENU_VIDEOSIGNAL_HD1_OPTION_COUNT 8 +const CMenuOptionChooser::keyval VIDEOMENU_VIDEOSIGNAL_HD1_OPTIONS[VIDEOMENU_VIDEOSIGNAL_HD1_OPTION_COUNT] = +{ + { ANALOG_SD_RGB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_SD_RGB_SCART }, /* composite + RGB (for both SCART and Cinch) */ + { ANALOG_SD_RGB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_SD_RGB_CINCH }, /* composite + RGB (for both SCART and Cinch) */ + { ANALOG_SD_YPRPB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_SD_YPRPB_SCART }, /* YPbPr SCART (with wrongly connected Cinch) */ + { ANALOG_SD_YPRPB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_SD_YPRPB_CINCH }, /* YPbPr Cinch (with wrongly connected SCART) */ + { ANALOG_HD_RGB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_HD_RGB_SCART }, + { ANALOG_HD_RGB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_HD_RGB_CINCH }, + { ANALOG_HD_YPRPB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_HD_YPRPB_SCART }, + { ANALOG_HD_YPRPB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_HD_YPRPB_CINCH } +}; + +#define VIDEOMENU_VIDEOSIGNAL_HD1PLUS_SCART_OPTION_COUNT 4 +const CMenuOptionChooser::keyval VIDEOMENU_VIDEOSIGNAL_HD1PLUS_SCART_OPTIONS[VIDEOMENU_VIDEOSIGNAL_HD1PLUS_SCART_OPTION_COUNT] = +{ + { ANALOG_SD_RGB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_SD_RGB_SCART }, /* composite + RGB */ + { ANALOG_SD_YPRPB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_SD_YPRPB_SCART }, /* YPbPr SCART */ + { ANALOG_HD_RGB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_HD_RGB_SCART }, + { ANALOG_HD_YPRPB_SCART, LOCALE_VIDEOMENU_ANALOG_MODE_HD_YPRPB_SCART }, +}; + +#define VIDEOMENU_VIDEOSIGNAL_HD1PLUS_CINCH_OPTION_COUNT 4 +const CMenuOptionChooser::keyval VIDEOMENU_VIDEOSIGNAL_HD1PLUS_CINCH_OPTIONS[VIDEOMENU_VIDEOSIGNAL_HD1PLUS_CINCH_OPTION_COUNT] = +{ + { ANALOG_SD_RGB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_SD_RGB_CINCH }, /* composite + RGB (for both SCART and Cinch) */ + { ANALOG_SD_YPRPB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_SD_YPRPB_CINCH }, /* YPbPr Cinch (with wrongly connected SCART) */ + { ANALOG_HD_RGB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_HD_RGB_CINCH }, + { ANALOG_HD_YPRPB_CINCH, LOCALE_VIDEOMENU_ANALOG_MODE_HD_YPRPB_CINCH } +}; + +#define VIDEOMENU_VCRSIGNAL_OPTION_COUNT 2 +const CMenuOptionChooser::keyval VIDEOMENU_VCRSIGNAL_OPTIONS[VIDEOMENU_VCRSIGNAL_OPTION_COUNT] = +{ + { 2, LOCALE_VIDEOMENU_VCRSIGNAL_SVIDEO }, + { 0, LOCALE_VIDEOMENU_VCRSIGNAL_COMPOSITE } +}; + +#define VIDEOMENU_VIDEOFORMAT_OPTION_COUNT 3//2 +const CMenuOptionChooser::keyval VIDEOMENU_VIDEOFORMAT_OPTIONS[VIDEOMENU_VIDEOFORMAT_OPTION_COUNT] = +{ + { 1, LOCALE_VIDEOMENU_VIDEOFORMAT_43 }, + { 3, LOCALE_VIDEOMENU_VIDEOFORMAT_169 }, + { 2, LOCALE_VIDEOMENU_VIDEOFORMAT_149 } +}; + +#define VIDEOMENU_43MODE_OPTION_COUNT 4 +const CMenuOptionChooser::keyval VIDEOMENU_43MODE_OPTIONS[VIDEOMENU_43MODE_OPTION_COUNT] = +{ + { DISPLAY_AR_MODE_PANSCAN, LOCALE_VIDEOMENU_PANSCAN }, + { DISPLAY_AR_MODE_PANSCAN2, LOCALE_VIDEOMENU_PANSCAN2 }, + { DISPLAY_AR_MODE_LETTERBOX, LOCALE_VIDEOMENU_LETTERBOX }, + { DISPLAY_AR_MODE_NONE, LOCALE_VIDEOMENU_FULLSCREEN } + //{ 2, LOCALE_VIDEOMENU_AUTO } // whatever is this auto mode, it seems its totally broken +}; + +/* numbers corresponding to video.cpp from zapit */ +//#define VIDEOMENU_VIDEOMODE_OPTION_COUNT 11 +const CMenuOptionChooser::keyval VIDEOMENU_VIDEOMODE_OPTIONS[VIDEOMENU_VIDEOMODE_OPTION_COUNT] = +{ + { 1, NONEXISTANT_LOCALE, "SECAM" }, + { 2, NONEXISTANT_LOCALE, "PAL" }, + { 4, NONEXISTANT_LOCALE, "576p" }, + { 7, NONEXISTANT_LOCALE, "720p 50Hz" }, + { 8, NONEXISTANT_LOCALE, "1080i 50Hz" }, + { 10, NONEXISTANT_LOCALE, "1080p 24Hz" }, + { 11, NONEXISTANT_LOCALE, "1080p 25Hz" }, + + { 0, NONEXISTANT_LOCALE, "NTSC" }, + { 3, NONEXISTANT_LOCALE, "480p" }, + { 5, NONEXISTANT_LOCALE, "720p 60Hz" }, + { 6, NONEXISTANT_LOCALE, "1080i 60Hz" }, + { VIDEO_STD_AUTO, NONEXISTANT_LOCALE, "Auto" } +}; + +#define VIDEOMENU_DBDR_OPTION_COUNT 3 +const CMenuOptionChooser::keyval VIDEOMENU_DBDR_OPTIONS[VIDEOMENU_DBDR_OPTION_COUNT] = +{ + { 0, LOCALE_VIDEOMENU_DBDR_NONE }, + { 1, LOCALE_VIDEOMENU_DBDR_DEBLOCK }, + { 2, LOCALE_VIDEOMENU_DBDR_BOTH } +}; + +#include "videosettings.h" +CVideoSettings::CVideoSettings() : CMenuWidget(LOCALE_VIDEOMENU_HEAD, "video.raw"), RGBCSyncControler(LOCALE_VIDEOMENU_RGB_CENTERING, &g_settings.video_csync) +{ + addItem(GenericMenuSeparator); + addItem(GenericMenuBack); + + addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_VIDEOMENU_TV_SCART)); + + if (system_rev == 0x06) { + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_ANALOG_MODE, &g_settings.analog_mode1, VIDEOMENU_VIDEOSIGNAL_HD1_OPTIONS, VIDEOMENU_VIDEOSIGNAL_HD1_OPTION_COUNT, true, this)); + } else if (system_rev > 0x06) { + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_SCART, &g_settings.analog_mode1, VIDEOMENU_VIDEOSIGNAL_HD1PLUS_SCART_OPTIONS, VIDEOMENU_VIDEOSIGNAL_HD1PLUS_SCART_OPTION_COUNT, true, this)); + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_CINCH, &g_settings.analog_mode2, VIDEOMENU_VIDEOSIGNAL_HD1PLUS_CINCH_OPTIONS, VIDEOMENU_VIDEOSIGNAL_HD1PLUS_CINCH_OPTION_COUNT, true, this)); + } + + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_VIDEOFORMAT, &g_settings.video_Format, VIDEOMENU_VIDEOFORMAT_OPTIONS, VIDEOMENU_VIDEOFORMAT_OPTION_COUNT, true, this)); + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_43MODE, &g_settings.video_43mode, VIDEOMENU_43MODE_OPTIONS, VIDEOMENU_43MODE_OPTION_COUNT, true, this)); + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_VIDEOMODE, &g_settings.video_Mode, VIDEOMENU_VIDEOMODE_OPTIONS, VIDEOMENU_VIDEOMODE_OPTION_COUNT, true, this, CRCInput::RC_nokey, "", true)); + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_DBDR, &g_settings.video_dbdr, VIDEOMENU_DBDR_OPTIONS, VIDEOMENU_DBDR_OPTION_COUNT, true, this)); + + CMenuWidget* menu = new CMenuWidget(LOCALE_VIDEOMENU_ENABLED_MODES, NEUTRINO_ICON_SETTINGS); + for(int i = 0; i < VIDEOMENU_VIDEOMODE_OPTION_COUNT; i++) + menu->addItem(new CMenuOptionChooser(VIDEOMENU_VIDEOMODE_OPTIONS[i].valname, &g_settings.enabled_video_modes[i], OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + addItem(new CMenuForwarder(LOCALE_VIDEOMENU_ENABLED_MODES, true, NULL, menu)); +#if 0 + addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_VIDEOMENU_VCR_SCART)); + addItem(new CMenuOptionChooser(LOCALE_VIDEOMENU_VCRSWITCH, &g_settings.vcr_AutoSwitch, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_VIDEOMENU_OSD)); + addItem(new CMenuForwarder(LOCALE_VIDEOMENU_SCREENSETUP, true, NULL, &ScreenSetup, NULL, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); +#endif + changeNotify(LOCALE_VIDEOMENU_VIDEOFORMAT, NULL); + if (system_rev == 0x06) { + changeNotify(LOCALE_VIDEOMENU_ANALOG_MODE, NULL); + } else { + changeNotify(LOCALE_VIDEOMENU_SCART, NULL); + changeNotify(LOCALE_VIDEOMENU_CINCH, NULL); + } +}; + +void CVideoSettings::nextMode(void) +{ + const char * text; + int curmode = 0; + int i; + + for(i = 0; i < VIDEOMENU_VIDEOMODE_OPTION_COUNT; i++) { + if(VIDEOMENU_VIDEOMODE_OPTIONS[i].key == g_settings.video_Mode) { + curmode = i; + break; + } + } + + i = 0; + while(true) { + curmode++; + if(curmode >= VIDEOMENU_VIDEOMODE_OPTION_COUNT) + curmode = 0; + if(g_settings.enabled_video_modes[curmode]) + break; + i++; + if(i >= VIDEOMENU_VIDEOMODE_OPTION_COUNT) + return; + } + + text = VIDEOMENU_VIDEOMODE_OPTIONS[curmode].valname; + + g_settings.video_Mode = VIDEOMENU_VIDEOMODE_OPTIONS[curmode].key; + videoDecoder->SetVideoSystem(g_settings.video_Mode); + //videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode);//FIXME + ShowHintUTF(LOCALE_VIDEOMENU_VIDEOMODE, text, 450, 2); +} + +void CVideoSettings::next43Mode(void) +{ + neutrino_locale_t text; + int curmode = 0; + + for(int i = 0; i < VIDEOMENU_43MODE_OPTION_COUNT; i++) { + if(VIDEOMENU_43MODE_OPTIONS[i].key == g_settings.video_43mode) { + curmode = i; + break; + } + } + curmode++; + if(curmode >= VIDEOMENU_43MODE_OPTION_COUNT) + curmode = 0; + + text = VIDEOMENU_43MODE_OPTIONS[curmode].value; + g_settings.video_43mode = VIDEOMENU_43MODE_OPTIONS[curmode].key; + videoDecoder->setAspectRatio(-1, g_settings.video_43mode); + ShowHintUTF(LOCALE_VIDEOMENU_43MODE, g_Locale->getText(text), 450, 2); +} + +void CVideoSettings::SwitchFormat(void) +{ + neutrino_locale_t text; + int curmode = 0; + + for(int i = 0; i < VIDEOMENU_VIDEOFORMAT_OPTION_COUNT; i++) { + if(VIDEOMENU_VIDEOFORMAT_OPTIONS[i].key == g_settings.video_Format) { + curmode = i; + break; + } + } + curmode++; + if(curmode >= VIDEOMENU_VIDEOFORMAT_OPTION_COUNT) + curmode = 0; + + text = VIDEOMENU_VIDEOFORMAT_OPTIONS[curmode].value; + g_settings.video_Format = VIDEOMENU_VIDEOFORMAT_OPTIONS[curmode].key; + + videoDecoder->setAspectRatio(g_settings.video_Format, -1); + ShowHintUTF(LOCALE_VIDEOMENU_VIDEOFORMAT, g_Locale->getText(text), 450, 2); +} + +bool CVideoSettings::changeNotify(const neutrino_locale_t OptionName, void *) +{ + if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_ANALOG_MODE)) + { + videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode1); + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_SCART)) + { + videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode1); + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_CINCH)) + { + videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode2); + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_DBDR)) + { + videoDecoder->SetDBDR(g_settings.video_dbdr); + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_VCRSIGNAL)) + { + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_VIDEOFORMAT) || + ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_43MODE)) + { + //if(g_settings.video_Format != 1 && g_settings.video_Format != 3) + if(g_settings.video_Format != 1 && g_settings.video_Format != 3 && g_settings.video_Format != 2) + g_settings.video_Format = 3; + videoDecoder->setAspectRatio(g_settings.video_Format, g_settings.video_43mode); + } + else if (ARE_LOCALES_EQUAL(OptionName, LOCALE_VIDEOMENU_VIDEOMODE)) + { + videoDecoder->SetVideoSystem(g_settings.video_Mode); + //videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode);//FIXME + if(prev_video_mode != g_settings.video_Mode) { + frameBuffer->paintBackground(); + if(ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_VIDEOMODE_OK), CMessageBox::mbrNo, CMessageBox::mbYes | CMessageBox::mbNo, "info.raw") != CMessageBox::mbrYes) { + g_settings.video_Mode = prev_video_mode; + videoDecoder->SetVideoSystem(g_settings.video_Mode); + //videoDecoder->SetVideoMode((analog_mode_t) g_settings.analog_mode);//FIXME + } else + prev_video_mode = g_settings.video_Mode; + } + } + + return true; +}; + +void CVideoSettings::paint() +{ + CMenuWidget::paint(); +}; + +#ifdef TEST_MENU +class CTestMenu : public CMenuTarget +{ + public: + int exec(CMenuTarget* parent, const std::string &actionkey); +}; + +#include +#include +#include + +int CTestMenu::exec(CMenuTarget* parent, const std::string &actionKey) +{ + if(parent) + parent->hide(); + + printf("CTestMenu::exec: %s\n", actionKey.c_str()); + if(actionKey == "vfd") { + CVFD::getInstance()->Clear(); + int icon = 0x00040000; + while(icon > 0x2) { + CVFD::getInstance()->ShowIcon((vfd_icon) icon, true); + icon /= 2; + } + for(int i = 0x01000001; i <= 0x0C000001; i+= 0x01000000) { + CVFD::getInstance()->ShowIcon((vfd_icon) i, true); + } + CVFD::getInstance()->ShowIcon((vfd_icon) 0x09000002, true); + CVFD::getInstance()->ShowIcon((vfd_icon) 0x0B000002, true); + char text[255]; + char buf[XML_UTF8_ENCODE_MAX]; + int ch = 0x2588; + int len = XmlUtf8Encode(ch, buf); + + for(int i = 0; i < 12; i++) { + memcpy(&text[i*len], buf, len); + } + text[12*len] = 0; + + CVFD::getInstance()->ShowText(text); + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, "VFD test, Press OK to return", CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + CVFD::getInstance()->Clear(); + } + else if(actionKey == "network") { + int fd, ret; + struct ifreq ifr; + char * ip = NULL, str[255]; + struct sockaddr_in *addrp=NULL; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); + + ret = ioctl(fd, SIOCGIFHWADDR, &ifr); + if(ret < 0) + perror("SIOCGIFHWADDR"); + + ret = ioctl(fd, SIOCGIFADDR, &ifr ); + if(ret < 0) + perror("SIOCGIFADDR"); + else { + addrp = (struct sockaddr_in *)&(ifr.ifr_addr); + ip = inet_ntoa(addrp->sin_addr); + } + + sprintf(str, "MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\nIP: %s", + (unsigned char)ifr.ifr_hwaddr.sa_data[0], + (unsigned char)ifr.ifr_hwaddr.sa_data[1], + (unsigned char)ifr.ifr_hwaddr.sa_data[2], + (unsigned char)ifr.ifr_hwaddr.sa_data[3], + (unsigned char)ifr.ifr_hwaddr.sa_data[4], + (unsigned char)ifr.ifr_hwaddr.sa_data[5], ip == NULL ? "Unknown" : ip); + + close(fd); + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, str, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + else if(actionKey == "card") { + } + else if(actionKey == "hdd") { + char buffer[255]; + FILE *f = fopen("/proc/mounts", "r"); + bool mounted = false; + if(f != NULL) { + while (fgets (buffer, 255, f) != NULL) { + if(strstr(buffer, "/dev/sda1")) { + mounted = true; + break; + } + } + fclose(f); + } + sprintf(buffer, "HDD: /dev/sda1 is %s", mounted ? "mounted" : "NOT mounted"); + printf("%s\n", buffer); + ShowMsgUTF(LOCALE_MESSAGEBOX_INFO, buffer, CMessageBox::mbrBack, CMessageBox::mbBack, "info.raw"); + } + else if(actionKey == "buttons") { + neutrino_msg_t msg; + neutrino_msg_data_t data; + CHintBox * khintBox = NULL; + CHintBox * hintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, "Press button, or press EXIT to return"); + hintBox->paint(); + while(1) { + g_RCInput->getMsg(&msg, &data, 100); + if(msg == CRCInput::RC_home) + break; + + if (msg != CRCInput::RC_timeout && msg <= CRCInput::RC_MaxRC) { + char keyname[50]; + sprintf(keyname, "Button [%s] pressed (EXIT to return)", g_RCInput->getKeyName(msg).c_str()); + if(khintBox) { + delete khintBox; + } + khintBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, keyname); + hintBox->hide(); + khintBox->paint(); + } + } + if(khintBox) + delete khintBox; + delete hintBox; + } + else if(actionKey == "22kon" || actionKey == "22koff") { + CScanTs * scanTs = new CScanTs(); + + int freq = (actionKey == "22kon") ? 12000*1000: 11000*1000; + + sprintf(get_set.TP_freq, "%d", freq); +#if 0 // not needed ? + switch(frontend->getInfo()->type) { + case FE_QPSK: + sprintf(get_set.TP_rate, "%d", tmpI->second.feparams.u.qpsk.symbol_rate); + get_set.TP_fec = tmpI->second.feparams.u.qpsk.fec_inner; + get_set.TP_pol = tmpI->second.polarization; + break; + case FE_QAM: + sprintf(get_set.TP_rate, "%d", tmpI->second.feparams.u.qam.symbol_rate); + get_set.TP_fec = tmpI->second.feparams.u.qam.fec_inner; + get_set.TP_mod = tmpI->second.feparams.u.qam.modulation; + break; + } +#endif + scanTs->exec(NULL, "test"); + delete scanTs; + } + else if(actionKey == "scan") { + CScanTs * scanTs = new CScanTs(); + + int freq = 12538000; + sprintf(get_set.TP_freq, "%d", freq); + switch(frontend->getInfo()->type) { + case FE_QPSK: + sprintf(get_set.TP_rate, "%d", 41250*1000); + get_set.TP_fec = 1; + get_set.TP_pol = 1; + break; + case FE_QAM: +#if 0 + sprintf(get_set.TP_rate, "%d", tmpI->second.feparams.u.qam.symbol_rate); + get_set.TP_fec = tmpI->second.feparams.u.qam.fec_inner; + get_set.TP_mod = tmpI->second.feparams.u.qam.modulation; +#endif + break; + case FE_OFDM: + case FE_ATSC: + break; + } + scanTs->exec(NULL, "manual"); + delete scanTs; + } + + return menu_return::RETURN_REPAINT; +} + +CMenuWidget * TestMenu; +#endif + +CVideoSettings * videoSettings; +CMenuOptionStringChooser* tzSelect; +/************************************************************************************** +* CNeutrinoApp - init main menu * +**************************************************************************************/ +void CNeutrinoApp::InitMainMenu(CMenuWidget &mainMenu, CMenuWidget &mainSettings, CMenuWidget &audioSettings, CMenuWidget &parentallockSettings, + CMenuWidget &networkSettings, CMenuWidget &recordingSettings, CMenuWidget &colorSettings, CMenuWidget &lcdSettings, + CMenuWidget &keySettings, CMenuWidget &languageSettings, CMenuWidget &miscSettings, + CMenuWidget &service, CMenuWidget &fontSettings, CMenuWidget &audiopl_picSettings, CMenuWidget &streamingSettings, CMenuWidget &moviePlayer) +{ + +#ifdef TEST_MENU + TestMenu = new CMenuWidget("Test menu"); + CTestMenu * testHandler = new CTestMenu(); + TestMenu->addItem(new CMenuForwarderNonLocalized("VFD", true, NULL, testHandler, "vfd")); + TestMenu->addItem(new CMenuForwarderNonLocalized("Network", true, NULL, testHandler, "network")); + TestMenu->addItem(new CMenuForwarderNonLocalized("Smartcard", true, NULL, testHandler, "card")); + TestMenu->addItem(new CMenuForwarderNonLocalized("HDD", true, NULL, testHandler, "hdd")); + TestMenu->addItem(new CMenuForwarderNonLocalized("Buttons", true, NULL, testHandler, "buttons")); + TestMenu->addItem(new CMenuForwarderNonLocalized("Scan 12538000", true, NULL, testHandler, "scan")); + //TestMenu->addItem(new CMenuForwarderNonLocalized("22 Khz ON", true, NULL, testHandler, "22kon")); + //TestMenu->addItem(new CMenuForwarderNonLocalized("22 Khz OFF", true, NULL, testHandler, "22koff")); +#endif + videoSettings = new CVideoSettings(); + + dprintf(DEBUG_DEBUG, "init mainmenue\n"); + mainMenu.addItem(GenericMenuSeparator); + + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_TVMODE, true, NULL, this, "tv", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED), true); + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_RADIOMODE, true, NULL, this, "radio", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + //mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_SCARTMODE, true, NULL, this, "scart", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_GAMES, true, NULL, new CPluginList(LOCALE_MAINMENU_GAMES,CPlugins::P_TYPE_GAME), "", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + mainMenu.addItem(GenericMenuSeparatorLine); + + mainMenu.addItem(new CMenuForwarder(LOCALE_UPNPBROWSER_HEAD, true, NULL, new CUpnpBrowserGui(), NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + audioPlayer = new CAudioPlayerGui(); + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_AUDIOPLAYER, true, NULL, audioPlayer, NULL, CRCInput::RC_1)); + + moviePlayerGui = new CMoviePlayerGui(); + //mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_MOVIEPLAYER, true, NULL, &moviePlayer, NULL, CRCInput::RC_2)); + +#if 0 + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_MOVIEPLAYER, true, NULL, moviePlayerGui, "tsmoviebrowser", CRCInput::RC_2)); +#else + moviePlayer.addItem(GenericMenuSeparator); + moviePlayer.addItem(GenericMenuBack); + moviePlayer.addItem(GenericMenuSeparatorLine); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEBROWSER_HEAD, true, NULL, moviePlayerGui, "tsmoviebrowser", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_FILEPLAYBACK, true, NULL, moviePlayerGui, "fileplayback", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_MOVIEPLAYER, true, NULL, &moviePlayer, NULL, CRCInput::RC_2)); +#endif +#if 0 + moviePlayer.addItem(GenericMenuSeparator); + moviePlayer.addItem(GenericMenuBack); + moviePlayer.addItem(GenericMenuSeparatorLine); + + + //moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_PESPLAYBACK, true, NULL, moviePlayerGui, "pesplayback")); + //moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_TSPLAYBACK_PC, true, NULL, moviePlayerGui, "tsplayback_pc")); + moviePlayer.addItem(new CLockedMenuForwarder(LOCALE_MOVIEBROWSER_HEAD, g_settings.parentallock_pincode, false, true, NULL, moviePlayerGui, "tsmoviebrowser")); + moviePlayer.addItem(new CLockedMenuForwarder(LOCALE_MOVIEPLAYER_TSPLAYBACK, g_settings.parentallock_pincode, false, true, NULL, moviePlayerGui, "tsplayback", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + + moviePlayer.addItem(new CLockedMenuForwarder(LOCALE_MOVIEPLAYER_BOOKMARK, g_settings.parentallock_pincode, false, true, NULL, moviePlayerGui, "bookmarkplayback")); + moviePlayer.addItem(GenericMenuSeparator); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_FILEPLAYBACK, true, NULL, moviePlayerGui, "fileplayback", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_DVDPLAYBACK, true, NULL, moviePlayerGui, "dvdplayback", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MOVIEPLAYER_VCDPLAYBACK, true, NULL, moviePlayerGui, "vcdplayback", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + moviePlayer.addItem(GenericMenuSeparatorLine); + moviePlayer.addItem(new CMenuForwarder(LOCALE_MAINMENU_SETTINGS, true, NULL, &streamingSettings, NULL, CRCInput::RC_help, NEUTRINO_ICON_BUTTON_HELP_SMALL)); + moviePlayer.addItem(new CMenuForwarder(LOCALE_NFSMENU_HEAD, true, NULL, new CNFSSmallMenu(), NULL, CRCInput::RC_setup, NEUTRINO_ICON_BUTTON_DBOX_SMALL)); +#endif + + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_PICTUREVIEWER, true, NULL, new CPictureViewerGui(), NULL, CRCInput::RC_3)); + int shortcut = 4; + if (g_PluginList->hasPlugin(CPlugins::P_TYPE_SCRIPT)) + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_SCRIPTS, true, NULL, new CPluginList(LOCALE_MAINMENU_SCRIPTS,CPlugins::P_TYPE_SCRIPT), "", + CRCInput::convertDigitToKey(shortcut++))); + mainMenu.addItem(GenericMenuSeparatorLine); + + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_SETTINGS, true, NULL, &mainSettings, NULL, + CRCInput::convertDigitToKey(shortcut++))); + mainMenu.addItem(new CLockedMenuForwarder(LOCALE_MAINMENU_SERVICE, g_settings.parentallock_pincode, false, true, NULL, &service, NULL, + CRCInput::convertDigitToKey(shortcut++))); + mainMenu.addItem(GenericMenuSeparatorLine); + + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_SLEEPTIMER, true, NULL, new CSleepTimerWidget, NULL, + CRCInput::convertDigitToKey(shortcut++))); + mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_REBOOT, true, NULL, this, "reboot", + CRCInput::convertDigitToKey(shortcut++))); + + //mainMenu.addItem(new CMenuForwarder(LOCALE_MAINMENU_SHUTDOWN, true, NULL, this, "shutdown", CRCInput::RC_standby, "power.raw"));//FIXME + + mainMenu.addItem( new CMenuSeparator(CMenuSeparator::LINE) ); + mainMenu.addItem( new CMenuForwarder(LOCALE_DBOXINFO, true, NULL, new CDBoxInfoWidget, NULL, CRCInput::convertDigitToKey(shortcut++))); + + mainSettings.addItem(GenericMenuSeparator); + mainSettings.addItem(GenericMenuBack); + mainSettings.addItem(GenericMenuSeparatorLine); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_SAVESETTINGSNOW, true, NULL, this, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + mainSettings.addItem(new CMenuForwarder(LOCALE_EXTRA_LOADCONFIG, true, NULL, this, "loadconfig")); + mainSettings.addItem(new CMenuForwarder(LOCALE_EXTRA_SAVECONFIG, true, NULL, this, "saveconfig")); + + CDataResetNotifier * resetNotifier = new CDataResetNotifier(); + mainSettings.addItem(new CMenuForwarder(LOCALE_RESET_SETTINGS , true, NULL, resetNotifier, "settings")); + + mainSettings.addItem(GenericMenuSeparatorLine); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_VIDEO , true, NULL, videoSettings , NULL, CRCInput::RC_1)); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_AUDIO , true, NULL, &audioSettings , NULL, CRCInput::RC_2)); + mainSettings.addItem(new CLockedMenuForwarder(LOCALE_PARENTALLOCK_PARENTALLOCK, g_settings.parentallock_pincode, true, true, NULL, &parentallockSettings, NULL, CRCInput::RC_3)); +#if 0 + if(g_settings.parentallock_prompt) + mainSettings.addItem(new CLockedMenuForwarder(LOCALE_PARENTALLOCK_PARENTALLOCK, g_settings.parentallock_pincode, true, true, NULL, &parentallockSettings, NULL, CRCInput::RC_3)); + else + mainSettings.addItem(new CMenuForwarder(LOCALE_PARENTALLOCK_PARENTALLOCK, true, NULL, &parentallockSettings, NULL, CRCInput::RC_3)); +#endif + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_NETWORK , true, NULL, &networkSettings , NULL, CRCInput::RC_4)); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_RECORDING , true, NULL, &recordingSettings, NULL, CRCInput::RC_5)); + //mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_STREAMING , true, NULL, &streamingSettings, NULL, CRCInput::RC_6)); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_LANGUAGE , true, NULL, &languageSettings , NULL, CRCInput::RC_6)); + + xmlDocPtr parser; + + parser = parseXmlFile("/etc/timezone.xml"); + if (parser != NULL) { + tzSelect = new CMenuOptionStringChooser(LOCALE_MAINSETTINGS_TIMEZONE, g_settings.timezone, true, new CTZChangeNotifier(), CRCInput::RC_7, "", true); + xmlNodePtr search = xmlDocGetRootElement(parser)->xmlChildrenNode; + bool found = false; + while (search) { + if (!strcmp(xmlGetName(search), "zone")) { + std::string name = xmlGetAttribute(search, (char *) "name"); + std::string zone = xmlGetAttribute(search, (char *) "zone"); + //printf("Timezone: %s -> %s\n", name.c_str(), zone.c_str()); + tzSelect->addOption(name.c_str()); + found = true; + } + search = search->xmlNextNode; + } + if(found) + mainSettings.addItem(tzSelect); + else { + delete tzSelect; + tzSelect = NULL; + } + xmlFreeDoc(parser); + } + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_OSD , true, NULL, &colorSettings , NULL, CRCInput::RC_8)); + + if(CVFD::getInstance()->has_lcd) + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_LCD , true, NULL, &lcdSettings , NULL, CRCInput::RC_9)); + + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_KEYBINDING, true, NULL, &keySettings , NULL, CRCInput::RC_blue , NEUTRINO_ICON_BUTTON_BLUE )); + mainSettings.addItem(new CMenuForwarder(LOCALE_AUDIOPLAYERPICSETTINGS_GENERAL , true, NULL, &audiopl_picSettings , NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_MISC , true, NULL, &miscSettings , NULL, CRCInput::RC_green , NEUTRINO_ICON_BUTTON_GREEN )); + + mainSettings.addItem(new CMenuForwarder(LOCALE_HDD_SETTINGS, true, NULL, new CHDDMenuHandler())); + mainSettings.addItem(new CMenuForwarder(LOCALE_CAM_SETTINGS, true, NULL, g_CamHandler)); + +#ifdef TEST_MENU + mainMenu.addItem(new CMenuForwarderNonLocalized("Test menu", true, NULL, TestMenu)); +#endif +#if 0 + g_RFmod = new RFmod(); + if(g_RFmod->rfmodfd >=0) { + g_RFmod->init(); + CMenuWidget * rfmenu = new CMenuWidget(LOCALE_RFMOD, "settings.raw"); + CRfNotifier * rfnot = new CRfNotifier(); + CRfExec * rfexec = new CRfExec(); + + rfmenu->addItem( GenericMenuBack ); + rfmenu->addItem( new CMenuForwarder(LOCALE_RECORDINGMENU_SETUPNOW, true, NULL, rfexec, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + rfmenu->addItem( GenericMenuSeparatorLine ); + + rfmenu->addItem( new CMenuOptionChooser(LOCALE_RF_CARRIER, &g_settings.rf_subcarrier, RF_CARRIER_OPTIONS, RF_CARRIER_OPTION_COUNT, true, rfnot)); + rfmenu->addItem( new CMenuOptionChooser(LOCALE_RF_ENABLE, &g_settings.rf_soundenable, OPTIONS_OFF1_ON0_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, rfnot)); + rfmenu->addItem( new CMenuOptionNumberChooser(LOCALE_RF_CHANNEL, &g_settings.rf_channel, true, 21, 69) ); + rfmenu->addItem( new CMenuOptionNumberChooser(LOCALE_RF_FINETUNE, &g_settings.rf_finetune, true, 0, 64) ); + + rfmenu->addItem( new CMenuOptionChooser(LOCALE_RF_STANDBY, &g_settings.rf_standby, OPTIONS_OFF1_ON0_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, rfnot)); + rf_dummy = 0; + rfmenu->addItem( new CMenuOptionChooser(LOCALE_RF_TEST, &rf_dummy, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, rfnot)); + mainSettings.addItem(new CMenuForwarder(LOCALE_RFMOD, true, NULL, rfmenu)); + } +#endif +} + +#define SCANTS_BOUQUET_OPTION_COUNT 3 +const CMenuOptionChooser::keyval SCANTS_BOUQUET_OPTIONS[SCANTS_BOUQUET_OPTION_COUNT] = +{ + { CZapitClient::BM_DELETEBOUQUETS , LOCALE_SCANTS_BOUQUET_ERASE }, + /*{ CZapitClient::BM_CREATEBOUQUETS , LOCALE_SCANTS_BOUQUET_CREATE },*/ + { CZapitClient::BM_DONTTOUCHBOUQUETS , LOCALE_SCANTS_BOUQUET_LEAVE }, + { CZapitClient::BM_UPDATEBOUQUETS , LOCALE_SCANTS_BOUQUET_UPDATE } + /*{ CZapitClient::BM_CREATESATELLITEBOUQUET, LOCALE_SCANTS_BOUQUET_SATELLITE }*/ +}; + +#define SCANTS_ZAPIT_SCANTYPE_COUNT 4 +const CMenuOptionChooser::keyval SCANTS_ZAPIT_SCANTYPE[SCANTS_ZAPIT_SCANTYPE_COUNT] = +{ + { CZapitClient::ST_TVRADIO , LOCALE_ZAPIT_SCANTYPE_TVRADIO }, + { CZapitClient::ST_TV , LOCALE_ZAPIT_SCANTYPE_TV }, + { CZapitClient::ST_RADIO , LOCALE_ZAPIT_SCANTYPE_RADIO }, + { CZapitClient::ST_ALL , LOCALE_ZAPIT_SCANTYPE_ALL } +}; + +#define SATSETUP_DISEQC_OPTION_COUNT 6 +const CMenuOptionChooser::keyval SATSETUP_DISEQC_OPTIONS[SATSETUP_DISEQC_OPTION_COUNT] = +{ + { NO_DISEQC , LOCALE_SATSETUP_NODISEQC, NULL }, + { MINI_DISEQC , LOCALE_SATSETUP_MINIDISEQC, NULL }, + { DISEQC_1_0 , LOCALE_SATSETUP_DISEQC10, NULL }, + { DISEQC_1_1 , LOCALE_SATSETUP_DISEQC11, NULL }, + /*{ DISEQC_1_2 , LOCALE_SATSETUP_DISEQC12, NULL },*/ + { DISEQC_ADVANCED , LOCALE_SATSETUP_DISEQ_ADVANCED, NULL }, + { SMATV_REMOTE_TUNING, LOCALE_SATSETUP_SMATVREMOTE, NULL } +}; + +#define SATSETUP_SCANTP_FEC_COUNT 23 +#define CABLESETUP_SCANTP_FEC_COUNT 5 +const CMenuOptionChooser::keyval SATSETUP_SCANTP_FEC[SATSETUP_SCANTP_FEC_COUNT] = +{ + { FEC_1_2, LOCALE_SCANTP_FEC_1_2 }, + { FEC_2_3, LOCALE_SCANTP_FEC_2_3 }, + { FEC_3_4, LOCALE_SCANTP_FEC_3_4 }, + { FEC_5_6, LOCALE_SCANTP_FEC_5_6 }, + { FEC_7_8, LOCALE_SCANTP_FEC_7_8 }, + { FEC_S2_QPSK_1_2, LOCALE_FEC_S2_QPSK_1_2 }, + { FEC_S2_QPSK_2_3, LOCALE_FEC_S2_QPSK_2_3 }, + { FEC_S2_QPSK_3_4, LOCALE_FEC_S2_QPSK_3_4 }, + { FEC_S2_QPSK_5_6, LOCALE_FEC_S2_QPSK_5_6 }, + { FEC_S2_QPSK_7_8, LOCALE_FEC_S2_QPSK_7_8 }, + { FEC_S2_QPSK_8_9, LOCALE_FEC_S2_QPSK_8_9 }, + { FEC_S2_QPSK_3_5, LOCALE_FEC_S2_QPSK_3_5 }, + { FEC_S2_QPSK_4_5, LOCALE_FEC_S2_QPSK_4_5 }, + { FEC_S2_QPSK_9_10, LOCALE_FEC_S2_QPSK_9_10 }, + { FEC_S2_8PSK_1_2, LOCALE_FEC_S2_8PSK_1_2 }, + { FEC_S2_8PSK_2_3, LOCALE_FEC_S2_8PSK_2_3 }, + { FEC_S2_8PSK_3_4, LOCALE_FEC_S2_8PSK_3_4 }, + { FEC_S2_8PSK_5_6, LOCALE_FEC_S2_8PSK_5_6 }, + { FEC_S2_8PSK_7_8, LOCALE_FEC_S2_8PSK_7_8 }, + { FEC_S2_8PSK_8_9, LOCALE_FEC_S2_8PSK_8_9 }, + { FEC_S2_8PSK_3_5, LOCALE_FEC_S2_8PSK_3_5 }, + { FEC_S2_8PSK_4_5, LOCALE_FEC_S2_8PSK_4_5 }, + { FEC_S2_8PSK_9_10, LOCALE_FEC_S2_8PSK_9_10 } +}; + +#define SATSETUP_SCANTP_MOD_COUNT 5 +const CMenuOptionChooser::keyval SATSETUP_SCANTP_MOD[SATSETUP_SCANTP_MOD_COUNT] = +{ + { 1, LOCALE_SCANTP_MOD_16 }, + { 2, LOCALE_SCANTP_MOD_32 }, + { 3, LOCALE_SCANTP_MOD_64 }, + { 4, LOCALE_SCANTP_MOD_128}, + { 5, LOCALE_SCANTP_MOD_256} +}; +#define SATSETUP_SCANTP_POL_COUNT 2 +const CMenuOptionChooser::keyval SATSETUP_SCANTP_POL[SATSETUP_SCANTP_POL_COUNT] = +{ + { 0, LOCALE_EXTRA_POL_H }, + { 1, LOCALE_EXTRA_POL_V } +}; + +#if 0 +/*Cable*/ +#define CABLESETUP_SCANTP_MOD_COUNT 7 +const CMenuOptionChooser::keyval CABLESETUP_SCANTP_MOD[CABLESETUP_SCANTP_MOD_COUNT] = +{ + {0, LOCALE_SCANTP_MOD_QPSK } , + {1, LOCALE_SCANTP_MOD_QAM_16 } , + {2, LOCALE_SCANTP_MOD_QAM_32 } , + {3, LOCALE_SCANTP_MOD_QAM_64 } , + {4, LOCALE_SCANTP_MOD_QAM_128 } , + {5, LOCALE_SCANTP_MOD_QAM_256 } , + {6, LOCALE_SCANTP_MOD_QAM_AUTO } +}; +#endif + +#define SECTIONSD_SCAN_OPTIONS_COUNT 3 +const CMenuOptionChooser::keyval SECTIONSD_SCAN_OPTIONS[SECTIONSD_SCAN_OPTIONS_COUNT] = +{ + { 0, LOCALE_OPTIONS_OFF }, + { 1, LOCALE_OPTIONS_ON }, + { 2, LOCALE_OPTIONS_ON_WITHOUT_MESSAGES } +}; +#define DISEQC_ORDER_OPTION_COUNT 2 +const CMenuOptionChooser::keyval DISEQC_ORDER_OPTIONS[DISEQC_ORDER_OPTION_COUNT] = +{ + { COMMITED_FIRST, LOCALE_SATSETUP_DISEQC_COM_UNCOM }, + { UNCOMMITED_FIRST, LOCALE_SATSETUP_DISEQC_UNCOM_COM } +}; + +class CTPSelectHandler : public CMenuTarget +{ + public: + int exec(CMenuTarget* parent, const std::string &actionkey); +}; + +extern std::map select_transponders; +int CTPSelectHandler::exec(CMenuTarget* parent, const std::string &actionkey) +{ + transponder_list_t::iterator tI; + sat_iterator_t sit; + t_satellite_position position = 0; + std::map tmplist; + std::map::iterator tmpI; + int i; + char cnt[5]; + int select = -1; + static int old_selected = 0; + static t_satellite_position old_position = 0; + + if (parent) + parent->hide(); + + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + if(!strcmp(sit->second.name.c_str(), CNeutrinoApp::getInstance()->getScanSettings().satNameNoDiseqc)) { + position = sit->first; + break; + } + } + if(old_position != position) { + old_selected = 0; + old_position = position; + } + + CMenuWidget* menu = new CMenuWidget(LOCALE_SCANTS_SELECT_TP, NEUTRINO_ICON_SETTINGS); + CMenuSelectorTarget * selector = new CMenuSelectorTarget(&select); + i = 0; + for(tI = select_transponders.begin(); tI != select_transponders.end(); tI++) { + t_satellite_position satpos = GET_SATELLITEPOSITION_FROM_TRANSPONDER_ID(tI->first) & 0xFFF; + if(GET_SATELLITEPOSITION_FROM_TRANSPONDER_ID(tI->first) & 0xF000) + satpos = -satpos; + if(satpos != position) + continue; + + char buf[128]; + sprintf(cnt, "%d", i); + char * f, *s, *m; + switch(frontend->getInfo()->type) { + case FE_QPSK: + frontend->getDelSys(tI->second.feparams.u.qpsk.fec_inner, dvbs_get_modulation(tI->second.feparams.u.qpsk.fec_inner), f, s, m); + snprintf(buf, sizeof(buf), "%d %c %d %s %s %s ", tI->second.feparams.frequency/1000, tI->second.polarization ? 'V' : 'H', tI->second.feparams.u.qpsk.symbol_rate/1000, f, s, m); + break; + case FE_QAM: + frontend->getDelSys(tI->second.feparams.u.qam.fec_inner, tI->second.feparams.u.qam.modulation, f, s, m); + snprintf(buf, sizeof(buf), "%d %d %s %s %s ", tI->second.feparams.frequency/1000, tI->second.feparams.u.qam.symbol_rate/1000, f, s, m); + break; + case FE_OFDM: + case FE_ATSC: + break; + } + menu->addItem(new CMenuForwarderNonLocalized(buf, true, NULL, selector, cnt), old_selected == i); + tmplist.insert(std::pair (i, tI->second)); + i++; + } + int retval = menu->exec(NULL, ""); + delete menu; + delete selector; + if(select >= 0) { + old_selected = select; + + tmpI = tmplist.find(select); + printf("CTPSelectHandler::exec: selected TP: freq %d pol %d SR %d\n", tmpI->second.feparams.frequency, + tmpI->second.polarization, tmpI->second.feparams.u.qpsk.symbol_rate); + sprintf(get_set.TP_freq, "%d", tmpI->second.feparams.frequency); + switch(frontend->getInfo()->type) { + case FE_QPSK: + sprintf(get_set.TP_rate, "%d", tmpI->second.feparams.u.qpsk.symbol_rate); + get_set.TP_fec = tmpI->second.feparams.u.qpsk.fec_inner; + get_set.TP_pol = tmpI->second.polarization; + break; + case FE_QAM: + sprintf(get_set.TP_rate, "%d", tmpI->second.feparams.u.qam.symbol_rate); + get_set.TP_fec = tmpI->second.feparams.u.qam.fec_inner; + get_set.TP_mod = tmpI->second.feparams.u.qam.modulation; + break; + case FE_OFDM: + case FE_ATSC: + break; + } + + } + if(retval == menu_return::RETURN_EXIT_ALL) + return menu_return::RETURN_EXIT_ALL; + + return menu_return::RETURN_REPAINT; +} + +extern int scan_pids; +void CNeutrinoApp::InitScanSettings(CMenuWidget &settings) +{ + dprintf(DEBUG_DEBUG, "init scansettings\n"); + int sfound = 0; + int dmode = scanSettings.diseqcMode; + int shortcut = 1; + CTPSelectHandler * tpSelect = new CTPSelectHandler(); + + CSatelliteSetupNotifier * satNotify = new CSatelliteSetupNotifier(); + + CMenuOptionChooser* ojScantype = new CMenuOptionChooser(LOCALE_ZAPIT_SCANTYPE, (int *)&scanSettings.scanType, SCANTS_ZAPIT_SCANTYPE, SCANTS_ZAPIT_SCANTYPE_COUNT, true, NULL, CRCInput::convertDigitToKey(shortcut++), "", true); + CMenuOptionChooser* ojBouquets = new CMenuOptionChooser(LOCALE_SCANTS_BOUQUET, (int *)&scanSettings.bouquetMode, SCANTS_BOUQUET_OPTIONS, SCANTS_BOUQUET_OPTION_COUNT, true, NULL, CRCInput::convertDigitToKey(shortcut++), "", true); + + CMenuOptionChooser* useNit = new CMenuOptionChooser(LOCALE_SATSETUP_USE_NIT, (int *)&scanSettings.scan_mode, OPTIONS_OFF1_ON0_OPTIONS, OPTIONS_OFF1_ON0_OPTION_COUNT, true, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN); + CMenuOptionChooser* scanPids = new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_SCANPIDS, &scan_pids, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW); + + CMenuWidget* satSetup = new CMenuWidget(LOCALE_SATSETUP_SAT_SETUP, NEUTRINO_ICON_SETTINGS); + satSetup->addItem(GenericMenuSeparator); + satSetup->addItem(GenericMenuBack); + + satSetup->addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_SAVESETTINGSNOW, true, NULL, this, "savescansettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + satSetup->addItem(GenericMenuSeparatorLine); + + CMenuWidget* satfindMenu = new CMenuWidget(LOCALE_MOTORCONTROL_HEAD, NEUTRINO_ICON_SETTINGS); + + satfindMenu->addItem(GenericMenuSeparator); + satfindMenu->addItem(GenericMenuBack); + satfindMenu->addItem(GenericMenuSeparatorLine); + CMenuOptionStringChooser *satSelect = NULL; + CMenuWidget* satOnOff = NULL; + sat_iterator_t sit; + + if(g_info.delivery_system == DVB_S) { + satSelect = new CMenuOptionStringChooser(LOCALE_SATSETUP_SATELLITE, scanSettings.satNameNoDiseqc, true, NULL, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED, true); + satOnOff = new CMenuWidget(LOCALE_SATSETUP_SATELLITE, NEUTRINO_ICON_SETTINGS); + satOnOff->addItem(GenericMenuSeparator); + satOnOff->addItem(GenericMenuBack); + satOnOff->addItem(GenericMenuSeparatorLine); + + t_satellite_position currentSatellitePosition = frontend->getCurrentSatellitePosition(); + + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + printf("Adding sat menu for %s position %d\n", sit->second.name.c_str(), sit->first); + + satSelect->addOption(sit->second.name.c_str()); + if(currentSatellitePosition == sit->first) { + strcpy(scanSettings.satNameNoDiseqc, sit->second.name.c_str()); + sfound = 1; + } + + CMenuWidget* tempsat = new CMenuWidget(sit->second.name.c_str(), NEUTRINO_ICON_SETTINGS); + tempsat->addItem(GenericMenuSeparator); + tempsat->addItem(GenericMenuBack); + tempsat->addItem(GenericMenuSeparatorLine); + + CMenuOptionChooser * inuse = new CMenuOptionChooser(sit->second.name.c_str(), &sit->second.use_in_scan, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + CMenuOptionNumberChooser * diseqc = new CMenuOptionNumberChooser(LOCALE_SATSETUP_DISEQC_INPUT, &sit->second.diseqc, ((dmode != NO_DISEQC) && (dmode != DISEQC_ADVANCED)), -1, 15, NULL, 1, -1, LOCALE_OPTIONS_OFF); + CMenuOptionNumberChooser * comm = new CMenuOptionNumberChooser(LOCALE_SATSETUP_COMM_INPUT, &sit->second.commited, dmode == DISEQC_ADVANCED, -1, 15, NULL, 1, -1, LOCALE_OPTIONS_OFF); + CMenuOptionNumberChooser * uncomm = new CMenuOptionNumberChooser(LOCALE_SATSETUP_UNCOMM_INPUT, &sit->second.uncommited, dmode == DISEQC_ADVANCED, -1, 15, NULL, 1, -1, LOCALE_OPTIONS_OFF); + //CMenuOptionNumberChooser * motor = new CMenuOptionNumberChooser(LOCALE_SATSETUP_MOTOR_POS, &sit->second.motor_position, dmode == DISEQC_ADVANCED, 0, 64, NULL, 0, 0, LOCALE_OPTIONS_OFF); + CMenuOptionNumberChooser * motor = new CMenuOptionNumberChooser(LOCALE_SATSETUP_MOTOR_POS, &sit->second.motor_position, true, 0, 64, NULL, 0, 0, LOCALE_OPTIONS_OFF); + //CMenuOptionChooser * usals = new CMenuOptionChooser(LOCALE_EXTRA_USE_GOTOXX, &sit->second.use_usals, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, dmode == DISEQC_ADVANCED); + CMenuOptionChooser * usals = new CMenuOptionChooser(LOCALE_EXTRA_USE_GOTOXX, &sit->second.use_usals, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + satNotify->addItem(1, diseqc); + satNotify->addItem(0, comm); + satNotify->addItem(0, uncomm); + //satNotify->addItem(0, motor); //FIXME testing motor with not DISEQC_ADVANCED + //satNotify->addItem(0, usals); + + CIntInput* lofL = new CIntInput(LOCALE_SATSETUP_LOFL, (int&) sit->second.lnbOffsetLow, 5, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + CIntInput* lofH = new CIntInput(LOCALE_SATSETUP_LOFH, (int&) sit->second.lnbOffsetHigh, 5, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + CIntInput* lofS = new CIntInput(LOCALE_SATSETUP_LOFS, (int&) sit->second.lnbSwitch, 5, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE); + + satOnOff->addItem(inuse); + tempsat->addItem(diseqc); + tempsat->addItem(comm); + tempsat->addItem(uncomm); + tempsat->addItem(motor); + tempsat->addItem(usals); + tempsat->addItem(new CMenuForwarder(LOCALE_SATSETUP_LOFL, true, lofL->getValue(), lofL)); + tempsat->addItem(new CMenuForwarder(LOCALE_SATSETUP_LOFH, true, lofH->getValue(), lofH)); + tempsat->addItem(new CMenuForwarder(LOCALE_SATSETUP_LOFS, true, lofS->getValue(), lofS)); + satSetup->addItem(new CMenuForwarderNonLocalized(sit->second.name.c_str(), true, NULL, tempsat)); + } + } else if (g_info.delivery_system == DVB_C) { + satSelect = new CMenuOptionStringChooser(LOCALE_CABLESETUP_PROVIDER, (char*)scanSettings.satNameNoDiseqc, true); + for(sit = satellitePositions.begin(); sit != satellitePositions.end(); sit++) { + satSelect->addOption(sit->second.name.c_str()); +printf("Adding cable menu for %s position %d\n", sit->second.name.c_str(), sit->first); + dprintf(DEBUG_DEBUG, "got scanprovider (cable): %s\n", sit->second.name.c_str()); + } + } + satfindMenu->addItem(satSelect); + + int freq_length = (g_info.delivery_system == DVB_S) ? 8 : 6; + + CStringInput* freq = new CStringInput(LOCALE_EXTRA_FREQ, (char *) scanSettings.TP_freq, freq_length, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789"); + CStringInput* rate = new CStringInput(LOCALE_EXTRA_RATE, (char *) scanSettings.TP_rate, 8, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789"); + CMenuForwarder * Freq = new CMenuForwarder(LOCALE_EXTRA_FREQ, true, scanSettings.TP_freq, freq, "", CRCInput::convertDigitToKey(1)); + CMenuForwarder * Rate = new CMenuForwarder(LOCALE_EXTRA_RATE, true, scanSettings.TP_rate, rate, "", CRCInput::convertDigitToKey(2)); + + int fec_count = (g_info.delivery_system == DVB_S) ? SATSETUP_SCANTP_FEC_COUNT : CABLESETUP_SCANTP_FEC_COUNT; + + CMenuOptionChooser* fec = new CMenuOptionChooser(LOCALE_EXTRA_FEC, (int *)&scanSettings.TP_fec, SATSETUP_SCANTP_FEC, fec_count, true, NULL, CRCInput::convertDigitToKey(3), "", true); + CMenuOptionChooser* mod_pol = NULL; + + if (g_info.delivery_system == DVB_S) + mod_pol = new CMenuOptionChooser(LOCALE_EXTRA_POL, (int *)&scanSettings.TP_pol, SATSETUP_SCANTP_POL, SATSETUP_SCANTP_POL_COUNT, true, NULL, CRCInput::convertDigitToKey(4)); + else if(g_info.delivery_system == DVB_C) + mod_pol = new CMenuOptionChooser(LOCALE_EXTRA_MOD, (int *)&scanSettings.TP_mod, SATSETUP_SCANTP_MOD, SATSETUP_SCANTP_MOD_COUNT, true, NULL, CRCInput::convertDigitToKey(4)); + + satfindMenu->addItem(Freq); + satfindMenu->addItem(Rate); + satfindMenu->addItem(fec); + satfindMenu->addItem(mod_pol); + + CMenuWidget* motorMenu = NULL; + if (g_info.delivery_system == DVB_S) { + satfindMenu->addItem(new CMenuForwarder(LOCALE_MOTORCONTROL_HEAD, true, NULL, new CMotorControl(), "", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + + motorMenu = new CMenuWidget(LOCALE_SATSETUP_EXTENDED_MOTOR, NEUTRINO_ICON_SETTINGS); + motorMenu->addItem(GenericMenuSeparator); + motorMenu->addItem(GenericMenuBack); + motorMenu->addItem(new CMenuForwarder(LOCALE_SATSETUP_SAVESETTINGSNOW, true, NULL, this, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + //motorMenu->addItem(new CMenuForwarder(LOCALE_SATSETUP_MOTORCONTROL , true, NULL, new CMotorControl())); + motorMenu->addItem(new CMenuForwarder(LOCALE_MOTORCONTROL_HEAD, true, NULL, satfindMenu, "", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + motorMenu->addItem(GenericMenuSeparatorLine); + + motorMenu->addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_ZAPIT_ROTATION_SPEED, (int *)&zapitCfg.motorRotationSpeed, true, 0, 64, NULL) ); + //motorMenu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_USE_GOTOXX, (int *)&zapitCfg.useGotoXX, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + CStringInput * toff; + sprintf(zapit_lat, "%3.6f", zapitCfg.gotoXXLatitude); + sprintf(zapit_long, "%3.6f", zapitCfg.gotoXXLongitude); + + motorMenu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_LADIR, (int *)&zapitCfg.gotoXXLaDirection, OPTIONS_SOUTH0_NORTH1_OPTIONS, OPTIONS_SOUTH0_NORTH1_OPTION_COUNT, true)); + toff = new CStringInput(LOCALE_EXTRA_LAT, (char *) zapit_lat, 10, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789."); + motorMenu->addItem(new CMenuForwarder(LOCALE_EXTRA_LAT, true, zapit_lat, toff)); + + motorMenu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_LODIR, (int *)&zapitCfg.gotoXXLoDirection, OPTIONS_EAST0_WEST1_OPTIONS, OPTIONS_EAST0_WEST1_OPTION_COUNT, true)); + toff = new CStringInput(LOCALE_EXTRA_LONG, (char *) zapit_long, 10, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789."); + motorMenu->addItem(new CMenuForwarder(LOCALE_EXTRA_LONG, true, zapit_long, toff)); + motorMenu->addItem(new CMenuOptionNumberChooser(LOCALE_SATSETUP_USALS_REPEAT, (int *)&zapitCfg.repeatUsals, true, 0, 10, NULL, 0, 0, LOCALE_OPTIONS_OFF) ); + } + + if(!sfound && satellitePositions.size()) { + sit = satellitePositions.begin(); + strcpy(scanSettings.satNameNoDiseqc, sit->second.name.c_str()); + } + + CMenuWidget* manualScan = new CMenuWidget(LOCALE_SATSETUP_MANUAL_SCAN, NEUTRINO_ICON_SETTINGS); + + CScanTs * scanTs = new CScanTs(); + + manualScan->addItem(GenericMenuSeparator); + manualScan->addItem(GenericMenuBack); + manualScan->addItem(GenericMenuSeparatorLine); + + manualScan->addItem(satSelect); + manualScan->addItem(new CMenuForwarder(LOCALE_SCANTS_SELECT_TP, true, NULL, tpSelect, "test")); + manualScan->addItem(Freq); + manualScan->addItem(Rate); + manualScan->addItem(fec); + manualScan->addItem(mod_pol); + manualScan->addItem(useNit); + manualScan->addItem(GenericMenuSeparatorLine); + manualScan->addItem(new CMenuForwarder(LOCALE_SCANTS_TEST, true, NULL, scanTs, "test", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + manualScan->addItem(new CMenuForwarder(LOCALE_SCANTS_STARTNOW, true, NULL, scanTs, "manual", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + + CMenuWidget* autoScan = new CMenuWidget(LOCALE_SATSETUP_AUTO_SCAN, NEUTRINO_ICON_SETTINGS); + autoScan->addItem(GenericMenuSeparator); + autoScan->addItem(GenericMenuBack); + autoScan->addItem(GenericMenuSeparatorLine); + autoScan->addItem(satSelect); + autoScan->addItem(useNit); + autoScan->addItem(scanPids); + autoScan->addItem(new CMenuForwarder(LOCALE_SCANTS_STARTNOW, true, NULL, scanTs, "auto", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + + CMenuOptionChooser* ojDiseqc = NULL; + CMenuOptionNumberChooser * ojDiseqcRepeats = NULL; + CMenuForwarder *fsatSetup = NULL; + CMenuForwarder *fmotorMenu = NULL; + CMenuForwarder *fautoScanAll = NULL; + + if (g_info.delivery_system == DVB_S) { + ojDiseqc = new CMenuOptionChooser(LOCALE_SATSETUP_DISEQC, (int *)&scanSettings.diseqcMode, SATSETUP_DISEQC_OPTIONS, SATSETUP_DISEQC_OPTION_COUNT, true, satNotify, CRCInput::convertDigitToKey(shortcut++), "", true); + ojDiseqcRepeats = new CMenuOptionNumberChooser(LOCALE_SATSETUP_DISEQCREPEAT, (int *)&scanSettings.diseqcRepeat, (dmode != NO_DISEQC) && (dmode != DISEQC_ADVANCED), 0, 2, NULL); + + satNotify->addItem(1, ojDiseqcRepeats); + + fsatSetup = new CMenuForwarder(LOCALE_SATSETUP_SAT_SETUP, true, NULL, satSetup, "", CRCInput::convertDigitToKey(shortcut++)); + //fmotorMenu = new CMenuForwarder(LOCALE_SATSETUP_EXTENDED_MOTOR, (dmode == DISEQC_ADVANCED), NULL, motorMenu, "", CRCInput::convertDigitToKey(shortcut++)); + //satNotify->addItem(0, fmotorMenu); //FIXME testing motor with not DISEQC_ADVANCED + fmotorMenu = new CMenuForwarder(LOCALE_SATSETUP_EXTENDED_MOTOR, true, NULL, motorMenu, "", CRCInput::convertDigitToKey(shortcut++)); + + CMenuWidget* autoScanAll = new CMenuWidget(LOCALE_SATSETUP_AUTO_SCAN_ALL, NEUTRINO_ICON_SETTINGS); + fautoScanAll = new CMenuForwarder(LOCALE_SATSETUP_AUTO_SCAN_ALL, (dmode != NO_DISEQC), NULL, autoScanAll, "", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE); + satNotify->addItem(2, fautoScanAll); + + + autoScanAll->addItem(GenericMenuSeparator); + autoScanAll->addItem(GenericMenuBack); + autoScanAll->addItem(GenericMenuSeparatorLine); + autoScanAll->addItem(new CMenuForwarder(LOCALE_SATSETUP_SATELLITE, true, NULL, satOnOff, "", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + autoScanAll->addItem(useNit); + autoScanAll->addItem(scanPids); + autoScanAll->addItem(new CMenuForwarder(LOCALE_SCANTS_STARTNOW, true, NULL, scanTs, "all", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + } + + settings.addItem(GenericMenuSeparator); + settings.addItem(GenericMenuBack); + settings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_SAVESETTINGSNOW, true, NULL, this, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + settings.addItem(GenericMenuSeparatorLine); + + settings.addItem(ojScantype); + settings.addItem(ojBouquets); + + if(g_info.delivery_system == DVB_S) { + settings.addItem(ojDiseqc); + settings.addItem(ojDiseqcRepeats); + settings.addItem(fsatSetup); + settings.addItem(fmotorMenu); + } + + settings.addItem(new CMenuForwarder(LOCALE_SATSETUP_MANUAL_SCAN, true, NULL, manualScan, "", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + settings.addItem(new CMenuForwarder(LOCALE_SATSETUP_AUTO_SCAN, true, NULL, autoScan, "", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + + if(g_info.delivery_system == DVB_S) { + settings.addItem(fautoScanAll); + } +} + +#define FLASHUPDATE_UPDATEMODE_OPTION_COUNT 2 +const CMenuOptionChooser::keyval FLASHUPDATE_UPDATEMODE_OPTIONS[FLASHUPDATE_UPDATEMODE_OPTION_COUNT] = +{ + { 0, LOCALE_FLASHUPDATE_UPDATEMODE_MANUAL }, + { 1, LOCALE_FLASHUPDATE_UPDATEMODE_INTERNET } +}; + +void getZapitConfig(Zapit_config *Cfg); +void CNeutrinoApp::InitServiceSettings(CMenuWidget &service, CMenuWidget &scanSettings) +{ + dprintf(DEBUG_DEBUG, "init serviceSettings\n"); + getZapitConfig(&zapitCfg); + +#if 0 + CZapitDestExec * zexec = new CZapitDestExec(); + + CMenuWidget* zapit_menu = new CMenuWidget(LOCALE_EXTRA_ZAPIT_MENU, "settings.raw"); + zapit_menu->addItem( GenericMenuBack ); + //zapit_menu->addItem(new CMenuForwarder(LOCALE_EXTRA_SAVESETTINGS, true, "", new CZapitDestExec, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + zapit_menu->addItem(new CMenuForwarder(LOCALE_EXTRA_SAVESETTINGS, true, "", this, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + zapit_menu->addItem( GenericMenuSeparatorLine ); + + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_MAKE_BOUQUET, (int *)&zapitCfg.makeRemainingChannelsBouquet, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_SAVE_LAST, (int *)&zapitCfg.saveLastChannel, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_WRITE_NAMES, (int *)&zapitCfg.writeChannelsNames, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_SORTNAMES, (int *)&zapitCfg.sortNames, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + +#if 0 + zapit_menu->addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_ZAPIT_TIMEOUT, (int *)&zapitCfg.feTimeout, true, 0, 100) ); +#else + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_FASTZAP, (int *)&zapitCfg.fastZap, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); +#endif + zapit_menu->addItem( new CMenuOptionChooser(LOCALE_ZAPIT_SCANSDT, (int *)&zapitCfg.scanSDT, SECTIONSD_SCAN_OPTIONS, SECTIONSD_SCAN_OPTIONS_COUNT, true)); + + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_SCANPIDS, (int *)&zapitCfg.scanPids, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + zapit_menu->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAPIT_HVOLTAGE, (int *)&zapitCfg.highVoltage, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + zapit_menu->addItem( GenericMenuSeparatorLine ); + zapit_menu->addItem(new CMenuForwarder(LOCALE_EXTRA_ZAPIT_DELETE, true, "", zexec /*new CZapitDestExec*/, "delete")); + zapit_menu->addItem(new CMenuForwarder(LOCALE_EXTRA_ZAPIT_BACKUP, true, "", zexec /*new CZapitDestExec*/, "backup")); + zapit_menu->addItem(new CMenuForwarder(LOCALE_EXTRA_ZAPIT_RESTORE, true, "", zexec /*new CZapitDestExec*/, "restore")); +#endif + + service.addItem(GenericMenuSeparator); + service.addItem(GenericMenuBack); + service.addItem(GenericMenuSeparatorLine); + service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_SCANTS , true, NULL, &scanSettings, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED) ); + // service.addItem(new CMenuForwarder(LOCALE_EXTRA_ZAPIT_MENU , true, NULL, zapit_menu, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_RELOAD , true, NULL, this, "reloadchannels", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); + service.addItem(new CMenuForwarder(LOCALE_BOUQUETEDITOR_NAME , true, NULL, new CBEBouquetWidget(), NULL, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE )); + + CDataResetNotifier * resetNotifier = new CDataResetNotifier(); + service.addItem(new CMenuForwarder(LOCALE_RESET_CHANNELS , true, NULL, resetNotifier, "channels", CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN )); + //service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_GETPLUGINS, true, NULL, this, "reloadplugins")); + + service.addItem(GenericMenuSeparatorLine); + service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_IMAGEINFO, true, NULL, new CImageInfo(), NULL, CRCInput::RC_help, NEUTRINO_ICON_BUTTON_HELP_SMALL ), false); + service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_RESTART , true, NULL, this, "restart", CRCInput::RC_standby, "power.raw")); + //softupdate + //if(softupdate) + { + dprintf(DEBUG_DEBUG, "init soft-update-stuff\n"); + CMenuWidget* updateSettings = new CMenuWidget(LOCALE_SERVICEMENU_UPDATE, "softupdate.raw", 550); + updateSettings->addItem(GenericMenuSeparator); + updateSettings->addItem(GenericMenuBack); + updateSettings->addItem(GenericMenuSeparatorLine); + + //experten-funktionen für mtd lesen/schreiben + CMenuWidget* mtdexpert = new CMenuWidget(LOCALE_FLASHUPDATE_EXPERTFUNCTIONS, "softupdate.raw"); + mtdexpert->addItem(GenericMenuSeparator); + mtdexpert->addItem(GenericMenuBack); + mtdexpert->addItem(GenericMenuSeparatorLine); + CFlashExpert* fe = new CFlashExpert(); + + //mtdexpert->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_READFLASH , true, NULL, fe, "readflash" )); + //if(softupdate) + // mtdexpert->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_WRITEFLASH , true, NULL, fe, "writeflash" )); + mtdexpert->addItem(GenericMenuSeparatorLine); + mtdexpert->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_READFLASHMTD , true, NULL, fe, "readflashmtd" )); + if(softupdate) + mtdexpert->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_WRITEFLASHMTD, true, NULL, fe, "writeflashmtd")); + mtdexpert->addItem(GenericMenuSeparatorLine); + + CStringInputSMS * updateSettings_url_file = new CStringInputSMS(LOCALE_FLASHUPDATE_URL_FILE, g_settings.softupdate_url_file, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789!""§$%&/()=?-. "); + mtdexpert->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_URL_FILE, true, g_settings.softupdate_url_file, updateSettings_url_file)); + + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_EXPERTFUNCTIONS, true, NULL, mtdexpert, "", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + + updateSettings->addItem(GenericMenuSeparatorLine); + CMenuOptionChooser *oj = new CMenuOptionChooser(LOCALE_FLASHUPDATE_UPDATEMODE, &g_settings.softupdate_mode, FLASHUPDATE_UPDATEMODE_OPTIONS, FLASHUPDATE_UPDATEMODE_OPTION_COUNT, true, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN); + updateSettings->addItem( oj ); + updateSettings->addItem( new CMenuForwarder(LOCALE_EXTRA_UPDATE_DIR, true, g_settings.update_dir , this, "update_dir", CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW) ); + + /* show current version */ + updateSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_FLASHUPDATE_CURRENTVERSION_SEP)); + + /* get current version SBBBYYYYMMTTHHMM -- formatsting */ + CConfigFile configfile('\t'); + + const char * versionString = (configfile.loadConfig("/.version")) ? (configfile.getString( "version", "????????????????").c_str()) : "????????????????"; + + dprintf(DEBUG_INFO, "current flash-version: %s\n", versionString); + + static CFlashVersionInfo versionInfo(versionString); + + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_CURRENTVERSIONDATE , false, versionInfo.getDate())); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_CURRENTVERSIONTIME , false, versionInfo.getTime())); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_CURRENTRELEASECYCLE , false, versionInfo.getReleaseCycle())); + /* versionInfo.getType() returns const char * which is never deallocated */ + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_CURRENTVERSIONSNAPSHOT, false, versionInfo.getType())); + + updateSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_FLASHUPDATE_PROXYSERVER_SEP)); + + CStringInputSMS * updateSettings_proxy = new CStringInputSMS(LOCALE_FLASHUPDATE_PROXYSERVER, g_settings.softupdate_proxyserver, 23, LOCALE_FLASHUPDATE_PROXYSERVER_HINT1, LOCALE_FLASHUPDATE_PROXYSERVER_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789-.: "); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_PROXYSERVER, true, g_settings.softupdate_proxyserver, updateSettings_proxy)); + + CStringInputSMS * updateSettings_proxyuser = new CStringInputSMS(LOCALE_FLASHUPDATE_PROXYUSERNAME, g_settings.softupdate_proxyusername, 23, LOCALE_FLASHUPDATE_PROXYUSERNAME_HINT1, LOCALE_FLASHUPDATE_PROXYUSERNAME_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789!""§$%&/()=?-. "); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_PROXYUSERNAME, true, g_settings.softupdate_proxyusername, updateSettings_proxyuser)); + + CStringInputSMS * updateSettings_proxypass = new CStringInputSMS(LOCALE_FLASHUPDATE_PROXYPASSWORD, g_settings.softupdate_proxypassword, 20, LOCALE_FLASHUPDATE_PROXYPASSWORD_HINT1, LOCALE_FLASHUPDATE_PROXYPASSWORD_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789!""§$%&/()=?-. "); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_PROXYPASSWORD, true, g_settings.softupdate_proxypassword, updateSettings_proxypass)); + + updateSettings->addItem(GenericMenuSeparatorLine); + updateSettings->addItem(new CMenuForwarder(LOCALE_FLASHUPDATE_CHECKUPDATE, true, NULL, new CFlashUpdate(), "", CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE)); + + service.addItem(new CMenuForwarder(LOCALE_SERVICEMENU_UPDATE, true, NULL, updateSettings)); + } +} + +#define MESSAGEBOX_NO_YES_OPTION_COUNT 2 +const CMenuOptionChooser::keyval MESSAGEBOX_NO_YES_OPTIONS[MESSAGEBOX_NO_YES_OPTION_COUNT] = +{ + { 0, LOCALE_MESSAGEBOX_NO }, + { 1, LOCALE_MESSAGEBOX_YES } +}; + +#define PICTUREVIEWER_SCALING_OPTION_COUNT 3 +const CMenuOptionChooser::keyval PICTUREVIEWER_SCALING_OPTIONS[PICTUREVIEWER_SCALING_OPTION_COUNT] = +{ + { CPictureViewer::SIMPLE, LOCALE_PICTUREVIEWER_RESIZE_SIMPLE }, + { CPictureViewer::COLOR , LOCALE_PICTUREVIEWER_RESIZE_COLOR_AVERAGE }, + { CPictureViewer::NONE , LOCALE_PICTUREVIEWER_RESIZE_NONE } +}; + +#define AUDIOPLAYER_DISPLAY_ORDER_OPTION_COUNT 2 +const CMenuOptionChooser::keyval AUDIOPLAYER_DISPLAY_ORDER_OPTIONS[AUDIOPLAYER_DISPLAY_ORDER_OPTION_COUNT] = +{ + { CAudioPlayerGui::ARTIST_TITLE, LOCALE_AUDIOPLAYER_ARTIST_TITLE }, + { CAudioPlayerGui::TITLE_ARTIST, LOCALE_AUDIOPLAYER_TITLE_ARTIST } +}; + +void CNeutrinoApp::InitAudioplPicSettings(CMenuWidget &audioplPicSettings) +{ + audioplPicSettings.addItem(GenericMenuSeparator); + audioplPicSettings.addItem(GenericMenuBack); + + audioplPicSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_PICTUREVIEWER_HEAD)); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_PICTUREVIEWER_SCALING , &g_settings.picviewer_scaling , PICTUREVIEWER_SCALING_OPTIONS , PICTUREVIEWER_SCALING_OPTION_COUNT , true )); + CStringInput * pic_timeout= new CStringInput(LOCALE_PICTUREVIEWER_SLIDE_TIME, g_settings.picviewer_slide_time, 2, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789 "); + audioplPicSettings.addItem(new CMenuForwarder(LOCALE_PICTUREVIEWER_SLIDE_TIME, true, g_settings.picviewer_slide_time, pic_timeout)); + audioplPicSettings.addItem(new CMenuForwarder(LOCALE_PICTUREVIEWER_DEFDIR, true, g_settings.network_nfs_picturedir, this, "picturedir")); + + audioplPicSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_AUDIOPLAYER_NAME)); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_DISPLAY_ORDER, &g_settings.audioplayer_display , AUDIOPLAYER_DISPLAY_ORDER_OPTIONS, AUDIOPLAYER_DISPLAY_ORDER_OPTION_COUNT, true )); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_FOLLOW , &g_settings.audioplayer_follow , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_SELECT_TITLE_BY_NAME , &g_settings.audioplayer_select_title_by_name , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_REPEAT_ON , &g_settings.audioplayer_repeat_on , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_SHOW_PLAYLIST, &g_settings.audioplayer_show_playlist, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true )); + + CStringInput * audio_screensaver= new CStringInput(LOCALE_AUDIOPLAYER_SCREENSAVER_TIMEOUT, g_settings.audioplayer_screensaver, 2, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789 "); + audioplPicSettings.addItem(new CMenuForwarder(LOCALE_AUDIOPLAYER_SCREENSAVER_TIMEOUT, true, g_settings.audioplayer_screensaver, audio_screensaver)); + + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_HIGHPRIO , &g_settings.audioplayer_highprio , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + + if(CVFD::getInstance()->has_lcd) //FIXME + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_SPECTRUM , &g_settings.spectrum , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + audioplPicSettings.addItem(new CMenuForwarder(LOCALE_AUDIOPLAYER_DEFDIR, true, g_settings.network_nfs_audioplayerdir, this, "audioplayerdir")); + audioplPicSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOPLAYER_ENABLE_SC_METADATA, &g_settings.audioplayer_enable_sc_metadata, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true )); + +} + +#define MISCSETTINGS_FB_DESTINATION_OPTION_COUNT 3 +const CMenuOptionChooser::keyval MISCSETTINGS_FB_DESTINATION_OPTIONS[MISCSETTINGS_FB_DESTINATION_OPTION_COUNT] = +{ + { 0, LOCALE_OPTIONS_NULL }, + { 1, LOCALE_OPTIONS_SERIAL }, + { 2, LOCALE_OPTIONS_FB } +}; + +#define MISCSETTINGS_FILESYSTEM_IS_UTF8_OPTION_COUNT 2 +const CMenuOptionChooser::keyval MISCSETTINGS_FILESYSTEM_IS_UTF8_OPTIONS[MISCSETTINGS_FILESYSTEM_IS_UTF8_OPTION_COUNT] = +{ + { 0, LOCALE_FILESYSTEM_IS_UTF8_OPTION_ISO8859_1 }, + { 1, LOCALE_FILESYSTEM_IS_UTF8_OPTION_UTF8 } +}; + +#define INFOBAR_SUBCHAN_DISP_POS_OPTIONS_COUNT 4 +const CMenuOptionChooser::keyval INFOBAR_SUBCHAN_DISP_POS_OPTIONS[INFOBAR_SUBCHAN_DISP_POS_OPTIONS_COUNT]= +{ + { 0 , LOCALE_SETTINGS_POS_TOP_RIGHT }, + { 1 , LOCALE_SETTINGS_POS_TOP_LEFT }, + { 2 , LOCALE_SETTINGS_POS_BOTTOM_LEFT }, + { 3 , LOCALE_SETTINGS_POS_BOTTOM_RIGHT } +}; + +#define CHANNELLIST_EPGTEXT_ALIGN_RIGHT_OPTIONS_COUNT 2 +const CMenuOptionChooser::keyval CHANNELLIST_EPGTEXT_ALIGN_RIGHT_OPTIONS[CHANNELLIST_EPGTEXT_ALIGN_RIGHT_OPTIONS_COUNT]= +{ + { 0 , LOCALE_CHANNELLIST_EPGTEXT_ALIGN_LEFT }, + { 1 , LOCALE_CHANNELLIST_EPGTEXT_ALIGN_RIGHT } +}; + +#define CPU_FREQ_OPTION_COUNT 13 +const CMenuOptionChooser::keyval CPU_FREQ_OPTIONS[CPU_FREQ_OPTION_COUNT] = +{ + { 0, LOCALE_CPU_FREQ_DEFAULT }, + { 50, NONEXISTANT_LOCALE, "50 Mhz"}, + { 100, NONEXISTANT_LOCALE, "100 Mhz"}, + { 150, NONEXISTANT_LOCALE, "150 Mhz"}, + { 200, NONEXISTANT_LOCALE, "200 Mhz"}, + { 250, NONEXISTANT_LOCALE, "250 Mhz"}, + { 300, NONEXISTANT_LOCALE, "300 Mhz"}, + { 350, NONEXISTANT_LOCALE, "350 Mhz"}, + { 400, NONEXISTANT_LOCALE, "400 Mhz"}, + { 450, NONEXISTANT_LOCALE, "450 Mhz"}, + { 500, NONEXISTANT_LOCALE, "500 Mhz"}, + { 550, NONEXISTANT_LOCALE, "550 Mhz"}, + { 600, NONEXISTANT_LOCALE, "600 Mhz"}, +}; + +void CNeutrinoApp::InitMiscSettings(CMenuWidget &miscSettings) +{ + dprintf(DEBUG_DEBUG, "init miscsettings\n"); + miscSettings.addItem(GenericMenuSeparator); + miscSettings.addItem(GenericMenuBack); + miscSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_SAVESETTINGSNOW, true, NULL, this, "savesettings", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + miscSettings.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MISCSETTINGS_GENERAL)); + +#if 0 + CMenuOptionChooser *m1 = new CMenuOptionChooser(LOCALE_MISCSETTINGS_SHUTDOWN_REAL_RCDELAY, &g_settings.shutdown_real_rcdelay, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, !g_settings.shutdown_real); + + CMiscNotifier* miscNotifier = new CMiscNotifier( m1 ); + + miscSettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_SHUTDOWN_REAL, &g_settings.shutdown_real, OPTIONS_OFF1_ON0_OPTIONS, OPTIONS_OFF1_ON0_OPTION_COUNT, true, miscNotifier)); +#endif + CStringInput * miscSettings_shutdown_count = new CStringInput(LOCALE_MISCSETTINGS_SHUTDOWN_COUNT, g_settings.shutdown_count, 3, LOCALE_MISCSETTINGS_SHUTDOWN_COUNT_HINT1, LOCALE_MISCSETTINGS_SHUTDOWN_COUNT_HINT2, "0123456789 "); + + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_SHUTDOWN_COUNT, true, g_settings.shutdown_count, miscSettings_shutdown_count)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_STARTSTANDBY, &g_settings.power_standby, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + //miscSettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_INFOBAR_SAT_DISPLAY, &g_settings.infobar_sat_display, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_ROTORSWAP, &g_settings.rotor_swap, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + miscSettings.addItem(new CMenuOptionChooser(LOCALE_INFOVIEWER_SUBCHAN_DISP_POS, &g_settings.infobar_subchan_disp_pos, INFOBAR_SUBCHAN_DISP_POS_OPTIONS, INFOBAR_SUBCHAN_DISP_POS_OPTIONS_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_CACHE_TXT, (int *)&g_settings.cacheTXT, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_VIRTUAL_ZAP_MODE, &g_settings.virtual_zap_mode, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + //channellist + miscSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MISCSETTINGS_CHANNELLIST)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_CHANNELLIST_EPGTEXT_ALIGN, &g_settings.channellist_epgtext_align_right, CHANNELLIST_EPGTEXT_ALIGN_RIGHT_OPTIONS, CHANNELLIST_EPGTEXT_ALIGN_RIGHT_OPTIONS_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_CHANNELLIST_EXTENDED, &g_settings.channellist_extended, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_CHANNELLIST_MAKE_HDLIST, &g_settings.make_hd_list, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAP_CYCLE, &g_settings.zap_cycle, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + miscSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MISCSETTINGS_EPG_HEAD)); + + CSectionsdConfigNotifier* sectionsdConfigNotifier = new CSectionsdConfigNotifier; + miscSettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_EPG_SAVE, &g_settings.epg_save, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + CStringInput * miscSettings_epg_cache = new CStringInput(LOCALE_MISCSETTINGS_EPG_CACHE, &g_settings.epg_cache, 2,LOCALE_MISCSETTINGS_EPG_CACHE_HINT1, LOCALE_MISCSETTINGS_EPG_CACHE_HINT2 , "0123456789 ", sectionsdConfigNotifier); + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_EPG_CACHE, true, g_settings.epg_cache, miscSettings_epg_cache)); + CStringInput * miscSettings_epg_cache_e = new CStringInput(LOCALE_MISCSETTINGS_EPG_EXTENDEDCACHE, &g_settings.epg_extendedcache, 3,LOCALE_MISCSETTINGS_EPG_EXTENDEDCACHE_HINT1, LOCALE_MISCSETTINGS_EPG_EXTENDEDCACHE_HINT2 , "0123456789 ", sectionsdConfigNotifier); + + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_EPG_EXTENDEDCACHE, true, g_settings.epg_extendedcache, miscSettings_epg_cache_e)); + + CStringInput * miscSettings_epg_old_events = new CStringInput(LOCALE_MISCSETTINGS_EPG_OLD_EVENTS, &g_settings.epg_old_events, 2,LOCALE_MISCSETTINGS_EPG_OLD_EVENTS_HINT1, LOCALE_MISCSETTINGS_EPG_OLD_EVENTS_HINT2 , "0123456789 ", sectionsdConfigNotifier); + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_EPG_OLD_EVENTS, true, g_settings.epg_old_events, miscSettings_epg_old_events)); + CStringInput * miscSettings_epg_max_events = new CStringInput(LOCALE_MISCSETTINGS_EPG_MAX_EVENTS, &g_settings.epg_max_events, 5,LOCALE_MISCSETTINGS_EPG_MAX_EVENTS_HINT1, LOCALE_MISCSETTINGS_EPG_MAX_EVENTS_HINT2 , "0123456789 ", sectionsdConfigNotifier); + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_EPG_MAX_EVENTS, true, g_settings.epg_max_events, miscSettings_epg_max_events)); + miscSettings.addItem(new CMenuForwarder(LOCALE_MISCSETTINGS_EPG_DIR, true, g_settings.epg_dir, this, "epgdir")); + +#if 0 + miscSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MISCSETTINGS_DRIVER_BOOT)); + if(access("/var/tuxbox/emlog", F_OK) == 0) + g_settings.emlog = 1; + miscSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_USELOG, &g_settings.emlog, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, new CLogChangeNotifier)); +#endif + +#if 0 + keySetupNotifier = new CKeySetupNotifier; + CStringInput * keySettings_repeat_genericblocker = new CStringInput(LOCALE_KEYBINDINGMENU_REPEATBLOCKGENERIC, g_settings.repeat_genericblocker, 3, LOCALE_REPEATBLOCKER_HINT_1, LOCALE_REPEATBLOCKER_HINT_2, "0123456789 ", keySetupNotifier); + CStringInput * keySettings_repeatBlocker = new CStringInput(LOCALE_KEYBINDINGMENU_REPEATBLOCK, g_settings.repeat_blocker, 3, LOCALE_REPEATBLOCKER_HINT_1, LOCALE_REPEATBLOCKER_HINT_2, "0123456789 ", keySetupNotifier); + keySetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + + miscSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_RC)); + miscSettings.addItem(new CMenuForwarder(LOCALE_KEYBINDINGMENU_REPEATBLOCK, true, g_settings.repeat_blocker, keySettings_repeatBlocker)); + miscSettings.addItem(new CMenuForwarder(LOCALE_KEYBINDINGMENU_REPEATBLOCKGENERIC, true, g_settings.repeat_genericblocker, keySettings_repeat_genericblocker)); + miscSettings.addItem(m1); +#endif + + miscSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_FILEBROWSER_HEAD)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_FILESYSTEM_IS_UTF8 , &g_settings.filesystem_is_utf8 , MISCSETTINGS_FILESYSTEM_IS_UTF8_OPTIONS, MISCSETTINGS_FILESYSTEM_IS_UTF8_OPTION_COUNT, true )); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_FILEBROWSER_SHOWRIGHTS , &g_settings.filebrowser_showrights , MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_FILEBROWSER_DENYDIRECTORYLEAVE, &g_settings.filebrowser_denydirectoryleave, MESSAGEBOX_NO_YES_OPTIONS , MESSAGEBOX_NO_YES_OPTION_COUNT , true )); + // miscSettings.addItem(new CMenuForwarder(LOCALE_EXTRA_KEY_PLUGIN, true, g_settings.onekey_plugin,this,"onekeyplugin")); + funNotifier = new CFanControlNotifier(); + //miscSettings.addItem(new CMenuOptionNumberChooser(LOCALE_FAN_SPEED, &g_settings.fan_speed, true, 0, 14, funNotifier, 0, 0, LOCALE_OPTIONS_OFF) ); + miscSettings.addItem(GenericMenuSeparatorLine); + miscSettings.addItem(new CMenuOptionNumberChooser(LOCALE_FAN_SPEED, &g_settings.fan_speed, true, 1, 14, funNotifier, 0, 0, LOCALE_OPTIONS_OFF) ); + funNotifier->changeNotify(NONEXISTANT_LOCALE, (void*) &g_settings.fan_speed); + + CCpuFreqNotifier * cpuNotifier = new CCpuFreqNotifier(); +#if 0 + miscSettings.addItem(new CMenuOptionChooser(LOCALE_CPU_FREQ_NORMAL, &g_settings.cpufreq, CPU_FREQ_OPTIONS, CPU_FREQ_OPTION_COUNT, true, cpuNotifier)); + miscSettings.addItem(new CMenuOptionChooser(LOCALE_CPU_FREQ_STANDBY, &g_settings.standby_cpufreq, CPU_FREQ_OPTIONS, CPU_FREQ_OPTION_COUNT, true)); +#endif +} + +void CNeutrinoApp::InitLanguageSettings(CMenuWidget &languageSettings) +{ + languageSettings.addItem(GenericMenuSeparator); + languageSettings.addItem(GenericMenuBack); + languageSettings.addItem(GenericMenuSeparatorLine); + + struct dirent **namelist; + int n; + // printf("scanning locale dir now....(perhaps)\n"); + + char *pfad[] = {(char *) DATADIR "/neutrino/locale",(char *) "/var/tuxbox/config/locale"}; + + for(int p = 0;p < 2;p++) { + n = scandir(pfad[p], &namelist, 0, alphasort); + if(n < 0) { + perror("loading locales: scandir"); + } else { + for(int count=0;countd_name); + char * pos = strstr(locale, ".locale"); + if(pos != NULL) { + char iname[50]; + *pos = '\0'; + sprintf(iname, "%s.raw", locale); + CMenuOptionLanguageChooser* oj = new CMenuOptionLanguageChooser((char*)locale, this, iname); + oj->addOption(locale); + languageSettings.addItem( oj ); + } else + free(locale); + free(namelist[count]); + } + free(namelist); + } + } +} + +#define AUDIOMENU_ANALOGOUT_OPTION_COUNT 3 +const CMenuOptionChooser::keyval AUDIOMENU_ANALOGOUT_OPTIONS[AUDIOMENU_ANALOGOUT_OPTION_COUNT] = +{ + { 0, LOCALE_AUDIOMENU_STEREO }, + { 1, LOCALE_AUDIOMENU_MONOLEFT }, + { 2, LOCALE_AUDIOMENU_MONORIGHT } +}; + +#define AUDIOMENU_AVS_CONTROL_OPTION_COUNT 3 +const CMenuOptionChooser::keyval AUDIOMENU_AVS_CONTROL_OPTIONS[AUDIOMENU_AVS_CONTROL_OPTION_COUNT] = +{ + { CControld::TYPE_OST , LOCALE_AUDIOMENU_OST }, + { CControld::TYPE_AVS , LOCALE_AUDIOMENU_AVS }, + { CControld::TYPE_LIRC, LOCALE_AUDIOMENU_LIRC } +}; + +#define AUDIOMENU_SRS_OPTION_COUNT 2 +const CMenuOptionChooser::keyval AUDIOMENU_SRS_OPTIONS[AUDIOMENU_SRS_OPTION_COUNT] = +{ + { 0 , LOCALE_SRS_ALGO_LIGHT }, + { 1 , LOCALE_SRS_ALGO_NORMAL } +}; + +#define AUDIOMENU_AVSYNC_OPTION_COUNT 3 +const CMenuOptionChooser::keyval AUDIOMENU_AVSYNC_OPTIONS[AUDIOMENU_AVSYNC_OPTION_COUNT] = +{ + { 0, LOCALE_OPTIONS_OFF }, + { 1, LOCALE_OPTIONS_ON }, + { 2, LOCALE_AUDIOMENU_AVSYNC_AM } +}; + +#define AUDIOMENU_CLOCKREC_OPTION_COUNT 3 +const CMenuOptionChooser::keyval AUDIOMENU_CLOCKREC_OPTIONS[AUDIOMENU_CLOCKREC_OPTION_COUNT] = +{ + { 0, LOCALE_OPTIONS_OFF }, + { 1, LOCALE_OPTIONS_ON }, +}; + +void CNeutrinoApp::InitAudioSettings(CMenuWidget &audioSettings, CAudioSetupNotifier* audioSetupNotifier) +{ + audioSettings.addItem(GenericMenuSeparator); + audioSettings.addItem(GenericMenuBack); + audioSettings.addItem(GenericMenuSeparatorLine); + + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_ANALOGOUT, &g_settings.audio_AnalogMode, AUDIOMENU_ANALOGOUT_OPTIONS, AUDIOMENU_ANALOGOUT_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_DOLBYDIGITAL, &g_settings.audio_DolbyDigital, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_ENGLISH, &g_settings.audio_english, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_HDMI_DD, &g_settings.hdmi_dd, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_SPDIF_DD, &g_settings.spdif_dd, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_AVSYNC, &g_settings.avsync, AUDIOMENU_AVSYNC_OPTIONS, AUDIOMENU_AVSYNC_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_AUDIOMENU_CLOCKREC, &g_settings.clockrec, AUDIOMENU_CLOCKREC_OPTIONS, AUDIOMENU_CLOCKREC_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_SRS_IQ)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_SRS_IQ, &g_settings.srs_enable, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_SRS_ALGO, &g_settings.srs_algo, AUDIOMENU_SRS_OPTIONS, AUDIOMENU_SRS_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionChooser(LOCALE_SRS_NMGR, &g_settings.srs_nmgr_enable, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, audioSetupNotifier)); + audioSettings.addItem(new CMenuOptionNumberChooser(LOCALE_SRS_VOLUME, &g_settings.srs_ref_volume, true, 1, 100, audioSetupNotifier)); + +#if 0 + CStringInput * audio_PCMOffset = new CStringInput(LOCALE_AUDIOMENU_PCMOFFSET, g_settings.audio_PCMOffset, 2, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "0123456789 ", audioSetupNotifier); + CMenuForwarder *mf = new CMenuForwarder(LOCALE_AUDIOMENU_PCMOFFSET, (g_settings.audio_avs_Control == CControld::TYPE_LIRC), g_settings.audio_PCMOffset, audio_PCMOffset ); + audioSettings.addItem(mf); +#endif +} + + + +#if 1 +#define PARENTALLOCK_PROMPT_OPTION_COUNT 3 +#else +#define PARENTALLOCK_PROMPT_OPTION_COUNT 4 +#endif +const CMenuOptionChooser::keyval PARENTALLOCK_PROMPT_OPTIONS[PARENTALLOCK_PROMPT_OPTION_COUNT] = +{ + { PARENTALLOCK_PROMPT_NEVER , LOCALE_PARENTALLOCK_NEVER }, +#if 0 + { PARENTALLOCK_PROMPT_ONSTART , LOCALE_PARENTALLOCK_ONSTART }, +#endif + { PARENTALLOCK_PROMPT_CHANGETOLOCKED, LOCALE_PARENTALLOCK_CHANGETOLOCKED }, + { PARENTALLOCK_PROMPT_ONSIGNAL , LOCALE_PARENTALLOCK_ONSIGNAL } +}; + +#define PARENTALLOCK_LOCKAGE_OPTION_COUNT 3 +const CMenuOptionChooser::keyval PARENTALLOCK_LOCKAGE_OPTIONS[PARENTALLOCK_LOCKAGE_OPTION_COUNT] = +{ + { 12, LOCALE_PARENTALLOCK_LOCKAGE12 }, + { 16, LOCALE_PARENTALLOCK_LOCKAGE16 }, + { 18, LOCALE_PARENTALLOCK_LOCKAGE18 } +}; + +void CNeutrinoApp::InitParentalLockSettings(CMenuWidget &parentallockSettings) +{ + parentallockSettings.addItem(GenericMenuSeparator); + parentallockSettings.addItem(GenericMenuBack); + parentallockSettings.addItem(GenericMenuSeparatorLine); + + parentallockSettings.addItem(new CMenuOptionChooser(LOCALE_PARENTALLOCK_PROMPT , &g_settings.parentallock_prompt , PARENTALLOCK_PROMPT_OPTIONS , PARENTALLOCK_PROMPT_OPTION_COUNT , !parentallocked)); + + parentallockSettings.addItem(new CMenuOptionChooser(LOCALE_PARENTALLOCK_LOCKAGE, &g_settings.parentallock_lockage, PARENTALLOCK_LOCKAGE_OPTIONS, PARENTALLOCK_LOCKAGE_OPTION_COUNT, !parentallocked)); + + CPINChangeWidget * pinChangeWidget = new CPINChangeWidget(LOCALE_PARENTALLOCK_CHANGEPIN, g_settings.parentallock_pincode, 4, LOCALE_PARENTALLOCK_CHANGEPIN_HINT1); + parentallockSettings.addItem( new CMenuForwarder(LOCALE_PARENTALLOCK_CHANGEPIN, true, g_settings.parentallock_pincode, pinChangeWidget)); +} + +void CNeutrinoApp::InitNetworkSettings(CMenuWidget &networkSettings) +{ + CIPInput * networkSettings_NetworkIP = new CIPInput(LOCALE_NETWORKMENU_IPADDRESS , networkConfig.address , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2, MyIPChanger); + CIPInput * networkSettings_NetMask = new CIPInput(LOCALE_NETWORKMENU_NETMASK , networkConfig.netmask , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CIPInput * networkSettings_Broadcast = new CIPInput(LOCALE_NETWORKMENU_BROADCAST , networkConfig.broadcast , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CIPInput * networkSettings_Gateway = new CIPInput(LOCALE_NETWORKMENU_GATEWAY , networkConfig.gateway , LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CIPInput * networkSettings_NameServer = new CIPInput(LOCALE_NETWORKMENU_NAMESERVER, networkConfig.nameserver, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + + CSectionsdConfigNotifier* sectionsdConfigNotifier = new CSectionsdConfigNotifier; + CStringInputSMS * networkSettings_NtpServer = new CStringInputSMS(LOCALE_NETWORKMENU_NTPSERVER, &g_settings.network_ntpserver, 30, LOCALE_NETWORKMENU_NTPSERVER_HINT1, LOCALE_NETWORKMENU_NTPSERVER_HINT2, "abcdefghijklmnopqrstuvwxyz0123456789-. ", sectionsdConfigNotifier); + CStringInput * networkSettings_NtpRefresh = new CStringInput(LOCALE_NETWORKMENU_NTPREFRESH, &g_settings.network_ntprefresh, 3,LOCALE_NETWORKMENU_NTPREFRESH_HINT1, LOCALE_NETWORKMENU_NTPREFRESH_HINT2 , "0123456789 ", sectionsdConfigNotifier); + + CMenuForwarder *m0 = new CMenuForwarder(LOCALE_NETWORKMENU_SETUPNOW, true, NULL, this, "network", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED); + CMenuForwarder *m1 = new CMenuForwarder(LOCALE_NETWORKMENU_IPADDRESS , networkConfig.inet_static, networkConfig.address , networkSettings_NetworkIP ); + CMenuForwarder *m2 = new CMenuForwarder(LOCALE_NETWORKMENU_NETMASK , networkConfig.inet_static, networkConfig.netmask , networkSettings_NetMask ); + CMenuForwarder *m3 = new CMenuForwarder(LOCALE_NETWORKMENU_BROADCAST , networkConfig.inet_static, networkConfig.broadcast , networkSettings_Broadcast ); + CMenuForwarder *m4 = new CMenuForwarder(LOCALE_NETWORKMENU_GATEWAY , networkConfig.inet_static, networkConfig.gateway , networkSettings_Gateway ); + CMenuForwarder *m5 = new CMenuForwarder(LOCALE_NETWORKMENU_NAMESERVER, networkConfig.inet_static, networkConfig.nameserver, networkSettings_NameServer); + CMenuForwarder *m6 = new CMenuForwarder( LOCALE_NETWORKMENU_NTPSERVER, true , g_settings.network_ntpserver, networkSettings_NtpServer ); + CMenuForwarder *m7 = new CMenuForwarder( LOCALE_NETWORKMENU_NTPREFRESH, true , g_settings.network_ntprefresh, networkSettings_NtpRefresh ); + + CDHCPNotifier* dhcpNotifier = new CDHCPNotifier(m1,m2,m3,m4,m5); + + network_automatic_start = networkConfig.automatic_start ? 1 : 0; + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_NETWORKMENU_SETUPONSTARTUP, &network_automatic_start, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + networkSettings.addItem(GenericMenuSeparator); + networkSettings.addItem(GenericMenuBack); + networkSettings.addItem(GenericMenuSeparatorLine); + + networkSettings.addItem( oj ); + networkSettings.addItem(new CMenuForwarder(LOCALE_NETWORKMENU_TEST, true, NULL, this, "networktest")); + networkSettings.addItem(new CMenuForwarder(LOCALE_NETWORKMENU_SHOW, true, NULL, this, "networkshow", CRCInput::RC_help, NEUTRINO_ICON_BUTTON_HELP_SMALL)); + networkSettings.addItem( m0 ); + + networkSettings.addItem(GenericMenuSeparatorLine); + + + network_dhcp = networkConfig.inet_static ? 0 : 1; + oj = new CMenuOptionChooser(LOCALE_NETWORKMENU_DHCP, &network_dhcp, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, dhcpNotifier); + networkSettings.addItem(oj); + networkSettings.addItem(GenericMenuSeparatorLine); + + networkSettings.addItem( m1); + networkSettings.addItem( m2); + networkSettings.addItem( m3); + + networkSettings.addItem(GenericMenuSeparatorLine); + networkSettings.addItem( m4); + networkSettings.addItem( m5); + networkSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_NETWORKMENU_NTPTITLE)); + networkSettings.addItem(new CMenuOptionChooser(LOCALE_NETWORKMENU_NTPENABLE, &g_settings.network_ntpenable, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, sectionsdConfigNotifier)); + networkSettings.addItem( m6); + networkSettings.addItem( m7); + + networkSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_NETWORKMENU_MOUNT)); + networkSettings.addItem(new CMenuForwarder(LOCALE_NFS_MOUNT , true, NULL, new CNFSMountGui(), NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN)); + networkSettings.addItem(new CMenuForwarder(LOCALE_NFS_UMOUNT, true, NULL, new CNFSUmountGui(), NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW)); +} + +#define RECORDINGMENU_RECORDING_TYPE_OPTION_COUNT 4 +const CMenuOptionChooser::keyval RECORDINGMENU_RECORDING_TYPE_OPTIONS[RECORDINGMENU_RECORDING_TYPE_OPTION_COUNT] = +{ + { CNeutrinoApp::RECORDING_OFF , LOCALE_RECORDINGMENU_OFF }, + { CNeutrinoApp::RECORDING_SERVER, LOCALE_RECORDINGMENU_SERVER }, + { CNeutrinoApp::RECORDING_VCR , LOCALE_RECORDINGMENU_VCR }, + { CNeutrinoApp::RECORDING_FILE , LOCALE_RECORDINGMENU_FILE } +}; + +#define CHOOSE_DIRECT_REC_DIR_COUNT 3 +const CMenuOptionChooser::keyval CHOOSE_DIRECT_REC_DIR[RECORDINGMENU_RECORDING_TYPE_OPTION_COUNT] = +{ + {0, LOCALE_OPTIONS_OFF}, + {1, LOCALE_NFS_TYPE_NFS}, + {2, LOCALE_NFS_LOCALDIR} +}; +void CNeutrinoApp::InitRecordingSettings(CMenuWidget &recordingSettings) +{ +#if 0 + CIPInput * recordingSettings_server_ip = new CIPInput(LOCALE_RECORDINGMENU_SERVER_IP, g_settings.recording_server_ip, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CStringInput * recordingSettings_server_port = new CStringInput(LOCALE_RECORDINGMENU_SERVER_PORT, g_settings.recording_server_port, 6, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2, "0123456789 "); + + CMenuForwarder * mf1 = new CMenuForwarder(LOCALE_RECORDINGMENU_SERVER_IP, (g_settings.recording_type == RECORDING_SERVER), g_settings.recording_server_ip, recordingSettings_server_ip); + CMenuForwarder * mf2 = new CMenuForwarder(LOCALE_RECORDINGMENU_SERVER_PORT, (g_settings.recording_type == RECORDING_SERVER), g_settings.recording_server_port, recordingSettings_server_port); + + CMACInput * recordingSettings_server_mac = new CMACInput(LOCALE_RECORDINGMENU_SERVER_MAC, g_settings.recording_server_mac, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CMenuForwarder * mf3 = new CMenuForwarder(LOCALE_RECORDINGMENU_SERVER_MAC, ((g_settings.recording_type == RECORDING_SERVER) && g_settings.recording_server_wakeup==1), g_settings.recording_server_mac, recordingSettings_server_mac); + + CRecordingNotifier2 * RecordingNotifier2 = new CRecordingNotifier2(mf3); + + CMenuOptionChooser * oj2 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_SERVER_WAKEUP, &g_settings.recording_server_wakeup, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, (g_settings.recording_type == RECORDING_SERVER), RecordingNotifier2); + + CMenuOptionChooser* oj3 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_STOPPLAYBACK, &g_settings.recording_stopplayback, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, (g_settings.recording_type == RECORDING_SERVER || g_settings.recording_type == RECORDING_FILE)); + + CMenuOptionChooser* oj4 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_STOPSECTIONSD, &g_settings.recording_stopsectionsd, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, (g_settings.recording_type == RECORDING_SERVER || g_settings.recording_type == RECORDING_FILE)); + CMenuOptionChooser* oj4b = new CMenuOptionChooser(LOCALE_RECORDINGMENU_ZAP_ON_ANNOUNCE, &g_settings.recording_zap_on_announce, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + CMenuOptionChooser* oj5 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_NO_SCART, &g_settings.recording_vcr_no_scart, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, (g_settings.recording_type == RECORDING_VCR)); + + CMenuOptionChooser* oj12 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_RECORD_IN_SPTS_MODE, &g_settings.recording_in_spts_mode, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT,(g_settings.recording_type == RECORDING_SERVER || g_settings.recording_type == RECORDING_FILE) ); +#endif + int pre,post; + g_Timerd->getRecordingSafety(pre,post); + sprintf(g_settings.record_safety_time_before, "%02d", pre/60); + sprintf(g_settings.record_safety_time_after, "%02d", post/60); + + CRecordingSafetyNotifier *RecordingSafetyNotifier = new CRecordingSafetyNotifier; + CStringInput * timerBefore = new CStringInput(LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_BEFORE, g_settings.record_safety_time_before, 2, LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_BEFORE_HINT_1, LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_BEFORE_HINT_2,"0123456789 ", RecordingSafetyNotifier); + CMenuForwarder *fTimerBefore = new CMenuForwarder(LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_BEFORE, true, g_settings.record_safety_time_before, timerBefore); + + CStringInput * timerAfter = new CStringInput(LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_AFTER, g_settings.record_safety_time_after, 2, LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_AFTER_HINT_1, LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_AFTER_HINT_2,"0123456789 ", RecordingSafetyNotifier); + CMenuForwarder *fTimerAfter = new CMenuForwarder(LOCALE_TIMERSETTINGS_RECORD_SAFETY_TIME_AFTER, true, g_settings.record_safety_time_after, timerAfter); + + // default recording audio pids + //CMenuWidget * apidMenu = new CMenuWidget(LOCALE_RECORDINGMENU_APIDS, "audio.raw"); + //CMenuForwarder* fApidMenu = new CMenuForwarder(LOCALE_RECORDINGMENU_APIDS ,true, NULL, apidMenu); + g_settings.recording_audio_pids_std = ( g_settings.recording_audio_pids_default & TIMERD_APIDS_STD ) ? 1 : 0 ; + g_settings.recording_audio_pids_alt = ( g_settings.recording_audio_pids_default & TIMERD_APIDS_ALT ) ? 1 : 0 ; + g_settings.recording_audio_pids_ac3 = ( g_settings.recording_audio_pids_default & TIMERD_APIDS_AC3 ) ? 1 : 0 ; + + CRecAPIDSettingsNotifier * an = new CRecAPIDSettingsNotifier; + CMenuOptionChooser* aoj1 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_STD, &g_settings.recording_audio_pids_std, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, an); + CMenuOptionChooser* aoj2 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_ALT, &g_settings.recording_audio_pids_alt, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, an); + CMenuOptionChooser* aoj3 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_APIDS_AC3, &g_settings.recording_audio_pids_ac3, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true, an); +#if 0 + apidRecordingSettings->addItem(GenericMenuSeparator); + apidRecordingSettings->addItem(GenericMenuBack); + apidRecordingSettings->addItem(GenericMenuSeparatorLine); + apidRecordingSettings->addItem(aoj1); + apidRecordingSettings->addItem(aoj2); + apidRecordingSettings->addItem(aoj3); +#endif + CMenuForwarder* fRecDir = new CMenuForwarder(LOCALE_RECORDINGMENU_DEFDIR, true, g_settings.network_nfs_recordingdir, this, "recordingdir"); + CMenuForwarder* fTsDir = new CMenuForwarder(LOCALE_RECORDINGMENU_TSDIR, true, g_settings.timeshiftdir, this, "timeshiftdir"); + CMenuOptionChooser* zapAnnounce = new CMenuOptionChooser(LOCALE_RECORDINGMENU_ZAP_ON_ANNOUNCE, &g_settings.recording_zap_on_announce, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + recordingSettings.addItem(GenericMenuSeparator); + recordingSettings.addItem(GenericMenuBack); + recordingSettings.addItem(new CMenuForwarder(LOCALE_RECORDINGMENU_SETUPNOW, true, NULL, this, "recording", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + recordingSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_TIMERSETTINGS_SEPARATOR)); + recordingSettings.addItem(fTimerBefore); + recordingSettings.addItem(fTimerAfter); + recordingSettings.addItem(zapAnnounce); + recordingSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_RECORDINGMENU_APIDS)); + recordingSettings.addItem(aoj1); + recordingSettings.addItem(aoj2); + recordingSettings.addItem(aoj3); + recordingSettings.addItem(GenericMenuSeparatorLine); + recordingSettings.addItem(fRecDir); + recordingSettings.addItem(fTsDir); + recordingSettings.addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_RECORD_TIME, &g_settings.record_hours, true, 1, 24, NULL) ); + if(1) { //has_hdd) { + recordingSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_TIMESHIFT_PAUSE, &g_settings.timeshift_pause, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + recordingSettings.addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_AUTO_TIMESHIFT, &g_settings.auto_timeshift, true, 0, 300, NULL)); + recordingSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_AUTO_DELETE, &g_settings.auto_delete, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + recordingSettings.addItem(new CMenuOptionChooser(LOCALE_EXTRA_TEMP_TIMESHIFT, &g_settings.temp_timeshift, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + } +#if 0 + // for direct recording + CMenuWidget *directRecordingSettings = new CMenuWidget(LOCALE_RECORDINGMENU_FILESETTINGS, NEUTRINO_ICON_RECORDING); + + CMenuForwarder* mf7 = new CMenuForwarder(LOCALE_RECORDINGMENU_FILESETTINGS,(g_settings.recording_type == RECORDING_FILE),NULL,directRecordingSettings, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN); + + + CStringInput * recordingSettings_splitsize = new CStringInput(LOCALE_RECORDINGMENU_SPLITSIZE, g_settings.recording_splitsize, 6, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2, "0123456789 "); + CMenuForwarder* mf9 = new CMenuForwarder(LOCALE_RECORDINGMENU_SPLITSIZE, true, g_settings.recording_splitsize,recordingSettings_splitsize); + + CMenuOptionChooser* oj6 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_USE_O_SYNC, &g_settings.recording_use_o_sync, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + CMenuOptionChooser* oj7 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_USE_FDATASYNC, &g_settings.recording_use_fdatasync, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + CMenuOptionChooser* oj8 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_STREAM_VTXT_PID, &g_settings.recording_stream_vtxt_pid, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + CMenuOptionChooser* oj9 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_STREAM_PMT_PID, &g_settings.recording_stream_pmt_pid, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + CStringInput * recordingSettings_ringbuffers = new CStringInput(LOCALE_RECORDINGMENU_RINGBUFFERS, g_settings.recording_ringbuffers, 2, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2, "0123456789 "); + CMenuForwarder* mf10 = new CMenuForwarder(LOCALE_RECORDINGMENU_RINGBUFFERS, true, g_settings.recording_ringbuffers,recordingSettings_ringbuffers); + //CMenuOptionChooser* oj10 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_CHOOSE_DIRECT_REC_DIR, &g_settings.recording_choose_direct_rec_dir, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + CMenuOptionChooser* oj10 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_CHOOSE_DIRECT_REC_DIR, &g_settings.recording_choose_direct_rec_dir, CHOOSE_DIRECT_REC_DIR, CHOOSE_DIRECT_REC_DIR_COUNT, true); + + CMenuOptionChooser* oj11 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_EPG_FOR_FILENAME, &g_settings.recording_epg_for_filename, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + CMenuOptionChooser* oj13 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_SAVE_IN_CHANNELDIR, &g_settings.recording_save_in_channeldir, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true); + + CRecordingNotifier *RecordingNotifier = new CRecordingNotifier(mf1,mf2,oj2,mf3,oj3,oj4,oj5,mf7,oj12); + + CMenuOptionChooser* oj1 = new CMenuOptionChooser(LOCALE_RECORDINGMENU_RECORDING_TYPE, &g_settings.recording_type, RECORDINGMENU_RECORDING_TYPE_OPTIONS, RECORDINGMENU_RECORDING_TYPE_OPTION_COUNT, true, RecordingNotifier); + + recordingSettings.addItem(GenericMenuSeparator); + recordingSettings.addItem(GenericMenuBack); + //recordingSettings.addItem(new CMenuForwarder(LOCALE_SETTINGS_HELP, true, NULL, this, "help_recording", CRCInput::RC_help, NEUTRINO_ICON_BUTTON_HELP_SMALL)); + recordingSettings.addItem(new CMenuForwarder(LOCALE_RECORDINGMENU_SETUPNOW, true, NULL, this, "recording", CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); + recordingSettings.addItem(GenericMenuSeparatorLine); + recordingSettings.addItem( oj1); + recordingSettings.addItem(GenericMenuSeparatorLine); + recordingSettings.addItem( mf1); + recordingSettings.addItem( mf2); + recordingSettings.addItem( oj2); + recordingSettings.addItem( mf3); + recordingSettings.addItem( oj3); + recordingSettings.addItem( oj4); + recordingSettings.addItem(GenericMenuSeparatorLine); + recordingSettings.addItem( oj5); + recordingSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_TIMERSETTINGS_SEPARATOR)); + recordingSettings.addItem( mf5); + recordingSettings.addItem( mf6); + recordingSettings.addItem( oj4b); + recordingSettings.addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_RECORD_TIME, &g_settings.record_hours, true, 1, 24) ); + recordingSettings.addItem(GenericMenuSeparatorLine); +#if 0 + recordingSettings.addItem(oj12); +#endif + recordingSettings.addItem( mf13); + recordingSettings.addItem( mf7); + + directRecordingSettings->addItem(GenericMenuSeparator); + directRecordingSettings->addItem(GenericMenuBack); + directRecordingSettings->addItem(GenericMenuSeparatorLine); + directRecordingSettings->addItem(mf8); + directRecordingSettings->addItem(mf9); + directRecordingSettings->addItem(mf10); + directRecordingSettings->addItem(oj6); + directRecordingSettings->addItem(oj7); + directRecordingSettings->addItem(oj8); + directRecordingSettings->addItem(oj9); + directRecordingSettings->addItem(oj10); + directRecordingSettings->addItem(oj11); + directRecordingSettings->addItem(oj13); + recordingstatus = 0; + if(has_hdd) { + directRecordingSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_TIMESHIFT_PAUSE, &g_settings.timeshift_pause, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + directRecordingSettings->addItem(new CMenuOptionNumberChooser(LOCALE_EXTRA_AUTO_TIMESHIFT, &g_settings.auto_timeshift, true, 0, 300)); + directRecordingSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_AUTO_DELETE, &g_settings.auto_delete, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + directRecordingSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_TEMP_TIMESHIFT, &g_settings.temp_timeshift, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + } +#endif +} + +#define STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC_OPTION_COUNT 2 +const CMenuOptionChooser::keyval STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC_OPTIONS[STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC_OPTION_COUNT] = +{ + { 0, LOCALE_STREAMINGMENU_MPEG1 }, + { 1, LOCALE_STREAMINGMENU_MPEG2 } +}; + +#define STREAMINGMENU_STREAMING_RESOLUTION_OPTION_COUNT 4 +const CMenuOptionChooser::keyval STREAMINGMENU_STREAMING_RESOLUTION_OPTIONS[STREAMINGMENU_STREAMING_RESOLUTION_OPTION_COUNT] = +{ + { 0, LOCALE_STREAMINGMENU_352X288 }, + { 1, LOCALE_STREAMINGMENU_352X576 }, + { 2, LOCALE_STREAMINGMENU_480X576 }, + { 3, LOCALE_STREAMINGMENU_704X576 } +}; + +#define STREAMINGMENU_STREAMING_TYPE_OPTION_COUNT 2 +const CMenuOptionChooser::keyval STREAMINGMENU_STREAMING_TYPE_OPTIONS[STREAMINGMENU_STREAMING_TYPE_OPTION_COUNT] = +{ + { 0, LOCALE_STREAMINGMENU_OFF }, + { 1, LOCALE_STREAMINGMENU_ON } +}; + +void CNeutrinoApp::InitStreamingSettings(CMenuWidget &streamingSettings) +{ + streamingSettings.addItem(GenericMenuSeparator); + streamingSettings.addItem(GenericMenuBack); + streamingSettings.addItem(GenericMenuSeparatorLine); + + CIPInput * streamingSettings_server_ip = new CIPInput(LOCALE_STREAMINGMENU_SERVER_IP, g_settings.streaming_server_ip, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2); + CStringInput * streamingSettings_server_port = new CStringInput(LOCALE_STREAMINGMENU_SERVER_PORT, g_settings.streaming_server_port, 6, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2,"0123456789 "); + CStringInputSMS * cddriveInput = new CStringInputSMS(LOCALE_STREAMINGMENU_STREAMING_SERVER_CDDRIVE, g_settings.streaming_server_cddrive, 20, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, "abcdefghijklmnopqrstuvwxyz0123456789!""§$%&/()=?-:\\ "); + CStringInput * streamingSettings_videorate = new CStringInput(LOCALE_STREAMINGMENU_STREAMING_VIDEORATE, g_settings.streaming_videorate, 5, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2,"0123456789 "); + CStringInput * streamingSettings_audiorate = new CStringInput(LOCALE_STREAMINGMENU_STREAMING_AUDIORATE, g_settings.streaming_audiorate, 5, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2,"0123456789 "); + CStringInputSMS * startdirInput = new CStringInputSMS(LOCALE_STREAMINGMENU_STREAMING_SERVER_STARTDIR, g_settings.streaming_server_startdir, 30, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE,"abcdefghijklmnopqrstuvwxyz0123456789!""§$%&/()=?-:\\ "); + + CMenuForwarder* mf1 = new CMenuForwarder(LOCALE_STREAMINGMENU_SERVER_IP , (g_settings.streaming_type==1), g_settings.streaming_server_ip , streamingSettings_server_ip); + CMenuForwarder* mf2 = new CMenuForwarder(LOCALE_STREAMINGMENU_SERVER_PORT , (g_settings.streaming_type==1), g_settings.streaming_server_port , streamingSettings_server_port); + CMenuForwarder* mf3 = new CMenuForwarder(LOCALE_STREAMINGMENU_STREAMING_SERVER_CDDRIVE , (g_settings.streaming_type==1), g_settings.streaming_server_cddrive , cddriveInput); + CMenuForwarder* mf4 = new CMenuForwarder(LOCALE_STREAMINGMENU_STREAMING_VIDEORATE , (g_settings.streaming_type==1), g_settings.streaming_videorate , streamingSettings_videorate); + CMenuForwarder* mf5 = new CMenuForwarder(LOCALE_STREAMINGMENU_STREAMING_AUDIORATE , (g_settings.streaming_type==1), g_settings.streaming_audiorate , streamingSettings_audiorate); + CMenuForwarder* mf6 = new CMenuForwarder(LOCALE_STREAMINGMENU_STREAMING_SERVER_STARTDIR, (g_settings.streaming_type==1), g_settings.streaming_server_startdir, startdirInput); + CMenuForwarder* mf7 = new CMenuForwarder(LOCALE_MOVIEPLAYER_DEFDIR, true, g_settings.network_nfs_moviedir,this,"moviedir"); + CMenuForwarder* mf8 = new CMenuForwarder(LOCALE_MOVIEPLAYER_DEFPLUGIN, true, g_settings.movieplayer_plugin,this,"movieplugin"); + CMenuOptionChooser* oj1 = new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_TRANSCODE_AUDIO , &g_settings.streaming_transcode_audio , MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true); + + CMenuOptionChooser* oj2 = new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_FORCE_AVI_RAWAUDIO , &g_settings.streaming_force_avi_rawaudio , MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true); + + CMenuOptionChooser* oj3 = new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_FORCE_TRANSCODE_VIDEO, &g_settings.streaming_force_transcode_video, MESSAGEBOX_NO_YES_OPTIONS, MESSAGEBOX_NO_YES_OPTION_COUNT, true); + +// not yet supported by VLC + CMenuOptionChooser* oj4 = new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC, &g_settings.streaming_transcode_video_codec, STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC_OPTIONS, STREAMINGMENU_STREAMING_TRANSCODE_VIDEO_CODEC_OPTION_COUNT, true); + + CMenuOptionChooser* oj5 = new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_RESOLUTION , &g_settings.streaming_resolution , STREAMINGMENU_STREAMING_RESOLUTION_OPTIONS, STREAMINGMENU_STREAMING_RESOLUTION_OPTION_COUNT, true); + + CStreamingNotifier *StreamingNotifier = new CStreamingNotifier(mf1,mf2,mf3,mf4,mf5,mf6,oj1,oj2,oj3,oj4,oj5); + + streamingSettings.addItem(new CMenuOptionChooser(LOCALE_STREAMINGMENU_STREAMING_TYPE , &g_settings.streaming_type , STREAMINGMENU_STREAMING_TYPE_OPTIONS, STREAMINGMENU_STREAMING_TYPE_OPTION_COUNT, true, StreamingNotifier)); + streamingSettings.addItem(GenericMenuSeparatorLine); + streamingSettings.addItem( mf1); //Server IP + streamingSettings.addItem( mf2); //Server Port + streamingSettings.addItem( mf3); //CD-Drive + streamingSettings.addItem( mf6); //Startdir + streamingSettings.addItem(GenericMenuSeparatorLine); + streamingSettings.addItem( mf4); //Video-Rate + streamingSettings.addItem( oj3); + streamingSettings.addItem( oj4); + streamingSettings.addItem( oj5); + streamingSettings.addItem(GenericMenuSeparatorLine); + streamingSettings.addItem( mf5); //Audiorate + streamingSettings.addItem( oj1); + streamingSettings.addItem( oj2); + streamingSettings.addItem(GenericMenuSeparatorLine); + streamingSettings.addItem( mf7); //default dir + streamingSettings.addItem( mf8); //default movieplugin +} + + +class CMenuNumberInput : public CMenuForwarder, CMenuTarget, CChangeObserver +{ +private: + CChangeObserver * observer; + CConfigFile * configfile; + int32_t defaultvalue; + char value[11]; + +protected: + + virtual const char * getOption(void) + { + sprintf(value, "%u", configfile->getInt32(locale_real_names[text], defaultvalue)); + return value; + } + + virtual bool changeNotify(const neutrino_locale_t OptionName, void * Data) + { + configfile->setInt32(locale_real_names[text], atoi(value)); + return observer->changeNotify(OptionName, Data); + } + + +public: + CMenuNumberInput(const neutrino_locale_t Text, const int32_t DefaultValue, CChangeObserver * const _observer, CConfigFile * const _configfile) : CMenuForwarder(Text, true, NULL, this) + { + observer = _observer; + configfile = _configfile; + defaultvalue = DefaultValue; + } + + int exec(CMenuTarget * parent, const std::string & actionKey) + { + CStringInput input(text, (char *)getOption(), 3, LOCALE_IPSETUP_HINT_1, LOCALE_IPSETUP_HINT_2, "0123456789 ", this); + return input.exec(parent, actionKey); + } +}; + +const SNeutrinoSettings::FONT_TYPES channellist_font_sizes[4] = +{ + SNeutrinoSettings::FONT_TYPE_CHANNELLIST, + SNeutrinoSettings::FONT_TYPE_CHANNELLIST_DESCR, + SNeutrinoSettings::FONT_TYPE_CHANNELLIST_NUMBER, + SNeutrinoSettings::FONT_TYPE_CHANNEL_NUM_ZAP +}; + +const SNeutrinoSettings::FONT_TYPES eventlist_font_sizes[4] = +{ + SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE, + SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMLARGE, + SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMSMALL, + SNeutrinoSettings::FONT_TYPE_EVENTLIST_DATETIME, +}; + +const SNeutrinoSettings::FONT_TYPES infobar_font_sizes[4] = +{ + SNeutrinoSettings::FONT_TYPE_INFOBAR_NUMBER, + SNeutrinoSettings::FONT_TYPE_INFOBAR_CHANNAME, + SNeutrinoSettings::FONT_TYPE_INFOBAR_INFO, + SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL +}; + +const SNeutrinoSettings::FONT_TYPES epg_font_sizes[4] = +{ + SNeutrinoSettings::FONT_TYPE_EPG_TITLE, + SNeutrinoSettings::FONT_TYPE_EPG_INFO1, + SNeutrinoSettings::FONT_TYPE_EPG_INFO2, + SNeutrinoSettings::FONT_TYPE_EPG_DATE +}; + +const SNeutrinoSettings::FONT_TYPES gamelist_font_sizes[2] = +{ + SNeutrinoSettings::FONT_TYPE_GAMELIST_ITEMLARGE, + SNeutrinoSettings::FONT_TYPE_GAMELIST_ITEMSMALL +}; + +const SNeutrinoSettings::FONT_TYPES other_font_sizes[4] = +{ + SNeutrinoSettings::FONT_TYPE_MENU, + SNeutrinoSettings::FONT_TYPE_MENU_TITLE, + SNeutrinoSettings::FONT_TYPE_MENU_INFO, + SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM +}; + +font_sizes_groups font_sizes_groups[6] = +{ + {LOCALE_FONTMENU_CHANNELLIST, 4, channellist_font_sizes, "fontsize.dcha"}, + {LOCALE_FONTMENU_EVENTLIST , 4, eventlist_font_sizes , "fontsize.deve"}, + {LOCALE_FONTMENU_EPG , 4, epg_font_sizes , "fontsize.depg"}, + {LOCALE_FONTMENU_INFOBAR , 4, infobar_font_sizes , "fontsize.dinf"}, + {LOCALE_FONTMENU_GAMELIST , 2, gamelist_font_sizes , "fontsize.dgam"}, + {NONEXISTANT_LOCALE , 4, other_font_sizes , "fontsize.doth"} +}; + +#define FONT_STYLE_REGULAR 0 +#define FONT_STYLE_BOLD 1 +#define FONT_STYLE_ITALIC 2 + +font_sizes_struct neutrino_font[FONT_TYPE_COUNT] = +{ + {LOCALE_FONTSIZE_MENU , 20, FONT_STYLE_BOLD , 0}, + {LOCALE_FONTSIZE_MENU_TITLE , 30, FONT_STYLE_BOLD , 0}, + {LOCALE_FONTSIZE_MENU_INFO , 16, FONT_STYLE_REGULAR, 0}, + {LOCALE_FONTSIZE_EPG_TITLE , 25, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_EPG_INFO1 , 17, FONT_STYLE_ITALIC , 2}, + {LOCALE_FONTSIZE_EPG_INFO2 , 17, FONT_STYLE_REGULAR, 2}, + {LOCALE_FONTSIZE_EPG_DATE , 15, FONT_STYLE_REGULAR, 2}, + {LOCALE_FONTSIZE_EVENTLIST_TITLE , 30, FONT_STYLE_REGULAR, 0}, + {LOCALE_FONTSIZE_EVENTLIST_ITEMLARGE, 20, FONT_STYLE_BOLD , 1}, + {LOCALE_FONTSIZE_EVENTLIST_ITEMSMALL, 14, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_EVENTLIST_DATETIME , 16, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_GAMELIST_ITEMLARGE , 20, FONT_STYLE_BOLD , 1}, + {LOCALE_FONTSIZE_GAMELIST_ITEMSMALL , 16, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_CHANNELLIST , 20, FONT_STYLE_BOLD , 1}, + {LOCALE_FONTSIZE_CHANNELLIST_DESCR , 20, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_CHANNELLIST_NUMBER , 14, FONT_STYLE_BOLD , 2}, + {LOCALE_FONTSIZE_CHANNEL_NUM_ZAP , 40, FONT_STYLE_BOLD , 0}, + {LOCALE_FONTSIZE_INFOBAR_NUMBER , 30 /* orig 50*/, FONT_STYLE_BOLD , 0}, + {LOCALE_FONTSIZE_INFOBAR_CHANNAME , 30, FONT_STYLE_BOLD , 0}, + {LOCALE_FONTSIZE_INFOBAR_INFO , 20, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_INFOBAR_SMALL , 14, FONT_STYLE_REGULAR, 1}, + {LOCALE_FONTSIZE_FILEBROWSER_ITEM , 16, FONT_STYLE_BOLD , 1} +}; + +void CNeutrinoApp::AddFontSettingItem(CMenuWidget &fontSettings, const SNeutrinoSettings::FONT_TYPES number_of_fontsize_entry) +{ + fontSettings.addItem(new CMenuNumberInput(neutrino_font[number_of_fontsize_entry].name, neutrino_font[number_of_fontsize_entry].defaultsize, &fontsizenotifier, &configfile)); +} + +void CNeutrinoApp::InitFontSettings(CMenuWidget &fontSettings) +{ + fontSettings.addItem(GenericMenuSeparator); + fontSettings.addItem(GenericMenuBack); + fontSettings.addItem(GenericMenuSeparatorLine); + //fontSettings.addItem( new CMenuForwarder(LOCALE_EPGPLUS_SELECT_FONT_NAME, true, NULL, this, "select_font")); + AddFontSettingItem(fontSettings, SNeutrinoSettings::FONT_TYPE_MENU); + AddFontSettingItem(fontSettings, SNeutrinoSettings::FONT_TYPE_MENU_TITLE); + AddFontSettingItem(fontSettings, SNeutrinoSettings::FONT_TYPE_MENU_INFO); + fontSettings.addItem(GenericMenuSeparatorLine); + + for (int i = 0; i < 5; i++) + { + CMenuWidget * fontSettingsSubMenu = new CMenuWidget(font_sizes_groups[i].groupname, "colors.raw"); + fontSettingsSubMenu->addItem(GenericMenuSeparator); + fontSettingsSubMenu->addItem(GenericMenuBack); + fontSettingsSubMenu->addItem(GenericMenuSeparatorLine); + for (unsigned int j = 0; j < font_sizes_groups[i].count; j++) + { + AddFontSettingItem(*fontSettingsSubMenu, font_sizes_groups[i].content[j]); + } + fontSettingsSubMenu->addItem(GenericMenuSeparatorLine); + fontSettingsSubMenu->addItem(new CMenuForwarder(LOCALE_OPTIONS_DEFAULT, true, NULL, this, font_sizes_groups[i].actionkey)); + + fontSettings.addItem(new CMenuForwarder(font_sizes_groups[i].groupname, true, NULL, fontSettingsSubMenu)); + } + + AddFontSettingItem(fontSettings, SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM); + fontSettings.addItem(GenericMenuSeparatorLine); + fontSettings.addItem(new CMenuForwarder(LOCALE_OPTIONS_DEFAULT, true, NULL, this, font_sizes_groups[5].actionkey)); +} + +void CNeutrinoApp::InitColorSettings(CMenuWidget &colorSettings, CMenuWidget &fontSettings ) +{ + CScreenSetup * ScreenSetup = new CScreenSetup(); + colorSettings.addItem(GenericMenuSeparator); + colorSettings.addItem(GenericMenuBack); + colorSettings.addItem(GenericMenuSeparatorLine); + + CMenuWidget *colorSettings_Themes = new CMenuWidget(LOCALE_COLORTHEMEMENU_HEAD, NEUTRINO_ICON_SETTINGS); + InitColorThemesSettings(*colorSettings_Themes); + + colorSettings.addItem( new CMenuForwarder(LOCALE_COLORMENU_THEMESELECT, true, NULL, colorSettings_Themes, NULL, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED) ); + CMenuWidget *colorSettings_menuColors = new CMenuWidget(LOCALE_COLORMENUSETUP_HEAD, NEUTRINO_ICON_SETTINGS, 400, 400); + InitColorSettingsMenuColors(*colorSettings_menuColors); + colorSettings.addItem( new CMenuForwarder(LOCALE_COLORMENU_MENUCOLORS, true, NULL, colorSettings_menuColors, NULL, CRCInput::RC_green, NEUTRINO_ICON_BUTTON_GREEN) ); + + CMenuWidget *colorSettings_statusbarColors = new CMenuWidget(LOCALE_COLORMENU_STATUSBAR, NEUTRINO_ICON_SETTINGS); + InitColorSettingsStatusBarColors(*colorSettings_statusbarColors); + colorSettings.addItem( new CMenuForwarder(LOCALE_COLORSTATUSBAR_HEAD, true, NULL, colorSettings_statusbarColors, NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW) ); + + colorSettings.addItem(GenericMenuSeparatorLine); + colorSettings.addItem( new CMenuForwarder(LOCALE_COLORMENU_FONT, true, NULL, &fontSettings, NULL, CRCInput::RC_blue, NEUTRINO_ICON_BUTTON_BLUE) ); + colorSettings.addItem( new CMenuForwarder(LOCALE_EPGPLUS_SELECT_FONT_NAME, true, NULL, this, "select_font")); + + CMenuWidget *colorSettings_timing = new CMenuWidget(LOCALE_COLORMENU_TIMING, NEUTRINO_ICON_SETTINGS); + InitColorSettingsTiming(*colorSettings_timing); + + colorSettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_VIDEOMENU_OSD)); + colorSettings.addItem(new CMenuForwarder(LOCALE_TIMING_HEAD, true, NULL, colorSettings_timing)); + colorSettings.addItem(new CMenuForwarder(LOCALE_VIDEOMENU_SCREENSETUP, true, NULL, ScreenSetup));//, NULL, CRCInput::RC_red, NEUTRINO_ICON_BUTTON_RED)); +#if 0 + colorSettings.addItem(GenericMenuSeparatorLine); + //menuefaden nur bei enx-chips! + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_COLORMENU_FADE, &g_settings.widget_fade, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true ); + colorSettings.addItem( oj ); + + CAlphaSetup* chAlphaSetup = new CAlphaSetup(LOCALE_COLORMENU_GTX_ALPHA, &g_settings.gtx_alpha1, &g_settings.gtx_alpha2); + colorSettings.addItem( new CMenuForwarder(LOCALE_COLORMENU_GTX_ALPHA, true, NULL, chAlphaSetup)); +#endif +} + +void CNeutrinoApp::InitColorThemesSettings(CMenuWidget &colorSettings_Themes) +{ + colorSettings_Themes.addItem(GenericMenuSeparator); + colorSettings_Themes.addItem(GenericMenuBack); + colorSettings_Themes.addItem(GenericMenuSeparatorLine); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_EXTRA_LOADCOLORS, true, NULL, this, "loadcolors")); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_EXTRA_SAVECOLORS, true, NULL, this, "savecolors")); + colorSettings_Themes.addItem(GenericMenuSeparatorLine); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_COLORTHEMEMENU_NEUTRINO_THEME, true, NULL, this, "theme_neutrino")); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_COLORTHEMEMENU_CLASSIC_THEME, true, NULL, this, "theme_classic")); + colorSettings_Themes.addItem( new CMenuForwarder(LOCALE_COLORTHEMEMENU_GRAY, true, NULL, this, "theme_ru") ); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_COLORTHEMEMENU_DBLUE_THEME, true, NULL, this, "theme_dblue")); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_COLORTHEMEMENU_DVB2K_THEME, true, NULL, this, "theme_dvb2k")); + colorSettings_Themes.addItem(new CMenuForwarder(LOCALE_COLORTHEMEMENU_RED_THEME, true, NULL, this, "theme_red")); +} + +void CNeutrinoApp::InitColorSettingsMenuColors(CMenuWidget &colorSettings_menuColors) +{ + colorSettings_menuColors.addItem(GenericMenuSeparator); + colorSettings_menuColors.addItem(GenericMenuBack); + + CColorChooser* chHeadcolor = new CColorChooser(LOCALE_COLORMENU_BACKGROUND, &g_settings.menu_Head_red, &g_settings.menu_Head_green, &g_settings.menu_Head_blue, + &g_settings.menu_Head_alpha, colorSetupNotifier); + CColorChooser* chHeadTextcolor = new CColorChooser(LOCALE_COLORMENU_TEXTCOLOR, &g_settings.menu_Head_Text_red, &g_settings.menu_Head_Text_green, &g_settings.menu_Head_Text_blue, + NULL, colorSetupNotifier); + CColorChooser* chContentcolor = new CColorChooser(LOCALE_COLORMENU_BACKGROUND, &g_settings.menu_Content_red, &g_settings.menu_Content_green, &g_settings.menu_Content_blue, + &g_settings.menu_Content_alpha, colorSetupNotifier); + CColorChooser* chContentTextcolor = new CColorChooser(LOCALE_COLORMENU_TEXTCOLOR, &g_settings.menu_Content_Text_red, &g_settings.menu_Content_Text_green, &g_settings.menu_Content_Text_blue, + NULL, colorSetupNotifier); + CColorChooser* chContentSelectedcolor = new CColorChooser(LOCALE_COLORMENU_BACKGROUND, &g_settings.menu_Content_Selected_red, &g_settings.menu_Content_Selected_green, &g_settings.menu_Content_Selected_blue, + &g_settings.menu_Content_Selected_alpha, colorSetupNotifier); + CColorChooser* chContentSelectedTextcolor = new CColorChooser(LOCALE_COLORMENU_TEXTCOLOR, &g_settings.menu_Content_Selected_Text_red, &g_settings.menu_Content_Selected_Text_green, &g_settings.menu_Content_Selected_Text_blue, + NULL, colorSetupNotifier); + CColorChooser* chContentInactivecolor = new CColorChooser(LOCALE_COLORMENU_BACKGROUND, &g_settings.menu_Content_inactive_red, &g_settings.menu_Content_inactive_green, &g_settings.menu_Content_inactive_blue, + &g_settings.menu_Content_inactive_alpha, colorSetupNotifier); + CColorChooser* chContentInactiveTextcolor = new CColorChooser(LOCALE_COLORMENU_TEXTCOLOR, &g_settings.menu_Content_inactive_Text_red, &g_settings.menu_Content_inactive_Text_green, &g_settings.menu_Content_inactive_Text_blue, + NULL, colorSetupNotifier); + colorSettings_menuColors.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_COLORMENUSETUP_MENUHEAD)); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_BACKGROUND, true, NULL, chHeadcolor )); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_TEXTCOLOR, true, NULL, chHeadTextcolor )); + colorSettings_menuColors.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_COLORMENUSETUP_MENUCONTENT)); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_BACKGROUND, true, NULL, chContentcolor )); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_TEXTCOLOR, true, NULL, chContentTextcolor )); + colorSettings_menuColors.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_COLORMENUSETUP_MENUCONTENT_INACTIVE)); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_BACKGROUND, true, NULL, chContentInactivecolor )); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_TEXTCOLOR, true, NULL, chContentInactiveTextcolor)); + colorSettings_menuColors.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_COLORMENUSETUP_MENUCONTENT_SELECTED)); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_BACKGROUND, true, NULL, chContentSelectedcolor )); + colorSettings_menuColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_TEXTCOLOR, true, NULL, chContentSelectedTextcolor )); +} + +void CNeutrinoApp::InitColorSettingsStatusBarColors(CMenuWidget &colorSettings_statusbarColors) +{ + colorSettings_statusbarColors.addItem(GenericMenuSeparator); + + colorSettings_statusbarColors.addItem(GenericMenuBack); + + CColorChooser* chInfobarcolor = new CColorChooser(LOCALE_COLORMENU_BACKGROUND, &g_settings.infobar_red, &g_settings.infobar_green, &g_settings.infobar_blue, + &g_settings.infobar_alpha, colorSetupNotifier); + CColorChooser* chInfobarTextcolor = new CColorChooser(LOCALE_COLORMENU_TEXTCOLOR, &g_settings.infobar_Text_red, &g_settings.infobar_Text_green, &g_settings.infobar_Text_blue, + NULL, colorSetupNotifier); + + colorSettings_statusbarColors.addItem( new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_COLORSTATUSBAR_TEXT)); + colorSettings_statusbarColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_BACKGROUND, true, NULL, chInfobarcolor )); + colorSettings_statusbarColors.addItem( new CMenuForwarder(LOCALE_COLORMENU_TEXTCOLOR, true, NULL, chInfobarTextcolor )); +} + +void CNeutrinoApp::InitColorSettingsTiming(CMenuWidget &colorSettings_timing) +{ + colorSettings_timing.addItem(GenericMenuSeparator); + colorSettings_timing.addItem(GenericMenuBack); + colorSettings_timing.addItem(GenericMenuSeparatorLine); + + for (int i = 0; i < TIMING_SETTING_COUNT; i++) + { + CStringInput * colorSettings_timing_item = new CStringInput(timing_setting_name[i], g_settings.timing_string[i], 3, LOCALE_TIMING_HINT_1, LOCALE_TIMING_HINT_2, "0123456789 ", &timingsettingsnotifier); + colorSettings_timing.addItem(new CMenuForwarder(timing_setting_name[i], true, g_settings.timing_string[i], colorSettings_timing_item)); + } + + colorSettings_timing.addItem(GenericMenuSeparatorLine); + colorSettings_timing.addItem(new CMenuForwarder(LOCALE_OPTIONS_DEFAULT, true, NULL, this, "osd.def")); +} + +#define LCDMENU_STATUSLINE_OPTION_COUNT 2 +const CMenuOptionChooser::keyval LCDMENU_STATUSLINE_OPTIONS[LCDMENU_STATUSLINE_OPTION_COUNT] = +{ + { 0, LOCALE_LCDMENU_STATUSLINE_PLAYTIME }, + { 1, LOCALE_LCDMENU_STATUSLINE_VOLUME } + //,{ 2, LOCALE_LCDMENU_STATUSLINE_BOTH } +}; + +void CNeutrinoApp::InitLcdSettings(CMenuWidget &lcdSettings) +{ + lcdSettings.addItem(GenericMenuSeparator); + + lcdSettings.addItem(GenericMenuBack); + lcdSettings.addItem(GenericMenuSeparatorLine); + + CVfdControler* lcdsliders = new CVfdControler(LOCALE_LCDMENU_HEAD, NULL); + +#if 0 + CLcdNotifier* lcdnotifier = new CLcdNotifier(); + + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_LCDMENU_INVERSE, &g_settings.lcd_setting[SNeutrinoSettings::LCD_INVERSE], OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, lcdnotifier); + lcdSettings.addItem(oj); + + oj = new CMenuOptionChooser(LOCALE_LCDMENU_POWER, &g_settings.lcd_setting[SNeutrinoSettings::LCD_POWER], OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, lcdnotifier); + lcdSettings.addItem(oj); +#endif + CStringInput * dim_time = new CStringInput(LOCALE_LCDMENU_DIM_TIME, g_settings.lcd_setting_dim_time, 3, + NONEXISTANT_LOCALE, NONEXISTANT_LOCALE,"0123456789 "); + lcdSettings.addItem(new CMenuForwarder(LOCALE_LCDMENU_DIM_TIME,true, g_settings.lcd_setting_dim_time,dim_time)); + + CStringInput * dim_brightness = new CStringInput(LOCALE_LCDMENU_DIM_BRIGHTNESS, g_settings.lcd_setting_dim_brightness, 3, + NONEXISTANT_LOCALE, NONEXISTANT_LOCALE,"0123456789 "); + lcdSettings.addItem(new CMenuForwarder(LOCALE_LCDMENU_DIM_BRIGHTNESS,true, g_settings.lcd_setting_dim_brightness,dim_brightness)); + + lcdSettings.addItem(GenericMenuSeparatorLine); + lcdSettings.addItem(new CMenuForwarder(LOCALE_LCDMENU_LCDCONTROLER, true, NULL, lcdsliders)); + + lcdSettings.addItem(GenericMenuSeparatorLine); + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_LCDMENU_STATUSLINE, &g_settings.lcd_setting[SNeutrinoSettings::LCD_SHOW_VOLUME], LCDMENU_STATUSLINE_OPTIONS, LCDMENU_STATUSLINE_OPTION_COUNT, true); + lcdSettings.addItem(oj); +} + +#define KEYBINDINGMENU_BOUQUETHANDLING_OPTION_COUNT 3 +const CMenuOptionChooser::keyval KEYBINDINGMENU_BOUQUETHANDLING_OPTIONS[KEYBINDINGMENU_BOUQUETHANDLING_OPTION_COUNT] = +{ + { 0, LOCALE_KEYBINDINGMENU_BOUQUETCHANNELS_ON_OK }, + { 1, LOCALE_KEYBINDINGMENU_BOUQUETLIST_ON_OK }, + { 2, LOCALE_KEYBINDINGMENU_ALLCHANNELS_ON_OK } +}; + +enum keynames { + KEY_TV_RADIO_MODE, + KEY_PAGE_UP, + KEY_PAGE_DOWN, + KEY_LIST_START, + KEY_LIST_END, + KEY_CANCEL_ACTION, + KEY_SORT, + KEY_ADD_RECORD, + KEY_ADD_REMIND, + KEY_BOUQUET_UP, + KEY_BOUQUET_DOWN, + KEY_CHANNEL_UP, + KEY_CHANNEL_DOWN, + KEY_SUBCHANNEL_UP, + KEY_SUBCHANNEL_DOWN, + KEY_ZAP_HISTORY, + KEY_LASTCHANNEL, + MPKEY_REWIND, + MPKEY_FORWARD, + MPKEY_PAUSE, + MPKEY_STOP, + MPKEY_PLAY, + MPKEY_AUDIO, + MPKEY_TIME, + MPKEY_BOOKMARK, + KEY_TIMESHIFT, + MPKEY_PLUGIN, + KEY_PLUGIN, + KEY_UNLOCK +}; + +#define KEYBINDS_COUNT 29 +const neutrino_locale_t keydescription_head[KEYBINDS_COUNT] = +{ + LOCALE_KEYBINDINGMENU_TVRADIOMODE, + LOCALE_KEYBINDINGMENU_PAGEUP, + LOCALE_KEYBINDINGMENU_PAGEDOWN, + LOCALE_EXTRA_KEY_LIST_START, + LOCALE_EXTRA_KEY_LIST_END, + LOCALE_KEYBINDINGMENU_CANCEL, + LOCALE_KEYBINDINGMENU_SORT, + LOCALE_KEYBINDINGMENU_ADDRECORD, + LOCALE_KEYBINDINGMENU_ADDREMIND, + LOCALE_KEYBINDINGMENU_BOUQUETUP, + LOCALE_KEYBINDINGMENU_BOUQUETDOWN, + LOCALE_KEYBINDINGMENU_CHANNELUP, + LOCALE_KEYBINDINGMENU_CHANNELDOWN, + LOCALE_KEYBINDINGMENU_SUBCHANNELUP, + LOCALE_KEYBINDINGMENU_SUBCHANNELDOWN, + LOCALE_KEYBINDINGMENU_ZAPHISTORY, + LOCALE_KEYBINDINGMENU_LASTCHANNEL, + LOCALE_MPKEY_REWIND, + LOCALE_MPKEY_FORWARD, + LOCALE_MPKEY_PAUSE, + LOCALE_MPKEY_STOP, + LOCALE_MPKEY_PLAY, + LOCALE_MPKEY_AUDIO, + LOCALE_MPKEY_TIME, + LOCALE_MPKEY_BOOKMARK, + LOCALE_EXTRA_KEY_TIMESHIFT, + LOCALE_MPKEY_PLUGIN, + LOCALE_EXTRA_KEY_PLUGIN, + LOCALE_EXTRA_KEY_UNLOCK +}; + +const neutrino_locale_t keydescription[KEYBINDS_COUNT] = +{ + LOCALE_KEYBINDINGMENU_TVRADIOMODE, + LOCALE_KEYBINDINGMENU_PAGEUP, + LOCALE_KEYBINDINGMENU_PAGEDOWN, + LOCALE_EXTRA_KEY_LIST_START, + LOCALE_EXTRA_KEY_LIST_END, + LOCALE_KEYBINDINGMENU_CANCEL, + LOCALE_KEYBINDINGMENU_SORT, + LOCALE_KEYBINDINGMENU_ADDRECORD, + LOCALE_KEYBINDINGMENU_ADDREMIND, + LOCALE_KEYBINDINGMENU_BOUQUETUP, + LOCALE_KEYBINDINGMENU_BOUQUETDOWN, + LOCALE_KEYBINDINGMENU_CHANNELUP, + LOCALE_KEYBINDINGMENU_CHANNELDOWN, + LOCALE_KEYBINDINGMENU_SUBCHANNELUP, + LOCALE_KEYBINDINGMENU_SUBCHANNELDOWN, + LOCALE_KEYBINDINGMENU_ZAPHISTORY, + LOCALE_KEYBINDINGMENU_LASTCHANNEL, + LOCALE_MPKEY_REWIND, + LOCALE_MPKEY_FORWARD, + LOCALE_MPKEY_PAUSE, + LOCALE_MPKEY_STOP, + LOCALE_MPKEY_PLAY, + LOCALE_MPKEY_AUDIO, + LOCALE_MPKEY_TIME, + LOCALE_MPKEY_BOOKMARK, + LOCALE_EXTRA_KEY_TIMESHIFT, + LOCALE_MPKEY_PLUGIN, + LOCALE_EXTRA_KEY_PLUGIN, + LOCALE_EXTRA_KEY_UNLOCK +}; + +void CNeutrinoApp::InitKeySettings(CMenuWidget &keySettings) +{ + CMenuWidget* bindSettings = new CMenuWidget(LOCALE_KEYBINDINGMENU_HEAD, "keybinding.raw", 400); + + keySettings.addItem(GenericMenuSeparator); + keySettings.addItem(GenericMenuBack); + keySettings.addItem(GenericMenuSeparatorLine); + + keySettings.addItem(new CMenuForwarder(LOCALE_EXTRA_LOADKEYS, true, NULL, this, "loadkeys")); + keySettings.addItem(new CMenuForwarder(LOCALE_EXTRA_SAVEKEYS, true, NULL, this, "savekeys")); + //keySettings.addItem(GenericMenuSeparatorLine); + + keySetupNotifier = new CKeySetupNotifier; + CStringInput * keySettings_repeat_genericblocker = new CStringInput(LOCALE_KEYBINDINGMENU_REPEATBLOCKGENERIC, g_settings.repeat_genericblocker, 3, LOCALE_REPEATBLOCKER_HINT_1, LOCALE_REPEATBLOCKER_HINT_2, "0123456789 ", keySetupNotifier); + CStringInput * keySettings_repeatBlocker = new CStringInput(LOCALE_KEYBINDINGMENU_REPEATBLOCK, g_settings.repeat_blocker, 3, LOCALE_REPEATBLOCKER_HINT_1, LOCALE_REPEATBLOCKER_HINT_2, "0123456789 ", keySetupNotifier); + keySetupNotifier->changeNotify(NONEXISTANT_LOCALE, NULL); + + keySettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_RC)); + keySettings.addItem(new CMenuForwarder(LOCALE_KEYBINDINGMENU_REPEATBLOCK, true, g_settings.repeat_blocker, keySettings_repeatBlocker)); + keySettings.addItem(new CMenuForwarder(LOCALE_KEYBINDINGMENU_REPEATBLOCKGENERIC, true, g_settings.repeat_genericblocker, keySettings_repeat_genericblocker)); + + keySettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MAINMENU_SHUTDOWN)); + CMenuOptionChooser *m1 = new CMenuOptionChooser(LOCALE_MISCSETTINGS_SHUTDOWN_REAL_RCDELAY, &g_settings.shutdown_real_rcdelay, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, !g_settings.shutdown_real); + + CMiscNotifier* miscNotifier = new CMiscNotifier( m1 ); + + keySettings.addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_SHUTDOWN_REAL, &g_settings.shutdown_real, OPTIONS_OFF1_ON0_OPTIONS, OPTIONS_OFF1_ON0_OPTION_COUNT, true, miscNotifier)); + keySettings.addItem(m1); + + int * keyvalue_p[KEYBINDS_COUNT] = + { + &g_settings.key_tvradio_mode, + &g_settings.key_channelList_pageup, + &g_settings.key_channelList_pagedown, + &g_settings.key_list_start, + &g_settings.key_list_end, + + &g_settings.key_channelList_cancel, + &g_settings.key_channelList_sort, + &g_settings.key_channelList_addrecord, + &g_settings.key_channelList_addremind, + &g_settings.key_bouquet_up, + + &g_settings.key_bouquet_down, + &g_settings.key_quickzap_up, + &g_settings.key_quickzap_down, + &g_settings.key_subchannel_up, + &g_settings.key_subchannel_down, + + &g_settings.key_zaphistory, + &g_settings.key_lastchannel, + + &g_settings.mpkey_rewind, + &g_settings.mpkey_forward, + &g_settings.mpkey_pause, + &g_settings.mpkey_stop, + &g_settings.mpkey_play, + &g_settings.mpkey_audio, + &g_settings.mpkey_time, + &g_settings.mpkey_bookmark, + + &g_settings.key_timeshift, + &g_settings.mpkey_plugin, + &g_settings.key_plugin, + &g_settings.key_unlock + }; + + CKeyChooser * keychooser[KEYBINDS_COUNT]; + + for (int i = 0; i < KEYBINDS_COUNT; i++) + keychooser[i] = new CKeyChooser(keyvalue_p[i], keydescription_head[i], NEUTRINO_ICON_SETTINGS); + + keySettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_HEAD)); + + bindSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_MODECHANGE)); + bindSettings->addItem(new CMenuForwarder(keydescription[KEY_TV_RADIO_MODE], true, NULL, keychooser[KEY_TV_RADIO_MODE])); + + bindSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_CHANNELLIST)); + + CMenuOptionChooser *oj = new CMenuOptionChooser(LOCALE_KEYBINDINGMENU_BOUQUETHANDLING, &g_settings.bouquetlist_mode, KEYBINDINGMENU_BOUQUETHANDLING_OPTIONS, KEYBINDINGMENU_BOUQUETHANDLING_OPTION_COUNT, true ); + bindSettings->addItem(oj); + + for (int i = KEY_PAGE_UP; i <= KEY_BOUQUET_DOWN; i++) + bindSettings->addItem(new CMenuForwarder(keydescription[i], true, NULL, keychooser[i])); + + bindSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_SMS_CHANNEL, &g_settings.sms_channel, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); +// Zapping keys + bindSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_KEYBINDINGMENU_QUICKZAP)); + + for (int i = KEY_CHANNEL_UP; i <= KEY_LASTCHANNEL; i++) + bindSettings->addItem(new CMenuForwarder(keydescription[i], true, NULL, keychooser[i])); + + bindSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MAINMENU_MOVIEPLAYER)); + for (int i = MPKEY_REWIND; i <= MPKEY_PLUGIN; i++) + bindSettings->addItem(new CMenuForwarder(keydescription[i], true, NULL, keychooser[i])); + + bindSettings->addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_MAINSETTINGS_MISC)); + //bindSettings->addItem(new CMenuForwarder(keydescription[KEY_PLUGIN], true, NULL, keychooser[KEY_PLUGIN])); + bindSettings->addItem(new CMenuForwarder(keydescription[KEY_UNLOCK], true, NULL, keychooser[KEY_UNLOCK])); + //bindSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_ZAP_CYCLE, &g_settings.zap_cycle, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + bindSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_MENU_LEFT_EXIT, &g_settings.menu_left_exit, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + bindSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_AUDIO_RUN_PLAYER, &g_settings.audio_run_player, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + //bindSettings->addItem(new CMenuOptionChooser(LOCALE_EXTRA_KEY_CLICK, &g_settings.key_click, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + + keySettings.addItem(new CMenuForwarder(LOCALE_KEYBINDINGMENU_HEAD, true, NULL, bindSettings)); + //mainSettings.addItem(new CMenuForwarder(LOCALE_MAINSETTINGS_KEYBINDING, true, NULL, &keySettings , NULL, CRCInput::RC_blue , NEUTRINO_ICON_BUTTON_BLUE )); + + // USERMENU + keySettings.addItem(new CMenuSeparator(CMenuSeparator::LINE | CMenuSeparator::STRING, LOCALE_USERMENU_HEAD)); + keySettings.addItem(new CMenuForwarder(LOCALE_USERMENU_BUTTON_RED, true, NULL, new CUserMenuMenu(LOCALE_USERMENU_BUTTON_RED,0))); + keySettings.addItem(new CMenuForwarder(LOCALE_USERMENU_BUTTON_GREEN, true, NULL, new CUserMenuMenu(LOCALE_USERMENU_BUTTON_GREEN,1))); + keySettings.addItem(new CMenuForwarder(LOCALE_USERMENU_BUTTON_YELLOW, true, NULL, new CUserMenuMenu(LOCALE_USERMENU_BUTTON_YELLOW,2))); + keySettings.addItem(new CMenuForwarder(LOCALE_USERMENU_BUTTON_BLUE, true, NULL, new CUserMenuMenu(LOCALE_USERMENU_BUTTON_BLUE,3))); + + +} +// USERMENU +// leave this functions, somebody might want to use it in the future again +void CNeutrinoApp::SelectNVOD() +{ + if (!(g_RemoteControl->subChannels.empty())) + { + // NVOD/SubService- Kanal! + CMenuWidget NVODSelector(g_RemoteControl->are_subchannels ? LOCALE_NVODSELECTOR_SUBSERVICE : LOCALE_NVODSELECTOR_HEAD, "video.raw", 350); + if(getNVODMenu(&NVODSelector)) + NVODSelector.exec(NULL, ""); + } +} + +bool CNeutrinoApp::getNVODMenu(CMenuWidget* menu) +{ + if(menu == NULL) + return false; + if (g_RemoteControl->subChannels.empty()) + return false; + + menu->addItem(GenericMenuSeparator); + + int count = 0; + char nvod_id[5]; + + for( CSubServiceListSorted::iterator e=g_RemoteControl->subChannels.begin(); e!=g_RemoteControl->subChannels.end(); ++e) + { + sprintf(nvod_id, "%d", count); + + if( !g_RemoteControl->are_subchannels ) { + char nvod_time_a[50], nvod_time_e[50], nvod_time_x[50]; + char nvod_s[100]; + struct tm *tmZeit; + + tmZeit= localtime(&e->startzeit); + sprintf(nvod_time_a, "%02d:%02d", tmZeit->tm_hour, tmZeit->tm_min); + + time_t endtime = e->startzeit+ e->dauer; + tmZeit= localtime(&endtime); + sprintf(nvod_time_e, "%02d:%02d", tmZeit->tm_hour, tmZeit->tm_min); + + time_t jetzt=time(NULL); + if(e->startzeit > jetzt) { + int mins=(e->startzeit- jetzt)/ 60; + sprintf(nvod_time_x, g_Locale->getText(LOCALE_NVOD_STARTING), mins); + } + else if( (e->startzeit<= jetzt) && (jetzt < endtime) ) { + int proz=(jetzt- e->startzeit)*100/ e->dauer; + sprintf(nvod_time_x, g_Locale->getText(LOCALE_NVOD_PERCENTAGE), proz); + } + else + nvod_time_x[0]= 0; + + sprintf(nvod_s, "%s - %s %s", nvod_time_a, nvod_time_e, nvod_time_x); + menu->addItem(new CMenuForwarderNonLocalized(nvod_s, true, NULL, NVODChanger, nvod_id), (count == g_RemoteControl->selected_subchannel)); + } else { + menu->addItem(new CMenuForwarderNonLocalized(e->subservice_name.c_str(), true, NULL, NVODChanger, nvod_id, CRCInput::convertDigitToKey(count)), (count == g_RemoteControl->selected_subchannel)); + } + + count++; + } + + if( g_RemoteControl->are_subchannels ) { + menu->addItem(GenericMenuSeparatorLine); + CMenuOptionChooser* oj = new CMenuOptionChooser(LOCALE_NVODSELECTOR_DIRECTORMODE, &g_RemoteControl->director_mode, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, NULL, CRCInput::RC_yellow, NEUTRINO_ICON_BUTTON_YELLOW); + menu->addItem(oj); + } + return true; +} + +void CNeutrinoApp::SelectAPID() +{ +#if 0 + if( g_RemoteControl->current_PIDs.APIDs.size()> 1 ) + { + // wir haben APIDs für diesen Kanal! + + CMenuWidget APIDSelector(LOCALE_APIDSELECTOR_HEAD, "audio.raw", 300); + APIDSelector.addItem(GenericMenuSeparator); + + for( unsigned int count=0; countcurrent_PIDs.APIDs.size(); count++ ) + { + char apid[5]; + sprintf(apid, "%d", count); + APIDSelector.addItem(new CMenuForwarderNonLocalized(g_RemoteControl->current_PIDs.APIDs[count].desc, true, NULL, APIDChanger, apid, CRCInput::convertDigitToKey(count + 1)), (count == g_RemoteControl->current_PIDs.PIDs.selected_apid)); + } + APIDSelector.exec(NULL, ""); + } +#endif +} + +#define MAINMENU_RECORDING_OPTION_COUNT 2 +const CMenuOptionChooser::keyval MAINMENU_RECORDING_OPTIONS[MAINMENU_RECORDING_OPTION_COUNT] = +{ + { 0, LOCALE_MAINMENU_RECORDING_START }, + { 1, LOCALE_MAINMENU_RECORDING_STOP } +}; + +// USERMENU +// This is just a quick helper for the usermenu only. I already made it a class for future use. +#define BUTTONMAX 4 +const neutrino_msg_t key_helper_msg_def[BUTTONMAX]={CRCInput::RC_red,CRCInput::RC_green,CRCInput::RC_yellow,CRCInput::RC_blue}; +const char * key_helper_icon_def[BUTTONMAX]={NEUTRINO_ICON_BUTTON_RED,NEUTRINO_ICON_BUTTON_GREEN,NEUTRINO_ICON_BUTTON_YELLOW,NEUTRINO_ICON_BUTTON_BLUE}; +class CKeyHelper +{ + private: + int number_key; + bool color_key_used[BUTTONMAX]; + public: + CKeyHelper(){reset();}; + void reset(void) + { + number_key = 1; + for(int i= 0; i < BUTTONMAX; i++ ) + color_key_used[i] = false; + }; + + /* Returns the next available button, to be used in menu as 'direct' keys. Appropriate + * definitions are returnd in msp and icon + * A color button could be requested as prefered button (other buttons are not supported yet). + * If the appropriate button is already in used, the next number_key button is returned instead + * (first 1-9 and than 0). */ + bool get(neutrino_msg_t* msg, const char** icon, neutrino_msg_t prefered_key = CRCInput::RC_nokey) + { + bool result = false; + int button = -1; + if(prefered_key == CRCInput::RC_red) + button = 0; + if(prefered_key == CRCInput::RC_green) + button = 1; + if(prefered_key == CRCInput::RC_yellow) + button = 2; + if(prefered_key == CRCInput::RC_blue) + button = 3; + + *msg = CRCInput::RC_nokey; + *icon = ""; + if(button >= 0 && button < BUTTONMAX) + { // try to get color button + if( color_key_used[button] == false) + { + color_key_used[button] = true; + *msg = key_helper_msg_def[button]; + *icon = key_helper_icon_def[button]; + result = true; + } + } + + if( result == false && number_key < 10) // no key defined yet, at least try to get a numbered key + { + // there is still a available number_key + *msg = CRCInput::convertDigitToKey(number_key); + *icon = ""; + if(number_key == 9) + number_key = 0; + else if(number_key == 0) + number_key = 10; + else + number_key++; + result = true; + } + return (result); + }; +}; + +// USERMENU +bool CNeutrinoApp::showUserMenu(int button) +{ + if(button < 0 || button >= SNeutrinoSettings::BUTTON_MAX) + return false; + + CMenuItem* menu_item = NULL; + CKeyHelper keyhelper; + neutrino_msg_t key = CRCInput::RC_nokey; + const char * icon = NULL; + int dummy; + + int menu_items = 0; + int menu_prev = -1; + static int selected[SNeutrinoSettings::BUTTON_MAX] = {-1, -1, -1, -1}; + + // define classes + CFavorites* tmpFavorites = NULL; + CPauseSectionsdNotifier* tmpPauseSectionsdNotifier = NULL; + CAudioSelectMenuHandler* tmpAudioSelectMenuHandler = NULL; + CMenuWidget* tmpNVODSelector = NULL; + CStreamInfo2Handler* tmpStreamInfo2Handler = NULL; + CEventListHandler* tmpEventListHandler = NULL; + CEPGplusHandler* tmpEPGplusHandler = NULL; + CEPGDataHandler* tmpEPGDataHandler = NULL; + + std::string txt = g_settings.usermenu_text[button]; + if (button == SNeutrinoSettings::BUTTON_RED) { + if( txt.empty() ) + txt = g_Locale->getText(LOCALE_INFOVIEWER_EVENTLIST); + } + else if( button == SNeutrinoSettings::BUTTON_GREEN) { + if( txt.empty() ) + txt = g_Locale->getText(LOCALE_INFOVIEWER_LANGUAGES); + } + else if( button == SNeutrinoSettings::BUTTON_YELLOW) { + if( txt.empty() ) + txt = g_Locale->getText((g_RemoteControl->are_subchannels) ? LOCALE_INFOVIEWER_SUBSERVICE : LOCALE_INFOVIEWER_SELECTTIME); + //txt = g_Locale->getText(LOCALE_NVODSELECTOR_DIRECTORMODE); + } + else if( button == SNeutrinoSettings::BUTTON_BLUE) { + if( txt.empty() ) + txt = g_Locale->getText(LOCALE_INFOVIEWER_STREAMINFO); + } + CMenuWidget *menu = new CMenuWidget(txt.c_str() , "features.raw", 350); + if (menu == NULL) + return 0; + menu->addItem(GenericMenuSeparator); + + // go through any postition number + for(int pos = 0; pos < SNeutrinoSettings::ITEM_MAX ; pos++) { + // now compare pos with the position of any item. Add this item if position is the same + switch(g_settings.usermenu[button][pos]) { + case SNeutrinoSettings::ITEM_NONE: + // do nothing + break; + + case SNeutrinoSettings::ITEM_BAR: + if(menu_prev == -1 || menu_prev == SNeutrinoSettings::ITEM_BAR ) + break; + + menu->addItem(GenericMenuSeparatorLine); + menu_prev = SNeutrinoSettings::ITEM_BAR; + break; + + case SNeutrinoSettings::ITEM_FAVORITS: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_FAVORITS; + tmpFavorites = new CFavorites; + keyhelper.get(&key,&icon,CRCInput::RC_green); + menu_item = new CMenuForwarder(LOCALE_FAVORITES_MENUEADD, true, NULL, tmpFavorites, "-1", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_RECORD: + if(g_settings.recording_type == RECORDING_OFF) + break; + + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_RECORD; + keyhelper.get(&key,&icon,CRCInput::RC_red); + menu_item = new CMenuOptionChooser(LOCALE_MAINMENU_RECORDING, &recordingstatus, MAINMENU_RECORDING_OPTIONS, MAINMENU_RECORDING_OPTION_COUNT, true, this, key, icon); + menu->addItem(menu_item, false); + //if(has_hdd) + // menu->addItem(new CMenuForwarder(LOCALE_EXTRA_AUTO_TO_RECORD, autoshift, NULL, this, "autolink"), false); + break; + + case SNeutrinoSettings::ITEM_MOVIEPLAYER_MB: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_MOVIEPLAYER_MB; + keyhelper.get(&key,&icon,CRCInput::RC_green); + menu_item = new CMenuForwarder(LOCALE_MOVIEBROWSER_HEAD, true, NULL, moviePlayerGui, "tsmoviebrowser", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_TIMERLIST: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_TIMERLIST; + keyhelper.get(&key,&icon,CRCInput::RC_yellow); + menu_item = new CMenuForwarder(LOCALE_TIMERLIST_NAME, true, NULL, Timerlist, "-1", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_REMOTE: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_REMOTE; + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarder(LOCALE_RCLOCK_MENUEADD, true, NULL, this->rcLock, "-1" , key, icon ); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_EPG_SUPER: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_EPG_SUPER; + tmpEPGplusHandler = new CEPGplusHandler(); + keyhelper.get(&key,&icon,CRCInput::RC_green); + menu_item = new CMenuForwarder(LOCALE_EPGMENU_EPGPLUS , true, NULL, tmpEPGplusHandler , "-1", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_EPG_LIST: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_EPG_LIST; + tmpEventListHandler = new CEventListHandler(); + keyhelper.get(&key,&icon,CRCInput::RC_red); + menu_item = new CMenuForwarder(LOCALE_EPGMENU_EVENTLIST , true, NULL, tmpEventListHandler, "-1", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_EPG_INFO: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_EPG_INFO; + tmpEPGDataHandler = new CEPGDataHandler(); + keyhelper.get(&key,&icon,CRCInput::RC_yellow); + menu_item = new CMenuForwarder(LOCALE_EPGMENU_EVENTINFO , true, NULL, tmpEPGDataHandler , "-1", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_EPG_MISC: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_EPG_MISC; + dummy = g_Sectionsd->getIsScanningActive(); + //dummy = sectionsd_scanning; + tmpPauseSectionsdNotifier = new CPauseSectionsdNotifier; + keyhelper.get(&key,&icon); + menu_item = new CMenuOptionChooser(LOCALE_MAINMENU_PAUSESECTIONSD, &dummy, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, tmpPauseSectionsdNotifier , key, icon ); + menu->addItem(menu_item, false); + menu_items++; + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarder(LOCALE_MAINMENU_CLEARSECTIONSD, true, NULL, this, "clearSectionsd", key,icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_AUDIO_SELECT: + //g_settings.audio_left_right_selectable || g_RemoteControl->current_PIDs.APIDs.size() > 1) + if (1) { + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_AUDIO_SELECT; + tmpAudioSelectMenuHandler = new CAudioSelectMenuHandler; + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarder(LOCALE_AUDIOSELECTMENUE_HEAD, true, NULL, tmpAudioSelectMenuHandler, "-1", key,icon); + menu->addItem(menu_item, false); + } + break; + + case SNeutrinoSettings::ITEM_SUBCHANNEL: + if (!(g_RemoteControl->subChannels.empty())) { + // NVOD/SubService- Kanal! + tmpNVODSelector = new CMenuWidget(g_RemoteControl->are_subchannels ? LOCALE_NVODSELECTOR_SUBSERVICE : LOCALE_NVODSELECTOR_HEAD, "video.raw", 350); + if(getNVODMenu(tmpNVODSelector)) { + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_SUBCHANNEL; + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarder(g_RemoteControl->are_subchannels ? LOCALE_NVODSELECTOR_SUBSERVICE : LOCALE_NVODSELECTOR_HEAD, true, NULL, tmpNVODSelector, "-1", key,icon); + menu->addItem(menu_item, false); + } + } + break; + + case SNeutrinoSettings::ITEM_TECHINFO: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_TECHINFO; + tmpStreamInfo2Handler = new CStreamInfo2Handler(); + keyhelper.get(&key,&icon,CRCInput::RC_blue); + menu_item = new CMenuForwarder(LOCALE_EPGMENU_STREAMINFO, true, NULL, tmpStreamInfo2Handler , "-1", key, icon ); + menu->addItem(menu_item, false); + break; + case SNeutrinoSettings::ITEM_PLUGIN: + { + char id[5]; + int cnt = 0; + for(unsigned int count = 0; count < (unsigned int) g_PluginList->getNumberOfPlugins(); count++) + { + std::string tmp = g_PluginList->getName(count); + if (g_PluginList->getType(count)== CPlugins::P_TYPE_TOOL && !g_PluginList->isHidden(count) && tmp.find("Teletext") == std::string::npos) + { + sprintf(id, "%d", count); + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_PLUGIN; + + //keyhelper.get(&key,&icon,CRCInput::RC_blue); + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarderNonLocalized(g_PluginList->getName(count), true, NULL, StreamFeaturesChanger, id, key, icon); + //menu->addItem(menu_item, (cnt == 0)); + menu->addItem(menu_item, 0); + cnt++; + } + } + } + break; + case SNeutrinoSettings::ITEM_VTXT: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_VTXT; + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarder(LOCALE_USERMENU_ITEM_VTXT, true, NULL, StreamFeaturesChanger, "teletext", key, icon); + menu->addItem(menu_item, 0); + + break; +#if 0 // FIXME not supported yet + case SNeutrinoSettings::ITEM_MOVIEPLAYER_TS: + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_MOVIEPLAYER_TS; + keyhelper.get(&key,&icon,CRCInput::RC_green); + menu_item = new CMenuForwarder(LOCALE_MOVIEPLAYER_TSPLAYBACK, true, NULL, moviePlayerGui, "tsplayback", key, icon); + menu->addItem(menu_item, false); + break; + + case SNeutrinoSettings::ITEM_VTXT: + for(unsigned int count = 0; count < (unsigned int) g_PluginList->getNumberOfPlugins(); count++) + { + std::string tmp = g_PluginList->getName(count); + if (g_PluginList->getType(count)== CPlugins::P_TYPE_TOOL && !g_PluginList->isHidden(count) && tmp.find("Teletext") != std::string::npos) + { + sprintf(id, "%d", count); + menu_items++; + menu_prev = SNeutrinoSettings::ITEM_VTXT; + + //keyhelper.get(&key,&icon,CRCInput::RC_blue); + keyhelper.get(&key,&icon); + menu_item = new CMenuForwarderNonLocalized(g_PluginList->getName(count), true, NULL, StreamFeaturesChanger, id, key, icon); + menu->addItem(menu_item, 0); + } + } + break; +#endif + default: + printf("[neutrino] WARNING! menu wrong item!!\n"); + break; + } + } + + // Allow some tailoring for privat image bakers ;) + if (button == SNeutrinoSettings::BUTTON_RED) { + } + else if( button == SNeutrinoSettings::BUTTON_GREEN) { + } + else if( button == SNeutrinoSettings::BUTTON_YELLOW) { + } + else if( button == SNeutrinoSettings::BUTTON_BLUE) { +#ifdef _EXPERIMENTAL_SETTINGS_ + //Experimental Settings + if(menu_prev != -1) + menu->addItem(GenericMenuSeparatorLine); + menu_items ++; + menu_key++; + // FYI: there is a memory leak with 'new CExperimentalSettingsMenuHandler() + menu_item = new CMenuForwarder(LOCALE_EXPERIMENTALSETTINGS, true, NULL, new CExperimentalSettingsMenuHandler(), "-1", CRCInput::convertDigitToKey(menu_key), ""); + menu->addItem(menu_item, false); +#endif + } + + // show menu if there are more than 2 items only + // otherwise, we start the item directly (must be the last one) + if(menu_items > 1 ) { + menu->setSelected(selected[button]); + menu->exec(NULL,""); + selected[button] = menu->getSelected(); + } + else if (menu_item != NULL) + menu_item->exec( NULL ); + + // restore mute symbol + //AudioMute(current_muted, true); + + // clear the heap + if(tmpFavorites) delete tmpFavorites; + if(tmpPauseSectionsdNotifier) delete tmpPauseSectionsdNotifier; + if(tmpAudioSelectMenuHandler) delete tmpAudioSelectMenuHandler; + if(tmpNVODSelector) delete tmpNVODSelector; + if(tmpStreamInfo2Handler) delete tmpStreamInfo2Handler; + if(tmpEventListHandler) delete tmpEventListHandler; + if(tmpEPGplusHandler) delete tmpEPGplusHandler; + if(tmpEPGDataHandler) delete tmpEPGDataHandler; + if(menu) delete menu; + return 0; +} + +void CNeutrinoApp::ShowStreamFeatures() +{ +} diff --git a/src/nhttpd/AUTHORS b/src/nhttpd/AUTHORS new file mode 100644 index 000000000..5ee23b668 --- /dev/null +++ b/src/nhttpd/AUTHORS @@ -0,0 +1,21 @@ +AUTHORS +of Neutrino Webserver +in order of appearance (bottom up) + + +Johannes Golombek [yjogol@cvs.tuxbox.org] +Bengt Martensson [barf@cvs.tuxbox.org] +Jacek Jendrzej [metallica@cvs.tuxbox.org] +Henning Behrend [sat_man@cvs.tuxbox.org] +Axel Buehning [diemade@cvs.tuxbox.org] +Michael Schuele [chakazulu@cvs.tuxbox.org] +Peter Reich [digi_casi@cvs.tuxbox.org] +H.Heinold [heinold@physik.tu-cottbus.de] +Sven Traenkle [zwen@cvs.tuxbox.org] +gagga +wjoost +Ralf Gandy +Michael Lantzen +Andreas Oberritter +Dirk Szymanski [dirch@cvs.tuxbox.org] +Steffen Hehn diff --git a/src/nhttpd/COPYING b/src/nhttpd/COPYING new file mode 100644 index 000000000..623b6258a --- /dev/null +++ b/src/nhttpd/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/nhttpd/Makefile.am b/src/nhttpd/Makefile.am new file mode 100644 index 000000000..19586226b --- /dev/null +++ b/src/nhttpd/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = yhttpd_core yhttpd_mods tuxboxapi web + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/src/nhttpd/yhttpd_core \ + -I$(top_srcdir)/src/nhttpd/yhttpd_mods \ + -I$(top_srcdir)/src/nhttpd/tuxboxapi \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/connection \ + @FREETYPE_CFLAGS@ + +noinst_LIBRARIES = libnhttpd.a +libnhttpd_a_SOURCES = yhttpd.cpp diff --git a/src/nhttpd/README b/src/nhttpd/README new file mode 100644 index 000000000..052946f82 --- /dev/null +++ b/src/nhttpd/README @@ -0,0 +1,18 @@ +------------------------------------------------------------------------------ +nhttpd Version 3.x = (yhttpd 3.x + tuxboxapi) +------------------------------------------------------------------------------ + +13.09.2006 (yjogol) +------------------- +Starting with Version 3.0 the nhttpd source code was splitted into +a core Webserver (yhttpd_core), extension modules (yhttpd_mods) and the +tuxbox specific code (tuxboxapi). + +The yhttpd_core and yhttp_mods are completly rewritten. +All Neutrino specific code was reorganized into yParser- and ControlAPI-code. + +The Hook-System (extension modules) encapsulate all requests and responses +for the tuxboxapi. So both tuxboxapi and yhttpd can be developed seperatlly and +independ from each other. + +Look at yhttpd_core/README diff --git a/src/nhttpd/doc/Changelog.txt b/src/nhttpd/doc/Changelog.txt new file mode 100644 index 000000000..f06bd2b72 --- /dev/null +++ b/src/nhttpd/doc/Changelog.txt @@ -0,0 +1,320 @@ +Severity id Headline Build + +5-Enhancement JG200000540 nhttpd: new: /y/cache-info view and clear cache nhttpd 3.0.5 +5-Enhancement JG200000553 Live: Support for Non-compatible HTTP1.1-Clients like JtG-Servernhttpd 3.0.5 +5-Enhancement JG200000548 nhttpd: new hooks for Upload handling n httpd 3.0.5 +5-Enhancement JG200000549 nhttpd: mod_auth new Base64 algorism nhttpd 3.0.5 +5-Enhancement JG200000545 nhttpd: upload POST works with SSL now nhttpd 3.0.5 +5-Enhancement JG200000528 nhttpd: Autentication: new implemented nhttpd 3.0.5 +5-Enhancement JG200000538 nhttpd: yParser Cache nhttpd 3.0.5 +5-Enhancement JG200000544 nhttpd: new format of nhttpd.conf nhttpd 3.0.5 +5-Enhancement JG200000541 nhttpd: new /y/server-config nhttpd 3.0.5 +5-Enhancement JG200000550 nhttpd: add SSL support nhttpd 3.0.5 +5-Enhancement JG200000542 nhttpd: new module mod_cache for caching dynamic content nhttpd 3.0.5 +5-Enhancement JG200000543 nhttpd: new module mod_weblog CLF and ELF logging nhttpd 3.0.5 +5-Enhancement JG200000547 nhttpd: more Features for HTTP1.1 support nhttpd 3.0.5 +5-Enhancement JG200000546 nhttpd: new Socket multiplexing for multi-threading nhttpd 3.0.5 +5-Enhancement JG200000536 nhttpd: Upload Progress .. nhttpd 3.0.5 + +5-Enhancement JG200000552 Settings: Support for new nhttpd.conf Format R2.2.0 +5-Enhancement JG200000530 nhttpd: Webserver (yhhtpd) rewritten nhttpd 3.0.0 +5-Enhancement JG200000529 nhttpd: split nhttpd in yhttpd_core, yhttpd_mods, neutrinoAPI nhttpd 3.0.0 +5-Enhancement JG200000527 Bouquet: Bouquet-Editor moved to tools R2.1.0 +5-Enhancement JG200000523 Bouquet-Editor: new implemented R2.1.0 +5-Enhancement JG200000526 Bouquet: EPG-List: new implemented R2.1.0 +5-Enhancement JG200000534 Boxcontrol: Use /control/standby to determine standby status R2.1.0 +5-Enhancement JG200000440 Timer: ANNOUNCE_TIME_1=-120 at Rec from EPG .. R2.1.0 +5-Enhancement JG200000524 Timer: TimerList new implemented R2.1.0 +5-Enhancement JG200000531 Timer: use RecordingSafty in Timer-Prog R2.1.0 +5-Enhancement JG200000525 Timer: add/modify Timer: new implemented R2.1.0 +4-Minor JG200000522 Tools: Flash fix reboot button R2.1.0 + +5-Enhancement JG200000511 GUI: disable AutoMount if ManagementIP does not match R2.0.2 +4-Minor JG200000519 Live: VLC Snapshot with IE works again R2.0.2 +5-Enhancement JG200000512 Live: fix: Fullscreen-Button (IE) does not work R2.0.2 +5-Enhancement JG200000518 Live: add EPG-Button for Popup R2.0.2 +3-Average JG200000517 Record: fixed: Record without transcoding works again R2.0.2 +5-Enhancement JG200000514 Settings: Diverse: add EPG-Settings R2.0.2 +5-Enhancement JG200000513 Settings: Diverse: add Virtual zap R2.0.2 + +3-Average JG200000504 fix: call dboxshot correctly if installed in /var/bin (in Remote OSD) R2.0.1 +3-Average JG200000506 Live: fixed Problems switching channels in LiveTV/Radio R2.0.1 +5-Enhancement JG200000507 Live: MiniEPG only on demand R2.0.1 + +5-Enhancement JG200000478 Boxcontrol: OSD Screenshot: Support for dboxshot R2.0.0 +5-Enhancement JG200000480 Boxcontrol: Control: Live lock / unlock R2.0.0 +5-Enhancement JG200000482 Boxcontrol: Control: new: Standby Status R2.0.0 +5-Enhancement JG200000483 EPG: Link to IMDB R2.0.0 +5-Enhancement JG200000498 Boxcontrol: add SPTS Status R2.0.0 +5-Enhancement JG200000497 Boxcontrol: New RC-Imake: Nokia-Remote R2.0.0 +5-Enhancement JG200000494 Boxcontrol: Control: Add optical Output on/off/status R2.0.0 +4-Minor JG200000392 GUI: initialize Volumen correctly .. R2.0.0 +5-Enhancement JG200000331 GUI: all Pages W3C XHTML 1.0 conform .. R2.0.0 +5-Enhancement JG200000465 GUI: new volumen control R2.0.0 +5-Enhancement JG200000459 GUI: new yWeb 2.0 Layout R2.0.0 +5-Enhancement JG200000254 GUI: Show channel-logos (if mounted) R2.0.0 +5-Enhancement JG200000455 GUI: Help: New Item to Wiki R2.0.0 +5-Enhancement JG200000449 GUI: show actual time in main menu R2.0.0 +5-Enhancement JG200000439 GUI: show custom text in Menue to identify box (if you have several boxes) R2.0.0 +5-Enhancement JG200000468 Info: add external Links for getting help R2.0.0 +5-Enhancement JG200000473 Live: Radio-Streaming with VLC (0.8.5) New! R2.0.0 +5-Enhancement JG200000500 Live: Transcode: own dialogue and more settings R2.0.0 +5-Enhancement JG200000464 Live: Mini EPG for actual channel R2.0.0 +5-Enhancement JG200000484 Live: dont show bouquet if set to hidden R2.0.0 +5-Enhancement JG200000408 Live: deinterlace & http-caching R2.0.0 +5-Enhancement JG200000492 Live: Set Timer from Mini EPG R2.0.0 +5-Enhancement JG200000457 Record: Transcode while recording R2.0.0 +5-Enhancement JG200000443 Record: new option: display off R2.0.0 +5-Enhancement JG200000458 Record: add Profiles for transcoding (iPodVideo, custom...) R2.0.0 +5-Enhancement JG200000474 Record: Radio-Recording & Transcoding (VLC) to mp3, iPod, custom R2.0.0 +5-Enhancement JG200000477 Record: Radio-Recording & Transcoding (mp3, iPod, custom) R2.0.0 +5-Enhancement JG200000456 Settings: Backup & Restore settings R2.0.0 +5-Enhancement JG200000475 Settings: new direct recording Filename template R2.0.0 +5-Enhancement JG200000496 settings: remove support antiservices.xml R2.0.0 +5-Enhancement JG200000479 Settings: New Menu for Live-Settings R2.0.0 +5-Enhancement JG200000487 Settings: WebServer: new ExtrasDocRoot/URL R2.0.0 +5-Enhancement JG200000134 Timer: new: EPG+ R2.0.0 +5-Enhancement JG200000452 Timer: SyncTimer: Switch to TV only from radio-mode R2.0.0 +5-Enhancement JG200000490 Timer: EPG+ add set timer and zapto R2.0.0 +4-Minor JG200000137 Tools: Message to box displays special charachers correct R2.0.0 +5-Enhancement JG200000448 Tools: Wake on Lan fix Javascript Error R2.0.0 +5-Enhancement JG200000470 Tools: Flash image: add Demo mode R2.0.0 +5-Enhancement JG200000469 Tools: Flash image: new dialogs R2.0.0 +5-Enhancement JG200000476 Tools: AutoMount (Warmduscher Interface) R2.0.0 + +3-Average JG200000485 nhttpd: fixed: yvars now in WebserverRequest (multi-threading) nhttpd 2.2.4 +5-Enhancement JG200000463 nhttpd: add EPG data in XML format nhttpd 2.2.4 +5-Enhancement JG200000486 nhttpd: add caching for blocks nhttpd 2.2.4 +5-Enhancement JG200000488 nhttpd: Set Timer, zapto,epg: Channel_name with multiple values .. nhttpd 2.2.4 +5-Enhancement JG200000491 nhttpd: new: XML-Format for bouquets nhttpd 2.2.4 +5-Enhancement JG200000495 nhttpd: new: zapto?getallsubchannels nhttpd 2.2.4 + +5-Enhancement JG200000399 Boxcontrol: view box record-status .. R1.5.0 +3-Average JG200000424 Bouquetedit: fixed probs with Firefox 1.5 editing problems R1.5.0 +5-Enhancement JG200000414 GUI: Links to Wiki R1.5.0 +3-Average JG200000420 Live: fixed Stream-URL problems with yadd & Firefox R1.5.0 +5-Enhancement JG200000432 Live: Radio works with yadds R1.5.0 +5-Enhancement JG200000351 LiveView: Build Stream-URL with ClientAddr (for NAT, Remapping) R1.5.0 +4-Minor JG200000433 Settings: WOL fixed Javascript Errors R1.5.0 +5-Enhancement JG200000394 Settings: add: Neutrino direct recording settings ... R1.5.0 +5-Enhancement JG200000417 Settings: WebServer: add HostedDocumentRoot R1.5.0 +5-Enhancement JG200000418 Settings: WebServer: add NoAuthClient R1.5.0 +5-Enhancement JG200000373 Settings: yWeb speed up using caching R1.5.0 +5-Enhancement JG200000434 Settings: WOL Link to yWeb-Settings R1.5.0 +5-Enhancement JG200000435 Settings: About: Links to Wiki R1.5.0 +5-Enhancement JG200000425 Timer: SyncTimer: added start and stop deplay from recording settings R1.5.0 +5-Enhancement JG200000405 Timer: Sync Timer: add rec_dir R1.5.0 +5-Enhancement JG200000231 Timer: tvinfo.de download & TimerSync R1.5.0 +5-Enhancement JG200000398 Timer: new timer default now: record R1.5.0 +4-Minor JG200000410 Tools: Sagem RC is displayed in primary menu R1.5.0 +5-Enhancement JG200000415 Tools: new: Check Installation R1.5.0 +5-Enhancement JG200000348 Tools: Info: use echo -e to advoid escaped newline output R1.5.0 + +5-Enhancement JG200000438 nhttpd: add Timer: rec_dir from neutrino.conf as default nhttpd 2.2.3 +5-Enhancement JG200000423 nhttpd: fixed lost Data in POST-Method (Mozilla/Firefox) nhttpd 2.2.2 +5-Enhancement JG200000422 nhttpd: No Authentication with new NoAuthClient fixed nhttpd 2.2.2 +5-Enhancement JG200000428 nhttpd: ini-set caching: clear config on open nhttpd 2.2.2 + +5-Enhancement JG200000294 nhttpd: Host other Webs (Root defined at HostedDocumentRoot in nhttpd.conf) nhttpd 2.2.1 +5-Enhancement JG200000388 nhttpd: set IP with no authentication (e.g. for JtG etc) nhttpd 2.2.1 +5-Enhancement JG200000416 nhttdp: zapto mit channel_name nhttpd 2.2.1 + +3-Average JG200000369 Live: fixed problems switching Radio-Channels R1.4.3 +4-Minor JG200000355 nhttpd: change port now used in Scripts R1.4.3 +4-Minor JG200000365 Settings: fixed: upload cam-alpha.bin now named correctly R1.4.3 +5-Enhancement JG200000366 settings: add port to WebServer settings R1.4.3 +5-Enhancement JG200000367 gui: add remote control for Sagem R1.4.3 +5-Enhancement JG200000389 tools: restart sectionsd R1.4.3 +5-Enhancement JG200000391 flash: fixed reboot after flashing R1.4.3 + +4-Minor JG200000359 tools: fix reboot after flash R1.4.2 +5-Enhancement JG200000332 Live: record: filepath settings at settings->yWeb R1.4.2 +5-Enhancement JG200000337 bouquets: start bouquetlist at actual bouquet R1.4.2 +5-Enhancement JG200000360 bouquet: text in black again R1.4.2 + +5-Enhancement JG200000127 flash download: button for delete download-image in /tmp R1.4.1 +5-Enhancement JG200000339 nhttpd: new: file-action:;; R1.4.1 +5-Enhancement JG200000340 new: Neutrino-Settings: pictureviewer R1.4.1 +5-Enhancement JG200000341 new: Neutrino-Settings: Audioplayer R1.4.1 +5-Enhancement JG200000342 new: Neutrino-Settings: Movieplayer R1.4.1 +5-Enhancement JG200000343 new: Neutrino-Settings: Diverse R1.4.1 +5-Enhancement JG200000344 new: Neutrino-Settings: Boot Options R1.4.1 +5-Enhancement JG200000345 new: Neutrino-Settings: Parental R1.4.1 +5-Enhancement JG200000346 new: Neutrino-Settings: Recording R1.4.1 +5-Enhancement JG200000347 Timer: add recordingDir R1.4.1 + +3-Average JG200000156 Flash: Error in mtd list if length!=5 R1.4.0 +5-Enhancement JG200000326 Flash: New Dialogs R1.4.0 +5-Enhancement JG200000233 Tools: new: Wake on LAN for 3 MACs R1.4.0 +5-Enhancement JG200000246 mount: do mount at mount list R1.4.0 +5-Enhancement JG200000247 mount: new: unmount list R1.4.0 +5-Enhancement JG200000305 infos: /proc/cpuinfo, meminfo, version... R1.4.0 +5-Enhancement JG200000271 nhttpd: ycgi: new: include-block:;; R1.4.0 +5-Enhancement JG200000314 nhttpd: ycgi: new: ini-set:;; R1.4.0 +5-Enhancement JG200000234 nhttpd: ycgi: new: if-file-exists:~~ R1.4.0 +5-Enhancement JG200000315 nhttpd: ycgi: new: if-not-equal R1.4.0 +5-Enhancement JG200000317 nhttpd: ycgi: new: add var-set, var-get for local ycgi varables R1.4.0 +5-Enhancement JG200000318 Live: Experimental: Change VLC audio channel R1.4.0 +5-Enhancement JG200000311 Live: record (Expert Mode ActiveX).. R1.4.0 +5-Enhancement JG200000312 Live: record: mux type = ts, ps R1.4.0 +5-Enhancement JG200000319 Live: Expert Mode: Take VLC Snapshot R1.4.0 +5-Enhancement JG200000320 after change Web Password: reload nhttpd.conf R1.4.0 +5-Enhancement JG200000325 General from-dialog updates R1.4.0 +5-Enhancement JG200000327 New: OSD Snapshot with fbshot R1.4.0 +5-Enhancement JG200000316 Management IP for some dialogs required R1.4.0 + +3-Average JG200000308 nhttpd: re-write file upload handling (solved: problems with pre-send data - mozilla engines) R1.3.6 +5-Enhancement JG200000144 Live: add Streaminfo button R1.3.6 +5-Enhancement JG200000145 yInstaller: upload a tar-file and execude included install.sh R1.3.6 +5-Enhancement JG200000287 WebIF: telnet like commandline R1.3.6 +5-Enhancement JG200000292 Live: channel-EPG-Panel autorefresh all 5min R1.3.6 +5-Enhancement JG200000297 Live: epg preview for selected channel R1.3.6 +5-Enhancement JG200000304 nhttpd: new func get_request_data R1.3.6 +5-Enhancement JG200000307 nhttpd: re-write file upload handling (saving memory) R1.3.6 + +4-Minor JG200000266 Settings: WebServer JavaScript-Error for field port R1.3.5 +4-Minor JG200000269 _Y_Library.sh:config_set_value() interpret backslash R1.3.5 +4-Minor JG200000232 Live:Sprache: DropDown ins TV Fenster R1.3.5 +5-Enhancement JG200000141 Live: enable select audio-pid R1.3.5 +5-Enhancement JG200000237 robots.txt in html-root R1.3.5 +5-Enhancement JG200000255 keine hard-links nach httpd-y (z.B. _Y_Globals.sh) R1.3.5 +5-Enhancement JG200000272 Dialog Boot-Logo und LCD-Logo zusammenfassen R1.3.5 +5-Enhancement JG200000277 Tooltips in english R1.3.5 +5-Enhancement JG200000278 Tools->msg beide Dialoge zusammenfassen R1.3.5 +5-Enhancement JG200000280 tools: move Message to Boxcontrol R1.3.5 +5-Enhancement JG200000284 use image am maps for web-based remote control R1.3.5 + +4-Minor JG200000216 nhttp: Upload temporary file moved from /var/tmp to /tmp/upload.tmp R1.3.4 +5-Enhancement JG200000139 Live: EPG-Title in Channel-Dropdown R1.3.4 +5-Enhancement JG200000212 nhttpd: use relative path for includes R1.3.4 +5-Enhancement JG200000217 nhttpd: ycgi: set Working-Dir to template path R1.3.4 +5-Enhancement JG200000227 ycgi: new:func:get_audio_pids_as_dropdown R1.3.4 +5-Enhancement JG200000229 remote RC via Web R1.3.4 + +5-Enhancement JG200000218 ycgi: new cmd: if-empty R1.3.3 +5-Enhancement JG200000219 ycgi: new cmd: if-equal R1.3.3 +5-Enhancement JG200000220 ycgi: new:func:get_bouquts_as_dropdown R1.3.3 +5-Enhancement JG200000221 ycgi: new:func:get_actual_bouquet_numer R1.3.3 +5-Enhancement JG200000222 ycgi: new:func:get_canel_as_dropdown R1.3.3 +5-Enhancement JG200000223 ycgi: new:func:get_actual_channel_id R1.3.3 +5-Enhancement JG200000224 ycgi: new:func:get_mode R1.3.3 +5-Enhancement JG200000225 ycgi: new:func:get_video_pids R1.3.3 +5-Enhancement JG200000226 ycgi: new:func:get_radio_pid R1.3.3 +5-Enhancement JG200000235 ycgi: Neu *.yhtm-Dateien & Reorg der Dateien R1.3.3 + +5-Enhancement JG200000035 Live: Window size configurable R1.3.2 +5-Enhancement JG200000107 Live: new: Double-View R1.3.2 +5-Enhancement JG200000206 nhttpd: Set Working dir for Skripts R1.3.2 +5-Enhancement JG200000207 scripts: relative local include path R1.3.2 +5-Enhancement JG200000209 correct Frame-Destinations VNC, Skins, WebServer R1.3.2 +5-Enhancement JG200000210 Settings: yWeb Configurator (slave box, live resolution) R1.3.2 +5-Enhancement JG200000211 add ucode.bin for up/download R1.3.2 + +5-Enhancement JG200000200 Live: Radio: Player via Dateierweiterung m3u, m4u starten R1.3.1 +3-Average JG200000147 LiveView nur 9 Bouquets im Dropdown. fixed. R1.3.1 +4-Minor JG200000179 dietmarW-Images habe kein /var/tmp also /tmp nutzen R1.3.1 +5-Enhancement JG200000084 Plugins: Settings: Mount-Einstellungen R1.3.1 +5-Enhancement JG200000182 Live: Bouquets & Channel: Dropdown Breite begrenzen R1.3.1 +5-Enhancement JG200000183 Bouquets: epg und settings im yWeb_Layout R1.3.1 +5-Enhancement JG200000184 ycgi: execute= + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Bouquet-Editor=}{=var-set:menu=Bouquet "{=name=}" workon=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    +

    + + + + + + +
    + + +

    +

    +

    +

    +
    + +
    + +

    +

    + All changes must be still stored! +
    +
    +
    + + diff --git a/src/nhttpd/web/Y_Boxcontrol_Bouquet_Editor_Main.yhtm b/src/nhttpd/web/Y_Boxcontrol_Bouquet_Editor_Main.yhtm new file mode 100644 index 000000000..6b9255c55 --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Bouquet_Editor_Main.yhtm @@ -0,0 +1,186 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Bouquet-Editor=}{=var-set:menu=Bouquet-Editor=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +

    + [add Bouquet] + [save] +

    +
    +
    +

    Bouquet add
    + New Bouquets' name: + +
    + + +

    +
    +
    +
    +
    +

    +

    Bouquet rename
    + New Bouquets' name: + + +
    + + +

    +
    +
    + + +{=var-set:row= + + + + + + +=} +{=func:bouquet_editor_main {=var-get:row=}=} +
    + %s + + %s + + + %s + +%s + + umbenennen  + + löschen  + + nach unten  + + nach oben  +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Boxcontrol_Bouquetlist.yhtm b/src/nhttpd/web/Y_Boxcontrol_Bouquetlist.yhtm new file mode 100644 index 000000000..a55389a43 --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Bouquetlist.yhtm @@ -0,0 +1,11 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + + +{=func:get_bouquets_as_templatelist =} +
    Channels

    %s
    + + diff --git a/src/nhttpd/web/Y_Boxcontrol_Bouquets.yhtm b/src/nhttpd/web/Y_Boxcontrol_Bouquets.yhtm new file mode 100644 index 000000000..ff46521b2 --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Bouquets.yhtm @@ -0,0 +1,15 @@ +{=include-block:Y_Blocks.txt;head=} + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Bouquet=}{=var-set:menu=Bouquet=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Boxcontrol_Channellist.yhtm b/src/nhttpd/web/Y_Boxcontrol_Channellist.yhtm new file mode 100644 index 000000000..7baed8edc --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Channellist.yhtm @@ -0,0 +1,28 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + + +{=func:get_bouquets_with_epg {=bouquet=}=} +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Boxcontrol_Menue.yhtm b/src/nhttpd/web/Y_Boxcontrol_Menue.yhtm new file mode 100644 index 000000000..60f196876 --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Menue.yhtm @@ -0,0 +1,115 @@ +{=var-set:cancache=yPInstall=} +{=include-block:Y_Blocks.txt;head=} + + + +{=var-set:fbshot={=if-file-exists:/bin/fbshot~true~{=if-file-exists:/var/bin/fbshot~true~{=if-file-exists:/bin/dboxshot~true~{=if-file-exists:/var/bin/dboxshot~true~false=}=}=}=}=} +{=var-set:dboxshot={=if-file-exists:/bin/dboxshot~true~{=if-file-exists:/var/bin/dboxshot~true~false=}=}=} + +
    +

    Boxcontrol

    +
    +
    + +
    +
    +
    +
    +

    Control

    +
    +
    + + + + + +
    ..
    +
    + +  -  + +  +  + + Mute +

    + TV + Radio +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Boxcontrol_Messages.yhtm b/src/nhttpd/web/Y_Boxcontrol_Messages.yhtm new file mode 100644 index 000000000..efe1273bb --- /dev/null +++ b/src/nhttpd/web/Y_Boxcontrol_Messages.yhtm @@ -0,0 +1,40 @@ +{=var-set:cancache=yPyes=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Message=}{=var-set:menu=Message=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + +
    Message to Coolstram-TV Screen
    + + +
    Popup message to Coolstream-TV Screen
    + + +
    +
    +
    +
    +
    + + diff --git a/src/nhttpd/web/Y_Dyn_Pages.yhtm b/src/nhttpd/web/Y_Dyn_Pages.yhtm new file mode 100644 index 000000000..607f97fd4 --- /dev/null +++ b/src/nhttpd/web/Y_Dyn_Pages.yhtm @@ -0,0 +1 @@ +{=include-block:Y_Blocks.txt;{=page=}=} \ No newline at end of file diff --git a/src/nhttpd/web/Y_EPG_Plus.yhtm b/src/nhttpd/web/Y_EPG_Plus.yhtm new file mode 100644 index 000000000..1d673fb55 --- /dev/null +++ b/src/nhttpd/web/Y_EPG_Plus.yhtm @@ -0,0 +1,211 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + +{=var-set:wait_text=get EPG.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#EPG_Plus=}{=var-set:menu=EPG Plus=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + +
    + +
     
    +
    + Details + + +
     
    +
     
    +
     
    + + + +
    + + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Ext_Menue.yhtm b/src/nhttpd/web/Y_Ext_Menue.yhtm new file mode 100644 index 000000000..aa0c080c4 --- /dev/null +++ b/src/nhttpd/web/Y_Ext_Menue.yhtm @@ -0,0 +1,72 @@ +{=var-set:extention={=if-file-exists:/var/tuxbox/config/extentions.txt~/var/tuxbox/config/extentions.txt~{=if-file-exists:/var/httpd/extentions.txt~/var/httpd/extentions.txt~/share/tuxbox/neutrino/httpd-y/extentions.txt=}=}=} +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +

    Extentions

    +
    +
    Normal
    +
    +
      +
        +
    +
    Management
    +
    +
      +
    +
    +
    Admin
    +
    + +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Ext_Settings.yhtm b/src/nhttpd/web/Y_Ext_Settings.yhtm new file mode 100644 index 000000000..ca6f62ca7 --- /dev/null +++ b/src/nhttpd/web/Y_Ext_Settings.yhtm @@ -0,0 +1,37 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Extentions#Settings=}{=var-set:menu=Extentions Settings=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + +
     
    +
    + + + +
    +
    +
    + + +{=include-block:Y_Blocks.txt;management_check_bottom=} + diff --git a/src/nhttpd/web/Y_Ext_Update.yhtm b/src/nhttpd/web/Y_Ext_Update.yhtm new file mode 100644 index 000000000..1832d3a21 --- /dev/null +++ b/src/nhttpd/web/Y_Ext_Update.yhtm @@ -0,0 +1,236 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Values are updated.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Extentions#Update=}{=var-set:menu=Extentions Updater/Installer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + +
    + update Preview List + + + + + + + + + + + +
     SiteExtentionTagyour VersionUpdate Version
    +   +
    +
    New extention settings (preview)
    +
    + + + + +
    +
    + + +
    +
    +
    + Log
    + + + + + + + + + +
     AktionStatus
    +
    +
    +
    +
    + + +{=include-block:Y_Blocks.txt;management_check_bottom=} + diff --git a/src/nhttpd/web/Y_Ext_Update_refresh.yhtm b/src/nhttpd/web/Y_Ext_Update_refresh.yhtm new file mode 100644 index 000000000..bd71d09f1 --- /dev/null +++ b/src/nhttpd/web/Y_Ext_Update_refresh.yhtm @@ -0,0 +1,23 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Values are updated.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Extentions#Update=}{=var-set:menu=Extentions Settings saved=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + Settings stored. Menu is updated. +
    +
    + + + diff --git a/src/nhttpd/web/Y_Info_Help.yhtm b/src/nhttpd/web/Y_Info_Help.yhtm new file mode 100644 index 000000000..fc3462ca6 --- /dev/null +++ b/src/nhttpd/web/Y_Info_Help.yhtm @@ -0,0 +1,18 @@ +{=var-set:cancache=yPyes=} +{=include-block:Y_Blocks.txt;headCache=} + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Info#Hilfe=}{=var-set:menu=Help=}{=include-block:Y_Blocks.txt;work_menu=}
    + +
    + + diff --git a/src/nhttpd/web/Y_Info_Menue.yhtm b/src/nhttpd/web/Y_Info_Menue.yhtm new file mode 100644 index 000000000..f16744315 --- /dev/null +++ b/src/nhttpd/web/Y_Info_Menue.yhtm @@ -0,0 +1,25 @@ +{=var-set:cancache=yPConf=} +{=include-block:Y_Blocks.txt;head=} + +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} + +
    +

    Info

    +
    +
    + +
    + +
    +
    + + + diff --git a/src/nhttpd/web/Y_Info_Updates.yhtm b/src/nhttpd/web/Y_Info_Updates.yhtm new file mode 100644 index 000000000..be149bfc4 --- /dev/null +++ b/src/nhttpd/web/Y_Info_Updates.yhtm @@ -0,0 +1,47 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Info#Auf_Updates_pr.C3.BCfen=}{=var-set:menu=Auf Updates prüfen=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + + + +
    + Deine Version +

    + {=ini-get:Y_Version.txt;version=}
    + {=ini-get:Y_Version.txt;date=}
    + {=ini-get:Y_Version.txt;type=}

    + {=ini-get:Y_Version.txt;info=}
    +

    +
    + Neue Version +

    + {=ini-get:/tmp/version.txt;version=}
    + {=ini-get:/tmp/version.txt;date=}
    + {=ini-get:/tmp/version.txt;type=}

    + {=ini-get:/tmp/version.txt;info=}
    + {=if-empty:{=ini-get:/tmp/version.txt;url=}~~ + Download
    + =} +

    +
    +
    +
    + + diff --git a/src/nhttpd/web/Y_Live.yhtm b/src/nhttpd/web/Y_Live.yhtm new file mode 100644 index 000000000..c3a5bfffb --- /dev/null +++ b/src/nhttpd/web/Y_Live.yhtm @@ -0,0 +1,471 @@ +{=var-set:must_management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + + +{=var-set:wait_text=Streaming-Informationen
    werden ermittelt.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + + + +
    +
    + getting bouquets ...
    +
    +
    + {=if-equal:{=typ=}~popup~ + {=if-equal:{=mode=}~tv~ + + ~ =}=} +
    +
    + getting channels ...
    +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    + + + + + + {=if-equal:{=mode=}~tv~ + + =} + + + {=if-equal:{=mode=}~tv~ + + + + =} + {=if-equal:{=typ=}~popup~~ + {=if-equal:{=typ=}~transcode~~ + + =} + + =} + + + +
    +
    + + + + diff --git a/src/nhttpd/web/Y_LiveViewFull.yhtm b/src/nhttpd/web/Y_LiveViewFull.yhtm new file mode 100644 index 000000000..d678ef65e --- /dev/null +++ b/src/nhttpd/web/Y_LiveViewFull.yhtm @@ -0,0 +1,45 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#LiveView=}{=var-set:menu={=typ=}=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + +
    + + + +
    +
    +
    + + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Live_DoubleView.yhtm b/src/nhttpd/web/Y_Live_DoubleView.yhtm new file mode 100644 index 000000000..4fa1e2d68 --- /dev/null +++ b/src/nhttpd/web/Y_Live_DoubleView.yhtm @@ -0,0 +1,19 @@ +{=include-block:Y_Blocks.txt;head=} + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#Double_View=}{=var-set:menu=Live - DoubleView=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + {=if-empty:{=ini-get:/var/tuxbox/config/Y-Web.conf;slavebox=}~
    No IP for the Slave Box supplied!
    Einstellen unter Settings->yWeb.
    ~ + + =} +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Live_EPG.yhtm b/src/nhttpd/web/Y_Live_EPG.yhtm new file mode 100644 index 000000000..4163b183d --- /dev/null +++ b/src/nhttpd/web/Y_Live_EPG.yhtm @@ -0,0 +1,122 @@ +{=include-block:Y_Blocks.txt;head=} + + + +{=var-set:wait_text=EPG is loaded.=}{=include-block:Y_Blocks.txt;snip_show_wait=} + + + + + + + + + + + +
     FromtoBroadcasting
    +
    + + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Live_EPG_Info.yhtm b/src/nhttpd/web/Y_Live_EPG_Info.yhtm new file mode 100644 index 000000000..2b96573d2 --- /dev/null +++ b/src/nhttpd/web/Y_Live_EPG_Info.yhtm @@ -0,0 +1,22 @@ +{=include-block:Y_Blocks.txt;head=} + + + +Back + +
    +
     
    +
     
    +
    For details: move the mouse on the corresponding program.
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Live_Menue.yhtm b/src/nhttpd/web/Y_Live_Menue.yhtm new file mode 100644 index 000000000..1988372ff --- /dev/null +++ b/src/nhttpd/web/Y_Live_Menue.yhtm @@ -0,0 +1,85 @@ +{=include-block:Y_Blocks.txt;head=} + + + +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} + +
    +

    Live/Timer

    +
    +
    Live
    +
    + +
    +
    Timer
    +
    + +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Live_Msg.yhtm b/src/nhttpd/web/Y_Live_Msg.yhtm new file mode 100644 index 000000000..74a2f6d5b --- /dev/null +++ b/src/nhttpd/web/Y_Live_Msg.yhtm @@ -0,0 +1,14 @@ +{=include-block:Y_Blocks.txt;head=} + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#LiveView=}{=var-set:menu=LiveView PopUp=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + Under Live-View do not zap channels direct by the web or by the dbox' RC.
    + unlock +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Live_Record.yhtm b/src/nhttpd/web/Y_Live_Record.yhtm new file mode 100644 index 000000000..8a59bb5ef --- /dev/null +++ b/src/nhttpd/web/Y_Live_Record.yhtm @@ -0,0 +1,325 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + {=if-equal:{=mode=}~tv~ + + + + + + + + + + + + + + + + + + + + + + + =} + + + + + + + + + + + + + + + + + + + + + + + +
    Record Mode
     Filename:
      + Display on  + Transcoding on +
      +
    + +

    Record Transcode
     Profile: + +   

    Video
     
     Breite:Höhe:
     Codec: + + Bitrate: + +
     Scale: + +   
    Audio 
     Codec: + + Bitrate: + +
     Channels: + +   
    Mux 
     Mux: + +   
    +
    + + + diff --git a/src/nhttpd/web/Y_Live_Transcode.yhtm b/src/nhttpd/web/Y_Live_Transcode.yhtm new file mode 100644 index 000000000..bb2ecdf4c --- /dev/null +++ b/src/nhttpd/web/Y_Live_Transcode.yhtm @@ -0,0 +1,174 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Transcode-Broadcast

    Video
     
     Width:Height:
     Codec: + + Bitrate: + +
     Scale: + +   

    Audio
     
     Codec: + + Bitrate: + +
     Channels: + +   

    Access
     
     Type: + + Mux: + +
     IP:Port: + +
      +
    + Display on +   +
    +
    + + diff --git a/src/nhttpd/web/Y_Main.css b/src/nhttpd/web/Y_Main.css new file mode 100644 index 000000000..43054a4b4 --- /dev/null +++ b/src/nhttpd/web/Y_Main.css @@ -0,0 +1,678 @@ +body +{ + font-family: Arial, Tahoma, Verdana, Geneva, Helvetica, sans-serif; + background-color: White; + margin-left: 0px; + margin-right: 0px; + margin-top: 0px; + margin-bottom: 0px; + padding:0; + font-size:10pt; + color: #555555; +} + +img {border:0;} +button {font-family:Arial,Sans-Serif; color:#555555;} +input {font-family:Arial,Sans-Serif; color:#555555;} +select {font-family:Arial,Sans-Serif; color:#555555;} +form {margin: 0; padding: 0;} + +* html .boxhead h2 {height: 1%;} /* For IE 5 PC */ + +a { + color: #555555; + font-size:10pt; + text-decoration:none; +} + +td { + color: #555555; + font-size:10pt; +} + +tr { + color: #555555; + font-size:10pt; +} + +/* primary Navigation */ +.y_menu_prim_box { + margin: 0; + width: 100%; + background: url(/images/body-trans-r.gif) no-repeat bottom right; + font-size: 100%; +} +.y_menu_prim_box_head { + background: url(/images/head-trans-r.gif) no-repeat top right; + margin: 0; + padding: 0; + text-align: center; +} +.y_menu_prim_box_head h2 { + background: url(/images/head-trans-l.gif) no-repeat top left; + margin: 0; + padding: 5px 0px 5px; + color: white; + font-size: 0; + line-height: 0.5em; +} +.y_menu_prim_box_body { + background: url(/images/body-trans-l.gif) no-repeat bottom left; + margin: 0; + padding-bottom: 8px; + padding-left: 20px; + padding-right: 20px; +} +.y_menu_item +{ + margin: 0; + display: block; + background-repeat: no-repeat; +} +.y_menu_prim +{ + background-color: transparent; + padding: 0em 0em 0em 0em; + white-space: nowrap; + list-style: none; + margin: 0; + height: auto; + line-height: normal; +} +.y_menu_prim li +{ + display: inline; +} +.y_menu_prim_item +{ + margin: 0; + background-repeat: no-repeat; + background-position: 0 1px; + text-align:center; + font-weight: bold; + font-size: 12pt; + text-decoration: none; + height: auto; + padding: 0em 0.5em; +} +.y_menu_prim li a { +} +.y_menu_prim li.selected a +{ + background-color: #dee7ec; + border: 1px solid #6D96A9; + color: #555555; +} +.y_menu_prim li a:hover +{ + color: #FF6600; +} + +/* secondary Navigation */ +.y_menu_sec_box { + margin: 0; + width: 100%; + background: url(/images/body-trans-r.gif) no-repeat bottom right; + font-size: 100%; +} +.y_menu_sec_box_head { + background: url(/images/head-orange-r.gif) no-repeat top right; + margin: 0; + padding: 0; +} +.y_menu_sec_box_head h2 { + background: url(/images/head-orange-l.gif) no-repeat top left; + + margin: 0; + padding: 12px 30px 3px 30px; + color: white; + font-weight: bold; + font-size: 1.2em; + line-height: 1.5em; + text-align: left; +} +.y_menu_sec_box_body { + background: url(/images/body-trans-l.gif) no-repeat bottom left; + margin: 0; + padding-top: 5px; + padding-left: 6px; + padding-bottom: 20px; + padding-right: 6px; + +} +.y_menu_sec { + display: block; + background-repeat: no-repeat; + margin: 0px; + +} +.y_menu_sec ul { + padding: 0em 0em 0em 0em; + list-style: none; + margin: 0px; +} + +.y_menu_sec li { + text-align: left; + margin-bottom: 1px; + padding-left: 20px; + color: #555555 +} +.y_menu_sec li.disabled +{ + height: auto; + margin-bottom: 0.1em; + color: #808080; + text-decoration:line-through; +} +.y_menu_sec li a +{ + text-decoration: none; + font-size: 12px; +} +.y_menu_sec li.plain a +{ + color: #555555 +} +.y_menu_sec li.disabled a +{ +} +.y_menu_sec li a:hover +{ + color: #FF6600; +} +.y_menu_sec_section { + margin: 10px 0px 0px 0px; + padding: 5px 2px 5px 10px; + background-color: #FFCC99; + font-size: 10pt; + font-weight: bold; +} + +/*work*/ +.work_box { + margin: 0; + background: url(/images/body-trans-r.gif) no-repeat bottom right; + font-size: 100.01%; +} +.work_box_head { + background: url(/images/head-blue-r.gif) no-repeat top right; + margin: 0; + padding: 0; + text-align: center; +} +.work_box_head h2 { + background: url(/images/head-blue-l.gif) no-repeat top left; + margin: 0; + padding: 12px 30px 3px 30px; + color: white; + font-weight: bold; + font-size: 1.2em; + line-height: 1.5em; + text-align: left; +} +.work_box_head_h2 { + background: url(/images/head-blue-l.gif) no-repeat top left; + margin: 0; + padding: 12px 30px 3px 30px; + color: white; + font-weight: bold; + font-size: 1.2em; + line-height: 1.5em; + text-align: left; +} +.work_box_body { + background: url(/images/body-trans-l.gif) no-repeat bottom left; + margin: 0; + padding: 5px 30px 10px 30px; +} +.work_box_help { + float:right; + width:3em; + text-align:right; + font-size: 100.01%; +} +.work_box_help a { + float:right; + width:3em; + color: white; + font-weight: bold; + font-size: 1.2em; + text-align:right; +} +.y_menu +{ + font-size: 10pt; + text-align: center; + border: 1px solid #6D96A9; + border-collapse: collapse; + background: #F0FAFF; + border-spacing: 1px; + height: auto; +} +.y_menu a:hover +{ + text-decoration:underline; + font-weight:bold; +} +.y_menu_logo +{ + background-image: url(images/tuxbox3.gif); + background-position: center; + background-repeat: no-repeat; +} +.y_audio_bar +{ + background: #6D96A9; +} +.y_head +{ + font-weight: bold; + color: #555555; + font-size: 14pt; + clear:both; +} +.y_work_box +{ + border-spacing: 1px; + padding-left: 20px; + padding-right: 20px; + padding-top: 10px; + padding-bottom: 10px; + float:left; + width: auto; + color: #555555; + display:block; +} +.y_work_table +{ + border: 1px dashed #2188e0; + border-collapse: collapse; + background: white; + color: #555555; + margin:0; + padding:0; +} +.y_live_box +{ + border-top: 1px solid #6D96A9; + font-size: 8pt; + +} +/*wait box*/ +.y_wait_box { + margin: 0; + background: url(/images/body-trans-r.gif) no-repeat bottom right; + font-size: 100%; + position : absolute; + left : 100px; + top : 100px; + visibility : hidden; + z-index : 500; +} +.y_wait_box_visible { + margin: 0; + background: url(/images/body-trans-r.gif) no-repeat bottom right; + font-size: 100%; + position : absolute; + left : 100px; + top : 100px; + visibility : visible; + z-index : 500; +} +.y_wait_box_head { + background: url(/images/head-orange-r.gif) no-repeat top right; + margin: 0; + padding: 0; +} +.y_wait_box_head h2 { + background: url(/images/head-orange-l.gif) no-repeat top left; + + margin: 0; + padding: 12px 30px 3px 30px; + color: white; + font-weight: bold; + font-size: 1.2em; + line-height: 1.5em; + text-align: center; +} +.y_wait_box_body { + background: url(/images/body-trans-l.gif) no-repeat bottom left; + margin: 0; + padding-top: 5px; + padding-left: 16px; + padding-bottom: 20px; + padding-right: 16px; + +} +.y_wait_box_main { + color: #666666; + font-weight: bold; + text-align: center; +} +/**/ +.y_live_table +{ + color: #555555; + padding: 0; + margin: 0; + border: 0; +} +.y_live_button +{ +/* font-size: 8pt; + height: 22px;*/ + padding: 0; + color: #555555; + margin:0; + +} +.y_live_bouquets +{ + font-size: 8pt; + height: 20px; + width: 300px; + color: #555555; +} +.y_live_channels +{ + font-size: 8pt; + height: 20px; + width: 300px; + color: #555555; +} +.y_live_audio_pids +{ + font-size: 8pt; + height: 20px; + width: 70px; + color: #555555; +} + +.y_form_table +{ + font-size: 10pt; + border: 0px; + border-spacing: 0px; + padding: 0; + margin:0; +} + +.y_invisible_table +{ + border: 0px; + border-spacing: 0px; + padding: 0; +} +.y_topline +{ + border-top: 1px solid #2188e0; +} +.y_form_header +{ + font-size : 12pt; + font-weight:bold; + border-bottom : 1px solid #6cadfd; + color : #0066cc; + margin:0; +} +.y_form_section +{ + font-size : 10pt; + font-weight:bold; + border-bottom : 1px solid #aaaaaa; + color : #555555; +} +.y_text_boxcontrol_button +{ + padding : 1px; + border : #FF8e42 solid 1px; +} +.y_text_boxcontrol_button a:hover +{ + background:#FF8e42; +} +.y_text_boxcontrol_table { + width:114px; + height: 22px; + background : url('/images/audiobar.gif') no-repeat 1px 0px; + padding : 1px; + border : #FF8e42 solid 1px; +} +.y_epg_info { + border: 1px solid #CCCCCC; + display:block; +} +.y_epg_info #description { + font-weight:bold; +} +.y_epg_info #info2 { + font-size:8pt; +} +.channel_logos +{ + width: 40px; + height: 21px; +} +.ep_bouquet { + margin-bottom: 2px; + position:relative; +} +.ep_bouquet_name { + border : #BBBBBB solid 1px; + width : 100px; + height: 1.3em; + overflow:hidden; + padding-left: 1px; + padding-right: 1px; +} +.ep_bouquet_item { + border : #BBBBBB solid 1px; + overflow:hidden; + height: 1.3em; + padding-left: 1px; + padding-right: 1px; +} +.ep_time_bar { + width:100%; + margin-bottom: 2px; + position:relative; +} +.ep_time_bar_item { + border : #999999 solid 1px; + height: 1.3em; + padding-left: 1px; + padding-right: 1px; + overflow:hidden; +} +.ep_info_desc { + font-weight:bold; + font-size:10pt; +} +.ep_info_info1 { + font-size:8pt; +} +.ep_info_info2 { + font-size:10pt; +} +#epg_info { + border : #999999 solid 1px; + visibility:hidden; +} +#epg_plus { + width:auto; + overflow:auto; +} +#epg_time { + font-size: 8pt; + height: 20px; +} +/* bouquet*/ +.bouquetlist +{ + border-collapse: collapse; + color: #436976; + padding: 3px; +} +tr.bouquetlist +{ +} +/*channels*/ +.bouquetitemlist +{ + border-collapse: collapse; + padding: 0px; + margin: 0px; + width: 100%; +} +.a +{ + background-color: #F0FAFF; + font-family:Arial,Sans-Serif; + font-size:11pt; + border-spacing: 0px; + padding: 0px; + padding-left: 10px; + padding-right: 20px; + margin:0px; +} +.aepg +{ + background-color: #F0FAFF; + font-weight:normal; + font-size:10pt; + padding-left: 10px; +} +.b +{ + background-color: white; + font-size:11pt; + border-spacing: 0px; + padding: 0px; + padding-left: 10px; + padding-right: 20px; + margin:0px; +} +.bepg +{ + background-color: white; + font-weight:normal; + font-size:10pt; + padding-left: 10px; +} +a:hover.clist +{ + font-size:10pt; + text-decoration:underline; +} +a:hover.clistsmall +{ + font-size:10pt; + text-decoration:underline; +} +.c +{ + background-color:#dee7ec; + font-weight:bold; + font-size:11pt; + padding-left: 10px; + padding-right: 20px; + margin:0px; +} +.cepg +{ + background-color:#dee7ec; + font-weight:normal; + font-size:10pt; + padding-left: 10px; +} +.cslider_table +{ + height: 10px; + border: 1px solid #436976; + width: 30px; + border-spacing: 0px; + padding: 0px; + border-collapse: collapse; +} +.cslider_used +{ + background-color: #6D96A9; + height: 10px; +} +.cslider_free +{ + background-color: #EAEBFF; + height: 10px; +} +/*EPG*/ +.epg +{ + font-weight: bold; + color: #555555; + font-size: 12pt; + clear:both; + +} +.epglist +{ + border-collapse: collapse; + border-spacing: 0px; + padding: 0px; +} +a:hover.elist +{ + font-size:10pt; + text-decoration:underline; + font-weight:bold; +} + +/*Timer*/ +.timer +{ + border-collapse: collapse; + padding: 0px; + margin: 0px; +} +a.timer +{ + text-decoration:underline; +} +.atimer +{ + background-color: #F0FAFF; + font-weight:normal; + padding-left: 5px; + padding-right: 5px; +} +.atimer td +{ + text-align: center; +} +.btimer +{ + background-color:White; + font-weight:normal; + padding-left: 5px; + padding-right: 5px; +} +.btimer td +{ + text-align: center; +} +.ctimer +{ + background-color:#dee7ec; + font-weight:normal; + padding-left: 5px; + padding-right: 5px; +} +.ctimer td +{ + text-align: center; +} +.blist +{ + white-space: nowrap; + height: 20px; +} diff --git a/src/nhttpd/web/Y_Menue.yhtm b/src/nhttpd/web/Y_Menue.yhtm new file mode 100644 index 000000000..2db143c43 --- /dev/null +++ b/src/nhttpd/web/Y_Menue.yhtm @@ -0,0 +1,110 @@ +{=var-set:cancache=true=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +

     

    +
    + + + + + + + + +
    Tuxbox Wiki + yWeb +
    + Version {=ini-get:Y_Version.txt;version=} +
    + + +   +   + + +   + + {=ini-get:/var/tuxbox/config/Y-Web.conf;yweb_box_name=} + 00:00 +
    +
    +
    + + + + diff --git a/src/nhttpd/web/Y_Settings.yhtm b/src/nhttpd/web/Y_Settings.yhtm new file mode 100644 index 000000000..3f4ec1dbd --- /dev/null +++ b/src/nhttpd/web/Y_Settings.yhtm @@ -0,0 +1,17 @@ +{=var-set:cancache=yPyes=} + + + + + + + + + + <body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0" marginwidth="0" marginheight="0"> + <p>Diese Seite verwendet Frames. Frames werden von Ihrem Browser aber nicht unterst&uuml;zt.</p> + </body> + + + + diff --git a/src/nhttpd/web/Y_Settings_Backup.yhtm b/src/nhttpd/web/Y_Settings_Backup.yhtm new file mode 100644 index 000000000..9f2b61f44 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_Backup.yhtm @@ -0,0 +1,67 @@ +{=var-set:cancache=yPConf=} +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Settings=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Backup_.26_Restore=}{=var-set:menu=Backup & Restore=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + + + + + + +
    Backup
    + +
    Restore (Warning: box will REBOOT after restore!)
    +
    + tar-File:  
    + +

    +
    +
    + +
    +
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Settings_Live.yhtm b/src/nhttpd/web/Y_Settings_Live.yhtm new file mode 100644 index 000000000..df51355f3 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_Live.yhtm @@ -0,0 +1,69 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    + + + + + + + + + + + + + +

    VLC Settings (IE & Mozilla >= 0.8.6.1)
    Deinterlace + Off  + On +
    http caching
    UDP as default + Off  + On +
    +
    + + + + +
    + + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} + diff --git a/src/nhttpd/web/Y_Settings_Menue.yhtm b/src/nhttpd/web/Y_Settings_Menue.yhtm new file mode 100644 index 000000000..68f99c2d6 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_Menue.yhtm @@ -0,0 +1,71 @@ +{=var-set:cancache=yPConf=} +{=include-block:Y_Blocks.txt;head=} + + +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} +
    +

    Settings

    +
    +
    General
    +
    + +
    +
    Neutrino
    +
    + +
    +
    Plugins
    +
    +
      + {=if-empty:{=var-get:management=}~ +
    • VNC
    • + ~ +
    • VNC
    • + =} +
    +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Settings_Skin.yhtm b/src/nhttpd/web/Y_Settings_Skin.yhtm new file mode 100644 index 000000000..deeed572b --- /dev/null +++ b/src/nhttpd/web/Y_Settings_Skin.yhtm @@ -0,0 +1,36 @@ + + + + + + + + + +
    +
    Skin {=var-set:help_url=Neutrino:yWeb:Settings#Skins=}{=include-block:Y_Blocks.txt;wiki_help=}
    +
    +
    + + + + + + + +
    Skin 
    +
    + +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_Timer.yhtm b/src/nhttpd/web/Y_Settings_Timer.yhtm new file mode 100644 index 000000000..d0394d476 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_Timer.yhtm @@ -0,0 +1,60 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Timer_Settings=}{=var-set:menu=Timer Settings=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    tvinfo.de
    Username
    Passwort

    klack.de
    (& will be converted to ";" on save)
    personal RSS TV-Planer URL

    Sender name
     
    +
    + + + +
    +
    +
    + + +{=include-block:Y_Blocks.txt;management_check_bottom=} + diff --git a/src/nhttpd/web/Y_Settings_VNC.yhtm b/src/nhttpd/web/Y_Settings_VNC.yhtm new file mode 100644 index 000000000..ba0cf69ff --- /dev/null +++ b/src/nhttpd/web/Y_Settings_VNC.yhtm @@ -0,0 +1,58 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#VNC=}{=var-set:menu=VNC=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + +
    Server
    Port
    Passwort
    Scaling + +
    +
    + + + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_automount.yhtm b/src/nhttpd/web/Y_Settings_automount.yhtm new file mode 100644 index 000000000..f19396b13 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_automount.yhtm @@ -0,0 +1,182 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#AutoMount=}{=var-set:menu=AutoMount=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Mountname
    Typ + NFS  + CIFS  + +
    IP
    Directory
    Option
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_automount_liste.yhtm b/src/nhttpd/web/Y_Settings_automount_liste.yhtm new file mode 100644 index 000000000..92c860bf0 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_automount_liste.yhtm @@ -0,0 +1,55 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#AutoMount=}{=var-set:menu=AutoMount=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    +
    + {=script:Y_Tools automount_list {=if-file-exists:/var/etc/auto.net~/var/etc/auto.net~/etc/auto.net=}=} + + + + + + +
    +
    +
    +{=include-block:Y_neutrino_Blocks.txt;neutrino_form_helpbox=} + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_mount.yhtm b/src/nhttpd/web/Y_Settings_mount.yhtm new file mode 100644 index 000000000..4504d9b73 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_mount.yhtm @@ -0,0 +1,87 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#Mounts=}{=var-set:menu=Mount {=R1=}=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Typ + NFS  + CIFS  + FTPFS +
    IP
    Directory
    Local Directory
    MAC
    Option 1
    Option 2
    Automount + Off  + On +
    Username
    Passwort
    +
    + + + + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_mount_liste.yhtm b/src/nhttpd/web/Y_Settings_mount_liste.yhtm new file mode 100644 index 000000000..907fe9664 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_mount_liste.yhtm @@ -0,0 +1,45 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#Mounts=}{=var-set:menu=Mount=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    +
    + {=func:mount-get-list=} + + + + + +
    +
    +
    +{=include-block:Y_neutrino_Blocks.txt;neutrino_form_helpbox=} + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_neutrino_forms.yhtm b/src/nhttpd/web/Y_Settings_neutrino_forms.yhtm new file mode 100644 index 000000000..2987db99d --- /dev/null +++ b/src/nhttpd/web/Y_Settings_neutrino_forms.yhtm @@ -0,0 +1,2 @@ +{=var-set:neutrino={=form=}=} +{=include-block:Y_neutrino_Blocks.txt;neutrino_form=} diff --git a/src/nhttpd/web/Y_Settings_nhttpd.yhtm b/src/nhttpd/web/Y_Settings_nhttpd.yhtm new file mode 100644 index 000000000..b8b60d0e8 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_nhttpd.yhtm @@ -0,0 +1,116 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#WebServer=}{=var-set:menu=WebServer (nhttpd)=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Authentification
    User
    Passwort
    Client ohne Authentification
    Authentification + OFF  + ON +
    General
    Port +
    Changed port will only be valid after a restart. +
    Hosted Web (Mount)
    Logos & Extras
    Directory ("web"=nur URL)
    URL
    Server
    IPs Not keep-alive
    (like JtG, comma seperated)
    +
    CacheCache info 
    Server ConfigurationServer Config
    +
    + + + +
    +
    +
    + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Settings_ucodes.yhtm b/src/nhttpd/web/Y_Settings_ucodes.yhtm new file mode 100644 index 000000000..aa7ec6ba9 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_ucodes.yhtm @@ -0,0 +1,61 @@ +{=include-block:Y_Blocks.txt;head=} + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#ucodes=}{=var-set:menu=Ucodes=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + + + + + + +
    Upload
    +
    +

    + camalpha.bin
    + avia500.ux
    + avia600.ux
    + ucode.bin
    + +   + +

    +
    +
    Download
    + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_umount_liste.yhtm b/src/nhttpd/web/Y_Settings_umount_liste.yhtm new file mode 100644 index 000000000..993fb1970 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_umount_liste.yhtm @@ -0,0 +1,38 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#Mounts=}{=var-set:menu=UnMount=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + {=func:umount_get_list=} + + + + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Settings_wol.yhtm b/src/nhttpd/web/Y_Settings_wol.yhtm new file mode 100644 index 000000000..783b152d7 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_wol.yhtm @@ -0,0 +1,53 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#Wake_on_LAN=}{=var-set:menu=Wake on LAN=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + +
    Wake on Lan MAC 1 ({=ini-get:/var/tuxbox/config/Y-Web.conf;wol_mac_1=})
    Wake on Lan MAC 2 ({=ini-get:/var/tuxbox/config/Y-Web.conf;wol_mac_2=})
    Wake on Lan MAC 3 ({=ini-get:/var/tuxbox/config/Y-Web.conf;wol_mac_3=})
    +
    + +
    +
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Settings_yWeb.yhtm b/src/nhttpd/web/Y_Settings_yWeb.yhtm new file mode 100644 index 000000000..cd1412b32 --- /dev/null +++ b/src/nhttpd/web/Y_Settings_yWeb.yhtm @@ -0,0 +1,78 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Values are saved (Save).=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#yWeb=}{=var-set:menu=yWeb=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Slave Box IP (2. dbox)
    Management IP 1
    Management IP 2
    Wake on Lan MAC 1
    Wake on Lan MAC 2
    Wake on Lan MAC 3
    VLC Recording path
    Box-name
    Box-name/colour
    +
    + + + +
    +
    +
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} + diff --git a/src/nhttpd/web/Y_Settings_zapit.yhtm b/src/nhttpd/web/Y_Settings_zapit.yhtm new file mode 100644 index 000000000..050776c2e --- /dev/null +++ b/src/nhttpd/web/Y_Settings_zapit.yhtm @@ -0,0 +1,56 @@ +{=include-block:Y_Blocks.txt;head=} + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#zapit=}{=var-set:menu=zapit=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + + + + + + +
    Upload
    +
    +

    + bouquets.xml
    + services.xml
    + ubouquets.xml
    + +   + +

    +
    +
    Download
    + +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_StreamInfo.yhtm b/src/nhttpd/web/Y_StreamInfo.yhtm new file mode 100644 index 000000000..eebca22e1 --- /dev/null +++ b/src/nhttpd/web/Y_StreamInfo.yhtm @@ -0,0 +1,73 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + +
    +{=func:get_current_stream_info=} +
    Streaminfo ({=ServiceName=})
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Resolution: {=VideoFormat=}
    Relation: 
    {=AspectRatio=}
    Bitrate: 
    {=BitRate=} bit/sec
    Framerate: 
    {=FPS=} fps
    Audiotype: 
    {=AudioType=}
    onid: 
    {=onid=}
    sid: 
    {=sid=}
    tsid: 
    {=tsid=}
    frequency: 
    {=tsfrequency=} ({=polarisation=})
    vpid: 
    {=vpid=}
    apid: 
    {=apid=}
    vtxtpid: 
    {=vtxtpid=}
    + +
    + + diff --git a/src/nhttpd/web/Y_Timer_Edit.yhtm b/src/nhttpd/web/Y_Timer_Edit.yhtm new file mode 100644 index 000000000..dc309370e --- /dev/null +++ b/src/nhttpd/web/Y_Timer_Edit.yhtm @@ -0,0 +1,166 @@ +{=func:set_timer_form {=typ=} {=tid=}=} +{=include-block:Y_Blocks.txt;head_no_charset=} + + + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live#Timer=}{=var-set:menu=Timer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{=if-empty:{=timer_recordingDir=}~~ + + + + +=} +
    Timer
    Type +
    Times
    Alarm-Date + .  + .  +  Time  +  :  +
    Stop-Datum + .  + .  +  Time  +  :  +
    Repeat +
    Values
    List: +
    +
    + + + +
    +
    +
    + + diff --git a/src/nhttpd/web/Y_Timer_List.yhtm b/src/nhttpd/web/Y_Timer_List.yhtm new file mode 100644 index 000000000..cb0f14e75 --- /dev/null +++ b/src/nhttpd/web/Y_Timer_List.yhtm @@ -0,0 +1,55 @@ +{=include-block:Y_Blocks.txt;head_no_charset=} + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#Timer=}{=var-set:menu=Timer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + + + + + + + + + + +{=var-set:row= + + + + + + + + + +=} +{=func:get_timer_list {=var-get:row=}=} + + + + + + + +
    Alarm timeStop timeRepeatRepeatsTypeDescription  
    %s%s%s%s%s%s
    +
    + + del Timer + + + edit Timer +
    + + Update + + + new Timer +
    +
    +
    + diff --git a/src/nhttpd/web/Y_Timer_Menue.yhtm b/src/nhttpd/web/Y_Timer_Menue.yhtm new file mode 100644 index 000000000..9b82c5b23 --- /dev/null +++ b/src/nhttpd/web/Y_Timer_Menue.yhtm @@ -0,0 +1,24 @@ +{=include-block:Y_Blocks.txt;head=} + +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} + +
    +

    Timer

    +
    +
    + +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Tools_Bootlogo.yhtm b/src/nhttpd/web/Y_Tools_Bootlogo.yhtm new file mode 100644 index 000000000..9a740cdcb --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Bootlogo.yhtm @@ -0,0 +1,36 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Logo wird hochgeladen (Upload).=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Boot_Logo=}{=var-set:menu=Bootlogo / LCD=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    +

    + Achtung
    Das Boot-Logo muss schon im Zielformat vorliegen!

    + Boot Logo
    + Boot LCD Logo
    +
    + +

    +
    +
    +
    + + diff --git a/src/nhttpd/web/Y_Tools_Boxcontrol.yhtm b/src/nhttpd/web/Y_Tools_Boxcontrol.yhtm new file mode 100644 index 000000000..32a027d8d --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Boxcontrol.yhtm @@ -0,0 +1,77 @@ +{=var-set:cancache=yPyes=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Control=}{=var-set:menu=Control=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    Coolstream HD1Standby Mode
    + + + + + + +
    PlaybackRecording
    + + + + + + + +
    Remote ControlLive Lock
    + + + + + + +
    +
    +
    +
    +
    + + + + diff --git a/src/nhttpd/web/Y_Tools_Check_Install.yhtm b/src/nhttpd/web/Y_Tools_Check_Install.yhtm new file mode 100644 index 000000000..1aba765e8 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Check_Install.yhtm @@ -0,0 +1,148 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    +
    + {=var-set:help_url=Neutrino:yWeb:Tools#Check_Install=}{=var-set:menu=Check Install=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    +
    +   +
    + +
    + Check Results
    + + + + + + + + + +
     AktionStatus
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Tools_Cmd.yhtm b/src/nhttpd/web/Y_Tools_Cmd.yhtm new file mode 100644 index 000000000..d3cff727f --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Cmd.yhtm @@ -0,0 +1,91 @@ +{=var-set:cancache=yPConf=} +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Tools#Command_Shell=}{=var-set:menu=Command Shell=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + +
    + +
    + + + + + + + + + + + +
    append output (only IE scrollen) + +
    Path: 
    Command: +
    +
    +
    +
    + + + + + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Tools_Flash_Menue.yhtm b/src/nhttpd/web/Y_Tools_Flash_Menue.yhtm new file mode 100644 index 000000000..dbc0ce317 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Flash_Menue.yhtm @@ -0,0 +1,101 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Image will be generated (create image).=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    +
    {=var-set:help_url=Neutrino:yWeb:Tools#Image=}{=var-set:menu=Image=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + Partition
    +
    + {=func:get_partition_list=} + + + + + + +
    +
    +
    +
     
    +
    +

    Instruction

    +
    + + It is usefull to reboot the Box before flashing.
    + +
    +
    +
     
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Tools_Flash_Upload.yhtm b/src/nhttpd/web/Y_Tools_Flash_Upload.yhtm new file mode 100644 index 000000000..da5b5ecbb --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Flash_Upload.yhtm @@ -0,0 +1,162 @@ +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:fcp={=if-file-exists:/sbin/fcp~true~{=if-file-exists:/var/bin/fcp~true~{=if-file-exists:/bin/fcp~true~false=}=}=}=} +{=var-set:wait_text=Image wird hochgeladen (upload image).=}{=include-block:Y_Blocks.txt;snip_wait=} + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Tools#Image=}{=var-set:menu=Image flashen=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + Partition mtd {=mtd=}
    {=mtd_text=}
    + {=if-equal:{=var-get:fcp=}~true~ +
    +

    +
    + Demomodus  +
    + +

    +
    + ~ +




    fcp ist nicht installiert!

    + =} +
    +
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Tools_Info_Menue.yhtm b/src/nhttpd/web/Y_Tools_Info_Menue.yhtm new file mode 100644 index 000000000..815356ce2 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Info_Menue.yhtm @@ -0,0 +1,47 @@ +{=var-set:cancache=yPyes=} +{=include-block:Y_Blocks.txt;head=} + + + +
    +
    +
    {=var-set:help_url=Neutrino:yWeb#Info=}{=var-set:menu=Info=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + +
    Linux
    + + + +
    Coolstream HD1
    + + + + +
    proc
    + + + + + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Tools_Installer.yhtm b/src/nhttpd/web/Y_Tools_Installer.yhtm new file mode 100644 index 000000000..1069c1a1a --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Installer.yhtm @@ -0,0 +1,42 @@ +{=var-set:cancache=yPConf=} +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Tools#yInstaller=}{=var-set:menu=yInstaller=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + tar-File:  
    + +

    +
    +
    + +
    +
    +
    + + + +{=include-block:Y_Blocks.txt;management_check_bottom=} diff --git a/src/nhttpd/web/Y_Tools_Menue.yhtm b/src/nhttpd/web/Y_Tools_Menue.yhtm new file mode 100644 index 000000000..c7f15ce2d --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Menue.yhtm @@ -0,0 +1,48 @@ +{=include-block:Y_Blocks.txt;head=} + + +{=var-set:automount={=if-file-exists:/sbin/automount~true~{=if-file-exists:/var/bin/automount~true~false=}=}=} +{=var-set:management={=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip;{=func:get_request_data client_addr=}=}~1~=}{=if-equal:{=func:get_request_data client_addr=}~{=ini-get:/var/tuxbox/config/Y-Web.conf;management_ip2=}~1~=}=} + +
    +

    Tools

    +
    +
    Tools
    +
    +
      + {=if-empty:{=var-get:management=}~ + +
    • Mounts
    • +
    • Wake on LAN
    • + ~ + {=if-empty:{=var-get:automount=}~ + + ~ + + =} +
    • Mounts
    • +
    • Wake on LAN
    • + =} +
    • Check Install
    • +
    +
    +
    Expert
    +
    + +
    +
    +
    + + + diff --git a/src/nhttpd/web/Y_Tools_NetStream.yhtm b/src/nhttpd/web/Y_Tools_NetStream.yhtm new file mode 100644 index 000000000..23e6ec8e2 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_NetStream.yhtm @@ -0,0 +1,125 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#NetStream=}{=var-set:menu=Net Stream=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +     VLC Plugin Test +
    + + + + + + + + + + + + + +
    + MRL: + + +
    + {=if-equal:{=browser=}~ie~ + + + + + + The VideoLan Client ActiveX is not installed.
    + You need VideoLan Client V0.8.2 or higher.
    + Install with Option "ActiveX". +
    +
    + + + + + + + + ~ + + +
    + + + + + + + =} +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Tools_Rcsim.yhtm b/src/nhttpd/web/Y_Tools_Rcsim.yhtm new file mode 100644 index 000000000..74631b585 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Rcsim.yhtm @@ -0,0 +1,19 @@ +{=var-set:cancache=yPyes=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=include-block:Y_Blocks.txt;remote=} + + diff --git a/src/nhttpd/web/Y_Tools_Timer_Sync.yhtm b/src/nhttpd/web/Y_Tools_Timer_Sync.yhtm new file mode 100644 index 000000000..5e7020146 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_Timer_Sync.yhtm @@ -0,0 +1,385 @@ +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=get timer.=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:Live_Timer#Timer_Sync=}{=var-set:menu=Timer Sync=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + Sync Timer + + + + + + + +
    + TVInfo
    + Klack + +
    + Debugging + +
    + Recording Directory: +
    Settings within Settings-Timer
    +   + +
    +
    +
    + Timer Preview List + + + + + + + + + + +
    StartTimeEndeTimeSenderProgramQuelle
    +   + +
    +
    +
    + Log
    + + + + + + + + + +
     ActionStatus
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/src/nhttpd/web/Y_Tools_fbshot.yhtm b/src/nhttpd/web/Y_Tools_fbshot.yhtm new file mode 100644 index 000000000..0d7df60cc --- /dev/null +++ b/src/nhttpd/web/Y_Tools_fbshot.yhtm @@ -0,0 +1,67 @@ +{=var-set:cancache=yPInstall=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Snapshot taken=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#OSD_Screenshot=}{=var-set:menu=OSD Screenshot=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + + {=var-set:dboxshot={=if-file-exists:/bin/dboxshot~true~{=if-file-exists:/var/bin/dboxshot~true~false=}=}=} + {=if-equal:{=var-get:dboxshot=}~true~ + + =} + +  Filename: +
    + +
    +
    + + diff --git a/src/nhttpd/web/Y_Tools_remote_osd.yhtm b/src/nhttpd/web/Y_Tools_remote_osd.yhtm new file mode 100644 index 000000000..0ace138e7 --- /dev/null +++ b/src/nhttpd/web/Y_Tools_remote_osd.yhtm @@ -0,0 +1,60 @@ +{=var-set:cancache=yPInstall=} +{=var-set:dboxshot={=if-file-exists:/bin/dboxshot~/bin/dboxshot~{=if-file-exists:/var/bin/dboxshot~/var/bin/dboxshot~/var/bin/dboxshot=}=}=} +{=include-block:Y_Blocks.txt;head=} + + + + +{=var-set:wait_text=Snapshot taken=}{=include-block:Y_Blocks.txt;snip_wait=} +
    +
    + {=var-set:help_url=Neutrino:yWeb:BoxControl#Remote_OSD=}{=var-set:menu=Remote & OSD=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    + {=if-empty:{=var-get:dboxshot=}~~ + + =} + + + + + + +
    {=include-block:Y_Blocks.txt;remote=}
    + +
    +
    + + + diff --git a/src/nhttpd/web/Y_VLC.js b/src/nhttpd/web/Y_VLC.js new file mode 100644 index 000000000..4b590dba1 --- /dev/null +++ b/src/nhttpd/web/Y_VLC.js @@ -0,0 +1,246 @@ +/* VLC abstraction by yjogol@online.de + $Date: 2007/02/21 17:38:48 $ + $Revision: 1.1 $ +*/ +/*ie1=ActiveC, moz1=Mozilla<0.8.5.1, moz2>= 0.8.5.1*/ +var CyVLC = function(_id, masterid, width, height) { + this.initialize(_id, masterid, width, height); +}; +CyVLC.prototype = { + id : "vlc", + vlc : "", + plugin : "ie1", + version_string : "", + version_level1 : 0, + version_level2 : 0, + version_level3 : 0, + version_level4 : 0, + options : [], + + c_masterid : "vlc_view", + c_width : 384, + c_height: 288, + c_loop : false, + c_show_display : true, + c_auto_play : false, + + initialize : function(_id, masterid, width, height) { + this.id = _id; + this.c_masterid = masterid; + this.c_width = width; + this.c_height = height; + if(!is_ie) { + this.version_string = this._get_version(); + this._generate_sub_versions(); + this._determine_plugin_generation(); + } + this.insert_control(); + this.vlc = id(_id); + this.set_resolution(this.c_width, this.c_height); + if(is_ie) { + this.version_string = this._get_version(); + this._generate_sub_versions(); + this._determine_plugin_generation(); + } + }, + _get_version : function() { + if(is_ie) + { + var vstr = this.vlc.VersionInfo; + var words = vstr.split(" "); + return words[0]; + } + else + if(navigator.plugins) { + var plug = navigator.plugins['VLC multimedia plugin']; + if(typeof plug == 'undefined') + var plug = navigator.plugins['VLC Multimedia Plugin']; + var ex = /^.*[vV]ersion [\"]*([^ \"]*)[\"]*.*$/; + var ve = ex.exec(plug.description); + if(ve[1]) + return ve[1]; + else + return "0.0.0"; + } + else + return "0.0.0"; + }, + _generate_sub_versions : function() { + if(this.version_string == "") + return + var ex = /([^\.]*)[\.]*([^\.]*)[\.]*([^\.-]*)[\.-]*([^\.]*).*$/; + var ve = ex.exec(this.version_string); + if(ve.length >1) this.version_level1 = ve[1]; + if(ve.length >2) this.version_level2 = ve[2]; + if(ve.length >3 && ve[3] != "") this.version_level3 = ve[3]; + if(ve.length >4 && ve[4] != "") this.version_level4 = ve[4]; + }, + _determine_plugin_generation : function() { + if(is_ie) + this.plugin = "ie1"; + else + if(this.version_level1 <= "0" && this.version_level2 <= "8" && this.version_level3 <= "5") + this.plugin = "moz1"; + else + this.plugin = "moz2"; + }, + set_actual_mrl : function(mrl) { + switch(this.plugin) { + case "ie1": + this.vlc.playlistClear(); + this.vlc.addTarget(mrl, this.options, 4+8, -666); + break; + case "moz2": + this.vlc.playlist.clear(); + this.vlc.playlist.add(mrl, null, this.options); + break; + default: + this.vlc.clear_playlist(); + this.vlc.add_item(mrl); + break; + } + }, + play : function() { + switch(this.plugin) { + case "moz2": this.vlc.playlist.play();break; + default: this.vlc.play();break; + } + }, + stop : function() { + switch(this.plugin) { + case "moz2": this.vlc.playlist.stop();break; + default: this.vlc.stop();break; + } + }, + pause : function() { + switch(this.plugin) { + case "moz2": + if(this.vlc.playlist.isPlaying) + this.vlc.playlist.togglePause(); + break; + default: this.vlc.pause(); break; + } + }, + next : function() { + switch(this.plugin) { + case "moz1": this.vlc.next();break; + case "moz2": this.vlc.playlist.next();break; + default: this.vlc.playlistNext();break; + } + }, + prev : function() { + switch(this.plugin) { + case "moz1": this.vlc.playlist.previous();break; + case "moz2": this.vlc.playlist.prev();break; + default: this.vlc.playlistPrev();break; + } + }, + is_playing : function() { + switch(this.plugin) { + case "ie1": return this.vlc.Playing;break; + case "moz2": return this.vlc.playlist.isPlaying;break; + default: return this.vlc.isplaying();break; + } + }, + toggle_fullscreen : function() { + switch(this.plugin) { + case "moz2": this.vlc.video.toggleFullscreen();break; + default: this.vlc.fullscreen();break; + } + }, + set_volume : function(vol) { + switch(this.plugin) { + case "ie1": this.vlc.volume = vol;break; + case "moz2": this.vlc.audio.volume = vol;break; + default: this.vlc.set_volume(vol);break; + } + }, + get_volume : function() { + switch(this.plugin) { + case "ie1": return this.vlc.volume;break; + case "moz2": return this.vlc.audio.volume;break; + default: return this.vlc.get_volume();break; + } + }, + set_volume_delta : function(delta) { + var new_vol = this.get_volume() + delta; + new_vol = (new_vol >= 0) ? new_vol : 0; + new_vol = (new_vol <= 200) ? new_vol : 200; + this.set_volume(new_vol); + }, + toggle_mute : function() { + switch(this.plugin) { + case "ie1": this.vlc.toggleMute();reak; + case "moz2": this.vlc.audio.toggleMute();break; + default: this.vlc.mute();break; + } + }, + press_key : function(key) { + switch(this.plugin) { + case "ie1": + var keyvalue = this.vlc.getVariable(key); + this.vlc.setVariable("key-pressed", keyvalue); + break; + case "moz2": + var keyvalue = this.vlc.get_int_variable(key); + this.vlc.set_int_variable("key-pressed", keyvalue); +// alert("not implemented for this version of vlc"); + break; + default: + var keyvalue = this.vlc.get_int_variable(key); + this.vlc.set_int_variable("key-pressed", keyvalue); + break; + } + }, + snapshot : function() { + this.press_key("key-snapshot"); + }, + change_audio_channel : function() { + this.press_key("key-audio-track"); + }, + direct_record : function() { + this.press_key("key-record"); + }, + set_resolution : function (w,h) { + this.vlc.width = w; + this.vlc.height = h; + this.vlc.style.width = w; + this.vlc.style.height = h; + }, + have_options : function() { + switch(this.plugin) { + case "ie1": + case "moz2": return true;break; + default: return false;break; + } + }, + insert_control : function() + { + var vlc_control_html = ""; + if(is_ie) { + vlc_control_html = + "" + + "" + + "" + + "" + + "" + + "The VideoLan Client ActiveX is not installed.
    "+ + "You need VideoLan Client V0.8.5 or higher.
    " + + "Install with Option ActiveX." + + "
    "; + } + else { + vlc_control_html = "" + + ""; + } + obj_update(this.c_masterid,vlc_control_html); + this.vlc = id(this.id); + } +}; diff --git a/src/nhttpd/web/Y_Version.txt b/src/nhttpd/web/Y_Version.txt new file mode 100644 index 000000000..d039aa59a --- /dev/null +++ b/src/nhttpd/web/Y_Version.txt @@ -0,0 +1,6 @@ +version=2.5.0 +date=21.02.2007 +type=Release +info=mod neutrino.dream plus + + diff --git a/src/nhttpd/web/Y_blank.htm b/src/nhttpd/web/Y_blank.htm new file mode 100644 index 000000000..c99ab7e31 --- /dev/null +++ b/src/nhttpd/web/Y_blank.htm @@ -0,0 +1,10 @@ + + + + +yWeb + + + + + diff --git a/src/nhttpd/web/Y_neutrino_Blocks.txt b/src/nhttpd/web/Y_neutrino_Blocks.txt new file mode 100644 index 000000000..083e5ca6a --- /dev/null +++ b/src/nhttpd/web/Y_neutrino_Blocks.txt @@ -0,0 +1,1230 @@ +# ------- Neutrino form +# form-data in block (var-get:neutrino) +start-block~neutrino_form + +{=include-block:Y_Blocks.txt;management_check_top=} +{=include-block:Y_Blocks.txt;head=} + + + +{=var-set:wait_text=Save.=}{=include-block:Y_Blocks.txt;snip_wait=} +{=include-block:Y_neutrino_Blocks.txt;{=var-get:neutrino=}=} +{=include-block:Y_neutrino_Blocks.txt;neutrino_form_helpbox=} + + +{=include-block:Y_Blocks.txt;management_check_bottom=} +end-block~neutrino_form + +# ------- Neutrino form help box +start-block~neutrino_form_helpbox +
    +
    To Synchronize with Neutrino
    +
    + After changing settings via yWeb:
    + Choose on TV "Main Menu->Service->Soft restart", to accept the changes within neutrino. +

    + After changing settings within Neutrino:
    + Choose on TV "Main Menu->Settings->Save settings now", to accept these changes and reload this page. +
    +
    +end-block~neutrino_form_helpbox + +# ------- Neutrino form-data: record ------------------------------- +start-block~neutrino_form-data_record +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Aufnahme=}{=var-set:menu=Recording=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Recording device + +
    IP
    Port
    Wake up + Off  + On +
    MAC
    Stop Playback + Off + On +
    Sectionsd + Off + On +
    Scart + Off + On +
    Recording start time correction (sec) Reboot urgently
    Recording stop time correction (sec) Reboot urgently
    Standard audio stream + Off + On +
    Alternative audio streams + Off + On +
    AC3 audio stream + Off + On +
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_record + +# ------- yWeb save Settings +start-block~neutrino_record_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_server_ip;{=ip=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_server_port;{=port=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_server_mac;{=mac=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_server_wakeup;{=wol=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_type;{=rec_type=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_stopplayback;{=stopplayback=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_stopsectionsd;{=stopsectionsd=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_vcr_no_scart;{=vcr_no_scart=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_in_spts_mode;{=in_spts_mode=}~save=} +{=ini-set:/var/tuxbox/config/timerd.conf;EXTRA_TIME_START;{=EXTRA_TIME_START=}=} +{=ini-set:/var/tuxbox/config/timerd.conf;EXTRA_TIME_END;{=EXTRA_TIME_END=}=} +end-block~neutrino_record_save_settings + + +# ------- Neutrino form-data: movieplayer ------------------------------- +start-block~neutrino_form-data_movieplayer +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Movieplayer=}{=var-set:menu=Movieplayer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Streaming server + Off  + On +
    Streamingserver IP
    Streamingserver Port
    DVD Drive
    Directory (VLC)
    Data Rate Video
    Transcode + Off  + On +
    MPEG Video Codec + +
    Resulotion + +
    Data Rate Audio
    Transcode Audio (dvd/vcd/mpg) + Off  + On +
    Force AC3 with avi + Off  + On +
    Start Directory
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_movieplayer + +# ------- yWeb save Settings +start-block~neutrino_movieplayer_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_type;{=streaming_type=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_server_ip;{=streaming_server_ip=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_server_port;{=streaming_server_port=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_server_cddrive;{=streaming_server_cddrive=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_server_startdir;{=streaming_server_startdir=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_videorate;{=streaming_videorate=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_force_transcode_video;{=streaming_force_transcode_video=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_transcode_video_codec;{=streaming_transcode_video_codec=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_resolution;{=streaming_resolution=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_audiorate;{=streaming_audiorate=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_transcode_audio;{=streaming_transcode_audio=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;streaming_force_avi_rawaudio;{=streaming_force_avi_rawaudio=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;network_nfs_moviedir;{=network_nfs_moviedir=}~save=} +end-block~neutrino_movieplayer_save_settings + + +# ------- Neutrino form-data: parental ------------------------------- +start-block~neutrino_form-data_parental +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Jugendschutz=}{=var-set:menu=Parental=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + +
    Activate + +
    Minimum Age + +
    PIN
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_parental + +# ------- yWeb save Settings +start-block~neutrino_parental_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;parentallock_prompt;{=parentallock_prompt=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;parentallock_lockage;{=parentallock_lockage=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;parentallock_pincode;{=parentallock_pincode=}~save=} +end-block~neutrino_parental_save_settings + +# ------- Neutrino form-data: diverse ------------------------------- +start-block~neutrino_form-data_diverse +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Diverse=}{=var-set:menu=Misc=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Standard 
    Standby mode + Off  + On +
    Turn off completely
    Infobar Satellite type + Off  + On +
    Show Subchannel + +
    Save Teletext + Off  + On +
    Virtual zap Infobar + Off  + On +
    EPG-Settings 
    EPG-Cache (Days)
    EPG remove after (Std.)
    Max. Events
    Remote Control 
    Repeat delay
    Start delay
    Delay Shutdown + Off  + On +
    Filebrowser 
    Filesystem + ISO-8859-1  + UTF-8 +
    Show file rights + Off  + On +
    Absolute start directory + Off  + On +
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_diverse + +# ------- yWeb save Settings +start-block~neutrino_diverse_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;shutdown_real;{=shutdown_real=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;shutdown_count;{=shutdown_count=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;infobar_sat_display;{=infobar_sat_display=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;infobar_subchan_disp_pos;{=infobar_subchan_disp_pos=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;tuxtxt_cache;{=tuxtxt_cache=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;virtual_zap_mode;{=virtual_zap_mode=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;epg_cache_time;{=epg_cache_time=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;epg_old_events;{=epg_old_events=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;epg_max_events;{=epg_max_events=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;repeat_blocker;{=repeat_blocker=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;repeat_genericblocker;{=repeat_genericblocker=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;shutdown_real_rcdelay;{=shutdown_real_rcdelay=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;filesystem_is_utf8;{=filesystem_is_utf8=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;filebrowser_showrights;{=filebrowser_showrights=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;filebrowser_denydirectoryleave;{=filebrowser_denydirectoryleave=}~save=} +end-block~neutrino_diverse_save_settings + + +# ------- Neutrino form-data: bootoptions ------------------------------- +start-block~neutrino_form-data_bootoptions +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Bootoptionen=}{=var-set:menu=Driver- and Bootoptions=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Load SPTS-Mode Drivers + Off  + On +
    Show Bootmenu + Off  + On +
    Show Infos whilst booting + Off  + On +
    Use HW-Sections + Off  + On +
    Activate AVIA-Watchdog + Off  + On +
    Activate eNX-Watchdog + Off  + On +
    Use PMT Update + Off  + On +
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_bootoptions + +# ------- yWeb save Settings +start-block~neutrino_bootoptions_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;misc_spts;{=misc_spts=}=} +{=file-action:/var/etc/.neutrino;{=if-equal:{=bootmenu=}~0~add~delete=}=} +{=file-action:/var/etc/.boot_info;{=if-equal:{=bootinfo=}~1~add~delete=}=} +{=file-action:/var/etc/.hw_sections;{=if-equal:{=hwsections=}~0~add~delete=}=} +{=file-action:/var/etc/.no_watchdog;{=if-equal:{=no_watchdog=}~0~add~delete=}=} +{=file-action:/var/etc/.no_enxwatchdog;{=if-equal:{=no_enxwatchdog=}~0~add~delete=}=} +{=file-action:/var/etc/.pmt_update;{=if-equal:{=pmt_update=}~1~add~delete=}=} + +{=file-action:/var/etc/.test;add;moin\nduu=} +end-block~neutrino_bootoptions_save_settings + +# ------- Neutrino form-data: pictureviewer ------------------------------- +start-block~neutrino_form-data_pictureviewer +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Bildbetrachter=}{=var-set:menu=Pictureviewer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Scaling + +
    Slideshow display time
    Start Directory
    Decoding-Server IP
    Decoding-Server Port
    +
    + + + + +
    +
    +
    + + +end-block~neutrino_form-data_pictureviewer + +# ------- yWeb save Settings +start-block~neutrino_pictureviewer_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;picviewer_scaling;{=picviewer_scaling=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;picviewer_slide_time;{=picviewer_slide_time=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;network_nfs_picturedir;{=network_nfs_picturedir=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;picviewer_decode_server_ip;{=picviewer_decode_server_ip=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;picviewer_decode_server_port;{=picviewer_decode_server_port=}~save=} +end-block~neutrino_pictureviewer_save_settings + +# ------- Neutrino form-data: audioplayer ------------------------------- +start-block~neutrino_form-data_audioplayer +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Audioplayer=}{=var-set:menu=Audioplayer=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Display order + +
    Select actual track + Off  + On +
    Titele search by Name + Off  + On +
    Activate repeat-mode + Off  + On +
    Show Playlist + Off  + On +
    Screensaver (min, 0=off)
    High Decoding-Priority + Off  + On +
    Start Directory
    Shoutcast Meta-Daten + Off  + On +
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_audioplayer + +# ------- save Settings +start-block~neutrino_audioplayer_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_display;{=audioplayer_display=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_follow;{=audioplayer_follow=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_select_title_by_name;{=audioplayer_select_title_by_name=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_repeat_on;{=audioplayer_repeat_on=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_show_playlist;{=audioplayer_show_playlist=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_screensaver;{=audioplayer_screensaver=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_highprio;{=audioplayer_highprio=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;network_nfs_audioplayerdir;{=network_nfs_audioplayerdir=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;audioplayer_enable_sc_metadata;{=audioplayer_enable_sc_metadata=}~save=} +end-block~neutrino_audioplayer_save_settings + +# ------- Neutrino form-data: direct_recording ------------------------------- +start-block~neutrino_form-data_direct_recording +
    +
    + {=var-set:help_url=Neutrino:yWeb:Settings#Direktaufnahme=}{=var-set:menu=Direkt Recording=}{=include-block:Y_Blocks.txt;work_menu=}
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Recording Directory
    Max. File size (MB)
    Max. Ringbuffer
    Sync write (O_SYNC) + Off  + On +
    Sync write (fdatasync) + Off  + On +
    Record Teletext + Off  + On +
    Record PMT + Off  + On +
    Directory selection on direct recording + Off  + On +
    Long Filenames (with EPG-Data) + Off  + On +
    Recording Filename Template
    Directory permission
    +
    + + + + +
    +
    +
    + +end-block~neutrino_form-data_direct_recording + +# ------- direct recording save Settings +start-block~neutrino_direct_recording_save_settings +{=ini-set:/var/tuxbox/config/neutrino.conf;network_nfs_recordingdir;{=network_nfs_recordingdir=}~open=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_splitsize;{=recording_splitsize=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recordingmenu.ringbuffers;{=recordingmenu_ringbuffers=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recordingmenu.use_o_sync;{=recordingmenu_use_o_sync=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recordingmenu.use_fdatasync;{=recordingmenu_use_fdatasync=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recordingmenu.stream_vtxt_pid;{=recordingmenu_stream_vtxt_pid=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recordingmenu.stream_pmt_pid;{=recordingmenu_stream_pmt_pid=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_choose_direct_rec_dir;{=recording_choose_direct_rec_dir=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_epg_for_filename;{=recording_epg_for_filename=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_filename_template_0;{=recording_filename_template_0=}~cache=} +{=ini-set:/var/tuxbox/config/neutrino.conf;recording_dir_permissions_0;{=recording_dir_permissions_0=}~save=} +end-block~neutrino_direct_recording_save_settings + diff --git a/src/nhttpd/web/channels.js b/src/nhttpd/web/channels.js new file mode 100644 index 000000000..30cfb8fde --- /dev/null +++ b/src/nhttpd/web/channels.js @@ -0,0 +1,46 @@ +function fertig() +{ + for (i=document.getElementById('channels').bchannels.length-1; i >= 0 ; i--) { + document.getElementById('channels').bchannels.options[i].selected=true; + } + document.getElementById('channels').submit(); +} + +function movechannels(source, destination) +{ + for (i=0; i < source.length; i++) { + if (source.options[i].selected==true) { + destination.options[destination.length] = new Option(source.options[i].text, source.options[i].value); + } + } + for (i=source.length-1; i >= 0 ; i--) { + if (source.options[i].selected==true) { + source.options[i] = null; + } + } +} + +function poschannel(box, direction) +{ + if (direction==0) { + for (i=1; i < box.length ; i++) { + if (box.options[i].selected==true) { + buffer = new Option(box.options[i].text, box.options[i].value); + box.options[i].selected=false; + box.options[i]= new Option(box.options[i-1].text, box.options[i-1].value); + box.options[i-1]=buffer; + box.options[i-1].selected=true; + } + } + } else { + for (i=box.length-2; i >= 0 ; i--) { + if (box.options[i].selected==true) { + buffer = new Option(box.options[i].text, box.options[i].value); + box.options[i].selected=false; + box.options[i]=new Option(box.options[i+1].text, box.options[i+1].value); + box.options[i+1]=buffer; + box.options[i+1].selected=true; + } + } + } +} diff --git a/src/nhttpd/web/channels.txt b/src/nhttpd/web/channels.txt new file mode 100644 index 000000000..f99830109 --- /dev/null +++ b/src/nhttpd/web/channels.txt @@ -0,0 +1,25 @@ +dbox;tvinfo;klack +#ARDx +Das Erste;ARD;ARD +Bayerisches FS;Bayern;Bayern +WDR Köln;WDR;WDR +hr-fernsehen;HR;HR +BR-alpha;Alpha;BR Alpha +SÜWEST Ferns. BW;SWR;SWR BW + +#Free +Pro Sieben;Pro7;Pro7 +terranova;Terra Nova;Terra Nova +Kabel 1;Kabel1;Kabel1 +RTL II;RTL2;RTL2 +RTL Television;RTL;RTL +Sat. 1;SAT1;Sat1 +9Live;9 Live;9Live + +#Kabel Deutschland +History Channel;;The History Channel +SciFi (KD);Sci Fi;SciFi +13th Street (KD);13th Street;13th Street +Wetter Fernsehen;;Wetterfernsehen +Disney Toon;;Toon Disney +Disney Toon + 1;;Toon Disney Plus diff --git a/src/nhttpd/web/extentions.txt b/src/nhttpd/web/extentions.txt new file mode 100644 index 000000000..13437862f --- /dev/null +++ b/src/nhttpd/web/extentions.txt @@ -0,0 +1 @@ +u,yjogol,yjogol update site,http://www.yjogol.de/download/Y_extentions.txt diff --git a/src/nhttpd/web/favicon.ico b/src/nhttpd/web/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5e17fbdcf49d5bd14e491917b35610667a681331 GIT binary patch literal 894 zcmbW0Pm6*;7{+JWFVJb(;~w{8?QuVbf(0ImB0;6Bp#LX=YO zKCKv)v$5kHkLE6o$o^Yj`ue^%v}|5;9-N#VUENGn|b~oDOt%fQfyJZq7#|;`_?4rEcM&{ pKqv97>!QOAEqm_RI$vUFm5X5a>TxRxRpM!F zuW+_y^&;n%m41C27#&j?NCb*MSr{3BR_TEBf$U^pbx=_0OUaywS7(G)#I~ zS_?UtFIpW~VDMvtY2S&I#Tr!x^8z%AzeGf=@78=d@kQCzrR)hjX^UTVz34P D_k}%_ literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/arrowup.gif b/src/nhttpd/web/images/arrowup.gif new file mode 100644 index 0000000000000000000000000000000000000000..505dab69a16d62ea256527b6db5f32086e990fe8 GIT binary patch literal 192 zcmZ?wbhEHb zuW+_y^&;n%m41C27#&j?NCb*MSr{3BR_TEBf$U^pwN+5*OUayk-~Va;|3AI_zpC*6+S32~CjCD+?f*v`G01}|3%6F7pMK--0**@@Bfu~|2NkBKePJ((YgQcpZdQ%=l}Nh|F7@;fA{GB zn+N}Y|M>sx>i_F|{?7{gzjwm_ot^)0?Eimy#sBZ0{;#k4|KQC3U*G&pL6_WnOR;{V~<|NET&|NqZ0WP##O7O>lOKqM$m7}&2jI5su6w6?W( zbar+3^!7FJGYa)jnL2Iy6f;H!P8$)21v3^cUeal-!@$9=w_xqMr5iT(2}&|Bv&pR6 zwR_Xvz2*X9%xWyVj~(B4avH0uFf*6!@k^IaUG3oGHsWEDzI^ZgwX0g9;_6Ji_g}tx zxIx2ELQ3w{=PwR#rZMWO$jg5H_w~Do>!c6PEq(PI|5;_WBn2}`%@^`=k#h)yUAf^t80Ld^gF}W}@SOWlZ0R#L1 literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/body-trans-l.gif b/src/nhttpd/web/images/body-trans-l.gif new file mode 100644 index 0000000000000000000000000000000000000000..5eb6479cc68fc3832ed395880352d4a9a83a2e43 GIT binary patch literal 2706 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 M|0>vLLJ$A|JHn!`x&QzG literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/body-trans-r.gif b/src/nhttpd/web/images/body-trans-r.gif new file mode 100644 index 0000000000000000000000000000000000000000..3de6b14d4daa3085ee2a1d22c91ce88c97919902 GIT binary patch literal 8279 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 VADBQ5C;$PwXPWoDM{^JW06Q$ZJKF#N literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/check_green.gif b/src/nhttpd/web/images/check_green.gif new file mode 100644 index 0000000000000000000000000000000000000000..243b99b516df77341676eeff6d8a99afff848bac GIT binary patch literal 357 zcmZ?wbhEHb6krfwxN64m;Nh`VtEU}4yyn^S(@{(Ow{2f|@BWeFCpPq-V0i!G@{uEJ zy=OVG&tO=+W;$OB!;6<^r6w_0tzyXB#o$(N7qrq-Whuk?3)>lHFeGjWW5{DDKa$B_ z$VWz|A)E0wr_8J~QMj_G4n5nwLDe$uXHLFlfu0omVY* z^i)|=7>&*+=`wOLn8%h*|2*<1Y0Oqki&nAh$*cHGU8!5RSYkbRf{ literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/chmod.gif b/src/nhttpd/web/images/chmod.gif new file mode 100755 index 0000000000000000000000000000000000000000..5710725f6f726091e823657c0100a17fcbb74b1a GIT binary patch literal 346 zcmZ?wbhEHbDdyk5feL^Bm?`#aHUID5zke2-zk=Vr+O_NV4(>l7#Pwr_`1=pq~V;Qjsk_xtzn|NQy=iGk_& z?>~zfgdRNx!Nm-MpBb39Fz|i*_U+}%ms6)sb#rr5RaO1Z!0;ao7%+h1PZmZ721y1T zkPOI92G+=gu-=rJb48b}%V|vtaX!1{ao3F{?|;v|B*x7vc*IjhjNw4h78{9aSCk4j zuqYjDIM@{2tHav3;6UwNqiF{h)Cl!nU}<7Hl($%ay28G?^YeA8%Q8w`o7!>}8LR>Q Cq*y%w literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/download.gif b/src/nhttpd/web/images/download.gif new file mode 100755 index 0000000000000000000000000000000000000000..f43af17b66974b01046350b3d2a94f2696bb7a27 GIT binary patch literal 337 zcmZ?wbhEHb4?`S$JW zr_Z04oY(pI@x$N0e_yXLT zf1hx|^x3ngKY#xG`}g15w{LIWy7lb&^T$t~ynDg<`t|Gk5AJ{d^5xMJ{!gDi{`>#$ z-3zV@_rwn#JZNTSR#Q{+9}E~s0g69a7#SE07<53IKz?Fi%X64sV9_GkyKZ{X%o5o| zPk}>1N~-aGPgbi`)kqylp2R#+hi}caT}#jU_=xl@Hw6*7G>w4-eUaZTY&cKl4;-agiT<*Uq%iWQ|8UQv! Bp}znC literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/elist.gif b/src/nhttpd/web/images/elist.gif new file mode 100644 index 0000000000000000000000000000000000000000..d5281c0ee1a067733af8cc7667746731e80a304a GIT binary patch literal 336 zcmZ?wbhEHbA!#f z{`~p#gDs&Iq4<-9k%2*hK?h_3$WIJxxegN&9dv|vosJ}(j8WoY zIaqY)W`Y_wW8#53DF(9q({}V!D0F+dA6C#enw7WAF=d9b&$*+HIT??<`Q*j;UHD|= zWu(M~gMX<$Y2cs(x__| literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/epg.png b/src/nhttpd/web/images/epg.png new file mode 100755 index 0000000000000000000000000000000000000000..2fbdd6920a32ef87b344a60f88d4e077a647de39 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-$h_H=O!(Kw&{=X`^_fnmU^23D6T z|NkHVz|K?O{7~QE-_Z@zCpI>Iv^P`}`P2Tg^+n4;W0%&Nn`h_oB zXMCTNA-w1R;@9m5&b!P``2Amf(pd!#&g=F^W;hsZV)z*wDqmKyKNDyjgQu&X%Q~lo FCIDq2QD*=E literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/excl_yellow.gif b/src/nhttpd/web/images/excl_yellow.gif new file mode 100644 index 0000000000000000000000000000000000000000..e604db74ed8c7b2954f60789fe99be40e81a4294 GIT binary patch literal 596 zcmV-a0;~N;Nk%w1VGsZi0Oo%Hgi!$5XIYzR z2i370=jVC<{%_{f0N>WF`PKmc|JT8q0Q%^w;JP^f|5@mE!7R$5eAT>a^z=#p|4QQ4py=PN&cBk>xR24YP|28a;<(vq!u0KS$k`_Z!d-njq&xzMHu^z9$-?1OWkHL1HE(9TrzOEGh5t0jvNqP-H}cB{?KKcn|>tl__6l i{HTH?&Wbs5%)}srfR2wQC)_x(qazQG4Vw%F0suQ)JR+p;_Jkbxj~e@|75uXk{HzuC_xHiU z!SeF+{rvp&dL8lc@$T;K^z`-j`1kQ;BEG)9{r>&h+1mZM64ut%t*x#2`1jDz(fIlK z`uh3({r%I^)XK}t_V)JjY9aG-AgHLQ_m3K}v9Y_nyZWOR{QLX3xVZS481(b>{j(Id zwzl+gANl$E{InF!&Cav4wD_7B{r&v?xf4`WRKmi-B_$<%e0%@^0RR90=Kufz00000 z00000A^8LW002AyEC2ui01yBW000J&K%Ov1EE;pdWNyc7GM7l@?i15^E|t-8m$P}l zU>wG10|084n)+bCm1M0{SFHo+V7ikE_i(ICU3LI0IsrNb1UL&I8546l28IPXIxaW~ z90Lkn2mv!B1Pvk~5;y}iJ2yEvtgWs%0!*q>5D_mZ6i%~4I~xNXvAR(_OANtIsl!{y SO}oUYH__74%172YK>$0uti8Jc literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/ftype_folder.gif b/src/nhttpd/web/images/ftype_folder.gif new file mode 100755 index 0000000000000000000000000000000000000000..1e3e3d82d6e35c5f6274ee4db2c22d5db39557fb GIT binary patch literal 329 zcmZ?wbhEHb~w+mwb|IGdU!spXWrY{SaznvHT z_0s##$JpO5y#N2({{P>)|Nl1s|F`7lee?hSmVUn=`t7{f@Apx^UU>hwrSt#){_p3- z{(Ol1v7Y_c3!nf05B~qR>gN`Y-!Fas|J(KFW6YoTk^lc6{q@x4$1Ux@-!lIHTcEG6 z|NsAg1`2@UPZmZ725kl%kQpF9F|ef_m|EbWBh~LTHK;XarPl?a%PT(hNGyI6G*g7p zX*yrRMGJ;yS{*5;(^lE7no^@@zGRBv)g=lV86u)g93o!j{OTPY66&1l-h65kCo)c9 fVeyufR$*pV5mQl-$@AA(x@@UJMsBo|BZD;n>$QlF literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/ftype_link.gif b/src/nhttpd/web/images/ftype_link.gif new file mode 100755 index 0000000000000000000000000000000000000000..66881b86ab977da766c2f51fb14efab0a5c42d67 GIT binary patch literal 327 zcmZ?wbhEHbxff>iml%g^@R=`GDng>8O*3T_gv@70oSgT$J*kO*Aq9I~CW*s{G*(t$KS{OS+#aO%?udUme<*TTEO`Fr@r_QT zk=#}u-n~>Vm!+9S1PE{@3<)G~CPb<$Za;W?3+O}|+q)?*Pn355=}S(XIZmEANjZci zf5 zj<%@MX^bD1^BwlS^+AD|$dm-1wial0hwPI;CDM?Y9SXW#@w-UF0SQ8OgplRTleOB2 zUjkDS|0U9pI|lSN*EvXUa~*UIclJdZ#)Npbwh9>YT?Z;=B8|l&^t~P~om?<5Lre$+ z;%`P>SL7`djY#8Y9$wv9dv|3p)^tql(kFDxuX;T1ZauA`&FpP)^{b-#z72*XdVuC6K+ zioLzP{{H^a(NP+WHak13QmN+V=03*t(CKuIM#E$>84QM2tKHt-mdRvRtCd2b#EbJ!_aH9e!&%ua(Y4zN&UT-a$}@3+qw z*48&Rx7I*JgjZlt^htl?-u@Qo@a~23aeKTN19hAuRQV=>&d>mp8+H%}c%lNo`W{KB zu7Mm*De@L&j6)$wK2-1E;TM2!a<<@@Nnemghdcg(B}O9C8xjz!uLdKpr!dYI!*^o3 zmM1FH>k`QxWg(d@8SSONg=7`>^Uys9AHlOxGlD!3Cq4dTXztTO8~(*-@w@Hn-qKBJ z`=|BAIbyV3;af#@TbVCEaSh8Vsx7%7OYqazq8^%3<$>X2MH=A~bRTQIRN8Pe3a#+E zXpQ-l9Ph}wKA_fg%Km##c!T&ocU)qmc7BTKAr zeWKggRj^eg2j`QCY$lzmK(>HEsVmI-)QYyp^oZ!-R&qrjx2+K!&Er1GcoocLbJRRu ztAr7f#qWa$^Lx~}EBr30+VG3*_mI#hga72Bo?dlrDC&3_VdV=pPCN5DWhruZfzsKE L63VIWP$2$ajI8tY literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/head-blue-r.gif b/src/nhttpd/web/images/head-blue-r.gif new file mode 100644 index 0000000000000000000000000000000000000000..ab01edef744450a3cfe44388a6a1a716d9f0169c GIT binary patch literal 2583 zcmdUs`Ck%-0>G&>kF;P5CrkYGY?ci1UcmLVsG!ic;6qsKYu@;?@cccmrFl_HKqWo06<(^90Gw@ zSXk)m>+9<3;&3=aLqo&E!(1*`sZ{Rn?kW_Do}L~ak0%faQc_X|1_qEwBq$NRx3|aV z^W)>=NhH$j>@17L5{tzX6BCV%ji({=I2;ax!EA4DqfjU+m0DU_YM(UxAQ3)4KTo64 zmX?+n3A_fy2UBS&>~k9)U5hbujEEFV!@ z>qcwuRCdQgFo>0b0>|f{X&5A%QFblG7>sS@jzaIk zf1qPgJZAHgfl@FI&7WzH;C-g!+BW71@rO?O;M-pf>z$4jx)Tx)?>nq-Zmd>@mGUN$ z&*uj_aPo}>V#>+eop_~$GYWsq=4N(DG*z7VUXAH49V z<~is>oX$ngLOlO_q5Gi|KtQ+pMNeldBIB`%xj=a$yR92Qi>hP zoR#V{5zI<+S%tFF-DDhAhWkN_<;?5)&z7@1&($rfvOyQQ%Q<&EELL*w-G6pA&1l_Z z-=WhA;C|rYGr%#RU*5CTkHIx{tA(Lx?rKpu$%0)RIq{5L61`f-E{zcv5Vaj6O^90G z3QaseLC(QA6{#23IF*?mmfWh``yt%wf*{MYTHn}}eS1L52*!n0<%O)(L2F=ZuzK{` z8oZelvZ8xTEpY$A3lK|QBVM+~Ya$#xzuw%d|9rjWwYNzxzyMly9x>!`p5Hol|2ZEu z83gB}ozjJX0~U9LR&C39&jlDx4P1a-NAnt!?FsJgM|4xS1s#f2_(tcBjJMGRFwg?* z(=Z8qNzgQ_7xw5luM3I5$F9PC>PAK)Rp0Ta^&*l{)Vhd#9TZ^y(Ih`qOfj#m7gMcr zED?RyWGe~Hi5V)PyR^A9?=xkuOBn7N)|*4u*|-SC4YP*L5s))~b2P_T*KzEg-wWyZ zgQpD_(tLC9Qf6S1wQTZd_UxOf;MxY+bZA@J##9*DT0R@ed=Ui;pKp+Vj*;`_^S`X> zDi#t9!W4^;BJ=2_RA+&Lm6_BLy`1Y8rd%mNy8XIZ93@b)%eue-#@(c_EiSaSajUVY zNwCFhCfndD8W>^Q{C0LDE(f(C*xn#$*v2Gx9C^7T>NneXC!#uU>_~=82V*wJ{9f)# zgKuZ-%4VZBc88|_u-#KBmh(4WDOPKn_O{kfeC;~IC)=uaHj7ez-&L@iRD01~k6l#& zoic#NBy?L(7^?{?+u!svLETmOConZI!_d52+g})W+(=K|vA_H9JK&!ucFHtwdiKZ? zn-k1I<=PEVJ;$)ZM7y0whn{#64X1%`9P#Ccqxy;DmF6UOoeH2-Mm%LGf_NG|)XVAb zJ?EPB>{d+0m$jZZ3U9O|KcH3UUwYeT{Z5n;sP@2s=t*)Y)O+;At6_OV5TM-Uj0`QUYYMfRi_OoxUU_=S(&M4ih&jsZ+~&N zYs8sz&J>D|_6JpVsZO=|NiFJkh2q>ltj=2cQM_GXh`*Xs&mMnlG2lP#o7{@8wu!Rn z4G@d-3038`@m{o_vcn5Vpc)5j2<;bF$w!m1GfuT$bmNQ2!b#fdR%0DK>>cvA*_|2} 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 literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/head-trans-l.gif b/src/nhttpd/web/images/head-trans-l.gif new file mode 100644 index 0000000000000000000000000000000000000000..f92828d89a8dd37b6ad7837ec330ddb118df9f38 GIT binary patch literal 776 zcmV+j1NZz#Nk%w1VJrZ;0M!5h;Nal0va-d+#nI8x{QUg={r&m*`R(oP{{H^U%ggEM z>4SrV`1ttj?Cgt+i^9Uf?(Xi!#>VpU^0TwE$H&Lx+9>`;o;xk-?+HA%F4>( z;^LZ`n(^`Ryu7@go}QVRndRl>`}_Oq>gwCu+w=4D(9qE6=;*({zk-5-|Ns9000000 z00000A^8LV00000EC2ui04xBy000L6K%Q_&EEUQh=;>%k6pr z#ezYiP+d5QQ6wU0yWjA*oLFE$02Yc4gqchNfPsR8goOb^bqyv7IvXDj3pIYhE3&#g8Wjlv3k18eFE#=$CBw1@ zArB4>%CR*TI>FJW3IjSj*QYt!+}%#!+v4Nk=1n;`<>^c7?e0nN;qplI_VeuZ`9=8s zM*aaLB5le6J@@qJ&x3CtpZ@&!^6$aFZ~VLd{73i~;C}zvS0H`{4hUdC1S*&ygA4l8 zAcPM}7$Hp;4j~|g2zJ;d--H%ch@prYf;ggwIhj~uhR2cEB8qLX7@dkQekkLHA;Jhy G5CA(m=9zN< literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/head-trans-r.gif b/src/nhttpd/web/images/head-trans-r.gif new file mode 100644 index 0000000000000000000000000000000000000000..91da79fcd6732c3f32fc8a3e64be2dc91ee437fc GIT binary patch literal 2014 zcmV<42O;=JNk%w1VND0R0M!5hf`WqJ;NY^dvc<*4zrVle=;+YU(9zM+`1tt8$H)2k z`TqX?{r&yz?d|F5>F)0C!otF}wYBT(>-+orgM)*!v$Knfi^j&r^78WB+}z{igwIy-GqdM?Ck9G^Yi89<41ejE#w*%x31m0c=PJr%eSxJzkmY^9!$8f;lqd% zD_+dFvE#>(BTJr4xw7TUm@{kM%(=7Y&!9t#9!C>oFt6rU$f(08U2yn;%!>y&; zxO3~?&AYen-@tMEUpdJdCK~iY&J1;)^iGDC3NAv|s@QA2dMY zjy(40lAXr`&=nryb|W*20d`R1H-)@kRRc;?w+oO<@@=bwNED(IlaS!sZrgeI!!qKr1` z=%Zs8iYTO%R%+>`m}WYsq?&f>>8GHED(apD96IW$sHUpws;qv;X{)ftD(kGY)*8sG zw&tqquDtf@>l3*CD(tYt7He##!5*vZvdlK?ESkwaEA6z@R%@+`(O#?Vw%m5>ZGG8( zEAF`DmTT^A;hwAi?z-%@>#iN?zANv%^ww){6!G4x@4o!@OK!gY1}yNv1Pcpr!3Za; z@WP)S%<#hyM=Y_P4o__H#TaKyWyKnI?D5ASV_WK^A(w3O$tY{camp;W?DEUrtqk+b zG}ml1ZZhAj^Ugf?>_^T%2QBo_M3Vt@(MTt)^wL$1%=FVxM=f;`M^A0_)mVRAb=F*W z?KQ$%e=YXdWWO7B*=VP&wzg-l?e^Pn58HO!bk}V+t8?G2_uhP4ns?uT2QIjue-Cc> z;fO=)bmELR?)aUBKQ8&?l*<@-<(Ox#`G%Ko?)m4S^VfOkq?c~Gc%z@L`s%C~x_Ilb z$1Zz#sn2fz`|Y?BQhV;a_wM@~yZP)+^wd{x{S?Pv zul@F{XV3lj;Ct?U_~e)G`1t0huRh@DukZf*aJLWt{Pc@0fBpF9FZKQU_wPT^{QI8( z2WYbZ67YZsM415-$iN0>Y=I7ppad5N!3kRMg8ix>1~02F8XMTUJRocMP{lon(>T(0wWsN$VLaLv5jzyBZ1y1 z$2!{oaXxgsqaODNo;>pLkASp}9|K9qLIP)yhD@X)ErG~II`WZ>Xe1;jNl8ISvXYq0 zBpxoQ$xeFG4V?U>C`T#1GLrI?s5DL}Q_0F!GS8H*jHN6mvC3K6a+bBcr7l^S%U$|% zl)e0=Ff|#>VH$Ii#XP1m6`9OsIx~>Xe5N$@7|m%~Gmh1~rZ&--&24%!jNSaEII$Sc zahmgquqy0mxu zEq$p>Z5Pv-+O%~wy{S%3m(!j4^m9G^sZcQ&)S(*naYa3W!UmrZJYQ>k@)w;HFrG2ezrxn}T+Ll?hy{&GGmD}C=)>pm#t#ES{+~FEm wR>eK8a#xkyj2mEG-nS5e*lu6P6WsvrOWJL$lEZ2$lO literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/help.gif b/src/nhttpd/web/images/help.gif new file mode 100644 index 0000000000000000000000000000000000000000..4d1c304ba1af3c6c3990cec4c88afb3b973e95a9 GIT binary patch literal 1021 zcmZ?wbhEHb6krfw_|Cv!5mxCH5at^c5gZZc6`tl7l^zhA8yS}pomLc^QJS1rDG@qb zGHi}O=t6GO=4He~)qQO6%e+mKFQWDi2y$9q=!i z9bY-6sJhLu_OO5JUca{e_3e}Ldso*@TIJes$g}yVXY( z#kKa;m)qA}ZCH7uW%Z5Lb+_8q-E3cft82~0&b2qX*57L1c(ZfUot}+1dpF$d*?hBq z%bjU!j!fBlebTl&Q@7llw(Z9B?YCy_ygFm|{keN?&)aij{@(k`cAZ-hQUa$6UFumXnw2Ya`?uHUpjIe|&cQ`i|< z_*Mm_ck|2I2c{SpCO0xRPS}&8sr>lpc^a{hfOUUJZd5yxdlN&{OoKB3X3=vy>DV6V=s|wY4)hZhV}c9zA{fbd-lcEt3x?CBYOSdkt2q%FjsN$(__cp+`nHX5IoAxe(i9KRa88?aKRiEb#L3Y z>E`Cfh=|!+x2CULad&m~Z{7O*;>9}%@-#ocEjqe)-MaD0%AwrcQ#{_ukdTqm(!rxg zof?hn`O`cNhs`EvBI%&V@FI(oly7-4By|eqh2Vy9vL}lG!E?B*R^t`B`WI0 zwQGHwH|KM?ch{`BBawW|X7?x*H^t%;At7h^{LYw|O?3KU4(B@#rzSl7Zc>s-Bzkn< zfHOV4J0YQ0sqB`?{t%0Q6^XuLFouhYcKZ9D4h=mX99+ud{U{LJ+rIs`<;#B;i!X~r z+x+~TyLS&BI;3SXpB5DS91-y_E2}m44-5{gP^Y%6aWXIr_xZfk zkzGY3X8L$NPh&}^U_GcRzL|r~3&!L9O-hKegv(!6sc&p6TI78~lUA``4UAQ!(6u?z zofrzidq&%04ZgcbqDVouVB3g=ku2@Ft~w!c79s{KI3PaZ2WpDR{7Ky}^JJ}(BI?4N znuKzq_x>5sSDjlK5QC>@Pzw>1NI#)(UlNdvU_dl|M=nj}t}dW_?V@6XIP+9IIcQud zTZn^DgbR715|RQ%v7j`=WnIR1xsQ2&?@#Mw#fD++V6nl0g@kE93Y8?O3R5+EwN?*C za*Ym8YcI)N_bNvGIbe)7yq8{-`Y;wpM3%Zr| zKrC=>2Mjroc&S-z33b&}t-FoMZxY!&S1a9;fGtW%J574U5P&0VxeCnRT-faCJJUHq zJeVQ!@Ck(GY-Fw`&PIsw$0*3;zr-r#;T1WcftF@Mh>PbPxn3cjUQ!MZWLfcahZ@Dz z@maLZ6p~ZP#X&bv%rdku5WfV`B1D3o>WkOV;X;X@)@;GyyliGgiS)I6J+O%rpj~x; RO1#(zOGg0E76but{{Z!m;Qasq literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/home-dir.gif b/src/nhttpd/web/images/home-dir.gif new file mode 100755 index 0000000000000000000000000000000000000000..949be9d220d3767d93ea800d923061b25c67a062 GIT binary patch literal 352 zcmV-m0iXUyNk%w1VGaNe0M$PL_mUa-pB4F^6#Tvk?oI=no14qa%;MwX?Ck9P{QTzT z<>u$-_K6+;_511SN{r&yf+S=~#@AC5T z@LK}u>g)Ua`}dL>%*@Q=^lbs_>+ApT6#w)I_V)Jg z@bK;J?f3Wh_H+R9^YiG;0kzIROCz y9BV!}N{1&hJR&!aOFoAy0}%r#q&lu@4Lq_ijzkwc6g)mWBzI3nvca-HK>#~qCB+B; literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/hourglas.gif b/src/nhttpd/web/images/hourglas.gif new file mode 100644 index 0000000000000000000000000000000000000000..1345110a86c75fee41899f09485d23f5e3b152b1 GIT binary patch literal 819 zcmZ?wbhEHbRA5kGn8*MEL7O-K|NkGzQ2fvB=Nc01>=@u`q-Vg)2on0q!YTu#b$}SC zL4kpRDYm75<>|Nli|1^)*0A~5;U-?Tx#m-zSynY3*p{2Mb{%)T@mtB)3Lom`8~RTE>@{o!geNOGgX@(^a$Gr}2 zmC|>S%MnU=fl8Pec!5fIrD;{}Sd zf#rm+v@BzPeu0O@@si0ZFPZQ4I$lU-@FCPq(Vx7M_d%S;C!T~WPSdLA>N=yS;{`ex zcKcnPO@NkpNo7pu@_*${+@W3`~A4{o<3~id~y+V05dgLr9ykNPJo2!lzT8 zwFSM>+V$G%$|s#3PVt97KO0?bbC|EU`GjVxUU$)f9JPg47CV3Wvvf{Kf75H%BZnUG z{Y>~Q0!}cTm{x!iOe}|CtKr8b+4;5{iwralYNSYdDXFiD%i-ShVe7dYa~>J-_q4XD z1ZAkH@bMm(Ua`Z*#MN^}(kz(_$y@JIqbE)~^kmb|9yf>;tY}vFx9}UE=oOweo6XHC zS3pQ!w?&q%ZBh6v8ObeL+k@V!9qLNYX_kF#WxqQ>ONk%w1VGsZi0Oo%Hm#W4vFfc7GEw0!6ZfVfJXJ={Dy{xX?d1?y!Cy&_K(N<^Yr$3 zx%GIt_Mg!EXlQ7;xw&hz^ife!oX-26&H8kPqV@OsV6XLIVPTcZ`DL*6l*#&Lu=MFX zJaU7fZnX4ke3@;3nx@nIbhq}h+5CpW_-C>5hL^Nsukx78`~Uy{A^8LW004ggEC2ui z01yBW000N!fPWhR0Be7Th#4+(EQo^uFo=H{Y7UK(8#gpVhZzoaCz6O$d{!A?c3h#7 zWegk~AtWSQDg?F$Rfi#dyu2PCI}dvm6&8mI15~~rFJTiY6csDSPI+j*B@t#lL46c4 zhYTYl1AZkCM|5UN6G9+|4Ov)MXoF-8^Sm>~#0|*Tl4k1ynz*Peu e1PqYTfTB&#KW9X=Fmd5RfTsckV3_(R5CA)HIYYew literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/live.png b/src/nhttpd/web/images/live.png new file mode 100755 index 0000000000000000000000000000000000000000..1738a4f1061e2d50eec37aaca71fc4506daf737b GIT binary patch literal 696 zcmV;p0!RIcP)H`oW|{1}@v@LQ05MEhB<# z<4=>;WR@@@AqLtaC!HVPednGQ<4lg$g~L5?&-dMX?!97W^xd6Eo$oxkoJ>mN{Nh?T zKGK`#ALG)s`^~4%wdc0Bisi*@repfmSR`f!P$zkx{T@z?_SH!)UcKGidA1{0C>G0$ zA7?uRzSSI_Ih89GO69MgXFKNKK98zQ zodDp<&Bv*(&J#KH@!=2Cb72$}=}dR!3fra9w=bFS)4LbZ-g+@J1_uWp0QaggGoz)Y zB>|wSc<)(XUsp53%urPdg~G$}@$up0E-(~ceR!Hy^s(H0Oy=QCCAKYOhW5XAsPII%aO9W8L<_45+Wpm z3J{2(s?-F|Ih+_O=Jc>IDrA4Q#$1Wo2cBk)48vLrVrKOB_giagE2{e6@ZQ&E?>*UUmP{riNq~)w4Hgy_YKz``%erMF)=Y^u=8QB^M|@% zPEJlTP9pU6_ms-}m&^P6{QZr(WRb3cJ#Zk5#Q6IA`%{c4b#--*yk~@jgf?gzH)|RC z`unodm5GUow$z-a)BN@K`mL?4eZ2O6k*!*WNsqf~bA_T%Ye0sEhGvdWa<%l2p}k{c zV|k0Gp3VAev-G6W{FKT1mCE^sm$b3dkm)=;wA7W5$NGuG_|WFMfPjFDx?+K=Vf*|1 zTU%RhwDfM4V7A-*OnWgeFE6Xs{GrhLn$7!kxAu<4_>af>bGY_HS~iTlWjk#ixw*Ml zk1cSu^r+PQtJVCU(EDtnqiv+AYI~SLK|y`I^@hUuf4=vz){>H?z<9a!c)9j^yZ3N# za8XfFR#sM=&ikIu`}6enXtDHYvGaAf^@qauUWiJV%=`cU|NsC0A^8LW004ggEC2ui z01yBW000NyfPWzf2r_?%h#o&UCWwQDh<_e96OEA}ZaoKw9usISk%&)4H6C}Kppjq% ztSB!pIw}jZ6f=h?eYv?H9{?2%0sb3KlCm{0RgAJI#?j$^ZZW literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/live_popup.png b/src/nhttpd/web/images/live_popup.png new file mode 100755 index 0000000000000000000000000000000000000000..2baaad99e540cb309e76b454db7dba138256aacd GIT binary patch literal 809 zcmV+^1J?YBP)3%#1@% zpUUhS8NL$7k~=>;Umibxu+IO)Jr6uPwBy#T)kdS0%uY|1CO$ivikSgykbFP=Yx&fP zLmMRfM~@7R+)}DGTdidF!nxAqmv5&4eF4BjPra3?Y~6ANv)tLUv*i!pI9vy~`@ScJ z$~(7JTg`Uz)1`Bz^WVIit}`110QW!gN@n{=p{k)f@WaGZIZa!PRJK=}tL^sBGn3_u z-@TXqvtP`Nv9Yn|fM?caW=1}rj{sDa5CV&fi)v<=8LCRN*?fL{eEe9v1{^Iv_=5cV zXl9@WAsC^p(fo?1tzf|N$%99MV-8?&a8UBs2~PeCchy*^d#=_!-5^8)4)ER!^lHqE zTnsmE7Gie|PEzG+Ev1nLA`v3j1Dpt|N?+i;$7>I~J|~Fu;6GNoSYQnn9D)H}f4Zq|7y4@}vHPWuJni?8OzpbL7CAy!iRU5wxN!S n4dUShi40T0&u5wX=ok4L@Mn!_V?Zap00000NkvXXu0mjfm9%nh literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/livelock.png b/src/nhttpd/web/images/livelock.png new file mode 100755 index 0000000000000000000000000000000000000000..a7b566b1f9390e597f30a50e0dc809b87c65df16 GIT binary patch literal 824 zcmV-81IPS{P)D# zqJ;$!L?LRd}e49w&t(~$j)WaBu0@9%c*o7Cy&!r|R_?)koZzV9A^F^1ALJUm>k z>-uF))7mI1DM0aqVnS8b!JeL;<rL6_6GkUAaFSyzVmv$^|CA@kw_qu$?)4DNfMk+r%@LUhi56*+}GEa@Z5ADr7_&ySe%yVMuuAZa>R&dhFhLUY|yB)Du3cf5wWt^UJH@SAv{y6^y!r3!*jPK9NF%|U4$$flqzeSn<%3|-B_Ru=%13nc{9B6NO} zjGv)5q;nk{P3=fL9VC1WHg)*%WHN^Frs(C$?Nu9cAlhtC5?qmKw=Y&mrvUEPQAJ(WZm`O>-V zqM`fJ!CUawH1n;sC_$0Q1Dy=w9)w`Bucsq2QZiFsrP)|OXnq{;=f`lxe+O5eorcw9 z;n4gl0heH@Jqz1r69luR_=F<8gs$P_*9~|v^&IWZ`|x=BCd~BuW5H1lygsnSYr-p8LY@h*(vx=xS@d5|3BLf^$(>I_TlFFU00005&PNmf`5mIF?X(>Nr_OzAky>%LC^xjHsh2Y&83|M~y#eBXD@6&Pcv9797xHKu7^ zH4LMjVgm&zeo~C-x<1g;)AOscUZ?~Jg+e>2`EEl)gWv6TLli~O8pUD}sZnrfJSVp4c1)t9s z&@>IvXmlo<%_=mv@!<2Rsi`>*OzZ+AC0Op0%M(hryId~BVlhzD(O@u`sF0fI@9*zO zBoYxtQLNhk0n~HNbQ+(>@z>xv&W_g*+xj&g%)UCq#$v6Q&p0j3$J)jrF%8Vn!SvN{fuF<+_4FOc_Bsec4HZ@aP7kF`AfJJ;^aX0+8%#Y9r#}d$mMQ>Y|L@r?0{8Z$n|?5h5=~G1^ge$$AHj+-1zLv@U!O6N`8BLUfo#6Qx24@sY;} z9O#B4_6)+{Nw{Y2L20T56WC?B48XNAlHl2@KyuX*fQsf*=h3X`cyeMGj!k~x@RikT zm9P6CnS!zcpppkB8N@x9B!G2*gN&4Il-D7-?Z}58qWZx_V)Pw(>d7uiFeJ>a0&ofT z#hbpB;o(`8v&F^5?Ck6pX_Lp>=6gtG_rLnWL z;p*<^=jZeD^O9jJu*};5UW{m-xtyk}wsbGQzrPJ-kztIXu% zVvf1f-z0FBIeebP*yHi@^op&&?C$TAr?L9{{d$w2McR zBoPq-6AUpdCMXfZ%d`Oj1REhXKO!>$&W0c7=K&8TCk8?Z6+92y^xF>)6C*7gLqKLZ*U+}7DyFQ_caOwTA$FtoI^RB%km zPfAq?t}HG|%`H~&%uCKMD#$NNEJ;lPs)e`@3Vqo}x?d@*{ZZ;-{4_|*W zm`ZRl!sMPFT>byz(U}Z~_daEiQHx~Ao?Zu+0|+1{1|;zQ^M8idKtn!%Wny^o>IcKm zU;n^1{CM~D|NFn|7}P9oF-U3OV7T^l3d6mt$KbXD1P}|dAs@alGW__*#qgh%f#LQ0 z-wdySSXP{!frFcwK~C!f1D^yZgYi3{I_B35`~ni9T@z~3=B-HUx1Sj<)n|r(e_0tg*!dZl5fR7m z^@}jW=g+(hfB$kb{P-@!@cG+YxEw$LAzbqP*Z;>4KQO$1_k)3#TZG}?uip%}Z+v7h z(&YzBKffo=uzT|(hMSjwuKH)hAR;Uc*9s6oV3&Ml`1Joe3me0Qb59te^u!r@6EqpM z6rVAyxXjM*9jFJSMqeX@LCe{aflZH}LDklX;nyDtxFG-m1a`?k22O^H=RYtA32`vU z%kqFFv{cj>&fj{-aQlfkgPx241D~QjquT5G|4)=2Fj$+|Fz^U4z!Mul05L%fVP&{+ z?JCRz|WuqG7@As1B;_TgTnF}i;lhS5)5n{E|Fp_ z0)5Xsn{uMhgxuk3R-C9?xMX*ncE&Hs&b{&%Uwl^dKGWIY=^Dguz3P3#+7N+BQ7jDB E08A!J6#xJL literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/ok.gif b/src/nhttpd/web/images/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..73a4f1ce7fc256aef9cd108f41a4d4b4b136ae36 GIT binary patch literal 459 zcmV;+0W|(cNk%w1VGsZi0M!5hG?g^S=g4luZb!98OQTD{;K8%pv-0rr7?&8E%$rZQ zPXc}d0CfPV+o|yH@D7CzL8L*V)uLs>WsuX51A+t6=F-vX(VN(t+UVNE=frTbaQF20 z>GkQ8$&**TSMTueG^;eP->-JKb|8-+nAVuj>(7nSjp*>`j>eAm`}Plh4}HCT#OTEL z{P)J)##XLYzT>_Vh!cR#fFzkD^7-<1$##Xqg=({E0CxcX{{8@b0QBn-^ z0PpYbA^8LV00000EC2ui01yBW000K)z~9fYJ6T``1I%*TIfTChFK{tUt`pNGz&i|i zjap4cE{hvHV#6bm)JUNasn+&1b``}yAz;yRIW#N+dI}XbGav&QEhP~VB`p~PATu@u z2nYf+0T44Y5CJp-nFKKuJ{>tL3l9$qD>)rL6fr6|3@AAw3_U#zA~`4wI4S@=J})!{ zBRV=G1vD={JpdXwwFW5#HZ}z*2B$b0E-^knGZ7qs91%0;F)lkcIzBoN5H~jv5A`}W zJ9vD!v7m>J9yo9q?6Kg+2Od8HCICP!Au<37c%qypxm=P~KCwUk1<-E^%&v%&sfQfOPbAd^dMUz$2)JWFR zn(5E_apM_H=DRN?Tj1j{9obJa2Z=)=&L)N3y%h6eCr3~F>o%;x+TQ>p^I2c0Y>yvi zIld-=tn@%V9S}8IT_{!R5Z+_Ch0UxTYjubYFr%4GeU;lC74TEKeW=Sl5HvMAeX=Gi zUD%DrWY78$Ld*n!b75@ktedM+7b-uxz>7nb#SWC}9Rsqt73OZQ@HmbM0hR#sjmFrz z`Qn2R+;S%k^W^LfTz4f%8vIhZ??Cw@GT3qQ2$+f;Bm~?nEIi6bESWz7m0cmx39em$$Y&@l88-}q`@)p_|Tj# z*1_TN$urdYuZuK(mFK)s`?7zHi^MWe;3cvrICC;Dz(yzzbJPMi?R@k1RUUs#H=pHg zvrjkZN3JD?ns*D2owV@K;bCSR0s0c1`uAh{pXdA&89xkjaU%7W00000NkvXXu0mjf DW6MO* literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/play.png b/src/nhttpd/web/images/play.png new file mode 100755 index 0000000000000000000000000000000000000000..f8c8ec683edd6a974eacc253332f903d643dbe41 GIT binary patch literal 717 zcmV;;0y6!HP)*bxx-)TO!p6i!Q&%N5)kR|pVJuNho7M&@5o@ZpsgYXg z6pCpqAEgW*0v6~{n89ISU>M4A&V?DNy7MOQX68Ka`MBqf0l?U(ZY+X9l}VLZ)#Om- z;Wxvd@uT21RmKNz1dH$Bj0zp6>Db9B7mX*l{i7uPYA;7kd3g)QVC)rxA$;8vC|jea zS%$3%AWB_OF8f4{mJFo|55c22v$T`7VytGO85j|cC%=pBjskcjxd*)11x{77(<9$R zNrwG!M09PX(8Nd#urDkdGiz{FkfHKZhPUAguyq;A^$wKyj&8EE8)WXSvDl6Q4NN}z z2Zd#i!U*1a7=Vq#3W3jRZ6Z9$+&MVBAqrVEFbBV-XzUqFMNrFnb9RsDb%-T!q1pza zrNBj9g5~vhG_q(g8Ht^6IILQuOJK}cdY)autaf$;u_Hxz{;liNSF+ zP7JVo4aPtM&mF+{jGz3=veK}ME-bIS)D6sEz9#6p*nx(m=)Gd##2kGE&YZW%&7_NU zaJbxh3nsTeLlsIj==Xtu`3s4ZJ3jM?zzC+xEl8DG(CzhM_b>rg=Lda=hWnnX#UBVW zoG_R&W`Q?ax-3JI?gr8ns1oY(%Y_9|I!G4+85=MXv^z2{WgQJlI?w zaoVxOCXc(WFM0oa?j=5{RK~^!N+(o=niw2{+8mt~Bs6sR t8;qA~C8`RYe%XXmYn>v4H2@v@T>=0A literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/rc.gif b/src/nhttpd/web/images/rc.gif new file mode 100644 index 0000000000000000000000000000000000000000..dfa2a15201dd7a5f342815924209ace487c2819d GIT binary patch literal 20540 zcmWh!c|6nqAOGyL!)!ClIU;i}hM8-Qv^kd?bC1y6lr%Y$Y8!L&B`O_iRMJ69DoQnH zNLr;!r8yG1C`spj{a%mv```ED@qWBtujlLedOja-&rKdw`)Dp8g?yI*jxL)R3`SYg zm9&cEMMs+gwnT5^ACR?}N3hu^$5#{g)XvS%C+)3_PsxxCr3q>qWHRe*yd#2>Ekxq_ zZJgb#@Ngd=9|uRr=-9X=k*~Ro!YeB`0Ju*{;moU{+X)x0$DRKxsqE$y1~=O=q+B5~^hebrj#3y?PhVK;|V6(UGDm^57dCK~if62-72Wk%rii$&h{f6d$?khR8yu9pZ zMX|QF&b|LFx8@`>BqBa($M)R9m^494oK~cNP@14RF0&$)Tb{7H%&K!W`1X=5)xnHR zao9jp`;ND*y1g|d+{%_37MIeXZ(vYilp;7x^YFRU*7ldqG{3U^`}bOAP_XmHjlN93 zHKqNltE*dl{kEm-9Qw~YIdks`q0oe6zDA-nyYeS8i1kH9^)rpRB`WIHZRJhgKBY&` zZWA=@uBda{S z;YQ!&wRuAzJ0K{~neJvQ{$uK3Zr(8N9U9~8PAfWmDlx0T#@_aU4Xw!D+0)xg_spzs zKv2-uM0Q;KR#qfI&oCwbU}$(mps!!p)`Y~I>ePxeu{(ErdfA&9k==b+sU=6Y3y!-` zDEy;me0)5qRC|WUrqrT3^1s-cnY8za^~FtQW@fgww)o%-UoVD-hlhxLm7Ef}d3bub zdAR=D9&RiaOK|dno13Sg{+yuxLe!4^`Ss`NE-qV>(+Z9?>*(lg%Rlx;I$0iL|LN1G zrKP3Zy_DCJ>gq>c=#V+Dhex* z4|Px6+xIu4RQs6Z&C!!|vki6p+H)#!RHb$q?Rx9$n>k+>DhQ$U!1F=2NpBr?`moxH z6;>jEWLUex^}31Do7dak-m81RF5fixTrDw|L=R`jCBz&v8O=gM%)DN%-T5xzgwM@u zZ|~RF1lZf~ m_SgOG7mas4dVa0ObnESBmjaA+Nb%t!M2^pX4>f~FR`;%?%u0L9 zWM}t$2uU_6egTy2k(q+eNGe}1jpGp*3erEk<-5NF-XUR6zE=ZwCl@oap> z>C${0U+#gW5nIA8z$Z{&TDFRJH53vGbJUC0IS{8x2nSYmOGz%xjU{FSENL0#X^u{X z?JRO#rNc_n+W(vpB^p&OYUk98)OwO;4(Q)FU{J#x@7Gn_Y2DL(kd<)m?V*UQ`L~DJ zh3jYQ;-cm&m6BgA6#`1_^Rve|1MA-%&v=^ru0Ch>T;j0_PSxyMskPpmut06wTtl(Z z`MJh&+l9GP{M=J+@<9mH{F%Ch^YcxmE1T8}j~ALOG@teu?mjF1aBkuJ#cK=a>raHp zelA>Wf4a@|rJbQWA_Wg(BBhIEDuRQeKZr1iB z>cWmoFH+y{XnDQQH1)#XriS+&6S=2a?OtCD|I|6>^|HbaL;|CBZ^qD-cK3eD@9b(B zMURT5?T(3dUr~z+I-OksUhAWTjeX+W+iOYUi$)}|gBG!&z)^}6oA)RY%E`C#RI1$7 z%OcX;)k6UZRt`R$ymT2qy{Qcb{rV7V5%Vv1(NS)?yupQpnu;5sb#=d9QEdzoMZ`C{ z5<@#A`2gbC?)1?epR|m7yb|@fjvwRJY*K4zulNRsFY=ae_+&$_EBnqoqCq>GW%+z; zih8%v#lKPIsdg`_TCAr^Ok1sQiRZX*O$VWH^lfli8V~P3Vg~4hiOt-hNU0b^XA>k` z2ZsnLNT#}O$@wrr`Q$VBZTwiqi_LE;N0nGJ4V5aLge`frJSfy@JS@vbd58DCo+~+ zOYM^U_SlPbz+Fqj=nSmZdrsK8zvpggwCk?h&Vpc141)0lUG@DEx}`ZcUvGkraN)qw z#>~_RB-K`tg|B#9A)?=TL$B4L#&n zu`ZZXg|ma=I>&c}E$2FyL(%@up(4F$!pQqQx#+Vcrp{g!0JfDLK3}Ooq zWf`S8s4Q2->;_$c5T z(L!?^W^7C-!{M_K@7alwJ5_xUf|4&HIbFGIq)f=7R^ z0R+iF?MX(NQXgqF>xFz{W zg8pTnMVIv`slceCH>&HU)$!Tw9*_<;_$BE_SIxyZZzrBcng~8rb_jNl*AQ4eOcdr~FauzUslKm!ms)VN~=`~2eTJ+@!qSVOmHRG_r?N&HD){~Oa@ z@8a^FUY#&%{q-UrWb)B*x@G@4%6>@x`+GNjO%cc)SxoGgQ<``$uO<4Z}Xzk6=OZ8_Jc`B`04G62s7sobb4w3z{ zHlzmtx2vRG3dJ;w_Y@u8ccX?2oz7-4F+Bv#TnHvo2(ACBFki60=`FoKfoIaeH(}&H zKbxm>7(-xUQ0j8#1Uozt5KExVRp?F}+;leM)C`2?Nw!*5XaFZa#KiQAY%fV%7GT&YoLm?a<-3R&sDXM5 zoz8G=X5Z1z!*}Pn2nxR!-8o~?pLzM6H%ZVx0Er?8oU+NUCLlUF zng4}gifC1PRY2p0pr3zg-Et0!%s3Zxzi<^G{k77tLvJ5wwE|f%b^D|Ju;NkoKk%&%4J2z^F z82l;;u6+s*BPhIQ<g=3z>m@x1qnfyrtAC}6?W{{TT?89o%C;`mxo7_&W zqB;)P(+BPy!<%sYUuI?#XzmYpA9}G2eiT#8A4e!J@XR0c?myvadNr8$+FL%UL?)oq zILIgoa4tX4l)ce=5!E0HHP~?@DS!zRV&H(^ue#>a8_gxM z8xEK-z@p06#{u#|41Q@(KO_XBk2)~>{DNxP3r0dD4ShtjQk%A+Qgl|GiHJ$gs3&Zs0105ikns*hZUR zeU#%ctGxZO(SVxV2XgYMaR`SCEYU#IDx@Vxj*f#K!ol<<2o*BO;WSp#&?tBJJHg;E z8Czx@KhATDDg_z>f$=)Dc@xH(kf`(|ceV+`IB$Jr2J6689Fhq!nBsH>6QeAG5nPR* zyDbmAwCUd2OyzEDkKeDNe7tSqqta<}!yl$OA`^ z%N#kqd*`TCC{Dibk2cu)GDM;%=)3;DVFJcyz{tgMGl+vHp>ncAK!1)!=7H;O+v!rS z0%updi-D*n?6Nivw#0>6a+N6Ds+zSr$a7U{;*(pqwL=6yZp-VB5!~EmQAXXD-nWtVbin#CS=*VngVkY*k|?qTN#$$Oar3|dR(@*-?;!& z!*%t%G@{>(HXqn#M8-_wutwy*Ne^U0FCdjKc~!4vAuHn<-|9=6+`jZ~h%Wu|OfN zgQqUB#f63jvIo=32izQoi^>Lyx7&WIx_M*E{ik{NpTGL|0*u!3fkXlP@-QbA0ftp! zs>SzeFIwy7-|Vb%3KYUD{`J&*z8=IgAzwFE;m>7_jAVEZOOD_1yUPc6@P>as*l-c% zfRnSm$8kD_`Rdp|f6?b7vydgM4Pyel3ukr~BX%*0?xq*nRTYIwhX34qFq$GE);}`Y zJ6IP1qy_oDDrA40lOrJuK}=AFzlQpd{P1gd4Twb2-J_V?&b%ZUqA3t zc(UuySfY3D$nWYeL+FPco^C#VPm5s+q68nDT!R$V7aaeWrj2Ib_(}EdS9#X>YAk)j z3q;FkYDws!-(XI;=PGN11ch#PWkv3V@UKLdy=QAPuemUpa&O^9Ld^Dn7aX%^9B)DG z4qFTM9v?D%!y>Rm3Vd0N(A!Nj1)9DBa+|LP?&H);EcvzkIhUBWOIC#2tk*4^AAEtz5-F#4^rf#Ib_g8 zeEcnZzn0<4mpddDl0(q@eE6RS;35DQA(igh0yu~No?~I$&?3{i@-vJY4aMCOPnB6@ z`EIzuV%;zop;HFvleM-pU;70?sGARg<}X%m=3g(`v!D#8o|twxG2L<9`Q6Ox-KHnZ z7f&C2>=pd@iFxp3`#I+Fxk*!+;#f5@tne~}2I;ns8bWlt;BK5elbbB&NtLJnmJcMg zA0SOjL+fX^-JVVUBn=Zn1Eo)|qOmUt_FwZ+-c_6T?Lel*p{q+h%%|lKd!a4auo8)? zicAz#NrwJ@0qkGgsZOi&m7+7KhGa1 zz1U|0kO?sqE53eZeKHGi%lRK2P{NEIzqA-tuMadYCLb6DRfDyc*fcyD`cPJ>V8N7j zaTPz1PqurBvpLATQr7zVm-Q113X2yGpWm?li2Szuu&yID?F9c*KbOm3=w7$+G&A54 zaIpLy*op(cO3#QETYjpX#K@tbUjFndXm%;kdjw;l*&gGQ(GWOQB*v-}@|E9xI$H6_ z#bUZJ4qa;^dYP$Iw&S_(d!!5PpgmjOfQ)#3^t{H}XHqFD+H{*o`#8O-rcA{rF4t5rl-u7Ux{~bq)#UZ}rxObd(`%h>!YUew=B_L)wp&Ud zB{Go*84P=RRb=+Vkbo%?dLx+-Qd;0!L3(lK04d~b5)En}=TIc8!i;txg{+%Pub{jtEKEIBorko53rf6J!Y6wH!d>-@yvV-mFB2kP5a3mZ-T$Q-yfRR*W)@Q_) zSF+C5Qs}$vA~>S~T|;ezPq{!K=&js6f(NI_2BB)axm~(11nYD)MqzTJ+9%fsu68I3 zIkSF3i&~a+Y2ck7$7d=o`B1D>+vtdrfIWqBEp|InC+4Zdxa;Za#z{E^kOFdiUZriy zVa$CD3mPW&*UdjP!0)fUT2t~-z+E4ZS85c?8sXA zE7Il<16tYay`z?E|Jse#ioQtGS>c#xPjCV zF%tk{+v4UP?hh~@u4~}ht+u6jJbwM_`=^GkG8s0e`rahHU*#rS*W8S%PfjXOH9taD ze!$}^1NLk8|2eIUb66miVYDm|t}b;`v& zC%c<)oGM`$-GXe%uxtZcrX>P~NVMdkl|Htn%={2@GX*mK&=+q_s~)U&OkxhS$gk!u zZ6f~BJbz0fJ|Zjuubl zR||K9MCQf1KWomrfAu?@aFnH2rnwt)hD%&{b#-gucX;C3;_F9(V+!N1svcK7GzXTE zfJHo)q&C!376@FsI+d&+qA|61E9*ymUF;$I7&cGL!jP~bp7jyC{N)4zMv+sH8snzU z;L`i`_EU!rXZ@V7+q#zrsEcqi*Lu6-H)5|wFp*)J`?$rb+m2K>)ukMK)4!r{_+i$b z+0gD}V|NS9#R|dAH65pTp`$a;o?M6S5C7OWuJ(KJ^ww2Ehtv3C#Ua8}iE;7Gm8WzK zKP2wh+CInqM>y9usJ{ySfI9PbgHq6-fwF2r$GtTc5?o<{`pC-hucLIkh>Z3h$*RY} zQhPUjH6{NB1*0H_8v(ZP@(hY>zVI1EQV|)LP|JBE?j~4?`XZEeT3+VYS)79 zhLx9zZ_nmBKb`oK+g4--fho`aT6bXIz}DX17404xB=(!h)ihe@>+`{%_vpNscw5r* z(1Ig2MQ9e^y|MH|U)kVBuU_qCnRDMJ1uYtZCcgtgzP^FXSISsR)npz+c^ANAo69gk zWCTylN3Z}obe$3PP18kq^#Ar<_)tN@lAi1r*Yy=$Zc-5eQCiBMHdd*^1gqhHGcgEry2pbL>$ju(@1on;MRxQhPZKP1{V%I=i(Z1?yBWsaDNUT(HWljexN&gJZ+ShIt@=xKTC9@74J?6Qd!9;G@H2V zw_gn|gc>H>Jg5`MYkkpp26V%waHZ>M8A94~xaynqpy5Ob!G6SJvQ-TBX)TRTea2ErjRl+L?S#t2%M$5tDGKG9PkK{ zwbh(Si8tF5b`PO)rAEOJ*TsC|g~>V_FXB;>{9zhh4Uj0JSmO(u?<$%Gdx)qwyFFW8 z&Dyq|88b`xK@YxAnsxS!+2d$jdPJWVLjLN<9qIKqUy8JuH_G;=#{F0-J%5Pa;c%7C zsRF>o!qBdMmN`6SOXPA?h~n=YKz>4kwP>GKFvtNmE_0*ywL{xq-k+l|zeFw}SQbt~ z@!rRKq(ckQP=&#NYT2SxF1s79@SMx5zffdk02T4y?lk><0RzZUyP)P|zE2(s?9$a@ zbpZMc5Gejx{u-E7_uBLKCK!2B>$KE95yk+Zpp+#ZCWzLpLF2r8KqW|ovX#q6f^`fjaw z(p=AQ*sK+dEi5z7`Jv+HELevWJ($Y_QmM2*=6YR zVxiOL&IfrOZsZ%}L?iW|P!(eL0la3jyh<;^QSBq))8%qBfv0_VAfwvx&wEF8h(ajt zTl1!hLZ(WzWYcX6`P00@-r}4!s~tTT-NmuHv5lCTQ5Q=PH?_H@4iFpJ)qDfWwxYoEx(h5++wG3K1eI#stGsj8MwYV)^LdwX9#owy*zj~m zirbu?it5u|IZa$;lDd|BB;y=@T}$Dff(V5g1lJ0;n-{A7TdKp}eJ}FwuDZJsvy}bf z^>JigZr$YMK&z)g$RbaxnfMkicgZW@sKAfpI#1BYI6e|F8`nmRo~_&cq-Ec?LnlGC zH0JkFPlbF&j?0|C`ejNRVIc1GvfnVZz_f?H5*5f@V4dCbI&2?G&8_iy)LqKDRxXUw z8`(8X>(zBhD|Ri=*GfuimacWkyy^LOK~dOmr8S!EJk9eS=H!hPndc~Nqm^WuvdrbxJytGjS`LIR%*UQAt^%-eDUQI(JQ}!UmMpU#UQ5pTe?^;3D5?(rgeqMN$0_wd<7is!yc|t9pE7p*WaXh z6hqO{jaZnoB89c;Jm0DRtzQ80bzAqQw}7Q>k#PV zZsFa(@{9Z6JVb%h7g|1`BY_2W02 zguT@3;LN>bGUR&s2o^>gZufrdJ+huUbpL}lxvs?u=hIvS??S7aqR|Vn8=1-AE6=;GWu@AWl zx`^>3lLe83d~nWo0pt6=Z}h`ypPoE8>A4^dFVG|l{27IjTzzdCf8N{ACE|fL0Rqux zg4)bZ18)r$1SRCE5+Hp}P)jMiD%u3VLA8mzv}M0fOCV%b~ge?g^C z^Ois4HBS{MM_LsGGvST|DCsIRm?hZOtTG?;=)d0^v6%sKXMktS0@MCS%B;sS<-te6 zG4B`{rN?8Fhd|o*${3P68`PMmitcvEGI^PPFH*nk+`CRbl85;RqwU||MkY`(*FSE!A$&Y-znSsc{ zhwP1Gg_$W=n<-a43AvER$~pMFBobo4d0HO-*rGU}ce7of`XU2S$7CMy2VlgBqBOw||K$+oq+N}cg zAm2L4aL3zcehLC6`@Crf-@LehA*?k|+Jb^}ul{iqKYp=&!wcMt@e&2W){cU5n{oF# zNESnYEUBut3Bldu&j5g2`Z+Wewlf0~B^Bs{z)W46*HWGB@RrP5ClP`-{jWk}>aFswTJHWNc=R3jI zw^T=p`hiFA@GW&fBK)PZ&ujD?xJ8Kjbf#*igHO$o`^U-xm~zI<7A?HO=ZD2Rhk1xa zjZ88u3b~c7lpkTWC020nU9bfHajQ)Ap*)qpxf%MtC4Vy$roYU0otyaN4po_XNFu|G zX@Uq2>efRfW|WP|dQ< zmO1Dm4cc@R*A%{G-CMXy6(4n*tzbT-w^E^njB+i8ZY+l0eDX5WY*N`b+9W~3_~D)? zeX?~GPGXfB61iz?xjrj+7IC`rPv!XDEi!c)Bq#|mu#BNrPFExW>sc{*bNmU@5c52i zXLHQPD4rb)RIg(KQh~)NG!QT69yODOkL`2qV$Ok<$QV>kto`j7{j37%ADB%^RqRK) z^GfW-R4AIrGo-$?B0@s&Mb1g_p;_@BQE?=;oNXcejW{mCIR?W4RTg1Azv8fNP~|4B zEiob1c{a8QfaSy|q$VV$#%GE7u58Gkq4@3QQwWBRb&gey&BkaCAjTsh%{C!)4ysH| z%&>)odb|rsfM@SZ%wAU{U!~pF5%)k0~1X zNrlddIfaP^bzCO1P=*pKD43&JAl&TL?B)0(j)<#Kg;Xw#WhE8vjw&#um7P~ectTre zl#^UnIe$16s)a8qyq#RMZ!SBk;P4GFd@iv(fd?=Psl@U)F4@D%s_+KjSFBsMu2@OF zXt#Nx5>gJaJF>t31wS?U$fs>}O+1cx{#aIG=06YIw)k-4+>ccZnpyaAZxPEBf!rgi z^;&ReoI?wF5AaA2n)#F3m zZRkL1iY#LV=EvF68(3&cexGeyygQ^YA!&UW_dSUW2_-^kW69a0(2%+O)LyxdCxB=G zo))lX-nqcT7GjxHV%5pN8ZUj`@-bYM@~RN^x^i9vx&4jDJEys8POBRDTY&On!F1&u zqPss%#0(XsPB*32I~NpJLJ!jTBrzCDeD{77YME0KK!d0!`S&`;YKQE6el-kujCfM~ zxy~2*vvsE*ZH+aHZ<_?TR%!o5d|DU3Fs8ejhLu_yeczlWBbh>(ADNCw)a zN)ewpDPL_p7daUa`>kN`x-WWnxcZ?LSrQ1M^HXTZjB(MI&x8E+>PwFYA#Q#d?hhbF zVxIEO6u-fICZo_*1po75tp)et)S)H8%4Zuz&k$PWl~XTC)Pn4l&sPqm<`{iBLloF8 zuJcSP;;>*th#d)8U!Lp5ekLrV$;(S&%kGK%A0A7L#LG$|=~*_Y!u5|QL*7TFXa8Mx zq83!>mh1XN;6&A?)B?+A$ujI+$Lw$W68PZrg!iLM30b=Wb9PxTX0qk7LM2}qXO~+8;)>A>958C-q<8Ky#mxDumOGR~PJ1(xr!ZJ+6X+51g~sm#29ElNN8 zNw6;=FbRR7E@%5?uyV}aBz$f3g~mN#8&M0+m1k2FJ(XDnad?jj_yhv4W0doze!alk zOMWP8m0?`N;Y{lpWg{+gvu)oGB+LfS=4h?vxM#z5AI``tF5Wj#X5FFaN6r76_4OY< z#j|3Klej-R;wu&vFiG6it8dK7nJ4|^trv~-L!=_dY(L4ajHsWL*+1RxLhK(v?PYuH z-(_=tyZcdhuefs?qhMQzJjQ=t^%UW*NwIo`(~hgOOrn5+h1)55ru`f{|E%~tq05@E z{if$T4*Tb_0RCj_pC=C>;XvN=|Mn_(=yecdFl0y`vCxSPktJsTrgj_+HQwX>G|m+% zU`T%7-`Y+U^3Eo_31t_V;`d6;|D-$b#RvssiF-dLLe#5LKmWJa-U8CiE_(2$*pYG0 zR0J0%W?IuA@wXEy7`vu77Tn)hP}}CKKg#2D@DqSyrxS==O7tS3r@*Mkrk7?aS6HlM z9K#Bj(iU=q%d9g8F&JxWF_KW@7eSMb{OhN|puf82PsJ)HO}=O2g#wLrh-(m*+i4 zgUc_PUt@QhnJFDQeB6kV3tB?l;v17n&vf5=EO^xGt7_L;3Q%SYLrVqv8%u z7o~3_*BLdP%}?3!gXlCR^*Mr<5#yF6V6MBrmnwJ#pMT`}oq%jgR2U zlkS!M+1+K>4dOB!HkvM9-V8L^JQILz@}K8 zlI8g-jHa45{(ycqpjv76EpiF#Xj<3@R#N#hNGwT89@C}{7z8-R81!Pzmfu47I>UIR z5@~lF4}lfOzeRi-tQ&6SRHEUWCSi*6QU)L{^0rf4KuedV2NFH1vV7}(ehRo1JseQoxUI71I zBtt=~LFoghc5wr!b?DVG*`=-7$ScM0ly%!vwb(yw@+oDjH_0WfT}AAJG@Sz@R3xq| zmg>19oTgS??n=g*3Xi*bA2bt!%BDM8T#$*ynRit~bM#u|`y*(*M@Yz1BM)fLLd~wE z+Pjot6X$N(y-6I&UZ$#E!B;2S@fZR8m86K7km36cx>pd2{fojU@>RzWm7rFLm(`DBO+NRmk zVw?#{+;{w3tR-E(ef5^xc-UOtk?yh)5p2FSN48=0!b#udg^MpI?!tlwP9SU;uc93t zYZ}l>=#_*6P_%uYgNzUF?3qNNV^qPBB{>yZ_rr;v2HOVf3;1ewx5jPHeE9&;zqy*N znZ)J+i-mcUzoZKZ**xA4fm%fmBX9pUBn{M@jrAoLqDS1j@`Is(ir}#k3DwP>BeoFN9)UQNyWqe~63?nh#8lB2+4! z=Jk>}_iqfH)(&NO$j>cqCPn}{3=z$CO)0<{LHJ%~_qxvG7{+S^2xZ0qrUUorOkoD{ z1R*&&fg9lYlcE{dv))Jeqms#rZGxGhH)m=pVepHnG7iy=zSPAzUgOdsE9=u1ipTzQ zX>Vobm_V*~-rqIWQ{PkG3u~YB_H2jh3{jyf3FrUICb>k&ovCzH%YDwG)nXM(^QEky za?=Qc9PO%UQodKw!%DSF<73L(0?W9ZpQ?4%JGLg!dtjC+Pc$=v3rV$;tjm=!xgnOD zC04iifeQ~?UB}w;!zw@E2o2Y7XqIuxG2EP^tM=4p$F?hLYg(@zIz_8M*i1myU2fOM zR&yZQ#rFzhR^NeT^k#jwWj>V%g@tm=-8u_4k~qbuZWOl!cW(gwtu)#jNJCjNg22 z-1Eco@|E!XbggQw#k#xRt7(p3XpfMI*{!+)7pvdg|910FYZnbx2I!!IYzYvZ&?*%L z4~nW<|6%Lh#DTIu})h>}g0uv#ECJJwZ6JS5yt~C{{G0M|bGzYUOx*JKrmNy^wYXqS~C~qC5e-e>^;X?W$3# z@F9)-dUDRSolw`i{Vk#1(d#S=GiRwQBFg z*>!H&o0^f{HQmg*e$oH^JGUY=R0Or?z%3*in$Uoc8RYTo>e(<;s+u*`O{R&vCQ@dife)+Y0mYe9KmKyCFn_Bmg@9eVz1 zzB-AAZ-zS;6zVeR(78?K(t;nC*$TFF$YPHvS&SKeqwwe?_HQ&63n2`ImTWK(xA`oU9{~c?&%+}S^1D^*N6!6h=;?<=d-MU~R1FGyjIT!z? zKC%%V*BGS>D9`X6@IA=k9>+mYEv`E~BUXJ8L{YofGVa>)+_Vz9^+u-5em0m7#42Kd z4PP29G-XrASz`4C0Pa9HXP6k4yI2FcisGI?oY3LsW+@lG&3RlG%sOp1}X85|oZqKq~9%YtFp!#1R2(<$i)9d zJg7A!I^^|}bM9?NxQEP)BKp*^umFlduj&Xt?o&+s>@34KUR7gCdrnQW0tOC>Z@hx| ze?e+ygiLBoOxn4P^S}J0v(No#$n&aCl45Wae zb+gPqCXMSRcd=WunY)om)I#=SB<|P`lX-ekD%LzKOp~}yo>&fxb$S}V`c9iAhKtmR zQ~78zSBcPEVMIiblJXtqjz>WH!aS^u6b)u*Yx7`;8OXJ_{Gxp_;@&BO$zqT3RN}kS z^wU2Q-Q3_LZ`gk$TzLa5k_$Lnm!gF|6^G6RnGo|oCFM`>&|P@>%-I_w;wmwpGPb^E zJO)dlK{DvvrWoRR1;-pdoLvP+y8RoW$a(Wubm^4hMl7=%7;8Qk7TuweEg=S6V ztAk=wVNdJI`AY!_qE=JO1N^Zk|{!%O2j zCNq5XuAbF{E|46agAjy{=wE8up4w_EQi56*Kn4nV5b=e|C=)}sa7Q-pG%{bF3s#<{ z-=kl^<9f{yAL2YrE)859@%qpgaIrs}G%(sj!iz_5xcnX3ep!#Z**~J~h-#aqiA6#8 zos|>!ZjkODUEd$_p+Di`s3!MGxEXZyqCs8?Jfr1V%z}2i>-wAtxBCg|#XNO#{Nvki zA0J8{n;<<2`S^qaynfX3U=(Bhxpn@-fgqdfsdo3J6=-& zRVMxEaOlJ*v$p~KN9_za!v$X3yY`jQq^7KSGB3Z9Qi|)bn(6F*v@%k(>JM>4$Gd zsh_ufUU6t$$i4Je?eh}92Xm2zWASm~?!h3yka^%;Bp1I3Ld)pHk!|wc-OB@?BR-jb z{`u*1W9njS>)+hZPrg{ZZ37fUVnA8+Ptd~+0u$Gg9(2;&f9l&d0v15%I&TR4bJP8p zoqtZWB6cn=W^rDh_(XC8knhd@xd_@}GCqb(Uu&I)yC~+HKZeh7m6^1h#eY2}S}mhn zNqlLt^3(k&(VrFa_t}3RB`!bt*=iwnU9-mw-A>0LyHSIH_Om5zJXvY3AY>G#NQhTM z_OA67UE*7=#E=!=U(z^f`5LuED&SeHf`ru`4esX&FOax;2_N6~WrDu$^aG_n{BG?Y zxLZ?SIA>n-%e>w$y_1&ub`%3BNXP*Q-js>Loo^?)%hrMWZ@6O#zQmE%m$MwapP9+o zw(qSJ)2(I*M+yn*f;!D5J#JeX-<9vd576|ijQjfEE1z9nWd6}M{C}4rTIt{0D9}aG zXNNDQ8?78Ytqfo8dbTgcKq!Nlo{nEzPScyY;w&rC7twYhc4hOgOeK&travnhTRGTK zta>Sq*RPOzx105TeHKD{=cI~dia|-f$Ic84W<*0}vpJH2ePHqD=_Tp~ikyCiY3v76 z$8^^{fVb-v3)-@E;1wIi@Vt3b(g$n*7_R*3KxJebxhQSms5L#CV}HR~|H`t1>sPDZ z4DTyny<=%^n=_tGKs2pGQrz&hPWRwII$Z8OS-tJ^mqlt#x>vR+%=C(jMZ1-&O~f9+ z(ay?=>mIx%^S`(NtC>fv`fitb?rDBH^^#5a*{k7~Y#v<8-0b?#2>fQ{`9;%{8~Lo= ztk*@2F>IhbuHEcIsgkYVRFfH|vn$&4i=KX#{>@#1cGqHizv%b6!ra7`-q(WPn_+Xh zQR|^Or<;Su4Da0F1|9gsQqQz}bBV6g{#Va-OX7!^3)f=L0&zEOQ*`pd!IhM+AETqV zss8I_Jn)mjHsTEJ9pbvb@=B5^W!KruF?Lq57qVQh*gDa!XP9z9Nk-=T7X22$@g7W-y?3?<=y*k=1t9fmi<9ZUab#OK|f}* z2cn$QVldOwUwAF)upRECU9Rc%+)LT9*T1LV$;vYo7X(s+16NXdT^va~bTQrXz~r|1 z9mU^MOU{1c>DZMT%dVALS*P3CZTHN|*t}XE*liZnA$Y$d{zJPylRq2KYG5t?{J#MB z4+roXxM3Ulc`rCqtM`)2^@Fc5bi+23Pq^w%d5u@OoJ&GGka3ZF@R5@_ORxBuqq%dx zIE=UXEeP-9Qn{R0w%ErAvVm zIKiuDdaPS`IZ(nIoOOcVdOBw;sqZ?AdjKxPwXduBlUsVL7rU|3cd{RP11LBKFLSP! zI$U2nTxa`x2RgSCIt=;yM^l?D)cTf7=SwHfEe(&4zvRU0Jp`WyS3wjx)=OoKQgMvxUdhq zw;TJa*So~ac`nQX3IG5{luZ|u4FGsS82q_Ik37k@xWTWv%18O7QvpOa{KL;U%*T9v ze{7rXqahf=--bdKT)`4Vfn29M$(KA-v-@i!{K~_7%P;-QN4(R|c7t+ED7-?kHvk!8 zqalok4h#TPcYV;C{K^0Nw#)d~tF9Czy|J79+23{ATlF1;LmV7}7ofsNq!KJt;2|hO zJK%lZ>-}>Ry?p!sz0ni8;JbXnPq=(H{o&g(4$vMNlmG=FVo2ZsEzrUpbR-%?0p4T# zsS|y$|Gnx8zQc$9jE_Frmwx!(0Xty9AxMKGbd5I6KpW(9u(U%s2!kys!R7n;-sAq} zhkm!qJK_Jn0!TOM3;*$U7+Y!67>oc8*wF%nK#OdcFoeSZc>U08zMywL;B&w4i~je+ zcK8EC1%U$z7BqMeVL==-u&~Pm3ju;Qkm8LPV?+iE1Q?c}xnKa7hZG}80`lbxUIdlL zjwx_?3ua81RA^F>g33uwDN>v!_f3YZ*|%>11cxm@ZkW^c7)fQKftNE^V46O;ijWg2aet zDeG2P7P1Iq09NctvMXCEkb6r3161lzc=6KC398F;ftG~_2rp5ieOd3t!6I1}c2V!h z=nyYaCX5uvoV>76Ue zE=0UDW(*42>4jQL5LXIfQ6l6uCM?BNh-*JitP47OD+b~$s&?a zN z7^s|bV0i(TdLk_v39V6?qYr7@8dos#G?<#Pi)5-4<~byk0bvnoa!9J23N%H90m~S0Vv$gmpjJy3+K^%Y zg=@8Rq#YN8DZr$0$z}2pb7!z7-CMnIq1|GNiKLiCbYQ}feD}&1mRzVXpp;_5sna}5 zj8H*^4rIs@0VbBX01XBtmi3XKE6xk!jBS0?mI9V2z>6GU0k6zEY>5PoJaQ;FhMI+d z!=D9ms8%Xl*bzn@56BfdXj);p*yxNY0UL)hF}%Z*WY{?)s)%SAqk}fi$RiUKgwQA= zSi*T27odgS&y}@{ZX0qa@6e{93SdS62ORF0K?oz}AvhvO4pD{|vnND*KegG8oOST- z7{~5AE{}+XAo^KjaBa~L#s$S6NnLGFS|7eO9KvzRa#ZhVV~yQVb>sphTu7Y%;@?@i zz64l=FO&Q3z5gEk@WmgW{PMY9Di02!LX{R7jIhL&=W!_J85ax)3LdnDHg|jT{r?|; z0UTciX22nBK;%@*ivbC677+~O!Ce->ShYUXj&?L47rAJMColkta!3M%Ar#>VNmxP? zo)Cp0ER!Sz7`_T1VG;DX94&;Pf)QBiRIq>{5?mC*8T=54lCXd_I{3j99ubL2RN@jv zm@f-Vsu>70&k9DMl~ZAW1{2^}i%wXMG!W5)Ocdi7$(TeObO8xglA?%c0W%EXAP&MP zMi0CA#SWGck9o{vIcT9pWn5`?p~F%#x&VT3Y%Ya%1cfAk*hoh{5|WYsl;k8SS;-lI z@RBB+K?Piaj|(V68+U*Oq#X4qM|dI*R{#MT5RgepO5%&KpaCN-SxZ~q5|_2aK?x>6 z9we|M4J;4{4GtkJVuivHmW1U2X^=~2J``axM}9CFsHg;s63=AfXs>=z`6r&jxsIOdGsTpTgp$MOEtmQkmLRl^*q_X^`qv zsajR5UKOiZ)#_Hc+EuT9RjM?ADhIHtc3k5Vj0`mtoqdgX|TX#DO*{~UKX>N)$C?D+gZ04j>-WR|5)$e}!+u!*5G`gPf?|=#aTwntqIKKckuYeKUU0y^Vr8hCNPkNT;%<#*vLuduP4|s8znzE!A-^hl&PFzDOVZG|78an zrkrIgGnva?{_>W)EM_Q!xy)u(GMd$VW-_<=$!vymlI1*SBfr_scMfx&i;QPJ2iebm z?s1?^+zdk>8qt3yw1_^U3g%I1OIx}F6e*0GlLtZ5zV9CP}@ov;BZtibC3R$G7jOMT%j%U9RYo;J0uO$ZUZ8{YAj z_q?$kZf0}2+!5Y#4*1pqf8T}(MCix5tNjUgi=Yw-uLQjrUT<;7nA{a^MjJ-3hIL7yj-t7@`_YR(YwRt8#5{7!CN2a5K^{4~%16;|8xp5;)Ow zPFSH83Ws;XJ05a{8v+AuP`c7-zz%CHeFbfRafD-zfqXl-R|~lL#dA)GorgLBAUFCT zl8^u*%s~ZnNI(**@CizXIujs(1E}L6gR4{jUFHzZ`nm}-0)(_NN?E>0>9$3vE#vlOLq0@X1>+H|_hVSphK=3Y(2>Q?e&`$OO zz#43B9;hhPAfW;>@51Qs_tK#If{z0K32x)O&I%Gh24`>v2O-*spb%J%6XqZUtReDj zs2Hrl;y{faWPtZ9Ean`6^A3#WR*==kAPz*&1wTRC6wV40ZriHB3X*^eKduUlfd_&> z2!vpAv|$Cta16yT0G6=9l8_AxtXFJq+8{v&s&CpBzye&r2KJB-`_K>nkPmrb%VLiA zV$Kj_4(75z31`j|Zg1;+4G*=U8YYnvE71}!krLl<#5`gV8Bquv@e!$Qi1LgRH4zSX zZv{et9*lq$ThSF?F&;+I&Pox(ngI>8A(N132L6l|In1`S0T_c(7>Dr)XYm3$QP6a( z7n3o^mXR52%o(3C#-dRgS&SP0tMSCH5gRx07)@~-VeSHWpcuc=#5_VA$!t@RR9kE@WBD|k}xf@0pCFg=F%@0GZ8$AEeq2vN0J(Xz#kUVGXEj& zun#ivG600(9me1?Lo*%%AQ{ARGp!Q7;wckClQrpK1twrHPg5%C3Lm(SHE+`&;6RHW zQ#Ok-04@MA*WfmX^B*9AQcSZqe{v0rVFE^gIG@usCxthYvn$a6AIf0?o>M#jLHIxb zIjIvS*AN6`0S**%JD+nN+Q2cv6DX~d20Gw8%bAMW5pZ47TR01ce97|5ehLo&V$&Apz+>+oh=-?9E-6aHfhrnRLCAb6_+}#NtoXhjQ z@4ENfcC2W^cH}Ci1_9W3JNMBIw}$(0s!Gv3qZz0p~e4*N+79j45j1Zb_)1ONG}CW z$m=#S%_kz}lh$xfteZq*V0_PG7AWJ=v%xE;b@pnF3;}@nx-kFuVE>szMtRjS;{o2h zK|n-8_-~N^5rj8*i1@T0k&wA0jR^?p)KQ=T2``HPtk)Y}HFyAV!1>X5>zshl%>BR# z8MEueiOw@USi6F<|B?mX^RIHz8 zH{1GD{sQQ;kq?@T&=yPj2w6)ch-}ss;tcmEj)JT{Z5255oq#WB4?Y^$bsRx;W%w9` zvnMlsd7<@J?#_OgIUit2H)rmm^kTV@5Hn^5ucSS~**>B};08RjmuT73|;-GuU& z?6nl>AK!3ISzuS@L=sfRW<_q9rH|#k?+V_7<7Lx5o1vGyo`yq8B%Ewa%@i0);aNlC zWLYl&V{>&Q(nh%(k2U0iC+p;nsPAe+bpj#=Lw97Ylkp9?J3z3)DUV;Cg%WgsmW$#F zt8@f4G2#i5=lh30yaK$@_F3Y1lV+|kK}_YtRu;rNlfv|}_=0|Mrv#rCMboeH>(;Hx z8V~}On!n%~oI|rGRJ6NE7Q3--aUHAO$`;h}$~ej29Jjp&#;`LTvpI5}iRYCQy#Vs} zt@R`GJFzWO`!I?UyeUa)^#A^!1It3O zhFt%SjO^uToufa}Il_f`?`wQ-JV;#_G_k?3{w5K96Mdsr;znpMinVmIr*d7Fq48Z^ zghdAj#Ke%%nO9-pTlBNwZ?+3PN=M&^yk0)>dJ!R$r?Re6Rl*Ah-wjU4DGG%31bh?-yh0v8CT3`y_?v1_jeCR6!7Dyi*@D z{2!%wX@1>y9LRA6bn0bh7hY7i&4uo8!1dp$amuts^9 zZhr7-6ZKrJ%D5}SH(d1*HkWe6@8m?XII00$?JoX=kG{zI%7|2awY6wLww(K5&%Z3aBA%+)&9P9!vby40 z(@w|4d2_ZpXkX}LqGHNm(pfdnO^y8AIC>NNwS(#mU)frZp`W?CY8ktsy(9SzyFi)& zwF?`*{3iM>d}v24LyRYZ=plMGN_E^(fLy0Cw5tcu*5X3SQh z&`Wh$_S+c6T`hQovv6b*r9;EK2k$b7CaG|wEc2$bgyx?Lo=tVmSVi~k^rrWz8*47f z{!DM_4dMjIcU2?YO_ocnlRuU|06;VV{;_xc<%#La_tH_#gF1hthV`RU-XywarFwrw;#UkTNH0brcNls}i*XLx0M+g&q`{df4jIF%p+-6c(aCR#7gNThr;rFbKR6ru)ZgK7v@%Ukf12h8u|eV4 zzrFsJ*v{#6ByiAviZyb^VJX4=t|}fwZ$H(@jIY6hXP7{X%Iu_3?vndF?uI%*#NY0? zm*qpmT94a2RH9NMsns%+&*sI{80)ESbd7#iY+`1_&x*HMxW2Rf3}d1Cp^6t6ed+Nn z24hi#P}zke*Y+#*39)$iY<$9SJyl4ua+WkqsPmF+x?k3k1B6)3Got9ahreV+3V!tn z9vPuRy{gE}bS5k4yN&5kE9$qI>hJ4*_golccL+@Ocs1gd7g64ZpPW+8@GVx2!SGX) zYPVU#IJNRM6Rs&+Z(jf_CVEW3pAF@IZCV=(>@!AVsdX9dL6@k|^HSFK@(p~c)uu+X z+6E$55Dh!hWxr?iRK_oCSg+d=x(Vel5VLYnZh+pG$i47_W+o7EIOUB`%J*$jw!gr5IGir#WZOV!m6QY z@L|Ci7buu`ucuLV9iNEK;IaPIb5^vz_Uh@q%y~u9QySd0eu#P`pzq;#`D!k_rHXc~ znXAYbKmh9N(AyOwG4VFCzBbTSE7&Fqv}xQj524^2HA@)Hp7I_#5#!-N`7I;}kCw|S)^P-Di$yUC?$G)b*+lG|RqhNi*ey<(SBg`vj|N9W@sdm3@$TV}bqJvs zOBCtkza$2PyU5f8-U0MahDbpt3gh3xj+B#+BGhn{9honXyK;tGN&*SoCJ(QC*2)g@ z^6}>({h3+3-H)&bDQ24VQRa%ulO8y<$)Eh+%n!;G!qGC@9FXkQKW5`EZ-AgM1JB$o zOPXi;9#}9vvcmS^{pgD3^^RNb9oeZ>2hr+beI}orh*iU6hc|0B4*+8A-X!;{+fYzM z5~CYKvL`;*i4fNqB`fBD)JdI^1}UYvMho%#`5EE+X_%7EPc`3xvduTC>SVj~3#>|c zX=vw)jS~rli}iGTcPxjH(qsadJj!_NzLH->FrZ zb`h+g)=Xt&UVMB0s@=+yxTvQhulG~DPVfa#(*H9yIP-Upm$(=1cw=H<6S?|{}W)Uwk%~j5F79E3v zb|HL-c|S65B6>!`b)H24xT9p9);8PezxbZLhH&yy#bSFUsWsQ8H1Q8-CH8v!36e%n z^d8t9EtjCdK*L)lv%AM)O;#-q>6-Ng0;P zhF-nNK(6UP!Njb_%M$Hb!#K0hGQ?@?;@gJ{z!;{ZSt_#{1_mGyO(cfys&wqQX$f?$ zR{plLKg^Zq)2-zT06cdaB49ncOlk+`91M~`1)_u0w^5WOmv_Pw-tqRsFRRN~tP3yo zlO2O`BknR%GYZ2GC53Ay5!*6?XKd~nmbedZSTeHd=g}1d{kWLlNKs`6J3gI5a+sr2 zhr`Wl78C`)sep>!9~?o#^lZRYbzspq3yeh`EEM67hfk$!-i^Ua;YLr~_%LXVM!X9&ztG?-(W$q>1WY!0wepL)xEUigcNR)`e?t;KzLmWvQTp0I`dZX+U$DO+?6EjX@qCBM6RJ=$mZC`TW^*sue zL|y<>`L_*$C_b7@kNqKh2bANYR=dxo5_tyID9TqK_OS1rxF>?2Et=9h67iY`mp$il zgy@{Zv_Pt>@6vH>pdw}^I5lVb zpW|N+;)N~HZA^@ju1A5-<%nQO%;?|%&bJUt+4t&Iv#et`23$3hGbqbFflJ{V$O8&1 zjJopx_W>Wh;*R8O!V7YZh$(ngy;ha9XNa{q$EagUvxmMpVuXD~3w>jH3$bgXij-Pz zqK{e~>PCkB(WFTUP}5_pUFLnZb#_cG1E)*Yubq<7i(8C#1M!7c8F|_f z_`qqLvwoI-TG9(3;fYzjuHweRZl!9RHRK%$M6?^8Gn+-8yW?<^6E|yE0!Wmb^t?MPDb< ze`gm6!75DsbIHNjpDGX!%M8KAFhtLXJ$Pjd`o=(EpPKoy61WI_zB;-Q(by;5>6q4l zjgsk?*qlwFSU}C)DCI`sGk2Z%DzX))EB<91@2RQkvTig9)XV(%Yc7mQbCUrZ8M$Q+ z0jClp9e!eW@I-`oZgE-)0yUy>&{-MNnnsv&z{;BXyB*`8PUB+KOy2q|i=U~L7p^Ri zAs9BLbF+}|Q4`OFab16^!>jy%?6-U)>x*j(!QCX<4@dnt{n>W^X}m&TUhp~@_INaT z(_kU6S~oOAzcC)4e396=Bz}Noj9XzFuT>L=!H`B218r*3 ztJA9k12-lu0%d>pLE67AIrAbOVAu=w>xX9>3;Zp&h_{aF?@aN|h~H^a*+@Uh>aBxw zsm7sIY{y8xm;P;JiHog$?Sy~hhe_q?L8 z6(Hyld3>}MFXj|$Dc2)oUGVw|`CjdefgJFY%=mQutQOb!-Ph8m#^;_+jGz4?_{3`_ zT~Kj)y))V&;m9z!&4#h}1*j}`)%DBF2zIZh-gcmM1eI-eN8Q!^<&lE=GmvnZHP(`P z)mc0X;{cDnh~n_th1ZfaA#Q-KoyGhMp`!@0r|+U>)V6sD3-w=?p0JMx&NYg_CW3Ki8DoUfE^djlQNiu^Ly=CV9js9KoGy&o!b~`Eggcqae6fMuvYZ6E9*xt0 zxpD;9@A=y&_v^^AwD*(iM(>+9;rLR@P)wkN5~hyaPulnfu(upH&vs_D8%T~ht+B2 zM?$MP8&m2K!0}Q67^)v^ROLlla3spz6;xfdtt%$Vr#iH(*;vJ|r#`L+`xUAE(gW*H zCuuIHX{8R*TC3WxB(pCfBAYHX{ly?8z1ja4f^op0g}I)c@-!#;47}>kP}8)SnFq6u z))W?9XXC5l^{?{oKM`&?&ER>iC^O{r)Q{}rS&GC68GwAn--N1dVrZR{@cePdemHof z@1MhU2M!_RT)7=CGz`Mpa3$SqgzYtt^Y4tJ!KBwLAs`ds;RX8r!7; zxZWkX=A{ZU0}I1jca&l!XAfcNK2B6#cH=f-+hi)mx0{|UKyo~_kh3-u0n6a88{Z7& zJ9}aV8Wip9qg*Dva;nI^fIm#$s5M|gnYVF6G;%zeH`I6CtHns9vC2wP8>$4MK)E-n z0pnK-O2tbiAP&Sb&7`R6s*6I#jy$xDaYLlUPN9$)_teVSF!Krw=Na>knaT^o#Gj4a zwTLbULS}`5#q5r~v_@Gb<6CEAM%#eoJO;OOO>wzd)XID4dq?VF0&TNlrgU%%!yWDf z@5hf4%>pX(RivZ;+-T~5UQ{*}yWjF0l|39O`Uy?1ifh*Z2ZbEniRVvz{)KI03tWVn zjXt<|_k^JM{1V-e`i0KOR92j=gBcVRzSl%PyEi$q@79fA>Zq?e+;(<1ay0ZHzh}@X zbF?*e<~$$m>q=^rtMJD;dsv@5uS8A--!uDA#tX(T5@MqM(WyzM)!<72Z(%#-RkA?P z($;;Dp7+`?88O&yE^zC{l{J*&(K6VL)~8Q9&LOoJO*&|D8`nK7MX8;ba-41pWL}2V z{Gl-uP$ZSHX8e9r{q@QX_(+A(N^Mv(Dc)M`m$}>iFI&_!R=L%8Xrs zxj@OWu8?v&4U4+*d_m2LNCy+(g0!g6-paky6}e-Kq?LW0*hg;qSE{#v{(cs>WBc;r z3|UIzwB5b-Es!er7;4(t{0-s_y0WvW2PqGG#~Rs7L+MmDP}^CaUJGr;-S3iDjVIy< ztB^)Hr_{X6>(B+T6aV}Rph%{4JJDJ%w)x^RnyJ<@?FHcCj}m|s?>N;~L3q&JJ1gmM z1NWmf(CqxGY%Izr_}e{v%}LW&b7LDBA&M3N7K7?oeGVVB)M?|{6-!ag;)nq< z?!i41VQf470)vAeA)$8;QEp0Lvou65wZ(Q3z5#qsGsYQO>xod6{#wte6mE?r)5iQF z8RMB|-@|wQ2$wXO9vYDJJK15zbBgRM)f1;jS%rF+(2WPC6*XREUBMmM0u7l-cf;EQ z5Pg)G%dqT9E}GO2`B*1NIsYz;%*lpaF`=TU)20eupWAMMn)Xe#P$q{7hsCtD;xM@< zc1R~HDzd`(pqvvzHxp&DO*y4ewT3N)NTYT#H9i^<@1$Jbbo73|?k_1$>VmxDW_*F~ zEXIAzo*~I6^X|vFh1{RhqJu5g%1^601bsfDxfW4vevB81;fg$Z0Vw!l?Se-Kx1qrl zdDdd(f4sr&7c`NR)EzEPOChjDGm(WA6&iu0OrLn2+6liH%lkyvM&C|PHyPsfpA>}* zdn_0%G!%()8)i6NR)8e8uXl4fzf7IuH2C6vO^jx&?d6p1eRexA`sUTr{ZC)vZpQTD zP5D5R!zv*&_sQg6+$@acSB%WPV#KB$jg?O)>E9QfL2bDgK*N6ssc@mFx%~#)d(96Y zW#wdV19^9w6wXUWVxl`4!Ry1Cf-=U6=$q2%JAK(}qA`jC*QhDX`S@I(H`Y5PzA|1G0y?3Xw}4B9GHtZN1wN<2;_K6v`)*kX+3sIoZ>Cn>GV^-L>?LyrQVV7@sx=I zWTz;oaHTQmR6Zr72H&UF@oOb`P_f+m)PT|oPiv-@`?Zo~D8nWg7U@XHg|N-PJPlLX z03cCZls`TR(&DfDFw0I?74gWj?t|8CcAQgmpHq|lT;xfuYlBfYSx5DyYBjs|F$Occ z$2NeN)>)K_(`Hh&N1BWT-9iViv(|AMMHGJk5w{|<6w1wI^sf#2=w$mBC_83q7ia5t ze%C%8ex9*4Ssc(D0>caXwxR?LB1MKoi?crbBg3y|r~)ptGto>H{+oG^$QqYFStH<* z+97Jp*~Q?iwLg+~U%&I&Ztie%W_rS@>lsfcE}mxo=H#JwPb!FNh3YKNAkp;&0O|C2 zJOc0Y#O04sPT*e5>i(}QDRm+9t#*9c^wq5M2;{R6mE1APXy7NZXQWj^L_<2(R6*U9 zMOz&c)7;t5QTD(3S{K4178%Bq-FqfbZLi3mMe9(%ulxZ9D_#VX9aVFuit?wTXckd@ zqyL5@p=ouHBSHNFu=f{%*Ibo~;#C{3(!XDD0n-~9NXUer9#bqKD@;@dZEQoj*oB~` z-mkDUfWcZCmR!XwsR}WMOQ{;4kFF*`b;e5plD{FF z-^^COtB8J*_b}^EaPkmHMISOR^iQU_J&Vr4w+a(SG`Ch8mVbu}oc3GztE#^Tw5V#S zT`r*;(KK-8@Gs<1LnWUVnd|eAJVqDTQ|DYt2dlo36GkY!7l9vJdH0QAZ+ww!SZ4VC zp@?&#@z5XBTK;%DmPTcOK?`J8OD}t%ROBOS*|2IS59TJmhs%b3N4k!1EMV@RAK(0O zr9Rb|)uw0b7IsbV#Ch5;Wn?XDWRi(K93apwOvVRmTx!5c|J6Xu_O3)Jbj!94#v~$( z#BgpZx-_MavV{4`YUGo5)CbZm95OuG?IN*S6+(~m&MeLWS#RPSiL0@%jr!G4PVN3l z($=7&@6aDA__BnF%m7Y^s$=j83sY#3GQ|of~PgSHl|rRj?e$Dhz^tn>U2^(3v{X2RUO4U zc^f$-qD+_QSE+>vNnSzO6JigVd;0aXby+2xJG$xqFXB$y?Dqb8+jNSz#Dy~M-@4en zuUx9?Te-=17Sl0fB43l)*qM+tNCy58?f^woMk5(=EJj?d*;exNB$;Ssyh_^eeyOIA zwhKbo9qY?EKQ)X5@XPw6y4~?opo_wcGxvD(=$jka=`EA422ZMOx0K z0`SO5vEY1geHhr&50f-K+@oGC*->JOV4mN4_^6_0U5`3RxIr1%k{~tom+-5jv#cs` zf85&Nq&Ar-TiR<%NF)dH$A}=y>`F_qMb#R=xMT567VU39XuK)|4dKaJ(&#LtaEHMS zUL8A!s%Y@aRyaU&yx@VpF|@@aSzyq_n<@UT@jgqtR#w6H4EkQ22T>Z3r!VhEfQ&z} z@>19Iy~b%s)wCVC6lW#n5#4`!}8vB`E z0H^ORYm`^&XDU`|xFCK9*u5_R$IdL9sCbh9jn7!#ZvU;j0l=Dk(Af`Wqp|CpTzvp0@}4cG&(jO?vNiwCeW0gdLhJ7}QkVnf_jdOQL-GD&_g?>92p)UHGv z18ss;6Npf)zw`bmxM8hZax=v)+BIv;CLA`)gl_$mIF?WIAtqGXjq6dR1*I?o%47ii z3~=RQq%qCL!r>oWlp^xFDnrR{j2U)}Jf`7RrE>BuNZvOKJ>YQYZVN)Wf9s z1qCgepc|}IfQ<8}3ztv{+9o;_S&_6K2knG>#&_ktjm8(4k&@zfN)~-8D`R{zVN}4e zAQImEw|xw1szVBtz-7DQ)U85BbX7ccVF3U8+^REsva&+e6sPZY4Q+labkZW6gYpad zb7LY#5vmsax%hRi3Uo=1cq-SpvO4Ud=maV6|JeZ5>^OiPiT-t5_^{V@(>DrD-bTZe z7CPi1_Iz7gDDgn@)uGg#S~gLsNCs3Zov*t_?W>Qs@7dj-su0UBH%I?sY$;Khh~K6B%h5T|eRy8+ zJ+CUrlW{!ouW37s+fz^K!z&UA+~y}oMYdv@aFdmvKP_kHn$y)wB4k8D{+b6medGcf zCF}>N&-n;qY_9vo>`wu@TvLoc%hx{IWL1#47~M<2KXACMaD~^4#UBfU$&yu^(zu%$ zF2#7WrBdxP`oy|6O^~!|OHk<=%9w)P*O{2c6XoJGBG=P=n>*GZs2E%#U1Pdcjkl@u zrBT?)uqhP$cxEMU%*EK{1;Z$p(6=6%TZ$5*3rHI5l#UP+8hJO>KT$SzVVP>Hf*^Eo zE&o!M(v2qXoQiDbUGm6Z>6GYqdcrv$+{DSqYE@$73o%p5VnoIYRKHXuvR8>vxuB-D zI8)Oj&!H=jP%a!(c41YunCz5GA7{Vc=-0sf;sww3@I?^M;&D*?q_^>-)Iw|m@^;aH z!6Ttv)!^j(tNbUADn{&FTV$$ZHiuGOC!<7L?U|6ao>$hJg)X9q!*-;pccfTc`|VOX zDO>V7W#}E{%=^MXG>D*74;Bl$8Z6y*C#5J~vI$SST2+7?I84=lyFjd~!d2HG5b> z-Wl06VDpeOjb1(i?XgJCOp8U_dfo>3BMYte+~igD;pc>SmKfqt>Ora7pOC#wHgUK= z+Z z?=kZdr9&DV@3tVPfrg|pi+RJTw9@yDD=u*+S%I;_cdtN7jwdpO|6KeO>M%CMiUeC8 zL}*+d_&}VL6MLIy(ei!8#adklV}m4kD7wL{KqMJ=5p>L3<1)U}z9Lv$WKg^`gV)iX zJ|?dxqKphk!HSijLH3wl7|17L$)00EqvNiy>dZw5ZG~4W({d&CzFj z!=tO9F4+tM=|aD*7Zvvl{2U2?B_pk8mfP49rAd#JSkVT2ae(p#ceeaYs!x;@wy-OE zLWY1$7Gbk^1ThA%eP~KTBF_!XA926^cUI1!g~6y}{W5V|RL*qbrAlpcJv}p> zrfX(d5ni@WZcI1Dk;wrRSJd3=g!>M%ylG#Qx;~yv9H!Bo{Sy`)9FXb75RKSjgXalv ztqO|1Bz{I6Jh>k&5EJX{$^Iu(^a(j$c10# zoIen!vVFHl>{e%cp~ZO}M1Y0f==k{DRTRJwYK~BE)R42LSYxY69;fMFC~KIQz!-Un z^g;!3vsrQ5#*~u)Z{|u%aL#Z$Ew{@zn+Z{lrtZpYaZgxImQCECtZba^+AF!h{PASz zU)XqvJ*pXXjpOrVjr5h{nohCZ(7-jz9mZwv^Gf%FTsu>N`pm)x{V6QrPTSK1pKX~| zVM8Q#29)v3`4H9)@2G@)#_#o<%YN@@foz^19wB_rB3rcO$2))QJ~ zz%-H*@|8y>{eI|?N8K(m+sN8g1#deOBmJk|QX|jCmc|sN4#5QHtbU8bJ~?AO$ZWygaRln9~}Aya1jS7C^1y>V5JC z!k{)?s$Y3ZG9iWTXSS-nJkKjajQv3KULTISO8>%3BxfWl8LF?R%%~=@P5p^UOIm4F z{69=9v@+`+%}bMrb{x-=CPIvYda49VJ?GZHS2XF{)%_(fpb=XB`iAV^vN}5GY;Qw? zw($G??0)eiOKgq5BjW1d&6X;otDrRR?T@o7JjuJJ zmj*wxWwH@g3rnU3;(3|y4Xr21)3CS2dV`qtaU=-=tGZY2mAJ*lx4htY|GqydJJkJC zHqH{{k6M=9`T?O%uNc@y@%WEopcW#U6>a@5-c<)mvq6wZLD_Wd6zJCPzsIP>xqO^4&q_x@y-VV8j@w=u-xe8P!uF_qW{6 zTZXPFSPVUu_jZiH0)N1sWHoq>b;x|n^5$KnhrQAqd}61<@~$Q`$=~ab=GddHjl>?@ z6x>>?x`6uABo*j!&2mn@vy!)DL#gC()v4Pi$;B>Q+o}D%l~Vqg1D+V&f4lPh=F;py zP<+EiL+WHQX=c?)CHBVPXr$^~V8y5E#orr0&O*i0yvaESrtoMTS=+8pW8mk(s;NbH1sWNxsplg1J zJz6)BQ54_bqA%vQA6afp**Lh*pP2Mn1QqC;C__UlttZ>ADln$;`y<@u8^?b})z`wG zS}67P6~PMDlYNR==L5z&S?5~pST;;QqLcFo=v@C6(IZwWHhVk{3O~TM)Lkk{T{Y~a zy%>9R?acdN=~BWz#w4tIUC5t>F9r6LEBWq}e_cI9CYCv@zs=Et#VSxo2%fwTZTk7S z=RVZ5);7t!{zT8%Mm`<%8)Vhb{>McBL%(>OtGDC4|&@+5Qu>fRF0g(U=OC>GJ3I5Gc)}7{GjX%*=xCf22m;G?2 zjf#(<9OR1X-o^Ap9vgp`^>DtYTj|pD4m<|=fM%kk5Z~B8q}0Nqj#wWKPz@gwg}1XA z@tV1<7bAeBe_!(wBN*d|$O#Rg?^fF73>O^NZo^87Rn#;~23gU?A_3b8>*8F>Ld!Z0 zxZ4C>Hbe(2B;OHy9!+-)q@T+w-Ai0{J`juDhwbRZ4Pv##+5g|Z%@t!H6@LDufT;uQ zH_AHA=aViUU&A{eMqC`4^HBH#$Y^J^UMRfNCiQv4_|~lmU$s90w3w_495wMzWQI%F zwLxt4Vu>XW;}*Q4I>s_YPBVJIUoU&4H2fC5VP6FCD@m>I(cw3Q7xm4RIFFP z@1Dl)hurnpMygqO@1q6P-n>I0c+XnNW{yKsB;6IQ zDVGJm;F;+mE3U#C@js+6-$R{g?Q%-Wp|A&j%TT`kxm{A>E9}5JdJjoXZVSA>RTyGQD9`j#y>WrNp{Eya#eU(!hZ zo5Fm(laGc+^adt%oo%81JP!bDe3N40$455}uA_;WiL$Z-y4k|7W zkW(M=DhSSfalP0Nf-D|f=fNjP!FB-2$p5DLmj89Mh-Q&iV@PKmJ3GbcKSwor)9!qB zp9G0dFw6fNz9YMmZJmz;?u`t_-!Z5k@LU!YM#HhVKo_y1)N{|_*}5-c*^TjM^=e2ej-S~(6&@!XQ} zQ>K0Wp{gdKM%th~{-XL@TLiAga56<1L$)TgUE()FmHX3o39X9@1qvzduXZC-yzv?% z%Hvzs$&3a}3Zb_oTjsCxh7NpOw^D_2&zAXG&p=|Oo`Y#Zg1Yywvy7>ld;yE4UWX4Q z$CMm;<|~WPkR}9~F${2GGF5{JGAyV?9W}D9&n2&V+MgG(5a)(W{1Jkyeu&waml4qv z5t%}Z9hz7dT!N02X-tD-HXB9vIeM8~uHAbki*T%6B8jE-+kkI!!(hB!`2d1ckHXr0~he(HGZNb$u-gsE-~6c_M~%wzhsJkOEz1Jv44?AIaj9O?u#16((zq&^`ly$qGn(JQ1?Y?%{n6Ikzn$oi>OXA zEd0p-vByT}AnBg&;N-~QE@SfDVaE8q*>kw))1@D`vL|f;(f_nm|A#JfL(80$CmW~! zSN@7R_RfXav5E;Yfi2x$@ec7SFS1qNQcoSR&oW^{JYT=bZ^@_v(Mw5cd6#3)K{skB z{)8xDc5f->WC7H?UGmn|z-EzhiI4OG6GG&Ov6_m1?bIll>LOK^_hIOZg>M9W2=xAa z@)Y{G6$>V#&Zv-Zyl3LgaT)a6ZsOl#$$N&_BnzZh{I^M7+f+}|zGrzxfs9w*`)h*z zPQ_>2@$AGkMJmRE4{H=sY0onuyMpGA0iJb^QuH``^r zmd8nBu`HZAP)nbnBd>C)__wK&oa(p9XIU-zA#74a>TB#*iV@|%+$GP*gnB_0rzqU- zH0c>~rB(R^c0aD+@BX6WW!yW&h3^;H{?5t~D1;`O68c3-rgXxG!l^LP>aMb%nOklz z2o$Sn`v)ORh;Mn{|0Z&u2Vg1v4r|uVI7(p%1-eih|Mvn`;mjWQ&OPokG4?Icn zJxGPPq8$z3Rt5$2`MTD-_?7Bs*_pAMRwsMQO|`d7 z28Q{lrO%$}#8p${sp`%AfxEeYr+!3)GOJK<4RZ*eYN3l$}u<`|q0u|w+KnYW}txWVvwi0JC zZ*Wdc{mEKP0&dn3Z|W~??P}SLk{yAZf9I09#Lk?85TN_HvlX-Gz(~Jtnm&Z7?Pj`N zy8QGVVQPkSOrGI54y3#|Ywd8>eXqwxS6bzmZQIf7=CY~qwQ4zEbgz-0shOoV`%@+7 I_p$x3fQ5#Eg@J~Jg@J{GgM~-_)m^e7!FtM@m2q}p1@W}|Uu}SDj$SA33XlZbX8JHQUnJK7gsQ<$V1RNY3 z0z3i+0s;m#9yT8J|L^kA2|$B~l7v8of*=JzqCr5RL45QA2mt^HXebB>!2cGgPj4aN z;32+zc58kGKte!4LVbD<0|yTW3j+fI2?c;ggZU1N&H{%aqGUws6ch`O$(mc;g+(T+ zJjG`0oZr25iT#cIM{r!;6$K@`NiAuL>03;6b`AckfoW2lsO3w)*u-YU zl`7GfdBLJ}sRuQ<*Vn6w_a%$$_gHE^^vIo#o0|b1;-h-c55N(2oBg`yu50VR4?s5q zoY9o7r;q1OCE^In2jEP(m9X_{oa;aP3gC>DbX|SCb}}~gmwfG%Lsk|DLOuXC{{}t) zGPQ(J;!aa%C%_t^>C+FuWtl#3o%_UhUF&w`4eh_Dyc!U`@Ui#+gv#{Z-wgO zO0U*3#P-z}k$Qh=dpop_Ov}8Gs>2HQPfM-tLa%$-25*V9AArRN`p-kgnyz<}(p%b*^$r+U#W0Mb^UV zDp>Lg>KCx$hjhmsgQcPuHwy`A!sIuEIM~xl9Lf=+-T-s|tF=jTBqS5lH}~M9{s3eG zU=WA73c&XI(}q*ky3x!4!gh`yWsZU+E*6;7@z*Y2VE2C#c21;RUl%!#SsK;=HR#x- z35o}DjuMOCEL5XfLVK^-B;(BD&3P_YI*rSyl+(=n%3<2%WD}x*-{__K94ymE;np=5eArBv8?eesapoJBlKy4AihV4_S*&;a+Bng&0bV4Iumk>k%AvJn|3N5%?UMPWl+wLUtG4OPtLJW zc5vCvs@_4`F?-3@&E(h%D&h`qWRm=ryYx|my|6@lfi?{`zh1uD%g=LL-j2-+T8n#_ zL5O22Vk(LMPo;NPuz=o< zDp6lYKlh8>(XWCAVeBbA2VF3N!Js7`ccSwIoKgjE+0i~y0W^heYyFAPiVCo479tN4 zHLC&6M9b*hhODgT9RrhsEFurKtaxf>KFm{-^vMNe7Q<~1ZNU#LCgAJ1#i1dd? z)peq>K#PnItmA|{p2+sMbWd?>s(pH9a9^5iUnP;mgh;i#bCh6Emd##zHC_o%`uv=c z#6EI~Xh&sEHJimh#OOfMeYJ<&IKw&GZ6MRL$CoI7JhBrR*h5y}HH_jEe+c6(fifVN zl7kc{(qqBh2TRZz0%xL+wL;~9w4Lz1p5*yy>I|~WgV1HBI|#ky@u0$HU1j8t`@nb` z*~;p(x|l62uY*XG1Sm<2sH}N;wfBnYyE23`IQj}mFWG@ zJj&>gqcxSrbet>G*S!Q!5Z)rO){Fz`40vCB&&`!T+698Cp}nl(e$2*=qcvqeCNf(W zPpe{kgok2)y7b5cnYI?gRFHG)62SB~(rlP-s?pb)y?99i2RtRQ+C>P^3MTf<;p{2!>SOI8h(lEk#qz zmc{M|CN{K}N0exuTR=t&74BnFgrXF>H!*KE`qKGN=e_aS&z9`E0+d|64oCS zeUiQ|L-y5=$yvEm&Mzre7~HqrWtNIK{xG#z(`SZAFX94wvqA+bXy3sxZ`gXy;gEC( zWxK!Eg+ndV3Jby}p4@xjuEuIYa}#prTuF5eEfPcNqa_|o7MHJ7)@BW!q}?Rj61KYM zNO$gU6k0+-xin}iQbRxaV{H+PWrfTK(R@pVFo}Jf^9fgdaH_iGF!_U`*Dtk!*`ro> zai#|=AaGkIka-yQg`RXEQR|kk%nf)vd>8G$AOq*M5F0eeeGPW&S@-~Gm^g6NxYYa7 z&entxx>r&>JNupU7jfYY+5Od7)TNladn;QJTir7DH;*R-5Md#f=;4nXo$DGEI6qzw z6&6(Q$T=`h!)s**0+iS8@KPQ(=qo0@cVOqh!mAGMi_S>`x6Fxf3A-J-Z4+L>u<^jd#7w3&tgS@ShYrGgH2z{)o)w zlcn>-gf~Gv2x{YjBEJTi5+(lfT`|Nsk?m&{$c+`*E*Zi)Vpk@ z-4M+`LKRX&;`GBYVl`{jz&QB(^3ze+x(*c5unvv$_3z=H?D8KHBXbJhmz_enHOF~7 z>|Bz(TE5|}Z3mIlM;FT9Vhm&2N?)BQBnU7^=h)_cij2gp5E#WjH;Cm!n-+)f5BQ;)} zgUx69xap;A26VSKI#AN&9)8Qnrj?u0b^gPDWWE|S8@_(#YayvNoqflP8qA6O!+P;r+Y-M+@WHv)RLZgo47*)m%F0Pv=# zoZrzvB-9MdK0>G)nV~?@(_QSXaBPVW-Aht0$A7__*mbtxuu5#Dv%?X`H%=DBIoi=0 z%&{oamhvJ-sVl;qfNPXzm`vx{>j?Y+Xx7~#w0m9dU~V(e0K4q-gs8A?S^o>*{qI&l z+iAFH+zB`3HeCMo0eFtlThm4BwYONflSbK=ZPMI%Hl36p86Sy`*K*!jB6HccnS>w# z8f|5$qx5OM{20oMh!hI%_5_S_syWn3QhRHIx2my(Us4&SVGu^nw!@z2+m<^~cP7*U_t0X+)5E znw=zMtJ6Z%I@BdiYrP?Q`~m%CU5>4~o9U`Z8w9h2%qk}-*YVB$s!`gpJo?w|iU1u0 zCEZF{h(%GVtga7zxFe4Dv+8@r{W4=LWt0u+gd5xM=2ZW|0Wo5{*%>Zt)I&FefAk?E zD?Ei7b&@}w81b6DAX1v~ z)qHMD)0F>;$dNH2xx+cwcUds61-zW$ zopyVKS15Ew6ByuK*PN!ATxK(K%PWrx(X1Sbby2Sg|E=2m4tfBP$)ZMjOQL z_Bog(e0gV4VrZ8)bboyBqIXKm-XF7OQ)xzO)~m>L5?E_&okjc=w$`<`cV*Yk@f-JW z-U>V01*>B&W>s%zCG!dX;c20nG27#gYL0!##l?=2@2|cE08~q>5}BrTOUSyTBHWyh ztr9ythI15q4facYsoN0|$iiETlHl5P7@QfAp5Oku>Ba{l+mn|X@6WfJNqXOAso63h zs*9W&B!~~@4TB5t^TVj;pT8nzhdZy#oC%5s=9ssK9gU3@t#DNVFJv->v6><~816ZS5hT zU;7xWNQb@}O0ZV2^#15M(I5i=1Z4xk0(_vBnZs+-{Zm79=wFXZ}$z1M9st0D9< z-{uU>{&62XX`>uwWyZ|LyZejKx`NezP{16oUKdj}U1C%ZkyE8*MdyX*?807yE37EN z8VOI#rnz=>Dcay0Jw{P2X6%j4GtpxWN{%|c8bOdmxR!!f_OQ@J=Wj+X``$N?E=kfF zIB2T*w4_^Nt{b?<@A9DDfKd;jy6KrV7+4mxQ_qqH za9y&`RpvN^ySM0>))h}hg>xbTQQ$;h{|{E0ygogb)=4?;nAJdfLyqs~0nrSn98Kxv z2Y@GB0l+|RthW0iq7d<<=I%#&K6|q?tS5Pz8Kb{VIdV6~92zuhucdRZ>bIXl{`{+> zRL32~eP~fNGcjF9C(C7wF3W;LAavD-&!Nr_esU#SY-VMAo$UhvUzz3bhwEu!QXk9S z|J&5cL;04_B|@I^|KjXVxQ)4HC z@_whkTVGcdWW^lT0nPp(u^;bro^&0eX924KrM8mmlFH;s@Ixl{pwib9sy`_WT{53Iiimhzm_$~(_uUu2>gW}X->YvAoK zD1%lSH&OXB&eD;^uK?z5-AZo}v41W$x(n|u{`Ef5==wO{(jH%OI z$|lqN+!yU^veN&KKeJ#m40+ZNDlUE;C(yUcsZ^zAH1-|N*GuKZPH-J_Pipq_FwutO zPQhsZUIxHDimXmc-Lm_p&>W)FGRu%<$zYm2P)qYUN2+a_O=+8Krhx_u6oQidUL|aN zEM!a;MNa}F{a6$i90@sH(}>MUyY%=4?o8qERf#dUo}m&?Mmu86pEYAOV4M|{v|4kR z{%uRpP|wn4TLie@oxTt6?(lFV4NbTwT_vS<;`GbUE-z?S`=rckLH268`21_9Wt@{J zD;~by?Lq4sO`Qw0BwWWugcxn4l{Dy8^?|DHx}69w!Z>!+m&9}r{EHS%i#>ww9e?sl!^MA& zc-x7us)sZ7N7GL4sSA0c%Nxq;=>Fijt^zJiVCeEhZ*OVEf};f?|5Nk>W3>)cE3Co-_wqkzRuWOH%GV=VUq0jx_ZSBYpZfj_NA+Zq^h)g?gjcyR29XvLhxN8=e$X) z$Gv+=-P8usMPaWL&c;DevdTtUsRy7;aT6%$2wU{L1tGHx)R8iB2u*x$-!*Ind1^YS`uk9E`=;p7g zJ-aUCi%*WiTKDI3LbvQn7ac@U8~|z%qN-SZ>jaWlMrgTQ0eJ~|C#uwuI(pMVVtK%0)Nyv)|g7PPM7yi={oIO6?PJ&ur{5u(m*pO@$t z2DhV~nAUn(P=4jOHn*QO+2cX}Hq@9H__hBVd9b(XADZ7d6f*-KljlS!Z50P|kR8mN(-MVD-tk zrSc!_{^>?2=mS7sPfR_l0XM`bv1#5?R3mx8$GR2v0oc0)#RZA$SpFZ1WA+0e-NeJk zX7&MSZH)-HAs%iLW#+4{htob5D&Ws(%U&WmevS@sb=IISbe1}q&P%l(^eZ?0d?k)#smY5Jp9GtmbjY*8;cHfwqc(FnE;x0IY@ihDzX%oQVW4 z^>!(o5hGMDI!239q%QMNxLL%Q23QNT#bNq@3~1Avrt^TE9FZ;wYR)ITL`KmK)w720 zZhX8-Ta!e}c+XoYnNJ#zX=ybx;4@f;O*L{8DrbMn476c*e9znReu$G9-&v)-Kxrb5 z{QqI72>M898wG&M-sv7-0fi2?jS9ogaP^P-JWDMx+C}>=0jSQ^jm~Q8Qn#OGENy>F?=U zvyB$zV$Cg=N%ikuAie4XB{I#QFHSOlJrg;;+jwCO?uS0raP?VOYbC_~VTMZ`qXMn3 zdfyc|qa3qkP82`~WdMAS!!(lq=u=UQ8nuE%X^HHKJX5p!H!E*+9_OM%Z+bk{)vr7j z6#HJzzd4isWgyQ$**4daXJP1&kE2+!~vPa(F&30Jam(Gr9rF{B6kFK><+A2z+Li>G{N zT4O}9`XEwqgb*QO?EvSmX(agp%`LyHllD&=!rJI(eQW5#H@Jo9)6V>R@w00x2!vjF zh^dT}G$N_$uYT5OovAXymvC-d@s)|W=phtLH8;>W6GVn4pxDx?|N1hGGJI}?OoEoCx$tiu zl^Bq&1#X{&)iUbWlh{-+{Q>soby5tX!=AzfoLphvsx_wv;;18$DGA12M}J+Jit~3J zO;$sl9{NttAXzQzA^toXrR_hDf=eZXTQLBHNSGg2;*oVZX+q|zJSx)#QMHkA-|*1| z-9qZWKsgKI6sGy^%SvT}YE{jX|2%HGVkL?V;m&fz+75i}iCcB-XwG>*%{As;0n7g&2I?&Y>}20`k;(xS3-nUnbQ7)EK)&5%7)xCTghg@%)hqyN03DL*$AtAQwiZDK-GF z@66~}P=eXxNh5lFkRDzrkqUPu1+_gsOJA`UlC!*C{{9``5gPp5lR>?j?Pg?Dfhc=O zWn&eY{~ID@;EDp!^=`cdDfdjsw}+M8a!tC|e-H&jlK8$8?mCgZ#A zVl!DEd8HGRVgokziL=g{xyG3M+LqQyR7g%OTK3OIE=5s8I&dCvmXp-IC|N{R=YCW@ zzAeKov4c0zT^BJiablOS0(48MddGuA9#ns0TN`M%V5?8JENQ)i5m`*gP-}M|ci(1{ zzpRTCjrh6Imk|tEpEzU{{-_$*P-J4Q3Zvst(|90>6G;d{USwPwXMf&Isy-%0QRh$&5{Y$rlpApe5o?s}O`mGW^w66hwbuZ989ILoj58m@TM_mpcZ+ zF8I}UmUz=e$*hG_@}T6?M-#y1kit>G0AYQlROnO?~wu`8ao7 zvOV?~Jm!88G?2)u^LyUm`yT3@W_n6BKE^DB!ndFQR1)hzBog4yXvYM3WB%!ALh4_XMe8f_KzKrQB>)Yf?@&tQ;H!R7m) zV4{#!nU0p`OeNv} zszgSKWe)atkn#B6(g|L79k#Spk)AB@)z_bur6-mN<19|ChW-fXohU_T;6(!Gl=7KZ ziw8q%7Kv-zi;^Cuq=FI?qSZ}<|8hE=7iQ<2nMqS>n!%?WP&?)e(4beh&2NL~i0j{F zl@4x`at4SGuZVu#aoA5bdJk`?`FPpQYx!hdbD%r_HqH$szr~ptj-UN1>3y8{72<`t zgB6tkDn3mw@~-~311_@T&#?u1M?p0vOr^Z*?r^{*6xIc2k1yJ$<-U*w+{Sdi(xHA) zGQO#yVk9QO`}XL%5`yF`kl*q1T1h)X4mHXPqD6K8%xU%m<&CPLM!YS&JQ=ETz9qH~ z2Q~f07SMOFG87{s80%P5`vz9{#e}@@hWj;cwlvW^lPxY^KWA!D$(Dh_Ts~8vG ziFst?-_D+T91!E6(;ntvMU2id$2MYw}mUk@11sLynB+NGe7~_6X|3o@sm3@ zIoTG_ASMLZ>pYoILaf=-A4rj5&69S!6yT>Vhaa)mB@IJHAmaAWm^9(yzyAo3JhKrH zy6}?Hx6Q-79NuoR!Lh4&CTL&y9RR@-2KAR!e3Z+gljHKEBf*< zHN@LrBUZkc#0rI_EXl>BuM4bm`@33r<{m6)?*#H=siHjgNlzr|>ZZBrcaav)>BYZ` zAHI6ucS=i^ny1DhgHUvZ>hx0mUONKJcc48L%KyyxPs`DJ$qK!fX^_a)oI{(kdSnQSBrvC_U`GHK^y2myFsvN%+}zqYU^70@sCP!MMj}F z4xvn&+1inSZ8 zn%`(2fOR1mEUVc%8y!7Wy=AGh)dztU?oPvEC|E-8Xy4=d<(#x1cT)Q56XN4=k9}I~ zKuD{x*5%F%1<+nLg{kK8 zzZ|0>Y;TmpKfwL%={=)aMZf?;=a~j_M6n=i489EOSDwjT0_ARvd?FH`fl#w{K)--E zXHD$i+R&6N<8~^{1FB@_?y4zZWSL8)UTX;DS1gFjGASY2QcG6&yG#*$k|b!Ty~tX+ zGo9$U_U>~##1Qlc1sCMIv#Id$4m%U2d(}sWJ?Sg4iQ+?TPOwv0YkFIl6-x*enGg9> z22tI9it#+RnrpR-1I!tCDD69c5Vn|6Fa)0|;gKL^)-p1k%rwIRa9dXRg1?$mu_a@Z zm_Yq~v}{zusk(FmtG8;*%ryjcJ(T+jwp{!wgFww8iUNf`AC7#yMV+bOx6J$CxHG@a z^szK9v~j##GYI+NZ&7j*r{PlPb$Pv&lkfiKiniqX$hM^T!$R?L+usOYvCuzZF!)ID z8)O{Gu%VueRECKE?0t{@N(YlttaSC~YpR+=YY^RonRk*ClJ@uSc-9L^JL={*=xoG- zh~}|+osl94v83r1?t%VpVTm~(fCR3*KE6;7b=zS_PAt82iLCye)){*|Tr#^r+h`Ek zEA4UbswUGe+YyVd0+Q0t8MJTYMy!GMoW~ITsO@h( z;`?y`bYNwTyCCU5zdHC)!0N${p4+WC&uQtTm+sgHAgzgX%VXczy`uJ+mEc|vwP3a9 z0u$djlDhQ&MG0)bZ&0ASH&s3cPF4_K~xZ2uxOK&Lc+C1nZ}>Z%rVXa+6z?vk71YPiF{))PPIpxQ(bQ3b@gYiz+% zeXK3p++BP15{Yj#DZm?*RQ*f%EtOHS8^OACb+L*pgyr5gRO)f7Md+AtA{;6(6~c9~ z?#?{iP`8~l<1BowmZn`>bAB( zK-`)ePs$ojF|KUESmP6!e-EO1rju#9M#j6`$zp2NNs=W zd+m5q&R1(F&WkMmWK|*mi;+UXK9dk(veNze^Z|JJ)w8>+=16Se=*Oa>m*WuvYK)da z805f@>3I90=mmWMFs~`ps21`fxQuS~S|PS1szo7*_=ZkL_ZY2m1}etiuQnMSp#4Hb z;iASKm9E;}es8Osb*+I))QC|#aRL-?VhFC2!W{uH9wus<>G0-(E1ib$X?i;LOg?i$d=B-8R^Z}uwr zR(1iY#vcGHt+b?6ZU!O`CP%SoexbR_jt5WyU&ezuwOw*+bK);2>`U@Tl#ngitk#@gP(8zmlcHH{ z({Z8*KzG311P$1P%XM5s#m4_P3aI-k*0W#dGPf|LUvoElhs2hvr1H8$k|1YrC)*4b zZsWZ41Au1D+(*;^wcZ=Y>4b=C^$$HLBpc78H0Ofb5y_-Op_e^Ev5=JOSE<8L0)*=(K^1D-@krQm91K zQAQh0(iG6T-*mn6;I=i=f&SX|1>gH35*`rE#7s60{CO7hdcvqq_s*hA6hbIr*WNgn*t`r72d(jL{*i}$no=Mle%(Ys2D^Y!a-ONG_50_G3;XR)h_ z0{G!(TZMeouQs~4tI|v?%F|{zSa;Bw&#qXGrzBM2qJz64qAuf}ye_8$Gc zttvp>0u8$+`&>&4;kXhS(jB@{P97q-S>v>})_XS8vRx2-#CSlSuiu)rg+8cxdriq0 zLY0+sptqQ^%@CNmT9(MS`Ki;Y>!B$t^0nVB#MW@qEgVSvt;VxB6PI69z&uOuh2Iu4 zT(xBSn*R8D!bjKd&ODCfQW^AG%I*~s?ECwNW)h*~(oH!KtHad1+9@le&6mFzE#6sb zV?$5tR?+s}t=J@Ij{Fm!hBe9k4)fP{nsOAr;am1Kb?Fh$6$s;SSC6Ym4YT_7nvSTklC;!;ti zW;lS59@VBDWCD_;22u(aD0OV2dX}x`Z{n@&kgs+1hLGsU_u0k7 zl^1QJ<8+J_L{8LN9^1hIPWHo756-NZzAQN0UHt=F3VNCPN#X2z_~D-tWU}!yZs=`M zE&!6rmI`W5{j~?BY}vonKU4b#HIm_AGt1;<-F`j1lO~iUOH-mQ4%$_f-drBRGV7uu zA+Co7UoYmDfpkXthdJFlzH~vnn&!4NFCt0B{5F5%#c{w>+B`P@+4|}ze3};$cqjTo zMuBkdZDQUnD&KK)Pti8qZ;g(ILQQbIXLf=AiN6`x>g!MRmZTDT>;&T1$cc|b9T?gB z8PokZ*y5>fpcQ@V==Vt4)SPC+G8~@Fd_j*w4mX0;=xL)BqCK^h#feMHlW?aJ(`S7} z+HANP$Fe20LUEqx%E)dt>h+>TTr;--62jXV893mz70?MO=RlU>%hP7KA^qMN|EOBf zTc0M8(R60u;6D<&Fa80r#Ok`M6a=3gCCUF0d2(jvZy0CUi!=)`kIvmT z4DwLs&Aq-e=q{+VjcLV>OXhz-OXXp?mt;|T#Q6EEk(@n{jk55m}$Yo;-tvPWTUpvB_sVq;xVe-{%EoKa7bt$vMeSsjSJH zs@sS>3Kuc$F;kw=1ndcvU**l4wxR>x{|yWW2WSmKXve>h_Rl{LO5EZjQ#7&h1ET}^ zyRiLvH(31r?H`{9Y;ixSPn}9Uf&PqlHiVLXHxHSW1vgU*)FwX+lLN!rx_193OCR9% zb>~2ZfFh6bAA7?>*_Z|5^#Jj&Xx{ik+Mc45V{bytMh}Pizf*_7-}Sgu;Z`w2cT%e zu2M(NqC|&um2z5aWrxgIK)^=p3O4SQqvB1Kh111Hv06ZYc5k4h>4iof$?dL)2IU{A-)>A=ZR0~l-#L$sc;hV?Ge0*bu#IHEO zK&G=hBjv7227z5Rf`wpCOX&r$5nRR#Ncwyl0AcbI^m?xL7-Jpo&a&^K?n+0KxfjMx(Bi}cN7VFje z`AhBI>J_oryi>>uKml%jr93~nkC*cku7R9-TavExdxNI9AbmsPGTICIq?GhXT4G#NQYF2jXE6db#LaP98SvPHq`paJpmY%zilD)}@)Ck6G56Ir) zH?2cpMtZ|f5k=SV@j*ZIZ6D7C*IRigU7XDIM3wS;Oz>s#=xd}-aRRpzVd06Vgm~i` zx4kGYgp^BT6$s{oL`$OnY|?#YT_0b0La3ml%i+tcK@Fv#^WpFP0DyRVEIzroK92zM ztK6_9(Z7}oOX2{C2i5QG2jn7{H9I9@43DZ8@Q1{EggdKxH~yf|D$Ia#vTJaNcm1+n z3UHb`pDa;LG}?s3^>IPhvw=(3d4KLIMl?B=%|qEmVqKnD5^VCFS@&-X{2qVhF8<4j z+3|c*!xcP-4_42gGFE#CJ|v>Ny7PjPw6G0f5H(0Fc`@0f@S&HsZ6}OK$j>5VZsQG*_$c74%|cK3^CRHVDQ#cga-SIB(RV z_gS zxR8I8N)YT6`q9AWbFq0$M;Dz|9!RYXO*12+1r5W>64TZWbn1#zX5PiJfBCL&1Tij| zE5N2($i*c8WC0YMqemh~faO)+*c>NoSYYpsBO76jmik?qHmkm%Ebe&! zAa$kN?>M}Irq#tTALGj*Kyo&(DJem8yg}rFxI)nC5@*k~OHm_x{cNaF!wlG+ZwA$_ zacjBMx_CUk|77%dm{ZZ%S+h|z$2PLf72w>rzW9`S(r*pdn;oDY&8&~OeNU15KEgV> zBbZM7DU#oM6QpabK-*{SUmg58>U1MOCp}hTy#S4SCtJvKX$-d`##>QsyA-0@IB^rv z=kjJ7fU=RF)z&P?sq0prT16*$B#>)84rkR*wqQLs#)R$9JsB>glZ=e@e9t z=2+Hm!P*1}i;gVCC2! zx0pQIzw>(vXTLlAUS9gLQo;PYrtYtp&YZY7e_;6$E;77TJB_WIOt)<<+q}^;?-g)&^KLg&SDu1?MDlbBM{^*X!9FFG8dWV^X>O_3Z3Z$o?l`XmB2IYvB&QZ!L=y=I?5=dW}{ z{^95zX{cX`dA5W&tZxMDgY*95o!EcLh_A*myC(Sa^%NyF#Uexh28`_E^2+ENB@Xe89Q ziP8jZ@+bqH3u*IdNxWvg6r@()>we!w!qp;8oIxG)_(Q9uXO-$1SjW80HJ9ClWaq#py{pC#+jv}UK4$c+cX#V;3^f-(Ba@`L>ViF)%;?q_h zC!ZuZ>Z(+FvVZNUjmXeRX6`bx_e$q65Xhp2GJ4xptPLh$dY{ez2{4&daNjht+dp0eyYoo5YUemArv#d}xmVlf7lbB(JBT@om|z~GUEBzWC`yBk z3F!TntuaHwi2352nJBQ|_|wDNn^9SA2Aw7H#=o7T_V#^y!E=m z0;nI%YYJ*k`$hd}j9*r)_sp9sYAzbRJZFaJ)#n4-M%RZyM0E|Ga3ML94B~zL6deg> ztH7+_+P#>A!*i0E%52Z6P6v^9WLLevBTjY%)qj){zKVNuli%O?W>glc01njxNlwKr z{pqJa03ufeQxYAA=RS+S1J(IJK`${5zrg{^;~A9GIc-X3I)hlC8+?<8?PlklR^3L{ zc{wX5Q`1DMx<5U<=Qy6TeMMMKSQ*AVXK}m8pU=*olnHnc)~&VuS~pV)GD&@jAv^J^ zCknOeD{V~g!xlOYV(|FtvRC-N^XD67SWITqUdvh(HPA5CeNrmA3vS{pLKF#9=5#G+ zawI_vdFMOOD1YNyhr#%fFiIX$K{e69?h^Ehuel`LTWo_<*`*|iM z;8?t%ZqOkHJ8@2A?d}_g1itSI;1{BN0Dk|6M1Xtvh(H1NEh=1c^==#;QmOfNy#?xU z%FoJYsg-n{LpDlFd)fILK`;8TpI<8M=QGNx8QUpwtp7>Q%DI{EO<&*-e-pY)bNk61 zczwtl@1-LZu*FWyZ-<-DjI$V~wh!$FC$y%FX8ld+8nY=C0gKveaE~ra*Qdtmf&(Wo z0x<5FU;HrLNnm)^ktjmtRet>K_cx6O3>4+8l>=iDvi4c+I@J@wA27~p}%M9&vcZjpYnZrGB_%z z-X~QmECc-|&3-$@a)#Ok1fFHAx6O7Aw1O3_p58REBvy}Z%fi?LhVf|v#=WwYy|SY5 z6u0J^dO{KKEw>%1G10N`Z=xSA+6BL<2?70gerFL+b$*m&ld#r*n|&ln{P`Brlim~z zpyw$am&XG9?2K&LIC_I1t|ww#1CS5rn_**-Gc%0T;A(xfTBX5cOL>1Yb5+j+(x7LX|oX z&e9u_YyDF0`#8$H!M?P~B}>{%j^MVw8j>tD%-BJ=TFuyYUzfC)n%KQLy!AUC*M*LF zDn9nqd(~&V8}NHUriRdTlAXg2GQvtkt>mxkhd_^{8{{V)e1a$9?xx$aK00w`I&xdJ z-|_IB=3yX)X+&Nb0ufSltJbVlyfeYJG4o8%l5VQ=ysq<(a_8uv4+a?r~8 za$gWhJu4(6=DziFZeN7e;Z%KLt~T(*CH7OlAPu^cGe4KjHxsBmCd+i+d(%DxEyE2L z*0!{a{SZl#6I~VC(}bFoV!5yKz`f=;Om!%|c^q{!QMS~Gv2a6P75VvR^dGhTuV5VW z!j7NhF6{G3*z^A|O%I|6rS?1Nc~UlaNPH#=G}t$)JUY8?FPF2BP_l^Xwtc2Go?9FK z$PvVnSZPznK8&zikBs^$aK7iI9c$S6_%MtFg1beAo_pTCJtFlVLc<*+{#t*=+N#FA$L6r5ulj(%O{5(`eJAt!ud z_kiYP!4JTBVRM4-{9ZB@)Fsa=n|9Xp*nmWLuvGFVfh|{rq^*5YQmlO=CR$~waf-gU ziV9W7)ZMV7Gv zavf(ywtJs(4V(8=cMElKfjh~&83c$3?MKS&#HNgw)%yJWdmyP z1Ay1cGc;$B4csW@L?WQG!<@QNaHD8dn80coN|}5#bX)21)_ZiHE3W{jGy%6I5`hwG z5#y3c$h@`l^yhi#I;-NO*;t0+Mgd?gEm5Y}U*5_L zQ0_e<+}ri|AW3(Y2A2J&+X9y=0TdQ}xEW(8*v=EFhMeUD0gt0S&Df@+H0Z$x^Jb63 z03vP^{q%?PDSsJ-1Kl2`n!iuTuVrO|;g{F$0mVbXJav#oTbKd;IqU37;{U@TDtpjP z*wOl5qdh1ma4oSDa~HgeE-N5~uB^S

    _v)zoBjaKU|$vR9#!Mt+4>XA-KcBg1fuB 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 pNpL65I;{{3_Lo27g{`EO}PYOeqQ literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/record.gif b/src/nhttpd/web/images/record.gif new file mode 100644 index 0000000000000000000000000000000000000000..8390bfd82e492b929bbafdf29131ae589d46d54c GIT binary patch literal 1035 zcmZ?wbhEHb6krfw_})~{}E z7xwP`U}Ey#-27Kw-d8@px8mZzyu7{|82oB$`_W@qliK)~;L^A_9N zfBEvIUQ6rqhYtlYF?DWkAMf3L%ffQu(&gVlK|ffSKkDj!)YJPaEc}Lx>pdgmcYpu? zHa0*1{F%_)^gk&04iC?t!-wChs=c2#H$Ojrs*B5?{ri8Ln*KR(;LqVBKg7iExVit? zx%1EF&CAnLPtKn8JT2{muI}q+&+@#zpRZf@`_G?WrKQ1ga&K5!-?Ov-U|{$tE`Ij( z>EGSm?*#-t?Ara^%j?+0iRXNMzY7UnNK1S5>eZ=Jr~WKkHc?0GS60@mCr_U2Kk!yf zhckkc-s;Bo`Qu4RIzl)8{$M4_fIXNBB)GRVJeap)F5g0qVx}O;sJ}@x6XJGjM z|3AYhplS#x{$ycfVCZMi0htQQ6ATjY^IPA1)`Qze?5s)~maGLEd!Qg#j97#AvXxUY|)SHx7xd)pcwaUCIxsMFg7qur&Y zF6=Zo@b>oZ+)Ym!lu{WO+!_9{#qCz(WHoTDyP~*AZQ|qO1;-Q|6dz7(b8ZsRiP)gh z#3U$ZabhRqX literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/reload.gif b/src/nhttpd/web/images/reload.gif new file mode 100644 index 0000000000000000000000000000000000000000..c2c00610e7d9b441c68c5460166c7eebbccafe51 GIT binary patch literal 343 zcmZ?wbhEHb6krfwSjxbl8mO%qrLPgGryi!K8lFGsylLu1Ht-vr`V0;dT3S^t-br0UQ>(gAJKLF6M^~@VUVqYL14EZtGhB>} fO$1HNCN6a0GqKLZ*U+}7DyFQ_caOwTA$FtoI^RB%km zPfAq?t}HG|%`H~&%uCKMD#$NNEJ;lPs)e`@3S z5X-d_ZyAKd1;G-W?0h5}c4hw$hWG;6{|62{W&{Wzh7G5l{Qvp?KN$ahe(C?S|Np=k zCWeeb^5|l%JwN|1K6vZ@$$M}90|XG$r+5Du-ZK1W_!tkhf7Q@bu3}7)3`2O<~!|P8!7=Hfz!vGLKOfcuZ_{GHVhl7K`VEZBl?)gi= z27det4hjaYYnK?f%PPU_uP072ynVp{mqP{s0R%VfzpgIBrREMW+ivw_hQAy3G2H&b z!0_$WXNW=X-!fd4(qTAOFqJ_^@jnAU^M4G(00M{!nZsr8$gnTAl!4{P4+fvrJq#>+ zPckS}m4gks!Y>9k$kddbL7I($L6`yIvj2bofN6jLV*2szC)mkf{z7b$%8g;T;28%t zz;#|D!~Yi#!3G^qTgV`z!vQu31YltX0*uU%qy-Q_aF;QC`wutpPhAlMgP<^6@#)kF z3^I;F3?}^lu_h0g%K!q130(qYploL^!>4aw!59Qg&u+jP=rGs8Tm}$8aKrv_K@@@v z`nPE*!y^tphQ(eP3|Ia!GwAGJ#bB_wm*K=628O4C-J41!|>t5YoIawV8Z|ch>4w@g+Y{$gW;cs4#Ry{Uxv4Z6Byn|sxYwe{A1t~ z`VXexSr{`sjILmK{NMqDh8LYWc zQ8m_Y$?D0oYBY`{Z&Eb0wOL&v_R7gnhwtLitx@WVk`ufZtl1oqqpZm7U82LzuHz|M zQ}3n2%F5NHC!wdqB3LiQBGV8)osofY5ko^xrnU|TGc(8X02f^yHZ~n?|DF2vI~^IU E0n;m_2><{9 literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/smallwait.gif b/src/nhttpd/web/images/smallwait.gif new file mode 100755 index 0000000000000000000000000000000000000000..dba81f5113f127557dfdfad9ad5208bfd694c322 GIT binary patch literal 310 zcmZ?wbhEHbf=ZVnkn}S!FamXRfb`2GSO_27H%%Z}3|T)5P&Y_F z7fW06NtuTW1agq185!7NwzIS;CkZcfsalC-I12+zHxJMwD|QRAF)({oae<8I1pAa7 SVf&P{#ZrcQ86=8C7_0#!o;rsB literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/snapshot.png b/src/nhttpd/web/images/snapshot.png new file mode 100755 index 0000000000000000000000000000000000000000..f501a593a4e08356b0cb29b40612f8e60bbff625 GIT binary patch literal 656 zcmV;B0&o3^P)7Lxm88-p zDk^c;sHj}OKUc4lGZU}6umlGVNAJx0%sKDOFwQx|V2pVvxhYKe|L9TNk!~md3BVrm zYPDL8Hk*yU-ER3~LGwJ7N`0ZV&nOhBI{~~A;@ND*=kxg?#^W&`4u`zk?Mg_e)8XlK z`T#M+OaR1!<#Nf_>$S`xrqd}OjYhoJ>q)?3vEX8pY&I()ERjfjrXM$k7e+-Qs3Ihj zNyOuEQ2EGYG7ro!o6VOBQEwuV2z)*tR8>WxP{616FY)p1Pn1d}#9}cxolZC$4n(6- z35hJq0;FlHC{ zp*iF(lgUK(E`($(s9pJ8Kn?(M734H_63WHtf}6SQQ_MXEP!#0|&@>J8dL5TfG&tBw z#tYn{TCGZvAr>cca%YYn^!t73tg8OOJ2FvJ(`YpCyVZi*?Ur+1uUA$hAg8-aK)c;e zQ<)!XwHh|n&ND=$@^)>aF-`~n}#*WMkD*M|f8r$i*z7+W! qF|A!t*4fE(R`<_YIkN&?Jng?3oQ|aAqClPi0000Su zImdUlKjSCCE4u7+Z)8{a(f*WH^6Vt$sa4Pdn>Gm|hqxOd&DgzL7wR=Ny zr>K3{Pb3xpRu)a{!~pxkvN5v>H{?FlfAoQu4ArC_RK=cUva=WC?yvFKjtvbQVdguH zW&aC4ZFY(9WQu1%h2k`_XyA-}%`(_CmTH1G9&H+WxB<7lEP>07F324X&LtI=*Ebks z(O^1xbS&VonymqWF(ZPe(4cg#Lb0wAqrP?lYk_qESz>U=ricNSk8nlkTJZI`2;B0p zP~cS}qly76ue`DhoFBiW8A<6uOVzAiF)Myh40}1zPD8j{)c_{NT znF;osE(x$5WnrFL;%2<>{$AzD&vfr)$@TcfHvM#Qg`uALgat1xJn{IMSx=b0LuW4k dO#kzpe*)_`5%$M*g6RMN002ovPDHLkV1l_FIQ#$r literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/streaminfo.png b/src/nhttpd/web/images/streaminfo.png new file mode 100644 index 0000000000000000000000000000000000000000..7df94f66fbf9c942498544c1ffe80daba383b7c1 GIT binary patch literal 501 zcmVz@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ-Ye_^wRCwBA{Qv(yL+|!8|JR(p#effln5r=pdzvr; z1Q27-w$uMPm>C(e-Hh>Sn7ZpM!;ioJ82|!^!KQin|KET9{wD-LYCviM0*K|$zkduY z%uHZYRv$jkP%&>K!-E%Z!TcZ_1BO}okqm-7TwpaIwEzLcf^6-p@BbJS!~_{qJ*^nd z-hRxm?#KlOelB)~nR$_LH2?tw*YNl6Uxo~4126`GhQ(XJ^v7>M!E!JF5I`(2#W2AG z7jH3m*3E<~C=YgI`19uvoDUE{U=2Ti{=^8CL^pGW`bamRlQ>W`00a=+NiabnUM_~J za2Ez!ZDj^-HWn1cAOH|R;Nbc3;|Ev*gg2hJ%+R)S4@2jgy>NqI7z6+Uh=qlT3Cv+( zftVI+Z^)o0C(fX+D23wGKmYzSfYbs65IClP18s;hRbpTT#yBpp@WfpPRt^pZfB<3y rr6pjr|6hIP4#Uqse+i}~fB*vk+m*gfTkwvO00000NkvXXu0mjf77f1U literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/timer.gif b/src/nhttpd/web/images/timer.gif new file mode 100644 index 0000000000000000000000000000000000000000..e632d2739342d83c54a192a99425e92b1ffd10d1 GIT binary patch literal 1267 zcmVEB`S$kp@bvTY^YrcR z?s9W>=jZ3%-`<;@o!i~smY0{$&(p-k#-5*_zrVh_y}zBFp|rKM{r>&0v9Xbpll%Mq z@9*&M?(OXE?dt67>+9|4>gl4Rq~+!4eSUwJnVXoIo7B|R(9zV%$;-dO#kjh;xVgKy zxVN#gw0e4etgWx6r>1v#diC}7^Yioj`}_F#`2GF;^78Wj{{Hs%_V@Sq`1twr_4Luw z(ed%}`1<;>v$Fd7`uh6&`1<+9#l^$L#QgmH|NsBwu&=jg6|St-rv);LXywxwoRCqrJer`}+Ec zijCyu<*|K&-QME+`}+9#`h0eH^z`-g_4K*AyS%=?{QUj*`S{k?+`YiY!^Fwx>FMd| z>11SQ>gwv++TG02)#>Kt!NSDo=;z_%;<>uFp`xYt`1kkt^}xf$=;`Xo%+clL=HB1o z-{0V{vaa9G(6OPU?da;--s0Tb;qC70sH>~(>+ElEb+@y#?(OaG?e1V+J0E z^z)OIl#h^;mY0}EPtzU3S z$lcxH;^gA-_4McA;_~+O^Y`}l_V@Pp_VV}m(9zR`Q(42r!_(8#=-uGO$Hw*b^xe3- z*4WpquCMm>^}W5l%*@Zcy}sz_>;C@#^YZiJ;^E87&z_o_=jiIo(bVnk^T5Esz{0|5 zYinw2Zfk6A(bCk@)!5wK-{a5K$jr~-(beSA*XZcz(9Fx}=;+m3M z#M;^2+uGmW-QoKD`{m{2&(PM=)YsJ2*Ir&;>*nY2@$m8S@yN^1@bU8X_Vw}c^ZWh$ z`u+al;^WuZ*;rXy%FN5u)z!4NwQg>2_xSnT*Vgy>`IwlP%F4>h&eXxe#pd4M>FMgp z&C~Ss^#A|=A^8LW00930EC2ui02Kfg000R80RIUb*rXk}K*j7NGFD@t!+}SzoU5Wj z9wQeO{)D5{@Zmpt{oZ`JAVJN76cS?UA=ED#aA0VeN64Q6M6~vSid06BgBK_=T-h+9fHpHqs$D2W1IVje z(U46r1q4H_AZQfn(_>GGxg;^}Su->S0)-hwDiJ|dh!VFF$vRB~=C4MBVX-`g0OK#+ zlrU5*h1kXiLXagmaO`lG<&c9DHfZ?JL&t25h+tLan9iXT}VFPB1(W zAV+~HS+2yh1Px5wv4}1@+&2RU`0yaX3N+j>fCw^-lf({?VB&`>2i&&<0WvILf)7Ic z!+`}0V3CF`s+KnkY`Eo6h&V=bWA+7NR@wyLq4U|DET3pYsaU)z#lsRm~+9=1NbpotRP@QR?BSgMY-(!CUtKd&Fi%mGr^Lm|%1SMffWkU;#IdzCBL&UGNwUk% z3w3;hXa?gNmU=Q(-80<)DkYZ9q5-ko%lN?eIUdbj+ zNQ|)`L?h9?t^*3D5`Kc)lD^?%tP?)NJ&4LSybYf~_iP~;JICqkrwCgjflLIcSm-hX zC7Z06WmSkScoB}2V{qQ7?_VYKcURCGd(G=TlEDO~ z(CBCi0p}ZRwi-}gR@J|x$~&{u^2NTfu7%?dax5>aMwTT;I$0A5atT96>=BBk3YfJF zh@+)=F+%g#I1b!O-j!j`Lw@>R%*SKs{{1;xde?4ixgL({f}|CJkYXH0d-x8DlCQz4 zG8`#53q@0*s0vhQ7>|siFmpemY*G9g`VjHn7VY{LBE)mzioa$e3H2M{(9cMCAqXOR#lX@1Tgnr->YYTBeqS6v!3>T6kRG3WqCo3x$(B3 zLi6Ce2?`$`uhseluh-j80yoh!G&JN(G)7612HZE7ww8Df;r(Fq7~K;+Jh1#9Xj;Mx rxbAV+2r)%9SQb^qJz1*-|E>Q8ss6iyye%@B00000NkvXXu0mjfIy`F+ literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/tux.gif b/src/nhttpd/web/images/tux.gif new file mode 100644 index 0000000000000000000000000000000000000000..720c053b796295a7b242a869ed8d403b81366bab GIT binary patch literal 1692 zcmV;N24ne0Nk%w1VIcq|0QUd@e2J6w)-3hC477?Y=e!9wI5~lglW1mELo)*Bss)jn zq)MJ}uX+nwYjoV(+y4IidW4OovA*f)>8z=y^ytR2v$Z%zOY-vZjc_o2m`1ttSv{Qt=gZ%vby}iE1#mStdt;)s5-K{fXa(tqxt@ro# z?y&~-_4Luv&x2eIR%LB(dxDCRmV=3mVQ_oa*4DVSwZ_B7`uh38!ou_O@-!p~TWWju zzY3b8t>EC^Uv74_v9E4@hWNk?TU%TE`}&=xv6YsWQc_cnj*)MHibGOe$;ihlFfxUO zg=%|?_V)Jh@b5yYc4~QoLsC^rR$t7^%)`OI%gV@RbbV@ih5Y*a|HJ`te~5~ShCESD zS8H}`cY*!=`~3X-{QUaM#>mmr)zHt+gpZcWlnrliZ$VE{{{8)CZ*F~WP_(zd=+K$# z=htIeNuQ*tUuth`dxY7p3&D^G!NkbL#KQEs1H+go@4^iC)C=;`9P#t_l9G+|^z`cO z^yuj5=jiGC{QG%-kn8K~?Ck5tk`wv)`2GI>H9(G zpQxCjt(l;vnwFFAxDhx@IUgS%X`OI{keArm+Al>#EK^9%&(l3jRaIwj|IG|sXmMw+ za%a1G^t%X-nWVV5xj|V^L|If;P)0{sT2EhGP*_vVq)K35T%@kJif9?8n{%6@tG=aE znxw6NfPvDgG2N{_WO95~VrF>8eOzQ=86YW;kC4=+1l*tpC=~$Sqz}Bo#KDsR%$ou8 zw+H|L0RR90A^8LV00000EC2ui03iS+000R80RIUbNN`{d5I@+U@m8>*!-q5q_?tK* z1)B>GGdip{aUu$dGLC2*DeyNq-vdLq4+*5x<(VKC6ZX(qsRad_e~A9MdT{9W zgMVX~#aN=J(~C4DAG7*r=wF=&hzuS7GPKHtiWiCW$WjqyWR@BJxEn%568_WWoAm*S!iUun1w?zl*00M=e0$EW5CNi|5 z%Nb$B=}s}FM6rhsKbXTpJ26!M;DI}UBv6Dvt`H#v8Dy}KK>|?pGwK;x4Da3)o7YuxoPX>~JK}G{q z=)lE4HCz)&CVtE@Mu*AfjQ zB}8KY0KvdQjU9GuBab!~zyQk?0+o=7F~$G@gdA5GU;qRh1QG}bX86$u2iCyyO%FyG z(E~IV6i|Q=0&&uW8yrA+00abAK)`n(W|(ou9Zh5tPaM-&FvuWyi1WxiJn%4#Ksyvt z2o^OoK?WAUkkJG%gp__w165pM!WtSR;Q$FVtb$!Zj6e`bHrG(m06cwQ(S+|!G!cv$ m1~3v$2at^9#MlN{QOh!y@KV7q`W&N(G$f!x!)YT01OPins3n2` literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/unlock.gif b/src/nhttpd/web/images/unlock.gif new file mode 100644 index 0000000000000000000000000000000000000000..10eb77697215f3b201fd49984272c6f5dc22e48c GIT binary patch literal 333 zcmV-T0kZx_Nk%w1VG{rn0K^{v)YR0f$=FCpNK1&P?Ck4`v&hBA$6S%D^78TIhbpJ>F0!*r0?(V;p*-IUX1he^EiQ} z^Y{8SeWBjo-v9sqA^8LW0018VEC2ui022Tb000Hb;3tYy(PU^>aAPY%4m2tB14FJT z4Va?+2n_-=khmlVjv}b>2@!=5Hhy)b`2#r_*TnPqY0Rt7I7n)@R2_6Ps z91aNym!<-!01Fu`5(OTwWdgpwsS5`<9}^A+yRV~=sRj%V7$YVI03O!Y0L~5tE-(xW f)Y%IQ7TY-=0UHem7VQHLVdo<@8yy=mIUxW$un>jZ literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/upload.gif b/src/nhttpd/web/images/upload.gif new file mode 100755 index 0000000000000000000000000000000000000000..f76ba7c7a667d0256f30c1d0681acf44105c6af9 GIT binary patch literal 338 zcmZ?wbhEHbC?y8uV4TE_4~!km+#)cfA;+O`wt)f{{8p)%a=dDe?NNs=-aoikDomGF;Dcz zkMF-$^S^xc`sS@$Z{EE5K3VeTJduC@|NUAmaR0&mpFe;8JO!;xn{pI!WX zarw*T$3Gr_{NwSHA5RXxKe+PM%02J)%y~BF%%?LO-)wyO^X0tf^On6__WbAbg)bKF ze7AG{^ZApXOzwHqv;FOM&BdCtpUtkkSLt%WMPq?R^MmHgUoQ7O?wj^>TH&3-=})Jh z|9rmwetqDTz%6gK7#=oU@?y!!k0aVaY-Zzw#@+9Br{7Fhovb?L$&{7{Enb(r+8?&}KkiqVpwj)Qd)@1Gi8m56Z)Miq zuhU+wEgmJl`qk>iFBUspa56k<=y%yqf46?fl@Oy7M%&(Q3%U}Nbt}v2oR!uJE%USH z>T}g)i)D2-=vbb!jJy_Ue9Acfdc4V56YW*n{+IpJZl>vN*V9_Am2@M?@q(jxw77Dw zvP6P}e6_sYc{{OiG2N}Y_UG*l4;gAM)$DxKsnV}fez&~nPLX_-yxI&kgZ&11xATn8 z7%S8&xLtHp?o!s6uc5nH*YlF6Y>{lrjg**cF{)ElrBbEZ9=2(()mERa?sLh<;DCYq zMR&a&de^>QJN)7B{qOf5et&rV>-9U|?%ex+k6{#05CV!nS-`q=KqM$nFmU{55H^`$ zVlZim#}uJ3tB93Cjh_|{><>F&2Gg*0&>6mYyDO9%i$tbq9OZEDxUymAf}?y^ikoiy z*r0N{UB3QV&xPcJYCTLW0zpa(l$u)9IM^gMFdXrnuB$9pKVf$A$$tLMoSubA2@Ff- za%%cEnIs?V;pD$iA{o5gSI{9Upr+C(xuerfDR0TbiHF^C+1S-QwHRWW+l0gRT=?H1 z#CBTB-Oi=L)2YdI5(j@1hv2~`R(VBJHiZCzl@1LYl4~5=M08>t7#WOaC>|E`Sz*}0 zlX$0qNk#WV0V}5(OQ4$oGfRPkDl5+fM~Yl zHeo^&i++xS3roLGP-jGF!G(itQXxT(tO7e2j)@q3Ie0`m-Q^(@yGY=HMo!rjg9dh8 zjV}T!A~G8rSS&;u4yxE~XgI=AQIN>Ym72iV$i?AO(8wyYV8KCU9vdZ2Rqd4whj}a_ z7!I*ACTwUBHBnG(k=KrKy1uHFtXc%Tyu8+oKg(+f zuvh@jaS(}#iRpO+(9_rAc?*MsgjX>NO+q-|cn`qC$mQA0+rOf)XB@3)9^S~Y;MK|a z`1rDMFV@Dju3QJhYY(?y1oQ9fRzD{0@9yN7N{vqrgKJXCt&r{I-M5HW{QLRG(c90< z$HT+K=H}+3Sq!dT3a(xY*P?0Ba}nEk5Yu@Za-2N=HXWRaI5q)5pce$eo>@pmay*=jWiHp#JF4%92+` zC%f?4t+%lg)gi{{R30A^8LW004dfEC2ui z01yBW000M~fPR95eqeTlh=e*vDl>_RC{O|~kBBUCbxDmigAiOX za3vcu0x&~CN>L|*OIrzeO%DYO2m}W=I8}oIWHb{-9i*%YqX3JU0fZcQAu+;0o+}h8 T1VD(0PX-Yl;sDV_C=dWU@75g? literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/volumedown.png b/src/nhttpd/web/images/volumedown.png new file mode 100755 index 0000000000000000000000000000000000000000..ab9577aa132c9e7deee53585a037325676595a03 GIT binary patch literal 711 zcmV;&0yzDNP) zv4KjGLD@@SwqBY+AwKj_2x)Ps2A1Yat)lSTxi^%;0wX%`a$6Fye?p-;}9Oz@3_H4Is>e~!^?WT9P4(UVR_y$uYg2R z0LM90z};reLY114GXmmv;A$P-B^yEOFE%_9+Ev5395PjP4WPtcoxO#h*GhbsI?f-T zC;7Pu|AZ>;$? tsn=ZC^sk$??2gfnFO}FzBE_uKzXNI$nkY}mb87$q002ovPDHLkV1k=gKso>b literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/volumemute.png b/src/nhttpd/web/images/volumemute.png new file mode 100755 index 0000000000000000000000000000000000000000..b652d2a71fc0e866d855c08f415b7ec057b3cee9 GIT binary patch literal 474 zcmV<00VV#4P)VI0N<*QWk}{)T2Jp~1;rnJ&N2haulNhlC*Od9Gf>KFEtyV>~T1KT( zMys`lcDs%9Y!**GpCvG7uAr)U^!sP%^?K-byD$s`r=o}<$KlrRw*+%D1$0-K<~|ff zfh@~77KHKSyI>I8i8yRaw2IR8ItU>!0|7iT3@*K1Y{mtMqF^t`<+5lr>Nw*0@#HI( zfyeDeD71=LjJFr0(_1)Hq)$07*qoM6N<$f*zgBZU6uP literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/volumeunmute.png b/src/nhttpd/web/images/volumeunmute.png new file mode 100755 index 0000000000000000000000000000000000000000..b497ebd54abd420d6ad527e45cf61be55170e944 GIT binary patch literal 417 zcmV;S0bc%zP)=wlnx)<^8N0A$6XFU?l;N(8Q}Zq)nMgnHnL@z8KSBRn@Z&a%{xn|-d2Q4 zMH9;98$s8LZzsrcY-m~$XFnviYhEiADa-Lkz!&I><>UW8(|7X;y57kb#}^N300000 LNkvXXu0mjfTqdux literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/images/volumeup.png b/src/nhttpd/web/images/volumeup.png new file mode 100755 index 0000000000000000000000000000000000000000..965c503c6088f5ca20ae624ae9f9bb8f05490640 GIT binary patch literal 684 zcmV;d0#p5oP){@e)R=6tn`E0`O&UochFZ7FhGdgX0zI@b zfwou(BBezH*PQIpi)fL8z0AQtp}BZyynUTrZBg2SA`X0cGmm+n_nl#e0l@r=nEwVS z77H#}mcINopj0Xj&|*nZiK$9T94|fX&Uf=yW>Jbseo%3z8&JU=68c{18C+IKL-EK<@goWW*KkIou$FceWXN zR;;c*gQ6&qWf}Q=9;sA{&gT$|i6&ro%)LY0f_2wLyAC_e9ASq+u3Etu7u9GiKoA6Y zyE;cmU`TyT?i+$k6Byz~!4D7Y4!Pa#<`cE+CuDAd!f}ZXbiiVxce( zLZQIn5o-DIjdXP!!OAG^KN}ku5B~vbQ06k30fR6Ek~oG3E?;|b;X&0ux5We z`<%net$Dof>d@Xy!l_+?|K8aSlbnjOk;yO{4)bg<=w}7Nw_|`ZnG|^5X97IRt^WGQ zD%L;MjWyJc0Id_k^~kCIkw-$i4)6h=OtpUi7|C71tF5Q_b!>c?HvlI;Q3{%%>KgMK z(KG#$?uVkAKq#+$Cp$P!bmN>uJ&&@^o1KkltZ82T<6lQN7pT4(wtvQgk^c_UnVcJ% Sc~{E-0000O(U+TOV&$}rKAKDKedCtt*EcB?qSVV2Qn|gn`K>|aA_tva~NxIS6ivlK8Y)&wo znd8=c?di)F-RbT~_AoLqG6(@71B;))NzLO6ETU(x6u7D;$nvfBp~tk#oJrcZ_etKo zJ2&w`uz-S=7VPw;8w6OVX=Y} ooN`g4+Zp5wO~tEfvh1i<+%s*?0HHrNy%GS##I5@=0Om6Q^{IsaI{^Pq0RO@Oon%=5 zb^!mQ0RLeCra?94=gX`L0N>!n=;_V65dh8|0N~-q`#1pkFaY{60PgS7@$%J~0093p z00000A^8LW002AyEC2ui01yBW000KKK$I`1i0ybt-oiox(Gb2>z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ<0!c(cRCwCtm@io4Fc^nl_Dr;Hk(*oP)ol_S$l-roCT0@5>z;X_NR~r?udfWgLnt(r#9{ZvmZTh)wH(+racW&#=?k2&} zW<9JhfM?m5Cu1QJg+;Z$q2PhmZf;yLnkTt4eKqn+k) zQPUeAQ7tk&aVnX_*TxR*3=e0JB*}ecPBe1plI7925({2cOuJRM++Ko9c#JIVw3~(1 zF?l>OmL9)q2usSBjr^gH)rDmsig=Qb`K`u;PFc;WY_CXbDqIB&>h zns5#ax6i2uw0@$VA17HG)H|u>{gC_=3=PjlFi}x@yj?m!I z9~KU&t?4@{a8$7XfP|(4HUgLk`0w z5X=EuV?iz6iQJj5;U=_1nJ$*`(jj~2M}h39?^~rZmp%Kq^?9sUSqVWnfZ1 zx}kZ}JI5@qvor5f>wRjTZJFg22xom>XWuN3qoG+Iu3;Yk4e!(}@6<8NTNqxicW>B! zAKxQhhK6~U27-vIzvezH1jFD5?#0000HGe literal 0 HcmV?d00001 diff --git a/src/nhttpd/web/index.html b/src/nhttpd/web/index.html new file mode 100644 index 000000000..d819152c5 --- /dev/null +++ b/src/nhttpd/web/index.html @@ -0,0 +1,12 @@ + + + + +Coolstream HD1 yWeb + + + + + + + diff --git a/src/nhttpd/web/robots.txt b/src/nhttpd/web/robots.txt new file mode 100644 index 000000000..53bedca66 --- /dev/null +++ b/src/nhttpd/web/robots.txt @@ -0,0 +1,4 @@ +# robots.txt +User-agent: * +Disallow: / + diff --git a/src/nhttpd/web/scripts/Makefile.am b/src/nhttpd/web/scripts/Makefile.am new file mode 100644 index 000000000..3cfeaf2a5 --- /dev/null +++ b/src/nhttpd/web/scripts/Makefile.am @@ -0,0 +1,8 @@ +install-data-local: + install -d $(DATADIR)/neutrino/httpd-y/scripts + install -m 0755 Y_*.sh $(DATADIR)/neutrino/httpd-y/scripts + install -m 0644 _Y_*.sh $(DATADIR)/neutrino/httpd-y/scripts + install -m 0755 api.sh $(DATADIR)/neutrino/httpd-y/scripts + +uninstall-local: + -rm -rf $(DATADIR)/neutrino/httpd-y/scripts \ No newline at end of file diff --git a/src/nhttpd/web/scripts/Y_Live.sh b/src/nhttpd/web/scripts/Y_Live.sh new file mode 100755 index 000000000..1ba1dff5f --- /dev/null +++ b/src/nhttpd/web/scripts/Y_Live.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# ----------------------------------------------------------- +# Live (yjogol) +# $Date: 2007/02/21 17:41:04 $ +# $Revision: 1.2 $ +# ----------------------------------------------------------- + +. ./_Y_Globals.sh +. ./_Y_Library.sh + +# ----------------------------------------------------------- +live_lock() +{ + call_webserver "control/lcd?lock=1&clear=1&rect=10,10,110,50,1,0&xpos=20&ypos=27&size=22&font=2&text=%20%20%20%20yWeb%0A%20%20LiveView&update=1" >/dev/null + call_webserver "control/rc?lock" >/dev/null + call_webserver "control/zapto?stopplayback" >/dev/null +} +# ----------------------------------------------------------- +live_unlock() +{ + call_webserver "control/lcd?lock=0" >/dev/null + call_webserver "control/rc?unlock" >/dev/null + call_webserver "control/zapto?startplayback" >/dev/null +} +# ----------------------------------------------------------- +prepare_tv() +{ + # SPTS on + call_webserver "control/system?setAViAExtPlayBack=spts" >/dev/null +} +# ----------------------------------------------------------- +prepare_radio() +{ + # SPTS off + call_webserver "control/system?setAViAExtPlayBack=pes" >/dev/null +} + +# ----------------------------------- +# Main +# ----------------------------------- +echo "$1" >/tmp/debug.txt +echo "$*" +case "$1" in + zapto) + if [ "$2" != "" ] + then + call_webserver "control/zapto?$2" >/dev/null + fi + ;; + + switchto) + if [ "$2" = "Radio" ] + then + call_webserver "control/setmode?radio" >/dev/null + else + call_webserver "control/setmode?tv" >/dev/null + fi + ;; + + url) + url=`buildStreamingRawURL` + echo "$url" ;; + + audio-url) + url=`buildStreamingAudioRawURL` + echo "$url" ;; + + live_lock) + live_lock ;; + + live_unlock) + live_unlock ;; + + dboxIP) + buildLocalIP ;; + + prepare_radio) + prepare_radio + Y_APid=`call_webserver "control/yweb?radio_stream_pid"` + url="http://$2:31338/$Y_APid" + echo "$url" > $y_tmp_m3u + echo "$url" > $y_tmp_pls + ;; + + prepare_tv) + prepare_tv + ;; + + udp_stream) +echo "udp stream" + if [ "$2" = "start" ] + then + shift 2 +echo "kill all streams" + killall streamts + killall streampes + killall udpstreamts +echo "start udpstream" + if [ -e /var/bin/udpstreamts ] + then + /var/bin/udpstreamts $* & + else + udpstreamts $* & + fi + pidof udpstreamts >/tmp/udpstreamts.pid + fi + if [ "$2" = "stop" ] + then + killall udpstreamts + fi + ;; + + *) + echo "Parameter falsch: $*" ;; +esac + + + diff --git a/src/nhttpd/web/scripts/Y_Plugins.sh b/src/nhttpd/web/scripts/Y_Plugins.sh new file mode 100755 index 000000000..be1ae5584 --- /dev/null +++ b/src/nhttpd/web/scripts/Y_Plugins.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# ----------------------------------------------------------- +# Plugins (yjogol) +# $Date: 2006/09/16 14:52:23 $ +# $Revision: 1.1 $ +# ----------------------------------------------------------- + +. ./_Y_Globals.sh +. ./_Y_Library.sh + +# =========================================================== +# Settings : Skins +# =========================================================== +# ----------------------------------------------------------- +# Skin Liste +# ----------------------------------------------------------- +skin_get() +{ + check_Y_Web_conf + active_skin=`config_get_value_direct $y_config_Y_Web 'skin'` + html_option_list="" + skin_list=`find $y_path_httpd -name 'Y_Main-*'` + for f in $skin_list + do + skin=`echo "$f"|sed -e s/^.*Y_Main-//g|sed -e s/.css//g` + if [ "$skin" = "$active_skin" ] + then + selec="selected" + else + selec="" + fi + opt="" + html_option_list="$html_option_list $opt" + done + echo "$html_option_list" +} + +# ----------------------------------------------------------- +# Skin setzen : css ueberschreiben $1=Skin-Name +# ----------------------------------------------------------- +skin_set() +{ + cd $y_path_httpd + cp Y_Main-$1.css Y_Main.css + if [ -e global-$1.css ] + then + cp global-$1.css global.css + else + cp global-Standard.css global.css + fi + config_set_value_direct $y_config_Y_Web 'skin' $1 + + msg="Skin changed - Now browsers Refresh/actualization explain" + y_format_message_html +} + +# ----------------------------------------------------------- +# Main +# ----------------------------------------------------------- + +case "$1" in + skin_set) + skin_set $2 ;; + + skin_get) + skin_get ;; + *) + echo "Parameter falsch: $*" ;; +esac + diff --git a/src/nhttpd/web/scripts/Y_Tools.sh b/src/nhttpd/web/scripts/Y_Tools.sh new file mode 100755 index 000000000..c43f76e79 --- /dev/null +++ b/src/nhttpd/web/scripts/Y_Tools.sh @@ -0,0 +1,579 @@ +#!/bin/sh +# ----------------------------------------------------------- +# Tools (yjogol) +# $Date: 2007/02/21 17:41:04 $ +# $Revision: 1.2 $ +# ----------------------------------------------------------- +. ./_Y_Globals.sh +. ./_Y_Library.sh +# =========================================================== +# Settings : Skins +# =========================================================== +# ----------------------------------------------------------- +# Skin Liste +# ----------------------------------------------------------- +skin_get() +{ + check_Y_Web_conf + active_skin=`config_get_value_direct $y_config_Y_Web 'skin'` + html_option_list="" + skin_list=`find $y_path_httpd -name 'Y_Main-*'` + for f in $skin_list + do + skin=`echo "$f"|sed -e s/^.*Y_Main-//g|sed -e s/.css//g` + if [ "$skin" = "$active_skin" ] + then + selec="selected" + else + selec="" + fi + opt="" + html_option_list="$html_option_list $opt" + done + echo "$html_option_list" +} +# ----------------------------------------------------------- +# Skin setzen : css ueberschreiben $1=Skin-Name +# ----------------------------------------------------------- +skin_set() +{ + cd $y_path_httpd + cp Y_Main-$1.css Y_Main.css + if [ -e global-$1.css ] + then + cp global-$1.css global.css + else + cp global-Standard.css global.css + fi + config_set_value_direct $y_config_Y_Web 'skin' $1 + + msg="Skin changed - Now browsers Refresh/actualization explain" + y_format_message_html +} +# ----------------------------------------------------------- +# Image Backup - build form +# ----------------------------------------------------------- +image_upload() +{ + if [ -s "$y_upload_file" ] + then + msg="Image upload ok
    " + msg="$msg " + else + msg="Upload-Problem.
    Please, try again." + msg="$msg " + fi + y_format_message_html +} +# =========================================================== +# Flashimage +# =========================================================== +# ----------------------------------- +# Flash-Backup ($1=mtd Nummer) +# ----------------------------------- +image_backup_mtd() +{ + rm /tmp/*.img + cat /dev/mtd/$1 > /tmp/flash_mtd$1.img +} +# ----------------------------------- +# Sende Download-Page ($1=mtd Nummer) +# ----------------------------------- +# ----------------------------------- +image_delete_download_page() +{ + rm -r /tmp/*.img +# msg="

    " +# y_format_message_html +} +# ----------------------------------------------------------- +# Flash ($1=mtd Nummer) Upload-File $2=true/false =simulate +# ----------------------------------------------------------- +flash_mtd() +{ + simulate="true" + if [ "$2" = "false" ] + then + simulate="false" + fi + rm /tmp/*.img + if [ -s "$y_upload_file" ] + then + echo "" >/tmp/e.txt + msg_nmsg "Image%20%20flashing!" + if [ "$simulate" = "false" ] + then + fcp -v "$y_upload_file" /dev/mtd/$1 >/tmp/e.txt + else #simulation/DEMO + i="0" + while test $i -le 10 + do + p=`expr $i \* 10` + b=`expr $i \* 63` + b=`expr $b / 10` + echo -e "\rDEMO: Erasing blocks: $b/63 ($p%)" >>/tmp/e.txt + i=`expr $i + 1` + sleep 1 + done + i="0" + while test $i -le 20 + do + p=`expr $i \* 5` + b=`expr $i \* 8064` + b=`expr $b / 20` + echo -e "\rDEMO: Writing data: $b k/8064k ($p%)" >>/tmp/e.txt + i=`expr $i + 1` + sleep 2 + done + i="0" + while test $i -le 5 + do + p=`expr $i \* 20` + b=`expr $i \* 8064` + b=`expr $b / 5` + echo -e "\rDEMO: Verifying data: $b k/8064k ($p%)" >>/tmp/e.txt + i=`expr $i + 1` + sleep 1 + done + fi + msg_nmsg "flashing%20ready.%20Reboot..." + msg="flashing done ... please reboot box now ..." + msg="$msg " + y_format_message_html + + if [ "$simulate" = "false" ] + then + busybox reboot -d10 + fi + else + msg="Upload-Problem.
    Try again, please." + msg="$msg " + y_format_message_html + fi +} +# ----------------------------------------------------------- +# copies Uploadfile to $1 +# ----------------------------------------------------------- +upload_copy() +{ + if [ -s "$y_upload_file" ] + then + cp "$y_upload_file" "$1" + else + msg="Upload-Problem.
    Try again, please." + fi +} +# ----------------------------------------------------------- +bootlogo_upload() +{ + msg="Boot-Logo neu gesetzt" + upload_copy "$y_boot_logo" + y_format_message_html +} +# ----------------------------------------------------------- +bootlogo_lcd_upload() +{ + msg="Boot-Logo-LCD neu gesetzt" + upload_copy "$y_boot_logo_lcd" + y_format_message_html +} +# ----------------------------------------------------------- +ucodes_upload() +{ + msg="$1 hochgeladen
    next file" + upload_copy "$y_path_ucodes/$1" + y_format_message_html +} +# ----------------------------------------------------------- +zapit_upload() +{ + msg="$1 hochgeladen
    next file" + upload_copy "$y_path_zapit/$1" + y_format_message_html +} +# ----------------------------------------------------------- +# Mount from Neutrino-Settings $1=nr +# ----------------------------------------------------------- +do_mount() +{ + config_open $y_config_neutrino + fstype=`config_get_value "network_nfs_type_$1"` + ip=`config_get_value "network_nfs_ip_$1"` + local_dir=`config_get_value "network_nfs_local_dir_$1"` + dir=`config_get_value "network_nfs_dir_$1"` + options1=`config_get_value "network_nfs_mount_options1_$1"` + options2=`config_get_value "network_nfs_mount_options2_$1"` + username=`config_get_value "network_nfs_username_$1"` + password=`config_get_value "network_nfs_password_$1"` + + # check options + if [ "$options1" = "" ] + then + options1=options2 + options2="" + fi + + # default options + if [ "$options1" = "" ] + then + if [ "$options2" = "" ] + then + if [ "$fstype" = "0" ] + then + options1="ro,soft,udp" + options2="nolock,rsize=8192,wsize=8192" + fi + if [ "$fstype" = "1" ] + then + options1="ro" + fi + fi + fi + # build mount command + case "$fstype" in + 0) #nfs + cmd="mount -t nfs $ip:$dir $local_dir -o $options1" + ;; + 1) + cmd="mount -t cifs //$ip/$dir $local_dir -o username=$username,password=$password,unc=//$ip/$dir,$options1"; + ;; + 2) + cmd="lufsd none $local_dir -o fs=ftpfs,username=$username,password=$password,host=$ip,root=/$dir,$options1"; + ;; + default) + echo "mount type not supported" + esac + + if [ "$options2" != "" ] + then + cmd="$cmd,$options2" + fi + + res=`$cmd` + echo "$res" + echo "view mounts" + m=`mount` + msg="mount cmd:$cmd

    res=$res
    view Mounts;
    $m" + y_format_message_html +} +# ----------------------------------------------------------- +# unmount $1=local_dir +# ----------------------------------------------------------- +do_unmount() +{ + umount $1 +} +# ----------------------------------------------------------- +# AutoMount +# deaktive mount "#" replaces "---" and "=" replaced ",," +# ----------------------------------------------------------- +do_automount_list() +{ + i="1" + sel="checked='checked'" + cat $1|sed -n /-fstype/p|\ + while read mountname options share + do + mountvalue=`echo "$mountname"|sed -e "s/#/---/g"` + echo "$i: $mountname ($share)
    " + sel="" + i=`expr $i + 1` + done +} +do_automount_getline() +{ + mountname=`echo "$2"|sed -e "s/---/#/g"` + cat $1|sed -n "/^[#]*$mountname[^a-zA-Z0-9]/p" +} +# $1:filename, $2:mountname, $3-*:mountstring +do_automount_setline() +{ + if ! [ -e $1 ]; then + cp /etc/auto.net $1 + fi + filename=$1 + mountname=`echo "$2"|sed -e "s;---;;g"` + shift 2 + mountset=`echo "$*"|sed -e "s;---;#;g" -e "s;,,;=;g"` + cp $filename "$filename.old" + chmod ou+rwss $filename + ex=`cat $filename|sed -n "/^$mountname[^a-zA-Z0-9].*$/p"` + if [ "$ex" = "" ]; then + echo "$mountset" >>$filename + else + tmp=`cat "$filename"|sed -e "s;^$mountname[^a-zA-Z0-9].*$;$mountset;g"` + echo "$tmp" >$filename + fi + + kill -HUP `cat /var/run/automount.pid` +} +# ----------------------------------------------------------- +# Execute shell command +# 1: directory 2: append [true|false] 3+: cmd +# ----------------------------------------------------------- +do_cmd() +{ + cd $1 + pw1="$1" + app="$2" + shift 2 + + if [ "$1" = "cd" ] + then + cd $2 + else + tmp=`$*` #Execute command + fi + pw=`pwd` + echo '
    ' + echo '' +} +# ----------------------------------------------------------- +# yInstaller +# un-tar uploaded file to /tmp. Execute included install.sh +# ----------------------------------------------------------- +do_installer() +{ + # clean up + if [ -s "$y_out_html" ] + then + rm $y_out_html + fi + + if [ -s "$y_upload_file" ] + then + # unpack /tmp/upload.tmp + cd $y_path_tmp + tar -xf "$y_upload_file" + rm $y_upload_file + if [ -s "$y_install" ] #look for install.sh + then + chmod 755 $y_install + o=`$y_install` # execute + rm -f $y_install # clean up + if [ -s "$y_out_html" ] #html - output? + then + echo '' + echo "" + echo "If automatic forwarding does not go." + echo '' +# cat $y_out_html + else + echo '' + echo '' + echo "$o" + echo '' + fi + else + msg="$y_install not found" + y_format_message_html + fi + else + msg="Upload-Problem.
    Try again, please." + y_format_message_html + fi +} +# ----------------------------------------------------------- +# extention Installer $1=URL +# ----------------------------------------------------------- +do_ext_installer() +{ + rm $y_upload_file + wgetlog=`wget -O $y_upload_file $1 2>/tmp/err.log` + if [ -s "$y_upload_file" ];then + cd $y_path_tmp + tar -xf "$y_upload_file" + rm $y_upload_file + if [ -s "$y_install" ] #look for install.sh + then + chmod 755 $y_install + o=`$y_install` # execute + rm -f $y_install # clean up + echo "ok: wget=$wgetlog" + fi + else + e=`cat /tmp/err.log` + echo "error: $y_install not found. wget=$wgetlog $e" + fi +} +# ----------------------------------------------------------- +# view /proc/$1 Informations +# ----------------------------------------------------------- +proc() +{ + msg=`cat /proc/$1` + msg="proc: $1

    $msg" + y_format_message_html +} +# ----------------------------------------------------------- +# wake up $1=MAC +# ----------------------------------------------------------- +wol() +{ + msg=`etherwake $1` + msg="Wake on LAN $1

    $msg" + y_format_message_html +} +# ----------------------------------------------------------- +# ----------------------------------------------------------- +dofbshot() +{ + rm -r /tmp/a.png + fbshot -q /tmp/a.png >/dev/null + msg="" + msg="$msg " + y_format_message_html_plain +} + +# ----------------------------------------------------------- +# Settings Backup/Restore +# ----------------------------------------------------------- +do_settings_backup_restore() +{ + workdir="$y_path_tmp/y_save_settings" + yI_Version="0.1" + case "$1" in + backup) + mkdir $workdir >/dev/null + cp -r $y_path_config $workdir >/dev/null + t=`date +%y%m%d_%H%M%S` + filename="$y_path_tmp/y_Save_Settings_$t.tar" + cd $workdir + tar -cvf $filename ./* >/dev/null + rm -r $workdir >/dev/null + echo "$filename" + ;; + + restore) + msg="restore settings" + if [ -s "$y_upload_file" ] + then + # unpack /tmp/upload.tmp + cd $y_path_tmp + tar -xf "$y_upload_file" + rm $y_upload_file + cp -rf ./config /var/tuxbox/ + rm -r ./config + msg="$msg ok" + y_format_message_html + sync + sync + umount -a + reboot -f + else + msg="$msg error: no upload file" + fi + y_format_message_html + ;; + esac +} +# ----------------------------------------------------------- +# Main +# ----------------------------------------------------------- +#debug +echo "call:$*" >> "/tmp/debug.txt" +case "$1" in + skin_set) skin_set $2 ;; + skin_get) skin_get ;; + image_upload) image_upload ;; + image_backup) image_backup_mtd $2; echo "/tmp/flash_mtd$2.img" ;; + image_flash) shift 1; flash_mtd $* ;; + image_flash_free_tmp) rm -r /tmp/*.img ;; + image_delete) image_delete_download_page ;; + bootlogo_upload) bootlogo_upload ;; + bootlogo_lcd_upload) bootlogo_lcd_upload ;; + ucodes_upload) ucodes_upload $2 ;; + zapit_upload) zapit_upload $2 ;; + kernel-stack) msg=`dmesg`; y_format_message_html ;; + ps) msg=`ps -c`; y_format_message_html ;; + free) f=`free`; p=`df -h`; msg="RAM Memory use\n-------------------\n$f\n\nPartitions\n-------------------\n$p" + y_format_message_html ;; + yreboot) yreboot; echo "Reboot..." ;; + check_yWeb_conf) check_Y_Web_conf ;; + rcsim) rcsim $2 >/dev/null ;; + domount) shift 1; do_mount $* ;; + dounmount) shift 1; do_unmount $* ;; + cmd) shift 1; do_cmd $* ;; + installer) shift 1; do_installer $* ;; + ext_installer) shift 1; do_ext_installer $* ;; + proc) shift 1; proc $* ;; + wol) shift 1; wol $* ;; + dofbshot) dofbshot ;; + get_update_version) wget -O /tmp/version.txt "http://www.yjogol.de/download/Y_Version.txt" ;; + settings_backup_restore) shift 1; do_settings_backup_restore $* ;; + exec_cmd) shift 1; $* ;; + automount_list) shift 1; do_automount_list $* ;; + automount_getline) shift 1; do_automount_getline $* ;; + automount_setline) shift 1; do_automount_setline $* ;; + + timer_get_tvinfo) + shift 1 + res=`wget -O /tmp/tvinfo.xml "http://www.tvinfo.de/share/vidac/rec_info.php?username=$1&password=$2"` + if ! [ -s /tmp/tvinfo.xml ] + then + res="$res File empty!" + fi + echo "$res" + ;; + + timer_get_klack) + config_open $y_config_Y_Web + url=`config_get_value "klack_url"` + klack_url=`echo "$url"|sed -e 's/;/\&/g'` + wget -O /tmp/klack.xml "$klack_url" ;; + + get_synctimer_channels) + if [ -e "$y_path_config/channels.txt" ] + then + cat $y_path_config/channels.txt + else + cat $y_path_httpd/channels.txt + fi + ;; + + get_extention_list) + if [ -e "$y_path_config/extentions.txt" ] + then + cat $y_path_config/extentions.txt + else + cat $y_path_httpd/extentions.txt + fi + ;; + + write_extention_list) + shift 1 + echo "$*" >$y_path_config/extentions.txt + ;; + + url_get) + shift 1 + res=`wget -O /tmp/$2 "$1" >/tmp/url.log` + cat /tmp/$2 + ;; + + standby_status) + status=`switch -s|grep "FNC: 0"` + if [ "$status" = "" ] + then + echo "off" + else + echo "on" + fi + ;; + + *) + echo "[Y_Tools.sh] Parameter falsch: $*" ;; +esac diff --git a/src/nhttpd/web/scripts/_Y_Globals.sh b/src/nhttpd/web/scripts/_Y_Globals.sh new file mode 100755 index 000000000..ac814a69c --- /dev/null +++ b/src/nhttpd/web/scripts/_Y_Globals.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# ----------------------------------------------------------- +# Y Globals (yjogol) +# $Date: 2006/09/16 14:52:23 $ +# $Revision: 1.1 $ +# ----------------------------------------------------------- + +# ----------------------------------------------------------- +# Pathes +# ----------------------------------------------------------- +#y_path_httpd="/share/tuxbox/neutrino/httpd-y" +y_path_httpd=".." +y_path_scripts="$y_path_httpd/scripts" +y_path_usrbin="/var/bin" +y_path_config="/var/tuxbox/config" +y_path_tmp="/tmp" +y_path_ucodes="/var/tuxbox/ucodes" +y_path_zapit="/var/tuxbox/config/zapit" + +y_path_plugin_tuxnew="/var/tuxbox/config/tuxnews" + +y_url_control="http://localhost/control" + +# ----------------------------------------------------------- +# Files +# ----------------------------------------------------------- +y_config_Y_Web="$y_path_config/Y-Web.conf" +y_config_vnc="$y_path_config/vnc.conf" +y_config_nhttpd="$y_path_config/nhttpd.conf" +y_config_neutrino="$y_path_config/neutrino.conf" +y_upload_file="$y_path_tmp/upload.tmp" +y_boot_logo="/var/tuxbox/boot/logo-fb" +y_boot_logo_lcd="/var/tuxbox/boot/logo-lcd" +y_tmp="$y_path_tmp/y.tmp" +y_wait_live="$y_path_httpd/Y_Live_Wait.yhtm" +y_tmp_m3u="$y_path_tmp/y.m3u" +y_tmp_pls="$y_path_tmp/y.pls" + +y_out_html="$y_path_tmp/y_out.yhtm" +y_install="$y_path_tmp/install.sh" + + diff --git a/src/nhttpd/web/scripts/_Y_Library.sh b/src/nhttpd/web/scripts/_Y_Library.sh new file mode 100755 index 000000000..cb5fd8575 --- /dev/null +++ b/src/nhttpd/web/scripts/_Y_Library.sh @@ -0,0 +1,207 @@ +#!/bin/sh +# ----------------------------------------------------------- +# Y Library (yjogol) +# $Date: 2006/09/16 14:52:23 $ +# $Revision: 1.1 $ +# ----------------------------------------------------------- + +# ----------------------------------------------------------- +# local call to WebServer $1=url (after http://locahost:port/<$1>) +# ----------------------------------------------------------- +call_webserver() +{ + port=`sed -n /^Port=/p $y_config_nhttpd | sed -e s/^Port=//1` + tmp=`wget -O - -q "http://localhost:$port/$1"` + echo "$tmp" +} +# =========================================================== +# Streaming URL +# =========================================================== + +buildLocalIP() +{ + localIP=`ifconfig eth0|sed -n '/inet addr/p'|sed -e 's/^.*inet addr://g' -e 's/ .*//g'` + echo "$localIP" +} + +# ----------------------------------------------------------- +# Streaming URL für sed +# ----------------------------------------------------------- +buildStreamingURL() +{ + localIP=`buildLocalIP` + + pids=`call_webserver "control/yweb?video_stream_pids=0"` + echo "http:\/\/$localIP:31339\/0,$pids" +} + +# ----------------------------------------------------------- +# Streaming URL +# ----------------------------------------------------------- +buildStreamingRawURL() +{ + localIP=`buildLocalIP` + pids=`call_webserver "control/yweb?video_stream_pids=0"` + echo "http://$localIP:31339/0,$pids" +} + +# ----------------------------------------------------------- +# Audio: Streaming URL für sed +# ----------------------------------------------------------- +buildStreamingAudioURL() +{ + localIP=`buildLocalIP` + Y_APid=`call_webserver "control/yweb?radio_stream_pid"` + echo "http:\/\/$localIP:31338\/$Y_APid" +} + +# ----------------------------------------------------------- +# Streaming URL +# ----------------------------------------------------------- +buildStreamingAudioRawURL() +{ + localIP=`buildLocalIP` + Y_APid=`call_webserver "control/yweb?radio_stream_pid"` + echo "http://$localIP:31338/$Y_APid" +} + +# ----------------------------------- +# UNIX ($msg) Text als HTML ausgeben +# noch sehr unschoen +# ----------------------------------- + +y_format_message_html() +{ + tmp="" + tmp="$tmp " + tmp="$tmp
    Results
    " + tmp="$tmp
    \n$msg\n
    " + +# tmp="$tmp
    \n$msg\n
    " + echo -e "$tmp" +} +y_format_message_html2() +{ + tmp="" + tmp="$tmp " + tmp="$tmp
    Results
    " + tmp="$tmp $msg
    " + echo "$tmp" +} +y_format_message_html_plain() +{ + tmp="" + tmp="$tmp " + tmp="$tmp $msg" + echo "$tmp" +} +# =========================================================== +# config-Dateien - lesen / schreiben +# (Zeilenformat: VarName=VarValue) +# =========================================================== +cfg="" +# ----------------------------------------------------------- +# config-Datei lesen/cachen (Inhalt in $cfg) +# $1=config-Filename +# ----------------------------------------------------------- +config_open() +{ + cfg="" + cfg=`cat $1` +} +# ----------------------------------------------------------- +# config-Datei schreiben (Inhalt in $cfg) +# $1=config-Filename +# ----------------------------------------------------------- +config_write() +{ + echo "$cfg" >$1 +} +# ----------------------------------------------------------- +# Variablenwert zurueckgeben (vorher open) +# $1=VarName +# ----------------------------------------------------------- +config_get_value() +{ + cmd="sed -n /^$1=/p" + tmp=`echo "$cfg" | $cmd` + cmd="sed -e s/^$1=//1" + tmp=`echo "$tmp" | $cmd` + echo $tmp +} +# ----------------------------------------------------------- +# Variablenwert zurueckgeben (ohne open) +# $1=config-Filename +# $2=VarName +# ----------------------------------------------------------- +config_get_value_direct() +{ + config_open $1 + config_get_value $2 +} +# ----------------------------------------------------------- +# Variablenwert setzen (vorher open) +# $1=VarName) +# $2=VarValue +# ----------------------------------------------------------- +config_set_value() +{ + tmp=`echo "$cfg" | sed -n "/^$1=.*/p"` + if [ "$tmp" = "" ] + then + cfg=`echo -e "$cfg\n$1=$2"` + else + cmd="sed -e s/^$1=.*/$1=$2/g" + cfg=`echo "$cfg" | $cmd` + fi +} +# ----------------------------------------------------------- +# Variablenwert zurueckgeben (ohne open) +# $1=config-Filename +# $2=VarName) +# $3=VarValue +# ----------------------------------------------------------- +config_set_value_direct() +{ + config_open $1 + config_set_value $2 $3 + config_write $1 +} +# ----------------------------------------------------------- +# Reboot +# ----------------------------------------------------------- +yreboot() +{ +# reboot + call_webserver "control/reboot" +} + +# ----------------------------------------------------------- +# Message - nmsg (waiting - press ok on rc) +# ----------------------------------------------------------- +msg_nmsg() +{ + call_webserver "control/message?nmsg=$1" +} + +# ----------------------------------------------------------- +# Message - popup (closes autom.) +# ----------------------------------------------------------- +msg_popup() +{ + call_webserver "control/message?popup=$1" +} + +# ----------------------------------------------------------- +# create Y_Web.conf if does not exists +# ----------------------------------------------------------- +check_Y_Web_conf() +{ + if ! [ -e $y_config_Y_Web ] + then + echo "skin=Tuxbox" >$y_config_Y_Web + echo "slavebox=" >>$y_config_Y_Web + echo "live_resolution_w=384" >>$y_config_Y_Web + echo "live_resolution_wh=288" >>$y_config_Y_Web + fi +} diff --git a/src/nhttpd/web/scripts/api.sh b/src/nhttpd/web/scripts/api.sh new file mode 100755 index 000000000..f6f7aacc9 --- /dev/null +++ b/src/nhttpd/web/scripts/api.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# ----------------------------------------------------------- +# API Script (yjogol) +# $Date: 2007/02/21 17:41:04 $ +# $Revision: 1.1 $ +# ----------------------------------------------------------- +path_httpd=".." +path_scripts="$path_httpd/scripts" +path_usrbin="/var/bin" +path_config="/var/tuxbox/config" +path_tmp="/tmp" + +# ----------------------------------------------------------- +do_udp_stream() +{ +echo "para: $*" + up="no" + if [ -e /var/bin/udpstreamts ]; then + up="/var/bin/udpstreamts" + else + up="udpstreamts" + fi + case "$1" in + installed) + echo "$up" ;; + start) + shift 1 + killall streamts + killall streampes + killall udpstreamts + trap "" 1;$up $* & + ;; + stop) + killall udpstreamts ;; + esac +} +# ----------------------------------------------------------- +# Main +# ----------------------------------------------------------- +case "$1" in + version) + echo '$Revision: 1.1 $' ;; + + udp_stream) + shift 1 + do_udp_stream $* + ;; + *) + echo "[api.sh] Parameter wrong: $*" ;; +esac + + + diff --git a/src/nhttpd/yconfig.h b/src/nhttpd/yconfig.h new file mode 100644 index 000000000..b6e4d74e9 --- /dev/null +++ b/src/nhttpd/yconfig.h @@ -0,0 +1,236 @@ +//============================================================================= +// YHTTPD +// Global System Configuration +//============================================================================= + +//----------------------------------------------------------------------------- +#ifndef __yconfig_h__ +#define __yconfig_h__ + +// c++ +#include +#include +#include + +//----------------------------------------------------------------------------- +// TODO: comment it, if CVS checkin +//#define Y_UPDATE_BETA y +#ifdef Y_UPDATE_BETA +#warning "!!!! THIS IS BETA COMPILING. SWITCH OF (Y_UPDATE_BETA) FOR CVS" +#define Y_CONFIG_USE_WEBLOG y +#endif +//----------------------------------------------------------------------------- +// System Choice ONE choice +//----------------------------------------------------------------------------- +#ifndef CONFIG_SYSTEM_BY_COMPILER // use Compiler directive to set CONFIG_SYSTEM +#define CONFIG_SYSTEM_TUXBOX y // Tuxbox dbox project +//#define CONFIG_SYSTEM_AVM y // AVM FritzBox +//#define CONFIG_SYSTEM_MSS y // Maxtor Shared Storage +//#define CONFIG_SYSTEM_LINUX y // Normal Linux PC +//#define CONFIG_SYSTEM_CYGWIN y // Windows (Cygwin) +#endif +//----------------------------------------------------------------------------- +// General central Definitions +//----------------------------------------------------------------------------- +#define HTTPD_VERSION "3.1.2" // Webserver version (can be overloaded) +#define YHTTPD_VERSION "1.2.0" // Webserver version (Version of yhttpd-core!) +#define IADDR_LOCAL "127.0.0.1" // local IP +#define HTTPD_NAME "yhttpd" // Webserver name (can be overloaded) +#define YHTTPD_NAME "yhttpd_core" // Webserver name (Name of yhttpd-core!) +#define AUTH_NAME_MSG "yhhtpd" // Name in Authentication Dialogue + +#define HTTPD_KEEPALIVE_TIMEOUT 500000 // Timeout for Keep-Alive in mircoseconds +//============================================================================= +// Features wanted +//============================================================================= +//----------------------------------------------------------------------------- +// modules +//----------------------------------------------------------------------------- +//#define Y_CONFIG_USE_TESTHOOK y // Add mod: "Test-Hook" (hook example) +#define Y_CONFIG_USE_YPARSER y // Add mod: "y-Parsing" +#define Y_CONFIG_USE_AUTHHOOK y // Add mod: "Authentication" +#define Y_CONFIG_USE_WEBLOG y // Add mod: "WebLogging" +#define Y_CONFIG_USE_CACHE y // Add mod: Can cache production pages +#define Y_CONFIG_USE_SENDFILE y // Add mod: can send static files (mandantory) +//----------------------------------------------------------------------------- +// modules +//----------------------------------------------------------------------------- +#define Y_CONFIG_FEATURE_CHECK_PORT_AUTORITY y // System: Port < 1024 need Admin-Privileges-Check +#define Y_CONFIG_HAVE_SENDFILE y // System: Have *IX SendFile +#define Y_CONFIG_FEATURE_UPLOAD y // Add Feature: File Upload POST Command +#define Y_CONFIG_USE_HOSTEDWEB y // Add Feature: Use HOSTED Web +#define Y_CONFIG_FEATURE_SHOW_SERVER_CONFIG y // Add Feature (in yParser): add /y/server-config +//#define Y_CONFIG_USE_OPEN_SSL y // Add Feature: use openSSL +//#define Y_CONFIG_FEATURE_KEEP_ALIVE y // Add Feature: Keep-alive //FIXME: does not work correctly now +#define Y_CONFIG_FEATUE_SENDFILE_CAN_ACCESS_ALL y // Add Feature: every file can be accessed (use carefully: security!!) +//#define Y_CONFIG_FEATURE_CHROOT y // Add Feature: Use Change Root for Security +//#define Y_CONFIG_FEATURE_HTTPD_USER y // Add Feature: Set User for yhttpd-Process + +// TODO build error page function +//#define Y_CONFIG_HAVE_HTTPD_ERRORPAGE y // Have an HTTPD Error Page + +//----------------------------------------------------------------------------- +// Define/Undefine Features forced by CONFIG_SYSTEM_xxx +// Dependencies +//----------------------------------------------------------------------------- +#ifdef Y_CONFIG_USE_OPEN_SSL +#undef Y_CONFIG_HAVE_SENDFILE // Sendfile does not work for SSL +#endif + +#ifdef CONFIG_SYSTEM_CYGWIN +#undef Y_CONFIG_FEATURE_CHECK_PORT_AUTORITY +#undef Y_CONFIG_HAVE_SENDFILE // No Sendfile under cygwin +#endif +#ifdef CONFIG_SYSTEM_TUXBOX +#define Y_CONFIG_FEATURE_UPLOAD y +#define Y_CONFIG_USE_YPARSER y +#define Y_CONFIG_USE_AUTHHOOK y +#endif +#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE +#define HTTP_PROTOCOL "HTTP/1.1" +#else +#define HTTP_PROTOCOL "HTTP/1.0" +#endif +//============================================================================= +// Configurations for systems/OSs +//============================================================================= +//----------------------------------------------------------------------------- +// Configurations for LINUX +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_LINUX +#define HTTPD_STANDARD_PORT 8080 +#define HTTPD_MAX_CONNECTIONS 3 +#define HTTPD_CONFIGDIR "/home/y" +#define HTTPD_CONFIGFILE HTTPD_CONFIGDIR "/yhttpd.conf" +#define HTTPD_REQUEST_LOG "/tmp/httpd_log" //TODO: delete every occurence +#define HTTPD_ERRORPAGE "/Y_ErrorPage.yhtm" +#define HTTPD_SENDFILE_EXT "htm:text/html,html:text/html,xml:text/xml,txt:text/plain,jpg:image/jpeg,jpeg:image/jpeg,gif:image/gif,png:image/png,bmp:image/bmp,css:text/css,js:text/plain,img:application/octet-stream,ico:image/x-icon,m3u:application/octet-stream,tar:application/octet-stream" +#define AUTHUSER "test" +#define AUTHPASSWORD "test1" +#define PRIVATEDOCUMENTROOT "/home/y/nhttpd-y" +#define PUBLICDOCUMENTROOT "/var/httpd" +#define HOSTEDDOCUMENTROOT "/mnt/hosted" + +#define SSL_PEMFILE HTTPD_CONFIGDIR "/server.pem" +#define SSL_CA_FILE HTTPD_CONFIGDIR "/cacert.pem" + +#define UPLOAD_TMP_FILE "/tmp/upload.tmp" +#define CACHE_DIR "/tmp/.cache" +#endif +//----------------------------------------------------------------------------- +// Configurations for WINDOWS (Cygwin) +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_CYGWIN +#define HTTPD_STANDARD_PORT 80 +#define HTTPD_MAX_CONNECTIONS 32 +#define HTTPD_CONFIGDIR "/usr/local/yweb" +#define HTTPD_CONFIGFILE HTTPD_CONFIGDIR "/yhttpd.conf" +#define HTTPD_REQUEST_LOG "/tmp/httpd_log" +#define HTTPD_ERRORPAGE "/Y_ErrorPage.yhtm" +#define HTTPD_SENDFILE_EXT "htm:text/html,html:text/html,xml:text/xml,txt:text/plain,jpg:image/jpeg,jpeg:image/jpeg,gif:image/gif,png:image/png,bmp:image/bmp,css:text/css,js:text/plain" + +#define AUTHUSER "test" +#define AUTHPASSWORD "test1" +#define PRIVATEDOCUMENTROOT "/cygdrive/d/Work/y/ws/nhttpd30/web" +#define PUBLICDOCUMENTROOT "/var/httpd" +#define HOSTEDDOCUMENTROOT "/mnt/hosted" + +#define SSL_PEMFILE HTTPD_CONFIGDIR "/server.pem" +#define SSL_CA_FILE HTTPD_CONFIGDIR "/cacert.pem" + +#define LOG_FILE "./yhhtpd.log" +#define LOG_FORMAT "CLF" + +#define UPLOAD_TMP_FILE "/tmp/upload.tmp" +#define CACHE_DIR "/tmp/.cache" +#endif +//----------------------------------------------------------------------------- +// Configurations for LINUX (Tuxbox dbox2) +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_TUXBOX +#undef HTTPD_NAME +#define HTTPD_NAME "nhttpd" +#define HTTPD_STANDARD_PORT 80 +#define HTTPD_MAX_CONNECTIONS 10 +#define HTTPD_CONFIGDIR "/var/tuxbox/config" +#define HTTPD_CONFIGFILE HTTPD_CONFIGDIR "/nhttpd.conf" +#define HTTPD_REQUEST_LOG "/tmp/httpd_log" +#define HTTPD_ERRORPAGE "/Y_ErrorPage.yhtm" +#define HTTPD_SENDFILE_EXT "htm:text/html,html:text/html,xml:text/xml,txt:text/plain,jpg:image/jpeg,jpeg:image/jpeg,gif:image/gif,png:image/png,bmp:image/bmp,css:text/css,js:text/plain,img:application/octet-stream,ico:image/x-icon,m3u:application/octet-stream" + +#define AUTHUSER "root" +#define AUTHPASSWORD "dbox2" +#define PRIVATEDOCUMENTROOT "/share/tuxbox/neutrino/httpd-y" +#define PUBLICDOCUMENTROOT "/var/httpd" +#define NEUTRINO_CONFIGFILE "/var/tuxbox/config/neutrino.conf" +#define HOSTEDDOCUMENTROOT "/mnt/hosted" +#define EXTRASDOCUMENTROOT "/mnt/hosted/extras" +#define EXTRASDOCUMENTURL "/hosted/extras" +#define ZAPITXMLPATH "/var/tuxbox/config/zapit" + +#define SSL_PEMFILE HTTPD_CONFIGDIR "/server.pem" +#define SSL_CA_FILE HTTPD_CONFIGDIR "/cacert.pem" + +#define LOG_FILE "/tmp/yhhtpd.log" +#define LOG_FORMAT "" + +#define UPLOAD_TMP_FILE "/tmp/upload.tmp" +#define CACHE_DIR "/tmp/.cache" +#endif +//----------------------------------------------------------------------------- +// Configurations for AVM FritzBox +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_AVM +#define HTTPD_STANDARD_PORT 81 +#define HTTPD_MAX_CONNECTIONS 3 +#define HTTPD_CONFIGDIR "/tmp" +#define HTTPD_CONFIGFILE HTTPD_CONFIGDIR "/yhttpd.conf" +#define HTTPD_REQUEST_LOG "/tmp/httpd_log" +#define HTTPD_ERRORPAGE "/Y_ErrorPage.yhtm" +#define HTTPD_SENDFILE_EXT "htm:text/html,html:text/html,xml:text/xml,txt:text/plain,jpg:image/jpeg,jpeg:image/jpeg,gif:image/gif,png:image/png,bmp:image/bmp,css:text/css,js:text/plain" + +#define AUTHUSER "root" +#define AUTHPASSWORD "oxmox" +#define PRIVATEDOCUMENTROOT "/tmp/web" //FIXME: Test +#define PUBLICDOCUMENTROOT "/var/httpd" +#define HOSTEDDOCUMENTROOT "/mnt/hosted" + +#define SSL_PEMFILE HTTPD_CONFIGDIR "/server.pem" +#define SSL_CA_FILE HTTPD_CONFIGDIR "/cacert.pem" + +#define UPLOAD_TMP_FILE "/tmp/upload.tmp" +#define CACHE_DIR "/tmp/.cache" +#endif + +//----------------------------------------------------------------------------- +// Configurations for Maxtor Shared Storage +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_MSS +#define HTTPD_STANDARD_PORT 81 +#define HTTPD_MAX_CONNECTIONS 3 +#define HTTPD_CONFIGDIR "/tmp" +#define HTTPD_CONFIGFILE HTTPD_CONFIGDIR "/yhttpd.conf" +#define HTTPD_REQUEST_LOG "/tmp/httpd_log" +#define HTTPD_ERRORPAGE "/Y_ErrorPage.yhtm" +#define HTTPD_SENDFILE_EXT "htm:text/html,html:text/html,xml:text/xml,txt:text/plain,jpg:image/jpeg,jpeg:image/jpeg,gif:image/gif,png:image/png,bmp:image/bmp,css:text/css,js:text/plain" + +#define AUTHUSER "root" +#define AUTHPASSWORD "oxmox" +#define PRIVATEDOCUMENTROOT "/shares/mss-hdd/Install/web" +#define PUBLICDOCUMENTROOT "/var/httpd" +#define HOSTEDDOCUMENTROOT "/mnt/hosted" + +#define SSL_PEMFILE HTTPD_CONFIGDIR "/server.pem" +#define SSL_CA_FILE HTTPD_CONFIGDIR "/cacert.pem" + +#define UPLOAD_TMP_FILE "/tmp/upload.tmp" +#define CACHE_DIR "/tmp/.cache" +#endif + + +//----------------------------------------------------------------------------- +// Aggregated definitions +//----------------------------------------------------------------------------- +#define WEBSERVERNAME HTTPD_NAME "/" HTTPD_VERSION " (" YHTTPD_NAME "/" YHTTPD_VERSION ")" + +#endif // __yconfig_h__ diff --git a/src/nhttpd/yhttpd.cpp b/src/nhttpd/yhttpd.cpp new file mode 100644 index 000000000..f0e813de2 --- /dev/null +++ b/src/nhttpd/yhttpd.cpp @@ -0,0 +1,494 @@ +//============================================================================= +// YHTTPD +// Main Program +//============================================================================= + +// system +#include +#include +#include +#include +#include +#include + +// yhttpd +#include "yconfig.h" +#include "ylogging.h" +#include "yhook.h" + +#ifdef Y_CONFIG_USE_YPARSER +#include "mod_yparser.h" +static CyParser yParser; +#endif + +//----------------------------------------------------------------------------- +// Setting yhttpd Instance +//----------------------------------------------------------------------------- +#include "yhttpd.h" +static Cyhttpd *yhttpd = NULL; +CStringList Cyhttpd::ConfigList; +//============================================================================= +// HOOKS: Definition & Instance for Hooks, attach/detach Hooks +//============================================================================= + +#ifdef Y_CONFIG_USE_AUTHHOOK +#include "mod_auth.h" +static CmAuth *auth = NULL; +#endif + +#ifdef Y_CONFIG_USE_TESTHOOK +#include "mod_testhook.h" +static CTesthook *testhook = NULL; +#endif + +#ifdef Y_CONFIG_USE_WEBLOG +#include "mod_weblog.h" +static CmWebLog *weblog = NULL; +#endif + +#ifdef Y_CONFIG_USE_SENDFILE +#include "mod_sendfile.h" +static CmodSendfile *mod_sendfile = NULL; +#endif + +#ifdef Y_CONFIG_USE_CACHE +#include "mod_cache.h" +static CmodCache mod_cache; // static instance +#endif + +//----------------------------------------------------------------------------- +#ifdef CONFIG_SYSTEM_TUXBOX +#include "neutrinoapi.h" +static CNeutrinoAPI *NeutrinoAPI; +#endif + +//============================================================================= +// Main: Main Entry, Command line passing, Webserver Instance creation & Loop +//============================================================================= +volatile sig_atomic_t Cyhttpd::sig_do_shutdown = 0; +#if 0 +//----------------------------------------------------------------------------- +// Signal Handling +//----------------------------------------------------------------------------- +static void sig_catch(int msignal) +{ + aprintf("!!! SIGNAL !!! :%d!\n",msignal); + switch (msignal) { +// case SIGTERM: +// case SIGINT: + + case SIGPIPE: + aprintf("got signal PIPE, nice!\n"); + break; + case SIGHUP: + case SIGUSR1: + aprintf("got signal HUP/USR1, reading config\n"); + if (yhttpd) + yhttpd->ReadConfig(); + break; + default: + aprintf("No special SIGNAL-Handler:%d!\n",msignal); +// log_level_printf(1, "Got SIGTERM\n"); + Cyhttpd::sig_do_shutdown = 1; + yhttpd->stop_webserver(); + delete yhttpd; + exit(EXIT_SUCCESS); //FIXME: return to main() some way... + break; + + } +} +#endif + +void yhttpd_reload_config() +{ + if (yhttpd) + yhttpd->ReadConfig(); +} +//----------------------------------------------------------------------------- +// Main Entry +//----------------------------------------------------------------------------- +//int main(int argc, char **argv) +void * nhttpd_main_thread(void *data) +{ + //int argc = 1; + //char **argv; + //bool do_fork = false; + + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); + + aprintf("Webserver %s tid %ld\n", WEBSERVERNAME, syscall(__NR_gettid)); + yhttpd = new Cyhttpd(); + +//CLogging::getInstance()->setDebug(true); +//CLogging::getInstance()->LogLevel = 9; + if(!yhttpd) + { + aprintf("Error initializing WebServer\n"); + return (void *) EXIT_FAILURE; + } + yhttpd->flag_threading_off = true; +#if 0 + for (int i = 1; i < argc; i++) + { + if ((!strncmp(argv[i], "-d", 2)) || (!strncmp(argv[i], "--debug", 7))) + { + CLogging::getInstance()->setDebug(true); + do_fork = false; + } + else if ((!strncmp(argv[i], "-f", 2)) || (!strncmp(argv[i], "--fork", 6)) || (!strncmp(argv[i], "-nf", 3))) + { + do_fork = false; + } + else if ((!strncmp(argv[i], "-h", 2)) || (!strncmp(argv[i], "--help", 6))) + { + yhttpd->usage(stdout); + return (void *) EXIT_SUCCESS; + } + else if ((!strncmp(argv[i], "-v", 2)) || (!strncmp(argv[i],"--version", 9))) + { + yhttpd->version(stdout); + return (void *) EXIT_SUCCESS; + } + else if ((!strncmp(argv[i], "-t", 2)) || (!strncmp(argv[i],"--thread-off", 12))) + { + yhttpd->flag_threading_off = true; + } + else if ((!strncmp(argv[i], "-l", 2)) ) + { + if(argv[i][2] >= '0' && argv[i][2] <= '9') + CLogging::getInstance()->LogLevel = (argv[i][2]-'0'); + } + else + { + yhttpd->usage(stderr); + return (void *) EXIT_FAILURE; + } + } + // setup signal catching (subscribing) + //signal(SIGPIPE, sig_catch); + //signal(SIGINT, sig_catch); + //signal(SIGHUP, sig_catch); + //signal(SIGUSR1, sig_catch); + //signal(SIGTERM, sig_catch); + //signal(SIGCLD, SIG_IGN); +// signal(SIGALRM, sig_catch); +#endif + + yhttpd->hooks_attach(); + yhttpd->ReadConfig(); + if(yhttpd->Configure()) + { + // Start Webserver: fork ist if not in debug mode + aprintf("Webserver starting...\n"); + dprintf("Start in Debug-Mode\n"); // non forked debugging loop + + yhttpd->run(); + } + delete yhttpd; + + aprintf("Main end\n"); + return (void *) EXIT_SUCCESS; +} + +//============================================================================= +// Class yhttpd +//============================================================================= +Cyhttpd::Cyhttpd() +{ + webserver = new CWebserver(); + flag_threading_off = false; +} +//----------------------------------------------------------------------------- +Cyhttpd::~Cyhttpd() +{ + if(webserver) + delete webserver; + webserver = NULL; +} + +//----------------------------------------------------------------------------- +// Change to Root +//----------------------------------------------------------------------------- +bool Cyhttpd::Configure() +{ + + if(!getuid()) // you must be root to do that! + { + // Get user and group data +#ifdef Y_CONFIG_FEATURE_HTTPD_USER + struct passwd *pwd = NULL; + struct group *grp = NULL; + std::string username = ConfigList["server.user_name"]; + std::string groupname= ConfigList["server.group_name"]; + + // get user data + if(username != "") + { + if((pwd = getpwnam(username.c_str())) == NULL) + { + dperror("Dont know user to set uid\n"); + return false; + } + } + // get group data + if(groupname != "") + { + if((grp = getgrnam(groupname.c_str())) == NULL) + { + aprintf("Can not get Group-Information. Group: %s\n", groupname.c_str()); + return false; + } + } +#endif +// change root directory +#ifdef Y_CONFIG_FEATURE_CHROOT + if(!ConfigList["server.chroot"].empty()) + { + log_level_printf(2, "do chroot to dir:%s\n", ConfigList["server.chroot"].c_str() ); + // do change Root + if(chroot(ConfigList["server.chroot"].c_str()) == -1) + { + dperror("Change Root failed\n"); + return false; + } + // Set Working Dir + if(chdir("/") == -1) + { + dperror("Change Directory to Root failed\n"); + return false; + } + } +#endif +#ifdef Y_CONFIG_FEATURE_HTTPD_USER + if(username != "" && pwd != NULL && grp != NULL) + { + log_level_printf(2, "set user and groups\n"); + + // drop root privileges + setgid(grp->gr_gid); + setgroups(0, NULL); + // set user group + if(groupname != "") + initgroups(username.c_str(), grp->gr_gid); + // set user + if(setuid(pwd->pw_uid) == -1) + { + dperror("Change User Context failed\n"); + return false; + } + } +#endif + } + return true; +} +//----------------------------------------------------------------------------- +// Main Webserver call +//----------------------------------------------------------------------------- +void Cyhttpd::run() +{ + if(webserver) + { + if(flag_threading_off) + webserver->is_threading = false; + webserver->run(); + stop_webserver(); + } + else + aprintf("Error initializing WebServer\n"); +} + +//----------------------------------------------------------------------------- +// Show Version Text and Number +//----------------------------------------------------------------------------- +void Cyhttpd::version(FILE *dest) +{ + fprintf(dest, "%s - Webserver v%s\n", HTTPD_NAME, HTTPD_VERSION); +} + +//----------------------------------------------------------------------------- +// Show Usage +//----------------------------------------------------------------------------- +void Cyhttpd::usage(FILE *dest) +{ + version(dest); + fprintf(dest, "command line parameters:\n"); + fprintf(dest, "-d, --debug enable debugging code (implies -f)\n"); + fprintf(dest, "-f, --fork do not fork\n"); + fprintf(dest, "-h, --help display this text and exit\n\n"); + fprintf(dest, "-v, --version display version and exit\n"); + fprintf(dest, "-l, set loglevel (0 .. 9)\n"); + fprintf(dest, "-t, --thread-off set threading off\n"); +} + +//----------------------------------------------------------------------------- +// Stop WebServer +//----------------------------------------------------------------------------- +void Cyhttpd::stop_webserver() +{ + aprintf("stop requested......\n"); + if (webserver) { + webserver->stop(); + hooks_detach(); + } +} +//----------------------------------------------------------------------------- +// Attach hooks (use hook order carefully) +//----------------------------------------------------------------------------- +void Cyhttpd::hooks_attach() +{ +#ifdef Y_CONFIG_USE_AUTHHOOK + // First Check Authentication + auth = new CmAuth(); + CyhookHandler::attach(auth); +#endif + +#ifdef Y_CONFIG_USE_TESTHOOK + testhook = new CTesthook(); + CyhookHandler::attach(testhook); +#endif + +#ifdef CONFIG_SYSTEM_TUXBOX + NeutrinoAPI = new CNeutrinoAPI(); + CyhookHandler::attach(NeutrinoAPI->NeutrinoYParser); + CyhookHandler::attach(NeutrinoAPI->ControlAPI); +#else +#ifdef Y_CONFIG_USE_YPARSER + CyhookHandler::attach(&yParser); +#endif +#endif + +#ifdef Y_CONFIG_USE_CACHE + CyhookHandler::attach(&mod_cache); +#endif + +#ifdef Y_CONFIG_USE_SENDFILE + mod_sendfile = new CmodSendfile(); + CyhookHandler::attach(mod_sendfile); +#endif +#ifdef Y_CONFIG_USE_WEBLOG + weblog = new CmWebLog(); + CyhookHandler::attach(weblog); +#endif +} + +//----------------------------------------------------------------------------- +// Detach hooks & Destroy +//----------------------------------------------------------------------------- +void Cyhttpd::hooks_detach() +{ +#ifdef Y_CONFIG_USE_AUTHHOOK + CyhookHandler::detach(auth); + delete auth; +#endif + +#ifdef Y_CONFIG_USE_TESTHOOK + CyhookHandler::detach(testhook); + delete testhook; +#endif + +#ifdef CONFIG_SYSTEM_TUXBOX + CyhookHandler::detach(NeutrinoAPI->NeutrinoYParser); +#else +#ifdef Y_CONFIG_USE_YPARSER + CyhookHandler::detach(&yParser); +#endif +#endif + +#ifdef Y_CONFIG_USE_CACHE + CyhookHandler::detach(&mod_cache); +#endif + +#ifdef Y_CONFIG_USE_SENDFILE + CyhookHandler::detach(mod_sendfile); +#endif + +#ifdef Y_CONFIG_USE_WEBLOG + CyhookHandler::detach(weblog); + delete weblog; +#endif +} + +//----------------------------------------------------------------------------- +// Read Webserver Configurationfile +// Call "Hooks_ReadConfig" so Hooks can read/write own Configuration Values +//----------------------------------------------------------------------------- +void Cyhttpd::ReadConfig(void) +{ + log_level_printf(3,"ReadConfig Start\n"); + CConfigFile *Config = new CConfigFile(','); + bool have_config = false; + if(access(HTTPD_CONFIGFILE,4) == 0) + have_config = true; + Config->loadConfig(HTTPD_CONFIGFILE); + // convert old config files + if(have_config) + if(Config->getInt32("Port", 0) != 0) + { + CConfigFile OrgConfig = *Config; + Config->clear(); + + Config->setInt32("server.log.loglevel", OrgConfig.getInt32("LogLevel", 0)); + Config->setString("configfile.version", "1"); + Config->setString("webserver.websites", "WebsiteMain"); + Config->setBool("webserver.threading", OrgConfig.getBool("THREADS", true)); + Config->setInt32("WebsiteMain.port",OrgConfig.getInt32("Port", HTTPD_STANDARD_PORT)); + Config->setString("WebsiteMain.directory", OrgConfig.getString("PrivatDocRoot", PRIVATEDOCUMENTROOT)); + if(OrgConfig.getString("PublicDocRoot", "") != "") + Config->setString("WebsiteMain.override_directory", OrgConfig.getString("PublicDocRoot", PRIVATEDOCUMENTROOT)); + if(OrgConfig.getString("HostedDocRoot", "") != "") + Config->setString("WebsiteMain.special_locations", "/hosted/="+OrgConfig.getString("HostedDocRoot", PRIVATEDOCUMENTROOT)); + if(OrgConfig.getString("HostedDocRoot", "") != "") + Config->setString("Tuxbox.HostedDocumentRoot", OrgConfig.getString("HostedDocRoot", PRIVATEDOCUMENTROOT)); + // mod_auth + Config->setString("mod_auth.username", OrgConfig.getString("AuthUser", AUTHUSER)); + Config->setString("mod_auth.password", OrgConfig.getString("AuthPassword", AUTHPASSWORD)); + Config->setString("mod_auth.no_auth_client", OrgConfig.getString("NoAuthClient", "")); + Config->setString("mod_auth.authenticate", OrgConfig.getString("Authenticate", "false")); + + Config->setString("mod_sendfile.mime_types", HTTPD_SENDFILE_EXT); + + Config->saveConfig(HTTPD_CONFIGFILE); + + } + // configure debugging & logging + if(CLogging::getInstance()->LogLevel == 0) + CLogging::getInstance()->LogLevel = Config->getInt32("server.log.loglevel", 0); + + // get variables + webserver->init(Config->getInt32("WebsiteMain.port", HTTPD_STANDARD_PORT), Config->getBool("webserver.threading", true)); + // informational use + ConfigList["WebsiteMain.port"]= itoa(Config->getInt32("WebsiteMain.port", HTTPD_STANDARD_PORT)); + ConfigList["webserver.threading"]= Config->getString("webserver.threading", "true"); + ConfigList["configfile.version"]= Config->getString("configfile.version", "1"); + ConfigList["server.log.loglevel"]= itoa(Config->getInt32("server.log.loglevel", 0)); + ConfigList["server.no_keep-alive_ips"]= Config->getString("server.no_keep-alive_ips", ""); + webserver->conf_no_keep_alive_ips = Config->getStringVector("server.no_keep-alive_ips"); + + // MainSite + ConfigList["PrivatDocumentRoot"]= Config->getString("WebsiteMain.directory", PRIVATEDOCUMENTROOT); + ConfigList["PublicDocumentRoot"]= Config->getString("WebsiteMain.override_directory", PUBLICDOCUMENTROOT); + ConfigList["HostedDocumentRoot"]= Config->getString("Tuxbox.HostedDocumentRoot", HOSTEDDOCUMENTROOT); +#ifdef Y_CONFIG_USE_OPEN_SSL + ConfigList["SSL"] = Config->getString("WebsiteMain.ssl", "false"); + ConfigList["SSL_pemfile"] = Config->getString("WebsiteMain.ssl_pemfile", SSL_PEMFILE); + ConfigList["SSL_CA_file"] = Config->getString("WebsiteMain.ssl_ca_file", SSL_CA_FILE); + + CySocket::SSL_pemfile = ConfigList["SSL_pemfile"]; + CySocket::SSL_CA_file = ConfigList["SSL_CA_file"]; + if(ConfigList["SSL"] == "true") + CySocket::initSSL(); +#endif + ConfigList["server.user_name"]= Config->getString("server.user_name", ""); + ConfigList["server.group_name"]= Config->getString("server.group_name", ""); + ConfigList["server.chroot"]= Config->getString("server.chroot", ""); + + + // Read App specifig settings by Hook + CyhookHandler::Hooks_ReadConfig(Config, ConfigList); + + // Save if new defaults are set + if (!have_config) + Config->saveConfig(HTTPD_CONFIGFILE); + log_level_printf(3,"ReadConfig End\n"); + delete Config; +} diff --git a/src/nhttpd/yhttpd.h b/src/nhttpd/yhttpd.h new file mode 100644 index 000000000..507ad259c --- /dev/null +++ b/src/nhttpd/yhttpd.h @@ -0,0 +1,50 @@ +//============================================================================= +// YHTTPD +// Main Class +//----------------------------------------------------------------------------- +// Cyhttpd +// Main Function and Class for Handlicg the Webserver-Application +// - Handles Command Line Input +// - Reads and Handles "ReadConfig" (inclusive Hooking) +// - Creates Webserver and start them listening +//============================================================================= +#ifndef __yhttpd_h__ +#define __yhttpd_h__ +// system +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ywebserver.h" + + +//----------------------------------------------------------------------------- +class Cyhttpd +{ +private: + CWebserver *webserver; // Aggregation of Webserver (now: only one) + +public: + bool flag_threading_off; // switch of Connection Threading + static CStringList ConfigList; // Vars & Values from ReadConfig + + // signal handler + static volatile sig_atomic_t sig_do_shutdown; + + // constructor & destructor + Cyhttpd(); + ~Cyhttpd(); + + // Main Programm calls + void run(); // Init Hooks, ReadConfig, Start Webserver + bool Configure(); + void stop_webserver(); // Remove Hooks, Stop Webserver + static void version(FILE *dest); // Show Webserver Version + static void usage(FILE *dest); // Show command line usage + // Hooks + void hooks_attach(); // Add a Hook-Class to HookList + void hooks_detach(); // Remove a Hook-Class from HookList + void ReadConfig(void); // Read the config file for the webserver +}; + +#endif // __yhttpd_h__ diff --git a/src/nhttpd/yhttpd_core/AUTHORS b/src/nhttpd/yhttpd_core/AUTHORS new file mode 100644 index 000000000..ca6361b99 --- /dev/null +++ b/src/nhttpd/yhttpd_core/AUTHORS @@ -0,0 +1,4 @@ +The code-base of yhttpd is Tuxbox-nhttpd 2.x (cvs.tuxbox.org) + +Authors since re-writting of yhttpd: +Johannes Golombek [yjogol@cvs.tuxbox.org] diff --git a/src/nhttpd/yhttpd_core/Makefile.am b/src/nhttpd/yhttpd_core/Makefile.am new file mode 100644 index 000000000..1cd16c57f --- /dev/null +++ b/src/nhttpd/yhttpd_core/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/nhttpd \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + @FREETYPE_CFLAGS@ + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libyhttpd.a + +libyhttpd_a_SOURCES = \ + ylogging.cpp helper.cpp \ + ywebserver.cpp yconnection.cpp yrequest.cpp yresponse.cpp yhook.cpp ysocket.cpp + diff --git a/src/nhttpd/yhttpd_core/README b/src/nhttpd/yhttpd_core/README new file mode 100644 index 000000000..f4bb97634 --- /dev/null +++ b/src/nhttpd/yhttpd_core/README @@ -0,0 +1,22 @@ +------------------------------------------------------------------------------ +yhttpd-GOALS +(yjogol@online.de, August 2006) +------------------------------------------------------------------------------ +The yhttpd-Webserver is a small C++ based Webserver for embedded Systems. +It has a simple Software-Architecture and uses for string-operations +mostly buffer-save functions to prevent security exploits like buffer overruns. +It is designed for threading and multiuser access for static files. +It can be compiled on Windows Systems using Cygwin, on Linux-Systems +and had been succesfully compiled for AVM Fritzbox or for +Maxtor Shared Storage. + +The code-base of yhttpd is Tuxbox-nhttpd 2.x (cvs.tuxbox.org) + +Starting with Version 3.0 the nhttpd source code was splitted into +a core Webserver (yhttpd_core) and extension modules (yhttpd_mods). + +The yhttpd_core and yhttp_mods ware completly rewritten. + +The Hook-System (extension modules) encapsulate all requests and responses +for the tuxboxapi. So both tuxapi and yhttpd can be developed seperatlly and +independ from each other. \ No newline at end of file diff --git a/src/nhttpd/yhttpd_core/helper.cpp b/src/nhttpd/yhttpd_core/helper.cpp new file mode 100644 index 000000000..7490e090b --- /dev/null +++ b/src/nhttpd/yhttpd_core/helper.cpp @@ -0,0 +1,280 @@ +//============================================================================= +// YHTTPD +// Helper +//============================================================================= + +// c +#include // printf prototype. +#include // calloc and free prototypes. +#include // str* and memset prototypes. +#include + +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "helper.h" +#include "ylogging.h" + +//============================================================================= +// Integers +//============================================================================= +//------------------------------------------------------------------------- +// Check and set integer inside boundaries (min, max) +//------------------------------------------------------------------------- +int minmax(int value,int min, int max) +{ + if(value < min) return min; + if(value > max) return max; + return value; +} +//============================================================================= +// Date & Time +//============================================================================= +//------------------------------------------------------------------------- +// Check and set Date/Time (tm*) inside boundaries +//------------------------------------------------------------------------- +void correctTime(struct tm *zt) +{ + + zt->tm_year = minmax(zt->tm_year,0,129); + zt->tm_mon = minmax(zt->tm_mon,0,11); + zt->tm_mday = minmax(zt->tm_mday,1,31); //-> eine etwas laxe pruefung, aber mktime biegt das wieder grade + zt->tm_hour = minmax(zt->tm_hour,0,23); + zt->tm_min = minmax(zt->tm_min,0,59); + zt->tm_sec = minmax(zt->tm_sec,0,59); +} +//============================================================================= +// Strings +//============================================================================= +//------------------------------------------------------------------------- +// Integer to Hexadecimal-String +//------------------------------------------------------------------------- +std::string itoh(unsigned int conv) +{ + return string_printf("0x%06x",conv); +} +//------------------------------------------------------------------------- +// Integer to String +//------------------------------------------------------------------------- +std::string itoa(unsigned int conv) +{ + return string_printf("%u",conv); +} +//------------------------------------------------------------------------- +// convert timer_t to ":" String +//------------------------------------------------------------------------- +std::string timeString(time_t time) +{ + char tmp[7]={'\0'}; + struct tm *tm = localtime(&time); + if (strftime(tmp, 6, "%H:%M", tm)) + return std::string(tmp); + else + return std::string("??:??"); +} +//------------------------------------------------------------------------- +// Printf and return formatet String. Buffer-save! +// max length up to bufferlen -> then snip +//------------------------------------------------------------------------- +#define bufferlen 4*1024 +std::string string_printf(const char *fmt, ...) +{ + char buffer[bufferlen]; + va_list arglist; + va_start( arglist, fmt ); + if(arglist) + vsnprintf( buffer, bufferlen, fmt, arglist ); + va_end(arglist); + return std::string(buffer); +} +//------------------------------------------------------------------------- +// ySplitString: spit string "str" in two strings "left" and "right" at +// one of the chars in "delimiter" returns true if delimiter found +//------------------------------------------------------------------------- +bool ySplitString(std::string str, std::string delimiter, std::string& left, std::string& right) +{ + unsigned int pos; + if ((pos = str.find_first_of(delimiter)) != std::string::npos) + { + left = str.substr(0, pos); + right = str.substr(pos + 1, str.length() - (pos + 1 )); + } + else + { + left = str; //default if not found + right = ""; + } + return (pos != std::string::npos); +} +//------------------------------------------------------------------------- +// ySplitString: spit string "str" in two strings "left" and "right" at +// one of the chars in "delimiter" returns true if delimiter found +//------------------------------------------------------------------------- +bool ySplitStringExact(std::string str, std::string delimiter, std::string& left, std::string& right) +{ + unsigned int pos; + if ((pos = str.find(delimiter)) != std::string::npos) + { + left = str.substr(0, pos); + right = str.substr(pos + delimiter.length(), str.length() - (pos + delimiter.length() )); + } + else + { + left = str; //default if not found + right = ""; + } + return (pos != std::string::npos); +} +//------------------------------------------------------------------------- +// ySplitStringRight: spit string "str" in two strings "left" and "right" at +// one of the chars in "delimiter" returns true if delimiter found +//------------------------------------------------------------------------- +bool ySplitStringLast(std::string str, std::string delimiter, std::string& left, std::string& right) +{ + unsigned int pos; + if ((pos = str.find_last_of(delimiter)) != std::string::npos) + { + left = str.substr(0, pos); + right = str.substr(pos + 1, str.length() - (pos + 1 )); + } + else + { + left = str; //default if not found + right = ""; + } + return (pos != std::string::npos); +} +//------------------------------------------------------------------------- +// ySplitStringVector: spit string "str" and build vector of strings +//------------------------------------------------------------------------- +CStringArray ySplitStringVector(std::string str, std::string delimiter) +{ + std::string left, right, rest; + bool found; + CStringArray split; + rest = str; + do + { + found = ySplitString(rest, delimiter, left, right); + split.push_back(left); + rest = right; + } + while(found); + return split; +} +//------------------------------------------------------------------------- +// trim whitespaces +//------------------------------------------------------------------------- +std::string trim(std::string const& source, char const* delims) +{ + std::string result(source); + std::string::size_type index = result.find_last_not_of(delims); + if(index != std::string::npos) + result.erase(++index); + + index = result.find_first_not_of(delims); + if(index != std::string::npos) + result.erase(0, index); + else + result.erase(); + return result; +} +//------------------------------------------------------------------------- +// equal-function for case insensitive compare +//------------------------------------------------------------------------- +bool nocase_compare (char c1, char c2) +{ + return toupper(c1) == toupper(c2); +} +//----------------------------------------------------------------------------- +// Decode URLEncoded std::string +//----------------------------------------------------------------------------- +std::string decodeString(std::string encodedString) +{ + const char *string = encodedString.c_str(); + unsigned int count=0; + char hex[3]={'\0'}; + unsigned long iStr; + std::string result = ""; + count = 0; + + while(count +#include +// c++ +#include +#include +#include "ytypes_globals.h" + +//----------------------------------------------------------------------------- +int minmax(int value,int min, int max); +void correctTime(struct tm *zt); + +//----------------------------------------------------------------------------- +// String Converter +//----------------------------------------------------------------------------- +std::string itoa(unsigned int conv); +std::string itoh(unsigned int conv); +std::string decodeString(std::string encodedString); +std::string encodeString(std::string decodedString); +std::string string_tolower(std::string str); + +//----------------------------------------------------------------------------- +// String Helpers +//----------------------------------------------------------------------------- +std::string trim(std::string const& source, char const* delims = " \t\r\n"); +std::string string_printf(const char *fmt, ...); +bool ySplitString(std::string str, std::string delimiter, std::string& left, std::string& right); +bool ySplitStringExact(std::string str, std::string delimiter, std::string& left, std::string& right); +bool ySplitStringLast(std::string str, std::string delimiter, std::string& left, std::string& right); +CStringArray ySplitStringVector(std::string str, std::string delimiter); +bool nocase_compare (char c1, char c2); +std::string timeString(time_t time); +bool write_to_file(std::string filename, std::string content); + +#endif /* __yhttpd_helper_h__ */ diff --git a/src/nhttpd/yhttpd_core/yconnection.cpp b/src/nhttpd/yhttpd_core/yconnection.cpp new file mode 100644 index 000000000..d047563ad --- /dev/null +++ b/src/nhttpd/yhttpd_core/yconnection.cpp @@ -0,0 +1,147 @@ +//============================================================================= +// YHTTPD +// Connection +//============================================================================= + +// system +#include +#include +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ywebserver.h" +#include "yconnection.h" +#include "helper.h" +//============================================================================= +// Initialization of static variables +//============================================================================= +long CWebserverConnection::GConnectionNumber = 0; + +//============================================================================= +// Constructor & Destructor & Initialization +//============================================================================= +CWebserverConnection::CWebserverConnection(CWebserver *pWebserver) +{ + Webserver = pWebserver; + Request.Webserver = pWebserver; + Request.Connection = this; + Response.Webserver = pWebserver; + Response.Connection = this; + Method = M_UNKNOWN; + HttpStatus = 0; + RequestCanceled = false; +#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE + keep_alive = true; +#else + keep_alive = false; +#endif +} +//------------------------------------------------------------------------- +CWebserverConnection::CWebserverConnection() +{ +// aprintf("test CWebserverConnection::CWebserverConnection()\n"); + ConnectionNumber = ++GConnectionNumber; +} +//------------------------------------------------------------------------- +CWebserverConnection::~CWebserverConnection(void) +{ +} +//------------------------------------------------------------------------- +// End The Connection. Request and Response allready handled. +// do "after done" work, like create a www-Log entry. +// Use "Hooks_EndConnection()" Handler to write own Hooks. +//------------------------------------------------------------------------- +void CWebserverConnection::EndConnection() +{ + HookHandler.HookVarList["enlapsed_request"] = itoa(enlapsed_request/1000); + HookHandler.HookVarList["enlapsed_response"] = itoa(enlapsed_response/1000); + HookHandler.Hooks_EndConnection(); // Handle Hooks + if(RequestCanceled) // Canceled + keep_alive = false; + RequestCanceled = true; +// sock->Flush(); +#ifndef Y_CONFIG_FEATURE_KEEP_ALIVE + sock->close(); +#endif +} +//------------------------------------------------------------------------- +// Main +// Handle the Request, Handle (Send) Response), End the Connection +//------------------------------------------------------------------------- +void CWebserverConnection::HandleConnection() +{ + gettimeofday(&tv_connection_start, &tz_connection_start); + + // get the request + if (Request.HandleRequest()) + { + // determine time from Connection creation until now + gettimeofday(&tv_connection_Response_start, &tz_connection_Response_start); + enlapsed_request = ((tv_connection_Response_start.tv_sec - tv_connection_start.tv_sec) * 1000000 + + (tv_connection_Response_start.tv_usec - tv_connection_start.tv_usec)); + + // Keep-Alive checking +#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE + if(string_tolower(Request.HeaderList["Connection"]) == "close" + || (httprotocol != "HTTP/1.1" && string_tolower(Request.HeaderList["Connection"]) != "keep-alive") + || !Webserver->CheckKeepAliveAllowedByIP(sock->get_client_ip())) + keep_alive = false; +#else + keep_alive = false; +#endif + // Send a response + Response.SendResponse(); + + // determine time for SendResponse + gettimeofday(&tv_connection_Response_end, &tz_connection_Response_end); + enlapsed_response = ((tv_connection_Response_end.tv_sec - tv_connection_Response_start.tv_sec) * 1000000 + + (tv_connection_Response_end.tv_usec - tv_connection_Response_start.tv_usec)); + + // print production times + log_level_printf(1,"enlapsed time request:%ld response:%ld url:%s\n", + enlapsed_request, enlapsed_response, (Request.UrlData["fullurl"]).c_str()); + + } + else + { + RequestCanceled = true; + keep_alive = false; // close this connection socket +// dperror("Error while parsing request\n"); + log_level_printf(1,"request canceled: %s\n", strerror(errno)); + } + EndConnection(); +} + +//------------------------------------------------------------------------- +void CWebserverConnection::ShowEnlapsedRequest(char *text) +{ + + long enlapsed = GetEnlapsedRequestTime() / 1000; + log_level_printf(1,"enlapsed-f-start (%s) t:%ld url:%s\n", + text, enlapsed, (Request.UrlData["fullurl"]).c_str()); +} +//------------------------------------------------------------------------- +// Time from creation of socket until now in microseconds! +//------------------------------------------------------------------------- +long CWebserverConnection::GetEnlapsedRequestTime() +{ + struct timeval tv_now; + struct timezone tz_now; + gettimeofday(&tv_now, &tz_now); + return ((tv_now.tv_sec - tv_connection_start.tv_sec) * 1000000 + + (tv_now.tv_usec - tv_connection_start.tv_usec)); +} + +//------------------------------------------------------------------------- +// Time from beginning of response until now in microseconds! +//------------------------------------------------------------------------- +long CWebserverConnection::GetEnlapsedResponseTime() +{ + struct timeval tv_now; + struct timezone tz_now; + gettimeofday(&tv_now, &tz_now); + return ((tv_now.tv_sec - tv_connection_Response_start.tv_sec) * 1000000 + + (tv_now.tv_usec - tv_connection_Response_start.tv_usec)); +} + diff --git a/src/nhttpd/yhttpd_core/yconnection.h b/src/nhttpd/yhttpd_core/yconnection.h new file mode 100644 index 000000000..50dca5580 --- /dev/null +++ b/src/nhttpd/yhttpd_core/yconnection.h @@ -0,0 +1,73 @@ +//============================================================================= +// YHTTPD +// Connection +//----------------------------------------------------------------------------- +// The Connection Class handles a given Socket-Connection for an arriving +// Request. Normally many Connections exists and they are Threaded! +// "HandleConnection()" calls "HandleRequest()" and "SendResponse()" +//============================================================================= +#ifndef __yhttpd_connection_h__ +#define __yhttpd_connection_h__ + +// system +#include +// c++ +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ywebserver.h" +#include "yrequest.h" +#include "yresponse.h" + +// Forward definition +class CWebserver; + +//----------------------------------------------------------------------------- +class CWebserverConnection +{ +public: + class CWebserver *Webserver; // Backreference + CWebserverRequest Request; // http Request + CWebserverResponse Response; // http Response + CyhookHandler HookHandler; // One Instance per Connection + + CySocket *sock; // Connection Socket + bool RequestCanceled; // Canceled flag + static long GConnectionNumber; // Number of Connection. Global counter + long ConnectionNumber; // Number of Connection since Webserver start. Local number + bool keep_alive; // should the response keep-alive socket? + + THttp_Method Method; // http Method (GET, POST,...) + int HttpStatus; // http response code + std::string httprotocol; // HTTP 1.x + + long enlapsed_request, // time for request in usec + enlapsed_response; // time for response in usec + + CWebserverConnection(CWebserver *pWebserver); // Constructor with given Backreferece + CWebserverConnection(); + ~CWebserverConnection(void); + + void HandleConnection(); // Main: Handle the Connecten Live time + + // performance/time measurement + void ShowEnlapsedRequest(char *text); // Print text and time difference to Connection creation + long GetEnlapsedRequestTime(); // Get time in micro-seconds from Connection creation + long GetEnlapsedResponseTime(); // Get time in micro-seconds after Request handles. Start Respnse. + +private: + void EndConnection(); // End Connection Handling + + // performance/time measurement + struct timeval tv_connection_start, + tv_connection_Response_start, + tv_connection_Response_end; + struct timezone tz_connection_start, + tz_connection_Response_start, + tz_connection_Response_end; + + +}; + +#endif /* __yhttpd_connection_h__ */ diff --git a/src/nhttpd/yhttpd_core/yhook.cpp b/src/nhttpd/yhttpd_core/yhook.cpp new file mode 100644 index 000000000..a49ba0222 --- /dev/null +++ b/src/nhttpd/yhttpd_core/yhook.cpp @@ -0,0 +1,357 @@ +//============================================================================= +// YHTTPD +// Hook and HookHandler +//============================================================================= +// C +#include +#include +#include + +// yhttpd +#include "yhook.h" +#include "ylogging.h" +#include "helper.h" + +//============================================================================= +// Initialization of static variables +//============================================================================= +THookList CyhookHandler::HookList; + +//============================================================================= +// Hook Handling +//============================================================================= +//----------------------------------------------------------------------------- +// Hook Dispatcher for Session Hooks +// Execute every Hook in HookList until State change != HANDLED_NONE +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_SendResponse() +{ + log_level_printf(4,"Response Hook-List Start\n"); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { + log_level_printf(4,"Response Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_SendResponse(this); + log_level_printf(4,"Response Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), status); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"Response Hook-List End\n"); + log_level_printf(8,"Response Hook-List Result:\n%s\n", yresult.c_str()); + status = _status; + return _status; +} +//----------------------------------------------------------------------------- +// Hook Dispatcher for Session Hooks +// Execute every Hook in HookList until State change != HANDLED_NONE +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_PrepareResponse() +{ + log_level_printf(4,"PrepareResponse Hook-List Start\n"); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { + log_level_printf(4,"PrepareResponse Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_PrepareResponse(this); + log_level_printf(4,"PrepareResponse Hook-List (%s) End. Status (%d) HTTP Status (%d)\n", ((*i)->getHookName()).c_str(), status, httpStatus); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"PrepareResponse Hook-List End\n"); + log_level_printf(8,"PrepareResponse Hook-List Result:\n%s\n", yresult.c_str()); + status = _status; + return _status; +} + +//----------------------------------------------------------------------------- +// Hook Dispatcher for Server based Hooks +// Execute every Hook in HookList until State change != HANDLED_NONE and +// != HANDLED_CONTINUE +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_ReadConfig(CConfigFile *Config, CStringList &ConfigList) +{ + log_level_printf(4,"ReadConfig Hook-List Start\n"); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { +// log_level_printf(4,"ReadConfig Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_ReadConfig(Config, ConfigList); + log_level_printf(4,"ReadConfig Hook-List (%s) Status (%d)\n", ((*i)->getHookName()).c_str(), _status); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"ReadConfig Hook-List End\n"); + return _status; +} +//----------------------------------------------------------------------------- +// Hook Dispatcher for EndConnection +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_EndConnection() +{ + log_level_printf(4,"EndConnection Hook-List Start\n"); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { + log_level_printf(4,"EndConnection Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_EndConnection(this); + log_level_printf(4,"EndConnection Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"EndConnection Hook-List End\n"); + status = _status; + return _status; +} +//----------------------------------------------------------------------------- +// Hook Dispatcher for UploadSetFilename +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_UploadSetFilename(std::string &Filename) +{ + log_level_printf(4,"UploadSetFilename Hook-List Start. Filename:(%s)\n", Filename.c_str()); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { + log_level_printf(4,"UploadSetFilename Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_UploadSetFilename(this, Filename); + log_level_printf(4,"UploadSetFilename Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"UploadSetFilename Hook-List End\n"); + status = _status; + return _status; +} +//----------------------------------------------------------------------------- +// Hook Dispatcher for UploadSetFilename +//----------------------------------------------------------------------------- +THandleStatus CyhookHandler::Hooks_UploadReady(const std::string& Filename) +{ + log_level_printf(4,"UploadReady Hook-List Start. Filename:(%s)\n", Filename.c_str()); + THandleStatus _status = HANDLED_NONE; + THookList::iterator i = HookList.begin(); + for ( ; i!= HookList.end(); i++ ) + { + log_level_printf(4,"UploadReady Hook-List (%s) Start\n", ((*i)->getHookName()).c_str()); + // response Hook + _status = (*i)->Hook_UploadReady(this, Filename); + log_level_printf(4,"UploadReady Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status); + if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE)) + break; + } + log_level_printf(4,"UploadReady Hook-List End\n"); + status = _status; + return _status; +} + +//============================================================================= +// Output helpers +//============================================================================= +void CyhookHandler::session_init(CStringList _ParamList, CStringList _UrlData, CStringList _HeaderList, + CStringList& _ConfigList, THttp_Method _Method, bool _keep_alive) +{ + ParamList = _ParamList; + UrlData = _UrlData; + HeaderList = _HeaderList; + WebserverConfigList = _ConfigList; + Method = _Method; + status = HANDLED_NONE; + httpStatus = HTTP_OK; + ContentLength = 0; + LastModified = (time_t)-1; + keep_alive = _keep_alive; + HookVarList.clear(); +} + +//============================================================================= +// Build Header +//----------------------------------------------------------------------------- +// RFC 2616 / 6 Response (Header) +// +// The first line of a Response message is the Status-Line, consisting +// of the protocol version followed by a numeric status code and its +// associated textual phrase, with each element separated by SP +// characters. No CR or LF is allowed except in the final CRLF sequence. +// +// Response =Status-Line ; generated by SendHeader +// *(( general-header +// | response-header +// | entity-header ) CRLF) +// CRLF ; generated by SendHeader +// [ message-body ] ; by HOOK Handling Loop or Sendfile +// +// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF ; generated by SendHeader +// +// general-header = Cache-Control ; not implemented +// | Connection ; implemented +// | Date ; implemented +// | Pragma ; not implemented +// | Trailer ; not implemented +// | Transfer-Encoding ; not implemented +// | Upgrade ; not implemented +// | Via ; not implemented +// | Warning ; not implemented +// +// response-header = Accept-Ranges ; not implemented +// | Age ; not implemented +// | ETag ; not implemented +// | Location ; implemented (redirection / Object moved) +// | Proxy-Authenticate ; not implemented +// | Retry-After ; not implemented +// | Server ; implemented +// | Vary ; not implemented +// | WWW-Authenticate ; implemented (by mod_auth and SendHeader) +// +// entity-header = Allow ; not implemented +// | Content-Encoding ; not implemented +// | Content-Language ; not implemented +// | Content-Length ; implemented +// | Content-Location ; not implemented +// | Content-MD5 ; not implemented +// | Content-Range ; not implemented +// | Content-Type ; implemented +// | Expires ; not implemented +// | Last-Modified ; implemented for static files +// | extension-header +// +// extension-header = message-header +//============================================================================= +std::string CyhookHandler::BuildHeader(bool cache) +{ + std::string result=""; + + const char *responseString = ""; + const char *infoString = 0; + + // get Info Index + for (unsigned int i = 0;i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) + if (httpResponseNames[i].type == httpStatus) + { + responseString = httpResponseNames[i].name; + infoString = httpResponseNames[i].info; + break; + } + // print Status-line + result = string_printf(HTTP_PROTOCOL " %d %s\r\nContent-Type: %s\r\n",httpStatus, responseString, ResponseMimeType.c_str()); + log_level_printf(2,"Respose: HTTP/1.1 %d %s\r\nContent-Type: %s\r\n",httpStatus, responseString, ResponseMimeType.c_str()); + + switch (httpStatus) + { + case HTTP_UNAUTHORIZED: + result += "WWW-Authenticate: Basic realm=\""; + result += AUTH_NAME_MSG "\r\n"; + break; + + case HTTP_MOVED_TEMPORARILY: + case HTTP_MOVED_PERMANENTLY: + // Status HTTP_*_TEMPORARILY (redirection) + result += string_printf("Location: %s\r\n",NewURL.c_str()); + // NO break HERE !!! + + default: + time_t timer = time(0); + char timeStr[80]; + // cache + if(!cache && (HookVarList["CacheCategory"]).empty() ) + result += "Cache-Control: no-cache\r\n"; + else + { + time_t x_time = time(NULL); + struct tm *ptm = gmtime(&x_time); + ptm->tm_mday+=1; + x_time = mktime(ptm); + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&x_time)); + result += string_printf("Expires: %s\r\n", timeStr); + } + result += "Server: " WEBSERVERNAME "\r\n"; + // actual date + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer)); + result += string_printf("Date: %s\r\n", timeStr); + // connection type +#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE + if(keep_alive) + result += "Connection: keep-alive\r\n"; + else + result += "Connection: close\r\n"; +#else + result += "Connection: close\r\n"; +#endif + // content-len, last-modified + if(httpStatus == HTTP_NOT_MODIFIED ||httpStatus == HTTP_NOT_FOUND) + result += "Content-Length: 0\r\n"; + else if(GetContentLength() >0) + { + time_t mod_time = time(NULL); + if(LastModified != (time_t)-1) + mod_time = LastModified; + + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&mod_time)); + result += string_printf("Last-Modified: %s\r\nContent-Length: %ld\r\n", timeStr, GetContentLength()); + } + result += "\r\n"; // End of Header + break; + } + + // Body + if (Method != M_HEAD) + switch (httpStatus) + { + case HTTP_OK: + case HTTP_NOT_MODIFIED: + case HTTP_CONTINUE: + case HTTP_ACCEPTED: + case HTTP_NO_CONTENT: + case HTTP_NOT_FOUND: + case HTTP_INTERNAL_SERVER_ERROR: + break; + + case HTTP_MOVED_TEMPORARILY: + case HTTP_MOVED_PERMANENTLY: + result += "Object moved"; + result += string_printf("302 : Object moved.
    If you dont get redirected click here\n",NewURL.c_str()); + break; + + default: + // Error pages + break; + } + return result; +} + +//============================================================================= +// Output helpers +//============================================================================= +//----------------------------------------------------------------------------- +void CyhookHandler::SendHTMLHeader(const std::string& Titel) +{ + WriteLn("\n" + Titel + "\n"); + WriteLn(""); + WriteLn("\n\n\n"); +} +//----------------------------------------------------------------------------- +void CyhookHandler::SendHTMLFooter(void) +{ + WriteLn("\n\n\n"); +} + +//----------------------------------------------------------------------------- +#define OUTBUFSIZE 4096 +void CyhookHandler::printf ( const char *fmt, ... ) +{ + char outbuf[OUTBUFSIZE]; + bzero(outbuf,OUTBUFSIZE); + va_list arglist; + va_start( arglist, fmt ); + vsnprintf( outbuf,OUTBUFSIZE, fmt, arglist ); + va_end(arglist); + Write(outbuf); +} diff --git a/src/nhttpd/yhttpd_core/yhook.h b/src/nhttpd/yhttpd_core/yhook.h new file mode 100644 index 000000000..796031af0 --- /dev/null +++ b/src/nhttpd/yhttpd_core/yhook.h @@ -0,0 +1,187 @@ +//============================================================================= +// YHTTPD +// Hook and HookHandler +//----------------------------------------------------------------------------- +// Extentions for the Webserver can be implemented with Hooks. +// A "Hook"-Class must be inherited from Cyhook and can override the virtual +// functions from Cyhook. +// There are three types of Hooks. +// 1) Server based Hooks (exactly one Instance of Cyhttpd) +// 2) Webserver based Hooks (for each Instance of CWebserver, actually one; not now) +// 3) Connection based Hooks (for each Instance of CWebserverConnection) +//----------------------------------------------------------------------------- +// The CyhookHandler-Class itselfs calls the "attached" hooks. +// Hook-Classes must be attached/de-attached in Cyhttpd-Class attach()/detach(). +// Hook-Class instances should be initialized in yhttpd.cpp. +//----------------------------------------------------------------------------- +// "Hook_SendResponse" +//----------------------------------------------------------------------------- +// "Hook_SendResponse": this is the most important Hook. +// The CyhookHandler implements an abstraction for Request and Response data. +// Every Hook which has implemented Hook_SendResponse could be called in order +// of the "attach" to HookHandler. The control flow, which Hook will be called +// depends on the return value "THandleStatus" from the hook called before. +// For example HANDLED_NONE indicates that the Hook has not handled by the call. +// For example HANDLED_READY indicates that the call is handled and HookHandler +// should finish the Hook-Loop. HANDLED_NOT_IMPLEMENTED indicated that the Hook +// should handle the call, but has not any function to do that. +// Several Hooks can work together (HANDLED_CONTINUE or HANDLED_REWRITE); so the +// next called Hook can work with the output (in yresult) from the before called +// Hook or controls the workflow of following Hooks. +// For example the CmAuth-Hook validates HTTP-Authentication and returns +// HANDLED_CONTINUE if the authentication fits. +// Each Hook_SendResponse is encapsulated from the webserver and should not work +// on Socket-Connections. The Response is written to yresult. There are many +// helper-function for Output and Status-Handling. +// The Input is encapsulated too: ParamList, HeaderList, UrlData, ... +// Look at Response.SendResponse() +//----------------------------------------------------------------------------- +// Other Hooks +//----------------------------------------------------------------------------- +// - Hook_ReadConfig: Every Hook can read OWN Variables from the yhttpd-Configfile +// - Hook_EndConnection: After Response is sent and before Connection Instance +// is deleted +// - Hook_UploadSetFilename: this hook can set the filename for a file to upload +// via POST (before upload) +// - Hook_UploadReady: this Hook is called after uploading a file +//============================================================================= +#ifndef __yhttpd_yhook_h__ +#define __yhttpd_yhook_h__ +// C++ +#include +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ylogging.h" +// tuxbox +#include + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class CyhookHandler; +class Cyhook; + +//----------------------------------------------------------------------------- +// Type definitions for Hooks +//----------------------------------------------------------------------------- +typedef enum +{ + HANDLED_NONE = 0, // Init + HANDLED_READY, // Handled + HANDLED_ABORT, // Abort Connection Fatal + HANDLED_ERROR, // Have Error like missing Parameter + HANDLED_NOT_IMPLEMENTED,// URL should be handled but not implemented + HANDLED_REDIRECTION, // Set new URL and send HTTPD Object Moved + HANDLED_SENDFILE, // Set new URL and Send File + HANDLED_REWRITE, // Set new URL and call Hooks again + HANDLED_CONTINUE, // handled but go on + +} THandleStatus; +typedef std::list THookList; + +//----------------------------------------------------------------------------- +// An abstract Hook-Class. Custom Hook must be inherited. +//----------------------------------------------------------------------------- +class Cyhook +{ +protected: +public: + Cyhook(){}; + virtual ~Cyhook(){}; + // Informations & Control + virtual std::string getHookVersion(void) {return std::string("0.0.0");} + virtual std::string getHookName(void) {return std::string("Abstract Hook Class");} + // CWebserverConnection based hooks + virtual THandleStatus Hook_PrepareResponse(CyhookHandler *hh){return HANDLED_NONE;}; + virtual THandleStatus Hook_SendResponse(CyhookHandler *hh){return HANDLED_NONE;}; + virtual THandleStatus Hook_EndConnection(CyhookHandler *hh){return HANDLED_NONE;} + virtual THandleStatus Hook_UploadSetFilename(CyhookHandler *hh, std::string &Filename){return HANDLED_NONE;} + virtual THandleStatus Hook_UploadReady(CyhookHandler *hh, std::string Filename){return HANDLED_NONE;} + // Cyhttpd based hooks + virtual THandleStatus Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList){return HANDLED_NONE;}; +}; + +//----------------------------------------------------------------------------- +// Hook Handling and Input & Output abstraction +//----------------------------------------------------------------------------- +class CyhookHandler +{ +protected: + static THookList HookList; +public: + // Output + std::string yresult; // content for response output + THandleStatus status; // status of Hook handling + HttpResponseType httpStatus; // http-status code for response + std::string ResponseMimeType; // mime-type for response + std::string NewURL; // new URL for Redirection + long ContentLength; // Length of Response Body + time_t LastModified; // Last Modified Time of Item to send / -1 dynamic content + std::string Sendfile; // Path & Name (local os style) of file to send + bool keep_alive; + + // Input + CStringList ParamList; // local copy of ParamList (Request) + CStringList UrlData; // local copy of UrlData (Request) + CStringList HeaderList; // local copy of HeaderList (Request) + CStringList WebserverConfigList; // Reference (writable) to ConfigList + CStringList HookVarList; // Variables in Hook-Handling passing to other Hooks + THttp_Method Method; // HTTP Method (requested) + // constructor & deconstructor + CyhookHandler(){}; + virtual ~CyhookHandler(){}; + + // hook slot handler + static void attach(Cyhook *yh) // attach a Hook-Class to HookHandler + {HookList.push_back(yh);}; + static void detach(Cyhook *yh) // detach a Hook-Class to HookHandler + {HookList.remove(yh);}; + + // session handling + void session_init(CStringList _ParamList, CStringList _UrlData, CStringList _HeaderList, + CStringList& _ConfigList, THttp_Method _Method, bool _keep_alive); + + // Cyhttpd based hooks + static THandleStatus Hooks_ReadConfig(CConfigFile *Config, CStringList &ConfigList); + // CWebserverConnection based hooks + THandleStatus Hooks_PrepareResponse(); // Hook for Response.SendResonse Dispatching + THandleStatus Hooks_SendResponse(); // Hook for Response.SendResonse Dispatching + THandleStatus Hooks_EndConnection(); + THandleStatus Hooks_UploadSetFilename(std::string &Filename); + THandleStatus Hooks_UploadReady(const std::string& Filename); + + // status handling + void SetHeader(HttpResponseType _httpStatus, std::string _ResponseMimeType) + {httpStatus = _httpStatus; ResponseMimeType = _ResponseMimeType;} + void SetHeader(HttpResponseType _httpStatus, std::string _ResponseMimeType, THandleStatus _status) + {httpStatus = _httpStatus; ResponseMimeType = _ResponseMimeType; status = _status;} + void SetError(HttpResponseType responseType) + {SetHeader(responseType, "text/html");} + void SetError(HttpResponseType responseType, THandleStatus _status) + {SetError(responseType); status = _status;} + // others + long GetContentLength() + {return (status==HANDLED_SENDFILE)?ContentLength : (long)yresult.length();} + // output methods + std::string BuildHeader(bool cache = false); + void addResult(const std::string& result) {yresult += result;} + void addResult(const std::string& result, THandleStatus _status) + {yresult += result; status = _status;} + void printf(const char *fmt, ...); + void Write(const std::string& text) { addResult(text); } + void WriteLn(const std::string& text) { addResult(text+"\r\n"); } + void Write(char const *text) {Write(std::string(text));} + void WriteLn(char const *text) {WriteLn(std::string(text));} + void SendHTMLHeader(const std::string& Titel); + void SendHTMLFooter(void); + void SendOk(void) {Write("ok");} + void SendError(void) {Write("error");} + void SendFile(const std::string& url) {NewURL = url; status = HANDLED_SENDFILE;} + void SendRedirect(const std::string& url) {httpStatus=HTTP_MOVED_TEMPORARILY; NewURL = url; status = HANDLED_REDIRECTION;} + void SendRewrite(const std::string& url){NewURL = url; status = HANDLED_REWRITE;} + friend class CyParser; +}; + +#endif /*__yhttpd_yhook_h__*/ diff --git a/src/nhttpd/yhttpd_core/ylogging.cpp b/src/nhttpd/yhttpd_core/ylogging.cpp new file mode 100644 index 000000000..1a40c87cb --- /dev/null +++ b/src/nhttpd/yhttpd_core/ylogging.cpp @@ -0,0 +1,94 @@ +//============================================================================= +// YHTTPD +// Logging & Debugging +//============================================================================= + +// c +#include +#include +#include + +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ylogging.h" +#include "yconnection.h" +//============================================================================= +// Instance Handling - like Singelton Pattern +//============================================================================= +//----------------------------------------------------------------------------- +// Init as Singelton +//----------------------------------------------------------------------------- +CLogging *CLogging::instance = NULL; + +//----------------------------------------------------------------------------- +// There is only one Instance +//----------------------------------------------------------------------------- +CLogging *CLogging::getInstance(void) +{ + if (!instance) + instance = new CLogging(); + return instance; +} + +//----------------------------------------------------------------------------- +void CLogging::deleteInstance(void) +{ + if (instance) + delete instance; + instance = NULL; +} + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CLogging::CLogging(void) +{ + Debug = false; + LogToFile = false; //not implemented + LogLevel = 0; + Logfile = NULL; + pthread_mutex_init(&Log_mutex, NULL); +} + +//----------------------------------------------------------------------------- +CLogging::~CLogging(void) +{ +} +//============================================================================= + +//----------------------------------------------------------------------------- +void CLogging::setDebug(bool _debug) +{ + Debug = _debug; +} +//----------------------------------------------------------------------------- +bool CLogging::getDebug(void) +{ + return Debug; +} +//============================================================================= +// Logging Calls +// use mutex controlled calls to output resources +// Normal Logging to Stdout, if "Log" is true then Log to file +//============================================================================= +#define bufferlen 1024*8 +//----------------------------------------------------------------------------- +void CLogging::printf ( const char *fmt, ... ) +{ + char buffer[bufferlen]; + + + va_list arglist; + va_start( arglist, fmt ); + if(arglist) + vsnprintf( buffer, bufferlen, fmt, arglist ); + va_end(arglist); + + pthread_mutex_lock( &Log_mutex ); + ::printf(buffer); + if(LogToFile) + ; //FIXME Logging to File + pthread_mutex_unlock( &Log_mutex ); +} + diff --git a/src/nhttpd/yhttpd_core/ylogging.h b/src/nhttpd/yhttpd_core/ylogging.h new file mode 100644 index 000000000..d5a461bfd --- /dev/null +++ b/src/nhttpd/yhttpd_core/ylogging.h @@ -0,0 +1,86 @@ +//============================================================================= +// YHTTPD +// Logging & Debugging +//============================================================================= +#ifndef __yhttpd_logging_h__ +#define __yhttpd_logging_h__ + +// system +#include +// yhttpd +#include "yconfig.h" + +// forward declaration +class CWebserverConnection; + +//----------------------------------------------------------------------------- +// Log Level +//----------------------------------------------------------------------------- +// 1 : Connection Information (called URL) +// 2 : Parameters?? +// 3 : yParser Main Infos +// 5 : ycmd: Every yParser Loop Command expansion +// 6 : yresult: Every yParser Loop Command expansion +// 8 : Central Functions Detailed +// 9 : Socket Operations +//----------------------------------------------------------------------------- +class CLogging +{ + protected: + pthread_mutex_t Log_mutex; + FILE *Logfile; + bool Debug; + bool LogToFile; + + static CLogging *instance; + + CLogging(void); + ~CLogging(void); + + public: + int LogLevel; + + // Instance Handling + static CLogging *getInstance(void); + static void deleteInstance(void); + + void setDebug(bool _debug); + bool getDebug(void); + + // Logging + void printf(const char *fmt, ...); +}; + +//----------------------------------------------------------------------------- +// definitions for easy use +//----------------------------------------------------------------------------- + +// print always +#define aprintf(fmt, args...) \ + do { CLogging::getInstance()->printf("[yhttpd] " fmt, ## args); } while (0) + +// print show file and linenumber +#define log_printfX(fmt, args...) \ + do { CLogging::getInstance()->printf("[yhttpd(%s:%d)] " fmt, __FILE__, __LINE__, ## args); } while (0) + +//Set Watch Point (show file and linenumber and function) +#define WP() \ + do { CLogging::getInstance()->printf("[yhttpd(%s:%d)%s]\n", __FILE__, __LINE__, __FUNCTION__); } while (0) + +// print if level matches +#define log_level_printf(level, fmt, args...) \ + do { if(CLogging::getInstance()->LogLevel>=level) CLogging::getInstance()->printf("[yhttpd#%d] " fmt, level, ## args); } while (0) + +// print if level matches / show file and linenumber +#define log_level_printfX(level, fmt, args...) \ + do { if(CLogging::getInstance()->LogLevel>=level) CLogging::getInstance()->printf("[yhttpd#%d(%s:%d)] " fmt, level, __FILE__, __LINE__, ## args); } while (0) + +// print only if debug is on +#define dprintf(fmt, args...) \ + do { if(CLogging::getInstance()->getDebug())CLogging::getInstance()->printf("[yhttpd] " fmt, ## args); } while (0) + +// print string to stdandard error +#define dperror(str) \ + do { perror("[yhttpd] " str); } while (0) + +#endif /* __yttpd_logging_h__ */ diff --git a/src/nhttpd/yhttpd_core/yrequest.cpp b/src/nhttpd/yhttpd_core/yrequest.cpp new file mode 100644 index 000000000..71a067cd9 --- /dev/null +++ b/src/nhttpd/yhttpd_core/yrequest.cpp @@ -0,0 +1,601 @@ +//============================================================================= +// YHTTPD +// Request +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Request-Data +// UrlData (CStringList) +// startline First line of http-request (GET HTTP/1.x) +// fullurl ::= ? +// paramstring Is provided by String-Array ParameterList +// url ::= / "/" is last "/" in string +// path begin with /... +// filename ::= . +// filenamepure +// fileext Extension like html, jpg, ... +//============================================================================= + +// c++ +#include +#include +#include +#include +#include +// system +#include +#include + +// yhttpd +#include "yrequest.h" +#include "yconnection.h" +#include "helper.h" + +//============================================================================= +// Constructor & Destructor +//============================================================================= +CWebserverRequest::CWebserverRequest(CWebserver *pWebserver) +{ + Webserver = pWebserver; + CWebserverRequest(); +} + +//============================================================================= +// Parsing Request +//============================================================================= +//----------------------------------------------------------------------------- +// Main Request Parsing +// RFC2616 +// generic-message = start-line +// *(message-header CRLF) +// CRLF +// [ message-body ] +// start-line = Request-Line | Status-Line +//----------------------------------------------------------------------------- +bool CWebserverRequest::HandleRequest(void) +{ + std::string start_line = ""; + // read first line + do + { + start_line = Connection->sock->ReceiveLine(); + if(!Connection->sock->isValid) + return false; + if(start_line == "") // Socket empty + { + log_level_printf(1,"HandleRequest: End of line not found\n"); + Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR); + Connection->RequestCanceled = true; + return false; + } + } + while(start_line == "\r\n"); // ignore empty lines at begin on start-line + + start_line = trim(start_line); + log_level_printf(1,"Request: %s\n", start_line.c_str() ); + UrlData["startline"] = start_line; + if(!ParseStartLine(start_line)) + return false; + + + if(Connection->Method == M_GET || Connection->Method == M_HEAD) + { + std::string tmp_line; + //read header (speed up: read rest of request in blockmode) + tmp_line = Connection->sock->ReceiveBlock(); + if(!Connection->sock->isValid) + { + Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR); + return false; + } + + if(tmp_line == "") + { + Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR); + return false; + } + ParseHeader(tmp_line); + } + // Other Methods + if(Connection->Method == M_DELETE || Connection->Method == M_PUT || Connection->Method == M_TRACE) + { + //todo: implement + aprintf("HTTP Method not implemented :%d\n",Connection->Method); + Connection->Response.SendError(HTTP_NOT_IMPLEMENTED); + return false; + } + // handle POST (read header & body) + if(Connection->Method == M_POST) + { + Connection->Response.Write("HTTP/1.1 100 Continue\r\n\r\n"); // POST Requests requires CONTINUE in HTTP/1.1 + return HandlePost(); + } + // if you are here, something went wrong + + return true; +} + +//----------------------------------------------------------------------------- +// Parse the start-line +// from RFC2616 / 5.1 Request-Line (start-line): +// Request-Line = Method SP Request-URI SP HTTP-Version CRLF (SP=Space) +// +// Determine Reqest-Method, URL, HTTP-Version and Split Parameters +// Split URL into path, filename, fileext .. UrlData[] +//----------------------------------------------------------------------------- +bool CWebserverRequest::ParseStartLine(std::string start_line) +{ + std::string method,url,http,tmp; + + log_level_printf(8,": line: %s\n", start_line.c_str() ); + if(ySplitString(start_line," ",method,tmp)) + { + if(ySplitStringLast(tmp," ",url,Connection->httprotocol)) + { + analyzeURL(url); + UrlData["httprotocol"] = Connection->httprotocol; + // determine http Method + if(method.compare("POST") == 0) Connection->Method = M_POST; + else if(method.compare("GET") == 0) Connection->Method = M_GET; + else if(method.compare("PUT") == 0) Connection->Method = M_PUT; + else if(method.compare("HEAD") == 0) Connection->Method = M_HEAD; + else if(method.compare("PUT") == 0) Connection->Method = M_PUT; + else if(method.compare("DELETE") == 0) Connection->Method = M_DELETE; + else if(method.compare("TRACE") == 0) Connection->Method = M_TRACE; + else + { + log_level_printf(1,"Unknown Method or invalid request\n"); + Connection->Response.SendError(HTTP_NOT_IMPLEMENTED); + log_level_printf(3,"Request: '%s'\n",rawbuffer.c_str()); + return false; + } + log_level_printf(3,"Request: FullURL: %s\n",UrlData["fullurl"].c_str()); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// parse parameter string +// parameter = attribute "=" value +// attribute = token +// value = token | quoted-string +// +// If parameter attribute is multiple times given, the values are stored like this: +// =,,.., +//----------------------------------------------------------------------------- +bool CWebserverRequest::ParseParams(std::string param_string) +{ + bool ende = false; + std::string param, name="", value, number; + + while(!ende) + { + if(!ySplitStringExact(param_string,"&",param,param_string)) + ende = true; + if(ySplitStringExact(param,"=",name,value)) + { + value = trim(value); + if(ParameterList[name].empty()) + ParameterList[name] = value; + else + { + ParameterList[name] += ","; + ParameterList[name] += value; + } + } + number = string_printf("%d", ParameterList.size()+1); + log_level_printf(7,"ParseParams: name: %s value: %s\n",name.c_str(), value.c_str()); + ParameterList[number] = name; + } + return true; +} + +//----------------------------------------------------------------------------- +// parse the header of the request +// from RFC 2616 / 4.2 Message Header: +// message-header = field-name ":" [ field-value ] +// field-name = token +// field-value = *( field-content | LWS ) +// field-content = +//----------------------------------------------------------------------------- +bool CWebserverRequest::ParseHeader(std::string header) +{ + bool ende = false; + std::string sheader, name, value; + HeaderList.clear(); + + while(!ende) + { + if(!ySplitStringExact(header,"\r\n",sheader,header)) + ende = true; + if(ySplitStringExact(sheader,":",name,value)) + HeaderList[name] = trim(value); + log_level_printf(8,"ParseHeader: name: %s value: %s\n",name.c_str(), value.c_str()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Analyze URL +// Extract Filename, Path, FileExt +// form RFC2616 / 3.2.2: +// http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]] +// query data is splitted and stored in ParameterList +//----------------------------------------------------------------------------- +void CWebserverRequest::analyzeURL(std::string url) +{ + ParameterList.clear(); + // URI decode + url = decodeString(url); + url = trim(url, "\r\n"); // non-HTTP-Standard: allow \r or \n in URL. Delete it. + UrlData["fullurl"] = url; + // split Params + if(ySplitString(url,"?",UrlData["url"],UrlData["paramstring"])) // split pure URL and all Params + ParseParams(UrlData["paramstring"]); // split params to ParameterList + else // No Params + UrlData["url"] = url; + + if(!ySplitStringLast(UrlData["url"],"/",UrlData["path"],UrlData["filename"])) + { + UrlData["path"] = "/"; // Set "/" if not contained + } + else + UrlData["path"] += "/"; + if(( UrlData["url"].length() == 1) || (UrlData["url"][UrlData["url"].length()-1] == '/' )) + { // if "/" at end use index.html + UrlData["path"] = UrlData["url"]; + UrlData["filename"] = "index.html"; + } + ySplitStringLast(UrlData["filename"],".",UrlData["filenamepure"],UrlData["fileext"]); +} + +//----------------------------------------------------------------------------- +// Handle Post (Form and ONE file upload) +//----------------------------------------------------------------------------- +bool CWebserverRequest::HandlePost() +{ + //read header: line by line + std::string raw_header, tmp_line; + do + { + tmp_line = Connection->sock->ReceiveLine(); + if(tmp_line == "") // Socket empty + { + log_level_printf(1,"HandleRequest: (Header) End of line not found: %s\n", strerror(errno)); + Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR); + return false; + } + raw_header.append(tmp_line); + } + while(tmp_line != "\r\n"); // header ends with first empty line + ParseHeader(raw_header); + + // read meesage body + unsigned int content_len = 0; + if(HeaderList["Content-Length"] != "") + content_len = atoi( HeaderList["Content-Length"].c_str() ); + + // Get Rest of Request from Socket + log_level_printf(2,"Connection->Method Post !\n"); + log_level_printf(9,"Conntent Type:%s\n",(HeaderList["Content-Type"]).c_str()); + log_level_printf(8,"Post Content-Length:%d as string:(%s)\n", content_len, HeaderList["Content-Length"].c_str()); + + static const std::string t = "multipart/form-data; boundary="; + if(HeaderList["Content-Type"].compare(0,t.length(),t) == 0) // this a a multpart POST, normallly: file upload + { +#ifdef Y_CONFIG_FEATURE_UPLOAD + std::string boundary = "--" + HeaderList["Content-Type"].substr(t.length(),HeaderList["Content-Type"].length() - t.length()); + std::string post_header; + do + { + content_len = HandlePostBoundary(boundary, content_len); + } + while(content_len > 0); +#else + Connection->Response.SendError(HTTP_NOT_IMPLEMENTED); + return false; +#endif + } + else if(HeaderList["Content-Type"].compare("application/x-www-form-urlencoded") == 0) //this is a normal POST with form-data (no upload) + { + // handle normal form POST + log_level_printf(6,"Handle POST application/x-www-form-urlencoded\n"); + std::string post_header; + // get message-body + post_header = Connection->sock->ReceiveBlock(); + if(post_header.length() < content_len) + { + aprintf("POST form less data then expected\n"); + Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR); + return false; + } + // parse the params in post_header (message-body) an add them to ParameterList + ParseParams(post_header); + } + return true; +} +//----------------------------------------------------------------------------- +// POST multipart ! FILE UPLOAD! +// +// No 'Content-type: multipart/mixed' now supported +// designed for recursion for different boundaries. +// +// from RFC 1867: +// 2. HTML forms with file submission +// +// The current HTML specification defines eight possible values for the +// attribute TYPE of an INPUT element: CHECKBOX, HIDDEN, IMAGE, +// PASSWORD, RADIO, RESET, SUBMIT, TEXT. +// +// In addition, it defines the default ENCTYPE attribute of the FORM +// element using the POST METHOD to have the default value +// "application/x-www-form-urlencoded" +// +// 6. Examples +// +// Suppose the server supplies the following HTML: +// +//
    +// What is your name? +// What files are you sending? +//
    +// +// and the user types "Joe Blow" in the name field, and selects a text +// file "file1.txt" for the answer to 'What files are you sending?' +// +// The client might send back the following data: +// +// Content-type: multipart/form-data, boundary=AaB03x +// +// --AaB03x +// content-disposition: form-data; name="field1" +// +// Joe Blow +// --AaB03x +// content-disposition: form-data; name="pics"; filename="file1.txt" +// Content-Type: text/plain +// +// ... contents of file1.txt ... +// --AaB03x-- +// +// 7. Registration of multipart/form-data +// +// The media-type multipart/form-data follows the rules of all multipart +// MIME data streams as outlined in RFC 1521. It is intended for use in +// returning the data that comes about from filling out a form. In a +// form (in HTML, although other applications may also use forms), there +// are a series of fields to be supplied by the user who fills out the +// form. Each field has a name. Within a given form, the names are +// unique. +// +// multipart/form-data contains a series of parts. Each part is expected +// to contain a content-disposition header where the value is "form- +// data" and a name attribute specifies the field name within the form, +// e.g., 'content-disposition: form-data; name="xxxxx"', where xxxxx is +// the field name corresponding to that field. Field names originally in +// non-ASCII character sets may be encoded using the method outlined in +// RFC 1522. +// +// As with all multipart MIME types, each part has an optional Content- +// Type which defaults to text/plain. If the contents of a file are +// returned via filling out a form, then the file input is identified as +// application/octet-stream or the appropriate media type, if known. If +// multiple files are to be returned as the result of a single form +// entry, they can be returned as multipart/mixed embedded within the +// multipart/form-data. +// +// Each part may be encoded and the "content-transfer-encoding" header +// supplied if the value of that part does not conform to the default +// encoding. +// +// File inputs may also identify the file name. The file name may be +// described using the 'filename' parameter of the "content-disposition" +// header. This is not required, but is strongly recommended in any case +// where the original filename is known. This is useful or necessary in +// many applications. +//----------------------------------------------------------------------------- +unsigned int CWebserverRequest::HandlePostBoundary(std::string boundary, unsigned int content_len) +{ + std::string tmp_line; + + // read boundary + tmp_line = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + + log_level_printf(2," Start\n"); + if(tmp_line.find(boundary) != std::string::npos) + { + // is it the boudary end? + if(tmp_line.find(boundary+"--") != std::string::npos) + { + log_level_printf(7," Boundary END found\n"); + return 0; + } + log_level_printf(7," Boundary START found\n"); + + // read content-disposition: ... + tmp_line = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + if(tmp_line.find("Content-Disposition:") == std::string::npos) + { + log_level_printf(7," no content-disposition found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + if(tmp_line.find("filename") != std::string::npos) + { +#ifdef Y_CONFIG_FEATURE_UPLOAD + // this part is a file + log_level_printf(2," disposition !!this is a file!! found. line:(%s)\n", tmp_line.c_str()); + // get para from 'content-disposition: form-data; name="pics"; filename="file1.txt"' + // set to ParameterList[""]="" + std::string left, right, var_name, var_value; + if(!ySplitStringExact(tmp_line, "name=\"", left, right)) + { + log_level_printf(7," no var_name START found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + if(!ySplitStringExact(right, "\"", var_name, right)) + { + log_level_printf(7," no var_name END found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + if(!ySplitStringExact(right, "filename=\"", left, right)) + { + log_level_printf(7," no filename START found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + if(!ySplitStringExact(right, "\"", var_value, right)) + { + log_level_printf(7," no filename END found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + var_value = trim(var_value); + ParameterList[var_name] = var_value; + log_level_printf(7," filename found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str()); + + //read 'Content-Type: ' + tmp_line = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + // Get Content-Type: put it to ParameterList["_mime"]="" + if(!ySplitStringExact(tmp_line, "Content-Type:", left, right)) + { + log_level_printf(7," no Content-Type found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + var_value = trim(right); + ParameterList[var_name+"_mime"] = var_value; + log_level_printf(7," Content-Type found. name:(%s_mime) value:(%s)\n", var_name.c_str(), var_value.c_str()); + + + //read empty line as separator + tmp_line = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + if(tmp_line != "\r\n") + { + log_level_printf(7," no empty line found. line:(%s)\n", tmp_line.c_str()); + return 0; + + } + log_level_printf(7," read file Start\n"); + + std::string upload_filename; + upload_filename = UPLOAD_TMP_FILE; + // Hook for Filename naming + Connection->HookHandler.Hooks_UploadSetFilename(upload_filename); + // Set upload filename to ParameterList["_upload_filename"]="" + ParameterList[var_name+"_upload_filename"] = upload_filename; + + // open file for write + int fd = open(upload_filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC); + if (fd<=0) + { + aprintf("cannot open file %s: ", upload_filename.c_str()); + dperror(""); + return 0; + } + + // ASSUMPTION: the complete multipart has no more then SEARCH_BOUNDARY_LEN bytes after the file. + // It only works, if no multipart/mixed is used (e.g. in file attachments). Not nessesary in embedded systems. + // To speed up uploading, read content_len - SEARCH_BOUNDARY_LEN bytes in blockmode. + // To save memory, write them direct into the file. + #define SEARCH_BOUNDARY_LEN 2*RECEIVE_BLOCK_LEN // >= RECEIVE_BLOCK_LEN in ySocket + unsigned int _readbytes = 0; + if((int)content_len - SEARCH_BOUNDARY_LEN >0) + { + _readbytes = Connection->sock->ReceiveFileGivenLength(fd, content_len - SEARCH_BOUNDARY_LEN); + content_len -= _readbytes; + log_level_printf(8," read block (already:%d all:%d)\n", _readbytes, content_len); + } + + // read rest of file and check for boundary end + _readbytes = 0; + bool is_CRLF = false; + + bool found_end_boundary = false; + do + { + // read line by line + tmp_line = Connection->sock->ReceiveLine(); + _readbytes += tmp_line.length(); + + // is this line a boundary? + if(tmp_line.find(boundary) != std::string::npos) + { + if(tmp_line.find(boundary+"--") != std::string::npos) + found_end_boundary = true; // it is the end! of POST request! + break; // boundary found. end of file. + } + else // no Boundary: write CRFL if found in last line + { + if(is_CRLF) + if ((unsigned int)write(fd, "\r\n", 2) != 2) + { + perror("write file failed\n"); + return 0; + } + } + // normal line: write it to file + // CRLF at end? Maybe CRLF before boundary. Can not decide yet + is_CRLF = (tmp_line.length()>=2 && tmp_line[tmp_line.length()-2]=='\r' && tmp_line[tmp_line.length()-1]=='\n'); + int write_len = is_CRLF ? tmp_line.length()-2 : tmp_line.length(); + if (write(fd, tmp_line.c_str(), write_len) != write_len) + { + perror("write file failed\n"); + return 0; + } + log_level_printf(2," read file (already:%d all:%d)\n", _readbytes, content_len); + } + while((_readbytes < content_len) && (tmp_line.length() != 0)); + content_len -= _readbytes; + close(fd); + log_level_printf(2," read file End\n"); + if(found_end_boundary) // upload ok? + { + + Connection->HookHandler.Hooks_UploadReady(upload_filename); + return 0; + } +#endif // Y_CONFIG_FEATURE_UPLOAD + } + else + // this part is a POST variable/parameter + { + // get var_name from 'content-disposition: form-data; name="var_name"' + std::string left, right, var_name, var_value; + if(!ySplitStringExact(tmp_line, "name=\"", left, right)) + { + log_level_printf(7," no var_name START found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + if(!ySplitStringExact(right, "\"", var_name, right)) + { + log_level_printf(7," no var_name END found. line:(%s)\n", tmp_line.c_str()); + return 0; + } + + //read empty line as separator + tmp_line = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + if(tmp_line != "\r\n") + { + log_level_printf(7," no empty line found. line:(%s)\n", tmp_line.c_str()); + return 0; + + } + //read var_value line + // ASSUMPTION!!!! Only one Line for value, new line is a boundary again + // ATTENTION!! var_name must not be unique. So Parameters are store by number too. + var_value = Connection->sock->ReceiveLine(); + content_len -= tmp_line.length(); + var_value = trim(var_value); + ParameterList[var_name] = var_value; + log_level_printf(7," Parameter found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str()); + } + } + return content_len; +} diff --git a/src/nhttpd/yhttpd_core/yrequest.h b/src/nhttpd/yhttpd_core/yrequest.h new file mode 100644 index 000000000..65b670ab3 --- /dev/null +++ b/src/nhttpd/yhttpd_core/yrequest.h @@ -0,0 +1,52 @@ +//============================================================================= +// YHTTPD +// Request +//============================================================================= + +#ifndef __yhttpd_request_h__ +#define __yhttpd_request_h__ + +// C++ +#include +#include + +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" + +// forward declaration +class CWebserver; +class CWebserverConnection; + +//----------------------------------------------------------------------------- +class CWebserverRequest +{ +private: + bool HandlePost(); + unsigned int HandlePostBoundary(std::string boundary, unsigned int content_len); + bool ParseStartLine(std::string start_line); + bool ParseParams(std::string param_string); + bool ParseHeader(std::string header); + +protected: + std::string rawbuffer; + + // request control +public: + class CWebserver *Webserver; + class CWebserverConnection *Connection; + + // Request Data + CStringList ParameterList; + CStringList HeaderList; + CStringList UrlData; // url, fullurl, path, filename, fileext, paramstring + void analyzeURL(std::string); + + // constructor & destructor + CWebserverRequest() {}; + CWebserverRequest(CWebserver *pWebserver); + // Handler + bool HandleRequest(void); +}; + +#endif /* __yhttpd_request_h__ */ diff --git a/src/nhttpd/yhttpd_core/yresponse.cpp b/src/nhttpd/yhttpd_core/yresponse.cpp new file mode 100644 index 000000000..1a3f8c9cf --- /dev/null +++ b/src/nhttpd/yhttpd_core/yresponse.cpp @@ -0,0 +1,251 @@ +//============================================================================= +// YHTTPD +// Response +//============================================================================= + +// c +#include +#include +#include +// c++ +#include +// system +#include +#include +// yhttpd +#include "yconfig.h" +#include "yhttpd.h" +#include "ytypes_globals.h" +#include "ylogging.h" +#include "ywebserver.h" +#include "yconnection.h" +#include "helper.h" +#include "yhook.h" + +#ifdef Y_CONFIG_HAVE_SENDFILE +#include +#endif + +//============================================================================= +// Constructor & Destructor +//============================================================================= +CWebserverResponse::CWebserverResponse(CWebserver *pWebserver) +{ + Webserver = pWebserver; + CWebserverResponse(); +} +//----------------------------------------------------------------------------- +CWebserverResponse::CWebserverResponse() +{ +} + +//============================================================================= +// Main Dispacher for Response +// To understand HOOKS reade yhook.cpp Comments!!! +//----------------------------------------------------------------------------- +// RFC 2616 / 6 Response +// +// After receiving and interpreting a request message, a server responds +// with an HTTP response message. +// Response =Status-Line ; generated by SendHeader +// *(( general-header ; generated by SendHeader +// | response-header ; generated by SendHeader +// | entity-header ) CRLF); generated by SendHeader +// CRLF ; generated by SendHeader +// [ message-body ] ; by HOOK Handling Loop or Sendfile +//============================================================================= +bool CWebserverResponse::SendResponse() +{ + // Init Hookhandler + Connection->HookHandler.session_init(Connection->Request.ParameterList, Connection->Request.UrlData, + (Connection->Request.HeaderList), (Cyhttpd::ConfigList), Connection->Method, Connection->keep_alive); + //-------------------------------------------------------------- + // HOOK Handling Loop [ PREPARE response hook ] + // Checking and Preperation: Auth, static, cache, ... + //-------------------------------------------------------------- + +// move to mod_sendfile ??? +#ifdef Y_CONFIG_USE_HOSTEDWEB + // for hosted webs: rewrite URL + std::string _hosted="/hosted/"; + if((Connection->Request.UrlData["path"]).compare(0,_hosted.length(),"/hosted/") == 0) // hosted Web ? + Connection->Request.UrlData["path"]=Cyhttpd::ConfigList["HostedDocumentRoot"] + +(Connection->Request.UrlData["path"]).substr(_hosted.length()-1); +#endif //Y_CONFIG_USE_HOSTEDWEB + + do + { + if(Connection->RequestCanceled) + return false; + + Connection->HookHandler.Hooks_PrepareResponse(); + if(Connection->HookHandler.status == HANDLED_ERROR || Connection->HookHandler.status == HANDLED_ABORT) + { + log_level_printf(2,"Response Prepare Hook found but Error\n"); + Write(Connection->HookHandler.BuildHeader()); + Write(Connection->HookHandler.yresult); + return false; + } + // URL has new value. Analyze new URL for SendFile + else if(Connection->HookHandler.status == HANDLED_SENDFILE || + Connection->HookHandler.status == HANDLED_REWRITE) + { + Connection->Request.analyzeURL(Connection->HookHandler.NewURL); + Connection->HookHandler.UrlData = Connection->Request.UrlData; + } + if(Connection->HookHandler.status == HANDLED_REDIRECTION) + { + Write(Connection->HookHandler.BuildHeader()); + return false; + } + } + while(Connection->HookHandler.status == HANDLED_REWRITE); + + // Prepare = NOT_MODIFIED ? + if(Connection->HookHandler.httpStatus == HTTP_NOT_MODIFIED) + { + Write(Connection->HookHandler.BuildHeader()); + return true; + } + + //-------------------------------------------------------------- + // HOOK Handling Loop [ response hook ] + // Production + //-------------------------------------------------------------- + if(Connection->HookHandler.status != HANDLED_SENDFILE) + do + { + if(Connection->RequestCanceled) + return false; + + Connection->HookHandler.Hooks_SendResponse(); + if((Connection->HookHandler.status == HANDLED_READY)||(Connection->HookHandler.status == HANDLED_CONTINUE)) + { + log_level_printf(2,"Response Hook Output. Status:%d\n", Connection->HookHandler.status); + Write(Connection->HookHandler.BuildHeader()); + if(Connection->Method != M_HEAD) + Write(Connection->HookHandler.yresult); + if(Connection->HookHandler.status != HANDLED_CONTINUE) + return true; + } + else if(Connection->HookHandler.status == HANDLED_ERROR) + { + log_level_printf(2,"Response Hook found but Error\n"); + Write(Connection->HookHandler.BuildHeader()); + if(Connection->Method != M_HEAD) + Write(Connection->HookHandler.yresult); + return false; + } + else if(Connection->HookHandler.status == HANDLED_ABORT) + return false; + // URL has new value. Analyze new URL for SendFile + else if(Connection->HookHandler.status == HANDLED_SENDFILE || + Connection->HookHandler.status == HANDLED_REWRITE) + { + Connection->Request.analyzeURL(Connection->HookHandler.NewURL); + Connection->HookHandler.UrlData = Connection->Request.UrlData; + } + if(Connection->HookHandler.status == HANDLED_REDIRECTION) + { + Write(Connection->HookHandler.BuildHeader()); + return false; + } + } + while(Connection->HookHandler.status == HANDLED_REWRITE); + + // Send static file + if(Connection->HookHandler.status == HANDLED_SENDFILE && !Connection->RequestCanceled) + { + bool cache = true; +// if(Connection->HookHandler.UrlData["path"] == "/tmp/")//TODO: un-cachable dirs +// cache = false; + Write(Connection->HookHandler.BuildHeader(cache)); + if(Connection->Method != M_HEAD) + Sendfile(Connection->Request.UrlData["url"]); + return true; + } + + // arrived here? = error! + SendError(HTTP_NOT_FOUND); + return false; +} + +//============================================================================= +// Output +//============================================================================= +void CWebserverResponse::SendHeader(HttpResponseType responseType, bool cache, std::string ContentType) +{ + Connection->HookHandler.SetHeader(responseType, ContentType); + Write(Connection->HookHandler.BuildHeader(cache)); +} + +//----------------------------------------------------------------------------- +// BASIC Send over Socket for Strings (char*) +//----------------------------------------------------------------------------- +bool CWebserverResponse::WriteData(char const * data, long length) +{ + if(Connection->RequestCanceled) + return false; + if(Connection->sock->Send(data, length) == -1) + { + log_level_printf(1,"response canceled: %s\n", strerror(errno)); + Connection->RequestCanceled = true; + return false; + } + else + return true; +} +//----------------------------------------------------------------------------- +#define bufferlen 4*1024 +void CWebserverResponse::printf ( const char *fmt, ... ) +{ + char buffer[bufferlen]; + va_list arglist; + va_start( arglist, fmt ); + vsnprintf( buffer, bufferlen, fmt, arglist ); + va_end(arglist); + Write(buffer); +} +//----------------------------------------------------------------------------- +bool CWebserverResponse::Write(char const *text) +{ + return WriteData(text, strlen(text)); +} +//----------------------------------------------------------------------------- +bool CWebserverResponse::WriteLn(char const *text) +{ + if(!WriteData(text, strlen(text))) + return false; + return WriteData("\r\n",2); +} + +//----------------------------------------------------------------------------- +bool CWebserverResponse::Sendfile(std::string filename) +{ + if(Connection->RequestCanceled) + return false; + int filed = open( filename.c_str(), O_RDONLY ); + if(filed != -1 ) //can access file? + { + if(!Connection->sock->SendFile(filed)) + Connection->RequestCanceled = true; + close(filed); + } + return (filed != -1 ); +} + +//----------------------------------------------------------------------------- +// Send File: Determine MIME-Type fro File-Extention +//----------------------------------------------------------------------------- +std::string CWebserverResponse::GetContentType(std::string ext) +{ + std::string ctype = "text/plain"; + ext = string_tolower(ext); + for (unsigned int i = 0;i < (sizeof(MimeFileExtensions)/sizeof(MimeFileExtensions[0])); i++) + if (MimeFileExtensions[i].fileext == ext) + { + ctype = MimeFileExtensions[i].mime; + break; + } + return ctype; +} diff --git a/src/nhttpd/yhttpd_core/yresponse.h b/src/nhttpd/yhttpd_core/yresponse.h new file mode 100644 index 000000000..6bdaeab1b --- /dev/null +++ b/src/nhttpd/yhttpd_core/yresponse.h @@ -0,0 +1,56 @@ +//============================================================================= +// YHTTPD +// Response +//============================================================================= + +#ifndef __yhttpd_response_h__ +#define __yhttpd_response_h__ + +// c++ +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "yhook.h" + +// forward declaration +class CWebserver; +class CWebserverConnection; + +//----------------------------------------------------------------------------- +class CWebserverResponse +{ +private: + +protected: + bool WriteData(char const *data, long length); + bool Sendfile(std::string filename); + std::string redirectURI; // URI for redirection else: empty + +public: + class CWebserver *Webserver; + class CWebserverConnection *Connection; + + // con/destructors + CWebserverResponse(); + CWebserverResponse(CWebserver *pWebserver); + + // response control + bool SendResponse(void); + + // output methods + void printf(const char *fmt, ...); + bool Write(char const *text); + bool WriteLn(char const *text); + bool Write(const std::string text) { return Write(text.c_str()); } + bool WriteLn(const std::string text) { return WriteLn(text.c_str()); } + + // Headers + void SendError(HttpResponseType responseType) {SendHeader(responseType, false, "text/html");} + void SendHeader(HttpResponseType responseType, bool cache=false, std::string ContentType="text/html"); + + // Helpers + std::string GetContentType(std::string ext); +}; + +#endif /* __yhttpd_response_h__ */ diff --git a/src/nhttpd/yhttpd_core/ysocket.cpp b/src/nhttpd/yhttpd_core/ysocket.cpp new file mode 100644 index 000000000..3ff1aa675 --- /dev/null +++ b/src/nhttpd/yhttpd_core/ysocket.cpp @@ -0,0 +1,504 @@ +//============================================================================= +// YHTTPD +// Socket Class : Basic Socket Operations +//============================================================================= + +#include + +// system +#include +#include +#include +#include +#include +#include +#include +// yhttpd +#include "yhttpd.h" +#include "ysocket.h" +#include "ylogging.h" +// system +#ifdef Y_CONFIG_HAVE_SENDFILE +#include +#endif +#ifdef Y_CONFIG_USE_OPEN_SSL +#include +#include +#include +#endif + +//============================================================================= +// Initialization of static variables +//============================================================================= +#ifdef Y_CONFIG_USE_OPEN_SSL +SSL_CTX *CySocket::SSL_ctx; +std::string CySocket::SSL_pemfile; +std::string CySocket::SSL_CA_file; +#endif + +//============================================================================= +// Constructor & Destructor & Initialization +//============================================================================= +CySocket::CySocket() + : sock(0) +{ +#ifdef Y_CONFIG_USE_OPEN_SSL + ssl = NULL; +#endif + tv_start_waiting.tv_sec = 0; + tv_start_waiting.tv_usec = 0; + BytesSend =0; + sock = socket(AF_INET,SOCK_STREAM,0); + init(); +} +//----------------------------------------------------------------------------- +CySocket::~CySocket() +{ +#ifdef Y_CONFIG_USE_OPEN_SSL + if(isSSLSocket && ssl != NULL) + SSL_free(ssl); +#endif +} +//----------------------------------------------------------------------------- +// initialize +//----------------------------------------------------------------------------- +void CySocket::init(void) +{ + handling = false; + isOpened = false; + isValid = true; + addr_len = sizeof(addr); + memset(&addr, 0, addr_len); +#ifdef Y_CONFIG_USE_OPEN_SSL + isSSLSocket = false; +#endif +} +//----------------------------------------------------------------------------- +// Initialize this socket as a SSL-socket +//----------------------------------------------------------------------------- +#ifdef Y_CONFIG_USE_OPEN_SSL + +bool CySocket::initAsSSL(void) +{ + isSSLSocket = true; + if (NULL == (ssl = SSL_new(CySocket::SSL_ctx))) // create SSL-socket + { + aprintf("ySocket:SSL Error: Create SSL_new : %s\n", ERR_error_string(ERR_get_error(), NULL) ); + return false; + } + SSL_set_accept_state(ssl); // accept connection + if(1 != (SSL_set_fd(ssl, sock))) // associate socket descriptor + if (NULL == (ssl = SSL_new(CySocket::SSL_ctx))) + { + aprintf("ySocket:SSL Error: Create SSL_new : %s\n", ERR_error_string(ERR_get_error(), NULL) ); + return false; + } + return true; +} +//----------------------------------------------------------------------------- +// static: initialize the SSL-Library and the SSL ctx object. +// Read and assoziate the keyfile. +//----------------------------------------------------------------------------- +bool CySocket::initSSL(void) +{ + SSL_load_error_strings(); // Load SSL Error Strings + SSL_library_init(); // Load SSL Library + if (0 == RAND_status()) // set Random + { + aprintf("ySocket:SSL got no rand\n"); + return false; + } + if((SSL_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) // create master ctx + { + aprintf("ySocket:SSL Error: Create SSL_CTX_new : %s\n", ERR_error_string(ERR_get_error(), NULL) ); + return false; + } + if(SSL_pemfile == "") + { + aprintf("ySocket:SSL Error: no pemfile given\n"); + return false; + } + if(SSL_CA_file != "") // have a CA? + if(1 != SSL_CTX_load_verify_locations(SSL_ctx, SSL_CA_file.c_str(), NULL)) + { + aprintf("ySocket:SSL Error: %s CA-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_CA_file.c_str()); + return false; + } + if(SSL_CTX_use_certificate_file(SSL_ctx, SSL_pemfile.c_str(), SSL_FILETYPE_PEM) < 0) + { + aprintf("ySocket:SSL Error: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str()); + return false; + } + + if(SSL_CTX_use_PrivateKey_file(SSL_ctx, SSL_pemfile.c_str(), SSL_FILETYPE_PEM) < 0) + { + aprintf("ySocket:SSL Error: Private Keys: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str()); + return false; + } + if(SSL_CTX_check_private_key(SSL_ctx) != 1) + { + aprintf("ySocket:SSL Error: Private Keys not compatile to public keys: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str()); + return false; + } + return true; +} +#endif +//============================================================================= +// Socket handling +//============================================================================= +void CySocket::close(void) +{ + if(sock != 0 && sock != INVALID_SOCKET) + ::close(sock); +#ifndef Y_CONFIG_FEATURE_KEEP_ALIVE + sock = 0; +#endif + isOpened = false; +} +//----------------------------------------------------------------------------- +void CySocket::shutdown(void) +{ + if(sock != 0 && sock != INVALID_SOCKET) + ::shutdown(sock,SHUT_RDWR); +} +//----------------------------------------------------------------------------- +bool CySocket::listen(int port, int max_connections) +{ + if(sock == INVALID_SOCKET) + return false; + + // set sockaddr for listening + sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; // Protocol + sai.sin_addr.s_addr = htonl(INADDR_ANY); // No Filter + sai.sin_port = htons(port); // Listening Port + + set_reuse_port(); // Re-Use Port + set_reuse_addr(); // Re-Use IP + if(bind(sock, (sockaddr *)&sai, sizeof(sockaddr_in)) != SOCKET_ERROR) + if(::listen(sock, max_connections) == 0) + return true; + return false; +} +//----------------------------------------------------------------------------- +CySocket* CySocket::accept() +{ + init(); + SOCKET newSock = ::accept(sock, (sockaddr *) &addr, &addr_len); + if(newSock == INVALID_SOCKET) + { + dperror("accept: invalid socket\n"); + return NULL; + } + CySocket *new_ySocket = new CySocket(newSock); + if(new_ySocket != NULL) + { + new_ySocket->setAddr(addr); +#ifdef TCP_CORK + new_ySocket->set_option(IPPROTO_TCP, TCP_CORK); +#else + set_tcp_nodelay(); +#endif + } + new_ySocket->isOpened = true; +// handling = true; + return new_ySocket; +} +//----------------------------------------------------------------------------- +std::string CySocket::get_client_ip(void) +{ + return inet_ntoa(addr.sin_addr); +} +//----------------------------------------------------------------------------- +int CySocket::get_accept_port(void) +{ + return (int)ntohs(addr.sin_port); +} +//----------------------------------------------------------------------------- +void CySocket::setAddr(sockaddr_in _addr) +{ + addr = _addr; +} + +//----------------------------------------------------------------------------- +// Set Socket Option (return = false = error) +//----------------------------------------------------------------------------- +bool CySocket::set_option(int typ, int option) +{ + int on = 1; + return (setsockopt(sock, typ, option, (char *)&on, sizeof(on)) >= 0); +} + +//----------------------------------------------------------------------------- +// Set Re-Use Option for Port. +//----------------------------------------------------------------------------- +void CySocket::set_reuse_port() +{ +#ifdef SO_REUSEPORT + if(!set_option(SOL_SOCKET, SO_REUSEPORT)) + dperror("setsockopt(SO_REUSEPORT)\n"); +#endif +} + +//----------------------------------------------------------------------------- +// Set Re-Use Option for Address. +//----------------------------------------------------------------------------- +void CySocket::set_reuse_addr() +{ +#ifdef SO_REUSEADDR + if(!set_option(SOL_SOCKET, SO_REUSEADDR)) + dperror("setsockopt(SO_REUSEADDR)\n"); +#endif +} + +//----------------------------------------------------------------------------- +// Set Keep-Alive Option for Socket. +//----------------------------------------------------------------------------- +void CySocket::set_keep_alive() +{ +#ifdef SO_KEEPALIVE + if(!set_option(SOL_SOCKET, SO_KEEPALIVE)) + dperror("setsockopt(SO_KEEPALIVE)\n"); +#endif +} + +//----------------------------------------------------------------------------- +// Set Keep-Alive Option for Socket. +//----------------------------------------------------------------------------- +void CySocket::set_tcp_nodelay() +{ +#ifdef TCP_NODELAY + if(!set_option(IPPROTO_TCP, TCP_NODELAY)) + dperror("setsockopt(SO_KEEPALIVE)\n"); +#endif +} +//============================================================================= +// Send and receive +//============================================================================= +//----------------------------------------------------------------------------- +// Read a buffer (normal or SSL) +//----------------------------------------------------------------------------- +int CySocket::Read(char *buffer, unsigned int length) +{ +#ifdef Y_CONFIG_USE_OPEN_SSL + if(isSSLSocket) + return SSL_read(ssl, buffer, length); + else +#endif + return ::read(sock, buffer, length); +} +//----------------------------------------------------------------------------- +// Send a buffer (normal or SSL) +//----------------------------------------------------------------------------- +int CySocket::Send(char const *buffer, unsigned int length) +{ + unsigned int len = 0; +#ifdef Y_CONFIG_USE_OPEN_SSL + if(isSSLSocket) + len = SSL_write(ssl, buffer, length); + else +#endif + len = ::send(sock, buffer, length, MSG_NOSIGNAL); + if(len >= 0) + BytesSend += len; + return len; +} +//----------------------------------------------------------------------------- +// Check if Socket was closed by client +//----------------------------------------------------------------------------- +bool CySocket::CheckSocketOpen() +{ + char buffer[32]; + +#ifdef CONFIG_SYSTEM_CYGWIN + return !(recv(sock, buffer, sizeof(buffer), MSG_PEEK | MSG_NOSIGNAL) == 0); +#else + return !(recv(sock, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0); +#endif +} + +//============================================================================= +// Aggregated Send- and Receive- Operations +//============================================================================= + +//----------------------------------------------------------------------------- +// BASIC Send File over Socket for FILE* +// fd is an opened FILE-Descriptor +//----------------------------------------------------------------------------- +int CySocket::SendFile(int filed) +{ + if(!isValid) + return false; +#ifdef Y_CONFIG_HAVE_SENDFILE + // does not work with SSL !!! + off_t start = 0; + off_t end = lseek(filed,0,SEEK_END); + int written = 0; + if((written = ::sendfile(sock,filed,&start,end)) == -1) + { + perror("sendfile failed\n"); + return false; + } + else + BytesSend += written; +#else + char sbuf[1024]; + unsigned int r=0; + while ((r=read(filed, sbuf, 1024)) > 0) + { + if (Send(sbuf, r) < 0) + { + perror("sendfile failed\n"); + return false; + } + } +#endif // Y_CONFIG_HAVE_SENDFILE + log_level_printf(9,": Bytes:%ld\n", BytesSend); + return true; +} +//----------------------------------------------------------------------------- +// Receive File over Socket for FILE* filed +// read/write in small blocks (to save memory). +// usind sleep for sync input +// fd is an opened FILE-Descriptor +//----------------------------------------------------------------------------- +//TODO: Write upload Progress Informations into a file +unsigned int CySocket::ReceiveFileGivenLength(int filed, unsigned int _length) +{ + unsigned int _readbytes = 0; + char buffer[RECEIVE_BLOCK_LEN]; + int retries=0; + + do + { + // check bytes in Socket buffer + u_long readarg = 0; +#ifdef Y_CONFIG_USE_OPEN_SSL + if(isSSLSocket) + readarg = RECEIVE_BLOCK_LEN; + else +#endif + { + if(ioctl(sock, FIONREAD, &readarg) != 0)// How many bytes avaiable on socket? + break; + if(readarg > RECEIVE_BLOCK_LEN) // enough bytes to read + readarg = RECEIVE_BLOCK_LEN; // read only given length + } + if(readarg == 0) // nothing to read: sleep + { + retries++; + if(retries >NON_BLOCKING_MAX_RETRIES) + break; + sleep(1); + } + else + { + int bytes_gotten = Read(buffer, readarg); + + if(bytes_gotten == -1 && errno == EINTR)// non-blocking + continue; + if(bytes_gotten <= 0) // ERROR Code gotten or Conection closed by peer + { + isValid = false; + break; + } + _readbytes += bytes_gotten; + if (write(filed, buffer, bytes_gotten) != bytes_gotten) + { + perror("write file failed\n"); + return 0; + } + retries = 0; + if(bytes_gotten < NON_BLOCKING_TRY_BYTES) // to few bytes gotten: sleep + sleep(1); + } + log_level_printf(8,"Receive Block length:%d all:%d\n",_readbytes, _length); + } + while(_readbytes + RECEIVE_BLOCK_LEN < _length); + return _readbytes; +} +//----------------------------------------------------------------------------- +// read all data avaiable on Socket +//----------------------------------------------------------------------------- +std::string CySocket::ReceiveBlock() +{ + std::string result = ""; + char buffer[RECEIVE_BLOCK_LEN]; + + if(!isValid || !isOpened) + return ""; +// signal(SIGALRM, ytimeout); + alarm(1); + + while(true) + { + // check bytes in Socket buffer + u_long readarg = 0; +#ifdef Y_CONFIG_USE_OPEN_SSL + if(isSSLSocket) + readarg = RECEIVE_BLOCK_LEN; + else +#endif + // determine bytes that can be read + { + if(ioctl(sock, FIONREAD, &readarg) != 0) + break; + if(readarg == 0) // nothing to read + break; + if(readarg > RECEIVE_BLOCK_LEN) // need more loops + readarg = RECEIVE_BLOCK_LEN; + } + // Read data + int bytes_gotten = Read(buffer, readarg); + + if(bytes_gotten == -1 && errno == EINTR)// non-blocking + continue; + if(bytes_gotten <= 0) // ERROR Code gotten or Conection closed by peer + { + isValid = false; + break; + } + result.append(buffer, bytes_gotten); + if((u_long)bytes_gotten < readarg) // no more bytes + break; + } + alarm(0); + signal(SIGALRM,SIG_IGN); + return result; +} + +//----------------------------------------------------------------------------- +// Read on line (Ends with LF) or maximum MAX_LINE_BUFFER chars +// Result Contains [CR]LF! +//----------------------------------------------------------------------------- +std::string CySocket::ReceiveLine() +{ + char buffer[MAX_LINE_BUFFER]; + int bytes_gotten = 0; + std::string result=""; + + while(true) + { + // read one char + if(Read(buffer+bytes_gotten, 1) == 1) + { + if(buffer[bytes_gotten] == '\n') + break; + } + else + { + isValid = false; + break; + } + + if(bytes_gotten < MAX_LINE_BUFFER-1) + bytes_gotten ++; + else + break; + } + buffer[++bytes_gotten] = '\0'; + result.assign(buffer, bytes_gotten); + + return result; +} diff --git a/src/nhttpd/yhttpd_core/ysocket.h b/src/nhttpd/yhttpd_core/ysocket.h new file mode 100644 index 000000000..3db236a51 --- /dev/null +++ b/src/nhttpd/yhttpd_core/ysocket.h @@ -0,0 +1,98 @@ +//============================================================================= +// YHTTPD +// Socket Class : Basic Socket Operations +//----------------------------------------------------------------------------- +// Socket-Handler +//============================================================================= +#ifndef __yhttpd_ysocket_h__ +#define __yhttpd_ysocket_h__ + +// system +#include +#include +#include +#include + +#include "yconfig.h" +#include "ytypes_globals.h" + +#ifdef Y_CONFIG_USE_OPEN_SSL +#include +#endif +//----------------------------------------------------------------------------- +// Some common socket definitions +#ifndef SOCKET +#define SOCKET int +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR (-1) +#endif + +//----------------------------------------------------------------------------- +#define NON_BLOCKING_MAX_RETRIES 10 // Retries, if receive buffer is empty +#define NON_BLOCKING_TRY_BYTES 512 // try to catch min bytes .. else sleep +#define RECEIVE_BLOCK_LEN 1024 // receiving block length +#define MAX_LINE_BUFFER 1024*2 // Max length of one line + +//----------------------------------------------------------------------------- +class CySocket +{ +public: + // constructor & destructor + CySocket(); + CySocket(SOCKET s):sock(s){init();}; + virtual ~CySocket(void); + void init(void); // some extra initialization +#ifdef Y_CONFIG_USE_OPEN_SSL + bool initAsSSL(void); // initialize this socket as a SSL-socket + static bool initSSL(void); // global initialize of SSL-Library, CTX and cerificate/key-file + static std::string SSL_pemfile; // "server.pem" file with SSL-Certificate (now: only one certificate at all) + static std::string SSL_CA_file; // CA Certificate (optional) +#endif + // Socket handling + bool handling; // true: Socket yet is handled by a Connection Thread + bool isOpened; // is this socket open? + bool isValid; // false on Socket Errors. Must close. + struct timeval tv_start_waiting; // Put keep-alive Socket to Wait-Queue + + void close(void); // Close Socket + void shutdown(void); // Shutdown Socket + bool listen(int port, int max_connections); // Listen on Port for max Slave Connections + CySocket* accept(); // Wait for Connection. Returns "slave" Socket + void setAddr(sockaddr_in _clientaddr); + std::string get_client_ip(void); // Get IP from Client + SOCKET get_socket(){return sock;} // Return "C" Socket-ID + int get_accept_port(void); // Get Port for accepted connection + + // send & receive (basic) + int Read(char *buffer, unsigned int length); // Read a buffer (normal or SSL) + int Send(char const *buffer, unsigned int length); // Send a buffer (normal or SSL) + bool CheckSocketOpen(); // check if socket was closed by client + + // send & receive + int SendFile(int filed); // Send a File + std::string ReceiveBlock(); // receive a Block. Look at length + unsigned int ReceiveFileGivenLength(int filed, unsigned int _length); // Receive File of given length + std::string ReceiveLine(); // receive until "\n" + +protected: + long BytesSend; // Bytes send over Socket + bool set_option(int typ, int option); // Set Socket Options + void set_reuse_port(); // Set Reuse Port Option for Socket + void set_reuse_addr(); // Set Reuse Address Option for Socket + void set_keep_alive(); // Set Keep-Alive Option for Socket + void set_tcp_nodelay(); + +#ifdef Y_CONFIG_USE_OPEN_SSL + bool isSSLSocket; // This is a SSL based Socket + static SSL_CTX *SSL_ctx; // Global SSL ctx object + SSL *ssl; // ssl habdler for this socket +#endif +private: + std::string receive_buffer; // buffer for receive from socket + sockaddr_in addr; // "slave" Client Socket Data + socklen_t addr_len; // Length of addr struct + SOCKET sock; // "C" Socket-ID +}; +#endif // __yhttpd_ysocket_h__ diff --git a/src/nhttpd/yhttpd_core/ytypes_globals.h b/src/nhttpd/yhttpd_core/ytypes_globals.h new file mode 100644 index 000000000..43bdb43e3 --- /dev/null +++ b/src/nhttpd/yhttpd_core/ytypes_globals.h @@ -0,0 +1,157 @@ +//============================================================================= +// YHTTPD +// Type Definitions and Global Variables +//============================================================================= + +//----------------------------------------------------------------------------- +#ifndef __yhttpd_types_globals_h__ +#define __yhttpd_types_globals_h__ + +// c++ +#include +#include +#include + +//============================================================================= +// Global Types +//============================================================================= +typedef std::map CStringList; +typedef std::vector CStringArray; +typedef std::vector CStringVector; + +//============================================================================= +// HTTP Protocol Definitions +//============================================================================= +// HTTP-Status codes +typedef enum +{ + HTTP_NIL = -1, + HTTP_CONTINUE = 100, + HTTP_SWITCHING_PROTOCOLS = 101, // not used + HTTP_OK = 200, // use: everything is right. request (and response) handled + HTTP_CREATED = 201, // not used + HTTP_ACCEPTED = 202, // use: idicate accept of request. request take more time. + HTTP_NON_AUTHORITATIVE_INFO = 203, + HTTP_NO_CONTENT = 204, + HTTP_RESET_CONTENT = 205, // not used + HTTP_PARTIAL_CONTENT = 206, // not used + HTTP_MULTIBLE_CHOICES = 300, // not used + HTTP_MOVED_PERMANENTLY = 301, + HTTP_MOVED_TEMPORARILY = 302, + HTTP_SEE_OTHER = 303, // not used + HTTP_NOT_MODIFIED = 304, + HTTP_USE_PROXY = 305, // not used + HTTP_TEMPORARY_REDIRECT = 307, // not used + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_PAYMENT_REQUIRED = 402, // not used + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_METHOD_NOT_ALLOWED = 405, // not used + HTTP_NOT_ACCEPTABLE = 406, // not used + HTTP_PROXY_AUTHENTICATION_REQUIRED= 407, // not used + HTTP_REQUEST_TIMEOUT = 408, // not used + HTTP_CONFLICT = 409, // not used + HTTP_GONE = 410, // not used + HTTP_LENGTH_REQUIRED = 411, // not used + HTTP_PRECONDITION_FAILED = 412, // not used + HTTP_REQUEST_ENTITY_TOO_LARGE = 413, // not used + HTTP_REQUEST_URI_TOO_LARGE = 414, // not used + HTTP_UNSUPPORTED_MEDIA_TYPE = 415, // not used + HTTP_REQUEST_RANGE_NOT_SATISFIABLE= 416, // not used + HTTP_EXPECTAION_FAILED = 417, // not used + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_NOT_IMPLEMENTED = 501, + HTTP_BAD_GATEWAY = 502, // not used + HTTP_SERVICE_UNAVAILABLE = 503, // not used + HTTP_GATEWAY_TIMEOUT = 504, // not used + HTTP_HTTP_VERSION_NOT_SUPPORTED = 505, // not used +} HttpResponseType; + +typedef struct +{ + HttpResponseType type; + const char *name; + const char *info; +} HttpEnumString; + +static const HttpEnumString httpResponseNames[] = { + { HTTP_CONTINUE, "Continue" }, + { HTTP_SWITCHING_PROTOCOLS, "Switching Protocols" }, + { HTTP_OK, "OK" }, + { HTTP_CREATED, "Created" }, + { HTTP_ACCEPTED, "Accepted" }, + { HTTP_NON_AUTHORITATIVE_INFO, "No Authorative Info" }, + { HTTP_NO_CONTENT, "No Content" }, + { HTTP_RESET_CONTENT, "Reset Content" }, + { HTTP_PARTIAL_CONTENT, "Partial Content" }, + { HTTP_MULTIBLE_CHOICES, "Multiple Choices" }, + { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, + { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" }, + { HTTP_SEE_OTHER, "See Other" }, + { HTTP_NOT_MODIFIED, "Not Modified" }, + { HTTP_USE_PROXY, "Use Proxy" }, + { HTTP_TEMPORARY_REDIRECT, "Temporary Redirect" }, + { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." }, + { HTTP_UNAUTHORIZED, "Unauthorized", "Access denied." }, + { HTTP_PAYMENT_REQUIRED, "Payment Required" }, + { HTTP_FORBIDDEN, "Forbidden", "" }, + { HTTP_NOT_FOUND, "Not Found", "The requested URL was not found on this server." }, + { HTTP_METHOD_NOT_ALLOWED, "Method Not Allowed" }, + { HTTP_NOT_ACCEPTABLE, "Not Acceptable" }, + { HTTP_PROXY_AUTHENTICATION_REQUIRED,"Proxy Authentication Required" }, + { HTTP_REQUEST_TIMEOUT, "Request Time-out" }, + { HTTP_CONFLICT, "Conflict" }, + { HTTP_GONE, "Gone" }, + { HTTP_LENGTH_REQUIRED, "Length Required" }, + { HTTP_PRECONDITION_FAILED, "Precondition Failed" }, + { HTTP_REQUEST_ENTITY_TOO_LARGE,"Request Entity Too Large" }, + { HTTP_REQUEST_URI_TOO_LARGE, "Request-URI Too Large" }, + { HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" }, + { HTTP_REQUEST_RANGE_NOT_SATISFIABLE,"Requested range not satisfiable" }, + { HTTP_EXPECTAION_FAILED, "Expectation Failed" }, + { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", "Internal Server Error" }, + { HTTP_NOT_IMPLEMENTED, "Not Implemented", "The requested method is not recognized by this server." }, + { HTTP_BAD_GATEWAY, "Bad Gateway" }, + { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable" }, + { HTTP_GATEWAY_TIMEOUT, "Gateway Time-out" }, + { HTTP_HTTP_VERSION_NOT_SUPPORTED,"HTTP Version not supported" }, +}; + +// HTTP-methods +typedef enum +{ + M_UNKNOWN = 0, + M_POST = 1, + M_GET, + M_HEAD, + M_PUT, + M_DELETE, + M_TRACE +} THttp_Method; + +// Date Format RFC 1123 +static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; + +// mime-table +typedef struct +{ + const char *fileext; + const char *mime; +} TMimePair; +static const TMimePair MimeFileExtensions[] = { + {"xml", "text/xml"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"yhtm", "text/html"}, + {"yhtml", "text/html"}, + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, + {"gif", "image/gif"}, + {"png", "image/png"}, + {"txt", "image/plain"}, + {"css", "text/css"}, + +}; + +#endif // __yhttpd_types_globals_h__ diff --git a/src/nhttpd/yhttpd_core/ywebserver.cpp b/src/nhttpd/yhttpd_core/ywebserver.cpp new file mode 100644 index 000000000..27e32259a --- /dev/null +++ b/src/nhttpd/yhttpd_core/ywebserver.cpp @@ -0,0 +1,507 @@ +//============================================================================= +// YHTTPD +// Webserver Class : Until now: exact one instance +//============================================================================= +// c++ +#include +#include + +// system +#include +#include +#include +#include +#include +#include +#include +// tuxbox +#include + +// yhttpd +#include "yhttpd.h" +#include "ytypes_globals.h" +#include "ywebserver.h" +#include "ylogging.h" +#include "helper.h" +#include "ysocket.h" +#include "yconnection.h" +#include "yrequest.h" + +//============================================================================= +// Initialization of static variables +//============================================================================= + bool CWebserver::is_threading = true; + pthread_mutex_t CWebserver::mutex = PTHREAD_MUTEX_INITIALIZER;; + +//============================================================================= +// Constructor & Destructor & Initialization +//============================================================================= +CWebserver::CWebserver() +{ + terminate = false; + for(int i=0;i= HTTPD_MAX_CONNECTIONS-1) + sleep(1); + + // Socket Error? + if(fd == -1 && errno != EINTR) + { + perror("select"); + return false; + } + + // Socket Timeout? + if(fd == 0) + { + // Testoutput for long living threads + if(++test_counter >= MAX_TIMEOUTS_TO_TEST) + { + for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) + if(SocketList[j] != NULL) // here is a socket + log_level_printf(2,"FD-TEST sock:%d handle:%d open:%d\n",SocketList[j]->get_socket(), + SocketList[j]->handling,SocketList[j]->isOpened); + test_counter=0; + } + // some connection closing previous missed? + if(++timeout_counter >= MAX_TIMEOUTS_TO_CLOSE) + { + CloseConnectionSocketsByTimeout(); + timeout_counter=0; + } + continue; // main loop again + } + //---------------------------------------------------------------------------------------- + // Check all observed descriptors & check new or re-use Connections + //---------------------------------------------------------------------------------------- + for(int i = listener; i <= fdmax; i++) + { + int slot = -1; + if(FD_ISSET(i, &read_fds)) // Socket observed? + { // we got one!! + if (i == listener) // handle new connections + slot = AcceptNewConnectionSocket(); + else // Connection on an existing open Socket = reuse (keep-alive) + { + slot = SL_GetExistingSocket(i); + if(slot>=0) + log_level_printf(2,"FD: reuse con fd:%d\n",SocketList[slot]->get_socket()); + } + // prepare Connection handling + if(slot>=0) + if(SocketList[slot] != NULL && !SocketList[slot]->handling && SocketList[slot]->isValid) + { + log_level_printf(2,"FD: START CON HANDLING con fd:%d\n",SocketList[slot]->get_socket()); + FD_CLR(SocketList[slot]->get_socket(), &master); // remove from master set + SocketList[slot]->handling = true; // prepares for thread-handling + if(!handle_connection(SocketList[slot]))// handle this activity + { // Can not handle more threads + char httpstr[]=HTTP_PROTOCOL " 503 Service Unavailable\r\n\r\n"; + SocketList[slot]->Send(httpstr, strlen(httpstr)); + SL_CloseSocketBySlot(slot); + } + } + } + }// for + CloseConnectionSocketsByTimeout(); // Check connections to close + + }//while +#else + while(!terminate) + { + CySocket *newConnectionSock; + if(!(newConnectionSock = listenSocket.accept() )) //Now: Blocking wait + { + dperror("Socket accept error. Continue.\n"); + continue; + } + log_level_printf(3,"Socket connect from %s\n", (listenSocket.get_client_ip()).c_str() ); +#ifdef Y_CONFIG_USE_OPEN_SSL + if(Cyhttpd::ConfigList["SSL"]=="true") + newConnectionSock->initAsSSL(); // make it a SSL-socket +#endif + handle_connection(newConnectionSock); + } +#endif + return true; +} +//============================================================================= +// SocketList Handler +//============================================================================= +//----------------------------------------------------------------------------- +// Accept new Connection +//----------------------------------------------------------------------------- +int CWebserver::AcceptNewConnectionSocket() +{ + int slot = -1; + CySocket *connectionSock = NULL; + int newfd; + + if(!(connectionSock = listenSocket.accept() )) // Blocking wait + { + dperror("Socket accept error. Continue.\n"); + delete connectionSock; + return -1; + } +#ifdef Y_CONFIG_USE_OPEN_SSL + if(Cyhttpd::ConfigList["SSL"]=="true") + connectionSock->initAsSSL(); // make it a SSL-socket +#endif + log_level_printf(2,"FD: new con fd:%d on port:%d\n",connectionSock->get_socket(), connectionSock->get_accept_port()); + + // Add Socket to List + slot = SL_GetFreeSlot(); + if(slot < 0) + { + connectionSock->close(); + aprintf("No free Slot in SocketList found. Open:%d\n",open_connections); + } + else + { + SocketList[slot] = connectionSock; // put it to list + fcntl(connectionSock->get_socket() , F_SETFD , O_NONBLOCK); // set non-blocking + open_connections++; // count open connectins + newfd = connectionSock->get_socket(); + if (newfd > fdmax) // keep track of the maximum fd + fdmax = newfd; + } + return slot; +} + +//----------------------------------------------------------------------------- +// Get Index for Socket from SocketList +//----------------------------------------------------------------------------- +int CWebserver::SL_GetExistingSocket(SOCKET sock) +{ + int slot = -1; + for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) + if(SocketList[j] != NULL // here is a socket + && SocketList[j]->get_socket() == sock) // we know that socket + { + slot = j; + break; + } + return slot; +} +//----------------------------------------------------------------------------- +// Get Index for free Slot in SocketList +//----------------------------------------------------------------------------- +int CWebserver::SL_GetFreeSlot() +{ + int slot = -1; + for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) + if(SocketList[j] == NULL) // here is a free slot + { + slot = j; + break; + } + return slot; +} + +//----------------------------------------------------------------------------- +// Look for Sockets to close +//----------------------------------------------------------------------------- +void CWebserver::CloseConnectionSocketsByTimeout() +{ + CySocket *connectionSock = NULL; + for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) + if(SocketList[j] != NULL // here is a socket + && !SocketList[j]->handling) // it is not handled + { + connectionSock = SocketList[j]; + SOCKET thisSocket = connectionSock->get_socket(); + bool shouldClose = true; + + if(!connectionSock->isValid) // If not valid -> close + ; // close + else if(connectionSock->tv_start_waiting.tv_sec != 0 || SocketList[j]->tv_start_waiting.tv_usec != 0) + { // calculate keep-alive timeout + struct timeval tv_now; + struct timezone tz_now; + gettimeofday(&tv_now, &tz_now); + long long tdiff = ((tv_now.tv_sec - connectionSock->tv_start_waiting.tv_sec) * 1000000 + + (tv_now.tv_usec - connectionSock->tv_start_waiting.tv_usec)); + if(tdiff < HTTPD_KEEPALIVE_TIMEOUT || tdiff <0) + shouldClose = false; + } + if(shouldClose) + { + log_level_printf(2,"FD: close con Timeout fd:%d\n",thisSocket); + SL_CloseSocketBySlot(j); + } + } +} +//----------------------------------------------------------------------------- +// Add Socket fd to FD_SET again (for select-handling) +// Add start-time for waiting for connection re-use / keep-alive +//----------------------------------------------------------------------------- +void CWebserver::addSocketToMasterSet(SOCKET fd) +{ + int slot = SL_GetExistingSocket(fd); // get slot/index for fd + if(slot<0) + return; + log_level_printf(2,"FD: add to master fd:%d\n",fd); + struct timeval tv_now; + struct timezone tz_now; + gettimeofday(&tv_now, &tz_now); + SocketList[slot]->tv_start_waiting = tv_now; // add keep-alive wait time + FD_SET(fd, &master); // add fd to select-master-set +} + +//----------------------------------------------------------------------------- +// Close (FD_SET handled) Socket +// Clear it from SocketList +//----------------------------------------------------------------------------- +void CWebserver::SL_CloseSocketBySlot(int slot) +{ + open_connections--; // count open connections + if(SocketList[slot] == NULL) + return; + SocketList[slot]->handling = false; // no handling anymore + FD_CLR(SocketList[slot]->get_socket(), &master);// remove from master set + SocketList[slot]->close(); // close the socket + delete SocketList[slot]; // destroy ySocket + SocketList[slot] = NULL; // free in list +} + +//============================================================================= +// Thread Handling +//============================================================================= +//----------------------------------------------------------------------------- +// Check if IP is allowed for keep-alive +//----------------------------------------------------------------------------- +bool CWebserver::CheckKeepAliveAllowedByIP(std::string client_ip) +{ + pthread_mutex_lock( &mutex ); + bool do_keep_alive = true; + CStringVector::const_iterator it = conf_no_keep_alive_ips.begin(); + while(it != conf_no_keep_alive_ips.end()) + { + if(trim(*it) == client_ip) + do_keep_alive = false; + it++; + } + pthread_mutex_unlock( &mutex ); + return do_keep_alive; +} +//----------------------------------------------------------------------------- +// Set Entry(number)to NULL in Threadlist +//----------------------------------------------------------------------------- +void CWebserver::clear_Thread_List_Number(int number) +{ + pthread_mutex_lock( &mutex ); + if(number ySock = newSock; + newConn->ySock->handling = true; + newConn->WebserverBackref = this; + //newConn->is_treaded = is_threading; + newConn->is_treaded = false; + + int index = -1; + if(0) /*if(is_threading) FIXME not work */ + { + pthread_mutex_lock( &mutex ); + // look for free Thread slot + for(int i=0;ithread_number = index; //remember Index of Thread slot (for clean up) + + // Create an orphan Thread. It is not joinable anymore + pthread_mutex_unlock( &mutex ); + + // start connection Thread + if(pthread_create(&Connection_Thread_List[index], &attr, WebThread, (void *)newConn) != 0) + dperror("Could not create Connection-Thread\n"); + } + else // non threaded + WebThread((void *)newConn); + return ((index != -1) || !is_threading); +} +//------------------------------------------------------------------------- +// Webserver-Thread for each connection +//------------------------------------------------------------------------- +void *WebThread(void *args) +{ + CWebserverConnection *con; + CWebserver *ws; + TWebserverConnectionArgs *newConn = (TWebserverConnectionArgs *) args; + ws = newConn->WebserverBackref; + + bool is_threaded = newConn->is_treaded; + if(is_threaded) + log_level_printf(1,"++ Thread 0x06%X gestartet\n", (int) pthread_self()); + + if (!newConn) { + dperror("WebThread called without arguments!\n"); + if(newConn->is_treaded) + pthread_exit(NULL); + } + + // (1) create & init Connection + con = new CWebserverConnection(ws); + con->Request.UrlData["clientaddr"] = newConn->ySock->get_client_ip(); // TODO:here? + con->sock = newConn->ySock; // give socket reference + newConn->ySock->handling = true; // dont handle this socket now be webserver main loop + + // (2) handle the connection + con->HandleConnection(); + + // (3) end connection handling +#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE + if(!con->keep_alive) + log_level_printf(2,"FD SHOULD CLOSE sock:%d!!!\n",con->sock->get_socket()); + else + ws->addSocketToMasterSet(con->sock->get_socket()); // add to master set +#else + delete newConn->ySock; +#endif + if(!con->keep_alive) + con->sock->isValid = false; + con->sock->handling = false; // socket can be handled by webserver main loop (select) again + + // (4) end thread + delete con; + int thread_number = newConn->thread_number; + delete newConn; + if(is_threaded) + { + log_level_printf(1,"-- Thread 0x06%X beendet\n",(int)pthread_self()); + ws->clear_Thread_List_Number(thread_number); + pthread_exit(NULL); + } + return NULL; +} diff --git a/src/nhttpd/yhttpd_core/ywebserver.h b/src/nhttpd/yhttpd_core/ywebserver.h new file mode 100644 index 000000000..73c5c8b9f --- /dev/null +++ b/src/nhttpd/yhttpd_core/ywebserver.h @@ -0,0 +1,91 @@ +//============================================================================= +// YHTTPD +// Webserver Class : Until now: exact one instance +//----------------------------------------------------------------------------- +// The Webserver Class creates one "master" Listener Socket on given Port. +// If a Connection is accepted, a new Socket ist used. The Webserver creates +// a new Connection-Class witch is (normaly) Threaded. The Connection-Class +// handles Request and Response. +// For HTTP/1.1 permanent Connections (keep-alive) socket-multiplexing using +// FD_SET and select() is implemented. Sockets are stored in SocketList and +// can be reused for the connected client (to reduce socket handling overhead). +// Reuse of connections is often done by pipelinig (read->send->read->send ...) +//============================================================================= +#ifndef __yhttpd_ywebserver_h__ +#define __yhttpd_ywebserver_h__ + +// system +#include +#include +#include + +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "ysocket.h" + +//----------------------------------------------------------------------------- +class CWebserver; //forward declaration + +//----------------------------------------------------------------------------- +// Arguments for Connection-Thread +//----------------------------------------------------------------------------- +typedef struct +{ + CySocket *ySock; // Connection "Slave" Socket + CWebserver *WebserverBackref; // Backreference to Webserver + bool is_treaded; // Use threading? + int thread_number; // Number of Thread (informational use) +} TWebserverConnectionArgs; + +//----------------------------------------------------------------------------- +// until now: One Instance for one hosted Web +//----------------------------------------------------------------------------- +class CWebserver +{ +private: + static pthread_mutex_t mutex; + pthread_t Connection_Thread_List[HTTPD_MAX_CONNECTIONS]; //Thread-List per webserver + fd_set master; // master file descriptor list for select() + fd_set read_fds; // read file descriptor list for select() + int fdmax; // maximum file descriptor number + CySocket *SocketList[HTTPD_MAX_CONNECTIONS]; // List of concurrent hadled connections + int open_connections; // Number of opened connections + pthread_attr_t attr; // pthread Attributes for deattach +protected: + bool terminate; // flag: indicate to terminate the Webserver + CySocket listenSocket; // Master Socket for listening + unsigned int port; // Port to listen on + bool handle_connection(CySocket *newSock); // Create a new Connection Instance and handle Connection + + // Connection Socket handling + int AcceptNewConnectionSocket(); // Start new Socket connection + void CloseConnectionSocketsByTimeout(); // Check Sockets to close + void SL_CloseSocketBySlot(int slot); // Close socket by slot index + int SL_GetExistingSocket(SOCKET sock); // look for socket reuse + int SL_GetFreeSlot(); // get free slot + + +public: + static bool is_threading; // Use Threading for new Connections + CStringVector conf_no_keep_alive_ips; // List of IP for NO keep-alive + + // constructor & destructor + CWebserver(); + ~CWebserver(void); + + void init(unsigned int _port, bool _is_threading) // Initialize Webserver Settings + {port=_port; is_threading=_is_threading;} + void set_conf_no_keep_alive_ips(CStringVector _conf_no_keep_alive_ips) + {conf_no_keep_alive_ips=_conf_no_keep_alive_ips;} + bool run(void); // Start the Webserver + void stop(void) // Stop the Webserver + {terminate=true;}; + + // public for WebTread + void clear_Thread_List_Number(int number); // Set Entry(number)to NULL in Threadlist + void addSocketToMasterSet(SOCKET fd); // add Socket to select master set + bool CheckKeepAliveAllowedByIP(std::string client_ip); // Check if IP is allowed for keep-alive +}; + +#endif // __yhttpd_ywebserver_h__ diff --git a/src/nhttpd/yhttpd_mods/AUTHORS b/src/nhttpd/yhttpd_mods/AUTHORS new file mode 100644 index 000000000..aec719772 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/AUTHORS @@ -0,0 +1,3 @@ + + +Johannes Golombek [yjogol@cvs.tuxbox.org] diff --git a/src/nhttpd/yhttpd_mods/Makefile.am b/src/nhttpd/yhttpd_mods/Makefile.am new file mode 100644 index 000000000..74d213c4f --- /dev/null +++ b/src/nhttpd/yhttpd_mods/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/nhttpd \ + -I$(top_srcdir)/src/nhttpd/yhttpd_core \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libconfigfile \ + @FREETYPE_CFLAGS@ + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +noinst_LIBRARIES = libyhttpdmods.a + +libyhttpdmods_a_SOURCES = \ + mod_yparser.cpp mod_testhook.cpp mod_auth.cpp mod_cache.cpp mod_sendfile.cpp mod_weblog.cpp + diff --git a/src/nhttpd/yhttpd_mods/mod_auth.cpp b/src/nhttpd/yhttpd_mods/mod_auth.cpp new file mode 100644 index 000000000..5a7af3c5f --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_auth.cpp @@ -0,0 +1,108 @@ +//============================================================================= +// YHTTPD +// TestHook +//============================================================================= + +#include +#include +#include "mod_auth.h" +#include "helper.h" + +//----------------------------------------------------------------------------- +// HOOK: response_hook +//----------------------------------------------------------------------------- +THandleStatus CmAuth::Hook_PrepareResponse(CyhookHandler *hh) +{ + THandleStatus status = HANDLED_CONTINUE; + + if(authenticate) + { + if( (hh->UrlData["clientaddr"]).find(IADDR_LOCAL)>0 && + (no_auth_client == "" || + (hh->UrlData["clientaddr"]).find(no_auth_client)>0)) // dont check local calls or calls from NoAuthClient + { + if (!CheckAuth(hh)) + { + hh->SetError(HTTP_UNAUTHORIZED); + status = HANDLED_ERROR; + } + } + } + return status; +} + +//----------------------------------------------------------------------------- +// HOOK: webserver_readconfig_hook Handler +// This hook ist called from ReadConfig +//----------------------------------------------------------------------------- +THandleStatus CmAuth::Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList) +{ + username = Config->getString("mod_auth.username", AUTHUSER); + password = Config->getString("mod_auth.password", AUTHPASSWORD); + no_auth_client = Config->getString("mod_auth.no_auth_client", ""); + authenticate = Config->getBool("mod_auth.authenticate", false); + ConfigList["mod_auth.username"] = username; + ConfigList["mod_auth.password"] = password; + ConfigList["mod_auth.no_auth_client"] = no_auth_client; + ConfigList["mod_auth.authenticate"] = Config->getString("mod_auth.authenticate", "false"); + return HANDLED_CONTINUE; +} + +//----------------------------------------------------------------------------- +// check if given username an pssword are valid +//----------------------------------------------------------------------------- +bool CmAuth::CheckAuth(CyhookHandler *hh) +{ + if (hh->HeaderList["Authorization"] == "") + return false; + std::string encodet = hh->HeaderList["Authorization"].substr(6,hh->HeaderList["Authorization"].length() - 6); + std::string decodet = decodeBase64(encodet.c_str()); + int pos = decodet.find_first_of(':'); + std::string user = decodet.substr(0,pos); + std::string passwd = decodet.substr(pos + 1, decodet.length() - pos - 1); + return (user.compare(username) == 0 && + passwd.compare(password) == 0); +} + +//----------------------------------------------------------------------------- +// decode Base64 buffer to String +//----------------------------------------------------------------------------- +std::string CmAuth::decodeBase64(const char *b64buffer) +{ + char *newString, *org_newString; //shorter then b64buffer + std::string result; + if((newString = (char *)malloc(sizeof(char) * strlen(b64buffer) + 1) ) != NULL) + { + org_newString = newString; + int i = 0; + unsigned long c = 0; + + while (*b64buffer) + { + int oneChar = *b64buffer++; + if(oneChar >= '0' && oneChar <= '9') oneChar = oneChar - '0' + 52; + else if(oneChar >= 'A' && oneChar <= 'Z') oneChar = oneChar - 'A'; + else if(oneChar >= 'a' && oneChar <= 'z') oneChar = oneChar - 'a' + 26; + else if(oneChar == '+') oneChar = 62; + else if(oneChar == '/') oneChar = 63; + else if(oneChar == '=') oneChar = 0; + else continue; + + c = (c << 6) | oneChar; + if (++i == 4) + { + *newString++ = (char) (c >> 16); + *newString++ = (char) (c >> 8); + *newString++ = (char) c; + i = 0; + } + } + *newString++ = '\0'; + result = std::string(org_newString); + free(org_newString); + return result; + } + else + return ""; +} + diff --git a/src/nhttpd/yhttpd_mods/mod_auth.h b/src/nhttpd/yhttpd_mods/mod_auth.h new file mode 100644 index 000000000..a010dba63 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_auth.h @@ -0,0 +1,30 @@ +//============================================================================= +// YHTTPD +// mod_auth : Authentication +//============================================================================= +#ifndef __yhttpd_mod_auth_h__ +#define __yhttpd_mod_auth_h__ + + +#include "yhook.h" +class CmAuth : public Cyhook +{ +public: + bool authenticate; + CmAuth(){}; + ~CmAuth(){}; + + // Hooks + virtual THandleStatus Hook_PrepareResponse(CyhookHandler *hh); + virtual std::string getHookName(void) {return std::string("mod_auth");} + virtual std::string getHookVersion(void) {return std::string("$Revision: 1.2 $");} + virtual THandleStatus Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList); +protected: + bool CheckAuth(CyhookHandler *hh); + std::string decodeBase64(const char *b64buffer); + std::string username; + std::string password; + std::string no_auth_client; +}; +#endif // __yhttpd_mod_auth_h__ + diff --git a/src/nhttpd/yhttpd_mods/mod_cache.cpp b/src/nhttpd/yhttpd_mods/mod_cache.cpp new file mode 100644 index 000000000..fb685e1c2 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_cache.cpp @@ -0,0 +1,257 @@ +//============================================================================= +// YHTTPD +// Module: CacheManager (mod_cache) +//============================================================================= + +// system +#include +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "helper.h" +#include "mod_cache.h" + +//============================================================================= +// Initialization of static variables +//============================================================================= +pthread_mutex_t CmodCache::mutex = PTHREAD_MUTEX_INITIALIZER; +TCacheList CmodCache::CacheList; + +//============================================================================= +// Constructor & Destructor & Initialization +//============================================================================= + +//----------------------------------------------------------------------------- +// HOOK: Response Prepare Handler +// Response Prepare Check. +// Is it in cache? +//----------------------------------------------------------------------------- +THandleStatus CmodCache::Hook_PrepareResponse(CyhookHandler *hh) +{ + hh->status = HANDLED_NONE; + + log_level_printf(4,"mod_cache prepare hook start url:%s\n",hh->UrlData["fullurl"].c_str()); + std::string url = hh->UrlData["fullurl"]; + if(CacheList.find(url) != CacheList.end()) // is in Cache. Rewrite URL or not modified + { + pthread_mutex_lock(&mutex); // yeah, its mine + + // Check if modified + time_t if_modified_since = (time_t)-1; + if(hh->HeaderList["If-Modified-Since"] != "") // Have If-Modified-Since Requested by Browser? + { + struct tm mod; + if(strptime(hh->HeaderList["If-Modified-Since"].c_str(), RFC1123FMT, &mod) != NULL) + { + mod.tm_isdst = 0; // daylight saving flag! + if_modified_since = mktime(&mod); // Date given + } + } + + // normalize obj_last_modified to GMT + struct tm *tmp = gmtime(&(CacheList[url].created)); + time_t obj_last_modified_gmt = mktime(tmp); + bool modified = (if_modified_since == (time_t)-1) || (if_modified_since < obj_last_modified_gmt); + + // Send file or not-modified header + if(modified) + { + hh->SendFile(CacheList[url].filename); + hh->ResponseMimeType = CacheList[url].mime_type; + } + else + hh->SetHeader(HTTP_NOT_MODIFIED, CacheList[url].mime_type, HANDLED_READY); + pthread_mutex_unlock(&mutex); + } + log_level_printf(4,"mod_cache hook prepare end status:%d\n",(int)hh->status); + + return hh->status; +} +//----------------------------------------------------------------------------- +// HOOK: Response Send Handler +// If an other Hook has set HookVarList["CacheCategory"] then the Conntent +// in hh->yresult should be cached into a file. +// Remeber: url, filename, mimetype, category, createdate +//----------------------------------------------------------------------------- +THandleStatus CmodCache::Hook_SendResponse(CyhookHandler *hh) +{ + hh->status = HANDLED_NONE; + std::string url = hh->UrlData["fullurl"]; + log_level_printf(4,"mod_cache hook start url:%s\n",url.c_str()); + + std::string category = hh->HookVarList["CacheCategory"]; + if(!(hh->HookVarList["CacheCategory"]).empty()) // Category set = cache it + { + AddToCache(hh, url, hh->yresult, hh->HookVarList["CacheMimeType"], category); // create cache file and add to cache list + hh->ContentLength = (hh->yresult).length(); + hh->SendFile(CacheList[url].filename); // Send as file + hh->ResponseMimeType = CacheList[url].mime_type; // remember mime + } + else if(hh->UrlData["path"] == "/y/") // /y/ commands + { + hh->status = HANDLED_READY; + if(hh->UrlData["filename"] == "cache-info") + yshowCacheInfo(hh); + else if(hh->UrlData["filename"] == "cache-clear") + yCacheClear(hh); + else + hh->status = HANDLED_CONTINUE; // y-calls can be implemented anywhere + } + log_level_printf(4,"mod_cache hook end status:%d\n",(int)hh->status); + + return hh->status; +} + +//----------------------------------------------------------------------------- +// HOOK: Hook_ReadConfig +// This hook ist called from ReadConfig +//----------------------------------------------------------------------------- +THandleStatus CmodCache::Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList) +{ + cache_directory= Config->getString("mod_cache.cache_directory", CACHE_DIR); + ConfigList["mod_cache.cache_directory"] = cache_directory; + return HANDLED_CONTINUE; +} + +//------------------------------------------------------------------------- +// Build and Add a cache item +//------------------------------------------------------------------------- +void CmodCache::AddToCache(CyhookHandler *hh, std::string url, std::string content, std::string mime_type, std::string category) +{ + FILE *fd = NULL; + pthread_mutex_lock(&mutex); + std::string filename = cache_directory + "/" + itoa(CacheList.size()); // build cache filename + mkdir(cache_directory.c_str(), 0777); // Create Cache directory + if((fd = fopen(filename.c_str(),"w")) != NULL) // open file + { + if(fwrite(content.c_str(), content.length(), 1, fd) == 1) // write cache file + { + CacheList[url].filename = filename; // add cache data item + CacheList[url].mime_type = mime_type; + CacheList[url].category = category; + CacheList[url].created = time(NULL); + std::string test = CacheList[url].filename; + } + fflush(fd); // flush and close file + fclose(fd); + } + pthread_mutex_unlock(&mutex); // Free +} +//------------------------------------------------------------------------- +// Delete URL from cachelist +//------------------------------------------------------------------------- +void CmodCache::RemoveURLFromCache(std::string url) +{ + pthread_mutex_lock(&mutex); // yeah, its mine + if(CacheList.find(url) != CacheList.end()) + { + remove((CacheList[url].filename).c_str()); // delete file + CacheList.erase(url); // remove from list + } + pthread_mutex_unlock(&mutex); // Free +} +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- +void CmodCache::RemoveCategoryFromCache(std::string category) +{ + pthread_mutex_lock(&mutex); + bool restart = false; + do + { + restart = false; + TCacheList::iterator i = CacheList.begin(); + for ( ; i!= CacheList.end(); i++ ) + { + TCache *item = &((*i).second); + if(item->category == category) + { + CacheList.erase( ((*i).first) ); + restart = true; + break; + } + } + } + while(restart); + pthread_mutex_unlock(&mutex); +} + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +void CmodCache::DeleteCache(void) +{ + pthread_mutex_lock(&mutex); // yeah, its mine + CacheList.clear(); // Clear entire list + pthread_mutex_unlock(&mutex); // Free +} + +//------------------------------------------------------------------------- +// y-Call : show cache Information +//------------------------------------------------------------------------- +void CmodCache::yshowCacheInfo(CyhookHandler *hh) +{ + std::string yresult; + + hh->SendHTMLHeader("Cache Information"); + yresult += string_printf("Cache Information
    \n\n"); + yresult += string_printf("Cache Module...: %s
    \n",(getHookName()).c_str() ); + yresult += string_printf("Cache Version..: %s
    \n",(getHookVersion()).c_str() ); + yresult += string_printf("Cache Directory: %s
    \n",cache_directory.c_str() ); + yresult += string_printf("
    \n
    CACHE
    \n"); + + // cache list + yresult += string_printf("\n"); + yresult += string_printf("\n"); + pthread_mutex_lock(&mutex); + TCacheList::iterator i = CacheList.begin(); + for ( ; i!= CacheList.end(); i++ ) + { + TCache *item = &((*i).second); + char timeStr[80]; + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&(item->created)) ); + yresult += string_printf("" \ + "\n", + ((*i).first).c_str(), + item->mime_type.c_str(), + item->filename.c_str(), + item->category.c_str(), + timeStr, + ((*i).first).c_str(), + item->category.c_str() + ); + } + pthread_mutex_unlock(&mutex); // Free + yresult += string_printf("
    URLMimeFilenameCategoryCreatedRemove
    %s%s%s%s%surl " \ + "category
    \n"); + yresult += string_printf("Delete Cache
    \n"); + hh->addResult(yresult, HANDLED_READY); + hh->SendHTMLFooter(); +} +//------------------------------------------------------------------------- +// y-Call : clear cache +//------------------------------------------------------------------------- +void CmodCache::yCacheClear(CyhookHandler *hh) +{ + std::string result=""; + if(hh->ParamList["category"] != "") + { + RemoveCategoryFromCache(hh->ParamList["category"]); + result = string_printf("Category (%s) removed from cache.
    ", hh->ParamList["category"].c_str()); + } + else if(hh->ParamList["url"] != "") + { + RemoveURLFromCache(hh->ParamList["url"]); + result = string_printf("URL (%s) removed from cache.
    ", hh->ParamList["url"].c_str()); + } + else + { + DeleteCache(); + result = string_printf("Cache deleted.
    "); + } + hh->SendHTMLHeader("Cache deletion"); + hh->WriteLn(result); + hh->SendHTMLFooter(); + +} + diff --git a/src/nhttpd/yhttpd_mods/mod_cache.h b/src/nhttpd/yhttpd_mods/mod_cache.h new file mode 100644 index 000000000..c953163d6 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_cache.h @@ -0,0 +1,56 @@ +//============================================================================= +// YHTTPD +// CacheManager +//----------------------------------------------------------------------------- +//============================================================================= +#ifndef __yhttpd_mod_cache_h__ +#define __yhttpd_mod_cache_h__ + +// system +#include +// c++ +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "yhook.h" + +//----------------------------------------------------------------------------- +typedef struct +{ + std::string filename; + std::string mime_type; + std::string category; + time_t created; +} TCache; + +typedef std::map TCacheList; + +//----------------------------------------------------------------------------- +class CmodCache : public Cyhook +{ +private: + static TCacheList CacheList; + std::string cache_directory; + void yshowCacheInfo(CyhookHandler *hh); + void yCacheClear(CyhookHandler *hh); +public: + static pthread_mutex_t mutex; + + CmodCache(){}; + ~CmodCache(void){}; + + void AddToCache(CyhookHandler *hh, std::string url, std::string content, std::string mime_type, std::string cartegory="none"); + static void RemoveURLFromCache(std::string url); + static void RemoveCategoryFromCache(std::string category); + static void DeleteCache(void); + + // Hooks + virtual THandleStatus Hook_PrepareResponse(CyhookHandler *hh); + virtual THandleStatus Hook_SendResponse(CyhookHandler *hh); + virtual std::string getHookVersion(void) {return std::string("$Revision: 1.1 $");} + virtual std::string getHookName(void) {return std::string("mod_cache");} + virtual THandleStatus Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList); +}; + +#endif /* __yhttpd_mod_cache_h__ */ diff --git a/src/nhttpd/yhttpd_mods/mod_sendfile.cpp b/src/nhttpd/yhttpd_mods/mod_sendfile.cpp new file mode 100644 index 000000000..d355f4634 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_sendfile.cpp @@ -0,0 +1,214 @@ +//============================================================================= +// YHTTPD +// Module: SendFile (mod_sendfile) +//----------------------------------------------------------------------------- +// Send a File (main) with given path and filename. +// It procuced a Response-Header (SendHeader). +// It supports Client caching mechanism "If-Modified-Since". +//----------------------------------------------------------------------------- +// RFC 2616 / 14.25 If-Modified-Since +// +// The If-Modified-Since request-header field is used with a method to +// make it conditional: if the requested variant has not been modified +// since the time specified in this field, an entity will not be +// returned from the server; instead, a 304 (not modified) response will +// be returned without any message-body. +// +// If-Modified-Since = "If-Modified-Since" ":" HTTP-date +// An example of the field is: +// +// If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT +// +// A GET method with an If-Modified-Since header and no Range header +// requests that the identified entity be transferred only if it has +// been modified since the date given by the If-Modified-Since header. +// The algorithm for determining this includes the following cases: +// +// a) If the request would normally result in anything other than a +// 200 (OK) status, or if the passed If-Modified-Since date is +// invalid, the response is exactly the same as for a normal GET. +// A date which is later than the server's current time is +// invalid. +// +// b) If the variant has been modified since the If-Modified-Since +// date, the response is exactly the same as for a normal GET. +// +// c) If the variant has not been modified since a valid If- +// Modified-Since date, the server SHOULD return a 304 (Not +// Modified) response. +// +// yjogol: ASSUMPTION Date-Format is ONLY RFC 1123 compatible! +//============================================================================= + +// system +#include +#include +#include +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "helper.h" +#include "mod_sendfile.h" + +//============================================================================= +// Initialization of static variables +//============================================================================= +CStringList CmodSendfile::sendfileTypes; + +//============================================================================= +// Constructor & Destructor & Initialization +//============================================================================= + +//----------------------------------------------------------------------------- +// HOOK: Response Prepare Handler +// Response Prepare Check. +//----------------------------------------------------------------------------- +THandleStatus CmodSendfile::Hook_PrepareResponse(CyhookHandler *hh) +{ + hh->status = HANDLED_NONE; + + int filed; + log_level_printf(4,"mod_sendfile prepare hook start url:%s\n",hh->UrlData["fullurl"].c_str()); + std::string mime = sendfileTypes[hh->UrlData["fileext"]]; + if(mime != "") + { + //TODO: Check allowed directories / actually in GetFileName + // build filename + std::string fullfilename = GetFileName(hh, hh->UrlData["path"], hh->UrlData["filename"]); + + if( (filed = OpenFile(hh, fullfilename) ) != -1 ) //can access file? + { + struct stat statbuf; + hh->LastModified = (time_t)0; + // It is a regular file? + fstat(filed,&statbuf); + if (S_ISREG(statbuf.st_mode)) + { + // get file size and modify date + hh->ContentLength = statbuf.st_size; + hh->LastModified = statbuf.st_mtime; + } + close(filed); + + // check If-Modified-Since + time_t if_modified_since = (time_t)-1; + if(hh->HeaderList["If-Modified-Since"] != "") + { + struct tm mod; + if(strptime(hh->HeaderList["If-Modified-Since"].c_str(), RFC1123FMT, &mod) != NULL) + { + mod.tm_isdst = 0; // daylight saving flag! + if_modified_since = mktime(&mod); + } + } + + // normalize obj_last_modified to GMT + struct tm *tmp = gmtime(&(hh->LastModified)); + time_t LastModifiedGMT = mktime(tmp); + bool modified = (if_modified_since == (time_t)-1) || (if_modified_since < LastModifiedGMT); + + // Send normal or not-modified header + if(modified) + { + hh->SendFile(fullfilename); + hh->ResponseMimeType = mime; + } + else + hh->SetHeader(HTTP_NOT_MODIFIED, mime, HANDLED_READY); + } + else + { + aprintf("mod_sendfile: File not found. url:(%s)\n", hh->UrlData["url"].c_str()); + hh->SetError(HTTP_NOT_FOUND); + } + } + log_level_printf(4,"mod_sendfile prepare hook end status:%d\n",(int)hh->status); + + return hh->status; +} + +//----------------------------------------------------------------------------- +// HOOK: Hook_ReadConfig +// This hook ist called from ReadConfig +//----------------------------------------------------------------------------- +THandleStatus CmodSendfile::Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList) +{ + std::string exttypes = Config->getString("mod_sendfile.mime_types", HTTPD_SENDFILE_EXT); + ConfigList["mod_sendfile.mime_types"] = exttypes; + + bool ende = false; + std::string item, ext, mime; + sendfileTypes.clear(); + while(!ende) + { + if(!ySplitStringExact(exttypes,",",item,exttypes)) + ende = true; + if(ySplitStringExact(item,":",ext,mime)) + { + ext = trim(ext); + sendfileTypes[ext] = trim(mime); + } + } + return HANDLED_CONTINUE; +} + +//----------------------------------------------------------------------------- +// Send File: Build Filename +// First Look at PublicDocumentRoot than PrivateDocumentRoot than pure path +//----------------------------------------------------------------------------- +std::string CmodSendfile::GetFileName(CyhookHandler *hh, std::string path, std::string filename) +{ + std::string tmpfilename; + if(path[path.length()-1] != '/') + tmpfilename = path + "/" + filename; + else + tmpfilename = path + filename; + + if( access(std::string(hh->WebserverConfigList["PublicDocumentRoot"] + tmpfilename).c_str(),4) == 0) + tmpfilename = hh->WebserverConfigList["PublicDocumentRoot"] + tmpfilename; + else if(access(std::string(hh->WebserverConfigList["PrivatDocumentRoot"] + tmpfilename).c_str(),4) == 0) + tmpfilename = hh->WebserverConfigList["PrivatDocumentRoot"] + tmpfilename; +#ifdef Y_CONFIG_FEATUE_SENDFILE_CAN_ACCESS_ALL + else if(access(tmpfilename.c_str(),4) == 0) + ; +#endif + else + { + return ""; + } + return tmpfilename; +} +//----------------------------------------------------------------------------- +// Send File: Open File and check file type +//----------------------------------------------------------------------------- +int CmodSendfile::OpenFile(CyhookHandler *hh, std::string fullfilename) +{ + int fd= -1; + std::string tmpstring; + if(fullfilename.length() > 0) + { + fd = open( fullfilename.c_str(), O_RDONLY ); + if (fd<=0) + { + aprintf("cannot open file %s: ", fullfilename.c_str()); + dperror(""); + } + } + return fd; +} +//----------------------------------------------------------------------------- +// Send File: Determine MIME-Type fro File-Extention +//----------------------------------------------------------------------------- +std::string CmodSendfile::GetContentType(std::string ext) +{ + std::string ctype = "text/plain"; + ext = string_tolower(ext); + for (unsigned int i = 0;i < (sizeof(MimeFileExtensions)/sizeof(MimeFileExtensions[0])); i++) + if (MimeFileExtensions[i].fileext == ext) + { + ctype = MimeFileExtensions[i].mime; + break; + } + return ctype; +} diff --git a/src/nhttpd/yhttpd_mods/mod_sendfile.h b/src/nhttpd/yhttpd_mods/mod_sendfile.h new file mode 100644 index 000000000..1a2773856 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_sendfile.h @@ -0,0 +1,39 @@ +//============================================================================= +// YHTTPD +// SendFile +//----------------------------------------------------------------------------- +//============================================================================= +#ifndef __yhttpd_mod_sendfile_h__ +#define __yhttpd_mod_sendfile_h__ + +// system +#include +// c++ +#include +// yhttpd +#include "yconfig.h" +#include "ytypes_globals.h" +#include "yhook.h" + +//----------------------------------------------------------------------------- +class CmodSendfile : public Cyhook +{ +private: +protected: + int OpenFile(CyhookHandler *hh, std::string fullfilename); + std::string GetFileName(CyhookHandler *hh, std::string path, std::string filename); + std::string GetContentType(std::string ext); + static CStringList sendfileTypes; +public: + CmodSendfile(){}; + ~CmodSendfile(void){}; + + // Hooks + virtual THandleStatus Hook_PrepareResponse(CyhookHandler *hh); +// virtual THandleStatus Hook_SendResponse(CyhookHandler *hh); + virtual std::string getHookName(void) {return std::string("mod_sendfile");} + virtual std::string getHookVersion(void) {return std::string("$Revision: 1.1 $");} + virtual THandleStatus Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList); +}; + +#endif /* __yhttpd_mod_sendfile_h__ */ diff --git a/src/nhttpd/yhttpd_mods/mod_testhook.cpp b/src/nhttpd/yhttpd_mods/mod_testhook.cpp new file mode 100644 index 000000000..6f3195203 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_testhook.cpp @@ -0,0 +1,17 @@ +//============================================================================= +// YHTTPD +// TestHook +//============================================================================= + +#include "mod_testhook.h" + +THandleStatus CTesthook::Hook_SendResponse(CyhookHandler *hh) +{ + THandleStatus status = HANDLED_NONE; + if(hh->UrlData["filename"] == "test2") + { + hh->yresult = "test it 2222\n"; + status = HANDLED_READY; + } + return status; +} diff --git a/src/nhttpd/yhttpd_mods/mod_testhook.h b/src/nhttpd/yhttpd_mods/mod_testhook.h new file mode 100644 index 000000000..a92f9ed98 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_testhook.h @@ -0,0 +1,21 @@ +//============================================================================= +// YHTTPD +// TestHook +//============================================================================= +#ifndef TESTHOOK_H_ +#define TESTHOOK_H_ + + +#include "yhook.h" +class CTesthook : public Cyhook +{ +public: + THandleStatus Hook_SendResponse(CyhookHandler *hh); + CTesthook(){}; + ~CTesthook(){}; + + virtual std::string getHookName(void) {return std::string("Testhook");} + +}; +#endif /*TESTHOOK_H_*/ + diff --git a/src/nhttpd/yhttpd_mods/mod_weblog.cpp b/src/nhttpd/yhttpd_mods/mod_weblog.cpp new file mode 100644 index 000000000..41cc37230 --- /dev/null +++ b/src/nhttpd/yhttpd_mods/mod_weblog.cpp @@ -0,0 +1,364 @@ +//============================================================================= +// YHTTPD +// mod_weblog : Logging of HTTPD-Requests/Responses +//----------------------------------------------------------------------------- +// Normally +//============================================================================= +#include +#include +#include + +#include "mod_weblog.h" +#include "helper.h" + +//============================================================================= +// Initialization of static variables +//============================================================================= + pthread_mutex_t CmWebLog::WebLog_mutex = PTHREAD_MUTEX_INITIALIZER; + FILE *CmWebLog::WebLogFile = NULL; + int CmWebLog::RefCounter = 0; + std::string CmWebLog::WebLogFilename=LOG_FILE; + std::string CmWebLog::LogFormat =LOG_FORMAT; +//============================================================================= +// Constructor & Destructor +//============================================================================= +CmWebLog::CmWebLog(void) +{ + pthread_mutex_lock(&WebLog_mutex); // yea, its mine + RefCounter++; + pthread_mutex_unlock(&WebLog_mutex); +} +//----------------------------------------------------------------------------- +CmWebLog::~CmWebLog(void) +{ + pthread_mutex_lock(&WebLog_mutex); // yea, its mine + --RefCounter; + if(RefCounter <= 0) + CloseLogFile(); + pthread_mutex_unlock(&WebLog_mutex); +} +//============================================================================= +// HOOKS +//============================================================================= +//----------------------------------------------------------------------------- +// HOOK: Hook_EndConnection +//----------------------------------------------------------------------------- +THandleStatus CmWebLog::Hook_EndConnection(CyhookHandler *hh) +{ + if(LogFormat == "CLF") + AddLogEntry_CLF(hh); + else if(LogFormat == "ELF") + AddLogEntry_ELF(hh); + return HANDLED_CONTINUE; // even on Log-Error: continue +} + +//----------------------------------------------------------------------------- +// HOOK: Hook_ReadConfig +// This hook ist called from ReadConfig +//----------------------------------------------------------------------------- +THandleStatus CmWebLog::Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList) +{ + LogFormat = Config->getString("mod_weblog.log_format", LOG_FORMAT); + WebLogFilename = Config->getString("mod_weblog.logfile", LOG_FILE); + return HANDLED_CONTINUE; +} +//----------------------------------------------------------------------------- +bool CmWebLog::OpenLogFile() +{ + if(WebLogFilename == "") + return false; + if(WebLogFile == NULL) + { + bool isNew = false; + pthread_mutex_lock(&WebLog_mutex); // yeah, its mine + if(access(WebLogFilename.c_str(), 4) != 0) + isNew = true; + WebLogFile = fopen(WebLogFilename.c_str(),"a"); + if(isNew) + { + if(LogFormat == "ELF") + { + printf("#Version: 1.0\n"); + printf("#Remarks: yhttpd" WEBSERVERNAME "\n"); + printf("#Fields: c-ip username date time x-request cs-uri sc-status cs-method bytes time-taken x-time-request x-time-response cached\n"); + } + } + pthread_mutex_unlock(&WebLog_mutex); + } + return (WebLogFile != NULL); +} +//----------------------------------------------------------------------------- +void CmWebLog::CloseLogFile() +{ + if(WebLogFile != NULL) + { + pthread_mutex_lock(&WebLog_mutex); // yeah, its mine + fclose(WebLogFile); + WebLogFile = NULL; + pthread_mutex_unlock(&WebLog_mutex); + } +} +//----------------------------------------------------------------------------- +#define bufferlen 1024*8 +bool CmWebLog::printf(const char *fmt, ...) +{ + if(!OpenLogFile()) + return false; + bool success = false; + char buffer[bufferlen]; + if(WebLogFile != NULL) + { + pthread_mutex_lock(&WebLog_mutex); // yeah, its mine + va_list arglist; + va_start( arglist, fmt ); + if(arglist) + vsnprintf( buffer, bufferlen, fmt, arglist ); + va_end(arglist); + unsigned int len = strlen(buffer); + success = (fwrite(buffer, len, 1, WebLogFile) == len); + fflush(WebLogFile); + pthread_mutex_unlock(&WebLog_mutex); + } + return success; +} + +//----------------------------------------------------------------------------- +// CLF - Common Logfile Format +// +// Example: 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 +// +// The common logfile format is as follows: +// remotehost rfc931 authuser [date] "request" status bytes +// +// remotehost: Remote hostname (or IP number if DNS hostname is not available, or if DNSLookup is Off. +// rfc931: The remote logname of the user. +// authuser: The username as which the user has authenticated himself. +// [date]: Date and time of the request. +// "request": The request line exactly as it came from the client. +// status: The HTTP status code returned to the client. +// bytes: The content-length of the document transferred. +//----------------------------------------------------------------------------- +void CmWebLog::AddLogEntry_CLF(CyhookHandler *hh) +{ + std::string cs_method; + switch (hh->Method) + { + case M_GET: cs_method = "GET"; break; + case M_POST: cs_method = "POST"; break; + case M_HEAD: cs_method = "HEAD"; break; + + default: + cs_method = "unknown"; + break; + } + std::string c_ip = hh->UrlData["clientaddr"].c_str(); + std::string request_startline = hh->UrlData["startline"].c_str(); + int s_status = hh->httpStatus; + int bytes = hh->GetContentLength(); + + struct tm *time_now; + time_t now = time(NULL); + char request_time[80]; + + time_now = localtime(&now); + strftime(request_time, 80, "[%d/%b/%Y:%H:%M:%S]", time_now); + + printf("%s - - %s \"%s\" %d %d\n", + c_ip.c_str(), + request_time, + request_startline.c_str(), + s_status, + bytes); +} + +//----------------------------------------------------------------------------- +/* + Definition: (ELF) Extended Log File Format + W3C Working Draft WD-logfile-960323 (http://www.w3.org/pub/WWW/TR/WD-logfile-960323.html) + + An extended log file contains a sequence of lines containing ASCII + characters terminated by either the sequence LF or CRLF. + Log file generators should follow the line termination convention for + the platform on which they are executed. Analyzers should accept either form. + Each line may contain either a directive or an entry. + + Entries consist of a sequence of fields relating to a single HTTP + transaction. Fields are separated by whitespace, the use of tab characters + for this purpose is encouraged. If a field is unused in a particular + entry dash "-" marks the omitted field. Directives record information about + the logging process itself. + + Lines beginning with the # character contain directives. + The following directives are defined: + + - Version: . + The version of the extended log file format used. + This draft defines version 1.0. + - Fields: [...] + Specifies the fields recorded in the log. + - Software: string + Identifies the software which generated the log. + - Start-Date:

    Xgb}1r)Qz* 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�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 bwij`J!r}_r?O4z{hri0--{Sj;Z!i7{Rcs#| literal 0 HcmV?d00001 diff --git a/data/icons/radiomode.jpg b/data/icons/radiomode.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0113c51ea67ac3f3721f000f72639f59eb9d60f3 GIT binary patch literal 193824 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