]> www.vanbest.org Git - sasc-ng.git/commitdiff
sasc: initial import of opensasc-ng R77
authorleslie <unknown>
Tue, 21 Jul 2009 05:17:51 +0000 (13:17 +0800)
committerleslie <unknown>
Tue, 21 Jul 2009 05:17:51 +0000 (13:17 +0800)
282 files changed:
contrib/sasc-ng/FFdecsa/COPYING [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/ChangeLog [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/FFdecsa.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/FFdecsa.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/FFdecsa_test.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/FFdecsa_test_testcases.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/Makefile [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/README [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/FAQ.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/how_to_compile.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/how_to_release.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/how_to_understand.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/how_to_use.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/docs/technical_background.txt [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/fftable.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/logic/Makefile [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/logic/logic.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_032_4char.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_032_4charA.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_032_int.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_064_2int.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_064_8char.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_064_8charA.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_064_long.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_064_mmx.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_16char.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_16charA.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_2long.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_2mmx.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_4int.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_sse.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_128_sse2.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_generic.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/parallel_std_def.h [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/stream.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_FFdecsa.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_stream.c [new file with mode: 0644]
contrib/sasc-ng/FFdecsa/vdr_patches/README_vdr.txt [new file with mode: 0644]
contrib/sasc-ng/Makefile [new file with mode: 0644]
contrib/sasc-ng/configure [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/plugin_cam.c [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/plugin_cam.h [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/plugin_ffdecsa.c [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/plugin_msg.h [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/plugin_scan.c [new file with mode: 0644]
contrib/sasc-ng/dvblb_plugins/scanwrap.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/COPYING [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/README [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/Makefile [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/compat.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config-dvb/Makefile [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.14.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.18.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.5.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.v4l.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/config_dvb.pl [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvb_loopback.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.14.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.18.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.v4l.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvblb_forward.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvblb_internal.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvblb_proc.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/module/dvbloopback.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/Makefile [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/debug.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/forward.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/list.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/messages.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/msg_passing.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/msg_passing.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_dss.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_dummy.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_getsid.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_getsid.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_legacysw.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.h [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/plugin_showioctl.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/process_req.c [new file with mode: 0644]
contrib/sasc-ng/dvbloopback/src/process_req.h [new file with mode: 0644]
contrib/sasc-ng/objs/.empty [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/COPYING [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/HISTORY [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile.system [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/README [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/README.po2i18n [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/common.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto-bn.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/diff.exclude [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Ird-Beta.KID [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Seca.KID [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/SoftCam.Key [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Viaccess.KID [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/cardclient.conf.example [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/dialup.sh.example [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/externalau.sh.example [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/smartcard.conf.example [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/helper.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n-template.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-core.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sc.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sys.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/openssl-compat.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/opts.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx-old.diff [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx.diff [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-sct-cc.diff [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/vdr-1.4.x-sc7.diff [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/de_DE.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fi_FI.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fr_FR.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/hu_HU.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/it_IT.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/nl_NL.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/pl_PL.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/ru_RU.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/sv_SE.po [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/po2i18n.pl [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/scsetup.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes_core.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_cbc.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_skey.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea_lcl.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems-pre/.empty [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/aroureos.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/camd.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/gbox.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/newcamd.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/radegast.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/debug.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/log-nagra.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra-def.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra1.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0101.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0501.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-4101.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-map57.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/log-viaccess.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/opentv.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.mk [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/Makefile [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.h [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/filterhelper.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testECM.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testEMM.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN1Emu.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2Emu.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2RunEmu.c [new file with mode: 0644]
contrib/sasc-ng/sc/PLUGINS/src/sc-src/version.h [new file with mode: 0644]
contrib/sasc-ng/sc/device.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/dload.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/dvbdevice.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/include/asm/unaligned.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/libsi/descriptor.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/libsi/headers.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/libsi/section.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/libsi/si.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/libsi/util.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/channels.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/ci.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/config.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/csa.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/device.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/dload.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/dvbdevice.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/dvbspu.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/eit.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/epg.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/filter.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/font.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/i18n.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/interface.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/keys.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/menu.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/menuitems.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/nit.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/osd.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/osdbase.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/pat.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/player.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/plugin.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/recording.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/remote.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/sclink.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/sdt.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/sections.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/skins.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/sources.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/spu.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/status.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/svdrp.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/themes.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/thread.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/timers.h [new file with mode: 0644]
contrib/sasc-ng/sc/include/vdr/tools.h [new file with mode: 0644]
contrib/sasc-ng/sc/libsi/descriptor.c [new file with mode: 0644]
contrib/sasc-ng/sc/libsi/section.c [new file with mode: 0644]
contrib/sasc-ng/sc/libsi/si.c [new file with mode: 0644]
contrib/sasc-ng/sc/libsi/util.c [new file with mode: 0644]
contrib/sasc-ng/sc/log.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/menuitems.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/misc.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/osdbase.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/othercamd.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/othercamd.h [new file with mode: 0644]
contrib/sasc-ng/sc/sasccam.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/sasccam.h [new file with mode: 0644]
contrib/sasc-ng/sc/thread.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/tools.cpp [new file with mode: 0644]
contrib/sasc-ng/sc/vdrcompat.cpp [new file with mode: 0644]

diff --git a/contrib/sasc-ng/FFdecsa/COPYING b/contrib/sasc-ng/FFdecsa/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       Appendix: 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+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) 19yy 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.
+
+  <signature of Ty Coon>, 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/contrib/sasc-ng/FFdecsa/ChangeLog b/contrib/sasc-ng/FFdecsa/ChangeLog
new file mode 100644 (file)
index 0000000..d1c4f0c
--- /dev/null
@@ -0,0 +1,206 @@
+- created
+
+- released 0.0.1
+
+- simplified s, A, B
+
+- released 0.0.2
+
+- simplified nxt=
+
+- released 0.0.3
+
+- removed commented code
+- code formatting
+
+- released 0.0.4
+
+- kk now unsigned char
+- removed 64 bit ints
+
+- released 0.0.5
+
+- created decrypt_2ts
+
+- released 0.0.6
+
+- renamed files
+- created decrypt_many_ts, removed others
+- external interface has 2 functions only: set_cws() and decrypt_many_ts()
+- reformatted code
+- reimplemented s12,s34,s56,s7
+- unsigned char become int for table optimization
+
+- released 0.0.7
+
+- optional icc compiler
+- kk now 0..55
+- decrypt_many_ts really works (no parallelism yet)
+- added get_cws() to interface
+- created stream.c
+- created key_schedule_stream, using iA[] and iB[]
+
+- released 0.0.8
+
+- decrypt_many_ts() makes a group, sorts the packets, processes them
+- preliminar stream_cypher_group() created
+- parallel computing activated
+- huge speed increase (+500%) thanks to stream_cypher_group()
+
+- released 0.0.9
+
+- block_cypher_group() created (no parallelism yet)
+
+- released 0.0.10
+
+- block_cypher_group() has 56 simple iterations
+- block_cypher_group() doesn't shift registers anymore
+
+- released 0.0.11
+
+- some parallelization on block_cypher_group()
+
+- released 0.0.12
+
+- better parallelization of block_cypher_group()
+
+- released 0.0.13
+
+- block_cypher() was still called by error when N=23
+- speed is now 109Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.14
+
+- stream_cypher_group() has a init and normal variant
+- A[0]-A[9] instead of A[1]-A[10], same for B
+- implemented virtual shift of A and B
+- speed is now 117Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.15
+
+- better optimization of E and F in the stream cypher
+- speed is now 119Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.16
+
+- removed some debug overhead
+- speed is now 120Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.17
+
+- don't move packets with residue anymore
+- speed is now 123Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.18
+
+- solved alignment problems
+- search groupable packets even beyond ungroupable ones
+  (more speed in some real world cases)
+- created decrypt_many_ts2(), useful with circular buffers
+
+- released 0.0.19
+
+- removed old code
+
+- released 0.0.20
+
+- partially converted code to size-independent group
+- icc doesn't work with optimizations on
+
+- released 0.1.1
+
+- merge loops on block_decypher (speed++ gcc, speed-- icc)
+- transposition are now functions (speed-- icc)
+- icc works again (compiler bug work around?)
+
+- released 0.1.2
+
+- better use of COPY8 &co
+- better flags for gcc
+- removed old code
+
+- released 0.1.3
+
+- int and not char in block cypher (speed++++++ gcc, speed-- icc)
+
+- released 0.1.4
+
+- group abstraction finally implemented
+- support for group width 64
+
+- released 0.1.5
+
+- group 64 mmx implemented (speed++ gcc)
+
+- released 0.1.6
+
+- more parallelism in block cypher (speed++ gcc)
+- transposition before and after block (disabled because of no speed gain yet)
+
+- released 0.1.7
+
+- more parallelism in block cypher (speed++ gcc)
+- transposition before and after block enabled (speed++ gcc)
+- gcc options (unrolled 500) speed gcc++
+
+- released 0.1.8
+
+- reworked FFN_ALL_* constants (speed++++ gcc) 
+
+- released 0.1.9
+
+- transposition in block as inlined functions
+- group abstraction working well
+
+- released 0.1.10
+
+- group 128 sse implemented, but batch is 64 mmx (not faster than group 64 mmx)
+
+- released 0.1.11
+
+- lot of code polishing and dead code elimination
+- better and more debug output
+
+- released 0.1.12
+
+- name change: FFdecsa
+
+- released 0.2.0
+
+- separated test cases
+- corrected all group_modes (now called parallel_modes)
+- parallel 128 8 char implemented
+- parallel 64 long implemented
+- parallel 128 2 long implemented
+- parallel 128 2 mmx implemented (incredibly slow, the compiler is very confused)
+- parallel 128 16 charA implemented (very slow compilation)
+- parallel 128 16 char implemented
+- renamed softcsa* to FFdecsa*
+
+- released 0.2.1
+
+- new external interface (based on ranges)
+
+- released 0.2.2
+
+- can be compiled with g++ too
+- using g++ the code is 3% faster!
+- external interface: function name changing and new functions
+- a group of ranges is now called a cluster
+- renamed autogenerated files
+
+- released 0.2.3
+
+- written docs
+- removed unneeded files
+- added Copyright and license notes
+- reworked "logic"
+
+- released 0.3.0
+
+- Makefile reworked
+- misc fixes
+- added vdr patch
+
+- released 1.0.0 (public release)
+
diff --git a/contrib/sasc-ng/FFdecsa/FFdecsa.c b/contrib/sasc-ng/FFdecsa/FFdecsa.c
new file mode 100644 (file)
index 0000000..8ba7322
--- /dev/null
@@ -0,0 +1,878 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "FFdecsa.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+//#define DEBUG
+#ifdef DEBUG
+#define DBG(a) a
+#else
+#define DBG(a)
+#endif
+
+//// parallelization stuff, large speed differences are possible
+// possible choices
+#define PARALLEL_32_4CHAR     320
+#define PARALLEL_32_4CHARA    321
+#define PARALLEL_32_INT       322
+#define PARALLEL_64_8CHAR     640
+#define PARALLEL_64_8CHARA    641
+#define PARALLEL_64_2INT      642
+#define PARALLEL_64_LONG      643
+#define PARALLEL_64_MMX       644
+#define PARALLEL_128_16CHAR  1280
+#define PARALLEL_128_16CHARA 1281
+#define PARALLEL_128_4INT    1282
+#define PARALLEL_128_2LONG   1283
+#define PARALLEL_128_2MMX    1284
+#define PARALLEL_128_SSE     1285
+#define PARALLEL_128_SSE2    1286
+
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+#ifndef PARALLEL_MODE
+#define PARALLEL_MODE PARALLEL_32_INT
+#endif
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+
+#include "parallel_generic.h"
+//// conditionals
+#if PARALLEL_MODE==PARALLEL_32_4CHAR
+#include "parallel_032_4char.h"
+#elif PARALLEL_MODE==PARALLEL_32_4CHARA
+#include "parallel_032_4charA.h"
+#elif PARALLEL_MODE==PARALLEL_32_INT
+#include "parallel_032_int.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHAR
+#include "parallel_064_8char.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHARA
+#include "parallel_064_8charA.h"
+#elif PARALLEL_MODE==PARALLEL_64_2INT
+#include "parallel_064_2int.h"
+#elif PARALLEL_MODE==PARALLEL_64_LONG
+#include "parallel_064_long.h"
+#elif PARALLEL_MODE==PARALLEL_64_MMX
+#include "parallel_064_mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHAR
+#include "parallel_128_16char.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHARA
+#include "parallel_128_16charA.h"
+#elif PARALLEL_MODE==PARALLEL_128_4INT
+#include "parallel_128_4int.h"
+#elif PARALLEL_MODE==PARALLEL_128_2LONG
+#include "parallel_128_2long.h"
+#elif PARALLEL_MODE==PARALLEL_128_2MMX
+#include "parallel_128_2mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_SSE
+#include "parallel_128_sse.h"
+#elif PARALLEL_MODE==PARALLEL_128_SSE2
+#include "parallel_128_sse2.h"
+#else
+#error "unknown/undefined parallel mode"
+#endif
+
+// stuff depending on conditionals
+
+#define BYTES_PER_GROUP (GROUP_PARALLELISM/8)
+#define BYPG BYTES_PER_GROUP
+#define BITS_PER_GROUP GROUP_PARALLELISM
+#define BIPG BITS_PER_GROUP
+
+#ifndef MALLOC
+#define MALLOC(X) malloc(X)
+#endif
+#ifndef FREE
+#define FREE(X) free(X)
+#endif
+#ifndef MEMALIGN
+#define MEMALIGN
+#endif
+
+//// debug tool
+
+static void dump_mem(const char *string, const unsigned char *p, int len, int linelen){
+  int i;
+  for(i=0;i<len;i++){
+    if(i%linelen==0&&i) fprintf(stderr,"\n");
+    if(i%linelen==0) fprintf(stderr,"%s %08x:",string,i);
+    else{
+      if(i%8==0) fprintf(stderr," ");
+      if(i%4==0) fprintf(stderr," ");
+    }
+    fprintf(stderr," %02x",p[i]);
+  }
+  if(i%linelen==0) fprintf(stderr,"\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+struct csa_key_t{
+       unsigned char ck[8];
+// used by stream
+        int iA[8];  // iA[0] is for A1, iA[7] is for A8
+        int iB[8];  // iB[0] is for B1, iB[7] is for B8
+// used by stream (group)
+        MEMALIGN group ck_g[8][8]; // [byte][bit:0=LSB,7=MSB]
+        MEMALIGN group iA_g[8][4]; // [0 for A1][0 for LSB]
+        MEMALIGN group iB_g[8][4]; // [0 for B1][0 for LSB]
+// used by block
+       unsigned char kk[56];
+// used by block (group)
+       MEMALIGN batch kkmulti[56]; // many times the same byte in every batch
+};
+
+struct csa_keys_t{
+  struct csa_key_t even;
+  struct csa_key_t odd;
+};
+
+//-----stream cypher
+
+//-----key schedule for stream decypher
+static void key_schedule_stream(
+  unsigned char *ck,    // [In]  ck[0]-ck[7]   8 bytes   | Key.
+  int *iA,              // [Out] iA[0]-iA[7]   8 nibbles | Key schedule.
+  int *iB)              // [Out] iB[0]-iB[7]   8 nibbles | Key schedule.
+{
+    iA[0]=(ck[0]>>4)&0xf;
+    iA[1]=(ck[0]   )&0xf;
+    iA[2]=(ck[1]>>4)&0xf;
+    iA[3]=(ck[1]   )&0xf;
+    iA[4]=(ck[2]>>4)&0xf;
+    iA[5]=(ck[2]   )&0xf;
+    iA[6]=(ck[3]>>4)&0xf;
+    iA[7]=(ck[3]   )&0xf;
+    iB[0]=(ck[4]>>4)&0xf;
+    iB[1]=(ck[4]   )&0xf;
+    iB[2]=(ck[5]>>4)&0xf;
+    iB[3]=(ck[5]   )&0xf;
+    iB[4]=(ck[6]>>4)&0xf;
+    iB[5]=(ck[6]   )&0xf;
+    iB[6]=(ck[7]>>4)&0xf;
+    iB[7]=(ck[7]   )&0xf;
+}
+
+//----- stream main function
+
+#define STREAM_INIT
+#include "stream.c"
+#undef STREAM_INIT
+
+#define STREAM_NORMAL
+#include "stream.c"
+#undef STREAM_NORMAL
+
+
+//-----block decypher
+
+//-----key schedule for block decypher
+
+static void key_schedule_block(
+  unsigned char *ck,    // [In]  ck[0]-ck[7]   8 bytes | Key.
+  unsigned char *kk)    // [Out] kk[0]-kk[55] 56 bytes | Key schedule.
+{
+  static const unsigned char key_perm[0x40] = {
+    0x12,0x24,0x09,0x07,0x2A,0x31,0x1D,0x15, 0x1C,0x36,0x3E,0x32,0x13,0x21,0x3B,0x40,
+    0x18,0x14,0x25,0x27,0x02,0x35,0x1B,0x01, 0x22,0x04,0x0D,0x0E,0x39,0x28,0x1A,0x29,
+    0x33,0x23,0x34,0x0C,0x16,0x30,0x1E,0x3A, 0x2D,0x1F,0x08,0x19,0x17,0x2F,0x3D,0x11,
+    0x3C,0x05,0x38,0x2B,0x0B,0x06,0x0A,0x2C, 0x20,0x3F,0x2E,0x0F,0x03,0x26,0x10,0x37,
+  };
+
+  int i,j,k;
+  int bit[64];
+  int newbit[64];
+  int kb[7][8];
+
+  // 56 steps
+  // 56 key bytes kk(55)..kk(0) by key schedule from ck
+
+  // kb(6,0) .. kb(6,7) = ck(0) .. ck(7)
+  kb[6][0] = ck[0];
+  kb[6][1] = ck[1];
+  kb[6][2] = ck[2];
+  kb[6][3] = ck[3];
+  kb[6][4] = ck[4];
+  kb[6][5] = ck[5];
+  kb[6][6] = ck[6];
+  kb[6][7] = ck[7];
+
+  // calculate kb[5] .. kb[0]
+  for(i=5; i>=0; i--){
+    // 64 bit perm on kb
+    for(j=0; j<8; j++){
+      for(k=0; k<8; k++){
+        bit[j*8+k] = (kb[i+1][j] >> (7-k)) & 1;
+        newbit[key_perm[j*8+k]-1] = bit[j*8+k];
+      }
+    }
+    for(j=0; j<8; j++){
+      kb[i][j] = 0;
+      for(k=0; k<8; k++){
+        kb[i][j] |= newbit[j*8+k] << (7-k);
+      }
+    }
+  }
+
+  // xor to give kk
+  for(i=0; i<7; i++){
+    for(j=0; j<8; j++){
+      kk[i*8+j] = kb[i][j] ^ i;
+    }
+  }
+
+}
+
+//-----block utils
+
+static inline __attribute__((always_inline)) void trasp_N_8 (unsigned char *in,unsigned char* out,int count){
+  int *ri=(int *)in;
+  int *ibi=(int *)out;
+  int j,i,k,g;
+  // copy and first step
+  for(g=0;g<count;g++){
+    ri[g]=ibi[2*g];
+    ri[GROUP_PARALLELISM+g]=ibi[2*g+1];
+  }
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+  for(j=0;j<8;j+=4){
+    for(i=0;i<2;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+2)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x0000ffff)      | ((b           )<<16);
+        ri[INTS_PER_ROW*(j+i+2)+k]=  ((t           )>>16) |  (b&0xffff0000) ;
+      }
+    }
+  }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+  for(j=0;j<8;j+=2){
+    for(i=0;i<1;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+1)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+        ri[INTS_PER_ROW*(j+i+1)+k]=  ((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+      }
+    }
+  }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+}
+
+static inline __attribute__((always_inline)) void trasp_8_N (unsigned char *in,unsigned char* out,int count){
+  int *ri=(int *)in;
+  int *bdi=(int *)out;
+  int j,i,k,g;
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+  for(j=0;j<8;j+=2){
+    for(i=0;i<1;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+1)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+        ri[INTS_PER_ROW*(j+i+1)+k]=  ((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+      }
+    }
+  }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+  for(j=0;j<8;j+=4){
+    for(i=0;i<2;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+2)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x0000ffff)      | ((b           )<<16);
+        ri[INTS_PER_ROW*(j+i+2)+k]=  ((t           )>>16) |  (b&0xffff0000) ;
+      }
+    }
+  }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+  for(g=0;g<count;g++){
+    bdi[2*g]=ri[g];
+    bdi[2*g+1]=ri[GROUP_PARALLELISM+g];
+  }
+}
+
+//-----block main function
+
+// block group
+static void block_decypher_group(
+  batch *kkmulti,       // [In]  kkmulti[0]-kkmulti[55] 56 batches | Key schedule (each batch has repeated equal bytes).
+  unsigned char *ib,    // [In]  (ib0,ib1,...ib7)...x32 32*8 bytes | Initialization vector.
+  unsigned char *bd,    // [Out] (bd0,bd1,...bd7)...x32 32*8 bytes | Block decipher.
+  int count)
+{
+  // int is faster than unsigned char. apparently not
+  static const unsigned char block_sbox[0x100] = {
+    0x3A,0xEA,0x68,0xFE,0x33,0xE9,0x88,0x1A, 0x83,0xCF,0xE1,0x7F,0xBA,0xE2,0x38,0x12,
+    0xE8,0x27,0x61,0x95,0x0C,0x36,0xE5,0x70, 0xA2,0x06,0x82,0x7C,0x17,0xA3,0x26,0x49,
+    0xBE,0x7A,0x6D,0x47,0xC1,0x51,0x8F,0xF3, 0xCC,0x5B,0x67,0xBD,0xCD,0x18,0x08,0xC9,
+    0xFF,0x69,0xEF,0x03,0x4E,0x48,0x4A,0x84, 0x3F,0xB4,0x10,0x04,0xDC,0xF5,0x5C,0xC6,
+    0x16,0xAB,0xAC,0x4C,0xF1,0x6A,0x2F,0x3C, 0x3B,0xD4,0xD5,0x94,0xD0,0xC4,0x63,0x62,
+    0x71,0xA1,0xF9,0x4F,0x2E,0xAA,0xC5,0x56, 0xE3,0x39,0x93,0xCE,0x65,0x64,0xE4,0x58,
+    0x6C,0x19,0x42,0x79,0xDD,0xEE,0x96,0xF6, 0x8A,0xEC,0x1E,0x85,0x53,0x45,0xDE,0xBB,
+    0x7E,0x0A,0x9A,0x13,0x2A,0x9D,0xC2,0x5E, 0x5A,0x1F,0x32,0x35,0x9C,0xA8,0x73,0x30,
+
+    0x29,0x3D,0xE7,0x92,0x87,0x1B,0x2B,0x4B, 0xA5,0x57,0x97,0x40,0x15,0xE6,0xBC,0x0E,
+    0xEB,0xC3,0x34,0x2D,0xB8,0x44,0x25,0xA4, 0x1C,0xC7,0x23,0xED,0x90,0x6E,0x50,0x00,
+    0x99,0x9E,0x4D,0xD9,0xDA,0x8D,0x6F,0x5F, 0x3E,0xD7,0x21,0x74,0x86,0xDF,0x6B,0x05,
+    0x8E,0x5D,0x37,0x11,0xD2,0x28,0x75,0xD6, 0xA7,0x77,0x24,0xBF,0xF0,0xB0,0x02,0xB7,
+    0xF8,0xFC,0x81,0x09,0xB1,0x01,0x76,0x91, 0x7D,0x0F,0xC8,0xA0,0xF2,0xCB,0x78,0x60,
+    0xD1,0xF7,0xE0,0xB5,0x98,0x22,0xB3,0x20, 0x1D,0xA6,0xDB,0x7B,0x59,0x9F,0xAE,0x31,
+    0xFB,0xD3,0xB6,0xCA,0x43,0x72,0x07,0xF4, 0xD8,0x41,0x14,0x55,0x0D,0x54,0x8B,0xB9,
+    0xAD,0x46,0x0B,0xAF,0x80,0x52,0x2C,0xFA, 0x8C,0x89,0x66,0xFD,0xB2,0xA9,0x9B,0xC0,
+  };
+  MEMALIGN unsigned char r[GROUP_PARALLELISM*(8+56)];  /* 56 because we will move back in memory while looping */
+  MEMALIGN unsigned char sbox_in[GROUP_PARALLELISM],sbox_out[GROUP_PARALLELISM],perm_out[GROUP_PARALLELISM];
+  int roff;
+  int i,g,count_all=GROUP_PARALLELISM;
+
+  roff=GROUP_PARALLELISM*56;
+
+#define FASTTRASP1
+#ifndef FASTTRASP1
+  for(g=0;g<count;g++){
+    // Init registers 
+    int j;
+    for(j=0;j<8;j++){
+      r[roff+GROUP_PARALLELISM*j+g]=ib[8*g+j];
+    }
+  }
+#else
+  trasp_N_8((unsigned char *)&r[roff],(unsigned char *)ib,count);
+#endif
+//dump_mem("OLD r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+
+  // loop over kk[55]..kk[0]
+  for(i=55;i>=0;i--){
+    {
+      MEMALIGN batch tkkmulti=kkmulti[i];
+      batch *si=(batch *)sbox_in;
+      batch *r6_N=(batch *)(r+roff+GROUP_PARALLELISM*6);
+      for(g=0;g<count_all/BYTES_PER_BATCH;g++){
+        si[g]=B_FFXOR(tkkmulti,r6_N[g]);              //FIXME: introduce FASTBATCH?
+      }
+    }
+
+    // table lookup, this works on only one byte at a time
+    // most difficult part of all
+    // - can't be parallelized
+    // - can't be synthetized through boolean terms (8 input bits are too many)
+    for(g=0;g<count_all;g++){
+      sbox_out[g]=block_sbox[sbox_in[g]];
+    }
+
+    // bit permutation
+    {
+      unsigned char *po=(unsigned char *)perm_out;
+      unsigned char *so=(unsigned char *)sbox_out;
+//dump_mem("pre perm ",(unsigned char *)so,GROUP_PARALLELISM,GROUP_PARALLELISM);
+      for(g=0;g<count_all;g+=BYTES_PER_BATCH){
+        MEMALIGN batch in,out;
+        in=*(batch *)&so[g];
+
+        out=B_FFOR(
+           B_FFOR(
+           B_FFOR(
+           B_FFOR(
+           B_FFOR(
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_29()),1),
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_02()),6)),
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_04()),3)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_10()),2)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_40()),6)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_80()),4));
+
+        *(batch *)&po[g]=out;
+      }
+//dump_mem("post perm",(unsigned char *)po,GROUP_PARALLELISM,GROUP_PARALLELISM);
+    }
+
+    roff-=GROUP_PARALLELISM; /* virtual shift of registers */
+
+#if 0
+/* one by one */
+    for(g=0;g<count_all;g++){
+      r[roff+GROUP_PARALLELISM*0+g]=r[roff+GROUP_PARALLELISM*8+g]^sbox_out[g];
+      r[roff+GROUP_PARALLELISM*6+g]^=perm_out[g];
+      r[roff+GROUP_PARALLELISM*4+g]^=r[roff+GROUP_PARALLELISM*0+g];
+      r[roff+GROUP_PARALLELISM*3+g]^=r[roff+GROUP_PARALLELISM*0+g];
+      r[roff+GROUP_PARALLELISM*2+g]^=r[roff+GROUP_PARALLELISM*0+g];
+    }
+#else
+    for(g=0;g<count_all;g+=BEST_SPAN){
+      XOR_BEST_BY(&r[roff+GROUP_PARALLELISM*0+g],&r[roff+GROUP_PARALLELISM*8+g],&sbox_out[g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*6+g],&perm_out[g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*4+g],&r[roff+GROUP_PARALLELISM*0+g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*3+g],&r[roff+GROUP_PARALLELISM*0+g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*2+g],&r[roff+GROUP_PARALLELISM*0+g]);
+    }
+#endif
+  }
+
+#define FASTTRASP2
+#ifndef FASTTRASP2
+  for(g=0;g<count;g++){
+    // Copy results
+    int j;
+    for(j=0;j<8;j++){
+      bd[8*g+j]=r[roff+GROUP_PARALLELISM*j+g];
+    }
+  }
+#else
+  trasp_8_N((unsigned char *)&r[roff],(unsigned char *)bd,count);
+#endif
+}
+
+//-----------------------------------EXTERNAL INTERFACE
+
+//-----get internal parallelism
+
+int get_internal_parallelism(void){
+  return GROUP_PARALLELISM;
+}
+
+//-----get suggested cluster size
+
+int get_suggested_cluster_size(void){
+  int r;
+  r=GROUP_PARALLELISM+GROUP_PARALLELISM/10;
+  if(r<GROUP_PARALLELISM+5) r=GROUP_PARALLELISM+5;
+  return r;
+}
+
+//-----key structure
+
+void *get_key_struct(void){
+  struct csa_keys_t *keys=(struct csa_keys_t *)MALLOC(sizeof(struct csa_keys_t));
+  if(keys) {
+    static const unsigned char pk[8] = { 0,0,0,0,0,0,0,0 };
+    set_control_words(keys,pk,pk);
+    }
+  return keys;
+}
+
+void free_key_struct(void *keys){
+  return FREE(keys);
+}
+
+//-----set control words
+
+static void schedule_key(struct csa_key_t *key, const unsigned char *pk){
+  // could be made faster, but is not run often
+  int bi,by;
+  int i,j;
+// key
+  memcpy(key->ck,pk,8);
+// precalculations for stream
+  key_schedule_stream(key->ck,key->iA,key->iB);
+  for(by=0;by<8;by++){
+    for(bi=0;bi<8;bi++){
+      key->ck_g[by][bi]=(key->ck[by]&(1<<bi))?FF1():FF0();
+    }
+  }
+  for(by=0;by<8;by++){
+    for(bi=0;bi<4;bi++){
+      key->iA_g[by][bi]=(key->iA[by]&(1<<bi))?FF1():FF0();
+      key->iB_g[by][bi]=(key->iB[by]&(1<<bi))?FF1():FF0();
+    }
+  }
+// precalculations for block
+  key_schedule_block(key->ck,key->kk);
+  for(i=0;i<56;i++){
+    for(j=0;j<BYTES_PER_BATCH;j++){
+      *(((unsigned char *)&key->kkmulti[i])+j)=key->kk[i];
+    }
+  }
+}
+
+void set_control_words(void *keys, const unsigned char *ev, const unsigned char *od){
+  schedule_key(&((struct csa_keys_t *)keys)->even,ev);
+  schedule_key(&((struct csa_keys_t *)keys)->odd,od);
+}
+
+void set_even_control_word(void *keys, const unsigned char *pk){
+  schedule_key(&((struct csa_keys_t *)keys)->even,pk);
+}
+
+void set_odd_control_word(void *keys, const unsigned char *pk){
+  schedule_key(&((struct csa_keys_t *)keys)->odd,pk);
+}
+
+//-----get control words
+
+void get_control_words(void *keys, unsigned char *even, unsigned char *odd){
+  memcpy(even,&((struct csa_keys_t *)keys)->even.ck,8);
+  memcpy(odd,&((struct csa_keys_t *)keys)->odd.ck,8);
+}
+
+//----- decrypt
+
+int decrypt_packets(void *keys, unsigned char **cluster){
+  // statistics, currently unused
+  int stat_no_scramble=0;
+  int stat_reserved=0;
+  int stat_decrypted[2]={0,0};
+  int stat_decrypted_mini=0;
+  unsigned char **clst;
+  unsigned char **clst2;
+  int grouped;
+  int group_ev_od;
+  int advanced;
+  int can_advance;
+  unsigned char *g_pkt[GROUP_PARALLELISM];
+  int g_len[GROUP_PARALLELISM];
+  int g_offset[GROUP_PARALLELISM];
+  int g_n[GROUP_PARALLELISM];
+  int g_residue[GROUP_PARALLELISM];
+  unsigned char *pkt;
+  int xc0,ev_od,len,offset,n,residue;
+  struct csa_key_t* k;
+  int i,j,iter,g;
+  int t23,tsmall;
+  int alive[24];
+//icc craziness  int pad1=0; //////////align! FIXME
+  unsigned char *encp[GROUP_PARALLELISM];
+  unsigned char stream_in[GROUP_PARALLELISM*8];
+  unsigned char stream_out[GROUP_PARALLELISM*8];
+  MEMALIGN unsigned char ib[GROUP_PARALLELISM*8];
+  MEMALIGN unsigned char block_out[GROUP_PARALLELISM*8];
+  struct stream_regs regs;
+
+//icc craziness  i=(int)&pad1;//////////align!!! FIXME
+
+  // build a list of packets to be processed
+  clst=cluster;
+  grouped=0;
+  advanced=0;
+  can_advance=1;
+  group_ev_od=-1; // silence incorrect compiler warning
+  pkt=*clst;
+  do{ // find a new packet
+    if(grouped==GROUP_PARALLELISM){
+      // full
+      break;
+    }
+    if(pkt==NULL){
+      // no more ranges
+      break;
+    }
+    if(pkt>=*(clst+1)){
+      // out of this range, try next
+      clst++;clst++;
+      pkt=*clst;
+      continue;
+    }
+
+    do{ // handle this packet
+      xc0=pkt[3]&0xc0;
+      DBG(fprintf(stderr,"   exam pkt=%p, xc0=%02x, can_adv=%i\n",pkt,xc0,can_advance));
+      if(xc0==0x00){
+        DBG(fprintf(stderr,"skip clear pkt %p (can_advance is %i)\n",pkt,can_advance));
+        advanced+=can_advance;
+        stat_no_scramble++;
+        break;
+      }
+      if(xc0==0x40){
+        DBG(fprintf(stderr,"skip reserved pkt %p (can_advance is %i)\n",pkt,can_advance));
+        advanced+=can_advance;
+        stat_reserved++;
+        break;
+      }
+      if(xc0==0x80||xc0==0xc0){ // encrypted
+        ev_od=(xc0&0x40)>>6; // 0 even, 1 odd
+        if(grouped==0) group_ev_od=ev_od; // this group will be all even (or odd)
+        if(group_ev_od==ev_od){ // could be added to group
+          pkt[3]&=0x3f;  // consider it decrypted now
+          if(pkt[3]&0x20){ // incomplete packet
+            offset=4+pkt[4]+1;
+            len=188-offset;
+            n=len>>3;
+            residue=len-(n<<3);
+            if(n==0){ // decrypted==encrypted!
+              DBG(fprintf(stderr,"DECRYPTED MINI! (can_advance is %i)\n",can_advance));
+              advanced+=can_advance;
+              stat_decrypted_mini++;
+              break; // this doesn't need more processing
+            }
+          }else{
+            len=184;
+            offset=4;
+            n=23;
+            residue=0;
+          }
+          g_pkt[grouped]=pkt;
+          g_len[grouped]=len;
+          g_offset[grouped]=offset;
+          g_n[grouped]=n;
+          g_residue[grouped]=residue;
+          DBG(fprintf(stderr,"%2i: eo=%i pkt=%p len=%03i n=%2i residue=%i\n",grouped,ev_od,pkt,len,n,residue));
+          grouped++;
+          advanced+=can_advance;
+          stat_decrypted[ev_od]++;
+        }
+        else{
+          can_advance=0;
+          DBG(fprintf(stderr,"skip pkt %p and can_advance set to 0\n",pkt));
+          break; // skip and go on
+        }
+      }
+    } while(0);
+
+    if(can_advance){
+      // move range start forward
+      *clst+=188;
+    }
+    // next packet, if there is one
+    pkt+=188;
+  } while(1);
+  DBG(fprintf(stderr,"-- result: grouped %i pkts, advanced %i pkts\n",grouped,advanced));
+
+  // delete empty ranges and compact list
+  clst2=cluster;
+  for(clst=cluster;*clst!=NULL;clst+=2){
+    // if not empty
+    if(*clst<*(clst+1)){
+      // it will remain 
+      *clst2=*clst;
+      *(clst2+1)=*(clst+1);
+      clst2+=2;
+    }
+  }
+  *clst2=NULL;
+
+  if(grouped==0){
+    // no processing needed
+    return advanced;
+  }
+
+  //  sort them, longest payload first
+  //  we expect many n=23 packets and a few n<23
+  DBG(fprintf(stderr,"PRESORTING\n"));
+  for(i=0;i<grouped;i++){
+    DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+    }
+  // grouped is always <= GROUP_PARALLELISM
+
+#define g_swap(a,b) \
+    pkt=g_pkt[a]; \
+    g_pkt[a]=g_pkt[b]; \
+    g_pkt[b]=pkt; \
+\
+    len=g_len[a]; \
+    g_len[a]=g_len[b]; \
+    g_len[b]=len; \
+\
+    offset=g_offset[a]; \
+    g_offset[a]=g_offset[b]; \
+    g_offset[b]=offset; \
+\
+    n=g_n[a]; \
+    g_n[a]=g_n[b]; \
+    g_n[b]=n; \
+\
+    residue=g_residue[a]; \
+    g_residue[a]=g_residue[b]; \
+    g_residue[b]=residue;
+
+  // step 1: move n=23 packets before small packets
+  t23=0;
+  tsmall=grouped-1;
+  for(;;){
+    for(;t23<grouped;t23++){
+      if(g_n[t23]!=23) break;
+    }
+DBG(fprintf(stderr,"t23 after for =%i\n",t23));
+    
+    for(;tsmall>=0;tsmall--){
+      if(g_n[tsmall]==23) break;
+    }
+DBG(fprintf(stderr,"tsmall after for =%i\n",tsmall));
+    
+    if(tsmall-t23<1) break;
+    
+DBG(fprintf(stderr,"swap t23=%i,tsmall=%i\n",t23,tsmall));
+
+    g_swap(t23,tsmall);
+
+    t23++;
+    tsmall--;
+DBG(fprintf(stderr,"new t23=%i,tsmall=%i\n\n",t23,tsmall));
+  }
+  DBG(fprintf(stderr,"packets with n=23, t23=%i   grouped=%i\n",t23,grouped));
+  DBG(fprintf(stderr,"MIDSORTING\n"));
+  for(i=0;i<grouped;i++){
+    DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+    }
+
+  // step 2: sort small packets in decreasing order of n (bubble sort is enough)
+  for(i=t23;i<grouped;i++){
+    for(j=i+1;j<grouped;j++){
+      if(g_n[j]>g_n[i]){
+        g_swap(i,j);
+      }
+    }
+  }
+  DBG(fprintf(stderr,"POSTSORTING\n"));
+  for(i=0;i<grouped;i++){
+    DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+    }
+
+  // we need to know how many packets need 23 iterations, how many 22...
+  for(i=0;i<=23;i++){
+    alive[i]=0;
+  }
+  // count
+  alive[23-1]=t23;
+  for(i=t23;i<grouped;i++){
+    alive[g_n[i]-1]++;
+  }
+  // integrate
+  for(i=22;i>=0;i--){
+    alive[i]+=alive[i+1];
+  }
+  DBG(fprintf(stderr,"ALIVE\n"));
+  for(i=0;i<=23;i++){
+    DBG(fprintf(stderr,"alive%2i=%i\n",i,alive[i]));
+    }
+
+  // choose key
+  if(group_ev_od==0){
+    k=&((struct csa_keys_t *)keys)->even;
+  }
+  else{
+    k=&((struct csa_keys_t *)keys)->odd;
+  }
+
+  //INIT
+//#define INITIALIZE_UNUSED_INPUT
+#ifdef INITIALIZE_UNUSED_INPUT
+// unnecessary zeroing.
+// without this, we operate on uninitialized memory
+// when grouped<GROUP_PARALLELISM, but it's not a problem,
+// as final results will be discarded.
+// random data makes debugging sessions difficult.
+  for(j=0;j<GROUP_PARALLELISM*8;j++) stream_in[j]=0;
+DBG(fprintf(stderr,"--- WARNING: you could gain speed by not initializing unused memory ---\n"));
+#else
+DBG(fprintf(stderr,"--- WARNING: DEBUGGING IS MORE DIFFICULT WHEN PROCESSING RANDOM DATA CHANGING AT EVERY RUN! ---\n"));
+#endif
+
+  for(g=0;g<grouped;g++){
+    encp[g]=g_pkt[g];
+    DBG(fprintf(stderr,"header[%i]=%p (%02x)\n",g,encp[g],*(encp[g])));
+    encp[g]+=g_offset[g]; // skip header
+    FFTABLEIN(stream_in,g,encp[g]);
+  }
+//dump_mem("stream_in",stream_in,GROUP_PARALLELISM*8,BYPG);
+
+
+  // ITER 0
+DBG(fprintf(stderr,">>>>>ITER 0\n"));
+  iter=0;
+  stream_cypher_group_init(&regs,k->iA_g,k->iB_g,stream_in);
+  // fill first ib
+  for(g=0;g<alive[iter];g++){
+    COPY_8_BY(ib+8*g,encp[g]);
+  }
+DBG(dump_mem("IB ",ib,8*alive[iter],8));
+  // ITER 1..N-1
+  for (iter=1;iter<23&&alive[iter-1]>0;iter++){
+DBG(fprintf(stderr,">>>>>ITER %i\n",iter));
+    // alive and just dead packets: calc block
+    block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+DBG(dump_mem("BLO_ib ",block_out,8*alive[iter-1],8));
+    // all packets (dead too): calc stream
+    stream_cypher_group_normal(&regs,stream_out);
+//dump_mem("stream_out",stream_out,GROUP_PARALLELISM*8,BYPG);
+
+    // alive packets: calc ib
+    for(g=0;g<alive[iter];g++){
+      FFTABLEOUT(ib+8*g,stream_out,g);
+DBG(dump_mem("stream_out_ib ",ib+8*g,8,8));
+// XOREQ8BY gcc bug? 2x4 ok, 8 ko    UPDATE: result ok but speed 1-2% slower (!!!???)
+#if 1
+      XOREQ_4_BY(ib+8*g,encp[g]+8);
+      XOREQ_4_BY(ib+8*g+4,encp[g]+8+4);
+#else
+      XOREQ_8_BY(ib+8*g,encp[g]+8);
+#endif
+DBG(dump_mem("after_stream_xor_ib ",ib+8*g,8,8));
+    }
+    // alive packets: decrypt data
+    for(g=0;g<alive[iter];g++){
+DBG(dump_mem("before_ib_decrypt_data ",encp[g],8,8));
+      XOR_8_BY(encp[g],ib+8*g,block_out+8*g);
+DBG(dump_mem("after_ib_decrypt_data ",encp[g],8,8));
+    }
+    // just dead packets: write decrypted data
+    for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("jd_before_ib_decrypt_data ",encp[g],8,8));
+      COPY_8_BY(encp[g],block_out+8*g);
+DBG(dump_mem("jd_after_ib_decrypt_data ",encp[g],8,8));
+    }
+    // just dead packets: decrypt residue
+    for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("jd_before_decrypt_residue ",encp[g]+8,g_residue[g],g_residue[g]));
+      FFTABLEOUTXORNBY(g_residue[g],encp[g]+8,stream_out,g);
+DBG(dump_mem("jd_after_decrypt_residue ",encp[g]+8,g_residue[g],g_residue[g]));
+    }
+    // alive packets: pointers++
+    for(g=0;g<alive[iter];g++) encp[g]+=8;
+  };
+  // ITER N
+DBG(fprintf(stderr,">>>>>ITER 23\n"));
+  iter=23;
+  // calc block
+  block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+DBG(dump_mem("23BLO_ib ",block_out,8*alive[iter-1],8));
+  // just dead packets: write decrypted data
+  for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("23jd_before_ib_decrypt_data ",encp[g],8,8));
+    COPY_8_BY(encp[g],block_out+8*g);
+DBG(dump_mem("23jd_after_ib_decrypt_data ",encp[g],8,8));
+  }
+  // no residue possible
+  // so do nothing
+
+  DBG(fprintf(stderr,"returning advanced=%i\n",advanced));
+
+  M_EMPTY(); // restore CPU multimedia state
+
+  return advanced;
+}
diff --git a/contrib/sasc-ng/FFdecsa/FFdecsa.h b/contrib/sasc-ng/FFdecsa/FFdecsa.h
new file mode 100644 (file)
index 0000000..1be08e7
--- /dev/null
@@ -0,0 +1,62 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 FFDECSA_H
+#define FFDECSA_H
+
+//----- public interface
+
+// -- how many packets can be decrypted at the same time
+// This is an info about internal decryption parallelism.
+// You should try to call decrypt_packets with more packets than the number
+// returned here for performance reasons (use get_suggested_cluster_size to know
+// how many).
+int get_internal_parallelism(void);
+
+// -- how many packets you should have in a cluster when calling decrypt_packets
+// This is a suggestion to achieve optimal performance; typically a little
+// higher than what get_internal_parallelism returns.
+// Passing less packets could slow down the decryption.
+// Passing more packets is never bad (if you don't spend a lot of time building
+// the list).
+int get_suggested_cluster_size(void);
+
+// -- alloc & free the key structure
+void *get_key_struct(void);
+void free_key_struct(void *keys);
+
+// -- set control words, 8 bytes each
+void set_control_words(void *keys, const unsigned char *even, const unsigned char *odd);
+
+// -- set even control word, 8 bytes
+void set_even_control_word(void *keys, const unsigned char *even);
+
+// -- set odd control word, 8 bytes
+void set_odd_control_word(void *keys, const unsigned char *odd);
+
+// -- get control words, 8 bytes each
+//void get_control_words(void *keys, unsigned char *even, unsigned char *odd);
+
+// -- decrypt many TS packets
+// This interface is a bit complicated because it is designed for maximum speed.
+// Please read doc/how_to_use.txt.
+int decrypt_packets(void *keys, unsigned char **cluster);
+
+#endif
diff --git a/contrib/sasc-ng/FFdecsa/FFdecsa_test.c b/contrib/sasc-ng/FFdecsa/FFdecsa_test.c
new file mode 100644 (file)
index 0000000..dfaa93d
--- /dev/null
@@ -0,0 +1,174 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "FFdecsa.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#include "FFdecsa_test_testcases.h"
+
+int compare(unsigned char *p1, unsigned char *p2, int n, int silently){
+  int i;
+  int ok=1;
+  for(i=0;i<n;i++){
+    if(i==3) continue; // tolerate this
+    if(p1[i]!=p2[i]){
+      fprintf(stderr,"at pos 0x%02x, got 0x%02x instead of 0x%02x\n",i,p1[i],p2[i]);
+      ok=0;
+    }
+  }
+  if(!silently){
+    if(ok){
+       fprintf(stderr,"CORRECT!\n");
+    }
+    else{
+       fprintf(stderr,"FAILED!\n");
+    }
+  }
+  return ok;
+}
+
+
+//MAIN
+
+#define TS_PKTS_FOR_TEST 30*1000
+//#define TS_PKTS_FOR_TEST 1000*1000
+unsigned char megabuf[188*TS_PKTS_FOR_TEST];
+unsigned char onebuf[188];
+
+unsigned char *cluster[10];
+
+int main(void){
+  int i;
+  struct timeval tvs,tve;
+  void *keys=get_key_struct();
+
+  fprintf(stderr,"FFdecsa 1.0: testing correctness and speed\n");
+
+/* begin correctness testing */
+
+  set_control_words(keys,test_invalid_key,test_1_key);
+  memcpy(onebuf,test_1_encrypted,188);
+  cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+  decrypt_packets(keys,cluster);
+  compare(onebuf,test_1_expected,188,0);
+
+  set_control_words(keys,test_2_key,test_invalid_key);
+  memcpy(onebuf,test_2_encrypted,188);
+  cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+  decrypt_packets(keys,cluster);
+  compare(onebuf,test_2_expected,188,0);
+
+  set_control_words(keys,test_3_key,test_invalid_key);
+  memcpy(onebuf,test_3_encrypted,188);
+  cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+  decrypt_packets(keys,cluster);
+  compare(onebuf,test_3_expected,188,0);
+
+  set_control_words(keys,test_p_10_0_key,test_invalid_key);
+  memcpy(onebuf,test_p_10_0_encrypted,188);
+  cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+  decrypt_packets(keys,cluster);
+  compare(onebuf,test_p_10_0_expected,188,0);
+
+  set_control_words(keys,test_p_1_6_key,test_invalid_key);
+  memcpy(onebuf,test_p_1_6_encrypted,188);
+  cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+  decrypt_packets(keys,cluster);
+  compare(onebuf,test_p_1_6_expected,188,0);
+
+/* begin speed testing */
+
+#if 0
+// test on short packets
+#define s_encrypted test_p_1_6_encrypted
+#define s_key_e     test_p_1_6_key
+#define s_key_o     test_invalid_key
+#define s_expected  test_p_1_6_expected
+
+#else
+//test on full packets
+#define s_encrypted test_2_encrypted
+#define s_key_e     test_2_key
+#define s_key_o     test_invalid_key
+#define s_expected  test_2_expected
+
+#endif
+
+  for(i=0;i<TS_PKTS_FOR_TEST;i++){
+    memcpy(&megabuf[188*i],s_encrypted,188);
+  }
+// test that packets are not shuffled around
+// so, let's put an undecryptable packet somewhere in the middle (we will use a wrong key)
+#define noONE_POISONED_PACKET
+#ifdef ONE_POISONED_PACKET
+  memcpy(&megabuf[188*(TS_PKTS_FOR_TEST*2/3)],test_3_encrypted,188);
+#endif
+
+  // start decryption
+  set_control_words(keys,s_key_e,s_key_o);
+  gettimeofday(&tvs,NULL);
+#if 0
+// force one by one
+  for(i=0;i<TS_PKTS_FOR_TEST;i++){
+    cluster[0]=megabuf+188*i;cluster[1]=onebuf+188*i+188;cluster[2]=NULL;
+    decrypt_packets(keys,cluster);
+  }
+#else
+  {
+    int done=0;
+    while(done<TS_PKTS_FOR_TEST){
+      //fprintf(stderr,"done=%i\n",done);
+      cluster[0]=megabuf+188*done;cluster[1]=megabuf+188*TS_PKTS_FOR_TEST;cluster[2]=NULL;
+      done+=decrypt_packets(keys,cluster);
+    }
+  }
+#endif
+  gettimeofday(&tve,NULL);
+  //end decryption
+
+  fprintf(stderr,"speed=%f Mbit/s\n",(184*TS_PKTS_FOR_TEST*8)/((tve.tv_sec-tvs.tv_sec)+1e-6*(tve.tv_usec-tvs.tv_usec))/1000000);
+  fprintf(stderr,"speed=%f pkts/s\n",TS_PKTS_FOR_TEST/((tve.tv_sec-tvs.tv_sec)+1e-6*(tve.tv_usec-tvs.tv_usec)));
+
+  // this packet couldn't be decrypted correctly
+#ifdef ONE_POISONED_PACKET
+  compare(megabuf+188*(TS_PKTS_FOR_TEST*2/3),test_3_expected,188,0); /* will fail because we used a wrong key */
+#endif
+  // these should be ok
+  compare(megabuf,s_expected,188,0);
+  compare(megabuf+188*511,s_expected,188,0);
+  compare(megabuf+188*512,s_expected,188,0);
+  compare(megabuf+188*319,s_expected,188,0);
+  compare(megabuf+188*(TS_PKTS_FOR_TEST-1),s_expected,188,0);
+
+  for(i=0;i<TS_PKTS_FOR_TEST;i++){
+    if(!compare(megabuf+188*i,s_expected,188,1)){
+      fprintf(stderr,"FAILED COMPARISON OF PACKET %10i\n",i);
+    };
+  }
+
+  return 0;
+}
diff --git a/contrib/sasc-ng/FFdecsa/FFdecsa_test_testcases.h b/contrib/sasc-ng/FFdecsa/FFdecsa_test_testcases.h
new file mode 100644 (file)
index 0000000..62d3f94
--- /dev/null
@@ -0,0 +1,279 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+// TEST DATA
+
+////////// used as a wrong key
+unsigned char test_invalid_key[0x08] = {
+    0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78
+};
+
+
+////////// test 1: odd key
+unsigned char test_1_key[0x8] = {
+    0x07, 0xe0, 0x1b, 0x02, 0xc9, 0xe0, 0x45, 0xee
+};
+unsigned char test_1_encrypted[0x100] = {
+    0x47, 0x00, 0x00, 0xd0,
+    0xde, 0xcf, 0x0a, 0x0d, 0xb2, 0xd7, 0xc4, 0x40, 0xde, 0x5d, 0x63, 0x18, 0x5a, 0x98, 0x17, 0xaa,
+    0xc9, 0xbc, 0x27, 0xc6, 0xcb, 0x49, 0x40, 0x48, 0xfd, 0x20, 0xb7, 0x05, 0x5b, 0x27, 0xcb, 0xeb,
+    0x9a, 0xf0, 0xac, 0x45, 0x6d, 0x56, 0xf4, 0x7b, 0x6f, 0xa0, 0x57, 0xf3, 0x9b, 0xf7, 0xa2, 0xc7,
+    0xd4, 0x68, 0x24, 0x00, 0x2f, 0x28, 0x13, 0x96, 0x94, 0xa8, 0x7c, 0xf4, 0x6f, 0x07, 0x2a, 0x0e,
+    0xe8, 0xa1, 0xeb, 0xc7, 0x80, 0xac, 0x1f, 0x79, 0xbf, 0x5d, 0xb6, 0x10, 0x7c, 0x2e, 0x52, 0xe9,
+    0x34, 0x2c, 0xa8, 0x39, 0x01, 0x73, 0x04, 0x24, 0xa8, 0x1e, 0xdb, 0x5b, 0xcb, 0x24, 0xf6, 0x31,
+    0xab, 0x02, 0x6b, 0xf9, 0xf6, 0xf7, 0xe9, 0x52, 0xad, 0xcf, 0x62, 0x0f, 0x42, 0xf6, 0x66, 0x5d,
+    0xc0, 0x86, 0xf2, 0x7b, 0x40, 0x20, 0xa9, 0xbd, 0x1f, 0xfd, 0x16, 0xad, 0x2e, 0x75, 0xa6, 0xa0,
+    0x85, 0xf3, 0x9c, 0x31, 0x20, 0x4e, 0xfb, 0x95, 0x61, 0x78, 0xce, 0x10, 0xc1, 0x48, 0x5f, 0xd3,
+    0x61, 0x05, 0x12, 0xf4, 0xe2, 0x04, 0xae, 0xe0, 0x86, 0x01, 0x56, 0x55, 0xb1, 0x0f, 0xa6, 0x33,
+    0x95, 0x20, 0x92, 0xf0, 0xbe, 0x39, 0x31, 0xe1, 0x2a, 0xf7, 0x93, 0xb4, 0xf7, 0xe4, 0xf1, 0x85,
+    0xae, 0x50, 0xf1, 0x63, 0xd4, 0x5d, 0x9c, 0x6c
+};
+unsigned char test_1_expected[0x100] = {
+    0x47, 0x00, 0x00, 0xd0,
+    0xaf, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xe6, 0xb5, 0xad, 0x7c,
+    0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c, 0xf9, 0xf3, 0xe6, 0xb5, 0xad, 0x6b, 0x5f, 0x3e, 0x7c, 0xf9,
+    0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9, 0xad, 0x6b, 0x5a, 0xd7, 0xcf, 0x9f, 0x3e, 0x5b, 0x16, 0xc7,
+    0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6, 0xb5, 0xf3, 0xe7, 0xcf, 0x96, 0xc5, 0xb1, 0xf3, 0xe7, 0xcf,
+    0x9a, 0xd6, 0xb5, 0xad, 0x7c, 0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c, 0xf9, 0xf3, 0xe6, 0xb5, 0xad,
+    0x6b, 0x5f, 0x3e, 0x7c, 0xf9, 0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9, 0xad, 0x6b, 0x5a, 0xd7, 0xcf,
+    0x9f, 0x3e, 0x5b, 0x16, 0xc7, 0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6, 0xb5, 0xf3, 0xe7, 0xcf, 0x96,
+    0xc5, 0xb1, 0xf3, 0xe7, 0xcf, 0x9a, 0xd6, 0xb5, 0xad, 0x7c, 0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c,
+    0xf9, 0xf3, 0xe6, 0xb5, 0xad, 0x6b, 0x5f, 0x3e, 0x7c, 0xf9, 0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9,
+    0xad, 0x6b, 0x5a, 0xd7, 0xcf, 0x9f, 0x3e, 0x5b, 0x16, 0xc7, 0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6,
+    0xb5, 0xf3, 0xe7, 0xcf, 0x96, 0xc5, 0xb1, 0xf3, 0xe7, 0xcf, 0x9a, 0xd0, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xfc, 0x44, 0x00, 0x66, 0xb1, 0x11, 0x11
+};
+unsigned char test_1_expected_stream[0x100] = {
+    0xdc, 0x15, 0xde, 0xf1, 0x4a, 0xf1, 0xf8, 0x2c,
+    0x75, 0xc8, 0x3a, 0x1f, 0xbf, 0x67, 0x19, 0xe1,
+    0xf4, 0x6c, 0x78, 0x99, 0x48, 0xaf, 0xef, 0x94,
+    0x71, 0x6b, 0x23, 0x9e, 0x29, 0x69, 0x2d, 0xa1,
+    0x8a, 0xbb, 0xf4, 0x16, 0x68, 0xa5, 0x7f, 0x14,
+    0xa9, 0x37, 0x24, 0x05, 0x5e, 0xdd, 0xec, 0x4b,
+    0xb5, 0xcb, 0x7f, 0x1d, 0xa7, 0x09, 0x2a, 0xce,
+    0xc4, 0x30, 0x83, 0xfd, 0xd9, 0x88, 0xa9, 0xf3,
+    0x85, 0x9c, 0x38, 0x31, 0x88, 0xac, 0x74, 0x02,
+    0x44, 0xdc, 0xb7, 0x81, 0x07, 0xc8, 0x1b, 0x03,
+    0x9c, 0x76, 0xbe, 0xe9, 0x4d, 0x3e, 0x19, 0xad,
+    0xe1, 0xf1, 0xa5, 0x13, 0xe8, 0xc0, 0x12, 0x57,
+    0x68, 0xb1, 0x9c, 0x6c, 0x9f, 0x58, 0x78, 0xee,
+    0x4f, 0x5b, 0x33, 0x1e, 0xc6, 0x29, 0xfc, 0x40,
+    0x58, 0x22, 0xa2, 0xd8, 0x32, 0xdd, 0x29, 0x4f,
+    0x2b, 0xe1, 0xef, 0xe4, 0xbb, 0xf2, 0x60, 0x94,
+    0x6c, 0xc5, 0x51, 0xec, 0x35, 0x4c, 0x27, 0xc6,
+    0x9d, 0x73, 0xe0, 0xf4, 0x2b, 0xfa, 0x62, 0x12,
+    0xcd, 0x44, 0xbe, 0x57, 0xfe, 0x80, 0xe7, 0xa9,
+    0x3c, 0x49, 0x42, 0xb6, 0xed, 0x05, 0x57, 0x00,
+    0xd2, 0x25, 0x90, 0xb3, 0xe4, 0x65, 0x8f, 0xd6,
+    0x4e, 0x0c, 0x73, 0x30, 0x3b, 0x68, 0x48, 0xdd,
+// stream ^ sb
+//    0x02, 0x48, 0xbd, 0xe9, 0x10, 0x69, 0xef, 0x86,
+//    0xbc, 0x74, 0x1d, 0xd9, 0x74, 0x2e, 0x59, 0xa9,
+//    0x09, 0x4c, 0xcf, 0x9c, 0x13, 0x88, 0x24, 0x7f,
+//    0xeb, 0x9b, 0x8f, 0xdb, 0x44, 0x3f, 0xd9, 0xda,
+};
+unsigned char test_1_expected_block[0x100] = {
+    0xad, 0xf6, 0x46, 0x06, 0xae, 0x92, 0x00, 0x38,
+    0x47, 0x9b, 0xa3, 0x22, 0x92, 0x9b, 0xf4, 0xd5,
+    0xf0, 0xbf, 0x2a, 0x2d, 0x7f, 0xf4, 0xdd, 0x8c,
+    0x0d, 0x2e, 0x22, 0xb0, 0x1b, 0x01, 0xa5, 0x23,
+    0x89, 0x40, 0xbc, 0xdb, 0x8f, 0xab, 0x70, 0xb8,
+    0x27, 0x88, 0xcf, 0x9a, 0x4f, 0xae, 0xe9, 0x1a,
+    0xee, 0xfc, 0x3d, 0x82, 0x92, 0xd8, 0xb5, 0x33,
+    0xcb, 0x5e, 0xfe, 0xff, 0xe8, 0xd7, 0x51, 0x45,
+    0xa0, 0x17, 0x3b, 0x8c, 0x88, 0x7b, 0xd5, 0x0e,
+    0xc1, 0x9c, 0x63, 0x41, 0xf5, 0x5d, 0xaa, 0x8a,
+    0x5f, 0x37, 0x5b, 0xce, 0x7f, 0x76, 0xb4, 0x83,
+    0x74, 0x8f, 0x37, 0x47, 0x75, 0x6d, 0x2c, 0xca,
+    0x5a, 0x40, 0xa5, 0x75, 0x1a, 0x61, 0x81, 0x8d,
+    0xe4, 0x87, 0x17, 0xd0, 0x75, 0xee, 0x9a, 0x6b,
+    0x82, 0x6e, 0x47, 0x92, 0xd3, 0x32, 0x59, 0x5a,
+    0x03, 0x6e, 0x8a, 0x26, 0x7e, 0x0d, 0xf7, 0x7d,
+    0xf4, 0x4e, 0x79, 0x49, 0x59, 0x6f, 0x27, 0x2b,
+    0x80, 0x8f, 0x9e, 0x5b, 0xd6, 0xc0, 0xb0, 0x0b,
+    0xe6, 0x2e, 0xb2, 0xd5, 0x80, 0x10, 0x7f, 0xc1,
+    0xbf, 0xae, 0x1f, 0xd9, 0x6d, 0x57, 0x3c, 0x37,
+    0x4d, 0x21, 0xe4, 0xc8, 0x85, 0x44, 0xcf, 0xa0,
+    0x07, 0x93, 0x18, 0x83, 0xef, 0x35, 0xd4, 0xb1,
+    0xff, 0xfc, 0x44, 0x00, 0x66, 0xb1, 0x11, 0x11
+};
+unsigned char test_1_expected_kb[] = {
+    0xEE, 0x45, 0xE0, 0xC9, 0x02, 0x1B, 0xE0, 0x07,
+    0x46, 0xA4, 0x1C, 0x26, 0x7B, 0x0C, 0x01, 0xED,
+    0x93, 0x99, 0xC3, 0x14, 0xC4, 0x4A, 0x8D, 0x54,
+    0x19, 0x82, 0x39, 0xD1, 0x33, 0xB0, 0x33, 0x52,
+    0x75, 0x62, 0x80, 0x3A, 0xC8, 0x83, 0x5E, 0x23,
+    0xA2, 0x57, 0x0C, 0xC4, 0x2C, 0x2D, 0xD2, 0x98,
+    0xA0, 0x6C, 0x77, 0x29, 0x11, 0x42, 0x49, 0xCE,
+};
+unsigned char test_1_expected_kk[] = {
+    0x5e, 0x9d, 0xff, 0x2e, 0xbb, 0xaa, 0xa8, 0xe9,
+    0xf6, 0x0e, 0xff, 0x7c, 0xda, 0xce, 0x55, 0x03,
+    0xd9, 0xde, 0x79, 0xf5, 0x2c, 0xaf, 0x06, 0xf8,
+    0xb2, 0xc9, 0xf8, 0x78, 0x54, 0xf9, 0xd1, 0xe7,
+    0xeb, 0xbe, 0xd7, 0xeb, 0x25, 0xe9, 0x17, 0x99,
+    0xbf, 0x24, 0xce, 0x2a, 0x73, 0xfe, 0xf9, 0xbc,
+    0xd9, 0x55, 0x91, 0xcf, 0xe0, 0xc9, 0xdf, 0x88,
+};
+
+
+////////// test 2: even key
+unsigned char test_2_key[0x8] = {
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+unsigned char test_2_encrypted[0x100] = {
+    0x47, 0x00, 0x00, 0x90,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+};
+unsigned char test_2_expected[0x100] = {
+    0x47, 0x00, 0x00, 0x90,
+    0x2d, 0x0a, 0x47, 0x20, 0x18, 0x11, 0x9c, 0x8a, 0xd1, 0x2a, 0x65, 0x6b, 0x89, 0xe4, 0x35, 0x2b,
+    0xc2, 0xb5, 0x90, 0x61, 0xd1, 0x7e, 0x02, 0xe1, 0x3f, 0x46, 0x70, 0xcf, 0x77, 0x91, 0x2f, 0x22,
+    0x93, 0xc1, 0x6c, 0xfe, 0x49, 0xad, 0x7c, 0xc2, 0xaf, 0x86, 0x1b, 0xa3, 0x29, 0xbe, 0xaa, 0x64,
+    0xf0, 0x22, 0xb9, 0x5e, 0x98, 0xaa, 0x60, 0xef, 0xdf, 0xd6, 0x44, 0x77, 0xe6, 0xbf, 0xbb, 0x94,
+    0xb2, 0x0a, 0x63, 0x0e, 0x5c, 0xf2, 0xac, 0xb4, 0x49, 0xcc, 0x9e, 0x4f, 0x94, 0x4c, 0x30, 0x12,
+    0xe8, 0x55, 0xc2, 0x44, 0xa4, 0x52, 0xcb, 0x61, 0x81, 0xc9, 0xb6, 0xa6, 0x6b, 0xef, 0xaf, 0xa6,
+    0x71, 0x1d, 0x7b, 0x58, 0x2f, 0xfa, 0xd1, 0x0c, 0x07, 0x9d, 0x1f, 0x35, 0x87, 0xbe, 0x02, 0x9f,
+    0x20, 0xc6, 0x60, 0x8f, 0x1c, 0x30, 0x0f, 0x96, 0xd0, 0x71, 0xd6, 0x51, 0x10, 0xdf, 0x5b, 0xf6,
+    0x44, 0x2f, 0x80, 0x28, 0xb7, 0xec, 0x23, 0x59, 0x4b, 0x94, 0x0b, 0x9a, 0x74, 0xa1, 0x1f, 0xf7,
+    0x9e, 0x76, 0xb4, 0xdf, 0xbb, 0x3c, 0x8c, 0x88, 0x97, 0x22, 0x56, 0x73, 0x16, 0x05, 0xac, 0xf9,
+    0x4f, 0x77, 0x9d, 0x38, 0xa0, 0x6b, 0x05, 0xd2, 0xe6, 0x15, 0x01, 0xb1, 0x5c, 0xc9, 0x62, 0xa9,
+    0x9b, 0x1a, 0x6a, 0x1a, 0xcf, 0xe6, 0xa8, 0xba,
+};
+
+
+////////// test 3: even key
+unsigned char test_3_key[0x8] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+unsigned char test_3_encrypted[0x100] = {
+    0x47, 0x00, 0x00, 0x90,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+
+};
+unsigned char test_3_expected[0x100] = {
+    0x47, 0x00, 0x00, 0x90,
+    0xfe, 0x91, 0xa7, 0x2f, 0xbf, 0xb0, 0x6a, 0x54, 0xc1, 0xe4, 0x33, 0x27, 0x18, 0xd5, 0x9c, 0x43,
+    0xea, 0xaa, 0x6b, 0x38, 0x5c, 0xe7, 0xae, 0xc9, 0xac, 0xec, 0xef, 0xc3, 0x51, 0x7d, 0x53, 0x47,
+    0xa0, 0xa7, 0x6d, 0x73, 0x8a, 0x9d, 0x16, 0x7d, 0x05, 0x2d, 0xd6, 0x6b, 0xf4, 0x8d, 0x4b, 0x81,
+    0x98, 0x2f, 0x46, 0xa5, 0x34, 0x84, 0xf3, 0x70, 0xa4, 0xe9, 0x04, 0x84, 0x7b, 0x87, 0x79, 0x3c,
+    0x01, 0x25, 0xb5, 0xfc, 0x3d, 0xd0, 0x25, 0xea, 0x2f, 0x91, 0xf0, 0x3f, 0x7f, 0xd4, 0x8e, 0x1e,
+    0x36, 0x83, 0x22, 0x91, 0x57, 0x92, 0x36, 0x0b, 0x44, 0xa5, 0xcc, 0x5e, 0xef, 0x44, 0x3e, 0xf8,
+    0xe9, 0x7b, 0x5e, 0x0c, 0xea, 0xb2, 0x50, 0x39, 0xb7, 0xea, 0xc4, 0xfb, 0xe4, 0x37, 0xf8, 0x85,
+    0xc2, 0xdc, 0x01, 0x98, 0x01, 0x2a, 0x44, 0xd3, 0x75, 0x10, 0x38, 0xf4, 0x85, 0x3e, 0xc9, 0xf7,
+    0xe7, 0xe4, 0xec, 0x40, 0x3d, 0x8f, 0xa5, 0xd2, 0x8a, 0xca, 0x62, 0x03, 0x3f, 0x65, 0x28, 0x8d,
+    0xf5, 0x56, 0xa7, 0xea, 0xd1, 0x0d, 0x70, 0x82, 0xbc, 0x90, 0x59, 0xf8, 0x3e, 0x08, 0xc9, 0xe1,
+    0x97, 0xef, 0x82, 0x43, 0x35, 0x41, 0x3e, 0x7f, 0x00, 0x96, 0x3f, 0x90, 0xe5, 0x1e, 0x96, 0xba,
+    0xce, 0x6d, 0xd2, 0x54, 0xce, 0x84, 0x76, 0x3c
+};
+
+
+////////// odd key, only 80 (0x50) bytes of payload (10 groups of 8 bytes + 0 byte residue)
+unsigned char test_p_10_0_key[0x8] = {
+    0x2d, 0x11, 0x5f, 0x9d, 0x29, 0xbf, 0x7f, 0x67
+};
+unsigned char test_p_10_0_encrypted[0x100] = {
+  0x47, 0x00, 0x7a, 0xbe,
+  0x67, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x71, 0xa5, 0x7b, 0x8f, 0xf9, 0x87, 0xcb, 0xac,
+  0xea, 0x08, 0x0c, 0x02, 0x87, 0x7b, 0xad, 0x10, 0x40, 0x28, 0x8e, 0xd4, 0x4e, 0x62, 0xc7, 0x74,
+  0xd6, 0xbb, 0x3a, 0xaa, 0xb0, 0x7b, 0x70, 0xbe, 0x06, 0xc9, 0xdc, 0x07, 0xd2, 0x2d, 0xab, 0x2d,
+  0xe2, 0xc6, 0x36, 0xa6, 0xda, 0x64, 0x61, 0x15, 0xd1, 0x6a, 0x40, 0xc0, 0xa9, 0xfb, 0x3f, 0xb2,
+  0x6d, 0xa5, 0x59, 0xae, 0x57, 0x88, 0x6b, 0x0e, 0x00, 0xae, 0xce, 0x64, 0xee, 0xfd, 0xb1, 0x7f,
+  0x78, 0x9c, 0x12, 0x42, 0xbe, 0x30, 0x8a, 0xa3 
+};
+unsigned char test_p_10_0_expected[0x100] = {
+  0x47, 0x00, 0x7a, 0xbe,
+  0x67, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xca, 0x32, 0xaf, 0x2e, 0x6a, 0xea, 0x05,
+  0x39, 0x33, 0x67, 0x5d, 0xa3, 0x61, 0x0f, 0x34, 0x40, 0x6c, 0x1a, 0xb3, 0xee, 0x54, 0x64, 0xd5,
+  0xa3, 0x01, 0x95, 0x87, 0x9d, 0x3d, 0x38, 0xc5, 0x82, 0x8b, 0x8d, 0xab, 0xad, 0x93, 0x0f, 0xe8,
+  0xf9, 0xbd, 0x52, 0x98, 0x59, 0xb2, 0x41, 0x95, 0xcd, 0xae, 0x9b, 0x3e, 0xdf, 0xdb, 0x14, 0x9b,
+  0xa9, 0x22, 0x0d, 0x2d, 0x61, 0xf5, 0xf2, 0x52, 0x83, 0x20, 0xae, 0xb8, 0x83, 0x52, 0x02, 0xee,
+  0xbd, 0xd2, 0x94, 0x6c, 0x27, 0x58, 0x55, 0xd0
+};
+
+
+////////// odd key, only 14 (0x0e) bytes of payload (1 group of 8 bytes + 6 byte residue)
+unsigned char test_p_1_6_key[0x8] = {
+    0x2d, 0x11, 0x5f, 0x9d, 0x29, 0xbf, 0x7f, 0x67
+};
+unsigned char test_p_1_6_encrypted[0x100] = {
+  0x47, 0x00, 0x7a, 0xb7,
+  0xa9, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x5e, 0xfb, 0xc8, 0x4a, 0x63,
+  0xe3, 0x3c, 0x11, 0xd9, 0xe0, 0x75, 0x8e, 0xf2 
+};
+unsigned char test_p_1_6_expected[0x100] = {
+  0x47, 0x00, 0x7a, 0xb7,
+  0xa9, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5a, 0x2c, 0xee, 0xb3, 0xde, 0x92,
+  0xe7, 0xa6, 0x6c, 0xaa, 0x99, 0x84, 0xe4, 0x00 
+};
diff --git a/contrib/sasc-ng/FFdecsa/Makefile b/contrib/sasc-ng/FFdecsa/Makefile
new file mode 100644 (file)
index 0000000..0108645
--- /dev/null
@@ -0,0 +1,66 @@
+##### compiling with g++ gives a little more speed
+#COMPILER=gcc
+#COMPILER=g++
+PARALLEL_MODE ?= PARALLEL_32_INT
+
+ARCH = $(shell uname -m)
+ifeq ($(ARCH), x86_64)
+  FLAGS=-Wall -O3 -march=x86-64 -mmmx -fexpensive-optimizations -funroll-loops -finline-limit=6000000 --param max-unrolled-insns=500
+endif
+ifeq ($(ARCH), athlon-xp)
+  FLAGS=-Wall -O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops -finline-limit=6000000 --param max-unrolled-insns=500
+endif
+#ifeq ($(ARCH), i686)
+#  FLAGS=-Wall -O3 -march=pentium4 -mmmx -msse2 -fexpensive-optimizations -funroll-loops
+#endif
+
+###there are two functions which apparently don't want to be inlined
+#FLAGS=-O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops -finline-limit=6000000 --param max-unrolled-insns=500
+#FLAGS=-O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops --param max-unrolled-insns=500
+#FLAGS=-O3 -march=pentium3 -fexpensive-optimizations -funroll-loops
+
+###icc crashes for unknown reasons
+#COMPILER=/opt/intel_cc_80/bin/icc
+#FLAGS=-O3 -march=pentiumiii
+
+#FLAGS += -g
+#FLAGS += -fno-alias
+#FLAGS += -vec_report3
+#FLAGS += -Wall -Winline
+#FLAGS += -fomit-frame-pointer 
+#FLAGS += -pg
+
+COMPILER ?= g++
+FLAGS    ?= -Wall -O3 -march=pentium -mmmx -fomit-frame-pointer -fexpensive-optimizations -funroll-loops
+
+H_FILES = FFdecsa.h parallel_generic.h parallel_std_def.h \
+          parallel_032_4char.h \
+          parallel_032_int.h \
+          parallel_064_2int.h \
+          parallel_064_8charA.h \
+          parallel_064_8char.h \
+          parallel_064_long.h \
+          parallel_064_mmx.h \
+          parallel_128_16charA.h \
+          parallel_128_16char.h \
+          parallel_128_2long.h \
+          parallel_128_2mmx.h \
+          parallel_128_4int.h \
+          parallel_128_sse.h
+
+all: FFdecsa.o
+
+%.o: %.c
+       $(COMPILER) $(FLAGS) -DPARALLEL_MODE=$(PARALLEL_MODE) -c $<
+
+FFdecsa_test:  FFdecsa_test.o FFdecsa.o
+       $(COMPILER) $(FLAGS) -o FFdecsa_test FFdecsa_test.o FFdecsa.o
+
+FFdecsa_test.o: FFdecsa_test.c FFdecsa.h FFdecsa_test_testcases.h
+FFdecsa.o:     FFdecsa.c stream.c $(H_FILES)
+
+clean:
+       rm -f FFdecsa_test *.o
+
+test:  FFdecsa_test
+       sync;usleep 200000;nice --19 ./FFdecsa_test
diff --git a/contrib/sasc-ng/FFdecsa/README b/contrib/sasc-ng/FFdecsa/README
new file mode 100644 (file)
index 0000000..503c744
--- /dev/null
@@ -0,0 +1,50 @@
+-------
+FFdecsa
+-------
+version 1.0
+Copyright 2003-2004  fatih89r
+released under GPL
+
+
+FFdecsa is a fast implementation of a CSA decryption algorithm for MPEG
+TS packets. It is shockingly fast, more than 800% the speed of the
+fastest implementation I can find around. (read the docs to know what FF
+stands for)
+
+On an AthlonXP 2400 (2000MHz) it achieves 165Mbit/s; the previous record
+was around 20Mbit/s.
+
+This means that:
+- decrypting a 8Mbit/s stream takes 5% of CPU instead of 40%
+- decrypting a full transponder (with all its channels or with a big
+  HDTV stream) carrying 38Mbit/s takes 23% of CPU instead of 190%
+  (>100%, so undecryptable in real time)
+- a very slow processor can decrypt one channel with no problems
+- offline decoding of one hour of a 5Mbit/s channel takes less than
+  two minutes (30x than realtime)
+- offline decoding will work at more than 20MB/s (megabytes/s),
+  nearly as fast as a file copy
+
+The docs directory contains useful stuff:
+
+  FAQ.txt
+    to know something more about this software
+
+  how_to_compile.txt
+    if you want to compile this code (and get optimal speed)
+
+  how_to_use.txt
+    if you want to use this code
+
+  technical_background.txt
+    if you want to understand how this code works or you want to
+    modify/improve it
+
+  how_to_understand.txt
+    if you want to understand the code to make modifications
+
+  how_to_release.txt
+    if you want to release modified versions of the code
+
+
+fatih89r
diff --git a/contrib/sasc-ng/FFdecsa/docs/FAQ.txt b/contrib/sasc-ng/FFdecsa/docs/FAQ.txt
new file mode 100644 (file)
index 0000000..2d46f06
--- /dev/null
@@ -0,0 +1,77 @@
+-------
+FFdecsa
+-------
+
+FFdecsa is a fast implementation of the CSA decryption algorithm for MPEG
+TS packets.
+
+Q: What does FF stands for?
+A: FFdecsa means "Fucking Fast decsa".
+
+Q: Why would you use such a rude name?
+A: Because this code is fucking fast, more than 800% the speed of the best
+   implementation I'm able to find around at the moment.
+
+Q: How it that possible? Are all other programmers stupid?
+A: No, they just tried to save a cycle or two tweaking a fundamentally wrong
+   implementation. The algorithm has to be implemented in a totally different
+   way to achieve good speed.
+
+Q: Do you use multimedia instructions?
+A: I use every trick I could come up with, including multimedia instructions.
+   They are not fundamental in achieving speed, a version without them runs
+   at 6x the speed of the best implementation around (which uses MMX).
+
+Q: So how did you do that?
+A: By using a different approach for the implementation. This code is not
+   exploiting some new CSA vulnerability, it is just doing the same
+   calculations better. Think about replacing bubble sort with quick sort.
+
+Q: You're joking, it's impossible to gain so much speed.
+A: Speed test are available, technical documentation is available, source
+   code is available. Try it yourself.
+   If you want details, these are some of the documented tricks I used
+   (more details in the docs directory):
+    TRICK NUMBER 0: emulate the hardware
+    TRICK NUMBER 1: virtual shift registers
+    TRICK NUMBER 2: parallel bitslice
+    TRICK NUMBER 3: multimedia instructions
+    TRICK NUMBER 4: parallel byteslice
+    TRICK NUMBER 5: efficient bit permutation
+    TRICK NUMBER 6: efficient normal<->slice conversion
+    TRICK NUMBER 7: try hard to process packets together
+    TRICK NUMBER 8: try to avoid doing the same thing many times
+    TRICK NUMBER 9: compiler
+    TRICK NUMBER a: a lot of brain work
+
+Q: How can be this code useful?
+A: You can use this code in place of the old slow implementations and save a
+   lot of CPU power.
+
+Q: Just that?
+A: Well, new applications are possible.
+   Decrypting a whole transponder is easily doable now. Well, a $50 CPU can
+   decrypt four transponder at the same time if you have four DVB boards (but
+   I couldn't test that).
+
+Q: You're cheating, this code is fake, I don't believe one word.
+A: Go away. This is technical stuff for people with brains.
+
+Q: This code is great, may I distribute your code in original or modified
+   form?
+A: Only if you respect the license.
+
+Q: May I use your code in my player/library/plugin...?
+A: Again, you have to respect the license.
+
+Q: Are you an extraterrestrial programmer?
+A: No, just a Turkish guy with a PC to play with :-)
+
+Q: Why did you spend your time doing this?
+A: Because I thought that my approach was doable and I was sure it would
+   have been much faster, so I had to implement it to confirm I was right.
+   I got 8x the speed and that's enough to be proud of it. And I could not
+   just keep the code for myself only.
+
+Q: What is the answer to the meaning of the universe?
+A: 42,43,71,5f,65,85,f6,76,0d,13,28,96,...
diff --git a/contrib/sasc-ng/FFdecsa/docs/how_to_compile.txt b/contrib/sasc-ng/FFdecsa/docs/how_to_compile.txt
new file mode 100644 (file)
index 0000000..4f8c141
--- /dev/null
@@ -0,0 +1,114 @@
+-------
+FFdecsa
+-------
+
+Compiling is as easy as running a make command, if you have gcc and are
+using a little endian machine. 64 bit machines have not been tested but
+may work with little or no changes; big endian machines will certainly
+give incorrect results (read the technical_background.txt to know where
+the problem is).
+
+Before compiling you could edit the Makefile to tweak compiler flags for
+optimal performance. If you want to play with different bit-grouping
+strategies you have to edit FFdecsa_DBG.c and change the "our choice"
+definition. This is highly critical for performance.
+
+After compilation run the FFdecsa_test application. It will test correct
+decryption and print the meausered speed (use "nice --19 ./FFdecsa_test"
+on an idle machine for better results). Or just use "make test".
+
+gcc >=3.3.3 is highly recommended. Older versions could give performance
+problems.
+
+icc is currently unusable. In the initial phases of development of
+FFdecsa icc was able to compile the code and gave interesting speed
+results when using the 8charA grouping mode (array of 8 characters are
+automatically manipulated through MMX instructions). At some point the
+code began to work incorrectly because of a compiler bug (but I found a
+workaround). Then, the performance dropped with no reason; I found a
+workaround by adding an unused variable (alignment problem, grep for icc
+in the code to see where it happens). Then, with the introduction of
+group modes based on intrinsics, gcc was finally able to go beyond the
+speed record originally set by icc. Additional code tweaks added more
+speed to gcc, while icc started to segfault on compilation (both version
+7 and 8). In conclusion, icc is bugged and this code is too hard for it.
+gcc on the other hand is great. I tried to inspect generated assembler
+to find weak spots, and the generated code is very good indeed.
+
+Note: the code can be compiled with gcc or g++. g++ is 3% faster for
+some reason.
+
+You should not get any errors or warnings. I only get two "inlining
+failed" warnings on two functions I asked to be inlined but gcc doesn't
+want to inline.
+
+The build process creates additional temp files by running grep
+commands. This is how debugging output is handled. All the lines
+containing DBG are removed and the temp file is compiled (so the line
+numbers change between temp and original files). Don't edit the temp
+files, they will be overwritten. If you don't remove the DBG lines (for
+example, by changing "grep -v DBG" into "grep -v aaDBG" in Makefile) a
+lot of output will be generated. This is useful to understand what's
+wrong when the FFdecsa_test is failing. I included a reference "known
+good" output in the debug_output directory. Extra debug output is
+commented out in the code.
+
+The debug output functionality could be... bugged. This is because I
+tested everything using hard coded int grouping mode and then
+generalized the debug output to abstract grouping modes. A bug where 4
+bytes are printed instead of 8 could be present somewhere. I think it
+isn't, but you've been warned.
+
+This code was only tried on Linux.
+It should work on Windows or other platforms, but you may encounter
+problems related to the compiler quality. If you want to try, begin with
+the int grouping mode. It is only 30% slower then the best (MMX) and it
+should be easily portable because no intrinsics are used. I'm
+particularly interested in hearing what kind of performance can be
+obtained on x86_64 processors in int, long long int, mmx, 2mmx, sse
+modes.
+
+
+As a reference, here are the results I get on an Athlon XP 2400+ (this
+processor runs at 2000MHz); other processors belonging to the Athlon XP
+architecture, including Durons, should have the same speed per MHz.
+Cache size and bus speed don't matter.
+
+CPU: AMD Athlon XP 2400+
+
+Compiler: g++ (gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7))
+
+Flags: -O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops
+       --param max-unrolled-insns=500
+
+grouping mode           speed (Mbit/s)    notes
+---------------------------------------------------------------------
+PARALLEL_32_4CHAR            14
+PARALLEL_32_4CHARA           12
+PARALLEL_32_INT             125           very good and very portable
+PARALLEL_64_8CHAR            17
+PARALLEL_64_8CHARA           15           needs a vectorizing compiler
+PARALLEL_64_2INT             75           x86 has too few registers
+PARALLEL_64_LONG             97           try this on x86_64
+PARALLEL_64_MMX             165           the best
+PARALLEL_128_16CHAR           6
+PARALLEL_128_16CHARA          7
+PARALLEL_128_4INT            69
+PARALLEL_128_2LONG           52
+PARALLEL_128_2MMX            36           slower than expected
+PARALLEL_128_SSE            156           just slower than 64_MMX
+
+Best speeds are obtained with native data types: int, mmx, sse (this
+could be a compiler artifact).
+
+64 bit processors should try 64_LONG.
+
+Vectorizing compilers should like *CHARA.
+
+64_MMX is faster than 128_SSE on the Athlon; perhaps SSE instruction are
+internally split into 64 bit chunks. Could be different on x86_64 or
+Intel processors.
+
+128_SSE has a 64 bit (MMX) batch type because SSE has no shifting
+instructions, they are only available on SSE2. As the Athlon XP doesn't
+support SSE2, I couldn't experiment with that.
diff --git a/contrib/sasc-ng/FFdecsa/docs/how_to_release.txt b/contrib/sasc-ng/FFdecsa/docs/how_to_release.txt
new file mode 100644 (file)
index 0000000..923a61b
--- /dev/null
@@ -0,0 +1,21 @@
+-------
+FFdecsa
+-------
+
+Please use the name of the release you're basing on as a base name and
+add your suffix.
+
+For example if john modifies
+  FFdecsa-1.0.0
+he should release
+  FFdecsa-1.0.0-john_0.3
+or
+  FFdecsa-1.0.0-john_0.4
+
+If paul modifies john's version the correct name would be like
+  FFdecsa-1.0.0-john_0.4-paul_0.1
+
+This is to avoid many different versions with random version numbers, as
+development is not centralized.
+
+Thank you.
diff --git a/contrib/sasc-ng/FFdecsa/docs/how_to_understand.txt b/contrib/sasc-ng/FFdecsa/docs/how_to_understand.txt
new file mode 100644 (file)
index 0000000..4b3f2f1
--- /dev/null
@@ -0,0 +1,15 @@
+-------
+FFdecsa
+-------
+
+First, you need to know how decsa works, study the source of a classical
+implementation. Then you have to understand how things are done in
+slicing mode. Read all the documentation and have a working classical
+implementation to compare partial results. There are comments spread
+around the code. Some things are difficult to understand without paper
+notes; for example the matrix transpositions and meaning of array
+indices.
+
+Sorry, it is hard to understand and modify ...
+
+... but it was harder to design and implement!!!
diff --git a/contrib/sasc-ng/FFdecsa/docs/how_to_use.txt b/contrib/sasc-ng/FFdecsa/docs/how_to_use.txt
new file mode 100644 (file)
index 0000000..46fedd3
--- /dev/null
@@ -0,0 +1,239 @@
+-------
+FFdecsa
+-------
+
+This code is able to decrypt MPEG TS packets with the CSA algorithm. To
+achieve high speed, the decryption core works on many packets at the
+same time, so the interface is more complicated than usual decsa
+implementations.
+
+The FFdecsa.h file defines the external interface of this code.
+
+Basically:
+
+1) you use get_suggested_cluster_size to know the optimal number of
+packets you have to pass for decryption
+
+2) you use set_control_words to set the decryption keys
+
+3) you use decrypt_packets to do the actual decryption
+
+You don't need to always use set_control_words before decrypt_packets,
+if keys aren't changed. 
+
+
+The decrypt_packets function call decrypts many packets at the same
+time. The interface is complicated because the only design goal was
+speed, so it implements zero-copying of packets, out-of-order decryption
+and optimal packet aggregation for better parallelism. This part is the
+most difficult to understand.
+
+--- HOW TO USE int decrypt_packets(unsigned char **cluster); ---
+
+PARAMETERS
+  cluster points to an array of pointers, representing zero or more
+  ranges. Every range has a start and end pointer; a start pointer==NULL
+  terminates the array.
+  So, an array of pointers has this content:
+    start_of_buffer_1, end_of_buffer_1, ... start_of_buffer_N,
+    end_of_buffer_N, NULL
+  example:
+    0x12340000, 0x123400bc, 0x56780a00, 0x5678b78, NULL
+  has two ranges (0x12340000 - 0x123400bc and  0x56780a00 - 0x5678b78),
+  for a total of three packets (starting at 0x12340000, 0x56780a00,
+  0x5678abc)
+RETURNS
+  How many packets can now be consumed by the caller, this is always >=
+  1, unless the cluster contained zero packets (in that case it's
+  obviously zero).
+MODIFIES
+  The cluster is modified to try to exclude packets which shouldn't be
+  submitted again for decryption (because just decrypted or originally
+  not crypted). "Try to exclude" because the returned array will never
+  be bigger than what was passed, so if you passed only a range and some
+  packets in the middle were decrypted making "holes" into the range,
+  the range would have to be split into several ranges, and that will
+  not be done. If you want a strict description of what has to be passed
+  again to decrypt_packets, you have to use ranges with only one packet
+  inside. Note that the first packet will certainly be eliminated from
+  the returned cluster (see also RETURNS).
+
+You can now read the detailed description of operation or just skip to
+the API examples.
+
+
+---------------------------------
+DETAILED DESCRIPTION OF OPERATION
+---------------------------------
+  consider a sequence of packets like this:
+   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+   E  E  E  E  E  E  E  E  E  E  E  O  E  O  E  O  O  0  0  0  0  0  0  0  0  c  O  O  O  O  O  O  O  O  O  O  O ...
+  where
+   E = encrypted_even,
+   O = encrypted_odd,
+   e = clear_was_encrypted_even,
+   o = clear_was_encrypted_odd,
+   c = clear
+  and suppose the suggested cluster size is 10 (this could be for a function with internal parallelism 8)
+
+  1) we define the cluster to include packets 0-9 and
+  call decrypt_packets
+  a possible result is that the function call
+  - returns 8 (8 packets available)
+  - the buffer contains now this
+  -----------------------------
+   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+   e  e  e  e  e  e  e  e  E  E  E  O  E  O  E  O  O  0  0  0  0  0  0  0  0  c  O  O  O  O  O  O  O  O  O  O  O ...
+                          -----
+  - the modified cluster covers 8-9 [continue reading, but then see note 1 below]
+  so, we can use the first 8 packets of the original cluster (0-7)
+
+  2) now, we define cluster over 8-17 and call decrypt_packets
+  a possible result is:
+  - returns 3 (3 packets available)
+  - the buffer contains now this (!!!)
+                          -----------------------------
+   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+   e  e  e  e  e  e  e  e  e  e  e  O  e  O  e  O  O  0  0  0  0  0  0  0  0  c  O  O  O  O  O  O  O  O  O  O  O ...
+                                   --    --    --------
+  - the modified cluster covers 11-11,13-13,15-17 [continue reading, but then see note 1 below]
+  so, we can use the first 3 packets of the original cluster (8-10)
+
+  3) now, we define cluster over 11-20 and call decrypt packets (defining a cluster 11-11,13-13,15-22 would be better)
+  a possible result is:
+  - returns 10 (10 packets available)
+  - the buffer contains now this
+                                   -----------------------------
+   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+   e  e  e  e  e  e  e  e  e  e  e  o  e  o  e  o  o  o  o  o  o  0  0  0  0  c  O  O  O  O  O  O  O  O  O  O  O ...
+
+  - the modified cluster is empty
+  so, we can use the first 10 packets of the original cluster (11-20)
+  What it happened is that the second call decrypted packets 12 and 14 but they were
+  not made available because packet 11 was still encrypted,
+  the third call decrypted 11,13,15-20 and included 12 and 14 as available too.
+
+  4) now, we define cluster over 21-30 and call decrypt packets
+  a possible result is:
+  - returns 9 (9 packets available)
+  - the buffer contains now this
+                                                                 -----------------------------
+   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+   e  e  e  e  e  e  e  e  e  e  e  o  e  o  e  o  o  o  o  o  o  o  o  o  o  c  o  o  o  o  O  O  O  O  O  O  O ...
+                                                                                            --
+  - the modified cluster covers 30-30
+  so, we can use the first 9 packets of the original cluster (21-29)
+  What happened is that packet 25 could be skipped because it is in clear.
+
+  Note that the suggested cluster size (10) is higher than the maximum number
+  of packets that can be really decrypted (8), but we are able to skip 12 and 14
+  in step 3) and run the decryption on a full 8 packets group.
+  In the same way, we were able to skip 25 in step 4).
+  There are three kinds of "free" packets we can skip:
+  - packets decrypted in a previous call (as 12 and 14)
+  - packets already in clear (as 25)
+  - packets with a payload of less than 8 bytes (clear==encrypted!)
+
+  Note also that we could have defined a better cluster in step 3
+  (11-11,13-13,15-22), using what step 2 had returned. The risk of not
+  having 8 packets to decrypt would have been smaller (consider the case
+  where 19 and 20 were "c").
+
+  Final considerations:
+  - you can use a bigger or smaller cluster than the suggested number of packets
+  - every call to decrypt_packets has a *fixed* CPU cost, so you should try to
+    not run it with a few packets, when possible
+  - decrypt_packets can't decrypt even and odd at the same time; it guarantees
+    that the first packet will be decrypted and tries to decrypt as many packets
+    as possible
+  - clear packets in the middle of encrypted packets don't happen in real world,
+    but E,E,E,O,E,O,O,O sequences do happen (audio/video muxing problems?) and
+    small packets (<8 bytes) happen frequently; the ability to skip is useful.
+
+  note 1:
+    As the returned cluster will not have more ranges than the passed one, what it is
+    described above is not actually true.
+    In the step 1) the returned cluster will cover 8-9, but in step 2) it will
+    cover 11-17 (some extra packets had to remain in); this lack of information
+    prevents us from using an optimal 11-11,13-13,15-22 in step 3). Note that
+    in any case step 3) will decrypt 11,13,15,16,17,18,19,20 thanks to the
+    extra margin we use (we put ten packets (including 19 and 20) even if the
+    parallelism was just 8, and it was a good idea; but if 19 and 20 were of
+    type c, we would have run the decryption with only 6/8 efficiency).
+    This problem can be prevented by using ranges with only one packet: in
+    step 2) we would have passed
+    8-8,9-9,10-10,11-11,12-12,13-13,14-14,15-15,16-16,17-17
+    and got back
+    11-11,13-13,15-17.
+
+
+------------
+API EXAMPLES
+------------
+
+Some examples of how the API can be used (this is not real code, so it
+may have typos or other bugs).
+
+
+Example 1: (big linear buffer, simple use of cluster)
+
+  unsigned char *p;
+  unsigned char *cluster[3];
+  for(p=start;p<end;){
+    cluster[0]=p;cluster[1]=end;
+    cluster[2]=NULL;
+    p+=188*decrypt_packets(cluster);
+  }
+  //consume(start,end);
+
+
+Example 2: (circular buffer, simple use of cluster)
+
+  unsigned char *p;
+  unsigned char *cluster[5];
+
+  while(1){
+    if(read==write){
+      //buffer is empty
+      //write=refill_buffer(write,start,end);
+      continue;
+    }
+    else if(read<write){
+      cluster[0]=read;cluster[1]=write;
+      cluster[2]=NULL;
+    }
+    else{
+      cluster[0]=read;cluster[1]=end;
+      cluster[2]=start;cluster[3]=write;
+      cluster[4]=NULL;
+    }
+    new_read=read+188*decrypt_packets(cluster);
+    if(new_read<=end){
+      //consume(read,new_read);
+    }
+    else{
+      new_read=start+(new_read-end);
+      //consume(read,end);
+      //consume(start,new_read);
+    }
+    read=new_read;
+    if(read==end) read=start;
+  }
+
+
+Example 3: (undefined buffer structure, advanced use of cluster)
+
+  unsigned char *packets[1000000];
+  unsigned char *cluster[142]; //if suggested packets is 70
+  
+  cluster[0]=NULL;
+  for(n=0;n<1000000;){
+    i=0;
+    while(cluster[2*i]!=NULL) i++; //preserve returned ranges
+    for(k=i;k<70&&n<1000000;k++,n++){
+      cluster[2*k]=packets[n];cluster[2*k+1]=packets[n]+188;
+    }
+    cluster[2*k]=NULL;
+    decrypt_packets(cluster);
+  }
+  //consume_all_packets();
diff --git a/contrib/sasc-ng/FFdecsa/docs/technical_background.txt b/contrib/sasc-ng/FFdecsa/docs/technical_background.txt
new file mode 100644 (file)
index 0000000..613e208
--- /dev/null
@@ -0,0 +1,341 @@
+-------
+FFdecsa
+-------
+
+This doc is for people who looked into the source code and found it
+difficult to believe that this is a decsa algorithm, as it appears
+completely different from other decsa implementations.
+
+It appears different because it is different. Being different is what
+enables it to be a lot faster than all the others (currently it has more
+than 800% the speed of the best version I was able to find)
+
+The csa algo was designed to be run in hardware, but people are now
+running it in software.
+
+Hardware has data lines carrying bits and functional blocks doing
+calculations (logic operations, adders, shifters, table lookup, ...),
+software instead uses memory to contain data values and executes a
+sequence of instructions to transform the values. As a consequence,
+writing a software implementation of a hardware algorithm can be
+inefficient.
+
+For example, if you have 32 data lines, you can permutate the bits with
+zero cost in hardware (you just permute the physical traces), but if you
+have the bits in a 32 bit variable you have to use 32 "and" operations
+with 32 different masks, 32 shifts and 31 "or" operations (if you
+suggest using "if"s testing the bits one by one you know nothing about
+how jump prediction works in modern processors).
+
+So the approach is *emulating the hardware*.
+
+Then there are some additional cool tricks.
+
+TRICK NUMBER 0: emulate the hardware
+------------------------------------
+We will work on bits one by one, that is a 4 bit word is now four
+variables. In this way we revert complex software operations into
+hardware emulation:
+
+  software                      hardware
+  -------------------------------------------
+  copy values                   copy values
+  logic op                      logic op
+  (bit permut.) ands+shifts+ors copy values
+  additions                     logic op emulating adders
+  (comparisons) if              logic op selecting one of the two results
+  lookup tables                 logic op synthetizing a ROM (*)
+
+(*) sometimes lookup tables can be converted to logic expressions
+
+The sbox in the stream cypher have been converted to efficient logic
+operations using a custom written software (look into logic directory)
+and is responsible for a lot of speed increase. Maybe there exists a
+slightly better way to express the sbox as logical expressions, but it
+would be a minuscule improvement. The sbox in the block cypher can't be
+converted to efficient logic operations (8 bits of inputs are just too
+much) and is implemeted with a traditional lookup in an array.
+
+But there is a problem; if we want to process bits, but our external
+input and output wants bytes. We need conversion routines. Conversion
+routines are similar to the awful permutations we described before, so
+this has to be done efficiently someway.
+
+
+TRICK NUMBER 1: virtual shift registers
+---------------------------------------
+Shift registers are normally implemented by moving all data around.
+Better leave the data in the same memory locations and redefine where
+the start of the register is (updating a pointer). That is called
+virtual shift register.
+
+
+TRICK NUMBER 2: parallel bitslice
+---------------------------------
+Implementing the algorithm as described in tricks 1 and 2 give us about
+15% of the speed of a traditional implementation. This happens because
+we work on only one bit, even if our CPU is 32 bit wide. But *we can
+process 32 different packets at the same time*. This is called
+"bitslice" method. It can be done only if the program flow is not
+dependent of the data (if, while,...). Luckily this is true.
+Things like
+  if(a){
+    b=c&d;
+  }
+  else{
+    b=e&f;
+  }
+can be coded as (think of how hardware would implement this)
+  b1=c&d;
+  b2=e&f;
+  b=b2^(a&(b1^b2));
+and things like
+  if(a){
+    b=c&d
+  }
+can be transformed in the same way, as they may be written as
+  if(a){
+    b=c&d
+  }
+  else{
+    b=b;
+  }
+It could look wasteful, but it is not; and destroys data dependency.
+
+Our codes takes the same time as before, but produces 32 results, so
+speed is now 480% the speed of a traditional implementation.
+
+
+TRICK NUMBER 3: multimedia instructions
+---------------------------------------
+If our CPU is 32 bit but it can also process larger blocks of data
+efficiently (multimedia instructions), we can use them. We only need
+logic ops and these are typically available.
+
+We can use MMX and work on 64 packets, or SSE and work on 128 packets.
+The speed doesn't automatically double going from 32 to 64 because the
+integer registers of the processor are normally faster. However, some
+speed is gained in this way.
+
+Multimedia instructions are often used by writing assembler by hand, but
+compilers are very good in doing register allocation, loop unrolling and
+instruction scheduling, so it is better to write the code in C and use
+native multimedia data types (intrinsics).
+
+Depending on number of available registers, execution latency, number of
+execution units in the CPU, it may be good to process more than one data
+block at the same time, for example 2 64bit MMX values. In this case we
+work on 128 bits by simulating a 128 bit op with two consecutive 64 bit
+op. This may or may not help (apparently not because x86 architecture
+has a small number of registers).
+
+We can also try working on 96 bit, pairing a MMX and an int op, or 192
+bit by using MMX and SSE. While this is doable in theory and could
+exploit different execution units in the CPU, speed doesn't improve
+(because of cache line handling problems inside the CPU, maybe).
+
+Besides int, MMX, SSE, we can use long long int (64 bit) and, why not,
+unsigned char.
+
+Using groups of unsigned chars (8 or 16) could give the compiler an
+opportunity to insert multimedia instructions automatically. For
+example, icc can use one MMX istruction to do
+  unsigned char a[8],b[8],c[8];
+  for(i=0;i<8;i++){
+    a[i]=b[i]&c[i];
+  }
+Some compilers (like icc) are efficient in this case, but using
+intrinsics manually is generally faster.
+
+All these experiments can be easily done if the code is written in a way
+which abstracts the data type used. This is not easy but doable, all the
+operations on data become (inlined) function calls or preprocessor
+macros. Good compilers are able to simplify all the abstraction at
+compile time and generate perfect code (gcc is great).
+
+The data abstraction used in the code is called "group".
+
+
+TRICK NUMBER 4: parallel byteslice
+----------------------------------
+The bitslice method works wonderfully on the stream cypher, but can't be
+applied to the block cypher because of the evil big look up table.
+
+As we have to convert input data from normal to bitslice before starting
+processing and from bitslice to normal before output, we convert the
+stream cypher output to normal before the block calculations and do the
+block stage in a traditional way.
+
+There are some xors in the block cypher; so we arrange bytes from
+different packets side by side and use multimedia instructions to work
+on many bytes at the same time. This is not exactly bitslice, maybe it
+is called byteslice. The conversion routines are similar (just a bit
+simpler).
+
+The data type we use to do this in the code is called "batch".
+
+The virtual shift register described in trick number 2 is useful too.
+
+The look up table is the only thing which is done serially one byte at a
+time. Luckily if we do it on 32 or 64 bytes the loop is heavily
+unrolled, and the compiler and the CPU manage to get a good speed
+because there is little dependency between instructions.
+
+
+TRICK NUMBER 5: efficient bit permutation
+-----------------------------------------
+The block cypher has a bit permutation part. As we are not in a bit
+sliced form at that point, permuting bits in a byte takes 8 masks, 8
+and, 7 or; but three bits move in the same direction, so we make it with
+6 masks, 6 and, 5 or. Batch processing through multimedia instructions
+is applicable too.
+
+
+TRICK NUMBER 6: efficient normal<->slice conversion
+---------------------------------------------------
+The bitslice<->normal conversion routines are a sort of transposition
+operation, that is you have bits in rows and want them in columns. This
+can be done efficiently. For example, transposition of 8 bytes (matrix
+of 8x8=64 bits) can be done this way (we want to exchange bit[i][j] with
+bit[j][i] and we assume bit 0 is the MSB in the byte):
+
+  // untested code, may be bugged
+  unsigned char a[8];
+  unsigned char b[8];
+  for(i=0;i<8;i++) b[i]=0;
+  for(i=0;i<8;i++){
+    for(j=0;j<8;j++){
+      b[i]|=((a[j]>>(7-i)&1))<<(7-j);
+    }
+  }
+
+but it is slow (128 shifts, 64 and, 64 or), or
+
+  // untested code, may be bugged
+  unsigned char a[8];
+  unsigned char b[8];
+  for(i=0;i<8;i++) b[i]=0;
+  for(i=0;i<8;i++){
+    for(j=0;j<8;j++){
+      if(a[j]&(1<<(7-i))) b[i]|=1<<(7-j);
+    }
+  }
+
+but is very very slow (128 shifts, 64 and, 64 or, 128 unpredictable
+if!), or using a>>=1 and b<<=1, which gains you nothing, or
+
+  // untested code, may be bugged
+  unsigned char a[8];
+  unsigned char b[8];
+  unsigned char top,bottom;
+  for(j=0;j<1;j++){
+    for(i=0;i<4;i++){
+      top=   a[8*j+i];
+      bottom=a[8*j+4+i];
+      a[8*j+i]=   (top&0xf0)    |((bottom&0xf0)>>4);
+      a[8*j+4+i]=((top&0x0f)<<4)| (bottom&0x0f);
+    }
+  }
+  for(j=0;j<2;j++){
+    for(i=0;i<2;i++){
+      top=   a[4*j+i];
+      bottom=a[4*j+2+i];
+      a[4*j+i]  = (top&0xcc)    |((bottom&0xcc)>>2);
+      a[4*j+2+i]=((top&0x33)<<2)| (bottom&0x33);
+    }
+  }
+  for(j=0;j<4;j++){
+    for(i=0;i<1;i++){
+      top=   a[2*j+i];
+      bottom=a[2*j+1+i];
+      a[2*j+i]  = (top&0xaa)    |((bottom&0xaa)>>1);
+      a[2*j+1+i]=((top&0x55)<<1)| (bottom&0x55);
+    }
+  }
+  for(i=0;i<8;i++) b[i]=a[i]; //easy to integrate into one of the stages above
+
+which is very fast (24 shifts, 48 and, 24 or) and has redundant loops
+and address calculations which will be optimized away by the compiler.
+It can be written as 3 nested loops but it becomes less readable and
+makes it difficult to have results in b without an extra copy. The
+compiler always unrolls heavily.
+
+The gain is much bigger when operating with 32 bit or 64 bit values (we
+are going from N^2 to Nlog(N)). This method is used for rectangular
+matrixes too (they have to be seen as square matrixes side by side).
+Warning: this code is not *endian independent* if you use ints to work
+on 4 bytes. Running it on a big endian processor will give you a
+different and strange kind of bit rotation if you don't modify masks and
+shifts.
+
+This is done in the code using int or long long int. It should be
+possible to use MMX instead of long long int and it could be faster, but
+this code doesn't cost a great fraction of the total time. There are
+problems with the shifts, as multimedia instructions do not have all
+possible kind of shift we need (SSE has none!).
+
+
+TRICK NUMBER 7: try hard to process packets together
+----------------------------------------------------
+As we are able to process many packets together, we have to avoid
+running with many slots empty. Processing one packet or 64 packets takes
+the same time if the internal parallelism is 64! So we try hard to
+aggregate packets that can be processed together; for simplicity reasons
+we don't mix packets with even and odd parity (different keys), even if
+it should be doable with a little effort. Sometimes the transition from
+even to odd parity and viceversa is not sharp, but there are sequences
+like EEEEEOEEOEEOOOO. We try to group all the E together even if there
+are O between them. This out-of-order processing complicates the
+interface to the applications a bit but saves us three or four runs with
+many empty slots.
+
+We have also logic to process together packets with a different size of
+the payload, which is not always 184 bytes. This involves sorting the
+packets by size before processing and careful operation of the 23
+iteration loop to exclude some packets from the calculations. It is not
+CPU heavy.
+
+Packets with payload <8 bytes are identical before and after decryption
+(!), so we skip them without using a slot. (according to DVB specs these
+kind of packets shouldn't happen, but they are used in the real world).
+
+
+TRICK NUMBER 8: try to avoid doing the same thing many times
+------------------------------------------------------------
+Some calculations related to keys are only done when the keys are set,
+then all the values depending on keys are stored in a convenient form
+and used everytime we convert a group of packets.
+
+
+TRICK NUMBER 9: compiler
+------------------------
+
+Compilers have a lot of optimization options. I used -march to target my
+CPU and played with unsual options. In particular
+  "--param max-unrolled-insns=500"
+does a good job on the tricky table lookup in the block cypher. Bigger
+values unroll too much somewhere and loose speed. All the testing has
+been done on an AthlonXP CPU with a specific version of gcc
+  gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7)
+Other combinations of CPU and compiler can give different speeds. If the
+compiler is not able to simplify the group and batch structures and
+stores everything in memory instead of registers, performance will be
+low.
+
+Absolutely use a good compiler!
+
+Note: the same code can be compiled in C or C++ mode. g++ gives a 3%
+speed increase compared to gcc (I suppose some stricter constraint on
+array and pointers in C++ mode gives the optimizer more freedom).
+
+
+TRICK NUMBER a: a lot of brain work
+-----------------------------------
+The code started as very slow but correct implementation and was then
+tweaked for months with a lot of experimentation and by adding all the
+good ideas one after another to achieve little steps toward the best
+speed possible, while continously testing that nothing had been broken.
+
+Many hours were spent on this code.
+
+Enjoy the result.
diff --git a/contrib/sasc-ng/FFdecsa/fftable.h b/contrib/sasc-ng/FFdecsa/fftable.h
new file mode 100644 (file)
index 0000000..ed6345f
--- /dev/null
@@ -0,0 +1,55 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ *               2003-2004  fatih89r
+ *
+ * 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 FFTABLE_H
+#define FFTABLE_H
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data)
+{
+#if 0
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+#else
+  *(((long long *)tab)+g)=*((long long *)data);
+#endif
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g)
+{
+#if 1
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+#else
+  *((long long *)data)=*(((long long *)tab)+g);
+#endif
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g)
+{
+  for(int j=0;j<n;j++) *(data+j)^=*(tab+8*g+j);
+}
+
+#undef XOREQ_BEST_BY
+static inline void XOREQ_BEST_BY(unsigned char *d, unsigned char *s)
+{
+       XOR_BEST_BY(d, d, s);
+}
+
+#endif //FFTABLE_H 
diff --git a/contrib/sasc-ng/FFdecsa/logic/Makefile b/contrib/sasc-ng/FFdecsa/logic/Makefile
new file mode 100644 (file)
index 0000000..3e4aed1
--- /dev/null
@@ -0,0 +1,10 @@
+all: logic
+
+logic: logic.o
+       gcc -o logic logic.o
+
+logic.o: logic.c
+       gcc -O3 -march=athlon-xp -c logic.c
+
+clean:
+       rm logic *.o
diff --git a/contrib/sasc-ng/FFdecsa/logic/logic.c b/contrib/sasc-ng/FFdecsa/logic/logic.c
new file mode 100644 (file)
index 0000000..8be38dc
--- /dev/null
@@ -0,0 +1,330 @@
+/* logic -- synthetize logic functions with 4 inputs
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+
+
+/* Can we use negated inputs? */
+#define noNEGATEDTOO
+
+
+#include <stdio.h>
+
+
+/*
+ * abcd
+ */
+
+#define BINARY(b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0) \
+  ((b15)<<15)|((b14)<<14)|((b13)<<13)|((b12)<<12)| \
+  ((b11)<<11)|((b10)<<10)|((b9) << 9)|((b8) << 8)| \
+  ((b7) << 7)|((b6) << 6)|((b5) << 5)|((b4) << 4)| \
+  ((b3) << 3)|((b2) << 2)|((b1) << 1)|((b0) << 0)
+
+struct fun{
+  int level;
+  int op_type;
+  int op1;
+  int op2;
+};
+
+struct fun db[65536];
+int n_fun;
+
+#define LEVEL_ALOT 1000000
+
+#define OP_FALSE 0
+#define OP_TRUE  1
+#define OP_SRC   2
+#define OP_AND   3
+#define OP_OR    4
+#define OP_XOR   5
+
+#define SRC_A 10
+#define SRC_B 20
+#define SRC_C 30
+#define SRC_D 40
+#define SRC_AN 11
+#define SRC_BN 21
+#define SRC_CN 31
+#define SRC_DN 41
+
+void dump_element_prefix(int);
+void dump_element_infix(int);
+
+int main(void){
+  int i,j;
+  int l,p1,p2;
+  int candidate;
+  int max_p2_lev;
+  
+  for(i=0;i<65536;i++){
+    db[i].level=LEVEL_ALOT;
+  }
+  n_fun=0;
+
+  db[0].level=0;
+  db[0].op_type=OP_FALSE;
+  n_fun++;
+
+  db[65535].level=0;
+  db[65535].op_type=OP_TRUE;
+  n_fun++;
+
+  db[BINARY(0,0,0,0, 0,0,0,0,  1,1,1,1, 1,1,1,1)].level=0;
+  db[BINARY(0,0,0,0, 0,0,0,0,  1,1,1,1, 1,1,1,1)].op_type=OP_SRC;
+  db[BINARY(0,0,0,0, 0,0,0,0,  1,1,1,1, 1,1,1,1)].op1=SRC_A;
+  n_fun++;
+
+  db[BINARY(0,0,0,0, 1,1,1,1,  0,0,0,0, 1,1,1,1)].level=0;
+  db[BINARY(0,0,0,0, 1,1,1,1,  0,0,0,0, 1,1,1,1)].op_type=OP_SRC;
+  db[BINARY(0,0,0,0, 1,1,1,1,  0,0,0,0, 1,1,1,1)].op1=SRC_B;
+  n_fun++;
+
+  db[BINARY(0,0,1,1, 0,0,1,1,  0,0,1,1, 0,0,1,1)].level=0;
+  db[BINARY(0,0,1,1, 0,0,1,1,  0,0,1,1, 0,0,1,1)].op_type=OP_SRC;
+  db[BINARY(0,0,1,1, 0,0,1,1,  0,0,1,1, 0,0,1,1)].op1=SRC_C;
+  n_fun++;
+
+  db[BINARY(0,1,0,1, 0,1,0,1,  0,1,0,1, 0,1,0,1)].level=0;
+  db[BINARY(0,1,0,1, 0,1,0,1,  0,1,0,1, 0,1,0,1)].op_type=OP_SRC;
+  db[BINARY(0,1,0,1, 0,1,0,1,  0,1,0,1, 0,1,0,1)].op1=SRC_D;
+  n_fun++;
+#ifdef NEGATEDTOO
+  db[BINARY(1,1,1,1, 1,1,1,1,  0,0,0,0, 0,0,0,0)].level=0;
+  db[BINARY(1,1,1,1, 1,1,1,1,  0,0,0,0, 0,0,0,0)].op_type=OP_SRC;
+  db[BINARY(1,1,1,1, 1,1,1,1,  0,0,0,0, 0,0,0,0)].op1=SRC_AN;
+  n_fun++;
+
+  db[BINARY(1,1,1,1, 0,0,0,0,  1,1,1,1, 0,0,0,0)].level=0;
+  db[BINARY(1,1,1,1, 0,0,0,0,  1,1,1,1, 0,0,0,0)].op_type=OP_SRC;
+  db[BINARY(1,1,1,1, 0,0,0,0,  1,1,1,1, 0,0,0,0)].op1=SRC_BN;
+  n_fun++;
+
+  db[BINARY(1,1,0,0, 1,1,0,0,  1,1,0,0, 1,1,0,0)].level=0;
+  db[BINARY(1,1,0,0, 1,1,0,0,  1,1,0,0, 1,1,0,0)].op_type=OP_SRC;
+  db[BINARY(1,1,0,0, 1,1,0,0,  1,1,0,0, 1,1,0,0)].op1=SRC_CN;
+  n_fun++;
+
+  db[BINARY(1,0,1,0, 1,0,1,0,  1,0,1,0, 1,0,1,0)].level=0;
+  db[BINARY(1,0,1,0, 1,0,1,0,  1,0,1,0, 1,0,1,0)].op_type=OP_SRC;
+  db[BINARY(1,0,1,0, 1,0,1,0,  1,0,1,0, 1,0,1,0)].op1=SRC_DN;
+  n_fun++;
+#endif
+
+  for(l=0;l<100;l++){
+    printf("calculating level %i\n",l);
+    for(p1=1;p1<65536;p1++){
+      if(db[p1].level==LEVEL_ALOT) continue;
+      max_p2_lev=l-db[p1].level-1;
+      for(p2=p1+1;p2<65536;p2++){
+        if(db[p2].level>max_p2_lev) continue;
+
+        candidate=p1&p2;
+        if(db[candidate].level==LEVEL_ALOT){
+          //found new
+          db[candidate].level=db[p1].level+db[p2].level+1;
+          db[candidate].op_type=OP_AND;
+          db[candidate].op1=p1;
+          db[candidate].op2=p2;
+          n_fun++;
+       }
+
+        candidate=p1|p2;
+        if(db[candidate].level==LEVEL_ALOT){
+          //found new
+          db[candidate].level=db[p1].level+db[p2].level+1;
+          db[candidate].op_type=OP_OR;
+          db[candidate].op1=p1;
+          db[candidate].op2=p2;
+          n_fun++;
+       }
+
+        candidate=p1^p2;
+        if(db[candidate].level==LEVEL_ALOT){
+          //found new
+          db[candidate].level=db[p1].level+db[p2].level+1;
+          db[candidate].op_type=OP_XOR;
+          db[candidate].op1=p1;
+          db[candidate].op2=p2;
+          n_fun++;
+       }
+
+      }
+    }
+    printf("num fun=%i\n\n",n_fun);
+    fflush(stdout);
+    if(n_fun>=65536) break;
+  }
+
+
+  for(i=0;i<65536;i++){
+    if(db[i].level==LEVEL_ALOT) continue;
+
+    printf("PREFIX ");
+    for(j=15;j>=0;j--){
+      printf("%i",i&(1<<j)?1:0);
+      if(j%4==0) printf(" ");
+      if(j%8==0) printf(" ");
+    }
+    printf(" : lev %2i: ",db[i].level);
+    dump_element_prefix(i);
+    printf("\n");
+
+    printf("INFIX  ");
+    for(j=15;j>=0;j--){
+      printf("%i",i&(1<<j)?1:0);
+      if(j%4==0) printf(" ");
+      if(j%8==0) printf(" ");
+    }
+    printf(" : lev %2i: ",db[i].level);
+    dump_element_infix(i);
+    printf("\n");
+  }
+  
+  return 0;
+}
+
+void dump_element_prefix(int e){
+  if(db[e].level==LEVEL_ALOT){
+    printf("PANIC!\n");
+    return;
+  };
+  switch(db[e].op_type){
+  case OP_FALSE:
+    printf("0");
+    break;
+  case OP_TRUE:
+    printf("1");
+    break;
+  case OP_SRC:
+    switch(db[e].op1){
+    case SRC_A:
+      printf("a");
+      break;
+    case SRC_B:
+      printf("b");
+      break;
+    case SRC_C:
+      printf("c");
+      break;
+    case SRC_D:
+      printf("d");
+      break;
+    case SRC_AN:
+      printf("an");
+      break;
+    case SRC_BN:
+      printf("bn");
+      break;
+    case SRC_CN:
+      printf("cn");
+      break;
+    case SRC_DN:
+      printf("dn");
+      break;
+    }
+    break;
+  case OP_AND:
+    printf("FFAND(");
+    dump_element_prefix(db[e].op1);
+    printf(",");
+    dump_element_prefix(db[e].op2);
+    printf(")");
+    break;
+  case OP_OR:
+    printf("FFOR(");
+    dump_element_prefix(db[e].op1);
+    printf(",");
+    dump_element_prefix(db[e].op2);
+    printf(")");
+    break;
+  case OP_XOR:
+    printf("FFXOR(");
+    dump_element_prefix(db[e].op1);
+    printf(",");
+    dump_element_prefix(db[e].op2);
+    printf(")");
+    break;
+  }
+}
+
+void dump_element_infix(int e){
+  if(db[e].level==LEVEL_ALOT){
+    printf("PANIC!\n");
+    return;
+  };
+  switch(db[e].op_type){
+  case OP_FALSE:
+    printf("0");
+    break;
+  case OP_TRUE:
+    printf("1");
+    break;
+  case OP_SRC:
+    switch(db[e].op1){
+    case SRC_A:
+      printf("a");
+      break;
+    case SRC_B:
+      printf("b");
+      break;
+    case SRC_C:
+      printf("c");
+      break;
+    case SRC_D:
+      printf("d");
+      break;
+    case SRC_AN:
+      printf("an");
+      break;
+    case SRC_BN:
+      printf("bn");
+      break;
+    case SRC_CN:
+      printf("cn");
+      break;
+    case SRC_DN:
+      printf("dn");
+      break;
+    }
+    break;
+  case OP_AND:
+    printf("( ");
+    dump_element_infix(db[e].op1);
+    printf("&");
+    dump_element_infix(db[e].op2);
+    printf(" )");
+    break;
+  case OP_OR:
+    printf("( ");
+    dump_element_infix(db[e].op1);
+    printf("|");
+    dump_element_infix(db[e].op2);
+    printf(" )");
+    break;
+  case OP_XOR:
+    printf("( ");
+    dump_element_infix(db[e].op1);
+    printf("^");
+    dump_element_infix(db[e].op2);
+    printf(" )");
+    break;
+  }
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_032_4char.h b/contrib/sasc-ng/FFdecsa/parallel_032_4char.h
new file mode 100644 (file)
index 0000000..b9295f8
--- /dev/null
@@ -0,0 +1,206 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1,s2,s3,s4;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 32
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0;
+  res.s2=0x0;
+  res.s3=0x0;
+  res.s4=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xff;
+  res.s2=0xff;
+  res.s3=0xff;
+  res.s4=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  res.s3=~a.s3;
+  res.s4=~a.s4;
+  return res;
+}
+
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+g)=*((int *)data);
+  *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+g);
+  *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+  }
+}
+
+struct batch_t{
+  unsigned char s1,s2,s3,s4;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 4
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x29;
+  res.s2=0x29;
+  res.s3=0x29;
+  res.s4=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x02;
+  res.s2=0x02;
+  res.s3=0x02;
+  res.s4=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x04;
+  res.s2=0x04;
+  res.s3=0x04;
+  res.s4=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x10;
+  res.s2=0x10;
+  res.s3=0x10;
+  res.s4=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x40;
+  res.s2=0x40;
+  res.s3=0x40;
+  res.s4=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x80;
+  res.s2=0x80;
+  res.s3=0x80;
+  res.s4=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  res.s3=a.s3<<n;
+  res.s4=a.s4<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  res.s3=a.s3>>n;
+  res.s4=a.s4>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_032_4charA.h b/contrib/sasc-ng/FFdecsa/parallel_032_4charA.h
new file mode 100644 (file)
index 0000000..a8f295b
--- /dev/null
@@ -0,0 +1,171 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1[4];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 32
+
+group static inline FF0(){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=~a.s1[i];
+  return res;
+}
+
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+g)=*((int *)data);
+  *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+g);
+  *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+  }
+}
+
+struct batch_t{
+  unsigned char s1[4];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 4
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<4;i++) res.s1[i]=a.s1[i]>>n;
+  return res;
+}
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_032_int.h b/contrib/sasc-ng/FFdecsa/parallel_032_int.h
new file mode 100644 (file)
index 0000000..a21fe31
--- /dev/null
@@ -0,0 +1,55 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 "parallel_std_def.h"
+
+typedef unsigned int group;
+#define GROUP_PARALLELISM 32
+#define FF0()      0x0
+#define FF1()      0xffffffff
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+g)=*((int *)data);
+  *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+g);
+  *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+  }
+}
+
+typedef unsigned int batch;
+#define BYTES_PER_BATCH 4
+#define B_FFN_ALL_29() 0x29292929
+#define B_FFN_ALL_02() 0x02020202
+#define B_FFN_ALL_04() 0x04040404
+#define B_FFN_ALL_10() 0x10101010
+#define B_FFN_ALL_40() 0x40404040
+#define B_FFN_ALL_80() 0x80808080
+
+#define M_EMPTY()
diff --git a/contrib/sasc-ng/FFdecsa/parallel_064_2int.h b/contrib/sasc-ng/FFdecsa/parallel_064_2int.h
new file mode 100644 (file)
index 0000000..ffe331a
--- /dev/null
@@ -0,0 +1,175 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned int s1;
+  unsigned int s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0;
+  res.s2=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xffffffff;
+  res.s2=0xffffffff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+struct batch_t{
+  unsigned int s1;
+  unsigned int s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x29292929;
+  res.s2=0x29292929;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x02020202;
+  res.s2=0x02020202;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x04040404;
+  res.s2=0x04040404;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x10101010;
+  res.s2=0x10101010;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x40404040;
+  res.s2=0x40404040;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x80808080;
+  res.s2=0x80808080;
+  return res;
+}
+
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_064_8char.h b/contrib/sasc-ng/FFdecsa/parallel_064_8char.h
new file mode 100644 (file)
index 0000000..956c980
--- /dev/null
@@ -0,0 +1,274 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1,s2,s3,s4,s5,s6,s7,s8;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0;
+  res.s2=0x0;
+  res.s3=0x0;
+  res.s4=0x0;
+  res.s5=0x0;
+  res.s6=0x0;
+  res.s7=0x0;
+  res.s8=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xff;
+  res.s2=0xff;
+  res.s3=0xff;
+  res.s4=0xff;
+  res.s5=0xff;
+  res.s6=0xff;
+  res.s7=0xff;
+  res.s8=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  res.s5=a.s5&b.s5;
+  res.s6=a.s6&b.s6;
+  res.s7=a.s7&b.s7;
+  res.s8=a.s8&b.s8;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  res.s5=a.s5|b.s5;
+  res.s6=a.s6|b.s6;
+  res.s7=a.s7|b.s7;
+  res.s8=a.s8|b.s8;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  res.s5=a.s5^b.s5;
+  res.s6=a.s6^b.s6;
+  res.s7=a.s7^b.s7;
+  res.s8=a.s8^b.s8;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  res.s3=~a.s3;
+  res.s4=~a.s4;
+  res.s5=~a.s5;
+  res.s6=~a.s6;
+  res.s7=~a.s7;
+  res.s8=~a.s8;
+  return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+struct batch_t{
+  unsigned char s1,s2,s3,s4,s5,s6,s7,s8;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  res.s5=a.s5&b.s5;
+  res.s6=a.s6&b.s6;
+  res.s7=a.s7&b.s7;
+  res.s8=a.s8&b.s8;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  res.s5=a.s5|b.s5;
+  res.s6=a.s6|b.s6;
+  res.s7=a.s7|b.s7;
+  res.s8=a.s8|b.s8;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  res.s5=a.s5^b.s5;
+  res.s6=a.s6^b.s6;
+  res.s7=a.s7^b.s7;
+  res.s8=a.s8^b.s8;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x29;
+  res.s2=0x29;
+  res.s3=0x29;
+  res.s4=0x29;
+  res.s5=0x29;
+  res.s6=0x29;
+  res.s7=0x29;
+  res.s8=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x02;
+  res.s2=0x02;
+  res.s3=0x02;
+  res.s4=0x02;
+  res.s5=0x02;
+  res.s6=0x02;
+  res.s7=0x02;
+  res.s8=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x04;
+  res.s2=0x04;
+  res.s3=0x04;
+  res.s4=0x04;
+  res.s5=0x04;
+  res.s6=0x04;
+  res.s7=0x04;
+  res.s8=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x10;
+  res.s2=0x10;
+  res.s3=0x10;
+  res.s4=0x10;
+  res.s5=0x10;
+  res.s6=0x10;
+  res.s7=0x10;
+  res.s8=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x40;
+  res.s2=0x40;
+  res.s3=0x40;
+  res.s4=0x40;
+  res.s5=0x40;
+  res.s6=0x40;
+  res.s7=0x40;
+  res.s8=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x80;
+  res.s2=0x80;
+  res.s3=0x80;
+  res.s4=0x80;
+  res.s5=0x80;
+  res.s6=0x80;
+  res.s7=0x80;
+  res.s8=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  res.s3=a.s3<<n;
+  res.s4=a.s4<<n;
+  res.s5=a.s5<<n;
+  res.s6=a.s6<<n;
+  res.s7=a.s7<<n;
+  res.s8=a.s8<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  res.s3=a.s3>>n;
+  res.s4=a.s4>>n;
+  res.s5=a.s5>>n;
+  res.s6=a.s6>>n;
+  res.s7=a.s7>>n;
+  res.s8=a.s8>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_064_8charA.h b/contrib/sasc-ng/FFdecsa/parallel_064_8charA.h
new file mode 100644 (file)
index 0000000..b99490b
--- /dev/null
@@ -0,0 +1,171 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1[8];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=~a.s1[i];
+  return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+struct batch_t{
+  unsigned char s1[8];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<8;i++) res.s1[i]=a.s1[i]>>n;
+  return res;
+}
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_064_long.h b/contrib/sasc-ng/FFdecsa/parallel_064_long.h
new file mode 100644 (file)
index 0000000..09f7b95
--- /dev/null
@@ -0,0 +1,39 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ *               2003-2004  fatih89r
+ *
+ * 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 "parallel_std_def.h"
+
+typedef unsigned long long group;
+#define GROUP_PARALLELISM 64
+#define FF0() 0x0ULL
+#define FF1() 0xffffffffffffffffULL
+
+typedef unsigned long long batch;
+#define BYTES_PER_BATCH 8
+#define B_FFN_ALL_29() 0x2929292929292929ULL
+#define B_FFN_ALL_02() 0x0202020202020202ULL
+#define B_FFN_ALL_04() 0x0404040404040404ULL
+#define B_FFN_ALL_10() 0x1010101010101010ULL
+#define B_FFN_ALL_40() 0x4040404040404040ULL
+#define B_FFN_ALL_80() 0x8080808080808080ULL
+
+#define M_EMPTY()
+
+#include "fftable.h"
diff --git a/contrib/sasc-ng/FFdecsa/parallel_064_mmx.h b/contrib/sasc-ng/FFdecsa/parallel_064_mmx.h
new file mode 100644 (file)
index 0000000..3979233
--- /dev/null
@@ -0,0 +1,83 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ *               2003-2004  fatih89r
+ *
+ * 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 <mmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u64 {
+    unsigned int u[2];
+    __m64 v;
+};
+
+static const union __u64 ff0 = {{0x00000000U, 0x00000000U}};
+static const union __u64 ff1 = {{0xffffffffU, 0xffffffffU}};
+
+typedef __m64 group;
+#define GROUP_PARALLELISM 64
+#define FF0()      ff0.v
+#define FF1()      ff1.v
+#define FFAND(a,b) _mm_and_si64((a),(b))
+#define FFOR(a,b)  _mm_or_si64((a),(b))
+#define FFXOR(a,b) _mm_xor_si64((a),(b))
+#define FFNOT(a)   _mm_xor_si64((a),FF1())
+
+/* 64 rows of 64 bits */
+
+static const union __u64 ff29 = {{0x29292929U, 0x29292929U}};
+static const union __u64 ff02 = {{0x02020202U, 0x02020202U}};
+static const union __u64 ff04 = {{0x04040404U, 0x04040404U}};
+static const union __u64 ff10 = {{0x10101010U, 0x10101010U}};
+static const union __u64 ff40 = {{0x40404040U, 0x40404040U}};
+static const union __u64 ff80 = {{0x80808080U, 0x80808080U}};
+
+typedef __m64 batch;
+#define BYTES_PER_BATCH 8
+#define B_FFAND(a,b) FFAND((a),(b))
+#define B_FFOR(a,b)  FFOR((a),(b))
+#define B_FFXOR(a,b) FFXOR((a),(b))
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+#define B_FFSH8L(a,n) _mm_slli_si64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_si64((a),(n))
+
+#define M_EMPTY() _mm_empty()
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2)    do { *(__m64*)d = _mm_xor_si64(*(__m64*)(s1), *(__m64*)(s2)); } while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s)      XOR_8_BY(d, d, s)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s)       do { *(__m64 *)(d) = *(__m64 *)(s); } while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN            8
+
+#undef XOR_BEST_BY
+#define XOR_BEST_BY(d,s1,s2) XOR_8_BY(d,s1,s2)
+
+#include "fftable.h"
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_16char.h b/contrib/sasc-ng/FFdecsa/parallel_128_16char.h
new file mode 100644 (file)
index 0000000..ed28c61
--- /dev/null
@@ -0,0 +1,411 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0;
+  res.s2=0x0;
+  res.s3=0x0;
+  res.s4=0x0;
+  res.s5=0x0;
+  res.s6=0x0;
+  res.s7=0x0;
+  res.s8=0x0;
+  res.s9=0x0;
+  res.s10=0x0;
+  res.s11=0x0;
+  res.s12=0x0;
+  res.s13=0x0;
+  res.s14=0x0;
+  res.s15=0x0;
+  res.s16=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xff;
+  res.s2=0xff;
+  res.s3=0xff;
+  res.s4=0xff;
+  res.s5=0xff;
+  res.s6=0xff;
+  res.s7=0xff;
+  res.s8=0xff;
+  res.s9=0xff;
+  res.s10=0xff;
+  res.s11=0xff;
+  res.s12=0xff;
+  res.s13=0xff;
+  res.s14=0xff;
+  res.s15=0xff;
+  res.s16=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  res.s5=a.s5&b.s5;
+  res.s6=a.s6&b.s6;
+  res.s7=a.s7&b.s7;
+  res.s8=a.s8&b.s8;
+  res.s9=a.s9&b.s9;
+  res.s10=a.s10&b.s10;
+  res.s11=a.s11&b.s11;
+  res.s12=a.s12&b.s12;
+  res.s13=a.s13&b.s13;
+  res.s14=a.s14&b.s14;
+  res.s15=a.s15&b.s15;
+  res.s16=a.s16&b.s16;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  res.s5=a.s5|b.s5;
+  res.s6=a.s6|b.s6;
+  res.s7=a.s7|b.s7;
+  res.s8=a.s8|b.s8;
+  res.s9=a.s9|b.s9;
+  res.s10=a.s10|b.s10;
+  res.s11=a.s11|b.s11;
+  res.s12=a.s12|b.s12;
+  res.s13=a.s13|b.s13;
+  res.s14=a.s14|b.s14;
+  res.s15=a.s15|b.s15;
+  res.s16=a.s16|b.s16;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  res.s5=a.s5^b.s5;
+  res.s6=a.s6^b.s6;
+  res.s7=a.s7^b.s7;
+  res.s8=a.s8^b.s8;
+  res.s9=a.s9^b.s9;
+  res.s10=a.s10^b.s10;
+  res.s11=a.s11^b.s11;
+  res.s12=a.s12^b.s12;
+  res.s13=a.s13^b.s13;
+  res.s14=a.s14^b.s14;
+  res.s15=a.s15^b.s15;
+  res.s16=a.s16^b.s16;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  res.s3=~a.s3;
+  res.s4=~a.s4;
+  res.s5=~a.s5;
+  res.s6=~a.s6;
+  res.s7=~a.s7;
+  res.s8=~a.s8;
+  res.s9=~a.s9;
+  res.s10=~a.s10;
+  res.s11=~a.s11;
+  res.s12=~a.s12;
+  res.s13=~a.s13;
+  res.s14=~a.s14;
+  res.s15=~a.s15;
+  res.s16=~a.s16;
+  return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+
+struct batch_t{
+  unsigned char s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  res.s5=a.s5&b.s5;
+  res.s6=a.s6&b.s6;
+  res.s7=a.s7&b.s7;
+  res.s8=a.s8&b.s8;
+  res.s9=a.s9&b.s9;
+  res.s10=a.s10&b.s10;
+  res.s11=a.s11&b.s11;
+  res.s12=a.s12&b.s12;
+  res.s13=a.s13&b.s13;
+  res.s14=a.s14&b.s14;
+  res.s15=a.s15&b.s15;
+  res.s16=a.s16&b.s16;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  res.s5=a.s5|b.s5;
+  res.s6=a.s6|b.s6;
+  res.s7=a.s7|b.s7;
+  res.s8=a.s8|b.s8;
+  res.s9=a.s9|b.s9;
+  res.s10=a.s10|b.s10;
+  res.s11=a.s11|b.s11;
+  res.s12=a.s12|b.s12;
+  res.s13=a.s13|b.s13;
+  res.s14=a.s14|b.s14;
+  res.s15=a.s15|b.s15;
+  res.s16=a.s16|b.s16;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  res.s5=a.s5^b.s5;
+  res.s6=a.s6^b.s6;
+  res.s7=a.s7^b.s7;
+  res.s8=a.s8^b.s8;
+  res.s9=a.s9^b.s9;
+  res.s10=a.s10^b.s10;
+  res.s11=a.s11^b.s11;
+  res.s12=a.s12^b.s12;
+  res.s13=a.s13^b.s13;
+  res.s14=a.s14^b.s14;
+  res.s15=a.s15^b.s15;
+  res.s16=a.s16^b.s16;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x29;
+  res.s2=0x29;
+  res.s3=0x29;
+  res.s4=0x29;
+  res.s5=0x29;
+  res.s6=0x29;
+  res.s7=0x29;
+  res.s8=0x29;
+  res.s9=0x29;
+  res.s10=0x29;
+  res.s11=0x29;
+  res.s12=0x29;
+  res.s13=0x29;
+  res.s14=0x29;
+  res.s15=0x29;
+  res.s16=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x02;
+  res.s2=0x02;
+  res.s3=0x02;
+  res.s4=0x02;
+  res.s5=0x02;
+  res.s6=0x02;
+  res.s7=0x02;
+  res.s8=0x02;
+  res.s9=0x02;
+  res.s10=0x02;
+  res.s11=0x02;
+  res.s12=0x02;
+  res.s13=0x02;
+  res.s14=0x02;
+  res.s15=0x02;
+  res.s16=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x04;
+  res.s2=0x04;
+  res.s3=0x04;
+  res.s4=0x04;
+  res.s5=0x04;
+  res.s6=0x04;
+  res.s7=0x04;
+  res.s8=0x04;
+  res.s9=0x04;
+  res.s10=0x04;
+  res.s11=0x04;
+  res.s12=0x04;
+  res.s13=0x04;
+  res.s14=0x04;
+  res.s15=0x04;
+  res.s16=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x10;
+  res.s2=0x10;
+  res.s3=0x10;
+  res.s4=0x10;
+  res.s5=0x10;
+  res.s6=0x10;
+  res.s7=0x10;
+  res.s8=0x10;
+  res.s9=0x10;
+  res.s10=0x10;
+  res.s11=0x10;
+  res.s12=0x10;
+  res.s13=0x10;
+  res.s14=0x10;
+  res.s15=0x10;
+  res.s16=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x40;
+  res.s2=0x40;
+  res.s3=0x40;
+  res.s4=0x40;
+  res.s5=0x40;
+  res.s6=0x40;
+  res.s7=0x40;
+  res.s8=0x40;
+  res.s9=0x40;
+  res.s10=0x40;
+  res.s11=0x40;
+  res.s12=0x40;
+  res.s13=0x40;
+  res.s14=0x40;
+  res.s15=0x40;
+  res.s16=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x80;
+  res.s2=0x80;
+  res.s3=0x80;
+  res.s4=0x80;
+  res.s5=0x80;
+  res.s6=0x80;
+  res.s7=0x80;
+  res.s8=0x80;
+  res.s9=0x80;
+  res.s10=0x80;
+  res.s11=0x80;
+  res.s12=0x80;
+  res.s13=0x80;
+  res.s14=0x80;
+  res.s15=0x80;
+  res.s16=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  res.s3=a.s3<<n;
+  res.s4=a.s4<<n;
+  res.s5=a.s5<<n;
+  res.s6=a.s6<<n;
+  res.s7=a.s7<<n;
+  res.s8=a.s8<<n;
+  res.s9=a.s9<<n;
+  res.s10=a.s10<<n;
+  res.s11=a.s11<<n;
+  res.s12=a.s12<<n;
+  res.s13=a.s13<<n;
+  res.s14=a.s14<<n;
+  res.s15=a.s15<<n;
+  res.s16=a.s16<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  res.s3=a.s3>>n;
+  res.s4=a.s4>>n;
+  res.s5=a.s5>>n;
+  res.s6=a.s6>>n;
+  res.s7=a.s7>>n;
+  res.s8=a.s8>>n;
+  res.s9=a.s9>>n;
+  res.s10=a.s10>>n;
+  res.s11=a.s11>>n;
+  res.s12=a.s12>>n;
+  res.s13=a.s13>>n;
+  res.s14=a.s14>>n;
+  res.s15=a.s15>>n;
+  res.s16=a.s16>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_16charA.h b/contrib/sasc-ng/FFdecsa/parallel_128_16charA.h
new file mode 100644 (file)
index 0000000..2a0daa1
--- /dev/null
@@ -0,0 +1,172 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned char s1[16];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0xff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=~a.s1[i];
+  return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+
+struct batch_t{
+  unsigned char s1[16];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]&b.s1[i];
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]|b.s1[i];
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]^b.s1[i];
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x29;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x02;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x04;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x10;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x40;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=0x80;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  int i;
+  for(i=0;i<16;i++) res.s1[i]=a.s1[i]>>n;
+  return res;
+}
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_2long.h b/contrib/sasc-ng/FFdecsa/parallel_128_2long.h
new file mode 100644 (file)
index 0000000..1a3bdd9
--- /dev/null
@@ -0,0 +1,175 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned long long int s1;
+  unsigned long long int s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0ULL;
+  res.s2=0x0ULL;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xffffffffffffffffULL;
+  res.s2=0xffffffffffffffffULL;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+
+struct batch_t{
+  unsigned long long int s1;
+  unsigned long long int s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x2929292929292929ULL;
+  res.s2=0x2929292929292929ULL;
+  return res;
+}
+
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x0202020202020202ULL;
+  res.s2=0x0202020202020202ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x0404040404040404ULL;
+  res.s2=0x0404040404040404ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x1010101010101010ULL;
+  res.s2=0x1010101010101010ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x4040404040404040ULL;
+  res.s2=0x4040404040404040ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x8080808080808080ULL;
+  res.s2=0x8080808080808080ULL;
+  return res;
+}
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_2mmx.h b/contrib/sasc-ng/FFdecsa/parallel_128_2mmx.h
new file mode 100644 (file)
index 0000000..4afb7a7
--- /dev/null
@@ -0,0 +1,201 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 <mmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+struct group_t{
+  __m64 s1,s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+  group res;
+  res.s1=(__m64)0x0ULL;
+  res.s2=(__m64)0x0ULL;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=(__m64)0xffffffffffffffffULL;
+  res.s2=(__m64)0xffffffffffffffffULL;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=_m_pand(a.s1,b.s1);
+  res.s2=_m_pand(a.s2,b.s2);
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=_m_por(a.s1,b.s1);
+  res.s2=_m_por(a.s2,b.s2);
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=_m_pxor(a.s1,b.s1);
+  res.s2=_m_pxor(a.s2,b.s2);
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=_m_pxor(a.s1,FF1().s1);
+  res.s2=_m_pxor(a.s2,FF1().s2);
+  return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+
+struct batch_t{
+  __m64 s1,s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=_m_pand(a.s1,b.s1);
+  res.s2=_m_pand(a.s2,b.s2);
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=_m_por(a.s1,b.s1);
+  res.s2=_m_por(a.s2,b.s2);
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=_m_pxor(a.s1,b.s1);
+  res.s2=_m_pxor(a.s2,b.s2);
+  return res;
+}
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=(__m64)0x2929292929292929ULL;
+  res.s2=(__m64)0x2929292929292929ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=(__m64)0x0202020202020202ULL;
+  res.s2=(__m64)0x0202020202020202ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=(__m64)0x0404040404040404ULL;
+  res.s2=(__m64)0x0404040404040404ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=(__m64)0x1010101010101010ULL;
+  res.s2=(__m64)0x1010101010101010ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=(__m64)0x4040404040404040ULL;
+  res.s2=(__m64)0x4040404040404040ULL;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=(__m64)0x8080808080808080ULL;
+  res.s2=(__m64)0x8080808080808080ULL;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=_m_psllqi(a.s1,n);
+  res.s2=_m_psllqi(a.s2,n);
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=_m_psrlqi(a.s1,n);
+  res.s2=_m_psrlqi(a.s2,n);
+  return res;
+}
+
+void static inline M_EMPTY(void){
+  _m_empty();
+}
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2)    do{ __m64 *pd=(__m64 *)(d), *ps1=(__m64 *)(s1), *ps2=(__m64 *)(s2); \
+                                 *pd = _m_pxor( *ps1 , *ps2 ); }while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s)      do{ __m64 *pd=(__m64 *)(d), *ps=(__m64 *)(s); \
+                                 *pd = _m_pxor( *ps, *pd ); }while(0)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s)       do{ __m64 *pd=(__m64 *)(d), *ps=(__m64 *)(s); \
+                                 *pd =  *ps; }while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN            8
+
+#undef XOR_BEST_BY
+#define XOR_BEST_BY(d,s1,s2) do{ XOR_8_BY(d,s1,s2); }while(0);
+
+#undef XOREQ_BEST_BY
+#define XOREQ_BEST_BY(d,s)   do{ XOREQ_8_BY(d,s); }while(0);
+
+#undef COPY_BEST_BY
+#define COPY_BEST_BY(d,s)    do{ COPY_8_BY(d,s); }while(0);
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_4int.h b/contrib/sasc-ng/FFdecsa/parallel_128_4int.h
new file mode 100644 (file)
index 0000000..79b95f1
--- /dev/null
@@ -0,0 +1,207 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+struct group_t{
+  unsigned int s1,s2,s3,s4;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+  group res;
+  res.s1=0x0;
+  res.s2=0x0;
+  res.s3=0x0;
+  res.s4=0x0;
+  return res;
+}
+
+group static inline FF1(){
+  group res;
+  res.s1=0xffffffff;
+  res.s2=0xffffffff;
+  res.s3=0xffffffff;
+  res.s4=0xffffffff;
+  return res;
+}
+
+group static inline FFAND(group a,group b){
+  group res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  return res;
+}
+
+group static inline FFOR(group a,group b){
+  group res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  return res;
+}
+
+group static inline FFXOR(group a,group b){
+  group res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  return res;
+}
+
+group static inline FFNOT(group a){
+  group res;
+  res.s1=~a.s1;
+  res.s2=~a.s2;
+  res.s3=~a.s3;
+  res.s4=~a.s4;
+  return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+  *(((int *)tab)+2*g)=*((int *)data);
+  *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+  *((int *)data)=*(((int *)tab)+2*g);
+  *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+  int j;
+  for(j=0;j<n;j++){
+    *(data+j)^=*(tab+8*g+j);
+  }
+}
+
+
+struct batch_t{
+  unsigned int s1,s2,s3,s4;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+  batch res;
+  res.s1=a.s1&b.s1;
+  res.s2=a.s2&b.s2;
+  res.s3=a.s3&b.s3;
+  res.s4=a.s4&b.s4;
+  return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1|b.s1;
+  res.s2=a.s2|b.s2;
+  res.s3=a.s3|b.s3;
+  res.s4=a.s4|b.s4;
+  return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+  batch res;
+  res.s1=a.s1^b.s1;
+  res.s2=a.s2^b.s2;
+  res.s3=a.s3^b.s3;
+  res.s4=a.s4^b.s4;
+  return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+  batch res;
+  res.s1=0x29292929;
+  res.s2=0x29292929;
+  res.s3=0x29292929;
+  res.s4=0x29292929;
+  return res;
+}
+batch static inline B_FFN_ALL_02(){
+  batch res;
+  res.s1=0x02020202;
+  res.s2=0x02020202;
+  res.s3=0x02020202;
+  res.s4=0x02020202;
+  return res;
+}
+batch static inline B_FFN_ALL_04(){
+  batch res;
+  res.s1=0x04040404;
+  res.s2=0x04040404;
+  res.s3=0x04040404;
+  res.s4=0x04040404;
+  return res;
+}
+batch static inline B_FFN_ALL_10(){
+  batch res;
+  res.s1=0x10101010;
+  res.s2=0x10101010;
+  res.s3=0x10101010;
+  res.s4=0x10101010;
+  return res;
+}
+batch static inline B_FFN_ALL_40(){
+  batch res;
+  res.s1=0x40404040;
+  res.s2=0x40404040;
+  res.s3=0x40404040;
+  res.s4=0x40404040;
+  return res;
+}
+batch static inline B_FFN_ALL_80(){
+  batch res;
+  res.s1=0x80808080;
+  res.s2=0x80808080;
+  res.s3=0x80808080;
+  res.s4=0x80808080;
+  return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+  batch res;
+  res.s1=a.s1<<n;
+  res.s2=a.s2<<n;
+  res.s3=a.s3<<n;
+  res.s4=a.s4<<n;
+  return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+  batch res;
+  res.s1=a.s1>>n;
+  res.s2=a.s2>>n;
+  res.s3=a.s3>>n;
+  res.s4=a.s4>>n;
+  return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_sse.h b/contrib/sasc-ng/FFdecsa/parallel_128_sse.h
new file mode 100644 (file)
index 0000000..a26e6b3
--- /dev/null
@@ -0,0 +1,95 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ *               2003-2004  fatih89r
+ *
+ * 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 <xmmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u128 {
+    unsigned int u[4];
+    __m128 v;
+};
+
+static const union __u128 ff0 = {{0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}};
+static const union __u128 ff1 = {{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}};
+
+typedef __m128 group;
+#define GROUP_PARALLELISM 128
+#define FF0() ff0.v
+#define FF1() ff1.v
+#define FFAND(a,b) _mm_and_ps((a),(b))
+#define FFOR(a,b)  _mm_or_ps((a),(b))
+#define FFXOR(a,b) _mm_xor_ps((a),(b))
+#define FFNOT(a)   _mm_xor_ps((a),FF1())
+#define MALLOC(X)  _mm_malloc(X,16)
+#define FREE(X)    _mm_free(X)
+
+union __u64 {
+    unsigned int u[2];
+    __m64 v;
+};
+
+static const union __u64 ff29 = {{0x29292929U, 0x29292929U}};
+static const union __u64 ff02 = {{0x02020202U, 0x02020202U}};
+static const union __u64 ff04 = {{0x04040404U, 0x04040404U}};
+static const union __u64 ff10 = {{0x10101010U, 0x10101010U}};
+static const union __u64 ff40 = {{0x40404040U, 0x40404040U}};
+static const union __u64 ff80 = {{0x80808080U, 0x80808080U}};
+
+typedef __m64 batch;
+#define BYTES_PER_BATCH 8
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+#define B_FFAND(a,b)  _mm_and_si64((a),(b))
+#define B_FFOR(a,b)   _mm_or_si64((a),(b))
+#define B_FFXOR(a,b)  _mm_xor_si64((a),(b))
+#define B_FFSH8L(a,n) _mm_slli_si64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_si64((a),(n))
+
+#define M_EMPTY()     _mm_empty()
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2)    do { *(__m64*)d = _mm_xor_si64(*(__m64*)(s1), *(__m64*)(s2)); } while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s)      XOR_8_BY(d, d, s)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s)       do { *(__m64 *)(d) = *(__m64 *)(s); } while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN            16
+
+#undef XOR_BEST_BY
+static inline void XOR_BEST_BY(unsigned char *d, unsigned char *s1, unsigned char *s2)
+{
+    __m128 vs1 = _mm_load_ps((float*)s1);
+    __m128 vs2 = _mm_load_ps((float*)s2);
+    vs1 = _mm_xor_ps(vs1, vs2);
+    _mm_store_ps((float*)d, vs1);
+}
+
+#include "fftable.h"
diff --git a/contrib/sasc-ng/FFdecsa/parallel_128_sse2.h b/contrib/sasc-ng/FFdecsa/parallel_128_sse2.h
new file mode 100644 (file)
index 0000000..5a537a9
--- /dev/null
@@ -0,0 +1,82 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ *               2003-2004  fatih89r
+ *
+ * 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 <emmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u128i {
+       unsigned int u[4];
+       __m128i v;
+};
+
+static const union __u128i ff0 = {{0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}};
+static const union __u128i ff1 = {{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}};
+
+typedef __m128i group;
+#define GROUP_PARALLELISM 128
+#define FF0() ff0.v
+#define FF1() ff1.v
+#define FFAND(a,b) _mm_and_si128((a),(b))
+#define FFOR(a,b)  _mm_or_si128((a),(b))
+#define FFXOR(a,b) _mm_xor_si128((a),(b))
+#define FFNOT(a)   _mm_xor_si128((a),FF1())
+#define MALLOC(X)  _mm_malloc(X,16)
+#define FREE(X)    _mm_free(X)
+
+/* BATCH */
+
+static const union __u128i ff29 = {{0x29292929U, 0x29292929U, 0x29292929U, 0x29292929U}};
+static const union __u128i ff02 = {{0x02020202U, 0x02020202U, 0x02020202U, 0x02020202U}};
+static const union __u128i ff04 = {{0x04040404U, 0x04040404U, 0x04040404U, 0x04040404U}};
+static const union __u128i ff10 = {{0x10101010U, 0x10101010U, 0x10101010U, 0x10101010U}};
+static const union __u128i ff40 = {{0x40404040U, 0x40404040U, 0x40404040U, 0x40404040U}};
+static const union __u128i ff80 = {{0x80808080U, 0x80808080U, 0x80808080U, 0x80808080U}};
+
+typedef __m128i batch;
+#define BYTES_PER_BATCH 16
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+
+#define B_FFAND(a,b) FFAND(a,b)
+#define B_FFOR(a,b)  FFOR(a,b)
+#define B_FFXOR(a,b) FFXOR(a,b)
+#define B_FFSH8L(a,n) _mm_slli_epi64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_epi64((a),(n))
+
+#define M_EMPTY()
+
+#undef BEST_SPAN
+#define BEST_SPAN            16
+
+#undef XOR_BEST_BY
+static inline void XOR_BEST_BY(unsigned char *d, unsigned char *s1, unsigned char *s2)
+{
+       __m128i vs1 = _mm_load_si128((__m128i*)s1);
+       __m128i vs2 = _mm_load_si128((__m128i*)s2);
+       vs1 = _mm_xor_si128(vs1, vs2);
+       _mm_store_si128((__m128i*)d, vs1);
+}
+
+#include "fftable.h"
diff --git a/contrib/sasc-ng/FFdecsa/parallel_generic.h b/contrib/sasc-ng/FFdecsa/parallel_generic.h
new file mode 100644 (file)
index 0000000..2af4c1c
--- /dev/null
@@ -0,0 +1,102 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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.
+ */
+
+
+
+#if 0
+//// generics
+#define COPY4BY(d,s)     do{ int *pd=(int *)(d), *ps=(int *)(s); \
+                             *pd = *ps; }while(0)
+#define COPY8BY(d,s)     do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd = *ps; }while(0)
+#define COPY16BY(d,s)    do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd = *ps; \
+                            *(pd+1) = *(ps+1); }while(0)
+#define COPY32BY(d,s)    do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd = *ps; \
+                            *(pd+1) = *(ps+1) \
+                            *(pd+2) = *(ps+2) \
+                            *(pd+3) = *(ps+3); }while(0)
+#define XOR4BY(d,s1,s2)  do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \
+                             *pd = *ps1  ^ *ps2; }while(0)
+#define XOR8BY(d,s1,s2)  do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+                             *pd = *ps1  ^ *ps2; }while(0)
+#define XOR16BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+                             *pd = *ps1  ^ *ps2; \
+                             *(pd+8) = *(ps1+8)  ^ *(ps2+8); }while(0)
+#define XOR32BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+                             *pd = *ps1  ^ *ps2; \
+                             *(pd+1) = *(ps1+1)  ^ *(ps2+1); \
+                             *(pd+2) = *(ps1+2)  ^ *(ps2+2); \
+                             *(pd+3) = *(ps1+3)  ^ *(ps2+3); }while(0)
+#define XOR32BV(d,s1,s2) do{ int *const pd=(int *const)(d), *ps1=(const int *const)(s1), *ps2=(const int *const)(s2); \
+                             int z; \
+                            for(z=0;z<8;z++){ \
+                               pd[z]=ps1[z]^ps2[z]; \
+                            } \
+                           }while(0)
+#define XOREQ4BY(d,s)    do{ int *pd=(int *)(d), *ps=(int *)(s); \
+                             *pd ^= *ps; }while(0)
+#define XOREQ8BY(d,s)    do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd ^= *ps; }while(0)
+#define XOREQ16BY(d,s)   do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd ^= *ps; \
+                            *(pd+1) ^=*(ps+1); }while(0)
+#define XOREQ32BY(d,s)   do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                             *pd ^= *ps; \
+                            *(pd+1) ^=*(ps+1); \
+                            *(pd+2) ^=*(ps+2); \
+                            *(pd+3) ^=*(ps+3); }while(0)
+#define XOREQ32BY4(d,s)  do{ int *pd=(int *)(d), *ps=(int *)(s); \
+                             *pd ^= *ps; \
+                            *(pd+1) ^=*(ps+1); \
+                            *(pd+2) ^=*(ps+2); \
+                            *(pd+3) ^=*(ps+3); \
+                            *(pd+4) ^=*(ps+4); \
+                            *(pd+5) ^=*(ps+5); \
+                            *(pd+6) ^=*(ps+6); \
+                            *(pd+7) ^=*(ps+7); }while(0)
+#define XOREQ32BV(d,s)   do{ unsigned char *pd=(unsigned char *)(d), *ps=(unsigned char *)(s); \
+                             int z; \
+                            for(z=0;z<32;z++){ \
+                               pd[z]^=ps[z]; \
+                            } \
+                           }while(0)
+
+#else
+#define XOR_4_BY(d,s1,s2)    do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \
+                               *pd = *ps1  ^ *ps2; }while(0)
+#define XOR_8_BY(d,s1,s2)    do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+                               *pd = *ps1  ^ *ps2; }while(0)
+#define XOREQ_4_BY(d,s)      do{ int *pd=(int *)(d), *ps=(int *)(s); \
+                               *pd ^= *ps; }while(0)
+#define XOREQ_8_BY(d,s)      do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                               *pd ^= *ps; }while(0)
+#define COPY_4_BY(d,s)       do{ int *pd=(int *)(d), *ps=(int *)(s); \
+                               *pd = *ps; }while(0)
+#define COPY_8_BY(d,s)       do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+                               *pd = *ps; }while(0)
+
+#define BEST_SPAN            8
+#define XOR_BEST_BY(d,s1,s2) do{ XOR_8_BY(d,s1,s2); }while(0);
+#define XOREQ_BEST_BY(d,s)   do{ XOREQ_8_BY(d,s); }while(0);
+#define COPY_BEST_BY(d,s)    do{ COPY_8_BY(d,s); }while(0);
+
+#define END_MM             do{ }while(0);
+#endif
diff --git a/contrib/sasc-ng/FFdecsa/parallel_std_def.h b/contrib/sasc-ng/FFdecsa/parallel_std_def.h
new file mode 100644 (file)
index 0000000..10517d4
--- /dev/null
@@ -0,0 +1,29 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 FFXOR(a,b) ((a)^(b))
+#define FFAND(a,b) ((a)&(b))
+#define FFOR(a,b)  ((a)|(b))
+#define FFNOT(a)   (~(a))
+
+#define B_FFAND(a,b) ((a)&(b))
+#define B_FFOR(a,b)  ((a)|(b))
+#define B_FFXOR(a,b) ((a)^(b))
+#define B_FFSH8L(a,n) ((a)<<(n))
+#define B_FFSH8R(a,n) ((a)>>(n))
diff --git a/contrib/sasc-ng/FFdecsa/stream.c b/contrib/sasc-ng/FFdecsa/stream.c
new file mode 100644 (file)
index 0000000..1bda852
--- /dev/null
@@ -0,0 +1,906 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 statics only once, when STREAM_INIT
+#ifdef STREAM_INIT
+struct stream_regs {
+  group A[32+10][4]; // 32 because we will move back (virtual shift register)
+  group B[32+10][4]; // 32 because we will move back (virtual shift register)
+  group X[4];
+  group Y[4];
+  group Z[4];
+  group D[4];
+  group E[4];
+  group F[4];
+  group p;
+  group q;
+  group r;
+  };
+
+static inline void trasp64_32_88ccw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned int *)data)
+  int i,j;
+  for(j=0;j<64;j+=32){
+    unsigned int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff)      | ((b           )<<16);
+      row[j+16+i]=((t           )>>16) |  (b&0xffff0000) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+      row[j+8+i] =((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0x0f0f0f0f)<<4) |  (b&0x0f0f0f0f);
+      row[j+4+i] = (t&0xf0f0f0f0)     | ((b&0xf0f0f0f0)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0x33333333)<<2) |  (b&0x33333333);
+      row[j+2+i] = (t&0xcccccccc)     | ((b&0xcccccccc)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0x55555555)<<1) |  (b&0x55555555);
+      row[j+1+i] = (t&0xaaaaaaaa)     | ((b&0xaaaaaaaa)>>1);
+    }
+  }
+#undef row
+}
+
+static inline void trasp64_32_88cw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned int *)data)
+  int i,j;
+  for(j=0;j<64;j+=32){
+    unsigned int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff)      | ((b           )<<16);
+      row[j+16+i]=((t           )>>16) |  (b&0xffff0000) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+      row[j+8+i] =((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]  =((t&0xf0f0f0f0)>>4) |   (b&0xf0f0f0f0);
+      row[j+4+i]= (t&0x0f0f0f0f)     |  ((b&0x0f0f0f0f)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]  =((t&0xcccccccc)>>2) |  (b&0xcccccccc);
+      row[j+2+i]= (t&0x33333333)     | ((b&0x33333333)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]  =((t&0xaaaaaaaa)>>1) |  (b&0xaaaaaaaa);
+      row[j+1+i]= (t&0x55555555)     | ((b&0x55555555)<<1);
+    }
+  }
+#undef row
+}
+
+//64-64----------------------------------------------------------
+static inline void trasp64_64_88ccw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=row[j+i];
+      b=row[j+32+i];
+      row[j+i]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      row[j+32+i]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      row[j+4+i] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      row[j+2+i] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      row[j+1+i] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+    }
+  }
+#undef row
+}
+
+static inline void trasp64_64_88cw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=row[j+i];
+      b=row[j+32+i];
+      row[j+i]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      row[j+32+i]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      row[j+4+i] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      row[j+2+i] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      row[j+1+i] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+    }
+  }
+#undef row
+}
+
+//64-128----------------------------------------------------------
+static inline void trasp64_128_88ccw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define halfrow ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+32+i)];
+      halfrow[2*(j+i)]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+32+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)+1]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+16+i)];
+      halfrow[2*(j+i)]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+16+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+8+i)];
+      halfrow[2*(j+i)]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+8+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+4+i)];
+      halfrow[2*(j+i)]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      halfrow[2*(j+4+i)] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+4+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      halfrow[2*(j+4+i)+1] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+2+i)];
+      halfrow[2*(j+i)]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      halfrow[2*(j+2+i)] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+2+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      halfrow[2*(j+2+i)+1] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+1+i)];
+      halfrow[2*(j+i)]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      halfrow[2*(j+1+i)] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+1+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      halfrow[2*(j+1+i)+1] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+    }
+  }
+#undef halfrow
+}
+
+static inline void trasp64_128_88cw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define halfrow ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+32+i)];
+      halfrow[2*(j+i)]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+32+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)+1]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+16+i)];
+      halfrow[2*(j+i)]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+16+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+8+i)];
+      halfrow[2*(j+i)]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+8+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+4+i)];
+      halfrow[2*(j+i)]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      halfrow[2*(j+4+i)] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+4+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      halfrow[2*(j+4+i)+1] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+2+i)];
+      halfrow[2*(j+i)]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      halfrow[2*(j+2+i)] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+2+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      halfrow[2*(j+2+i)+1] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+1+i)];
+      halfrow[2*(j+i)]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      halfrow[2*(j+1+i)] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+1+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      halfrow[2*(j+1+i)+1] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+    }
+  }
+#undef halfrow
+}
+#endif
+
+
+#ifdef STREAM_INIT
+void stream_cypher_group_init(
+  struct stream_regs *regs,
+  group         iA[8][4], // [In]  iA00,iA01,...iA73 32 groups  | Derived from key.
+  group         iB[8][4], // [In]  iB00,iB01,...iB73 32 groups  | Derived from key.
+  unsigned char *sb)      // [In]  (SB0,SB1,...SB7)...x32 32*8 bytes | Extra input.
+#endif
+#ifdef STREAM_NORMAL
+void stream_cypher_group_normal(
+  struct stream_regs *regs,
+  unsigned char *cb)    // [Out] (CB0,CB1,...CB7)...x32 32*8 bytes | Output.
+#endif
+{
+#ifdef STREAM_INIT
+  group in1[4];
+  group in2[4];
+#endif
+  group extra_B[4];
+  group fa,fb,fc,fd,fe;
+  group s1a,s1b,s2a,s2b,s3a,s3b,s4a,s4b,s5a,s5b,s6a,s6b,s7a,s7b;
+  group next_E[4];
+  group tmp0,tmp1,tmp2,tmp3,tmp4;
+#ifdef STREAM_INIT
+  group *sb_g=(group *)sb;
+#endif
+#ifdef STREAM_NORMAL
+  group *cb_g=(group *)cb;
+#endif
+  int aboff;
+  int i,j,k,b;
+  int dbg;
+
+#ifdef STREAM_INIT
+  DBG(fprintf(stderr,":::::::::: BEGIN STREAM INIT\n"));
+#endif
+#ifdef STREAM_NORMAL
+  DBG(fprintf(stderr,":::::::::: BEGIN STREAM NORMAL\n"));
+#endif
+#ifdef STREAM_INIT
+for(j=0;j<64;j++){
+  DBG(fprintf(stderr,"precall prerot stream_in[%2i]=",j));
+  DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG));
+}
+
+DBG(dump_mem("stream_prerot ",sb,GROUP_PARALLELISM*8,BYPG));
+#if GROUP_PARALLELISM==32
+trasp64_32_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88ccw(sb);
+#endif
+DBG(dump_mem("stream_postrot",sb,GROUP_PARALLELISM*8,BYPG));
+
+for(j=0;j<64;j++){
+  DBG(fprintf(stderr,"precall stream_in[%2i]=",j));
+  DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG));
+}
+#endif
+
+  aboff=32;
+
+#ifdef STREAM_INIT
+  // load first 32 bits of ck into A[aboff+0]..A[aboff+7]
+  // load last  32 bits of ck into B[aboff+0]..B[aboff+7]
+  // all other regs = 0
+  for(i=0;i<8;i++){
+    for(b=0;b<4;b++){
+DBG(fprintf(stderr,"dbg from iA A[%i][%i]=",i,b));
+DBG(dump_mem("",(unsigned char *)&iA[i][b],BYPG,BYPG));
+DBG(fprintf(stderr,"                                       dbg from iB B[%i][%i]=",i,b));
+DBG(dump_mem("",(unsigned char *)&iB[i][b],BYPG,BYPG));
+      regs->A[aboff+i][b]=iA[i][b];
+      regs->B[aboff+i][b]=iB[i][b];
+    }
+  }
+  for(b=0;b<4;b++){
+    regs->A[aboff+8][b]=FF0();
+    regs->A[aboff+9][b]=FF0();
+    regs->B[aboff+8][b]=FF0();
+    regs->B[aboff+9][b]=FF0();
+  }
+  for(b=0;b<4;b++){
+    regs->X[b]=FF0();
+    regs->Y[b]=FF0();
+    regs->Z[b]=FF0();
+    regs->D[b]=FF0();
+    regs->E[b]=FF0();
+    regs->F[b]=FF0();
+  }
+  regs->p=FF0();
+  regs->q=FF0();
+  regs->r=FF0();
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"dbg A0[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->A[aboff+0][dbg],BYPG,BYPG));
+  DBG(fprintf(stderr,"dbg B0[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->B[aboff+0][dbg],BYPG,BYPG));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+  // EXTERNAL LOOP - 8 bytes per operation
+  for(i=0;i<8;i++){
+
+    DBG(fprintf(stderr,"--BEGIN EXTERNAL LOOP %i\n",i));
+
+#ifdef STREAM_INIT
+    for(b=0;b<4;b++){
+      in1[b]=sb_g[8*i+4+b];
+      in2[b]=sb_g[8*i+b];
+    }
+#endif
+
+    // INTERNAL LOOP - 2 bits per iteration
+    for(j=0; j<4; j++){
+
+      DBG(fprintf(stderr,"---BEGIN INTERNAL LOOP %i (EXT %i, INT %i)\n",j,i,j));
+
+      // from A0..A9, 35 bits are selected as inputs to 7 s-boxes
+      // 5 bits input per s-box, 2 bits output per s-box
+
+      // we can select bits with zero masking and shifting operations
+      // and synthetize s-boxes with optimized boolean functions.
+      // this is the actual reason we do all the crazy transposition
+      // stuff to switch between normal and bit slice representations.
+      // this code really flies.
+
+      fe=regs->A[aboff+3][0];fa=regs->A[aboff+0][2];fb=regs->A[aboff+5][1];fc=regs->A[aboff+6][3];fd=regs->A[aboff+8][0];
+/* 1000 1110  1110 0001   : lev  7: */ //tmp0=( fa^( fb^( ( ( ( fa|fb )^fc )|( fc^fd ) )^ALL_ONES ) ) );
+/* 1110 0010  0011 0011   : lev  6: */ //tmp1=( ( fa|fb )^( ( fc&( fa|( fb^fd ) ) )^ALL_ONES ) );
+/* 0011 0110  1000 1101   : lev  5: */ //tmp2=( fa^( ( fb&fd )^( ( fa&fd )|fc ) ) );
+/* 0101 0101  1001 0011   : lev  5: */ //tmp3=( ( fa&fc )^( fa^( ( fa&fb )|fd ) ) );
+/* 1000 1110  1110 0001   : lev  7: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFOR(FFXOR(FFOR(fa,fb),fc),FFXOR(fc,fd)),FF1())));
+/* 1110 0010  0011 0011   : lev  6: */ tmp1=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fa,FFXOR(fb,fd))),FF1()));
+/* 0011 0110  1000 1101   : lev  5: */ tmp2=FFXOR(fa,FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),fc)));
+/* 0101 0101  1001 0011   : lev  5: */ tmp3=FFXOR(FFAND(fa,fc),FFXOR(fa,FFOR(FFAND(fa,fb),fd)));
+      s1a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s1b=FFXOR(tmp2,FFAND(fe,tmp3));
+//dump_mem("s1as1b-fe",&fe,BYPG,BYPG);
+//dump_mem("s1as1b-fa",&fa,BYPG,BYPG);
+//dump_mem("s1as1b-fb",&fb,BYPG,BYPG);
+//dump_mem("s1as1b-fc",&fc,BYPG,BYPG);
+//dump_mem("s1as1b-fd",&fd,BYPG,BYPG);
+
+      fe=regs->A[aboff+1][1];fa=regs->A[aboff+2][2];fb=regs->A[aboff+5][3];fc=regs->A[aboff+6][0];fd=regs->A[aboff+8][1];
+/* 1001 1110  0110 0001   : lev  6: */ //tmp0=( fa^( ( fb&( fc|fd ) )^( fc^( fd^ALL_ONES ) ) ) );
+/* 0000 0011  0111 1011   : lev  5: */ //tmp1=( ( fa&( fb^fd ) )|( ( fa|fb )&fc ) );
+/* 1100 0110  1101 0010   : lev  6: */ //tmp2=( ( fb&fd )^( ( fa&fd )|( fb^( fc^ALL_ONES ) ) ) );
+/* 0001 1110  1111 0101   : lev  5: */ //tmp3=( ( fa&fd )|( fa^( fb^( fc&fd ) ) ) );
+/* 1001 1110  0110 0001   : lev  6: */ tmp0=FFXOR(fa,FFXOR(FFAND(fb,FFOR(fc,fd)),FFXOR(fc,FFXOR(fd,FF1()))));
+/* 0000 0011  0111 1011   : lev  5: */ tmp1=FFOR(FFAND(fa,FFXOR(fb,fd)),FFAND(FFOR(fa,fb),fc));
+/* 1100 0110  1101 0010   : lev  6: */ tmp2=FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),FFXOR(fb,FFXOR(fc,FF1()))));
+/* 0001 1110  1111 0101   : lev  5: */ tmp3=FFOR(FFAND(fa,fd),FFXOR(fa,FFXOR(fb,FFAND(fc,fd))));
+      s2a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s2b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=regs->A[aboff+0][3];fa=regs->A[aboff+1][0];fb=regs->A[aboff+4][1];fc=regs->A[aboff+4][3];fd=regs->A[aboff+5][2];
+/* 0100 1011  1001 0110   : lev  5: */ //tmp0=( fa^( fb^( ( fc&( fa|fd ) )^fd ) ) );
+/* 1101 0101  1000 1100   : lev  7: */ //tmp1=( ( fa&fc )^( ( fa^fd )|( ( fb|fc )^( fd^ALL_ONES ) ) ) );
+/* 0010 0111  1101 1000   : lev  4: */ //tmp2=( fa^( ( ( fb^fc )&fd )^fc ) );
+/* 1111 1111  1111 1111   : lev  0: */ //tmp3=ALL_ONES;
+/* 0100 1011  1001 0110   : lev  5: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFAND(fc,FFOR(fa,fd)),fd)));
+/* 1101 0101  1000 1100   : lev  7: */ tmp1=FFXOR(FFAND(fa,fc),FFOR(FFXOR(fa,fd),FFXOR(FFOR(fb,fc),FFXOR(fd,FF1()))));
+/* 0010 0111  1101 1000   : lev  4: */ tmp2=FFXOR(fa,FFXOR(FFAND(FFXOR(fb,fc),fd),fc));
+/* 1111 1111  1111 1111   : lev  0: */ tmp3=FF1();
+      s3a=FFXOR(tmp0,FFAND(FFNOT(fe),tmp1));
+      s3b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=regs->A[aboff+2][3];fa=regs->A[aboff+0][1];fb=regs->A[aboff+1][3];fc=regs->A[aboff+3][2];fd=regs->A[aboff+7][0];
+/* 1011 0101  0100 1001   : lev  7: */ //tmp0=( fa^( ( fc&( fa^fd ) )|( fb^( fc|( fd^ALL_ONES ) ) ) ) );
+/* 0010 1101  0110 0110   : lev  6: */ //tmp1=( ( fa&fb )^( fb^( ( ( fa|fc )&fd )^fc ) ) );
+/* 0110 0111  1101 0000   : lev  7: */ //tmp2=( fa^( ( fb&fc )|( ( ( fa&( fb^fd ) )|fc )^fd ) ) );
+/* 1111 1111  1111 1111   : lev  0: */ //tmp3=ALL_ONES;
+/* 1011 0101  0100 1001   : lev  7: */ tmp0=FFXOR(fa,FFOR(FFAND(fc,FFXOR(fa,fd)),FFXOR(fb,FFOR(fc,FFXOR(fd,FF1())))));
+/* 0010 1101  0110 0110   : lev  6: */ tmp1=FFXOR(FFAND(fa,fb),FFXOR(fb,FFXOR(FFAND(FFOR(fa,fc),fd),fc)));
+/* 0110 0111  1101 0000   : lev  7: */ tmp2=FFXOR(fa,FFOR(FFAND(fb,fc),FFXOR(FFOR(FFAND(fa,FFXOR(fb,fd)),fc),fd)));
+/* 1111 1111  1111 1111   : lev  0: */ tmp3=FF1();
+      s4a=FFXOR(tmp0,FFAND(fe,FFXOR(tmp1,tmp0)));
+      s4b=FFXOR(FFXOR(s4a,tmp2),FFAND(fe,tmp3));
+
+      fe=regs->A[aboff+4][2];fa=regs->A[aboff+3][3];fb=regs->A[aboff+5][0];fc=regs->A[aboff+7][1];fd=regs->A[aboff+8][2];
+/* 1000 1111  0011 0010   : lev  7: */ //tmp0=( ( ( fa&( fb|fc ) )^fb )|( ( ( fa^fc )|fd )^ALL_ONES ) );
+/* 0110 1011  0000 1011   : lev  6: */ //tmp1=( fb^( ( fc^fd )&( fc^( fb|( fa^fd ) ) ) ) );
+/* 0001 1010  0111 1001   : lev  6: */ //tmp2=( ( fa&fc )^( fb^( ( fb|( fa^fc ) )&fd ) ) );
+/* 0101 1101  1101 0101   : lev  4: */ //tmp3=( ( ( fa^fb )&( fc^ALL_ONES ) )|fd );
+/* 1000 1111  0011 0010   : lev  7: */ tmp0=FFOR(FFXOR(FFAND(fa,FFOR(fb,fc)),fb),FFXOR(FFOR(FFXOR(fa,fc),fd),FF1()));
+/* 0110 1011  0000 1011   : lev  6: */ tmp1=FFXOR(fb,FFAND(FFXOR(fc,fd),FFXOR(fc,FFOR(fb,FFXOR(fa,fd)))));
+/* 0001 1010  0111 1001   : lev  6: */ tmp2=FFXOR(FFAND(fa,fc),FFXOR(fb,FFAND(FFOR(fb,FFXOR(fa,fc)),fd)));
+/* 0101 1101  1101 0101   : lev  4: */ tmp3=FFOR(FFAND(FFXOR(fa,fb),FFXOR(fc,FF1())),fd);
+      s5a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s5b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=regs->A[aboff+2][1];fa=regs->A[aboff+3][1];fb=regs->A[aboff+4][0];fc=regs->A[aboff+6][2];fd=regs->A[aboff+8][3];
+/* 0011 0110  0010 1101   : lev  6: */ //tmp0=( ( ( fa&fc )&fd )^( ( fb&( fa|fd ) )^fc ) );
+/* 1110 1110  1011 1011   : lev  3: */ //tmp1=( ( ( fa^fc )&fd )^ALL_ONES );
+/* 0101 1000  0110 0111   : lev  6: */ //tmp2=( ( fa&( fb|fc ) )^( fb^( ( fb&fc )|fd ) ) );
+/* 0001 0011  0000 0001   : lev  5: */ //tmp3=( fc&( ( fa&( fb^fd ) )^( fb|fd ) ) );
+/* 0011 0110  0010 1101   : lev  6: */ tmp0=FFXOR(FFAND(FFAND(fa,fc),fd),FFXOR(FFAND(fb,FFOR(fa,fd)),fc));
+/* 1110 1110  1011 1011   : lev  3: */ tmp1=FFXOR(FFAND(FFXOR(fa,fc),fd),FF1());
+/* 0101 1000  0110 0111   : lev  6: */ tmp2=FFXOR(FFAND(fa,FFOR(fb,fc)),FFXOR(fb,FFOR(FFAND(fb,fc),fd)));
+/* 0001 0011  0000 0001   : lev  5: */ tmp3=FFAND(fc,FFXOR(FFAND(fa,FFXOR(fb,fd)),FFOR(fb,fd)));
+      s6a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s6b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=regs->A[aboff+1][2];fa=regs->A[aboff+2][0];fb=regs->A[aboff+6][1];fc=regs->A[aboff+7][2];fd=regs->A[aboff+7][3];
+/* 0111 1000  1001 0110   : lev  5: */ //tmp0=( fb^( ( fc&fd )|( fa^( fc^fd ) ) ) );
+/* 0100 1001  0101 1011   : lev  6: */ //tmp1=( ( fb|fd )&( ( fa&fc )|( fb^( fc^fd ) ) ) );
+/* 0100 1001  1011 1001   : lev  5: */ //tmp2=( ( fa|fb )^( ( fc&( fb|fd ) )^fd ) );
+/* 1111 1111  1101 1101   : lev  3: */ //tmp3=( fd|( ( fa&fc )^ALL_ONES ) );
+/* 0111 1000  1001 0110   : lev  5: */ tmp0=FFXOR(fb,FFOR(FFAND(fc,fd),FFXOR(fa,FFXOR(fc,fd))));
+/* 0100 1001  0101 1011   : lev  6: */ tmp1=FFAND(FFOR(fb,fd),FFOR(FFAND(fa,fc),FFXOR(fb,FFXOR(fc,fd))));
+/* 0100 1001  1011 1001   : lev  5: */ tmp2=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fb,fd)),fd));
+/* 1111 1111  1101 1101   : lev  3: */ tmp3=FFOR(fd,FFXOR(FFAND(fa,fc),FF1()));
+      s7a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s7b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+
+/*
+      we have just done this:
+      
+      int sbox1[0x20] = {2,0,1,1,2,3,3,0, 3,2,2,0,1,1,0,3, 0,3,3,0,2,2,1,1, 2,2,0,3,1,1,3,0};
+      int sbox2[0x20] = {3,1,0,2,2,3,3,0, 1,3,2,1,0,0,1,2, 3,1,0,3,3,2,0,2, 0,0,1,2,2,1,3,1};
+      int sbox3[0x20] = {2,0,1,2,2,3,3,1, 1,1,0,3,3,0,2,0, 1,3,0,1,3,0,2,2, 2,0,1,2,0,3,3,1};
+      int sbox4[0x20] = {3,1,2,3,0,2,1,2, 1,2,0,1,3,0,0,3, 1,0,3,1,2,3,0,3, 0,3,2,0,1,2,2,1};
+      int sbox5[0x20] = {2,0,0,1,3,2,3,2, 0,1,3,3,1,0,2,1, 2,3,2,0,0,3,1,1, 1,0,3,2,3,1,0,2};
+      int sbox6[0x20] = {0,1,2,3,1,2,2,0, 0,1,3,0,2,3,1,3, 2,3,0,2,3,0,1,1, 2,1,1,2,0,3,3,0};
+      int sbox7[0x20] = {0,3,2,2,3,0,0,1, 3,0,1,3,1,2,2,1, 1,0,3,3,0,1,1,2, 2,3,1,0,2,3,0,2};
+
+      s12 = sbox1[ (((A3>>0)&1)<<4) | (((A0>>2)&1)<<3) | (((A5>>1)&1)<<2) | (((A6>>3)&1)<<1) | (((A8>>0)&1)<<0) ]
+           |sbox2[ (((A1>>1)&1)<<4) | (((A2>>2)&1)<<3) | (((A5>>3)&1)<<2) | (((A6>>0)&1)<<1) | (((A8>>1)&1)<<0) ];
+      s34 = sbox3[ (((A0>>3)&1)<<4) | (((A1>>0)&1)<<3) | (((A4>>1)&1)<<2) | (((A4>>3)&1)<<1) | (((A5>>2)&1)<<0) ]
+           |sbox4[ (((A2>>3)&1)<<4) | (((A0>>1)&1)<<3) | (((A1>>3)&1)<<2) | (((A3>>2)&1)<<1) | (((A7>>0)&1)<<0) ];
+      s56 = sbox5[ (((A4>>2)&1)<<4) | (((A3>>3)&1)<<3) | (((A5>>0)&1)<<2) | (((A7>>1)&1)<<1) | (((A8>>2)&1)<<0) ]
+           |sbox6[ (((A2>>1)&1)<<4) | (((A3>>1)&1)<<3) | (((A4>>0)&1)<<2) | (((A6>>2)&1)<<1) | (((A8>>3)&1)<<0) ];
+      s7 =  sbox7[ (((A1>>2)&1)<<4) | (((A2>>0)&1)<<3) | (((A6>>1)&1)<<2) | (((A7>>2)&1)<<1) | (((A7>>3)&1)<<0) ];
+*/
+
+      // use 4x4 xor to produce extra nibble for T3
+
+      extra_B[3]=FFXOR(FFXOR(FFXOR(regs->B[aboff+2][0],regs->B[aboff+5][1]),regs->B[aboff+6][2]),regs->B[aboff+8][3]);
+      extra_B[2]=FFXOR(FFXOR(FFXOR(regs->B[aboff+5][0],regs->B[aboff+7][1]),regs->B[aboff+2][3]),regs->B[aboff+3][2]);
+      extra_B[1]=FFXOR(FFXOR(FFXOR(regs->B[aboff+4][3],regs->B[aboff+7][2]),regs->B[aboff+3][0]),regs->B[aboff+4][1]);
+      extra_B[0]=FFXOR(FFXOR(FFXOR(regs->B[aboff+8][2],regs->B[aboff+5][3]),regs->B[aboff+2][1]),regs->B[aboff+7][0]);
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"extra_B[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&extra_B[dbg],BYPG,BYPG));
+}
+
+      // T1 = xor all inputs
+      // in1, in2, D are only used in T1 during initialisation, not generation
+      for(b=0;b<4;b++){
+        regs->A[aboff-1][b]=FFXOR(regs->A[aboff+9][b],regs->X[b]);
+      }
+
+#ifdef STREAM_INIT
+      for(b=0;b<4;b++){
+        regs->A[aboff-1][b]=FFXOR(FFXOR(regs->A[aboff-1][b],regs->D[b]),((j % 2) ? in2[b] : in1[b]));
+      }
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"next_A0[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->A[aboff-1][dbg],BYPG,BYPG));
+}
+
+      // T2 =  xor all inputs
+      // in1, in2 are only used in T1 during initialisation, not generation
+      // if p=0, use this, if p=1, rotate the result left
+      for(b=0;b<4;b++){
+        regs->B[aboff-1][b]=FFXOR(FFXOR(regs->B[aboff+6][b],regs->B[aboff+9][b]),regs->Y[b]);
+      }
+
+#ifdef STREAM_INIT
+      for(b=0;b<4;b++){
+        regs->B[aboff-1][b]=FFXOR(regs->B[aboff-1][b],((j % 2) ? in1[b] : in2[b]));
+      }
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"next_B0[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->B[aboff-1][dbg],BYPG,BYPG));
+}
+
+      // if p=1, rotate left (yes, this is what we're doing)
+      tmp3=regs->B[aboff-1][3];
+      regs->B[aboff-1][3]=FFXOR(regs->B[aboff-1][3],FFAND(FFXOR(regs->B[aboff-1][3],regs->B[aboff-1][2]),regs->p));
+      regs->B[aboff-1][2]=FFXOR(regs->B[aboff-1][2],FFAND(FFXOR(regs->B[aboff-1][2],regs->B[aboff-1][1]),regs->p));
+      regs->B[aboff-1][1]=FFXOR(regs->B[aboff-1][1],FFAND(FFXOR(regs->B[aboff-1][1],regs->B[aboff-1][0]),regs->p));
+      regs->B[aboff-1][0]=FFXOR(regs->B[aboff-1][0],FFAND(FFXOR(regs->B[aboff-1][0],tmp3),regs->p));
+
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"next_B0[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->B[aboff-1][dbg],BYPG,BYPG));
+}
+
+      // T3 = xor all inputs
+      for(b=0;b<4;b++){
+        regs->D[b]=FFXOR(FFXOR(regs->E[b],regs->Z[b]),extra_B[b]);
+      }
+
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"D[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->D[dbg],BYPG,BYPG));
+}
+
+      // T4 = sum, carry of Z + E + r
+      for(b=0;b<4;b++){
+        next_E[b]=regs->F[b];
+      }
+
+      tmp0=FFXOR(regs->Z[0],regs->E[0]);
+      tmp1=FFAND(regs->Z[0],regs->E[0]);
+      regs->F[0]=FFXOR(regs->E[0],FFAND(regs->q,FFXOR(regs->Z[0],regs->r)));
+      tmp3=FFAND(tmp0,regs->r);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(regs->Z[1],regs->E[1]);
+      tmp1=FFAND(regs->Z[1],regs->E[1]);
+      regs->F[1]=FFXOR(regs->E[1],FFAND(regs->q,FFXOR(regs->Z[1],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(regs->Z[2],regs->E[2]);
+      tmp1=FFAND(regs->Z[2],regs->E[2]);
+      regs->F[2]=FFXOR(regs->E[2],FFAND(regs->q,FFXOR(regs->Z[2],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(regs->Z[3],regs->E[3]);
+      tmp1=FFAND(regs->Z[3],regs->E[3]);
+      regs->F[3]=FFXOR(regs->E[3],FFAND(regs->q,FFXOR(regs->Z[3],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      regs->r=FFXOR(regs->r,FFAND(regs->q,FFXOR(FFOR(tmp1,tmp3),regs->r))); // ultimate carry
+
+/*
+      we have just done this: (believe it or not)
+      
+      if (q) {
+        F = Z + E + r;
+        r = (F >> 4) & 1;
+        F = F & 0x0f;
+      }
+      else {
+          F = E;
+      }
+*/
+      for(b=0;b<4;b++){
+        regs->E[b]=next_E[b];
+      }
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"F[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->F[dbg],BYPG,BYPG));
+}
+DBG(fprintf(stderr,"r="));
+DBG(dump_mem("",(unsigned char *)&regs->r,BYPG,BYPG));
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"E[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->E[dbg],BYPG,BYPG));
+}
+
+      // this simple instruction is virtually shifting all the shift registers
+      aboff--;
+
+/*
+      we've just done this:
+
+      A9=A8;A8=A7;A7=A6;A6=A5;A5=A4;A4=A3;A3=A2;A2=A1;A1=A0;A0=next_A0;
+      B9=B8;B8=B7;B7=B6;B6=B5;B5=B4;B4=B3;B3=B2;B2=B1;B1=B0;B0=next_B0;
+*/
+
+      regs->X[0]=s1a;
+      regs->X[1]=s2a;
+      regs->X[2]=s3b;
+      regs->X[3]=s4b;
+      regs->Y[0]=s3a;
+      regs->Y[1]=s4a;
+      regs->Y[2]=s5b;
+      regs->Y[3]=s6b;
+      regs->Z[0]=s5a;
+      regs->Z[1]=s6a;
+      regs->Z[2]=s1b;
+      regs->Z[3]=s2b;
+      regs->p=s7a;
+      regs->q=s7b;
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"X[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->X[dbg],BYPG,BYPG));
+}
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"Y[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->Y[dbg],BYPG,BYPG));
+}
+for(dbg=0;dbg<4;dbg++){
+  DBG(fprintf(stderr,"Z[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&regs->Z[dbg],BYPG,BYPG));
+}
+DBG(fprintf(stderr,"p="));
+DBG(dump_mem("",(unsigned char *)&regs->p,BYPG,BYPG));
+DBG(fprintf(stderr,"q="));
+DBG(dump_mem("",(unsigned char *)&regs->q,BYPG,BYPG));
+
+#ifdef STREAM_NORMAL
+      // require 4 loops per output byte
+      // 2 output bits are a function of the 4 bits of D
+      // xor 2 by 2
+      cb_g[8*i+7-2*j]=FFXOR(regs->D[2],regs->D[3]);
+      cb_g[8*i+6-2*j]=FFXOR(regs->D[0],regs->D[1]);
+for(dbg=0;dbg<8;dbg++){
+  DBG(fprintf(stderr,"op[%i]=",dbg));
+  DBG(dump_mem("",(unsigned char *)&cb_g[8*i+dbg],BYPG,BYPG));
+}
+#endif
+
+DBG(fprintf(stderr,"---END INTERNAL LOOP\n"));
+
+    } // INTERNAL LOOP
+
+DBG(fprintf(stderr,"--END EXTERNAL LOOP\n"));
+
+  } // EXTERNAL LOOP
+
+  // move 32 steps forward, ready for next call
+  for(k=0;k<10;k++){
+    for(b=0;b<4;b++){
+DBG(fprintf(stderr,"moving forward AB k=%i b=%i\n",k,b));
+      regs->A[32+k][b]=regs->A[k][b];
+      regs->B[32+k][b]=regs->B[k][b];
+    }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef STREAM_NORMAL
+for(j=0;j<64;j++){
+  DBG(fprintf(stderr,"postcall prerot cb[%2i]=",j));
+  DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG));
+}
+
+#if GROUP_PARALLELISM==32
+trasp64_32_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88cw(cb);
+#endif
+
+for(j=0;j<64;j++){
+  DBG(fprintf(stderr,"postcall postrot cb[%2i]=",j));
+  DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG));
+}
+#endif
+
+#ifdef STREAM_INIT
+  DBG(fprintf(stderr,":::::::::: END STREAM INIT\n"));
+#endif
+#ifdef STREAM_NORMAL
+  DBG(fprintf(stderr,":::::::::: END STREAM NORMAL\n"));
+#endif
+
+}
+
diff --git a/contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_FFdecsa.c b/contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_FFdecsa.c
new file mode 100644 (file)
index 0000000..3c7788b
--- /dev/null
@@ -0,0 +1,790 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "FFdecsa.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+// activate debug by changing the grep command there.
+// don't edit autogenerated files (name beginning with "_").
+
+//// parallelization stuff, large speed differences are possible
+// possible choices
+#define PARALLEL_32_4CHAR     320
+#define PARALLEL_32_4CHARA    321
+#define PARALLEL_32_INT       322
+#define PARALLEL_64_8CHAR     640
+#define PARALLEL_64_8CHARA    641
+#define PARALLEL_64_2INT      642
+#define PARALLEL_64_LONG      643
+#define PARALLEL_64_MMX       644
+#define PARALLEL_128_16CHAR  1280
+#define PARALLEL_128_16CHARA 1281
+#define PARALLEL_128_4INT    1282
+#define PARALLEL_128_2LONG   1283
+#define PARALLEL_128_2MMX    1284
+#define PARALLEL_128_SSE     1285
+
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+#define PARALLEL_MODE PARALLEL_64_MMX
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+
+#include "parallel_generic.h"
+//// conditionals
+#if PARALLEL_MODE==PARALLEL_32_4CHAR
+#include "parallel_032_4char.h"
+#elif PARALLEL_MODE==PARALLEL_32_4CHARA
+#include "parallel_032_4charA.h"
+#elif PARALLEL_MODE==PARALLEL_32_INT
+#include "parallel_032_int.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHAR
+#include "parallel_064_8char.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHARA
+#include "parallel_064_8charA.h"
+#elif PARALLEL_MODE==PARALLEL_64_2INT
+#include "parallel_064_2int.h"
+#elif PARALLEL_MODE==PARALLEL_64_LONG
+#include "parallel_064_long.h"
+#elif PARALLEL_MODE==PARALLEL_64_MMX
+#include "parallel_064_mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHAR
+#include "parallel_128_16char.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHARA
+#include "parallel_128_16charA.h"
+#elif PARALLEL_MODE==PARALLEL_128_4INT
+#include "parallel_128_4int.h"
+#elif PARALLEL_MODE==PARALLEL_128_2LONG
+#include "parallel_128_2long.h"
+#elif PARALLEL_MODE==PARALLEL_128_2MMX
+#include "parallel_128_2mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_SSE
+#include "parallel_128_sse.h"
+#else
+#error "unknown/undefined parallel mode"
+#endif
+
+// stuff depending on conditionals
+
+#define BYTES_PER_GROUP (GROUP_PARALLELISM/8)
+#define BYPG BYTES_PER_GROUP
+#define BITS_PER_GROUP GROUP_PARALLELISM
+#define BIPG BITS_PER_GROUP
+
+
+//// debug tool
+
+static void dump_mem(unsigned char *string, unsigned char *p, int len, int linelen){
+  int i;
+  for(i=0;i<len;i++){
+    if(i%linelen==0&&i) fprintf(stderr,"\n");
+    if(i%linelen==0) fprintf(stderr,"%s %08x:",string,i);
+    else{
+      if(i%8==0) fprintf(stderr," ");
+      if(i%4==0) fprintf(stderr," ");
+    }
+    fprintf(stderr," %02x",p[i]);
+  }
+  if(i%linelen==0) fprintf(stderr,"\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+struct csa_key_t{
+       unsigned char ck[8];
+// used by stream
+        int iA[8];  // iA[0] is for A1, iA[7] is for A8
+        int iB[8];  // iB[0] is for B1, iB[7] is for B8
+// used by stream (group)
+        group ck_g[8][8]; // [byte][bit:0=LSB,7=MSB]
+        group iA_g[8][4]; // [0 for A1][0 for LSB]
+        group iB_g[8][4]; // [0 for B1][0 for LSB]
+// used by block
+       unsigned char kk[56];
+// used by block (group)
+       __attribute__((aligned(16))) batch kkmulti[56]; // many times the same byte in every batch
+};
+
+static struct csa_keys_t{
+  struct csa_key_t even;
+  struct csa_key_t odd;
+} keys;
+
+
+//-----stream cypher
+
+//-----key schedule for stream decypher
+static void key_schedule_stream(
+  unsigned char *ck,    // [In]  ck[0]-ck[7]   8 bytes   | Key.
+  int *iA,              // [Out] iA[0]-iA[7]   8 nibbles | Key schedule.
+  int *iB)              // [Out] iB[0]-iB[7]   8 nibbles | Key schedule.
+{
+    iA[0]=(ck[0]>>4)&0xf;
+    iA[1]=(ck[0]   )&0xf;
+    iA[2]=(ck[1]>>4)&0xf;
+    iA[3]=(ck[1]   )&0xf;
+    iA[4]=(ck[2]>>4)&0xf;
+    iA[5]=(ck[2]   )&0xf;
+    iA[6]=(ck[3]>>4)&0xf;
+    iA[7]=(ck[3]   )&0xf;
+    iB[0]=(ck[4]>>4)&0xf;
+    iB[1]=(ck[4]   )&0xf;
+    iB[2]=(ck[5]>>4)&0xf;
+    iB[3]=(ck[5]   )&0xf;
+    iB[4]=(ck[6]>>4)&0xf;
+    iB[5]=(ck[6]   )&0xf;
+    iB[6]=(ck[7]>>4)&0xf;
+    iB[7]=(ck[7]   )&0xf;
+}
+
+//----- stream main function
+
+#define STREAM_INIT
+#include "tmp_autogenerated_stuff_stream.c"
+#undef STREAM_INIT
+
+#define STREAM_NORMAL
+#include "tmp_autogenerated_stuff_stream.c"
+#undef STREAM_NORMAL
+
+
+//-----block decypher
+
+//-----key schedule for block decypher
+
+static void key_schedule_block(
+  unsigned char *ck,    // [In]  ck[0]-ck[7]   8 bytes | Key.
+  unsigned char *kk)    // [Out] kk[0]-kk[55] 56 bytes | Key schedule.
+{
+  static const unsigned char key_perm[0x40] = {
+    0x12,0x24,0x09,0x07,0x2A,0x31,0x1D,0x15, 0x1C,0x36,0x3E,0x32,0x13,0x21,0x3B,0x40,
+    0x18,0x14,0x25,0x27,0x02,0x35,0x1B,0x01, 0x22,0x04,0x0D,0x0E,0x39,0x28,0x1A,0x29,
+    0x33,0x23,0x34,0x0C,0x16,0x30,0x1E,0x3A, 0x2D,0x1F,0x08,0x19,0x17,0x2F,0x3D,0x11,
+    0x3C,0x05,0x38,0x2B,0x0B,0x06,0x0A,0x2C, 0x20,0x3F,0x2E,0x0F,0x03,0x26,0x10,0x37,
+  };
+
+  int i,j,k;
+  int bit[64];
+  int newbit[64];
+  int kb[7][8];
+
+  // 56 steps
+  // 56 key bytes kk(55)..kk(0) by key schedule from ck
+
+  // kb(6,0) .. kb(6,7) = ck(0) .. ck(7)
+  kb[6][0] = ck[0];
+  kb[6][1] = ck[1];
+  kb[6][2] = ck[2];
+  kb[6][3] = ck[3];
+  kb[6][4] = ck[4];
+  kb[6][5] = ck[5];
+  kb[6][6] = ck[6];
+  kb[6][7] = ck[7];
+
+  // calculate kb[5] .. kb[0]
+  for(i=5; i>=0; i--){
+    // 64 bit perm on kb
+    for(j=0; j<8; j++){
+      for(k=0; k<8; k++){
+        bit[j*8+k] = (kb[i+1][j] >> (7-k)) & 1;
+        newbit[key_perm[j*8+k]-1] = bit[j*8+k];
+      }
+    }
+    for(j=0; j<8; j++){
+      kb[i][j] = 0;
+      for(k=0; k<8; k++){
+        kb[i][j] |= newbit[j*8+k] << (7-k);
+      }
+    }
+  }
+
+  // xor to give kk
+  for(i=0; i<7; i++){
+    for(j=0; j<8; j++){
+      kk[i*8+j] = kb[i][j] ^ i;
+    }
+  }
+
+}
+
+//-----block utils
+
+static inline __attribute__((always_inline)) void trasp_N_8 (unsigned char *in,unsigned char* out,int count){
+  int *ri=(int *)in;
+  int *ibi=(int *)out;
+  int j,i,k,g;
+  // copy and first step
+  for(g=0;g<count;g++){
+    ri[g]=ibi[2*g];
+    ri[GROUP_PARALLELISM+g]=ibi[2*g+1];
+  }
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+  for(j=0;j<8;j+=4){
+    for(i=0;i<2;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+2)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x0000ffff)      | ((b           )<<16);
+        ri[INTS_PER_ROW*(j+i+2)+k]=  ((t           )>>16) |  (b&0xffff0000) ;
+      }
+    }
+  }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+  for(j=0;j<8;j+=2){
+    for(i=0;i<1;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+1)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+        ri[INTS_PER_ROW*(j+i+1)+k]=  ((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+      }
+    }
+  }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+}
+
+static inline __attribute__((always_inline)) void trasp_8_N (unsigned char *in,unsigned char* out,int count){
+  int *ri=(int *)in;
+  int *bdi=(int *)out;
+  int j,i,k,g;
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+  for(j=0;j<8;j+=2){
+    for(i=0;i<1;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+1)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+        ri[INTS_PER_ROW*(j+i+1)+k]=  ((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+      }
+    }
+  }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+  for(j=0;j<8;j+=4){
+    for(i=0;i<2;i++){
+      for(k=0;k<INTS_PER_ROW;k++){
+        unsigned int t,b;
+        t=ri[INTS_PER_ROW*(j+i)+k];
+        b=ri[INTS_PER_ROW*(j+i+2)+k];
+        ri[INTS_PER_ROW*(j+i)+k]=     (t&0x0000ffff)      | ((b           )<<16);
+        ri[INTS_PER_ROW*(j+i+2)+k]=  ((t           )>>16) |  (b&0xffff0000) ;
+      }
+    }
+  }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+  for(g=0;g<count;g++){
+    bdi[2*g]=ri[g];
+    bdi[2*g+1]=ri[GROUP_PARALLELISM+g];
+  }
+}
+
+//-----block main function
+
+// block group
+static void block_decypher_group(
+  batch *kkmulti,       // [In]  kkmulti[0]-kkmulti[55] 56 batches | Key schedule (each batch has repeated equal bytes).
+  unsigned char *ib,    // [In]  (ib0,ib1,...ib7)...x32 32*8 bytes | Initialization vector.
+  unsigned char *bd,    // [Out] (bd0,bd1,...bd7)...x32 32*8 bytes | Block decipher.
+  int count)
+{
+  // int is faster than unsigned char. apparently not
+  static const unsigned char block_sbox[0x100] = {
+    0x3A,0xEA,0x68,0xFE,0x33,0xE9,0x88,0x1A, 0x83,0xCF,0xE1,0x7F,0xBA,0xE2,0x38,0x12,
+    0xE8,0x27,0x61,0x95,0x0C,0x36,0xE5,0x70, 0xA2,0x06,0x82,0x7C,0x17,0xA3,0x26,0x49,
+    0xBE,0x7A,0x6D,0x47,0xC1,0x51,0x8F,0xF3, 0xCC,0x5B,0x67,0xBD,0xCD,0x18,0x08,0xC9,
+    0xFF,0x69,0xEF,0x03,0x4E,0x48,0x4A,0x84, 0x3F,0xB4,0x10,0x04,0xDC,0xF5,0x5C,0xC6,
+    0x16,0xAB,0xAC,0x4C,0xF1,0x6A,0x2F,0x3C, 0x3B,0xD4,0xD5,0x94,0xD0,0xC4,0x63,0x62,
+    0x71,0xA1,0xF9,0x4F,0x2E,0xAA,0xC5,0x56, 0xE3,0x39,0x93,0xCE,0x65,0x64,0xE4,0x58,
+    0x6C,0x19,0x42,0x79,0xDD,0xEE,0x96,0xF6, 0x8A,0xEC,0x1E,0x85,0x53,0x45,0xDE,0xBB,
+    0x7E,0x0A,0x9A,0x13,0x2A,0x9D,0xC2,0x5E, 0x5A,0x1F,0x32,0x35,0x9C,0xA8,0x73,0x30,
+
+    0x29,0x3D,0xE7,0x92,0x87,0x1B,0x2B,0x4B, 0xA5,0x57,0x97,0x40,0x15,0xE6,0xBC,0x0E,
+    0xEB,0xC3,0x34,0x2D,0xB8,0x44,0x25,0xA4, 0x1C,0xC7,0x23,0xED,0x90,0x6E,0x50,0x00,
+    0x99,0x9E,0x4D,0xD9,0xDA,0x8D,0x6F,0x5F, 0x3E,0xD7,0x21,0x74,0x86,0xDF,0x6B,0x05,
+    0x8E,0x5D,0x37,0x11,0xD2,0x28,0x75,0xD6, 0xA7,0x77,0x24,0xBF,0xF0,0xB0,0x02,0xB7,
+    0xF8,0xFC,0x81,0x09,0xB1,0x01,0x76,0x91, 0x7D,0x0F,0xC8,0xA0,0xF2,0xCB,0x78,0x60,
+    0xD1,0xF7,0xE0,0xB5,0x98,0x22,0xB3,0x20, 0x1D,0xA6,0xDB,0x7B,0x59,0x9F,0xAE,0x31,
+    0xFB,0xD3,0xB6,0xCA,0x43,0x72,0x07,0xF4, 0xD8,0x41,0x14,0x55,0x0D,0x54,0x8B,0xB9,
+    0xAD,0x46,0x0B,0xAF,0x80,0x52,0x2C,0xFA, 0x8C,0x89,0x66,0xFD,0xB2,0xA9,0x9B,0xC0,
+  };
+  unsigned char r[GROUP_PARALLELISM*(8+56)];  /* 56 because we will move back in memory while looping */
+  unsigned char sbox_in[GROUP_PARALLELISM],sbox_out[GROUP_PARALLELISM],perm_out[GROUP_PARALLELISM];
+  int roff;
+  int i,g,count_all=GROUP_PARALLELISM;
+
+  roff=GROUP_PARALLELISM*56;
+
+#define FASTTRASP1
+#ifndef FASTTRASP1
+  for(g=0;g<count;g++){
+    // Init registers 
+    int j;
+    for(j=0;j<8;j++){
+      r[roff+GROUP_PARALLELISM*j+g]=ib[8*g+j];
+    }
+  }
+#else
+  trasp_N_8((unsigned char *)&r[roff],(unsigned char *)ib,count);
+#endif
+//dump_mem("OLD r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+
+  // loop over kk[55]..kk[0]
+  for(i=55;i>=0;i--){
+    {
+      batch tkkmulti=kkmulti[i];
+      batch *si=(batch *)sbox_in;
+      batch *r6_N=(batch *)(r+roff+GROUP_PARALLELISM*6);
+      for(g=0;g<count_all/BYTES_PER_BATCH;g++){
+        si[g]=B_FFXOR(tkkmulti,r6_N[g]);              //FIXME: introduce FASTBATCH?
+      }
+    }
+
+    // table lookup, this works on only one byte at a time
+    // most difficult part of all
+    // - can't be parallelized
+    // - can't be synthetized through boolean terms (8 input bits are too many)
+    for(g=0;g<count_all;g++){
+      sbox_out[g]=block_sbox[sbox_in[g]];
+    }
+
+    // bit permutation
+    {
+      unsigned char *po=(unsigned char *)perm_out;
+      unsigned char *so=(unsigned char *)sbox_out;
+//dump_mem("pre perm ",(unsigned char *)so,GROUP_PARALLELISM,GROUP_PARALLELISM);
+      for(g=0;g<count_all;g+=BYTES_PER_BATCH){
+        batch in,out;
+        in=*(batch *)&so[g];
+
+        out=B_FFOR(
+           B_FFOR(
+           B_FFOR(
+           B_FFOR(
+           B_FFOR(
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_29()),1),
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_02()),6)),
+                  B_FFSH8L(B_FFAND(in,B_FFN_ALL_04()),3)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_10()),2)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_40()),6)),
+                  B_FFSH8R(B_FFAND(in,B_FFN_ALL_80()),4));
+
+        *(batch *)&po[g]=out;
+      }
+//dump_mem("post perm",(unsigned char *)po,GROUP_PARALLELISM,GROUP_PARALLELISM);
+    }
+
+    roff-=GROUP_PARALLELISM; /* virtual shift of registers */
+
+#if 0
+/* one by one */
+    for(g=0;g<count_all;g++){
+      r[roff+GROUP_PARALLELISM*0+g]=r[roff+GROUP_PARALLELISM*8+g]^sbox_out[g];
+      r[roff+GROUP_PARALLELISM*6+g]^=perm_out[g];
+      r[roff+GROUP_PARALLELISM*4+g]^=r[roff+GROUP_PARALLELISM*0+g];
+      r[roff+GROUP_PARALLELISM*3+g]^=r[roff+GROUP_PARALLELISM*0+g];
+      r[roff+GROUP_PARALLELISM*2+g]^=r[roff+GROUP_PARALLELISM*0+g];
+    }
+#else
+    for(g=0;g<count_all;g+=BEST_SPAN){
+      XOR_BEST_BY(&r[roff+GROUP_PARALLELISM*0+g],&r[roff+GROUP_PARALLELISM*8+g],&sbox_out[g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*6+g],&perm_out[g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*4+g],&r[roff+GROUP_PARALLELISM*0+g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*3+g],&r[roff+GROUP_PARALLELISM*0+g]);
+      XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*2+g],&r[roff+GROUP_PARALLELISM*0+g]);
+    }
+#endif
+  }
+
+#define FASTTRASP2
+#ifndef FASTTRASP2
+  for(g=0;g<count;g++){
+    // Copy results
+    int j;
+    for(j=0;j<8;j++){
+      bd[8*g+j]=r[roff+GROUP_PARALLELISM*j+g];
+    }
+  }
+#else
+  trasp_8_N((unsigned char *)&r[roff],(unsigned char *)bd,count);
+#endif
+}
+
+//-----------------------------------EXTERNAL INTERFACE
+
+//-----get internal parallelism
+
+int get_internal_parallelism(void){
+  return GROUP_PARALLELISM;
+}
+
+//-----get suggested cluster size
+
+int get_suggested_cluster_size(void){
+  int r;
+  r=GROUP_PARALLELISM+GROUP_PARALLELISM/10;
+  if(r<GROUP_PARALLELISM+5) r=GROUP_PARALLELISM+5;
+  return r;
+}
+
+//-----set control words
+
+void set_control_words(unsigned char *ev, unsigned char *od){
+  // could be made faster, but is not run often
+  int bi,by;
+  int i,j;
+// key
+  memcpy(keys.even.ck,ev,8);
+  memcpy(keys.odd.ck,od,8);
+// precalculations for stream
+  key_schedule_stream(keys.even.ck,keys.even.iA,keys.even.iB);
+  key_schedule_stream(keys.odd.ck,keys.odd.iA,keys.odd.iB);
+  for(by=0;by<8;by++){
+    for(bi=0;bi<8;bi++){
+      keys.even.ck_g[by][bi]=(keys.even.ck[by]&(1<<bi))?FF1():FF0();
+      keys.odd.ck_g[by][bi]=(keys.odd.ck[by]&(1<<bi))?FF1():FF0();
+    }
+  }
+  for(by=0;by<8;by++){
+    for(bi=0;bi<4;bi++){
+      keys.even.iA_g[by][bi]=(keys.even.iA[by]&(1<<bi))?FF1():FF0();
+      keys.odd.iA_g[by][bi]=(keys.odd.iA[by]&(1<<bi))?FF1():FF0();
+      keys.even.iB_g[by][bi]=(keys.even.iB[by]&(1<<bi))?FF1():FF0();
+      keys.odd.iB_g[by][bi]=(keys.odd.iB[by]&(1<<bi))?FF1():FF0();
+    }
+  }
+// precalculations for block
+  key_schedule_block(keys.even.ck,keys.even.kk);
+  key_schedule_block(keys.odd.ck,keys.odd.kk);
+  for(i=0;i<56;i++){
+    for(j=0;j<BYTES_PER_BATCH;j++){
+      *(((unsigned char *)&keys.even.kkmulti[i])+j)=keys.even.kk[i];
+      *(((unsigned char *)&keys.odd.kkmulti[i])+j)=keys.odd.kk[i];
+    }
+  }
+}
+
+//-----get control words
+
+void get_control_words(unsigned char *even, unsigned char *odd){
+  memcpy(even,keys.even.ck,8);
+  memcpy(odd,keys.odd.ck,8);
+}
+
+//----- decrypt
+
+int decrypt_packets(unsigned char **cluster){
+  // statistics, currently unused
+  int stat_no_scramble=0;
+  int stat_reserved=0;
+  int stat_decrypted[2]={0,0};
+  int stat_decrypted_mini=0;
+  unsigned char **clst;
+  unsigned char **clst2;
+  int grouped;
+  int group_ev_od;
+  int advanced;
+  int can_advance;
+  unsigned char *g_pkt[GROUP_PARALLELISM];
+  int g_len[GROUP_PARALLELISM];
+  int g_offset[GROUP_PARALLELISM];
+  int g_n[GROUP_PARALLELISM];
+  int g_residue[GROUP_PARALLELISM];
+  unsigned char *pkt;
+  int xc0,ev_od,len,offset,n,residue;
+  struct csa_key_t* k;
+  int i,j,iter,g;
+  int t23,tsmall;
+  int alive[24];
+//icc craziness  int pad1=0; //////////align! FIXME
+  unsigned char *encp[GROUP_PARALLELISM];
+  unsigned char stream_in[GROUP_PARALLELISM*8];
+  unsigned char stream_out[GROUP_PARALLELISM*8];
+  unsigned char ib[GROUP_PARALLELISM*8];
+  unsigned char block_out[GROUP_PARALLELISM*8];
+
+//icc craziness  i=(int)&pad1;//////////align!!! FIXME
+
+  // build a list of packets to be processed
+  clst=cluster;
+  grouped=0;
+  advanced=0;
+  can_advance=1;
+  group_ev_od=-1; // silence incorrect compiler warning
+  pkt=*clst;
+  do{ // find a new packet
+    if(grouped==GROUP_PARALLELISM){
+      // full
+      break;
+    }
+    if(pkt==NULL){
+      // no more ranges
+      break;
+    }
+    if(pkt>=*(clst+1)){
+      // out of this range, try next
+      clst++;clst++;
+      pkt=*clst;
+      continue;
+    }
+
+    do{ // handle this packet
+      xc0=pkt[3]&0xc0;
+      if(xc0==0x00){
+        advanced+=can_advance;
+        stat_no_scramble++;
+        break;
+      }
+      if(xc0==0x40){
+        advanced+=can_advance;
+        stat_reserved++;
+        break;
+      }
+      if(xc0==0x80||xc0==0xc0){ // encrypted
+        ev_od=(xc0&0x40)>>6; // 0 even, 1 odd
+        if(grouped==0) group_ev_od=ev_od; // this group will be all even (or odd)
+        if(group_ev_od==ev_od){ // could be added to group
+          pkt[3]&=0x3f;  // consider it decrypted now
+          if(pkt[3]&0x20){ // incomplete packet
+            offset=4+pkt[4]+1;
+            len=188-offset;
+            n=len>>3;
+            residue=len-(n<<3);
+            if(n==0){ // decrypted==encrypted!
+              advanced+=can_advance;
+              stat_decrypted_mini++;
+              break; // this doesn't need more processing
+            }
+          }else{
+            len=184;
+            offset=4;
+            n=23;
+            residue=0;
+          }
+          g_pkt[grouped]=pkt;
+          g_len[grouped]=len;
+          g_offset[grouped]=offset;
+          g_n[grouped]=n;
+          g_residue[grouped]=residue;
+          grouped++;
+          advanced+=can_advance;
+          stat_decrypted[ev_od]++;
+        }
+        else{
+          can_advance=0;
+          break; // skip and go on
+        }
+      }
+    } while(0);
+
+    if(can_advance){
+      // move range start forward
+      *clst+=188;
+    }
+    // next packet, if there is one
+    pkt+=188;
+  } while(1);
+
+  // delete empty ranges and compact list
+  clst2=cluster;
+  for(clst=cluster;*clst!=NULL;clst+=2){
+    // if not empty
+    if(*clst<*(clst+1)){
+      // it will remain 
+      *clst2=*clst;
+      *(clst2+1)=*(clst+1);
+      clst2+=2;
+    }
+  }
+  *clst2=NULL;
+
+  if(grouped==0){
+    // no processing needed
+    return advanced;
+  }
+
+  //  sort them, longest payload first
+  //  we expect many n=23 packets and a few n<23
+  // grouped is always <= GROUP_PARALLELISM
+
+#define g_swap(a,b) \
+    pkt=g_pkt[a]; \
+    g_pkt[a]=g_pkt[b]; \
+    g_pkt[b]=pkt; \
+\
+    len=g_len[a]; \
+    g_len[a]=g_len[b]; \
+    g_len[b]=len; \
+\
+    offset=g_offset[a]; \
+    g_offset[a]=g_offset[b]; \
+    g_offset[b]=offset; \
+\
+    n=g_n[a]; \
+    g_n[a]=g_n[b]; \
+    g_n[b]=n; \
+\
+    residue=g_residue[a]; \
+    g_residue[a]=g_residue[b]; \
+    g_residue[b]=residue;
+
+  // step 1: move n=23 packets before small packets
+  t23=0;
+  tsmall=grouped-1;
+  for(;;){
+    for(;t23<grouped;t23++){
+      if(g_n[t23]!=23) break;
+    }
+    
+    for(;tsmall>=0;tsmall--){
+      if(g_n[tsmall]==23) break;
+    }
+    
+    if(tsmall-t23<1) break;
+    
+
+    g_swap(t23,tsmall);
+
+    t23++;
+    tsmall--;
+  }
+
+  // step 2: sort small packets in decreasing order of n (bubble sort is enough)
+  for(i=t23;i<grouped;i++){
+    for(j=i+1;j<grouped;j++){
+      if(g_n[j]>g_n[i]){
+        g_swap(i,j);
+      }
+    }
+  }
+
+  // we need to know how many packets need 23 iterations, how many 22...
+  for(i=0;i<=23;i++){
+    alive[i]=0;
+  }
+  // count
+  alive[23-1]=t23;
+  for(i=t23;i<grouped;i++){
+    alive[g_n[i]-1]++;
+  }
+  // integrate
+  for(i=22;i>=0;i--){
+    alive[i]+=alive[i+1];
+  }
+
+  // choose key
+  if(group_ev_od==0){
+    k=&keys.even;
+  }
+  else{
+    k=&keys.odd;
+  }
+
+  //INIT
+#define INITIALIZE_UNUSED_INPUT
+#ifdef INITIALIZE_UNUSED_INPUT
+// unnecessary zeroing.
+// without this, we operate on uninitialized memory
+// when grouped<GROUP_PARALLELISM, but it's not a problem,
+// as final results will be discarded.
+// random data makes debugging sessions difficult.
+  for(j=0;j<GROUP_PARALLELISM*8;j++) stream_in[j]=0;
+#else
+#endif
+
+  for(g=0;g<grouped;g++){
+    encp[g]=g_pkt[g];
+    encp[g]+=g_offset[g]; // skip header
+    FFTABLEIN(stream_in,g,encp[g]);
+  }
+//dump_mem("stream_in",stream_in,GROUP_PARALLELISM*8,BYPG);
+
+
+  // ITER 0
+  iter=0;
+  stream_cypher_group_init(k->iA_g,k->iB_g,stream_in);
+  // fill first ib
+  for(g=0;g<alive[iter];g++){
+    COPY_8_BY(ib+8*g,encp[g]);
+  }
+  // ITER 1..N-1
+  for (iter=1;iter<23&&alive[iter-1]>0;iter++){
+    // alive and just dead packets: calc block
+    block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+    // all packets (dead too): calc stream
+    stream_cypher_group_normal(stream_out);
+//dump_mem("stream_out",stream_out,GROUP_PARALLELISM*8,BYPG);
+
+    // alive packets: calc ib
+    for(g=0;g<alive[iter];g++){
+      FFTABLEOUT(ib+8*g,stream_out,g);
+// XOREQ8BY gcc bug? 2x4 ok, 8 ko    UPDATE: result ok but speed 1-2% slower (!!!???)
+#if 1
+      XOREQ_4_BY(ib+8*g,encp[g]+8);
+      XOREQ_4_BY(ib+8*g+4,encp[g]+8+4);
+#else
+      XOREQ_8_BY(ib+8*g,encp[g]+8);
+#endif
+    }
+    // alive packets: decrypt data
+    for(g=0;g<alive[iter];g++){
+      XOR_8_BY(encp[g],ib+8*g,block_out+8*g);
+    }
+    // just dead packets: write decrypted data
+    for(g=alive[iter];g<alive[iter-1];g++){
+      COPY_8_BY(encp[g],block_out+8*g);
+    }
+    // just dead packets: decrypt residue
+    for(g=alive[iter];g<alive[iter-1];g++){
+      FFTABLEOUTXORNBY(g_residue[g],encp[g]+8,stream_out,g);
+    }
+    // alive packets: pointers++
+    for(g=0;g<alive[iter];g++) encp[g]+=8;
+  };
+  // ITER N
+  iter=23;
+  // calc block
+  block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+  // just dead packets: write decrypted data
+  for(g=alive[iter];g<alive[iter-1];g++){
+    COPY_8_BY(encp[g],block_out+8*g);
+  }
+  // no residue possible
+  // so do nothing
+
+
+  M_EMPTY(); // restore CPU multimedia state
+
+  return advanced;
+}
diff --git a/contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_stream.c b/contrib/sasc-ng/FFdecsa/tmp_autogenerated_stuff_stream.c
new file mode 100644 (file)
index 0000000..cb8ef63
--- /dev/null
@@ -0,0 +1,814 @@
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004  fatih89r
+ *
+ * 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 statics only once, when STREAM_INIT
+#ifdef STREAM_INIT
+static group A[32+10][4]; // 32 because we will move back (virtual shift register)
+static group B[32+10][4]; // 32 because we will move back (virtual shift register)
+static group X[4];
+static group Y[4];
+static group Z[4];
+static group D[4];
+static group E[4];
+static group F[4];
+static group p;
+static group q;
+static group r;
+
+static inline void trasp64_32_88ccw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned int *)data)
+  int i,j;
+  for(j=0;j<64;j+=32){
+    unsigned int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff)      | ((b           )<<16);
+      row[j+16+i]=((t           )>>16) |  (b&0xffff0000) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+      row[j+8+i] =((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0x0f0f0f0f)<<4) |  (b&0x0f0f0f0f);
+      row[j+4+i] = (t&0xf0f0f0f0)     | ((b&0xf0f0f0f0)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0x33333333)<<2) |  (b&0x33333333);
+      row[j+2+i] = (t&0xcccccccc)     | ((b&0xcccccccc)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0x55555555)<<1) |  (b&0x55555555);
+      row[j+1+i] = (t&0xaaaaaaaa)     | ((b&0xaaaaaaaa)>>1);
+    }
+  }
+#undef row
+}
+
+static inline void trasp64_32_88cw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned int *)data)
+  int i,j;
+  for(j=0;j<64;j+=32){
+    unsigned int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff)      | ((b           )<<16);
+      row[j+16+i]=((t           )>>16) |  (b&0xffff0000) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff)     | ((b&0x00ff00ff)<<8);
+      row[j+8+i] =((t&0xff00ff00)>>8) |  (b&0xff00ff00);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]  =((t&0xf0f0f0f0)>>4) |   (b&0xf0f0f0f0);
+      row[j+4+i]= (t&0x0f0f0f0f)     |  ((b&0x0f0f0f0f)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]  =((t&0xcccccccc)>>2) |  (b&0xcccccccc);
+      row[j+2+i]= (t&0x33333333)     | ((b&0x33333333)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]  =((t&0xaaaaaaaa)>>1) |  (b&0xaaaaaaaa);
+      row[j+1+i]= (t&0x55555555)     | ((b&0x55555555)<<1);
+    }
+  }
+#undef row
+}
+
+//64-64----------------------------------------------------------
+static inline void trasp64_64_88ccw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=row[j+i];
+      b=row[j+32+i];
+      row[j+i]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      row[j+32+i]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      row[j+4+i] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      row[j+2+i] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      row[j+1+i] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+    }
+  }
+#undef row
+}
+
+static inline void trasp64_64_88cw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=row[j+i];
+      b=row[j+32+i];
+      row[j+i]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      row[j+32+i]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=row[j+i];
+      b=row[j+16+i];
+      row[j+i]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=row[j+i];
+      b=row[j+8+i];
+      row[j+i]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=row[j+i];
+      b=row[j+4+i];
+      row[j+i]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      row[j+4+i] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=row[j+i];
+      b=row[j+2+i];
+      row[j+i]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      row[j+2+i] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=row[j+i];
+      b=row[j+1+i];
+      row[j+i]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      row[j+1+i] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+    }
+  }
+#undef row
+}
+
+//64-128----------------------------------------------------------
+static inline void trasp64_128_88ccw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define halfrow ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+32+i)];
+      halfrow[2*(j+i)]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+32+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)+1]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+16+i)];
+      halfrow[2*(j+i)]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+16+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+8+i)];
+      halfrow[2*(j+i)]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+8+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+4+i)];
+      halfrow[2*(j+i)]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      halfrow[2*(j+4+i)] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+4+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x0f0f0f0f0f0f0f0fULL)<<4) |  (b&0x0f0f0f0f0f0f0f0fULL);
+      halfrow[2*(j+4+i)+1] = (t&0xf0f0f0f0f0f0f0f0ULL)     | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+2+i)];
+      halfrow[2*(j+i)]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      halfrow[2*(j+2+i)] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+2+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x3333333333333333ULL)<<2) |  (b&0x3333333333333333ULL);
+      halfrow[2*(j+2+i)+1] = (t&0xccccccccccccccccULL)     | ((b&0xccccccccccccccccULL)>>2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+1+i)];
+      halfrow[2*(j+i)]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      halfrow[2*(j+1+i)] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+1+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0x5555555555555555ULL)<<1) |  (b&0x5555555555555555ULL);
+      halfrow[2*(j+1+i)+1] = (t&0xaaaaaaaaaaaaaaaaULL)     | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+    }
+  }
+#undef halfrow
+}
+
+static inline void trasp64_128_88cw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define halfrow ((unsigned long long int *)data)
+  int i,j;
+  for(j=0;j<64;j+=64){
+    unsigned long long int t,b;
+    for(i=0;i<32;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+32+i)];
+      halfrow[2*(j+i)]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+32+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00000000ffffffffULL)      | ((b                      )<<32);
+      halfrow[2*(j+32+i)+1]=((t                      )>>32) |  (b&0xffffffff00000000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=32){
+    unsigned long long int t,b;
+    for(i=0;i<16;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+16+i)];
+      halfrow[2*(j+i)]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+16+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x0000ffff0000ffffULL)      | ((b&0x0000ffff0000ffffULL)<<16);
+      halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) |  (b&0xffff0000ffff0000ULL) ;
+    }
+  }
+  for(j=0;j<64;j+=16){
+    unsigned long long int t,b;
+    for(i=0;i<8;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+8+i)];
+      halfrow[2*(j+i)]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+8+i)+1];
+      halfrow[2*(j+i)+1]   = (t&0x00ff00ff00ff00ffULL)     | ((b&0x00ff00ff00ff00ffULL)<<8);
+      halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) |  (b&0xff00ff00ff00ff00ULL);
+    }
+  }
+  for(j=0;j<64;j+=8){
+    unsigned long long int t,b;
+    for(i=0;i<4;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+4+i)];
+      halfrow[2*(j+i)]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      halfrow[2*(j+4+i)] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+4+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) |   (b&0xf0f0f0f0f0f0f0f0ULL);
+      halfrow[2*(j+4+i)+1] = (t&0x0f0f0f0f0f0f0f0fULL)     |  ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+    }
+  }
+  for(j=0;j<64;j+=4){
+    unsigned long long int t,b;
+    for(i=0;i<2;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+2+i)];
+      halfrow[2*(j+i)]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      halfrow[2*(j+2+i)] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+2+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xccccccccccccccccULL)>>2) |  (b&0xccccccccccccccccULL);
+      halfrow[2*(j+2+i)+1] = (t&0x3333333333333333ULL)     | ((b&0x3333333333333333ULL)<<2);
+    }
+  }
+  for(j=0;j<64;j+=2){
+    unsigned long long int t,b;
+    for(i=0;i<1;i++){
+      t=halfrow[2*(j+i)];
+      b=halfrow[2*(j+1+i)];
+      halfrow[2*(j+i)]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      halfrow[2*(j+1+i)] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+      t=halfrow[2*(j+i)+1];
+      b=halfrow[2*(j+1+i)+1];
+      halfrow[2*(j+i)+1]   =((t&0xaaaaaaaaaaaaaaaaULL)>>1) |  (b&0xaaaaaaaaaaaaaaaaULL);
+      halfrow[2*(j+1+i)+1] = (t&0x5555555555555555ULL)     | ((b&0x5555555555555555ULL)<<1);
+    }
+  }
+#undef halfrow
+}
+#endif
+
+
+#ifdef STREAM_INIT
+void stream_cypher_group_init(
+  group         iA[8][4], // [In]  iA00,iA01,...iA73 32 groups  | Derived from key.
+  group         iB[8][4], // [In]  iB00,iB01,...iB73 32 groups  | Derived from key.
+  unsigned char *sb)      // [In]  (SB0,SB1,...SB7)...x32 32*8 bytes | Extra input.
+#endif
+#ifdef STREAM_NORMAL
+void stream_cypher_group_normal(
+  unsigned char *cb)    // [Out] (CB0,CB1,...CB7)...x32 32*8 bytes | Output.
+#endif
+{
+#ifdef STREAM_INIT
+  group in1[4];
+  group in2[4];
+#endif
+  group extra_B[4];
+  group fa,fb,fc,fd,fe;
+  group s1a,s1b,s2a,s2b,s3a,s3b,s4a,s4b,s5a,s5b,s6a,s6b,s7a,s7b;
+  group next_E[4];
+  group tmp0,tmp1,tmp2,tmp3,tmp4;
+#ifdef STREAM_INIT
+  group *sb_g=(group *)sb;
+#endif
+#ifdef STREAM_NORMAL
+  group *cb_g=(group *)cb;
+#endif
+  int aboff;
+  int i,j,k,b;
+
+#ifdef STREAM_INIT
+#endif
+#ifdef STREAM_NORMAL
+#endif
+#ifdef STREAM_INIT
+
+#if GROUP_PARALLELISM==32
+trasp64_32_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88ccw(sb);
+#endif
+
+#endif
+
+  aboff=32;
+
+#ifdef STREAM_INIT
+  // load first 32 bits of ck into A[aboff+0]..A[aboff+7]
+  // load last  32 bits of ck into B[aboff+0]..B[aboff+7]
+  // all other regs = 0
+  for(i=0;i<8;i++){
+    for(b=0;b<4;b++){
+      A[aboff+i][b]=iA[i][b];
+      B[aboff+i][b]=iB[i][b];
+    }
+  }
+  for(b=0;b<4;b++){
+    A[aboff+8][b]=FF0();
+    A[aboff+9][b]=FF0();
+    B[aboff+8][b]=FF0();
+    B[aboff+9][b]=FF0();
+  }
+  for(b=0;b<4;b++){
+    X[b]=FF0();
+    Y[b]=FF0();
+    Z[b]=FF0();
+    D[b]=FF0();
+    E[b]=FF0();
+    F[b]=FF0();
+  }
+  p=FF0();
+  q=FF0();
+  r=FF0();
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+  // EXTERNAL LOOP - 8 bytes per operation
+  for(i=0;i<8;i++){
+
+
+#ifdef STREAM_INIT
+    for(b=0;b<4;b++){
+      in1[b]=sb_g[8*i+4+b];
+      in2[b]=sb_g[8*i+b];
+    }
+#endif
+
+    // INTERNAL LOOP - 2 bits per iteration
+    for(j=0; j<4; j++){
+
+
+      // from A0..A9, 35 bits are selected as inputs to 7 s-boxes
+      // 5 bits input per s-box, 2 bits output per s-box
+
+      // we can select bits with zero masking and shifting operations
+      // and synthetize s-boxes with optimized boolean functions.
+      // this is the actual reason we do all the crazy transposition
+      // stuff to switch between normal and bit slice representations.
+      // this code really flies.
+
+      fe=A[aboff+3][0];fa=A[aboff+0][2];fb=A[aboff+5][1];fc=A[aboff+6][3];fd=A[aboff+8][0];
+/* 1000 1110  1110 0001   : lev  7: */ //tmp0=( fa^( fb^( ( ( ( fa|fb )^fc )|( fc^fd ) )^ALL_ONES ) ) );
+/* 1110 0010  0011 0011   : lev  6: */ //tmp1=( ( fa|fb )^( ( fc&( fa|( fb^fd ) ) )^ALL_ONES ) );
+/* 0011 0110  1000 1101   : lev  5: */ //tmp2=( fa^( ( fb&fd )^( ( fa&fd )|fc ) ) );
+/* 0101 0101  1001 0011   : lev  5: */ //tmp3=( ( fa&fc )^( fa^( ( fa&fb )|fd ) ) );
+/* 1000 1110  1110 0001   : lev  7: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFOR(FFXOR(FFOR(fa,fb),fc),FFXOR(fc,fd)),FF1())));
+/* 1110 0010  0011 0011   : lev  6: */ tmp1=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fa,FFXOR(fb,fd))),FF1()));
+/* 0011 0110  1000 1101   : lev  5: */ tmp2=FFXOR(fa,FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),fc)));
+/* 0101 0101  1001 0011   : lev  5: */ tmp3=FFXOR(FFAND(fa,fc),FFXOR(fa,FFOR(FFAND(fa,fb),fd)));
+      s1a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s1b=FFXOR(tmp2,FFAND(fe,tmp3));
+//dump_mem("s1as1b-fe",&fe,BYPG,BYPG);
+//dump_mem("s1as1b-fa",&fa,BYPG,BYPG);
+//dump_mem("s1as1b-fb",&fb,BYPG,BYPG);
+//dump_mem("s1as1b-fc",&fc,BYPG,BYPG);
+//dump_mem("s1as1b-fd",&fd,BYPG,BYPG);
+
+      fe=A[aboff+1][1];fa=A[aboff+2][2];fb=A[aboff+5][3];fc=A[aboff+6][0];fd=A[aboff+8][1];
+/* 1001 1110  0110 0001   : lev  6: */ //tmp0=( fa^( ( fb&( fc|fd ) )^( fc^( fd^ALL_ONES ) ) ) );
+/* 0000 0011  0111 1011   : lev  5: */ //tmp1=( ( fa&( fb^fd ) )|( ( fa|fb )&fc ) );
+/* 1100 0110  1101 0010   : lev  6: */ //tmp2=( ( fb&fd )^( ( fa&fd )|( fb^( fc^ALL_ONES ) ) ) );
+/* 0001 1110  1111 0101   : lev  5: */ //tmp3=( ( fa&fd )|( fa^( fb^( fc&fd ) ) ) );
+/* 1001 1110  0110 0001   : lev  6: */ tmp0=FFXOR(fa,FFXOR(FFAND(fb,FFOR(fc,fd)),FFXOR(fc,FFXOR(fd,FF1()))));
+/* 0000 0011  0111 1011   : lev  5: */ tmp1=FFOR(FFAND(fa,FFXOR(fb,fd)),FFAND(FFOR(fa,fb),fc));
+/* 1100 0110  1101 0010   : lev  6: */ tmp2=FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),FFXOR(fb,FFXOR(fc,FF1()))));
+/* 0001 1110  1111 0101   : lev  5: */ tmp3=FFOR(FFAND(fa,fd),FFXOR(fa,FFXOR(fb,FFAND(fc,fd))));
+      s2a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s2b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=A[aboff+0][3];fa=A[aboff+1][0];fb=A[aboff+4][1];fc=A[aboff+4][3];fd=A[aboff+5][2];
+/* 0100 1011  1001 0110   : lev  5: */ //tmp0=( fa^( fb^( ( fc&( fa|fd ) )^fd ) ) );
+/* 1101 0101  1000 1100   : lev  7: */ //tmp1=( ( fa&fc )^( ( fa^fd )|( ( fb|fc )^( fd^ALL_ONES ) ) ) );
+/* 0010 0111  1101 1000   : lev  4: */ //tmp2=( fa^( ( ( fb^fc )&fd )^fc ) );
+/* 1111 1111  1111 1111   : lev  0: */ //tmp3=ALL_ONES;
+/* 0100 1011  1001 0110   : lev  5: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFAND(fc,FFOR(fa,fd)),fd)));
+/* 1101 0101  1000 1100   : lev  7: */ tmp1=FFXOR(FFAND(fa,fc),FFOR(FFXOR(fa,fd),FFXOR(FFOR(fb,fc),FFXOR(fd,FF1()))));
+/* 0010 0111  1101 1000   : lev  4: */ tmp2=FFXOR(fa,FFXOR(FFAND(FFXOR(fb,fc),fd),fc));
+/* 1111 1111  1111 1111   : lev  0: */ tmp3=FF1();
+      s3a=FFXOR(tmp0,FFAND(FFNOT(fe),tmp1));
+      s3b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=A[aboff+2][3];fa=A[aboff+0][1];fb=A[aboff+1][3];fc=A[aboff+3][2];fd=A[aboff+7][0];
+/* 1011 0101  0100 1001   : lev  7: */ //tmp0=( fa^( ( fc&( fa^fd ) )|( fb^( fc|( fd^ALL_ONES ) ) ) ) );
+/* 0010 1101  0110 0110   : lev  6: */ //tmp1=( ( fa&fb )^( fb^( ( ( fa|fc )&fd )^fc ) ) );
+/* 0110 0111  1101 0000   : lev  7: */ //tmp2=( fa^( ( fb&fc )|( ( ( fa&( fb^fd ) )|fc )^fd ) ) );
+/* 1111 1111  1111 1111   : lev  0: */ //tmp3=ALL_ONES;
+/* 1011 0101  0100 1001   : lev  7: */ tmp0=FFXOR(fa,FFOR(FFAND(fc,FFXOR(fa,fd)),FFXOR(fb,FFOR(fc,FFXOR(fd,FF1())))));
+/* 0010 1101  0110 0110   : lev  6: */ tmp1=FFXOR(FFAND(fa,fb),FFXOR(fb,FFXOR(FFAND(FFOR(fa,fc),fd),fc)));
+/* 0110 0111  1101 0000   : lev  7: */ tmp2=FFXOR(fa,FFOR(FFAND(fb,fc),FFXOR(FFOR(FFAND(fa,FFXOR(fb,fd)),fc),fd)));
+/* 1111 1111  1111 1111   : lev  0: */ tmp3=FF1();
+      s4a=FFXOR(tmp0,FFAND(fe,FFXOR(tmp1,tmp0)));
+      s4b=FFXOR(FFXOR(s4a,tmp2),FFAND(fe,tmp3));
+
+      fe=A[aboff+4][2];fa=A[aboff+3][3];fb=A[aboff+5][0];fc=A[aboff+7][1];fd=A[aboff+8][2];
+/* 1000 1111  0011 0010   : lev  7: */ //tmp0=( ( ( fa&( fb|fc ) )^fb )|( ( ( fa^fc )|fd )^ALL_ONES ) );
+/* 0110 1011  0000 1011   : lev  6: */ //tmp1=( fb^( ( fc^fd )&( fc^( fb|( fa^fd ) ) ) ) );
+/* 0001 1010  0111 1001   : lev  6: */ //tmp2=( ( fa&fc )^( fb^( ( fb|( fa^fc ) )&fd ) ) );
+/* 0101 1101  1101 0101   : lev  4: */ //tmp3=( ( ( fa^fb )&( fc^ALL_ONES ) )|fd );
+/* 1000 1111  0011 0010   : lev  7: */ tmp0=FFOR(FFXOR(FFAND(fa,FFOR(fb,fc)),fb),FFXOR(FFOR(FFXOR(fa,fc),fd),FF1()));
+/* 0110 1011  0000 1011   : lev  6: */ tmp1=FFXOR(fb,FFAND(FFXOR(fc,fd),FFXOR(fc,FFOR(fb,FFXOR(fa,fd)))));
+/* 0001 1010  0111 1001   : lev  6: */ tmp2=FFXOR(FFAND(fa,fc),FFXOR(fb,FFAND(FFOR(fb,FFXOR(fa,fc)),fd)));
+/* 0101 1101  1101 0101   : lev  4: */ tmp3=FFOR(FFAND(FFXOR(fa,fb),FFXOR(fc,FF1())),fd);
+      s5a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s5b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=A[aboff+2][1];fa=A[aboff+3][1];fb=A[aboff+4][0];fc=A[aboff+6][2];fd=A[aboff+8][3];
+/* 0011 0110  0010 1101   : lev  6: */ //tmp0=( ( ( fa&fc )&fd )^( ( fb&( fa|fd ) )^fc ) );
+/* 1110 1110  1011 1011   : lev  3: */ //tmp1=( ( ( fa^fc )&fd )^ALL_ONES );
+/* 0101 1000  0110 0111   : lev  6: */ //tmp2=( ( fa&( fb|fc ) )^( fb^( ( fb&fc )|fd ) ) );
+/* 0001 0011  0000 0001   : lev  5: */ //tmp3=( fc&( ( fa&( fb^fd ) )^( fb|fd ) ) );
+/* 0011 0110  0010 1101   : lev  6: */ tmp0=FFXOR(FFAND(FFAND(fa,fc),fd),FFXOR(FFAND(fb,FFOR(fa,fd)),fc));
+/* 1110 1110  1011 1011   : lev  3: */ tmp1=FFXOR(FFAND(FFXOR(fa,fc),fd),FF1());
+/* 0101 1000  0110 0111   : lev  6: */ tmp2=FFXOR(FFAND(fa,FFOR(fb,fc)),FFXOR(fb,FFOR(FFAND(fb,fc),fd)));
+/* 0001 0011  0000 0001   : lev  5: */ tmp3=FFAND(fc,FFXOR(FFAND(fa,FFXOR(fb,fd)),FFOR(fb,fd)));
+      s6a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s6b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+      fe=A[aboff+1][2];fa=A[aboff+2][0];fb=A[aboff+6][1];fc=A[aboff+7][2];fd=A[aboff+7][3];
+/* 0111 1000  1001 0110   : lev  5: */ //tmp0=( fb^( ( fc&fd )|( fa^( fc^fd ) ) ) );
+/* 0100 1001  0101 1011   : lev  6: */ //tmp1=( ( fb|fd )&( ( fa&fc )|( fb^( fc^fd ) ) ) );
+/* 0100 1001  1011 1001   : lev  5: */ //tmp2=( ( fa|fb )^( ( fc&( fb|fd ) )^fd ) );
+/* 1111 1111  1101 1101   : lev  3: */ //tmp3=( fd|( ( fa&fc )^ALL_ONES ) );
+/* 0111 1000  1001 0110   : lev  5: */ tmp0=FFXOR(fb,FFOR(FFAND(fc,fd),FFXOR(fa,FFXOR(fc,fd))));
+/* 0100 1001  0101 1011   : lev  6: */ tmp1=FFAND(FFOR(fb,fd),FFOR(FFAND(fa,fc),FFXOR(fb,FFXOR(fc,fd))));
+/* 0100 1001  1011 1001   : lev  5: */ tmp2=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fb,fd)),fd));
+/* 1111 1111  1101 1101   : lev  3: */ tmp3=FFOR(fd,FFXOR(FFAND(fa,fc),FF1()));
+      s7a=FFXOR(tmp0,FFAND(fe,tmp1));
+      s7b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+
+/*
+      we have just done this:
+      
+      int sbox1[0x20] = {2,0,1,1,2,3,3,0, 3,2,2,0,1,1,0,3, 0,3,3,0,2,2,1,1, 2,2,0,3,1,1,3,0};
+      int sbox2[0x20] = {3,1,0,2,2,3,3,0, 1,3,2,1,0,0,1,2, 3,1,0,3,3,2,0,2, 0,0,1,2,2,1,3,1};
+      int sbox3[0x20] = {2,0,1,2,2,3,3,1, 1,1,0,3,3,0,2,0, 1,3,0,1,3,0,2,2, 2,0,1,2,0,3,3,1};
+      int sbox4[0x20] = {3,1,2,3,0,2,1,2, 1,2,0,1,3,0,0,3, 1,0,3,1,2,3,0,3, 0,3,2,0,1,2,2,1};
+      int sbox5[0x20] = {2,0,0,1,3,2,3,2, 0,1,3,3,1,0,2,1, 2,3,2,0,0,3,1,1, 1,0,3,2,3,1,0,2};
+      int sbox6[0x20] = {0,1,2,3,1,2,2,0, 0,1,3,0,2,3,1,3, 2,3,0,2,3,0,1,1, 2,1,1,2,0,3,3,0};
+      int sbox7[0x20] = {0,3,2,2,3,0,0,1, 3,0,1,3,1,2,2,1, 1,0,3,3,0,1,1,2, 2,3,1,0,2,3,0,2};
+
+      s12 = sbox1[ (((A3>>0)&1)<<4) | (((A0>>2)&1)<<3) | (((A5>>1)&1)<<2) | (((A6>>3)&1)<<1) | (((A8>>0)&1)<<0) ]
+           |sbox2[ (((A1>>1)&1)<<4) | (((A2>>2)&1)<<3) | (((A5>>3)&1)<<2) | (((A6>>0)&1)<<1) | (((A8>>1)&1)<<0) ];
+      s34 = sbox3[ (((A0>>3)&1)<<4) | (((A1>>0)&1)<<3) | (((A4>>1)&1)<<2) | (((A4>>3)&1)<<1) | (((A5>>2)&1)<<0) ]
+           |sbox4[ (((A2>>3)&1)<<4) | (((A0>>1)&1)<<3) | (((A1>>3)&1)<<2) | (((A3>>2)&1)<<1) | (((A7>>0)&1)<<0) ];
+      s56 = sbox5[ (((A4>>2)&1)<<4) | (((A3>>3)&1)<<3) | (((A5>>0)&1)<<2) | (((A7>>1)&1)<<1) | (((A8>>2)&1)<<0) ]
+           |sbox6[ (((A2>>1)&1)<<4) | (((A3>>1)&1)<<3) | (((A4>>0)&1)<<2) | (((A6>>2)&1)<<1) | (((A8>>3)&1)<<0) ];
+      s7 =  sbox7[ (((A1>>2)&1)<<4) | (((A2>>0)&1)<<3) | (((A6>>1)&1)<<2) | (((A7>>2)&1)<<1) | (((A7>>3)&1)<<0) ];
+*/
+
+      // use 4x4 xor to produce extra nibble for T3
+
+      extra_B[3]=FFXOR(FFXOR(FFXOR(B[aboff+2][0],B[aboff+5][1]),B[aboff+6][2]),B[aboff+8][3]);
+      extra_B[2]=FFXOR(FFXOR(FFXOR(B[aboff+5][0],B[aboff+7][1]),B[aboff+2][3]),B[aboff+3][2]);
+      extra_B[1]=FFXOR(FFXOR(FFXOR(B[aboff+4][3],B[aboff+7][2]),B[aboff+3][0]),B[aboff+4][1]);
+      extra_B[0]=FFXOR(FFXOR(FFXOR(B[aboff+8][2],B[aboff+5][3]),B[aboff+2][1]),B[aboff+7][0]);
+
+      // T1 = xor all inputs
+      // in1, in2, D are only used in T1 during initialisation, not generation
+      for(b=0;b<4;b++){
+        A[aboff-1][b]=FFXOR(A[aboff+9][b],X[b]);
+      }
+
+#ifdef STREAM_INIT
+      for(b=0;b<4;b++){
+        A[aboff-1][b]=FFXOR(FFXOR(A[aboff-1][b],D[b]),((j % 2) ? in2[b] : in1[b]));
+      }
+#endif
+
+
+      // T2 =  xor all inputs
+      // in1, in2 are only used in T1 during initialisation, not generation
+      // if p=0, use this, if p=1, rotate the result left
+      for(b=0;b<4;b++){
+        B[aboff-1][b]=FFXOR(FFXOR(B[aboff+6][b],B[aboff+9][b]),Y[b]);
+      }
+
+#ifdef STREAM_INIT
+      for(b=0;b<4;b++){
+        B[aboff-1][b]=FFXOR(B[aboff-1][b],((j % 2) ? in1[b] : in2[b]));
+      }
+#endif
+
+
+      // if p=1, rotate left (yes, this is what we're doing)
+      tmp3=B[aboff-1][3];
+      B[aboff-1][3]=FFXOR(B[aboff-1][3],FFAND(FFXOR(B[aboff-1][3],B[aboff-1][2]),p));
+      B[aboff-1][2]=FFXOR(B[aboff-1][2],FFAND(FFXOR(B[aboff-1][2],B[aboff-1][1]),p));
+      B[aboff-1][1]=FFXOR(B[aboff-1][1],FFAND(FFXOR(B[aboff-1][1],B[aboff-1][0]),p));
+      B[aboff-1][0]=FFXOR(B[aboff-1][0],FFAND(FFXOR(B[aboff-1][0],tmp3),p));
+
+
+      // T3 = xor all inputs
+      for(b=0;b<4;b++){
+        D[b]=FFXOR(FFXOR(E[b],Z[b]),extra_B[b]);
+      }
+
+
+      // T4 = sum, carry of Z + E + r
+      for(b=0;b<4;b++){
+        next_E[b]=F[b];
+      }
+
+      tmp0=FFXOR(Z[0],E[0]);
+      tmp1=FFAND(Z[0],E[0]);
+      F[0]=FFXOR(E[0],FFAND(q,FFXOR(Z[0],r)));
+      tmp3=FFAND(tmp0,r);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(Z[1],E[1]);
+      tmp1=FFAND(Z[1],E[1]);
+      F[1]=FFXOR(E[1],FFAND(q,FFXOR(Z[1],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(Z[2],E[2]);
+      tmp1=FFAND(Z[2],E[2]);
+      F[2]=FFXOR(E[2],FFAND(q,FFXOR(Z[2],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      tmp4=FFOR(tmp1,tmp3);
+
+      tmp0=FFXOR(Z[3],E[3]);
+      tmp1=FFAND(Z[3],E[3]);
+      F[3]=FFXOR(E[3],FFAND(q,FFXOR(Z[3],tmp4)));
+      tmp3=FFAND(tmp0,tmp4);
+      r=FFXOR(r,FFAND(q,FFXOR(FFOR(tmp1,tmp3),r))); // ultimate carry
+
+/*
+      we have just done this: (believe it or not)
+      
+      if (q) {
+        F = Z + E + r;
+        r = (F >> 4) & 1;
+        F = F & 0x0f;
+      }
+      else {
+          F = E;
+      }
+*/
+      for(b=0;b<4;b++){
+        E[b]=next_E[b];
+      }
+
+      // this simple instruction is virtually shifting all the shift registers
+      aboff--;
+
+/*
+      we've just done this:
+
+      A9=A8;A8=A7;A7=A6;A6=A5;A5=A4;A4=A3;A3=A2;A2=A1;A1=A0;A0=next_A0;
+      B9=B8;B8=B7;B7=B6;B6=B5;B5=B4;B4=B3;B3=B2;B2=B1;B1=B0;B0=next_B0;
+*/
+
+      X[0]=s1a;
+      X[1]=s2a;
+      X[2]=s3b;
+      X[3]=s4b;
+      Y[0]=s3a;
+      Y[1]=s4a;
+      Y[2]=s5b;
+      Y[3]=s6b;
+      Z[0]=s5a;
+      Z[1]=s6a;
+      Z[2]=s1b;
+      Z[3]=s2b;
+      p=s7a;
+      q=s7b;
+
+#ifdef STREAM_NORMAL
+      // require 4 loops per output byte
+      // 2 output bits are a function of the 4 bits of D
+      // xor 2 by 2
+      cb_g[8*i+7-2*j]=FFXOR(D[2],D[3]);
+      cb_g[8*i+6-2*j]=FFXOR(D[0],D[1]);
+#endif
+
+
+    } // INTERNAL LOOP
+
+
+  } // EXTERNAL LOOP
+
+  // move 32 steps forward, ready for next call
+  for(k=0;k<10;k++){
+    for(b=0;b<4;b++){
+      A[32+k][b]=A[k][b];
+      B[32+k][b]=B[k][b];
+    }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef STREAM_NORMAL
+
+#if GROUP_PARALLELISM==32
+trasp64_32_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88cw(cb);
+#endif
+
+#endif
+
+#ifdef STREAM_INIT
+#endif
+#ifdef STREAM_NORMAL
+#endif
+
+}
+
diff --git a/contrib/sasc-ng/FFdecsa/vdr_patches/README_vdr.txt b/contrib/sasc-ng/FFdecsa/vdr_patches/README_vdr.txt
new file mode 100644 (file)
index 0000000..389d05c
--- /dev/null
@@ -0,0 +1,58 @@
+-------
+FFdecsa
+-------
+
+This directory contains patches to use FFdecsa with vdr, by means of a
+new FFdecsa-based SoftCSA.
+
+You don't need a SoftCSA patch!!!
+
+Step by step instructions:
+
+- create a directory somewhere, we will call this dir $BASE
+
+- download vdr-1.3.11.tar.bz2 and put it in $BASE
+
+- download vdr-sc-0.3.15.tar.gz and put it in $BASE
+
+- download FFdecsa-1.0.0.tar.bz2 and put it in $BASE
+
+- cd $BASE
+
+- tar xvjf vdr-1.3.11.tar.bz2
+
+- cd vdr-1.3.11/PLUGINS/src/
+
+- tar xvzf ../../../vdr-sc-0.3.15.tar.gz
+
+- ln -s sc-0.3.15 sc
+
+- cd $BASE/vdr-1.3.11
+
+- tar xvjf ../FFdecsa-1.0.0.tar.bz2
+
+- ln -s FFdecsa-1.0.0 FFdecsa
+
+- patch -p1 <PLUGINS/src/sc-0.3.15/patches/vdr-1.3.10-sc.diff
+
+- patch -p1 <FFdecsa/vdr_patches/vdr-1.3.11-FFdecsa.diff
+
+- cd FFdecsa
+
+- optional: edit Makefile
+
+- make
+
+- ./FFdecsa_test
+
+- cd $BASE/vdr-1.3.11
+
+- cp Make.config.template Make.config
+
+- optional: edit Make.config
+
+- make
+
+- make plugins
+
+Good luck!
diff --git a/contrib/sasc-ng/Makefile b/contrib/sasc-ng/Makefile
new file mode 100644 (file)
index 0000000..a9835d8
--- /dev/null
@@ -0,0 +1,171 @@
+VERSION = 0.0.2
+TOOL = sasc-ng
+SCVER = sc-src
+
+include config.mak
+
+#DEFINES = -DNO_RINGBUF
+
+CC       ?= gcc
+CXX      ?= g++
+CXXFLAGS ?= -Wall -D__user= -Werror 
+CFLAGS   ?= -Wall -D__user= 
+
+ifdef DVB_DIR
+  INCLUDES = -I$(DVB_DIR)/include
+  DVB_MOD_DIR = DVB_DIR=$(DVB_DIR)
+endif
+
+DEFINES += -DRELEASE_VERSION=\"$(VERSION)\"
+INCLUDES += -Idvbloopback/module -I/lib/modules/$(shell uname -r)/build/include
+LBDIR = dvbloopback/src
+SCDIR = ./sc/PLUGINS/src/$(SCVER)
+SCSYSLIBS := $(foreach ob,$(shell ls -A -I ".*" $(SCDIR)/systems), -lsc-$(ob))
+SC_FLAGS = -O2 -fPIC -Wall -Woverloaded-virtual
+
+ifdef AUXSERVER_OPTS
+  DEFINES += ${AUXSERVER_OPTS}
+endif
+
+ifndef RELEASE
+  CXXFLAGS += -g
+  CFLAGS   += -g
+  SC_FLAGS += -g
+endif
+
+ifdef USE_DLOAD
+  SCLIBS = -Lsc/PLUGINS/lib `find sc/PLUGINS/lib/ -name "*.so" \
+           -exec basename {} \;|cut -d. -f1|sed -e 's/^lib//'|xargs -n 1 -i echo "-l{}"`
+else
+  SCLIBS = -Wl,-whole-archive ./sc/PLUGINS/lib/libsc-*.a -Wl,-no-whole-archive \
+       ./sc/PLUGINS/lib/libvdr-sc.a
+endif
+
+RELDIR = $(TOOL)-$(VERSION)
+
+OBJ  := forward.o process_req.o msg_passing.o plugin_getsid.o plugin_ringbuf.o\
+       plugin_showioctl.o plugin_legacysw.o plugin_dss.o plugin_cam.o \
+       plugin_ffdecsa.o plugin_scan.o version.o
+
+OBJ_SC := misc.o dvbdevice.o osdbase.o menuitems.o device.o thread.o \
+       tools.o sasccam.o log.o vdrcompat.o libsi.a
+ifdef USE_DLOAD
+  OBJ_SC += dload.o
+endif
+
+OBJS := $(foreach ob,$(OBJ) $(OBJ_SC), objs/$(ob)) FFdecsa/FFdecsa.o
+INCLUDES_SC := -I$(SCDIR) -I./sc/include
+
+INCLUDES_SI := -Isc/include/libsi
+OBJ_LIBSI := objs/si_descriptor.o objs/si_section.o objs/si_si.o objs/si_util.o
+
+INC_DEPS := $(shell ls $(LBDIR)/*.h) dvbloopback/module/dvbloopback.h
+INC_DEPS_LB := $(shell ls dvblb_plugins/*.h)
+
+LIBS = -lpthread -lcrypto -lcrypt
+
+all: $(TOOL) libscanwrap.so
+
+$(TOOL): $(OBJS) sc-plugin
+       $(CXX) $(CFLAGS) -o $(TOOL) $(SCLIBS) $(OBJS) $(LIBS)
+ifdef RELEASE
+       $(MAKE) strip-sasc
+endif
+
+libscanwrap.so: dvblb_plugins/scanwrap.c
+       $(CC) -fPIC -g -O2 -Wall -I. -nostdlib -shared -o $@ $< -ldl  -lc
+
+clean:
+       @$(MAKE) -C $(SCDIR) SASC=1 clean
+       @-rm -f ./sc/PLUGINS/lib/*.so*
+       @-rm -f ./sc/config.h
+       @-rm -f $(OBJS)
+       @-rm -f $(OBJ_LIBSI)
+       @-rm -f objs/version.cpp
+       @-rm -f objs/dload.o
+       @-rm -f $(TOOL)
+       @$(MAKE) -C scripts clean
+
+module_clean:
+       cd dvbloopback/module && $(MAKE) clean
+
+utils:
+       @$(MAKE) -C scripts
+
+sc-plugin:
+       @-rm -f sc/config.h
+       @ln -s include/vdr/config.h sc/config.h
+       @if [ ! -d sc/PLUGINS/lib ]; then mkdir sc/PLUGINS/lib; fi
+ifdef USE_DLOAD
+       $(MAKE) -C $(SCDIR) $(SCOPTS) CXX=$(CXX) CXXFLAGS="$(SC_FLAGS)" SASC=1 all
+       $(MAKE) link-shared
+ifdef RELEASE
+       $(MAKE) strip-sc
+endif 
+else
+       $(MAKE) -C $(SCDIR) $(SCOPTS) CXX=$(CXX) CXXFLAGS="$(SC_FLAGS)" SASC=1 STATIC=1 all
+endif
+
+FFdecsa/FFdecsa.o:
+        $(MAKE) -C FFdecsa $(FFDECSA_OPTS)
+
+module:
+       cd dvbloopback/module && $(MAKE) $(DVB_MOD_DIR)
+       @cp -f dvbloopback/module/dvbloopback.ko .
+
+release:
+       rm -rf $(RELDIR)
+       mkdir $(RELDIR)
+       @mkdir $(RELDIR)/objs
+       @mkdir $(RELDIR)/sc_files
+       @cp Makefile $(RELDIR)/Makefile
+       @ln -s ../dvbloopback  $(RELDIR)/dvbloopback
+       @ln -s ../sc $(RELDIR)/sc
+       @ln -s ../dvblb_plugins $(RELDIR)/dvblb_plugins
+       @ln -s ../FFdecsa $(RELDIR)/FFdecsa
+       tar --exclude="*.o" --exclude ".svn" --numeric-owner -h -cvzf $(RELDIR).tgz $(RELDIR)
+
+link-shared:
+       @cd ./sc/PLUGINS/lib; \
+       for i in *.so.*; do \
+               link=`echo $$i|cut -d. -f-2`; \
+               if [ ! -e $$link ]; then \
+                       ln -s $$i $$link; \
+               fi \
+       done
+
+strip-sc:
+       @cd ./sc/PLUGINS/lib; \
+       for i in *.so.*; do \
+               strip $$i; \
+       done
+
+strip-sasc:
+       @strip sasc-ng
+
+objs/libsi.a: $(OBJ_LIBSI)
+       ar ru $@ $(OBJ_LIBSI)
+
+objs/%.o: $(LBDIR)/%.c $(INC_DEPS)
+       $(CXX) $(CXXFLAGS) -o $@ -c  $(DEFINES) -I$(LBDIR) $(INCLUDES) $<
+
+objs/%.o: dvblb_plugins/%.c $(INC_DEPS) $(INC_DEPS_LB)
+       $(CXX) $(CXXFLAGS) -o $@ -c  $(DEFINES) -I$(LBDIR) $(INCLUDES) $<
+
+objs/%.o: sc/%.cpp
+       $(CXX) $(CXXFLAGS) -o $@ -c  $(DEFINES) $(INCLUDES_SC) $(INCLUDES) $<
+
+objs/si_%.o: sc/libsi/%.c
+       $(CXX) $(CXXFLAGS) -o $@ -c  $(DEFINES) $(INCLUDES_SI) $<
+
+objs/version.o: objs/version.cpp
+       $(CXX) $(CXXFLAGS) -o $@ -c $(DEFINES) $<
+
+objs/version.cpp: FORCE
+ifeq ($(shell test -d .hg && echo -n 1), 1)
+       echo 'const char *source_version =' '"'`(hg identify 2>/dev/null || echo -n Unknown) | sed -e 's/ .*//'`'";' > .vers.new ; diff .vers.new $@ > .vers.diff 2>&1 ; if test -s .vers.diff ; then mv -f .vers.new $@ ; fi ; rm -f .vers.new .vers.diff
+else
+       echo 'const char *source_version =' '"'`(svnversion $(shell pwd) 2>/dev/null) || echo Unknown`/`(svnversion $(shell pwd)/dvbloopback 2>/dev/null) || echo Unknown`'";' > .vers.new ; diff .vers.new $@ > .vers.diff 2>&1 ; if test -s .vers.diff ; then mv -f .vers.new $@ ; fi ; rm -f .vers.new .vers.diff
+endif
+
+FORCE:
diff --git a/contrib/sasc-ng/configure b/contrib/sasc-ng/configure
new file mode 100644 (file)
index 0000000..ca54092
--- /dev/null
@@ -0,0 +1,230 @@
+#!/bin/sh
+
+die_unknown(){
+    echo "Unknown option \"$1\"."
+    echo "See $0 --help for available options."
+    exit 1
+}
+
+#check whether option is supported by this cpu
+check_cpu(){
+    ok=0
+    grep "^flags.* $1 " /proc/cpuinfo >/dev/null 2>&1
+    if test $? -eq 0; then
+      ok=1
+    else
+      grep "^flags.* $1\$" /proc/cpuinfo >/dev/null 2>&1
+      if test $? -eq 0; then
+        ok=1
+      fi
+    fi
+    eval test $ok -eq 1
+}
+
+#try to figure out best FFdecsa compiler options
+get_cpu_optimization() {
+   FLAGS="FLAGS=-O3 -fexpensive-optimizations -funroll-loops"
+   arch=`uname -m`
+   processors_flags=`cat /proc/cpuinfo | grep "flags" | head -n 1`
+   vendor=`cat /proc/cpuinfo |grep "vendor_id" | head -n 1 | sed -e "s/.*:\W*//"`
+   gcc_ver=`$CXX -v 2>&1 | grep "gcc version" | head -n 1`
+   gcc_major=`echo $gcc_ver | sed -e 's/^gcc version \([0-9]*\)\..*/\1/'`
+   gcc_minor=`echo $gcc_ver | sed -e 's/^gcc version [0-9]*\.\([0-9]*\).*/\1/'`
+   if test $gcc_major -gt 4; then
+     ARCH="native"
+   elif test $gcc_major -eq 4 && test $gcc_minor -ge 2; then
+     ARCH="native"
+   elif test "x$arch" = "xx86_64"; then
+     if test "x$vendor" = "xAuthenticAMD"; then
+        ARCH="k8"
+     elif test "x$vendor" = "xGenuineIntel"; then
+        ARCH="nocona"
+     else
+        echo **WARNING** - Unknown vendor $vendor - assuming nocona
+        ARCH="nocona"
+     fi
+     FLAGS="$FLAGS -finline-limit=6000000 --param max-unrolled-insns=500"
+   elif test "x$arch" = "xathlon-xp"; then
+     ARCH="athlon-xp"
+     FLAGS="$FLAGS -finline-limit=6000000 --param max-unrolled-insns=500"
+   else
+     ARCH="pentium"
+   fi
+   OPTS=""
+   for opt in mmx sse sse2; do
+     if check_cpu $opt; then
+       OPTS="$OPTS $opt"
+       FLAGS="$FLAGS -m$opt"
+     fi
+   done
+   FLAGS="$FLAGS -march=$ARCH"
+   echo "Processor capabilities: $ARCH ($OPTS )"
+}
+
+show_help() {
+  echo "Usage: configure [options]"
+  echo "Options: [defaults in brackets after descriptions]"
+  echo
+  echo "Standard options:"
+  echo "  --help                   print this message"
+  echo "  --compiletype=<type>     specify compile type of release or debug,"
+  echo "                           default is debug"
+  echo "  --shared                 compile sc shared libs instaed of static"
+  echo "  --dvb-dir=<path>         use <path> for DVB headers"
+  echo "  --auxserver              use auxserver with localhost:7777:auxserver"
+  echo "  --auxserver=<host:port:password>"
+  echo "                           use auxserver with given parms"
+  echo "  --optimize=<opts>        set FFDecsa optimiation detection"
+  echo "                           yes: Try most common optimizations (default)"
+  echo "                           long: Try all known optimizations"
+  echo "                           no: Don't do any optimizations"
+  echo "  --ffdecsa_mode=<val>     use <val> optimization"
+  echo "  -cxx=<c++ compiler>      command for C++ compilation (default: g++)"
+  exit 0
+}
+MAX_MODE=PARALLEL_32_INT
+ffdecsa_opt="yes"
+compiletype_opt="debug"
+for opt do
+  optval="${opt#*=}"
+  case "$opt" in
+  --dvb-dir=*) dvb_path=`eval echo $optval`
+  ;;
+  --cxx=*) CXX="$optval"
+  ;;
+  --ffdecsa_mode=*) ffdecsa_opt="no"; MAX_MODE="$optval"
+  ;;
+  --ffdecsa_flags=*) ffdecsa_flags="$optval"
+  ;;
+  --optimize=*) ffdecsa_opt="$optval"
+  ;;
+  --auxserver) auxserver_opt="localhost:7777:auxserver"
+  ;;
+  --auxserver=*) auxserver_opt="$optval"
+  ;;
+  --compiletype=*) compiletype_opt="$optval"
+  ;;
+  --shared) shared_opt="1"
+  ;;
+  --help) show_help
+  ;;
+  *)
+  die_unknown $opt
+  ;;
+  esac
+done
+
+if test "x$CXX" = "x"; then
+  CXX=g++
+fi
+echo "Using C++ compiler: $CXX"
+
+if test "x$ffdecsa_opt" = "xlong"; then
+  FFDECSA_MODES="PARALLEL_32_INT PARALLEL_32_4CHAR PARALLEL_32_4CHARA \
+                 PARALLEL_64_8CHAR PARALLEL_64_8CHARA PARALLEL_64_2INT \
+                 PARALLEL_64_LONG PARALLEL_64_MMX PARALLEL_128_16CHAR \
+                 PARALLEL_128_16CHARA PARALLEL_128_4INT PARALLEL_128_2LONG \
+                 PARALLEL_128_2MMX PARALLEL_128_SSE PARALLEL_128_SSE2"
+elif test "x$ffdecsa_opt" = "xyes"; then
+  FFDECSA_MODES="PARALLEL_32_INT PARALLEL_64_2INT PARALLEL_64_LONG \
+                 PARALLEL_64_MMX PARALLEL_128_2LONG PARALLEL_128_2MMX \
+                 PARALLEL_128_SSE PARALLEL_128_SSE2"
+elif test "x$ffdecsa_opt" != "xno"; then
+  echo "Bad option to --optimize '$ffdecsa_opt'.  Should be 'yes, no, long'"
+  exit 1
+fi
+if test "x${TMPDIR}" = "x"; then
+  TMPDIR="/tmp"
+fi
+
+echo "# Automatically generated by configure - do not modify" > config.mak
+
+if test "x$compiletype_opt" = "xrelease"; then
+  echo "Using compile type release"
+  echo "RELEASE=1" >> config.mak
+elif test "x$compiletype_opt" = "xdebug"; then
+  echo "Using compile type debug"
+else
+  echo "Bad option to --compiletype '$compiletype_opt'. Should be 'release, debug'"
+  exit 1
+fi
+
+if test "x$auxserver_opt" != "x"; then
+  echo $auxserver_opt | {
+    IFS=: read host port pass
+    echo "AUXSERVER_OPTS=-DAUXSERVER_HOST=\\\"$host\\\" -DAUXSERVER_PORT=\\\"$port\\\" -DAUXSERVER_PASSWD=\\\"$pass\\\" -DUSE_AUXSERVER" >> config.mak
+  }
+fi
+
+if test "x$shared_opt" = "x1"; then
+  echo "Compiling sc for shared libraries"
+  echo "USE_DLOAD=1" >> config.mak
+fi
+
+TMPDIR="${TMPDIR}/sasc-ng.${RANDOM}"
+mkdir ${TMPDIR}
+
+#Test FFDECSA compile
+MAX_val=0
+if test "x$ffdecsa_opt" != "xno"; then
+   if test "x$ffdecsa_flags" = "x"; then
+     if test -f /proc/cpuinfo; then
+       get_cpu_optimization
+     fi
+   else
+     FLAGS=$ffdecsa_flags
+   fi
+   TMPOUT="${TMPDIR}/FFdecsa/out"
+   mkdir "${TMPDIR}/FFdecsa"
+   cp FFdecsa/*.c FFdecsa/*.h FFdecsa/Makefile "${TMPDIR}/FFdecsa/"
+   echo "Trying various FFdecsa optimizations..."
+   for var in ${FFDECSA_MODES}; do
+     make -C "${TMPDIR}/FFdecsa" FFdecsa_test "PARALLEL_MODE=${var}" "${FLAGS}" "COMPILER=$CXX" >/dev/null 2>&1
+     if test $? -ne 0 ; then
+       echo "    ${var}: build failed"
+     else
+       rm -f ${TMPOUT}
+       sync;sleep 2; "${TMPDIR}/FFdecsa/FFdecsa_test" > /dev/null 2>"${TMPOUT}"
+       if test $? -ne 0; then
+         echo "    ${var}: test failed"
+       else
+         grep FAILED "${TMPOUT}" >/dev/null 2>&1
+         if test $? -ne 1; then
+           echo "    ${var}: test failed"
+         else
+           res=`grep "speed=.*Mbit" "${TMPOUT}" | sed -e 's/^.*=\([0-9]*\)\.[0-9]* Mbit.*$/\1/'`
+           echo "    ${var}: $res"
+           if test $res -gt $MAX_val; then
+             MAX_val=$res
+             MAX_MODE=$var
+           fi
+         fi
+       fi
+     fi
+     make -C "${TMPDIR}/FFdecsa" clean >/dev/null 2>&1
+   done
+   echo "Choosing PARALLEL_MODE = ${MAX_MODE}"
+   echo "FFDECSA_OPTS = \"$FLAGS\" PARALLEL_MODE=${MAX_MODE} COMPILER=$CXX" >> config.mak
+else
+   if test "x$ffdecsa_flags" != "x"; then
+     echo "FFDECSA_OPTS = \"$ffdecsa_flags\" PARALLEL_MODE=${MAX_MODE} COMPILER=$CXX" >> config.mak
+   elif test "x$MAX_MODE" != "xPARALLEL_32_INT"; then
+     echo "FFDECSA_OPTS = PARALLEL_MODE=${MAX_MODE} COMPILER=$CXX" >> config.mak
+   fi
+fi
+
+if test "x$dvb_path" != "x"; then
+  if test -e "${dvb_path}/include/linux/dvb/frontend.h"; then
+    echo "DVB_DIR=${dvb_path}" >> config.mak
+    echo "Using DVB_DIR: ${dvb_path}"
+  elif test -e "${dvb_path}/linux/include/linux/dvb/frontend.h"; then
+    echo "DVB_DIR=${dvb_path}/linux" >> config.mak
+    echo "Using DVB_DIR: ${dvb_path}/linux"
+  else
+    echo "Could not find DVB headers within $dvb_path"
+  fi
+fi
+rm -rf "${TMPDIR}"
+echo "CXX=$CXX" >> config.mak
+date >> config.log
+echo " $0 $*" >> config.log
diff --git a/contrib/sasc-ng/dvblb_plugins/plugin_cam.c b/contrib/sasc-ng/dvblb_plugins/plugin_cam.c
new file mode 100644 (file)
index 0000000..14ff64d
--- /dev/null
@@ -0,0 +1,704 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/dmx.h>
+
+#include "../sc/include/vdr/plugin.h"
+#include "../sc/include/vdr/dvbdevice.h"
+#include "../sc/include/vdr/sclink.h"
+#include "../sc/include/vdr/channels.h"
+#include "../sc/include/vdr/tools.h"
+#include "../sc/sasccam.h"
+// Hack to disable debug print control from sc
+
+#include "process_req.h"
+#include "plugin_getsid.h"
+#include "plugin_cam.h"
+#include "plugin_msg.h"
+
+#define PLUGIN_ID 28
+#define DBG_NAME "CAM"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+#define ll_find_elem(elem, lhead, item, value, type) {  \
+  struct list_head *lptr;                               \
+  type *ptr;                                            \
+  elem = NULL;                                          \
+  list_for_each(lptr, &lhead) {                         \
+    ptr = list_entry(lptr, type);                       \
+    if(ptr->item == value)                              \
+      elem = ptr;                                       \
+  }                                                     \
+}
+
+static int cam_opt = 0;
+static int opt_budget = 0;
+static int opt_fixcat = 1;
+static char opt_camdir[256] = {"./sc_files"};
+char * get_camdir() { return opt_camdir;}
+static char opt_extau[256];
+static struct option Cam_Opts[] = {
+  {"cam-budget", 0, &cam_opt, 'b'},
+  {"cam-dir", 1, &cam_opt, 'd'},
+  {"cam-nofixcat", 0, &cam_opt, 'f'},
+  {"cam-extau", 1, &cam_opt, 'e'},
+  {"cam-scopts", 1, &cam_opt, 'o'},
+  {0, 0, 0, 0},
+};
+
+struct cam_epid {
+  struct list_head list;
+  unsigned int epid;
+  unsigned char type;
+  unsigned int sid;
+  int delayclose;
+};
+
+LIST_HEAD(sclist);
+
+LIST_HEAD(pid_empty_queue);
+LIST_HEAD(pid_list);
+
+struct fdmap_list {
+  struct list_head list;
+  unsigned int pid;
+  int fd;
+};
+LIST_HEAD(fdmap_empty_queue);
+LIST_HEAD(fdmap_ll);
+
+struct scopt_list {
+  char cmd[32];
+  char value[256];
+};
+struct scopt_list scopts[32] = {{{'\0'}}};
+
+static cPlugin *sc = NULL;
+static char scCap[80];
+
+extern const char *externalAU;
+extern void update_keys(int, unsigned char, int, unsigned char *, int);
+extern void SetCAMPrint(const char *_plugin_name, unsigned int plugin_id, unsigned int _print_level, unsigned int *_log_level);
+const char *cPlugin::ConfigDirectory(const char *PluginName) {return opt_camdir;}
+
+int GetCaDescriptors(int Source, int Transponder, int ServiceId,
+                     const int *CaSystemIds, int BufSize, uchar *Data,
+                     bool &StreamFlag) {
+  cChannel *ch;
+  for(ch=Channels.First(); ch; ch=Channels.Next(ch)) {
+    if(ch->Sid() == ServiceId) {
+       return ch->GetPMTBuf(Data);
+    }
+  }
+//  printf("Transfering %d bytes\n", pmtlen);
+  return 0;
+}
+
+static int init_sc(void) {
+  sc=(cPlugin *)VDRPluginCreator();
+
+  dprintf0("initializing plugin: SoftCam (%s): %s\n", sc->Version(), sc->Description());
+#ifndef __x86_64__
+  //I have no idea why 64bit systems crash with the redirect
+  SetCAMPrint(DBG_NAME, PLUGIN_ID, 0, &_dbglvl);
+#endif
+  if (!sc->Initialize()) {
+    dprintf0("Failed to initialize sc\n");
+    return false;
+  }
+  dprintf0("starting plugin:\n");
+  if (!sc->Start()) {
+    dprintf0("Failed to start sc plugin\n");
+    return false;
+  }
+
+  memset(scCap, 0, 80);
+  sc->SetupParse("LoggerActive","2");
+#ifdef USE_AUXSERVER
+  sc->SetupParse("Nagra2.AuxServerEnable", "1");
+  #ifdef AUXSERVER_PORT
+  sc->SetupParse("Nagra2.AuxServerPort", AUXSERVER_PORT);
+  #endif
+  #ifdef AUXSERVER_HOST
+  sc->SetupParse("Nagra2.AuxServerAddr", AUXSERVER_HOST);
+  #endif
+  #ifdef AUXSERVER_PASSWD
+  sc->SetupParse("Nagra2.AuxServerPass", AUXSERVER_PASSWD);
+  #endif
+#endif
+  for(int i = 0; i < 32 && scopts[i].cmd[0] != '\0'; i++) {
+    dprintf0("Setting SC options: %s = %s\n", scopts[i].cmd, scopts[i].value);
+    sc->SetupParse(scopts[i].cmd, scopts[i].value);
+  }
+  return true;
+}
+
+static struct sc_data *find_sc_from_adpt(int adapter) {
+  struct sc_data *sc_data;
+  ll_find_elem(sc_data, sclist, real, adapter, struct sc_data);
+  return sc_data;
+}
+
+void cam_del_pid(struct sc_data *sc_data, struct cam_epid *cam_epid) {
+  //struct ScLink link;
+  struct list_head *ptr;
+  struct cam_epid *cam_epid1;
+  int epidlist[MAXDPIDS], *epidptr = epidlist;
+  list_del(&cam_epid->list);
+  list_for_each(ptr, &pid_list) {
+    cam_epid1 = list_entry(ptr, struct cam_epid);
+    if(cam_epid1->sid == cam_epid->sid)
+      *(epidptr++) = cam_epid1->epid;
+  }
+  *epidptr = 0;
+  sc_data->cam->AddPrg(cam_epid->sid,epidlist);
+  //PrepareScLink(&link, sc_data->dev, OP_DELPID);
+  //link.data.pids.pid=cam_epid->epid;
+  //link.data.pids.type=cam_epid->type;
+  //DoScLinkOp(sc, &link);
+  list_add(&cam_epid->list, &pid_empty_queue);
+}
+
+void _SetCaDescr(int adapter, ca_descr_t *ca_descr) {
+  struct sc_data *sc_data = find_sc_from_adpt(adapter);
+  unsigned long cadata;
+  if(!sc_data)
+    return;
+  dprintf1("Sending key(%d) %c %d %x%x...%x\n", sc_data->cafd,
+           (ca_descr->parity ? 'O' : 'E'), ca_descr->index,
+           ca_descr->cw[0], ca_descr->cw[1], ca_descr->cw[7]);
+  
+  cadata = (ca_descr->index << 1) | (ca_descr->parity==0 ? 0 : 1);
+  msg_send(MSG_LOW_PRIORITY, MSG_CAMUPDATE, sc_data->virt, (void *)cadata);
+  if(sc_data->cafd >= 0) {
+    //Using a real CA
+    ca_descr_t tmp_dscr;
+    memcpy(&tmp_dscr, ca_descr, sizeof(tmp_dscr));
+    tmp_dscr.index--;
+    if(ioctl(sc_data->cafd,CA_SET_DESCR,&tmp_dscr)<0) {
+      dprintf0("CA_SET_DESCR failed (%s)\n", strerror(errno));
+    } else {
+      update_keys(sc_data->virt, 'N', ca_descr->index, NULL, 0);
+    }
+  } else {
+    update_keys(sc_data->virt, (ca_descr->parity==0) ? 'E' : 'O',
+                ca_descr->index, ca_descr->cw, 0);
+  }
+}
+
+void _SetCaPid(int adapter, ca_pid_t *ca_pid) {
+  struct sc_data *sc_data = find_sc_from_adpt(adapter);
+  if(!sc_data)
+    return;
+  dprintf1("Sending (%d) key P %d PID: %d\n", sc_data->virt, ca_pid->index,
+           ca_pid->pid);
+  if(sc_data->cafd >= 0) {
+    //Using a real CA
+    ca_pid_t tmp_pid;
+    memcpy(&tmp_pid, ca_pid, sizeof(tmp_pid));
+    tmp_pid.index--;
+    if(ioctl(sc_data->cafd,CA_SET_PID,&tmp_pid) < 0 && tmp_pid.index > 0) {
+      dprintf0("CA_SET_PID failed (%s)\n", strerror(errno));
+    }
+  }
+  update_keys(sc_data->virt, 'P', ca_pid->index, NULL, ca_pid->pid);
+}
+
+void process_cam(struct msg *msg, unsigned int priority)
+{
+  struct list_head *ptr;
+  struct cam_epid *cam_epid;
+  struct sid_msg *sidmsg;
+  int match = 0;
+  cChannel *ch;
+
+  int vpid = 0, ppid = 0, tpid = 0, dcnt = 0;
+  int apid[MAXAPIDS], dpid[MAXDPIDS];
+
+  //struct ScLink link;
+  struct sc_data *sc_data;
+
+  if (msg->type == MSG_RESETSID) {
+    sc_data = find_sc_from_adpt(msg->id);
+    assert(sc_data);
+    dprintf1("Got MSG_RESETSID\n");
+    while(! list_empty(&pid_list)) {
+      cam_epid = list_entry(pid_list.next, struct cam_epid);
+      cam_del_pid(sc_data, cam_epid);
+    }
+    sc_data->cam->Stop();
+    //PrepareScLink(&link, sc_data->dev, OP_TUNE);
+    //link.data.tune.source=0;
+    //link.data.tune.transponder=0;
+    //DoScLinkOp(sc, &link);
+    //sc_data->dev->SetChannelDevice(0, 0);
+    update_keys(sc_data->virt, 'I', 0, NULL, 0);
+    sc_data->valid = 0;
+    msg->type = MSG_PROCESSED;
+    return;
+  }
+  if (msg->type == MSG_REMOVESID) {
+    unsigned int sid = 0xffff & (unsigned long)(msg->data);
+    dprintf1("Got MSG_REMOVESID with sid: %d\n", sid);
+    //delay removal of pids so we can continue to watch for key-rolls
+    list_for_each(ptr, &pid_list) {
+      cam_epid = list_entry(ptr, struct cam_epid);
+      if(cam_epid->sid == sid) {
+        dprintf1("Mapped sid to %d\n", cam_epid->epid);
+        cam_epid->delayclose = 1;
+        //update_keys('C', 0, NULL, cam_epid->epid);
+        //cam_del_pid(sc_data, cam_epid);
+      }
+    }
+    msg->type = MSG_PROCESSED;
+    return;
+  }
+  if (msg->type == MSG_HOUSEKEEPING) {
+    sc->Housekeeping();
+    msg->type = MSG_PROCESSED;
+    return;
+  }
+  if (msg->type != MSG_ADDSID)
+    return;
+
+  sc_data = find_sc_from_adpt(msg->id);
+  assert(sc_data);
+  sidmsg = (struct sid_msg *)msg->data;
+
+  if (sidmsg->calen == 0) {
+    free_sidmsg(sidmsg);
+    return;
+  }
+  for(ch=Channels.First(); ch; ch=Channels.Next(ch)) {
+    if((unsigned int)ch->Sid() == sidmsg->sid) {
+      match = 1;
+      break;
+    }
+  }
+  if(match) {
+    //if the sid is the same as last time, and a tune hasn't happened, we're
+    //already good to go
+    if(sc_data->valid) {
+      list_for_each(ptr, &pid_list) {
+        cam_epid = list_entry(ptr, struct cam_epid);
+        if(cam_epid->delayclose && cam_epid->sid == sidmsg->sid) {
+          dprintf1("Reenabling delayed-closed sid: %d\n", sidmsg->sid);
+          cam_epid->delayclose = 0;
+        }
+      }
+      free_sidmsg(sidmsg);
+      return;
+    }
+  } else {
+    //the sid is different, but we may be on the same transponder, so clear
+    //all delayed-close pids before proceeding
+    while (1) {
+      ll_find_elem(cam_epid, pid_list, delayclose, 1, struct cam_epid);
+      if(cam_epid == NULL)
+        break;
+      dprintf1("Mapped sid %d to epid %d\n", cam_epid->sid, cam_epid->epid);
+      update_keys(sc_data->virt, 'C', 0, NULL, cam_epid->epid);
+      for(ch=Channels.First(); ch; ch=Channels.Next(ch)) {
+        if((unsigned int)ch->Sid() == cam_epid->sid) {
+          Channels.Del(ch);
+          break;
+        }
+      }
+      cam_del_pid(sc_data, cam_epid);
+    }
+  }
+  msg->type = MSG_PROCESSED;
+
+  //create new channel
+  memset(apid, 0, sizeof(int)*MAXAPIDS);
+  memset(dpid, 0, sizeof(int)*MAXDPIDS);
+  ch = new cChannel();
+
+  ch->SetPMTBuf(sidmsg->ca, sidmsg->calen);
+  ch->SetId(0, 1, sidmsg->sid, 0);
+  //set source type to Satellite.  Use orbit and E/W data
+  int source = 0x8000 | (BCD2INT(sidmsg->nit.orbit) & 0x7ff) | ((int)sidmsg->nit.is_east << 19);
+  ch->SetSatTransponderData(source, sidmsg->nit.frequency, sidmsg->nit.polarization, sidmsg->nit.symbolrate, 0);
+  dcnt = (MAXDPIDS >= sidmsg->epid_count) ?
+            sidmsg->epid_count : MAXDPIDS;
+  memcpy(dpid, sidmsg->epid, sizeof(int)*dcnt);
+  ch->SetPids(vpid, ppid, apid, NULL, dpid, NULL, tpid);
+
+  if(! sc_data->valid) {
+    sc_data->valid = 1;
+    while(Channels.First())
+      Channels.Del(Channels.First());
+  }
+
+  Channels.Add(ch);
+  if(Channels.First() == Channels.Last()) {
+    sc_data->cam->Tune(ch);
+    //This is the first channel
+    //PrepareScLink(&link, sc_data->dev, OP_TUNE);
+    //link.data.tune.source=ch->Source();
+    //link.data.tune.transponder=ch->Transponder();
+    //dprintf0("Sending Tune cmd to SC\n");
+    //DoScLinkOp(sc, &link);
+    dprintf0("SC completed  Tune cmd\n");
+  }
+
+  int i, epidlist[MAXDPIDS], *epidptr = epidlist;
+  for(i=0; i < sidmsg->epid_count && i < MAXDPIDS; i++) {
+    pop_entry_from_queue(cam_epid, &pid_empty_queue, struct cam_epid);
+    cam_epid->delayclose = 0;
+    cam_epid->epid = sidmsg->epid[i];
+    cam_epid->type = 5; //epid->type;
+    cam_epid->sid = sidmsg->sid;
+    list_add(&cam_epid->list, &pid_list);
+    *(epidptr++) = sidmsg->epid[i];
+    dprintf1("Adding pid %d for sid %d to pidlist\n", cam_epid->epid, cam_epid->sid);
+    //PrepareScLink(&link, sc_data->dev, OP_ADDPID);
+    //link.data.pids.pid=epid->epid;
+    //link.data.pids.type = 5; //epid->type;
+    //DoScLinkOp(sc, &link);
+  }
+  *epidptr = 0;
+  sc_data->cam->AddPrg(sidmsg->sid,epidlist);
+  free_sidmsg(sidmsg);
+}
+
+//taken and adapted from libdtv, (c) Rolf Hakenes
+// CRC32 lookup table for polynomial 0x04c11db7
+static unsigned int 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};
+
+unsigned int crc32 (const unsigned char *d, int len, unsigned int crc)
+{
+   register int i;
+   const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
+
+   for (i=0; i<len; i++)
+      crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
+
+   return crc;
+}
+
+static int parse_ca(unsigned char * buf, int len)
+{
+  int count, pos = 0;
+  int found = 0;
+  while (pos < len) {
+    count = buf[pos+1] +2;
+    //printf("Found CA: %d (len %d)\n", buf[pos], count);
+    if(buf[pos] == 0x09) {
+      buf[pos] = 0xff;
+      found++;
+    }
+    pos+=count;
+  }
+  return found;
+}
+
+static void replace_cat(unsigned char *buf, int len, int pid)
+{
+  int desc_len, count;
+  int found = 0;
+  int pos = 0;
+  if(len > 0) {
+    if (buf[0] == 0x02) {
+      count = (((buf[1] & 0x03) << 8) | buf[2]) + 3 - 4;
+      desc_len = ((buf[10] & 0x03) << 8) | buf[11];
+      if(desc_len > len - 12 || count > len) {
+        //bogus data
+        return;
+      }
+      found+=parse_ca(buf+12, desc_len);
+      //handle epids here
+      for(pos = 12 + desc_len; pos < count;) {
+        desc_len = ((buf[pos+3] & 0x03) << 8) | buf[pos+4];
+        if(pos+desc_len +5 > count) {
+          //bogus data
+          return;
+        }
+        found+=parse_ca(buf+pos+5, desc_len);
+        pos += desc_len + 5;
+      }
+      if(found) {
+        //compute CRC32
+        int crc = crc32(buf, len-4, 0xFFFFFFFF);
+        buf[len-4] = (crc >> 24) & 0xff;
+        buf[len-3] = (crc >> 16) & 0xff;
+        buf[len-2] = (crc >> 8) & 0xff;
+        buf[len-1] = (crc >> 0) & 0xff;
+      }
+      dprintf1("Replaced %d cas on pid %d (len=%d)\n", found, pid, len);
+    }
+  }
+}
+
+static void dmxread_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct fdmap_list *fd_map;
+  ll_find_elem(fd_map, fdmap_ll, fd, fdptr->fd, struct fdmap_list);
+  if(! fd_map)
+    return;
+  replace_cat(pc->mmap, *ret, fd_map->pid);
+}
+
+static void dmxioctl_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct fdmap_list *fd_map;
+  int pid;
+  ll_find_elem(fd_map, fdmap_ll, fd, fdptr->fd, struct fdmap_list);
+  if(fd_map)
+    return;
+  if(cmd == DMX_SET_FILTER) {
+    struct dmx_sct_filter_params *dmx =
+           (struct dmx_sct_filter_params *)data;
+    pid = dmx->pid;
+  } else if(cmd == DMX_SET_PES_FILTER) {
+    struct dmx_pes_filter_params *dmx =
+           (struct dmx_pes_filter_params *)data;
+    pid = dmx->pid;
+    //supporting PES filters means parsing dvr.  let's skip that for now.
+    return;
+  } else {
+    return;
+  }
+  //This is a hack.  We need to get the pmt handels from the pat like getsid
+  if(pid > 100)
+    return;
+  pop_entry_from_queue(fd_map, &fdmap_empty_queue, struct fdmap_list);
+  fd_map->fd = fdptr->fd;
+  fd_map->pid = pid;
+  list_add(&fd_map->list, &fdmap_ll);
+}
+static void dmxclose_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct fdmap_list *fd_map;
+  ll_find_elem(fd_map, fdmap_ll, fd, fdptr->fd, struct fdmap_list);
+  if(fd_map) {
+    list_del(&fd_map->list);
+    list_add(&fd_map->list, &fdmap_empty_queue);
+  }
+}
+
+void connect_cam(struct parser_adpt *pc_all)
+{
+  int cardnum = pc_all->frontend->common->real_adapt;
+  struct list_head *ptr;
+  char *cadev;
+  char tmpstr[5];
+  struct sc_data *sc_data;
+
+  if(! sc)
+    if(! init_sc())
+      return;
+  sc_data = (struct sc_data *)malloc(sizeof(struct sc_data));
+  memset(sc_data, 0, sizeof(struct sc_data));
+
+
+  sprintf(tmpstr," %d",cardnum+1);
+  strncat(scCap, tmpstr, 80);
+  sc->SetupParse("ScCaps",scCap);
+
+  sc_data->valid = 0;
+  sc_data->virt = pc_all->frontend->common->virt_adapt;
+  sc_data->real = cardnum;
+  sc_data->cam = new sascCam(cardnum);
+
+  if (opt_budget) {
+    sc_data->cafd = -1;
+  } else {
+    asprintf(&cadev, "/dev/dvb/adapter%d/ca0", cardnum);
+    sc_data->cafd = open(cadev, O_RDWR|O_NONBLOCK);
+    if (sc_data->cafd >= 0) {
+      ca_caps_t ca_caps;
+      if (ioctl(sc_data->cafd, CA_GET_CAP, &ca_caps) == 0 &&
+          ca_caps.slot_num > 0 && (ca_caps.slot_type & CA_CI_LINK)) {
+        dprintf0("Found a FF card\n");
+      } else {
+        sc_data->cafd = -1;
+      }
+    }
+    free(cadev);
+  }
+  list_add(&sc_data->list, &sclist);
+  if(opt_fixcat) {
+    ATTACH_CALLBACK(&pc_all->demux->pre_ioctl, dmxioctl_call, 0);
+    ATTACH_CALLBACK(&pc_all->demux->post_read, dmxread_call, 0);
+    ATTACH_CALLBACK(&pc_all->demux->pre_close, dmxclose_call, 0);
+  }
+  list_for_each(ptr, &plugin_cmdlist) {
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+    if(cmd->plugin == PLUGIN_GETSID) {
+      cmd->send_msg(pc_all->frontend, 1);
+      break;
+    }
+  }
+}
+
+static void launch_cam()
+{
+  //Call housecleaning every 60 seconds
+  msg_send_replace(MSG_LOW_PRIORITY, MSG_HOUSEKEEPING, 0, NULL,
+                   60, MSG_RECURRING);
+}
+
+static void shutdown_cam()
+{
+  if (sc)
+  {
+    sc->Stop();
+    struct sc_data *sc_data;
+    struct list_head *ptr;
+    list_for_each(ptr, &sclist) {
+      sc_data = list_entry(ptr, struct sc_data);
+      if (sc_data->cafd >= 0) {
+        close(sc_data->cafd);
+        sc_data->cafd = -1;
+        sc_data->valid = 0;
+      }
+    }
+    delete sc;
+    sc = NULL;
+  }
+}
+
+static void usermsg_cam(char * str) {
+  char *data;
+  cString outstr;
+  int res;
+  if(sc) {
+    printf(":: %s ||\n", str);
+    data = strchr(str, ' ');
+    if(data) {
+      *data = '\0';
+      data++;
+    }
+    printf("User Message %s data: %s\n", str, data);
+    outstr = sc->SVDRPCommand((const char *)str, (const char *)data, res);
+    printf(outstr);
+    printf("User Message %s returned: %d\n", str, res);
+  }
+}
+
+static struct option *parseopt_cam(arg_enum_t cmd)
+{
+  if(cmd == ARG_INIT) {
+    return Cam_Opts;
+  }
+  if(cmd == ARG_HELP) {
+    printf("   --cam-budget      : Force budget card mode\n");
+    printf("   --cam-dir <dir>   : Set directory for sc files (default ./sc_files)\n");
+    printf("   --cam-nofixcat    : Don't remove cat entries\n");
+    printf("   --cam-extau <cmd> : Execute cmd to retrieve updated keys\n");
+    printf("   --cam-scopts <cmd>: Set sc options (opt=value)\n");
+    return NULL;
+  }
+  if(! cam_opt)
+    return NULL;
+
+  switch(cam_opt) {
+    case 'b':
+      opt_budget = 1;
+      break;
+    case 'd':
+      strncpy(opt_camdir, optarg, sizeof(opt_camdir)-1);
+      opt_camdir[sizeof(opt_camdir)-1]=0;
+      break;
+    case 'e':
+      strncpy(opt_extau, optarg, sizeof(opt_extau)-1);
+      opt_extau[sizeof(opt_extau)-1]=0;
+      externalAU = opt_extau;
+    case 'f':
+      opt_fixcat = 0;
+      break;
+    case 'o':
+      {
+        int i;
+        for(i = 0; i < 32 && scopts[i].cmd[0] != '\0'; i++)
+          ;
+        if(i == 32) {
+          dprintf0("Too many SC options.  Ignoring: %s\n", optarg);
+        } else {
+          char *pos =optarg;
+          while(*pos != '=' && *pos != '\0')
+            pos++;
+          if(*pos == '\0') {
+            dprintf0("Could not parse SC argument: %s\n", optarg);
+          } else {
+            memcpy(scopts[i].cmd, optarg, pos - optarg);
+            memcpy(scopts[i].value, pos+1, strlen(optarg) - (1+pos - optarg));
+printf("here: %s %s %d\n", scopts[i].cmd, scopts[i].value, i);
+            if(i != 31)
+              scopts[i+1].cmd[0] = '\0';
+          }
+        }
+        break;
+      }
+  }
+  //must reset cam_opt after every call
+  cam_opt = 0;
+  return NULL;
+}
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "cam",
+                 parseopt_cam, connect_cam, launch_cam, process_cam, NULL,
+                 shutdown_cam, usermsg_cam};
+
+int __attribute__((constructor)) __cam_init(void)
+{
+  list_add_tail(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
diff --git a/contrib/sasc-ng/dvblb_plugins/plugin_cam.h b/contrib/sasc-ng/dvblb_plugins/plugin_cam.h
new file mode 100644 (file)
index 0000000..0bf7e62
--- /dev/null
@@ -0,0 +1,15 @@
+
+#define PLUGIN_CAM 28
+class sascCam;
+
+struct sc_data {
+  struct list_head list;
+  sascCam *cam;
+  unsigned int lastsid;
+  int cafd;
+  int virt;
+  int real;
+  int valid;
+};
+
+extern "C" void *VDRPluginCreator(void);
diff --git a/contrib/sasc-ng/dvblb_plugins/plugin_ffdecsa.c b/contrib/sasc-ng/dvblb_plugins/plugin_ffdecsa.c
new file mode 100644 (file)
index 0000000..e3b2ae1
--- /dev/null
@@ -0,0 +1,636 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include "plugin_ringbuf.h"
+#include "plugin_getsid.h"
+
+#include "../FFdecsa/FFdecsa.h"
+
+#define PLUGIN_ID 30
+#define DBG_NAME "CSA"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+#define FF_MAX_IDX 8
+#define FF_MAX_PID 8
+
+#define push_empty_queue(_item, _queue) {      \
+          pthread_mutex_lock(&list_lock);    \
+          list_add(_item, _queue);             \
+          pthread_mutex_unlock(&list_lock);  \
+}
+
+struct keyindex {
+  int valid;
+  unsigned char even[8];
+  unsigned char odd[8];
+  void *keys;
+  int queued;
+  int status;
+};
+
+struct csastruct {
+  list_head list;
+  int adapter;
+  ringbuffer *rb;
+  unsigned char *csaPtr;
+  struct list_head pid_map;
+  struct keyindex keyindex[FF_MAX_IDX];
+  int nexus_fixup;
+  unsigned int avg;
+  int avgcnt;
+  struct parser_cmds *dvr;
+  int state;
+  pthread_mutex_t keylock;
+  pthread_mutex_t state_lock;
+  pthread_cond_t csa_cond;
+};
+
+static unsigned char **range;
+static int cluster_size;
+static uint cluster_size_bytes;
+
+static LIST_HEAD(csalist);
+static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+enum {
+  NOT_ENCRYPTED = 0,
+  ENCRYPTED_NOT_READY = 1,
+  ENCRYPTED_READY = 2,
+};
+
+struct pid {
+  struct list_head list;
+  int pid;
+  int index;
+};
+static LIST_HEAD(pidmap_empty_queue);
+
+static inline struct csastruct *find_csa_from_adpt(int adapt)
+{
+  struct csastruct *csa;
+  ll_find_elem(csa, csalist, adapter, adapt, struct csastruct);
+  return csa;
+}
+
+void update_keys(int adpt, unsigned char keytype, int index, unsigned char *key, int pid)
+{
+    int i;
+    struct pid *pid_ll;
+    struct csastruct *csa = find_csa_from_adpt(adpt);
+
+    if(key == NULL) {
+      dprintf0("Got command(%d): %c idx: %d pid: %d\n", adpt, keytype, index,
+               pid);
+    } else {
+      dprintf0("Got command(%d): %c idx: %d pid: %d key: "
+             "%02x%02x...%02x\n", adpt, keytype, index, pid,
+             key[0], key[1], key[7]);
+    }
+    assert(csa);
+    if(! csa)
+      return;
+
+    pthread_mutex_lock(&csa->state_lock);
+    if (keytype == 'I') //Invalidate
+    {
+        csa->state = NOT_ENCRYPTED;
+        dprintf1("Setting state: NOT_ENCRYPTED\n");
+        pthread_mutex_lock(&csa->keylock);
+        for(i=0; i < FF_MAX_IDX; i++)
+          csa->keyindex[i].valid = 0;
+        //csa_ready = 0;
+        pthread_mutex_unlock(&csa->keylock);
+        while(! list_empty(&csa->pid_map)) {
+          pid_ll = list_entry(csa->pid_map.next, struct pid);
+          list_del(&pid_ll->list);
+          push_empty_queue(&pid_ll->list, &pidmap_empty_queue);
+        }
+        pthread_mutex_unlock(&csa->state_lock);
+        return;
+    }
+    if (keytype == 'P') //PID
+    {
+        ll_find_elem(pid_ll, csa->pid_map, pid, pid, struct pid);
+        if(pid_ll) {
+          pthread_mutex_unlock(&csa->state_lock);
+          return;
+        }
+        if(! csa->keyindex[index].valid) {
+          csa->keyindex[index].valid = 1;
+          csa->keyindex[index].status = 0;
+          csa->keyindex[index].queued = 0;
+        }
+        pop_entry_from_queue_l(pid_ll, &pidmap_empty_queue, struct pid, &list_lock);
+        pid_ll->pid = pid;
+        pid_ll->index = index;
+        list_add(&pid_ll->list, &csa->pid_map);
+        pthread_mutex_unlock(&csa->state_lock);
+        dprintf1("Adding pid %d to list\n", pid);
+        return;
+    }
+    if (keytype == 'C') //Close PID
+    {
+        ll_find_elem(pid_ll, csa->pid_map, pid, pid, struct pid);
+        if(pid_ll) {
+          index = pid_ll->index;
+          list_del(&pid_ll->list);
+          push_empty_queue(&pid_ll->list, &pidmap_empty_queue);
+          ll_find_elem(pid_ll, csa->pid_map, index, index, struct pid);
+          if(pid_ll == NULL) {
+            //no valid pids on this index
+            csa->keyindex[index].status = 0;
+            if(list_empty(&csa->pid_map)) {
+              //state = ENCRYPTED_NOT_READY;
+              csa->state = NOT_ENCRYPTED;
+              dprintf1("Setting state: NOT_ENCRYPTED\n");
+            }
+          }
+        }
+        pthread_mutex_unlock(&csa->state_lock);
+        dprintf1("Removing pid %d to list state = %d\n", pid, csa->state);
+        return;
+    }
+    ll_find_elem(pid_ll, csa->pid_map, index, index, struct pid);
+    if(pid_ll == NULL) {
+        pthread_mutex_unlock(&csa->state_lock);
+        return;
+    }
+    pthread_mutex_lock(&csa->keylock);
+    if (keytype == 'E') //Even
+    {
+        memcpy( csa->keyindex[index].even, key, 8);
+        csa->keyindex[index].queued |= 0x02;
+        csa->keyindex[index].status |= 0x02;
+        if(csa->state == NOT_ENCRYPTED && csa->keyindex[index].status == 3) {
+          csa->state = ENCRYPTED_NOT_READY;
+          dprintf1("Setting state: ENCRYPTED_NOT_READY\n");
+        }
+        dprintf1("Processed Even Key (idx=%d): State = %d, ready = %d\n",
+                 index, csa->state, csa->keyindex[index].status);
+    }
+    else if (keytype == 'O') //Odd
+    {
+        memcpy( csa->keyindex[index].odd, key, 8);
+        csa->keyindex[index].queued |= 0x01;
+        csa->keyindex[index].status |= 0x01;
+        if(csa->state == NOT_ENCRYPTED && csa->keyindex[index].status == 3) {
+          csa->state = ENCRYPTED_NOT_READY;
+          dprintf1("Setting state: ENCRYPTED_NOT_READY\n");
+        }
+        dprintf1("Processed Odd Key (idx=%d): State = %d, ready = %d\n",
+                 index, csa->state, csa->keyindex[index].status);
+    }
+    else if (keytype == 'N') //Nexus
+    {
+        csa->nexus_fixup = 1;
+        if(csa->state == NOT_ENCRYPTED) {
+          csa->state = ENCRYPTED_NOT_READY;
+          dprintf1("Setting state: ENCRYPTED_NOT_READY\n");
+        }
+    }
+    pthread_mutex_unlock(&csa->keylock);
+    pthread_mutex_unlock(&csa->state_lock);
+    return;
+}
+
+static int process_ts(struct csastruct *csa, unsigned char *buffer, uint end,
+                      int force)
+{
+    unsigned char tmp;
+    struct pid *pid_ll;
+    int ret = 0, rangeptr = 0, dec = 0;
+    uint pos = 0;
+    uint start_enc, end_enc;
+    int index = -1;
+    int odd_even;
+    int pkt_count = 0;
+    if(csa->nexus_fixup) {
+      while(pos < end) {
+        buffer[pos+3] &= 0x3F;
+        pos += TSPacketSIZE;
+      }
+      return end;
+    }
+    //old way
+    //if(! end || ! force && end < 6000)
+    //  return 0;
+
+    while (pos < end)
+    {
+        tmp=buffer[pos+3] & 0xC0;
+        if(tmp == 0xc0 || tmp == 0x80)
+            break;
+        pos += TSPacketSIZE;
+    }
+    //new way
+    if(! force && end - pos < cluster_size_bytes)
+      return  pos;
+    ret = pos;
+    start_enc = end_enc = pos;
+    pthread_mutex_lock(&csa->state_lock);
+    while(end - end_enc >= TSPacketSIZE && pkt_count < cluster_size) {
+        odd_even = buffer[end_enc+3] & 0xC0;
+        if(odd_even) {
+            int pid = ((buffer[end_enc + 1] << 8) + buffer[end_enc + 2])
+                      & 0x1FFF;
+            ll_find_elem(pid_ll, csa->pid_map, pid, pid, struct pid);
+            if(pid_ll == NULL) {
+                //What to do with an unknown pid?
+                //Let's try to decode it anyway
+                dprintf3("Didn't find pid %d\n", pid);
+                end_enc+=TSPacketSIZE;
+                continue;
+            }
+            if(index == -1) {
+                //First encrypted packet (with a valid pid)
+                index = pid_ll->index;
+                tmp=odd_even;
+                pkt_count++;
+           }
+            else if(index != pid_ll->index) {
+                //Encrypted packet with a different index
+                if(start_enc != end_enc) {
+                    //there are previous packets that can be decoded
+                    range[rangeptr++] = buffer + start_enc;
+                    range[rangeptr++] = buffer + end_enc;
+                    range[rangeptr] = 0;
+                }
+                //skip this packet
+                start_enc = end_enc + TSPacketSIZE;
+            } else {
+              pkt_count++;
+            }
+        }
+        end_enc+=TSPacketSIZE;
+    }
+    pthread_mutex_unlock(&csa->state_lock);
+    if (index != -1 && start_enc != end_enc) {
+        //add the last set of packets to the encryption queue
+        range[rangeptr++] = buffer + start_enc;
+        range[rangeptr++] = buffer + end_enc;
+        range[rangeptr] = 0;
+    }
+    if (rangeptr > 0) {
+        pthread_mutex_lock(&csa->keylock);
+        if (! csa->keyindex[index].valid || csa->keyindex[index].status != 3) {
+           pthread_mutex_unlock(&csa->keylock);
+           return ret;
+        }
+        if (csa->keyindex[index].queued == 3)
+        {
+            csa->keyindex[index].queued = 0;
+            set_even_control_word(csa->keyindex[index].keys, csa->keyindex[index].even);
+            set_odd_control_word(csa->keyindex[index].keys, csa->keyindex[index].odd);
+        }
+
+        if (csa->keyindex[index].queued & 0x02 && tmp != 0x80)
+        {
+            csa->keyindex[index].queued &= 0x01;
+            set_even_control_word(csa->keyindex[index].keys, csa->keyindex[index].even);
+        }
+        if (csa->keyindex[index].queued & 0x01 && tmp != 0xC0)
+        {
+            csa->keyindex[index].queued &= 0x02;
+            set_odd_control_word(csa->keyindex[index].keys, csa->keyindex[index].odd);
+        }
+        pthread_mutex_unlock(&csa->keylock);
+
+        if((_dbglvl >> PLUGIN_ID) & 3) {
+          csa->avg = (csa->avg * 99 + pkt_count*100) / 100;
+          if(csa->avg == 0)
+            csa->avg = 1;
+          csa->avgcnt++;
+          if(csa->avgcnt == 100) {
+            csa->avgcnt = 0;
+            dprintf0("decrypted packets max:%d now:%u of %u avg:%f\n",
+                cluster_size, pkt_count, end / TSPacketSIZE, csa->avg / 100.0);
+          }
+        }
+        dec = decrypt_packets(csa->keyindex[index].keys, range) * TSPacketSIZE;
+        if(ret + buffer == range[0])
+          ret+=dec;
+        //printf("decrypted now=%d, decrypted=%d, total=%d\n", pkt, pkt_done, pkt_cnt);
+    }
+    return ret;
+}
+
+static struct csastruct *find_csa_from_rb(struct ringbuffer *rb, int init) {
+  struct csastruct *entry;
+  int notalign;
+
+  if(rb->state != RB_OPEN)
+    return NULL;
+  pthread_mutex_lock(&list_lock);
+  ll_find_elem(entry, csalist, rb, rb, struct csastruct);
+  if (! init) {
+    pthread_mutex_unlock(&list_lock);
+    return entry;
+  }
+  if(! entry) {
+    dprintf0("Creating csa for rb: %d\n", rb->num);
+    entry = find_csa_from_adpt(rb->num);
+    assert(entry);
+  } else {
+    dprintf0("Resetting csa for rb: %d\n", rb->num);
+  }
+  pthread_mutex_lock(&rb->rw_lock);
+  //things may have changed since above
+  if(rb->state != RB_OPEN) {
+    pthread_mutex_unlock(&rb->rw_lock);
+    pthread_mutex_unlock(&list_lock);
+    return NULL;
+  }
+  entry->rb = rb;
+  entry->csaPtr = rb->rdPtr;
+  entry->avg = cluster_size * 100;
+  entry->avgcnt = 0;
+  notalign = (entry->csaPtr - rb->buffer) % TSPacketSIZE;
+  if(notalign) {
+    //The read pointer is not aligned, but the wrptr is required to be
+    entry->csaPtr += TSPacketSIZE - notalign;
+    if(entry->csaPtr > rb->end)
+      entry->csaPtr = rb->buffer + (entry->csaPtr - rb->end);
+    if((rb->rdPtr > rb->wrPtr && entry->csaPtr > rb->wrPtr && entry->csaPtr < rb->rdPtr) || (rb->rdPtr <= rb->wrPtr && entry->csaPtr > rb->wrPtr)) { 
+      dprintf0("INIT buf: %p rd: %p csa: %p wr: %p end: %p\n",
+          rb->buffer, rb->rdPtr, entry->csaPtr, rb->wrPtr, rb->end);
+      assert(0);
+    }
+  }
+  dprintf1("INIT buf: %p rd: %p csa: %p wr: %p end: %p\n",
+         rb->buffer, rb->rdPtr, entry->csaPtr, rb->wrPtr, rb->end);
+  pthread_mutex_unlock(&rb->rw_lock);
+  pthread_mutex_unlock(&list_lock);
+  return entry;
+}
+
+/*Check and/or set state */
+/*MUST lock state_lock before calling!*/
+int check_state(struct csastruct *csa, struct ringbuffer *rb) {
+  if(csa->state == NOT_ENCRYPTED)
+    return NOT_ENCRYPTED;
+  if(csa->state == ENCRYPTED_READY)
+    return ENCRYPTED_READY;
+  if(csa->state == ENCRYPTED_NOT_READY) {
+    if(list_empty(&csa->pid_map))
+      return NOT_ENCRYPTED;
+    find_csa_from_rb(rb, 1);
+    csa->state = ENCRYPTED_READY;
+    dprintf1("Setting state: ENCRYPTED_READY\n");
+    return ENCRYPTED_READY;
+  }
+  return NOT_ENCRYPTED;
+}
+/*If need_bytes >0 we must block until some bytes can be read */
+int check_encrypted(struct ringbuffer *rb, int need_bytes)
+{
+  int processed_bytes = 0;
+  int avail_bytes;
+  int state;
+  struct csastruct *csa = find_csa_from_adpt(rb->num);
+
+  assert(csa);
+  if(csa) {
+    pthread_mutex_lock(&csa->state_lock);
+    state = check_state(csa, rb);
+  } else {
+    state = NOT_ENCRYPTED;
+  }
+
+  if(state == NOT_ENCRYPTED) {
+#if 0
+    unsigned char *ptr = rb->buffer;
+    while(ptr < rb->end) {
+      ptr[3] &= 0x3f;
+      ptr += TSPacketSIZE;
+    }
+#endif
+    if(rb->rdPtr > rb->wrPtr)
+      avail_bytes = rb->end - rb->rdPtr + rb->wrPtr - rb->buffer - TSPacketSIZE;
+    else
+      avail_bytes = rb->wrPtr - rb->rdPtr - TSPacketSIZE;
+    if(csa) {
+      pthread_mutex_unlock(&csa->state_lock);
+    }
+    //printf("Unencrypted.  Returning %d bytes\n", avail_bytes);
+    return avail_bytes;
+  }
+  pthread_mutex_lock(&rb->rw_lock); // protect csaPtr
+  pthread_mutex_unlock(&csa->state_lock);
+
+  //There should be no way for csa not to be set here
+  assert(csa->rb);
+
+  if((rb->rdPtr <= csa->csaPtr  && csa->csaPtr <= rb->wrPtr) ||
+     (rb->wrPtr < rb->rdPtr && rb->rdPtr <= csa->csaPtr)) {
+    processed_bytes = csa->csaPtr -  rb->rdPtr;
+  } else if(csa->csaPtr <= rb->wrPtr && rb->wrPtr < rb->rdPtr) {
+    processed_bytes = rb->end - rb->rdPtr + csa->csaPtr - rb->buffer ;
+  } else {
+    //rd == wr && csa != rd
+    //     OR
+    //wr < csa < rd
+    //We used to get here when switching between encrypted and clear
+    //But no more!
+    //csa->csaPtr = rb->rdPtr;
+    //processed_bytes = 0;
+    dprintf0("buf: %p rd: %p csa: %p wr: %p end: %p\n",
+           rb->buffer, rb->rdPtr, csa->csaPtr, rb->wrPtr, rb->end);
+    assert(0);
+  }
+  if(need_bytes > 0 && processed_bytes < need_bytes) {
+    //ring-read is waiting for decrypted data.  we will stall until we get some
+    //NOTE: We don't care about the return in this case as we need to call again
+    //      to compute processed_bytes
+    dprintf0("buf: %p rd: %p csa: %p wr: %p end: %p\n",
+           rb->buffer, rb->rdPtr, csa->csaPtr, rb->wrPtr, rb->end);
+    msg_replace(MSG_HIGH_PRIORITY, MSG_RINGBUF, rb->num, rb);
+    pthread_cond_wait(&csa->csa_cond, &rb->rw_lock);
+  }
+  //printf("buf: %p rd: %p csa: %p wr: %p end: %p processed: %d\n",
+  //       rb->buffer, rb->rdPtr, csa->csaPtr, rb->wrPtr, rb->end,
+  //       processed_bytes);
+  pthread_mutex_unlock(&rb->rw_lock);
+  return processed_bytes;
+}
+
+static void process_ffd(struct msg *msg, unsigned int priority)
+{
+  struct csastruct *csa;
+  int ret;
+  int end = 0;
+  unsigned char *wrPtr;
+  if(msg->type == MSG_RINGBUF) {
+    struct ringbuffer *rb = (struct ringbuffer *)msg->data;
+    int bytes;
+    msg->type = MSG_PROCESSED;
+#ifdef NO_RINGBUF
+    return;
+#endif
+    csa = find_csa_from_adpt(rb->num);
+    assert(csa);
+    if(! csa)
+      return;
+
+    pthread_mutex_lock(&csa->state_lock);
+    if(check_state(csa, rb) == NOT_ENCRYPTED) {
+      pthread_mutex_unlock(&csa->state_lock);
+      return;
+    }
+
+    assert(csa->rb);
+
+    pthread_mutex_lock(&rb->rw_lock);
+    pthread_mutex_unlock(&csa->state_lock);
+
+    wrPtr = rb->wrPtr;
+
+    if (csa->csaPtr > wrPtr) {
+      bytes = rb->end - csa->csaPtr;
+      end = 1;
+    } else
+      bytes = wrPtr - csa->csaPtr;
+    pthread_mutex_unlock(&rb->rw_lock);
+    ret = process_ts(csa, csa->csaPtr, bytes, end);
+    pthread_mutex_lock(&rb->rw_lock);
+    if (csa->csaPtr + ret > rb->end) {
+      dprintf0("rdPtr: %lu csaPtr: %lu wrPtr: %lu end: %lu bytes: %d end: %d\n",
+             (unsigned long) (rb->rdPtr - rb->buffer),
+             (unsigned long)(csa->csaPtr - rb->buffer),
+             (unsigned long)(rb->wrPtr - rb->buffer),
+             (unsigned long)(rb->end - rb->buffer), bytes, ret);
+      assert(csa->csaPtr + ret <= rb->end);
+    }
+    csa->csaPtr += ret;
+    if(csa->csaPtr == rb->end)
+      csa->csaPtr = rb->buffer;
+
+    if(ret) {
+      if (rb->flags & O_NONBLOCK) {
+        struct dvblb_pollmsg msg;
+        msg.count = 0;
+        //Is this thread safe?
+        pthread_mutex_lock(&csa->dvr->poll_mutex);
+        ioctl(csa->dvr->virtfd, DVBLB_CMD_ASYNC, &msg);
+        pthread_mutex_unlock(&csa->dvr->poll_mutex);
+      } else
+        pthread_cond_signal(&csa->csa_cond);
+    }
+    pthread_mutex_unlock(&rb->rw_lock);
+//    dprintf0("decoded: %d - %d\n", ret, bytes);
+    if(ret) {
+//      dprintf0("Sending RB1: %p\n", rb);
+      msg_replace(MSG_HIGH_PRIORITY, MSG_RINGBUF, rb->num, rb);
+    }
+    sched_yield();
+  }
+  else if(msg->type == MSG_RINGCLOSE) {
+    msg->type = MSG_PROCESSED;
+    struct ringbuffer *rb = (struct ringbuffer *)msg->data;
+    csa = find_csa_from_adpt(rb->num);
+    msg_remove_type_from_list(MSG_HIGH_PRIORITY, MSG_RINGBUF, rb->num, NULL);
+    msg_remove_type_from_list(MSG_HIGH_PRIORITY, MSG_RINGCLOSE, rb->num, NULL);
+    assert(csa);
+    if(! csa)
+      return;
+    dprintf0("Removing csa for rb: %d\n", rb->num);
+    pthread_mutex_lock(&csa->state_lock);
+    rb->release(rb);
+    pthread_mutex_lock(&list_lock);
+    csa->rb = NULL;
+    pthread_mutex_unlock(&list_lock);
+    if(csa->state == ENCRYPTED_READY) {
+      csa->state = ENCRYPTED_NOT_READY;
+      dprintf1("Setting state: ENCRYPTED_NOT_READY\n");
+    }
+    pthread_mutex_unlock(&csa->state_lock);
+  }
+}
+
+//static void launch_ffd() {
+//  pthread_create(&thread, NULL, ffd_loop, NULL);
+//}
+
+#ifdef NO_RINGBUF
+static void preread_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  ci->u.count -= ci->u.count % TSPacketSIZE;
+}
+  
+static void read_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+//  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  int bytes_left = *ret, tmp;
+  do {
+//    dprintf0("Decrypting %d bytes (%d done so far)\n", bytes_left, (*ret - bytes_left));
+    tmp = process_ts(pc->mmap + (*ret - bytes_left), bytes_left);
+    bytes_left -= tmp;
+  } while(bytes_left);
+  *result = CMD_SKIPCALL;
+}
+
+#endif
+
+static void connect_ffd(struct parser_adpt *pc_all)
+{
+  struct list_head *ptr;
+  struct csastruct *csa;
+#ifdef NO_RINGBUF
+  struct cmd_list *dvr_preread = register_cmd(preread_call);
+  struct cmd_list *dvr_postread = register_cmd(read_call);
+  list_add(&dvr_preread.list, &pc_all->dvr->pre_read);
+  list_add(&dvr_postread.list, &pc_all->dvr->post_read);
+#else
+  
+  range = (unsigned char **)malloc(pc_all->dvr->common->buffersize /
+                                   TSPacketSIZE + 1);
+  csa = (struct csastruct *)malloc(sizeof(struct csastruct));
+  memset(csa, 0, sizeof(struct csastruct));
+  pthread_mutex_init(&csa->keylock, NULL);
+  pthread_mutex_init(&csa->state_lock, NULL);
+  pthread_cond_init(&csa->csa_cond, NULL);
+  INIT_LIST_HEAD(&csa->pid_map);
+
+  csa->adapter = pc_all->dvr->common->virt_adapt;
+  csa->dvr = pc_all->dvr;
+  csa->state = NOT_ENCRYPTED;
+  dprintf1("Setting state: NOT_ENCRYPTED\n");
+  for(int i = 0; i < FF_MAX_IDX; i++) {
+    csa->keyindex[i].valid = 0;
+    csa->keyindex[i].keys =  get_key_struct();
+  }
+  list_add_tail(&csa->list, &csalist);
+  cluster_size = get_suggested_cluster_size();
+  cluster_size_bytes = (uint)cluster_size * TSPacketSIZE;
+  ringbuf_register_callback(check_encrypted);
+  list_for_each(ptr, &plugin_cmdlist) {
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+    if(cmd->plugin == PLUGIN_RINGBUF) {
+      cmd->send_msg(pc_all->dvr, 1);
+      break;
+    }
+  }
+#endif
+}
+
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "ffdecsa", 
+                 NULL, connect_ffd, NULL, process_ffd, NULL, NULL};
+int __attribute__((constructor)) __ffdecsa_init(void)
+{
+  list_add_tail(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
+
diff --git a/contrib/sasc-ng/dvblb_plugins/plugin_msg.h b/contrib/sasc-ng/dvblb_plugins/plugin_msg.h
new file mode 100644 (file)
index 0000000..f7d2047
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef DVBLB_MSG
+#define DVBLB_MSG
+#define MSG_HOUSEKEEPING 0x100
+#define MSG_CAMUPDATE    0x200
+#define MSG_TRYSCAN      0x400
+#endif
diff --git a/contrib/sasc-ng/dvblb_plugins/plugin_scan.c b/contrib/sasc-ng/dvblb_plugins/plugin_scan.c
new file mode 100644 (file)
index 0000000..2c6b1ca
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+   DVBLoopback - plugin_scan.c
+   Copyright AxesOfEvil 2007
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include "list.h"
+
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include "process_req.h"
+#include "msg_passing.h"
+#include "plugin_getsid.h"
+#include "plugin_msg.h"
+
+#define PLUGIN_ID 30
+#define DBG_NAME "Scanner"
+#include "debug.h"
+
+static LIST_HEAD(scanner_list);
+
+static int scan_opt = 0;
+static int scan_ports = 0;
+static struct option Scan_Opts[] = {
+  {"scan-ports", 1, &scan_opt, 'p'},
+  {0, 0, 0, 0},
+};
+//SCAN_MIN_WAITTIME: minimum time between an app closing the frontend and the scanner starting
+#define SCAN_MIN_WAITTIME (10)
+//SCAN_WAITTIME: time between completing a scan on the current port and starting it again
+#define SCAN_WAITTIME (10*60)
+//SCAN_TIMEOUT: time to watch a specific port for updates if the keys can't be found
+#define SCAN_TIMEOUT (30*60)
+//SCAN_MIN_TUNETIME: Minimum time to wait before switching to next port (only used after
+//                   Each port has successfully tuned once
+#define SCAN_MIN_TUNETIME (10*60)
+//SCAN_CAM_MINTIME: Minimum time between even/odd keys for them to be considered valid.
+//                  Don't play with this unless you know what you are doing
+#define SCAN_CAM_MINTIME 5
+
+#define MAX_PORTS 16
+
+
+extern char * get_camdir();
+static char scan_script[256], scan_lib[256];
+
+struct scanner {
+  struct list_head list;
+  struct poll_ll *fe_fdptr;
+  int fecount;
+  int has_tuned;
+  int port;
+  int pid;
+  int waiting;
+  int done;
+  struct parser_adpt *pc_all;
+  time_t lastcmd;
+  time_t starttime[MAX_PORTS];
+  time_t found[MAX_PORTS];
+  struct {
+    int state;
+    int last_pol;
+    time_t lasttime;
+  } ca[8];
+};
+unsigned char boo[1024];
+static pthread_mutex_t scanner_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t scanner_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t scanner_thread;
+
+static struct scanner *find_scanner_from_pc(struct parser_cmds *pc)
+{
+  struct list_head *ptr;
+  list_for_each(ptr, &scanner_list) {
+    struct scanner *scanner = list_entry(ptr, struct scanner);
+    if(scanner->pc_all->frontend->common == pc->common)
+      return scanner;
+  }
+  return NULL;
+}
+
+void * scan_loop(void *arg)
+{
+  struct scanner *scanner;
+  struct list_head *ptr;
+  time_t curtime;
+  pthread_mutex_lock(&scanner_mutex);
+  while(1) {
+    int ok = 0;
+    while(! ok) {
+      curtime = time(NULL);
+      list_for_each(ptr, &scanner_list) {
+        scanner = list_entry(ptr, struct scanner);
+        if(scanner->fecount==0 && curtime - scanner->lastcmd >= SCAN_MIN_WAITTIME) {
+          ok = 1;
+          break;
+        }
+      }
+      if(! ok) {
+        pthread_cond_wait(&scanner_cond, &scanner_mutex);
+      }
+    }
+    //We can start the scanner if we get here
+    if(! scanner->pid) {
+      if(curtime > scanner->starttime[scanner->port] + SCAN_WAITTIME) {
+        //Time to try scanning the current port
+        scanner->done = 0;
+        for(int i = 0; i < 8; i++) {
+          scanner->ca[i].lasttime = curtime;
+          scanner->ca[i].state = 0;
+        }
+        dprintf0("Starting scanner on port %d\n", scanner->port);
+        if(0 == (scanner->pid = fork())) {
+          char env[256], *env1[3] = {env, (char *) 0};
+          struct { char a[8], b[8], c[8];} arg = {"scanner", "", ""};
+          char *arg1[] = {arg.a, arg.b, arg.c, (char *) 0};
+          snprintf(env,sizeof(env), "LD_PRELOAD=%s", scan_lib);
+          snprintf(arg.b, 8, "%d", scanner->pc_all->frontend->common->virt_adapt);
+          snprintf(arg.c, 8, "%d", scanner->port);
+          int fd = open("/dev/null", O_WRONLY);
+          while (dup2(fd, 1) < 0 && errno == EINTR)
+            ;
+          while (dup2(fd, 2) < 0 && errno == EINTR)
+            ;
+          while (close(fd) < 0 && errno == EINTR)
+            ;
+          execve(scan_script, arg1, env1);
+          exit(0);
+        }
+        scanner->starttime[scanner->port] = curtime;
+      }
+    } else if(waitpid(scanner->pid, NULL, WNOHANG)) {
+      scanner->pid = 0;
+      scanner->port = (scanner->port+1) % scan_ports;
+      scanner->waiting = 0;
+    } else if(! scanner->waiting &&
+              (  (scanner->done
+                    && (! scanner->found[scanner->port]
+                          || curtime > scanner->starttime[scanner->port] + SCAN_MIN_TUNETIME)
+                 )
+                 || curtime > scanner->starttime[scanner->port] + SCAN_TIMEOUT
+              )) {
+      if(scanner->done) {
+        dprintf0("Scan complete for port %d.  Terminating %d\n", scanner->port, scanner->pid);
+        scanner->found[scanner->port] = 1;
+      }
+      kill(scanner->pid, 9);
+      scanner->waiting = 1;
+    }
+    scanner->lastcmd = curtime;
+  }
+}
+static void process_scan(struct msg *msg, unsigned int priority)
+{
+  if (msg->type == MSG_TRYSCAN) {
+    msg->type = MSG_PROCESSED;
+    pthread_mutex_lock(&scanner_mutex);
+    pthread_cond_signal(&scanner_cond);
+    pthread_mutex_unlock(&scanner_mutex);
+  } else if(msg->type == MSG_CAMUPDATE) {
+    struct scanner *scanner;
+    time_t curtime = time(NULL);
+    int idx = (((unsigned long)msg->data)& 0xff) >> 1;
+    int polarity = ((unsigned long)msg->data)& 0x01;
+    msg->type = MSG_PROCESSED;
+    ll_find_elem(scanner, scanner_list, pc_all->frontend->common->virt_adapt, msg->id, struct scanner);
+    if(scanner == NULL || ! scanner->pid || scanner->waiting)
+      return;
+    if(polarity != scanner->ca[idx].last_pol &&
+       curtime - scanner->ca[idx].lasttime > SCAN_CAM_MINTIME) {
+      if(scanner->ca[idx].state < 4)
+        scanner->ca[idx].state++;
+      if(scanner->ca[idx].state == 4) {
+        pthread_mutex_lock(&scanner_mutex);
+        scanner->done = 1;
+        pthread_mutex_unlock(&scanner_mutex);
+      }
+    } else {
+      pthread_mutex_lock(&scanner_mutex);
+      scanner->done = 0;
+      pthread_mutex_unlock(&scanner_mutex);
+      scanner->ca[idx].state = 0;
+    }
+    scanner->ca[idx].last_pol = polarity;
+    scanner->ca[idx].lasttime = curtime;
+    dprintf0("id:%d idx: %d state:%d pol:%d done:%d\n", msg->id, idx, scanner->ca[idx].state, polarity, scanner->done);
+  }
+}
+
+static void fe_open_pre(struct parser_cmds *pc, struct poll_ll *fdptr,
+                         cmdret_t *result, int *ret,
+                         unsigned long int cmd, unsigned char *data)
+{
+  struct scanner *scanner;
+  scanner = find_scanner_from_pc(pc);
+  if(scanner == NULL)
+    return;
+
+  if((fdptr->flags & O_ACCMODE) == O_RDONLY)
+    return;
+
+  pthread_mutex_lock(&scanner_mutex);
+  if(scanner->fe_fdptr) {
+    if((fdptr->flags & (O_SYNC | O_ASYNC)) != (O_SYNC | O_ASYNC)) {
+      //This isn't the scanner fd so kill the scanner
+      if(scanner->pid && ! scanner->waiting) {
+        kill(scanner->pid, 9);
+        scanner->waiting = 1;
+      }
+      close(scanner->fe_fdptr->fd);
+      scanner->fe_fdptr->fd = open("/dev/null", O_RDWR);
+      scanner->fe_fdptr = NULL;
+      scanner->lastcmd = time(NULL);
+    }
+  }
+  pthread_mutex_unlock(&scanner_mutex);
+}
+
+static void fe_open_post(struct parser_cmds *pc, struct poll_ll *fdptr,
+                         cmdret_t *result, int *ret,
+                         unsigned long int cmd, unsigned char *data)
+{
+  struct scanner *scanner;
+  scanner = find_scanner_from_pc(pc);
+  if(scanner == NULL)
+    return;
+
+  if((fdptr->flags & O_ACCMODE) == O_RDONLY)
+    return;
+  if(fdptr->fd < 0)
+    return;
+  pthread_mutex_lock(&scanner_mutex);
+  if((fdptr->flags & (O_SYNC | O_ASYNC)) == (O_SYNC | O_ASYNC)) {
+    // This is the scanner fd
+    scanner->fe_fdptr = fdptr;
+  } else {
+    scanner->fecount++;
+    scanner->lastcmd = time(NULL);
+  }
+  pthread_mutex_unlock(&scanner_mutex);
+}
+
+static void fe_close(struct parser_cmds *pc, struct poll_ll *fdptr,
+                         cmdret_t *result, int *ret,
+                         unsigned long int cmd, unsigned char *data)
+{
+  struct scanner *scanner;
+  scanner = find_scanner_from_pc(pc);
+  if(scanner == NULL)
+    return;
+
+  if((fdptr->flags & O_ACCMODE) == O_RDONLY)
+    return;
+
+  pthread_mutex_lock(&scanner_mutex);
+  if((fdptr->flags & (O_SYNC | O_ASYNC)) == (O_SYNC | O_ASYNC)) {
+    // This is the scanner fd
+    scanner->fe_fdptr = NULL;
+  } else {
+    if(scanner->fecount > 0)
+      scanner->fecount--;
+    scanner->lastcmd = time(NULL);
+  }
+  pthread_mutex_unlock(&scanner_mutex);
+}
+
+#define FIND_CMD(result, adpt, type, _id) do { \
+  struct cmd_list *cmd; \
+  ll_find_elem(cmd, adpt->pre_##type, id, (_id), struct cmd_list); \
+  if(! cmd) \
+    ll_find_elem(cmd, adpt->post_##type, id, (_id), struct cmd_list); \
+  if(! cmd) \
+    result = NULL;\
+  else \
+    result = cmd->cmd; \
+  } while(0);
+
+static void launch_scan()
+{
+  if(scan_ports) {
+    pthread_create(&scanner_thread, &default_attr, scan_loop, NULL);
+    msg_send_replace(MSG_LOW_PRIORITY, MSG_TRYSCAN, 0, NULL,
+                     10, MSG_RECURRING);
+  }
+}
+
+static void connect_scan(struct parser_adpt *pc_all)
+{
+  struct scanner  *scanner;
+  struct stat st;
+  scanner = (struct scanner *)malloc(sizeof(struct scanner));
+  if(! scanner)
+    return;
+  bzero(scanner, sizeof(struct scanner));
+  if(scan_ports) {
+    snprintf(scan_script, sizeof(scan_script), "%s/scanner", get_camdir());
+    snprintf(scan_lib, sizeof(scan_lib), "%s/libscanwrap.so", get_camdir());
+    if(stat(scan_script, &st) == -1 || ! (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+      dprintf0("Couldn't locate executable %s\n", scan_script);
+      exit(1);
+    }
+    if(stat(scan_lib, &st) == -1) {
+      dprintf0("Couldn't locate wrapper lib %s\n", scan_lib);
+      exit(1);
+    }
+  }
+  scanner->pc_all = pc_all;
+  scanner->lastcmd = time(NULL);
+  ATTACH_CALLBACK(&pc_all->frontend->pre_open, fe_open_pre, -1);
+  ATTACH_CALLBACK(&pc_all->frontend->post_open, fe_open_post, -1);
+  ATTACH_CALLBACK(&pc_all->frontend->post_close, fe_close, -1);
+  list_add_tail(&scanner->list, &scanner_list);
+}
+
+static struct option *parseopt_scan(arg_enum_t cmd)
+{
+  if(cmd == ARG_INIT) {
+    return Scan_Opts;
+  } 
+  if(cmd == ARG_HELP) {
+    printf("   --scan-ports <num_ports>: Turn on scanner\n");
+  } 
+  if(! scan_opt)
+    return NULL;
+
+  switch(scan_opt) {
+    case 'p':
+      scan_ports = strtol(optarg, NULL, 0);
+      dprintf0("Enabling scanner on %d ports\n", scan_ports);
+      break;
+  }
+  //must reset scan_opt after every call
+  scan_opt = 0;
+  return NULL;
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "scanner",
+                     parseopt_scan, connect_scan, launch_scan, process_scan, NULL, NULL, NULL};
+
+int __attribute__((constructor)) __scan_init(void)
+{
+  // communicates the existance of teh current plugin to the main program.
+  if(1) list_add(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
diff --git a/contrib/sasc-ng/dvblb_plugins/scanwrap.c b/contrib/sasc-ng/dvblb_plugins/scanwrap.c
new file mode 100644 (file)
index 0000000..cc5eb82
--- /dev/null
@@ -0,0 +1,30 @@
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#define O_SYNC           010000
+#define O_ASYNC          020000
+
+static int (*realopen) (__const char *__file, int __oflag, ...);
+static int (*realopen64) (__const char *__file, int __oflag, ...);
+void _init(void) {
+       realopen = dlsym(RTLD_NEXT, "open");
+       realopen64 = dlsym(RTLD_NEXT, "open64");
+}
+
+int open(__const char *__file, int __oflag, mode_t __mode) {
+  if(strncmp(__file, "/dev/dvb/adapter", 16) == 0)
+    __oflag |= (O_SYNC | O_ASYNC);
+  printf("flags: %04x",__oflag);
+  return realopen(__file, __oflag, __mode);
+}
+
+int open64(__const char *__file, int __oflag, mode_t __mode) {
+  if(strncmp(__file, "/dev/dvb/adapter", 16) == 0)
+    __oflag |= (O_SYNC | O_ASYNC);
+  return realopen64(__file, __oflag, __mode);
+}
diff --git a/contrib/sasc-ng/dvbloopback/COPYING b/contrib/sasc-ng/dvbloopback/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       Appendix: 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+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) 19yy 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.
+
+  <signature of Ty Coon>, 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/contrib/sasc-ng/dvbloopback/README b/contrib/sasc-ng/dvbloopback/README
new file mode 100644 (file)
index 0000000..dc1b025
--- /dev/null
@@ -0,0 +1,28 @@
+DVB Loopback Driver
+Version 0.0.1
+Alan Nisota
+
+test_read application:
+The test_read application forwards all read/write/ioctl/poll commands from a dvbloopback adapter to a real dvb adapter.
+It works as follows:
+  a request on loopadapter/<dev>0 comes in
+  the dvbloopback driver sends SIGIO to test_read
+  the signal handleing thread in test_read polls all loopadapter/*1 devices and
+      signals the relevant threads that new data is available via SIGALRM
+  the relevant thread reads data from loopadapter/<dev>1
+  this data is formatted as ioctl-cmd,file-descriptor,data
+    if 'ioctl-cmd' is '0' then it is a read/write/open/close/or poll command
+      and data is formatted as a fake ioctl
+    otherwise it is a real ioctl with relevant data-structure
+    the file-descriptor is a unique tag representing the file-descriptor to
+      query, but IS NOT the same as a file-descriptor returned by open
+
+  for the fake-ioctl, the relevant command is sent to the real dvb-adapter
+  for an ioctl, an ioctl is sent to the real dvb-adapter.
+
+  Any returned data, along with the return status is sent back to the
+    dvbloopback driver via an ioctl call.
+  The dvbloopback driver returns the relevant data to loopadapter/<dev>0
+
+
diff --git a/contrib/sasc-ng/dvbloopback/module/Makefile b/contrib/sasc-ng/dvbloopback/module/Makefile
new file mode 100644 (file)
index 0000000..f5bfe2d
--- /dev/null
@@ -0,0 +1,35 @@
+#DVB_DIR = $(HOME)/hg/v4l-dvb/linux
+
+obj-m = dvbloopback.o
+dvbloopback-objs := dvb_loopback.o dvblb_proc.o dvblb_forward.o
+
+ifdef DVB_DIR
+  EXTRA_CFLAGS = -I$(DVB_DIR) -I$(DVB_DIR)/drivers/media/dvb/dvb-core
+  DVB_SRC = $(DVB_DIR)
+  SYMVER = $(DVB_DIR)/../v4l/Module.symvers
+  have_modver := $(wildcard $(SYMVER))
+endif
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -I$(PWD)
+
+BUILD_DIR ?= /lib/modules/$(shell uname -r)/build
+
+all: add_modver
+       ./config_dvb.pl "BUILD_DIR=$(BUILD_DIR)" "EXTRA_CFLAGS=$(EXTRA_CFLAGS)"
+       make -C $(BUILD_DIR) M=$(PWD) modules
+ifeq ($(strip $(have_modver)),) 
+add_modver:
+       echo "Skipping Modver $(SYMVER)"
+
+else
+add_modver: $(SYMVER)
+       cp -f $(SYMVER) .
+       
+endif
+
+clean:
+       make -C $(BUILD_DIR) M=$(PWD) clean
+       cd config-dvb && make clean
+       rm -f config-dvb/chkdvb.c
+       rm -f dvbdevwrap.h
+       rm -f dvbdev.h
+       rm -f Module.symvers
diff --git a/contrib/sasc-ng/dvbloopback/module/compat.h b/contrib/sasc-ng/dvbloopback/module/compat.h
new file mode 100644 (file)
index 0000000..3019cfe
--- /dev/null
@@ -0,0 +1,3 @@
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/module/config-dvb/Makefile b/contrib/sasc-ng/dvbloopback/module/config-dvb/Makefile
new file mode 100644 (file)
index 0000000..367acc0
--- /dev/null
@@ -0,0 +1,13 @@
+#DVB_DIR = $(HOME)/hg/v4l-dvb/
+
+obj-m = chkdvb.o
+
+EXTRA_CFLAGS ?= -Idrivers/media/dvb/dvb-core/ -I$(PWD)
+
+BUILD_DIR := /lib/modules/$(shell uname -r)/build
+
+all: clean
+       make -C $(BUILD_DIR) M=$(PWD) V=1 modules
+
+clean:
+       make -C $(BUILD_DIR) M=$(PWD) clean
diff --git a/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.14.c b/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.14.c
new file mode 100644 (file)
index 0000000..74a9c7a
--- /dev/null
@@ -0,0 +1,14 @@
+  #include "dvbdev.h"
+  int test(void) {
+    struct dvb_adapter *adap = NULL;
+    const char *name = NULL;
+    struct module *module = NULL;
+    struct device *device = NULL;
+    int ret;
+    adap = adap;
+    name = name;
+    module = module;
+    device = device;
+ret = dvb_register_adapter(adap, name, module);
+return ret;
+}
diff --git a/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.18.c b/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.18.c
new file mode 100644 (file)
index 0000000..f3b8ecb
--- /dev/null
@@ -0,0 +1,14 @@
+  #include "dvbdev.h"
+  int test(void) {
+    struct dvb_adapter *adap = NULL;
+    const char *name = NULL;
+    struct module *module = NULL;
+    struct device *device = NULL;
+    int ret;
+    adap = adap;
+    name = name;
+    module = module;
+    device = device;
+ret = dvb_register_adapter(adap, name, module, device);
+return ret;
+}
diff --git a/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.5.c b/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.5.c
new file mode 100644 (file)
index 0000000..ad778e9
--- /dev/null
@@ -0,0 +1,15 @@
+#include <linux/config.h>       /* needed to get LINUX_VERSION_CODE >= 2.6.13 */
+  #include "dvbdev.h"
+  int test(void) {
+    struct dvb_adapter *adap = NULL;
+    const char *name = NULL;
+    struct module *module = NULL;
+    struct device *device = NULL;
+    int ret;
+    adap = adap;
+    name = name;
+    module = module;
+    device = device;
+//ret = dvb_register_adapter(adap, name, module, device);
+return ret;
+}
diff --git a/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.v4l.c b/contrib/sasc-ng/dvbloopback/module/config-dvb/chkdvb-2.6.v4l.c
new file mode 100644 (file)
index 0000000..30c8fac
--- /dev/null
@@ -0,0 +1,14 @@
+  #include "dvbdev.h" 
+  int test(void) { 
+    struct dvb_adapter *adap = NULL; 
+    const char *name = NULL; 
+    struct module *module = NULL; 
+    struct device *device = NULL; 
+    int ret; 
+    adap = adap; 
+    name = name; 
+    module = module; 
+    device = device; 
+ret = dvb_register_adapter(adap, name, module, device,1); 
+return ret; 
+} 
diff --git a/contrib/sasc-ng/dvbloopback/module/config_dvb.pl b/contrib/sasc-ng/dvbloopback/module/config_dvb.pl
new file mode 100644 (file)
index 0000000..9433e15
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+# -*-Perl-*-
+exec perl -x -- "$0" "$@"
+#!perl -w
+
+$debug = 0;
+
+sub test_dvb_adapter(\@) {
+  my($inc) = @_;
+  my($vars)="";
+  foreach $i (@$inc) {
+    $vars .= "\"$i\" ";
+  }
+  unlink("dvbdevwrap.h");
+  unlink("dvbdev.h");
+  unlink("config-dvb/dvbdev.h");
+  $cmd = "cd config-dvb && make $vars" . ($debug ? "" : "2>/dev/null 1>/dev/null");
+  print "$cmd\n" if($debug);
+
+  #test linux-version >= 2.6.22
+  system("ln -sf chkdvb-2.6.v4l.c config-dvb/chkdvb.c");
+  if(system("$cmd") == 0) {
+    print "Found dvbdev.h from 2.6.22 or later\n";
+    `echo "DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);" >> dvbdevwrap.h`; 
+    `echo "#define wrap_dvb_reg_adapter(a, b, c) dvb_register_adapter(a, b, c, &dvblb_basedev->dev, adapter_nr)" >> dvbdevwrap.h`; 
+    return 0;
+  }
+  
+  #test linux-version >= 2.6.18
+  system("ln -sf chkdvb-2.6.18.c config-dvb/chkdvb.c");
+  if(system("$cmd") == 0) {
+    print "Found dvbdev.h from 2.6.18 or later\n";
+    `echo "#define wrap_dvb_reg_adapter(a, b, c) dvb_register_adapter(a, b, c, &dvblb_basedev->dev)" >> dvbdevwrap.h`;
+    return 0;
+  }
+  
+  #test linux-version >= 2.6.14
+  system("ln -sf chkdvb-2.6.14.c config-dvb/chkdvb.c");
+  if(system("$cmd") == 0) {
+    print "Found dvbdev.h from 2.6.14 or later\n";
+    `echo "#define wrap_dvb_reg_adapter dvb_register_adapter" >> dvbdevwrap.h`;
+    return 0;
+  }
+
+  #test linux-version >= 2.6.5
+  system("ln -sf chkdvb-2.6.5.c config-dvb/chkdvb.c");
+  if(system("$cmd") == 0) {
+    print "Found dvbdev.h from 2.6.5 or later\n";
+    print "But this is an unsupported kernel!\n";
+    return 1;
+  }
+
+  #maybe kernel headers aren't available.  let's use canned dvbdev.h
+  #this is dangerous!
+  $uname = `uname -r`;
+  if($uname =~ /2\.6\.(\d\d)/ && $1 >= 22) {
+    system("ln -sf ../dvbdev-2.6.v4l.h config-dvb/dvbdev.h"); 
+    system("ln -sf chkdvb-2.6.v4l.c config-dvb/chkdvb.c"); 
+    if(system("$cmd") == 0) {
+      print "Found 2.6.22 or later kernel, but no dvbdev.h\n";
+      print "Using canned header\n";
+      `echo "DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);" >> dvbdevwrap.h`; 
+      `echo "#define wrap_dvb_reg_adapter(a, b, c) dvb_register_adapter(a, b, c, &dvblb_basedev->dev, adapter_nr)" >> dvbdevwrap.h`; 
+      system("ln -sf dvbdev-2.6.v4l.h dvbdev.h"); 
+      return 0;
+    }
+  }
+  elsif($uname =~ /2\.6\.2[01]/ ||
+        $uname =~ /2\.6\.1[89]/) {
+    system("ln -sf ../dvbdev-2.6.18.h config-dvb/dvbdev.h");
+    system("ln -sf chkdvb-2.6.18.c config-dvb/chkdvb.c");
+    if(system("$cmd") == 0) {
+      print "Found 2.6.18 or later kernel, but no dvbdev.h\n";
+      print "Using canned header\n";
+      `echo "#define wrap_dvb_reg_adapter(a, b, c) dvb_register_adapter(a, b, c, &dvblb_basedev->dev)" >> dvbdevwrap.h`;
+      system("ln -sf dvbdev-2.6.18.h dvbdev.h");
+      return 0;
+    }
+  }
+  elsif($uname =~ /2\.6\.1[4-7]/) {
+    system("ln -sf ../dvbdev-2.6.14.h config-dvb/dvbdev.h");
+    system("ln -sf chkdvb-2.6.14.c config-dvb/chkdvb.c");
+    if(system("$cmd") == 0) {
+      print "Found 2.6.14 or later kernel, but no dvbdev.h\n";
+      print "Using canned header\n";
+      `echo "#define wrap_dvb_reg_adapter dvb_register_adapter" >> dvbdevwrap.h`;
+      system("ln -sf dvbdev-2.6.14.h dvbdev.h");
+      return 0;
+    }
+  }
+  print "Could not identify kernel\n";
+  return 1;
+}
+
+exit(test_dvb_adapter(@ARGV));
diff --git a/contrib/sasc-ng/dvbloopback/module/dvb_loopback.c b/contrib/sasc-ng/dvbloopback/module/dvb_loopback.c
new file mode 100644 (file)
index 0000000..c2caa3b
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ *     dvbloopback.c
+ *
+ *     DVBLoopback Copyright Alan Nisota 2006
+ *
+ *     Portions of this code also have copyright:
+ *     Video Loopback Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000
+ *
+ *     Published under the GNU Public License.
+ *     DVBLoopback 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.
+ *
+ *     DVBLoopback 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 Foobar; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ *     This driver creates a virtual dvb adaper which can forward all I/O to
+ *     userspace or to another DVB card.  Thus, it is possible to clone an
+ *     existing DVB card (why would you do that?), or to pre-process all data
+ *     in userspace before sending it on.  It could also be used, in theory to
+ *     send arbitrary data (hand-crafted stream) to a DVB app.  In addition, a
+ *     mixed mode is allowed.  Each device can be set independantly, and in
+ *     fact each type of I/O can be set independantly (i.e. read, write, and
+ *     ioctl).  The allowed modes are: forward to userspace, forward to
+ *     alternate dvb adapter, forward to alternate dvb adapter and send to
+ *     userspace (for monitor only).
+ *     The driver creates two devices of each type <dev>0 is the new virtual
+ *     device, and <dev>1 is the interface to userspace.
+ *
+ *     This code is based off the video-loopback driver
+ *     http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
+ *
+ */
+
+
+#define DVBLOOPBACK_VERSION "0.0.1"
+
+/* Include files common to 2.4 and 2.6 versions */
+#include <linux/version.h>     /* >= 2.6.14 LINUX_VERSION_CODE */ 
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/version.h>
+#include <linux/vmalloc.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+
+#include "dvbdev.h"
+#include "dvbdevwrap.h"
+#include "dvblb_internal.h"
+
+#define        N_BUFFS 2       /* Number of buffers used for pipes */
+
+#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg)
+
+#define dprintk if (dvblb_debug) printk
+#define dprintk2 if (dvblb_debug > 1) printk
+#define dprintk3 if (dvblb_debug > 2) printk
+
+static int dvblb_debug = 0;
+static int num_adapters = 1;
+module_param(dvblb_debug, int, S_IRUGO | S_IWUSR);
+module_param(num_adapters, int, S_IRUGO);
+
+static struct dvblb *dvblb_global;
+
+static void *rvmalloc(unsigned long size)
+{
+       void *mem;
+       unsigned long adr;
+
+       size = PAGE_ALIGN(size);
+       mem = vmalloc_32(size);
+       if (!mem)
+               return NULL;
+       memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+       adr = (unsigned long) mem;
+       while (size > 0) {
+               SetPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+       unsigned long adr;
+
+       if (!mem)
+               return;
+
+       adr = (unsigned long) mem;
+       while ((long) size > 0) {
+               ClearPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vfree(mem);
+}
+
+/* This is a copy of dvb_usercopy.  We need to do this because it isn't exported
+   by dvbdev
+*/
+static int dvblb_usercopy(struct inode *inode, struct file *file,
+                    unsigned int cmd, unsigned long arg,
+                    int (*func)(struct inode *inode, struct file *file,
+                    unsigned int cmd, void *arg))
+{
+       char    sbuf[128];
+       void    *mbuf = NULL;
+       void    *parg = NULL;
+       int     err  = -EINVAL;
+#if DVB_API_VERSION >=5
+       struct  dtv_properties *tvps = NULL;
+       struct  dtv_property *tvp = NULL;
+#endif
+
+       /*  Copy arguments into temp kernel buffer  */
+       switch (_IOC_DIR(cmd)) {
+       case _IOC_NONE:
+               /*
+                * For this command, the pointer is actually an integer
+                * argument.
+                */
+               parg = (void *) arg;
+               break;
+       case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+       case _IOC_WRITE:
+       case (_IOC_WRITE | _IOC_READ):
+               if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+                       parg = sbuf;
+               } else {
+                       /* too big to allocate from stack */
+                       mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
+                       if (NULL == mbuf)
+                               return -ENOMEM;
+                       parg = mbuf;
+               }
+
+               err = -EFAULT;
+               if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+                       goto out;
+#if DVB_API_VERSION >=5
+               if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) {
+                   tvps = (struct dtv_properties __user *)arg;
+                   tvp = (struct dtv_property *) kmalloc(tvps->num *
+                       sizeof(struct dtv_property), GFP_KERNEL);
+                   if (!tvp){
+                       err = -ENOMEM;
+                       goto out;
+                   }
+                   if (copy_from_user(tvp, tvps->props, 
+                       (tvps->num) * sizeof(struct dtv_property))) {
+                       err = -EFAULT;
+                       goto out;
+                   }
+                   tvps = (struct dtv_properties __user *)parg;
+                   tvps->props = tvp;
+                   tvp = NULL;
+               }
+#endif
+               break;
+       }
+
+       /* call driver */
+       if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
+               err = -EINVAL;
+
+       if (err < 0)
+               goto out;
+
+       /*  Copy results into user buffer  */
+       switch (_IOC_DIR(cmd))
+       {
+       case _IOC_READ:
+       case (_IOC_WRITE | _IOC_READ):
+#if DVB_API_VERSION >=5
+               if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)) 
+               {
+                   tvps = (struct dtv_properties __user *)arg;
+                   tvp = tvps->props;
+                   tvps = (struct dtv_properties __user *)parg;
+                   if (copy_to_user(tvp, tvps->props, tvps->num * 
+                           sizeof(struct dtv_property))) {
+                       err = -EFAULT;
+                       goto out;
+                   }
+                   tvps->props = tvp;
+               }
+#endif
+               if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+                       err = -EFAULT;
+               break;
+       }
+
+out:
+       kfree(mbuf);
+       return err;
+}
+
+static int get_new_filemap(struct dvblb_devinfo *lbdev, int id) {
+       int i;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+       {
+               if(lbdev->filemap[i] == NULL) {
+                       if(id)
+                               lbdev->filemap[i] = lbdev->user_dev;
+                       else
+                               lbdev->filemap[i] = lbdev->lb_dev;
+                       return i;
+               }
+       }
+       return -1;
+}
+
+static int find_filemap(struct dvblb_devinfo *lbdev, struct dvb_device **fm) {
+       int i;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+               if(&lbdev->filemap[i] == fm)
+                       return i;
+       return -1;
+}
+
+int inuse_filemap(struct dvblb_devinfo *lbdev) {
+       int i, count = 0;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+               if(lbdev->filemap[i] != NULL)
+                       count++;
+       return count;
+}
+
+static int dvblb_fake_ioctl(struct dvblb_devinfo *lbdev, struct dvb_device **f,
+                            unsigned long int cmd, void *arg)
+{
+       int ret = 0;
+#if DVB_API_VERSION >=5
+       struct dtv_properties *tvps = NULL;
+       long unsigned int _ioctllen;
+#endif
+       dprintk2("dvblb_fake_ioctl (%d) : %lu\n", lbdev->parent->adapter.num,
+                0xFF & cmd);
+       if (mutex_lock_interruptible(&lbdev->lock_fake_ioctl))
+               return -ERESTARTSYS;
+       if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
+               mutex_unlock(&lbdev->lock_fake_ioctl);
+               return -ERESTARTSYS;
+       }
+       //printk("Sleep up on cmd: %u\n", cmd);
+       lbdev->ioctlcmd = cmd;
+       lbdev->ioctl_already_read = 0;
+       if (cmd < DVBLB_MAX_CMDS) {
+               // Read data command
+               lbdev->ioctllen = sizeof(struct dvblb_custommsg);
+       } else {
+               // Regular ioctl
+               lbdev->ioctllen = _IOC_SIZE(cmd);
+       }
+
+       lbdev->ioctlfd = f;
+       lbdev->ioctldata = arg;
+       //kill_proc(lbdev->pid, SIGIO, 1);
+
+       mutex_unlock(&lbdev->lock_ioctl);
+       wake_up_interruptible(&lbdev->wait_virt_poll);
+       while(1) {
+               ret = wait_event_interruptible_timeout(lbdev->wait_ioctl,
+                        lbdev->ioctlcmd == ULONG_MAX, HZ);
+               dprintk2("fake-ioctl wait (%lu) returned: %d/%lu\n", cmd,
+                        ret, lbdev->ioctlcmd);
+               if(ret >= 0 && lbdev->ioctlcmd == ULONG_MAX)
+                       break;
+               if(cmd == DVBLB_CMD_CLOSE) {
+                       dprintk("Received a signal."
+                               "  Resending DVBLB_CLOSE message\n");
+                       wait_event_timeout(lbdev->wait_ioctl,
+                                  lbdev->ioctlcmd == ULONG_MAX, HZ);
+                       break;
+               }
+               if(ret < 0 || lbdev->filemap[0] == NULL) {
+                       /* userspace driver has closed it would be nice if we
+                          could do something about it here */
+                       printk("dvblb_fake_ioctl interrupted: %lu\n",
+                              lbdev->ioctlcmd);
+                       mutex_unlock(&lbdev->lock_fake_ioctl);
+                       return -1;
+               }
+               dprintk("dvbloopback: timeout waiting for userspace\n");
+       }
+
+       if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
+               mutex_unlock(&lbdev->lock_fake_ioctl);
+               return -ERESTARTSYS;
+       }
+       //printk("Woke up on cmd: %u\n", cmd);
+       if (lbdev->ioctlcmd != ULONG_MAX) {
+               mutex_unlock(&lbdev->lock_ioctl);
+               mutex_unlock(&lbdev->lock_fake_ioctl);
+               printk("dvblb_fake_ioctl failed: %lu\n",lbdev->ioctlcmd);
+               return -1;
+       }
+
+       if (cmd < DVBLB_MAX_CMDS || ! (cmd & IOC_IN)){
+#if DVB_API_VERSION >=5
+               if ((cmd == FE_GET_PROPERTY)) {
+                   tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));
+                   _ioctllen = lbdev->ioctllen + (tvps->num * sizeof(struct dtv_property));
+                   memcpy(arg, lbdev->ioctlretdata + sizeof(int), _ioctllen);
+                   tvps = (struct dtv_properties __user *)arg;
+                   tvps->props = (struct dtv_property __user *)(arg + lbdev->ioctllen);
+               }
+               else
+#endif
+                   memcpy(arg, lbdev->ioctlretdata + sizeof(int), lbdev->ioctllen);
+       }
+
+       ret = lbdev->ioctlretval;
+       mutex_unlock(&lbdev->lock_ioctl);
+       mutex_unlock(&lbdev->lock_fake_ioctl);
+       return ret;
+}
+
+static int dvblb_open(struct inode *inode, struct file *f)
+{
+       struct dvb_device *dvbdev;
+       struct dvblb_devinfo *lbdev;
+       int ret, map;
+
+       dvbdev  = (struct dvb_device *) f->private_data;
+       if (dvbdev == NULL) {
+               printk("Failed to open device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during open\n");
+               return -EFAULT;
+       }
+       dprintk("dvblb_open %d%s%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type], dvbdev->id);
+
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               struct dvblb_custommsg ci;
+
+               /*must update private_data so we can map fds properly */
+               if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+                       return -ERESTARTSYS;
+               map = get_new_filemap(lbdev, 0);
+               if (map < 0) {
+                       printk("dvblb_open %s: Failed to set filemap\n",
+                              dnames[dvbdev->type]);
+                       mutex_unlock(&lbdev->lock_ioctl);
+                       return -EBUSY;
+               }
+               try_module_get(THIS_MODULE);
+               lbdev->dbgfilemap[map] = f;
+               f->private_data = &lbdev->filemap[map];
+               lbdev->poll_waiting = 0;
+               mutex_unlock(&lbdev->lock_ioctl);
+               dprintk("dvblb_open %s: fd: %d\n", dnames[dvbdev->type], map);
+               if (lbdev->forward_dev) {
+                       ret = dvblb_forward_open(lbdev, inode, f);
+                       if(ret < 0) {
+                               module_put(THIS_MODULE);
+                               lbdev->filemap[map] = NULL;
+                       }
+                       return ret;
+               }
+
+               if (lbdev->pid == -1) {
+                       module_put(THIS_MODULE);
+                       lbdev->filemap[map] = NULL;
+                       return -EFAULT;
+               }
+               ci.type = DVBLB_OPEN;
+               ci.u.mode = f->f_flags;
+               ret = dvblb_fake_ioctl(lbdev, &lbdev->filemap[map],
+                                      DVBLB_CMD_OPEN, &ci);
+               if(ret < 0) {
+                       module_put(THIS_MODULE);
+                       lbdev->filemap[map] = NULL;
+               }
+
+               return ret;
+       }
+       /* This is the userspace control device */
+       if(lbdev->forward_dev)
+               return -EFAULT;
+       ret = dvb_generic_open(inode, f);
+       if (ret >= 0) {
+               lbdev->pid = current->pid;
+               lbdev->ioctlcmd = ULONG_MAX;
+               lbdev->ioctllen = 0;
+               mutex_lock(&lbdev->lock_ioctl);
+               memset(lbdev->filemap, 0, sizeof(lbdev->filemap));
+               map = get_new_filemap(lbdev, 1);
+               if (map < 0) {
+                       printk("dvblb_open %s: Failed to set filemap\n",
+                              dnames[dvbdev->type]);
+                       mutex_unlock(&lbdev->lock_ioctl);
+                       return -EBUSY;
+               }
+               try_module_get(THIS_MODULE);
+               f->private_data = &lbdev->filemap[map];
+                lbdev->dbgfilemap[map] = f;
+               lbdev->poll_waiting = 0;
+               mutex_unlock(&lbdev->lock_ioctl);
+               dprintk("dvblb_open %s: fd: %d\n", dnames[dvbdev->type], map);
+       }
+       return ret;
+}
+
+static int dvblb_release(struct inode *inode, struct file *f)
+{
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+       int ret;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during close\n");
+               return -EFAULT;
+       }
+       dprintk("dvblb_release %d%s%d fd:%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap));
+
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               struct dvblb_custommsg ci;
+
+               if (lbdev->forward_dev) {
+                       ret = dvblb_forward_release(lbdev, f);
+                       *filemap = NULL;
+                       goto out;
+               }
+
+               if (lbdev->pid == -1) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+               ci.type = DVBLB_CLOSE;
+               ret = dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_CLOSE, &ci);
+               if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
+                       ret = -ERESTARTSYS;
+                       goto out;
+               }
+               lbdev->poll_waiting = 0;
+               *filemap = NULL;
+               mutex_unlock(&lbdev->lock_ioctl);
+               goto out;
+       }
+       /* This is the userspace control device */
+       *filemap = NULL;
+       f->private_data = dvbdev;
+       ret = dvb_generic_release(inode, f);
+       if (ret < 0) {
+               goto out;
+       }
+       lbdev->pid = -1;
+       mutex_lock_interruptible(&lbdev->lock_buffer);
+       if (lbdev->buffer) {
+               rvfree(lbdev->buffer, lbdev->buflen*N_BUFFS);
+               lbdev->buffer = NULL;
+       }
+       mutex_unlock(&lbdev->lock_buffer);
+       ret = 0;
+       goto out;
+
+out:
+       module_put(THIS_MODULE);
+       return ret;
+}
+
+static ssize_t dvblb_write(struct file *f, const char *buf,
+               size_t count, loff_t *offset)
+{
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during close\n");
+               return -EFAULT;
+       }
+       dprintk3("dvblb_write %d%s%d fd:%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap));
+
+       if (lbdev->forward_dev)
+               return dvblb_forward_write(lbdev, f, buf, count, offset);
+
+       printk("Write not supported on loopback device\n");
+       return -EFAULT;
+}
+
+static ssize_t dvblb_read (struct file *f, char * buf, size_t count, loff_t *offset)
+{
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+#if DVB_API_VERSION >=5
+       struct dtv_properties *tvps = NULL;
+#endif
+       unsigned long int _count = 0;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to find device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during read\n");
+               return -EFAULT;
+       }
+       dprintk3("dvblb_read %d%s%d fd:%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap));
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               struct dvblb_custommsg ci;
+
+               if (lbdev->forward_dev)
+                       return dvblb_forward_read(lbdev, f, buf, count, offset);
+
+               ci.type = DVBLB_READ;
+               ci.u.count = count;
+               if (lbdev->pid == -1)
+                       return -EFAULT;
+               if (mutex_lock_interruptible(&lbdev->lock_buffer))
+                       return -ERESTARTSYS;
+               if(dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_READ, &ci) < 0 ||
+                  ! lbdev->buffer) {
+                       mutex_unlock(&lbdev->lock_buffer);
+                       return 0;
+               }
+               if (ci.u.count > lbdev->buflen)
+                       ci.u.count = lbdev->buflen;
+               if (copy_to_user(buf, lbdev->buffer, ci.u.count)) {
+                       mutex_unlock(&lbdev->lock_buffer);
+                       return -EFAULT;
+               }
+               mutex_unlock(&lbdev->lock_buffer);
+               dprintk3("Read %ld bytes\n", (long)ci.u.count);
+               return ci.u.count;
+       } else {
+               /* This is the userspace control device */
+               /* We pass ioctls to the userspace driver this way */
+               unsigned long int cmd, base_size, size;
+               base_size = sizeof(cmd) + sizeof(lbdev->ioctlfd);
+               if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+                       return -ERESTARTSYS;
+
+               cmd = lbdev->ioctlcmd;
+               size = (lbdev->ioctllen > sizeof(int)) ? lbdev->ioctllen :
+                                                        sizeof(int);
+#if DVB_API_VERSION >=5
+               if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)){
+                   tvps = (struct dtv_properties __user *)(lbdev->ioctldata);
+                   _count = size + base_size + (tvps->num * sizeof(struct dtv_property));
+               }
+               else
+#endif
+                   _count = base_size + size;
+                   
+               if (count < _count || lbdev->ioctlcmd == ULONG_MAX ||
+                    lbdev->ioctl_already_read) {
+                       mutex_unlock(&lbdev->lock_ioctl);
+                       return -EFAULT;
+               }
+               if (cmd == ULONG_MAX)
+                   count = base_size;
+               else
+                   count = _count;
+
+               if (copy_to_user(buf, &cmd, sizeof(cmd))) {
+                       mutex_unlock(&lbdev->lock_ioctl);
+                       return -EFAULT;
+               }
+
+               if (copy_to_user(buf + sizeof(cmd), &lbdev->ioctlfd,
+                                sizeof(lbdev->ioctlfd))) {
+                       mutex_unlock(&lbdev->lock_ioctl);
+                       return -EFAULT;
+               }
+
+               if (lbdev->ioctllen == 0) {
+                       if (copy_to_user(buf + base_size, &lbdev->ioctldata,
+                                        size)) {
+                               mutex_unlock(&lbdev->lock_ioctl);
+                               return -EFAULT;
+                       }
+                       
+               } else {
+#if DVB_API_VERSION >=5
+                       if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)) {    
+                           if (copy_to_user(buf + base_size + size, tvps->props,
+                                        tvps->num * sizeof(struct dtv_property))) {
+                               mutex_unlock(&lbdev->lock_ioctl);
+                               return -EFAULT;
+                           }
+                           tvps->props = (struct dtv_property __user *)(buf + base_size + size);
+                       }
+#endif
+                       if (copy_to_user(buf + base_size, lbdev->ioctldata,
+                                        size)) {
+                               mutex_unlock(&lbdev->lock_ioctl);
+                               return -EFAULT;
+                       }
+               }
+               lbdev->ioctl_already_read = 1;
+               mutex_unlock(&lbdev->lock_ioctl);
+               return count;
+       }
+}
+
+/* The following routine gets called by dvblb_usercopy (used to be 
+   dvb_generic_ioctl) which is called by dvblb_ioctl for device-0.  It is
+   used to forward ioctl commands back to the userspace application
+*/
+static int dvblb_looped_ioctl(struct inode *inode, struct file *f,
+       unsigned int cmd, void *parg)
+{
+       int ret;
+        struct dvb_device *dvbdev, **filemap;
+        struct dvblb_devinfo *lbdev;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+        if (dvbdev == NULL) {
+                printk("Failed to locate device\n");
+                return -EFAULT;
+        }
+        lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+        if (lbdev == NULL) {
+                printk("Failed to find private data during ioctl\n");
+                return -EFAULT;
+        }
+       dprintk("dvblb_ioctl %d%s%d fd:%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap));
+       ret = dvblb_fake_ioctl(lbdev, filemap, cmd, parg);
+       return ret;
+}
+
+static int dvblb_ioctl(struct inode *inode, struct file *f,
+       unsigned int cmd, unsigned long arg)
+{
+       void * parg = (void *)arg;
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+#if DVB_API_VERSION >=5
+       struct dtv_properties *tvps;
+#endif
+       int size;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during ioctl\n");
+               return -EFAULT;
+       }
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               if (lbdev->forward_dev)
+                       return dvblb_forward_ioctl(lbdev, f, cmd, arg);
+
+               return dvblb_usercopy (inode, f, cmd, arg,
+                                      dvbdev->kernel_ioctl);
+       }
+       /* This is the userspace control device */
+       dprintk2("dvblb_ioctl %d%s%d fd:%d cmd:%x\n",lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap), cmd);
+       if (cmd == DVBLB_CMD_ASYNC) {
+               /* async command return */
+               int pos, i;
+                struct dvblb_pollmsg pollmsg;
+               if (copy_from_user(&pollmsg, parg,
+                                  sizeof(struct dvblb_pollmsg))) {
+                       printk("Failed to read pollmsg from ioctl\n");
+                       return -EFAULT;
+               }
+               if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+                       return -ERESTARTSYS;
+               if (pollmsg.count == 0) {
+                       /* Send wakeup to all waiting polls */
+                       /*
+                       printk("Waking all fds on %s\n", dnames[dvbdev->type]);
+                       */
+                       lbdev->poll_waiting = 0;
+                       for (i = 0; i < DVBLB_MAXFD; i++)
+                               if(lbdev->filemap[i] != 0)
+                                       wake_up_interruptible(
+                                                       &lbdev->wait_poll[i]);
+               } 
+               for (i = 0; i < pollmsg.count; i++) {
+                       pos = find_filemap(lbdev, pollmsg.file[i]);
+                       if (pos >= 0) {
+                               /*
+                               printk("Sending wakeup command to %p, pos:%d\n",
+                                      lbdev->filemap[pos], pos);
+                               */
+                               lbdev->poll_waiting &= ~(1<<pos);
+                               wake_up_interruptible(&lbdev->wait_poll[pos]);
+                       }
+               }
+               mutex_unlock(&lbdev->lock_ioctl);
+               return 0;
+       }
+       if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+               return -ERESTARTSYS;
+       if (cmd != lbdev->ioctlcmd) {
+               // Incorrect response
+               printk("dvbloopback: Got wrong response (%u != %lu)\n", cmd,
+                      lbdev->ioctlcmd);
+               mutex_unlock(&lbdev->lock_ioctl);
+               return 0;
+       }
+       if (cmd < DVBLB_MAX_CMDS)
+               size = sizeof(int) + sizeof(struct dvblb_custommsg);
+       else if (cmd & IOC_IN)
+               size = sizeof(int);
+       else
+               size = sizeof(int) + _IOC_SIZE(cmd);
+
+       if (copy_from_user(lbdev->ioctlretdata, parg, size)) {
+               mutex_unlock(&lbdev->lock_ioctl);
+               return -EFAULT;
+       }
+#if DVB_API_VERSION >=5
+       if (cmd == FE_GET_PROPERTY)
+       {    
+           tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));    
+           if (copy_from_user(lbdev->ioctlretdata + size, tvps->props,
+                        tvps->num * sizeof(struct dtv_property))) 
+           {
+               mutex_unlock(&lbdev->lock_ioctl);
+               return -EFAULT;
+           }
+           tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));
+           tvps->props = (struct dtv_property __user *)(lbdev->ioctlretdata + size);
+           dprintk("%s() copy_from_user: cmd %u\n", __func__, tvps->props[0].cmd);
+       }
+#endif
+
+       lbdev->ioctlretval = *(int *)lbdev->ioctlretdata;
+       lbdev->ioctlcmd = ULONG_MAX;
+       mutex_unlock(&lbdev->lock_ioctl);
+       wake_up(&lbdev->wait_ioctl);
+       return 0;
+}
+
+static int dvblb_mmap(struct file *f, struct vm_area_struct *vma)
+{
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+       unsigned long start = (unsigned long)vma->vm_start;
+       long size = vma->vm_end - vma->vm_start;
+       unsigned long page, pos;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during mmap\n");
+               return -EFAULT;
+       }
+       dprintk("dvblb_mmap %d%s%d fd:%d\n", lbdev->parent->adapter.num,
+               dnames[dvbdev->type],
+               dvbdev->id, find_filemap(lbdev, filemap));
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               if (lbdev->forward_dev)
+                       return dvblb_forward_mmap(lbdev, f, vma);
+
+               return -EINVAL;
+       }
+       /* This is the userspace control device */
+       if (!size)
+               return -EINVAL;
+       if (mutex_lock_interruptible(&lbdev->lock_buffer))
+               return -ERESTARTSYS;
+       if (lbdev->buffer)
+               rvfree(lbdev->buffer, lbdev->buflen*N_BUFFS);
+       lbdev->buflen=size;
+       lbdev->buffer=rvmalloc(lbdev->buflen*N_BUFFS);
+
+       if (lbdev->buffer == NULL) {
+               mutex_unlock(&lbdev->lock_buffer);
+               return -EINVAL;
+       }
+
+        if (size > (((N_BUFFS * lbdev->buflen) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+               mutex_unlock(&lbdev->lock_buffer);
+                return -EINVAL;
+       }
+
+       pos = (unsigned long)lbdev->buffer;
+        while (size > 0) {
+               page = vmalloc_to_pfn((void *)pos);
+               if (remap_pfn_range(vma, start, page, PAGE_SIZE,
+                                PAGE_SHARED)) {
+                       mutex_unlock(&lbdev->lock_buffer);
+                        return -EAGAIN;
+               }
+                start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
+        }
+       mutex_unlock(&lbdev->lock_buffer);
+
+       return 0;
+}
+
+static unsigned int dvblb_poll(struct file *f, struct poll_table_struct *wait)
+{
+       struct dvb_device *dvbdev, **filemap;
+       struct dvblb_devinfo *lbdev;
+
+       filemap  = (struct dvb_device **) f->private_data;
+       if(filemap == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       dvbdev = *filemap;
+       if (dvbdev == NULL) {
+               printk("Failed to locate device\n");
+               return -EFAULT;
+       }
+       lbdev = (struct dvblb_devinfo *)dvbdev->priv;
+       if (lbdev == NULL) {
+               printk("Failed to find private data during poll\n");
+               return -EFAULT;
+       }
+       dprintk3("dvblb_poll %d%s%d: fd:%d\n", lbdev->parent->adapter.num,
+                dnames[dvbdev->type],
+                dvbdev->id, find_filemap(lbdev, filemap));
+       if(dvbdev->id == 0) {
+               /* This is the looped device */
+               struct dvblb_custommsg ci;
+               int ret;
+               int do_poll = 1;
+               int pos;
+
+               if (lbdev->forward_dev)
+                       return dvblb_forward_poll(lbdev, f, wait);
+
+               if (lbdev->pid == -1) {
+                       printk("no pid found!\n");
+                       return -EFAULT;
+               }
+               pos = find_filemap(lbdev, filemap);
+               if (pos == -1) {
+                       printk("dvblb_poll %s: Didn't find filemap!\n",
+                              dnames[dvbdev->type]);
+                       return -EFAULT;
+               }
+               poll_wait(f, &lbdev->wait_poll[pos], wait);
+
+               if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+                       return -ERESTARTSYS;
+               if(lbdev->poll_waiting & (1 << pos))
+                       do_poll = 0;
+               else /* need to set it before the fake_ioctl to prevent race */
+                       lbdev->poll_waiting |= (1 << pos);
+               mutex_unlock(&lbdev->lock_ioctl);
+
+               if(do_poll) {
+                       ci.type = DVBLB_POLL;
+                       ci.u.mode = (wait == NULL) ? 0 : 1;
+                       ret = dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_POLL,
+                                              &ci);
+                       if(ret != 0) {
+                               if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+                                       return -ERESTARTSYS;
+                               lbdev->poll_waiting &= ~(1 << pos);
+                               mutex_unlock(&lbdev->lock_ioctl);
+                               if(ret < 0)
+                                       return ret;
+                       }
+                       if (ci.u.mode & POLLIN)
+                               ci.u.mode |= POLLRDNORM;
+               } else {
+                       ci.u.mode = 0;
+                       /*
+                       printk("skipping poll on %p (%d)\n", f, pos);
+                       */
+               }
+       dprintk3("dvblb_poll %d%s%d: fd:%d returned: %d\n",
+                lbdev->parent->adapter.num,
+                dnames[dvbdev->type], dvbdev->id, pos, ci.u.mode);
+               return(ci.u.mode);
+       }
+
+       poll_wait(f, &lbdev->wait_virt_poll, wait);
+       if (mutex_lock_interruptible(&lbdev->lock_ioctl))
+               return -ERESTARTSYS;
+       if (lbdev->ioctlcmd != ULONG_MAX && ! lbdev->ioctl_already_read) {
+               mutex_unlock(&lbdev->lock_ioctl);
+               return (POLLIN | POLLPRI | POLLRDNORM);
+       }
+       mutex_unlock(&lbdev->lock_ioctl);
+       return 0;
+}
+
+static struct file_operations dvbdev_looped_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dvblb_open,
+       .release        = dvblb_release,
+       .read           = dvblb_read,
+       .write          = dvblb_write,
+       .poll           = dvblb_poll,
+       .mmap           = dvblb_mmap,
+       .ioctl          = dvblb_ioctl,
+};
+
+static struct dvb_device dvbdev_looped = {
+        .priv = NULL,
+        .users = DVBLB_MAXFD,
+        .readers = 1,
+        .writers = 1,
+       .fops = &dvbdev_looped_fops,
+       .kernel_ioctl  = dvblb_looped_ioctl,
+};
+
+static struct file_operations dvbdev_userspace_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dvblb_open,
+       .release        = dvblb_release,
+       .read           = dvblb_read,
+       .write          = dvblb_write,
+       .poll           = dvblb_poll,
+       .mmap           = dvblb_mmap,
+       .ioctl          = dvblb_ioctl,
+};
+
+static struct dvb_device dvbdev_userspace = {
+        .priv = NULL,
+        .users = 1,
+        .readers = 1,
+        .writers = 1,
+       .fops = &dvbdev_userspace_fops,
+       .kernel_ioctl  = dvblb_looped_ioctl,
+};
+
+static int create_lb_dev(struct dvblb *dvblb, int dev_idx, int type)
+{
+       int ret;
+       struct dvblb_devinfo *lbdev = &dvblb->devinfo[dev_idx];
+
+// create loopback device (will be frontend0)
+       ret = dvb_register_device(&dvblb->adapter, &lbdev->lb_dev,
+                           &dvbdev_looped, lbdev, type);
+       if (ret != 0) {
+               info("error registering device adapter%d",
+                    dvblb->adapter.num);
+               return ret;
+       }
+// create userspace device (will be frontend1)
+       ret = dvb_register_device(&dvblb->adapter, &lbdev->user_dev,
+                           &dvbdev_userspace, lbdev, type);
+       if (ret != 0) {
+               info("error registering device adapter%d",
+                    dvblb->adapter.num);
+               dvb_unregister_device(lbdev->lb_dev);
+               return ret;
+       }
+// initialize all data
+       //lbdev->ioctldata=kmalloc(1024, GFP_KERNEL);
+       //if(lbdev->ioctldata == NULL)
+       //{
+       //      dvb_unregister_device(lbdev->lb_dev);
+       //      dvb_unregister_device(lbdev->user_dev);
+       //      return -ENOMEM;
+       //}
+       lbdev->parent = dvblb;
+       lbdev->ioctlretdata=kmalloc(5*1024, GFP_KERNEL);
+       lbdev->pid = -1;
+       lbdev->buffer = NULL;
+       lbdev->buflen = 0;
+       lbdev->ioctlcmd = ULONG_MAX;
+       lbdev->ioctllen = 0;
+       lbdev->ioctl_already_read = 1;
+       lbdev->forward_dev = NULL;
+       memset(lbdev->filemap, 0, sizeof(lbdev->filemap));
+       memset(lbdev->forwardmap, 0, sizeof(lbdev->forwardmap));
+
+       lbdev->poll_waiting = 0;
+       dvblb_init_procfs_device(dvblb, lbdev);
+
+       {
+               int i;
+               for(i = 0; i < DVBLB_MAXFD; i++)
+                       init_waitqueue_head(&lbdev->wait_poll[i]);
+       }
+       init_waitqueue_head(&lbdev->wait_virt_poll);
+       init_waitqueue_head(&lbdev->wait_ioctl);
+       mutex_init(&lbdev->lock_fake_ioctl);
+       mutex_init(&lbdev->lock_ioctl);
+       mutex_init(&lbdev->lock_buffer);
+       dvblb->init |= (1 << dev_idx);
+       return 0;
+}
+
+static int destroy_lb_dev(struct dvblb *dvblb, int dev_idx)
+{
+       if (0 == (dvblb->init & (1 << dev_idx)))
+               return 0;
+       dvb_unregister_device(dvblb->devinfo[dev_idx].lb_dev);
+       dvb_unregister_device(dvblb->devinfo[dev_idx].user_dev);
+       if (dvblb->devinfo[dev_idx].buffer)
+               rvfree(dvblb->devinfo[dev_idx].buffer,
+                      dvblb->devinfo[dev_idx].buflen*N_BUFFS);
+       //kfree(lbdev->ioctldata);
+       kfree(dvblb->devinfo[dev_idx].ioctlretdata);
+       dvblb_remove_procfs(dvblb->devinfo[dev_idx].procfile, dvblb->procdir);
+       dvblb->init &= ~(1 << dev_idx);
+       return 0;
+}
+
+static int destroy_lb_adapter(struct dvblb *dvblb)
+{
+       destroy_lb_dev(dvblb, DVBLB_FRONTEND);
+       destroy_lb_dev(dvblb, DVBLB_DEMUX);
+       destroy_lb_dev(dvblb, DVBLB_DVR);
+       destroy_lb_dev(dvblb, DVBLB_VIDEO);
+       destroy_lb_dev(dvblb, DVBLB_AUDIO);
+       destroy_lb_dev(dvblb, DVBLB_OSD);
+
+       dvblb_remove_procfs_adapter(dvblb);
+       dvb_unregister_adapter(&dvblb->adapter);
+       dvblb->init &= ~DVBLB_STATUS_ADAPTER;
+
+       return 0;
+}
+/****************************************************************************
+ *     init stuff
+ ****************************************************************************/
+
+
+MODULE_AUTHOR("Alan Nisota");
+MODULE_DESCRIPTION("DVB loopback device.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION( DVBLOOPBACK_VERSION );
+
+static struct platform_device *dvblb_basedev;
+       
+static int __init dvblb_init(void)
+{
+       int i,ret, failed;
+       i=0;
+       failed=0;
+
+       dvblb_init_procfs();
+
+       info("frontend loopback driver v"DVBLOOPBACK_VERSION);
+       if (num_adapters < 1 || num_adapters > DVBLB_MAX_ADAPTERS) {
+               printk("dvbloopback: param num_adapters=%d. Must be between"
+                      " 1 and %d\n", num_adapters, DVBLB_MAX_ADAPTERS);
+               return -EFAULT;
+       }
+       printk("dvbloopback: registering %d adapters\n", num_adapters);
+
+       dvblb_global = kmalloc(sizeof(struct dvblb) * num_adapters, GFP_KERNEL);
+       if (dvblb_global == NULL)
+               return -ENOMEM;
+       for(i=0; i < num_adapters; i++)
+               dvblb_global[i].init = 0;
+       dvblb_basedev = platform_device_alloc("dvbloopback", -1);
+       if (!dvblb_basedev) {
+               kfree(dvblb_global);
+               return -ENOMEM;
+       }
+       ret = platform_device_add(dvblb_basedev);
+       if (ret) {
+               platform_device_put(dvblb_basedev);
+               kfree(dvblb_global);
+               return ret;
+       }
+       for(i=0; i < num_adapters; i++) {
+               struct dvblb *this_adptr = &dvblb_global[i];
+               // register new adapter (happens in main driver)
+               if (wrap_dvb_reg_adapter(&this_adptr->adapter,
+                                        "DVB-LOOPBACK", NULL) < 0) {
+                       failed = 1;
+                       break;
+               }
+               this_adptr->init |= DVBLB_STATUS_ADAPTER;
+
+               // NOTE: we use the next line to fetch the start-node for the 
+               // the adapter linked-list.  However it relies on us grabbing it
+               // before another device is added (i.e. this adapter must be the
+               // last on the list.
+               if (0 == i) {
+                       this_adptr->adapter_ll =
+                                       this_adptr->adapter.list_head.next;
+               } else {
+                       this_adptr->adapter_ll = dvblb_global[0].adapter_ll;
+               }
+               this_adptr->link = -1;
+               dvblb_init_procfs_adapter(this_adptr);
+
+               ret = create_lb_dev(this_adptr, DVBLB_FRONTEND,
+                                   DVB_DEVICE_FRONTEND);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+
+               ret = create_lb_dev(this_adptr, DVBLB_DEMUX,
+                           DVB_DEVICE_DEMUX);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+
+               ret = create_lb_dev(this_adptr, DVBLB_DVR,
+                                   DVB_DEVICE_DVR);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+
+               ret = create_lb_dev(this_adptr, DVBLB_VIDEO,
+                                   DVB_DEVICE_VIDEO);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+
+               ret = create_lb_dev(this_adptr, DVBLB_AUDIO,
+                                   DVB_DEVICE_AUDIO);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+
+               ret = create_lb_dev(this_adptr, DVBLB_OSD,
+                                   DVB_DEVICE_OSD);
+               if (ret != 0) {
+                       info("Failed to add loopback for adapter");
+                       failed = 1;
+                       break;
+               }
+       }
+
+       if (failed) {
+               for(i = 0; i < num_adapters; i++) {
+                       if(0 == dvblb_global[i].init)
+                               break;
+                       destroy_lb_adapter(&dvblb_global[i]);
+               }
+               platform_device_unregister(dvblb_basedev);
+               kfree(dvblb_global);
+               return -EFAULT;
+       }
+       return 0;
+}
+
+static void __exit cleanup_dvblb_module(void)
+{
+       int i;
+       info("Unregistering ca loopback devices");
+       for(i = 0; i < num_adapters; i++) {
+               if(0 == dvblb_global[i].init)
+                       break;
+               destroy_lb_adapter(&dvblb_global[i]);
+       }
+       platform_device_unregister(dvblb_basedev);
+       kfree(dvblb_global);
+       dvblb_uninit_procfs();
+}
+
+module_init(dvblb_init);
+module_exit(cleanup_dvblb_module);
diff --git a/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.14.h b/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.14.h
new file mode 100644 (file)
index 0000000..0cc6e4a
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser 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 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.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+
+#define DVB_MAJOR 212
+
+#define DVB_DEVICE_VIDEO      0
+#define DVB_DEVICE_AUDIO      1
+#define DVB_DEVICE_SEC        2
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+#define DVB_DEVICE_NET        7
+#define DVB_DEVICE_OSD        8
+
+
+struct dvb_adapter {
+       int num;
+       struct list_head list_head;
+       struct list_head device_list;
+       const char *name;
+       u8 proposed_mac [6];
+       void* priv;
+
+       struct module *module;
+};
+
+
+struct dvb_device {
+       struct list_head list_head;
+       struct file_operations *fops;
+       struct dvb_adapter *adapter;
+       int type;
+       u32 id;
+
+       /* in theory, 'users' can vanish now,
+          but I don't want to change too much now... */
+       int readers;
+       int writers;
+       int users;
+
+       /* don't really need those !? -- FIXME: use video_usercopy  */
+       int (*kernel_ioctl)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg);
+
+       void *priv;
+};
+
+
+extern int dvb_register_adapter (struct dvb_adapter *adap, const char *name, struct module *module);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+                               struct dvb_device **pdvbdev,
+                               const struct dvb_device *template,
+                               void *priv,
+                               int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy()  someday... */
+
+extern int dvb_usercopy(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg,
+                           int (*func)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg));
+
+#endif /* #ifndef _DVBDEV_H_ */
diff --git a/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.18.h b/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.18.h
new file mode 100644 (file)
index 0000000..620e788
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser 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 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.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+
+#define DVB_MAJOR 212
+
+#define DVB_DEVICE_VIDEO      0
+#define DVB_DEVICE_AUDIO      1
+#define DVB_DEVICE_SEC        2
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+#define DVB_DEVICE_NET        7
+#define DVB_DEVICE_OSD        8
+
+
+struct dvb_adapter {
+       int num;
+       struct list_head list_head;
+       struct list_head device_list;
+       const char *name;
+       u8 proposed_mac [6];
+       void* priv;
+
+       struct device *device;
+
+       struct module *module;
+};
+
+
+struct dvb_device {
+       struct list_head list_head;
+       struct file_operations *fops;
+       struct dvb_adapter *adapter;
+       int type;
+       u32 id;
+
+       /* in theory, 'users' can vanish now,
+          but I don't want to change too much now... */
+       int readers;
+       int writers;
+       int users;
+
+       /* don't really need those !? -- FIXME: use video_usercopy  */
+       int (*kernel_ioctl)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg);
+
+       void *priv;
+};
+
+
+extern int dvb_register_adapter (struct dvb_adapter *adap, const char *name, struct module *module, struct device *device);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+                               struct dvb_device **pdvbdev,
+                               const struct dvb_device *template,
+                               void *priv,
+                               int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy()  someday... */
+
+extern int dvb_usercopy(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg,
+                           int (*func)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg));
+
+/** generic DVB attach function. */
+#ifdef CONFIG_DVB_CORE_ATTACH
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+       void *__r = NULL; \
+       typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+       if (__a) { \
+               __r = (void *) __a(ARGS); \
+               if (__r == NULL) \
+                       symbol_put(FUNCTION); \
+       } else { \
+               printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
+       } \
+       __r; \
+})
+
+#else
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+       FUNCTION(ARGS); \
+})
+
+#endif
+
+#endif /* #ifndef _DVBDEV_H_ */
diff --git a/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.v4l.h b/contrib/sasc-ng/dvbloopback/module/dvbdev-2.6.v4l.h
new file mode 100644 (file)
index 0000000..89d12dc
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser 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 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.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+
+#define DVB_MAJOR 212
+
+#define DVB_MAX_ADAPTERS 8
+
+#define DVB_UNSET (-1)
+
+#define DVB_DEVICE_VIDEO      0
+#define DVB_DEVICE_AUDIO      1
+#define DVB_DEVICE_SEC        2
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+#define DVB_DEVICE_NET        7
+#define DVB_DEVICE_OSD        8
+
+#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \
+       static short adapter_nr[] = \
+               {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \
+       module_param_array(adapter_nr, short, NULL, 0444); \
+       MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
+
+struct dvb_adapter {
+       int num;
+       struct list_head list_head;
+       struct list_head device_list;
+       const char *name;
+       u8 proposed_mac [6];
+       void* priv;
+
+       struct device *device;
+
+       struct module *module;
+};
+
+
+struct dvb_device {
+       struct list_head list_head;
+       struct file_operations *fops;
+       struct dvb_adapter *adapter;
+       int type;
+       u32 id;
+
+       /* in theory, 'users' can vanish now,
+          but I don't want to change too much now... */
+       int readers;
+       int writers;
+       int users;
+
+       wait_queue_head_t         wait_queue;
+       /* don't really need those !? -- FIXME: use video_usercopy  */
+       int (*kernel_ioctl)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg);
+
+       void *priv;
+};
+
+
+extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+                               struct module *module, struct device *device,
+                               short *adapter_nums);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+                               struct dvb_device **pdvbdev,
+                               const struct dvb_device *template,
+                               void *priv,
+                               int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy()  someday... */
+
+extern int dvb_usercopy(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg,
+                           int (*func)(struct inode *inode, struct file *file,
+                           unsigned int cmd, void *arg));
+
+/** generic DVB attach function. */
+#ifdef CONFIG_MEDIA_ATTACH
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+       void *__r = NULL; \
+       typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+       if (__a) { \
+               __r = (void *) __a(ARGS); \
+               if (__r == NULL) \
+                       symbol_put(FUNCTION); \
+       } else { \
+               printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
+       } \
+       __r; \
+})
+
+#else
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+       FUNCTION(ARGS); \
+})
+
+#endif
+
+#endif /* #ifndef _DVBDEV_H_ */
diff --git a/contrib/sasc-ng/dvbloopback/module/dvblb_forward.c b/contrib/sasc-ng/dvbloopback/module/dvblb_forward.c
new file mode 100644 (file)
index 0000000..dcb4107
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ *     dvblb_forward.c
+ *
+ *     DVBLoopback Copyright Alan Nisota 2006
+ *
+ *     Portions of this code also have copyright:
+ *     Video Loopback Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000
+ *
+ *     Published under the GNU Public License.
+ *     DVBLoopback 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.
+ *
+ *     DVBLoopback 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 Foobar; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/version.h>      /* >= 2.6.14 LINUX_VERSION_CODE */
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include "dvblb_internal.h"
+
+#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
+
+// forward to correct device
+static struct file *find_forwardmap(struct dvblb_devinfo *lbdev,
+                                    struct dvb_device **f)
+{
+       int i;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+               if(lbdev->forwardmap[i].f == f)
+                       return lbdev->forwardmap[i].map;
+       return ERR_PTR(-1);
+}
+
+static int set_forwardmap(struct dvblb_devinfo *lbdev, struct dvb_device **f,
+                          struct file *map) {
+       int i;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+               if(lbdev->forwardmap[i].f == NULL) {
+                       lbdev->forwardmap[i].f = f;
+                       lbdev->forwardmap[i].map = map;
+                       return i;
+               }
+       return -1;
+}
+
+static void clear_forwardmap(struct dvblb_devinfo *lbdev, struct dvb_device **f)
+{
+       int i;
+       for(i = 0; i < DVBLB_MAXFD; i++)
+               if(lbdev->forwardmap[i].f == f) {
+                       lbdev->forwardmap[i].f = NULL;
+                       return;
+               }
+}
+
+int dvblb_forward_open(struct dvblb_devinfo *lbdev, struct inode *inode,
+                       struct file *f)
+{
+//     int minor;
+//     struct inode *tmpinode;
+//     struct dentry *dentry;
+       struct file *ftmp;
+       char tmpstr[35];
+
+       if (! inode->i_cdev || ! inode->i_cdev->ops ||
+           ! inode->i_cdev->ops->open) {
+               printk("dvblb_forward_open: "
+                      "device wasn't correctly initialized\n");
+               return -EFAULT;
+       }
+       sprintf(tmpstr, "/dev/dvb/adapter%d/%s%d", lbdev->forward_dev->adapter->num, dnames[lbdev->forward_dev->type], lbdev->forward_dev->id);
+       /* printk("linking to %s\n", tmpstr); */
+       ftmp = filp_open(tmpstr, f->f_flags, f->f_mode);
+
+       /* It would be nice to find a way to open the device without relying
+          on a fixed path, but the attempt below certainly isn't it */
+/*
+       minor = nums2minor(lbdev->forward_dev->adapter->num,
+                          lbdev->forward_dev->type, lbdev->forward_dev->id);
+       tmpinode = iget_locked(inode->i_sb, MKDEV(DVB_MAJOR, minor));
+       if (! tmpinode)
+               return -EFAULT;
+       if (is_bad_inode(tmpinode)) {
+               iput(tmpinode);
+               return -EFAULT;
+       }
+       dentry = d_alloc_anon(tmpinode);
+       if (!dentry) {
+               iput(tmpinode);
+               return -EFAULT;
+       }
+       ftmp = dentry_open(dentry, f->f_vfsmnt, f->f_flags);
+*/
+       if (!ftmp || IS_ERR(ftmp)) {
+               int fd = PTR_ERR(ftmp);
+               printk("open failed: %d\n", fd);
+               return fd;
+       }
+       if (! ftmp->f_op || ! lbdev->forward_dev->fops ||
+           ftmp->f_op->open !=  lbdev->forward_dev->fops->open) {
+               printk("DVB device din't initialize correctly\n");
+               filp_close(ftmp, NULL);
+               return -EFAULT;
+       }
+       if (set_forwardmap(lbdev, f->private_data, ftmp) == -1) {
+               printk("Didn't find a valid forwardmap\n");
+               filp_close(ftmp, NULL);
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int dvblb_forward_release(struct dvblb_devinfo *lbdev, struct file *f)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->release) {
+               filp_close(ftmp, NULL);
+               clear_forwardmap(lbdev, f->private_data);
+       }
+       return -EFAULT;
+}
+
+ssize_t dvblb_forward_write(struct dvblb_devinfo *lbdev, struct file *f,
+                            const char *buf, size_t count, loff_t *offset)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->write)
+               return lbdev->forward_dev->fops->write(
+                          ftmp, buf, count, offset);
+       return -EFAULT;
+}
+
+ssize_t dvblb_forward_read (struct dvblb_devinfo *lbdev, struct file *f,
+                            char * buf, size_t count, loff_t *offset)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->read)
+               return lbdev->forward_dev->fops->read(
+                          ftmp, buf, count, offset);
+       return -EFAULT;
+}
+
+int dvblb_forward_ioctl(struct dvblb_devinfo *lbdev, struct file *f,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->ioctl)
+               return lbdev->forward_dev->fops->ioctl(
+                          ftmp->f_dentry->d_inode, ftmp, cmd, arg);
+       return -EFAULT;
+}
+
+int dvblb_forward_mmap(struct dvblb_devinfo *lbdev, struct file *f,
+                       struct vm_area_struct *vma)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->mmap)
+               return lbdev->forward_dev->fops->mmap(ftmp, vma);
+       return -EFAULT;
+}
+
+int dvblb_forward_poll(struct dvblb_devinfo *lbdev, struct file *f,
+                       struct poll_table_struct *wait)
+{
+       struct file *ftmp = find_forwardmap(lbdev, f->private_data);
+       if (!ftmp || IS_ERR(ftmp))
+               return -EFAULT;
+       if (lbdev->forward_dev->fops &&lbdev->forward_dev->fops->poll)
+               return lbdev->forward_dev->fops->poll(ftmp, wait);
+       return -EFAULT;
+}
diff --git a/contrib/sasc-ng/dvbloopback/module/dvblb_internal.h b/contrib/sasc-ng/dvbloopback/module/dvblb_internal.h
new file mode 100644 (file)
index 0000000..498e5d0
--- /dev/null
@@ -0,0 +1,109 @@
+#include <linux/wait.h>
+#include <linux/proc_fs.h>
+#include "dvbdev.h"
+#include "dvbloopback.h"
+
+#define DVBLB_MAX_ADAPTERS 8
+
+struct dvblb;
+
+struct dvblb_fwdmap {
+       struct dvb_device **f;
+       struct file *map;
+};
+
+struct dvblb_devinfo {
+       struct dvblb      *parent;
+       struct dvb_device *lb_dev;
+       struct dvb_device *user_dev;
+       int                pid;
+       unsigned long int  ioctlcmd;
+       int                ioctlretval;
+       unsigned char     *ioctldata;
+       unsigned char     *ioctlretdata;
+       unsigned int       ioctllen;
+       unsigned int       ioctl_already_read;
+       void              *ioctlfd;
+       unsigned char     *buffer;
+       unsigned long int  buflen;
+       wait_queue_head_t  wait_ioctl;
+       wait_queue_head_t  wait_virt_poll;
+       struct mutex   lock_fake_ioctl;
+       struct mutex   lock_ioctl;
+       struct mutex   lock_buffer;
+
+       wait_queue_head_t    wait_poll[DVBLB_MAXFD];
+       unsigned long int    poll_waiting;
+       struct dvb_device    *filemap[DVBLB_MAXFD];
+       void                 *dbgfilemap[DVBLB_MAXFD];
+
+       struct dvb_device   *forward_dev;
+       struct dvblb_fwdmap  forwardmap[DVBLB_MAXFD];
+
+       struct proc_dir_entry *procfile;
+};
+
+enum
+{
+       DVBLB_FRONTEND = 0,
+       DVBLB_DEMUX    = 1,
+       DVBLB_DVR      = 2,
+       DVBLB_VIDEO    = 3,
+       DVBLB_AUDIO    = 4,
+       DVBLB_OSD      = 5,
+       DVBLB_NUM_DEVS = 6
+};
+#define DVBLB_STATUS_FRONTEND (1 << DVBLB_FRONTEND)
+#define DVBLB_STATUS_DEMUX    (1 << DVBLB_DEMUX)
+#define DVBLB_STATUS_DVR      (1 << DVBLB_DVR)
+#define DVBLB_STATUS_ADAPTER  0x0100U
+#define DVBLB_STATUS_PROC     0x0200U
+
+struct dvblb {
+       struct dvb_adapter adapter;
+       struct dvblb_devinfo devinfo[DVBLB_NUM_DEVS];
+
+       struct proc_dir_entry *procdir;
+       struct proc_dir_entry *procfile;
+       int                    link;
+       struct list_head *adapter_ll;
+       unsigned int init;
+};
+extern int inuse_filemap(struct dvblb_devinfo *lbdev);
+
+/* dvblb_proc.c */
+extern int dvblb_remove_procfs(struct proc_dir_entry *pdir,
+                        struct proc_dir_entry *parent);
+
+extern int dvblb_init_procfs_device(struct dvblb *dvblb,
+                                    struct dvblb_devinfo *lbdev);
+
+extern int dvblb_init_procfs_adapter(struct dvblb *dvblb);
+
+extern int dvblb_remove_procfs_adapter(struct dvblb *dvblb);
+
+extern int dvblb_init_procfs(void);
+
+extern int dvblb_uninit_procfs(void);
+
+/* dvblb_forward.c */
+extern int dvblb_forward_open(struct dvblb_devinfo *lbdev, struct inode *inode,
+                       struct file *f);
+
+extern int dvblb_forward_release(struct dvblb_devinfo *lbdev, struct file *f);
+
+extern ssize_t dvblb_forward_write(struct dvblb_devinfo *lbdev, struct file *f,
+                            const char *buf, size_t count, loff_t *offset);
+
+extern ssize_t dvblb_forward_read (struct dvblb_devinfo *lbdev, struct file *f,
+                            char * buf, size_t count, loff_t *offset);
+
+extern int dvblb_forward_ioctl(struct dvblb_devinfo *lbdev, struct file *f,
+                        unsigned int cmd, unsigned long arg);
+
+extern int dvblb_forward_mmap(struct dvblb_devinfo *lbdev, struct file *f,
+                       struct vm_area_struct *vma);
+
+extern int dvblb_forward_poll(struct dvblb_devinfo *lbdev, struct file *f,
+                       struct poll_table_struct *wait);
+
diff --git a/contrib/sasc-ng/dvbloopback/module/dvblb_proc.c b/contrib/sasc-ng/dvbloopback/module/dvblb_proc.c
new file mode 100644 (file)
index 0000000..2083bd4
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *     dvblb_proc.c
+ *
+ *     DVBLoopback Copyright Alan Nisota 2006
+ *
+ *     Portions of this code also have copyright:
+ *     Video Loopback Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000
+ *
+ *     Published under the GNU Public License.
+ *     DVBLoopback 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.
+ *
+ *     DVBLoopback 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 Foobar; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/version.h>      /* >= 2.6.14 LINUX_VERSION_CODE */
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/proc_fs.h>
+#include "dvblb_internal.h"
+
+static struct proc_dir_entry *procdir;
+
+static int dvblb_procfs_read(char *page, char **start, off_t off, int count,
+                             int *eof, void *data)
+{
+       struct dvblb_devinfo *lbdev = (struct dvblb_devinfo *)data;
+       int val;
+       if (lbdev == NULL)
+               return 0;
+       val = (lbdev->forward_dev) ? 1 : 0;
+       return sprintf(page, "%03d", val);
+}
+
+static int dvblb_procfs_write(struct file *file, const char *buffer,
+                              unsigned long count, void *data)
+{
+       char str[10];
+       int val, v1, v2, v3, fm;
+       struct dvblb_devinfo *lbdev = (struct dvblb_devinfo *)data;
+       if (lbdev == NULL)
+               return count;
+       if (lbdev->parent->link == -1)
+               return count;
+       if (count > 10)
+               count = 10;
+       if (copy_from_user(str, buffer, count)) {
+               return -EFAULT;
+       }
+       val = simple_strtoul(str, NULL, 0);
+       v1 = val /100;
+       v2 = (val - v1*100) / 10;
+       v3 = val - v1*100 - v2*10;
+       if (v1 < 0 || v1 > 2) 
+               return -EFAULT;
+       if((fm = inuse_filemap(lbdev))) {
+               int type = lbdev->lb_dev->type;
+               printk("dvbloopback: Can't change forward on adapter%d."
+                      " Device %s still has %d users!\n",
+                      lbdev->parent->adapter.num, dnames[type], fm);
+               return count;
+       }
+       if (v3 == 1) {
+               struct list_head *entry;
+               list_for_each (entry, lbdev->parent->adapter_ll) {
+                       struct dvb_adapter *adap;
+                       adap = list_entry (entry, struct dvb_adapter,
+                                          list_head);
+                       if (adap->num == lbdev->parent->link) {
+                               struct list_head *entry0;
+                               list_for_each (entry0,
+                                              &adap->device_list) {
+                                       struct dvb_device *dev;
+                                       dev = list_entry (entry0,
+                                              struct dvb_device, list_head);
+                                       if (dev->type == lbdev->lb_dev->type) {
+                                               lbdev->forward_dev = dev;
+                                               return count;
+                                       }
+                               }
+                       }
+               }
+       } else if (v3 == 0) {
+               lbdev->forward_dev = NULL;
+       }
+       return count;
+}
+
+static int dvblb_procfs_adapter_read(char *page, char **start, off_t off,
+                                     int count, int *eof, void *data)
+{
+       struct dvblb *dvblb = (struct dvblb *)data;
+       if (dvblb == NULL)
+               return 0;
+       return sprintf(page, "%d", dvblb->link);
+}
+
+static int dvblb_procfs_adapter_write(struct file *file, const char *buffer,
+                                      unsigned long count, void *data)
+{
+       char str[10];
+       int val, i, fm;
+       struct dvblb *dvbdev = (struct dvblb *)data;
+       if (dvbdev == NULL)
+               return count;
+       if (count > 10)
+               count = 10;
+       if (copy_from_user(str, buffer, count)) {
+               return -EFAULT;
+       }
+       val = simple_strtol(str, NULL, 0);
+
+       if(val == -999) {
+               /*This is a debug case.  Try to force close all open fds
+                 This is known not to be very reliable, but better than
+                 nothing
+               */
+               for(i = 0; i < DVBLB_NUM_DEVS; i++) {
+                       while((fm = inuse_filemap(&dvbdev->devinfo[i]))) {
+                               filp_close(dvbdev->devinfo[i].dbgfilemap[fm],
+                                          NULL);
+                               dvbdev->devinfo[i].filemap[fm] = NULL;
+                       }
+               }
+               return count;
+       }
+       for(i = 0; i < DVBLB_NUM_DEVS; i++) {
+               if(dvbdev->devinfo[i].forward_dev == NULL)
+                       continue;
+               if((fm = inuse_filemap(&dvbdev->devinfo[i]))) {
+                       int type = dvbdev->devinfo[i].lb_dev->type;
+                       printk("dvbloopback: Can't change forward on adapter%d."
+                              " Device %s still has %d users!\n",
+                              dvbdev->adapter.num, dnames[type], fm);
+                       return count;
+               }
+       }
+       for(i = 0; i < DVBLB_NUM_DEVS; i++)
+               dvbdev->devinfo[i].forward_dev = NULL;
+       dvbdev->link = val;
+       return count;
+}
+
+int dvblb_remove_procfs(struct proc_dir_entry *pdir,
+                        struct proc_dir_entry *parent)
+{
+       char name[20];
+       memcpy(name, pdir->name, pdir->namelen);
+       name[pdir->namelen] = '\0';
+       // printk("Removing proc: %s\n", name);
+       remove_proc_entry(name, parent);
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_remove_procfs);
+
+int dvblb_init_procfs_device(struct dvblb *dvblb, struct dvblb_devinfo *lbdev)
+{
+       int type = lbdev->lb_dev->type;
+       lbdev->procfile = create_proc_entry(dnames[type], 0644, dvblb->procdir);
+       if (lbdev->procfile == NULL)
+               return -ENOMEM;
+       lbdev->procfile->data = lbdev;
+       lbdev->procfile->read_proc = dvblb_procfs_read;
+       lbdev->procfile->write_proc = dvblb_procfs_write;
+       lbdev->procfile->owner = THIS_MODULE;
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_init_procfs_device);
+
+int dvblb_init_procfs_adapter(struct dvblb *dvblb)
+{
+       char name[10];
+       sprintf(name, "adapter%d", dvblb->adapter.num);
+       dvblb->procdir = proc_mkdir(name, procdir);
+       if (dvblb->procdir == NULL)
+               return -ENOMEM;
+       dvblb->procdir->owner = THIS_MODULE;
+       dvblb->procfile = create_proc_entry("adapter", 0644, dvblb->procdir);
+       if (dvblb->procfile == NULL) {
+               dvblb_remove_procfs(dvblb->procdir, procdir);
+               return -ENOMEM;
+       }
+       dvblb->procfile->data = dvblb;
+       dvblb->procfile->read_proc = dvblb_procfs_adapter_read;
+       dvblb->procfile->write_proc = dvblb_procfs_adapter_write;
+       dvblb->procfile->owner = THIS_MODULE;
+       dvblb->init |= DVBLB_STATUS_PROC;
+
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_init_procfs_adapter);
+
+int dvblb_remove_procfs_adapter(struct dvblb *dvblb)
+{
+       if(dvblb->init & DVBLB_STATUS_PROC) {
+               dvblb_remove_procfs(dvblb->procfile, dvblb->procdir);
+               dvblb_remove_procfs(dvblb->procdir, procdir);
+               printk("removing dvblb proc adapter\n");
+       }
+       dvblb->init &= ~DVBLB_STATUS_PROC;
+       printk("dvblb init = %x\n", dvblb->init);
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_remove_procfs_adapter);
+
+int dvblb_init_procfs(void)
+{
+       procdir = proc_mkdir("dvbloopback", NULL);
+       if (procdir == NULL)
+               return -ENOMEM;
+       procdir->owner = THIS_MODULE;
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_init_procfs);
+
+int dvblb_uninit_procfs(void)
+{
+       dvblb_remove_procfs(procdir, NULL);
+       return 0;
+}
+EXPORT_SYMBOL(dvblb_uninit_procfs);
+
diff --git a/contrib/sasc-ng/dvbloopback/module/dvbloopback.h b/contrib/sasc-ng/dvbloopback/module/dvbloopback.h
new file mode 100644 (file)
index 0000000..78aa00b
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef DVBLOOPBACK_H
+#define DVBLOOPBACK_H
+
+#define DVBLB_MAXFD 96
+
+static const char * const dnames[] = {
+        "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+        "net", "osd"
+};
+
+typedef enum dvblb_type {
+       DVBLB_OPEN,
+       DVBLB_CLOSE,
+       DVBLB_READ,
+       DVBLB_WRITE,
+       DVBLB_POLL,
+} dvblb_type_t;
+
+enum {
+       DVBLB_CMD_OPEN = 0,
+       DVBLB_CMD_CLOSE,
+       DVBLB_CMD_READ,
+       DVBLB_CMD_WRITE,
+       DVBLB_CMD_POLL,
+       DVBLB_CMD_ASYNC,
+       DVBLB_MAX_CMDS,
+};
+
+struct dvblb_custommsg {
+       dvblb_type_t    type;
+       union {
+               unsigned int mode;
+               size_t       count;
+       } u;
+};
+
+struct dvblb_pollmsg {
+       int count;
+       void *file[DVBLB_MAXFD];
+};
+#endif
+
diff --git a/contrib/sasc-ng/dvbloopback/src/Makefile b/contrib/sasc-ng/dvbloopback/src/Makefile
new file mode 100644 (file)
index 0000000..b938cf9
--- /dev/null
@@ -0,0 +1,46 @@
+VERSION = 0.0.2
+TOOL = forward
+
+CC       ?= gcc
+CXX      ?= g++
+CXXFLAGS ?= -g -Wall -Werror
+CFLAGS   ?= -g -Wall
+
+ifdef DVB_DIR
+  INCLUDES = -I$(DVB_DIR)/linux/include
+  DVB_MOD_DIR = DVB_DIR=$(DVB_DIR)
+endif
+
+DEFINES += -DRELEASE_VERSION=\"$(VERSION)\"
+INCLUDES += -I../module
+
+#OBJS = forward.o process_req.o msg_passing.o plugin_getsid.o plugin_ringbuf.o \
+#       plugin_dss.o version.o
+OBJS = forward.o process_req.o msg_passing.o \
+       plugin_dss.o version.o
+LIBS = -lpthread
+
+INC_DEPS := $(shell ls *.h)
+
+all: $(TOOL)
+
+$(TOOL): $(OBJS)
+       $(CXX) $(CFLAGS) -o $(TOOL) $(OBJS) $(LIBS)
+
+clean:
+       rm -f $(OBJS)
+       rm version.cpp
+
+%.o: %.c $(INC_DEPS)
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+%.o: %.cpp
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+version.o: version.cpp
+       $(CXX) $(CXXFLAGS) -o $@ -c $(DEFINES) $<
+
+version.cpp: FORCE
+       echo 'const char *source_version =' '"'`(svnversion $(shell pwd) 2>/dev/null) || echo Unknown`'";' > .vers.new ; diff .vers.new $@ > .vers.diff 2>&1 ; if test -s .vers.diff ; then mv -f .vers.new $@ ; fi ; rm -f .vers.new .vers.diff
+
+FORCE:
diff --git a/contrib/sasc-ng/dvbloopback/src/debug.h b/contrib/sasc-ng/dvbloopback/src/debug.h
new file mode 100644 (file)
index 0000000..9c7beba
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _DVBLB_DEBUG_H_
+#define _DVBLB_DEBUG_H_
+enum {
+  DVBDBG_FRONTEND = 0,
+  DVBDBG_DEMUX    = 2,
+  DVBDBG_DVR      = 4,
+  DVBDBG_CA       = 6,
+  DVBDBG_SID      = 8,
+  DVBDBG_IOCTL    = 10,
+  DVBDBG_RINGBUF  = 12,
+  DVBDBG_DSS      = 14,
+};
+extern unsigned int _dbglvl;
+int tmprintf(const char *plugin, const char *fmt, ...);
+
+#define dprintf(args...) tmprintf("", args)
+#define dprintf0(args...) tmprintf(DBG_NAME, args)
+#define dprintf1(args...) if(1 <= ((_dbglvl >> PLUGIN_ID) & 3)) \
+       tmprintf(DBG_NAME, args)
+#define dprintf2(args...) if(2 <= ((_dbglvl >> PLUGIN_ID) & 3)) \
+       tmprintf(DBG_NAME, args)
+#define dprintf3(args...) if(3 == ((_dbglvl >> PLUGIN_ID) & 3)) \
+       tmprintf(DBG_NAME, args)
+#else
+#ifdef PLUGIN_ID
+  #if (PLUGIN_ID % 2) == 1
+    #undef  dprintf1
+    #undef  dprintf2
+    #undef  dprintf3
+  #else
+    #if PLUGIN_ID > 30
+      #error PLUGIN_ID is too large (must be between 8 and 30)
+    #elif PLUGIN_ID < 8 && PLUGIN_ID != 0
+      #error PLUGIN_ID is too small (must be between 8 and 30)
+    #endif
+  #endif
+#endif //#ifdef PLUGIN_ID
+#endif
+
diff --git a/contrib/sasc-ng/dvbloopback/src/forward.c b/contrib/sasc-ng/dvbloopback/src/forward.c
new file mode 100644 (file)
index 0000000..e535a21
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <signal.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+
+#include <sched.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+
+#include <syslog.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "process_req.h"
+#include "plugin_getsid.h"
+#include "plugin_ringbuf.h"
+#include "msg_passing.h"
+
+struct t_adaptermap {
+  char name[128];
+  int adapter;
+  int used;
+};
+
+LIST_HEAD(plugin_cmdlist);
+unsigned int _dbglvl;
+void * listen_loop(void * arg);
+extern const char *source_version;
+pthread_attr_t default_attr;
+static pthread_t main_thread;
+static pthread_mutex_t tmprintf_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static char logfile[512] = "";
+static char pidfile[512] = "";
+enum {
+  LOGMODE_STDOUT = 0x01,
+  LOGMODE_SYSLOG = 0x02,
+  LOGMODE_LOGFILE = 0x04,
+};
+static struct {
+  unsigned int logmode;
+} logcfg = {LOGMODE_STDOUT};
+
+int tmprintf(const char *plugin, const char *fmt, ...)
+{
+  va_list args;
+  struct timeval tv;
+  struct tm tm;
+  unsigned long usec;
+  char stamp[32], logmsg[1024];
+  gettimeofday(&tv, NULL);
+  localtime_r(&tv.tv_sec, &tm);
+  strftime(stamp,sizeof(stamp),"%b %e %T",localtime_r(&tv.tv_sec, &tm));
+  usec = tv.tv_usec;
+  pthread_mutex_lock(&tmprintf_mutex);
+  /*
+   ** Display the remainder of the message
+   */
+  va_start(args,fmt);
+  vsnprintf(logmsg,sizeof(logmsg),fmt,args);
+  va_end(args); 
+  if(logcfg.logmode & LOGMODE_STDOUT)
+    fprintf(stdout, "%s.%03lu %s: %s", stamp, usec/1000, plugin, logmsg);
+  if(logcfg.logmode & LOGMODE_SYSLOG)
+    syslog(LOG_INFO, "%s: %s", plugin, logmsg);
+  pthread_mutex_unlock(&tmprintf_mutex);
+   return 0;
+}
+
+int get_adapters(struct t_adaptermap **adaptermap)
+{
+  int count = 0;
+  struct dirent **adptrlist;
+  int i, n, fd, adapter;
+  char frontend[30];
+  struct dvb_frontend_info fe;
+
+  n = scandir("/dev/dvb/", &adptrlist, NULL, alphasort);
+  if(n < 0) {
+    perror("scandir");
+    return 0;
+  }
+  *adaptermap = (struct t_adaptermap *)malloc(n * sizeof(struct t_adaptermap));
+  for(i = 0; i < n; i++) {
+    if(1 == sscanf(adptrlist[i]->d_name, "adapter%d", &adapter)) {
+      snprintf(frontend, 30, "/dev/dvb/adapter%d/frontend0", adapter);
+      fd = open(frontend, O_RDONLY);
+      if(fd >= 0) {
+        if(ioctl(fd, FE_GET_INFO, &fe) >=0) {
+          (*adaptermap)[count].adapter = adapter;
+          snprintf((*adaptermap)[count].name, 128, "%s", fe.name);
+          (*adaptermap)[count].used = 0;
+          count++;
+        }
+        close(fd);
+      }
+    }
+    free(adptrlist[i]);
+  }
+  free(adptrlist);
+  if(count == 0)
+    free(*adaptermap);
+  return count;
+}
+
+void show_help()
+{
+  struct list_head *ptr;
+  int i;
+  printf("sasc-ng [options] -j <real>:<virtual> ...\n"); 
+  printf("Version: %s-%s\n\n", RELEASE_VERSION, source_version);
+  printf("   -j//--join <real>:<virt>\n");
+  printf("                     : Connect real and loopback dvb adapters\n");
+  printf("                       This option can be specified multiple times\n");
+  printf("                       to support multiple loopback adapters.\n");
+  printf("                       NOTE: <real> can be either the adapter number\n");
+  printf("                       or the adapter name (see --identify)\n");
+  printf("Optional args:\n");
+  printf("   -h/--help         : This help message\n");
+  printf("   -i/--identify     : List all available adpaters\n");
+  printf("   -b/--buffer <num> : Set size of read buffer (default: 2M)\n");
+  printf("   -d/--debug <num>  : Set debug level (this is a 32bit bitmask)\n");
+  printf("   -l/--log          : Set log file for output\n");
+  printf("   -n/--noload <num> : Don't load module <num>. Use with care!\n");
+  printf("   -o/--osd          : Enable passthrough for cards with mpeg2 decoders\n");
+  printf("   -p/--port <num>   : Set debug port to listen on (default 5456)\n");
+  printf("   -D/--daemon       : Enable daemon/background mode (logs to syslog unless -l is set)\n");
+  printf("   -P/--pidfile      : write pid to file\n");
+  list_for_each(ptr, &plugin_cmdlist) {
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+    if(cmd->parse_args) {
+      printf("\nOptions for %s module:\n", cmd->name);
+      cmd->parse_args(ARG_HELP);
+    }
+  }
+  printf("\nDebug bitmask:\n");
+  printf("   0x%08x        : %s\n", 3, dnames[3]);
+  printf("   0x%08x        : %s\n", 3<<2, dnames[4]);
+  printf("   0x%08x        : %s\n", 3<<4, dnames[5]);
+  printf("   0x%08x        : %s\n", 3<<6, dnames[6]);
+  for(i = 8; i < 32; i+=2) {
+    list_for_each(ptr, &plugin_cmdlist) {
+      struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+      if(cmd->plugin == i) {
+        printf("   0x%08x        : %s\n", 3<<i, cmd->name);
+        break;
+      }
+    }
+  }
+}
+
+int init_osd(int real, int virt) {
+  FILE *FH;
+  char str[256];
+  int tmp;
+  int link[] = {0, 1, 8}; //video, audio, osd
+  //Link real adapter to virtual adapter
+  sprintf(str, "/proc/dvbloopback/adapter%d/adapter", virt);
+  FH = fopen(str, "w");
+  if(! FH)
+    return 0;
+  fprintf(FH, "%d", real);
+  fclose(FH);
+  FH = fopen(str, "r");
+  fscanf(FH,"%d", &tmp);
+  fclose(FH);
+  if(tmp != real) {
+    dprintf("Could not setup adapter link: %d != %d\n", real, tmp);
+    return 0;
+  }
+  for(int i=0; i < 3; i++) {
+    sprintf(str, "/proc/dvbloopback/adapter%d/%s", virt, dnames[link[i]]);
+    FH = fopen(str, "w");
+    if(! FH)
+      return 0;
+    fprintf(FH, "1");
+    fclose(FH);
+    FH = fopen(str, "r");
+    fscanf(FH,"%d", &tmp);
+    fclose(FH);
+    if(tmp != 1) {
+      dprintf("Could not setup device %s: %d != 1\n", dnames[link[i]], tmp);
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int log_rotate(int report_error)
+{
+  /* http://www.gossamer-threads.com/lists/mythtv/dev/110113 */
+
+  int new_logfd = open(logfile, O_WRONLY|O_CREAT|O_APPEND|O_SYNC|O_LARGEFILE, 0664);
+  if (new_logfd < 0) {
+    // If we can't open the new logfile, send data to /dev/null
+    if (report_error) {
+      fprintf(stderr, "cannot open logfile %s\n",logfile);
+      return 0;
+    }
+    new_logfd = open("/dev/null", O_WRONLY);
+    if (new_logfd < 0) {
+      // There's not much we can do, so punt.
+      return 0;
+    }
+  }
+  while (dup2(new_logfd, 1) < 0 && errno == EINTR)
+    ;
+  while (dup2(new_logfd, 2) < 0 && errno == EINTR)
+    ;
+  while (close(new_logfd) < 0 && errno == EINTR)
+    ;
+  return 1;
+}
+
+void log_rotate_handler(int)
+{
+  log_rotate(0);
+}
+
+static void exit_handler(int type)
+{
+  if(type == SIGTERM && ! pthread_equal(pthread_self(), main_thread)) {
+    pthread_exit(NULL);
+    return;
+  }
+  //take care of main message handler.  Main thread will do the rest
+  msg_terminate(MSG_LOW_PRIORITY);
+}
+
+int main(int argc, char *argv[])
+{
+  unsigned long bufsize = 2000000;
+  struct parser_adpt pc_all[8];
+  struct common_data common[8];
+  struct list_head *ptr;
+  pthread_t msg_highpri_thread;
+  pthread_t socket_thread;
+  int virt_adapt[8], real_adapt[8], adapter_cnt=0;
+  unsigned long debug_port = 5456; 
+  int longopt = 0;
+  int c, Option_Index = 0;
+  int illegal_opt = 0;
+  int use_osd = 0;
+  struct t_adaptermap *adptrmap;
+  int adptrmap_count;
+  struct option *LongOpts;
+  int optcount;
+  int use_daemon = 0;
+  static struct option Long_Options[] = {
+    {"buffer", 1, 0, 'b'},
+    {"debug", 1, 0, 'd'},
+    {"help", 0, 0, 'h'},
+    {"identify", 0, 0, 'i'},
+    {"join", 1, 0, 'j'},
+    {"noload", 1, 0, 'n'},
+    {"osd", 0, 0, 'o'},
+    {"port", 0, 0, 'p'},
+    {"daemon", 0, 0, 'D'},
+    {"log", 1, 0, 'l'},
+    {"pidfile", 1, 0, 'P'},
+  };
+
+  pthread_attr_init(&default_attr);
+  pthread_attr_setstacksize(&default_attr, PTHREAD_STACK_MIN + 0x4000);
+  main_thread = pthread_self();
+
+  _dbglvl = 0;
+  //enable unbuffered stdout 
+  setbuf(stdout, 0); 
+
+  //enable a core dump file
+  struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY};
+  setrlimit(RLIMIT_CORE, &rl);
+
+  adptrmap_count = get_adapters(&adptrmap);
+  if(adptrmap_count == 0) {
+    dprintf("Didn't find any DVB adapters!\n");
+    return(-1);
+  }
+
+  //get all plugin args
+  optcount = sizeof(Long_Options) / sizeof(struct option);
+  LongOpts = (struct option *)malloc(sizeof(struct option) * (optcount+1));
+  memcpy(LongOpts, Long_Options, sizeof(Long_Options));
+  
+  list_for_each(ptr, &plugin_cmdlist) {
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+    if(cmd->parse_args) {
+      struct option *optstr;
+      optstr = cmd->parse_args(ARG_INIT);
+      if(! optstr)
+        continue;
+      while(optstr->name) {
+        LongOpts = (struct option *)realloc(LongOpts,
+                                         sizeof(struct option) * (optcount+2));
+        memcpy(LongOpts+optcount, optstr, sizeof(struct option));
+        optstr++;
+        optcount++;
+      }
+    }
+  }
+  memset(LongOpts+optcount, 0, sizeof(struct option));
+
+  while (1) {
+
+    c = getopt_long(argc, argv, "b:d:hij:l:n:op:DP:", LongOpts, &Option_Index); 
+    if (c == EOF)
+      break;
+    switch (c) {
+      case 'b':
+        {
+          char type;
+          int l = sscanf(optarg, "%lu%c", &bufsize,&type);
+          if(l == 2) {
+            if (type == 'M') {
+              bufsize *= 1000000;
+            } else if (type == 'k') {
+              bufsize *= 1024;
+            }
+            break;
+          }
+        }
+      case 'd':
+        _dbglvl = strtol(optarg, NULL, 0);
+        break;
+      case 'D':
+        use_daemon = 1;
+        break;
+      case 'h':
+        show_help();
+        return(0);
+        break;
+      case 'i':
+        {
+          int i;
+          for(i = 0; i < adptrmap_count; i++)
+            printf("%d: %s\n", adptrmap[i].adapter, adptrmap[i].name);
+          return(0);
+        }
+      case 'j':
+        {
+          int r, v, len, i;
+          char *v_chr, *r_end;
+          if((v_chr = strrchr(optarg, ':')) == NULL) {
+            illegal_opt++;
+            break;
+          }
+          v = strtol(v_chr+1, NULL, 0);
+          r = strtol(optarg, &r_end, 0);
+          len = v_chr - optarg;
+          if (len > 128)
+            len = 128;
+          for(i = 0; i < adptrmap_count; i++) {
+            if((r_end != v_chr && strncmp(optarg, adptrmap[i].name, len) == 0) || (r_end == v_chr && adptrmap[i].adapter == r)) { 
+              if(adptrmap[i].used) {
+                dprintf("Adapter %d is already in use\n", r);
+                return(-1);
+              }
+              adptrmap[i].used = 1;
+              real_adapt[adapter_cnt] = adptrmap[i].adapter;
+              virt_adapt[adapter_cnt] = v;
+              adapter_cnt++;
+              break;
+            }
+          }
+          if(i == adptrmap_count) {
+            dprintf("Could not find adapter for %s\n", optarg);
+            return(-1);
+          }
+          break;
+        }
+      case 'l':
+        strncpy(logfile, optarg, sizeof(logfile)-1);
+        logfile[sizeof(logfile)-1] = 0;
+        break;
+      case 'n':
+        {
+          int val = atoi(optarg);
+          list_for_each(ptr, &plugin_cmdlist) {
+            struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+            if(cmd->plugin == val) {
+              list_del(&cmd->list);
+              break;
+            }
+          }
+          break;
+        }
+      case 'o':
+        use_osd = 1;
+        break;
+      case 'p': 
+        debug_port = atoi(optarg); 
+        break; 
+      case 'P':
+        strncpy(pidfile, optarg, sizeof(pidfile)-1);
+        pidfile[sizeof(pidfile)-1] = 0;
+        break;
+      case 0:
+        if(! longopt) {
+          list_for_each(ptr, &plugin_cmdlist) {
+            struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+            if(cmd->parse_args) {
+              cmd->parse_args(ARG_PARSE);
+            }
+          }
+          break;
+        }
+        break;
+      case '?':
+        illegal_opt++;
+        break;
+    }
+  }
+  free(LongOpts);
+  free(adptrmap);
+
+  if (illegal_opt) {
+    dprintf("Illegal options specified.  Aborting!\n");
+    return(-1);
+  }
+  if (adapter_cnt == 0) {
+    show_help();
+    return(-1);
+  }
+
+  if(bufsize < 1000000 || bufsize > 20000000) {
+    dprintf("Buffer size is out of range.  1000000 <= size <= 20000000\n");
+    return(-1);
+  }
+
+  if (use_daemon) {
+    if(logfile[0] == 0) {
+      logcfg.logmode = LOGMODE_SYSLOG;
+      openlog(argv[0],LOG_PID,LOG_LOCAL0);
+    }
+    if (daemon(0,1) < -1) {
+      dprintf("Couldnt go into background.  Aborting!\n");
+      return(-1);
+    }
+  }
+
+  if (pidfile[0] != 0 ) {
+    FILE *f = fopen(pidfile,"wt");
+    if (f) {
+      fprintf(f,"%d\n",getpid());
+      fclose(f);
+    }
+  }
+
+  if (logfile[0] != 0 ) {
+    if (! log_rotate(1))
+      fprintf(stderr, "cannot open logfile; using stdout/stderr\n");
+    else
+      signal(SIGHUP, &log_rotate_handler);
+  }
+
+  dprintf("Version: %s-%s\n", RELEASE_VERSION, source_version);
+
+  //Need to init msg_loop early in case some plugins setup messages during init
+  msg_loop_init();
+
+  //setup exit_handler afer msg_loop_init!
+  signal(SIGTERM, &exit_handler); 
+  signal(SIGQUIT, &exit_handler); 
+  signal(SIGINT, &exit_handler);
+
+  for(int i = 0; i < adapter_cnt; i++) {
+    if (use_osd) {
+      if(init_osd(real_adapt[i], virt_adapt[i])) {
+        dprintf("Initiailized osd on adapter pair %d:%d\n",
+                 real_adapt[i], virt_adapt[i]);
+      }
+    }
+    common[i].virt_adapt = virt_adapt[i];
+    common[i].real_adapt = real_adapt[i];
+    common[i].private_data = NULL;
+    common[i].buffersize = bufsize;
+    pthread_mutex_init(&common[i].cond_lock, NULL);
+    pthread_cond_init(&common[i].cond, NULL);
+    common[i].cond_count = 0;
+    init_parser(&pc_all[i], &common[i]);
+    if(! pc_all[i].frontend || ! pc_all[i].demux || ! pc_all[i].dvr) {
+      dprintf("Could not connect to loopback device %d\n", virt_adapt[i]);
+      dprintf("Are you sure you have loaded the dvbloopback module\n");
+      dprintf("properly and/or used the correct values to the '-j' switch\n");
+      return(-1);
+    }
+    list_for_each(ptr, &plugin_cmdlist) {
+      struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+      if(cmd->connect)
+        cmd->connect(&pc_all[i]);
+    }
+
+  }
+
+  for(int i = 0; i < adapter_cnt; i++) {
+    launch_processors(&pc_all[i]);
+    pthread_mutex_lock(&common[i].cond_lock);
+    while (common[i].cond_count > 0) {
+      pthread_cond_wait(&common[i].cond, &common[i].cond_lock);
+    }
+    pthread_mutex_unlock(&common[i].cond_lock);
+  }
+
+  list_for_each(ptr, &plugin_cmdlist) {
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+    if(cmd->launch)
+      cmd->launch();
+  }
+  pthread_create(&socket_thread, &default_attr, listen_loop,
+                 (void *)debug_port); 
+  pthread_create(&msg_highpri_thread, &default_attr, msg_loop,
+                 (void *) MSG_HIGH_PRIORITY);
+
+  msg_loop((void *)MSG_LOW_PRIORITY);
+  //Shutting down
+  msg_terminate(MSG_HIGH_PRIORITY);
+  pthread_join(msg_highpri_thread, NULL);
+  pthread_kill(socket_thread, SIGTERM);
+  pthread_join(socket_thread, NULL);
+  //Now kill the device handlers
+  for(int i = 0; i < adapter_cnt; i++)
+    shutdown_parser(&pc_all[i]);
+  //Lastly kill the plugins
+  list_for_each(ptr, &plugin_cmdlist) { 
+    struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd); 
+    if(cmd->shutdown) 
+      cmd->shutdown(); 
+  }
+  dprintf("Exiting...\n");
+  fflush(stdout);
+  fflush(stderr);
+  return 0;
+}
+
+void * listen_loop(void * arg)
+{
+    unsigned long port = (unsigned long)arg;
+    struct list_head *ptr;
+    int sockfd, opt = 0;
+    unsigned int len;
+    char buf[256];
+    struct sockaddr_in serv_addr, cli_addr;
+
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if(sockfd < 0) {
+      perror("Socket creation failed");
+      return NULL;
+    }
+    dprintf("Listening on port %d\n", port); 
+    len = sizeof(opt);
+    getsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
+               (socklen_t *)&len);
+    opt |= 1;
+    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = INADDR_ANY;
+    serv_addr.sin_port = htons(port);
+
+    if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+      perror("Failed to bind to debug port:");
+      tmprintf("DEBUG","The debug port will not be available");
+      return NULL;
+    }
+    if(listen(sockfd, 1) < 0) {
+      perror("Listen failed:");
+      return NULL;
+    }
+    while(1) {
+      int connfd;
+      int found;
+      len =  sizeof(cli_addr);
+      connfd = accept(sockfd, (struct sockaddr *)&cli_addr, (socklen_t *)&len);
+      if(connfd < 0) {
+        perror("Connection failed:");
+        continue;
+      }
+      len = read(connfd, buf, 256);
+      found = 0;
+      list_for_each(ptr, &plugin_cmdlist) {
+        struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+        if(cmd->user_msg) {
+          if(strncasecmp(buf, cmd->name, strlen(cmd->name)) == 0 && buf[strlen(cmd->name)] == ' ') {
+            char *p;
+            printf("xx %s yy\n", buf);
+            if((p = strchr(buf, '\r')) || (p = strchr(buf, '\n'))) { *p = '\0'; }
+            printf("XX %s YY\n", buf);
+            cmd->user_msg(&buf[strlen(cmd->name)+1]);
+            found = 1;
+            break;
+          }
+        }
+      }
+      if(! found) {
+        if(len < sizeof(int)) {
+          tmprintf("DEBUG","Got bad command\n");
+        } else {
+          /* This isn't thread safe, but that's ok*/
+          _dbglvl = *(unsigned int *)&buf;
+          tmprintf("DEBUG","Got debug value: %u\n", _dbglvl);
+        }
+      }
+      close(connfd);
+    }
+}
diff --git a/contrib/sasc-ng/dvbloopback/src/list.h b/contrib/sasc-ng/dvbloopback/src/list.h
new file mode 100644 (file)
index 0000000..e3ebbab
--- /dev/null
@@ -0,0 +1,175 @@
+#ifndef _LIST_
+#define _LIST_
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+struct list_head {
+       struct list_head *next, *prev;
+        unsigned int priority;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name), (unsigned int)-1 }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); (ptr)->priority = (unsigned int)-1;\
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *_new,
+                             struct list_head *_prev,
+                             struct list_head *_next)
+{
+       _next->prev = _new;
+       _new->next = _next;
+       _new->prev = _prev;
+       _prev->next = _new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *_new, struct list_head *_head)
+{
+       __list_add(_new, _head, _head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *_new,
+                                 struct list_head *_head)
+{
+       __list_add(_new, _head->prev, _head);
+}
+
+static inline void list_add_priority(struct list_head *_new,
+                                     struct list_head *_head,
+                                     int priority)
+{
+       struct list_head *pos = _head;
+        _new->priority = priority;
+       for (pos = _head->next; pos != _head; pos = pos->next)
+         if((unsigned int)priority <= pos->priority) {
+            __list_add(_new, pos->prev, pos);
+            return;
+          }
+        __list_add(_new, _head, _head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = (struct list_head *)LIST_POISON1;
+       entry->prev = (struct list_head *)LIST_POISON2;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type) ((type *)ptr)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+        for (pos = (head)->next, n = pos->next; pos != (head); \
+                pos = n, n = pos->next)
+
+#define list_add_l(_var, _ll, _lock) {\
+  pthread_mutex_lock(_lock);           \
+  list_add(_var, _ll);                 \
+  pthread_mutex_unlock(_lock);         \
+}
+  
+#define pop_entry_from_queue_l(_var, _ll, _type, _lock) {\
+  pthread_mutex_lock(_lock);           \
+  if(list_empty(_ll))  {                       \
+    _var = (_type *)calloc(1, sizeof(_type));  \
+  } else {                                     \
+    _var = list_entry((_ll)->next, _type);     \
+    list_del(&_var->list);                     \
+  }                                            \
+  pthread_mutex_unlock(_lock);         \
+}
+
+#define pop_entry_from_queue(_var, _ll, _type) {\
+  if(list_empty(_ll))  {                       \
+    _var = (_type *)calloc(1, sizeof(_type));  \
+  } else {                                     \
+    _var = list_entry((_ll)->next, _type);     \
+    list_del(&_var->list);                     \
+  }                                            \
+}
+
+#define ll_find_elem(elem, lhead, item, value, type) { \
+  struct list_head *lptr;                              \
+  type *ptr;                                           \
+  elem = NULL;                                         \
+  list_for_each(lptr, &lhead) {                                \
+    ptr = list_entry(lptr, type);                      \
+    if(ptr->item == value)                             \
+      elem = ptr;                                      \
+  }                                                    \
+}
+
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/src/messages.h b/contrib/sasc-ng/dvbloopback/src/messages.h
new file mode 100644 (file)
index 0000000..1bc2226
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef MSG_MESSAGES
+#define MSG_MESSAGES
+#define MSG_ADDSID    0x01
+#define MSG_REMOVESID 0x02 
+#define MSG_RESETSID  0x03
+#define MSG_RINGBUF   0x0A
+#define MSG_RINGCLOSE 0x0B
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/src/msg_passing.c b/contrib/sasc-ng/dvbloopback/src/msg_passing.c
new file mode 100644 (file)
index 0000000..0f4b637
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sched.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+#include "process_req.h"
+#include "plugin_getsid.h"
+#include "plugin_ringbuf.h"
+#include "msg_passing.h"
+
+struct msgctrl {
+  struct list_head msglist;
+  struct list_head empty_queue;
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+};
+static struct msgctrl message_control[MSG_HIGH_PRIORITY+1];
+
+void msg_loop_init()
+{
+  struct msgctrl *msgctrl;
+  int priority;
+  for (priority=0; priority <= MSG_HIGH_PRIORITY; priority++) {
+    msgctrl = &message_control[priority];
+    bzero(msgctrl, sizeof(struct msgctrl));
+    INIT_LIST_HEAD(&msgctrl->msglist);
+    INIT_LIST_HEAD(&msgctrl->empty_queue);
+    pthread_mutex_init(&msgctrl->mutex, NULL);
+    pthread_cond_init(&msgctrl->cond, NULL);
+  }
+}
+void * msg_loop(void * arg)
+{
+  unsigned long priority = (unsigned long)arg;
+  struct list_head *ptr;
+  struct msgctrl *msgctrl;
+
+  if (priority == MSG_HIGH_PRIORITY) {
+    struct sched_param param;
+    param.sched_priority = sched_get_priority_min(SCHED_FIFO);
+    if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)) {
+      perror("sched_setscheduler");
+    }
+  }
+  if (priority > MSG_HIGH_PRIORITY) {
+    tmprintf("MSG", "Invalid priority: %lu\n", priority);
+    exit(-1);
+  }
+
+  msgctrl = &message_control[priority];
+  pthread_mutex_lock(&msgctrl->mutex);
+  while(1) { //main loop
+    struct timespec ts = {0, 0};
+    ts.tv_sec = time(NULL) + 60*60; //1 hour
+    while(1) { //iterate over all elements in queue
+      struct msg *msg;
+      time_t now = time(NULL);
+      int orig_type;
+
+      //We don't need the 'safe' variant because we always break after removing
+      //an element
+      list_for_each(ptr, &msgctrl->msglist) {
+        msg = list_entry(ptr, struct msg);
+        if(now < msg->next_run) {
+          if (msg->next_run < ts.tv_sec)
+            ts.tv_sec = msg->next_run;
+          continue;
+        }
+        list_del(&msg->list);
+        break;
+      }
+      //If we've seen all elements on the queue, or the queue is empty,
+      //we are done
+      if(ptr == &msgctrl->msglist)
+        break;
+      pthread_mutex_unlock(&msgctrl->mutex);
+
+      if(msg->type == MSG_TERMINATE)
+        return NULL;
+
+      orig_type = msg->type;
+      list_for_each(ptr, &plugin_cmdlist) {
+        struct plugin_cmd *cmd = list_entry(ptr, struct plugin_cmd);
+        if(cmd->message)
+          cmd->message(msg, priority);
+        if(msg->type == MSG_PROCESSED)
+          break;
+      }
+      if(msg->type != MSG_PROCESSED) {
+        tmprintf("MSG", "Got unprocessed message type: %d\n", msg->type);
+      }
+      if (msg->recurring) {
+        msg->next_run = time(NULL) + msg->recurring;
+        msg->type = orig_type;
+        if (msg->next_run < ts.tv_sec)
+          ts.tv_sec = msg->next_run;
+        pthread_mutex_lock(&msgctrl->mutex);
+        list_add_tail(&msg->list, &msgctrl->msglist);
+      } else {
+        pthread_mutex_lock(&msgctrl->mutex);
+        list_add(&msg->list, &msgctrl->empty_queue);
+      }
+    }
+    pthread_cond_timedwait(&msgctrl->cond, &msgctrl->mutex, &ts);
+  }
+}
+
+void msg_remove_type_from_list(unsigned int priority, int type, int id,
+                               void (*cmd)(void *)) {
+  struct list_head *ptr;
+  struct msgctrl *msgctrl;
+
+  if (priority > MSG_HIGH_PRIORITY)
+    return;
+  msgctrl = &message_control[priority];
+  pthread_mutex_lock(&msgctrl->mutex);
+  list_for_each(ptr, &msgctrl->msglist) {
+    struct msg *msg = list_entry(ptr, struct msg);
+    if (msg->type == type && msg->id == id) {
+      list_del(&msg->list);
+      if(cmd)
+        cmd(msg->data);
+      list_add(&msg->list, &msgctrl->empty_queue);
+      break;
+    }
+  }
+  pthread_mutex_unlock(&msgctrl->mutex);
+}
+
+static struct msg *msg_add_type_to_list(unsigned int priority, int type, int id,
+                                        int replace) {
+  struct list_head *ptr;
+  struct msg *msg;
+  struct msgctrl *msgctrl;
+
+  msgctrl = &message_control[priority];
+
+  if(replace) {
+    //if a message of this type is already on the list
+    list_for_each(ptr, &msgctrl->msglist) {
+      msg = list_entry(ptr, struct msg);
+      if (msg->type == type && msg->id == id) {
+        return msg;
+      }
+    }
+  }
+  if(! list_empty(&msgctrl->empty_queue)) {
+    msg = list_entry(msgctrl->empty_queue.next, struct msg);
+    list_del(&msg->list);
+  } else {
+    msg = (struct msg *)malloc(sizeof(struct msg));
+  }
+  list_add_tail(&msg->list, &msgctrl->msglist);
+  msg->type = type;
+  msg->id = id;
+  msg->recurring = 0;
+  msg->next_run = 0;
+  return msg;
+}
+
+void msg_send_replace(unsigned int priority, int type, int id, void *data,
+                      int delay, unsigned int flags)
+{
+  struct msg *msg;
+  struct msgctrl *msgctrl;
+
+  if (priority > MSG_HIGH_PRIORITY)
+    return;
+  msgctrl = &message_control[priority];
+
+  pthread_mutex_lock(&msgctrl->mutex);
+  msg = msg_add_type_to_list(priority, type, id, flags & MSG_REPLACE);
+  msg->data = data;
+  if (delay)
+    msg->next_run = delay + time(NULL);
+  if (flags & MSG_RECURRING)
+    msg->recurring = delay;
+  pthread_cond_signal(&msgctrl->cond);
+  pthread_mutex_unlock(&msgctrl->mutex);
+}
+
+void msg_terminate(unsigned int priority)
+{
+  struct msg *msg;
+  struct msgctrl *msgctrl;
+  msgctrl = &message_control[priority];
+  pthread_mutex_lock(&msgctrl->mutex);
+  if(! list_empty(&msgctrl->empty_queue)) {
+    msg = list_entry(msgctrl->empty_queue.next, struct msg);
+    list_del(&msg->list);
+  } else {
+    msg = (struct msg *)malloc(sizeof(struct msg));
+  }
+  msg->type = MSG_TERMINATE;
+  msg->id = 0;
+  msg->recurring = 0;
+  msg->next_run = 0;
+  list_add(&msg->list, &msgctrl->msglist);
+  pthread_cond_signal(&msgctrl->cond);
+  pthread_mutex_unlock(&msgctrl->mutex);
+}
diff --git a/contrib/sasc-ng/dvbloopback/src/msg_passing.h b/contrib/sasc-ng/dvbloopback/src/msg_passing.h
new file mode 100644 (file)
index 0000000..a4a54d2
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _MSG_PASSING_H_
+#define _MSG_PASSING_H_
+#include "list.h"
+
+#define MSG_PROCESSED -1
+#define MSG_TERMINATE -2
+#define MSG_LOW_PRIORITY 0
+#define MSG_HIGH_PRIORITY 1
+
+#define MSG_REPLACE   0x01
+#define MSG_RECURRING 0x02
+
+struct msg {
+  struct list_head list;
+  int type;
+  int id;
+  int recurring;
+  time_t next_run;
+  void *data;
+};
+
+extern void msg_loop_init();
+extern void *msg_loop(void *arg);
+extern void msg_remove_type_from_list(unsigned int priority, int type, int id,
+                               void (*cmd)(void *msg));
+extern void msg_send_replace(unsigned int priority, int type, int id,
+                       void *data, int delay, unsigned int flags);
+extern void msg_terminate(unsigned int priority);
+#define msg_send(a,b,c,d) msg_send_replace(a,b,c,d,0,0)
+#define msg_replace(a,b,c,d) msg_send_replace(a,b,c,d,0,MSG_REPLACE)
+#define msg_delayed(a,b,c,d,e,f) msg_send_replace(a,b,c,d,e, \
+                                                  ((f)? MSG_RECURRING : 0)
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_dss.c b/contrib/sasc-ng/dvbloopback/src/plugin_dss.c
new file mode 100644 (file)
index 0000000..a01cc01
--- /dev/null
@@ -0,0 +1,1243 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifdef USE_DSS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <netinet/in.h>
+
+#include "list.h"
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include "msg_passing.h"
+#include "process_req.h"
+
+#define PLUGIN_ID 14
+#define DBG_NAME "DSS"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+#define DSS_SYMBOLRATE 22211000
+#define DSS_PMTPID 0x20
+#define DSS_NITPID 0x10
+#define DSS_NETWORKID 0x1f0f
+#define TS_SIZE 188
+#define PTS_ONLY         0x80
+#define PTS_DTS          0xC0
+
+struct dss_hdr {
+  int packet_framing;
+  int bundle_boundary;
+  int control_flag;
+  int control_sync;
+  int scid;
+  int continuity_counter;
+  int header_designator_type;
+  int header_designator_id;
+  int toggle_flag;
+};
+
+struct dss_pkt {
+  struct list_head list;
+  struct dss_hdr hdr;
+  int64_t        rts;
+  uint8_t        buf[184+127+1];
+  int            buf_len;
+  int            cont;
+  int            start;
+  int            fd;
+  //Audio related stuff
+  int            is_video;
+  int            audio_len;
+  int            audio_packlen;
+  uint8_t        audio_pack[16];
+  int            audio_tmp_len;
+  uint8_t        audio_tmp[4];
+};
+
+struct cir {
+  int pip_flag;
+  int dip_flag;
+  int use_heap;
+  int long_name_flag;
+  int logo_flag;
+  int scid_size_flag;
+  int service_paradigm_indicator;
+  int number_of_scids;
+  int pip_transponder;
+  int dip_transponder;
+  int channel_transponder;
+  int virt_channel_number;
+  int network_id;
+  uint8_t short_name[4];
+  int scid[16];
+  int scid_type[16];
+};
+
+struct cssm {
+  int number_of_channels;
+  int segment_size;
+  int buf_len;
+  uint8_t buf[65536];
+};
+
+struct segment_list {
+  int number_of_segments;
+  int up_scid;
+  int pip_scid;
+  int dip_scid;
+  int default_network_id;
+  int provider_id;
+  int checksum;
+};
+
+struct mpg {
+  struct segment_list segl;
+  struct cssm cssm;
+  struct cir *cir;
+  int cir_len;
+  int num_channels;
+  int current_transponder;
+};
+
+struct dss {
+  struct list_head list;
+  int adapt;
+  pthread_mutex_t lock;
+  struct list_head pkt_ll; //struct dss_pkt
+  struct mpg mpg;
+  uint32_t old_standard;
+  int is_dss;
+  uint8_t *buf;
+  uint32_t buf_len;
+  uint32_t buf_used;
+  uint8_t unused[131];
+  uint8_t unused_len;
+  
+};
+
+static int dss_opt = 0;
+static int opt_enable = 0;
+static char * opt_file = NULL;
+static struct option dss_Opts[] = {
+  {"dss-enable", 0, &dss_opt, 'e'},
+  {"dss-file", 1, &dss_opt, 'f'},
+  {0, 0, 0, 0},
+};
+
+LIST_HEAD(dsslist);
+LIST_HEAD(dsspkt_empty_queue);
+
+//taken and adapted from libdtv, (c) Rolf Hakenes
+// CRC32 lookup table for polynomial 0x04c11db7
+static unsigned int 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};
+
+unsigned int crc32_04c11db7 (const unsigned char *d, int len, unsigned int crc)
+{
+   register int i;
+   const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
+
+   for (i=0; i<len; i++)
+      crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
+
+   return crc;
+}
+static void get_pespts(uint8_t *spts,uint8_t *pts)
+{
+
+        pts[0] = 0x01 |
+                ((spts[0] & 0xC0) >>5);
+        pts[1] = ((spts[0] & 0x3F) << 2) |
+                ((spts[1] & 0xC0) >> 6);
+        pts[2] = 0x01 | ((spts[1] & 0x3F) << 2) |
+                ((spts[2] & 0x80) >> 6);
+        pts[3] = ((spts[2] & 0x7F) << 1) |
+                ((spts[3] & 0x80) >> 7);
+        pts[4] = 0x01 | ((spts[3] & 0x7F) << 1);
+}
+static int write_pes_header(uint8_t id, int length , uint64_t PTS, uint64_t DTS,
+                    uint8_t *obuf, int stuffing, uint8_t ptsdts)
+{
+       uint8_t le[2];
+       uint8_t dummy[3];
+       uint8_t *pts;
+       uint8_t ppts[5];
+       uint32_t lpts;
+       uint8_t *dts;
+       uint8_t pdts[5];
+       uint32_t ldts;
+       int c;
+       uint8_t headr[3] = {0x00, 0x00, 0x01};
+       
+       lpts = htonl((PTS/300ULL) & 0x00000000FFFFFFFFULL);
+       pts = (uint8_t *) &lpts;
+       get_pespts(pts,ppts);
+       if ((PTS/300ULL) & 0x0000000100000000ULL) ppts[0] |= 0x80;
+
+       ldts = htonl((DTS/300ULL) & 0x00000000FFFFFFFFULL);
+       dts = (uint8_t *) &ldts;
+       get_pespts(dts,pdts);
+       if ((DTS/300ULL) & 0x0000000100000000ULL) pdts[0] |= 0x80;
+
+       c = 0;
+       memcpy(obuf+c,headr,3);
+       c += 3;
+       memcpy(obuf+c,&id,1);
+       c++;
+
+       le[0] = 0;
+       le[1] = 0;
+       length -= 6;
+
+       le[0] |= ((uint8_t)(length >> 8) & 0xFF); 
+       le[1] |= ((uint8_t)(length) & 0xFF); 
+       memcpy(obuf+c,le,2);
+       c += 2;
+
+       dummy[0] = 0x80;
+       dummy[1] = 0;
+       dummy[2] = stuffing;
+       
+       if (ptsdts == PTS_ONLY){
+               dummy[2] += 5;
+               dummy[1] |= PTS_ONLY;
+               ppts[0] |= 0x20;
+       } else  if (ptsdts == PTS_DTS){
+               dummy[2] += 10;
+               dummy[1] |= PTS_DTS;
+               ppts[0] |= 0x30;
+               pdts[0] |= 0x10;
+       }
+               
+
+       memcpy(obuf+c,dummy,3);
+       c += 3;
+
+       if (ptsdts == PTS_ONLY){
+               memcpy(obuf+c,ppts,5);
+               c += 5;
+       } else if ( ptsdts == PTS_DTS ){
+               memcpy(obuf+c,ppts,5);
+               c += 5;
+               memcpy(obuf+c,pdts,5);
+               c += 5;
+       }
+
+       memset(obuf+c,0xFF,stuffing);
+       c += stuffing;
+
+       return c;
+}
+static int write_ts_header(int pid, int payload_start, int count,
+                   int64_t SCR, uint8_t *obuf, int stuff)
+{
+       int c = 0;
+       uint8_t *scr;
+       uint32_t lscr;
+       uint16_t scr_ext = 0;
+
+       obuf[c++] = 0x47;
+       obuf[c++] = (payload_start ? 0x40 : 0x00) | ((pid >> 8) & 0x1f);
+       obuf[c++] = pid & 0xff;
+       obuf[c++] = ((SCR >= 0 || stuff) ? 0x30 : 0x10) | count;
+       if (SCR >= 0|| stuff) {
+               if (stuff)
+                       stuff--;
+               int size = stuff;
+               unsigned char flags = 0;
+               if(SCR >= 0) {
+                       if(size < 7)
+                               size = 7;
+                       flags |= 0x10;
+               }
+               obuf[c++] = size;
+               if(size) {
+                       obuf[c++] = flags;
+                       size--;
+               }
+               if(SCR >= 0) {
+                       uint8_t bit;
+                       lscr = (uint32_t) ((SCR/300ULL) & 0xFFFFFFFFULL);
+                       bit = (lscr & 0x01) << 7;
+                       lscr = htonl(lscr >> 1);
+
+                       scr = (uint8_t *) &lscr;
+                       scr_ext = (uint16_t) ((SCR%300ULL) & 0x1FFULL);
+                       obuf[c++] = scr[0];
+                       obuf[c++] = scr[1];
+                       obuf[c++] = scr[2];
+                       obuf[c++] = scr[3];
+                       obuf[c++] = bit | 0x7e | (scr_ext >> 8);
+                       obuf[c++] = scr_ext & 0xff;
+                       size -= 6;
+               }
+               while(size-- > 0)
+                       obuf[c++] = 0xff;
+       }
+       return c;
+}
+//extract PTS from Picture frame
+int get_ptsdts(unsigned char *buf, int len, uint64_t *pts, uint64_t *dts)
+{
+  int picture_coding_type;
+  int hdr_len;
+  uint32_t pts1, dts1;
+  picture_coding_type = (buf[5] >> 3) & 0x07;
+  hdr_len = (picture_coding_type > 1) ? 9 : 8;
+  if(buf[hdr_len + 3] == 0xb5)
+    hdr_len += 9;
+  if(buf[hdr_len + 3] == 0xb2) {
+    pts1 = ((buf[hdr_len+6] & 0x03)   << 30) +
+           ((buf[hdr_len+7] & 0x7f) << 23) +
+           ((buf[hdr_len+8])          << 15) +
+           ((buf[hdr_len+9] & 0x7f) << 8) +
+           buf[hdr_len+10];
+    dts1 = ((buf[hdr_len+13] & 0x03)   << 30) +
+           ((buf[hdr_len+14] & 0x7f) << 23) +
+           ((buf[hdr_len+15])          << 15) +
+           ((buf[hdr_len+16] & 0x7f) << 8) +
+           buf[hdr_len+17];
+    //NOTE:  This is wrong.  DSS timestamps only have a resolution of 2^32/300
+    dprintf2("pts: %08x/%f dts: %08x/%f\n", pts1, pts1 / 27000000.0, dts1, dts1 / 27000000.0);
+    *pts = pts1;
+    *dts = dts1;
+    return 1;
+  }
+  return 0;
+}
+struct dss_pkt *parse_dss_hdr(unsigned char *buf, struct dss *dss)
+{
+  struct dss_pkt *pkt;
+  int scid = ((buf[1] << 8) + buf[2]) & 0x0fff;
+  
+  ll_find_elem(pkt, dss->pkt_ll, hdr.scid, scid, struct dss_pkt);
+  if(! pkt) {
+    //dprintf0("Found unexpected scid: %d %02x %02x %02x ...\n", scid, buf[0], buf[1], buf[2]);
+    return NULL;
+  }
+  pkt->hdr.packet_framing         = buf[1] >> 7;
+  pkt->hdr.bundle_boundary        = (buf[1] >> 6) & 0x01;
+  pkt->hdr.control_flag           = (buf[1] >> 5) & 0x01;
+  pkt->hdr.control_sync           = (buf[1] >> 4) & 0x01;
+  pkt->hdr.continuity_counter     = (buf[3] >> 4);
+  pkt->hdr.header_designator_type = (buf[3] >> 2) & 0x03;
+  pkt->hdr.header_designator_id   = buf[3] & 0x03;
+  return pkt;
+}
+
+void write_dummy_ts(uint8_t *ts)
+{
+  static const uint8_t data[TS_SIZE] = {0x47, 0x1f, 0xff, 0x10};
+  memcpy(ts, data, TS_SIZE);
+}
+
+
+//We should always write out a frame here
+int write_ts_frame(struct dss_pkt *pkt, uint8_t *ts)
+{
+  int len;
+  int stuff;
+  int pos = 0;
+
+  stuff = (pkt->buf_len < TS_SIZE - 4) ? TS_SIZE - 4 - pkt->buf_len : 0;
+
+  pos = write_ts_header(pkt->hdr.scid, pkt->start, pkt->cont,
+                        pkt->rts, ts, stuff);
+  pkt->cont = (pkt->cont + 1) & 0x0f;
+  len = TS_SIZE - pos;
+  pkt->rts = -1;
+  pkt->start = 0;
+  memcpy(ts+pos, pkt->buf, len);
+  pkt->buf_len -= len;
+  //This is horribly inefficient!
+  if(pkt->buf_len > 0)
+    memmove(pkt->buf, pkt->buf + len, pkt->buf_len);
+  else 
+    pkt->buf_len = 0;
+  return 1;
+}
+
+void write_ts(uint8_t **out, uint8_t *buf, int len, struct dss_pkt *pkt)
+{
+  uint8_t ts[188];
+
+  if(pkt->hdr.bundle_boundary) {
+    while(pkt->buf_len != 0) {
+      if(write_ts_frame(pkt, ts)) {
+        memcpy(*out,  ts, TS_SIZE);
+        *out += TS_SIZE;
+      }
+    }
+    pkt->start = 1;
+    pkt->hdr.bundle_boundary = 0;
+  }
+  memcpy(pkt->buf + pkt->buf_len, buf, len);
+  pkt->buf_len += len;
+  if(pkt->buf_len >= 184) {
+    if(write_ts_frame(pkt, ts)) {
+      memcpy(*out,  ts, TS_SIZE);
+      *out += TS_SIZE;
+    }
+  }
+}
+
+int make_pes(uint8_t *outbuf, uint8_t *inbuf, struct dss_pkt *pkt)
+{
+  int ptsdts;
+  uint64_t pts, dts;
+  if(get_ptsdts(inbuf, 127, &pts, &dts))
+    ptsdts = PTS_DTS;
+  else
+    ptsdts = 0;
+  if(ptsdts && pkt)
+    pkt->rts = dts;
+  return write_pes_header(0xE0, 6, pts, dts, outbuf, 0, ptsdts);
+}
+
+void parse_aux(uint8_t *buf, struct dss_pkt *pkt)
+{
+  if((buf[0] & 0x3f) > 0x01)
+    return;
+  pkt->rts = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
+}
+void get_scids(struct cir *cir, int basescid)
+{
+  static const uint8_t sc[16][17] = {
+    {}, //0x00
+    {2, 0x0f, 0}, //0x01
+    {9, 0x0f, 0, 1, 2, 3, 4, 5, 6, 7}, //0x02
+    {9, 0x0f, 1, 1, 2, 3, 4, 5, 6, 7}, //0x03
+    {9, 0x0f, 2, 1, 2, 3, 4, 5, 6, 7}, //0x04
+    {9, 0x0f, 3, 1, 2, 3, 4, 5, 6, 7}, //0x05
+    {}, //0x06
+    {}, //0x07
+    {}, //0x08
+    {}, //0x09
+    {9, 0x0f, 0, 1, 2, 3, 4, 5, 6, 7}, //0x0a
+    {}, //0x0b
+    {1, 0x00}, //0x0c
+    {1, 0x0d}, //0x0d
+    {1, 0x0c}, //0x0e
+    {1, 0x0f}, //0x0f
+  };
+  int spi = cir->service_paradigm_indicator;
+  int i;
+
+  for(i = 0; i < cir->number_of_scids; i++) {
+    cir->scid[i] = basescid+i;
+    if(i < sc[spi][0])
+      cir->scid_type[i] = sc[spi][i+1];
+    else
+      cir->scid_type[i] = sc[spi][sc[spi][0]];
+  }
+  if(spi == 0x0a)
+    cir->scid_type[cir->number_of_scids-1] = 0x09;
+}
+void read_extended_chaninfo(struct cir *cir, uint8_t *heap)
+{
+  int i, offset;
+
+  if(cir->service_paradigm_indicator == 0) {
+    for(i=0; i < cir->number_of_scids; i++) {
+      if(! cir->scid_size_flag) {
+        if((i & 0x01) == 0) {
+          cir->scid[i] = heap[0];
+          cir->scid_type[i] = heap[1] >> 4;
+          heap++;
+        } else {
+          cir->scid[i] = heap[1];
+          cir->scid_type[i] = heap[0] & 0x0f;
+          heap+=2;
+        }
+      } else {
+        if((i & 0x01) == 0) {
+          cir->scid[i] = (heap[0] << 4) | (heap[1] >> 4);
+          cir->scid_type[i] = heap[1] & 0x0f;
+        } else {
+          cir->scid[i] = ((heap[0] << 8) | heap[1] >> 4) & 0xfff;
+          cir->scid_type[i] = heap[0] >> 4;
+        }
+        heap+=2;
+      }
+    }
+  } else {
+    int basescid;
+    basescid = cir->scid_size_flag ? ((heap[0] << 4) | (heap[1] >> 4)) :
+                                     heap[0];
+    get_scids(cir, basescid);
+    heap+=2;
+  }
+  if(cir->pip_flag == 0x02)
+    heap+=2;
+  if(cir->dip_flag == 0x02)
+    heap+=2;
+  if(cir->long_name_flag) {
+    //we could do a strcpy here
+    while(*heap)
+      heap++;
+    heap++;
+  }
+  if(cir->logo_flag)
+    heap++;
+  offset = 1;
+  if(*heap & 0x20) offset+=2; //altcn_flag
+  if(*heap & 0x10) offset+=2; //ext4_flag
+  if(*heap & 0x08) offset+=1; //altci_flag
+  if(*heap & 0x04) cir->network_id = heap[offset]; //altni_flag
+}
+
+void process_cir(struct cir *cir, uint8_t *cssm, int satid,
+                 int provid, uint8_t *heap)
+{
+  int i;
+  int offset = provid ? 1 : 0;
+  cir->network_id = satid;
+  cir->pip_flag = (cssm[0] >> 2) & 0x03;
+  cir->dip_flag = cssm[0] & 0x03;
+  cir->use_heap = cssm[1] & 0x80 ? 1 : 0;
+  cir->long_name_flag = cssm[1] & 0x40;
+  cir->logo_flag = cssm[1] & 0x20;
+  cir->scid_size_flag = (cssm[1] >> 4) & 0x01;
+  cir->service_paradigm_indicator = cssm[1] & 0x0f;
+  cir->number_of_scids = cssm[2+offset];
+  cir->pip_transponder = cssm[3+offset];
+  cir->dip_transponder = cssm[4+offset];
+  cir->channel_transponder = cssm[5+offset];
+  cir->virt_channel_number = cssm[6+offset] << 8 | cssm[7+offset];
+  memcpy(cir->short_name, &cssm[8+offset], 4);
+  if(cir->use_heap) {
+    int index = (cssm[15+4*offset] << 8) | cssm[16+4*offset];
+    read_extended_chaninfo(cir, heap + index);
+  } else {
+    get_scids(cir, cssm[15+4*offset]);
+  }
+  //we have added a channel
+  dprintf2("%d(%c%c%c%c) tp:%d sat:%02x", cir->virt_channel_number,
+         cir->short_name[0], cir->short_name[1], cir->short_name[2],
+         cir->short_name[3], cir->channel_transponder, cir->network_id);
+  for(i = 0; i < cir->number_of_scids; i++)
+    dprintf2(" %03x:%02x", cir->scid[i], cir->scid_type[i]);
+  dprintf2("\n");
+}
+
+int get_avail_chan(struct mpg *mpg, uint32_t *indices, int max_chan)
+{
+  int i, j = 0;
+  int cur_tp = mpg->current_transponder;
+  dprintf2("Searching %d channels on sat:%d tp:%d\n", mpg->num_channels, mpg->segl.default_network_id, cur_tp);
+  for(i = 0; i < mpg->num_channels && j < max_chan; i++) {
+    if(mpg->cir[i].network_id != mpg->segl.default_network_id ||
+       mpg->cir[i].channel_transponder != cur_tp)
+      continue;
+    indices[j++] = i;
+    dprintf2("  Found channel: %d\n", mpg->cir[i].virt_channel_number);
+  }
+  dprintf2("Found %d channels\n", j);
+  return j;
+}
+
+void write_chaninfo(uint8_t **out, struct mpg *mpg)
+{
+  static int patcount = 0, pmtcount = 0, nitcount = 0;
+  int pos, i, j, pmtpos, patpos = 9, chan_cnt;
+  uint8_t buf[188];
+  //PMT Program number = 1
+  //PMT PID = 0x20
+  uint8_t pat[184] = {0x00, 0x00, 0xb0, 0x00, 0xfe, 0xef, 0xc1, 0x00, 0x00};
+  uint8_t pmt[184] ={0x00, 0x02, 0xb0, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00,
+                     0x00, 0x00, 0xf0, 0x00};
+  uint8_t nit[184] ={0x00, 0x40, 0xf0, 0x0d, 0x00, 0x00, 0xff, 0x00, 0x00,
+                     0xf0, 0x00, 0xf0, 0x00};
+  uint32_t chanidx[40]; //40 is max channels in a single PAT frame
+
+  chan_cnt = get_avail_chan(mpg, chanidx, 40);
+  if(!chan_cnt)
+    return;
+  patcount = (patcount + 1) & 0x0f;
+  //PAT
+  //add NIT info
+  pat[patpos++] = 0;
+  pat[patpos++] = 0;
+  pat[patpos++] = 0xe0 | ((DSS_NITPID >> 8) & 0x1f);
+  pat[patpos++] = DSS_NITPID & 0xff;
+  for(i = 0; i < chan_cnt; i++) {
+    pat[patpos++] = (mpg->cir[chanidx[i]].virt_channel_number >> 8) & 0xff;
+    pat[patpos++] = mpg->cir[chanidx[i]].virt_channel_number & 0xff;
+    pat[patpos++] = 0xe0 | ((DSS_PMTPID >> 8) & 0x1f);
+    pat[patpos++] = DSS_PMTPID & 0xff;
+  }
+  pat[3] = (patpos + 4 - 3 - 1);
+  *(uint32_t *)(pat+patpos)= htonl(crc32_04c11db7(pat+1, patpos-1, 0xffffffff));
+  patpos+=4;
+  pos = write_ts_header(0x00, 1, patcount, -1, buf, 0);
+  memcpy(buf+pos, pat, patpos);
+  pos += patpos;
+  memset(buf+pos, 0xff, TS_SIZE - pos);
+  memcpy(*out,  buf, TS_SIZE);
+  *out += TS_SIZE;
+  //PMT
+  for(i = 0; i < chan_cnt; i++) {
+    pos = 0;
+    pmtpos = 13;
+    pos += write_ts_header(DSS_PMTPID, 1, pmtcount, -1, buf+pos, 0);
+    for(j = 0; j < mpg->cir[i].number_of_scids; j++) {
+      uint8_t type;
+      uint32_t pid = mpg->cir[i].scid[j];
+      uint8_t lang[3] = {0, 0, 0};
+      switch(mpg->cir[i].scid_type[j]) {
+        case 0x08:
+        case 0x0f:
+          type = 0x02;
+          break;
+        case 0x09:
+          type = 0x81;
+          break;
+        case 0x00:
+          type = 0x04;
+          lang[0] = 'e'; lang[1] = 'n'; lang[2] = 'g';
+          break;
+        case 0x01:
+          type = 0x04;
+          lang[0] = 's'; lang[1] = 'p'; lang[2] = 'a';
+          break;
+        case 0x02:
+          type = 0x04;
+          lang[0] = 'f'; lang[1] = 'r'; lang[2] = 'e';
+          break;
+        case 0x03:
+          type = 0x04;
+          lang[0] = 'd'; lang[1] = 'e'; lang[2] = 'u';
+          break;
+        case 0x04:
+          type = 0x04;
+          lang[0] = 'i'; lang[1] = 't'; lang[2] = 'a';
+          break;
+        case 0x05:
+          type = 0x04;
+          lang[0] = 'j'; lang[1] = 'p'; lang[2] = 'n';
+          break;
+        case 0x06:
+          type = 0x04;
+          lang[0] = 'k'; lang[1] = 'o'; lang[2] = 'r';
+          break;
+        case 0x07:
+          type = 0x04;
+          lang[0] = 'z'; lang[1] = 'h'; lang[2] = 'o';
+          break;
+      }
+      pmt[pmtpos++] = type;
+      pmt[pmtpos++] = 0xe0 | (0xff & (pid >> 8));
+      pmt[pmtpos++] = 0xff & pid;
+      pmt[pmtpos++] = 0xf0;
+      if(lang[0]) {
+        pmt[pmtpos++] = 0x06;
+        pmt[pmtpos++] = 0x0a;
+        pmt[pmtpos++] = 0x04;
+        pmt[pmtpos++] = lang[0];
+        pmt[pmtpos++] = lang[1];
+        pmt[pmtpos++] = lang[2];
+        pmt[pmtpos++] = 0x00;
+      } else {
+        pmt[pmtpos++] = 0x00;
+      }
+    }
+    pmt[3] = pmtpos + 4/*crc*/ - 3 - 1/*pointer_field*/;
+    pmt[4] = (mpg->cir[i].virt_channel_number >> 8) & 0xff;
+    pmt[5] = mpg->cir[i].virt_channel_number & 0xff;
+    pmt[9] = 0xf0 | (0xff & (mpg->cir[i].scid[0] >> 8));
+    pmt[10] = 0xff & mpg->cir[i].scid[0];
+    *(uint32_t *)&pmt[pmtpos] = htonl(crc32_04c11db7(&pmt[1], pmtpos -1,
+                                      0xffffffff));
+    pmtpos+=4;
+    memcpy(buf+pos, pmt, pmtpos);
+    pos += pmtpos;
+    memset(buf+pos, 0xff, TS_SIZE - pos);
+    memcpy(*out,  buf, TS_SIZE);
+    *out += TS_SIZE;
+    pmtcount = (pmtcount+1) & 0x0f;
+  }
+  //NIT
+  pos = 0;
+  nit[4] |= (DSS_NETWORKID >> 8) & 0xff;
+  nit[5] |= DSS_NETWORKID & 0xff;
+  *(uint32_t *)&nit[13] = htonl(crc32_04c11db7(&nit[1], 12, 0xffffffff));
+  pos += write_ts_header(DSS_NITPID, 1, nitcount, -1, *out, 0);
+  memcpy(*out + pos, nit, 17);
+  pos+=17;
+  memset(*out+pos, 0xff, TS_SIZE - pos);
+  *out += TS_SIZE;
+  nitcount = (nitcount+1) & 0x0f;
+}
+
+void parse_mpg(uint8_t **out, struct mpg *mpg, uint8_t *buf)
+{
+  uint8_t seg_sync[4] = {0xaa, 0x55, 0xa5, 0xa5};
+  uint32_t *segsync = (uint32_t *)seg_sync;
+  uint8_t cssm_sync[4] = {0x55, 0xaa, 0x5a, 0x5a};
+  uint32_t *cssmsync = (uint32_t *)cssm_sync;
+  struct segment_list *segl = &mpg->segl;
+  struct cssm         *cssm = &mpg->cssm;
+  uint8_t *p, *heap;
+  int i, offset;
+
+  if(segl->number_of_segments == 0) {
+    if (*(uint32_t *)buf == *segsync && (buf[4] & 0xf0) == 0) {
+      segl->number_of_segments = buf[19];
+      segl->up_scid            = (buf[53] << 8 | buf[54]) & 0xfff;
+      segl->pip_scid           = (buf[55] << 8 | buf[56]) & 0xfff;
+      segl->dip_scid           = (buf[57] << 8 | buf[58]) & 0xfff;
+      segl->default_network_id = buf[97];
+      segl->provider_id        = buf[107];
+      segl->checksum           = buf[125] << 8 | buf[126];
+      segl->number_of_segments--;
+      mpg->num_channels = 0;
+    }
+    return;
+  }
+  if(cssm->segment_size == 0 && *(uint32_t *)buf == *cssmsync) {
+    cssm->number_of_channels = buf[5];
+    cssm->segment_size       = buf[6] << 8 | buf[7];
+    cssm->buf_len = 0;
+    dprintf2("Searching for %d channels in %d bytes (ns:%d)\n",
+           cssm->number_of_channels, cssm->segment_size,segl->number_of_segments);
+  }
+  if(cssm->segment_size) {
+    memcpy(cssm->buf + cssm->buf_len, buf, 127);
+    cssm->buf_len += 127;
+    if(cssm->segment_size > 127) {
+      cssm->segment_size -= 127;
+      return;
+    }
+    p = &cssm->buf[8];
+    offset = segl->provider_id ? 1 : 0;
+    heap = &p[(17 + 4 * offset) * cssm->number_of_channels];
+    mpg->num_channels += cssm->number_of_channels;
+    if(mpg->cir_len < mpg->num_channels) {
+      dprintf1("Reallocating CIR: %d < %d\n", mpg->cir_len, mpg->num_channels);
+      mpg->cir = (struct cir *)realloc(mpg->cir,
+                                       mpg->num_channels * sizeof(struct cir));
+      mpg->cir_len = mpg->num_channels;
+    }
+    for(i = cssm->number_of_channels; i > 0; i--) {
+      process_cir(&mpg->cir[mpg->num_channels-i], p, segl->default_network_id,
+                  segl->provider_id, heap);
+      p += 17 + 4*offset;
+    }
+    cssm->segment_size = 0;
+  }
+  if(--segl->number_of_segments == 0)
+    write_chaninfo(out, mpg);
+}
+
+void write_audio_pack(uint8_t **out, uint8_t *pack, struct dss_pkt *pkt)
+{
+  pkt->audio_len = (pack[4] << 8) | pack[5];
+  assert(pkt->audio_len);
+  pkt->hdr.bundle_boundary = 1;
+  if(pack[3] == 0xbf) {
+    //This is already a PES stream
+    write_ts(out, pack, 11, pkt);
+  } else {
+    //Need to convert MPEG1 pkt to MPEG2-PES
+    //for our purpose the only difference is that the PES is 3 bytes longer
+    int i, len;
+    for(i = 10; i >= 6; i--)
+      pack[i+3] = pack[i];
+    len = pkt->audio_len + 3;
+    pack[4] = (len >> 8) & 0xff;
+    pack[5] = len & 0xff;
+    pack[6] = 0x80;
+    pack[7] = 0x80;
+    pack[8] = 0x05;
+    write_ts(out, pack, 14, pkt);
+  }
+  pkt->audio_len -=5;
+  pkt->audio_packlen = 0;
+}
+
+//Try to find a start code, since audio frames aren't aligned to DSS
+//boundaries
+void find_audio(uint8_t **out, uint8_t *buf, int buflen, struct dss_pkt *pkt)
+{
+  int len;
+  int ok = 0;
+  if(pkt->audio_packlen) {
+    memcpy(pkt->audio_pack + pkt->audio_packlen, buf, 11 - pkt->audio_packlen);
+    buf += 11 - pkt->audio_packlen;
+    buflen -= 11 - pkt->audio_packlen;
+    write_audio_pack(out, pkt->audio_pack, pkt);
+  }
+  len = (pkt->audio_len < buflen) ? pkt->audio_len : buflen;
+  if(len) {
+    write_ts(out, buf, len, pkt);
+    pkt->audio_len -= len;
+    ok = 1;
+  }
+  if(len == buflen || pkt->audio_len)
+    return;
+  if(pkt->audio_tmp_len) {
+    //we know there are 4 bytes of header in front of buf, so let's overwrite
+    buf = buf - pkt->audio_tmp_len;
+    buflen += pkt->audio_tmp_len;
+    memcpy(buf, pkt->audio_tmp, pkt->audio_tmp_len);
+    pkt->audio_tmp_len = 0;
+  }
+  while(len + 4 <= buflen) {
+    if(buf[len] == 0x00  && buf[len+1] == 0x00 && buf[len+2] == 0x01 &&
+       ((buf[len+3] & 0xe0) == 0xc0 || buf[len+3] == 0xbf)) {
+      int packlen = (11 < buflen - len) ? 11 : buflen - len;
+      memcpy(pkt->audio_pack, buf + len, packlen);
+      pkt->audio_packlen = packlen;
+      if(packlen == 11) {
+        write_audio_pack(out, pkt->audio_pack, pkt);
+        len += 11;
+        if(len < buflen) {
+          write_ts(out, buf + len, buflen - len, pkt);
+          pkt->audio_len -= buflen - len;
+        }
+      }
+      return;
+    }
+    assert(ok != 1);
+    len++;
+  }
+  if (len == buflen)
+    return;
+  pkt->audio_tmp_len = buflen - len;
+  memcpy(pkt->audio_tmp, buf + len, pkt->audio_tmp_len);
+  return;
+}
+
+void parse_dss(struct dss *dss, uint8_t *data, int len) {
+  int pos;
+  uint8_t *buf, *nextbuf, *out = dss->buf + dss->buf_used;
+  struct dss_pkt *pktptr;
+  if(dss->unused_len) {
+    memcpy(dss->unused + dss->unused_len, data, 131 - dss->unused_len);
+    nextbuf = dss->unused;
+    pos = -dss->unused_len;
+  } else {
+    nextbuf = data;
+    pos = 0;
+  }
+  while (pos <= len - 131) {
+    pos+= 131;
+    buf = nextbuf;
+    if(dss->unused_len) {
+      nextbuf = data + (131 - dss->unused_len);
+      dss->unused_len = 0;
+    } else {
+      nextbuf = buf + 131;
+    }
+    if((pktptr = parse_dss_hdr(buf, dss)) == NULL)
+      continue;
+    if(pktptr->hdr.control_flag == 0) {
+      //can't handle encrypted streams
+      continue;
+    }
+    if(pktptr->hdr.header_designator_type == 0) {
+      //Check the aux packet to see if there's an RTS in there
+      //We're currently using the DTS for the SCR, so no need to parse
+      //AUX packets at the moment
+      //parse_aux(buf+4, pktptr);
+      continue;
+    }
+    if(pktptr->hdr.scid == 0x009) {
+        //do pat/pmt stuff here
+        parse_mpg(&out, &dss->mpg, buf+4);
+        continue;
+    }
+    if(pktptr->hdr.bundle_boundary) {
+      //We should only get here for PES or ES data packets
+      pktptr->is_video = 1;
+      if(buf[7] >= 0xb9) {
+        //This is a PES packet nothing else to do
+        write_ts(&out, buf+4, 127, pktptr);
+      } else {
+        //This is an ES frame, and we need to add a PES to it
+        uint8_t peshdr[TS_SIZE];
+        int peslen;
+        peslen = make_pes(peshdr, buf+4, pktptr);
+        write_ts(&out, peshdr, peslen, pktptr);
+        write_ts(&out, buf+4, 127, pktptr);
+      }
+    } else {
+      //This is a continuation of the previous frame
+      //Do we need to find the end 1st?
+      if(! pktptr->is_video) {
+        find_audio(&out, buf+4, 127, pktptr);
+      } else {
+        write_ts(&out, buf+4, 127, pktptr);
+      }
+    }
+  }
+  dss->buf_used = out - dss->buf;
+  if(pos < len) {
+    dprintf3("Storing %d bytes %02x %02x %02x...\n", len - pos, data[pos], data[pos+1], data[pos+2]);
+    memcpy(dss->unused, data + pos, len - pos);
+    dss->unused_len = len - pos;
+  }
+}
+
+static uint32_t get_transponder(uint32_t freq)
+{
+#define DSS_LOF 11250000L
+  int i;
+  static uint32_t freqmap[] = {
+    12224, 12239, 12253, 12268, 12282, 12297, 12311, 12326, 12341, 12355,
+    12370, 12381, 12399, 12414, 12428, 12443, 12457, 12472, 12486, 12501,
+    12516, 12530, 12545, 12559, 12574, 12588, 12603, 12618, 12632, 12647,
+    12661, 12676};
+  freq = (freq + DSS_LOF) / 1000L;
+  for(i = 0; i < 32; i++) {
+    if(freq >= freqmap[i] - 2 && freq <= freqmap[i] + 2) {
+      dprintf0("Tuned to transponder: %d\n", i);
+      return i;
+    }
+  }
+  return 32;
+}
+static struct dss *find_dss_from_pc(struct parser_cmds *pc)
+{
+  struct list_head *ptr;
+  list_for_each(ptr, &dsslist) {
+    struct dss *dss = list_entry(ptr, struct dss);
+    if(dss->adapt == pc->common->virt_adapt);
+      return dss;
+  }
+  return NULL;
+}
+static void fe_tune(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+  int is_dss = 0;
+  uint32_t freq;
+  struct dvb_frontend_parameters_new fep2;
+  struct dss *dss = find_dss_from_pc(pc);
+  
+  if(cmd == FE_SET_FRONTEND) {
+    dprintf1("Tuning frontend\n");
+    struct dvb_frontend_parameters *fep =(struct dvb_frontend_parameters *)data;
+    is_dss = (fep->u.qpsk.symbol_rate == DSS_SYMBOLRATE);
+    freq = fep->frequency;
+  } else if(cmd == FE_SET_FRONTEND2) {
+    dprintf1("Tuning frontend\n");
+    struct dvb_frontend_parameters_new *fep =
+          (struct dvb_frontend_parameters_new *)data;
+    if(dss->old_standard == FE_DVB_S || dss->old_standard == FE_QPSK)
+      is_dss = (fep->u.qpsk.symbol_rate == DSS_SYMBOLRATE);
+    else if(dss->old_standard == FE_DVB_S2)
+      is_dss = (fep->u.qpsk2.symbol_rate == DSS_SYMBOLRATE);
+    else
+      return;
+    freq = fep->frequency;
+  } else if(cmd == FE_SET_STANDARD) {
+    dprintf1("Setting standard to %u\n", (uint32_t)data);
+    dss->old_standard = (*(unsigned long *)data) & 0xffffffff;
+    return;
+  }
+  if(! is_dss) {
+    dprintf1("Got non DSS frontend message\n");
+//    if(dss->is_dss)
+//      ioctl(fdptr->fd, FE_SET_STANDARD, dss->old_standard);
+    return;
+  }
+  pthread_mutex_lock(&dss->lock);
+  if(ioctl(fdptr->fd, FE_SET_STANDARD, FE_DSS))
+    return;
+  dss->mpg.current_transponder = get_transponder(freq);
+  dss->is_dss = 1;
+  dss->buf_used = 0;
+  dss->unused_len = 0;
+  fep2.frequency = freq;
+  fep2.inversion = INVERSION_AUTO;
+  fep2.u.qpsk2.symbol_rate = 20000000;
+  fep2.u.qpsk2.fec_inner = FEC_5_6;
+  fep2.u.qpsk2.modulation = MOD_DSS_QPSK;
+  usleep(200000);
+  *ret = ioctl(fdptr->fd, FE_SET_FRONTEND2, &fep2);
+  usleep(200000);
+  pthread_mutex_unlock(&dss->lock);
+  *result = CMD_STOPALL;
+  dprintf0("Switched to DSS mode\n");
+  return;
+}
+
+static void fe_close(struct parser_cmds *pc, struct poll_ll *fdptr,
+                     cmdret_t *result, int *ret,
+                     unsigned long int cmd, unsigned char *data)
+{
+  struct dss *dss = find_dss_from_pc(pc);
+  dss->is_dss = 0;
+  pthread_mutex_lock(&dss->lock);
+  while(! list_empty(&dss->pkt_ll)) {
+    struct dss_pkt * pkt = list_entry(dss->pkt_ll.next, struct dss_pkt);
+    list_del(&pkt->list);
+    list_add(&pkt->list, &dsspkt_empty_queue);
+  }
+  pthread_mutex_unlock(&dss->lock);
+}
+
+static void close_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
+                        cmdret_t *result, int *ret,
+                        unsigned long int cmd, unsigned char *data)
+{
+  struct dss_pkt *pkt;
+  struct dss *dss = find_dss_from_pc(pc);
+  pthread_mutex_lock(&dss->lock);
+  ll_find_elem(pkt, dss->pkt_ll, fd, fdptr->fd, struct dss_pkt);
+  if(pkt) {
+    list_del(&pkt->list);
+    list_add(&pkt->list, &dsspkt_empty_queue);
+  }
+  pthread_mutex_unlock(&dss->lock);
+}
+static void set_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret,
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct dss_pkt *pkt;
+  struct dmx_pes_filter_params *dmx;
+  struct dss *dss= find_dss_from_pc(pc);
+  if(! dss->is_dss) {
+    dprintf2("Ignoring demux ioctl\n");
+    return;
+  }
+  if(cmd == DMX_SET_PES_FILTER) {
+    dmx = (struct dmx_pes_filter_params *)data;
+    if(dmx->output != DMX_OUT_TS_TAP) {
+      dprintf0("Tried to open DMX in non TAP mode\n");
+      *ret = 1;
+      *result = CMD_STOPALL;
+      return;
+    }
+    if(dmx->pid == 0x00) {
+      //PAT requested let's open 009
+      ll_find_elem(pkt, dss->pkt_ll, hdr.scid, 0x009, struct dss_pkt);
+      if(pkt) {
+        *ret = 0;
+        *result = CMD_STOPALL;
+        return;
+      }
+      dmx->pid = 0x009;
+    } else if(dmx->pid == DSS_PMTPID) {
+      *ret = 0;
+      *result = CMD_STOPALL;
+      return;
+    }
+    pthread_mutex_lock(&dss->lock);
+    pop_entry_from_queue(pkt, &dsspkt_empty_queue, struct dss_pkt);
+    bzero(pkt, sizeof( struct dss_pkt));
+    pkt->hdr.scid = dmx->pid;
+    pkt->fd = fdptr->fd;
+    pkt->rts = -1;
+    list_add(&pkt->list, &dss->pkt_ll);
+    *ret = ioctl(fdptr->fd, cmd, dmx);
+    pthread_mutex_unlock(&dss->lock);
+    *result = CMD_STOPALL;
+    dprintf0("Set demux ioctl on pid %d\n", pkt->hdr.scid);
+  }
+}
+
+static void dvr_open(struct parser_cmds *pc, struct poll_ll *fdptr,
+                     cmdret_t *result, int *ret,
+                     unsigned long int cmd, unsigned char *data)
+{
+  struct dss *dss= find_dss_from_pc(pc);
+  dss->buf_used = 0;
+  dss->unused_len = 0;
+}
+static void dvr_preread(struct parser_cmds *pc, struct poll_ll *fdptr,
+                     cmdret_t *result, int *ret,
+                     unsigned long int cmd, unsigned char *data)
+{
+  struct dss *dss= find_dss_from_pc(pc);
+  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  static int fd = -1;
+  int bytes;
+
+  if(! dss->is_dss)
+    return;
+  if(fd == -1) {
+    fd = open(opt_file, O_RDONLY);
+  }
+  bytes = read(fd, pc->mmap, ci->u.count);
+  if(bytes == 0)
+    lseek(fd, 0, SEEK_SET);
+  ci->u.count = bytes;
+  *ret = bytes;
+  *result = CMD_SKIPCALL;
+}
+//NOTE: This is very broken.  We don't return the number of bytes requested ever
+static void dvr_read(struct parser_cmds *pc, struct poll_ll *fdptr,
+                     cmdret_t *result, int *ret,
+                     unsigned long int cmd, unsigned char *data)
+{
+  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  struct dss *dss= find_dss_from_pc(pc);
+  if(! dss->is_dss)
+    return;
+  if(dss->buf_len < ci->u.count + dss->buf_used) {
+    dprintf2("Reallocating %d < %d\n", dss->buf_len + dss->buf_used, ci->u.count);
+    dss->buf = (uint8_t *)realloc(dss->buf, dss->buf_used + ci->u.count);
+    dss->buf_len = ci->u.count;
+  }
+  if(*ret > 0) {
+    dprintf3("Reading %d bytes(%08x): %02x %02x %02x %02x ...\n", *ret, pc->mmap, pc->mmap[0], pc->mmap[1], pc->mmap[2], pc->mmap[3]);
+    parse_dss(dss, pc->mmap, *ret);
+    if(dss->buf_used) {
+      int max = ((int)dss->buf_used < *ret) ? dss->buf_used : *ret;
+      memcpy(pc->mmap, dss->buf, max);
+      *ret = max;
+      memmove(dss->buf, dss->buf + max, dss->buf_used - max);
+      dss->buf_used -= max;
+    } else {
+      write_dummy_ts(pc->mmap);
+      if(*ret < TS_SIZE) {
+        memcpy(dss->buf, pc->mmap + *ret, TS_SIZE - *ret);
+        dss->buf_used = TS_SIZE - *ret;
+      } else {
+        *ret = TS_SIZE;
+      }
+    }
+    dprintf3("Writing %d bytes(%08x): %02x %02x %02x %02x ...\n", *ret, pc->mmap, pc->mmap[0], pc->mmap[1], pc->mmap[2], pc->mmap[3]);
+  }
+}
+
+static void connect_dss(struct parser_adpt *pc_all)
+{
+  struct cmd_list *fe_preioctl = register_cmd(fe_tune);
+  struct cmd_list *demux_preioctl =  register_cmd(set_demux);
+  struct cmd_list *demux_postclose = register_cmd(close_demux);
+  struct cmd_list *fe_postclose = register_cmd(fe_close);
+  struct cmd_list *dvr_postread = register_cmd(dvr_read);
+  struct cmd_list *dvr_preread1 = register_cmd(dvr_preread);
+  struct cmd_list *dvr_postopen = register_cmd(dvr_open);
+  struct dss *dss;
+
+  if(! opt_enable)
+    return;
+  dprintf0("Enabling DSS for adapter %d\n", pc_all->frontend->common->virt_adapt);
+  dss = (struct dss *)malloc(sizeof(struct dss));
+  bzero(dss, sizeof(struct dss));
+  INIT_LIST_HEAD(&dss->pkt_ll);
+  dss->buf = (uint8_t *)malloc(2000000);
+  dss->buf_len = 2000000;
+  dss->adapt = pc_all->frontend->common->virt_adapt;
+  pthread_mutex_init(&dss->lock, NULL);
+  list_add(&fe_preioctl->list, &pc_all->frontend->pre_ioctl);
+  list_add(&fe_postclose->list, &pc_all->frontend->post_close);
+  list_add(&demux_preioctl->list, &pc_all->demux->pre_ioctl);
+  list_add(&demux_postclose->list, &pc_all->demux->post_close);
+  list_add(&dvr_postread->list, &pc_all->dvr->post_read);
+  list_add(&dvr_postopen->list, &pc_all->dvr->post_open);
+  if(opt_file != NULL)
+    list_add(&dvr_preread1->list, &pc_all->dvr->pre_read);
+  list_add_tail(&dss->list, &dsslist);
+}
+
+static struct option *parseopt_dss(arg_enum_t cmd)
+{
+  if(cmd == ARG_INIT) {
+    return dss_Opts;
+  } 
+  if(cmd == ARG_HELP) {
+    printf("   --dss-file <file> : Read DSS stream from <file> instead of adapter\n");
+    printf("   --dss-enable      : Enable DSS to DVB mapping\n");
+  } 
+  if(! dss_opt)
+    return NULL;
+
+  switch(dss_opt) {
+    case 'e':
+      opt_enable = 1;
+      break;
+    case 'f':
+      opt_file = optarg;
+      break;
+  }
+  dss_opt = 0;
+  return NULL;
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "dss",
+                parseopt_dss, connect_dss, NULL, NULL, NULL, NULL, NULL};
+int __attribute__((constructor)) __dss_init(void)
+{
+  list_add(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_dummy.c b/contrib/sasc-ng/dvbloopback/src/plugin_dummy.c
new file mode 100644 (file)
index 0000000..da812ac
--- /dev/null
@@ -0,0 +1,249 @@
+/*\r
+   DVBLoopback - plugin_dummy.c\r
+   Copyright Alan Nisota 2006\r
+\r
+   This file is part of DVBLoopback.\r
+\r
+    DVBLoopback is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    DVBLoopback is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License\r
+    along with DVBLoopback; if not, write to the Free Software\r
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+*/\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <fcntl.h>\r
+#include <pthread.h>\r
+#include <sys/mman.h>\r
+#include <sys/poll.h>\r
+#include <sys/ioctl.h>\r
+#include <signal.h>\r
+#include <string.h>\r
+#include <getopt.h>\r
+#include <unistd.h>\r
+#include <errno.h>\r
+#include "list.h"\r
+\r
+// Header Needed for Demuxer\r
+#include <linux/dvb/dmx.h>\r
+\r
+// Header Needed for Frontend\r
+#include <linux/dvb/frontend.h>\r
+\r
+// YOU MUST INCLUDE THIS\r
+//\r
+// Provides Structs for parser commands\r
+#include "process_req.h"\r
+\r
+// YOU MUST INCLUDE THIS\r
+//\r
+// Provides Structs for message passing\r
+#include "msg_passing.h"\r
+\r
+// Define your plugin load order here\r
+//\r
+// Must be even, < 32, >= 8, and unique (total of 12 plugins allowed currently)\r
+#define PLUGIN_ID 16\r
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined\r
+\r
+/* LongOpt Array \r
+ *\r
+ * option constructor: String name, int has_arg, StringBuffer flag, int val\r
+ *\r
+ *      name    - The long option String. \r
+ *      has_arg - Indicates whether the option has no argument (NO_ARGUMENT) 0, a \r
+ *                required argument (REQUIRED_ARGUMENT) 1 or an optional argument \r
+ *                (OPTIONAL_ARGUMENT) 2. \r
+ *      flag    - If non-null, this is a location to store the value of "val" \r
+ *                when this option is encountered, otherwise "val" is treated \r
+ *                as the equivalent short option character. \r
+ *      val     - The value to return for this long option, or the equivalent \r
+ *                single letter option to emulate if flag is null.\r
+ *\r
+ * MUST BE TERMINATED WITH {0,0,0,0} (Null Option?)\r
+ */\r
+\r
+static int dummy_value = 0;\r
+static struct option Dummy_Opts[] = {\r
+  {"dummy-param", 2, &dummy_value, 'd'},\r
+  {0, 0, 0, 0},\r
+};\r
+\r
+static void connect_dummy(struct parser_adpt *pc_all)\r
+{\r
+  printf("Connect Called\n");\r
+}\r
+\r
+static struct option *parseopt_dummy(arg_enum_t cmd)\r
+{\r
+  if(cmd == ARG_INIT) {\r
+    return Dummy_Opts;\r
+  } \r
+  if(cmd == ARG_HELP) {\r
+    printf("   --dummy-param <adapter number> :\n");\r
+  } \r
+  if(! dummy_value)\r
+    return NULL;\r
+\r
+  switch(dummy_value) {\r
+    case 'd':\r
+      if(optarg){\r
+       printf("Attaching to adapter%i\n", atoi(optarg));\r
+      }\r
+      break;\r
+  }\r
+  //must reset dummy_value after every call\r
+  dummy_value = 0;\r
+  return NULL;\r
+}\r
+\r
+/* struct plugin_cmd is used to pass information about this plugin to the main loop\r
+   \r
+   plugin_cmd struct is defined as:\r
+  \r
+   struct plugin_cmd {\r
+     struct list_head list;\r
+     int plugin;\r
+     struct option * (*parse_args)(arg_enum_t);\r
+     void (*connect)(struct parser_adpt *);\r
+     void (*launch)();\r
+     void (*message)(struct msg *, unsigned int priority);\r
+     void (*send_msg)(struct parser_cmds *pc, int msg);\r
+     void (*shutdown)();\r
+   };\r
+\r
+   'list' and 'plugin' MUST be initialized properly (see below) but all others\r
+   may be set to 'NULL' when that feature is not required.\r
+  \r
+   ---\r
+   struct list_head list;\r
+\r
+   Any structure which is used as a list must have 'struct list_head list;' as\r
+   the first element.  This will be managed by the list-handling functions, so\r
+   just initialize it to '{NULL, NULL}'.  there should never be a need to work\r
+   with the elements of a list, since the functions and macros do that for you.\r
+\r
+\r
+   ---\r
+   int plugin;\r
+\r
+   'plugin' is the identifier for this plugin.  It should be unique compared to\r
+   any other plugins used.  Other plugins can pass messages to this one by\r
+   specifying this identifier\r
+   \r
+   ---\r
+   struct option * (*parse_args)(arg_enum_t);\r
+\r
+   function which deals with option parsing.  It should behave as follows:\r
+     when arg_enum_t is 'ARG_INIT' returns a pointer to the parameter structure\r
+     when arg_enum_t is 'ARG_HELP' displays a help message describing cmdline\r
+          arguments\r
+     in all other cases, check the cmdline variable and process arguments as\r
+          needed\r
+\r
+   ---\r
+\r
+   void (*connect)(struct parser_adpt *);\r
+\r
+   connect will be called once for each adapter that is being used. (So if 3\r
+   cards are used, 'connect' will be called 3 times each time with a different\r
+   parser_adpt parameter.\r
+   All device-level initialization should be done here.\r
+\r
+   parser_adpt is defined as:\r
+\r
+   struct parser_adpt {\r
+     struct parser_cmds *frontend;\r
+     struct parser_cmds *demux;\r
+     struct parser_cmds *dvr;\r
+     struct parser_cmds *ca;\r
+   };\r
+\r
+   each element of parser_adpt supplies information about a given device\r
+\r
+  struct parser_cmds {\r
+    int type;\r
+    pthread_t thread;\r
+    struct common_data *common;\r
+    struct list_head pre_open;\r
+    struct list_head post_open;\r
+    struct list_head pre_close;\r
+    struct list_head post_close;\r
+    struct list_head pre_read;\r
+    struct list_head post_read;\r
+    struct list_head pre_poll;\r
+    struct list_head post_poll;\r
+    struct list_head pre_ioctl;\r
+    struct list_head post_ioctl;\r
+    unsigned char *mmap;\r
+    ...\r
+  }\r
+\r
+  type is the type of device defined in this structure.  It will be one of:\r
+    DVB_DEVICE_FRONTEND, DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, -1\r
+\r
+  thread is the processing thread handling this device.  Plugins should not\r
+    normally need to access this parameter\r
+\r
+  common contains information common to all devices on the current adapter.\r
+    This should primarily be used to determine the adapter number\r
+\r
+  all of the pre/post lists contain commands to be executed before/after\r
+  receiving a query for the given syscall.\r
+  normally inside the 'connect' function a plugin would do something like:\r
+    struct cmd_list *fe_postioctl = register_cmd(fe_tune);\r
+    list_add_tail(&fe_postioctl->list, &pc_all->frontend->post_ioctl); \r
+\r
+    which indicates that the 'fe_tune' function inside the current plugin will\r
+    be called after an ioctl has been processed (this could be used to post-\r
+    process the output of an ioctl from the 'real' card)\r
+\r
+  mmap conatins a pointer that is mmaped to the virtual device (but only for the\r
+    dvr device).  It will contain the results of a 'read' in the case of\r
+    a post_read callback, or can be filled in by the plugin during a pre_read\r
+    call instead of allowing a read from the real card to happen.\r
+\r
+  the parser_cmds struct contains additional elements, but these should be\r
+    ignored by plugins\r
+\r
+  ---\r
+  void (*launch)();\r
+\r
+  launch is called once all plugins have gone through 'connect' for all adapters\r
+  it is often used to spawn processing threads needed by the plugins\r
+\r
+  ---\r
+  void (*message)(struct msg *, unsigned int priority);\r
+\r
+  message is used to receive a message from another plugin\r
+\r
+\r
+  ---\r
+  void (*send_msg)(struct parser_cmds *pc, int msg);\r
+\r
+  send_msg is used to tell this plugin that another plugin is expecting to\r
+    receive messages from this one.  Normally this just indicates that the current\r
+    plugin should broadcast messages.\r
+\r
+  ---\r
+  void (*shutdown)();\r
+\r
+  shutdown is called to try to cleanup gracefully before exitiong\r
+*/ \r
+\r
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "dummy",\r
+                     parseopt_dummy, connect_dummy, NULL, NULL, NULL, NULL, NULL};\r
+int __attribute__((constructor)) __dummy_init(void)\r
+{\r
+  // communicates the existance of teh current plugin to the main program.\r
+  list_add(&plugin_cmds.list, &plugin_cmdlist);\r
+  return 0;\r
+}\r
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_getsid.c b/contrib/sasc-ng/dvbloopback/src/plugin_getsid.c
new file mode 100644 (file)
index 0000000..71de566
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "list.h"
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/version.h>
+#include "plugin_getsid.h"
+#include "msg_passing.h"
+
+#ifndef FE_SET_FRONTEND2
+  #define FE_SET_FRONTEND2 FE_SET_FRONTEND
+#endif
+
+static LIST_HEAD(getsidlist);
+
+#define DBG_NAME "CHANNEL"
+#define PLUGIN_ID PLUGIN_GETSID
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+struct filter {
+  struct list_head list;  //this must remain first!!!
+  struct list_head sids;  //contains sids to find for this fd
+  __u16 pid;
+  int   fd;
+  int used;
+  int parse_err;
+};
+
+struct sidnum {
+  struct list_head list;
+  int sid;
+  int seen;
+};
+
+//ISO 13818.1 says there can only be one PAT section, but I've already coded
+//it so let's leave the capability
+#define MAX_PAT_SECTIONS 5
+struct pat {
+  int patfd;
+  int version;
+  unsigned char last_section; 
+  unsigned char section_seen[MAX_PAT_SECTIONS];
+  int has_nit;
+  struct list_head dmx_filter_ll;
+};
+
+#define MAX_SIMULTANEOUS_PMT 32
+
+static int sid_opt = 0;
+static int opt_maxfilters = 2;
+static int opt_max_fail = 10;
+static int opt_allpids = 0;
+static int opt_resetpidmap = 0;
+static int opt_experimental = 0;
+static int opt_orbit = 0;
+static struct option Sid_Opts[] = {
+  {"sid-filt", 1, &sid_opt, 'f'},
+  {"sid-allpid", 0, &sid_opt, 'p'},
+  {"sid-nocache", 0, &sid_opt, 'c'},
+  {"sid-orbit", 1, &sid_opt, 'o'},
+  {"sid-experimental", 0, &sid_opt, 'e'},
+  {0, 0, 0, 0},
+};
+
+static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
+LIST_HEAD(sid_empty_queue);
+LIST_HEAD(sidnum_empty_queue);
+LIST_HEAD(epid_empty_queue);
+LIST_HEAD(dmxcmd_empty_queue);
+LIST_HEAD(sidmsg_empty_queue);
+LIST_HEAD(filt_empty_queue);
+
+void free_sidmsg(struct sid_msg *sidmsg) {
+  list_add_l(&sidmsg->list, &sidmsg_empty_queue, &list_lock);
+}
+
+void free_addsid_msg(void *msg) {
+  struct sid_msg *sidmsg = (struct sid_msg *)msg;
+  free_sidmsg(sidmsg);
+}
+
+static int add_pid(int patfd, int pid) {
+  struct dmx_sct_filter_params sct_filter;
+
+  bzero(&sct_filter, sizeof(struct dmx_sct_filter_params));
+  sct_filter.pid      = (__u16) pid;
+  sct_filter.flags    = DMX_IMMEDIATE_START;
+  if(ioctl(patfd, DMX_SET_BUFFER_SIZE, 0x40000) < 0) {
+    dprintf0("Failed to set buffersize on fd:%d, pid:%d (err:%d)\n",
+             patfd,pid, errno);
+    return 1;
+  }
+  if (ioctl(patfd, DMX_SET_FILTER, &sct_filter) < 0) {
+    dprintf0("Failed to set filter on fd:%d pid:%d (err: %d)\n",
+             patfd, pid, errno);
+    return 1;
+  }
+  return 0;
+}
+
+static void free_sid(struct sid *sid_ll) {
+  struct epid *epid_ll;
+  pthread_mutex_lock(&list_lock);
+  while(! list_empty(&sid_ll->epid)) {
+    epid_ll = list_entry(sid_ll->epid.next, struct epid);
+    list_del(&epid_ll->list);
+    list_add(&epid_ll->list, &epid_empty_queue);
+  }
+  list_add(&sid_ll->list, &sid_empty_queue);
+  pthread_mutex_unlock(&list_lock);
+}
+
+static void free_pat(struct pat *pat)
+{
+  while(! list_empty(&pat->dmx_filter_ll)) {
+    struct filter *filt = list_entry(pat->dmx_filter_ll.next, struct filter);
+    list_del(&filt->list);
+    while(! list_empty(&filt->sids)) {
+      struct sidnum *sidnum = list_entry(filt->sids.next, struct sidnum);
+      list_del(&sidnum->list);
+      list_add_l(&sidnum->list, &sidnum_empty_queue, &list_lock);
+    }
+    if(filt->fd > 0) {
+      close(filt->fd);
+    }
+    list_add_l(&filt->list, &filt_empty_queue, &list_lock);
+  }
+}
+
+static int read_pat(unsigned char *pes, struct pat *pat, unsigned int size) {
+  unsigned int sec, end;
+  int version;
+  unsigned char *ptr, last_sec;
+  struct sidnum *sidnum;
+
+  if (pes[0] != 0x00) {
+    dprintf0(
+             "read_pat: expected PAT table 0x00 but got 0x%02x\n", pes[0]);
+    return 1;
+  }
+  end = (((pes[1] & 0x03) << 8 | pes[2]) + 3 - 4);
+  if(end > size-4) {
+    dprintf0("read_pat: invalid PAT table size (%d > %d)\n", end, size-4);
+    return 1;
+  }
+  version = (pes[5] >> 1) & 0x1f;
+  sec = pes[6];
+  last_sec = pes[7];
+  if(last_sec >= MAX_PAT_SECTIONS) {
+    dprintf0("read_pat: illegal section count %d > %d\n",
+             last_sec, MAX_PAT_SECTIONS);
+    return 1;
+  }
+  if (pat->version != version || last_sec != pat->last_section) {
+    pat->version = version;
+    pat->last_section = last_sec;
+    while(! list_empty(&pat->dmx_filter_ll)) {
+      struct filter *filt = list_entry(pat->dmx_filter_ll.next, struct filter);
+      list_del(&filt->list);
+      while(! list_empty(&filt->sids)) {
+        sidnum = list_entry(filt->sids.next, struct sidnum);
+        list_del(&sidnum->list);
+        list_add_l(&sidnum->list, &sidnum_empty_queue, &list_lock);
+      }
+      list_add_l(&filt->list, &filt_empty_queue, &list_lock);
+    }
+  }
+  if(pat->section_seen[sec])
+    return 0;
+  pat->section_seen[sec] = 1;
+  for(ptr = pes + 8; ptr < pes + end; ptr += 4) {
+    int sid, pid;
+    struct filter *filt;
+    sid = (ptr[0] << 8) | ptr[1];
+    pid = ((ptr[2] & 0x1F) << 8) | ptr[3];
+    if(sid != 0) {
+      dprintf2("found PID: %d for sid: %d\n", pid, sid);
+      ll_find_elem(filt, pat->dmx_filter_ll, pid, pid, struct filter);
+      if(! filt) {
+        pop_entry_from_queue_l(filt, &filt_empty_queue, struct filter,
+                               &list_lock);
+        bzero(filt, sizeof(struct filter));
+        INIT_LIST_HEAD(&filt->sids);
+        list_add(&filt->list, &pat->dmx_filter_ll);
+        filt->pid = pid;
+      }
+      pop_entry_from_queue_l(sidnum, &sidnum_empty_queue, struct sidnum,
+                           &list_lock);
+      sidnum->sid = sid;
+      sidnum->seen = 0;
+      list_add(&sidnum->list, &filt->sids);
+    } else {
+      pat->has_nit = pid;
+      dprintf2("found NIT at PID: %d", pid);
+    }
+  }
+  return 0;
+}
+/*
+static int get_type(int type)
+{
+  switch (type) {
+    case 2:  //video
+      return 1;
+    case 4:  //audio
+      return 0;
+  }
+  return 5;
+}
+*/
+unsigned char *parse_ca(unsigned char *buf, unsigned char *ca, int len)
+{
+  unsigned char *ptr = buf;
+  int count = 0;
+  while (len >= 2) {
+    count = 2 + *(ca +1 );
+    if (*ca != 0x09) {
+      ca += count;
+      len -= count;
+      continue;
+    }
+    if(len < 0)
+      return NULL;
+    memcpy(ptr, ca, count);
+    len -= count;
+    ca += count;
+    ptr += count;
+  }
+  return ptr;
+}
+
+static int read_nit(unsigned char *buf, struct nit_data *nit, unsigned int size) {
+  int len, tsl_len, td_len, tag_len, network_desc_len;
+  int network_id, pos, tag;
+  if (buf[0] != 0x40 && buf[0] != 0x41 && buf[0] != 0x72) {
+    dprintf0(
+             "read_nit expected table 0x40  or 0x41 but got 0x%02x\n", buf[0]);
+    return -1;
+  }
+  if (buf[0] != 0x40) {
+    return 0;
+  }
+  len = ((buf[1] & 0x07) << 8) | buf[2];
+  network_id = (buf[3]<<8) | buf[4];
+  network_desc_len = ((buf[8] & 0x0f) << 8) | buf[9];
+  tsl_len = ((buf[10+network_desc_len] & 0x0f) << 8) | buf[11+network_desc_len];
+  pos = 12+network_desc_len;
+  while (pos-network_desc_len-12 < tsl_len - 6 && pos < len - 3) {
+    td_len = ((buf[pos+4] & 0x0f) << 8) | buf[pos+5];
+    while (td_len > 0) {
+      tag = buf[pos+6];
+      tag_len = buf[pos+7];
+      if(tag == 0x43 && tag_len >= 11) { //satellite descriptor
+        nit->frequency = (buf[pos+8] << 24) | (buf[pos+9]<<16) | (buf[pos+10]<<8) | buf[pos+11];
+        nit->orbit = (buf[pos+12] << 8) | buf[pos+13];
+        nit->is_east = buf[pos+14] >> 7;
+        nit->polarization = (buf[pos+14] >> 5) & 0x03;
+        nit->modulation = buf[pos+14] & 0x1f;
+        nit->symbolrate = (buf[pos+15]<<16) | (buf[pos+16]<<8) | buf[pos+17];
+        nit->fec = buf[pos+18];
+        printf("Orbit: %08x%c\n", nit->orbit, nit->is_east ? 'E' : 'W');
+        return 1;
+      }
+      pos += tag_len+2;
+      td_len -= tag_len+2;
+    }
+    pos += 6;
+  }
+  return 0;
+}
+static int read_pmt(unsigned char *buf, struct filter *filt,
+                    struct sid_data *sid_data, unsigned int size) {
+  //
+  // NOTE we aren't using last_sec here yet!
+  //
+  struct sid *sid_ll;
+  struct epid *epid_ll;
+  struct sidnum *sidnum;
+
+  unsigned char *captr;
+  unsigned int count, skip, pos;
+  int sid, sec, last_sec, pcrpid, epid, type;
+  if (buf[0] != 0x02) {
+    dprintf0(
+             "read_pmt expected table 0x02 but got 0x%02x\n", buf[0]);
+    return -1;
+  }
+  count = (((buf[1] & 0x03) << 8) | buf[2]) + 3 - 4;
+  sid = (buf[3] << 8) | buf[4];
+  sec = buf[6];
+  last_sec = buf[7];
+  pcrpid = ((buf[8] & 0x1F) << 8) | buf[9];
+  skip = ((buf[10] & 0x03) << 8) | buf[11];
+  if(skip > count - 12 || count > size) {
+    dprintf0("skip: %d > count: %d - 12 || count > size: %d\n",
+           skip, count, size);
+    return -1;
+  }
+
+  pop_entry_from_queue_l(sid_ll, &sid_empty_queue, struct sid, &list_lock);
+  INIT_LIST_HEAD(&sid_ll->epid);
+
+  captr = parse_ca(sid_ll->ca, buf + 12, skip);
+  if(captr == NULL) {
+    dprintf0("Bad CA found\n");
+    free_sid(sid_ll);
+    return -1;
+  }
+  dprintf3("read_pmt: sid: %d pcrpid: %d skip: %d count: %d\n", sid, pcrpid, skip, count); 
+  ll_find_elem(sidnum, filt->sids, sid, sid, struct sidnum);
+  if(sidnum == NULL) {
+    dprintf1("Sid %d is unexpected\n", sid);
+    free_sid(sid_ll);
+    return -1;
+  }
+  if(sidnum->seen) {
+    dprintf0("Already seen sid: %d\n", sid);
+    free_sid(sid_ll);
+    return -1;
+  }
+  sidnum->seen = 1;
+  sid_ll->sid = sid;
+  for(pos = 12 + skip; pos < count;) {
+    type = buf[pos];
+    epid = ((buf[pos+1] & 0x1F) << 8) | buf[pos+2];
+    skip = ((buf[pos+3] & 0x03) << 8) | buf[pos+4];
+    captr = parse_ca(captr, buf+pos+5, skip);
+    pop_entry_from_queue_l(epid_ll, &epid_empty_queue, struct epid, &list_lock);
+    
+    epid_ll->epid = epid;
+    epid_ll->type = type;
+    list_add_tail(&epid_ll->list, &sid_ll->epid);
+    dprintf3("read_pmt: epid %d (type %d) mapped to sid %d\n", epid, type, sid);
+    pos += 5 + skip;
+  }
+  sid_ll->calen = captr - sid_ll->ca;
+  pthread_mutex_lock(&sid_data->mutex);
+  if(! sid_data->has_map) {
+    pthread_mutex_unlock(&sid_data->mutex);
+    free_sid(sid_ll);
+    return -1;
+  }
+  list_add(&sid_ll->list, &sid_data->sidlist);
+  pthread_mutex_unlock(&sid_data->mutex);
+  return 0;
+}
+
+static int start(char *dmxdev, struct sid_data *sid_data, int timeout) {
+  unsigned char pes[4096];
+  struct pollfd  pfd, pollfd[MAX_SIMULTANEOUS_PMT];
+  int done = 0, size, i;
+  int ret;
+
+  struct pat pat;
+  struct list_head *lptr;
+
+  bzero(&pat, sizeof(struct pat));
+  INIT_LIST_HEAD(&pat.dmx_filter_ll);
+  pat.patfd = open(dmxdev, O_RDWR | O_NONBLOCK);
+  if(pat.patfd < 0) {
+    perror("start: open returned:");
+    free_pat(&pat);
+    return 1;
+  }
+  if(add_pid(pat.patfd, 0x00)) {
+    perror("start: failed to initialize pat demux:");
+    free_pat(&pat);
+    return 1;
+  }
+
+  pfd.fd = pat.patfd;
+  pfd.events = POLLIN;
+  pfd.revents = 0;
+  while(! done) {
+    poll(&pfd, 1, timeout);
+    if ((size = read(pat.patfd, pes, sizeof(pes))) < 0) {
+      dprintf0("start: read pes returned err: %d\n", errno);
+      perror("start: read pes returned");
+      close(pat.patfd);
+      free_pat(&pat);
+      return 1;
+    }
+///read_pat
+    if(read_pat(pes, &pat, size)) {
+      close(pat.patfd);
+      free_pat(&pat);
+      return 1;
+    }
+    done = 1;
+    for (int i = 0; i < MAX_PAT_SECTIONS; i++) {
+      if(pat.section_seen == 0) {
+        done = 0;
+        break;
+      }
+    }
+  }
+  close(pat.patfd);
+
+  if(opt_orbit) {
+    sid_data->nit.orbit = opt_orbit;
+    pat.has_nit = 0;
+  }
+
+  if(pat.has_nit) {
+    int fd;
+    fd=open(dmxdev, O_RDWR | O_NONBLOCK);
+    if (fd <= 0) {
+      dprintf0("start: Failed to open demux device for NIT!\n");
+      perror("demux open failed:");
+    }
+    else if (add_pid(fd, pat.has_nit)) {
+        char str[80];
+        sprintf(str, "start: Failed to initialize NIT demux");
+        perror(str);
+    } else {
+      pfd.fd = fd;
+      pfd.events = POLLIN;
+      pfd.revents = 0;
+      done = 0;
+      int nit_retries = 0; 
+      while(done <= 0) { 
+        poll(&pfd, 1, timeout);
+        if ((size = read(fd, pes, sizeof(pes))) < 0) {
+          dprintf0("start: read pes returned err: %d\n", errno);
+          perror("start: read pes returned");
+        }
+        done = read_nit(pes, &sid_data->nit, size);
+        if (done <= 0) 
+          nit_retries++; 
+        if (nit_retries == 5) { 
+          dprintf0("start: giving up reading nit\n"); 
+          done = 1; 
+        } 
+      }
+    }
+    close(fd);
+  }
+  while(1) {
+    int num_filters = 0, found = 0;
+    ret = 0;
+
+    list_for_each(lptr, &pat.dmx_filter_ll) {
+      struct filter *filt = list_entry(lptr, struct filter);
+      if(filt->used) {
+        if(filt->fd >0) {
+          close(filt->fd);
+          filt->fd = -1;
+        }
+        continue;
+      }
+      filt->fd=open(dmxdev, O_RDWR | O_NONBLOCK);
+      if (filt->fd <= 0) {
+        dprintf0("start: Failed to open %dth demux device!\n", num_filters);
+        perror("demux open failed:");
+        ret = 1;
+        break;
+      }
+      if (add_pid(filt->fd, filt->pid)) {
+        char str[80];
+        sprintf(str, "start: Failed to initialize %dth pmt demux", num_filters);
+        perror(str);
+        close(filt->fd);
+        filt->fd = -1;
+        ret = 1;
+        break;
+      }
+      filt->used = 1;
+      num_filters++;
+      if(num_filters >= opt_maxfilters) {
+        ret = 1;
+        break;
+      }
+    }
+
+    if(! num_filters)
+      break;
+
+    dprintf2("Reading %d of %d filters\n", num_filters, opt_maxfilters);
+    while(found != num_filters) {
+      int count = 0;
+      list_for_each(lptr, &pat.dmx_filter_ll) {
+        struct filter *filt = list_entry(lptr, struct filter);
+        if (filt->used && filt->fd > 0 && ! list_empty(&filt->sids)) {
+          pollfd[count].fd=filt->fd;
+          pollfd[count].events = POLLIN;
+          pollfd[count].revents = 0;
+          count++;
+        }
+      }
+      dprintf3("polling %d fds\n", count);
+      poll(pollfd, count, timeout);
+      for(i = 0; i < count; i++) {
+        if(! (pollfd[i].revents & POLLIN))
+          continue;
+        // read pmt
+        while((size = read(pollfd[i].fd, pes, sizeof(pes))) >= 3) {
+          struct filter *filt;
+          dprintf3("Read %d bytes\n", size);
+          ll_find_elem(filt, pat.dmx_filter_ll, fd, pollfd[i].fd, struct filter);
+          int ret = read_pmt(pes, filt, sid_data, size);
+          if(ret < 0) {
+             filt->parse_err++;
+             if (filt->parse_err > opt_max_fail)
+               goto exit;
+          }
+          if(ret == 0) {
+            struct sidnum *sidnum;
+            filt->parse_err = 0;
+            ll_find_elem(sidnum, filt->sids, seen, 0, struct sidnum);
+            if(sidnum == NULL) {
+              found ++;
+              break;
+            }
+          } else {
+            pthread_mutex_lock(&sid_data->mutex);
+            ret = ! sid_data->has_map;
+            pthread_mutex_unlock(&sid_data->mutex);
+            if(ret)
+              goto exit;
+          }
+        }
+      }
+      pthread_mutex_lock(&sid_data->mutex);
+      ret = ! sid_data->has_map;
+      pthread_mutex_unlock(&sid_data->mutex);
+      if(ret)
+        goto exit;
+    }
+  }
+exit:
+  free_pat(&pat);
+  return ret;
+}
+
+static int check_av(unsigned char type) {
+  switch(type) {
+    case MPEG1Video:
+    case MPEG2Video:
+    case MPEG4Video:
+    case H264Video:
+    case OpenCableVideo:
+      return 1;
+    case MPEG1Audio:
+    case MPEG2Audio:
+    case MPEG2AudioAmd1:
+    case AACAudio:
+    case AC3Audio:
+    case DTSAudio:
+      return 1;
+    case MHEG:
+    case H222_1:
+      return 1;
+    default:
+      return 0;
+  }
+  return 0;
+}
+
+struct sid* find_pid(struct list_head *sidlist, unsigned int pid) {
+  struct list_head *ptr, *ptr1;
+  struct sid *sid_ll;
+  struct epid *epid_ll;
+  list_for_each(ptr, sidlist) {
+    sid_ll = list_entry(ptr, struct sid);
+    list_for_each(ptr1, &sid_ll->epid) {
+      epid_ll = list_entry(ptr1, struct epid);
+      if(epid_ll->epid == pid) {
+        if(opt_allpids || check_av(epid_ll->type)) {
+          return sid_ll;
+        } else {
+          dprintf3("Skipping pid %d because type is %d\n", epid_ll->epid,
+                   epid_ll->type);
+          return NULL;
+        }
+      }
+    }
+  }
+  return NULL;
+}
+static void *read_sid(void *arg)
+{
+  struct sid_data *sid_data = (struct sid_data *)arg;
+  struct sid *sid_ll;
+  struct dmxcmd *dmxcmd;
+  char dmxdev[256];
+  int ret;
+
+  sprintf(dmxdev, "/dev/dvb/adapter%d/demux0", sid_data->common->real_adapt);
+  pthread_mutex_lock(&sid_data->mutex);
+  while(1) {
+    while(1) {
+      ll_find_elem(dmxcmd, sid_data->cmdqueue, checked, 0, struct dmxcmd);
+      if(dmxcmd) {
+          break;
+      }
+      pthread_cond_wait(&sid_data->cond, &sid_data->mutex);
+    }
+    //pop_entry_from_queue_l(dmxcmd, &sid_data->cmdqueue, struct dmxcmd,
+    //                       &list_lock);
+    //dmxcmd_stored = 0;
+    if(dmxcmd->fd == -1 && dmxcmd->sid == 0 && dmxcmd->sid == NULL)
+      return NULL;
+    if(! sid_data->has_map) {
+      //Try to read pmt, use a short-timeout in case the lock
+      //hasn't happened yet
+      pthread_mutex_unlock(&sid_data->mutex);
+      dprintf2("Got Start!\n");
+      //set has_map first, since it could get invalidated...
+      sid_data->has_map = 1;
+      ret =  start(dmxdev, sid_data, 200 /*ms*/);
+      dprintf2("returned: %d\n", ret);
+      if(ret)
+        usleep(200000); /*200ms*/
+      pthread_mutex_lock(&sid_data->mutex);
+      if(ret || sid_data->has_map == 0) {
+        sid_data->has_map = 0;
+        while(! list_empty(&sid_data->sidlist)) {
+          sid_ll = list_entry(sid_data->sidlist.next, struct sid);
+          list_del(&sid_ll->list);
+          free_sid(sid_ll);
+        }
+      }
+      //We skip processing in case the dmx was closed while pmt parsing
+      continue;
+    }
+    dprintf2("Got Pid: %d\n", dmxcmd->pid);
+    dmxcmd->checked = 1;
+    if((sid_ll = find_pid(&sid_data->sidlist, dmxcmd->pid))) {
+      dprintf1("Found sid: %lu\n", sid_ll->sid);
+      dmxcmd->sid = sid_ll;
+      if (sid_data->sendmsg) {
+        struct list_head *lptr;
+        struct sid_msg *sidmsg;
+        struct epid *epid;
+        int count = 0;
+        //Duplicate dmxcmds to prevent a race
+        pop_entry_from_queue_l(sidmsg, &sidmsg_empty_queue, struct sid_msg,
+                               &list_lock);
+        sidmsg->sid = dmxcmd->sid->sid;
+        sidmsg->calen = dmxcmd->sid->calen;
+        memcpy(sidmsg->ca, dmxcmd->sid->ca, dmxcmd->sid->calen);
+        list_for_each(lptr, &dmxcmd->sid->epid) {
+          epid = list_entry(lptr, struct epid);
+          sidmsg->epid[count] = epid->epid;
+          sidmsg->epidtype[count] = epid->type;
+          count++;
+        }
+        sidmsg->epid_count = count;
+        sidmsg->nit = sid_data->nit;
+        msg_send(MSG_LOW_PRIORITY, MSG_ADDSID, sid_data->common->real_adapt,
+                 sidmsg);
+      } else {
+        dprintf0("Didn't find sid for pid: %d\n", dmxcmd->pid);
+      }
+    }
+  }
+}
+
+static struct sid_data *find_siddata_from_pc(struct parser_cmds *pc)
+{
+  struct list_head *ptr;
+  list_for_each(ptr, &getsidlist) {
+    struct sid_data *sid_data = list_entry(ptr, struct sid_data);
+    if(sid_data->common == pc->common)
+      return sid_data;
+  }
+  return NULL;
+}
+
+static void clear_sid_data(struct sid_data *sid_data)
+{
+    int adapt;
+    struct sid *sid_ll;
+    struct dmxcmd *dmxcmd;
+
+  
+    adapt = sid_data->common->real_adapt;
+    while(! list_empty(&sid_data->sidlist)) {
+      sid_ll = list_entry(sid_data->sidlist.next, struct sid);
+      list_del(&sid_ll->list);
+      free_sid(sid_ll);
+    }
+  
+    //reset queued pids
+    {
+      struct list_head *lptr;
+      list_for_each(lptr, &sid_data->cmdqueue) {
+        dmxcmd = list_entry(lptr, struct dmxcmd);
+        dmxcmd->checked = 0;
+        dmxcmd->sid = NULL;
+      }
+    }
+    //force a rescan of pmts
+    sid_data->has_map = 0;
+  
+    pthread_cond_signal(&sid_data->cond);
+}
+
+static void fe_tune(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+  struct sid_data *sid_data;
+  int adapt;
+
+  sid_data = find_siddata_from_pc(pc);
+  adapt = sid_data->common->real_adapt;
+  if(sid_data == NULL)
+    return;
+
+  if(cmd == FE_SET_FRONTEND || cmd == FE_SET_FRONTEND2) {
+    dprintf0("Tuning frontend\n");
+    if(memcmp(&sid_data->tunecache, data, sizeof(struct dvb_frontend_parameters))) {
+      pthread_mutex_lock(&sid_data->mutex);
+      memcpy(&sid_data->tunecache, data, sizeof(struct dvb_frontend_parameters));
+      if(sid_data->sendmsg) {
+        msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
+                                  free_addsid_msg);
+        msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
+       msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+      } 
+      msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+
+      clear_sid_data(sid_data);
+
+      pthread_mutex_unlock(&sid_data->mutex);
+    } else {
+      dprintf0("Skipping cache reset since tuning matches last tune\n");
+      if(opt_experimental)
+        *result = CMD_SKIPCALL;
+    }
+#if DVB_API_VERSION >=5
+  } else if (cmd == FE_SET_PROPERTY) {
+    dprintf0("Tuning frontend (new)\n");
+    pthread_mutex_lock(&sid_data->mutex);
+    if(sid_data->sendmsg) {
+      msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
+                                  free_addsid_msg);
+      msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
+      msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+    } 
+    msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+    clear_sid_data(sid_data);
+    pthread_mutex_unlock(&sid_data->mutex);
+    memset(&sid_data->tunecache, 0, sizeof(struct dvb_frontend_parameters));
+#endif
+  } else if(cmd == FE_DISEQC_SEND_MASTER_CMD
+            || cmd == FE_DISEQC_SEND_BURST
+            || cmd == FE_SET_TONE
+            || cmd == FE_SET_VOLTAGE
+#ifdef FE_SET_STANDARD
+            || cmd == FE_SET_STANDARD
+#endif
+#ifdef FE_DISHNETWORK_SEND_LEGACY_CMD
+            || cmd == FE_DISHNETWORK_SEND_LEGACY_CMD
+#endif
+    ) 
+  {
+    dprintf0("Clearing tuning cache due to switch cmd\n");
+    memset(&sid_data->tunecache, 0, sizeof(struct dvb_frontend_parameters));
+  }
+}
+
+static void fe_close(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+  struct sid_data *sid_data;
+  int adapt;
+
+  sid_data = find_siddata_from_pc(pc);
+  adapt = sid_data->common->real_adapt;
+
+  if(sid_data == NULL)
+    return;
+
+  if((fdptr->flags & O_ACCMODE) == O_RDONLY)
+    return;
+
+  pthread_mutex_lock(&sid_data->mutex);
+  if(sid_data->sendmsg) {
+    msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
+                              free_addsid_msg);
+    msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
+    msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+  }
+  msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
+  pthread_mutex_unlock(&sid_data->mutex);
+}
+
+//if a demux is closed, we really don't want to clear the pid<->sid map
+//insead we should only do something when a mapped pid is closed, and the
+//'something' should be to invalidate the associated sid
+static void close_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
+                         cmdret_t *result, int *ret,
+                         unsigned long int cmd, unsigned char *data)
+{
+  struct sid_data *sid_data;
+  struct dmxcmd *dmxcmd, *tmpdmx;
+
+  sid_data = find_siddata_from_pc(pc);
+  if(sid_data == NULL)
+    return;
+
+  pthread_mutex_lock(&sid_data->mutex);
+  //if(sid_data->sendmsg)
+    //remove only GETSID on this demux!
+    //msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, free_addsid_msg);
+
+  //removed this fd from the cmdqueue
+  ll_find_elem(dmxcmd, sid_data->cmdqueue, fd, fdptr->fd, struct dmxcmd);
+  if(dmxcmd) {
+    list_del(&dmxcmd->list);
+    if(dmxcmd->checked && dmxcmd->sid) {
+      msg_send(MSG_LOW_PRIORITY, MSG_REMOVESID, sid_data->common->real_adapt,
+               (void *)(dmxcmd->sid->sid));
+      //check that this sid is no longer being viewed
+      ll_find_elem(tmpdmx, sid_data->cmdqueue, sid, dmxcmd->sid, struct dmxcmd);
+      if(! tmpdmx) {
+        sid_data->removed_sid = 1;
+      }
+    }
+    list_add_l(&dmxcmd->list, &dmxcmd_empty_queue, &list_lock);
+  }
+  pthread_mutex_unlock(&sid_data->mutex);
+}
+
+static void set_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret,
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct sid_data *sid_data;
+  struct dmxcmd *dmxcmd;
+  int pid;
+
+  sid_data = find_siddata_from_pc(pc);
+  if(sid_data == NULL)
+    return;
+
+  if(cmd == DMX_SET_PES_FILTER) {
+    pid = *(unsigned short *)data; //get pid from *filter_params
+    pthread_mutex_lock(&sid_data->mutex);
+
+    if(opt_resetpidmap && sid_data->removed_sid) {
+      sid_data->removed_sid = 0;
+      //NOTE: If this occurs while a 'ADD_SID' is in the message queue, 
+      //then the message will be corrupted!  We should deep-copy the epids
+      clear_sid_data(sid_data);
+    }
+
+    //Make sure we didn't just change pids on the same fd
+    ll_find_elem(dmxcmd, sid_data->cmdqueue, fd, fdptr->fd, struct dmxcmd);
+    if(dmxcmd) {
+      if(dmxcmd->sid)
+        msg_send(MSG_LOW_PRIORITY, MSG_REMOVESID, sid_data->common->real_adapt,
+                 (void *)(dmxcmd->sid->sid));
+      list_del(&dmxcmd->list);
+      list_add_l(&dmxcmd->list, &dmxcmd_empty_queue, &list_lock);
+    }
+
+    pop_entry_from_queue_l(dmxcmd, &dmxcmd_empty_queue, struct dmxcmd,
+                           &list_lock);
+    dmxcmd->pid = pid;
+    dmxcmd->fd = fdptr->fd;
+    dmxcmd->sid = NULL;
+    dmxcmd->checked = 0;
+    list_add_tail(&dmxcmd->list, &sid_data->cmdqueue);
+
+    dprintf1("Sending PID: %d\n", pid);
+    pthread_cond_signal(&sid_data->cond);
+    pthread_mutex_unlock(&sid_data->mutex);
+  }
+}
+
+static void enable_msg(struct parser_cmds *pc, int enable)
+{
+  struct sid_data *sid_data = find_siddata_from_pc(pc);
+  if(sid_data)
+    sid_data->sendmsg = enable;
+}
+
+static void connect_sid(struct parser_adpt *pc_all)
+{
+  struct sid_data *sid_data;
+
+  sid_data = (struct sid_data *)malloc(sizeof(struct sid_data));
+  if(! sid_data)
+    return;
+  bzero(sid_data, sizeof(struct sid_data));
+  //these shouldn't be needed
+  sid_data->sendmsg = 0;
+  sid_data->has_map = 0;
+  sid_data->common = pc_all->frontend->common;
+  pthread_mutex_init(&sid_data->mutex, NULL);
+  pthread_cond_init(&sid_data->cond, NULL);
+  INIT_LIST_HEAD(&sid_data->sidlist);
+  INIT_LIST_HEAD(&sid_data->cmdqueue);
+  if(opt_experimental) {
+    ATTACH_CALLBACK(&pc_all->frontend->pre_ioctl,  fe_tune, -1);
+  } else {
+    ATTACH_CALLBACK(&pc_all->frontend->post_ioctl, fe_tune, -1);
+  }
+  ATTACH_CALLBACK(&pc_all->frontend->post_close, fe_close,    -1);
+  ATTACH_CALLBACK(&pc_all->demux->post_ioctl,    set_demux,   -1);
+  ATTACH_CALLBACK(&pc_all->demux->post_close,    close_demux, -1);
+  list_add_tail(&sid_data->list, &getsidlist);
+}
+
+static void launch_sid()
+{
+  struct list_head *ptr;
+  list_for_each(ptr, &getsidlist) {
+    struct sid_data *sid_data = list_entry(ptr, struct sid_data);
+    pthread_create(&sid_data->thread, &default_attr, read_sid, sid_data);
+  }
+}
+
+static void shutdown_sid()
+{
+  struct dmxcmd *dmxcmd;
+  struct list_head *ptr;
+  list_for_each(ptr, &getsidlist) {
+    struct sid_data *sid_data = list_entry(ptr, struct sid_data);
+    if(sid_data->thread) {
+      pthread_mutex_lock(&sid_data->mutex);
+      pop_entry_from_queue_l(dmxcmd, &dmxcmd_empty_queue, struct dmxcmd,
+                             &list_lock);
+      dmxcmd->pid = 0;
+      dmxcmd->fd = -1; //Send a terminate command
+      dmxcmd->sid = NULL;
+      dmxcmd->checked = 0;
+      list_add(&dmxcmd->list, &sid_data->cmdqueue);
+      pthread_cond_signal(&sid_data->cond);
+      pthread_mutex_unlock(&sid_data->mutex);
+      pthread_join(sid_data->thread, NULL);
+    }
+  }
+}
+
+static struct option *parseopt_sid(arg_enum_t cmd)
+{
+  if(cmd == ARG_INIT) {
+    return Sid_Opts;
+  } 
+  if(cmd == ARG_HELP) {
+    printf("   --sid-filt <num>  : Maximum number of open filters (default 2)\n");
+    printf("   --sid-allpid      : Parse all pids instead of just A/V\n");
+    printf("   --sid-nocache     : Don't cache pid<->sid mapping\n");
+    printf("   --sid-orbit <val> : Set the satellit orbit to 'val' and don't scan the NIT\n");
+    printf("   --sid-experimental: Enable experimental tuning code\n");
+  } 
+  if(! sid_opt)
+    return NULL;
+
+  switch(sid_opt) {
+    case 'f':
+      opt_maxfilters = atoi(optarg);
+      if (opt_maxfilters < 1)
+        opt_maxfilters = 1;
+      else if (opt_maxfilters > MAX_SIMULTANEOUS_PMT)
+        opt_maxfilters = MAX_SIMULTANEOUS_PMT;
+      break;
+    case 'p':
+      opt_allpids = 1;
+      break;
+    case 'o':
+      opt_orbit = strtol(optarg, NULL, 0);
+      break;
+    case 'c':
+      opt_resetpidmap = 1;
+    case 'e':
+      opt_experimental = 1;
+  }
+  //must reset sid_opt after every call
+  sid_opt = 0;
+  return NULL;
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "channel",
+                parseopt_sid, connect_sid, launch_sid, NULL, enable_msg,
+                shutdown_sid, NULL};
+int __attribute__((constructor)) __getsid_init(void)
+{
+  list_add(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_getsid.h b/contrib/sasc-ng/dvbloopback/src/plugin_getsid.h
new file mode 100644 (file)
index 0000000..1b1d6d8
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef _PLUGIN_GETSID_H_
+#define _PLUGIN_GETSID_H_
+
+#include "process_req.h"
+#include "messages.h"
+#define PLUGIN_GETSID 8
+
+#define MAX_EPID 128
+struct nit_data {
+  unsigned int frequency;
+  unsigned short orbit;
+  unsigned char is_east;
+  unsigned char polarization;
+  unsigned char modulation;
+  unsigned int symbolrate;
+  unsigned char fec;
+};
+
+struct sid_msg {
+  struct list_head list;
+  unsigned long sid;
+  int calen;
+  int epid_count;
+  unsigned char ca[1024];
+  int epid[MAX_EPID];
+  unsigned char epidtype[MAX_EPID];
+  struct nit_data nit;
+};
+
+struct epid {
+  struct list_head list;
+  unsigned int epid;
+  unsigned char type;
+};
+
+struct sid {
+  struct list_head list;
+  unsigned long sid;
+  struct list_head epid;
+  unsigned char ca[1024];
+  int calen;
+};
+
+struct dmxcmd {
+  struct list_head list;
+  int pid;
+  int fd;
+  int checked;
+  struct sid *sid;
+};
+
+struct sid_data {
+  struct list_head list;
+  struct common_data *common;
+
+  struct list_head cmdqueue;
+  struct list_head sidlist;
+
+  struct nit_data nit;
+  int has_map;
+  int removed_sid;
+  int sendmsg;
+  unsigned char tunecache[256]; //Should be large enough
+  pthread_t thread;
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+};
+
+enum {
+  GETSID_CMD_RESET  = 0,
+  GETSID_CMD_START  = 1,
+  GETSID_CMD_PID    = 2
+};
+
+enum
+{
+    // video
+    MPEG1Video     = 0x01, ///< ISO 11172-2 (aka MPEG-1)
+    MPEG2Video     = 0x02, ///< ISO 13818-2 & ITU H.262 (aka MPEG-2)
+    MPEG4Video     = 0x10, ///< ISO 14492-2 (aka MPEG-4)
+    H264Video      = 0x1b, ///< ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
+    OpenCableVideo = 0x80,
+
+    // audio
+    MPEG1Audio     = 0x03, ///< ISO 11172-3
+    MPEG2Audio     = 0x04, ///< ISO 13818-3
+    MPEG2AudioAmd1 = 0x11, ///< ISO 13818-3/AMD-1 Audio using LATM syntax
+    AACAudio       = 0x0f, ///< ISO 13818-7 Audio w/ADTS syntax
+    AC3Audio       = 0x81,
+    DTSAudio       = 0x8a,
+
+    // DSM-CC Object Carousel
+    DSMCC          = 0x08, ///< ISO 13818-1 Annex A DSM-CC & ITU H.222.0
+    DSMCC_A        = 0x0a, ///< ISO 13818-6 type A Multi-protocol Encap
+    DSMCC_B        = 0x0b, ///< ISO 13818-6 type B Std DSMCC Data
+    DSMCC_C        = 0x0c, ///< ISO 13818-6 type C NPT DSMCC Data
+    DSMCC_D        = 0x0d, ///< ISO 13818-6 type D Any DSMCC Data
+    DSMCC_DL       = 0x14, ///< ISO 13818-6 Download Protocol
+    MetaDataPES    = 0x15, ///< Meta data in PES packets
+    MetaDataSec    = 0x16, ///< Meta data in metadata_section's
+    MetaDataDC     = 0x17, ///< ISO 13818-6 Metadata in Data Carousel 
+    MetaDataOC     = 0x18, ///< ISO 13818-6 Metadata in Object Carousel 
+    MetaDataDL     = 0x19, ///< ISO 13818-6 Metadata in Download Protocol
+
+    // other
+    PrivSec        = 0x05, ///< ISO 13818-1 private tables   & ITU H.222.0
+    PrivData       = 0x06, ///< ISO 13818-1 PES private data & ITU H.222.0
+
+    MHEG           = 0x07, ///< ISO 13522 MHEG
+    H222_1         = 0x09, ///< ITU H.222.1
+
+    MPEG2Aux       = 0x0e, ///< ISO 13818-1 auxiliary & ITU H.222.0
+
+    FlexMuxPES     = 0x12, ///< ISO 14496-1 SL/FlexMux in PES packets
+    FlexMuxSec     = 0x13, ///< ISO 14496-1 SL/FlexMux in 14496_sections
+
+    MPEG2IPMP      = 0x1a, ///< ISO 13818-10 Digital Restrictions Mangment
+    MPEG2IPMP2     = 0x7f, ///< ISO 13818-10 Digital Restrictions Mangment
+
+    // special id's, not actually ID's but can be used in FindPIDs
+    AnyMask        = 0xFFFF0000,
+    AnyVideo       = 0xFFFF0001,
+    AnyAudio       = 0xFFFF0002,
+};
+
+void free_sidmsg(struct sid_msg *sidmsg);
+#endif
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_legacysw.c b/contrib/sasc-ng/dvbloopback/src/plugin_legacysw.c
new file mode 100644 (file)
index 0000000..bb1d61f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include "list.h"
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+
+#include "process_req.h"
+#include "msg_passing.h"
+
+#define PLUGIN_ID 9
+#define DBG_NAME "LEGACYSW"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+#ifdef FE_DISHNETWORK_SEND_LEGACY_CMD
+enum {
+  LEGSW_UNKNOWN = 0,
+  LEGSW_SW64,
+  LEGSW_SW21,
+};
+static int legsw_opt = 0;
+static int opt_mapdiseqc = LEGSW_UNKNOWN;
+static struct option LegacySW_Opts[] = {
+  {"legsw-diseqcmap", 1, &legsw_opt, 'd'},
+  {0, 0, 0, 0},
+};
+
+static void fe_ioctl(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+    if(FE_DISEQC_SEND_MASTER_CMD == cmd) {
+      struct dvb_diseqc_master_cmd *msg = (struct dvb_diseqc_master_cmd *)data;
+      unsigned char lcmd = 0;
+      if(msg->msg_len != 4)
+        return;
+      if(msg->msg[0] != 0xe0 || msg->msg[1] != 0x10 || msg->msg[2] != 0x38)
+        return;
+      switch(msg->msg[3] & 0x0c) {
+        case 0x00: //Port 0
+          if(opt_mapdiseqc == LEGSW_SW64) {
+            if(msg->msg[3] & 0x02) {
+              lcmd = 0x1a;
+            } else {
+              lcmd = 0x39;
+            }
+          } else if(opt_mapdiseqc == LEGSW_SW21) {
+            lcmd = 0x34;
+          }
+          break;
+        case 0x04: //Port 1
+          if(opt_mapdiseqc == LEGSW_SW64) {
+            if(msg->msg[3] & 0x02) {
+              lcmd = 0x5c;
+            } else {
+              lcmd = 0x4b;
+            }
+          } else if(opt_mapdiseqc == LEGSW_SW21) {
+            lcmd = 0x65;
+          }
+          break;
+        case 0x08: //Port 2
+          if(opt_mapdiseqc == LEGSW_SW64) {
+            if(msg->msg[3] & 0x02) {
+              lcmd = 0x2e;
+            } else {
+              lcmd = 0x0d;
+            }
+          }
+          break;
+      }
+      printf("Got Diseqc cmd!!!\n len: %d %02x->%02x\n", msg->msg_len, msg->msg[3], lcmd);
+      if (lcmd == 0)
+        return;
+      if(msg->msg[3] & 0x02)
+        lcmd |= 0x80;
+      *result = CMD_SKIPCALL;
+      *ret = ioctl(fdptr->fd, FE_DISHNETWORK_SEND_LEGACY_CMD, lcmd);
+      if(*ret < 0) {
+        *ret = errno;
+      }
+    }
+}
+
+static void connect_legsw(struct parser_adpt *pc_all)
+{
+  if(opt_mapdiseqc) {
+    ATTACH_CALLBACK(&pc_all->frontend->pre_ioctl, fe_ioctl, -1);
+  }
+}
+static struct option *parseopt_legsw(arg_enum_t cmd)
+{
+  if(cmd == ARG_INIT) {
+    return LegacySW_Opts;
+  } 
+  if(cmd == ARG_HELP) {
+    printf("   --legsw-diseqcmap <legacy-type> :\n");
+    printf("                        map diseqc switch cmds to legacy cmds\n");
+    printf("                        legacy-type must be sw64 or sw21\n");
+  } 
+  if(! legsw_opt)
+    return NULL;
+
+  switch(legsw_opt) {
+    case 'd':
+      if(strncmp(optarg, "sw64", 4) == 0) {
+        opt_mapdiseqc = LEGSW_SW64;
+      } else if(strncmp(optarg, "sw21", 4) == 0) {
+        opt_mapdiseqc = LEGSW_SW21;
+      }
+      break;
+  }
+  //must reset sid_opt after every call
+  legsw_opt = 0;
+  return NULL;
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, 
+                     "legacy switch",
+                     parseopt_legsw, connect_legsw, NULL, NULL, NULL, NULL, NULL};
+int __attribute__((constructor)) __legacysw_init(void)
+{
+  list_add(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
+#endif //FE_DISHNETWORK_SEND_LEGACY_CMD
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.c b/contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.c
new file mode 100644 (file)
index 0000000..65398c8
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/timeb.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include "process_req.h"
+#include "plugin_ringbuf.h"
+#include "msg_passing.h"
+
+#define PLUGIN_ID PLUGIN_RINGBUF
+#define DBG_NAME "RINGBUF"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+//#define CHECK_READ_OK
+//#define POLL_WORKS
+
+//defining WRITE_RAW_DVR will creat /tmp/rawdvr.mpg and write out all data
+//read from the 'real' dvr to that file
+//#define WRITE_RAW_DVR
+#ifdef WRITE_RAW_DVR
+static int rawdvr;
+#endif
+
+static LIST_HEAD(ringbuflist);
+static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
+static int rb_sendmsg;
+static int (* rb_check_read_callback)(struct ringbuffer *, int) = NULL;
+#define min(a,b) ((a) < (b) ? (a) : (b))
+inline int min1(int a, int b) { return min(a, b); }
+unsigned int mtime() {
+  struct timeb tp;
+  unsigned int time0;
+  ftime(&tp);
+  time0 = tp.time * 1000 + tp.millitm;
+  return time0;
+}
+
+static int rb_max_free(struct ringbuffer *rb)
+{
+  int allowed_bytes;
+  if(rb->wrPtr >= rb->rdPtr)
+    allowed_bytes = rb->end - rb->wrPtr + min1(rb->rdPtr - rb->buffer, rb->reserved) - TSPacketSIZE;
+  else
+    allowed_bytes = rb->rdPtr - rb->wrPtr - TSPacketSIZE;
+  return allowed_bytes;
+}
+
+static int rb_free(struct ringbuffer *rb, int numbytes)
+{
+  return (rb_max_free(rb) >= numbytes);
+}
+
+static int rb_avail(struct ringbuffer *rb, int numbytes)
+{
+  int avail_bytes;
+  if(rb->rdPtr > rb->wrPtr)
+    avail_bytes = rb->end - rb->rdPtr + rb->wrPtr - rb->buffer - TSPacketSIZE;
+  else
+    avail_bytes = rb->wrPtr - rb->rdPtr - TSPacketSIZE;
+  if (avail_bytes && avail_bytes >= numbytes && rb_check_read_callback) {
+    avail_bytes = rb_check_read_callback(rb, numbytes);
+  }
+  return (avail_bytes);
+}
+
+static void rb_fill_bytes(struct ringbuffer *rb, int numbytes) {
+  int bytes;
+  unsigned char *newptr;
+  //printf("Start read on dvr t:%lu\n", pthread_self());
+  //wrPtr must always be an integer number of packets!
+  numbytes -= numbytes % TSPacketSIZE;
+  bytes = read(rb->fd, rb->wrPtr, numbytes);
+  //printf("Read done\n");
+  if (bytes <= 0) {
+    if (!(rb->flags & O_NONBLOCK))
+      perror("Read Failed");
+    return;
+  }
+#ifdef WRITE_RAW_DVR
+  write(rawdvr, rb->wrPtr, bytes);
+#endif
+  newptr = rb->wrPtr + bytes;
+  if (newptr > rb->end)
+    memcpy(rb->buffer, rb->end, newptr - rb->end);
+  if (newptr >= rb->end)
+    newptr = rb->buffer + (newptr - rb->end);
+
+  pthread_mutex_lock(&rb->rw_lock);
+  rb->wrPtr = newptr;
+  pthread_mutex_unlock(&rb->rw_lock);
+  if(rb_sendmsg)
+    msg_replace(MSG_HIGH_PRIORITY, MSG_RINGBUF, rb->num, rb);
+}
+
+static int rb_fill_bytes_block(struct ringbuffer *rb, int numbytes) {
+  int bytes = 0, ret, size;
+  unsigned char *newptr;
+  struct pollfd ufd[2];
+  ufd[0].fd = rb->fd;
+  ufd[0].events = POLLIN | POLLPRI;
+  ufd[1].fd = rb->virtfd;
+  ufd[1].events = POLLIN | POLLPRI;
+  //printf("Start read on dvr t:%lu\n", pthread_self());
+  //wrPtr must always be an integer number of packets!
+  numbytes -= numbytes % TSPacketSIZE;
+  while( bytes < numbytes && (size = rb_max_free(rb)) > 0) {
+    dprintf1("Starting loop %d of %d\n", bytes, numbytes);
+    //poll dvr fd and dvblb fd
+    ufd[0].revents = 0;
+    ufd[1].revents = 0;
+    if(poll(&ufd[0], 2, -1) < 0 || ufd[1].revents) {
+      //We should abort
+      dprintf0("Aborting block read!\n");
+      return (errno ? errno : EFAULT);
+    }
+    //opportunistically read as many bytes as possible
+    ret = read(rb->fd, rb->wrPtr, size);
+    if (ret <= 0) {
+      break;
+    }
+#ifdef WRITE_RAW_DVR
+    write(rawdvr, rb->wrPtr, ret);
+#endif
+    bytes += ret;
+    newptr = rb->wrPtr + ret;
+    if (newptr > rb->end)
+      memcpy(rb->buffer, rb->end, newptr - rb->end);
+    if (newptr >= rb->end)
+      newptr = rb->buffer + (newptr - rb->end);
+
+    pthread_mutex_lock(&rb->rw_lock);
+    rb->wrPtr = newptr;
+    pthread_mutex_unlock(&rb->rw_lock);
+  }
+  if(bytes && rb_sendmsg)
+    msg_replace(MSG_HIGH_PRIORITY, MSG_RINGBUF, rb->num, rb);
+  return 0;
+}
+
+static void rb_get_bytes(struct ringbuffer *rb, unsigned char *ptr,
+                         int numbytes)
+{
+  unsigned char *newptr;
+  newptr = rb->rdPtr;
+  if (newptr + numbytes > rb->end) {
+    memcpy(ptr, newptr, rb->end - newptr);
+    numbytes -= rb->end - newptr;
+    ptr += rb->end - newptr;
+    newptr = rb->buffer;
+  }
+  memcpy(ptr, newptr, numbytes);
+  pthread_mutex_lock(&rb->rw_lock);
+  rb->rdPtr = newptr + numbytes;
+  pthread_mutex_unlock(&rb->rw_lock);
+}
+
+static struct ringbuffer *find_rb_from_pc(struct parser_cmds *pc) {
+  struct list_head *ptr;
+  int num =  pc->common->virt_adapt; 
+  list_for_each(ptr, &ringbuflist) {
+    struct ringbuffer *rb = list_entry(ptr, struct ringbuffer);
+    if (rb->num == num)
+      return rb;
+  }
+  return NULL;
+}
+
+static void rb_release(struct ringbuffer *rb)
+{
+  dprintf1("Releasing ringbuffer: %d\n", rb->num);
+  if(rb->state != RB_CLOSING) {
+    dprintf0("Ringbuffer state should have been RB_CLOSING but was '%d;'\n",
+              rb->state);
+  }
+  pthread_mutex_lock(&list_lock);
+  rb->state = RB_CLOSED;
+  pthread_mutex_unlock(&list_lock);
+#ifdef WRITE_RAW_DVR
+  close(rawdvr);
+#endif
+}
+
+static void open_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  char realdev[256];
+
+  struct ringbuffer *rb = find_rb_from_pc(pc);
+  if(! rb)
+    return;
+
+  *result = CMD_SKIPCALL;
+  if(rb->state == RB_OPEN) {
+    dprintf0("Cannot open dvr; it is already open.\n");
+    fdptr->fd = -EMFILE;
+    return;
+  }
+  while(rb->state == RB_CLOSING) {
+    //This isn't ideal, but the situation shouldn't last too long
+    sched_yield();
+  }
+  sprintf(realdev, "/dev/dvb/adapter%d/%s0", pc->common->real_adapt,
+                                             dnames[pc->type]);
+  //we need to open nonblocking so we never get deadlocked
+  fdptr->fd = open(realdev, fdptr->flags | O_NONBLOCK);
+  if(fdptr->fd < 0) {
+    fdptr->fd = -errno;
+    perror("Failed to open dvr");
+    return;
+  }
+
+  rb->virtfd = pc->virtfd;
+  rb->fd = fdptr->fd;
+  rb->flags = fdptr->flags;
+  rb->rdPtr = rb->wrPtr = rb->buffer;
+  pthread_mutex_lock(&list_lock);
+  rb->state = RB_OPEN;
+  rb->readok = 0;
+  pthread_mutex_unlock(&list_lock);
+  dprintf1("Creating ringbuffer: %d\n", rb->num);
+#ifdef WRITE_RAW_DVR
+  rawdvr = open("/tmp/rawdvr.mpg", O_WRONLY | O_CREAT | O_TRUNC, 0666);
+#endif
+}
+
+static void close_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                       cmdret_t *result, int *ret, 
+                       unsigned long int cmd, unsigned char *data)
+{
+  struct ringbuffer *rb = find_rb_from_pc(pc);
+  if(rb == NULL || rb->state != RB_OPEN)
+    return;
+  pthread_mutex_lock(&list_lock);
+  rb->state = RB_CLOSING;
+  pthread_mutex_unlock(&list_lock);
+  //NOTE: need to use high priority queue to prevent a read/close race
+  //MSG_RINGCLOSE MUST release the ringbuffer!
+  if(rb_sendmsg)
+    msg_replace(MSG_HIGH_PRIORITY, MSG_RINGCLOSE, rb->num, rb);
+  else
+    rb_release(rb);
+}
+
+static void poll_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  int avail;
+  struct ringbuffer *rb = find_rb_from_pc(pc);
+
+  if(! rb || rb->state != RB_OPEN)
+  {
+    dprintf0("Shouldn't be here!\n");
+    rb->readok = (*ret ? 1 : 0);
+    return;
+  }
+  if(*ret) {
+    //oportunistically fill the buffer, doesn't matter if we're blocking or not
+    rb_fill_bytes(rb, rb_max_free(rb));
+  }
+  avail = rb_avail(rb, 0);
+  if(avail > 0) {
+    *ret = 1;
+    ci->u.mode |= POLLIN;
+    rb->readok = 1;
+  } else {
+    *ret = 0;
+    ci->u.mode &= ~(POLLIN | POLLPRI);
+    rb->readok = 0;
+  }
+}
+
+static void read_call(struct parser_cmds *pc, struct poll_ll *fdptr,
+                      cmdret_t *result, int *ret, 
+                      unsigned long int cmd, unsigned char *data)
+{
+  struct dvblb_custommsg *ci = (struct dvblb_custommsg *)data;
+  unsigned int trybytes, small = TSPacketSIZE * 1;
+  int avail, err;
+  struct ringbuffer *rb = find_rb_from_pc(pc);
+
+  if(! rb || rb->state != RB_OPEN)
+    return;
+
+  *result = CMD_SKIPCALL;
+//  ci->u.count -= ci->u.count % TSPacketSIZE;
+  //trybytes must be multiple of TSPacketSIZE and > 0
+  trybytes = ci->u.count % TSPacketSIZE;
+  if(trybytes)
+    trybytes =  ci->u.count + TSPacketSIZE - trybytes;
+  else
+    trybytes =  ci->u.count;
+  avail = rb_avail(rb, 0);
+  if (rb->flags & O_NONBLOCK) {
+    if (avail > 0) {
+       if ((unsigned int)avail > ci->u.count)
+         avail = ci->u.count ;
+       else
+         ci->u.count = avail;
+       rb_get_bytes(rb, pc->mmap, avail);
+       *ret = ci->u.count;
+    } else {
+#ifdef CHECK_READ_OK
+      assert(rb->readok != 1);
+#endif
+      *ret = -EAGAIN;
+    }
+    rb->readok = 0;
+    rb_fill_bytes(rb, rb_max_free(rb));
+    return;
+  }
+  //Blocking read
+  if(avail > 0 && (unsigned int)avail >= ci->u.count) {
+    // enough bytes can be read from ring buffer
+    rb_get_bytes(rb, pc->mmap, ci->u.count);
+    if((unsigned int)avail > ci->u.count * 16)
+      trybytes = ci->u.count >> 2;
+    else if((unsigned int)avail > ci->u.count * 4)
+      trybytes = ci->u.count >> 1;
+    if (trybytes >= TSPacketSIZE) {
+      //NOTE: because trybytes < ci->u.count, and we just read
+      // ci->u.count bytes from the ringbuffer, we KNOW that
+      // there is room to read at least trybytes into the buffer
+      trybytes -= trybytes % TSPacketSIZE;
+      if((err = rb_fill_bytes_block(rb, trybytes)) != 0) {
+        *ret = -err;
+        return;
+      }
+    }
+    *ret = ci->u.count;
+    return;
+  }
+  // not enough bytes can be read from ring buffer
+  while(1) {
+    if(rb_free(rb, trybytes)) {
+      //the ring buffer has room  to get 'trybytes' bytes
+      printf("Buffer has room, reading %d bytes\n", trybytes);
+      if((err = rb_fill_bytes_block(rb, trybytes)) != 0) {
+        *ret = -err;
+        return;
+      }
+      if(trybytes < ci->u.count)
+        sched_yield(); //give up the rest of our timeslice
+    } else {
+      // signal decryptor
+      // wait for decryptor
+      dprintf0("Buffer is full, waiting for decode on %d bytes\n", trybytes);
+      rb_avail(rb, ci->u.count);
+    }
+    if (rb_avail(rb, 0) >= (int)ci->u.count) {
+      // enough bytes can be read from ring buffer
+      rb_get_bytes(rb, pc->mmap, ci->u.count);
+      *ret = ci->u.count;
+      printf("Returning %d\n", *ret);
+      return;
+    }
+    trybytes = small;
+  }
+}
+
+static void enable_msg(struct parser_cmds *pc, int enable)
+{
+  rb_sendmsg = enable;
+}
+
+void ringbuf_register_callback(int (* cb)(struct ringbuffer *, int)) {
+  rb_check_read_callback = cb;
+}
+
+static void connect_rb(struct parser_adpt *pc_all) {
+  struct ringbuffer *rb;
+
+  unsigned long rbsize = (1 + pc_all->dvr->common->buffersize / TSPacketSIZE)
+                         * TSPacketSIZE;
+  unsigned long rbextra = rbsize / 20;
+  rb  = (struct ringbuffer *)malloc(sizeof(struct ringbuffer)+rbsize+rbextra);
+  memset(rb, 0, sizeof(struct ringbuffer));
+  pthread_mutex_init(&rb->rw_lock, NULL);
+  rb->release = rb_release;
+  rb->num = pc_all->dvr->common->virt_adapt;
+  rb->buffer = (unsigned char *)&rb->buffer + sizeof(rb->buffer);
+  rb->end = rb->buffer + rbsize;
+  rb->state = RB_CLOSED;
+  rb->reserved = rbextra;
+  list_add_tail(&rb->list, &ringbuflist);
+
+  ATTACH_CALLBACK(&pc_all->dvr->pre_open, open_call,   -1);
+  ATTACH_CALLBACK(&pc_all->dvr->pre_close, close_call, -1);
+  ATTACH_CALLBACK(&pc_all->dvr->post_poll, poll_call,  -1);
+  ATTACH_CALLBACK(&pc_all->dvr->pre_read,  read_call,  -1);
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_RINGBUF,
+                             "ringbuffer",
+                             NULL, connect_rb, NULL, NULL, enable_msg, NULL, NULL};
+int __attribute__((constructor)) __ringbuf_init(void)
+{
+#ifndef NO_RINGBUF
+  list_add(&plugin_cmds.list, &plugin_cmdlist);
+#endif
+  return 0;
+}
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.h b/contrib/sasc-ng/dvbloopback/src/plugin_ringbuf.h
new file mode 100644 (file)
index 0000000..07c472a
--- /dev/null
@@ -0,0 +1,36 @@
+#include <pthread.h>
+#include "process_req.h"
+#include "list.h"
+#include "messages.h"
+
+#define PLUGIN_RINGBUF 12
+
+#define TSPacketSIZE 188
+//#define RB_RESERVED 100000
+//#define RB_BUFSIZE ((2000000 / TSPacketSIZE) * TSPacketSIZE)
+//#define RB_BUFSIZE 1024 * TSPacketSIZE
+enum {
+  RB_CLOSED = 0,
+  RB_CLOSING,
+  RB_OPEN
+};
+
+struct ringbuffer {
+  struct list_head list;
+  int virtfd;
+  int fd;
+  unsigned int flags;
+  unsigned char num;
+  unsigned char *rdPtr;
+  unsigned char *wrPtr;
+  unsigned char *end;
+  pthread_mutex_t rw_lock;
+  void (*release)(struct ringbuffer *);
+  int readok;
+  int state;
+  unsigned long reserved;
+  //buffer MUST be last do to our allocation method
+  unsigned char *buffer;
+};
+
+void ringbuf_register_callback(int (* cb)(struct ringbuffer *, int));
diff --git a/contrib/sasc-ng/dvbloopback/src/plugin_showioctl.c b/contrib/sasc-ng/dvbloopback/src/plugin_showioctl.c
new file mode 100644 (file)
index 0000000..e2c0df0
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include "list.h"
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+
+#include "plugin_getsid.h"
+#include "msg_passing.h"
+
+#define PLUGIN_ID 10
+#define DBG_NAME "IOCTL"
+#include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
+
+//Get rid of the usefless ioctl typechecking
+#ifdef _IOC_TYPECHECK
+  #undef _IOC_TYPECHECK
+  #define _IOC_TYPECHECK(t) (sizeof(t))
+#endif
+static void fe_ioctl(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+    __u32 d32 = (*(unsigned long *)data) & 0xffffffff;
+    __u16 d16 = d32 & 0xffff;
+    char str[256], str1[80];;
+    if(! ((_dbglvl >> DVBDBG_IOCTL) & 1))
+      return;
+    switch(cmd) {
+      case FE_GET_INFO:
+        {
+          struct dvb_frontend_info *d = (struct dvb_frontend_info *)data;
+          tmprintf("","FE_GET_INFO(%d): '%s'\n"
+                 "min:%u max:%u step:%u tol:%u minsr:%u maxsr:%u tolsr:%u\n",
+                 fdptr->fd, d->name, d->frequency_min, d->frequency_max,
+                 d->frequency_stepsize, d->frequency_tolerance,
+                 d->symbol_rate_min, d->symbol_rate_max,
+                 d->symbol_rate_tolerance);
+        }
+        break;
+      case FE_SET_TONE:
+        tmprintf("","FE_SET_TONE(%d): %s\n", fdptr->fd, (d32 ==SEC_TONE_ON) ?
+               "ON" : "OFF");
+        break;
+      case FE_SET_VOLTAGE:
+        tmprintf("","FE_SET_VOLTAGE(%d): %s\n", fdptr->fd, (d32 ==SEC_VOLTAGE_13) ?
+               "13" : (d32 == SEC_VOLTAGE_18 ? "18" : "OFF"));
+        break;
+      case FE_ENABLE_HIGH_LNB_VOLTAGE:
+        tmprintf("","FE_ENABLE_HIGH_LNB_VOLTAGE:(%d): %d\n", fdptr->fd, d32);
+        break;
+      case FE_READ_STATUS:
+        str[0] = 0;
+        if(d32 & FE_HAS_SIGNAL)
+          strcat(str," SIGNAL");
+        if(d32 & FE_HAS_CARRIER)
+          strcat(str," CARRIER");
+        if(d32 & FE_HAS_VITERBI)
+          strcat(str," VITERBI");
+        if(d32 & FE_HAS_SYNC)
+          strcat(str," SYNC");
+        if(d32 & FE_HAS_LOCK)
+          strcat(str," LOCK");
+        if(d32 & FE_TIMEDOUT)
+          strcat(str," TIMEDOUT");
+        if(d32 & FE_REINIT)
+          strcat(str," REINIT");
+        tmprintf("","FE_READ_STATUS(%d):%s\n", fdptr->fd, str);
+        break;
+      case FE_READ_BER:
+        tmprintf("","FE_READ_BER(%d): %u\n", fdptr->fd, d32);
+        break;
+      case FE_READ_SIGNAL_STRENGTH:
+        tmprintf("","FE_READ_SIGNAL_STRENGTH(%d): %u\n", fdptr->fd, d16);
+        break;
+      case FE_READ_SNR:
+        tmprintf("","FE_READ_SNR(%d): %u\n", fdptr->fd, d16);
+        break;
+      case FE_READ_UNCORRECTED_BLOCKS:
+        tmprintf("","FE_READ_UNCORRECTED_BLOCKS(%d): %u\n", fdptr->fd, d32);
+        break;
+#ifdef FE_SET_FRONTEND2
+      case FE_SET_FRONTEND2:
+        {
+          struct dvb_frontend_parameters_new *fe =
+                 (struct dvb_frontend_parameters_new *)data;
+          tmprintf("","FE_SET_FRONTEND2(%d): freq: %d inv: %s\n"
+                      "  QPSK: sr: %d fec: %d\n"
+                      "  DVBS2: sr: %d fec: %d mod: %d rol: %d\n", fdptr->fd,
+                fe->frequency, (fe->inversion == INVERSION_OFF ? "OFF" :
+                (fe->inversion == INVERSION_ON ? "ON" : "AUTO")),
+                fe->u.qpsk.symbol_rate, fe->u.qpsk.fec_inner,
+                fe->u.qpsk2.symbol_rate, fe->u.qpsk2.fec_inner,
+                fe->u.qpsk2.modulation, fe->u.qpsk2.rolloff_factor);
+        }
+        break;
+      case FE_GET_FRONTEND2:
+        tmprintf("","FE_GET_FRONTEND2(%d)\n", fdptr->fd);
+        break;
+      case FE_SET_STANDARD:
+        {
+          char t[10];
+          switch(d32) {
+            case FE_QPSK:   strcpy(t, "QPSK"); break;
+            case FE_QAM:    strcpy(t, "QAM"); break;
+            case FE_OFDM:   strcpy(t, "OFDM"); break;
+            case FE_ATSC:   strcpy(t, "ATSC"); break;
+            case FE_DVB_S:  strcpy(t, "DVB_S"); break;
+            case FE_DVB_C:  strcpy(t, "DVB_C"); break;
+            case FE_DVB_T:  strcpy(t, "DVB_T"); break;
+            case FE_DVB_S2: strcpy(t, "DVB_S2"); break;
+            default: strcpy(t, "Unkown"); break;
+          }
+          tmprintf("","FE_SET_STANDARD(%d): %s\n", fdptr->fd, t);
+        }
+        break;
+      case FE_GET_EXTENDED_INFO:
+        tmprintf("","FE_GET_EXTENDED_INFO(%d)\n", fdptr->fd);
+        break;
+#endif
+      case FE_SET_FRONTEND:
+        {
+          struct dvb_frontend_parameters *fe =
+                 (struct dvb_frontend_parameters *)data;
+          tmprintf("","FE_SET_FRONTEND(%d): freq: %d inv: %s\n"
+                      "  QPSK: sr: %d fec: %d\n", fdptr->fd,
+                fe->frequency, (fe->inversion == INVERSION_OFF ? "OFF" :
+                (fe->inversion == INVERSION_ON ? "ON" : "AUTO")),
+                fe->u.qpsk.symbol_rate, fe->u.qpsk.fec_inner);
+        }
+        break;
+      case FE_GET_FRONTEND:
+        tmprintf("","FE_GET_FRONTEND(%d)\n", fdptr->fd);
+        break;
+      case FE_GET_EVENT:
+        {
+          struct dvb_frontend_event *event =
+                 (struct dvb_frontend_event *)data;
+          str[0] = 0;
+          if(*ret >= 0) {
+            d32 = event->status;
+            if(d32 & FE_HAS_SIGNAL)
+              strcat(str," SIGNAL");
+            if(d32 & FE_HAS_CARRIER)
+              strcat(str," CARRIER");
+            if(d32 & FE_HAS_VITERBI)
+              strcat(str," VITERBI");
+            if(d32 & FE_HAS_SYNC)
+              strcat(str," SYNC");
+            if(d32 & FE_HAS_LOCK)
+              strcat(str," LOCK");
+            if(d32 & FE_TIMEDOUT)
+              strcat(str," TIMEDOUT");
+            if(d32 & FE_REINIT)
+              strcat(str," REINIT");
+            strcat(str,"\n");
+          }
+          tmprintf("","FE_GET_EVENT(%d) returned %d\n%s", fdptr->fd, *ret, str);
+        }
+        break;
+      case FE_DISEQC_SEND_MASTER_CMD:
+        {
+          struct dvb_diseqc_master_cmd *msg =
+                                (struct dvb_diseqc_master_cmd *)data;
+          str[0] = 0;
+          for(int i = 0; i < msg->msg_len; i++) {
+            sprintf(str1," %02x", msg->msg[i]);
+            strcat(str, str1);
+          }
+          tmprintf("","FE_DISEQC_SEND_MASTER_CMD(%d):%s\n", fdptr->fd, str);
+        }
+        break; 
+#ifdef FE_DISHNETWORK_SEND_LEGACY_CMD
+      case FE_DISHNETWORK_SEND_LEGACY_CMD:
+        {
+          int s, p, c = d32 & 0x7f;
+          switch(c) {
+            case 0x34: s=21, p = 0; break;
+            case 0x65: s=21, p = 1; break;
+            case 0x46: s=42, p = 1; break;
+            case 0x17: s=42, p = 1; break;
+            case 0x39: s=64, p = 10; break;
+            case 0x1a: s=64, p = 11; break;
+            case 0x4b: s=64, p = 20; break;
+            case 0x5c: s=64, p = 21; break;
+            case 0x0d: s=64, p = 30; break;
+            case 0x2e: s=64, p = 31; break;
+            default: s=0; p=0; break;
+          }
+          tmprintf("","FE_DISHNETWORK_SEND_LEGACY_CMD(%d): SW%d p:%d\n", fdptr->fd,
+                 s, p);
+        }
+        break;
+#endif
+      default:
+        break;
+    }
+}
+static void demux_ioctl(struct parser_cmds *pc, struct poll_ll *fdptr,
+                    cmdret_t *result, int *ret,
+                    unsigned long int cmd, unsigned char *data)
+{
+    char str[256], str1[80];
+    __u32 d32 = (*(unsigned long *)data) & 0xffffffff;
+    if(! ((_dbglvl >> DVBDBG_IOCTL) & 2))
+      return;
+    switch(cmd) {
+      case DMX_START:
+        tmprintf("","DMX_START(%d)\n", fdptr->fd);
+        break;
+      case DMX_STOP:
+        tmprintf("","DMX_STOP(%d)\n", fdptr->fd);
+        break;
+      case DMX_SET_FILTER:
+        {
+          struct dmx_sct_filter_params *dmx =
+                 (struct dmx_sct_filter_params *)data;
+          str[0] = 0;
+          if(dmx->flags)
+            sprintf(str,"    flags: %s%s%s%s\n",
+                   (dmx->flags & DMX_CHECK_CRC ? " CHECK_CRC" : ""),
+                   (dmx->flags & DMX_ONESHOT ? " ONESHOT" : ""),
+                   (dmx->flags & DMX_IMMEDIATE_START ? " IMMEDIATE_START" : ""),
+                   (dmx->flags & DMX_KERNEL_CLIENT ? " KERNEL_CLIENT" : ""));
+          for(int i = 0; i < DMX_FILTER_SIZE; i++) {
+            if(dmx->filter.filter[i] == 0)
+              break;
+            sprintf(str1,"    filter: %02x mask: %02x mode: %02x\n",
+               dmx->filter.filter[i], dmx->filter.mask[i],
+               dmx->filter.mode[i]);
+            strcat(str, str1);
+          }
+          tmprintf("","DMX_SET_FILTER(%d): pid:%d timeout:%d\n%s", fdptr->fd, 
+                 dmx->pid, dmx->timeout, str);
+        }
+        break;
+      case DMX_SET_PES_FILTER:
+        {
+          struct dmx_pes_filter_params *dmx =
+                 (struct dmx_pes_filter_params *)data;
+          char t[10];
+          switch(dmx->pes_type) {
+            case DMX_PES_AUDIO0: strcpy(t, "AUDIO"); break;
+            case DMX_PES_VIDEO0: strcpy(t, "VIDEO"); break;
+            case DMX_PES_TELETEXT0: strcpy(t, "TELETEXT"); break;
+            case DMX_PES_SUBTITLE0: strcpy(t, "SUBTITLE"); break;
+            case DMX_PES_PCR0: strcpy(t, "PCR"); break;
+
+            case DMX_PES_AUDIO1: strcpy(t, "AUDIO1"); break;
+            case DMX_PES_VIDEO1: strcpy(t, "VIDEO1"); break;
+            case DMX_PES_TELETEXT1: strcpy(t, "TELETEXT1"); break;
+            case DMX_PES_SUBTITLE1: strcpy(t, "SUBTITLE1"); break;
+            case DMX_PES_PCR1: strcpy(t, "PCR1"); break;
+
+            case DMX_PES_AUDIO2: strcpy(t, "AUDIO2"); break;
+            case DMX_PES_VIDEO2: strcpy(t, "VIDEO2"); break;
+            case DMX_PES_TELETEXT2: strcpy(t, "TELETEXT2"); break;
+            case DMX_PES_SUBTITLE2: strcpy(t, "SUBTITLE2"); break;
+            case DMX_PES_PCR2: strcpy(t, "PCR2"); break;
+
+            case DMX_PES_AUDIO3: strcpy(t, "AUDIO3"); break;
+            case DMX_PES_VIDEO3: strcpy(t, "VIDEO3"); break;
+            case DMX_PES_TELETEXT3: strcpy(t, "TELETEXT3"); break;
+            case DMX_PES_SUBTITLE3: strcpy(t, "SUBTITLE3"); break;
+            case DMX_PES_PCR3: strcpy(t, "PCR3"); break;
+
+            case DMX_PES_OTHER: strcpy(t, "OTHER"); break;
+            default: strcpy(t, "Unknown"); break;
+          }
+          if(dmx->flags)
+            sprintf(str,"    flags: %s%s%s%s\n",
+                   (dmx->flags & DMX_CHECK_CRC ? " CHECK_CRC" : ""),
+                   (dmx->flags & DMX_ONESHOT ? " ONESHOT" : ""),
+                   (dmx->flags & DMX_IMMEDIATE_START ? " IMMEDIATE_START" : ""),
+                   (dmx->flags & DMX_KERNEL_CLIENT ? " KERNEL_CLIENT" : ""));
+          tmprintf("","DMX_SET_PES_FILTER(%d): pid:%d in:%s out:%s type:%s\n%s",
+                 fdptr->fd, dmx->pid,
+                 dmx->input == DMX_IN_FRONTEND ? "FRONTEND" : "DVR",
+                 dmx->output == DMX_OUT_DECODER ? "DECODER" :
+                     (dmx->output == DMX_OUT_TAP ? "TAP" : "TS_TAP"), t, str);
+        }
+        break;
+      case DMX_SET_BUFFER_SIZE:
+        tmprintf("","DMX_SET_BUFFER_SIZE(%d): %d\n", fdptr->fd, d32);
+        break;
+      case DMX_GET_PES_PIDS:
+        {
+          __u16 *pid = (__u16 *)data;
+          str[0] = 0;
+          for(int i = 0; i < 5; i++) {
+            if(pid[i] <= 0)
+              break;
+            tmprintf(str1," %d", pid[i]);
+            strcat(str, str1);
+          }
+          tmprintf("","DMX_GET_PES_PIDS(%d):%s\n", fdptr->fd, str);
+        }
+        break;
+      case DMX_GET_CAPS:
+        {
+          struct dmx_caps *dmx = (struct dmx_caps*)data;
+          tmprintf("","DMX_GET_CAPS(%d): %u num:%d\n", fdptr->fd, dmx->caps,
+                 dmx->num_decoders);
+        }
+        break;
+      case DMX_SET_SOURCE:
+        tmprintf("","DMX_SET_SOURCE(%d): %d\n", fdptr->fd, d32);
+        break;
+      case DMX_GET_STC:
+        tmprintf("","DMX_GET_STC(%d)\n", fdptr->fd);
+        break;
+    }
+}
+
+static void connect_imsg(struct parser_adpt *pc_all)
+{
+  ATTACH_CALLBACK(&pc_all->frontend->post_ioctl, fe_ioctl,    -1);
+  ATTACH_CALLBACK(&pc_all->demux->post_ioctl,    demux_ioctl, -1);
+}
+
+//list, plugin_id, name, parse_args, connect, launch, message, send_msg
+static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID,
+                       "ioctl debug",
+                       NULL, connect_imsg, NULL, NULL, NULL, NULL, NULL};
+int __attribute__((constructor)) __showioctl_init(void)
+{
+  list_add(&plugin_cmds.list, &plugin_cmdlist);
+  return 0;
+}
diff --git a/contrib/sasc-ng/dvbloopback/src/process_req.c b/contrib/sasc-ng/dvbloopback/src/process_req.c
new file mode 100644 (file)
index 0000000..d1cd167
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+   DVBLoopback
+   Copyright Alan Nisota 2006
+
+   This file is part of DVBLoopback.
+
+    DVBLoopback 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.
+
+    DVBLoopback 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 DVBLoopback; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sched.h>
+#include <netinet/in.h>
+
+#include "process_req.h"
+
+#define DBG_NAME dnames[pc->type]
+#define PLUGIN_ID (2*(pc->type-3))
+
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+
+#define MEMSIZE 100000
+
+int find_free(struct parser_cmds *pc)
+{
+  int i;
+  for(i=0; i < DVBLB_MAXFD; i++)
+    if(pc->realfd_ll[i].kernfd == 0)
+      return i;
+  return -1;
+}
+
+struct poll_ll *find_pos(struct parser_cmds *pc, void *filp)
+{
+  struct list_head *ptr;
+  struct poll_ll *entry;
+  list_for_each(ptr, &pc->used_list) {
+    entry = list_entry(ptr, struct poll_ll);
+    if (entry->kernfd == filp)
+      return entry;
+  }
+  return NULL;
+}
+
+// The poll_handler routine is used to trap a SIGPOLL
+// which is sent to restart the poll-loop if something changes
+void poll_handler(int num) {}
+void *poll_loop(void * parm)
+{
+  struct parser_cmds *pc = (struct parser_cmds *)parm;
+  int count;
+  struct pollfd ufd[DVBLB_MAXFD+1];
+
+  struct list_head *ptr;
+  struct poll_ll *entry;
+
+  struct dvblb_pollmsg msg;
+
+  pthread_mutex_lock(&pc->poll_mutex);
+  while(1) {
+    int ret;
+    pthread_cond_wait(&pc->poll_cond, &pc->poll_mutex);
+    do {
+      ret = 1;
+      count = 0;
+      list_for_each(ptr, &pc->used_list) {
+        entry = list_entry(ptr, struct poll_ll);
+        if(entry->poll) {
+          ufd[count].fd = entry->fd;
+          ufd[count].events = POLLIN;
+          ufd[count].revents = 0;
+          entry->pfd = &ufd[count];
+          count++;
+        }
+      }
+      if (count > 0) {
+        pthread_mutex_unlock(&pc->poll_mutex);
+        ret = poll(ufd, count, -1);
+        pthread_mutex_lock(&pc->poll_mutex);
+      }
+    } while(ret <= 0);
+    msg.count = 0;
+    list_for_each(ptr, &pc->used_list) {
+      entry = list_entry(ptr, struct poll_ll);
+      if(entry->pfd && entry->kernfd && entry->poll && entry->pfd->revents) {
+        msg.file[msg.count++] = entry->kernfd;
+        entry->pfd = NULL;
+        entry->poll = 0;
+      }
+    }
+    if (msg.count) {
+      ioctl(pc->virtfd, DVBLB_CMD_ASYNC, &msg);
+    }
+  }
+}
+
+static cmdret_t do_cmd(struct list_head *list, struct parser_cmds *pc,
+                         struct poll_ll *fdptr, int *ret,
+                         unsigned long int cmd, unsigned char *data)
+{
+  cmdret_t result = CMD_NOCHANGE;
+  struct list_head *ptr;
+  struct cmd_list *entry;
+  list_for_each(ptr, list) {
+    entry = list_entry(ptr, struct cmd_list);
+    if(entry->cmd)
+      entry->cmd(pc, fdptr, &result, ret, cmd, data);
+    if(result & CMD_STOP)
+      break;
+  }
+  return result;
+}
+
+void get_thread_priority(char *str)
+{
+  struct sched_param param;
+  int priority;
+  int policy;
+  int ret;
+  sleep(1);
+  /* scheduling parameters of target thread */
+  ret = pthread_getschedparam (pthread_self(), &policy, &param);
+  /* sched_priority contains the priority of the thread */
+  priority = param.sched_priority;
+  sprintf(str, "The thread scheduling parameters indicate:\n"
+    "policy = %d\npriority = %d\n", policy, param.sched_priority);
+}
+
+//We can't allow blocking reads, since they could continue to stall even after
+//The requesting app is terminated.  So we must emulate a blocking read by
+//alwaysw opening with 'O_NONBLOCK', and doing a poll/read pair to fill the
+//buffer
+static int read_block(struct parser_cmds *pc, int fd, unsigned char *buf,
+                      int numbytes) {
+  int bytes = 0, ret;
+  struct pollfd ufd[2];
+  ufd[0].fd = fd;
+  ufd[0].events = POLLIN | POLLPRI;
+  ufd[1].fd = pc->virtfd;
+  ufd[1].events = POLLIN | POLLPRI;
+  //printf("Start read on dvr t:%lu\n", pthread_self());
+  //wrPtr must always be an integer number of packets!
+  while( bytes < numbytes) {
+    dprintf1("Starting loop %d of %d\n", bytes, numbytes);
+    //poll dvr fd and dvblb fd
+    ufd[0].revents = 0;
+    ufd[1].revents = 0;
+    if(poll(&ufd[0], 2, -1) < 0 || ufd[1].revents) {
+      //We should abort
+      dprintf0("Aborting block read!\n");
+      if(! errno)
+        errno = EFAULT;
+      return -1;
+    }
+    ret = read(fd, buf+bytes, numbytes-bytes);
+    if (ret <= 0) {
+      break;
+    }
+    bytes += ret;
+  }
+  if(bytes)
+    return bytes;
+  else
+    return ret;
+}
+
+void *forward_data(void *parm)
+{
+  struct parser_cmds *pc = (struct parser_cmds *)parm;
+  char realdev[256];
+  char virtdev[256];
+  char str[256];
+  char *buf;
+  void *file;
+  int pos;
+
+  struct poll_ll *fdptr;
+
+  int size, ret;
+  cmdret_t  result;
+  unsigned char ioctldata[1024], *start, *ioctldatastart;
+  unsigned long int cmd;
+  struct pollfd poll_fds;
+  size_t memsize = MEMSIZE;
+
+  int offset = sizeof(unsigned long int) + sizeof (void *) - sizeof(int);
+  if (offset < 0) {
+    fprintf(stderr, "int is larger than long_int!  that can't be right.\n");
+    exit(-1);
+  }
+
+  if(pc->type == DVB_DEVICE_DVR) {
+    memsize = pc->common->buffersize;
+  } else if(pc->type == DVB_DEVICE_DEMUX) {
+    memsize =  pc->common->buffersize >> 3;
+  }
+
+  start = ioctldata + offset;
+  ioctldatastart = ioctldata + offset + sizeof(int);
+
+  sprintf(realdev, "/dev/dvb/adapter%d/%s0", pc->common->real_adapt,
+                                             dnames[pc->type]);
+  sprintf(virtdev, "/dev/dvb/adapter%d/%s1", pc->common->virt_adapt,
+                                             dnames[pc->type]);
+
+  pc->virtfd = open(virtdev, O_RDWR);
+  if(pc->virtfd < 0) {
+    dprintf0("Could not open %s. Error was: %d\n", virtdev, errno);
+    perror("Open failed\n");
+    exit(-1);
+  }
+  poll_fds.fd = pc->virtfd;
+
+  pc->mmap=(unsigned char *)mmap(0, memsize, PROT_READ|PROT_WRITE,
+                                 MAP_SHARED, pc->virtfd, 0);
+  buf = (char *)malloc(memsize);
+
+  if (! pc->mmap || ! buf) {
+    fprintf(stderr, "Failed to execute mmap!\n");
+    exit(-1);
+  }
+  pthread_mutex_init(&pc->poll_mutex, NULL);
+  pthread_cond_init(&pc->poll_cond, NULL);
+  INIT_LIST_HEAD(&pc->used_list);
+  bzero(&pc->realfd_ll, sizeof(pc->realfd_ll));
+  pthread_create(&pc->pollthread, &default_attr, poll_loop, pc);
+
+  if(pc->type == DVB_DEVICE_DVR) {
+    struct sched_param param;
+    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
+    pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
+  }
+  get_thread_priority(str);
+  dprintf0("Starting thread on %s\n%s", virtdev, str);
+
+  //inform master thread that we're done
+  pthread_mutex_lock(&pc->common->cond_lock);
+  pc->common->cond_count--;
+  pthread_cond_signal(&pc->common->cond);
+  pthread_mutex_unlock(&pc->common->cond_lock);
+
+  //main loop
+  while(1) {
+    int poll_signal = 0;
+    poll_fds.events = POLLIN;
+    poll_fds.revents = 0;
+    do {
+      ret = poll(&poll_fds, 1, -1);
+      if(ret < 0)
+        perror("Poll failed");
+    } while (!(poll_fds.revents & POLLIN));
+    size=read(pc->virtfd, ioctldata, 1024);
+    if(size < (int)(sizeof(long int)+ sizeof(void *))) {
+      dprintf0("Didn't read enough data\n");
+      exit(-1);
+    }
+    cmd = *((unsigned long int *)ioctldata);
+    file = *(void **)(ioctldata + sizeof(cmd));
+    if (cmd < DVBLB_MAX_CMDS) {
+      struct dvblb_custommsg *ci;
+      struct pollfd ufd;
+      ci = (struct dvblb_custommsg *)ioctldatastart;
+      dprintf3("Got custom command: %d\n", ci->type);
+      switch(ci->type) {
+        case DVBLB_OPEN:
+          pos = find_free(pc);
+          if (pos < 0) {
+            dprintf0("Too Many files open!\n");
+            exit(-1);
+            ret = -1;
+            break;
+          }
+          fdptr = &pc->realfd_ll[pos];
+          fdptr->flags = ci->u.mode;
+          result = do_cmd(&pc->pre_open, pc, fdptr, &ret, cmd, ioctldatastart);
+          if(!(result & CMD_SKIPCALL))
+             fdptr->fd = open(realdev, fdptr->flags | O_NONBLOCK);
+          if(!(result & CMD_SKIPPOST))
+             do_cmd(&pc->post_open, pc, fdptr, &ret, cmd, ioctldatastart);
+          if (fdptr->fd < 0) {
+            ret = fdptr->fd;
+            dprintf1("Open failed.  fd: %d\n", fdptr->fd);
+            break;
+          }
+          fdptr->kernfd = file;
+          fdptr->pfd = NULL;
+          fdptr->poll = 0;
+
+          pthread_mutex_lock(&pc->poll_mutex);
+          list_add(&fdptr->list, &pc->used_list);
+          pthread_mutex_unlock(&pc->poll_mutex);
+
+          ret = 0;
+          dprintf1("Opened fd: %d=%d mode: %o\n", pos, fdptr->fd, fdptr->flags);
+          break;
+        case DVBLB_CLOSE:
+          fdptr = find_pos(pc, file);
+          if (fdptr == NULL) {
+            ret = -1;
+            break;
+          }
+          //need to kill any polls before closing thread
+          pthread_mutex_lock(&pc->poll_mutex);
+          list_del(&fdptr->list);
+          pthread_mutex_unlock(&pc->poll_mutex);
+          pthread_kill(pc->pollthread, SIGPOLL);
+
+          result = do_cmd(&pc->pre_close, pc, fdptr, &ret, cmd, ioctldatastart);
+          if(!(result & CMD_SKIPCALL)) {
+            ret = close(fdptr->fd);
+            if (ret < 0) {
+              perror("Close failed:");
+            }
+          }
+          if(!(result & CMD_SKIPPOST))
+            do_cmd(&pc->post_close, pc, fdptr, &ret, cmd, ioctldatastart);
+          fdptr->kernfd = 0;
+          ret = 0;
+          dprintf1("Closed fd: %d ret: 0\n", fdptr->fd);
+          break;
+        case DVBLB_READ:
+          fdptr = find_pos(pc, file);
+          if (fdptr == NULL) {
+            ret = -1;
+            break;
+          }
+          if(ci->u.count > memsize) {
+            dprintf2("Too many bytes: %zu > %zu\n", ci->u.count, memsize);
+            ci->u.count = memsize;
+          }
+          {
+            int orig_cnt = ci->u.count;
+          result = do_cmd(&pc->pre_read, pc, fdptr, &ret, cmd, ioctldatastart);
+          if(!(result & CMD_SKIPCALL)) {
+            if(fdptr->flags & O_NONBLOCK)
+              ret = read(fdptr->fd, pc->mmap, ci->u.count);
+            else
+              ret = read_block(pc, fdptr->fd, pc->mmap, ci->u.count);
+            if (ret < 0)
+              ret = -errno;
+          }
+          if(!(result & CMD_SKIPPOST))
+            do_cmd(&pc->post_read, pc, fdptr, &ret, cmd, ioctldatastart);
+          dprintf3("Read %d bytes (requested %zu or %d)\n",
+                   ret,  ci->u.count, orig_cnt);
+          }
+          ci->u.count = ret;
+          break;
+        case DVBLB_WRITE:
+          ret = -1;
+          break;
+        case DVBLB_POLL:
+          fdptr = find_pos(pc, file);
+          if (fdptr == NULL) {
+            ret = -1;
+            break;
+          }
+          result = do_cmd(&pc->pre_poll, pc, fdptr, &ret, cmd, ioctldatastart);
+          if(!(result & CMD_SKIPCALL)) {
+            ufd.fd = fdptr->fd;
+            ufd.events = POLLIN | POLLPRI;
+            ret = poll(&ufd, 1, 0);
+            if(ret > 0 && !(ufd.revents & POLLIN))
+              ret = 0;
+            //Only start the poll thread if there isn't data on the real dev
+            //if (ret == 0)
+            //  poll_signal = 1;
+            ci->u.mode = ufd.revents;
+          }
+          if(!(result & CMD_SKIPPOST))
+            do_cmd(&pc->post_poll, pc, fdptr, &ret, cmd, ioctldatastart);
+          if (ret == 0) {
+            poll_signal = 1;
+            fdptr->poll = 1;
+          }
+          dprintf3("Poll mode:%o ret:%d - sig: %d\n",
+                   ci->u.mode, ret, poll_signal);
+          break;
+      }
+    } else {
+      dprintf2("Got command %lu (size: %d)\n", cmd & 0xff, size);
+      fdptr = find_pos(pc, file);
+      if (fdptr == NULL) {
+        ret = -1;
+      } else {
+        result = do_cmd(&pc->pre_ioctl, pc, fdptr, &ret, cmd, ioctldatastart);
+        if(!(result & CMD_SKIPCALL)) {
+          if(_IOC_SIZE(cmd)) {
+            ret = ioctl(fdptr->fd, cmd, ioctldatastart);
+          } else {
+            ret = ioctl(fdptr->fd, cmd, *(int *)ioctldatastart);
+          }
+          if(ret < 0)
+            ret = -errno;
+        }
+        dprintf2("ioctl on (%d) returned: %d\n", fdptr->fd, ret);
+        if(!(result & CMD_SKIPPOST))
+          do_cmd(&pc->post_ioctl, pc, fdptr, &ret, cmd, ioctldatastart);
+      }
+    }
+    *(int *)start = ret;
+    pthread_mutex_lock(&pc->poll_mutex);
+    // Need to protect ioctl call in addition to the cond
+    ioctl(pc->virtfd, cmd, start);
+    if(poll_signal) {
+      pthread_kill(pc->pollthread, SIGPOLL); // restart poll if already polling
+      pthread_cond_signal(&pc->poll_cond); //otherwise start polling
+    }
+    pthread_mutex_unlock(&pc->poll_mutex);
+  }
+}
+
+static struct parser_cmds * init_device(struct common_data *common, int type)
+{
+  struct parser_cmds *pc;
+  pc = (struct parser_cmds *)malloc(sizeof(struct parser_cmds));
+  if(! pc)
+    return NULL;
+  bzero(pc, sizeof(struct parser_cmds));
+  common->cond_count++; //this doesn't need to be protected (no threads yet)
+  pc->common = common;
+  pc->type = type;
+
+  INIT_LIST_HEAD(&pc->pre_open);
+  INIT_LIST_HEAD(&pc->post_open);
+
+  INIT_LIST_HEAD(&pc->pre_close);
+  INIT_LIST_HEAD(&pc->post_close);
+
+  INIT_LIST_HEAD(&pc->pre_read);
+  INIT_LIST_HEAD(&pc->post_read);
+
+  INIT_LIST_HEAD(&pc->pre_poll);
+  INIT_LIST_HEAD(&pc->post_poll);
+
+  INIT_LIST_HEAD(&pc->pre_ioctl);
+  INIT_LIST_HEAD(&pc->post_ioctl);
+  return pc;
+}
+
+void launch_processors(struct parser_adpt *pc_all)
+{
+//pthread_attr_t attr;
+  signal(SIGPOLL, poll_handler);
+//pthread_attr_init(&attr);
+//pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
+//pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+//pthread_attr_setschedpolicy( &attr, SCHED_FIFO );
+  if(pc_all->frontend) {
+    pthread_create(&pc_all->frontend->thread, &default_attr, forward_data,
+                   pc_all->frontend);
+  }
+  if(pc_all->demux) {
+    pthread_create(&pc_all->demux->thread, &default_attr, forward_data,
+                   pc_all->demux);
+  }
+  if(pc_all->dvr) {
+    pthread_create(&pc_all->dvr->thread, &default_attr, forward_data,
+                   pc_all->dvr);
+  }
+  if(pc_all->ca) {
+    pthread_create(&pc_all->ca->thread, &default_attr, forward_data,
+                   pc_all->ca);
+  }
+}
+
+static void kill_parser(struct parser_cmds *pc)
+{
+  if(pc->pollthread) {
+    pthread_kill(pc->pollthread, SIGTERM);
+    pthread_join(pc->pollthread, NULL);
+  }
+  if(pc->thread) {
+    pthread_kill(pc->thread, SIGTERM);
+    pthread_join(pc->thread, NULL);
+  }
+}
+void shutdown_parser(struct parser_adpt *pc_all)
+{
+  if(pc_all->ca)
+    kill_parser(pc_all->ca);
+  if(pc_all->dvr)
+    kill_parser(pc_all->dvr);
+  if(pc_all->demux)
+    kill_parser(pc_all->demux);
+  if(pc_all->frontend)
+    kill_parser(pc_all->frontend);
+}
+
+void init_parser(struct parser_adpt *pc_all, struct common_data *common)
+{
+  char realdev[255], virtdev[255];
+  struct stat st;
+
+  int types[] = {DVB_DEVICE_FRONTEND, DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, -1};
+  struct parser_cmds **ptr;
+  int i;
+  
+  bzero(pc_all, sizeof(struct parser_adpt));
+  for (i = 0; types[i] != -1; i++) {
+    sprintf(realdev, "/dev/dvb/adapter%d/%s0", common->real_adapt,
+                                               dnames[types[i]]);
+    sprintf(virtdev, "/dev/dvb/adapter%d/%s0", common->virt_adapt,
+                                               dnames[types[i]]);
+    if (stat(realdev, &st) != 0 || stat(virtdev, &st) != 0) {
+      continue;
+    }
+    ptr = (&pc_all->frontend) + i;// * sizeof(struct parser_cmds *);
+    *ptr = init_device(common, types[i]);
+  }
+}
+
diff --git a/contrib/sasc-ng/dvbloopback/src/process_req.h b/contrib/sasc-ng/dvbloopback/src/process_req.h
new file mode 100644 (file)
index 0000000..1bf2e25
--- /dev/null
@@ -0,0 +1,123 @@
+#ifndef _PROCESS_REQ_H_
+#define _PROCESS_REQ_H_
+#include <getopt.h>
+#include "list.h"
+#include "msg_passing.h"
+#include "dvbloopback.h"
+#include "debug.h"
+
+struct poll_ll {
+       struct list_head list;  //this must remain first!!!
+       void * kernfd;
+       struct pollfd *pfd;
+       int fd;
+       unsigned int flags;
+       int poll;
+};
+
+struct common_data {
+  int virt_adapt;
+  int real_adapt;
+  unsigned long buffersize;
+  void * private_data;
+  pthread_mutex_t cond_lock;
+  pthread_cond_t cond;
+  int cond_count;
+  
+};
+
+
+struct parser_cmds {
+  int type;
+  pthread_t thread;
+  pthread_t pollthread;
+  struct common_data *common;
+  struct list_head pre_open;
+  struct list_head post_open;
+  struct list_head pre_close;
+  struct list_head post_close;
+  struct list_head pre_read;
+  struct list_head post_read;
+  struct list_head pre_poll;
+  struct list_head post_poll;
+  struct list_head pre_ioctl;
+  struct list_head post_ioctl;
+  unsigned char *mmap;
+  void * private_data;
+
+  /* everything from here down is private to the processor */
+  struct poll_ll realfd_ll[DVBLB_MAXFD];
+  struct list_head used_list;
+  int virtfd;
+  pthread_mutex_t poll_mutex;
+  pthread_cond_t poll_cond;
+};
+
+struct parser_adpt {
+  /* NOTE keep these in the same order as dnames! */
+  struct parser_cmds *frontend;
+  struct parser_cmds *demux;
+  struct parser_cmds *dvr;
+  struct parser_cmds *ca;
+};
+
+typedef enum {
+  CMD_NOCHANGE = 0x00, //Default state
+  CMD_STOP =     0x01, //Don't do any further processing in the current loop
+  CMD_SKIPCALL = 0x02, //Don't execute the requested command
+  CMD_SKIPPOST = 0x04, //Don't execute the post-call loop
+  CMD_STOPALL  = 0x07,
+} cmdret_t;
+
+struct cmd_list {
+  struct list_head list;
+  void (*cmd) (struct parser_cmds *, struct poll_ll *, cmdret_t *, int *,
+               unsigned long int, unsigned char *);
+  unsigned int id;
+};
+/*
+inline static struct cmd_list *register_cmd(void (*cmd) (struct parser_cmds *, struct poll_ll *, cmdret_t *, int *, unsigned long int, unsigned char *)) {
+  struct cmd_list *reg = (struct cmd_list *)malloc(sizeof(struct cmd_list));
+  reg->cmd = cmd;
+  return reg;
+}
+*/
+static inline void _attach_callback(struct list_head *cb,
+        void (*cmd) (struct parser_cmds *, struct poll_ll *, cmdret_t *, int *, unsigned long int, unsigned char *),
+        int priority, int _id) {
+  struct cmd_list *reg = (struct cmd_list *)malloc(sizeof(struct cmd_list));
+  reg->cmd = cmd;
+  reg->id = _id;
+  list_add_priority(&reg->list, cb, priority);
+}
+#define ATTACH_CALLBACK(_cb, _cmd, _pri) _attach_callback(_cb, _cmd, _pri, PLUGIN_ID)
+
+//#define REGISTER_CMD(var, c) static struct cmd_list var = { {NULL, NULL}, c}
+
+typedef enum {
+  ARG_INIT,
+  ARG_HELP,
+  ARG_PARSE,
+} arg_enum_t;
+
+struct plugin_cmd {
+  struct list_head list;
+  int plugin;
+  const char *name;
+  struct option * (*parse_args)(arg_enum_t);
+  void (*connect)(struct parser_adpt *);
+  void (*launch)();
+  void (*message)(struct msg *, unsigned int priority);
+  void (*send_msg)(struct parser_cmds *pc, int msg);
+  void (*shutdown)();
+  void (*user_msg)(char *msg);
+};
+
+extern void launch_processors(struct parser_adpt *pc_all);
+extern void shutdown_parser(struct parser_adpt *pc_all);
+extern void init_parser(struct parser_adpt *pc_all, struct common_data *common);
+
+extern struct list_head plugin_cmdlist;
+extern pthread_attr_t default_attr;
+#endif
+
diff --git a/contrib/sasc-ng/objs/.empty b/contrib/sasc-ng/objs/.empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/COPYING b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/COPYING
new file mode 100644 (file)
index 0000000..5b6e7c6
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+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.
+
+  <signature of Ty Coon>, 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/contrib/sasc-ng/sc/PLUGINS/src/sc-src/HISTORY b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/HISTORY
new file mode 100644 (file)
index 0000000..36f83fb
--- /dev/null
@@ -0,0 +1,959 @@
+VDR Plugin 'sc' Revision History
+--------------------------------
+
+09.02.2008: Version 0.9.0
+- NOTE: the versioning scheme has been changed. All releases (regardless if even
+  or odd minor) are considered stable. Unstable development is in HG only.
+- Updated keys are now written back to the SoftCAM.Key file. If the keyfile is
+  modified externaly it will be re-read. Introduce new cache files ecm.cache &
+  tps.cache. ca.cache is obsolete now.
+- All config files are expected to be located in the "sc" subdir of the plugin
+  config directory now. Removed commandline option -c.
+- A bunch of changes to the Nagra2 code, including 0501 maprom, 0101 map 29, 97W
+  hack, fixed HW_SECURITY and map timings.
+- Fixed Viaccess TPS AU.
+- Added dedicated housekeeping thread.
+- Display (important) user messages on the OSD (if enabled in setup).
+- Various updates to testing code.
+- Fixed Makefile default target.
+- Fixed gcc 4.x warnings.
+- Added patch for DVB multiproto driver.
+- Updated finnish and french translations.
+
+------------------------------------------------------------
+
+18.01.2008: Version 0.8.7
+- Several changes to the Nagra2 code including HW reg mapper, map cycle counts,
+  0501 AU, 0101 auxed map57, tableless map3e and new build system.
+- Increased smartcard info buffer size.
+- Fixed memcpy in Seca ECM/EMM processing.
+- Fixed ExtAU to trigger on wrong key. Don't trigger on EMM keys.
+- Fixed RotateBytes to be robust against bad input.
+- Updated Makefile for HG.
+- Updated italian, russian, finnish translations.
+- The 0.8.x branch is now frozen, only bugfixes will be applied.
+
+21.12.2007: Version 0.8.6
+- Added Nagra2 0501 dyn. AU execution fix.
+- Added Nagra2 4101 map58 AU (though already outdated). Using unified ECC code
+  for map57 & map58.
+- Added instruction cycle counter to Nagra ST7/19 cpu emulation.
+- Added support for recent Nagra2 0101/0901 ECM (including map4e/22/3e cycle
+  calculation, timer/CRC hardware simulation, DES map, EEPROM updates, dynamic
+  ECM code, auxserver V2 call).
+- Added Nagra2 config option to drop EMM-S packets.
+- Added Nagra1 UK reg05 fix (i.e. moved it from N2 to N1).
+- Added TPS AU fix.
+- Added SVDR commands to inject keys and to configure logging to file.
+- Removed some gcc'isims (case ranges, named variadic macros, variable length
+  arrays).
+- Updated finnish translations.
+
+10.11.2007: Version 0.8.5
+- Added new Nagra2 0501 AU sheme.
+- Added Nagra2 NA 'temporarily' fixes.
+- For simplicity the high provider id is used always now, when loading an EEP
+  file in Nagra2 emu. e.g. for provider 0901 you have to rename the file to
+  EEP09_102.bin.
+- Added an option to start AutoUpdate even if no audio/video pids are set (e.g.
+  EPG scan). Has to be enabled from the plugin setup menu.
+- Fixed cardclient camd35 EMM request length check.
+- Fixed smartcard Viaccess ATR check and transcription errors in smartcard NDS
+  camcrypt. Thanks to dr.aus.
+- Fixed saving ScCaps to config if all cards are disabled.
+- Fixed sasc-ng compile (FFdecsa dir).
+- Obmit error messages about (optional) config files (e.g. *.KID).
+- Added some glue for gcc 2.95 compile.
+- Added Hungarian translations.
+
+30.09.2007: Version 0.8.4
+- Added Nagra2 4101 Bx support.
+- Added smartcard NDS Videoguard2 code (untested). Needs NDS seed and boxid in
+  smartcard.conf (later only for newer card revisions). This is based on code
+  from sasc-ng changeset r155.
+- Fixed Nagra cpu emu SetPc and ReadHandler call.
+- Fixed crash on TPS ECM when no TPS encryption is used and no TPS AU keys are
+  available (null pointer dereference).
+- Updated russian and finnish translations.
+
+15.09.2007: Version 0.8.3
+- Added Nagra2 3101 MECM (same as 0501).
+- Added Nagra2 0901 new 0x40 MECM and rev248 morph (from sasc-ng).
+- Changed TPS AU instruction limit for st20 emulation. Fixed callback pointer
+  search and decryption in case super encryption is disabled temporarily.
+- Added a setup option to limit the size of the logfile.
+- Fixed Makefile for Debian compile.
+- Some fixes for sasc-ng compile/operation.
+- Now autogenerating i18n.c file with po2i18n.pl from po files on VDR < 1.5.7.
+  Note that after changes to the *.c files the po files have to be recreated, so
+  you should have gettext package installed.
+- Fixed plugin .mo filename for VDR 1.5.7.
+- Updated finnish translations.
+
+02.09.2007: Version 0.8.2
+- Reworked Nagra2 EMM nano handling, key updates and map core (the latter is
+  partly based on Emunation 2.0.0 code).
+- Added Nagra2 0101 register 0x05,0x16 fix (not well tested).
+- Added Nagra2 0511,1101 MECM (same as 0501).
+- Added compatibility option for sasc-ng.
+- Added commandline option -c to select a config subdirectory.
+- Fixed FFdecsa to prevent CW change while key in use. Based on racepatch from
+  DVBN.
+- Fixed cardclient camd35 packet ID compare (short vs. int).
+- Fixed Nagra2 skipping expiry date in ECM.
+- Added support for VDR 1.5.7+ gettext sheme (most po-files need to be revised
+  by translators).
+
+06.07.2007: Version 0.8.1
+- Added Nagra2 7101 3DES support.
+- Added a setup option to force transfermode with digital audio. Another common
+  used patched for the vdr-core is made unnecessary by this option.
+- Fixed camd33 EMM packet processing.
+- Fixed recording device allocation (1.4.x). You have to update the VDR core
+  patch. Thanks to dingo35.
+- Fixed compiling with VDR 1.5.0.
+
+22.06.2007: Version 0.8.0
+- Added Nagra2 0501 MECM support.
+- Added support for Nagra smartcards. Code is not well tested. Volunteers
+  welcome. Timing is tricky, so you have to get cardreader clock AND -C
+  parameter right.
+- Added debug description to CI adapter ringbuffers.
+- Added a note to README that the plugin have to be put first on VDR
+  commandline. Loading certain plugins (e.g. softdevice) in front of SC leads to
+  mismatched device numbering in VDR which causes some strange effects.
+- Added sanity check for device numbering.
+- Fixed Nagra2 nano processing.
+- Fixed Seca EMM signature check.
+- Never ending story: fixed cardclient reconnecting on read timeout again.
+- Fixed cardclient camd35 sending Nagra provider in ECM request.
+- Added russian translations (core only).
+
+------------------------------------------------------------
+
+13.05.2007: Version 0.7.5
+- Fixed memcpy race in OpenTV decompress.
+- Fixed endless loop with evil EMM data in Nagra2 0101 B1 code.
+- Fixed access to disabled DVB cards (1.4.x).
+- Fixed budget card problem in VDR core patch (1.4.x).
+
+06.05.2007: Version 0.7.4
+- Added compatibility for VDR 1.4.6 (VDR core has to be patched). Note that this
+  is experimental code.
+- Added support for new TPS AU sheme.
+- Added some Map handling to DN/BEV B1 processing code.
+- Added version information to hello message in cardclient radegast.
+- Added logic to Makefile to copy max. number of CAIDs from VDR.
+- Extended ConstCW key to handle cable/terrestrial sources.
+- Fixed race in camslot reset code.
+- Fixed zero-cw-index handling for CI update messages.
+- Fixed handling of same SID on different transponders in lru-caid-cache.
+- Fixed menu processing on ECM cache flush abort.
+
+06.04.2007: Version 0.7.3
+- This is a beta release. As the code seems pretty stable, we would like to
+  encourage everybody to try this release. Nevertheless it should be used under
+  controlled conditions only.
+- Further improved camslot reset behaviour. Toggling concurrent flag takes
+  effect at runtime now too.
+- Removed obsolete D+ AU code.
+- Fixed unexpect side effect of cardclient reconnecting on read timeout.
+- Fixed CAID allocation (CheckIgnore).
+
+31.03.2007: Version 0.7.2
+- Now creating the devices nodes after loading setup.conf. Should solve problems
+  with SourceCaps patch and remove the "nextCardIndex to big" error. But this
+  requires some ugly hacking which may not work with every gcc/vdr version.
+- Added DarkAvengers FFdecsa optimizations for mmx, sse and sse2 modes.
+- Added SVDR command to display all message classes.
+- Added new D+ AU. You need an additional NN 52 RSA key (and optional NN 53
+  verify key).
+- Added autodetection of pid where TPS broadcasts AU data. You still have to
+  switch to transponder 10873 for AU.
+- Changed the plugin shutdown sequence to fix the hang-on-exit problem. It seems
+  to be fixed now, although the reason for the problem is still unknown.
+- Fixed Nagra2 0101 map 4d input.
+- Fixed logging Nagra cpu emu messages to general.unknown.
+- Fixed reseting module options to default.
+- Fixed a mismatched Lock/Unlock in smartcard code (in error path).
+- Fixed off-by-one error in CI adapter read.
+- Fixed camslot reset logic.
+- Fixed some 'valgrind' problems and properly unload all sub-libraries on plugin
+  exit too.
+- Updated README documentation, removed README.0.7.x.
+- Updated finnish translations.
+
+09.03.2007: Version 0.7.1
+- Reduced the number of used camslots to 1 per device. Multiply camslots cause
+  all kinds of problems. Reworked CAID allocation algorithm. This should make
+  operation much more reliable.
+- Improved operation with hardware CAM.
+- Introducing a completely new message logging system. All messages classes can
+  be enabled/disabled at runtime (via setup menu and SVDR) and can be written to
+  console, file and/or syslog (configurable from setup menu).
+- Added Nagra2 provider 0101 Map57 call.
+- Added TPS AU code. Note that you have to add 8 TPS master keys to your
+  keyfile. Update seems to work only on transponder 10873, e.g. switch to
+  Equidia.
+- Fixed FF concurrent streams if sc.ConcurrentFF is set to a value >1.
+- Fixed CI adapter TPDU length decoding.
+- Fixed processing stale ECM packets after a channel switch.
+- Fixed long standing bug in Nagra1 RSA key update (which was a problem in a
+  key base class, introduced in 0.5.10).
+- Fixed paged long indirect and paged long indirect indexed adressing modes in
+  Nagra cpu emu (HILOS macro).
+- Fixed cardclient reconnecting on read timeout.
+- Fixed clobbering the name of the sc shared library in the shared objects
+  table.
+- Fixed compiling if openssl lacks IDEA support.
+
+17.02.2007: Version 0.7.0
+- Forked development branch.
+- This is an alpha release. Using it in a production enviroment is not
+  recommended. You are strongly advised to read the file README.0.7.x. 
+- Requires VDR version 1.5.0 or newer and openssl package 0.9.7 or newer.
+- Now operates without any patches to the VDR core.
+- Integrated FFdecsa functionality.
+- Using a openssl package without IDEA & AES support is deprecated. Included
+  support code will be removed in the future.
+- Added commandline option to force budget mode on a DVB device.
+- Added setup menu item to flush ECM cache (from ca.cache).
+- Added support for Nagra2 3DES encrypted key updates. Tweaked EMM caid and RSA
+  key selection for D+ AU.
+- Added fix for Nagra2 BEV B1 updates.
+- Added pre-crypted camkey challenges for Irdeto ACS 384. This is a last resort
+  convenience mode and usage is strongly deprecated! Don't relay on this!
+- Added NewCS client identification in cardclient newcamd.
+- Fixed include path order.
+- Added swedish translations.
+
+------------------------------------------------------------
+
+06.04.2007: Version 0.6.2
+- Backported from 0.7.2:
+    * Mismatched Lock/Unlock in smartcard code.
+    * Autodetection of pid with TPS AU data.
+    * Finnish translations.
+- Backported from 0.7.1:
+    * Nagra2 provider 0101 Map57 call.
+    * Nagra1 RSA key update.
+    * Paged adressing modes in Nagra cpu emu.
+    * TPS AU.
+    * Cardclient reconnecting on read timeout.
+    * Processing stale ECM packets after a channel switch.
+    * Clobbering shared library name in shared objects table.
+    * Compiling if openssl lacks IDEA support.
+
+17.02.2007: Version 0.6.1
+- Backported from 0.7.0:
+    * Nagra2 3DES encrypted key updates (D+ AU).
+    * Nagra2 BEV B1 updates.
+    * Pre-crypted camkey challenges for Irdeto ACS 384.
+    * NewCS client identification in cardclient newcamd.
+    * Include path order.
+    * Swedish translations.
+
+13.01.2007: Version 0.6.0
+- Stable release. Minimum supported VDR version is 1.4.0. Note that the stable
+  branch doesn't support and probably will not support VDR 1.5.x series. This is
+  left to the upcomming unstable branch.
+- Added support for new TPS algo. You need a current tps.bin (mostly changing
+  daily) file in plugins/viaccess/.
+- Fixed CW swap for Nagra2 providers 0501/1101/1102.
+- Fixed BEV inadvertently using Nagra1 ECM decoding only.
+- Fixed Nagra1 ROM10 updates (broken by ROM3 changes in 0.5.12).
+- Fixed compiling issue with missing "asm/unaligned.h".
+- Added Makefile option for static build (sasc-ng).
+
+------------------------------------------------------------
+
+15.12.2006: Version 0.5.12
+- Major restructure of Nagra code. Added basic ST19 features to Nagra cpu
+  emulation (Nano B1 processing). Added provider 0101/0901 map 3b. Fixed long
+  standing bug in ROM3 key update.
+- Updated Viaccess AU to new parsing code. Fixed signature check in shared
+  updates (introduced in 0.5.11).
+- Changed the order in which ECM pids/systems are tried to a more consistent way
+  regarding the system priority.
+- Fixed cardclient newcamd using wrong SA for Seca provider.
+- Fixed ignoring CAIDs when used together with a hardware CAM.
+- Fixed writing garbage to setup.conf if CAID ignore list is empty.
+- Fixed AutoUpdate switch. Turning it off had no effect. ExternalAU honors the
+  AutoUpdate switch now too.
+- Added russian translations.
+
+04.10.2006: Version 0.5.11
+- Added Nagra2 MECM handling, support for AUX server (version 0.9.3 or newer
+  only) and DN EMM hacks.
+- Added smartcard Cryptoworks camcrypt. You must have a valid IPK or UCPK in
+  your smartcard.conf to make it actually work. If you have a PIN for your card
+  in smartcard.conf, parental rating can be disabled even if the PIN cannot be
+  read from the card. See example smartcard.conf for format.
+- Added additional checks to Viaccess AU code to prevent segfault on bad input
+  data (e.g. short EMM).
+- Added V4 server capability check in cardclient radegast. EMM processing would
+  be possible, if the server would send UA/SA to the client.
+- Added cardclient gbox. GBOX must be running on the local machine and you have
+  to make sure that there is no /var/tmp/pmt.tmp file. Based on morfsta's
+  version but rewritten nearly from scratch.
+- Added a configurable list of CAIDs which are ignore by the plugin. See plugin
+  setup menu.
+- Fixed triggering external AU too often.
+- Fixed stupid Viacess key length error.
+- Fixed parsing CA descriptors for all SIDs if a handler has attached several.
+  Thanks to Aroureos for testing.
+
+21.07.2006: Version 0.5.10
+- Added external key updates via shell script. See README for details.
+- Now checking for duplicate serial ports in smartcard config.
+- Fixed supersede for Viaccess TPS keys.
+- Fixed re-adding DEFAULT_PORT on config-file reload.
+- Fixed design bug in key comparison.
+
+05.06.2006: Version 0.5.9
+- Smartcard code now supports setting the cardreader clock (see commandline
+  switch -C/--clock), custom baudrates (if your UART supports that), PTS
+  specific mode and PTS 1-stopbit mode. Note: due to the new clock parameter,
+  the format of DEFAULT_PORT has changed!
+- Added a filter for unused chid's in Irdeto ECM stream.
+- Fixed cardclient camd33 checking for unsuccessfull ECM answer (causing 5s
+  delay). Thanks to Aroureos for all the testing.
+- Fixed cardclient camd35 parsing provider information in EMM request.
+- Fixed a side effect in main cam loop.
+- Various fixes to Makefile (unsupported cp options, sublibrary naming scheme,
+  honour DVBDIR in Makefile.system, libcrypto linkage).
+- Fixed libvdr-sc overloading a global VDR symbol (translations).
+- Fixed compiling with VDR < 1.3.47.
+- Corrected information about min. required VDR version (currently 1.3.31).
+
+26.05.2006: Version 0.5.8
+- Completely new build system. Now using shared libraries for individual
+  encryption systems, which are loaded at runtime. Copy all wanted libsc-* to
+  your VDR plugin lib directory. Encryption systems might be provided in binary
+  form only. See README for details.
+- Added Cryptoworks emulation. See example keyfile for key format.
+- Added Nagra2 EMM nano E0 (DN) handling.
+- Don't try failed ECMs too often (by caching them).
+- Fixed Nagra2 PW inverse CW.
+- Fixed handling of channels which share the same PIDs.
+- Fixed ConstCW key handling (broken in 0.5.7).
+- Fixed Cardclient ECM caching.
+- Fixed Newcamd EMM Viaccess provider ID matching.
+- Fixed Viaccess EMM assembling.
+- Fixed dvb-cwidx patch for undefined LINUX_VERSION_CODE. There seems to be
+  compatibility problems with certain kernel/driver version (e.g. kernel driver
+  vs HG): gcc complains mutex_lock() is called with incompatible pointer type.
+  Therefore the old dvb-cwidx patch has been re-added as dvb-cwidx-old in case
+  someone has problems with the new version.
+- Fixed odd compiler error for a struct member with the same name as the struct
+  itself (probably some ancient gcc version).
+- Added frensh translations.
+
+06.05.2006: Version 0.5.7
+- Disable keyfile stuff if no system uses keys, prevent loader error messages
+  about unknown sections, prevent ca.cache trashing, fixed key saving. 
+- If smartcard PTS request fails, reset card again and continue without PTS.
+- Now reading entitlements from Conax smartcard.
+- Added a workaround for providers which seem to broadcast fake ECM.
+- Added warning about channels which share the same PID.
+- Fixed Nagra2 crash with EMM-S keys.
+- Fixed compile problem when disabling certain modules.
+- Updated dvb-cwidx patch for kernel >= 2.6.16.
+- Changed Makefile to take APIVERSION into account.
+- Added polish translations.
+
+30.03.2006: Version 0.5.6
+- Added Nagra2 ECM DES decryption code.
+- Nagra signature check is now skipped, if your keyfile lacks verify keys. Don't
+  put fake verify keys in there!
+- Now reading entitlements from Cryptoworks smartcard.
+- Smartcard engine now supports ISO7816 PTS protocol i.e. baudrate changes.
+- Improved OSD display of smartcard information/entitlements.
+- Fixed exit condition in Nagra2 ECM/EMM nano parse loop.
+- Prevent frequent relaunch of the logger thread.
+- Fixed CW-index allocation for idle ECM handler.
+- Fixed missing initialisation in ECM handler.
+- Fixed ECM handler selection in special cases where a recording starts and
+  used-FF-streams == allowed-FF-streams but one of the stream is used for live
+  viewing. Involves changes to vdr-sc patch too.
+- Fixed concurrent check in vdr-sc patch. A bad comparison broke the check if
+  the first CAID in channel entry was 0x100.
+- Fixed cardclient radegast to handle NULL-cw.
+- Added dutch translations and updated finnish translations.
+- NOTE: you have to upgrade the VDR core with the supplied vdr-sc patch. Older
+  versions will refuse to work.
+
+16.02.2006: Version 0.5.5
+- Complete restructure of Nagra code (e.g. separating Nagra1 & Nagra2). Added
+  signature check, verify keys are mandatory now.
+  As a consequence Nagra2 key format has changed (as announced). See example
+  keyfile. A quick upgrade guide:
+  N xxxx 10   ->   N xxxx 01
+  N xxxx 02   ->   N xxxx NN 02
+  N xxxx N2   ->   N xxxx NN 12
+  verify keys ->   N xxxx V
+              ->   N xxxx NN 03
+- Restructured AutoUpdate code. EMM data is now passed to all systems which can
+  handle it, concurrent recordings doesn't cause multiple processing of the same
+  date and it's possible to log on all available CAIDs concurrently (though this
+  may cause high CPU load). See plugin setup menu. Option LoggerActive has been
+  renamed to AutoUpdate. Option LoggerTimeout has been removed. Check your
+  config.
+- Added some statistics about EMM packet load.
+- Now using non-RSA (i.e. plain) camkey challenge for Irdeto smartcards if the
+  word PLAIN is given in smartcard.conf instead of a certificate. See example
+  smartcard.conf. For ACS 0383/0384 cards the challenge type is autodetected. If
+  anybody knows a generic way to detect the challenge type, please let us know.
+- Fixed several crypto classes to be reentrant (fixing possible race in
+  multi-threaded ECM/EMM handling).
+- Fixed access to (possibly) deleted filter class in section filter handling. 
+- Fixed complaining about erroneous entries in smartcard.conf if compiled
+  without SC_IRDETO.
+- Fixed endless loop in SimpleList handling.
+- Fixed vdr-1.3.38-sc patch (one hunk was lost).
+
+08.01.2006: Version 0.5.4
+- Added a plugin SVDR interface. The only command is RELOAD for now (triggers a
+  reload of the configuration files).
+- Added support for multiple TPS keys (key is selected based on Viaccess hash).
+- Added premiliary support for Nagra2 AU. See example keyfile for format of
+  needed keys. Key format is subject to change in next releases.
+- Complete rewrite of the Seca2 provider specific code.
+- Added generic RSA & IDEA crypto classes.
+- Added better checking of CW decryption status in smartcard Cryptoworks.
+- General code review (eliminated duplicate code, replaced VDR base classes by
+  own shorter ones, beautified code).
+- Now generating smartcard Irdeto info string for OSD card menu.
+- If the DVB driver doesn't support the CA_SET_PID ioctl call (i.e. unpatched
+  driver), auto adjust the FF concurrent limit to 1.
+- Fixed checking card status in smartcard Irdeto camkey exchange.
+- Fixed detaching a PID from a handler if VDR has removed this PID from the
+  channels.conf entry meanwhile.
+- Fixed ECM table handling in ca.cache.
+- Fixed Nagra2 CW order for D+.
+- Fixed a gcc4.1 compiling issue.
+- Added additional debug output in cardclient camd35 to snoop for AU problems.
+  You may comment DEBUG_EXTRA to disable verbose log.
+- Updated FFdecsa patch to 0.1.3 (fixing a race condition).
+- Note: this release was forced by the release of VDR 1.3.38 and not all of the
+  changes above are tested as well as they should be.
+
+09.12.2005: Version 0.5.3
+- Added Nagra2 EMM support and EMM caching to cardclient newcamd.
+- Added Cryptoworks EMM support and EMM caching to cardclient camd35.
+- You may now add multiple CAID fields to cardclient.conf. A valid line would be
+  e.g. camd35:192.168.0.1:20248:1/1702/FF00,0604,0d0c/FF00:hero:itsme
+- Added support for systems which broadcast ECM on tables other than 0x80/0x81.
+- Added support for Nagra2 BEV & dual IDEA opkeys (00/10).
+- Now reading CAID from Cryptoworks smartcard.
+- Added sanity check to nano sorting to prevent memory trashing.
+- Added new CAID nano to cardclient Radegast ECM request.
+- Fixed Cryptoworks shared EMM parsing. Added additional checks to reduced EMM
+  load too.
+- Fixed smartcard Irdeto camkey challenge for Premiere S01 and other non-Z
+  cards. As these cards don't support the RSA challenge, you don't need a
+  certificate for them.
+- Fixed matching Irdeto certificates which are given with ACS version only.
+- Fixed misleading error messages in smartcard.conf parser.
+- Fixed ECM-EMM caid mismatch and segfault due to leftover debug statements in
+  cardclient camd35.
+- Fixed (i.e. removed) global cardclient EMM cache as it corrupts EMM merging
+  for systems which broadcast shared updates on different tables.
+- Fixed cardclient newcamd segfault on bad config data.
+
+12.11.2005: Version 0.5.2
+- Introducing a new config file for smartcard specific data e.g. RSA certificate
+  or box keys. The file is called smartcard.conf. See the example file.
+- Added support for full camkey challenge in smartcard Irdeto. You need a RSA
+  certificate matching your card (either Irdeto default or card specific one) in
+  smartcard.conf or your card won't work!
+- Added an abstract layer to parse and assemble EMM messages. This touches
+  several systems e.g. Viaccess, Cryptoworks & NDS. A bug here may break them
+  all.
+- Added Cryptoworks & Viaccess EMM handling to newcamd cardclient (via new
+  assemble code).
+- Added some more MAP math calls to Nagra emulation to fix Rom10 AU.
+- Several fixes to NDS EMM parsing/assembling.
+- Fixed ConstCW key lookup (was broken due to Nagra1/2 key handling changes).
+- Fixed cardclient camd3 to include SID in ECM request.
+- Fixed cardclient newcamd to handle NULL-cw which are send by newcs.
+- Removed logger option 'non-recording on' from the config. In 0.5.x it is
+  without function anyways.
+- Added a message to hint the user about unusual values in ScCaps. Changed the
+  default values to something more reasonable too.
+- Updated FFdecsa patch to 0.1.2 (fixing a segfault).
+
+13.10.2005: Version 0.5.1
+- Added smartcard Cryptoworks EMM handling. There is a setup option to disable
+  the parental rating on the card too (experimental). Thanks to scotty for the
+  sample code.
+- Added smartcard Viaccess EMM handling (experimental).
+- Added FFdecsa-0.1.1 patch.
+- Don't count a live stream on a FF card when deciding about allowed
+  concurrency. A live stream doesn't use bandwidth on the bus.
+- Apply min. ECM processing time to Nagra2 too.
+- Fixed segfault for Seca1.
+- Fixed Irdeto key updates. Somehow/sometime the code was broken.
+- Fixed cardclient newcamd NDS EMM (still experimental).
+- Now considering the key size when superseding keys.
+- Forgot to mention that you need additional table files for Seca 6a (s2_sse.bin
+  5120; s2_sse_006a.bin 336; s2_cw_006a.bin 512 (Numbers are the filesize)).
+- Updated finnish translations.
+
+15.09.2005: Version 0.5.0
+- Forked development branch.
+- Added concurrent recording feature i.e. record/view multiple encrypted
+  channels on a single DVB card. This also works on a full-featured DVB card!
+  To make this feature work you NEED:
+  on a BUDGET card:
+    * SoftCSA version 0.1.0 or greater
+  on a FULL-FEATURED card:
+    * a DVB driver patched with dvb-cwidx.diff
+    * a specialy patched firmware. The "normal" patched firmware doesn't work!
+  See further details in the README.
+- vdr-sc patch has been updated. This plugin version ONLY works with this patch!
+  Older plugin versions doesn't work with this patch, although they may compile
+  fine!
+- Dropped compatibility for older VDR versions. Requires VDR version 1.3.29 or
+  newer.
+- Changed card & provider handling in cardclient camd35 (experimental).
+- Added NDS support to newcamd cardclient (experimental).
+- Added limited Seca 6a support.
+- Fixed mismatching Nagra1 & Nagra2 keys.
+
+------------------------------------------------------------
+
+17.12.2005: Version 0.4.12
+- Backported from 0.5.3:
+   * Fixed cardclient newcamd segfault on bad config data.
+   * Fixed ECM-EMM caid mismatch and segfault in cardclient camd35.
+   * Added new CAID nano to cardclient Radegast ECM request.
+- Backported from 0.5.2:
+   * Fixed ConstCW key lookup.
+   * Fixed cardclient camd3 to include SID in ECM request.
+   * Fixed cardclient newcamd to handle NULL-cw.
+   * Changed plugin default conf values to something more reasonable.
+- This is the final release for the 0.4.x branch. The 0.5.x branch is already
+  pretty stable. Please consider upgrading.
+
+31.10.2005: Version 0.4.11
+- Backported from 0.5.1:
+   * Fixed segfault for Seca1.
+   * Fixed Irdeto key updates.
+   * Now considering the key size when superseding keys.
+   * Updated finnish translations.
+
+24.09.2005: Version 0.4.10
+- Apply min. ECM processing time to Nagra2 too.
+- Backported from 0.5.0:
+   * Fixed mismatching Nagra1 & Nagra2 keys.
+   * Limited Seca 6a support.
+
+10.09.2005: Version 0.4.9
+- Fixed cardclient camd35 to include provider ID in ECM request.
+- Fixed Nagra2 padding 2nd RSA and CW swap for NA.
+- Fixed compiling issue in Nagra code on older VDR versions.
+- Fixed compiling issue in IDEA code if a recent openssl but without IDEA is
+  installed.
+
+04.09.2005: Version 0.4.8
+- Added Nagra2 code. See example keyfile for key format.
+- Fixed coredump in ECM delay feature.
+- Fixed buffer overflow in cardclient camd35.
+- Fixed setting serial IO speed for smartcard access.
+- Now truncating all key printouts in the debug log.
+
+18.08.2005: Version 0.4.7
+- Fixed compatibility issue with camd 3.7x.
+- Fixed switching to FTA while card is replaying.
+- Changed decoding, so that if the decoding fails consecutively, further
+  processing is delayed until the next parity change.
+- Fixed ECM card response parsing in smartcard Conax.
+- Added some generic ECM/EMM parsing.
+- Updated finnish translations.
+
+20.06.2005: Version 0.4.6
+- Added EMM support for smartcard Conax.
+- Added EMM support to Newcamd client.
+- Fixed EMM card command in smartcard Seca.
+- Fixed Camd Cmd05/Cmd06 offsets.
+- Now really fixed canceling netwatcher thread.
+
+25.05.2005: Version 0.4.5
+- Fixed Nagra key update issues.
+- Added support for the new Camd Cmd05/Cmd06. Thanks to Dukat for the example.
+- Added support for broadcasts without ECM data (only if system delivers
+  constant CW). Based on Ragnos patch.
+- Fixed ca.cache handling if ECM PID changed since entry was created.
+- Fixed Radegast client to work with shared Seca cards.
+- Fixed netwatcher cancel timeout.
+- Now using unaligned.h in cDes to prevent unaligned memory accesses on
+  architectures which doesn't support them (e.g. Alpha).
+
+07.02.2005: Version 0.4.4
+- Now caching CA descriptor data to speed up intial sync for systems which
+  depend on this data (e.g. Seca).
+- Fixed Nagra EMM code to handle additional nanos in front of cpu updates.
+- Fixed Seca RSA for short decrypt results.
+- Fixed Viaccess EMM assemble buffer size.
+
+21.01.2005: Version 0.4.3
+- Added Seca2 nano 5109 handling. You need special RSA keys for this. See
+  example keyfile.
+- Increased wait timeout for ECM extra data.
+- Fixed Seca permtables. Thanks to millemila.
+- Fixed Seca SHA1 signature. Thanks to BB.
+- Fixed return length position in Newcamd cardclient. Thanks to cart.
+
+10.01.2005: Version 0.4.2
+- Added workarounds for changes in vdr 1.3.18.
+- Fixed comparision of BIGNUM keys (Nagra, but not only Nagra).
+- Fixed cCondWait calls for vdr before 1.3.13.
+
+23.12.2004: Version 0.4.1
+- Added constant-CW system, CONSTCW=1 to activate. See example keyfile for CW
+  key format.
+- Added support for Viaccess TPS crypt v2. You need a suitable TPS key for your
+  provider. See example keyfile. 
+- Added support for v5.25 protocol in Newcamd cardclient (with fallback to old
+  protocol).
+- Added support for Nagra RSA key updates (high system id only). You need a
+  proper eeprom file for this to work.
+- Now delaying first access to smartcards until the cards are initialised. 
+- Fixed Seca code which though it has a correct decode while it hasn't.
+- Fixed several minor issues in Nagra code.
+- Fixed CA descriptor parsing for sc-viaccess and cardclients.
+- Fixed cardd client to send network message of at least 96 bytes.
+- Fixed inline asm macros to be used only on x86 CPUs.
+- Replaced non-reentrant libc functions with their reentrant counter part.
+- Replaced usleep() calls with proper cCondWait calls (vdr 1.3.x only).
+
+26.10.2004: Version 0.4.0
+- Versions bump, now in stable branch.
+- Added Viaccess smartcard code, SC_VIACCESS=1 to activate. ECM only for now.
+- Added ragnos Sc-Seca-PPV patch.
+- Added zens FullX-2 patch.
+- Added permtable for Seca provider 0x65.
+- Added cardclient config option to trigger immediate connect to cardserver on
+  startup.
+- Extended smartcard code to support indirect convention, zero-byte writes and
+  256-byte reads.
+- Fixed Nagra plaintext AU.
+- Fixed camd33 reconnecting and discard keep-alive packets.
+- Removed unnecessary bind() in udp networking (allows camd35 server on same
+  machine).
+- Fixed disconnect timeout not working if no dial script is given.
+- Updated finnish translations.
+
+------------------------------------------------------------
+
+26.09.2004: Version 0.3.17
+- Joined "dukat" & "freezer" cardclients to new "camd33" client.
+- Added cardclient "camd35", supporting camd 3.5 udp protocol.
+- Added caid & mask to cardclient config. Added carddata (hexbase, hexser) to
+  Aroureos cardclient config. See example cardclient.conf.
+- Fixed Viaccess logger (wrong section filter mask).
+- Fixed dialup networking not hanging up in case of connect error.
+
+14.09.2004: Version 0.3.16
+- Now supporting all Nagra keysets (pk0-2,typ0-1). Keyformat has changed. See
+  example keyfile.
+- Fixed some long standing bugs in Nagra ECM handling. Verify keys (V) are
+  mandatory now, 80 keynumber is obsolete. Note: you need both keysets for a
+  provider, e.g. for Polsat 7001 & 7101.
+- Added support for Seca provider 0x65 (appropriate hash/mt files needed).
+- Added finnish translations.
+- Fixed key length mismatch on EMM update for 8/16 byte keys.
+- Fixed sc-cryptoworks ATR parsing for cards with bios3mod.
+- Fixed stupid typo bug in sc-Irdeto which prevented EMM updates at all
+  (introduced in .13), fixed potentional buffer overflows.
+- Fixed Seca to really loop through all available keys (with same key nr).
+- Several compatibility fixes for 64-bit systems.
+
+07.08.2004: Version 0.3.15
+- Unified Seca, Viaccess & Nagra DES implementation.
+- Added CAID parsing to dukat cardclient.
+- Added support for 16-byte Seca keys.
+- Added Seca2 0064 51 nano processing.
+- Attempt to fix cardclient EMM update problem (appearently AES related).
+- Fixed Nagra CPU emu (random generator).
+
+23.07.2004: Version 0.3.14
+- Fixed Seca2 nano processing (Thanks to Sandali for new permutation values).
+- Fixed missing CAT parsing in Viaccess logger.
+- Fixed logger debug output (cardNum vs. CardNum()).
+- Fixed error-case memory leak in Nagra file mapper.
+
+01.07.2004: Version 0.3.13
+- Added Viaccess2 algo. Note that you need 16-byte keys for Viaccess2.
+- Added extended Seca2 nano handling.
+- Added provider based addressing and Irdeto2 support in common cardclient.
+- Added local caching of ECM/EMM messages to reduce cardserver load.
+- Major code restructure to unify system & logger code.
+- Fixed reconnecting to server in newcamd client.
+- Some changes for gcc 3.4.x compatibility.
+
+06.06.2004: Version 0.3.12
+- Added new Seca2 nano handling. Thanks to Nightshad.
+- Added some glue for VDR 1.3.7+ compatibility.
+- Major code rewrite in cardclient. Use CARDCLIENT=1 to activate. Configuration
+  of cardclients has changed completely. See README.
+- New network code supporting on demand dialup networking.
+- Added Radegast and Newcamd client.
+- Added minimum processing time to Nagra ECM code (emulates card processing time
+  in case timing depends on this). Configurable from the setup menu (0=disabled).
+- Fixed Nagra EMM tester code for DISH and ROM10/11 OTP size.
+- Fixed some possible table overflows in Seca code.
+- Fixed processing of short blocks in Conax code.
+- Fixed FTA switch in case VDR shortly interrupts a recording (e.g. when
+  changing PIDs).
+
+07.04.2004: Version 0.3.11a
+- Fixed Seca2 segfault on mask table access.
+- Now flushing ECM filter buffers on error.
+
+02.04.2004: Version 0.3.11
+- Added Seca2 support. Openssl and mask/hash table files needed. See README.
+  Seca1 compatiblility not yet tested. Thanks to Nightshad.
+- Added fix for Nagra ROM11 and plain key updates.
+- Using multiple threads to read from PID filters. Prevents that high-latency
+  systems (like smartcards) block out other systems.
+- Remember the system name in ca.cache and use this system first later on.
+  Prevents delays for low-priority systems if ECM is cached.
+- Fixed smartcard Seca PBM checking for cards which doesn't support the needed
+  card command.
+- Fixed network timeout and flushing read buffer in cardserver client.
+- Fixed transponder handling for VDR 1.3.x (frequency vs. transponder).
+
+22.02.2004: Version 0.3.10
+- Added cardserver client (Streamboard client). EMM transfers are disabled at
+  the moment. Contributed by S.Laurel.
+- Added support for Conax smartcards. Thanks to Nightshad.
+- Added provider based addressing for EMM updates in Irdeto smartcard.
+- Added commandline option to support card reader with reverse reset line (-R).
+- Added make option to add a smartcard default port (DEFAULT_PORT).
+- Reworked plugin setup menu. Added separate status page. Added smartcard
+  information page (press OK on the smartcard interface line).
+- Added code to allow system specific setup options.
+- Fixed off by one error and more mixed up arguments in Seca1 decryption.
+- Fixed typos in Viaccess logger debug statements.
+
+05.02.2004: Version 0.3.9
+- Fixed memory initialisation in Nagra cardemu.
+- Fixed filedescriptor leak in Nagra ROM mapping code.
+- Fixed MECM XOR table if there are less than 64 bytes. Thanks to Vlinders.
+- Fixed off by one error in (unused) smartcard Irdeto camcrypt code.
+- Fixed mixed up function arguments in Seca1 decryption.
+- Fixed Viaccess logger for ViaSat and TPS shared updates. Note that the format
+  of the Viaccess.KID file has changed (SA added). Thanks to Nightshad.
+- Changed default make options. Irdeto, Seca & Viaccess systems aren't compiled
+  by default anymore. Use IRDETO=1 SECA=1 VIACCESS=1 for old behaviour.
+
+18.01.2004: Version 0.3.8
+- Several improvements to the Nagra code (Cardemu, MECM). Completely new
+  layout, no more libnagra. Now you need some Eeprom files and the location for
+  the ROM files has changed. See README file.
+  Thanks to Nightshad, BB and their friends.
+- Fixed Irdeto smartcard EMM command creation for PW. Also don't overrun the
+  card with ECM requests in case of not subscribed/expired channel.
+- Reworked Seca, Viaccess and Irdeto crypto code. No more libmgcam.
+- Added commandline option to detect smartcards in readers with reverse CD.
+- Added a SC patch for vdr 1.3.1 and some glue to the plugin. See README for
+  notes about 1.3.1 support.
+
+13.12.2003: Version 0.3.7
+- Several Nagra auto-update fixes. Thanks to Nightshad.
+- Fixed Irdeto smartcard EMM path. Thanks to Scotty.
+- Fixed wrong behaviour in case of transfer mode.
+- Added support to load Nagra ROM extentions (needs ROM?ext.bin files).
+- Now checking PBM and date in Seca smartcard. Fixed card status for EMM
+  updates. Thanks to Nightshad.
+- Added a VDR patch to prevent SC activation during EPG scans.
+  Contributed by Andy.
+- Changed CICAM handling. SC activation is now more channel base.
+  Note: you have to update your config! See README file.
+
+8.11.2003: Version 0.3.6
+- Added support for Irdeto smartcards (e.g. Premiere). Thanks to Scotty.
+- Added smartcard insert/remove detection, card auto-initialisation and support
+  for multiple card interfaces.
+- Enhanced smartcard ISO functions (e.g. 0x60 handling, ATR parsing).
+- Fixed smartcard Cryptoworks (V3 ATR, serial timeout).
+- Fixed Nagra ROM10 emu (Cabo 4801).
+- Fixed saving ECM values to ca.cache.
+- Fixed removing of cached ECM entries so that only really failed entries are
+  removed.
+
+24.10.2003: Version 0.3.5
+- Fixed Nagra cardemulator (MUL bug, OTP area, etc). Thanks to BB & Nightshad.
+- Added handling for ROM specific Nagra keys. See examples/Softcam.Key.
+- Fixed display of used Nagra key in the OSD.
+
+18.10.2003: Version 0.3.4
+- Added EMM logger in sc-Seca to auto-update card data. Works for Seca2 cards
+  too. Thanks to Nightshad.
+- Added cardemulator to execute ROM code for improved Nagra key updates. Thanks
+  to BB & Nightshad. Probably there are still some issues with providers which
+  require seperate keys for EMM processing. This will be addressed in the next
+  release.
+- Now removing old, failed ECM entries from the cache.
+- Fixed possible race condition in Nagra rom locking.
+- Fixed memory leaks in Nagra & Conax code (BIGNUM handling).
+
+18.07.2003: Version 0.3.3
+- Allow new keys to supersede/invalidate older keys (only for systems with
+  unique key identifiers; for now all beside Irdeto & @SHL). This should fix
+  Viaccess key updates.
+- Made Viaccess loop through all available keys while decrypting.
+
+05.07.2003: Version 0.3.2
+- Added some information about @SHL to the README.
+- Cleaned up @SHL code. Now supporting multiple keys in keyfile, no need to
+  uncomment unused keys.
+- New code for hex dumping of incoming data (see common.h for activation).
+- Excluding a range of potentional "fake" ECM pids for Viaccess system. Does
+  this affects any valid channel?
+
+24.06.2003: Version 0.3.1unstable
+- Completely new section filter handling.
+- Restructure of the logger code (will allow EMM processing for smartcards).
+- Reworked Viaccess logger. Thanks to BB & Nightshad.
+- Added @SHL (SkyCrypyt) support. Thanks to Nightshad and his friends.
+- Fixed writing duplicate ecm entries into cache file.
+- Fixed SoftCSA activation (was broken in 0.3.0, 0.2.x is fine).
+- Fixed saving Viaccess keys with odd digit count.
+- Fixed leaving a locked mutex in CAM setup.
+- Fixed crash in smartcard setup if no serial device was given.
+- Added examples files to show file formats.
+- Added system specific Makefile's (*.mk).
+- Added french translations.
+
+25.05.2003: Version 0.3.0unstable
+- Forked 0.3.x unstable branch.
+- Major restructure of CA system code.
+- Added Viaccess logger. Requires Viaccess.KID. Thanks to BB for providing
+  the sample code.
+- Generalized smartcard code. Use new plugin commandline option -s to set
+  serial port. Use setup menu to select card type.
+- Now supporting Seca smartcards. Another big thanks to Nightshad.
+- Reworked Cryptoworks smartcard code. Please report working status.
+
+------------------------------------------------------------
+
+29.05.2003: Version 0.2.2
+- Added patch for vdr 1.1.32/33.
+- Added french translations. Thanks to Piout.
+
+14.05.2003: Version 0.2.1
+- Adapted to the changes in (and now also requires) VDR 1.1.31.
+
+05.05.2003: Version 0.2.0
+- Some code cleanup.
+- Fixed Nagra key saving.
+- Added Greek translations. Thanks to Aroureos.
+- Stable release version. A new development branch will fork soon.
+
+27.04.2003: Version 0.2.0rc1
+- Added patch for vdr 1.1.29
+
+24.04.2003: Version 0.1.13
+- Rescanning CaDescr from time to time in case that we initialy got some
+  descriptors but are unable to find a valid key with them.
+- Some more Cryptoworks fixes.
+- Fixed BN bug in Nagra RSA code.
+- Newbie protection: actively checking required VDR version and some patch
+  versions.
+- Adapted to the changes in (and now also requires) VDR 1.1.28. Timeshifting of
+  encrypted channels works. You must use a patched LL firmware for this feature,
+  the -icam firmware won't allow timeshift.
+
+14.04.2003: Version 0.1.12
+- Changed debug messages to avoid confusion between loaded keys and loaded
+  cards *sigh*.
+- Now using VDR internal functions to get the ECM pids. This requires VDR 1.1.27
+  to work. Note also, that there is a new sc patch for VDR 1.1.27.
+- Make the ECM cache take care of the fact that the SID may not be unique (now
+  matching for source & transponder too).
+- Cleanup of Nagra code (now in libnagra). Got rid of the miracle stuff, now
+  using libcrypto (openssl) here too. Big thanks to Nightshad.
+- Added (untested) Nagra BEV/DISH support.
+
+21.03.2003: Version 0.1.11
+- Fixed segmentation fault on plugin exit (stupid: static initialization of
+  list members is a no-no).
+- Increased timeout for reading PAT.
+- Some fixes to the Cryptoworks code. Hope this works better. No testing here.
+
+03.03.2003: Version 0.1.10
+- Improved "stop logger" with transfer mode. Disabled this feature for budget
+  cards. They don't crash with logger enabled.
+- Fixed stopping sc processing if a transfer mode ends.
+- SoftCSA now available as separate archive.
+
+19.03.2003: Version 0.1.9
+- Fixed Nagra MECM handling.
+- Logger is stopped when a transfer mode is detected, too.
+- Experimental SoftCSA support. See README for setup instructions. Thanks to
+  emumensch for the initial version.
+
+31.01.2003: Version 0.1.8
+- Fixed Conax decoding and key handling.
+- Fixed Nagra key strings which havn't been NULL terminated.
+- Fixed loading SoftCam.Key with Nagra/Conax keys if one or both have been
+  disabled at compile time.
+- Fixed long standing bug in FastKey().
+- Added Nagra logger. No need for a Nagra.KID, the keys should still be in
+  SoftCam.Key if you are able to watch Nagra channels. Thanks to Nightshad.
+
+15.01.2003: Version 0.1.7
+- Switched to Conax code from mgcam_20030114 (much shorter). You must have
+  installed libcrypto (part of openssl). This fixes the Conax linking problem in
+  0.1.6 too.
+- Nagra & Conax keys are now loaded from SoftCam.Key too. See README for format.
+
+14.01.2003: Version 0.1.6
+- Fixed Viacess code for NTV and TPS crypt (mgcam_20021123).
+- Extended setup option for logger. Logger can be stopped during recordings as
+  soon as a valid key is found. Is this more stable?
+- Fixed a compile error in Nagra code (miracl.h).
+- Switched Cryptoworks support to libcwemu. Serial line detection is now in the
+  main Makefile, see ISO_TTY. CRYTOWORKS=1 to enable.
+- Added Conax support, based on lincardemu0.1.9n and some adaptions to mgcam
+  found on the net. Thanks to all the guys for their work. CONAX=1 to enable.
+- Fixed PMK update in Irdeto logger.
+- Updated card data is also written to ca.cache now. The ca.cache format has
+  changed for this. The old format is converted automatically.
+- Changed detection of fake channel id's.
+- Improved filter settings and paket reading.
+
+16.11.2002: Version 0.1.5
+- Adapted to the changes in vdr 1.1.15. Now requires vdr >=1.1.15 and HEAD
+  driver.
+- Added Cryptoworks support together with original cards and a cardreader with
+  Phoenix ISO interface at the serial line. Add CRYPTOWORKS=1 to enable. (see
+  libmgcam/cryptoworks.c for serial line selection).
+- Remove xor/firmware hacks (not needed anymore).
+
+28.10.2002: Version 0.1.4
+- Adapted to the changes up to vdr 1.1.14 and the new HEAD DVB driver.
+- Added option to setup menu to reload config files.
+- Added code to detect fake channel id's on primafila.
+- Minor logger fixes.
+
+28.09.2002: Version 0.1.3
+- Now compiles with vdr 1.1.9+.
+- Cleaned up Viaccess code.
+- Added Seca logger.
+- Fixed switching back to a FTA channel after recording.
+
+09.09.2002: Version 0.1.2
+- Load cardinfos/cacache even if no keyfile found.
+- Fixed rescanning of PAT/PMT if temporarily no enc. system found.
+- Fixed timeout handling while reading from dvb device.
+- Removed usleep() loops from logger code.
+
+29.08.2002: Version 0.1.1
+- Added missing code to save the xor & firmware hack setup.
+
+22.08.2002: Version 0.1.0
+- Initial release.
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile
new file mode 100644 (file)
index 0000000..fee8353
--- /dev/null
@@ -0,0 +1,260 @@
+#
+# Softcam plugin to VDR
+#
+# This code 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 code 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
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = sc
+
+### The version number of this plugin
+
+DISTFILE = .distvers
+HGARCHIVE = .hg_archival.txt
+RELEASE := $(shell grep 'define SC_RELEASE' version.h | awk '{ print $$3 }' | sed -e 's/[";]//g')
+SUBREL  := $(shell if test -d .hg; then \
+                     echo -n "HG-"; (hg identify 2>/dev/null || echo -n "Unknown") | sed -e 's/ .*//'; \
+                   elif test -r $(HGARCHIVE); then \
+                     echo -n "AR-"; grep "^node" $(HGARCHIVE) | awk '{ printf "%.12s",$$2 }'; \
+                   elif test -r $(DISTFILE); then \
+                     cat $(DISTFILE); \
+                   else \
+                     echo -n "Unknown"; \
+                   fi)
+VERSION := $(RELEASE)-$(SUBREL)
+SCAPIVERS := $(shell sed -ne '/define SCAPIVERS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' $(PLUGIN).c)
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+SYSDIR = ./systems
+PREDIR = ./systems-pre
+TMPDIR = /tmp
+
+### The C++ compiler and options:
+
+CXX      ?= g++
+CXXFLAGS ?= -O2 -g -fPIC -Wall -Woverloaded-virtual
+
+### Includes and Defines
+
+INCLUDES      = -I$(VDRDIR)/include
+DEFINES       = -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+SHAREDDEFINES = -DAPIVERSNUM=$(APIVERSNUM) -D_GNU_SOURCE
+LIBS          = -lcrypto
+SHAREDLIBS    =
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+-include Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION := $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSION := $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+ifeq ($(strip $(APIVERSION)),)
+   APIVERSION = $(VDRVERSION)
+endif
+VDRVERSNUM := $(shell sed -ne '/define VDRVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSNUM := $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+ifeq ($(strip $(APIVERSNUM)),)
+   APIVERSNUM = $(VDRVERSNUM)
+endif
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o data.o filter.o system.o misc.o cam.o version.o \
+       smartcard.o network.o crypto.o system-common.o parse.o log.o
+
+### Internationalization (I18N):
+
+PODIR     = po
+I18Npot   = $(PODIR)/$(PLUGIN).pot
+ifeq ($(strip $(APIVERSION)),1.5.7)
+  I18Nmo  = $(PLUGIN).mo
+else
+  I18Nmo  = vdr-$(PLUGIN).mo
+endif
+I18Nmsgs  = $(addprefix $(LOCALEDIR)/,$(addsuffix /LC_MESSAGES/$(I18Nmo),$(notdir $(foreach file, $(wildcard $(PODIR)/*.po), $(basename $(file))))))
+LOCALEDIR = $(VDRDIR)/locale
+HASLOCALE = $(shell grep -l 'I18N_DEFAULT_LOCALE' $(VDRDIR)/include/vdr/i18n.h)
+
+ifeq ($(strip $(HASLOCALE)),)
+  OBJS += i18n.o
+endif
+
+#
+# generic stuff
+#
+
+# smartcard default port
+ifdef DEFAULT_PORT
+  TEST := $(shell echo '$(DEFAULT_PORT)' | sed -ne '/".*",.*,.*,.*/p')
+  ifneq ($(strip $(TEST)),)
+    DEFINES += -DDEFAULT_PORT='$(DEFAULT_PORT)'
+  else
+    $(error DEFAULT_PORT has bad format)
+  endif 
+endif
+
+# max number of CAIDs per slot
+MAXCAID := $(shell sed -ne '/define MAXCASYSTEMIDS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' $(VDRDIR)/ci.c)
+ifneq ($(strip $(MAXCAID)),)
+  DEFINES += -DVDR_MAXCAID=$(MAXCAID)
+endif
+
+# FFdeCSA
+CPUOPT     ?= pentium
+PARALLEL   ?= PARALLEL_32_INT
+CSAFLAGS   ?= -Wall -fPIC -g -O3 -mmmx -fomit-frame-pointer -fexpensive-optimizations -funroll-loops
+FFDECSADIR  = FFdecsa
+FFDECSA     = $(FFDECSADIR)/FFdecsa.o
+
+# SASC
+ifdef SASC
+DEFINES += -DSASC
+FFDECSA =
+endif
+
+# export for system makefiles
+export SCAPIVERS
+export APIVERSION
+export INCLUDES
+export SHAREDDEFINES
+export SHAREDLIBS
+export CXX
+export CXXFLAGS
+
+### Targets:
+
+ifdef STATIC
+BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).a systems
+SHAREDDEFINES += -DSTATICBUILD
+else
+BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION) systems systems-pre
+endif
+
+ifneq ($(strip $(HASLOCALE)),)
+BUILDTARGETS += i18n
+endif
+
+all: $(BUILDTARGETS)
+.PHONY: i18n systems systems-pre clean clean-core clean-systems clean-pre dist srcdist
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+DEPFILES = $(subst i18n.c,,$(subst version.c,,$(OBJS:%.o=%.c)))
+$(DEPFILE): $(DEPFILES) $(wildcard *.h)
+       @$(MAKEDEP) $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $(DEPFILES) > $@
+
+-include $(DEPFILE)
+
+# Rules
+
+%.o: %.c
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $<
+
+libvdr-$(PLUGIN).so: $(OBJS) $(FFDECSA)
+       $(CXX) $(CXXFLAGS) -shared $(OBJS) $(FFDECSA) $(LIBS) $(SHAREDLIBS) -o $@
+
+$(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION): libvdr-$(PLUGIN).so
+       @cp -p $< $@
+
+$(LIBDIR)/libvdr-$(PLUGIN).a: $(OBJS)
+       $(AR) r $@ $(OBJS)
+
+$(FFDECSA): $(FFDECSADIR)/*.c $(FFDECSADIR)/*.h
+       @$(MAKE) COMPILER="$(CXX)" FLAGS="$(CSAFLAGS) -march=$(CPUOPT) -DPARALLEL_MODE=$(PARALLEL)" -C $(FFDECSADIR) all
+
+$(I18Npot): $(shell grep -rl '\(tr\|trNOOP\)(\".*\")' *.c $(SYSDIR))
+       xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<noone@nowhere.org>' -o $@ $^
+
+%.po: $(I18Npot)
+       msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+       @touch $@
+
+%.mo: %.po
+       msgfmt -c -o $@ $<
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/$(I18Nmo): $(PODIR)/%.mo
+       @mkdir -p $(dir $@)
+       cp $< $@
+
+i18n: $(I18Nmsgs)
+
+i18n.c: $(PODIR)/*.po i18n-template.c po2i18n.pl
+       perl ./po2i18n.pl <i18n-template.c >i18n.c
+
+version.c: FORCE
+       @echo >$@.new "/* generated file, do not edit */"; \
+        echo >>$@.new 'const char *ScVersion =' '"'$(VERSION)'";'; \
+        diff $@.new $@ >$@.diff 2>&1; \
+        if test -s $@.diff; then mv -f $@.new $@; fi; \
+        rm -f $@.new $@.diff;
+
+systems:
+       @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" all || exit 1; done
+
+systems-pre:
+       @for i in `ls -A -I ".*" $(PREDIR) | grep -- '-$(SCAPIVERS).so.$(APIVERSION)$$'`; do cp -p "$(PREDIR)/$$i" "$(LIBDIR)"; done
+
+clean-systems:
+       @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" clean; done
+
+clean-core:
+       @$(MAKE) -C testing clean
+       @if test -d $(FFDECSADIR); then $(MAKE) -C $(FFDECSADIR) clean; fi
+       @-rm -f $(LIBDIR)/libsc-*-$(SCAPIVERS).so.$(APIVERSION)
+       @-rm -f $(LIBDIR)/libvdr-$(PLUGIN).a $(LIBDIR)/libsc-*.a
+       @-rm -f $(OBJS) $(DEPFILE) version.c i18n.c *.so *.tar.gz core* *~
+       @-rm -f $(PODIR)/*.mo
+
+clean-pre:
+       @-find "$(PREDIR)" -type f -not -name ".empty" -not -iname "*-$(SCAPIVERS).so.*" | xargs rm -f
+
+clean: clean-core clean-systems
+
+dist: ARCHIVE := $(PLUGIN)-$(RELEASE)
+dist: clean-core
+       @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" dist; done
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @mkdir $(TMPDIR)/$(ARCHIVE)
+       @cp -a * $(TMPDIR)/$(ARCHIVE)
+       @echo -n "release" >$(TMPDIR)/$(ARCHIVE)/$(DISTFILE)
+       @path="$(TMPDIR)/$(ARCHIVE)/$(notdir $(SYSDIR))";\
+        for i in `ls -A -I ".*" $$path`; do if [ -f "$$path/$$i/nonpublic.mk" ]; then rm -rf "$$path/$$i"; fi; if [ -f "$$path/$$i/nonpublic.sh" ]; then (cd $$path/$$i ; source ./nonpublic.sh ; rm ./nonpublic.sh); fi; done
+       @strip --strip-unneeded --preserve-dates $(TMPDIR)/$(ARCHIVE)/$(notdir $(PREDIR))/*
+       @tar czf vdr-$(ARCHIVE).tar.gz -C $(TMPDIR) $(ARCHIVE)
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @echo Distribution package created as vdr-$(ARCHIVE).tar.gz
+
+copy: ARCHIVE := $(PLUGIN)-$(VERSION)
+copy: clean clean-pre
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @mkdir $(TMPDIR)/$(ARCHIVE)
+       @cp -a .hgtags .hgignore * $(TMPDIR)/$(ARCHIVE)
+       @echo -n $(SUBREL) | sed -e 's/HG-/CP-/' >$(TMPDIR)/$(ARCHIVE)/$(DISTFILE)
+       @tar czf vdr-$(ARCHIVE).tar.gz -C $(TMPDIR) $(ARCHIVE)
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @echo Full copy package created as vdr-$(ARCHIVE).tar.gz
+
+FORCE:
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile.system b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/Makefile.system
new file mode 100644 (file)
index 0000000..36a192e
--- /dev/null
@@ -0,0 +1,94 @@
+#
+# Softcam plugin to VDR
+#
+# This code 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 code 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
+
+### The directory environment:
+
+LIBDIR = ../../../../lib
+PREDIR = ../../systems-pre
+
+### Includes and Defines
+
+SINCLUDES = -I../..
+SINCLUDES += $(shell echo "$(INCLUDES)" | sed -e 's+-I *+-I+g' | sed -e 's+-I\([^/]\)+-I../../\1+g')
+
+-include *.mk
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): $(OBJS:%.o=%.c) $(wildcard *.h)
+       @$(MAKEDEP) $(DEFINES) $(SHAREDDEFINES) $(SINCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+LIBSC    = libsc-$(TARGET).so
+LIBSCVER = libsc-$(TARGET)-$(SCAPIVERS).so.$(APIVERSION)
+LIBSCAR  = libsc-$(TARGET).a
+
+ifdef STATIC
+BUILDTARGETS = $(LIBDIR)/$(LIBSCAR)
+else
+BUILDTARGETS = $(LIBDIR)/$(LIBSCVER) $(PREDIR)/$(LIBSCVER)
+endif
+
+define clean
+@-rm -f $(OBJS) $(CLEAN_RM) $(DEPFILE) $(LIBSC) core* *~
+endef
+
+define clean-pre
+@find "$(PREDIR)" -type f -iname "libsc-$(TARGET)-*.so.*" -not -iname "libsc-$(TARGET)-$(SCAPIVERS).so.*" | xargs rm -f
+@-rm -f $(PREDIR)/$(LIBSCVER)
+endef
+
+all: $(BUILDTARGETS)
+.PHONY: clean dist
+
+%.o: %.c
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(SINCLUDES) $<
+
+$(LIBSC): $(OBJS)
+       $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) $(SHAREDLIBS) -o $@
+       $(clean-pre)
+
+$(LIBDIR)/$(LIBSCVER): $(LIBSC)
+       @cp -p $< $@
+
+$(LIBDIR)/$(LIBSCAR): $(OBJS)
+       $(AR) r $@ $(OBJS)
+
+ifdef NONPUBLIC
+$(PREDIR)/$(LIBSCVER): $(LIBSC)
+       $(clean-pre)
+       @cp -p $< $@
+
+dist: $(PREDIR)/$(LIBSCVER)
+       $(clean)
+else
+$(PREDIR)/$(LIBSCVER):
+
+dist:
+       $(clean-pre)
+       $(clean)
+endif
+
+clean:
+       $(clean-pre)
+       $(clean)
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/README b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/README
new file mode 100644 (file)
index 0000000..7c44088
--- /dev/null
@@ -0,0 +1,380 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+See the file COPYING for license information.
+
+Description: SoftCAM for Irdeto, Seca, Viaccess, Nagra, Conax & Cryptoworks
+
+-----------------------------------------------------------------------
+
+
+
+What is it ?
+------------
+
+First: Most certainly it's not legal to use this software in most countries of
+the world. But probably you already know this...
+
+SC means softcam, which means a software CAM emulation.
+
+The plugin captures the DVB devices in an early startup stage of VDR startup
+(before VDR itself has got access to them) and makes VDR believe that there is
+another CAM connected to the device. This CAM is emulated from the plugin by
+providing a cut-down EN50221 interface for communication with the VDR core. From
+VDR views there is no difference between a real hardware CAM and the emulated
+CAM. 
+
+The plugin decrypts the scrambling codewords from the incomming ECM stream. The
+actual descrambling of the video stream is either done by the ECD chip on
+full-featured DVB cards or with the included FFdecsa implementation on budget
+cards.
+
+This piece of software is originaly based on (and still contains code from)
+mgcam (a standalone CAM emulation). Many thanks to the (anonymous) author for
+his really fine piece of software :-)
+
+
+
+Requirements
+------------
+
+* DVB driver from dvb-kernel 2.6 or 2.4 branch with applied patches
+* a patched firmware version 2620 or newer
+* VDR 1.5.0 or newer (VDR 1.4.6+ in compatibility mode, see 1.4.x setup section)
+* Openssl package version 0.9.7 or newer
+
+
+
+How to setup ?
+--------------
+
+First you should start with a recent dvb-kernel driver (cvs recomended). Copy
+the patched firmware in place and apply at least the dvb-cwidx patch. Make sure
+that you use a patched firmware if you intend to use the plugin together with a
+full-featured DVB card. You definitely need a patched firmware in this case, but
+only recent versions support concurrent recording! Recompile the driver, unload
+the modules, install the new ones and reload the DVB driver. If you suffer from
+ARM crashes, add "hw_sections=0" while loading the dvb-ttpci module.
+
+Contrary to older plugin versions you MUST NOT apply any patches to the VDR core
+(neither vdr-sc nor ffdecsa/softcsa).
+
+You must have installed the openssl development files. For most distributions
+this means to install openssl-devel package. You should use a openssl package
+with AES and IDEA enabled, as support for openssl without these will be removed
+in the near future.
+
+Now follow the VDR instruction to compile plugins (make plugins). Beside the
+core plugin (libvdr-sc.so), the make process (if successfull) creates an
+additional shared library object for every supported system (libsc-*.so). You
+can enable/disable individual systems by adding or removing the shared library
+from your VDR plugin lib directory.
+
+Starting with version 0.5.8 all make compile time options (e.g. IRDETO=1) except
+DEFAULT_PORT have been removed!
+
+Note that in combination with other plugins which create devices (e.g.
+softdevice) it's essential that this plugin is loaded before any of these
+plugins, i.e. as a rule of thumb put this plugins first on the VDR commandline.
+The plugin will fail on startup if the plugin load order results in mismatched
+device numbering inside VDR.
+
+Note that some budget card drivers provide a CA device too. This might make VDR
+and the plugin detect the card as a full-featured card, thus disabling FFdecsa.
+You should use commandline option -B to force detection as a budget card in such
+a case. This commandline option takes a device number as argument and may appear
+multiple times (compare VDR commandline option -D).
+
+By default the plugin logs his messages to console only. For testing purpose
+it's a good idea to start VDR in foreground so you can see any error messages.
+Other available log targets are file and syslog. You can enable/disable any of
+the log targets in the plugin setup menu. For the file target you have to set a
+destination filename too. If you set a filesize limit (in KByte) and the logfile
+grows bigger than the limit, the current logfile is renamed to logfile.old and a
+new logfile is started. If the filesize limit is zero, the logfile never is
+rotated.
+
+
+
+How to setup for VDR 1.4.x ?
+----------------------------
+
+Additional to the points mentioned above, you have to patch the VDR core with
+the supplied patch (vdr-1.4.x-sc7.diff). Recompile VDR and use the new binary.
+Patches from older SC releases are not going to work.
+
+Even with VDR 1.4.x you don't have to use a softcsa/ffdecsa patch.
+
+Activating/deactivating DVB cards in the plugin setup menu needs a VDR restart
+to take effect.
+
+
+
+Pre-compiled libraries
+----------------------
+
+
+There is the possibility that encryption systems are provided in binary, pre-
+compiled only form. During make process, all pre-compiled libraries are copied
+to your VDR plugin lib directory.
+
+Please be aware, that pre-compiled libraries are more or less bound to the hard-
+& software configuration they have been build on. Currently the build system is
+Intel 32bit, gcc 3.2.2, glibc 2.2.5. If your system differs too much, it may be
+impossible to use the pre-compiled libraries.
+
+Obviously, pre-compiled libraries cannot be exchanged between different SC
+and/or VDR API versions. Be aware that if you patch your VDR core and this patch
+involves changes to header files (*.h) this might change the VDR API even if the
+API version number hasn't changed. This may lead to silent malfunction/failure
+of pre-compiled libraries. In particular you should stay away from thread.h and
+tools.h as classes from there are used at many, many places.
+
+The naming scheme for the libraries is libsc-<MODULE>-<SCAPI>.so.<APIVERSION>,
+e.g. libsc-cardclient-2.so.1.3.47
+
+
+
+CAID and VDR CICAM setup
+------------------------
+
+The activation of the SC is controlled by your CICAM setup. As general setup
+(which is not SC specific) you should leave the CA values (in channels.conf)
+set to zero and let VDR's channel scanner (autopid) fill in the correct values.
+Don't touch the CA values afterwards.
+In the plugin setup menu, you now have to specify for which DVB cards the SC
+should be activated. The first two cards can be setup from the menu. If you
+need more, you can edit the setup.conf file manualy and add up to 10 cards.
+
+A real hardware CAM normaly knows which CAIDs it supports. With SC the situation
+is a bit different. There is support for a wide range of encryption system and
+cardclients. This results in a huge number of supported CAIDs, but for most of
+them it's uncertain if SC will actualy be able to decrypt a channel for them. On
+the other hand VDR limits the number of supported CAIDs to 16 for a CAM slot (64
+CAIDs in VDR 1.5.3 or later), so SC is able to announce a small number of CAIDs
+only. This is not as bad as it sounds, as VDR will try a CAM if ANY of the
+channel CAIDs matches the CAIDs announced by the CAM.
+On startup and at regular intervals the plugin scans the channels list and
+builds a chain of CAIDs. The CAIDs are assigned to the simulated CAM. 
+
+To reduce the number of CAIDs SC has to deal with, you should obey some rules:
+-Remove all libsc-* files for encryption system which you don't intend to use
+ (e.g. SHL seems pretty useless nowadays).
+-When using a cardclient, be as precise as possible with the CAID/MASK values in
+ the cardclient.conf file. Using wide open 0000/0000 is deprecated.
+-Add CAIDs which you cannot use due to lack of keys to the SC.CaIgnore setting.
+
+
+
+
+Concurrent Recordings
+---------------------
+
+There is an entries in the plugin setup menu to control concurrent usage of a
+full-featured DVB card. You should enable concurrent usage only if you are using
+the special patched firmware AND a patched DVB driver. Note that toggling the
+flag will take effect the next time the plugin is idle on that specific DVB card
+only (i.e. no channel is being decrypted).
+
+There is no possibility to limit the number of concurrent streams. VDR itself
+has no limit in concurrent streams (neither FTA nor encrypted) and as the VDR
+core control all aspects of operation, there is no way to enforce a limit
+(beside disabling concurrent encrypted streams at all).
+
+
+
+Additional files
+----------------
+
+All config files are expected to be located in the subdirectory "sc" of VDRs
+plugin config directory. The user executing VDR should have write permissions
+for this directory, as the plugin will create cache files there.
+
+The keyfile must be named "SoftCam.Key". Any updated keys are saved back to this
+file. At this the structure of the file (e.g. comments) is preserved as far as
+possible. Updated key are inserted near to the old one (there is a setup option
+to selected if the old key is commented out or deleted), while new keys are
+inserted close to the top of the file.
+
+For Irdeto, Seca and Viaccess AU you need valid subscription card data, which
+have to be located in the files "Ird-Beta.KID", "Seca.KID" or "Viaccess.KID".
+See the files in the "examples" subdirectory for file formats.
+
+Note, that for this @SHL implementation the key must be in Z 00 00 <key> format
+(the V 000000 00 <key> format doesn't work).
+
+For Seca2 support you need binary files which contain the hash & mask tables.
+The file format is the same as for Yankse. The files must be located in the
+"seca" subdirectory. The name sheme is s2_TTTT_XXXX.bin where TTTT is
+one of "hash","mt" and XXXX is the provider ID (e.g. s2_hash_0064.bin,
+s2_mt_0070.bin). The hash file must be 1536 bytes long. The mt file is normaly
+16384 bytes long, but this may differ for your provider. For advanced Seca2
+providers you may need additional table files. At the moment these are
+s2_sse.bin, s2_sse_XXXX.bin and s2_cw_XXXX.bin.
+
+For Nagra1 AU you need appropriate binary Rom and Eeprom files. The files have
+to be named "ROMX.bin", "ROMXext.bin" or "eepX_Z.bin", where X is the ROM number
+(decimal) and Z is the upper part of the provider ID (hexadecimal). The Eeprom
+files may be updated from the EMM data, take care that the permissions are set
+right. The plugin searches for these files in the "nagra" subdirectory.
+
+For Nagra2 AU some providers need binary Rom and Eeprom files. The files have to
+be named "ROMxxx.bin" and "EEPyy_xxx.bin", where xxx is the ROM version (e.g.
+102) and yy is the upper part of the provider ID (e.g. 09 for BEV). The files
+must contain the joined contents of all Rom/Eeprom pages. The plugin searches
+for these files in the "nagra" subdirectory.
+
+
+
+External key updates
+--------------------
+
+If key updates are available from external sources (e.g. website) only, they may
+be feed from a shell script. To enable this, you have to specify the script name
+with commandline option "-E". The script will be called at a regular interval
+(currently 15 minutes) or whenever a needed key is not available (but not more
+often than every 2 minutes). The script has to output the keys to it's stdout in
+the same format as for the key file. The script may output several keys in one
+call (each key on a seperate line). You can find an example script in the
+"examples" subdirectory.
+
+
+
+Smartcard support
+-----------------
+
+For most encrpytion systems this plugin supports original subscription
+smartcards on a Phoenix/Smartmouse ISO interface connected to a serial port.
+
+To enable smartcard support you have to copy one or more of the smartcard
+systems to the VDR plugin lib directory. To actually activate the smartcard
+interface, you should use the commandline option "-s" to specify one or more
+serial devices to which the Phoenix interface are connected e.g. use "-s
+/dev/ttyS0 -s /dev/ttyS1" to use two intefaces at COM1/COM2. If you want to add
+a default smartcard interface at compile time use the make option DEFAULT_PORT,
+e.g. DEFAULT_PORT='"/dev/ttyS0","phoenix",0,0,0'. Note the quotes and double quotes. The
+three numeric values are identical to the -I and -R options (set to 1 to enable)
+and -C option (set to 0 for default clock) below.
+
+Appearently there are "broken" card readers which swap the meaning of the CD
+line (used for card detection). For these readers use the option "-I". This
+enables inverse CD detection for the next interface e.g. "-I -s /dev/ttyS0 -s
+/dev/ttyS1" will use inverse CD on COM1 and normal CD on COM2 while "-I -s
+/dev/ttyS0 -I -s /dev/ttyS1" will use inverse CD on both.
+Some other card readers have a reversed logic with the reset line (card won't
+reset with default settings). You can use the option "-R" for these readers.
+In some cases it's mandatory to know the exact clock frequency at which your
+cardreader runs (e.g. for baudrate calculations). With the option "-C" you can
+give a clock frequency (in Hz) which will be used instead of the default
+(3571200 Hz) for the next interface e.g. "-C 3579545 -s /dev/ttyS1".
+
+Some smartcards need additional information to establish communication with the
+card (e.g. certificate or box key for camcrypt). These information must be
+available in the "smartcard.conf" file (see example file for format) or you card
+won't work correctly.
+
+If you insert a card into a interface the card is autodetected (your interface
+should use the CD line to signal card presence or it won't work) and
+initialised (this may take some seconds to complete). You can use the setup
+menu to see which cards are currently inserted and detected. You can remove a
+smartcard at any time without prior action, but of course this will disrupt
+decryption if you are tuned to a channel which requires the card.
+
+Note that a normal Phoenix interface may not be sufficient for communication
+with some smartcards. This is due to timing issues. The Smargo SmartReader+
+interface has a special "SmartReader+" mode, where the interface itself
+handles communication with the smartcard (i.e. independent of host serial
+configuration). The "SmartReader+" mode is supported via the option "-m",
+e.g. "-m smartreader -s /dev/ttyUSB0". For compile time configuration use e.g.
+make DEFAULT_PORT='"/dev/ttyUSB0","smartreader",0,0,0'.
+
+In "SmartReader+" mode the cardreader clock frequency is automatically
+configured according to ISO 7816-3. It is still possible to override the
+clock frequency via the option "-C" for overclocking, however don't do this
+unless you know exactly what you're doing - you may possibly fry your
+smartcard!
+
+
+Cardserver client
+-----------------
+
+The cardclient is a client for several cardservers. Supported cardservers are :
+radegast, newcamd, camd33 (tcp), camd35 (udp), cardd, buffy and aroureos.
+
+You can configure as many clients for different servers as you want. The client
+configuration is read from the file "cardclient.conf". Every line in the file
+defines a client-server connection. The line starts with the client name and is
+followed by additional arguments which depend on the client type. See the file
+"examples/cardclient.conf.example" for format and arguments.
+
+The connections are tried in the order they are defined in the conf file until
+a valid decryption is obtained. After that, the decryption sticks to that
+particular connection until next channel switch.
+
+The network code supports dialup on demand. To use this you have to provide an
+external script to connect/disconnect your dialup link. Use commandline option
+-d to set the script name and enable the feature, e.g. "-d dialup.sh". See the
+example script "examples/dialup.sh.example". The network is brought up as soon
+as an server connection is requested. All server connections are disconnected if
+they are idle too long (normaly after 120 seconds). The network is brought down
+after the last connection has terminated and an additional network timeout has
+expired. The network timeout is configurable with the commandline option -t and
+defaults to 60 seconds, e.g. "-t 120".
+
+The current cardclient implementation is loosely based on the Streamboard
+client (contributed by S.Laurel from the Streamboard), which was included in
+earlier releases.
+
+
+
+Summary of commandline options
+------------------------------
+
+-B N      --budget=N     forces DVB device N to budget mode (using FFdecsa)
+-I        --inverse-cd   use inverse CD detection for the next serial device
+-R        --inverse-rst  use inverse RESET for the next serial device
+-C FREQ   --clock=FREQ   use FREQ as clock for the card reader on the next
+                         serial device (rather than 3.5712 MHz
+-s DEV    --serial=DEV   activate Phoenix ISO interface on serial device DEV
+                         (default: none)
+-d CMD    --dialup=CMD   call CMD to start/stop dialup-network
+                         (default: none)
+-t SECS   --timeout=SECS shutdown timeout for dialup-network
+                         (default: 60 secs)
+
+
+
+SVDR interface
+--------------
+
+The plugin implements a SVDR interface. Supported commands are:
+   RELOAD
+     Reload all configuration files (only if the softcam isn't active at the
+     moment).
+     Return codes: 550 - Softcam active, can't reload files.
+                   901 - Reloading files not entirely successfull. Most of the
+                         time this will leave you with an unusable softcam.
+                   900 - Reload successfull.
+
+   KEY string
+     Parse the given string and add the key to the key database (as if it was
+     received from EMM stream).
+     Return codes: 501 - Syntax error.
+                   900 - Key added.
+                   901 - Invalid key format or key already known.
+
+   LOG <on|off> module.option[,module.option][,...]
+     Enables or disables all given message classes.
+     Return codes: 501 - Syntax error or unknown message class.
+                   900 - Options set and saved.
+
+   LOGCFG
+     Display all available message classes and report their status. This can be
+     usefull if you want to provide an external GUI or whatever to handle the
+     message classes.
+     Return codes: 901 - No message classes available.
+                   900 - Message class status (multi line reply).
+
+   LOGFILE <on|off> [filename]
+     Enables or disables logging to file and optionaly sets the filename.",
+     Return codes: 501 - Syntax error.
+                   900 - Logfile option set and saved.
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/README.po2i18n b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/README.po2i18n
new file mode 100644 (file)
index 0000000..c690235
--- /dev/null
@@ -0,0 +1,44 @@
+        
+                  po2i18n - Converter for po files
+
+
+Written by:              Udo Richter <udo_richter@gmx.de>
+Project's homepage:      http://www.udo-richter.de/vdr/scripts.html#po2i18n
+                         http://www.udo-richter.de/vdr/scripts.en.html#po2i18n
+
+
+
+About
+--------------------------------------------------------------------------
+po2i18n is a perl script that generates an i18n.c file compatible to the i18n
+system of VDR 1.2.0 - VDR 1.5.6, based on the .po files of VDR 1.5.7. This
+allows plugins to transit to the translation system of VDR 1.5.7 while 
+maintaining compatibility to earlier versions. The script can be used manually
+or automatically as part of the Makefile.
+
+
+Use
+--------------------------------------------------------------------------
+po2i18n.pl is a filter and can be used manually like this:
+
+  ./po2i18n.pl < i18n-template.c > i18n.c
+  
+The filter reads all relevant ./po/*.po files and writes the i18n strings
+into the template file. Strings will be added between the following two lines:
+
+  // START I18N
+  // END I18N
+
+See also the sample i18n.h and i18n-template.c file. Note that the phrases data
+structure is encapsulated in #if VDRVERSNUM < 10507, so the i18n strings won't 
+be in the plugin file after 1.5.7. The call to RegisterI18n() of your plugin 
+should also be encapsulated like this.
+
+po2i18n can also generate the i18n.c file on the fly while compiling. The 
+changes to the Makefile are demonstrated by the included Makefile.diff sample.
+With these changes, the i18n.c file will be generated on VDR up to 1.5.6, and
+the whole gettext conversion is skipped. From 1.5.7 on, the i18n-template.c 
+file will be simply copied as a dummy, and the new locale system will run.
+
+As a drawback, the automatic .dependencies for i18n.c won't work.
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.c
new file mode 100644 (file)
index 0000000..6faa738
--- /dev/null
@@ -0,0 +1,2964 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <dlfcn.h>
+
+#include <linux/dvb/ca.h>
+#include <vdr/channels.h>
+#include <vdr/ci.h>
+#include <vdr/dvbdevice.h>
+#ifndef SASC
+#if APIVERSNUM >= 10500
+#include <vdr/dvbci.h>
+#endif
+#include <vdr/thread.h>
+
+#include "FFdecsa/FFdecsa.h"
+#endif //SASC
+
+#include "cam.h"
+#include "scsetup.h"
+#include "filter.h"
+#include "system.h"
+#include "data.h"
+#include "misc.h"
+#include "log-core.h"
+
+#define IDLE_SLEEP          0 // idleTime when sleeping
+#define IDLE_GETCA        200 // idleTime when waiting for ca descriptors
+#define IDLE_GETCA_SLOW 20000 // idleTime if no enc. system
+#define IDLE_NO_SYNC      800 // idleTime when not in sync
+#define IDLE_SYNC        2000 // idleTime when in sync
+
+#define CW_REPEAT_TIME   2000 // rewrite CW after X ms
+#define LOG_COUNT           3 // stop logging after X complete ECM cycles
+#define CHAIN_HOLD     120000 // min. time to hold a logger chain
+#define ECM_DATA_TIME    6000 // time to wait for ECM data updates
+#define ECM_UPD_TIME   120000 // delay between ECM data updates
+#define MAX_ECM_IDLE   300000 // delay before an idle handler can be removed
+#define MAX_ECM_HOLD    15000 // delay before an idle handler stops processing
+#define CAID_TIME      300000 // time between caid scans
+
+#define ECMCACHE_FILE "ecm.cache"
+
+#define L_HEX       2
+#define L_HEX_ECM   LCLASS(L_HEX,2)
+#define L_HEX_EMM   LCLASS(L_HEX,4)
+#define L_HEX_CAT   LCLASS(L_HEX,8)
+#define L_HEX_PMT   LCLASS(L_HEX,16)
+#define L_HEX_HOOK  LCLASS(L_HEX,32)
+#define L_HEX_ALL   LALL(L_HEX_HOOK)
+
+static const struct LogModule lm_hex = {
+  (LMOD_ENABLE|L_HEX_ALL)&LOPT_MASK,
+  (LMOD_ENABLE)&LOPT_MASK,
+  "hexdata",
+  { "ecm","emm","cat","pmt","hook" }
+  };
+ADD_MODULE(L_HEX,lm_hex)
+
+static const char *typeNames[] = { "typ0","typ1","VIDEO","typ3","AUDIO","typ5","DOLBY","typ6+" };
+#define TYPENAME(type) (typeNames[(type)<=7?(type):7])
+
+// -- cLogStats ---------------------------------------------------------------
+
+#define COUNTS  20
+#define SAMPLE (30*1000)
+#define AVR1   (60*1000)
+#define AVR2   (4*60*1000)
+#define AVR3   (10*60*1000)
+#define REPORT (60*1000)
+
+class cLogStats : public cThread {
+private:
+  cTimeMs sTime, repTime;
+  int sCount, sIdx, sCounts[COUNTS][2];
+protected:
+  virtual void Action(void);
+public:
+  cLogStats(void);
+  ~cLogStats();
+  void Count(void);
+  };
+
+static cMutex logstatsMutex;
+static cLogStats *logstats=0;
+
+void LogStatsUp(void)
+{
+  logstatsMutex.Lock();
+  if(LOG(L_CORE_AUSTATS) && !logstats) logstats=new cLogStats;
+  logstatsMutex.Unlock();
+}
+
+void LogStatsDown(void)
+{
+  logstatsMutex.Lock();
+  if(logstats) { delete logstats; logstats=0; }
+  logstatsMutex.Unlock();
+}
+
+cLogStats::cLogStats(void)
+{
+  sCount=sIdx=0;
+  for(int i=0; i<COUNTS; i++) { sCounts[i][0]=0; sCounts[i][1]=SAMPLE; }
+  SetDescription("logger stats");
+  Start();
+}
+
+cLogStats::~cLogStats()
+{
+  Cancel(2);
+}
+
+void cLogStats::Count(void)
+{
+  sCount++;
+}
+
+void cLogStats::Action(void)
+{
+  while(Running()) {
+    cCondWait::SleepMs(50);
+    if(sTime.Elapsed()>SAMPLE) {
+      sCounts[sIdx][0]=sCount;          sCount=0;
+      sCounts[sIdx][1]=sTime.Elapsed(); sTime.Set();
+      if(++sIdx >= COUNTS) sIdx=0;
+      }
+    if(repTime.Elapsed()>REPORT) {
+      repTime.Set();
+      if(sCounts[(sIdx+COUNTS-1)%COUNTS][0]>0) {
+        LBSTART(L_CORE_AUSTATS);
+        LBPUT("EMM packet load average (%d/%d/%dmin)",AVR1/60000,AVR2/60000,AVR3/60000);
+        int s=0, t=0;
+        for(int i=1; i<=COUNTS; i++) {
+          s+=sCounts[(sIdx+COUNTS-i)%COUNTS][0];
+          t+=sCounts[(sIdx+COUNTS-i)%COUNTS][1];
+          if(i==(AVR1/SAMPLE) || i==(AVR2/SAMPLE) || i==(AVR3/SAMPLE))
+            LBPUT(" %4d",(int)((float)s/(float)t*1000.0));
+          }
+        LBPUT(" pks/s");
+        LBEND();
+        }
+      }
+    }
+}
+
+// -- cHookManager -------------------------------------------------------------
+
+class cHookManager : public cAction {
+  int cardNum;
+  cSimpleList<cLogHook> hooks;
+  //
+  cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc);
+  void ClearHooks(void);
+  void DelHook(cLogHook *hook);
+protected:
+  virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+  cHookManager(int CardNum);
+  virtual ~cHookManager();
+  void AddHook(cLogHook *hook);
+  bool TriggerHook(int id);
+  void Down(void);
+  };
+
+cHookManager::cHookManager(int CardNum)
+:cAction("hookmanager",CardNum)
+{
+  cardNum=CardNum;
+  Priority(10);
+}
+
+cHookManager::~cHookManager()
+{
+  Down();
+}
+
+void cHookManager::Down(void)
+{
+  Lock();
+  while(cLogHook *hook=hooks.First()) DelHook(hook);
+  DelAllFilter();
+  Unlock();
+}
+
+bool cHookManager::TriggerHook(int id)
+{
+  Lock();
+  for(cLogHook *hook=hooks.First(); hook; hook=hooks.Next(hook))
+    if(hook->id==id) {
+      hook->delay.Set(CHAIN_HOLD);
+      Unlock();
+      return true;
+      }
+  Unlock();
+  return false;
+}
+
+void cHookManager::AddHook(cLogHook *hook)
+{
+  Lock();
+  PRINTF(L_CORE_HOOK,"%d: starting hook '%s' (%04x)",cardNum,hook->name,hook->id);
+  hook->delay.Set(CHAIN_HOLD);
+  hook->cardNum=cardNum;
+  hooks.Add(hook);
+  for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
+    cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false);
+    if(filter) {
+      filter->userData=(void *)hook;
+      pid->filter=filter;
+      }
+    }
+  Unlock();
+}
+
+void cHookManager::DelHook(cLogHook *hook)
+{
+  PRINTF(L_CORE_HOOK,"%d: stopping hook '%s' (%04x)",cardNum,hook->name,hook->id);
+  for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
+    cPidFilter *filter=pid->filter;
+    if(filter) {
+      DelFilter(filter);
+      pid->filter=0;
+      }
+    }
+  hooks.Del(hook);
+}
+
+cPidFilter *cHookManager::AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc)
+{
+  cPidFilter *filter=NewFilter(IdleTime);
+  if(filter) {
+    filter->SetBuffSize(32768);
+    filter->Start(Pid,Section,Mask,Mode,Crc);
+    PRINTF(L_CORE_HOOK,"%d: added filter pid=0x%.4x sct=0x%.2x/0x%.2x/0x%.2x idle=%d crc=%d",cardNum,Pid,Section,Mask,Mode,IdleTime,Crc);
+    }
+  else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for hookmanager %d",cardNum);
+  return filter;
+}
+
+void cHookManager::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+  if(data && len>0) {
+    HEXDUMP(L_HEX_HOOK,data,len,"HOOK pid 0x%04x",filter->Pid());
+    if(SCT_LEN(data)==len) {
+      cLogHook *hook=(cLogHook *)(filter->userData);
+      if(hook) {
+        hook->Process(filter->Pid(),data);
+        if(hook->bailOut || hook->delay.TimedOut()) DelHook(hook);
+        }
+      }
+    else PRINTF(L_CORE_HOOK,"%d: incomplete section %d != %d",cardNum,len,SCT_LEN(data));
+    }
+  else {
+    cLogHook *hook=(cLogHook *)(filter->userData);
+    if(hook && (hook->bailOut || hook->delay.TimedOut())) DelHook(hook);
+    }
+}
+
+// -- cLogChain ----------------------------------------------------------------
+
+class cLogChain : public cSimpleItem {
+public:
+  int cardNum, caid;
+  bool softCSA, active, delayed;
+  cTimeMs delay;
+  cPids pids;
+  cSimpleList<cSystem> systems;
+  //
+  cLogChain(int CardNum, bool soft);
+  void Process(int pid, unsigned char *data);
+  bool Parse(const unsigned char *cat);
+  };
+
+cLogChain::cLogChain(int CardNum, bool soft)
+{
+  cardNum=CardNum; softCSA=soft; active=delayed=false;
+}
+
+void cLogChain::Process(int pid, unsigned char *data)
+{
+  if(active) {
+    for(cSystem *sys=systems.First(); sys; sys=systems.Next(sys))
+      sys->ProcessEMM(pid,caid,data);
+    }
+}
+
+bool cLogChain::Parse(const unsigned char *cat)
+{
+  if(cat[0]==0x09) {
+    caid=WORD(cat,2,0xFFFF);
+    LBSTARTF(L_CORE_AU);
+    LBPUT("%d: chain caid %04x",cardNum,caid);
+    cSystem *sys;
+    if(systems.Count()>0) {
+      LBPUT(" ++");
+      for(sys=systems.First(); sys; sys=systems.Next(sys))
+        sys->ParseCAT(&pids,cat);
+      }
+    else {
+      LBPUT(" ->");
+      int Pri=0;
+      while((sys=cSystems::FindBySysId(caid,!softCSA,Pri))) {
+        Pri=sys->Pri();
+        if(sys->HasLogger()) {
+          sys->CardNum(cardNum);
+          sys->ParseCAT(&pids,cat);
+          systems.Add(sys);
+          LBPUT(" %s(%d)",sys->Name(),sys->Pri());
+          }
+        else
+          delete sys;
+        }
+      }
+    if(systems.Count()==0) LBPUT(" none available");
+    for(cPid *pid=pids.First(); pid; pid=pids.Next(pid))
+       LBPUT(" [%04x-%02x/%02x/%02x]",pid->pid,pid->sct,pid->mask,pid->mode);
+    LBEND();
+    if(systems.Count()>0 && pids.Count()>0)
+      return true;
+    }
+  return false;
+}
+
+// -- cLogger ------------------------------------------------------------------
+
+class cLogger : public cAction {
+private:
+  int cardNum;
+  bool softCSA, up;
+  cSimpleList<cLogChain> chains;
+  cSimpleList<cEcmInfo> active;
+  //
+  cPidFilter *catfilt;
+  int catVers;
+  //
+  enum ePreMode { pmNone, pmStart, pmWait, pmActive, pmStop };
+  ePreMode prescan;
+  cTimeMs pretime;
+  //
+  cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc);
+  void SetChains(void);
+  void ClearChains(void);
+  void StartChain(cLogChain *chain);
+  void StopChain(cLogChain *chain, bool force);
+protected:
+  virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+  cLogger(int CardNum, bool soft);
+  virtual ~cLogger();
+  void EcmStatus(const cEcmInfo *ecm, bool on);
+  void Up(void);
+  void Down(void);
+  void PreScan(void);
+  };
+
+cLogger::cLogger(int CardNum, bool soft)
+:cAction("logger",CardNum)
+{
+  cardNum=CardNum; softCSA=soft;
+  catfilt=0; up=false; prescan=pmNone;
+  Priority(10);
+}
+
+cLogger::~cLogger()
+{
+  Down();
+}
+
+void cLogger::Up(void)
+{
+  Lock();
+  if(!up) {
+    PRINTF(L_CORE_AUEXTRA,"%d: UP",cardNum);
+    catVers=-1;
+    catfilt=AddFilter(1,0x01,0xFF,0,0,true);
+    up=true;
+    }
+  Unlock();
+}
+
+void cLogger::Down(void)
+{
+  Lock();
+  if(up) {
+    PRINTF(L_CORE_AUEXTRA,"%d: DOWN",cardNum);
+    ClearChains();
+    DelAllFilter();
+    catfilt=0; up=false; prescan=pmNone;
+    }
+  Unlock();
+}
+
+void cLogger::PreScan(void)
+{
+  Lock();
+  prescan=pmStart; Up();
+  Unlock();
+}
+
+void cLogger::EcmStatus(const cEcmInfo *ecm, bool on)
+{
+  Lock();
+  PRINTF(L_CORE_AUEXTRA,"%d: ecm prgid=%d caid=%04x prov=%.4x %s",cardNum,ecm->prgId,ecm->caId,ecm->provId,on ? "active":"inactive");
+  cEcmInfo *e;
+  if(on) {
+    e=new cEcmInfo(ecm);
+    active.Add(e);
+    if(!up) Up();
+    }
+  else {
+    for(e=active.First(); e; e=active.Next(e))
+      if(e->Compare(ecm)) {
+        active.Del(e);
+        break;
+        }
+    }
+  if(prescan>=pmWait) prescan=pmStop;
+  SetChains();
+  prescan=pmNone;
+  Unlock();
+}
+
+void cLogger::SetChains(void)
+{
+  for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) {
+    bool act=false;
+    if(ScSetup.AutoUpdate>1 || prescan==pmActive) act=true;
+    else if(ScSetup.AutoUpdate==1) {
+      for(cEcmInfo *e=active.First(); e; e=active.Next(e))
+        if((e->emmCaId && chain->caid==e->emmCaId) || chain->caid==e->caId) {
+          act=true; break;
+          }
+       }
+    if(act) StartChain(chain);
+    else StopChain(chain,prescan==pmStop);
+    }
+}
+
+void cLogger::ClearChains(void)
+{
+  for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain))
+    StopChain(chain,true);
+  chains.Clear();
+}
+
+void cLogger::StartChain(cLogChain *chain)
+{
+  if(chain->delayed)
+    PRINTF(L_CORE_AUEXTRA,"%d: restarting delayed chain %04x",cardNum,chain->caid);
+  chain->delayed=false;
+  if(!chain->active) {
+    PRINTF(L_CORE_AU,"%d: starting chain %04x",cardNum,chain->caid);
+    chain->active=true;
+    for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
+      cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false);
+      if(filter) {
+        filter->userData=(void *)chain;
+        pid->filter=filter;
+        }
+      }
+    }
+}
+
+void cLogger::StopChain(cLogChain *chain, bool force)
+{
+  if(chain->active) {
+    if(force || (chain->delayed && chain->delay.TimedOut())) {
+      PRINTF(L_CORE_AU,"%d: stopping chain %04x",cardNum,chain->caid);
+      chain->active=false;
+      for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
+        cPidFilter *filter=pid->filter;
+        if(filter) {
+          DelFilter(filter);
+          pid->filter=0;
+          }
+        }
+      }
+    else if(!chain->delayed) {
+      PRINTF(L_CORE_AUEXTRA,"%d: delaying chain %04x",cardNum,chain->caid);
+      chain->delayed=true;
+      chain->delay.Set(CHAIN_HOLD);
+      }
+    }
+}
+
+cPidFilter *cLogger::AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc)
+{
+  cPidFilter *filter=NewFilter(IdleTime);
+  if(filter) {
+    if(Pid>1) filter->SetBuffSize(32768);
+    filter->Start(Pid,Section,Mask,Mode,Crc);
+    PRINTF(L_CORE_AUEXTRA,"%d: added filter pid=0x%.4x sct=0x%.2x/0x%.2x/0x%.2x idle=%d crc=%d",cardNum,Pid,Section,Mask,Mode,IdleTime,Crc);
+    }
+  else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for logger %d",cardNum);
+  return filter;
+}
+
+void cLogger::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+  if(data && len>0) {
+    if(filter==catfilt) {
+      int vers=(data[5]&0x3E)>>1;
+      if(data[0]==0x01 && vers!=catVers) {
+        PRINTF(L_CORE_AUEXTRA,"%d: got CAT version %02x",cardNum,vers);
+        catVers=vers;
+        HEXDUMP(L_HEX_CAT,data,len,"CAT vers %02x",catVers);
+        ClearChains();
+        for(int i=8; i<len-4; i+=data[i+1]+2) {
+          if(data[i]==0x09) {
+            int caid=WORD(data,i+2,0xFFFF);
+            cLogChain *chain;
+            for(chain=chains.First(); chain; chain=chains.Next(chain))
+              if(chain->caid==caid) break;
+            if(chain)
+              chain->Parse(&data[i]);
+            else {
+              chain=new cLogChain(cardNum,softCSA);
+              if(chain->Parse(&data[i]))
+                chains.Add(chain);
+              else
+                delete chain;
+              }
+            }
+          }
+        SetChains();
+        if(prescan==pmStart) { prescan=pmWait; pretime.Set(2000); }
+        }
+      if(prescan==pmWait && pretime.TimedOut()) { prescan=pmActive; SetChains(); }
+      }
+    else {
+      HEXDUMP(L_HEX_EMM,data,len,"EMM pid 0x%04x",filter->Pid());
+      if(logstats) logstats->Count();
+      if(SCT_LEN(data)==len) {
+        cLogChain *chain=(cLogChain *)(filter->userData);
+        if(chain) {
+          chain->Process(filter->Pid(),data);
+          if(chain->delayed) StopChain(chain,false);
+          }
+        }
+      else PRINTF(L_CORE_AU,"%d: incomplete section %d != %d",cardNum,len,SCT_LEN(data));
+      }
+    }
+  else {
+    cLogChain *chain=(cLogChain *)(filter->userData);
+    if(chain && chain->delayed) StopChain(chain,false);
+    }
+}
+
+// -- cEcmData -----------------------------------------------------------------
+
+class cEcmData : public cEcmInfo {
+public:
+  cEcmData(void):cEcmInfo() {}
+  cEcmData(cEcmInfo *e):cEcmInfo(e) {}
+  virtual cString ToString(bool hide);
+  bool Parse(const char *buf);
+  };
+
+bool cEcmData::Parse(const char *buf)
+{
+  char Name[64];
+  int nu=0, num;
+  Name[0]=0;
+  if(sscanf(buf,"%d:%x:%x:%63[^:]:%x/%x:%x:%x/%x:%d%n",&prgId,&source,&transponder,Name,&caId,&emmCaId,&provId,&ecm_pid,&ecm_table,&nu,&num)>=9) {
+    SetName(Name);
+    const char *line=buf+num;
+    if(nu>0 && *line++==':') {
+      unsigned char *dat=AUTOMEM(nu);
+      if(GetHex(line,dat,nu,true)==nu) AddData(dat,nu);
+      }
+    return true;
+    }
+  return false;
+}
+
+cString cEcmData::ToString(bool hide)
+{
+  char *str;
+  if(data) {
+    str=AUTOARRAY(char,dataLen*2+2);
+    sprintf(str,":%d:%s",dataLen,HexStr(str,data,dataLen));
+    }
+  else {
+    str=AUTOARRAY(char,4);
+    strcpy(str,":0");
+    }
+  return cString::sprintf("%d:%x:%x:%s:%x/%x:%x:%x/%x%s",
+                            prgId,source,transponder,name,
+                            caId,emmCaId,provId,ecm_pid,ecm_table,
+                            str);
+}
+
+// -- cEcmCache ----------------------------------------------------------------
+
+cEcmCache ecmcache;
+
+cEcmCache::cEcmCache(void)
+:cStructListPlain<cEcmData>("ecm cache",ECMCACHE_FILE,SL_READWRITE|SL_MISSINGOK)
+{}
+
+void cEcmCache::New(cEcmInfo *e)
+{
+  ListLock(true);
+  cEcmData *dat;
+  if(!(dat=Exists(e))) {
+    dat=new cEcmData(e);
+    Add(dat);
+    Modified();
+    PRINTF(L_CORE_ECM,"cache add prgId=%d source=%x transponder=%x ecm=%x/%x",e->prgId,e->source,e->transponder,e->ecm_pid,e->ecm_table);
+    }
+  else {
+    if(strcasecmp(e->name,dat->name)) {
+      dat->SetName(e->name);
+      Modified();
+      }
+    if(dat->Update(e))
+      Modified();
+    }
+  ListUnlock();
+  e->SetCached();
+}
+
+cEcmData *cEcmCache::Exists(cEcmInfo *e)
+{
+  cEcmData *dat;
+  for(dat=First(); dat; dat=Next(dat))
+    if(dat->Compare(e)) break;
+  return dat;
+}
+
+int cEcmCache::GetCached(cSimpleList<cEcmInfo> *list, int sid, int Source, int Transponder)
+{
+  int n=0;
+  list->Clear();
+  ListLock(false);
+  for(cEcmData *dat=First(); dat; dat=Next(dat)) {
+    if(dat->prgId==sid && dat->source==Source && dat->transponder==Transponder) {
+      cEcmInfo *e=new cEcmInfo(dat);
+      if(e) {
+        PRINTF(L_CORE_ECM,"from cache: system %s (%04x) id %04x with ecm %x/%x",e->name,e->caId,e->provId,e->ecm_pid,e->ecm_table);
+        e->SetCached();
+        list->Add(e);
+        n++;
+        }
+      }
+    }
+  ListUnlock();
+  return n;
+}
+
+void cEcmCache::Delete(cEcmInfo *e)
+{
+  ListLock(false);
+  cEcmData *dat=Exists(e);
+  ListUnlock();
+  if(dat) {
+    DelItem(dat);
+    PRINTF(L_CORE_ECM,"invalidated cached prgId=%d source=%x transponder=%x ecm=%x/%x",dat->prgId,dat->source,dat->transponder,dat->ecm_pid,dat->ecm_table);
+    }
+}
+
+void cEcmCache::Flush(void)
+{
+  ListLock(true);
+  Clear();
+  Modified();
+  PRINTF(L_CORE_ECM,"cache flushed");
+  ListUnlock();
+}
+
+bool cEcmCache::ParseLinePlain(const char *line)
+{
+  cEcmData *dat=new cEcmData;
+  if(dat && dat->Parse(line) && !Exists(dat)) { Add(dat); return true; }
+  delete dat;
+  return false;
+}
+
+// -- cEcmSys ------------------------------------------------------------------
+
+class cEcmPri : public cSimpleItem {
+public:
+  cEcmInfo *ecm;
+  int pri, sysIdent;
+  };
+
+// -- cEcmHandler --------------------------------------------------------------
+
+class cEcmHandler : public cSimpleItem, public cAction {
+private:
+  int cardNum, cwIndex;
+  cCam *cam;
+  char *id;
+  cTimeMs idleTime;
+  //
+  cMutex dataMutex;
+  int sid;
+  cSimpleList<cPrgPid> pids;
+  //
+  cSystem *sys;
+  cPidFilter *filter;
+  int filterCwIndex, filterSource, filterTransponder, filterSid;
+  unsigned char lastCw[16];
+  bool sync, noKey, trigger;
+  int triggerMode;
+  int mode, count;
+  cTimeMs lastsync, startecm, resendTime;
+  unsigned int cryptPeriod;
+  unsigned char parity;
+  cMsgCache failed;
+  //
+  cSimpleList<cEcmInfo> ecmList;
+  cSimpleList<cEcmPri> ecmPriList;
+  cEcmInfo *ecm;
+  cEcmPri *ecmPri;
+  cTimeMs ecmUpdTime;
+  //
+  int dolog;
+  //
+  void DeleteSys(void);
+  void NoSync(bool clearParity);
+  cEcmInfo *NewEcm(void);
+  cEcmInfo *JumpEcm(void);
+  void StopEcm(void);
+  bool UpdateEcm(void);
+  void EcmOk(void);
+  void EcmFail(void);
+  void ParseCAInfo(int sys, int source=0);
+  void AddEcmPri(cEcmInfo *n);
+protected:
+  virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+  cEcmHandler(cCam *Cam, int CardNum, int cwindex);
+  virtual ~cEcmHandler();
+  void Stop(void);
+  void SetPrg(cPrg *prg);
+  void ShiftCwIndex(int cwindex);
+  char *CurrentKeyStr(void) const;
+  bool IsRemoveable(void);
+  bool IsIdle(void);
+  int Sid(void) const { return sid; }
+  int CwIndex(void) const { return cwIndex; }
+  const char *Id(void) const { return id; }
+  };
+
+cEcmHandler::cEcmHandler(cCam *Cam, int CardNum, int cwindex)
+:cAction("ecmhandler",CardNum)
+,failed(32,0)
+{
+  cam=Cam;
+  cardNum=CardNum;
+  cwIndex=cwindex;
+  sys=0; filter=0; ecm=0; ecmPri=0; mode=-1; sid=-1;
+  trigger=false; triggerMode=-1;
+  filterSource=filterTransponder=0; filterCwIndex=-1; filterSid=-1;
+  asprintf(&id,"%d.%d",cardNum,cwindex);
+}
+
+cEcmHandler::~cEcmHandler()
+{
+  Lock();
+  StopEcm();
+  DelAllFilter(); // delete filters before sys for multi-threading reasons
+  DeleteSys();
+  Unlock();
+  free(id);
+}
+
+bool cEcmHandler::IsIdle(void)
+{
+  dataMutex.Lock();
+  int n=pids.Count();
+  dataMutex.Unlock();
+  return n==0;
+}
+
+bool cEcmHandler::IsRemoveable(void)
+{
+  return IsIdle() && idleTime.Elapsed()>MAX_ECM_IDLE;
+}
+
+void cEcmHandler::Stop(void)
+{
+  dataMutex.Lock();
+  if(!IsIdle() || sid!=-1) {
+    PRINTF(L_CORE_ECM,"%s: stop",id);
+    sid=-1;
+    idleTime.Set();
+    pids.Clear();
+    trigger=true;
+    }
+  dataMutex.Unlock();
+  if(filter) filter->Wakeup();
+}
+
+void cEcmHandler::ShiftCwIndex(int cwindex)
+{
+  if(cwIndex!=cwindex) {
+    PRINTF(L_CORE_PIDS,"%s: shifting cwIndex from %d to %d",id,cwIndex,cwindex);
+    free(id);
+    asprintf(&id,"%d.%d",cardNum,cwindex);
+    dataMutex.Lock();
+    trigger=true;
+    cwIndex=cwindex;
+    for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+      cam->SetCWIndex(pid->Pid(),cwIndex);
+    dataMutex.Unlock();
+    if(filter) filter->Wakeup();
+    }
+}
+
+void cEcmHandler::SetPrg(cPrg *prg)
+{
+  dataMutex.Lock();
+  bool wasIdle=IsIdle();
+  if(prg->Prg()!=sid) {
+    PRINTF(L_CORE_ECM,"%s: setting new SID %d",id,prg->Prg());
+    sid=prg->Prg();
+    idleTime.Set();
+    pids.Clear();
+    trigger=true;
+    }
+  LBSTART(L_CORE_PIDS);
+  LBPUT("%s: pids on entry",id);
+  for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+    LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+  LBEND();
+
+  for(cPrgPid *pid=pids.First(); pid;) {
+    cPrgPid *npid;
+    for(npid=prg->pids.First(); npid; npid=prg->pids.Next(npid)) {
+      if(pid->Pid()==npid->Pid()) {
+        npid->Proc(true);
+        break;
+        }
+      }
+    if(!npid) {
+      npid=pids.Next(pid);
+      pids.Del(pid);
+      pid=npid;
+      }
+    else pid=pids.Next(pid);
+    }
+  LBSTART(L_CORE_PIDS);
+  LBPUT("%s: pids after delete",id);
+  for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+    LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+  LBEND();
+  for(cPrgPid *npid=prg->pids.First(); npid; npid=prg->pids.Next(npid)) {
+    if(!npid->Proc()) {
+      cPrgPid *pid=new cPrgPid(npid->Type(),npid->Pid());
+      pids.Add(pid);
+      cam->SetCWIndex(pid->Pid(),cwIndex);
+      }
+    }
+  LBSTART(L_CORE_PIDS);
+  LBPUT("%s: pids after add",id);
+  for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+    LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+  LBEND();
+  if(!IsIdle()) {
+    trigger=true;
+    triggerMode=0;
+    if(wasIdle) PRINTF(L_CORE_ECM,"%s: is no longer idle",id);
+    }
+  else {
+    if(!wasIdle) idleTime.Set();
+    PRINTF(L_CORE_ECM,"%s: is idle%s",id,wasIdle?"":" now");
+    }
+
+  if(!filter) {
+    filter=NewFilter(IDLE_SLEEP);
+    if(!filter) PRINTF(L_GEN_ERROR,"failed to open ECM filter in handler %s",id);
+    }
+  dataMutex.Unlock();
+  if(filter) filter->Wakeup();
+}
+
+void cEcmHandler::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+  dataMutex.Lock();
+  if(trigger) {
+    PRINTF(L_CORE_ECM,"%s: triggered SID %d/%d idx %d/%d mode %d/%d %s",
+      id,filterSid,sid,filterCwIndex,cwIndex,mode,triggerMode,(mode==3 && sync)?"sync":"-");
+    trigger=false;
+    if(filterSid!=sid) {
+      filterSid=sid;
+      filterSource=cam->Source();
+      filterTransponder=cam->Transponder();
+      filterCwIndex=cwIndex;
+      noKey=true; mode=0;
+      }
+    else {
+      if(filterCwIndex!=cwIndex) {
+        filterCwIndex=cwIndex;
+        if(mode==3 && sync)
+          cam->WriteCW(filterCwIndex,lastCw,true);
+        }
+      if(mode<triggerMode) mode=triggerMode;
+      }
+    triggerMode=-1;
+    }
+  dataMutex.Unlock();
+
+  switch(mode) {
+    case -1:
+      filter->SetIdleTime(IDLE_SLEEP);
+      break;
+
+    case 0:
+      StopEcm();
+      if(IsIdle()) { mode=-1; break; }
+
+      dolog=LOG_COUNT;
+      NewEcm();
+      filter->SetIdleTime(IDLE_GETCA);
+      startecm.Set();
+      mode=1;
+      break;
+
+    case 1:
+      if(!ecm && !JumpEcm()) {
+        if(startecm.Elapsed()>IDLE_GETCA_SLOW) {
+          if(IsIdle()) { mode=0; break; }
+          PRINTF(L_CORE_ECM,"%s: no encryption system found",id);
+          filter->SetIdleTime(IDLE_GETCA_SLOW/4);
+          startecm.Set();
+          }
+        break;
+        }
+      mode=4;
+      // fall through
+
+    case 4:
+    case 5:
+      NoSync(mode==4);
+      failed.Clear();
+      filter->SetIdleTime(IDLE_NO_SYNC/2);
+      lastsync.Set();
+      cryptPeriod=20*1000;
+      mode=2;
+      // fall through
+          
+    case 2:
+      if(sys->NeedsData()) {
+        if(!UpdateEcm()) {
+          if(lastsync.Elapsed()<ECM_DATA_TIME) break;
+          PRINTF(L_CORE_ECM,"%s: no ecm extra data update (waited %d ms)",id,(int)lastsync.Elapsed());
+          }
+        if(lastsync.Elapsed()>IDLE_NO_SYNC/4 && dolog)
+          PRINTF(L_CORE_ECM,"%s: ecm extra data update took %d ms",id,(int)lastsync.Elapsed());
+        }
+      filter->SetIdleTime(IDLE_NO_SYNC);
+      mode=3;
+      // fall through
+
+    case 3:
+      {
+      bool resend=false, cwok=false;
+      if(resendTime.TimedOut()) {
+        resend=sync; resendTime.Set(8*24*60*60*1000);
+        }
+
+      if(startecm.Elapsed()<3*60*1000) cam->DumpAV7110();
+
+      if(data && len>0) {
+        HEXDUMP(L_HEX_ECM,data,len,"ECM sys 0x%04x id 0x%02x pid 0x%04x",ecm->caId,ecm->provId,filter->Pid());
+        if(SCT_LEN(data)==len) {
+          LDUMP(L_CORE_ECMPROC,data,16,"%s: ECM",id);
+          int n;
+          if(!(n=sys->CheckECM(ecm,data,sync))) {
+            if(resend || parity!=(data[0]&1)) {
+              int ecmid;
+              cTimeMs procTime;
+              cwok=(ecmid=failed.Get(data,len,0))>=0 && sys->ProcessECM(ecm,data);
+              n=(ecmid>0)?failed.Cache(ecmid,cwok,0):99;
+              sys->CheckECMResult(ecm,data,cwok);
+              if(cwok) {
+                parity=data[0]&1;
+                }
+              else {
+                if(procTime.Elapsed()>6000) {
+                  PRINTF(L_CORE_ECM,"%s: filter flush (elapsed %d)",id,(int)procTime.Elapsed());
+                  filter->Flush();
+                  }
+                if(n>=2) { count++; if(n==2) count++; }
+                parity=0xFF;
+                if(sync && lastsync.Elapsed()>cryptPeriod*2) {
+                  PRINTF(L_CORE_ECM,"%s: lost sync (period %d, elapsed %d)",id,cryptPeriod,(int)lastsync.Elapsed());
+                  NoSync(true);
+                  }
+                }
+              PRINTF(L_CORE_ECMPROC,"%s: (%s) cwok=%d ecmid=%d n=%d sync=%d parity=%d count=%d ELA=%d",
+                    id,sys->Name(),cwok,ecmid,n,sync,parity,count,(int)procTime.Elapsed());
+              }
+            }
+          else {
+            PRINTF(L_CORE_ECMPROC,"%s: check result %d\n",id,n);
+            switch(n) {
+              case 1: NoSync(true); break;
+              case 2: count++; break;
+              case 3: break;
+              }
+            }
+          }
+        else {
+          PRINTF(L_CORE_ECM,"%s: incomplete section %d != %d",id,len,SCT_LEN(data));
+          count++;
+          }
+        }
+      else if(sys->Constant()) {
+        if(sys->ProcessECM(ecm,NULL)) {
+          cwok=true;
+          if(sync) filter->SetIdleTime(IDLE_SYNC*10); 
+          }
+        else count++;
+        }
+      else count++;
+
+      if(cwok) {
+        dolog=LOG_COUNT; sys->DoLog(true);
+        cam->WriteCW(filterCwIndex,sys->CW(),resend || !sync);
+        memcpy(lastCw,sys->CW(),sizeof(lastCw));
+        noKey=false; count=0;
+        UpdateEcm(); EcmOk();
+        if(!sync) {
+          sync=true;
+          filter->SetIdleTime(IDLE_SYNC);
+          PRINTF(L_CORE_ECM,"%s: correct key found",id);
+          if(!cam->IsSoftCSA())
+            resendTime.Set(CW_REPEAT_TIME);
+          }
+        else if(!resend)
+          cryptPeriod=max(5000,min(60000,(int)lastsync.Elapsed()));
+        lastsync.Set();
+        }
+
+      if(!sync && !trigger) {
+        if(count>=sys->MaxEcmTry()) {
+          EcmFail(); JumpEcm();
+          mode=4;
+          if(!ecm) {
+            JumpEcm();
+            if(!ecm) { // this should not happen!
+              PRINTF(L_GEN_DEBUG,"internal: handler %s, empty ecm list in sync loop",id);
+              mode=0; break;
+              }
+            // if we looped through all systems, we wait until the next parity
+            // change before we try again.
+            if(dolog!=LOG_COUNT && data) { parity=data[0]&1; mode=5; }
+            if(dolog && !--dolog) {
+              sys->DoLog(false);
+              PRINTF(L_CORE_ECM,"%s: stopping message log until valid key is found",id);
+              }
+            }
+          break;
+          }
+        }
+        
+      if(IsIdle() && idleTime.Elapsed()>MAX_ECM_HOLD) {
+        PRINTF(L_CORE_ECM,"%s: hold timeout expired",id);
+        mode=0;
+        }
+
+      break;
+      }
+    }
+}
+
+void cEcmHandler::NoSync(bool clearParity)
+{
+  if(clearParity) parity=0xFF;
+  count=0; sync=false;
+}
+
+void cEcmHandler::DeleteSys(void)
+{
+  delete sys; sys=0;
+}
+
+char *cEcmHandler::CurrentKeyStr(void) const
+{
+  if(noKey || !sys) return 0;
+  return strdup(sys->CurrentKeyStr());
+}
+
+cEcmInfo *cEcmHandler::NewEcm(void)
+{
+  ecmcache.GetCached(&ecmList,filterSid,filterSource,filterTransponder);
+  ecmPriList.Clear();
+  for(cEcmInfo *n=ecmList.First(); n; n=ecmList.Next(n)) AddEcmPri(n);
+  ecm=0; ecmPri=0;
+  return JumpEcm();
+}
+
+void cEcmHandler::AddEcmPri(cEcmInfo *n)
+{
+  int ident, pri=0;
+  while((ident=cSystems::FindIdentBySysId(n->caId,!cam->IsSoftCSA(),pri))>0) {
+    cEcmPri *ep=new cEcmPri;
+    if(ep) {
+      ep->ecm=n; 
+      ep->pri=pri;
+      ep->sysIdent=ident;
+      if(n->Cached() && (!ScSetup.LocalPriority || pri!=-15)) ep->pri+=20;
+
+      // keep ecmPriList sorted
+      cEcmPri *eppp, *epp=ecmPriList.First();
+      if(!epp || epp->pri<ep->pri)
+        ecmPriList.Ins(ep);
+      else {
+        do {
+          eppp=ecmPriList.Next(epp);
+          if(!eppp || eppp->pri<ep->pri) {
+            ecmPriList.Add(ep,epp);
+            break;
+            }
+          } while((epp=eppp));
+        }
+      }
+    }
+}
+
+void cEcmHandler::StopEcm(void)
+{
+  filter->Stop(); filter->Flush();
+  if(ecm) cam->LogEcmStatus(ecm,false);
+  DeleteSys();
+}
+
+bool cEcmHandler::UpdateEcm(void)
+{
+  if(!ecm->Data() || ecmUpdTime.TimedOut()) {
+    bool log=dolog;
+    dolog=(sys && sys->NeedsData() && ecm->Data()==0);
+    if(dolog) PRINTF(L_CORE_ECM,"%s: try to update ecm extra data",id);
+    ParseCAInfo(ecm->caId, ecm->source);
+    ecmUpdTime.Set(ECM_UPD_TIME);
+    dolog=log;
+    if(!ecm->Data()) return false;
+    }
+  return true;
+}
+
+cEcmInfo *cEcmHandler::JumpEcm(void)
+{
+  noKey=true;
+  if(!ecmPri) {
+    ParseCAInfo(0xFFFF); // all systems
+    ecmPri=ecmPriList.First();
+    }
+  else ecmPri=ecmPriList.Next(ecmPri);
+  if(ecmPri) {
+    if(ecmPri->ecm!=ecm) {
+      StopEcm(); ecmUpdTime.Set();
+      ecm=ecmPri->ecm;
+      filter->Start(ecm->ecm_pid,ecm->ecm_table,0xfe,0,false);
+      cam->LogEcmStatus(ecm,true);
+      }
+    else {
+      DeleteSys();
+      filter->Flush();
+      }
+    sys=cSystems::FindBySysIdent(ecmPri->sysIdent);
+    if(!sys) {
+      if(dolog) PRINTF(L_GEN_DEBUG,"internal: handler %s, no system found for ident %04x (caid %04x pri %d)",id,ecmPri->sysIdent,ecmPri->ecm->caId,ecmPri->pri);
+      return JumpEcm();
+      }
+    sys->DoLog(dolog!=0); sys->CardNum(cardNum);
+    failed.SetMaxFail(sys->MaxEcmTry());
+
+    if(dolog) PRINTF(L_CORE_ECM,"%s: try system %s (%04x) id %04x with ecm %x%s (pri=%d)",
+                     id,sys->Name(),ecm->caId,ecm->provId,ecm->ecm_pid,ecm->Cached()?" (cached)":"",sys->Pri());
+    }
+  else {
+    StopEcm();
+    ecm=0;
+    }
+  return ecm;
+}
+
+void cEcmHandler::EcmOk(void)
+{
+  ecm->SetName(sys->Name());
+  ecm->Fail(false);
+  ecmcache.New(ecm);
+  cEcmInfo *e=ecmList.First();
+  while(e) {
+    if(e->Cached() && e->Failed()) ecmcache.Delete(e);
+    e=ecmList.Next(e);
+    }
+}
+
+void cEcmHandler::EcmFail(void)
+{
+  ecm->Fail(true);
+}
+
+void cEcmHandler::ParseCAInfo(int SysId, int Source)
+{
+  unsigned char buff[2048];
+  caid_t casys[MAXCAIDS+1];
+  if(SysId==0xFFFF) {
+    if(!cam->GetPrgCaids(filterSource,filterTransponder,filterSid,casys)) {
+      PRINTF(L_CORE_ECM,"%s: no CAIDs for SID %d",id,sid);
+      return;
+      }
+    }
+  else {
+    casys[0]=SysId;
+    casys[1]=0;
+    }
+  bool streamFlag;
+  int len=GetCaDescriptors(filterSource,filterTransponder,filterSid,casys,sizeof(buff),buff,streamFlag);
+  if(len>0) {
+    if(dolog) PRINTF(L_CORE_ECM,"%s: got CaDescriptors for SID %d (len=%d)",id,sid,len);
+    HEXDUMP(L_HEX_PMT,buff,len,"PMT");
+    for(int index=0; index<len; index+=buff[index+1]+2) {
+      if(buff[index]==0x09) {
+        if(dolog) LDUMP(L_CORE_ECM,&buff[index+2],buff[index+1],"%s: descriptor",id);
+        int sysId=WORD(buff,index+2,0xFFFF);
+        int sysPri=0;
+        cSystem *sys;
+        while((sys=cSystems::FindBySysId(sysId,!cam->IsSoftCSA(),sysPri))) {
+          sysPri=sys->Pri();
+          cSimpleList<cEcmInfo> ecms;
+          sys->ParseCADescriptor(&ecms,sysId,Source,&buff[index+2],buff[index+1]);
+          delete sys;
+          if(ecms.Count()) {
+            cEcmInfo *n;
+            while((n=ecms.First())) {
+              ecms.Del(n,false);
+              LBSTARTF(L_CORE_ECM);
+              if(dolog) LBPUT("%s: found %04x (%s) id %04x with ecm %x ",id,n->caId,n->name,n->provId,n->ecm_pid);
+              cEcmInfo *e=ecmList.First();
+              while(e) {
+                if(e->ecm_pid==n->ecm_pid) {
+                  if(e->caId==n->caId && e->provId==n->provId) {
+                    if(n->Data()) {
+                      if(e->Update(n) && dolog) LBPUT("(updated) ");
+                      }
+                    if(dolog) LBPUT("(already present)");
+                    delete n; n=0;
+                    break;
+                    }
+                  else {
+                    e->Fail(true);
+                    if(dolog) LBPUT("(dup) ");
+                    }
+                  }
+                e=ecmList.Next(e);
+                }
+              if(n) {
+                if(dolog) LBPUT("(new)");
+                n->SetSource(sid,filterSource,filterTransponder);
+                ecmList.Add(n);
+                AddEcmPri(n);
+                }
+              LBEND();
+              }
+            break;
+            }
+          }
+        if(sysPri==0 && dolog) PRINTF(L_CORE_ECM,"%s: no module available for system %04x",id,sysId);
+        }
+      }
+    }
+  else if(len<0)
+    PRINTF(L_CORE_ECM,"%s: CA parse buffer overflow",id);
+  if(SysId==0xFFFF) {
+    for(cEcmPri *ep=ecmPriList.First(); ep; ep=ecmPriList.Next(ep))
+      PRINTF(L_CORE_ECMPROC,"%s: ecmPriList pri=%d ident=%04x caid=%04x pid=%04x",id,ep->pri,ep->sysIdent,ep->ecm->caId,ep->ecm->ecm_pid);
+    PRINTF(L_CORE_ECMPROC,"%s: ecmPri list end",id);
+    }
+}
+
+// -- cCam ---------------------------------------------------------------
+
+cCam::cCam(cScDvbDevice *dev, int CardNum)
+{
+  device=dev; cardNum=CardNum;
+  source=transponder=-1; liveVpid=liveApid=0; logger=0; hookman=0;
+  memset(lastCW,0,sizeof(lastCW));
+  memset(indexMap,0,sizeof(indexMap));
+}
+
+cCam::~cCam()
+{
+  handlerList.Clear();
+  delete hookman;
+  delete logger;
+}
+
+bool cCam::IsSoftCSA(void)
+{
+  return device->SoftCSA();
+}
+
+void cCam::Tune(const cChannel *channel)
+{
+  cMutexLock lock(this);
+  if(source!=channel->Source() || transponder!=channel->Transponder()) {
+    source=channel->Source(); transponder=channel->Transponder();
+    PRINTF(L_CORE_PIDS,"%d: now tuned to source %x transponder %x",cardNum,source,transponder);
+    Stop();
+    }
+  else PRINTF(L_CORE_PIDS,"%d: tune to same source/transponder",cardNum);
+}
+
+void cCam::PostTune(void)
+{
+  if(ScSetup.PrestartAU) {
+    LogStartup();
+    if(logger) logger->PreScan();
+    }
+}
+
+void cCam::SetPid(int type, int pid, bool on)
+{
+  cMutexLock lock(this);
+  int oldA=liveApid, oldV=liveVpid;
+  if(type==1) liveVpid=on ? pid:0;
+  else if(type==0) liveApid=on ? pid:0;
+  else if(liveVpid==pid && on) liveVpid=0;
+  else if(liveApid==pid && on) liveApid=0;
+  if(oldA!=liveApid || oldV!=liveVpid)
+    PRINTF(L_CORE_PIDS,"%d: livepids video=%04x audio=%04x",cardNum,liveVpid,liveApid);
+}
+
+void cCam::Stop(void)
+{
+  cMutexLock lock(this);
+  for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+    handler->Stop();
+  if(logger) logger->Down();
+  if(hookman) hookman->Down();
+}
+
+void cCam::AddPrg(cPrg *prg)
+{
+  cMutexLock lock(this);
+  bool islive=false;
+  for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
+    if(pid->Pid()==liveVpid || pid->Pid()==liveApid) {
+      islive=true;
+      break;
+      }
+  bool needZero=!IsSoftCSA() && (islive || !ScSetup.ConcurrentFF);
+  bool noshift=IsSoftCSA() || (prg->IsUpdate() && prg->pids.Count()==0);
+  PRINTF(L_CORE_PIDS,"%d: %s SID %d (zero=%d noshift=%d)",cardNum,prg->IsUpdate()?"update":"add",prg->Prg(),needZero,noshift);
+  if(prg->pids.Count()>0) {
+    LBSTART(L_CORE_PIDS);
+    LBPUT("%d: pids",cardNum);
+    for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
+      LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+    LBEND();
+    }
+  cEcmHandler *handler=GetHandler(prg->Prg(),needZero,noshift);
+  if(handler) {
+    PRINTF(L_CORE_PIDS,"%d: found handler for SID %d (%s idle=%d idx=%d)",cardNum,prg->Prg(),handler->Id(),handler->IsIdle(),handler->CwIndex());
+    handler->SetPrg(prg);
+    }
+}
+
+bool cCam::HasPrg(int prg)
+{
+  cMutexLock lock(this);
+  for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+    if(!handler->IsIdle() && handler->Sid()==prg)
+      return true;
+  return false;
+}
+
+char *cCam::CurrentKeyStr(int num)
+{
+  cMutexLock lock(this);
+  cEcmHandler *handler;
+  for(handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+    if(--num<0) return handler->CurrentKeyStr();
+  return 0;
+}
+
+bool cCam::Active(bool log)
+{
+  cMutexLock lock(this);
+  for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+    if(!handler->IsIdle()) {
+      if(log) PRINTF(L_GEN_INFO,"handler %s on card %d is not idle",handler->Id(),cardNum);
+      return true;
+      }
+  return false;
+}
+
+void cCam::HouseKeeping(void)
+{
+  cMutexLock lock(this);
+  for(cEcmHandler *handler=handlerList.First(); handler;) {
+    cEcmHandler *next=handlerList.Next(handler);
+    if(handler->IsRemoveable()) RemHandler(handler);
+    handler=next;
+    }
+  if(handlerList.Count()<1 && !ScSetup.PrestartAU) {
+    delete hookman; hookman=0;
+    delete logger; logger=0;
+    }
+}
+
+bool cCam::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+  return device->GetPrgCaids(source,transponder,prg,c);
+}
+
+void cCam::LogStartup(void)
+{
+  cMutexLock lock(this);
+  if(!logger && ScSetup.AutoUpdate) {
+    logger=new cLogger(cardNum,IsSoftCSA());
+    LogStatsUp();
+    }
+}
+
+void cCam::LogEcmStatus(const cEcmInfo *ecm, bool on)
+{
+  cMutexLock lock(this);
+  if(on) LogStartup();
+  if(logger) logger->EcmStatus(ecm,on);
+}
+
+void cCam::AddHook(cLogHook *hook)
+{
+  cMutexLock lock(this);
+  if(!hookman) hookman=new cHookManager(cardNum);
+  if(hookman) hookman->AddHook(hook);
+}
+
+bool cCam::TriggerHook(int id)
+{
+  return hookman && hookman->TriggerHook(id);
+}
+
+void cCam::SetCWIndex(int pid, int index)
+{
+  if(index<MAX_CW_IDX) {
+    ca_pid_t ca_pid;
+    ca_pid.pid=pid;
+    ca_pid.index=index;
+    PRINTF(L_CORE_PIDS,"%d: descrambling pid %04x on index %x",cardNum,pid,index);
+    if(!device->SetCaPid(&ca_pid))
+      if(index>0) {
+        PRINTF(L_GEN_ERROR,"CA_SET_PID failed (%s). Expect a black screen/bad recording. Do you use the patched DVB driver?",strerror(errno));
+        PRINTF(L_GEN_WARN,"Adjusting 'Concurrent FF streams' to NO");
+        ScSetup.ConcurrentFF=0;
+        ScSetup.Store(true);
+        }
+    }
+}
+
+void cCam::WriteCW(int index, unsigned char *cw, bool force)
+{
+  if(index<MAX_CW_IDX) {
+    for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
+    ca_descr_t ca_descr;
+    ca_descr.index=index;
+    unsigned char *last=lastCW[index];
+    if(force || memcmp(&cw[0],&last[0],8)) {
+      memcpy(&last[0],&cw[0],8);
+      ca_descr.parity=0;
+      memcpy(ca_descr.cw,&cw[0],8);
+      if(!device->SetCaDescr(&ca_descr,force))
+        PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
+      }
+
+    if(force || memcmp(&cw[8],&last[8],8)) {
+      memcpy(&last[8],&cw[8],8);
+      ca_descr.parity=1;
+      memcpy(ca_descr.cw,&cw[8],8);
+      if(!device->SetCaDescr(&ca_descr,force))
+        PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
+      }
+    }
+}
+
+void cCam::DumpAV7110(void)
+{
+  device->DumpAV7110();
+}
+
+int cCam::GetFreeIndex(void)
+{
+  for(int idx=0; idx<MAX_CW_IDX; idx++)
+    if(!indexMap[idx]) return idx;
+  return -1;
+}
+
+cEcmHandler *cCam::GetHandler(int sid, bool needZero, bool noshift)
+{
+  cEcmHandler *zerohandler=0, *sidhandler=0, *idlehandler=0;
+  for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler)) {
+    if(handler->Sid()==sid)
+      sidhandler=handler;
+    if(handler->CwIndex()==0)
+      zerohandler=handler;
+    if(handler->IsIdle() && (!idlehandler || (!(needZero ^ (idlehandler->CwIndex()!=0)) && (needZero ^ (handler->CwIndex()!=0))) ))
+      idlehandler=handler;
+    }
+  LBSTART(L_CORE_PIDS);
+  LBPUT("%d: SID=%d zero=%d |",cardNum,sid,needZero);
+  if(sidhandler) LBPUT(" sid=%d/%d/%d",sidhandler->CwIndex(),sidhandler->Sid(),sidhandler->IsIdle());
+  else LBPUT(" sid=-/-/-");
+  if(zerohandler) LBPUT(" zero=%d/%d/%d",zerohandler->CwIndex(),zerohandler->Sid(),zerohandler->IsIdle());
+  else LBPUT(" zero=-/-/-");
+  if(idlehandler) LBPUT(" idle=%d/%d/%d",idlehandler->CwIndex(),idlehandler->Sid(),idlehandler->IsIdle());
+  else LBPUT(" idle=-/-/-");
+  LBEND();
+
+  if(sidhandler) {
+    if(needZero && sidhandler->CwIndex()!=0 && !noshift) {
+      if(!sidhandler->IsIdle())
+        PRINTF(L_CORE_ECM,"%d: shifting cwindex on non-idle handler.",cardNum);
+      if(zerohandler) {
+        if(!zerohandler->IsIdle())
+          PRINTF(L_CORE_ECM,"%d: shifting non-idle zero handler. This shouldn't happen!",cardNum);
+        zerohandler->ShiftCwIndex(sidhandler->CwIndex());
+        sidhandler->ShiftCwIndex(0);
+        }
+      else if(indexMap[0]==0) {
+        indexMap[0]=1;
+        indexMap[sidhandler->CwIndex()]=0;
+        sidhandler->ShiftCwIndex(0);
+        }
+      else PRINTF(L_CORE_ECM,"%d: zero index not free.",cardNum);
+      }
+
+    if(!needZero && sidhandler->CwIndex()==0 && !noshift) {
+      if(!sidhandler->IsIdle())
+        PRINTF(L_CORE_ECM,"%d: shifting cwindex on non-idle handler.",cardNum);
+      int idx=GetFreeIndex();
+      if(idx>=0) {
+        indexMap[idx]=1;
+        sidhandler->ShiftCwIndex(idx);
+        indexMap[0]=0;
+        }
+      else PRINTF(L_CORE_ECM,"%d: no free cwindex. Can't free zero index.",cardNum);
+      }
+
+    return sidhandler;
+    }
+
+  if(needZero && zerohandler) {
+    if(!zerohandler->IsIdle())
+      PRINTF(L_CORE_ECM,"%d: changing SID on non-idle zero handler. This shouldn't happen!",cardNum);
+    return zerohandler;
+    }
+  
+  if(idlehandler) {
+    if(needZero && idlehandler->CwIndex()!=0 && !noshift) {
+      if(indexMap[0]==0) {
+        indexMap[0]=1;
+        indexMap[idlehandler->CwIndex()]=0;
+        idlehandler->ShiftCwIndex(0);
+        }
+      else PRINTF(L_CORE_ECM,"%d: zero index not free. (2)",cardNum);
+      }
+    if(!needZero && idlehandler->CwIndex()==0 && !noshift) {
+      int idx=GetFreeIndex();
+      if(idx>=0) {
+        indexMap[idx]=1;
+        idlehandler->ShiftCwIndex(idx);
+        indexMap[0]=0;
+        }
+      else PRINTF(L_CORE_ECM,"%d: no free cwindex. Can't free zero index. (2)",cardNum);
+      }
+    if((needZero ^ (idlehandler->CwIndex()==0)))
+      PRINTF(L_CORE_ECM,"%d: idlehandler index doesn't match needZero",cardNum);
+    return idlehandler;
+    }
+
+  int idx=GetFreeIndex();
+  if(!needZero && idx==0) {
+    indexMap[0]=1;
+    idx=GetFreeIndex();
+    indexMap[0]=0;
+    if(idx<0) {
+      idx=0;
+      PRINTF(L_CORE_ECM,"%d: can't respect !needZero for new handler",cardNum);
+      }
+    }
+  if(idx<0) {
+    PRINTF(L_CORE_ECM,"%d: no free cwindex",cardNum);
+    return 0;
+    }
+  indexMap[idx]=1;
+  idlehandler=new cEcmHandler(this,cardNum,idx);
+  handlerList.Add(idlehandler);
+  return idlehandler;
+}
+
+void cCam::RemHandler(cEcmHandler *handler)
+{
+  int idx=handler->CwIndex();
+  PRINTF(L_CORE_PIDS,"%d: removing %s on cw index %d",cardNum,handler->Id(),idx);
+  handlerList.Del(handler);
+  indexMap[idx]=0;
+}
+
+#ifndef SASC
+
+// --- cChannelCaids -----------------------------------------------------------
+
+#if APIVERSNUM >= 10500
+
+class cChannelCaids : public cSimpleItem {
+private:
+  int prg, source, transponder;
+  int numcaids;
+  caid_t caids[MAX_CI_SLOT_CAIDS+1];
+public:
+  cChannelCaids(cChannel *channel);
+  bool IsChannel(cChannel *channel);
+  void Sort(void);
+  void Del(caid_t caid);
+  bool HasCaid(caid_t caid);
+  bool Same(cChannelCaids *ch);
+  void HistAdd(unsigned short *hist);
+  void Dump(int n);
+  const caid_t *Caids(void) { caids[numcaids]=0; return caids; }
+  int NumCaids(void) { return numcaids; }
+  };
+
+cChannelCaids::cChannelCaids(cChannel *channel)
+{
+  prg=channel->Sid(); source=channel->Source(); transponder=channel->Transponder();
+  numcaids=0;
+  for(const caid_t *ids=channel->Caids(); *ids; ids++)
+    if(numcaids<MAX_CI_SLOT_CAIDS) caids[numcaids++]=*ids;
+  Sort();
+}
+
+bool cChannelCaids::IsChannel(cChannel *channel)
+{
+  return prg==channel->Sid() && source==channel->Source() && transponder==channel->Transponder();
+}
+
+void cChannelCaids::Sort(void)
+{
+  caid_t tmp[MAX_CI_SLOT_CAIDS];
+  int c=0xFFFF;
+  for(int i=0; i<numcaids; i++) {
+    int d=0;
+    for(int j=0; j<numcaids; j++) if(caids[j]>d && caids[j]<c) d=caids[j];
+    tmp[i]=d; c=d;
+    }
+  memcpy(caids,tmp,sizeof(caids));
+}
+
+void cChannelCaids::Del(caid_t caid)
+{
+  for(int i=0; i<numcaids; i++)
+    if(caids[i]==caid) {
+      numcaids--; caids[i]=caids[numcaids];
+      if(numcaids>0) Sort();
+      caids[numcaids]=0;
+      break;
+      }
+}
+
+bool cChannelCaids::HasCaid(caid_t caid)
+{
+  for(int i=0; i<numcaids; i++) if(caids[i]==caid) return true;
+  return false;
+}
+
+bool cChannelCaids::Same(cChannelCaids *ch)
+{
+  if(numcaids!=ch->numcaids) return false;
+  return memcmp(caids,ch->caids,numcaids*sizeof(caid_t))==0;
+}
+
+void cChannelCaids::HistAdd(unsigned short *hist)
+{
+  for(int i=numcaids-1; i>=0; i--) hist[caids[i]]++;
+}
+
+void cChannelCaids::Dump(int n)
+{
+  LBSTART(L_CORE_CAIDS);
+  LBPUT("%d: channel %d/%x/%x",n,prg,source,transponder);
+  for(const caid_t *ids=Caids(); *ids; ids++) LBPUT(" %04x",*ids);
+  LBEND();
+}
+
+// --- cChannelList ------------------------------------------------------------
+
+class cChannelList : public cSimpleList<cChannelCaids> {
+private:
+  int n;
+public:
+  cChannelList(int N);
+  void Unique(void);
+  void CheckIgnore(void);
+  int Histo(void);
+  void Purge(int caid);
+  };
+
+cChannelList::cChannelList(int N)
+{
+  n=N;
+}
+
+void cChannelList::CheckIgnore(void)
+{
+  for(cChannelCaids *ch=First(); ch; ch=Next(ch)) {
+    const caid_t *ids=ch->Caids();
+    while(*ids) {
+      int pri=0;
+      if(!cSystems::FindIdentBySysId(*ids,false,pri)) {
+        for(cChannelCaids *ch2=Next(ch); ch2; ch2=Next(ch2)) ch2->Del(*ids);
+        ch->Del(*ids);
+        }
+      else ids++;
+      }
+    }
+  PRINTF(L_CORE_CAIDS,"%d: after check",n);
+  for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+}
+
+void cChannelList::Unique(void)
+{
+  for(cChannelCaids *ch1=First(); ch1; ch1=Next(ch1)) {
+    for(cChannelCaids *ch2=Next(ch1); ch2;) {
+      if(ch1->Same(ch2) || ch2->NumCaids()<1) {
+        cChannelCaids *t=Next(ch2);
+        Del(ch2);
+        ch2=t;
+        }
+      else ch2=Next(ch2);
+      }
+    }
+  if(Count()==1 && First() && First()->NumCaids()<1) Del(First());
+  PRINTF(L_CORE_CAIDS,"%d: after unique",n);
+  for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+}
+
+int cChannelList::Histo(void)
+{
+  int h=-1;
+  unsigned short *hist=MALLOC(unsigned short,0x10000);
+  if(hist) {
+    memset(hist,0,sizeof(unsigned short)*0x10000);
+    for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->HistAdd(hist);
+    int c=0;
+    for(int i=0; i<0x10000; i++)
+      if(hist[i]>c) { h=i; c=hist[i]; }
+    free(hist);
+    }
+  else PRINTF(L_GEN_ERROR,"malloc failed in cChannelList::Histo");
+  return h;
+}
+
+void cChannelList::Purge(int caid)
+{
+  for(cChannelCaids *ch=First(); ch;) {
+    if(ch->NumCaids()<=0 || ch->HasCaid(caid)) {
+      cChannelCaids *t=Next(ch);
+      Del(ch);
+      ch=t;
+      }
+    else ch=Next(ch);
+    }
+  if(Count()>0) {
+    PRINTF(L_CORE_CAIDS,"%d: still left",n);
+    for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+    }
+}
+
+// -- cScCiAdapter -------------------------------------------------------------
+
+struct TPDU {
+  unsigned char slot;
+  unsigned char tcid;
+  unsigned char tag;
+  unsigned char len;
+  unsigned char data[1];
+  };
+
+class cScCamSlot;
+
+class cScCiAdapter : public cCiAdapter {
+private:
+  cDevice *device;
+  cCam *cam;
+  cMutex ciMutex;
+  int cardIndex;
+  cRingBufferLinear *rb;
+  cScCamSlot *slots[MAX_CI_SLOTS];
+  //
+  cTimeMs caidTimer;
+  int version[MAX_CI_SLOTS];
+  caid_t caids[MAX_CI_SLOTS][MAX_CI_SLOT_CAIDS+1];
+  int tcid;
+  //
+  cTimeMs readTimer, writeTimer;
+  //
+  void BuildCaids(bool force);
+protected:
+  virtual int Read(unsigned char *Buffer, int MaxLength);
+  virtual void Write(const unsigned char *Buffer, int Length);
+  virtual bool Reset(int Slot);
+  virtual eModuleStatus ModuleStatus(int Slot);
+  virtual bool Assign(cDevice *Device, bool Query=false);
+public:
+  cScCiAdapter(cDevice *Device, int CardIndex, cCam *Cam);
+  ~cScCiAdapter();
+  void CamStop(void);
+  void CamAddPrg(cPrg *prg);
+  bool CamSoftCSA(void);
+  int GetCaids(int slot, unsigned short *Caids, int max);
+  };
+
+// -- cScCamSlot ---------------------------------------------------------------
+
+#define SLOT_CAID_CHECK 10000
+#define SLOT_RESET_TIME 600
+
+class cScCamSlot : public cCamSlot, public cRingBufferLinear {
+private:
+  cScCiAdapter *ciadapter;
+  unsigned short caids[MAX_CI_SLOT_CAIDS+1];
+  int slot, cardIndex, version;
+  cTimeMs checkTimer;
+  bool reset, doReply;
+  cTimeMs resetTimer;
+  eModuleStatus lastStatus;
+  //
+  int GetLength(const unsigned char * &data);
+  void CaInfo(unsigned char *b, int tcid, int cid);
+  bool Check(void);
+public:
+  cScCamSlot(cScCiAdapter *ca, int CardIndex, int Slot);
+  void Process(const unsigned char *data, int len);
+  eModuleStatus Status(void);
+  bool Reset(bool log=true);
+  };
+
+cScCamSlot::cScCamSlot(cScCiAdapter *ca, int CardIndex, int Slot)
+:cCamSlot(ca)
+,cRingBufferLinear(KILOBYTE(2),5+1,false,"SC-CI slot answer")
+,checkTimer(-SLOT_CAID_CHECK-1000)
+{
+  ciadapter=ca; cardIndex=CardIndex; slot=Slot;
+  version=0; caids[0]=0; doReply=false; lastStatus=msReset;
+  Reset(false);
+}
+
+eModuleStatus cScCamSlot::Status(void)
+{
+  eModuleStatus status;
+  if(reset) { 
+    status=msReset;
+    if(resetTimer.TimedOut()) reset=false;
+    }
+  else if(caids[0]) status=msReady;
+  else {
+    status=msPresent; //msNone;
+    Check();
+    }
+  if(status!=lastStatus) {
+    static const char *stext[] = { "none","reset","present","ready" };
+    PRINTF(L_CORE_CI,"%d.%d: status '%s'",cardIndex,slot,stext[status]);
+    lastStatus=status;
+    }
+  return status;
+}
+
+bool cScCamSlot::Reset(bool log)
+{
+  reset=true; resetTimer.Set(SLOT_RESET_TIME);
+  Clear();
+  if(log) PRINTF(L_CORE_CI,"%d.%d: reset",cardIndex,slot);
+  return true;
+}
+
+bool cScCamSlot::Check(void)
+{
+  bool res=false;
+  bool dr=ciadapter->CamSoftCSA() || ScSetup.ConcurrentFF>0;
+  if(dr!=doReply && !IsDecrypting()) {
+    PRINTF(L_CORE_CI,"%d.%d: doReply changed, reset triggered",cardIndex,slot);
+    Reset(false);
+    doReply=dr;
+    }
+  if(checkTimer.TimedOut()) {
+    if(version!=ciadapter->GetCaids(slot,0,0)) {
+      version=ciadapter->GetCaids(slot,caids,MAX_CI_SLOT_CAIDS);
+      PRINTF(L_CORE_CI,"%d.%d: now using CAIDs version %d",cardIndex,slot,version);
+      res=true;
+      }
+    checkTimer.Set(SLOT_CAID_CHECK);
+    }
+  return res;
+}
+
+int cScCamSlot::GetLength(const unsigned char * &data)
+{
+  int len=*data++;
+  if(len&0x80) {
+    int i;
+    for(i=len&~0x80, len=0; i>0; i--) len=(len<<8) + *data++;
+    }
+  return len;
+}
+
+void cScCamSlot::CaInfo(unsigned char *b, int tcid, int cid)
+{
+  b[0]=0xa0; b[2]=tcid;
+  b[3]=0x90;
+  b[4]=0x02; b[5]=cid<<8; b[6]=cid&0xff;
+  b[7]=0x9f; b[8]=0x80;   b[9]=0x31; // AOT_CA_INFO
+  int l=0;
+  for(int i=0; caids[i]; i++) {
+    b[l+11]=caids[i]>>8;
+    b[l+12]=caids[i]&0xff;
+    l+=2;
+    }
+  b[10]=l; b[1]=l+9; b[-1]=l+11; Put(b-1,l+12);
+  PRINTF(L_CORE_CI,"%d.%d sending CA info",cardIndex,slot);
+}
+
+void cScCamSlot::Process(const unsigned char *data, int len)
+{
+  const unsigned char *save=data;
+  data+=3;
+  int dlen=GetLength(data);
+  if(dlen>len-(data-save)) {
+    PRINTF(L_CORE_CI,"%d.%d TDPU length exceeds data length",cardIndex,slot);
+    dlen=len-(data-save);
+    }
+  int tcid=data[0];
+
+  unsigned char a[128], *b=&a[1];
+  if(Check()) CaInfo(b,tcid,0x01);
+
+  if(dlen<8 || data[1]!=0x90) return;
+  int cid=(data[3]<<8)+data[4];
+  int tag=(data[5]<<16)+(data[6]<<8)+data[7];
+  data+=8;
+  dlen=GetLength(data);
+  if(dlen>len-(data-save)) {
+    PRINTF(L_CORE_CI,"%d.%d tag length exceeds data length",cardIndex,slot);
+    dlen=len-(data-save);
+    }
+  switch(tag) {
+    case 0x9f8030: // AOT_CA_INFO_ENQ
+      CaInfo(b,tcid,cid);
+      break;
+    
+    case 0x9f8032: // AOT_CA_PMT
+      if(dlen>=6) {
+        int ca_lm=data[0];
+        int ci_cmd=-1;
+        cPrg *prg=new cPrg((data[1]<<8)+data[2],ca_lm==5);
+        int ilen=(data[4]<<8)+data[5];
+        LBSTARTF(L_CORE_CI);
+        LBPUT("%d.%d CA_PMT decoding len=%x lm=%x prg=%d len=%x",cardIndex,slot,dlen,ca_lm,(data[1]<<8)+data[2],ilen);
+        data+=6; dlen-=6;
+        LBPUT("/%x",dlen);
+        if(ilen>0 && dlen>=ilen) {
+          ci_cmd=data[0];
+          LBPUT(" ci_cmd(G)=%02x",ci_cmd);
+          }
+        data+=ilen; dlen-=ilen;
+        while(dlen>=5) {
+          cPrgPid *pid=new cPrgPid(data[0],(data[1]<<8)+data[2]);
+          prg->pids.Add(pid);
+          ilen=(data[3]<<8)+data[4];
+          LBPUT(" pid=%d,%x len=%x",data[0],(data[1]<<8)+data[2],ilen);
+          data+=5; dlen-=5;
+          LBPUT("/%x",dlen);
+          if(ilen>0 && dlen>=ilen) {
+            ci_cmd=data[0];
+            LBPUT(" ci_cmd(S)=%x",ci_cmd);
+            }
+          data+=ilen; dlen-=ilen;
+          }
+        LBEND();
+        PRINTF(L_CORE_CI,"%d.%d got CA pmt ciCmd=%d caLm=%d",cardIndex,slot,ci_cmd,ca_lm);
+        if(doReply && (ci_cmd==0x03 || (ci_cmd==0x01 && ca_lm==0x03))) {
+          b[0]=0xa0; b[2]=tcid;
+          b[3]=0x90;
+          b[4]=0x02; b[5]=cid<<8; b[6]=cid&0xff;
+          b[7]=0x9f; b[8]=0x80; b[9]=0x33; // AOT_CA_PMT_REPLY
+          b[11]=prg->Prg()<<8;
+          b[12]=prg->Prg()&0xff;
+          b[13]=0x00;
+          b[14]=0x81;  // CA_ENABLE
+          b[10]=4; b[1]=4+9; a[0]=4+11; Put(a,4+12);
+          PRINTF(L_CORE_CI,"%d.%d answer to query",cardIndex,slot);
+          }
+        if(prg->Prg()!=0) {
+          if(ci_cmd==0x04) {
+            PRINTF(L_CORE_CI,"%d.%d stop decrypt",cardIndex,slot);
+            ciadapter->CamStop();
+            }
+          if(ci_cmd==0x01 || (ci_cmd==-1 && (ca_lm==0x04 || ca_lm==0x05))) {
+            PRINTF(L_CORE_CI,"%d.%d set CAM decrypt (prg %d)",cardIndex,slot,prg->Prg());
+            ciadapter->CamAddPrg(prg);
+            }
+          }
+        delete prg;
+        }
+      break;
+    }
+}
+
+// -- cScCiAdapter -------------------------------------------------------------
+
+cScCiAdapter::cScCiAdapter(cDevice *Device, int CardIndex, cCam *Cam)
+{
+  device=Device; cardIndex=CardIndex; cam=Cam;
+  tcid=0;
+  memset(version,0,sizeof(version));
+  memset(slots,0,sizeof(slots));
+  SetDescription("SC-CI adapter on device %d",cardIndex);
+  rb=new cRingBufferLinear(KILOBYTE(5),6+1,false,"SC-CI adapter read");
+  if(rb) {
+    rb->SetTimeouts(0,CAM_READ_TIMEOUT);
+/*
+    bool spare=true;
+    for(int i=0; i<MAX_CI_SLOTS; i++) {
+      if(GetCaids(i,0,0)<=0) {
+        if(!spare) break;
+        spare=false;
+        }
+      slots[i]=new cScCamSlot(this,cardIndex,i);
+      }
+*/
+    BuildCaids(true);
+    slots[0]=new cScCamSlot(this,cardIndex,0);
+    Start();
+    }
+  else PRINTF(L_GEN_ERROR,"failed to create ringbuffer for SC-CI adapter %d.",cardIndex);
+}
+
+cScCiAdapter::~cScCiAdapter()
+{
+  Cancel(3);
+  ciMutex.Lock();
+  delete rb; rb=0;
+  ciMutex.Unlock();
+}
+
+void cScCiAdapter::CamStop(void)
+{
+  if(cam) cam->Stop();
+}
+
+void cScCiAdapter::CamAddPrg(cPrg *prg)
+{
+  if(cam) cam->AddPrg(prg);
+}
+
+bool cScCiAdapter::CamSoftCSA(void)
+{
+  return cam && cam->IsSoftCSA();
+}
+
+int cScCiAdapter::GetCaids(int slot, unsigned short *Caids, int max)
+{
+  BuildCaids(false);
+  cMutexLock lock(&ciMutex);
+  if(Caids) {
+    int i;
+    for(i=0; i<MAX_CI_SLOT_CAIDS && i<max && caids[i]; i++) Caids[i]=caids[slot][i];
+    Caids[i]=0;
+    }
+  return version[slot];
+}
+
+void cScCiAdapter::BuildCaids(bool force)
+{
+  if(caidTimer.TimedOut() || force) {
+    PRINTF(L_CORE_CAIDS,"%d: building caid lists",cardIndex);
+    cChannelList list(cardIndex);
+    Channels.Lock(false);
+    for(cChannel *channel=Channels.First(); channel; channel=Channels.Next(channel)) {
+      if(!channel->GroupSep() && channel->Ca()>=CA_ENCRYPTED_MIN && device->ProvidesTransponder(channel)) {
+        cChannelCaids *ch=new cChannelCaids(channel);
+        if(ch) list.Add(ch);
+        }
+      }
+    Channels.Unlock();
+    list.Unique();
+    list.CheckIgnore();
+    list.Unique();
+
+    int n=0, h;
+    caid_t c[MAX_CI_SLOT_CAIDS+1];
+    memset(c,0,sizeof(c));
+    do {
+      if((h=list.Histo())<0) break;
+      c[n++]=h;
+      LBSTART(L_CORE_CAIDS);
+      LBPUT("%d: added %04x caids now",cardIndex,h); for(int i=0; i<n; i++) LBPUT(" %04x",c[i]);
+      LBEND();
+      list.Purge(h);
+      } while(n<MAX_CI_SLOT_CAIDS && list.Count()>0);
+    c[n]=0;
+    if(n==0) PRINTF(L_CORE_CI,"no active CAIDs");
+    else if(list.Count()>0) PRINTF(L_GEN_ERROR,"too many CAIDs. You should ignore some CAIDs.");
+
+    ciMutex.Lock();
+    if((version[0]==0 && c[0]!=0) || memcmp(caids[0],c,sizeof(caids[0]))) {
+      memcpy(caids[0],c,sizeof(caids[0]));
+      version[0]++;
+      if(version[0]>0) {
+        LBSTART(L_CORE_CI);
+        LBPUT("card %d, slot %d (v=%2d) caids:",cardIndex,0,version[0]);
+        for(int i=0; caids[0][i]; i++) LBPUT(" %04x",caids[0][i]);
+        LBEND();
+        }
+      }
+    ciMutex.Unlock();
+
+    caidTimer.Set(CAID_TIME);
+    }
+}
+
+int cScCiAdapter::Read(unsigned char *Buffer, int MaxLength)
+{
+  cMutexLock lock(&ciMutex);
+  if(cam && rb && Buffer && MaxLength>0) {
+    int c;
+    unsigned char *data=rb->Get(c);
+    if(data) {
+      int s=data[0];
+      if(c>=s+1) {
+        c=s>MaxLength ? MaxLength : s;
+        memcpy(Buffer,&data[1],c);
+        if(c<s) { data[c]=s-c; rb->Del(c); }
+        else rb->Del(c+1);
+        if(Buffer[2]!=0x80 || LOG(L_CORE_CIFULL)) {
+          LDUMP(L_CORE_CI,Buffer,c,"%d.%d <-",cardIndex,Buffer[0]);
+          readTimer.Set();
+          }
+        return c;
+        }
+      else {
+        LDUMP(L_GEN_DEBUG,data,c,"internal: sc-ci %d rb frame sync got=%d avail=%d -",cardIndex,c,rb->Available());
+        rb->Clear();
+        }
+      }
+    }
+  else cCondWait::SleepMs(CAM_READ_TIMEOUT);
+  if(LOG(L_CORE_CIFULL) && readTimer.Elapsed()>2000) {
+    PRINTF(L_CORE_CIFULL,"%d: read heartbeat",cardIndex);
+    readTimer.Set();
+    }
+  return 0;
+}
+
+#define TPDU(data,slot) do { unsigned char *_d=(data); _d[0]=(slot); _d[1]=tcid; } while(0)
+#define TAG(data,tag,len) do { unsigned char *_d=(data); _d[0]=(tag); _d[1]=(len); } while(0)
+#define SB_TAG(data,sb) do { unsigned char *_d=(data); _d[0]=0x80; _d[1]=0x02; _d[2]=tcid; _d[3]=(sb); } while(0)
+#define PUT_TAG(data,len) do { unsigned char *_d=(data)-1; int _l=(len); _d[0]=_l; if(rb) rb->Put(_d,_l+1); } while(0)
+
+void cScCiAdapter::Write(const unsigned char *buff, int len)
+{
+  cMutexLock lock(&ciMutex);
+  if(cam && buff && len>=5) {
+    unsigned char a[128], *b=&a[1];
+    struct TPDU *tpdu=(struct TPDU *)buff;
+    int slot=tpdu->slot;
+    if(buff[2]!=0xA0 || buff[3]>0x01 || LOG(L_CORE_CIFULL))
+      LDUMP(L_CORE_CI,buff,len,"%d.%d ->",cardIndex,slot);
+    if(slots[slot]) {
+      switch(tpdu->tag) {
+        case 0x81: // T_RCV
+          {
+          TPDU(b,slot);
+          int l=2, c;
+          unsigned char *d=slots[slot]->Get(c);
+          if(d) {
+            int s=d[0];
+            if(c>=s) {
+              memcpy(&b[l],&d[1],s);
+              l+=s;
+              slots[slot]->Del(s+1);
+              }
+            else slots[slot]->Del(c);
+            }
+          SB_TAG(&b[l],0x00);
+          PUT_TAG(b,l+4);
+          break;
+          }
+        case 0x82: // T_CREATE_TC
+          tcid=tpdu->data[0];
+          TPDU(b,slot);
+          TAG(&b[2],0x83,0x01); b[4]=tcid;
+          SB_TAG(&b[5],0x00);
+          PUT_TAG(b,9);
+
+          static const unsigned char reqCAS[] = { 0xA0,0x07,0x01,0x91,0x04,0x00,0x03,0x00,0x41 };
+          memcpy(b,reqCAS,sizeof(reqCAS));
+          b[2]=tcid;
+          a[0]=sizeof(reqCAS);
+          slots[slot]->Put(a,sizeof(reqCAS)+1);
+          break;
+        case 0xA0: // T_DATA_LAST
+          slots[slot]->Process(buff,len);
+          TPDU(b,slot);
+          SB_TAG(&b[2],slots[slot]->Available()>0 ? 0x80:0x00);
+          PUT_TAG(b,6);
+          break;
+        }
+      }
+    }
+  else PRINTF(L_CORE_CIFULL,"%d: short write (cam=%d buff=%d len=%d)",cardIndex,cam!=0,buff!=0,len);
+}
+
+bool cScCiAdapter::Reset(int Slot)
+{
+  cMutexLock lock(&ciMutex);
+  PRINTF(L_CORE_CI,"%d: reset of slot %d requested",cardIndex,Slot);
+  return slots[Slot] ? slots[Slot]->Reset():false;
+}
+
+eModuleStatus cScCiAdapter::ModuleStatus(int Slot)
+{
+  cMutexLock lock(&ciMutex);
+  bool enable=ScSetup.CapCheck(cardIndex);
+  if(!enable) CamStop();
+  return (enable && cam && slots[Slot]) ? slots[Slot]->Status():msNone;
+}
+
+bool cScCiAdapter::Assign(cDevice *Device, bool Query)
+{
+  return Device ? (Device==device) : true;
+}
+
+#endif //APIVERSNUM >= 10500
+
+// -- cDeCSA -------------------------------------------------------------------
+
+#define MAX_CSA_PIDS 8192
+#define MAX_CSA_IDX  16
+
+//#define DEBUG_CSA
+
+class cDeCSA {
+private:
+  int cs;
+  unsigned char **range;
+  unsigned char pidmap[MAX_CSA_PIDS];
+  void *keys[MAX_CSA_IDX];
+  unsigned int even_odd[MAX_CSA_IDX];
+  cMutex mutex;
+  cCondVar wait;
+  bool active;
+  int cardindex;
+  //
+  bool GetKeyStruct(int idx);
+public:
+  cDeCSA(int CardIndex);
+  ~cDeCSA();
+  bool Decrypt(unsigned char *data, int len, bool force);
+  bool SetDescr(ca_descr_t *ca_descr, bool initial);
+  bool SetCaPid(ca_pid_t *ca_pid);
+  void SetActive(bool on);
+  };
+
+cDeCSA::cDeCSA(int CardIndex)
+{
+  cardindex=CardIndex;
+  cs=get_suggested_cluster_size();
+  PRINTF(L_CORE_CSA,"%d: clustersize=%d rangesize=%d",cardindex,cs,cs*2+5);
+  range=MALLOC(unsigned char *,(cs*2+5));
+  memset(keys,0,sizeof(keys));
+  memset(even_odd,0,sizeof(even_odd));
+  memset(pidmap,0,sizeof(pidmap));
+}
+
+cDeCSA::~cDeCSA()
+{
+  for(int i=0; i<MAX_CSA_IDX; i++)
+    if(keys[i]) free_key_struct(keys[i]);
+  free(range);
+}
+
+void cDeCSA::SetActive(bool on)
+{
+  active=on;
+  PRINTF(L_CORE_CSA,"%d: set active %s",cardindex,active?"on":"off");
+}
+
+bool cDeCSA::GetKeyStruct(int idx)
+{
+  if(!keys[idx]) keys[idx]=get_key_struct();
+  return keys[idx]!=0;
+}
+
+bool cDeCSA::SetDescr(ca_descr_t *ca_descr, bool initial)
+{
+  cMutexLock lock(&mutex);
+  int idx=ca_descr->index;
+  if(idx<MAX_CSA_IDX && GetKeyStruct(idx)) {
+    if(!initial && active && ca_descr->parity==(even_odd[idx]&0x40)>>6) {
+      PRINTF(L_CORE_CSA,"%d.%d: %s key in use",cardindex,idx,ca_descr->parity?"odd":"even");
+      if(wait.TimedWait(mutex,100)) PRINTF(L_CORE_CSA,"%d.%d: successfully waited for release",cardindex,idx);
+      else PRINTF(L_CORE_CSA,"%d.%d: timed out. setting anyways",cardindex,idx);
+      }
+    PRINTF(L_CORE_CSA,"%d.%d: %s key set",cardindex,idx,ca_descr->parity?"odd":"even");
+    if(ca_descr->parity==0) set_even_control_word(keys[idx],ca_descr->cw);
+    else                    set_odd_control_word(keys[idx],ca_descr->cw);
+    }
+  return true;
+}
+
+bool cDeCSA::SetCaPid(ca_pid_t *ca_pid)
+{
+  cMutexLock lock(&mutex);
+  if(ca_pid->index<MAX_CSA_IDX && ca_pid->pid<MAX_CSA_PIDS) {
+    pidmap[ca_pid->pid]=ca_pid->index;
+    PRINTF(L_CORE_CSA,"%d.%d: set pid %04x",cardindex,ca_pid->index,ca_pid->pid);
+    }
+  return true;
+}
+
+bool cDeCSA::Decrypt(unsigned char *data, int len, bool force)
+{
+  cMutexLock lock(&mutex);
+  int r=-2, ccs=0, currIdx=-1;
+  bool newRange=true;
+  range[0]=0;
+  len-=(TS_SIZE-1);
+  for(int l=0; l<len; l+=TS_SIZE) {
+    if(data[l]!=TS_SYNC_BYTE) {       // let higher level cope with that
+      PRINTF(L_CORE_CSA,"%d: garbage in TS buffer",cardindex);
+      break;
+      }
+    unsigned int ev_od=data[l+3]&0xC0;
+    if(ev_od==0x80 || ev_od==0xC0) { // encrypted
+      int idx=pidmap[((data[l+1]<<8)+data[l+2])&(MAX_CSA_PIDS-1)];
+      if(currIdx<0 || idx==currIdx) { // same or no index
+        currIdx=idx;
+        if(ccs==0 && ev_od!=even_odd[idx]) {
+          even_odd[idx]=ev_od;
+          wait.Broadcast();
+          PRINTF(L_CORE_CSA,"%d.%d: change to %s key",cardindex,idx,(ev_od&0x40)?"odd":"even");
+          }
+        if(newRange) {
+          r+=2; newRange=false;
+          range[r]=&data[l];
+          range[r+2]=0;
+          }
+        range[r+1]=&data[l+TS_SIZE];
+        if(++ccs>=cs) break;
+        }
+      else newRange=true;             // other index, create hole
+      }
+    else {                            // unencrypted
+      // nothing, we don't create holes for unencrypted packets
+      }
+    }
+  if(r>=0) {                          // we have some range
+    if(ccs>=cs || force) {
+      if(GetKeyStruct(currIdx)) {
+        int n=decrypt_packets(keys[currIdx],range);
+#ifdef DEBUG_CSA
+        PRINTF(L_CORE_CSA,"%d.%d: decrypting ccs=%3d cs=%3d %s -> %3d decrypted",cardindex,currIdx,ccs,cs,ccs>=cs?"OK ":"INC",n);
+#endif
+        if(n>0) return true;
+        }
+      }
+#ifdef DEBUG_CSA
+    else PRINTF(L_CORE_CSA,"%d.%d: incomplete cluster ccs=%3d cs=%3d",cardindex,currIdx,ccs,cs);
+#endif
+    }
+  return false;
+}
+
+// -- cDeCsaTSBuffer -----------------------------------------------------------
+
+class cDeCsaTSBuffer : public cThread {
+private:
+  int f;
+  int cardIndex;
+  bool delivered;
+  cRingBufferLinear *ringBuffer;
+  //
+  cDeCSA *decsa;
+  bool scActive;
+  unsigned char *lastP;
+  int lastCount;
+  //
+  virtual void Action(void);
+public:
+  cDeCsaTSBuffer(int File, int Size, int CardIndex, cDeCSA *DeCsa, bool ScActive);
+  ~cDeCsaTSBuffer();
+  uchar *Get(void);
+  void SetActive(bool ScActive);
+  };
+
+cDeCsaTSBuffer::cDeCsaTSBuffer(int File, int Size, int CardIndex, cDeCSA *DeCsa, bool ScActive)
+{
+  SetDescription("TS buffer on device %d", CardIndex);
+  f=File; cardIndex=CardIndex; decsa=DeCsa;
+  delivered=false;
+  lastP=0; lastCount=0;
+  ringBuffer=new cRingBufferLinear(Size,TS_SIZE,true,"FFdecsa-TS");
+  ringBuffer->SetTimeouts(100,100);
+  if(decsa) decsa->SetActive(true);
+  SetActive(ScActive);
+  Start();
+}
+
+cDeCsaTSBuffer::~cDeCsaTSBuffer()
+{
+  Cancel(3);
+  if(decsa) decsa->SetActive(false);
+  delete ringBuffer;
+}
+
+void cDeCsaTSBuffer::SetActive(bool ScActive)
+{
+  scActive=ScActive;
+}
+
+void cDeCsaTSBuffer::Action(void)
+{
+  if(ringBuffer) {
+    bool firstRead=true;
+    cPoller Poller(f);
+    while(Running()) {
+      if(firstRead || Poller.Poll(100)) {
+        firstRead=false;
+        int r=ringBuffer->Read(f);
+        if(r<0 && FATALERRNO) {
+          if(errno==EOVERFLOW)
+            esyslog("ERROR: driver buffer overflow on device %d",cardIndex);
+          else { LOG_ERROR; break; }
+          }
+        }
+      }
+    }
+}
+
+uchar *cDeCsaTSBuffer::Get(void)
+{
+  int Count=0;
+  if(delivered) { ringBuffer->Del(TS_SIZE); delivered=false; }
+  uchar *p=ringBuffer->Get(Count);
+  if(p && Count>=TS_SIZE) {
+    if(*p!=TS_SYNC_BYTE) {
+      for(int i=1; i<Count; i++)
+        if(p[i]==TS_SYNC_BYTE) { Count=i; break; }
+      ringBuffer->Del(Count);
+      esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d",Count,cardIndex);
+      return NULL;
+      }
+
+    if(scActive && (p[3]&0xC0)) {
+      if(decsa) {
+        if(!decsa->Decrypt(p,Count,(lastP==p && lastCount==Count))) {
+          lastP=p; lastCount=Count;
+          cCondWait::SleepMs(20);
+          return NULL;
+          }
+        lastP=0;
+        }
+      else p[3]&=~0xC0; // FF hack
+      }
+
+    delivered=true;
+    return p;
+    }
+  return NULL;
+}
+
+#endif //SASC
+
+// -- cScDvbDevice -------------------------------------------------------------
+
+int cScDvbDevice::budget=0;
+
+#ifndef SASC
+
+cScDvbDevice::cScDvbDevice(int n, int cafd)
+:cDvbDevice(n)
+{
+  decsa=0; tsBuffer=0; cam=0;
+#if APIVERSNUM >= 10500
+  ciadapter=0; hwciadapter=0;
+#endif
+  memset(lrucaid,0,sizeof(lrucaid));
+  fd_ca=cafd; fd_ca2=dup(fd_ca); fd_dvr=-1;
+  softcsa=(fd_ca<0);
+}
+
+cScDvbDevice::~cScDvbDevice()
+{
+  DetachAllReceivers();
+  Cancel(3);
+  EarlyShutdown();
+  delete decsa;
+  if(fd_ca>=0) close(fd_ca);
+#if APIVERSNUM >= 10500
+  if(fd_ca2>=0) close(fd_ca2);
+#endif
+}
+
+void cScDvbDevice::EarlyShutdown(void)
+{
+#if APIVERSNUM >= 10500
+  SetCamSlot(0);
+  delete ciadapter; ciadapter=0;
+  delete hwciadapter; hwciadapter=0;
+#endif
+  if(cam) cam->Stop();
+  delete cam; cam=0;
+}
+
+void cScDvbDevice::LateInit(void)
+{
+  int n=CardIndex();
+  if(DeviceNumber()!=n)
+    PRINTF(L_GEN_ERROR,"CardIndex - DeviceNumber mismatch! Put SC plugin first on VDR commandline!");
+  if(softcsa) {
+    if(HasDecoder()) PRINTF(L_GEN_ERROR,"Card %d is a full-featured card but no ca device found!",n);
+    }
+  else if(ForceBudget(n)) {
+    PRINTF(L_GEN_INFO,"Budget mode forced on card %d",n);
+    softcsa=true;
+    }
+  
+#if APIVERSNUM >= 10500
+  if(fd_ca2>=0) hwciadapter=cDvbCiAdapter::CreateCiAdapter(this,fd_ca2);
+  cam=new cCam(this,n);
+  ciadapter=new cScCiAdapter(this,n,cam);
+#else
+  if(fd_ca2>=0) {
+    ciHandler=cCiHandler::CreateCiHandler(fd_ca2);
+    if(!ciHandler) close(fd_ca2);
+    }
+  cam=ScSetup.CapCheck(n) ? new cCam(this,n):0;
+#endif
+  if(softcsa) {
+    PRINTF(L_GEN_INFO,"Using software decryption on card %d",n);
+    decsa=new cDeCSA(n);
+    }
+}
+
+void cScDvbDevice::Shutdown(void)
+{
+  for(int n=cDevice::NumDevices(); --n>=0;) {
+    cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+    if(dev) dev->EarlyShutdown();
+    }
+}
+
+void cScDvbDevice::Startup(void)
+{
+  if(ScSetup.ForceTransfer)
+    SetTransferModeForDolbyDigital(2);
+  for(int n=cDevice::NumDevices(); --n>=0;) {
+    cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+    if(dev) dev->LateInit();
+    }
+}
+
+void cScDvbDevice::SetForceBudget(int n)
+{
+   if(n>=0 && n<MAXDVBDEVICES) budget|=(1<<n);
+}
+
+bool cScDvbDevice::ForceBudget(int n)
+{
+   return budget && (budget&(1<<n));
+}
+
+static int *vdr_nci=0, *vdr_ud=0, vdr_save_ud;
+
+void cScDvbDevice::Capture(void)
+{
+/*
+  This is an extremly ugly hack to access VDRs device scan parameters, which are
+  protected in this context. Heavily dependant on the actual symbol names
+  created by the compiler. May fail in any future version!
+
+  To get the actual symbol names of your VDR binary you may use the command:
+  objdump -T <path-to-vdr>/vdr | grep -E "(useDevice|nextCardIndex)"
+  Insert the symbol names below.
+*/
+#if __GNUC__ >= 3
+  vdr_nci=(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice13nextCardIndexE");
+  vdr_ud =(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice9useDeviceE");
+#else
+  vdr_nci=(int *)dlsym(RTLD_DEFAULT,"_7cDevice.nextCardIndex");
+  vdr_ud =(int *)dlsym(RTLD_DEFAULT,"_7cDevice.useDevice");
+#endif
+  if(vdr_nci && vdr_ud) { vdr_save_ud=*vdr_ud; *vdr_ud=1<<30; }
+}
+
+bool cScDvbDevice::Initialize(void)
+{
+  if(!vdr_nci || !vdr_ud) {
+    PRINTF(L_GEN_ERROR,"Failed to locate VDR symbols. Plugin not operable");
+    return false;
+    }
+  if(NumDevices()>0) {
+    PRINTF(L_GEN_ERROR,"Number of devices != 0 on init. Put SC plugin first on VDR commandline! Aborting.");
+    return false;
+    }
+  *vdr_nci=0; *vdr_ud=vdr_save_ud;
+
+  int i, found=0;
+  for(i=0; i<MAXDVBDEVICES; i++) {
+    if(UseDevice(NextCardIndex())) {
+      char name[128];
+      DvbName(DEV_DVB_FRONTEND,i,name,sizeof(name));
+      if(access(name,F_OK)==0) {
+        PRINTF(L_GEN_DEBUG,"probing %s",name);
+        int f=open(name,O_RDONLY);
+        if(f>=0) {
+          close(f);
+          PRINTF(L_GEN_DEBUG,"capturing device %d",i);
+          new cScDvbDevice(i,DvbOpen(DEV_DVB_CA,i,O_RDWR));
+          found++;
+          }
+        else {
+          if(errno!=ENODEV && errno!=EINVAL) PRINTF(L_GEN_ERROR,"open %s failed: %s",name,strerror(errno));
+          break;
+          }
+        }
+      else {
+        if(errno!=ENOENT) PRINTF(L_GEN_ERROR,"access %s failed: %s",name,strerror(errno));
+        break;
+        }
+      }
+    else NextCardIndex(1);
+    }
+  NextCardIndex(MAXDVBDEVICES-i);
+  if(found>0) PRINTF(L_GEN_INFO,"captured %d video device%s",found,found>1 ? "s" : "");
+  else PRINTF(L_GEN_INFO,"no DVB device captured");
+  return found>0;
+}
+
+#if APIVERSNUM >= 10501
+bool cScDvbDevice::HasCi(void)
+{
+  return ciadapter || hwciadapter;
+}
+#endif
+
+#if APIVERSNUM >= 10500
+bool cScDvbDevice::Ready(void)
+{
+  return (ciadapter   ? ciadapter->Ready():true) &&
+         (hwciadapter ? hwciadapter->Ready():true);
+}
+#endif
+
+bool cScDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
+{
+  if(cam) cam->SetPid(Type,Handle->pid,On);
+  tsMutex.Lock();
+  if(tsBuffer) tsBuffer->SetActive(ScActive());
+  tsMutex.Unlock();
+  return cDvbDevice::SetPid(Handle,Type,On);
+}
+
+bool cScDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
+{
+  lruMutex.Lock();
+  int i=FindLRUPrg(Channel->Source(),Channel->Transponder(),Channel->Sid());
+  if(i<0) i=MAX_LRU_CAID-1;
+  if(i>0) memmove(&lrucaid[1],&lrucaid[0],sizeof(struct LruCaid)*i);
+#if APIVERSNUM >= 10500
+  const caid_t *c=Channel->Caids();
+  for(i=0; i<MAXCAIDS && *c; i++) lrucaid[0].caids[i]=*c++;
+  lrucaid[0].caids[i]=0;
+#else
+  for(i=0; i<=MAXCAIDS; i++) if((lrucaid[0].caids[i]=Channel->Ca(i))==0) break;
+#endif
+  lrucaid[0].src=Channel->Source();
+  lrucaid[0].tr=Channel->Transponder();
+  lrucaid[0].prg=Channel->Sid();
+  lruMutex.Unlock();
+  if(cam) cam->Tune(Channel);
+  bool ret=cDvbDevice::SetChannelDevice(Channel,LiveView);
+  if(ret && cam) cam->PostTune();
+  return ret;
+}
+
+bool cScDvbDevice::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+  cMutexLock lock(&lruMutex);
+  int i=FindLRUPrg(source,transponder,prg);
+  if(i>=0) {
+    for(int j=0; j<MAXCAIDS && lrucaid[i].caids[j]; j++) *c++=lrucaid[i].caids[j];
+    *c=0;
+    return true;
+    }
+  return false;
+}
+
+int cScDvbDevice::FindLRUPrg(int source, int transponder, int prg)
+{
+  for(int i=0; i<MAX_LRU_CAID; i++)
+    if(lrucaid[i].src==source && lrucaid[i].tr==transponder && lrucaid[i].prg==prg) return i;
+  return -1;
+}
+
+#if APIVERSNUM < 10500
+int cScDvbDevice::ProvidesCa(const cChannel *Channel) const
+{
+  if(cam && Channel->Ca()>=CA_ENCRYPTED_MIN) {
+    int j;
+    caid_t ids[MAXCAIDS+1];
+    for(j=0; j<=MAXCAIDS; j++) if((ids[j]=Channel->Ca(j))==0) break;
+    if(cSystems::Provides(ids,!softcsa)>0) return 2;
+    }
+  return cDvbDevice::ProvidesCa(Channel);
+}
+
+bool cScDvbDevice::CiAllowConcurrent(void) const
+{
+  return softcsa || ScSetup.ConcurrentFF>0;
+}
+
+void cScDvbDevice::CiStartDecrypting(void)
+{
+  if(cam) {
+    cSimpleList<cPrg> prgList;
+    for(cCiCaProgramData *p=ciProgramList.First(); p; p=ciProgramList.Next(p)) {
+      if(p->modified) {
+        cPrg *prg=new cPrg(p->programNumber,cam->HasPrg(p->programNumber));
+        if(prg) {
+          for(cCiCaPidData *q=p->pidList.First(); q; q=p->pidList.Next(q)) {
+            if(q->active)
+              prg->pids.Add(new cPrgPid(q->streamType,q->pid));
+            }
+          prgList.Add(prg);
+          }
+        p->modified=false;
+        }
+      }
+    for(int loop=1; loop<=2; loop++) // first delete, then add
+      for(cPrg *prg=prgList.First(); prg; prg=prgList.Next(prg))
+        if((loop==1)!=(prg->pids.Count()>0))
+          cam->AddPrg(prg);
+    }
+  cDvbDevice::CiStartDecrypting();
+}
+#endif //APIVERSNUM < 10500
+
+bool cScDvbDevice::ScActive(void)
+{
+#if APIVERSNUM >= 10500
+  return dynamic_cast<cScCamSlot *>(CamSlot())!=0;
+#else
+  return cam && softcsa;
+#endif
+}
+
+bool cScDvbDevice::OpenDvr(void)
+{
+  CloseDvr();
+  fd_dvr=DvbOpen(DEV_DVB_DVR,CardIndex(),O_RDONLY|O_NONBLOCK,true);
+  if(fd_dvr>=0) {
+    tsMutex.Lock();
+    tsBuffer=new cDeCsaTSBuffer(fd_dvr,MEGABYTE(2),CardIndex()+1,decsa,ScActive());
+    tsMutex.Unlock();
+    }
+  return fd_dvr>=0;
+}
+
+void cScDvbDevice::CloseDvr(void)
+{
+  tsMutex.Lock();
+  delete tsBuffer; tsBuffer=0;
+  tsMutex.Unlock();
+  if(fd_dvr>=0) { close(fd_dvr); fd_dvr=-1; }
+}
+
+bool cScDvbDevice::GetTSPacket(uchar *&Data)
+{
+  if(tsBuffer) { Data=tsBuffer->Get(); return true; }
+  return false;
+}
+
+bool cScDvbDevice::SetCaDescr(ca_descr_t *ca_descr, bool initial)
+{
+  if(!softcsa) {
+    cMutexLock lock(&cafdMutex);
+    return ioctl(fd_ca,CA_SET_DESCR,ca_descr)>=0;
+    }
+  else if(decsa) return decsa->SetDescr(ca_descr,initial);
+  return false;
+}
+
+bool cScDvbDevice::SetCaPid(ca_pid_t *ca_pid)
+{
+  if(!softcsa) {
+    cMutexLock lock(&cafdMutex);
+    return ioctl(fd_ca,CA_SET_PID,ca_pid)>=0;
+    }
+  else if(decsa) return decsa->SetCaPid(ca_pid);
+  return false;
+}
+
+static unsigned int av7110_read(int fd, unsigned int addr)
+{
+  ca_pid_t arg;
+  arg.pid=addr;
+  ioctl(fd,CA_GET_MSG,&arg);
+  return arg.index;
+}
+
+#if 0
+static void av7110_write(int fd, unsigned int addr, unsigned int val)
+{
+  ca_pid_t arg;
+  arg.pid=addr;
+  arg.index=val;
+  ioctl(fd,CA_SEND_MSG,&arg);
+}
+#endif
+
+void cScDvbDevice::DumpAV7110(void)
+{
+  if(LOG(L_CORE_AV7110)) {
+#define CODEBASE (0x2e000404+0x1ce00)
+    cMutexLock lock(&cafdMutex);
+    if(HasDecoder() && lastDump.Elapsed()>20000) {
+      lastDump.Set();
+      static unsigned int handles=0, hw_handles=0;
+      static const unsigned int code[] = {
+        0xb5100040,0x4a095a12,0x48094282,0xd00b4b09,0x20000044,
+        0x5b1c4294,0xd0033001,0x281cdbf8,0xe001f7fe,0xfd14bc10
+        };
+      static const unsigned int mask[] = {
+        0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
+        0xffffffff,0xffffffff,0xffffffff,0xfffff800,0xf800ffff
+        };
+      if(!handles) {
+        handles=1;
+        PRINTF(L_CORE_AV7110,"searching handle tables");
+        for(int i=0; i<0x2000; i+=4) {
+          int j;
+          for(j=0; j<20; j+=4) {
+            int r=av7110_read(fd_ca,CODEBASE+i+j);
+            if((r&mask[j/4])!=(code[j/4]&mask[j/4])) break;
+            }
+          if(j==20) {
+            handles=av7110_read(fd_ca,CODEBASE+i+44);
+            hw_handles=av7110_read(fd_ca,CODEBASE+i+52);
+            PRINTF(L_CORE_AV7110,"found handles=%08x hw_handles=%08x at 0x%08x",handles,hw_handles,CODEBASE+i);
+            if((handles>>16)!=0x2e08 || (hw_handles>>16)!=0x2e08) {
+              PRINTF(L_CORE_AV7110,"seems to be invalid");
+              }
+            break;
+            }
+          }
+        }
+
+      unsigned int hdl=0, hwhdl=0;
+      PRINTF(L_CORE_AV7110,"         : 64000080 64000400");
+      for(int i=0; i<=31; i++) {
+        unsigned int off80 =av7110_read(fd_ca,0x64000080+i*4);
+        unsigned int off400=av7110_read(fd_ca,0x64000400+i*8);
+        LBSTART(L_CORE_AV7110);
+        LBPUT("handle %2d: %08x %08x %s pid=%04x idx=%d",
+          i,off80,off400,off80&0x2000?"ACT":"---",off80&0x1fff,(off400&0x1e)>>1);
+        if(handles>1 && i<=27) {
+          if((i&1)==0) {
+            hdl=av7110_read(fd_ca,handles+i*2);
+            hwhdl=av7110_read(fd_ca,hw_handles+i*2);
+            }
+          unsigned int s=((~i)&1)<<4;
+          LBPUT(" | %02d hdl=%04x hwfilt=%04x",i,(hdl>>s)&0xffff,(hwhdl>>s)&0xffff);
+          }
+        LBEND();
+        }
+      }
+    }
+}
+
+#else //SASC
+
+cScDvbDevice::cScDvbDevice(int n, int cafd)
+:cDvbDevice(n)
+{
+  softcsa=false;
+  cam=new cCam(this,n);
+}
+
+cScDvbDevice::~cScDvbDevice()
+{
+  delete cam;
+}
+
+void cScDvbDevice::Shutdown(void)
+{}
+
+void cScDvbDevice::Startup(void)
+{}
+
+void cScDvbDevice::SetForceBudget(int n)
+{}
+
+bool cScDvbDevice::ForceBudget(int n)
+{
+   return true;
+}
+
+void cScDvbDevice::Capture(void)
+{}
+
+bool cScDvbDevice::Initialize(void)
+{
+  return true;
+}
+
+bool cScDvbDevice::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+  return false;
+}
+
+bool cScDvbDevice::SetCaDescr(ca_descr_t *ca_descr, bool initial)
+{
+  return false;
+}
+
+bool cScDvbDevice::SetCaPid(ca_pid_t *ca_pid)
+{
+  return false;
+}
+
+void cScDvbDevice::DumpAV7110(void)
+{}
+
+#endif //SASC
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/cam.h
new file mode 100644 (file)
index 0000000..94031d6
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___CAM_H
+#define ___CAM_H
+
+#include <linux/dvb/ca.h>
+#include <vdr/dvbdevice.h>
+#include <vdr/thread.h>
+#include "data.h"
+#include "misc.h"
+
+class cChannel;
+
+class cEcmHandler;
+class cEcmData;
+class cLogger;
+class cHookManager;
+class cLogHook;
+class cDeCSA;
+class cDeCsaTSBuffer;
+class cScCiAdapter;
+class cScDvbDevice;
+class cPrg;
+
+// ----------------------------------------------------------------
+
+class cEcmCache : public cStructListPlain<cEcmData> {
+private:
+  cEcmData *Exists(cEcmInfo *e);
+protected:
+  virtual bool ParseLinePlain(const char *line);
+public:
+  cEcmCache(void);
+  void New(cEcmInfo *e);
+  int GetCached(cSimpleList<cEcmInfo> *list, int sid, int Source, int Transponder);
+  void Delete(cEcmInfo *e);
+  void Flush(void);
+  };
+
+extern cEcmCache ecmcache;
+
+// ----------------------------------------------------------------
+
+class cPrgPid : public cSimpleItem {
+private:
+  int type, pid;
+  bool proc;
+public:
+  cPrgPid(int Type, int Pid) { type=Type; pid=Pid; proc=false; }
+  int Pid(void) { return pid; }
+  int Type(void) { return type; }
+  bool Proc(void) { return proc; }
+  void Proc(bool is) { proc=is; };
+  };
+
+// ----------------------------------------------------------------
+
+class cPrg : public cSimpleItem {
+private:
+  int prg;
+  bool isUpdate;
+public:
+  cSimpleList<cPrgPid> pids;
+  //
+  cPrg(int Prg, bool IsUpdate) { prg=Prg; isUpdate=IsUpdate; }
+  int Prg(void) { return prg; }
+  bool IsUpdate(void) { return isUpdate; }
+  };
+
+// ----------------------------------------------------------------
+
+#if APIVERSNUM >= 10500
+typedef int caid_t;
+#else
+typedef unsigned short caid_t;
+#endif
+
+#define MAX_CW_IDX        16
+#define MAX_CI_SLOTS      8
+#ifdef VDR_MAXCAID
+#define MAX_CI_SLOT_CAIDS VDR_MAXCAID
+#else
+#define MAX_CI_SLOT_CAIDS 16
+#endif
+
+class cCam : private cMutex {
+private:
+  int cardNum;
+  cScDvbDevice *device;
+  cSimpleList<cEcmHandler> handlerList;
+  cLogger *logger;
+  cHookManager *hookman;
+  int source, transponder, liveVpid, liveApid;
+  unsigned char indexMap[MAX_CW_IDX], lastCW[MAX_CW_IDX][2*8];
+  //
+  cEcmHandler *GetHandler(int sid, bool needZero, bool noshift);
+  void RemHandler(cEcmHandler *handler);
+  int GetFreeIndex(void);
+  void LogStartup(void);
+public:
+  cCam(cScDvbDevice *dev, int CardNum);
+  virtual ~cCam();
+  // EcmHandler API
+  void WriteCW(int index, unsigned char *cw, bool force);
+  void SetCWIndex(int pid, int index);
+  void DumpAV7110(void);
+  void LogEcmStatus(const cEcmInfo *ecm, bool on);
+  bool GetPrgCaids(int source, int transponder, int prg, caid_t *c);
+  void AddHook(cLogHook *hook);
+  bool TriggerHook(int id);
+  // Plugin API
+  bool Active(bool log);
+  void HouseKeeping(void);
+  void Tune(const cChannel *channel);
+  void PostTune(void);
+  void SetPid(int type, int pid, bool on);
+  void Stop(void);
+  void AddPrg(cPrg *prg);
+  bool HasPrg(int prg);
+  char *CurrentKeyStr(int num);
+  //
+  bool IsSoftCSA(void);
+  int Source(void) { return source; }
+  int Transponder(void) { return transponder; }
+  };
+
+void LogStatsDown(void);
+
+// ----------------------------------------------------------------
+
+#define MAX_LRU_CAID 10
+
+class cScDvbDevice : public cDvbDevice {
+private:
+  cDeCSA *decsa;
+  cDeCsaTSBuffer *tsBuffer;
+  cMutex tsMutex;
+#if APIVERSNUM >= 10500
+  cScCiAdapter *ciadapter;
+  cCiAdapter *hwciadapter;
+#endif
+  cCam *cam;
+  int fd_dvr, fd_ca, fd_ca2;
+  bool softcsa;
+  cMutex cafdMutex;
+  cTimeMs lastDump;
+  struct LruCaid {
+    int src, tr, prg;
+    caid_t caids[MAXCAIDS+1];
+    } lrucaid[MAX_LRU_CAID];
+  cMutex lruMutex;
+  static int budget;
+  //
+#ifndef SASC
+  void LateInit(void);
+  void EarlyShutdown(void);
+  int FindLRUPrg(int source, int transponder, int prg);
+  bool ScActive(void);
+#endif //SASC
+protected:
+#ifndef SASC
+#if APIVERSNUM >= 10500
+  virtual bool Ready(void);
+#else
+  virtual void CiStartDecrypting(void);
+  virtual bool CiAllowConcurrent(void) const;
+#endif
+  virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+  virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+  virtual bool OpenDvr(void);
+  virtual void CloseDvr(void);
+  virtual bool GetTSPacket(uchar *&Data);
+#endif //SASC
+public:
+  cScDvbDevice(int n, int cafd);
+  ~cScDvbDevice();
+#ifndef SASC
+#if APIVERSNUM >= 10501
+  virtual bool HasCi(void);
+#endif
+#if APIVERSNUM < 10500
+  virtual int ProvidesCa(const cChannel *Channel) const;
+#endif
+#endif //SASC
+  static void Capture(void);
+  static bool Initialize(void);
+  static void Startup(void);
+  static void Shutdown(void);
+  static void SetForceBudget(int n);
+  static bool ForceBudget(int n);
+  virtual bool SetCaDescr(ca_descr_t *ca_descr, bool initial);
+  virtual bool SetCaPid(ca_pid_t *ca_pid);
+  void DumpAV7110(void);
+  cCam *Cam(void) { return cam; }
+  bool SoftCSA(void) { return softcsa; }
+  virtual bool GetPrgCaids(int source, int transponder, int prg, caid_t *c);
+  };
+
+#endif // ___CAM_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/common.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/common.h
new file mode 100644 (file)
index 0000000..5396444
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___COMMON_H
+#define ___COMMON_H
+
+// Debugging
+#define DEBUG
+//#define DEBUG_DYN
+#define DEBUG_LOG_FILE "/var/tmp/logger.log"
+
+#ifdef DEBUG
+#define d(x) { (x); }
+#else
+#define d(x) ; 
+#endif
+
+#ifdef DEBUG_DYN
+#define dyn(x) { (x); }
+#else
+#define dyn(x) ; 
+#endif
+
+#endif //___COMMON_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto-bn.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto-bn.h
new file mode 100644 (file)
index 0000000..0168d94
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___CRYPTO_BN_H
+#define ___CRYPTO_BN_H
+
+#include <openssl/bn.h>
+
+// ----------------------------------------------------------------
+
+void RotateBytes(unsigned char *in, int n);
+void RotateBytes(unsigned char *out, const unsigned char *in, int n);
+
+// ----------------------------------------------------------------
+
+class cBN {
+private:
+  BIGNUM big;
+public:
+  cBN(void) { BN_init(&big); }
+  ~cBN() { BN_free(&big); }
+  operator BIGNUM* () { return &big; }
+  BIGNUM *operator->() { return &big; }
+  bool Get(const unsigned char *in, int n);
+  bool GetLE(const unsigned char *in, int n);
+  int Put(unsigned char *out, int n) const;
+  int PutLE(unsigned char *out, int n) const;
+  };
+
+class cBNctx {
+private:
+  BN_CTX *ctx;
+public:
+  cBNctx(void) { ctx=BN_CTX_new(); }
+  ~cBNctx() { BN_CTX_free(ctx); }
+  operator BN_CTX* () { return ctx; }
+  };
+
+#endif //___CRYPTO_BN_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.c
new file mode 100644 (file)
index 0000000..a6c5bb4
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "crypto.h"
+#include "helper.h"
+#include "log.h"
+
+// -----------------------------------------------------------------------------
+
+void RotateBytes(unsigned char *out, const unsigned char *in, int n)
+{
+  if(n>0) {
+    out+=n;
+    do { *(--out)=*(in++); } while(--n);
+    }
+}
+
+void RotateBytes(unsigned char *in, int n)
+{
+  if(n>1) {
+    unsigned char *e=in+n-1;
+    do {
+      unsigned char temp=*in;
+      *in++=*e;
+      *e-- =temp;
+      } while(in<e);
+    }
+}
+
+// -- cBN ----------------------------------------------------------------------
+
+bool cBN::Get(const unsigned char *in, int n)
+{
+  return BN_bin2bn(in,n,&big)!=0;
+}
+
+int cBN::Put(unsigned char *out, int n) const
+{
+  int s=BN_num_bytes(&big);
+  if(s>n) {
+    unsigned char *buff=AUTOMEM(s);
+    BN_bn2bin(&big,buff);
+    memcpy(out,buff+s-n,n);
+    }
+  else if(s<n) {
+    int l=n-s;
+    memset(out,0,l);
+    BN_bn2bin(&big,out+l);
+    }
+  else BN_bn2bin(&big,out);
+  return s;
+}
+
+bool cBN::GetLE(const unsigned char *in, int n)
+{
+  unsigned char *tmp=AUTOMEM(n);
+  RotateBytes(tmp,in,n);
+  return BN_bin2bn(tmp,n,&big)!=0;
+}
+
+int cBN::PutLE(unsigned char *out, int n) const
+{
+  int s=Put(out,n);
+  RotateBytes(out,n);
+  return s;
+}
+
+// -- cIDEA --------------------------------------------------------------------
+
+#ifndef OPENSSL_HAS_IDEA
+#warning ** openssl lacks IDEA support. Using deprecated static support code. Update your openssl package.
+#include "support/i_cbc.c"
+#include "support/i_skey.c"
+#endif
+
+void cIDEA::Decrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) const
+{
+  unsigned char v[8];
+  if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+  IDEA_KEY_SCHEDULE ks;
+  idea_set_encrypt_key(key,&ks);
+  idea_cbc_encrypt(data,data,len&~7,&ks,iv,IDEA_DECRYPT);
+}
+
+int cIDEA::Encrypt(const unsigned char *data, int len, unsigned char *crypt, const unsigned char *key, unsigned char *iv) const
+{
+  unsigned char v[8];
+  if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+  len&=~7;
+  IDEA_KEY_SCHEDULE ks;
+  idea_set_encrypt_key(key,&ks);
+  idea_cbc_encrypt(data,crypt,len,&ks,iv,IDEA_ENCRYPT);
+  return len;
+}
+
+void cIDEA::SetEncKey(const unsigned char *key, IdeaKS *ks) const
+{
+  idea_set_encrypt_key(key,ks);
+}
+
+void cIDEA::SetDecKey(const unsigned char *key, IdeaKS *ks) const
+{
+  IDEA_KEY_SCHEDULE tmp;
+  idea_set_encrypt_key(key,&tmp);
+  idea_set_decrypt_key(&tmp,ks);
+}
+
+void cIDEA::Decrypt(unsigned char *data, int len, IdeaKS *ks, unsigned char *iv) const
+{
+  unsigned char v[8];
+  if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+  idea_cbc_encrypt(data,data,len&~7,ks,iv,IDEA_DECRYPT);
+}
+
+int cIDEA::Encrypt(const unsigned char *data, int len, unsigned char *crypt, IdeaKS *ks, unsigned char *iv) const
+{
+  unsigned char v[8];
+  if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+  idea_cbc_encrypt(data,crypt,len&~7,ks,iv,IDEA_ENCRYPT);
+  return len;
+}
+
+// -- cRSA ---------------------------------------------------------------------
+
+bool cRSA::Input(cBN *d, const unsigned char *in, int n, bool LE) const
+{
+  if(LE) return d->GetLE(in,n); 
+  else   return d->Get(in,n);
+}
+
+int cRSA::Output(unsigned char *out, int n, cBN *r, bool LE) const
+{
+  if(LE) return r->PutLE(out,n);
+  else   return r->Put(out,n);
+}
+
+int cRSA::RSA(unsigned char *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+  cBNctx ctx;
+  cBN r, d;
+  if(Input(&d,in,n,LE)) {
+    if(BN_mod_exp(r,d,exp,mod,ctx)) return Output(out,n,&r,LE);
+    PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+    }
+  return 0;
+}
+
+int cRSA::RSA(BIGNUM *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+  cBNctx ctx;
+  cBN d;
+  if(Input(&d,in,n,LE)) {
+    if(BN_mod_exp(out,d,exp,mod,ctx)) return BN_num_bytes(out);
+    PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+    }
+  return 0;
+}
+
+int cRSA::RSA(unsigned char *out, int n, BIGNUM *in, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+  cBNctx ctx;
+  cBN r;
+  if(BN_mod_exp(r,in,exp,mod,ctx)) return Output(out,n,&r,LE);
+  PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+  return 0;
+}
+
+// -- cDes ---------------------------------------------------------------------
+
+const unsigned char cDes::_PC1[] = {
+   57,49,41,33,25,17, 9, 1, 
+   58,50,42,34,26,18,10, 2,
+   59,51,43,35,27,19,11, 3,
+   60,52,44,36,63,55,47,39,
+   31,23,15, 7,62,54,46,38,
+   30,22,14, 6,61,53,45,37,
+   29,21,13, 5,28,20,12, 4
+  };
+
+const unsigned char cDes::_PC2[] = {
+  14,17,11,24, 1, 5,   3,28,15, 6,21,10,
+  23,19,12, 4,26, 8,  16, 7,27,20,13, 2,
+  41,52,31,37,47,55,  30,40,51,45,33,48,
+  44,49,39,56,34,53,  46,42,50,36,29,32
+  };
+
+const unsigned char cDes::_E[] = {
+  32, 1, 2, 3, 4, 5,   4, 5, 6, 7, 8, 9,
+   8, 9,10,11,12,13,  12,13,14,15,16,17,
+  16,17,18,19,20,21,  20,21,22,23,24,25,
+  24,25,26,27,28,29,  28,29,30,31,32, 1
+  };
+
+const unsigned char cDes::_P[] = {
+  16, 7,20,21,  29,12,28,17,
+   1,15,23,26,   5,18,31,10,
+   2, 8,24,14,  32,27, 3, 9,
+  19,13,30, 6,  22,11, 4,25
+  };
+
+const unsigned char cDes::IP[] = {
+  58,50,42,34,26,18,10,2,  60,52,44,36,28,20,12,4,
+  62,54,46,38,30,22,14,6,  64,56,48,40,32,24,16,8,
+  57,49,41,33,25,17, 9,1,  59,51,43,35,27,19,11,3,
+  61,53,45,37,29,21,13,5,  63,55,47,39,31,23,15,7
+  };
+
+const unsigned char cDes::FP[] = {
+  40,8,48,16,56,24,64,32,  39,7,47,15,55,23,63,31,
+  38,6,46,14,54,22,62,30,  37,5,45,13,53,21,61,29,
+  36,4,44,12,52,20,60,28,  35,3,43,11,51,19,59,27,
+  34,2,42,10,50,18,58,26,  33,1,41, 9,49,17,57,25
+  };
+
+const unsigned char cDes::S[][64] = {
+  { 0xe,0x0,0x4,0xf,0xd,0x7,0x1,0x4,  0x2,0xe,0xf,0x2,0xb,0xd,0x8,0x1,
+    0x3,0xa,0xa,0x6,0x6,0xc,0xc,0xb,  0x5,0x9,0x9,0x5,0x0,0x3,0x7,0x8,
+    0x4,0xf,0x1,0xc,0xe,0x8,0x8,0x2,  0xd,0x4,0x6,0x9,0x2,0x1,0xb,0x7,
+    0xf,0x5,0xc,0xb,0x9,0x3,0x7,0xe,  0x3,0xa,0xa,0x0,0x5,0x6,0x0,0xd
+  },
+  { 0xf,0x3,0x1,0xd,0x8,0x4,0xe,0x7,  0x6,0xf,0xb,0x2,0x3,0x8,0x4,0xe,
+    0x9,0xc,0x7,0x0,0x2,0x1,0xd,0xa,  0xc,0x6,0x0,0x9,0x5,0xb,0xa,0x5,
+    0x0,0xd,0xe,0x8,0x7,0xa,0xb,0x1,  0xa,0x3,0x4,0xf,0xd,0x4,0x1,0x2,
+    0x5,0xb,0x8,0x6,0xc,0x7,0x6,0xc,  0x9,0x0,0x3,0x5,0x2,0xe,0xf,0x9
+  },
+  { 0xa,0xd,0x0,0x7,0x9,0x0,0xe,0x9,  0x6,0x3,0x3,0x4,0xf,0x6,0x5,0xa,
+    0x1,0x2,0xd,0x8,0xc,0x5,0x7,0xe,  0xb,0xc,0x4,0xb,0x2,0xf,0x8,0x1,
+    0xd,0x1,0x6,0xa,0x4,0xd,0x9,0x0,  0x8,0x6,0xf,0x9,0x3,0x8,0x0,0x7,
+    0xb,0x4,0x1,0xf,0x2,0xe,0xc,0x3,  0x5,0xb,0xa,0x5,0xe,0x2,0x7,0xc
+  },
+  { 0x7,0xd,0xd,0x8,0xe,0xb,0x3,0x5,  0x0,0x6,0x6,0xf,0x9,0x0,0xa,0x3,
+    0x1,0x4,0x2,0x7,0x8,0x2,0x5,0xc,  0xb,0x1,0xc,0xa,0x4,0xe,0xf,0x9,
+    0xa,0x3,0x6,0xf,0x9,0x0,0x0,0x6,  0xc,0xa,0xb,0x1,0x7,0xd,0xd,0x8,
+    0xf,0x9,0x1,0x4,0x3,0x5,0xe,0xb,  0x5,0xc,0x2,0x7,0x8,0x2,0x4,0xe
+  },
+  { 0x2,0xe,0xc,0xb,0x4,0x2,0x1,0xc,  0x7,0x4,0xa,0x7,0xb,0xd,0x6,0x1,
+    0x8,0x5,0x5,0x0,0x3,0xf,0xf,0xa,  0xd,0x3,0x0,0x9,0xe,0x8,0x9,0x6,
+    0x4,0xb,0x2,0x8,0x1,0xc,0xb,0x7,  0xa,0x1,0xd,0xe,0x7,0x2,0x8,0xd,
+    0xf,0x6,0x9,0xf,0xc,0x0,0x5,0x9,  0x6,0xa,0x3,0x4,0x0,0x5,0xe,0x3
+  },
+  { 0xc,0xa,0x1,0xf,0xa,0x4,0xf,0x2,  0x9,0x7,0x2,0xc,0x6,0x9,0x8,0x5,
+    0x0,0x6,0xd,0x1,0x3,0xd,0x4,0xe,  0xe,0x0,0x7,0xb,0x5,0x3,0xb,0x8,
+    0x9,0x4,0xe,0x3,0xf,0x2,0x5,0xc,  0x2,0x9,0x8,0x5,0xc,0xf,0x3,0xa,
+    0x7,0xb,0x0,0xe,0x4,0x1,0xa,0x7,  0x1,0x6,0xd,0x0,0xb,0x8,0x6,0xd
+  },
+  { 0x4,0xd,0xb,0x0,0x2,0xb,0xe,0x7,  0xf,0x4,0x0,0x9,0x8,0x1,0xd,0xa,
+    0x3,0xe,0xc,0x3,0x9,0x5,0x7,0xc,  0x5,0x2,0xa,0xf,0x6,0x8,0x1,0x6,
+    0x1,0x6,0x4,0xb,0xb,0xd,0xd,0x8,  0xc,0x1,0x3,0x4,0x7,0xa,0xe,0x7,
+    0xa,0x9,0xf,0x5,0x6,0x0,0x8,0xf,  0x0,0xe,0x5,0x2,0x9,0x3,0x2,0xc
+  },
+  { 0xd,0x1,0x2,0xf,0x8,0xd,0x4,0x8,  0x6,0xa,0xf,0x3,0xb,0x7,0x1,0x4,
+    0xa,0xc,0x9,0x5,0x3,0x6,0xe,0xb,  0x5,0x0,0x0,0xe,0xc,0x9,0x7,0x2,
+    0x7,0x2,0xb,0x1,0x4,0xe,0x1,0x7,  0x9,0x4,0xc,0xa,0xe,0x8,0x2,0xd,
+    0x0,0xf,0x6,0xc,0xa,0x9,0xd,0x0,  0xf,0x3,0x3,0x5,0x5,0x6,0x8,0xb
+  } };
+
+const unsigned char cDes::LS[] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 };
+
+/* actualy slower ... :-(
+#if defined __GNUC__ && __GNUC__ >= 2
+#define shiftin(V,R,n) ({unsigned int VV; \
+                         __asm__ ("btl %3,%2\n\tadcl %0,%0" : "=g" (VV) : "0" (V), "g" (R), "r" (n) : "cc"); \
+                         VV; })
+#else
+*/
+#define shiftin(V,R,n) ((V<<1)+(((R)>>(n))&1))
+#define rol28(V,n) ((V<<(n) ^ V>>(28-(n)))&0xfffffffL)
+#define ror28(V,n) ((V>>(n) ^ V<<(28-(n)))&0xfffffffL)
+
+#if defined __GNUC__ && __GNUC__ >= 2
+#if defined __i486__ || defined __pentium__ || defined __pentiumpro__ || defined __amd64__
+#define hash(T) ({unsigned int TT; \
+                 __asm__("bswapl %k0\n\trorw $8,%w0\n\tbswapl %k0" : "=g" (TT) : "0" (T) : "cc"); \
+                 TT; })
+#elif defined __i386__
+#define hash(T) ({unsigned int TT; \
+                 __asm__("rorl $16,%k0\n\trorw $8,%w0\n\troll $16,%k0" : "=g" (TT) : "0" (T) : "cc"); \
+                 TT; })
+#endif
+#endif
+#ifndef hash // fallback default
+#define hash(T) ((T&0x0000ffffL) | ((T>>8)&0x00ff0000L) | ((T<<8)&0xff000000L))
+#endif
+
+#define DESROUND(C,D,T) { \
+   unsigned int s=0; \
+   for(int j=7, k=0; j>=0; j--) { \
+     unsigned int v=0, K=0; \
+     for(int t=5; t>=0; t--, k++) { \
+       v=shiftin(v,T,E[k]); \
+       if(PC2[k]<29) K=shiftin(K,C,28-PC2[k]); \
+       else          K=shiftin(K,D,56-PC2[k]); \
+       } \
+     s=(s<<4) + S[7-j][v^K]; \
+     } \
+   T=0; \
+   for(int j=31; j>=0; j--) T=shiftin(T,s,P[j]); \
+   }
+
+#define DESHASH(T) { if(mode&DES_HASH) T=hash(T); }
+
+cDes::cDes(const unsigned char *pc1, const unsigned char *pc2)
+{
+  PC1=pc1 ? pc1 : _PC1;
+  PC2=pc2 ? pc2 : _PC2;
+  for(int i=0; i<48; i++) E[i]=32-_E[i];
+  for(int i=0; i<32; i++) P[i]=32-_P[31-i];
+}
+
+void cDes::Permute(unsigned char *data, const unsigned char *P, int n) const
+{
+  unsigned char pin[8];
+  for(int i=0, k=0; k<n; i++) {
+    int p=0;
+    for(int j=7; j>=0; j--,k++) {
+      const int t=P[k]-1;
+      p=shiftin(p,data[t>>3],7-(t&7));
+      }
+    pin[i]=p;
+    }
+  memcpy(data,pin,8);
+}
+
+void cDes::Des(unsigned char *data, const unsigned char *key, int mode) const
+{
+  unsigned char mkey[8];
+  if(mode&DES_PC1) {
+    memcpy(mkey,key,sizeof(mkey));
+    Permute(mkey,PC1,56);
+    key=mkey;
+    }
+  if(mode&DES_IP) Permute(data,IP,64);
+  unsigned int C=UINT32_BE(key  ) >> 4;
+  unsigned int D=UINT32_BE(key+3) & 0xfffffffL;
+  unsigned int L=UINT32_BE(data  );
+  unsigned int R=UINT32_BE(data+4);
+  if(!(mode&DES_RIGHT)) {
+    for(int i=15; i>=0; i--) {
+      C=rol28(C,LS[15-i]); D=rol28(D,LS[15-i]);
+      unsigned int T=R;
+      if(mode&DES_MOD) T=Mod(T,key[7]); // apply costum mod e.g. Viaccess
+      DESROUND(C,D,T);
+      DESHASH(T);
+      T^=L; L=R; R=T;
+      }
+    }
+  else {
+    for(int i=15; i>=0; i--) {
+      unsigned int T=R;
+      if(mode&DES_MOD) T=Mod(T,key[7]); // apply costum mod e.g. Viaccess
+      DESROUND(C,D,T);
+      DESHASH(T);
+      T^=L; L=R; R=T;
+      C=ror28(C,LS[i]); D=ror28(D,LS[i]);
+      }
+    }
+  BYTE4_BE(data  ,R);
+  BYTE4_BE(data+4,L);
+  if(mode&DES_FP) Permute(data,FP,64);
+}
+
+// -- cAES ---------------------------------------------------------------------
+
+#ifndef OPENSSL_HAS_AES
+#warning ** openssl lacks AES support. Using deprecated static support code. Update your openssl package.
+#include "support/aes_core.c"
+#endif
+
+cAES::cAES(void)
+{
+  active=false;
+}
+
+void cAES::SetKey(const unsigned char *key)
+{
+  AES_set_decrypt_key(key,128,&dkey);
+  AES_set_encrypt_key(key,128,&ekey);
+  active=true;
+}
+
+int cAES::Encrypt(const unsigned char *data, int len, unsigned char *crypt) const
+{
+  if(active) {
+    len=(len+15)&(~15); // pad up to a multiple of 16
+    for(int i=0; i<len; i+=16) AES_encrypt(data+i,crypt+i,(const AES_KEY *)&ekey);
+    return len;
+    }
+  return -1;
+}
+
+void cAES::Decrypt(unsigned char *data, int len) const
+{
+  if(active)
+    for(int i=0; i<len; i+=16) AES_decrypt(data+i,data+i,(const AES_KEY *)&dkey);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/crypto.h
new file mode 100644 (file)
index 0000000..ddc9494
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___CRYPTO_H
+#define ___CRYPTO_H
+
+//
+// NOTE: all crypto classes SHOULD be fully reentrant.
+//       They may be called from different threads concurrently.
+//
+// NOTE: cAES is not fully reentrant. Encrypt/Decrypt are reentrant
+//       (as AES_KEY is const there), but SetKey is not. Be carefull.
+//
+
+#define OPENSSL_ALGORITHM_DEFINES
+#include <openssl/opensslconf.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x0090700fL
+#error Openssl version 0.9.7 or newer is strongly recomended
+#endif
+
+#include "crypto-bn.h"
+
+// ----------------------------------------------------------------
+
+#define DES_LEFT    0
+#define DES_RIGHT   1
+
+#define DES_HASH    8
+#define DES_PC1    16
+#define DES_IP     32
+#define DES_FP     64
+#define DES_MOD   128
+
+// common DES modes
+
+#define PRV_DES_ENCRYPT (DES_LEFT|DES_PC1|DES_IP|DES_FP)
+#define PRV_DES_DECRYPT (DES_RIGHT|DES_PC1|DES_IP|DES_FP)
+
+#define VIA_DES       (DES_LEFT|DES_MOD)
+#define VIA_DES_HASH  (DES_LEFT|DES_HASH|DES_MOD)
+#define SECA_DES_ENCR  PRV_DES_ENCRYPT
+#define SECA_DES_DECR  PRV_DES_DECRYPT
+#define NAGRA_DES_ENCR PRV_DES_ENCRYPT
+#define NAGRA_DES_DECR PRV_DES_DECRYPT
+
+class cDes {
+private:
+  static const unsigned char _E[], _P[], _PC1[], _PC2[], IP[], FP[];
+protected:
+  static const unsigned char S[][64], LS[];
+  const unsigned char *PC1, *PC2;
+  unsigned char E[48], P[32];
+  //
+  virtual unsigned int Mod(unsigned int R, unsigned int key7) const { return R; }
+  void Permute(unsigned char *data, const unsigned char *P, int n) const;
+public:
+  cDes(const unsigned char *pc1=0, const unsigned char *pc2=0);
+  virtual ~cDes() {}
+  void Des(unsigned char *data, const unsigned char *key, int mode) const;
+  };
+
+// ----------------------------------------------------------------
+
+#if defined(OPENSSL_NO_AES) | defined(NO_AES) | OPENSSL_VERSION_NUMBER<0x0090700fL
+#include "support/aes.h"
+#else
+#define OPENSSL_HAS_AES
+#include <openssl/aes.h>
+#endif
+
+class cAES {
+private:
+  bool active;
+  AES_KEY dkey, ekey;
+protected:
+  void SetKey(const unsigned char *key);
+  void Decrypt(unsigned char *data, int len) const ;
+  int Encrypt(const unsigned char *data, int len, unsigned char *crypt) const;
+public:
+  cAES(void);
+  };
+
+// ----------------------------------------------------------------
+
+#if defined(OPENSSL_NO_IDEA) | defined(NO_IDEA) | OPENSSL_VERSION_NUMBER<0x0090700fL
+#include "support/idea.h"
+#else
+#define OPENSSL_HAS_IDEA
+#include <openssl/idea.h>
+#endif
+
+typedef IDEA_KEY_SCHEDULE IdeaKS;
+
+class cIDEA {
+public:
+  // single shot API
+  void Decrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) const;
+  int Encrypt(const unsigned char *data, int len, unsigned char *crypt, const unsigned char *key, unsigned char *iv) const;
+  // multi shot API
+  void SetEncKey(const unsigned char *key, IdeaKS *ks) const;
+  void SetDecKey(const unsigned char *key, IdeaKS *ks) const;
+  void Decrypt(unsigned char *data, int len, IdeaKS *ks, unsigned char *iv) const;
+  int Encrypt(const unsigned char *data, int len, unsigned char *crypt, IdeaKS *ks, unsigned char *iv) const;
+  };
+
+// ----------------------------------------------------------------
+
+class cRSA {
+private:
+  bool Input(cBN *d, const unsigned char *in, int n, bool LE) const;
+  int Output(unsigned char *out, int n, cBN *r, bool LE) const;
+public:
+  int RSA(unsigned char *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+  int RSA(BIGNUM *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+  int RSA(unsigned char *out, int len, BIGNUM *in, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+  };
+
+#endif //___CRYPTO_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.c
new file mode 100644 (file)
index 0000000..73584a5
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "data.h"
+#include "misc.h"
+#include "scsetup.h"
+#include "log-core.h"
+#include "i18n.h"
+
+#define KEY_FILE     "SoftCam.Key"
+#define EXT_AU_INT   (15*60*1000) // ms interval for external AU
+#define EXT_AU_MIN   ( 2*60*1000) // ms min. interval for external AU
+
+// -- cFileMap -----------------------------------------------------------------
+
+cFileMap::cFileMap(const char *Filename, bool Rw)
+{
+  filename=strdup(Filename);
+  rw=Rw;
+  fd=-1; count=len=0; addr=0; failed=false;
+}
+
+cFileMap::~cFileMap()
+{
+  Clean();
+  free(filename);
+}
+
+bool cFileMap::IsFileMap(const char *Name, bool Rw)
+{
+  return (!strcmp(Name,filename) && (!Rw || rw));
+}
+
+void cFileMap::Clean(void)
+{
+  if(addr) { munmap(addr,len); addr=0; len=0; }
+  if(fd>=0) { close(fd); fd=-1; }
+}
+
+bool cFileMap::Map(void)
+{
+  cMutexLock lock(this);
+  if(addr) { count++; return true; }
+  if(!failed) {
+    struct stat64 ds;
+    if(!stat64(filename,&ds)) {
+      if(S_ISREG(ds.st_mode)) {
+        fd=open(filename,rw ? O_RDWR : O_RDONLY);
+        if(fd>=0) {
+          unsigned char *map=(unsigned char *)mmap(0,ds.st_size,rw ? (PROT_READ|PROT_WRITE):(PROT_READ),MAP_SHARED,fd,0);
+          if(map!=MAP_FAILED) {
+            addr=map; len=ds.st_size; count=1;
+            return true;
+            }
+          else PRINTF(L_GEN_ERROR,"mapping failed on %s: %s",filename,strerror(errno));
+          close(fd); fd=-1;
+          }
+        else PRINTF(L_GEN_ERROR,"error opening filemap %s: %s",filename,strerror(errno));
+        }
+      else PRINTF(L_GEN_ERROR,"filemap %s is not a regular file",filename);
+      }
+    else PRINTF(L_GEN_ERROR,"can't stat filemap %s: %s",filename,strerror(errno));
+    failed=true; // don't try this one over and over again
+    }
+  return false;
+}
+
+bool cFileMap::Unmap(void)
+{
+  cMutexLock lock(this);
+  if(addr) {
+    if(!(--count)) { Clean(); return true; }
+    else Sync();
+    }
+  return false;
+}
+
+void cFileMap::Sync(void)
+{
+  cMutexLock lock(this);
+  if(addr) msync(addr,len,MS_ASYNC);
+}
+
+// -- cFileMaps ----------------------------------------------------------------
+
+cFileMaps filemaps;
+
+cFileMaps::cFileMaps(void)
+{
+  cfgDir=0;
+}
+
+cFileMaps::~cFileMaps()
+{
+  Clear();
+  free(cfgDir);
+}
+
+void cFileMaps::SetCfgDir(const char *CfgDir)
+{
+  free(cfgDir);
+  cfgDir=strdup(CfgDir);
+}
+
+cFileMap *cFileMaps::GetFileMap(const char *name, const char *domain, bool rw)
+{
+  cMutexLock lock(this);
+  char path[256];
+  snprintf(path,sizeof(path),"%s/%s/%s",cfgDir,domain,name);
+  cFileMap *fm=First();
+  while(fm) {
+    if(fm->IsFileMap(path,rw)) return fm;
+    fm=Next(fm);
+    }
+  fm=new cFileMap(path,rw);
+  Add(fm);
+  return fm;  
+}
+
+// -- cStructItem --------------------------------------------------------------
+
+cStructItem::cStructItem(void)
+{
+  comment=0; deleted=special=false;
+}
+
+cStructItem::~cStructItem()
+{
+  free(comment);
+}
+
+void cStructItem::SetComment(const char *com)
+{
+  free(comment);
+  comment=com ? strdup(com):0;
+}
+
+bool cStructItem::Save(FILE *f)
+{
+  fprintf(f,"%s%s\n",*ToString(false),comment?comment:"");
+  return ferror(f)==0;
+}
+
+// -- cCommentItem -------------------------------------------------------------
+
+class cCommentItem : public cStructItem {
+public:
+  cCommentItem(void);
+  };
+
+cCommentItem::cCommentItem(void)
+{
+  SetSpecial();
+}
+
+// -- cStructLoader ------------------------------------------------------------
+
+cStructLoader::cStructLoader(const char *Type, const char *Filename, int Flags)
+:lock(true)
+{
+  path=0; mtime=0;
+  type=Type; filename=Filename; flags=Flags&SL_CUSTOMMASK;
+  cStructLoaders::Register(this);
+}
+
+cStructLoader::~cStructLoader()
+{
+  free(path);
+}
+
+void cStructLoader::AddItem(cStructItem *n, const char *com, cStructItem *ref)
+{
+  n->SetComment(com);
+  ListLock(true);
+  cStructItem *a=0;
+  if(ref) { // insert before reference
+    for(a=First(); a; a=Next(a))
+      if(Next(a)==ref) break;
+    }
+  if(!a) { // insert before first non-special
+    for(a=First(); a;) {
+      cStructItem *nn=Next(a);
+      if(nn && !nn->Special()) break;
+      a=nn;
+      }
+    }
+  Add(n,a);
+  Modified();
+  ListUnlock();
+}
+
+void cStructLoader::DelItem(cStructItem *d, bool keep)
+{
+  if(d) {
+    d->Delete();
+    if(keep) {
+      cStructItem *n=new cCommentItem;
+      n->SetComment(cString::sprintf(";%s%s",*d->ToString(false),d->Comment()?d->Comment():""));
+      ListLock(true);
+      Add(n,d);
+      ListUnlock();
+      }
+    Modified();
+    }
+}
+
+cStructItem *cStructLoader::NextValid(cStructItem *it) const
+{
+  while(it && !it->Valid()) it=Next(it);
+  return it;
+}
+
+void cStructLoader::SetCfgDir(const char *cfgdir)
+{
+  free(path);
+  path=strdup(AddDirectory(cfgdir,filename));
+}
+
+time_t cStructLoader::MTime(bool log)
+{
+  struct stat64 st;
+  if(stat64(path,&st)!=0) {
+    if(log) {
+      PRINTF(L_GEN_ERROR,"failed fstat %s: %s",path,strerror(errno));
+      PRINTF(L_GEN_WARN,"automatic reload of %s disabled",path);
+      }
+    st.st_mtime=0;
+    }
+  return st.st_mtime;
+}
+
+void cStructLoader::CheckAccess(void)
+{
+  if(access(path,R_OK|W_OK)!=0) {
+    if(errno!=EACCES)
+      PRINTF(L_GEN_ERROR,"failed access %s: %s",path,strerror(errno));
+    PRINTF(L_GEN_WARN,"no write permission on %s. Changes will not be saved!",path);
+    SL_SETFLAG(SL_NOACCESS);
+    }
+  else SL_CLRFLAG(SL_NOACCESS);
+}
+
+bool cStructLoader::CheckUnmodified(void)
+{
+  time_t curr_mtime=MTime(false);
+  if(mtime && mtime<curr_mtime && SL_TSTFLAG(SL_WATCH)) {
+     PRINTF(L_CORE_LOAD,"abort save as file %s has been changed externaly",path);
+     return false;
+     }
+  return true;
+}
+
+bool cStructLoader::CheckDoSave(void)
+{
+  return !SL_TSTFLAG(SL_DISABLED) && SL_TSTFLAG(SL_READWRITE)
+         && !SL_TSTFLAG(SL_NOACCESS) && SL_TSTFLAG(SL_LOADED)
+         && IsModified() && CheckUnmodified();
+}
+
+void cStructLoader::LoadFinished(void)
+{
+  SL_CLRFLAG(SL_SHUTUP);
+  if(!SL_TSTFLAG(SL_LOADED))
+    PRINTF(L_CORE_LOAD,"loading %s terminated with error. Changes will not be saved!",path);
+}
+
+void cStructLoader::OpenFailed(void)
+{
+  if(SL_TSTFLAG(SL_VERBOSE) && !SL_TSTFLAG(SL_SHUTUP)) {
+    PRINTF(L_GEN_ERROR,"failed open %s: %s",path,strerror(errno));
+    SL_SETFLAG(SL_SHUTUP);
+    }
+  if(SL_TSTFLAG(SL_MISSINGOK)) SL_SETFLAG(SL_LOADED);
+  else SL_CLRFLAG(SL_LOADED);
+}
+
+void cStructLoader::Load(bool reload)
+{
+  if(SL_TSTFLAG(SL_DISABLED) || (reload && !SL_TSTFLAG(SL_WATCH))) return;
+  FILE *f=fopen(path,"r");
+  if(f) {
+    int curr_mtime=MTime(true);
+    ListLock(true);
+    bool doload=false;
+    if(!reload) {
+      Clear(); Modified(false);
+      mtime=curr_mtime;
+      doload=true;
+      }
+    else if(mtime<curr_mtime) {
+      PRINTF(L_CORE_LOAD,"detected change of %s",path);
+      if(IsModified())
+        PRINTF(L_CORE_LOAD,"discarding in-memory changes");
+      for(cStructItem *a=First(); a; a=Next(a)) DelItem(a);
+      Modified(false);
+      mtime=curr_mtime;
+      doload=true;
+      }
+    if(doload) {
+      SL_SETFLAG(SL_LOADED);
+      PRINTF(L_GEN_INFO,"loading %s from %s",type,path);
+      CheckAccess();
+      int lineNum=0, num=0;
+      char buff[4096];
+      while(fgets(buff,sizeof(buff),f)) {
+        lineNum++;
+        if(!index(buff,'\n') && !feof(f)) {
+          PRINTF(L_GEN_ERROR,"file %s readbuffer overflow line#%d",path,lineNum);
+          SL_CLRFLAG(SL_LOADED);
+          break;
+          }
+        strreplace(buff,'\n',0); strreplace(buff,'\r',0); // chomp
+        bool hasContent=false;
+        char *ls;
+        for(ls=buff; *ls; ls++) {
+          if(*ls==';' || *ls=='#') {             // comment
+            if(hasContent)
+              while(ls>buff && ls[-1]<=' ') ls--; // search back to non-whitespace
+            break;
+            }
+          if(*ls>' ') hasContent=true;           // line contains something usefull
+          }
+        cStructItem *it=0;
+        if(hasContent) {
+          char save=*ls;
+          *ls=0; it=ParseLine(skipspace(buff)); *ls=save;
+          if(!it) {
+            PRINTF(L_GEN_ERROR,"file %s has error in line #%d",path,lineNum);
+            ls=buff;
+            }
+          else num++;
+          }
+        else ls=buff;
+        if(!it) it=new cCommentItem;
+        if(it) {
+          it->SetComment(ls);
+          Add(it);
+          }
+        else {
+          PRINTF(L_GEN_ERROR,"out of memory loading file %s",path);
+          SL_CLRFLAG(SL_LOADED);
+          break;
+          }
+        }
+      ListUnlock();
+      PRINTF(L_CORE_LOAD,"loaded %d %s from %s",num,type,path);
+      PostLoad();
+      }
+    else ListUnlock();
+
+    fclose(f);
+    LoadFinished();
+    }
+  else
+    OpenFailed();
+}
+
+void cStructLoader::Purge(void)
+{
+  if(!SL_TSTFLAG(SL_DISABLED) && !SL_TSTFLAG(SL_NOPURGE)) {
+    ListLock(true);
+    for(cStructItem *it=First(); it;) {
+      cStructItem *n=Next(it);
+      if(it->Deleted()) Del(it);
+      it=n;
+      }
+    ListUnlock();
+    }
+}
+
+void cStructLoader::Save(void)
+{
+  if(CheckDoSave()) {
+    cSafeFile f(path);
+    if(f.Open()) {
+      ListLock(false);
+      for(cStructItem *it=First(); it; it=Next(it))
+        if(!it->Deleted() && !it->Save(f)) break;
+      f.Close();
+      mtime=MTime(true);
+      Modified(false);
+      ListUnlock();
+      PRINTF(L_CORE_LOAD,"saved %s to %s",type,path);
+      }
+    }
+}
+
+// -- cStructLoaderPlain -------------------------------------------------------
+
+cStructLoaderPlain::cStructLoaderPlain(const char *Type, const char *Filename, int Flags)
+:cStructLoader(Type,Filename,Flags)
+{}
+
+void cStructLoaderPlain::PreLoad(void)
+{
+  ListLock(true);
+  Clear(); Modified(false);
+  ListUnlock();
+}
+
+void cStructLoaderPlain::Load(bool reload)
+{
+  if(SL_TSTFLAG(SL_DISABLED) || reload) return;
+  FILE *f=fopen(path,"r");
+  if(f) {
+    PreLoad();
+    ListLock(true);
+    SL_SETFLAG(SL_LOADED);
+    PRINTF(L_GEN_INFO,"loading %s from %s",type,path);
+    CheckAccess();
+    int lineNum=0;
+    char buff[4096];
+    while(fgets(buff,sizeof(buff),f)) {
+      lineNum++;
+      if(!index(buff,'\n') && !feof(f)) {
+        PRINTF(L_GEN_ERROR,"file %s readbuffer overflow line#%d",path,lineNum);
+        SL_CLRFLAG(SL_LOADED);
+        break;
+        }
+      strreplace(buff,'\n',0); strreplace(buff,'\r',0); // chomp
+      bool hasContent=false;
+      char *ls;
+      for(ls=buff; *ls; ls++) {
+        if(*ls==';' || *ls=='#') break;
+        if(*ls>' ') hasContent=true;
+        }
+      if(hasContent) {
+        *ls=0;
+        if(!ParseLinePlain(skipspace(buff)))
+          PRINTF(L_GEN_ERROR,"file %s has error in line #%d",path,lineNum);
+        }
+      }
+    ListUnlock();
+    PostLoad();
+    fclose(f);
+    LoadFinished();
+    }
+  else
+    OpenFailed();
+}
+
+void cStructLoaderPlain::PreSave(FILE *f)
+{
+  fprintf(f,"## This is a generated file. DO NOT EDIT!!\n"
+            "## This file will be OVERWRITTEN WITHOUT WARNING!!\n");
+}
+
+void cStructLoaderPlain::Save(void)
+{
+  if(CheckDoSave()) {
+    cSafeFile f(path);
+    if(f.Open()) {
+      ListLock(false);
+      PreSave(f);
+      for(cStructItem *it=First(); it; it=Next(it))
+        if(!it->Deleted() && !it->Save(f)) break;
+      PostSave(f);
+      f.Close();
+      Modified(false);
+      ListUnlock();
+      PRINTF(L_CORE_LOAD,"saved %s to %s",type,path);
+      }
+    }
+}
+
+// -- cStructLoaders -----------------------------------------------------------
+
+#define RELOAD_TIMEOUT  20300 // ms
+#define PURGE_TIMEOUT   60700 // ms
+#define SAVE_TIMEOUT     5000 // ms
+
+cStructLoader *cStructLoaders::first=0;
+cTimeMs cStructLoaders::lastReload;
+cTimeMs cStructLoaders::lastPurge;
+cTimeMs cStructLoaders::lastSave;
+
+void cStructLoaders::Register(cStructLoader *ld)
+{
+  PRINTF(L_CORE_DYN,"structloaders: registering loader %s",ld->type);
+  ld->next=first;
+  first=ld;
+}
+
+void cStructLoaders::SetCfgDir(const char *cfgdir)
+{
+  for(cStructLoader *ld=first; ld; ld=ld->next)
+    ld->SetCfgDir(cfgdir);
+}
+
+void cStructLoaders::Load(bool reload)
+{
+  if(!reload || lastReload.TimedOut()) {
+    for(cStructLoader *ld=first; ld; ld=ld->next) ld->Load(reload);
+    lastReload.Set(RELOAD_TIMEOUT);
+    }
+}
+
+void cStructLoaders::Save(bool force)
+{
+  if(force || lastSave.TimedOut()) {
+    for(cStructLoader *ld=first; ld; ld=ld->next) ld->Save();
+    lastSave.Set(SAVE_TIMEOUT);
+    }
+}
+
+void cStructLoaders::Purge(void)
+{
+  if(lastPurge.TimedOut()) {
+    for(cStructLoader *ld=first; ld; ld=ld->next) ld->Purge();
+    lastPurge.Set(PURGE_TIMEOUT);
+    }
+}
+
+// -- cPid ---------------------------------------------------------------------
+
+cPid::cPid(int Pid, int Section, int Mask, int Mode)
+{
+  pid=Pid;
+  sct=Section;
+  mask=Mask;
+  mode=Mode;
+  filter=0;
+}
+
+// -- cPids --------------------------------------------------------------------
+
+void cPids::AddPid(int Pid, int Section, int Mask, int Mode)
+{
+  if(!HasPid(Pid,Section,Mask,Mode)) {
+    cPid *pid=new cPid(Pid,Section,Mask,Mode);
+    Add(pid);
+    }
+}
+
+bool cPids::HasPid(int Pid, int Section, int Mask, int Mode)
+{
+  for(cPid *pid=First(); pid; pid=Next(pid))
+    if(pid->pid==Pid && pid->sct==Section && pid->mask==Mask && pid->mode==Mode)
+      return true;
+  return false;
+}
+
+// -- cEcmInfo -----------------------------------------------------------------
+
+cEcmInfo::cEcmInfo(void)
+{
+  Setup();
+}
+
+cEcmInfo::cEcmInfo(const cEcmInfo *e)
+{
+  Setup(); SetName(e->name);
+  ecm_pid=e->ecm_pid;
+  ecm_table=e->ecm_table;
+  caId=e->caId;
+  emmCaId=e->emmCaId;
+  provId=e->provId;
+  Update(e);
+  prgId=e->prgId;
+  source=e->source;
+  transponder=e->transponder;
+}
+
+cEcmInfo::cEcmInfo(const char *Name, int Pid, int CaId, int ProvId)
+{
+  Setup(); SetName(Name);
+  ecm_pid=Pid;
+  caId=CaId;
+  provId=ProvId;
+}
+
+cEcmInfo::~cEcmInfo()
+{
+  ClearData();
+  free(name);
+}
+
+void cEcmInfo::Setup(void)
+{
+  cached=failed=false;
+  name=0; data=0;
+  prgId=source=transponder=-1;
+  ecm_table=0x80; emmCaId=0;
+}
+
+bool cEcmInfo::Compare(const cEcmInfo *e)
+{
+  return prgId==e->prgId && source==e->source && transponder==e->transponder &&
+         caId==e->caId && ecm_pid==e->ecm_pid && provId==e->provId;
+}
+
+bool cEcmInfo::Update(const cEcmInfo *e)
+{
+  return (e->data && (!data || e->dataLen!=dataLen)) ? AddData(e->data,e->dataLen) : false;
+}
+
+void cEcmInfo::SetSource(int PrgId, int Source, int Transponder)
+{
+  prgId=PrgId;
+  source=Source;
+  transponder=Transponder;
+}
+
+void cEcmInfo::ClearData(void)
+{
+  free(data); data=0;
+}
+
+bool cEcmInfo::AddData(const unsigned char *Data, int DataLen)
+{
+  ClearData();
+  data=MALLOC(unsigned char,DataLen);
+  if(data) {
+    memcpy(data,Data,DataLen);
+    dataLen=DataLen;
+    }
+  else PRINTF(L_GEN_ERROR,"malloc failed in cEcmInfo::AddData()");
+  return (data!=0);
+}
+
+void cEcmInfo::SetName(const char *Name)
+{
+  free(name);
+  name=strdup(Name);
+}
+
+// -- cPlainKey ----------------------------------------------------------------
+
+cPlainKey::cPlainKey(bool CanSupersede)
+{
+  super=CanSupersede;
+}
+
+bool cPlainKey::Set(int Type, int Id, int Keynr, void *Key, int Keylen)
+{
+  type=Type; id=Id; keynr=Keynr;
+  return SetKey(Key,Keylen);
+}
+
+cString cPlainKey::PrintKeyNr(void)
+{
+  return cString::sprintf("%02X",keynr);
+}
+
+int cPlainKey::IdSize(void)
+{
+  return id>0xFF ? (id>0xFFFF ? 6 : 4) : 2;
+}
+
+cString cPlainKey::ToString(bool hide)
+{
+  return cString::sprintf(hide ? "%c %.*X %s %.4s..." : "%c %.*X %s %s",type,IdSize(),id,*PrintKeyNr(),*Print());
+}
+
+// -- cMutableKey --------------------------------------------------------------
+
+cMutableKey::cMutableKey(bool Super)
+:cPlainKey(Super)
+{
+  real=0;
+}
+
+cMutableKey::~cMutableKey()
+{
+  delete real;
+}
+
+bool cMutableKey::SetKey(void *Key, int Keylen)
+{
+  delete real;
+  return (real=Alloc()) && real->SetKey(Key,Keylen);
+}
+
+bool cMutableKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+  delete real;
+  return (real=Alloc()) && real->SetBinKey(Mem,Keylen);
+}
+
+int cMutableKey::Size(void)
+{
+  return real->Size();
+}
+
+bool cMutableKey::Cmp(void *Key, int Keylen)
+{
+  return real->Cmp(Key,Keylen);
+}
+
+bool cMutableKey::Cmp(cPlainKey *k)
+{
+  cMutableKey *mk=dynamic_cast<cMutableKey *>(k); // high magic ;)
+  return real->Cmp(mk ? mk->real : k);
+}
+
+void cMutableKey::Get(void *mem)
+{
+  real->Get(mem);
+}
+
+cString cMutableKey::Print(void)
+{
+  return real->Print();
+}
+
+// ----------------------------------------------------------------
+
+class cPlainKeyDummy : public cPlainKey {
+private:
+  char *str;
+  int len;
+protected:
+  virtual cString Print(void);
+  virtual cString PrintKeyNr(void) { return ""; }
+public:
+  cPlainKeyDummy(void);
+  ~cPlainKeyDummy();
+  virtual bool Parse(const char *line);
+  virtual bool Cmp(void *Key, int Keylen) { return false; }
+  virtual bool Cmp(cPlainKey *k) { return false; }
+  virtual void Get(void *mem) {}
+  virtual int Size(void) { return len; }
+  virtual bool SetKey(void *Key, int Keylen);
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+  };
+
+cPlainKeyDummy::cPlainKeyDummy(void):
+cPlainKey(false)
+{
+  str=0;
+}
+
+cPlainKeyDummy::~cPlainKeyDummy()
+{
+  free(str);
+}
+
+bool cPlainKeyDummy::SetKey(void *Key, int Keylen)
+{
+  len=Keylen;
+  free(str);
+  str=MALLOC(char,len+1);
+  if(str) strn0cpy(str,(char *)Key,len+1);
+  return str!=0;
+}
+
+bool cPlainKeyDummy::SetBinKey(unsigned char *Mem, int Keylen)
+{
+  return SetKey(Mem,Keylen);
+}
+
+cString cPlainKeyDummy::Print(void)
+{
+  return str;
+}
+
+bool cPlainKeyDummy::Parse(const char *line)
+{
+  unsigned char sid[3];
+  int len;
+  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) {
+    type=toupper(type); id=Bin2Int(sid,len);
+    char *l=strdup(line);
+    line=skipspace(stripspace(l));
+    SetKey((void *)line,strlen(line));
+    free(l);
+    return true;
+    }
+  return false;
+}
+
+// -- cPlainKeyTypeDummy -------------------------------------------------------
+
+class cPlainKeyTypeDummy : public cPlainKeyType {
+public:
+  cPlainKeyTypeDummy(int Type):cPlainKeyType(Type,false) {}
+  virtual cPlainKey *Create(void) { return new cPlainKeyDummy; }
+  };
+
+// -- cPlainKeyType ------------------------------------------------------------
+
+cPlainKeyType::cPlainKeyType(int Type, bool Super)
+{
+  type=Type;
+  cPlainKeys::Register(this,Super);
+}
+
+// -- cLastKey -----------------------------------------------------------------
+
+cLastKey::cLastKey(void)
+{
+  lastType=lastId=lastKeynr=-1;
+}
+
+bool cLastKey::NotLast(int Type, int Id, int Keynr)
+{
+  if(lastType!=Type || lastId!=Id || lastKeynr!=Keynr) {
+    lastType=Type; lastId=Id; lastKeynr=Keynr;
+    return true;
+    }
+  return false;
+}
+
+// -- cPlainKeys ---------------------------------------------------------------
+
+const char *externalAU=0;
+
+cPlainKeys keys;
+
+cPlainKeyType *cPlainKeys::first=0;
+
+cPlainKeys::cPlainKeys(void)
+:cStructList<cPlainKey>("keys",KEY_FILE,SL_READWRITE|SL_MISSINGOK|SL_WATCH|SL_VERBOSE)
+{}
+
+void cPlainKeys::Register(cPlainKeyType *pkt, bool Super)
+{
+  PRINTF(L_CORE_DYN,"registering key type %c%s",pkt->type,Super?" (super)":"");
+  pkt->next=first;
+  first=pkt;
+}
+
+void cPlainKeys::Trigger(int Type, int Id, int Keynr)
+{
+  if(lastkey.NotLast(Type,Id,Keynr))
+    PRINTF(L_CORE_AUEXTERN,"triggered from findkey (%s)",*KeyString(Type,Id,Keynr));
+  ExternalUpdate();
+}
+
+cPlainKey *cPlainKeys::FindKey(int Type, int Id, int Keynr, int Size, cPlainKey *key)
+{
+  key=FindKeyNoTrig(Type,Id,Keynr,Size,key);
+  if(!key) Trigger(Type,Id,Keynr);
+  return key;
+}
+
+cPlainKey *cPlainKeys::FindKeyNoTrig(int Type, int Id, int Keynr, int Size, cPlainKey *key)
+{
+  ListLock(false);
+  for(key=key?Next(key):First(); key; key=Next(key))
+    if(key->type==Type && key->id==Id && key->keynr==Keynr && (Size<0 || key->Size()==Size))
+      break;
+  ListUnlock();
+  return key;
+}
+
+cPlainKey *cPlainKeys::NewFromType(int type)
+{
+  cPlainKeyType *pkt;
+  for(pkt=first; pkt; pkt=pkt->next)
+    if(pkt->type==type) return pkt->Create();
+  PRINTF(L_CORE_LOAD,"unknown key type '%c', adding dummy",type);
+  pkt=new cPlainKeyTypeDummy(type);
+  return pkt->Create();
+}
+
+bool cPlainKeys::AddNewKey(cPlainKey *nk, const char *reason)
+{
+  cPlainKey *k;
+  for(k=0; (k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,-1,k)); )
+    if(k->Cmp(nk)) return false;
+
+  cPlainKey *ref=0;
+  cString ks=nk->ToString(true);
+  PRINTF(L_GEN_INFO,"key update for ID %s",*ks);
+  ums.Queue("%s %s",tr("Key update"),*ks);
+  for(k=0; (k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,nk->Size(),k)); ) {
+    if(nk->CanSupersede()) {
+      PRINTF(L_GEN_INFO,"supersedes key: %s",*k->ToString(true));
+      DelItem(k,ScSetup.SuperKeys==0);
+      }
+    if(!ref) ref=k;
+    }
+  char stamp[32], com[256];
+  time_t tt=time(0);
+  struct tm tm_r;
+  strftime(stamp,sizeof(stamp),"%d.%m.%Y %T",localtime_r(&tt,&tm_r));
+  snprintf(com,sizeof(com)," ; %s %s",reason,stamp);
+  AddItem(nk,com,ref);
+  return true;
+}
+
+bool cPlainKeys::NewKey(int Type, int Id, int Keynr, void *Key, int Keylen)
+{
+  cPlainKey *nk=NewFromType(Type);
+  if(nk) {
+    nk->Set(Type,Id,Keynr,Key,Keylen);
+    return AddNewKey(nk,"from AU");
+    }
+  return false;
+}
+
+bool cPlainKeys::NewKeyParse(char *line, const char *reason)
+{
+  cPlainKey *nk=ParseLine(line);
+  return nk && AddNewKey(nk,reason);
+}
+
+cPlainKey *cPlainKeys::ParseLine(char *line)
+{
+  char *s=skipspace(line);
+  cPlainKey *k=NewFromType(toupper(*s));
+  if(k && !k->Parse(line)) { delete k; k=0; }
+  return k;
+}
+
+cString cPlainKeys::KeyString(int Type, int Id, int Keynr)
+{
+  cPlainKey *pk=NewFromType(Type);
+  char *s;
+  if(pk) {
+    pk->type=Type; pk->id=Id; pk->keynr=Keynr;
+    asprintf(&s,"%c %.*X %s",Type,pk->IdSize(),Id,*pk->PrintKeyNr());
+    delete pk;
+    }
+  else s=strdup("unknown");
+  return cString(s,true);
+}
+
+void cPlainKeys::PostLoad(void)
+{
+  ListLock(false);
+  if(Count() && LOG(L_CORE_KEYS)) {
+    for(cPlainKey *dat=First(); dat; dat=Next(dat))
+      PRINTF(L_CORE_KEYS,"keys %s",*dat->ToString(false));
+    }
+  ListUnlock();
+}
+
+void cPlainKeys::HouseKeeping(void)
+{
+  if(trigger.TimedOut()) {
+    trigger.Set(EXT_AU_INT);
+    if(externalAU) PRINTF(L_CORE_AUEXTERN,"triggered from housekeeping");
+    ExternalUpdate();
+    }
+}
+
+void cPlainKeys::ExternalUpdate(void)
+{
+  if(externalAU && ScSetup.AutoUpdate>0) {
+    if(last.TimedOut()) {
+      Lock();
+      if(!Active())
+        Start();
+      else PRINTF(L_CORE_AUEXTERN,"still running");
+      Unlock();
+      }
+    else PRINTF(L_CORE_AUEXTERN,"denied, min. timeout not expired");
+    }
+}
+
+void cPlainKeys::Action(void)
+{
+  last.Set(EXT_AU_MIN);
+  PRINTF(L_CORE_AUEXTERN,"starting...");
+  cTimeMs start;
+  cPipe pipe;
+  if(pipe.Open(externalAU,"r")) {
+    char buff[1024];
+    while(fgets(buff,sizeof(buff),pipe)) {
+      char *line=skipspace(stripspace(buff));
+      if(line[0]==0 || line[0]==';' || line[0]=='#') continue;
+      NewKeyParse(line,"from ExtAU");
+      }
+    }
+  pipe.Close();
+  PRINTF(L_CORE_AUEXTERN,"done (elapsed %d)",(int)start.Elapsed());
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/data.h
new file mode 100644 (file)
index 0000000..eba9719
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___DATA_H
+#define ___DATA_H
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#include "misc.h"
+
+class cStructLoaders;
+class cLoaders;
+class cPidFilter;
+class cPlainKeys;
+
+// ----------------------------------------------------------------
+
+class cFileMap : public cSimpleItem, private cMutex {
+private:
+  char *filename;
+  bool rw;
+  //
+  int fd, len, count;
+  unsigned char *addr;
+  bool failed;
+  //
+  void Clean(void);
+public:
+  cFileMap(const char *Filename, bool Rw);
+  ~cFileMap();
+  bool Map(void);
+  bool Unmap(void);
+  void Sync(void);
+  int Size(void) const { return len; }
+  unsigned char *Addr(void) const { return addr; }
+  bool IsFileMap(const char *Name, bool Rw);
+  };
+
+// ----------------------------------------------------------------
+
+class cFileMaps : public cSimpleList<cFileMap>, private cMutex {
+private:
+  char *cfgDir;
+public:
+  cFileMaps(void);
+  ~cFileMaps();
+  void SetCfgDir(const char *CfgDir);
+  cFileMap *GetFileMap(const char *name, const char *domain, bool rw);
+  };
+
+extern cFileMaps filemaps;
+
+//--------------------------------------------------------------
+
+class cStructItem : public cSimpleItem {
+private:
+  char *comment;
+  bool deleted, special;
+protected:
+  void SetSpecial(void) { special=true; }
+public:
+  cStructItem(void);
+  virtual ~cStructItem();
+  virtual cString ToString(bool hide) { return ""; }
+  bool Save(FILE *f);
+  //
+  void SetComment(const char *com);
+  const char *Comment(void) const { return comment; }
+  void Delete(void) { deleted=true; }
+  bool Deleted(void) const { return deleted; }
+  bool Special(void) const { return special; }
+  bool Valid(void) const { return !deleted && !special; }
+  };
+
+//--------------------------------------------------------------
+
+#define SL_READWRITE 1
+#define SL_MISSINGOK 2
+#define SL_WATCH     4
+#define SL_VERBOSE   8
+#define SL_NOPURGE   16
+#define SL_CUSTOMMASK 0xFF
+#define SL_LOADED    0x100
+#define SL_MODIFIED  0x200
+#define SL_DISABLED  0x400
+#define SL_SHUTUP    0x800
+#define SL_NOACCESS  0x1000
+
+#define SL_SETFLAG(x) flags|=(x)
+#define SL_CLRFLAG(x) flags&=~(x)
+#define SL_TSTFLAG(x) (flags&(x))
+
+class cStructLoader : public cSimpleList<cStructItem> {
+friend class cStructLoaders;
+private:
+  cStructLoader *next;
+  cRwLock lock;
+  //
+protected:
+  int flags;
+  const char *type, *filename;
+  char *path;
+  time_t mtime;
+  //
+  void CheckAccess(void);
+  bool CheckUnmodified(void);
+  void LoadFinished(void);
+  void OpenFailed(void);
+  bool CheckDoSave(void);
+  time_t MTime(bool log);
+  //
+  virtual cStructItem *ParseLine(char *line)=0;
+  void Modified(bool mod=true) { if(mod) SL_SETFLAG(SL_MODIFIED); else SL_CLRFLAG(SL_MODIFIED); }
+  bool IsModified(void) const { return SL_TSTFLAG(SL_MODIFIED); }
+  void ListLock(bool rw) { lock.Lock(rw); }
+  void ListUnlock(void) { lock.Unlock(); }
+  virtual void PostLoad(void) {}
+public:
+  cStructLoader(const char *Type, const char *Filename, int Flags);
+  virtual ~cStructLoader();
+  void AddItem(cStructItem *n, const char *com, cStructItem *ref);
+  void DelItem(cStructItem *d, bool keep=false);
+  cStructItem *NextValid(cStructItem *it) const;
+  //
+  void SetCfgDir(const char *cfgdir);
+  virtual void Load(bool reload);
+  virtual void Save(void);
+  void Purge(void);
+  void Disable(void) { SL_SETFLAG(SL_DISABLED); }
+  };
+
+//--------------------------------------------------------------
+
+template<class T> class cStructList : public cStructLoader {
+public:
+  cStructList<T>(const char *Type, const char *Filename, int Flags):cStructLoader(Type,Filename,Flags) {}
+  T *First(void) const { return (T *)NextValid(cStructLoader::First()); }
+  T *Next(const T *item) const { return (T *)NextValid(cStructLoader::Next(item)); }
+  };
+
+//--------------------------------------------------------------
+
+class cStructLoaderPlain : public cStructLoader {
+protected:
+  virtual cStructItem *ParseLine(char *line) { return 0; }
+  virtual bool ParseLinePlain(const char *line)=0;
+  virtual void PreLoad(void);
+  virtual void PreSave(FILE *f);
+  virtual void PostSave(FILE *f) {};
+public:
+  cStructLoaderPlain(const char *Type, const char *Filename, int Flags);
+  virtual void Load(bool reload);
+  virtual void Save(void);
+  };
+
+//--------------------------------------------------------------
+
+template<class T> class cStructListPlain : public cStructLoaderPlain {
+public:
+  cStructListPlain<T>(const char *Type, const char *Filename, int Flags):cStructLoaderPlain(Type,Filename,Flags) {}
+  T *First(void) const { return (T *)NextValid(cStructLoaderPlain::First()); }
+  T *Next(const T *item) const { return (T *)NextValid(cStructLoaderPlain::Next(item)); }
+  };
+
+//--------------------------------------------------------------
+
+class cStructLoaders {
+friend class cStructLoader;
+private:
+  static cStructLoader *first;
+  static cTimeMs lastReload, lastPurge, lastSave;
+  //
+  static void Register(cStructLoader *ld);
+public:
+  static void SetCfgDir(const char *cfgdir);
+  static void Load(bool reload);
+  static void Save(bool force=false);
+  static void Purge(void);
+  };
+
+// ----------------------------------------------------------------
+
+class cPid : public cSimpleItem {
+public:
+  int pid, sct, mask, mode;
+  cPidFilter *filter;
+  //
+  cPid(int Pid, int Section, int Mask, int Mode);
+  };
+
+// ----------------------------------------------------------------
+
+class cPids : public cSimpleList<cPid> {
+public:
+  void AddPid(int Pid, int Section, int Mask, int Mode=0);
+  bool HasPid(int Pid, int Section, int Mask, int Mode=0);
+  };
+
+// ----------------------------------------------------------------
+
+class cEcmInfo : public cStructItem {
+private:
+  bool cached, failed;
+  //
+  void Setup(void);
+protected:
+  int dataLen;
+  unsigned char *data;
+  //
+  void ClearData(void);
+public:
+  char *name;
+  int ecm_pid, ecm_table;
+  int caId, provId, emmCaId;
+  int prgId, source, transponder;
+  //
+  cEcmInfo(void);
+  cEcmInfo(const cEcmInfo *e);
+  cEcmInfo(const char *Name, int Pid, int CaId, int ProvId);
+  ~cEcmInfo();
+  virtual cString ToString(bool hide=false) { return ""; }
+  bool Compare(const cEcmInfo *e);
+  bool Update(const cEcmInfo *e);
+  void SetSource(int PrgId, int Source, int Transponder);
+  void SetName(const char *Name);
+  bool AddData(const unsigned char *Data, int DataLen);
+  const unsigned char *Data(void) const { return data; }
+  void Fail(bool st) { failed=st; }
+  bool Failed(void) const { return failed; }
+  void SetCached(void) { cached=true; }
+  bool Cached(void) const { return cached; }
+  };
+
+// ----------------------------------------------------------------
+
+class cMutableKey;
+
+class cPlainKey : public cStructItem {
+friend class cPlainKeys;
+friend class cMutableKey;
+private:
+  bool super;
+protected:
+  void SetSupersede(bool val) { super=val; }
+  bool CanSupersede(void) const { return super; }
+  virtual int IdSize(void);
+  virtual cString Print(void)=0;
+  virtual cString PrintKeyNr(void);
+public:
+  int type, id, keynr;
+  //
+  cPlainKey(bool CanSupersede);
+  virtual bool Parse(const char *line)=0;
+  virtual cString ToString(bool hide);
+  virtual bool Cmp(void *Key, int Keylen)=0;
+  virtual bool Cmp(cPlainKey *k)=0;
+  virtual void Get(void *mem)=0;
+  virtual int Size(void)=0;
+  bool Set(int Type, int Id, int Keynr, void *Key, int Keylen);
+  virtual bool SetKey(void *Key, int Keylen)=0;
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen)=0;
+  };
+
+// ----------------------------------------------------------------
+
+class cMutableKey : public cPlainKey {
+private:
+  cPlainKey *real;
+protected:
+  virtual cString Print(void);
+  virtual cPlainKey *Alloc(void) const=0;
+public:
+  cMutableKey(bool Super);
+  virtual ~cMutableKey();
+  virtual bool Cmp(void *Key, int Keylen);
+  virtual bool Cmp(cPlainKey *k);
+  virtual void Get(void *mem);
+  virtual int Size(void);
+  virtual bool SetKey(void *Key, int Keylen);
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+  };
+
+// ----------------------------------------------------------------
+
+class cPlainKeyType {
+friend class cPlainKeys;
+private:
+  int type;
+  cPlainKeyType *next;
+public:
+  cPlainKeyType(int Type, bool Super);
+  virtual ~cPlainKeyType() {}
+  virtual cPlainKey *Create(void)=0;
+  };
+
+// ----------------------------------------------------------------
+
+template<class PKT, int KT, bool SUP=true> class cPlainKeyTypeReg : public cPlainKeyType {
+public:
+  cPlainKeyTypeReg(void):cPlainKeyType(KT,SUP) {}
+  virtual cPlainKey *Create(void) { return new PKT(SUP); }
+  };
+
+// ----------------------------------------------------------------
+
+class cLastKey {
+private:
+  int lastType, lastId, lastKeynr;
+public:
+  cLastKey(void);
+  bool NotLast(int Type, int Id, int Keynr);
+  };
+
+// ----------------------------------------------------------------
+
+extern const char *externalAU;
+
+class cPlainKeys : private cThread, public cStructList<cPlainKey> {
+friend class cPlainKeyType;
+private:
+  static cPlainKeyType *first;
+  cTimeMs trigger, last;
+  cLastKey lastkey;
+  //
+  static void Register(cPlainKeyType *pkt, bool Super);
+  cPlainKey *NewFromType(int type);
+  bool AddNewKey(cPlainKey *nk, const char *reason);
+  void ExternalUpdate(void);
+protected:
+  virtual void Action(void);
+  virtual void PostLoad(void);
+public:
+  cPlainKeys(void);
+  virtual cPlainKey *ParseLine(char *line);
+  cPlainKey *FindKey(int Type, int Id, int Keynr, int Size, cPlainKey *key=0);
+  cPlainKey *FindKeyNoTrig(int Type, int Id, int Keynr, int Size, cPlainKey *key=0);
+  void Trigger(int Type, int Id, int Keynr);
+  cString KeyString(int Type, int Id, int Keynr);
+  bool NewKey(int Type, int Id, int Keynr, void *Key, int Keylen);
+  bool NewKeyParse(char *line, const char *reason);
+  void HouseKeeping(void);
+  };
+
+extern cPlainKeys keys;
+
+#endif //___DATA_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/diff.exclude b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/diff.exclude
new file mode 100644 (file)
index 0000000..36161cd
--- /dev/null
@@ -0,0 +1,16 @@
+patches
+systems-pre
+.dependencies
+diff.exclude
+*.so
+*.a
+*.o
+*.save
+*.orig
+*.rej
+*.bak
+*.mo
+*.pot
+dump.txt
+.hg
+version.c
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Ird-Beta.KID b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Ird-Beta.KID
new file mode 100644 (file)
index 0000000..3598b4a
--- /dev/null
@@ -0,0 +1,12 @@
+; Irdeto/Betacrypt cards\r
+;\r
+; AAAAAA BBBBBBBBBBBBBBBBBBBB CC DDDDDD EEEEEEEEEEEEEEEE\r
+;\r
+; AAAAAA               - HS hex serial\r
+; BBBBBBBBBBBBBBBBBBBB - HMK hex master key\r
+; CC                   - provider\r
+; DDDDDD               - provider ID\r
+; EEEEEEEEEEEEEEEE     - PMK plain master key\r
+;\r
+123456 12345612345612345612 00 123456 1234567890123456 ; dummy\r
+\r
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Seca.KID b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Seca.KID
new file mode 100644 (file)
index 0000000..65edc54
--- /dev/null
@@ -0,0 +1,9 @@
+; Seca cards\r
+;\r
+; AAAA BBBBBBBB CCCCCCCCCCCCCCCC\r
+;\r
+; AAAA             - provider ident\r
+; BBBBBB           - shared address\r
+; CCCCCCCCCCCCCCCC - key 01\r
+;\r
+1234 123456 1234561234561234 ; dummy\r
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/SoftCam.Key b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/SoftCam.Key
new file mode 100644 (file)
index 0000000..7038809
--- /dev/null
@@ -0,0 +1,108 @@
+;
+; Irdeto/Betacrypt
+;
+; I XX YY <16 characters>
+;
+; XX               - provider
+; YY               - key number
+;
+
+;
+; Seca
+;
+; S XXXX YY <16 characters/32 characters>
+; S XXXX [EMM] Mz <180 characters>
+; S XXXX [EMM] Ez <12 characters/180 characters>
+; S XXXX N51 M9 <258 characters>
+; S XXXX N51 E9 <2 characters>
+;
+; XXXX             - provider ident
+; YY               - key number
+; Mz / Ez          - RSA key name (z = 1,3,5).
+;                    Exponent keys may be padded up to 180 chars.
+;                    Optional "EMM" for EMM RSA keys.
+; N51              - RSA keys for Nano 5109 handling.
+;
+
+;
+; Viaccess
+;
+; V XXXXXX YY <16 characters/32 characters>
+; V XXXXXX TPS <32 characters>                ; old TPS keys. no longer used
+; V XXXXXX TPSMKy <32 characters>             ; TPS masterkeys for AU y=0-7
+;
+; XXXXXX           - provider ident
+; YY               - key number
+;
+
+;
+; Nagra 1
+;
+; N XXXX YY <16 characters>
+; N XXXX E1 [ROMq] [PKv] [TYPw] <128 characters>
+; N XXXX N1 [ROMq] [PKv] [TYPw] <128 characters>
+; N XXXX N2 [ROMq] [TYPw] <128 characters>
+; N XXXX V  [ROMq] [TYPw] <16 characters>
+;
+; Nagra 2
+;
+; N XXXX YY <32 characters>           ; IDEA opkey (00/01)
+; N XXXX V  <32 characters>           ; IDEA verify key
+; N XXXX M1 <128 characters>          ; RSA key
+; N XXXX NN ZZ <32/48/192 characters> ; EMM keys (literal 'NN'), some examples:
+; N XXXX NN 02 <32/48 characters>     ; EMM-G IDEA key (former N XXXX 02)
+; N XXXX NN 03 <32 characters>        ; EMM-G signature key
+; N XXXX NN 12 <192 characters>       ; EMM-G RSA key  (former N XXXX N2)
+; N XXXX NN 30 <32 characters>        ; EMM-G 3DES key
+;
+; XXXX             - provider ident
+; YY               - key number
+; ZZ               - EEPROM key number
+; ROMq             - (optional) ROM specific keys (q = ROM number)
+; PKv              - (optional) PK key number (v = 0,1,2)
+; TYPw             - (optional) key type number (w = 0,1)
+;
+
+;
+; Conax
+;
+; C XX M <128 characters>
+; C XX E <128 characters>
+;
+; XX         - key number
+;
+
+;
+; Cryptoworks
+;
+; W XXXX YY ZZ VV <32 characters>  ; opkey
+; W XXXX YY ZZ VV <128 characters> ; RSA key (normaly ZZ=10)
+; W XXXX YY CC <12 characters>     ; cardkey (literal 'CC')
+;
+; XXXX  - caid
+; YY    - provider ident
+; ZZ    - keytype (20/31/10 derived from cardfiles EF20/EF31/EF10)
+; VV    - keyid   (00/01)
+;
+
+;
+; @SHL (SkyCrypt)
+;
+; Z 00 00 <16 characters>
+;
+
+;
+; Constant CW
+;
+; X YYYY freq:pol:src:sid <32 characters>
+;
+; YYYY      - CA system id e.g. 0100
+; freq      - frequency
+; pol       - polarisation (v/h/r/l)
+; src       - source
+; sid       - service ID
+;
+; example:
+X 0d02 12670:v:S19.2E:23457 00000000000000000000000000000000
+; cable example (Z can be an arbitrary letter, is ignored)
+X 1801 346:Z:C:23457 00000000000000000000000000000000
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Viaccess.KID b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/Viaccess.KID
new file mode 100644 (file)
index 0000000..a1bc85c
--- /dev/null
@@ -0,0 +1,11 @@
+; Viaccess cards
+;
+; AAAAAA BBBBBBBBBB CC DDDDDDDDDDDDDDDD
+;
+; AAAAAA           - Provider ident
+; BBBBBBBBBB       - Unique address (UA)
+; SSSSSSSS         - Shared address (SA)
+; CC               - Management key number
+; DDDDDDDDDDDDDDDD - Management key
+;
+123456 1234567890 12345678 01 1234561234561234 ; Dummy
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/cardclient.conf.example b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/cardclient.conf.example
new file mode 100644 (file)
index 0000000..f72df6a
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Comment lines can start with # or ;
+#
+# every client line starts with the client name, followed by some arguments:
+# 'hostname' is the name of the server
+# 'port'     is the port on the server
+# 'emm'      is a flag to allow EMM transfers to the server
+#            (0=disabled 1=enabled)
+# 'caid'     (optional) caid on which this client should work
+# 'mask'     (optional) mask for caid e.g. caid=1700 mask=FF00 would allow
+#            anything between 1700 & 17FF.
+#            Default is 1700 & FF00. If only caid is given mask is FFFF.
+#            You may give multiple caid/mask values comma separated
+#            (e.g. 1702,1722,0d0c/ff00).
+# 'username' is the login username
+# 'password' is the login password
+#
+# radegast client
+radegast:hostname:port:emm/caid/mask
+#
+# aroureos client
+# 'hexbase'
+# 'hexserial' card data for which EMM updates should be send
+aroureos:hostname:port:emm/caid/mask:hexbase:hexserial
+#
+# camd33 client (tcp protocol)
+# 'aeskey'   is the AES key (32bytes), disable encryption if missing
+camd33:hostname:port:emm/caid/mask:username:password:aeskey
+#
+# camd35 client (udp protocol)
+camd35:hostname:port:emm/caid/mask:username:password
+#
+# cardd client
+cardd:hostname:port:emm/caid/mask:username:password
+#
+# buffy client
+# 'aeskey'   is the AES key (32bytes), disable encryption if missing
+buffy:hostname:port:emm:username:password:aeskey
+#
+# newcamd client
+# 'cfgkey' is the config key (28bytes)
+newcamd:hostname:port:emm/caid/mask:username:password:cfgKey
+#
+# gbox client
+#
+# NOTE: hostname & port will be ignore. GBOX must be runnning on the local
+# machine. For convinience you should choose localhost:8004
+gbox:hostname:port:emm/caid/mask
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/dialup.sh.example b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/dialup.sh.example
new file mode 100644 (file)
index 0000000..71925eb
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# This script is called from VDR to start/stop dialup-network
+#
+# argument 1: wanted action, one of start,stop
+#
+
+RUN="/var/tmp/vdr.dialup.run"
+
+action="$1"
+
+case "$action" in
+up)
+  up ppp0
+  touch "$RUN"
+  sleep 2
+  ;;
+down)
+  down ppp0
+  rm -f "$RUN"
+  ;;
+esac
+
+exit 0
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/externalau.sh.example b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/externalau.sh.example
new file mode 100644 (file)
index 0000000..3bf09d5
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+URL="http://www.some-host.com/aes/"
+
+key=`wget -q -O- -U "Mozilla" "--referer=$URL" "$URL" | sed -ne '/007C00/ s/^.*clef Pri//p' | sed -ne 's/<pre>.*$//p' | sed -ne 's/: [0-9]\{2\} ://gp' | sed -ne 's/[^0-9A-F]//gp'`
+if [ "aa$key" != "aa" ]; then
+  echo "V 007C00 TPS $key"
+fi
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/smartcard.conf.example b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/examples/smartcard.conf.example
new file mode 100644 (file)
index 0000000..c2fb4a2
--- /dev/null
@@ -0,0 +1,84 @@
+;
+; Comment lines can start with # or ;
+;
+
+; Videoguard2
+;
+; NDS seed and boxid for camcrypt
+;
+; boxid  - boxid (4 byte = 8 chars)
+; seed1  - 1st seed (64 byte = 128 chars)
+; seed2  - 2nd seed (64 byte = 128 chars)
+;
+; videoguard2: SEED seed1 seed2
+videoguard2: SEED 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; videoguard2: BOXID boxid
+videoguard2: BOXID 11223344
+
+; Nagra
+;
+; boxkey and modulus for camcrypt
+;
+; prvid  - provider ID (2 byte = 4 chars)
+; boxkey - boxkey (8 byte = 16 chars)
+; mod    - modulus (64 byte = 128 chars)
+;
+; If IRDMOD is present, the given mod is used to decrypt the cammod from DT08
+; data. If IRDMOD is obmited, the given mod is used as cammod directly
+;
+; nagra: [prvid] IRDMOD boxkey mod
+nagra: [0501] IRDMOD 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; nagra: [prvid] boxkey mod
+nagra: [1201] 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+
+; Cryptoworks
+;
+; RSA modulus for camcrypt
+;
+; caid   - caid identifying the provider
+; serial - serial number identifying the card
+; ipk    - issuer public key (IPK), 64 bytes = 128 chars
+; ucpk   - session key (calculated from ISK, IPK & exponent), 64 bytes = 128 chars
+; pin    - pin number to disable parental rating
+; 
+; Note: you need only one of them for your caid. IPK is more general (common for
+; all cards of a provider), while the UCPK is different for every single card.
+;
+; cryptoworks: IPK caid ipk
+cryptoworks: IPK 0d22 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; cryptoworks: UCPK serial ucpk
+cryptoworks: UCPK 0102030405 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; cryptoworks: PIN serial pin
+cryptoworks: PIN 0102030405 1234
+
+;
+; Irdeto/Betacrypt
+;
+; RSA certificate for camkey challenge
+;
+; acs  - ACS version identifying the card (optional)
+; caid - caid for further identification of the card (optional)
+; mod  - RSA modulus, usualy 128 bytes = 256 chars
+; exp  - RSA exponent, usualy 1 byte = 2 chars
+;
+; Irdeto default certificate
+; irdeto: mod exp
+irdeto: 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; a ACS specific certificate
+; irdeto: [acs] mod exp
+irdeto: [0604] 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; a ACS/caid specific certificate
+; irdeto: [acs/caid] mod exp
+irdeto: [0384/1722] 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; indicates that a card doesn't want/need RSA camkey exchange
+; NOTE: this is autodetected for ACS 0383/0384 cards (Z/non-Z mode)
+;
+; irdeto: [acs/caid] PLAIN
+irdeto: [0605/0622] PLAIN
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.c
new file mode 100644 (file)
index 0000000..9f92029
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <linux/dvb/dmx.h>
+
+#include <vdr/tools.h>
+
+#include "filter.h"
+#include "misc.h"
+#include "log-core.h"
+
+// -- cPidFilter ------------------------------------------------------------------
+
+cPidFilter::cPidFilter(const char *Id, int Num, int DvbNum, unsigned int IdleTime)
+{
+  dvbNum=DvbNum;
+  idleTime=IdleTime;
+  id=0; fd=-1; active=false; forceRun=false; userData=0;
+  asprintf(&id,"%s/%d",Id,Num);
+  PRINTF(L_CORE_ACTION,"new filter '%s' on card %d (%d ms)",id,dvbNum,idleTime);
+}
+
+cPidFilter::~cPidFilter()
+{
+  cMutexLock lock(this);
+  if(fd>=0) {
+    Stop();
+    close(fd);
+    }
+  PRINTF(L_CORE_ACTION,"filter '%s' on card %d removed",id,dvbNum);
+  free(id);
+}
+
+bool cPidFilter::Open(void)
+{
+  cMutexLock lock(this);
+  if(fd<0) {
+    fd=DvbOpen(DEV_DVB_DEMUX,dvbNum,O_RDWR|O_NONBLOCK);
+    if(fd>=0) return true;
+    }
+  return false;
+}
+
+void cPidFilter::Flush(void)
+{
+  cMutexLock lock(this);
+  if(fd>=0) {
+    unsigned char buff[MAX_SECT_SIZE];
+    while(read(fd,buff,sizeof(buff))>0);
+    }
+}
+
+void cPidFilter::Start(int Pid, int Section, int Mask, int Mode, bool Crc)
+{
+  cMutexLock lock(this);
+  if(fd>=0) {
+    Stop();
+    struct dmx_sct_filter_params FilterParams;
+    memset(&FilterParams,0,sizeof(FilterParams));
+    FilterParams.filter.filter[0]=Section;
+    FilterParams.filter.mask[0]=Mask;
+    FilterParams.filter.mode[0]=Mode;
+    FilterParams.flags=DMX_IMMEDIATE_START;
+    if(Crc) FilterParams.flags|=DMX_CHECK_CRC;
+    FilterParams.pid=Pid;
+    if(ioctl(fd,DMX_SET_FILTER,&FilterParams)<0) {
+      PRINTF(L_GEN_ERROR,"filter '%s': DMX_SET_FILTER failed: %s",id,strerror(errno));
+      return;
+      }
+    pid=Pid;
+    active=true;
+
+    LBSTART(L_CORE_ACTION);
+    LBPUT("filter '%s' -> pid=0x%04x sct=0x%02x/0x%02x/0x%02x crc=%d matching",id,Pid,Section,Mask,Mode,Crc);
+    int mam =Mask &  (~Mode);
+    int manm=Mask & ~(~Mode);
+    for(int i=0; i<256; i++) {
+      int xxxor=Section ^ i;
+      if((mam&xxxor) || (manm && !(manm&xxxor))) {}
+      else
+        LBPUT(" 0x%02x",i);
+      }
+    LBEND();
+    }
+}
+
+void cPidFilter::Stop(void)
+{
+  cMutexLock lock(this);
+  if(fd>=0) {
+    CHECK(ioctl(fd,DMX_STOP));
+    active=false;
+    }
+}
+
+void cPidFilter::SetBuffSize(int BuffSize)
+{
+  cMutexLock lock(this);
+  if(fd>=0) {
+    Stop();
+    int s=max(BuffSize,8192);
+    CHECK(ioctl(fd,DMX_SET_BUFFER_SIZE,s));
+    }
+}
+
+int cPidFilter::SetIdleTime(unsigned int IdleTime)
+{
+  int i=idleTime;
+  idleTime=IdleTime;
+  return i;
+}
+
+void cPidFilter::Wakeup(void)
+{
+  cMutexLock lock(this);
+  forceRun=true;
+  PRINTF(L_CORE_ACTION,"filter '%s': wakeup",id);
+}
+
+int cPidFilter::Pid(void)
+{
+  return active ? pid : -1;
+}
+
+// -- cAction ------------------------------------------------------------------
+
+cAction::cAction(const char *Id, int DvbNum)
+{
+  asprintf(&id,"%s %d",Id,DvbNum);
+  dvbNum=DvbNum;
+  unique=0; pri=-1;
+  SetDescription("%s filter",id);
+}
+
+cAction::~cAction()
+{
+  Cancel(2);
+  DelAllFilter();
+  PRINTF(L_CORE_ACTION,"%s: stopped",id);
+  free(id);
+}
+
+cPidFilter *cAction::CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+{
+  return new cPidFilter(Id,Num,DvbNum,IdleTime);
+}
+
+cPidFilter *cAction::NewFilter(int IdleTime)
+{
+  Lock();
+  cPidFilter *filter=CreateFilter(id,unique++,dvbNum,IdleTime);
+  if(filter && filter->Open()) {
+    if(!Active()) {
+      Start();
+      PRINTF(L_CORE_ACTION,"%s: started",id);
+      }
+    filters.Add(filter);
+    }
+  else {
+    PRINTF(L_CORE_ACTION,"%s: filter '%s' failed to open",id,filter?filter->id:"XxX");
+    delete filter;
+    filter=0;
+    }
+  Unlock();
+  return filter;
+}
+
+cPidFilter *cAction::IdleFilter(void)
+{
+  Lock();
+  cPidFilter *filter;
+  for(filter=filters.First(); filter; filter=filters.Next(filter))
+    if(!filter->Active()) break;
+  Unlock();
+  return filter;
+}
+
+void cAction::DelFilter(cPidFilter *filter)
+{
+  Lock();
+  filters.Del(filter);
+  Unlock();
+}
+
+void cAction::DelAllFilter(void)
+{
+  Lock();
+  filters.Clear();
+  unique=0;
+  Unlock();
+}
+
+void cAction::Priority(int Pri)
+{
+  pri=Pri;
+}
+
+void cAction::Action(void)
+{
+  if(pri>0) SetPriority(pri);
+  struct pollfd *pfd=0;
+  while(Running()) {
+    if(filters.Count()<=0) {
+      cCondWait::SleepMs(100);
+      }
+    else {
+      // first build pfd data
+      Lock();
+      delete pfd; pfd=new struct pollfd[filters.Count()];
+      if(!pfd) {
+        PRINTF(L_GEN_ERROR,"action %s: pollfd: out of memory",id);
+        break;
+        }
+      int num=0;
+      cPidFilter *filter;
+      for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+        memset(&pfd[num],0,sizeof(struct pollfd));
+        pfd[num].fd=filter->fd;
+        pfd[num].events=POLLIN;
+        num++;
+        }
+      Unlock();
+
+      // now poll for data
+      int r=poll(pfd,num,60);
+      if(r<0) {
+        PRINTF(L_GEN_ERROR,"action %s poll: %s",id,strerror(errno));
+        break;
+        }
+      if(r>0) {
+        for(r=0 ; r<num ; r++)
+          if(pfd[r].revents&POLLIN) {
+            Lock();
+            for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+              if(filter->fd==pfd[r].fd) {
+                unsigned char buff[MAX_SECT_SIZE];
+                int n=read(filter->fd,buff,sizeof(buff));
+                if(n<0 && errno!=EAGAIN) {
+                  if(errno==EOVERFLOW) PRINTF(L_GEN_ERROR,"action %s read: Buffer overflow",filter->id);
+                  else PRINTF(L_GEN_ERROR,"action %s read: %s",filter->id,strerror(errno));
+                  }
+                if(n>0) {
+                  filter->lastTime.Set(); filter->forceRun=false;
+                  Process(filter,buff,n);
+                  // don't make any assumption about data-structs here
+                  // Process() may have changed them
+                  }
+                break;
+                }
+              }
+            Unlock();
+            }
+        }
+
+      // call filters which are idle too long
+      Lock();
+      do {
+        r=0;
+        for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+          if(filter->forceRun || (filter->idleTime && filter->lastTime.Elapsed()>filter->idleTime)) {
+            filter->lastTime.Set(); filter->forceRun=false;
+            Process(filter,0,0);
+            // don't make any assumption about data-structs here
+            // Process() may have changed them
+            r=1; break;
+            }
+          }
+        } while(r);
+      Unlock();
+      }
+    }
+  delete pfd;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/filter.h
new file mode 100644 (file)
index 0000000..f93c671
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___FILTER_H
+#define ___FILTER_H
+
+#include <vdr/thread.h>
+#include "misc.h"
+
+class cPidFilter;
+
+// ----------------------------------------------------------------
+
+#define MAX_SECT_SIZE 4096
+
+class cAction : protected cThread {
+private:
+  char *id;
+  int dvbNum, unique, pri;
+  cSimpleList<cPidFilter> filters;
+protected:
+  virtual void Process(cPidFilter *filter, unsigned char *data, int len)=0;
+  virtual void Action(void);
+  virtual cPidFilter *CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+  //
+  cPidFilter *NewFilter(int IdleTime);
+  cPidFilter *IdleFilter(void);
+  void DelFilter(cPidFilter *filter);
+  void DelAllFilter(void);
+  void Priority(int Pri);
+public:
+  cAction(const char *Id, int DvbNum);
+  virtual ~cAction();
+  };
+
+// ----------------------------------------------------------------
+
+class cPidFilter : public cSimpleItem, private cMutex {
+friend class cAction;
+private:
+  int dvbNum;
+  unsigned int idleTime;
+  cTimeMs lastTime;
+  bool forceRun;
+  //
+  bool Open(void);
+protected:
+  char *id;
+  int fd;
+  int pid;
+  bool active;
+public:
+  void *userData;
+  //
+  cPidFilter(const char *Id, int Num, int DvbNum, unsigned int IdleTime);
+  virtual ~cPidFilter();
+  void Flush(void);
+  virtual void Start(int Pid, int Section, int Mask, int Mode, bool Crc);
+  void Stop(void);
+  void Wakeup(void);
+  void SetBuffSize(int BuffSize);
+  int SetIdleTime(unsigned int IdleTime);
+  int Pid(void);
+  bool Active(void) { return active; }
+  };
+
+#endif //___FILTER_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/helper.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/helper.h
new file mode 100644 (file)
index 0000000..3430208
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___HELPER_H
+#define ___HELPER_H
+
+#include <byteswap.h>
+
+#if defined __i386__
+#define get_misalign(_a)    *(_a)
+#define put_misalign(_b,_a) *(_a)=(_b)
+#else
+template<class T> inline T get_misalign(T *p)
+{ struct s { T v; } __attribute__((packed)); return ((s *)p)->v; }
+
+template<class T> inline void put_misalign(unsigned int v, T* p)
+{ struct s { T v; } __attribute__((packed)); ((s *)p)->v = v; }
+#endif
+
+#define UINT32_BE(a) bswap_32(get_misalign((unsigned int *)(a)))
+#define UINT16_BE(a) bswap_16(get_misalign((unsigned short *)(a)))
+#define UINT8_BE(a)  *((unsigned char *)a)
+#define BYTE4_BE(a,b) put_misalign(bswap_32(b),(unsigned int *)(a))
+#define BYTE2_BE(a,b) put_misalign(bswap_16(b),(unsigned short *)(a))
+#define BYTE1_BE(a,b) *(unsigned char *)(a)=(b)
+
+#define UINT32_LE(a) get_misalign((unsigned int *)(a))
+#define UINT16_LE(a) get_misalign((unsigned short *)(a))
+#define UINT8_LE(a)  *((unsigned char *)a)
+#define BYTE4_LE(a,b) put_misalign(b,(unsigned int *)(a))
+#define BYTE2_LE(a,b) put_misalign(b,(unsigned short *)(a))
+#define BYTE1_LE(a,b) *(unsigned char *)(a)=(b)
+
+#define __ror16_const(x,xx) ((((x)&0xFFFF)>>(xx)) | ((x)<<(16-(xx))))
+#define __rol16_const(x,xx) (((x)<<(xx)) | (((x)&0xFFFF)>>(16-(xx))))
+#define __sn8_const(b)      ((((b)>>4)&0xf) + (((b)&0xf)<<4))
+
+#if defined __GNUC__ && __GNUC__ >= 2 && defined __i386__
+
+#define ror16(x,xx) ({ unsigned int v; \
+                     __asm__ ("rorw %2, %w0" : "=g" (v) : "0" (x), "i" (xx) : "cc"); \
+                     v; })
+#define rol16(x,xx) ({ unsigned int v; \
+                     __asm__ ("rolw %2, %w0" : "=g" (v) : "0" (x), "i" (xx) : "cc"); \
+                     v; })
+#define sn8(b)      ({ unsigned char v; \
+                     if(__builtin_constant_p(b)) \
+                       v=__sn8_const(b); \
+                     else \
+                       __asm__ ("rorb $4, %b0" : "=g" (v) : "0" (b) : "cc"); \
+                     v; })
+
+#else //__GNUC__
+
+#define ror16(x,xx) __ror16_const(x,xx)
+#define rol16(x,xx) __rol16_const(x,xx)
+#define sn8(b)      __sn8_const(b)
+
+#endif //__GNUC__
+
+static inline void __xxor(unsigned char *data, int len, const unsigned char *v1, const unsigned char *v2)
+{
+  switch(len) { // looks ugly, but the compiler can optimize it very well ;)
+    case 16:
+      *((unsigned int *)data+3) = *((unsigned int *)v1+3) ^ *((unsigned int *)v2+3);
+      *((unsigned int *)data+2) = *((unsigned int *)v1+2) ^ *((unsigned int *)v2+2);
+    case 8:
+      *((unsigned int *)data+1) = *((unsigned int *)v1+1) ^ *((unsigned int *)v2+1);
+    case 4:
+      *((unsigned int *)data+0) = *((unsigned int *)v1+0) ^ *((unsigned int *)v2+0);
+      break;
+    default:
+      while(len--) *data++ = *v1++ ^ *v2++;
+      break;
+    }
+}
+#define xxor(d,l,v1,v2) __xxor(d,l,v1,v2)
+
+#endif // ___HELPER_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n-template.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n-template.c
new file mode 100644 (file)
index 0000000..2678e03
--- /dev/null
@@ -0,0 +1,18 @@
+/* 
+ * Auto generated file, do not edit
+ * Will be overwritten/deleted without warning
+ * 
+ * Edit the .po files if you want to update translations!!
+ */
+
+#include "i18n.h"
+
+#if VDRVERSNUM < 10507
+
+const tI18nPhrase ScPhrases[] = {
+// START I18N
+// END I18N
+  { NULL }
+  };
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/i18n.h
new file mode 100644 (file)
index 0000000..151afb1
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___I18N_H
+#define ___I18N_H
+
+#include <vdr/i18n.h>
+
+#if APIVERSNUM < 10507
+extern const tI18nPhrase ScPhrases[];
+#define trNOOP(s) (s)
+#endif
+
+#endif //___I18N_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-core.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-core.h
new file mode 100644 (file)
index 0000000..31fe10f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___LOG_CORE_H
+#define ___LOG_CORE_H
+
+#include "log.h"
+
+// ----------------------------------------------------------------
+
+#define L_CORE         1
+
+#define L_CORE_LOAD     LCLASS(L_CORE,0x2)
+#define L_CORE_ACTION  LCLASS(L_CORE,0x4)
+#define L_CORE_ECM      LCLASS(L_CORE,0x8)
+#define L_CORE_ECMPROC LCLASS(L_CORE,0x10)
+#define L_CORE_PIDS    LCLASS(L_CORE,0x20)
+#define L_CORE_AU      LCLASS(L_CORE,0x40)
+#define L_CORE_AUSTATS LCLASS(L_CORE,0x80)
+#define L_CORE_AUEXTRA LCLASS(L_CORE,0x100)
+#define L_CORE_AUEXTERN        LCLASS(L_CORE,0x200)
+#define L_CORE_CAIDS    LCLASS(L_CORE,0x400)
+#define L_CORE_KEYS    LCLASS(L_CORE,0x800)
+#define L_CORE_DYN     LCLASS(L_CORE,0x1000)
+#define L_CORE_CSA     LCLASS(L_CORE,0x2000)
+#define L_CORE_CI      LCLASS(L_CORE,0x4000)
+#define L_CORE_AV7110  LCLASS(L_CORE,0x8000)
+#define L_CORE_NET     LCLASS(L_CORE,0x10000)
+#define L_CORE_NETDATA LCLASS(L_CORE,0x20000)
+#define L_CORE_MSGCACHE        LCLASS(L_CORE,0x40000)
+#define L_CORE_SERIAL  LCLASS(L_CORE,0x80000)
+#define L_CORE_SC      LCLASS(L_CORE,0x100000)
+#define L_CORE_HOOK    LCLASS(L_CORE,0x200000)
+#define L_CORE_CIFULL  LCLASS(L_CORE,0x400000)
+
+#define L_CORE_ALL      LALL(L_CORE_CIFULL)
+
+#endif //___LOG_CORE_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sc.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sc.h
new file mode 100644 (file)
index 0000000..b6d2b39
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___LOG_SC_H
+#define ___LOG_SC_H
+
+#include "log.h"
+
+#define L_SC_ERROR    LCLASS(L_SC,0x2)
+#define L_SC_INIT     LCLASS(L_SC,0x4)
+
+#define L_SC_LASTDEF  L_SC_INIT
+#define L_SC_DEFDEF   L_SC_ERROR|L_SC_INIT
+#define L_SC_DEFNAMES "error","init"
+
+#endif //___LOG_SC_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sys.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log-sys.h
new file mode 100644 (file)
index 0000000..184f2a8
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___LOG_SYS_H
+#define ___LOG_SYS_H
+
+#include "log.h"
+
+#define L_SYS_KEY      LCLASS(L_SYS,0x2)
+#define L_SYS_ECM      LCLASS(L_SYS,0x4)
+#define L_SYS_EMM      LCLASS(L_SYS,0x8)
+#define L_SYS_CRYPTO   LCLASS(L_SYS,0x10)
+#define L_SYS_VERBOSE  LCLASS(L_SYS,0x20)
+
+#define L_SYS_LASTDEF  L_SYS_VERBOSE
+#define L_SYS_DEFDEF   L_SYS_KEY|L_SYS_ECM
+#define L_SYS_DEFNAMES "key","ecm","emm","crypto","verbose"
+
+#endif //___LOG_SYS_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.c
new file mode 100644 (file)
index 0000000..6176256
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "log.h"
+#include "misc.h"
+
+#define LMOD_SUP 32
+#define LMOD_CFG_VALID  0x80000000
+
+struct LogConfig logcfg = {
+  1,0,0,0,0,
+  0,
+  "/var/log/vdr-sc"
+  };
+
+static const struct LogModule lm_general = {
+  (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
+  "general", { "error","warn","info","debug","misc" }
+  };
+
+static const struct LogModule *mods[LMOD_SUP] = { &lm_general };
+static int config[LMOD_SUP] = { LMOD_ENABLE|L_GEN_ALL|LMOD_CFG_VALID };
+static int cmask[LMOD_SUP]  = { LMOD_ENABLE|L_GEN_ALL };
+static cMutex logfileMutex;
+static FILE *logfile=0;
+static bool logfileShutup=false, logfileReopen=false;
+static long long logfileSize;
+
+static unsigned int lastCrc=0;
+static int lastC=0, lastCount=0;
+static cTimeMs lastTime;
+static cMutex lastMutex;
+
+// -- cLogging -----------------------------------------------------------------
+
+void (*cLogging::LogPrint)(const struct LogHeader *lh, const char *txt)=cLogging::PrivateLogPrint;
+
+bool cLogging::AddModule(int m, const struct LogModule *lm)
+{
+  if(m<LMOD_SUP) {
+    if(mods[m]) Printf(L_GEN_DEBUG,"duplicate logging module id %d (%s & %s)",m,mods[m]->Name,lm->Name);
+    mods[m]=lm;
+    if(config[m]&LMOD_CFG_VALID) UpgradeOptions(m);
+    else SetModuleDefault(LCLASS(m,0));
+//printf("module %-16s added. mod=%x supp=%x def=%x cfg=%x\n",lm->Name,m,lm->OptSupported,lm->OptDefault,config[m]);
+    }
+  else Printf(L_GEN_DEBUG,"failed to add logging module %d (%s)",m,lm->Name);
+  return true;
+}
+
+void cLogging::SetLogPrint(void (*lp)(const struct LogHeader *lh, const char *txt))
+{
+  LogPrint=lp;
+}
+
+const struct LogModule *cLogging::GetModule(int c)
+{
+  int m=LMOD(c);
+  return m<LMOD_SUP ? mods[m] : 0;
+}
+
+bool cLogging::GetHeader(int c, struct LogHeader *lh)
+{
+  const struct LogModule *lm=GetModule(c);
+  if(lm) {
+    if(!logcfg.noTimestamp) {
+      time_t tt=time(0);
+      struct tm tm_r;
+      strftime(lh->stamp,sizeof(lh->stamp),"%b %e %T",localtime_r(&tt,&tm_r));
+      }
+    else lh->stamp[0]=0;
+    int i, o=LOPT(c)&~LMOD_ENABLE;
+    for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
+    snprintf(lh->tag,sizeof(lh->tag),"%s.%s",lm->Name,(i>=1 && i<LOPT_NUM && lm->OptName[i-1])?lm->OptName[i-1]:"unknown");
+    lh->c=c;
+    return true;
+    }
+  return false;  
+}
+
+void cLogging::LogLine(const struct LogHeader *lh, const char *txt, bool doCrc)
+{
+  if(doCrc) {
+    unsigned int crc=crc32_le(0,(const unsigned char *)txt,strlen(txt)+1);
+    lastMutex.Lock();
+    if(lastCrc==crc && lastC==lh->c && ++lastCount<4000000 && lastTime.Elapsed()<=30*1000) {
+      lastMutex.Unlock();
+      return;
+      }
+    else {
+      unsigned int saveCrc=lastCrc;
+      int saveCount=lastCount, saveC=lastC;
+      lastTime.Set();
+      lastCrc=crc; lastC=lh->c; lastCount=0;
+      lastMutex.Unlock();
+      if(saveCount>1) {
+        struct LogHeader lh2;
+        if(GetHeader(saveC,&lh2)) {
+          char buff[128];
+          snprintf(buff,sizeof(buff),"last message repeated %d times",saveCount);
+          LogLine(&lh2,buff,false);
+          }
+        if(saveCrc==crc && saveC==lh->c) return;
+        }
+      }
+    }
+
+  LogPrint(lh,txt);
+}
+
+void cLogging::PrivateLogPrint(const struct LogHeader *lh, const char *txt)
+{
+  if(logcfg.logFile) {
+    logfileMutex.Lock();
+    if(logfileReopen) {
+      logfileReopen=false; logfileShutup=false;
+      if(logfile) {
+        PRINTF(L_GEN_DEBUG,"logfile closed, reopen as '%s'",logcfg.logFilename);
+        fclose(logfile);
+        logfile=0;
+        }
+      }
+    if(!logfile && !logfileShutup) {
+      logfile=fopen(logcfg.logFilename,"a");
+      if(logfile) {
+        setlinebuf(logfile);
+        logfileSize=ftell(logfile);
+        if(logfileSize<0) {
+          logfileSize=0;
+          PRINTF(L_GEN_ERROR,"cannot determine size of logfile '%s', assuming zero",logcfg.logFilename);
+          }
+        PRINTF(L_GEN_DEBUG,"logfile '%s' opened",logcfg.logFilename);
+        }
+      else {
+        logfileShutup=true;
+        PRINTF(L_GEN_ERROR,"failed to open logfile '%s': %s",logcfg.logFilename,strerror(errno));
+        }
+      }
+    if(logfile) {
+      int q=fprintf(logfile,"%s [%s] %s\n",lh->stamp,lh->tag,txt);
+      if(q>0) logfileSize+=q;
+
+      if(logcfg.maxFilesize>0 && logfileSize>((long long)logcfg.maxFilesize*1024)) {
+        fprintf(logfile,"%s [%s] %s\n",lh->stamp,lh->tag,"logfile closed, filesize limit reached");
+        fclose(logfile);
+        logfile=0; logfileShutup=false;
+        char *name;
+        asprintf(&name,"%s.old",logcfg.logFilename);
+        if(rename(logcfg.logFilename,name)) {
+          logfileShutup=true;
+          PRINTF(L_GEN_ERROR,"failed to rotate logfile: %s",strerror(errno));
+          PRINTF(L_GEN_ERROR,"logging to file disabled!");
+          }
+        free(name);
+        }
+      }
+    logfileMutex.Unlock();
+    }
+
+  if(logcfg.logCon)
+    printf("%s [%s] %s\n",lh->stamp,lh->tag,txt);
+
+  if(logcfg.logSys || LMOD(lh->c)==L_GEN) {
+    int pri=-1;
+    switch(lh->c) {
+      case L_GEN_ERROR:
+      case L_GEN_WARN:  if(SysLogLevel>0) pri=LOG_ERR; break;
+      case L_GEN_INFO:  if(SysLogLevel>1) pri=LOG_INFO; break;
+      case L_GEN_DEBUG: if(SysLogLevel>2) pri=LOG_DEBUG; break;
+      case L_GEN_MISC:  if(logcfg.logSys) pri=LOG_DEBUG; break;
+      default:          pri=LOG_DEBUG; break;
+      }
+    if(pri>=0)
+      syslog(pri,"[%d] [%s] %s",cThread::ThreadId(),lh->tag,txt);
+    }
+}
+
+bool cLogging::Enabled(int c)
+{
+  int m=LMOD(c);
+  int o=LOPT(c)|LMOD_CFG_VALID|LMOD_ENABLE;
+  return m<LMOD_SUP && (config[m] & o)==o;
+}
+
+void cLogging::Printf(int c, const char *format, ...)
+{
+  if(Enabled(c)) {
+    struct LogHeader lh;
+    if(GetHeader(c,&lh)) {
+      char buff[1024];
+      va_list ap;
+      va_start(ap,format);
+      vsnprintf(buff,sizeof(buff),format,ap);
+      va_end(ap);
+      LogLine(&lh,buff);
+      }
+    }
+}
+
+void cLogging::Puts(int c, const char *txt)
+{
+  if(txt && Enabled(c)) {
+    struct LogHeader lh;
+    if(GetHeader(c,&lh)) {
+      LogLine(&lh,txt);
+      }
+    }
+}
+
+void cLogging::PutLB(int c, cLineBuff *lb)
+{
+  if(lb->Length()>0) {
+    Puts(c,lb->Line());
+    lb->Flush();
+    }
+}
+
+void cLogging::Dump(int c, const void *data, int n, const char *format, ...)
+{
+  if(Enabled(c)) {
+    struct LogHeader lh;
+    if(GetHeader(c,&lh)) {
+      char buff[1024];
+      va_list ap;
+      va_start(ap,format);
+      vsnprintf(buff,sizeof(buff),format,ap);
+      va_end(ap);
+      LogLine(&lh,buff);
+      const unsigned char *d=(const unsigned char *)data;
+      for(int i=0; i<n;) {
+        int q=sprintf(buff,"%04x:",i);
+        for(int l=0 ; l<16 && i<n ; l++) q+=sprintf(&buff[q]," %02x",d[i++]);
+        LogLine(&lh,buff);
+        }
+      }
+    }
+}
+
+void cLogging::LineDump(int c, const void *data, int n, const char *format, ...)
+{
+  if(Enabled(c)) {
+    struct LogHeader lh;
+    if(GetHeader(c,&lh)) {
+      char buff[4096];
+      va_list ap;
+      va_start(ap,format);
+      unsigned int q=vsnprintf(buff,sizeof(buff),format,ap);
+      va_end(ap);
+      const unsigned char *d=(const unsigned char *)data;
+      for(int i=0; i<n; i++) {
+        if(q>=sizeof(buff)-8) {
+          q+=snprintf(&buff[q],sizeof(buff)-q,"....");
+          break;
+          }
+        q+=snprintf(&buff[q],sizeof(buff)-q," %02x",d[i]);
+        }
+      LogLine(&lh,buff);
+      }
+    }
+}
+
+void cLogging::SetModuleDefault(int c)
+{
+  const struct LogModule *lm=GetModule(c);
+  if(lm) {
+    int m=LMOD(c);
+    config[m]=(lm->OptDefault&LOPT_MASK)|LMOD_CFG_VALID;
+    cmask[m] = lm->OptSupported&LOPT_MASK;
+    }
+}
+
+void cLogging::SetModuleOptions(int c)
+{
+  const struct LogModule *lm=GetModule(c);
+  if(lm) config[LMOD(c)]=(LOPT(c)&(lm->OptSupported&LOPT_MASK))|LMOD_CFG_VALID;
+}
+
+int cLogging::GetModuleOptions(int c)
+{
+  const struct LogModule *lm=GetModule(c);
+  return lm ? LOPT(config[LMOD(c)]) : -1;
+}
+
+void cLogging::SetModuleOption(int c, bool on)
+{
+  const struct LogModule *lm=GetModule(c);
+  if(lm) {
+    int m=LMOD(c);
+    int o=LOPT(c)&(lm->OptSupported&LOPT_MASK);
+    if(o) {
+      if(on) config[m]|=o; else config[m]&=(~o);
+      config[m]|=LMOD_CFG_VALID;
+      }
+    }
+}
+
+void cLogging::UpgradeOptions(int m)
+{
+  const struct LogModule *lm=mods[m];
+  if(lm) {
+    config[m]&=(lm->OptSupported&LOPT_MASK);
+    config[m]|=(lm->OptDefault&LOPT_MASK)&(~cmask[m]);
+    cmask[m]  =lm->OptSupported&LOPT_MASK;
+    config[m]|=LMOD_CFG_VALID;
+    }
+}
+
+const char *cLogging::GetModuleName(int c)
+{
+  const struct LogModule *lm=GetModule(c);
+  return lm ? lm->Name : 0;
+}
+
+const char *cLogging::GetOptionName(int c)
+{
+  const struct LogModule *lm=GetModule(c);
+  if(lm) {
+    int o=LOPT(c)&~LMOD_ENABLE;
+    if(lm->OptSupported & o) {
+      int i;
+      for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
+      return (i>=1 && i<LOPT_NUM && lm->OptName[i-1]) ? lm->OptName[i-1] : 0;
+      }
+    }
+  return 0;
+}
+
+bool cLogging::GetConfig(cLineBuff *lb)
+{
+  lb->Flush();
+  bool cont=false;
+  for(int m=1; m<LMOD_SUP; m++)
+    if(config[m]&LMOD_CFG_VALID) {
+      lb->Printf("%s%d:%x:%x",cont?",":"",m,config[m]&~LMOD_CFG_VALID,cmask[m]);
+      cont=true;
+      }
+  return cont;
+}
+
+void cLogging::ParseConfig(const char *txt)
+{
+  int n=0, l, m, o, d;
+  while(sscanf(&txt[n],"%d:%x:%x%n",&m,&o,&d,&l)==3) {
+    if(m>=1 && m<LMOD_SUP) {
+      config[m]=(o&LOPT_MASK)|LMOD_CFG_VALID;
+      cmask[m] =(d&LOPT_MASK);
+      UpgradeOptions(m);
+      }
+    n+=l;
+    if(txt[n]!=',') break;
+    n++;
+    }
+}
+
+void cLogging::ReopenLogfile(void)
+{
+  logfileMutex.Lock();
+  logfileReopen=true;
+  logfileMutex.Unlock();
+}
+
+int cLogging::GetClassByName(const char *name)
+{
+  const char *s=index(name,'.');
+  if(s) {
+    unsigned int l=s-name;
+    for(int m=1; m<LMOD_SUP; m++) {
+      const struct LogModule *lm=mods[m];
+      if(lm && strlen(lm->Name)==l && !strncasecmp(name,lm->Name,l)) {
+        s++;
+        if(!strcasecmp(s,"enable"))
+           return LCLASS(m,1);
+        for(int o=1; o<LOPT_NUM; o++)
+          if((lm->OptSupported&(1<<o)) && lm->OptName[o-1] && !strcasecmp(s,lm->OptName[o-1]))
+            return LCLASS(m,1<<o);
+        break;
+        }
+      }
+    }
+  return -1;  
+}
+
+// -- cUserMsg -----------------------------------------------------------------
+
+cUserMsg::cUserMsg(const char *m)
+{
+  msg=strdup(m);
+}
+
+cUserMsg::~cUserMsg()
+{
+  free(msg);
+}
+
+// -- cUserMsgs ----------------------------------------------------------------
+
+cUserMsgs ums;
+
+cUserMsgs::cUserMsgs(void)
+{
+  mutex=new cMutex;
+}
+
+cUserMsgs::~cUserMsgs()
+{
+  delete mutex;
+}
+
+void cUserMsgs::Queue(const char *fmt, ...)
+{
+  if(logcfg.logUser) {
+    char buff[1024];
+    va_list ap;
+    va_start(ap,fmt);
+    vsnprintf(buff,sizeof(buff),fmt,ap);
+    va_end(ap);
+    mutex->Lock();
+    Add(new cUserMsg(buff));
+    mutex->Unlock();
+    }
+}
+
+cUserMsg *cUserMsgs::GetQueuedMsg(void)
+{
+  mutex->Lock();
+  cUserMsg *um=First();
+  if(um) Del(um,false);
+  mutex->Unlock();
+  return um;
+}
+
+// -- cLogLineBuff -------------------------------------------------------------
+
+cLogLineBuff::cLogLineBuff(int C)
+:cLineBuff(256)
+{
+  c=C;
+}
+
+cLogLineBuff::~cLogLineBuff()
+{
+  Flush();
+}
+
+void cLogLineBuff::Flush(void)
+{
+  cLogging::PutLB(c,this);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/log.h
new file mode 100644 (file)
index 0000000..32880e3
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___LOG_H
+#define ___LOG_H
+
+#include "misc.h"
+
+class cMutex;
+
+// ----------------------------------------------------------------
+
+#define LOPT_NUM    24
+#define LOPT_MAX    (1<<LOPT_NUM)
+#define LOPT_MASK   (LOPT_MAX-1)
+#define LMOD_MAX    (1<<(32-LOPT_NUM))
+#define LMOD_MASK   (LMOD_MAX-1)
+#define LMOD_ENABLE 1
+
+#define LCLASS(m,o) ((((m)&LMOD_MASK)<<LOPT_NUM)|((o)&LOPT_MASK))
+#define LMOD(c)     (((c)>>LOPT_NUM)&LMOD_MASK)
+#define LOPT(o)     ((o)&LOPT_MASK)
+#define LALL(o)     (((o)<<1)-1)
+
+#define ADD_MODULE(MOD,NAME) static bool __add_mod_ ## NAME = cLogging::AddModule(MOD,&NAME);
+
+#define PRINTF(c,...)       cLogging::Printf((c),__VA_ARGS__)
+#define PUTS(c,t)           cLogging::Puts((c),(t))
+#define PUTLB(c,lb)         cLogging::PutLB((c),(lb))
+#define HEXDUMP(c,d,n,...)  cLogging::Dump((c),(d),(n),__VA_ARGS__)
+#define LDUMP(c,d,n,...)    cLogging::LineDump((c),(d),(n),__VA_ARGS__)
+#define LOG(c)              cLogging::Enabled((c))
+
+// backward compatibility
+#define HexDump(d,n)        do { int __n=(n); HEXDUMP(L_GEN_MISC,(d),__n,"dump: n=%d/0x%04x",__n,__n); } while(0)
+
+#define LBSTART(c)          do { int __c=(c); if(LOG(__c)) { cLogLineBuff __llb(__c)
+#define LBSTARTF(c)         do { int __c=(c); { cLogLineBuff __llb(__c)
+#define LBPUT(...)          __llb.Printf(__VA_ARGS__)
+#define LBFLUSH()           __llb.Flush()
+#define LBEND( )            } } while(0)
+
+// ----------------------------------------------------------------
+
+#define L_GEN       0
+#define L_GEN_ERROR LCLASS(L_GEN,0x2)
+#define L_GEN_WARN  LCLASS(L_GEN,0x4)
+#define L_GEN_INFO  LCLASS(L_GEN,0x8)
+#define L_GEN_DEBUG LCLASS(L_GEN,0x10)
+#define L_GEN_MISC  LCLASS(L_GEN,0x20)
+
+#define L_GEN_ALL   LALL(L_GEN_MISC)
+
+// ----------------------------------------------------------------
+
+struct LogConfig {
+  int logCon, logFile, logSys, logUser, noTimestamp;
+  int maxFilesize;
+  char logFilename[128];
+  };
+
+extern struct LogConfig logcfg;
+
+struct LogModule {
+  int OptSupported, OptDefault;
+  const char *Name, *OptName[LOPT_NUM];
+  };
+
+struct LogHeader {
+  int c;
+  char stamp[32];
+  char tag[64];
+  };
+
+class cLogging {
+private:
+  static void (*LogPrint)(const struct LogHeader *lh, const char *txt);
+  //
+  static void PrivateLogPrint(const struct LogHeader *lh, const char *txt);
+  static bool GetHeader(int c, struct LogHeader *lh);
+  static void LogLine(const struct LogHeader *lh, const char *txt, bool doCrc=true);
+  static const struct LogModule *GetModule(int c);
+  static void UpgradeOptions(int m);
+public:
+  static bool AddModule(int m, const struct LogModule *dm);
+  static void SetLogPrint(void (*lp)(const struct LogHeader *lh, const char *txt));
+  //
+  static void Printf(int c, const char *format, ...) __attribute__ ((format (printf,2,3)));
+  static void Puts(int c, const char *txt);
+  static void PutLB(int c, cLineBuff *lb);
+  static void Dump(int c, const void *data, int n, const char *format, ...) __attribute__ ((format (printf,4,5)));
+  static void LineDump(int c, const void *data, int n, const char *format, ...) __attribute__ ((format (printf,4,5)));
+  static bool Enabled(int c);
+  //
+  static void SetModuleOptions(int c);
+  static int GetModuleOptions(int c);
+  static void SetModuleOption(int c, bool on);
+  static void SetModuleDefault(int c);
+  static const char *GetModuleName(int c);
+  static const char *GetOptionName(int c);
+  static void ParseConfig(const char *txt);
+  static bool GetConfig(cLineBuff *lb);
+  static void ReopenLogfile(void);
+  static int GetClassByName(const char *name);
+  };
+
+// ----------------------------------------------------------------
+
+class cUserMsg : public cSimpleItem {
+private:
+  char *msg;
+public:
+  cUserMsg(const char *m);
+  ~cUserMsg();
+  const char *Message(void) { return msg; };
+  };
+
+// ----------------------------------------------------------------
+
+class cUserMsgs : public cSimpleList<cUserMsg> {
+private:
+  cMutex *mutex;
+public:
+  cUserMsgs(void);
+  ~cUserMsgs();
+  void Queue(const char *fmt, ...) __attribute__ ((format (printf,2,3)));
+  cUserMsg *GetQueuedMsg(void);
+  };
+
+extern cUserMsgs ums;
+
+// ----------------------------------------------------------------
+
+class cLogLineBuff : public cLineBuff {
+private:
+  int c;
+public:
+  cLogLineBuff(int C);
+  ~cLogLineBuff();
+  void Flush(void);
+  };
+
+#endif //___LOG_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.c
new file mode 100644 (file)
index 0000000..fa9965a
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "misc.h"
+
+// -----------------------------------------------------------------------------
+
+void DvbName(const char *Name, int n, char *buffer, int len)
+{
+  snprintf(buffer,len,"/dev/dvb/adapter%d/%s%d",n,Name,0);
+}
+
+int DvbOpen(const char *Name, int n, int Mode, bool ReportError)
+{
+  char FileName[128];
+  DvbName(Name,n,FileName,sizeof(FileName));
+  int fd=open(FileName,Mode);
+  if(fd<0 && ReportError) LOG_ERROR_STR(FileName);
+  return fd;
+}
+
+const char *HexStr(char *str, const unsigned char *mem, int len)
+{
+  for(int i=0 ; i<len ; i++) sprintf(&str[i*2],"%02X",mem[i]);
+  return str;
+}
+
+static int HexDigit(int asc)
+{
+  if(asc>='A') return toupper(asc)-'A'+10;
+  return asc-'0';
+}
+
+int GetChar(const char * &line, int *store, int count)
+{
+  line=skipspace(line);
+  const char *sline=line;
+  *store=0;
+  while(count) {
+    if(line[0]==0 || !isalnum(line[0])) break;
+    *store<<=8; *store+=line[0];
+    line++; count--;
+    }
+  if(!count && (!*line || isspace(*line) || *line==';')) return sline-line;
+  *store=0;
+  line=sline;
+  return 0;
+}
+
+static int GetHexBase(const char * &line, unsigned char *store, int count, bool fixedLen, bool asc)
+{
+  line=skipspace(line);
+  const char *sline=line;
+  unsigned char *sstore=store;
+  int scount=count;
+  while(count) {
+    if(line[0]==0 || line[1]==0 || !isxdigit(line[0]) || !isxdigit(line[1])) break;
+    if(asc) {
+       *store++=line[0]; *store++=line[1];
+       }
+    else {
+      int d1=HexDigit(line[0]);
+      int d0=HexDigit(line[1]);
+      *store++=(d1<<4) + d0;
+      }
+    line+=2; count--;
+    }
+  if(asc) *store=0; // make sure strings are NULL terminated
+  if((!count || (!fixedLen && count!=scount)) &&
+     (!*line || isspace(*line) || *line==';' || *line==']' || *line=='/') ) return scount-count;
+  memset(sstore,0,scount);
+  line=sline;
+  return 0;
+}
+
+int GetHex(const char * &line, unsigned char *store, int count, bool fixedLen)
+{
+  return GetHexBase(line,store,count,fixedLen,false);
+}
+
+int GetHexAsc(const char * &line, unsigned char *store, int count)
+{
+  return GetHexBase(line,store,count/2,true,true)*2;
+}
+
+unsigned long long Bin2LongLong(unsigned char *mem, int len)
+{
+  unsigned long long k=0;
+  for(int i=0 ; i<len ; i++) { k<<=8; k+=mem[i]; }
+  return k;
+}
+
+bool CheckNull(const unsigned char *data, int len)
+{
+  while(--len>=0) if(data[len]) return false;
+  return true;
+}
+
+bool CheckFF(const unsigned char *data, int len)
+{
+  while(--len>=0) if(data[len]!=0xFF) return false;
+  return true;
+}
+
+unsigned char XorSum(const unsigned char *mem, int len)
+{
+  unsigned char cs=0;
+  while(len>0) { cs ^= *mem++; len--; }
+  return cs;
+}
+
+// crc stuff taken from linux-2.6.0/lib/crc32.c
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+
+unsigned int crc32_le(unsigned int crc, unsigned char const *p, int len)
+{
+  crc^=0xffffffff; // zlib mod
+  while(len--) {
+    crc^=*p++;
+    for(int i=0; i<8; i++)
+      crc=(crc&1) ? (crc>>1)^CRCPOLY_LE : (crc>>1);
+    }
+  crc^=0xffffffff; // zlib mod
+  return crc;
+}
+
+// -- cLineBuff -----------------------------------------------------------------
+
+cLineBuff::cLineBuff(int blocksize)
+{
+  blockSize=blocksize;
+  work=0;
+  blen=wlen=0;
+}
+
+cLineBuff::~cLineBuff()
+{
+  free(work);
+}
+
+void cLineBuff::Flush(void)
+{
+  blen=0;
+}
+
+char *cLineBuff::Grab(void)
+{
+  char *w=work;
+  work=0; wlen=0;
+  Flush();
+  return w;
+}
+
+bool cLineBuff::Check(int num)
+{
+  if(wlen-blen<num) {
+    if(num<blockSize) num=blockSize;
+    char *w=(char *)realloc(work,wlen+num);
+    if(w) {
+      wlen+=num;
+      work=w;
+      }
+    }
+  return work!=0;
+}
+
+void cLineBuff::Printf(const char *fmt, ...)
+{
+  int q=120;
+  while(Check(q)) {
+    int s=wlen-blen;
+    va_list ap;
+    va_start(ap,fmt);
+    q=vsnprintf(work+blen,s,fmt,ap);
+    va_end(ap);
+    if(q<s) {
+      if(q>0) blen+=q;
+      break;
+      }
+    // truncated
+    q+=100;
+    }
+}
+
+void cLineBuff::Strcat(const char *str)
+{
+  if(str) Printf("%s",str);
+}
+
+void cLineBuff::Back(int n)
+{
+  if(n>blen) blen=0; else blen-=n;
+  if(work) work[blen]=0;
+}
+
+// -- cSimpleListBase --------------------------------------------------------------
+
+cSimpleListBase::cSimpleListBase(void)
+{
+  first=last=0; count=0;
+}
+
+cSimpleListBase::~cSimpleListBase()
+{
+  Clear();
+}
+
+void cSimpleListBase::Add(cSimpleItem *Item, cSimpleItem *After)
+{
+  if(After) {
+    Item->next=After->next;
+    After->next=Item;
+    }
+  else {
+    Item->next=0;
+    if(last) last->next=Item;
+    else first=Item;
+    }
+  if(!Item->next) last=Item;
+  count++;
+}
+
+void cSimpleListBase::Ins(cSimpleItem *Item)
+{
+  Item->next=first;
+  first=Item;
+  if(!Item->next) last=Item;
+  count++;
+}
+
+void cSimpleListBase::Del(cSimpleItem *Item, bool Del)
+{
+  if(first==Item) {
+    first=Item->next;
+    if(!first) last=0;
+    }
+  else {
+    cSimpleItem *item=first;
+    while(item) {
+      if(item->next==Item) {
+        item->next=Item->next;
+        if(!item->next) last=item;
+        break;
+        }
+      item=item->next;
+      }
+    }
+  count--;
+  if(Del) delete Item;
+}
+
+void cSimpleListBase::Clear(void)
+{
+  while(first) Del(first);
+  first=last=0; count=0;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/misc.h
new file mode 100644 (file)
index 0000000..dc5868f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___MISC_H
+#define ___MISC_H
+
+#include <alloca.h>
+
+// ----------------------------------------------------------------
+
+#define WORD(buffer,index,mask) (((buffer[(index)]<<8) + buffer[(index)+1]) & mask)
+#define SCT_LEN(sct) (3+(((sct)[1]&0x0f)<<8)+(sct)[2])
+
+#define MBC(a,b)    (((a)<<8)+(b))
+#define ADDC3(ab,c) ((ab)+((c)<<16))
+#define MBC3(a,b,c) ADDC3(MBC((a),(b)),(c))
+#define C2(x)       (((x)>>8)&0xFF)
+#define C3(x)       (((x)>>16)&0xFF)
+#define C2MASK      0xFFFF
+
+// replacement for variable-sized arrays
+#define AUTOARRAY(type,size) (type *)alloca(sizeof(type)*(size))
+#define AUTOMEM(size)        (unsigned char *)alloca(size)
+
+// ----------------------------------------------------------------
+
+#define DEV_DVB_FRONTEND "frontend"
+#define DEV_DVB_DVR      "dvr"
+#define DEV_DVB_DEMUX    "demux"
+#define DEV_DVB_CA       "ca"
+void DvbName(const char *Name, int n, char *buffer, int len);
+int DvbOpen(const char *Name, int n, int Mode, bool ReportError=false);
+
+const char *HexStr(char *str, const unsigned char *mem, int len);
+#define KeyStr(str,key) HexStr(str,key,8)
+
+int GetHex(const char * &line, unsigned char *store, int count, bool fixedLen=true);
+int GetHexAsc(const char * &line, unsigned char *store, int count);
+int GetChar(const char * &line, int *store, int count);
+unsigned long long Bin2LongLong(unsigned char *mem, int len);
+#define Bin2Int(mem,len) ((int)Bin2LongLong((mem),(len)))
+
+bool CheckNull(const unsigned char *data, int len);
+bool CheckFF(const unsigned char *data, int len);
+unsigned char XorSum(const unsigned char *mem, int len);
+unsigned int crc32_le(unsigned int crc, unsigned char const *p, int len);
+
+// ----------------------------------------------------------------
+
+class cLineBuff {
+private:
+  int blockSize;
+  char *work;
+  int blen, wlen;
+  //
+  bool Check(int num);
+protected:
+  char *Grab(void);
+public:
+  cLineBuff(int blocksize);
+  ~cLineBuff();
+  void Printf(const char *fmt, ...) __attribute__ ((format (printf,2,3)));
+  void Strcat(const char *str);
+  void Flush(void);
+  void Back(int n);
+  const char *Line(void) const { return work; }
+  int Length(void) const { return blen; }
+  };
+
+// ----------------------------------------------------------------
+
+class cSimpleListBase;
+
+class cSimpleItem {
+friend class cSimpleListBase;
+private:
+  cSimpleItem *next;
+public:
+  virtual ~cSimpleItem() {}
+  cSimpleItem *Next(void) const { return next; }
+  };
+
+class cSimpleListBase {
+protected:
+  cSimpleItem *first, *last;
+  int count;
+public:
+  cSimpleListBase(void);
+  ~cSimpleListBase();
+  void Add(cSimpleItem *Item, cSimpleItem *After=0);
+  void Ins(cSimpleItem *Item);
+  void Del(cSimpleItem *Item, bool Del=true);
+  void Clear(void);
+  int Count(void) const { return count; }
+  };
+
+template<class T> class cSimpleList : public cSimpleListBase {
+public:
+  T *First(void) const { return (T *)first; }
+  T *Last(void) const { return (T *)last; }
+  T *Next(const T *item) const { return (T *)item->cSimpleItem::Next(); }
+  };
+
+#endif //___MISC_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.c
new file mode 100644 (file)
index 0000000..9e87911
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#include "network.h"
+#include "misc.h"
+#include "log-core.h"
+
+const char *netscript=0;
+int netTimeout=60*1000;   // timeout for shutting down dialup-network
+
+// -- cNetWatcher ---------------------------------------------------------------
+
+class cNetWatcher : protected cThread {
+private:
+  int count;
+  cTimeMs down;
+  bool netup, downDelay;
+  cSimpleList<cNetSocket> socks;
+  //
+  int RunCommand(const char *cmd, const char *state);
+protected:
+  virtual void Action(void);
+public:
+  cNetWatcher(void);
+  ~cNetWatcher();
+  void Up(cNetSocket *so);
+  void Down(cNetSocket *so);
+  void Block(void) { Lock(); }
+  void Unblock(void) { Unlock(); }
+  };
+
+static cNetWatcher nw;
+
+cNetWatcher::cNetWatcher(void)
+:cThread("Netwatcher")
+{
+  count=0; netup=downDelay=false;
+}
+
+cNetWatcher::~cNetWatcher()
+{
+  Cancel(2);
+  Lock();
+  if(netup) RunCommand(netscript,"down");
+  cNetSocket *so;
+  while((so=socks.First())) socks.Del(so,false);
+  Unlock();
+}
+
+void cNetWatcher::Up(cNetSocket *so)
+{
+  Lock();
+  socks.Add(so);
+  if(!Active()) Start();
+  if(netscript) {
+    downDelay=false; netup=true;
+    RunCommand(netscript,"up");
+    count++;
+    PRINTF(L_CORE_NET,"netwatch up (%d)",count);
+    }
+  else PRINTF(L_CORE_NET,"netwatch up");
+  Unlock();
+}
+
+void cNetWatcher::Down(cNetSocket *so)
+{
+  Lock();
+  socks.Del(so,false);
+  if(netscript) {
+    if(--count==0) {
+      downDelay=true;
+      down.Set(netTimeout);
+      }
+    PRINTF(L_CORE_NET,"netwatch down (%d)",count);
+    }
+  else PRINTF(L_CORE_NET,"netwatch down");
+  Unlock();
+}
+
+void cNetWatcher::Action(void)
+{
+  while(Running()) {
+    sleep(1);
+    Lock();
+    if(downDelay && down.TimedOut()) {
+      PRINTF(L_CORE_NET,"netdown timeout expired");
+      if(netup) {
+        RunCommand(netscript,"down");
+        netup=false;
+        }
+      downDelay=false;
+      }
+    for(cNetSocket *so=socks.First(); so;) {
+      cNetSocket *next=socks.Next(so);
+      so->Lock();
+      if(so->connected && so->activity.TimedOut()) {
+        PRINTF(L_CORE_NET,"idle timeout, disconnected %s:%d",so->hostname,so->port);
+        so->Disconnect();
+        }
+      so->Unlock();
+      so=next;
+      }
+    Unlock();
+    }
+}
+
+int cNetWatcher::RunCommand(const char *cmd, const char *state)
+{
+  char *tmp;
+  asprintf(&tmp,"%s %s",cmd,state);
+  PRINTF(L_CORE_NET,"netwatch cmd exec '%s'",tmp);
+  int res=SystemExec(tmp);
+  free(tmp);
+  return res;
+}
+
+// -- cNetSocket ------------------------------------------------------------------
+
+cNetSocket::cNetSocket(int ConnectTimeout, int ReadWriteTimeout, int IdleTimeout, bool Udp)
+{
+  hostname=0; sd=-1; connected=netup=false;
+  udp=Udp; conTimeout=ConnectTimeout; rwTimeout=ReadWriteTimeout;
+  idleTimeout=IdleTimeout*1000;
+}
+
+cNetSocket::~cNetSocket()
+{
+  Disconnect();
+  free(hostname);
+}
+
+void cNetSocket::Activity(void)
+{
+  activity.Set(idleTimeout);
+}
+
+bool cNetSocket::GetAddr(struct sockaddr_in *saddr, const char *Hostname, int Port)
+{
+  const struct hostent * const hostaddr=gethostbyname(Hostname);
+  if(hostaddr) {
+    saddr->sin_family=AF_INET;
+    saddr->sin_port=htons(Port);
+    saddr->sin_addr.s_addr=((struct in_addr *)hostaddr->h_addr)->s_addr;
+    return true;
+    }
+  PRINTF(L_GEN_ERROR,"socket: name lookup error for %s",Hostname);
+  return false;
+}
+
+int cNetSocket::GetSocket(bool Udp)
+{
+  int proto=0;
+  const struct protoent * const ptrp=getprotobyname(Udp?"udp":"tcp");
+  if(ptrp) proto=ptrp->p_proto;
+  int so;
+  if((so=socket(PF_INET,Udp?SOCK_DGRAM:SOCK_STREAM,proto))>=0) {
+    int flgs=fcntl(so,F_GETFL);
+    if(flgs>=0) {
+      if(fcntl(so,F_SETFL,flgs|O_NONBLOCK)==0)
+        return so;
+      else PRINTF(L_GEN_ERROR,"socket: fcntl SETFL failed: %s",strerror(errno)); 
+      }
+    else PRINTF(L_GEN_ERROR,"socket: fcntl GETFL failed: %s",strerror(errno));
+    close(so);
+    }
+  else PRINTF(L_GEN_ERROR,"socket: socket failed: %s",strerror(errno));
+  return -1;
+}
+
+bool cNetSocket::Connect(const char *Hostname, int Port, int timeout)
+{
+  nw.Block();
+  Lock();
+  Disconnect();
+  if(Hostname) {
+    free(hostname);
+    hostname=strdup(Hostname); port=Port;
+    }
+  if(timeout<0) timeout=conTimeout;
+  nw.Up(this); netup=true;
+  nw.Unblock();
+  struct sockaddr_in socketAddr;
+  if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
+    PRINTF(L_CORE_NET,"connecting to %s:%d/%s (%d.%d.%d.%d)",
+               hostname,port,udp?"udp":"tcp",
+               (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
+    if(connect(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr))==0)
+      connected=true;
+    else if(errno==EINPROGRESS) {
+      if(Select(false,timeout)>0) {
+        int r=-1;
+        unsigned int l=sizeof(r);
+        if(getsockopt(sd,SOL_SOCKET,SO_ERROR,&r,&l)==0) {
+          if(r==0)
+            connected=true;
+          else PRINTF(L_GEN_ERROR,"socket: connect failed (late): %s",strerror(r));
+          }
+        else PRINTF(L_GEN_ERROR,"socket: getsockopt failed: %s",strerror(errno));
+        }
+      else PRINTF(L_GEN_ERROR,"socket: connect timed out");
+      }
+    else PRINTF(L_GEN_ERROR,"socket: connect failed: %s",strerror(errno));
+
+    if(connected) { Activity(); Unlock(); return true; }
+    }
+  Unlock();
+  Disconnect();
+  return false;
+}
+
+
+bool cNetSocket::Bind(const char *Hostname, int Port)
+{
+  nw.Block();
+  Lock();
+  Disconnect();
+  if(Hostname) {
+    free(hostname);
+    hostname=strdup(Hostname); port=Port;
+    }
+  nw.Up(this); netup=true;
+  nw.Unblock();
+  struct sockaddr_in socketAddr;
+  if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
+    PRINTF(L_CORE_NET,"socket: binding to %s:%d/%s (%d.%d.%d.%d)",
+               hostname,port,udp?"udp":"tcp",
+               (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
+    if(bind(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr))==0) {
+      connected=true;
+      Activity(); Unlock();
+      return true;
+      }
+    else PRINTF(L_GEN_ERROR,"socket: bind failed: %s",strerror(errno));
+    }
+  Unlock();
+  Disconnect();
+  return false;
+}
+
+void cNetSocket::Disconnect(void)
+{
+  nw.Block();
+  cMutexLock lock(this);
+  if(sd>=0) { close(sd); sd=-1; }
+  connected=false;
+  if(netup) { nw.Down(this); netup=false; }
+  nw.Unblock();
+}
+
+void cNetSocket::Flush(void)
+{
+  cMutexLock lock(this);
+  if(sd>=0) {
+    unsigned char buff[512];
+    while(read(sd,buff,sizeof(buff))>0) Activity();
+    }
+}
+
+int cNetSocket::Read(unsigned char *data, int len, int timeout)
+{
+  cMutexLock lock(this);
+  if(timeout<0) timeout=rwTimeout;
+  int r=Select(true,timeout);
+  if(r>0) {
+    r=read(sd,data,len);
+    if(r<0) PRINTF(L_GEN_ERROR,"socket: read failed: %s",strerror(errno));
+    else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network read");
+    }
+  Activity();
+  return r;
+}
+
+int cNetSocket::Write(const unsigned char *data, int len, int timeout)
+{
+  cMutexLock lock(this);
+  if(timeout<0) timeout=rwTimeout;
+  int r=Select(false,timeout);
+  if(r>0) {
+    r=write(sd,data,len);
+    if(r<0) PRINTF(L_GEN_ERROR,"socket: write failed: %s",strerror(errno));
+    else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network write");
+    }
+  Activity();
+  return r;
+}
+
+int cNetSocket::SendTo(const char *Host, int Port, const unsigned char *data, int len, int timeout)
+{
+  cMutexLock lock(this);
+  if(timeout<0) timeout=rwTimeout;
+  int r=Select(false,timeout);
+  if(r>0) {
+    struct sockaddr_in saddr;
+    if(GetAddr(&saddr,Host,Port)) {
+      r=sendto(sd,data,len,0,(struct sockaddr *)&saddr,sizeof(saddr));
+      if(r<0) PRINTF(L_GEN_ERROR,"socket: sendto %d.%d.%d.%d:%d failed: %s",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port,strerror(errno));
+      else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network sendto %d.%d.%d.%d:%d",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port);
+      }
+    else r=-1;
+    }
+  Activity();
+  return r;
+}
+
+int cNetSocket::Select(bool forRead, int timeout)
+{
+  if(sd>=0) {
+    fd_set fds;
+    FD_ZERO(&fds); FD_SET(sd,&fds);
+    struct timeval tv;
+    tv.tv_sec=timeout; tv.tv_usec=0;
+    int r=select(sd+1,forRead ? &fds:0,forRead ? 0:&fds,0,&tv);
+    if(r>0) return 1;
+    else if(r<0) {
+      PRINTF(L_GEN_ERROR,"socket: select failed: %s",strerror(errno));
+      return -1;
+      }
+    else {
+      if(timeout>0) PRINTF(L_CORE_NET,"socket: select timed out (%d secs)",timeout);
+      return 0;
+      }
+    }
+  return -1;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/network.h
new file mode 100644 (file)
index 0000000..713bbe6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___NETWORK_H
+#define ___NETWORK_H
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define DEFAULT_CONNECT_TIMEOUT   20
+#define DEFAULT_READWRITE_TIMEOUT 3
+#define DEFAULT_IDLE_TIMEOUT      120
+
+extern const char *netscript;
+extern int netTimeout;
+
+// ----------------------------------------------------------------
+
+class cNetWatcher;
+
+class cNetSocket : public cSimpleItem, private cMutex {
+friend class cNetWatcher;
+private:
+  int sd;
+  char *hostname;
+  int port, dummy, conTimeout, rwTimeout, idleTimeout;
+  bool udp, connected, netup;
+  cTimeMs activity;
+  //
+  int Select(bool forRead, int timeout);
+  void Activity(void);
+  bool GetAddr(struct sockaddr_in *saddr, const char *Hostname, int Port);
+  int GetSocket(bool Udp);
+public:
+  cNetSocket(int ConnectTimeout, int ReadWriteTimeout, int IdleTimeout, bool Udp=false);
+  ~cNetSocket();
+  bool Connect(const char *Hostname, int Port, int timeout=-1);
+  bool Bind(const char *Hostname, int Port);
+  void Disconnect(void);
+  int Read(unsigned char *data, int len, int timeout=-1);
+  int Write(const unsigned char *data, int len, int timeout=-1);
+  int SendTo(const char *Host, int Port, const unsigned char *data, int len, int timeout=-1);
+  void Flush(void);
+  bool Connected(void) { return connected; }
+  };
+
+#endif //___NETWORK_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/openssl-compat.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/openssl-compat.h
new file mode 100644 (file)
index 0000000..ea1ab27
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___OPENSSL_COMPAT_H
+#define ___OPENSSL_COMPAT_H
+
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL || OPENSSL_VERSION_NUMBER < 0x0090705fL
+#define DES_CAST(x) ((DES_cblock *)(x))
+#else
+#define DES_CAST(x) (x)
+#endif
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/opts.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/opts.h
new file mode 100644 (file)
index 0000000..8525444
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___OPTS_H
+#define ___OPTS_H
+
+#include "i18n.h"
+
+class cOsdMenu;
+
+// ----------------------------------------------------------------
+
+class cOpt {
+private:
+  char *fullname;
+  bool persistant;
+protected:
+  const char *name, *title;
+  //
+  const char *FullName(const char *PreStr);
+public:
+  cOpt(const char *Name, const char *Title);
+  virtual ~cOpt();
+  virtual void Parse(const char *Value)=0;
+  virtual void Backup(void)=0;
+  virtual bool Set(void)=0;
+  virtual void Store(const char *PreStr)=0;
+  virtual void Create(cOsdMenu *menu)=0;
+  const char *Name(void) { return name; }
+  void Persistant(bool per) { persistant=per; }
+  bool Persistant(void) { return persistant; }
+  };
+
+// ----------------------------------------------------------------
+
+class cOptInt : public cOpt {
+protected:
+  int *storage, value, min, max;
+public:
+  cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max);
+  virtual void Parse(const char *Value);
+  virtual void Backup(void);
+  virtual bool Set(void);
+  virtual void Store(const char *PreStr);
+  virtual void Create(cOsdMenu *menu);
+  };
+
+// ----------------------------------------------------------------
+
+class cOptSel : public cOptInt {
+private:
+  const char **trStrings;
+protected:
+  const char * const *strings;
+public:
+  cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings);
+  virtual ~cOptSel();
+  virtual void Create(cOsdMenu *menu);
+  };
+
+// ----------------------------------------------------------------
+
+class cOptBool : public cOptInt {
+public:
+  cOptBool(const char *Name, const char *Title, int *Storage);
+  virtual void Create(cOsdMenu *menu);
+  };
+
+// ----------------------------------------------------------------
+
+class cOptStr : public cOpt {
+protected:
+  char *storage, *value;
+  const char *allowed;
+  int size;
+public:
+  cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed);
+  virtual ~cOptStr();
+  virtual void Parse(const char *Value);
+  virtual void Backup(void);
+  virtual bool Set(void);
+  virtual void Store(const char *PreStr);
+  virtual void Create(cOsdMenu *menu);
+  };
+
+// ----------------------------------------------------------------
+
+class cOpts {
+private:
+  int numOpts, numAdd;
+  const char *preStr;
+  cOpt **opts;
+public:
+  cOpts(const char *PreStr, int NumOpts);
+  ~cOpts();
+  void Add(cOpt *opt);
+  bool Parse(const char *Name, const char *Value);
+  bool Store(bool AsIs);
+  void Backup(void);
+  void Create(cOsdMenu *menu);
+  };
+
+#endif //___OPTS_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.c
new file mode 100644 (file)
index 0000000..429f144
--- /dev/null
@@ -0,0 +1,805 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "parse.h"
+#include "misc.h"
+#include "log.h"
+
+// -----------------------------------------------------------------------------
+
+void SetSctLen(unsigned char *data, int len)
+{
+  data[1]=(len>>8) | 0x70;
+  data[2]=len & 0xFF;
+}
+
+static void SortNanos(unsigned char *dest, const unsigned char *src, int len)
+{
+  int w=0, c=-1;
+  while(1) {
+    int n=0x100;
+    for(int j=0; j<len;) {
+      int l=src[j+1]+2;
+      if(src[j]==c) {
+        if(w+l>len) {
+          PRINTF(L_GEN_ERROR,"sortnanos: sanity check failed. Exceeding memory area. Probably corrupted nanos!");
+          memset(dest,0,len); // zero out everything
+          return;
+          }
+        memcpy(&dest[w],&src[j],l);
+        w+=l;
+        }
+      else if(src[j]>c && src[j]<n)
+        n=src[j];
+      j+=l;
+      }
+    if(n==0x100) break;
+    c=n;
+    }
+}
+
+// -- cProviders ---------------------------------------------------------------
+
+void cProviders::AddProv(cProvider *p)
+{
+  if(p) Add(p);
+}
+
+cProvider *cProviders::FindProv(const unsigned char *data)
+{
+  cProvider *p=First();
+  while(p) {
+    if(p->MatchID(data)) break;
+    p=Next(p);
+    }
+  return p;
+}
+
+cProvider *cProviders::MatchProv(const unsigned char *data)
+{
+  cProvider *p=First();
+  while(p) {
+    if(p->MatchEMM(data)) break;
+    p=Next(p);
+    }
+  return p;
+}
+
+// -- cAssSct ------------------------------------------------------------------
+
+cAssSct::cAssSct(const unsigned char *Data)
+{
+  data=Data;
+}
+
+// -- cAssembleData ------------------------------------------------------------
+
+cAssembleData::cAssembleData(const unsigned char *Data)
+{
+  data=Data;
+  curr=0;
+}
+
+void cAssembleData::SetAssembled(const unsigned char *Data)
+{
+  Add(new cAssSct(Data));
+  curr=First();
+}
+
+const unsigned char *cAssembleData::Assembled(void)
+{
+  const unsigned char *ret=0;
+  if(First()) {
+    if(curr) {
+      ret=curr->Data();
+      curr=Next(curr);
+      }
+    }
+  else {
+    ret=data;
+    data=0;
+    }
+  return ret;
+}
+
+// -- cIdSet -------------------------------------------------------------------
+
+cIdSet::cIdSet(void)
+{
+  card=0;
+}
+
+cIdSet::~cIdSet()
+{
+  delete card;
+}
+
+void cIdSet::ResetIdSet(void)
+{
+  delete card; card=0;
+  Clear();
+}
+
+void cIdSet::SetCard(cCard *c)
+{
+  delete card;
+  card=c;
+}
+
+bool cIdSet::MatchEMM(const unsigned char *data)
+{
+  return (card && card->MatchEMM(data)) || MatchProv(data);
+}
+
+bool cIdSet::MatchAndAssemble(cAssembleData *ad, int *updtype, cProvider **prov)
+{
+  const unsigned char *data=ad->Data();
+  cProvider *p;
+  if(card && card->MatchEMM(data)) {
+    if(updtype) *updtype=card->UpdateType(data);
+    if(prov) *prov=0;
+    if(card->Assemble(ad)>=0) return true;
+    }
+  else if((p=MatchProv(data))) {
+    if(updtype) *updtype=p->UpdateType(data);
+    if(prov) *prov=p;
+    if(p->Assemble(ad)>=0) return true;
+    }
+  return false;
+}
+
+// -- cProviderIrdeto ----------------------------------------------------------
+
+cProviderIrdeto::cProviderIrdeto(unsigned char pb, const unsigned char *pi)
+{
+  provBase=pb;
+  memcpy(provId,pi,sizeof(provId));
+}
+
+bool cProviderIrdeto::MatchID(const unsigned char *data)
+{
+  const unsigned int mode=cParseIrdeto::AddrBase(data);
+  return (mode<0x10 && mode==provBase);
+}
+
+bool cProviderIrdeto::MatchEMM(const unsigned char *data)
+{
+  const unsigned int len=cParseIrdeto::AddrLen(data);
+  const unsigned int mode=cParseIrdeto::AddrBase(data);
+  return (   mode<0x10 && len<=sizeof(provId)
+          && mode==provBase && (!len || !memcmp(&data[4],provId,len)));
+}
+
+unsigned long long cProviderIrdeto::ProvId(void)
+{
+  return (provId[0]<<24)+(provId[1]<<16)+(provId[2]<<8)+provBase;
+}
+
+// -- cCardIrdeto --------------------------------------------------------------
+
+cCardIrdeto::cCardIrdeto(unsigned char hb, const unsigned char *hs)
+{
+  hexBase=hb;
+  memcpy(hexSer,hs,sizeof(hexSer));
+}
+
+bool cCardIrdeto::MatchEMM(const unsigned char *data)
+{
+  const unsigned int len=cParseIrdeto::AddrLen(data);
+  const unsigned int mode=cParseIrdeto::AddrBase(data);
+  return (   mode>=0x10 && len<=sizeof(hexSer)
+          && mode==hexBase && (!len || !memcmp(&data[4],hexSer,len)));
+}
+
+int cCardIrdeto::UpdateType(const unsigned char *data)
+{
+  switch(cParseIrdeto::AddrLen(data)) {
+    case 0:  return 0; // global
+    case 1:
+    case 2:  return 2; // shared
+    default: return 3; // unique
+    }
+}
+
+// -- cParseIrdeto -------------------------------------------------------------
+
+unsigned int cParseIrdeto::AddrLen(const unsigned char *data)
+{
+  return data[3] & 0x07;
+}
+
+unsigned int cParseIrdeto::AddrBase(const unsigned char *data)
+{
+  return data[3] >> 3;
+}
+
+// -- cProviderSeca ------------------------------------------------------------
+
+cProviderSeca::cProviderSeca(const unsigned char *pi, const unsigned char *s)
+{
+  memcpy(provId,pi,sizeof(provId));
+  memcpy(sa,s,sizeof(sa));
+}
+
+bool cProviderSeca::MatchID(const unsigned char *data)
+{
+  const unsigned char *id=cParseSeca::ProvIdPtr(data);
+  return id && !memcmp(id,provId,sizeof(provId));
+}
+
+bool cProviderSeca::MatchEMM(const unsigned char *data)
+{
+  return TID(data)==0x84 &&
+         !memcmp(SID(data),provId,sizeof(provId)) && !memcmp(SA(data),sa,sizeof(sa));
+}
+
+unsigned long long cProviderSeca::ProvId(void)
+{
+  return (provId[0]<<8)+provId[1];
+}
+
+// -- cCardSeca ----------------------------------------------------------------
+
+cCardSeca::cCardSeca(const unsigned char *u)
+{
+  memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardSeca::MatchEMM(const unsigned char *data)
+{
+  return TID(data)==0x82 &&
+         !memcmp(UA(data),ua,sizeof(ua));
+}
+
+// -- cParseSeca ---------------------------------------------------------------
+
+int cParseSeca::CmdLen(const unsigned char *data)
+{
+  struct SecaCmd *s=(struct SecaCmd *)data;
+  return ((s->cmdLen1&0x0f)<<8) | s->cmdLen2;
+}
+
+int cParseSeca::Payload(const unsigned char *data, const unsigned char **payload)
+{
+  int l;
+  switch(TID(data)) {
+    case 0x80:
+    case 0x81: l=sizeof(struct SecaEcm); break;
+    case 0x82: l=sizeof(struct SecaEmmUnique); break;
+    case 0x84: l=sizeof(struct SecaEmmShared); break;
+    default: return -1;
+    }
+  if(payload) *payload=&data[l];
+  return CmdLen(data)-l+sizeof(struct SecaCmd);
+}
+
+int cParseSeca::SysMode(const unsigned char *data)
+{
+  switch(TID(data)) {
+    case 0x80:
+    case 0x81: return ((struct SecaEcm *)data)->sm; break;
+    case 0x82: return ((struct SecaEmmUnique *)data)->sm; break;
+    case 0x84: return ((struct SecaEmmShared *)data)->sm; break;
+    default: return -1;
+    }
+}
+
+int cParseSeca::KeyNr(const unsigned char *data)
+{
+  switch(TID(data)) {
+    case 0x80:
+    case 0x81: return ((struct SecaEcm *)data)->keyNr; break;
+    case 0x82: return ((struct SecaEmmUnique *)data)->keyNr; break;
+    case 0x84: return ((struct SecaEmmShared *)data)->keyNr; break;
+    default: return -1;
+    }
+}
+
+const unsigned char *cParseSeca::ProvIdPtr(const unsigned char *data)
+{
+  switch(TID(data)) {
+    case 0x80:
+    case 0x81: return ((struct SecaEcm *)data)->id;
+    case 0x82: return ((struct SecaEmmUnique *)data)->id;
+    case 0x84: return ((struct SecaEmmShared *)data)->id;
+    }
+  return 0;
+}
+
+int cParseSeca::ProvId(const unsigned char *data)
+{
+  const unsigned char *id=cParseSeca::ProvIdPtr(data);
+  return id ? (id[0]<<8)+id[1] : -1;
+}
+
+// -- cProviderViaccess --------------------------------------------------------
+
+cProviderViaccess::cProviderViaccess(void)
+{
+  sharedEmm=0; sharedLen=sharedToggle=0;
+}
+
+cProviderViaccess::cProviderViaccess(const unsigned char *id, const unsigned char *s)
+{
+  sharedEmm=0; sharedLen=sharedToggle=0;
+  memcpy(ident,id,sizeof(ident));
+  ident[2]&=0xf0;
+  memcpy(sa,s,sizeof(sa));
+}
+
+cProviderViaccess::~cProviderViaccess()
+{
+  free(sharedEmm);
+}
+
+bool cProviderViaccess::MatchID(const unsigned char *data)
+{
+  const unsigned char *id=cParseViaccess::ProvIdPtr(data);
+  return id && id[0]==ident[0] && id[1]==ident[1] && (id[2]&0xf0)==ident[2];
+}
+
+bool cProviderViaccess::MatchEMM(const unsigned char *data)
+{
+  switch(data[0]) {
+    case 0x8e: 
+      if(memcmp(&data[3],sa,sizeof(sa)-1)) break;
+      if((data[6]&2)==0)
+        return sharedEmm && MatchID(sharedEmm);
+      // fall through
+    case 0x8c:
+    case 0x8d:
+      return MatchID(data);
+    }
+  return false;
+}
+
+unsigned long long cProviderViaccess::ProvId(void)
+{
+  return (ident[0]<<16)+(ident[1]<<8)+ident[2];
+}
+
+int cProviderViaccess::UpdateType(const unsigned char *data)
+{
+  switch(data[0]) {
+    case 0x8e: return 2; // shared
+    case 0x8c:
+    case 0x8d:
+    default:   return 0; // global
+    }
+}
+
+int cProviderViaccess::Assemble(cAssembleData *ad)
+{
+  const unsigned char *data=ad->Data();
+  int len=SCT_LEN(data);
+  switch(data[0]) {
+    case 0x8C:
+    case 0x8D:
+      if(data[0]!=sharedToggle) {
+        free(sharedEmm);
+        sharedLen=len;
+        sharedEmm=(unsigned char *)malloc(len);
+        if(sharedEmm) memcpy(sharedEmm,data,sharedLen);
+        sharedToggle=data[0];
+        }
+      break;
+
+    case 0x8E:
+      if(sharedEmm) {
+        unsigned char *tmp=AUTOMEM(len+sharedLen);
+        unsigned char *ass=(unsigned char *)cParseViaccess::NanoStart(data);
+        len-=(ass-data);
+        if((data[6]&2)==0) {
+          const int addrlen=len-8;
+          len=0;
+          tmp[len++]=0x9e;
+          tmp[len++]=addrlen;
+          memcpy(&tmp[len],&ass[0],addrlen); len+=addrlen;
+          tmp[len++]=0xf0;
+          tmp[len++]=0x08;
+          memcpy(&tmp[len],&ass[addrlen],8); len+=8;
+          }
+        else {
+          memcpy(tmp,ass,len);
+          }
+        ass=(unsigned char *)cParseViaccess::NanoStart(sharedEmm);
+        int l=sharedLen-(ass-sharedEmm);
+        memcpy(&tmp[len],ass,l); len+=l;
+
+        ass=(unsigned char *)malloc(len+7);
+        if(ass) {
+          memcpy(ass,data,7);
+          SortNanos(ass+7,tmp,len);
+          SetSctLen(ass,len+4);
+          ad->SetAssembled(ass);
+          return 1; // assembled
+          }
+        }
+      break;
+    }
+  return -1; // ignore
+}
+
+// -- cCardViaccess ------------------------------------------------------------
+
+cCardViaccess::cCardViaccess(const unsigned char *u)
+{
+  memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardViaccess::MatchEMM(const unsigned char *data)
+{
+  return data[0]==0x88 && !memcmp(&data[3],ua,sizeof(ua));
+}
+
+// -- cParseViaccess -----------------------------------------------------------
+
+const unsigned char *cParseViaccess::NanoStart(const unsigned char *data)
+{
+  switch(data[0]) {
+    case 0x88: return &data[8];
+    case 0x8e: return &data[7];
+    case 0x8c:
+    case 0x8d: return &data[3];
+    case 0x80:
+    case 0x81: return &data[4];
+    }
+  return 0;
+}
+
+const unsigned char *cParseViaccess::CheckNano90(const unsigned char *data)
+{
+  return CheckNano90FromNano(NanoStart(data));
+}
+
+const unsigned char *cParseViaccess::CheckNano90FromNano(const unsigned char *data)
+{
+  if(data && data[0]==0x90 && data[1]==0x03) return data;
+  return 0;
+}
+
+int cParseViaccess::KeyNr(const unsigned char *data)
+{
+  return KeyNrFromNano(CheckNano90(data));
+}
+
+int cParseViaccess::KeyNrFromNano(const unsigned char *data)
+{
+  return data ? data[4]&0x0f : -1;
+}
+
+const unsigned char *cParseViaccess::ProvIdPtr(const unsigned char *data)
+{
+  data=CheckNano90(data);
+  return data ? &data[2] : 0;
+}
+
+int cParseViaccess::ProvId(const unsigned char *data)
+{
+  const unsigned char *id=cParseViaccess::ProvIdPtr(data);
+  return id ? (id[0]<<16)+(id[1]<<8)+(id[2]&0xf0) : -1;
+}
+
+// -- cCardNagra2 --------------------------------------------------------------
+
+cCardNagra2::cCardNagra2(const unsigned char *a)
+{
+  addr[0]=a[2];
+  addr[1]=a[1];
+  addr[2]=a[0];
+  addr[3]=a[3];
+}
+
+bool cCardNagra2::MatchEMM(const unsigned char *data)
+{
+  return data[0]==0x82 ||
+        (data[0]==0x83 && !memcmp(&data[3],addr,(data[7]==0x10)?3:4));
+}
+
+int cCardNagra2::UpdateType(const unsigned char *data)
+{
+  if(data[0]==0x83) return (data[7]==0x10) ? 2:3;
+  return 0;
+}
+
+// -- cProviderConax -----------------------------------------------------------
+
+cProviderConax::cProviderConax(const unsigned char *a)
+{
+  memcpy(addr,a,sizeof(addr));
+}
+
+bool cProviderConax::MatchID(const unsigned char *data)
+{
+  return MatchEMM(data);
+}
+
+bool cProviderConax::MatchEMM(const unsigned char *data)
+{
+  return !memcmp(&data[3],addr,sizeof(addr));
+}
+
+unsigned long long cProviderConax::ProvId(void)
+{
+  return Bin2LongLong(addr,sizeof(addr));
+}
+
+// -- cCardConax ---------------------------------------------------------------
+
+cCardConax::cCardConax(const unsigned char *a)
+{
+  memcpy(addr,a,sizeof(addr));
+}
+
+bool cCardConax::MatchEMM(const unsigned char *data)
+{
+  return !memcmp(&data[3],addr,sizeof(addr));
+}
+
+// -- cProviderCryptoworks -----------------------------------------------------
+
+cProviderCryptoworks::cProviderCryptoworks(const unsigned char *a)
+{
+  memcpy(addr,a,sizeof(addr));
+}
+
+bool cProviderCryptoworks::MatchID(const unsigned char *data)
+{
+  return MatchEMM(data);
+}
+
+bool cProviderCryptoworks::MatchEMM(const unsigned char *data)
+{
+  return false;
+}
+
+unsigned long long cProviderCryptoworks::ProvId(void)
+{
+  return Bin2LongLong(addr,sizeof(addr));
+}
+
+// -- cCardCryptoworks----------------------------------------------------------
+
+cCardCryptoworks::cCardCryptoworks(const unsigned char *a)
+{
+  memcpy(addr,a,sizeof(addr));
+  sharedEmm=0; sharedLen=globalToggle=0; globalCrc=0;
+}
+
+cCardCryptoworks::~cCardCryptoworks()
+{
+  free(sharedEmm);
+}
+
+bool cCardCryptoworks::MatchEMM(const unsigned char *data)
+{
+  if((data[1]&0xF0)==0x70 && data[3]==0xA9 && data[4]==0xff) {
+    switch(data[0]) {
+      case 0x82: return !memcmp(&data[5],addr,sizeof(addr));
+      case 0x84: return !memcmp(&data[5],addr,sizeof(addr)-1);
+      case 0x86:
+      case 0x88:
+      case 0x89: return true;
+      }
+    }
+  return false;
+}
+
+int cCardCryptoworks::UpdateType(const unsigned char *data)
+{
+  switch(data[0]) {
+    case 0x82: return 3; // unique
+    case 0x84:
+    case 0x86: return 2; // shared
+    default:   return 0; // global
+    }
+}
+
+int cCardCryptoworks::Assemble(cAssembleData *ad)
+{
+  const unsigned char *data=ad->Data();
+  int len=SCT_LEN(data);
+  switch(data[0]) {
+    case 0x82:
+      return 0; // no assemble needed
+
+    case 0x84:
+      free(sharedEmm);
+      sharedEmm=(unsigned char *)malloc(len);
+      if(sharedEmm) {
+        memcpy(sharedEmm,data,len);
+        sharedLen=len;
+        }
+      break;
+
+    case 0x86:
+      if(sharedEmm) {
+        int alen=len-5 + sharedLen-12;
+        unsigned char *tmp=AUTOMEM(alen);
+        memcpy(tmp,&data[5],len-5);
+        memcpy(tmp+len-5,&sharedEmm[12],sharedLen-12);
+
+        unsigned char *ass=(unsigned char *)malloc(alen+12);
+        if(!ass) return -1; // ignore
+        memcpy(ass,sharedEmm,12);
+        SortNanos(ass+12,tmp,alen);
+        SetSctLen(ass,alen+9);
+        free(sharedEmm); sharedEmm=0;
+        if(ass[11]==alen) { // sanity check
+          ad->SetAssembled(ass);
+          return 1; // assembled
+          }
+        }
+      break;
+
+    case 0x88:
+    case 0x89:
+      if(data[0]!=globalToggle) {
+        globalToggle=data[0];
+        unsigned int crc=crc32_le(0,data+1,len-1);
+        if(crc!=globalCrc) {
+          globalCrc=crc;
+          return 0; // no assemble needed
+          }
+        }
+      break;
+    }
+  return -1; // ignore
+}
+
+// -- cProviderNDS -------------------------------------------------------------
+
+cProviderNDS::cProviderNDS(const unsigned char *s)
+{
+  memcpy(sa,s,sizeof(sa));
+}
+
+bool cProviderNDS::MatchID(const unsigned char *data)
+{
+  return MatchEMM(data);
+}
+
+bool cProviderNDS::MatchEMM(const unsigned char *data)
+{
+  return cParseNDS::HasAddr(data,sa);
+}
+
+unsigned long long cProviderNDS::ProvId(void)
+{
+  return Bin2LongLong(sa,sizeof(sa));
+}
+
+int cProviderNDS::Assemble(cAssembleData *ad)
+{
+  return cParseNDS::Assemble(ad,sa);
+}
+
+// -- cCardNDS -----------------------------------------------------------------
+
+cCardNDS::cCardNDS(const unsigned char *u)
+{
+  memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardNDS::MatchEMM(const unsigned char *data)
+{
+  return cParseNDS::HasAddr(data,ua);
+}
+
+int cCardNDS::Assemble(cAssembleData *ad)
+{
+  return cParseNDS::Assemble(ad,ua);
+}
+
+// -- cParseNDS ----------------------------------------------------------------
+
+unsigned int cParseNDS::NumAddr(const unsigned char *data)
+{
+  return ((data[3]&0x30)>>4)+1;
+}
+
+int cParseNDS::AddrMode(const unsigned char *data)
+{
+  switch(data[3]&0xC0) {
+    case 0x40: return 3;
+    case 0x80: return 2;
+    default:   return 0;
+    }
+}
+
+bool cParseNDS::HasAddr(const unsigned char *data, const unsigned char *a)
+{
+  int s;
+  switch(AddrMode(data)) {
+    case 2: s=3; break;
+    case 3: s=4; break;
+    default: return true;
+    }
+  for(int l=NumAddr(data)-1; l>=0; l--) {
+    if(!memcmp(&data[l*4+4],a,s)) return true;
+    }
+  return false;
+}
+
+const unsigned char *cParseNDS::PayloadStart(const unsigned char *data)
+{
+  //return &data[4 + NumAddr(data)*4 + 2];
+  if(AddrMode(data)==0) return &data[4];
+  else                  return &data[4+NumAddr(data)*4];
+}
+
+int cParseNDS::PayloadSize(const unsigned char *data)
+{
+  //return emm[2]+emm[3]+4-1+5;
+  int l=SCT_LEN(data);
+  if(AddrMode(data)==0) return l-(4);
+  else                  return l-(4+NumAddr(data)*4);
+}
+
+int cParseNDS::Assemble(cAssembleData *ad, const unsigned char *a)
+{
+  const unsigned char *data=ad->Data();
+  int len=cParseNDS::PayloadSize(data);
+  const unsigned char *pl=cParseNDS::PayloadStart(data);
+  switch(cParseNDS::AddrMode(data)) {
+    case 0:
+     {
+     int n=*(pl-1) & 0x01;
+     for(int i=cParseNDS::NumAddr(data); i>0 && len>0; i--) {
+       int l;
+       if(n) { 
+         l=(((*pl & 0x3F)<<8) + *(pl+1)) << 1;
+         pl+=2; len-=2;
+         }
+       else {
+         l=(*pl & 0x3F) << 1;
+         pl++; len--;
+         }
+       if(l>0 && l<=len) {
+         unsigned char *ass=(unsigned char *)malloc(len+4); if(!ass) return -1; // ignore
+         ass[0]=data[0];
+         ass[3]=data[3]&0x0F;
+         memcpy(&ass[4],pl,l);
+         SetSctLen(ass,l+1);
+         ad->SetAssembled(ass);
+         pl+=l; len-=l;
+         }
+       }
+     return 1; // assembled
+     }
+
+    case 2:
+    case 3:
+      {
+      unsigned char *ass=(unsigned char *)malloc(len+8); if(!ass) return -1; // ignore
+      ass[0]=data[0];
+      ass[3]=data[3]&0x0F;
+      memcpy(&ass[4],a,4);
+      memcpy(&ass[8],pl,len);
+      SetSctLen(ass,len+5);
+      ad->SetAssembled(ass);
+      return 1; // assembled
+      }
+    }
+  return -1; // ignore
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/parse.h
new file mode 100644 (file)
index 0000000..4d7cb15
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___PARSE_H
+#define ___PARSE_H
+
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+void SetSctLen(unsigned char *data, int len);
+
+// ----------------------------------------------------------------
+
+class cAssSct : public cSimpleItem {
+private:
+  const unsigned char *data;
+public:
+  cAssSct(const unsigned char *Data);
+  const unsigned char *Data(void) const { return data; }
+  };
+
+class cAssembleData : private cSimpleList<cAssSct> {
+private:
+  const unsigned char *data;
+  cAssSct *curr;
+public:
+  cAssembleData(const unsigned char *Data);
+  void SetAssembled(const unsigned char *Data);
+  const unsigned char *Assembled(void);
+  const unsigned char *Data(void) const { return data; }
+  };
+
+class cProvider : public cSimpleItem {
+public:
+  virtual bool MatchID(const unsigned char *data)=0;
+  virtual bool MatchEMM(const unsigned char *data)=0;
+  virtual unsigned long long ProvId(void)=0;
+  virtual int UpdateType(const unsigned char *data) { return 2; }
+  virtual int Assemble(cAssembleData *ad) { return 0; }
+  };
+
+class cProviders : public cSimpleList<cProvider> {
+public:
+  void AddProv(cProvider *p);
+  cProvider *FindProv(const unsigned char *data);
+  cProvider *MatchProv(const unsigned char *data);
+  };  
+
+class cCard {
+public:
+  virtual ~cCard() {}
+  virtual bool MatchEMM(const unsigned char *data)=0;
+  virtual int UpdateType(const unsigned char *data) { return 3; }
+  virtual int Assemble(cAssembleData *ad) { return 0; }
+  };
+
+class cIdSet : public cProviders {
+protected:
+  cCard *card;
+public:
+  cIdSet(void);
+  ~cIdSet();
+  void SetCard(cCard *c);
+  void ResetIdSet(void);
+  bool MatchEMM(const unsigned char *data);
+  bool MatchAndAssemble(cAssembleData *ad, int *updtype, cProvider **p);
+  };
+
+// -- IRDETO ------------------------------------------------------
+
+class cProviderIrdeto : public cProvider {
+public:
+  unsigned char provBase;
+  unsigned char provId[3];
+  //
+  cProviderIrdeto(void) {}
+  cProviderIrdeto(unsigned char pb, const unsigned char *pi);
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  };
+
+class cCardIrdeto : public cCard {
+public:
+  unsigned char hexBase;
+  unsigned char hexSer[3];
+  //
+  cCardIrdeto(void) {}
+  cCardIrdeto(unsigned char hb, const unsigned char *hs);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual int UpdateType(const unsigned char *data);
+  };
+
+class cParseIrdeto {
+public:
+  static unsigned int AddrLen(const unsigned char *data);
+  static unsigned int AddrBase(const unsigned char *data);
+  };
+
+// -- SECA --------------------------------------------------------
+
+struct SecaCmd {
+  unsigned char tid;
+  unsigned char cmdLen1;
+  unsigned char cmdLen2;
+  };
+
+struct SecaEcm {
+  struct SecaCmd cmd;
+  unsigned char id[2];
+  unsigned char unknown;
+  unsigned char sm;
+  unsigned char keyNr;
+  };
+
+struct SecaEmmShared {
+  struct SecaCmd cmd;
+  unsigned char id[2];
+  unsigned char sa[3];
+  unsigned char sm;
+  unsigned char keyNr;
+  };
+      
+struct SecaEmmUnique {
+  struct SecaCmd cmd;
+  unsigned char ua[6];
+  unsigned char id[2];
+  unsigned char sm;
+  unsigned char keyNr;
+  };
+
+#define TID(x) (((struct SecaCmd *)(x))->tid)
+#define SA(x)  (((struct SecaEmmShared *)(x))->sa)
+#define SID(x) (((struct SecaEmmShared *)(x))->id)
+#define UA(x)  (((struct SecaEmmUnique *)(x))->ua)
+#define UID(x) (((struct SecaEmmUnique *)(x))->id)
+
+class cProviderSeca : public cProvider {
+public:
+  unsigned char provId[2];
+  unsigned char sa[3];
+  //
+  cProviderSeca(void) {}
+  cProviderSeca(const unsigned char *pi, const unsigned char *s);
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  };
+
+class cCardSeca : public cCard {
+public:
+  unsigned char ua[6];
+  //
+  cCardSeca(void) {}
+  cCardSeca(const unsigned char *u);
+  virtual bool MatchEMM(const unsigned char *data);
+  };
+
+class cParseSeca {
+public:
+  static int CmdLen(const unsigned char *data);
+  static int Payload(const unsigned char *data, const unsigned char **payload);
+  static int SysMode(const unsigned char *data);
+  static int KeyNr(const unsigned char *data);
+  static const unsigned char *ProvIdPtr(const unsigned char *data);
+  static int ProvId(const unsigned char *data);
+  };
+
+// -- VIACCESS -----------------------------------------------------
+
+class cProviderViaccess : public cProvider {
+private:
+  unsigned char *sharedEmm;
+  int sharedLen, sharedToggle;
+public:
+  unsigned char ident[3];
+  unsigned char sa[4];
+  //
+  cProviderViaccess(void);
+  cProviderViaccess(const unsigned char *id, const unsigned char *s);
+  virtual ~cProviderViaccess();
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  virtual int UpdateType(const unsigned char *data);
+  virtual int Assemble(cAssembleData *ad);
+  };
+
+class cCardViaccess : public cCard {
+public:
+  unsigned char ua[5];
+  //
+  cCardViaccess(void) {}
+  cCardViaccess(const unsigned char *u);
+  virtual bool MatchEMM(const unsigned char *data);
+  };
+
+class cParseViaccess {
+public:
+  static const unsigned char *NanoStart(const unsigned char *data);
+  static const unsigned char *CheckNano90(const unsigned char *data);
+  static const unsigned char *CheckNano90FromNano(const unsigned char *data);
+  static int KeyNr(const unsigned char *data);
+  static int KeyNrFromNano(const unsigned char *data);
+  static const unsigned char *ProvIdPtr(const unsigned char *data);
+  static int ProvId(const unsigned char *data);
+  };
+
+// -- NAGRA 2 ------------------------------------------------------
+
+class cCardNagra2 : public cCard {
+public:
+  unsigned char addr[4];
+  //
+  cCardNagra2(const unsigned char *a);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual int UpdateType(const unsigned char *data);
+  };
+
+// -- CONAX --------------------------------------------------------
+
+class cProviderConax : public cProvider {
+public:
+  unsigned char addr[7];
+  //
+  cProviderConax(const unsigned char *a);
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  };
+
+class cCardConax : public cCard {
+public:
+  unsigned char addr[7];
+  //
+  cCardConax(const unsigned char *a);
+  virtual bool MatchEMM(const unsigned char *data);
+  };
+
+// -- CRYPTOWORKS --------------------------------------------------
+
+class cProviderCryptoworks : public cProvider {
+public:
+  unsigned char addr[5];
+  //
+  cProviderCryptoworks(const unsigned char *a);
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  };
+
+class cCardCryptoworks : public cCard {
+private:
+  unsigned char *sharedEmm;
+  int sharedLen, globalToggle;
+  unsigned int globalCrc;
+public:
+  unsigned char addr[5];
+  //
+  cCardCryptoworks(const unsigned char *a);
+  virtual ~cCardCryptoworks();
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual int UpdateType(const unsigned char *data);
+  virtual int Assemble(cAssembleData *ad);
+  };
+
+// -- NDS -----------------------------------------------------------
+
+class cProviderNDS : public cProvider {
+public:
+  unsigned char sa[4];
+  //
+  cProviderNDS(const unsigned char *s);
+  virtual bool MatchID(const unsigned char *data);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual unsigned long long ProvId(void);
+  virtual int Assemble(cAssembleData *ad);
+  };
+
+class cCardNDS : public cCard {
+public:
+  unsigned char ua[4];
+  //
+  cCardNDS(const unsigned char *u);
+  virtual bool MatchEMM(const unsigned char *data);
+  virtual int Assemble(cAssembleData *ad);
+  };
+
+class cParseNDS {
+public:
+  static unsigned int NumAddr(const unsigned char *data);
+  static int AddrMode(const unsigned char *data);
+  static bool IsSA(const unsigned char *data);
+  static bool HasAddr(const unsigned char *data, const unsigned char *a);
+  static const unsigned char *PayloadStart(const unsigned char *data);
+  static int PayloadSize(const unsigned char *data);
+  static int Assemble(cAssembleData *ad, const unsigned char *a);
+  };
+
+#endif //___PARSE_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx-old.diff b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx-old.diff
new file mode 100644 (file)
index 0000000..610f2a9
--- /dev/null
@@ -0,0 +1,75 @@
+diff -ur linux/drivers/media/dvb/ttpci/av7110_ca.c linux/drivers/media/dvb/ttpci/av7110_ca.c
+--- linux/drivers/media/dvb/ttpci/av7110_ca.c  2004-01-09 14:44:57.000000000 +0100
++++ linux/drivers/media/dvb/ttpci/av7110_ca.c  2005-09-02 16:52:08.000000000 +0200
+@@ -263,10 +263,31 @@
+       }
+       case CA_GET_MSG:
+-              break;
++        {
++                ca_pid_t *arg = (ca_pid_t*) parg;
++                u16 buf[4], res[2];
++                buf[0]=0x0745;
++                buf[1]=2;
++                buf[2]=arg->pid >> 16;
++                buf[3]=arg->pid & 0xFFFF;
++                av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++                arg->index=(res[0]<<16) + res[1];
++                break;
++        }
+       case CA_SEND_MSG:
+-              break;
++        {
++                ca_pid_t *arg = (ca_pid_t*) parg;
++                u16 buf[6], res[2];
++                buf[0]=0x0746;
++                buf[1]=4;
++                buf[2]=arg->pid >> 16;
++                buf[3]=arg->pid & 0xFFFF;
++                buf[4]=arg->index >> 16;
++                buf[5]=arg->index & 0xFFFF;
++                av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++                break;
++        }
+       case CA_GET_DESCR_INFO:
+       {
+@@ -295,6 +316,37 @@
+               break;
+       }
++        case CA_SET_PID:
++        {
++                int handle;
++                ca_pid_t *pid = (ca_pid_t*) parg;
++
++                if (pid->pid >= 0x1fff)
++                        return -EINVAL;
++                if (pid->index < 0 || pid->index >= 16)
++                        return -EINVAL;
++
++              if (down_interruptible (&av7110->demux.mutex))
++                      return -ERESTARTSYS;
++
++                for(handle=0; handle<32; handle++) {
++                  struct dvb_demux_filter *dvbdmxfilter=av7110->handle2filter[handle];
++                  if(dvbdmxfilter) {
++                    struct dvb_demux_feed *feed=dvbdmxfilter->feed;
++                    if(feed && feed->state==DMX_STATE_GO && feed->pid==pid->pid) {
++                      /* we map the new cmd to CacheError as it's not
++                         implemented anyways, i.e. free. */
++                      av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, CacheError, 1,
++                                  (handle<<8)|pid->index); /* hw handle / cw index*/
++                      break;
++                      }
++                    }
++                  }
++
++                up(&av7110->demux.mutex);
++                break;
++        }
++
+       default:
+               return -EINVAL;
+       }
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx.diff b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-cwidx.diff
new file mode 100644 (file)
index 0000000..7e28cd7
--- /dev/null
@@ -0,0 +1,93 @@
+diff -ur linux/drivers/media/dvb/ttpci/av7110_ca.c linux/drivers/media/dvb/ttpci/av7110_ca.c
+--- linux/drivers/media/dvb/ttpci/av7110_ca.c  2004-01-09 14:44:57.000000000 +0100
++++ linux/drivers/media/dvb/ttpci/av7110_ca.c  2006-05-09 19:49:42.000000000 +0200
+@@ -37,6 +37,9 @@
+ #include <linux/poll.h>
+ #include <linux/byteorder/swabb.h>
+ #include <linux/smp_lock.h>
++#ifndef LINUX_VERSION_CODE
++#include <linux/version.h>
++#endif
+ #define DEBUG_VARIABLE av7110_debug
+ extern int av7110_debug;
+@@ -263,10 +266,31 @@
+       }
+       case CA_GET_MSG:
+-              break;
++        {
++                ca_pid_t *arg = (ca_pid_t*) parg;
++                u16 buf[4], res[2];
++                buf[0]=0x0745;
++                buf[1]=2;
++                buf[2]=arg->pid >> 16;
++                buf[3]=arg->pid & 0xFFFF;
++                av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++                arg->index=(res[0]<<16) + res[1];
++                break;
++        }
+       case CA_SEND_MSG:
+-              break;
++        {
++                ca_pid_t *arg = (ca_pid_t*) parg;
++                u16 buf[6], res[2];
++                buf[0]=0x0746;
++                buf[1]=4;
++                buf[2]=arg->pid >> 16;
++                buf[3]=arg->pid & 0xFFFF;
++                buf[4]=arg->index >> 16;
++                buf[5]=arg->index & 0xFFFF;
++                av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++                break;
++        }
+       case CA_GET_DESCR_INFO:
+       {
+@@ -295,6 +319,45 @@
+               break;
+       }
++        case CA_SET_PID:
++        {
++                int handle;
++                ca_pid_t *pid = (ca_pid_t*) parg;
++
++                if (pid->pid >= 0x1fff)
++                        return -EINVAL;
++                if (pid->index < 0 || pid->index >= 16)
++                        return -EINVAL;
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16))
++                if (down_interruptible (&av7110->demux.mutex))
++#else
++                if (mutex_lock_interruptible (&av7110->demux.mutex))
++#endif
++                      return -ERESTARTSYS;
++
++                for(handle=0; handle<32; handle++) {
++                  struct dvb_demux_filter *dvbdmxfilter=av7110->handle2filter[handle];
++                  if(dvbdmxfilter) {
++                    struct dvb_demux_feed *feed=dvbdmxfilter->feed;
++                    if(feed && feed->state==DMX_STATE_GO && feed->pid==pid->pid) {
++                      /* we map the new cmd to CacheError as it's not
++                         implemented anyways, i.e. free. */
++                      av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, CacheError, 1,
++                                  (handle<<8)|pid->index); /* hw handle / cw index*/
++                      break;
++                      }
++                    }
++                  }
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16))
++                up(&av7110->demux.mutex);
++#else
++                mutex_unlock(&av7110->demux.mutex);
++#endif
++                break;
++        }
++
+       default:
+               return -EINVAL;
+       }
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-sct-cc.diff b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/dvb-sct-cc.diff
new file mode 100644 (file)
index 0000000..a9e7354
--- /dev/null
@@ -0,0 +1,13 @@
+--- linux/drivers/media/dvb/dvb-core/dvb_demux.c       2004-03-03 17:49:42.000000000 +0100
++++ linux/drivers/media/dvb/dvb-core/dvb_demux.c       2004-04-05 12:44:59.000000000 +0200
+@@ -329,8 +329,8 @@
+               ** in the following dvb_dmx_swfilter_section_new
+               */
+ #endif
+-              dvb_dmx_swfilter_section_new(feed);
+-              return 0;
++              //dvb_dmx_swfilter_section_new(feed);
++              //return 0;
+       }
+       if(buf[1] & 0x40)
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/vdr-1.4.x-sc7.diff b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/patches/vdr-1.4.x-sc7.diff
new file mode 100644 (file)
index 0000000..8ee66ef
--- /dev/null
@@ -0,0 +1,272 @@
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/ci.c vdr-1.4.7-sc7/ci.c
+--- vdr-1.4.7-orig/ci.c        2007-04-30 14:58:41.000000000 +0200
++++ vdr-1.4.7-sc7/ci.c 2007-05-13 18:04:04.000000000 +0200
+@@ -1502,9 +1502,8 @@
+   close(fd);
+ }
+-cCiHandler *cCiHandler::CreateCiHandler(const char *FileName)
++cCiHandler *cCiHandler::CreateCiHandler(int fd_ca)
+ {
+-  int fd_ca = open(FileName, O_RDWR);
+   if (fd_ca >= 0) {
+      ca_caps_t Caps;
+      if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0) {
+@@ -1520,8 +1519,7 @@
+            esyslog("ERROR: no CAM slots found");
+         }
+      else
+-        LOG_ERROR_STR(FileName);
+-     close(fd_ca);
++        LOG_ERROR_STR("CA_GET_CAP");
+      }
+   return NULL;
+ }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/ci.h vdr-1.4.7-sc7/ci.h
+--- vdr-1.4.7-orig/ci.h        2006-08-12 11:43:31.000000000 +0200
++++ vdr-1.4.7-sc7/ci.h 2007-06-24 19:46:39.000000000 +0200
+@@ -85,10 +85,12 @@
+ class cCiCaProgramData : public cListObject {
+ public:
+   int programNumber;
++  bool modified;
+   cList<cCiCaPidData> pidList;
+   cCiCaProgramData(int ProgramNumber)
+   {
+     programNumber = ProgramNumber;
++    modified = true;
+   }
+   };
+@@ -96,6 +98,8 @@
+ class cCiTransportLayer;
+ class cCiTransportConnection;
++#define VDR_IS_SC_PATCHED 402
++
+ class cCiHandler {
+ private:
+   cMutex mutex;
+@@ -123,7 +127,7 @@
+   void SendCaPmt(void);
+ public:
+   ~cCiHandler();
+-  static cCiHandler *CreateCiHandler(const char *FileName);
++  static cCiHandler *CreateCiHandler(int fd_ca);
+        ///< Creates a new cCiHandler for the given CA device.
+   int NumSlots(void) { return numSlots; }
+        ///< Returns the number of CAM slots provided by this CA device.
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/device.c vdr-1.4.7-sc7/device.c
+--- vdr-1.4.7-orig/device.c    2006-09-03 12:13:25.000000000 +0200
++++ vdr-1.4.7-sc7/device.c     2007-05-13 18:30:57.000000000 +0200
+@@ -395,6 +395,54 @@
+   return false;
+ }
++void cDevice::CiStartDecrypting(void)
++{
++  if (ciHandler)
++     ciHandler->StartDecrypting();
++}
++
++void cDevice::CiSetSource(int Source, int Transponder)
++{
++  cMutexLock MutexLock(&ciListMutex);
++  if (ciSource != Source || ciTransponder != Transponder)
++     ciProgramList.Clear();
++  ciSource = Source;
++  ciTransponder = Transponder;
++}
++
++void cDevice::CiAddPid(int ProgramNumber, int Pid, int StreamType)
++{
++  cMutexLock MutexLock(&ciListMutex);
++  cCiCaProgramData *ProgramData = NULL;
++  for (cCiCaProgramData *p = ciProgramList.First(); p; p = ciProgramList.Next(p)) {
++      if (p->programNumber == ProgramNumber) {
++         ProgramData = p;
++         for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
++             if (q->pid == Pid)
++                return;
++             }
++         }
++      }
++  if (!ProgramData)
++     ciProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
++  ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
++  ProgramData->modified=true;
++}
++
++void cDevice::CiSetPid(int Pid, bool Active)
++{
++  cMutexLock MutexLock(&ciListMutex);
++  for (cCiCaProgramData *p = ciProgramList.First(); p; p = ciProgramList.Next(p)) {
++      for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
++          if (q->pid == Pid) {
++             q->active = Active;
++             p->modified = true;
++             return;
++             }
++         }
++      }
++}
++
+ bool cDevice::AddPid(int Pid, ePidType PidType)
+ {
+   if (Pid || PidType == ptPcr) {
+@@ -424,6 +472,7 @@
+               }
+            if (ciHandler)
+               ciHandler->SetPid(Pid, true);
++           CiSetPid(Pid, true);
+            }
+         PRINTPIDS("a");
+         return true;
+@@ -453,6 +502,7 @@
+            }
+         if (ciHandler)
+            ciHandler->SetPid(Pid, true);
++        CiSetPid(Pid, true);
+         }
+      }
+   return true;
+@@ -481,6 +531,7 @@
+               pidHandles[n].pid = 0;
+               if (ciHandler)
+                  ciHandler->SetPid(Pid, false);
++              CiSetPid(Pid, false);
+               }
+            }
+         PRINTPIDS("E");
+@@ -663,6 +714,16 @@
+            }
+ #endif
+         }
++
++     CiSetSource(Channel->Source(), Channel->Transponder());
++     if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
++        CiAddPid(Channel->Sid(), Channel->Vpid(), 2);
++        for (const int *Apid = Channel->Apids(); *Apid; Apid++)
++            CiAddPid(Channel->Sid(), *Apid, 4);
++        for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
++            CiAddPid(Channel->Sid(), *Dpid, 0);
++        }
++
+      if (NeedsDetachReceivers)
+         DetachAllReceivers();
+      if (SetChannelDevice(Channel, LiveView)) {
+@@ -672,8 +733,7 @@
+            sectionHandler->SetStatus(true);
+            }
+         // Start decrypting any PIDs that might have been set in SetChannelDevice():
+-        if (ciHandler)
+-           ciHandler->StartDecrypting();
++        CiStartDecrypting();
+         }
+      else
+         Result = scrFailed;
+@@ -1258,8 +1318,7 @@
+          Unlock();
+          if (!Running())
+             Start();
+-         if (ciHandler)
+-            ciHandler->StartDecrypting();
++         CiStartDecrypting();
+          return true;
+          }
+       }
+@@ -1286,8 +1345,7 @@
+       else if (receiver[i])
+          receiversLeft = true;
+       }
+-  if (ciHandler)
+-     ciHandler->StartDecrypting();
++  CiStartDecrypting();
+   if (!receiversLeft)
+      Cancel(3);
+ }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/device.h vdr-1.4.7-sc7/device.h
+--- vdr-1.4.7-orig/device.h    2006-06-15 11:32:48.000000000 +0200
++++ vdr-1.4.7-sc7/device.h     2007-05-13 18:04:04.000000000 +0200
+@@ -311,6 +311,14 @@
+ protected:
+   cCiHandler *ciHandler;
++  int ciSource, ciTransponder;
++  cList<cCiCaProgramData> ciProgramList;
++  cMutex ciListMutex;
++  virtual void CiStartDecrypting(void);
++  virtual bool CiAllowConcurrent(void) const { return false; }
++  void CiSetSource(int Source, int Transponder);
++  void CiAddPid(int ProgramNumber, int Pid, int StreamType);
++  void CiSetPid(int Pid, bool Active);
+ public:
+   cCiHandler *CiHandler(void) { return ciHandler; }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/dvbdevice.c vdr-1.4.7-sc7/dvbdevice.c
+--- vdr-1.4.7-orig/dvbdevice.c 2007-02-24 12:10:14.000000000 +0100
++++ vdr-1.4.7-sc7/dvbdevice.c  2007-06-24 19:42:24.000000000 +0200
+@@ -419,7 +419,11 @@
+      dvb_frontend_info feinfo;
+      if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
+         frontendType = feinfo.type;
+-        ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n));
++        int fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
++        if(fd_ca>=0) {
++          ciHandler = cCiHandler::CreateCiHandler(fd_ca);
++          if(!ciHandler) close(fd_ca);
++          }
+         dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
+         }
+      else
+@@ -776,8 +780,12 @@
+            if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
+ #ifdef DO_MULTIPLE_RECORDINGS
+ #ifndef DO_MULTIPLE_CA_CHANNELS
+-              if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
+-                 needsDetachReceivers = Ca() != Channel->Ca();
++              if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN) {
++                 if(Channel->Ca()<CA_ENCRYPTED_MIN || CiAllowConcurrent())
++                   result = true;
++                 else
++                   needsDetachReceivers = Ca() != Channel->Ca();
++                 }
+               else
+ #endif
+               if (!IsPrimaryDevice())
+@@ -927,7 +935,7 @@
+            SetPid(&pidHandles[ptAudio], ptAudio, true);
+            if (ciHandler) {
+               ciHandler->SetPid(pidHandles[ptAudio].pid, true);
+-              ciHandler->StartDecrypting();
++              CiStartDecrypting();
+               }
+            }
+         }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/tools.c vdr-1.4.7-sc7/tools.c
+--- vdr-1.4.7-orig/tools.c     2006-12-02 12:12:59.000000000 +0100
++++ vdr-1.4.7-sc7/tools.c      2007-05-13 18:04:04.000000000 +0200
+@@ -542,9 +542,9 @@
+ // --- cTimeMs ---------------------------------------------------------------
+-cTimeMs::cTimeMs(void)
++cTimeMs::cTimeMs(int Ms)
+ {
+-  Set();
++  Set(Ms);
+ }
+ uint64_t cTimeMs::Now(void)
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/tools.h vdr-1.4.7-sc7/tools.h
+--- vdr-1.4.7-orig/tools.h     2006-12-03 18:38:38.000000000 +0100
++++ vdr-1.4.7-sc7/tools.h      2007-05-13 18:04:04.000000000 +0200
+@@ -162,7 +162,7 @@
+ private:
+   uint64_t begin;
+ public:
+-  cTimeMs(void);
++  cTimeMs(int Ms = 0);
+   static uint64_t Now(void);
+   void Set(int Ms = 0);
+   bool TimedOut(void);
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/de_DE.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/de_DE.po
new file mode 100644 (file)
index 0000000..29bdc5f
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr "Key Update"
+
+msgid "off"
+msgstr "aus"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Aktuelle Keys:"
+
+msgid "Key update status:"
+msgstr "Key update Status:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Gefundene Keys]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Neue Keys]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Karte reseten"
+
+msgid "Really reset card?"
+msgstr "Karte wirklich reseten?"
+
+msgid "Module config"
+msgstr "Modul Einstellungen"
+
+msgid "Reset module to default"
+msgstr "Modul auf Standard zurücksetzen"
+
+msgid "Really reset module to default?"
+msgstr "Wirklich Modul auf Standard zurücksetzen?"
+
+msgid "Module"
+msgstr "Modul"
+
+msgid "Message logging"
+msgstr "Meldungsprotokolierung"
+
+msgid "Disable ALL modules"
+msgstr "ALLE Module ausschalten"
+
+msgid "Reset ALL modules to default"
+msgstr "ALLE Module auf Standard zurücksetzen"
+
+msgid "Really disable ALL modules?"
+msgstr "Wirklich ALLE Module ausschalten?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "Wirklich ALLE Module auf Standard zurücksetzen?"
+
+msgid "Cryptsystem options"
+msgstr "Cryptsystem Optionen"
+
+msgid "Cryptsystem options..."
+msgstr "Cryptsystem Optionen..."
+
+msgid "Message logging..."
+msgstr "Meldungsprotokolierung..."
+
+msgid "Smartcard interface"
+msgstr "Smartcard Interface"
+
+msgid "(empty)"
+msgstr "(leer)"
+
+msgid "Status information..."
+msgstr "Status Informationen..."
+
+msgid "Flush ECM cache"
+msgstr "ECM Zwischenspeicher leeren"
+
+msgid "Reload files"
+msgstr "Dateien neu laden"
+
+msgid "Really flush ECM cache?"
+msgstr "ECM Zwischenspeicher wirklich leeren?"
+
+msgid "Really reload files?"
+msgstr "Dateien wirklich neu laden?"
+
+msgid "Active! Can't reload files now"
+msgstr "Aktiv! Kann Dateien jetzt nicht neu laden"
+
+msgid "(none)"
+msgstr "(keiner)"
+
+msgid "active CAIDs"
+msgstr "aktive CAIDs"
+
+msgid "all CAIDs"
+msgstr "alle CAIDs"
+
+msgid "comment out"
+msgstr "auskommentieren"
+
+msgid "remove"
+msgstr "entfernen"
+
+msgid "Update keys (AU)"
+msgstr "Keys updaten (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr "AU starten bei EPG-Scan"
+
+msgid "Superseded keys"
+msgstr "Veraltete Keys"
+
+msgid "Concurrent FF streams"
+msgstr "Gleichzeitige FF Streams"
+
+msgid "Force TransferMode"
+msgstr "Transfermodus erzwingen"
+
+msgid "Prefer local systems"
+msgstr "Lokale Systeme bevorzugen"
+
+msgid "Active on DVB card"
+msgstr "Aktiv auf DVB Karte"
+
+msgid "Ignore CAID"
+msgstr "Ignoriere CAID"
+
+msgid "Log to console"
+msgstr "Meldungen auf Konsole"
+
+msgid "Log to file"
+msgstr "Meldungen in Datei"
+
+msgid "Filename"
+msgstr "Dateiname"
+
+msgid "Filesize limit (KB)"
+msgstr "Dateigrößenlimit (KB)"
+
+msgid "Log to syslog"
+msgstr "Meldungen in Syslog"
+
+msgid "Show user messages"
+msgstr "Benutzer Meldungen zeigen"
+
+msgid "A software emulated CAM"
+msgstr "Ein Software emuliertes CAM"
+
+msgid "undisclosed key"
+msgstr "unbekannter Key"
+
+msgid "allow ALL"
+msgstr "alle erlauben"
+
+msgid "block UNIQUE"
+msgstr "UNIQUE blocken"
+
+msgid "block SHARED"
+msgstr "SHARED blocken"
+
+msgid "block ALL"
+msgstr "alle blocken"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: EMM updates"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: PPV aktivieren"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: min. ECM Bearbeitungszeit"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr "Nagra2: EMM-S Pakete verwerfen"
+
+msgid "Nagra2: Enable AUXserver"
+msgstr "Nagra2: AUXserver aktivieren"
+
+msgid "Nagra2: AUXserver hostname"
+msgstr "Nagra2: AUXserver Hostname"
+
+msgid "Nagra2: AUXserver port"
+msgstr "Nagra2: AUXserver Port"
+
+msgid "Nagra2: AUXserver password"
+msgstr "Nagra2: AUXserver Passwort"
+
+msgid "don't touch"
+msgstr "nicht ändern"
+
+msgid "disable"
+msgstr "ausschalten"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Altersbeschränkung"
+
+msgid "Cardclient: connect immediately"
+msgstr "Cardclient: sofort verbinden"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fi_FI.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fi_FI.po
new file mode 100644 (file)
index 0000000..38071c5
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr "Avaimen päivitys"
+
+msgid "off"
+msgstr "pois"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Nykyiset avaimet:"
+
+msgid "Key update status:"
+msgstr "Tilausten päivitys:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Löydetyt päivitykset]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Uudet päivitykset]"
+
+msgid "Smartcard"
+msgstr "Älykortti"
+
+msgid "Reset card"
+msgstr "Nollaa kortti"
+
+msgid "Really reset card?"
+msgstr "Nollataanko kortti?"
+
+msgid "Module config"
+msgstr "Moduulin asetukset"
+
+msgid "Reset module to default"
+msgstr "Nollaa moduuli oletusarvoihin"
+
+msgid "Really reset module to default?"
+msgstr "Nollataanko moduuli oletusarvoihin?"
+
+msgid "Module"
+msgstr "Moduuli"
+
+msgid "Message logging"
+msgstr "Viestien tulostus"
+
+msgid "Disable ALL modules"
+msgstr "Poista kaikki moduulit käytöstä"
+
+msgid "Reset ALL modules to default"
+msgstr "Nollaa kaikki moduulit oletusarvoihin"
+
+msgid "Really disable ALL modules?"
+msgstr "Poistetaanko kaikki moduulit käytöstä?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "Nollataanko kaikki moduulit oletusarvoihin?"
+
+msgid "Cryptsystem options"
+msgstr "Salausjärjestelmien asetukset"
+
+msgid "Cryptsystem options..."
+msgstr "Salausjärjestelmien asetukset..."
+
+msgid "Message logging..."
+msgstr "Viestien tulostus..."
+
+msgid "Smartcard interface"
+msgstr "Älykorttiliitäntä"
+
+msgid "(empty)"
+msgstr "(tyhjä)"
+
+msgid "Status information..."
+msgstr "Tilannetiedot..."
+
+msgid "Flush ECM cache"
+msgstr "Tyhjennä ECM-välimuisti"
+
+msgid "Reload files"
+msgstr "Lataa avaintiedostot uudelleen"
+
+msgid "Really flush ECM cache?"
+msgstr "Tyhjennetäänkö ECM-välimuisti?"
+
+msgid "Really reload files?"
+msgstr "Ladataanko avaintiedostot uudelleen?"
+
+msgid "Active! Can't reload files now"
+msgstr "Aktiivinen! Uudelleen lataus ei onnistu"
+
+msgid "(none)"
+msgstr "(ei)"
+
+msgid "active CAIDs"
+msgstr "aktiiviset CAID:t"
+
+msgid "all CAIDs"
+msgstr "kaikki CAID:t"
+
+msgid "comment out"
+msgstr "kommentoi"
+
+msgid "remove"
+msgstr "poista"
+
+msgid "Update keys (AU)"
+msgstr "Tilausten päivitys (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr "Aloita AU EPG-päivityksessä"
+
+msgid "Superseded keys"
+msgstr "Vanhentuneet avaimet"
+
+msgid "Concurrent FF streams"
+msgstr "Yhtäaikainen salauksenpurku (FF)"
+
+msgid "Force TransferMode"
+msgstr "Pakota siirtotila"
+
+msgid "Prefer local systems"
+msgstr "Suosi paikallista salauksenpurkua"
+
+msgid "Active on DVB card"
+msgstr "Aktiivinen DVB-kortilla"
+
+msgid "Ignore CAID"
+msgstr "Jätä huomioimatta CAID"
+
+msgid "Log to console"
+msgstr "Tulosta konsoliin"
+
+msgid "Log to file"
+msgstr "Tulosta tiedostoon"
+
+msgid "Filename"
+msgstr "Tiedoston nimi"
+
+msgid "Filesize limit (KB)"
+msgstr "Tiedoston maksimikoko (KB)"
+
+msgid "Log to syslog"
+msgstr "Tulosta systeemilokiin"
+
+msgid "Show user messages"
+msgstr "Näytä käyttäjäviestit"
+
+msgid "A software emulated CAM"
+msgstr "Ohjelmistopohjainen salauksenpurku"
+
+msgid "undisclosed key"
+msgstr "tuntematon avain"
+
+msgid "allow ALL"
+msgstr "salli kaikki"
+
+msgid "block UNIQUE"
+msgstr "estä uniikit"
+
+msgid "block SHARED"
+msgstr "estä jaetut"
+
+msgid "block ALL"
+msgstr "estä kaikki"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: EMM-päivitykset"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: Aktivoi PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: Min. ECM-prosessointiaika"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr "Nagra2: Hylkää EMM-S-paketit"
+
+msgid "Nagra2: Enable AUXserver"
+msgstr "Nagra2: Käytä AUX-palvelinta"
+
+msgid "Nagra2: AUXserver hostname"
+msgstr "Nagra2: AUX-palvelimen osoite"
+
+msgid "Nagra2: AUXserver port"
+msgstr "Nagra2: AUX-palvelimen portti"
+
+msgid "Nagra2: AUXserver password"
+msgstr "Nagra2: AUX-palvelimen salasana"
+
+msgid "don't touch"
+msgstr "älä koske"
+
+msgid "disable"
+msgstr "poista"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Ikäraja"
+
+msgid "Cardclient: connect immediately"
+msgstr "Korttiasiakas: Yhdistä välittömästi"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fr_FR.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/fr_FR.po
new file mode 100644 (file)
index 0000000..b49c0ea
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr "Mise à jour des clés"
+
+msgid "off"
+msgstr "off"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Clés en cours:"
+
+msgid "Key update status:"
+msgstr "Statut de la mise à jour des clés:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Clés trouvées]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Nouvelles clés]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Réinitialiser la carte"
+
+msgid "Really reset card?"
+msgstr "Réinitialiser la carte?"
+
+msgid "Module config"
+msgstr "Paramètrer le module"
+
+msgid "Reset module to default"
+msgstr "Réinitialiser le module"
+
+msgid "Really reset module to default?"
+msgstr "Réinitialiser le module?"
+
+msgid "Module"
+msgstr "Module"
+
+msgid "Message logging"
+msgstr "Message logué"
+
+msgid "Disable ALL modules"
+msgstr "Désactiver TOUS les modules"
+
+msgid "Reset ALL modules to default"
+msgstr "Réinitialiser TOUS les modules"
+
+msgid "Really disable ALL modules?"
+msgstr "Désactiver TOUS les modules?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "Réinitialiser TOUS les modules?"
+
+msgid "Cryptsystem options"
+msgstr "Options Cryptsystem"
+
+msgid "Cryptsystem options..."
+msgstr "Options Cryptsystem..."
+
+msgid "Message logging..."
+msgstr "Message logué..."
+
+msgid "Smartcard interface"
+msgstr "Interface Smartcard"
+
+msgid "(empty)"
+msgstr "(vide)"
+
+msgid "Status information..."
+msgstr "Statut..."
+
+msgid "Flush ECM cache"
+msgstr "Vider le cache ECM"
+
+msgid "Reload files"
+msgstr "Recharger les fichiers"
+
+msgid "Really flush ECM cache?"
+msgstr "Vider le cache ECM?"
+
+msgid "Really reload files?"
+msgstr "Recharger les fichiers?"
+
+msgid "Active! Can't reload files now"
+msgstr "Actif! Impossible de recharger les fichiers"
+
+msgid "(none)"
+msgstr "(aucune)"
+
+msgid "active CAIDs"
+msgstr "CAIDs actifs"
+
+msgid "all CAIDs"
+msgstr "tous les CAIDs"
+
+msgid "comment out"
+msgstr "commenter"
+
+msgid "remove"
+msgstr "supprimer"
+
+msgid "Update keys (AU)"
+msgstr "Mise à jour des Clés (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr "Démarrer AU lors du scan EPG"
+
+msgid "Superseded keys"
+msgstr "Clés obsolètes"
+
+msgid "Concurrent FF streams"
+msgstr "FF streams en parallèle"
+
+msgid "Force TransferMode"
+msgstr "Forcer le TransferMode"
+
+msgid "Prefer local systems"
+msgstr "Préférer les systèmes locaux"
+
+msgid "Active on DVB card"
+msgstr "Actif sur la carte DVB"
+
+msgid "Ignore CAID"
+msgstr "Ignorer les CAID"
+
+msgid "Log to console"
+msgstr "Logger dans la console"
+
+msgid "Log to file"
+msgstr "Logger dans un fichier"
+
+msgid "Filename"
+msgstr "Nom du fichier"
+
+msgid "Filesize limit (KB)"
+msgstr "Taille limite (Ko)"
+
+msgid "Log to syslog"
+msgstr "Logger dans Syslog"
+
+msgid "Show user messages"
+msgstr "Afficher les messages utilisateur"
+
+msgid "A software emulated CAM"
+msgstr "Un logiciel emulateur de CAM"
+
+msgid "undisclosed key"
+msgstr "clé non-révélée"
+
+msgid "allow ALL"
+msgstr "permettre TOUS"
+
+msgid "block UNIQUE"
+msgstr "bloquer UNIQUE"
+
+msgid "block SHARED"
+msgstr "bloquer PARTAGE"
+
+msgid "block ALL"
+msgstr "bloquer TOUS"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: mise à jour EMM"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: activer les PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: temps de traitement min. ECM"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr "Nagra2: ignorer les packets EMM-S"
+
+msgid "Nagra2: Enable AUXserver"
+msgstr "Nagra2: Activer AUXserver"
+
+msgid "Nagra2: AUXserver hostname"
+msgstr "Nagra2: Hostname AUXserver"
+
+msgid "Nagra2: AUXserver port"
+msgstr "Nagra2: Port AUXserver"
+
+msgid "Nagra2: AUXserver password"
+msgstr "Nagra2: Mot de passe AUXserver"
+
+msgid "don't touch"
+msgstr "ne pas modifier"
+
+msgid "disable"
+msgstr "désactiver"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Contrôle parentale"
+
+msgid "Cardclient: connect immediately"
+msgstr "Cardclient: connecter immediatement"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/hu_HU.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/hu_HU.po
new file mode 100644 (file)
index 0000000..22136e0
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the VDR package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.11\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-11-01 16:45+0200\n"
+"Last-Translator: jv\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr ""
+
+msgid "off"
+msgstr "ki"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Jelenleg használt kulcs:"
+
+msgid "Key update status:"
+msgstr "Kulcsfrissítés állapota:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Talált kulcsok]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Új kulcsok]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Kártya újraindítása"
+
+msgid "Really reset card?"
+msgstr "Valóban újraindítjuk?"
+
+msgid "Module config"
+msgstr "Modul konfig"
+
+msgid "Reset module to default"
+msgstr "Modul alapállapotba"
+
+msgid "Really reset module to default?"
+msgstr "Valóban resetáljuk a modult?"
+
+msgid "Module"
+msgstr "Modul"
+
+msgid "Message logging"
+msgstr "Üzenetek naplózása"
+
+msgid "Disable ALL modules"
+msgstr "Összes modul ki"
+
+msgid "Reset ALL modules to default"
+msgstr "Összes modul alapállapotba"
+
+msgid "Really disable ALL modules?"
+msgstr "Valóban kapcsoljuk ki az összes modult?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "Valóban összes modul alapállapotba?"
+
+msgid "Cryptsystem options"
+msgstr "Kriptorendszer opciók"
+
+msgid "Cryptsystem options..."
+msgstr "Kriptorendszer opciók..."
+
+msgid "Message logging..."
+msgstr "Üzenetek naplózása..."
+
+msgid "Smartcard interface"
+msgstr "Smartcard interfész"
+
+msgid "(empty)"
+msgstr "(üres)"
+
+msgid "Status information..."
+msgstr "Státusz információ"
+
+msgid "Flush ECM cache"
+msgstr "ECM tár üritése"
+
+msgid "Reload files"
+msgstr "Fileok újraolvasása"
+
+msgid "Really flush ECM cache?"
+msgstr "Valóban üritsük az ECM tárat?"
+
+msgid "Really reload files?"
+msgstr "Biztos, hogy újraolvassuk a fileokat?"
+
+msgid "Active! Can't reload files now"
+msgstr "SC aktív! Nem tud újraolvasni."
+
+msgid "(none)"
+msgstr "(nincs)"
+
+msgid "active CAIDs"
+msgstr "aktív CAIDok"
+
+msgid "all CAIDs"
+msgstr "minden CAID"
+
+msgid "comment out"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "Update keys (AU)"
+msgstr "Kulcsok automatikus frissítése (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr ""
+
+msgid "Superseded keys"
+msgstr ""
+
+msgid "Concurrent FF streams"
+msgstr "Párhuzamos FF streamek"
+
+msgid "Force TransferMode"
+msgstr "Force TransferMode"
+
+msgid "Prefer local systems"
+msgstr "Helyi rendszerek előnyben"
+
+msgid "Active on DVB card"
+msgstr "Aktív a következő DVB kártyán"
+
+msgid "Ignore CAID"
+msgstr "CAID mellőzése"
+
+msgid "Log to console"
+msgstr "Naplózás a konzolon"
+
+msgid "Log to file"
+msgstr "Naplózás fileba"
+
+msgid "Filename"
+msgstr "File neve"
+
+msgid "Filesize limit (KB)"
+msgstr "Fileméret limit (KB)"
+
+msgid "Log to syslog"
+msgstr "Naplózás a rendszelogba"
+
+msgid "Show user messages"
+msgstr ""
+
+msgid "A software emulated CAM"
+msgstr "Szoftveresen emulált CAM"
+
+msgid "undisclosed key"
+msgstr "rejtett kulcs"
+
+msgid "allow ALL"
+msgstr "engedélyezd MIND"
+
+msgid "block UNIQUE"
+msgstr "tiltsd az EGYEDIt"
+
+msgid "block SHARED"
+msgstr "tiltsd a MEGOSZTOTTat"
+
+msgid "block ALL"
+msgstr "tiltsd MINDET"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: EMM frissítés"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: aktiváld a PPV-t"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: min. ECM feldolgozási idő"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr ""
+
+msgid "Nagra2: Enable AUXserver"
+msgstr ""
+
+msgid "Nagra2: AUXserver hostname"
+msgstr ""
+
+msgid "Nagra2: AUXserver port"
+msgstr ""
+
+msgid "Nagra2: AUXserver password"
+msgstr ""
+
+msgid "don't touch"
+msgstr "ne bántsd"
+
+msgid "disable"
+msgstr "kikapcsolva"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Szülői zár"
+
+msgid "Cardclient: connect immediately"
+msgstr "Kliens: csatlakozzon azonnal"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/it_IT.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/it_IT.po
new file mode 100644 (file)
index 0000000..34985a8
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr "Aggiornamento chiavi"
+
+msgid "off"
+msgstr "disattivo"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Chiavi in uso:"
+
+msgid "Key update status:"
+msgstr "Stato aggiornamento chiavi:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Chiavi trovate]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Nuove chiavi]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Reimposta smartcard"
+
+msgid "Really reset card?"
+msgstr "Reimpostare smartcard?"
+
+msgid "Module config"
+msgstr "Configurazione modulo"
+
+msgid "Reset module to default"
+msgstr "Reimposta modulo predefinito"
+
+msgid "Really reset module to default?"
+msgstr "Reimpostare modulo predefinito?"
+
+msgid "Module"
+msgstr "Modulo"
+
+msgid "Message logging"
+msgstr "Messaggi di log"
+
+msgid "Disable ALL modules"
+msgstr "Disabilita TUTTI i moduli"
+
+msgid "Reset ALL modules to default"
+msgstr "Reimposta TUTTI i moduli predefiniti"
+
+msgid "Really disable ALL modules?"
+msgstr "Disabilitare TUTTI i moduli?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "Reimpostare TUTTI i moduli predefiniti?"
+
+msgid "Cryptsystem options"
+msgstr "Opzioni sistemi di codifica"
+
+msgid "Cryptsystem options..."
+msgstr "Opzioni sistema codifica..."
+
+msgid "Message logging..."
+msgstr "Opzioni di log..."
+
+msgid "Smartcard interface"
+msgstr "Interfaccia Smartcard"
+
+msgid "(empty)"
+msgstr "(vuota)"
+
+msgid "Status information..."
+msgstr "Informazioni SoftCam"
+
+msgid "Flush ECM cache"
+msgstr "Svuota cache ECM"
+
+msgid "Reload files"
+msgstr "Ricarica il file delle chiavi"
+
+msgid "Really flush ECM cache?"
+msgstr "Svuotare cache ECM?"
+
+msgid "Really reload files?"
+msgstr "Ricaricare il file delle chiavi?"
+
+msgid "Active! Can't reload files now"
+msgstr "Chiave in uso! Impossibile ricaricare adesso"
+
+msgid "(none)"
+msgstr "(nessuna)"
+
+msgid "active CAIDs"
+msgstr "CAIDs attivi"
+
+msgid "all CAIDs"
+msgstr "tutti i CAIDs"
+
+msgid "comment out"
+msgstr "commenta"
+
+msgid "remove"
+msgstr "rimuovi"
+
+msgid "Update keys (AU)"
+msgstr "Aggiornamento chiavi (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr "Avvia AU con la scansione EPG"
+
+msgid "Superseded keys"
+msgstr "Chiavi scadute"
+
+msgid "Concurrent FF streams"
+msgstr "Flussi simultanei schede FF"
+
+msgid "Force TransferMode"
+msgstr "Forza mod. trasferimento"
+
+msgid "Prefer local systems"
+msgstr "Preferisci sistemi locali"
+
+msgid "Active on DVB card"
+msgstr "Attiva sulla scheda DVB"
+
+msgid "Ignore CAID"
+msgstr "Ignora CAID"
+
+msgid "Log to console"
+msgstr "Log in console"
+
+msgid "Log to file"
+msgstr "Log in un file"
+
+msgid "Filename"
+msgstr "Nome file"
+
+msgid "Filesize limit (KB)"
+msgstr "Limite dimensione file (KB)"
+
+msgid "Log to syslog"
+msgstr "Log in registro di sistema"
+
+msgid "Show user messages"
+msgstr "Mostra messaggi utente"
+
+msgid "A software emulated CAM"
+msgstr "Un software di emulazione CAM"
+
+msgid "undisclosed key"
+msgstr "chiave nascosta"
+
+msgid "allow ALL"
+msgstr "permetti TUTTI"
+
+msgid "block UNIQUE"
+msgstr "blocca al SERIALE"
+
+msgid "block SHARED"
+msgstr "blocca al GRUPPO"
+
+msgid "block ALL"
+msgstr "blocca TUTTO"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: aggiornamenti EMM"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: attiva PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: tempo min. esec. ECM"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr "Nagra2: elimina pacchetti EMM-S"
+
+msgid "Nagra2: Enable AUXserver"
+msgstr "Nagra2: abilita AUXserver"
+
+msgid "Nagra2: AUXserver hostname"
+msgstr "Nagra2: nome AUXserver"
+
+msgid "Nagra2: AUXserver port"
+msgstr "Nagra2: porta AUXserver"
+
+msgid "Nagra2: AUXserver password"
+msgstr "Nagra2: password AUXserver"
+
+msgid "don't touch"
+msgstr "non toccare"
+
+msgid "disable"
+msgstr "disabilita"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Filtro famiglia"
+
+msgid "Cardclient: connect immediately"
+msgstr "Cardclient: connetti subito"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/nl_NL.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/nl_NL.po
new file mode 100644 (file)
index 0000000..8c1d031
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr ""
+
+msgid "off"
+msgstr "uit"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Huidige keys:"
+
+msgid "Key update status:"
+msgstr "Key update status:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr ""
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Nieuwe keys]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Reset smartcard"
+
+msgid "Really reset card?"
+msgstr "Werkelijk smartcard resetten?"
+
+msgid "Module config"
+msgstr ""
+
+msgid "Reset module to default"
+msgstr ""
+
+msgid "Really reset module to default?"
+msgstr ""
+
+msgid "Module"
+msgstr ""
+
+msgid "Message logging"
+msgstr ""
+
+msgid "Disable ALL modules"
+msgstr ""
+
+msgid "Reset ALL modules to default"
+msgstr ""
+
+msgid "Really disable ALL modules?"
+msgstr ""
+
+msgid "Really reset ALL modules to default?"
+msgstr ""
+
+msgid "Cryptsystem options"
+msgstr ""
+
+msgid "Cryptsystem options..."
+msgstr ""
+
+msgid "Message logging..."
+msgstr ""
+
+msgid "Smartcard interface"
+msgstr "Smartcard interface"
+
+msgid "(empty)"
+msgstr "(leeg)"
+
+msgid "Status information..."
+msgstr "Status informatie..."
+
+msgid "Flush ECM cache"
+msgstr ""
+
+msgid "Reload files"
+msgstr "Opnieuw laden bestanden?"
+
+msgid "Really flush ECM cache?"
+msgstr ""
+
+msgid "Really reload files?"
+msgstr "Werkelijk opnieuw laden bestanden?"
+
+msgid "Active! Can't reload files now"
+msgstr "Actief! Kan bestanden niet herladen"
+
+msgid "(none)"
+msgstr "(geen)"
+
+msgid "active CAIDs"
+msgstr "actieve CAIDS"
+
+msgid "all CAIDs"
+msgstr "alle CAIDs"
+
+msgid "comment out"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "Update keys (AU)"
+msgstr "Keys updaten (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr ""
+
+msgid "Superseded keys"
+msgstr ""
+
+msgid "Concurrent FF streams"
+msgstr ""
+
+msgid "Force TransferMode"
+msgstr ""
+
+msgid "Prefer local systems"
+msgstr ""
+
+msgid "Active on DVB card"
+msgstr "Actief op DVB kaart"
+
+msgid "Ignore CAID"
+msgstr ""
+
+msgid "Log to console"
+msgstr ""
+
+msgid "Log to file"
+msgstr ""
+
+msgid "Filename"
+msgstr ""
+
+msgid "Filesize limit (KB)"
+msgstr ""
+
+msgid "Log to syslog"
+msgstr ""
+
+msgid "Show user messages"
+msgstr ""
+
+msgid "A software emulated CAM"
+msgstr "In software geëmuleerde CAM"
+
+msgid "undisclosed key"
+msgstr "onbekende key"
+
+msgid "allow ALL"
+msgstr "ALLES toestaan"
+
+msgid "block UNIQUE"
+msgstr "UNIEKE blokkeren"
+
+msgid "block SHARED"
+msgstr "GEDEELDE blokkeren"
+
+msgid "block ALL"
+msgstr "ALLES blokkeren"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: EMM updates"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: activeer PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: min. ECM bewerkingstijd"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr ""
+
+msgid "Nagra2: Enable AUXserver"
+msgstr ""
+
+msgid "Nagra2: AUXserver hostname"
+msgstr ""
+
+msgid "Nagra2: AUXserver port"
+msgstr ""
+
+msgid "Nagra2: AUXserver password"
+msgstr ""
+
+msgid "don't touch"
+msgstr "niet wijzigen"
+
+msgid "disable"
+msgstr "uitschakelen"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: Leeftijdsadvies"
+
+msgid "Cardclient: connect immediately"
+msgstr "Cardclient: direct contact maken"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/pl_PL.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/pl_PL.po
new file mode 100644 (file)
index 0000000..cccd71d
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr ""
+
+msgid "off"
+msgstr "wy³±cz"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Obecne klucze:"
+
+msgid "Key update status:"
+msgstr "Stan aktualizacji kluczy:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [widzianych kluczy]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [nowych kluczy]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Resetuj kartê"
+
+msgid "Really reset card?"
+msgstr "Na pewno zrestartowaæ kartê?"
+
+msgid "Module config"
+msgstr ""
+
+msgid "Reset module to default"
+msgstr ""
+
+msgid "Really reset module to default?"
+msgstr ""
+
+msgid "Module"
+msgstr ""
+
+msgid "Message logging"
+msgstr ""
+
+msgid "Disable ALL modules"
+msgstr ""
+
+msgid "Reset ALL modules to default"
+msgstr ""
+
+msgid "Really disable ALL modules?"
+msgstr ""
+
+msgid "Really reset ALL modules to default?"
+msgstr ""
+
+msgid "Cryptsystem options"
+msgstr ""
+
+msgid "Cryptsystem options..."
+msgstr ""
+
+msgid "Message logging..."
+msgstr ""
+
+msgid "Smartcard interface"
+msgstr "Interfejs Smartcard"
+
+msgid "(empty)"
+msgstr "(puste)"
+
+msgid "Status information..."
+msgstr "Informacje o stanie..."
+
+msgid "Flush ECM cache"
+msgstr ""
+
+msgid "Reload files"
+msgstr "Prze³aduj pliki"
+
+msgid "Really flush ECM cache?"
+msgstr ""
+
+msgid "Really reload files?"
+msgstr "Na pewno prze³adowaæ klucze?"
+
+msgid "Active! Can't reload files now"
+msgstr "Aktywny! Nie mo¿na teraz prze³adowaæ plików."
+
+msgid "(none)"
+msgstr "(brak)"
+
+msgid "active CAIDs"
+msgstr "aktywne CAID"
+
+msgid "all CAIDs"
+msgstr "wszystkie CAID"
+
+msgid "comment out"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "Update keys (AU)"
+msgstr "Aktualizuj klucze (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr ""
+
+msgid "Superseded keys"
+msgstr ""
+
+msgid "Concurrent FF streams"
+msgstr ""
+
+msgid "Force TransferMode"
+msgstr ""
+
+msgid "Prefer local systems"
+msgstr ""
+
+msgid "Active on DVB card"
+msgstr "Aktywny na karcie DVB"
+
+msgid "Ignore CAID"
+msgstr ""
+
+msgid "Log to console"
+msgstr ""
+
+msgid "Log to file"
+msgstr ""
+
+msgid "Filename"
+msgstr ""
+
+msgid "Filesize limit (KB)"
+msgstr ""
+
+msgid "Log to syslog"
+msgstr ""
+
+msgid "Show user messages"
+msgstr ""
+
+msgid "A software emulated CAM"
+msgstr "Programowo emulowany CAM"
+
+msgid "undisclosed key"
+msgstr "niejawny klucz"
+
+msgid "allow ALL"
+msgstr "dopu¶æ wszystkie"
+
+msgid "block UNIQUE"
+msgstr "blokuj unikaty"
+
+msgid "block SHARED"
+msgstr "blokuj dzielone"
+
+msgid "block ALL"
+msgstr "blokuj wszystkie"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: aktualizacje EMM"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: aktywuj PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: min. czas przetwarzania ECM"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr ""
+
+msgid "Nagra2: Enable AUXserver"
+msgstr ""
+
+msgid "Nagra2: AUXserver hostname"
+msgstr ""
+
+msgid "Nagra2: AUXserver port"
+msgstr ""
+
+msgid "Nagra2: AUXserver password"
+msgstr ""
+
+msgid "don't touch"
+msgstr "nie dotykaj"
+
+msgid "disable"
+msgstr "wy³±cz"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: wska¼nik rodzica"
+
+msgid "Cardclient: connect immediately"
+msgstr "Klient karty: pod³±cz natychmiast"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/ru_RU.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/ru_RU.po
new file mode 100644 (file)
index 0000000..30ad9b0
--- /dev/null
@@ -0,0 +1,219 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2008-03-13 15:43+0100\n"
+"Last-Translator: somebody\n"
+"Language-Team: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-5\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+
+msgid "Key update"
+msgstr "¾ÑÝÞÒÛÕÝØÕ ÚÛîçÐ"
+
+msgid "off"
+msgstr "ÒëÚÛ"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "¸áßÞÛì×ãÕÜëÕ ÚÛîçØ:"
+
+msgid "Key update status:"
+msgstr "¾ÑÝÞÒÛÕÝØÕ ÚÛîçÕÙ:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [ÚÛîçÕÙ ÝÐÙÔÕÝÞ]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [ÝÞÒëå ÚÛîçÕÙ]"
+
+msgid "Smartcard"
+msgstr "ºÐàâÞçÚÐ"
+
+msgid "Reset card"
+msgstr "ÁÑàÞáØâì ÚÐàâÞçÚã"
+
+msgid "Really reset card?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÚÐàâÞçÚã?"
+
+msgid "Module config"
+msgstr "½ÐáâàÞÙÚРÜÞÔãÛï"
+
+msgid "Reset module to default"
+msgstr "ÁÑàÞá ÜÞÔãÛï ßÞ ãÜÞÛçÐÝØî"
+
+msgid "Really reset module to default?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÜÞÔãÛì ßÞ ãÜÞÛçÐÝØî?"
+
+msgid "Module"
+msgstr "¼ÞÔãÛì"
+
+msgid "Message logging"
+msgstr "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ"
+
+msgid "Disable ALL modules"
+msgstr "²ëÚÛîçØâì ÒáÕ ÜÞÔãÛØ"
+
+msgid "Reset ALL modules to default"
+msgstr "ÁÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî"
+
+msgid "Really disable ALL modules?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ ÒëÚÛîçØâì ÒáÕ ÜÞÔãÛØ?"
+
+msgid "Really reset ALL modules to default?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî?"
+
+msgid "Cryptsystem options"
+msgstr "¾ßæØØ ÚàØßâÞáØáâÕÜ"
+
+msgid "Cryptsystem options..."
+msgstr "¾ßæØØ ÚàØßâÞáØáâÕÜ..."
+
+msgid "Message logging..."
+msgstr "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ..."
+
+msgid "Smartcard interface"
+msgstr "ºÐàâÞçÝëÙ ÜÞÔãÛì"
+
+msgid "(empty)"
+msgstr "(ßãáâÞ)"
+
+msgid "Status information..."
+msgstr "ÁÞáâÞïÝØÕ..."
+
+msgid "Flush ECM cache"
+msgstr "¾çØáâØâì Úíè ECM"
+
+msgid "Reload files"
+msgstr "¿ÕàÕ×ÐÓàãרâì äÐÙÛë"
+
+msgid "Really flush ECM cache?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ ÞçØáâØâì Úíè ECM?"
+
+msgid "Really reload files?"
+msgstr "´ÕÙáâÒØâÕÛìÝÞ ßÕàÕ×ÐÓàãרâì äÐÙÛë?"
+
+msgid "Active! Can't reload files now"
+msgstr "·ÐÝïâ! ½Õ ÜÞÓã ßÕàÕ×ÐÓàãרâì äÐÙÛë"
+
+msgid "(none)"
+msgstr "(ÝÕâ)"
+
+msgid "active CAIDs"
+msgstr "ÐÚâØÒÝëÕ CAID"
+
+msgid "all CAIDs"
+msgstr "ÒáÕ CAID"
+
+msgid "comment out"
+msgstr "×ÐÚÞÜÕÝâØàÞÒÐâì"
+
+msgid "remove"
+msgstr "ãÔÐÛØâì"
+
+msgid "Update keys (AU)"
+msgstr "¾ÑÝÞÒÛïâì ÚÛîçØ (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr "ÁâÐàâ ÐÒâÞÞÑÝÞÒÛÕÝØï ßàØ EPG áÚÐÝØàÞÒÐÝØØ"
+
+msgid "Superseded keys"
+msgstr "ÃáâÐàÕÒèØÕ ÚÛîçØ"
+
+msgid "Concurrent FF streams"
+msgstr "¿ÐàÐÛÛÕÛìÝëÕ ßÞâÞÚØ FF"
+
+msgid "Force TransferMode"
+msgstr "²ëÝãÖÔÕÝÝëÙ TransferMode"
+
+msgid "Prefer local systems"
+msgstr "¿àÕÔßÞçØâÐâì ÛÞÚÐÛìÝëÕ áØáâÕÜë"
+
+msgid "Active on DVB card"
+msgstr "°ÚâØÒÝëÙ ÝРßÛÐâÕ DVB"
+
+msgid "Ignore CAID"
+msgstr "¸ÓÝÞàØàÞÒÐâì CAID"
+
+msgid "Log to console"
+msgstr "ÁÞÞÑéÕÝØï Ò ÚÞÝáÞÛÕ"
+
+msgid "Log to file"
+msgstr "ÁÞÞÑéÕÝØï Ò äÐÙÛ"
+
+msgid "Filename"
+msgstr "½Ð×ÒÐÝØÕ äÐÙÛÐ"
+
+msgid "Filesize limit (KB)"
+msgstr "»ØÜØâ äÐÙÛР(Ú±)"
+
+msgid "Log to syslog"
+msgstr "ÁÞÞÑéÕÝØï Ò Syslog"
+
+msgid "Show user messages"
+msgstr "¿ÞÚÐ×Ðâì áÞÞÑéÕÝØï ßÞÛì×ÞÒÐâÕÛï"
+
+msgid "A software emulated CAM"
+msgstr "¿àÞÓàÐÜÜÝëÙ íÜãÛïâÞà CAM"
+
+msgid "undisclosed key"
+msgstr "ÝÕØ×ÒÕáâÝëÙ ÚÛîç"
+
+msgid "allow ALL"
+msgstr "ÒáÕ àÐ×àÕèØâì"
+
+msgid "block UNIQUE"
+msgstr "ÑÛÞÚØàÞÒÐâì UNIQUE"
+
+msgid "block SHARED"
+msgstr "ÑÛÞÚØàÞÒÐâì SHARED"
+
+msgid "block ALL"
+msgstr "ÒáÕ ÑÛÞÚØàÞÒÐâì"
+
+msgid "SC-Seca: EMM updates"
+msgstr "SC-Seca: ÞÑÝÞÒÛÕÝØï EMM"
+
+msgid "SC-Seca: activate PPV"
+msgstr "SC-Seca: ÐÚâØÒØàÞÒÐâì PPV"
+
+msgid "Nagra: min. ECM processing time"
+msgstr "Nagra: ÜØÝ. ÒàÕÜï ÞÑàÐÑÞâÚØ ECM"
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr "½ÐÓàÐ2: ×ÐßàÕâ EMM-S ßÐÚÕâÞÒ"
+
+msgid "Nagra2: Enable AUXserver"
+msgstr "Nagra2: àÐ×àÕèØâì AUXserver"
+
+msgid "Nagra2: AUXserver hostname"
+msgstr "Nagra2: AUXserver ØÜï åÞáâÐ"
+
+msgid "Nagra2: AUXserver port"
+msgstr "Nagra2: AUXserver ßÞàâ"
+
+msgid "Nagra2: AUXserver password"
+msgstr "Nagra2: AUXserver ßÐàÞÛì"
+
+msgid "don't touch"
+msgstr "ÝÕ Ø×ÜÕÝïâì"
+
+msgid "disable"
+msgstr "ÒëÚÛîçØâì"
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr "SC-Cryptoworks: ÒÞ×àÐáâÝÞÕ ÞÓàÐÝØçÕÝØÕ"
+
+msgid "Cardclient: connect immediately"
+msgstr "Cardclient: áÞÕÔØÝØâì ÝÕÜÕÔÛÕÝÝÞ"
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/sv_SE.po b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po/sv_SE.po
new file mode 100644 (file)
index 0000000..a7330ee
--- /dev/null
@@ -0,0 +1,218 @@
+# VDR plugin language source file.
+# Copyright (C) 2007 somebody
+# This file is distributed under the same license as the vdr-sc package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VDR 1.5.9\n"
+"Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
+"POT-Creation-Date: 2008-07-11 20:28-0400\n"
+"PO-Revision-Date: 2007-08-27 12:45+0200\n"
+"Last-Translator: somebody\n"
+"Language-Team: somebody\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Key update"
+msgstr ""
+
+msgid "off"
+msgstr "av"
+
+msgid "SoftCAM"
+msgstr "SoftCAM"
+
+msgid "Current keys:"
+msgstr "Aktuell nyckel:"
+
+msgid "Key update status:"
+msgstr "Nyckeluppdateringsstatus:"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [Seen keys]"
+msgstr "  [Påträffade nycklar]"
+
+#. TRANSLATORS: 2 leading spaces!
+msgid "  [New keys]"
+msgstr "  [Nya nycklar]"
+
+msgid "Smartcard"
+msgstr "Smartcard"
+
+msgid "Reset card"
+msgstr "Nollställ kortet"
+
+msgid "Really reset card?"
+msgstr "Bekräfta nollställning av kortet?"
+
+msgid "Module config"
+msgstr ""
+
+msgid "Reset module to default"
+msgstr ""
+
+msgid "Really reset module to default?"
+msgstr ""
+
+msgid "Module"
+msgstr ""
+
+msgid "Message logging"
+msgstr ""
+
+msgid "Disable ALL modules"
+msgstr ""
+
+msgid "Reset ALL modules to default"
+msgstr ""
+
+msgid "Really disable ALL modules?"
+msgstr ""
+
+msgid "Really reset ALL modules to default?"
+msgstr ""
+
+msgid "Cryptsystem options"
+msgstr ""
+
+msgid "Cryptsystem options..."
+msgstr ""
+
+msgid "Message logging..."
+msgstr ""
+
+msgid "Smartcard interface"
+msgstr "Smartcard-gränssnitt"
+
+msgid "(empty)"
+msgstr "(tom)"
+
+msgid "Status information..."
+msgstr "Statusinformation..."
+
+msgid "Flush ECM cache"
+msgstr ""
+
+msgid "Reload files"
+msgstr "Ladda om filer"
+
+msgid "Really flush ECM cache?"
+msgstr ""
+
+msgid "Really reload files?"
+msgstr "Bekräfta omladdning av filer?"
+
+msgid "Active! Can't reload files now"
+msgstr "Aktiv! Kan inte ladda om filer nu"
+
+msgid "(none)"
+msgstr "(ingen)"
+
+msgid "active CAIDs"
+msgstr "aktiva CAID"
+
+msgid "all CAIDs"
+msgstr "alla CAID"
+
+msgid "comment out"
+msgstr ""
+
+msgid "remove"
+msgstr ""
+
+msgid "Update keys (AU)"
+msgstr "Nyckeluppdatering (AU)"
+
+msgid "Start AU on EPG scan"
+msgstr ""
+
+msgid "Superseded keys"
+msgstr ""
+
+msgid "Concurrent FF streams"
+msgstr ""
+
+msgid "Force TransferMode"
+msgstr ""
+
+msgid "Prefer local systems"
+msgstr ""
+
+msgid "Active on DVB card"
+msgstr "Aktiv på DVB-kort"
+
+msgid "Ignore CAID"
+msgstr "Ignorera CAID"
+
+msgid "Log to console"
+msgstr ""
+
+msgid "Log to file"
+msgstr ""
+
+msgid "Filename"
+msgstr ""
+
+msgid "Filesize limit (KB)"
+msgstr ""
+
+msgid "Log to syslog"
+msgstr ""
+
+msgid "Show user messages"
+msgstr ""
+
+msgid "A software emulated CAM"
+msgstr "En mjukvaruemulerad CAM"
+
+msgid "undisclosed key"
+msgstr "ej avslöjad nyckel"
+
+msgid "allow ALL"
+msgstr ""
+
+msgid "block UNIQUE"
+msgstr ""
+
+msgid "block SHARED"
+msgstr ""
+
+msgid "block ALL"
+msgstr ""
+
+msgid "SC-Seca: EMM updates"
+msgstr ""
+
+msgid "SC-Seca: activate PPV"
+msgstr ""
+
+msgid "Nagra: min. ECM processing time"
+msgstr ""
+
+msgid "Nagra2: drop EMM-S packets"
+msgstr ""
+
+msgid "Nagra2: Enable AUXserver"
+msgstr ""
+
+msgid "Nagra2: AUXserver hostname"
+msgstr ""
+
+msgid "Nagra2: AUXserver port"
+msgstr ""
+
+msgid "Nagra2: AUXserver password"
+msgstr ""
+
+msgid "don't touch"
+msgstr ""
+
+msgid "disable"
+msgstr ""
+
+msgid "SC-Cryptoworks: Parental rating"
+msgstr ""
+
+msgid "Cardclient: connect immediately"
+msgstr ""
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po2i18n.pl b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/po2i18n.pl
new file mode 100644 (file)
index 0000000..cef8092
--- /dev/null
@@ -0,0 +1,156 @@
+#!/usr/bin/perl
+#
+#  po2i18n - Convert plugin po files in into i18n.c-format
+#
+#  See the README file for copyright information and how to reach the author.
+#
+
+use strict;
+use warnings;
+
+my @LANGS = (
+  "en_US",
+  "de_DE",
+  "sl_SI",
+  "it_IT",
+  "nl_NL",
+  "pt_PT",
+  "fr_FR",
+  "nn_NO",
+  "fi_FI",
+  "pl_PL",
+  "es_ES",
+  "el_GR",
+  "sv_SE",
+  "ro_RO",
+  "hu_HU",
+  "ca_ES",
+  "ru_RU",
+  "hr_HR",
+  "et_EE",
+  "da_DK",
+  "cs_CZ",
+  "tr_TR"
+  );
+
+my %VERS = (
+  "en_US" => 10200,
+  "de_DE" => 10200,
+  "sl_SI" => 10200,
+  "it_IT" => 10200,
+  "nl_NL" => 10200,
+  "pt_PT" => 10200,
+  "fr_FR" => 10200,
+  "nn_NO" => 10200,
+  "fi_FI" => 10200,
+  "pl_PL" => 10200,
+  "es_ES" => 10200,
+  "el_GR" => 10200,
+  "sv_SE" => 10200,
+  "ro_RO" => 10200,
+  "hu_HU" => 10200,
+  "ca_ES" => 10200,
+  "ru_RU" => 10302,
+  "hr_HR" => 10307,
+  "et_EE" => 10313,
+  "da_DK" => 10316,
+  "cs_CZ" => 10342,
+  "tr_TR" => 10502
+  );
+
+
+my %strings;
+
+foreach my $lang (@LANGS) { $strings{$lang} = { }; }
+
+
+sub LoadLanguage(*) {
+    my ($lang) = @_;
+    
+    if (!open FILE, "<", "po/$lang.po") {
+        return 0;   
+    }
+    
+    my $msgid = "";
+    my $msgstr = "";
+    my $last = 0; # 0=init, 1=msgid was last, 2=msgstr was last
+    
+    while (<FILE>) {
+        chomp;
+        my $line = $_;
+        
+        if ($line =~ /^msgid "(.*)"$/) {
+            if ($last eq 2) {
+                $strings{$lang}->{$msgid} = $msgstr;
+                $strings{"en_US"}->{$msgid} = $msgid;
+            }
+            $msgid = $1;
+            $last = 1;
+        } elsif ($line =~ /^msgstr "(.*)"/) {
+            $msgstr = $1;
+            $last = 2;
+        } elsif ($line =~ /^"(.*)"/) {
+            if ($last eq 1) {
+                $msgid = $msgid . $1;
+            } elsif ($last eq 2) {
+                $msgstr = $msgstr . $1;
+            }
+        }       
+    }
+    if ($last eq 2) {
+        $strings{$lang}->{$msgid} = $msgstr;
+        $strings{"en_US"}->{$msgid} = $msgid;
+    }
+    
+    close FILE;
+}
+
+
+
+foreach my $lang (@LANGS) { 
+    LoadLanguage($lang);
+}
+
+my @msgids = sort keys %{$strings{"en_US"}};
+
+
+my $silent = 0;
+
+while (<>) {
+    my $line = $_;
+
+    if ($line =~ /^\/\/ START I18N/) {
+        print "// START I18N - automatically generated by po2i18n.pl\n";
+        for my $msgid (@msgids) {
+            next if $msgid eq "";
+
+            my $head = "  { ";
+            my $endif = "";
+            my $versnum = 10200;
+            
+            for my $lang (@LANGS) {
+                if ($VERS{$lang} ne $versnum) {
+                    $versnum = $VERS{$lang};
+                    print $endif;
+                    print "#if VDRVERSNUM >= $versnum\n";
+                    $endif = "#endif\n";
+                }
+                my $msgstr = $strings{$lang}->{$msgid};
+                $msgstr = "" if !defined $msgstr;
+                
+                print "$head\"$msgstr\",\n";
+                $head = "    ";
+            }
+            print $endif;
+            print "  },\n";
+        }
+        $silent = 1;
+    }
+
+    if (!$silent) { print $line; }
+
+    if ($line =~ /^\/\/ END I18N/) { 
+        print "// END I18N - automatically generated by po2i18n.pl\n";
+        $silent = 0; 
+    }    
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.c
new file mode 100644 (file)
index 0000000..f531323
--- /dev/null
@@ -0,0 +1,1548 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <typeinfo>
+#ifndef STATICBUILD
+#include <dlfcn.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#endif
+
+#include <vdr/plugin.h>
+#include <vdr/menuitems.h>
+#include <vdr/status.h>
+#include <vdr/dvbdevice.h>
+#include <vdr/channels.h>
+#include <vdr/ci.h>
+#include <vdr/interface.h>
+#include <vdr/menu.h>
+#include <vdr/tools.h>
+#include <vdr/config.h>
+
+#include "sc.h"
+#include "scsetup.h"
+#include "filter.h"
+#include "system.h"
+#include "cam.h"
+#include "smartcard.h"
+#include "data.h"
+#include "network.h"
+#include "misc.h"
+#include "opts.h"
+#include "i18n.h"
+#include "log-core.h"
+#include "version.h"
+
+#define MIN_VERS   1 // required VDR version
+#define MIN_MAJOR  4
+#define MIN_MINOR  6
+#define MINAPIVERSNUM 10405
+
+// some sanity checks
+#ifdef HAVE_SOFTCSA
+#error softcsa/ffdecsa patch MUST NOT be applied. Next time read the README first.
+#endif
+#if APIVERSNUM >= 10500
+#ifdef VDR_IS_SC_PATCHED
+#error You MUST NOT patch the VDR core. Next time read the README first.
+#endif
+#else //APIVERSNUM >= 10500
+#if !defined(VDR_IS_SC_PATCHED)
+#error You MUST patch the VDR core with the supplied patch. Next time read the README first.
+#endif
+#if VDR_IS_SC_PATCHED<402
+#error Your VDR core is patched with an outdated patch version. Please upgrade to the supplied version.
+#endif
+#endif //APIVERSNUM >= 10500
+#if APIVERSNUM<MINAPIVERSNUM
+#error Your VDR API version is too old. See README.
+#endif
+
+// SC API version number for loading shared libraries
+#define SCAPIVERS 20
+
+static cPlugin *ScPlugin;
+static cOpts *ScOpts, *LogOpts;
+static const char * const cfgsub="sc";
+
+static const struct LogModule lm_core = {
+  (LMOD_ENABLE|L_CORE_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_CORE_LOAD|L_CORE_ECM|L_CORE_PIDS|L_CORE_AU|L_CORE_AUSTATS|L_CORE_CAIDS|L_CORE_NET|L_CORE_CI|L_CORE_SC|L_CORE_HOOK)&LOPT_MASK,
+  "core",
+  { "load","action","ecm","ecmProc","pids","au","auStats","auExtra","auExtern",
+    "caids","keys","dynamic","csa","ci","av7110","net","netData","msgcache",
+    "serial","smartcard","hook","ciFull" }
+  };
+ADD_MODULE(L_CORE,lm_core)
+
+// --- cMenuEditCapItem --------------------------------------------------------
+
+class cMenuEditCapItem : public cMenuEditIntItem {
+protected:
+  virtual void Set(void);
+public:
+  cMenuEditCapItem(const char *Name, int *Value);
+  eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuEditCapItem::cMenuEditCapItem(const char *Name, int *Value)
+:cMenuEditIntItem(Name, Value, 0)
+{
+  Set();
+}
+
+void cMenuEditCapItem::Set(void)
+{
+  if(!*value) SetValue(tr("off"));
+  else cMenuEditIntItem::Set();
+}
+
+eOSState cMenuEditCapItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditItem::ProcessKey(Key);
+  if(state==osUnknown)
+    state=cMenuEditIntItem::ProcessKey(Key);
+  return state;
+}
+
+// --- cMenuEditHexItem ------------------------------------------------------
+
+class cMenuEditHexItem : public cMenuEditItem {
+private:
+  bool abc, isOn;
+  //
+  void SetButtons(bool on);
+protected:
+  int *value;
+  int min, max;
+  //
+  virtual void Set(void);
+public:
+  cMenuEditHexItem(const char *Name, int *Value, int Min=0, int Max=INT_MAX);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuEditHexItem::cMenuEditHexItem(const char *Name, int *Value, int Min, int Max)
+:cMenuEditItem(Name)
+{
+  value=Value; min=Min; max=Max;
+  if(*value<min) *value=min;
+  else if(*value>max) *value=max;
+  Set();
+  abc=true; isOn=false;
+}
+
+void cMenuEditHexItem::SetButtons(bool on)
+{
+  if(on) {
+    if(abc) cSkinDisplay::Current()->SetButtons("A","B","C","D-F");
+    else    cSkinDisplay::Current()->SetButtons("D","E","F","A-C");
+    isOn=true;
+    }
+  else {
+    cSkinDisplay::Current()->SetButtons(0);
+    isOn=false;
+    }
+}
+
+void cMenuEditHexItem::Set(void)
+{
+   char buf[16];
+   snprintf(buf,sizeof(buf),"%X",*value);
+   SetValue(buf);
+}
+
+eOSState cMenuEditHexItem::ProcessKey(eKeys Key)
+{
+  switch(NORMALKEY(Key)) {
+    case kUp:
+    case kDown:
+      if(isOn) SetButtons(false);
+      break;
+    default:
+      if(!isOn) SetButtons(true);
+      break;
+    }
+  eOSState state=cMenuEditItem::ProcessKey(Key);
+  if(state!=osUnknown) return state;
+
+  int newValue=*value;
+  bool IsRepeat=Key & k_Repeat;
+  Key=NORMALKEY(Key);
+  switch(Key) {
+    case kBlue:
+      abc=!abc; SetButtons(true);
+      break;
+    case kRed:
+    case kGreen:
+    case kYellow:
+    case k0 ... k9:
+      {
+      if(fresh) { newValue=0; fresh=false; }
+      int add;
+      if(Key>=kRed && Key<=kYellow) add=(abc ? 10:13)+(Key-kRed);
+      else                          add=(Key-k0);
+      newValue=newValue*16+add;
+      break;
+      }
+    case kLeft:
+      newValue=*value-1; fresh=true;
+      if(!IsRepeat && newValue<min) newValue=max;
+      break;
+    case kRight:
+      newValue=*value+1; fresh=true;
+      if(!IsRepeat && newValue>max) newValue=min;
+      break;
+    default:
+      if(*value<min) { *value=min; Set(); }
+      if(*value>max) { *value=max; Set(); }
+      return osUnknown;
+    }
+  if(newValue!=*value && (!fresh || min<=newValue) && newValue<=max) {
+     *value=newValue;
+     Set();
+     }
+  return osContinue;
+}
+
+// --- cScInfoItem -------------------------------------------------------------
+
+class cScInfoItem : public cOsdItem {
+private:
+  void SetValue(const char *Name, const char *Value);
+public:
+  cScInfoItem(const char *Name, int Value, eOSState State=osUnknown);
+  cScInfoItem(const char *Name, const char *Value=0, eOSState State=osUnknown);
+  };
+
+cScInfoItem::cScInfoItem(const char *Name, int Value, eOSState State)
+:cOsdItem(State)
+{
+  char buf[16];
+  snprintf(buf,sizeof(buf),"%d",Value);
+  SetValue(Name,buf);
+  if(State==osUnknown) SetSelectable(false);
+}
+
+cScInfoItem::cScInfoItem(const char *Name, const char *Value, eOSState State)
+:cOsdItem(State)
+{
+  SetValue(Name,Value);
+  if(State==osUnknown) SetSelectable(false);
+}
+
+void cScInfoItem::SetValue(const char *Name, const char *Value)
+{
+  char *buff;
+  asprintf(&buff,Value ? "%s:\t%s":"%s",Name,Value);
+  SetText(buff,false);
+  cStatus::MsgOsdCurrentItem(buff);
+}
+
+// --- cOpt --------------------------------------------------------------------
+
+cOpt::cOpt(const char *Name, const char *Title)
+{
+  name=Name; title=Title;
+  fullname=0; persistant=true;
+}
+
+cOpt::~cOpt()
+{
+  free(fullname);
+}
+
+const char *cOpt::FullName(const char *PreStr)
+{
+  if(PreStr) {
+    free(fullname);
+    asprintf(&fullname,"%s.%s",PreStr,name);
+    return fullname;
+    }
+  else return name;
+}
+
+// --- cOptInt -----------------------------------------------------------------
+
+cOptInt::cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max)
+:cOpt(Name,Title)
+{
+  storage=Storage; min=Min; max=Max;
+}
+
+void cOptInt::Parse(const char *Value)
+{
+  *storage=atoi(Value);
+}
+
+void cOptInt::Backup(void)
+{
+  value=*storage;
+}
+
+bool cOptInt::Set(void)
+{
+  if(value!=*storage) { *storage=value; return true; }
+  return false;
+}
+
+void cOptInt::Store(const char *PreStr)
+{
+  ScPlugin->SetupStore(FullName(PreStr),*storage);
+}
+
+void cOptInt::Create(cOsdMenu *menu)
+{
+  menu->Add(new cMenuEditIntItem(tr(title),&value,min,max));
+}
+
+// --- cOptSel -----------------------------------------------------------------
+
+cOptSel::cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings)
+:cOptInt(Name,Title,Storage,0,NumStr)
+{
+  strings=Strings;
+  trStrings=0;
+}
+
+cOptSel::~cOptSel()
+{
+  free(trStrings);
+}
+
+void cOptSel::Create(cOsdMenu *menu)
+{
+  free(trStrings);
+  if((trStrings=MALLOC(const char *,max))) {
+    for(int i=0; i<max ; i++) trStrings[i]=tr(strings[i]);
+    menu->Add(new cMenuEditStraItem(tr(title),&value,max,trStrings));
+    }
+}
+
+// --- cOptBool -----------------------------------------------------------------
+
+cOptBool::cOptBool(const char *Name, const char *Title, int *Storage)
+:cOptInt(Name,Title,Storage,0,1)
+{}
+
+void cOptBool::Create(cOsdMenu *menu)
+{
+  menu->Add(new cMenuEditBoolItem(tr(title),&value));
+}
+
+// --- cOptStr -----------------------------------------------------------------
+
+cOptStr::cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed)
+:cOpt(Name,Title)
+{
+  storage=Storage; size=Size; allowed=Allowed;
+  value=MALLOC(char,size);
+}
+
+cOptStr::~cOptStr()
+{
+  free(value);
+}
+
+void cOptStr::Parse(const char *Value)
+{
+  strn0cpy(storage,Value,size);
+}
+
+void cOptStr::Backup(void)
+{
+  strn0cpy(value,storage,size);
+}
+
+bool cOptStr::Set(void)
+{
+  if(strcmp(value,storage)) { strn0cpy(storage,value,size); return true; }
+  return false;
+}
+
+void cOptStr::Store(const char *PreStr)
+{
+  ScPlugin->SetupStore(FullName(PreStr),storage);
+}
+
+void cOptStr::Create(cOsdMenu *menu)
+{
+  menu->Add(new cMenuEditStrItem(tr(title),value,size,allowed));
+}
+
+// --- cOptMInt ----------------------------------------------------------------
+
+class cOptMInt : public cOpt {
+protected:
+  int *storage, *value;
+  int size, mode, len;
+public:
+  cOptMInt(const char *Name, const char *Title, int *Storage, int Size, int Mode);
+  virtual ~cOptMInt();
+  virtual void Parse(const char *Value);
+  virtual void Backup(void);
+  virtual bool Set(void);
+  virtual void Store(const char *PreStr);
+  virtual void Create(cOsdMenu *menu);
+  };
+
+// mode: 0-Cap 1-Int 2-Hex
+
+cOptMInt::cOptMInt(const char *Name, const char *Title, int *Storage, int Size, int Mode)
+:cOpt(Name,Title)
+{
+  storage=Storage; size=Size; mode=Mode; len=sizeof(int)*size;
+  value=MALLOC(int,size);
+}
+
+cOptMInt::~cOptMInt()
+{
+  free(value);
+}
+
+void cOptMInt::Parse(const char *Value)
+{
+  memset(storage,0,len);
+  int i=0;
+  while(1) {
+    char *p;
+    const int c=strtol(Value,&p,mode>1 ? 16:10);
+    if(p==Value || i>=size) return;
+    if(c>0) storage[i++]=c;
+    Value=p;
+    }
+}
+
+void cOptMInt::Backup(void)
+{
+  memcpy(value,storage,len);
+}
+
+bool cOptMInt::Set(void)
+{
+  if(memcmp(value,storage,len)) {
+    memset(storage,0,len);
+    for(int i=0, k=0; i<size; i++) if(value[i]>0) storage[k++]=value[i];
+    return true;
+    }
+  return false;
+}
+
+void cOptMInt::Store(const char *PreStr)
+{
+  char b[256];
+  int p=0;
+  for(int i=0; i<size; i++)
+    if(storage[i] || mode==0) p+=snprintf(b+p,sizeof(b)-p,mode>1 ? "%x ":"%d ",storage[i]);
+  ScPlugin->SetupStore(FullName(PreStr),p>0?b:0);
+}
+
+void cOptMInt::Create(cOsdMenu *menu)
+{
+  for(int i=0; i<size; i++) {
+    const char *buff=tr(title);
+    switch(mode) {
+      case 0: menu->Add(new cMenuEditCapItem(buff,&value[i])); break;
+      case 1: menu->Add(new cMenuEditIntItem(buff,&value[i],0,65535)); break;
+      case 2: menu->Add(new cMenuEditHexItem(buff,&value[i],0,65535)); break;
+      }
+    if(value[i]==0) break;
+    }
+}
+
+// --- cOpts -------------------------------------------------------------------
+
+cOpts::cOpts(const char *PreStr, int NumOpts)
+{
+  preStr=PreStr;
+  numOpts=NumOpts; numAdd=0;
+  if((opts=MALLOC(cOpt *,numOpts))) memset(opts,0,sizeof(cOpt *)*numOpts);
+}
+
+cOpts::~cOpts()
+{
+  if(opts) {
+    for(int i=0; i<numOpts; i++) delete opts[i];
+    free(opts);
+    }
+}
+
+void cOpts::Add(cOpt *opt)
+{
+  if(opts && numAdd<numOpts) opts[numAdd++]=opt;
+}
+
+bool cOpts::Parse(const char *Name, const char *Value)
+{
+  if(opts) {
+    for(int i=0; i<numAdd; i++)
+      if(opts[i] && opts[i]->Persistant() && !strcasecmp(Name,opts[i]->Name())) {
+        opts[i]->Parse(Value);
+        return true;
+        }
+    }
+  return false;
+}
+
+bool cOpts::Store(bool AsIs)
+{
+  bool res=false;
+  if(opts) {
+    for(int i=0; i<numAdd; i++)
+      if(opts[i]) {
+        if(!AsIs && opts[i]->Set()) res=true;
+        if(opts[i]->Persistant()) opts[i]->Store(preStr);
+        }
+    }
+  return res;
+}
+
+void cOpts::Backup(void)
+{
+  if(opts) {
+    for(int i=0; i<numAdd; i++)
+      if(opts[i]) opts[i]->Backup();
+    }
+}
+
+void cOpts::Create(cOsdMenu *menu)
+{
+  if(opts) {
+    for(int i=0; i<numAdd; i++)
+      if(opts[i]) opts[i]->Create(menu);
+    }
+}
+
+// --- cMenuInfoSc -------------------------------------------------------------
+
+class cMenuInfoSc : public cOsdMenu {
+public:
+  cMenuInfoSc(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuInfoSc::cMenuInfoSc(void)
+:cOsdMenu(tr("SoftCAM"),25)
+{
+  Add(new cScInfoItem(tr("Current keys:")));
+  for(int d=0; d<MAXDVBDEVICES; d++) {
+    int n=0;
+    char *ks;
+    do {
+      if((ks=cSoftCAM::CurrKeyStr(d,n))) {
+        char buffer[32];
+        snprintf(buffer,sizeof(buffer),"  [%s %d:%d]","DVB",d+1,n+1);
+        Add(new cScInfoItem(buffer,ks));
+        free(ks);
+        }
+      n++;
+      } while(ks);
+    }
+  if(Feature.KeyFile()) {
+    Add(new cScInfoItem(tr("Key update status:")));
+    int fk, nk;
+    cSystem::KeyStats(fk,nk);
+    // TRANSLATORS: 2 leading spaces!
+    Add(new cScInfoItem(tr("  [Seen keys]"),fk));
+    // TRANSLATORS: 2 leading spaces!
+    Add(new cScInfoItem(tr("  [New keys]"), nk));
+    }
+  Display();
+}
+
+eOSState cMenuInfoSc::ProcessKey(eKeys Key)
+{
+  eOSState state=cOsdMenu::ProcessKey(Key);
+  if(state==osUnknown && Key==kOk) state=osBack;
+  return state;
+}
+
+// --- cMenuInfoCard -----------------------------------------------------------
+
+class cMenuInfoCard : public cMenuText {
+private:
+  int port;
+  char infoStr[4096];
+public:
+  cMenuInfoCard(int Port);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuInfoCard::cMenuInfoCard(int Port)
+:cMenuText(tr("Smartcard"),0,fontFix)
+{
+  port=Port;
+  smartcards.CardInfo(port,infoStr,sizeof(infoStr));
+  SetText(infoStr);
+  SetHelp(tr("Reset card"));
+  Display();
+}
+
+eOSState cMenuInfoCard::ProcessKey(eKeys Key)
+{
+  if(Key==kRed && Interface->Confirm(tr("Really reset card?"))) {
+    smartcards.CardReset(port);
+    return osEnd;
+    }
+  eOSState state=cMenuText::ProcessKey(Key);
+  if(state==osUnknown) state=osContinue;
+  return state;
+}
+
+// --- cLogOptItem -------------------------------------------------------------
+
+class cLogOptItem : public cMenuEditBoolItem {
+private:
+  int o;
+public:
+  cLogOptItem(const char *Name, int O, int *val);
+  int Option(void) { return o; }
+  };
+
+cLogOptItem::cLogOptItem(const char *Name, int O, int *val)
+:cMenuEditBoolItem(Name,val)
+{
+  o=O;
+}
+
+// --- cMenuLogMod -------------------------------------------------------------
+
+class cMenuLogMod : public cOsdMenu {
+private:
+  int m;
+  int v[LOPT_NUM], cfg[LOPT_NUM];
+  //
+  void Store(void);
+public:
+  cMenuLogMod(int M);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuLogMod::cMenuLogMod(int M)
+:cOsdMenu(tr("Module config"),33)
+{
+  m=M;
+  Add(new cOsdItem(tr("Reset module to default"),osUser9));
+  const char *name=cLogging::GetModuleName(LCLASS(m,0));
+  int o=cLogging::GetModuleOptions(LCLASS(m,0));
+  if(o>=0) {
+    for(int i=0; i<LOPT_NUM; i++) {
+      const char *opt;
+      if(i==0) opt="enable";
+      else opt=cLogging::GetOptionName(LCLASS(m,1<<i));
+      if(opt) {
+        char buff[64];
+        snprintf(buff,sizeof(buff),"%s.%s",name,opt);
+        cfg[i]=(o&(1<<i)) ? 1:0;
+        v[i]=1;
+        Add(new cLogOptItem(buff,i,&cfg[i]));
+        }
+      else v[i]=0;
+      }
+    }
+  Display();
+}
+
+void cMenuLogMod::Store(void)
+{
+  int o=0;
+  for(int i=0; i<LOPT_NUM; i++) if(v[i] && cfg[i]) o|=(1<<i);
+  cLogging::SetModuleOptions(LCLASS(m,o));
+  ScSetup.Store(false);
+}
+
+eOSState cMenuLogMod::ProcessKey(eKeys Key)
+{
+  eOSState state=cOsdMenu::ProcessKey(Key);
+  switch(state) {
+    case osUser9:
+      if(Interface->Confirm(tr("Really reset module to default?"))) {
+        cLogging::SetModuleDefault(LCLASS(m,0));
+        ScSetup.Store(false); state=osBack;
+        }
+      break;
+
+    case osContinue:
+      if(NORMALKEY(Key)==kLeft || NORMALKEY(Key)==kRight) {
+        cLogOptItem *item=dynamic_cast<cLogOptItem *>(Get(Current()));
+        if(item) {
+          int o=item->Option();
+          cLogging::SetModuleOption(LCLASS(m,1<<o),cfg[o]);
+          }
+        }
+      break;
+
+    case osUnknown:
+      if(Key==kOk) { Store(); state=osBack; }
+      break;
+
+    default:
+      break;
+    }
+  return state;
+}
+
+// --- cLogModItem -------------------------------------------------------------
+
+class cLogModItem : public cOsdItem {
+private:
+  int m;
+public:
+  cLogModItem(const char *Name, int M);
+  int Module(void) { return m; }
+  };
+
+cLogModItem::cLogModItem(const char *Name, int M)
+:cOsdItem(osUnknown)
+{
+  m=M;
+  char buf[64];
+  snprintf(buf,sizeof(buf),"%s '%s'...",tr("Module"),Name);
+  SetText(buf,true);
+}
+
+// --- cMenuLogSys -------------------------------------------------------------
+
+class cMenuLogSys : public cOsdMenu {
+private:
+  void Store(void);
+public:
+  cMenuLogSys(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuLogSys::cMenuLogSys(void)
+:cOsdMenu(tr("Message logging"),33)
+{
+  LogOpts->Backup(); LogOpts->Create(this);
+  Add(new cOsdItem(tr("Disable ALL modules"),osUser9));
+  Add(new cOsdItem(tr("Reset ALL modules to default"),osUser8));
+  for(int m=1; m<LMOD_MAX; m++) {
+    const char *name=cLogging::GetModuleName(LCLASS(m,0));
+    if(name)
+      Add(new cLogModItem(name,m));
+    }
+  Display();
+}
+
+void cMenuLogSys::Store(void)
+{
+  char *lf=strdup(logcfg.logFilename);
+  ScSetup.Store(false);
+  if(!lf || strcmp(lf,logcfg.logFilename))
+    cLogging::ReopenLogfile();
+  free(lf);
+}
+
+eOSState cMenuLogSys::ProcessKey(eKeys Key)
+{
+  eOSState state=cOsdMenu::ProcessKey(Key);
+  switch(state) {
+    case osUser9:
+      if(Interface->Confirm(tr("Really disable ALL modules?"))) {
+        for(int m=1; m<LMOD_MAX; m++)
+          cLogging::SetModuleOption(LCLASS(m,LMOD_ENABLE),false);
+        Store(); state=osBack;
+        }
+      break;
+
+    case osUser8:
+      if(Interface->Confirm(tr("Really reset ALL modules to default?"))) {
+        for(int m=1; m<LMOD_MAX; m++)
+          cLogging::SetModuleDefault(LCLASS(m,0));
+        Store(); state=osBack;
+        }
+      break;
+
+    case osUnknown:
+      if(Key==kOk) {
+        cLogModItem *item=dynamic_cast<cLogModItem *>(Get(Current()));
+        if(item) state=AddSubMenu(new cMenuLogMod(item->Module()));
+        else { Store(); state=osBack; }
+        }
+      break;
+
+    default:
+      break;
+    }
+  return state;
+}
+
+// --- cMenuSysOpts -------------------------------------------------------------
+
+class cMenuSysOpts : public cOsdMenu {
+public:
+  cMenuSysOpts(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuSysOpts::cMenuSysOpts(void)
+:cOsdMenu(tr("Cryptsystem options"),33)
+{
+  for(cOpts *opts=0; (opts=cSystems::GetSystemOpts(opts==0));) {
+    opts->Backup();
+    opts->Create(this);
+    }
+  Display();
+}
+
+eOSState cMenuSysOpts::ProcessKey(eKeys Key)
+{
+  eOSState state=cOsdMenu::ProcessKey(Key);
+  switch(state) {
+    case osContinue:
+      if(NORMALKEY(Key)==kUp || NORMALKEY(Key)==kDown) {
+        cOsdItem *item=Get(Current());
+        if(item) item->ProcessKey(kNone);
+        }
+      break;
+
+    case osUnknown:
+      if(Key==kOk) { ScSetup.Store(false); state=osBack; }
+      break;
+
+    default:
+      break;
+    }
+  return state;
+}
+
+// --- cMenuSetupSc ------------------------------------------------------------
+
+class cMenuSetupSc : public cMenuSetupPage {
+private:
+  char *cfgdir;
+protected:
+  virtual void Store(void);
+public:
+  cMenuSetupSc(const char *CfgDir);
+  virtual ~cMenuSetupSc();
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+static eOSState portStates[] = { osUser1,osUser2,osUser3,osUser4 };
+#if MAX_PORTS!=4
+#error Update portStates[]
+#endif
+
+cMenuSetupSc::cMenuSetupSc(const char *CfgDir)
+{
+  cfgdir=strdup(CfgDir);
+  SetSection(tr("SoftCAM"));
+
+  ScOpts->Backup(); LogOpts->Backup();
+  for(cOpts *opts=0; (opts=cSystems::GetSystemOpts(opts==0));) opts->Backup();
+
+  ScOpts->Create(this);
+  Add(new cOsdItem(tr("Cryptsystem options..."),osUser5));
+  Add(new cOsdItem(tr("Message logging..."),osUser6));
+  if(Feature.SmartCard()) {
+    char id[IDSTR_LEN];
+    for(int i=0; smartcards.ListCard(i,id,sizeof(id)); i++) {
+      char buff[32];
+      snprintf(buff,sizeof(buff),"%s %d",tr("Smartcard interface"),i);
+      if(id[0]) Add(new cScInfoItem(buff,id,portStates[i]));
+      else      Add(new cScInfoItem(buff,tr("(empty)")));
+      }
+    }
+  Add(new cOsdItem(tr("Status information..."),osUser8));
+  Add(new cOsdItem(tr("Flush ECM cache"),osUser7));
+  Add(new cOsdItem(tr("Reload files"),osUser9));
+}
+
+cMenuSetupSc::~cMenuSetupSc()
+{
+  free(cfgdir);
+}
+
+void cMenuSetupSc::Store(void)
+{
+  ScSetup.Store(false);
+}
+
+eOSState cMenuSetupSc::ProcessKey(eKeys Key)
+{
+  eOSState state = cOsdMenu::ProcessKey(Key);
+  switch(state) {
+    case osUser1...osUser4:
+      if(Feature.SmartCard()) {
+        for(unsigned int i=0; i<sizeof(portStates)/sizeof(eOSState); i++)
+          if(portStates[i]==state) return(AddSubMenu(new cMenuInfoCard(i)));
+        }
+      state=osContinue;
+      break;
+
+    case osUser7:
+      state=osContinue;
+      if(Interface->Confirm(tr("Really flush ECM cache?"))) {
+        ecmcache.Flush();
+        state=osEnd;
+        }
+      break;
+
+    case osUser8:
+      return AddSubMenu(new cMenuInfoSc);
+
+    case osUser6:
+      return AddSubMenu(new cMenuLogSys);
+
+    case osUser5:
+      return AddSubMenu(new cMenuSysOpts);
+
+    case osUser9:
+      state=osContinue;
+      if(!cSoftCAM::Active(true)) {
+        if(Interface->Confirm(tr("Really reload files?"))) {
+          Store();
+          cSoftCAM::Load(cfgdir);
+          state=osEnd;
+          }
+        }
+      else 
+        Skins.Message(mtError,tr("Active! Can't reload files now"));
+      break;
+
+    case osContinue:
+      if(NORMALKEY(Key)==kUp || NORMALKEY(Key)==kDown) {
+        cOsdItem *item=Get(Current());
+        if(item) item->ProcessKey(kNone);
+        }
+      break;
+
+    case osUnknown:
+      if(Key==kOk) { Store(); state=osBack; }
+      break;
+
+    default:
+      break;
+    }
+  return state;
+}
+
+// --- cScSetup ---------------------------------------------------------------
+
+cScSetup ScSetup;
+
+cScSetup::cScSetup(void)
+{
+  AutoUpdate=1;
+  memset(ScCaps,0,sizeof(ScCaps));
+  ScCaps[0]=1;
+  ScCaps[1]=2;
+  ConcurrentFF=0;
+  memset(CaIgnore,0,sizeof(CaIgnore));
+  LocalPriority=0;
+  ForceTransfer=1;
+  PrestartAU=0;
+  SuperKeys=0;
+}
+
+void cScSetup::Check(void)
+{
+  if(AutoUpdate==0)
+    PRINTF(L_GEN_WARN,"Keys updates (AU) are disabled.");
+  for(int i=0; i<MAXSCCAPS; i++)
+    if(ScCaps[i]>=16) {
+      PRINTF(L_GEN_WARN,"ScCaps contains unusual value. Check your config! (You can ignore this message if you have more than 16 dvb cards in your system ;)");
+      break;
+      }
+
+  PRINTF(L_CORE_LOAD,"** Plugin config:");
+  PRINTF(L_CORE_LOAD,"** Key updates (AU) are %s (%sprestart)",AutoUpdate?(AutoUpdate==1?"enabled (active CAIDs)":"enabled (all CAIDs)"):"DISABLED",PrestartAU?"":"no ");
+  PRINTF(L_CORE_LOAD,"** Local systems %stake priority over cached remote",LocalPriority?"":"DON'T ");
+  PRINTF(L_CORE_LOAD,"** Concurrent FF recordings are %sallowed",ConcurrentFF?"":"NOT ");
+  PRINTF(L_CORE_LOAD,"** %sorce transfermode with digital audio",ForceTransfer?"F":"DON'T f");
+  LBSTART(L_CORE_LOAD);
+  LBPUT("** ScCaps are"); for(int i=0; i<MAXSCCAPS ; i++) LBPUT(" %d",ScCaps[i]);
+  LBFLUSH();
+  LBPUT("** Ignored CAIDs"); for(int i=0; i<MAXCAIGN ; i++) LBPUT(" %04X",CaIgnore[i]);
+  LBEND();
+}
+
+void cScSetup::Store(bool AsIs)
+{
+  if(ScOpts) ScOpts->Store(AsIs);
+  cSystems::ConfigStore(AsIs);
+  if(LogOpts) LogOpts->Store(AsIs);
+  cLineBuff lb(128);
+  if(cLogging::GetConfig(&lb))
+    ScPlugin->SetupStore("LogConfig",lb.Line());
+}
+
+bool cScSetup::CapCheck(int n)
+{
+  for(int j=0; j<MAXSCCAPS; j++)
+    if(ScCaps[j] && ScCaps[j]==n+1) return true;
+  return false;
+}
+
+bool cScSetup::Ignore(unsigned short caid)
+{
+  for(int i=0; i<MAXCAIGN; i++)
+    if(CaIgnore[i]==caid) return true;
+  return false;
+}
+
+// --- cSoftCAM ---------------------------------------------------------------
+
+bool cSoftCAM::Load(const char *cfgdir)
+{
+  if(!Feature.KeyFile()) keys.Disable();
+  if(!Feature.SmartCard()) smartcards.Disable();
+  cStructLoaders::Load(false);
+  if(Feature.KeyFile() && keys.Count()<1)
+    PRINTF(L_GEN_ERROR,"no keys loaded for softcam!");
+  if(!cSystems::Init(cfgdir)) return false;
+  return true;
+}
+
+void cSoftCAM::Shutdown(void)
+{
+  cStructLoaders::Save(true);
+  cSystems::Clean();
+  smartcards.Shutdown();
+  keys.Clear();
+}
+
+char *cSoftCAM::CurrKeyStr(int CardNum, int num)
+{
+  cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+  char *str=0;
+  if(dev) {
+    if(dev->Cam()) str=dev->Cam()->CurrentKeyStr(num);
+    if(!str && num==0 && ScSetup.CapCheck(CardNum)) str=strdup(tr("(none)"));
+    }
+  return str;
+}
+
+bool cSoftCAM::Active(bool log)
+{
+  for(int n=cDevice::NumDevices(); --n>=0;) {
+    cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+    if(dev && dev->Cam() && dev->Cam()->Active(log)) return true;
+    }
+  return false;
+}
+
+void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on)
+{
+  cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+  if(dev && dev->Cam()) dev->Cam()->LogEcmStatus(ecm,on);
+}
+
+void cSoftCAM::AddHook(int CardNum, cLogHook *hook)
+{
+  cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+  if(dev && dev->Cam()) dev->Cam()->AddHook(hook);
+}
+
+bool cSoftCAM::TriggerHook(int CardNum, int id)
+{
+  cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+  return dev && dev->Cam() && dev->Cam()->TriggerHook(id);
+}
+
+// --- cScHousekeeper ----------------------------------------------------------
+
+class cScHousekeeper : public cThread {
+protected:
+  virtual void Action(void);
+public:
+  cScHousekeeper(void);
+  ~cScHousekeeper();
+  };
+
+cScHousekeeper::cScHousekeeper(void)
+:cThread("SC housekeeper")
+{
+  Start();
+}
+
+cScHousekeeper::~cScHousekeeper()
+{
+  Cancel(3);
+}
+
+void cScHousekeeper::Action(void)
+{
+  int c=0;
+  while(Running()) {
+    if(++c==20) {
+      c=0;
+      for(int n=cDevice::NumDevices(); --n>=0;) {
+        cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+        if(dev && dev->Cam()) dev->Cam()->HouseKeeping();
+        }
+      }
+
+    if(Feature.KeyFile()) keys.HouseKeeping();
+    if(!cSoftCAM::Active(false)) cStructLoaders::Purge();
+    cStructLoaders::Load(true);
+    cStructLoaders::Save();
+
+    cCondWait::SleepMs(987);
+    }
+}
+
+#ifndef STATICBUILD
+
+// --- cScDll ------------------------------------------------------------------
+
+class cScDll : public cSimpleItem {
+private:
+  char *fileName;
+  void *handle;
+public:
+  cScDll(const char *FileName);
+  ~cScDll();
+  bool Load(void);
+  };
+
+cScDll::cScDll(const char *FileName)
+{
+  fileName=strdup(FileName);
+  handle=0;
+}
+
+cScDll::~cScDll()
+{
+  if(handle) dlclose(handle);
+  free(fileName);
+}
+
+bool cScDll::Load(void)
+{
+  char *base=rindex(fileName,'/');
+  if(!base) base=fileName;
+  PRINTF(L_CORE_DYN,"loading library: %s",base);
+  if(!handle) {
+    handle=dlopen(fileName,RTLD_NOW|RTLD_LOCAL);
+    if(handle) return true;
+    PRINTF(L_GEN_ERROR,"dload: %s: %s",base,dlerror());
+    }
+  return false;
+}
+
+// --- cScDlls -----------------------------------------------------------------
+
+#define LIBSC_PREFIX  "libsc-"
+#define SO_INDICATOR   ".so."
+
+class cScDlls : public cSimpleList<cScDll> {
+private:
+  void *handle;
+public:
+  cScDlls(void);
+  ~cScDlls();
+  bool Load(void);
+  };
+
+cScDlls::cScDlls(void)
+{
+  handle=0;
+}
+
+cScDlls::~cScDlls()
+{
+  Clear();
+  if(handle) dlclose(handle);
+  PRINTF(L_CORE_DYN,"unload done");
+}
+
+bool cScDlls::Load(void)
+{
+  Dl_info info;
+  static int marker=0;
+  if(!dladdr((void *)&marker,&info)) {
+    PRINTF(L_GEN_ERROR,"dladdr: %s",dlerror());
+    return false;
+    }
+
+  // we have to re-dlopen our selfs as VDR doesn't use RTLD_GLOBAL
+  // but our symbols have to be available to the sub libs.
+  handle=dlopen(info.dli_fname,RTLD_NOW|RTLD_GLOBAL);
+  if(!handle) {
+    PRINTF(L_GEN_ERROR,"dlopen myself: %s",dlerror());
+    return false;
+    }
+
+  char *path=strdup(info.dli_fname);
+  char *p;
+  if((p=rindex(path,'/'))) *p=0;
+  PRINTF(L_CORE_DYN,"library path %sn",path);
+
+  char pat[32];
+  snprintf(pat,sizeof(pat),"%s*-%d%s%s",LIBSC_PREFIX,SCAPIVERS,SO_INDICATOR,APIVERSION);
+  bool res=true;
+  cReadDir dir(path);
+  struct dirent *e;
+  while((e=dir.Next())) {
+    if(!fnmatch(pat,e->d_name,FNM_PATHNAME|FNM_NOESCAPE)) {
+      cScDll *dll=new cScDll(AddDirectory(path,e->d_name));
+      if(dll) {
+        if(!dll->Load()) res=false;
+        Ins(dll);
+        }
+      }
+    }
+  free(path);
+  return res;
+}
+
+#endif
+
+// --- cScPlugin ---------------------------------------------------------------
+
+class cScPlugin : public cPlugin {
+private:
+#ifndef STATICBUILD
+  cScDlls dlls;
+#endif
+  cScHousekeeper *keeper;
+public:
+  cScPlugin(void);
+  virtual ~cScPlugin();
+  virtual const char *Version(void);
+  virtual const char *Description(void);
+  virtual const char *CommandLineHelp(void);
+  virtual bool ProcessArgs(int argc, char *argv[]);
+  virtual bool Initialize(void);
+  virtual bool Start(void);
+  virtual void Stop(void);
+#ifndef SASC
+  virtual void MainThreadHook(void);
+#endif
+  virtual cMenuSetupPage *SetupMenu(void);
+  virtual bool SetupParse(const char *Name, const char *Value);
+  virtual const char **SVDRPHelpPages(void);
+  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+  };
+
+cScPlugin::cScPlugin(void)
+{
+  static const char *logg[] = { trNOOP("off"),trNOOP("active CAIDs"),trNOOP("all CAIDs") };
+  static const char *skey[] = { trNOOP("comment out"),trNOOP("remove") };
+  ScOpts=new cOpts(0,8);
+  ScOpts->Add(new cOptSel  ("AutoUpdate"   ,trNOOP("Update keys (AU)")     ,&ScSetup.AutoUpdate,3,logg));
+  ScOpts->Add(new cOptBool ("PrestartAU"   ,trNOOP("Start AU on EPG scan") ,&ScSetup.PrestartAU));
+  ScOpts->Add(new cOptSel  ("SuperKeys"    ,trNOOP("Superseded keys")      ,&ScSetup.SuperKeys,2,skey));
+  ScOpts->Add(new cOptBool ("ConcurrentFF" ,trNOOP("Concurrent FF streams"),&ScSetup.ConcurrentFF));
+  ScOpts->Add(new cOptBool ("ForceTranfer" ,trNOOP("Force TransferMode")   ,&ScSetup.ForceTransfer));
+  ScOpts->Add(new cOptBool ("LocalPriority",trNOOP("Prefer local systems") ,&ScSetup.LocalPriority));
+  ScOpts->Add(new cOptMInt ("ScCaps"       ,trNOOP("Active on DVB card")   , ScSetup.ScCaps,MAXSCCAPS,0));
+  ScOpts->Add(new cOptMInt ("CaIgnore"     ,trNOOP("Ignore CAID")          , ScSetup.CaIgnore,MAXCAIGN,2));
+  LogOpts=new cOpts(0,6);
+  LogOpts->Add(new cOptBool ("LogConsole"  ,trNOOP("Log to console")      ,&logcfg.logCon));
+  LogOpts->Add(new cOptBool ("LogFile"     ,trNOOP("Log to file")         ,&logcfg.logFile));
+  LogOpts->Add(new cOptStr  ("LogFileName" ,trNOOP("Filename")            ,logcfg.logFilename,sizeof(logcfg.logFilename),FileNameChars));
+  LogOpts->Add(new cOptInt  ("LogFileLimit",trNOOP("Filesize limit (KB)") ,&logcfg.maxFilesize,0,2000000));
+  LogOpts->Add(new cOptBool ("LogSyslog"   ,trNOOP("Log to syslog")       ,&logcfg.logSys));
+  LogOpts->Add(new cOptBool ("LogUserMsg"  ,trNOOP("Show user messages")  ,&logcfg.logUser));
+#ifndef STATICBUILD
+  dlls.Load();
+#endif
+  cScDvbDevice::Capture();
+  keeper=0;
+}
+
+cScPlugin::~cScPlugin()
+{
+  delete keeper;
+  delete ScOpts;
+  delete LogOpts;
+}
+
+bool cScPlugin::Initialize(void)
+{
+  PRINTF(L_GEN_INFO,"SC version %s initializing",ScVersion);
+  return cScDvbDevice::Initialize();
+}
+
+bool cScPlugin::Start(void)
+{
+  PRINTF(L_GEN_INFO,"SC version %s starting",ScVersion);
+  if(APIVERSNUM<MINAPIVERSNUM) {
+    PRINTF(L_GEN_ERROR,"SC plugin needs at least VDR API version %d.%d.%d",MIN_VERS,MIN_MAJOR,MIN_MINOR);
+    return false;
+    }
+  if(sizeof(int)!=4) {
+    PRINTF(L_GEN_ERROR,"compiled with 'int' as %d bit. Only supporting 32 bit.",(int)sizeof(int)*8);
+    return false;
+    }
+  if(sizeof(long long)!=8) {
+    PRINTF(L_GEN_ERROR,"compiled with 'long long' as %d bit. Only supporting 64 bit.",(int)sizeof(long long)*8);
+    return false;
+    }
+    
+  ScPlugin=this;
+#if APIVERSNUM < 10507
+  RegisterI18n(ScPhrases);
+#endif
+  const char *cfgdir=ConfigDirectory(cfgsub);
+  filemaps.SetCfgDir(cfgdir);
+  cStructLoaders::SetCfgDir(cfgdir);
+  ScSetup.Check();
+  if(!cSoftCAM::Load(cfgdir)) return false;
+  if(Feature.SmartCard()) {
+#ifdef DEFAULT_PORT
+    smartcards.AddPort(DEFAULT_PORT);
+#endif
+    smartcards.LaunchWatcher();
+    }
+  cScDvbDevice::Startup();
+  keeper=new cScHousekeeper;
+  return true;
+}
+
+void cScPlugin::Stop(void)
+{
+  delete keeper; keeper=0;
+  cScDvbDevice::Shutdown();
+  LogStatsDown();
+  cSoftCAM::Shutdown();
+#if APIVERSNUM < 10507
+  RegisterI18n(NULL);
+#endif
+  PRINTF(L_GEN_DEBUG,"SC cleanup done");
+}
+
+const char *cScPlugin::Version(void)
+{
+  return ScVersion;
+}
+
+const char *cScPlugin::Description(void)
+{
+  return tr("A software emulated CAM");
+}
+
+const char *cScPlugin::CommandLineHelp(void)
+{
+  static char *help_str=0;
+  
+  free(help_str);    //                                     for easier orientation, this is column 80|
+  asprintf(&help_str,"  -B N      --budget=N     forces DVB device N to budget mode (using FFdecsa)\n"
+                     "  -I        --inverse-cd   use inverse CD detection for the next serial device\n"
+                     "  -R        --inverse-rst  use inverse RESET for the next serial device\n"
+                     "  -C FREQ   --clock=FREQ   use FREQ as clock for the card reader on the next\n"
+                     "                           serial device (rather than 3.5712 MHz\n"
+                     "  -m MODE   --mode=MODE    cardreader mode for the next serial device:\n"
+                     "                           phoenix|smartreader\n"
+                     "                           (default: phoenix)\n"
+                     "  -s DEV    --serial=DEV   activate Phoenix ISO interface on serial device DEV\n"
+                     "                           (default: %s)\n"
+                     "  -d CMD    --dialup=CMD   call CMD to start/stop dialup-network\n"
+                     "                           (default: %s)\n"
+                     "  -t SECS   --timeout=SECS shutdown timeout for dialup-network\n"
+                     "                           (default: %d secs)\n",
+                     "none","none",netTimeout/1000
+                     );
+  return help_str;
+}
+
+bool cScPlugin::ProcessArgs(int argc, char *argv[])
+{
+  static struct option long_options[] = {
+      { "serial",      required_argument, NULL, 's' },
+      { "mode",        required_argument, NULL, 'm' },
+      { "inverse-cd",  no_argument,       NULL, 'I' },
+      { "inverse-rst", no_argument,       NULL, 'R' },
+      { "clock",       required_argument, NULL, 'C' },
+      { "dialup",      required_argument, NULL, 'd' },
+      { "external-au", required_argument, NULL, 'E' },
+      { "budget",      required_argument, NULL, 'B' },
+      { NULL }
+    };
+
+  int c, option_index=0;
+  bool invCD=false, invRST=false;
+  int clock=0;
+  char* mode=NULL;
+  while((c=getopt_long(argc,argv,"d:m:s:t:B:C:E:IR",long_options,&option_index))!=-1) {
+    switch (c) {
+      case 'I': invCD=true; break;
+      case 'R': invRST=true; break;
+      case 'C': clock=atoi(optarg); break;
+      case 'm': free(mode); mode=strdup(optarg); break;
+      case 's': smartcards.AddPort(optarg,mode,invCD,invRST,clock); free(mode); mode=NULL; invCD=false; invRST=false; clock=0; break;
+      case 'd': netscript=optarg; break;
+      case 't': netTimeout=atoi(optarg)*1000; break;
+      case 'E': externalAU=optarg; break;
+      case 'B': cScDvbDevice::SetForceBudget(atoi(optarg)); break;
+      default:  free(mode); return false;
+      }
+    }
+  free(mode);
+  return true;
+}
+
+cMenuSetupPage *cScPlugin::SetupMenu(void)
+{
+  return new cMenuSetupSc(ConfigDirectory(cfgsub));
+}
+
+bool cScPlugin::SetupParse(const char *Name, const char *Value)
+{
+  if((ScOpts && ScOpts->Parse(Name,Value)) ||
+     (LogOpts && LogOpts->Parse(Name,Value)) ||
+     cSystems::ConfigParse(Name,Value)) ;
+  else if(!strcasecmp(Name,"LogConfig")) cLogging::ParseConfig(Value);
+  else return false;
+  return true;
+}
+
+#ifndef SASC
+
+void cScPlugin::MainThreadHook(void)
+{
+  int n=0;
+  cUserMsg *um;
+  while(++n<=10 && (um=ums.GetQueuedMsg())) {
+    Skins.QueueMessage(mtInfo,um->Message());
+    delete um;
+    }
+}
+
+#endif //SASC
+
+const char **cScPlugin::SVDRPHelpPages(void)
+{
+  static const char *HelpPages[] = {
+    "RELOAD\n"
+    "    Reload all configuration files.",
+    "KEY <string>\n",
+    "    Add key to the key database (as if it was received from EMM stream).",
+    "LOG <on|off> <class>[,<class>...]\n"
+    "    Turn the given message class(es) on or off.",
+    "LOGCFG\n"
+    "    Display available message classes and their status.",
+    "LOGFILE <on|off> [<filename>]\n"
+    "    Enables/disables logging to file and optionaly sets the filename.",
+    NULL
+    };
+  return HelpPages;
+}
+
+cString cScPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+  if(!strcasecmp(Command,"RELOAD")) {
+    if(cSoftCAM::Active(true)) {
+      ReplyCode=550;
+      return "Softcam active. Can't reload files now";
+      }
+    else {
+      if(cSoftCAM::Load(ConfigDirectory(cfgsub)))
+        return "Files reloaded successfully";
+      else {
+        ReplyCode=901;
+        return "Reloading files not entirely successfull";
+        }
+      }
+    }
+  else if(!strcasecmp(Command,"KEY")) {
+    if(Option && *Option) {
+      if(keys.NewKeyParse(skipspace(Option),"from SVDR"))
+        return "Key update successfull";
+      else {
+        ReplyCode=901;
+        return "Key already known or invalid key format";
+        }
+      }
+    else { ReplyCode=501; return "Missing args"; }
+    }
+  else if(!strcasecmp(Command,"LOG")) {
+    if(Option && *Option) {
+      char tmp[1024];
+      strn0cpy(tmp,Option,sizeof(tmp));
+      char *opt=tmp;
+      opt=skipspace(opt);
+      bool mode;
+      if(!strncasecmp(opt,"ON ",3)) { mode=true; opt+=3; }
+      else if(!strncasecmp(opt,"OFF ",4)) { mode=false; opt+=4; }
+      else { ReplyCode=501; return "Bad mode, valid: on off"; }
+      do {
+        char *s=index(opt,',');
+        if(s) *s++=0;
+        int c=cLogging::GetClassByName(opt);
+        if(c>=0) cLogging::SetModuleOption(c,mode);
+        else { ReplyCode=501; return "Unknown message class"; }
+        opt=s;
+        } while(opt);
+      ScSetup.Store(true);
+      Setup.Save();
+      return "Done";
+      }
+    else { ReplyCode=501; return "Missing args"; }
+    }
+  else if(!strcasecmp(Command,"LOGCFG")) {
+    cLineBuff lb(256);
+    for(int m=1; m<LMOD_MAX; m++) {
+      const char *name=cLogging::GetModuleName(LCLASS(m,0));
+      if(name) {
+        int o=cLogging::GetModuleOptions(LCLASS(m,0));
+        if(o>=0) {
+          for(int i=0; i<LOPT_NUM; i++) {
+            const char *opt;
+            if(i==0) opt="enable";
+            else opt=cLogging::GetOptionName(LCLASS(m,1<<i));
+            if(opt)
+              lb.Printf("%s.%s %s\n",name,opt,(o&(1<<i))?"on":"off");
+            }
+          }
+        }
+      }
+    if(lb.Length()>0) return lb.Line();
+    ReplyCode=901; return "No config available";
+    }
+  else if(!strcasecmp(Command,"LOGFILE")){
+    if(Option && *Option) {
+      char tmp[1024];
+      strn0cpy(tmp,Option,sizeof(tmp));
+      char *opt=tmp;
+      opt=skipspace(opt);
+      bool mode;
+      if(!strncasecmp(opt,"ON",2)) { mode=true; opt+=2; }
+      else if(!strncasecmp(opt,"OFF",3)) { mode=false; opt+=3; }
+      else { ReplyCode=501; return "Bad mode, valid: on off"; }
+      cLineBuff lb(256);
+      if(mode) {
+        logcfg.logFile=true;
+        if(*opt==' ' || *opt=='\t') {
+          opt=stripspace(skipspace(opt));
+          strn0cpy(logcfg.logFilename,opt,sizeof(logcfg.logFilename));
+          }
+        lb.Printf("logging to file enabled, file %s",logcfg.logFilename);
+        }
+      else {
+        logcfg.logFile=false;
+        lb.Printf("logging to file disabled");
+        }
+      ScSetup.Store(true);
+      Setup.Save();
+      return lb.Line();
+      }
+    else { ReplyCode=501; return "Missing args"; }
+    }
+  return NULL;
+}
+
+VDRPLUGINCREATOR(cScPlugin); // Don't touch this!
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/sc.h
new file mode 100644 (file)
index 0000000..cb8f4fe
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SC_H
+#define ___SC_H
+
+class cEcmInfo;
+class cLogHook;
+
+// ----------------------------------------------------------------
+
+class cSoftCAM {
+public:
+  static bool Load(const char *cfgdir);
+  static void Shutdown(void);
+  //
+  static bool Active(bool log);
+  static char *CurrKeyStr(int CardNum, int num);
+  static void SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on);
+  static void AddHook(int CardNum, cLogHook *hook);
+  static bool TriggerHook(int CardNum, int id);
+  };
+
+#endif // ___SC_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/scsetup.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/scsetup.h
new file mode 100644 (file)
index 0000000..8d7c5c6
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SC_SETUP_H
+#define ___SC_SETUP_H
+
+#define MAXSCCAPS 10
+#define MAXCAIGN  16
+
+// ----------------------------------------------------------------
+
+class cScSetup {
+public:
+  int AutoUpdate;
+  int ScCaps[MAXSCCAPS];
+  int ConcurrentFF;
+  int CaIgnore[MAXCAIGN];
+  int LocalPriority;
+  int ForceTransfer;
+  int PrestartAU;
+  int SuperKeys;
+public:
+  cScSetup(void);
+  void Check(void);
+  void Store(bool AsIs);
+  bool CapCheck(int n);
+  bool Ignore(unsigned short caid);
+  };
+
+extern cScSetup ScSetup;
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.c
new file mode 100644 (file)
index 0000000..2fd2b2e
--- /dev/null
@@ -0,0 +1,1568 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <termios.h>
+#include <linux/serial.h>
+#include <ctype.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "smartcard.h"
+#include "misc.h"
+#include "log-core.h"
+
+#define DATAFILE "smartcard.conf"
+#define ISO_FREQ 3571200 // Hz
+
+//#define SER_EMU   // use serial emulation (select one of the following)
+//#define EMU_SECA  // fake Seca card
+//#define EMU_IRD   // fake Irdeto/Beta card
+//#define EMU_IRD_384 // fake ACS 384, if undefined ACS 383
+//#define EMU_CRYPTO // fake Cryptoworks card
+//#define NO_PTS_PROTO // disable PTS protocol (baudrate changes)
+
+struct BaudRates {
+  int real;
+  speed_t apival;
+  };
+
+static const struct BaudRates BaudRateTab[] = {
+  {   9600, B9600   },
+  {  19200, B19200  },
+  {  38400, B38400  },
+  {  57600, B57600  },
+  { 115200, B115200 },
+  { 230400, B230400 }
+  };
+
+// -- cSerial ------------------------------------------------------------------
+
+class cSerial {
+private:
+  char *devName;
+  int devType;
+  int fd;
+  int currMode, statInv;
+  bool invRST;
+  //
+  speed_t FindBaud(int baud);
+#ifdef SER_EMU
+  unsigned char devBuff[1024], nextSW[SB_LEN];
+  int buffCount, dataLen, record;
+  bool cmdStart, rts, dsr, flag;
+  int remTime;
+#endif
+public:
+  cSerial(const char *DevName, int DevType, bool invCD, bool InvRST);
+  ~cSerial();
+  bool Open(void);
+  bool SetMode(int mode, int baud=9600);
+  int CurrentMode(void) const { return currMode; }
+  void Flush(void);
+  int Read(unsigned char *mem, int len, int timeout, int initialTimeout=0);
+  int Write(const unsigned char *mem, int len, int delay=0);
+  void ToggleRTS(void);
+  bool CheckCAR(void);
+  void Close(void);
+  const char *DeviceName(void) const { return devName; }
+  int DeviceType(void) const { return devType; }
+  bool ConfigSmartReader(int F, int D, int fs, int N, int T, int inv);
+  };
+
+cSerial::cSerial(const char *DevName, int DevType, bool invCD, bool InvRST)
+{
+  devName=strdup(DevName);
+  devType=DevType;
+  statInv=invCD ? TIOCM_CAR:0;
+  invRST=InvRST;
+  fd=-1; currMode=SM_NONE;
+}
+
+cSerial::~cSerial()
+{
+  Close();
+  free(devName);
+}
+
+#ifndef SER_EMU
+
+bool cSerial::Open(void)
+{
+  PRINTF(L_CORE_SERIAL,"%s: open serial port",devName);
+  fd=open(devName,O_RDWR|O_NONBLOCK|O_NOCTTY);
+  if(fd>=0) {
+    PRINTF(L_CORE_SERIAL,"%s: set DTR/RTS",devName);
+    unsigned int modembits;
+    modembits=TIOCM_DTR; CHECK(ioctl(fd, TIOCMBIS, &modembits));
+    modembits=TIOCM_RTS; CHECK(ioctl(fd, invRST?TIOCMBIS:TIOCMBIC, &modembits));
+    PRINTF(L_CORE_SERIAL,"%s: init done",devName);
+    return true;
+    }
+  else PRINTF(L_GEN_ERROR,"%s: open failed: %s",devName,strerror(errno));
+  return false;
+}
+
+void cSerial::Close(void)
+{
+  if(fd>=0) {
+    PRINTF(L_CORE_SERIAL,"%s: shutting down",devName);
+    Flush();
+    unsigned int modembits=0;
+    CHECK(ioctl(fd,TIOCMSET,&modembits));
+    close(fd); fd=-1;
+    PRINTF(L_CORE_SERIAL,"%s: shutdown done",devName);
+    }
+}
+
+bool cSerial::ConfigSmartReader(int F, int D, int fs, int N, int T, int inv)
+{
+  // Convert fs to kHz.
+  fs/=1000;
+
+  PRINTF(L_CORE_SERIAL,
+        "%s: set SmartReader+ options: F=%d, D=%d, fs=%d, N=%d, T=%d, inv=%d",
+        devName,F,D,fs,N,T,inv);
+
+  // Set SmartReader+ in CMD mode.
+  struct termios term;
+  if(tcgetattr(fd,&term)==-1) {
+    PRINTF(L_GEN_ERROR,"%s: tcgetattr failed: %s",devName,strerror(errno));
+    return false;
+  }
+  term.c_cflag&=~CSIZE;
+  term.c_cflag|=CS5;
+  if(tcsetattr(fd,TCSADRAIN,&term)==-1) {
+    PRINTF(L_GEN_ERROR,"%s: tcsetattr failed: %s",devName,strerror(errno));
+    return false;
+  }
+
+  // Write SmartReader+ configuration commands.
+  // Each command must be written with a separate write.
+  unsigned char cmd[] =
+    {
+      4,1,F>>8,F&0xff,D,
+      3,2,fs>>8,fs&0xff,
+      2,3,N,
+      2,4,T,
+      2,5,inv
+    };
+
+  unsigned int i=0;
+  while(i<sizeof(cmd)) {
+    int len=cmd[i++];
+    if(Write(cmd+i,len)!=len) {
+      return false;
+    }
+    i+=len;
+  }
+
+  // Send zero bits for 0.25 - 0.5 seconds.
+  if(tcsendbreak(fd,0)==-1) {
+    PRINTF(L_GEN_ERROR,"%s: tcsendbreak failed: %s",devName,strerror(errno));
+    return false;
+  }
+
+  // Set SmartReader+ in DATA mode.
+  term.c_cflag&=~CSIZE;
+  term.c_cflag|=CS8;
+  if(tcsetattr(fd,TCSADRAIN,&term)==-1) {
+    PRINTF(L_GEN_ERROR,"%s: tcsetattr failed: %s",devName,strerror(errno));
+    return false;
+  }
+
+  return true;
+}
+
+speed_t cSerial::FindBaud(int baud)
+{
+  for(int i=0; i<(int)(sizeof(BaudRateTab)/sizeof(struct BaudRates)); i++) {
+    int b=BaudRateTab[i].real;
+    int d=((b-baud)*10000)/b;
+    if(abs(d)<=300) {
+      PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> %d (%+.2f%%)",devName,baud,b,(float)d/100.0);
+      return BaudRateTab[i].apival;
+      }
+    }
+  PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> custom",devName,baud);
+  return B0;
+}
+
+bool cSerial::SetMode(int mode, int baud)
+{
+  if(fd>=0) {
+    speed_t bconst=FindBaud(baud);
+    bool custom=false;
+    if(bconst==B0) { custom=true; bconst=B38400; }
+    
+    struct termios tio;
+    memset(&tio,0,sizeof(tio));
+    LBSTARTF(L_CORE_SERIAL);
+    LBPUT("%s: set serial options: %d,",devName,baud);
+    tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL);
+    if(!(mode&SM_1SB)) tio.c_cflag |= CSTOPB;
+    tio.c_iflag = (INPCK | BRKINT);
+    tio.c_cc[VMIN] = 1;
+    cfsetispeed(&tio,bconst);
+    cfsetospeed(&tio,bconst);
+    switch(mode&SM_MASK) {
+      case SM_8E2: 
+        LBPUT("8e%d",(mode&SM_1SB)?1:2);
+        tio.c_cflag |= PARENB; break;
+      case SM_8N2:
+        LBPUT("8n%d",(mode&SM_1SB)?1:2);
+        break;
+      case SM_8O2:
+        LBPUT("8o%d",(mode&SM_1SB)?1:2);
+        tio.c_cflag |= (PARENB | PARODD); break;
+      default:
+        LBPUT("BAD MODE");
+        return false;
+      }
+    LBEND();
+
+    struct serial_struct s;
+    if(ioctl(fd,TIOCGSERIAL,&s)<0) {
+      PRINTF(L_GEN_ERROR,"%s: get serial failed: %s",devName,strerror(errno));
+      return false;
+      }
+    if(!custom && ((s.flags&ASYNC_SPD_MASK)==ASYNC_SPD_CUST || s.custom_divisor!=0)) {
+      s.custom_divisor=0;
+      s.flags &= ~ASYNC_SPD_MASK;
+      if(ioctl(fd,TIOCSSERIAL,&s)<0) {
+        PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno));
+        return false;
+        }
+      }
+    if(!tcsetattr(fd,TCSANOW,&tio)) {
+      if(custom) {
+        s.custom_divisor=(s.baud_base+(baud/2))/baud;
+        s.flags=(s.flags&~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
+        PRINTF(L_CORE_SERIAL,"%s: custom: baud_base=%d baud=%d devisor=%d -> effective baudrate %d (%+.2f%% off)",devName,s.baud_base,baud,s.custom_divisor,s.baud_base/s.custom_divisor,(float)(s.baud_base/s.custom_divisor-baud)/(float)baud);
+        if(ioctl(fd,TIOCSSERIAL,&s)<0) {
+          PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno));
+          return false;
+          }
+        }
+      currMode=mode; Flush();
+      return true;
+      }
+    else PRINTF(L_GEN_ERROR,"%s: tcsetattr failed: %s",devName,strerror(errno));
+    }
+  return false;
+}
+
+void cSerial::Flush(void)
+{
+  if(fd>=0)
+    CHECK(tcflush(fd,TCIOFLUSH));
+}
+
+int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout)
+{
+  PRINTF(L_CORE_SERIAL,"%s: read len=%d timeout=%d:%d",devName,len,timeout,initialTimeout);
+  bool incomplete=false;
+  if(len<0) { len=-len; incomplete=true; }
+  int to=initialTimeout>0 ? initialTimeout : timeout;
+  int n=0;
+  while(n<len) {
+    int r=read(fd,mem+n,len-n);
+    if(r<=0) {
+      if(r==0) PRINTF(L_CORE_SERIAL,"%s: read bogus eof",devName);
+      if(errno==EAGAIN) {
+        struct pollfd u;
+        u.fd=fd; u.events=POLLIN;
+        r=poll(&u,1,to);
+        if(r<0) {
+          PRINTF(L_GEN_ERROR,"%s: read poll failed: %s",devName,strerror(errno));
+          return -1;
+          }
+        else if(r==0) { // timeout
+          PRINTF(L_CORE_SERIAL,"%s: read timeout (%d ms)",devName,to);
+          if(n>0 && incomplete) break; // return bytes read so far
+          return -2;
+          }
+        continue;
+        }
+      else {
+        PRINTF(L_GEN_ERROR,"%s: read failed: %s",devName,strerror(errno));
+        return -1;
+        }
+      }
+    n+=r; to=timeout;
+    }
+  HEXDUMP(L_CORE_SERIAL,mem,n,"%s: read data",devName);
+  return n;
+}
+
+int cSerial::Write(const unsigned char *mem, int len, int delay)
+{
+  PRINTF(L_CORE_SERIAL,"%s: write len=%d delay=%d",devName,len,delay);
+  HEXDUMP(L_CORE_SERIAL,mem,len,"%s: write data",devName);
+  Flush();
+  int n=0;
+  while(n<len) {
+    struct pollfd u;
+    u.fd=fd; u.events=POLLOUT;
+    int r;
+    do {
+      r=poll(&u,1,500);
+      if(r<0) {
+        PRINTF(L_GEN_ERROR,"%s: write poll failed: %s",devName,strerror(errno));
+        return -1;
+        }
+      else if(r==0) { // timeout
+        PRINTF(L_CORE_SERIAL,"%s: write timeout",devName);
+        return -2;
+        }
+      } while(r<1);
+    r=write(fd,mem+n,delay>0?1:len-n);
+    if(r<0 && errno!=EAGAIN) {
+      PRINTF(L_GEN_ERROR,"%s: write failed: %s",devName,strerror(errno));
+      return -1;
+      }
+    if(r>0) n+=r;
+    if(delay>0) cCondWait::SleepMs(delay);
+    }
+  return n;
+}
+
+void cSerial::ToggleRTS(void)
+{
+  int mask=0;
+  if(ioctl(fd,TIOCMGET,&mask)<0) { LOG_ERROR; return; }
+  if(mask&TIOCM_RTS) { mask&=~TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now off",devName); }
+  else               { mask|= TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now on",devName); }
+  CHECK(ioctl(fd,TIOCMSET,&mask));
+  Flush();
+}
+
+bool cSerial::CheckCAR(void)
+{
+  int status=0;
+  if(ioctl(fd,TIOCMGET,&status)<0) { LOG_ERROR; return false; }
+  PRINTF(L_CORE_SERIAL,"%s: CAR is %sactive (lines:%s%s%s%s%s%s%s%s%s)",
+     devName,(status&TIOCM_CAR)?"in":"",
+     (status&TIOCM_LE)?" LE":"",  (status&TIOCM_DTR)?" DTR":"",
+     (status&TIOCM_RTS)?" RTS":"",(status&TIOCM_ST)?" ST":"",
+     (status&TIOCM_SR)?" SR":"",  (status&TIOCM_CTS)?" CTS":"",
+     (status&TIOCM_CAR)?" CAR":"",(status&TIOCM_RNG)?" RNG":"",
+     (status&TIOCM_DSR)?" DSR":"" );
+  status^=statInv; // invert status for broken cardreaders
+  return !(status & TIOCM_CAR);
+}
+
+#else //SER_EMU
+
+//
+// emulator for a serial phoenix interface
+// (activate in smartcard.h)
+//
+
+bool cSerial::Open(void)
+{
+  PRINTF(L_CORE_SERIAL,"serial: open serial port %s (emulator)",devName);
+  PRINTF(L_CORE_SERIAL,"serial: init done");
+  fd=1; buffCount=0; cmdStart=true; rts=false; flag=false;
+  remTime=time(0); dsr=true;
+  return true;
+}
+
+void cSerial::Close(void)
+{
+  if(fd>=0) {
+    PRINTF(L_CORE_SERIAL,"serial: shutting down ... ");
+    fd=-1;
+    PRINTF(L_CORE_SERIAL,"done");
+    }
+}
+
+speed_t cSerial::FindBaud(int baud)
+{
+  return B0;
+}
+
+bool cSerial::SetMode(int mode, int baud)
+{
+  currMode=mode;
+  Flush();
+  return true;
+}
+
+void cSerial::Flush(void)
+{
+  buffCount=0;
+}
+
+#define PUSH(mem,len) { memcpy(devBuff+buffCount,(mem),(len)); buffCount+=(len); }
+#define PUSHB(b)      { devBuff[buffCount++]=(b); }
+
+int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout)
+{
+  PRINTF(L_CORE_SERIAL,"serial: read len=%d timeout=%d",len,timeout);
+  if(len<0) {
+    len=-len;
+    if(buffCount<len) len=buffCount;
+    }
+  if(buffCount<len) return -2; // timeout
+  memcpy(mem,devBuff,len);
+  memmove(devBuff,devBuff+len,buffCount-len);
+  buffCount-=len;
+  return len;
+}
+
+int cSerial::Write(const unsigned char *mem, int len, int delay)
+{
+  PRINTF(L_CORE_SERIAL,"serial: write len=%d delay=%d",len,delay);
+  Flush();
+  cCondWait::SleepMs(300);
+  PUSH(mem,len); // echo back
+#if defined(EMU_SECA)
+  if(cmdStart && len==CMD_LEN) { // new INS
+    PUSHB(mem[INS_IDX]); // ACK byte
+    cmdStart=false;
+    dataLen=mem[LEN_IDX];
+    unsigned char data[256];
+    memset(data,0,sizeof(data));
+    nextSW[0]=0x90; nextSW[1]=0x00;
+    bool readcmd=false;
+    switch(mem[0]*256+mem[1]) {
+      case 0xc13a:
+      case 0xc10e:
+        readcmd=true;
+        break;
+      case 0xc116:
+        data[3]=0x04;
+        readcmd=true;
+        break;
+      case 0xc112:
+        data[1]=0x64;
+        data[22]=0x1d; data[23]=0x84;
+        readcmd=true;
+        break;
+      case 0xc132:
+        data[1]=0xFF; data[2]=0xFF; data[3]=0xFF; data[4]=0xFF;
+        data[5]=0xFF; data[6]=0xFF; data[7]=0xFF; data[8]=0xFF;
+        readcmd=true;
+        break;
+      }
+    if(readcmd) {
+      PUSH(data,dataLen);
+      PUSH(nextSW,2);
+      cmdStart=true;
+      }
+    }
+  else {
+    dataLen-=len;
+    if(dataLen<=0 && !cmdStart) {
+      PUSH(nextSW,2);
+      cmdStart=true;
+      }
+    }
+#elif defined(EMU_CRYPTO)
+  if(cmdStart && len==CMD_LEN) { // new INS
+    cmdStart=false;
+    dataLen=mem[LEN_IDX];
+    unsigned char data[256];
+    memset(data,0,sizeof(data));
+    nextSW[0]=0x90; nextSW[1]=0x00;
+    bool readcmd=false;
+    switch(mem[0]*256+mem[1]) {
+      case 0xa4a4:
+        switch(mem[5]*256+mem[6]) {
+          case 0x2f01: 
+          case 0x0f00:
+          case 0x0f20:
+          //case 0x0f40:
+          case 0x0f60:
+          case 0x0e11:
+          case 0x1f88:
+          case 0x1f8c: nextSW[0]=0x9f; nextSW[1]=0x11; break;
+          default:     nextSW[0]=0x94; nextSW[1]=0x04; break;
+          }
+        break;
+      case 0xa4a2:
+        if(mem[2]==1) {
+          if(mem[4]==3) {
+            record=0x01;
+            nextSW[0]=0x9f; nextSW[1]=0x26;
+            }
+          else {
+            record=0x02;
+            nextSW[0]=0x9f; nextSW[1]=0x42;
+            }
+          }
+        else {
+          record=mem[5];
+          switch(record) {
+            case 0x80: nextSW[0]=0x9f; nextSW[1]=0x07; break;
+            case 0xD1: nextSW[0]=0x9f; nextSW[1]=0x04; break;
+            case 0x9F: nextSW[0]=0x9f; nextSW[1]=0x03; break;
+            case 0x9E: nextSW[0]=0x9f; nextSW[1]=0x42; break;
+            case 0xD6:
+            case 0xC0: nextSW[0]=0x9f; nextSW[1]=0x12; break;
+            default:   nextSW[0]=0x94; nextSW[1]=0x02; break;
+            }
+          }
+        break;
+      case 0xa4b2:
+        readcmd=true;
+        switch(record) {
+          case 0x01:
+            if(mem[3]==1) {
+              nextSW[0]=0x94; nextSW[1]=0x02;
+              dataLen=0;
+              }
+            else {
+              dataLen=0x26;
+              static const unsigned char resp[0x26] = {
+                0x83,0x01,0x88,
+                0x8c,0x03,0x40,0x30,0x40,
+                0x8d,0x04,0x16,0xac,0x16,0xba,
+                0xd5,0x10,'D','u','m','m','y',0,0,0,0,0,0,0,0x12,0x34,0x56,0x67,
+                0x8f,0x01,0x55,
+                0x91,0x01,0x40
+                };
+              memcpy(data,resp,sizeof(resp));
+              }
+            break;
+          case 0x02:
+            if(mem[3]==1) {
+              nextSW[0]=0x94; nextSW[1]=0x02;
+              dataLen=0;
+              }
+            else {
+              dataLen=0x42;
+              static const unsigned char resp[0x42] = {
+                0x83,0x01,0x88,
+                0x8c,0x03,0xA0,0x30,0x40,
+                0x8d,0x04,0x16,0xac,0x16,0xba,
+                0xd5,0x10,'D','u','m','m','y',0,0,0,0,0,0,0,0x12,0x34,0x56,0x67,
+                0x92,0x20,0x00 // ...
+                };
+              memcpy(data,resp,sizeof(resp));
+              }    
+            break;
+          case 0x80:
+            dataLen=0x07;
+            data[0]=0x80;
+            data[1]=0x05;
+            data[2]=0x5c;
+            data[3]=0x0c;
+            data[4]=0x00;
+            data[5]=0xec;
+            data[6]=0x0a;
+            break;
+          case 0xD1:
+            dataLen=0x04;
+            data[0]=0xd1;
+            data[1]=0x02;
+            data[2]=0x0d;
+            data[3]=0x22;
+            break;
+          case 0x9F:
+            dataLen=0x03;
+            data[0]=0x9f;
+            data[1]=0x01;
+            data[2]=0x88;
+            break;
+          case 0x9E:
+            {
+            dataLen=0x42;
+            static const unsigned char resp[0x42] = {
+              0x9E,0x40,
+              0x28,0xFE, /// ...
+              };
+            memcpy(data,resp,sizeof(resp));
+            flag=true;
+            break;
+            }
+          case 0xC0:
+          case 0xD6:
+            dataLen=0x12;
+            data[0]=record;
+            data[1]=0x10;
+            memset(data+2,0,16);
+            strcpy((char *)data+2,"Dum Issuer");
+            break;
+          }
+        break;
+      case 0xa4b8:
+        readcmd=true;
+        if(mem[2]==0) {
+          dataLen=0x0C;
+          memset(data,0,0x0c);
+          data[0]=0xDF;
+          data[1]=0x0A;
+          data[4]=0xDF;
+          data[5]=0x88;
+          }
+        else {
+          dataLen=0;
+          nextSW[0]=0x94; nextSW[1]=0x02;
+          }
+        break;
+      case 0xa44c:
+        nextSW[0]=0x9f; nextSW[1]=flag ? 0x42 : 0x1c;
+        break;
+      case 0xa4c0:
+        readcmd=true;
+        if(mem[4]==0x1c) {
+          data[0]=0xdb;
+          data[1]=0x10;
+          memset(&data[2],0x45,16);
+          data[18]=0xdf;
+          data[19]=0x08;
+          data[20]=0x50;
+          data[23]=0x80;
+          }
+        else if(mem[4]==0x11) {
+          memset(data,0,0x11);
+          data[0]=0xdf;
+          data[1]=0x0f;
+          data[6]=0x3f;
+          data[7]=0x6a;
+          }
+        else if(mem[4]==0x42) {
+          static const unsigned char resp[0x42] = {
+            0xdf,0x40,
+            0x6d,0xbb,0x81, // ...
+            };
+          memcpy(data,resp,sizeof(resp));
+          }
+        break;
+      }
+    if(readcmd) {
+      if(dataLen>0) {
+        PUSHB(mem[INS_IDX]); // ACK byte
+        PUSH(data,dataLen);
+        }
+      PUSH(nextSW,2);
+      cmdStart=true;
+      }
+    else {
+      PUSHB(mem[INS_IDX]); // ACK byte
+      }
+    }
+  else {
+    dataLen-=len;
+    if(dataLen<=0 && !cmdStart) {
+      PUSH(nextSW,2);
+      cmdStart=true;
+      }
+    }
+#elif defined(EMU_IRD)
+  static const struct Resp {
+    unsigned int cmd;
+    unsigned char data[MAX_LEN];
+    } resp[] = {
+#ifdef EMU_IRD_384
+  { 0x01020203,
+    {0x01,0x02,0x00,0x00,0x02,0x00,0x00,0x10,0x03,0x84,0x00,0x00,0x00,0x17,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x45,0x52} },
+  { 0x01020E02,
+    {0x01,0x02,0x00,0x00,0x0e,0x02,0x00,0x40,0xa9,0x6d,0x73,0x97,0x9e,0xfc,0x9b,0x8e,0x5b,0x8c,0xfa,0xb2,0x0c,0xb2,0x57,0x0f,0xb2,0xf7,0x29,0x4e,0xa2,0xcb,0xbd,0x5b,0x52,0x74,0xb1,0x2a,0xb7,0xc5,0xd9,0x62,0x6d,0x37,0x6d,0x9d,0xa3,0xe9,0x61,0xbb,0x1b,0x2b,0x56,0xb7,0x86,0x0c,0xe6,0xb1,0x07,0x6f,0xe0,0xf8,0x8a,0xd3,0x05,0x83,0xf6,0x53,0x0e,0xd2,0x72,0xd5,0xc1,0x50} },
+  { 0x01020E03,
+    {0x01,0x02,0x00,0x00,0x0e,0x03,0x00,0x40,0xb6,0xde,0xa8,0xce,0x86,0x1c,0x42,0x72,0xa8,0x16,0x4b,0xf9,0xce,0x33,0xb5,0x43,0xd0,0x50,0xe6,0xa7,0xf1,0xcb,0x55,0x25,0x97,0x13,0xee,0x62,0x98,0xe7,0x17,0x50,0xeb,0x3b,0x59,0x10,0x0a,0xb6,0x2e,0x93,0x61,0x71,0x3c,0xe6,0xe2,0x2c,0x1e,0x7d,0xbd,0x6a,0x49,0xbb,0x04,0x5b,0xdf,0x2f,0xb7,0xa7,0x93,0xe0,0x3d,0x40,0x4e,0xd1} },
+#else
+  { 0x01020203,
+    {0x01,0x02,0x00,0x00,0x02,0x00,0x00,0x10,0x03,0x83,0x00,0x00,0x00,0x17,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x45,0x52} },
+  { 0x01020E02,
+    {0x01,0x02,0x00,0x00,0x0E,0x02,0x00,0x40,0x65,0xEF,0xD0,0xA7,0xF3,0x80,0x48,0x0E,0xC1,0x4B,0x41,0x6C,0xDB,0x68,0x47,0xE4,0x23,0xAD,0x96,0x12,0x07,0x58,0x58,0x29,0xE9,0x14,0x27,0x7F,0x7D,0xF8,0xC2,0x65,0x76,0x4D,0x75,0x04,0x7C,0x9B,0xAA,0x99,0x58,0xEA,0xE2,0x43,0xB5,0x03,0x05,0xD6,0x62,0x99,0xF5,0x18,0x16,0x4E,0xCF,0x49,0x11,0xBD,0xF3,0xEE,0xC3,0xCD,0x90,0x3B} },
+  { 0x01020E03,
+    {0x01,0x02,0x00,0x00,0x0E,0x03,0x00,0x40,0x9B,0x06,0xB5,0x0A,0x98,0xC6,0x2E,0x1D,0x71,0xA1,0xE8,0x84,0xAE,0x98,0x57,0xE9,0xE6,0xC2,0x97,0x46,0x25,0x7A,0x2B,0xA1,0xD5,0x33,0x18,0xDE,0x16,0xC1,0xAB,0x22,0x2C,0xC2,0x11,0x24,0x81,0x11,0xA8,0x39,0xE3,0xB1,0xDB,0x33,0x1A,0x93,0x31,0xB0,0x61,0xD8,0xDE,0x92,0x1F,0x29,0x20,0xD0,0x9E,0x0F,0x6A,0xF0,0x7C,0xBA,0xCD,0xCC} },
+#endif
+  { 0x01020003,
+    {0x01,0x02,0x00,0x00,0x00,0x03,0x00,0x14,0x37,0x30,0x31,0x32,0x30,0x36,0x39,0x33,0x34,0x37,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} },
+  { 0x01020103,
+    {0x01,0x02,0x00,0x00,0x01,0x00,0x00,0x10,0x00,0x17,0x00,0x00,0x01,0x00,0x17,0x00,0x00,0x01,0x04,0x00,0xF3,0x86,0x01,0x1A} },
+  { 0x01021100,
+    {0x01,0x02,0x58,0x00,0x11,0x00,0x00,0x00} },
+  { 0x01020903,
+    {0x01,0x02,0x55,0x00,0x09,0x03,0x00,0x00} },
+  { 0x01020303,
+    {0x01,0x02,0x00,0x00,0x03,0x03,0x02,0x18,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} },
+  { 0x01020400,
+    {0x01,0x02,0x54,0x00,0x04,0x00,0x00,0x00} },
+  { 0x01050000,
+    {0x01,0x05,0x9D,0x00,0x38,0x00,0x02,0x16,0x00,0x01,0x00,0x13,0xFF,0xFF,0x22,0x88,0xBF,0x02,0x70,0xFA,0x5F,0x80,0xFD,0x1E,0xD4,0xD6,0xF0,0xF1,0x81,0xB3} },
+  { 0x01010000,
+    {0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0xFF} },
+  { 0x00000000, // don't delete this one
+    {} }
+  };
+  const unsigned int cmd=(mem[0]<<24) + (mem[1]<<16) + (mem[2]<<8) + mem[3];
+  const struct Resp *r=&resp[0];
+  while(1) {
+    if(!r->cmd) {
+      static const unsigned char fail[] = { 0x01,0x99,0x6f };
+      PUSH(fail,sizeof(fail));
+      break;
+      }
+    if(cmd==r->cmd) {
+      const int len=r->data[7]+9-1;
+      int cs=0x3f;
+      for(int i=0 ; i<len ; i++) cs ^= r->data[i];
+      PUSH(r->data,len);
+      PUSHB(cs);
+      break;
+      }
+    r++;
+    }
+#else
+#error No serial emulation mode selected
+#endif
+  return len;
+}
+
+void cSerial::ToggleRTS(void)
+{
+  rts=!rts;
+  if(!rts) { 
+#if defined(EMU_SECA)
+    static const unsigned char atr[] = { 0x3b,0x77,0x12,0x00,0x00,
+                                         0x60,0x60,0x03,0x0E,0x6C,0xB6,0xD6 };
+#elif defined(EMU_IRD)
+    static const unsigned char atr[] = { 0x3B,0x9F,0x21,0x0E,
+#ifdef EMU_IRD_384
+                                         0x49,0x52,0x44,0x45,0x54,0x4f,0x20,0x41,0x43,0x53,0x03,0x84,0x55,0xff,0x80,0x6d };
+#else
+                                         0x49,0x52,0x44,0x45,0x54,0x4F,0x20,0x41,0x43,0x53,0x03,0x83,0x95,0x00,0x80,0x55 };
+#endif
+#elif defined(EMU_CRYPTO)
+    static const unsigned char atr[] = { 0x3B,0x78,0x12,0x00,0x00,
+                                         0x65,0xC4,0x05,0x05,0x8F,0xF1,0x90,0x00 };
+#else
+#error No serial emulation mode selected
+#endif
+    PUSH(atr,sizeof(atr));
+    PRINTF(L_CORE_SERIAL,"serial: toggle RTS, now off");
+    }
+  else { PRINTF(L_CORE_SERIAL,"serial: toggle RTS, now on"); }
+}
+
+bool cSerial::CheckCAR(void)
+{
+#if 0
+  if(time(0)>=remTime+30) {
+    dsr=!dsr;
+    remTime=time(0);
+    }
+#endif
+  return dsr;
+}
+
+#endif //SER_EMU
+
+// -- cInfoStr -----------------------------------------------------------------
+
+cInfoStr::cInfoStr(void)
+:cLineBuff(256)
+{
+  current=0;
+}
+
+cInfoStr::~cInfoStr()
+{
+  free(current);
+}
+
+bool cInfoStr::Get(char *buff, int len)
+{
+  cMutexLock lock(&mutex);
+  if(current && current[0]) {
+    strn0cpy(buff,current,len);
+    return true;
+    }
+  return false;
+}
+
+void cInfoStr::Begin(void)
+{
+  Flush();
+}
+
+void cInfoStr::Finish()
+{
+  cMutexLock lock(&mutex);
+  free(current);
+  current=Grab();
+}
+
+// -- cSmartCard ---------------------------------------------------------------
+
+static const int Ftable[16] = {
+  372,372,558,744,1116,1488,1860,0,0,512,768,1024,1536,2048,0,0
+  };
+
+static const int fstable[16] = {
+  3571200,5000000,6000000, 8000000,12000000,16000000,20000000,0,
+        0,5000000,7500000,10000000,15000000,20000000,       0,0
+};
+
+static const float Dtable[16] = {
+  0.0,1.0,2.0,4.0,8.0,16.0,0.0,0.0,
+  0.0,0.0,0.5,0.25,0.125,0.0625,0.03125,0.015625
+  };
+  
+cSmartCard::cSmartCard(const struct CardConfig *Cfg, const struct StatusMsg *Msg)
+{
+  cfg=Cfg; msg=Msg;
+  ser=0; atr=0; idStr[0]=0; cardUp=false; needsReset=true;
+}
+
+bool cSmartCard::GetCardIdStr(char *str, int len)
+{
+  strn0cpy(str,idStr,len);
+  return (str[0]!=0);
+}
+
+bool cSmartCard::GetCardInfoStr(char *str, int len)
+{
+  return infoStr.Get(str,len);
+}
+
+bool cSmartCard::Setup(cSerial *Ser, const struct Atr *Atr, int Id)
+{
+  ser=Ser; atr=Atr; id=Id;
+  if(cfg->SerMode==(ser->CurrentMode()&SM_MASK) && Init()) {
+    cardUp=true; needsReset=false;
+    return true;
+    }
+  return false;
+}
+
+#define NEED(x) { \
+                if(len+(x)>Atr->atrLen) { \
+                    LBPUT("SHORT ATR"); \
+                    return false; \
+                    } \
+                }
+
+bool cSmartCard::ParseAtr(struct Atr *Atr, int id, int clock)
+{
+  // default values
+  Atr->histLen=0; Atr->T=0; Atr->F=372; Atr->fs=clock; Atr->D=1.0; Atr->N=0;
+  Atr->WI=10; Atr->BWI=4; Atr->CWI=0; Atr->Tspec=-1;
+
+  const unsigned char *atr=Atr->atr;
+  if(atr[0]==0x03) {
+    PRINTF(L_CORE_SC,"%d: indirect convention detected",id);
+    cSmartCard::Invert(Atr->atr,Atr->atrLen);
+    Atr->convention=SM_INDIRECT;
+    }
+  else if(atr[0]==0x3F) {
+    PRINTF(L_CORE_SC,"%d: indirect convention detected (converted)",id);
+    Atr->convention=SM_INDIRECT_CONVERTED;
+    }
+  else if(atr[0]==0x3B) {
+    PRINTF(L_CORE_SC,"%d: direct convention detected",id);
+    Atr->convention=SM_DIRECT;
+    }
+  else {
+    PRINTF(L_CORE_SC,"%d: byte mode not supported 0x%02x",id,atr[0]);
+    return false;
+    }
+
+  // TS TO
+  Atr->histLen=atr[1]&0x0F;
+  int Y=atr[1]&0xF0, i=1, len=2;
+  LBSTARTF(L_CORE_SC);
+  LBPUT("%d: atr decoding TS=%02x hist=%d Y%d=%02x ",id,atr[0],Atr->histLen,i,Y);
+  do {
+    if(Y&0x10) { // TAi
+      NEED(1);
+      LBPUT("TA%d=%02x ",i,atr[len]);
+      if(i==1) {
+        Atr->TA1=atr[len];
+        Atr->F=Ftable[(atr[len]>>4)&0x0F];
+        Atr->fs=fstable[(atr[len]>>4)&0x0F];
+        Atr->D=Dtable[ atr[len]    &0x0F];
+        LBPUT("F=%d, fs=%.4fMHz, D=%f ",Atr->F,Atr->fs/1000000.0,Atr->D);
+        }
+      else if(i==2) {
+        Atr->Tspec=atr[len]&0x0F;
+        LBPUT("Tspec=%d ",Atr->Tspec);
+        }
+      else if(i==3) {
+        LBPUT("IFSC=%d ",atr[len]);
+        }
+      len++;
+      }
+    if(Y&0x20) { // TBi
+      NEED(1);
+      LBPUT("TB%d=%02x ",i,atr[len]);
+      if(i==3) {
+        Atr->BWI=(atr[len]>>4)&0x0F;
+        Atr->CWI=atr[len]&0x0F;
+        LBPUT("BWI=%d CWI=%d ",Atr->BWI,Atr->CWI);
+        }
+      len++;
+      }
+    if(Y&0x40) { // TCi
+      NEED(1);
+      LBPUT("TC%d=%02x ",i,atr[len]);
+      if(i==1) {
+        Atr->N=atr[len];
+        LBPUT("N=%d ",Atr->N);
+        }
+      else if(i==2) {
+        Atr->WI=atr[len];
+        LBPUT("WI=%d ",Atr->WI);
+        }
+      else if(i==3) {
+        LBPUT("CHK=%s ",atr[len]&1 ? "CRC16":"LRC");
+        }
+      len++;
+      }
+    if(Y&0x80) { // TDi
+      NEED(1);
+      LBPUT("TD%d=%02x ",i,atr[len]);
+      if(i==1) {
+        Atr->T=atr[len]&0x0F;
+        LBPUT("T=%d ",Atr->T);
+        }
+      else {
+        LBPUT("Tn=%d ",atr[len]&0x0F);
+        }
+      Y=atr[len]&0xF0;
+      len++;
+      }
+    else Y=0;
+    i++;
+    LBPUT("Y%d=%02x ",i,Y);
+    } while(Y);
+  NEED(Atr->histLen);
+  LBEND();
+
+  LBSTART(L_CORE_SC);
+  LBPUT("%d: historical:",id);
+  for(int i=0 ; i<Atr->histLen ; i++) LBPUT(" %02x",atr[len+i]);
+  LBFLUSH();
+  LBPUT("%d: historical: '",id);
+  for(int i=0 ; i<Atr->histLen ; i++) LBPUT("%c",isprint(atr[len+i]) ? atr[len+i] : '.');
+  LBEND();
+
+  memcpy(Atr->hist,&atr[len],Atr->histLen);
+  len+=Atr->histLen;
+
+  // TCK
+  if(Atr->T!=0 && len+1<=Atr->atrLen) {
+    len++;
+    unsigned char cs=XorSum(atr+1,len-1);
+    // according to the ISO the initial TS byte isn't checksumed, but
+    // some cards do so. In this case the checksum is equal TS.
+    if(cs==0) PRINTF(L_CORE_SC,"%d: atr checksum ok",id);
+    else if(cs==atr[0]) PRINTF(L_CORE_SC,"iso: %d: atr checksum is TS (not ISO compliant)",id);
+    else {
+      PRINTF(L_CORE_SC,"%d: atr checksum FAILED (cs:%02x)",id,cs);
+      return false;
+      }
+    }
+  else PRINTF(L_CORE_SC,"%d: atr checksum not given/not required",id);
+
+  if(Atr->atrLen>len) PRINTF(L_CORE_SC,"%d: long atr read=%d len=%d",id,Atr->atrLen,len);
+  Atr->wwt=960*Atr->WI*Atr->F/(clock/1000);
+  Atr->bwt=(int)(960*(float)(1<<Atr->BWI)*372/(clock/1000));
+  PRINTF(L_CORE_SC,"%d: indicated wwt(T0)=%d ms, bwt(T1)=%d ms (at %.4f MHz)",id,Atr->wwt,Atr->bwt,(float)clock/1000000);
+  return true;
+}
+
+void cSmartCard::Invert(unsigned char *data, int n)
+{
+  static const unsigned char swaptab[] =  { 15,7,11,3,13,5,9,1,14,6,10,2,12,4,8,0 };
+  for(int i=n-1; i>=0; i--)
+    data[i]=(swaptab[data[i]&0x0f]<<4) | swaptab[data[i]>>4];
+}
+
+int cSmartCard::SerRead(unsigned char *data, int len, int to)
+{
+  if(atr->T==1 || atr->T==14) {
+    int r=Read(data,len,to);
+    if(r<0) Test(false);
+    return r;
+    }
+  else {
+    PRINTF(L_CORE_SC,"%d: SerRead() not allowed for ISO compliant cards (T=%d)",id,atr->T);
+    return -1;
+    }
+}
+
+int cSmartCard::SerWrite(const unsigned char *data, int len)
+{
+  if(atr->T==1 || atr->T==14) {
+    int r=Write(data,len);
+    if(r<0) Test(false);
+    return r;
+    }
+  else {
+    PRINTF(L_CORE_SC,"%d: SerWrite() not allowed for ISO compliant cards (T=%d)",id,atr->T);
+    return -1;
+    }
+}
+
+int cSmartCard::Read(unsigned char *data, int len, int to)
+{
+  int r=ser->Read(data,len,cfg->serTO,to);
+  if(atr->convention==SM_INDIRECT && r>0) Invert(data,r);
+  return r;
+}
+
+int cSmartCard::Write(const unsigned char *data, int len)
+{
+  unsigned char *tmp=AUTOMEM(len);
+  if(atr->convention==SM_INDIRECT) {
+    memcpy(tmp,data,len);
+    Invert(tmp,len);
+    data=tmp;
+    }
+  int r=ser->Write(data,len);
+  // Discard command echo.
+  // NB! There is no command echo in SmartReader+ mode.
+  if(ser->DeviceType()!=SM_SMARTREADER && r>0) {
+    unsigned char *buff=AUTOMEM(r);
+    int rr=ser->Read(buff,r,cfg->serTO);
+    if(rr<0) r=rr;
+    }
+  return r;
+}
+
+int cSmartCard::Procedure(unsigned char ins, int restLen)
+{
+  int r;
+  unsigned char buff;
+  LBSTARTF(L_CORE_SC);
+  LBPUT("%d: <- PROC: ",id);
+  do {
+    do {
+      if(Read(&buff,1,cfg->workTO)<=0) return -1;
+      LBPUT("%02x ",buff);
+      } while(buff==0x60);
+
+    if((buff&0xF0)==0x60 || (buff&0xF0)==0x90) { // SW1/SW2
+      sb[0]=buff;
+      if(Read(&buff,1)<=0) return -1;
+      LBPUT("%02x",buff);
+      sb[1]=buff;
+      return 0;
+      }
+    else {
+      if((buff&0xFE)==(ins&0xFE)) r=restLen;
+      else if((~buff&0xFE)==(ins&0xFE)) r=1;
+      else {
+        LBPUT("cannot handle procedure %02x (ins=%02x)",buff,ins);
+        return -1;
+        }
+      if(r>restLen) {
+        LBPUT("data overrun r=%d restLen=%d",r,restLen);
+        return -1;
+        }
+      }
+    } while(r==0);
+  LBEND();
+  return r;
+}
+
+bool cSmartCard::IsoRead(const unsigned char *cmd, unsigned char *data)
+{
+  if(atr->T==0) { // only for ISO complaint cards
+    LDUMP(L_CORE_SC,cmd,CMD_LEN,"%d: -> INS:",id);
+    if(Write(cmd,CMD_LEN)<0) return Test(false);
+    int tr=cmd[LEN_IDX] ? cmd[LEN_IDX] : 256;
+    int len=0;
+    while(1) {
+      int r=Procedure(cmd[INS_IDX],tr-len);
+      if(r<=0) return Test(r==0);
+      if(Read(data+len,r)<0) return Test(false);
+      LDUMP(L_CORE_SC,data+len,r,"%d: <- DATA:",id);
+      len+=r;
+      }
+    }
+  else PRINTF(L_CORE_SC,"%d: can't IsoRead() from incompatible card (T=%d)",id,atr->T);
+  return false;
+}
+
+bool cSmartCard::IsoWrite(const unsigned char *cmd, const unsigned char *data)
+{
+  if(atr->T==0) { // only for ISO complaint cards
+    LDUMP(L_CORE_SC,cmd,CMD_LEN,"%d: -> INS:",id);
+    if(Write(cmd,CMD_LEN)<0) return Test(false);
+    int len=0;
+    while(1) {
+      int r=Procedure(cmd[INS_IDX],cmd[LEN_IDX]-len);
+      if(r<=0) return Test(r==0);
+      if(Write(data+len,r)<0) return Test(false);
+      LDUMP(L_CORE_SC,data+len,r,"%d: -> DATA:",id);
+      len+=r;
+      }
+    }
+  else PRINTF(L_CORE_SC,"%d: can't IsoWrite() to incompatible card (T=%d)",id,atr->T);
+  return false;
+}
+
+bool cSmartCard::Test(bool res)
+{
+ if(!res) {
+   TriggerReset();
+   PRINTF(L_CORE_SC,"%d: reset triggered",id);
+   }
+ return res;
+}
+
+bool cSmartCard::Status(void)
+{
+  const struct StatusMsg *m=msg;
+  while(m->sb[0]!=0xFF) {
+    if(sb[0]==m->sb[0] && (m->sb[1]==0xFF || sb[1]==m->sb[1])) {
+      if(!m->retval) PRINTF(L_CORE_SC,"%d: %s (status: %02x %02x)",id,m->message,sb[0],sb[1]);
+      return m->retval;
+      }
+    m++;
+    }
+  PRINTF(L_CORE_SC,"%d: unknown (status: %02x %02x)",id,sb[0],sb[1]);
+  return false;
+}
+
+int cSmartCard::CheckSctLen(const unsigned char *data, int off)
+{
+  int l=SCT_LEN(data);
+  if(l+off > MAX_LEN) {
+    PRINTF(L_CORE_SC,"section too long %d > %d",l,MAX_LEN-off);
+    l=-1;
+    }
+  return l;
+}
+
+// -- cSmartCardData -----------------------------------------------------------
+
+cSmartCardData::cSmartCardData(int Ident)
+{
+  ident=Ident;
+}
+
+// -- cSmartCardLink -----------------------------------------------------------
+
+cSmartCardLink::cSmartCardLink(const char *Name, int Id)
+{
+  name=Name; id=Id;
+  cSmartCards::Register(this);
+}
+
+// -- cSmartCards --------------------------------------------------------------
+
+cSmartCardLink *cSmartCards::first=0;
+
+cSmartCards smartcards;
+
+static const char *serModes[] = { 0,"8e2","8o2","8n2" };
+
+cSmartCards::cSmartCards(void)
+:cThread("SmartcardWatcher")
+,cStructList<cSmartCardData>("smartcard data",DATAFILE,SL_MISSINGOK|SL_WATCH|SL_VERBOSE)
+{
+  for(int i=0 ; i<MAX_PORTS ; i++) ports[i].Serial=0;
+  firstRun=true;
+}
+
+void cSmartCards::Register(cSmartCardLink *scl)
+{
+  PRINTF(L_CORE_DYN,"registering %s smartcard (id %x)",scl->name,scl->id);
+  scl->next=first;
+  first=scl;
+}
+
+void cSmartCards::LaunchWatcher(void)
+{
+  for(int i=0 ; i<MAX_PORTS ; i++)
+    if(ports[i].Serial) { Start(); return; }
+  firstRun=false;
+  PRINTF(L_GEN_WARN,"no smartcard interface defined!");
+}
+
+void cSmartCards::Shutdown(void)
+{
+  Cancel(3);
+  mutex.Lock();
+  for(int i=0 ; i<MAX_PORTS ; i++) {
+    if(ports[i].Serial) {
+      delete ports[i].Card;
+      delete ports[i].Serial;
+      ports[i].Serial=0;
+      }
+    }
+  mutex.Unlock();
+  ListLock(true); Clear(); ListUnlock();
+}
+
+bool cSmartCards::AddPort(const char *devName, const char *devType, bool invCD, bool invRST, int clock)
+{
+  cMutexLock lock(&mutex);
+  int devtype=SM_PHOENIX;
+  if (devType && strcmp(devType,"smartreader")==0) {
+    devtype=SM_SMARTREADER;
+  }
+  for(int i=0 ; i<MAX_PORTS ; i++) {
+    if(!ports[i].Serial) {
+      cSerial *ser=new cSerial(devName,devtype,invCD,invRST);
+      if(ser && ser->Open()) {
+        ports[i].Card=0;
+        ports[i].CardId=0;
+        ports[i].UseCount=0;
+        ports[i].Dead=false;
+        ports[i].PortNum=i;
+        ports[i].Serial=ser;
+        ports[i].Clock=clock ? clock : ISO_FREQ;
+        PRINTF(L_CORE_LOAD,"smartcards: added serial port %s as port %d (%s CD, %s RESET, CLOCK %d)",
+                  devName,i,invCD?"inverse":"normal",invRST?"inverse":"normal",ports[i].Clock);
+        return true;
+        }
+      PRINTF(L_GEN_ERROR,"failed to open serial port %s",devName);
+      delete ser;
+      return false;
+      }
+    else if(!strcmp(ports[i].Serial->DeviceName(),devName)) {
+      PRINTF(L_GEN_ERROR,"duplicate serial port %s. Check your config!",devName);
+      return false;
+      }
+    }
+  PRINTF(L_GEN_ERROR,"only %d serial ports supported",MAX_PORTS);
+  return false;
+}
+
+cStructItem *cSmartCards::ParseLine(char *line)
+{
+  char *r=index(line,':');
+  if(r)
+    for(cSmartCardLink *scl=first; scl; scl=scl->next)
+      if(!strncasecmp(scl->name,line,r-line)) {
+        cSmartCardData *scd=scl->CreateData();
+        if(scd && scd->Parse(r+1)) return scd;
+        delete scd;
+        break;
+        }
+  return 0;
+}
+
+cSmartCardData *cSmartCards::FindCardData(cSmartCardData *param)
+{
+  ListLock(false);
+  cSmartCardData *cd;
+  for(cd=First(); cd; cd=Next(cd))
+    if(cd->ident==param->ident && cd->Matches(param)) break;
+  ListUnlock();
+  return cd;
+}
+
+bool cSmartCards::HaveCard(int id)
+{
+  cMutexLock lock(&mutex);
+  while(Running() && firstRun) cond.Wait(mutex);
+  for(int i=0 ; i<MAX_PORTS ; i++)
+    if(ports[i].Serial && ports[i].CardId==id) return true;
+  return false;
+}
+
+cSmartCard *cSmartCards::LockCard(int id)
+{
+  mutex.Lock();
+  while(Running() && firstRun) cond.Wait(mutex);
+  for(int i=0 ; i<MAX_PORTS ; i++) {
+    if(ports[i].Serial && ports[i].CardId==id) {
+      ports[i].UseCount++;
+      mutex.Unlock();
+      cSmartCard *sc=ports[i].Card;
+      sc->Lock();
+      if(CardInserted(ports[i].Serial) && sc->CardUp() && !sc->NeedsReset())
+        return sc;
+      // if failed, unlock the card and decrement UseCount
+      sc->Unlock();
+      mutex.Lock();
+      ports[i].UseCount--;
+      cond.Broadcast();
+      break;
+      }
+    }
+  mutex.Unlock();
+  return 0;
+}
+
+void cSmartCards::ReleaseCard(cSmartCard *sc)
+{
+  sc->Unlock();
+  mutex.Lock();
+  for(int i=0 ; i<MAX_PORTS ; i++) {
+    if(ports[i].Card==sc) {
+      ports[i].UseCount--;
+      cond.Broadcast();
+      mutex.Unlock();
+      return;
+      }
+    }
+  mutex.Unlock();
+  PRINTF(L_GEN_DEBUG,"internal: can't find port in cSmartCards::ReleaseCard()");
+}
+
+bool cSmartCards::CardInserted(cSerial *ser)
+{
+  return ser->CheckCAR();
+}
+
+bool cSmartCards::CardReset(struct Port *port)
+{
+  cSerial *ser=port->Serial;
+  int clock = port->Clock;
+
+  if(ser->DeviceType()==SM_SMARTREADER) {
+    // Configure SmartReader+ with initial settings, ref ISO 7816 3.1.
+    PRINTF(L_CORE_SC,"%d: resetting SmartReader+",port->PortNum);
+    if(clock==ISO_FREQ) {
+      // No fixed frequency specified; use ISO initial clock frequency.
+      clock=fstable[0];
+    }
+    if(!ser->ConfigSmartReader(Ftable[0],Dtable[1],clock,0,0,0)) {
+      return false;
+    }
+  }
+
+  if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,clock))
+    return false;
+  if(!DoPTS(port)) {
+    // reset card again and continue without PTS
+    if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,clock))
+      return false;
+    }
+  return true;
+}
+
+int cSmartCards::Reset(struct Port *port)
+{
+  cSerial *ser=port->Serial;
+  PRINTF(L_CORE_SC,"%d: reseting card (sermode %s)",port->PortNum,serModes[ser->CurrentMode()]);
+  ser->ToggleRTS();
+  cCondWait::SleepMs(100);
+  ser->ToggleRTS();
+  int r=ser->Read(port->Atr.atr,-MAX_ATR_LEN,800,2000);
+  port->Atr.atrLen=r;
+  if(r>0) LDUMP(L_CORE_SC,port->Atr.atr,r,"%d: <- ATR len=%d:",port->PortNum,r);
+  return r;
+}
+
+bool cSmartCards::DoPTS(struct Port *port)
+{
+  struct Atr *atr=&port->Atr;
+  cSerial *ser=port->Serial;
+  const int id=port->PortNum;
+
+  if(ser->DeviceType()==SM_SMARTREADER) {
+    // Configure SmartReader+ with work settings, ref ISO 7816 3.1.
+    int inv;
+    if((inv = atr->convention==SM_DIRECT ? 0 : 1)) {
+      atr->convention = SM_INDIRECT_CONVERTED;
+    }
+    // Use ISO work clock frequency unless a fixed frequency is specified.
+    int clock=port->Clock==ISO_FREQ ? atr->fs : port->Clock;
+    if(!ser->ConfigSmartReader(atr->F,atr->D,clock,atr->N,atr->T,inv)) {
+      return false;
+    }
+    PRINTF(L_CORE_SC,"%d: SmartReader+ mode: fs=%.4fMHz",id,clock/1000000.0);
+    return true;
+  }
+
+  if(atr->F!=372 || atr->D!=1.0) { // PTS required
+    int baud=(int)((float)port->Clock*atr->D/atr->F);
+    PRINTF(L_CORE_SC,"%d: PTS cycle: calculated baudrate %d",id,baud);
+
+    if(atr->Tspec<0) {
+      PRINTF(L_CORE_SC,"%d: negotiable mode",id);
+#ifdef NO_PTS_PROTO
+      PRINTF(L_CORE_SC,"%d: PTS disabled at compile time!!!",id);
+      return true;
+#else
+      unsigned char req[4], conf[16];
+      req[0]=0xFF;
+      req[1]=0x10 | atr->T;
+      req[2]=atr->TA1;
+      req[3]=XorSum(req,3);
+      LDUMP(L_CORE_SC,req,4,"%d: PTS request:",id);
+      if(ser->Write(req,4)!=4) {
+        PRINTF(L_CORE_SC,"%d: PTS request, write failed",id);
+        return false;
+        }
+      if(ser->Read(conf,4,100,100)!=4) {
+        PRINTF(L_CORE_SC,"%d: PTS request, echo readback failed",id);
+        return false;
+        }
+      int n=ser->Read(conf,-16,200,1000);
+      if(n>0) LDUMP(L_CORE_SC,conf,n,"%d: PTS confirm:",id);
+      if(n<1 || conf[0]!=0xFF || XorSum(conf,n)!=0) {
+        PRINTF(L_CORE_SC,"%d: PTS confirm, bad format",id);
+        return false;
+        }
+      if(n<4 || conf[1]!=req[1] || conf[2]!=req[2]) {
+        PRINTF(L_CORE_SC,"%d: PTS not confirmed",id);
+        return false;
+        }
+#endif // NO_PTS_PROTO
+      }
+    else
+      PRINTF(L_CORE_SC,"%d: specific mode Tspec=%d",id,atr->Tspec);
+
+    int mode=ser->CurrentMode();
+    if(atr->N==255) mode|=SM_1SB;
+    if(!ser->SetMode(mode,baud)) {
+      PRINTF(L_CORE_SC,"%d: setting baudrate %d failed",id,baud);
+      return false;
+      }
+    }
+ return true;
+}
+
+void cSmartCards::SetPort(struct Port *port, cSmartCard *sc, int id, bool dead)
+{
+  mutex.Lock();
+  while(port->UseCount) cond.Wait(mutex);
+  delete port->Card;
+  port->Card=sc;
+  port->CardId=id;
+  port->Dead=dead;
+  port->UseCount=0;
+  mutex.Unlock();
+}
+
+void cSmartCards::Action(void)
+{
+  while(Running()) {
+    for(int i=0 ; i<MAX_PORTS ;) {
+      struct Port *port=&ports[i];
+      cSerial *ser=port->Serial;
+      if(ser) {
+        if(port->Card) {
+          cSmartCard *sc=port->Card;
+          sc->Lock();
+          if(!CardInserted(ser)) {
+            sc->Unlock(); sc=0;
+            PRINTF(L_CORE_SC,"%d: card removed (UseCount=%d)",port->PortNum,port->UseCount);
+            SetPort(port,0,0,false);
+            }
+          else if(sc->NeedsReset()) {
+            PRINTF(L_CORE_SC,"%d: card reset requested",port->PortNum);
+            if(!ser->SetMode(ser->CurrentMode()&SM_MASK)
+                || !CardReset(port)
+                || !sc->Setup(ser,&port->Atr,port->PortNum)) {
+              sc->Unlock(); sc=0;
+              PRINTF(L_CORE_SC,"%d: card re-init failed",port->PortNum);
+              SetPort(port,0,0,true);
+              }
+            }
+          if(sc) sc->Unlock();
+          }
+        else if(CardInserted(ser)) {
+          if(!port->Dead) {
+            PRINTF(L_CORE_SC,"%d: new card inserted",port->PortNum);
+            for(int mode=SM_NONE+1 ; mode<SM_MAX ; mode++) {
+              if(ser->SetMode(mode)) {
+                if(CardReset(port)) {
+                  cSmartCardLink *scl=first;
+                  while(scl) {
+                    PRINTF(L_CORE_SC,"%d: checking for %s card",port->PortNum,scl->name);
+                    cSmartCard *sc=scl->Create();
+                    if(sc && sc->Setup(ser,&port->Atr,port->PortNum)) {
+                      SetPort(port,sc,scl->id,false);
+                      goto next; // ugly, any better solution?
+                      }
+                    delete sc;
+                    scl=scl->next;
+                    }
+                  PRINTF(L_CORE_SC,"%d: no card handler found",port->PortNum);
+                  }
+                else PRINTF(L_CORE_SC,"%d: reset/atr error",port->PortNum);
+                }
+              else PRINTF(L_CORE_SC,"%d: failed to set serial mode %s",port->PortNum,serModes[mode]);
+              }
+            port->Dead=true;
+            PRINTF(L_CORE_SC,"%d: can't initialise new card, ignoring port until card reinserted",port->PortNum);
+            }
+          }
+        else {
+          if(port->Dead) PRINTF(L_CORE_SC,"%d: card removed, port reactivated",port->PortNum);
+          port->Dead=false;
+          }
+        }
+next: i++;
+      }
+    if(firstRun) {
+      mutex.Lock();
+      cond.Broadcast();
+      firstRun=false;
+      mutex.Unlock();
+      }
+    cCondWait::SleepMs(300);
+    }  
+}
+
+bool cSmartCards::ListCard(int num, char *str, int len)
+{
+  if(num>=MAX_PORTS || !ports[num].Serial) return false;
+  str[0]=0;
+  mutex.Lock();
+  cSmartCard *sc=ports[num].Card;
+  if(sc) {
+    if(!sc->GetCardIdStr(str,len)) {
+      cSmartCardLink *scl=first;
+      while(scl) {
+        if(scl->id==ports[num].CardId) {
+          strn0cpy(str,scl->name,len);
+          break;
+          }
+        scl=scl->next;
+        }
+      }
+    }
+  mutex.Unlock();
+  return true;
+}
+
+bool cSmartCards::CardInfo(int num, char *str, int len)
+{
+  bool res=false;
+  if(num<MAX_PORTS && ports[num].Serial) {
+    str[0]=0;
+    mutex.Lock();
+    cSmartCard *sc=ports[num].Card;
+    if(sc) res=sc->GetCardInfoStr(str,len);
+    mutex.Unlock();
+    }
+  return res;
+}
+
+void cSmartCards::CardReset(int num)
+{
+  if(num<MAX_PORTS && ports[num].Serial) {
+    mutex.Lock();
+    cSmartCard *sc=ports[num].Card;
+    if(sc) sc->TriggerReset();
+    mutex.Unlock();
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/smartcard.h
new file mode 100644 (file)
index 0000000..0b1a138
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SMARTCARD_H
+#define ___SMARTCARD_H
+
+#include <vdr/thread.h>
+#include "data.h"
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define MAX_LEN     256        // max. response length
+#define CMD_LEN     5          // command length
+#define INS_IDX     1          // INS index
+#define LEN_IDX     4          // LEN index
+
+#define SB_LEN      2          // status byte (SB) len
+#define MAX_ATR_LEN 33         // max. ATR length
+#define MAX_HIST    15         // max. number of historical characters
+#define IDSTR_LEN   25         // lenght of card identify string
+
+#define MAKE_SC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+
+// ----------------------------------------------------------------
+
+class cSerial;
+class cSmartCards;
+
+// ----------------------------------------------------------------
+
+class cInfoStr : public cLineBuff {
+private:
+  cMutex mutex;
+  char *current;
+public:
+  cInfoStr(void);
+  ~cInfoStr();
+  // Query API
+  bool Get(char *buff, int len);
+  // Construct API
+  void Begin(void);
+  void Finish();
+  };
+
+// ----------------------------------------------------------------
+
+#define SM_NONE 0
+#define SM_8E2  1
+#define SM_8O2  2
+#define SM_8N2  3
+#define SM_MAX  4
+#define SM_MASK 0x1F
+#define SM_1SB  0x80
+
+#define SM_DIRECT   0
+#define SM_INDIRECT 1
+#define SM_INDIRECT_CONVERTED 2
+
+#define SM_PHOENIX 0
+#define SM_SMARTREADER 1
+
+struct CardConfig {
+  int SerMode;
+  int workTO, serTO; // values in ms
+  };
+
+struct StatusMsg {
+  unsigned char sb[SB_LEN];
+  const char *message; 
+  bool retval;
+  };
+
+struct Atr {
+  int T, F, fs, N, WI, BWI, CWI, TA1, Tspec;
+  float D;
+  int wwt, bwt;
+  int atrLen, histLen;
+  int convention;
+  unsigned char atr[MAX_ATR_LEN];
+  unsigned char hist[MAX_HIST];
+  };
+
+class cSmartCard : protected cMutex {
+friend class cSmartCards;
+private:
+  cSerial *ser;
+  const struct CardConfig *cfg;
+  const struct StatusMsg *msg;
+  bool cardUp, needsReset;
+  int id;
+  //
+  int Procedure(unsigned char ins, int restLen);
+  int Read(unsigned char *data, int len, int to=0);
+  int Write(const unsigned char *data, int len);
+protected:
+  const struct Atr *atr;
+  unsigned char sb[SB_LEN];
+  char idStr[IDSTR_LEN];
+  cInfoStr infoStr;
+  //
+  int SerRead(unsigned char *data, int len, int to=0);
+  int SerWrite(const unsigned char *data, int len);
+  bool IsoRead(const unsigned char *cmd, unsigned char *data);
+  bool IsoWrite(const unsigned char *cmd, const unsigned char *data);
+  bool Status(void);
+  bool Test(bool res);
+  int CheckSctLen(const unsigned char *data, int off);
+  void TriggerReset(void) { needsReset=true; }
+  static void Invert(unsigned char *data, int n);
+public:
+  cSmartCard(const struct CardConfig *Cfg, const struct StatusMsg *Msg);
+  virtual ~cSmartCard() {};
+  virtual bool Init(void)=0;
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)=0;
+  virtual bool Update(int pid, int caid, const unsigned char *data) { return false; }
+  virtual bool CanHandle(unsigned short CaId) { return true; }
+  //
+  bool Setup(cSerial *Ser, const struct Atr *Atr, int Id);
+  static bool ParseAtr(struct Atr *atr, int id, int clock);
+  bool CardUp(void) { return cardUp; }
+  bool NeedsReset(void) { return needsReset; }
+  bool GetCardIdStr(char *str, int len);
+  bool GetCardInfoStr(char *str, int len);
+  };
+
+// ----------------------------------------------------------------
+
+class cSmartCardData : public cStructItem {
+friend class cSmartCards;
+protected:
+  int ident;
+public:
+  cSmartCardData(int Ident);
+  virtual ~cSmartCardData() {}
+  virtual bool Parse(const char *line)=0;
+  virtual bool Matches(cSmartCardData *cmp)=0;
+  };
+
+// ----------------------------------------------------------------
+
+class cSmartCardLink {
+friend class cSmartCards;
+private:
+  cSmartCardLink *next;
+  const char *name;
+  int id;
+public:
+  cSmartCardLink(const char *Name, int Id);
+  virtual ~cSmartCardLink() {}
+  virtual cSmartCard *Create(void)=0;
+  virtual cSmartCardData *CreateData(void) { return 0; }
+  int ID(void) { return id; }
+  };
+
+// ----------------------------------------------------------------
+
+#define MAX_PORTS 4
+
+struct Port {
+  cSerial *Serial;
+  bool Dead;
+  cSmartCard *Card;
+  int CardId, UseCount, PortNum, Clock;
+  struct Atr Atr;
+  };
+
+class cSmartCards : private cThread, public cStructList<cSmartCardData> {
+friend class cSmartCardLink;
+private:
+  static cSmartCardLink *first;
+  cMutex mutex;
+  cCondVar cond;
+  struct Port ports[MAX_PORTS];
+  bool firstRun;
+  //
+  static void Register(cSmartCardLink *scl);
+  bool CardInserted(cSerial *ser);
+  bool CardReset(struct Port *port);
+  int Reset(struct Port *port);
+  bool DoPTS(struct Port *port);
+  void SetPort(struct Port *port, cSmartCard *sc, int id, bool dead);
+protected:
+  virtual void Action(void);
+  virtual cStructItem *ParseLine(char *line);
+public:
+  cSmartCards(void);
+  void Shutdown(void);
+  // to be called ONLY from a system class!
+  bool HaveCard(int id);
+  cSmartCard *LockCard(int id);
+  void ReleaseCard(cSmartCard *sc);
+  cSmartCardData *FindCardData(cSmartCardData *param);
+  // to be called ONLY from frontend thread!
+  bool AddPort(const char *devName, const char *devType, bool invCD, bool invRST, int clock);
+  void LaunchWatcher(void);
+  bool ListCard(int num, char *str, int len);
+  bool CardInfo(int num, char *str, int len);
+  void CardReset(int num);
+  };
+
+extern cSmartCards smartcards;
+
+#endif //___SMARTCARD_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes.h
new file mode 100644 (file)
index 0000000..a6d90ec
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef HEADER_AES_H\r
+#define HEADER_AES_H\r
+\r
+/*\r
+#ifdef OPENSSL_NO_AES\r
+#error AES is disabled.\r
+#endif\r
+*/\r
+\r
+#define AES_ENCRYPT    1\r
+#define AES_DECRYPT    0\r
+\r
+/* Because array size can't be a const in C, the following two are macros.\r
+   Both sizes are in bytes. */\r
+#define AES_MAXNR 14\r
+#define AES_BLOCK_SIZE 16\r
+\r
+#ifdef  __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#if defined(_MSC_VER) && !defined(OPENSSL_SYS_WINCE)\r
+# define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)\r
+# define GETU32(p) SWAP(*((u32 *)(p)))\r
+# define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }\r
+#else\r
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))\r
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }\r
+#endif\r
+\r
+typedef unsigned long u32;\r
+typedef unsigned short u16;\r
+typedef unsigned char u8;\r
+\r
+#define MAXKC   (256/32)\r
+#define MAXKB   (256/8)\r
+#define MAXNR   14\r
+\r
+/* This controls loop-unrolling in aes_core.c */\r
+#undef FULL_UNROLL\r
+\r
+/* This should be a hidden type, but EVP requires that the size be known */\r
+struct aes_key_st {\r
+    unsigned long rd_key[4 *(AES_MAXNR + 1)];\r
+    int rounds;\r
+};\r
+typedef struct aes_key_st AES_KEY;\r
+\r
+const char *AES_options(void);\r
+\r
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,\r
+       AES_KEY *key);\r
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,\r
+       AES_KEY *key);\r
+\r
+void AES_encrypt(const unsigned char *in, unsigned char *out,\r
+       const AES_KEY *key);\r
+void AES_decrypt(const unsigned char *in, unsigned char *out,\r
+       const AES_KEY *key);\r
+\r
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,\r
+       const AES_KEY *key, const int enc);\r
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char *ivec, const int enc);\r
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char *ivec, int *num, const int enc);\r
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char *ivec, int *num);\r
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char ivec[AES_BLOCK_SIZE],\r
+       unsigned char ecount_buf[AES_BLOCK_SIZE],\r
+       unsigned int *num);\r
+\r
+\r
+#ifdef  __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* !HEADER_AES_H */\r
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes_core.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/aes_core.c
new file mode 100644 (file)
index 0000000..189eb90
--- /dev/null
@@ -0,0 +1,1425 @@
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "aes.h"\r
+\r
+static const u32 Te0[256] = {\r
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,\r
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,\r
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,\r
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,\r
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,\r
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,\r
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,\r
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,\r
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,\r
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,\r
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,\r
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,\r
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,\r
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,\r
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,\r
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,\r
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,\r
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,\r
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,\r
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,\r
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,\r
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,\r
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,\r
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,\r
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,\r
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,\r
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,\r
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,\r
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,\r
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,\r
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,\r
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,\r
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,\r
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,\r
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,\r
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,\r
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,\r
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,\r
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,\r
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,\r
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,\r
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,\r
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,\r
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,\r
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,\r
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,\r
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,\r
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,\r
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,\r
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,\r
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,\r
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,\r
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,\r
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,\r
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,\r
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,\r
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,\r
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,\r
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,\r
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,\r
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,\r
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,\r
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,\r
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,\r
+};\r
+static const u32 Te1[256] = {\r
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,\r
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,\r
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,\r
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,\r
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,\r
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,\r
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,\r
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,\r
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,\r
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,\r
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,\r
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,\r
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,\r
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,\r
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,\r
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,\r
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,\r
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,\r
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,\r
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,\r
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,\r
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,\r
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,\r
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,\r
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,\r
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,\r
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,\r
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,\r
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,\r
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,\r
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,\r
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,\r
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,\r
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,\r
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,\r
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,\r
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,\r
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,\r
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,\r
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,\r
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,\r
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,\r
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,\r
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,\r
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,\r
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,\r
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,\r
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,\r
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,\r
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,\r
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,\r
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,\r
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,\r
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,\r
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,\r
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,\r
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,\r
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,\r
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,\r
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,\r
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,\r
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,\r
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,\r
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,\r
+};\r
+static const u32 Te2[256] = {\r
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,\r
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,\r
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,\r
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,\r
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,\r
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,\r
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,\r
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,\r
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,\r
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,\r
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,\r
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,\r
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,\r
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,\r
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,\r
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,\r
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,\r
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,\r
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,\r
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,\r
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,\r
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,\r
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,\r
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,\r
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,\r
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,\r
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,\r
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,\r
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,\r
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,\r
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,\r
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,\r
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,\r
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,\r
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,\r
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,\r
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,\r
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,\r
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,\r
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,\r
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,\r
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,\r
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,\r
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,\r
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,\r
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,\r
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,\r
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,\r
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,\r
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,\r
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,\r
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,\r
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,\r
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,\r
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,\r
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,\r
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,\r
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,\r
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,\r
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,\r
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,\r
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,\r
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,\r
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,\r
+};\r
+static const u32 Te3[256] = {\r
+\r
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,\r
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,\r
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,\r
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,\r
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,\r
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,\r
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,\r
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,\r
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,\r
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,\r
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,\r
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,\r
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,\r
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,\r
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,\r
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,\r
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,\r
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,\r
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,\r
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,\r
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,\r
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,\r
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,\r
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,\r
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,\r
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,\r
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,\r
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,\r
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,\r
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,\r
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,\r
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,\r
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,\r
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,\r
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,\r
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,\r
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,\r
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,\r
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,\r
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,\r
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,\r
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,\r
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,\r
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,\r
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,\r
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,\r
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,\r
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,\r
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,\r
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,\r
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,\r
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,\r
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,\r
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,\r
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,\r
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,\r
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,\r
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,\r
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,\r
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,\r
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,\r
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,\r
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,\r
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,\r
+};\r
+static const u32 Te4[256] = {\r
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,\r
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,\r
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,\r
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,\r
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,\r
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,\r
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,\r
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,\r
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,\r
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,\r
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,\r
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,\r
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,\r
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,\r
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,\r
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,\r
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,\r
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,\r
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,\r
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,\r
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,\r
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,\r
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,\r
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,\r
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,\r
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,\r
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,\r
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,\r
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,\r
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,\r
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,\r
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,\r
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,\r
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,\r
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,\r
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,\r
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,\r
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,\r
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,\r
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,\r
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,\r
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,\r
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,\r
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,\r
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,\r
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,\r
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,\r
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,\r
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,\r
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,\r
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,\r
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,\r
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,\r
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,\r
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,\r
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,\r
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,\r
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,\r
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,\r
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,\r
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,\r
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,\r
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,\r
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,\r
+};\r
+static const u32 Td0[256] = {\r
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,\r
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,\r
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,\r
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,\r
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,\r
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,\r
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,\r
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,\r
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,\r
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,\r
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,\r
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,\r
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,\r
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,\r
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,\r
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,\r
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,\r
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,\r
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,\r
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,\r
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,\r
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,\r
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,\r
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,\r
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,\r
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,\r
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,\r
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,\r
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,\r
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,\r
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,\r
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,\r
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,\r
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,\r
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,\r
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,\r
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,\r
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,\r
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,\r
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,\r
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,\r
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,\r
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,\r
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,\r
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,\r
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,\r
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,\r
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,\r
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,\r
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,\r
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,\r
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,\r
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,\r
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,\r
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,\r
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,\r
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,\r
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,\r
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,\r
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,\r
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,\r
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,\r
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,\r
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,\r
+};\r
+static const u32 Td1[256] = {\r
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,\r
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,\r
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,\r
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,\r
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,\r
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,\r
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,\r
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,\r
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,\r
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,\r
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,\r
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,\r
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,\r
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,\r
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,\r
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,\r
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,\r
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,\r
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,\r
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,\r
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,\r
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,\r
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,\r
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,\r
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,\r
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,\r
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,\r
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,\r
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,\r
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,\r
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,\r
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,\r
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,\r
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,\r
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,\r
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,\r
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,\r
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,\r
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,\r
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,\r
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,\r
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,\r
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,\r
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,\r
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,\r
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,\r
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,\r
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,\r
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,\r
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,\r
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,\r
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,\r
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,\r
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,\r
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,\r
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,\r
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,\r
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,\r
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,\r
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,\r
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,\r
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,\r
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,\r
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,\r
+};\r
+static const u32 Td2[256] = {\r
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,\r
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,\r
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,\r
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,\r
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,\r
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,\r
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,\r
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,\r
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,\r
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,\r
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,\r
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,\r
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,\r
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,\r
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,\r
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,\r
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,\r
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,\r
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,\r
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,\r
+\r
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,\r
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,\r
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,\r
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,\r
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,\r
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,\r
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,\r
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,\r
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,\r
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,\r
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,\r
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,\r
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,\r
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,\r
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,\r
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,\r
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,\r
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,\r
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,\r
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,\r
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,\r
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,\r
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,\r
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,\r
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,\r
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,\r
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,\r
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,\r
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,\r
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,\r
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,\r
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,\r
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,\r
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,\r
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,\r
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,\r
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,\r
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,\r
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,\r
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,\r
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,\r
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,\r
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,\r
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,\r
+};\r
+static const u32 Td3[256] = {\r
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,\r
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,\r
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,\r
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,\r
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,\r
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,\r
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,\r
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,\r
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,\r
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,\r
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,\r
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,\r
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,\r
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,\r
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,\r
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,\r
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,\r
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,\r
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,\r
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,\r
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,\r
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,\r
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,\r
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,\r
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,\r
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,\r
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,\r
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,\r
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,\r
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,\r
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,\r
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,\r
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,\r
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,\r
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,\r
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,\r
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,\r
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,\r
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,\r
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,\r
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,\r
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,\r
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,\r
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,\r
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,\r
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,\r
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,\r
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,\r
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,\r
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,\r
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,\r
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,\r
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,\r
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,\r
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,\r
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,\r
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,\r
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,\r
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,\r
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,\r
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,\r
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,\r
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,\r
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,\r
+};\r
+static const u32 Td4[256] = {\r
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,\r
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,\r
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,\r
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,\r
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,\r
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,\r
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,\r
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,\r
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,\r
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,\r
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,\r
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,\r
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,\r
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,\r
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,\r
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,\r
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,\r
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,\r
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,\r
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,\r
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,\r
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,\r
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,\r
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,\r
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,\r
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,\r
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,\r
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,\r
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,\r
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,\r
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,\r
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,\r
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,\r
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,\r
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,\r
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,\r
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,\r
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,\r
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,\r
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,\r
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,\r
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,\r
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,\r
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,\r
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,\r
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,\r
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,\r
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,\r
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,\r
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,\r
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,\r
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,\r
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,\r
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,\r
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,\r
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,\r
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,\r
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,\r
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,\r
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,\r
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,\r
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,\r
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,\r
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,\r
+};\r
+static const u32 rcon[] = {\r
+       0x01000000, 0x02000000, 0x04000000, 0x08000000,\r
+       0x10000000, 0x20000000, 0x40000000, 0x80000000,\r
+       0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */\r
+};\r
+\r
+/**\r
+ * Expand the cipher key into the encryption key schedule.\r
+ */\r
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,\r
+                       AES_KEY *key) {\r
+\r
+       u32 *rk;\r
+       int i = 0;\r
+       u32 temp;\r
+\r
+       if (!userKey || !key)\r
+               return -1;\r
+       if (bits != 128 && bits != 192 && bits != 256)\r
+               return -2;\r
+\r
+       rk = key->rd_key;\r
+\r
+       if (bits==128)\r
+               key->rounds = 10;\r
+       else if (bits==192)\r
+               key->rounds = 12;\r
+       else\r
+               key->rounds = 14;\r
+\r
+       rk[0] = GETU32(userKey     );\r
+       rk[1] = GETU32(userKey +  4);\r
+       rk[2] = GETU32(userKey +  8);\r
+       rk[3] = GETU32(userKey + 12);\r
+       if (bits == 128) {\r
+               while (1) {\r
+                       temp  = rk[3];\r
+                       rk[4] = rk[0] ^\r
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^\r
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^\r
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^\r
+                               rcon[i];\r
+                       rk[5] = rk[1] ^ rk[4];\r
+                       rk[6] = rk[2] ^ rk[5];\r
+                       rk[7] = rk[3] ^ rk[6];\r
+                       if (++i == 10) {\r
+                               return 0;\r
+                       }\r
+                       rk += 4;\r
+               }\r
+       }\r
+       rk[4] = GETU32(userKey + 16);\r
+       rk[5] = GETU32(userKey + 20);\r
+       if (bits == 192) {\r
+               while (1) {\r
+                       temp = rk[ 5];\r
+                       rk[ 6] = rk[ 0] ^\r
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^\r
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^\r
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^\r
+                               rcon[i];\r
+                       rk[ 7] = rk[ 1] ^ rk[ 6];\r
+                       rk[ 8] = rk[ 2] ^ rk[ 7];\r
+                       rk[ 9] = rk[ 3] ^ rk[ 8];\r
+                       if (++i == 8) {\r
+                               return 0;\r
+                       }\r
+                       rk[10] = rk[ 4] ^ rk[ 9];\r
+                       rk[11] = rk[ 5] ^ rk[10];\r
+                       rk += 6;\r
+               }\r
+       }\r
+       rk[6] = GETU32(userKey + 24);\r
+       rk[7] = GETU32(userKey + 28);\r
+       if (bits == 256) {\r
+               while (1) {\r
+                       temp = rk[ 7];\r
+                       rk[ 8] = rk[ 0] ^\r
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^\r
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^\r
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^\r
+                               rcon[i];\r
+                       rk[ 9] = rk[ 1] ^ rk[ 8];\r
+                       rk[10] = rk[ 2] ^ rk[ 9];\r
+                       rk[11] = rk[ 3] ^ rk[10];\r
+                       if (++i == 7) {\r
+                               return 0;\r
+                       }\r
+                       temp = rk[11];\r
+                       rk[12] = rk[ 4] ^\r
+                               (Te4[(temp >> 24)       ] & 0xff000000) ^\r
+                               (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^\r
+                               (Te4[(temp >>  8) & 0xff] & 0x0000ff00) ^\r
+                               (Te4[(temp      ) & 0xff] & 0x000000ff);\r
+                       rk[13] = rk[ 5] ^ rk[12];\r
+                       rk[14] = rk[ 6] ^ rk[13];\r
+                       rk[15] = rk[ 7] ^ rk[14];\r
+\r
+                       rk += 8;\r
+               }\r
+       }\r
+       return 0;\r
+}\r
+\r
+/**\r
+ * Expand the cipher key into the decryption key schedule.\r
+ */\r
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,\r
+                        AES_KEY *key) {\r
+\r
+        u32 *rk;\r
+       int i, j, status;\r
+       u32 temp;\r
+\r
+       /* first, start with an encryption schedule */\r
+       status = AES_set_encrypt_key(userKey, bits, key);\r
+       if (status < 0)\r
+               return status;\r
+\r
+       rk = key->rd_key;\r
+\r
+       /* invert the order of the round keys: */\r
+       for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {\r
+               temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;\r
+               temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;\r
+               temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;\r
+               temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;\r
+       }\r
+       /* apply the inverse MixColumn transform to all round keys but the first and the last: */\r
+       for (i = 1; i < (key->rounds); i++) {\r
+               rk += 4;\r
+               rk[0] =\r
+                       Td0[Te4[(rk[0] >> 24)       ] & 0xff] ^\r
+                       Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^\r
+                       Td2[Te4[(rk[0] >>  8) & 0xff] & 0xff] ^\r
+                       Td3[Te4[(rk[0]      ) & 0xff] & 0xff];\r
+               rk[1] =\r
+                       Td0[Te4[(rk[1] >> 24)       ] & 0xff] ^\r
+                       Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^\r
+                       Td2[Te4[(rk[1] >>  8) & 0xff] & 0xff] ^\r
+                       Td3[Te4[(rk[1]      ) & 0xff] & 0xff];\r
+               rk[2] =\r
+                       Td0[Te4[(rk[2] >> 24)       ] & 0xff] ^\r
+                       Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^\r
+                       Td2[Te4[(rk[2] >>  8) & 0xff] & 0xff] ^\r
+                       Td3[Te4[(rk[2]      ) & 0xff] & 0xff];\r
+               rk[3] =\r
+                       Td0[Te4[(rk[3] >> 24)       ] & 0xff] ^\r
+                       Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^\r
+                       Td2[Te4[(rk[3] >>  8) & 0xff] & 0xff] ^\r
+                       Td3[Te4[(rk[3]      ) & 0xff] & 0xff];\r
+       }\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Encrypt a single block\r
+ * in and out can overlap\r
+ */\r
+void AES_encrypt(const unsigned char *in, unsigned char *out,\r
+                const AES_KEY *key) {\r
+\r
+       const u32 *rk;\r
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;\r
+#ifndef FULL_UNROLL\r
+       int r;\r
+#endif /* ?FULL_UNROLL */\r
+\r
+       assert(in && out && key);\r
+       rk = key->rd_key;\r
+\r
+       /*\r
+        * map byte array block to cipher state\r
+        * and add initial round key:\r
+        */\r
+       s0 = GETU32(in     ) ^ rk[0];\r
+       s1 = GETU32(in +  4) ^ rk[1];\r
+       s2 = GETU32(in +  8) ^ rk[2];\r
+       s3 = GETU32(in + 12) ^ rk[3];\r
+#ifdef FULL_UNROLL\r
+       /* round 1: */\r
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];\r
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];\r
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];\r
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];\r
+       /* round 2: */\r
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];\r
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];\r
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];\r
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];\r
+       /* round 3: */\r
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];\r
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];\r
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];\r
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];\r
+       /* round 4: */\r
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];\r
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];\r
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];\r
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];\r
+       /* round 5: */\r
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];\r
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];\r
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];\r
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];\r
+       /* round 6: */\r
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];\r
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];\r
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];\r
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];\r
+       /* round 7: */\r
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];\r
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];\r
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];\r
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];\r
+       /* round 8: */\r
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];\r
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];\r
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];\r
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];\r
+       /* round 9: */\r
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];\r
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];\r
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];\r
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];\r
+    if (key->rounds > 10) {\r
+        /* round 10: */\r
+        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];\r
+        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];\r
+        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];\r
+        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];\r
+        /* round 11: */\r
+        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];\r
+        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];\r
+        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];\r
+        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];\r
+        if (key->rounds > 12) {\r
+            /* round 12: */\r
+            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];\r
+            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];\r
+            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];\r
+            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];\r
+            /* round 13: */\r
+            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];\r
+            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];\r
+            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];\r
+            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];\r
+        }\r
+    }\r
+    rk += key->rounds << 2;\r
+#else  /* !FULL_UNROLL */\r
+    /*\r
+     * Nr - 1 full rounds:\r
+     */\r
+    r = key->rounds >> 1;\r
+    for (;;) {\r
+        t0 =\r
+            Te0[(s0 >> 24)       ] ^\r
+            Te1[(s1 >> 16) & 0xff] ^\r
+            Te2[(s2 >>  8) & 0xff] ^\r
+            Te3[(s3      ) & 0xff] ^\r
+            rk[4];\r
+        t1 =\r
+            Te0[(s1 >> 24)       ] ^\r
+            Te1[(s2 >> 16) & 0xff] ^\r
+            Te2[(s3 >>  8) & 0xff] ^\r
+            Te3[(s0      ) & 0xff] ^\r
+            rk[5];\r
+        t2 =\r
+            Te0[(s2 >> 24)       ] ^\r
+            Te1[(s3 >> 16) & 0xff] ^\r
+            Te2[(s0 >>  8) & 0xff] ^\r
+            Te3[(s1      ) & 0xff] ^\r
+            rk[6];\r
+        t3 =\r
+            Te0[(s3 >> 24)       ] ^\r
+            Te1[(s0 >> 16) & 0xff] ^\r
+            Te2[(s1 >>  8) & 0xff] ^\r
+            Te3[(s2      ) & 0xff] ^\r
+            rk[7];\r
+\r
+        rk += 8;\r
+        if (--r == 0) {\r
+            break;\r
+        }\r
+\r
+        s0 =\r
+            Te0[(t0 >> 24)       ] ^\r
+            Te1[(t1 >> 16) & 0xff] ^\r
+            Te2[(t2 >>  8) & 0xff] ^\r
+            Te3[(t3      ) & 0xff] ^\r
+            rk[0];\r
+        s1 =\r
+            Te0[(t1 >> 24)       ] ^\r
+            Te1[(t2 >> 16) & 0xff] ^\r
+            Te2[(t3 >>  8) & 0xff] ^\r
+            Te3[(t0      ) & 0xff] ^\r
+            rk[1];\r
+        s2 =\r
+            Te0[(t2 >> 24)       ] ^\r
+            Te1[(t3 >> 16) & 0xff] ^\r
+            Te2[(t0 >>  8) & 0xff] ^\r
+            Te3[(t1      ) & 0xff] ^\r
+            rk[2];\r
+        s3 =\r
+            Te0[(t3 >> 24)       ] ^\r
+            Te1[(t0 >> 16) & 0xff] ^\r
+            Te2[(t1 >>  8) & 0xff] ^\r
+            Te3[(t2      ) & 0xff] ^\r
+            rk[3];\r
+    }\r
+#endif /* ?FULL_UNROLL */\r
+    /*\r
+        * apply last round and\r
+        * map cipher state to byte array block:\r
+        */\r
+       s0 =\r
+               (Te4[(t0 >> 24)       ] & 0xff000000) ^\r
+               (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Te4[(t2 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Te4[(t3      ) & 0xff] & 0x000000ff) ^\r
+               rk[0];\r
+       PUTU32(out     , s0);\r
+       s1 =\r
+               (Te4[(t1 >> 24)       ] & 0xff000000) ^\r
+               (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Te4[(t3 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Te4[(t0      ) & 0xff] & 0x000000ff) ^\r
+               rk[1];\r
+       PUTU32(out +  4, s1);\r
+       s2 =\r
+               (Te4[(t2 >> 24)       ] & 0xff000000) ^\r
+               (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Te4[(t0 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Te4[(t1      ) & 0xff] & 0x000000ff) ^\r
+               rk[2];\r
+       PUTU32(out +  8, s2);\r
+       s3 =\r
+               (Te4[(t3 >> 24)       ] & 0xff000000) ^\r
+               (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Te4[(t1 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Te4[(t2      ) & 0xff] & 0x000000ff) ^\r
+               rk[3];\r
+       PUTU32(out + 12, s3);\r
+}\r
+\r
+/*\r
+ * Decrypt a single block\r
+ * in and out can overlap\r
+ */\r
+void AES_decrypt(const unsigned char *in, unsigned char *out,\r
+                const AES_KEY *key) {\r
+\r
+       const u32 *rk;\r
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;\r
+#ifndef FULL_UNROLL\r
+       int r;\r
+#endif /* ?FULL_UNROLL */\r
+\r
+       assert(in && out && key);\r
+       rk = key->rd_key;\r
+\r
+       /*\r
+        * map byte array block to cipher state\r
+        * and add initial round key:\r
+        */\r
+    s0 = GETU32(in     ) ^ rk[0];\r
+    s1 = GETU32(in +  4) ^ rk[1];\r
+    s2 = GETU32(in +  8) ^ rk[2];\r
+    s3 = GETU32(in + 12) ^ rk[3];\r
+#ifdef FULL_UNROLL\r
+    /* round 1: */\r
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];\r
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];\r
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];\r
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];\r
+    /* round 2: */\r
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];\r
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];\r
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];\r
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];\r
+    /* round 3: */\r
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];\r
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];\r
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];\r
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];\r
+    /* round 4: */\r
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];\r
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];\r
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];\r
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];\r
+    /* round 5: */\r
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];\r
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];\r
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];\r
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];\r
+    /* round 6: */\r
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];\r
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];\r
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];\r
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];\r
+    /* round 7: */\r
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];\r
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];\r
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];\r
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];\r
+    /* round 8: */\r
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];\r
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];\r
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];\r
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];\r
+    /* round 9: */\r
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];\r
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];\r
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];\r
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];\r
+    if (key->rounds > 10) {\r
+        /* round 10: */\r
+        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];\r
+        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];\r
+        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];\r
+        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];\r
+        /* round 11: */\r
+        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];\r
+        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];\r
+        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];\r
+        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];\r
+        if (key->rounds > 12) {\r
+            /* round 12: */\r
+            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];\r
+            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];\r
+            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];\r
+            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];\r
+            /* round 13: */\r
+            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];\r
+            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];\r
+            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];\r
+            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];\r
+        }\r
+    }\r
+       rk += key->rounds << 2;\r
+#else  /* !FULL_UNROLL */\r
+    /*\r
+     * Nr - 1 full rounds:\r
+     */\r
+    r = key->rounds >> 1;\r
+    for (;;) {\r
+        t0 =\r
+            Td0[(s0 >> 24)       ] ^\r
+            Td1[(s3 >> 16) & 0xff] ^\r
+            Td2[(s2 >>  8) & 0xff] ^\r
+            Td3[(s1      ) & 0xff] ^\r
+            rk[4];\r
+        t1 =\r
+            Td0[(s1 >> 24)       ] ^\r
+            Td1[(s0 >> 16) & 0xff] ^\r
+            Td2[(s3 >>  8) & 0xff] ^\r
+            Td3[(s2      ) & 0xff] ^\r
+            rk[5];\r
+        t2 =\r
+            Td0[(s2 >> 24)       ] ^\r
+            Td1[(s1 >> 16) & 0xff] ^\r
+            Td2[(s0 >>  8) & 0xff] ^\r
+            Td3[(s3      ) & 0xff] ^\r
+            rk[6];\r
+        t3 =\r
+            Td0[(s3 >> 24)       ] ^\r
+            Td1[(s2 >> 16) & 0xff] ^\r
+            Td2[(s1 >>  8) & 0xff] ^\r
+            Td3[(s0      ) & 0xff] ^\r
+            rk[7];\r
+\r
+        rk += 8;\r
+        if (--r == 0) {\r
+            break;\r
+        }\r
+\r
+        s0 =\r
+            Td0[(t0 >> 24)       ] ^\r
+            Td1[(t3 >> 16) & 0xff] ^\r
+            Td2[(t2 >>  8) & 0xff] ^\r
+            Td3[(t1      ) & 0xff] ^\r
+            rk[0];\r
+        s1 =\r
+            Td0[(t1 >> 24)       ] ^\r
+            Td1[(t0 >> 16) & 0xff] ^\r
+            Td2[(t3 >>  8) & 0xff] ^\r
+            Td3[(t2      ) & 0xff] ^\r
+            rk[1];\r
+        s2 =\r
+            Td0[(t2 >> 24)       ] ^\r
+            Td1[(t1 >> 16) & 0xff] ^\r
+            Td2[(t0 >>  8) & 0xff] ^\r
+            Td3[(t3      ) & 0xff] ^\r
+            rk[2];\r
+        s3 =\r
+            Td0[(t3 >> 24)       ] ^\r
+            Td1[(t2 >> 16) & 0xff] ^\r
+            Td2[(t1 >>  8) & 0xff] ^\r
+            Td3[(t0      ) & 0xff] ^\r
+            rk[3];\r
+    }\r
+#endif /* ?FULL_UNROLL */\r
+    /*\r
+        * apply last round and\r
+        * map cipher state to byte array block:\r
+        */\r
+       s0 =\r
+               (Td4[(t0 >> 24)       ] & 0xff000000) ^\r
+               (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Td4[(t2 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Td4[(t1      ) & 0xff] & 0x000000ff) ^\r
+               rk[0];\r
+       PUTU32(out     , s0);\r
+       s1 =\r
+               (Td4[(t1 >> 24)       ] & 0xff000000) ^\r
+               (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Td4[(t3 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Td4[(t2      ) & 0xff] & 0x000000ff) ^\r
+               rk[1];\r
+       PUTU32(out +  4, s1);\r
+       s2 =\r
+               (Td4[(t2 >> 24)       ] & 0xff000000) ^\r
+               (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Td4[(t0 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Td4[(t3      ) & 0xff] & 0x000000ff) ^\r
+               rk[2];\r
+       PUTU32(out +  8, s2);\r
+       s3 =\r
+               (Td4[(t3 >> 24)       ] & 0xff000000) ^\r
+               (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^\r
+               (Td4[(t1 >>  8) & 0xff] & 0x0000ff00) ^\r
+               (Td4[(t0      ) & 0xff] & 0x000000ff) ^\r
+               rk[3];\r
+       PUTU32(out + 12, s3);\r
+}\r
+\r
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\r
+                    const unsigned long length, const AES_KEY *key,\r
+                    unsigned char *ivec, const int enc) {\r
+\r
+       unsigned long n;\r
+       unsigned long len = length;\r
+       unsigned char tmp[AES_BLOCK_SIZE];\r
+\r
+       assert(in && out && key && ivec);\r
+       assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));\r
+\r
+       if (AES_ENCRYPT == enc) {\r
+               while (len >= AES_BLOCK_SIZE) {\r
+                       for(n=0; n < AES_BLOCK_SIZE; ++n)\r
+                               tmp[n] = in[n] ^ ivec[n];\r
+                       AES_encrypt(tmp, out, key);\r
+                       memcpy(ivec, out, AES_BLOCK_SIZE);\r
+                       len -= AES_BLOCK_SIZE;\r
+                       in += AES_BLOCK_SIZE;\r
+                       out += AES_BLOCK_SIZE;\r
+               }\r
+               if (len) {\r
+                       for(n=0; n < len; ++n)\r
+                               tmp[n] = in[n] ^ ivec[n];\r
+                       for(n=len; n < AES_BLOCK_SIZE; ++n)\r
+                               tmp[n] = ivec[n];\r
+                       AES_encrypt(tmp, tmp, key);\r
+                       memcpy(out, tmp, AES_BLOCK_SIZE);\r
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+               }                       \r
+       } else {\r
+               while (len >= AES_BLOCK_SIZE) {\r
+                       memcpy(tmp, in, AES_BLOCK_SIZE);\r
+                       AES_decrypt(in, out, key);\r
+                       for(n=0; n < AES_BLOCK_SIZE; ++n)\r
+                               out[n] ^= ivec[n];\r
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+                       len -= AES_BLOCK_SIZE;\r
+                       in += AES_BLOCK_SIZE;\r
+                       out += AES_BLOCK_SIZE;\r
+               }\r
+               if (len) {\r
+                       memcpy(tmp, in, AES_BLOCK_SIZE);\r
+                       AES_decrypt(tmp, tmp, key);\r
+                       for(n=0; n < len; ++n)\r
+                               out[n] ^= ivec[n];\r
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+               }                       \r
+       }\r
+}\r
+\r
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char *ivec, int *num, const int enc) {\r
+\r
+       unsigned int n;\r
+       unsigned long l = length;\r
+       unsigned char c;\r
+\r
+       assert(in && out && key && ivec && num);\r
+\r
+       n = *num;\r
+\r
+       if (enc) {\r
+               while (l--) {\r
+                       if (n == 0) {\r
+                               AES_encrypt(ivec, ivec, key);\r
+                       }\r
+                       ivec[n] = *(out++) = *(in++) ^ ivec[n];\r
+                       n = (n+1) % AES_BLOCK_SIZE;\r
+               }\r
+       } else {\r
+               while (l--) {\r
+                       if (n == 0) {\r
+                               AES_encrypt(ivec, ivec, key);\r
+                       }\r
+                       c = *(in);\r
+                       *(out++) = *(in++) ^ ivec[n];\r
+                       ivec[n] = c;\r
+                       n = (n+1) % AES_BLOCK_SIZE;\r
+               }\r
+       }\r
+\r
+       *num=n;\r
+}\r
+\r
+/* increment counter (128-bit int) by 1 */\r
+static void AES_ctr128_inc(unsigned char *counter) {\r
+       unsigned long c;\r
+\r
+       /* Grab bottom dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+       c = GETU32(counter +  0);\r
+       c++;\r
+       PUTU32(counter +  0, c);\r
+#else\r
+       c = GETU32(counter + 12);\r
+       c++;\r
+       PUTU32(counter + 12, c);\r
+#endif\r
+\r
+       /* if no overflow, we're done */\r
+       if (c)\r
+               return;\r
+\r
+       /* Grab 1st dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+       c = GETU32(counter +  4);\r
+       c++;\r
+       PUTU32(counter +  4, c);\r
+#else\r
+       c = GETU32(counter +  8);\r
+       c++;\r
+       PUTU32(counter +  8, c);\r
+#endif\r
+\r
+       /* if no overflow, we're done */\r
+       if (c)\r
+               return;\r
+\r
+       /* Grab 2nd dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+       c = GETU32(counter +  8);\r
+       c++;\r
+       PUTU32(counter +  8, c);\r
+#else\r
+       c = GETU32(counter +  4);\r
+       c++;\r
+       PUTU32(counter +  4, c);\r
+#endif\r
+\r
+       /* if no overflow, we're done */\r
+       if (c)\r
+               return;\r
+\r
+       /* Grab top dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+       c = GETU32(counter + 12);\r
+       c++;\r
+       PUTU32(counter + 12, c);\r
+#else\r
+       c = GETU32(counter +  0);\r
+       c++;\r
+       PUTU32(counter +  0, c);\r
+#endif\r
+\r
+}\r
+\r
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char ivec[AES_BLOCK_SIZE],\r
+       unsigned char ecount_buf[AES_BLOCK_SIZE],\r
+       unsigned int *num) {\r
+\r
+       unsigned int n;\r
+       unsigned long l=length;\r
+\r
+//     assert(in && out && key && counter && num);\r
+       assert(in && out && key && num);\r
+       assert(*num < AES_BLOCK_SIZE);\r
+\r
+       n = *num;\r
+\r
+       while (l--) {\r
+               if (n == 0) {\r
+                       AES_encrypt(ivec, ecount_buf, key);\r
+                       AES_ctr128_inc(ivec);\r
+               }\r
+               *(out++) = *(in++) ^ ecount_buf[n];\r
+               n = (n+1) % AES_BLOCK_SIZE;\r
+       }\r
+\r
+       *num=n;\r
+}\r
+\r
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,\r
+                    const AES_KEY *key, const int enc) {\r
+\r
+        assert(in && out && key);\r
+       assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));\r
+\r
+       if (AES_ENCRYPT == enc)\r
+               AES_encrypt(in, out, key);\r
+       else\r
+               AES_decrypt(in, out, key);\r
+}\r
+\r
+const char *AES_version="AES";\r
+\r
+const char *AES_options(void) {\r
+#ifdef FULL_UNROLL\r
+        return "aes(full)";\r
+#else   \r
+        return "aes(partial)";\r
+#endif\r
+}\r
+\r
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,\r
+       const unsigned long length, const AES_KEY *key,\r
+       unsigned char *ivec, int *num) {\r
+\r
+       unsigned int n;\r
+       unsigned long l=length;\r
+\r
+       assert(in && out && key && ivec && num);\r
+\r
+       n = *num;\r
+\r
+       while (l--) {\r
+               if (n == 0) {\r
+                       AES_encrypt(ivec, ivec, key);\r
+               }\r
+               *(out++) = *(in++) ^ ivec[n];\r
+               n = (n+1) % AES_BLOCK_SIZE;\r
+       }\r
+\r
+       *num=n;\r
+}\r
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_cbc.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_cbc.c
new file mode 100644 (file)
index 0000000..b964437
--- /dev/null
@@ -0,0 +1,168 @@
+/* crypto/idea/i_cbc.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include "idea.h"
+#include "idea_lcl.h"
+
+void idea_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+            IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int encrypt)
+       {
+       register unsigned long tin0,tin1;
+       register unsigned long tout0,tout1,xor0,xor1;
+       register long l=length;
+       unsigned long tin[2];
+
+       if (encrypt)
+               {
+               n2l(iv,tout0);
+               n2l(iv,tout1);
+               iv-=8;
+               for (l-=8; l>=0; l-=8)
+                       {
+                       n2l(in,tin0);
+                       n2l(in,tin1);
+                       tin0^=tout0;
+                       tin1^=tout1;
+                       tin[0]=tin0;
+                       tin[1]=tin1;
+                       idea_encrypt(tin,ks);
+                       tout0=tin[0]; l2n(tout0,out);
+                       tout1=tin[1]; l2n(tout1,out);
+                       }
+               if (l != -8)
+                       {
+                       n2ln(in,tin0,tin1,l+8);
+                       tin0^=tout0;
+                       tin1^=tout1;
+                       tin[0]=tin0;
+                       tin[1]=tin1;
+                       idea_encrypt(tin,ks);
+                       tout0=tin[0]; l2n(tout0,out);
+                       tout1=tin[1]; l2n(tout1,out);
+                       }
+               l2n(tout0,iv);
+               l2n(tout1,iv);
+               }
+       else
+               {
+               n2l(iv,xor0);
+               n2l(iv,xor1);
+               iv-=8;
+               for (l-=8; l>=0; l-=8)
+                       {
+                       n2l(in,tin0); tin[0]=tin0;
+                       n2l(in,tin1); tin[1]=tin1;
+                       idea_encrypt(tin,ks);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2n(tout0,out);
+                       l2n(tout1,out);
+                       xor0=tin0;
+                       xor1=tin1;
+                       }
+               if (l != -8)
+                       {
+                       n2l(in,tin0); tin[0]=tin0;
+                       n2l(in,tin1); tin[1]=tin1;
+                       idea_encrypt(tin,ks);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2nn(tout0,tout1,out,l+8);
+                       xor0=tin0;
+                       xor1=tin1;
+                       }
+               l2n(xor0,iv);
+               l2n(xor1,iv);
+               }
+       tin0=tin1=tout0=tout1=xor0=xor1=0;
+       tin[0]=tin[1]=0;
+       }
+
+void idea_encrypt(unsigned long *d, IDEA_KEY_SCHEDULE *key)
+       {
+       register IDEA_INT *p;
+       register unsigned long x1,x2,x3,x4,t0,t1,ul;
+
+       x2=d[0];
+       x1=(x2>>16);
+       x4=d[1];
+       x3=(x4>>16);
+
+       p= &(key->data[0][0]);
+
+       E_IDEA(0);
+       E_IDEA(1);
+       E_IDEA(2);
+       E_IDEA(3);
+       E_IDEA(4);
+       E_IDEA(5);
+       E_IDEA(6);
+       E_IDEA(7);
+
+       x1&=0xffff;
+       idea_mul(x1,x1,*p,ul); p++;
+
+       t0= x3+ *(p++);
+       t1= x2+ *(p++);
+
+       x4&=0xffff;
+       idea_mul(x4,x4,*p,ul);
+
+       d[0]=(t0&0xffff)|((x1&0xffff)<<16);
+       d[1]=(x4&0xffff)|((t1&0xffff)<<16);
+       }
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_skey.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/i_skey.c
new file mode 100644 (file)
index 0000000..feb9489
--- /dev/null
@@ -0,0 +1,156 @@
+/* crypto/idea/i_skey.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include "idea.h"
+#include "idea_lcl.h"
+
+static IDEA_INT inverse(unsigned int xin);
+void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks)
+       {
+       int i;
+       register IDEA_INT *kt,*kf,r0,r1,r2;
+
+       kt= &(ks->data[0][0]);
+       n2s(key,kt[0]); n2s(key,kt[1]); n2s(key,kt[2]); n2s(key,kt[3]);
+       n2s(key,kt[4]); n2s(key,kt[5]); n2s(key,kt[6]); n2s(key,kt[7]);
+
+       kf=kt;
+       kt+=8;
+       for (i=0; i<6; i++)
+               {
+               r2= kf[1];
+               r1= kf[2];
+               *(kt++)= ((r2<<9) | (r1>>7))&0xffff;
+               r0= kf[3];
+               *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+               r1= kf[4];
+               *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+               r0= kf[5];
+               *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+               r1= kf[6];
+               *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+               r0= kf[7];
+               *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+               r1= kf[0];
+               if (i >= 5) break;
+               *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+               *(kt++)= ((r1<<9) | (r2>>7))&0xffff;
+               kf+=8;
+               }
+       }
+
+void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk)
+       {
+       int r;
+       register IDEA_INT *fp,*tp,t;
+
+       tp= &(dk->data[0][0]);
+       fp= &(ek->data[8][0]);
+       for (r=0; r<9; r++)
+               {
+               *(tp++)=inverse(fp[0]);
+               *(tp++)=((int)(0x10000L-fp[2])&0xffff);
+               *(tp++)=((int)(0x10000L-fp[1])&0xffff);
+               *(tp++)=inverse(fp[3]);
+               if (r == 8) break;
+               fp-=6;
+               *(tp++)=fp[4];
+               *(tp++)=fp[5];
+               }
+
+       tp= &(dk->data[0][0]);
+       t=tp[1];
+       tp[1]=tp[2];
+       tp[2]=t;
+
+       t=tp[49];
+       tp[49]=tp[50];
+       tp[50]=t;
+       }
+
+/* taken directly from the 'paper' I'll have a look at it later */
+static IDEA_INT inverse(unsigned int xin)
+       {
+       long n1,n2,q,r,b1,b2,t;
+
+       if (xin == 0)
+               b2=0;
+       else
+               {
+               n1=0x10001;
+               n2=xin;
+               b2=1;
+               b1=0;
+
+               do      {
+                       r=(n1%n2);
+                       q=(n1-r)/n2;
+                       if (r == 0)
+                               { if (b2 < 0) b2=0x10001+b2; }
+                       else
+                               {
+                               n1=n2;
+                               n2=r;
+                               t=b2;
+                               b2=b1-q*b2;
+                               b1=t;
+                               }
+                       } while (r != 0);
+               }
+       return((IDEA_INT)b2);
+       }
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea.h
new file mode 100644 (file)
index 0000000..7072e81
--- /dev/null
@@ -0,0 +1,103 @@
+/* crypto/idea/idea.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_IDEA_H
+#define HEADER_IDEA_H
+
+/*#include <openssl/opensslconf.h>*/ /* IDEA_INT, OPENSSL_NO_IDEA */
+#define IDEA_INT unsigned int
+
+/*
+#ifdef OPENSSL_NO_IDEA
+#error IDEA is disabled.
+#endif
+*/
+
+#define IDEA_ENCRYPT   1
+#define IDEA_DECRYPT   0
+
+#define IDEA_BLOCK     8
+#define IDEA_KEY_LENGTH        16
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+typedef struct idea_key_st
+       {
+       IDEA_INT data[9][6];
+       } IDEA_KEY_SCHEDULE;
+
+const char *idea_options(void);
+void idea_ecb_encrypt(const unsigned char *in, unsigned char *out,
+       IDEA_KEY_SCHEDULE *ks);
+void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks);
+void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk);
+void idea_cbc_encrypt(const unsigned char *in, unsigned char *out,
+       long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,int enc);
+void idea_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+       long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,
+       int *num,int enc);
+void idea_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+       long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int *num);
+void idea_encrypt(unsigned long *in, IDEA_KEY_SCHEDULE *ks);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea_lcl.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/support/idea_lcl.h
new file mode 100644 (file)
index 0000000..463aa36
--- /dev/null
@@ -0,0 +1,215 @@
+/* crypto/idea/idea_lcl.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/* The new form of this macro (check if the a*b == 0) was suggested by 
+ * Colin Plumb <colin@nyx10.cs.du.edu> */
+/* Removal of the inner if from from Wei Dai 24/4/96 */
+#define idea_mul(r,a,b,ul) \
+ul=(unsigned long)a*b; \
+if (ul != 0) \
+       { \
+       r=(ul&0xffff)-(ul>>16); \
+       r-=((r)>>16); \
+       } \
+else \
+       r=(-(int)a-b+1); /* assuming a or b is 0 and in range */ \
+
+#ifdef undef
+#define idea_mul(r,a,b,ul,sl) \
+if (a == 0) r=(0x10001-b)&0xffff; \
+else if (b == 0) r=(0x10001-a)&0xffff; \
+else   { \
+       ul=(unsigned long)a*b; \
+       sl=(ul&0xffff)-(ul>>16); \
+       if (sl <= 0) sl+=0x10001; \
+       r=sl; \
+       } 
+#endif
+
+/*  7/12/95 - Many thanks to Rhys Weatherley <rweather@us.oracle.com>
+ * for pointing out that I was assuming little endian
+ * byte order for all quantities what idea
+ * actually used bigendian.  No where in the spec does it mention
+ * this, it is all in terms of 16 bit numbers and even the example
+ * does not use byte streams for the input example :-(.
+ * If you byte swap each pair of input, keys and iv, the functions
+ * would produce the output as the old version :-(.
+ */
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n)        { \
+                       c+=n; \
+                       l1=l2=0; \
+                       switch (n) { \
+                       case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                       case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                       case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                       case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                       case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                       case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                       case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                       case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                               } \
+                       }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n)        { \
+                       c+=n; \
+                       switch (n) { \
+                       case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                       case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                       case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                       case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                       case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                       case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                       case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                       case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                               } \
+                       }
+
+#undef n2l
+#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+#undef s2n
+#define s2n(l,c)       (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                        *((c)++)=(unsigned char)(((l)>> 8L)&0xff))
+
+#undef n2s
+#define n2s(c,l)       (l =((IDEA_INT)(*((c)++)))<< 8L, \
+                        l|=((IDEA_INT)(*((c)++)))      )
+
+#ifdef undef
+/* NOTE - c is not incremented as per c2l */
+#define c2ln(c,l1,l2,n)        { \
+                       c+=n; \
+                       l1=l2=0; \
+                       switch (n) { \
+                       case 8: l2 =((unsigned long)(*(--(c))))<<24; \
+                       case 7: l2|=((unsigned long)(*(--(c))))<<16; \
+                       case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
+                       case 5: l2|=((unsigned long)(*(--(c))));     \
+                       case 4: l1 =((unsigned long)(*(--(c))))<<24; \
+                       case 3: l1|=((unsigned long)(*(--(c))))<<16; \
+                       case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
+                       case 1: l1|=((unsigned long)(*(--(c))));     \
+                               } \
+                       }
+
+/* NOTE - c is not incremented as per l2c */
+#define l2cn(l1,l2,c,n)        { \
+                       c+=n; \
+                       switch (n) { \
+                       case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                       case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                       case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                       case 5: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                       case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                       case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                       case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                       case 1: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                               } \
+                       }
+
+#undef c2s
+#define c2s(c,l)       (l =((unsigned long)(*((c)++)))    , \
+                        l|=((unsigned long)(*((c)++)))<< 8L)
+
+#undef s2c
+#define s2c(l,c)       (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                        *((c)++)=(unsigned char)(((l)>> 8L)&0xff))
+
+#undef c2l
+#define c2l(c,l)       (l =((unsigned long)(*((c)++)))     , \
+                        l|=((unsigned long)(*((c)++)))<< 8L, \
+                        l|=((unsigned long)(*((c)++)))<<16L, \
+                        l|=((unsigned long)(*((c)++)))<<24L)
+
+#undef l2c
+#define l2c(l,c)       (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                        *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                        *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                        *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+#endif
+
+#define E_IDEA(num) \
+       x1&=0xffff; \
+       idea_mul(x1,x1,*p,ul); p++; \
+       x2+= *(p++); \
+       x3+= *(p++); \
+       x4&=0xffff; \
+       idea_mul(x4,x4,*p,ul); p++; \
+       t0=(x1^x3)&0xffff; \
+       idea_mul(t0,t0,*p,ul); p++; \
+       t1=(t0+(x2^x4))&0xffff; \
+       idea_mul(t1,t1,*p,ul); p++; \
+       t0+=t1; \
+       x1^=t1; \
+       x4^=t0; \
+       ul=x2^t0; /* do the swap to x3 */ \
+       x2=x3^t1; \
+       x3=ul;
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.c
new file mode 100644 (file)
index 0000000..fdaec09
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#include <vdr/tools.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "crypto-bn.h"
+
+// -- cHexKey ------------------------------------------------------------------
+
+cHexKey::cHexKey(bool Super)
+:cPlainKey(Super)
+{
+  key=0;
+}
+
+cHexKey::~cHexKey()
+{
+  free(key);
+}
+
+bool cHexKey::SetKey(void *Key, int Keylen)
+{
+  keylen=Keylen;
+  free(key); key=MALLOC(unsigned char,keylen);
+  if(key) memcpy(key,Key,keylen);
+  return key!=0;
+}
+
+bool cHexKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+  return SetKey(Mem,Keylen);
+}
+
+bool cHexKey::Cmp(void *Key, int Keylen)
+{
+  return keylen==Keylen && memcmp(key,Key,keylen)==0;
+}
+
+bool cHexKey::Cmp(cPlainKey *k)
+{
+  cHexKey *hk=dynamic_cast<cHexKey *>(k); // downcast
+  return hk && Cmp(hk->key,hk->keylen);
+}
+
+void cHexKey::Get(void *mem)
+{
+  memcpy(mem,key,keylen);
+}
+
+cString cHexKey::Print(void)
+{
+  char *str=(char *)malloc(keylen*2+2);
+  if(str) HexStr(str,key,keylen);
+  return cString(str,true);
+}
+
+// -- cBNKey -------------------------------------------------------------------
+
+cBNKey::cBNKey(bool Super, bool Rotate)
+:cPlainKey(Super)
+{
+  key=new cBN; rotate=Rotate;
+}
+
+cBNKey::~cBNKey()
+{
+  delete key;
+}
+
+bool cBNKey::SetKey(void *Key ,int Keylen)
+{
+  keylen=Keylen;
+  return BN_copy(*key,(BIGNUM *)Key)!=0;
+}
+
+bool cBNKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+  keylen=Keylen;
+  return rotate ? key->GetLE(Mem,Keylen) : key->Get(Mem,Keylen);
+}
+
+bool cBNKey::Cmp(void *Key, int Keylen)
+{
+  return BN_cmp(*key,(BIGNUM *)Key)==0;
+}
+
+bool cBNKey::Cmp(cPlainKey *k)
+{
+  cBNKey *bk=dynamic_cast<cBNKey *>(k); // downcast
+  return bk && BN_cmp(*key,*bk->key)==0;
+}
+
+void cBNKey::Get(void *mem)
+{
+  BN_copy((BIGNUM *)mem,*key);
+}
+
+cString cBNKey::Print(void)
+{
+  unsigned char *mem=AUTOMEM(keylen);
+  if(rotate) key->PutLE(mem,keylen); else key->Put(mem,keylen);
+  char *str=(char *)malloc(keylen*2+2);
+  if(str) HexStr(str,mem,keylen);
+  return cString(str,true);
+}
+
+// -- cDualKey -----------------------------------------------------------------
+
+cDualKey::cDualKey(bool Super, bool Rotate)
+:cMutableKey(Super)
+{
+  rotate=Rotate;
+}
+
+cPlainKey *cDualKey::Alloc(void) const
+{
+  if(IsBNKey()) return new cBNKey(CanSupersede(),rotate);
+  else          return new cHexKey(CanSupersede());
+}
+
+// -- cPlainKeyStd ---------------------------------------------------------------
+
+#define PLAINLEN_STD 8
+
+cPlainKeyStd::cPlainKeyStd(bool Super)
+:cHexKey(Super)
+{}
+
+bool cPlainKeyStd::Parse(const char *line)
+{
+  unsigned char sid[3], skeynr, skey[PLAINLEN_STD];
+  int len;
+  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false)) &&
+     GetHex(line,&skeynr,1) && GetHex(line,skey,PLAINLEN_STD)) {
+    type=toupper(type); id=Bin2Int(sid,len); keynr=skeynr;
+    SetBinKey(skey,PLAINLEN_STD);
+    return true;
+    }
+  return false;
+}
+
+// -- cSystemScCore ------------------------------------------------------------
+
+cSystemScCore::cSystemScCore(const char *Name, int Pri, int ScId, const char *ScName)
+:cSystem(Name,Pri)
+{
+  scId=ScId; scName=ScName;
+}
+
+bool cSystemScCore::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(scId);
+  if(card) {
+    res=card->Decode(ecm,source,cw);
+    smartcards.ReleaseCard(card);
+    if(res) KeyOK(scName);
+    }
+  return res;
+}
+
+void cSystemScCore::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  cSmartCard *card=smartcards.LockCard(scId);
+  if(card) {
+    card->Update(pid,caid,buffer);
+    smartcards.ReleaseCard(card);
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system-common.h
new file mode 100644 (file)
index 0000000..3d85d61
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SYSTEM_COMMON_H
+#define ___SYSTEM_COMMON_H
+
+#include "data.h"
+#include "system.h"
+
+// ----------------------------------------------------------------
+
+class cHexKey : public cPlainKey {
+private:
+  int keylen;
+  unsigned char *key;
+protected:
+  virtual bool SetKey(void *Key, int Keylen);
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+  virtual cString Print(void);
+  virtual bool Cmp(void *Key, int Keylen);
+  virtual bool Cmp(cPlainKey *k);
+  virtual void Get(void *mem);
+  virtual int Size(void) { return keylen; }
+public:
+  cHexKey(bool Super);
+  virtual ~cHexKey();
+  virtual bool Parse(const char *line) { return false; }
+  };
+
+// ----------------------------------------------------------------
+
+class cBN;
+
+class cBNKey : public cPlainKey {
+private:
+  bool rotate;
+  int keylen;
+  cBN *key;
+protected:
+  virtual bool SetKey(void *Key, int Keylen);
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+  virtual cString Print(void);
+  virtual bool Cmp(void *Key, int Keylen);
+  virtual bool Cmp(cPlainKey *k);
+  virtual void Get(void *mem);
+  virtual int Size(void) { return keylen; }
+public:
+  cBNKey(bool Super, bool Rotate=false);
+  virtual ~cBNKey();
+  virtual bool Parse(const char *line) { return false; }
+  };
+
+// ----------------------------------------------------------------
+
+class cDualKey : public cMutableKey {
+private:
+  bool rotate;
+protected:
+  virtual bool IsBNKey(void) const=0;
+  virtual cPlainKey *Alloc(void) const;
+public:
+  cDualKey(bool Super, bool Rotate);
+  };
+
+// ----------------------------------------------------------------
+
+class cPlainKeyStd : public cHexKey {
+public:
+  cPlainKeyStd(bool Super);
+  virtual bool Parse(const char *line);
+  };
+
+// ----------------------------------------------------------------
+
+template<class T> class cCardInfos : public cStructList<T> {
+public:
+  cCardInfos(const char *Type, const char *Filename, int fl):cStructList<T>(Type,Filename,fl|SL_MISSINGOK|SL_WATCH) {}
+  virtual cStructItem *ParseLine(char *line)
+    {
+      T *k=new T;
+      if(k && !k->Parse(line)) { delete k; k=0; }
+      return k;
+    }
+  };
+
+// ----------------------------------------------------------------
+
+class cSystemScCore : public cSystem {
+private:
+  int scId;
+  const char *scName;
+public:
+  cSystemScCore(const char *Name, int Pri, int ScId, const char *ScName);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+  };
+
+#endif //___SYSTEM_COMMON_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.c
new file mode 100644 (file)
index 0000000..f1a36b7
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vdr/tools.h>
+
+#include "sc.h"
+#include "scsetup.h"
+#include "system.h"
+#include "data.h"
+#include "opts.h"
+#include "log-core.h"
+#include "i18n.h"
+
+// --- cFeature ----------------------------------------------------------------
+
+cFeature Feature;
+
+bool cFeature::keyfile=false;
+bool cFeature::smartcard=false;
+
+void cFeature::NeedsKeyFile(void)
+{
+  if(!keyfile) PRINTF(L_CORE_DYN,"feature: using feature KEYFILE");
+  keyfile=true;
+}
+
+void cFeature::NeedsSmartCard(void)
+{
+  if(!smartcard) PRINTF(L_CORE_DYN,"feature: using feature SMARTCARD");
+  smartcard=true;
+}
+
+// -- cKeySnoop ----------------------------------------------------------------
+
+cKeySnoop::cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr)
+{
+  ok=false;
+  sys=Sys; type=Type; id=Id; keynr=Keynr;
+}
+
+cKeySnoop::~cKeySnoop()
+{
+  if(!ok) sys->KeyFail(type,id,keynr);
+}
+
+void cKeySnoop::OK(cPlainKey *pk)
+{
+  sys->KeyOK(pk);
+  ok=true;
+}
+
+// -- cLogHook -----------------------------------------------------------------
+
+cLogHook::cLogHook(int Id, const char *Name)
+{
+  id=Id; name=Name;
+  bailOut=false;
+}
+
+// -- cSystem ------------------------------------------------------------------
+
+#define MAX_CHID 10
+
+struct EcmCheck {
+  union {
+    struct {
+      bool startFlag;
+      int current, chids[MAX_CHID];
+      } sys06;
+    } caid;
+  };
+
+int cSystem::foundKeys=0;
+int cSystem::newKeys=0;
+
+cSystem::cSystem(const char *Name, int Pri)
+{
+  name=Name; pri=Pri;
+  currentKeyStr[0]=0; doLog=true; cardNum=-1; logecm=0;
+  check=new struct EcmCheck;
+  memset(check,0,sizeof(struct EcmCheck));
+  // default config
+  maxEcmTry=2; // try to get a key X times from the same ECM pid (default)
+  hasLogger=false;
+  needsLogger=false;
+  local=true;
+  needsDescrData=false;
+  constant=false;
+}
+
+cSystem::~cSystem()
+{
+  delete check;
+  if(logecm) cSoftCAM::SetLogStatus(cardNum,logecm,false);
+  delete logecm;
+}
+
+void cSystem::StartLog(const cEcmInfo *ecm, int caid)
+{
+  if(!logecm || logecm->caId!=caid) {
+    if(logecm) cSoftCAM::SetLogStatus(cardNum,logecm,false);
+    delete logecm;
+    logecm=new cEcmInfo(ecm);
+    logecm->caId=caid; logecm->emmCaId=0;
+    cSoftCAM::SetLogStatus(cardNum,logecm,true);
+    }
+}
+
+void cSystem::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len)
+{
+  const int pid=WORD(data,2,0x1FFF);
+  switch(sysId>>8) {
+    case 0x01: // Seca style
+      for(int p=2; p<len; p+=15) {
+        cEcmInfo *n=new cEcmInfo(name,WORD(data,p,0x1FFF),sysId,WORD(data,p+2,0xFFFF));
+        if(data[p+4]==0xFF) n->AddData(&data[p+5],10);
+        ecms->Add(n);
+        }
+      break;
+    case 0x05: // Viaccess style
+      for(int p=4; p<len; p+=2+data[p+1])
+        if(data[p]==0x14)
+          ecms->Add(new cEcmInfo(name,pid,sysId,(data[p+2]<<16)|(data[p+3]<<8)|(data[p+4]&0xF0)));
+      break;
+    default:   // default style
+      {
+      cEcmInfo *n=new cEcmInfo(name,pid,sysId,0);
+      if((sysId==0x1234) || (source==0x8334) || (source==0x838e)) { // BEV
+        n->ecm_table=0x8e;
+        n->emmCaId=0x1801;
+        }
+      ecms->Add(n);
+      break;
+      }
+    }
+}
+
+void cSystem::ParseCAT(cPids *pids, const unsigned char *buffer)
+{
+  if(buffer[0]==0x09) {
+    int caid=WORD(buffer,2,0xFFFF);
+    int pid=WORD(buffer,4,0x1FFF);
+    switch(caid>>8) {
+      case 0x01: // Seca style (82/84)
+        if(buffer[1]>4) {
+          pids->AddPid(pid,0x82,0xFF); // Unique updates
+          for(int i=7, nn=buffer[6] ; nn ; nn--,i+=4)
+            pids->AddPid(WORD(buffer,i,0x1FFF),0x84,0xFF); // Shared updates
+          }
+        break;
+      case 0x05: // Viaccess style (88/8c/8d/8e)
+        pids->AddPid(pid,0x8B,0xFE,0x07); // mismatching 89/8f
+        break;
+      case 0x0d: // Cryptoworks style (82/84/86/88/89)
+        pids->AddPid(pid,0x80,0xFF,0x06);
+        pids->AddPid(pid,0x88,0xFE);
+        break;
+      case 0x18: // Nagra style, Nagra1(82) Nagra2(82/83) 
+        pids->AddPid(pid,0x82,caid==0x1801 ? 0xFE:0xFF);
+        break;
+      default:   // default style (82)
+        pids->AddPid(pid,0x82,0xFF);
+        break;
+      }
+    }
+}
+
+int cSystem::CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync)
+{
+  switch(ecm->caId>>8) {
+    case 0x06: // Irdeto
+      {
+      const int cur=data[4];
+      const int max=data[5];
+      const int chid=WORD(data,6,0xFFFF);
+      // if multiple channel id's, use current one only
+      if(sync && ecm->caId==0x0604 && check->caid.sys06.current>0 && chid!=check->caid.sys06.current) {
+        PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): chid %04x != current %04x",name,chid,check->caid.sys06.current);
+        return 2;
+        }
+      // search for fake channel id's on Stream
+      if(!sync && max>0 && max<MAX_CHID) {
+        if(cur==0) check->caid.sys06.startFlag=true;
+        if(check->caid.sys06.startFlag) {
+          if(cur<=max) check->caid.sys06.chids[cur]=chid;
+          if(cur==max) {
+            for(int i=0 ; i<max ; i++) {
+              if(check->caid.sys06.chids[i]==0) {
+                check->caid.sys06.startFlag=false;
+                PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): zero chid",name);
+                return 1;
+                }
+              for(int j=i+1 ; j<=max ; j++) {
+                if(check->caid.sys06.chids[i]==check->caid.sys06.chids[j]) {
+                  check->caid.sys06.startFlag=false;
+                  PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): duplicate chid %04x",name,check->caid.sys06.chids[i]);
+                  return 1;
+                  }
+                }
+              }
+            }
+          }
+        }
+      break;
+      }
+    }
+  return 0;
+}
+
+void cSystem::CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result)
+{
+  switch(ecm->caId>>8) {
+    case 0x06: // Irdeto
+      check->caid.sys06.current=result ? WORD(data,6,0xFFFF) : 0;
+      break;
+    }
+}
+
+void cSystem::KeyOK(cPlainKey *pk)
+{
+  if(lastkey.NotLast(pk->type,pk->id,pk->keynr)) {
+    strn0cpy(currentKeyStr,pk->ToString(false),sizeof(currentKeyStr));
+    PRINTF(L_CORE_ECM,"system: using key %s",*pk->ToString(true));
+    doLog=true;
+    }
+}
+
+void cSystem::KeyOK(const char *txt)
+{
+  snprintf(currentKeyStr,sizeof(currentKeyStr),"%s (%s)\n",txt?txt:name,tr("undisclosed key"));
+  doLog=true;
+}
+
+void cSystem::KeyFail(int type, int id, int keynr)
+{
+  keys.Trigger(type,id,keynr);
+  if(lastkey.NotLast(type,id,keynr) && doLog)
+    PRINTF(L_CORE_ECM,"system: no key found for %s",*keys.KeyString(type,id,keynr));
+}
+
+// -- cSystemLink --------------------------------------------------------------
+
+cSystemLink::cSystemLink(const char *Name, int Pri)
+{
+  name=Name; pri=Pri;
+  opts=0; noFF=false;
+  cSystems::Register(this);
+}
+
+cSystemLink::~cSystemLink()
+{
+  delete opts;
+}
+
+// -- cSystems -----------------------------------------------------------------
+
+cSystemLink *cSystems::first=0;
+int cSystems::nextSysIdent=0x1000;
+
+void cSystems::Register(cSystemLink *sysLink)
+{
+  PRINTF(L_CORE_DYN,"systems: registering CA system %s, pri %d, ident %04X",sysLink->name,sysLink->pri,nextSysIdent);
+  sysLink->next=first;
+  sysLink->sysIdent=nextSysIdent++;
+  first=sysLink;
+}
+
+int cSystems::Provides(const unsigned short *SysIds, bool ff)
+{
+  int n=0;
+  for(; *SysIds; SysIds++) {
+    if(ScSetup.Ignore(*SysIds)) continue;
+    cSystemLink *sl=first;
+    while(sl) {
+      if(sl->CanHandle(*SysIds)) {
+        if(sl->noFF && ff) return 0;
+        n++;
+        break;
+        }
+      sl=sl->next;
+      }
+    }
+  return n;
+}
+
+cSystemLink *cSystems::FindByName(const char *Name)
+{
+  cSystemLink *sl=first;
+  while(sl) {
+    if(!strcasecmp(sl->name,Name)) return sl;
+    sl=sl->next;
+    }
+  return 0;
+}
+
+cSystemLink *cSystems::FindById(unsigned short SysId, bool ff, int oldPri)
+{
+  // all pri's are negative!
+  // oldPri = 0 -> get highest pri system
+  // oldPri < 0 -> get highest pri system with pri<oldPri
+  // oldPri > 0 -> get lowest pri system
+
+  cSystemLink *sl=first, *csl=0;
+  while(sl) {
+    if((!ff || !sl->noFF) && sl->CanHandle(SysId)
+       && sl->pri<oldPri
+       && (!csl 
+           || (oldPri<=0 && sl->pri>csl->pri)
+           || (oldPri>0 && sl->pri<csl->pri)
+          )
+       ) csl=sl;
+    sl=sl->next;
+    }
+  return csl;
+}
+
+cSystemLink *cSystems::FindByIdent(int ident)
+{
+  cSystemLink *sl=first;
+  while(sl) {
+    if(sl->sysIdent==ident) return sl;
+    sl=sl->next;
+    }
+  return 0;
+}
+
+cSystem *cSystems::FindBySysName(unsigned short SysId, bool ff, const char *Name)
+{
+  cSystemLink *sl=FindByName(Name);
+  if(sl && (!ff || !sl->noFF) && !ScSetup.Ignore(SysId) && sl->CanHandle(SysId)) return sl->Create();
+  return 0;
+}
+
+cSystem *cSystems::FindBySysId(unsigned short SysId, bool ff, int oldPri)
+{
+  if(!ScSetup.Ignore(SysId)) {
+    cSystemLink *csl=FindById(SysId,ff,oldPri);
+    if(csl) return csl->Create();
+    }
+  return 0;
+}
+
+int cSystems::FindIdentBySysId(unsigned short SysId, bool ff, int &Pri)
+{
+  if(!ScSetup.Ignore(SysId)) {
+    cSystemLink *csl=FindById(SysId,ff,Pri);
+    if(csl) {
+      Pri=csl->pri;
+      return csl->sysIdent;
+      }
+    }
+  return 0;
+}
+
+cSystem *cSystems::FindBySysIdent(int ident)
+{
+  cSystemLink *csl=FindByIdent(ident);
+  return csl ? csl->Create() : 0;
+}
+
+bool cSystems::Init(const char *cfgdir)
+{
+  PRINTF(L_CORE_LOAD,"** registered systems:");
+  for(cSystemLink *sl=first; sl; sl=sl->next)
+    PRINTF(L_CORE_LOAD,"** %-16s  (pri %3d)",sl->name,sl->pri);
+  for(cSystemLink *sl=first; sl; sl=sl->next)
+    if(!sl->Init(cfgdir)) return false;
+  return true;
+}
+
+void cSystems::Clean(void)
+{
+  for(cSystemLink *sl=first; sl; sl=sl->next) sl->Clean();
+}
+
+bool cSystems::ConfigParse(const char *Name, const char *Value)
+{
+  char sysName[32];
+  unsigned int i;
+  for(i=0; Name[i] && Name[i]!='.' && i<sizeof(sysName); i++)
+    sysName[i]=Name[i];
+  if(Name[i]) {
+    sysName[i]=0; i++;
+    cSystemLink *sl=FindByName(sysName);
+    if(sl && sl->opts) return sl->opts->Parse(&Name[i],Value);
+    }
+  return false;
+}
+
+void cSystems::ConfigStore(bool AsIs)
+{
+  for(cSystemLink *sl=first; sl; sl=sl->next)
+    if(sl->opts && sl->opts->Store(AsIs)) sl->NewConfig();
+}
+
+cOpts *cSystems::GetSystemOpts(bool start)
+{
+  static cSystemLink *sl=0;
+  sl=(start || !sl) ? first : sl->next;
+  while(sl) {
+    if(sl->opts) return sl->opts;
+    sl=sl->next;
+    }
+  return 0;
+}
+
+// -- cMsgCache ----------------------------------------------------------------
+
+#define FREE   0x00 // modes
+#define FAIL1  0x01
+#define FAIL2  0x3D
+#define FAILN  0x3E
+#define GOOD   0x3F
+#define MASK   0x3F
+#define QUEUED 0x40
+#define WAIT   0x80
+
+struct Cache {
+  int crc;
+  int mode;
+  };
+
+cMsgCache::cMsgCache(int NumCache, int StoreSize)
+{
+  numCache=NumCache;
+  storeSize=StoreSize;
+  ptr=0; stores=0; maxFail=2;
+  caches=MALLOC(struct Cache,numCache);
+  if(caches) {
+    Clear();
+    if(storeSize>0) {
+      stores=MALLOC(unsigned char,numCache*storeSize);
+      if(!stores) PRINTF(L_GEN_ERROR,"msgcache: no memory for store area");
+      }
+    }
+  else PRINTF(L_GEN_ERROR,"msgcache: no memory for cache");
+}
+
+cMsgCache::~cMsgCache()
+{
+  free(caches);
+  free(stores);
+}
+
+void cMsgCache::SetMaxFail(int maxfail)
+{
+  maxFail=min(maxfail,FAILN);
+  PRINTF(L_CORE_MSGCACHE,"%d/%p: maxFail set to %d",getpid(),this,maxFail);
+}
+
+void cMsgCache::Clear(void)
+{
+  cMutexLock lock(&mutex);
+  memset(caches,0,sizeof(struct Cache)*numCache);
+  ptr=0;
+  PRINTF(L_CORE_MSGCACHE,"%d/%p: clear",getpid(),this);
+}
+
+struct Cache *cMsgCache::FindMsg(int crc)
+{
+  int i=ptr;
+  while(1) {
+    if(--i<0) i=numCache-1;
+    struct Cache * const s=&caches[i];
+    if(!s->mode) break;
+    if(s->crc==crc) return s;
+    if(i==ptr) break;
+    }
+  return 0;
+}
+
+// returns:
+// -1 - msg cached as failed (max failed)
+// 0  - msg cached as good, result stored
+// >0 - msg not cached, queue id
+int cMsgCache::Get(const unsigned char *msg, int len, unsigned char *store)
+{
+  int crc=crc32_le(0,msg,len);
+  cMutexLock lock(&mutex);
+  if(!caches || (storeSize>0 && !stores)) return -1; // sanity
+  struct Cache *s;
+  while((s=FindMsg(crc))) {
+    if(!(s->mode&QUEUED)) break;
+    s->mode|=WAIT;
+    PRINTF(L_CORE_MSGCACHE,"%d/%p: msg already queued. waiting to complete",getpid(),this);
+    wait.Wait(mutex);
+    }
+  int id;
+  if(!s) {
+    while(1) {
+      s=&caches[ptr];
+      if(!(s->mode&QUEUED)) break;
+      s->mode|=WAIT;
+      PRINTF(L_CORE_MSGCACHE,"%d/%p: queue overwrite protection id=%d",getpid(),this,ptr+1);
+      wait.Wait(mutex); // don't overwrite queued msg's
+      }
+    id=ptr+1;
+    s->crc=crc;
+    s->mode=QUEUED;
+    PRINTF(L_CORE_MSGCACHE,"%d/%p: queued msg with id=%d",getpid(),this,id);
+    ptr++; if(ptr>=numCache) { ptr=0; PRINTF(L_CORE_MSGCACHE,"msgcache: roll-over (%d)",numCache); }
+    return id;
+    }
+  else {
+    id=(s-&caches[0])+1;
+    if(s->mode==GOOD) {
+      if(store && storeSize>0)
+        memcpy(store,&stores[(id-1)*storeSize],storeSize);
+      PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as GOOD (%d)",getpid(),this,id);
+      return 0;
+      }
+    else if(s->mode>=FAIL1 && s->mode<=FAIL2) {
+      PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAIL%d (%d)",getpid(),this,s->mode,id);
+      s->mode|=QUEUED;
+      return id;
+      }
+    else {
+      PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAILN (%d)",getpid(),this,id);
+      return -1;
+      }
+    }
+}
+
+int cMsgCache::Cache(int id, bool result, const unsigned char *store)
+{
+  cMutexLock lock(&mutex);
+  if(id<1 || !caches || (storeSize>0 && !stores)) return 0; // sanity
+  struct Cache *s=&caches[id-1];
+  LBSTARTF(L_CORE_MSGCACHE);
+  LBPUT("%d/%p: de-queued msg with id=%d ",getpid(),this,id);
+  if(s->mode&WAIT) wait.Broadcast();
+  if(result) {
+    if(store && storeSize>0)
+      memcpy(&stores[(id-1)*storeSize],store,storeSize);
+    s->mode=GOOD;
+    LBPUT("(GOOD)");
+    return 0;
+    }
+  else {
+    int m=s->mode&MASK;
+    if(m==GOOD)
+      m=s->mode=FREE;
+    if(m==FREE || (m>=FAIL1 && m<=FAIL2)) {
+      s->mode=(s->mode&MASK)+1;
+      if(s->mode<maxFail)
+        LBPUT("(FAIL%d)",s->mode);
+      else
+        m=s->mode=FAILN;
+      }
+    if(m==FAILN)
+      LBPUT("(FAILN)");
+    return s->mode;
+    }
+  LBEND();
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/system.h
new file mode 100644 (file)
index 0000000..29e4871
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SYSTEM_H
+#define ___SYSTEM_H
+
+#include <vdr/tools.h>
+
+#include "data.h"
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define SYSTEM_MASK 0xFF00
+
+// ----------------------------------------------------------------
+
+class cEcmInfo;
+class cPlainKey;
+class cSystem;
+class cSystems;
+class cLogger;
+class cOpts;
+class cHookManager;
+
+// ----------------------------------------------------------------
+
+class cFeature {
+private:
+  static bool keyfile, smartcard;
+public:
+  void NeedsKeyFile(void);
+  void NeedsSmartCard(void);
+  bool KeyFile(void) const { return keyfile; }
+  bool SmartCard(void) const { return smartcard; }
+  };
+
+extern cFeature Feature;
+
+// ----------------------------------------------------------------
+
+class cKeySnoop {
+private:
+  cSystem *sys;
+  int type, id, keynr;
+  bool ok;
+public:
+  cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr);
+  ~cKeySnoop();
+  void OK(cPlainKey *pk);
+  };
+
+// ----------------------------------------------------------------
+
+class cLogHook : public cSimpleItem {
+friend class cHookManager;
+private:
+  int cardNum, id;
+  const char *name;
+  cTimeMs delay;
+  bool bailOut;
+protected:
+  cPids pids;
+public:
+  cLogHook(int Id, const char *Name);
+  virtual ~cLogHook() {}
+  virtual void Process(int pid, unsigned char *data)=0;
+  void BailOut(void) { bailOut=true; }
+  };
+
+// ----------------------------------------------------------------
+
+struct EcmCheck;
+
+class cSystem : public cSimpleItem {
+friend class cKeySnoop;
+private:
+  static int foundKeys, newKeys;
+  int pri, cardNum;
+  cLastKey lastkey;
+  char *lastTxt;
+  char currentKeyStr[48];
+  struct EcmCheck *check;
+  cEcmInfo *logecm;
+protected:
+  const char *name;
+  unsigned char cw[16];
+  bool doLog;
+  // config details
+  int maxEcmTry;
+  bool local, hasLogger, needsLogger, needsDescrData, constant;
+  //
+  void KeyOK(cPlainKey *pk);
+  void KeyOK(const char *txt);
+  void KeyFail(int type, int id, int keynr);
+  void StartLog(const cEcmInfo *ecm, int caid);
+public:
+  cSystem(const char *Name, int Pri);
+  virtual ~cSystem();
+  virtual int CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync);
+  virtual void CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *buffer)=0;
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer) {};
+  virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len);
+  virtual void ParseCAT(cPids *pids, const unsigned char *buffer);
+  unsigned char *CW(void) { return cw; }
+  void DoLog(bool Log) { doLog=Log; }
+  int Pri(void) { return pri; }
+  const char *CurrentKeyStr(void) { return currentKeyStr; }
+  const char *Name(void) { return name; }
+  int CardNum(void) { return cardNum; }
+  void CardNum(int CardNum) { cardNum=CardNum; }
+  int MaxEcmTry(void) { return maxEcmTry; }
+  bool HasLogger(void) { return hasLogger; }
+  bool NeedsLogger(void) { return needsLogger; }
+  bool Local(void) { return local; }
+  bool NeedsData(void) { return needsDescrData; }
+  bool Constant(void) { return constant; }
+  static void FoundKey(void) { foundKeys++; }
+  static void NewKey(void) { newKeys++; }
+  static void KeyStats(int &fk, int &nk) { fk=foundKeys; nk=newKeys; }
+  };
+
+// ----------------------------------------------------------------
+
+class cSystemLink {
+friend class cSystems;
+private:
+  int sysIdent;
+protected:
+  cSystemLink *next;
+  const char *name;
+  int pri;
+  cOpts *opts;
+  bool noFF;
+public:
+  cSystemLink(const char *Name, int Pri);
+  virtual ~cSystemLink();
+  virtual bool CanHandle(unsigned short SysId)=0;
+  virtual cSystem *Create(void)=0;
+  virtual bool Init(const char *cfgdir) { return true; }
+  virtual void Clean(void) {}
+  virtual void NewConfig(void) {}
+  };
+
+// ----------------------------------------------------------------
+
+class cSystems {
+friend class cSystemLink;
+private:
+  static cSystemLink *first;
+  static int nextSysIdent;
+  //
+  static void Register(cSystemLink *sysLink);
+  static cSystemLink *FindByName(const char *Name);
+  static cSystemLink *FindById(unsigned short SysId, bool ff, int oldPri);
+  static cSystemLink *FindByIdent(int ident);
+public:
+  static int Provides(const unsigned short *SysIds, bool ff);
+  static cSystem *FindBySysId(unsigned short SysId, bool ff, int oldPri);
+  static cSystem *FindBySysName(unsigned short SysId, bool ff, const char *Name);
+  static int FindIdentBySysId(unsigned short SysId, bool ff, int &Pri);
+  static cSystem *FindBySysIdent(int ident);
+  static bool Init(const char *cfgdir);
+  static void Clean(void);
+  //
+  static bool ConfigParse(const char *Name, const char *Value);
+  static void ConfigStore(bool AsIs);
+  static cOpts *GetSystemOpts(bool start);
+  };
+
+// ----------------------------------------------------------------
+
+struct Cache;
+
+class cMsgCache {
+private:
+  struct Cache *caches;
+  unsigned char *stores;
+  int numCache, ptr, storeSize, maxFail;
+  cMutex mutex;
+  cCondVar wait;
+  //
+  struct Cache *FindMsg(int crc);
+public:
+  cMsgCache(int NumCache, int StoreSize);
+  ~cMsgCache();
+  int Cache(int id, bool result, const unsigned char *store);
+  int Get(const unsigned char *msg, int len, unsigned char *store);
+  void Clear(void);
+  void SetMaxFail(int max);
+  };
+
+#endif //___SYSTEM_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems-pre/.empty b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems-pre/.empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/aroureos.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/aroureos.c
new file mode 100644 (file)
index 0000000..d34544d
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cc.h"
+#include "network.h"
+#include "parse.h"
+
+// -- cCardClientAroureos ------------------------------------------------------
+
+class cCardClientAroureos : public cCardClient, protected cIdSet {
+private:
+  cNetSocket so;
+  //
+  bool ParseCardConfig(const char *config, int *num);
+protected:
+  virtual bool Login(void);
+public:
+  cCardClientAroureos(const char *Name);
+  virtual bool Init(const char *config);
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+  virtual bool ProcessEMM(int caSys, const unsigned char *source);
+  };
+
+static cCardClientLinkReg<cCardClientAroureos> __aroureos("Aroureos");
+
+cCardClientAroureos::cCardClientAroureos(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,5,DEFAULT_IDLE_TIMEOUT)
+{}
+
+bool cCardClientAroureos::ParseCardConfig(const char *config, int *num)
+{
+  int hb, hs;
+  int startNum=*num;
+  if(sscanf(&config[*num],":%x:%x%n",&hb,&hs,num)==2) {
+    *num+=startNum;
+    unsigned char h[3];
+    h[0]=(hs>>16)&0xFF;
+    h[1]=(hs>> 8)&0xFF;
+    h[2]=(hs>> 0)&0xFF;
+    PRINTF(L_CC_LOGIN,"%s: hexser %02X%02X%02X hexbase %02X",name,h[0],h[1],h[2],hb);
+    ResetIdSet();
+    SetCard(new cCardIrdeto(hb,&h[0]));
+    }
+  return true;
+}
+
+bool cCardClientAroureos::Init(const char *config)
+{
+  cMutexLock lock(this);
+  so.Disconnect();
+  int num=0;
+  return ParseStdConfig(config,&num) &&
+         ParseCardConfig(config,&num);
+}
+
+bool cCardClientAroureos::Login(void)
+{
+  so.Disconnect();
+  if(!so.Connect(hostname,port)) return false;
+  PRINTF(L_CC_LOGIN,"%s: connected to %s:%d",name,hostname,port);
+  if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+  return true;  
+}
+
+bool cCardClientAroureos::ProcessEMM(int caSys, const unsigned char *source)
+{
+  if(emmAllowed) {
+    cMutexLock lock(this);
+    if(MatchEMM(source)) {
+      const int length=SCT_LEN(source);
+      int id=msEMM.Get(source,length,0);
+      if(id>0) {
+        unsigned char *buff=AUTOMEM(length+8);
+        memcpy(buff,"EMM",3);
+        memcpy(&buff[3],source,length);
+        SendMsg(&so,buff,length+3);
+        msEMM.Cache(id,true,0);
+        }
+      return true;
+      }
+    }
+  return false;
+}
+
+bool cCardClientAroureos::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+  cMutexLock lock(this);
+  so.Flush();
+  const int len=SCT_LEN(source);
+  if(len<=93) {
+    unsigned char buff[128];
+    memcpy(buff,"ECM",3);
+    memcpy(&buff[3],source,len);
+
+    if(!SendMsg(&so,buff,96)) return false;
+    int n=RecvMsg(&so,buff,sizeof(buff));
+    if(n>0) {
+      memcpy(cw,buff,16);
+      for(n=0; n<16; n++) if(cw[n]) break;
+      if(n<16) return true;
+      }
+    }
+  return false;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/camd.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/camd.c
new file mode 100644 (file)
index 0000000..7d5c096
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+
+#include "cc.h"
+#include "network.h"
+#include "crypto.h"
+#include "misc.h"
+#include "parse.h"
+
+#include <openssl/md5.h>
+
+#define CCVERSION "3.37"
+#define CCTIMEOUT 5000 // ms
+
+// -- cCardClientCommon --------------------------------------------------------
+
+class cCardClientCommon : public cCardClient, public cAES, protected cIdSet {
+private:
+  bool conReply, logReply, doAES;
+  bool exclusive;
+  int minMsgLen;
+  cCondVar sleepCond;
+  cTimeMs time;
+protected:
+  cNetSocket so;
+  bool emmProcessing;
+  char username[11], password[11];
+  //
+  virtual bool Login(void);
+  bool ParseKeyConfig(const char *config, int *num);
+  bool ParseUserConfig(const char *config, int *num);
+  virtual bool SendMsg(cNetSocket *so, const unsigned char *data, int len);
+  virtual int RecvMsg(cNetSocket *so, unsigned char *data, int len, int to=-1);
+  virtual void HandleEMMRequest(const unsigned char *buff, int len) {}
+  virtual bool CanHandleEMM(int SysId) { return false; }
+public:
+  cCardClientCommon(const char *Name, bool ConReply, bool LogReply, bool DoAES, int MinMsgLen);
+  virtual bool Init(const char *config);
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+  virtual bool ProcessEMM(int caSys, const unsigned char *source);
+  };
+
+cCardClientCommon::cCardClientCommon(const char *Name, bool ConReply, bool LogReply, bool DoAES, int MinMsgLen)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,CCTIMEOUT/1000,DEFAULT_IDLE_TIMEOUT)
+{
+  conReply=ConReply; logReply=LogReply; doAES=DoAES; minMsgLen=MinMsgLen;
+  emmProcessing=exclusive=false;
+}
+
+bool cCardClientCommon::ParseUserConfig(const char *config, int *num)
+{
+  int startNum=*num;
+  if(sscanf(&config[*num],":%10[^:]:%10[^:]%n",username,password,num)==2) {
+    *num+=startNum;
+    PRINTF(L_CC_CORE,"%s: username=%s password=%s",name,username,password);;
+    return true;
+    }
+  return false;
+}
+
+bool cCardClientCommon::ParseKeyConfig(const char *config, int *num)
+{
+  char hexkey[33];
+  int startNum=*num;
+  if(sscanf(&config[*num],":%32[^:]%n",hexkey,num)==1) {
+    *num+=startNum;
+    PRINTF(L_CC_CORE,"%s: key=%s",name,hexkey);
+    unsigned char binkey[16];
+    memset(binkey,0,sizeof(binkey));
+    const char *line=hexkey;
+    int n=GetHex(line,binkey,sizeof(binkey),false);
+    if(n!=(int)sizeof(binkey))
+      PRINTF(L_CC_CAMD,"warning AES key not %d bytes long",(int)sizeof(binkey));
+    LDUMP(L_CC_CAMD,binkey,16,"AES activated key =");
+    SetKey(binkey);
+    }
+  return true;
+}
+
+bool cCardClientCommon::SendMsg(cNetSocket *so, const unsigned char *data, int len)
+{
+  unsigned char *buff2=AUTOMEM(minMsgLen);
+  if(len<minMsgLen) {
+    memcpy(buff2,data,len);
+    memset(buff2+len,0,minMsgLen-len);
+    data=buff2; len=minMsgLen;
+    }
+  unsigned char *buff=AUTOMEM(len+16);
+  const int l=Encrypt(data,len,buff);
+  if(l>0) { data=buff; len=l; }
+  return cCardClient::SendMsg(so,data,len);
+}
+
+int cCardClientCommon::RecvMsg(cNetSocket *so, unsigned char *data, int len, int to)
+{
+  int n=cCardClient::RecvMsg(so,data,len,to);
+  if(n>0) {
+    if(n&15) PRINTF(L_CC_CAMD,"AES crypted message length not a multiple of 16");
+    Decrypt(data,n);
+    }
+  return n;
+}
+
+bool cCardClientCommon::Init(const char *config)
+{
+  cMutexLock lock(this);
+  so.Disconnect();
+  int num=0;
+  if(ParseStdConfig(config,&num) &&
+     ParseUserConfig(config,&num) &&
+     (!doAES || ParseKeyConfig(config,&num))) {
+    return (emmAllowed && logReply && Immediate()) ? Login() : true;
+    }
+  return false;
+}
+
+bool cCardClientCommon::Login(void)
+{
+  so.Disconnect();
+  if(!so.Connect(hostname,port)) return false;
+  PRINTF(L_CC_LOGIN,"%s: connected to %s:%d (%s)",name,hostname,port,name);
+  emmProcessing=false;
+
+  unsigned char buff[128];
+  if(conReply) {
+    if(RecvMsg(&so,buff,sizeof(buff))!=16) {
+      PRINTF(L_CC_CAMD,"bad connect reply");
+      return false;
+      }
+    }
+
+  memset(buff,0,32);
+  int user_len=strlen(username)+1;
+  memcpy(buff+1,username,user_len);
+  int pass_len=strlen(password)+1;
+  memcpy(buff+1+user_len+1,password,pass_len);
+  int vers_len=strlen(CCVERSION)+1;
+  memcpy(buff+1+user_len+pass_len+1,CCVERSION,vers_len);
+  PRINTF(L_CC_CAMD,"login user='%s' password=hidden version=%s",username,CCVERSION);
+  if(!SendMsg(&so,buff,32)) return false;
+
+  if(emmAllowed && logReply) {
+    PRINTF(L_CC_CAMD,"waiting for login reply ...");
+    int r=RecvMsg(&so,buff,sizeof(buff));
+    if(r>0) HandleEMMRequest(buff,r);
+    }
+  PRINTF(L_CC_LOGIN,"%s: login done",name);
+  return true;
+}
+
+bool cCardClientCommon::ProcessEMM(int caSys, const unsigned char *source)
+{
+  if(emmAllowed && CanHandleEMM(caSys)) {
+    cMutexLock lock(this);
+    if(MatchEMM(source)) {
+      const int length=SCT_LEN(source);
+      int id=msEMM.Get(source,length,0);
+      if(id>0) {
+        unsigned char *buff=AUTOMEM(length+32);
+        buff[0]=0x03;
+        buff[1]=(caSys>>8);
+        buff[2]=(caSys&0xFF);
+        memcpy(buff+3,((cCardIrdeto *)card)->hexSer,3);
+        buff[6]=((cCardIrdeto *)card)->hexBase;
+        memcpy(&buff[7],source,length);
+        //PRINTF(L_CC_CAMD,"%s: sending EMM for caid 0x%04X",name,caSys);
+        SendMsg(&so,buff,length+7);
+        msEMM.Cache(id,true,0);
+        }
+      return true;
+      }
+    }
+  return false;
+}
+
+bool cCardClientCommon::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+  Lock();
+  bool res=false;
+  while(exclusive) sleepCond.Wait(*this);
+  if((so.Connected() || Login()) && (!emmProcessing || CanHandle(ecm->caId))) {
+    const int length=SCT_LEN(source);
+    unsigned char *buff=AUTOMEM(length+32);
+    int n;
+    while((n=RecvMsg(&so,buff,16,0))>0) HandleEMMRequest(buff,n);
+    buff[0]=0x02;
+    buff[1]=(ecm->caId>>8);
+    buff[2]=(ecm->caId&0xFF);
+    memset(&buff[3],0,4);
+    memcpy(&buff[7],source,length);
+    if(SendMsg(&so,buff,length+7)) {
+      exclusive=true;
+      time.Set(CCTIMEOUT);
+      do {
+        sleepCond.TimedWait(*this,50);
+        while((n=RecvMsg(&so,buff,32,0))>0) {
+          if(n>=21 && buff[0]==2) {
+            if(!CheckNull(buff+5,16)) {
+              if(!res) {
+                memcpy(cw,buff+5,16);
+                res=true;
+                }
+              else PRINTF(L_CC_CAMD,"unexpected CW packet");
+              }
+            else {
+              PRINTF(L_CC_ECM,"%s: server is unable to handle ECM",name);
+              n=-1;
+              break;
+              }
+            }
+          else HandleEMMRequest(buff,n);
+          }
+        } while(!res && n>=0 && !time.TimedOut());
+      if(!res && time.TimedOut()) PRINTF(L_CC_ECM,"%s: CW request timed out",name);
+      exclusive=false;
+      sleepCond.Broadcast();
+      }
+    }
+  Unlock();
+  return res;
+}
+
+// -- cCardClientCamd33 --------------------------------------------------------
+
+class cCardClientCamd33 : public cCardClientCommon {
+private:
+  int CAID;
+  unsigned char lastEmmReq[32];
+protected:
+  virtual void HandleEMMRequest(const unsigned char *buff, int len);
+  virtual bool CanHandleEMM(int SysId);
+public:
+  cCardClientCamd33(const char *Name);
+  virtual bool CanHandle(unsigned short SysId);
+  };
+
+static cCardClientLinkReg<cCardClientCamd33> __camd33("Camd33");
+
+cCardClientCamd33::cCardClientCamd33(const char *Name)
+:cCardClientCommon(Name,true,true,true,0)
+{
+  CAID=0;
+  memset(lastEmmReq,0,sizeof(lastEmmReq));
+}
+
+bool cCardClientCamd33::CanHandle(unsigned short SysId)
+{
+  return CanHandleEMM(SysId) || cCardClient::CanHandle(SysId);
+}
+
+bool cCardClientCamd33::CanHandleEMM(int SysId)
+{
+  return (emmProcessing && SysId==CAID);
+}
+
+void cCardClientCamd33::HandleEMMRequest(const unsigned char *buff, int len)
+{
+  if(len>=13 && buff[0]==0 && !CheckNull(buff,len) && memcmp(buff,lastEmmReq,13)) {
+    emmProcessing=false;
+    CAID=buff[1]*256+buff[2];
+    ResetIdSet();
+    switch(CAID>>8) {
+      case 0x17:
+      case 0x06:
+        SetCard(new cCardIrdeto(buff[6],&buff[3]));
+        AddProv(new cProviderIrdeto(0,&buff[7]));
+        AddProv(new cProviderIrdeto(2,&buff[10]));
+        memcpy(lastEmmReq,buff,13);
+        PRINTF(L_CC_LOGIN,"%s: CAID: %04x HexSerial: %02X%02X%02X, HexBase: %02X",name,CAID,buff[3],buff[4],buff[5],buff[6]);
+        PRINTF(L_CC_LOGIN,"%s: Provider00: %02X%02X%02X, Provider10: %02X%02X%02X",name,buff[7],buff[8],buff[9],buff[10],buff[11],buff[12]);
+        if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+        emmProcessing=true;
+        break;
+      }
+    }
+}
+
+// -- cCardClientCardd ---------------------------------------------------------
+
+class cCardClientCardd : public cCardClientCommon {
+public:
+  cCardClientCardd(const char *Name);
+  };
+
+static cCardClientLinkReg<cCardClientCardd> __cardd("Cardd");
+
+cCardClientCardd::cCardClientCardd(const char *Name)
+:cCardClientCommon(Name,false,true,false,96)
+{}
+
+// -- cCardClientBuffy ---------------------------------------------------------
+
+#define MAX_CAIDS 16
+
+class cCardClientBuffy : public cCardClientCommon {
+private:
+  unsigned short CAIDs[MAX_CAIDS], numCAIDs;
+protected:
+  virtual bool Login(void);
+public:
+  cCardClientBuffy(const char *Name);
+  virtual bool Init(const char *config);
+  virtual bool CanHandle(unsigned short SysId);
+  };
+
+static cCardClientLinkReg<cCardClientBuffy> __buffy("Buffy");
+
+cCardClientBuffy::cCardClientBuffy(const char *Name)
+:cCardClientCommon(Name,true,false,true,0)
+{}
+
+bool cCardClientBuffy::Init(const char *config)
+{
+  cMutexLock lock(this);
+  if(cCardClientCommon::Init(config)) {
+    return Immediate() ? Login() : true;
+    }
+  return false;
+}
+
+bool cCardClientBuffy::CanHandle(unsigned short SysId)
+{
+  cMutexLock lock(this);
+  for(int i=0; i<numCAIDs; i++) if(CAIDs[i]==SysId) return true;
+  return false;
+}
+
+bool cCardClientBuffy::Login(void)
+{
+  cMutexLock lock(this);
+  if(!cCardClientCommon::Login()) return false;
+
+  unsigned char buff[128];
+  memset(buff,0,sizeof(buff));
+  buff[0]=0x0A;
+  if(!SendMsg(&so,buff,32)) return false;
+  int n=RecvMsg(&so,buff,sizeof(buff));
+  if(n<0) return false;
+  for(int i=1; i<n && numCAIDs<MAX_CAIDS; i+=2) {
+    unsigned short caid=(buff[i+1]<<8)+buff[i];
+    if(caid==0xFFFF) break;
+    if(caid) CAIDs[numCAIDs++]=caid;
+    }
+  LBSTART(L_CC_LOGIN);
+  LBPUT("%s: CAIDs ",name);
+  for(int i=0; i<numCAIDs && CAIDs[i]; i++) LBPUT("%04X ",CAIDs[i]);
+  LBEND();
+  return true;
+}
+
+// -- cCardClientCamd35 --------------------------------------------------------
+
+struct CmdBlock {
+  unsigned int ucrc;
+  struct {
+    unsigned char cmd;    // 0
+    unsigned char len;    // 1
+    unsigned char pad[2]; // 2  XXXX
+    unsigned int crc;     // 4
+    } udp_header;
+  struct {
+    unsigned short srvID; // 8
+    unsigned short casID; // 10
+    unsigned int prvID;   // 12
+    unsigned short pinID; // 16
+    unsigned char pad2[2];// 18 XXXX
+    } service;
+  unsigned char data[2];  // 20
+  };
+
+#define CBSIZE(cb) (sizeof((cb)->udp_header)+sizeof((cb)->service))
+#define UCSIZE(cb) (sizeof((cb)->ucrc))
+#define HDSIZE(cb) (CBSIZE(cb)+UCSIZE(cb))
+
+struct EmmReq02 {
+  unsigned short caid;
+  unsigned char hex[4];
+  unsigned char D0, D2, D3;
+  };
+
+struct EmmReq05 {
+  unsigned short caids[8]; // 0
+  int caidCount;           // 16
+  unsigned char ua[6];     // 20
+  unsigned short provMap;  // 26
+  struct {                 // 28
+    unsigned char prov[2];
+    unsigned char sa[3];
+    } provInfo[16];
+  unsigned char D0, D2, D3; // 108
+  };
+
+class cCardClientCamd35 : public cCardClient, public cAES, private cIdSet {
+private:
+  unsigned int ucrc;
+  unsigned short pinid;
+  bool exclusive, emmCmd06;
+  cCondVar sleepCond;
+  cTimeMs time;
+protected:
+  cNetSocket so;
+  bool emmProcessing;
+  char username[33], password[33];
+  int numCaids, Caids[8];
+  unsigned char Dx[8];
+  int lastEmmReq;
+  //
+  virtual bool Login(void);
+  bool ParseUserConfig(const char *config, int *num);
+  bool SendBlock(struct CmdBlock *cb, int datalen);
+  int RecvBlock(struct CmdBlock *cb, int maxlen, int to);
+  void HandleEMMRequest(const struct CmdBlock *cb);
+  bool CanHandleEMM(unsigned short SysId);
+public:
+  cCardClientCamd35(const char *Name);
+  virtual bool Init(const char *config);
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool ProcessEMM(int caSys, const unsigned char *data);
+  };
+
+static cCardClientLinkReg<cCardClientCamd35> __camd35("Camd35");
+
+cCardClientCamd35::cCardClientCamd35(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,CCTIMEOUT/1000,DEFAULT_IDLE_TIMEOUT,true)
+{
+  emmProcessing=exclusive=emmCmd06=false; pinid=0; numCaids=0; lastEmmReq=0;
+  memset(Dx,0,sizeof(Dx));
+}
+
+bool cCardClientCamd35::ParseUserConfig(const char *config, int *num)
+{
+  int startNum=*num;
+  if(sscanf(&config[*num],":%32[^:]:%32[^:]%n",username,password,num)==2) {
+    *num+=startNum;
+    ucrc=bswap_32(crc32_le(0,MD5((unsigned char *)username,strlen(username),0),16));
+    PRINTF(L_CC_CORE,"%s: username=%s password=%s ucrc=%08x",name,username,password,ucrc);;
+    SetKey(MD5((unsigned char *)password,strlen(password),0));
+    return true;
+    }
+  return false;
+}
+
+bool cCardClientCamd35::SendBlock(struct CmdBlock *cb, int datalen)
+{
+  cb->udp_header.len=datalen;
+  cb->udp_header.crc=bswap_32(crc32_le(0,&cb->data[0],datalen));
+  datalen+=CBSIZE(cb);
+  unsigned char *buff=AUTOMEM(datalen+UCSIZE(cb)+16);
+  *((unsigned int *)buff)=ucrc;
+  HEXDUMP(L_CC_CAMDEXTR,cb,datalen+UCSIZE(cb),"send:");
+  const int l=Encrypt((unsigned char *)cb+UCSIZE(cb),datalen,buff+UCSIZE(cb));
+  if(l<=0) return false;
+  return cCardClient::SendMsg(&so,buff,l+UCSIZE(cb));
+}
+
+int cCardClientCamd35::RecvBlock(struct CmdBlock *cb, int maxlen, int to)
+{
+  int n=cCardClient::RecvMsg(&so,(unsigned char *)cb,maxlen,to);
+  if(n<=0) return n;
+  if((unsigned int)n>=CBSIZE(cb)+UCSIZE(cb)) {
+    if(cb->ucrc==ucrc) {
+      Decrypt((unsigned char *)cb+UCSIZE(cb),n-UCSIZE(cb));
+      if((unsigned int)n<cb->udp_header.len+CBSIZE(cb)+UCSIZE(cb))
+        PRINTF(L_CC_CAMD35,"packet length doesn't match data length");
+      else if(cb->udp_header.crc!=bswap_32(crc32_le(0,&cb->data[0],cb->udp_header.len)))
+        PRINTF(L_CC_CAMD35,"data crc failed");
+      else {
+        HEXDUMP(L_CC_CAMDEXTR,cb,n,"recv:");
+        return n;
+        }
+      }
+    else PRINTF(L_CC_CAMD35,"wrong ucrc: got %08x, want %08x",cb->ucrc,ucrc);
+    }
+  else PRINTF(L_CC_CAMD35,"short packet received");
+  return -1;
+}
+
+bool cCardClientCamd35::Init(const char *config)
+{
+  cMutexLock lock(this);
+  so.Disconnect();
+  int num=0;
+  return (ParseStdConfig(config,&num) && ParseUserConfig(config,&num));
+}
+
+bool cCardClientCamd35::Login(void)
+{
+  so.Disconnect();
+  if(!so.Connect(hostname,port)) return false;
+  PRINTF(L_CC_LOGIN,"%s: connected to %s:%d (%s)",name,hostname,port,name);
+  emmProcessing=false; lastEmmReq=0;
+  PRINTF(L_CC_LOGIN,"%s: login done",name);
+  return true;
+}
+
+void cCardClientCamd35::HandleEMMRequest(const struct CmdBlock *cb)
+{
+  int c=crc32_le(0,cb->data,cb->udp_header.len);
+  if(c!=lastEmmReq) {
+    lastEmmReq=c;
+    ResetIdSet();
+    if(cb->udp_header.cmd==0x02 && cb->udp_header.len>=9) {
+      struct EmmReq02 *req=(struct EmmReq02 *)cb->data;
+      numCaids=1;
+      Caids[0]=bswap_16(req->caid);
+      SetCard(new cCardIrdeto(req->hex[3],&req->hex[0]));
+      Dx[0]=req->D0;
+      Dx[2]=req->D2;
+      Dx[3]=req->D3;
+      char str[20];
+      PRINTF(L_CC_LOGIN,"%s: CAID: %04x HexSerial: %s, HexBase: %02X (D0-%d D2-%d D3-%d)\n",
+             name,Caids[0],HexStr(str,((cCardIrdeto *)card)->hexSer,3),((cCardIrdeto *)card)->hexBase,
+             Dx[0],Dx[2],Dx[3]);
+      if(!emmProcessing || emmCmd06) PRINTF(L_CC_CAMD35,"got cmd 02, doing cmd02 EMM");
+      emmCmd06=false;
+      }
+    else if(cb->udp_header.cmd==0x05 && cb->udp_header.len>=111) {
+      struct EmmReq05 *req=(struct EmmReq05 *)cb->data;
+      numCaids=bswap_32(req->caidCount);
+      for(int i=numCaids-1; i>=0; i--) Caids[i]=bswap_16(req->caids[i]);
+      LBSTARTF(L_CC_LOGIN);
+      LBPUT("%s: CAIDS:",name);
+      for(int i=numCaids-1; i>=0; i--) LBPUT(" %04x",Caids[i]);
+      char str[20], str2[20];
+      LBPUT(" ua=%s",HexStr(str,req->ua,6));
+      switch(Caids[0]>>8) {
+        case 0x17:
+        case 0x06: SetCard(new cCardIrdeto(req->ua[3],&req->ua[0])); break;
+        case 0x01: SetCard(new cCardSeca(&req->ua[0])); break;
+        case 0x0d: SetCard(new cCardCryptoworks(&req->ua[0])); break;
+        case 0x05: SetCard(new cCardViaccess(&req->ua[0])); break;
+        default:
+          LBPUT(" (unhandled)");
+          break;
+        }
+      LBPUT(" providers");
+      int map=bswap_16(req->provMap);
+//      for(int i=0; i<15; i++) //XXX not sure what actualy is the correct
+//        if(map & (1<<i)) {    //XXX interpretation of provMap
+      for(int i=0; i<map && i<15; i++) {
+        LBPUT(" %d:%s/%s",i,HexStr(str,req->provInfo[i].prov,2),HexStr(str2,req->provInfo[i].sa,3));
+        if(!CheckNull(req->provInfo[i].sa,3) && !CheckFF(req->provInfo[i].sa,3)) {
+          switch(Caids[0]>>8) {
+            case 0x17:
+            case 0x06: AddProv(new cProviderIrdeto(req->provInfo[i].prov[0],req->provInfo[i].sa)); break;
+            case 0x01: AddProv(new cProviderSeca(req->provInfo[i].prov,req->provInfo[i].sa)); break;
+            default:
+              LBPUT(" <unhandled>");
+              break;
+            }
+          }
+        }
+      Dx[0]=req->D0;
+      Dx[2]=req->D2;
+      Dx[3]=req->D3;
+      LBPUT(" (D0-%d D2-%d D3-%d)",Dx[0],Dx[2],Dx[3]);
+      LBEND();
+
+      if(!emmProcessing || !emmCmd06) PRINTF(L_CC_CAMD35,"got cmd 05, doing cmd06 EMM");
+      emmCmd06=true;
+      }
+    else return;
+
+    if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+    emmProcessing=true;
+    }
+}
+
+bool cCardClientCamd35::CanHandleEMM(unsigned short SysId)
+{
+  for(int i=numCaids-1; i>=0; i--) if(SysId==Caids[i]) return true;
+  return false;
+}
+
+bool cCardClientCamd35::ProcessEMM(int caSys, const unsigned char *data)
+{
+  if(emmProcessing && emmAllowed) {
+    cMutexLock lock(this);
+    PRINTF(L_CC_CAMDEXTR,"got EMM caSys=%.4x",caSys);
+    if(CanHandleEMM(caSys)) {
+      LDUMP(L_CC_CAMDEXTR,&data[0],10,"EMM starts with");
+      int upd;
+      cProvider *p;
+      cAssembleData ad(data);
+      if(MatchAndAssemble(&ad,&upd,&p)) {
+        PRINTF(L_CC_CAMDEXTR,"EMM matched upd=%d provId=%.4llx",upd,p ? p->ProvId() : 0);
+        while((data=ad.Assembled())) {
+          LDUMP(L_CC_CAMDEXTR,&data[0],10,"processing assembled EMM");
+          if(Dx[upd]) {
+            unsigned char buff[300];
+            memset(buff,0xff,sizeof(buff));
+            struct CmdBlock *cb=(struct CmdBlock *)buff;
+            cb->udp_header.cmd=emmCmd06 ? 0x06 : 0x02;
+            cb->service.srvID=0;
+            cb->service.casID=bswap_16(caSys);
+            cb->service.prvID=bswap_32(p ? p->ProvId() : 0);
+            cb->service.pinID=pinid++;
+            int len=SCT_LEN(data), off=0;
+            switch(caSys>>8) {
+              case 0x0d:
+                {
+                static const unsigned char head[] = { 0x8F,0x70,0x00,0xA4,0x42,0x00,0x00,0x00 };
+                int c, n;
+                switch(data[0]) {
+                  case 0x82: c=0x42; n=10; break;
+                  case 0x84: c=0x48; n=9; break;
+                  case 0x88:
+                  case 0x89: c=0x44; n=5; break;
+                  default:   continue;
+                  }
+                if(len<n) continue;
+                off=sizeof(head);
+                memcpy(&cb->data[0],head,off);
+                cb->data[3+1]=c;
+                cb->data[3+4]=len-n;
+                SetSctLen(&cb->data[0],len-n+5);
+                data+=n; len-=n;
+                break;
+                }
+              }
+            if(len+off<=255) {
+              memcpy(&cb->data[off],data,len);
+              int id=msEMM.Get(&cb->data[0],len+off,0);
+              if(id>0) {
+                SendBlock(cb,len+off);
+                msEMM.Cache(id,true,0);
+                }
+              else PRINTF(L_CC_CAMDEXTR,"not send, already in cache");
+              }
+            else PRINTF(L_CC_EMM,"%s: EMM length %d > 255, not supported by UDP protocol",name,len+off);
+            }
+          else PRINTF(L_CC_CAMDEXTR,"dropped, updtype %d blocked",upd);
+          }
+        return true;
+        }
+      else PRINTF(L_CC_CAMDEXTR,"dropped, doesn't match card data");
+      }
+    else PRINTF(L_CC_CAMDEXTR,"dropped, doesn't want caSys %.4x",caSys);
+    }
+  return false;
+}
+
+bool cCardClientCamd35::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  bool res=false;
+  const int length=SCT_LEN(data);
+  if(length<=255) {
+    Lock();
+    while(exclusive) sleepCond.Wait(*this);
+    unsigned char buff[300];
+    memset(buff,0xff,sizeof(buff));
+    struct CmdBlock *cb=(struct CmdBlock *)buff;
+    const unsigned short pid=pinid++;
+    int n;
+    while((n=RecvBlock(cb,sizeof(buff),0))>0) {
+      if(cb->udp_header.cmd==0x01 || cb->udp_header.cmd==0x44)
+        PRINTF(L_CC_CAMD35,"unexpected CW answer on flush");
+      else
+        HandleEMMRequest(cb);
+      }
+    cb->udp_header.cmd=0x00;
+    cb->service.srvID=bswap_16(ecm->prgId);
+    cb->service.casID=bswap_16(ecm->caId);
+    switch(ecm->caId>>8) {
+      case 0x18: n=(data[5]*256)+data[6]; break;
+      default: n=ecm->provId; break;
+      }
+    cb->service.prvID=bswap_32(n);
+    cb->service.pinID=pid;
+    memcpy(&cb->data[0],data,length);
+    if(SendBlock(cb,length)) {
+      exclusive=true;
+      time.Set(CCTIMEOUT);
+      do {
+        sleepCond.TimedWait(*this,50);
+        while((n=RecvBlock(cb,sizeof(buff),0))>0) {
+          if(cb->udp_header.cmd==0x01) {
+            if(cb->udp_header.len>=16 && cb->service.pinID==pid && !res) {
+              memcpy(cw,cb->data,16);
+              res=true;
+              }
+            else PRINTF(L_CC_CAMD35,"unexpected/bad CW packet");
+            }
+          else if(cb->udp_header.cmd==0x44) {
+            if(cb->service.pinID==pid) {
+              PRINTF(L_CC_ECM,"%s: server is unable to handle ECM",name);
+              n=-1;
+              break;
+              }
+            }
+          else HandleEMMRequest(cb);
+          }
+        } while(!res && n>=0 && !time.TimedOut());
+      if(!res && time.TimedOut()) PRINTF(L_CC_ECM,"%s: CW request timed out",name);
+      exclusive=false;
+      sleepCond.Broadcast();
+      }
+    Unlock();
+    }
+  else PRINTF(L_CC_ECM,"%s: ECM length %d > 255, not supported by UDP protocol",name,length);
+  return res;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.c
new file mode 100644 (file)
index 0000000..feb9816
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#include "system.h"
+#include "cc.h"
+#include "network.h"
+#include "misc.h"
+#include "opts.h"
+#include "log-core.h"
+
+#define SYSTEM_NAME          "Cardclient"
+#define SYSTEM_PRI           -15
+
+#define CONF_FILE            "cardclient.conf"
+
+static const struct LogModule lm_cc = {
+  (LMOD_ENABLE|L_CC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_CC_CORE|L_CC_LOGIN|L_CC_ECM|L_CC_EMM|L_CC_CAMD|L_CC_CAMD35|
+   L_CC_RDGD|L_CC_NEWCAMD|L_CC_GBOX)&LOPT_MASK,
+  "cardclient",
+  { "core","login","ecm","emm","camd","camd35","camd35extra","radegast",
+    "newcamd","gbox" }
+  };
+ADD_MODULE(L_CC,lm_cc)
+
+static int immediate=true;
+
+// -- cCardClient --------------------------------------------------------------
+
+cCardClient::cCardClient(const char *Name)
+:msECM(32,16)
+,msEMM(32,0)
+{
+  name=Name;
+  emmAllowed=false; emmCaid[0]=0x1700; emmMask[0]=0xFF00; numCaid=1;
+}
+
+bool cCardClient::Immediate(void)
+{
+  return immediate;
+}
+
+bool cCardClient::ParseStdConfig(const char *config, int *num)
+{
+  int n, emm=0;
+  if(!num) num=&n;
+  if(sscanf(config,"%63[^:]:%d:%d%n",hostname,&port,&emm,num)<3) return false;
+  if(emm>0) emmAllowed=true;
+  if(config[*num]=='/') {
+    numCaid=0;
+    do {
+      emmMask[numCaid]=0xFFFF;
+      int start=(*num)+1;
+      if(sscanf(&config[start],"%x%n/%x%n",&emmCaid[numCaid],num,&emmMask[numCaid],num)<1)
+        return false;
+      *num+=start;
+      numCaid++;
+      } while(numCaid<MAX_CC_CAID && config[*num]==',');
+    }
+  LBSTART(L_CC_CORE);
+  LBPUT("hostname=%s port=%d emm=%d emmCaids",hostname,port,emmAllowed);
+  for(int i=0; i<numCaid; i++) LBPUT(" %04x/%04x",emmCaid[i],emmMask[i]);
+  LBEND();
+  return true;
+}
+
+bool cCardClient::CanHandle(unsigned short SysId)
+{
+  for(int i=0; i<numCaid; i++)
+    if((SysId&emmMask[i])==emmCaid[i]) return true;
+  return false;
+}
+
+bool cCardClient::SendMsg(cNetSocket *so, const unsigned char *data, int len)
+{
+  if(!so->Connected() && !Login()) return false;
+  if(so->Write(data,len)<0) {
+    PRINTF(L_CC_CORE,"send error. reconnecting...");;
+    so->Disconnect();
+    return false;
+    }
+  return true;
+}
+
+int cCardClient::RecvMsg(cNetSocket *so, unsigned char *data, int len, int to)
+{
+  if(!so->Connected() && !Login()) return -1;
+  int n=so->Read(data,len,to);
+  if(n<0 || (n==0 && to!=0)) {
+    PRINTF(L_CC_CORE,"recv error. reconnecting...");;
+    so->Disconnect();
+    return -1;
+    }
+  return n;
+}
+
+// -- cCardClients -------------------------------------------------------------
+
+class cCardClients {
+friend class cCardClientLink;
+private:
+  static cCardClientLink *first;
+  //
+  static void Register(cCardClientLink *ccl);
+public:
+  cCardClientLink *FindByName(const char *name);
+  };
+
+cCardClientLink *cCardClients::first=0;
+
+static cCardClients cardclients;
+
+void cCardClients::Register(cCardClientLink *ccl)
+{
+  PRINTF(L_CORE_DYN,"cardclients: registering cardclient %s",ccl->name);
+  ccl->next=first;
+  first=ccl;
+}
+
+cCardClientLink *cCardClients::FindByName(const char *name)
+{
+  cCardClientLink *ccl=first;
+  while(ccl) {
+    if(!strcasecmp(ccl->name,name)) break;
+    ccl=ccl->next;
+    }
+  return ccl;
+}
+
+// -- cCardClientLink ----------------------------------------------------------
+
+cCardClientLink::cCardClientLink(const char *Name)
+{
+  name=Name;
+  cCardClients::Register(this);
+}
+
+// -- cSystemLinkCardClient -----------------------------------------------------------
+
+class cSystemLinkCardClient : public cSystemLink, public cStructListPlain<cCardClient> {
+protected:
+  virtual bool ParseLinePlain(const char *line);
+public:
+  cSystemLinkCardClient(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void);
+  virtual void Clean(void);
+  cCardClient *FindBySysId(unsigned short id, cCardClient *cc);
+  };
+
+static cSystemLinkCardClient staticCcl;
+
+// -- cSystemCardClient ---------------------------------------------------------------
+
+class cSystemCardClient : public cSystem {
+private:
+  cCardClient *cc;
+public:
+  cSystemCardClient(void);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemCardClient::cSystemCardClient(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  cc=0;
+  local=false; hasLogger=true;
+}
+
+bool cSystemCardClient::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+  cCardClient *startCc=cc;
+  do {
+    if(cc) {
+      cTimeMs start;
+      int id=cc->msECM.Get(data,SCT_LEN(data),cw);
+      if(id==0 || (id>0 && cc->ProcessECM(ecm,data,cw))) {
+        int dur=start.Elapsed();
+        if(dur>2000) {
+          char bb[32];
+          time_t now=time(0);
+          ctime_r(&now,bb); stripspace(bb);
+          PRINTF(L_CC_CORE,"%s: lagged cw %d ms (%s)",bb,dur,cc->Name());
+          }
+        char buff[32];
+        snprintf(buff,sizeof(buff),"CC %s",cc->Name());
+        KeyOK(buff);
+        if(id>0) cc->msECM.Cache(id,true,cw);
+        return true;
+        }
+      if(id>0) {
+        PRINTF(L_CC_CORE,"client %s (%s:%d) ECM failed (%d ms)",cc->Name(),cc->hostname,cc->port,(int)start.Elapsed());
+        cc->msECM.Cache(id,false,cw);
+        }
+      }
+    if(!cc) PRINTF(L_CC_CORE,"cc-loop");
+    cc=staticCcl.FindBySysId(ecm->caId,cc);
+    if(cc && cc!=startCc) PRINTF(L_CC_CORE,"now trying client %s (%s:%d)",cc->Name(),cc->hostname,cc->port);
+    } while(cc!=startCc);
+  return false;
+}
+
+void cSystemCardClient::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  cCardClient *cc=0;
+  while((cc=staticCcl.FindBySysId(caid,cc)))
+    cc->ProcessEMM(caid,buffer);
+}
+
+// -- cSystemLinkCardClient -----------------------------------------------------------
+
+cSystemLinkCardClient::cSystemLinkCardClient(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+,cStructListPlain<cCardClient>("cardclient config",CONF_FILE,SL_MISSINGOK|SL_VERBOSE|SL_NOPURGE)
+{
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptBool("Immediate",trNOOP("Cardclient: connect immediately"),&immediate));
+}
+
+cCardClient *cSystemLinkCardClient::FindBySysId(unsigned short id, cCardClient *cc)
+{
+  ListLock(false);
+  for(cc=cc ? Next(cc):First(); cc; cc=Next(cc)) 
+    if(cc->CanHandle(id)) break;
+  ListUnlock();
+  return cc;
+}
+
+bool cSystemLinkCardClient::CanHandle(unsigned short SysId)
+{
+  return FindBySysId(SysId,0)!=0;
+}
+
+cSystem *cSystemLinkCardClient::Create(void)
+{
+  return new cSystemCardClient();
+}
+
+bool cSystemLinkCardClient::ParseLinePlain(const char *line)
+{
+  char name[32];
+  int num;
+  if(sscanf(line,"%31[^:]:%n",name,&num)==1) {
+    cCardClientLink *ccl=cardclients.FindByName(name);
+    if(ccl) {
+      cCardClient *cc=ccl->Create();
+      if(cc) {
+        if(cc->Init(&line[num])) {
+          Add(cc);
+          PRINTF(L_CC_CORE,"client '%s' ready",cc->Name());
+          return true;
+          }
+        else {
+          delete cc;
+          PRINTF(L_GEN_ERROR,"init of cardclient '%s' failed",name);
+          }
+        }
+      else PRINTF(L_GEN_ERROR,"failed to create cardclient '%s'",name);
+      }
+    else PRINTF(L_GEN_ERROR,"no client found for card server type '%s'",name);
+    }
+  return false;
+}
+
+void cSystemLinkCardClient::Clean(void)
+{
+  ListLock(true); Clear(); ListUnlock();
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.h
new file mode 100644 (file)
index 0000000..2cc117a
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___SYSTEM_CC_H
+#define ___SYSTEM_CC_H
+
+#include <vdr/thread.h>
+#include "system.h"
+#include "misc.h"
+#include "log.h"
+
+#define L_CC          6
+#define L_CC_CORE     LCLASS(L_CC,0x2)
+#define L_CC_LOGIN    LCLASS(L_CC,0x4)
+#define L_CC_ECM      LCLASS(L_CC,0x8)
+#define L_CC_EMM      LCLASS(L_CC,0x10)
+#define L_CC_CAMD     LCLASS(L_CC,0x20)
+#define L_CC_CAMD35   LCLASS(L_CC,0x40)
+#define L_CC_CAMDEXTR LCLASS(L_CC,0x80)
+#define L_CC_RDGD     LCLASS(L_CC,0x100)
+#define L_CC_NEWCAMD  LCLASS(L_CC,0x200)
+#define L_CC_GBOX     LCLASS(L_CC,0x400)
+
+#define L_CC_ALL      LALL(L_CC_GBOX)
+
+// ----------------------------------------------------------------
+
+class cEcmInfo;
+class cNetSocket;
+class cCardClients;
+class cSystemCardClient;
+
+// ----------------------------------------------------------------
+
+#define MAX_CC_CAID 16
+
+class cCardClient : public cStructItem, protected cMutex {
+friend class cSystemCardClient;
+protected:
+  const char *name;
+  char hostname[64];
+  int port;
+  bool emmAllowed;
+  int emmCaid[MAX_CC_CAID], emmMask[MAX_CC_CAID], numCaid;
+  cMsgCache msECM, msEMM;
+  //
+  bool ParseStdConfig(const char *config, int *num=0);
+  virtual bool SendMsg(cNetSocket *so, const unsigned char *data, int len);
+  virtual int RecvMsg(cNetSocket *so, unsigned char *data, int len, int to=-1);
+  virtual bool Login(void) { return false; }
+  bool Immediate(void);
+public:
+  cCardClient(const char *Name);
+  virtual bool Init(const char *config)=0;
+  virtual bool CanHandle(unsigned short SysId);
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)=0;
+  virtual bool ProcessEMM(int caSys, const unsigned char *data) { return false; }
+  const char *Name(void) { return name; }
+  };
+
+// ----------------------------------------------------------------
+
+class cCardClientLink {
+friend class cCardClients;
+private:
+  cCardClientLink *next;
+protected:
+  const char *name;
+public:
+  cCardClientLink(const char *Name);
+  virtual ~cCardClientLink() {};
+  virtual cCardClient *Create(void)=0;
+  };
+
+// ----------------------------------------------------------------
+
+template<class CC> class cCardClientLinkReg : public cCardClientLink {
+public:
+  cCardClientLinkReg(const char *Name):cCardClientLink(Name) {}
+  virtual cCardClient *Create(void) { return new CC(name); }
+  };
+
+#endif //___SYSTEM_CC_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/cc.mk
new file mode 100644 (file)
index 0000000..e220060
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Cardclient
+#
+TARGET = cardclient
+OBJS   = cc.o camd.o radegast.o aroureos.o newcamd.o gbox.o
+LIBS   = -lcrypto -lcrypt
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/gbox.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/gbox.c
new file mode 100644 (file)
index 0000000..fa7c88a
--- /dev/null
@@ -0,0 +1,173 @@
+/* 
+ * Softcam plugin to VDR (C++) 
+ * 
+ * This code 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 code 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 
+ *
+ * This code is based on morfsta's version but has been rewritten nearly
+ * from scratch.
+ * 
+ * To get it working ensure that GBOX is running with no F: { 01 } line,
+ * and no pmt.tmp file in /var/tmp.
+ */ 
+
+#include <stdio.h>
+
+#include <vdr/pat.h>
+
+#include "cc.h" 
+#include "network.h"
+#include "parse.h"
+
+// -- cGboxClient ----------------------------------------------------------- 
+class cCardClientGbox : public cCardClient { 
+private: 
+  cNetSocket so;
+  //
+  int GetMsg(int cmd, unsigned char *buff, int len);
+protected: 
+  virtual bool Login(void); 
+public: 
+  cCardClientGbox(const char *Name); 
+  virtual bool Init(const char *CfgDir); 
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw); 
+  }; 
+static cCardClientLinkReg<cCardClientGbox> __ncd("gbox");
+
+cCardClientGbox::cCardClientGbox(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,2,DEFAULT_IDLE_TIMEOUT,true)
+{}
+
+bool cCardClientGbox::Init(const char *config) 
+{ 
+  cMutexLock lock(this); 
+  int num=0;
+  if(!ParseStdConfig(config,&num)) return false;
+  return Immediate() ? Login() : true; 
+} 
+bool cCardClientGbox::Login(void) 
+{ 
+  so.Disconnect();
+  if(!so.Bind("127.0.0.1",8003)) return false;
+  return true;
+} 
+
+int cCardClientGbox::GetMsg(int cmd, unsigned char *buff, int len)
+{
+  int n;
+  do {
+    n=so.Read(buff,len);
+    if(n<=0) {
+      if(n==0) PRINTF(L_CC_GBOX,"timeout on GetMsg.");
+      break;
+      }
+    } while(buff[0]!=cmd);
+  return n;
+}
+
+bool cCardClientGbox::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw) 
+{
+  cMutexLock lock(this);
+  if((!so.Connected() && !Login()) || !CanHandle(ecm->caId)) return false;
+  so.Flush();
+
+  const int caid=ecm->caId;
+  const int pid =ecm->ecm_pid;
+
+  static const unsigned char pmt[] = {
+    0x87,
+    0x02,0xb0,0x25,    // tid, sct len
+    0x17,0x74,                 // sid
+    0xc3,0x00,0x00,    // vers, sctnr
+    0xff,0xec,                 // pcr pid
+    0xf0,0x00,         // prg info len
+    0x02, 0xff,0xec, 0xf0,0x00
+    };
+  unsigned char buff[512];
+  memcpy(buff,pmt,sizeof(pmt));
+  buff[4]=ecm->prgId >> 8;
+  buff[5]=ecm->prgId & 0xFF;
+#if APIVERSNUM >= 10500
+  int casys[2];
+#else
+  unsigned short casys[2];
+#endif
+  casys[0]=caid;
+  casys[1]=0;
+  bool streamFlag;
+  int n=GetCaDescriptors(ecm->source,ecm->transponder,ecm->prgId,casys,sizeof(buff)-sizeof(pmt),&buff[sizeof(pmt)],streamFlag);
+  if(n<=0) {
+    PRINTF(L_CC_GBOX,"no CA descriptor for caid %04x sid %d prov %04x",caid,ecm->prgId,ecm->provId);
+    return false;
+    }
+  buff[16]=0xF0 | ((n>>8)&0x0F);
+  buff[17]=n & 0xFF;
+  SetSctLen(&buff[1],sizeof(pmt)-4+n+4);
+  buff[2]=(buff[2]&0x0F)|0xB0;
+
+  if(so.SendTo("127.0.0.1",8004,buff,sizeof(pmt)+n+4)<(int)sizeof(pmt)) {
+    PRINTF(L_CC_GBOX,"failed to send PMT data. GBOX running?");
+    return false;
+    }
+
+  if((n=GetMsg(0x8a,buff,sizeof(buff)))<=0) {
+    PRINTF(L_CC_GBOX,"failed to get ECM port. GBOX running?");
+    return false;
+    }
+  int pidnum=-1;
+  if(n>=2) {
+    for(int i=0 ; i<buff[1]; i++) {
+      if(WORD(buff,2+i*2,0x1FFF)==pid) {
+        pidnum=i;
+        break;
+        }
+      }
+    }
+  if(pidnum<0) {
+    PRINTF(L_CC_ECM,"%s: unable to decode for CAID %04X/PID %04X",name,caid,pid);
+    return false;
+    }
+
+  n=SCT_LEN(data);
+  if(n>=256) {
+    PRINTF(L_CC_ECM,"%s: ECM section too long %d > 255",name,n);
+    return false;
+    }
+  buff[0]=0x88;
+  buff[1]=(pid>>8)&0x1F;
+  buff[2]=pid & 0xFF;
+  buff[3]=n;
+  memcpy(&buff[4],data,n);
+  n+=4;
+  if(so.SendTo("127.0.0.1",8005+pidnum,buff,n)<n) {
+    PRINTF(L_CC_GBOX,"failed to send ECM data. GBOX running?");
+    return false;
+    }
+
+  if(GetMsg(0x89,buff,sizeof(buff))<=0) {
+    PRINTF(L_CC_GBOX,"failed to get CW. GBOX running?");
+    return false;
+    }
+  if(n<17) {
+    PRINTF(L_CC_GBOX,"bad CW answer from GBOX");
+    return false;
+    }
+  memcpy(cw,&buff[1],16);
+  return true;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/newcamd.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/newcamd.c
new file mode 100644 (file)
index 0000000..55e8228
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <crypt.h>
+#include <byteswap.h>
+
+#include <vdr/thread.h>
+
+#include "cc.h"
+#include "network.h"
+#include "misc.h"
+#include "parse.h"
+
+#include <openssl/des.h>
+
+#define CWS_NETMSGSIZE 240
+
+// -- cTripleDes ---------------------------------------------------------------
+
+class cTripleDes {
+private:
+  DES_key_schedule ks1,ks2;
+  //
+  void SetOddParity(unsigned char *key); // key must be 16 bytes!
+protected:
+  unsigned char desKey[16];
+  //
+  void ScheduleKey(void);
+  int PadMessage(unsigned char *data, int len);
+  void Expand(unsigned char *expanded, const unsigned char *normal); // 14 byte key input, 16 byte expanded output
+  void Decrypt(unsigned char *data, int len);
+  const unsigned char *Encrypt(const unsigned char *data, int len, unsigned char *crypt);
+  };
+
+void cTripleDes::SetOddParity(unsigned char *key)
+{
+  DES_set_odd_parity((DES_cblock *)&key[0]); // set odd parity on both keys
+  DES_set_odd_parity((DES_cblock *)&key[8]); // 
+}
+
+void cTripleDes::ScheduleKey(void)
+{
+  DES_key_sched((DES_cblock *)&desKey[0],&ks1);
+  DES_key_sched((DES_cblock *)&desKey[8],&ks2);
+}
+
+void cTripleDes::Expand(unsigned char *expand, const unsigned char *normal)
+{
+  expand[0]  =   normal[0] & 0xfe;
+  expand[1]  = ((normal[0] << 7) | (normal[1] >> 1)) & 0xfe;
+  expand[2]  = ((normal[1] << 6) | (normal[2] >> 2)) & 0xfe;
+  expand[3]  = ((normal[2] << 5) | (normal[3] >> 3)) & 0xfe;
+  expand[4]  = ((normal[3] << 4) | (normal[4] >> 4)) & 0xfe;
+  expand[5]  = ((normal[4] << 3) | (normal[5] >> 5)) & 0xfe;
+  expand[6]  = ((normal[5] << 2) | (normal[6] >> 6)) & 0xfe;
+  expand[7]  =   normal[6] << 1;
+  expand[8]  =   normal[7] & 0xfe;
+  expand[9]  = ((normal[7] << 7)  | (normal[8] >> 1)) & 0xfe;
+  expand[10] = ((normal[8] << 6)  | (normal[9] >> 2)) & 0xfe;
+  expand[11] = ((normal[9] << 5)  | (normal[10] >> 3)) & 0xfe;
+  expand[12] = ((normal[10] << 4) | (normal[11] >> 4)) & 0xfe;
+  expand[13] = ((normal[11] << 3) | (normal[12] >> 5)) & 0xfe;
+  expand[14] = ((normal[12] << 2) | (normal[13] >> 6)) & 0xfe;
+  expand[15] =   normal[13] << 1;
+  SetOddParity(expand);
+}
+
+int cTripleDes::PadMessage(unsigned char *data, int len)
+{
+  DES_cblock padBytes;
+  unsigned char noPadBytes;
+
+  noPadBytes = (8 - ((len - 1) % 8)) % 8;
+  if(len+noPadBytes+1 >= CWS_NETMSGSIZE-8) {
+    PRINTF(L_CC_NEWCAMD,"message overflow in cTripleDes::PadMessage");
+    return -1;
+    }
+
+  srand(time(NULL)); // make sure the random generator is initialized
+  DES_random_key((DES_cblock *)padBytes);
+  memcpy(data+len,padBytes,noPadBytes); len+=noPadBytes;
+  data[len]=XorSum(data+2,len-2);
+  return len+1;
+}
+
+const unsigned char *cTripleDes::Encrypt(const unsigned char *data, int len, unsigned char *crypt)
+{
+  DES_cblock ivec;
+  DES_random_key((DES_cblock *)ivec);
+  memcpy(crypt+len,ivec,sizeof(ivec));
+  DES_ede2_cbc_encrypt(data+2,crypt+2,len-2,&ks1,&ks2,(DES_cblock *)ivec,DES_ENCRYPT);
+  return crypt;
+}
+
+void cTripleDes::Decrypt(unsigned char *data, int len)
+{
+  if((len-2) % 8 || (len-2)<16) {
+    PRINTF(L_CC_NEWCAMD,"warning: encrypted data size mismatch");
+    return;
+    }
+  DES_cblock ivec;
+  len-=sizeof(ivec); memcpy(ivec, data+len, sizeof(ivec));
+  DES_ede2_cbc_encrypt(data+2,data+2,len-2,&ks1,&ks2,(DES_cblock *)ivec,DES_DECRYPT);
+}
+
+// -- cNewCamdClient -----------------------------------------------------------
+
+#define USERLEN        32
+#define PASSWDLEN      32
+#define CWS_FIRSTCMDNO 0xe0
+
+typedef enum {
+  MSG_CLIENT_2_SERVER_LOGIN = CWS_FIRSTCMDNO,
+  MSG_CLIENT_2_SERVER_LOGIN_ACK,
+  MSG_CLIENT_2_SERVER_LOGIN_NAK,
+  MSG_CARD_DATA_REQ,
+  MSG_CARD_DATA,
+  MSG_SERVER_2_CLIENT_NAME,
+  MSG_SERVER_2_CLIENT_NAME_ACK,
+  MSG_SERVER_2_CLIENT_NAME_NAK,
+  MSG_SERVER_2_CLIENT_LOGIN,
+  MSG_SERVER_2_CLIENT_LOGIN_ACK,
+  MSG_SERVER_2_CLIENT_LOGIN_NAK,
+  MSG_ADMIN,
+  MSG_ADMIN_ACK,
+  MSG_ADMIN_LOGIN,
+  MSG_ADMIN_LOGIN_ACK,
+  MSG_ADMIN_LOGIN_NAK,
+  MSG_ADMIN_COMMAND,
+  MSG_ADMIN_COMMAND_ACK,
+  MSG_ADMIN_COMMAND_NAK
+  } net_msg_type_t;
+
+typedef enum {
+  COMMTYPE_CLIENT,
+  COMMTYPE_SERVER
+  } comm_type_t;
+
+struct CustomData {
+  union {
+    struct {
+      unsigned short prgId; // Big-Endian
+      unsigned char data[6];
+      } V525;
+    struct {
+      unsigned int prgId;  // Big-Endian
+      } V520;
+    };
+  };
+
+class cCardClientNewCamd : public cCardClient, private cTripleDes, private cIdSet {
+private:
+  cNetSocket so;
+  unsigned char configKey[14];
+  unsigned short netMsgId;
+  int caId, protoVers, cdLen;
+  bool emmProcessing;
+  char username[USERLEN], password[PASSWDLEN];
+  //
+  void InitVars(void);
+  void InitProtoVers(int vers);
+  bool NextProto(void);
+  void InitCustomData(struct CustomData *cd, const unsigned short PrgId, const unsigned char *data);
+  void PrepareLoginKey(unsigned char *deskey, const unsigned char *rkey, const unsigned char *ckey);
+protected:
+  virtual bool Login(void);
+public:
+  cCardClientNewCamd(const char *Name);
+  // Client Helper functions
+  bool SendMessage(cNetSocket *so, const unsigned char *data, int len, bool UseMsgId, const struct CustomData *cd=0, comm_type_t commType=COMMTYPE_CLIENT);
+  int ReceiveMessage(cNetSocket *so, unsigned char *data, bool UseMsgId, struct CustomData *cd=0, comm_type_t commType=COMMTYPE_CLIENT);
+  bool CmdSend(cNetSocket *so, net_msg_type_t cmd,  comm_type_t commType=COMMTYPE_CLIENT);
+  int CmdReceive(cNetSocket *so, comm_type_t commType=COMMTYPE_CLIENT);
+  // 
+  virtual bool Init(const char *CfgDir);
+  virtual bool CanHandle(unsigned short SysId);  
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw);
+  virtual bool ProcessEMM(int caSys, const unsigned char *data);
+  };
+
+static cCardClientLinkReg<cCardClientNewCamd> __ncd("Newcamd");
+
+cCardClientNewCamd::cCardClientNewCamd(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,20,DEFAULT_IDLE_TIMEOUT)
+{
+  memset(username,0,sizeof(username));
+  memset(password,0,sizeof(password));
+  InitVars();
+  InitProtoVers(525);
+}
+
+void cCardClientNewCamd::InitVars(void)
+{
+  netMsgId=0; caId=-1; emmProcessing=false;
+  ResetIdSet();
+}
+
+void cCardClientNewCamd::InitProtoVers(int vers)
+{
+  switch(vers) {
+    case 525: protoVers=525; cdLen=8; break;
+    default:  protoVers=520; cdLen=4; break;
+    }
+  PRINTF(L_CC_NEWCAMD,"now using protocol version %d (cdLen=%d)",protoVers,cdLen);
+}
+
+bool cCardClientNewCamd::NextProto(void)
+{
+  switch(protoVers) {
+    case 525: InitProtoVers(520); break;
+    default:  return false;
+    }
+  return true;
+}
+
+void cCardClientNewCamd::InitCustomData(struct CustomData *cd, const unsigned short PrgId, const unsigned char *data)
+{
+  if(cd) {
+    switch(protoVers) {
+      case 525:
+        cd->V525.prgId=bswap_16(PrgId);
+        if(data) memcpy(cd->V525.data,data,sizeof(cd->V525.data));
+        else memset(cd->V525.data,0,sizeof(cd->V525.data));
+        break;
+      default:
+        cd->V520.prgId=bswap_32((unsigned int)PrgId);
+        break;
+      }
+    }
+}
+
+void cCardClientNewCamd::PrepareLoginKey(unsigned char *deskey, const unsigned char *rkey, const unsigned char *ckey)
+{
+  unsigned char tmpkey[14];
+  for (int i=0; i<(int)sizeof(tmpkey); i++) { tmpkey[i]=rkey[i]^ckey[i]; }
+  Expand(deskey, tmpkey);
+}
+
+bool cCardClientNewCamd::SendMessage(cNetSocket *so, const unsigned char *data, int len, bool UseMsgId, const struct CustomData *cd, comm_type_t commType)
+{
+  if(len<3||len+cdLen+4>CWS_NETMSGSIZE) {
+    PRINTF(L_CC_NEWCAMD,"bad message size %d in SendMessage",len);
+    return false;
+    }
+  unsigned char netbuf[CWS_NETMSGSIZE];
+  memset(&netbuf[2],0,cdLen+2);
+  memcpy(&netbuf[cdLen+4],data,len);
+  netbuf[cdLen+4+1]=(data[1]&0xf0)|(((len-3)>>8)&0x0f);
+  netbuf[cdLen+4+2]=(len-3)&0xff;
+  len+=4;
+  if(cd) memcpy(&netbuf[4],cd,cdLen);
+  len+=cdLen;
+  if(UseMsgId) {
+    if(commType==COMMTYPE_CLIENT) netMsgId++;
+    netbuf[2]=netMsgId>>8;
+    netbuf[3]=netMsgId&0xff;
+    }
+  if((len=cTripleDes::PadMessage(netbuf,len))<0) {
+    PRINTF(L_CC_NEWCAMD,"PadMessage failed");
+    return false;
+    }
+  if((data=cTripleDes::Encrypt(netbuf,len,netbuf))==0) {
+    PRINTF(L_CC_NEWCAMD,"Encrypt failed");
+    return false;
+    }
+  len+=sizeof(DES_cblock);
+  netbuf[0]=(len-2)>>8;
+  netbuf[1]=(len-2)&0xff;
+  return cCardClient::SendMsg(so,netbuf,len);
+}
+
+int cCardClientNewCamd::ReceiveMessage(cNetSocket *so, unsigned char *data, bool UseMsgId, struct CustomData *cd, comm_type_t commType)
+{
+  unsigned char netbuf[CWS_NETMSGSIZE];
+  int len=cCardClient::RecvMsg(so,netbuf,2);
+  if(len!=2) {
+    if(len>0) PRINTF(L_CC_NEWCAMD,"bad length %d != 2 on message length read",len);
+    return 0;
+    }
+  const int mlen=WORD(netbuf,0,0xFFFF);
+  if(mlen>CWS_NETMSGSIZE-2) {
+   PRINTF(L_CC_NEWCAMD,"receive message buffer overflow");
+   return 0;
+   }
+  len=cCardClient::RecvMsg(so,netbuf+2,mlen);
+  if(len!=mlen) {
+    PRINTF(L_CC_NEWCAMD,"bad length %d != %d on message read",len,mlen);
+    return 0;
+    }
+  len+=2;
+  cTripleDes::Decrypt(netbuf,len); len-=sizeof(DES_cblock);
+  if(XorSum(netbuf+2, len-2)) {
+    PRINTF(L_CC_NEWCAMD,"checksum error");
+    return 0;
+    }
+
+  int returnLen=WORD(netbuf,5+cdLen,0x0FFF)+3;
+  if(cd) memcpy(cd,&netbuf[4],cdLen);
+  if(UseMsgId) {
+    switch(commType) {
+      case COMMTYPE_SERVER:
+        netMsgId=WORD(netbuf,2,0xFFFF);
+        break;
+      case COMMTYPE_CLIENT:
+        if(netMsgId!=WORD(netbuf,2,0xFFFF)) {
+          PRINTF(L_CC_NEWCAMD,"bad msgid %04x != %04x ",netMsgId,WORD(netbuf,2,0xFFFF));
+          return -1;
+          }
+        break;
+      default:
+        PRINTF(L_CC_NEWCAMD,"unknown commType %x",commType);
+        return -1;
+      }
+    }
+  memcpy(data,netbuf+4+cdLen,returnLen);
+  return returnLen;
+}
+
+bool cCardClientNewCamd::CmdSend(cNetSocket *so, net_msg_type_t cmd, comm_type_t commType)
+{
+  unsigned char buffer[3];
+  buffer[0] = cmd; buffer[1] = buffer[2] = 0;
+  return SendMessage(so,buffer,sizeof(buffer),false,0,commType);
+}
+
+int cCardClientNewCamd::CmdReceive(cNetSocket *so, comm_type_t commType)
+{
+  unsigned char buffer[CWS_NETMSGSIZE];
+  if(ReceiveMessage(so,buffer,false,0,commType)!=3) return -1;
+  return buffer[0];
+}
+
+bool cCardClientNewCamd::CanHandle(unsigned short SysId)
+{
+  return (caId>=0 && SysId==caId) || (caId==0x1234 && SysId == 0x1801) || 
+    cCardClient::CanHandle(SysId); 
+//  return (caId>=0 && SysId==caId) || cCardClient::CanHandle(SysId); 
+}
+
+bool cCardClientNewCamd::Init(const char *config)
+{
+  cMutexLock lock(this);
+  int num=0;
+  char key[29];
+  const char *tmp=key;
+  if(!ParseStdConfig(config,&num)
+     || sscanf(&config[num],":%31[^:]:%31[^:]:%28[^:]",username,password,key)!=3
+     || GetHex(tmp,configKey,sizeof(configKey),false)!=14) return false;
+  char str[32];
+  PRINTF(L_CC_CORE,"%s: username=%s password=%s key=%s",name,username,password,HexStr(str,configKey,14));
+  return Immediate() ? Login() : true;
+}
+
+bool cCardClientNewCamd::Login(void)
+{
+  so.Disconnect();
+  if(!so.Connect(hostname,port)) return false;
+
+  InitVars();
+  unsigned char randData[14];
+  if(so.Read(randData,sizeof(randData))!=14) {
+    PRINTF(L_CC_NEWCAMD,"no connect answer from %s:%d",hostname,port);
+    so.Disconnect();
+    return false;
+    }
+
+  char *crPasswd=crypt(password,"$1$abcdefgh$");
+  unsigned char buffer[CWS_NETMSGSIZE];
+  const int userLen=strlen(username)+1;
+  const int passLen=strlen(crPasswd)+1;
+  
+  // prepare the initial login message
+  buffer[0] = MSG_CLIENT_2_SERVER_LOGIN;
+  buffer[1] = 0;
+  buffer[2] = userLen+passLen;
+  memcpy(&buffer[3],username,userLen);
+  memcpy(&buffer[3]+userLen,crPasswd,passLen);
+
+  // XOR configKey with randData and expand the 14 byte result -> 16 byte
+  PrepareLoginKey(desKey,randData,configKey);
+  cTripleDes::ScheduleKey();
+
+  // set NewCS client identification
+  struct CustomData cd;
+  InitCustomData(&cd,0x5644,0);
+
+  if(!SendMessage(&so,buffer,buffer[2]+3,true,&cd) || CmdReceive(&so)!=MSG_CLIENT_2_SERVER_LOGIN_ACK) {
+    PRINTF(L_CC_NEWCAMD,"failed to login to cardserver for username %s (proto %d)",username,protoVers);
+    if(NextProto()) return Login();
+    return false;
+    }
+
+  // create the session key (for usage later)
+  unsigned char tmpkey[14];
+  memcpy(tmpkey, configKey, sizeof(tmpkey));
+  const int passStrLen=strlen(crPasswd);
+  for(int i=0; i<passStrLen; ++i) tmpkey[i%14]^=crPasswd[i];
+
+  cTripleDes::Expand(desKey,tmpkey); // expand 14 byte key -> 16 byte
+  cTripleDes::ScheduleKey();
+
+  if(!CmdSend(&so,MSG_CARD_DATA_REQ) || ReceiveMessage(&so,buffer,false)<=0) return false;
+  if(buffer[0] == MSG_CARD_DATA) {
+    caId=(buffer[4]<<8)+buffer[5];
+    LBSTARTF(L_CC_LOGIN);
+    char str[32], str2[32];
+    LBPUT("%s: CaID=%04x admin=%d srvUA=%s",name,caId,buffer[3]==1,HexStr(str,&buffer[6],8));
+    if(!CheckNull(&buffer[6],8)) {
+      emmProcessing=true;
+      switch(caId>>8) {
+        case 0x17:
+        case 0x06: SetCard(new cCardIrdeto(buffer[6+4],&buffer[6+5])); break;
+        case 0x01: SetCard(new cCardSeca(&buffer[6+2])); break;
+        case 0x0b: SetCard(new cCardConax(&buffer[6+1])); break;
+        case 0x09: SetCard(new cCardNDS(&buffer[6+4])); break;
+        case 0x05: SetCard(new cCardViaccess(&buffer[6+3])); break;
+        case 0x0d: SetCard(new cCardCryptoworks(&buffer[6+3])); break;
+        case 0x12: 
+        case 0x18: if(caId==0x1801 || caId==0x1234 ) {
+                     SetCard(new cCardNagra2(&buffer[6+4]));
+                     break;
+                     }
+                   // fall through to default
+        default:
+          LBPUT(" (unhandled)");
+          break;
+        }
+      }
+    LBPUT(" provider");
+    for(int i=(buffer[14]-1)*11; i>=0; i-=11) {
+      LBPUT(" %s/%s",HexStr(str2,&buffer[15+i],3),HexStr(str,&buffer[18+i],8));
+      if(!CheckNull(&buffer[18+i],8)) {
+        switch(caId>>8) {
+          case 0x17:
+          case 0x06: AddProv(new cProviderIrdeto(buffer[18+i+4],&buffer[18+i+5])); break;
+          case 0x01: AddProv(new cProviderSeca(&buffer[15+i+1],&buffer[18+i+4])); break;
+          case 0x0b: AddProv(new cProviderConax(&buffer[18+i+1])); break;
+          case 0x09: AddProv(new cProviderNDS(&buffer[18+i+4])); break;
+          case 0x05: AddProv(new cProviderViaccess(&buffer[15+i],&buffer[18+i+4])); break;
+          case 0x0d: AddProv(new cProviderCryptoworks(&buffer[18+i+3])); break;
+          default:
+            LBPUT(" <unhandled>");
+            break;
+          }
+        }
+      }
+    LBEND();
+    if(emmProcessing && !emmAllowed)
+      PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+    }
+  return true;
+}
+
+bool cCardClientNewCamd::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  cMutexLock lock(this);
+  if((!so.Connected() && !Login()) || !CanHandle(ecm->caId)) return false;
+  so.Flush();
+
+  struct CustomData cd;
+  InitCustomData(&cd,(unsigned short)ecm->prgId,0);
+  if(!SendMessage(&so,data,SCT_LEN(data),true,&cd)) return false;
+  unsigned char buffer[CWS_NETMSGSIZE];
+  switch(ReceiveMessage(&so,buffer,true)) {
+    case 19: // ecm was decoded
+      // check for zero cw, as newcs doesn't send both cw's every time
+      if(!CheckNull(buffer+3+0,8)) memcpy(cw+0,buffer+3+0,8);
+      if(!CheckNull(buffer+3+8,8)) memcpy(cw+8,buffer+3+8,8);
+      return true;
+    case 3:
+      PRINTF(L_CC_ECM,"%s: card was not able to decode the channel",name);
+      break;
+    default:
+      PRINTF(L_CC_NEWCAMD,"warning an unexpected error occurred");
+      break;
+    }
+  return false;
+}
+
+bool cCardClientNewCamd::ProcessEMM(int caSys, const unsigned char *data)
+{
+  if(emmProcessing && emmAllowed) {
+    cMutexLock lock(this);
+    cAssembleData ad(data);
+    if(MatchAndAssemble(&ad,0,0)) {
+      while((data=ad.Assembled())) {
+        int len=SCT_LEN(data);
+        int id=msEMM.Get(data,len,0);
+        if(id>0) {
+          if(SendMessage(&so,data,len,true,0)) {
+            unsigned char buffer[CWS_NETMSGSIZE];
+            ReceiveMessage(&so,buffer,true);
+            }
+          msEMM.Cache(id,true,0);
+          }
+        }
+      return true;
+      }
+    }
+  return false;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/radegast.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cardclient/radegast.c
new file mode 100644 (file)
index 0000000..e497df0
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cc.h"
+#include "parse.h"
+#include "network.h"
+#include "version.h"
+
+// -- cCardClientRadegast ------------------------------------------------------
+
+#define MAXCAIDS 16
+
+class cCardClientRadegast : public cCardClient, private cIdSet {
+private:
+  cNetSocket so;
+  bool emmProcessing;
+  int caids[MAXCAIDS], numCaids;
+  //
+  void InitVars(void);
+  void SetLength(unsigned char *buff, int len);
+  int GetLength(const unsigned char *buff);
+  int GetNanoStart(const unsigned char *buff);
+  int GetMsgLength(const unsigned char *buff);
+  int GetMaxLength(const unsigned char *buff);
+  void StartMsg(unsigned char *buff, int cmd);
+  bool CheckLength(const unsigned char *buff, int len);
+  void AddNano(unsigned char *buff, int nano, int len, int value);
+  void AddNano(unsigned char *buff, int nano, int len, const unsigned char *data);
+  bool Send(const unsigned char *buff);
+  int Recv(unsigned char *buff, int len);
+protected:
+  virtual bool Login(void);
+public:
+  cCardClientRadegast(const char *Name);
+  virtual bool Init(const char *config);
+  virtual bool CanHandle(unsigned short SysId);  
+  virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+  virtual bool ProcessEMM(int caSys, const unsigned char *data);
+  };
+
+static cCardClientLinkReg<cCardClientRadegast> __rdg("Radegast");
+
+cCardClientRadegast::cCardClientRadegast(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
+{
+  InitVars();
+}
+
+void cCardClientRadegast::InitVars(void)
+{
+  emmProcessing=false; numCaids=0;
+}
+
+bool cCardClientRadegast::CanHandle(unsigned short SysId)
+{
+  cMutexLock lock(this);
+  if(numCaids<=0) return cCardClient::CanHandle(SysId);
+  for(int i=0; i<numCaids; i++) if(caids[i]==SysId) return true;
+  return false;
+}
+
+bool cCardClientRadegast::Init(const char *config)
+{
+  cMutexLock lock(this);
+  so.Disconnect();
+  return ParseStdConfig(config) && (Immediate() ? Login() : true);
+}
+
+void cCardClientRadegast::SetLength(unsigned char *buff, int len)
+{
+  if(buff[0]<=0x90) buff[1]=min(255,len);
+  else { buff[1]=len>>8; buff[2]=len&0xFF; }
+}
+
+int cCardClientRadegast::GetLength(const unsigned char *buff)
+{
+  return (buff[0]<=0x90) ? buff[1] : ((buff[1]<<8)+buff[2]);
+}
+
+int cCardClientRadegast::GetNanoStart(const unsigned char *buff)
+{
+  return (buff[0]<=0x90) ? 2 : 3;
+}
+
+int cCardClientRadegast::GetMsgLength(const unsigned char *buff)
+{
+  return GetLength(buff)+GetNanoStart(buff);
+}
+
+int cCardClientRadegast::GetMaxLength(const unsigned char *buff)
+{
+  return (buff[0]<=0x90) ? 0xFF : 0xFFFF;
+}
+
+void cCardClientRadegast::StartMsg(unsigned char *buff, int cmd)
+{
+  buff[0]=cmd;
+  SetLength(buff,0);
+}
+
+bool cCardClientRadegast::CheckLength(const unsigned char *buff, int len)
+{
+  int l=GetLength(buff)+len+2;
+  int max=GetMaxLength(buff);
+  if(len>255) PRINTF(L_CC_RDGD,"cmd %02x: nano too long %d > 255",buff[0],len);
+  else if(l>max) PRINTF(L_CC_RDGD,"cmd %02x: msg too long %d > %d",buff[0],l,max);
+  return l<=max;
+}
+
+void cCardClientRadegast::AddNano(unsigned char *buff, int nano, int len, int value)
+{
+  unsigned char hex[4];
+  for(int i=0; i<len; i++) hex[i]=value >> ((len-i-1)*8);
+  AddNano(buff,nano,len,hex);
+}
+
+void cCardClientRadegast::AddNano(unsigned char *buff, int nano, int len, const unsigned char *data)
+{
+  int pos=GetLength(buff), off=GetNanoStart(buff);
+  if(pos<GetMaxLength(buff)) {
+    buff[off+pos]=nano;
+    buff[off+pos+1]=len;
+    memcpy(&buff[off+pos+2],data,len);
+    pos+=len+2;
+    }
+  SetLength(buff,pos);
+}
+
+bool cCardClientRadegast::Send(const unsigned char *buff)
+{
+  return SendMsg(&so,buff,GetMsgLength(buff));
+}
+
+int cCardClientRadegast::Recv(unsigned char *buff, int len)
+{
+  int n=RecvMsg(&so,buff,len);
+  if(n<1) return n;
+  int k=GetNanoStart(buff);
+  if(n<k) {
+    PRINTF(L_CC_RDGD,"bad length %d < %d on cmd read",n,k);
+    return -1;
+    }
+  k=GetMsgLength(buff);
+  if(n<k) {
+    PRINTF(L_CC_RDGD,"bad length %d < %d on nano read",n,k);
+    return -1;
+    }
+  return n;
+}
+
+bool cCardClientRadegast::Login(void)
+{
+  so.Disconnect();
+  if(!so.Connect(hostname,port)) return false;
+  PRINTF(L_CC_LOGIN,"%s: connected to %s:%d",name,hostname,port);
+
+  InitVars();
+  unsigned char buff[512];
+  char hello[32];
+  snprintf(hello,sizeof(hello),"rdgd/vdr-sc-%s",ScVersion);
+  StartMsg(buff,0x90);                 // RDGD_MSG_CLIENT_HELLO
+  AddNano(buff,1,strlen(hello),(unsigned char *)hello);        // RDGD_NANO_DESCR
+  int n;
+  if(!Send(buff) || (n=Recv(buff,sizeof(buff)))<0) return false;
+  if(n>0 && buff[0]==0x91) {
+    PRINTF(L_CC_RDGD,"got server hello, assuming V4 mode");
+    StartMsg(buff,0x94);               // RDGD_MSG_CLIENT_CAP_REQ;
+    if(!Send(buff) || (n=Recv(buff,sizeof(buff)))<0) return false;
+    if(n>0 && buff[0]==0x95) {
+      LBSTARTF(L_CC_LOGIN);
+      LBPUT("radegast: got caps");
+      int caid;
+      for(int l=GetNanoStart(buff); l<n; l+=buff[l+1]+2) {
+        switch(buff[l]) {
+          case 0xE2:
+            LBPUT(" VERS %s",(char *)&buff[l+2]);
+            break;
+          case 0xE4: // CAP_NANO_CAID
+            if(numCaids>=MAXCAIDS) { l=n; break; } //stop processing
+            caid=(buff[l+2]<<8)+buff[l+3];
+            LBPUT(" CAID %04X",caid);
+            caids[numCaids++]=caid;
+            // XXX we should have EMM processing setup here, but as we don't
+            // XXX get any ua/sa we cannot filter EMM anyways
+            break;
+          case 0xE5: // CAP_NANO_PROVID
+            for(int i=0; i<buff[l+1]; i+=3)
+              LBPUT("/%02X%02X%02X",buff[l+2+i],buff[l+2+i+1],buff[l+2+i+2]);
+            break;
+          }
+        }
+      LBEND();
+      }
+    }
+  else PRINTF(L_CC_RDGD,"no server hello, assuming old mode");
+  if(emmProcessing && !emmAllowed) 
+    PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+  return true; 
+}
+
+bool cCardClientRadegast::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+  cMutexLock lock(this);
+  so.Flush();
+  int len=SCT_LEN(source);
+  int keynr=-1;
+  switch(ecm->caId>>8) {
+    case 0x01: // Seca
+      keynr=source[7]&0x0F; break;
+    case 0x05: // Viaccess
+      keynr=source[4]&0x0F; break;
+    }
+  unsigned char buff[512], tmp[10];
+  StartMsg(buff,1);                    // CMD_ECM_KEY_ASK
+  AddNano(buff,2,1,ecm->caId>>8);       // ECM_NANO_CAID_INDEX (old)
+  AddNano(buff,10,2,ecm->caId);                // ECM_NANO_CAID
+  sprintf((char *)tmp,"%08X",ecm->provId);
+  AddNano(buff,6,8,tmp);               // ECM_NANO_PROVIDER
+  if(keynr>=0) {
+    sprintf((char *)tmp,"%04X",keynr);
+    AddNano(buff,7,4,tmp);             // ECM_NANO_KEYNO
+    }
+  if(!CheckLength(buff,len)) return false;
+  AddNano(buff,3,len,source);          // ECM_NANO_PACKET
+
+  if(!Send(buff) || (len=Recv(buff,sizeof(buff)))<=0) return false;
+  if(buff[0]==2) {
+    for(int l=GetNanoStart(buff); l<len; l+=buff[l+1]+2) {
+      switch(buff[l]) {
+        case 0x05:
+          if(buff[l+1]==16) {
+            // check for zero cw, as someone may not send both cw's every time
+            if(!CheckNull(buff+l+ 2,8)) memcpy(cw+0,buff+l+ 2,8);
+            if(!CheckNull(buff+l+10,8)) memcpy(cw+8,buff+l+10,8);
+            return true;
+            }
+          else PRINTF(L_CC_RDGD,"wrong cw length %d from server",buff[l+1]);
+          break;
+        case 0x04:
+          PRINTF(L_CC_ECM,"%s: key not available from server",name);
+          break;
+        default:
+          PRINTF(L_CC_RDGD,"unknown nano %02x in ECM response",buff[l]);
+          break;
+        }
+      }
+    }
+  else PRINTF(L_CC_RDGD,"bad ECM response from server %02x != 02",buff[0]);
+  return false;
+}
+
+bool cCardClientRadegast::ProcessEMM(int caSys, const unsigned char *data)
+{
+  if(emmProcessing && emmAllowed) {
+    cMutexLock lock(this);
+    int upd;
+    cProvider *p;
+    cAssembleData ad(data);
+    if(MatchAndAssemble(&ad,&upd,&p)) {
+      while((data=ad.Assembled())) {
+        int len=SCT_LEN(data);
+        int id=msEMM.Get(data,len,0);
+        if(id>0) {
+          unsigned char buff[512];
+          StartMsg(buff,0x41);                 //
+          AddNano(buff,0x42,2,caSys);          // EMM_CAID
+          if(p) {
+            unsigned char tmp[10];
+            sprintf((char *)tmp,"%08X",(int)p->ProvId());
+            AddNano(buff,0x46,8,tmp);          // EMM_PROVID
+            }
+/*
+          if(upd==2 || upd==3) {
+            AddNano(buff,0x44,2,(unsigned char *)"aa");        // EMM_ADDR_VAL
+            }
+*/
+          AddNano(buff,0x45,1,upd==3 ? 0x11 : (upd==2 ? 0x12 : 0x13)); // EMM_ADDR_TYPE
+          if(CheckLength(buff,len)) {
+            AddNano(buff,0x43,len,data);       // EMM_CA_DATA
+            Send(buff);
+            }
+          msEMM.Cache(id,true,0);
+          }
+        }
+      return true;
+      }
+    }
+  return false;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.c
new file mode 100644 (file)
index 0000000..390760b
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "system-common.h"
+#include "crypto.h"
+#include "data.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_CONAX         0x0B00
+
+#define SYSTEM_NAME          "Conax"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CONAX)
+
+#define L_SYS        3
+#define L_SYS_ALL    LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+  "conax",
+  { L_SYS_DEFNAMES }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cSystemConax ---------------------------------------------------------------
+
+class cSystemConax : public cSystem {
+private:
+  cRSA rsa;
+  //
+  void ParseECM(unsigned char *buf, int len);
+public:
+  cSystemConax(void);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+  };
+
+cSystemConax::cSystemConax(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{}
+
+void cSystemConax::ParseECM(unsigned char *buf, int len)
+{
+  int i=0;
+  while(i<len) {
+    int nano=buf[i++];
+    int nanolen=buf[i++];
+    
+    switch(nano) {
+      case 0x80:
+      case 0x82:
+        ParseECM(buf+i+3,nanolen-3); break;
+      case 0x81:
+        ParseECM(buf+i+2,nanolen-2); break;
+      case 0x30:
+       memcpy(cw  ,buf+i+10,8);
+       memcpy(cw+8,buf+i+2 ,8);
+        break;
+      }
+    i+=nanolen;
+    }
+}
+
+bool cSystemConax::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+  int length=source[4]-2;
+  const int keyid=source[6];
+  const int nano=source[5];
+  source+=7;
+
+  cKeySnoop ks(this,'C',keyid,'M');
+  cPlainKey *pk;
+  cBN mod, exp;
+  if(!(pk=keys.FindKey('C',keyid,'E',-1))) {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %02X E key",keyid);
+    return false;
+    }
+  pk->Get(exp);
+  if(!(pk=keys.FindKey('C',keyid,'M',-1))) {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %02X M key",keyid);
+    return false;
+    }
+  pk->Get(mod);
+
+  for(int i=length ; i>0 ;) {
+    int index=i-64;
+    if(index<0) index=0;
+    if(rsa.RSA(source+index,source+index,64,exp,mod,false)<=0) {
+      if(doLog) PRINTF(L_SYS_CRYPTO,"RSA failed");
+      return false;
+      }
+    if(nano==0x63) {
+      // nano 0x63 block only decodes to 63 bytes
+      memmove(source+index,source+index+1,length-(index+1));
+      length-=1;
+      }
+    i-=(i%60 && index) ? i%60 : 64;
+    }
+
+  static const unsigned char hash[] = { 0x05,0x00,0x05 };
+  if(memcmp(hash,source+2,3) || memcmp(source+5,source+length-5,3)) {
+    if(doLog) PRINTF(L_SYS_ECM,"signature check failed");
+    return false;
+    }
+
+  ParseECM(source+10,length-10);
+  ks.OK(pk);
+  return true;
+}
+
+// -- cPlainKeyConax --------------------------------------------------------------
+
+#define PLAINLEN_CONAX 64
+
+class cPlainKeyConax : public cBNKey {
+protected:
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeyConax(bool Super);
+  virtual bool Parse(const char *line);
+  };
+
+static cPlainKeyTypeReg<cPlainKeyConax,'C'> KeyReg;
+
+cPlainKeyConax::cPlainKeyConax(bool Super)
+:cBNKey(Super)
+{}
+
+bool cPlainKeyConax::Parse(const char *line)
+{
+  unsigned char sid, skey[PLAINLEN_CONAX];
+  if(GetChar(line,&type,1) && GetHex(line,&sid,1) &&
+     GetChar(line,&keynr,1) && GetHex(line,skey,PLAINLEN_CONAX)) {
+    type=toupper(type); keynr=toupper(keynr); id=sid;
+    SetBinKey(skey,PLAINLEN_CONAX);
+    return true;
+    }
+  return false;
+}
+
+cString cPlainKeyConax::PrintKeyNr(void)
+{
+  return cString::sprintf("%c",keynr);
+}
+
+// -- cSystemLinkConax ---------------------------------------------------------
+
+class cSystemLinkConax : public cSystemLink {
+public:
+  cSystemLinkConax(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemConax; }
+  };
+
+static cSystemLinkConax staticInit;
+
+cSystemLinkConax::cSystemLinkConax(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkConax::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/conax/conax.mk
new file mode 100644 (file)
index 0000000..27d38dd
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Conax
+#
+TARGET = conax
+OBJS   = conax.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.c
new file mode 100644 (file)
index 0000000..8a37fa6
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <typeinfo>
+
+#include <vdr/channels.h>
+#include <vdr/sources.h>
+
+#include "system.h"
+#include "system-common.h"
+#include "data.h"
+#include "misc.h"
+
+#define SYSTEM_NAME          "ConstCW"
+#define SYSTEM_PRI           -20
+
+// -- cPlainKeyConstCw ----------------------------------------------------------------
+
+#define PLAINLEN_CW 16
+
+class cPlainKeyConstCw : public cHexKey {
+private:
+  int prgId, source, transponder;
+  int freq;
+  char pol;
+protected:
+  virtual int IdSize(void) { return 4; }
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeyConstCw(bool Super);
+  virtual bool Parse(const char *line);
+  bool Matches(const cEcmInfo *ecm);
+  };
+
+static cPlainKeyTypeReg<cPlainKeyConstCw,'X'> KeyReg;
+
+cPlainKeyConstCw::cPlainKeyConstCw(bool Super)
+:cHexKey(Super)
+{
+  freq=-1;
+}
+
+bool cPlainKeyConstCw::Matches(const cEcmInfo *ecm)
+{
+  return ecm->prgId==prgId && ecm->source==source && ecm->transponder==transponder;
+}
+
+bool cPlainKeyConstCw::Parse(const char *line)
+{
+  unsigned char caid[2], skey[PLAINLEN_CW];
+  if(GetChar(line,&type,1) && GetHex(line,caid,2)) {
+    int num;
+    char srcBuf[16];
+    if(sscanf(line," %d:%c:%15[^:]:%d%n",&freq,&pol,srcBuf,&prgId,&num)==4) {
+      source=cSource::FromString(srcBuf);
+      transponder=freq;
+      while(transponder>20000) transponder/=1000;
+      if(cSource::IsSat(source)) transponder=cChannel::Transponder(transponder,pol);
+      line+=num;
+      if(GetHex(line,skey,PLAINLEN_CW)) {
+        type=toupper(type);
+        id=Bin2Int(caid,2);
+        keynr=0;
+        SetBinKey(skey,PLAINLEN_CW);
+        return true;
+        }
+      }
+    }
+  return false;
+}
+
+cString cPlainKeyConstCw::PrintKeyNr(void)
+{
+  return freq<0 ? "" : cString::sprintf("%d:%c:%s:%d",freq,pol,*cSource::ToString(source),prgId);
+}
+
+// -- cSystemConstCw ------------------------------------------------------------------
+
+class cSystemConstCw : public cSystem {
+public:
+  cSystemConstCw(void);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+  };
+
+cSystemConstCw::cSystemConstCw(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  constant=true;
+}
+
+bool cSystemConstCw::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+  cKeySnoop ks(this,'X',ecm->caId,0);
+  cPlainKey *pk=0;
+  while((pk=keys.FindKey('X',ecm->caId,0,16,pk))) {
+    if(typeid(*pk)==typeid(cPlainKeyConstCw) && ((cPlainKeyConstCw *)pk)->Matches(ecm)) {
+      pk->Get(cw);
+      ks.OK(pk);
+      return true;
+      }
+    }
+  return false;
+}
+
+// -- cSystemLinkConstCw ------------------------------------------------------------
+
+class cSystemLinkConstCw : public cSystemLink {
+public:
+  cSystemLinkConstCw(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemConstCw; }
+  };
+
+static cSystemLinkConstCw staticInit;
+
+cSystemLinkConstCw::cSystemLinkConstCw(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkConstCw::CanHandle(unsigned short SysId)
+{
+  return keys.FindKeyNoTrig('X',SysId,0,16)!=0;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/constcw/constcw.mk
new file mode 100644 (file)
index 0000000..845f92d
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Const CW
+#
+TARGET = constcw
+OBJS   = constcw.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.c
new file mode 100644 (file)
index 0000000..1fc0d6b
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <byteswap.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "helper.h"
+#include "crypto.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_CRYPTOWORKS   0x0D00
+
+#define SYSTEM_NAME          "Cryptoworks"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CRYPTOWORKS)
+
+#define L_SYS        4
+#define L_SYS_ALL    LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+  "cryptoworks",
+  { L_SYS_DEFNAMES }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+#define DUMPNANO(t,p,n) \
+  LBSTART(L_SYS_VERBOSE); \
+  LBPUT("%s",(t)); \
+  unsigned char *__p=(unsigned char *)(p); \
+  int __n=(n); \
+  for(int i=0; i<__n;) { \
+    LBFLUSH(); \
+    LBPUT("%02X %02X -",__p[i],__p[i+1]); \
+    i+=2; \
+    for(int l=__p[i-1]; l>0; l--) { LBPUT(" %02X",__p[i]); i++; }\
+    if(i>__n) { LBFLUSH(); LBPUT("length exceeded %d != %d",i,__n); } \
+    } \
+  LBEND();
+
+// -- cCwDes -------------------------------------------------------------------
+
+static const unsigned char cryptoPC1[] = {
+   53,46,39,32,50,43,36,29,
+   22,15, 8, 1,51,44,37,30,
+   23,16, 9, 2,52,45,38,31,
+   24,17,10, 3,53,46,39,32,
+   25,18,11, 4,56,49,42,35,
+   28,21,14, 7,55,48,41,34,
+   27,20,13, 6,54,47,40,33,
+   26,19,12, 5,25,18,11, 4
+  };
+
+static const unsigned char cryptoPC2[] = {
+  18,21,15,28, 5, 9,   7,32,19,10,25,14,
+  27,23,16, 8,30,12,  20,11,31,24,17, 6,
+  49,60,39,45,55,63,  38,48,59,53,41,56,
+  52,57,47,64,42,61,  54,50,58,44,37,40,
+  };
+
+#define shiftin(V,R,n) ((V<<1)+(((R)>>(n))&1))
+#define rol28(V,n) (V<<(n) | ((V&0x0fffffffL)>>(28-(n))))
+#define ror28(V,n) (V>>(n) | ((V&0xfffffff0L)<<(28-(n))))
+
+#define DESROUND(C,D,T) { \
+   unsigned int s=0; \
+   for(int j=7, k=0; j>=0; j--) { \
+     unsigned int v=0, K=0; \
+     for(int t=5; t>=0; t--, k++) { \
+       v=shiftin(v,T,E[k]); \
+       if(PC2[k]<33) K=shiftin(K,C,32-PC2[k]); \
+       else          K=shiftin(K,D,64-PC2[k]); \
+       } \
+     s=(s<<4) + S[7-j][v^K]; \
+     } \
+   T=0; \
+   for(int j=31; j>=0; j--) T=shiftin(T,s,P[j]); \
+   }
+
+class cCwDes : public cDes {
+private:
+  void PrepKey(unsigned char *out, const unsigned char *key) const;
+  void PrepKey48(unsigned char *out, const unsigned char *key22, unsigned char algo) const;
+public:
+  cCwDes(void);
+  void CwDes(unsigned char *data, const unsigned char *key22, int mode) const;
+  void CwL2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const;
+  void CwR2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const;
+  };
+
+cCwDes::cCwDes(void)
+:cDes(cryptoPC1,cryptoPC2)
+{}
+
+void cCwDes::PrepKey(unsigned char *out, const unsigned char *key) const
+{
+  if(key!=out) memcpy(out,key,8);
+  Permute(out,PC1,64);
+}
+
+void cCwDes::PrepKey48(unsigned char *out, const unsigned char *key22, unsigned char algo) const
+{
+  memset(out,0,8);
+  memcpy(out,&key22[16],6);
+  int ctr=7-(algo&7);
+  for(int i=8; i>=2 && i>ctr; i--) out[i-2]=key22[i];
+}
+
+void cCwDes::CwDes(unsigned char *data, const unsigned char *key22, int mode) const
+{
+  unsigned char mkey[8];
+  PrepKey(mkey,key22+9);
+  LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key DES:");
+  unsigned int C=UINT32_BE(mkey  );
+  unsigned int D=UINT32_BE(mkey+4);
+  unsigned int L=UINT32_BE(data  );
+  unsigned int R=UINT32_BE(data+4);
+  if(!(mode&DES_RIGHT)) {
+    for(int i=15; i>=0; i--) {
+      C=rol28(C,LS[15-i]); D=rol28(D,LS[15-i]);
+      unsigned int T=R;
+      DESROUND(C,D,T);
+      T^=L; L=R; R=T;
+      }
+    }
+  else {
+    for(int i=15; i>=0; i--) {
+      unsigned int T=R;
+      DESROUND(C,D,T);
+      T^=L; L=R; R=T;
+      C=ror28(C,LS[i]); D=ror28(D,LS[i]);
+      }
+    }
+  BYTE4_BE(data  ,L);
+  BYTE4_BE(data+4,R);
+}
+
+void cCwDes::CwL2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const
+{
+  unsigned char mkey[8];
+  PrepKey48(mkey,key22,algo);
+  PrepKey(mkey,mkey);
+  LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key L2:");
+  unsigned int C=UINT32_BE(mkey  );
+  unsigned int D=UINT32_BE(mkey+4);
+  unsigned int L=UINT32_BE(data  );
+  unsigned int R=UINT32_BE(data+4);
+  for(int i=1; i>=0; i--) {
+    C=rol28(C,1); D=rol28(D,1);
+    unsigned int T=R;
+    DESROUND(C,D,T);
+    T^=L; L=R; R=T;
+    PRINTF(L_SYS_VERBOSE,"round %d key L2: %08x %08x",i,C,D);
+    }
+  BYTE4_BE(data  ,L);
+  BYTE4_BE(data+4,R);
+}
+
+void cCwDes::CwR2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const
+{
+  unsigned char mkey[8];
+  PrepKey48(mkey,key22,algo);
+  PrepKey(mkey,mkey);
+  LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key R2:");
+  unsigned int C=UINT32_BE(mkey  );
+  unsigned int D=UINT32_BE(mkey+4);
+  unsigned int L=UINT32_BE(data  );
+  unsigned int R=UINT32_BE(data+4);
+  for(int i=1; i>=0; i--) {
+    C=rol28(C,15); D=rol28(D,15);
+    }
+  for(int i=1; i>=0; i--) {
+    unsigned int T=R;
+    DESROUND(C,D,T);
+    T^=L; L=R; R=T;
+    C=ror28(C,1); D=ror28(D,1);
+    }
+  BYTE4_BE(data  ,R);
+  BYTE4_BE(data+4,L);
+}
+
+// -- cCryptoworks -------------------------------------------------------------
+
+class cCryptoworks {
+private:
+  cCwDes des;
+  cRSA rsa;
+  cBN exp;
+  //
+  void EncDec(unsigned char *data, const unsigned char *key22, unsigned char algo, int mode);
+public:
+  cCryptoworks(void);
+  void Signature(const unsigned char *data, int len, const unsigned char *key22, unsigned char *sig);
+  void DecryptDES(unsigned char *data, unsigned char algo, const unsigned char *key22);
+  bool DecryptRSA(unsigned char *data, int len, unsigned char algo, const unsigned char *key22, BIGNUM *mod);
+  };
+
+cCryptoworks::cCryptoworks(void)
+{
+  BN_set_word(exp,2);
+}
+
+void cCryptoworks::EncDec(unsigned char *data, const unsigned char *key22, unsigned char algo, int mode)
+{
+  LDUMP(L_SYS_VERBOSE,data,8,"encdec in :");
+  PRINTF(L_SYS_VERBOSE,"algo %d",algo);
+  LDUMP(L_SYS_VERBOSE,key22,22,"encdec key:");
+  des.CwL2Des(data,key22,algo);
+  des.CwDes(data,key22,mode);
+  des.CwR2Des(data,key22,algo);
+  LDUMP(L_SYS_VERBOSE,data,8,"encdec out:");
+}
+
+void cCryptoworks::Signature(const unsigned char *data, int len, const unsigned char *key22, unsigned char *sig)
+{
+  PRINTF(L_SYS_VERBOSE,"sig start");
+  int algo=data[0]&7;
+  if(algo==7) algo=6;
+  memset(sig,0,8);
+  int j=0;
+  bool first=true;
+  for(int i=0; i<len; i++) {
+    sig[j]^=data[i];
+    if(++j>7) {
+      LDUMP(L_SYS_VERBOSE,sig,8,"sig buf");
+      if(first) {
+        des.CwL2Des(sig,key22,algo);
+        LDUMP(L_SYS_VERBOSE,sig,8,"sig -> ");
+        }
+      des.CwDes(sig,key22,DES_LEFT);
+      j=0; first=false;
+      LDUMP(L_SYS_VERBOSE,sig,8,"sig -> ");
+      }
+    }
+  if(j>0) {
+    LDUMP(L_SYS_VERBOSE,sig,8,"sig buf final ");
+    des.CwDes(sig,key22,DES_LEFT);
+    LDUMP(L_SYS_VERBOSE,sig,8,"sig        -> ");
+    }
+  des.CwR2Des(sig,key22,algo);
+  LDUMP(L_SYS_VERBOSE,sig,8,"sig calc ");
+}
+
+void cCryptoworks::DecryptDES(unsigned char *data, unsigned char algo, const unsigned char *key22)
+{
+  algo&=7;
+  if(algo<7) {
+    EncDec(data,key22,algo,DES_RIGHT);
+    }
+  else {
+    unsigned char k[22], t[8];
+    memcpy(k,key22,22);
+    for(int i=0; i<3; i++) {
+      EncDec(data,k,algo,i&1);
+      memcpy(t,k,8); memcpy(k,k+8,8); memcpy(k+8,t,8);
+      }
+    }
+}
+
+bool cCryptoworks::DecryptRSA(unsigned char *data, int len, unsigned char algo, const unsigned char *key22, BIGNUM *mod)
+{
+  unsigned char buf[64], *mask=AUTOMEM(len);
+
+  LDUMP(L_SYS_VERBOSE,data+len,8,"rsa in:");
+  memcpy(buf,data+len,8);
+  EncDec(buf,key22,algo,DES_LEFT);
+  buf[0]|=0x80;
+  if((algo&0x18)<0x18) buf[0]=0xFF;
+  if(algo&8) buf[1]=0xFF;
+  LDUMP(L_SYS_VERBOSE,buf,8,"rsa seed:");
+  
+  static const unsigned char t1[] = { 0xE,0x3,0x5,0x8,0x9,0x4,0x2,0xF,0x0,0xD,0xB,0x6,0x7,0xA,0xC,0x1 };
+  for(int k=0; k<len; k+=32) {
+    memcpy(buf+8,buf,8);
+    for(int i=0; i<8; i++) {
+      int n=i<<1;
+      buf[n+1]=buf[i+8];
+      buf[n  ]=(t1[buf[n+1]>>4]<<4) | t1[buf[i+8]&0xF];
+      LDUMP(L_SYS_VERBOSE,buf,16,"rsa buf:");
+      }
+    for(int i=16; i<64; i+=16) memcpy(&buf[i],buf,16);
+    buf[31]=((buf[15]<<4)&0xFF) | 6;
+    buf[16]=buf[0]^1;
+    buf[32]&=0x7F;
+    buf[32]|=0x40;
+    RotateBytes(buf,32);
+    RotateBytes(buf+32,32);
+    LDUMP(L_SYS_VERBOSE,buf,64,"rsa data:");
+
+    if(rsa.RSA(buf,buf,64,exp,mod,true)==0) {
+      PRINTF(L_SYS_CRYPTO,"RSA failed");
+      return false;
+      }
+    RotateBytes(buf,8);
+    RotateBytes(mask+k,buf+8,min(32,len-k));
+    }
+  LDUMP(L_SYS_VERBOSE,mask,len,"rsa out:");
+
+  xxor(data,len,data,mask);
+  return true;
+}
+
+// -- cPlainKeyCryptoworks -----------------------------------------------------
+
+#define PLAINLEN_CW_D  16
+#define PLAINLEN_CW_CC  6
+#define PLAINLEN_CW_R  64
+
+#define CCTYP               0x00
+#define CCID                0xFF
+
+#define PROV(keynr)         (((keynr)>>16)&0xFF)
+#define TYPE(keynr)         (((keynr)>> 8)&0xFF)
+#define ID(keynr)           (((keynr)   )&0xFF)
+#define KEYSET(prov,typ,id) ((((prov)&0xFF)<<16)|(((typ)&0xFF)<<8)|((id)&0xFF))
+
+class cPlainKeyCryptoworks : public cDualKey {
+protected:
+  virtual bool IsBNKey(void) const;
+  virtual int IdSize(void) { return 4; }
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeyCryptoworks(bool Super);
+  virtual bool Parse(const char *line);
+  };
+
+static cPlainKeyTypeReg<cPlainKeyCryptoworks,'W'> KeyReg;
+
+cPlainKeyCryptoworks::cPlainKeyCryptoworks(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyCryptoworks::IsBNKey(void) const
+{
+  return TYPE(keynr)==0x10;
+}
+
+bool cPlainKeyCryptoworks::Parse(const char *line)
+{
+  unsigned char sid[2], sprov;
+  if(GetChar(line,&type,1) && GetHex(line,sid,2) && GetHex(line,&sprov,1)) {
+    int keylen, prov;
+    type=toupper(type); id=Bin2Int(sid,2); prov=sprov;
+    line=skipspace(line);
+    bool ok;
+    if(!strncasecmp(line,"CC",2)) { // cardkey
+      keynr=KEYSET(prov,CCTYP,CCID);
+      keylen=PLAINLEN_CW_CC;
+      line+=2;
+      ok=true;
+      }
+    else {
+      unsigned char sid, styp;
+      ok=GetHex(line,&styp,1) && GetHex(line,&sid,1);
+      keynr=KEYSET(prov,styp,sid);
+      keylen=IsBNKey() ? PLAINLEN_CW_R : PLAINLEN_CW_D;
+      }
+    if(ok) {
+      unsigned char *skey=AUTOMEM(keylen);
+      if(GetHex(line,skey,keylen)) {
+        SetBinKey(skey,keylen);
+        return true;
+        }
+      }
+    }
+  return false;
+}
+
+cString cPlainKeyCryptoworks::PrintKeyNr(void)
+{
+  int prov=PROV(keynr);
+  int keytyp=TYPE(keynr);
+  int keyid=ID(keynr);
+  return cString::sprintf(keytyp==CCTYP && keyid==CCID ? "%02X CC":"%02X %02X %02X",prov,keytyp,keyid);
+}
+
+// -- cSystemCryptoworks -------------------------------------------------------
+
+#define ECM_ALGO_TYP   5
+#define ECM_DATA_START ECM_ALGO_TYP
+#define ECM_NANO_LEN   7
+#define ECM_NANO_START 8
+
+class cSystemCryptoworks : public cSystem, private cCryptoworks {
+private:
+public:
+  cSystemCryptoworks(void);
+  virtual bool ProcessECM(const cEcmInfo *ecmInfo, unsigned char *data);
+//  virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+  };
+
+cSystemCryptoworks::cSystemCryptoworks(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+//  hasLogger=true;
+}
+
+bool cSystemCryptoworks::ProcessECM(const cEcmInfo *ecmInfo, unsigned char *data)
+{
+  int len=SCT_LEN(data);
+  if(data[ECM_NANO_LEN]!=len-ECM_NANO_START) {
+    PRINTF(L_SYS_ECM,"invalid ECM structure");
+    return false;
+    }
+
+  int prov=-1, keyid=0;
+  for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+    if(data[i]==0x83) {
+      prov =data[i+2]&0xFC;
+      keyid=data[i+2]&0x03;
+      break;
+      }
+    }
+  if(prov<0) {
+    PRINTF(L_SYS_ECM,"provider ID not located in ECM");
+    return false;
+    }
+
+  unsigned char key[22];
+  cPlainKey *pk;
+  if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,CCTYP,CCID),6))) {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X CC key",ecmInfo->caId,prov);
+    return false;
+    }
+  pk->Get(key+16);
+
+  // RSA stage
+  DUMPNANO("pre-RSA",&data[ECM_NANO_START],len-ECM_NANO_START);
+  for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+    int l=data[i+1]+2;
+    switch(data[i]) {
+      case 0x85:
+        {
+        if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x31,keyid),16))) {
+          if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 31 %02X key",ecmInfo->caId,prov,keyid);
+          return false;
+          }
+        pk->Get(key);
+        cBN mod;
+        if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x10,0x00),64))) {
+          if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 10 00 key",ecmInfo->caId,prov);
+          return false;
+          }
+        pk->Get(mod);
+
+        l-=10;
+        if(!DecryptRSA(&data[i+2],l,data[ECM_ALGO_TYP],key,mod))
+          return false;
+        memmove(&data[i],&data[i+2],l);
+        memmove(&data[i+l],&data[i+l+10],len-i-l);
+        len-=10;
+        break;
+        }
+      case 0x86:
+        memmove(&data[i],&data[i+l],len-i-l);
+        len-=l;
+        continue;
+      }
+    }
+  DUMPNANO("post-RSA",&data[ECM_NANO_START],len-ECM_NANO_START);
+
+  cKeySnoop ks(this,'W',ecmInfo->caId,KEYSET(prov,0x20,keyid));
+  if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x20,keyid),16))) {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 20 %02X key",ecmInfo->caId,prov,keyid);
+    return false;
+    }
+  pk->Get(key);
+
+  // DES stage
+  unsigned char sig[8];
+  LDUMP(L_SYS_VERBOSE,&data[len-8],8,"sig org:");
+  data[ECM_NANO_LEN]=len-ECM_NANO_START;
+  Signature(&data[ECM_DATA_START],len-ECM_DATA_START-10,key,sig);
+  for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+    switch(data[i]) {
+      case 0xDA:
+      case 0xDB:
+      case 0xDC:
+        for(int j=0; j<data[i+1]; j+=8)
+          DecryptDES(&data[i+2+j],data[ECM_ALGO_TYP],key);
+        break;
+      case 0xDF:
+        if(memcmp(&data[i+2],sig,8)) {
+          PRINTF(L_SYS_ECM,"signature failed in ECM");
+          return false;
+          }
+        break;
+      }
+    }
+  
+  // CW stage
+  for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+    switch(data[i]) {
+      case 0xDB:
+        if(data[i+1]==0x10) {
+          memcpy(cw,&data[i+2],16);
+          LDUMP(L_SYS_VERBOSE,cw,16,"cw:");
+          ks.OK(pk);
+          return true;
+          }
+        break;
+      }
+    }
+
+  return false;
+}
+
+/*
+void cSystemCryptoworks::ProcessEMM(int pid, int caid, unsigned char *data)
+{
+}
+*/
+
+// -- cSystemLinkCryptoworks ---------------------------------------------------
+
+class cSystemLinkCryptoworks : public cSystemLink {
+public:
+  cSystemLinkCryptoworks(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemCryptoworks; }
+  };
+
+static cSystemLinkCryptoworks staticInit;
+
+cSystemLinkCryptoworks::cSystemLinkCryptoworks(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkCryptoworks::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/cryptoworks/cryptoworks.mk
new file mode 100644 (file)
index 0000000..7194e70
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Cryptoworks
+#
+TARGET = cryptoworks
+OBJS   = cryptoworks.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.c
new file mode 100644 (file)
index 0000000..9414976
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_IRDETO        0x0600
+#define SYSTEM_BETA          0x1700
+
+#define SYSTEM_NAME          "Irdeto"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_IRDETO || (x)==SYSTEM_BETA)
+
+#define L_SYS        5
+#define L_SYS_ALL    LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+  "irdeto",
+  { L_SYS_DEFNAMES }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+static cPlainKeyTypeReg<cPlainKeyStd,'I',false> KeyReg;
+
+// -- cIrdCardInfo -------------------------------------------------------------
+
+class cIrdCardInfo : public cStructItem, public cProviderIrdeto, public cCardIrdeto {
+public:
+  unsigned char PMK[8], HMK[10];
+  bool haveHMK;
+  //
+  bool Parse(const char *line);
+  virtual cString ToString(bool hide);
+  };
+
+bool cIrdCardInfo::Parse(const char *line)
+{
+  haveHMK=false;
+  if(GetHex(line,hexSer,sizeof(hexSer)) && GetHex(line,HMK,sizeof(HMK))) haveHMK=true;
+  return GetHex(line,&provBase,sizeof(provBase)) &&
+         GetHex(line,provId,sizeof(provId)) &&
+         GetHex(line,PMK,sizeof(PMK));
+}
+
+cString cIrdCardInfo::ToString(bool hide)
+{
+  char s1[20], s2[20], s3[20], s4[20];
+  return cString::sprintf("%s %s %02x %s %s",
+     HexStr(s1,hexSer,sizeof(hexSer)),HexStr(s2,HMK,sizeof(HMK)),provBase,
+     HexStr(s3,provId,sizeof(provId)),HexStr(s4,PMK,sizeof(PMK)));
+}
+
+// -- cIrdCardInfos ------------------------------------------------------------
+
+class cIrdCardInfos : public cCardInfos<cIrdCardInfo> {
+public:
+  cIrdCardInfos(void);
+  bool Update(cIrdCardInfo *ci, const unsigned char *pmk, const unsigned char *id);
+  };
+
+static cIrdCardInfos Icards;
+
+cIrdCardInfos::cIrdCardInfos(void)
+:cCardInfos<cIrdCardInfo>("Irdeto cards","Ird-Beta.KID",SL_READWRITE)
+{}
+
+bool cIrdCardInfos::Update(cIrdCardInfo *ci, const unsigned char *pmk, const unsigned char *id)
+{
+  bool res=false;
+  char str[20], str2[12];
+  if(memcmp(ci->PMK,pmk,sizeof(ci->PMK))) {
+    PRINTF(L_GEN_INFO,"new PMK for I card %s: %s",HexStr(str2,ci->hexSer,sizeof(ci->hexSer)),KeyStr(str,pmk));
+    memcpy(ci->PMK,pmk,sizeof(ci->PMK));
+    res=true;
+    }
+  if(id && memcmp(ci->provId,id,sizeof(ci->provId))) {
+    PRINTF(L_GEN_INFO,"new PrvID for I card %s: %s",HexStr(str2,ci->hexSer,sizeof(ci->hexSer)),HexStr(str,id,sizeof(ci->provId)));
+    memcpy(ci->provId,id,sizeof(ci->provId));
+    res=true;
+    }
+  if(res) Modified();
+  return res;
+}
+
+// -- cIrdeto ------------------------------------------------------------------
+
+class cIrdeto {
+private:
+  static const unsigned char T0[], T1[], T2[];
+  unsigned char tempKey[10];
+  //
+  void Rotate(unsigned char *p);
+  void MakeTempKey(const unsigned char *key, int date);
+protected:
+  void SessionKeyCrypt(unsigned char *data, const unsigned char *key, int date);
+  bool SignatureCheck(const unsigned char *data, int length, const unsigned char *key, int date, const unsigned char *signature, int keylen);
+  void DecryptIrd(unsigned char *data, unsigned char *key, int rounds, int offset);
+  };
+
+const unsigned char cIrdeto::T0[32] = {
+  0,1,2,3,4,5,6,7, 0,1,2,3,4,5,6,7,
+  0,1,2,3,4,5,6,7, 0,3,6,1,4,7,2,5
+  };
+
+const unsigned char cIrdeto::T1[256] = {
+  0xDA,0x26,0xE8,0x72,0x11,0x52,0x3E,0x46, 0x32,0xFF,0x8C,0x1E,0xA7,0xBE,0x2C,0x29,
+  0x5F,0x86,0x7E,0x75,0x0A,0x08,0xA5,0x21, 0x61,0xFB,0x7A,0x58,0x60,0xF7,0x81,0x4F,
+  0xE4,0xFC,0xDF,0xB1,0xBB,0x6A,0x02,0xB3, 0x0B,0x6E,0x5D,0x5C,0xD5,0xCF,0xCA,0x2A,
+  0x14,0xB7,0x90,0xF3,0xD9,0x37,0x3A,0x59, 0x44,0x69,0xC9,0x78,0x30,0x16,0x39,0x9A,
+  0x0D,0x05,0x1F,0x8B,0x5E,0xEE,0x1B,0xC4, 0x76,0x43,0xBD,0xEB,0x42,0xEF,0xF9,0xD0,
+  0x4D,0xE3,0xF4,0x57,0x56,0xA3,0x0F,0xA6, 0x50,0xFD,0xDE,0xD2,0x80,0x4C,0xD3,0xCB,
+  0xF8,0x49,0x8F,0x22,0x71,0x84,0x33,0xE0, 0x47,0xC2,0x93,0xBC,0x7C,0x3B,0x9C,0x7D,
+  0xEC,0xC3,0xF1,0x89,0xCE,0x98,0xA2,0xE1, 0xC1,0xF2,0x27,0x12,0x01,0xEA,0xE5,0x9B,
+  0x25,0x87,0x96,0x7B,0x34,0x45,0xAD,0xD1, 0xB5,0xDB,0x83,0x55,0xB0,0x9E,0x19,0xD7,
+  0x17,0xC6,0x35,0xD8,0xF0,0xAE,0xD4,0x2B, 0x1D,0xA0,0x99,0x8A,0x15,0x00,0xAF,0x2D,
+  0x09,0xA8,0xF5,0x6C,0xA1,0x63,0x67,0x51, 0x3C,0xB2,0xC0,0xED,0x94,0x03,0x6F,0xBA,
+  0x3F,0x4E,0x62,0x92,0x85,0xDD,0xAB,0xFE, 0x10,0x2E,0x68,0x65,0xE7,0x04,0xF6,0x0C,
+  0x20,0x1C,0xA9,0x53,0x40,0x77,0x2F,0xA4, 0xFA,0x6D,0x73,0x28,0xE2,0xCD,0x79,0xC8,
+  0x97,0x66,0x8E,0x82,0x74,0x06,0xC7,0x88, 0x1A,0x4A,0x6B,0xCC,0x41,0xE9,0x9D,0xB8,
+  0x23,0x9F,0x3D,0xBF,0x8D,0x95,0xC5,0x13, 0xB9,0x24,0x5A,0xDC,0x64,0x18,0x38,0x91,
+  0x7F,0x5B,0x70,0x54,0x07,0xB6,0x4B,0x0E, 0x36,0xAC,0x31,0xE6,0xD6,0x48,0xAA,0xB4
+  };
+
+const unsigned char cIrdeto::T2[256] = {
+  0x8E,0xD5,0x32,0x53,0x4B,0x18,0x7F,0x95, 0xBE,0x30,0xF3,0xE0,0x22,0xE1,0x68,0x90,
+  0x82,0xC8,0xA8,0x57,0x21,0xC5,0x38,0x73, 0x61,0x5D,0x5A,0xD6,0x60,0xB7,0x48,0x70,
+  0x2B,0x7A,0x1D,0xD1,0xB1,0xEC,0x7C,0xAA, 0x2F,0x1F,0x37,0x58,0x72,0x88,0xFF,0x87,
+  0x1C,0xCB,0x00,0xE6,0x4E,0xAB,0xEB,0xB3, 0xF7,0x59,0x71,0x6A,0x64,0x2A,0x55,0x4D,
+  0xFC,0xC0,0x51,0x01,0x2D,0xC4,0x54,0xE2, 0x9F,0x26,0x16,0x27,0xF2,0x9C,0x86,0x11,
+  0x05,0x29,0xA2,0x78,0x49,0xB2,0xA6,0xCA, 0x96,0xE5,0x33,0x3F,0x46,0xBA,0xD0,0xBB,
+  0x5F,0x84,0x98,0xE4,0xF9,0x0A,0x62,0xEE, 0xF6,0xCF,0x94,0xF0,0xEA,0x1E,0xBF,0x07,
+  0x9B,0xD9,0xE9,0x74,0xC6,0xA4,0xB9,0x56, 0x3E,0xDB,0xC7,0x15,0xE3,0x80,0xD7,0xED,
+  0xEF,0x13,0xAC,0xA1,0x91,0xC2,0x89,0x5B, 0x08,0x0B,0x4C,0x02,0x3A,0x5C,0xA9,0x3B,
+  0xCE,0x6B,0xA7,0xE7,0xCD,0x7B,0xA0,0x47, 0x09,0x6D,0xF8,0xF1,0x8B,0xB0,0x12,0x42,
+  0x4A,0x9A,0x17,0xB4,0x7E,0xAD,0xFE,0xFD, 0x2C,0xD3,0xF4,0xB6,0xA3,0xFA,0xDF,0xB8,
+  0xD4,0xDA,0x0F,0x50,0x93,0x66,0x6C,0x20, 0xD8,0x8A,0xDD,0x31,0x1A,0x8C,0x06,0xD2,
+  0x44,0xE8,0x23,0x43,0x6E,0x10,0x69,0x36, 0xBC,0x19,0x8D,0x24,0x81,0x14,0x40,0xC9,
+  0x6F,0x2E,0x45,0x52,0x41,0x92,0x34,0xFB, 0x5E,0x0D,0xF5,0x76,0x25,0x77,0x63,0x65,
+  0xAF,0x4F,0xCC,0x03,0x9D,0x0C,0x28,0x39, 0x85,0xDE,0xB5,0x7D,0x67,0x83,0xBD,0xC3,
+  0xDC,0x3C,0xAE,0x99,0x04,0x75,0x8F,0x97, 0xC1,0xA5,0x9E,0x35,0x0E,0x3D,0x1B,0x79
+  };
+
+void cIrdeto::Rotate(unsigned char *p)
+{
+  unsigned char c, t;
+  c=p[9]<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+  t=*p; *p++=(t>>1) | c; c=t<<7;
+}
+
+void cIrdeto::MakeTempKey(const unsigned char *key, int date)
+{
+  memcpy(tempKey,key,8);
+  tempKey[8]=(unsigned char)(date >> 8) ^ key[0];
+  tempKey[9]=(unsigned char)date ^ key[1];
+}
+
+void cIrdeto::DecryptIrd(unsigned char *data, unsigned char *key, int rounds, int offset)
+{
+  for(int count=0; count<rounds; count++) {
+    int index=count % 10;
+    if(index==0) Rotate(key);
+    unsigned char k=key[index];
+    unsigned char d=data[T0[(count & 0x0f) + offset]] ^ k;
+    d=(k&1) ? T1[d] : T2[d];
+    data[T0[((count+1) & 0x0f) + offset]] ^= d;
+    }
+}
+
+void cIrdeto::SessionKeyCrypt(unsigned char *data, const unsigned char *key, int date)
+{
+  MakeTempKey(key,date);
+  DecryptIrd(data,tempKey,128,16);
+}
+
+bool cIrdeto::SignatureCheck(const unsigned char *data, int length, const unsigned char *key, int date, const unsigned char *signature, int keylen)
+{
+  unsigned char buffer[256];
+  memcpy(buffer,data,length);
+  int rounds;
+  if((length%8)==0) rounds=40;
+  else {
+    int i=0x61;
+    while((length%8)!=0) buffer[length++]=i++;
+    rounds=104;
+    }
+
+  if(keylen==10) memcpy(tempKey,key,10);
+  else MakeTempKey(key,date);
+
+  int i=0;
+  while(i<length-8) {
+    DecryptIrd(buffer,tempKey,40,0);
+    i+=8;
+    for(int j=0; j<8; j++) buffer[j] ^= buffer[i+j];
+    Rotate(tempKey);
+    }
+  DecryptIrd(buffer,tempKey,rounds,0);
+  return memcmp(buffer,signature,5)==0;
+}
+
+// -- cSystemIrd ---------------------------------------------------------------
+
+class cSystemIrd : public cSystem, private cIrdeto {
+private:
+  unsigned char lastKey;
+public:
+  cSystemIrd(void);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemIrd::cSystemIrd(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  lastKey=0;
+  hasLogger=true;
+}
+
+bool cSystemIrd::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+  unsigned char *data=0;
+  int date=-1, keynr=0;
+  source+=6;
+  int length=source[5]+6-5; // 6 header bytes - 5 signature bytes
+  for(int index=6 ; index<length && (data==0 || date==-1) ;) {
+    int param=source[index++];
+    int len  =source[index++] & 0x3f;
+
+    switch(param) {
+      case 0x78:
+        keynr = source[index];
+        data = &source[index+2];
+        break;
+      case 0x00:
+      case 0x40:
+        date = (source[index]<<8) | source[index+1];
+        break;
+      }
+    index+=len;
+    }
+  if(data==0 || date==-1) {
+    PRINTF(L_SYS_ECM,"incomplete ECM structure");
+    return false;
+    }
+
+  cKeySnoop ks(this,'I',source[2],keynr);
+  cPlainKey *pk=0;
+  unsigned char key[8];
+  while((pk=keys.FindKey('I',source[2],keynr,sizeof(key),pk))) {
+    unsigned char save[16];
+    memcpy(save,data,16); // save the encrypted data
+    pk->Get(key);
+    SessionKeyCrypt(&data[0],key,date);
+    SessionKeyCrypt(&data[8],key,date);
+    if(SignatureCheck(source,length,key,date,&source[length],8)) {
+      memcpy(cw,&data[0],16);
+      ks.OK(pk);
+      return true;
+      }
+    memcpy(data,save,16); // put back the encrypted data if it didn't works
+    }
+  return false;
+}
+
+void cSystemIrd::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  int i, numKeys=0, date=0;
+  unsigned char adr[10], id[4], *pk[4], prov, *mk=0, prvId[3]={0,0,0};
+
+  int n=SCT_LEN(buffer);
+  unsigned char savebuf[4096];
+  if(n>(int)sizeof(savebuf)) {
+    PRINTF(L_SYS_EMM,"%d: paket size %d to big for savebuffer in IrdetoLog",CardNum(),n);
+    return;
+    }
+
+  int index=cParseIrdeto::AddrLen(buffer);
+  memset(adr,0,sizeof(adr));
+  memcpy(adr,&buffer[3],index+1);
+  index+=4+4; // 3 header + adr type + 4 {0x01 0x00 0x00 len}
+  
+  int sindex=index;           // save index for sig. check
+  int slen=buffer[index-1]-5; // save packet length, 5 signature bytes
+  int maxIndex=index+slen;
+  if(maxIndex>n-5) {
+    PRINTF(L_SYS_EMM,"%d: bad packet length (%d > %d)",CardNum(),maxIndex,n-5);
+    maxIndex=n-5;
+    }
+  bool badnano=false;
+  while(!badnano && index<maxIndex) {
+    unsigned char nlen=buffer[index+1] & 0x3F;
+    //unsigned char prio=buffer[index] & 0x40;
+    unsigned char nano=buffer[index] & ~0x40;
+    switch(nano) {
+      case 0x10: // key update
+        {
+        int k=(buffer[index+1]>>6)+1; // key counter
+        if(nlen!=k*9) { badnano=true; break; }
+        for(i=0 ; i<k ; i++) {
+          id[i]= buffer[index+2+0+i*9];
+          pk[i]=&buffer[index+2+1+i*9];
+          numKeys++;
+          }
+        }
+       break;
+      case 0x00: // date
+        if(nlen<2) { badnano=true; break; }
+       date=WORD(buffer,index+2,0xFFFF);
+       break;
+      case 0x28: // pmk & provid update
+        if(nlen!=13) { badnano=true; break; }
+        prov= buffer[index+2+0];
+        mk=  &buffer[index+2+2];
+        prvId[0]=buffer[index+2+10];
+        prvId[1]=buffer[index+2+11];
+        prvId[2]=buffer[index+2+12];
+        break;
+      case 0x29: // pmk update
+        if(nlen!=10) { badnano=true; break; }
+        prov= buffer[index+2+0];
+        mk=  &buffer[index+2+2];
+        break;
+      case 0x11: // channel id
+        if(nlen!=6) { badnano=true; break; }
+        //chId[0]=buffer[index+2+0];
+        //chId[1]=buffer[index+2+1];
+        break;
+      case 0x91: // erase channel id
+        if(nlen!=6) { badnano=true; break; }
+        //eraseChId[0]=buffer[index+2+0];
+        //eraseChId[1]=buffer[index+2+1];
+        break;
+      case 0x8B: // CB20-matrix
+        if(nlen!=0x20) { badnano=true; break; }
+        //cb20ptr=&buffer[index+2];
+        break;
+      case 0x22: // set country code
+        if(nlen!=3) { badnano=true; break; }
+        //
+        break;
+      case 0x95: // unknown
+        if(nlen!=2) { badnano=true; break; }
+        //
+        break;
+      case 0x1E: // unknown
+        if(nlen!=15) { badnano=true; break; }
+        //
+        break;
+      case 0x1F: // unknown
+        if(nlen!=3) { badnano=true; break; }
+        //
+        break;
+      case 0x16: // unknown
+        if(nlen!=2) { badnano=true; break; }
+        //
+        break;
+      case 0x12: // unknown
+        if(nlen!=6) { badnano=true; break; }
+        //
+        break;
+
+      default:
+        PRINTF(L_SYS_EMM,"%d: unhandled nano 0x%02x",CardNum(),nano);
+        break;
+      }
+    index+=nlen+2;
+    }
+  if(badnano || index!=maxIndex) {
+    PRINTF(L_SYS_EMM,"%d: bad nano/bad paket",CardNum());
+    return;
+    }
+
+  // lastKey: save cpu time if we get bursts of the same key
+  if((numKeys>0 && (id[0]!=lastKey || numKeys>1)) || mk) {
+    memcpy(savebuf,buffer,n); // save the buffer
+    cIrdCardInfo *ci=Icards.First();
+    unsigned char *chkkey=AUTOMEM(max(sizeof(ci->PMK),sizeof(ci->HMK)));
+    while(ci) {
+      ci->hexBase=cParseIrdeto::AddrBase(buffer);
+      if((numKeys>0         && (ci->cProviderIrdeto::MatchEMM(buffer) || CheckNull(ci->provId,sizeof(ci->provId)) )) ||
+         (mk && ci->haveHMK && (ci->cCardIrdeto::MatchEMM(buffer)))) {
+        LBSTARTF(L_SYS_EMM);
+        LBPUT("%02x %02x%02x%02x",buffer[3],buffer[4],buffer[5],buffer[6]);
+
+        for(i=0 ; i<numKeys ; i++) {
+          lastKey=id[i];
+          SessionKeyCrypt(pk[i],ci->PMK,date);
+          }
+        int keylen;
+        if(mk) {
+          memcpy(chkkey,ci->HMK,sizeof(ci->HMK)); // key is modified in decryptIrd()
+          DecryptIrd(mk,chkkey,128,16);
+          keylen=sizeof(ci->HMK);
+          memcpy(chkkey,ci->HMK,sizeof(ci->HMK)); // signature check with HMK
+          }
+       else {
+          keylen=sizeof(ci->PMK);
+          memcpy(chkkey,ci->PMK,sizeof(ci->PMK)); // signature check with PMK
+          }
+
+        memcpy(&buffer[sindex-6],adr,5);
+        if(SignatureCheck(&buffer[sindex-6],slen+6,chkkey,date,&buffer[sindex+slen],keylen))
+         {
+          char str[20];
+          LBPUT(" - OK");
+          for(i=0 ; i<numKeys ; i++) LBPUT(" - PK %02x %s",id[i],KeyStr(str,pk[i]));
+          if(mk) LBPUT(" - PMK %s",KeyStr(str,mk));
+
+          for(i=0 ; i<numKeys ; i++) {
+            FoundKey();
+            if(keys.NewKey('I',ci->provBase,id[i],pk[i],8)) NewKey();
+            }
+          if(mk) {
+            FoundKey();
+            if(Icards.Update(ci,mk,prvId[0]?prvId:0)) NewKey();
+            }
+          break;
+          }
+        else {
+          LBPUT(" - FAIL");
+          }
+        LBEND();
+
+        memcpy(buffer,savebuf,n); // restore the buffer
+        }
+      ci=Icards.Next(ci);
+      }
+    }
+}
+
+// -- cSystemLinkIrd -----------------------------------------------------------
+
+class cSystemLinkIrd : public cSystemLink {
+public:
+  cSystemLinkIrd(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemIrd; }
+  };
+
+static cSystemLinkIrd staticInit;
+
+cSystemLinkIrd::cSystemLinkIrd(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkIrd::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/irdeto/irdeto.mk
new file mode 100644 (file)
index 0000000..7fff9f5
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Irdeto
+#
+TARGET = irdeto
+OBJS   = irdeto.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.c
new file mode 100644 (file)
index 0000000..58164c6
--- /dev/null
@@ -0,0 +1,1187 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "data.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define LOGLBPUT(...)   loglb->Printf(__VA_ARGS__)
+#define CCLOGLBPUT(...) do { if(doDisAsm) loglb->Printf(__VA_ARGS__); } while(0)
+
+// -- cMapMem ------------------------------------------------------------------
+
+cMapMem::cMapMem(unsigned short Offset, int Size)
+{
+  offset=Offset; size=Size;
+  if((mem=(unsigned char *)malloc(size)))
+    memset(mem,0,size);
+//  PRINTF(L_SYS_EMU,"mapmem: new map off=%04x size=%04x",offset,size);
+}
+
+cMapMem::~cMapMem()
+{
+  free(mem);
+}
+
+bool cMapMem::IsFine(void)
+{
+  return (mem!=0);
+}
+
+unsigned char cMapMem::Get(unsigned short ea)
+{
+  return (ea>=offset && ea<offset+size) ? mem[ea-offset] : 0;
+}
+
+void cMapMem::Set(unsigned short ea, unsigned char val)
+{
+  if(ea>=offset && ea<offset+size)
+    mem[ea-offset]=val;
+}
+
+// -- cMapRom ------------------------------------------------------------------
+
+cMapRom::cMapRom(unsigned short Offset, const char *Filename, int InFileOffset)
+{
+  offset=Offset; addr=0;
+  fm=filemaps.GetFileMap(Filename,FILEMAP_DOMAIN,false);
+  if(fm && fm->Map()) {
+    addr=fm->Addr()+InFileOffset;
+    size=fm->Size()-InFileOffset;
+    PRINTF(L_SYS_EMU,"maprom: new map off=%04x size=%04x",offset,size);
+    }
+}
+
+cMapRom::~cMapRom()
+{
+  if(fm) fm->Unmap();
+}
+
+bool cMapRom::IsFine(void)
+{
+  return addr!=0 && size>0;
+}
+
+unsigned char cMapRom::Get(unsigned short ea)
+{
+  return (ea>=offset && ea<offset+size) ? addr[ea-offset] : 0;
+}
+
+void cMapRom::Set(unsigned short ea, unsigned char val)
+{
+  if(ea>=offset && ea<offset+size) LOGLBPUT("[ROM] ");
+  // this is a ROM!
+}
+
+// -- cMapEeprom ---------------------------------------------------------------
+
+cMapEeprom::cMapEeprom(unsigned short Offset, const char *Filename, int OtpSize, int InFileOffset)
+{
+  offset=Offset; otpSize=OtpSize; addr=0;
+  fm=filemaps.GetFileMap(Filename,FILEMAP_DOMAIN,true);
+  if(fm && fm->Map()) {
+    addr=fm->Addr()+InFileOffset;
+    size=fm->Size()-InFileOffset;
+    PRINTF(L_SYS_EMU,"mapeeprom: new map off=%04x size=%04x otp=%04x",offset,size,otpSize);
+  }
+}
+
+cMapEeprom::~cMapEeprom()
+{
+  if(fm) fm->Unmap();
+}
+
+bool cMapEeprom::IsFine(void)
+{
+  return (addr!=0);
+}
+
+unsigned char cMapEeprom::Get(unsigned short ea)
+{
+  return (ea>=offset && ea<offset+size) ? addr[ea-offset] : 0;
+}
+
+void cMapEeprom::Set(unsigned short ea, unsigned char val)
+{
+       if(ea>=offset && ea<offset+otpSize) {
+    if(addr[ea-offset]==0) {
+      addr[ea-offset]=val;
+      LOGLBPUT("[OTP-SET] ");
+      }
+    LOGLBPUT("[OTP] ");
+    }
+  if(ea>=offset+otpSize && ea<offset+size) {
+    addr[ea-offset]=val;
+    LOGLBPUT("[EEP] ");
+    }
+}
+
+// -- c6805 --------------------------------------------------------------------
+
+#define PAGEOFF(ea,s) (((ea)&0x8000) ? pageMap[s]:0)
+
+c6805::c6805(void) {
+  cc.c=0; cc.z=0; cc.n=0; cc.i=0; cc.h=0; cc.v=1;
+  pc=0; a=0; x=0; y=0; cr=dr=0; sp=spHi=0x100; spLow=0xC0;
+  hasReadHandler=hasWriteHandler=false;
+  exptBase=0x4000;
+  ClearExceptions();
+  ClearBreakpoints();
+  InitMapper();
+  ResetCycles();
+  memset(stats,0,sizeof(stats));
+  loglb=new cLineBuff(128);
+}
+
+c6805::~c6805()
+{
+  if(LOG(L_SYS_CPUSTATS)) {
+    int i, j, sort[256];
+    for(i=0; i<256; i++) sort[i]=i;
+    for(i=0; i<256; i++)
+      for(j=0; j<255; j++)
+        if(stats[sort[j]]<stats[sort[j+1]]) {
+          int x=sort[j]; sort[j]=sort[j+1]; sort[j+1]=x;
+          }
+    PRINTF(L_SYS_CPUSTATS,"opcode statistics");
+    for(i=0; i<256; i++)
+      if((j=stats[sort[i]])) PRINTF(L_SYS_CPUSTATS,"opcode %02x: %d",sort[i],j);
+    }
+  ClearMapper();
+  delete loglb;
+}
+
+bool c6805::AddMapper(cMap *map, unsigned short start, int size, unsigned char seg)
+{
+  if(map && map->IsFine()) {
+    if(nextMapper<MAX_MAPPER) {
+      map->loglb=loglb;
+      mapper[nextMapper]=map;
+      memset(&mapMap[start+PAGEOFF(start,seg)],nextMapper,size);
+      nextMapper++;
+      return true;
+      }
+    else PRINTF(L_SYS_EMU,"6805: too many mappers");
+    }
+  else PRINTF(L_SYS_EMU,"6805: mapper not ready (start=%02x:%04x size=%04x)",seg,start,size);
+  delete map;
+  return false;
+}
+
+void c6805::ResetMapper(void)
+{
+  ClearMapper(); InitMapper();
+}
+
+void c6805::InitMapper(void)
+{
+  memset(mapMap,0,sizeof(mapMap));
+  memset(mapper,0,sizeof(mapper));
+  mapper[0]=new cMapMem(0,PAGE_SIZE*2);
+  nextMapper=1;
+  memset(pageMap,0,sizeof(pageMap));
+  pageMap[0]=PAGE_SIZE*0;
+  pageMap[1]=PAGE_SIZE*1;
+  pageMap[2]=PAGE_SIZE*2;
+  pageMap[0x80]=PAGE_SIZE*3;
+  pageMap[0x40]=PAGE_SIZE*4;
+}
+
+void c6805::ClearMapper(void)
+{
+  for(int i=0; i<MAX_MAPPER; i++) delete mapper[i];
+}
+
+unsigned char c6805::Get(unsigned short ea) const
+{
+  return mapper[mapMap[ea+PAGEOFF(ea,cr)]&0x7f]->Get(ea);
+}
+
+unsigned char c6805::Get(unsigned char seg, unsigned short ea) const
+{
+  return mapper[mapMap[ea+PAGEOFF(ea,seg)]&0x7f]->Get(ea);
+}
+
+void c6805::Set(unsigned short ea, unsigned char val)
+{
+  if(hasWriteHandler) WriteHandler(cr,ea,val);
+  unsigned char mapId=mapMap[ea+PAGEOFF(ea,cr)];
+  if(!(mapId&0x80)) mapper[mapId&0x7f]->Set(ea,val);
+}
+
+void c6805::Set(unsigned char seg, unsigned short ea, unsigned char val)
+{
+  if(hasWriteHandler) WriteHandler(seg,ea,val);
+  unsigned char mapId=mapMap[ea+PAGEOFF(ea,seg)];
+  if(!(mapId&0x80)) mapper[mapId&0x7f]->Set(ea,val);
+}
+
+void c6805::ForceSet(unsigned short ea, unsigned char val, bool ro)
+{
+  mapMap[ea]=0;                // reset to RAM map
+  Set(0,ea,val);               // set value
+  if(ro) mapMap[ea]|=0x80;     // protect byte
+}
+
+void c6805::SetMem(unsigned short addr, const unsigned char *data, int len, unsigned char seg)
+{
+  while(len>0) { Set(seg,addr++,*data++); len--; }
+}
+
+void c6805::GetMem(unsigned short addr, unsigned char *data, int len, unsigned char seg) const
+{
+  while(len>0) { *data++=Get(seg,addr++); len--; }
+}
+
+void c6805::SetSp(unsigned short SpHi, unsigned short SpLow)
+{
+  spHi =sp=SpHi;
+  spLow   =SpLow;
+}
+
+void c6805::SetPc(unsigned short addr, unsigned char seg)
+{
+  pc=addr; cr=seg;
+  ResetCycles();
+  ClearExceptions();
+}
+
+void c6805::PopPc(void)
+{
+  poppc();
+}
+
+void c6805::PopCr(void)
+{
+  cr=pop();
+}
+
+void c6805::Push(unsigned char c)
+{
+  push(c);
+}
+
+void c6805::AddBreakpoint(unsigned short addr)
+{
+  if(numBp<MAX_BREAKPOINTS) {
+    bp[numBp++]=addr;
+    PRINTF(L_SYS_EMU,"6805: setting breakpoint at 0x%04x",addr);
+    }
+  else PRINTF(L_SYS_EMU,"6805: too many breakpoints");
+}
+
+void c6805::ClearBreakpoints(void)
+{
+  numBp=0;
+  memset(bp,0,sizeof(bp));
+}
+
+void c6805::AddCycles(unsigned int num)
+{
+  if(num>0) {
+    clockcycles+=num;
+    if(timerDisable>0) {
+      timerDisable-=num;
+      if(timerDisable<0) timerDisable=0;
+      }
+    if(!timerDisable)
+      TimerHandler(num);
+    }
+}
+
+void c6805::ResetCycles(void)
+{
+  clockcycles=0;
+}
+
+void c6805::RaiseException(int num)
+{
+  if(num<EXPT_MAX) {
+    exptPending=true;
+    expt[num]=true;
+    }
+}
+
+void c6805::ClearExceptions(void)
+{
+       timerDisable = delayTimerDisable = 0;
+       exptPending = 0;
+       exptReady = true;
+  for(int i=0; i<EXPT_MAX; i++) expt[i]=false;
+}
+
+static const char * const ops[] = {
+//         0x00    0x01    0x02    0x03    0x04    0x05    0x06    0x07    0x08    0x09    0x0a    0x0b    0x0c    0x0d    0x0e    0x0f
+/* 0x00 */ "BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR",
+/* 0x10 */ "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR",
+/* 0x20 */ "BRA",  "BRN",  "BHI",  "BLS",  "BCC",  "BCS",  "BNE",  "BEQ",  "BHCC", "BHCS", "BPL",  "BMI",  "BMC",  "BMS",  "BIL",  "BIH",
+/* 0x30 */ "NEG",  "pre31","pre32","COM",  "LSR",  "op35", "ROR",  "ASR",  "ASL",  "ROL",  "DEC",  "op3b", "INC",  "TST",  "SWAP", "CLR",
+/* 0x40 */ "NEG",  "op41", "MUL",  "COM",  "LSR",  "op45", "ROR",  "ASR",  "ASL",  "ROL",  "DEC",  "op4b", "INC",  "TST",  "SWAP", "CLR",
+/* 0x50 */ "NEG",  "op51", "MUL",  "COM",  "LSR",  "op55", "ROR",  "ASR",  "ASL",  "ROL",  "DEC",  "op5b", "INC",  "TST",  "SWAP", "CLR",
+/* 0x60 */ "NEG",  "op61", "op62", "COM",  "LSR",  "op65", "ROR",  "ASR",  "ASL",  "ROL",  "DEC",  "op6b", "INC",  "TST",  "SWAP", "CLR",
+/* 0x70 */ "NEG",  "LDD",  "LDD",  "COM",  "LSR",  "LDD",  "ROR",  "ASR",  "ASL",  "ROL",  "DEC",  "TAD",  "INC",  "TST",  "SWAP", "CLR",
+/* 0x80 */ "RTI",  "RTS",  "op82", "SWI",  "POPA", "POP%c","POPC", "PRTS", "PUSHA","PUSH%c","PUSHC","TDA", "TCA",  "PJSR", "STOP", "WAIT",
+/* 0x90 */ "pre90","pre91","pre92","T%2$c%1$c","T%cS","TAS","TS%c","TA%c", "CLC",  "SEC",  "CLI",  "SEI",  "RSP",  "NOP",  "TSA",  "T%cA",
+/* 0xa0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "opa7", "EOR",  "ADC",  "ORA",  "ADD",  "PUSHD","BSR",  "LD%c", "POPD",
+/* 0xb0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "STA",  "EOR",  "ADC",  "ORA",  "ADD",  "JMP",  "JSR",  "LD%c", "ST%c",
+/* 0xc0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "STA",  "EOR",  "ADC",  "ORA",  "ADD",  "JMP",  "JSR",  "LD%c", "ST%c",
+/* 0xd0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "STA",  "EOR",  "ADC",  "ORA",  "ADD",  "JMP",  "JSR",  "LD%c", "ST%c",
+/* 0xe0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "STA",  "EOR",  "ADC",  "ORA",  "ADD",  "JMP",  "JSR",  "LD%c", "ST%c",
+/* 0xf0 */ "SUB",  "CMP",  "SBC",  "CP%c", "AND",  "BIT",  "LDA",  "STA",  "EOR",  "ADC",  "ORA",  "ADD",  "JMP",  "JSR",  "LD%c", "ST%c",
+  };
+static const char * const vops[] = {
+  "BVGT","BVLE","BVGE","BVLT","BVC","BVS"
+  };
+
+// Flags:
+// 1 - read operant
+// 2 - write operant
+// 4 - use dr register
+// 8 - special address mode in high nibble
+static const char opFlags[] = {
+//            00    01    02    03    04    05    06    07    08    09    0a    0b    0c    0d    0e    0f
+/* 0x00 */  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+/* 0x10 */  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+/* 0x20 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x30 */  0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x40 */  0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x50 */  0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x60 */  0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x70 */  0x03, 0xA9, 0xB9, 0x03, 0x03, 0xC9, 0x03, 0x03, 0x03, 0x03, 0x03, 0x88, 0x03, 0x01, 0x03, 0x02,
+/* 0x80 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x90 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0xa0 */  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x88, 0x88, 0x01, 0x88,
+/* 0xb0 */  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+/* 0xc0 */  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xd0 */  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xe0 */  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xf0 */  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+  };
+
+static const unsigned char clock_cycles[] = {
+//         00  01  02  03  04  05  06  07  08  09  0a  0b  0c  0d  0e  0f
+/* 0x00 */  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
+/* 0x10 */  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
+/* 0x20 */  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
+/* 0x30 */  5,  0,  0,  5,  5,  0,  5,  5,  5,  5,  5,  0,  5,  4,  5,  5,
+/* 0x40 */  3,  0, 11,  3,  3,  0,  3,  3,  3,  3,  3,  0,  3,  3,  3,  3,
+/* 0x50 */  3,  0,  0,  3,  3,  0,  3,  3,  3,  3,  3,  0,  3,  3,  3,  3,
+/* 0x60 */  6,  0,  0,  6,  6,  0,  6,  6,  6,  6,  6,  0,  6,  5,  6,  6,
+/* 0x70 */  5,  2,  3,  5,  5,  4,  5,  5,  5,  5,  5,  2,  5,  4,  5,  5,
+/* 0x80 */  9,  6,  0, 10,  4,  4,  4,  6,  3,  3,  3,  2,  2,  7,  2,  2,
+/* 0x90 */  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
+/* 0xa0 */  2,  2,  2,  2,  2,  2,  2,  0,  2,  2,  2,  2,  3,  6,  2,  4,
+/* 0xb0 */  3,  3,  3,  3,  3,  3,  3,  4,  3,  3,  3,  3,  2,  5,  3,  4,
+/* 0xc0 */  4,  4,  4,  4,  4,  4,  4,  5,  4,  4,  4,  4,  3,  6,  4,  5,
+/* 0xd0 */  5,  5,  5,  5,  5,  5,  5,  6,  5,  5,  5,  5,  4,  7,  5,  6,
+/* 0xe0 */  4,  4,  4,  4,  4,  4,  4,  5,  4,  4,  4,  4,  3,  6,  4,  5,
+/* 0xf0 */  3,  3,  3,  3,  3,  3,  3,  4,  3,  3,  3,  3,  2,  5,  3,  4, 
+  };
+
+char * c6805::PADDR(unsigned char s, unsigned short ea)
+{
+  snprintf(addrBuff,sizeof(addrBuff),((ea&0x8000) && s>0)?"%2$x:%1$04x":"%04x",ea,s);
+  return addrBuff;
+}
+
+int c6805::Run(int max_count)
+{
+// returns:
+// 0 - breakpoint
+// 1 - stack overflow
+// 2 - instruction counter exeeded
+// 3 - unsupported instruction
+
+       unsigned char cycles;
+       unsigned short instrPc;
+  bool disAsmHeader=false;
+  disAsmLogClass=L_SYS_DISASM;
+  if(LOG(L_SYS_DISASM)) doDisAsm=true;
+  else {
+    doDisAsm=false;
+    if(LOG(L_SYS_DISASM80)) disAsmLogClass=L_SYS_DISASM80;
+    }
+
+  int count=0;
+  while (1) {
+               instrPc = pc;
+               cycles = 0;
+               if(exptPending && exptReady) {
+                       exptPending = false;
+                       for(int i=0; i<EXPT_MAX; ++i) {
+                               if(expt[i]) {
+                                       exptPending = true; // to force check for another interrupt in next Run pass
+                                       expt[i] = false;
+                                       pushpc(); push(x); push(a); pushc(); cc.i = 1;
+                                       pc = exptBase+4*i;
+                                       timerDisable = delayTimerDisable;
+                                       break;
+                               }
+                       }
+               }
+               delayTimerDisable = 0;
+               exptReady = !cc.i;
+
+               Stepper();
+
+    if(sp<spLow) {
+      PRINTF(L_SYS_EMU,"stack overflow (count=%d)",count);
+      return 1;
+      }
+    if(spHi>spLow && sp>spHi) {
+      PRINTF(L_SYS_EMU,"stack underflow (count=%d)",count);
+      return 1;
+      }
+    if(pc>0x0400 && mapMap[pc+PAGEOFF(pc,cr)]==0) {
+      PRINTF(L_SYS_EMU,"NX protection at %04x (count=%d)",pc,count);
+      return 1;
+      }
+    count++;
+
+    if(!LOG(L_SYS_DISASM) && LOG(L_SYS_DISASM80)) {
+      bool flag=(pc>=0x80 && pc<0x200);
+      if(doDisAsm && !flag) PRINTF(disAsmLogClass,"[...]");
+      doDisAsm=flag;
+      }
+
+    if(doDisAsm && !disAsmHeader) {
+      PRINTF(disAsmLogClass,"cr:-pc- aa xx yy dr -sp- VHINZC -mem@pc- -mem@sp- -cycles-");
+      disAsmHeader=true;
+      }
+    CCLOGLBPUT("%02x:%04x %02x %02x %02x %02x %04x %c%c%c%c%c%c %02x%02x%02x%02x %02x%02x%02x%02x %08x ",
+               cr,pc,a,x,y,dr,sp,
+               cc.v?'V':'.',cc.h?'H':'.',cc.i?'I':'.',cc.n?'N':'.',cc.z?'Z':'.',cc.c?'C':'.',
+               Get(pc),Get(pc+1),Get(pc+2),Get(pc+3),Get(sp+1),Get(sp+2),Get(sp+3),Get(sp+4),
+               clockcycles);
+
+    unsigned char *ex=&x;
+    unsigned short idx=*ex;
+    indirect=false;
+    bool paged=false, vbra=false;
+    unsigned char ins=Get(pc++);
+    char xs='X', xi='X';
+    int cycles=0;
+    // check pre-bytes
+    switch(ins) {
+      case 0x31: // use SP indexed or indirect paged mode (ST19)
+        cycles+=2;
+        ins=Get(pc++);
+        switch(ins) {
+          case 0x22: case 0x23: case 0x24: case 0x25:
+          case 0x26: case 0x27:
+            vbra=true; break;
+          case 0x75:
+          case 0x8D:
+          case 0xC0: case 0xC1: case 0xC2: case 0xC3:
+          case 0xC4: case 0xC5: case 0xC6: case 0xC7:
+          case 0xC8: case 0xC9: case 0xCA: case 0xCB:
+          case 0xCE: case 0xCF:
+          case 0xD0: case 0xD1: case 0xD2: case 0xD3:
+          case 0xD4: case 0xD5: case 0xD6: case 0xD7:
+          case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+          case 0xDE: case 0xDF:
+            paged=true; indirect=true; break;
+          case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+          case 0xE4: case 0xE5: case 0xE6: case 0xE7:
+          case 0xE8: case 0xE9: case 0xEA: case 0xEB:
+          case 0xEE: case 0xEF:
+            idx=sp; xi='S'; break;
+          }
+        break;
+      case 0x32: // use indirect SP indexed or indirect paged Y indexed mode (ST19)
+        cycles+=2;
+        ins=Get(pc++);
+        switch(ins) {
+          case 0x22: case 0x23: case 0x24: case 0x25:
+          case 0x26: case 0x27:
+            vbra=true; indirect=true; break;
+          case 0xC3:
+          case 0xCE: case 0xCF:
+          case 0xD0: case 0xD1: case 0xD2: case 0xD3:
+          case 0xD4: case 0xD5: case 0xD6: case 0xD7:
+          case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+          case 0xDE: case 0xDF:
+            paged=true; indirect=true; ex=&y; idx=*ex; xs='Y'; xi='Y'; break;
+          case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+          case 0xE4: case 0xE5: case 0xE6: case 0xE7:
+          case 0xE8: case 0xE9: case 0xEA: case 0xEB:
+          case 0xEE: case 0xEF:
+            indirect=true; idx=sp; xi='S'; break;
+          }
+        break;
+      case 0x91: // use Y register with indirect addr mode (ST7)
+        cycles++;
+        indirect=true;
+        // fall through
+      case 0x90: // use Y register (ST7)
+        cycles++;
+        ex=&y; idx=*ex; xs='Y'; xi='Y';
+        ins=Get(pc++);
+        break;
+      case 0x92: // use indirect addr mode (ST7)
+        cycles+=2;
+        indirect=true;
+        ins=Get(pc++);
+        break;
+      }
+    int postCycles=0;
+//XXX    if(ins<=0x1F) postCycles=2;  // btjt/btjf/bres/bset
+    AddCycles(cycles+clock_cycles[ins]-postCycles);
+
+    if(doDisAsm) {
+      char str[8];
+      if(!vbra) snprintf(str,sizeof(str),ops[ins],xs,xs^1);
+      else snprintf(str,sizeof(str),"%s",vops[ins-0x22]);
+      LOGLBPUT("%-5s ",str);
+      }
+
+    // address decoding
+    unsigned short ea=0;
+    unsigned char flags=opFlags[ins];
+    unsigned char pr=(flags&4) ? dr:cr;
+    switch(((flags&8) ? flags:ins)>>4) {
+      case 0x2:                        // no or special address mode
+      case 0x8:
+      case 0x9:
+        break;
+      case 0xA:                        // immediate
+        CCLOGLBPUT("#%02x ",Get(pc));
+        ea=pc++; break;
+      case 0x3:                        // short
+      case 0xB:
+        ea=Get(pc++);
+        if(!indirect) {                // short direct
+          }
+        else {                 // short indirect
+          CCLOGLBPUT("[%02x] -> ",ea);
+          ea=Get(ea);
+          }
+        CCLOGLBPUT("%02x ",ea);
+        break;
+      case 0xC:                        // long
+        if(!indirect) {                // long direct
+          ea=HILO(pc); pc+=2;
+          }
+        else {                 // long indirect
+          if(paged) {
+            ea=HILO(pc); pc+=2;
+            CCLOGLBPUT("[%s] -> ",PADDR(pr,ea));
+            unsigned char s=Get(pr,ea);
+            ea=HILOS(pr,ea+1);
+            pr=s;
+            }
+          else {
+            ea=Get(pc++);
+            CCLOGLBPUT("[%02x] -> ",ea);
+            ea=HILO(ea);
+            }
+          }
+        CCLOGLBPUT("%s ",PADDR(pr,ea));
+        break;
+      case 0xD:                        // long indexed
+        if(!indirect) {                // long direct indexed
+          ea=HILO(pc); pc+=2;
+          CCLOGLBPUT("(%s",PADDR(cr,ea));
+          }
+        else {                 // long indirect indexed
+          if(paged) {
+            ea=HILO(pc); pc+=2;
+            CCLOGLBPUT("([%s]",PADDR(pr,ea));
+            unsigned char s=Get(pr,ea++);
+            ea=HILOS(pr,ea);
+            pr=s;
+            }
+          else {
+            ea=Get(pc++);
+            CCLOGLBPUT("([%02x]",ea);
+            ea=HILO(ea);
+            }
+          CCLOGLBPUT(",%c) -> (%s",xi,PADDR(pr,ea));
+          }
+        ea+=idx;
+        CCLOGLBPUT(",%c) -> %s ",xi,PADDR(pr,ea));
+        break;
+      case 0x6:                        // short indexed
+      case 0xE:
+        ea=Get(pc++);
+        if(!indirect) {                // short direct indexed
+          CCLOGLBPUT("(%02x",ea);
+          }
+        else {                 // short indirect indexed
+          CCLOGLBPUT("([%02x]",ea);
+          ea=Get(ea);
+          CCLOGLBPUT(",%c) -> (%02x",xi,ea);
+          }
+        ea+=idx;
+        CCLOGLBPUT(",%c) -> %s ",xi,PADDR(pr,ea));
+        break;
+      case 0x7:                        // indexed
+      case 0xF:
+        ea=idx;
+        CCLOGLBPUT("(%c) -> %s ",xi,PADDR(pr,ea));
+        break;
+      case 0x4:                        // inherent A
+        CCLOGLBPUT("A ");
+        break;
+      case 0x5:                        // inherent X/Y
+        CCLOGLBPUT("%c ",xs);
+        break;
+      case 0x0:                        // bit
+      case 0x1:
+        ea=Get(pc++);
+        if(!indirect) {
+          }
+        else {
+          CCLOGLBPUT("[%02x] -> ",ea);
+          ea=Get(ea);
+          indirect=false; // don't use indirect mode in case this is a bit branch
+          }
+        CCLOGLBPUT("%02x ",ea);
+        break;
+      }
+
+    // read operant
+    unsigned char op=0;
+    if(flags & 1) {
+      switch(((flags&8) ? flags:ins)>>4) {
+        case 0x2:                      // no or special address mode
+        case 0x8:
+        case 0x9:
+          break;
+        case 0x3:                      // short
+        case 0xB:
+        case 0xC:                      // long
+        case 0xD:                      // long indexed
+        case 0x6:                      // short indexed
+        case 0xE:
+        case 0x7:                      // indexed
+        case 0xF:
+        case 0x0:                      // bit
+        case 0x1:
+          op=Get(pr,ea);
+          if(hasReadHandler) ReadHandler(pr,ea,op);
+          CCLOGLBPUT("{%02x} ",op);
+          break;
+        case 0xA:                      // immediate
+          op=Get(pr,ea);
+          break;
+        case 0x4:                      // inherent A
+          op=a; break;
+        case 0x5:                      // inherent X/Y
+          op=*ex; break;
+        }
+      }
+
+    if(postCycles) AddCycles(postCycles);
+
+    // command decoding
+    stats[ins]++;
+    switch(ins) {
+      case 0xA6: // LDA
+      case 0xB6:
+      case 0xC6:
+      case 0xD6:
+      case 0xE6:
+      case 0xF6:
+        a=op; tst(op); break;
+      case 0xAE: // LDX
+      case 0xBE:
+      case 0xCE:
+      case 0xDE:
+      case 0xEE:
+      case 0xFE:
+        *ex=op; tst(op); break;
+      case 0xB7: // STA
+      case 0xC7:
+      case 0xD7:
+      case 0xE7:
+      case 0xF7:
+        op=a; tst(op); break;
+      case 0xBF: // STX
+      case 0xCF:
+      case 0xDF:
+      case 0xEF:
+      case 0xFF:
+        op=*ex; tst(op); break;
+      case 0x97: // TAX
+        *ex=a; break;
+      case 0x9F: // TXA
+        a=*ex; break;
+      case 0x93: // TYX (ST7)
+        if(ex==&x) *ex=y; else *ex=x; break;
+      case 0x3D: // TST
+      case 0x4D:
+      case 0x5D:
+      case 0x6D:
+      case 0x7D:
+        tst(op); break;
+      case 0x3F: // CLR
+      case 0x4F:
+      case 0x5F:
+      case 0x6F:
+      case 0x7F:
+        op=0; tst(0); break;
+      case 0x3C: // INC
+      case 0x4C:
+      case 0x5C:
+      case 0x6C:
+      case 0x7C:
+        op++; cc.v=(op==0x80); tst(op); break;
+      case 0x3A: // DEC
+      case 0x4A:
+      case 0x5A:
+      case 0x6A:
+      case 0x7A:
+        op--; cc.v=(op==0x7f); tst(op); break;
+      case 0x33: // COM
+      case 0x43:
+      case 0x53:
+      case 0x63:
+      case 0x73:
+        op=~op; cc.c=1; tst(op); break;
+      case 0x30: // NEG
+      case 0x40:
+      case 0x50:
+      case 0x60:
+      case 0x70:
+        op=~op+1; cc.c=(op!=0); cc.v=(op==0x80); tst(op); break;
+      case 0x42: // MUL
+      case 0x52:
+        {
+        unsigned short res=*ex * a;
+        *ex=(res>>8); a=res&0xff; cc.c=0; cc.h=0;
+        break;
+        }
+      case 0xA9: // ADC
+      case 0xB9:
+      case 0xC9:
+      case 0xD9:
+      case 0xE9:
+      case 0xF9:
+        a=add(op,cc.c); break;
+      case 0xAB: // ADD
+      case 0xBB:
+      case 0xCB:
+      case 0xDB:
+      case 0xEB:
+      case 0xFB:
+        a=add(op,0); break;
+      case 0xA2: // SBC
+      case 0xB2:
+      case 0xC2:
+      case 0xD2:
+      case 0xE2:
+      case 0xF2:
+        a=sub(a,op,cc.c); break;
+      case 0xA0: // SUB
+      case 0xB0:
+      case 0xC0:
+      case 0xD0:
+      case 0xE0:
+      case 0xF0:
+        a=sub(a,op,0); break;
+      case 0xA1: // CMP
+      case 0xB1:
+      case 0xC1:
+      case 0xD1:
+      case 0xE1:
+      case 0xF1:
+        sub(a,op,0); break;
+      case 0xA3: // CPX
+      case 0xB3:
+      case 0xC3:
+      case 0xD3:
+      case 0xE3:
+      case 0xF3:
+        sub(*ex,op,0); break;
+      case 0xA4: // AND
+      case 0xB4:
+      case 0xC4:
+      case 0xD4:
+      case 0xE4:
+      case 0xF4:
+        a &= op; tst(a); break;
+      case 0xAA: // ORA
+      case 0xBA:
+      case 0xCA:
+      case 0xDA:
+      case 0xEA:
+      case 0xFA:
+        a |= op; tst(a); break;
+      case 0xA8: // EOR
+      case 0xB8:
+      case 0xC8:
+      case 0xD8:
+      case 0xE8:
+      case 0xF8:
+        a ^= op; tst(a); break;
+      case 0xA5: // BIT
+      case 0xB5:
+      case 0xC5:
+      case 0xD5:
+      case 0xE5:
+      case 0xF5:
+        tst(a & op); break;
+      case 0x38: // ASL
+      case 0x48:
+      case 0x58:
+      case 0x68:
+      case 0x78:
+        op=rollL(op,0); break;
+      case 0x39: // ROL
+      case 0x49:
+      case 0x59:
+      case 0x69:
+      case 0x79:
+        op=rollL(op,cc.c); break;
+      case 0x37: // ASR
+      case 0x47:
+      case 0x57:
+      case 0x67:
+      case 0x77:
+        op=rollR(op,bitset(op,7)); break;
+      case 0x34: // LSR
+      case 0x44:
+      case 0x54:
+      case 0x64:
+      case 0x74:
+        op=rollR(op,0); break;
+      case 0x36: // ROR
+      case 0x46:
+      case 0x56:
+      case 0x66:
+      case 0x76:
+        op=rollR(op,cc.c); break;
+      case 0x3E: // SWAP (ST7)
+      case 0x4E:
+      case 0x5E:
+      case 0x6E:
+      case 0x7E:
+        op=(op<<4)|(op>>4); tst(op); break;
+      //case 0x00 ... 0x0F: // BRSET BRCLR
+      case 0x00: case 0x01: case 0x02: case 0x03:
+      case 0x04: case 0x05: case 0x06: case 0x07:
+      case 0x08: case 0x09: case 0x0A: case 0x0B:
+      case 0x0C: case 0x0D: case 0x0E: case 0x0F:
+        {
+        int bit=(ins&0x0F)>>1;
+        CCLOGLBPUT(",#%x,",bit);
+        cc.c=bitset(op,bit);
+        branch((ins&0x01) ? !cc.c:cc.c);
+        break;
+        }
+      //case 0x10 ... 0x1F: // BSET BCLR
+      case 0x10: case 0x11: case 0x12: case 0x13:
+      case 0x14: case 0x15: case 0x16: case 0x17:
+      case 0x18: case 0x19: case 0x1A: case 0x1B:
+      case 0x1C: case 0x1D: case 0x1E: case 0x1F:
+        {
+        int bit=(ins&0x0F)>>1;
+        CCLOGLBPUT(",#%x",bit);
+        if(ins&0x01) op &= ~(1<<bit);
+        else         op |=  (1<<bit);
+        break;
+        }
+      case 0x20: // BRA
+        branch(true); break;
+      case 0x21: // BRN
+        branch(false); break;
+      case 0x22: // BHI
+        if(vbra) branch(!cc.z && ((cc.n && cc.v) || (!cc.n && !cc.v)));
+        else branch(!cc.c && !cc.z);
+        break;
+      case 0x23: // BLS
+        if(vbra) branch(cc.z || (cc.n && !cc.v) || (!cc.n && cc.v));
+        else branch( cc.c ||  cc.z);
+        break;
+      case 0x24: // BCC BHS
+        if(vbra) branch((cc.n && cc.v) || (!cc.n && !cc.v));
+        else branch(!cc.c);
+        break;
+      case 0x25: // BCS BLO
+        if(vbra) branch((cc.n && !cc.v) || (!cc.n && cc.v));
+        else branch( cc.c);
+        break;
+      case 0x26: // BNE
+        if(vbra) branch(!cc.v);
+        else branch(!cc.z);
+        break;
+      case 0x27: // BEQ
+        if(vbra) branch(cc.v);
+        else branch( cc.z); break;
+      case 0x28: // BHCC
+        branch(!cc.h); break;
+      case 0x29: // BHCS
+        branch( cc.h); break;
+      case 0x2A: // BPL
+        branch(!cc.n); break;
+      case 0x2B: // BMI
+        branch( cc.n); break;
+      case 0x2C: // BMC
+        branch(!cc.i); break;
+      case 0x2D: // BMS
+        branch( cc.i); break;
+      case 0xBC: // JMP
+      case 0xCC:
+      case 0xDC:
+      case 0xEC:
+      case 0xFC:
+        pc=ea; break;
+      case 0xAD: // BSR
+        pc++; pushpc(); pc--; branch(true); break;
+      case 0xBD: // JSR
+      case 0xCD:
+      case 0xDD:
+      case 0xED:
+      case 0xFD:
+        pushpc(); pc=ea; break;
+      case 0x81: // RTS
+        poppc(); break;
+      case 0x83: // SWI
+        pushpc(); push(x); push(a); pushc();
+        cc.i=1; pc=HILO(0x1ffc); break;
+      case 0x80: // RTI
+        popc(); a=pop(); x=pop(); poppc(); break;
+      case 0x9C: // RSP
+        sp=spHi; break;
+      case 0x96: // TSX
+        *ex=sp&0xFF; break;
+      case 0x94: // TXS (ST7)
+        sp=(sp&0xFF00)|*ex; break;
+      case 0x9E: // TSA
+        a=sp&0xFF; break;
+      case 0x95: // TAS (ST7)
+        sp=(sp&0xFF00)|a; break;
+      case 0x84: // POPA (ST7)
+        a=pop(); break;
+      case 0x85: // POPX (ST7)
+        *ex=pop(); break;
+      case 0x86: // POPC (ST7)
+        popc(); break;
+      case 0x88: // PUSHA (ST7)
+        push(a); break;
+      case 0x89: // PUSHX (ST7)
+        push(*ex); break;
+      case 0x8A: // PUSHC (ST7)
+        pushc(); break;
+      case 0x98: // CLC
+        cc.c=0; break;
+      case 0x99: // SEC
+        cc.c=1; break;
+      case 0x9A: // CLI
+        cc.i=0; break;
+      case 0x9B: // SEI
+        cc.i=1; break;
+      case 0x9D: // NOP
+        break;
+
+      case 0x71: // LDD (ST19)
+      case 0x72:
+      case 0x75:
+        dr=op; break;
+      case 0x7B: // TAD (ST19)
+        dr=a; break;
+      case 0x8B: // TDA (ST19)
+        a=dr; break;
+      case 0x8C: // TCA (ST19)
+        a=cr; break;
+      case 0xAC: // PUSHD (ST19)
+        push(dr); break;
+      case 0xAF: // POPD (ST19)
+        dr=pop(); break;
+      case 0x87: // PRTS (ST19)
+        cr=pop(); poppc(); break;
+      case 0x8D: // PJSR (ST19)
+        if(paged) {
+          ea=HILO(pc); pc+=2;
+          CCLOGLBPUT("[%s] -> ",PADDR(cr,ea));
+          }
+        else {
+          ea=pc; pc+=3;
+          }
+        pr=Get(ea++);
+        ea=HILO(ea);
+        CCLOGLBPUT("%s ",PADDR(pr,ea));
+        pushpc(); push(cr); cr=pr; pc=ea; break;
+
+      case 0x90: // pre-bytes
+      case 0x91:
+      case 0x92:
+      case 0x31:
+      case 0x32:
+        PRINTF(L_SYS_EMU,"pre-byte %02x in command decoding (count=%d)",ins,count);
+        loglb->cLineBuff::Flush();
+        return 3;
+      default:
+        PRINTF(L_SYS_EMU,"unsupported instruction 0x%02x (count=%d)",ins,count);
+        loglb->cLineBuff::Flush();
+        return 3;
+      }
+
+    // write operant
+    if(flags & 2) {
+      switch(((flags&8) ? flags:ins)>>4) {
+        case 0x2:                      // no or special address mode
+        case 0x8:
+        case 0x9:
+          break;
+        case 0xA:                      // immediate
+        case 0x3:                      // short
+        case 0xB:
+        case 0xC:                      // long
+        case 0xD:                      // long indexed
+        case 0x6:                      // short indexed
+        case 0xE:
+        case 0x7:                      // indexed
+        case 0xF:
+        case 0x0:                      // bit
+        case 0x1:
+          Set(ea,op); break;
+        case 0x4:                      // inherent A
+          a=op; break;
+        case 0x5:                      // inherent X/Y
+          *ex=op; break;
+        }
+      }
+    if(doDisAsm) PUTLB(disAsmLogClass,loglb);
+    else loglb->cLineBuff::Flush();
+
+    for(int i=numBp-1 ; i>=0 ; i--) {
+      if(bp[i]==pc) {
+        PRINTF(L_SYS_EMU,"6805: breakpoint at %04x (count=%d)",pc,count);
+        return 0;
+        }
+      }
+    if(count>=max_count) {
+      PRINTF(L_SYS_EMU,"max. instruction counter exceeded (count=%d)",count);
+      return 2;
+      }
+    }
+}
+
+void c6805::branch(bool branch)
+{
+  if(doDisAsm) {
+    unsigned char off=Get(pc);
+    if(indirect) {
+      LOGLBPUT("[%02x] -> ",off);
+      off=Get(off);
+      }
+    unsigned short npc=pc+off+1;
+    if(off&0x80) npc-=0x100; // gcc fixup. take care of sign
+    LOGLBPUT("%s ",PADDR(cr,npc));
+    if(branch) LOGLBPUT("(taken) ");
+    }
+  pc++;
+  if(branch) {
+    unsigned char offset=Get(pc-1);
+    if(indirect) offset=Get(offset);
+    pc+=offset;
+    if(offset&0x80) pc-=0x100; // gcc fixup. take care of sign
+    }
+}
+
+void c6805::push(unsigned char c)
+{
+  Set(sp--,c);
+}
+
+unsigned char c6805::pop(void)
+{
+  return Get(++sp);
+}
+
+void c6805::pushpc(void)
+{
+  push(pc & 0xff);
+  push(pc >> 8);
+}
+
+void c6805::poppc(void)
+{
+  pc=(pop()<<8) | pop();
+}
+
+void c6805::pushc(void)
+{
+  unsigned char c=0xC0+(cc.v?32:0)+(cc.h?16:0)+(cc.i?8:0)+(cc.n?4:0)+(cc.z?2:0)+(cc.c?1:0);
+  push(c);
+}
+
+void c6805::popc(void)
+{
+  unsigned char c=pop();
+  cc.v=(c&32) ? 1:0;
+  cc.h=(c&16) ? 1:0;
+  cc.i=(c& 8) ? 1:0;
+  cc.n=(c& 4) ? 1:0;
+  cc.z=(c& 2) ? 1:0;
+  cc.c=(c& 1) ? 1:0;
+}
+
+void c6805::tst(unsigned char c)
+{
+  cc.z=!c;
+  cc.n=bitset(c,7);
+}
+
+unsigned char c6805::add(unsigned char op, unsigned char c)
+{
+  unsigned short res_half=(a&0x0f) + (op&0x0f) + c;
+  unsigned short res=(unsigned short)a + (unsigned short)op + (unsigned short)c;
+  cc.h=res_half > 0x0f;
+  cc.c=res > 0xff;
+  res&=0xff;
+  cc.v=((op+c)&0x80) ? ((a&0x80) && !(res&0x80)) : (!(a&0x80) && (res&0x80));
+  tst(res);
+  return res;
+}
+
+unsigned char c6805::sub(unsigned char op1, unsigned char op2, unsigned char c)
+{
+  short res=(short)op1 - (short)op2 - (short)c;
+  cc.c=res < 0;
+  res&=0xff;
+  cc.v=((op2+c)&0x80) ? (!(op1&0x80) && (res&0x80)) : ((op1&0x80) && !(res&0x80));
+  tst(res);
+  return res;
+}
+
+unsigned char c6805::rollR(unsigned char op, unsigned char c)
+{
+  cc.c=bitset(op,0);
+  op >>= 1;
+  op |= c << 7;
+  tst(op);
+  return op;
+}
+
+unsigned char c6805::rollL(unsigned char op, unsigned char c)
+{
+  cc.c=bitset(op,7);
+  op <<= 1;
+  op |= c;
+  tst(op);
+  return op;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/cpu.h
new file mode 100644 (file)
index 0000000..963affd
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __NAGRA_CPU_H
+#define __NAGRA_CPU_H
+
+#define FILEMAP_DOMAIN "nagra"
+
+class cFileMap;
+class cLineBuff;
+class c6805;
+
+// ----------------------------------------------------------------
+
+class cMap {
+friend class c6805;
+protected:
+  cLineBuff *loglb;
+public:
+  virtual ~cMap() {};
+  virtual unsigned char Get(unsigned short ea)=0;
+  virtual void Set(unsigned short ea, unsigned char val)=0;
+  virtual bool IsFine(void)=0;
+};
+
+// ----------------------------------------------------------------
+
+class cMapMem : public cMap {
+protected:
+  unsigned short offset;
+  unsigned char *mem;
+  int size;
+public:
+  cMapMem(unsigned short Offset, int Size);
+  virtual ~cMapMem();
+  virtual unsigned char Get(unsigned short ea);
+  virtual void Set(unsigned short ea, unsigned char val);
+  virtual bool IsFine(void);
+  };
+
+// ----------------------------------------------------------------
+
+class cMapRom : public cMap {
+private:
+  unsigned short offset;
+  cFileMap *fm;
+  unsigned char *addr;
+  int size;
+public:
+  cMapRom(unsigned short Offset, const char *Filename, int InFileOffset=0);
+  virtual ~cMapRom();
+  virtual unsigned char Get(unsigned short ea);
+  virtual void Set(unsigned short ea, unsigned char val);
+  virtual bool IsFine(void);
+  };
+
+// ----------------------------------------------------------------
+
+class cMapEeprom : public cMap {
+private:
+  unsigned short offset;
+  cFileMap *fm;
+  unsigned char *addr;
+  int size, otpSize;
+public:
+  cMapEeprom(unsigned short Offset, const char *Filename, int OtpSize, int InFileOffset=0);
+  virtual ~cMapEeprom();
+  virtual unsigned char Get(unsigned short ea);
+  virtual void Set(unsigned short ea, unsigned char val);
+  virtual bool IsFine(void);
+  };
+
+// ----------------------------------------------------------------
+
+#define MAX_BREAKPOINTS 24
+#define MAX_MAPPER      10
+#define MAX_PAGES       5
+#define PAGE_SIZE       32*1024
+#define EXPT_MAX        16
+
+#define bitset(d,bit) (((d)>>(bit))&1)
+
+#define HILO(ea)      ((Get(ea)<<8)+Get((ea)+1))
+#define HILOS(s,ea)   ((Get((s),(ea))<<8)+Get((s),(ea)+1))
+
+class c6805 {
+private:
+  unsigned short pc, sp, spHi, spLow, exptBase;
+  unsigned short bp[MAX_BREAKPOINTS], numBp;
+  unsigned char mapMap[(MAX_PAGES+1)*PAGE_SIZE];
+  cMap *mapper[MAX_MAPPER];
+  int nextMapper;
+  int pageMap[256];
+  bool indirect;
+  unsigned int clockcycles;
+  bool exptPending, exptReady, expt[EXPT_MAX];
+  int timerDisable, delayTimerDisable;
+  int instruction;
+  //
+  void InitMapper(void);
+  void ClearMapper(void);
+  void branch(bool branch);
+  inline void tst(unsigned char c);
+  inline void push(unsigned char c);
+  inline unsigned char pop(void);
+  void pushpc(void);
+  void poppc(void);
+  void pushc(void);
+  void popc(void);
+  unsigned char add(unsigned char op, unsigned char c);
+  unsigned char sub(unsigned char op1, unsigned char op2, unsigned char c);
+  unsigned char rollR(unsigned char op, unsigned char c);
+  unsigned char rollL(unsigned char op, unsigned char c);
+protected:
+  unsigned char a, x, y, cr, dr;
+  struct CC { unsigned char c, z, n, i, h, v; } cc;
+  bool hasReadHandler, hasWriteHandler;
+  //
+  int Run(int max_count);
+  void AddBreakpoint(unsigned short addr);
+  void ClearBreakpoints(void);
+  void ClearExceptions(void);
+  bool AddMapper(cMap *map, unsigned short start, int size, unsigned char seg=0);
+  cMap* FindMapper(unsigned char seg, unsigned short ea);
+  void ResetMapper(void);
+  void Set(unsigned short ea, unsigned char val);
+  void Set(unsigned char seg, unsigned short ea, unsigned char val);
+  void GetMem(unsigned short addr, unsigned char *data, int len, unsigned char seg=0) const;
+  void SetMem(unsigned short addr, const unsigned char *data, int len, unsigned char seg=0);
+  void ForceSet(unsigned short ea, unsigned char val, bool ro);
+  void SetSp(unsigned short SpHi, unsigned short SpLow);
+  void SetPc(unsigned short addr, unsigned char seg=0);
+  void ResetCycles();
+  void PopPc(void);
+  void PopCr(void);
+  void Push(unsigned char c);
+  virtual void Stepper(void)=0;
+  virtual void WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op) {}
+  virtual void ReadHandler(unsigned char seg, unsigned short ea, unsigned char &op) {}
+  virtual void TimerHandler(unsigned int num) {}
+private:
+  unsigned int stats[256];
+  char addrBuff[32];
+  bool doDisAsm;
+  int disAsmLogClass;
+protected:
+  cLineBuff *loglb;
+  char * PADDR(unsigned char s, unsigned short ea);
+public:
+  c6805(void);
+  virtual ~c6805();
+  unsigned char Get(unsigned short ea) const;
+  unsigned char Get(unsigned char seg, unsigned short ea) const;
+  unsigned short GetPc(void) const { return pc; }
+  unsigned short GetCr(void) const { return cr; }
+  unsigned short GetSp(void) const { return sp; }
+  unsigned int Cycles() const { return clockcycles; }
+  void AddCycles(unsigned int num);
+  void RaiseException(int num);
+  void SetExptBase(unsigned short base) { exptBase=base; }
+  void DisableTimers(int num) { delayTimerDisable = num; }
+  int Show_Diss;
+  int LoopCount;
+  char displaystr1[80];
+  char displaystr2[80];
+  char txt[80];
+};
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/debug.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/debug.h
new file mode 100644 (file)
index 0000000..60ae793
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __NAGRA_DEBUG_H
+#define __NAGRA_DEBUG_H
+
+#define DEBUG_EMU      // debug CardEmu (very verbose!)
+#define DEBUG_EMU_0x80 // if the above is enabled, limit output to range x080-0xc0
+//#define DEBUG_STAT     // give some statistics on CardEmu
+//#define DEBUG_MAP      // debug file mapping code
+//#define DEBUG_NAGRA    // debug Nagra crypt code
+//#define DEBUG_LOG      // debug Nagra logger code
+
+#ifdef DEBUG_EMU
+#define dee(x) { (x); }
+#ifdef DEBUG_EMU_0x80
+#define de(x) { if(pc80flag) { (x); } }
+#else
+#define de(x) { (x); }
+#endif
+#else
+#define de(x) ;
+#define dee(x) ;
+#endif
+
+#ifdef DEBUG_NAGRA
+#define dn(a) { a; }
+#else
+#define dn(a) ;
+#endif
+
+#ifdef DEBUG_LOG
+#define dl(x) { (x); }
+#else
+#define dl(x) ; 
+#endif
+
+#endif
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/log-nagra.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/log-nagra.h
new file mode 100644 (file)
index 0000000..23dec2e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __LOG_NAGRA_H
+#define __LOG_NAGRA_H
+
+#include "log-sys.h"
+
+#define L_SYS          7
+#define L_SYS_EMU      LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_DISASM   LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_DISASM80 LCLASS(L_SYS,L_SYS_LASTDEF<<3)
+#define L_SYS_CPUSTATS LCLASS(L_SYS,L_SYS_LASTDEF<<4)
+#define L_SYS_MAP      LCLASS(L_SYS,L_SYS_LASTDEF<<5)
+#define L_SYS_RAWEMM   LCLASS(L_SYS,L_SYS_LASTDEF<<6)
+#define L_SYS_RAWECM   LCLASS(L_SYS,L_SYS_LASTDEF<<7)
+#define L_SYS_ALL      LALL(L_SYS_RAWECM)
+
+#define bprint(a) {fprintf(stdout, #a "="); BN_print_fp(stdout,a); fprintf(stdout,"\n");}
+
+#endif
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra-def.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra-def.h
new file mode 100644 (file)
index 0000000..71b43bd
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __NAGRA_NAGRA_DEF_H
+#define __NAGRA_NAGRA_DEF_H
+
+// ----------------------------------------------------------------
+
+#define SYSTEM_NAGRA         0x1800
+#define SYSTEM_NAGRA2        0x1801
+#define SYSTEM_NAGRA_BEV     0x1234
+
+// ----------------------------------------------------------------
+
+// Nagra1 defines
+#define DEF_TYPE 0
+#define DEF_PK   2
+#define DEF_ROM  0
+
+#define TYPE(keynr)         (((keynr)>>16)&1)
+#define PK(keynr)           (((keynr)>>17)&3)
+#define ROM(keynr)          (((keynr)>>19)&15)
+#define KEYSET(rom,pk,type) ((((rom)&15)<<3)|(((pk)&3)<<1)|((type)&1))
+
+// Nagra2 defines
+#define N2_MAGIC   0x80
+#define N2_EMM_G_I 0x02
+#define N2_EMM_G_R 0x12
+#define N2_EMM_S_I 0x01
+#define N2_EMM_S_R 0x11
+#define N2_EMM_SEL 0x40
+#define N2_EMM_V   0x03
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.c
new file mode 100644 (file)
index 0000000..09fa884
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nagra.h"
+#include "nagra-def.h"
+#include "log-nagra.h"
+#include "log-core.h"
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF|L_SYS_EMU|L_SYS_DISASM80|L_SYS_MAP)&LOPT_MASK,
+  "nagra",
+  { L_SYS_DEFNAMES,"emu","disasm","disasm80","cpuStats","map","rawemm","rawecm" }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+int minEcmTime=400; // ms
+
+// -- cPlainKeyNagra -----------------------------------------------------------
+
+#define PLAINLEN_NAGRA_H 8
+#define PLAINLEN_NAGRA_I 16
+#define PLAINLEN_NAGRA_B 64
+#define PLAINLEN_NAGRA_IE 24
+#define PLAINLEN_NAGRA_BE 96
+
+static cPlainKeyTypeReg<cPlainKeyNagra,'N'> KeyReg;
+
+cPlainKeyNagra::cPlainKeyNagra(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyNagra::IsBNKey(int kn)
+{
+  switch(kn & C2MASK) {
+    // Nagra1
+    case MBC('M','1'):
+    case MBC('E','1'):
+    case MBC('N','1'):
+    case MBC('N','2'):
+    // Nagra2
+    case MBC(N2_MAGIC,N2_EMM_G_R):
+    case MBC(N2_MAGIC,N2_EMM_S_R):
+    case MBC(N2_MAGIC,N2_EMM_G_R|N2_EMM_SEL):
+    case MBC(N2_MAGIC,N2_EMM_S_R|N2_EMM_SEL):
+      return true;
+    }
+  return false;
+}
+
+bool cPlainKeyNagra::IsBNKey(void) const
+{
+  return IsBNKey(keynr);
+}
+
+int cPlainKeyNagra::ParseTag(const char *tag, const char * &line)
+{
+  int r=-1;
+  const int l=strlen(tag);
+  if(!strncasecmp(line,tag,l)) {
+    line+=l; r=0;
+    while(*line!=0 && isdigit(*line)) {
+      r=r*10 + *line-'0';
+      line++;
+      }
+    line=skipspace(line);
+    }
+  return r;
+}
+
+void cPlainKeyNagra::GetTagDef(int nr, int &romnr, int &pk, int &keytype)
+{
+  romnr  =DEF_ROM;
+  pk     =DEF_PK;
+  keytype=DEF_TYPE;
+  nr&=C2MASK;
+  if(nr=='V' || nr==MBC('N','2') || nr==MBC('M','1'))
+    pk=0; // different default
+}
+
+bool cPlainKeyNagra::Parse(const char *line)
+{
+  unsigned char sid[2];
+  int len;
+  if(GetChar(line,&type,1) && (len=GetHex(line,sid,2,false))) {
+    type=toupper(type); id=Bin2Int(sid,len); 
+    line=skipspace(line);
+    bool ok;
+    if(!strncasecmp(line,"NN",2)) {
+      line=skipspace(line+2);
+      unsigned char skeynr;
+      if((ok=GetHex(line,&skeynr,1)))
+        keynr=MBC(N2_MAGIC,skeynr);
+      }
+    else {
+      bool tags;
+      switch(toupper(*line)) {
+        case 'E':
+        case 'N': ok=GetChar(line,&keynr,2); tags=true; break;
+        case 'M': ok=GetChar(line,&keynr,2); tags=false; break;
+        case 'V': ok=GetChar(line,&keynr,1); tags=true; break;
+        default:  {
+                  unsigned char skeynr;
+                  ok=GetHex(line,&skeynr,1); keynr=skeynr; tags=false; break;
+                  }
+        }
+      if(ok) {
+        int romnr, pk, keytype;
+        GetTagDef(keynr,romnr,pk,keytype);
+        line=skipspace(line);
+        while(tags) {
+          int r;
+          if((r=ParseTag("ROM",line))>=0) {
+            if(r>0 && r<=15) romnr=r;
+            else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad ROM number %d",r);
+            }
+          else if((r=ParseTag("TYP",line))>=0) {
+            if(r>=0 && r<=1) keytype=r;
+            else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad key type %d",r);
+            }
+          else if((r=ParseTag("PK",line))>=0) {
+            if(r>=0 && r<=2) pk=r;
+            else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad PK key number %d",pk);
+            }
+          else {
+            tags=false;
+            if(pk!=0 && (keynr=='V' || keynr==MBC('N','2'))) {
+              pk=0;
+              PRINTF(L_CORE_LOAD,"nagrakey: auto-adjusting to PK0 for N2/V key");
+              }
+            if(romnr!=0 && keytype!=1) {
+              keytype=1;
+              PRINTF(L_CORE_LOAD,"nagrakey: auto-adjusting to TYP1 for ROM key");
+              }
+            }
+          }
+        if(IsBNKey() || keynr=='V') keynr=ADDC3(keynr,KEYSET(romnr,pk,keytype));
+        }
+      }
+
+    unsigned char skey[PLAINLEN_NAGRA_BE];
+    int keylen=PLAINLEN_NAGRA_BE;
+    len=GetHex(line,skey,keylen,false);
+    if(   (!IsBNKey() && (len==PLAINLEN_NAGRA_H || len==PLAINLEN_NAGRA_I || len==PLAINLEN_NAGRA_IE))
+       || ( IsBNKey() && (len==PLAINLEN_NAGRA_B || len==PLAINLEN_NAGRA_BE)))
+      keylen=len;
+    if(len==keylen) {
+      if((keynr=='V' || keynr==MBC(N2_MAGIC,0x03) || keynr==MBC(N2_MAGIC,(0x03|N2_EMM_SEL))) && CheckNull(skey,len))
+        PRINTF(L_GEN_WARN,"nagrakey: FAKE verify keys will cause problems. Please remove them!");
+      SetBinKey(skey,len);
+      return true;
+      }
+    }
+  return false;
+}
+
+cString cPlainKeyNagra::PrintKeyNr(void)
+{
+  if(C2(keynr)==N2_MAGIC)
+    return cString::sprintf("NN %.2X",keynr&0xFF);
+  int k=keynr & C2MASK;
+  if(!IsBNKey() && k!='V')
+    return cString::sprintf("%.2X",keynr);
+  char nr[32];
+  int q=0;
+  if(IsBNKey()) nr[q++]=(keynr>>8) & 0xff;
+  nr[q++]=keynr & 0xff;
+  nr[q]=0;
+  int romnr, pk, keytype;
+  GetTagDef(keynr,romnr,pk,keytype);
+  if(ROM(keynr) !=romnr  ) q+=snprintf(nr+q,sizeof(nr)-q," ROM%d",ROM(keynr));
+  if(PK(keynr)  !=pk     ) q+=snprintf(nr+q,sizeof(nr)-q," PK%d",PK(keynr));
+  if(TYPE(keynr)!=keytype) q+=snprintf(nr+q,sizeof(nr)-q," TYP%d",TYPE(keynr));
+  return nr;
+}
+
+// -- cNagra -------------------------------------------------------------------
+
+cNagra::cNagra(void)
+{
+  BN_set_word(pubExp,3);
+}
+
+void cNagra::CreateRSAPair(const unsigned char *key, const unsigned char *data, BIGNUM *e, BIGNUM *m)
+{
+  // Calculate P and Q from data
+  cBN p,q;
+  CreatePQ(key,p,q);
+  ExpandPQ(p,q,data,e,m);
+}
+
+void cNagra::ExpandPQ(BIGNUM *p, BIGNUM *q, const unsigned char *data, BIGNUM *e, BIGNUM *m)
+{
+  // Calculate N=P*Q (modulus)
+  cBNctx ctx;
+  BN_mul(m,p,q,ctx);
+  if(data) BN_bin2bn(data,64,e); // set provided data as E1
+  else {                         // else calculate the 'official' one
+    // E = ( ( ( (P-1) * (Q-1) * 2) + 1) / 3)
+    BN_sub_word(p,1);
+    BN_sub_word(q,1);
+    BN_mul(e,p,q,ctx);
+    BN_mul_word(e,2);
+    BN_add_word(e,1);
+    BN_div_word(e,3);
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.h
new file mode 100644 (file)
index 0000000..aedad54
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __NAGRA_NAGRA_H
+#define __NAGRA_NAGRA_H
+
+#include "system-common.h"
+#include "crypto.h"
+
+// ----------------------------------------------------------------
+
+extern int minEcmTime;
+
+// ----------------------------------------------------------------
+
+class cPlainKeyNagra : public cDualKey {
+private:
+  int ParseTag(const char *tag, const char * &line);
+  void GetTagDef(int nr, int &romnr, int &pk, int &keytype);
+protected:
+  virtual bool IsBNKey(void) const;
+  virtual int IdSize(void) { return 4; }
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeyNagra(bool Super);
+  virtual bool Parse(const char *line);
+  static bool IsBNKey(int kn);
+  };
+
+// ----------------------------------------------------------------
+
+class cNagra {
+protected:
+  cRSA rsa;
+  cBN pubExp;
+  //
+  virtual void CreatePQ(const unsigned char *pk, BIGNUM *p, BIGNUM *q)=0;
+  void ExpandPQ(BIGNUM *p, BIGNUM *q, const unsigned char *data, BIGNUM *e, BIGNUM *m);
+  void CreateRSAPair(const unsigned char *key, const unsigned char *data, BIGNUM *e, BIGNUM *m);
+public:
+  cNagra(void);
+  virtual ~cNagra() {};
+  };
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra.mk
new file mode 100644 (file)
index 0000000..d511b13
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Nagra
+#
+TARGET = nagra
+OBJS   = nagra.o nagra1.o nagra2.o cpu.o \
+         $(patsubst %.c,%.o,$(wildcard nagra2-[0-9][0-9][0-9][0-9].c))
+LIBS   = -lcrypto
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra1.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra1.c
new file mode 100644 (file)
index 0000000..1eddc3d
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "system.h"
+#include "misc.h"
+#include "opts.h"
+#include "helper.h"
+
+#include "nagra.h"
+#include "nagra-def.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define SYSTEM_NAME          "Nagra"
+#define SYSTEM_PRI           -10
+
+// -- cEmu ---------------------------------------------------------------------
+
+#define MAX_COUNT 200000
+
+class cEmu : public c6805 {
+protected:
+  int romNr, id;
+  char *romName, *romExtName, *eepromName;
+  //
+  int InitStart, InitEnd;
+  int EmmStart, EmmEnd, EmmKey0, EmmKey1;
+  int FindKeyStart, FindKeyEnd;
+  int MapAddr;
+  int Rc1H, Rc1L;
+  int EnsIrdChk, Cmd83Chk;
+  int SoftInt, StackHigh;
+  //
+  bool AddRom(unsigned short addr, unsigned short size, const char *name);
+  bool AddEeprom(unsigned short addr, unsigned short size, unsigned short otpSize, const char *name);
+  //
+  virtual bool InitSetup(void) { return true; }
+  virtual bool UpdateSetup(const unsigned char *emm) { return true; }
+  virtual bool MathMapHandler(void);
+public:
+  cEmu(void);
+  virtual ~cEmu();
+  bool Init(int RomNr, int Id);
+  bool GetOpKeys(const unsigned char *Emm, unsigned char *id, unsigned char *key0, unsigned char *key1);
+  bool GetPkKeys(const unsigned char *select, unsigned char *pkset);
+  bool Matches(int RomNr, int Id);
+  };
+
+cEmu::cEmu(void)
+{
+  romName=romExtName=eepromName=0;
+}
+
+cEmu::~cEmu()
+{
+  free(romName);
+  free(romExtName);
+  free(eepromName);
+}
+
+bool cEmu::AddRom(unsigned short addr, unsigned short size, const char *name)
+{
+  cMap *map=new cMapRom(addr,name);
+  return AddMapper(map,addr,size);
+}
+
+bool cEmu::AddEeprom(unsigned short addr, unsigned short size, unsigned short otpSize, const char *name)
+{
+  cMap *map=new cMapEeprom(addr,name,otpSize);
+  return AddMapper(map,addr,size);
+}
+
+bool cEmu::Matches(int RomNr, int Id)
+{
+  return (romNr==RomNr && id==Id);
+}
+
+bool cEmu::Init(int RomNr, int Id)
+{
+  romNr=RomNr; id=Id;
+  asprintf(&romName,"ROM%d.bin",romNr);
+  asprintf(&romExtName,"ROM%dext.bin",romNr);
+  asprintf(&eepromName,"eep%i_%02x.bin",romNr,(id&0xff00)>>8);
+  if(InitSetup()) {
+    ForceSet(EnsIrdChk, 0x81,true);
+    ForceSet(Cmd83Chk+0,0x98,true);
+    ForceSet(Cmd83Chk+1,0x9d,true);
+    if(SoftInt) {
+      Set(0x1ffc,SoftInt>>8);
+      Set(0x1ffd,SoftInt&0xff);
+      }
+    SetSp(StackHigh,0);
+    SetPc(InitStart);
+    ClearBreakpoints();
+    AddBreakpoint(InitEnd);
+    if(!Run(MAX_COUNT)) return true;
+    }
+  return false;
+}
+
+bool cEmu::GetOpKeys(const unsigned char *Emm, unsigned char *id, unsigned char *key0, unsigned char *key1)
+{
+  int keys=0;
+  if(UpdateSetup(Emm)) {
+    SetMem(0x0080,&Emm[0],64);
+    SetMem(0x00F8,&Emm[1],2);
+    SetPc(EmmStart);
+    ClearBreakpoints();
+    AddBreakpoint(EmmEnd);
+    AddBreakpoint(MapAddr);
+    AddBreakpoint(EmmKey0);
+    AddBreakpoint(EmmKey1);
+    while(!Run(MAX_COUNT)) {
+      unsigned short pc=GetPc();
+      if(pc==EmmKey0) {
+        GetMem(0x82,key0,8);
+        keys++;
+        }
+      if(pc==EmmKey1) {
+        GetMem(0x82,key1,8);
+        keys++;
+        }
+      if(pc==MapAddr) {
+        if(!MathMapHandler()) break;
+        PopPc(); // remove return address from stack
+        }
+      if(pc==EmmEnd) {
+        GetMem(0x00F8,id,2);
+        break;
+        }
+      }
+    }
+  return (keys==2);
+}
+
+bool cEmu::GetPkKeys(const unsigned char *select, unsigned char *pkset)
+{
+  Set(0x0081,select[2]<<7);
+  SetMem(0x00F8,select,3);
+  SetPc(FindKeyStart);
+  ClearBreakpoints();
+  AddBreakpoint(FindKeyEnd);
+  while(!Run(MAX_COUNT)) {
+    unsigned short pc=GetPc();
+    if(pc==FindKeyEnd) {
+      if(!cc.c) {
+        PRINTF(L_SYS_EMU,"Updating PK keys");
+        unsigned short loc=(Get(Rc1H)<<8)+Get(Rc1L);
+        for(int i=0; i<45; i+=15) GetMem(loc+4+i,pkset+i,15);
+        return true;
+        }
+      else {
+        PRINTF(L_SYS_EMU,"Updating PK keys failed. Used a correct EEPROM image for provider %04x ?",((select[0]<<8)|select[1]));
+        break;
+        }
+      }
+    }
+  return false;
+}
+
+bool cEmu::MathMapHandler(void)
+{
+  PRINTF(L_SYS_EMU,"Unsupported math call $%02x in ROM %d, please report",a,romNr);
+  return false;
+}
+
+// -- cEmuRom3Core -------------------------------------------------------------
+
+class cEmuRom3Core : public cEmu {
+private:
+  bool special05;
+protected:
+  virtual void Stepper(void);
+  virtual void WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op);
+  virtual void ReadHandler(unsigned char seg, unsigned short ea, unsigned char &op);
+  bool CoreInitSetup(void);
+  bool DoMaps(bool hasExt);
+public:
+  cEmuRom3Core(void);
+  };
+
+cEmuRom3Core::cEmuRom3Core(void)
+{
+  special05=false;
+}
+
+void cEmuRom3Core::WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op)
+{
+  if(ea==0x05) special05=(op&0x40)!=0;
+}
+
+void cEmuRom3Core::ReadHandler(unsigned char seg, unsigned short ea, unsigned char &op)
+{
+  if(special05) {
+    special05=false; // prevent loop
+    unsigned short start=Get(0x30C0);
+    unsigned short end=Get(0x30C1);
+    if(((ea>>8)>=start) && ((ea>>8)<=end)) op=0x00; // dataspace
+    else op=0x01;                                   // codespace
+    special05=true;
+    }
+  else
+    switch(ea) {
+      case 0x06:
+      case 0x07:
+        if(!(Get(0x04)&4)) op=random()&0xFF;
+        break;
+      }
+}
+
+void cEmuRom3Core::Stepper(void)
+{}
+
+bool cEmuRom3Core::CoreInitSetup(void)
+{
+  ForceSet(0x0001,1<<3,true);
+  Set(0x0002,0x03);
+  Set(0x004e,0x4B);
+  return true;
+}
+
+bool cEmuRom3Core::DoMaps(bool hasExt)
+{
+  // Eeprom & ROMext are non-fatal also they are required for some providers
+  if(hasExt) AddRom(0x2000,0x2000,romExtName);
+  AddEeprom(0xE000,0x1000,0x20,eepromName);
+  return AddRom(0x4000,0x4000,romName);
+}
+
+// -- cEmuRom3 -----------------------------------------------------------------
+
+class cEmuRom3 : public cEmuRom3Core {
+protected:
+  virtual bool InitSetup(void);
+  virtual bool UpdateSetup(const unsigned char *emm);
+public:
+  cEmuRom3(void);
+  };
+
+cEmuRom3::cEmuRom3(void)
+{
+  InitStart=0x4000;
+  InitEnd  =0x734b;
+  EmmStart =0x5a82;
+  EmmEnd   =0x67db;
+  EmmKey0  =0x617c;
+  EmmKey1  =0x6184;
+  FindKeyStart=0x6133;
+  FindKeyEnd  =0x6147;
+  MapAddr  =0x2800;
+  Rc1H     =0x0024;
+  Rc1L     =0x0025;
+  EnsIrdChk=0x6437;
+  Cmd83Chk =0x6431;
+  SoftInt  =0x0000;
+  StackHigh=0x7f;
+}
+
+bool cEmuRom3::InitSetup(void)
+{
+  return DoMaps(true) && CoreInitSetup();
+}
+
+bool cEmuRom3::UpdateSetup(const unsigned char *emm)
+{
+  return CoreInitSetup();
+}
+
+// -- cEmuRom7 -----------------------------------------------------------------
+
+class cEmuRom7 : public cEmuRom3Core {
+protected:
+  virtual bool InitSetup(void);
+  virtual bool UpdateSetup(const unsigned char *emm);
+public:
+  cEmuRom7(void);
+  };
+
+cEmuRom7::cEmuRom7(void)
+{
+  InitStart=0x4000;
+  InitEnd  =0x7b68;
+  EmmStart =0x482b;
+  EmmEnd   =0x6146;
+  EmmKey0  =0x4f2a;
+  EmmKey1  =0x4f32;
+  FindKeyStart=0x4ee4;
+  FindKeyEnd  =0x4ef5;
+  MapAddr  =0x200f;
+  Rc1H     =0x0028;
+  Rc1L     =0x0029;
+  EnsIrdChk=0x51df;
+  Cmd83Chk =0x51d9;
+  SoftInt  =0x4008;
+  StackHigh=0x7f;
+}
+
+bool cEmuRom7::InitSetup(void)
+{
+  return DoMaps(false) && CoreInitSetup();
+}
+
+bool cEmuRom7::UpdateSetup(const unsigned char *emm)
+{
+  SetMem(0x01A2,&emm[1],2);
+  return true;
+}
+
+// -- cEmuRom10Core ------------------------------------------------------------
+
+class cEmuRom10Core : public cEmu {
+protected:
+  struct Map { unsigned char A[64], B[64], C[64], D[4], opSize; } map;
+  //
+  virtual void Stepper(void);
+  bool DoMaps(bool hasExt, int romSize);
+  bool CoreInitSetup(void);
+  bool CoreUpdateSetup(const unsigned char *emm);
+  };
+
+void cEmuRom10Core::Stepper(void)
+{
+  int rnd=random();
+  unsigned char mem7=Get(0x07);
+  if(cc.i) mem7&=0xFD; else mem7|=0x02;
+  Set(0x07,mem7);
+  if(bitset(mem7,1) && bitset(mem7,7)) {
+    Set(0x05,(rnd&0xFF00)>>8);
+    Set(0x06,rnd&0xFF);
+    }
+}
+
+bool cEmuRom10Core::DoMaps(bool hasExt, int romSize)
+{
+  // Eeprom & ROMext are non-fatal also they are required for some providers
+  if(hasExt) AddRom(0x2000,0x2000,romExtName);
+  AddEeprom(0xC000,0x2000,0x40,eepromName);
+  return AddRom(0x4000,romSize,romName);
+}
+
+bool cEmuRom10Core::CoreInitSetup(void)
+{
+  ForceSet(0x01,0x13,true);
+  Set(0x02,0x3);
+  Set(0x07,0xFF);
+  return true;
+}
+
+bool cEmuRom10Core::CoreUpdateSetup(const unsigned char *emm)
+{
+  SetMem(0x01A2,&emm[1],2);
+  SetMem(0x0307,&emm[1],2);
+  Set(0x0300,emm[5]);
+  Set(0x0301,emm[2]);
+  return true;
+}
+
+// -- cEmuRom10 ----------------------------------------------------------------
+
+class cEmuRom10 : public cEmuRom10Core {
+protected:
+  virtual bool InitSetup(void);
+  virtual bool UpdateSetup(const unsigned char *emm);
+  virtual bool MathMapHandler(void);
+public:
+  cEmuRom10(void);
+  };
+
+cEmuRom10::cEmuRom10(void)
+{
+  InitStart=0x4000;
+  InitEnd  =0x81ca;
+  EmmStart =0x6a71;
+  EmmEnd   =0x81f7;
+  EmmKey0  =0x7172;
+  EmmKey1  =0x717a;
+  FindKeyStart=0x712c;
+  FindKeyEnd  =0x713d;
+  MapAddr  =0x2020;
+  Rc1H     =0x004a;
+  Rc1L     =0x004b;
+  EnsIrdChk=0x7427;
+  Cmd83Chk =0x7421;
+  SoftInt  =0x4004;
+  StackHigh=0x3ff;
+}
+
+bool cEmuRom10::InitSetup(void)
+{
+  return DoMaps(false,0x5A00) && CoreInitSetup();
+}
+
+bool cEmuRom10::UpdateSetup(const unsigned char *emm)
+{
+  Set(0x006e,0x4B);
+  return CoreUpdateSetup(emm);
+}
+
+bool cEmuRom10::MathMapHandler(void)
+{
+  PRINTF(L_SYS_EMU,"math call: $%02x",a);
+  switch(a) {
+    case 0x02:
+      switch(Get(0x41)) {
+        case 0: map.opSize=0x04; break;
+        case 1: map.opSize=0x20; break;
+        case 2: map.opSize=0x24; break;
+        case 3: map.opSize=0x30; break;
+        case 4: map.opSize=0x34; break;
+        case 5:
+        default: map.opSize=0x40; break;
+        }
+      return true;
+
+    case 0x0e:
+    case 0x0F:
+    case 0x10:
+      {
+      const unsigned short addr=(Get(0x4d)<<8)|Get(0x4e);
+      unsigned char tmp[64];
+      GetMem(addr,tmp,map.opSize);
+      unsigned char *reg;
+      switch(a) {
+        case 0x0e: reg=map.A; break;
+        case 0x0f: reg=map.B; break;
+        case 0x10: reg=map.C; break;
+        default: return false;
+        }
+      SetMem(addr,reg,map.opSize);
+      memcpy(reg,tmp,map.opSize);
+      Set(0x41,map.opSize);
+      return true;
+      }
+
+    case 0x29:
+      Set(0x120,1);
+      return true;
+    }
+  return cEmu::MathMapHandler();
+}
+
+// -- cEmuRom11 ----------------------------------------------------------------
+
+class cEmuRom11 : public cEmuRom10Core {
+protected:
+  virtual bool InitSetup(void);
+  virtual bool UpdateSetup(const unsigned char *emm);
+public:
+  cEmuRom11(void);
+  };
+
+cEmuRom11::cEmuRom11(void)
+{
+  InitStart=0x4000;
+  InitEnd  =0x405b;
+  EmmStart =0x5865;
+  EmmEnd   =0x9990;
+  EmmKey0  =0x5f66;
+  EmmKey1  =0x5f6e;
+  FindKeyStart=0x5f20;
+  FindKeyEnd  =0x5f31;
+  MapAddr  =0x2020;
+  Rc1H     =0x004a;
+  Rc1L     =0x004b;
+  EnsIrdChk=0x621b;
+  Cmd83Chk =0x6215;
+  SoftInt  =0x4004;
+  StackHigh=0x3ff;
+}
+
+bool cEmuRom11::InitSetup(void)
+{
+  return DoMaps(false,0x8000) && CoreInitSetup();
+}
+
+bool cEmuRom11::UpdateSetup(const unsigned char *emm)
+{
+  Set(0x0068,0x4B);
+  return CoreUpdateSetup(emm);
+}
+
+// -- cNagraDES ----------------------------------------------------------------
+
+class cNagraDES {
+private:
+  cDes des;
+protected:
+  void Decrypt(const unsigned char *data, const unsigned char *key, unsigned char *out, bool desmod=false);
+  void Crypt(const unsigned char *data, const unsigned char *key, unsigned char *out);
+  bool SigCheck(const unsigned char *block, const unsigned char *sig, const unsigned char *vkey, const int rounds);
+};
+
+void cNagraDES::Decrypt(const unsigned char *data, const unsigned char *key, unsigned char *out, bool desmod)
+{
+  unsigned char cardkey[8];
+  memcpy(cardkey,key,8);
+  RotateBytes(cardkey,8);
+  memcpy(out,data,8);
+  if(!desmod) RotateBytes(out,8);
+  des.Des(out,cardkey,NAGRA_DES_DECR);
+  if(!desmod) RotateBytes(out,8);
+}
+
+void cNagraDES::Crypt(const unsigned char *data, const unsigned char *key, unsigned char *out)
+{
+  unsigned char cardkey[8];
+  memcpy(cardkey,key,8);
+  RotateBytes(cardkey,8);
+  memcpy(out,data,8);
+  RotateBytes(out,8);
+  des.Des(out,cardkey,NAGRA_DES_ENCR);
+  RotateBytes(out,8);
+}
+
+bool cNagraDES::SigCheck(const unsigned char *block, const unsigned char *sig, const unsigned char *vkey, const int rounds)
+{
+  unsigned char hash[8];
+  memcpy(hash,vkey,8);
+  for(int j=0; j<rounds; j++) {
+    unsigned char cr[8];
+    Crypt(block+j*8,hash,cr);
+    xxor(hash,8,cr,&block[j*8]);
+    }
+  return (0==memcmp(hash,sig,8));
+}
+
+// -- cNagra1 ------------------------------------------------------------------
+
+class cNagra1 : public cNagra, public cNagraDES {
+protected:
+  virtual void CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q);
+  bool DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2);
+  bool DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2);
+  };
+
+bool cNagra1::DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2)
+{
+  cBN result;
+  if(rsa.RSA(result,&in[2],len,pubExp,n2)<=0) {
+    PRINTF(L_SYS_CRYPTO,"error decrypting ECM (round 1)");
+    return false;;
+    }
+  cBN mod;
+  BN_set_word(mod,in[0]>>4);
+  BN_lshift(mod,mod,508);
+  BN_add(result,result,mod);
+  if(rsa.RSA(out,64,result,e1,n1,false)!=64) {
+    PRINTF(L_SYS_CRYPTO,"error: result of ECM decryption is not 64 bytes");
+    return false;
+    }
+  if(vkey && !SigCheck(out,&out[56],vkey,7)) {
+    PRINTF(L_SYS_CRYPTO,"ECM decryption failed");
+    return false;
+    }
+  return true;
+}
+
+bool cNagra1::DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2)
+{
+  cBN result;
+  if(rsa.RSA(result,&in[9],len,pubExp,n2)<=0) {
+    PRINTF(L_SYS_CRYPTO,"error decrypting EMM (round 1)");
+    return false;;
+    }
+  cBN mod;
+  BN_set_word(mod,in[0]>>6);
+  BN_lshift(mod,mod,510);
+  BN_add(result,result,mod);
+  if(rsa.RSA(out,64,result,e1,n1,false)!=64) {
+    PRINTF(L_SYS_CRYPTO,"error: result of EMM decryption is not 64 bytes");
+    return false;
+    }
+  if(!vkey || SigCheck(out,&in[1],vkey,8))
+    return true;
+    
+  // We need to use signature exchange method (7blocks emm,1 block signature)
+  PRINTF(L_SYS_CRYPTO,"warning: signature failed,trying signature exchange method");
+  unsigned char buf[8];
+  RotateBytes(buf,&in[1],sizeof(buf));
+  cBN newdata, newsig;
+  BN_bin2bn(buf,sizeof(buf),newdata);
+  BN_copy(newsig,result);
+  BN_mask_bits(newsig,64);
+  BN_bn2bin(newsig,buf);
+  RotateBytes(buf,sizeof(buf));
+
+  BN_rshift(result,result,64); // delete the lower 64 bits,we saved it
+  BN_lshift(result,result,64); // before as the new 64 bit signature
+  BN_add(result,result,newdata);
+  
+  if(rsa.RSA(out,64,result,e1,n1)!=64) {
+    PRINTF(L_SYS_CRYPTO,"error: result of EMM decryption is not 64 bytes");
+    return false;
+    }
+  if(vkey && !SigCheck(out,buf,vkey,8)) {
+    PRINTF(L_SYS_CRYPTO,"fatal: signature failed in signature exchange method");
+    return false;
+    }
+  return true;
+}
+
+void cNagra1::CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q)
+{
+  // Make des_key
+  unsigned char des_data[32];
+  unsigned char des_key[8], des_tmp[8];
+  memcpy(des_data,key,8);
+  RotateBytes(des_tmp,key,8);
+  des_tmp[7]=0x00;
+  Decrypt(des_data,des_tmp,des_key,true);
+  RotateBytes(des_key,8);
+  des_key[7]=0x00;
+
+  // Calculate P
+  for(int i=0; i<4; i++) {
+    const int off=i*8;
+    memcpy(&des_data[off],&key[4],8);
+    des_data[off]^=i;
+    Decrypt(&des_data[off],des_key,des_tmp,true);
+    memcpy(&des_data[off],des_tmp,8);
+    }
+  BN_bin2bn(des_data,32,p);
+  BN_add_word(p,(key[12]<<4)|((key[13]>>4)&0x0f));
+  BN_set_bit(p,(BN_num_bytes(p)*8)-1);
+
+  // Calculate Q
+  for(int i=0; i<4; i++) {
+    const int off=i*8;
+    memcpy(&des_data[off],&key[4],8);
+    des_data[off]^=(i+4);
+    Decrypt(&des_data[off],des_key,des_tmp,true);
+    memcpy(&des_data[off],des_tmp,8);
+    }
+  BN_bin2bn(des_data,32,q);
+  BN_add_word(q,((key[13]&0x0f)<<8)|key[14]);
+  BN_set_bit(q,(BN_num_bytes(q)*8)-1);
+}
+
+#ifndef TESTER
+
+// -- cSystemNagra -------------------------------------------------------------
+
+class cSystemNagra : public cSystem, public cNagra1 {
+private:
+  unsigned char mecmTable[256];
+  cEmu *emu;
+  //
+  void WriteTable(unsigned char *from, int off);
+public:
+  cSystemNagra(void);
+  virtual ~cSystemNagra();
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemNagra::cSystemNagra(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  emu=0;
+  memset(mecmTable,0,sizeof(mecmTable));
+  hasLogger=true;
+}
+
+cSystemNagra::~cSystemNagra()
+{
+  delete emu;
+}
+
+void cSystemNagra::WriteTable(unsigned char *from, int off)
+{
+  off&=0xFF;
+  if(off+16<256) memcpy(mecmTable+off,from,16);
+  else {
+    int l=256-off;
+    memcpy(mecmTable+off,from,l);
+    memcpy(mecmTable,from+l,16-l);
+    }
+}
+
+bool cSystemNagra::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+  int cmdLen=data[4];
+  int id=(data[5]*256)+data[6];
+  cTimeMs minTime;
+  if(data[3]!=0x03) {
+    PRINTF(L_SYS_ECM,"invalid ECM");
+    return false;
+    }
+  data+=7;
+  if(data[0]!=0x10) {
+    PRINTF(L_SYS_ECM,"no ECM data available");
+    return false;
+    }
+  const int ecmLen=data[1];
+  const int verType=(data[2]>>3)&1;
+  const int keynr=(data[2]>>4)&1;
+  const int ecmParm=data[2]&7;
+  bool decrypt;
+  if(ecmParm==7) decrypt=false;
+  else if(ecmParm==5) decrypt=true;
+  else {
+    PRINTF(L_SYS_ECM,"unknown ecmParm, ignoring ECM");
+    return false;
+    }
+
+  cKeySnoop ks(this,'N',id,keynr);
+  unsigned char sessionKey[8], verifyKey[8];
+  bool hasVerifyKey=false;
+  cPlainKey *pk;
+  if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(0,0,verType)),sizeof(verifyKey)))) {
+    pk->Get(verifyKey);
+    hasVerifyKey=true;
+    }
+  else if(doLog) PRINTF(L_SYS_KEY,"missing %04X V TYP%d key (non-fatal)",id,verType);
+  if(!(pk=keys.FindKey('N',id,keynr,sizeof(sessionKey)))) return false;
+  pk->Get(sessionKey);
+
+  const int desLen=(ecmLen-9) & ~7; // datalen - signature - ks byte
+  unsigned char *decrypted=AUTOMEM(desLen);
+  for(int i=(desLen/8)-1; decrypt && i>=0; i--) {
+    const int off=i*8;
+    Decrypt(data+11+off,sessionKey,decrypted+off);
+    }
+  if(decrypt && hasVerifyKey && !SigCheck(decrypted,data+3,verifyKey,desLen/8)) {
+    PRINTF(L_SYS_ECM,"signature check failed in DES decrypt");
+    return false;
+    }
+  int cwEvenMecmIndex=-1, cwOddMecmIndex=-1;
+  switch(decrypted[0]) {
+    case 0x10:  // Whole CW
+      cwOddMecmIndex=decrypted[1];
+      memcpy(cw+8,&decrypted[2],8);
+      cwEvenMecmIndex=decrypted[10];
+      memcpy(cw,&decrypted[11],8);
+      break;
+    case 0x11: // Odd CW
+      cwOddMecmIndex=decrypted[1];
+      memcpy(cw+8, &decrypted[2],8);
+      break;
+    case 0x12: // Even CW
+      cwEvenMecmIndex=decrypted[1];
+      memcpy(cw,&decrypted[2],8);
+      break;
+    default: PRINTF(L_SYS_ECM,"failed to get CW"); return false;
+    }
+
+  const unsigned char * const mecmData=data+(ecmLen+2);
+  const int mecmRSALen=mecmData[1]-4;
+  if((cmdLen-(ecmLen+2))>64 && (*mecmData==0x20 || *mecmData==0x39)) {
+    if(mecmRSALen!=64) {
+      if(mecmRSALen>64 && doLog)
+        PRINTF(L_SYS_ECM,"ECM too big (len: %d)",mecmRSALen);
+      return false;
+      }
+
+    const int mecmProvId=mecmData[2]*256+mecmData[3];
+    const int keyType=(mecmData[4]>>3)&1;
+    const int pkey=mecmData[4]&3;
+
+    cBN e1, n1, n2;
+    cPlainKey *pk;
+    if(!(pk=keys.FindKey('N',mecmProvId,MBC3('E','1',KEYSET(0,pkey,keyType)),-1))) {
+      if(doLog) PRINTF(L_SYS_KEY,"missing %04x E1 PK%d TYP%d key",mecmProvId,pkey,keyType);
+      return false;
+      }
+    pk->Get(e1);
+    if(!(pk=keys.FindKey('N',mecmProvId,MBC3('N','1',KEYSET(0,pkey,keyType)),-1)))  {
+      if(doLog) PRINTF(L_SYS_KEY,"missing %04x N1 PK%d TYP%d key",mecmProvId,pkey,keyType);
+      return false;
+      }
+    pk->Get(n1);
+    if(!(pk=keys.FindKey('N',mecmProvId,MBC3('N','2',KEYSET(0,0,keyType)),-1)))  {
+      if(doLog) PRINTF(L_SYS_KEY,"missing %04x N2 TYP%d key",mecmProvId,keyType);
+      return false;
+      }
+    pk->Get(n2);
+    hasVerifyKey=false;
+    if((pk=keys.FindKey('N',mecmProvId,ADDC3('V',KEYSET(0,0,keyType)),sizeof(verifyKey))))  {
+      pk->Get(verifyKey);
+      hasVerifyKey=true;
+      }
+    else if(doLog) PRINTF(L_SYS_KEY,"missing %04x V TYP%d key (non-fatal)",mecmProvId,keyType);
+    unsigned char *decrMecmData=AUTOMEM(mecmRSALen);
+    if(!DecryptECM(&mecmData[4],decrMecmData,hasVerifyKey?verifyKey:0,mecmRSALen,e1,n1,n2))
+      return false;
+
+    if(*mecmData==0x39 || (*mecmData==0x20 && (mecmProvId&0xFE00)==0x4800)) {
+      unsigned char xor_table[64];
+      for(int i=sizeof(xor_table)-1; i>=0; i--) xor_table[i]=63-i;
+      CreateRSAPair(&decrMecmData[24],xor_table,e1,n1);
+
+      // new XOR table for MECM data
+      cBNctx ctx;
+      cBN x;
+      BN_mod_exp(x,e1,pubExp,n1,ctx);
+      int l=sizeof(xor_table)-BN_num_bytes(x);
+      memset(xor_table,0,l);
+      BN_bn2bin(x,xor_table+l);
+      RotateBytes(xor_table,sizeof(xor_table));
+
+      // And finally, new MECM table
+      for(int i=39; i<mecmRSALen; i++) decrMecmData[i]^=xor_table[i-39];
+      memcpy(&decrMecmData[3],&decrMecmData[39],mecmRSALen-39);
+      }
+
+    if(decrMecmData[0]==0x2F && decrMecmData[1]==mecmData[2] && decrMecmData[2]==mecmData[3])
+      WriteTable(decrMecmData+4,decrMecmData[3]*2);
+    }
+
+  if(cwOddMecmIndex>=0 && cwOddMecmIndex<0x80) {
+    const int d=cwOddMecmIndex*2;
+    for(int i=0 ; i<8 ; i++) cw[i+8]^=mecmTable[(d+i)&0xFF]; // odd
+    }
+  if(cwEvenMecmIndex>=0 && cwEvenMecmIndex<0x80) {
+    const int d=cwEvenMecmIndex*2;
+    for(int i=0 ; i<8 ; i++) cw[i]^=mecmTable[(d+i)&0xFF]; // even
+    }
+  ks.OK(pk);
+  int i=minEcmTime-minTime.Elapsed();
+  if(i>0) cCondWait::SleepMs(i);
+
+  return true;
+}
+  
+void cSystemNagra::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  const int id=buffer[10]*256+buffer[11];
+  static const unsigned char tester[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x4B };
+  if(memcmp(&buffer[3],tester,sizeof(tester)) || SCT_LEN(buffer)<(12+9+64)) {
+    PRINTF(L_SYS_EMM,"%d: not a valid EMM structure",CardNum());
+    return;
+    }
+  const int pkey=(buffer[12]&3);
+  cPlainKey *pk;
+  cBN e1, n1, n2;
+  if(!(pk=keys.FindKey('N',id,MBC3('E','1',KEYSET(0,pkey,0)),-1))) return;
+  pk->Get(e1);
+  if(!(pk=keys.FindKey('N',id,MBC3('N','1',KEYSET(0,pkey,0)),-1))) return;
+  pk->Get(n1);
+  unsigned char v[8];
+  bool hasVerifyKey=false;
+  if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(0,0,0)),sizeof(v)))) {
+    pk->Get(v);
+    hasVerifyKey=true;
+    }
+  int mode=(buffer[12]&8) ? 0:2;
+  unsigned char emmdata[64];
+  bool r;
+  do {
+    switch(mode) {
+      case 0: // get the ROM10 specific management key
+        if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(10,0,1)),-1))) return;
+        pk->Get(n2);
+        mode=1; break;
+      case 1: // get the ROM11 specific management keys
+        if(!(pk=keys.FindKey('N',id,MBC3('E','1',KEYSET(11,pkey,1)),-1))) return;
+        pk->Get(e1);
+        if(!(pk=keys.FindKey('N',id,MBC3('N','1',KEYSET(11,pkey,1)),-1))) return;
+        pk->Get(n1);
+        if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(11,0,1)),-1))) return;
+        pk->Get(n2);
+        hasVerifyKey=false;
+        if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(11,0,1)),sizeof(v)))) {
+          pk->Get(v);
+          hasVerifyKey=true;
+          }
+        mode=-1; break;
+      case 2: // get the universal management key
+        if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(0,0,0)),-1))) return;
+        pk->Get(n2);
+        mode=-1; break;
+      }
+    r=DecryptEMM(&buffer[12],emmdata,hasVerifyKey?v:0,64,e1,n1,n2);
+    } while(!r && mode>=0);
+  if(r) {
+    HEXDUMP(L_SYS_RAWEMM,emmdata,sizeof(emmdata),"Nagra1 RAWEMM");
+    unsigned int i=0;
+    bool gotKeys=false;
+    unsigned char key0[8], key1[8];
+    int keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+    switch(emmdata[i]) { // Check filter type
+      case 0x00: // One card
+        i+=7; break;
+      case 0x01: case 0x02: case 0x03: case 0x04: // Group of cards
+      case 0x05: case 0x06: case 0x07: case 0x08:
+      case 0x09: case 0x0A: case 0x0B: case 0x0C:
+      case 0x0D: case 0x0E: case 0x0F: case 0x10:
+      case 0x11: case 0x12: case 0x13: case 0x14:
+      case 0x15: case 0x16: case 0x17: case 0x18:
+      case 0x19: case 0x1A: case 0x1B: case 0x1C:
+      case 0x1D: case 0x1E: case 0x1F: case 0x20:
+        i+=emmdata[i]+0x7; break;
+      case 0x3e:
+        i+=6; break;
+      case 0x3d: // All cards with same system ID
+      case 0x3f:
+        i+=3; break;
+      }
+    int nrKeys=0;
+    while(i<sizeof(emmdata)) {
+      if((emmdata[i]&0xF0)==0xF0) { // Update with CPU code
+        const int romNr=emmdata[i]&0x0F;
+        if(!emu || !emu->Matches(romNr,id)) {
+          delete emu; emu=0;
+          PRINTF(L_SYS_EMM,"%d: setting defaults for ROM %d",CardNum(),romNr);
+          switch(romNr) {
+            case 3:  emu=new cEmuRom3;  break;
+            case 7:  emu=new cEmuRom7;  break;
+            case 10: emu=new cEmuRom10; break;
+            case 11: emu=new cEmuRom11; break;
+            default: PRINTF(L_SYS_EMM,"%d: unsupported ROM",CardNum()); return;
+            }
+          if(!emu || !emu->Init(romNr,id)) {
+            delete emu; emu=0;
+            PRINTF(L_SYS_EMM,"%d: initialization failed for ROM %d",CardNum(),romNr);
+            return;
+            }
+          }
+        unsigned char ki[2];
+        if((gotKeys=emu->GetOpKeys(emmdata,ki,key0,key1))) {
+          keyId=(ki[0]<<8)+ki[1];
+          PRINTF(L_SYS_EMM,"%d: got keys for %04X (ROM %d)",CardNum(),keyId,romNr);
+          }
+        unsigned char select[3], pkset[3][15];
+        select[0]=(keyId>>8)|0x01; // always high id for ECM RSA keys
+        select[1]=keyId&0xFF;
+        select[2]=0; // type 0
+        if(emu->GetPkKeys(&select[0],&pkset[0][0])) {
+          int pkKeyId=((select[0]<<8)+select[1]);
+          PRINTF(L_SYS_EMM,"%d: got PK keys for %04X (ROM %d)",CardNum(),pkKeyId,romNr);
+          for(int i=0; i<3; i++) {
+            CreateRSAPair(pkset[i],0,e1,n1);
+            FoundKey();
+            if(keys.NewKey('N',pkKeyId,ADDC3(MBC('N','1'),KEYSET(0,i,0)),n1,64)) NewKey();
+            FoundKey();
+            if(keys.NewKey('N',pkKeyId,ADDC3(MBC('E','1'),KEYSET(0,i,0)),e1,64)) NewKey();
+            }
+          }
+        break; // don't process other nanos
+        }
+      else if(emmdata[i]==0x60) { // NULL nano
+        i+=2;
+        }
+      else if(emmdata[i]==0x00) {
+        i++;
+        }
+      else if(emmdata[i]==0x81) {
+        i++;
+        }
+      else if(emmdata[i]==0x83) {
+        keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+        i+=3;
+        }
+      else if(emmdata[i]==0x42) { // plain Key
+        if(emmdata[i+1]==0x05) memcpy(key0,&emmdata[i+2],sizeof(key0));
+        else                   memcpy(key1,&emmdata[i+2],sizeof(key1));
+        i+=10;
+        if(++nrKeys==2) {
+          gotKeys=true;
+          PRINTF(L_SYS_EMM,"%d: got keys for %04X (plain)",CardNum(),keyId);
+          break;
+          }
+        }
+      else {
+        PRINTF(L_SYS_EMM,"%d: ignored nano %02x",CardNum(),emmdata[i]);
+        break;
+        }
+      }
+    if(gotKeys) {
+      FoundKey();
+      if(keys.NewKey('N',keyId,00,key0,8)) NewKey();
+      FoundKey();
+      if(keys.NewKey('N',keyId,01,key1,8)) NewKey();
+      }
+    }
+}
+
+// -- cSystemLinkNagra ---------------------------------------------------------
+
+class cSystemLinkNagra : public cSystemLink {
+public:
+  cSystemLinkNagra(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemNagra; }
+  };
+
+static cSystemLinkNagra staticInitN1;
+
+cSystemLinkNagra::cSystemLinkNagra(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptInt("MinEcmTime",trNOOP("Nagra: min. ECM processing time"),&minEcmTime,0,5000));
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkNagra::CanHandle(unsigned short SysId)
+{
+  return SysId==SYSTEM_NAGRA; // || SysId==SYSTEM_NAGRA_BEV;
+}
+
+#endif //TESTER
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0101.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0101.c
new file mode 100644 (file)
index 0000000..5a4a731
--- /dev/null
@@ -0,0 +1,1469 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nagra2.h"
+
+// -- cAuxSrv ------------------------------------------------------------------
+
+#ifdef HAS_AUXSRV
+#include "network.h"
+#define AUX_PROTOCOL_VERSION 2
+int auxEnabled=0;
+int auxPort=7777;
+char auxAddr[80]="localhost";
+char auxPassword[250]="auxserver";
+
+class cAuxSrv : public cMutex {
+private:
+  cNetSocket so;
+  //
+  bool Login(void);
+public:
+  cAuxSrv(void);
+  int Map(int map, unsigned char *data, int len, int outlen);
+  };
+
+cAuxSrv::cAuxSrv(void)
+:so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
+{}
+
+bool cAuxSrv::Login()
+{
+  unsigned char buff[256];
+  PRINTF(L_SYS_MAP,"auxsrv: connecting to %s:%d",auxAddr,auxPort);
+  if(so.Connect(auxAddr,auxPort)) {
+    buff[0]=0xA7;
+    buff[1]=0x7A;
+    buff[2]=0;
+    int l=strlen(auxPassword);
+    buff[3]=l;
+    memcpy(&buff[4],auxPassword,l);
+    buff[4+l]=0xFF;
+    if(so.Write(buff,l+5)==l+5 &&
+       so.Read(buff,sizeof(buff))>=9 &&
+       buff[0]==0x7A && buff[1]==0xA7 && buff[2]==0x00 && buff[3]==0x04 && buff[8]==0xff &&
+       ((buff[4]<<8)|buff[5])==AUX_PROTOCOL_VERSION) return true;
+    PRINTF(L_SYS_MAP,"auxsrv: login write failed");
+    }
+  so.Disconnect();
+  return false;
+}
+
+int cAuxSrv::Map(int map, unsigned char *data, int len, int outlen)
+{
+  if(!auxEnabled) {
+    PRINTF(L_SYS_MAP,"auxsrv: AUXserver is disabled!");
+    return -1;
+    }
+  if(len>500 || outlen>500) return -1;
+  cMutexLock lock(this);
+  if(!so.Connected() && !Login()) return false;
+  PRINTF(L_SYS_MAP,"auxsrv: calling map%02x",map);
+  unsigned char buff[512];
+  buff[0]=0xA7;
+  buff[1]=0x7A;
+  buff[2]=((len+1)>>8) & 0xff;
+  buff[3]=(len+1)&0xff;
+  buff[4]=map;
+  memcpy(&buff[5],data,len);
+  buff[len+5]=0xFF;
+  if(so.Write(buff,len+6)==len+6) {
+    if((len=so.Read(buff,sizeof(buff)))>0) {
+      if(buff[0]==0x7A && buff[1]==0xA7) {
+        if(buff[4]==0x00) {
+          int cycles=(buff[5]<<16)|(buff[6]<<8)|buff[7];
+          int l=buff[2]*256+buff[3];
+          if(len>=l+5 && l==outlen+4) {
+            if(buff[l+4]==0xFF) {
+              memcpy(data,buff+8,outlen);
+              return cycles;
+              }
+            else PRINTF(L_SYS_MAP,"auxsrv: bad footer in map%02x response",map);
+            }
+          else PRINTF(L_SYS_MAP,"auxsrv: bad length in map%02x response (got=%d,%d want=%d,%d)",map,l-4,len,outlen,l+8);
+          }
+        else PRINTF(L_SYS_MAP,"auxsrv: map%02x not successfull (unsupported?)",map);
+        }
+      else PRINTF(L_SYS_MAP,"auxsrv: bad response to map%02x",map);
+      }
+    else PRINTF(L_SYS_MAP,"auxsrv: map%02x read failed",map);
+    }
+  else  PRINTF(L_SYS_MAP,"auxsrv: map%02x write failed",map);
+  so.Disconnect();
+  return -1;
+}
+#endif //HAS_AUXSRV
+
+// -- cMap0101 ----------------------------------------------------------------
+
+class cMap0101 : public cMapCore {
+private:
+  static const unsigned char primes[];
+  static const unsigned short coef22[][32];
+#ifdef HAS_AUXSRV
+  cAuxSrv aux;
+#endif
+  //
+  void MakePrime(BIGNUM *n, unsigned char *residues);
+protected:
+  unsigned char residues[53];
+
+  void Map22(int pow);
+  void Map3b(unsigned char *data, unsigned char size);
+  void Map3c(unsigned char *data, unsigned char size);
+  void Map3e(unsigned char *data, unsigned char size);
+  void Map57(unsigned char *data, unsigned char size);
+
+  void IMakeJ();
+  void IMonInit0(int bits=0);
+  void IMonInit(int bits=0);
+public:
+  cMap0101(void);
+  virtual bool Map(int f, unsigned char *data, int l);
+  };
+
+cMap0101::cMap0101(void)
+{
+}
+const unsigned char cMap0101::primes[] = {
+  0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,
+  0x3D,0x43,0x47,0x49,0x4F,0x53,0x59,0x61,0x65,0x67,0x6B,0x6D,0x71,0x7F,0x83,0x89,
+  0x8B,0x95,0x97,0x9D,0xA3,0xA7,0xAD,0xB3,0xB5,0xBF,0xC1,0xC5,0xC7,0xD3,0xDF,0xE3,
+  0xE5,0xE9,0xEF,0xF1,0xFB
+  };
+
+void cMap0101::MakePrime(BIGNUM *n, unsigned char *residues)
+{
+  bool isPrime;
+  unsigned int counts[3] = { 0 };
+  do {
+    ++counts[0];
+    BN_add_word(n,2);
+    isPrime=true;
+    for(int i=0; i<sizeof(primes); i++) {
+      residues[i]+=2;
+
+         unsigned char num, denom, r;
+         num = residues[i];
+         denom = primes[i];
+         r = 0;
+
+         if(num>denom) {
+                 while(denom>=r) {
+                         ++counts[1];
+                         r = (r<<1)|((num&0x80)>>7);
+                         num <<= 1;
+                 }
+         }
+
+         residues[i]%=primes[i];
+         if(residues[i]==0) {
+                 ++counts[2];
+                 isPrime=false;
+         }
+    }
+  } while(!isPrime);
+
+  cycles = 1290 + 1465 * counts[0] + counts[1] + 13 * counts[2];
+}
+
+void cMap0101::Map57(unsigned char *data, unsigned char size) {
+   cBN a, b, x, y, scalar;
+   int owordsize = wordsize;
+   if(size<=0) size = wordsize;
+
+   if(size<2 || size>4) {
+      size = 4;
+   }
+   wordsize = size;
+   size *= 8;
+
+   D.GetLE(data+0*size,size);
+   x.GetLE(data+1*size,size);
+   y.GetLE(data+2*size,size);
+   b.GetLE(data+3*size,size);
+   a.GetLE(data+4*size,size);
+   scalar.GetLE(data+5*size,size);
+
+   bool doz = false;
+   int scalarbits = BN_num_bits(scalar);
+   if(scalarbits>=2) {
+      if(BN_is_zero(x) && (BN_is_zero(y) || (BN_is_zero(b) && BN_num_bits(y)==1))) {
+         BN_zero(Px);
+         BN_copy(Py,y);
+         BN_zero(Qz);
+         MakeJ0(J, D);
+      } else {
+         CurveInit(a);
+         ToProjective(0,x,y);
+         BN_copy(Qx,Px);
+         BN_copy(Qy,Py);
+           for(int i=scalarbits-2; i>=0; i--) {
+              DoubleP(0);
+             if(BN_is_bit_set(scalar,i)) {
+                BN_copy(A,Pz);
+               if(BN_is_zero(Pz) || BN_is_zero(D)) {
+                  BN_copy(Px,Qx);
+                  BN_copy(Py,Qy);
+                  BN_copy(Pz,Qz);
+                  AddP(1);
+               }
+               else {
+                  doz = true;
+                  if(wordsize==4) {
+                     BN_rshift(Py,Py,32);
+                     BN_lshift(Py,Py,32);
+                     BN_rshift(b,Qz,224);
+                     BN_add(Py,Py,b);
+                  }
+                  BN_mask_bits(Px,32);
+                  BN_lshift(b,Qz,32);
+                  BN_add(Px,Px,b);
+                  BN_mask_bits(Px,8*size);
+                  AddP(0);
+               }
+            }
+         }
+         ToAffine();
+        }
+   } else {
+      BN_copy(Px,x);
+      BN_copy(Py,y);
+      BN_zero(Qz);
+      MakeJ0(J, D);
+   }
+
+   memset(data,0,0x40);
+   Px.PutLE(data+0x00,size);
+   if(size<0x20 && doz) {
+      unsigned char tmp[0x20];
+      Qz.PutLE(tmp,size);
+      memcpy(data+0x00+size,tmp+(size-4),4);
+   }
+   Py.PutLE(data+0x20,size);
+   BN_zero(A);
+   BN_zero(B);
+   BN_zero(C);
+   wordsize = owordsize;
+} 
+
+void cMap0101::Map22(int pow)
+{
+   BN_zero(B);
+   BN_set_bit(B,80);
+   AddMapCycles(644);
+   BN_zero(B);
+   BN_set_bit(B,96);
+   AddMapCycles(76);
+
+   cBN v;
+
+   if(BN_is_zero(D)) {
+      cycles = 639 - 6;
+      return;
+   }
+
+   pow &= 0x1fff;
+   BN_one(B);
+   BN_mod_lshift(B, B, pow, D, ctx);
+
+   if(pow<64)
+      cycles = 927 + (pow&7)*9;
+   else {
+      div_t val = div(pow-64, 2046);
+      int j = 16*((val.rem+1)/2) + (val.rem>4?val.rem-7:val.rem-12)/4;
+      cycles = 1086 + j - ((j-2)%5) + 16923*val.quot - ((4*val.quot-2)%5);
+   }
+} 
+
+void cMap0101::Map3b(unsigned char *data, unsigned char size)
+{
+static int tbl[][17] = {
+          { 2666, 2804, 2957, 3105, 3258, 3406, 3554, 3707, 3855, 4008, 4156, 4304, 4457, 4605, 4758, 4906, 5054 },
+          { 3163, 3316, 3484, 3652, 3815, 3983, 4146, 4314, 4482, 4645, 4813, 4976, 5144, 5312, 5475, 5643, 5806 },
+          { 3715, 3888, 4071, 4254, 4432, 4615, 4798, 4981, 5164, 5342, 5525, 5708, 5891, 6074, 6252, 6435, 6618 },
+          { 4302, 4490, 4688, 4886, 5084, 5282, 5480, 5678, 5876, 6074, 6272, 6470, 6668, 6866, 7064, 7262, 7460 },
+          { 4899, 5107, 5320, 5533, 5751, 5964, 6177, 6390, 6603, 6821, 7034, 7247, 7460, 7673, 7891, 8104, 8317 },
+          { 5541, 5759, 5992, 6220, 6453, 6681, 6909, 7142, 7370, 7603, 7831, 8059, 8292, 8520, 8753, 8981, 9209 },
+          { 6198, 6431, 6679, 6927, 7170, 7418, 7661, 7909, 8157, 8400, 8648, 8891, 9139, 9387, 9630, 9878, 10121 },
+          { 6910, 7163, 7426, 7689, 7947, 8210, 8473, 8736, 8999, 9257, 9520, 9783, 10046, 10309, 10567, 10830, 11093 },
+          { 7637, 7905, 8183, 8461, 8739, 9017, 9295, 9573, 9851, 10129, 10407, 10685, 10963, 11241, 11519, 11797, 12075 },
+          { 8394, 8682, 8975, 9268, 9566, 9859, 10152, 10445, 10738, 11036, 11329, 11622, 11915, 12208, 12506, 12799, 13092 },
+          { 9196, 9494, 9807, 10115, 10428, 10736, 11044, 11357, 11665, 11978, 12286, 12594, 12907, 13215, 13528, 13836, 14144 },
+          { 10003, 10346, 10674, 11002, 11325, 11653, 11976, 12304, 12632, 12955, 13283, 13606, 13934, 14262, 14585, 14913, 15236 },
+          { 10885, 11218, 11561, 11904, 12242, 12585, 12928, 13271, 13614, 13952, 14295, 14638, 14981, 15324, 15662, 16005, 16348 },
+          { 11792, 12145, 12498, 12856, 13214, 13572, 13930, 14288, 14646, 15004, 15362, 15720, 16078, 16436, 16794, 17152, 17510 },
+          { 12709, 13087, 13465, 13843, 14226, 14604, 14982, 15360, 15738, 16121, 16499, 16877, 17255, 17633, 18016, 18394, 18772 },
+          { 13671, 14069, 14477, 14880, 15283, 15686, 16084, 16492, 16890, 17298, 17696, 18099, 18507, 18905, 19313, 19711, 20114 },
+          { 14668, 15091, 15519, 15947, 16370, 16798, 17221, 17654, 18082, 18505, 18933, 19356, 19784, 20212, 20640, 21068, 21491 },
+          };
+   cBN scalar;
+   AddMapCycles(441);
+   IMakeJ();
+   AddMapCycles(327);
+   BN_zero(B);
+   BN_set_bit(B,104);
+   AddMapCycles(46-373); 
+   IMonInit0(wordsize*0x3c + 4*size);
+   scalar.GetLE(data, size*8);
+   MonMul(B, scalar, B, size);
+   cycles = tbl[wordsize-1][size-1] - 6;
+} 
+
+void cMap0101::Map3c(unsigned char *data, unsigned char size)
+{
+   
+   cBN scalar;
+
+   cycles = 0;
+
+   if(size<=0) {
+      cycles += 4;
+      size = wordsize;
+   } else if(size>wordsize) {
+      cycles += size>17?9:4;
+      size = wordsize;
+   }
+
+   scalar.GetLE(data, size*8);
+
+   int sbits = BN_num_bits(scalar);
+
+   int i, msb;
+   msb = data[(sbits-1)/8];
+   cycles += 2714 + ((sbits-1)/8)*650 - 11;
+   for(i=7; i>=1; --i) if((msb&(1<<i))) { cycles += i*75 - 15; break; }
+   for(i=0; i<sbits; ++i) if(BN_is_bit_set(scalar, i)) cycles += 88;
+
+   AddMapCycles(441);
+   
+   if(BN_is_zero(scalar) || BN_num_bits(D)<=1) {
+      IMakeJ();      
+
+      if(BN_num_bits(D)==1 || !BN_is_zero(scalar))
+         BN_zero(B);
+      else
+         BN_one(B);
+      BN_one(A);
+   } else {
+      IMonInit();      
+   AddMapCycles(60);
+      MonMul(B,A,B);
+      AddMapCycles(121);
+      MonExp(scalar);
+   }
+   BN_zero(C);
+}
+
+
+void cMap0101::Map3e(unsigned char *data, unsigned char size)
+{
+  cBN scalar;
+
+  cycles = 0;
+
+  if(size<=0) {
+    cycles += 4;
+    size = wordsize;
+  } else if(size>wordsize) {
+    cycles += size>17?9:4;
+    size = wordsize;
+  }
+
+  scalar.GetLE(data, size*8);
+
+  int sbits = BN_num_bits(scalar);
+
+  int i, msb;
+  msb = data[(sbits-1)/8];
+  cycles += 3848 + ((sbits-1)/8)*650 - 11;
+  for(i=7; i>=1; --i) if((msb&(1<<i))) { cycles += i*75 - 15; break; }
+  for(i=0; i<sbits; ++i) if(BN_is_bit_set(scalar, i)) cycles += 88;
+
+  AddMapCycles(441);
+  if(BN_is_zero(scalar) || BN_num_bits(D)<=1) {
+    IMakeJ();
+    if(BN_num_bits(D)==1 || !BN_is_zero(scalar))
+      BN_zero(B);
+    else
+      BN_one(B);
+    BN_one(A);
+  } else {
+    IMonInit();
+    MonMul(B,A,B);
+    MonExp(scalar);
+  }
+  BN_zero(C);
+}
+
+void cMap0101::IMakeJ()
+{
+       int owordsize = wordsize;
+       AddMapCycles(19);
+       wordsize = 1;
+       AddMapCycles(102);
+       MakeJ0(J, D, C);
+       AddMapCycles(303);
+       BN_zero(C);
+       AddMapCycles(34);
+       wordsize = owordsize;
+       AddMapCycles(10);
+}
+
+void cMap0101::IMonInit0(int bits)
+{
+       AddMapCycles(132+(wordsize*8+3)/5*5);
+       if(BN_num_bits(D)>1) AddMapCycles(54);
+       if(!BN_is_zero(D)) {
+               AddMapCycles(54);
+               BN_zero(I);
+               BN_set_bit(I,68*wordsize);
+               BN_zero(B);
+               AddMapCycles(141+(wordsize*8+3)/5*5);
+               BN_set_bit(B,64*(wordsize-1)+bits);
+               AddMapCycles(92+72*wordsize);
+               BN_mod(B,I,D,ctx);
+               AddMapCycles(639);
+       }
+
+       AddMapCycles(52);
+       for(int i=0; i<4; i++) {
+               MonMul0(B,B,B,C,D,J);
+               AddMapCycles(96+6*(i>0));
+               MonMulFin(B,D);
+       }
+}
+
+void cMap0101::IMonInit(int bits)
+{
+       IMakeJ();
+       IMonInit0(bits);
+}
+
+bool cMap0101::Map(int f, unsigned char *data, int l)
+{
+  switch(f) {
+       case 0x21:
+         AddMapCycles(169-6);
+         IMakeJ();
+         cycles = 898;
+         break;
+       case 0x22:
+          Map22(l<0?-l:l);
+          break;
+    case 0x23:
+/*      AddMapCycles(169);
+      IMonInit0(24);*/
+      {        
+          cBN s,x,y;
+          BN_copy(s,D);                                
+          BN_rshift(s,s,64+ 1 *8);                     
+          BN_lshift(s,s,64);
+          BN_copy(x,D);
+          BN_mask_bits(x,64);
+          BN_copy(y,D);
+          BN_rshift(y,y,64);                   
+          BN_mask_bits(y,1*8);
+          BN_lshift(y,y,128-1*8);
+          BN_copy(D,s);
+          BN_add(D,D,x);
+          BN_add(D,D,y);
+      }
+       BN_zero(B);
+       BN_set_bit(B,88);
+      break; 
+       case 0x25:
+       {
+          AddMapCycles(254);
+          MakeJ0(B, D, C, wordsize*64);
+          // valid for wordsize 1 and 2
+          AddMapCycles(795*wordsize-492);
+          BN_zero(C);
+          cycles = 49 + 800*wordsize; 
+       }
+       break;
+       case 0x29:
+      {
+               BN_add(B, B, C);
+               bool b = BN_is_bit_set(B, wordsize<<6);
+               if(b) BN_mask_bits(B, wordsize<<6);
+               if(data) data[0] = b;
+               cycles = 501+(8*wordsize+3)/5*5-6;
+      }
+      break; 
+   case 0x2a:
+     {
+      bool b = ModSub(B, B, D);
+      if(data) data[0] = b;
+         BN_zero(C);
+     }
+     break;
+    case 0x2e:
+     {
+         int ti=1;
+         H.GetLE(data,16);
+         BN_rshift(H,H,64);
+         BN_lshift(H,H,64);
+         BN_add(H,J,H);
+         BN_rshift(H,H,ti<<3);
+         BN_copy(J,H);
+         BN_mask_bits(J,64);
+      cycles = 864;
+     }
+     break;
+   case 0x2F:
+     H.GetLE(data,16);
+     BN_rshift(H,H,64);
+     BN_lshift(H,H,64);
+     BN_add(H,H,J);
+     BN_rshift(J,H,8);
+     BN_mask_bits(J,64);
+     cycles = 808;
+        break;
+   case 0x30:
+   case 0x31:
+     {
+      BN_mul(D, B, B, ctx);
+      if((f&1)) BN_add(D, D, C);
+      BN_rshift(C, D, wordsize*64);
+         ++wordsize;
+      BN_rshift(J, B, (wordsize/2)*128-64);
+      BN_mask_bits(J, 64);
+         --wordsize;
+     } 
+      break; 
+       case 0x32:
+     {
+      if(l<=0) l = wordsize;
+      else if(l>34) l = 34;
+      cBN scalar;
+      if(!BN_is_zero(D)) {
+               scalar.GetLE(data, l*8);
+               BN_div(C, B, scalar, D, ctx);
+               BN_rshift(A, C, 17*8*8);
+               A.Commit(17);
+               C.Commit(17);
+      }
+      BN_zero(J);
+     }
+     break;
+    case 0x36:
+    case 0x38: 
+         {
+        AddMapCycles(230);
+        MonMul0(B, f==0x36?A:B, B, C, D, J);
+        AddMapCycles(102);
+        MonMulFin(B, D); 
+         }
+         break;
+       case 0x39:
+       case 0x3a:
+     {
+
+      cBN scalar;
+
+      AddMapCycles(f==0x39?433:192);
+      IMonInit();
+
+      if(f==0x39) scalar.GetLE(data, wordsize*8);
+      MonMul(B, (f==0x39?(BIGNUM*)scalar:A), B);
+      MonMul(B, A, B);
+/*      {
+      cBN tmp1,tmp2,tmp3;         
+      BN_rshift(tmp1,D,64);
+      BN_mask_bits(tmp1,64);
+      BN_lshift(tmp1,tmp1,64);         
+      BN_copy(tmp2,D);
+      BN_mask_bits(tmp2,64);
+      BN_rshift(tmp2,tmp2,16);
+      BN_copy(tmp3,D);
+      BN_mask_bits(tmp3,16);
+      BN_lshift(tmp3,tmp3,48);
+      BN_copy(D,tmp1);
+      BN_add(D,D,tmp2);
+      BN_add(D,D,tmp3);
+      }
+*/
+      MakeJ();
+      BN_zero(R);
+      BN_set_bit(R,136); //27:26
+      BN_mod(B,R,D,ctx);
+      for (int i=0; i<4; i++)
+      {
+        MonMul(B,B,B);
+      }
+      MonMul(B,A,B);
+               BN_zero(B);
+      }
+     break;
+    case 0x3b:
+         Map3b(data, l);
+      break;
+   case 0x3c: 
+     Map3c(data, l); 
+     break;
+    case 0x3d:
+       D.GetLE(data,16);
+        {
+          cBN v;
+          BN_zero(v);
+          BN_sub(J,v,D);
+          BN_set_bit(J,0);
+          BN_set_bit(v,64);
+          BN_mod_inverse(J,J,v,ctx);
+          BN_mask_bits(J,64);
+          BN_mod(D,D,v,ctx); 
+          BN_mul(C,J,D,ctx); 
+          BN_rshift(C,C,64); 
+          BN_mask_bits(C,64);
+          BN_rshift(A,A,64);
+          BN_mask_bits(A,64);
+          BN_lshift(A,A,64);
+          BN_add(C,A,C);
+          BN_free(v);
+        }
+/*      {
+         D.GetLE(data,(l<=0? l:wordsize)<<3);
+         MakeJ();
+         MonMul0(C,B,B,B,D,J);
+      }*/
+     break;
+       case 0x3e:
+         Map3e(data, l);
+         break;
+    case 0x46:
+      AddMapCycles(328);
+      IMonInit();
+      break;
+       case 0x4d:
+         if(-0x018000==l) {
+                 BN_mask_bits(B,64);
+         } else {
+                 BN_set_bit(B,(wordsize<<6)-1);
+                 if(-0x028000==l) {
+                         BN_set_bit(B,(wordsize<<6)-2);
+                 }
+         }
+         BN_set_bit(B,0);
+         for(int i=0; i<sizeof(primes); ++i) residues[i]=BN_mod_word(B,primes[i]);
+         break;
+       case 0x4e:
+//       MakePrime(B,residues);
+         BN_copy(D,B);
+         break;
+    case 0x4f:
+/*      BN_copy(D,B);
+      BN_zero(A);
+//       BN_set_bit(A,56);//cycles=752
+      BN_set_bit(A,40);//cycles=808 
+         BN_add(A,A,A);*/
+         BN_set_word(A,2);
+         BN_add(B,B,A);
+      break;
+    case 0x57:
+         Map57(data, l);
+         break;
+
+    default:
+      return false;
+    }
+  return true;
+}
+
+// -- cMECM ----------------------------------------------------------------
+
+class cMECM: public cMap0101 {
+private:
+  unsigned char sig[12];
+  cIDEA idea;
+public:
+  cMECM(const unsigned char* sig);
+  virtual bool CompareSig(const unsigned char *sig) const;
+  void ComputeCW(unsigned char *digest, unsigned char *cw);
+  virtual int Algo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw) { return MARV_NOT_PROCESSED; }
+  virtual int PostAlgo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw) { return MARV_NOT_PROCESSED; }
+};
+
+cMECM::cMECM(const unsigned char* sig)
+{
+  memcpy(this->sig, sig, sizeof(this->sig));
+}
+
+bool cMECM::CompareSig(const unsigned char *sig) const
+{
+  return memcmp(this->sig, sig, sizeof(this->sig))==0;
+}
+
+void cMECM::ComputeCW(unsigned char *digest, unsigned char *cw)
+{
+  memcpy(digest+8,digest,8);
+  IdeaKS ks;
+  idea.SetEncKey(digest,&ks);
+  memcpy(digest+0,cw+8,6);
+  memcpy(digest+6,cw+0,6);
+  idea.Encrypt(digest+4,8,digest+4,&ks,0);
+  idea.Encrypt(digest+0,8,digest+0,&ks,0);
+
+  memcpy(cw+ 0,digest+6,3);
+  memcpy(cw+ 4,digest+9,3);
+  memcpy(cw+ 8,digest+0,3);
+  memcpy(cw+12,digest+3,3);
+  for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
+}
+
+class cMECM_Map57_071227: public cMECM {
+private:
+  static unsigned char sig[12];
+  int lasttm;
+public:
+  cMECM_Map57_071227(): cMECM(sig),lasttm(0) {}
+  virtual int Algo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw);
+  virtual int PostAlgo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw);
+  virtual bool Brute(const unsigned char* cw, const unsigned char* lcw, unsigned char* hw, bool first);
+};
+
+unsigned char cMECM_Map57_071227::sig[12] = { 0x32, 0x03, 0xaa, 0x03, 0xf0, 0x09, 0x50, 0xa9, 0x99, 0x8a, 0x9b, 0xb6 };
+
+int cMECM_Map57_071227::Algo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw)
+{
+#if 0
+  unsigned char tmp[0x60];
+  
+  DoMap(SETSIZE, 0, 2);
+  memcpy(tmp, hw, sizeof(tmp));
+  DoMap(0x57, tmp, 2);
+  memcpy(hw+8, tmp, 0x40);
+  hw[0x08] = 4;
+  hw[0x10] = 16;
+  return MARV_SUCCESS;
+#else
+  return MARV_NOT_PROCESSED;
+#endif
+}
+
+int cMECM_Map57_071227::PostAlgo(int hdsize, const unsigned char *hd, const unsigned char *lhd, const unsigned char *cw, const unsigned char *lcw, const unsigned char *pcw, unsigned char *hw)
+{
+  int ret = MARV_NOT_PROCESSED;
+  if(!lcw[16]) {
+    ret = MARV_SYNCING;
+    if(CheckNull(lhd, hdsize)) {
+      PRINTF(L_SYS_ECM,"Syncing to stream");
+    } else {
+      if(memcmp(lhd, hd, hdsize)==0 && memcmp(cw, pcw, 16)) {
+        if(Brute(cw, pcw, hw, true)) {
+          PRINTF(L_SYS_ECM,"Stream synced");
+          ret = MARV_SUCCESS;
+        }
+      }
+      if(ret!=MARV_SUCCESS && !CheckNull(lcw, 16) && Brute(cw, lcw, hw, false)) {
+        PRINTF(L_SYS_ECM,"Stream synced");
+        ret = MARV_SUCCESS;
+      }
+      if(ret!=MARV_SUCCESS) {
+        PRINTF(L_SYS_ECM,"Syncing to stream");
+        ret = MARV_MAYBE_SUCCESS;
+      }
+    }
+    if(ret!=MARV_SUCCESS && lasttm) {
+      hw[0x08] = 0xff - (((lasttm-1+4-0x101*4)/4)&0xff);
+      hw[0x10] = 0xff - (((lasttm-1+16-0x101*16)/16)&0xff);
+    }
+  } else {
+    if(Brute(cw, lcw, hw, false))
+      ret = MARV_SUCCESS;
+    else {
+      PRINTF(L_SYS_ECM,"Stream desynced");
+      ret = MARV_DESYNCED;
+    }
+  }
+
+  return ret;
+}
+
+bool cMECM_Map57_071227::Brute(const unsigned char* cw, const unsigned char* lcw, unsigned char* hw, bool first)
+{
+  bool ret = false;
+  unsigned char digest[20], digest2[20];
+  unsigned char tcw[2][16];
+  const unsigned char* ccw = first ? tcw[1] : lcw;
+
+  memcpy(hw+128,hw,64);
+  RotateBytes(hw+64,128);
+
+  for(int tm=4096+4; tm<8192+4; tm+=4) {
+    hw[127-0x08] = 0xff - (((tm-1+4-0x101*4)/4)&0xff);
+    hw[127-0x10] = 0xff - (((tm-1+16-0x101*16)/16)&0xff);
+    SHA1(hw+64,128,digest);
+    RotateBytes(digest,20);
+    memcpy(tcw[0], cw, 16);
+    if(first) {
+      memcpy(digest2, digest, sizeof(digest2));
+      memcpy(tcw[1], lcw, 16);
+      ComputeCW(digest2, tcw[1]);
+      }
+    ComputeCW(digest, tcw[0]);
+    if(memcmp(tcw[0], ccw, 8)==0 || memcmp(tcw[0]+8, ccw+8, 8)==0) {
+      lasttm = tm;
+      ret = true;
+      break;
+      }
+  }
+
+  RotateBytes(hw+64,128);
+  if(ret)
+    memcpy(hw,hw+128,64);
+  else if(lasttm) {
+    hw[0x08] = 0xff - (((lasttm-1+4-0x101*4)/4)&0xff);
+    hw[0x10] = 0xff - (((lasttm-1+16-0x101*16)/16)&0xff);
+    }
+  return ret;
+}
+
+static cMECM_Map57_071227 MECM_Map57;
+static cMECM* vMECM0101[] = { &MECM_Map57 };
+
+// -- cN2Prov0101 --------------------------------------------------------------
+
+class cN2Prov0101 : public cMutex, public cN2Prov, public cN2Emu, private cMap0101 {
+private:
+  int desSize;
+  DES_key_schedule desks1, desks2;
+  unsigned char desblock[8];
+  IdeaKS ks;
+  cMapMemHW *hwMapper;
+  bool special05;
+  //
+protected:
+  int mecmAddr[2];
+  int mecmKeyId;
+  int bfValue;
+  //
+  virtual int Algo(int algo, const unsigned char *hd, const unsigned char *ed, unsigned char *hw, const unsigned char *cw);
+  virtual void DynamicHD(unsigned char *hd, const unsigned char *ed);
+  virtual bool RomInit(void);
+  virtual void Stepper(void);
+  virtual void TimerHandler(unsigned int num);
+  virtual void AddMapCycles(unsigned int num);
+  virtual void WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op);
+  virtual void ReadHandler(unsigned char seg, unsigned short ea, unsigned char &op);
+  virtual unsigned int CpuCycles(void) { return Cycles(); }
+  void AddRomCallbacks(void);
+  bool RomCallbacks(void);
+  bool ProcessMap(int f);
+  bool ProcessDESMap(int f);
+public:
+  cN2Prov0101(int Id, int Flags);
+  virtual bool PostProcAU(int id, unsigned char *data);
+  virtual int ProcessBx(unsigned char *data, int len, int pos);
+  virtual int ProcessEx(unsigned char *data, int len, int pos);
+  virtual int RunEmu(unsigned char *data, int len, unsigned short load, unsigned short run, unsigned short stop, unsigned short fetch, int fetch_len, int maxinstr=100000);
+  virtual bool BFReset() { bfValue=0; return true; }
+  virtual bool BFNext() { return (++bfValue<1024); }
+  };
+
+static cN2ProvLinkReg<cN2Prov0101,0x0101,(N2FLAG_MECM|N2FLAG_POSTAU|N2FLAG_Bx|N2FLAG_Ex)> staticPL0101;
+
+cN2Prov0101::cN2Prov0101(int Id, int Flags)
+:cN2Prov(Id,Flags)
+{
+  special05 = false;
+  hasWriteHandler = hasReadHandler = true;
+  mecmAddr[0]=0x91d7; mecmAddr[1]=0x92d7; mecmKeyId=0x106;
+  seedSize=10;
+  desSize=16; hwMapper=0;
+  SetMapIdent(Id);
+}
+
+void cN2Prov0101::DynamicHD(unsigned char *hd, const unsigned char *ed)
+{
+  hd[5]=ed[5];
+  hd[6]=(ed[7]&0xEF) | ((ed[6]&0x40)>>2);
+  hd[7]=ed[8];
+  hd[8]=(ed[9]&0x7F) | ((ed[6]&0x20)<<2);
+  hd[9]=ed[6]&0x80;
+}
+
+int cN2Prov0101::Algo(int algo, const unsigned char *hd, const unsigned char *ed, unsigned char *hw, const unsigned char *cw)
+{
+  if(algo!=0x40 && algo!=0x60) {
+    PRINTF(L_SYS_ECM,"%04X: unknown MECM algo %02x",id,algo);
+    return MARV_ERROR;
+    }
+  if(!Init(id,102)) {
+    PRINTF(L_SYS_ECM,"%04X: failed to initialize ROM",id);
+    return MARV_ERROR;
+    }
+
+  unsigned char keyNr=(algo>>5)&0x01;
+  unsigned char mecmCode[256];
+  GetMem(mecmAddr[keyNr],mecmCode,256,0x80);
+  cPlainKey *pk;
+  unsigned char ideaKey[16];
+  if(!(pk=keys.FindKey('N',mecmKeyId,keyNr,sizeof(ideaKey)))) {
+    PRINTF(L_SYS_KEY,"missing %04x %02x MECM key",mecmKeyId,keyNr);
+    return false;
+    }
+  pk->Get(ideaKey);
+  idea.SetEncKey(ideaKey,&ks);
+  for(int i=0x100-8; i>=8; i-=8) {
+    idea.Encrypt(mecmCode+i,8,mecmCode+i,&ks,0);
+    xxor(mecmCode+i,8,mecmCode+i,mecmCode+i-8);
+    }
+  idea.Encrypt(mecmCode,8,mecmCode,&ks,0);
+  HEXDUMP(L_SYS_RAWECM,mecmCode,sizeof(mecmCode),"decrypted MECM code");
+  // check signature
+  unsigned char data[256];
+  memcpy(data,mecmCode,sizeof(data));
+  RotateBytes(data,sizeof(data));
+  SHA1(data,sizeof(data)-8,data);
+  RotateBytes(data,20);
+  if(memcmp(data,mecmCode,8)) {
+     PRINTF(L_SYS_ECM,"%04X: MECM %02x decrypt signature failed",id,keyNr);
+     return false;
+    }
+
+  memcpy(hw,hd,seedSize);
+  ExpandInput(hw);
+
+  cMECM *mecm = NULL;
+  int i;
+  for(i=0; i<sizeof(vMECM0101)/sizeof(*vMECM0101); ++i) {
+    if(vMECM0101[i]->CompareSig(mecmCode)) {
+      mecm = vMECM0101[i];
+      break;
+    }
+  }
+
+  i = MARV_NOT_PROCESSED;
+  if(mecm) {
+    if((i=mecm->Algo(seedSize, hd, lasthd, cw, lastcw, prevcw, hw))==MARV_ERROR)
+      return i;
+  }
+
+  if(i==MARV_NOT_PROCESSED) {
+    SetMem(0xa00,hd,seedSize);
+    SetMem(0x0ba2,hw,0x80);
+    i = MARV_ERROR;
+    if(RunEmu(mecmCode+8, sizeof(mecmCode)-8, 0x100, 0x100, 0, 0, 0, 200000)>0) {
+      GetMem(0x0ba2,hw,0x80);
+      i = MARV_SUCCESS;
+      }
+    if(i==MARV_ERROR) return i;
+  }
+
+  if(mecm && (i=mecm->PostAlgo(seedSize, hd, lasthd, cw, lastcw, prevcw, hw))!=MARV_NOT_PROCESSED)
+    return i;
+
+  return MARV_SUCCESS;
+
+/*
+  if(algo==0x40) {
+    const int hwCount=2;
+    memcpy(hw,hd,hwCount+1);
+    ExpandInput(hw);
+    hw[0x18]|=1; hw[0x40]|=1;
+    DoMap(SETSIZE,0,2);
+    DoMap(IMPORT_A,hw,3);
+    DoMap(IMPORT_D,hw+24);
+    DoMap(0x3b);
+    DoMap(EXPORT_C,hw);
+    DoMap(IMPORT_A,hw+40,3);
+    DoMap(IMPORT_D,hw+64);
+    DoMap(0x3b);
+    DoMap(EXPORT_C,hw+40);
+    DoMap(0x43);
+    DoMap(0x44,hw);
+    DoMap(0x44,hw+64);
+    memcpy(&hw[0],&hw[64+4],hwCount);
+    memset(&hw[hwCount],0,128-hwCount);
+    return true;
+    }
+  else if(algo==0x60) { // map 4D/4E/57
+    hw[127]|=0x80; hw[0]|=0x01;
+    DoMap(SETSIZE,0,16);
+    DoMap(IMPORT_A,hw);
+    DoMap(0x4d);
+    DoMap(0x4e);
+    DoMap(EXPORT_A,hw);
+    RotateBytes(hw,16);
+    DoMap(IMPORT_A,hw);
+    DoMap(0x4e);
+    DoMap(EXPORT_A,hw);
+    DoMap(0x57,hw);
+    return true;
+    }
+
+  PRINTF(L_SYS_ECM,"%04X: unknown MECM algo %02x",id,algo);
+*/
+  return MARV_ERROR;
+}
+
+bool cN2Prov0101::PostProcAU(int id, unsigned char *data)
+{
+  if(data[1]&0x5f) return false;
+  return true;
+}
+
+bool cN2Prov0101::RomInit(void)
+{
+       cMutexLock lock(this);
+       if(!AddMapper(hwMapper=new cMapMemHW(*this),HW_OFFSET,HW_REGS,0x00)) return false;
+  SetPc(0x4000);
+  SetSp(0x0FFF,0x0FE0);
+  ClearBreakpoints();
+  AddBreakpoint(0x537d);
+  AddBreakpoint(0x8992);
+  AddBreakpoint(0xA822);
+  while(!Run(5000)) {
+    switch(GetPc()) {
+      case 0x537d:
+        PRINTF(L_SYS_EMU,"%04x: ROM init successfull",id);
+        return true;
+      default:
+        PRINTF(L_SYS_EMU,"%04x: ROM init failed: unexpected breakpoint",id);
+        break;
+      }
+    }
+  return false;
+}
+
+void cN2Prov0101::AddMapCycles(unsigned int num)
+{
+  AddCycles(num);
+}
+
+bool cN2Prov0101::ProcessMap(int f)
+{
+  unsigned short addr;
+  unsigned char tmp[512];
+  int l=GetOpSize(Get(0x48));
+  int dl=l<<3;
+
+  switch(f) {
+    case SETSIZE:
+      DoMap(f,0,Get(0x48));
+      break;
+    case IMPORT_J:
+      l=8; dl=8<<3;
+      // fall throught
+    case IMPORT_A:
+    case IMPORT_B:
+    case IMPORT_C:
+    case IMPORT_D:
+    case IMPORT_LAST:
+      addr=HILO(0x44);
+      GetMem(addr,tmp,dl,0); DoMap(f,tmp,l);
+      break;
+    case EXPORT_J:
+      l=8; dl=8<<3;
+      // fall throught
+    case EXPORT_A:
+    case EXPORT_B:
+    case EXPORT_C:
+    case EXPORT_D:
+    case EXPORT_LAST:
+      addr=HILO(0x44);
+      DoMap(f,tmp,l); SetMem(addr,tmp,(f == EXPORT_J ? 8 : dl),0);
+      break;
+    case SWAP_A:
+    case SWAP_B:
+    case SWAP_C:
+    case SWAP_D:
+      addr=HILO(0x44);
+      GetMem(addr,tmp,dl,0); DoMap(f,tmp,l); SetMem(addr,tmp,dl,0);
+      break;
+    case CLEAR_A:
+    case CLEAR_B:
+    case CLEAR_C:
+    case CLEAR_D:
+    case COPY_A_B:
+    case COPY_B_A:
+    case COPY_A_C:
+    case COPY_C_A:
+    case COPY_C_D:
+    case COPY_D_C:
+      DoMap(f);
+      break;
+    case 0x22:
+      DoMap(f,tmp,(Get(0x48)<<16)|(Get(0x4a)<<8)|Get(0x49));
+      break;
+    case 0x23:
+    case 0x25:
+       DoMap(f);
+     break;
+    case 0x29:
+    case 0x2a:
+      DoMap(f,tmp);
+      Set(0x4b,tmp[0]);
+      break;
+    case 0x3c:
+    case 0x3e:
+      if(l>wordsize) { l=wordsize; dl=l<<3; }
+      // fall through
+    case 0x32:
+    case 0x39:
+      if(l>34) { l=34; dl=34<<3; }
+    case 0x2e:
+       case 0x2F:
+    case 0x3b:
+    case 0x3d:
+      GetMem(HILO(0x44),tmp,dl,0);
+      DoMap(f,tmp,l);
+      break;
+    case 0x21:
+    case 0x30:
+    case 0x31:
+    case 0x36:
+    case 0x38:
+    case 0x3a:
+    case 0x43:
+       case 0x4f:
+      DoMap(f);
+      break;
+    case 0x44:
+    case 0x45:
+      GetMem(0x400,tmp,64,0);
+      DoMap(f,tmp,l);
+      SetMem(0x440,tmp,20,0);
+      break;
+    case 0x46:
+      GetMem(HILO(0x44),tmp,8,0);
+      DoMap(f,tmp,l);
+      break;
+    case 0x4d:
+      DoMap(f,tmp,-((Get(0x48)<<16)|(Get(0x49)<<8)|Get(0x4a)));
+      SetMem(0x400,tmp,53,0);
+      break;
+    case 0x4e:
+      GetMem(0x400,tmp,53,0);
+      DoMap(f,tmp);
+      SetMem(0x400,tmp,53,0);
+      break;
+    case 0x57:
+      addr=HILO(0x46);
+      l=wordsize; if(l<2 || l>4) l=4;
+      dl=l<<3;
+      for(int i=0; i<6; i++) GetMem(HILO(addr+i*2),tmp+i*dl,dl,0);
+      DoMap(f,tmp);
+      SetMem(0x400,tmp,0x40,0);
+      //memset(tmp,0,11*32);
+      //SetMem(0x440,tmp,11*32,0);
+      //AddCycles(MapCycles());
+      AddCycles((int)MapCycles()>0 ? MapCycles() : 4*bfValue);
+      break;
+    default:
+      PRINTF(L_SYS_MAP,"%04x: map call %02x not emulated",id,f);
+      return false;
+    }
+  c6805::a=0; c6805::x=0; c6805::y=0; 
+  return true;
+}
+
+bool cN2Prov0101::ProcessDESMap(int f)
+{
+  unsigned char data[16];
+  switch(f) {
+    case 0x05:  // 3DES encrypt
+      DES_ecb2_encrypt(DES_CAST(desblock),DES_CAST(desblock),&desks1,&desks2,DES_ENCRYPT);
+      break;
+    case 0x06:  // 3DES decrypt
+      DES_ecb2_encrypt(DES_CAST(desblock),DES_CAST(desblock),&desks1,&desks2,DES_DECRYPT);
+      break;
+    case 0x07:  // 3DES decrypt
+      Set(0x00, 0x2d, 0x02);
+      break;
+    case 0x0b:  // load DES data block from memory
+      GetMem(HILO(0x25),desblock,8,Get(0x24));
+      break;
+    case 0x0c:  // store DES data block to memory
+      SetMem(HILO(0x2b),desblock,8,Get(0x2a));
+      break;
+    case 0x0e:  // get DES key1 and key2
+      GetMem(HILO(0x25),data,8,Get(0x24));
+      DES_key_sched((DES_cblock *)data,&desks1);
+      GetMem(HILO(0x28),data,8,Get(0x27));
+      DES_key_sched((DES_cblock *)data,&desks2);
+      break;
+    case 0x0f:  // set DES size
+      desSize=Get(0x2d);
+      if(desSize!=0x10 && desSize!=0x18) {
+        PRINTF(L_SYS_EMU,"%04x: invalid DES key size %02x",id,desSize);
+        return false;
+        }
+      break;
+    default:
+      PRINTF(L_SYS_MAP,"%04x: DES map call %02x not emulated",id,f);
+      return false;
+  }
+  return true;
+}
+
+bool cN2Prov0101::RomCallbacks(void)
+{
+  bool dopop=true;
+  unsigned int ea=GetPc();
+  if(ea&0x8000) ea|=(cr<<16);
+  switch(ea) {
+    case 0x3840: //MAP Handler
+    case 0x00A822:
+      if(!ProcessMap(a)) return false;
+      if(Interrupted()) dopop=false;
+      break;
+    case 0x3844: //DES Handler
+      if(!ProcessDESMap(a)) return false;
+      break;
+    case 0x5F23: //Erase_RAM_and_Hang
+    case 0x5F27: //Erase_RAM_and_Hang_Lp
+    case 0x5F5E: //BrainDead
+      PRINTF(L_SYS_EMU,"%04x: emu hung at %04x",id,ea);
+      return false;
+    case 0x70A6: //Write_Row_EEP_RC2_Len_A_To_RC1
+      {
+      unsigned short rc1=HILO(0x47);
+      unsigned short rc2=HILO(0x4a);
+      for(int i=0; i<a; i++) Set(0x80,rc1++,Get(dr,rc2++));
+      Set(0x4a,rc2>>8);
+      Set(0x4b,rc2&0xff);
+      break;
+      }
+    case 0x7BFE: //Write_Row_EEPROM_A_from_X_to_RC1
+      {
+      unsigned short rc1=HILO(0x47);
+      unsigned short rc2=c6805::x;
+      for(int i=0; i<a; i++) Set(0x80,rc1++,Get(rc2++));
+      break;
+      }
+    case 0x7CFF: //UPDATE_USW_03DD_03DE
+      Set(0x30E8,Get(0x03DD));
+      Set(0x30E9,Get(0x03DE));
+      break;
+    case 0x00A23C: //IDEA_Generate_Expanded_Key
+      {
+      unsigned char key[16];
+      GetMem(0x0a20,key,16);
+      idea.SetEncKey(key,&ks);
+      break;
+      }
+    case 0x00A2E9: //IDEA_Cypher
+      {
+      unsigned char data[8];
+      GetMem(0x070,data,8);
+      idea.Encrypt(data,8,data,&ks,0);
+      SetMem(0x070,data,8);
+      break;
+      }
+    default:
+      PRINTF(L_SYS_EMU,"%04X: unknown ROM breakpoint %04x",id,ea);
+      return false;
+    }
+  if(dopop) {
+    if(ea>=0x8000) PopCr();
+    PopPc();
+    }
+  return true;
+}
+
+void cN2Prov0101::AddRomCallbacks(void)
+{
+  AddBreakpoint(0xA822); // map handler
+  AddBreakpoint(0x3840);
+  AddBreakpoint(0x3844);
+  AddBreakpoint(0xA23C); //IDEA 
+  AddBreakpoint(0xA2E9);
+  AddBreakpoint(0x70A6);
+  AddBreakpoint(0x7BFE);
+  AddBreakpoint(0x7CFF);
+  AddBreakpoint(0x5F23);
+  AddBreakpoint(0x5F27);
+  AddBreakpoint(0x5F5E);
+}
+
+int cN2Prov0101::ProcessBx(unsigned char *data, int len, int pos)
+{
+  if(Init(id,102)) {
+    SetMem(0x92,data+pos-1,len-pos+1);
+    SetPc(0x93);
+    SetSp(0x0FFF,0x0EF8);
+    ClearBreakpoints();
+    AddBreakpoint(0x9569);
+    AddBreakpoint(0x0000);
+    AddRomCallbacks();
+    while(!Run(1000)) {
+      if(GetPc()==0x9569) {
+        GetMem(0x80,data,len);
+        return a;
+        }
+      else if(GetPc()==0x0000) break;
+      else if(!RomCallbacks()) break;
+      }
+    }
+  return -1;
+}
+
+int cN2Prov0101::ProcessEx(unsigned char *data, int len, int pos)
+{
+  if(Init(id,102)) {
+    SetMem(0x80,data,len);
+    SetPc(0x9591);
+    SetSp(0x0FFF,0x0EF8);
+    Push(0x99); //push the bug-table return onto the stack
+    Push(0x95);
+    Push(0x00);
+    Set(0x00,0x62,0x26);
+    Set(0x00,0x63,0x02);
+    Set(0x00,0x03d3,len-0x12);
+    ClearBreakpoints();
+    AddBreakpoint(0x9569);
+    AddBreakpoint(0x9599);
+    AddRomCallbacks();
+    while(!Run(10000)) {
+      if(GetPc()==0x9569) {
+        GetMem(0x80,data,len);
+        return max((int)a,6);
+        }
+      else if(GetPc()==0x9599) break;
+      else if(!RomCallbacks()) break;
+      }
+    }
+  return -1;
+}
+
+int cN2Prov0101::RunEmu(unsigned char *data, int len, unsigned short load, unsigned short run, unsigned short stop, unsigned short fetch, int fetch_len, int maxinstr)
+{
+  if(Init(id,102)) {
+    SetSp(0x0FFF,0x0EF8);
+    SetMem(load,data,len);
+    SetPc(run);
+    ClearBreakpoints();
+    AddBreakpoint(stop);
+    if(stop!=0x0000) AddBreakpoint(0x0000);
+    AddRomCallbacks();
+    while(!Run(maxinstr)) {
+      if(GetPc()==0x0000 || GetPc()==stop) {
+        GetMem(fetch,data,fetch_len);
+        return 1;
+        }
+      else if(!RomCallbacks()) break;
+      }
+    }
+  return -1;
+}
+
+void cN2Prov0101::TimerHandler(unsigned int num)
+{
+  if(hwMapper) {
+    int mask=hwMapper->AddCycles(num);
+    for(int t=0; mask; mask>>=1,t++)
+      if(mask&1) {
+        DisableTimers(11);
+        if(t==2) {
+          PRINTF(L_SYS_MAP,"Timer interrupt %u @ %04x",t,GetPc());
+          RaiseException(9);
+          if(Interruptible()) throw(t);
+          }
+        }
+    }
+}
+
+void cN2Prov0101::Stepper(void)
+{
+  unsigned short pc=GetPc();
+  if(pc>=0x93 && pc<=0xE0) {   // pc in EMM data
+    unsigned char op=Get(pc);
+    if((op&0xF0)==0x00) {      // current opcode BRCLR/BRSET
+      int fake=0;              // 1=branch -1=non-branch
+      if(Get(pc+3)==0x81)      // next opcode == RTS
+        fake=1;                        // fake branch
+      else {
+        unsigned char off=Get(pc+2);
+        unsigned short target=pc+3+off;
+        if(off&0x80) target-=0x100;
+        if(Get(target)==0x81)  // branch target == RTS
+          fake=-1;             // fake non-branch
+        }
+      if(fake) {
+        unsigned short ea=Get(pc+1);
+        unsigned char val=Get(dr,ea);
+        int bit=1<<((op&0xF)>>1);
+        // set/clr bit according to fake-mode and opcode
+        if((fake>0 && (op&0x01)) || (fake<0 && !(op&0x01)))  {
+          if(val&bit) loglb->Printf("*");
+          val&=~bit;
+          }
+        else {
+          if(!(val&bit)) loglb->Printf("*");
+          val|=bit;
+          }
+        Set(dr,ea,val);
+        }
+      }
+    }
+}
+void cN2Prov0101::WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op)
+{
+       switch(ea) {
+       case cMapMemHW::HW_SECURITY:
+               if(cr==0x00) op = Get(ea);
+               break;
+       case 0x05:
+               if(cr==0x00) special05=(op&0x40)!=0;
+               break;
+       }
+}
+
+void cN2Prov0101::ReadHandler(unsigned char seg, unsigned short ea, unsigned char &op)
+{
+       if(special05) {
+               special05=false; // prevent loop
+               unsigned short start=Get(0x00,0x30C0);
+               unsigned short end=Get(0x00,0x30C1);
+               if(((ea>>8)>=start) && ((ea>>8)<=end)) op=0x00; // dataspace
+               else op=0x01;                                   // codespace
+               special05=true;
+       }
+
+       switch(ea) {
+        case 0x2f71:
+//                op = (rand()&0xeb)^((op&2)<<3)^((op&0x40)>>4);
+                               op = (rand()&0xe3)^((op&2)<<3)^((op&0x40)>>4)^((op&1)<<3)^((op&0x80)>>4);
+                Set(seg, ea, op);
+                break;
+        } 
+}
+
+
+
+// -- cN2Prov0901 --------------------------------------------------------------
+
+class cN2Prov0901 : public cN2Prov0101 {
+public:
+  cN2Prov0901(int Id, int Flags);
+  };
+
+static cN2ProvLinkReg<cN2Prov0901,0x0901,(N2FLAG_MECM|N2FLAG_POSTAU|N2FLAG_Bx|N2FLAG_Ex)> staticPL0901;
+
+cN2Prov0901::cN2Prov0901(int Id, int Flags)
+:cN2Prov0101(Id,Flags)
+{
+  mecmAddr[0]=0x91f5; mecmAddr[1]=0x92f5; mecmKeyId=0x907;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0501.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-0501.c
new file mode 100644 (file)
index 0000000..0be284e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nagra2.h"
+
+// -- cMap0501 -----------------------------------------------------------------
+
+class cMap0501 : public cMapCore {
+private:
+protected:
+  virtual bool Map(int f, unsigned char *data, int l);
+public:
+  cMap0501(void);
+  };
+
+cMap0501::cMap0501(void)
+{
+}
+
+bool cMap0501::Map(int f, unsigned char *data, int l)
+{
+  switch(f) {
+    case 0x37:
+      l=(l?l:wordsize)<<3;
+      H.GetLE(data,l);
+      MonMul(B,H,A);
+      break;
+    case 0x3a:
+      MakeJ();
+      BN_zero(R);
+      BN_set_bit(R,68*wordsize);
+      BN_mod(H,R,D,ctx);
+      for(int i=0; i<4; i++) MonMul(H,H,H);
+      MonMul(B,A,H);
+      MonMul(B,A,B);
+      break;
+    default:
+         return false;
+    }
+  return true;
+}
+
+// -- cN2Prov0501 --------------------------------------------------------------
+
+class cN2Prov0501 : public cN2Prov, private cMap0501 {
+protected:
+  virtual int Algo(int algo, const unsigned char *hd, const unsigned char *ed, unsigned char *hw, const unsigned char *cw);
+  virtual bool NeedsCwSwap(void) { return true; }
+public:
+  cN2Prov0501(int Id, int Flags):cN2Prov(Id,Flags) { seedSize=3; SetMapIdent(Id); }
+  };
+
+static cN2ProvLinkReg<cN2Prov0501,0x0501,N2FLAG_MECM|N2FLAG_INV> staticPL0501;
+
+int cN2Prov0501::Algo(int algo, const unsigned char *hd, const unsigned char *ed, unsigned char *hw, const unsigned char *cw)
+{
+  if(algo==0x60) {
+    hw[0]=hd[0];
+    hw[1]=hd[1];
+    hw[2]=hd[2]&0xF8;
+    ExpandInput(hw);
+    hw[63]|=0x80;
+    hw[95]=hw[127]=hw[95]&0x7F;
+    SetWordSize(4);
+    ImportReg(IMPORT_J,hw+0x18);
+    ImportReg(IMPORT_D,hw+0x20);
+    ImportReg(IMPORT_A,hw+0x60);
+    DoMap(0x37,hw+0x40);
+    ExportReg(EXPORT_C,hw);
+    DoMap(0x3a);
+    ExportReg(EXPORT_C,hw+0x20);
+    DoMap(0x43);
+    DoMap(0x44,hw);
+    hw[0]&=7;
+    ExportReg(EXPORT_B,hw+3);
+    memset(hw+3+0x20,0,128-(3+0x20));
+    return MARV_SUCCESS;
+    }
+
+  PRINTF(L_SYS_MAP,"0501: unknown MECM algo %02x",algo);
+  return MARV_ERROR;
+}
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-4101.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-4101.c
new file mode 100644 (file)
index 0000000..0b00dc7
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nagra2.h"
+
+// -- cMap4101 -----------------------------------------------------------------
+
+class cMap4101 : public cMapCore {
+protected:
+  virtual bool Map(int f, unsigned char *data, int l);
+  };
+
+bool cMap4101::Map(int f, unsigned char *data, int l)
+{
+  switch(f) {
+    case 0x58:
+      {
+      cBN a, b, x, y, scalar;
+      D.GetLE(data+0x00,16);
+      x.GetLE(data+0x10,16);
+      y.GetLE(data+0x20,16);
+      b.GetLE(data+0x30,16);
+      a.GetLE(data+0x40,16);
+      scalar.GetLE(data+0x50,16);
+      int scalarbits=BN_num_bits(scalar);
+      if(scalarbits>=2 && !BN_is_zero(x) && !BN_is_zero(y) && !BN_is_zero(b)) {
+        CurveInit(a);
+        ToProjective(0,x,y);
+        BN_copy(Qx,Px);
+        BN_copy(Qy,Py);
+        for(int i=scalarbits-2; i>=0; i--) {
+          DoubleP(0);
+          if(BN_is_bit_set(scalar,i)) AddP(0);
+          }
+        ToAffine();
+        }
+      memset(data,0,64);
+      Px.PutLE(&data[0],16);
+      Py.PutLE(&data[32],16);
+      break;
+      }
+    case 0x44:
+      sctx.h4=UINT32_LE(data);
+      sctx.h3=UINT32_LE(data+4);
+      sctx.h2=UINT32_LE(data+8);
+      sctx.h1=UINT32_LE(data+12);
+      sctx.h0=UINT32_LE(data+16);
+      cMapCore::DoMap(0x44,data);
+      break;
+    default:
+      return false;
+    }
+  return true;
+}
+
+// -- cN2Prov4101 ----------------------------------------------------------------
+
+class cN2Prov4101 : public cN2Prov, public cMap4101, public cN2Emu {
+protected:
+  virtual void WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op);
+public:
+  cN2Prov4101(int Id, int Flags);
+  virtual bool PostProcAU(int id, unsigned char *data);
+  virtual int ProcessBx(unsigned char *data, int len, int pos);
+  };
+
+static cN2ProvLinkReg<cN2Prov4101,0x4101,(N2FLAG_POSTAU|N2FLAG_Bx)> staticPL4101;
+
+cN2Prov4101::cN2Prov4101(int Id, int Flags)
+:cN2Prov(Id,Flags)
+{
+  hasWriteHandler=true;
+  SetMapIdent(Id);
+}
+
+bool cN2Prov4101::PostProcAU(int id, unsigned char *data)
+{
+  if(data[1]==0x01) {
+    cPlainKey *pk;
+    if(!(pk=keys.FindKeyNoTrig('N',id,MBC(N2_MAGIC,0x30),16))) {
+      PRINTF(L_SYS_EMM,"missing %04x NN 30 3DES key (16 bytes)",id);
+      return false;
+      }
+    unsigned char dkey[16];
+    pk->Get(dkey);
+    DES_key_schedule ks1, ks2;
+    DES_key_sched((DES_cblock *)&dkey[0],&ks1);
+    DES_key_sched((DES_cblock *)&dkey[8],&ks2);
+    DES_ecb2_encrypt(DES_CAST(&data[7]),DES_CAST(&data[7]),&ks1,&ks2,DES_DECRYPT);
+    DES_ecb2_encrypt(DES_CAST(&data[7+8]),DES_CAST(&data[7+8]),&ks1,&ks2,DES_DECRYPT);
+    }
+  return true;
+}
+
+int cN2Prov4101::ProcessBx(unsigned char *data, int len, int pos)
+{
+  if(Init(id,110)) {
+    SetMem(0x80,data,len);
+    SetPc(0x80+pos);
+    SetSp(0x0FFF,0x0FE0);
+    ClearBreakpoints();
+    AddBreakpoint(0xACDD);
+    AddBreakpoint(0x9BDD);
+    AddBreakpoint(0x7EC5);
+    while(!Run(5000)) {
+      switch(GetPc()) {
+        case 0xACDD:
+          return -1;
+        case 0x9BDD:
+          GetMem(0x80,data,len);
+          return a;
+        case 0x7EC5:
+          {
+          PopPc();
+          unsigned short args=GetPc();
+          int clen=Get(args+4);
+          unsigned short ptr=(Get(args)<<8)+Get(args+1);
+          if(clen+pos-1>len) return -1; // sanity
+          GetMem(ptr,&data[pos-1],clen);
+          ptr=(Get(args+2)<<8)+Get(args+3)-0x0CED+0x00B4-0x0080; // wild hack
+          if(ptr<7 || ptr+8>len || data[ptr-1]!=0x10 || data[ptr-7]!=0x42) // sanity
+            return -1;
+          unsigned char tmp[96];
+          memcpy(&tmp[0],&data[ptr],8);
+          ptr=(((data[ptr]&0x3F)|0x40)<<8)+(data[ptr+1]&0x7F);
+          GetMem(ptr,&tmp[8],sizeof(tmp)-8);
+          DoMap(0x02,0,2);
+          DoMap(0x58,tmp);
+          DoMap(0x43);
+          DoMap(0x44,tmp);
+          SetMem(0x0440,tmp,20);
+          SetMem(0x80,data,len);
+          SetPc(0x80+pos);
+          break;
+          }
+        }
+      }
+    }
+  return -1;
+}
+
+void cN2Prov4101::WriteHandler(unsigned char seg, unsigned short ea, unsigned char &op)
+{
+  if(cr==0x00) {
+    if(ea==0x0a || ea==0x12 || ea==0x16) {
+      unsigned char old=Get(ea);
+      if(old&2) op=(old&~0x02) | (op&0x02);
+      }
+    }
+}
+
+// -- cN2Prov7101 ----------------------------------------------------------------
+
+static cN2ProvLinkReg<cN2Prov4101,0x7101,(N2FLAG_POSTAU)> staticPL7101;
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-map57.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2-map57.c
new file mode 100644 (file)
index 0000000..8beb844
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+// -- cN2Map57 ----------------------------------------------------------------
+
+class cN2Map57 {
+private:
+  void mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4);
+  void bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5);
+  void bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5);
+  void mod_sub(void);
+  void bn_func1(BIGNUM *arg0);
+  void bn_func2(int arg0);
+  void bn_func3(int arg0);
+  void bn_cmplx7(void);
+  void bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3, BIGNUM *var4, BIGNUM *var5, BIGNUM *var6);
+  BIGNUM *bn_glb0, *bn_glb1, *bn_glb3, *bn_glb5, *bn_glb6, *bn_glb7;
+  BIGNUM *bn_glb_a, *bn_glb_b, *bn_glb_c, *bn_glb_d, *bn_glb_e, *bn_glb_f, *bn_glb_g;
+  BIGNUM *bn_glb_h, *bn_glb_i, *bn_glb_j, *bn_glb_k, *bn_glb_l, *bn_glb_m;
+  BIGNUM *glb2pow128, *mask128, *glb2pow64, *mask64;
+  BN_CTX *t1;
+public:
+  void Map57(unsigned char *data);
+  };
+
+void cN2Map57::mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4)
+{
+  BN_add(arg1, arg2, arg3);
+  if(BN_cmp(arg1, arg4) >= 0) {
+    BN_sub(arg1, arg1, arg4);
+  }
+  BN_mask_bits(arg1, 128);
+}
+
+void cN2Map57::bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5)
+{
+  int j;
+  BIGNUM *var44, *var64, *var84, *vara4;
+  var44 = BN_new();
+  var64 = BN_new();
+  var84 = BN_new();
+  vara4 = BN_new();
+  BN_copy(var44, arg2);
+  BN_copy(var64, arg3);
+  BN_clear(vara4);
+  for(j=0; j<2; j++) {
+    BN_copy(var84, var64);
+    BN_mask_bits(var84, 64);
+    BN_rshift(var64, var64, 64);
+    BN_mul(var84, var84, var44, t1);
+    BN_add(vara4, vara4, var84);
+    BN_copy(var84, vara4);
+    BN_mask_bits(var84, 128);
+    BN_mul(var84, vara4, arg4, t1);
+    BN_mask_bits(var84, 64);
+    BN_mul(var84, var84, arg5, t1);
+    BN_add(vara4, vara4, var84);
+    BN_rshift(vara4, vara4, 64);
+    if(BN_cmp(vara4, arg5) >= 0) {
+      BN_sub(vara4, vara4, arg5);
+    }
+    BN_mask_bits(vara4, 128);
+  }
+  BN_copy(arg1, vara4);
+  BN_free(var44);
+  BN_free(var64);
+  BN_free(var84);
+  BN_free(vara4);
+}
+
+void cN2Map57::bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5)
+{
+  int j;
+  BIGNUM *var44, *var64, *var84, *vara4;
+  var44 = BN_new();
+  var64 = BN_new();
+  var84 = BN_new();
+  vara4 = BN_new();
+  BN_copy(var44, arg2);
+  BN_copy(var64, arg3);
+  BN_clear(vara4);
+  for(j=0; j<2; j++) {
+    BN_copy(var84, var64);
+    BN_mask_bits(var84, 64);
+    BN_rshift(var64, var64, 64);
+    BN_mul(var84, var84, var44, t1);
+    BN_add(vara4, vara4, var84);
+    BN_copy(var84, vara4);
+    BN_mask_bits(var84, 128);
+    BN_mul(var84, vara4, arg4, t1);
+    BN_mask_bits(var84, 64);
+    BN_mul(var84, var84, arg5, t1);
+    BN_add(vara4, vara4, var84);
+    BN_rshift(vara4, vara4, 64);
+    if(j==0 && BN_cmp(vara4, arg5) >= 0) {
+      BN_sub(vara4, vara4, arg5);
+    }
+    BN_mask_bits(vara4, 128);
+  }
+  BN_copy(arg1, vara4);
+  BN_free(var44);
+  BN_free(var64);
+  BN_free(var84);
+  BN_free(vara4);
+}
+
+//uses 3, 1, glb2pow128
+//sets 1, 0 (unused)
+void cN2Map57::mod_sub()
+{
+  BN_copy(bn_glb0, bn_glb3);
+  BN_mod_sub(bn_glb1, bn_glb3, bn_glb1, glb2pow128, t1);
+  BN_mask_bits(bn_glb1, 128);
+}
+
+//uses 1, 3, 6
+//sets  1, 0 (unused), 7(unused)
+void cN2Map57::bn_func1(BIGNUM *arg0)
+{
+  BIGNUM *var30 = BN_new();
+  BIGNUM *var50 = BN_new();
+  BN_copy(var30,arg0);
+  BN_mask_bits(var30, 8);
+  unsigned int x = BN_get_word(var30);
+  BN_copy(var30,arg0);
+  if( x != 0) {
+    BN_clear(var50);
+    BN_set_word(var50, 2);
+    BN_sub(var30, var30, var50);
+  } else {
+    BN_clear(var50);
+    BN_set_word(var50, 0xfe);
+    BN_add(var30, var30, var50);
+  }
+  BN_copy(bn_glb7, bn_glb1);
+  if(BN_is_zero(arg0)) {
+    BN_clear(bn_glb7);
+    BN_set_word(bn_glb7, 1);
+    BN_clear(bn_glb0);
+
+    mod_add(bn_glb1, bn_glb7, bn_glb0, bn_glb3);
+    BN_free(var30);
+    BN_free(var50);
+    return;
+  } else {
+    int msb = BN_num_bits(var30) -1;
+    while (msb > 0) {
+
+      bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+      msb--;
+      if(BN_is_bit_set(var30, msb)) {
+
+        bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+      }
+    }
+    BN_clear(bn_glb7);
+    BN_set_word(bn_glb7, 1);
+
+    bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+    BN_clear(bn_glb0);
+  }
+  BN_free(var30);
+  BN_free(var50);
+}
+
+//uses 3, 6, a, b, c, l, glb2pow128
+//sets 0, 1, 5, 7, a, b, c, f, g
+void cN2Map57::bn_func2(int arg0)
+{
+  BN_copy(bn_glb1, bn_glb_b);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+  BN_copy(bn_glb5, bn_glb_c);
+  BN_mask_bits(bn_glb1, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_g, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb7, bn_glb_a);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  mod_sub();
+  BN_copy(bn_glb_f, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+  BN_copy(bn_glb1, bn_glb_c);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb5, bn_glb_l);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb0, bn_glb_f);
+
+  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+  if(arg0 == 0) {
+    BN_copy(bn_glb_a, bn_glb1);
+  } else {
+    BN_copy(bn_glb_f, bn_glb1);
+  }
+
+  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+  BN_copy(bn_glb1, bn_glb_b);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  mod_sub();
+  if(arg0 == 0) {
+    BN_copy(bn_glb_b, bn_glb1);
+    BN_copy(bn_glb_c, bn_glb_g);
+  } else {
+    BN_copy(bn_glb_f, bn_glb1);
+    BN_copy(bn_glb_f, bn_glb_g);
+  }
+}
+
+//uses 3, 6, a, b, c, d, e, k
+//sets 0, 1, 5, 7, a, b, c, f, g, h, i, j
+void cN2Map57::bn_func3(int arg0)
+{
+  BN_copy(bn_glb1, bn_glb_c);
+  BN_copy(bn_glb7, bn_glb1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_f, bn_glb0);
+  BN_copy(bn_glb5, bn_glb_d);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+  mod_sub();
+  BN_copy(bn_glb0, bn_glb_a);
+
+  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+  BN_copy(bn_glb_g, bn_glb1);
+  BN_copy(bn_glb5, bn_glb_c);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  if(arg0 == 0) {
+    BN_copy(bn_glb_c, bn_glb0);
+  } else {
+    BN_copy(bn_glb_g, bn_glb0);
+  }
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_h, bn_glb1);
+  BN_copy(bn_glb0, bn_glb_a);
+
+  mod_add(bn_glb0, bn_glb0, bn_glb7, bn_glb3);
+  BN_copy(bn_glb7, bn_glb0);
+
+  //NOTE: don't 'mod' bn_glb1, but DO 'mod' glb_i
+  bn_cmplx1(bn_glb7, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  bn_cmplx1a(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_i, bn_glb7);
+  BN_copy(bn_glb1, bn_glb_e);
+  BN_copy(bn_glb5, bn_glb_f);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_f, bn_glb1);
+  mod_sub();
+  BN_copy(bn_glb0, bn_glb_b);
+
+  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+  BN_copy(bn_glb_j, bn_glb1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  BN_copy(bn_glb0, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+  BN_copy(bn_glb7, bn_glb0);
+  mod_sub();
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  if(arg0 == 0) {
+    BN_copy(bn_glb_a, bn_glb1);
+  } else {
+    BN_copy(bn_glb_f, bn_glb1);
+  }
+
+  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+  mod_sub();
+  BN_copy(bn_glb7, bn_glb_i);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  BN_copy(bn_glb5, bn_glb_j);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb1, bn_glb_f);
+  BN_copy(bn_glb_f, bn_glb0);
+  BN_copy(bn_glb7, bn_glb_b);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  BN_copy(bn_glb7, bn_glb1);
+  BN_copy(bn_glb1, bn_glb_g);
+  BN_copy(bn_glb5, bn_glb_h);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  mod_sub();
+  BN_copy(bn_glb7, bn_glb_f);
+
+  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+  BN_copy(bn_glb5, bn_glb_k);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  if(arg0 == 0) {
+    BN_copy(bn_glb_b, bn_glb1);
+  } else {
+    BN_copy(bn_glb_f, bn_glb1);
+  }
+}
+
+//uses c, d, e, m
+//sets 0, a, b, c
+void cN2Map57::bn_cmplx7()
+{
+  BIGNUM *var1;
+  var1 = BN_new();
+  BN_copy(bn_glb0, bn_glb_c);
+  if(BN_is_zero(bn_glb_c)) {
+    BN_copy(bn_glb_a, bn_glb_d);
+    BN_copy(bn_glb_b, bn_glb_e);
+    BN_copy(bn_glb_c, bn_glb_m);
+    bn_func3(1);
+  } else {
+    BN_clear(var1);
+    BN_set_word(var1, 0xFFFFFFFF);
+    BN_mask_bits(bn_glb_a, 32);
+    BN_lshift(var1, bn_glb_m, 0x20);
+    BN_add(bn_glb_a, bn_glb_a, var1);
+    BN_mask_bits(bn_glb_a, 128);
+    bn_func3(0);
+  }
+  BN_free(var1);
+}
+
+void cN2Map57::bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3, BIGNUM *var4, BIGNUM *var5, BIGNUM *var6)
+{
+  BIGNUM *var48;
+  int len = BN_num_bits(var6);
+  int i;
+  if(len < 2)
+    return;
+
+  if(BN_is_zero(var2) && BN_is_zero(var3) && BN_is_zero(var4))
+    return;
+  var48 = BN_new();
+  BN_copy(bn_glb3, var1);
+
+  BN_copy(bn_glb6, bn_glb3);
+  BN_set_bit(bn_glb6, 0);
+  BN_sub(bn_glb6, glb2pow128, bn_glb6);
+  BN_mod_inverse(bn_glb6, bn_glb6, glb2pow64, t1);
+  BN_clear(bn_glb0);
+  //
+  if(! BN_is_zero(bn_glb3)) {
+    BN_clear(bn_glb1);
+    BN_set_word(bn_glb1, 2);
+    BN_clear(bn_glb_k);
+    BN_set_word(bn_glb_k, 0x88);
+    BN_mod_exp(bn_glb1, bn_glb1, bn_glb_k, bn_glb3, t1);
+  }
+  //
+  for(i=0; i < 4; i++) {
+
+    bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+  }
+  //
+  BN_clear(bn_glb7);
+  BN_set_word(bn_glb7, 1);
+  BN_add(bn_glb0, bn_glb3, bn_glb7);
+  BN_copy(bn_glb_k, bn_glb0);
+  BN_rshift(bn_glb_k, bn_glb_k, 1);
+  BN_copy(bn_glb7, bn_glb1);
+  BN_copy(bn_glb5, bn_glb_k);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_k, bn_glb1);
+
+  BN_copy(bn_glb1, var5);
+  BN_mask_bits(bn_glb1, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_l, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+  BN_clear(bn_glb5);
+  BN_set_word(bn_glb5, 1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_c, bn_glb1);
+  BN_copy(bn_glb_m, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+
+  BN_copy(bn_glb5, var2);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_a, bn_glb1);
+  BN_copy(bn_glb1, bn_glb7);
+
+  BN_copy(bn_glb5, var3);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_b, bn_glb1);
+  BN_copy(bn_glb_d, bn_glb_a);
+  BN_copy(bn_glb_e, bn_glb_b);
+
+  int x = len -1;
+  while(x > 0) {
+    x--;
+    bn_func2(0);
+    if(BN_is_bit_set(var6, x)) {
+      bn_cmplx7();
+    }
+  }
+
+  BN_copy(bn_glb1, bn_glb_c);
+  BN_mask_bits(bn_glb1, 128);
+  BN_copy(bn_glb7, bn_glb1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_clear(bn_glb7);
+  BN_set_word(bn_glb7, 1);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+  BN_copy(bn_glb0, bn_glb1);
+  BN_clear(bn_glb7);
+  BN_set_word(bn_glb7, 1);
+  BN_copy(bn_glb1, bn_glb0);
+  BN_clear(bn_glb0);
+  bn_func1(var1);
+  BN_copy(bn_glb5, bn_glb_b);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+  BN_copy(bn_glb7, bn_glb0);
+  BN_copy(bn_glb5, bn_glb_c);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+  BN_copy(bn_glb5, bn_glb_a);
+  BN_mask_bits(bn_glb5, 128);
+
+  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+  BN_clear(bn_glb5);
+  BN_set_word(bn_glb5, 1);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_a, bn_glb0);
+  BN_copy(bn_glb1, bn_glb7);
+
+  BN_clear(bn_glb5);
+  BN_set_word(bn_glb5, 1);
+
+  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+  BN_copy(bn_glb_b, bn_glb0);
+  BN_free(var48);
+}
+void cN2Map57::Map57(unsigned char *data)
+{
+  BIGNUM *var38, *var58, *var78, *var98, *varb8, *vard8;
+  BN_CTX *t;
+  unsigned char tmpdata[256];
+  unsigned char res[256];
+
+  t = BN_CTX_new();
+  t1 = BN_CTX_new();
+  BN_CTX_init(t);
+
+  glb2pow128 = BN_new();
+  BN_clear(glb2pow128);
+  BN_set_bit(glb2pow128, 128);
+  mask128 = BN_new();
+  BN_hex2bn(&mask128, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+  glb2pow64 = BN_new();
+  BN_clear(glb2pow64);
+  BN_set_bit(glb2pow64, 64);
+  mask64 = BN_new();
+  BN_hex2bn(&mask64, "FFFFFFFFFFFFFFFF");
+  
+  bn_glb0=BN_new(); BN_clear(bn_glb0);
+  bn_glb1=BN_new(); BN_clear(bn_glb1);
+  bn_glb3=BN_new(); BN_clear(bn_glb3);
+  bn_glb5=BN_new(); BN_clear(bn_glb5);
+  bn_glb6=BN_new(); BN_clear(bn_glb6);
+  bn_glb7=BN_new(); BN_clear(bn_glb7);
+
+  bn_glb_a=BN_new(); BN_clear(bn_glb_a);
+  bn_glb_b=BN_new(); BN_clear(bn_glb_b);
+  bn_glb_c=BN_new(); BN_clear(bn_glb_c);
+  bn_glb_d=BN_new(); BN_clear(bn_glb_d);
+  bn_glb_e=BN_new(); BN_clear(bn_glb_e);
+  bn_glb_f=BN_new(); BN_clear(bn_glb_f);
+  bn_glb_g=BN_new(); BN_clear(bn_glb_g);
+  bn_glb_h=BN_new(); BN_clear(bn_glb_h);
+  bn_glb_i=BN_new(); BN_clear(bn_glb_i);
+  bn_glb_j=BN_new(); BN_clear(bn_glb_j);
+  bn_glb_k=BN_new(); BN_clear(bn_glb_k);
+  bn_glb_l=BN_new(); BN_clear(bn_glb_l);
+  bn_glb_m=BN_new(); BN_clear(bn_glb_m);
+
+  var38=BN_new(); BN_clear(var38);
+  var58=BN_new(); BN_clear(var58);
+  var78=BN_new(); BN_clear(var78);
+  var98=BN_new(); BN_clear(var98);
+  varb8=BN_new(); BN_clear(varb8);
+  vard8=BN_new(); BN_clear(vard8);
+
+  memcpy(tmpdata, data, 0x60);
+  RotateBytes(tmpdata, 0x60);
+  BN_bin2bn(tmpdata, 16, var98);
+  BN_bin2bn(tmpdata+0x10, 16, var78);
+  BN_bin2bn(tmpdata+0x20, 16, var38);
+  BN_bin2bn(tmpdata+0x30, 16, vard8);
+  BN_bin2bn(tmpdata+0x40, 16, var58);
+  BN_bin2bn(tmpdata+0x50, 16, varb8);
+
+  bn_cmplx2(varb8, var58, vard8, var38, var78, var98);
+
+  memset(res, 0, 0x80);
+  unsigned int *dest = (unsigned int *)res, *src = (unsigned int *)data;
+  *dest++ = src[0x03];
+  *dest++ = src[0x02];
+  *dest++ = src[0x01];
+  *dest++ = src[0x00];
+  *dest++ = src[0x07];
+  *dest++ = src[0x06];
+  *dest++ = src[0x05];
+  *dest++ = src[0x04];
+
+  memset(tmpdata, 0, 0x20);
+  int len = BN_bn2bin(bn_glb_a, tmpdata);
+  if(len) {
+    RotateBytes(tmpdata, len);
+  }
+  src = (unsigned int *)tmpdata;
+  *dest++ = src[0x03];
+  *dest++ = src[0x02];
+  *dest++ = src[0x01];
+  *dest++ = src[0x00];
+
+  memset(tmpdata, 0, 0x20);
+  len = BN_bn2bin(bn_glb_m, tmpdata);
+  if(len) {
+    RotateBytes(tmpdata, len);
+  }
+  *dest = src[0x03];
+  dest+=4;
+
+  memset(tmpdata, 0, 0x20);
+  len = BN_bn2bin(bn_glb_b, tmpdata);
+  if(len) {
+    RotateBytes(tmpdata, len);
+  }
+  *dest++ = src[0x03];
+  *dest++ = src[0x02];
+  *dest++ = src[0x01];
+  *dest++ = src[0x00];
+
+  dest+=4;
+  src = (unsigned int *)(data+0x60);
+  *dest++ = src[0x03];
+  *dest++ = src[0x02];
+  *dest++ = src[0x01];
+  *dest++ = src[0x00];
+  *dest++ = src[0x07];
+  *dest++ = src[0x06];
+  *dest++ = src[0x05];
+  *dest++ = src[0x04];
+
+  *(unsigned int *)(data + (0<<2))= *(unsigned int *)(res + (11<<2));
+  *(unsigned int *)(data + (1<<2))= *(unsigned int *)(res + (10<<2));
+  *(unsigned int *)(data + (2<<2))= *(unsigned int *)(res + (9<<2));
+  *(unsigned int *)(data + (3<<2))= *(unsigned int *)(res + (8<<2));
+  *(unsigned int *)(data + (4<<2))= *(unsigned int *)(res + (12<<2));
+  *(unsigned int *)(data + (5<<2))= *(unsigned int *)(res + (13<<2));
+  *(unsigned int *)(data + (6<<2))= *(unsigned int *)(res + (14<<2));
+  *(unsigned int *)(data + (7<<2))= *(unsigned int *)(res + (15<<2));
+  *(unsigned int *)(data + (8<<2))= *(unsigned int *)(res + (19<<2));
+  *(unsigned int *)(data + (9<<2))= *(unsigned int *)(res + (18<<2));
+  *(unsigned int *)(data + (10<<2))= *(unsigned int *)(res + (17<<2));
+  *(unsigned int *)(data + (11<<2))= *(unsigned int *)(res + (16<<2));
+  *(unsigned int *)(data + (12<<2))= *(unsigned int *)(res + (20<<2));
+  *(unsigned int *)(data + (13<<2))= *(unsigned int *)(res + (21<<2));
+  *(unsigned int *)(data + (14<<2))= *(unsigned int *)(res + (22<<2));
+  *(unsigned int *)(data + (15<<2))= *(unsigned int *)(res + (23<<2));
+
+  BN_free(glb2pow128);
+  BN_free(mask128);
+  BN_free(glb2pow64);
+  BN_free(mask64);
+  
+  BN_free(bn_glb0);
+  BN_free(bn_glb1);
+  BN_free(bn_glb3);
+  BN_free(bn_glb5);
+  BN_free(bn_glb6);
+  BN_free(bn_glb7);
+
+  BN_free(bn_glb_a);
+  BN_free(bn_glb_b);
+  BN_free(bn_glb_c);
+  BN_free(bn_glb_d);
+  BN_free(bn_glb_e);
+  BN_free(bn_glb_f);
+  BN_free(bn_glb_g);
+  BN_free(bn_glb_h);
+  BN_free(bn_glb_i);
+  BN_free(bn_glb_j);
+  BN_free(bn_glb_k);
+  BN_free(bn_glb_l);
+  BN_free(bn_glb_m);
+
+  BN_free(var38);
+  BN_free(var58);
+  BN_free(var78);
+  BN_free(var98);
+  BN_free(varb8);
+  BN_free(vard8);
+
+  BN_CTX_free(t);
+  BN_CTX_free(t1);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.c
new file mode 100644 (file)
index 0000000..68d1cb8
--- /dev/null
@@ -0,0 +1,1634 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "system.h"
+#include "opts.h"
+#include "log-core.h"
+#include "debug.h"
+#include "cpu.h"
+#include "stdio.h"
+#include "math.h"
+
+#include "nagra.h"
+#include "nagra2.h"
+
+#define SYSTEM_NAME          "Nagra2"
+#define SYSTEM_PRI           -10
+
+// -- cN2Timer -----------------------------------------------------------------
+
+cN2Timer::cN2Timer(): cpu(0)
+{
+  cycles=intrCycles=0; ctrl=0; val=0; divisor=0; invDivisor=0; timerBugged = false; latch=0xFF;
+  delayInterrupt = 0;
+}
+
+double cN2Timer::GetDivisor()
+{
+       int prescalar = ((ctrl&0x38)>>3)+1;
+       double divisor = pow(4.0, prescalar);
+       double multiplier;
+
+       // multipliers for 0 and 2 aren't correct yet
+       switch((ctrl&0xc0)>>6) {
+       case 0: multiplier = 10.3f; break;
+       case 1: multiplier = 1.0f; break;
+       case 2: multiplier = 6.375f; break;
+       default:multiplier = 0;
+       }
+
+       return divisor * multiplier;
+}
+
+void cN2Timer::Update()
+{
+       int rlatch = latch;
+
+       if(divisor) {
+               if(timerBugged && cycles>=0x100 * divisor) {
+                       timerBugged = false;
+                       cycles -= 0x101 * divisor;
+               }
+               if(timerBugged) rlatch = 0x100;
+
+               unsigned int v = cycles * invDivisor;
+               if(ctrl&tmCONTINUOUS) {
+                       val = rlatch - (v % (rlatch+1));
+                       if(InterruptSet() && val==0) {
+                               intrCycles = cycles + (unsigned int)((val?val:latch)*divisor);
+                       }
+               } else {
+                       // one-shot
+                       if(v>=rlatch) {
+                               val = 0;
+                               if(Running() && ((cycles - 2) * invDivisor)>=rlatch)
+                                       Stop();
+                       } else
+                               val = rlatch - v;
+               }
+       } else
+               val = 0;
+}
+
+bool cN2Timer::AddCycles(unsigned int count)
+{
+       bool irq = false;
+       if(Running()) {
+               cycles += count;
+               if(!delayInterrupt && InterruptSet() && cycles>=intrCycles) {
+                       delayInterrupt = intrCycles-cycles;
+                       if(delayInterrupt < 0)
+                               delayInterrupt = count;
+                       else
+                               delayInterrupt += count;
+                       intrCycles = 0;
+                       if((ctrl&tmCONTINUOUS)) Update();
+               }
+       }
+
+       if(delayInterrupt) {
+               delayInterrupt -= count;
+               if(delayInterrupt < 0)
+                       delayInterrupt = 0;
+               if(!delayInterrupt) {
+                       Update();
+                       irq = true;
+               }
+       }
+       return irq;
+}
+
+unsigned int cN2Timer::Cycles()
+{
+       if(Running()) Update();
+       PRINTF(L_SYS_EMU,"n2timer read %u @ %02x:%04x: %u %02x %02x = %02x", id, cpu?cpu->GetCr():0, cpu?cpu->GetPc():0, cycles, ctrl&0xff, latch&0xff, val);
+       return val;
+}
+
+unsigned char cN2Timer::Ctrl()
+{
+       if(Running()) Update();
+       return ctrl;
+}
+
+void cN2Timer::Latch(unsigned char val)
+{
+  if(!Running()) {
+    latch=val; if(latch==0) latch=0x100;
+    cycles=0;
+    ctrl|=tmLATCHED;
+       timerBugged = false;
+  }
+}
+
+void cN2Timer::Ctrl(unsigned char val)
+{
+  if(Running()) {
+    ctrl=(ctrl&~tmRUNNING) | (val&tmRUNNING);
+    if(!Running()) {
+      Stop();
+         --cycles;
+         Update();
+      PRINTF(L_SYS_EMU,"n2timer %u: stopped cycles=%d (%02x) ctrl=%02x latch=%02x",id,cycles,val,ctrl,latch&0xff);
+    }
+  }
+  else {
+    ctrl=(ctrl&~tmMASK) | (val&tmMASK);
+       divisor = GetDivisor();
+       if(divisor) invDivisor = 1.0 / divisor;
+    if(Running()) {
+               if(!(ctrl&tmLATCHED) && divisor>0) {
+                       if(this->val==0) {
+                               timerBugged = true;
+                               cycles = (unsigned int)divisor;
+                       } else {
+                               cycles = (unsigned int)(((timerBugged?0x100:latch)-this->val+1) * divisor);
+                       }
+               }
+               if((ctrl&tmINTERRUPT)) {
+                       intrCycles = cycles + (unsigned int)((timerBugged?0x101:this->val?this->val:latch)*divisor);
+                       if(id==2) PRINTF(L_SYS_MAP,"Timer %d set for %u cycles (%02x %02x)", id, intrCycles-cycles, Latch(), ctrl&0xff);
+               }
+               ctrl &= ~tmLATCHED;
+       }
+  }
+}
+
+void cN2Timer::Stop(void)
+{
+  ctrl&=~(tmRUNNING|tmLATCHED);
+  if(delayInterrupt>=3)
+         delayInterrupt = 0;
+}
+
+// -- cMapMemHW ----------------------------------------------------------------
+
+//#define CRC_POLY 0x8408 // ccitt poly
+
+
+cMapMemHW::cMapMemHW(c6805& _cpu)
+:cMapMem(HW_OFFSET,HW_REGS),cpu(_cpu)
+{
+  CRCvalue=0xffff; CRCit=CRCCALC_DELAY+1; CRCpos=0; CRCtime=0;
+  GenCRC16Table();
+  for(int i=0; i<MAX_TIMERS; ++i)
+         timer[i].SetId((i+1)%3, &cpu);
+  PRINTF(L_SYS_EMU,"mapmemhw: new HW map off=%04x size=%04x",offset,size);
+
+  Set(HW_IO, 0xfe);                            // IO
+  Set(HW_CRC_CONTROL, CRC_DISABLED); // CRC disabled
+  Set(HW_TIMER0_LATCH, 0x00);  // initialize timer0 latch
+  Set(HW_TIMER1_LATCH, 0x00);  // initialize timer1 latch
+  Set(HW_TIMER2_LATCH, 0x00);  // initialize timer2 latch
+  Set(HW_TIMER0_CONTROL, 0x00);        // disable timer0
+  Set(HW_TIMER1_CONTROL, 0x00);        // disable timer1
+  Set(HW_TIMER2_CONTROL, 0x00);        // disable timer2
+}
+
+void cMapMemHW::GenCRC16Table()
+{
+       for(int i=0; i<256; ++i) {
+               unsigned short c = i;
+               for(int j=0; j<8; ++j)
+                       c = (c>>1) ^ ((c&1) ? CRC_POLY : 0);
+               CRC16table[i] = c;
+       }
+}
+
+unsigned short cMapMemHW::CRCcalculate(unsigned short crc, unsigned char val, int bits)
+{
+       if(bits>=8) return (crc>>8)^CRC16table[(crc^val)&0xff];
+       if(bits>0) crc ^= (val&(0xff>>(8-bits)));
+       for(int i=0; i<bits; ++i)
+               crc = (crc>>1) ^ ((crc&1) ? CRC_POLY : 0);
+       return crc;
+}
+
+void cMapMemHW::CRCupdate(unsigned int cycles)
+{
+       if(!(mem[HW_CRC_CONTROL-offset]&CRC_DISABLED) && CRCit<=CRCCALC_DELAY) {
+               int elapsed = cycles-CRCtime;
+               CRCtime = cycles;
+               int it = CRCit;
+               unsigned char val = mem[HW_CRC_DATA-offset];
+               if((CRCit+=elapsed)>CRCCALC_DELAY)      // > instead of >= because of CRC_BUSY lockout
+                       CRCit = CRCCALC_DELAY+1;
+               if(it==0 && CRCit>=CRCCALC_DELAY)
+                       CRCvalue = (CRCvalue>>8)^CRC16table[(CRCvalue^val)&0xff];
+               else
+                       for(int i=it; i<CRCit && i<CRCCALC_DELAY; ++i)
+                               CRCvalue = (CRCvalue>>1) ^ (((CRCvalue^(val>>i))&1) ? CRC_POLY : 0);
+       }
+}
+
+int cMapMemHW::AddCycles(unsigned int num)
+{
+  int mask=0;
+  for(int i=0; i<MAX_TIMERS; i++)
+         if(timer[i].AddCycles(num)) mask |= 1<<timer[i].Id();
+  return mask;
+}
+
+unsigned char cMapMemHW::Get(unsigned short ea)
+{
+  if(ea<offset || ea>=offset+size) return 0;
+  ea-=offset;
+  switch(ea) {
+       case HW_SECURITY:
+         return (mem[ea]&0x70)|0x0f;
+    case HW_TIMER0_CONTROL:
+    case HW_TIMER1_CONTROL:
+    case HW_TIMER2_CONTROL:
+      return timer[TIMER_NUM(ea)].Ctrl();
+    case HW_TIMER0_DATA:
+    case HW_TIMER1_DATA:
+    case HW_TIMER2_DATA:
+      return timer[TIMER_NUM(ea)].Cycles();
+    case HW_TIMER0_LATCH:
+    case HW_TIMER1_LATCH:
+    case HW_TIMER2_LATCH:
+      return timer[TIMER_NUM(ea)].Latch();
+    case HW_CRC_CONTROL:
+      {
+      unsigned char r=mem[ea];
+         CRCupdate(cpu.Cycles());
+         if(CRCit<CRCCALC_DELAY) r |= CRC_BUSY;
+         else r &= ~CRC_BUSY;
+      return r|0x80;
+      }
+    case HW_CRC_DATA:
+      {
+         CRCupdate(cpu.Cycles());
+         unsigned char r=(CRCvalue^0xffff)>>((CRCpos&1)<<3);
+      CRCpos^=1;
+      return r;
+      }
+    default:
+      return mem[ea];
+    }
+}
+
+void cMapMemHW::Set(unsigned short ea, unsigned char val)
+{
+  if(ea<offset || ea>=offset+size) return;
+  ea-=offset;
+  switch(ea) {
+    case HW_TIMER0_CONTROL:
+    case HW_TIMER1_CONTROL:
+    case HW_TIMER2_CONTROL:
+      timer[TIMER_NUM(ea)].Ctrl(val);
+      break;
+    case HW_TIMER0_LATCH:
+    case HW_TIMER1_LATCH:
+    case HW_TIMER2_LATCH:
+      timer[TIMER_NUM(ea)].Latch(val);
+      break;
+    case HW_CRC_CONTROL:
+         if((mem[ea]&CRC_DISABLED)) {
+                 if(!(val&CRC_DISABLED)) {
+               CRCvalue=0xffff; CRCpos=0; CRCtime=cpu.Cycles();
+                       if(CRCit<=CRCCALC_DELAY) {
+                               ++CRCtime;
+                               ++CRCit;
+                       }
+                 }
+         } else if((val&CRC_DISABLED)) {
+                 CRCupdate(cpu.Cycles());
+         }
+      mem[ea]=val;
+      break;
+    case HW_CRC_DATA:
+         CRCupdate(cpu.Cycles());
+      if(CRCit>CRCCALC_DELAY) {
+         if(!(mem[HW_CRC_CONTROL-offset]&CRC_DISABLED)) {
+                       mem[ea] = val;
+                       CRCit = 0;
+                       CRCtime=cpu.Cycles();
+         }
+         }
+      break;
+    default:
+      mem[ea]=val;
+      break;
+    }
+}
+
+// -- cN2Emu -------------------------------------------------------------------
+
+cN2Emu::cN2Emu(void)
+{
+  initDone=false;
+}
+
+bool cN2Emu::Init(int id, int romv)
+{
+  if(!initDone) {
+    ResetMapper();
+    char buff[256];
+    snprintf(buff,sizeof(buff),"ROM%d.bin",romv);
+    // UROM  0x00:0x4000-0x7fff
+    if(!AddMapper(new cMapRom(0x4000,buff,0x00000),0x4000,0x4000,0x00)) return false;
+    // ROM00 0x00:0x8000-0xffff
+    if(!AddMapper(new cMapRom(0x8000,buff,0x04000),0x8000,0x8000,0x00)) return false;
+    // ROM01 0x01:0x8000-0xffff
+    if(!AddMapper(new cMapRom(0x8000,buff,0x0C000),0x8000,0x8000,0x01)) return false;
+    // ROM02 0x02:0x8000-0xbfff
+    if(!AddMapper(new cMapRom(0x8000,buff,0x14000),0x8000,romv>=110?0x8000:0x4000,0x02)) return false;
+
+    snprintf(buff,sizeof(buff),"EEP%02X_%d.bin",(id>>8)&0xFF|0x01,romv);
+    // Eeprom00 0x00:0x3000-0x37ff OTP 0x80
+    //XXX if(!AddMapper(new cMapRom(0x3000,buff,0x0000),0x3000,0x0800,0x00)) return false;
+    if(!AddMapper(new cMapEeprom(0x3000,buff,128,0x0000),0x3000,0x0800,0x00)) return false;
+    // Eeprom80 0x80:0x8000-0xbfff
+    //XXX if(!AddMapper(new cMapRom(0x8000,buff,0x0800),0x8000,0x4000,0x80)) return false;
+    if(!AddMapper(new cMapEeprom(0x8000,buff,  0,0x0800),0x8000,0x4000,0x80)) return false;
+    initDone=RomInit();
+    }
+  return initDone;
+}
+
+// -- cMapReg ------------------------------------------------------------------
+
+cMapReg::cMapReg(int *_defwordsize, int _maxwordsize): wordsize(DEF_WORDSIZE),maxwordsize(_maxwordsize),
+       defwordsize(_defwordsize)
+{
+}
+
+BIGNUM* cMapReg::Value(int wsize, bool mask)
+{
+       wsize = OpWordSize(wsize);
+       if(wordsize!=wsize) {
+               Commit();
+               Reload();
+       } else if(mask) {
+               BN_mask_bits(reg, wsize*64);
+       }
+       return reg;
+}
+
+void cMapReg::ClearReg(int wsize)
+{
+       BN_rshift(reg,reg,wsize*64);
+       BN_lshift(reg,reg,wsize*64);
+}
+
+void cMapReg::ClearFullReg(int wsize)
+{
+       BN_rshift(fullreg,fullreg,wsize*64);
+       BN_lshift(fullreg,fullreg,wsize*64);
+}
+
+void cMapReg::SetTmp(BIGNUM* val, int wsize)
+{
+       if(val->neg) {
+               BN_clear(tmp);
+               BN_set_bit(tmp,wsize*64);
+               BN_add(tmp,tmp,val);
+       } else
+               BN_copy(tmp,val);
+       BN_mask_bits(tmp,wsize*64);
+}
+
+void cMapReg::Commit(int wsize, int resync)
+{
+       if(resync<0 && wsize<0) resync = 1;
+       wsize = OpWordSize(wsize>=0?wsize:wordsize);
+       ClearFullReg(wsize);
+       SetTmp(reg,wsize);
+       BN_add(fullreg, fullreg, tmp);
+       if(resync) {
+               if(wordsize==wsize) BN_mask_bits(reg, wsize*64);
+               else wordsize = wsize;
+       }
+}
+
+void cMapReg::Reload(int wsize)
+{
+       wsize = OpWordSize(wsize>=0?wsize:wordsize);
+       wordsize = wsize;
+       BN_copy(reg, fullreg);
+       BN_mask_bits(reg, 64*wsize);
+}
+
+void cMapReg::GetLE(const unsigned char *in, int n)
+{
+       int wsize = OpWordSize(n<=0?n:(n+7)/8);
+       if(wordsize>wsize) Commit();
+       reg.GetLE(in, 8*wsize);
+       Commit(wsize);
+}
+
+void cMapReg::PutLE(unsigned char* out, int n)
+{
+       int wsize = OpWordSize(n<=0?n:(n+7)/8);
+       Commit();
+       fullreg.PutLE(out, 8*wsize);
+}
+
+void cMapReg::Set(BIGNUM* val, int wsize)
+{
+       wsize = OpWordSize(wsize);
+       if(wordsize!=wsize) Commit();
+       ClearReg(wsize);
+       SetTmp(val,wsize);
+       if(wordsize!=wsize) {
+               ClearFullReg(wsize);
+               BN_add(fullreg, fullreg, tmp);
+       }
+       BN_add(reg, reg, tmp);
+}
+
+void cMapReg::Clear(int wsize)
+{
+       wsize = OpWordSize(wsize);
+       if(wordsize!=wsize) {
+               Commit();
+               ClearFullReg(wsize);
+       }
+       ClearReg(wsize);
+}
+
+// -- cMapMath -----------------------------------------------------------------
+
+cMapMath::cMapMath(): A(&wordsize),B(&wordsize),C(&wordsize),D(&wordsize),J(NULL,1),I(&wordsize)
+{
+       wordsize = 4;
+}
+bool cMapMath::ModAdd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BIGNUM *d)
+{
+       bool ret = false;
+    BN_add(r,a,b);
+       if(BN_cmp(r,d)>=0) {
+               BN_sub(r,r,d);
+               ret = true;
+       }
+    BN_mask_bits(r,wordsize<<6);
+       return ret;
+}
+
+bool cMapMath::ModSub(BIGNUM *r, BIGNUM *d, BIGNUM *b)
+{
+    cBN p;
+       bool ret = BN_cmp(d, b)<0;
+    BN_set_bit(p,wordsize<<6);
+    BN_mod_sub(r,d,b,p,ctx);
+    BN_mask_bits(r,wordsize<<6);
+       return ret;
+}
+
+void cMapMath::MakeJ0(BIGNUM *j, BIGNUM *d, BIGNUM* c, int bits)
+{
+#if OPENSSL_VERSION_NUMBER < 0x0090700fL
+#error BN_mod_inverse is probably buggy in your openssl version
+#endif
+    BN_zero(x);
+    BN_sub(j,x,d);
+       j->neg = 1;
+    BN_set_bit(j,0);
+    BN_set_bit(x,bits);
+       BN_mod_inverse(j,j,x,ctx);
+       if(c) {
+               BN_copy(c,d);
+               BN_mask_bits(c,bits);
+               BN_mul(c,j,c,ctx);
+               BN_rshift(c,c,bits);
+               BN_mask_bits(c,bits);
+       }
+}
+
+void cMapMath::MonMul0(BIGNUM *o, BIGNUM *a, BIGNUM *b, BIGNUM *c, BIGNUM *d, BIGNUM *j, int words)
+{
+       if(words<=0) words = wordsize;
+    BN_zero(s);
+       for(int i=0; --words>=0; ) {
+        BN_rshift(x,a,(i++)<<6);
+        BN_mask_bits(x,64);
+        BN_mul(x,x,b,ctx);
+        BN_add(s,s,x);
+
+        BN_copy(x,s);
+        BN_mask_bits(x,64);
+        BN_mul(x,x,j,ctx);
+        if(!words) {
+            BN_lshift(y,x,64);
+            BN_add(y,y,x);
+            // Low
+            BN_rshift(c,y,2);
+            BN_add(c,c,s);
+            BN_rshift(c,c,52);
+            BN_mask_bits(c,12);
+        }
+
+        BN_mask_bits(x,64);
+        BN_mul(x,x,d,ctx);
+        BN_add(s,s,x);
+        if(!words) {
+            // High
+            BN_lshift(y,s,12);
+            BN_add(c,c,y);
+            BN_mask_bits(c,wordsize<<6);
+        }
+
+        BN_rshift(s,s,64);
+        if(words && BN_cmp(s,d)>=0) {
+            BN_copy(x,s);
+            BN_sub(s,x,d);
+        }
+    }
+    BN_copy(o,s);
+}
+
+void cMapMath::MonMulFin(BIGNUM *o, BIGNUM *d)
+{
+       if(BN_cmp(o,d)>=0)
+               BN_sub(o,o,d);
+}
+
+void cMapMath::MonMul(BIGNUM *o, BIGNUM *a, BIGNUM *b, BIGNUM *c, BIGNUM *d, BIGNUM *j, int words)
+{
+       MonMul0(o, a, b, c, d, j, words);
+       MonMulFin(o, d);
+}
+
+
+// -- cMapCore -----------------------------------------------------------------
+
+cMapCore::cMapCore(void)
+{
+  last=1; mapid=0;
+  regs[0]=&J; regs[1]=&A; regs[2]=&B; regs[3]=&C; regs[4]=&D;
+  interruptible=false;
+}
+
+void cMapCore::ImportReg(unsigned char reg, const unsigned char *in, int l)
+{
+  int dl=(l>0?l:wordsize)<<3;
+  cBN tmp;
+  switch(reg) {
+    case IMPORT_J:
+    case IMPORT_A:
+    case IMPORT_B:
+    case IMPORT_C:
+    case IMPORT_D:
+               last = reg-IMPORT_J;
+               // fall through
+       case IMPORT_LAST:
+               regs[last]->GetLE(in+(last>0?0:dl-8),last>0?dl:8);
+               break;
+    default: PRINTF(L_SYS_MAP,"internal: nagramap import register not supported"); return;
+    }
+}
+
+void cMapCore::ExportReg(unsigned char reg, unsigned char *out, int l)
+{
+  int dl=(l>0?l:wordsize)<<3;
+  switch(reg) {
+       case EXPORT_J:
+    case EXPORT_A:
+    case EXPORT_B:
+    case EXPORT_C:
+    case EXPORT_D:
+               last = reg-EXPORT_J;
+               // fall through
+       case EXPORT_LAST:
+               regs[last]->PutLE(out,last>0?dl:8);
+               if(reg==EXPORT_LAST && last==0)
+                       for(int i=8; i<dl; i+=8) memcpy(out+i,out,8);
+               break;
+    default: PRINTF(L_SYS_MAP,"internal: nagramap export register not supported"); return;
+    }
+}
+ void cMapCore::MonInit(int bits)
+{
+  // Calculate J0 & H montgomery elements in J and B
+  MakeJ0(J,D);
+  BN_zero(I);
+  BN_set_bit(I,bits ? bits : 68*wordsize);
+  BN_mod(B,I,D,ctx);
+  for(int i=0; i<4; i++) MonMul(B,B,B);
+}
+
+void cMapCore::MonExp(BIGNUM *scalar)
+{
+  if(BN_is_zero(D)) { BN_one(A); return; }
+  BN_copy(A,B);
+  for(int i=BN_num_bits(scalar)-2; i>-1; i--) {
+    MonMul(B,B,B);
+    if(BN_is_bit_set(scalar,i)) MonMul(B,A,B);
+    }
+  BN_one(A);
+  MonMul(B,A,B);
+}
+
+void cMapCore::MonExpNeg(void)
+{
+  if(BN_is_zero(D)) { BN_set_word(A,1); return; }
+  BN_copy(e,D);
+  BN_mask_bits(e,8);           // check LSB
+  unsigned int n=BN_get_word(e);
+  BN_copy(e,D);
+  if(n) BN_sub_word(e,0x02);   // N -2
+  else  BN_add_word(e,0xFE);   // N + 254 ('carryless' -2)
+  BN_copy(A,B);
+  for(int i=BN_num_bits(e)-2; i>-1; i--) {
+    MonMul(B,B,B);
+    if(BN_is_bit_set(e,i)) MonMul(B,A,B);
+    }
+  if(BN_is_bit_set(D,0)) {
+    int i;
+    for(i=BN_num_bits(D)-2; i>0; i--) if(BN_is_bit_set(D,i)) break;
+    if(i<=0) {
+      MonMul(B,B,B);
+      MonMul(B,A,B);
+      }
+    }
+  BN_set_word(A,1);
+  MonMul(B,A,B);
+}
+
+void cMapCore::DoubleP(int temp)
+{
+  ModAdd(B,Py,Py,D);
+  MonMul(sC0,Pz,B);
+  MonMul(B,B,B);
+  MonMul(B,Px,B);
+  ModSub(sA0,D,B);
+  MonMul(B,Px,Px);
+  BN_copy(A,B);
+  ModAdd(B,B,B,D);
+  ModAdd(A,B,A,D);
+  MonMul(B,Pz,Pz);
+  MonMul(B,B,B);
+  MonMul(B,s160,B);
+  ModAdd(B,B,A,D);
+  BN_copy(A,B);
+  MonMul(B,B,B);
+  BN_copy(C,sA0);
+  ModAdd(B,C,B,D);
+  ModAdd(B,C,B,D);
+  if(temp==0) BN_copy(Px,B); else BN_copy(sA0,B);
+  ModAdd(B,C,B,D);
+  MonMul(B,A,B);
+  BN_copy(A,B);
+  MonMul(B,Py,Py);
+  ModAdd(B,B,B,D);
+  MonMul(B,B,B);
+  ModAdd(B,B,B,D);
+  ModAdd(B,B,A,D);
+  ModSub(B,D,B);
+  if(temp==0) { BN_copy(Py,B); BN_copy(Pz,sC0); }
+  else BN_copy(sA0,sC0);
+}
+
+void cMapCore::AddP(int temp)
+{
+  MonMul(B,Pz,Pz);
+  MonMul(sA0,Pz,B);
+  MonMul(B,Qx,B);
+  BN_copy(A,B);
+  ModSub(B,D,B);
+  ModAdd(B,Px,B,D);
+  BN_copy(sC0,B);
+  MonMul(I,Pz,B);
+  if(temp==0) BN_copy(Pz,I); else BN_copy(sA0,I);
+  MonMul(B,B,B);
+  BN_copy(sE0,B);
+  ModAdd(A,Px,A,D);
+  MonMul(A,A,B);
+  BN_copy(s100,A);
+  MonMul(B,sA0,Qy);
+  BN_copy(sA0,B);
+  ModSub(B,D,B);
+  ModAdd(B,Py,B,D);
+  BN_copy(s120,B);
+  MonMul(B,B,B);
+  BN_swap(A,B);
+  ModSub(B,D,B);
+  ModAdd(B,A,B,D);
+  if(temp==0) BN_copy(Px,B); else BN_copy(sA0,B);
+  ModAdd(B,B,B,D);
+  ModSub(B,D,B);
+  ModAdd(B,B,s100,D);
+  ModAdd(A,sA0,Py,D);
+  MonMul(sA0,s120,B);
+  MonMul(B,sE0,sC0);
+  MonMul(B,A,B);
+  ModSub(B,D,B);
+  ModAdd(B,B,sA0,D);
+  MonMul(B,s140,B);
+  if(temp==0) BN_copy(Py,B); else BN_copy(sA0,B);
+}
+
+void cMapCore::ToProjective(int set, BIGNUM *x, BIGNUM *y)
+{
+  if(set==0) {
+    BN_set_word(I,1); MonMul(Pz,I,B);
+    BN_copy(Qz,Pz);
+    MonMul(Px,x,B);
+    MonMul(Py,y,B);
+    }
+  else {
+    MonMul(Qx,x,B);
+    MonMul(Qy,y,B);
+    }
+}
+
+void cMapCore::ToAffine(void)
+{
+  BN_set_word(I,1);
+  MonMul(B,Pz,Pz); MonMul(B,Pz,B); MonMul(B,I,B);
+  MonExpNeg();
+  BN_set_word(I,1);
+  MonMul(A,Py,B); MonMul(Py,I,A);
+  MonMul(B,Pz,B); MonMul(B,Px,B); MonMul(Px,I,B);
+}
+
+void cMapCore::CurveInit(BIGNUM *a)
+{
+  BN_zero(Px); BN_zero(Py); BN_zero(Pz); BN_zero(Qx); BN_zero(Qy); BN_zero(Qz);
+  BN_zero(sA0); BN_zero(sC0); BN_zero(sE0); BN_zero(s100);
+  BN_zero(s120); BN_zero(s140); BN_zero(s160);
+  MonInit();
+  BN_copy(A,B);
+  BN_copy(I,D);
+  BN_add_word(I,1);
+  BN_mask_bits(I,wordsize<<6);
+  BN_rshift(I,I,1);
+  MonMul(s140,I,B);
+  MonMul(s160,B,a);
+}
+
+int cMapCore::GetOpSize(int l)
+{
+  return l!=0 ? l : wordsize;
+}
+
+void cMapCore::DoMap(int f, unsigned char *data, int l)
+{
+  PRINTF(L_SYS_MAP,"%04x: calling function %02X",mapid,f);
+  cycles=0;
+  unsigned int startcycles=CpuCycles();
+  interrupted=false; interruptible=true;
+  try {
+    if(!Map(f,data,l) && !MapGeneric(f,data,l))
+      PRINTF(L_SYS_MAP,"%04x: unsupported call %02x",mapid,f);
+    if(cycles) {
+      unsigned int elapsed=CpuCycles()-startcycles;
+      if(cycles>elapsed) AddMapCycles(cycles-elapsed);
+      }
+    } catch(int) { interrupted=true; }
+  interruptible=false;
+  cycles=CpuCycles()-startcycles;
+}
+
+bool cMapCore::MapGeneric(int f, unsigned char *data, int l)
+{
+  const int l1=GetOpSize(l);
+  const int dl=l1<<3;
+  switch(f) {
+    case SETSIZE:
+      cycles=(l>17 ? 459 : (l ? 475 : 454))-6;
+      if(l>=1 && l<=17) wordsize=l;
+      break;
+
+    case IMPORT_J:
+      cycles=890-6;
+      last=0;
+      regs[0]->GetLE(data,8);
+      break;
+    case IMPORT_A:
+    case IMPORT_B:
+    case IMPORT_C:
+    case IMPORT_D:
+      if(l>17) { l=17; cycles+=5; }
+      else if(l<=0) { l=wordsize; cycles+=4; }
+      cycles+=771+160*l-6;
+      last=f-IMPORT_J;
+      regs[last]->GetLE(data,l<<3);
+      break;
+    case IMPORT_LAST:
+      if(l>16) { l=1; cycles+=5; }
+      else if(l<=0) l=1;
+      cycles=656+160*l-6;
+      regs[last]->GetLE(data,(last==0?1:l)<<3);
+      break;
+
+    case EXPORT_J:
+      cycles=897-6;
+      last=0;
+      regs[0]->PutLE(data,8);
+      break;
+    case EXPORT_A:
+    case EXPORT_B:
+    case EXPORT_C:
+    case EXPORT_D:
+      if(l>17) { l=17; cycles+=5; }
+      else if(l<=0) { l=wordsize; cycles+=4; }
+      cycles=778+160*l-6;
+      last=f-EXPORT_J;
+      regs[last]->PutLE(data,l<<3);
+      break;
+    case EXPORT_LAST:
+      if(l>16) { l=1; cycles+=5; }
+      else if(l<=0) l=1;
+      cycles=668+160*l-6;
+      regs[last]->PutLE(data,(last==0?1:l)<<3);
+      break;
+
+    case SWAP_A:
+    case SWAP_B:
+    case SWAP_C:
+    case SWAP_D:
+      cycles=776+248*l1-6;
+      last=f-SWAP_A+1;
+      e.GetLE(data,dl);
+      regs[last]->PutLE(data,dl);
+      regs[last]->Set(e,l1);
+      break;
+
+    case CLEAR_A:
+    case CLEAR_B:
+    case CLEAR_C:
+    case CLEAR_D:
+      cycles=462+(8*l1+3)/5*5-6;
+      last=f-CLEAR_A+1;
+      regs[last]->Clear(l1);
+      break;
+
+    case COPY_A_B:
+      last=2; B.Set(A,l1); cycles=462+(8*l1+3)/5*5-6; break;
+    case COPY_B_A:
+      last=1; A.Set(B,l1); cycles=462+(8*l1+3)/5*5-6; break;
+    case COPY_A_C:
+      last=3; C.Set(A,l1); cycles=462+(8*l1+3)/5*5-6; break;
+    case COPY_C_A:
+      last=1; A.Set(C,l1); cycles=462+(8*l1+3)/5*5-6; break;
+    case COPY_C_D:
+      last=4; D.Set(C,l1); cycles=462+(8*l1+3)/5*5-6; break;
+    case COPY_D_C:
+      last=3; C.Set(D,l1); cycles=462+(8*l1+3)/5*5-6; break;
+
+    case 0x39:
+    case 0x3a:
+      AddMapCycles(512);
+      WS_START(1);
+      MakeJ0(J,D);
+      AddMapCycles(256);
+      WS_END();
+      AddMapCycles(340);
+      if(!BN_is_zero(D)) {
+        BN_zero(I);
+        BN_set_bit(I,68*wordsize);
+        BN_mod(B,I,D,ctx);
+        }
+      AddMapCycles(320);
+      for(int i=0; i<4; i++) MonMul(B,B,B);
+
+      if(f==0x39) I.GetLE(data,wordsize<<3);
+      MonMul(B,(f==0x39?I:A),B);
+      MonMul(B,A,B);
+      break;
+    case 0x43: // init SHA1
+      SHA1_Init(&sctx);
+      break;
+    case 0x44: // add 64 bytes to SHA1 buffer
+      RotateBytes(data,64);
+      SHA1_Update(&sctx,data,64);
+      BYTE4_LE(data   ,sctx.h4);
+      BYTE4_LE(data+4 ,sctx.h3);
+      BYTE4_LE(data+8 ,sctx.h2);
+      BYTE4_LE(data+12,sctx.h1);
+      BYTE4_LE(data+16,sctx.h0);
+      break;
+    case 0x45: // add wordsize bytes to SHA1 buffer and finalize SHA result
+      if(dl) {
+        if(dl>1) RotateBytes(data,dl);
+        SHA1_Update(&sctx,data,dl);
+        }
+      SHA1_Final(data,&sctx);
+      RotateBytes(data,20);
+      break;
+    default:
+      return false;
+    }
+  return true;
+}
+
+// -- cN2Prov ------------------------------------------------------------------
+
+cN2Prov::cN2Prov(int Id, int Flags)
+{
+//  keyValid=false; id=Id|0x100; flags=Flags; seedSize=5;
+       id=Id|0x100; flags=Flags;
+  memset(lastcw, 0, sizeof(lastcw));
+  memset(prevcw, 0, sizeof(prevcw));
+  memset(lasthd, 0, sizeof(lasthd));
+}
+
+void cN2Prov::PrintCaps(int c)
+{
+  PRINTF(c,"provider %04x capabilities%s%s%s%s%s",id,
+           HasFlags(N2FLAG_MECM)    ?" MECM":"",
+           HasFlags(N2FLAG_Bx)      ?" Bx":"",
+           HasFlags(N2FLAG_Ex)      ?" Ex":"",
+           HasFlags(N2FLAG_POSTAU)  ?" POSTPROCAU":"",
+           HasFlags(N2FLAG_INV)     ?" INVCW":"");
+}
+
+void cN2Prov::ExpandInput(unsigned char *hw)
+{
+  hw[0]^=(0xDE +(0xDE<<1)) & 0xFF;
+  hw[1]^=(hw[0]+(0xDE<<1)) & 0xFF;
+  for(int i=2; i<128; i++) hw[i]^=hw[i-2]+hw[i-1];
+  IdeaKS ks;
+  idea.SetEncKey((unsigned char *)"NagraVision S.A.",&ks);
+  unsigned char buf[8];
+  memset(buf,0,8);
+  for(int i=0; i<128; i+=8) {
+    xxor(buf,8,buf,&hw[i]);
+    idea.Encrypt(buf,8,buf,&ks,0);
+    xxor(buf,8,buf,&hw[i]);
+    memcpy(&hw[i],buf,8);
+    }
+}
+
+bool cN2Prov::MECM(unsigned char in15, int algo, const unsigned char *ed, unsigned char *cw)
+{
+  unsigned char hd[32], hw[128+64], buf[20];
+  int rv;
+  hd[0]=in15&0x7F;
+  hd[1]=cw[14];
+  hd[2]=cw[15];
+  hd[3]=cw[6];
+  hd[4]=cw[7];
+
+  DynamicHD(hd,ed);
+  hd[seedSize] = algo;
+
+  if(keyValid && !memcmp(seed,hd,seedSize+1)) {        // key cached
+    memcpy(buf,cwkey,8);
+    rv = MARV_SUCCESS;
+  }
+  else {                               // key not cached
+    memset(hw,0,sizeof(hw));
+    if((rv=Algo(algo,hd,ed,hw,cw))==MARV_ERROR) return false;
+    memcpy(&hw[128],hw,64);
+    RotateBytes(&hw[64],128);
+    SHA1(&hw[64],128,buf);
+    RotateBytes(buf,20);
+
+    if(rv==MARV_SUCCESS) {
+      memcpy(seed,hd,seedSize+1);
+      memcpy(cwkey,buf,8);
+      keyValid=true;
+    }
+  }
+
+  memcpy(prevcw, cw, sizeof(prevcw));
+  memcpy(lasthd, hd, seedSize+1);
+
+  memcpy(&buf[8],buf,8);
+  IdeaKS ks;
+  idea.SetEncKey(buf,&ks);
+  memcpy(&buf[0],&cw[8],6);
+  memcpy(&buf[6],&cw[0],6);
+  idea.Encrypt(&buf[4],8,&buf[4],&ks,0);
+  idea.Encrypt(buf,8,buf,&ks,0);
+
+  memcpy(&cw[ 0],&buf[6],3);
+  memcpy(&cw[ 4],&buf[9],3);
+  memcpy(&cw[ 8],&buf[0],3);
+  memcpy(&cw[12],&buf[3],3);
+  for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
+
+  if(rv==MARV_DESYNCED)
+    lastcw[16] = 0;
+  else if(rv>=MARV_SUCCESS) {
+    if(rv==MARV_SUCCESS) lastcw[16] = 1;
+    memcpy(lastcw, cw, 16);
+  }
+
+  return true;
+}
+
+void cN2Prov::SwapCW(unsigned char *cw)
+{
+  if(NeedsCwSwap()) {
+    unsigned char tt[8];
+    memcpy(&tt[0],&cw[0],8);
+    memcpy(&cw[0],&cw[8],8);
+    memcpy(&cw[8],&tt[0],8);
+    }
+}
+
+// -- cN2Providers -------------------------------------------------------------
+
+cN2ProvLink *cN2Providers::first=0;
+
+void cN2Providers::Register(cN2ProvLink *plink)
+{
+  PRINTF(L_CORE_DYN,"n2providers: registering prov %04X with flags %d",plink->id,plink->flags);
+  plink->next=first;
+  first=plink;
+}
+
+cN2Prov *cN2Providers::GetProv(int Id, int Flags)
+{
+  cN2ProvLink *pl=first;
+  while(pl) {
+    if(pl->CanHandle(Id) && pl->HasFlags(Flags)) return pl->Create();
+    pl=pl->next;
+    }
+  return 0;
+}
+
+// -- cN2ProvLink --------------------------------------------------------------
+
+cN2ProvLink::cN2ProvLink(int Id, int Flags)
+{
+  id=Id; flags=Flags;
+  cN2Providers::Register(this);
+}
+
+// -- cNagra2 ------------------------------------------------------------------
+
+class cNagra2 : public cNagra {
+private:
+  bool Signature(const unsigned char *vkey, const unsigned char *sig, const unsigned char *msg, int len);
+protected:
+  cIDEA idea;
+  //
+  virtual void CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q);
+  bool DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m);
+  bool DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m);
+  };
+
+void cNagra2::CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q)
+{
+  // Calculate P and Q from PK
+  IdeaKS ks;
+  idea.SetEncKey(key,&ks);
+  // expand IDEA-G key
+  unsigned char idata[96];
+  for(int i=11; i>=0; i--) {
+    unsigned char *d=&idata[i*8];
+    memcpy(d,&key[13],8);
+    *d^=i;
+    idea.Decrypt(d,8,&ks,0);
+    xxor(d,8,d,&key[13]);
+    *d^=i;
+    }
+  // Calculate P
+  idata[0] |= 0x80;
+  idata[47] |= 1;
+  BN_bin2bn(idata,48,p);
+  BN_add_word(p,(key[21] << 5 ) | ((key[22] & 0xf0) >> 3));
+  // Calculate Q
+  idata[48] |= 0x80;
+  idata[95] |= 1;
+  BN_bin2bn(idata+48,48,q);
+  BN_add_word(q,((key[22]&0xf)<<9) | (key[23]<<1));
+}
+
+bool cNagra2::Signature(const unsigned char *vkey, const unsigned char *sig, const unsigned char *msg, int len)
+{
+  unsigned char buff[16];
+  memcpy(buff,vkey,sizeof(buff));
+  for(int i=0; i<len; i+=8) {
+    IdeaKS ks;
+    idea.SetEncKey(buff,&ks);
+    memcpy(buff,buff+8,8);
+    idea.Encrypt(msg+i,8,buff+8,&ks,0);
+    xxor(&buff[8],8,&buff[8],msg+i);
+    }
+  buff[8]&=0x7F;
+  return (memcmp(sig,buff+8,8)==0);
+}
+
+bool cNagra2::DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m)
+{
+  int sign=in[0] & 0x80;
+  if(rsa.RSA(out,in+1,64,pubExp,m)<=0) {
+    PRINTF(L_SYS_CRYPTO,"first RSA failed (ECM)");
+    return false;
+    }
+  out[63]|=sign; // sign adjustment
+  if(len>64) memcpy(out+64,in+65,len-64);
+
+  if(in[0]&0x04) {
+    unsigned char tmp[8];
+    DES_key_schedule ks1, ks2; 
+    RotateBytes(tmp,&key[0],8);
+    DES_key_sched((DES_cblock *)tmp,&ks1);
+    RotateBytes(tmp,&key[8],8);
+    DES_key_sched((DES_cblock *)tmp,&ks2);
+    memset(tmp,0,sizeof(tmp));
+    for(int i=7; i>=0; i--) RotateBytes(out+8*i,8);
+    DES_ede2_cbc_encrypt(out,out,len,&ks1,&ks2,(DES_cblock *)tmp,DES_DECRYPT);
+    for(int i=7; i>=0; i--) RotateBytes(out+8*i,8);
+    } 
+  else idea.Decrypt(out,len,key,0); 
+
+  RotateBytes(out,64);
+  if(rsa.RSA(out,out,64,pubExp,m,false)<=0) {
+    PRINTF(L_SYS_CRYPTO,"second RSA failed (ECM)");
+    return false;
+    }
+  if(vkey && !Signature(vkey,out,out+8,len-8)) {
+    PRINTF(L_SYS_CRYPTO,"signature failed (ECM)");
+    return false;
+    }
+  return true;
+}
+
+bool cNagra2::DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m)
+{
+  int sign=in[0]&0x80;
+  if(rsa.RSA(out,in+1,96,pubExp,m)<=0) {
+    PRINTF(L_SYS_CRYPTO,"first RSA failed (EMM)");
+    return false;
+    }
+  out[95]|=sign; // sign adjustment
+  cBN exp;
+  if(in[0]&0x08) {
+    // standard IDEA decrypt
+    if(len>96) memcpy(out+96,in+97,len-96);
+    idea.Decrypt(out,len,key,0);
+    BN_set_word(exp,3);
+    }
+  else {
+    // private RSA key expansion
+    CreateRSAPair(key,0,exp,m);
+    }
+  RotateBytes(out,96);
+  if(rsa.RSA(out,out,96,exp,m,false)<=0) {
+    PRINTF(L_SYS_CRYPTO,"second RSA failed (EMM)");
+    return false;
+    }
+  if(vkey && !Signature(vkey,out,out+8,len-8)) {
+    PRINTF(L_SYS_CRYPTO,"signature failed (EMM)");
+    return false;
+    }
+  return true;
+}
+
+// -- cSystemNagra2 ------------------------------------------------------------
+
+static int dropEMMs=1;
+
+class cSystemNagra2 : public cSystem, protected cNagra2 {
+private:
+  int lastEcmId, lastEmmId;
+  cN2Prov *ecmP, *emmP;
+public:
+  cSystemNagra2(void);
+  ~cSystemNagra2();
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemNagra2::cSystemNagra2(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  hasLogger=true;
+  lastEcmId=lastEmmId=0; ecmP=emmP=0;
+}
+
+cSystemNagra2::~cSystemNagra2()
+{
+  delete ecmP;
+  delete emmP;
+}
+
+bool cSystemNagra2::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+#define NA_SOURCE_START 0x8267 // cSource::FromString("S61.5W");
+#define NA_SOURCE_END   0x85c8 // cSource::FromString("S148W");
+  unsigned char odata[15];
+  memcpy(odata,data,sizeof(odata));
+  if(ecm->source>=NA_SOURCE_START && ecm->source<=NA_SOURCE_END) {
+    switch(ecm->source) { 
+      case 0x8267: // S61.5W 
+      case 0x82d0: // S72.0W 
+      case 0x8302: // S77.0W 
+      case 0x844c: // S110.0W 
+      case 0x849c: // S118.0W 
+      case 0x84a6: // S119.0W 
+      case 0x841a: // S105.0W 
+      case 0x84ba: // S121.0W 
+      case 0x850a: // S129.0W 
+      case 0x85c8: // S148.0W 
+        data[5]=0x01; 
+        break; 
+      case 0x8334: // S82.0W 
+      case 0x838e: // S91.0W 
+        data[5]=0x09; 
+        break; 
+      case 0x83ca: // S97.0W 
+        data[5]=0xc1; 
+        break; 
+      default: 
+        if (ecm->caId==0x1234) 
+          data[5]=0x09; 
+        else 
+          data[5]=0x01; 
+    } 
+    data[6]&=0x1F;      
+    data[7]=data[7]&0x10|0x86;
+    data[8]=0;
+    data[9]=data[9]&0x80|0x08;
+  } 
+  int cmdLen=data[4]-5;
+  int id=(data[5]*256)+data[6];
+  cTimeMs minTime;
+  int provider=id;
+  if(id==0x4101) StartLog(ecm,0x1881); // D+ AU
+  if(id==0x0505 || id==0x0503 || id==0x0511) id=0x0501; // PremStar  ugly again :(
+  if (id >= 0x0101 && id <= 0x01FF) {
+       provider = 0x0101;
+  } else if (id >= 0x0901 && id <= 0x09FF) {
+       provider = 0x0901;
+  } else {
+       provider = id;
+  }
+
+  if(cmdLen<64 || SCT_LEN(data)<cmdLen+10) {
+    if(doLog) PRINTF(L_SYS_ECM,"bad ECM message msgLen=%d sctLen=%d",cmdLen,SCT_LEN(data));
+    return false;
+    }
+
+  int keyNr=(data[7]&0x10)>>4;
+  cKeySnoop ks(this,'N',provider,keyNr);
+  cPlainKey *pk;
+
+  cBN m1;
+  unsigned char ideaKey[16], vKey[16];
+  bool hasVerifyKey=false;
+  if(!(pk=keys.FindKey('N',provider,MBC('M','1'),-1)))  {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %04x M1 key",id);
+    return false;
+    }
+  pk->Get(m1);
+  if((pk=keys.FindKeyNoTrig('N',provider,'V',sizeof(vKey)))) {
+    pk->Get(vKey);
+    hasVerifyKey=true;
+    }
+  else if(doLog && id!=lastEcmId) PRINTF(L_SYS_KEY,"missing %04x V key (non-fatal)",id);
+  if(!(pk=keys.FindKey('N',provider,keyNr,sizeof(ideaKey)))) return false;
+  pk->Get(ideaKey);
+
+  unsigned char buff[256];
+  if(!DecryptECM(data+9,buff,ideaKey,cmdLen,hasVerifyKey?vKey:0,m1)) {
+    if(doLog) PRINTF(L_SYS_ECM,"decrypt of ECM failed (%04x)",id);
+    return false;
+    }
+
+  if((!ecmP && id!=lastEcmId) || (ecmP && !ecmP->CanHandle(id))) {
+    delete ecmP;
+    ecmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+    if(ecmP) ecmP->PrintCaps(L_SYS_ECM);
+    }
+  lastEcmId=id;
+
+  HEXDUMP(L_SYS_RAWECM,buff,cmdLen,"Nagra2 RAWECM");
+  int l=0, mecmAlgo=0;
+  LBSTARTF(L_SYS_ECM);
+  bool contFail=false;
+  for(int i=(buff[14]&0x10)?16:20; i<cmdLen-10 && l!=3; ) {
+    switch(buff[i]) {
+      case 0x10:
+      case 0x11:
+        if(buff[i+1]==0x09) {
+          int s=(~buff[i])&1;
+          mecmAlgo=buff[i+2]&0x60;
+          memcpy(cw+(s<<3),&buff[i+3],8);
+          i+=11; l|=(s+1);
+          }
+        else {
+          PRINTF(L_SYS_ECM,"bad length %d in CW nano %02x",buff[i+1],buff[i]);
+          i++;
+          }
+        break;
+      case 0x00:
+        i+=2; break;
+      case 0x30:
+      case 0x31:
+      case 0x32:
+      case 0x33:
+      case 0x34:
+      case 0x35:
+      case 0x36:
+      case 0xB0:
+        i+=buff[i+1]+2;
+        break;
+      default:
+        if(!contFail) LBPUT("unknown ECM nano");
+        LBPUT(" %02x",buff[i]);
+        contFail=true;
+        i++;
+        continue;
+      }
+    LBFLUSH(); contFail=false;
+    }
+  LBEND();
+  if(l!=3) return false;
+  if(mecmAlgo>0) {
+    if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+      if(!ecmP->MECM(buff[15],mecmAlgo,odata,cw)) return false;
+      }
+    else { PRINTF(L_SYS_ECM,"MECM for provider %04x not supported",id); return false; }
+    }
+  if(ecmP) ecmP->SwapCW(cw);
+  ks.OK(pk);
+
+  int i=minEcmTime-minTime.Elapsed();
+  if(i>0) cCondWait::SleepMs(i);
+  return true;
+}
+
+void cSystemNagra2::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+   int cmdLen=buffer[9]-5;
+   int id=buffer[10]*256+buffer[11];
+   char txt[150];
+
+  if(buffer[0]==0x83 && dropEMMs) return; // skip EMM-S if disabled
+   if(cmdLen<96 || SCT_LEN(buffer)<cmdLen+15) {
+       PRINTF(L_SYS_EMM,"Bad EMM message msgLen=%d sctLen=%d",cmdLen,SCT_LEN(buffer));
+       return;
+    }
+   char time_str[20];
+   char text[100];
+   int keyset=(buffer[12]&0x03);
+   int sel=(buffer[12]&0x10)<<2;
+   int sigsel=(buffer[13]&0x80)>>1;
+       int CamID = 0;
+       int provider=id;
+       if (id >= 0x0101 && id <= 0x010F) {
+               provider = 0x0101;
+       } else if (id >= 0x0901 && id <= 0x090F) {
+               provider = 0x0901;
+       } else {
+               provider = id;
+       }
+  if((!emmP && id!=lastEmmId) || (emmP && !emmP->CanHandle(id))) {
+    delete emmP;
+    emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+//    if(emmP) emmP->PrintCaps(L_SYS_EMM);
+    }
+  lastEmmId=id;
+
+  //HEXDUMP(L_SYS_RAWEMM,buffer+14,cmdLen,"Nagra2 Encrypted EMM");
+  //id=(emmdata[8]<<8)+emmdata[9];
+  //LBSTARTF(L_SYS_EMM);
+       int keyPTR = N2_MAGIC;
+       if (buffer[0] == 0x83) {
+               keyPTR = 0;
+               for (int it=5; it>2; it--) {
+                       CamID = MBC(CamID,buffer[it]);
+               }
+               //keyPTR=CID(N2_EMM_S,buffer[5],buffer[4],buffer[3]);
+       }       
+
+
+  cPlainKey *pk;
+  cBN n;
+  unsigned char ideaKey[24], vKey[16];
+  bool hasVerifyKey=false;
+       if(!(pk=keys.FindKey('N',provider,MBC(keyPTR,keyset+0x10+sel),96,0)))  
+               {
+               PRINTF(L_SYS_EMM,"You are missing %04x NN %.02X RSA key (96 bytes)",id,keyset+0x10+sel);
+               return;
+    }
+  pk->Get(n);
+       if (buffer[0] != 0x83) {
+           if((pk=keys.FindKey('N',provider,MBC(keyPTR,0x03+sigsel),sizeof(vKey)))) {
+                       pk->Get(vKey);
+                       hasVerifyKey=true;
+               }
+               else if(id!=lastEmmId) {
+                       PRINTF(L_SYS_EMM,"You are missing %04x NN %.02X signature key (non-fatal)",id,0x03+sigsel);
+               }
+       }
+       if(!(pk=keys.FindKey('N',provider,MBC(keyPTR,keyset),24,0))) {
+           if(!(pk=keys.FindKey('N',provider,MBC(keyPTR,keyset+sel),16))) {
+                       PRINTF(L_SYS_EMM,"You are missing %04x NN %.02x IDEA key (24 or 16 bytes)",id,keyset);
+                       return;
+               }
+               memset(ideaKey+16,0,8);
+       }
+  pk->Get(ideaKey);
+
+  unsigned char emmdata[256];
+       if(!DecryptEMM(buffer+14,emmdata,ideaKey,cmdLen,hasVerifyKey?vKey:0,n)) {
+               sprintf(txt,"[%s] nagra2: decrypt of EMM failed (%04x)", time_str, id);
+               if (buffer[0] == 0x82) {
+                       PRINTF(L_SYS_EMM,"Bad EMM-G Packet (Idea Hash failed! -> RSA decryption failed!): %04x", id);
+               } else if (buffer[0] == 0x83) {
+                       PRINTF(L_SYS_EMM,"EMM-S {%06X%02X} (Idea Hash failed! -> RSA decryption failed!)",CamID,buffer[6]);
+               }
+               return;
+       } else {
+               if (buffer[0] == 0x82) {
+                       PRINTF(L_SYS_EMM,"Decrypt of EMM-G Succeeded (%04x)", id);
+                       char str2[51];  // 4AFFB6B0C6C52F7C0801144F8C9F18717787E3
+                       sprintf(&str2[0],"%02X ",emmdata[18]);
+                       for(int it=1; it<20; it++) sprintf(&str2[(it*2)+1],"%02X",emmdata[it+18]);
+                       PRINTF(L_SYS_EMM,"Decrypt of EMM-S Succeeded (%04x)", id);
+               }
+       }
+       lastEmmId=id;
+  if((!emmP && id!=lastEmmId) || (emmP && !emmP->CanHandle(id))) {
+    delete emmP;
+    emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+    if(emmP) emmP->PrintCaps(L_SYS_EMM);
+    }
+  lastEmmId=id;
+
+  HEXDUMP(L_SYS_RAWEMM,buffer+14,cmdLen,"Nagra2 Encrypted EMM");
+  id=(emmdata[8]<<8)+emmdata[9];
+  LBSTARTF(L_SYS_EMM);
+  bool contFail=false;
+
+       if (buffer[0] == 0x82) {
+               id=(emmdata[8]<<8)+emmdata[9];
+       }
+
+  for(int i=8+2+4+4; i<cmdLen-22; ) {
+    switch(emmdata[i]) {
+      case 0x42: // plain Key update
+        if((((emmdata[i+3]|0xF3)+1)&0xFF) != 0) {
+          int len=emmdata[i+2];
+          int off=emmdata[i+5];
+          int ulen=emmdata[i+6];
+          if(len>0 && ulen>0 && off+ulen<=len) {
+            int ks=emmdata[i+3], kn;
+            if(ks==0x06 || ks==0x46) kn=(ks>>6)&1; else kn=MBC(N2_MAGIC,ks);
+            unsigned char key[256];
+            memset(key,0,sizeof(key));
+            if((pk=keys.FindKeyNoTrig('N',id,kn,len))) {
+              if(cPlainKeyNagra::IsBNKey(kn)) { pk->Get(n); n.PutLE(key,len); }
+              else pk->Get(key);
+              }
+            bool ok=false;
+            if((emmdata[i+1]&0x7F)==0) ok=true;
+            else {
+              if(emmP && emmP->HasFlags(N2FLAG_POSTAU)) {
+                if(emmP->PostProcAU(id,&emmdata[i])) ok=true;
+                }
+              else PRINTF(L_SYS_EMM,"POSTAU for provider %04x not supported",id);
+              }
+            if(ok) {
+              memcpy(&key[off],&emmdata[i+7],ulen);
+              FoundKey();
+                                       for(int it=7 ; it<23 ; it++) sprintf(&text[(it-7)*2],"%02X",emmdata[i+it]);
+                                       PRINTF(L_SYS_EMM,"Key %02X: %s  (%04X)",(emmdata[i+3]&0x40)>>6,text,id);
+              if(cPlainKeyNagra::IsBNKey(kn)) {
+                n.GetLE(key,len);
+                if(keys.NewKey('N',id,kn,n,len)) NewKey();
+                }
+              else {
+                 if(keys.NewKey('N',id,kn,key,len)) {
+                       NewKey();
+                 }
+                }
+              }
+            i+=ulen;
+            }
+          else PRINTF(L_SYS_EMM,"nano42 key size mismatch len=%d off=%d ulen=%d",len,off,ulen);
+          }
+        else PRINTF(L_SYS_EMM,"nano42 0xf3 status exceeded");
+        i+=7;
+        break;
+      case 0xE0: // DN key update
+        if(emmdata[i+1]==0x25) {
+          FoundKey();
+          PRINTF(L_SYS_EMM,"Key packet for %04x",id);
+          if(keys.NewKey('N',id,(emmdata[i+16]&0x40)>>6,&emmdata[i+23],16)) NewKey();
+          }
+        i+=emmdata[i+1]+2;
+        break;
+      case 0x83: // change data prov. id
+        id=(emmdata[i+1]<<8)|emmdata[i+2];
+        i+=3;
+        break;
+               //CamID target update
+         case 0xA0:
+                   PRINTF(L_SYS_EMM, "EMM-S CAM Id Update");
+               i+=5;
+                   break;
+         case 0xB0: case 0xB1: case 0xB2: case 0xB3: // Update with ROM CPU code
+      case 0xB4: case 0xB5: case 0xB6: case 0xB7:
+      case 0xB8: case 0xB9: case 0xBA: case 0xBB:
+      case 0xBC: case 0xBD: case 0xBE: case 0xBF:
+        {
+        int bx=emmdata[i]&15;
+        if(!emmP || !emmP->HasFlags(N2FLAG_Bx)) {
+          PRINTF(L_SYS_EMM,"B%X for provider %04x not supported",bx,id);
+          i=cmdLen;
+          break;
+          }
+        int r;
+               if((r=emmP->ProcessBx(emmdata,cmdLen,i+1))>0 || (r==0 && emmdata[i]!=0xb1)){
+          PRINTF(L_SYS_EMM,"B%X executing succeeded for %04x",bx,id);
+          i+=r;
+                 } 
+               else {
+                 PRINTF(L_SYS_EMM,"B%X executing failed for %04x",bx,id);
+          i=cmdLen;
+          }
+        break;
+        }
+      case 0xA4: i+=emmdata[i+1]+2+4; break;   // conditional (always no match assumed for now)
+      case 0xA6: i+=15; break;
+      case 0xAA: i+=emmdata[i+1]+5; break;
+      case 0xAD: i+=emmdata[i+1]+2; break;
+      case 0xA2:
+      case 0xAE: i+=11;break;
+      case 0x12: i+=emmdata[i+1]+2; PRINTF(L_SYS_EMM,"Create tier packet"); break; // create tier                      
+      case 0x20: i+=19; PRINTF(L_SYS_EMM,"Modify tier packet"); break;   // modify tier
+      case 0x9F: i+=6; break;
+      case 0xE3:                                // Eeprom update
+        {
+        int ex=emmdata[i]&15;
+        if(!emmP || !emmP->HasFlags(N2FLAG_Ex)) {
+          i+=emmdata[i+4]+5;
+          PRINTF(L_SYS_EMM,"E%X for provider %04x not supported",ex,id);
+          break;
+          }
+        int r;
+               if((r=emmP->ProcessEx(emmdata,cmdLen,i+1))>0){
+          PRINTF(L_SYS_EMM,"E%X for provider %04x succeeded",ex,id);
+          i+=r;
+                 } 
+               else {
+                 PRINTF(L_SYS_EMM,"E%X executing failed for %04x",ex,id);
+          i=cmdLen;
+          }
+        break;
+        }
+      case 0xE1:
+      case 0xE2:
+      case 0x00: i=cmdLen; break;              // end of processing
+      default:
+        if(!contFail) LBPUT("unknown EMM nano");
+        LBPUT(" %02x",emmdata[i]);
+        contFail=true;
+        i++;
+        continue;
+      }
+    LBFLUSH(); contFail=false;
+    }
+  LBEND();
+}
+// -- cSystemLinkNagra2 --------------------------------------------------------
+
+class cSystemLinkNagra2 : public cSystemLink {
+public:
+  cSystemLinkNagra2(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemNagra2; }
+  };
+
+static cSystemLinkNagra2 staticInitN2;
+
+cSystemLinkNagra2::cSystemLinkNagra2(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  opts=new cOpts(SYSTEM_NAME,5);
+  opts->Add(new cOptBool("DropEMMS",trNOOP("Nagra2: drop EMM-S packets"),&dropEMMs));
+#ifdef HAS_AUXSRV
+  static const char allowed_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz-.";
+  opts->Add(new cOptBool("AuxServerEnable",trNOOP("Nagra2: Enable AUXserver"),&auxEnabled));
+  opts->Add(new cOptStr("AuxServerAddr",trNOOP("Nagra2: AUXserver hostname"),auxAddr,sizeof(auxAddr),allowed_chars));
+  opts->Add(new cOptInt("AuxServerPort",trNOOP("Nagra2: AUXserver port"),&auxPort,0,65535));
+  opts->Add(new cOptStr("AuxServerPass",trNOOP("Nagra2: AUXserver password"),auxPassword,sizeof(auxPassword),allowed_chars));
+#endif
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkNagra2::CanHandle(unsigned short SysId)
+{
+  return ((SysId&SYSTEM_MASK)==SYSTEM_NAGRA && (SysId&0xFF)>0) ||
+          SysId==SYSTEM_NAGRA_BEV;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/nagra/nagra2.h
new file mode 100644 (file)
index 0000000..1e85235
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __NAGRA_NAGRA2_H
+#define __NAGRA_NAGRA2_H
+
+#include "data.h"
+#include "crypto.h"
+#include "misc.h"
+#include "helper.h"
+
+#include <openssl/des.h>
+#include <openssl/sha.h>
+#include "openssl-compat.h"
+
+#include "nagra-def.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+// ----------------------------------------------------------------
+
+#define MATCH_ID(x,y) ((((x)^(y))&~0x107)==0)
+
+// ----------------------------------------------------------------
+
+#define HAS_AUXSRV
+
+#ifdef HAS_AUXSRV
+extern int auxEnabled;
+extern int auxPort;
+extern char auxAddr[80];
+extern char auxPassword[250];
+#endif
+
+// ----------------------------------------------------------------
+
+class cMapReg  {
+protected:
+  enum { DEF_WORDSIZE = 4, DEF_MAXWORDSIZE = 17 };
+  cBN fullreg, reg, tmp;
+  int wordsize, maxwordsize, *defwordsize;
+  int DefWordSize() const { return defwordsize ? *defwordsize : DEF_WORDSIZE; }
+  int OpWordSize(int wsize) const { int sz = wsize>0 ? wsize : DefWordSize(); return sz>maxwordsize ? maxwordsize : sz; }
+  void ClearReg(int wsize);
+  void ClearFullReg(int wsize);
+  void SetTmp(BIGNUM* val, int wsize);
+protected:
+  void SetMaxWordSize(int max) { maxwordsize = max; }
+  void SetDefWordSize(int* _defwordsize) { defwordsize = _defwordsize; }
+public:
+  cMapReg(int* defwordsize=NULL, int maxwordsize=DEF_MAXWORDSIZE);
+  operator BIGNUM* () { return Value(); }
+  BIGNUM *operator->() { return Value(); }
+  BIGNUM* Value(int wsize=0, bool mask=false);
+  int OpWordSize() const { return wordsize; }
+
+  void Commit(int wsize=-1, int resync=-1);
+  void Reload(int wsize=0);
+  void GetLE(const unsigned char *in, int n=0);
+  void PutLE(unsigned char *out, int n=0);
+  void Set(BIGNUM* val, int wsize);
+  void Clear(int wsize);
+};
+
+
+
+// ----------------------------------------------------------------
+
+#define WS_START(x) { int __oldws=wordsize; wordsize=(x);
+#define WS_END()    wordsize=__oldws; }
+
+class cMapMath {
+private:
+  cBN x, y, s;
+protected:
+  int wordsize;
+  cMapReg A, B, C, D, J, I;
+  cBNctx ctx;
+  SHA_CTX sctx;
+  // stateless
+#if 0
+  void ClearReg(BIGNUM* reg, int size=0);
+  void SetReg(BIGNUM* reg, BIGNUM* in, int size=0);
+  void SetReg(BIGNUM* reg, unsigned int val, int size=0);
+  void SetRestoreReg(BIGNUM* reg, BIGNUM* restorereg, int size=0);
+  void LoadReg(BIGNUM* out, BIGNUM* reg, int size=0);
+  void LoadSaveReg(BIGNUM* reg, BIGNUM* savereg, int size=0);
+#endif
+
+  void MakeJ0(BIGNUM *j, BIGNUM *d, BIGNUM *c=NULL, int bits=64);
+  void MakeJ() { MakeJ0(J, D, C); }
+  bool ModAdd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BIGNUM *d);
+  bool ModSub(BIGNUM *r, BIGNUM *d, BIGNUM *b);
+  void MonMul0(BIGNUM *o, BIGNUM *a, BIGNUM *b, BIGNUM *c, BIGNUM *d, BIGNUM *j, int words=0);
+  void MonMulFin(BIGNUM *o, BIGNUM *d);
+  void MonMul(BIGNUM *o, BIGNUM *a, BIGNUM *b, BIGNUM *c, BIGNUM *d, BIGNUM *j, int words=0);
+  // statefull
+  void MonMul(BIGNUM *o, BIGNUM *a, BIGNUM *b, int words=0) { MonMul(o, a, b, C, D, J, words); }
+public:
+  cMapMath(void);
+  };
+
+// ----------------------------------------------------------------
+
+#define SETSIZE     0x02
+#define IMPORT_J    0x03
+#define IMPORT_A    0x04
+#define IMPORT_B    0x05
+#define IMPORT_C    0x06
+#define IMPORT_D    0x07
+#define IMPORT_LAST 0x08
+#define EXPORT_J    0x09
+#define EXPORT_A    0x0A
+#define EXPORT_B    0x0B
+#define EXPORT_C    0x0C
+#define EXPORT_D    0x0D
+#define EXPORT_LAST 0x0E
+#define SWAP_A      0x0F
+#define SWAP_B      0x10
+#define SWAP_C      0x11
+#define SWAP_D      0x12
+#define CLEAR_A     0x13
+#define CLEAR_B     0x14
+#define CLEAR_C     0x15
+#define CLEAR_D     0x16
+#define COPY_A_B    0x17
+#define COPY_B_A    0x18
+#define COPY_A_C    0x19
+#define COPY_C_A    0x1A
+#define COPY_C_D    0x1B
+#define COPY_D_C    0x1C
+
+class cMapCore : public cMapMath {
+private:
+  int mapid, last;
+  cBN x, y, s, j;
+  cBN e;
+  cMapReg *regs[5];
+  c6805* cpu;
+  bool interruptible, interrupted;
+  //
+  bool MapGeneric(int f, unsigned char *data, int l);
+protected:
+  unsigned int cycles;
+  cBN Px, Py, Pz,Qx, Qy, Qz; // 0x00,0x20,0x40,0x60,0x80,0x180
+  cBN sA0, sC0, sE0, s100, s120, s140, s160;
+  cBN H, R;
+  void ImportReg(unsigned char reg, const unsigned char *data, int l=0);
+  void ExportReg(unsigned char reg, unsigned char *data, int l=0);
+  void SetWordSize(int l) { wordsize=l; }
+  // statefull
+  void MonInit(int bits=0);
+  void MonExp(BIGNUM *scalar);
+  void MonExpNeg(void);
+  // ECC
+  void DoubleP(int temp);
+  void AddP(int temp);
+  void ToProjective(int set, BIGNUM *x, BIGNUM *y);
+  void ToAffine(void);
+  void CurveInit(BIGNUM *a);
+  //
+  int GetOpSize(int l);
+  void DoMap(int f, unsigned char *data=0, int l=0);
+  virtual bool Map(int f, unsigned char *data, int l) { return false; }
+  void SetMapIdent(int Mapid) { mapid=Mapid; }
+  bool Interruptible(void) { return interruptible; }
+  bool Interrupted(void) { return interrupted; }
+  virtual void AddMapCycles(unsigned int num) {}
+  unsigned int MapCycles(void) { return cycles; }
+  virtual unsigned int CpuCycles(void) { return 0; }
+public:
+  cMapCore(void);
+  virtual ~cMapCore() {}
+  };
+
+// ----------------------------------------------------------------
+
+class cN2Timer {
+private:
+  int ctrl, latch, id, delayInterrupt;
+  unsigned char val;
+  unsigned int cycles, intrCycles;
+  double divisor, invDivisor;
+  bool timerBugged;
+  enum { tmCONTINUOUS=0x01, tmRUNNING=0x02, tmINTERRUPT=0x04, tmMASK=0xFF, tmLATCHED=0x100 };
+  //
+  void Stop(void);
+  double GetDivisor();
+  void Update();
+  c6805* cpu;
+public:
+  cN2Timer();
+  int Id() const { return id; }
+  void SetId(unsigned char num, c6805* _cpu=NULL) { id = num; cpu = _cpu; }
+  bool AddCycles(unsigned int count);
+  unsigned int Cycles();
+  unsigned char Ctrl();
+  void Ctrl(unsigned char c);
+  unsigned char Latch(void) { return latch&0xFF; }
+  void Latch(unsigned char val);
+  bool Running(void) { return ctrl&tmRUNNING; }
+  bool InterruptSet(void) { return ctrl&tmINTERRUPT; }
+};
+
+// ----------------------------------------------------------------
+#define HW_REGS   0x20
+#define HW_OFFSET 0x0000
+
+#define MAX_TIMERS 3
+#define TIMER_NUM(x) (((x)>>2)&3) // timer order doesn't match HW order
+
+// $00 = I/O Reg
+//             0 = stop bit
+// $01 = Security
+// $06 = UART Clock control
+// $08 = timer 0 value
+// $09 = timer 0 latch
+// $0a = timer 0 config
+// $0d = PIC IMR
+//             3 = timer 0
+// $0e = CRC control
+//             0 = busy
+//             1 = disabled
+// $0f = CRC data
+// $10 = timer 1 value
+// $11 = timer 1 latch
+// $12 = timer 1 control
+// $14 = timer 2 value
+// $15 = timer 2 latch
+// $16 = timer 2 control
+//             0 = one-shot/periodic
+//             1 = enabled
+//             3-5 = prescalar
+//             6-7 = multiplier
+
+class cMapMemHW : public cMapMem {
+public:
+  // memory mapped HW
+  enum {
+    HW_IO=0x00, HW_SECURITY,
+    HW_TIMER0_DATA=0x08, HW_TIMER0_LATCH, HW_TIMER0_CONTROL,
+    HW_CRC_CONTROL=0x0e, HW_CRC_DATA,
+    HW_TIMER1_DATA=0x10, HW_TIMER1_LATCH, HW_TIMER1_CONTROL,
+    HW_TIMER2_DATA=0x14, HW_TIMER2_LATCH, HW_TIMER2_CONTROL
+    };
+private:
+  c6805& cpu;
+  // timer hardware
+  cN2Timer timer[MAX_TIMERS];
+  // CRC hardware
+  enum { CRCCALC_DELAY=8, CRC_BUSY=1, CRC_DISABLED=2, CRC_POLY=0x8408 /* ccitt*/};
+  unsigned short CRCvalue;
+  unsigned char CRCpos:1;
+  unsigned int CRCtime, CRCit;
+  unsigned short CRC16table[256];
+  void GenCRC16Table();
+  unsigned short CRCcalculate(unsigned short crc, unsigned char val, int bits);
+  void CRCupdate(unsigned int cycles);
+  //
+public:
+  cMapMemHW(c6805& cpu);
+  virtual unsigned char Get(unsigned short ea);
+  virtual void Set(unsigned short ea, unsigned char val);
+  int AddCycles(unsigned int num);
+};
+
+// ----------------------------------------------------------------
+
+class cN2Emu : protected c6805 {
+private:
+  bool initDone;
+protected:
+  bool Init(int id, int romv);
+  virtual bool RomInit(void) { return true; }
+  virtual void Stepper(void) {}
+public:
+  cN2Emu(void);
+  virtual ~cN2Emu() {}
+  };
+
+// ----------------------------------------------------------------
+#define N2FLAG_NONE     0
+#define N2FLAG_MECM     1
+#define N2FLAG_Bx       2
+#define N2FLAG_POSTAU   4
+#define N2FLAG_Ex       8
+#define N2FLAG_INV      128
+
+// MECM Algo return values
+enum {
+  MARV_SYNCING = -3, MARV_DESYNCED, MARV_NOT_PROCESSED,
+  MARV_ERROR = 0, MARV_SUCCESS, MARV_MAYBE_SUCCESS
+};
+
+class cN2Prov {
+private:
+  unsigned char seed[32], cwkey[8];
+  bool keyValid;
+protected:
+  int id, flags, seedSize;
+  unsigned char lastcw[17];    // last byte used to indicate confirmed cw
+  unsigned char prevcw[16], lasthd[32];
+  cIDEA idea;
+  //
+  virtual int Algo(int algo, const unsigned char *hd, const unsigned char *ed, unsigned char *hw, const unsigned char* cw) { return false; }
+  virtual void DynamicHD(unsigned char *hd, const unsigned char *ed) {}
+  virtual bool NeedsCwSwap(void) { return false; }
+  void ExpandInput(unsigned char *hw);
+public:
+  cN2Prov(int Id, int Flags);
+  virtual ~cN2Prov() {}
+  bool MECM(unsigned char in15, int algo, const unsigned char *ed, unsigned char *cw);
+  void SwapCW(unsigned char *cw);
+  virtual int ProcessBx(unsigned char *data, int len, int pos) { return -1; }
+  virtual int ProcessEx(unsigned char *data, int len, int pos) { return -1; }
+  virtual bool PostProcAU(int id, unsigned char *data) { return true; }
+  virtual int RunEmu(unsigned char *data, int len, unsigned short load, unsigned short run, unsigned short stop, unsigned short fetch, int fetch_len, int maxinstr=100000) { return -1; }
+  bool CanHandle(int Id) { return MATCH_ID(Id,id); }
+  bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+  void PrintCaps(int c);
+  virtual bool BFReset() { return false;}
+  virtual bool BFNext() { return false; }
+  };
+
+// ----------------------------------------------------------------
+
+class cN2Providers;
+
+class cN2ProvLink {
+friend class cN2Providers;
+private:
+  cN2ProvLink *next;
+protected:
+  int id, flags;
+  //
+  virtual cN2Prov *Create(void)=0;
+  bool CanHandle(int Id) { return MATCH_ID(Id,id); }
+  bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+public:
+  cN2ProvLink(int Id, int Flags);
+  virtual ~cN2ProvLink() {}
+  };
+
+template<class PROV, int ID, int FLAGS> class cN2ProvLinkReg : public cN2ProvLink {
+public:
+  cN2ProvLinkReg(void):cN2ProvLink(ID,FLAGS) {}
+  virtual cN2Prov *Create(void) { return new PROV(id,flags); }
+  };
+
+// ----------------------------------------------------------------
+
+class cN2Providers {
+friend class cN2ProvLink;
+private:
+  static cN2ProvLink *first;
+  //
+  static void Register(cN2ProvLink *plink);
+public:
+  static cN2Prov *GetProv(int Id, int Flags);
+  };
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.c
new file mode 100644 (file)
index 0000000..06a9b2e
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sc.h"
+
+#define SYSTEM_CONAX         0x0B00
+
+#define SYSTEM_NAME          "SC-Conax"
+#define SYSTEM_PRI           -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CONAX)
+
+#define SC_NAME "Conax"
+#define SC_ID   MAKE_SC_ID('C','o','n','x')
+
+#define L_SC        9
+#define L_SC_ALL    LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-conax",
+  { L_SC_DEFNAMES }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScConax ------------------------------------------------------------------
+
+class cSystemScConax : public cSystemScCore {
+public:
+  cSystemScConax(void);
+  };
+
+cSystemScConax::cSystemScConax(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Conax")
+{
+  hasLogger=true;
+}
+
+// -- cSystemLinkScConax --------------------------------------------------------------
+
+class cSystemLinkScConax : public cSystemLink {
+public:
+  cSystemLinkScConax(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScConax; }
+  };
+
+static cSystemLinkScConax staticInit;
+
+cSystemLinkScConax::cSystemLinkScConax(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScConax::CanHandle(unsigned short SysId)
+{
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(SC_ID);
+  if(card) {
+    res=card->CanHandle(SysId);
+    smartcards.ReleaseCard(card);
+    }
+  return res;
+}
+
+// -- cSmartCardConax -----------------------------------------------------------------
+
+#define STDENTTAG 0x32
+#define PPVENTTAG 0x39
+#define ADDR_SIZE 7
+
+struct stdent {
+  unsigned short id;
+  char name[16], date[4][16];
+  unsigned int pbm[2];
+  };
+
+class cSmartCardConax : public cSmartCard, private cProviders {
+private:
+  int sysId, cardVer, currency;
+  bool emmInitDone;
+  //
+  int GetLen(void);
+public:
+  cSmartCardConax(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  virtual bool CanHandle(unsigned short SysId);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x90,0x00 }, "Instruction executed without errors", true },
+  { { 0x90,0x11 }, "Bad instruction", false },
+  { { 0x90,0x12 }, "Access denied, check your subscription", false },
+  { { 0x90,0x13 }, "Bit error detected", false },
+  { { 0x90,0x16 }, "Insuficient info", false },
+  { { 0x94,0x00 }, "Instruction executed without errors, waiting for user interaction", true },
+  { { 0x98,0xFF }, "Instruction accepted, data to be read", true },
+  { { 0x9c,0xFF }, "Instruction accepted, data to be read", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8E2,1000,100
+  };
+
+cSmartCardConax::cSmartCardConax(void)
+:cSmartCard(&cardCfg,msgs)
+{
+  sysId=0; cardVer=0; currency=0;
+  emmInitDone=false;
+}
+
+bool cSmartCardConax::CanHandle(unsigned short SysId)
+{
+  return (SysId==sysId);
+}
+
+int cSmartCardConax::GetLen(void)
+{
+  return (sb[0]==0x98 || sb[0]==0x9C || (sb[0]==0x90 && sb[1]==0x00)) ? sb[1] : -1;
+}
+
+bool cSmartCardConax::Init(void)
+{
+  static const unsigned char cnxHist[] = { '0','B','0','0' }; // XXX is this always true ?
+
+  sysId=0; cardVer=0; currency=0;
+  if(atr->histLen<4 || memcmp(atr->hist, cnxHist, atr->histLen)) {
+    PRINTF(L_SC_INIT,"doesn't look like a Conax card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Conax smartcard\n");
+  static unsigned char ins26[] = { 0xdd, 0x26, 0x00, 0x00, 0x03 };
+  static unsigned char insc6[] = { 0xdd, 0xc6, 0x00, 0x00, 0x03 };
+  static unsigned char insca[] = { 0xdd, 0xca, 0x00, 0x00, 0x00 };
+  static const unsigned char hostVer[] = { 0x10,0x01,0x01 };  // host version
+  unsigned char buff[MAX_LEN];
+  int l;
+  if(!IsoWrite(ins26,hostVer) || !Status() || (l=GetLen())<=0) {
+    PRINTF(L_SC_ERROR,"card init failed (1)");
+    return false;
+    }
+  insca[4]=l;
+  if(!IsoRead(insca,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"card init failed (2)");
+    return false;
+    }
+  for(int i=0; i<l; i+=buff[i+1]+2) {
+    switch(buff[i]) {
+      case 0x20: cardVer=buff[i+2]; break;
+      case 0x28: sysId=(buff[i+2]<<8)+buff[i+3]; break;
+      case 0x2f: currency=(buff[i+2]<<8)+buff[i+3]; break;
+      }
+    }
+  infoStr.Printf("Card v.%d Caid %04x\n",cardVer,sysId);
+  PRINTF(L_SC_INIT,"card v.%d",cardVer);
+  snprintf(idStr,sizeof(idStr),"%s (V.%d)",SC_NAME,cardVer);
+
+  static const unsigned char stdEntits[] = { 0x1C,0x01,0x00 };
+  if(IsoWrite(insc6,stdEntits) && Status() && (l=GetLen())>0) {
+    infoStr.Printf("Subscriptions:\n");
+    infoStr.Printf("|id  |name        |date             |\n");
+    infoStr.Printf("+----+------------+-----------------+\n");
+
+    do {
+      insca[4]=l;
+      if(!IsoRead(insca,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"failed to read entitlements");
+        break;
+        }
+      for(int i=0; i<l; i+=buff[i+1]+2) {
+        if(buff[i]!=STDENTTAG) {
+          PRINTF(L_SC_ERROR,"bad entitlement format %02x != %02x",buff[i],STDENTTAG);
+          break;
+          }
+
+        struct stdent ent;
+        memset(&ent,0,sizeof(ent));
+        ent.id=(buff[i+2]<<8)|buff[i+3];
+
+        int date=0, pbm=0;
+        int max=i+buff[i+1]+2;
+        for(int j=i+4; j<max; j+=buff[j+1]+2) {
+          switch(buff[j]) {
+            case 0x01: // prov. name
+              snprintf(ent.name,sizeof(ent.name),"%.12s",&buff[j+2]);
+              break;
+            case 0x30: // date
+              if(date<=3) {
+                snprintf(ent.date[date],sizeof(ent.date[0]),"%02d.%02d.%02d",buff[j+2]&0x1F,buff[j+3]&0xF,(1990+((buff[j+3]>>4)+((buff[j+2]>>5)&0x7)*10))%100);
+                date++;
+                }
+              break;
+            case 0x20:
+              if(pbm<=1) {
+                ent.pbm[pbm]=(buff[j+2]<<24)|(buff[j+3]<<16)|(buff[j+4]<<8)|buff[j+5];
+                pbm++;
+                }
+              break;
+            }         
+          }
+        infoStr.Printf("|%04X|%s|%s-%s|\n",ent.id,ent.name,ent.date[0],ent.date[1]);
+        infoStr.Printf("|    |            |%s-%s|\n",ent.date[2],ent.date[3]);
+        }
+      } while((l=GetLen())>0);
+    }
+  else
+    PRINTF(L_SC_ERROR,"requesting entitlements failed");
+
+/*
+  static const unsigned char ppvEntits[] = { 0x1C,0x01,0x01 };
+  if(IsoWrite(ins26,ppvEntits) && Status()) {
+    while((l=GetLen())>0) {
+      insca[4]=l;
+      if(!IsoRead(insca,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"failed to read PPV entitlements");
+        break;
+        }
+
+
+      }
+    }
+  else
+    PRINTF(L_SC_ERROR,"getting PPV entitlements failed");
+*/
+
+  emmInitDone=false;
+  infoStr.Finish();
+  return true;
+}
+
+bool cSmartCardConax::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static unsigned char insa2[] = { 0xDD,0xA2,0x00,0x00,0x00 };
+  static unsigned char insca[] = { 0xDD,0xCA,0x00,0x00,0x00 };
+
+  int l;
+  if((l=CheckSctLen(data,3))<=0) return false;
+  unsigned char buff[MAX_LEN];
+  buff[0]=0x14;
+  buff[1]=l+1;
+  buff[2]=0;
+  memcpy(buff+3,data,l);
+
+  insa2[4]=l+3;
+  if(!IsoWrite(insa2,buff) || !Status() || (l=GetLen())<=0) return false;
+  int gotIdx=0;
+  do {
+    insca[4]=l;
+    if(!IsoRead(insca,buff) || !Status()) return false;
+    for(int i=0; i<l; i+=buff[i+1]+2) {
+      switch(buff[i]) {
+        case 0x25:
+          if(buff[i+1]>=13) {
+            int idx=buff[i+4];
+            if(idx<=1) {
+              gotIdx|=(1<<idx);
+              memcpy(cw+idx*8,&buff[i+7],8);
+              }
+            }
+          break;
+        }
+      }
+    } while((l=GetLen())>0);
+  if(gotIdx!=3) PRINTF(L_SC_ERROR,"strange, only got index %d cw. Failing... (this may be a bug)",gotIdx==1?0:1);
+  return gotIdx==3;
+}
+
+bool cSmartCardConax::Update(int pid, int caid, const unsigned char *data)
+{
+  static unsigned char ins82[] = { 0xdd,0x82,0x00,0x00,0x10 };
+  static unsigned char ins84[] = { 0xdd,0x84,0x00,0x00,0x00 };
+  static unsigned char insca[] = { 0xdd,0xca,0x00,0x00,0x00 };
+  static unsigned char ins82Data[] = { 0x11,0x0e,0x01,0xb0,0x0f,0xff,0xff,0xdd,0x00,0x00,0x09,0x04,0x0b,0x00,0x00,0x00 };
+  unsigned char buff[MAX_LEN];
+
+  if(!emmInitDone) {
+    ins82Data[14]=(pid>>8)&0xFF;
+    ins82Data[15]=pid&0xFF;
+    int l;
+    if(!IsoWrite(ins82,ins82Data) || !Status() || (l=GetLen())<=0) return false;
+    insca[4]=l;
+    if(!IsoRead(insca,buff) || !Status()) return false;
+    if(buff[0]!=0x22 || buff[1]+2!=l) {
+      PRINTF(L_SC_ERROR,"bad card reply on EMM init");
+      return false;
+      }
+
+    LBSTARTF(L_SC_INIT);
+    LBPUT("set filter");
+    Clear();
+    for(int i=2; i<l; i+=buff[i+1]+2) {
+      if(buff[i]==0x23) { // Card Address
+        if(buff[i+1]==ADDR_SIZE) {
+          AddProv(new cProviderConax(&buff[i+2]));
+          char str[ADDR_SIZE*2+4];
+          LBPUT(" addr %s",HexStr(str,&buff[i+2],ADDR_SIZE));
+          }    
+        else {
+          LBPUT(" bad addr cmd (size=%d)",buff[i+1]);
+          return false;
+          }
+        }
+      }
+    LBEND();
+    emmInitDone=true;
+    }
+
+  if(MatchProv(data)) {
+    int l;
+    if((l=CheckSctLen(data,2))>0) {
+      buff[0]=0x12; buff[1]=l;
+      memcpy(&buff[2],data,l);
+      ins84[4]=l+2;
+      if(IsoWrite(ins84,buff) && !Status()) return true;
+      }
+    }
+  return false;
+}
+
+// -- cSmartCardLinkConax -------------------------------------------------------------
+
+class cSmartCardLinkConax : public cSmartCardLink {
+public:
+  cSmartCardLinkConax(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardConax(); }
+  };
+
+static cSmartCardLinkConax staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-conax/sc-conax.mk
new file mode 100644 (file)
index 0000000..2ab4e12
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard Conax
+#
+TARGET = sc_conax
+OBJS   = sc-conax.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.c
new file mode 100644 (file)
index 0000000..9f16889
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "parse.h"
+#include "crypto.h"
+#include "opts.h"
+#include "misc.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_NAME          "SC-Cryptoworks"
+#define SYSTEM_PRI           -5
+
+#define SC_NAME "Cryptoworks"
+#define SC_ID   MAKE_SC_ID('C','r','W','o')
+
+#define L_SC        10
+#define L_SC_EXTRA  LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL    LALL(L_SC_EXTRA)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-cryptoworks",
+  { L_SC_DEFNAMES,"extra" }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+static int disableParental=0;
+
+// -- cSystemScCryptoworks ---------------------------------------------------------------
+
+class cSystemScCryptoworks : public cSystemScCore {
+public:
+  cSystemScCryptoworks(void);
+  };
+
+cSystemScCryptoworks::cSystemScCryptoworks(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Cryptoworks")
+{
+  hasLogger=true;
+}
+
+// -- cSystemLinkScCryptoworks --------------------------------------------------------
+
+class cSystemLinkScCryptoworks : public cSystemLink {
+public:
+  cSystemLinkScCryptoworks(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScCryptoworks; }
+  };
+
+static cSystemLinkScCryptoworks staticInit;
+
+cSystemLinkScCryptoworks::cSystemLinkScCryptoworks(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  static const char *rat[] = {
+    trNOOP("don't touch"),
+    trNOOP("disable")
+    };
+
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptSel("DisableParental",trNOOP("SC-Cryptoworks: Parental rating"),&disableParental,sizeof(rat)/sizeof(char *),rat));
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScCryptoworks::CanHandle(unsigned short SysId)
+{
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(SC_ID);
+  if(card) {
+    res=card->CanHandle(SysId);
+    smartcards.ReleaseCard(card);
+    }
+  return res;
+}
+
+// -- cSmartCardDataCryptoworks -----------------------------------------------------
+
+enum eDataType { dtIPK, dtUCPK, dtPIN };
+
+class cSmartCardDataCryptoworks : public cSmartCardData {
+private:
+  int IdLen(void) const { return type==dtIPK ? 2:5; }
+public:
+  eDataType type;
+  unsigned char id[5], pin[4];
+  cBN key;
+  //
+  cSmartCardDataCryptoworks(void);
+  cSmartCardDataCryptoworks(eDataType Type, unsigned char *Id);
+  virtual bool Parse(const char *line);
+  virtual bool Matches(cSmartCardData *param);
+  };
+
+cSmartCardDataCryptoworks::cSmartCardDataCryptoworks(void)
+:cSmartCardData(SC_ID)
+{}
+
+cSmartCardDataCryptoworks::cSmartCardDataCryptoworks(eDataType Type, unsigned char *Id)
+:cSmartCardData(SC_ID)
+{
+  type=Type;
+  memset(id,0,sizeof(id));
+  memcpy(id,Id,IdLen());
+}
+
+bool cSmartCardDataCryptoworks::Matches(cSmartCardData *param)
+{
+  cSmartCardDataCryptoworks *cd=(cSmartCardDataCryptoworks *)param;
+  return cd->type==type && !memcmp(cd->id,id,IdLen());
+}
+
+bool cSmartCardDataCryptoworks::Parse(const char *line)
+{
+  line=skipspace(line);
+  if(!strncasecmp(line,"IPK",3))       { type=dtIPK;  line+=3; }
+  else if(!strncasecmp(line,"UCPK",4)) { type=dtUCPK; line+=4; }
+  else if(!strncasecmp(line,"PIN",3))  { type=dtPIN;  line+=3; }
+  else {
+    PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: datatype");
+    return false;
+    }
+  line=skipspace(line);
+  if(GetHex(line,id,IdLen())!=IdLen()) {
+    PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: caid/serial");
+    return false;
+    }
+  line=skipspace(line);
+  if(type==dtPIN) {
+    for(int i=0; i<4; i++) {
+      if(!isdigit(*line)) {
+        PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: pin");
+        return false;
+        }
+      pin[i]=*line++;
+      }
+    }
+  else {
+    unsigned char buff[64];
+    if(GetHex(line,buff,64,true)!=64) {
+      PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: ipk/ucpk");
+      return false;
+      }
+    BN_bin2bn(buff,64,key);
+    }
+  return true;
+}
+
+// -- cSmartCardCryptoworks -----------------------------------------------------------
+
+class cSmartCardCryptoworks : public cSmartCard, public cIdSet {
+private:
+  int caid;
+  cRSA rsa;
+  cBN ucpk, exp;
+  bool ucpkValid;
+  //
+  int GetLen(void);
+  bool EndOfData(void);
+  bool SelectFile(int file);
+  int ReadRecord(unsigned char *buff, int num);
+  int ReadData(unsigned char *buff, int len);
+public:
+  cSmartCardCryptoworks(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  virtual bool CanHandle(unsigned short CaId);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x90,0x00 }, "Instruction executed without errors", true },
+  { { 0x92,0x40 }, "Memory problem", false },
+  { { 0x94,0x02 }, "Out of range", false },
+  { { 0x94,0x04 }, "File not found", false },
+  { { 0x98,0x04 }, "Verification failed (wrong PIN)", false },
+  { { 0x98,0x05 }, "Wrong signature", false },
+  { { 0x98,0x40 }, "Verification failed, card blocked (deblocking by uplink required)", false },
+  { { 0x9F,0xFF }, "Instruction accepted, data to be read", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8E2,1000,100
+  };
+
+struct chid_dat {
+  unsigned int chid, version;
+  unsigned char id, status;
+  char from[16], to[16], name[16];
+  };
+  
+cSmartCardCryptoworks::cSmartCardCryptoworks(void)
+:cSmartCard(&cardCfg,msgs)
+{
+  static const unsigned char cwexp[] = { 0x01,0x00,0x01 };
+  BN_bin2bn(cwexp,sizeof(cwexp),exp);
+  ucpkValid=false;
+}
+
+int cSmartCardCryptoworks::GetLen(void)
+{
+  return (sb[0]==0x9F) ? sb[1] : -1;
+}
+
+bool cSmartCardCryptoworks::EndOfData(void)
+{
+  return sb[0]==0x94 && sb[1]==0x02;
+}
+
+bool cSmartCardCryptoworks::SelectFile(int file)
+{
+  static unsigned char insa4[] = { 0xA4,0xA4,0x00,0x00,0x02,0x00,0x00 };
+  insa4[5]=file>>8;
+  insa4[6]=file&0xFF;
+  return IsoWrite(insa4,&insa4[5]) && Status();
+}
+
+int cSmartCardCryptoworks::ReadRecord(unsigned char *buff, int num)
+{
+  static unsigned char insa2[] = { 0xA4,0xA2,0x00,0x00,0x01,0x00 };
+  static unsigned char insb2[] = { 0xA4,0xB2,0x00,0x00,0x00 };
+  insa2[5]=num;
+  if(IsoWrite(insa2,&insa2[5]) && Status() && (num=GetLen())>0) {
+    insb2[4]=num;
+    if(IsoRead(insb2,buff) && Status()) return num;
+    }
+  return -1;
+}
+
+int cSmartCardCryptoworks::ReadData(unsigned char *buff, int len)
+{
+  static unsigned char insc0[] = { 0xA4,0xC0,0x00,0x00,0x00 };
+  insc0[4]=len;
+  if(IsoRead(insc0,buff) && Status()) return len;
+  return -1;
+}
+
+bool cSmartCardCryptoworks::Init(void)
+{
+  if(atr->histLen<6 || atr->hist[1]!=0xC4 || atr->hist[4]!=0x8F || atr->hist[5]!=0xF1) {
+    PRINTF(L_SC_INIT,"doesn't look like a Cryptoworks card");
+    return false;
+    }
+  infoStr.Begin();
+  infoStr.Strcat("Cryptoworks smartcard\n");
+  ucpkValid=false;
+  unsigned char buff[MAX_LEN];
+  int mfid=0x3F20;
+  if(ReadData(buff,0x11)>0 && buff[0]==0xDF && buff[1]>=6)
+    mfid=buff[6]*256+buff[7];
+  else PRINTF(L_SC_ERROR,"reading MF-ID failed, using default 3F20");
+
+  unsigned char Caid[2], serial[5];
+  if(!SelectFile(0x2F01) || ReadRecord(buff,0xD1)<4) {
+    PRINTF(L_SC_ERROR,"reading record 2f01/d1 failed");
+    return false;
+    }
+  memcpy(Caid,&buff[2],2);
+  caid=buff[2]*256+buff[3];
+  if(ReadRecord(buff,0x80)<7) {
+    PRINTF(L_SC_ERROR,"reading record 2f01/80 failed");
+    return false;
+    }
+  SetCard(new cCardCryptoworks(&buff[2]));
+  memcpy(serial,&buff[2],5);
+  snprintf(idStr,sizeof(idStr),"%s (V.%d)",SC_NAME,atr->hist[2]);
+  char str[20];
+  infoStr.Printf("Card v.%d (PINcount=%d)\n"
+                 "Caid %04x Serial %s\n",
+                 atr->hist[2],atr->hist[3],caid,HexStr(str,&buff[2],5));
+  PRINTF(L_SC_INIT,"card v.%d (pindown=%d) caid %04x serial %s MF %04X",atr->hist[2],atr->hist[3],caid,HexStr(str,&buff[2],5),mfid);
+  if(ReadRecord(buff,0x9F)>=3) {
+    const char *n="(unknown)";
+    if(ReadRecord(buff+10,0xC0)>=18) n=(char *)buff+10+2;
+    infoStr.Printf("Issuer:     0x%02x (%.16s)\n",buff[2],n);
+    PRINTF(L_SC_INIT,"card issuer: 0x%02x %.16s",buff[2],n);
+    }
+  if(ReadRecord(buff,0x9E)>=66) {
+    HEXDUMP(L_SC_EXTRA,&buff[2],64,"card ISK");
+    cSmartCardDataCryptoworks cd(dtIPK,Caid);
+    cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+    if(entry) {
+      PRINTF(L_SC_EXTRA,"got IPK from smartcard.conf");
+      if(rsa.RSA(&buff[2],&buff[2],64,exp,entry->key,false)>0) {
+        HEXDUMP(L_SC_EXTRA,&buff[2],64,"decrypted ISK");
+        if(buff[2] == ((mfid&0xFF)>>1)) {
+          buff[2]|=0x80;
+          BN_bin2bn(&buff[2],64,ucpk);
+          ucpkValid=true;
+          PRINTF(L_SC_INIT,"got UCPK from IPK/ISK");
+          }
+        else PRINTF(L_SC_ERROR,"UCPK check failed %02x != %02x",buff[2],(mfid&0xFF)>>1);
+        }
+      else PRINTF(L_SC_ERROR,"RSA failed for UCPK");
+      }
+    }
+  if(!ucpkValid) {
+    cSmartCardDataCryptoworks cd(dtUCPK,serial);
+    cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+    if(entry) {
+      BN_copy(ucpk,entry->key);
+      ucpkValid=true;
+      PRINTF(L_SC_INIT,"got UCPK from smartcard.conf");
+      }
+    }
+  if(!ucpkValid) PRINTF(L_GEN_WARN,"no valid UCPK for cryptoworks smartcard");
+
+  // read entitlements
+  static unsigned char insb8[] = { 0xA4,0xB8,0x00,0x00,0x0C };
+  unsigned char provId[16];
+  unsigned int count=0;
+  insb8[2]=insb8[3]=0x00;
+  while(IsoRead(insb8,buff) && !EndOfData() && Status()) {
+    if(buff[0]==0xDF && buff[1]==0x0A) {
+      int fileno=(buff[4]&0x3F)*256+buff[5];
+      if((fileno&0xFF00)==0x1F00) {
+        provId[count++]=fileno&0xFF;
+        if(count>=sizeof(provId)) break;
+        }
+      }
+    insb8[2]=insb8[3]=0xFF;
+    }
+  for(unsigned int i=0; i<count ; i++) {
+    if(SelectFile(0x1F00+provId[i])) {
+      const char *n="(unknown)";
+      if(SelectFile(0x0E11) && ReadRecord(buff,0xD6)>=18) n=(char *)buff+2;
+      infoStr.Printf("Provider %d: 0x%02x (%.16s)\n",i,provId[i],n);
+      PRINTF(L_SC_INIT,"provider %d: 0x%02x %.16s",i,provId[i],n);
+      static unsigned char insa2_0[] = { 0xA4,0xA2,0x01,0x00,0x03,0x83,0x01,0x00 };
+      static unsigned char insa2_1[] = { 0xA4,0xA2,0x01,0x00,0x05,0x8C,0x00,0x00,0x00,0x00 };
+      static unsigned char insb2[] = { 0xA4,0xB2,0x00,0x00,0x00 };
+      static const unsigned int fn[] = { 0x0f00,0x0f20,0x0f40,0x0f60,0 };
+      static const char *fnName[] = { "Download","Subscriptions","PPV Events","PPV Events",0 };
+      bool first=true;
+      for(int j=0; fn[j]; j++) {
+        if(SelectFile(fn[j])) {
+          insa2_0[7]=provId[i];
+          unsigned char *ins=(j==1) ? insa2_1 : insa2_0;
+          int l;
+          if(IsoWrite(ins,&ins[5]) && Status() && (l=GetLen())>0) {
+            if(first) {
+              infoStr.Printf("id|chid|st|date             |name\n");
+              PRINTF(L_SC_INIT,"| id | chid | stat | date              | version  | name        ");
+              first=false;
+              }
+            infoStr.Printf(  "--+----+--+-----------------+------\n"
+                           "%s\n",
+                           fnName[j]);
+            PRINTF(L_SC_INIT,"+----+------+------+-------------------+----------+-------------");
+            PRINTF(L_SC_INIT,"| %s",fnName[j]);
+            insb2[3]=0x00;
+            insb2[4]=l;
+            while(IsoRead(insb2,buff) && !EndOfData() && Status()) {
+              struct chid_dat chid;
+              memset(&chid,0,sizeof(chid));
+              for(int k=0; k<l; k+=buff[k+1]+2) {
+                switch(buff[k]) {
+                  case 0x83:
+                    chid.id=buff[k+2];
+                    break;
+                  case 0x8c:
+                    chid.status=buff[k+2];
+                    chid.chid=buff[k+3]*256+buff[k+4];
+                    break;
+                  case 0x8D:
+                    snprintf(chid.from,sizeof(chid.from),"%02d.%02d.%02d",buff[k+3]&0x1F,((buff[k+2]&1)<<3)+(buff[k+3]>>5),(1990+(buff[k+2]>>1))%100);
+                    snprintf(chid.to  ,sizeof(chid.to)  ,"%02d.%02d.%02d",buff[k+5]&0x1F,((buff[k+4]&1)<<3)+(buff[k+5]>>5),(1990+(buff[k+4]>>1))%100);
+                    break;
+                  case 0xD5:
+                    snprintf(chid.name,sizeof(chid.name),"%.12s",&buff[k+2]);
+                    chid.version=Bin2Int(&buff[k+14],4);
+                    break;
+                  }
+                }
+              infoStr.Printf("%02x|%04x|%02x|%s-%s|%.12s\n",
+                  chid.id,chid.chid,chid.status,chid.from,chid.to,chid.name);
+              PRINTF(L_SC_INIT,"| %02x | %04x |  %02x  | %s-%s | %08x | %.12s",
+                  chid.id,chid.chid,chid.status,chid.from,chid.to,chid.version,chid.name);
+              insb2[3]=0x01;
+              }
+            }
+          }
+        }
+      }
+    }
+
+  if(disableParental) {
+    bool pinOK=false;
+    if(SelectFile(0x2F11) && ReadRecord(buff,atr->hist[3])>=7) {
+      pinOK=true;
+      PRINTF(L_SC_INIT,"read PIN from card.");
+      }
+    if(!pinOK) {
+      cSmartCardDataCryptoworks cd(dtPIN,serial);
+      cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+      if(entry) {
+        memcpy(&buff[2],entry->pin,4);
+        pinOK=true;
+        PRINTF(L_SC_INIT,"got PIN from smartcard.conf.");
+        }
+      }
+    if(pinOK) {
+      static const unsigned char ins24[] = { 0xA4,0x24,0x00,0x01,0x05 };
+      PRINTF(L_SC_INIT,"your card PIN is %.4s",&buff[2]);
+      // parental rating
+      // 0x00      - undefined
+      // 0x01-0x0f - minimum age=rating+3 years
+      // 0x10-0xff - reserved for provider usage
+      buff[6]=0;
+      if(IsoWrite(ins24,&buff[2]) && Status())
+        PRINTF(L_SC_INIT,"parental rating set to %02x",buff[6]);
+      else
+        PRINTF(L_SC_ERROR,"failed to set parental rating.");
+      }
+    else PRINTF(L_SC_INIT,"no PIN available.");
+    }
+
+  infoStr.Finish();
+  return true;
+}
+
+bool cSmartCardCryptoworks::CanHandle(unsigned short CaId)
+{
+  return CaId==caid;
+}
+
+bool cSmartCardCryptoworks::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static unsigned char ins4c[] = { 0xA4,0x4C,0x00,0x00,0x00 };
+
+  unsigned char nanoD4[10];
+  int l=CheckSctLen(data,-5+(ucpkValid ? sizeof(nanoD4):0));
+  if(l>5) {
+    unsigned char buff[MAX_LEN];
+    if(ucpkValid) {
+      memcpy(buff,data,l);
+      nanoD4[0]=0xD4;
+      nanoD4[1]=0x08;
+      for(unsigned int i=2; i<sizeof(nanoD4); i++) nanoD4[i]=rand();
+      memcpy(&buff[l],nanoD4,sizeof(nanoD4));
+      data=buff; l+=sizeof(nanoD4);
+      }
+    ins4c[3]=ucpkValid ? 2 : 0;
+    ins4c[4]=l-5;
+    if(IsoWrite(ins4c,&data[5]) && Status() &&
+       (l=GetLen())>0 && ReadData(buff,l)==l) {
+      int r=0;
+      for(int i=0; i<l && r<2; ) {
+        int n=buff[i+1];
+        switch(buff[i]) {
+          case 0xDB: // CW
+            PRINTF(L_SC_EXTRA,"nano DB (cw)");
+            if(n==0x10) {
+              memcpy(cw,&buff[i+2],16);
+              r|=1;
+              }
+            break;
+          case 0xDF: // signature
+            PRINTF(L_SC_EXTRA,"nano DF %02x (sig)",n);
+            if(n==0x08) {
+              if((buff[i+2]&0x50)==0x50) {
+                if(buff[i+3]&0x01) PRINTF(L_SC_ERROR,"not decyphered. PIN protected?");
+                else if(!(buff[i+5]&0x80)) PRINTF(L_SC_ERROR,"missing entitlement. Check your subscription");
+                else r|=2;
+                }
+              }
+            else if(n==0x40) { // camcrypt
+              if(ucpkValid) {
+                if(rsa.RSA(&buff[i+2],&buff[i+2],n,exp,ucpk,false)<=0) {
+                  PRINTF(L_SC_ERROR,"camcrypt RSA failed.");
+                  return false;
+                  }
+                HEXDUMP(L_SC_EXTRA,&buff[i+2],n,"after camcrypt");
+                if(!memmem(&buff[i+2+4],n-4,nanoD4,sizeof(nanoD4))) {
+                  PRINTF(L_SC_ERROR,"camcrypt failed. Check IPK/UCPK in your smartcard.conf!");
+                  return false;
+                  }
+                r=0; l=n-4; n=4;
+                }
+              else {
+                PRINTF(L_SC_ERROR,"valid UCPK needed for camcrypt! Check your smartcard.conf for a IPK/UCPK!");
+                return false;
+                }
+              }
+            break;
+          default:
+            PRINTF(L_SC_EXTRA,"nano %02x (unhandled)",buff[i]);
+            break;
+          }
+        i+=n+2;
+        }
+      return r==3;
+      }
+    }
+  return false;
+}
+
+bool cSmartCardCryptoworks::Update(int pid, int caid, const unsigned char *data)
+{
+  static unsigned char ins[] = { 0xA4,0x42,0x00,0x00,0x00 };
+
+  cAssembleData ad(data);
+  if(MatchAndAssemble(&ad,0,0)) {
+    while((data=ad.Assembled())) {
+      int c, n;
+      switch(data[0]) {
+        case 0x82: c=0x42; n=10; break;
+        case 0x84: c=0x48; n=9; break;
+        case 0x88:
+        case 0x89: c=0x44; n=5; break;
+        default:   continue;
+        }
+
+      int len=CheckSctLen(data,-n);
+      if(len>n) {
+        ins[1]=c;
+        ins[4]=len-n;
+        if(IsoWrite(ins,&data[n])) Status();
+        }
+      }
+    return true;
+    }
+  return false;
+}
+
+// -- cSmartCardLinkCryptoworks -------------------------------------------------------
+
+class cSmartCardLinkCryptoworks : public cSmartCardLink {
+public:
+  cSmartCardLinkCryptoworks(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardCryptoworks(); }
+  virtual cSmartCardData *CreateData(void) { return new cSmartCardDataCryptoworks; }
+  };
+
+static cSmartCardLinkCryptoworks staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-cryptoworks/sc-cryptoworks.mk
new file mode 100644 (file)
index 0000000..45faad2
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard Cryptoworks
+#
+TARGET = sc_cryptoworks
+OBJS   = sc-cryptoworks.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.c
new file mode 100644 (file)
index 0000000..2a316f5
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "crypto.h"
+#include "data.h"
+#include "misc.h"
+#include "parse.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_NAME          "SC-Irdeto"
+#define SYSTEM_PRI           -5
+
+#define SC_NAME "Irdeto"
+#define SC_ID   MAKE_SC_ID('I','r','d','t')
+
+#define L_SC        8
+#define L_SC_ALL    LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-irdeto",
+  { L_SC_DEFNAMES }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+static void BN_complement(const unsigned char *data, int len, BIGNUM *bn)
+{
+  unsigned char *buff=AUTOMEM(len);
+  for(int i=len-1; i>=0; i--) buff[i]=~data[i];
+  BN_bin2bn(buff,len,bn);
+  BN_add_word(bn,1);
+}
+
+// -- cSystemScIrdeto ----------------------------------------------------------
+
+class cSystemScIrdeto : public cSystemScCore {
+public:
+  cSystemScIrdeto(void);
+  };
+
+cSystemScIrdeto::cSystemScIrdeto(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Irdeto")
+{
+  hasLogger=true;
+}
+
+// -- cSystemLinkScIrdeto ------------------------------------------------------
+
+class cSystemLinkScIrdeto : public cSystemLink {
+public:
+  cSystemLinkScIrdeto(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScIrdeto; }
+  };
+
+static cSystemLinkScIrdeto staticInit;
+
+cSystemLinkScIrdeto::cSystemLinkScIrdeto(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScIrdeto::CanHandle(unsigned short SysId)
+{
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(SC_ID);
+  if(card) {
+    res=card->CanHandle(SysId);
+    smartcards.ReleaseCard(card);
+    }
+  return res;
+}
+
+// -- cCamCrypt ----------------------------------------------------------------
+
+class cCamCrypt {
+private:
+  static const unsigned char cryptTable[];
+  bool randomInit;
+  cRSA rsa;
+  cBN cardExp, cardMod;
+  //
+  void GenerateRandom(unsigned char *buf, int len);
+  void RotateRight8Byte(unsigned char *key);
+  void RotateLeft8Byte(unsigned char *key);
+protected:
+  void CamCrypt(const unsigned char *key, unsigned char *data);
+  void RevCamCrypt(const unsigned char *key, unsigned char *data);
+  //
+  bool SetupCardFiles(unsigned char *data, int len, BIGNUM *exp, BIGNUM *mod);
+  void PrepareCamMessage(unsigned char *plain);
+  bool EncryptCamMessage(unsigned char *encrypted, const unsigned char *plain);
+  const unsigned char *CamKey(const unsigned char *data) { return data+8; }
+  const unsigned char *HelperKey(const unsigned char *data) { return data+24; }
+public:
+  cCamCrypt(void);
+  };
+
+const unsigned char cCamCrypt::cryptTable[256] = {
+  0xDA,0x26,0xE8,0x72,0x11,0x52,0x3E,0x46,0x32,0xFF,0x8C,0x1E,0xA7,0xBE,0x2C,0x29,
+  0x5F,0x86,0x7E,0x75,0x0A,0x08,0xA5,0x21,0x61,0xFB,0x7A,0x58,0x60,0xF7,0x81,0x4F,
+  0xE4,0xFC,0xDF,0xB1,0xBB,0x6A,0x02,0xB3,0x0B,0x6E,0x5D,0x5C,0xD5,0xCF,0xCA,0x2A,
+  0x14,0xB7,0x90,0xF3,0xD9,0x37,0x3A,0x59,0x44,0x69,0xC9,0x78,0x30,0x16,0x39,0x9A,
+  0x0D,0x05,0x1F,0x8B,0x5E,0xEE,0x1B,0xC4,0x76,0x43,0xBD,0xEB,0x42,0xEF,0xF9,0xD0,
+  0x4D,0xE3,0xF4,0x57,0x56,0xA3,0x0F,0xA6,0x50,0xFD,0xDE,0xD2,0x80,0x4C,0xD3,0xCB,
+  0xF8,0x49,0x8F,0x22,0x71,0x84,0x33,0xE0,0x47,0xC2,0x93,0xBC,0x7C,0x3B,0x9C,0x7D,
+  0xEC,0xC3,0xF1,0x89,0xCE,0x98,0xA2,0xE1,0xC1,0xF2,0x27,0x12,0x01,0xEA,0xE5,0x9B,
+  0x25,0x87,0x96,0x7B,0x34,0x45,0xAD,0xD1,0xB5,0xDB,0x83,0x55,0xB0,0x9E,0x19,0xD7,
+  0x17,0xC6,0x35,0xD8,0xF0,0xAE,0xD4,0x2B,0x1D,0xA0,0x99,0x8A,0x15,0x00,0xAF,0x2D,
+  0x09,0xA8,0xF5,0x6C,0xA1,0x63,0x67,0x51,0x3C,0xB2,0xC0,0xED,0x94,0x03,0x6F,0xBA,
+  0x3F,0x4E,0x62,0x92,0x85,0xDD,0xAB,0xFE,0x10,0x2E,0x68,0x65,0xE7,0x04,0xF6,0x0C,
+  0x20,0x1C,0xA9,0x53,0x40,0x77,0x2F,0xA4,0xFA,0x6D,0x73,0x28,0xE2,0xCD,0x79,0xC8,
+  0x97,0x66,0x8E,0x82,0x74,0x06,0xC7,0x88,0x1A,0x4A,0x6B,0xCC,0x41,0xE9,0x9D,0xB8,
+  0x23,0x9F,0x3D,0xBF,0x8D,0x95,0xC5,0x13,0xB9,0x24,0x5A,0xDC,0x64,0x18,0x38,0x91,
+  0x7F,0x5B,0x70,0x54,0x07,0xB6,0x4B,0x0E,0x36,0xAC,0x31,0xE6,0xD6,0x48,0xAA,0xB4
+  };
+
+cCamCrypt::cCamCrypt(void)
+{
+  randomInit=false;
+}
+
+void cCamCrypt::GenerateRandom(unsigned char *randVal, int len)
+{
+  static const unsigned int seed = 0x9E3779B9;
+  if(!randomInit) {
+    RAND_seed(&seed,sizeof(seed));
+    randomInit=true;
+    }
+  RAND_bytes(randVal,len);
+}
+
+//
+// Rotates the 8 bytes bitwise right
+//
+void cCamCrypt::RotateRight8Byte(unsigned char *key)
+{
+  unsigned char t1=key[0];
+  for(int k=7 ; k>=0 ; k--) {
+    unsigned char t2=t1<<7;
+    t1=key[k]; key[k]=(t1>>1) | t2;
+    }
+}
+
+//
+// Rotates the 8 bytes bitwise left
+//
+void cCamCrypt::RotateLeft8Byte(unsigned char *key)
+{
+  unsigned char t1=key[7];
+  for(int k=0 ; k<8 ; k++) {
+    unsigned char t2=t1>>7;
+    t1=key[k]; key[k]=(t1<<1) | t2;
+    }
+}
+
+void cCamCrypt::RevCamCrypt(const unsigned char *key, unsigned char *data)
+{
+  unsigned char localKey[8];
+  memcpy(localKey,key,sizeof(localKey));
+  for(int idx1=0 ; idx1<8 ; idx1++) {
+    for(int idx2=0 ; idx2<8 ; idx2++) {
+      const unsigned char tmp1=cryptTable[data[7] ^ localKey[idx2] ^ idx1];
+      const unsigned char tmp2=data[0];
+      data[0]=data[1];
+      data[1]=data[2];
+      data[2]=data[3];
+      data[3]=data[4];
+      data[4]=data[5];
+      data[5]=data[6] ^ tmp1;
+      data[6]=data[7];
+      data[7]=tmp1 ^ tmp2 ;
+      }
+    RotateLeft8Byte(localKey);
+    }
+}
+
+void cCamCrypt::CamCrypt(const unsigned char *key, unsigned char *data)
+{
+  unsigned char localKey[8];
+  memcpy(localKey,key,sizeof(localKey));
+  for(int idx1=0 ; idx1<7 ; idx1++) RotateLeft8Byte(localKey);
+  for(int idx1=7 ; idx1>=0 ; idx1--) {
+    for(int idx2=7 ; idx2>=0 ; idx2--) {
+      const unsigned char tmp1=cryptTable[data[6] ^ localKey[idx2] ^ idx1];
+      const unsigned char tmp2=data[0];
+      data[0]=data[7] ^ tmp1;
+      data[7]=data[6];
+      data[6]=data[5] ^ tmp1;
+      data[5]=data[4];
+      data[4]=data[3];
+      data[3]=data[2];
+      data[2]=data[1];
+      data[1]=tmp2;
+      }
+    RotateRight8Byte(localKey);
+    }
+}
+
+bool cCamCrypt::SetupCardFiles(unsigned char *data, int len, BIGNUM *exp, BIGNUM *mod)
+{
+  if(rsa.RSA(data,data,len,exp,mod,false)<=0 || (data[0]!=0x80 || data[11]!=0x40 || data[12]!=0x06))
+    return false;
+  BN_complement(data+13,64,cardMod);
+  BN_bin2bn(data+13+64,6,cardExp);
+  return true;
+}
+
+void cCamCrypt::PrepareCamMessage(unsigned char *plain)
+{
+/*
+  static const unsigned char camMsg[] = {
+    0x80,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,
+    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00
+    };
+  memcpy(plain,camMsg,sizeof(camMsg));
+*/
+  plain[0]=0x80;
+  memset(plain+1 ,0x00,7);
+  GenerateRandom(plain+16,16);
+  memcpy(plain+8,plain+16,8);
+  memset(plain+32,0xFF,31);
+  plain[63]=0x00;
+}
+
+bool cCamCrypt::EncryptCamMessage(unsigned char *encrypted, const unsigned char *plain)
+{
+  return rsa.RSA(encrypted,plain,64,cardExp,cardMod,false)>0;
+}
+
+// -- cSmartCardDataIrdeto -----------------------------------------------------
+
+class cSmartCardDataIrdeto : public cSmartCardData {
+public:
+  int acs, caid;
+  cBN mod, exp;
+  bool plain;
+  //
+  cSmartCardDataIrdeto(void);
+  cSmartCardDataIrdeto(int Acs, int Caid);
+  virtual bool Parse(const char *line);
+  virtual bool Matches(cSmartCardData *param);
+  };
+
+cSmartCardDataIrdeto::cSmartCardDataIrdeto(void)
+:cSmartCardData(SC_ID)
+{
+  plain=false;
+}
+
+cSmartCardDataIrdeto::cSmartCardDataIrdeto(int Acs, int Caid)
+:cSmartCardData(SC_ID)
+{
+  acs=Acs; caid=Caid;
+  plain=false;
+}
+
+bool cSmartCardDataIrdeto::Matches(cSmartCardData *param)
+{
+  cSmartCardDataIrdeto *cd=(cSmartCardDataIrdeto *)param;
+  return cd->acs==acs && (cd->caid==caid || caid==-1);
+}
+
+bool cSmartCardDataIrdeto::Parse(const char *line)
+{
+  unsigned char buff[512];
+  acs=caid=-1; // default
+  line=skipspace(line);
+  if(*line=='[') { // parse acs & caid
+    line++;
+    if(GetHex(line,buff,2)!=2) {
+      PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: acs");
+      return false;
+      }
+    acs=buff[0]*256+buff[1];
+
+    line=skipspace(line);
+    if(*line=='/') {
+      line++;
+      if(GetHex(line,buff,2)!=2) {
+        PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: caid");
+        return false;
+        }
+      caid=buff[0]*256+buff[1];
+      line=skipspace(line);
+      }
+
+    if(!*line==']') {
+      PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: closing ]");
+      return false;
+      }
+    line++;
+    }
+
+  line=skipspace(line);
+  if(!strncasecmp(line,"plain",5)) {
+    plain=true;
+    return true;
+    }
+  int l;
+  if((l=GetHex(line,buff,sizeof(buff),false))<=0) {
+    PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: mod");
+    return false;
+    }
+  BN_complement(buff,l,mod);
+  if((l=GetHex(line,buff,sizeof(buff),false))<=0) {
+    PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: exp");
+    return false;
+    }
+  BN_bin2bn(buff,l,exp);
+  return true;
+}
+
+// -- cSmartCardIrdeto ---------------------------------------------------------
+
+#define XOR_START    0x3F // Start value for xor checksumm
+#define ADDRLEN      4    // Address length in EMM commands
+#define MAX_PROV     16
+#define RECOVER_TIME 100  // Time in ms which the card needs to recover after
+                          // a failed command
+
+class cSmartCardIrdeto : public cSmartCard, private cCamCrypt, private cIdSet {
+private:
+  unsigned char buff[MAX_LEN+1];
+  unsigned char camKey[8];
+  char asciiSerial[22], coco[4];
+  int ACS, caId;
+  int numProv;
+  cTimeMs recoverTime;
+  //
+  int DoCmd(unsigned char *cmd, int goodSB, int secGoodSB=-1);
+  bool ReadCardInfo(void);
+  time_t Date(int date, char *buff, int len);
+public:
+  cSmartCardIrdeto(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  virtual bool CanHandle(unsigned short CaId);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x00,0x00 }, "Instruction executed without error", true },
+  { { 0x55,0x00 }, "Instruction executed without error", true },
+  { { 0x57,0x00 }, "CAM string rejected", false },
+  { { 0x58,0x00 }, "Instruction executed without error", true },
+  { { 0x9D,0x00 }, "Decoding successfull", true },
+  { { 0x90,0x00 }, "ChID missing. Not subscribed?", false },
+  { { 0x93,0x00 }, "ChID out of date. Subscription expired?", false },
+  { { 0x9C,0x00 }, "Master key error", false },
+  { { 0x9E,0x00 }, "Wrong decryption key", false },
+  { { 0x9F,0x00 }, "Missing key", false },
+  { { 0x70,0x00 }, "Wrong hex serial", false },
+  { { 0x71,0x00 }, "Wrong provider", false },
+  { { 0x72,0x00 }, "Wrong provider group", false },
+  { { 0x73,0x00 }, "Wrong provider group", false },
+  { { 0x7C,0x00 }, "Wrong signature", false },
+  { { 0x7D,0x00 }, "Masterkey missing", false },
+  { { 0x7E,0x00 }, "Wrong provider identifier", false },
+  { { 0x7F,0x00 }, "Invalid nano", false },
+  { { 0x54,0x00 }, "No more ChID's", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8N2,3000,100
+  };
+
+cSmartCardIrdeto::cSmartCardIrdeto(void)
+:cSmartCard(&cardCfg,msgs)
+{}
+
+bool cSmartCardIrdeto::Init(void)
+{
+  ResetIdSet();
+  recoverTime.Set(-RECOVER_TIME);
+  if(atr->histLen<6 || memcmp(atr->hist,"IRDETO",6)) {
+    PRINTF(L_SC_INIT,"doesn't looks like a Irdeto/Beta card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Irdeto smartcard\n");
+  int r;
+  static unsigned char getCountryCode[] = { 0x01,0x02,0x02,0x03,0x00,0x00,0xCC };
+  if((r=DoCmd(getCountryCode,0x0000))<=0 || !Status() || r<16) {
+    PRINTF(L_SC_ERROR,"country code error");
+    return false;
+    }
+  ACS=buff[8]*256+buff[9];
+  caId=buff[13]*256+buff[14];
+  memcpy(coco,&buff[21],3); coco[3]=0;
+  PRINTF(L_SC_INIT,"ACS Version %04x, CAID %04x, CoCo %s",ACS,caId,coco);
+  snprintf(idStr,sizeof(idStr),"%s (ACS %x)",SC_NAME,ACS);
+  infoStr.Printf("ACS: %04x CAID: %04x CoCo: %s\n",ACS,caId,coco);
+
+  static unsigned char getAsciiSerial[] = { 0x01,0x02,0x00,0x03,0x00,0x00,0xCC };
+  if((r=DoCmd(getAsciiSerial,0x0000))<=0 || !Status() || r<10) {
+    PRINTF(L_SC_ERROR,"ASCII serial error");
+    return false;
+    }
+  strn0cpy(asciiSerial,(char*)buff+8,sizeof(asciiSerial));
+  PRINTF(L_SC_INIT,"ASCII serial %s",asciiSerial);
+
+  static unsigned char getHexSerial[] = { 0x01,0x02,0x01,0x03,0x00,0x00,0xCC };
+  if((r=DoCmd(getHexSerial,0x0000))<=0 || !Status() || r<25) {
+    PRINTF(L_SC_ERROR,"hex serial error");
+    return false;
+    }
+  int numProv=buff[18];
+  SetCard(new cCardIrdeto(buff[23],&buff[20]));
+  PRINTF(L_SC_INIT,"Providers: %d HEX Serial: %02X%02X%02X  HEX Base: %02X",numProv,buff[20],buff[21],buff[22],buff[23]);
+  infoStr.Printf("HEX: %02X/%02X%02X%02X ASCII: %s\n",buff[23],buff[20],buff[21],buff[22],asciiSerial);
+
+  static unsigned char getCardFile[] = { 0x01,0x02,0x0E,0x02,0x00,0x00,0xCC };
+  unsigned char encr[128], plain[64+6+2];
+  PrepareCamMessage(plain+6);
+  getCardFile[3]=0x02;
+  if((r=DoCmd(getCardFile,0x0000))<=0 || !Status() || r<73) {
+    PRINTF(L_SC_ERROR,"cardfile2 error");
+    return false;
+    }
+  memcpy(encr,buff+8,64);
+  getCardFile[3]=0x03;
+  if((r=DoCmd(getCardFile,0x0000))<=0 || !Status() || r<73) {
+    PRINTF(L_SC_ERROR,"cardfile3 error");
+    return false;
+    }
+  memcpy(encr+64,buff+8,64);
+
+  bool doPlain=false;
+  if((ACS==0x0383 || ACS==0x0384) && atr->histLen>=12 && atr->hist[12]==0x95)
+    doPlain=true;
+  cSmartCardDataIrdeto *entry=0;
+  if(!doPlain) {
+    cSmartCardDataIrdeto cd(ACS,caId);
+    if(!(entry=(cSmartCardDataIrdeto *)smartcards.FindCardData(&cd))) {
+      PRINTF(L_GEN_WARN,"didn't find Irdeto card specific certificate, falling back to default");
+      cSmartCardDataIrdeto cd(-1,-1);
+      if(!(entry=(cSmartCardDataIrdeto *)smartcards.FindCardData(&cd))) {
+        PRINTF(L_GEN_WARN,"didn't find default Irdeto certificate, please add one");
+        if(ACS!=0x0384) return false;
+        PRINTF(L_GEN_WARN,"trying pre-coded ACS 384 challenge. This mode is DEPRECATED. There ARE valid certificates for these cards available!");
+        }
+      }
+    else doPlain=entry->plain;
+    }
+  static unsigned char doCamKeyExchange[] = { 0x01,0x02,0x09,0x03,0x00,0x40 };
+  if(doPlain) {
+    // plain challenge
+    memcpy(plain,doCamKeyExchange,sizeof(doCamKeyExchange));
+    plain[4]=1; // set block counter
+    r=DoCmd(plain,0x5500,0x0000);
+    }
+  else {
+    // RSA challenge
+    if(entry) {
+      if(!SetupCardFiles(encr,sizeof(encr),entry->exp,entry->mod)) {
+        PRINTF(L_SC_ERROR,"decrypting cardfiles failed. Probably bad certificate.");
+        return false;
+        }
+      if(!EncryptCamMessage(encr+6,plain+6)) {
+        PRINTF(L_SC_ERROR,"encrypting CAM message failed. Probably bad certificate.");
+        return false;
+        }
+
+      static unsigned char doRSACheck[] = { 0x01,0x02,0x11,0x00,0x00,0x40 };
+      memcpy(plain,doRSACheck,sizeof(doRSACheck));
+      if((r=DoCmd(plain,0x5800,0x0000))<=0 || !Status() || r<73) {
+        PRINTF(L_SC_ERROR,"card didn't give a proper reply (buggy RSA unit?), trying to continue...");
+        // non-fatal
+        }
+      if(r==73 && memcmp(encr+6,buff+8,64)) {
+        PRINTF(L_SC_ERROR,"card failed on RSA check, trying to continue...");
+        // non-fatal
+        }
+      }
+    else {
+      static const unsigned char enc384cz[] = {
+        0x18,0xD7,0x55,0x14,0xC0,0x83,0xF1,0x38,  0x39,0x6F,0xF2,0xEC,0x4F,0xE3,0xF1,0x85,
+        0x01,0x46,0x06,0xCE,0x7D,0x08,0x2C,0x74,  0x46,0x8F,0x72,0xC4,0xEA,0xD7,0x9C,0xE0,
+        0xE1,0xFF,0x58,0xE7,0x70,0x0C,0x92,0x45,  0x26,0x18,0x4F,0xA0,0xE2,0xF5,0x9E,0x46,
+        0x6F,0xAE,0x95,0x35,0xB0,0x49,0xB2,0x0E,  0xA4,0x1F,0x8E,0x47,0xD0,0x24,0x11,0xD0
+        };
+      static const unsigned char enc384dz[] = {
+        0x27,0xF2,0xD6,0xCD,0xE6,0x88,0x62,0x46,  0x81,0xB0,0xF5,0x3E,0x6F,0x13,0x4D,0xCC,
+        0xFE,0xD0,0x67,0xB1,0x93,0xDD,0xF4,0xDE,  0xEF,0xF5,0x3B,0x04,0x1D,0xE5,0xC3,0xB2,
+        0x54,0x38,0x57,0x7E,0xC8,0x39,0x07,0x2E,  0xD2,0xF4,0x05,0xAA,0x15,0xB5,0x55,0x24,
+        0x90,0xBB,0x9B,0x00,0x96,0xF0,0xCB,0xF1,  0x8A,0x08,0x7F,0x0B,0xB8,0x79,0xC3,0x5D
+        };
+      static const unsigned char ck[] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88 };
+      static const unsigned char hk[] = { 0x12,0x34,0x56,0x78,0x90,0xAB,0xCD,0xEF };
+
+      if(caId==0x1702) memcpy(encr+6,enc384cz,sizeof(enc384cz));
+      else if(caId==0x1722) memcpy(encr+6,enc384dz,sizeof(enc384dz));
+      else {
+        PRINTF(L_GEN_WARN,"no pre-coded Irdeto camkey challenge for caid %04x",caId);
+        return false;
+        }
+      memcpy((void *)CamKey(plain+6),ck,sizeof(ck));
+      memcpy((void *)HelperKey(plain+6),hk,sizeof(hk));
+      }
+
+    memcpy(encr,doCamKeyExchange,sizeof(doCamKeyExchange));
+    r=DoCmd(encr,0x5500,0x0000);
+    }
+
+  if(r>0 && Status() && r>=9) {
+    memcpy(camKey,CamKey(plain+6),8);
+    if(r>=17) {
+      RevCamCrypt(camKey,buff+8);
+      if(memcmp(HelperKey(plain+6),buff+8,8)) {
+        PRINTF(L_SC_ERROR,"camkey challenge failed");
+        return false;
+        }
+      }
+    LDUMP(L_SC_INIT,camKey,sizeof(camKey),"camkey");
+    }
+  else {
+    PRINTF(L_SC_ERROR,"camkey error");
+    return false;
+    }
+
+  static unsigned char getProvider[]  = { 0x01,0x02,0x03,0x03,0x00,0x00,0xCC };
+  static unsigned char getChanelIds[] = { 0x01,0x02,0x04,0x00,0x00,0x01,0x00,0xCC };
+  for(int i=0; i<numProv; i++) {
+    getProvider[4]=i;
+    if((r=DoCmd(getProvider,0x0000))>0 && Status() && r>=33) {
+      AddProv(new cProviderIrdeto(buff[8]&0x0f,&buff[9]));
+      PRINTF(L_SC_INIT,"provider %d with ProvBase 0x%02x ProvId 0x%02x%02x%02x",i,buff[8]&0x0f,buff[9],buff[10],buff[11]);
+      infoStr.Printf("Provider %d Id: %02X/%02X%02X%02X\n",i,buff[8]&0x0f,buff[9],buff[10],buff[11]);
+
+      getChanelIds[4]=i;
+      for(int l=0; l<10; l++) {
+        getChanelIds[6]=l;
+        if((r=DoCmd(getChanelIds,0x0000,0x5400))<=0 || !Status() || r<69) break;
+        for(int k=0; k<buff[7]; k+=6) {
+          int chanId=buff[k+8+0]*256+buff[k+8+1];
+          int date  =buff[k+8+2]*256+buff[k+8+3];
+          int durr  =buff[k+8+4];
+          if(chanId!=0x0000 && chanId!=0xFFFF) {
+            char sdate[16], edate[16];
+            Date(date,sdate,sizeof(sdate));
+            Date(date+durr,edate,sizeof(edate));
+            PRINTF(L_SC_INIT,"ChanId 0x%04x Date 0x%04x %s Duration 0x%02x %s",chanId,date,sdate,durr,edate);
+            infoStr.Printf("ChanId: %04X Date: %s-%s\n",chanId,sdate,edate);
+            }
+          }
+        }
+      }
+    }
+
+#if 0
+  static unsigned char getCountryCode2[] = { 0x01,0x02,0x0B,0x00,0x00,0x00,0xCC };
+  if((r=DoCmd(getCountryCode2,0x0000))>0 && Status() && r>=32) {
+    PRINTF(L_SC_INIT,"max ChID's %d,%d,%d,%d",buff[14],buff[15],buff[16],buff[17]);
+    }
+#endif
+
+  infoStr.Finish();
+  return true;
+}
+
+time_t cSmartCardIrdeto::Date(int date, char *buff, int len)
+{
+  // Irdeto date starts 01.08.1997 which is
+  // 870393600 seconds in unix calendar time
+  time_t utcTime=870393600L+date*(24*3600);
+  if(buff) {
+    struct tm utcTm;
+    gmtime_r(&utcTime,&utcTm);
+    snprintf(buff,len,"%04d/%02d/%02d",utcTm.tm_year+1900,utcTm.tm_mon+1,utcTm.tm_mday);
+    }
+  return utcTime;
+}
+
+bool cSmartCardIrdeto::CanHandle(unsigned short CaId)
+{
+  return (CaId==caId);
+}
+
+bool cSmartCardIrdeto::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static const unsigned char ecmCmd[] = { 0x01,0x05,0x00,0x00,0x02,0x00 };
+
+  int r=SCT_LEN(data)-6;
+  if(r<=255) {
+    unsigned char cmd[257+sizeof(ecmCmd)];
+    memcpy(cmd,ecmCmd,sizeof(ecmCmd));
+    cmd[5]=r;
+    memcpy(cmd+sizeof(ecmCmd),&data[6],r);
+    if((r=DoCmd(cmd,0x9D00))>0) {
+      if(Status() && r>=31) {
+        RevCamCrypt(camKey,&buff[14]);
+        RevCamCrypt(camKey,&buff[22]);
+        memcpy(cw,&buff[14],16);
+        return true;
+        }
+      }
+    }
+  return false;
+}
+
+bool cSmartCardIrdeto::Update(int pid, int caid, const unsigned char *data)
+{
+  static const unsigned char emmCmd[] = { 0x01,0x01,0x00,0x00,0x00,0x00 };
+
+  if(MatchEMM(data)) {
+    int len=cParseIrdeto::AddrLen(data)+1;
+    if(len<=ADDRLEN) {
+      const int dataLen=SCT_LEN(data)-5-len; // sizeof of data bytes (nanos)
+      if(dataLen<=255-ADDRLEN) {
+        unsigned char cmd[257+sizeof(emmCmd)];
+        memcpy(cmd,emmCmd,sizeof(emmCmd));
+        cmd[5]=dataLen+ADDRLEN;
+        memset(cmd+sizeof(emmCmd),0,ADDRLEN);
+        memcpy(cmd+sizeof(emmCmd),&data[3],len);
+        memcpy(cmd+sizeof(emmCmd)+ADDRLEN,&data[len+5],dataLen);
+        if(DoCmd(cmd,0x0000)>0 && Status()) return true;
+        }
+      }
+    else PRINTF(L_SC_ERROR,"addrlen %d > %d",len,ADDRLEN);
+    }
+  return false;
+}
+
+int cSmartCardIrdeto::DoCmd(unsigned char *cmd, int goodSB, int secGoodSB)
+{
+  int len=cmd[5]+6;
+  cmd[len]=XorSum(cmd,len) ^ XOR_START;
+  // wait until recover time is over
+  int r=RECOVER_TIME-recoverTime.Elapsed();
+  if(r>0) {
+    PRINTF(L_SC_ERROR,"recover time, waiting %d ms",r);
+    cCondWait::SleepMs(r+1);
+    }
+  r=-1;
+  LDUMP(L_CORE_SC,cmd,len+1,"IRDETO: CMD ->");
+  if(SerWrite(cmd,len+1)>0 && SerRead(buff,4,cardCfg.workTO)>0) {
+    len=4;
+    if(buff[0]==cmd[0] && buff[1]==cmd[1]) {
+      sb[0]=buff[2]; sb[1]=buff[3];
+      int SB=buff[2]*256+buff[3];
+      if(SB==goodSB || (secGoodSB>=0 && SB==secGoodSB)) {
+        if(SerRead(buff+len,5)>0) {
+          len+=5;
+          if(buff[7]) {
+            if(SerRead(buff+len,buff[7])<=0) return -1;
+            len+=buff[7];
+            }
+          if(XorSum(buff,len)==XOR_START) r=len;
+          else LDUMP(L_CORE_SC,buff,len,"IRDETO: checksum failed");
+          }
+        }
+      else r=len;
+      }
+    else {
+      sb[0]=buff[1]; sb[1]=buff[2];
+      r=3;
+      }
+    }
+  if(r>0) LDUMP(L_CORE_SC,buff,r,"IRDETO: RESP <-");
+  if(r<=4) {
+    recoverTime.Set();
+    PRINTF(L_SC_ERROR,"setting %d ms recover time",RECOVER_TIME);
+    }
+  return r;
+}
+
+// -- cSmartCardLinkIrdeto -----------------------------------------------------
+
+class cSmartCardLinkIrdeto : public cSmartCardLink {
+public:
+  cSmartCardLinkIrdeto(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardIrdeto(); }
+  virtual cSmartCardData *CreateData(void) { return new cSmartCardDataIrdeto; }
+  };
+
+static cSmartCardLinkIrdeto staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-irdeto/sc-irdeto.mk
new file mode 100644 (file)
index 0000000..8dfdd54
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Smartcard Irdeto/Beta
+#
+TARGET = sc_irdeto
+OBJS   = sc-irdeto.o
+LIBS   = -lcrypto
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.c
new file mode 100644 (file)
index 0000000..e0feeac
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "crypto.h"
+#include "helper.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_NAGRA         0x1801
+
+#define SYSTEM_NAME          "SC-Nagra"
+#define SYSTEM_PRI           -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_NAGRA)
+
+#define SC_NAME "Nagra"
+#define SC_ID   MAKE_SC_ID('N','a','g','r')
+
+#define L_SC        11
+#define L_SC_PROC   LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL    LALL(L_SC_PROC)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF|L_SC_PROC)&LOPT_MASK,
+  "sc-nagra",
+  { L_SC_DEFNAMES,"process" }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScNagra ---------------------------------------------------------------------
+
+class cSystemScNagra : public cSystemScCore {
+public:
+  cSystemScNagra(void);
+  };
+
+cSystemScNagra::cSystemScNagra(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Nagra")
+{
+  hasLogger=true;
+}
+
+// -- cSystemLinkScNagra --------------------------------------------------------------
+
+class cSystemLinkScNagra : public cSystemLink {
+public:
+  cSystemLinkScNagra(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScNagra; }
+  };
+
+static cSystemLinkScNagra staticInit;
+
+cSystemLinkScNagra::cSystemLinkScNagra(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScNagra::CanHandle(unsigned short SysId)
+{
+  return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cCamCryptNagra ------------------------------------------------------------------
+
+class cCamCryptNagra {
+private:
+  cIDEA idea;
+  cRSA rsa;
+  cBN camMod, camExp;
+  bool hasMod;
+  unsigned char sk[16];
+  //
+  void Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len);
+public:
+  cCamCryptNagra(void);
+  void InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data);
+  void SetCamMod(BIGNUM *m);
+  bool DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *key2, BIGNUM *m);
+  bool DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *in);
+  bool DecryptCW(unsigned char *dcw, const unsigned char *in);
+  };
+
+cCamCryptNagra::cCamCryptNagra(void)
+{
+  hasMod=false;
+  BN_set_word(camExp,3);
+}
+
+void cCamCryptNagra::Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len)
+{
+  unsigned char buff[16];
+  memcpy(buff,key,16);
+  for(int i=0; i<len; i+=8) {
+    IdeaKS ks;
+    idea.SetEncKey(buff,&ks);
+    idea.Encrypt(msg+i,8,buff,&ks,0);
+    xxor(buff,8,buff,&msg[i]);
+    //for(int j=7; j>=0; j--) buff[j]^=msg[i+j];
+    memcpy(&buff[8],buff,8);
+    }
+  memcpy(sig,buff,8);
+}
+
+void cCamCryptNagra::InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data)
+{
+  memcpy(key,bk,8);
+  int d=UINT32_LE(data);
+  BYTE4_LE(key+8 , d);
+  BYTE4_LE(key+12,~d);
+  //memcpy(key+8,data,4);
+  //for(int i=0; i<4; i++) key[i+12]=~data[i];
+}
+
+void cCamCryptNagra::SetCamMod(BIGNUM *m)
+{
+  BN_copy(camMod,m);
+  hasMod=true;
+}
+
+bool cCamCryptNagra::DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *cardid, BIGNUM *m)
+{
+  unsigned char buff[72];
+  if(rsa.RSA(buff,dt08+13,64,camExp,m)!=64) return false;
+  memcpy(buff+64,dt08+13+64,8);
+  buff[63]|=dt08[12]&0x80;
+  IdeaKS dks;
+  idea.SetDecKey(key,&dks);
+  idea.Decrypt(buff,72,&dks,0);
+  unsigned char sig[8];
+  memcpy(sig,buff,8);
+
+  memset(buff+0,0,4);
+  memcpy(buff+4,cardid,4);
+  Signature(buff,key,buff,72);
+  BN_bin2bn(buff+8,64,camMod);
+  if(memcmp(sig,buff,8)) {
+    PRINTF(L_SC_PROC,"DT08 signature failed. Check boxkey/IRD modulus");
+    return false;
+    }
+  hasMod=true;
+  return true;
+}
+
+bool cCamCryptNagra::DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *camdata)
+{
+  if(!hasMod) return false;
+  //decrypt $2A data here and prepare $2B reply
+  if(rsa.RSA(out,camdata,64,camExp,camMod)!=64) return false;
+  Signature(sk,key,out,32);
+  memcpy(sk+8,sk,8);
+  Signature(sk+8,sk,out,32);;
+  if(rsa.RSA(out,out,64,camExp,camMod)!=64) return false;
+  LDUMP(L_SC_PROC,sk,16,"established session key ->");
+  return true;
+}
+
+bool cCamCryptNagra::DecryptCW(unsigned char *dcw, const unsigned char *ecw)
+{
+  const int nCw=ecw[4]/2;
+  if(nCw<10) return false;
+  IdeaKS ks;
+  ecw+=5;
+  idea.SetDecKey(sk,&ks);
+  memcpy(dcw+8,ecw    +2,8);
+  memcpy(dcw+0,ecw+nCw+2,8);
+  idea.Decrypt(dcw+0,8,&ks,0);
+  idea.Decrypt(dcw+8,8,&ks,0);
+  return true;
+}
+
+// -- cSmartCardDataNagra ------------------------------------------------------
+
+class cSmartCardDataNagra : public cSmartCardData {
+public:
+  bool global;
+  int provId;
+  unsigned char bk[8];
+  cBN mod;
+  //
+  cSmartCardDataNagra(void);
+  cSmartCardDataNagra(int ProvId, bool Global);
+  virtual bool Parse(const char *line);
+  virtual bool Matches(cSmartCardData *param);
+  };
+
+cSmartCardDataNagra::cSmartCardDataNagra(void)
+:cSmartCardData(SC_ID) {}
+
+cSmartCardDataNagra::cSmartCardDataNagra(int ProvId, bool Global=false)
+:cSmartCardData(SC_ID)
+{
+  provId=ProvId; global=Global;
+}
+
+bool cSmartCardDataNagra::Matches(cSmartCardData *param)
+{
+  cSmartCardDataNagra *cd=(cSmartCardDataNagra *)param;
+  return (cd->provId==provId) && (cd->global==global);
+}
+
+bool cSmartCardDataNagra::Parse(const char *line)
+{
+  unsigned char buff[512];
+  line=skipspace(line);
+  if(*line++!='[' || GetHex(line,buff,2)!=2 || *(line=skipspace(line))!=']' ) {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: provider id");
+    return false;
+    }
+  provId=buff[0]*256+buff[1];
+  line=skipspace(line+1);
+  if(!strncasecmp(line,"IRDMOD",6)) {
+    global=true;
+    line+=6;
+    }
+  if(GetHex(line,bk,sizeof(bk),false)<=0) {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: boxkey");
+    return false;
+    }
+  int l=GetHex(line,buff,sizeof(buff),false);
+  if(l!=64) {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: %s",global?"IRDMOD":"CAMMOD");
+    return false;
+    }
+  BN_bin2bn(buff,l,mod);
+  return true;
+}
+
+// -- cSmartCardNagra -----------------------------------------------------------------
+
+// ISO 7816 T=1 defines
+#define NAD(a)      (a[0])
+#define PCB(a)      (a[1])
+#define LEN(a)      (a[2])
+#define CLA(a)      (a[3])
+#define INS(a)      (a[4])
+#define P1(a)       (a[5])
+#define P2(a)       (a[6])
+#define L(a)        (a[7])
+
+// Nagra NAD and reply NAD
+#define N2_NAD      0x21
+#define N2_R_NAD    sn8(N2_NAD)
+#define N2_CLA      0xA0
+#define N2_INS      0xCA
+#define N2_P1       0x00
+#define N2_P2       0x00
+#define N2_CMD(a)   (a[8])
+#define N2_CLEN(a)  (a[9]) 
+#define N2_DATA(a)  (a[10]) 
+
+#define SETIFS      0xC1
+#define CAMDATA     0x08
+
+// Datatypes
+#define IRDINFO     0x00
+#define TIERS       0x05
+#define MAX_REC     20
+
+// Card Status checks
+#define HAS_CW      ((status[1]&1)&&((status[3]&6)==6))
+
+class cSmartCardNagra : public cSmartCard, private cCamCryptNagra {
+private:
+  unsigned char buff[MAX_LEN+1], status[4], cardId[4], irdId[4], block;
+  unsigned short provId[MAX_REC], irdProvId;
+  //
+  bool GetCardStatus(void);
+  bool GetDataType(unsigned char dt, int len, int shots=MAX_REC);
+  bool ParseDataType(unsigned char dt);
+  bool DoCamExchange(const unsigned char *key);
+  void Date(const unsigned char *date, char *dt, char *ti);
+  bool DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data=0);
+  bool SetIFS(unsigned char len);
+public:
+  cSmartCardNagra(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x6F,0x00 }, "Instruction not supported", false },
+  { { 0x90,0x00 }, "Instruction executed without errors", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8O2,500,800
+  };
+
+cSmartCardNagra::cSmartCardNagra(void)
+:cSmartCard(&cardCfg,msgs)
+{
+  block=0;
+}
+
+bool cSmartCardNagra::SetIFS(unsigned char size)
+{
+  unsigned char buf[5];
+  // NAD, PCB, LEN
+  NAD(buf)=N2_NAD; PCB(buf)=SETIFS; LEN(buf)=1;
+  // Information Field size
+  buf[3]=size; buf[4]=XorSum(buf,4);
+  if(SerWrite(buf,5)!=5) {
+    PRINTF(L_SC_ERROR,"failed to write IFS command");
+    return false;
+    }
+  if(SerRead(buff,5,cardCfg.workTO)!=5 || XorSum(buff,5)) {
+    PRINTF(L_SC_ERROR,"setting IFS to %02x failed",size);
+    return false;
+    }
+  return true;
+}
+
+bool cSmartCardNagra::GetCardStatus(void)
+{
+  if(!DoBlkCmd(0xC0,0x02,0xB0,0x06) || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to read card status");
+    return false;
+    }
+  memcpy(status,buff+5,4);
+  return true;
+}
+
+bool cSmartCardNagra::Init(void)
+{
+  static const unsigned char verifyBytes[] = { 'D','N','A','S','P','1' };
+  if(atr->T!=1 || (atr->histLen<8 && memcmp(atr->hist,verifyBytes,sizeof(verifyBytes)))) {
+    PRINTF(L_SC_INIT,"doesn't look like a nagra V2 card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Nagra smartcard\n");
+
+  char rom[12], rev[12];
+  snprintf(rom,sizeof(rom),"%.3s",(char*)&atr->hist[5]);
+  snprintf(rev,sizeof(rev),"%.3s",(char*)&atr->hist[12]);
+
+  PRINTF(L_SC_INIT,"card version: %s revision: %s",rom,rev);
+  infoStr.Printf("Card v.%s Rev.%s\n",rom,rev);
+
+  if(!GetCardStatus() || !SetIFS(0x80)) return false;
+  // get UA/CardID here
+  if(!DoBlkCmd(0x12,0x02,0x92,0x06) || !Status()) return false;
+
+  memcpy(cardId,buff+5,4);
+  if(!GetDataType(IRDINFO,0x39) || !GetDataType(0x04,0x44)) return false;
+  infoStr.Printf("Tiers\n");
+  infoStr.Printf("|id  |tier low|tier high| dates    |\n");
+  infoStr.Printf("+----+--------+---------+----------+\n");
+
+  if(!GetDataType(TIERS,0x57))
+    PRINTF(L_SC_ERROR,"failed to get tiers");
+    
+  if(!GetDataType(CAMDATA,0x55,1)) return false;
+
+  unsigned char key[16];
+  cSmartCardDataNagra *entry=0;
+  bool sessOk=false;
+  if(buff[5]!=0 && irdProvId==((buff[10]*256)|buff[11])) { // prepare DT08 data
+    cSmartCardDataNagra cd(irdProvId,true);
+    if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
+      PRINTF(L_GEN_WARN,"didn't find smartcard Nagra IRD modulus");
+      }
+    else {
+      InitCamKey(key,entry->bk,irdId);
+      if(DecryptCamMod(buff+3,key,cardId,entry->mod)) sessOk=true;
+      }
+    }
+  else {
+    cSmartCardDataNagra cd(irdProvId);
+    if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
+      PRINTF(L_GEN_WARN,"didn't find smartcard Nagra CAM modulus");
+      }
+    else {
+      InitCamKey(key,entry->bk,cardId);
+      SetCamMod(entry->mod);
+      if(GetDataType(0x08,0x03,1)) sessOk=true;;
+      }
+    }
+  if(sessOk) DoCamExchange(key);
+  infoStr.Finish();
+  return true;
+}
+
+bool cSmartCardNagra::GetDataType(unsigned char dt, int len, int shots)
+{
+  for(int i=0; i<shots; i++) {
+    if(!DoBlkCmd(0x22,0x03,0xA2,len,&dt) || !Status()) {
+     PRINTF(L_SC_ERROR,"failed to get datatype %02X",dt);
+     return false;
+     }
+    if(buff[5]==0) return true;
+    if(!ParseDataType(dt&0x0F)) return false;
+    dt|=0x80; // get next item
+    }
+  return true;
+}
+
+bool cSmartCardNagra::ParseDataType(unsigned char dt)
+{
+  switch(dt) {
+   case IRDINFO:
+     {
+     irdProvId=(buff[10]*256)|buff[11];
+     char id[12];
+     memcpy(irdId,buff+17,4);
+     LDUMP(L_SC_INIT,irdId,4,"IRD system-ID: %04X, IRD ID:",irdProvId);
+     HexStr(id,irdId,sizeof(irdId));
+     infoStr.Printf("IRD system-ID: %04X\nIRD ID: %s\n",irdProvId,id);
+     return true;
+     }
+   case TIERS:
+     if(buff[7]==0x88 || buff[7]==0x08  || buff[7]==0x0C) {
+       int id=(buff[10]*256)|buff[11];
+       int tLowId=(buff[20]*256)|buff[21];
+       int tHiId=(buff[31]*256)|buff[32];
+       char date1[12], time1[12], date2[12], time2[12];
+       Date(buff+23,date1,time1);
+       Date(buff+27,date2,time2);
+
+       infoStr.Printf("|%04X|%04X    |%04X     |%s|\n",id,tLowId,tHiId,date1);
+       infoStr.Printf("|    |        |         |%s|\n",date2);
+       PRINTF(L_SC_INIT,"|%04X|%04X|%04X|%s|%s|",id,tLowId,tHiId,date1,time1);
+       PRINTF(L_SC_INIT,"|    |    |    |%s|%s|",date2,time2);
+       }
+   default:
+     return true;
+   }
+  return false;
+}
+
+bool cSmartCardNagra::DoCamExchange(const unsigned char *key)
+{
+  if(!GetCardStatus()) return false;
+  if(!DoBlkCmd(0x2A,0x02,0xAA,0x42) || !Status()) return false;
+  if(buff[4]!=64) {
+    PRINTF(L_SC_ERROR,"reading CAM 2A data failed");
+    return false;
+    }
+  unsigned char res[64];
+  if(!DecryptCamData(res,key,buff+5)) return false;
+  if(!DoBlkCmd(0x2B,0x42,0xAB,0x02,res) || !Status()) return false;
+  return true;
+}
+
+void cSmartCardNagra::Date(const unsigned char *data, char *dt, char *ti)
+{
+  int date=(data[0]<<8)|data[1];
+  int time=(data[2]<<8)|data[3];
+  struct tm t;
+  memset(&t,0,sizeof(struct tm));
+  t.tm_min =0;//-300;
+  t.tm_year=92;
+  t.tm_mday=date + 1;
+  t.tm_sec =(time - 1) * 2;
+  mktime(&t);
+  snprintf(dt,11,"%.2d.%.2d.%.4d",t.tm_mon+1,t.tm_mday,t.tm_year+1900);
+  snprintf(ti,9,"%.2d:%.2d:%.2d",t.tm_hour,t.tm_min,t.tm_sec);
+}
+
+bool cSmartCardNagra::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  if(DoBlkCmd(data[3],data[4]+2,0x87,0x02,data+3+2) && Status()) {
+    cCondWait::SleepMs(100);
+    if(GetCardStatus() && HAS_CW &&
+       DoBlkCmd(0x1C,0x02,0x9C,0x36) && Status() &&
+       DecryptCW(cw,buff)) return true;
+    }
+  return false;
+}
+
+bool cSmartCardNagra::Update(int pid, int caid, const unsigned char *data)
+{
+  if(DoBlkCmd(data[8],data[9]+2,0x84,0x02,data+8+2) && Status()) {
+    cCondWait::SleepMs(500);
+    if(GetCardStatus()) return true;
+    }
+  return false;
+}
+
+bool cSmartCardNagra::DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data)
+{
+  unsigned char msg[MAX_LEN+3+1];
+
+  NAD(msg)=N2_NAD; PCB(msg)=0; PCB(msg)^=block; block^=0x40; LEN(msg)=ilen+6;
+  CLA(msg)=N2_CLA; INS(msg)=N2_INS; P1(msg)=N2_P1; P2(msg)=N2_P2; L(msg)=ilen;
+
+  int dlen=ilen-2;
+  if(dlen<0) {
+    PRINTF(L_SC_ERROR,"invalid data length encountered");
+    return false;
+    }
+
+  N2_CMD(msg)=cmd; N2_CLEN(msg)=dlen;
+
+  if(data && dlen>0) memcpy(&N2_DATA(msg),data,dlen);
+  int msglen=LEN(msg)+3;
+  msg[msglen-1]=rlen;
+  msg[msglen]=XorSum(msg,msglen);
+  msglen++;
+
+  if(SerWrite(msg,msglen)==msglen) {
+    LDUMP(L_CORE_SC,msg,msglen,"NAGRA: <-");
+    cCondWait::SleepMs(10);
+    if(SerRead(buff,3,cardCfg.workTO)!=3) {
+      PRINTF(L_SC_ERROR,"reading back reply failed");
+      return false;
+      }
+    int reslen=buff[2]+1;
+    if(SerRead(buff+3,reslen,cardCfg.workTO)!=reslen) {
+      PRINTF(L_SC_ERROR,"reading back information block failed");
+      return false;
+      }
+    if(XorSum(buff,reslen+3)) {
+      PRINTF(L_SC_ERROR,"checksum failed");
+      return false;
+      }
+    LDUMP(L_CORE_SC,buff,3+reslen,"NAGRA: ->");
+
+    if(buff[3]!=res) {
+      PRINTF(L_SC_ERROR,"result not expected (%02X != %02X)",buff[3],res);
+      return false;
+      }
+    memcpy(sb,&buff[3+rlen],2);
+    LDUMP(L_CORE_SC,sb,2,"NAGRA: ->");
+    cCondWait::SleepMs(10);
+    return true;
+    }
+  return false;
+}
+
+// -- cSmartCardLinkNagra -------------------------------------------------------------
+
+class cSmartCardLinkNagra : public cSmartCardLink {
+public:
+  cSmartCardLinkNagra(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardNagra(); }
+  virtual cSmartCardData *CreateData(void) { return new cSmartCardDataNagra; }
+  };
+
+static cSmartCardLinkNagra staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-nagra/sc-nagra.mk
new file mode 100644 (file)
index 0000000..266a409
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard Nagra
+#
+TARGET = sc_nagra
+OBJS   = sc-nagra.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.c
new file mode 100644 (file)
index 0000000..763da5f
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "misc.h"
+#include "opts.h"
+#include "parse.h"
+#include "log-sc.h"
+
+#define SYSTEM_SECA          0x0100
+
+#define SYSTEM_NAME          "SC-Seca"
+#define SYSTEM_PRI           -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)
+
+#define SC_NAME "Seca"
+#define SC_ID   MAKE_SC_ID('S','e','c','a')
+
+#define SECADATE(buff,len,odate) { \
+                                 const unsigned char *dd=(odate); \
+                                 snprintf(buff,len,"%04d/%02d/%02d", \
+                                   ((dd[0]&0xFE)>>1)+1990, \
+                                   ((dd[0]&0x01)<<3) + ((dd[1]&0xE0)>>5), \
+                                   dd[1]&0x1F); \
+                                 }
+
+#define L_SC        12
+#define L_SC_PROC   LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL    LALL(L_SC_PROC)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-seca",
+  { L_SC_DEFNAMES,"process" }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+static int blocker=0;
+static int ppv=false;
+
+// -- cSystemScSeca ---------------------------------------------------------------
+
+class cSystemScSeca : public cSystemScCore {
+public:
+  cSystemScSeca(void);
+  };
+
+cSystemScSeca::cSystemScSeca(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Seca")
+{
+  hasLogger=true;
+  needsDescrData=true;
+}
+
+// -- cSystemLinkScSeca --------------------------------------------------------
+
+static const char *block[] = {
+  trNOOP("allow ALL"),
+  trNOOP("block UNIQUE"),
+  trNOOP("block SHARED"),
+  trNOOP("block ALL")
+  };
+
+class cSystemLinkScSeca : public cSystemLink {
+public:
+  cSystemLinkScSeca(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScSeca; }
+  };
+
+static cSystemLinkScSeca staticInit;
+
+cSystemLinkScSeca::cSystemLinkScSeca(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  opts=new cOpts(SYSTEM_NAME,2);
+  opts->Add(new cOptSel("Blocker",trNOOP("SC-Seca: EMM updates"),&blocker,sizeof(block)/sizeof(char *),block));
+  cOpt *opt=new cOptBool("Ppv",trNOOP("SC-Seca: activate PPV"),&ppv);
+  if(opt) opt->Persistant(false);
+  opts->Add(opt);
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScSeca::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cProviderScSeca ----------------------------------------------------------
+
+class cProviderScSeca : public cProviderSeca {
+public:
+  int index;
+  unsigned char date[2], pbm[8];
+  char name[17];
+  //
+  cProviderScSeca(const unsigned char *pi, const unsigned char *s):cProviderSeca(pi,s) {}
+  };
+
+// -- cSmartCardSeca -----------------------------------------------------------
+
+struct SecaProvInfo {
+  unsigned char prov[2];
+  char name[16];
+  unsigned char sa[3];
+  unsigned char cb;
+  unsigned char date[2];
+  unsigned char rr;
+  unsigned char rstart;
+  unsigned char pbm[8];
+  unsigned char rend;
+  };
+
+struct SecaChannelInfo {
+  unsigned char pbm[8];
+  unsigned char date[2];
+  };
+
+class cSmartCardSeca : public cSmartCard, private cIdSet {
+private:
+  char datebuff[16];
+  //
+  const char *Date(const unsigned char *date);
+  bool CheckAccess(const unsigned char *data, const cProviderScSeca *p);
+public:
+  cSmartCardSeca(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  };
+
+static const struct StatusMsg msgs[] = {
+  // ECM status messages
+  { { 0x90,0x00 }, "Instruction executed without errors", true },
+  { { 0x90,0x02 }, "Signature failed", false },
+  { { 0x90,0x27 }, "Decoding the preview (non-error)", true },
+  { { 0x93,0x02 }, "No access, check your subscription", false },
+  { { 0x96,0x00 }, "Chain of nonvalid entrance or null event or all the nanos process, none decoding", false },
+  // EMM update status messages
+  { { 0x90,0x09 }, "Card update was not meant for this card", false },
+  { { 0x90,0x19 }, "Card update successfull, PPUA updated", true },
+  { { 0x97,0x00 }, "Card update was successful", true },
+  { { 0x97,0x40 }, "Card update was successful", true },
+  { { 0x97,0x50 }, "Card update was successful", true },
+  { { 0x97,0x78 }, "Card update was not necessary", false },
+  { { 0x97,0xe0 }, "EEPROM update was not necessary", false },
+  // unknown message
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8E2,2000,300
+  };
+
+cSmartCardSeca::cSmartCardSeca(void)
+:cSmartCard(&cardCfg,msgs)
+{}
+
+bool cSmartCardSeca::Init(void)
+{
+  static unsigned char ins0e[] = { 0xC1,0x0e,0x00,0x00,0x08 }; // get serial nr. (UA)
+  static unsigned char ins16[] = { 0xC1,0x16,0x00,0x00,0x07 }; // get nr. of providers
+  static unsigned char ins12[] = { 0xC1,0x12,0x00,0x00,0x19 }; // get provider info
+  static unsigned char ins34[] = { 0xC1,0x34,0x00,0x00,0x03 }; // request provider pbm (data=0x00 0x00 0x00)
+  static unsigned char ins32[] = { 0xC1,0x32,0x00,0x00,0x0A }; // get the pbm data (p1=provider)
+
+  static const unsigned char atrTester[] = { 0x0E,0x6C,0xB6,0xD6 };
+  if(atr->T!=0 || atr->histLen<7 || memcmp(&atr->hist[3],atrTester,4)) {
+    PRINTF(L_SC_INIT,"doesn't looks like a Seca card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Seca smartcard\n");
+  const char *type;
+  switch(atr->hist[0]*256+atr->hist[1]) {
+    case 0x5084: type="Generic"; break;
+    case 0x5384: type="Philips"; break;
+    case 0x5130:
+    case 0x5430:
+    case 0x5760: type="Thompson"; break;
+    case 0x5284:
+    case 0x5842:
+    case 0x6060: type="Siemens"; break;
+    case 0x7070: type="Canal+ NL"; break;
+    default:     type="Unknown"; break;
+    }
+  snprintf(idStr,sizeof(idStr),"%s (%s %d.%d)",SC_NAME,type,atr->hist[2]&0x0F,atr->hist[2]>>4);
+  PRINTF(L_SC_INIT,"cardtype: %s %d.%d",type,atr->hist[2]&0x0F,atr->hist[2]>>4);
+  
+  ResetIdSet();
+  unsigned char buff[MAX_LEN];
+  if(!IsoRead(ins0e,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"reading card serial failed");
+    return false;
+    }
+  SetCard(new cCardSeca(&buff[2]));
+  PRINTF(L_SC_INIT,"card serial number: %llu",Bin2LongLong(&buff[2],6));
+  infoStr.Printf("Type: %s %d.%d  Serial: %llu\n",type,atr->hist[2]&0x0F,atr->hist[2]>>4,Bin2LongLong(&buff[2],6));
+
+  if(!IsoRead(ins16,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"reading provider map failed");
+    return false;
+    }
+  int provMap=buff[2]*256+buff[3];
+  if(LOG(L_SC_INIT)) {
+    int n=0, i=provMap;
+    do { n+=i&1; i>>=1; } while(i);
+    PRINTF(L_SC_INIT,"card has %d providers (0x%04x)",n,provMap);
+    }
+
+  for(int i=0 ; i<16 ; i++) {
+    if(provMap&(1<<i)) {
+      PRINTF(L_SC_INIT,"reading info for provider index %d",i);
+      ins12[2]=i;
+      if(!IsoRead(ins12,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"reading provider info failed");
+        return false;
+        }
+      ins32[2]=i;
+      static const unsigned char ins34data[] = { 0x00,0x00,0x00 };
+      if(!IsoWrite(ins34,ins34data) || !Status() ||
+         !IsoRead(ins32,&buff[ins12[4]]) || !Status())
+        memset(&buff[ins12[4]],0xFF,ins32[4]); // fake PBM response if card doesn't support command
+
+      struct SecaProvInfo *spi=(struct SecaProvInfo *)buff;
+      cProviderScSeca *p=new cProviderScSeca(spi->prov,spi->sa);
+      if(p) {
+        AddProv(p);
+        p->index=i;
+        strn0cpy(p->name,spi->name,sizeof(p->name));
+        memcpy(p->date,spi->date,sizeof(p->date));
+        memcpy(p->pbm,spi->pbm,sizeof(p->pbm));
+        }
+      infoStr.Printf("Prov %x (%.16s) until %s\n",p->provId[0]*256+p->provId[1],p->name,Date(p->date));
+      char str[20];
+      PRINTF(L_SC_INIT,"provider 0x%02x%02x '%.16s' expires %s pbm %s",
+             p->provId[0],p->provId[1],p->name,Date(p->date),HexStr(str,p->pbm,sizeof(p->pbm)));
+      }
+    }
+
+  infoStr.Finish();
+  return true;
+}
+
+const char *cSmartCardSeca::Date(const unsigned char *date)
+{
+  SECADATE(datebuff,sizeof(datebuff),date);
+  return datebuff;
+}
+
+bool cSmartCardSeca::CheckAccess(const unsigned char *data, const cProviderScSeca *p)
+{
+  struct SecaChannelInfo *sci=(struct SecaChannelInfo*)data;
+  if(sci->date[0]>p->date[0] || (sci->date[0]==p->date[0] && sci->date[1]>p->date[1]))
+    return false;
+  char str[20];  
+  PRINTF(L_SC_PROC,"channelinfo date %s pbm %s",Date(sci->date),HexStr(str,sci->pbm,sizeof(sci->pbm)));
+  int result=0;
+  for(int i=7; i>=0; i--) // check pbm (is this right? seems to work though)
+    result|=(sci->pbm[i]&p->pbm[i]);
+  return (result!=0);
+}
+
+bool cSmartCardSeca::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static unsigned char ins3c[] = { 0xC1,0x3c,0x00,0x00,0x00 }; // coding cw
+  static unsigned char ins3a[] = { 0xC1,0x3a,0x00,0x00,0x10 }; // decoding cw    
+  static unsigned char ins30[] = { 0xC1,0x30,0x00,0x02,0x09 }; // view ppv (active bx record)
+  static unsigned char ins30data[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF }; 
+
+  cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
+  if(p && ecm->Data()) {
+    PRINTF(L_SC_PROC,"provider 0x%04x index %d '%.16s' (expires %s)",cParseSeca::ProvId(data),p->index,p->name,Date(p->date));
+    if(CheckAccess(ecm->Data(),p)) {
+      if(ppv) {
+        PRINTF(L_SC_PROC,"activating PPV");
+        if(IsoWrite(ins30,ins30data)) Status();
+        ppv=false;
+        }
+      const unsigned char *payload;
+      ins3c[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
+      ins3c[3]=cParseSeca::KeyNr(data);
+      ins3c[4]=cParseSeca::Payload(data,&payload);
+      if(IsoWrite(ins3c,payload) && Status() &&
+         IsoRead(ins3a,cw) && Status()) return true;
+      }
+    else PRINTF(L_SC_ERROR,"update your subscription to view this channel");
+    }
+  return false;
+}
+
+bool cSmartCardSeca::Update(int pid, int caid, const unsigned char *data)
+{
+  static unsigned char ins40[] = { 0xC1,0x40,0x00,0x00,0x00 }; 
+
+  if(blocker==0 || (data[0]==0x82 && blocker==2) || (data[0]==0x84 && blocker==1)) {
+    cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
+    if(p && MatchEMM(data)) {
+      PRINTF(L_SC_PROC,"got %s update",data[0]==0x82?"UNIQUE":"SHARED");
+      const unsigned char *payload;
+      ins40[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
+      ins40[3]=cParseSeca::KeyNr(data);
+      ins40[4]=cParseSeca::Payload(data,&payload);
+      if(IsoWrite(ins40,payload) && Status()) return Init();
+      }
+    }
+  return false;
+}
+
+// -- cSmartCardLinkSeca -------------------------------------------------------
+
+class cSmartCardLinkSeca : public cSmartCardLink {
+public:
+  cSmartCardLinkSeca(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardSeca(); }
+  };
+
+static cSmartCardLinkSeca staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-seca/sc-seca.mk
new file mode 100644 (file)
index 0000000..7f5e09c
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard Seca
+#
+TARGET = sc_seca
+OBJS   = sc-seca.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.c
new file mode 100644 (file)
index 0000000..af4395e
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_VIACCESS      0x0500
+
+#define SYSTEM_NAME          "SC-Viaccess"
+#define SYSTEM_PRI           -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)
+
+#define SC_NAME "Viaccess"
+#define SC_ID   MAKE_SC_ID('V','i','a','s')
+
+#define L_SC        13
+#define L_SC_ALL    LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-viaccess",
+  { L_SC_DEFNAMES }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScViaccess ------------------------------------------------------------------
+
+class cSystemScViaccess : public cSystemScCore { //, private cTPS {
+public:
+  cSystemScViaccess(void);
+  virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len); 
+  };
+
+cSystemScViaccess::cSystemScViaccess(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Viaccess")
+{
+  hasLogger=true;
+}
+
+void cSystemScViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len)
+{
+  const int pid=WORD(data,2,0x1FFF);
+  if(pid>=0xAA && pid<=0xCF) {
+    PRINTF(L_CORE_ECMPROC,"sc-viaccess: dropped \"fake\" ecm pid 0x%04xn",pid);
+    return;
+    }
+  cSystem::ParseCADescriptor(ecms,sysId,source,data,len);
+}
+
+// -- cSystemLinkScViaccess --------------------------------------------------------------
+
+class cSystemLinkScViaccess : public cSystemLink {
+public:
+  cSystemLinkScViaccess(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScViaccess; }
+  };
+
+static cSystemLinkScViaccess staticInit;
+
+cSystemLinkScViaccess::cSystemLinkScViaccess(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScViaccess::CanHandle(unsigned short SysId)
+{
+  return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cProviderScViaccess ----------------------------------------------------------
+
+class cProviderScViaccess : public cProviderViaccess {
+public:
+  unsigned char availKeys[16];
+  char name[33];
+  //
+  cProviderScViaccess(const unsigned char *id, const unsigned char *s):cProviderViaccess(id,s) {}
+  };
+
+// -- cSmartCardViaccess -----------------------------------------------------------------
+
+class cSmartCardViaccess : public cSmartCard, public cIdSet {
+private:
+  unsigned char lastId[3];
+  //
+  unsigned char GetSW1(void) { return sb[1]; }
+  bool CheckKey(cProviderScViaccess *p, const unsigned char keynr);
+  bool SetProvider(const unsigned char *id);
+public:
+  cSmartCardViaccess(void);
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x6b,0x00 }, "Instruction not supported", false },
+  { { 0x6d,0x00 }, "Instruction not supported", false },
+  { { 0x90,0x00 }, "Instruction executed without errors", true },
+  { { 0x90,0x08 }, "Instruction executed without errors", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8O2,500,300
+  };
+
+cSmartCardViaccess::cSmartCardViaccess(void)
+:cSmartCard(&cardCfg,msgs)
+{
+  memset(lastId,0,sizeof(lastId));
+}
+
+bool cSmartCardViaccess::Init(void)
+{
+  static const unsigned char verifyBytes[] = { 0x90,0x00 };
+  if(atr->T!=0 || atr->histLen<7 || memcmp(&atr->hist[5],verifyBytes,sizeof(verifyBytes))) {
+    PRINTF(L_SC_INIT,"doesn't look like a Viaccess card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Viaccess smartcard\n");
+  const char *ver=0;
+  switch((atr->hist[3]<<8)|atr->hist[4]) {
+    case 0x6268: ver="2.3"; break;
+    case 0x6468:
+    case 0x6668: ver="2.4"; break;
+    case 0xa268: ver="2.5"; break;
+    case 0xc168: ver="2.6"; break;
+    default: ver="unknown"; break;
+    }
+      
+  PRINTF(L_SC_INIT,"card v.%s",ver);
+  snprintf(idStr,sizeof(idStr),"%s (V.%s)",SC_NAME,ver);
+
+  static unsigned char insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data
+  static unsigned char insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data
+  static unsigned char insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer
+  static unsigned char insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item
+  unsigned char buff[MAX_LEN];
+
+  ResetIdSet();
+  insac[2]=0xa4; // request unique id
+  if(!IsoWrite(insac,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to request ua");
+    return false;
+    }
+  insb8[4]=0x07; // read unique id
+  if(!IsoRead(insb8,buff) || !Status() || buff[1]!=0x05) {
+    PRINTF(L_SC_ERROR,"failed to read ua");
+    return false;
+    }
+  SetCard(new cCardViaccess(&buff[2]));
+  PRINTF(L_SC_INIT,"card UA: %llu",Bin2LongLong(&buff[2],5));
+  infoStr.Printf("Card v.%s UA %010llu\n",ver,Bin2LongLong(&buff[2],5));
+
+  insa4[2]=0x00; // select issuer 0
+  if(!IsoWrite(insa4,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to select issuer 0");
+    return false;
+    }
+  do {
+    insc0[4]=0x1a; // show provider properties
+    if(!IsoRead(insc0,buff) || !Status()) {
+      PRINTF(L_SC_ERROR,"failed to read prov properties");
+      return false;
+      }
+
+    unsigned char buff2[MAX_LEN];
+    insac[2]=0xa5; // request sa
+    if(!IsoWrite(insac,buff2) || !Status()) {
+      PRINTF(L_SC_ERROR,"failed to request sa");
+      return false;
+      }
+    insb8[4]=0x06; // read sa
+    if(!IsoRead(insb8,buff2) || !Status()) {
+      PRINTF(L_SC_ERROR,"failed to read sa");
+      return false;
+      }
+    cProviderScViaccess *p=new cProviderScViaccess(&buff[0],&buff2[2]);
+    if(p) {
+      AddProv(p);
+      memcpy(p->availKeys,buff+10,sizeof(p->availKeys));
+
+      insac[2]=0xa7; // request name
+      if(!IsoWrite(insac,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"failed to request prov name");
+        return false;
+        }
+      insb8[4]=0x02; // read name nano + len
+      if(!IsoRead(insb8,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"failed to read prov name length");
+        return false;
+        }
+      unsigned int nameLen=buff[1];
+      if(nameLen>=sizeof(p->name)) {
+        PRINTF(L_SC_ERROR,"provider name buffer overflow");
+        nameLen=sizeof(p->name)-1;
+        }
+      insb8[4]=nameLen;
+      if(!IsoRead(insb8,buff) || !Status()) {
+        PRINTF(L_SC_ERROR,"failed to read prov name");
+        return false;
+        }
+      memcpy(p->name,buff,nameLen); p->name[nameLen]=0;
+
+      PRINTF(L_SC_INIT,"provider %06x (%s)",(int)p->ProvId(),p->name);
+      infoStr.Printf("Prov %06x (%s) SA %08u\n",(int)p->ProvId(),p->name,Bin2Int(&buff2[2],4));
+      }
+    else PRINTF(L_SC_ERROR,"no memory for provider");
+
+    insa4[2]=0x02; // next issuer
+    if(!IsoWrite(insa4,buff) || !Status()) {
+      PRINTF(L_SC_ERROR,"failed to select next issuer");
+      return false;
+      }
+    } while(GetSW1()==0x00);
+  infoStr.Finish();
+  return true;
+}
+
+bool cSmartCardViaccess::CheckKey(cProviderScViaccess *p, const unsigned char keynr)
+{
+  for(unsigned int j=0; j<sizeof(p->availKeys); j++)
+    if(p->availKeys[j]==keynr) return true;
+  return false;
+}
+
+bool cSmartCardViaccess::SetProvider(const unsigned char *id)
+{
+  static const unsigned char insa4[] = { 0xca,0xa4,0x04,0x00,0x03 };
+
+  if(id[0]!=lastId[0] || id[1]!=lastId[1] || (id[2]&0xF0)!=lastId[2]) {
+    memcpy(lastId,id,3);
+    lastId[2]&=0xF0;
+    if(!IsoWrite(insa4,lastId) || !Status()) return false;
+    }
+  return true;
+}
+
+bool cSmartCardViaccess::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static unsigned char ins88[] = { 0xca,0x88,0x00,0x00,0x00 }; // set ecm
+  static unsigned char insf8[] = { 0xca,0xf8,0x00,0x00,0x00 }; // set geographic info 
+  static unsigned char insc0[] = { 0xca,0xc0,0x00,0x00,0x12 }; // read dcw
+
+  cProviderScViaccess *p=(cProviderScViaccess *)FindProv(data);
+  if(p) {
+    unsigned char keynr=cParseViaccess::KeyNr(data);
+    if(CheckKey(p,keynr)) {
+      if(!SetProvider(cParseViaccess::ProvIdPtr(data))) return false;
+
+      const unsigned char *start=cParseViaccess::NanoStart(data)+5;
+      const unsigned char *ecm88Data=start;
+      int ecm88Len=SCT_LEN(data)-(start-data);
+      int ecmf8Len=0;
+      while(ecm88Len>0 && ecm88Data[0]<0xA0) {
+        const int nanoLen=ecm88Data[1]+2;
+        ecmf8Len+=nanoLen;
+        ecm88Len-=nanoLen; ecm88Data+=nanoLen;
+        }
+      if(ecmf8Len) {
+        insf8[3]=keynr;
+        insf8[4]=ecmf8Len;
+        if(!IsoWrite(insf8,start) || !Status()) return false;
+        }
+
+      ins88[2]=ecmf8Len?1:0;
+      ins88[3]=keynr;
+      ins88[4]=ecm88Len;
+      if(!IsoWrite(ins88,ecm88Data) || !Status()) return false; // request dcw
+      unsigned char buff[MAX_LEN];
+      if(!IsoRead(insc0,buff) || !Status()) return false; // read dcw
+      switch(buff[0]) {
+        case 0xe8: // even
+          if(buff[1]==8) { memcpy(cw,buff+2,8); return true; }
+          break;
+        case 0xe9: // odd
+          if(buff[1]==8) { memcpy(cw+8,buff+2,8); return true; }
+          break;
+        case 0xea: // complete
+          if(buff[1]==16) { memcpy(cw,buff+2,16); return true; }
+          break;
+        }
+      }
+    else PRINTF(L_SC_ERROR,"key not found on card");
+    }
+  else PRINTF(L_SC_ERROR,"provider not found on card");
+  return false;
+}
+
+bool cSmartCardViaccess::Update(int pid, int caid, const unsigned char *data)
+{
+  static unsigned char insf0[] = { 0xca,0xf0,0x00,0x00,0x00 };
+  static unsigned char ins18[] = { 0xca,0x18,0x00,0x00,0x00 };
+
+  int updtype;
+  cAssembleData ad(data);
+  if(MatchAndAssemble(&ad,&updtype,0)) {
+    while((data=ad.Assembled())) {
+      const unsigned char *start=cParseViaccess::NanoStart(data);
+      int nanolen=SCT_LEN(data)-(start-data);
+
+      if(start && start[0]==0x90 && start[1]==0x03 && nanolen>=5 && SetProvider(&start[2])) {
+        insf0[3]=ins18[3]=cParseViaccess::KeyNrFromNano(start);
+        start+=5; nanolen-=5;
+
+        int n;
+        if(start[0]==0x9e && nanolen>=(n=start[1]+2)) {
+          insf0[4]=n;
+          if(!IsoWrite(insf0,start) || !Status()) continue;
+          start+=n; nanolen-=n;
+          }
+
+        if(nanolen>0) {
+          ins18[2]=updtype>0 ? updtype-1 : updtype;
+          ins18[4]=nanolen;
+          if(IsoWrite(ins18,start)) Status();
+          }
+        }
+      }
+    return true;
+    }
+  return false;
+}
+
+// -- cSmartCardLinkViaccess -------------------------------------------------------------
+
+class cSmartCardLinkViaccess : public cSmartCardLink {
+public:
+  cSmartCardLinkViaccess(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardViaccess(); }
+  };
+
+static cSmartCardLinkViaccess staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-viaccess/sc-viaccess.mk
new file mode 100644 (file)
index 0000000..bd119e6
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard Viaccess
+#
+TARGET = sc_viaccess
+OBJS   = sc-viaccess.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.c
new file mode 100644 (file)
index 0000000..ea37485
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "crypto.h"
+#include "misc.h"
+#include "parse.h"
+#include "helper.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_VIDEOGUARD2   0x0900
+
+#define SYSTEM_NAME          "SC-VideoGuard2"
+#define SYSTEM_PRI           -5
+
+#define SC_NAME "VideoGuard2"
+#define SC_ID   MAKE_SC_ID('V','i','G','2')
+
+#define L_SC        17
+#define L_SC_ALL    LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+  (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+  "sc-videoguard2",
+  { L_SC_DEFNAMES }
+  };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScVideoGuard2 ---------------------------------------------------------------
+
+class cSystemScVideoGuard2 : public cSystemScCore {
+public:
+  cSystemScVideoGuard2(void);
+  };
+
+cSystemScVideoGuard2::cSystemScVideoGuard2(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC VideoGuard2")
+{
+  hasLogger=true;
+}
+
+// -- cSystemLinkScVideoGuard2 --------------------------------------------------------
+
+class cSystemLinkScVideoGuard2 : public cSystemLink {
+public:
+  cSystemLinkScVideoGuard2(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemScVideoGuard2; }
+  };
+
+static cSystemLinkScVideoGuard2 staticInit;
+
+cSystemLinkScVideoGuard2::cSystemLinkScVideoGuard2(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{}
+
+bool cSystemLinkScVideoGuard2::CanHandle(unsigned short SysId)
+{
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(SC_ID);
+  if(card) {
+    res=card->CanHandle(SysId);
+    smartcards.ReleaseCard(card);
+    }
+  return res;
+}
+
+// -- cSmartCardDataVideoGuard2 -----------------------------------------------------
+
+enum eDataType { dtBoxId, dtSeed };
+
+class cSmartCardDataVideoGuard2 : public cSmartCardData {
+public:
+  eDataType type;
+  unsigned char boxid[4];
+  unsigned char seed0[64], seed1[64];
+  //
+  cSmartCardDataVideoGuard2(void);
+  cSmartCardDataVideoGuard2(eDataType Type);
+  virtual bool Parse(const char *line);
+  virtual bool Matches(cSmartCardData *param);
+  };
+
+cSmartCardDataVideoGuard2::cSmartCardDataVideoGuard2(void)
+:cSmartCardData(SC_ID)
+{}
+
+cSmartCardDataVideoGuard2::cSmartCardDataVideoGuard2(eDataType Type)
+:cSmartCardData(SC_ID)
+{
+  type=Type;
+}
+
+bool cSmartCardDataVideoGuard2::Matches(cSmartCardData *param)
+{
+  cSmartCardDataVideoGuard2 *cd=(cSmartCardDataVideoGuard2 *)param;
+  return cd->type==type;
+}
+
+bool cSmartCardDataVideoGuard2::Parse(const char *line)
+{
+  line=skipspace(line);
+  if(!strncasecmp(line,"BOXID",5))     { type=dtBoxId; line+=5; }
+  else if(!strncasecmp(line,"SEED",4)) { type=dtSeed; line+=4; }
+  else {
+    PRINTF(L_CORE_LOAD,"smartcarddatavideoguard2: format error: datatype");
+    return false;
+    }
+  line=skipspace(line);
+  switch(type) {
+    case dtBoxId:
+      if(GetHex(line,boxid,sizeof(boxid))!=sizeof(boxid)) {
+        PRINTF(L_CORE_LOAD,"smartcarddatavideoguard2: format error: boxid");
+        return false;
+        }
+      break;
+    case dtSeed:
+      if(GetHex(line,seed0,sizeof(seed0))!=sizeof(seed0)) {
+        PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: seed0");
+        return false;
+        }
+      line=skipspace(line);
+      if(GetHex(line,seed1,sizeof(seed1))!=sizeof(seed1)) {
+        PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: seed1");
+        return false;
+        }
+      break;
+    }
+  return true;
+}
+
+// -- cCamCryptVG2 -------------------------------------------------------------
+
+#define xor16(v1,v2,d) xxor((d),16,(v1),(v2))
+#define val_by2on3(x)  ((0xaaab*(x))>>16) //fixed point *2/3
+
+class cCamCryptVG2 : private cAES {
+private:
+  unsigned short cardkeys[3][32];
+  unsigned char stateD3A[16];
+  //
+  void LongMult(unsigned short *pData, unsigned short *pLen, unsigned int mult, unsigned int carry);
+  void PartialMod(unsigned short val, unsigned int count, unsigned short *outkey, const unsigned short *inkey);
+  void RotateRightAndHash(unsigned char *p);
+  void Reorder16A(unsigned char *dest, const unsigned char *src);
+  void ReorderAndEncrypt(unsigned char *p);
+  //
+  void Process_D0(const unsigned char *ins, unsigned char *data, const unsigned char *status);
+  void Process_D1(const unsigned char *ins, unsigned char *data, const unsigned char *status);
+  void Decrypt_D3(unsigned char *ins, unsigned char *data, const unsigned char *status);
+public:
+  void PostProcess_Decrypt(unsigned char *buff, int len, unsigned char *cw1, unsigned char *cw2);
+  void SetSeed(const unsigned char *Key1, const unsigned char *Key2);
+  void GetCamKey(unsigned char *buff);
+};
+
+void cCamCryptVG2::SetSeed(const unsigned char *Key1, const unsigned char *Key2)
+{
+  memcpy(cardkeys[1],Key1,sizeof(cardkeys[1]));
+  memcpy(cardkeys[2],Key2,sizeof(cardkeys[2]));
+}
+
+void cCamCryptVG2::GetCamKey(unsigned char *buff)
+{
+  unsigned short *tb2=(unsigned short *)buff, c=1;
+  memset(tb2,0,64);
+  tb2[0]=1;
+  for(int i=0; i<32; i++) LongMult(tb2,&c,cardkeys[1][i],0);
+}
+
+void cCamCryptVG2::PostProcess_Decrypt(unsigned char *buff, int len, unsigned char *cw1, unsigned char *cw2)
+{
+  switch(buff[0]) {
+    case 0xD0:
+      Process_D0(buff,buff+5,buff+buff[4]+5);
+      break;
+    case 0xD1:
+      Process_D1(buff,buff+5,buff+buff[4]+5);
+      break;
+    case 0xD3:
+      Decrypt_D3(buff,buff+5,buff+buff[4]+5);
+      if(buff[1]==0x54) {
+        memcpy(cw1,buff+5,8);
+        for(int ind=14; ind<len+5;) {
+          if(buff[ind]==0x25) {
+            memcpy(cw2,buff+5+ind+2,8);
+            break;
+            }
+          if(buff[ind+1]==0) break;
+          ind+=buff[ind+1];
+          }
+        }
+      break;
+    }
+}
+
+void cCamCryptVG2::Process_D0(const unsigned char *ins, unsigned char *data, const unsigned char *status)
+{
+  switch(ins[1]) {
+    case 0xb4:
+      memcpy(cardkeys[0],data,sizeof(cardkeys[0]));
+      break;
+    case 0xbc: 
+      {
+      unsigned short *idata=(unsigned short *)data;
+      const unsigned short *key1=(const unsigned short *)cardkeys[1];
+      unsigned short key2[32];
+      memcpy(key2,cardkeys[2],sizeof(key2));
+      for(int count2=0; count2<32; count2++) {
+        unsigned int rem=0, div=key1[count2];
+        for(int i=31; i>=0; i--) {
+          unsigned int x=idata[i] | (rem<<16);
+          rem=(x%div)&0xffff;
+          }
+        unsigned int carry=1, t=val_by2on3(div) | 1;
+        while(t) {
+          if(t&1) carry=((carry*rem)%div)&0xffff;
+          rem=((rem*rem)%div)&0xffff;
+          t>>=1;
+          }
+        PartialMod(carry,count2,key2,key1);
+        }
+      unsigned short idatacount=0;
+      for(int i=31; i>=0; i--) LongMult(idata,&idatacount,key1[i],key2[i]);
+      unsigned char stateD1[16];
+      Reorder16A(stateD1,data);
+      cAES::SetKey(stateD1);
+      break;
+      }
+  }
+}
+
+void cCamCryptVG2::Process_D1(const unsigned char *ins, unsigned char *data, const unsigned char *status)
+{
+  unsigned char iter[16], tmp[16];
+  memset(iter,0,sizeof(iter));
+  memcpy(iter,ins,5);
+  xor16(iter,stateD3A,iter);
+  memcpy(stateD3A,iter,sizeof(iter));
+
+  int datalen=status-data;
+  int datalen1=datalen;
+  if(datalen<0) datalen1+=15;
+  int blocklen=datalen1>>4;
+  for(int i=0,iblock=0; i<blocklen+2; i++,iblock+=16) {
+    unsigned char in[16];
+    if(blocklen==i) {
+      memset(in,0,sizeof(in));
+      memcpy(in,&data[iblock],datalen-(datalen1&~0xf));
+      }
+    else if(blocklen+1==i) {
+      memset(in,0,sizeof(in));
+      memcpy(&in[5],status,2);
+      }
+    else
+      memcpy(in,&data[iblock],sizeof(in));
+
+    xor16(iter,in,tmp);
+    ReorderAndEncrypt(tmp);
+    xor16(tmp,stateD3A,iter);
+    }
+  memcpy(stateD3A,tmp,16);
+}
+
+void cCamCryptVG2::Decrypt_D3(unsigned char *ins, unsigned char *data, const unsigned char *status)
+{
+  if(ins[4]>16) ins[4]-=16;
+  if(ins[1]==0xbe) memset(stateD3A,0,sizeof(stateD3A));
+
+  unsigned char tmp[16];
+  memset(tmp,0,sizeof(tmp));
+  memcpy(tmp,ins,5);
+  xor16(tmp,stateD3A,stateD3A);
+
+  int len1=ins[4];
+  int blocklen=len1>>4;
+  if(ins[1]!=0xbe) blocklen++;
+
+  unsigned char iter[16], states[16][16];
+  memset(iter,0,sizeof(iter));
+  for(int blockindex=0; blockindex<blocklen; blockindex++) {
+    iter[0]+=blockindex;
+    xor16(iter,stateD3A,iter);
+    ReorderAndEncrypt(iter);
+    xor16(iter,&data[blockindex*16],states[blockindex]);
+    if(blockindex==(len1>>4)) {
+      int c=len1-(blockindex*16);
+      if(c<16) memset(&states[blockindex][c],0,16-c);
+      }
+    xor16(states[blockindex],stateD3A,stateD3A);
+    RotateRightAndHash(stateD3A);
+    }
+  memset(tmp,0,sizeof(tmp));
+  memcpy(tmp+5,status,2);
+  xor16(tmp,stateD3A,stateD3A);
+  ReorderAndEncrypt(stateD3A);
+
+  memcpy(stateD3A,status-16,sizeof(stateD3A));
+  ReorderAndEncrypt(stateD3A);
+
+  memcpy(data,states[0],len1);
+  if(ins[1]==0xbe) {
+    Reorder16A(tmp,states[0]);
+    cAES::SetKey(tmp);
+    }
+}
+
+void cCamCryptVG2::ReorderAndEncrypt(unsigned char *p)
+{
+  unsigned char tmp[16];
+  Reorder16A(tmp,p); cAES::Encrypt(tmp,16,tmp); Reorder16A(p,tmp);
+}
+
+// reorder AAAABBBBCCCCDDDD to ABCDABCDABCDABCD
+
+void cCamCryptVG2::Reorder16A(unsigned char *dest, const unsigned char *src)
+{
+  for(int i=0,k=0; i<4; i++)
+    for(int j=i; j<16; j+=4,k++)
+      dest[k]=src[j];
+}
+
+void cCamCryptVG2::LongMult(unsigned short *pData, unsigned short *pLen, unsigned int mult, unsigned int carry)
+{
+  for(int i=0; i<*pLen; i++) {
+    carry+=pData[i]*mult;
+    pData[i]=(unsigned short)carry;
+    carry>>=16;
+    }
+  if(carry) pData[(*pLen)++]=carry;
+}
+
+void cCamCryptVG2::PartialMod(unsigned short val, unsigned int count, unsigned short *outkey, const unsigned short *inkey)
+{
+  if(count) {
+    unsigned int mod=inkey[count];
+    unsigned short mult=(inkey[count]-outkey[count-1])&0xffff;
+    for(unsigned int i=0,ib1=count-2; i<count-1; i++,ib1--) {
+      unsigned int t=(inkey[ib1]*mult)%mod;
+      mult=t-outkey[ib1];
+      if(mult>t) mult+=mod;
+      }
+    mult+=val;
+    if((val>mult) || (mod<mult)) mult-=mod;
+    outkey[count]=(outkey[count]*mult)%mod;
+    }
+  else 
+    outkey[0]=val;
+}
+
+static const unsigned char table1[256] = {
+  0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5, 0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
+  0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0, 0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
+  0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc, 0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
+  0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a, 0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
+  0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0, 0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
+  0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b, 0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
+  0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85, 0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
+  0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5, 0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
+  0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17, 0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
+  0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88, 0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
+  0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c, 0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
+  0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9, 0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
+  0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6, 0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
+  0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e, 0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
+  0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94, 0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
+  0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68, 0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
+  };
+
+void cCamCryptVG2::RotateRightAndHash(unsigned char *p)
+{
+  unsigned char t1=p[15];
+  for(int i=0; i<16; i++) {
+    unsigned char t2=t1;
+    t1=p[i]; p[i]=table1[(t1>>1)|((t2&1)<<7)];
+    }
+}
+
+// -- cCmdTable ----------------------------------------------------------------
+
+struct CmdTabEntry {
+  unsigned char cla;
+  unsigned char cmd;
+  unsigned char len;
+  unsigned char mode;
+};
+
+struct CmdTab {
+  unsigned char index;
+  unsigned char size;
+  unsigned char Nentries; 
+  unsigned char dummy;
+  CmdTabEntry e[1];
+};
+
+class CmdTable {
+private:
+  struct CmdTab *tab;
+public:
+  CmdTable(const unsigned char *mem, int size);
+  ~CmdTable();
+  bool GetInfo(const unsigned char *cmd, unsigned char &rlen, unsigned char &rmode);
+  };
+
+CmdTable::CmdTable(const unsigned char *mem, int size)
+{
+  tab=(struct CmdTab *)new unsigned char[size];
+  memcpy(tab,mem,size);
+}
+
+CmdTable::~CmdTable()
+{
+  delete tab;
+}
+
+bool CmdTable::GetInfo(const unsigned char *cmd, unsigned char &rlen, unsigned char & rmode)
+{
+  struct CmdTabEntry *pcte=tab->e;
+  for(int i=0; i<tab->Nentries; i++,pcte++)
+    if(cmd[1]==pcte->cmd) {
+      rlen=pcte->len;
+      rmode=pcte->mode;
+      return true;
+      }
+  return false;
+}
+
+// -- cSmartCardVideoGuard2 -----------------------------------------------------------
+
+#define BASEYEAR 2000 // for date calculation
+
+class cSmartCardVideoGuard2 : public cSmartCard, private cIdSet {
+private:
+  unsigned char CW1[8], CW2[8];
+  unsigned char cardID[4], groupID[4], boxID[4];
+  unsigned int CAID;
+  cCamCryptVG2 state;
+  CmdTable *cmdList;
+public:
+  void ReadTiers(void);
+  void RevDateCalc(const unsigned char *Date, int &year, int &mon, int &day, int &hh, int &mm, int &ss);
+  void Datecalc(int year, int mon, int day, int hh, int mm, int ss , unsigned char * Date);
+  int DoCmd(const unsigned char *ins, const unsigned char *txbuff=0, unsigned char *rxbuff=0);
+  int ReadCmdLen(const unsigned char *cmd);
+public:
+  cSmartCardVideoGuard2(void);
+  ~cSmartCardVideoGuard2();
+  virtual bool Init(void);
+  virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+  virtual bool Update(int pid, int caid, const unsigned char *data);
+  virtual bool CanHandle(unsigned short SysId);
+  };
+
+static const struct StatusMsg msgs[] = {
+  { { 0x90,0x00 }, "Instruction executed without errors, noupdate, nofilter, IRD# not set", true },
+  { { 0x90,0x01 }, "Instruction executed without errors, update, nofilter, IRD# not set", true },
+  { { 0x90,0x20 }, "Instruction executed without errors, noupdate, nofilter, IRD# set", true },
+  { { 0x90,0x21 }, "Instruction executed without errors, update, nofilter, IRD# set", true },
+  { { 0x90,0x80 }, "Instruction executed without errors, noupdate, filter, IRD# not set", true },
+  { { 0x90,0x81 }, "Instruction executed without errors, update, filter, IRD# not set", true },
+  { { 0x90,0xa0 }, "Instruction executed without errors, noupdate, filter, IRD# set", true },
+  { { 0x90,0xa1 }, "Instruction executed without errors, update, filter, IRD# set", true },
+  { { 0x91,0x00 }, "Instruction executed without errors, noupdate, nofilter, IRD# not set", true },
+  { { 0x91,0x01 }, "Instruction executed without errors, update, nofilter, IRD# not set", true },
+  { { 0x91,0x20 }, "Instruction executed without errors, noupdate, nofilter, IRD# set", true },
+  { { 0x91,0x21 }, "Instruction executed without errors, update, nofilter, IRD# set", true },
+  { { 0x91,0x80 }, "Instruction executed without errors, noupdate, filter, IRD# not set", true },
+  { { 0x91,0x81 }, "Instruction executed without errors, update, filter, IRD# not set", true },
+  { { 0x91,0xa0 }, "Instruction executed without errors, noupdate, filter, IRD# set", true },
+  { { 0x91,0xa1 }, "Instruction executed without errors, update, filter, IRD# set", true },
+  { { 0xFF,0xFF }, 0, false }
+  };
+
+static const struct CardConfig cardCfg = {
+  SM_8O2,2000,1400
+  };
+
+cSmartCardVideoGuard2::cSmartCardVideoGuard2(void)
+:cSmartCard(&cardCfg,msgs)
+{
+  cmdList=0;
+}
+
+cSmartCardVideoGuard2::~cSmartCardVideoGuard2(void)
+{
+  delete cmdList;
+}
+
+bool cSmartCardVideoGuard2::CanHandle(unsigned short SysId)
+{
+  return SysId==CAID;
+}
+
+bool cSmartCardVideoGuard2::Init(void)
+{
+  static const unsigned char vg2Hist[] = { 'i',0xff,'J','P' };
+  if(atr->histLen<4 || memcmp(&atr->hist[3],vg2Hist,4)) {
+    PRINTF(L_SC_INIT,"doesn't look like a VideoGuard2 card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("VideoGuard2 smartcard\n");
+  snprintf(idStr,sizeof(idStr),"%s (%c%c.%d)",SC_NAME,atr->hist[10],atr->hist[11],atr->hist[12]);
+  
+  ResetIdSet();
+  delete cmdList; cmdList=0;
+
+  static unsigned char ins7401[] = { 0xD0,0x74,0x01,0x00,0x00 };
+  int l;
+  if((l=ReadCmdLen(ins7401))<0) return false;
+  ins7401[4]=l;
+  unsigned char buff[256];
+  if(!IsoRead(ins7401,buff) || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to read cmd list");
+    return false;
+    }
+  cmdList=new CmdTable(buff,l);
+
+  static const unsigned char ins7416[5] = { 0xD0,0x74,0x16,0x00,0x00 };
+  if(DoCmd(ins7416)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd 7416 failed");
+    return false;
+    }
+
+  static const unsigned char ins36[5] = { 0xD0,0x36,0x00,0x00,0x00 };
+  bool boxidOK=false;
+  if((l=DoCmd(ins36,0,buff))>0 && Status())
+    for(int i=0; i<l ;i++) {
+      if(buff[i]==0x00 && buff[i+1]==0xF3) {
+        memcpy(&boxID,&buff[i+2],sizeof(boxID));
+        boxidOK=true;
+        break;
+        }
+      }
+  if(!boxidOK) {
+    cSmartCardDataVideoGuard2 cd(dtBoxId);
+    cSmartCardDataVideoGuard2 *entry=(cSmartCardDataVideoGuard2 *)smartcards.FindCardData(&cd);
+    if(entry) {
+      memcpy(&boxID,entry->boxid,sizeof(boxID));
+      boxidOK=true;
+      }
+    }
+  if(!boxidOK) {
+    PRINTF(L_SC_ERROR,"no boxID available");
+    return false;
+    }
+
+  static const unsigned char ins4C[5] = { 0xD0,0x4C,0x00,0x00,0x00 }; 
+  static unsigned char payload4C[9] = { 0,0,0,0, 3,0,0,2,4 };
+  memcpy(payload4C,boxID,4);
+  if(DoCmd(ins4C,payload4C)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"sending boxid failed");
+    return false;
+    }
+
+  static const unsigned char ins58[5] = { 0xD0,0x58,0x00,0x00,0x00 };
+  if(DoCmd(ins58,0,buff)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to read card details");
+    return false;
+    }
+  
+  CAID=WORD(buff,0x1D,0xFFFF);
+  memcpy(&cardID,&buff[8],4);
+  memcpy(&groupID,&buff[8],4); groupID[3]=0;
+  SetCard(new cCardNDS(cardID));
+  AddProv(new cProviderNDS(groupID));
+  char str[20], str2[20];
+  infoStr.Printf("Cardtype: %c%c.%d\n"
+                 "BoxID %s Caid %04x CardID %s\n",
+                 atr->hist[10],atr->hist[11],atr->hist[12],HexStr(str,boxID,4),CAID,HexStr(str2,cardID,4));
+  PRINTF(L_SC_INIT,"cardtype: %c%c.%d boxID %s caid %04x cardID %s",atr->hist[10],atr->hist[11],atr->hist[12],HexStr(str,boxID,4),CAID,HexStr(str2,cardID,4));
+
+  cSmartCardDataVideoGuard2 cd(dtSeed);
+  cSmartCardDataVideoGuard2 *entry=(cSmartCardDataVideoGuard2 *)smartcards.FindCardData(&cd);
+  if(!entry) {
+    PRINTF(L_SC_ERROR,"no NDS seed available");
+    return false;
+    }
+  state.SetSeed(entry->seed0,entry->seed1);
+  unsigned char tbuff[64];
+  state.GetCamKey(tbuff);
+
+  static const unsigned char insB4[5] = { 0xD0,0xB4,0x00,0x00,0x00 };
+  if(DoCmd(insB4,tbuff)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd D0B4 failed");
+    return false;
+    }
+  
+  static const unsigned char insBC[5] = { 0xD0,0xBC,0x00,0x00,0x00 };
+  if(DoCmd(insBC)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd D0BC failed");
+    return false;
+    }
+  static const unsigned char insBE[5] = { 0xD3,0xBE,0x00,0x00,0x00 };
+  if(DoCmd(insBE)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd D3BE failed");
+    return false;
+    }
+
+  static const unsigned char ins58a[5] = { 0xD1,0x58,0x00,0x00,0x00 }; 
+  if(DoCmd(ins58a)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd D158 failed");
+    return false;
+    }
+  static const unsigned char ins4Ca[5] = { 0xD1,0x4C,0x00,0x00,0x00 }; 
+  if(DoCmd(ins4Ca,payload4C)<0 || !Status()) {
+    PRINTF(L_SC_ERROR,"cmd D14C failed");
+    return false;
+    }
+  ReadTiers();
+  return true;
+}
+
+bool cSmartCardVideoGuard2::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+  static unsigned char ins40[5] = { 0xD1,0x40,0x40,0x80,0xFF };
+  static const unsigned char ins54[5] = { 0xD3,0x54,0x00,0x00,0x00 };
+  int posECMpart2=data[6]+7;
+  int lenECMpart2=data[posECMpart2]+1;
+  unsigned char tbuff[264];
+  tbuff[0]=0;
+  memcpy(&tbuff[1],&data[posECMpart2+1],lenECMpart2);
+  ins40[4]=lenECMpart2;
+  if(DoCmd(ins40,tbuff)>0 && Status()) {
+    if(DoCmd(ins54)>0 && Status()) {
+      if(data[0]&1) memcpy(cw+8,CW1,8);
+      else          memcpy(cw+0,CW1,8);
+      return true;
+      }
+    }
+  return false;
+}
+
+bool cSmartCardVideoGuard2::Update(int pid, int caid, const unsigned char *data)
+{
+  static unsigned char ins42[5] = { 0xD1,0x42,0x00,0x00,0xFF }; 
+  if(MatchEMM(data)) {
+    const unsigned char *payloaddata=cParseNDS::PayloadStart(data); //points to 02 xx yy
+    int lenEMM;
+    switch(payloaddata[0]) {
+      case 2:
+        lenEMM=payloaddata[payloaddata[1]+2];
+        payloaddata+=3+payloaddata[1]; // skip len specifier
+        break;
+      default:
+//        di(printf("scvg2: ***ERROR***: EMM: bad payload type byte %02x\n",payloaddata[0]));
+//        DUMP3("VG2EMM",data,32);
+        return false;
+      }
+//    di(printf("scvg2: EMM: pid %d (%x) caid %d (%x) len %d (%x)\n",pid,pid,caid,caid,lenEMM,lenEMM));
+//    DUMP3("VG2EMM",data,32);
+    if(lenEMM<=8 || lenEMM>188) {
+//      di(printf("scvg2: ***ERROR***: EMM: len %d bad\n",lenEMM));
+      return false;
+      }
+    ins42[4]=lenEMM;
+    if(DoCmd(ins42,payloaddata)>0 && Status())
+      return true;
+    }
+  return false;
+}
+
+/*
+bool cSmartCardVideoGuard2::ZKT(void)
+{
+  static unsigned char payload4A[1] = { 0x01 };
+  static unsigned char ins4A[5] = { 0xD0,0x4A,0x15,0x01,0x01 };
+  DoCmd(ins4A,payload4A);
+  static unsigned char ins5A1[5] = { 0xD0,0x5A,0x15,0x01,0x10 };
+  DoCmd(ins5A1);
+  static unsigned char ins5A2[5] = { 0xD0,0x5A,0x11,0x02,0x40 };
+  DoCmd(ins5A2);
+  DoCmd(ins4A,payload4A);
+  DoCmd(ins5A1);
+  static unsigned char ins5A3[5] = { 0xD0,0x5A,0x10,0x02,0x40 };
+  DoCmd(ins5A3);
+  return true;
+}
+*/
+
+void cSmartCardVideoGuard2::ReadTiers(void)
+{
+  static const unsigned char ins2a[5] = { 0xd0,0x2a,0x00,0x00,0x00 };
+  if(DoCmd(ins2a)<0 || !Status()) return;
+  static unsigned char ins76[5] = { 0xd0,0x76,0x00,0x00,0x00 };
+  ins76[3]=0x7f; ins76[4]=2;
+  unsigned char buff[270];
+  if(!IsoRead(ins76,buff) || !Status()) return;
+  ins76[3]=0; ins76[4]=0;
+  int num=buff[1];
+  if(num>0) {
+    infoStr.Strcat("Tier\tExpiry Date\n");
+    infoStr.Strcat("----\t-----------\n");
+    }
+  for(int i=0; i<num; i++) {
+    ins76[2]=i;
+    if(DoCmd(ins76,0,buff)<0 || !Status()) return;
+    if(buff[5+2]==0 && buff[5+3]==0) break;
+    int y,m,d,H,M,S;
+    RevDateCalc(&buff[5+4],y,m,d,H,M,S);
+    char str[100];
+    snprintf(str,sizeof(str),"%02x%02x\t%04d/%02d/%02d-%02d:%02d:%02d\n",buff[5+2],buff[5+3],y,m,d,H,M,S);
+    infoStr.Strcat(str);
+    PRINTF(L_SC_INIT,"Tier %02x%02x expires %04d/%02d/%02d-%02d:%02d:%02d",buff[5+2],buff[5+3],y,m,d,H,M,S);
+    }
+}
+
+void cSmartCardVideoGuard2::RevDateCalc(const unsigned char *Date, int &year, int &mon, int &day, int &hh, int &mm, int &ss)
+{ 
+  year=(Date[0]/12)+BASEYEAR; 
+  mon=(Date[0]%12)+1; 
+  day=Date[1]; 
+  hh=Date[2]/8; 
+  mm=(0x100*(Date[2]-hh*8)+Date[3])/32; 
+  ss=(Date[3]-mm*32)*2; 
+}
+
+void cSmartCardVideoGuard2::Datecalc(int year, int mon, int day, int hh, int mm, int ss , unsigned char *Date)
+{ 
+  Date[0]=((year-BASEYEAR)*12 + (mon-1)); 
+  Date[1]=day; 
+  Date[2]=hh*8+mm/8; 
+  Date[3]=ss/2+mm*32; 
+} 
+
+int cSmartCardVideoGuard2::DoCmd(const unsigned char *ins, const unsigned char *txbuff, unsigned char *rxbuff)
+{
+  unsigned char ins2[5];
+  memcpy(ins2,ins,5);
+  unsigned char len=0, mode=0;
+  if(cmdList->GetInfo(ins2,len,mode)) {
+    if(len==0xFF && mode==2) {
+      if(ins2[4]==0) ins2[4]=len=ReadCmdLen(ins2);
+      }
+    else if(mode!=0) ins2[4]=len;
+    }
+  if(ins2[0]==0xd3) ins2[4]=len+16;
+  len=ins2[4];
+
+  unsigned char b[256], tmp[264];
+  if(!rxbuff) rxbuff=tmp;
+  if(mode>1) {
+    if(!IsoRead(ins2,b) || !Status()) return -1;
+    memcpy(rxbuff,ins2,5);
+    memcpy(rxbuff+5,b,len);
+    memcpy(rxbuff+5+len,sb,2);
+    }
+  else {
+    if(!IsoWrite(ins2,txbuff) || !Status()) return -1;
+    memcpy(rxbuff,ins2,5);
+    memcpy(rxbuff+5,txbuff,len);
+    memcpy(rxbuff+5+len,sb,2);
+    }
+  state.PostProcess_Decrypt(rxbuff,len,CW1,CW2);
+  return len;
+}
+
+int cSmartCardVideoGuard2::ReadCmdLen(const unsigned char *cmd)
+{
+  unsigned char cmd2[5], resp[16];
+  memcpy(cmd2,cmd,5);
+  cmd2[3]=0x80;
+  cmd2[4]=1;
+  if(!IsoRead(cmd2,resp) || !Status()) {
+    PRINTF(L_SC_ERROR,"failed to read %02x%02x cmd length",cmd[1],cmd[2]);
+    return -1;
+    }
+  return resp[0];  
+}
+
+// -- cSmartCardLinkVG2 -------------------------------------------------------
+
+class cSmartCardLinkVG2 : public cSmartCardLink {
+public:
+  cSmartCardLinkVG2(void):cSmartCardLink(SC_NAME,SC_ID) {}
+  virtual cSmartCard *Create(void) { return new cSmartCardVideoGuard2; }
+  virtual cSmartCardData *CreateData(void) { return new cSmartCardDataVideoGuard2; }
+  };
+
+static cSmartCardLinkVG2 staticScInit;
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/sc-videoguard2/sc-videoguard2.mk
new file mode 100644 (file)
index 0000000..749aaeb
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Smartcard VideoGuard2
+#
+TARGET = sc_videoguard2
+OBJS   = sc-videoguard2.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.c
new file mode 100644 (file)
index 0000000..5f51736
--- /dev/null
@@ -0,0 +1,1642 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <byteswap.h>
+
+#include <openssl/sha.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "helper.h"
+#include "crypto.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sys.h"
+#include "log-core.h"
+
+#define SYSTEM_SECA          0x0100
+
+#define SYSTEM_NAME          "Seca"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)
+
+#define FILEMAP_DOMAIN       "seca"
+
+#define L_SYS        14
+#define L_SYS_ALL    LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+  "seca",
+  { L_SYS_DEFNAMES }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cPlainKeySeca ------------------------------------------------------------
+
+#define PLAINLEN_SECA_H 8
+#define PLAINLEN_SECA_D 16
+#define PLAINLEN_SECA_B 90
+#define PLAINLEN_SECA_E 6  // exponent key len
+#define EMM_MAGIC       1
+#define N51_MAGIC       2
+
+class cPlainKeySeca : public cDualKey {
+protected:
+  virtual bool IsBNKey(void) const;
+  virtual int IdSize(void) { return 4; }
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeySeca(bool Super);
+  virtual bool Parse(const char *line);
+  };
+
+static cPlainKeyTypeReg<cPlainKeySeca,'S'> KeyReg;
+
+cPlainKeySeca::cPlainKeySeca(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeySeca::IsBNKey(void) const
+{
+  switch(keynr&C2MASK) {
+    case MBC('E','1'):
+    case MBC('M','1'):
+    case MBC('E','3'):
+    case MBC('M','3'):
+    case MBC('E','5'):
+    case MBC('M','5'):
+    case MBC('E','9'):
+    case MBC('M','9'): return true;
+    }
+  return false;
+}
+
+bool cPlainKeySeca::Parse(const char *line)
+{
+  unsigned char sid[2];
+  int len;
+  if(GetChar(line,&type,1) && (len=GetHex(line,sid,2,false))) {
+    type=toupper(type); id=Bin2Int(sid,len);
+    line=skipspace(line);
+    int emmnr=0, keylen=PLAINLEN_SECA_B;
+    if(!strncasecmp(line,"EMM",3)) { // EMM RSA key
+      emmnr=EMM_MAGIC;
+      line+=3;
+      line=skipspace(line);
+      }
+    else if(!strncasecmp(line,"N51",3)) { // Nano 51 RSA key
+      emmnr=N51_MAGIC;
+      line+=3;
+      line=skipspace(line);
+      keylen=toupper(*line)=='E'?1:129;
+      }
+    bool ok;
+    switch(toupper(*line)) {
+      case 'E':
+      case 'M': ok=GetChar(line,&keynr,2);
+                keynr=ADDC3(keynr,emmnr);
+                break;
+      default:  ok=emmnr ? false : GetHex(line,sid,1);
+                keynr=sid[0];
+                break;
+      }
+    if(ok) {
+      unsigned char *skey=AUTOMEM(keylen);
+      len=GetHex(line,skey,keylen,false);
+      if(IsBNKey()) {
+        if(C2(keynr)=='E' && len==PLAINLEN_SECA_E) {
+          // support short exponent keys
+          keylen=len;
+          }
+        }
+      else {
+        if(len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D) {
+          // support for 16 & 8 byte keys
+          keylen=len;
+          }
+        }
+      if(len==keylen) {
+        SetBinKey(skey,keylen);
+        return true;
+        }
+      }
+    }
+  return false;
+}
+
+cString cPlainKeySeca::PrintKeyNr(void)
+{
+  char nr[32];
+  int q=0;
+  switch(C3(keynr)) {
+    case EMM_MAGIC: q+=snprintf(nr+q,sizeof(nr)-q,"EMM "); break;
+    case N51_MAGIC: q+=snprintf(nr+q,sizeof(nr)-q,"N51 "); break;
+    }
+  if(IsBNKey()) {
+    nr[q  ]=(keynr>>8) & 0xff;
+    nr[q+1]= keynr     & 0xff;
+    nr[q+2]=0;
+    q+=2;
+    }
+  else q+=snprintf(nr+q,sizeof(nr)-q,"%.2X",keynr);
+  return nr;
+}
+
+// -- cSecaCardInfo ------------------------------------------------------------
+
+class cSecaCardInfo : public cStructItem, public cProviderSeca {
+private:
+  int len;
+public:
+  unsigned char key[16];
+  //
+  bool Parse(const char *line);
+  int KeySize(void) { return len; }
+  };
+
+bool cSecaCardInfo::Parse(const char *line)
+{
+  return GetHex(line,provId,sizeof(provId)) &&
+         GetHex(line,sa,sizeof(sa)) &&
+         (len=GetHex(line,key,sizeof(key),false)) && 
+         (len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D);
+}
+
+// -- cSecaCardInfos -----------------------------------------------------------
+
+class cSecaCardInfos : public cCardInfos<cSecaCardInfo> {
+public:
+  cSecaCardInfos(void):cCardInfos<cSecaCardInfo>("Seca cards","Seca.KID",0) {}
+  };
+
+static cSecaCardInfos Scards;
+
+// -- cSeca --------------------------------------------------------------------
+
+static const unsigned char secaPC1[] = {
+  42,57,29,34,  41,53,30,15,
+  19,36,23,14,  43,61,12, 3,
+  51,49,5,  6,  45,54,52,47,
+  63,38,58,22,  60,33,10,26,
+  37,35,44, 1,  20,62,28,18,
+  46, 9,39, 4,  27,11,21,50,
+  31,25, 2, 7,  13,55,59,17
+  };
+
+static const unsigned char secaPC2[] = {
+  18, 3,21,15,  42,35,37, 8,
+  49,41,30,55,  56,29,12,23,
+  43,14,7 ,27,  13, 2,11,45,
+   4,34,54,51,  22,40,16,25,
+  26,48,53,28,   1,17, 5,31,
+  50, 6,39,24,  33,47,38,32
+  };
+
+static inline void __swap8_4(unsigned char *data)
+{
+  unsigned char temp[4];
+  memcpy(temp,data,4);
+  memcpy(data,&data[4],4);
+  memcpy(&data[4],temp,4);
+}
+#define swap8_4(d) __swap8_4(d)
+
+class cSeca {
+private:
+  static const unsigned char TD[];
+  //
+  void Fase(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+protected:
+  static const unsigned char T1_S1[], T2_S1[];
+  //
+  void Decrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+  void Encrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+  void CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2);
+  // Seca2 functions
+  void AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode);
+  };
+
+const unsigned char cSeca::TD[4] = { 1,3,0,2 };
+
+const unsigned char cSeca::T1_S1[256] = { // Original Tables
+  0x2a,0xe1,0x0b,0x13,0x3e,0x6e,0x32,0x48, 0xd3,0x31,0x08,0x8c,0x8f,0x95,0xbd,0xd0,
+  0xe4,0x6d,0x50,0x81,0x20,0x30,0xbb,0x75, 0xf5,0xd4,0x7c,0x87,0x2c,0x4e,0xe8,0xf4,
+  0xbe,0x24,0x9e,0x4d,0x80,0x37,0xd2,0x5f, 0xdb,0x04,0x7a,0x3f,0x14,0x72,0x67,0x2d,
+  0xcd,0x15,0xa6,0x4c,0x2e,0x3b,0x0c,0x41, 0x62,0xfa,0xee,0x83,0x1e,0xa2,0x01,0x0e,//8
+  0x7f,0x59,0xc9,0xb9,0xc4,0x9d,0x9b,0x1b, 0x9c,0xca,0xaf,0x3c,0x73,0x1a,0x65,0xb1,
+  0x76,0x84,0x39,0x98,0xe9,0x53,0x94,0xba, 0x1d,0x29,0xcf,0xb4,0x0d,0x05,0x7d,0xd1,
+  0xd7,0x0a,0xa0,0x5c,0x91,0x71,0x92,0x88, 0xab,0x93,0x11,0x8a,0xd6,0x5a,0x77,0xb5,
+  0xc3,0x19,0xc1,0xc7,0x8e,0xf9,0xec,0x35, 0x4b,0xcc,0xd9,0x4a,0x18,0x23,0x9f,0x52,//16
+  0xdd,0xe3,0xad,0x7b,0x47,0x97,0x60,0x10, 0x43,0xef,0x07,0xa5,0x49,0xc6,0xb3,0x55,
+  0x28,0x51,0x5d,0x64,0x66,0xfc,0x44,0x42, 0xbc,0x26,0x09,0x74,0x6f,0xf7,0x6b,0x4f,
+  0x2f,0xf0,0xea,0xb8,0xae,0xf3,0x63,0x6a, 0x56,0xb2,0x02,0xd8,0x34,0xa4,0x00,0xe6,
+  0x58,0xeb,0xa3,0x82,0x85,0x45,0xe0,0x89, 0x7e,0xfd,0xf2,0x3a,0x36,0x57,0xff,0x06,//24
+  0x69,0x54,0x79,0x9a,0xb6,0x6c,0xdc,0x8b, 0xa7,0x1f,0x90,0x03,0x17,0x1c,0xed,0xd5,
+  0xaa,0x5e,0xfe,0xda,0x78,0xb0,0xbf,0x12, 0xa8,0x22,0x21,0x3d,0xc2,0xc0,0xb7,0xa9,
+  0xe7,0x33,0xfb,0xf1,0x70,0xe5,0x17,0x96, 0xf8,0x8d,0x46,0xa1,0x86,0xe2,0x40,0x38,
+  0xf6,0x68,0x25,0x16,0xac,0x61,0x27,0xcb, 0x5b,0xc8,0x2b,0x0f,0x99,0xde,0xce,0xc5
+  };
+
+const unsigned char cSeca::T2_S1[256] = {
+  0xbf,0x11,0x6d,0xfa,0x26,0x7f,0xf3,0xc8, 0x9e,0xdd,0x3f,0x16,0x97,0xbd,0x08,0x80,
+  0x51,0x42,0x93,0x49,0x5b,0x64,0x9b,0x25, 0xf5,0x0f,0x24,0x34,0x44,0xb8,0xee,0x2e,
+  0xda,0x8f,0x31,0xcc,0xc0,0x5e,0x8a,0x61, 0xa1,0x63,0xc7,0xb2,0x58,0x09,0x4d,0x46,
+  0x81,0x82,0x68,0x4b,0xf6,0xbc,0x9d,0x03, 0xac,0x91,0xe8,0x3d,0x94,0x37,0xa0,0xbb, //8
+  0xce,0xeb,0x98,0xd8,0x38,0x56,0xe9,0x6b, 0x28,0xfd,0x84,0xc6,0xcd,0x5f,0x6e,0xb6,
+  0x32,0xf7,0x0e,0xf1,0xf8,0x54,0xc1,0x53, 0xf0,0xa7,0x95,0x7b,0x19,0x21,0x23,0x7d,
+  0xe1,0xa9,0x75,0x3e,0xd6,0xed,0x8e,0x6f, 0xdb,0xb7,0x07,0x41,0x05,0x77,0xb4,0x2d,
+  0x45,0xdf,0x29,0x22,0x43,0x89,0x83,0xfc, 0xd5,0xa4,0x88,0xd1,0xf4,0x55,0x4f,0x78,//16
+  0x62,0x1e,0x1d,0xb9,0xe0,0x2f,0x01,0x13, 0x15,0xe6,0x17,0x6a,0x8d,0x0c,0x96,0x7e,
+  0x86,0x27,0xa6,0x0d,0xb5,0x73,0x71,0xaa, 0x36,0xd0,0x06,0x66,0xdc,0xb1,0x2a,0x5a,
+  0x72,0xbe,0x3a,0xc5,0x40,0x65,0x1b,0x02, 0x10,0x9f,0x3b,0xf9,0x2b,0x18,0x5c,0xd7,
+  0x12,0x47,0xef,0x1a,0x87,0xd2,0xc2,0x8b, 0x99,0x9c,0xd3,0x57,0xe4,0x76,0x67,0xca,//24
+  0x3c,0xfb,0x90,0x20,0x14,0x48,0xc9,0x60, 0xb0,0x70,0x4e,0xa2,0xad,0x35,0xea,0xc4,
+  0x74,0xcb,0x39,0xde,0xe7,0xd4,0xa3,0xa5, 0x04,0x92,0x8c,0xd9,0x7c,0x1c,0x7a,0xa8,
+  0x52,0x79,0xf2,0x33,0xba,0x1f,0x30,0x9a, 0x00,0x50,0x4c,0xff,0xe5,0xcf,0x59,0xc3,
+  0xe3,0x0a,0x85,0xb3,0xae,0xec,0x0b,0xfe, 0xe2,0xab,0x4a,0xaf,0x69,0x6c,0x2c,0x5d
+  };
+
+void cSeca::Fase(unsigned char *D, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+  for(int l=0; l<4; ++l) { D[l]^=key[l]; D[l]=T1[D[l]&0xff]; }
+  for(int l=6; l>3; --l) {
+    D[(l+2)&3]^=D[(l+1)&3];
+    D[l&3]=T2[(sn8(D[(l+1)&3])+D[l&3])&0xff];
+    } 
+  for(int l=3; l>0; --l) {
+    D[(l+2)&3]^=D[(l+1)&3];
+    D[l&3]=T1[(sn8(D[(l+1)&3])+D[l&3])&0xff];
+    }
+  D[2]^=D[1];
+  D[1]^=D[0];
+}
+
+void cSeca::Decrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+  unsigned char k[16],D[4];
+  memcpy(k,key,sizeof(k));
+  unsigned char C=0xff;
+  for(int j=0; j<4; ++j) {
+    for(int i=0; i<16; ++i) {
+      if((i&3)==0) ++C;
+      k[i]^=T1[(k[(15+i)&0xf]^k[(i+1)&0xf]^C)&0xff]; 
+      } 
+    }
+  int j=0;
+  for(int i=0; i<16; ++i) {
+    for(int l=0; l<4; ++l) D[l]=d[l+4];
+    j=(j+12)&0xf;
+    Fase(D,&k[j&0xc],T1,T2);
+    for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
+    for(int l=3; l>=0; --l) k[(j+l)&0xf]^=T1[(k[(j+l+1)&0xf]^k[(j+l+15)&0xf]^(15-i))&0xff];
+    if(i<15) swap8_4(d);
+    }
+}
+
+void cSeca::Encrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+  unsigned char D[4],kk[16];
+  memcpy(kk, key, sizeof(kk));
+  for(int j=0, i=0; i<16; ++i, j=(j+4)&0xf) {
+    for(int l=0; l<4; ++l) kk[(j+l)&0xf] ^= T1[(kk[(j+l+1)&0xf]^kk[(j+l+15)&0xf]^i)&0xff]; 
+    if(i>0) swap8_4(d);
+    for(int l=0; l<4; ++l) D[l]=d[l+4];
+    Fase(D,&kk[j&0xc],T1,T2);
+    for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
+    } 
+}
+
+void cSeca::CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2)
+{
+  memset(signature,0,8);
+  for(int i=0 ; i<len ; i+=8) {
+    for(int j=0 ; j<8 && i+j<len; j++) signature[j]^=buff[i+j];
+    Encrypt(signature,k,T1,T2);
+    }
+}
+
+void cSeca::AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode)
+{
+  static const unsigned int adders[] = {
+    0x0000,0xe555,0xafff,0x5ffe,0xf552,0x6ffb,0xcff9,0x154c,
+    0x3ff4,0x4ff1,0x4543,0x1fea,0xdfe6,0x8537,0x0fdd,0x7fd8
+    };
+  const unsigned short * const k = (const unsigned short *)key;
+  unsigned short * const dd = (unsigned short *)data;
+  unsigned int d1=dd[0], d2=dd[1], d3=dd[2], d4=dd[3];
+  PRINTF(L_SYS_VERBOSE,"additional algo: %s",!mode?"encrypt":"decrypt");
+  if(!mode) {
+    for(int i=0; i<0x10; i++) {
+      const unsigned int adder = adders[i];
+      d1 += (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
+      d2 += (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
+      d1 = ror16(d1,5);
+      d2 = rol16(d2,3);
+      d3 += (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
+      d4 += (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
+      d3 = rol16(d3,3);
+      d4 = ror16(d4,5);
+      }
+    }
+  else {
+    for(int i=0xf; i>=0; i--) {
+      const unsigned int adder = adders[i];
+      d4 = rol16(d4,5);
+      d3 = ror16(d3,3);
+      d4 -= (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
+      d3 -= (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
+      d2 = ror16(d2,3);
+      d1 = rol16(d1,5);
+      d2 -= (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
+      d1 -= (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
+      }
+    }
+  dd[0]=d1; dd[1]=d2; dd[2]=d3; dd[3]=d4;
+}
+
+// -- cSeca2Prov ---------------------------------------------------------------
+
+#define MAX_PERMS    10
+#define KEY2INDEX(x) (((x)>>5)&0x03)
+
+struct Perm {
+  unsigned char P1[8], P2[8], P3[8], P4[8];
+  };
+
+struct ProvData {
+  unsigned short LoadId, MTLoadSize, MTSize;
+  unsigned char SHA1Pad, SHA1End;
+  unsigned char FP[8];
+  };
+
+class cSeca2Prov: protected cSeca {
+private:
+  cFileMap *hash, *mask;
+  unsigned short id;
+  //
+  unsigned short Sum(const unsigned char *data, int n) const;
+protected:
+  const struct ProvData *pData;
+  const struct Perm *perm;
+  //
+  cFileMap *GetMap(const char *type, int size, bool generic) const;
+public:
+  cSeca2Prov(unsigned short Id);
+  virtual ~cSeca2Prov();
+  // provider specific mods
+  virtual void PreSSE(unsigned char *data, int pos) {}
+  virtual void PostSSE(unsigned char *data, int pos) {}
+  virtual void SignatureMod(unsigned char *MD, const unsigned char *PK) {}
+  virtual void PreCW(unsigned char *data) {}
+  virtual void PostCW(unsigned char *data) {}
+  virtual void ChainTableXor(unsigned char *data, unsigned short index)=0;
+  virtual bool DoSigCheck(void) { return true; }
+  //
+  virtual bool Init(void);
+  void CalcSHASignature(const unsigned char *data, int n, unsigned char *signature, bool PadMode=true);
+  bool DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2);
+  bool Matches(unsigned short Id) const { return Id==id; }
+  //
+  const unsigned char *T1(int index) const;
+  const unsigned char *T2(int index) const;
+  const struct Perm *P(int index) const;
+  inline const unsigned char *MT(void) const { return mask->Addr(); }
+  inline int MTSize(void) const { return pData->MTSize; }
+  inline const unsigned char *FP(void) const { return pData->FP; }
+  };
+
+cSeca2Prov::cSeca2Prov(unsigned short Id)
+{
+  id=Id;
+  pData=0; perm=0; hash=0; mask=0;
+}
+
+cSeca2Prov::~cSeca2Prov()
+{
+  if(hash) hash->Unmap();
+  if(mask) mask->Unmap();
+}
+
+bool cSeca2Prov::Init(void)
+{
+  hash=GetMap("hash",1536,false);
+  mask=GetMap("mt",pData->MTLoadSize,false);
+  if(!hash || !mask) return false;
+  return true;
+}
+
+cFileMap *cSeca2Prov::GetMap(const char *type, int size, bool generic) const
+{
+  char name[32];
+  snprintf(name,sizeof(name),generic ? "s2_%s.bin" : "s2_%s_%04x.bin",type,pData->LoadId);
+  cFileMap *map=filemaps.GetFileMap(name,FILEMAP_DOMAIN,false);
+  if(!map)
+    PRINTF(L_SYS_CRYPTO,"no filemap for %s",name);
+  else if(!map->Map()) {
+    PRINTF(L_SYS_CRYPTO,"mapping failed for %s",name);
+    map=0;
+    }
+  else if(map->Size()<size) {
+    PRINTF(L_SYS_CRYPTO,"%s file %s has wrong size (has=%d wanted=%d)",type,name,map->Size(),size);
+    map->Unmap();
+    map=0;
+    }
+  return map;
+}
+
+// Hash file layout is (Yankse style):
+//  Table T1 for 9x, 
+//  Table T1 for Bx,
+//  Table T1 for FX,
+//  Table T2 for 9x, 
+//  Table T2 for Bx,
+//  Table T2 for FX  (total: 1536 bytes)
+
+const unsigned char *cSeca2Prov::T1(int index) const
+{
+  static int idxTrans[] = { 0,1,2,2 };
+  if(index>=0 && index<=3) {
+    return hash->Addr()+(idxTrans[index]*256);
+    }
+  PRINTF(L_SYS_CRYPTO,"bad T1 table index %d",index);
+  return 0;
+}
+
+const unsigned char *cSeca2Prov::T2(int index) const
+{
+  static int idxTrans[] = { 0,1,2,2 };
+  if(index>=0 && index<=3) {
+    return hash->Addr()+(idxTrans[index]*256)+768;
+    }
+  PRINTF(L_SYS_CRYPTO,"bad T2 table index %d",index);
+  return 0;
+}
+
+const struct Perm *cSeca2Prov::P(int index) const
+{
+  if(index>=0 && index<MAX_PERMS && perm) {
+    return &perm[index];
+    }
+  PRINTF(L_SYS_CRYPTO,"no perm table or bad index %d",index);
+  return 0;
+}
+
+unsigned short cSeca2Prov::Sum(const unsigned char *data, int n) const
+{
+  unsigned int sum=0;
+  for(int i=1; i<n; i+=2) sum+=((data[i-1]<<8)+data[i]);
+  if(n&1) sum+=(data[n-1]<<4);
+  return sum&0x3FFF;
+}
+
+bool cSeca2Prov::DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2)
+{
+  const int sigStart=n-data[n-1]-9; // 82 <8 bytes> P5(sigStart)
+  const int encrLen=sigStart-16;
+
+#ifdef DEBUG_SECA_EXTRA
+  if(encrLen>=n || encrLen<0) printf("encrLen error in SE\n");
+  if(sigStart<16) printf("sigStart error in SE\n");
+#endif
+  if(encrLen>=n || encrLen<0 || sigStart<16) return false;
+
+  Decrypt(data+sigStart-8,key,T1,T2);
+  ChainTableXor(data+sigStart-8,Sum(data+start,sigStart-8-start));
+  int i;
+
+  for(i=start; i<encrLen; i+=8) {
+    Decrypt(data+i,key,T1,T2);
+    ChainTableXor(data+i,Sum(&data[i+8],8));
+    }
+
+  int restBytes=sigStart&0x07;
+  if(!restBytes) restBytes=8;
+
+  // Last Block
+  unsigned char lastBlock[8];
+  memset(lastBlock,0,sizeof(lastBlock));
+  memcpy(lastBlock,data+sigStart-restBytes,restBytes);
+  Decrypt(lastBlock,key,T1,T2);
+
+  Decrypt(data+i,key,T1,T2);
+  ChainTableXor(data+i,Sum(lastBlock,sizeof(lastBlock)));
+  return true;
+}
+
+void cSeca2Prov::CalcSHASignature(const unsigned char *data, int n, unsigned char *signature, bool PadMode)
+{
+  SHA_CTX ctx;
+  SHA1_Init(&ctx);
+  SHA1_Update(&ctx,data,n);
+  unsigned char Pad=0;
+  if(PadMode) {
+    unsigned char End=pData->SHA1End;
+    SHA1_Update(&ctx,&End,1);
+    int l=(n&63)+1;
+    Pad=pData->SHA1Pad;
+    if(l>62) {
+      for(; l<64; l++) SHA1_Update(&ctx,&Pad,1);
+      l=0;
+      }
+    for(; l<62; l++) SHA1_Update(&ctx,&Pad,1);
+    unsigned short s=bswap_16(n);
+    SHA1_Update(&ctx,&s,2);
+    }
+  else for(; n&63; n++) SHA1_Update(&ctx,&Pad,1);
+
+  *((unsigned int *)(signature   ))=bswap_32(ctx.h0);
+  *((unsigned int *)(signature+ 4))=bswap_32(ctx.h1);
+  *((unsigned int *)(signature+ 8))=bswap_32(ctx.h2);
+  *((unsigned int *)(signature+12))=bswap_32(ctx.h3);
+  *((unsigned int *)(signature+16))=bswap_32(ctx.h4);
+}
+
+// -- cSecaProviders & cSecaProviderLink ---------------------------------------
+
+class cSeca2ProvLink {
+friend class cSeca2Providers;
+private:
+  cSeca2ProvLink *next;
+  const unsigned short *ids;
+public:
+  cSeca2ProvLink(const unsigned short *Ids);
+  bool CanHandle(unsigned short Id);
+  virtual cSeca2Prov *Create(unsigned short Id)=0;
+  virtual ~cSeca2ProvLink() {};
+  };
+
+template<class CC> class cSeca2ProvLinkReg : public cSeca2ProvLink {
+public:
+  cSeca2ProvLinkReg(const unsigned short *Ids):cSeca2ProvLink(Ids) {}
+  virtual cSeca2Prov *Create(unsigned short Id) { return new CC(Id); }
+  };
+
+class cSeca2Providers {
+friend class cSeca2ProvLink;
+private:
+  static cSeca2ProvLink *first;
+  //
+  static void Register(cSeca2ProvLink *spl);
+public:
+  static cSeca2Prov *GetProv(unsigned short id);
+  };
+
+cSeca2ProvLink *cSeca2Providers::first=0;
+
+void cSeca2Providers::Register(cSeca2ProvLink *spl)
+{
+  LBSTART(L_CORE_DYN);
+  LBPUT("seca2prov: registering Seca2 provider");
+  for(int i=0; spl->ids[i]; i++) LBPUT(" %.4x",spl->ids[i]);
+  LBEND();
+  spl->next=first;
+  first=spl;
+}
+
+cSeca2Prov *cSeca2Providers::GetProv(unsigned short id)
+{
+  cSeca2ProvLink *spl=first;
+  while(spl) {
+    if(spl->CanHandle(id)) {
+      cSeca2Prov *sp=spl->Create(id);
+      if(sp && sp->Init()) return sp;
+      delete sp;
+      }
+    spl=spl->next;
+    }
+  return 0;
+}
+
+cSeca2ProvLink::cSeca2ProvLink(const unsigned short *Ids)
+{
+  ids=Ids;
+  cSeca2Providers::Register(this);
+}
+
+bool cSeca2ProvLink::CanHandle(unsigned short Id)
+{
+  for(int i=0; ids[i]; i++) if(ids[i]==Id) return true;
+  return false;
+}
+
+// -- cSeca2ProvSSE ------------------------------------------------------------
+
+#define beforeSig (pos-8)
+#define afterSig  (pos+1)
+
+class cSeca2ProvSSE : public cSeca2Prov {
+private:
+  virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i)=0;
+  virtual void PostSSECore1(unsigned char *data, int pos)=0;
+  virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)=0;
+  virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)=0;
+  virtual void PostSSECore4(unsigned char *data, int pos)=0;
+protected:
+  cDes des;
+  cFileMap *sse, *sseP, *cw;
+  //
+  virtual bool InitSSE(void)=0;
+  virtual void PreSSE(unsigned char *data, int pos);
+  virtual void PostSSE(unsigned char *data, int pos);
+  //
+  inline const unsigned char *SSET1(void) const { return sse->Addr(); }
+  inline const unsigned char *SSET2(void) const { return sse->Addr()+3072; }
+  inline const unsigned char *SSEPT1(void) const { return sseP->Addr(); }
+  inline const unsigned char *SSEPT2(void) const { return sseP->Addr()+80; }
+  inline const unsigned char *CWT1(void) const { return cw->Addr(); }
+public:
+  cSeca2ProvSSE(unsigned short Id);
+  virtual ~cSeca2ProvSSE();
+  virtual bool Init(void);
+  };
+
+cSeca2ProvSSE::cSeca2ProvSSE(unsigned short Id)
+:cSeca2Prov(Id)
+,des(secaPC1,secaPC2)
+{
+  sse=0; sseP=0; cw=0;
+}
+
+cSeca2ProvSSE::~cSeca2ProvSSE()
+{
+  if(sse)  sse->Unmap();
+  if(sseP) sseP->Unmap();
+  if(cw)   cw->Unmap();
+}
+
+bool cSeca2ProvSSE::Init(void)
+{
+  return InitSSE() && cSeca2Prov::Init();
+}
+
+void cSeca2ProvSSE::PreSSE(unsigned char *data, int pos)
+{
+  const unsigned char *T1=SSEPT1();
+  unsigned char tmpBuf[80];
+
+  data+=pos+5; // Start at offset 5
+  for(int i=4; i>=0; i--) {
+    int j=i*16;
+    PreSSECore(&tmpBuf[j],&data[j],i);
+    }
+  for(int i=79; i>=0; i--) data[i]=tmpBuf[i]^T1[i];
+}
+
+void cSeca2ProvSSE::PostSSE(unsigned char *data, int pos)
+{
+  PostSSECore1(data,pos);
+  // create the SHA hash buffer
+  unsigned char tmpBuf[64];
+  memcpy(tmpBuf+0,&data[beforeSig+0],4);
+  memcpy(tmpBuf+4,&data[afterSig +4],4);
+  PostSSECore2(tmpBuf+8,data,pos);
+  // Calc Signature of the generated buffer here
+  unsigned char MD[20];
+  CalcSHASignature(tmpBuf,sizeof(tmpBuf),MD,false);
+  // Prepare DES data
+  memcpy(tmpBuf+0,&data[beforeSig+4],4);
+  memcpy(tmpBuf+4,&data[afterSig +0],4);
+  // DES Enrypt
+  des.Des(tmpBuf,MD,SECA_DES_ENCR);
+  // modify data with encrypted DES data
+  PostSSECore3(data,tmpBuf,pos);
+  // save the signature
+  memcpy(tmpBuf,data+afterSig,8);
+  PostSSECore4(data,pos);
+  // put the signature in the data
+  memcpy(data+beforeSig,tmpBuf,8);
+}
+
+// -- France -------------------------------------------------------------------
+
+class cSeca2ProvFR : public cSeca2ProvSSE {
+private:
+  virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i);
+  virtual void PostSSECore1(unsigned char *data, int pos);
+  virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos);
+  virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos);
+  virtual void PostSSECore4(unsigned char *data, int pos);
+protected:
+  virtual bool InitSSE(void);
+  virtual void PostCW(unsigned char *data);
+  virtual void ChainTableXor(unsigned char *data, unsigned short index);
+  virtual void SignatureMod(unsigned char *MD, const unsigned char *PK);
+public:
+  cSeca2ProvFR(unsigned short Id);
+  };
+
+static const unsigned short IdsFR[] = { 0x80,0x81,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvFR> staticLinkFR(IdsFR);
+
+static const struct ProvData provDataFR = {
+  0x80,16895,16895,0x7F,0xF7, { 3, 0, 0, 0, 2, 4, 3, 0 }
+  };
+
+cSeca2ProvFR::cSeca2ProvFR(unsigned short Id)
+:cSeca2ProvSSE(Id)
+{
+  pData=&provDataFR;
+}
+
+bool cSeca2ProvFR::InitSSE(void)
+{
+  sse=GetMap("sse",5120,true);
+  sseP=GetMap("sse",1104,false);
+  cw=GetMap("cw",512,false);
+  return sse && sseP && cw;
+}
+
+void cSeca2ProvFR::PreSSECore(unsigned char *buf, const unsigned char *data, int i)
+{
+  const unsigned char *T1=SSET1(), *T2=SSET2();
+
+  buf[ 0]=data[0x06]^T2[i+0x0a];
+  buf[ 1]=data[0x01]^T2[i+0x00];
+  buf[ 2]=data[0x02]^T1[i+0x00];
+  buf[ 3]=data[0x03]^T2[i+0x04];
+  buf[ 4]=~data[0x04];
+  buf[ 5]=data[0x05]^T1[i+0x01];
+  buf[ 6]=data[0x00]^T2[i+0x0f];
+  buf[ 7]=data[0x07]^T1[i+0x02];
+  buf[ 8]=data[0x08]^T1[i+0x00];
+  buf[ 9]=data[0x0d]^T2[i+0x14];
+  buf[10]=data[0x0f]^T2[i+0x07];
+  buf[11]=~data[0x0b];
+  buf[12]=data[0x0c]^T1[i+0x00];
+  buf[13]=data[0x09]^T2[i+0x19];
+  buf[14]=data[0x0e]^T2[i+0x1e];
+  buf[15]=data[0x0a]^T1[i+0x01];
+}
+
+void cSeca2ProvFR::PostSSECore1(unsigned char *data, int pos)
+{
+  data[beforeSig+0]^=-(0x2d);
+  data[beforeSig+1]-=0x22;
+  data[beforeSig+2]^=0x1d;
+  data[beforeSig+3]^=-(0x68);
+  data[beforeSig+4] =~data[beforeSig + 4];
+  data[beforeSig+5]^=0x26;
+  data[beforeSig+6]^=0x09;
+  data[beforeSig+7]-=0x3e;
+  data[afterSig +0]^=-(0x5d);
+  data[afterSig +1]-=0x74;
+  data[afterSig +2]^=0x2d;
+  data[afterSig +3]^=-(0x2a);
+  data[afterSig +4]+=0x0d;
+  data[afterSig +5]^=-(0x6c);
+  data[afterSig +6]^=-(0x76);
+  data[afterSig +7]+=0x31;
+}
+
+void cSeca2ProvFR::PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)
+{
+  const unsigned char *T1=SSEPT2();
+  memcpy(buf, &T1[data[afterSig+4]+0x48], 56);
+}
+
+void cSeca2ProvFR::PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)
+{
+  data[beforeSig+4]=buf[5];
+  data[beforeSig+5]=buf[4];
+  data[beforeSig+6]=buf[7];
+  data[beforeSig+7]=buf[2];
+  data[afterSig +0]=buf[3];
+  data[afterSig +1]=buf[1];
+  data[afterSig +2]=buf[0];
+  data[afterSig +3]=buf[6];
+}
+
+void cSeca2ProvFR::PostSSECore4(unsigned char *data, int pos)
+{
+  data[afterSig+0]=data[beforeSig+3]^0x3e;
+  data[afterSig+1]=data[beforeSig+1]^0x5e;
+  data[afterSig+2]=data[beforeSig+5]^0x2f;
+  data[afterSig+3]=data[beforeSig+0]^0x77;
+  data[afterSig+4]=data[beforeSig+6]^-(0x4b);
+  data[afterSig+5]=data[beforeSig+2]^-(0x38);
+  data[afterSig+6]=data[beforeSig+7]^0x29;
+  data[afterSig+7]=data[beforeSig+4]^0x2b;
+}
+
+void cSeca2ProvFR::PostCW(unsigned char *data)
+{
+  const unsigned char *T1=SSET1(), *T2=SSET2(), *T3=SSEPT2(), *T4=CWT1();
+  unsigned int idx;
+  unsigned char key[8];
+  idx=((data[0]<<8)|data[1]);     key[0]=T3[idx & 0x3FF];
+  idx=(idx + key[0]);             key[1]=T3[idx & 0x3FF];
+  idx=((data[2]<<8)|data[3]);     key[2]=T4[idx & 0x1FF];
+  idx=idx + key[2];               key[3]=T4[idx & 0x1FF];
+  idx=((data[8+4]<<8)|data[8+5]); key[4]=T2[idx & 0x7FF];
+  idx=idx + key[4];               key[5]=T2[idx & 0x7FF];
+  idx=((data[8+6]<<8)|data[8+7]); key[6]=T1[idx & 0xBFF];
+  idx=idx + key[6];               key[7]=T1[idx & 0xBFF];
+  des.Des(data+4,key,SECA_DES_ENCR);
+}
+
+void cSeca2ProvFR::ChainTableXor(unsigned char *data, unsigned short index)
+{
+  static const unsigned char tabIdx[] = { 0x00, 0x08, 0x03, 0x1F, 0x06, 0x32, 0x12, 0x0C };
+  static const unsigned char tabXor[] = { 0x77, 0x2B, 0xC8, 0xEE, 0x2F, 0xD3, 0x22, 0x29 };
+  static const unsigned char tabPos[] = {    0,    2,    1,    6,    4,    5,    7,    3 };
+
+  unsigned int idx1, idx2;
+  unsigned char xorVal=0;
+  const unsigned char *T1=MT(), *T2=SSET1();
+
+  idx1 = (index^0x17AC) & 0x3FFF;
+  idx2 = idx1 & 0xBFF;
+  for(int i=0; i<8; i++) {
+    idx1 = (idx1 + tabIdx[i]) & 0x3FFF;
+    idx2 = (idx2 + xorVal) & 0xBFF;
+    xorVal ^= T1[idx1] ^ tabXor[i];
+    data[tabPos[i]] ^= xorVal ^ T2[idx2];
+    }
+}
+
+void cSeca2ProvFR::SignatureMod(unsigned char *MD, const unsigned char *PK)
+{
+  const unsigned char *T1=SSEPT2();
+  unsigned int idx;
+
+  idx = ((MD[18] << 8) | MD[19]) & 0x3FF;
+  MD[10]  = T1[idx];
+  MD[13] ^= PK[6];
+  idx = ((MD[15] << 8) | MD[13]) & 0x3FF;
+  MD[16]  = T1[idx];
+  des.Des(MD,MD+10,SECA_DES_ENCR);
+}
+
+// -- Netherlands --------------------------------------------------------------
+
+class cSeca2ProvNL : public cSeca2ProvSSE {
+private:
+  virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i);
+  virtual void PostSSECore1(unsigned char *data, int pos);
+  virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos);
+  virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos);
+  virtual void PostSSECore4(unsigned char *data, int pos);
+protected:
+  virtual bool InitSSE(void);
+  virtual void PostCW(unsigned char *data);
+  virtual void ChainTableXor(unsigned char *data, unsigned short index);
+  virtual bool DoSigCheck(void) { return false; }
+public:
+  cSeca2ProvNL(unsigned short Id);
+  };
+
+static const unsigned short IdsNL[] = { 0x6a,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvNL> staticLinkNL(IdsNL);
+
+static const struct ProvData provDataNL = {
+  0x6a,16895,16895,0xa3,0x3a, { 3, 0, 0, 0, 2, 4, 3, 0 }
+  };
+
+cSeca2ProvNL::cSeca2ProvNL(unsigned short Id)
+:cSeca2ProvSSE(Id)
+{
+  pData=&provDataNL;
+}
+
+bool cSeca2ProvNL::InitSSE(void)
+{
+  sse=GetMap("sse",5120,true);
+  sseP=GetMap("sse",336,false);
+  cw=GetMap("cw",512,false);
+  return sse && sseP && cw;
+}
+
+void cSeca2ProvNL::PreSSECore(unsigned char *buf, const unsigned char *data, int i)
+{
+  const unsigned char *T1=SSET1(), *T2=SSET2();
+
+  buf[0] =data[7] ^T1[i+0x17];
+  buf[1] =data[1] ^T2[i+0x07];
+  buf[2] =data[2] ^T1[i+0x03];
+  buf[3] =data[3] ^T2[i+0x0C];
+  buf[4] =~data[4];
+  buf[5] =data[5] ^T1[i+0x1E];
+  buf[6] =data[6] ^T1[i+0x11];
+  buf[7] =data[0] ^T2[i+0x0F];
+  buf[8] =data[9] ^T1[i+0x15];
+  buf[9] =data[8] ^T2[i+0x04];
+  buf[10]=data[12]^T2[i+0x07];
+  buf[11]=data[11]^T1[i+0x16];
+  buf[12]=data[10]^T1[i+0x01];
+  buf[13]=data[13]^T1[i+0x0F];
+  buf[14]=~data[14];
+  buf[15]=data[15]^T2[i+0x0B];
+}
+
+void cSeca2ProvNL::PostSSECore1(unsigned char *data, int pos)
+{
+  // modify 8 bytes before signature byte (0x82)
+  data[beforeSig+0] ^= 0xd6; data[beforeSig+1] ^= 0x96;
+  data[beforeSig+2] -= 0x51; data[beforeSig+3] ^= 0x3a;
+  data[beforeSig+4] -= 0x8d; data[beforeSig+5] ^= 0xf1;
+  data[beforeSig+6] -= 0xc2; data[beforeSig+7] ^= 0xb1;
+
+  data[afterSig+0] ^= 0x84; data[afterSig+1] ^= 0xf8;
+  data[afterSig+2] -= 0x7d; data[afterSig+3] = ~(data[afterSig+3]);
+  data[afterSig+4] ^= 0xfd; data[afterSig+5] ^= 0xd0;
+  data[afterSig+6] ^= 0x77; data[afterSig+7] ^= 0x25;
+}
+
+void cSeca2ProvNL::PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)
+{
+  const unsigned char *T1=SSEPT2();
+  memcpy(buf,&T1[(data[afterSig+6]+0x19)&0x7F],56);
+}
+
+void cSeca2ProvNL::PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)
+{
+  data[beforeSig+4]=buf[7];
+  data[beforeSig+5]=buf[0];
+  data[beforeSig+6]=buf[3];
+  data[beforeSig+7]=buf[5];
+  data[afterSig+0]=buf[6];
+  data[afterSig+1]=buf[2];
+  data[afterSig+2]=buf[1];
+  data[afterSig+3]=buf[4];
+}
+
+void cSeca2ProvNL::PostSSECore4(unsigned char *data, int pos)
+{
+  data[afterSig+0]=data[beforeSig+1] ^ 0x65; data[afterSig+1]=data[beforeSig+0] ^ 0x75;
+  data[afterSig+2]=data[beforeSig+5] ^ 0x35; data[afterSig+3]=data[beforeSig+3] ^ 0xd9;
+  data[afterSig+4]=data[beforeSig+6] ^ 0xb7; data[afterSig+5]=data[beforeSig+7] ^ 0x9a;
+  data[afterSig+6]=data[beforeSig+4] ^ 0xc7; data[afterSig+7]=data[beforeSig+2] ^ 0x1f;
+}
+
+void cSeca2ProvNL::PostCW(unsigned char *data)
+{
+  const unsigned char *T1=SSET1(), *T2=SSET2(), *T3=CWT1(), *T4=SSEPT2();
+  unsigned char key[8];
+  unsigned int off2;
+
+  off2=data[2]^data[8+6];
+  key[2]=T4[off2]; key[5]=T4[(off2+key[2])&0xff];
+
+  off2=(data[3]<<8)|data[8+4]; key[0]=T3[off2&0x1ff];
+  off2+=key[2]; key[4]=T3[off2&0x1ff];
+
+  off2=(data[0]<<8)|data[8+7]; key[7]=T2[off2&0x7ff];
+  off2+=key[0]; key[1]=T2[off2&0x7ff];
+
+  off2=(data[1]<<8)|data[8+5]; key[3]=T1[off2&0xbff];
+  off2+=key[4]; key[6]=T1[off2&0xbff];
+
+  des.Des(data+4,key,SECA_DES_ENCR);
+}
+
+void cSeca2ProvNL::ChainTableXor(unsigned char *data, unsigned short index)
+{
+  static const unsigned short tabIdx[] = { 0x000, 0x4C3, 0x5D8, 0x63A, 0x471, 0x639, 0x417, 0x6CD };
+  static const unsigned char  tabXor[] = {  0x84,  0xD6,  0x3A,  0x1F,  0x25,  0xB1,  0x7D,  0xF7 };
+  static const unsigned char tabPos1[] = {     2,     4,     3,     4,     5,     7,     6,     7 };
+  static const unsigned char tabPos2[] = {     3,     1,     5,     6,     0,     1,     0,     2 };
+
+  unsigned int idx1, idx2;
+  unsigned char xorVal=0;
+  const unsigned char *T1=MT(), *T2=SSET1();
+
+  idx1=(index^0x2B36) & 0x3FFF;
+  idx2=idx1 & 0xBFF;
+  for(int i=0; i<8; i++) {
+    idx1=(idx1 + tabIdx[i]) & 0x3FFF;
+    idx2=(idx2 + xorVal) & 0xBFF;
+    xorVal ^= T1[idx1] ^ tabXor[i];
+    data[tabPos1[i]]^=xorVal;
+    data[tabPos2[i]]^=T2[idx2];
+    }
+}
+
+#undef beforeSig
+#undef afterSig
+
+// -- Poland -------------------------------------------------------------------
+
+class cSeca2ProvPL : public cSeca2Prov {
+protected:
+  virtual void PreCW(unsigned char *data);
+  virtual void ChainTableXor(unsigned char *data, unsigned short index);
+public:
+  cSeca2ProvPL(unsigned short Id);
+  };
+
+static const unsigned short IdsPL[] = { 0x65,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvPL> staticLinkPL(IdsPL);
+
+static const struct ProvData provDataPL = {
+  0x65,16895,16895,0x96,0x69, { 3, 0, 0, 0, 2, 4, 3, 0 }
+  };
+
+static const struct Perm ptPL[MAX_PERMS] = {
+    { // 1
+    { 2, 4, 4, 3, 2, 2, 4, 4 },
+    { 3, 2, 4, 3, 4, 4, 2, 3 },
+    { 0, 0, 0, 0, 0, 0, 0, 0 },
+    { 0, 0, 0, 0, 0, 0, 0, 0 }
+  },{ // 2
+    { 4, 4, 3, 2, 2, 3, 3, 4 },
+    { 4, 2, 3, 4, 3, 2, 4, 2 },
+    { 0, 0, 0, 0, 0, 0, 0, 0 },
+    { 0, 0, 0, 0, 0, 0, 0, 0 }
+  },{ // 3
+  },{ // 4
+  },{ // 5
+  },{ // 6
+  },{ // 7
+  },{ // 8
+  },{ // 9
+    { 4, 4, 3, 2, 3, 4, 3, 2 },
+    { 2, 4, 3, 2, 2, 4, 4, 3 },
+    { 2, 3, 4, 1, 0, 0, 0, 0 },
+    { 0, 0, 0, 0, 0, 0, 0, 0 },
+  },{ // 10
+  } };
+
+cSeca2ProvPL::cSeca2ProvPL(unsigned short Id)
+:cSeca2Prov(Id)
+{
+  pData=&provDataPL; perm=ptPL;
+}
+
+void cSeca2ProvPL::PreCW(unsigned char *data)
+{
+  static const unsigned char XT[]={ 0xA0,0x12,0x23,0x35,0x46,0xB0,0xDF,0xCA, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+  static const unsigned char PT[]={ 0x05,0x04,0x06,0x07,0x03,0x00,0x01,0x02, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F };
+  unsigned char temp[16];
+
+  xxor(temp,16,data,XT);
+  for(int i=15; i>=0; i--) data[i]=temp[PT[i]];
+}
+
+void cSeca2ProvPL::ChainTableXor(unsigned char *data, unsigned short index)
+{
+  const unsigned char *T1=MT();
+  for(int i=0; i<8; i++) data[i]^=T1[index++];
+}
+
+// -- cSystemSeca ---------------------------------------------------------------
+
+class cSystemSeca : public cSystem, private cSeca {
+private:
+  cSeca2Prov *sp, *spL;
+  int emmLenCnt;
+  cDes des;
+  cRSA rsa;
+  //
+  bool GetSeca2Prov(int provId, cSeca2Prov * &sp);
+  bool Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2);
+  bool Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId);
+  void Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode);
+  void Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P);
+public:
+  cSystemSeca(void);
+  virtual ~cSystemSeca();
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemSeca::cSystemSeca(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI),
+des(secaPC1,secaPC2)
+{
+  sp=spL=0; emmLenCnt=0;
+  hasLogger=true;
+}
+
+cSystemSeca::~cSystemSeca()
+{
+  delete sp;
+  delete spL;
+}
+
+bool cSystemSeca::GetSeca2Prov(int provId, cSeca2Prov * &sp)
+{
+  if(!sp || !sp->Matches(provId)) {
+    delete sp;
+    sp=cSeca2Providers::GetProv(provId);
+    }
+  return sp!=0;
+}
+
+bool cSystemSeca::ProcessECM(const cEcmInfo *ecmD, unsigned char *data)
+{
+  bool SE=false;
+  unsigned char *ecm;
+  const int msgLen=cParseSeca::Payload(data,(const unsigned char **)&ecm);
+  const int keyNr=cParseSeca::KeyNr(data);
+
+  if(keyNr&0x80) {
+    PRINTF(L_SYS_VERBOSE,"Super Encryption enabled");
+    SE=true;
+    if(!GetSeca2Prov(ecmD->provId,sp)) {
+      if(doLog) PRINTF(L_SYS_ECM,"Seca2 provider %04x not supported",ecmD->provId);
+      return false;
+      }
+    }
+  HEXDUMP(L_SYS_VERBOSE,data,SCT_LEN(data),"ECM:");
+  if(ecm[0]==0x10) {
+    if(msgLen<0x5c) {
+      if(doLog) PRINTF(L_SYS_ECM,"ECM length too small (%d)",msgLen);
+      return false;
+      }
+
+    if(SE) {
+      HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before SSE data modification :");
+      sp->PreSSE(ecm,msgLen-0x5a);
+      HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after SSE data modification :");
+      }
+
+    const int rsaKeynr=(ecm[1]&0x0F)+'0';
+    cBN exp, mod;
+    cPlainKey *rsaKey;
+    if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('E',rsaKeynr),-1))) {
+      if(doLog) PRINTF(L_SYS_KEY,"missing %04x E%c key",ecmD->provId,rsaKeynr);
+      return false;
+      }
+    rsaKey->Get(exp);
+    if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('M',rsaKeynr),-1))) {
+      if(doLog) PRINTF(L_SYS_KEY,"missing %04x M%c key",ecmD->provId,rsaKeynr);
+      return false;
+      }
+    rsaKey->Get(mod);
+    HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before RSA decrypt :");
+    if(rsa.RSA(ecm+(msgLen-0x5a),ecm+(msgLen-0x5a),0x5a,exp,mod)!=0x5a) {
+      PRINTF(L_SYS_CRYPTO,"RSA decrypt failed (ECM)");
+      return false;
+      }
+    HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after RSA decrypt :");
+
+    if(SE) { // PostSSE needed
+      const int hashPtr=msgLen-ecm[msgLen-1]-9;
+      if(hashPtr>=msgLen-9 || ecm[hashPtr]!=0x82) {
+        PRINTF(L_SYS_CRYPTO,"RSA decrypt failed, signature pointer not found");
+        return false;
+        }
+      HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before SSE result modification :");
+      sp->PostSSE(ecm,hashPtr);
+      HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after SSE result modification :");
+      }
+    }
+
+  const unsigned char *T1=T1_S1, *T2=T2_S1;
+  int decrLen=msgLen;
+  if(SE) {
+    if(!(T1=sp->T1(KEY2INDEX(keyNr))) ||
+       !(T2=sp->T2(KEY2INDEX(keyNr)))) return false;
+    decrLen-=ecm[msgLen-1]; if(decrLen<8) return false;
+    }
+
+  bool key8=!(cParseSeca::SysMode(data)&0x10);
+  cPlainKey *pk=0;
+  cKeySnoop ks(this,'S',ecmD->provId,keyNr&0x0F);
+  unsigned char *buff=AUTOMEM(msgLen);
+  while((pk=keys.FindKey('S',ecmD->provId,keyNr&0x0F,key8?8:16,pk))) {
+    memcpy(buff,ecm,msgLen); // if decoding fails we need the original data
+    
+    unsigned char PK[16], signature[20];
+    pk->Get(PK);
+    if(key8) memcpy(&PK[8],&PK[0],8); // duplicate key
+
+    if(SE) {
+      sp->CalcSHASignature(buff,decrLen-8,signature);
+      LDUMP(L_SYS_VERBOSE,signature,8,"signature before encrypt :");
+      sp->SignatureMod(signature,PK);
+      Encrypt(signature,PK,T1,T2);
+      LDUMP(L_SYS_VERBOSE,signature,8,"signature after encrypt :");
+
+      if(sp->DoSigCheck() && memcmp(signature,&buff[decrLen-8],8)) {
+        PRINTF(L_SYS_VERBOSE,"signature check failed before SE decrypt");
+        continue;
+        }
+      HEXDUMP(L_SYS_VERBOSE,buff+2,msgLen-2,"ECM before SE decrypt :");
+      if(!sp->DecryptSE(buff+2,PK,msgLen-2,8,T1,T2)) {
+        PRINTF(L_SYS_VERBOSE,"SE decrypt failed");
+        continue;
+        }
+      HEXDUMP(L_SYS_VERBOSE,buff+2,msgLen-2,"ECM after SE decrypt :");
+      }
+    else {
+      CalcSignature(buff,decrLen-8,signature,PK,T1,T2);
+      }
+    unsigned char hashDW[28]; // actually 16 bytes used for processing, but SHA needs more
+    unsigned char *cCW=0;
+    const unsigned char *nano51Data=0;
+    int nr0F=0;
+
+    for(int i=0 ; i<decrLen; ) {
+      int param=buff[i++];
+      int extra=(param >> 4) & 0x0f;
+      switch(extra) {
+        case 0x0d: extra=0x10; break;
+        case 0x0e: extra=0x18; break;
+        case 0x0f: extra=0x20; break;
+        }
+      switch(param) {
+        case 0x0f:
+          if(SE) nr0F++;
+          break;
+        case 0x51:
+          if(SE) nano51Data=&buff[i];
+          break;
+        case 0xd1:
+          cCW=&buff[i];
+          if(SE) {
+            sp->CalcSHASignature(buff,i,hashDW,false);
+            memcpy(&hashDW[20],hashDW,8);
+            Decrypt(&hashDW[20],PK,sp->T1(0),sp->T2(0));
+            sp->CalcSHASignature(hashDW,28,hashDW,false);
+            }
+          break;
+        case 0x82:
+          if((!SE || sp->DoSigCheck()) && memcmp(&buff[i],signature,8)) { 
+            PRINTF(L_SYS_VERBOSE,"signature check failed after decrypt");
+            cCW=0; i=decrLen;
+            }
+          break;
+        }
+      i+=extra;
+      }
+    if(cCW) {
+      if(!nr0F || Process0FNano(nr0F,hashDW,PK,T1,T2)) {
+        if(SE) sp->PreCW(cCW);
+        if(!nano51Data || Process51Nano(cCW,nano51Data,hashDW,T1,T2,ecmD->provId)) {
+          LDUMP(L_SYS_VERBOSE,cCW,16,"crypted CW :");
+          Decrypt(&cCW[0],PK,T1,T2); Decrypt(&cCW[8],PK,T1,T2);
+          LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW :");
+          if(SE) {
+            LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW before modification :");
+            sp->PostCW(cCW);
+            LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW after modification :");
+            }
+          memcpy(cw,cCW,16);
+          ks.OK(pk);
+          return true;
+          }
+        }
+      }
+    }
+  return false;
+}
+
+bool cSystemSeca::Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2)
+{
+  const unsigned char *MT=sp->MT();
+  unsigned char buffB[8];
+
+  LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: initial hashDW:");
+  LDUMP(L_SYS_VERBOSE,PK,16,"0f: PK:");
+  while(count--) {
+    unsigned char buffA[8];
+    swap8_4(hashDW);
+    memcpy(buffA,hashDW,8);
+    Decrypt(hashDW,PK,sp->T1(0),sp->T2(0));
+    LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: hashDW:");
+
+    unsigned char buffE[8];
+    int off=WORD(hashDW,0,0x3FFF);
+    xxor(buffE,8,&MT[off],&MT[0x3FFF-off]);
+    xxor(buffE,8,&MT[( ((buffE[0] ^ hashDW[6])<<8)
+                     +  buffE[7] ^ hashDW[7]     )&0x3FFF],buffE);
+    LDUMP(L_SYS_VERBOSE,buffE,8,"0f: buffE:");
+
+    unsigned char buffC[16];
+    off=WORD(hashDW,2,0x3FFF);
+    xxor(buffC,16,&MT[off],&MT[0x3FFF-off]);
+    xxor(buffC,16,&MT[( ((hashDW[6] ^ buffC[0])<<8)
+                      + (hashDW[7] ^ buffC[15])   )&0x3FFF],buffC);
+    LDUMP(L_SYS_VERBOSE,buffC,16,"0f: buffC:");
+
+    off=WORD(hashDW,4,0x3FFF);
+    for(int i=0; i<16; ++i) off=WORD(MT,off,0x3FFF);
+    if(off+512 >= sp->MTSize()) {
+      printf("BUG!!!: error: masking table overflow\n");
+      return false;
+      }
+    PRINTF(L_SYS_VERBOSE,"0f: off=%04x",off);
+
+    memcpy(buffB,buffE,8);
+    Decrypt(buffB,buffC,&MT[off],&MT[off+256]);
+    LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after 1st decr:");
+    xxor(buffB,8,buffB,buffE);
+    LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after xor:");
+    Decrypt(buffB,PK,T1,T2);
+    LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after 2nd decr:");
+    xxor(hashDW,8,buffA,buffB);
+    }
+
+  for(int i=0; i<8; ++i) {
+    PK[i] = buffB[i];
+    PK[i+8] = ~sn8(buffB[i]);
+    }
+  LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: hashDW:");
+  LDUMP(L_SYS_VERBOSE,PK,16,"0f: new PK:");
+  return true;
+}
+
+void cSystemSeca::Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode)
+{
+  switch(crypto) {
+    case 0: // Xor
+      PRINTF(L_SYS_VERBOSE,"51: XOR crypto selected");
+      xxor(data+0,8,key,data+0); xxor(data+8,8,key,data+8);
+      break;
+    case 1: // Seca crypto, with 9x table, always!
+      {
+      unsigned char PK[16];
+      const unsigned char *T1 = sp->T1(0);
+      const unsigned char *T2 = sp->T2(0);
+      memcpy(PK+0,key,8); memcpy(PK+8,key,8);
+      PRINTF(L_SYS_VERBOSE,"51: SECA crypto selected: %s",mode?"decrypt":"encrypt");
+      if(mode) { Decrypt(data,PK,T1,T2); Decrypt(data+8,PK,T1,T2); }
+      else     { Encrypt(data,PK,T1,T2); Encrypt(data+8,PK,T1,T2); }
+      break;
+      }
+    case 2: // DES crypto (Modified PC1,PC2)
+      PRINTF(L_SYS_VERBOSE,"51: DES crypto selected: %s",mode?"decrypt":"encrypt");
+      if(mode) { des.Des(data,key,SECA_DES_DECR); des.Des(data+8,key,SECA_DES_DECR); }
+      else     { des.Des(data,key,SECA_DES_ENCR); des.Des(data+8,key,SECA_DES_ENCR); }
+      break;
+    case 3: // Additional Algo
+      PRINTF(L_SYS_VERBOSE,"51: Additional crypto selected: %s",mode?"decrypt":"encrypt");
+      AdditionalAlgo(data,key,mode); AdditionalAlgo(data+8,key,mode);
+      break;
+    }
+}
+
+bool cSystemSeca::Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId)
+{
+  const int pMode=(PermData[0]&0x3F)-1;
+  const struct Perm *PT=sp->P(pMode);
+  if(!PT) {
+    PRINTF(L_SYS_VERBOSE,"51: failed to get permutation table");
+    return false;
+    }
+  unsigned char buffF[8], buffG[8];
+  switch(pMode) {
+    case 0x00:
+    case 0x01:
+      {
+      // Permutation 1
+      Permute(buffF,PermData,PT->P1);
+      LDUMP(L_SYS_VERBOSE,buffF,8,"51: buffF:");
+      xxor(buffG,8,buffF,hashDW);
+      const int addAlgoMode=(PermData[0]&1)^1;
+      for(int i=(PermData[1]&0x0f); i>0; i--) AdditionalAlgo(buffG,hashDW+8,addAlgoMode);
+      // Permutation 2
+      Permute(buffF,PermData,PT->P2);
+      xxor(buffG,8,buffF,buffG);
+      LDUMP(L_SYS_VERBOSE,buffG,8,"51: buffG:");
+      // Permutation 3
+      Permute(buffF,PermData,PT->P3);
+      xxor(buffG,8,buffF,buffG);
+      // Permutation 4
+      Permute(buffF,PermData,PT->P4);
+      xxor(buffG,8,buffF,buffG);
+      break;
+      }
+    case 0x08:
+      {
+      unsigned char buff1[128], buff2[64], buff3[9];
+      // prepare buffers for rsa data
+      for(int i=0; i<20; i++)
+        buff1[i]=buff2[i]=buff2[i+20]=buff2[i+40]=hashDW[i];
+      memcpy(buff3,PermData+2,3);
+      for(int i=0; i<8; i++) buff1[i] = buff3[PT->P1[i]-2] ^= buff1[i];
+      LDUMP(L_SYS_VERBOSE,buff1,20,"51: permuted hashDW:");
+      for(int i=0; i<20; i++)
+        buff1[i+24]=buff1[i+44]=buff1[i+68]=buff1[i+88]=buff1[i+108]=buff1[i];
+      LBSTARTF(L_SYS_VERBOSE);
+      LBPUT("51: nano51Data:");
+      for(int i=0; i<4; i++) {
+        unsigned char t=PermData[PT->P3[i]];
+        if(PT->P3[i]==1) t&=0x0F;
+        buff1[i+20]=buff1[i+64]=buff2[i+60]=t;
+        LBPUT(" %02x",t);
+        }
+      LBEND();
+      HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1:");
+      HEXDUMP(L_SYS_VERBOSE,buff2,64,"51: buff2:");
+
+      memcpy(buff3,buff1,9);
+      for(int j=0; j<64; j++) { // XOR even with buff2, odd with 0xFF
+        buff1[ j*2   ] ^= buff2[j];
+        buff1[(j*2)+1] ^= 0xFF;
+        }
+      HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1 rsa prepped:");
+
+      // RSA decrypt
+      {
+      cBN exp, mod;
+      cPlainKey *pk;
+      if(!(pk=keys.FindKey('S',provId,MBC3('E','9',N51_MAGIC),-1))) {
+        if(doLog) PRINTF(L_SYS_KEY,"missing %04x N51 E9 key",provId);
+        return false;
+        }
+      pk->Get(exp);
+      if(!(pk=keys.FindKey('S',provId,MBC3('M','9',N51_MAGIC),-1))) {
+        if(doLog) PRINTF(L_SYS_KEY,"missing %04x N51 M9 key",provId);
+        return false;
+        }
+      pk->Get(mod);
+      if(rsa.RSA(buff1,buff1,128,exp,mod)<9) return false;
+      HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1 rsa decrypted:");
+      }
+
+      unsigned int sum=0;
+      for(int j=0; j<9; j++) {
+        unsigned int nextSum=(buff1[j]&0x80) ? 1:0;
+        buff1[j]=(sum+2*buff1[j])^buff3[j];
+        sum=nextSum;        
+        }
+      LDUMP(L_SYS_VERBOSE,buff1,9,"51: buff1 after sumalgo:");
+
+      memcpy(buffG,buff1,8);
+      memcpy(buff3,PermData+2,3);
+      for(int i=0; i<8; i++) buffG[i] = buff3[PT->P2[i]-2] ^= buffG[i];
+      break;
+      }
+
+    default:
+      PRINTF(L_SYS_VERBOSE,"51: data incorrect or proccessing not needed");
+      return false;
+    }
+  LDUMP(L_SYS_VERBOSE,buffG,8,"51: cryptokey:");
+  Crypto51Nano(CW,buffG,(PermData[1]>>6)&3,PermData[0]&0x80);
+  // Final Permutation
+  Permute(buffF,PermData,sp->FP());
+  xxor(CW+0,8,CW+0,buffF); xxor(CW+8,8,CW+8,buffF);
+  LDUMP(L_SYS_VERBOSE,hashDW,8,"51: cryptokey:");
+  Crypto51Nano(CW,hashDW,(PermData[1]>>4)&0x03,PermData[0]&0x40);
+  LDUMP(L_SYS_VERBOSE,CW,16,"51: new encrypted CW:");
+  return true;
+}
+
+void cSystemSeca::Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P)
+{
+  for(int i=7; i>=0; i--) data[i] = P[i] ? pdata[P[i]] : 0;
+}
+
+void cSystemSeca::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  if(buffer[0]!=0x84) return; // we only support shared updates
+
+  int msgLen=cParseSeca::CmdLen(buffer);
+  if(msgLen!=0x55 && msgLen!=0x63) {
+    if(++emmLenCnt<=5)
+      PRINTF(L_SYS_EMM,"length of EMM does not match (%02x <> 55/63)%s",msgLen,emmLenCnt==5 ? " ....":"");
+    return;
+    }
+  emmLenCnt=0;
+  unsigned char *emm;
+  msgLen=cParseSeca::Payload(buffer,(const unsigned char **)&emm);
+  int provId=cParseSeca::ProvId(buffer);
+  bool SE=false;
+  const unsigned char *T1=T1_S1, *T2=T2_S1;
+  int decrLen=msgLen;
+  if(emm[0]==0x10) { // de-SSE
+    const int rsaKeynr = emm[1]+'0';
+    cPlainKey *pk;
+    cBN exp, mod;
+    
+    if(!(pk=keys.FindKeyNoTrig('S',provId,MBC3('E',rsaKeynr,EMM_MAGIC),-1))) return;
+    pk->Get(exp);
+    if(!(pk=keys.FindKeyNoTrig('S',provId,MBC3('M',rsaKeynr,EMM_MAGIC),-1))) return;
+    pk->Get(mod);
+    if(rsa.RSA(emm+2,emm+2,msgLen-2,exp,mod)!=msgLen-2) return;
+    const int keyNr=cParseSeca::KeyNr(buffer);
+    if(keyNr&0x80) {
+      SE=true;
+      decrLen-=emm[msgLen-1]; 
+      if(!GetSeca2Prov(provId,spL) ||
+         !(T1=spL->T1(KEY2INDEX(keyNr))) ||
+         !(T2=spL->T2(KEY2INDEX(keyNr))) ||
+         decrLen<8) return;
+      }
+    }
+
+  unsigned char *buff=AUTOMEM(msgLen);
+  for(cSecaCardInfo *ci=Scards.First(); ci; ci=Scards.Next(ci)) {
+    if(ci->MatchEMM(buffer) || (CheckNull(ci->sa,sizeof(ci->sa)) && ci->MatchID(buffer))) {
+      unsigned char MK[16];
+      if(cParseSeca::SysMode(buffer)&0x10) {
+        if(ci->KeySize()!=16) continue; // don't bother
+        memcpy(&MK[0],ci->key,16);
+        }
+      else {
+        memcpy(&MK[0],ci->key,8);
+        memcpy(&MK[8],ci->key,8);
+        }
+
+      unsigned char signature[20];
+      memcpy(buff,emm,msgLen); // if decoding fails we need the original de-sse'd data
+
+      if(!SE) {
+        for(int i=0 ; i<=64 && i<msgLen-8; i+=8) Decrypt(&buff[i],MK,T1,T2);
+        CalcSignature(buff,decrLen-8,signature,MK,T1,T2);
+        }
+      else {
+        spL->CalcSHASignature(buff,decrLen-8,signature);
+        Encrypt(signature,MK,T1,T2);
+        if(memcmp(signature,&buff[decrLen-8],8) ||
+           !spL->DecryptSE(buff+2,MK,msgLen-2,0,T1,T2)) continue;
+        }
+
+      unsigned char keyN[4], *key[4];
+      int pos, len;
+      unsigned int numKeys=0;
+      bool sigOk=false;
+      for(pos=0 ; pos<decrLen ; pos+=len) {
+        int cmd=buff[pos++];
+        len=(cmd>>4)&0x0f;
+        switch(len) {
+          case 0x0b: len=0x0e; break;
+          case 0x0d: len=0x10; break;
+          case 0x0e: len=0x18; break;
+          case 0x0f: len=0x20; break;
+          }
+        switch(cmd) {
+          case 0x82: // signature
+            if(!memcmp(signature,&buff[pos],8)) sigOk=true;
+            pos=decrLen;
+            break;
+          case 0x90: // new primary key
+            if(numKeys<sizeof(keyN)) {
+              keyN[numKeys]=buff[pos] & 0x0f;
+              key[numKeys]=&buff[pos+1];
+              numKeys++;
+              }
+            break;
+          }
+        }
+      char str[20],str2[20],str3[20];
+      if(sigOk) {
+        LBSTARTF(L_SYS_EMM);
+        LBPUT("ID %s SA %s",HexStr(str,SID(buffer),2),HexStr(str2,SA(buffer),3));
+        LBPUT(" - OK (CI ID %s SA %s KEY01 %s)",HexStr(str,ci->provId,2),HexStr(str2,ci->sa,3),HexStr(str3,ci->key,8));
+        for(unsigned int i=0 ; i<numKeys ; i++) {
+          Decrypt(key[i],MK,T1,T2);
+          LBPUT(" KEY %02x %s",keyN[i],HexStr(str,key[i],8));
+          FoundKey();
+          if(keys.NewKey('S',provId,keyN[i],key[i],8)) NewKey();
+         }
+        LBEND();
+        break;
+        }
+      else if(!CheckNull(ci->sa,sizeof(ci->sa)))
+        PRINTF(L_SYS_EMM,"ID %s SA %s - FAIL",HexStr(str,SID(buffer),2),HexStr(str2,SA(buffer),3));
+      }
+    }
+  return;
+}
+
+// -- cSystemLinkSeca ----------------------------------------------------------
+
+class cSystemLinkSeca : public cSystemLink {
+public:
+  cSystemLinkSeca(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemSeca; }
+  };
+
+static cSystemLinkSeca staticInit;
+
+cSystemLinkSeca::cSystemLinkSeca(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkSeca::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/seca/seca.mk
new file mode 100644 (file)
index 0000000..19ba449
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Seca
+#
+TARGET = seca
+OBJS   = seca.o
+LIBS   = -lcrypto
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.c
new file mode 100644 (file)
index 0000000..95a8877
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <linux/dvb/dmx.h>
+
+#include <openssl/des.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "filter.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_SHL           0x4A60
+
+#define SYSTEM_NAME          "@SHL"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SHL)
+
+#define L_SYS        15
+#define L_SYS_HOPPER LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_HSTATS LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_ALL    LALL(L_SYS_HSTATS)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+  "shl",
+  { L_SYS_DEFNAMES,"pidHopper","hopperStats" }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+// evil hack, but it's not worth to put much effort in a dead system ...
+static cTimeMs __time;
+#define time_ms() ((int)__time.Elapsed())
+
+static cPlainKeyTypeReg<cPlainKeyStd,'Z',false> KeyReg;
+
+// -- cPesFilter ---------------------------------------------------------------
+
+class cPesFilter : public cPidFilter {
+public:
+  cPesFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+  virtual void Start(int Pid, int Section, int Mask, int Mode, bool Crc);
+  };
+
+cPesFilter::cPesFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+:cPidFilter(Id,Num,DvbNum,IdleTime)
+{}
+
+void cPesFilter::Start(int Pid, int Section, int Mask, int Mode, bool Crc)
+{
+  if(fd>=0) {
+    Stop();
+    struct dmx_pes_filter_params FilterParams;
+    FilterParams.input=DMX_IN_FRONTEND;
+    FilterParams.output=DMX_OUT_TAP;
+    FilterParams.pes_type=DMX_PES_OTHER;
+    FilterParams.flags=DMX_IMMEDIATE_START;
+    FilterParams.pid=Pid;
+    CHECK(ioctl(fd,DMX_SET_PES_FILTER,&FilterParams));
+    pid=Pid;
+    active=true;
+    }
+}
+
+// -- cPidHopper ---------------------------------------------------------------
+
+struct FilterPacket {
+  struct FilterPacket *next;
+  int lastTime, dataLen;
+  unsigned char lastData[1];
+  };
+  
+struct FilterData {
+  cPidFilter *filter;
+  int lastTime, lastRead, createTime, readCount;
+  struct FilterPacket *first, *last;
+  };
+
+class cPidHopper : public cAction {
+private:
+  struct FilterData *d;
+  int numFilters;
+  cMutex sleepMutex;
+  cCondVar sleep;
+  //
+  void PurgeFilter(struct FilterData *dd);
+  void PurgePacket(struct FilterData *dd, struct FilterPacket *p);
+protected:
+  virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+  virtual cPidFilter *CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+public:
+  cPidHopper(int DvbNum, int NumFilters);
+  virtual ~cPidHopper();
+  int Read(int pid, int & now, unsigned char *buff, int len, int timeout);
+  bool AddPid(int pid);
+  void CutOff(int now);
+  };
+
+cPidHopper::cPidHopper(int DvbNum, int NumFilters)
+:cAction("pidhopper",DvbNum)
+{
+  numFilters=NumFilters;
+  d=MALLOC(struct FilterData,NumFilters);
+  memset(d,0,sizeof(struct FilterData)*NumFilters);
+  PRINTF(L_SYS_HOPPER,"pid hopper started (%d filters)",numFilters);
+}
+
+cPidHopper::~cPidHopper()
+{
+  Lock();
+  int now=time_ms(), num=1;
+  PRINTF(L_SYS_HSTATS,"active pids: (now=%d)\n",now);
+  for(int i=0; i<numFilters; i++) {
+    struct FilterData *dd=&d[i];
+    if(dd->filter) {
+      if(dd->filter->Pid()>=0)
+        PRINTF(L_SYS_HSTATS,"%2d. pid %04x lastTime=%4d lastRead=%4d created=%4d count=%4d",
+                num++,
+                dd->filter->Pid(),
+                dd->lastTime>0 ? (now-dd->lastTime)/1000 : -1,
+                dd->lastRead>0 ? (now-dd->lastRead)/1000 : -1,
+                dd->createTime>0 ? (now-dd->createTime)/1000 : -1,
+                dd->readCount);
+      DelFilter(dd->filter);
+      dd->filter=0;
+      }
+    PurgeFilter(dd);
+    }
+  Unlock();
+  free(d);
+}
+
+void cPidHopper::PurgeFilter(struct FilterData *dd)
+{
+  if(dd->first && dd->last) PurgePacket(dd,dd->last);
+}
+
+void cPidHopper::PurgePacket(struct FilterData *dd, struct FilterPacket *p)
+{
+  while(dd->first) {
+    struct FilterPacket *del=dd->first;
+    dd->first=del->next;
+    if(dd->filter) PRINTF(L_SYS_HOPPER,"PID %04x purging packet %p (time=%d)",dd->filter->Pid(),del,del->lastTime);
+    free(del);
+    if(del==p) break;
+    }
+  if(!dd->first) dd->last=0;
+}
+
+void cPidHopper::CutOff(int now)
+{
+  Lock();
+  PRINTF(L_SYS_HOPPER,"cutoff (now=%d)",now);
+  for(int i=0; i<numFilters; i++) {
+    struct FilterData *dd=&d[i];
+    struct FilterPacket *p=dd->first, *del=0;
+    while(p) {
+      if(p->lastTime>=now) break;
+      del=p;
+      p=p->next;
+      }
+    if(del) {
+      PurgePacket(dd,del);
+      dd->lastTime=dd->first ? dd->first->lastTime : -1;
+      }
+    }
+  Unlock();
+}
+
+cPidFilter *cPidHopper::CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+{
+  cPidFilter *filter=new cPesFilter(Id,Num,DvbNum,IdleTime);
+  d[Num].filter=filter;
+  d[Num].lastTime=-1;
+  d[Num].lastRead=-1;
+  d[Num].createTime=time_ms();
+  d[Num].readCount=0;
+  d[Num].first=0;
+  d[Num].last=0;
+  return filter;
+}
+
+void cPidHopper::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+  if(data && len>0) {
+    int now=time_ms();
+    for(int i=0; i<numFilters; i++)
+      if(d[i].filter==filter) {
+        do {
+          int l=min(len,184);
+          struct FilterPacket *p=(struct FilterPacket *)malloc(sizeof(struct FilterPacket)+l-1);
+          if(p) {
+            p->next=0;
+            memcpy(p->lastData,data,l);
+            p->dataLen=l;
+            p->lastTime=now;
+            if(d[i].first)
+              d[i].last->next=p;
+            else {
+              d[i].first=p;
+              d[i].lastTime=now;
+              }
+            d[i].last=p;
+            PRINTF(L_SYS_HOPPER,"PID %04x queued packet %p (time=%d len=%d) (%s)",d[i].filter->Pid(),p,p->lastTime,l,d[i].first==p?"first":"queue");
+            }
+          len-=l; data+=l;
+          } while(len>0);
+        sleep.Broadcast();
+        break;
+        }
+    }
+}
+
+bool cPidHopper::AddPid(int pid)
+{
+  cPidFilter *filter=NewFilter(0);
+  if(!filter) return false;
+  filter->Start(pid,0,0,0,false);
+  return true;
+}
+
+int cPidHopper::Read(int pid, int & now, unsigned char *buff, int len, int timeout)
+{
+  Lock();
+  timeout+=time_ms();
+  PRINTF(L_SYS_HOPPER,"PID %04x read (now=%d)",pid,now);
+  int n=-1;
+  while(time_ms()<timeout) {
+    struct FilterData *dd=0, *old=0;
+    for(int i=0; i<numFilters; i++)
+      if(d[i].filter) {
+        if(d[i].filter->Pid()==pid) { dd=&d[i]; break; }
+        if(!old ||
+           (d[i].lastRead<0 && d[i].createTime<old->createTime) ||
+           (d[i].lastRead>=0 && d[i].lastRead<old->lastRead))
+          old=&d[i];
+        }
+    if(!dd) {
+      LBSTARTF(L_SYS_VERBOSE);
+      LBPUT("NEW ");
+      if(!AddPid(pid)) {
+        if(!old) break;
+        LBPUT("{0x%04x/%d/%d} ",old->filter->Pid(),old->lastRead>0 ? (time_ms()-old->lastRead)/1000:-1,old->lastTime>0 ? (time_ms()-old->lastTime)/1000:-1);
+        old->filter->Stop(); // purge LRU filter
+        PurgeFilter(old);
+        old->lastTime=-1;
+        old->lastRead=-1;
+        old->createTime=time_ms();
+        old->readCount=0;
+        old->filter->Start(pid,0,0,0,false);
+        }
+      LBEND();
+      continue;
+      }
+    for(struct FilterPacket *p=dd->first; p; p=p->next) {
+      if(p->lastTime>=now) {
+        n=min(len,p->dataLen);
+        memcpy(buff,p->lastData,n);
+        now=p->lastTime;
+        PRINTF(L_SYS_HOPPER,"PID %04x returning packet %p (time=%d)",dd->filter->Pid(),p,p->lastTime);
+        PurgePacket(dd,p);
+        dd->lastRead=time_ms();
+        dd->readCount++;
+        dd->lastTime=dd->first ? dd->first->lastTime : -1;
+        break;
+        }
+      else {
+        PRINTF(L_SYS_HOPPER,"PID %04x skipping packet %p (time=%d)",dd->filter->Pid(),p,p->lastTime);
+        if(!p->next) {
+          PRINTF(L_SYS_HOPPER,"PID %04x queue empty",dd->filter->Pid());
+          PurgePacket(dd,p);
+          break;
+          }
+        }
+      }
+    if(n>0) break;
+    Unlock();
+    sleepMutex.Lock();
+    sleep.TimedWait(sleepMutex,200);
+    sleepMutex.Unlock();
+    Lock();
+    }
+  Unlock();
+  return n;
+}
+
+// -- cShl ---------------------------------------------------------------------
+
+class cShl {
+private:
+  DES_key_schedule sched;
+public:
+  void SetKey( const unsigned char *key);
+  void Decode(unsigned char *data, int n);
+  };
+
+void cShl::SetKey(const unsigned char *key)
+{
+  DES_key_sched((DES_cblock *)key,&sched);
+}
+
+void cShl::Decode(unsigned char *data, int n)
+{
+  int r=n&7;
+  n-=r;
+  for(int i=0; i<n; i+=8)
+    DES_ecb_encrypt((DES_cblock *)&data[i],(DES_cblock *)&data[i],&sched,DES_DECRYPT);
+  if(r) { // possible last incomplete block
+    DES_cblock out;
+    DES_ecb_encrypt((DES_cblock *)&data[n],&out,&sched,DES_DECRYPT);
+    memcpy(&data[n],out,r);
+    }
+}
+
+// -- SHL provider -------------------------------------------------------------
+
+struct ShlProv {
+  const char *name;
+  unsigned short id;
+  bool hasXor;
+  int filters, pidRange;
+  const unsigned short pids[128];
+  const unsigned char xorTab[16];
+  };
+
+static const struct ShlProv prov[] = {
+/*
+  { "FreeX",0x4A90,false,40,0x1300,
+    { 0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,0x1390,
+      0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,0x1398,
+      0x1399,0x139a,0x139b,0x139c,0x13a4,0x13a5,0x13a6,0x13a7,
+      0x13a8,0x13a9,0x13bf,0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,
+      0x13c5,0x13c6,0x13c7,0x13c8,0x13c9,0x13ca,0x13cb,0x13cc },
+    {} },
+*/
+  { "FullX-2",0x4A90,false,128,0x0000,
+    { 0x0bb9,0x0bba,0x0bbc,0x0bbf,0x0bc0,0x0bc2,0x0bc3,0x0bc4,
+      0x0bc7,0x0bc8,0x0bc9,0x0bcc,0x0bcd,0x0bce,0x0bcf,0x0bd0,
+      0x0bd3,0x0bd5,0x0bd6,0x0bd7,0x0bd8,0x0bd9,0x0bdb,0x0bdd,
+      0x0bdf,0x0be0,0x0be1,0x0be2,0x0be3,0x0be4,0x0be5,0x0be6,
+      0x0be7,0x0be8,0x0be9,0x0bea,0x1784,0x177c,0x178a,0x1796,
+      0x1774,0x1772,0x17a2,0x1783,0x1792,0x177b,0x1775,0x179b,
+      0x1794,0x1789,0x1778,0x1799,0x1793,0x1797,0x1782,0x1779,
+      0x179e,0x1787,0x178d,0x177d,0x1771,0x1791,0x1776,0x1788,
+      0x178f,0x179c,0x177e,0x178b,0x1785,0x1781,0x0bc5,0x17a1,
+      0x0bbe,0x179f,0x0bdc,0x1780,0x0bca,0x0bd2,0x0bbd,0x179a,
+      0x1773,0x178e,0x1795 },
+    {} },
+  { "FullX",0x6997,true,60,0x1700,
+    { 0x178e,0x178f,0x1783,0x1780,0x1776,0x177b,0x1772,0x1779,
+      0x1785,0x179e,0x1791,0x1771,0x177e,0x1793,0x1778,0x179a,
+      0x17a2,0x17a1,0x178d,0x1787,0x1797,0x1796,0x177d,0x1781,
+      0x1799,0x1794,0x179b,0x1784,0x1795,0x177c,0x1782,0x179c,
+      0x1773,0x1788,0x1789,0x178b,0x179f,0x1775,0x1792,0x1774,
+      0x178a },
+    { 0x4E,0x9E,0x5C,0xFA,0x62,0x80,0x4C,0x86,0x56,0xDF,0x7E,0x03,0x9B,0x05,0xB2,0xE7 } },
+  { "DP",0x6996,true,48,0x1300,
+    { 0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,0x1390,
+      0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,0x1398,
+      0x1399,0x139a,0x139b,0x139c,0x13a4,0x13a5,0x13a6,0x13a7,
+      0x13a8,0x13a9,0x13bf,0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,
+      0x13c5,0x13c6,0x13c7,0x13c8,0x13c9,0x13ca,0x13cb,0x13cc },
+    { 0x30,0x61,0xD7,0xC0,0x8E,0x41,0x2C,0xB9,0xAA,0x50,0xB2,0xF4,0x5B,0x2E,0x84,0xCF } }
+  };
+
+static const struct ShlProv *FindProv(unsigned short id)
+{
+  for(unsigned int i=0; i<(sizeof(prov)/sizeof(struct ShlProv)); i++)
+    if(prov[i].id==id) return &prov[i];
+  return 0;
+}
+
+// -- cSystemShl ---------------------------------------------------------------
+
+#define ENC_OFF 12 // encrypted bytes offset
+#define ENC_LEN 24 // encrypted bytes length
+
+#define KEY_OFF   7
+#define PID_OFF   15
+#define EMASK_OFF 8
+#define OMASK_OFF 12
+#define CW_OFF    17
+
+class cSystemShl : public cSystem, private cShl {
+private:
+  cPidHopper *ph;
+  int prvId;
+  bool hasPrvId;
+  cMutex mutex;
+  cCondVar wait;
+  //
+  void ProcessCw(const unsigned char *data, int prvId, const unsigned char *xorTable);
+public:
+  cSystemShl(void);
+  virtual ~cSystemShl();
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+  };
+
+cSystemShl::cSystemShl(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  ph=0; hasPrvId=false;
+  hasLogger=needsLogger=true; maxEcmTry=20;
+}
+
+cSystemShl::~cSystemShl()
+{
+  delete ph;
+}
+
+bool cSystemShl::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+  static const unsigned char tester[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+  // in old SHL algo ECM starts with 12 0xFF
+  if(SCT_LEN(source)<(ENC_OFF+ENC_LEN+3)) {
+    PRINTF(L_SYS_VERBOSE,"short ecm");
+    return false;
+    }
+  if(memcmp(tester,&source[3],sizeof(tester))) { // new SHL
+    mutex.Lock();
+    if(!hasPrvId) {
+      wait.TimedWait(mutex,500); // wait for provider ID
+      if(!hasPrvId) {
+        PRINTF(L_SYS_ECM,"no provider ID (logger enabled?)");
+        mutex.Unlock();
+        return false;
+        }
+      }
+    const struct ShlProv *prv=FindProv(prvId);
+    if(!prv) {
+      PRINTF(L_SYS_ECM,"provider %04x not supported",prvId);
+      mutex.Unlock();
+      return false;
+      }
+    mutex.Unlock();
+    if(!ph) {
+      PRINTF(L_SYS_ECM,"using card %d",CardNum());
+      ph=new cPidHopper(CardNum(),prv->filters);
+      if(!ph) {
+        PRINTF(L_SYS_ECM,"no pid hopper");
+        return false;
+        }
+#if 1
+      unsigned int l=min((int)(sizeof(prv->pids)/sizeof(unsigned short)),prv->filters);
+      for(unsigned int i=0; i<l; i++) {
+        if(prv->pids[i]<=0) break;
+        ph->AddPid(prv->pids[i]);
+        }
+#endif
+      }
+
+    int pid=ecm->ecm_pid;
+    int now=time_ms();
+    unsigned char buff[4096];
+    unsigned char emask=0, omask=0;
+    memcpy(buff+1,source,SCT_LEN(source)); buff[0]=0;
+    LBSTARTF(L_SYS_VERBOSE);
+    LBPUT("chain 0x%04x [%d] ",pid,SCT_LEN(source)+1);
+    while(1) {
+      unsigned char *ptr=buff;
+      if(ptr[0]!=0) { // decrypt whole payload
+        LBPUT("PES");
+        unsigned char test[8];
+        memcpy(test,ptr,sizeof(test)); Decode(test,sizeof(test));
+        if(test[0]!=source[0] && (ptr[0]==0x11 || ptr[0]==0x12)) {     // Section is available at section_pointer offset.
+          SetKey(&ptr[KEY_OFF+1]);             // Use the same key and pid offsets which you use
+          Decode(&ptr[PID_OFF+1],183-PID_OFF); // for normal SHL section data decoding.
+          ptr+=ptr[0]+1;
+          if(ptr[0]==0x91 && SCT_LEN(ptr)==11) ptr+=11;
+          LBPUT("s");
+          }
+        else if(test[0]!=source[0] && ptr[0]==0x0a) {
+          SetKey(&ptr[1]);
+          Decode(&ptr[9],183-9);
+          ptr+=ptr[0]+1;
+          LBPUT("a");
+          }
+        else {
+          Decode(ptr,184);
+          LBPUT("n");
+          }
+        LBPUT(" ");
+        }
+      else {          // decrypt data only
+        LBPUT("SCT ");
+        ptr++;
+        SetKey(&ptr[KEY_OFF]);
+        Decode(&ptr[PID_OFF],SCT_LEN(ptr)-PID_OFF);
+        }
+      if(ptr[0]!=source[0]) {
+        LBPUT("wrong section %02x != %02x",ptr[0],source[0]);
+        return false;
+        }
+      pid=(ptr[PID_OFF]<<8)+ptr[PID_OFF+1];
+      if(pid==0x1FFF) {     // finished
+        if(prv->hasXor) ProcessCw(ptr,prv->id,prv->xorTab);
+        else {
+          for(int i=0; i<8; i++) {
+            const int m=1<<(7-i);
+            if(!(emask & m)) cw[i  ]=ptr[CW_OFF+i];
+            if(!(omask & m)) cw[i+8]=ptr[CW_OFF+i+8];
+            }
+          }
+        LBPUT("done");
+        ph->CutOff(now);
+        return true;
+        }
+      else {                // next hop
+        if(prv->hasXor) ProcessCw(ptr,prv->id,prv->xorTab);
+        else {
+          emask=ptr[EMASK_OFF];
+          omask=ptr[OMASK_OFF];
+          memcpy(cw,&ptr[CW_OFF],16);
+          }
+        LBPUT("0x%04x ",pid);
+#if 1
+        if(prv->pidRange && (pid&0xFF00)!=prv->pidRange) {
+          LBPUT("wrong pid range");
+          return false;
+          }
+#endif
+        int n=ph->Read(pid,now,buff,sizeof(buff),1000);
+        if(n<CW_OFF+16+1) {
+          LBPUT("read failed");
+          return false;
+          }
+        LBPUT("[%d] ",n);
+        }
+      }
+    LBEND();
+    }
+  else { // old SHL
+    static const unsigned char signature[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+    source+=ENC_OFF+3; // here's the DES crypto content
+
+    cPlainKey *pk=0;
+    cKeySnoop ks(this,'Z',ecm->provId,0x00);
+    unsigned char key[8];
+    while((pk=keys.FindKey('Z',ecm->provId,0x00,sizeof(key),pk))) {
+      pk->Get(key);
+      SetKey(key);
+      Decode(source,ENC_LEN);
+      unsigned char sigbuf[8];
+      memcpy(&sigbuf[0],&source[0],2);
+      memcpy(&sigbuf[2],&source[18],6);
+      if(!memcmp(sigbuf,signature,sizeof(sigbuf))) {
+        memcpy(cw,&source[2],16);
+        ks.OK(pk);
+        return true;
+        }
+      }
+    }
+  return false;
+}
+
+void cSystemShl::ProcessCw(const unsigned char *data, int prvId, const unsigned char *xorTable)
+{
+  if(SCT_LEN(data)<150) {
+    PRINTF(L_SYS_VERBOSE,"data is too short");
+    return;
+    }
+  const unsigned int dataStart=data[33];
+  if((dataStart+35)>184) {
+    PRINTF(L_SYS_VERBOSE,"data broken (%d)",dataStart);
+    return;
+    }
+  const unsigned int xorVal=data[dataStart+35];
+  const unsigned int emask =data[dataStart+34];
+  const unsigned int omask =data[dataStart+33];
+  for(int i=0; i<8; i++) {
+    const unsigned int m=1<<i;
+    if(emask & m) cw[i  ]=data[CW_OFF+i  ]^xorVal^xorTable[i  ];
+    if(omask & m) cw[i+8]=data[CW_OFF+8+i]^xorVal^xorTable[i+8];
+    }
+}
+
+void cSystemShl::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+  int n=SCT_LEN(buffer)-3;
+  buffer+=3;
+  for(int i=0; i<n;) {
+    switch(buffer[i]) {
+      case 0x0a:
+        mutex.Lock();
+        prvId=WORD(buffer,i+2,0xFFFF);
+        if(!hasPrvId) {
+          const struct ShlProv *prv=FindProv(prvId);
+          PRINTF(L_SYS_EMM,"provider id %04x (%s)\n",prvId,prv ? prv->name:"unknown");
+          wait.Broadcast();
+          }
+        hasPrvId=true;
+        mutex.Unlock();
+        break;
+      case 0x08:
+      case 0x0b:
+      case 0x09:
+      case 0x06:
+      case 0x02:
+      case 0x30:
+      case 0x07:
+        break;
+      default:
+        HEXDUMP(L_SYS_EMM,&buffer[i+2],buffer[i+1],"unknown nano: %02x:",buffer[i]);
+        break;
+      }
+    i+=buffer[i+1]+2;
+    }
+}
+
+// -- cSystemLinkShl -----------------------------------------------------------
+
+class cSystemLinkShl : public cSystemLink {
+public:
+  cSystemLinkShl(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemShl; }
+  };
+
+static cSystemLinkShl staticInit;
+
+cSystemLinkShl::cSystemLinkShl(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkShl::CanHandle(unsigned short SysId)
+{
+  SysId&=0xFFF0;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/shl/shl.mk
new file mode 100644 (file)
index 0000000..b35e358
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# @SHL
+#
+TARGET = shl
+OBJS   = shl.o
+LIBS   = -lcrypto
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/log-viaccess.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/log-viaccess.h
new file mode 100644 (file)
index 0000000..e2f48db
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __LOG_VIACCESS_H
+#define __LOG_VIACCESS_H
+
+#include "log-sys.h"
+
+#define L_SYS        16
+#define L_SYS_TPS    LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_TPSAU  LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_TIME   LCLASS(L_SYS,L_SYS_LASTDEF<<3)
+#define L_SYS_ST20   LCLASS(L_SYS,L_SYS_LASTDEF<<4)
+#define L_SYS_DISASM LCLASS(L_SYS,L_SYS_LASTDEF<<5)
+#define L_SYS_ALL    LALL(L_SYS_DISASM)
+
+#endif
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/opentv.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/opentv.h
new file mode 100644 (file)
index 0000000..4d122dc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __OPENTV_H
+#define __OPENTV_H
+
+#define COMP_SECTION_HDR 0x434F4D50
+#define INFO_SECTION_HDR 0x494E464F
+#define CODE_SECTION_HDR 0x434F4445
+#define DATA_SECTION_HDR 0x44415441
+#define GDBO_SECTION_HDR 0x4744424F
+#define LAST_SECTION_HDR 0x4C415354
+#define SWAP_SECTION_HDR 0x53574150
+
+typedef struct comp_header {
+       unsigned int magic;
+       unsigned int csize, dsize, usize;
+       unsigned char end;
+} comp_header_t;
+
+typedef struct info_header {
+       unsigned int magic;
+       unsigned int bsssize;
+       unsigned int stacksize;
+} info_header_t;
+
+typedef struct code_header {
+       unsigned int magic;
+       unsigned int size, m_id;
+       unsigned short entry_point;
+       unsigned short end;
+       const unsigned char *code;
+} code_header_t;
+
+typedef struct data_header {
+       unsigned int magic;
+       unsigned int dlen;
+       const unsigned char *data;
+} data_header_t;
+
+#endif /* __OPENTV_H */
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.c
new file mode 100644 (file)
index 0000000..bbee87b
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vdr/tools.h>
+
+#include "log-viaccess.h"
+#include "st20.h"
+
+#include "helper.h"
+#include "misc.h"
+
+//#define SAVE_DEBUG // add some (slow) failsafe checks and stricter emulation
+
+#define INVALID_VALUE  0xCCCCCCCC
+#define ERRORVAL       0xDEADBEEF
+
+#define MININT         0x7FFFFFFF
+#define MOSTPOS                0x7FFFFFFF
+#define MOSTNEG                0x80000000
+
+#ifndef SAVE_DEBUG
+#define POP() stack[(sptr++)&STACKMASK]
+#else
+#define POP() ({ int __t=stack[sptr&STACKMASK]; stack[(sptr++)&STACKMASK]=INVALID_VALUE; __t; })
+#endif
+#define PUSH(v) do { int __v=(v); stack[(--sptr)&STACKMASK]=__v; } while(0)
+#define DROP(n) sptr+=n
+
+#define AAA stack[sptr&STACKMASK]
+#define BBB stack[(sptr+1)&STACKMASK]
+#define CCC stack[(sptr+2)&STACKMASK]
+
+#define GET_OP()        operand|=op1&0x0F
+#define CLEAR_OP()      operand=0
+#define JUMP(x)         Iptr+=(x)
+#define POP64()         ({ unsigned int __b=POP(); ((unsigned long long)POP()<<32)|__b; })
+#define PUSHPOP(op,val) do { int __a=val; AAA op##= (__a); } while(0)
+
+#ifndef SAVE_DEBUG
+#define RB(off) UINT8_LE(Addr(off))
+#define RW(off) UINT32_LE(Addr(off))
+#define WW(off,val) BYTE4_LE(Addr(off),val)
+#else
+#define RB(off) ReadByte(off)
+#define RW(off) ReadWord(off)
+#define WW(off,val) WriteWord(off,val)
+#endif
+
+#define LOG_OP(op) do { if(v) LogOp(op); } while(0)
+
+// -- cST20 --------------------------------------------------------------------
+
+cST20::cST20(void)
+{
+  flash=ram=0;
+  loglb=new cLineBuff(128);
+}
+
+cST20::~cST20()
+{
+  free(flash);
+  free(ram);
+  delete loglb;
+}
+
+void cST20::SetFlash(unsigned char *m, int len)
+{
+  free(flash);
+  flash=MALLOC(unsigned char,len);
+  if(flash && m) memcpy(flash,m,len);
+  else memset(flash,0,len);
+  flashSize=len;
+}
+
+void cST20::SetRam(unsigned char *m, int len)
+{
+  free(ram);
+  ram=MALLOC(unsigned char,len);
+  if(ram && m) memcpy(ram,m,len);
+  else memset(ram,0,len);
+  ramSize=len;
+}
+
+void cST20::Init(unsigned int IPtr, unsigned int WPtr)
+{
+  Wptr=WPtr; Iptr=IPtr;
+  memset(stack,INVALID_VALUE,sizeof(stack)); sptr=STACKMAX-3;
+  memset(iram,0,sizeof(iram));
+  verbose=LOG(L_SYS_DISASM);
+}
+
+void cST20::SetCallFrame(unsigned int raddr, int p1, int p2, int p3)
+{
+  Wptr-=16;
+  WriteWord(Wptr,raddr); // RET
+  WriteWord(Wptr+4,0);
+  WriteWord(Wptr+8,0);
+  WriteWord(Wptr+12,p1);
+  WriteWord(Wptr+16,p2);
+  WriteWord(Wptr+20,p3);
+  SetReg(AREG,raddr);    // RET
+}
+
+unsigned int cST20::GetReg(int reg) const
+{
+  switch(reg) {
+    case IPTR: return Iptr;
+    case WPTR: return Wptr;
+    case AREG: return AAA;
+    case BREG: return BBB;
+    case CREG: return CCC;
+    default:   PRINTF(L_SYS_ST20,"getreg: unknown reg"); return ERRORVAL;
+    }
+}
+
+void cST20::SetReg(int reg, unsigned int val)
+{
+  switch(reg) {
+    case IPTR: Iptr=val; return;
+    case WPTR: Wptr=val; return;
+    case AREG: AAA=val; return;
+    case BREG: BBB=val; return;
+    case CREG: CCC=val; return;
+    default:   PRINTF(L_SYS_ST20,"setreg: unknown reg"); return;
+    }
+}
+
+unsigned char *cST20::Addr(unsigned int off)
+{
+  if(off>=FLASHS && off<=FLASHE) {
+#ifndef SAVE_DEBUG
+    return &flash[off-FLASHS];
+#else
+    off-=FLASHS; if(off<flashSize && flash) return &flash[off]; break;
+#endif
+    }
+  else if(off>=RAMS && off<=RAME) {
+#ifndef SAVE_DEBUG
+    return &ram[off-RAMS];
+#else
+    off-=RAMS; if(off<ramSize && ram) return &ram[off]; break;
+#endif
+    }
+  else if(off>=IRAMS && off<=IRAME)
+    return &iram[off-IRAMS];
+#ifndef SAVE_DEBUG
+  invalid=ERRORVAL; return (unsigned char *)&invalid;
+#else
+  return 0;
+#endif
+}
+
+unsigned int cST20::ReadWord(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+  return UINT32_LE(Addr(off));
+#else
+  unsigned char *addr=Addr(off);
+  return addr ? UINT32_LE(addr) : ERRORVAL;
+#endif
+}
+
+unsigned short cST20::ReadShort(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+  return UINT16_LE(Addr(off));
+#else
+  unsigned char *addr=Addr(off);
+  return addr ? UINT16_LE(addr) : ERRORVAL>>16;
+#endif
+}
+
+unsigned char cST20::ReadByte(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+  return UINT8_LE(Addr(off));
+#else
+  unsigned char *addr=Addr(off);
+  return addr ? UINT8_LE(addr) : ERRORVAL>>24;
+#endif
+}
+
+void cST20::WriteWord(unsigned int off, unsigned int val)
+{
+#ifndef SAVE_DEBUG
+  BYTE4_LE(Addr(off),val);
+#else
+  unsigned char *addr=Addr(off);
+  if(addr) BYTE4_LE(addr,val);
+#endif
+}
+
+void cST20::WriteShort(unsigned int off, unsigned short val)
+{
+#ifndef SAVE_DEBUG
+  BYTE2_LE(Addr(off),val);
+#else
+  unsigned char *addr=Addr(off);
+  if(addr) BYTE2_LE(addr,val);
+#endif
+}
+
+void cST20::WriteByte(unsigned int off, unsigned char val)
+{
+#ifndef SAVE_DEBUG
+  BYTE1_LE(Addr(off),val);
+#else
+  unsigned char *addr=Addr(off);
+  if(addr) BYTE1_LE(addr,val);
+#endif
+}
+
+#define OP_COL 20
+
+void cST20::LogOpOper(int op, int oper)
+{
+  const static char *cmds[] = { "j","ldlp",0,"ldnl","ldc","ldnlp",0,"ldl","adc","call","cj","ajw","eqc","stl","stnl",0 };
+  const static char flags[] = {  3,  0,    0, 0,     1,    0,     0, 0,    1,    3,     3,   0,    1,    0,    0,    0 };
+  if(oper==0) loglb->Printf("%08X %02X",Iptr-1,op);
+  else        loglb->Printf("%02X",op);
+  oper|=op&0xF; op>>=4;
+  if(!cmds[op]) return;
+  int n=loglb->Length();
+  if(flags[op]&2) oper+=Iptr;
+  if(flags[op]&1) loglb->Printf("%*s%-5s $%-8X ",max(OP_COL-n,1)," ",cmds[op],oper);
+  else            loglb->Printf("%*s%-5s  %-8d ",max(OP_COL-n,1)," ",cmds[op],oper);
+}
+
+void cST20::LogOp(const char *op)
+{
+  int n=loglb->Length();
+  loglb->Printf("%*s%-15s ",max(OP_COL-n,1)," ",op);
+}
+
+int cST20::Decode(int count)
+{
+  int operand;
+  bool v=verbose;
+  CLEAR_OP();
+  while(Iptr!=0) {
+    int a, op1=RB(Iptr++);
+    if(v) LogOpOper(op1,operand);
+    GET_OP();
+    switch(op1>>4) {
+      case 0x0: // j / jump
+#ifdef SAVE_DEBUG
+        POP(); POP(); POP();
+#endif
+        JUMP(operand);
+        CLEAR_OP();
+        break;
+      case 0x1: // ldlp
+        PUSH(Wptr+(operand*4));
+        CLEAR_OP();
+        break;
+      case 0x2: // positive prefix
+        operand<<=4;
+        break;
+      case 0x3: // ldnl
+        AAA=RW(AAA+(operand*4));
+        CLEAR_OP();
+        break;
+      case 0x4: // ldc
+        PUSH(operand);
+        CLEAR_OP();
+        break;
+      case 0x5: // ldnlp
+        PUSHPOP(+,operand*4);
+        CLEAR_OP();
+        break;
+      case 0x6: // negative prefix
+        operand=(~operand)<<4;
+        break;
+      case 0x7: // ldl
+        PUSH(RW(Wptr+(operand*4)));
+        CLEAR_OP();
+        break;
+      case 0x8: // adc
+        PUSHPOP(+,operand);
+        CLEAR_OP();
+        break;
+      case 0x9: // call
+        Wptr-=16;
+        WW(Wptr,Iptr); WW(Wptr+4,POP()); WW(Wptr+8,POP()); WW(Wptr+12,POP());
+        PUSH(Iptr);
+        JUMP(operand);
+        CLEAR_OP();
+        break;
+      case 0xA: // cj / conditional jump
+        if(AAA) DROP(1); else JUMP(operand);
+        CLEAR_OP();
+        break;
+      case 0xB: // ajw / adjust workspace
+        Wptr+=operand*4;
+        CLEAR_OP();
+        break;
+      case 0xC: // eqc / equals constant
+        AAA=(operand==AAA ? 1 : 0);
+        CLEAR_OP();
+        break;
+      case 0xD: // stl
+        WW(Wptr+(operand*4),POP());
+        CLEAR_OP();
+        break;
+      case 0xE: // stnl
+        a=POP(); WW(a+(operand*4),POP());
+        CLEAR_OP();
+        break;
+      case 0xF: // opr (secondary ins)
+        switch(operand) {
+         case  0x00: LOG_OP("rev");   a=AAA; AAA=BBB; BBB=a; break;
+         case  0x01: LOG_OP("lb");    AAA=RB(AAA); break;
+         case  0x02: LOG_OP("bsub");  PUSHPOP(+,POP()); break;
+         case  0x04: LOG_OP("diff");  PUSHPOP(-,POP()); break;
+         case  0x05: LOG_OP("add");   PUSHPOP(+,POP()); break;
+         case  0x06: LOG_OP("gcall"); a=AAA; AAA=Iptr; Iptr=a; break;
+         case  0x08: LOG_OP("prod");  PUSHPOP(*,POP()); break;
+         case  0x09: LOG_OP("gt");    a=POP(); AAA=(AAA>a); break;
+         case  0x0A: LOG_OP("wsub");  a=POP(); AAA=a+(AAA*4); break;
+         case  0x0C: LOG_OP("sub");   PUSHPOP(-,POP()); break;
+
+          case  0x1B: LOG_OP("ldpi");  PUSHPOP(+,Iptr); break;
+          case  0x20: LOG_OP("ret");   Iptr=RW(Wptr); Wptr=Wptr+16; break;
+          case  0x2C: LOG_OP("div");   PUSHPOP(/,POP()); break;
+          case  0x32: LOG_OP("not");   AAA=~AAA; break;
+          case  0x33: LOG_OP("xor");   PUSHPOP(^,POP()); break;
+          case  0x34: LOG_OP("bcnt");  PUSHPOP(*,4); break;
+          case  0x3B: LOG_OP("sb");    a=POP(); WriteByte(a,POP()); break;
+          case  0x3F: LOG_OP("wcnt");  a=POP(); PUSH(a&3); PUSH((unsigned int)a>>2); break;
+          case  0x40: LOG_OP("shr");   a=POP(); AAA=(unsigned int)AAA>>a; break;
+          case  0x41: LOG_OP("shl");   a=POP(); AAA=(unsigned int)AAA<<a; break;
+          case  0x42: LOG_OP("mint");  PUSH(MOSTNEG); break;
+          case  0x46: LOG_OP("and");   PUSHPOP(&,POP()); break;
+          case  0x4A: LOG_OP("move");  { a=POP(); int b=POP(); int c=POP(); while(a--) WriteByte(b++,ReadByte(c++)); } break;
+          case  0x4B: LOG_OP("or");    PUSHPOP(|,POP()); break;
+          case  0x53: LOG_OP("mul");   PUSHPOP(*,POP()); break;
+          case  0x5A: LOG_OP("dup");   PUSH(AAA); break;
+          case  0x5F: LOG_OP("gtu");   a=POP(); AAA=((unsigned int)AAA>(unsigned int)a); break;
+          case  0x1D: LOG_OP("xdble"); CCC=BBB; BBB=(AAA>=0 ? 0:-1); break;
+          case  0x1A: LOG_OP("ldiv");  { a=POP(); unsigned long long ll=POP64(); PUSH(ll%(unsigned int)a); PUSH(ll/(unsigned int)a); } break;
+          case  0x1F: LOG_OP("rem");   PUSHPOP(%,POP()); break;
+          case  0x35: LOG_OP("lshr");  { a=POP(); unsigned long long ll=POP64()>>a; PUSH((ll>>32)&0xFFFFFFFF); PUSH(ll&0xFFFFFFFF); } break;
+
+          case  0xCA: LOG_OP("ls");    AAA=ReadShort(AAA); break;
+          case  0xCD: LOG_OP("gintdis"); break;
+          case  0xCE: LOG_OP("gintenb"); break;
+
+          case -0x40: LOG_OP("nop");   break;
+
+         default: 
+            if(verbose) PUTLB(L_SYS_DISASM,loglb);
+            PRINTF(L_SYS_ST20,"unknown opcode %X",operand);
+            return ERR_ILL_OP;
+         }
+        CLEAR_OP();
+        break;
+      }
+
+    if(v && operand==0) {
+      loglb->Printf("%08X %08X %08X W:%08X",AAA,BBB,CCC,Wptr);
+      PUTLB(L_SYS_DISASM,loglb);
+      }
+    if(--count<=0 && operand==0) {
+      PRINTF(L_SYS_ST20,"instruction counter exceeded");
+      return ERR_CNT;
+      }
+    }
+  return 0;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/st20.h
new file mode 100644 (file)
index 0000000..ae70ab5
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __ST20_H
+#define __ST20_H
+
+class cLineBuff;
+
+// ----------------------------------------------------------------
+
+#define IPTR 0
+#define WPTR 1
+#define AREG 2
+#define BREG 3
+#define CREG 4
+
+#define FLASHS 0x7FE00000
+#define FLASHE 0x7FFFFFFF
+#define RAMS   0x40000000
+#define RAME   0x401FFFFF
+#define IRAMS  0x80000000
+#define IRAME  0x800017FF
+
+#define ERR_ILL_OP -1
+#define ERR_CNT    -2
+
+// ----------------------------------------------------------------
+
+#define STACKMAX  16
+#define STACKMASK (STACKMAX-1)
+
+class cST20 {
+private:
+  unsigned int Iptr, Wptr;
+  unsigned char *flash, *ram;
+  unsigned int flashSize, ramSize;
+  int sptr, stack[STACKMAX];
+  unsigned char iram[0x1800];
+  int invalid;
+  //
+  bool verbose;
+  cLineBuff *loglb;
+  //
+  unsigned char *Addr(unsigned int off);
+  void LogOp(const char *op);
+  void LogOpOper(int op, int oper);
+public:
+  cST20(void);
+  ~cST20();
+  void Init(unsigned int IPtr, unsigned int WPtr);
+  void SetCallFrame(unsigned int raddr, int p1, int p2, int p3);
+  void SetFlash(unsigned char *m, int len);
+  void SetRam(unsigned char *m, int len);
+  int Decode(int count);
+  //
+  unsigned int GetReg(int reg) const;
+  void SetReg(int reg, unsigned int val);
+  unsigned int ReadWord(unsigned int off);
+  unsigned short ReadShort(unsigned int off);
+  unsigned char ReadByte(unsigned int off);
+  void WriteWord(unsigned int off, unsigned int val);
+  void WriteShort(unsigned int off, unsigned short val);
+  void WriteByte(unsigned int off, unsigned char val);
+  };
+
+#endif // __ST20_H
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.c
new file mode 100644 (file)
index 0000000..084dc2f
--- /dev/null
@@ -0,0 +1,1248 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libsi/section.h>
+
+#include "viaccess.h"
+#include "log-viaccess.h"
+#include "tps.h"
+#include "opentv.h"
+#include "st20.h"
+
+#include "sc.h"
+#include "scsetup.h"
+#include "system-common.h"
+#include "filter.h"
+#include "misc.h"
+#include "helper.h"
+
+#define CHECK_TIME    5*60*1000
+#define LOADBIN_TIME 60*60*1000
+#define TPSAU_TIME   30*60*1000
+
+//#define TRUST_BOXTIME // should we trust the local time on this machine?
+//#define DUMP_TPSAU "/var/tmp"
+
+#ifdef DUMP_TPSAU
+#include <unistd.h>
+#endif
+
+// -- cRC6 ---------------------------------------------------------------------
+
+/*
+ * This code implements the RC6-32/20 block cipher.
+ *
+ * The algorithm is due to Ron Rivest and RSA Labs. This code is based on code
+ * which was written by Martin Hinner <mhi@penguin.cz> in 1999, no copyright is
+ * claimed.
+ */
+
+#define RC6_WORDSIZE   32
+#define RC6_P32                0xB7E15163L
+#define RC6_Q32                0x9E3779B9L
+
+unsigned int cRC6::rol(unsigned int v, unsigned int cnt)
+{
+  cnt&=(RC6_WORDSIZE-1);
+  return (v<<cnt) | (v>>(RC6_WORDSIZE-cnt));
+}
+
+unsigned int cRC6::ror(unsigned int v, unsigned int cnt)
+{
+  cnt&=(RC6_WORDSIZE-1);
+  return (v>>cnt) | (v<<(RC6_WORDSIZE-cnt));
+}
+
+void cRC6::SetKey(const unsigned char *Key, int len)
+{
+  key[0]=RC6_P32;
+  for(int v=1; v<RC6_MAX; v++) key[v]=key[v-1]+RC6_Q32;
+  len/=4;
+  unsigned int a=0, b=0, *l=AUTOARRAY(unsigned int,len);
+  memcpy(l,Key,len*4);
+  for(int i=0,j=0,v=3*(len>RC6_MAX ? len : RC6_MAX) ; v>0; v--) {
+    a=key[i]=rol(key[i]+a+b,3);
+    b=  l[j]=rol(  l[j]+a+b,a+b);
+    i++; i%=RC6_MAX;
+    j++; j%=len;
+    }
+}
+
+void cRC6::Decrypt(unsigned char *data)
+{
+  unsigned int *l=(unsigned int *)data;
+  unsigned int a, b, c, d;
+  a=l[0]-key[RC6_MAX-2];
+  b=l[1];
+  c=l[2]-key[RC6_MAX-1];
+  d=l[3];
+  for(int i=RC6_ROUNDS; i>0; i--) {
+    unsigned int t=a;
+    unsigned int u=b;
+    a=d; d=c; b=t; c=u;
+    u=rol((d*(2*d+1)),5);
+    t=rol((b*(2*b+1)),5);
+    c=ror(c-key[2*i+1],t)^u;
+    a=ror(a-key[2*i  ],u)^t;
+    }
+  l[0]=a;
+  l[1]=b-key[0];
+  l[2]=c;
+  l[3]=d-key[1];
+}
+
+// -- cTransponderTime ---------------------------------------------------------
+
+class cTransponderTime : public cSimpleItem {
+private:
+  time_t sattime;
+  cTimeMs reftime;
+  cMutex mutex;
+  cCondVar wait;
+  int source, transponder;
+  bool hasHandler;
+public:
+  cTransponderTime(int Source, int Transponder);
+  bool Is(int Source, int Transponder) const;
+  void Set(time_t st);
+  time_t Now(void);
+  void SetHasHandler(bool on) { hasHandler=on; }
+  bool HasHandler(void) const { return hasHandler; }
+  };
+
+cTransponderTime::cTransponderTime(int Source, int Transponder)
+{
+  source=Source; transponder=Transponder;
+#ifdef TRUST_BOXTIME
+  sattime=time(0);
+#else
+  sattime=-1;
+#endif
+  hasHandler=false;
+}
+
+bool cTransponderTime::Is(int Source, int Transponder) const
+{
+  return source==Source && transponder==Transponder;
+}
+
+void cTransponderTime::Set(time_t st)
+{
+  cMutexLock lock(&mutex);
+  if(sattime<0 || reftime.Elapsed()>5*1000) {
+    reftime.Set(-100);
+    sattime=st;
+    wait.Broadcast();
+
+    char str[32];
+    struct tm utm;
+    st=Now();
+    time_t now=time(0);
+    gmtime_r(&st,&utm); asctime_r(&utm,str); stripspace(str);
+    PRINTF(L_SYS_TIME,"%x:%x: %s (delta %ld)",source,transponder,str,st-now);
+    }
+}
+
+time_t cTransponderTime::Now(void)
+{
+  cMutexLock lock(&mutex);
+  if(sattime<0) {
+    if(!wait.TimedWait(mutex,3*1000)) {
+      PRINTF(L_SYS_TIME,"%x:%x: unsuccessfull wait",source,transponder);
+      return -1;
+      }
+    }
+  return sattime+(reftime.Elapsed()/1000);
+}
+
+// -- cSatTimeHook -------------------------------------------------------------
+
+#ifndef TESTER
+
+class cSatTimeHook : public cLogHook {
+private:
+  cTransponderTime *ttime;
+public:
+  cSatTimeHook(cTransponderTime *Ttime);
+  ~cSatTimeHook();
+  virtual void Process(int pid, unsigned char *data);
+  };
+
+cSatTimeHook::cSatTimeHook(cTransponderTime *Ttime)
+:cLogHook(HOOK_SATTIME,"sattime")
+{
+  ttime=Ttime;
+  pids.AddPid(0x14,0x71,0xff,0x03);
+  ttime->SetHasHandler(true);
+}
+
+cSatTimeHook::~cSatTimeHook()
+{
+  ttime->SetHasHandler(false);
+}
+
+void cSatTimeHook::Process(int pid, unsigned char *data)
+{
+  if(data && ttime) {
+    if(data[0]==0x70) { // TDT
+      SI::TDT tdt(data,false);
+      tdt.CheckParse();
+      ttime->Set(tdt.getTime());
+      }
+    else if(data[0]==0x73) { // TOT
+      SI::TOT tot(data,false);
+      if(!tot.CheckCRCAndParse()) return;
+      ttime->Set(tot.getTime());
+      }
+    }
+}
+
+#endif //TESTER
+
+// -- cSatTime -----------------------------------------------------------------
+
+class cSatTime {
+private:
+  static cMutex mutex;
+  static cSimpleList<cTransponderTime> list;
+  //
+  int cardNum;
+  cTransponderTime *ttime;
+  //
+  void CheckHandler(void);
+public:
+  cSatTime(int CardNum, int Source, int Transponder);
+  time_t Now(void);
+  };
+
+cMutex cSatTime::mutex;
+cSimpleList<cTransponderTime> cSatTime::list;
+
+cSatTime::cSatTime(int CardNum, int Source, int Transponder)
+{
+  cardNum=CardNum;
+  cMutexLock lock(&mutex);
+  for(ttime=list.First(); ttime; ttime=list.Next(ttime))
+    if(ttime->Is(Source,Transponder)) break;
+  if(!ttime) {
+    ttime=new cTransponderTime(Source,Transponder);
+    if(ttime) list.Add(ttime);
+    PRINTF(L_SYS_TIME,"%x:%x: created new transpondertime",Source,Transponder);
+    }
+  else PRINTF(L_SYS_TIME,"%x:%x: using existing transpondertime",Source,Transponder);
+  CheckHandler();
+}
+
+time_t cSatTime::Now(void)
+{
+  CheckHandler();
+  return ttime ? ttime->Now() : -1;
+}
+
+void cSatTime::CheckHandler(void)
+{
+  if(ttime) {
+    if(!cSoftCAM::TriggerHook(cardNum,HOOK_SATTIME) && !ttime->HasHandler()) {
+      cSatTimeHook *hook=new cSatTimeHook(ttime);
+      cSoftCAM::AddHook(cardNum,hook);
+      PRINTF(L_SYS_TIME,"added hook");
+      }
+    }
+}
+
+// -- cOpenTVModule ------------------------------------------------------------
+
+class cOpenTVModule {
+private:
+  int id, modlen;
+  unsigned char *mem;
+  int received;
+  info_header_t info;
+  code_header_t code;
+  data_header_t data;
+  //
+  bool Decompress(void);
+  bool DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize);
+  bool ParseSections(void);
+  void Dump(void);
+public:
+  cOpenTVModule(const unsigned char *data);
+  cOpenTVModule(int Id, const unsigned char *data, int len);
+  ~cOpenTVModule();
+  int AddPart(const unsigned char *data, int len);
+  const info_header_t *InfoHdr(void) const { return &info; }
+  const code_header_t *CodeHdr(void) const { return &code; }
+  const data_header_t *DataHdr(void) const { return &data; }
+  //
+  inline static int Id(const unsigned char *data) { return UINT16_BE(&data[3]); }
+  inline static int Offset(const unsigned char *data) { return UINT32_BE(&data[16]); }
+  inline static int Length(const unsigned char *data) { return UINT32_BE(&data[20]); }
+  };
+
+cOpenTVModule::cOpenTVModule(const unsigned char *data)
+{
+  id=Id(data); modlen=Length(data);
+  received=0;
+  mem=MALLOC(unsigned char,modlen);
+}
+
+cOpenTVModule::cOpenTVModule(int Id, const unsigned char *data, int len)
+{
+  id=Id; modlen=received=len;
+  mem=MALLOC(unsigned char,modlen);
+  memcpy(mem,data,len);
+  ParseSections();
+}
+
+cOpenTVModule::~cOpenTVModule()
+{
+  free(mem);
+}
+
+int cOpenTVModule::AddPart(const unsigned char *data, int slen)
+{
+  if(Id(data)==id) {
+    int off=Offset(data);
+    int mlen=Length(data);
+    int rec=slen-28;
+    if(mlen!=modlen || (off+rec)>mlen || !mem) {
+      PRINTF(L_SYS_TPSAU,"length mismatch while adding to OpenTV module");
+      return -1;
+      }
+    memcpy(&mem[off],data+24,rec);
+    received+=rec;
+    if(received==mlen) {
+      if(!Decompress()) {
+        PRINTF(L_SYS_TPSAU,"failed to decompress OpenTV module");
+        return -1;
+        }
+      if(!ParseSections()) {
+        PRINTF(L_SYS_TPSAU,"DATA & CODE section not located in OpenTV module");
+        return -1;
+        }
+      Dump();
+      return 1;
+      }
+    }
+  return 0;
+}
+
+void cOpenTVModule::Dump(void)
+{
+#ifdef DUMP_TPSAU
+#warning Dumping TPS AU data
+  {
+  char fname[32];
+  snprintf(fname,sizeof(fname),"%s/decomp.bin.%d.%x",DUMP_TPSAU,getpid(),(int)time(0));
+  int fd=open(fname,O_CREAT|O_TRUNC|O_WRONLY,DEFFILEMODE);
+  if(fd>=0) {
+    write(fd,mem,modlen);
+    close(fd);
+    PRINTF(L_SYS_TPSAU,"dumped to file '%s'",fname);
+    }
+  }
+#endif
+}
+
+bool cOpenTVModule::ParseSections(void)
+{
+  int sections=0;
+  for(int idx=0; idx<modlen;) {
+    unsigned int hdr=UINT32_BE(mem+idx);
+    unsigned int s_len=UINT32_BE(mem+idx+4);
+    switch(hdr) {
+      case INFO_SECTION_HDR:
+       info.magic=hdr;
+       info.bsssize=UINT32_BE(mem+idx+8);
+       info.stacksize=UINT32_BE(mem+idx+12);
+        sections|=1;
+        break;
+      case CODE_SECTION_HDR:
+       code.magic=hdr;
+       code.size=s_len-8;
+       code.m_id=UINT32_BE(mem+idx+8);
+       code.entry_point=UINT16_BE(mem+idx+12);
+       code.end=UINT16_BE(mem+idx+14);
+       code.code=mem+idx+8;
+        sections|=2;
+        break;
+      case DATA_SECTION_HDR:
+       data.magic=hdr;
+       data.dlen=s_len-8;
+       data.data=mem+idx+8;
+        sections|=4;
+        break;
+      case SWAP_SECTION_HDR:
+      case GDBO_SECTION_HDR:
+        break;
+      case LAST_SECTION_HDR:
+      case 0:
+        idx=modlen;
+        break;
+      case COMP_SECTION_HDR:
+        PRINTF(L_GEN_DEBUG,"OpenTV module still compressed in ParseSections()");
+        return 0;
+      default:
+        PRINTF(L_SYS_TPSAU,"unknown section hdr %08x (%c%c%c%c) in OpenTV module",hdr,(hdr>>24)&0xFF,(hdr>>16)&0xFF,(hdr>>8)&0xFF,hdr&0xFF);
+        break;
+      }
+    idx+=s_len;
+    }
+  return sections>=6;
+}
+
+bool cOpenTVModule::Decompress(void)
+{
+  comp_header_t comp;
+  comp.magic=UINT32_BE(mem);
+  comp.csize=UINT32_BE(mem+4);
+  comp.dsize=UINT32_BE(mem+8);
+  comp.usize=UINT32_BE(mem+12);
+  comp.end  =UINT8_BE(mem+16);
+  if((COMP_SECTION_HDR!=comp.magic) || (comp.dsize<=comp.csize) || 
+     (comp.usize>comp.dsize) || (comp.end>=1) || (comp.csize<=17))
+    return true;
+  unsigned char *decomp=MALLOC(unsigned char,comp.dsize);
+  if(!decomp || !DoDecompress(decomp,mem+17,comp.dsize,comp.csize-17,comp.usize))
+    return false;
+  free(mem);
+  mem=decomp; modlen=comp.dsize;
+  return true;
+}
+
+#define BYTE() ((odd)?((in_ptr[0]&0x0F)|(in_ptr[1]&0xF0)):in_ptr[0])
+
+#define NIBBLE(__v) \
+  do { \
+    odd^=1; \
+    if(odd) __v=in_ptr[0]&0xF0; \
+    else {  __v=(in_ptr[0]&0xF)<<4; in_ptr++; } \
+    } while(0)
+
+bool cOpenTVModule::DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize)
+{
+  if(usize==0) return false;
+  const unsigned char *data_start=in_ptr;
+  unsigned char *out_start=out_ptr;
+  unsigned char *out_end=out_ptr+usize;
+  int odd=0;
+  while(1) {
+    unsigned char mask=BYTE(); in_ptr++;
+    for(int cnt=8; cnt>0; mask<<=1,cnt--) {
+      if(mask&0x80) {
+        out_ptr[0]=BYTE(); in_ptr++;
+        out_ptr++;
+        }
+      else {
+        int off=0, len=0;
+        unsigned char cmd=BYTE(); in_ptr++;
+        switch(cmd>>4) {
+          case 0x0:
+          case 0x1:
+          case 0x2:
+          case 0x3:
+          case 0x4:
+          case 0x5:
+          case 0x6:
+            off=((cmd&0xF)<<8)+BYTE(); in_ptr++;
+            len=((cmd>>4)&0x7)+3;
+            break;
+          case 0x7:
+            {
+            unsigned char high=BYTE(); in_ptr++;
+            off=((high&0x7F)<<8)+BYTE(); in_ptr++;
+            if((cmd==0x7F) && (high&0x80)) {
+              len=BYTE(); in_ptr++;
+              if(len==0xFF) {
+                len=BYTE(); in_ptr++;
+                len=((len<<8)+BYTE()+0x121)&0xFFFF; in_ptr++;
+                }
+              else len+=0x22;
+              }
+            else {
+              len=((cmd&0x0F)<<1)+3; if(high&0x80) len++;
+              }
+            break;
+            }
+          case 0x8:
+          case 0x9:
+          case 0xA:
+          case 0xB:
+            if(cmd&0x20) NIBBLE(off); else off=0;
+            off=(off<<1)|(cmd&0x1F); len=2;
+            break;
+          case 0xC:
+            off=cmd&0x0F; len=3;
+            break;
+          case 0xD:
+          case 0xE:
+          case 0xF:
+            NIBBLE(off);
+            off|=cmd&0x0F; len=((cmd>>4)&0x3)+2;
+            break;
+          }
+        const unsigned char *from=out_ptr-(off+1);
+        if(from<out_start || from>=out_ptr || len>(out_end-out_ptr)) {
+          PRINTF(L_SYS_TPSAU,"length mismatch in OpenTV decompress");
+          return false;
+          }
+        while(--len>=0) *out_ptr++=*from++;
+        }
+      if(out_end<=out_ptr) {
+        if(out_end!=out_ptr) {
+          PRINTF(L_SYS_TPSAU,"pointer mismatch in OpenTV decompress");
+          return false;
+          }
+        int len=out_start+dsize-out_ptr;
+        if(len>0) memmove(out_ptr,data_start+(data_size-(dsize-usize)),len);
+        return true;
+        }
+      }
+    }
+}
+
+#undef BYTE
+#undef NIBBLE
+
+// -- cTpsKey ------------------------------------------------------------------
+
+cTpsKey::cTpsKey(void)
+{
+  timestamp=0;
+}
+
+void cTpsKey::Set(const cTpsKey *k)
+{
+  timestamp=k->timestamp;
+  opmode=k->opmode;
+  memcpy(step,k->step,sizeof(step));
+}
+
+void cTpsKey::Set(const unsigned char *mem)
+{
+  timestamp=*((unsigned int *)mem);
+  opmode=mem[52+3];
+  for(int i=0; i<3; i++) {
+    memcpy(step[i].key,&mem[4+i*16],16);
+    step[i].mode=mem[52+i];
+    }
+}
+
+void cTpsKey::Put(unsigned char *mem) const
+{
+  *((unsigned int *)mem)=timestamp;
+  mem[52+3]=opmode;
+  for(int i=0; i<3; i++) {
+    memcpy(&mem[4+i*16],step[i].key,16);
+    mem[52+i]=step[i].mode;
+    }
+}
+
+cString cTpsKey::ToString(bool hide)
+{
+  unsigned char tmp[60];
+  Put(&tmp[4]);
+  *((unsigned int *)tmp)=crc32_le(0,&tmp[4],sizeof(tmp)-4);
+  char str[420];
+  HexStr(str,tmp,sizeof(tmp));
+  return str;
+}
+
+// -- cTpsAuHook ---------------------------------------------------------------
+
+#ifndef TESTER
+
+#define BUFF_SIZE 20000
+
+class cTpsAuHook : public cLogHook {
+private:
+  cOpenTVModule *mod;
+  int pmtpid, aupid;
+public:
+  cTpsAuHook(void);
+  ~cTpsAuHook();
+  virtual void Process(int pid, unsigned char *data);
+  };
+
+#endif //TESTER
+
+// -- cTpsKeys -----------------------------------------------------------------
+
+cTpsKeys tpskeys;
+
+cTpsKeys::cTpsKeys(void)
+:cStructListPlain<cTpsKey>("TPS keys","tps.cache",SL_READWRITE|SL_MISSINGOK|SL_WATCH|SL_NOPURGE)
+,lastLoad(-LOADBIN_TIME)
+,lastAu(-TPSAU_TIME)
+{
+  first=last=0; algomem=0; loadlist=0;
+}
+
+cTpsKeys::~cTpsKeys()
+{
+  free(algomem);
+  delete loadlist;
+}
+
+const cTpsKey *cTpsKeys::GetKey(time_t t)
+{
+  ListLock(false);
+  cTpsKey *k;
+  for(k=First(); k; k=Next(k)) if(t<k->Timestamp()) break;
+  ListUnlock();
+  return k;
+}
+
+const cTpsKey *cTpsKeys::GetV2Key(int id)
+{
+  unsigned char tmp[56];
+  memset(tmp,0,sizeof(tmp));
+  cPlainKey *pk=keys.FindKey('V',id,MBC3('T','P','S'),16,0);
+  if(pk) {
+    pk->Get(&tmp[4+2*16]);
+    tmp[52+2]=1;
+    tmp[52+3]=0x1C;
+    cTpsKey *tk=new cTpsKey;
+    if(tk) {
+      tk->Set(tmp);
+      return tk;
+      }
+    }
+  else PRINTF(L_SYS_KEY,"missing %.4x TPS key\n",id);
+  return 0;
+}
+
+void cTpsKeys::Check(time_t now, int cardnum)
+{
+  checkMutex.Lock();
+  if(first==0 && last==0 && Count()>0)
+    GetFirstLast();
+  if(now>0 && lastCheck.Elapsed()>CHECK_TIME) {
+    Purge(now);
+    lastCheck.Set();
+    }
+  bool nokey=now+2*3600>last;
+/*
+  if(lastLoad.Elapsed()>(nokey ? LOADBIN_TIME/60 : LOADBIN_TIME)) {
+    PRINTF(L_SYS_TPSAU,"loading "TPSBIN" triggered");
+    LoadBin();
+    if(now>0) Purge(now);
+    lastLoad.Set();
+    }
+*/
+  if(lastAu.Elapsed()>(nokey ? TPSAU_TIME/60 : TPSAU_TIME)) {
+    if(ScSetup.AutoUpdate>0) {
+      PRINTF(L_SYS_TPSAU,"TPS AU triggered");
+      if(!cSoftCAM::TriggerHook(cardnum,HOOK_TPSAU)) {
+        cTpsAuHook *hook=new cTpsAuHook;
+        cSoftCAM::AddHook(cardnum,hook);
+        PRINTF(L_SYS_TPSAU,"TPS AU hook added");
+        }
+      }
+    lastAu.Set();
+    }
+  checkMutex.Unlock();
+}
+
+void cTpsKeys::Purge(time_t now)
+{
+  PRINTF(L_SYS_TPSAU,"purging TPS keylist");
+  bool del=false;
+  ListLock(true);
+  for(cTpsKey *k=First(); k;) {
+    cTpsKey *n=Next(k);
+    if(k->Timestamp()<now-3600) { Del(k); del=true; }
+    k=n;
+    }
+  ListUnlock();
+  if(del) {
+    GetFirstLast();
+    Modified();
+    }
+}
+
+void cTpsKeys::Join(cSimpleList<cTpsKey> *nlist)
+{
+  ListLock(true);
+  cTpsKey *k;
+  while((k=nlist->First())) {
+    nlist->Del(k,false);
+    cTpsKey *p=First();
+    do {
+      if(!p) {
+        Add(k);
+        Modified();
+        break;
+        }
+      cTpsKey *n=Next(p);
+      if(k->Timestamp()==p->Timestamp()) {
+        p->Set(k);
+        Modified();
+        delete k;
+        break;
+        }
+      if(k->Timestamp()>p->Timestamp() && (!n || k->Timestamp()<n->Timestamp())) {
+        Add(k,p);
+        Modified();
+        break;
+        }
+      p=n;
+      } while(p);
+    }
+  ListUnlock();
+  delete nlist;
+  GetFirstLast();
+}
+
+cString cTpsKeys::Time(time_t t)
+{
+  char str[32];
+  struct tm tm_r;
+  strftime(str,sizeof(str),"%b %e %T",localtime_r(&t,&tm_r));
+  return str;
+}
+
+void cTpsKeys::GetFirstLast(void)
+{
+  if(Count()>0) {
+    ListLock(false);
+    cTpsKey *k=First();
+    first=last=k->Timestamp();
+    for(; k; k=Next(k)) {
+      if(k->Timestamp()<last)
+        PRINTF(L_SYS_TPSAU,"TPS keys not in accending order!");
+      last=k->Timestamp();
+      }
+    PRINTF(L_SYS_TPS,"%d TPS keys available (from %s to %s)",Count(),*Time(first),*Time(last));
+    ListUnlock();
+    }
+  else {
+    last=first=0;
+    PRINTF(L_SYS_TPS,"no TPS keys available");
+    }
+}
+
+bool cTpsKeys::ProcessAu(const cOpenTVModule *mod)
+{
+  PRINTF(L_SYS_TPSAU,"processing TPS AU data");
+
+  const code_header_t *codehdr=mod->CodeHdr();
+  const data_header_t *datahdr=mod->DataHdr();
+  const unsigned char *c=codehdr->code;
+  const unsigned char *d=datahdr->data;
+  unsigned int kd=0, cb1=0, cb2=0, cb3=0;
+  for(unsigned int i=0; i<codehdr->size; i++) {
+    if(c[i]==0x81) { // PushEA DS:$xxxx
+      unsigned int addr=(c[i+1]<<8)|c[i+2];
+      if(addr<(datahdr->dlen-3)) {
+        if(d[addr]==0x79 && d[addr+1]==0x00 && d[addr+2]==0x79 && d[addr+3]==0x00)
+          kd=addr;
+//XXX this needs proper fix sometime...
+       else if(d[addr+1]==0x00 && d[addr+3]==0x00 && (d[addr+4]==3||d[addr+4]==2))
+          kd=addr;
+//XXX
+        else if(d[addr]==0x73 && d[addr+1]==0x25 && d[addr+2]==0xFA)
+          cb1=addr;
+        else if(d[addr]==0x64 && (d[addr+1]&0xB0)==0xB0 && d[addr+2]==0x24)
+          cb2=addr;
+        else if((d[addr]&0x60)==0x60 && (d[addr+1]&0xB0)==0xB0 && (d[addr+2]&0x20)==0x20)
+          cb3=addr;
+/*
+        else if(d[addr]==0x73 && d[addr+1]==0x25) {
+          static const unsigned char scan1[] = { 0x28, 0x20, 0x20, 0xC0 };
+          for(int j=2; j < 0xC; j++)
+            if(!memcmp(&d[addr+j],scan1,sizeof(scan1))) { cb1=addr; break; }
+          }
+        else if(cb1 && !cb2) cb2=addr;
+        else if(cb1 && cb2 && !cb3) cb3=addr;
+*/
+        }
+      }
+    }
+  if(!kd || !cb1 || !cb2 || !cb3) {
+    PRINTF(L_SYS_TPSAU,"couldn't locate all pointers in data section (%d,%d,%d,%d)",kd,cb1,cb2,cb3);
+    return false;
+    }
+
+  unsigned int end=(kd>cb1 && kd>cb2 && kd>cb3) ? kd : datahdr->dlen;
+  unsigned int off=min(cb1,min(cb2,cb3))-2;
+  PRINTF(L_SYS_TPSAU,"pointers in data section kd=%d cb1=%d cb2=%d cb3=%d - dlen=%d off=%d end=%d - cb1=%d cb2=%d cb3=%d",kd,cb1,cb2,cb3,datahdr->dlen,off,end,cb1-off,cb2-off,cb3-off);
+  RegisterAlgo3(d+off,cb1-off,cb2-off,cb3-off,end-off);
+
+  const unsigned char *data=&d[kd];
+  int seclen, numkeys;
+  seclen=data[0] | (data[1]<<8);
+  numkeys=data[2] | (data[3]<<8);
+  int algo=data[4];
+  int mkidx=data[5]&7;
+  unsigned char *sec[7];
+  sec[0]=(unsigned char *)data+6;
+  for(int i=1; i<6; i++) sec[i]=sec[i-1]+seclen;
+  sec[6]=sec[5]+numkeys;
+  unsigned char key[16];
+  cPlainKey *pk=keys.FindKey('V',0x7c00,MBC3('M','K',mkidx),16);
+  if(!pk) {
+    PRINTF(L_SYS_KEY,"missing V 7C00 TPSMK%d key",mkidx);
+    return false;
+    }
+  pk->Get(key);
+
+  if(sec[6]>=d+datahdr->dlen) {
+    PRINTF(L_SYS_TPSAU,"section 5 exceeds buffer");
+    return false;
+    }
+  int keylen=0;
+  for(int i=0; i<numkeys; i++) keylen+=sec[5][i];
+  keylen=(keylen+15)&~15;
+  if(sec[6]+keylen>=d+datahdr->dlen) {
+    PRINTF(L_SYS_TPSAU,"section 6 exceeds buffer");
+    return false;
+    }
+  for(int i=0; i<keylen; i+=16) TpsDecrypt(&sec[6][i],algo,key);
+
+  cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
+  for(int i=0; i<seclen; i++) {
+    static const unsigned char startkey[] = { 0x01,0x01 };
+    static const unsigned char startaes[] = { 0x09,0x10 };
+    static const unsigned char startse[] = { 0x0a,0x10 };
+    unsigned char tmp[56];
+    tmp[0]=sec[0][i];
+    tmp[1]=sec[1][i];
+    tmp[2]=sec[2][i];
+    tmp[3]=sec[3][i];
+    if(CheckFF(tmp,4)) continue;
+    int keyid=sec[4][i];
+    int keylen=sec[5][keyid];
+    if(keylen<32) continue;
+    const unsigned char *tkey=sec[6];
+    for(int j=0; j<keyid; j++) tkey+=sec[5][j];
+
+    unsigned char ke[128];
+    if(keylen!=45) {
+      if(!Handle80008003(tkey,keylen,ke)) continue;
+      tkey=ke;
+      }
+
+    if(memcmp(tkey,startkey,sizeof(startkey))) continue;
+    tmp[52]=0;
+    tmp[53]=tkey[5]; //tkey[4];
+    tmp[54]=1;       //tkey[5];
+    tmp[55]=0x1c;
+    tkey+=9;
+    if(memcmp(tkey,startaes,sizeof(startaes))) continue;
+    memset(&tmp[4+ 0],0,16);
+    memcpy(&tmp[4+16],&tkey[2],16);
+    tkey+=18;
+    if(memcmp(tkey,startse,sizeof(startse))) continue;
+    memcpy(&tmp[4+32],&tkey[2],16);
+    cTpsKey *k=new cTpsKey;
+    if(k) { k->Set(tmp); nlist->Add(k); }
+    }
+  PRINTF(L_SYS_TPSAU,"got %d keys from AU data",nlist->Count());
+  bool res=nlist->Count()>0;
+  Join(nlist);
+  return res;
+}
+
+/*
+bool cTpsKeys::LoadBin(void)
+{
+  static const unsigned char mark[] = { 'T','P','S',0 };
+  cFileMap *tpsbin=filemaps.GetFileMap(TPSBIN,FILEMAP_DOMAIN,false);
+  if(!tpsbin) {
+    PRINTF(L_SYS_TPS,"no filemap for "TPSBIN);
+    return false;
+    }
+  else if(!tpsbin->Map()) {
+    PRINTF(L_SYS_TPS,"mapping failed for "TPSBIN);
+    return false;
+    }
+  else if(tpsbin->Size()<65536 || memcmp(mark,tpsbin->Addr()+32,sizeof(mark)) || memcmp(mark,tpsbin->Addr()+48,sizeof(mark))) {
+    PRINTF(L_SYS_TPS,TPSBIN" format not recognised");
+    tpsbin->Unmap();
+    return false;
+    }
+
+  int size=tpsbin->Size()-56;
+  cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
+  for(int i=68; i<size; i+=56) {
+    const unsigned char *a=tpsbin->Addr()+i;
+    if(*((const unsigned int *)a)==0x00000000L || *((const unsigned int *)a)==0xFFFFFFFFL)
+      break;
+    unsigned char tmp[56];
+    DecryptBin(a,tmp);
+    cTpsKey *k=new cTpsKey;
+    if(k) { k->Set(tmp); nlist->Add(k); }
+    }
+  tpsbin->Unmap();
+  PRINTF(L_SYS_TPSAU,"loaded %d keys from "TPSBIN" file",nlist->Count());
+  Join(nlist);
+  return true;
+}
+
+void cTpsKeys::DecryptBin(const unsigned char *in, unsigned char *out)
+{
+  unsigned int var2=*((unsigned int *)in);
+  *((unsigned int *)out)=var2;
+  for(int i=0; i<13; i++) {
+    in+=4; out+=4;
+    var2=(var2<<3) | (var2>>(32-3));
+    unsigned int var1=*((unsigned int *)in) ^ var2;
+    *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
+    }
+}
+*/
+
+void cTpsKeys::PreLoad(void)
+{
+  delete loadlist;
+  loadlist=new cSimpleList<cTpsKey>;
+  if(!loadlist) PRINTF(L_SYS_TPS,"no memory for loadlist");
+}
+
+void cTpsKeys::PostLoad(void)
+{
+  if(loadlist) { Join(loadlist); loadlist=0; }
+}
+
+bool cTpsKeys::ParseLinePlain(const char *line)
+{
+  unsigned char tmp[60];
+  if(line[0]=='X') {
+    if(line[1]=='S') {
+      if(algomem) PRINTF(L_SYS_TPS,"multiple start extentions during cache load");
+      if(sscanf(&line[2],"%x %x %x %x",&algolen,&cb1off,&cb2off,&cb3off)==4) {
+        free(algomem);
+        if((algomem=MALLOC(unsigned char,algolen))) {
+          algoread=0;
+          return true;
+          }
+        else PRINTF(L_SYS_TPS,"no memory for algo");
+        }
+      else PRINTF(L_SYS_TPS,"bad format in start extention");
+      }
+    else if(line[1]=='C') {
+      if(algomem) {
+        int off, len;
+        unsigned int crc;
+        if(sscanf(&line[2],"%x %x %n",&off,&crc,&len)==2) {
+          line+=len+2;
+          unsigned char buff[210];
+          if((len=GetHex(line,buff,200,false))) {
+            if(crc==crc32_le(0,buff,len) && off>=0 && off+len<=algolen) {
+              memcpy(&algomem[off],buff,len);
+              algoread+=len;
+              if(algoread==algolen) {
+                RegisterAlgo3(algomem,cb1off,cb2off,cb3off,algolen);
+                free(algomem); algomem=0;
+                }
+              return true;
+              }
+            }
+          }
+        PRINTF(L_SYS_TPS,"bad format in code extention");
+        }
+      else PRINTF(L_SYS_TPS,"unexpected code extention");
+      }
+    else PRINTF(L_SYS_TPS,"unknown extention during cache load");
+    }
+  else {
+    if(GetHex(line,tmp,sizeof(tmp))) {
+      unsigned int crc=crc32_le(0,&tmp[4],sizeof(tmp)-4);
+      if(*((unsigned int *)tmp)==crc) {
+        cTpsKey *k=new cTpsKey;
+        if(k) { k->Set(&tmp[4]); if(loadlist) loadlist->Add(k); }
+        return true;
+        }
+      else PRINTF(L_SYS_TPS,"CRC failed during cache load");
+      }
+    }
+  return false;
+}
+
+void cTpsKeys::PostSave(FILE *f)
+{
+  char str[420];
+  unsigned char *mem;
+  int len=0, cb1=0, cb2=0, cb3=0;
+  if((mem=DumpAlgo3(len,cb1,cb2,cb3))) {
+    fprintf(f,"XS %04X %04X %04X %04X\n",len,cb1,cb2,cb3);
+    for(int i=0; i<len; i+=200) {
+      int l=min(200,len-i);
+      fprintf(f,"XC %04X %08X %s\n",i,crc32_le(0,&mem[i],l),HexStr(str,&mem[i],l));
+      }
+    free(mem);
+    }
+}
+
+// -- cTpsAuHook ---------------------------------------------------------------
+
+#ifndef TESTER
+
+#define AUSID 0x12C0
+
+cTpsAuHook::cTpsAuHook(void)
+:cLogHook(HOOK_TPSAU,"tpsau")
+{
+  mod=0; pmtpid=aupid=-1;
+  pids.AddPid(0x0000,0x00,0xff); // PAT
+}
+
+cTpsAuHook::~cTpsAuHook()
+{
+  delete mod;
+}
+
+void cTpsAuHook::Process(int pid, unsigned char *data)
+{
+  if(data) {
+    if(pid==0) { // PAT
+      SI::PAT pat(data,false);
+      if(pat.CheckCRCAndParse()) {
+        SI::PAT::Association assoc;
+        for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
+          if(!assoc.isNITPid() && assoc.getServiceId()==AUSID) {
+            pmtpid=assoc.getPid();
+            PRINTF(L_SYS_TPSAU,"got PMT pid %04x for SID %04x",pmtpid,AUSID);
+            cPid *pid=pids.First();
+            if(pid && pid->filter) {
+              pid->filter->Start(pmtpid,0x02,0xFF,0x00,false);
+              pid->filter->Flush();
+              }
+            else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pat");
+            return;
+            }
+          }
+        PRINTF(L_SYS_TPSAU,"no PMT pid found for SID %04x",AUSID);
+        BailOut();
+        }
+      }
+    else if(pid==pmtpid) { // PMT
+      SI::PMT pmt(data,false);
+      if(pmt.CheckCRCAndParse() && pmt.getServiceId()==AUSID) {
+        SI::PMT::Stream stream;
+        for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
+          if(stream.getStreamType()==0x05) {
+            aupid=stream.getPid();
+            PRINTF(L_SYS_TPSAU,"got AU pid %04x",aupid);
+            cPid *pid=pids.First();
+            if(pid && pid->filter) {
+              pid->filter->Start(aupid,0x87,0xFF,0x00,false);
+              pid->filter->Flush();
+              }
+            else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pmt");
+            return;
+            }
+          }
+        PRINTF(L_SYS_TPSAU,"could not locate AU pid in PMT %04x data",pmtpid);
+        BailOut();
+        }
+      }
+    else if(pid==aupid) {
+      if(cOpenTVModule::Id(data)==2) {
+        if(!mod) mod=new cOpenTVModule(data);
+        if(mod) {
+          int r=mod->AddPart(data,SCT_LEN(data));
+          if(r>0) {
+            PRINTF(L_SYS_TPSAU,"received complete OpenTV module ID 2");
+            if(tpskeys.ProcessAu(mod)) BailOut();
+            r=-1;
+            }            
+          if(r<0) { delete mod; mod=0; }
+          }
+        }
+      }
+    }
+}
+
+#endif //TESTER
+
+// -- cTPSDecrypt --------------------------------------------------------------
+
+unsigned char *cTPSDecrypt::mem=0;
+int cTPSDecrypt::memLen=0;
+int cTPSDecrypt::cb1off=0;
+int cTPSDecrypt::cb2off=0;
+int cTPSDecrypt::cb3off=0;
+cMutex cTPSDecrypt::st20Mutex;
+cST20 cTPSDecrypt::st20;
+bool cTPSDecrypt::st20Inited=false;
+
+void cTPSDecrypt::TpsDecrypt(unsigned char *data, short mode, const unsigned char *key)
+{
+  switch(mode) {
+    case 0: break;
+    case 1: cAES::SetKey(key); cAES::Decrypt(data,16); break;
+    case 2: cRC6::SetKey(key,16); cRC6::Decrypt(data); break;
+    case 3: if(mem) {
+              if(!DecryptAlgo3(key,data))
+                PRINTF(L_SYS_TPS,"decrypt failed in algo 3");
+              }
+            else PRINTF(L_SYS_TPS,"no callbacks for algo 3 registered");
+            break;
+    default: PRINTF(L_SYS_TPS,"unknown TPS decryption algo %d",mode); break;
+    }
+}
+
+bool cTPSDecrypt::RegisterAlgo3(const unsigned char *data, int cb1, int cb2, int cb3, int len)
+{
+  cMutexLock lock(&st20Mutex);
+  free(mem);
+  if(!(mem=MALLOC(unsigned char,len))) return false;
+  memcpy(mem,data,len);
+  memLen=len; cb1off=cb1; cb2off=cb2; cb3off=cb3;
+  st20Inited=false;
+  PRINTF(L_SYS_TPSAU,"registered callbacks for algo 3");
+  return true;
+}
+
+unsigned char *cTPSDecrypt::DumpAlgo3(int &len, int &cb1, int &cb2, int &cb3)
+{
+  cMutexLock lock(&st20Mutex);
+  if(!mem) return 0;
+  unsigned char *buff=MALLOC(unsigned char,memLen);
+  if(!buff) return 0;
+  memcpy(buff,mem,memLen);
+  len=memLen; cb1=cb1off; cb2=cb2off; cb3=cb3off;
+  return buff;
+}
+
+bool cTPSDecrypt::InitST20(void)
+{
+  if(!mem) return false;
+  if(!st20Inited) {
+    st20.SetFlash(mem,memLen);
+    st20.SetRam(NULL,0x10000);
+    st20Inited=true;
+    }
+  return true;
+}
+
+bool cTPSDecrypt::Handle80008003(const unsigned char *src, int len, unsigned char *dest)
+{
+  cMutexLock lock(&st20Mutex);
+  if(cb1off && InitST20()) {
+    for(int i=0; i<len; i++) st20.WriteByte(RAMS+0x400+i,src[i]);
+    st20.WriteShort(RAMS+0x0,0x8000);
+    st20.WriteShort(RAMS+0x2,0x8003);
+    st20.WriteWord(RAMS+0x8,RAMS+0x400);
+    st20.Init(FLASHS+cb1off,RAMS+0xF000);
+    st20.SetCallFrame(0,RAMS,0,0);
+    int err=st20.Decode(1000);
+    if(err<0) {
+      PRINTF(L_SYS_TPSAU,"ST20 processing failed in callback1 (%d)",err);
+      return false;
+      }
+    for(int i=0; i<0x2D; i++) dest[i]=st20.ReadByte(RAMS+0x400+i);
+    return true;
+    }
+  return false;
+}
+
+bool cTPSDecrypt::DecryptAlgo3(const unsigned char *key, unsigned char *data)
+{
+  cMutexLock lock(&st20Mutex);
+  if(cb2off && cb3off && InitST20()) {
+    for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,key[i]);
+    st20.Init(FLASHS+cb2off,RAMS+0xF000);
+    st20.SetCallFrame(0,RAMS+0x400,RAMS+0x800,0);
+    int err=st20.Decode(30000);
+    if(err<0) {
+      PRINTF(L_SYS_TPS,"ST20 processing failed in callback2 (%d)",err);
+      return false;
+      }
+
+    for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,data[i]);
+    st20.Init(FLASHS+cb3off,RAMS+0xF000);
+    st20.SetCallFrame(0,RAMS+0x400,RAMS+0x1000,RAMS+0x800);
+    err=st20.Decode(40000);
+    if(err<0) {
+      PRINTF(L_SYS_TPS,"ST20 processing failed in callback3 (%d)",err);
+      return false;
+      }
+    for(int i=0; i<16; i++) data[i]=st20.ReadByte(RAMS+0x1000+i);
+    return true;
+    }
+  return false;
+}
+
+// -- cTPS ---------------------------------------------------------------------
+
+cTPS::cTPS(void)
+{
+  sattime=0;
+}
+
+cTPS::~cTPS()
+{
+  delete sattime;
+}
+
+int cTPS::Decrypt(int cardNum, int Source, int Transponder, unsigned char *data, int len)
+{
+  if(!sattime) {
+    sattime=new cSatTime(cardNum,Source,Transponder);
+    if(!sattime) {
+      PRINTF(L_SYS_TPS,"failed to create time class");
+      return -1;
+      }
+    }
+  time_t now=sattime->Now();
+  tpskeys.Check(now,cardNum);
+  if(now<0) return -1;
+
+  const cTpsKey *k=tpskeys.GetKey(now);
+  doPost=0;
+  int opmode=k?k->Opmode():4, doTPS=0, doPre=0, hasDF=0, ret=0;
+  if((opmode&4) && data[0]==0xD2 && data[1]==0x01 && data[2]==0x01) {
+    data+=3; len-=3; ret=3;
+    if(data[0]==0x90) PRINTF(L_SYS_TPS,"TPS v1 is no longer supported");
+    if(data[0]==0x40) data[0]=0x90;
+    doTPS=1;
+    if(!k) k=tpskeys.GetV2Key(0x7c00);
+    }
+  for(int i=0; i<len; i+=data[i+1]+2) {
+    if(!k && (doTPS || doPre || doPost)) {
+      PRINTF(L_SYS_TPS,"no TPS key available for current time of day");
+      return -1;
+      }
+    switch(data[i]) {
+      case 0xDF:
+        if(!(opmode&4)) doTPS =(0x6996>>((data[i+2]&0xF)^(data[i+2]>>4)))&1;
+        if(opmode&8)    doPre =(0x6996>>((data[i+3]&0xF)^(data[i+3]>>4)))&1;
+        if(opmode&16)   doPost=(0x6996>>((data[i+4]&0xF)^(data[i+4]>>4)))&1;
+        hasDF=1;
+        break;
+      case 0xEA:
+        if(doPre) TpsDecrypt(&data[i+2],k->Mode(0),k->Key(0));
+        if(doTPS) TpsDecrypt(&data[i+2],(hasDF)?k->Mode(2):1,k->Key(2));
+        if(doPost) { postMode=k->Mode(1); memcpy(postKey,k->Key(1),sizeof(postKey)); }
+        break;
+      }
+    }
+  return ret;
+}
+
+void cTPS::PostProc(unsigned char *cw)
+{
+  if(doPost) TpsDecrypt(cw,postMode,postKey);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/tps.h
new file mode 100644 (file)
index 0000000..57bfc84
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __VIACCESS_TPS_H
+#define __VIACCESS_TPS_H
+
+#include <time.h>
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+#include "data.h"
+#include "crypto.h"
+
+class cSatTime;
+class cTpsAuHook;
+class cOpenTVModule;
+class cST20;
+
+// ----------------------------------------------------------------
+
+#define RC6_ROUNDS     20
+#define RC6_MAX                (RC6_ROUNDS*2+4)
+
+class cRC6 {
+private:
+  unsigned int key[RC6_MAX];
+  //
+  unsigned int rol(unsigned int v, unsigned int cnt);
+  unsigned int ror(unsigned int v, unsigned int cnt);
+public:
+  void SetKey(const unsigned char *Key, int len);
+  void Decrypt(unsigned char *data);
+  };
+
+// ----------------------------------------------------------------
+
+class cTPSDecrypt : private cAES, private cRC6 {
+private:
+  static unsigned char *mem;
+  static int memLen, cb1off, cb2off, cb3off;
+  static cMutex st20Mutex;
+  static cST20 st20;
+  static bool st20Inited;
+  //
+  static bool InitST20(void);
+  static bool DecryptAlgo3(const unsigned char *key, unsigned char *data);
+protected:
+  void TpsDecrypt(unsigned char *data, short mode, const unsigned char *key);
+  static bool RegisterAlgo3(const unsigned char *data, int cb1, int cb2, int cb3, int len);
+  static unsigned char *DumpAlgo3(int &len, int &cb1, int &cb2, int &cb3);
+  static bool Handle80008003(const unsigned char *src, int len, unsigned char *dest);
+  };
+
+// ----------------------------------------------------------------
+
+class cTPS : private cTPSDecrypt {
+private:
+  cSatTime *sattime;
+  int doPost;
+  short postMode;
+  unsigned char postKey[16];
+public:
+  cTPS(void);
+  ~cTPS();
+  int Decrypt(int cardNum, int Source, int Transponder, unsigned char *data, int len);
+  void PostProc(unsigned char *cw);
+  };
+
+// ----------------------------------------------------------------
+
+class cTpsKey : public cStructItem {
+private:
+  time_t timestamp;
+  int opmode;
+  struct {
+    unsigned char mode;
+    unsigned char key[16];
+    } step[3];
+  //
+  void Put(unsigned char *mem) const;
+public:
+  cTpsKey(void);
+  const unsigned char *Key(int st) const { return step[st].key; }
+  int Mode(int st) const { return step[st].mode; }
+  int Opmode(void) const { return opmode; }
+  time_t Timestamp(void) const { return timestamp; }
+  void Set(const cTpsKey *k);
+  void Set(const unsigned char *mem);
+  virtual cString ToString(bool hide=false);
+  };
+
+// ----------------------------------------------------------------
+
+class cTpsKeys : public cStructListPlain<cTpsKey>, private cTPSDecrypt {
+friend class cTpsAuHook;
+private:
+  time_t first, last;
+  cSimpleList<cTpsKey> *loadlist;
+  //
+  cTimeMs lastCheck, lastLoad, lastAu;
+  cMutex checkMutex;
+  //
+  unsigned char *algomem;
+  int algolen, algoread, cb1off, cb2off, cb3off;
+  //
+  void Join(cSimpleList<cTpsKey> *nlist);
+  void Purge(time_t now);
+  void GetFirstLast(void);
+//  bool LoadBin(void);
+//  void DecryptBin(const unsigned char *in, unsigned char *out);
+  cString Time(time_t t);
+  bool ProcessAu(const cOpenTVModule *mod);
+protected:
+  virtual bool ParseLinePlain(const char *line);
+  virtual void PreLoad(void);
+  virtual void PostLoad(void);
+  virtual void PostSave(FILE *f);
+public:
+  cTpsKeys(void);
+  ~cTpsKeys();
+  const cTpsKey *GetKey(time_t t);
+  const cTpsKey *GetV2Key(int id);
+  void Check(time_t now, int cardnum);
+  };
+
+extern cTpsKeys tpskeys;
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.c
new file mode 100644 (file)
index 0000000..a8d4fa7
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "system-common.h"
+#include "misc.h"
+#include "parse.h"
+#include "log-core.h"
+
+#include "viaccess.h"
+#include "tps.h"
+#include "log-viaccess.h"
+
+#define SYSTEM_NAME          "Viaccess"
+#define SYSTEM_PRI           -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)
+
+static const struct LogModule lm_sys = {
+  (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_SYS_DEFDEF|L_SYS_TPS|L_SYS_TPSAU|L_SYS_TIME|L_SYS_ST20)&LOPT_MASK,
+  "viaccess",
+  { L_SYS_DEFNAMES,"tps","tpsau","time","st20","disasm" }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cPlainKeyVia -------------------------------------------------------------
+
+#define VIA1_KEYLEN 8
+#define VIA2_KEYLEN 16
+#define VIATPS_KEYLEN 16
+
+class cPlainKeyVia : public cPlainKeyStd {
+protected:
+  virtual int IdSize(void) { return 6; }
+  virtual cString PrintKeyNr(void);
+public:
+  cPlainKeyVia(bool Super);
+  virtual bool Parse(const char *line);
+  virtual bool SetKey(void *Key, int Keylen);
+  virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+  };
+
+static cPlainKeyTypeReg<cPlainKeyVia,'V'> KeyReg;
+
+cPlainKeyVia::cPlainKeyVia(bool Super)
+:cPlainKeyStd(Super)
+{}
+
+bool cPlainKeyVia::SetKey(void *Key, int Keylen)
+{
+  if(keynr==MBC3('T','P','S')) SetSupersede(false);
+  return cPlainKeyStd::SetKey(Key,Keylen);
+}
+
+bool cPlainKeyVia::SetBinKey(unsigned char *Mem, int Keylen)
+{
+  if(keynr==MBC3('T','P','S')) SetSupersede(false);
+  return cPlainKeyStd::SetBinKey(Mem,Keylen);
+}
+
+bool cPlainKeyVia::Parse(const char *line)
+{
+  unsigned char sid[3], skeynr, skey[VIA2_KEYLEN];
+  int len;
+  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) {
+     type=toupper(type); id=Bin2Int(sid,len);
+     line=skipspace(line);
+     bool ok=false;
+     if(!strncasecmp(line,"TPSMK",5) && line[5]>='0' && line[5]<='9') {
+       keynr=MBC3('M','K',line[5]-'0');
+       line+=6;
+       ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
+       }
+     else if(!strncasecmp(line,"TPS ",4)) {
+       line+=4;
+       keynr=MBC3('T','P','S');
+       ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
+       }
+     else if(GetHex(line,&skeynr,1)) {
+       keynr=skeynr;
+       ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==VIA1_KEYLEN || len==VIA2_KEYLEN));
+       }
+     if(ok) {
+       SetBinKey(skey,len);
+       return true;
+       }
+    }
+  return false;
+}
+
+cString cPlainKeyVia::PrintKeyNr(void)
+{
+  char tmp[12];
+  const char *kn=tmp;
+  switch(keynr) {
+    case MBC3('T','P','S'):
+      kn="TPS"; break;
+    case MBC3('M','K',0): case MBC3('M','K',1): case MBC3('M','K',2):
+    case MBC3('M','K',3): case MBC3('M','K',4): case MBC3('M','K',5):
+    case MBC3('M','K',6): case MBC3('M','K',7): case MBC3('M','K',8):
+    case MBC3('M','K',9):
+      snprintf(tmp,sizeof(tmp),"TPSMK%d",C3(keynr)); break;
+    default:
+      snprintf(tmp,sizeof(tmp),"%02X",keynr); break;
+    }
+  return kn;
+}
+
+// -- cViaccessCardInfo --------------------------------------------------------
+
+class cViaccessCardInfo : public cStructItem, public cProviderViaccess, public cCardViaccess {
+public:
+  unsigned char keyno, key[8];
+  //
+  bool Parse(const char *line);
+  };
+
+bool cViaccessCardInfo::Parse(const char *line)
+{
+  return GetHex(line,ident,sizeof(ident)) && 
+         GetHex(line,ua,sizeof(ua)) &&
+         GetHex(line,sa,sizeof(sa)) &&
+         GetHex(line,&keyno,1) && 
+         GetHex(line,key,sizeof(key));
+}
+
+// -- cViaccessCardInfos -------------------------------------------------------
+
+class cViaccessCardInfos : public cCardInfos<cViaccessCardInfo> {
+public:
+  cViaccessCardInfos(void):cCardInfos<cViaccessCardInfo>("Viaccess cards","Viaccess.KID",0) {}
+  };
+
+static cViaccessCardInfos Vcards;
+
+// -- cViaccess ----------------------------------------------------------------
+
+class cViaccess : protected cDes {
+private:
+  unsigned char v2key[8];
+  bool v2mode;
+  //
+  int HashNanos(const unsigned char *data, int len);
+  void Via2Mod(const unsigned char *key2, unsigned char *data);
+protected:
+  unsigned char hbuff[8], hkey[8];
+  int pH;
+  //
+  void SetV2Mode(const unsigned char *key2);
+  void SetHashKey(const unsigned char *key);
+  void HashByte(unsigned char c);
+  void HashClear(void);
+  void Hash(void);
+  //
+  void Decode(unsigned char *data, const unsigned char *key);
+  bool Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2);
+  //
+  virtual unsigned int Mod(unsigned int R, unsigned int key7) const;
+public:
+  cViaccess(void);
+  };
+
+cViaccess::cViaccess(void)
+:cDes()
+{
+  v2mode=false;
+}
+
+/* viaccess DES modification */
+
+unsigned int cViaccess::Mod(unsigned int R, unsigned int key7) const
+{
+  if(key7!=0) {
+    const unsigned int key5=(R>>24)&0xff;
+    unsigned int al=key7*key5 + key7 + key5;
+    al=(al&0xff)-((al>>8)&0xff);
+    if(al&0x100) al++;
+    R=(R&0x00ffffffL) + (al<<24);
+    }
+  return R;
+}
+
+/* viaccess2 modification. Extracted from "Russian wafer" card.
+   A lot of thanks to it's author :) */
+
+void cViaccess::Via2Mod(const unsigned char *key2, unsigned char *data)
+{
+  int kb, db;
+  for(db=7; db>=0; db--) {
+    for(kb=7; kb>3; kb--) {
+      int a0=kb^db;
+      int pos=7;
+      if(a0&4) { a0^=7; pos^=7; }
+      a0=(a0^(kb&3)) + (kb&3);
+      if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
+      }
+    }
+  for(db=0; db<8; db++) {
+    for(kb=0; kb<4; kb++) {
+      int a0=kb^db;
+      int pos=7;
+      if(a0&4) { a0^=7; pos^=7; }
+      a0=(a0^(kb&3)) + (kb&3);
+      if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
+      }
+    }
+}
+
+void cViaccess::Decode(unsigned char *data, const unsigned char *key)
+{
+  if(v2mode) Via2Mod(v2key,data);
+  Des(data,key,VIA_DES);
+  if(v2mode) Via2Mod(v2key,data);
+}
+
+void cViaccess::SetV2Mode(const unsigned char *key2)
+{
+  if(key2) {
+    memcpy(v2key,key2,sizeof(v2key));
+    v2mode=true;
+    }
+  else v2mode=false;
+}
+
+
+void cViaccess::SetHashKey(const unsigned char *key)
+{
+  memcpy(hkey,key,sizeof(hkey));
+}
+
+void cViaccess::HashByte(unsigned char c)
+{
+  hbuff[pH++]^=c;
+  if(pH==8) { pH=0; Hash(); }
+}
+
+void cViaccess::HashClear(void)
+{
+  memset(hbuff,0,sizeof(hbuff));
+  pH=0;
+}
+
+void cViaccess::Hash(void)
+{
+  if(v2mode) Via2Mod(v2key,hbuff);
+  Des(hbuff,hkey,VIA_DES_HASH);
+  if(v2mode) Via2Mod(v2key,hbuff);
+}
+
+int cViaccess::HashNanos(const unsigned char *data, int len)
+{
+  int i=0;
+  pH=0;
+  if(data[0]==0x9f) {
+    HashByte(data[i++]);
+    HashByte(data[i++]);
+    for(int j=0; j<data[1]; j++) HashByte(data[i++]);
+    while(pH!=0) HashByte(0);
+    }
+  for(; i<len; i++) HashByte(data[i]);
+  return i;
+}
+
+bool cViaccess::Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2)
+{
+  int pos=0, encStart=0;
+  unsigned char signatur[8];
+  while(pos<len) {
+    switch(data[pos]) {
+      case 0xea:                 // encrypted bytes
+        encStart = pos + 2;
+        memcpy(des_data1,&data[pos+2],8);
+        memcpy(des_data2,&data[pos+2+8],8);
+        break;
+      case 0xf0:                 // signature
+        memcpy(signatur,&data[pos+2],8);
+        break;
+      }
+    pos += data[pos+1]+2;
+    }
+  HashClear();
+  SetHashKey(work_key);
+  // key preparation
+  unsigned char prepared_key[8];
+  if(work_key[7]==0) {
+    // 8th key-byte = 0 then like Eurocrypt-M but with viaccess mods
+    HashNanos(data,encStart+16);
+    memcpy(prepared_key,work_key,sizeof(prepared_key));
+    }
+  else { // key8 not zero
+    // rotate the key 2x left
+    prepared_key[0]=work_key[2];
+    prepared_key[1]=work_key[3];
+    prepared_key[2]=work_key[4];
+    prepared_key[3]=work_key[5];
+    prepared_key[4]=work_key[6];
+    prepared_key[5]=work_key[0];
+    prepared_key[6]=work_key[1];
+    prepared_key[7]=work_key[7];
+    // test if key8 odd
+    if(work_key[7]&1) {
+      HashNanos(data,encStart);
+      // test if low nibble zero
+      unsigned char k = ((work_key[7] & 0xf0) == 0) ? 0x5a : 0xa5;
+      for(int i=0; i<8; i++) {
+        unsigned char tmp=des_data1[i];
+        des_data1[i]=(k & hbuff[pH]) ^ tmp;
+        HashByte(tmp);
+        }
+      for(int i=0; i<8; i++) {
+        unsigned char tmp=des_data2[i];
+        des_data2[i]=(k & hbuff[pH]) ^ tmp;
+        HashByte(tmp);
+        }
+      }
+    else {
+      HashNanos(data,encStart+16);
+      }
+    }
+  Decode(des_data1,prepared_key);
+  Decode(des_data2,prepared_key);
+  Hash();
+  return (memcmp(signatur,hbuff,8)==0);
+}
+
+// -- cSystemViaccess ----------------------------------------------------------
+
+#define MAX_NEW_KEYS 5
+
+class cSystemViaccess : public cSystem, private cViaccess {
+private:
+  cTPS tps;
+public:
+  cSystemViaccess(void);
+  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+  virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+  virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len);
+  };
+
+cSystemViaccess::cSystemViaccess(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  hasLogger=true;
+}
+
+void cSystemViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, unsigned short source, const unsigned char *data, int len)
+{
+  const int pid=WORD(data,2,0x1FFF);
+  if(pid>=0xAA && pid<=0xCF) {
+    PRINTF(L_CORE_ECMPROC,"viaccess: dropped \"fake\" ecm pid 0x%04x",pid);
+    return;
+    }
+  cSystem::ParseCADescriptor(ecms,sysId,source,data,len); 
+}
+
+bool cSystemViaccess::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+  unsigned char *nanos=(unsigned char *)cParseViaccess::NanoStart(data);
+  int len=SCT_LEN(data)-(nanos-data);
+
+  bool mayHaveTps=false;
+  if(ecm->provId==0x007c00) { // TPS
+    maxEcmTry=4;
+    mayHaveTps=true;
+    int num=tps.Decrypt(CardNum(),ecm->source,ecm->transponder,nanos,len);
+    if(num<0) return false;
+    nanos+=num; len-=num;
+    }
+
+  if(cParseViaccess::CheckNano90FromNano(nanos)) {
+    int keynr=cParseViaccess::KeyNrFromNano(nanos);
+    cKeySnoop ks(this,'V',ecm->provId,keynr);
+    cPlainKey *pk=0;
+    while((pk=keys.FindKey('V',ecm->provId,keynr,-1,pk))) {
+      unsigned char key[16];
+      if(pk->Size()<=(int)sizeof(key)) {
+        pk->Get(key);
+        SetV2Mode(pk->Size()==VIA2_KEYLEN ? &key[VIA1_KEYLEN] : 0);
+        if(cViaccess::Decrypt(key,&nanos[5],len-5,&cw[0],&cw[8])) {
+          if(mayHaveTps) tps.PostProc(cw);
+          ks.OK(pk);
+          return true;
+          }
+        }
+      }
+    }
+  return false;
+}
+
+void cSystemViaccess::ProcessEMM(int pid, int caid, unsigned char *data)
+{
+  for(cViaccessCardInfo *mkey=Vcards.First(); mkey; mkey=Vcards.Next(mkey)) {
+    int updtype;
+    cAssembleData ad(data);
+    if(mkey->cCardViaccess::MatchEMM(data)) {
+      updtype=3;
+      HashClear();
+      memcpy(hbuff+3,mkey->ua,sizeof(mkey->ua));
+      }
+    else if(mkey->cProviderViaccess::MatchEMM(data)) {
+      if(mkey->cProviderViaccess::Assemble(&ad)<0) continue;
+      updtype=2;
+      HashClear();
+      memcpy(hbuff+5,mkey->sa,sizeof(mkey->sa)-1);
+      }
+    else continue;
+
+    const unsigned char *buff;
+    if((buff=ad.Assembled())) {
+      const unsigned char *scan=cParseViaccess::NanoStart(buff);
+      unsigned int scanlen=SCT_LEN(buff)-(scan-buff);
+
+      if(scanlen>=5 && mkey->cProviderViaccess::MatchID(buff) &&
+         cParseViaccess::KeyNrFromNano(scan)==mkey->keyno) {
+        scan+=5; scanlen-=5;
+        SetHashKey(mkey->key);
+        Hash();
+
+        unsigned int n;
+        if(scan[0]==0x9e && scanlen>=(n=scan[1]+2)) {
+          for(unsigned int i=0; i<n; i++) HashByte(scan[i]);
+          Hash(); pH=0;
+          scan+=n; scanlen-=5;
+          }
+        if(scanlen>0) {
+          unsigned char newKey[MAX_NEW_KEYS][8];
+          int numKeys=0, updPrv[MAX_NEW_KEYS]={}, updKey[MAX_NEW_KEYS]={};
+
+          for(unsigned int cnt=0; cnt<scanlen && numKeys<MAX_NEW_KEYS;) {
+            const unsigned int parm=scan[cnt++];
+            unsigned int plen=scan[cnt++];
+
+            switch(parm) {
+              case 0x90:
+              case 0x9E:
+                cnt+=plen;
+                break;
+              case 0xA1: // keyupdate
+                updPrv[numKeys]=(scan[cnt]<<16)+(scan[cnt+1]<<8)+(scan[cnt+2]&0xF0);
+                updKey[numKeys]=scan[cnt+2]&0x0F;
+                // fall through
+              default:
+                HashByte(parm); HashByte(plen);
+                while(plen--) HashByte(scan[cnt++]);
+                break;
+              case 0xEF: // crypted key(s)
+                HashByte(parm); HashByte(plen);
+                if(plen==sizeof(newKey[0])) {
+                  const unsigned char k7=mkey->key[7];
+                  for(unsigned int kc=0 ; kc<sizeof(newKey[0]) ; kc++) {
+                    const unsigned char b=scan[cnt++];
+                    if(k7&1) newKey[numKeys][kc]=b^(hbuff[pH]&(k7<0x10 ? 0x5a : 0xa5));
+                    else     newKey[numKeys][kc]=b;
+                    HashByte(b);
+                    }
+                  numKeys++;
+                  }
+                else {
+                  PRINTF(L_SYS_EMM,"%d: key length mismatch %d!=%d",CardNum(),plen,(int)sizeof(newKey[0]));
+                  cnt=scanlen;
+                  }
+                break;
+              case 0xF0: // signature
+                {
+                char str[20], str2[20];
+                static const char *ptext[] = { 0,0,"SHARED","UNIQUE" };
+                const char *addr = (updtype==2) ? HexStr(str,mkey->sa,sizeof(mkey->sa)) : HexStr(str,mkey->ua,sizeof(mkey->ua));
+
+                Hash();
+                if(!memcmp(&scan[cnt],hbuff,sizeof(hbuff))) {
+                  unsigned char key[8];
+                  memcpy(key,mkey->key,sizeof(key));
+                  if(key[7]) { // Rotate key
+                    const unsigned char t1=key[0], t2=key[1];
+                    key[0]=key[2]; key[1]=key[3]; key[2]=key[4]; key[3]=key[5]; key[4]=key[6];
+                    key[5]=t1; key[6]=t2;
+                    }
+
+                  while(numKeys--) {
+                    Decode(newKey[numKeys],key);
+                    PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - KEY %06X.%02X -> %s",
+                        mkey->ident[0],mkey->ident[1],mkey->keyno,addr,
+                        ptext[updtype],updPrv[numKeys],updKey[numKeys],
+                        HexStr(str2,newKey[numKeys],sizeof(newKey[numKeys])));
+                    FoundKey();
+                    if(keys.NewKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8)) NewKey();
+                    }
+                  }
+                else
+                  PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - FAIL",mkey->ident[0],mkey->ident[1],mkey->keyno,addr,ptext[updtype]);
+                cnt=scanlen;
+                break;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+}
+
+// -- cSystemLinkViaccess ------------------------------------------------------
+
+class cSystemLinkViaccess : public cSystemLink {
+public:
+  cSystemLinkViaccess(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void) { return new cSystemViaccess; }
+  };
+
+static cSystemLinkViaccess staticInit;
+
+cSystemLinkViaccess::cSystemLinkViaccess(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkViaccess::CanHandle(unsigned short SysId)
+{
+  SysId&=SYSTEM_MASK;
+  return SYSTEM_CAN_HANDLE(SysId);
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.h
new file mode 100644 (file)
index 0000000..47cff12
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef __VIACCESS_VIACCESS_H
+#define __VIACCESS_VIACCESS_H
+
+#define SYSTEM_VIACCESS      0x0500
+
+#define FILEMAP_DOMAIN       "viaccess"
+#define TPSBIN               "tps.bin"
+#define HOOK_SATTIME         ((SYSTEM_VIACCESS<<8)+0x01)
+#define HOOK_TPSAU           ((SYSTEM_VIACCESS<<8)+0x02)
+
+#endif
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.mk b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/systems/viaccess/viaccess.mk
new file mode 100644 (file)
index 0000000..c5fde80
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Viaccess
+#
+TARGET = viaccess
+OBJS   = viaccess.o tps.o st20.o
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/Makefile b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/Makefile
new file mode 100644 (file)
index 0000000..3f5074b
--- /dev/null
@@ -0,0 +1,77 @@
+
+### The directory environment:
+
+VDRDIR = ../../../..
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The C++ compiler and options:
+
+CXX      ?= g++
+CXXFLAGS ?= -g -march=pentium3 -O2 -Wall -Woverloaded-virtual
+
+### Includes and Defines
+
+SCAPIVERS = $(shell sed -ne '/define SCAPIVERS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' ../sc.c)
+APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSNUM = $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+
+INCLUDES = -I.. -I$(VDRDIR)/include
+DEFINES  = -DAPIVERSNUM=$(APIVERSNUM) -DAPIVERSION='"$(APIVERSION)"' -DSCAPIVERS=$(SCAPIVERS) -D_GNU_SOURCE
+
+OBJS = misc.o log.o data.o crypto.o parse.o system.o system-common.o smartcard.o network.o filter.o version.o
+SHAREDOBJS = compat.o $(VDRDIR)/tools.o $(VDRDIR)/thread.o
+LIBS = -lpthread -ljpeg -ldl -lcrypto
+
+NOBJS  = $(patsubst %.o,../%.o,$(OBJS))
+
+### Implicit rules:
+
+$(VDRDIR)/%.o: $(VDRDIR)/%.c
+       make -C $(VDRDIR) $(@F)
+
+../%.o: ../%.c
+       make -C .. $(@F)
+
+%.o: %.c
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Targets:
+
+all: testECM testEMM testN1Emu testN2Emu testN2RunEmu
+
+testECM.o: testECM.c compat.h
+testECM: testECM.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) -rdynamic $^ $(LIBS) -o $@
+
+testEMM.o: testEMM.c compat.h
+testEMM: testEMM.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) -rdynamic $^ $(LIBS) -o $@
+
+testN1Emu.o: testN1Emu.c ../systems/nagra/nagra.c ../systems/nagra/nagra1.c ../systems/nagra/cpu.c ../systems/nagra/log-nagra.h
+testN1Emu: testN1Emu.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+testN2Emu.o: testN2Emu.c compat.h
+testN2Emu: testN2Emu.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) $^ $(LIBS) -L../systems/nagra -lsc-nagra -o $@
+       @echo "don't forget: export LD_LIBRARY_PATH=../systems/nagra"
+
+testN2RunEmu.o: testN2RunEmu.c
+testN2RunEmu: testN2RunEmu.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) $^ $(LIBS) -L../systems/nagra -lsc-nagra -o $@
+       @echo "don't forget: export LD_LIBRARY_PATH=../systems/nagra"
+
+testTPS.o: testTPS.c ../systems/viaccess/tps.c ../systems/viaccess/st20.c ../systems/viaccess/viaccess.c
+testTPS: testTPS.o $(SHAREDOBJS) $(NOBJS)
+       $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+filterhelper: filterhelper.o
+       $(CXX) $(CXXFLAGS) $^ -o $@
+clean:
+       @-rm -f *.o core* *~
+       @-rm -f testECM testEMM testN1Emu testN2Emu testN2RunEmu testTPS
+       @-rm -f filterhelper
+       @-rm -f dump.txt
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.c
new file mode 100644 (file)
index 0000000..8a0ec39
--- /dev/null
@@ -0,0 +1,478 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+#include "data.h"
+#include "sc.h"
+#include "scsetup.h"
+#include "opts.h"
+#include "misc.h"
+#include "system.h"
+#include "smartcard.h"
+#include "cam.h"
+#include "log-core.h"
+#include "version.h"
+
+#define LIBSC_PREFIX  "libsc-"
+#define SO_INDICATOR   ".so."
+
+static bool DllLoad(const char *fileName)
+{
+  const char *base=rindex(fileName,'/');
+  if(!base) base=fileName;
+  void *handle=dlopen(fileName,RTLD_NOW|RTLD_LOCAL);
+  if(handle) return true;
+  printf("dload: %s: %s\n",base,dlerror());
+  return false;
+}
+
+bool DllsLoad(const char *libdir)
+{
+  char pat[32];
+  snprintf(pat,sizeof(pat),"%s*-%d%s%s",LIBSC_PREFIX,SCAPIVERS,SO_INDICATOR,APIVERSION);
+  bool res=true;
+  cReadDir dir(libdir);
+  struct dirent *e;
+  while((e=dir.Next())) {
+    if(!fnmatch(pat,e->d_name,FNM_PATHNAME|FNM_NOESCAPE)) {
+      DllLoad(AddDirectory(libdir,e->d_name));
+      }
+    }
+  return res;
+}
+
+void InitAll(const char *cfgdir)
+{
+  logcfg.logCon=1;
+  logcfg.noTimestamp=1;
+  cSystems::ConfigParse("Cardclient.Immediate","0");
+
+  filemaps.SetCfgDir(cfgdir);
+  cStructLoaders::SetCfgDir(cfgdir);
+
+  if(!Feature.KeyFile()) keys.Disable();
+  if(!Feature.SmartCard()) smartcards.Disable();
+  cStructLoaders::Load(false);
+  if(Feature.KeyFile() && keys.Count()<1)
+    PRINTF(L_GEN_ERROR,"no keys loaded for softcam!");
+  if(!cSystems::Init(cfgdir)) exit(2);
+
+#ifdef DEFAULT_PORT
+  smartcards.AddPort(DEFAULT_PORT);
+#endif
+  smartcards.LaunchWatcher();
+}
+
+void LogAll(void)
+{
+  for(int i=0; i<32; i++)
+    cLogging::SetModuleOptions(LCLASS(i,0xFFFFFFFF));
+}
+
+void SDump(const unsigned char *buffer, int n)
+{
+  for(int l=0 ; l<n ; l++) printf("%02x ",buffer[l]);
+  printf("\n");
+}
+
+int ReadRaw(const char *name, unsigned char *buff, int maxlen)
+{
+  FILE *f=fopen(name,"r");
+  if(f) {
+    char line[512];
+    int len=0;
+    while(len<maxlen && fgets(line,sizeof(line),f)) {
+      if(strstr(line,"dump: n=")) continue;
+      char *p=index(line,'#');
+      if(p) *p=0;
+      p=index(line,'[');
+      if(p) {
+        p=index(p,']');
+        if(p) {
+          strcpy(line,p+1);
+          }
+        }
+      p=index(line,':');
+      if(p) p++; else p=line;
+      while(1) {
+        char *np;
+        int val=strtol(p,&np,16);
+        if(p!=np) {
+          buff[len++]=val;
+          p=np;
+          }
+        else break;
+        }
+      }
+    fclose(f);
+//    printf("using raw from %s:\n",name);
+//    HexDump(buff,len);
+    return len;
+    }
+  printf("failed to open raw file %s: %s\n",name,strerror(errno));
+  exit(1);
+}
+
+//
+//
+
+extern const char *I18nTranslate(const char *s, const char *Plugin)
+{
+  return s;
+}
+
+//
+//
+
+static const struct LogModule lm_core = {
+  (LMOD_ENABLE|L_CORE_ALL)&LOPT_MASK,
+  (LMOD_ENABLE|L_CORE_LOAD|L_CORE_ECM|L_CORE_PIDS|L_CORE_AU|L_CORE_AUSTATS|L_CORE_CAIDS|L_CORE_NET|L_CORE_CI|L_CORE_SC|L_CORE_HOOK)&LOPT_MASK,
+  "core",
+  { "load","action","ecm","ecmProc","pids","au","auStats","auExtra","auExtern",
+    "caids","keys","dynamic","csa","ci","av7110","net","netData","msgcache",
+    "serial","smartcard","hook","ciFull" }
+  };
+ADD_MODULE(L_CORE,lm_core)
+
+//
+//
+
+cScSetup ScSetup;
+
+cScSetup::cScSetup(void)
+{
+  AutoUpdate = 1;
+  memset(ScCaps,0,sizeof(ScCaps));
+  ScCaps[0] = 1;
+  ScCaps[1] = 2;
+  ConcurrentFF = 0;
+  memset(CaIgnore,0,sizeof(CaIgnore));
+  LocalPriority = 0;
+  ForceTransfer = 1;
+  PrestartAU = 0;
+}
+
+void cScSetup::Check(void) {}
+bool cScSetup::Ignore(unsigned short caid) { return false; }
+
+//
+//
+
+void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on) {}
+void cSoftCAM::AddHook(int CardNum, cLogHook *hook) {}
+bool cSoftCAM::TriggerHook(int CardNum, int id) { return true; }
+
+//
+//
+
+// --- cOpt --------------------------------------------------------------------
+
+cOpt::cOpt(const char *Name, const char *Title)
+{
+  name=Name; title=Title;
+  fullname=0; persistant=true;
+}
+
+cOpt::~cOpt()
+{
+  free(fullname);
+}
+
+const char *cOpt::FullName(const char *PreStr)
+{
+  if(PreStr) {
+    free(fullname);
+    asprintf(&fullname,"%s.%s",PreStr,name);
+    return fullname;
+    }
+  else return name;
+}
+
+// --- cOptInt -----------------------------------------------------------------
+
+cOptInt::cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max)
+:cOpt(Name,Title)
+{
+  storage=Storage; min=Min; max=Max;
+}
+
+void cOptInt::Parse(const char *Value)
+{
+  *storage=atoi(Value);
+}
+
+void cOptInt::Backup(void)
+{
+}
+
+bool cOptInt::Set(void)
+{
+  if(value!=*storage) { *storage=value; return true; }
+  return false;
+}
+
+void cOptInt::Store(const char *PreStr)
+{
+}
+
+void cOptInt::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptSel -----------------------------------------------------------------
+
+cOptSel::cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings)
+:cOptInt(Name,Title,Storage,0,NumStr)
+{
+  strings=Strings;
+  trStrings=0;
+}
+
+cOptSel::~cOptSel()
+{
+  free(trStrings);
+}
+
+void cOptSel::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptBool -----------------------------------------------------------------
+
+cOptBool::cOptBool(const char *Name, const char *Title, int *Storage)
+:cOptInt(Name,Title,Storage,0,1)
+{}
+
+void cOptBool::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptStr -----------------------------------------------------------------
+
+cOptStr::cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed)
+:cOpt(Name,Title)
+{
+  storage=Storage; size=Size; allowed=Allowed;
+  value=MALLOC(char,size);
+}
+
+cOptStr::~cOptStr()
+{
+  free(value);
+}
+
+void cOptStr::Parse(const char *Value)
+{
+  strn0cpy(storage,Value,size);
+}
+
+void cOptStr::Backup(void)
+{
+}
+
+bool cOptStr::Set(void)
+{
+  if(strcmp(value,storage)) { strn0cpy(storage,value,size); return true; }
+  return false;
+}
+
+void cOptStr::Store(const char *PreStr)
+{
+}
+
+void cOptStr::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptCap -----------------------------------------------------------------
+
+class cOptCap : public cOpt {
+protected:
+  int *storage, *value;
+  int size, disp, len;
+public:
+  cOptCap(const char *Name, const char *Title, int *Storage, int Size, int Disp);
+  virtual ~cOptCap();
+  virtual void Parse(const char *Value);
+  virtual void Backup(void);
+  virtual bool Set(void);
+  virtual void Store(const char *PreStr);
+  virtual void Create(cOsdMenu *menu);
+  };
+
+cOptCap::cOptCap(const char *Name, const char *Title, int *Storage, int Size, int Disp)
+:cOpt(Name,Title)
+{
+  storage=Storage; size=Size; disp=Disp; len=sizeof(int)*size;
+  value=MALLOC(int,size);
+}
+
+cOptCap::~cOptCap()
+{
+  free(value);
+}
+
+void cOptCap::Parse(const char *Value)
+{
+  memset(storage,0,len);
+  int i=0;
+  while(1) {
+    char *p;
+    const int c=strtol(Value,&p,10);
+    if(c<=0 || p==Value || i>=size) return;
+    storage[i++]=c;
+    Value=p;
+    }
+}
+
+void cOptCap::Backup(void)
+{
+}
+
+bool cOptCap::Set(void)
+{
+  if(memcmp(value,storage,len)) { memcpy(storage,value,len); return true; }
+  return false;
+}
+
+void cOptCap::Store(const char *PreStr)
+{
+}
+
+void cOptCap::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOpts -------------------------------------------------------------------
+
+cOpts::cOpts(const char *PreStr, int NumOpts)
+{
+  preStr=PreStr;
+  numOpts=NumOpts; numAdd=0;
+  if((opts=MALLOC(cOpt *,numOpts))) memset(opts,0,sizeof(cOpt *)*numOpts);
+}
+
+cOpts::~cOpts()
+{
+  if(opts) {
+    for(int i=0; i<numOpts; i++) delete opts[i];
+    free(opts);
+    }
+}
+
+void cOpts::Add(cOpt *opt)
+{
+  if(opts && numAdd<numOpts) opts[numAdd++]=opt;
+}
+
+bool cOpts::Parse(const char *Name, const char *Value)
+{
+  if(opts) {
+    for(int i=0; i<numAdd; i++)
+      if(opts[i] && opts[i]->Persistant() && !strcasecmp(Name,opts[i]->Name())) {
+        opts[i]->Parse(Value);
+        return true;
+        }
+    }
+  return false;
+}
+
+bool cOpts::Store(bool AsIs)
+{
+  return false;
+}
+
+void cOpts::Backup(void)
+{
+}
+
+void cOpts::Create(cOsdMenu *menu)
+{
+}
+
+//
+//
+// VDR
+//
+//
+
+#include <vdr/sources.h>
+
+cString cSource::ToString(int Code)
+{
+  char buffer[16];
+  char *q = buffer;
+  switch (Code & st_Mask) {
+    case stCable: *q++ = 'C'; break;
+    case stSat:   *q++ = 'S';
+                  {
+                    int pos = Code & ~st_Mask;
+                    q += snprintf(q, sizeof(buffer) - 2, "%u.%u", (pos & ~st_Neg) / 10, (pos & ~st_Neg) % 10); // can't simply use "%g" here since the silly 'locale' messes up the decimal point
+                    *q++ = (Code & st_Neg) ? 'E' : 'W';
+                  }
+                  break;
+    case stTerr:  *q++ = 'T'; break;
+    default:      *q++ = Code + '0'; // backward compatibility
+    }
+  *q = 0;
+  return buffer;
+}
+
+int cSource::FromString(const char *s)
+{
+  int type = stNone;
+  switch (toupper(*s)) {
+    case 'C': type = stCable; break;
+    case 'S': type = stSat;   break;
+    case 'T': type = stTerr;  break;
+    case '0' ... '9': type = *s - '0'; break; // backward compatibility
+    default: esyslog("ERROR: unknown source key '%c'", *s);
+             return stNone;
+    }
+  int code = type;
+  if (type == stSat) {
+     int pos = 0;
+     bool dot = false;
+     bool neg = false;
+     while (*++s) {
+           switch (toupper(*s)) {
+             case '0' ... '9': pos *= 10;
+                               pos += *s - '0';
+                               break;
+             case '.':         dot = true;
+                               break;
+             case 'E':         neg = true; // fall through to 'W'
+             case 'W':         if (!dot)
+                                  pos *= 10;
+                               break;
+             default: esyslog("ERROR: unknown source character '%c'", *s);
+                      return stNone;
+             }
+           }
+     if (neg)
+        pos |= st_Neg;
+     code |= pos;
+     }
+  return code;
+}
+
+//
+//
+
+#include <vdr/channels.h>
+
+int cChannel::Transponder(int Frequency, char Polarization)
+{
+  // some satellites have transponders at the same frequency, just with different polarization:
+  switch (tolower(Polarization)) {
+    case 'h': Frequency += 100000; break;
+    case 'v': Frequency += 200000; break;
+    case 'l': Frequency += 300000; break;
+    case 'r': Frequency += 400000; break;
+    }
+  return Frequency;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/compat.h
new file mode 100644 (file)
index 0000000..f5b2b3f
--- /dev/null
@@ -0,0 +1,6 @@
+
+bool DllsLoad(const char *libdir);
+void InitAll(const char *cfgdir);
+void LogAll(void);
+void SDump(const unsigned char *buffer, int n);
+int ReadRaw(const char *name, unsigned char *buff, int maxlen);
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/filterhelper.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/filterhelper.c
new file mode 100644 (file)
index 0000000..bdbd8e8
--- /dev/null
@@ -0,0 +1,117 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+  unsigned char match[256];
+  int i, max;
+  int filt, mask, mode;
+  int mam, manm;
+
+  if(argc<2) {
+    printf("missing operation mode\n");
+    exit(1);
+    }
+  if(!strcasecmp(argv[1],"MATCH")) {
+    if(argc<3) {
+      printf("no matches given\n");
+      exit(1);
+      }
+    memset(match,0,sizeof(match));
+    printf("searching filter settings for:");
+    for(i=2; i<argc; i++) {
+      int l=strtol(argv[i],0,0);
+      if(l<0 || l>255) {
+        printf("allowed range 0-255 / 0x00-0xFF\n");
+        exit(1);
+        }
+      match[l]=1;
+      printf(" 0x%02x",l);
+      }
+    printf("\n");
+
+    max=5;
+    for(mode=0; mode<256; mode++) {
+      for(mask=0; mask<256; mask++) {
+        mam =mask &  (~mode);
+        manm=mask & ~(~mode);
+        for(filt=0; filt<256; filt++) {
+          int ok=1, miss=0;
+          unsigned char mm[256];
+          memset(mm,0,sizeof(mm));
+          for(i=0; i<256; i++) {
+            int xxor=filt^i;
+            if((mam&xxor) || (manm && !(manm&xxor))) {
+              if(match[i]!=0) {
+                ok=0;
+                miss=0;
+                break;
+                }
+              }
+            else {
+              if(match[i]!=1) {
+                ok=0;
+                miss++; mm[i]=1;
+                if(miss>max) break;
+                }
+              }
+            }
+
+          if(ok) {
+            printf("found exact settings filt=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+            exit(0);
+            }
+          else if(miss>0 && miss<=max) {
+            printf("mismatching");
+            for(i=0; i<256; i++) if(mm[i]) printf(" %02x",i);
+            printf(" - settings filt=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+            max=miss;
+            }
+          }
+        }
+      }
+    printf("no exact settings found\n");
+    exit(1);
+    }
+  else if(!strcasecmp(argv[1],"TEST")) {
+    if(argc<5) {
+      printf("missing arguments\n");
+      exit(1);
+      }
+    filt=strtol(argv[2],0,0);
+    if(filt<0 || filt>255) {
+      printf("filter range 0-255 exceeded\n");
+      exit(1);
+      }
+    mask=strtol(argv[3],0,0);
+    if(mask<0 || mask>255) {
+      printf("mask range 0-255 exceeded\n");
+      exit(1);
+      }
+    mode=strtol(argv[4],0,0);
+    if(mode<0 || mode>255) {
+      printf("mode range 0-255 exceeded\n");
+      exit(1);
+      }
+    printf("settings file=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+
+    mam =mask &  (~mode);
+    manm=mask & ~(~mode);
+    printf("matching:");
+    for(i=0; i<256; i++) {
+      int xxor=filt^i;
+      if((mam&xxor) || (manm && !(manm&xxor))) {}
+      else
+        printf(" %02x",i);
+      }
+    printf("\n");
+    }
+  else {
+    printf("unknown operation mode\n");
+    }
+  exit(1);
+}
+
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testECM.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testECM.c
new file mode 100644 (file)
index 0000000..2d61379
--- /dev/null
@@ -0,0 +1,43 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "data.h"
+#include "system.h"
+#include "log.h"
+#include "compat.h"
+#include <vdr/sources.h>
+
+int main(int argc, char *argv[])
+{
+  if(argc<6) {
+    printf("usage: %s <lib-dir> <plugin-dir> <caid> <provid> <source> <ecm-dump>\n",argv[0]);
+    return 1;
+    }
+  DllsLoad(argv[1]);
+  InitAll(argv[2]);
+  LogAll();
+  cLogging::SetModuleOption(LCLASS(7,0x20<<2),false); // Nagra L_SYS_DISASM
+  cLogging::SetModuleOption(LCLASS(7,0x20<<4),false); // Nagra L_SYS_CPUSTATS
+  unsigned char ecm[4096];
+  ReadRaw(argv[6],ecm,sizeof(ecm));
+    
+  int caid=strtol(argv[3],0,0);
+  int provid=strtol(argv[4],0,0);
+  printf("using caid %04x provid %04x\n",caid,provid);
+  cEcmInfo ecmD("dummy",0x123,caid,provid);
+  ecmD.SetSource(10,cSource::FromString(argv[5]),120);
+  cSystem *sys=0;
+  int lastPri=0;
+  while((sys=cSystems::FindBySysId(caid,false,lastPri))) {
+    lastPri=sys->Pri();
+    printf("processing with module '%s'\n",sys->Name());
+    bool res=sys->ProcessECM(&ecmD,ecm);
+    if(res) {
+      printf("resulting CW: ");
+      SDump(sys->CW(),16);
+      }
+    delete sys;
+    if(res) break;
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testEMM.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testEMM.c
new file mode 100644 (file)
index 0000000..5c59c98
--- /dev/null
@@ -0,0 +1,37 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "data.h"
+#include "system.h"
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+  if(argc<4) {
+    printf("usage: %s <lib-dir> <plugin-dir> <caid> <rawemm-file>\n",argv[0]);
+    return 1;
+    }
+  DllsLoad(argv[1]);
+  InitAll(argv[2]);
+  LogAll();
+  unsigned char emm[4096];
+  int len=ReadRaw(argv[4],emm,sizeof(emm));
+    
+  int caid=strtol(argv[3],0,0);
+  printf("using caid %04x\n",caid);
+  cSystem *sys=0;
+  int lastPri=0;
+  while((sys=cSystems::FindBySysId(caid,false,lastPri))) {
+    sys->DoLog(true); sys->CardNum(0);
+    lastPri=sys->Pri();
+    if(sys->HasLogger()) {
+      for(int i=0; i<len;) {
+        int s=SCT_LEN(&emm[i]);
+        sys->ProcessEMM(0x123,caid,&emm[i]);
+        i+=s;
+        }
+      }
+    delete sys;
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN1Emu.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN1Emu.c
new file mode 100644 (file)
index 0000000..32eb11f
--- /dev/null
@@ -0,0 +1,138 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define TESTER
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/cpu.c"
+#include "systems/nagra/nagra.c"
+#include "systems/nagra/nagra1.c"
+
+#include "compat.h"
+
+class cTestNagra : public cNagra1 {
+public:
+  virtual void Process(int id, unsigned char *emmdata);
+  };
+
+void cTestNagra::Process(int id, unsigned char *emmdata)
+{
+  cEmu *emu=0;
+  cBN n1, e1;
+
+  unsigned int i=0;
+  bool gotKeys=false;
+  unsigned char key0[8], key1[8];
+  int keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+  switch(emmdata[i]) { // Check filter type
+    case 0x00: // One card
+      i+=7; break;
+    case 0x01 ... 0x20: // Group of cards
+      i+=emmdata[i]+0x7; break;
+    case 0x3e:
+      i+=6; break;
+    case 0x3d: // All cards with same system ID
+    case 0x3f:
+      i+=3; break;
+    }
+  int nrKeys=0;
+  while(i<sizeof(emmdata)) {
+    if((emmdata[i]&0xF0)==0xF0) { // Update with CPU code
+      const int romNr=emmdata[i]&0x0F;
+      if(!emu || !emu->Matches(romNr,id)) {
+        delete emu; emu=0;
+        printf("Testing with ROM %d, 0xID %04x\n",romNr,id);
+        switch(romNr) {
+          case 3:  emu=new cEmuRom3;  break;
+          case 7:  emu=new cEmuRom7;  break;
+          case 10: emu=new cEmuRom10; break;
+          case 11: emu=new cEmuRom11; break;
+          default: printf("unsupported ROM"); return;
+          }
+        if(!emu || !emu->Init(romNr,id)) {
+          delete emu; emu=0;
+          printf("initialization failed for ROM %d\n",romNr);
+          return;
+          }
+        }
+      unsigned char ki[2];
+      if((gotKeys=emu->GetOpKeys(emmdata,ki,key0,key1))) {
+        printf("keyid: "); SDump(ki,2);
+        printf("key0: "); SDump(key0,8);
+        printf("key1: "); SDump(key1,8);
+        keyId=(ki[0]<<8)+ki[1];
+        printf("got keys for %04X (ROM %d)\n",keyId,romNr);
+        }
+      unsigned char select[3], pkset[3][15];
+      select[0]=(keyId>>8)|0x01; // always high id for ECM RSA keys
+      select[1]=keyId&0xFF;
+      select[2]=0; // type 0
+HexDump(select,3);
+      if(emu->GetPkKeys(&select[0],&pkset[0][0])) {
+        int pkKeyId=((select[0]<<8)+select[1]);
+        printf("got PK keys for %04X (ROM %d)\n",pkKeyId,romNr);
+HexDump(pkset[0],sizeof(pkset[0]));
+HexDump(pkset[1],sizeof(pkset[1]));
+HexDump(pkset[2],sizeof(pkset[2]));
+        for(int i=0; i<3; i++) {
+          CreateRSAPair(pkset[i],0,e1,n1);
+          printf("keyE1 set=%d ",i); bprint(e1);
+          printf("keyN1 set=%d ",i); bprint(n1);
+          }
+        }
+      break; // don't process other nanos
+      }
+   else if(emmdata[i]==0x60) { // NULL nano
+      i+=2;
+      }
+    else if(emmdata[i]==0x00) {
+      i++;
+      }
+    else if(emmdata[i]==0x81) {
+      i++;
+      }
+    else if(emmdata[i]==0x83) {
+      keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+      i+=3;
+      }
+    else if(emmdata[i]==0x42) { // plain Key
+      if(emmdata[i+1]==0x05) memcpy(key0,&emmdata[i+2],sizeof(key0));
+      else                   memcpy(key1,&emmdata[i+2],sizeof(key1));
+      i+=10;
+      if(++nrKeys==2) {
+        gotKeys=true;
+        printf("got keys for %04X (plain)\n",keyId);
+        break;
+        }
+      }
+    else {
+      printf("ignored nano %02x\n",emmdata[i]);
+      break;
+      }
+    }
+
+  delete emu;
+}
+
+
+int main(int argc, char *argv[])
+{
+  if(argc<3) {
+    printf("usage: %s <plugin-dir> <id> <rawemm-file>\n",argv[0]);
+    return 1;
+    }
+
+  InitAll(argv[1]);
+  unsigned char emmdata[64];
+  int emmlen=ReadRaw(argv[3],emmdata,sizeof(emmdata));
+  if(emmlen!=64) {
+    printf("bad rawemm file format\n");
+    return 1;
+    }
+  const int id=strtol(argv[2],0,0);
+  cTestNagra test;
+  test.Process(id,emmdata);
+  return 0;
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2Emu.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2Emu.c
new file mode 100644 (file)
index 0000000..c2ea053
--- /dev/null
@@ -0,0 +1,212 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/nagra2.h"
+
+#include "compat.h"
+
+static const unsigned char key3des[16] = {  };
+
+static cN2Prov *emmP=0;
+
+void Emm(unsigned char *emmdata, int cmdLen, int id)
+{
+  if(!emmP || (emmP && !emmP->CanHandle(id))) {
+    delete emmP;
+    emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+    if(emmP) emmP->PrintCaps(L_SYS_EMM);
+    }
+
+  HEXDUMP(L_SYS_RAWEMM,emmdata,cmdLen,"Nagra2 RAWEMM");
+  id=(emmdata[8]<<8)+emmdata[9];
+  LBSTARTF(L_SYS_EMM);
+  bool contFail=false;
+  for(int i=8+2+4+4; i<cmdLen-22; ) {
+    switch(emmdata[i]) {
+      case 0x42: // plain Key update
+        if((((emmdata[i+3]|0xF3)+1)&0xFF) != 0) {
+          int len=emmdata[i+2];
+          int off=emmdata[i+5];
+          int ulen=emmdata[i+6];
+          if(len>0 && ulen>0 && off+ulen<=len) {
+            int ks=emmdata[i+3], kn;
+            if(ks==0x06 || ks==0x46) kn=(ks>>6)&1; else kn=MBC(N2_MAGIC,ks);
+            unsigned char key[256];
+            memset(key,0,sizeof(key));
+            bool ok=false;
+            if((emmdata[i+1]&0x7F)==0) ok=true;
+            else {
+              if(emmP && emmP->HasFlags(N2FLAG_POSTAU)) {
+                if(emmP->PostProcAU(id,&emmdata[i])) ok=true;
+                }
+              else PRINTF(L_SYS_EMM,"POSTAU for provider %04x not supported",id);
+              }
+            if(ok) {
+              printf("key %x: off=%x ulen=%x ",kn,off,ulen);
+              SDump(&emmdata[i+7],ulen);
+              }
+            i+=ulen;
+            }
+          else PRINTF(L_SYS_EMM,"nano42 key size mismatch len=%d off=%d ulen=%d",len,off,ulen);
+          }
+        else PRINTF(L_SYS_EMM,"nano42 0xf3 status exceeded");
+        i+=7;
+        break;
+      case 0xE0: // DN key update
+        if(emmdata[i+1]==0x25) {
+          printf("key%02x: ",(emmdata[i+16]&0x40)>>6);
+          SDump(&emmdata[i+23],16);
+          }
+        i+=emmdata[i+1]+2;
+        break;
+      case 0x83: // change data prov. id
+        id=(emmdata[i+1]<<8)|emmdata[i+2];
+        i+=3;
+        break;
+      case 0xB0: case 0xB1: case 0xB2: case 0xB3: // Update with ROM CPU code
+      case 0xB4: case 0xB5: case 0xB6: case 0xB7:
+      case 0xB8: case 0xB9: case 0xBA: case 0xBB:
+      case 0xBC: case 0xBD: case 0xBE: case 0xBF:
+        {
+        int bx=emmdata[i]&15;
+        if(!emmP || !emmP->HasFlags(N2FLAG_Bx)) {
+          PRINTF(L_SYS_EMM,"B%X for provider %04x not supported",bx,id);
+          i=cmdLen;
+          break;
+          }
+        int r;
+        if((r=emmP->ProcessBx(emmdata,cmdLen,i+1))>0)
+          i+=r;
+        else {
+          PRINTF(L_SYS_EMM,"B%X executing failed for %04x",bx,id);
+          i=cmdLen;
+          }
+        break;
+        }
+      case 0xA4: i+=emmdata[i+1]+2+4; break;   // conditional (always no match assumed for now)
+      case 0xA6: i+=15; break;
+      case 0xAA: i+=emmdata[i+1]+5; break;
+      case 0xAD: i+=emmdata[i+1]+2; break;
+      case 0xA2:
+      case 0xAE: i+=11;break;
+      case 0x12: i+=emmdata[i+1]+2; break;      // create tier
+      case 0x20: i+=19; break;                  // modify tier
+      case 0x9F: i+=6; break;
+      case 0xE3:                                // Eeprom update
+        {
+        int ex=emmdata[i]&15;
+        if(!emmP || !emmP->HasFlags(N2FLAG_Ex)) {
+          i+=emmdata[i+4]+5;
+          PRINTF(L_SYS_EMM,"E%X for provider %04x not supported",ex,id);
+          break;
+          }
+        int r;
+        if((r=emmP->ProcessEx(emmdata,cmdLen,i+1))>0)
+          i+=r;
+        else {
+          PRINTF(L_SYS_EMM,"E%X executing failed for %04x",ex,id);
+          i=cmdLen;
+          }
+        break;
+        }
+      case 0xE1:
+      case 0xE2:
+      case 0x00: i=cmdLen; break;              // end of processing
+      default:
+        if(!contFail) LBPUT("unknown EMM nano");
+        LBPUT(" %02x",emmdata[i]);
+        contFail=true;
+        i++;
+        continue;
+      }
+    LBFLUSH(); contFail=false;
+    }
+  LBEND();
+}
+
+bool Ecm(unsigned char *buff, int cmdLen, int id)
+{
+  unsigned char cw[16];
+
+  cN2Prov *ecmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+  if(ecmP) ecmP->PrintCaps(L_SYS_ECM);
+
+  int l=0, mecmAlgo=0;
+  for(int i=(buff[14]&0x10)?16:20; i<cmdLen-10 && l!=3; ) {
+printf("%02x: nano %02x\n",i,buff[i]);
+    switch(buff[i]) {
+      case 0x10:
+      case 0x11:
+        if(buff[i+1]==0x09) {
+          int s=(~buff[i])&1;
+          mecmAlgo=buff[i+2]&0x60;
+          memcpy(cw+(s<<3),&buff[i+3],8);
+          i+=11; l|=(s+1);
+          }
+        else {
+          printf("bad length %d in CW nano %02x\n",buff[i+1],buff[i]);
+          i++;
+          }
+        break;
+      case 0x00:
+        i+=2; break;
+      case 0x13 ... 0x15:
+        i+=4; break;
+      case 0x30 ... 0x36:
+      case 0xB0:
+        i+=buff[i+1]+2;
+        break;
+      default:
+        i++;
+        continue;
+      }
+    }
+  if(l!=3) return false;
+  if(mecmAlgo>0) {
+    if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+static unsigned char odata[15] = { 0x80,0x30,0x47,0x07,0x45,0xec,0xc1,0xee,0xc5,0x53,0x91,0x2f,0x70,0x34,0x34 };
+      if(!ecmP->MECM(buff[15],mecmAlgo,odata,cw)) return false;
+      }
+    else { printf("MECM for provider %04x not supported\n",id); return false; }
+    }
+  if(ecmP) ecmP->SwapCW(cw);
+  printf("resulting CW: "); SDump(cw,16);
+  return true;
+}
+
+int main(int argc, char *argv[])
+{
+  if(argc<5) {
+    printf("usage: %s <plugin-dir> <id> <ECM/EMM> <raw-file> [<raw-file> [..]]\n",argv[0]);
+    return 1;
+    }
+
+  int mode;
+  if(!strcasecmp(argv[3],"ECM")) mode=1;
+  else if(!strcasecmp(argv[3],"EMM")) mode=2;
+  else {
+    printf("bad mode '%s'\n",argv[3]);
+    return 1;
+    }
+  
+  InitAll(argv[1]);
+  LogAll();
+  cLogging::SetModuleOption(LCLASS(L_SYS,L_SYS_DISASM),false);
+
+  int id=strtol(argv[2],0,0);
+  int dlen=(mode==1) ? 64 : 96;
+  unsigned char data[4096];
+  for(int pos=4; pos<argc; pos++) {
+    int len=ReadRaw(argv[pos],data,sizeof(data));
+    if(len%dlen != 0) { printf("bad raw file format\n"); exit(1); }
+
+    for(int alen=0; alen<len; alen+=dlen) {
+      if(mode==1) Ecm(&data[alen],dlen,id);
+      else if(mode==2) Emm(&data[alen],dlen,id);
+      }
+    }
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2RunEmu.c b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/testing/testN2RunEmu.c
new file mode 100644 (file)
index 0000000..b77c232
--- /dev/null
@@ -0,0 +1,30 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/nagra2.h"
+
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+  if(argc<4) {
+    printf("usage: %s <plugin-dir> <id> <cmdfile> [LOG]\n",argv[0]);
+    return 1;
+    }
+
+  InitAll(argv[1]);
+  LogAll();
+  if(argc<=4 || strcasecmp(argv[4],"LOG"))
+    cLogging::SetModuleOption(LCLASS(L_SYS,L_SYS_DISASM),false);
+  unsigned char data[4096];
+  unsigned int len=ReadRaw(argv[3],data,sizeof(data));
+  int id=strtol(argv[2],0,0);
+  cN2Prov *emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+  HEXDUMP(L_SYS_EMU,data,len,"Input");
+  if(emmP->RunEmu(data,len,0x90,0x90,0x00,0x00,0x460)>=0)
+    HEXDUMP(L_SYS_EMU,data,0x460,"Output");
+}
diff --git a/contrib/sasc-ng/sc/PLUGINS/src/sc-src/version.h b/contrib/sasc-ng/sc/PLUGINS/src/sc-src/version.h
new file mode 100644 (file)
index 0000000..569c4ff
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#ifndef ___VERSION_H
+#define ___VERSION_H
+
+#define SC_RELEASE "0.9.0"
+
+extern const char *ScVersion;
+
+#endif
diff --git a/contrib/sasc-ng/sc/device.cpp b/contrib/sasc-ng/sc/device.cpp
new file mode 100644 (file)
index 0000000..f63046e
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * device.c: The basic device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: device.c 1.56 2004/06/19 08:51:05 kls Exp $
+ */
+
+#include "include/vdr/device.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include "include/vdr/channels.h"
+#include "include/vdr/i18n.h"
+
+// --- cDevice ---------------------------------------------------------------
+
+// The default priority for non-primary devices:
+#define DEFAULTPRIORITY  -1
+
+int cDevice::numDevices = 0;
+int cDevice::useDevice = 0;
+int cDevice::nextCardIndex = 0;
+int cDevice::currentChannel = 1;
+cDevice *cDevice::device[MAXDEVICES] = { NULL };
+cDevice *cDevice::primaryDevice = NULL;
+
+cDevice::cDevice(void)
+{
+  cardIndex = nextCardIndex++;
+
+  SetDescription("receiver on device %d", CardIndex() + 1);
+
+  mute = false;
+
+  sectionHandler = NULL;
+  eitFilter = NULL;
+  patFilter = NULL;
+  sdtFilter = NULL;
+  nitFilter = NULL;
+
+  player = NULL;
+
+  for (int i = 0; i < MAXRECEIVERS; i++)
+      receiver[i] = NULL;
+
+  if (numDevices < MAXDEVICES)
+     device[numDevices++] = this;
+  else
+     esyslog("ERROR: too many devices!");
+}
+
+cDevice::~cDevice()
+{
+}
+
+void cDevice::SetUseDevice(int n)
+{
+  if (n < MAXDEVICES)
+     useDevice |= (1 << n);
+}
+
+int cDevice::NextCardIndex(int n)
+{
+  if (n > 0) {
+     nextCardIndex += n;
+     if (nextCardIndex >= MAXDEVICES)
+        esyslog("ERROR: nextCardIndex too big (%d)", nextCardIndex);
+     }
+  else if (n < 0)
+     esyslog("ERROR: illegal value in IncCardIndex(%d)", n);
+  return nextCardIndex;
+}
+
+int cDevice::DeviceNumber(void) const
+{
+  for (int i = 0; i < numDevices; i++) {
+      if (device[i] == this)
+         return i;
+      }
+  return -1;
+}
+
+void cDevice::MakePrimaryDevice(bool On)
+{
+}
+
+bool cDevice::SetPrimaryDevice(int n)
+{
+  n--;
+  if (0 <= n && n < numDevices && device[n]) {
+     isyslog("setting primary device to %d", n + 1);
+     if (primaryDevice)
+        primaryDevice->MakePrimaryDevice(false);
+     primaryDevice = device[n];
+     primaryDevice->MakePrimaryDevice(true);
+     return true;
+     }
+  esyslog("ERROR: invalid primary device number: %d", n + 1);
+  return false;
+}
+
+bool cDevice::HasDecoder(void) const
+{
+  return false;
+}
+
+cSpuDecoder *cDevice::GetSpuDecoder(void)
+{
+  return NULL;
+}
+
+cDevice *cDevice::ActualDevice(void)
+{
+  return NULL;
+}
+
+cDevice *cDevice::GetDevice(int Index)
+{
+  int i;
+  for (i = 0; i < numDevices; i++)
+    if (device[i]->CardIndex() == Index)
+      return device[i];
+  return NULL;
+}
+
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView)
+{
+  return NULL;
+}
+
+bool cDevice::HasCi(void)
+{
+  return false;
+}
+
+void cDevice::SetCamSlot(cCamSlot *CamSlot)
+{
+}
+
+void cDevice::Shutdown(void)
+{
+}
+
+uchar *cDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
+{
+  return false;
+}
+
+void cDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
+{
+}
+
+void cDevice::SetVideoFormat(bool VideoFormat16_9)
+{
+}
+
+eVideoSystem cDevice::GetVideoSystem(void)
+{
+  return vsNTSC;
+}
+
+#define PRINTPIDS(s)
+
+bool cDevice::HasPid(int Pid) const
+{
+  for (int i = 0; i < MAXPIDHANDLES; i++) {
+      if (pidHandles[i].pid == Pid)
+         return true;
+      }
+  return false;
+}
+
+bool cDevice::AddPid(int Pid, ePidType PidType)
+{
+  if (Pid || PidType == ptPcr) {
+     int n = -1;
+     int a = -1;
+     if (PidType != ptPcr) { // PPID always has to be explicit
+        for (int i = 0; i < MAXPIDHANDLES; i++) {
+            if (i != ptPcr) {
+               if (pidHandles[i].pid == Pid)
+                  n = i;
+               else if (a < 0 && i >= ptOther && !pidHandles[i].used)
+                  a = i;
+               }
+            }
+        }
+     if (n >= 0) {
+        // The Pid is already in use
+        if (++pidHandles[n].used == 2 && n <= ptTeletext) {
+           // It's a special PID that may have to be switched into "tap" mode
+           PRINTPIDS("A");
+           return SetPid(&pidHandles[n], n, true);
+           }
+        PRINTPIDS("a");
+        return true;
+        }
+     else if (PidType < ptOther) {
+        // The Pid is not yet in use and it is a special one
+        n = PidType;
+        }
+     else if (a >= 0) {
+        // The Pid is not yet in use and we have a free slot
+        n = a;
+        }
+     else
+        esyslog("ERROR: no free slot for PID %d", Pid);
+     if (n >= 0) {
+        pidHandles[n].pid = Pid;
+        pidHandles[n].used = 1;
+        PRINTPIDS("C");
+        return SetPid(&pidHandles[n], n, true);
+        }
+     }
+  return true;
+}
+
+void cDevice::DelPid(int Pid, ePidType PidType)
+{
+  if (Pid || PidType == ptPcr) {
+     int n = -1;
+     if (PidType == ptPcr)
+        n = PidType; // PPID always has to be explicit
+     else {
+        for (int i = 0; i < MAXPIDHANDLES; i++) {
+            if (pidHandles[i].pid == Pid) {
+               n = i;
+               break;
+               }
+            }
+        }
+     if (n >= 0 && pidHandles[n].used) {
+        PRINTPIDS("D");
+        if (--pidHandles[n].used < 2) {
+           SetPid(&pidHandles[n], n, false);
+           if (pidHandles[n].used == 0) {
+              pidHandles[n].handle = -1;
+              pidHandles[n].pid = 0;
+              }
+           }
+        PRINTPIDS("E");
+        }
+     }
+}
+
+bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
+{
+  return false;
+}
+
+void cDevice::StartSectionHandler(void)
+{
+}
+
+int cDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
+{
+  return -1;
+}
+
+void cDevice::AttachFilter(cFilter *Filter)
+{
+}
+
+void cDevice::Detach(cFilter *Filter)
+{
+}
+
+bool cDevice::ProvidesSource(int Source) const
+{
+  return false;
+}
+
+bool cDevice::ProvidesTransponder(const cChannel *Channel) const
+{
+  return false;
+}
+
+bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
+{
+  for (int i = 0; i < numDevices; i++) {
+      if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
+         return false;
+      }
+  return true;
+}
+
+bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+{
+  return false;
+}
+
+bool cDevice::IsTunedToTransponder(const cChannel *Channel)
+{
+  return false;
+}
+
+bool cDevice::MaySwitchTransponder(void)
+{
+  return !Receiving(true) && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);
+}
+
+bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
+{
+  return false;
+}
+
+bool cDevice::SwitchChannel(int Direction)
+{
+  return false;
+}
+
+eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+{
+  return scrOk;
+}
+
+bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
+{
+  return false;
+}
+
+bool cDevice::HasLock(int TimeoutMs)
+{
+  return true;
+}
+
+bool cDevice::HasProgramme(void)
+{
+  return false;
+}
+
+int cDevice::GetAudioChannelDevice(void)
+{
+  return 0;
+}
+
+void cDevice::SetAudioChannelDevice(int AudioChannel)
+{
+}
+
+void cDevice::SetVolumeDevice(int Volume)
+{
+}
+
+void cDevice::SetDigitalAudioDevice(bool On)
+{
+}
+
+void cDevice::SetAudioTrackDevice(eTrackType Type)
+{
+}
+
+bool cDevice::ToggleMute(void)
+{
+  return true;
+}
+
+void cDevice::SetVolume(int Volume, bool Absolute)
+{
+}
+
+int cDevice::NumAudioTracks(void) const
+{
+  return 0;
+}
+
+
+bool cDevice::CanReplay(void) const
+{
+  return HasDecoder();
+}
+
+bool cDevice::SetPlayMode(ePlayMode PlayMode)
+{
+  return false;
+}
+
+int64_t cDevice::GetSTC(void)
+{
+  return -1;
+}
+
+void cDevice::TrickSpeed(int Speed)
+{
+}
+
+void cDevice::Clear(void)
+{
+}
+
+void cDevice::Play(void)
+{
+}
+
+void cDevice::Freeze(void)
+{
+}
+
+void cDevice::Mute(void)
+{
+}
+
+void cDevice::StillPicture(const uchar *Data, int Length)
+{
+}
+
+bool cDevice::Replaying(void) const
+{
+  return player != NULL;
+}
+
+bool cDevice::AttachPlayer(cPlayer *Player)
+{
+  return false;
+}
+
+void cDevice::Detach(cPlayer *Player)
+{
+}
+
+void cDevice::StopReplay(void)
+{
+}
+
+bool cDevice::Poll(cPoller &Poller, int TimeoutMs)
+{
+  return false;
+}
+
+bool cDevice::Flush(int TimeoutMs)
+{
+  return true;
+}
+
+int cDevice::PlayVideo(const uchar *Data, int Length)
+{
+  return -1;
+}
+
+int cDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
+{
+  return -1;
+}
+
+int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
+{
+  return Length;
+}
+
+int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
+{
+  return Length;
+}
+
+int cDevice::Priority(void) const
+{
+  return 0;
+}
+
+bool cDevice::Ready(void)
+{
+  return true;
+}
+
+bool cDevice::Receiving(bool CheckAny) const
+{
+  return false;
+}
+
+void cDevice::Action(void)
+{
+}
+
+bool cDevice::OpenDvr(void)
+{
+  return false;
+}
+
+void cDevice::CloseDvr(void)
+{
+}
+
+bool cDevice::GetTSPacket(uchar *&Data)
+{
+  return false;
+}
+
+bool cDevice::AttachReceiver(cReceiver *Receiver)
+{
+  return false;
+}
+
+void cDevice::Detach(cReceiver *Receiver)
+{
+}
+
+void cDevice::DetachAllReceivers(void)
+{
+}
+// --- cTSBuffer -------------------------------------------------------------
+cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
+{
+}
+
+cTSBuffer::~cTSBuffer()
+{
+}
+
+void cTSBuffer::Action(void)
+{
+}
+
+uchar *cTSBuffer::Get(void)
+{
+  return NULL;
+}
+
diff --git a/contrib/sasc-ng/sc/dload.cpp b/contrib/sasc-ng/sc/dload.cpp
new file mode 100644 (file)
index 0000000..2cc96e2
--- /dev/null
@@ -0,0 +1,24 @@
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+       /* dl*() stub routines for static compilation.  Prepared from
+          /usr/include/dlfcn.h by Hal Pomeranz <hal@deer-run.com> */
+
+       void *dlopen(const char *str, int x) {return (void *)1;}
+       void *dlsym(void *ptr, const char *str) {return NULL;}
+       int dlclose(void *ptr) {return 0;}
+       char *dlerror() {return NULL;}
+       void *dlmopen(Lmid_t a, const char *str, int x) {return NULL;}
+       int dladdr(void *ptr1, Dl_info *ptr2) {return 0;}
+       int dldump(const char *str1, const char *str2, int x) {return 0;}
+       int dlinfo(void *ptr1, int x, void *ptr2) {return 0;}
+
+       void *_dlopen(const char *str, int x) {return NULL;}
+       void *_dlsym(void *ptr, const char *str) {return NULL;}
+       int _dlclose(void *ptr) {return 0;}
+       char *_dlerror() {return NULL;}
+       void *_dlmopen(Lmid_t a, const char *str, int x) {return NULL;}
+       int _dladdr(void *ptr1, Dl_info *ptr2) {return 0;}
+       int _dldump(const char *str1, const char *str2, int x) {return 0;}
+       int _dlinfo(void *ptr1, int x, void *ptr2) {return 0;}
diff --git a/contrib/sasc-ng/sc/dvbdevice.cpp b/contrib/sasc-ng/sc/dvbdevice.cpp
new file mode 100644 (file)
index 0000000..2232d29
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * dvbdevice.c: The DVB device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbdevice.c 1.93 2004/06/19 09:33:42 kls Exp $
+ */
+
+#include "include/vdr/dvbdevice.h"
+#include <errno.h>
+#include <limits.h>
+#include <linux/videodev.h>
+#include <linux/dvb/audio.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/video.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
+#define DO_MULTIPLE_RECORDINGS 1
+//#define WAIT_FOR_LOCK_AFTER_TUNING 1
+
+#define DEV_VIDEO         "/dev/video"
+#define DEV_DVB_ADAPTER   "/dev/dvb/adapter"
+#define DEV_DVB_OSD       "osd"
+#define DEV_DVB_FRONTEND  "frontend"
+#define DEV_DVB_DVR       "dvr"
+#define DEV_DVB_DEMUX     "demux"
+#define DEV_DVB_VIDEO     "video"
+#define DEV_DVB_AUDIO     "audio"
+#define DEV_DVB_CA        "ca"
+
+// --- cDvbDevice ------------------------------------------------------------
+
+int cDvbDevice::devVideoOffset = -1;
+int DvbOpen(const char *Name, int n, int Mode, bool ReportError=false);
+cDvbDevice::cDvbDevice(int n)
+{
+    //int camfd;
+    fd_video = 0;
+    cardIndex=n;
+    //For sasc-ng, we never use the ca device here!
+    //camfd=DvbOpen("ca",cardIndex,O_RDWR|O_NONBLOCK);
+    //if (camfd > 0)
+    //{
+    //    fd_video = 1;
+    //    close(camfd);
+    //}
+    //else
+    //    fd_video = 0;
+    //printf("HW Decoder: %s\n", fd_video ? "Yes" : "No");
+}
+
+cDvbDevice::~cDvbDevice()
+{
+}
+
+bool cDvbDevice::Probe(const char *FileName)
+{
+  return false;
+}
+
+bool cDvbDevice::Initialize(void)
+{
+  return true;
+}
+
+void cDvbDevice::MakePrimaryDevice(bool On)
+{
+}
+
+bool cDvbDevice::HasDecoder(void) const
+{
+  return fd_video;
+}
+
+#include "include/vdr/plugin.h"
+#define SC_NAME "sc"
+#define SC_MAGIC { 0,'S','C',0xc4,0x5e,0xa1 }
+#define OP_PROVIDES 0
+#define OP_IGNORE   1
+
+struct ScLink {
+  char magic[6];
+  short op;
+  const cDevice *dev;
+  unsigned short *caids;
+  const cChannel *channel;
+  int num;
+  };
+
+cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
+{
+  return NULL;
+}
+
+void cDvbDevice::SetVideoFormat(bool VideoFormat16_9)
+{
+}
+
+eVideoSystem cDvbDevice::GetVideoSystem(void)
+{
+  return vsNTSC;
+}
+
+//                            ptAudio        ptVideo        ptPcr        ptTeletext        ptDolby        ptOther
+dmx_pes_type_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_PCR, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
+
+bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
+{
+  return true;
+}
+
+int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
+{
+  return -1;
+}
+
+bool cDvbDevice::ProvidesSource(int Source) const
+{
+  return true;
+}
+
+bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
+{
+  return false;
+}
+
+bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+{
+  bool result = false;
+  return result;
+}
+
+bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
+{
+  return true;
+}
+
+bool cDvbDevice::HasLock(int TimeoutMs)
+{
+  return true;
+}
+
+void cDvbDevice::SetVolumeDevice(int Volume)
+{
+}
+
+void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
+{
+}
+
+bool cDvbDevice::CanReplay(void) const
+{
+  return false;
+}
+
+bool cDvbDevice::SetPlayMode(ePlayMode PlayMode)
+{
+  return true;
+}
+
+int64_t cDvbDevice::GetSTC(void)
+{
+  return -1;
+}
+
+void cDvbDevice::TrickSpeed(int Speed)
+{
+}
+
+void cDvbDevice::Clear(void)
+{
+}
+
+void cDvbDevice::Play(void)
+{
+}
+
+void cDvbDevice::Freeze(void)
+{
+}
+
+void cDvbDevice::Mute(void)
+{
+}
+
+void cDvbDevice::StillPicture(const uchar *Data, int Length)
+{
+}
+
+bool cDvbDevice::Poll(cPoller &Poller, int TimeoutMs)
+{
+  return false;
+}
+
+bool cDvbDevice::Flush(int TimeoutMs)
+{
+  return true;
+}
+
+int cDvbDevice::PlayVideo(const uchar *Data, int Length)
+{
+  return -1;
+}
+
+int cDvbDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
+{
+  return -1;
+}
+
+bool cDvbDevice::OpenDvr(void)
+{
+  return false;
+}
+
+void cDvbDevice::CloseDvr(void)
+{
+}
+
+bool cDvbDevice::GetTSPacket(uchar *&Data)
+{
+  return false;
+}
+
+bool cDvbDevice::Ready(void)
+{
+  return true;
+}
+
+bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
+{
+  return true;
+}
+
+bool cDvbDevice::HasCi(void)
+{          
+  return false;
+}             
+
+uchar *cDvbDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
+{
+  return NULL;
+}
+
+void cDvbDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
+{
+}
+
+int cDvbDevice::GetAudioChannelDevice(void)
+{
+  return 0;
+}
+
+void cDvbDevice::SetAudioChannelDevice(int AudioChannel)
+{
+}
+
+void cDvbDevice::SetDigitalAudioDevice(bool On)
+{
+}
+
+void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
+{
+}
diff --git a/contrib/sasc-ng/sc/include/asm/unaligned.h b/contrib/sasc-ng/sc/include/asm/unaligned.h
new file mode 100644 (file)
index 0000000..bd545f0
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __ASM_UNALIGNED_H
+#define __ASM_UNALIGNED_H
+
+/*
+ * The i386 can do unaligned accesses itself. 
+ *
+ * The strange macros are there to make sure these can't
+ * be misused in a way that makes them not work on other
+ * architectures where unaligned accesses aren't as simple.
+ */
+
+/**
+ * get_unaligned - get value from possibly mis-aligned location
+ * @ptr: pointer to value
+ *
+ * This macro should be used for accessing values larger in size than 
+ * single bytes at locations that are expected to be improperly aligned, 
+ * e.g. retrieving a u16 value from a location not u16-aligned.
+ *
+ * Note that unaligned accesses can be very expensive on some architectures.
+ */
+#define get_unaligned(ptr) (*(ptr))
+
+/**
+ * put_unaligned - put value to a possibly mis-aligned location
+ * @val: value to place
+ * @ptr: pointer to location
+ *
+ * This macro should be used for placing values larger in size than 
+ * single bytes at locations that are expected to be improperly aligned, 
+ * e.g. writing a u16 value to a location not u16-aligned.
+ *
+ * Note that unaligned accesses can be very expensive on some architectures.
+ */
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+
+#endif
diff --git a/contrib/sasc-ng/sc/include/libsi/descriptor.h b/contrib/sasc-ng/sc/include/libsi/descriptor.h
new file mode 100644 (file)
index 0000000..c0c884f
--- /dev/null
@@ -0,0 +1,636 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: descriptor.h 1.15 2006/05/28 14:25:30 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef LIBSI_DESCRIPTOR_H
+#define LIBSI_DESCRIPTOR_H
+
+#include "si.h"
+#include "headers.h"
+
+namespace SI {
+
+class ShortEventDescriptor : public Descriptor {
+public:
+   char languageCode[4];
+   String name; //name of the event
+   String text; //short description
+protected:
+   virtual void Parse();
+};
+
+class ExtendedEventDescriptor : public GroupDescriptor {
+public:
+   class Item : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(item_extended_event)+sizeof(item_extended_event_mid)+item.getLength()+itemDescription.getLength(); }
+      String item;
+      String itemDescription;
+   protected:
+      virtual void Parse();
+   };
+   char languageCode[4];
+   int getDescriptorNumber();
+   int getLastDescriptorNumber();
+   StructureLoop<Item> itemLoop;
+   String text;
+protected:
+   virtual void Parse();
+private:
+   const descr_extended_event *s;
+};
+
+class ExtendedEventDescriptors : public DescriptorGroup {
+public:
+   int getMaximumTextLength(const char *separation1="\t", const char *separation2="\n");
+   //Returns a concatenated version of first the non-itemized and then the itemized text
+   //same semantics as with SI::String
+   char *getText(const char *separation1="\t", const char *separation2="\n");
+   //buffer must at least be getTextLength(), getMaximumTextLength() is a good choice
+   char *getText(char *buffer, int size, const char *separation1="\t", const char *separation2="\n");
+
+   //these only return the non-itemized text fields in concatenated form
+   int getMaximumTextPlainLength();
+   char *getTextPlain();
+   char *getTextPlain(char *buffer, int size);
+
+   //these only return the itemized text fields in concatenated form.
+   //Between the description and the text the separation1 character is used,
+   //separation2 used between two pairs. Example:
+   //Director\tSteven Spielberg\nActor\tMichael Mendl\n
+   int getMaximumTextItemizedLength(const char *separation1="\t", const char *separation2="\n");
+   char *getTextItemized(const char *separation1="\t", const char *separation2="\n");
+   char *getTextItemized(char *buffer, int size, const char *separation1="\t", const char *separation2="\n");
+   //returns the itemized text pair by pair. Maximum length for buffers is 256.
+   //Return value is false if and only if the end of the list is reached.
+   //The argument valid indicates whether the buffers contain valid content.
+   bool getTextItemized(Loop::Iterator &it, bool &valid, char *itemDescription, char *itemText, int sizeItemDescription, int sizeItemText);
+};
+
+class TimeShiftedEventDescriptor : public Descriptor {
+public:
+   int getReferenceServiceId() const;
+   int getReferenceEventId() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_time_shifted_event *s;
+};
+
+class ContentDescriptor : public Descriptor {
+public:
+   class Nibble : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(nibble_content); }
+      int getContentNibbleLevel1() const;
+      int getContentNibbleLevel2() const;
+      int getUserNibble1() const;
+      int getUserNibble2() const;
+   protected:
+      virtual void Parse();
+   private:
+      const nibble_content *s;
+   };
+   StructureLoop<Nibble> nibbleLoop;
+protected:
+   virtual void Parse();
+};
+
+class ParentalRatingDescriptor : public Descriptor {
+public:
+   class Rating : public LoopElement {
+   public:
+      char languageCode[4];
+      int getRating() const;
+      virtual int getLength() { return sizeof(parental_rating); }
+   protected:
+      virtual void Parse();
+   private:
+      const parental_rating *s;
+   };
+   StructureLoop<Rating> ratingLoop;
+protected:
+   virtual void Parse();
+};
+
+class TeletextDescriptor : public Descriptor {
+public:
+   class Teletext : public LoopElement {
+   public:
+      char languageCode[4];
+      int getTeletextType() const;
+      int getTeletextMagazineNumber() const;
+      int getTeletextPageNumber() const;
+      virtual int getLength() { return sizeof(item_teletext); }
+   protected:
+      virtual void Parse();
+   private:
+      const item_teletext *s;
+   };
+   StructureLoop<Teletext> teletextLoop;
+protected:
+   virtual void Parse();
+};
+
+class CaDescriptor : public Descriptor {
+public:
+   int getCaType() const;
+   int getCaPid() const;
+   CharArray privateData;
+protected:
+   virtual void Parse();
+private:
+   const descr_ca *s;
+};
+
+class StreamIdentifierDescriptor : public Descriptor {
+public:
+   int getComponentTag() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_stream_identifier *s;
+};
+
+class NetworkNameDescriptor : public Descriptor {
+public:
+   String name;
+protected:
+   virtual void Parse();
+};
+
+class CaIdentifierDescriptor : public Descriptor {
+public:
+   TypeLoop<SixteenBit> identifiers;
+protected:
+   virtual void Parse();
+};
+
+class CarouselIdentifierDescriptor : public Descriptor {
+public:
+   int getCarouselId() const;
+   int getFormatId() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_carousel_identifier *s;
+};
+
+class BouquetNameDescriptor : public NetworkNameDescriptor {
+};
+
+class ServiceListDescriptor : public Descriptor {
+public:
+   class Service : public LoopElement {
+   public:
+      int getServiceId() const;
+      int getServiceType() const;
+   virtual int getLength() { return sizeof(descr_service_list_loop); }
+   protected:
+      virtual void Parse();
+   private:
+      const descr_service_list_loop *s;
+   };
+   StructureLoop<Service> serviceLoop;
+protected:
+   virtual void Parse();
+};
+
+class SatelliteDeliverySystemDescriptor : public Descriptor {
+public:
+   int getFrequency() const;
+   int getOrbitalPosition() const;
+   int getWestEastFlag() const;
+   int getPolarization() const;
+   int getModulation() const;
+   int getSymbolRate() const;
+   int getFecInner() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_satellite_delivery_system *s;
+};
+
+class CableDeliverySystemDescriptor : public Descriptor {
+public:
+   int getFrequency() const;
+   int getFecOuter() const;
+   int getModulation() const;
+   int getSymbolRate() const;
+   int getFecInner() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_cable_delivery_system *s;
+};
+
+class TerrestrialDeliverySystemDescriptor : public Descriptor {
+public:
+   int getFrequency() const;
+   int getBandwidth() const;
+   int getConstellation() const;
+   int getHierarchy() const;
+   int getCodeRateHP() const;
+   int getCodeRateLP() const;
+   int getGuardInterval() const;
+   int getTransmissionMode() const;
+   bool getOtherFrequency() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_terrestrial_delivery *s;
+};
+
+class ServiceDescriptor : public Descriptor {
+public:
+   int getServiceType() const;
+   String serviceName;
+   String providerName;
+protected:
+   virtual void Parse();
+private:
+   const descr_service *s;
+};
+
+class NVODReferenceDescriptor : public Descriptor {
+public:
+   class Service : public LoopElement {
+   public:
+      int getTransportStream() const;
+      int getOriginalNetworkId() const;
+      int getServiceId() const;
+      virtual int getLength() { return sizeof(item_nvod_reference); }
+   protected:
+      virtual void Parse();
+   private:
+      const item_nvod_reference *s;
+   };
+   StructureLoop<Service> serviceLoop;
+protected:
+   virtual void Parse();
+};
+
+class TimeShiftedServiceDescriptor : public Descriptor {
+public:
+   int getReferenceServiceId() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_time_shifted_service *s;
+};
+
+class ComponentDescriptor : public Descriptor {
+public:
+   int getStreamContent() const;
+   int getComponentType() const;
+   int getComponentTag() const;
+   char languageCode[4];
+   String description;
+protected:
+   virtual void Parse();
+private:
+   const descr_component *s;
+};
+
+class PrivateDataSpecifierDescriptor : public Descriptor {
+public:
+   int getPrivateDataSpecifier() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_private_data_specifier *s;
+};
+
+class SubtitlingDescriptor : public Descriptor {
+public:
+   class Subtitling : public LoopElement {
+   public:
+      char languageCode[4];
+      int getSubtitlingType() const;
+      int getCompositionPageId() const;
+      int getAncillaryPageId() const;
+      virtual int getLength() { return sizeof(item_subtitling); }
+   protected:
+      virtual void Parse();
+   private:
+      const item_subtitling *s;
+   };
+   StructureLoop<Subtitling> subtitlingLoop;
+protected:
+   virtual void Parse();
+};
+
+class ServiceMoveDescriptor : public Descriptor {
+public:
+   int getNewOriginalNetworkId() const;
+   int getNewTransportStreamId() const;
+   int getNewServiceId() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_service_move *s;
+};
+
+class FrequencyListDescriptor : public Descriptor {
+public:
+   int getCodingType() const;
+   TypeLoop<ThirtyTwoBit> frequencies;
+protected:
+   virtual void Parse();
+private:
+   const descr_frequency_list *s;
+};
+
+class ServiceIdentifierDescriptor : public Descriptor {
+public:
+   String textualServiceIdentifier;
+protected:
+   virtual void Parse();
+};
+
+//abstract base class
+class MultilingualNameDescriptor : public Descriptor {
+public:
+   class Name : public LoopElement {
+   public:
+      char languageCode[4];
+      String name;
+      virtual int getLength() { return sizeof(entry_multilingual_name)+name.getLength(); }
+   protected:
+      virtual void Parse();
+   };
+   StructureLoop<Name> nameLoop;
+protected:
+   virtual void Parse();
+};
+
+class MultilingualNetworkNameDescriptor : public MultilingualNameDescriptor {
+   //inherits nameLoop from MultilingualNameDescriptor
+};
+
+class MultilingualBouquetNameDescriptor : public MultilingualNameDescriptor {
+   //inherits nameLoop from MultilingualNameDescriptor
+};
+
+class MultilingualComponentDescriptor : public MultilingualNameDescriptor {
+public:
+   int getComponentTag() const;
+   //inherits nameLoop from MultilingualNameDescriptor
+protected:
+   virtual void Parse();
+private:
+   const descr_multilingual_component *s;
+};
+
+class MultilingualServiceNameDescriptor : public Descriptor {
+public:
+   class Name : public MultilingualNameDescriptor::Name {
+   public:
+      virtual int getLength() { return sizeof(entry_multilingual_name)+providerName.getLength()+sizeof(entry_multilingual_service_name_mid)+name.getLength(); }
+      String providerName;
+      //inherits name, meaning: service name;
+   protected:
+      virtual void Parse();
+   };
+   StructureLoop<Name> nameLoop;
+protected:
+   virtual void Parse();
+};
+
+class LocalTimeOffsetDescriptor : public Descriptor {
+public:
+   class LocalTimeOffset : public LoopElement {
+   public:
+      char countryCode[4];
+      virtual int getLength() { return sizeof(local_time_offset_entry); }
+      int getCountryId() const;
+      int getLocalTimeOffsetPolarity() const;
+      int getLocalTimeOffset() const;
+      time_t getTimeOfChange() const;
+      int getNextTimeOffset() const;
+   protected:
+      virtual void Parse();
+   private:
+      const local_time_offset_entry *s;
+   };
+   StructureLoop<LocalTimeOffset> localTimeOffsetLoop;
+protected:
+   virtual void Parse();
+};
+
+class LinkageDescriptor : public Descriptor {
+public:
+   int getTransportStreamId() const;
+   int getOriginalNetworkId() const;
+   int getServiceId() const;
+   LinkageType getLinkageType() const;
+   int getHandOverType() const;
+   int getOriginType() const;
+   int getId() const;
+   CharArray privateData;
+protected:
+   virtual void Parse();
+private:
+   const descr_linkage *s;
+   const descr_linkage_8 *s1;
+};
+
+class ISO639LanguageDescriptor : public Descriptor {
+public:
+   char languageCode[4]; //for backwards compatibility
+   class Language : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(descr_iso_639_language_loop); }
+      char languageCode[4];
+      AudioType getAudioType();
+   protected:
+      virtual void Parse();
+   private:
+      const descr_iso_639_language_loop *s;
+   };
+   StructureLoop<Language> languageLoop;
+protected:
+   virtual void Parse();
+private:
+   const descr_iso_639_language *s;
+};
+
+class PDCDescriptor : public Descriptor {
+public:
+   int getDay() const;
+   int getMonth() const;
+   int getHour() const;
+   int getMinute() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_pdc *s;
+};
+
+class AncillaryDataDescriptor : public Descriptor {
+public:
+   int getAncillaryDataIdentifier() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_ancillary_data *s;
+};
+
+// Private DVB Descriptor  Premiere.de
+// 0xF2  Content Transmission Descriptor
+// http://dvbsnoop.sourceforge.net/examples/example-private-section.html
+
+class PremiereContentTransmissionDescriptor : public Descriptor {
+public:
+   class StartDayEntry : public LoopElement {
+   public:
+      class StartTimeEntry : public LoopElement {
+      public:
+         virtual int getLength() { return sizeof(item_premiere_content_transmission_time); }
+         time_t getStartTime(int mjd) const; //UTC
+      protected:
+         virtual void Parse();
+      private:
+         const item_premiere_content_transmission_time *s;
+      };
+      StructureLoop<StartTimeEntry> startTimeLoop;
+      virtual int getLength();
+      int getMJD() const;
+      int getLoopLength() const;
+   protected:
+      virtual void Parse();
+   private:
+      const item_premiere_content_transmission_day *s;
+   };
+   StructureLoop<StartDayEntry> startDayLoop;
+   int getOriginalNetworkId() const;
+   int getTransportStreamId() const;
+   int getServiceId() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_premiere_content_transmission *s;
+};
+
+//a descriptor currently unimplemented in this library
+class UnimplementedDescriptor : public Descriptor {
+protected:
+   virtual void Parse() {}
+};
+
+class ApplicationSignallingDescriptor : public Descriptor {
+public:
+   class ApplicationEntryDescriptor : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(application_signalling_entry); }
+      int getApplicationType() const;
+      int getAITVersionNumber() const;
+   protected:
+      virtual void Parse();
+   private:
+      const application_signalling_entry *s;
+   };
+   StructureLoop<ApplicationEntryDescriptor> entryLoop;
+protected:
+   virtual void Parse();
+};
+
+class MHP_ApplicationDescriptor : public Descriptor {
+public:
+   class Profile : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(application_profile_entry); }
+      int getApplicationProfile() const;
+      int getVersionMajor() const;
+      int getVersionMinor() const;
+      int getVersionMicro() const;
+   private:
+      const application_profile_entry *s;
+   protected:
+      virtual void Parse();
+   };
+   StructureLoop<Profile> profileLoop;
+   bool isServiceBound() const;
+   int getVisibility() const;
+   int getApplicationPriority() const;
+   TypeLoop<EightBit> transportProtocolLabels;
+private:
+   const descr_application_end *s;
+protected:
+   virtual void Parse();
+};
+
+class MHP_ApplicationNameDescriptor : public Descriptor {
+public:
+   class NameEntry : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(descr_application_name_entry)+name.getLength(); }
+      char languageCode[4];
+      String name;
+   protected:
+      virtual void Parse();
+   };
+   StructureLoop<NameEntry> nameLoop;
+protected:
+   virtual void Parse();
+};
+
+class MHP_TransportProtocolDescriptor : public Descriptor {
+public:
+   enum Protocol { ObjectCarousel = 0x01, IPviaDVB = 0x02, HTTPoverInteractionChannel = 0x03 };
+   int getProtocolId() const;
+   int getProtocolLabel() const;
+   bool isRemote() const;
+   int getComponentTag() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_transport_protocol *s;
+   bool remote;
+   int componentTag;
+};
+
+class MHP_DVBJApplicationDescriptor : public Descriptor {
+public:
+   class ApplicationEntry : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(descr_dvbj_application_entry)+parameter.getLength(); }
+      String parameter;
+   protected:
+      virtual void Parse();
+   };
+   StructureLoop<ApplicationEntry> applicationLoop;
+protected:
+   virtual void Parse();
+};
+
+class MHP_DVBJApplicationLocationDescriptor : public Descriptor {
+public:
+   String baseDirectory;
+   String classPath;
+   String initialClass;
+protected:
+   virtual void Parse();
+};
+
+class MHP_ApplicationIconsDescriptor : public Descriptor {
+public:
+   String iconLocator;
+   int getIconFlags() const;
+protected:
+   virtual void Parse();
+private:
+   const descr_application_icons_descriptor_end *s;
+};
+
+} //end of namespace
+
+#endif //LIBSI_TABLE_H
diff --git a/contrib/sasc-ng/sc/include/libsi/headers.h b/contrib/sasc-ng/sc/include/libsi/headers.h
new file mode 100644 (file)
index 0000000..ea7824b
--- /dev/null
@@ -0,0 +1,1818 @@
+/***************************************************************************
+ *                                                                         *
+ *   (C) 2001-03 Rolf Hakenes <hakenes@hippomi.de>, under the              *
+ *               GNU GPL with contribution of Oleg Assovski,               *
+ *               www.satmania.com                                          *
+ *               Adapted and extended by Marcel Wiesweg                    *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: headers.h 1.8 2006/09/02 20:25:16 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef LIBSI_HEADERS_H
+#define LIBSI_HEADERS_H
+
+#include <endian.h>
+
+namespace SI {
+
+typedef unsigned char u_char;
+
+struct SectionHeader {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+};
+
+struct ExtendedSectionHeader {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char table_id_extension_hi                  :8;
+   u_char table_id_extension_lo                  :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+};
+
+struct DescriptorHeader {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/*
+ *
+ *    ETSI ISO/IEC 13818-1 specifies SI which is referred to as PSI. The PSI
+ *    data provides information to enable automatic configuration of the
+ *    receiver to demultiplex and decode the various streams of programs
+ *    within the multiplex. The PSI data is structured as four types of table.
+ *    The tables are transmitted in sections.
+ *
+ *    1) Program Association Table (PAT):
+ *
+ *       - for each service in the multiplex, the PAT indicates the location
+ *         (the Packet Identifier (PID) values of the Transport Stream (TS)
+ *         packets) of the corresponding Program Map Table (PMT).
+ *         It also gives the location of the Network Information Table (NIT).
+ *
+ */
+
+#define PAT_LEN 8
+
+struct pat {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char dummy                                  :1;        // has to be 0
+   u_char                                        :2;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :2;
+   u_char dummy                                  :1;        // has to be 0
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+};
+
+#define PAT_PROG_LEN 4
+
+struct pat_prog {
+   u_char program_number_hi                      :8;
+   u_char program_number_lo                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :3;
+   u_char network_pid_hi                         :5;
+#else
+   u_char network_pid_hi                         :5;
+   u_char                                        :3;
+#endif
+   u_char network_pid_lo                         :8;
+   /* or program_map_pid (if prog_num=0)*/
+};
+
+/*
+ *
+ *    2) Conditional Access Table (CAT):
+ *
+ *       - the CAT provides information on the CA systems used in the
+ *         multiplex; the information is private and dependent on the CA
+ *         system, but includes the location of the EMM stream, when
+ *         applicable.
+ *
+ */
+#define CAT_LEN 8
+
+struct cat {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char dummy                                  :1;        // has to be 0
+   u_char                                        :2;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :2;
+   u_char dummy                                  :1;        // has to be 0
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char reserved_1                             :8;
+   u_char reserved_2                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+};
+
+/*
+ *
+ *    3) Program Map Table (PMT):
+ *
+ *       - the PMT identifies and indicates the locations of the streams that
+ *         make up each service, and the location of the Program Clock
+ *         Reference fields for a service.
+ *
+ */
+
+#define PMT_LEN 12
+
+struct pmt {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char dummy                                  :1; // has to be 0
+   u_char                                        :2;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :2;
+   u_char dummy                                  :1; // has to be 0
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char program_number_hi                      :8;
+   u_char program_number_lo                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :3;
+   u_char PCR_PID_hi                             :5;
+#else
+   u_char PCR_PID_hi                             :5;
+   u_char                                        :3;
+#endif
+   u_char PCR_PID_lo                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char program_info_length_hi                 :4;
+#else
+   u_char program_info_length_hi                 :4;
+   u_char                                        :4;
+#endif
+   u_char program_info_length_lo                 :8;
+   //descriptors
+};
+
+#define PMT_INFO_LEN 5
+
+struct pmt_info {
+   u_char stream_type                            :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :3;
+   u_char elementary_PID_hi                      :5;
+#else
+   u_char elementary_PID_hi                      :5;
+   u_char                                        :3;
+#endif
+   u_char elementary_PID_lo                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char ES_info_length_hi                      :4;
+#else
+   u_char ES_info_length_hi                      :4;
+   u_char                                        :4;
+#endif
+   u_char ES_info_length_lo                      :8;
+   // descriptors
+};
+
+/*
+ *
+ *    4) Transport Stream Description Table (TSDT):
+ *
+ *       - The TSDT carries a loop of descriptors that apply to
+ *         the whole transport stream. The syntax and semantics
+ *         of the TSDT are defined in newer versions of ISO/IEC 13818-1.
+ *
+ */
+
+#define TSDT_LEN 8
+
+struct tsdt {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char dummy                                  :1; // has to be 0
+   u_char                                        :2;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :2;
+   u_char dummy                                  :1; // has to be 0
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char                                        :8;
+   u_char                                        :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+};
+
+/*
+ *
+ *    5) Network Information Table (NIT):
+ *
+ *       - the NIT is intended to provide information about the physical
+ *         network. The syntax and semantics of the NIT are defined in
+ *         ETSI EN 300 468.
+ *
+ */
+
+#define NIT_LEN 10
+
+struct nit {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char network_id_hi                          :8;
+   u_char network_id_lo                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char network_descriptor_length_hi           :4;
+#else
+   u_char network_descriptor_length_hi           :4;
+   u_char                                        :4;
+#endif
+   u_char network_descriptor_length_lo           :8;
+  /* descriptors */
+};
+
+#define SIZE_NIT_MID 2
+
+struct nit_mid {                                 // after descriptors
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char transport_stream_loop_length_hi        :4;
+#else
+   u_char transport_stream_loop_length_hi        :4;
+   u_char                                        :4;
+#endif
+   u_char transport_stream_loop_length_lo        :8;
+};
+
+#define SIZE_NIT_END 4
+
+struct nit_end {
+   long CRC;
+};
+
+#define NIT_TS_LEN 6
+
+struct ni_ts {
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char transport_descriptors_length_hi        :4;
+#else
+   u_char transport_descriptors_length_hi        :4;
+   u_char                                        :4;
+#endif
+   u_char transport_descriptors_length_lo        :8;
+   /* descriptors  */
+};
+
+/*
+ *
+ *    In addition to the PSI, data is needed to provide identification of
+ *    services and events for the user. In contrast with the PAT, CAT, and
+ *    PMT of the PSI, which give information only for the multiplex in which
+ *    they are contained (the actual multiplex), the additional information
+ *    defined within the present document can also provide information on
+ *    services and events carried by different multiplexes, and even on other
+ *    networks. This data is structured as nine tables:
+ *
+ *    1) Bouquet Association Table (BAT):
+ *
+ *       - the BAT provides information regarding bouquets. As well as giving
+ *         the name of the bouquet, it provides a list of services for each
+ *         bouquet.
+ *
+ */
+/* SEE NIT (It has the same structure but has different allowed descriptors) */
+/*
+ *
+ *    2) Service Description Table (SDT):
+ *
+ *       - the SDT contains data describing the services in the system e.g.
+ *         names of services, the service provider, etc.
+ *
+ */
+
+#define SDT_LEN 11
+
+struct sdt {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char                                        :8;
+};
+
+#define GetSDTTransportStreamId(x) (HILO(((sdt_t *) x)->transport_stream_id))
+#define GetSDTOriginalNetworkId(x) (HILO(((sdt_t *) x)->original_network_id))
+
+#define SDT_DESCR_LEN 5
+
+struct sdt_descr {
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :6;
+   u_char eit_schedule_flag                      :1;
+   u_char eit_present_following_flag             :1;
+   u_char running_status                         :3;
+   u_char free_ca_mode                           :1;
+   u_char descriptors_loop_length_hi             :4;
+#else
+   u_char eit_present_following_flag             :1;
+   u_char eit_schedule_flag                      :1;
+   u_char                                        :6;
+   u_char descriptors_loop_length_hi             :4;
+   u_char free_ca_mode                           :1;
+   u_char running_status                         :3;
+#endif
+   u_char descriptors_loop_length_lo             :8;
+};
+
+/*
+ *
+ *    3) Event Information Table (EIT):
+ *
+ *       - the EIT contains data concerning events or programmes such as event
+ *         name, start time, duration, etc.; - the use of different descriptors
+ *         allows the transmission of different kinds of event information e.g.
+ *         for different service types.
+ *
+ */
+
+#define EIT_LEN 14
+
+struct eit {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char segment_last_section_number            :8;
+   u_char last_table_id                          :8;
+};
+
+#define EIT_EVENT_LEN 12
+
+struct eit_event {
+   u_char event_id_hi                            :8;
+   u_char event_id_lo                            :8;
+   u_char mjd_hi                                 :8;
+   u_char mjd_lo                                 :8;
+   u_char start_time_h                           :8;
+   u_char start_time_m                           :8;
+   u_char start_time_s                           :8;
+   u_char duration_h                             :8;
+   u_char duration_m                             :8;
+   u_char duration_s                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char running_status                         :3;
+   u_char free_ca_mode                           :1;
+   u_char descriptors_loop_length_hi             :4;
+#else
+   u_char descriptors_loop_length_hi             :4;
+   u_char free_ca_mode                           :1;
+   u_char running_status                         :3;
+#endif
+   u_char descriptors_loop_length_lo             :8;
+};
+
+/*
+ *
+ *    4) Running Status Table (RST):
+ *
+ *       - the RST gives the status of an event (running/not running). The RST
+ *         updates this information and allows timely automatic switching to
+ *         events.
+ *
+ */
+
+struct rst {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+};
+
+struct rst_info {
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+   u_char event_id_hi                            :8;
+   u_char event_id_lo                            :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :5;
+   u_char running_status                         :3;
+#else
+   u_char running_status                         :3;
+   u_char                                        :5;
+#endif
+};
+
+/*
+ *
+ *    5) Time and Date Table (TDT):
+ *
+ *       - the TDT gives information relating to the present time and date.
+ *         This information is given in a separate table due to the frequent
+ *         updating of this information.
+ *
+ */
+
+#define TDT_LEN 8
+
+struct tdt {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char utc_mjd_hi                             :8;
+   u_char utc_mjd_lo                             :8;
+   u_char utc_time_h                             :8;
+   u_char utc_time_m                             :8;
+   u_char utc_time_s                             :8;
+};
+
+/*
+ *
+ *    6) Time Offset Table (TOT):
+ *
+ *       - the TOT gives information relating to the present time and date and
+ *         local time offset. This information is given in a separate table due
+ *         to the frequent updating of the time information.
+ *
+ */
+#define TOT_LEN 10
+
+struct tot {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char utc_mjd_hi                             :8;
+   u_char utc_mjd_lo                             :8;
+   u_char utc_time_h                             :8;
+   u_char utc_time_m                             :8;
+   u_char utc_time_s                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char descriptors_loop_length_hi             :4;
+#else
+   u_char descriptors_loop_length_hi             :4;
+   u_char                                        :4;
+#endif
+   u_char descriptors_loop_length_lo             :8;
+};
+
+/*
+ *
+ *    7) Stuffing Table (ST):
+ *
+ *       - the ST is used to invalidate existing sections, for example at
+ *         delivery system boundaries.
+ *
+ */
+    /* TO BE DONE */
+/*
+ *
+ *    8) Selection Information Table (SIT):
+ *
+ *       - the SIT is used only in "partial" (i.e. recorded) bitstreams. It
+ *         carries a summary of the SI information required to describe the
+ *         streams in the partial bitstream.
+ *
+ */
+    /* TO BE DONE */
+/*
+ *
+ *    9) Discontinuity Information Table (DIT):
+ *
+ *       - the DIT is used only in "partial" (i.e. recorded) bitstreams.
+ *         It is inserted where the SI information in the partial bitstream may
+ *         be discontinuous. Where applicable the use of descriptors allows a
+ *         flexible approach to the organization of the tables and allows for
+ *         future compatible extensions.
+ *
+ */
+    /* TO BE DONE */
+
+/*
+ *
+ *    3) Application Information Table (AIT):
+ *
+ *       - the AIT contains data concerning MHP application broadcast by a service.
+ *
+ */
+
+#define AIT_LEN 10
+
+struct ait {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char                                        :3;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :3;
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char application_type_hi                    :8;
+   u_char application_type_lo                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char common_descriptors_length_hi           :4;
+#else
+   u_char common_descriptors_length_hi           :4;
+   u_char                                        :4;
+#endif
+   u_char common_descriptors_length_lo           :8;
+};
+
+#define SIZE_AIT_MID 2
+
+struct ait_mid {                                 // after descriptors
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char application_loop_length_hi             :4;
+#else
+   u_char application_loop_length_hi             :4;
+   u_char                                        :4;
+#endif
+   u_char application_loop_length_lo             :8;
+};
+
+#define SIZE_AIT_END 4
+
+struct ait_end {
+   long CRC;
+};
+
+#define AIT_APP_LEN 9
+
+struct ait_app {
+   //how to deal with 32 bit fields?
+
+   u_char organisation_id_hi_hi                  :8;
+   u_char organisation_id_hi_lo                  :8;
+   u_char organisation_id_lo_hi                  :8;
+   u_char organisation_id_lo_lo                  :8;
+
+   //long organisation_id                          :32;
+   u_char application_id_hi                      :8;
+   u_char application_id_lo                      :8;
+   u_char application_control_code               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char application_descriptors_length_hi      :4;
+#else
+   u_char application_descriptors_length_hi      :4;
+   u_char                                        :4;
+#endif
+   u_char application_descriptors_length_lo      :8;
+   /* descriptors  */
+};
+
+/* Premiere Content Information Table */
+
+#define PCIT_LEN 17
+
+struct pcit {
+   u_char table_id                               :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char section_syntax_indicator               :1;
+   u_char dummy                                  :1; // has to be 0
+   u_char                                        :2;
+   u_char section_length_hi                      :4;
+#else
+   u_char section_length_hi                      :4;
+   u_char                                        :2;
+   u_char dummy                                  :1; // has to be 0
+   u_char section_syntax_indicator               :1;
+#endif
+   u_char section_length_lo                      :8;
+   u_char                                        :8;
+   u_char                                        :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :2;
+   u_char version_number                         :5;
+   u_char current_next_indicator                 :1;
+#else
+   u_char current_next_indicator                 :1;
+   u_char version_number                         :5;
+   u_char                                        :2;
+#endif
+   u_char section_number                         :8;
+   u_char last_section_number                    :8;
+
+   u_char contentId_hi_hi                        :8;
+   u_char contentId_hi_lo                        :8;
+   u_char contentId_lo_hi                        :8;
+   u_char contentId_lo_lo                        :8;
+
+   u_char duration_h                             :8;
+   u_char duration_m                             :8;
+   u_char duration_s                             :8;
+
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :4;
+   u_char descriptors_loop_length_hi             :4;
+#else
+   u_char descriptors_loop_length_hi             :4;
+   u_char                                        :4;
+#endif
+   u_char descriptors_loop_length_lo             :8;
+};
+
+/*
+ *
+ *    The following describes the different descriptors that can be used within
+ *    the SI.
+ *
+ *    The following semantics apply to all the descriptors defined in this
+ *    subclause:
+ *
+ *    descriptor_tag: The descriptor tag is an 8-bit field which identifies
+ *                    each descriptor. Those values with MPEG-2 normative
+ *                    meaning are described in ISO/IEC 13818-1. The values of
+ *                    descriptor_tag are defined in 'libsi.h'
+ *    descriptor_length: The descriptor length is an 8-bit field specifying the
+ *                       total number of bytes of the data portion of the
+ *                       descriptor following the byte defining the value of
+ *                       this field.
+ *
+ */
+
+#define DESCR_GEN_LEN 2
+struct descr_gen {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define GetDescriptorTag(x) (((descr_gen_t *) x)->descriptor_tag)
+#define GetDescriptorLength(x) (((descr_gen_t *) x)->descriptor_length+DESCR_GEN_LEN)
+
+/* 0x09 ca_descriptor */
+
+#define DESCR_CA_LEN 6
+struct descr_ca {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char CA_type_hi                             :8;
+   u_char CA_type_lo                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char reserved                               :3;
+   u_char CA_PID_hi                              :5;
+#else
+   u_char CA_PID_hi                              :5;
+   u_char reserved                               :3;
+#endif
+   u_char CA_PID_lo                              :8;
+};
+
+/* 0x0A iso_639_language_descriptor */
+
+#define DESCR_ISO_639_LANGUAGE_LEN 5
+struct descr_iso_639_language {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+struct descr_iso_639_language_loop {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char audio_type                             :8;
+};
+
+/* 0x13 carousel_identifier_descriptor */
+
+#define DESCR_CAROUSEL_IDENTIFIER_LEN 7
+struct descr_carousel_identifier {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char carousel_id_hi_hi                      :8;
+   u_char carousel_id_hi_lo                      :8;
+   u_char carousel_id_lo_hi                      :8;
+   u_char carousel_id_lo_lo                      :8;
+   u_char FormatId                               :8;
+   /* FormatSpecifier follows */
+};
+
+/* 0x40 network_name_descriptor */
+
+#define DESCR_NETWORK_NAME_LEN 2
+struct descr_network_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/* 0x41 service_list_descriptor */
+
+#define DESCR_SERVICE_LIST_LEN 2
+struct descr_service_list {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define DESCR_SERVICE_LIST_LOOP_LEN 3
+struct descr_service_list_loop {
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+   u_char service_type                           :8;
+};
+
+/* 0x42 stuffing_descriptor */
+
+#define DESCR_STUFFING_LEN XX
+struct descr_stuffing {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x43 satellite_delivery_system_descriptor */
+
+#define DESCR_SATELLITE_DELIVERY_SYSTEM_LEN 13
+struct descr_satellite_delivery_system {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char frequency_hi_hi                        :8;
+   u_char frequency_hi_lo                        :8;
+   u_char frequency_lo_hi                        :8;
+   u_char frequency_lo_lo                        :8;
+   u_char orbital_position_hi                    :8;
+   u_char orbital_position_lo                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char west_east_flag                         :1;
+   u_char polarization                           :2;
+   u_char modulation                             :5;
+#else
+   u_char modulation                             :5;
+   u_char polarization                           :2;
+   u_char west_east_flag                         :1;
+#endif
+   u_char symbol_rate_hi_hi                      :8;
+   u_char symbol_rate_hi_lo                      :8;
+   u_char symbol_rate_lo_1                       :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char symbol_rate_lo_2                       :4;
+   u_char fec_inner                              :4;
+#else
+   u_char fec_inner                              :4;
+   u_char symbol_rate_lo_2                       :4;
+#endif
+};
+
+/* 0x44 cable_delivery_system_descriptor */
+
+#define DESCR_CABLE_DELIVERY_SYSTEM_LEN 13
+struct descr_cable_delivery_system {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char frequency_hi_hi                        :8;
+   u_char frequency_hi_lo                        :8;
+   u_char frequency_lo_hi                        :8;
+   u_char frequency_lo_lo                        :8;
+   u_char reserved1                              :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char reserved2                              :4;
+   u_char fec_outer                              :4;
+#else
+   u_char fec_outer                              :4;
+   u_char reserved2                              :4;
+#endif
+   u_char modulation                             :8;
+   u_char symbol_rate_hi_hi                      :8;
+   u_char symbol_rate_hi_lo                      :8;
+   u_char symbol_rate_lo_1                       :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char symbol_rate_lo_2                       :4;
+   u_char fec_inner                              :4;
+#else
+   u_char fec_inner                              :4;
+   u_char symbol_rate_lo_2                       :4;
+#endif
+};
+
+/* 0x45 vbi_data_descriptor */
+
+#define DESCR_VBI_DATA_LEN XX
+struct descr_vbi_data {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x46 vbi_teletext_descriptor */
+
+#define DESCR_VBI_TELETEXT_LEN XX
+struct descr_vbi_teletext {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x47 bouquet_name_descriptor */
+
+#define DESCR_BOUQUET_NAME_LEN 2
+struct descr_bouquet_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/* 0x48 service_descriptor */
+
+#define DESCR_SERVICE_LEN  4
+struct descr_service {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char service_type                           :8;
+   u_char provider_name_length                   :8;
+};
+
+struct descr_service_mid {
+   u_char service_name_length                   :8;
+};
+
+/* 0x49 country_availability_descriptor */
+
+#define DESCR_COUNTRY_AVAILABILITY_LEN 3
+struct descr_country_availability {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char country_availability_flag              :1;
+   u_char reserved                               :7;
+#else
+   u_char reserved                               :7;
+   u_char country_availability_flag              :1;
+#endif
+};
+
+/* 0x4A linkage_descriptor */
+
+#define DESCR_LINKAGE_LEN 9
+struct descr_linkage {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+   u_char linkage_type                           :8;
+};
+
+#define DESCR_LINKAGE_8_LEN 3
+struct descr_linkage_8 {
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char hand_over_type                         :4;
+   u_char reserved                               :3;
+   u_char origin_type                            :1;
+#else
+   u_char origin_type                            :1;
+   u_char reserved                               :3;
+   u_char hand_over_type                         :4;
+#endif
+   u_char id_hi                                  :8;
+   u_char id_lo                                  :8;
+};
+
+/* 0x4B nvod_reference_descriptor */
+
+#define DESCR_NVOD_REFERENCE_LEN 2
+struct descr_nvod_reference {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define ITEM_NVOD_REFERENCE_LEN 6
+struct item_nvod_reference {
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+};
+
+/* 0x4C time_shifted_service_descriptor */
+
+#define DESCR_TIME_SHIFTED_SERVICE_LEN 4
+struct descr_time_shifted_service {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char reference_service_id_hi                :8;
+   u_char reference_service_id_lo                :8;
+};
+
+/* 0x4D short_event_descriptor */
+
+#define DESCR_SHORT_EVENT_LEN 6
+struct descr_short_event {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char event_name_length                      :8;
+};
+
+struct descr_short_event_mid {
+   u_char text_length                      :8;
+};
+
+/* 0x4E extended_event_descriptor */
+
+#define DESCR_EXTENDED_EVENT_LEN 7
+struct descr_extended_event {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char descriptor_number                      :4;
+   u_char last_descriptor_number                 :4;
+#else
+   u_char last_descriptor_number                 :4;
+   u_char descriptor_number                      :4;
+#endif
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char length_of_items                        :8;
+};
+
+struct descr_extended_event_mid {
+   u_char text_length                            :8;
+};
+
+#define ITEM_EXTENDED_EVENT_LEN 1
+struct item_extended_event {
+   u_char item_description_length                :8;
+};
+
+struct item_extended_event_mid {
+   u_char item_length                            :8;
+};
+
+/* 0x4F time_shifted_event_descriptor */
+
+#define DESCR_TIME_SHIFTED_EVENT_LEN 6
+struct descr_time_shifted_event {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char reference_service_id_hi                :8;
+   u_char reference_service_id_lo                :8;
+   u_char reference_event_id_hi                  :8;
+   u_char reference_event_id_lo                  :8;
+};
+
+/* 0x50 component_descriptor */
+
+#define DESCR_COMPONENT_LEN  8
+struct descr_component {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char reserved                               :4;
+   u_char stream_content                         :4;
+#else
+   u_char stream_content                         :4;
+   u_char reserved                               :4;
+#endif
+   u_char component_type                         :8;
+   u_char component_tag                          :8;
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+};
+
+/* 0x51 mosaic_descriptor */
+
+#define DESCR_MOSAIC_LEN XX
+struct descr_mosaic {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x52 stream_identifier_descriptor */
+
+#define DESCR_STREAM_IDENTIFIER_LEN 3
+struct descr_stream_identifier {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char component_tag                          :8;
+};
+
+/* 0x53 ca_identifier_descriptor */
+
+#define DESCR_CA_IDENTIFIER_LEN 2
+struct descr_ca_identifier {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/* 0x54 content_descriptor */
+
+#define DESCR_CONTENT_LEN 2
+struct descr_content {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+struct nibble_content {
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char content_nibble_level_1                 :4;
+   u_char content_nibble_level_2                 :4;
+#else
+   u_char content_nibble_level_2                 :4;
+   u_char content_nibble_level_1                 :4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char user_nibble_1                          :4;
+   u_char user_nibble_2                          :4;
+#else
+   u_char user_nibble_2                          :4;
+   u_char user_nibble_1                          :4;
+#endif
+};
+
+/* 0x55 parental_rating_descriptor */
+
+#define DESCR_PARENTAL_RATING_LEN 2
+struct descr_parental_rating {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define PARENTAL_RATING_LEN 4
+struct parental_rating {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char rating                                 :8;
+};
+
+/* 0x56 teletext_descriptor */
+
+#define DESCR_TELETEXT_LEN 2
+struct descr_teletext {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define ITEM_TELETEXT_LEN 5
+struct item_teletext {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char type                                   :5;
+   u_char magazine_number                        :3;
+#else
+   u_char magazine_number                        :3;
+   u_char type                                   :5;
+#endif
+   u_char page_number                            :8;
+};
+
+/* 0x57 telephone_descriptor */
+
+#define DESCR_TELEPHONE_LEN XX
+struct descr_telephone {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x58 local_time_offset_descriptor */
+
+#define DESCR_LOCAL_TIME_OFFSET_LEN 2
+struct descr_local_time_offset {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define LOCAL_TIME_OFFSET_ENTRY_LEN 15
+struct local_time_offset_entry {
+   u_char country_code1                          :8;
+   u_char country_code2                          :8;
+   u_char country_code3                          :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char country_region_id                      :6;
+   u_char                                        :1;
+   u_char local_time_offset_polarity             :1;
+#else
+   u_char local_time_offset_polarity             :1;
+   u_char                                        :1;
+   u_char country_region_id                      :6;
+#endif
+   u_char local_time_offset_h                    :8;
+   u_char local_time_offset_m                    :8;
+   u_char time_of_change_mjd_hi                  :8;
+   u_char time_of_change_mjd_lo                  :8;
+   u_char time_of_change_time_h                  :8;
+   u_char time_of_change_time_m                  :8;
+   u_char time_of_change_time_s                  :8;
+   u_char next_time_offset_h                     :8;
+   u_char next_time_offset_m                     :8;
+};
+
+/* 0x59 subtitling_descriptor */
+
+#define DESCR_SUBTITLING_LEN 2
+struct descr_subtitling {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define ITEM_SUBTITLING_LEN 8
+struct item_subtitling {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char subtitling_type                        :8;
+   u_char composition_page_id_hi                 :8;
+   u_char composition_page_id_lo                 :8;
+   u_char ancillary_page_id_hi                   :8;
+   u_char ancillary_page_id_lo                   :8;
+};
+
+/* 0x5A terrestrial_delivery_system_descriptor */
+
+#define DESCR_TERRESTRIAL_DELIVERY_SYSTEM_LEN XX
+struct descr_terrestrial_delivery {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char frequency_hi_hi                        :8;
+   u_char frequency_hi_lo                        :8;
+   u_char frequency_lo_hi                        :8;
+   u_char frequency_lo_lo                        :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char bandwidth                              :3;
+   u_char reserved1                              :5;
+#else
+   u_char reserved1                              :5;
+   u_char bandwidth                              :3;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char constellation                          :2;
+   u_char hierarchy                              :3;
+   u_char code_rate_HP                           :3;
+#else
+   u_char code_rate_HP                           :3;
+   u_char hierarchy                              :3;
+   u_char constellation                          :2;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char code_rate_LP                           :3;
+   u_char guard_interval                         :2;
+   u_char transmission_mode                      :2;
+   u_char other_frequency_flag                   :1;
+#else
+   u_char other_frequency_flag                   :1;
+   u_char transmission_mode                      :2;
+   u_char guard_interval                         :2;
+   u_char code_rate_LP                           :3;
+#endif
+   u_char reserver2                              :8;
+   u_char reserver3                              :8;
+   u_char reserver4                              :8;
+   u_char reserver5                              :8;
+};
+
+/* 0x5B multilingual_network_name_descriptor */
+
+#define DESCR_MULTILINGUAL_NETWORK_NAME_LEN XX
+struct descr_multilingual_network_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+struct entry_multilingual_name {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char text_length                            :8;
+};
+
+/* 0x5C multilingual_bouquet_name_descriptor */
+
+#define DESCR_MULTILINGUAL_BOUQUET_NAME_LEN XX
+struct descr_multilingual_bouquet_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/* 0x5D multilingual_service_name_descriptor */
+
+#define DESCR_MULTILINGUAL_SERVICE_NAME_LEN XX
+struct descr_multilingual_service_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+struct entry_multilingual_service_name_mid {
+   u_char service_name_length                    :8;
+};
+
+/* 0x5E multilingual_component_descriptor */
+
+#define DESCR_MULTILINGUAL_COMPONENT_LEN XX
+struct descr_multilingual_component {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char component_tag                          :8;
+};
+
+/* 0x5F private_data_specifier_descriptor */
+
+#define DESCR_PRIVATE_DATA_SPECIFIER_LEN XX
+struct descr_private_data_specifier {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char private_data_specifier_hi_hi           :8;
+   u_char private_data_specifier_hi_lo           :8;
+   u_char private_data_specifier_lo_hi           :8;
+   u_char private_data_specifier_lo_lo           :8;
+};
+
+/* 0x60 service_move_descriptor */
+
+#define DESCR_SERVICE_MOVE_LEN XX
+struct descr_service_move {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char new_original_network_id_hi             :8;
+   u_char new_original_network_id_lo             :8;
+   u_char new_transport_stream_id_hi             :8;
+   u_char new_transport_stream_id_lo             :8;
+   u_char new_service_id_hi                      :8;
+   u_char new_service_id_lo                      :8;
+};
+
+/* 0x61 short_smoothing_buffer_descriptor */
+
+#define DESCR_SHORT_SMOOTHING_BUFFER_LEN XX
+struct descr_short_smoothing_buffer {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x62 frequency_list_descriptor */
+
+#define DESCR_FREQUENCY_LIST_LEN XX
+struct descr_frequency_list {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :6;
+   u_char coding_type                            :2;
+#else
+   u_char coding_type                            :2;
+   u_char                                        :6;
+#endif
+};
+
+/* 0x63 partial_transport_stream_descriptor */
+
+#define DESCR_PARTIAL_TRANSPORT_STREAM_LEN XX
+struct descr_partial_transport_stream {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x64 data_broadcast_descriptor */
+
+#define DESCR_DATA_BROADCAST_LEN XX
+struct descr_data_broadcast {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x65 ca_system_descriptor */
+
+#define DESCR_CA_SYSTEM_LEN XX
+struct descr_ca_system {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x66 data_broadcast_id_descriptor */
+
+#define DESCR_DATA_BROADCAST_ID_LEN XX
+struct descr_data_broadcast_id {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x67 transport_stream_descriptor */
+
+#define DESCR_TRANSPORT_STREAM_LEN XX
+struct descr_transport_stream {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x68 dsng_descriptor */
+
+#define DESCR_DSNG_LEN XX
+struct descr_dsng {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x69 pdc_descriptor */
+
+#define DESCR_PDC_LEN 5
+struct descr_pdc {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char pil0                                   :8;
+   u_char pil1                                   :8;
+   u_char pil2                                   :8;
+};
+
+/* 0x6A ac3_descriptor */
+
+#define DESCR_AC3_LEN 3
+struct descr_ac3 {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char ac3_type_flag                          :1;
+   u_char bsid_flag                              :1;
+   u_char mainid_flag                            :1;
+   u_char asvc_flag                              :1;
+   u_char reserved                               :4;
+#else
+   u_char reserved                               :4;
+   u_char asvc_flag                              :1;
+   u_char mainid_flag                            :1;
+   u_char bsid_flag                              :1;
+   u_char ac3_type_flag                          :1;
+#endif
+   u_char ac3_type                               :8;
+   u_char bsid                                   :8;
+   u_char mainid                                 :8;
+   u_char asvc                                   :8;
+};
+
+/* 0x6B ancillary_data_descriptor */
+
+#define DESCR_ANCILLARY_DATA_LEN 3
+struct descr_ancillary_data {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char ancillary_data_identifier              :8;
+};
+
+/* 0x6C cell_list_descriptor */
+
+#define DESCR_CELL_LIST_LEN XX
+struct descr_cell_list {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x6D cell_frequency_link_descriptor */
+
+#define DESCR_CELL_FREQUENCY_LINK_LEN XX
+struct descr_cell_frequency_link {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x6E announcement_support_descriptor */
+
+#define DESCR_ANNOUNCEMENT_SUPPORT_LEN XX
+struct descr_announcement_support {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   /* TBD */
+};
+
+/* 0x6F application_signalling_descriptor */
+
+#define DESCR_APPLICATION_SIGNALLING_LEN 2
+struct descr_application_signalling {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define APPLICATION_SIGNALLING_ENTRY_LEN 3
+struct application_signalling_entry {
+   u_char application_type_hi                    :8;
+   u_char application_type_lo                    :8;
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char                                        :3;
+   u_char AIT_version_number                     :5;
+#else
+   u_char AIT_version_number                     :5;
+   u_char                                        :3;
+#endif
+};
+
+/* 0x71 service_identifier_descriptor (ETSI TS 102 812, MHP) */
+
+struct descr_service_identifier {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+/* MHP 0x00 application_descriptor */
+
+#define DESCR_APPLICATION_LEN 3
+
+struct descr_application {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char application_profiles_length            :8;
+};
+
+#define DESCR_APPLICATION_END_LEN 2
+
+struct descr_application_end {
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char service_bound_flag                     :1;
+   u_char visibility                             :2;
+   u_char                                        :5;
+#else
+   u_char                                        :5;
+   u_char visibility                             :2;
+   u_char service_bound_flag                     :1;
+#endif
+   u_char application_priority                   :8;
+/*now follow 8bit transport_protocol_label fields to the end */
+};
+
+#define APPLICATION_PROFILE_ENTRY_LEN 5
+
+struct application_profile_entry {
+   u_char application_profile_hi                 :8;
+   u_char application_profile_lo                 :8;
+   u_char version_major                          :8;
+   u_char version_minor                          :8;
+   u_char version_micro                          :8;
+};
+
+/* MHP 0x01 application_name_desriptor */
+
+#define DESCR_APPLICATION_NAME_LEN 2
+
+struct descr_application_name {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define APPLICATION_NAME_ENTRY_LEN 4
+
+struct descr_application_name_entry {
+   u_char lang_code1                             :8;
+   u_char lang_code2                             :8;
+   u_char lang_code3                             :8;
+   u_char application_name_length                :8;
+   /* application name string */
+};
+
+/* MHP 0x02 transport_protocol_descriptor */
+
+#define DESCR_TRANSPORT_PROTOCOL_LEN 5
+
+struct descr_transport_protocol {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char protocol_id_hi                         :8;
+   u_char protocol_id_lo                         :8;
+   u_char transport_protocol_label               :8;
+   /* protocol_id-specific selector bytes follow */
+};
+
+#define TRANSPORT_VIA_OC_LEN 1
+
+struct transport_via_oc {
+#if BYTE_ORDER == BIG_ENDIAN
+   u_char remote                                 :1;
+   u_char                                        :7;
+#else
+   u_char                                        :7;
+   u_char remote                                 :1;
+#endif
+};
+
+//if remote is true, transport_via_oc_remote_end_t follows,
+// else transport_via_oc_end_t.
+
+#define TRANSPORT_VIA_OC_REMOTE_END_LEN 7
+
+struct transport_via_oc_remote_end {
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+   u_char component_tag                          :8;
+};
+
+#define TRANSPORT_VIA_OC_END_LEN 1
+
+struct transport_via_oc_end {
+   u_char component_tag                          :8;
+};
+
+/* 0x03 dvb_j_application_descriptor() */
+
+#define DESCR_DVBJ_APPLICATION_LEN 2
+
+struct descr_dvbj_application {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+};
+
+#define DESCR_DVBJ_APPLICATION_ENTRY_LEN 1
+
+struct descr_dvbj_application_entry {
+   u_char parameter_length                       :8;
+   /* parameter string */
+};
+
+/* 0x04 dvb_j_application_location_descriptor */
+
+#define DESCR_DVBJ_APPLICATION_LOCATION_LEN 3
+
+struct descr_dvbj_application_location {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char base_directory_length                  :8;
+   /* base directory string */
+};
+
+#define DESCR_DVBJ_APPLICATION_LOCATION_MID_LEN 1
+
+struct descr_dvbj_application_location_mid {
+   u_char classpath_extension_length                  :8;
+};
+
+/* 0x0B application_icons_descriptor */
+
+#define DESCR_APPLICATION_ICONS_LEN 3
+
+struct descr_application_icons_descriptor {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char icon_locator_length                    :8;
+   /* icon locator */
+};
+
+#define DESCR_APPLICATION_ICONS_END_LEN 2
+
+struct descr_application_icons_descriptor_end {
+   u_char icon_flags_hi                          :8;
+   u_char icon_flags_lo                          :8;
+};
+
+// Private DVB Descriptor  Premiere.de
+// 0xF2  Content Transmission Descriptor
+// http://dvbsnoop.sourceforge.net/examples/example-private-section.html
+
+#define DESCR_PREMIERE_CONTENT_TRANSMISSION_LEN 8
+
+struct descr_premiere_content_transmission {
+   u_char descriptor_tag                         :8;
+   u_char descriptor_length                      :8;
+   u_char transport_stream_id_hi                 :8;
+   u_char transport_stream_id_lo                 :8;
+   u_char original_network_id_hi                 :8;
+   u_char original_network_id_lo                 :8;
+   u_char service_id_hi                          :8;
+   u_char service_id_lo                          :8;
+};
+
+#define ITEM_PREMIERE_CONTENT_TRANSMISSION_DAY_LEN 3
+
+struct item_premiere_content_transmission_day {
+   u_char mjd_hi                                 :8;
+   u_char mjd_lo                                 :8;
+   u_char start_time_loop                        :8;
+};
+
+#define ITEM_PREMIERE_CONTENT_TRANSMISSION_TIME_LEN 3
+
+struct item_premiere_content_transmission_time {
+   u_char start_time_h                           :8;
+   u_char start_time_m                           :8;
+   u_char start_time_s                           :8;
+};
+
+} //end of namespace
+
+#endif //LIBSI_HEADERS_H
diff --git a/contrib/sasc-ng/sc/include/libsi/section.h b/contrib/sasc-ng/sc/include/libsi/section.h
new file mode 100644 (file)
index 0000000..29af7f4
--- /dev/null
@@ -0,0 +1,280 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: section.h 1.4 2006/04/14 10:53:44 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef LIBSI_SECTION_H
+#define LIBSI_SECTION_H
+
+#include <time.h>
+
+#include "si.h"
+#include "headers.h"
+
+namespace SI {
+
+class PAT : public NumberedSection {
+public:
+   PAT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   PAT() {}
+   class Association : public LoopElement {
+   public:
+      int getServiceId() const;
+      int getPid() const;
+      bool isNITPid() const { return getServiceId()==0; }
+      virtual int getLength() { return sizeof(pat_prog); }
+   protected:
+      virtual void Parse();
+   private:
+      const pat_prog *s;
+   };
+   int getTransportStreamId() const;
+   StructureLoop<Association> associationLoop;
+protected:
+   virtual void Parse();
+private:
+   const pat *s;
+};
+
+class CAT : public NumberedSection {
+public:
+   CAT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   CAT() {}
+   DescriptorLoop loop;
+protected:
+   virtual void Parse();
+};
+
+class PMT : public NumberedSection {
+public:
+   PMT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   PMT() {}
+   class Stream : public LoopElement {
+   public:
+      int getPid() const;
+      int getStreamType() const;
+      DescriptorLoop streamDescriptors;
+      virtual int getLength() { return sizeof(pmt_info)+streamDescriptors.getLength(); }
+   protected:
+      virtual void Parse();
+   private:
+      const pmt_info *s;
+   };
+   DescriptorLoop commonDescriptors;
+   StructureLoop<Stream> streamLoop;
+   int getServiceId() const;
+   int getPCRPid() const;
+protected:
+   virtual void Parse();
+private:
+   const pmt *s;
+};
+
+class TSDT : public NumberedSection {
+public:
+   TSDT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   TSDT() {}
+   DescriptorLoop transportStreamDescriptors;
+protected:
+   virtual void Parse();
+private:
+   const tsdt *s;
+};
+
+class NIT : public NumberedSection {
+public:
+   NIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   NIT() {}
+   class TransportStream : public LoopElement {
+   public:
+      int getTransportStreamId() const;
+      int getOriginalNetworkId() const;
+      virtual int getLength() { return sizeof(ni_ts)+transportStreamDescriptors.getLength(); }
+      DescriptorLoop transportStreamDescriptors;
+   protected:
+      virtual void Parse();
+   private:
+      const ni_ts *s;
+   };
+   DescriptorLoop commonDescriptors;
+   StructureLoop<TransportStream> transportStreamLoop;
+   int getNetworkId() const;
+protected:
+   virtual void Parse();
+private:
+   const nit *s;
+};
+
+//BAT has the same structure as NIT but different allowed descriptors
+class BAT : public NIT {
+public:
+   BAT(const unsigned char *data, bool doCopy=true) : NIT(data, doCopy) {}
+   BAT() {}
+   int getBouquetId() const { return getNetworkId(); }
+};
+
+class SDT : public NumberedSection {
+public:
+   SDT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   SDT() {}
+   class Service : public LoopElement {
+   public:
+      int getServiceId() const;
+      int getEITscheduleFlag() const;
+      int getEITpresentFollowingFlag() const;
+      RunningStatus getRunningStatus() const;
+      int getFreeCaMode() const;
+      virtual int getLength() { return sizeof(sdt_descr)+serviceDescriptors.getLength(); }
+      DescriptorLoop serviceDescriptors;
+   protected:
+      virtual void Parse();
+   private:
+      const sdt_descr *s;
+   };
+   int getTransportStreamId() const;
+   int getOriginalNetworkId() const;
+   StructureLoop<Service> serviceLoop;
+protected:
+   virtual void Parse();
+private:
+   const sdt *s;
+};
+
+class EIT : public NumberedSection {
+public:
+   EIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   EIT() {}
+   class Event : public LoopElement {
+   public:
+      int getEventId() const;
+      time_t getStartTime() const; //UTC
+      time_t getDuration() const;
+
+      int getMJD() const;
+      int getStartTimeHour() const; //UTC
+      int getStartTimeMinute() const; //UTC
+      int getStartTimeSecond() const; //UTC
+      int getDurationHour() const;
+      int getDurationMinute() const;
+      int getDurationSecond() const;
+      RunningStatus getRunningStatus() const;
+      int getFreeCaMode() const;
+
+      DescriptorLoop eventDescriptors;
+      virtual int getLength() { return sizeof(eit_event)+eventDescriptors.getLength(); }
+   protected:
+      virtual void Parse();
+   private:
+      const eit_event *s;
+   };
+   int getServiceId() const;
+   int getTransportStreamId() const;
+   int getOriginalNetworkId() const;
+   int getSegmentLastSectionNumber() const;
+   int getLastTableId() const;
+   StructureLoop<Event> eventLoop;
+
+   //true if table conveys present/following information, false if it conveys schedule information
+   bool isPresentFollowing() const;
+   //true if table describes TS on which it is broadcast, false if it describes other TS
+   bool isActualTS() const;
+protected:
+   virtual void Parse();
+private:
+   const eit *s;
+};
+
+class TDT : public Section {
+public:
+   TDT(const unsigned char *data, bool doCopy=true) : Section(data, doCopy) {}
+   TDT() {}
+   time_t getTime() const; //UTC
+protected:
+   virtual void Parse();
+private:
+   const tdt *s;
+};
+
+class TOT : public CRCSection {
+public:
+   TOT(const unsigned char *data, bool doCopy=true) : CRCSection(data, doCopy) {}
+   TOT() {}
+   time_t getTime() const;
+   DescriptorLoop descriptorLoop;
+protected:
+   virtual void Parse();
+private:
+   const tot *s;
+};
+
+class RST : public Section {
+public:
+   RST(const unsigned char *data, bool doCopy=true) : Section(data, doCopy) {}
+   RST() {}
+   class RunningInfo : public LoopElement {
+   public:
+      int getTransportStreamId() const;
+      int getOriginalNetworkId() const;
+      int getServiceId() const;
+      int getEventId() const;
+      RunningStatus getRunningStatus() const;
+      virtual int getLength() { return sizeof(rst_info); }
+   protected:
+      virtual void Parse();
+   private:
+      const rst_info *s;
+   };
+   StructureLoop<RunningInfo> infoLoop;
+protected:
+   virtual void Parse();
+};
+
+class AIT : public NumberedSection {
+public:
+   AIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   AIT() {}
+   class Application : public LoopElement {
+   public:
+      virtual int getLength() { return sizeof(ait_app)+applicationDescriptors.getLength(); }
+      long getOrganisationId() const;
+      int getApplicationId() const;
+      int getControlCode() const;
+      MHP_DescriptorLoop applicationDescriptors;
+   protected:
+      virtual void Parse();
+      const ait_app *s;
+   };
+   MHP_DescriptorLoop commonDescriptors;
+   StructureLoop<Application> applicationLoop;
+   int getApplicationType() const;
+   int getAITVersion() const;
+protected:
+   const ait *first;
+   virtual void Parse();
+};
+
+/* Premiere Content Information Table */
+
+class PremiereCIT : public NumberedSection {
+public:
+   PremiereCIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
+   PremiereCIT() {}
+   int getContentId() const;
+   time_t getDuration() const;
+   PCIT_DescriptorLoop eventDescriptors;
+protected:
+   virtual void Parse();
+private:
+   const pcit *s;
+};
+
+} //end of namespace
+
+#endif //LIBSI_TABLE_H
diff --git a/contrib/sasc-ng/sc/include/libsi/si.h b/contrib/sasc-ng/sc/include/libsi/si.h
new file mode 100644 (file)
index 0000000..a487716
--- /dev/null
@@ -0,0 +1,478 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: si.h 1.15 2006/05/27 13:07:20 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef LIBSI_SI_H
+#define LIBSI_SI_H
+
+#include <stdint.h>
+
+#include "util.h"
+#include "headers.h"
+
+namespace SI {
+
+enum TableId { TableIdPAT = 0x00, //program association section
+               TableIdCAT = 0x01, //conditional access section
+               TableIdPMT = 0x02, //program map section
+               TableIdTSDT = 0x03,//transport stream description section
+               TableIdNIT = 0x40, //network information, actual network section
+               TableIdNIT_other  = 0x41, //network information section, other network
+               TableIdSDT = 0x42, //service description section
+               TableIdSDT_other  = 0x46,
+               TableIdBAT = 0x46, //bouquet association section
+               TableIdEIT_presentFollowing = 0x4E, //event information section
+               TableIdEIT_presentFollowing_other = 0x4F,
+               //range from 0x50 to 0x5F
+               TableIdEIT_schedule_first = 0x50,
+               TableIdEIT_schedule_last = 0x5F,
+               //range from 0x60 to 0x6F
+               TableIdEIT_schedule_Other_first = 0x60,
+               TableIdEIT_schedule_Other_last = 0x6F,
+               TableIdTDT = 0x70, //time date section
+               TableIdRST = 0x71, //running status section
+               TableIdST  = 0x72, //stuffing section
+               TableIdTOT = 0x73, //time offset section
+               TableIdDIT = 0x7E, //discontinuity information section
+               TableIdSIT = 0x7F, //service information section
+               TableIdAIT = 0x74, //application information section
+               TableIdPremiereCIT = 0xA0 //premiere content information section
+             };
+
+
+enum DescriptorTag {
+  // defined by ISO/IEC 13818-1
+               VideoStreamDescriptorTag = 0x02,
+               AudioStreamDescriptorTag = 0x03,
+               HierarchyDescriptorTag = 0x04,
+               RegistrationDescriptorTag = 0x05,
+               DataStreamAlignmentDescriptorTag = 0x06,
+               TargetBackgroundGridDescriptorTag = 0x07,
+               VideoWindowDescriptorTag = 0x08,
+               CaDescriptorTag = 0x09,
+               ISO639LanguageDescriptorTag = 0x0A,
+               SystemClockDescriptorTag = 0x0B,
+               MultiplexBufferUtilizationDescriptorTag = 0x0C,
+               CopyrightDescriptorTag = 0x0D,
+               MaximumBitrateDescriptorTag = 0x0E,
+               PrivateDataIndicatorDescriptorTag = 0x0F,
+               SmoothingBufferDescriptorTag = 0x10,
+               STDDescriptorTag = 0x11,
+               IBPDescriptorTag = 0x12,
+  // defined by ISO-13818-6 (DSM-CC)
+               CarouselIdentifierDescriptorTag = 0x13,
+               // 0x14 - 0x3F  Reserved
+  // defined by ETSI (EN 300 468)
+               NetworkNameDescriptorTag = 0x40,
+               ServiceListDescriptorTag = 0x41,
+               StuffingDescriptorTag = 0x42,
+               SatelliteDeliverySystemDescriptorTag = 0x43,
+               CableDeliverySystemDescriptorTag = 0x44,
+               VBIDataDescriptorTag = 0x45,
+               VBITeletextDescriptorTag = 0x46,
+               BouquetNameDescriptorTag = 0x47,
+               ServiceDescriptorTag = 0x48,
+               CountryAvailabilityDescriptorTag = 0x49,
+               LinkageDescriptorTag = 0x4A,
+               NVODReferenceDescriptorTag = 0x4B,
+               TimeShiftedServiceDescriptorTag = 0x4C,
+               ShortEventDescriptorTag = 0x4D,
+               ExtendedEventDescriptorTag = 0x4E,
+               TimeShiftedEventDescriptorTag = 0x4F,
+               ComponentDescriptorTag = 0x50,
+               MocaicDescriptorTag = 0x51,
+               StreamIdentifierDescriptorTag = 0x52,
+               CaIdentifierDescriptorTag = 0x53,
+               ContentDescriptorTag = 0x54,
+               ParentalRatingDescriptorTag = 0x55,
+               TeletextDescriptorTag = 0x56,
+               TelephoneDescriptorTag = 0x57,
+               LocalTimeOffsetDescriptorTag = 0x58,
+               SubtitlingDescriptorTag = 0x59,
+               TerrestrialDeliverySystemDescriptorTag = 0x5A,
+               MultilingualNetworkNameDescriptorTag = 0x5B,
+               MultilingualBouquetNameDescriptorTag = 0x5C,
+               MultilingualServiceNameDescriptorTag = 0x5D,
+               MultilingualComponentDescriptorTag = 0x5E,
+               PrivateDataSpecifierDescriptorTag = 0x5F,
+               ServiceMoveDescriptorTag = 0x60,
+               ShortSmoothingBufferDescriptorTag = 0x61,
+               FrequencyListDescriptorTag = 0x62,
+               PartialTransportStreamDescriptorTag = 0x63,
+               DataBroadcastDescriptorTag = 0x64,
+               CaSystemDescriptorTag = 0x65,
+               DataBroadcastIdDescriptorTag = 0x66,
+               TransportStreamDescriptorTag = 0x67,
+               DSNGDescriptorTag = 0x68,
+               PDCDescriptorTag = 0x69,
+               AC3DescriptorTag = 0x6A,
+               AncillaryDataDescriptorTag = 0x6B,
+               CellListDescriptorTag = 0x6C,
+               CellFrequencyLinkDescriptorTag = 0x6D,
+               AnnouncementSupportDescriptorTag = 0x6E,
+               ApplicationSignallingDescriptorTag = 0x6F,
+               AdaptationFieldDataDescriptorTag = 0x70,
+               ServiceIdentifierDescriptorTag = 0x71,
+               ServiceAvailabilityDescriptorTag = 0x72,
+ // Defined by ETSI TS 102 812 (MHP)
+               // They once again start with 0x00 (see page 234, MHP specification)
+               MHP_ApplicationDescriptorTag = 0x00,
+               MHP_ApplicationNameDescriptorTag = 0x01,
+               MHP_TransportProtocolDescriptorTag = 0x02,
+               MHP_DVBJApplicationDescriptorTag = 0x03,
+               MHP_DVBJApplicationLocationDescriptorTag = 0x04,
+               // 0x05 - 0x0A is unimplemented this library
+               MHP_ExternalApplicationAuthorisationDescriptorTag = 0x05,
+               MHP_IPv4RoutingDescriptorTag = 0x06,
+               MHP_IPv6RoutingDescriptorTag = 0x07,
+               MHP_DVBHTMLApplicationDescriptorTag = 0x08,
+               MHP_DVBHTMLApplicationLocationDescriptorTag = 0x09,
+               MHP_DVBHTMLApplicationBoundaryDescriptorTag = 0x0A,
+               MHP_ApplicationIconsDescriptorTag = 0x0B,
+               MHP_PrefetchDescriptorTag = 0x0C,
+               MHP_DelegatedApplicationDescriptorTag = 0x0E,
+               MHP_ApplicationStorageDescriptorTag = 0x10,
+  // Premiere private Descriptor Tags
+               PremiereContentTransmissionDescriptorTag = 0xF2,
+
+               //a descriptor currently unimplemented in this library
+               //the actual value 0xFF is "forbidden" according to the spec.
+               UnimplementedDescriptorTag = 0xFF
+};
+
+enum DescriptorTagDomain { SI, MHP, PCIT };
+
+enum RunningStatus { RunningStatusUndefined = 0,
+                     RunningStatusNotRunning = 1,
+                     RunningStatusStartsInAFewSeconds = 2,
+                     RunningStatusPausing = 3,
+                     RunningStatusRunning = 4
+                   };
+
+enum LinkageType { LinkageTypeInformationService = 0x01,
+                   LinkageTypeEPGService = 0x02,
+                   LinkageTypeCaReplacementService = 0x03,
+                   LinkageTypeTSContainingCompleteNetworkBouquetSi = 0x04,
+                   LinkageTypeServiceReplacementService = 0x05,
+                   LinkageTypeDataBroadcastService = 0x06,
+                   LinkageTypeRCSMap = 0x07,
+                   LinkageTypeMobileHandover = 0x08,
+                   LinkageTypeSystemSoftwareUpdateService = 0x09,
+                   LinkageTypeTSContainingSsuBatOrNit = 0x0A
+                 };
+
+enum AudioType { AudioTypeUndefined = 0x00,
+                 AudioTypeCleanEffects = 0x01,
+                 AudioTypeHearingImpaired = 0x02,
+                 AudioTypeVisualImpairedCommentary = 0x03
+               };
+
+/* Some principles:
+   - Objects that return references to other objects contained in their data must make sure
+     that the returned objects have been parsed.
+     (the Loop subclasses take care of that.)
+     Note that this does not apply to Loops and Strings (their are never returned by reference, BTW).
+*/
+
+class Object : public Parsable {
+public:
+   Object();
+   Object(CharArray &d);
+   //can only be called once since data is immutable
+   void setData(const unsigned char*data, int size, bool doCopy=true);
+   CharArray getData() { return data; }
+   //returns the valid flag which indicates if data is all right or errors have been encountered
+   bool isValid() { return data.isValid(); }
+   virtual int getLength() = 0;
+protected:
+   CharArray data;
+   //is protected - not used for sections
+   template <class T> friend class StructureLoop;
+   void setData(CharArray &d);
+   //returns whether the given offset fits within the limits of the actual data
+   //The valid flag will be set accordingly
+   bool checkSize(int offset);
+};
+
+class Section : public Object {
+public:
+   //convenience: sets data and parses if doParse
+   Section(const unsigned char *data, bool doCopy=true);
+   Section() {}
+   TableId getTableId() const;
+   virtual int getLength();
+
+   static int getLength(const unsigned char *d);
+   static TableId getTableId(const unsigned char *d);
+};
+
+class CRCSection : public Section {
+public:
+   //convenience: sets data and parses if doParse
+   CRCSection(const unsigned char *data, bool doCopy=true) : Section(data, doCopy) {}
+   CRCSection() {}
+   bool isCRCValid();
+   //convenience: isValid+CheckParse
+   bool CheckCRCAndParse();
+};
+
+/* A section which has the ExtendedSectionHeader
+   (section_syntax_indicator==1) */
+class NumberedSection : public CRCSection {
+public:
+   NumberedSection(const unsigned char *data, bool doCopy=true) : CRCSection(data, doCopy) {}
+   NumberedSection() {}
+   int getTableIdExtension() const;
+   bool getCurrentNextIndicator() const;
+   int getVersionNumber() const;
+   int getSectionNumber() const;
+   int getLastSectionNumber() const;
+   bool moreThanOneSection()  const { return getLastSectionNumber()>0; }
+
+   static int getTableIdExtension(const unsigned char *d);
+};
+
+class VariableLengthPart : public Object {
+public:
+   //never forget to call this
+   void setData(CharArray d, int l) { Object::setData(d); checkSize(l); length=l; }
+   //convenience method
+   void setDataAndOffset(CharArray d, int l, int &offset) { Object::setData(d); checkSize(l); length=l; offset+=l; }
+   virtual int getLength() { return length; }
+private:
+   int length;
+};
+
+class LoopElement : public Object {
+};
+
+class Descriptor : public LoopElement {
+public:
+   virtual int getLength();
+   DescriptorTag getDescriptorTag() const;
+
+   static int getLength(const unsigned char *d);
+   static DescriptorTag getDescriptorTag(const unsigned char *d);
+protected:
+   friend class DescriptorLoop;
+   //returns a subclass of descriptor according to the data given.
+   //The object is allocated with new and must be delete'd.
+   //setData() will have been called, CheckParse() not.
+   //if returnUnimplemetedDescriptor==true:
+   //   Never returns null - maybe the UnimplementedDescriptor.
+   //if returnUnimplemetedDescriptor==false:
+   //   Never returns the UnimplementedDescriptor - maybe null
+   static Descriptor *getDescriptor(CharArray d, DescriptorTagDomain domain, bool returnUnimplemetedDescriptor);
+};
+
+class Loop : public VariableLengthPart {
+public:
+   class Iterator {
+   public:
+      Iterator() { i=0; }
+      void reset() { i=0; }
+   private:
+      template <class T> friend class StructureLoop;
+      friend class DescriptorLoop;
+      template <class T> friend class TypeLoop;
+      friend class ExtendedEventDescriptors;
+      int i;
+   };
+protected:
+   virtual void Parse() {}
+};
+
+//contains LoopElements of one type only
+template <class T> class StructureLoop : public Loop {
+public:
+   //currently you must use a while-loop testing for hasNext()
+   //i must be 0 to get the first descriptor (with the first call)
+   bool getNext(T &obj, Iterator &it)
+      {
+         if (!isValid() || it.i >= getLength())
+            return false;
+         CharArray d=data;
+         d.addOffset(it.i);
+         T ret;
+         ret.setData(d);
+         ret.CheckParse();
+         if (!checkSize(ret.getLength()))
+            return false;
+         it.i+=ret.getLength();
+         obj=ret;
+         return true;
+      }
+   T* getNextAsPointer(Iterator &it)
+      {
+         if (!isValid() || it.i >= getLength())
+            return 0;
+         CharArray d=data;
+         d.addOffset(it.i);
+         T *ret=new T();
+         ret->setData(d);
+         ret->CheckParse();
+         if (!checkSize(ret->getLength()))
+            return 0;
+         it.i+=ret->getLength();
+         return ret;
+      }
+   //bool hasNext(Iterator &it) { return getLength() > it.i; }
+};
+
+//contains descriptors of different types
+class DescriptorLoop : public Loop {
+public:
+   DescriptorLoop() { domain=SI; }
+   //i must be 0 to get the first descriptor (with the first call)
+   //All returned descriptors must be delete'd.
+   //returns null if no more descriptors available
+   Descriptor *getNext(Iterator &it);
+   //return the next descriptor with given tag, or 0 if not available.
+   //if returnUnimplemetedDescriptor==true:
+   //   an UnimplementedDescriptor may be returned if the next matching descriptor is unimplemented,
+   //   0 will be returned if and only if no matching descriptor is found.
+   //if returnUnimplemetedDescriptor==false:
+   //   if 0 is returned, either no descriptor with the given tag was found,
+   //   or descriptors were found, but the descriptor type is not implemented
+   //In either case, a return value of 0 indicates that no further calls to this method
+   //with the iterator shall be made.
+   Descriptor *getNext(Iterator &it, DescriptorTag tag, bool returnUnimplemetedDescriptor=false);
+   //return the next descriptor with one of the given tags, or 0 if not available.
+   //if returnUnimplemetedDescriptor==true:
+   //   returns 0 if and only if no descriptor with one of the given tags was found.
+   //   The UnimplementedDescriptor may be returned.
+   //if returnUnimplemetedDescriptor==false:
+   //   if 0 is returned, either no descriptor with one of the given tags was found,
+   //   or descriptors were found, but none of them are implemented.
+   //   The UnimplementedDescriptor will never be returned.
+   //In either case, a return value of 0 indicates that no further calls to this method
+   //with the iterator shall be made.
+   Descriptor *getNext(Iterator &it, DescriptorTag *tags, int arrayLength, bool returnUnimplemetedDescriptor=false);
+   //returns the number of descriptors in this loop
+   int getNumberOfDescriptors();
+   //writes the tags of the descriptors in this loop in the array,
+   // which must at least have the size getNumberOfDescriptors().
+   //The number of descriptors, i.e. getNumberOfDescriptors(), is returned.
+   // You can specify the array type (Descriptor tags are 8 Bit,
+   // you might e.g. choose a char, short, int or DescriptorTag array)
+   template <typename T> int getDescriptorTags(T *tags)
+      {
+         const unsigned char *p=data.getData();
+         const unsigned char *end=p+getLength();
+         int count=0;
+         while (p < end) {
+            tags[count++]=(T)Descriptor::getDescriptorTag(p);
+            p+=Descriptor::getLength(p);
+         }
+         return count;
+      }
+protected:
+   Descriptor *createDescriptor(int &i, bool returnUnimplemetedDescriptor);
+   DescriptorTagDomain domain;
+};
+
+typedef uint8_t   EightBit;
+typedef uint16_t  SixteenBit;
+typedef uint32_t  ThirtyTwoBit;
+typedef uint64_t  SixtyFourBit;
+
+template <typename T> class TypeLoop : public Loop {
+public:
+   int getCount() { return getLength()/sizeof(T); }
+   T operator[](const int index) const
+      {
+         switch (sizeof(T)) {
+         case 1:
+            return data[index];
+         case 2:
+            return data.TwoBytes(index);
+         case 4:
+            return data.FourBytes(index);
+         case 8:
+            return (SixtyFourBit(data.FourBytes(index)) << 32) | data.FourBytes(index+4);
+         }
+         return 0; // just to avoid a compiler warning
+      }
+   T getNext(Iterator &it) const
+      {
+         T ret=operator[](it.i);
+         it.i+=sizeof(T);
+         return ret;
+      }
+   bool hasNext(Iterator &it) { return isValid() && (getLength() > it.i); }
+};
+
+class MHP_DescriptorLoop : public DescriptorLoop {
+public:
+   MHP_DescriptorLoop() { domain=MHP; }
+};
+
+//Premiere Content Information Table
+class PCIT_DescriptorLoop : public DescriptorLoop {
+public:
+   PCIT_DescriptorLoop() { domain=PCIT; }
+};
+
+//The content of the ExtendedEventDescriptor may be split over several
+//descriptors if the text is longer than 256 bytes.
+//The following classes provide base functionality to handle this case.
+class GroupDescriptor : public Descriptor {
+public:
+   virtual int getDescriptorNumber() = 0;
+   virtual int getLastDescriptorNumber() = 0;
+};
+
+class DescriptorGroup {
+public:
+   DescriptorGroup(bool deleteOnDesctruction=true);
+   ~DescriptorGroup();
+   void Add(GroupDescriptor *d);
+   void Delete();
+   int getLength() { return length; }
+   GroupDescriptor **getDescriptors() { return array; }
+   bool isComplete(); //if all descriptors have been added
+protected:
+   int length;
+   GroupDescriptor **array;
+   bool deleteOnDesctruction;
+};
+
+class String : public VariableLengthPart {
+public:
+   //A note to the length: getLength() returns the length of the raw data.
+   //The text may be shorter. Its length can be obtained with one of the
+   //getText functions and strlen.
+
+   //returns text. Data is allocated with new and must be delete'd by the user.
+   char *getText();
+   //copies text into given buffer.
+   //a buffer of size getLength()+1 is guaranteed to be sufficiently large.
+   //In most descriptors the string length is an 8-bit field,
+   //so the maximum there is 256.
+   //returns the given buffer for convenience.
+   //The emphasis marks 0x86 and 0x87 are still available.
+   char *getText(char *buffer, int size);
+   //The same semantics as for getText(char*) apply.
+   //The short version of the text according to ETSI TR 101 211 (chapter 4.6)
+   //will be written into the shortVersion buffer (which should, therefore, have the same
+   //length as buffer). If no shortVersion is available, shortVersion will contain
+   //an empty string.
+   //The emphasis marks 0x86 and 0x87 are still available in buffer, but not in shortVersion.
+   char *getText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion);
+protected:
+   virtual void Parse() {}
+   void decodeText(char *buffer, int size);
+   void decodeText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion);
+};
+
+} //end of namespace
+
+#endif //LIBSI_SI_H
diff --git a/contrib/sasc-ng/sc/include/libsi/util.h b/contrib/sasc-ng/sc/include/libsi/util.h
new file mode 100644 (file)
index 0000000..08b1614
--- /dev/null
@@ -0,0 +1,162 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: util.h 1.7 2006/02/25 10:13:28 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef LIBSI_UTIL_H
+#define LIBSI_UTIL_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <time.h>
+
+#define HILO(x) (x##_hi << 8 | x##_lo)
+#define BCD_TIME_TO_SECONDS(x) ((3600 * ((10*((x##_h & 0xF0)>>4)) + (x##_h & 0xF))) + \
+                             (60 * ((10*((x##_m & 0xF0)>>4)) + (x##_m & 0xF))) + \
+                             ((10*((x##_s & 0xF0)>>4)) + (x##_s & 0xF)))
+
+namespace SI {
+
+//Holds an array of unsigned char which is deleted
+//when the last object pointing to it is deleted.
+//Optimized for use in libsi.
+class CharArray {
+public:
+   CharArray();
+
+   CharArray(const CharArray &source);
+   CharArray& operator=(const CharArray &source);
+   ~CharArray();
+
+   //can be called exactly once
+   void assign(const unsigned char*data, int size, bool doCopy=true);
+   //compares to a null-terminated string
+   bool operator==(const char *string) const;
+   //compares to another CharArray (data not necessarily null-terminated)
+   bool operator==(const CharArray &other) const;
+
+   //returns another CharArray with its offset incremented by offset
+   CharArray operator+(const int offset) const;
+
+   //access and convenience methods
+   const unsigned char* getData() const { return data_->data+off; }
+   const unsigned char* getData(int offset) const { return data_->data+offset+off; }
+   template <typename T> const T* getData() const { return (T*)(data_->data+off); }
+   template <typename T> const T* getData(int offset) const { return (T*)(data_->data+offset+off); }
+      //sets p to point to data+offset, increments offset
+   template <typename T> void setPointerAndOffset(const T* &p, int &offset) const { p=(T*)getData(offset); offset+=sizeof(T); }
+   unsigned char operator[](const int index) const { return data_->data ? data_->data[off+index] : 0; }
+   int getLength() const { return data_->size; }
+   u_int16_t TwoBytes(const int index) const { return data_->data ? data_->TwoBytes(off+index) : 0; }
+   u_int32_t FourBytes(const int index) const { return data_->data ? data_->FourBytes(off+index) : 0; }
+
+   bool isValid() const { return data_->valid; }
+   bool checkSize(int offset) { return (data_->valid && (data_->valid=(offset>=0 && off+offset < data_->size))); }
+
+   void addOffset(int offset) { off+=offset; }
+private:
+   class Data {
+   public:
+      Data();
+      virtual ~Data();
+
+      virtual void assign(const unsigned char*data, int size) = 0;
+      virtual void Delete() = 0;
+
+      u_int16_t TwoBytes(const int index) const
+         { return (data[index] << 8) | data[index+1]; }
+      u_int32_t FourBytes(const int index) const
+         { return (data[index] << 24) | (data[index+1] << 16) | (data[index+2] << 8) | data[index+3]; }
+      /*#ifdef CHARARRAY_THREADSAFE
+      void Lock();
+      void Unlock();
+      #else
+      void Lock() {}
+      void Unlock() {}
+      #endif
+      Data(const Data& d);
+      void assign(int size);
+      */
+
+      const unsigned char*data;
+      int size;
+
+      // count_ is the number of CharArray objects that point at this
+      // count_ must be initialized to 1 by all constructors
+      // (it starts as 1 since it is pointed to by the CharArray object that created it)
+      unsigned count_;
+
+      bool valid;
+
+      /*
+      pthread_mutex_t mutex;
+      pid_t lockingPid;
+      pthread_t locked;
+      */
+   };
+   class DataOwnData : public Data {
+   public:
+      DataOwnData() {}
+      virtual ~DataOwnData();
+      virtual void assign(const unsigned char*data, int size);
+      virtual void Delete();
+   };
+   class DataForeignData : public Data {
+   public:
+      DataForeignData() {}
+      virtual ~DataForeignData();
+      virtual void assign(const unsigned char*data, int size);
+      virtual void Delete();
+   };
+   Data* data_;
+   int off;
+};
+
+
+
+//abstract base class
+class Parsable {
+public:
+   void CheckParse();
+protected:
+   Parsable();
+   virtual ~Parsable() {}
+   //actually parses given data.
+   virtual void Parse() = 0;
+private:
+   bool parsed;
+};
+
+//taken and adapted from libdtv, (c) Rolf Hakenes and VDR, (c) Klaus Schmidinger
+namespace DVBTime {
+time_t getTime(unsigned char date_hi, unsigned char date_lo, unsigned char timehr, unsigned char timemi, unsigned char timese);
+time_t getDuration(unsigned char timehr, unsigned char timemi, unsigned char timese);
+inline unsigned char bcdToDec(unsigned char b) { return ((b >> 4) & 0x0F) * 10 + (b & 0x0F); }
+}
+
+//taken and adapted from libdtv, (c) Rolf Hakenes
+class CRC32 {
+public:
+   CRC32(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF);
+   bool isValid() { return crc32(data, length, value) == 0; }
+   static bool isValid(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF) { return crc32(d, len, CRCvalue) == 0; }
+protected:
+   static u_int32_t crc_table[256];
+   static u_int32_t crc32 (const char *d, int len, u_int32_t CRCvalue);
+
+   const char *data;
+   int length;
+   u_int32_t value;
+};
+
+} //end of namespace
+
+#endif
diff --git a/contrib/sasc-ng/sc/include/vdr/channels.h b/contrib/sasc-ng/sc/include/vdr/channels.h
new file mode 100644 (file)
index 0000000..caa1a83
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * channels.h: Channel handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: channels.h 1.43 2007/01/05 10:37:35 kls Exp $
+ */
+
+#ifndef __CHANNELS_H
+#define __CHANNELS_H
+
+#include "config.h"
+#include "sources.h"
+#include "thread.h"
+#include "tools.h"
+
+#define ISTRANSPONDER(f1, f2)  (abs((f1) - (f2)) < 4) //XXX
+
+#define CHANNELMOD_NONE     0x00
+#define CHANNELMOD_ALL      0xFF
+#define CHANNELMOD_NAME     0x01
+#define CHANNELMOD_PIDS     0x02
+#define CHANNELMOD_ID       0x04
+#define CHANNELMOD_CA       0x10
+#define CHANNELMOD_TRANSP   0x20
+#define CHANNELMOD_LANGS    0x40
+#define CHANNELMOD_RETUNE   (CHANNELMOD_PIDS | CHANNELMOD_CA | CHANNELMOD_TRANSP)
+
+#define CHANNELSMOD_NONE    0
+#define CHANNELSMOD_AUTO    1
+#define CHANNELSMOD_USER    2
+
+#define MAXAPIDS 32 // audio
+#define MAXDPIDS 64 // dolby (AC3 + DTS)
+#define MAXSPIDS  8 // subtitles
+#define MAXCAIDS  8 // conditional access
+
+#define MAXLANGCODE1 4 // a 3 letter language code, zero terminated
+#define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated
+
+#define CA_FTA           0x0000
+#define CA_DVB_MIN       0x0001
+#define CA_DVB_MAX       0x000F
+#define CA_USER_MIN      0x0010
+#define CA_USER_MAX      0x00FF
+#define CA_ENCRYPTED_MIN 0x0100
+#define CA_ENCRYPTED_MAX 0xFFFF
+
+struct tChannelParameterMap {
+  int userValue;
+  int driverValue;
+  };
+
+//XXX into cChannel???
+int MapToUser(int Value, const tChannelParameterMap *Map);
+int MapToDriver(int Value, const tChannelParameterMap *Map);
+int UserIndex(int Value, const tChannelParameterMap *Map);
+int DriverIndex(int Value, const tChannelParameterMap *Map);
+
+extern const tChannelParameterMap InversionValues[];
+extern const tChannelParameterMap BandwidthValues[];
+extern const tChannelParameterMap CoderateValues[];
+extern const tChannelParameterMap ModulationValues[];
+extern const tChannelParameterMap TransmissionValues[];
+extern const tChannelParameterMap GuardValues[];
+extern const tChannelParameterMap HierarchyValues[];
+
+struct tChannelID {
+private:
+  int source;
+  int nid; ///< actually the "original" network id
+  int tid;
+  int sid;
+  int rid;
+public:
+  tChannelID(void) { source = nid = tid = sid = rid = 0; }
+  tChannelID(int Source, int Nid, int Tid, int Sid, int Rid = 0) { source = Source; nid = Nid; tid = Tid; sid = Sid; rid = Rid; }
+  bool operator== (const tChannelID &arg) const { return source == arg.source && nid == arg.nid && tid == arg.tid && sid == arg.sid && rid == arg.rid; }
+  bool Valid(void) const { return (nid || tid) && sid; } // rid is optional and source may be 0//XXX source may not be 0???
+  tChannelID &ClrRid(void) { rid = 0; return *this; }
+  tChannelID &ClrPolarization(void);
+  int Source(void) { return source; }
+  int Nid(void) { return nid; }
+  int Tid(void) { return tid; }
+  int Sid(void) { return sid; }
+  int Rid(void) { return rid; }
+  static tChannelID FromString(const char *s);
+  cString ToString(void) const;
+  static const tChannelID InvalidID;
+  };
+
+class cChannel;
+
+class cLinkChannel : public cListObject {
+private:
+  cChannel *channel;
+public:
+  cLinkChannel(cChannel *Channel) { channel = Channel; }
+  cChannel *Channel(void) { return channel; }
+  };
+
+class cLinkChannels : public cList<cLinkChannel> {
+  };
+
+class cSchedule;
+
+class cChannel : public cListObject {
+  friend class cSchedules;
+  friend class cMenuEditChannel;
+private:
+  static cString ToText(const cChannel *Channel);
+  char *name;
+  char *shortName;
+  char *provider;
+  char *portalName;
+  int __BeginData__;
+  int frequency; // MHz
+  int source;
+  int srate;
+  int vpid;
+  int ppid;
+  int apids[MAXAPIDS + 1]; // list is zero-terminated
+  char alangs[MAXAPIDS][MAXLANGCODE2];
+  int dpids[MAXDPIDS + 1]; // list is zero-terminated
+  char dlangs[MAXDPIDS][MAXLANGCODE2];
+  int spids[MAXSPIDS + 1]; // list is zero-terminated
+  char slangs[MAXSPIDS][MAXLANGCODE2];
+  int tpid;
+  int caids[MAXCAIDS + 1]; // list is zero-terminated
+  int nid;
+  int tid;
+  int sid;
+  int rid;
+  int number;    // Sequence number assigned on load
+  bool groupSep;
+  char polarization;
+  int inversion;
+  int bandwidth;
+  int coderateH;
+  int coderateL;
+  int modulation;
+  int transmission;
+  int guard;
+  int hierarchy;
+  int __EndData__;
+  int modification;
+  mutable const cSchedule *schedule;
+  cLinkChannels *linkChannels;
+  cChannel *refChannel;
+  cString ParametersToString(void) const;
+  bool StringToParameters(const char *s);
+public:
+  cChannel(void);
+  cChannel(const cChannel &Channel);
+  ~cChannel();
+  cChannel& operator= (const cChannel &Channel);
+  cString ToText(void) const;
+  bool Parse(const char *s);
+  bool Save(FILE *f);
+  const char *Name(void) const { return name; }
+  const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; }
+  const char *Provider(void) const { return provider; }
+  const char *PortalName(void) const { return portalName; }
+  int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
+  int Transponder(void) const;                    ///< Returns the transponder frequency in MHz, plus the polarization in case of sat
+  static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization
+  int Source(void) const { return source; }
+  int Srate(void) const { return srate; }
+  int Vpid(void) const { return vpid; }
+  int Ppid(void) const { return ppid; }
+  const int *Apids(void) const { return apids; }
+  const int *Dpids(void) const { return dpids; }
+  const int *Spids(void) const { return spids; }
+  int Apid(int i) const { return (0 <= i && i < MAXAPIDS) ? apids[i] : 0; }
+  int Dpid(int i) const { return (0 <= i && i < MAXDPIDS) ? dpids[i] : 0; }
+  int Spid(int i) const { return (0 <= i && i < MAXSPIDS) ? spids[i] : 0; }
+  const char *Alang(int i) const { return (0 <= i && i < MAXAPIDS) ? alangs[i] : ""; }
+  const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; }
+  const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
+  int Tpid(void) const { return tpid; }
+  const int *Caids(void) const { return caids; }
+  int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
+  int Nid(void) const { return nid; }
+  int Tid(void) const { return tid; }
+  int Sid(void) const { return sid; }
+  int Rid(void) const { return rid; }
+  int Number(void) const { return number; }
+  void SetNumber(int Number) { number = Number; }
+  bool GroupSep(void) const { return groupSep; }
+  char Polarization(void) const { return polarization; }
+  int Inversion(void) const { return inversion; }
+  int Bandwidth(void) const { return bandwidth; }
+  int CoderateH(void) const { return coderateH; }
+  int CoderateL(void) const { return coderateL; }
+  int Modulation(void) const { return modulation; }
+  int Transmission(void) const { return transmission; }
+  int Guard(void) const { return guard; }
+  int Hierarchy(void) const { return hierarchy; }
+  const cLinkChannels* LinkChannels(void) const { return linkChannels; }
+  const cChannel *RefChannel(void) const { return refChannel; }
+  bool IsCable(void) const { return cSource::IsCable(source); }
+  bool IsSat(void) const { return cSource::IsSat(source); }
+  bool IsTerr(void) const { return cSource::IsTerr(source); }
+  tChannelID GetChannelID(void) const { return tChannelID(source, nid, (nid || tid) ? tid : Transponder(), sid, rid); }
+  bool HasTimer(void) const;
+  int Modification(int Mask = CHANNELMOD_ALL);
+  void CopyTransponderData(const cChannel *Channel);
+  bool SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH);
+  bool SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH);
+  bool SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CodeRateH, int CodeRateL, int Guard, int Transmission);
+  void SetId(int Nid, int Tid, int Sid, int Rid = 0);
+  void SetName(const char *Name, const char *ShortName, const char *Provider);
+  void SetPortalName(const char *PortalName);
+  void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int Tpid);
+  void SetCaIds(const int *CaIds); // list must be zero-terminated
+  void SetCaDescriptors(int Level);
+  void SetLinkChannels(cLinkChannels *LinkChannels);
+  void SetRefChannel(cChannel *RefChannel);
+  void SetPMTBuf(const unsigned char *buf, int len);
+  int  GetPMTBuf(unsigned char *buf);
+private:
+  int pmtlen;
+  unsigned char pmtbuf[4096];
+  };
+
+class cChannels : public cRwLock, public cConfig<cChannel> {
+private:
+  int maxNumber;
+  int modified;
+  int beingEdited;
+  cHash<cChannel> channelsHashSid;
+  void DeleteDuplicateChannels(void);
+public:
+  cChannels(void);
+  bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
+  void HashChannel(cChannel *Channel);
+  void UnhashChannel(cChannel *Channel);
+  int GetNextGroup(int Idx);   // Get next channel group
+  int GetPrevGroup(int Idx);   // Get previous channel group
+  int GetNextNormal(int Idx);  // Get next normal channel (not group)
+  int GetPrevNormal(int Idx);  // Get previous normal channel (not group)
+  void ReNumber(void);         // Recalculate 'number' based on channel type
+  cChannel *GetByNumber(int Number, int SkipGap = 0);
+  cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID);
+  cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false);
+  int BeingEdited(void) { return beingEdited; }
+  void IncBeingEdited(void) { beingEdited++; }
+  void DecBeingEdited(void) { beingEdited--; }
+  bool HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel = NULL);
+  bool SwitchTo(int Number);
+  int MaxNumber(void) { return maxNumber; }
+  void SetModified(bool ByUser = false);
+  int Modified(void);
+      ///< Returns 0 if no channels have been modified, 1 if an automatic
+      ///< modification has been made, and 2 if the user has made a modification.
+      ///< Calling this function resets the 'modified' flag to 0.
+  cChannel *NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid = 0);
+  };
+
+extern cChannels Channels;
+
+cString ChannelString(const cChannel *Channel, int Number);
+
+#endif //__CHANNELS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/ci.h b/contrib/sasc-ng/sc/include/vdr/ci.h
new file mode 100644 (file)
index 0000000..37514a5
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * ci.h: Common Interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: ci.h 1.23 2007/01/03 12:49:10 kls Exp $
+ */
+
+#ifndef __CI_H
+#define __CI_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "channels.h"
+#include "thread.h"
+#include "tools.h"
+
+#define MAX_CAM_SLOTS_PER_ADAPTER     8 // maximum possible value is 255
+#define MAX_CONNECTIONS_PER_CAM_SLOT  8 // maximum possible value is 254
+#define CAM_READ_TIMEOUT  50 // ms
+
+class cCiMMI;
+
+class cCiMenu {
+  friend class cCamSlot;
+  friend class cCiMMI;
+private:
+  enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
+  cCiMMI *mmi;
+  cMutex *mutex;
+  bool selectable;
+  char *titleText;
+  char *subTitleText;
+  char *bottomText;
+  char *entries[MAX_CIMENU_ENTRIES];
+  int numEntries;
+  bool AddEntry(char *s);
+  cCiMenu(cCiMMI *MMI, bool Selectable);
+public:
+  ~cCiMenu();
+  const char *TitleText(void) { return titleText; }
+  const char *SubTitleText(void) { return subTitleText; }
+  const char *BottomText(void) { return bottomText; }
+  const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
+  int NumEntries(void) { return numEntries; }
+  bool Selectable(void) { return selectable; }
+  void Select(int Index);
+  void Cancel(void);
+  void Abort(void);
+  bool HasUpdate(void);
+  };
+
+class cCiEnquiry {
+  friend class cCamSlot;
+  friend class cCiMMI;
+private:
+  cCiMMI *mmi;
+  cMutex *mutex;
+  char *text;
+  bool blind;
+  int expectedLength;
+  cCiEnquiry(cCiMMI *MMI);
+public:
+  ~cCiEnquiry();
+  const char *Text(void) { return text; }
+  bool Blind(void) { return blind; }
+  int ExpectedLength(void) { return expectedLength; }
+  void Reply(const char *s);
+  void Cancel(void);
+  void Abort(void);
+  };
+
+class cDevice;
+class cCamSlot;
+
+enum eModuleStatus { msNone, msReset, msPresent, msReady };
+
+class cCiAdapter : public cThread {
+  friend class cCamSlot;
+private:
+  cDevice *assignedDevice;
+  cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
+  void AddCamSlot(cCamSlot *CamSlot);
+       ///< Adds the given CamSlot to this CI adapter.
+protected:
+  virtual void Action(void);
+       ///< Handles the attached CAM slots in a separate thread.
+       ///< The derived class must call the Start() function to
+       ///< actually start CAM handling.
+  virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
+       ///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
+       ///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
+       ///< Returns the number of bytes read (in case of an error it will also
+       ///< return 0).
+  virtual void Write(const uint8_t *Buffer, int Length) = 0;
+       ///< Writes Length bytes of the given Buffer.
+  virtual bool Reset(int Slot) = 0;
+       ///< Resets the CAM in the given Slot.
+       ///< Returns true if the operation was successful.
+  virtual eModuleStatus ModuleStatus(int Slot) = 0;
+       ///< Returns the status of the CAM in the given Slot.
+  virtual bool Assign(cDevice *Device, bool Query = false) = 0;
+       ///< Assigns this adapter to the given Device, if this is possible.
+       ///< If Query is 'true', the adapter only checks whether it can be
+       ///< assigned to the Device, but doesn't actually assign itself to it.
+       ///< Returns true if the adapter can be assigned to the Device.
+       ///< If Device is NULL, the adapter will be unassigned from any
+       ///< device it was previously assigned to. The value of Query
+       ///< is ignored in that case, and this function always returns
+       ///< 'true'.
+public:
+  cCiAdapter(void);
+  virtual ~cCiAdapter();
+       ///< The derived class must call Cancel(3) in its destructor.
+  virtual bool Ready(void);
+       ///< Returns 'true' if all present CAMs in this adapter are ready.
+  };
+
+class cTPDU;
+class cCiTransportConnection;
+class cCiSession;
+class cCiCaProgramData;
+
+class cCamSlot : public cListObject {
+  friend class cCiAdapter;
+  friend class cCiTransportConnection;
+private:
+  cMutex mutex;
+  cCondVar processed;
+  cCiAdapter *ciAdapter;
+  int slotIndex;
+  int slotNumber;
+  cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1];  // connection numbering starts with 1
+  eModuleStatus lastModuleStatus;
+  time_t resetTime;
+  cTimeMs moduleCheckTimer;
+  bool resendPmt;
+  int source;
+  int transponder;
+  cList<cCiCaProgramData> caProgramList;
+  const int *GetCaSystemIds(void);
+  void SendCaPmt(uint8_t CmdId);
+  void NewConnection(void);
+  void DeleteAllConnections(void);
+  void Process(cTPDU *TPDU = NULL);
+  void Write(cTPDU *TPDU);
+  cCiSession *GetSessionByResourceId(uint32_t ResourceId);
+public:
+  cCamSlot(cCiAdapter *CiAdapter);
+       ///< Creates a new CAM slot for the given CiAdapter.
+       ///< The CiAdapter will take care of deleting the CAM slot,
+       ///< so the caller must not delete it!
+  virtual ~cCamSlot();
+  bool Assign(cDevice *Device, bool Query = false);
+       ///< Assigns this CAM slot to the given Device, if this is possible.
+       ///< If Query is 'true', the CI adapter of this slot only checks whether
+       ///< it can be assigned to the Device, but doesn't actually assign itself to it.
+       ///< Returns true if this slot can be assigned to the Device.
+       ///< If Device is NULL, the slot will be unassigned from any
+       ///< device it was previously assigned to. The value of Query
+       ///< is ignored in that case, and this function always returns
+       ///< 'true'.
+  cDevice *Device(void);
+       ///< Returns the device this CAM slot is currently assigned to.
+  int SlotIndex(void) { return slotIndex; }
+       ///< Returns the index of this CAM slot within its CI adapter.
+       ///< The first slot has an index of 0.
+  int SlotNumber(void) { return slotNumber; }
+       ///< Returns the number of this CAM slot within the whole system.
+       ///< The first slot has the number 1.
+  bool Reset(void);
+       ///< Resets the CAM in this slot.
+       ///< Returns true if the operation was successful.
+  eModuleStatus ModuleStatus(void);
+       ///< Returns the status of the CAM in this slot.
+  const char *GetCamName(void);
+       ///< Returns the name of the CAM in this slot, or NULL if there is
+       ///< no ready CAM in this slot.
+  bool Ready(void);
+       ///< Returns 'true' if the CAM in this slot is ready to decrypt.
+  bool HasMMI(void);
+       ///< Returns 'true' if the CAM in this slot has an active MMI.
+  bool HasUserIO(void);
+       ///< Returns true if there is a pending user interaction, which shall
+       ///< be retrieved via GetMenu() or GetEnquiry().
+  bool EnterMenu(void);
+       ///< Requests the CAM in this slot to start its menu.
+  cCiMenu *GetMenu(void);
+       ///< Gets a pending menu, or NULL if there is no menu.
+  cCiEnquiry *GetEnquiry(void);
+       ///< Gets a pending enquiry, or NULL if there is no enquiry.
+  int Priority(void);
+       ///< Returns the priority if the device this slot is currently assigned
+       ///< to, or -1 if it is not assigned to any device.
+  bool ProvidesCa(const int *CaSystemIds);
+       ///< Returns true if the CAM in this slot provides one of the given
+       ///< CaSystemIds. This doesn't necessarily mean that it will be
+       ///< possible to actually decrypt such a programme, since CAMs
+       ///< usually advertise several CA system ids, while the actual
+       ///< decryption is controlled by the smart card inserted into
+       ///< the CAM.
+  void AddPid(int ProgramNumber, int Pid, int StreamType);
+       ///< Adds the given PID information to the list of PIDs. A later call
+       ///< to SetPid() will (de)activate one of these entries.
+  void SetPid(int Pid, bool Active);
+       ///< Sets the given Pid (which has previously been added through a
+       ///< call to AddPid()) to Active. A later call to StartDecrypting() will
+       ///< send the full list of currently active CA_PMT entries to the CAM.
+  void AddChannel(const cChannel *Channel);
+       ///< Adds all PIDs if the given Channel to the current list of PIDs.
+       ///< If the source or transponder of the channel are different than
+       ///< what was given in a previous call to AddChannel(), any previously
+       ///< added PIDs will be cleared.
+  bool CanDecrypt(const cChannel *Channel);
+       ///< Returns true if there is a CAM in this slot that is able to decrypt
+       ///< the given Channel (or at least claims to be able to do so).
+       ///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
+       ///< CAMs don't reply to queries at all), we always return true if the
+       ///< CAM is currently not decrypting anything. If there is already a
+       ///< channel being decrypted, a call to CanDecrypt() checks whether the
+       ///< CAM can also decrypt the given channel. Only CAMs that have replied
+       ///< to the inital QUERY will perform this check at all. CAMs that never
+       ///< replied to the initial QUERY are assumed not to be able to handle
+       ///< more than one channel at a time.
+  void StartDecrypting(void);
+       ///< Triggers sending all currently active CA_PMT entries to the CAM,
+       ///< so that it will start decrypting.
+  void StopDecrypting(void);
+       ///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
+  bool IsDecrypting(void);
+       ///< Returns true if the CAM in this slot is currently used for decrypting.
+  };
+
+class cCamSlots : public cList<cCamSlot> {};
+
+extern cCamSlots CamSlots;
+
+class cChannelCamRelation;
+
+class cChannelCamRelations : public cList<cChannelCamRelation> {
+private:
+  cMutex mutex;
+  cChannelCamRelation *GetEntry(tChannelID ChannelID);
+  cChannelCamRelation *AddEntry(tChannelID ChannelID);
+  time_t lastCleanup;
+  void Cleanup(void);
+public:
+  cChannelCamRelations(void);
+  void Reset(int CamSlotNumber);
+  bool CamChecked(tChannelID ChannelID, int CamSlotNumber);
+  bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber);
+  void SetChecked(tChannelID ChannelID, int CamSlotNumber);
+  void SetDecrypt(tChannelID ChannelID, int CamSlotNumber);
+  void ClrChecked(tChannelID ChannelID, int CamSlotNumber);
+  void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber);
+  };
+
+extern cChannelCamRelations ChannelCamRelations;
+
+#endif //__CI_H
diff --git a/contrib/sasc-ng/sc/include/vdr/config.h b/contrib/sasc-ng/sc/include/vdr/config.h
new file mode 100644 (file)
index 0000000..aa4cfe8
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * config.h: Configuration file handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: config.h 1.292 2007/06/23 09:06:24 kls Exp $
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "i18n.h"
+#include "font.h"
+#include "tools.h"
+
+// VDR's own version number:
+
+#define VDRVERSION  "1.5.5"
+#define VDRVERSNUM   10505  // Version * 10000 + Major * 100 + Minor
+
+// The plugin API's version number:
+
+#define APIVERSION  "1.5.5"
+#define APIVERSNUM   10505  // Version * 10000 + Major * 100 + Minor
+
+// When loading plugins, VDR searches them by their APIVERSION, which
+// may be smaller than VDRVERSION in case there have been no changes to
+// VDR header files since the last APIVERSION. This allows compiled
+// plugins to work with newer versions of the core VDR as long as no
+// VDR header files have changed.
+
+#define MAXPRIORITY 99
+#define MAXLIFETIME 99
+
+#define MINOSDWIDTH  480
+#define MAXOSDWIDTH  672
+#define MINOSDHEIGHT 324
+#define MAXOSDHEIGHT 567
+
+#define MaxFileName 256
+#define MaxSkinName 16
+#define MaxThemeName 16
+
+class cCommand : public cListObject {
+private:
+  char *title;
+  char *command;
+  bool confirm;
+  static char *result;
+public:
+  cCommand(void);
+  virtual ~cCommand();
+  bool Parse(const char *s);
+  const char *Title(void) { return title; }
+  bool Confirm(void) { return confirm; }
+  const char *Execute(const char *Parameters = NULL);
+  };
+
+typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
+
+class cSVDRPhost : public cListObject {
+private:
+  struct in_addr addr;
+  in_addr_t mask;
+public:
+  cSVDRPhost(void);
+  bool Parse(const char *s);
+  bool Accepts(in_addr_t Address);
+  };
+
+template<class T> class cConfig : public cList<T> {
+private:
+  char *fileName;
+  bool allowComments;
+  void Clear(void)
+  {
+    free(fileName);
+    fileName = NULL;
+    cList<T>::Clear();
+  }
+public:
+  cConfig(void) { fileName = NULL; }
+  virtual ~cConfig() { free(fileName); }
+  const char *FileName(void) { return fileName; }
+  bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)
+  {
+    cConfig<T>::Clear();
+    if (FileName) {
+       free(fileName);
+       fileName = strdup(FileName);
+       allowComments = AllowComments;
+       }
+    bool result = !MustExist;
+    if (fileName && access(fileName, F_OK) == 0) {
+       isyslog("loading %s", fileName);
+       FILE *f = fopen(fileName, "r");
+       if (f) {
+          char *s;
+          int line = 0;
+          cReadLine ReadLine;
+          result = true;
+          while ((s = ReadLine.Read(f)) != NULL) {
+                line++;
+                if (allowComments) {
+                   char *p = strchr(s, '#');
+                   if (p)
+                      *p = 0;
+                   }
+                stripspace(s);
+                if (!isempty(s)) {
+                   T *l = new T;
+                   if (l->Parse(s))
+                      Add(l);
+                   else {
+                      esyslog("ERROR: error in %s, line %d", fileName, line);
+                      delete l;
+                      result = false;
+                      break;
+                      }
+                   }
+                }
+          fclose(f);
+          }
+       else {
+          LOG_ERROR_STR(fileName);
+          result = false;
+          }
+       }
+    if (!result)
+       fprintf(stderr, "vdr: error while reading '%s'\n", fileName);
+    return result;
+  }
+  bool Save(void)
+  {
+    bool result = true;
+    T *l = (T *)this->First();
+    cSafeFile f(fileName);
+    if (f.Open()) {
+       while (l) {
+             if (!l->Save(f)) {
+                result = false;
+                break;
+                }
+             l = (T *)l->Next();
+             }
+       if (!f.Close())
+          result = false;
+       }
+    else
+       result = false;
+    return result;
+  }
+  };
+
+class cCommands : public cConfig<cCommand> {};
+
+class cSVDRPhosts : public cConfig<cSVDRPhost> {
+public:
+  bool Acceptable(in_addr_t Address);
+  };
+
+extern cCommands Commands;
+extern cCommands RecordingCommands;
+extern cSVDRPhosts SVDRPhosts;
+
+class cSetupLine : public cListObject {
+private:
+  char *plugin;
+  char *name;
+  char *value;
+public:
+  cSetupLine(void);
+  cSetupLine(const char *Name, const char *Value, const char *Plugin = NULL);
+  virtual ~cSetupLine();
+  virtual int Compare(const cListObject &ListObject) const;
+  const char *Plugin(void) { return plugin; }
+  const char *Name(void) { return name; }
+  const char *Value(void) { return value; }
+  bool Parse(char *s);
+  bool Save(FILE *f);
+  };
+
+class cSetup : public cConfig<cSetupLine> {
+  friend class cPlugin; // needs to be able to call Store()
+private:
+  void StoreLanguages(const char *Name, int *Values);
+  bool ParseLanguages(const char *Value, int *Values);
+  bool Parse(const char *Name, const char *Value);
+  cSetupLine *Get(const char *Name, const char *Plugin = NULL);
+  void Store(const char *Name, const char *Value, const char *Plugin = NULL, bool AllowMultiple = false);
+  void Store(const char *Name, int Value, const char *Plugin = NULL);
+public:
+  // Also adjust cMenuSetup (menu.c) when adding parameters here!
+  int __BeginData__;
+  int OSDLanguage;
+  char OSDSkin[MaxSkinName];
+  char OSDTheme[MaxThemeName];
+  int PrimaryDVB;
+  int ShowInfoOnChSwitch;
+  int TimeoutRequChInfo;
+  int MenuScrollPage;
+  int MenuScrollWrap;
+  int MenuKeyCloses;
+  int MarkInstantRecord;
+  char NameInstantRecord[MaxFileName];
+  int InstantRecordTime;
+  int LnbSLOF;
+  int LnbFrequLo;
+  int LnbFrequHi;
+  int DiSEqC;
+  int SetSystemTime;
+  int TimeSource;
+  int TimeTransponder;
+  int MarginStart, MarginStop;
+  int AudioLanguages[I18nNumLanguages + 1];
+  int EPGLanguages[I18nNumLanguages + 1];
+  int EPGScanTimeout;
+  int EPGBugfixLevel;
+  int EPGLinger;
+  int SVDRPTimeout;
+  int ZapTimeout;
+  int ChannelEntryTimeout;
+  int PrimaryLimit;
+  int DefaultPriority, DefaultLifetime;
+  int PausePriority, PauseLifetime;
+  int UseSubtitle;
+  int UseVps;
+  int VpsMargin;
+  int RecordingDirs;
+  int VideoDisplayFormat;
+  int VideoFormat;
+  int UpdateChannels;
+  int UseDolbyDigital;
+  int ChannelInfoPos;
+  int ChannelInfoTime;
+  int OSDLeft, OSDTop, OSDWidth, OSDHeight;
+  int OSDMessageTime;
+  int UseSmallFont;
+  int AntiAlias;
+  char FontOsd[MAXFONTNAME];
+  char FontSml[MAXFONTNAME];
+  char FontFix[MAXFONTNAME];
+  int FontOsdSize;
+  int FontSmlSize;
+  int FontFixSize;
+  int MaxVideoFileSize;
+  int SplitEditedFiles;
+  int MinEventTimeout, MinUserInactivity;
+  time_t NextWakeupTime;
+  int MultiSpeedMode;
+  int ShowReplayMode;
+  int ResumeID;
+  int CurrentChannel;
+  int CurrentVolume;
+  int CurrentDolby;
+  int InitialChannel;
+  int InitialVolume;
+  int __EndData__;
+  cSetup(void);
+  cSetup& operator= (const cSetup &s);
+  bool Load(const char *FileName);
+  bool Save(void);
+  };
+
+extern cSetup Setup;
+
+#endif //__CONFIG_H
diff --git a/contrib/sasc-ng/sc/include/vdr/csa.h b/contrib/sasc-ng/sc/include/vdr/csa.h
new file mode 100644 (file)
index 0000000..02a0133
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * csa.h:
+ *
+ */
+
+#ifndef __CSA_H
+#define __CSA_H
+
+#include <linux/dvb/ca.h>
+
+//#define HAVE_SOFTCSA // make this patch detectable
+//#define SOFTCSA_VERS 100 // ff stands for FFdecsa
+
+class cCSA {
+private:
+  unsigned char even_ck[8], odd_ck[8];
+  unsigned char queued_even_ck[8], queued_odd_ck[8];
+  bool even_is_queued, odd_is_queued;
+  bool force_dequeueing;
+public:
+  cCSA(void);
+  void SetDescr(ca_descr_t *ca_descr);
+  void SetCaPid(ca_pid_t *ca_pid);
+  };
+
+#endif //__CSA_H
diff --git a/contrib/sasc-ng/sc/include/vdr/device.h b/contrib/sasc-ng/sc/include/vdr/device.h
new file mode 100644 (file)
index 0000000..22d50d5
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * device.h: The basic device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: device.h 1.81 2007/01/13 11:33:57 kls Exp $
+ */
+
+#ifndef __DEVICE_H
+#define __DEVICE_H
+
+#include "channels.h"
+#include "ci.h"
+#include "eit.h"
+#include "filter.h"
+#include "nit.h"
+#include "pat.h"
+//#include "ringbuffer.h"
+#include "sdt.h"
+#include "sections.h"
+#include "spu.h"
+#include "thread.h"
+#include "tools.h"
+
+#define MAXDEVICES         16 // the maximum number of devices in the system
+#define MAXPIDHANDLES      64 // the maximum number of different PIDs per device
+#define MAXRECEIVERS       16 // the maximum number of receivers per device
+#define MAXVOLUME         255
+#define VOLUMEDELTA         5 // used to increase/decrease the volume
+
+#define TS_SIZE          188
+#define TS_SYNC_BYTE     0x47
+#define PID_MASK_HI      0x1F
+
+enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+
+enum ePlayMode { pmNone,           // audio/video from decoder
+                 pmAudioVideo,     // audio/video from player
+                 pmAudioOnly,      // audio only from player, video from decoder
+                 pmAudioOnlyBlack, // audio only from player, no video (black screen)
+                 pmVideoOnly,      // video only from player, audio from decoder
+                 pmExtern_THIS_SHOULD_BE_AVOIDED
+                 // external player (e.g. MPlayer), release the device
+                 // WARNING: USE THIS MODE ONLY AS A LAST RESORT, IF YOU
+                 // ABSOLUTELY, POSITIVELY CAN'T IMPLEMENT YOUR PLAYER
+                 // THE WAY IT IS SUPPOSED TO WORK. FORCING THE DEVICE
+                 // TO RELEASE ITS FILES HANDLES (OR WHATEVER RESOURCES
+                 // IT MAY USE) TO ALLOW AN EXTERNAL PLAYER TO ACCESS
+                 // THEM MEANS THAT SUCH A PLAYER WILL NEED TO HAVE
+                 // DETAILED KNOWLEDGE ABOUT THE INTERNALS OF THE DEVICE
+                 // IN USE. AS A CONSEQUENCE, YOUR PLAYER MAY NOT WORK
+                 // IF A PARTICULAR VDR INSTALLATION USES A DEVICE NOT
+                 // KNOWN TO YOUR PLAYER.
+               };
+
+enum eVideoSystem { vsPAL,
+                    vsNTSC
+                  };
+
+enum eVideoDisplayFormat { vdfPanAndScan,
+                           vdfLetterBox,
+                           vdfCenterCutOut
+                         };
+
+enum eTrackType { ttNone,
+                  ttAudio,
+                  ttAudioFirst = ttAudio,
+                  ttAudioLast  = ttAudioFirst + 31, // MAXAPIDS - 1
+                  ttDolby,
+                  ttDolbyFirst = ttDolby,
+                  ttDolbyLast  = ttDolbyFirst + 15, // MAXDPIDS - 1
+                  /* future...
+                  ttSubtitle,
+                  ttSubtitleFirst = ttSubtitle,
+                  ttSubtitleLast  = ttSubtitleFirst + 7, // MAXSPIDS - 1
+                  */
+                  ttMaxTrackTypes
+                };
+
+#define IS_AUDIO_TRACK(t) (ttAudioFirst <= (t) && (t) <= ttAudioLast)
+#define IS_DOLBY_TRACK(t) (ttDolbyFirst <= (t) && (t) <= ttDolbyLast)
+
+struct tTrackId {
+  uint16_t id;                  // The PES packet id or the PID.
+  char language[MAXLANGCODE2];  // something like either "eng" or "deu+eng"
+  char description[32];         // something like "Dolby Digital 5.1"
+  };
+
+class cPlayer;
+class cReceiver;
+class cPesAssembler;
+
+/// The cDevice class is the base from which actual devices can be derived.
+
+class cDevice : public cThread {
+private:
+  static int numDevices;
+  static int useDevice;
+  static cDevice *device[MAXDEVICES];
+  static cDevice *primaryDevice;
+public:
+  static int NumDevices(void) { return numDevices; }
+         ///< Returns the total number of devices.
+  static bool WaitForAllDevicesReady(int Timeout = 0);
+         ///< Waits until all devices have become ready, or the given Timeout
+         ///< (seconds) has expired. While waiting, the Ready() function of each
+         ///< device is called in turn, until they all return true.
+         ///< \return True if all devices have become ready within the given
+         ///< timeout.
+  static void SetUseDevice(int n);
+         ///< Sets the 'useDevice' flag of the given device.
+         ///< If this function is not called before initializing, all devices
+         ///< will be used.
+  static bool UseDevice(int n) { return useDevice == 0 || (useDevice & (1 << n)) != 0; }
+         ///< Tells whether the device with the given card index shall be used in
+         ///< this instance of VDR.
+  static bool SetPrimaryDevice(int n);
+         ///< Sets the primary device to 'n'.
+         ///< \param n must be in the range 1...numDevices.
+         ///< \return true if this was possible.
+  static cDevice *PrimaryDevice(void) { return primaryDevice; }
+         ///< Returns the primary device.
+  static cDevice *ActualDevice(void);
+         ///< Returns the actual receiving device in case of Transfer Mode, or the
+         ///< primary device otherwise.
+  static cDevice *GetDevice(int Index);
+         ///< Gets the device with the given Index.
+         ///< \param Index must be in the range 0..numDevices-1.
+         ///< \return A pointer to the device, or NULL if the Index was invalid.
+  static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView);
+         ///< Returns a device that is able to receive the given Channel at the
+         ///< given Priority, with the least impact on active recordings and
+         ///< live viewing. The LiveView parameter tells whether the device will
+         ///< be used for live viewing or a recording.
+         ///< If the Channel is encrypted, a CAM slot that claims to be able to
+         ///< decrypt the channel is automatically selected and assigned to the
+         ///< returned device. Whether or not this combination of device and CAM
+         ///< slot is actually able to decrypt the channel can only be determined
+         ///< by checking the "scrambling control" bits of the received TS packets.
+         ///< The Action() function automatically does this and takes care that
+         ///< after detaching any receivers because the channel can't be decrypted,
+         ///< this device/CAM combination will be skipped in the next call to
+         ///< GetDevice().
+         ///< See also ProvidesChannel().
+  static void Shutdown(void);
+         ///< Closes down all devices.
+         ///< Must be called at the end of the program.
+private:
+  static int nextCardIndex;
+protected:
+  int cardIndex;
+  cDevice(void);
+  virtual ~cDevice();
+  virtual bool Ready(void);
+         ///< Returns true if this device is ready. Devices with conditional
+         ///< access hardware may need some time until they are up and running.
+         ///< This function is called in a loop at startup until all devices
+         ///< are ready (see WaitForAllDevicesReady()).
+  static int NextCardIndex(int n = 0);
+         ///< Calculates the next card index.
+         ///< Each device in a given machine must have a unique card index, which
+         ///< will be used to identify the device for assigning Ca parameters and
+         ///< deciding whether to actually use that device in this particular
+         ///< instance of VDR. Every time a new cDevice is created, it will be
+         ///< given the current nextCardIndex, and then nextCardIndex will be
+         ///< automatically incremented by 1. A derived class can determine whether
+         ///< a given device shall be used by checking UseDevice(NextCardIndex()).
+         ///< If a device is skipped, or if there are possible device indexes left
+         ///< after a derived class has set up all its devices, NextCardIndex(n)
+         ///< must be called, where n is the number of card indexes to skip.
+  virtual void MakePrimaryDevice(bool On);
+         ///< Informs a device that it will be the primary device. If there is
+         ///< anything the device needs to set up when it becomes the primary
+         ///< device (On = true) or to shut down when it no longer is the primary
+         ///< device (On = false), it should do so in this function.
+public:
+  bool IsPrimaryDevice(void) const { return this == primaryDevice; }
+  int CardIndex(void) const { return cardIndex; }
+         ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
+  int DeviceNumber(void) const;
+         ///< Returns the number of this device (0 ... numDevices).
+  virtual bool HasDecoder(void) const;
+         ///< Tells whether this device has an MPEG decoder.
+
+// SPU facilities
+
+public:
+  virtual cSpuDecoder *GetSpuDecoder(void);
+         ///< Returns a pointer to the device's SPU decoder (or NULL, if this
+         ///< device doesn't have an SPU decoder).
+
+// Channel facilities
+
+protected:
+  static int currentChannel;
+public:
+  virtual bool ProvidesSource(int Source) const;
+         ///< Returns true if this device can provide the given source.
+  virtual bool ProvidesTransponder(const cChannel *Channel) const;
+         ///< Returns true if this device can provide the transponder of the
+         ///< given Channel (which implies that it can provide the Channel's
+         ///< source).
+  virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const;
+         ///< Returns true if this is the only device that is able to provide
+         ///< the given channel's transponder.
+  virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
+         ///< Returns true if this device can provide the given channel.
+         ///< In case the device has cReceivers attached to it or it is the primary
+         ///< device, Priority is used to decide whether the caller's request can
+         ///< be honored.
+         ///< The special Priority value -1 will tell the caller whether this device
+         ///< is principally able to provide the given Channel, regardless of any
+         ///< attached cReceivers.
+         ///< If NeedsDetachReceivers is given, the resulting value in it will tell the
+         ///< caller whether or not it will have to detach any currently attached
+         ///< receivers from this device before calling SwitchChannel. Note
+         ///< that the return value in NeedsDetachReceivers is only meaningful if the
+         ///< function itself actually returns true.
+         ///< The default implementation always returns false, so a derived cDevice
+         ///< class that can provide channels must implement this function.
+  virtual bool IsTunedToTransponder(const cChannel *Channel);
+         ///< Returns true if this device is currently tuned to the given Channel's
+         ///< transponder.
+  virtual bool MaySwitchTransponder(void);
+         ///< Returns true if it is ok to switch the transponder on this device,
+         ///< without disturbing any other activities.
+  bool SwitchChannel(const cChannel *Channel, bool LiveView);
+         ///< Switches the device to the given Channel, initiating transfer mode
+         ///< if necessary.
+  static bool SwitchChannel(int Direction);
+         ///< Switches the primary device to the next available channel in the given
+         ///< Direction (only the sign of Direction is evaluated, positive values
+         ///< switch to higher channel numbers).
+private:
+  eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
+         ///< Sets the device to the given channel (general setup).
+protected:
+  virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+         ///< Sets the device to the given channel (actual physical setup).
+public:
+  static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
+         ///< Returns the number of the current channel on the primary device.
+  static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
+         ///< Sets the number of the current channel on the primary device, without
+         ///< actually switching to it. This can be used to correct the current
+         ///< channel number while replaying.
+  void ForceTransferMode(void);
+         ///< Forces the device into transfermode for the current channel.
+  virtual bool HasLock(int TimeoutMs = 0);
+         ///< Returns true if the device has a lock on the requested transponder.
+         ///< Default is true, a specific device implementation may return false
+         ///< to indicate that it is not ready yet.
+         ///< If TimeoutMs is not zero, waits for the given number of milliseconds
+         ///< before returning false.
+  virtual bool HasProgramme(void);
+         ///< Returns true if the device is currently showing any programme to
+         ///< the user, either through replaying or live.
+
+// PID handle facilities
+
+private:
+  virtual void Action(void);
+public:
+  enum ePidType { ptAudio, ptVideo, ptPcr, ptTeletext, ptDolby, ptOther };
+protected:
+  class cPidHandle {
+  public:
+    int pid;
+    int handle;
+    int used;
+    cPidHandle(void) { pid = used = 0; handle = -1; }
+    };
+  cPidHandle pidHandles[MAXPIDHANDLES];
+  bool HasPid(int Pid) const;
+         ///< Returns true if this device is currently receiving the given PID.
+public:
+  bool AddPid(int Pid, ePidType PidType = ptOther);
+         ///< Adds a PID to the set of PIDs this device shall receive.
+protected:
+  void DelPid(int Pid, ePidType PidType = ptOther);
+         ///< Deletes a PID from the set of PIDs this device shall receive.
+  virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+         ///< Does the actual PID setting on this device.
+         ///< On indicates whether the PID shall be added or deleted.
+         ///< Handle->handle can be used by the device to store information it
+         ///< needs to receive this PID (for instance a file handle).
+         ///< Handle->used indicates how many receivers are using this PID.
+         ///< Type indicates some special types of PIDs, which the device may
+         ///< need to set in a specific way.
+
+// Section filter facilities
+
+private:
+  cSectionHandler *sectionHandler;
+  cEitFilter *eitFilter;
+  cPatFilter *patFilter;
+  cSdtFilter *sdtFilter;
+  cNitFilter *nitFilter;
+protected:
+  void StartSectionHandler(void);
+       ///< A derived device that provides section data must call
+       ///< this function to actually set up the section handler.
+public:
+  virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+       ///< Opens a file handle for the given filter data.
+       ///< A derived device that provides section data must
+       ///< implement this function.
+  void AttachFilter(cFilter *Filter);
+       ///< Attaches the given filter to this device.
+  void Detach(cFilter *Filter);
+       ///< Detaches the given filter from this device.
+
+// Common Interface facilities:
+
+private:
+  time_t startScrambleDetection;
+  cCamSlot *camSlot;
+public:
+  virtual bool HasCi(void);
+         ///< Returns true if this device has a Common Interface.
+  void SetCamSlot(cCamSlot *CamSlot);
+         ///< Sets the given CamSlot to be used with this device.
+  cCamSlot *CamSlot(void) const { return camSlot; }
+         ///< Returns the CAM slot that is currently used with this device,
+         ///< or NULL if no CAM slot is in use.
+
+// Image Grab facilities
+
+public:
+  virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
+         ///< Grabs the currently visible screen image.
+         ///< \param Size The size of the returned data block.
+         ///< \param Jpeg If true will write a JPEG file. Otherwise a PNM file will be written.
+         ///< \param Quality The compression factor for JPEG. 1 will create a very blocky
+         ///<        and small image, 70..80 will yield reasonable quality images while keeping the
+         ///<        image file size around 50 KB for a full frame. The default will create a big
+         ///<        but very high quality image.
+         ///< \param SizeX The number of horizontal pixels in the frame (default is the current screen width).
+         ///< \param SizeY The number of vertical pixels in the frame (default is the current screen height).
+         ///< \return A pointer to the grabbed image data, or NULL in case of an error.
+         ///< The caller takes ownership of the returned memory and must free() it once it isn't needed any more.
+  bool GrabImageFile(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
+         ///< Calls GrabImage() and stores the resulting image in a file with the given name.
+         ///< \return True if all went well.
+         ///< The caller is responsible for making sure that the given file name
+         ///< doesn't lead to overwriting any important other file.
+
+// Video format facilities
+
+public:
+  virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat);
+         ///< Sets the video display format to the given one (only useful
+         ///< if this device has an MPEG decoder).
+         ///< A derived class must first call the base class function!
+  virtual void SetVideoFormat(bool VideoFormat16_9);
+         ///< Sets the output video format to either 16:9 or 4:3 (only useful
+         ///< if this device has an MPEG decoder).
+  virtual eVideoSystem GetVideoSystem(void);
+         ///< Returns the video system of the currently displayed material
+         ///< (default is PAL).
+
+// Track facilities
+
+private:
+  tTrackId availableTracks[ttMaxTrackTypes];
+  eTrackType currentAudioTrack;
+  cMutex mutexCurrentAudioTrack;
+  int currentAudioTrackMissingCount;
+  bool pre_1_3_19_PrivateStream;
+protected:
+  virtual void SetAudioTrackDevice(eTrackType Type);
+       ///< Sets the current audio track to the given value.
+public:
+  void ClrAvailableTracks(bool DescriptionsOnly = false, bool IdsOnly = false);
+       ///< Clears the list of currently availabe tracks. If DescriptionsOnly
+       ///< is true, only the track descriptions will be cleared. With IdsOnly
+       ///< set to true only the ids will be cleared. IdsOnly is only taken
+       ///< into account if DescriptionsOnly is false.
+  bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = NULL);
+       ///< Sets the track of the given Type and Index to the given values.
+       ///< Type must be one of the basic eTrackType values, like ttAudio or ttDolby.
+       ///< Index tells which track of the given basic type is meant.
+       ///< If Id is 0 any existing id will be left untouched and only the
+       ///< given Language and Description will be set.
+       ///< \return Returns true if the track was set correctly, false otherwise.
+  const tTrackId *GetTrack(eTrackType Type);
+       ///< Returns a pointer to the given track id, or NULL if Type is not
+       ///< less than ttMaxTrackTypes.
+  int NumAudioTracks(void) const;
+       ///< Returns the number of audio tracks that are currently available.
+       ///< This is just for information, to quickly find out whether there
+       ///< is more than one audio track.
+  eTrackType GetCurrentAudioTrack(void) { return currentAudioTrack; }
+  bool SetCurrentAudioTrack(eTrackType Type);
+       ///< Sets the current audio track to the given Type.
+       ///< \return Returns true if Type is a valid audio track, false otherwise.
+  void EnsureAudioTrack(bool Force = false);
+       ///< Makes sure an audio track is selected that is actually available.
+       ///< If Force is true, the language and Dolby Digital settings will
+       ///< be verified even if the current audio track is available.
+
+// Audio facilities
+
+private:
+  bool mute;
+  int volume;
+protected:
+  virtual int GetAudioChannelDevice(void);
+       ///< Gets the current audio channel, which is stereo (0), mono left (1) or
+       ///< mono right (2).
+  virtual void SetAudioChannelDevice(int AudioChannel);
+       ///< Sets the audio channel to stereo (0), mono left (1) or mono right (2).
+  virtual void SetVolumeDevice(int Volume);
+       ///< Sets the audio volume on this device (Volume = 0...255).
+  virtual void SetDigitalAudioDevice(bool On);
+       ///< Tells the actual device that digital audio output shall be switched
+       ///< on or off.
+public:
+  bool IsMute(void) const { return mute; }
+  bool ToggleMute(void);
+       ///< Turns the volume off or on and returns the new mute state.
+  int GetAudioChannel(void);
+       ///< Gets the current audio channel, which is stereo (0), mono left (1) or
+       ///< mono right (2).
+  void SetAudioChannel(int AudioChannel);
+       ///< Sets the audio channel to stereo (0), mono left (1) or mono right (2).
+       ///< Any other values will be silently ignored.
+  void SetVolume(int Volume, bool Absolute = false);
+       ///< Sets the volume to the given value, either absolutely or relative to
+       ///< the current volume.
+  static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???
+
+// Player facilities
+
+private:
+  cPlayer *player;
+  cPesAssembler *pesAssembler;
+protected:
+  virtual bool CanReplay(void) const;
+       ///< Returns true if this device can currently start a replay session.
+  virtual bool SetPlayMode(ePlayMode PlayMode);
+       ///< Sets the device into the given play mode.
+       ///< \return true if the operation was successful.
+  virtual int PlayVideo(const uchar *Data, int Length);
+       ///< Plays the given data block as video.
+       ///< Data points to exactly one complete PES packet of the given Length.
+       ///< PlayVideo() shall process the packet either as a whole (returning
+       ///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
+       ///< \return Returns the number of bytes actually taken from Data, or -1
+       ///< in case of an error.
+  virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
+       ///< Plays the given data block as audio.
+       ///< Data points to exactly one complete PES packet of the given Length.
+       ///< Id indicates the type of audio data this packet holds.
+       ///< PlayAudio() shall process the packet either as a whole (returning
+       ///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
+       ///< \return Returns the number of bytes actually taken from Data, or -1
+       ///< in case of an error.
+  virtual int PlayPesPacket(const uchar *Data, int Length, bool VideoOnly = false);
+       ///< Plays the single PES packet in Data with the given Length.
+       ///< If VideoOnly is true, only the video will be displayed,
+       ///< which is necessary for trick modes like 'fast forward'.
+       ///< Data must point to one single, complete PES packet.
+public:
+  virtual int64_t GetSTC(void);
+       ///< Gets the current System Time Counter, which can be used to
+       ///< synchronize audio and video. If this device is unable to
+       ///< provide the STC, -1 will be returned.
+  virtual void TrickSpeed(int Speed);
+       ///< Sets the device into a mode where replay is done slower.
+       ///< Every single frame shall then be displayed the given number of
+       ///< times.
+  virtual void Clear(void);
+       ///< Clears all video and audio data from the device.
+       ///< A derived class must call the base class function to make sure
+       ///< all registered cAudio objects are notified.
+  virtual void Play(void);
+       ///< Sets the device into play mode (after a previous trick
+       ///< mode).
+  virtual void Freeze(void);
+       ///< Puts the device into "freeze frame" mode.
+  virtual void Mute(void);
+       ///< Turns off audio while replaying.
+       ///< A derived class must call the base class function to make sure
+       ///< all registered cAudio objects are notified.
+  virtual void StillPicture(const uchar *Data, int Length);
+       ///< Displays the given I-frame as a still picture.
+  virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
+       ///< Returns true if the device itself or any of the file handles in
+       ///< Poller is ready for further action.
+       ///< If TimeoutMs is not zero, the device will wait up to the given number
+       ///< of milleseconds before returning in case it can't accept any data.
+  virtual bool Flush(int TimeoutMs = 0);
+       ///< Returns true if the device's output buffers are empty, i. e. any
+       ///< data which was bufferd so far has been processed.
+       ///< If TimeoutMs is not zero, the device will wait up to the given
+       ///< number of milliseconds before returning in case there is still
+       ///< data in the buffers..
+  virtual int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
+       ///< Plays all valid PES packets in Data with the given Length.
+       ///< If Data is NULL any leftover data from a previous call will be
+       ///< discarded. If VideoOnly is true, only the video will be displayed,
+       ///< which is necessary for trick modes like 'fast forward'.
+       ///< Data should point to a sequence of complete PES packets. If the
+       ///< last packet in Data is not complete, it will be copied and combined
+       ///< to a complete packet with data from the next call to PlayPes().
+       ///< That way any functions called from within PlayPes() will be
+       ///< guaranteed to always receive complete PES packets.
+  bool Replaying(void) const;
+       ///< Returns true if we are currently replaying.
+  bool Transferring(void) const;
+       ///< Returns true if we are currently in Transfer Mode.
+  void StopReplay(void);
+       ///< Stops the current replay session (if any).
+  bool AttachPlayer(cPlayer *Player);
+       ///< Attaches the given player to this device.
+  void Detach(cPlayer *Player);
+       ///< Detaches the given player from this device.
+
+// Receiver facilities
+
+private:
+  cMutex mutexReceiver;
+  cReceiver *receiver[MAXRECEIVERS];
+public:
+  int Priority(void) const;
+      ///< Returns the priority of the current receiving session (0..MAXPRIORITY),
+      ///< or -1 if no receiver is currently active. The primary device will
+      ///< always return at least Setup.PrimaryLimit-1.
+protected:
+  virtual bool OpenDvr(void);
+      ///< Opens the DVR of this device and prepares it to deliver a Transport
+      ///< Stream for use in a cReceiver.
+  virtual void CloseDvr(void);
+      ///< Shuts down the DVR.
+  virtual bool GetTSPacket(uchar *&Data);
+      ///< Gets exactly one TS packet from the DVR of this device and returns
+      ///< a pointer to it in Data. Only the first 188 bytes (TS_SIZE) Data
+      ///< points to are valid and may be accessed. If there is currently no
+      ///< new data available, Data will be set to NULL. The function returns
+      ///< false in case of a non recoverable error, otherwise it returns true,
+      ///< even if Data is NULL.
+public:
+  bool Receiving(bool CheckAny = false) const;
+       ///< Returns true if we are currently receiving.
+  bool AttachReceiver(cReceiver *Receiver);
+       ///< Attaches the given receiver to this device.
+  void Detach(cReceiver *Receiver);
+       ///< Detaches the given receiver from this device.
+  void DetachAll(int Pid);
+       ///< Detaches all receivers from this device for this pid.
+  void DetachAllReceivers(void);
+       ///< Detaches all receivers from this device.
+  };
+
+/// Derived cDevice classes that can receive channels will have to provide
+/// Transport Stream (TS) packets one at a time. cTSBuffer implements a
+/// simple buffer that allows the device to read a larger amount of data
+/// from the driver with each call to Read(), thus avoiding the overhead
+/// of getting each TS packet separately from the driver. It also makes
+/// sure the returned data points to a TS packet and automatically
+/// re-synchronizes after broken packets.
+
+class cTSBuffer : public cThread {
+private:
+  int f;
+  int cardIndex;
+  bool delivered;
+  //cRingBufferLinear *ringBuffer;
+  virtual void Action(void);
+public:
+  cTSBuffer(int File, int Size, int CardIndex);
+  ~cTSBuffer();
+  uchar *Get(void);
+  };
+
+#endif //__DEVICE_H
diff --git a/contrib/sasc-ng/sc/include/vdr/dload.h b/contrib/sasc-ng/sc/include/vdr/dload.h
new file mode 100644 (file)
index 0000000..334ea53
--- /dev/null
@@ -0,0 +1,23 @@
+#include <sys/types.h>
+#include <dlfcn.h>
+
+       /* dl*() stub routines for static compilation.  Prepared from
+          /usr/include/dlfcn.h by Hal Pomeranz <hal@deer-run.com> */
+
+static void *dlopen(const char *str, int x) {}
+static void *dlsym(void *ptr, const char *str) {}
+static int dlclose(void *ptr) {}
+static char *dlerror() {}
+static void *dlmopen(Lmid_t a, const char *str, int x) {}
+static int dladdr(void *ptr1, Dl_info *ptr2) {}
+static int dldump(const char *str1, const char *str2, int x) {}
+static int dlinfo(void *ptr1, int x, void *ptr2) {}
+
+static void *_dlopen(const char *str, int x) {}
+static void *_dlsym(void *ptr, const char *str) {}
+static int _dlclose(void *ptr) {}
+static char *_dlerror() {}
+static void *_dlmopen(Lmid_t a, const char *str, int x) {}
+static int _dladdr(void *ptr1, Dl_info *ptr2) {}
+static int _dldump(const char *str1, const char *str2, int x) {}
+static int _dlinfo(void *ptr1, int x, void *ptr2) {}
diff --git a/contrib/sasc-ng/sc/include/vdr/dvbdevice.h b/contrib/sasc-ng/sc/include/vdr/dvbdevice.h
new file mode 100644 (file)
index 0000000..72d6a2c
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * dvbdevice.h: The DVB device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbdevice.h 1.44 2007/02/25 12:23:57 kls Exp $
+ */
+
+#ifndef __DVBDEVICE_H
+#define __DVBDEVICE_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/version.h>
+#include "device.h"
+#include "dvbspu.h"
+
+#if DVB_API_VERSION < 3 
+#error VDR requires Linux DVB driver API version 3 or higher! 
+#endif
+
+#define MAXDVBDEVICES  8
+
+class cDvbTuner;
+
+/// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
+
+class cDvbDevice : public cDevice {
+private:
+  static bool Probe(const char *FileName);
+         ///< Probes for existing DVB devices.
+public:
+  static bool Initialize(void);
+         ///< Initializes the DVB devices.
+         ///< Must be called before accessing any DVB functions.
+         ///< \return True if any devices are available.
+private:
+  fe_type_t frontendType;
+  int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc, fd_ca;
+protected:
+  virtual void MakePrimaryDevice(bool On);
+public:
+  cDvbDevice(int n);
+  virtual ~cDvbDevice();
+  virtual bool Ready(void);
+  virtual bool HasDecoder(void) const;
+
+// Common Interface facilities:
+
+private:
+  cCiAdapter *ciAdapter;
+
+// SPU facilities
+
+private:
+  cDvbSpuDecoder *spuDecoder;
+public:
+  virtual cSpuDecoder *GetSpuDecoder(void);
+
+// Channel facilities
+
+private:
+  cDvbTuner *dvbTuner;
+  void TurnOffLiveMode(bool LiveView);
+public:
+  virtual bool ProvidesSource(int Source) const;
+  virtual bool ProvidesTransponder(const cChannel *Channel) const;
+  virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
+  virtual bool IsTunedToTransponder(const cChannel *Channel);
+public:
+  virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+public:
+  virtual bool HasLock(int TimeoutMs = 0);
+
+// PID handle facilities
+
+private:
+  bool SetAudioBypass(bool On);
+protected:
+  virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+
+// Section filter facilities
+
+protected:
+  virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
+
+// Common Interface facilities:
+
+public:
+  virtual bool HasCi(void);
+
+// Image Grab facilities
+
+private:
+  static int devVideoOffset;
+  int devVideoIndex;
+public:
+  virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
+
+// Video format facilities
+
+public:
+  virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat);
+  virtual void SetVideoFormat(bool VideoFormat16_9);
+  virtual eVideoSystem GetVideoSystem(void);
+
+// Track facilities
+
+protected:
+  virtual void SetAudioTrackDevice(eTrackType Type);
+
+// Audio facilities
+
+private:
+  bool digitalAudio;
+  static int setTransferModeForDolbyDigital;
+protected:
+  virtual int GetAudioChannelDevice(void);
+  virtual void SetAudioChannelDevice(int AudioChannel);
+  virtual void SetVolumeDevice(int Volume);
+  virtual void SetDigitalAudioDevice(bool On);
+public:
+  static void SetTransferModeForDolbyDigital(int Mode);
+         ///< Controls how the DVB device handles Transfer Mode when replaying
+         ///< Dolby Digital audio.
+         ///< 0 = don't set "audio bypass" in driver/firmware, don't force Transfer Mode
+         ///< 1 = set "audio bypass" in driver/firmware, force Transfer Mode (default)
+         ///< 2 = don't set "audio bypass" in driver/firmware, force Transfer Mode
+
+// Player facilities
+
+protected:
+  ePlayMode playMode;
+  virtual bool CanReplay(void) const;
+  virtual bool SetPlayMode(ePlayMode PlayMode);
+  virtual int PlayVideo(const uchar *Data, int Length);
+  virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
+public:
+  virtual int64_t GetSTC(void);
+  virtual void TrickSpeed(int Speed);
+  virtual void Clear(void);
+  virtual void Play(void);
+  virtual void Freeze(void);
+  virtual void Mute(void);
+  virtual void StillPicture(const uchar *Data, int Length);
+  virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
+  virtual bool Flush(int TimeoutMs = 0);
+
+// Receiver facilities
+
+private:
+  cTSBuffer *tsBuffer;
+protected:
+  virtual bool OpenDvr(void);
+  virtual void CloseDvr(void);
+  virtual bool GetTSPacket(uchar *&Data);
+  };
+
+#endif //__DVBDEVICE_H
diff --git a/contrib/sasc-ng/sc/include/vdr/dvbspu.h b/contrib/sasc-ng/sc/include/vdr/dvbspu.h
new file mode 100644 (file)
index 0000000..1812b14
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * SPU decoder for DVB devices
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * parts of this file are derived from the OMS program.
+ *
+ * $Id: dvbspu.h 1.12 2006/04/17 12:47:29 kls Exp $
+ */
+
+#ifndef __DVBSPU_H
+#define __DVBSPU_H
+
+#include <inttypes.h>
+#include "osd.h"
+#include "spu.h"
+#include "thread.h"
+
+typedef struct sDvbSpuPalDescr {
+    uint8_t index;
+    uint8_t trans;
+
+    bool operator != (const sDvbSpuPalDescr pd) const {
+        return index != pd.index && trans != pd.trans;
+    };
+} aDvbSpuPalDescr[4];
+
+typedef struct sDvbSpuRect {
+    int x1, y1;
+    int x2, y2;
+
+    int width() {
+        return x2 - x1 + 1;
+    };
+    int height() {
+        return y2 - y1 + 1;
+    };
+
+    bool operator != (const sDvbSpuRect r) const {
+        return r.x1 != x1 || r.y1 != y1 || r.x2 != x2 || r.y2 != y2;
+    };
+}
+
+sDvbSpuRect;
+
+// --- cDvbSpuPalette---------------------------------------------------------
+
+class cDvbSpuPalette {
+  private:
+    uint32_t palette[16];
+
+  private:
+    uint32_t yuv2rgb(uint32_t yuv_color);
+
+  public:
+    void setPalette(const uint32_t * pal);
+    uint32_t getColor(uint8_t idx, uint8_t trans) const;
+};
+
+// --- cDvbSpuBitmap----------------------------------------------------------
+
+class cDvbSpuBitmap {
+
+  public:
+  private:
+    sDvbSpuRect bmpsize;
+    sDvbSpuRect minsize[4];
+    uint8_t *bmp;
+
+  private:
+    void putPixel(int xp, int yp, int len, uint8_t colorid);
+    void putFieldData(int field, uint8_t * data, uint8_t * endp);
+
+  public:
+     cDvbSpuBitmap(sDvbSpuRect size,
+                   uint8_t * fodd, uint8_t * eodd,
+                   uint8_t * feven, uint8_t * eeven);
+    ~cDvbSpuBitmap();
+
+    bool getMinSize(const aDvbSpuPalDescr paldescr,
+                    sDvbSpuRect & size) const;
+    cBitmap *getBitmap(const aDvbSpuPalDescr paldescr,
+                       const cDvbSpuPalette & pal,
+                       sDvbSpuRect & size) const;
+};
+
+// --- cDvbSpuDecoder---------------------------------------------------------
+
+class cDvbSpuDecoder:public cSpuDecoder {
+  private:
+    cOsd *osd;
+    cMutex mutex;
+
+    // processing state
+    uint8_t *spu;
+    uint32_t spupts;
+    bool clean;
+    bool ready;
+
+    enum spFlag { spNONE, spHIDE, spSHOW, spMENU };
+    spFlag state;
+
+     cSpuDecoder::eScaleMode scaleMode;
+
+    //highligh area
+    bool highlight;
+    sDvbSpuRect hlpsize;
+    aDvbSpuPalDescr hlpDescr;
+
+    //palette
+    cDvbSpuPalette palette;
+
+    // spu info's
+    sDvbSpuRect size;
+    aDvbSpuPalDescr palDescr;
+
+    uint16_t DCSQ_offset;
+    uint16_t prev_DCSQ_offset;
+
+    cDvbSpuBitmap *spubmp;
+    bool allowedShow;
+  private:
+    int cmdOffs(void) {
+        return ((spu[2] << 8) | spu[3]);
+    };
+    int spuSize(void) {
+        return ((spu[0] << 8) | spu[1]);
+    };
+
+    sDvbSpuRect CalcAreaSize(sDvbSpuRect fgsize, cBitmap *fgbmp, sDvbSpuRect bgsize, cBitmap *bgbmp);
+
+  public:
+    cDvbSpuDecoder();
+    ~cDvbSpuDecoder();
+
+    int setTime(uint32_t pts);
+
+    cSpuDecoder::eScaleMode getScaleMode(void) { return scaleMode; }
+    void setScaleMode(cSpuDecoder::eScaleMode ScaleMode);
+    void setPalette(uint32_t * pal);
+    void setHighlight(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey,
+                      uint32_t palette);
+    void clearHighlight(void);
+    void Empty(void);
+    void Hide(void);
+    void Draw(void);
+    bool IsVisible(void) { return osd != NULL; }
+    void processSPU(uint32_t pts, uint8_t * buf, bool AllowedShow);
+};
+
+// --- cDvbSpuPalette --------------------------------------------------------
+
+inline uint32_t cDvbSpuPalette::yuv2rgb(uint32_t yuv_color)
+{
+    int Y, Cb, Cr;
+    int Ey, Epb, Epr;
+    int Eg, Eb, Er;
+
+    Y = (yuv_color >> 16) & 0xff;
+    Cb = (yuv_color) & 0xff;
+    Cr = (yuv_color >> 8) & 0xff;
+
+    Ey = (Y - 16);
+    Epb = (Cb - 128);
+    Epr = (Cr - 128);
+    /* ITU-R 709
+       Eg = (298*Ey - 55*Epb - 137*Epr)/256;
+       Eb = (298*Ey + 543*Epb)/256;
+       Er = (298*Ey + 460*Epr)/256;
+     */
+    /* FCC ~= mediaLib */
+    Eg = (298 * Ey - 100 * Epb - 208 * Epr) / 256;
+    Eb = (298 * Ey + 516 * Epb) / 256;
+    Er = (298 * Ey + 408 * Epr) / 256;
+
+    if (Eg > 255)
+        Eg = 255;
+    if (Eg < 0)
+        Eg = 0;
+
+    if (Eb > 255)
+        Eb = 255;
+    if (Eb < 0)
+        Eb = 0;
+
+    if (Er > 255)
+        Er = 255;
+    if (Er < 0)
+        Er = 0;
+
+    return Eb | (Eg << 8) | (Er << 16);
+}
+
+inline uint32_t cDvbSpuPalette::getColor(uint8_t idx, uint8_t trans) const
+{
+    uint8_t t = trans == 0x0f ? 0xff : trans << 4;
+    return palette[idx] | (t << 24);
+}
+
+#endif                          // __DVBSPU_H
diff --git a/contrib/sasc-ng/sc/include/vdr/eit.h b/contrib/sasc-ng/sc/include/vdr/eit.h
new file mode 100644 (file)
index 0000000..c57dd51
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * eit.h: EIT section filter
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: eit.h 1.30 2003/12/21 14:51:50 kls Exp $
+ */
+
+#ifndef __EIT_H
+#define __EIT_H
+
+#include "filter.h"
+
+class cEitFilter : public cFilter {
+protected:
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+public:
+  cEitFilter(void);
+  };
+
+#endif //__EIT_H
diff --git a/contrib/sasc-ng/sc/include/vdr/epg.h b/contrib/sasc-ng/sc/include/vdr/epg.h
new file mode 100644 (file)
index 0000000..792bb13
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * epg.h: Electronic Program Guide
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Original version (as used in VDR before 1.3.0) written by
+ * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
+ *
+ * $Id: epg.h 1.35 2006/10/07 13:47:19 kls Exp $
+ */
+
+#ifndef __EPG_H
+#define __EPG_H
+
+#include "channels.h"
+#include "thread.h"
+#include "tools.h"
+
+#define MAXEPGBUGFIXLEVEL 3
+
+enum eDumpMode { dmAll, dmPresent, dmFollowing, dmAtTime };
+
+struct tComponent {
+  uchar stream;
+  uchar type;
+  char language[MAXLANGCODE2];
+  char *description;
+  cString ToString(void);
+  bool FromString(const char *s);
+  };
+
+class cComponents {
+private:
+  int numComponents;
+  tComponent *components;
+  void Realloc(int Index);
+public:
+  cComponents(void);
+  ~cComponents(void);
+  int NumComponents(void) const { return numComponents; }
+  void SetComponent(int Index, const char *s);
+  void SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description);
+  tComponent *Component(int Index) const { return (Index < numComponents) ? &components[Index] : NULL; }
+  tComponent *GetComponent(int Index, uchar Stream, uchar Type); // Gets the Index'th component of Stream and Type, skipping other components
+                                                                 // In case of an audio stream the 'type' check actually just distinguishes between "normal" and "Dolby Digital"
+  };
+
+class cSchedule;
+
+typedef u_int32_t tEventID;
+
+class cEvent : public cListObject {
+  friend class cSchedule;
+private:
+  cSchedule *schedule;     // The Schedule this event belongs to
+  tEventID eventID;        // Event ID of this event
+  uchar tableID;           // Table ID this event came from
+  uchar version;           // Version number of section this event came from
+  int runningStatus;       // 0=undefined, 1=not running, 2=starts in a few seconds, 3=pausing, 4=running
+  char *title;             // Title of this event
+  char *shortText;         // Short description of this event (typically the episode name in case of a series)
+  char *description;       // Description of this event
+  cComponents *components; // The stream components of this event
+  time_t startTime;        // Start time of this event
+  int duration;            // Duration of this event in seconds
+  time_t vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+  time_t seen;             // When this event was last seen in the data stream
+public:
+  cEvent(tEventID EventID);
+  ~cEvent();
+  virtual int Compare(const cListObject &ListObject) const;
+  tChannelID ChannelID(void) const;
+  const cSchedule *Schedule(void) const { return schedule; }
+  tEventID EventID(void) const { return eventID; }
+  uchar TableID(void) const { return tableID; }
+  uchar Version(void) const { return version; }
+  int RunningStatus(void) const { return runningStatus; }
+  const char *Title(void) const { return title; }
+  const char *ShortText(void) const { return shortText; }
+  const char *Description(void) const { return description; }
+  const cComponents *Components(void) const { return components; }
+  time_t StartTime(void) const { return startTime; }
+  time_t EndTime(void) const { return startTime + duration; }
+  int Duration(void) const { return duration; }
+  time_t Vps(void) const { return vps; }
+  time_t Seen(void) const { return seen; }
+  bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
+  bool HasTimer(void) const;
+  bool IsRunning(bool OrAboutToStart = false) const;
+  cString GetDateString(void) const;
+  cString GetTimeString(void) const;
+  cString GetEndTimeString(void) const;
+  cString GetVpsString(void) const;
+  void SetEventID(tEventID EventID);
+  void SetTableID(uchar TableID);
+  void SetVersion(uchar Version);
+  void SetRunningStatus(int RunningStatus, cChannel *Channel = NULL);
+  void SetTitle(const char *Title);
+  void SetShortText(const char *ShortText);
+  void SetDescription(const char *Description);
+  void SetComponents(cComponents *Components); // Will take ownership of Components!
+  void SetStartTime(time_t StartTime);
+  void SetDuration(int Duration);
+  void SetVps(time_t Vps);
+  void SetSeen(void);
+  cString ToDescr(void) const;
+  void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const;
+  bool Parse(char *s);
+  static bool Read(FILE *f, cSchedule *Schedule);
+  void FixEpgBugs(void);
+  };
+
+class cSchedules;
+
+class cSchedule : public cListObject  {
+private:
+  tChannelID channelID;
+  cList<cEvent> events;
+  cHash<cEvent> eventsHashID;
+  cHash<cEvent> eventsHashStartTime;
+  bool hasRunning;
+  time_t modified;
+  time_t presentSeen;
+public:
+  cSchedule(tChannelID ChannelID);
+  tChannelID ChannelID(void) const { return channelID; }
+  time_t Modified(void) const { return modified; }
+  time_t PresentSeen(void) const { return presentSeen; }
+  bool PresentSeenWithin(int Seconds) const { return time(NULL) - presentSeen < Seconds; }
+  void SetModified(void) { modified = time(NULL); }
+  void SetPresentSeen(void) { presentSeen = time(NULL); }
+  void SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel = NULL);
+  void ClrRunningStatus(cChannel *Channel = NULL);
+  void ResetVersions(void);
+  void Sort(void);
+  void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+  void Cleanup(time_t Time);
+  void Cleanup(void);
+  cEvent *AddEvent(cEvent *Event);
+  void DelEvent(cEvent *Event);
+  void HashEvent(cEvent *Event);
+  void UnhashEvent(cEvent *Event);
+  const cList<cEvent> *Events(void) const { return &events; }
+  const cEvent *GetPresentEvent(void) const;
+  const cEvent *GetFollowingEvent(void) const;
+  const cEvent *GetEvent(tEventID EventID, time_t StartTime = 0) const;
+  const cEvent *GetEventAround(time_t Time) const;
+  void Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0) const;
+  static bool Read(FILE *f, cSchedules *Schedules);
+  };
+
+class cSchedulesLock {
+private:
+  bool locked;
+public:
+  cSchedulesLock(bool WriteLock = false, int TimeoutMs = 0);
+  ~cSchedulesLock();
+  bool Locked(void) { return locked; }
+  };
+
+class cSchedules : public cList<cSchedule> {
+  friend class cSchedule;
+  friend class cSchedulesLock;
+private:
+  cRwLock rwlock;
+  static cSchedules schedules;
+  static const char *epgDataFileName;
+  static time_t lastCleanup;
+  static time_t lastDump;
+  static time_t modified;
+public:
+  static void SetEpgDataFileName(const char *FileName);
+  static const cSchedules *Schedules(cSchedulesLock &SchedulesLock);
+         ///< Caller must provide a cSchedulesLock which has to survive the entire
+         ///< time the returned cSchedules is accessed. Once the cSchedules is no
+         ///< longer used, the cSchedulesLock must be destroyed.
+  static time_t Modified(void) { return modified; }
+  static void SetModified(cSchedule *Schedule);
+  static void Cleanup(bool Force = false);
+  static void ResetVersions(void);
+  static bool ClearAll(void);
+  static bool Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0);
+  static bool Read(FILE *f = NULL);
+  cSchedule *AddSchedule(tChannelID ChannelID);
+  const cSchedule *GetSchedule(tChannelID ChannelID) const;
+  const cSchedule *GetSchedule(const cChannel *Channel, bool AddIfMissing = false) const;
+  };
+
+void ReportEpgBugFixStats(bool Reset = false);
+
+#endif //__EPG_H
diff --git a/contrib/sasc-ng/sc/include/vdr/filter.h b/contrib/sasc-ng/sc/include/vdr/filter.h
new file mode 100644 (file)
index 0000000..9bd163f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * filter.h: Section filter
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: filter.h 1.3 2004/01/11 13:31:59 kls Exp $
+ */
+
+#ifndef __FILTER_H
+#define __FILTER_H
+
+#include <sys/types.h>
+#include "tools.h"
+
+class cSectionSyncer {
+private:
+  int lastVersion;
+  bool synced;
+public:
+  cSectionSyncer(void);
+  void Reset(void);
+  bool Sync(uchar Version, int Number, int LastNumber);
+  };
+
+class cFilterData : public cListObject {
+public:
+  u_short pid;
+  u_char tid;
+  u_char mask;
+  bool sticky;
+  cFilterData(void);
+  cFilterData(u_short Pid, u_char Tid, u_char Mask, bool Sticky);
+  bool Is(u_short Pid, u_char Tid, u_char Mask);
+  bool Matches(u_short Pid, u_char Tid);
+  };
+
+class cChannel;
+class cSectionHandler;
+
+class cFilter : public cListObject {
+  friend class cSectionHandler;
+private:
+  cSectionHandler *sectionHandler;
+  cList<cFilterData> data;
+  bool on;
+protected:
+  cFilter(void);
+  cFilter(u_short Pid, u_char Tid, u_char Mask = 0xFF);
+  virtual ~cFilter();
+  virtual void SetStatus(bool On);
+       ///< Turns this filter on or off, depending on the value of On.
+       ///< If the filter is turned off, any filter data that has been
+       ///< added without the Sticky parameter set to 'true' will be
+       ///< automatically deleted. Those parameters that have been added
+       ///< with Sticky set to 'true' will be automatically reused when
+       ///< SetStatus(true) is called.
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length) = 0;
+       ///< Processes the data delivered to this filter.
+       ///< Pid and Tid is one of the combinations added to this filter by
+       ///< a previous call to Add(), Data is a pointer to Length bytes of
+       ///< data. This function will be called from the section handler's
+       ///< thread, so it has to use proper locking mechanisms in case it
+       ///< accesses any global data. It is guaranteed that if several cFilters
+       ///< are attached to the same cSectionHandler, only _one_ of them has
+       ///< its Process() function called at any given time. It is allowed
+       ///< that more than one cFilter are set up to receive the same Pid/Tid.
+       ///< The Process() function must return as soon as possible.
+  int Source(void);
+       ///< Returns the source of the data delivered to this filter.
+  int Transponder(void);
+       ///< Returns the transponder of the data delivered to this filter.
+  const cChannel *Channel(void);
+       ///< Returns the channel of the data delivered to this filter.
+  bool Matches(u_short Pid, u_char Tid);
+       ///< Indicates whether this filter wants to receive data from the given Pid/Tid.
+  void Set(u_short Pid, u_char Tid, u_char Mask = 0xFF);
+       ///< Sets the given filter data by calling Add() with Sticky = true.
+  void Add(u_short Pid, u_char Tid, u_char Mask = 0xFF, bool Sticky = false);
+       ///< Adds the given filter data to this filter.
+       ///< If Sticky is true, this will survive a status change, otherwise
+       ///< it will be automatically deleted.
+  void Del(u_short Pid, u_char Tid, u_char Mask = 0xFF);
+       ///< Deletes the given filter data from this filter.
+  };
+
+#endif //__FILTER_H
diff --git a/contrib/sasc-ng/sc/include/vdr/font.h b/contrib/sasc-ng/sc/include/vdr/font.h
new file mode 100644 (file)
index 0000000..4794304
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * font.h: Font handling for the DVB On Screen Display
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: font.h 1.20 2007/06/23 10:09:14 kls Exp $
+ */
+
+#ifndef __FONT_H
+#define __FONT_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "tools.h"
+
+#define MAXFONTNAME 64
+#define MAXFONTSIZE 64
+
+enum eDvbFont {
+  fontOsd,
+  fontFix,
+  fontSml
+#define eDvbFontSize (fontSml + 1)
+  };
+
+class cBitmap;
+typedef uint32_t tColor; // see also osd.h
+typedef uint8_t tIndex;
+
+extern const char *DefaultFontOsd;
+extern const char *DefaultFontSml;
+extern const char *DefaultFontFix;
+
+class cFont {
+private:
+  static cFont *fonts[];
+public:
+  virtual ~cFont() {}
+  virtual int Width(uint c) const = 0;
+          ///< Returns the width of the given character in pixel.
+  virtual int Width(const char *s) const = 0;
+          ///< Returns the width of the given string in pixel.
+  virtual int Height(void) const = 0;
+          ///< Returns the height of this font in pixel (all characters have the same height).
+  int Height(const char *s) const { return Height(); }
+          ///< Returns the height of this font in pixel (obsolete, just for backwards compatibilty).
+  virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const = 0;
+          ///< Draws the given text into the Bitmap at position (x, y) with the given colors.
+          ///< The text will not exceed the given Width (if > 0), and will end with a complete character.
+  static void SetFont(eDvbFont Font, const char *Name, int CharHeight);
+          ///< Sets the given Font to use the font data according to Name (see CreateFont())
+          ///< and make its characters CharHeight pixels high.
+  static const cFont *GetFont(eDvbFont Font);
+          ///< Gets the given Font, which was previously set by a call to SetFont().
+          ///< If no SetFont() call has been made, the font as defined in the setup is returned.
+          ///< The caller must not use the returned font outside the scope in which
+          ///< it was retrieved by the call to GetFont(), because a call to SetFont()
+          ///< may delete an existing font.
+  static cFont *CreateFont(const char *Name, int CharHeight, int CharWidth = 0);
+          ///< Creates a new font object with the given Name and makes its characters
+          ///< CharHeight pixels high. If CharWidth is given, it overwrites the font's
+          ///< default width. Name is of the form "Family:Style", for instance
+          ///< "Verdana:Bold Italic" or "Times New Roman". See GetAvailableFontNames()
+          ///< for how to get a list of all available font names.
+          ///< If the requested font can't be created, NULL is returned.
+          ///< The caller must delete the font when it is no longer needed.
+  static bool GetAvailableFontNames(cStringList *FontNames, bool Monospaced = false);
+          ///< Queries the font configuration for a list of available font names,
+          ///< which is returned in FontNames. If Monospaced is true, only
+          ///< monospaced fonts will be returned. The resulting font names are
+          ///< in a format that can be used with GetFontFileName() to get the name
+          ///< of the actual font file.
+          ///< Returns true if any font names were found.
+  static cString GetFontFileName(const char *FontName);
+          ///< Retruns the actual font file name for the given FontName.
+  };
+
+class cTextWrapper {
+private:
+  char *text;
+  char *eol;
+  int lines;
+  int lastLine;
+public:
+  cTextWrapper(void);
+  cTextWrapper(const char *Text, const cFont *Font, int Width);
+  ~cTextWrapper();
+  void Set(const char *Text, const cFont *Font, int Width);
+      ///< Wraps the Text to make it fit into the area defined by the given Width
+      ///< when displayed with the given Font.
+      ///< Wrapping is done by inserting the necessary number of newline
+      ///< characters into the string.
+  const char *Text(void);
+      ///< Returns the full wrapped text.
+  int Lines(void) { return lines; }
+      ///< Returns the actual number of lines needed to display the full wrapped text.
+  const char *GetLine(int Line);
+      ///< Returns the given Line. The first line is numbered 0.
+  };
+
+#endif //__FONT_H
diff --git a/contrib/sasc-ng/sc/include/vdr/i18n.h b/contrib/sasc-ng/sc/include/vdr/i18n.h
new file mode 100644 (file)
index 0000000..7f9f065
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * i18n.h: Internationalization
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: i18n.h 1.20 2007/05/28 11:43:14 kls Exp $
+ */
+
+#ifndef __I18N_H
+#define __I18N_H
+
+#include <stdio.h>
+
+const int I18nNumLanguages = 22;
+
+typedef const char *tI18nPhrase[I18nNumLanguages];
+
+void I18nRegister(const tI18nPhrase * const Phrases, const char *Plugin);
+
+const char *I18nTranslate(const char *s, const char *Plugin = NULL) __attribute_format_arg__(1);
+
+const char * const * I18nLanguages(void);
+const char *I18nLanguageCode(int Index);
+int I18nLanguageIndex(const char *Code);
+const char *I18nNormalizeLanguageCode(const char *Code);
+   ///< Returns a 3 letter language code that may not be zero terminated.
+   ///< If no normalized language code can be found, the given Code is returned.
+   ///< Make sure at most 3 characters are copied when using it!
+bool I18nIsPreferredLanguage(int *PreferredLanguages, const char *LanguageCode, int &OldPreference, int *Position = NULL);
+   ///< Checks the given LanguageCode (which may be something like "eng" or "eng+deu")
+   ///< against the PreferredLanguages and returns true if one is found that has an index
+   ///< smaller than OldPreference (which should be initialized to -1 before the first
+   ///< call to this function in a sequence of checks). If LanguageCode is not any of
+   ///< the PreferredLanguages, and OldPreference is less than zero, OldPreference will
+   ///< be set to a value higher than the highest language index.  If Position is given,
+   ///< it will return 0 if this was a single language code (like "eng"), 1 if it was
+   ///< the first of two language codes (like "eng" out of "eng+deu") and 2 if it was
+   ///< the second one (like "deu" out of ""eng+deu").
+
+#ifdef PLUGIN_NAME_I18N
+#define tr(s)  I18nTranslate(s, PLUGIN_NAME_I18N)
+#else
+#define tr(s)  I18nTranslate(s)
+#endif
+
+#endif //__I18N_H
diff --git a/contrib/sasc-ng/sc/include/vdr/interface.h b/contrib/sasc-ng/sc/include/vdr/interface.h
new file mode 100644 (file)
index 0000000..2b3f979
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * interface.h: Abstract user interface layer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: interface.h 1.31 2004/05/01 11:11:13 kls Exp $
+ */
+
+#ifndef __INTERFACE_H
+#define __INTERFACE_H
+
+#include "config.h"
+#include "remote.h"
+#include "skins.h"
+#include "svdrp.h"
+
+class cInterface {
+private:
+  bool interrupted;
+  cSVDRP *SVDRP;
+  bool QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu);
+public:
+  cInterface(int SVDRPport = 0);
+  ~cInterface();
+  bool HasSVDRPConnection(void) { return SVDRP && SVDRP->HasConnection(); }
+  void Interrupt(void) { interrupted = true; }
+  eKeys GetKey(bool Wait = true);
+  eKeys Wait(int Seconds = 0, bool KeepChar = false);
+  bool Confirm(const char *s, int Seconds = 10, bool WaitForTimeout = false);
+  void LearnKeys(void);
+  };
+
+extern cInterface *Interface;
+
+#endif //__INTERFACE_H
diff --git a/contrib/sasc-ng/sc/include/vdr/keys.h b/contrib/sasc-ng/sc/include/vdr/keys.h
new file mode 100644 (file)
index 0000000..0799a3b
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * keys.h: Remote control Key handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: keys.h 1.11 2007/02/25 10:49:35 kls Exp $
+ */
+
+#ifndef __KEYS_H
+#define __KEYS_H
+
+#include "config.h"
+#include "tools.h"
+
+enum eKeys { // "Up" and "Down" must be the first two keys!
+             kUp,
+             kDown,
+             kMenu,
+             kOk,
+             kBack,
+             kLeft,
+             kRight,
+             kRed,
+             kGreen,
+             kYellow,
+             kBlue,
+             k0, k1, k2, k3, k4, k5, k6, k7, k8, k9,
+             kInfo,
+             kPlay,
+             kPause,
+             kStop,
+             kRecord,
+             kFastFwd,
+             kFastRew,
+             kNext,
+             kPrev,
+             kPower,
+             kChanUp,
+             kChanDn,
+             kChanPrev,
+             kVolUp,
+             kVolDn,
+             kMute,
+             kAudio,
+             kSchedule,
+             kChannels,
+             kTimers,
+             kRecordings,
+             kSetup,
+             kCommands,
+             kUser1, kUser2, kUser3, kUser4, kUser5, kUser6, kUser7, kUser8, kUser9,
+             kNone,
+             kKbd,
+             // The following codes are used internally:
+             k_Plugin,
+             k_Setup,
+             // The following flags are OR'd with the above codes:
+             k_Repeat  = 0x8000,
+             k_Release = 0x4000,
+             k_Flags   = k_Repeat | k_Release,
+           };
+
+// This is in preparation for having more key codes:
+#define kMarkToggle      k0
+#define kMarkMoveBack    k4
+#define kMarkMoveForward k6
+#define kMarkJumpBack    k7
+#define kMarkJumpForward k9
+#define kEditCut         k2
+#define kEditTest        k8
+
+#define RAWKEY(k)        (eKeys((k) & ~k_Flags))
+#define ISRAWKEY(k)      ((k) != kNone && ((k) & k_Flags) == 0)
+#define NORMALKEY(k)     (eKeys((k) & ~k_Repeat))
+#define ISMODELESSKEY(k) (RAWKEY(k) > k9)
+#define ISREALKEY(k)     (k != kNone && k != k_Plugin)
+
+#define BASICKEY(k)      (eKeys((k) & 0xFFFF))
+#define KBDKEY(k)        (eKeys(((k) << 16) | kKbd))
+#define KEYKBD(k)        (((k) >> 16) & 0xFFFF)
+
+struct tKey {
+  eKeys type;
+  char *name;
+  };
+
+class cKey : public cListObject {
+private:
+  char *remote;
+  char *code;
+  eKeys key;
+public:
+  cKey(void);
+  cKey(const char *Remote, const char *Code, eKeys Key);
+  ~cKey();
+  const char *Remote(void) { return remote; }
+  const char *Code(void) { return code; }
+  eKeys Key(void) { return key; }
+  bool Parse(char *s);
+  bool Save(FILE *f);
+  static eKeys FromString(const char *Name);
+  static const char *ToString(eKeys Key);
+  };
+
+class cKeys : public cConfig<cKey> {
+public:
+  bool KnowsRemote(const char *Remote);
+  eKeys Get(const char *Remote, const char *Code);
+  const char *GetSetup(const char *Remote);
+  void PutSetup(const char *Remote, const char *Setup);
+  };
+
+extern cKeys Keys;
+
+#define MAXKEYSINMACRO 16
+
+class cKeyMacro : public cListObject {
+private:
+  eKeys macro[MAXKEYSINMACRO];
+  int numKeys;
+  char *plugin;
+public:
+  cKeyMacro(void);
+  ~cKeyMacro();
+  bool Parse(char *s);
+  int NumKeys(void) const { return numKeys; }
+      ///< Returns the number of keys in this macro. The first key (with
+      ///< index 0) is the macro code. The actual macro expansion codes
+      ///< start at index 1 and go to NumKeys() - 1.
+  const eKeys *Macro(void) const { return macro; }
+  const char *Plugin(void) const { return plugin; }
+  };
+
+class cKeyMacros : public cConfig<cKeyMacro> {
+public:
+  const cKeyMacro *Get(eKeys Key);
+  };
+
+extern cKeyMacros KeyMacros;
+
+#endif //__KEYS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/menu.h b/contrib/sasc-ng/sc/include/vdr/menu.h
new file mode 100644 (file)
index 0000000..76a98a8
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * menu.h: The actual menu implementations
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: menu.h 1.87 2007/01/05 10:40:54 kls Exp $
+ */
+
+#ifndef __MENU_H
+#define __MENU_H
+
+#include "ci.h"
+#include "device.h"
+#include "epg.h"
+#include "osdbase.h"
+//#include "dvbplayer.h"
+#include "menuitems.h"
+//#include "recorder.h"
+#include "skins.h"
+
+class cMenuText : public cOsdMenu {
+private:
+  char *text;
+  eDvbFont font;
+public:
+  cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
+  virtual ~cMenuText();
+  void SetText(const char *Text);
+  virtual void Display(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditTimer : public cOsdMenu {
+private:
+  cTimer *timer;
+  cTimer data;
+  int channel;
+  bool addIfConfirmed;
+  cMenuEditDateItem *firstday;
+  void SetFirstDayItem(void);
+public:
+  cMenuEditTimer(cTimer *Timer, bool New = false);
+  virtual ~cMenuEditTimer();
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEvent : public cOsdMenu {
+private:
+  const cEvent *event;
+public:
+  cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false);
+  virtual void Display(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuMain : public cOsdMenu {
+private:
+  time_t lastDiskSpaceCheck;
+  int lastFreeMB;
+  bool replaying;
+  cOsdItem *stopReplayItem;
+  cOsdItem *cancelEditingItem;
+  cOsdItem *stopRecordingItem;
+  int recordControlsState;
+  static cOsdObject *pluginOsdObject;
+  void Set(void);
+  bool Update(bool Force = false);
+public:
+  cMenuMain(eOSState State = osUnknown);
+  virtual eOSState ProcessKey(eKeys Key);
+  static cOsdObject *PluginOsdObject(void);
+  };
+
+class cDisplayChannel : public cOsdObject {
+private:
+  cSkinDisplayChannel *displayChannel;
+  int group;
+  bool withInfo;
+  cTimeMs lastTime;
+  int number;
+  bool timeout;
+  cChannel *channel;
+  const cEvent *lastPresent;
+  const cEvent *lastFollowing;
+  static cDisplayChannel *currentDisplayChannel;
+  void DisplayChannel(void);
+  void DisplayInfo(void);
+  void Refresh(void);
+  cChannel *NextAvailableChannel(cChannel *Channel, int Direction);
+public:
+  cDisplayChannel(int Number, bool Switched);
+  cDisplayChannel(eKeys FirstKey);
+  virtual ~cDisplayChannel();
+  virtual eOSState ProcessKey(eKeys Key);
+  static bool IsOpen(void) { return currentDisplayChannel != NULL; }
+  };
+
+class cDisplayVolume : public cOsdObject {
+private:
+  cSkinDisplayVolume *displayVolume;
+  cTimeMs timeout;
+  static cDisplayVolume *currentDisplayVolume;
+  virtual void Show(void);
+  cDisplayVolume(void);
+public:
+  virtual ~cDisplayVolume();
+  static cDisplayVolume *Create(void);
+  static void Process(eKeys Key);
+  eOSState ProcessKey(eKeys Key);
+  };
+
+class cDisplayTracks : public cOsdObject {
+private:
+  cSkinDisplayTracks *displayTracks;
+  cTimeMs timeout;
+  eTrackType types[ttMaxTrackTypes];
+  char *descriptions[ttMaxTrackTypes];
+  int numTracks, track, audioChannel;
+  static cDisplayTracks *currentDisplayTracks;
+  virtual void Show(void);
+  cDisplayTracks(void);
+public:
+  virtual ~cDisplayTracks();
+  static bool IsOpen(void) { return currentDisplayTracks != NULL; }
+  static cDisplayTracks *Create(void);
+  static void Process(eKeys Key);
+  eOSState ProcessKey(eKeys Key);
+  };
+
+cOsdObject *CamControl(void);
+
+class cMenuRecordingItem;
+
+class cMenuRecordings : public cOsdMenu {
+private:
+  char *base;
+  int level;
+  int recordingsState;
+  int helpKeys;
+  void SetHelpKeys(void);
+  void Set(bool Refresh = false);
+  bool Open(bool OpenSubMenus = false);
+  eOSState Play(void);
+  eOSState Rewind(void);
+  eOSState Delete(void);
+  eOSState Info(void);
+  eOSState Commands(eKeys Key = kNone);
+protected:
+  cRecording *GetRecording(cMenuRecordingItem *Item);
+public:
+  cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false);
+  ~cMenuRecordings();
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cRecordControl {
+private:
+  cDevice *device;
+  cTimer *timer;
+  //cRecorder *recorder;
+  const cEvent *event;
+  char *instantId;
+  char *fileName;
+  bool GetEvent(void);
+public:
+  cRecordControl(cDevice *Device, cTimer *Timer = NULL, bool Pause = false);
+  virtual ~cRecordControl();
+  bool Process(time_t t);
+  cDevice *Device(void) { return device; }
+  void Stop(void);
+  const char *InstantId(void) { return instantId; }
+  const char *FileName(void) { return fileName; }
+  cTimer *Timer(void) { return timer; }
+  };
+
+class cRecordControls {
+private:
+  static cRecordControl *RecordControls[];
+  static int state;
+public:
+  static bool Start(cTimer *Timer = NULL, bool Pause = false);
+  static void Stop(const char *InstantId);
+  static bool PauseLiveVideo(void);
+  static const char *GetInstantId(const char *LastInstantId);
+  static cRecordControl *GetRecordControl(const char *FileName);
+  static void Process(time_t t);
+  static void ChannelDataModified(cChannel *Channel);
+  static bool Active(void);
+  static void Shutdown(void);
+  static void ChangeState(void) { state++; }
+  static bool StateChanged(int &State);
+  };
+
+#if 0
+class cReplayControl : public cDvbPlayerControl {
+private:
+  cSkinDisplayReplay *displayReplay;
+  cMarks marks;
+  bool visible, modeOnly, shown, displayFrames;
+  int lastCurrent, lastTotal;
+  bool lastPlay, lastForward;
+  int lastSpeed;
+  time_t timeoutShow;
+  bool timeSearchActive, timeSearchHide;
+  int timeSearchTime, timeSearchPos;
+  void TimeSearchDisplay(void);
+  void TimeSearchProcess(eKeys Key);
+  void TimeSearch(void);
+  void ShowTimed(int Seconds = 0);
+  static cReplayControl *currentReplayControl;
+  static char *fileName;
+  static char *title;
+  void ShowMode(void);
+  bool ShowProgress(bool Initial);
+  void MarkToggle(void);
+  void MarkJump(bool Forward);
+  void MarkMove(bool Forward);
+  void EditCut(void);
+  void EditTest(void);
+public:
+  cReplayControl(void);
+  virtual ~cReplayControl();
+  virtual cOsdObject *GetInfo(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  virtual void Show(void);
+  virtual void Hide(void);
+  bool Visible(void) { return visible; }
+  static void SetRecording(const char *FileName, const char *Title);
+  static const char *NowReplaying(void);
+  static const char *LastReplayed(void);
+  static void ClearLastReplayed(const char *FileName);
+  };
+#endif
+#endif //__MENU_H
diff --git a/contrib/sasc-ng/sc/include/vdr/menuitems.h b/contrib/sasc-ng/sc/include/vdr/menuitems.h
new file mode 100644 (file)
index 0000000..50778b1
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * menuitems.h: General purpose menu items
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: menuitems.h 1.21 2007/06/08 11:53:37 kls Exp $
+ */
+
+#ifndef __MENUITEMS_H
+#define __MENUITEMS_H
+
+#include "osdbase.h"
+
+extern const char *FileNameChars;
+
+class cMenuEditItem : public cOsdItem {
+private:
+  char *name;
+public:
+  cMenuEditItem(const char *Name);
+  ~cMenuEditItem();
+  void SetValue(const char *Value);
+  };
+
+class cMenuEditIntItem : public cMenuEditItem {
+protected:
+  int *value;
+  int min, max;
+  const char *minString, *maxString;
+  virtual void Set(void);
+public:
+  cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX, const char *MinString = NULL, const char *MaxString = NULL);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditBoolItem : public cMenuEditIntItem {
+protected:
+  const char *falseString, *trueString;
+  virtual void Set(void);
+public:
+  cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
+  };
+
+class cMenuEditBitItem : public cMenuEditBoolItem {
+protected:
+  uint *value;
+  uint mask;
+  int bit;
+  virtual void Set(void);
+public:
+  cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString = NULL, const char *TrueString = NULL);
+  };
+
+class cMenuEditNumItem : public cMenuEditItem {
+protected:
+  char *value;
+  int length;
+  bool blind;
+  virtual void Set(void);
+public:
+  cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind = false);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditChrItem : public cMenuEditItem {
+private:
+  char *value;
+  char *allowed;
+  const char *current;
+  virtual void Set(void);
+public:
+  cMenuEditChrItem(const char *Name, char *Value, const char *Allowed);
+  ~cMenuEditChrItem();
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditStrItem : public cMenuEditItem {
+private:
+  char *value;
+  int length;
+  const char *allowed;
+  int pos, offset;
+  bool insert, newchar, uppercase;
+  int lengthUtf8;
+  uint *valueUtf8;
+  uint *allowedUtf8;
+  uint *charMapUtf8;
+  uint *currentCharUtf8;
+  eKeys lastKey;
+  cTimeMs autoAdvanceTimeout;
+  void SetHelpKeys(void);
+  uint *IsAllowed(uint c);
+  void AdvancePos(void);
+  virtual void Set(void);
+  uint Inc(uint c, bool Up);
+  void Insert(void);
+  void Delete(void);
+protected:
+  void EnterEditMode(void);
+  void LeaveEditMode(bool SaveValue = false);
+  bool InEditMode(void) { return valueUtf8 != NULL; }
+public:
+  cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed);
+  ~cMenuEditStrItem();
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditStraItem : public cMenuEditIntItem {
+private:
+  const char * const *strings;
+protected:
+  virtual void Set(void);
+public:
+  cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings);
+  };
+
+class cMenuEditChanItem : public cMenuEditIntItem {
+protected:
+  const char *noneString;
+  virtual void Set(void);
+public:
+  cMenuEditChanItem(const char *Name, int *Value, const char *NoneString = NULL);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditTranItem : public cMenuEditChanItem {
+private:
+  int number;
+  int *source;
+  int *transponder;
+public:
+  cMenuEditTranItem(const char *Name, int *Value, int *Source);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditDateItem : public cMenuEditItem {
+private:
+  static int days[];
+  time_t *value;
+  int *weekdays;
+  time_t oldvalue;
+  int dayindex;
+  int FindDayIndex(int WeekDays);
+  virtual void Set(void);
+public:
+  cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays = NULL);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cMenuEditTimeItem : public cMenuEditItem {
+protected:
+  int *value;
+  int hh, mm;
+  int pos;
+  virtual void Set(void);
+public:
+  cMenuEditTimeItem(const char *Name, int *Value);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cPlugin;
+
+class cMenuSetupPage : public cOsdMenu {
+private:
+  cPlugin *plugin;
+protected:
+  void SetSection(const char *Section);
+  virtual void Store(void) = 0;
+  void SetupStore(const char *Name, const char *Value = NULL);
+  void SetupStore(const char *Name, int Value);
+public:
+  cMenuSetupPage(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  void SetPlugin(cPlugin *Plugin);
+  };
+
+#endif //__MENUITEMS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/nit.h b/contrib/sasc-ng/sc/include/vdr/nit.h
new file mode 100644 (file)
index 0000000..c4ac82b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * nit.h: NIT section filter
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: nit.h 1.3 2007/06/10 08:50:21 kls Exp $
+ */
+
+#ifndef __NIT_H
+#define __NIT_H
+
+#include "filter.h"
+
+#define MAXNITS 16
+#define MAXNETWORKNAME Utf8BufSize(256)
+
+class cNitFilter : public cFilter {
+private:
+
+  class cNit {
+  public:
+    u_short networkId;
+    char name[MAXNETWORKNAME];
+    bool hasTransponder;
+    };
+
+  cSectionSyncer sectionSyncer;
+  cNit nits[MAXNITS];
+  u_short networkId;
+  int numNits;
+protected:
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+public:
+  cNitFilter(void);
+  virtual void SetStatus(bool On);
+  };
+
+#endif //__NIT_H
diff --git a/contrib/sasc-ng/sc/include/vdr/osd.h b/contrib/sasc-ng/sc/include/vdr/osd.h
new file mode 100644 (file)
index 0000000..0753058
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * osd.h: Abstract On Screen Display layer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osd.h 1.55 2007/06/17 13:59:22 kls Exp $
+ */
+
+#ifndef __OSD_H
+#define __OSD_H
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "config.h"
+#include "font.h"
+
+#define MAXNUMCOLORS 256
+
+enum {
+                   //AARRGGBB
+  clrTransparent = 0x00000000,
+  clrGray50      = 0x7F000000, // 50% gray
+  clrBlack       = 0xFF000000,
+  clrRed         = 0xFFFC1414,
+  clrGreen       = 0xFF24FC24,
+  clrYellow      = 0xFFFCC024,
+  clrMagenta     = 0xFFB000FC,
+  clrBlue        = 0xFF0000FC,
+  clrCyan        = 0xFF00FCFC,
+  clrWhite       = 0xFFFCFCFC,
+  };
+
+enum eOsdError { oeOk,
+                 oeTooManyAreas,
+                 oeTooManyColors,
+                 oeBppNotSupported,
+                 oeAreasOverlap,
+                 oeWrongAlignment,
+                 oeOutOfMemory,
+                 oeWrongAreaSize,
+                 oeUnknown,
+               };
+
+typedef uint32_t tColor; // see also font.h
+typedef uint8_t 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) { 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) { 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);
+        ///< 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);
+        ///< 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);
+        ///< Returns the index of a color in this paltte 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.
+  };
+
+enum eTextAlignment { taCenter  = 0x00,
+                      taLeft    = 0x01,
+                      taRight   = 0x02,
+                      taTop     = 0x04,
+                      taBottom  = 0x08,
+                      taDefault = taTop | taLeft
+                    };
+
+class cFont;
+
+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.
+  cBitmap(const char *const Xpm[]);
+       ///< Creates a bitmap from the given XPM data.
+  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.
+  bool Contains(int x, int y) const;
+       ///< Returns true if this bitmap contains the point (x, y).
+  bool Covers(int x1, int y1, int x2, int y2) const;
+       ///< Returns true if the rectangle defined by the given coordinates
+       ///< completely covers this bitmap.
+  bool Intersects(int x1, int y1, int x2, int y2) const;
+       ///< Returns true if the rectangle defined by the given coordinates
+       ///< intersects with this bitmap.
+  bool Dirty(int &x1, int &y1, int &x2, int &y2);
+       ///< Tells whether there is a dirty area and returns the bounding
+       ///< rectangle of that area (relative to the bitmaps origin).
+  void Clean(void);
+       ///< Marks the dirty area as clean.
+  bool LoadXpm(const char *FileName);
+       ///< Calls SetXpm() with the data from the file FileName.
+       ///< Returns true if the operation was successful.
+  bool SetXpm(const char *const Xpm[], bool IgnoreNone = false);
+       ///< Sets this bitmap to the given XPM data. Any previous bitmap or
+       ///< palette data will be overwritten with the new data.
+       ///< If IgnoreNone is true, a "none" color entry will be ignored.
+       ///< Only set IgnoreNone to true if you know that there is a "none"
+       ///< color entry in the XPM data and that this entry is not used!
+       ///< If SetXpm() is called with IgnoreNone set to false and the XPM
+       ///< data contains an unused "none" entry, it will be automatically
+       ///< called again with IgnoreNone set to true.
+       ///< Returns true if the operation was successful.
+  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 DrawPixel(int x, int y, tColor Color);
+       ///< Sets the pixel at the given coordinates to the given Color, which is
+       ///< a full 32 bit ARGB value.
+       ///< If the coordinates are outside the bitmap area, no pixel will be set.
+  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.
+  void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault);
+       ///< Draws the given string at coordinates (x, y) with the given foreground
+       ///< and background color and font. If Width and Height are given, the text
+       ///< will be drawn into a rectangle with the given size and the given
+       ///< Alignment (default is top-left). If ColorBg is clrTransparent, no
+       ///< background pixels will be drawn, which allows drawing "transparent" text.
+  void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color);
+       ///< Draws a filled rectangle defined by the upper left (x1, y1) and lower right
+       ///< (x2, y2) corners with the given Color. If the rectangle covers the entire
+       ///< bitmap area, the color palette will be reset, so that new colors can be
+       ///< used for drawing.
+  void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0);
+       ///< Draws a filled ellipse defined by the upper left (x1, y1) and lower right
+       ///< (x2, y2) corners with the given Color. Quadrants controls which parts of
+       ///< the ellipse are actually drawn:
+       ///< 0       draws the entire ellipse
+       ///< 1..4    draws only the first, second, third or fourth quadrant, respectively
+       ///< 5..8    draws the right, top, left or bottom half, respectively
+       ///< -1..-8  draws the inverted part of the given quadrant(s)
+       ///< If Quadrants is not 0, the coordinates are those of the actual area, not
+       ///< the full circle!
+  void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type);
+       ///< Draws a "slope" into the rectangle defined by the upper left (x1, y1) and
+       ///< lower right (x2, y2) corners with the given Color. Type controls the
+       ///< direction of the slope and which side of it will be drawn:
+       ///< 0: horizontal, rising,  lower
+       ///< 1: horizontal, rising,  upper
+       ///< 2: horizontal, falling, lower
+       ///< 3: horizontal, falling, upper
+       ///< 4: vertical,   rising,  lower
+       ///< 5: vertical,   rising,  upper
+       ///< 6: vertical,   falling, lower
+       ///< 7: vertical,   falling, upper
+  const tIndex *Data(int x, int y);
+       ///< Returns the address of the index byte at the given coordinates.
+  tColor GetColor(int x, int y) { return Color(*Data(x, y)); }
+       ///< Returns the color at the given coordinates.
+  };
+
+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); }
+  };
+
+#define MAXOSDAREAS 16
+
+class cOsd {
+  friend class cOsdProvider;
+private:
+  static int osdLeft, osdTop, osdWidth, osdHeight;
+  static int isOpen;
+  cBitmap *savedRegion;
+  cBitmap *bitmaps[MAXOSDAREAS];
+  int numBitmaps;
+  int left, top, width, height;
+protected:
+  cOsd(int Left, int Top);
+       ///< Initializes the OSD with the given coordinates.
+       ///< By default it is assumed that the full area will be able to display
+       ///< full 32 bit graphics (ARGB with eight bit for each color and the alpha
+       ///< value, repectively). However, the actual hardware in use may not be
+       ///< able to display such a high resolution OSD, so there is an option to
+       ///< divide the full OSD area into several sub-areas with lower color depths
+       ///< and individual palettes. The sub-areas need not necessarily cover the
+       ///< entire OSD area, but only the OSD area actually covered by sub-areas
+       ///< will be available for drawing.
+       ///< At least one area must be defined in order to set the actual width and
+       ///< height of the OSD. Also, the caller must first try to use an area that
+       ///< consists of only one sub-area that covers the entire drawing space,
+       ///< and should require only the minimum necessary color depth. This is
+       ///< because a derived cOsd class may or may not be able to handle more
+       ///< than one area.
+public:
+  virtual ~cOsd();
+       ///< Shuts down the OSD.
+  static int OsdLeft(void) { return osdLeft ? osdLeft : Setup.OSDLeft; }
+  static int OsdTop(void) { return osdTop ? osdTop : Setup.OSDTop; }
+  static int OsdWidth(void) { return osdWidth ? osdWidth : Setup.OSDWidth; }
+  static int OsdHeight(void) { return osdHeight ? osdHeight : Setup.OSDHeight; }
+  static void SetOsdPostion(int Left, int Top, int Width, int Height);
+       ///< Sets the position and size of the OSD to the given values.
+       ///< This may be useful for plugins that determine the scaling of the
+       ///< video image and need to scale the OSD accordingly to fit on the
+       ///< screen.
+  static int IsOpen(void) { return isOpen; }
+  int Left(void) { return left; }
+  int Top(void) { return top; }
+  int Width(void) { return width; }
+  int Height(void) { return height; }
+  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.
+  cBitmap *GetBitmap(int Area);
+       ///< Returns a pointer to the bitmap for the given Area, or NULL if no
+       ///< such bitmap exists.
+  virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
+       ///< Checks whether the OSD can display the given set of sub-areas.
+       ///< The return value indicates whether a call to SetAreas() with this
+       ///< set of areas will succeed. CanHandleAreas() may be called with an
+       ///< OSD that is already in use with other areas and will not interfere
+       ///< with the current operation of the OSD.
+       ///< A derived class must first call the base class CanHandleAreas()
+       ///< to check the basic conditions, like not overlapping etc.
+  virtual eOsdError SetAreas(const tArea *Areas, int NumAreas);
+       ///< Sets the sub-areas to the given areas.
+       ///< The return value indicates whether the operation was successful.
+       ///< If an error is reported, nothing will have changed and the previous
+       ///< OSD (if any) will still be displayed as before.
+       ///< If the OSD has been divided into several sub-areas, all areas that
+       ///< are part of the rectangle that surrounds a given drawing operation
+       ///< will be drawn into, with the proper offsets.
+  virtual void SaveRegion(int x1, int y1, int x2, int y2);
+       ///< Saves the region defined by the given coordinates for later restoration
+       ///< through RestoreRegion(). Only one saved region can be active at any
+       ///< given time.
+  virtual void RestoreRegion(void);
+       ///< Restores the region previously saved by a call to SaveRegion().
+       ///< If SaveRegion() has not been called before, nothing will happen.
+  virtual eOsdError SetPalette(const cPalette &Palette, int Area);
+       ///< Sets the Palette for the given Area (the first area is numbered 0).
+  virtual void DrawPixel(int x, int y, tColor Color);
+       ///< Sets the pixel at the given coordinates to the given Color, which is
+       ///< a full 32 bit ARGB value.
+       ///< If the OSD area has been divided into separate sub-areas, and the
+       ///< given coordinates don't fall into any of these sub-areas, no pixel will
+       ///< be set.
+  virtual 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 the OSD 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.
+  virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault);
+       ///< Draws the given string at coordinates (x, y) with the given foreground
+       ///< and background color and font. If Width and Height are given, the text
+       ///< will be drawn into a rectangle with the given size and the given
+       ///< Alignment (default is top-left). If ColorBg is clrTransparent, no
+       ///< background pixels will be drawn, which allows drawing "transparent" text.
+  virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color);
+       ///< Draws a filled rectangle defined by the upper left (x1, y1) and lower right
+       ///< (x2, y2) corners with the given Color.
+  virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0);
+       ///< Draws a filled ellipse defined by the upper left (x1, y1) and lower right
+       ///< (x2, y2) corners with the given Color. Quadrants controls which parts of
+       ///< the ellipse are actually drawn:
+       ///< 0       draws the entire ellipse
+       ///< 1..4    draws only the first, second, third or fourth quadrant, respectively
+       ///< 5..8    draws the right, top, left or bottom half, respectively
+       ///< -1..-8  draws the inverted part of the given quadrant(s)
+       ///< If Quadrants is not 0, the coordinates are those of the actual area, not
+       ///< the full circle!
+  virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type);
+       ///< Draws a "slope" into the rectangle defined by the upper left (x1, y1) and
+       ///< lower right (x2, y2) corners with the given Color. Type controls the
+       ///< direction of the slope and which side of it will be drawn:
+       ///< 0: horizontal, rising,  lower
+       ///< 1: horizontal, rising,  upper
+       ///< 2: horizontal, falling, lower
+       ///< 3: horizontal, falling, upper
+       ///< 4: vertical,   rising,  lower
+       ///< 5: vertical,   rising,  upper
+       ///< 6: vertical,   falling, lower
+       ///< 7: vertical,   falling, upper
+  virtual void Flush(void);
+       ///< Actually commits all data to the OSD hardware.
+  };
+
+class cOsdProvider {
+private:
+  static cOsdProvider *osdProvider;
+protected:
+  virtual cOsd *CreateOsd(int Left, int Top) = 0;
+      ///< Returns a pointer to a newly created cOsd object, which will be located
+      ///< at the given coordinates.
+public:
+  cOsdProvider(void);
+      //XXX maybe parameter to make this one "sticky"??? (frame-buffer etc.)
+  virtual ~cOsdProvider();
+  static cOsd *NewOsd(int Left, int Top);
+      ///< Returns a pointer to a newly created cOsd object, which will be located
+      ///< at the given coordinates. When the cOsd object is no longer needed, the
+      ///< caller must delete it. If the OSD is already in use, or there is no OSD
+      ///< provider, a dummy OSD is returned so that the caller may always use the
+      ///< returned pointer without having to check it every time it is accessed.
+  static void Shutdown(void);
+      ///< Shuts down the OSD provider facility by deleting the current OSD provider.
+  };
+
+class cTextScroller {
+private:
+  cOsd *osd;
+  int left, top, width, height;
+  const cFont *font;
+  tColor colorFg, colorBg;
+  int offset, shown;
+  cTextWrapper textWrapper;
+  void DrawText(void);
+public:
+  cTextScroller(void);
+  cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg);
+  void Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg);
+  void Reset(void);
+  int Left(void) { return left; }
+  int Top(void) { return top; }
+  int Width(void) { return width; }
+  int Height(void) { return height; }
+  int Total(void) { return textWrapper.Lines(); }
+  int Offset(void) { return offset; }
+  int Shown(void) { return shown; }
+  bool CanScroll(void) { return CanScrollUp() || CanScrollDown(); }
+  bool CanScrollUp(void) { return offset > 0; }
+  bool CanScrollDown(void) { return offset + shown < Total(); }
+  void Scroll(bool Up, bool Page);
+  };
+
+#endif //__OSD_H
diff --git a/contrib/sasc-ng/sc/include/vdr/osdbase.h b/contrib/sasc-ng/sc/include/vdr/osdbase.h
new file mode 100644 (file)
index 0000000..032a64e
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * osdbase.h: Basic interface to the On Screen Display
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osdbase.h 1.16 2007/06/09 11:49:00 kls Exp $
+ */
+
+#ifndef __OSDBASE_H
+#define __OSDBASE_H
+
+#include "config.h"
+#include "osd.h"
+#include "skins.h"
+#include "tools.h"
+
+enum eOSState { osUnknown,
+                osContinue,
+                osSchedule,
+                osChannels,
+                osTimers,
+                osRecordings,
+                osPlugin,
+                osSetup,
+                osCommands,
+                osPause,
+                osRecord,
+                osReplay,
+                osStopRecord,
+                osStopReplay,
+                osCancelEdit,
+                osSwitchDvb,
+                osBack,
+                osEnd,
+                os_User, // the following values can be used locally
+                osUser1,
+                osUser2,
+                osUser3,
+                osUser4,
+                osUser5,
+                osUser6,
+                osUser7,
+                osUser8,
+                osUser9,
+                osUser10,
+              };
+
+class cOsdItem : public cListObject {
+private:
+  char *text;
+  eOSState state;
+  bool selectable;
+protected:
+  bool fresh;
+public:
+  cOsdItem(eOSState State = osUnknown);
+  cOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true);
+  virtual ~cOsdItem();
+  bool Selectable(void) { return selectable; }
+  void SetText(const char *Text, bool Copy = true);
+  void SetSelectable(bool Selectable);
+  void SetFresh(bool Fresh);
+  const char *Text(void) { return text; }
+  virtual void Set(void) {}
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+class cOsdObject {
+  friend class cOsdMenu;
+private:
+  bool isMenu;
+  bool needsFastResponse;
+protected:
+  void SetNeedsFastResponse(bool NeedsFastResponse) { needsFastResponse = NeedsFastResponse; }
+public:
+  cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; }
+  virtual ~cOsdObject() {}
+  virtual bool NeedsFastResponse(void) { return needsFastResponse; }
+  bool IsMenu(void) { return isMenu; }
+  virtual void Show(void);
+  virtual eOSState ProcessKey(eKeys Key) { return osUnknown; }
+  };
+
+class cOsdMenu : public cOsdObject, public cList<cOsdItem> {
+private:
+  static cSkinDisplayMenu *displayMenu;
+  static int displayMenuCount;
+  static int displayMenuItems;
+  char *title;
+  int cols[cSkinDisplayMenu::MaxTabs];
+  int first, current, marked;
+  cOsdMenu *subMenu;
+  const char *helpRed, *helpGreen, *helpYellow, *helpBlue;
+  char *status;
+  int digit;
+  bool hasHotkeys;
+protected:
+  void SetDisplayMenu(void);
+  cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
+  const char *hk(const char *s);
+  void SetCols(int c0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
+  void SetHasHotkeys(bool HasHotkeys = true);
+  virtual void Clear(void);
+  bool SelectableItem(int idx);
+  void SetCurrent(cOsdItem *Item);
+  void RefreshCurrent(void);
+  void DisplayCurrent(bool Current);
+  void DisplayItem(cOsdItem *Item);
+  void CursorUp(void);
+  void CursorDown(void);
+  void PageUp(void);
+  void PageDown(void);
+  void Mark(void);
+  eOSState HotKey(eKeys Key);
+  eOSState AddSubMenu(cOsdMenu *SubMenu);
+  eOSState CloseSubMenu();
+  bool HasSubMenu(void) { return subMenu; }
+  void SetStatus(const char *s);
+  void SetTitle(const char *Title);
+  void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL);
+  virtual void Del(int Index);
+public:
+  cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
+  virtual ~cOsdMenu();
+  virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
+  int Current(void) { return current; }
+  void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
+  void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
+  virtual void Display(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+#endif //__OSDBASE_H
diff --git a/contrib/sasc-ng/sc/include/vdr/pat.h b/contrib/sasc-ng/sc/include/vdr/pat.h
new file mode 100644 (file)
index 0000000..a8053ae
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * pat.h: PAT section filter
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: pat.h 1.7 2007/01/05 10:42:11 kls Exp $
+ */
+
+#ifndef __PAT_H
+#define __PAT_H
+
+#include <stdint.h>
+#include "filter.h"
+
+#define MAXPMTENTRIES 64
+
+class cPatFilter : public cFilter {
+private:
+  time_t lastPmtScan;
+  int pmtIndex;
+  int pmtPid;
+  int pmtSid;
+  uint64_t pmtVersion[MAXPMTENTRIES];
+  int numPmtEntries;
+  bool PmtVersionChanged(int PmtPid, int Sid, int Version);
+protected:
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+public:
+  cPatFilter(void);
+  virtual void SetStatus(bool On);
+  void Trigger(void);
+  };
+
+int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
+         ///< Gets all CA descriptors for a given channel.
+         ///< Copies all available CA descriptors for the given Source, Transponder and ServiceId
+         ///< into the provided buffer at Data (at most BufSize bytes). Only those CA descriptors
+         ///< are copied that match one of the given CA system IDs.
+         ///< \return Returns the number of bytes copied into Data (0 if no CA descriptors are
+         ///< available), or -1 if BufSize was too small to hold all CA descriptors.
+         ///< The return value in StreamFlag tells whether these CA descriptors are to be used
+         ///< for the individual streams.
+
+#endif //__PAT_H
diff --git a/contrib/sasc-ng/sc/include/vdr/player.h b/contrib/sasc-ng/sc/include/vdr/player.h
new file mode 100644 (file)
index 0000000..11d1656
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * player.h: The basic player interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: player.h 1.19 2006/01/06 11:29:27 kls Exp $
+ */
+
+#ifndef __PLAYER_H
+#define __PLAYER_H
+
+#include "device.h"
+#include "osdbase.h"
+
+class cPlayer {
+  friend class cDevice;
+private:
+  cDevice *device;
+  ePlayMode playMode;
+protected:
+  void DeviceClrAvailableTracks(bool DescriptionsOnly = false) { if (device) device->ClrAvailableTracks(DescriptionsOnly); }
+  bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = NULL) { return device ? device->SetAvailableTrack(Type, Index, Id, Language, Description) : false; }
+  bool DeviceSetCurrentAudioTrack(eTrackType Type) { return device ? device->SetCurrentAudioTrack(Type) : false; }
+  bool DevicePoll(cPoller &Poller, int TimeoutMs = 0) { return device ? device->Poll(Poller, TimeoutMs) : false; }
+  bool DeviceFlush(int TimeoutMs = 0) { return device ? device->Flush(TimeoutMs) : true; }
+  void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
+  void DeviceClear(void) { if (device) device->Clear(); }
+  void DevicePlay(void) { if (device) device->Play(); }
+  void DeviceFreeze(void) { if (device) device->Freeze(); }
+  void DeviceMute(void) { if (device) device->Mute(); }
+  void DeviceSetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) { if (device) device->SetVideoDisplayFormat(VideoDisplayFormat); }
+  void DeviceStillPicture(const uchar *Data, int Length) { if (device) device->StillPicture(Data, Length); }
+  void Detach(void);
+  virtual void Activate(bool On) {}
+       // This function is called right after the cPlayer has been attached to
+       // (On == true) or before it gets detached from (On == false) a cDevice.
+       // It can be used to do things like starting/stopping a thread.
+  int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
+       // Sends the given PES Data to the device and returns the number of
+       // bytes that have actually been accepted by the device (or a
+       // negative value in case of an error).
+public:
+  cPlayer(ePlayMode PlayMode = pmAudioVideo);
+  virtual ~cPlayer();
+  bool IsAttached(void) { return device != NULL; }
+  virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return false; }
+       // Returns the current and total frame index, optionally snapped to the
+       // nearest I-frame.
+  virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed) { return false; }
+       // Returns the current replay mode (if applicable).
+       // 'Play' tells whether we are playing or pausing, 'Forward' tells whether
+       // we are going forward or backward and 'Speed' is -1 if this is normal
+       // play/pause mode, 0 if it is single speed fast/slow forward/back mode
+       // and >0 if this is multi speed mode.
+  virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId) {}
+       // Sets the current audio track to the given value.
+       // This is just a virtual hook for players that need to do special things
+       // in order to switch audio tracks.
+  };
+
+class cControl : public cOsdObject {
+private:
+  static cControl *control;
+  static cMutex mutex;
+  bool attached;
+  bool hidden;
+protected:
+  cPlayer *player;
+public:
+  cControl(cPlayer *Player, bool Hidden = false);
+  virtual ~cControl();
+  virtual void Hide(void) = 0;
+  virtual cOsdObject *GetInfo(void);
+  bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return player->GetIndex(Current, Total, SnapToIFrame); }
+  bool GetReplayMode(bool &Play, bool &Forward, int &Speed) { return player->GetReplayMode(Play, Forward, Speed); }
+  static void Launch(cControl *Control);
+  static void Attach(void);
+  static void Shutdown(void);
+  static cControl *Control(void);
+  };
+
+#endif //__PLAYER_H
diff --git a/contrib/sasc-ng/sc/include/vdr/plugin.h b/contrib/sasc-ng/sc/include/vdr/plugin.h
new file mode 100644 (file)
index 0000000..ddb498c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * plugin.h: The VDR plugin interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: plugin.h 1.14 2007/02/24 13:45:28 kls Exp $
+ */
+
+#ifndef __PLUGIN_H
+#define __PLUGIN_H
+
+#include "i18n.h"
+#include "menuitems.h"
+#include "osdbase.h"
+#include "tools.h"
+
+#define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; }
+
+class cPlugin {
+  friend class cDll;
+  friend class cPluginManager;
+private:
+  static char *configDirectory;
+  const char *name;
+  bool started;
+  void SetName(const char *s);
+public:
+  cPlugin(void);
+  virtual ~cPlugin();
+
+  const char *Name(void) { return name; }
+  virtual const char *Version(void) = 0;
+  virtual const char *Description(void) = 0;
+  virtual const char *CommandLineHelp(void);
+
+  virtual bool ProcessArgs(int argc, char *argv[]);
+  virtual bool Initialize(void);
+  virtual bool Start(void);
+  virtual void Stop(void);
+  virtual void Housekeeping(void);
+  virtual void MainThreadHook(void);
+  virtual cString Active(void);
+  virtual time_t WakeupTime(void);
+
+  virtual const char *MainMenuEntry(void);
+  virtual cOsdObject *MainMenuAction(void);
+
+  virtual cMenuSetupPage *SetupMenu(void);
+  virtual bool SetupParse(const char *Name, const char *Value);
+  void SetupStore(const char *Name, const char *Value = NULL);
+  void SetupStore(const char *Name, int Value);
+
+  void RegisterI18n(const tI18nPhrase * const Phrases);
+
+  virtual bool Service(const char *Id, void *Data = NULL);
+  virtual const char **SVDRPHelpPages(void);
+  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+
+  static void SetConfigDirectory(const char *Dir);
+  static const char *ConfigDirectory(const char *PluginName = NULL);
+  };
+
+class cDll : public cListObject {
+private:
+  char *fileName;
+  char *args;
+  void *handle;
+  cPlugin *plugin;
+public:
+  cDll(const char *FileName, const char *Args);
+  virtual ~cDll();
+  bool Load(bool Log = false);
+  cPlugin *Plugin(void) { return plugin; }
+  };
+
+class cDlls : public cList<cDll> {};
+
+class cPluginManager {
+private:
+  static cPluginManager *pluginManager;
+  char *directory;
+  time_t lastHousekeeping;
+  int nextHousekeeping;
+  cDlls dlls;
+public:
+  cPluginManager(const char *Directory);
+  virtual ~cPluginManager();
+  void SetDirectory(const char *Directory);
+  void AddPlugin(const char *Args);
+  bool LoadPlugins(bool Log = false);
+  bool InitializePlugins(void);
+  bool StartPlugins(void);
+  void Housekeeping(void);
+  void MainThreadHook(void);
+  static bool Active(const char *Prompt = NULL);
+  static cPlugin *GetNextWakeupPlugin(void);
+  static bool HasPlugins(void);
+  static cPlugin *GetPlugin(int Index);
+  static cPlugin *GetPlugin(const char *Name);
+  static cPlugin *CallFirstService(const char *Id, void *Data = NULL);
+  static bool CallAllServices(const char *Id, void *Data = NULL);
+  void StopPlugins(void);
+  void Shutdown(bool Log = false);
+  };
+
+#endif //__PLUGIN_H
diff --git a/contrib/sasc-ng/sc/include/vdr/recording.h b/contrib/sasc-ng/sc/include/vdr/recording.h
new file mode 100644 (file)
index 0000000..045bb24
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * recording.h: Recording file handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: recording.h 1.57 2007/06/17 12:53:05 kls Exp $
+ */
+
+#ifndef __RECORDING_H
+#define __RECORDING_H
+
+#include <time.h>
+#include "channels.h"
+#include "config.h"
+#include "epg.h"
+#include "thread.h"
+#include "timers.h"
+#include "tools.h"
+
+extern bool VfatFileSystem;
+
+void RemoveDeletedRecordings(void);
+void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
+     ///< The special Priority value -1 means that we shall get rid of any
+     ///< deleted recordings faster than normal (because we're cutting).
+     ///< If Force is true, the check will be done even if the timeout
+     ///< hasn't expired yet.
+
+class cResumeFile {
+private:
+  char *fileName;
+public:
+  cResumeFile(const char *FileName);
+  ~cResumeFile();
+  int Read(void);
+  bool Save(int Index);
+  void Delete(void);
+  };
+
+class cRecordingInfo {
+  friend class cRecording;
+private:
+  tChannelID channelID;
+  char *channelName;
+  const cEvent *event;
+  cEvent *ownEvent;
+  char *aux;
+  cRecordingInfo(const cChannel *Channel = NULL, const cEvent *Event = NULL);
+  void SetData(const char *Title, const char *ShortText, const char *Description);
+  void SetAux(const char *Aux);
+public:
+  ~cRecordingInfo();
+  tChannelID ChannelID(void) const { return channelID; }
+  const char *ChannelName(void) const { return channelName; }
+  const char *Title(void) const { return event->Title(); }
+  const char *ShortText(void) const { return event->ShortText(); }
+  const char *Description(void) const { return event->Description(); }
+  const cComponents *Components(void) const { return event->Components(); }
+  const char *Aux(void) const { return aux; }
+  bool Read(FILE *f);
+  bool Write(FILE *f, const char *Prefix = "") const;
+  };
+
+class cRecording : public cListObject {
+  friend class cRecordings;
+private:
+  mutable int resume;
+  mutable char *titleBuffer;
+  mutable char *sortBuffer;
+  mutable char *fileName;
+  mutable char *name;
+  mutable int fileSizeMB;
+  cRecordingInfo *info;
+  static char *StripEpisodeName(char *s);
+  char *SortName(void) const;
+  int GetResume(void) const;
+public:
+  time_t start;
+  int priority;
+  int lifetime;
+  time_t deleted;
+  cRecording(cTimer *Timer, const cEvent *Event);
+  cRecording(const char *FileName);
+  virtual ~cRecording();
+  virtual int Compare(const cListObject &ListObject) const;
+  const char *Name(void) const { return name; }
+  const char *FileName(void) const;
+  const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
+  const cRecordingInfo *Info(void) const { return info; }
+  const char *PrefixFileName(char Prefix);
+  int HierarchyLevels(void) const;
+  void ResetResume(void) const;
+  bool IsNew(void) const { return GetResume() <= 0; }
+  bool IsEdited(void) const;
+  bool WriteInfo(void);
+  bool Delete(void);
+       // Changes the file name so that it will no longer be visible in the "Recordings" menu
+       // Returns false in case of error
+  bool Remove(void);
+       // Actually removes the file from the disk
+       // Returns false in case of error
+  };
+
+class cRecordings : public cList<cRecording>, public cThread {
+private:
+  static char *updateFileName;
+  bool deleted;
+  time_t lastUpdate;
+  int state;
+  const char *UpdateFileName(void);
+  void Refresh(bool Foreground = false);
+  void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
+protected:
+  void Action(void);
+public:
+  cRecordings(bool Deleted = false);
+  virtual ~cRecordings();
+  bool Load(void) { return Update(true); }
+       ///< Loads the current list of recordings and returns true if there
+       ///< is anything in it (for compatibility with older plugins - use
+       ///< Update(true) instead).
+  bool Update(bool Wait = false);
+       ///< Triggers an update of the list of recordings, which will run
+       ///< as a separate thread if Wait is false. If Wait is true, the
+       ///< function returns only after the update has completed.
+       ///< Returns true if Wait is true and there is anyting in the list
+       ///< of recordings, false otherwise.
+  void TouchUpdate(void);
+       ///< Touches the '.update' file in the video directory, so that other
+       ///< instances of VDR that access the same video directory can be triggered
+       ///< to update their recordings list.
+  bool NeedsUpdate(void);
+  void ChangeState(void) { state++; }
+  bool StateChanged(int &State);
+  void ResetResume(const char *ResumeFileName = NULL);
+  cRecording *GetByName(const char *FileName);
+  void AddByName(const char *FileName, bool TriggerUpdate = true);
+  void DelByName(const char *FileName);
+  int TotalFileSizeMB(void); ///< Only for deleted recordings!
+  };
+
+extern cRecordings Recordings;
+extern cRecordings DeletedRecordings;
+
+class cMark : public cListObject {
+public:
+  int position;
+  char *comment;
+  cMark(int Position = 0, const char *Comment = NULL);
+  virtual ~cMark();
+  cString ToText(void);
+  bool Parse(const char *s);
+  bool Save(FILE *f);
+  };
+
+class cMarks : public cConfig<cMark> {
+public:
+  bool Load(const char *RecordingFileName);
+  void Sort(void);
+  cMark *Add(int Position);
+  cMark *Get(int Position);
+  cMark *GetPrev(int Position);
+  cMark *GetNext(int Position);
+  };
+
+#define RUC_BEFORERECORDING "before"
+#define RUC_AFTERRECORDING  "after"
+#define RUC_EDITEDRECORDING "edited"
+
+class cRecordingUserCommand {
+private:
+  static const char *command;
+public:
+  static void SetCommand(const char *Command) { command = Command; }
+  static void InvokeCommand(const char *State, const char *RecordingFileName);
+  };
+
+//XXX+
+#define FRAMESPERSEC 25
+
+// The maximum size of a single frame (up to HDTV 1920x1080):
+#define MAXFRAMESIZE  KILOBYTE(512)
+
+// The maximum file size is limited by the range that can be covered
+// with 'int'. 4GB might be possible (if the range is considered
+// 'unsigned'), 2GB should be possible (even if the range is considered
+// 'signed'), so let's use 2000MB for absolute safety (the actual file size
+// may be slightly higher because we stop recording only before the next
+// 'I' frame, to have a complete Group Of Pictures):
+#define MAXVIDEOFILESIZE 2000 // MB
+#define MINVIDEOFILESIZE  100 // MB
+
+class cIndexFile {
+private:
+  struct tIndex { int offset; uchar type; uchar number; short reserved; };
+  int f;
+  char *fileName;
+  int size, last;
+  tIndex *index;
+  cResumeFile resumeFile;
+  cMutex mutex;
+  bool CatchUp(int Index = -1);
+public:
+  cIndexFile(const char *FileName, bool Record);
+  ~cIndexFile();
+  bool Ok(void) { return index != NULL; }
+  bool Write(uchar PictureType, uchar FileNumber, int FileOffset);
+  bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
+  int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
+  int Get(uchar FileNumber, int FileOffset);
+  int Last(void) { CatchUp(); return last; }
+  int GetResume(void) { return resumeFile.Read(); }
+  bool StoreResume(int Index) { return resumeFile.Save(Index); }
+  bool IsStillRecording(void);
+  };
+
+class cFileName {
+private:
+  cUnbufferedFile *file;
+  int fileNumber;
+  char *fileName, *pFileNumber;
+  bool record;
+  bool blocking;
+public:
+  cFileName(const char *FileName, bool Record, bool Blocking = false);
+  ~cFileName();
+  const char *Name(void) { return fileName; }
+  int Number(void) { return fileNumber; }
+  cUnbufferedFile *Open(void);
+  void Close(void);
+  cUnbufferedFile *SetOffset(int Number, int Offset = 0);
+  cUnbufferedFile *NextFile(void);
+  };
+
+cString IndexToHMSF(int Index, bool WithFrame = false);
+      // Converts the given index to a string, optionally containing the frame number.
+int HMSFToIndex(const char *HMSF);
+      // Converts the given string (format: "hh:mm:ss.ff") to an index.
+int SecondsToFrames(int Seconds); //XXX+ ->player???
+      // Returns the number of frames corresponding to the given number of seconds.
+
+int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
+
+char *ExchangeChars(char *s, bool ToFileSystem);
+      // Exchanges the characters in the given string to or from a file system
+      // specific representation (depending on ToFileSystem). The given string will
+      // be modified and may be reallocated if more space is needed. The return
+      // value points to the resulting string, which may be different from s.
+
+#endif //__RECORDING_H
diff --git a/contrib/sasc-ng/sc/include/vdr/remote.h b/contrib/sasc-ng/sc/include/vdr/remote.h
new file mode 100644 (file)
index 0000000..a38d661
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * remote.h: General Remote Control handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: remote.h 1.40 2007/04/30 12:37:37 kls Exp $
+ */
+
+#ifndef __REMOTE_H
+#define __REMOTE_H
+
+#include <stdio.h>
+#include <termios.h>
+#include <time.h>
+#include "keys.h"
+#include "thread.h"
+#include "tools.h"
+
+class cRemote : public cListObject {
+private:
+  enum { MaxKeys = 2 * MAXKEYSINMACRO };
+  static eKeys keys[MaxKeys];
+  static int in;
+  static int out;
+  static cTimeMs repeatTimeout;
+  static cRemote *learning;
+  static char *unknownCode;
+  static cMutex mutex;
+  static cCondVar keyPressed;
+  static time_t lastActivity;
+  static const char *keyMacroPlugin;
+  static const char *callPlugin;
+  static bool enabled;
+  char *name;
+protected:
+  cRemote(const char *Name);
+  const char *GetSetup(void);
+  void PutSetup(const char *Setup);
+  bool Put(uint64_t Code, bool Repeat = false, bool Release = false);
+  bool Put(const char *Code, bool Repeat = false, bool Release = false);
+public:
+  virtual ~cRemote();
+  virtual bool Ready(void) { return true; }
+  virtual bool Initialize(void);
+  const char *Name(void) { return name; }
+  static void SetLearning(cRemote *Learning) { learning = Learning; }
+  static bool IsLearning() { return learning != NULL; }
+  static bool Enabled(void) { return enabled; }
+  static void SetEnabled(bool Enabled) { enabled = Enabled; }
+  static void Clear(void);
+  static bool Put(eKeys Key, bool AtFront = false);
+  static bool PutMacro(eKeys Key);
+  static bool CallPlugin(const char *Plugin);
+      ///< Initiates calling the given plugin's main menu function.
+      ///< The Plugin parameter is the name of the plugin, and must be
+      ///< a static string. Returns true if the plugin call was successfully
+      ///< initiated (the actual call to the plugin's main menu function
+      ///< will take place some time later, during the next execution
+      ///< of VDR's main loop). If there is already a plugin call pending
+      ///< false will be returned and the caller should try again later.
+  static const char *GetPlugin(void);
+      ///< Returns the name of the plugin that was set with a previous
+      ///< call to PutMacro() or CallPlugin(). The internally stored pointer to the
+      ///< plugin name will be reset to NULL by this call.
+  static bool HasKeys(void);
+  static eKeys Get(int WaitMs = 1000, char **UnknownCode = NULL);
+  static time_t LastActivity(void) { return lastActivity; }
+      ///< Absolute time when last key was delivered by Get().
+  };
+
+class cRemotes : public cList<cRemote> {};
+
+extern cRemotes Remotes;
+
+enum eKbdFunc {
+  kfNone,
+  kfF1 = 0x100,
+  kfF2,
+  kfF3,
+  kfF4,
+  kfF5,
+  kfF6,
+  kfF7,
+  kfF8,
+  kfF9,
+  kfF10,
+  kfF11,
+  kfF12,
+  kfUp,
+  kfDown,
+  kfLeft,
+  kfRight,
+  kfHome,
+  kfEnd,
+  kfPgUp,
+  kfPgDown,
+  kfIns,
+  kfDel,
+  };
+
+class cKbdRemote : public cRemote, private cThread {
+private:
+  static bool kbdAvailable;
+  static bool rawMode;
+  struct termios savedTm;
+  virtual void Action(void);
+  int ReadKey(void);
+  uint64_t ReadKeySequence(void);
+  int MapCodeToFunc(uint64_t Code);
+public:
+  cKbdRemote(void);
+  virtual ~cKbdRemote();
+  static bool KbdAvailable(void) { return kbdAvailable; }
+  static uint64_t MapFuncToCode(int Func);
+  static void SetRawMode(bool RawMode);
+  };
+
+#endif //__REMOTE_H
diff --git a/contrib/sasc-ng/sc/include/vdr/sclink.h b/contrib/sasc-ng/sc/include/vdr/sclink.h
new file mode 100644 (file)
index 0000000..5eac795
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#define SC_NAME "sc"
+#define SC_MAGIC { 0,'S','C',0xc4,0x5e,0xa2 }
+
+#define OP_PROVIDES 0
+#define OP_ADDPID   1
+#define OP_DELPID   2
+#define OP_TUNE     3
+#define OP_ALLOWED  4
+
+struct ScLink {
+  char magic[6];
+  short op;
+  const cDevice *dev;
+  union {
+    unsigned short *caids;
+    struct {
+      int pid;
+      int type;
+      } pids;
+    struct {
+      int source;
+      int transponder;
+      } tune;
+    } data;
+  int num;
+  };
+
+static const char magic[] = SC_MAGIC;
+
+void PrepareScLink(struct ScLink *link, const cDevice *dev, int op)
+{
+  memcpy(link->magic,magic,sizeof(link->magic));
+  link->op=op;
+  link->dev=dev;
+  link->num=-1;
+}
+
+int DoScLinkOp(cPlugin *p, struct ScLink *link)
+{
+  if(p) {
+    p->SetupParse((const char *)link,"");
+    return link->num;
+    }
+  return -1;
+}
+
diff --git a/contrib/sasc-ng/sc/include/vdr/sdt.h b/contrib/sasc-ng/sc/include/vdr/sdt.h
new file mode 100644 (file)
index 0000000..a427119
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * sdt.h: SDT section filter
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sdt.h 1.2 2004/01/05 14:30:14 kls Exp $
+ */
+
+#ifndef __SDT_H
+#define __SDT_H
+
+#include "filter.h"
+#include "pat.h"
+
+class cSdtFilter : public cFilter {
+private:
+  cSectionSyncer sectionSyncer;
+  cPatFilter *patFilter;
+protected:
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+public:
+  cSdtFilter(cPatFilter *PatFilter);
+  virtual void SetStatus(bool On);
+  };
+
+#endif //__SDT_H
diff --git a/contrib/sasc-ng/sc/include/vdr/sections.h b/contrib/sasc-ng/sc/include/vdr/sections.h
new file mode 100644 (file)
index 0000000..f11f479
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * sections.h: Section data handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sections.h 1.5 2005/08/13 11:23:55 kls Exp $
+ */
+
+#ifndef __SECTIONS_H
+#define __SECTIONS_H
+
+#include <time.h>
+#include "filter.h"
+#include "thread.h"
+#include "tools.h"
+
+class cDevice;
+class cChannel;
+class cFilterHandle;
+class cSectionHandlerPrivate;
+
+class cSectionHandler : public cThread {
+  friend class cFilter;
+private:
+  cSectionHandlerPrivate *shp;
+  cDevice *device;
+  int statusCount;
+  bool on, waitForLock;
+  time_t lastIncompleteSection;
+  cList<cFilter> filters;
+  cList<cFilterHandle> filterHandles;
+  void Add(const cFilterData *FilterData);
+  void Del(const cFilterData *FilterData);
+  virtual void Action(void);
+public:
+  cSectionHandler(cDevice *Device);
+  virtual ~cSectionHandler();
+  int Source(void);
+  int Transponder(void);
+  const cChannel *Channel(void);
+  void Attach(cFilter *Filter);
+  void Detach(cFilter *Filter);
+  void SetChannel(const cChannel *Channel);
+  void SetStatus(bool On);
+  };
+
+#endif //__SECTIONS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/skins.h b/contrib/sasc-ng/sc/include/vdr/skins.h
new file mode 100644 (file)
index 0000000..e4a500b
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * skins.h: The optical appearance of the OSD
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: skins.h 1.15 2007/01/04 13:08:55 kls Exp $
+ */
+
+#ifndef __SKINS_H
+#define __SKINS_H
+
+#include "channels.h"
+#include "epg.h"
+#include "keys.h"
+#include "osd.h"
+#include "recording.h"
+#include "themes.h"
+#include "thread.h"
+#include "tools.h"
+
+enum eMessageType { mtStatus = 0, mtInfo, mtWarning, mtError }; // will be used to calculate color offsets!
+
+class cSkinDisplay {
+private:
+  static cSkinDisplay *current;
+  int editableWidth; //XXX this is not nice, but how else could we know this value?
+public:
+  cSkinDisplay(void);
+  virtual ~cSkinDisplay();
+  int EditableWidth(void) { return editableWidth; }
+  void SetEditableWidth(int Width) { editableWidth = Width; }
+       ///< If an item is set through a call to cSkinDisplayMenu::SetItem(), this
+       ///< function shall be called to set the width of the rightmost tab separated
+       ///< field. This information will be used for editable items.
+  virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL) {}
+       ///< Sets the color buttons to the given strings, provided this cSkinDisplay
+       ///< actually has a color button display.
+  virtual void SetMessage(eMessageType Type, const char *Text) {}
+       ///< Sets a one line message Text, with the given Type. Type can be used
+       ///< to determine, e.g., the colors for displaying the Text.
+  virtual void Flush(void) {}
+       ///< Actually draws the OSD display to the output device.
+  static cSkinDisplay *Current(void) { return NULL; }
+       ///< Returns the currently active cSkinDisplay.
+  };
+
+class cSkinDisplayChannel : public cSkinDisplay {
+       ///< This class is used to display the current channel, together with
+       ///< the present and following EPG even. How and to what extent this
+       ///< is done is totally up to the derived class.
+public:
+  virtual void SetChannel(const cChannel *Channel, int Number) = 0;
+       ///< Sets the current channel to Channel. If Number is not 0, the
+       ///< user is in the process of entering a channel number, which must
+       ///< be displayed accordingly.
+  virtual void SetEvents(const cEvent *Present, const cEvent *Following) = 0;
+       ///< Sets the Present and Following EPG events. If either of these
+       ///< is not available, NULL will be given.
+  virtual void SetMessage(eMessageType Type, const char *Text) = 0;
+       ///< Sets a one line message Text, with the given Type. Type can be used
+       ///< to determine, e.g., the colors for displaying the Text.
+       ///< If Text is NULL, any previously displayed message must be removed, and
+       ///< any previous contents overwritten by the message must be restored.
+  /*TODO
+  SetButtons
+    Red    = Video options
+    Green  = Info now
+    Yellow = Info next
+  */
+  };
+
+class cSkinDisplayMenu : public cSkinDisplay {
+       ///< This class implements the general purpose menu display, which is
+       ///< used throughout the program to display information and let the
+       ///< user interact with items.
+       ///< A menu consists of the following fields, each of which is explicitly
+       ///< set by calls to the member functions below:
+       ///< - Title: a single line of text, indicating what this menu displays
+       ///< - Color buttons: the red, green, yellow and blue buttons, used for
+       ///<   various functions
+       ///< - Message: a one line message, indicating a Status, Info, Warning,
+       ///<   or Error condition
+       ///< - Central area: the main central area of the menu, used to display
+       ///<   one of the following:
+       ///<   - Items: a list of single line items, of which the user may be
+       ///<     able to select one
+       ///<   - Event: the full information about one EPG event
+       ///<   - Text: a multi line, scrollable text
+public:
+  enum { MaxTabs = 6 };
+private:
+  int tabs[MaxTabs];
+protected:
+  cTextScroller textScroller;
+  int Tab(int n) { return (n >= 0 && n < MaxTabs) ? tabs[n] : 0; }
+       ///< Returns the offset of the given tab from the left border of the
+       ///< item display area. The value returned is in pixel.
+  const char *GetTabbedText(const char *s, int Tab);
+       ///< Returns the part of the given string that follows the given
+       ///< Tab (where 0 indicates the beginning of the string). If no such
+       ///< part can be found, NULL will be returned.
+public:
+  cSkinDisplayMenu(void);
+  virtual void SetTabs(int Tab1, int Tab2 = 0, int Tab3 = 0, int Tab4 = 0, int Tab5 = 0);
+       ///< Sets the tab columns to the given values, which are the number of
+       ///< characters in each column.
+  virtual void Scroll(bool Up, bool Page);
+       ///< If this menu contains a text area that can be scrolled, this function
+       ///< will be called to actually scroll the text. Up indicates whether the
+       ///< text shall be scrolled up or down, and Page is true if it shall be
+       ///< scrolled by a full page, rather than a single line. An object of the
+       ///< cTextScroller class can be used to implement the scrolling text area.
+  virtual int MaxItems(void) = 0;
+       ///< Returns the maximum number of items the menu can display.
+  virtual void Clear(void) = 0;
+       ///< Clears the entire central area of the menu.
+  virtual void SetTitle(const char *Title) = 0;
+       ///< Sets the title of this menu to Title.
+  virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL) = 0;
+       ///< Sets the color buttons to the given strings. If any of the values is
+       ///< NULL, any previous text must be removed from the related button.
+  virtual void SetMessage(eMessageType Type, const char *Text) = 0;
+       ///< Sets a one line message Text, with the given Type. Type can be used
+       ///< to determine, e.g., the colors for displaying the Text.
+       ///< If Text is NULL, any previously displayed message must be removed, and
+       ///< any previous contents overwritten by the message must be restored.
+  virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable) = 0;
+       ///< Sets the item at the given Index to Text. Index is between 0 and the
+       ///< value returned by MaxItems(), minus one. Text may contain tab characters ('\t'),
+       ///< which shall be used to separate the text into several columns, according to the
+       ///< values set by a prior call to SetTabs(). If Current is true, this item shall
+       ///< be drawn in a way indicating to the user that it is the currently selected
+       ///< one. Selectable can be used to display items differently that can't be
+       ///< selected by the user.
+       ///< Whenever the current status is moved from one item to another,
+       ///< this function will be first called for the old current item
+       ///< with Current set to false, and then for the new current item
+       ///< with Current set to true.
+  /*TODO
+  virtual void SetItem(const cEvent *Event, int Index, bool Current, bool Selectable, bool NowNext???, bool Schedule???);
+  virtual void SetItem(const cTimer *Timer, int Index, bool Current, bool Selectable);
+  virtual void SetItem(const cChannel *Channel, int Index, bool Current, bool Selectable);
+  virtual void SetItem(const cRecording *Recording, int Index, bool Current, bool Selectable);
+  --> false: call SetItem(text)
+  */
+  virtual void SetEvent(const cEvent *Event) = 0;
+       ///< Sets the Event that shall be displayed, using the entire central area
+       ///< of the menu. The Event's 'description' shall be displayed using a
+       ///< cTextScroller, and the Scroll() function will be called to drive scrolling
+       ///< that text if necessary.
+  virtual void SetRecording(const cRecording *Recording) = 0;
+       ///< Sets the Recording that shall be displayed, using the entire central area
+       ///< of the menu. The Recording's 'description' shall be displayed using a
+       ///< cTextScroller, and the Scroll() function will be called to drive scrolling
+       ///< that text if necessary.
+  virtual void SetText(const char *Text, bool FixedFont) = 0;
+       ///< Sets the Text that shall be displayed, using the entire central area
+       ///< of the menu. The Text shall be displayed using a cTextScroller, and
+       ///< the Scroll() function will be called to drive scrolling that text if
+       ///< necessary.
+  //XXX ??? virtual void SetHelp(const char *Help) = 0;
+  virtual int GetTextAreaWidth(void) const;
+       ///< Returns the width in pixel of the area which is used to display text
+       ///< with SetText(). The width of the area is the width of the central area
+       ///< minus the width of any possibly displayed scroll-bar or other decoration.
+       ///< The default implementation returns 0. Therefore a caller of this method
+       ///< must be prepared to receive 0 if the plugin doesn't implement this method.
+  virtual const cFont *GetTextAreaFont(bool FixedFont) const;
+       ///< Returns a pointer to the font which is used to display text with SetText().
+       ///< The parameter FixedFont has the same meaning as in SetText(). The default
+       ///< implementation returns NULL. Therefore a caller of this method must be
+       ///< prepared to receive NULL if the plugin doesn't implement this method.
+       ///< The returned pointer is valid a long as the instance of cSkinDisplayMenu
+       ///< exists.
+  };
+
+class cSkinDisplayReplay : public cSkinDisplay {
+       ///< This class implements the progress display used during replay of
+       ///< a recording.
+protected:
+  const cMarks *marks;
+  class cProgressBar : public cBitmap {
+  protected:
+    int total;
+    int Pos(int p) { return p * Width() / total; }
+    void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent);
+  public:
+    cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent);
+    };
+public:
+  cSkinDisplayReplay(void);
+  virtual void SetMarks(const cMarks *Marks);
+       ///< Sets the editing marks to Marks, which shall be used to display the
+       ///< progress bar through a cProgressBar object.
+  virtual void SetTitle(const char *Title) = 0;
+       ///< Sets the title of the recording.
+  virtual void SetMode(bool Play, bool Forward, int Speed) = 0;
+       ///< Sets the current replay mode, which can be used to display some
+       ///< indicator, showing the user whether we are currently in normal
+       ///< play mode, fast forward etc.
+  virtual void SetProgress(int Current, int Total) = 0;
+       ///< This function will be called whenever the position in or the total
+       ///< length of the recording has changed. A cProgressBar shall then be
+       ///< used to display a progress indicator.
+  virtual void SetCurrent(const char *Current) = 0;
+       ///< Sets the current position within the recording, as a user readable
+       ///< string if the form "h:mm:ss.ff". The ".ff" part, indicating the
+       ///< frame number, is optional and the actual implementation needs to
+       ///< take care that it is erased from the display when a Current string
+       ///< _with_ ".ff" is followed by one without it.
+  virtual void SetTotal(const char *Total) = 0;
+       ///< Sets the total length of the recording, as a user readable
+       ///< string if the form "h:mm:ss".
+  virtual void SetJump(const char *Jump) = 0;
+       ///< Sets the prompt that allows the user to enter a jump point.
+       ///< Jump is a string of the form "Jump: mm:ss". The actual implementation
+       ///< needs to be able to handle variations in the length of this
+       ///< string, which will occur when the user enters an actual value.
+       ///< If Jump is NULL, the jump prompt shall be removed from the display.
+  virtual void SetMessage(eMessageType Type, const char *Text) = 0;
+       ///< Sets a one line message Text, with the given Type. Type can be used
+       ///< to determine, e.g., the colors for displaying the Text.
+       ///< If Text is NULL, any previously displayed message must be removed, and
+       ///< any previous contents overwritten by the message must be restored.
+  };
+
+class cSkinDisplayVolume : public cSkinDisplay {
+       ///< This class implements the volume/mute display.
+public:
+  virtual void SetVolume(int Current, int Total, bool Mute) = 0;
+       ///< Sets the volume to the given Current value, which is in the range
+       ///< 0...Total. If Mute is true, audio is currently muted and a "mute"
+       ///< indicator shall be displayed.
+  };
+
+class cSkinDisplayTracks : public cSkinDisplay {
+       ///< This class implements the track display.
+public:
+  virtual void SetTrack(int Index, const char * const *Tracks) = 0;
+       ///< Sets the current track to the one given by Index, which
+       ///< points into the Tracks array of strings.
+  virtual void SetAudioChannel(int AudioChannel) = 0;
+       ///< Sets the audio channel indicator.
+       ///< 0=stereo, 1=left, 2=right, -1=don't display the audio channel indicator.
+  };
+
+class cSkinDisplayMessage : public cSkinDisplay {
+       ///< This class implements a simple message display.
+public:
+  virtual void SetMessage(eMessageType Type, const char *Text) = 0;
+       ///< Sets the message to Text. Type can be used to decide how to display
+       ///< the message, for instance in which colors.
+  };
+
+class cSkin : public cListObject {
+private:
+  char *name;
+  cTheme *theme;
+public:
+  cSkin(const char *Name, cTheme *Theme = NULL);
+       ///< Creates a new skin class, with the given Name and Theme.
+       ///< Name will be used to identify this skin in the 'setup.conf'
+       ///< file, and is normally not seen by the user. It should
+       ///< consist of only lowercase letters and digits.
+       ///< Theme must be a static object that survives the entire lifetime
+       ///< of this skin.
+       ///< The constructor of a derived class shall not set up any data
+       ///< structures yet, because whether or not this skin will actually
+       ///< be used is not yet known at this point. All actual work shall
+       ///< be done in the pure functions below.
+       ///< A cSkin object must be created on the heap and shall not be
+       ///< explicitly deleted.
+  virtual ~cSkin();
+  const char *Name(void) { return name; }
+  cTheme *Theme(void) { return theme; }
+  virtual const char *Description(void) = 0;
+       ///< Returns a user visible, single line description of this skin,
+       ///< which may consist of arbitrary text and can, if the skin
+       ///< implementation wishes to do so, be internationalized.
+       ///< The actual text shouldn't be too long, so that it can be
+       ///< fully displayed in the Setup/OSD menu.
+  virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo) = 0;
+       ///< Creates and returns a new object for displaying the current
+       ///< channel. WithInfo indicates whether it shall display only
+       ///< the basic channel data, or also information about the present
+       ///< and following EPG event.
+       ///< The caller must delete the object after use.
+  virtual cSkinDisplayMenu *DisplayMenu(void) = 0;
+       ///< Creates and returns a new object for displaying a menu.
+       ///< The caller must delete the object after use.
+  virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly) = 0;
+       ///< Creates and returns a new object for displaying replay progress.
+       ///< ModeOnly indicates whether this should be a full featured replay
+       ///< display, or just a replay mode indicator.
+       ///< The caller must delete the object after use.
+  virtual cSkinDisplayVolume *DisplayVolume(void) = 0;
+       ///< Creates and returns a new object for displaying the current volume.
+       ///< The caller must delete the object after use.
+  virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) = 0;
+       ///< Creates and returns a new object for displaying the available tracks.
+       ///< NumTracks indicates how many entries in Tracks are available.
+       ///< Tracks will be valid throughout the entire lifetime of the returned
+       ///< cSkinDisplayTrack object.
+       ///< The caller must delete the object after use.
+  virtual cSkinDisplayMessage *DisplayMessage(void) = 0;
+       ///< Creates and returns a new object for displaying a message.
+       ///< The caller must delete the object after use.
+  };
+
+class cSkins : public cList<cSkin> {
+private:
+  cSkin *current;
+  cSkinDisplayMessage *displayMessage;
+  cMutex queueMessageMutex;
+public:
+  cSkins(void);
+  ~cSkins();
+  bool SetCurrent(const char *Name = NULL);
+       ///< Sets the current skin to the one indicated by name.
+       ///< If no such skin can be found, the first one will be used.
+  cSkin *Current(void) { return current; }
+       ///< Returns a pointer to the current skin.
+  bool IsOpen(void) { return cSkinDisplay::Current(); }
+       ///< Returns true if there is currently a skin display object active.
+  eKeys Message(eMessageType Type, const char *s, int Seconds = 0);
+       ///< Displays the given message, either through a currently visible
+       ///< display object that is capable of doing so, or by creating a
+       ///< temporary cSkinDisplayMessage object.
+       ///< The return value is the key pressed by the user. If no user input
+       ///< has been received within Seconds (the default value of 0 results
+       ///< in the value defined for "Message time" in the setup), kNone
+       ///< will be returned.
+  int QueueMessage(eMessageType Type, const char *s, int Seconds = 0, int Timeout = 0);
+       ///< Like Message(), but this function may be called from a background
+       ///< thread. The given message is put into a queue and the main program
+       ///< loop will display it as soon as this is suitable. If Timeout is 0,
+       ///< QueueMessage() returns immediately and the return value will be kNone.
+       ///< If a positive Timeout is given, the thread will wait at most the given
+       ///< number of seconds for the message to be actually displayed (note that
+       ///< the user may currently be doing something that doesn't allow for
+       ///< queued messages to be displayed immediately). If the timeout expires
+       ///< and the message hasn't been displayed yet, the return value is -1
+       ///< and the message will be removed from the queue without being displayed.
+       ///< Positive values of Timeout are only allowed for background threads.
+       ///< If QueueMessage() is called from the foreground thread with a Timeout
+       ///< greater than 0, the call is ignored and nothing is displayed.
+       ///< Queued messages will be displayed in the sequence they have been
+       ///< put into the queue, so messages from different threads may appear
+       ///< mingled. If a particular thread queues a message with a Timeout of
+       ///< -1, and the previous message from the same thread also had a Timeout
+       ///< of -1, only the last message will be displayed. This can be used for
+       ///< progress displays, where only the most recent message is actually
+       ///< important.
+       ///< Type may only be mtInfo, mtWarning or mtError. A call with mtStatus
+       ///< will be ignored. A call with an empty message from a background thread
+       ///< removes all queued messages from the calling thread. A call with
+       ///< an empty message from the main thread will be ignored.
+  void ProcessQueuedMessages(void);
+       ///< Processes the first queued message, if any.
+  void Flush(void);
+       ///< Flushes the currently active cSkinDisplay, if any.
+  virtual void Clear(void);
+       ///< Free up all registered skins
+  };
+
+extern cSkins Skins;
+
+#endif //__SKINS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/sources.h b/contrib/sasc-ng/sc/include/vdr/sources.h
new file mode 100644 (file)
index 0000000..ae941f8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * sources.h: Source handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sources.h 1.4 2005/05/14 09:30:41 kls Exp $
+ */
+
+#ifndef __SOURCES_H
+#define __SOURCES_H
+
+#include "config.h"
+
+class cSource : public cListObject {
+public:
+  enum eSourceType {
+    stNone  = 0x0000,
+    stCable = 0x4000,
+    stSat   = 0x8000,
+    stTerr  = 0xC000,
+    st_Mask = 0xC000,
+    st_Neg  = 0x0800,
+    st_Pos  = 0x07FF,
+    };
+private:
+  int code;
+  char *description;
+public:
+  cSource(void);
+  ~cSource();
+  int Code(void) const { return code; }
+  const char *Description(void) const { return description; }
+  bool Parse(const char *s);
+  static cString ToString(int Code);
+  static int FromString(const char *s);
+  static int FromData(eSourceType SourceType, int Position = 0, bool East = false);
+  static bool IsCable(int Code) { return (Code & st_Mask) == stCable; }
+  static bool IsSat(int Code) { return (Code & st_Mask) == stSat; }
+  static bool IsTerr(int Code) { return (Code & st_Mask) == stTerr; }
+  };
+
+class cSources : public cConfig<cSource> {
+public:
+  cSource *Get(int Code);
+  };
+
+extern cSources Sources;
+
+#endif //__SOURCES_H
diff --git a/contrib/sasc-ng/sc/include/vdr/spu.h b/contrib/sasc-ng/sc/include/vdr/spu.h
new file mode 100644 (file)
index 0000000..39c721f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SPU Decoder Prototype
+ *
+ * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
+ *
+ * This code is distributed under the terms and conditions of the
+ * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
+ *
+ * $Id: spu.h 1.5 2006/04/17 12:48:55 kls Exp $
+ */
+
+#ifndef __SPU_VDR_H
+#define __SPU_VDR_H
+
+#include <inttypes.h>
+
+// --- cSpuDecoder -----------------------------------------------------------
+
+class cSpuDecoder {
+  public:
+    typedef enum { eSpuNormal, eSpuLetterBox, eSpuPanAndScan } eScaleMode;
+  public:
+    //    cSpuDecoder();
+    virtual ~cSpuDecoder();
+
+    virtual int setTime(uint32_t pts) = 0;
+
+    virtual cSpuDecoder::eScaleMode getScaleMode(void) = 0;
+    virtual void setScaleMode(cSpuDecoder::eScaleMode ScaleMode) = 0;
+    virtual void setPalette(uint32_t * pal) = 0;
+    virtual void setHighlight(uint16_t sx, uint16_t sy,
+                              uint16_t ex, uint16_t ey,
+                              uint32_t palette) = 0;
+    virtual void clearHighlight(void) = 0;
+    virtual void Empty(void) = 0;
+    virtual void Hide(void) = 0;
+    virtual void Draw(void) = 0;
+    virtual bool IsVisible(void) = 0;
+    virtual void processSPU(uint32_t pts, uint8_t * buf, bool AllowedShow = true) = 0;
+};
+
+#endif                          // __SPU_VDR_H
diff --git a/contrib/sasc-ng/sc/include/vdr/status.h b/contrib/sasc-ng/sc/include/vdr/status.h
new file mode 100644 (file)
index 0000000..57e4b91
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * status.h: Status monitoring
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: status.h 1.9 2005/12/31 15:15:25 kls Exp $
+ */
+
+#ifndef __STATUS_H
+#define __STATUS_H
+
+#include "config.h"
+#include "device.h"
+#include "player.h"
+#include "tools.h"
+
+class cStatus : public cListObject {
+private:
+  static cList<cStatus> statusMonitors;
+protected:
+  // These functions can be implemented by derived classes to receive status information:
+  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {}
+               // Indicates a channel switch on the given DVB device.
+               // If ChannelNumber is 0, this is before the channel is being switched,
+               // otherwise ChannelNumber is the number of the channel that has been switched to.
+  virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) {}
+               // The given DVB device has started (On = true) or stopped (On = false) recording Name.
+               // Name is the name of the recording, without any directory path. The full file name
+               // of the recording is given in FileName, which may be NULL in case there is no
+               // actual file involved. If On is false, Name may be NULL.
+  virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On) {}
+               // The given player control has started (On = true) or stopped (On = false) replaying Name.
+               // Name is the name of the recording, without any directory path. In case of a player that can't provide
+               // a name, Name can be a string that identifies the player type (like, e.g., "DVD").
+               // The full file name of the recording is given in FileName, which may be NULL in case there is no
+               // actual file involved. If On is false, Name may be NULL.
+  virtual void SetVolume(int Volume, bool Absolute) {}
+               // The volume has been set to the given value, either
+               // absolutely or relative to the current volume.
+  virtual void SetAudioTrack(int Index, const char * const *Tracks) {}
+               // The audio track has been set to the one given by Index, which
+               // points into the Tracks array of strings.
+  virtual void SetAudioChannel(int AudioChannel) {}
+               // The audio channel has been set to the given value.
+               // 0=stereo, 1=left, 2=right, -1=no information available.
+  virtual void OsdClear(void) {}
+               // The OSD has been cleared.
+  virtual void OsdTitle(const char *Title) {}
+               // Title has been displayed in the title line of the menu.
+  virtual void OsdStatusMessage(const char *Message) {}
+               // Message has been displayed in the status line of the menu.
+               // If Message is NULL, the status line has been cleared.
+  virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
+               // The help keys have been set to the given values (may be NULL).
+  virtual void OsdItem(const char *Text, int Index) {}
+               // The OSD displays the given single line Text as menu item at Index.
+  virtual void OsdCurrentItem(const char *Text) {}
+               // The OSD displays the given single line Text as the current menu item.
+  virtual void OsdTextItem(const char *Text, bool Scroll) {}
+               // The OSD displays the given multi line text. If Text points to an
+               // actual string, that text shall be displayed and Scroll has no
+               // meaning. If Text is NULL, Scroll defines whether the previously
+               // received text shall be scrolled up (true) or down (false) and
+               // the text shall be redisplayed with the new offset.
+  virtual void OsdChannel(const char *Text) {}
+               // The OSD displays the single line Text with the current channel information.
+  virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+               // The OSD displays the given programme information.
+public:
+  cStatus(void);
+  virtual ~cStatus();
+  // These functions are called whenever the related status information changes:
+  static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber);
+  static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+  static void MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On);
+  static void MsgSetVolume(int Volume, bool Absolute);
+  static void MsgSetAudioTrack(int Index, const char * const *Tracks);
+  static void MsgSetAudioChannel(int AudioChannel);
+  static void MsgOsdClear(void);
+  static void MsgOsdTitle(const char *Title);
+  static void MsgOsdStatusMessage(const char *Message);
+  static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+  static void MsgOsdItem(const char *Text, int Index);
+  static void MsgOsdCurrentItem(const char *Text);
+  static void MsgOsdTextItem(const char *Text,  bool Scroll = false);
+  static void MsgOsdChannel(const char *Text);
+  static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+  };
+
+#endif //__STATUS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/svdrp.h b/contrib/sasc-ng/sc/include/vdr/svdrp.h
new file mode 100644 (file)
index 0000000..deee42c
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * svdrp.h: Simple Video Disk Recorder Protocol
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: svdrp.h 1.29 2007/04/30 12:28:28 kls Exp $
+ */
+
+#ifndef __SVDRP_H
+#define __SVDRP_H
+
+#include "recording.h"
+#include "tools.h"
+
+class cSocket {
+private:
+  int port;
+  int sock;
+  int queue;
+  void Close(void);
+public:
+  cSocket(int Port, int Queue = 1);
+  ~cSocket();
+  bool Open(void);
+  int Accept(void);
+  };
+
+class cPUTEhandler {
+private:
+  FILE *f;
+  int status;
+  const char *message;
+public:
+  cPUTEhandler(void);
+  ~cPUTEhandler();
+  bool Process(const char *s);
+  int Status(void) { return status; }
+  const char *Message(void) { return message; }
+  };
+
+class cSVDRP {
+private:
+  cSocket socket;
+  cFile file;
+  cRecordings Recordings;
+  cPUTEhandler *PUTEhandler;
+  int numChars;
+  int length;
+  char *cmdLine;
+  time_t lastActivity;
+  static char *grabImageDir;
+  void Close(bool SendReply = false, bool Timeout = false);
+  bool Send(const char *s, int length = -1);
+  void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+  void PrintHelpTopics(const char **hp);
+  void CmdCHAN(const char *Option);
+  void CmdCLRE(const char *Option);
+  void CmdDELC(const char *Option);
+  void CmdDELR(const char *Option);
+  void CmdDELT(const char *Option);
+  void CmdEDIT(const char *Option);
+  void CmdGRAB(const char *Option);
+  void CmdHELP(const char *Option);
+  void CmdHITK(const char *Option);
+  void CmdLSTC(const char *Option);
+  void CmdLSTE(const char *Option);
+  void CmdLSTR(const char *Option);
+  void CmdLSTT(const char *Option);
+  void CmdMESG(const char *Option);
+  void CmdMODC(const char *Option);
+  void CmdMODT(const char *Option);
+  void CmdMOVC(const char *Option);
+  void CmdMOVT(const char *Option);
+  void CmdNEWC(const char *Option);
+  void CmdNEWT(const char *Option);
+  void CmdNEXT(const char *Option);
+  void CmdPLAY(const char *Option);
+  void CmdPLUG(const char *Option);
+  void CmdPUTE(const char *Option);
+  void CmdREMO(const char *Option);
+  void CmdSCAN(const char *Option);
+  void CmdSTAT(const char *Option);
+  void CmdUPDT(const char *Option);
+  void CmdVOLU(const char *Option);
+  void Execute(char *Cmd);
+public:
+  cSVDRP(int Port);
+  ~cSVDRP();
+  bool HasConnection(void) { return file.IsOpen(); }
+  bool Process(void);
+  static void SetGrabImageDir(const char *GrabImageDir);
+  };
+
+#endif //__SVDRP_H
diff --git a/contrib/sasc-ng/sc/include/vdr/themes.h b/contrib/sasc-ng/sc/include/vdr/themes.h
new file mode 100644 (file)
index 0000000..617f1d2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * themes.h: Color themes used by skins
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: themes.h 1.1 2004/05/15 14:22:16 kls Exp $
+ */
+
+#ifndef __THEMES_H
+#define __THEMES_H
+
+#include "i18n.h"
+#include "osd.h"
+
+class cTheme {
+public:
+  enum { MaxThemeColors = 128 };
+private:
+  char *name;
+  char *descriptions[I18nNumLanguages];
+  char *colorNames[MaxThemeColors];
+  tColor colorValues[MaxThemeColors];
+  bool FileNameOk(const char *FileName, bool SetName = false);
+public:
+  cTheme(void);
+       ///< Creates a new theme class.
+  ~cTheme();
+  const char *Name(void) { return name; }
+  const char *Description(void);
+       ///< Returns a user visible, single line description of this theme.
+       ///< The actual text shouldn't be too long, so that it can be
+       ///< fully displayed in the Setup/OSD menu.
+  bool Load(const char *FileName, bool OnlyDescriptions = false);
+       ///< Loads the theme data from the given file.
+  bool Save(const char *FileName);
+       ///< Saves the theme data to the given file.
+       ///< FileName must be in the form "<skin>-<theme>.theme", where <skin>
+       ///< is the name of the skin this theme applies to, and <theme> is the
+       ///< actual theme name, which will be used to identify this theme in the
+       ///< 'setup.conf', and is normally not seen by the user. It should
+       ///< consist of only lowercase letters and digits.
+  int AddColor(const char *Name, tColor Color);
+       ///< Adds a color with the given Name to this theme, initializes it
+       ///< with Color and returns an index into the color array that can
+       ///< be used in a call to Color() later. The index returned from the
+       ///< first call to AddColor() is 0, and subsequent calls will return
+       ///< values that are incremented by 1 with every call.
+       ///< If a color entry with the given Name already exists, its value
+       ///< will be overwritten with Color and the returned index will be
+       ///< that of the existing entry.
+  tColor Color(int Subject);
+       ///< Returns the color for the given Subject. Subject must be one of
+       ///< the values returned by a previous call to AddColor().
+  };
+
+// A helper macro that simplifies defining theme colors.
+#define THEME_CLR(Theme, Subject, Color) static const int Subject = Theme.AddColor(#Subject, Color)
+
+class cThemes {
+private:
+  int numThemes;
+  char **names;
+  char **fileNames;
+  char **descriptions;
+  static char *themesDirectory;
+  void Clear(void);
+public:
+  cThemes(void);
+  ~cThemes();
+  bool Load(const char *SkinName);
+  int NumThemes(void) { return numThemes; }
+  const char *Name(int Index) { return Index < numThemes ? names[Index] : NULL; }
+  const char *FileName(int Index) { return Index < numThemes ? fileNames[Index] : NULL; }
+  const char * const *Descriptions(void) { return descriptions; }
+  int GetThemeIndex(const char *Description);
+  static void SetThemesDirectory(const char *ThemesDirectory);
+  static void Load(const char *SkinName, const char *ThemeName, cTheme *Theme);
+  static void Save(const char *SkinName, cTheme *Theme);
+  };
+
+#endif //__THEMES_H
diff --git a/contrib/sasc-ng/sc/include/vdr/thread.h b/contrib/sasc-ng/sc/include/vdr/thread.h
new file mode 100644 (file)
index 0000000..6eaa79a
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * thread.h: A simple thread base class
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: thread.h 1.39 2007/02/24 16:13:28 kls Exp $
+ */
+
+#ifndef __THREAD_H
+#define __THREAD_H
+
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+class cCondWait {
+private:
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+  bool signaled;
+public:
+  cCondWait(void);
+  ~cCondWait();
+  static void SleepMs(int TimeoutMs);
+       ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+       ///< milliseconds, immediately giving up the calling thread's time
+       ///< slice and thus avoiding a "busy wait".
+       ///< In order to avoid a possible busy wait, TimeoutMs will be automatically
+       ///< limited to values >2.
+  bool Wait(int TimeoutMs = 0);
+       ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+       ///< forever if TimeoutMs is 0.
+       ///< \return Returns true if Signal() has been called, false it the given
+       ///< timeout has expired.
+  void Signal(void);
+       ///< Signals a caller of Wait() that the condition it is waiting for is met.
+  };
+
+class cMutex;
+
+class cCondVar {
+private:
+  pthread_cond_t cond;
+public:
+  cCondVar(void);
+  ~cCondVar();
+  void Wait(cMutex &Mutex);
+  bool TimedWait(cMutex &Mutex, int TimeoutMs);
+  void Broadcast(void);
+  };
+
+class cRwLock {
+private:
+  pthread_rwlock_t rwlock;
+public:
+  cRwLock(bool PreferWriter = false);
+  ~cRwLock();
+  bool Lock(bool Write, int TimeoutMs = 0);
+  void Unlock(void);
+  };
+
+class cMutex {
+  friend class cCondVar;
+private:
+  pthread_mutex_t mutex;
+  int locked;
+public:
+  cMutex(void);
+  ~cMutex();
+  void Lock(void);
+  void Unlock(void);
+  };
+
+typedef pid_t tThreadId;
+
+class cThread {
+  friend class cThreadLock;
+private:
+  bool active;
+  bool running;
+  pthread_t childTid;
+  tThreadId childThreadId;
+  cMutex mutex;
+  char *description;
+  static tThreadId mainThreadId;
+  static void *StartThread(cThread *Thread);
+protected:
+  void SetPriority(int Priority);
+  void Lock(void) { mutex.Lock(); }
+  void Unlock(void) { mutex.Unlock(); }
+  virtual void Action(void) = 0;
+       ///< A derived cThread class must implement the code it wants to
+       ///< execute as a separate thread in this function. If this is
+       ///< a loop, it must check Running() repeatedly to see whether
+       ///< it's time to stop.
+  bool Running(void) { return running; }
+       ///< Returns false if a derived cThread object shall leave its Action()
+       ///< function.
+  void Cancel(int WaitSeconds = 0);
+       ///< Cancels the thread by first setting 'running' to false, so that
+       ///< the Action() loop can finish in an orderly fashion and then waiting
+       ///< up to WaitSeconds seconds for the thread to actually end. If the
+       ///< thread doesn't end by itself, it is killed.
+       ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+       ///< returns immediately, without killing the thread.
+public:
+  cThread(const char *Description = NULL);
+       ///< Creates a new thread.
+       ///< If Description is present, a log file entry will be made when
+       ///< the thread starts and stops. The Start() function must be called
+       ///< to actually start the thread.
+  virtual ~cThread();
+  void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
+  bool Start(void);
+       ///< Actually starts the thread.
+       ///< If the thread is already running, nothing happens.
+  bool Active(void);
+       ///< Checks whether the thread is still alive.
+  static tThreadId ThreadId(void);
+  static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
+  static void SetMainThreadId(void);
+  };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
+// short ones.
+
+class cMutexLock {
+private:
+  cMutex *mutex;
+  bool locked;
+public:
+  cMutexLock(cMutex *Mutex = NULL);
+  ~cMutexLock();
+  bool Lock(cMutex *Mutex);
+  };
+
+// cThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cThreadLock may itself use a cThreadLock to make one longer lock instead of many
+// short ones.
+
+class cThreadLock {
+private:
+  cThread *thread;
+  bool locked;
+public:
+  cThreadLock(cThread *Thread = NULL);
+  ~cThreadLock();
+  bool Lock(cThread *Thread);
+  };
+
+#define LOCK_THREAD cThreadLock ThreadLock(this)
+
+// cPipe implements a pipe that closes all unnecessary file descriptors in
+// the child process.
+
+class cPipe {
+private:
+  pid_t pid;
+  FILE *f;
+public:
+  cPipe(void);
+  ~cPipe();
+  operator FILE* () { return f; }
+  bool Open(const char *Command, const char *Mode);
+  int Close(void);
+  };
+
+// SystemExec() implements a 'system()' call that closes all unnecessary file
+// descriptors in the child process.
+// With Detached=true, calls command in background and in a separate session,
+// with stdin connected to /dev/null.
+
+int SystemExec(const char *Command, bool Detached = false);
+
+#endif //__THREAD_H
diff --git a/contrib/sasc-ng/sc/include/vdr/timers.h b/contrib/sasc-ng/sc/include/vdr/timers.h
new file mode 100644 (file)
index 0000000..64d562b
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * timers.h: Timer handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: timers.h 1.30 2007/06/03 13:24:58 kls Exp $
+ */
+
+#ifndef __TIMERS_H
+#define __TIMERS_H
+
+#include "channels.h"
+#include "config.h"
+#include "epg.h"
+#include "tools.h"
+
+enum eTimerFlags { tfNone      = 0x0000,
+                   tfActive    = 0x0001,
+                   tfInstant   = 0x0002,
+                   tfVps       = 0x0004,
+                   tfRecording = 0x0008,
+                   tfAll       = 0xFFFF,
+                 };
+enum eTimerMatch { tmNone, tmPartial, tmFull };
+
+class cTimer : public cListObject {
+  friend class cMenuEditTimer;
+private:
+  mutable time_t startTime, stopTime;
+  time_t lastSetEvent;
+  bool recording, pending, inVpsMargin;
+  uint flags;
+  cChannel *channel;
+  mutable time_t day; ///< midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating timer
+  int weekdays;       ///< bitmask, lowest bits: SSFTWTM  (the 'M' is the LSB)
+  int start;
+  int stop;
+  int priority;
+  int lifetime;
+  char file[MaxFileName];
+  char *aux;
+  const cEvent *event;
+public:
+  cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL);
+  cTimer(const cEvent *Event);
+  cTimer(const cTimer &Timer);
+  virtual ~cTimer();
+  cTimer& operator= (const cTimer &Timer);
+  virtual int Compare(const cListObject &ListObject) const;
+  bool Recording(void) const { return recording; }
+  bool Pending(void) const { return pending; }
+  bool InVpsMargin(void) const { return inVpsMargin; }
+  uint Flags(void) const { return flags; }
+  const cChannel *Channel(void) const { return channel; }
+  time_t Day(void) const { return day; }
+  int WeekDays(void) const { return weekdays; }
+  int Start(void) const { return start; }
+  int Stop(void) const { return stop; }
+  int Priority(void) const { return priority; }
+  int Lifetime(void) const { return lifetime; }
+  const char *File(void) const { return file; }
+  time_t FirstDay(void) const { return weekdays ? day : 0; }
+  const char *Aux(void) const { return aux; }
+  cString ToText(bool UseChannelID = false);
+  cString ToDescr(void) const;
+  const cEvent *Event(void) const { return event; }
+  bool Parse(const char *s);
+  bool Save(FILE *f);
+  bool IsSingleEvent(void) const;
+  static int GetMDay(time_t t);
+  static int GetWDay(time_t t);
+  bool DayMatches(time_t t) const;
+  static time_t IncDay(time_t t, int Days);
+  static time_t SetTime(time_t t, int SecondsFromMidnight);
+  char *SetFile(const char *File);
+  bool Matches(time_t t = 0, bool Directly = false, int Margin = 0) const;
+  int Matches(const cEvent *Event, int *Overlap = NULL) const;
+  bool Expired(void) const;
+  time_t StartTime(void) const;
+  time_t StopTime(void) const;
+  void SetEventFromSchedule(const cSchedules *Schedules = NULL);
+  void SetEvent(const cEvent *Event);
+  void SetRecording(bool Recording);
+  void SetPending(bool Pending);
+  void SetInVpsMargin(bool InVpsMargin);
+  void SetPriority(int Priority);
+  void SetFlags(uint Flags);
+  void ClrFlags(uint Flags);
+  void InvFlags(uint Flags);
+  bool HasFlags(uint Flags) const;
+  void Skip(void);
+  void OnOff(void);
+  cString PrintFirstDay(void) const;
+  static int TimeToInt(int t);
+  static bool ParseDay(const char *s, time_t &Day, int &WeekDays);
+  static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars);
+  };
+
+class cTimers : public cConfig<cTimer> {
+private:
+  int state;
+  int beingEdited;
+  time_t lastSetEvents;
+  time_t lastDeleteExpired;
+public:
+  cTimers(void);
+  cTimer *GetTimer(cTimer *Timer);
+  cTimer *GetMatch(time_t t);
+  cTimer *GetMatch(const cEvent *Event, int *Match = NULL);
+  cTimer *GetNextActiveTimer(void);
+  int BeingEdited(void) { return beingEdited; }
+  void IncBeingEdited(void) { beingEdited++; }
+  void DecBeingEdited(void) { if (!--beingEdited) lastSetEvents = 0; }
+  void SetModified(void);
+  bool Modified(int &State);
+      ///< Returns true if any of the timers have been modified, which
+      ///< is detected by State being different than the internal state.
+      ///< Upon return the internal state will be stored in State.
+  void SetEvents(void);
+  void DeleteExpired(void);
+  };
+
+extern cTimers Timers;
+
+#endif //__TIMERS_H
diff --git a/contrib/sasc-ng/sc/include/vdr/tools.h b/contrib/sasc-ng/sc/include/vdr/tools.h
new file mode 100644 (file)
index 0000000..fc9c87b
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * tools.h: Various tools
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: tools.h 1.103 2007/06/23 13:34:28 kls Exp $
+ */
+
+#ifndef __TOOLS_H
+#define __TOOLS_H
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <iconv.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+typedef unsigned char uchar;
+
+extern int SysLogLevel;
+
+#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR,   a) : void() )
+#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_INFO,  a) : void() )
+#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_DEBUG, a) : void() )
+
+#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<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+void syslog_with_tid(int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+
+#define BCDCHARTOINT(x) (10 * ((x & 0xF0) >> 4) + (x & 0xF))
+int BCD2INT(int x);
+
+// Unfortunately there are no platform independent macros for unaligned
+// access. so we do it this way:
+
+template<class T> inline T get_unaligned(T *p)
+{
+  struct s { T v; } __attribute__((packed));
+  return ((s *)p)->v;
+}
+
+template<class T> inline void put_unaligned(unsigned int v, T* p)
+{
+  struct s { T v; } __attribute__((packed));
+  ((s *)p)->v = v;
+}
+
+// When handling strings that might contain UTF-8 characters, it may be necessary
+// to process a "symbol" that consists of several actual character bytes. The
+// following functions allow transparently accessing a "char *" string without
+// having to worry about what character set is actually used.
+
+int Utf8CharLen(const char *s);
+    ///< Returns the number of character bytes at the beginning of the given
+    ///< string that form a UTF-8 symbol.
+uint Utf8CharGet(const char *s, int Length = 0);
+    ///< Returns the UTF-8 symbol at the beginning of the given string.
+    ///< Length can be given from a previous call to Utf8CharLen() to avoid calculating
+    ///< it again. If no Length is given, Utf8CharLen() will be called.
+int Utf8CharSet(uint c, char *s = NULL);
+    ///< Converts the given UTF-8 symbol to a sequence of character bytes and copies
+    ///< them to the given string. Returns the number of bytes written. If no string
+    ///< is given, only the number of bytes is returned and nothing is copied.
+int Utf8SymChars(const char *s, int Symbols);
+    ///< Returns the number of character bytes at the beginning of the given
+    ///< string that form at most the given number of UTF-8 symbols.
+int Utf8StrLen(const char *s);
+    ///< Returns the number of UTF-8 symbols formed by the given string of
+    ///< character bytes.
+char *Utf8Strn0Cpy(char *Dest, const char *Src, int n);
+    ///< Copies at most n character bytes from Src to Dst, making sure that the
+    ///< resulting copy ends with a complete UTF-8 symbol. The copy is guaranteed
+    ///< to be zero terminated.
+    ///< Returns a pointer to Dest.
+int Utf8ToArray(const char *s, uint *a, int Size);
+    ///< Converts the given character bytes (including the terminating 0) into an
+    ///< array of UTF-8 symbols of the given Size. Returns the number of symbols
+    ///< in the array (without the terminating 0).
+int Utf8FromArray(const uint *a, char *s, int Size, int Max = -1);
+    ///< Converts the given array of UTF-8 symbols (including the terminating 0)
+    ///< into a sequence of character bytes of at most Size length. Returns the
+    ///< number of character bytes written (without the terminating 0).
+    ///< If Max is given, only that many symbols will be converted.
+    ///< The resulting string is always zero-terminated if Size is big enough.
+
+// When allocating buffer space, make sure we reserve enough space to hold
+// a string in UTF-8 representation:
+
+#define Utf8BufSize(s) ((s) * 4)
+
+// The following macros automatically use the correct versions of the character
+// class functions:
+
+#define Utf8to(conv, c) (cCharSetConv::SystemCharacterTable() ? to##conv(c) : tow##conv(c))
+#define Utf8is(ccls, c) (cCharSetConv::SystemCharacterTable() ? is##ccls(c) : isw##ccls(c))
+
+class cCharSetConv {
+private:
+  iconv_t cd;
+  char *result;
+  size_t length;
+  static char *systemCharacterTable;
+public:
+  cCharSetConv(const char *FromCode = NULL, const char *ToCode = NULL);
+     ///< Sets up a character set converter to convert from FromCode to ToCode.
+     ///< If FromCode is NULL, the previously set systemCharacterTable is used.
+     ///< If ToCode is NULL, "UTF-8" is used.
+  ~cCharSetConv();
+  const char *Convert(const char *From, char *To = NULL, size_t ToLength = 0);
+     ///< Converts the given Text from FromCode to ToCode (as set in the cosntructor).
+     ///< If To is given, it is used to copy at most ToLength bytes of the result
+     ///< (including the terminating 0) into that buffer. If To is not given,
+     ///< the result is copied into a dynamically allocated buffer and is valid as
+     ///< long as this object lives, or until the next call to Convert(). The
+     ///< return value always points to the result if the conversion was successful
+     ///< (even if a fixed size To buffer was given and the result didn't fit into
+     ///< it). If the string could not be converted, the result points to the
+     ///< original From string.
+  static const char *SystemCharacterTable(void) { return systemCharacterTable; }
+  static void SetSystemCharacterTable(const char *CharacterTable);
+  };
+
+class cString {
+private:
+  char *s;
+public:
+  cString(const char *S = NULL, bool TakePointer = false);
+  cString(const cString &String);
+  virtual ~cString();
+  operator const char * () const { return s; } // for use in (const char *) context
+  const char * operator*() const { return s; } // for use in (const void *) context (printf() etc.)
+  cString &operator=(const cString &String);
+  static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+  };
+
+ssize_t safe_read(int filedes, void *buffer, size_t size);
+ssize_t safe_write(int filedes, const void *buffer, size_t size);
+void writechar(int filedes, char c);
+int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs = 0, int RetryMs = 0);
+    ///< Writes either all Data to the given file descriptor, or nothing at all.
+    ///< If TimeoutMs is greater than 0, it will only retry for that long, otherwise
+    ///< it will retry forever. RetryMs defines the time between two retries.
+char *strcpyrealloc(char *dest, const char *src);
+char *strn0cpy(char *dest, const char *src, size_t n);
+char *strreplace(char *s, char c1, char c2);
+char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary!
+char *skipspace(const char *s);
+char *stripspace(char *s);
+char *compactspace(char *s);
+cString strescape(const char *s, const char *chars);
+bool startswith(const char *s, const char *p);
+bool endswith(const char *s, const char *p);
+bool isempty(const char *s);
+int numdigits(int n);
+bool isnumber(const char *s);
+cString itoa(int n);
+cString AddDirectory(const char *DirName, const char *FileName);
+int FreeDiskSpaceMB(const char *Directory, int *UsedMB = NULL);
+bool DirectoryOk(const char *DirName, bool LogErrors = false);
+bool MakeDirs(const char *FileName, bool IsDirectory = false);
+bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
+bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false);
+int DirSizeMB(const char *DirName); ///< returns the total size of the files in the given directory, or -1 in case of an error
+char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
+bool SpinUpDisk(const char *FileName);
+void TouchFile(const char *FileName);
+time_t LastModifiedTime(const char *FileName);
+cString WeekDayName(int WeekDay);
+cString WeekDayName(time_t t);
+cString WeekDayNameFull(int WeekDay);
+cString WeekDayNameFull(time_t t);
+cString DayDateTime(time_t t = 0);
+cString TimeToString(time_t t);
+cString DateString(time_t t);
+cString TimeString(time_t t);
+uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality = 100);
+    ///< Converts the given Memory to a JPEG image and returns a pointer
+    ///< to the resulting image. Mem must point to a data block of exactly
+    ///< (Width * Height) triplets of RGB image data bytes. Upon return, Size
+    ///< will hold the number of bytes of the resulting JPEG data.
+    ///< Quality can be in the range 0..100 and controls the quality of the
+    ///< resulting image, where 100 is "best". The caller takes ownership of
+    ///< the result and has to delete it once it is no longer needed.
+    ///< The result may be NULL in case of an error.
+
+class cBase64Encoder {
+private:
+  const uchar *data;
+  int length;
+  int maxResult;
+  int i;
+  char *result;
+  static const char *b64;
+public:
+  cBase64Encoder(const uchar *Data, int Length, int MaxResult = 64);
+      ///< Sets up a new base 64 encoder for the given Data, with the given Length.
+      ///< Data will not be copied and must be valid as long as NextLine() will be
+      ///< called. MaxResult defines the maximum number of characters in any
+      ///< result line. The resulting lines may be shorter than MaxResult in case
+      ///< its value is not a multiple of 4.
+  ~cBase64Encoder();
+  const char *NextLine(void);
+      ///< Returns the next line of encoded data (terminated by '\0'), or NULL if
+      ///< there is no more encoded data. The caller must call NextLine() and process
+      ///< each returned line until NULL is returned, in order to get the entire
+      ///< data encoded. The returned data is only valid until the next time NextLine()
+      ///< is called, or until the object is destroyed.
+  };
+
+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 cReadLine {
+private:
+  size_t size;
+  char *buffer;
+public:
+  cReadLine(void);
+  ~cReadLine();
+  char *Read(FILE *f);
+  };
+
+class cPoller {
+private:
+  enum { MaxPollFiles = 16 };
+  pollfd pfd[MaxPollFiles];
+  int numFileHandles;
+public:
+  cPoller(int FileHandle = -1, bool Out = false);
+  bool Add(int FileHandle, bool Out);
+  bool Poll(int TimeoutMs = 0);
+  };
+
+class cReadDir {
+private:
+  DIR *directory;
+  struct dirent *result;
+  union { // according to "The GNU C Library Reference Manual"
+    struct dirent d;
+    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
+    } u;
+public:
+  cReadDir(const char *Directory);
+  ~cReadDir();
+  bool Ok(void) { return directory != NULL; }
+  struct dirent *Next(void);
+  };
+
+class cFile {
+private:
+  static bool files[];
+  static int maxFiles;
+  int f;
+public:
+  cFile(void);
+  ~cFile();
+  operator int () { return f; }
+  bool Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  bool Open(int FileDes);
+  void Close(void);
+  bool IsOpen(void) { return f >= 0; }
+  bool Ready(bool Wait = true);
+  static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
+  static bool FileReady(int FileDes, int TimeoutMs = 1000);
+  static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
+  };
+
+class cSafeFile {
+private:
+  FILE *f;
+  char *fileName;
+  char *tempName;
+public:
+  cSafeFile(const char *FileName);
+  ~cSafeFile();
+  operator FILE* () { return f; }
+  bool Open(void);
+  bool Close(void);
+  };
+
+/// cUnbufferedFile is used for large files that are mainly written or read
+/// in a streaming manner, and thus should not be cached.
+
+class cUnbufferedFile {
+private:
+  int fd;
+  off_t curpos;
+  off_t cachedstart;
+  off_t cachedend;
+  off_t begin;
+  off_t lastpos;
+  off_t ahead;
+  size_t readahead;
+  size_t written;
+  size_t totwritten;
+  int FadviseDrop(off_t Offset, off_t Len);
+public:
+  cUnbufferedFile(void);
+  ~cUnbufferedFile();
+  int Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  int Close(void);
+  void SetReadAhead(size_t ra);
+  off_t Seek(off_t Offset, int Whence);
+  ssize_t Read(void *Data, size_t Size);
+  ssize_t Write(const void *Data, size_t Size);
+  static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  };
+
+class cLockFile {
+private:
+  char *fileName;
+  int f;
+public:
+  cLockFile(const char *Directory);
+  ~cLockFile();
+  bool Lock(int WaitSeconds = 0);
+  void Unlock(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 T> 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 T> 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 Clear(void) {}
+  void Sort(__compar_fn_t Compare)
+  {
+    qsort(data, size, sizeof(T), Compare);
+  }
+  };
+
+inline int CompareStrings(const void *a, const void *b)
+{
+  return strcmp(*(const char **)a, *(const char **)b);
+}
+
+class cStringList : public cVector<char *> {
+public:
+  cStringList(int Allocated = 10): cVector<char *>(Allocated) {}
+  virtual ~cStringList();
+  int Find(const char *s) const;
+  void Sort(void) { cVector<char *>::Sort(CompareStrings); }
+  virtual void Clear(void);
+  };
+
+class cFileNameList : public cStringList {
+public:
+  cFileNameList(const char *Directory = NULL);
+  bool Load(const char *Directory);
+  };
+
+class cHashObject : public cListObject {
+  friend class cHashBase;
+private:
+  unsigned int id;
+  cListObject *object;
+public:
+  cHashObject(cListObject *Object, unsigned int Id) { object = Object; id = Id; }
+  cListObject *Object(void) { return object; }
+  };
+
+class cHashBase {
+private:
+  cList<cHashObject> **hashTable;
+  int size;
+  unsigned int hashfn(unsigned int Id) const { return Id % size; }
+protected:
+  cHashBase(int Size);
+public:
+  virtual ~cHashBase();
+  void Add(cListObject *Object, unsigned int Id);
+  void Del(cListObject *Object, unsigned int Id);
+  void Clear(void);
+  cListObject *Get(unsigned int Id) const;
+  cList<cHashObject> *GetList(unsigned int Id) const;
+  };
+
+#define HASHSIZE 512
+
+template<class T> class cHash : public cHashBase {
+public:
+  cHash(int Size = HASHSIZE) : cHashBase(Size) {}
+  T *Get(unsigned int Id) const { return (T *)cHashBase::Get(Id); }
+};
+
+#endif //__TOOLS_H
diff --git a/contrib/sasc-ng/sc/libsi/descriptor.c b/contrib/sasc-ng/sc/libsi/descriptor.c
new file mode 100644 (file)
index 0000000..1654a33
--- /dev/null
@@ -0,0 +1,980 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: descriptor.c 1.21 2006/05/28 14:25:30 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#include <string.h>
+#include "descriptor.h"
+
+namespace SI {
+
+void ShortEventDescriptor::Parse() {
+   int offset=0;
+   const descr_short_event *s;
+   data.setPointerAndOffset<const descr_short_event>(s, offset);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+   name.setDataAndOffset(data+offset, s->event_name_length, offset);
+   const descr_short_event_mid *mid;
+   data.setPointerAndOffset<const descr_short_event_mid>(mid, offset);
+   text.setData(data+offset, mid->text_length);
+}
+
+int ExtendedEventDescriptor::getDescriptorNumber() {
+   return s->descriptor_number;
+}
+
+int ExtendedEventDescriptor::getLastDescriptorNumber() {
+   return s->last_descriptor_number;
+}
+
+void ExtendedEventDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_extended_event>(s, offset);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+   itemLoop.setDataAndOffset(data+offset, s->length_of_items, offset);
+   const descr_extended_event_mid *mid;
+   data.setPointerAndOffset<const descr_extended_event_mid>(mid, offset);
+   text.setData(data+offset, mid->text_length);
+}
+
+void ExtendedEventDescriptor::Item::Parse() {
+   int offset=0;
+   const item_extended_event *first;
+   data.setPointerAndOffset<const item_extended_event>(first, offset);
+   itemDescription.setDataAndOffset(data+offset, first->item_description_length, offset);
+   const item_extended_event_mid *mid;
+   data.setPointerAndOffset<const item_extended_event_mid>(mid, offset);
+   item.setData(data+offset, mid->item_length);
+}
+
+/*int ExtendedEventDescriptors::getTextLength() {
+   int ret=0;
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+      ret+=d->text.getLength();
+      ExtendedEventDescriptor::Item item;
+      for (Loop::Iterator it; d->itemLoop.hasNext(it);   ) {
+         item=d->itemLoop.getNext(it);
+         ret+=item.item.getLength();
+         ret+=item.itemDescription.getLength();
+         ret+=2; //the blanks
+      }
+   }
+   return ret;
+}*/
+
+int ExtendedEventDescriptors::getMaximumTextLength(const char *separation1, const char *separation2) {
+   //add length of plain text, of itemized text with separators, and for one separator between the two fields.
+   return getMaximumTextPlainLength()+getMaximumTextItemizedLength(separation1, separation2)+strlen(separation2);
+}
+
+char *ExtendedEventDescriptors::getText(const char *separation1, const char *separation2) {
+   int size = getMaximumTextLength(separation1, separation2);
+   char *text=new char[size];
+   return getText(text, size, separation1, separation2);
+}
+
+char *ExtendedEventDescriptors::getText(char *buffer, int size, const char *separation1, const char *separation2) {
+   int index=0, len;
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+      d->text.getText(buffer+index, size);
+      len = strlen(buffer+index);
+      index += len;
+      size -= len;
+   }
+
+   int sepLen1 = strlen(separation1);
+   int sepLen2 = strlen(separation2);
+   bool separated = false;
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+
+      ExtendedEventDescriptor::Item item;
+      for (Loop::Iterator it; d->itemLoop.getNext(item, it);   ) {
+         if (!separated && size > sepLen2) {
+            strcpy(buffer+index, separation2); // let's have a separator between the long text and the items
+            index += sepLen2;
+            size -= sepLen2;
+            separated = true;
+         }
+
+         item.itemDescription.getText(buffer+index, size);
+         len = strlen(buffer+index);
+         index += len;
+         size -= len;
+         if (size > sepLen1) {
+            strcpy(buffer+index, separation1);
+            index += sepLen1;
+            size -= sepLen1;
+         }
+
+         item.item.getText(buffer+index, size);
+         len = strlen(buffer+index);
+         index += len;
+         size -= len;
+         if (size > sepLen2) {
+            strcpy(buffer+index, separation2);
+            index += sepLen2;
+            size -= sepLen2;
+         }
+      }
+   }
+
+   buffer[index]='\0';
+   return buffer;
+}
+
+int ExtendedEventDescriptors::getMaximumTextPlainLength() {
+   int ret=0;
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+      ret+=d->text.getLength();
+   }
+   return ret;
+}
+
+char *ExtendedEventDescriptors::getTextPlain() {
+   int size = getMaximumTextPlainLength();
+   char *text=new char[size];
+   return getTextPlain(text, size);
+}
+
+char *ExtendedEventDescriptors::getTextPlain(char *buffer, int size) {
+   int index=0, len;
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+      d->text.getText(buffer+index, size);
+      len = strlen(buffer+index);
+      index += len;
+      size -= len;
+   }
+   buffer[index]='\0';
+   return buffer;
+}
+
+int ExtendedEventDescriptors::getMaximumTextItemizedLength(const char *separation1, const char *separation2) {
+   int ret=0;
+   int sepLength=strlen(separation1)+strlen(separation2);
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+      //The length includes two 8-bit length fields which have already been subtracted from sepLength //XXX kls 2004-06-06: what does this mean???
+      ret+=d->itemLoop.getLength()+sepLength;
+   }
+   return ret;
+}
+
+char *ExtendedEventDescriptors::getTextItemized(const char *separation1, const char *separation2) {
+   int size = getMaximumTextItemizedLength(separation1, separation2);
+   char *text=new char[size];
+   return getTextItemized(text, size, separation1, separation2);
+}
+
+char *ExtendedEventDescriptors::getTextItemized(char *buffer, int size, const char *separation1, const char *separation2) {
+   int index=0, len;
+   int sepLen1 = strlen(separation1);
+   int sepLen2 = strlen(separation2);
+   for (int i=0;i<length;i++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i];
+      if (!d)
+         continue;
+
+      ExtendedEventDescriptor::Item item;
+      for (Loop::Iterator it; d->itemLoop.getNext(item, it);   ) {
+         item.itemDescription.getText(buffer+index, size);
+         len = strlen(buffer+index);
+         index += len;
+         size -= len;
+         if (size > sepLen1) {
+            strcpy(buffer+index, separation1);
+            index += sepLen1;
+            size -= sepLen1;
+         }
+
+         item.item.getText(buffer+index, size);
+         len = strlen(buffer+index);
+         index += len;
+         size -= len;
+         if (size > sepLen2) {
+            strcpy(buffer+index, separation2);
+            index += sepLen2;
+            size -= sepLen2;
+         }
+      }
+   }
+   buffer[index]='\0';
+   return buffer;
+}
+
+//returns the itemized text pair by pair. Maximum length for buffers is 256.
+//Return value is false if and only if the end of the list is reached.
+bool ExtendedEventDescriptors::getTextItemized(Loop::Iterator &it, bool &valid, char *itemDescription, char *itemText, int sizeItemDescription, int sizeItemText) {
+   //The iterator has to store two values: The descriptor index (4bit)
+   //and the item loop index (max overall length 256, min item length 16 => max number 128 => 7bit)
+   valid=false;
+
+   int index=(it.i & 0x780) >> 7; // 0x780 == 1111 000 0000
+   it.i &= 0x7F; //0x7F == 111 1111
+
+   for (;index<length;index++) {
+      ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[index];
+      if (!d)
+         continue;
+
+      ExtendedEventDescriptor::Item item;
+      if (d->itemLoop.getNext(item, it)) {
+         item.item.getText(itemDescription, sizeItemDescription);
+         item.itemDescription.getText(itemText, sizeItemText);
+         valid=true;
+         break;
+      } else {
+         it.reset();
+         continue;
+      }
+   }
+
+   it.i &= 0x7F;
+   it.i |= (index & 0xF) << 7; //0xF == 1111
+
+   return index<length;
+}
+
+int TimeShiftedEventDescriptor::getReferenceServiceId() const {
+   return HILO(s->reference_service_id);
+}
+
+int TimeShiftedEventDescriptor::getReferenceEventId() const {
+   return HILO(s->reference_event_id);
+}
+
+void TimeShiftedEventDescriptor::Parse() {
+   s=data.getData<const descr_time_shifted_event>();
+}
+
+void ContentDescriptor::Parse() {
+   //this descriptor is only a header and a loop
+   nibbleLoop.setData(data+sizeof(descr_content), getLength()-sizeof(descr_content));
+}
+
+int ContentDescriptor::Nibble::getContentNibbleLevel1() const {
+   return s->content_nibble_level_1;
+}
+
+int ContentDescriptor::Nibble::getContentNibbleLevel2() const {
+   return s->content_nibble_level_2;
+}
+
+int ContentDescriptor::Nibble::getUserNibble1() const {
+   return s->user_nibble_1;
+}
+
+int ContentDescriptor::Nibble::getUserNibble2() const {
+   return s->user_nibble_2;
+}
+
+void ContentDescriptor::Nibble::Parse() {
+   s=data.getData<const nibble_content>();
+}
+
+void ParentalRatingDescriptor::Parse() {
+   //this descriptor is only a header and a loop
+   ratingLoop.setData(data+sizeof(descr_parental_rating), getLength()-sizeof(descr_parental_rating));
+}
+
+int ParentalRatingDescriptor::Rating::getRating() const {
+   return s->rating;
+}
+
+void ParentalRatingDescriptor::Rating::Parse() {
+   s=data.getData<const parental_rating>();
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+}
+
+void TeletextDescriptor::Parse() {
+   //this descriptor is only a header and a loop
+   teletextLoop.setData(data+sizeof(descr_teletext), getLength()-sizeof(descr_teletext));
+}
+
+void TeletextDescriptor::Teletext::Parse() {
+   s=data.getData<const item_teletext>();
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+}
+
+int TeletextDescriptor::Teletext::getTeletextType() const {
+   return s->type;
+}
+
+int TeletextDescriptor::Teletext::getTeletextMagazineNumber() const {
+   return s->magazine_number;
+}
+
+int TeletextDescriptor::Teletext::getTeletextPageNumber() const {
+   return s->page_number;
+}
+
+int CaDescriptor::getCaType() const {
+   return HILO(s->CA_type);
+}
+
+int CaDescriptor::getCaPid() const {
+   return HILO(s->CA_PID);
+}
+
+void CaDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_ca>(s, offset);
+   if (checkSize(getLength()-offset))
+      privateData.assign(data.getData(offset), getLength()-offset);
+}
+
+int StreamIdentifierDescriptor::getComponentTag() const {
+   return s->component_tag;
+}
+
+void StreamIdentifierDescriptor::Parse() {
+   s=data.getData<const descr_stream_identifier>();
+}
+
+void NetworkNameDescriptor::Parse() {
+   name.setData(data+sizeof(descr_network_name), getLength()-sizeof(descr_network_name));
+}
+
+void CaIdentifierDescriptor::Parse() {
+   identifiers.setData(data+sizeof(descr_ca_identifier), getLength()-sizeof(descr_ca_identifier));
+}
+
+int CarouselIdentifierDescriptor::getCarouselId() const {
+   return (HILO(s->carousel_id_hi) << 16) | HILO(s->carousel_id_lo);
+}
+
+int CarouselIdentifierDescriptor::getFormatId() const {
+   return s->FormatId;
+}
+
+void CarouselIdentifierDescriptor::Parse() {
+   s=data.getData<const descr_carousel_identifier>();
+}
+
+void ServiceListDescriptor::Parse() {
+   serviceLoop.setData(data+sizeof(descr_service_list), getLength()-sizeof(descr_service_list));
+}
+
+int ServiceListDescriptor::Service::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int ServiceListDescriptor::Service::getServiceType() const {
+   return s->service_type;
+}
+
+void ServiceListDescriptor::Service::Parse() {
+   s=data.getData<const descr_service_list_loop>();
+}
+
+int SatelliteDeliverySystemDescriptor::getFrequency() const {
+   return (HILO(s->frequency_hi) << 16) | HILO(s->frequency_lo);
+}
+
+int SatelliteDeliverySystemDescriptor::getOrbitalPosition() const {
+   return HILO(s->orbital_position);
+}
+
+int SatelliteDeliverySystemDescriptor::getWestEastFlag() const {
+   return s->west_east_flag;
+}
+
+int SatelliteDeliverySystemDescriptor::getPolarization() const {
+   return s->polarization;
+}
+
+int SatelliteDeliverySystemDescriptor::getModulation() const {
+   return s->modulation;
+}
+
+int SatelliteDeliverySystemDescriptor::getSymbolRate() const {
+   return (HILO(s->symbol_rate_hi) << 12) | (s->symbol_rate_lo_1 << 4) | s->symbol_rate_lo_2;
+}
+
+int SatelliteDeliverySystemDescriptor::getFecInner() const {
+   return s->fec_inner;
+}
+
+void SatelliteDeliverySystemDescriptor::Parse() {
+   s=data.getData<const descr_satellite_delivery_system>();
+}
+
+int CableDeliverySystemDescriptor::getFrequency() const {
+   return (HILO(s->frequency_hi) << 16) | HILO(s->frequency_lo);
+}
+
+int CableDeliverySystemDescriptor::getFecOuter() const {
+   return s->fec_outer;
+}
+
+int CableDeliverySystemDescriptor::getModulation() const {
+   return s->modulation;
+}
+
+int CableDeliverySystemDescriptor::getSymbolRate() const {
+   return (HILO(s->symbol_rate_hi) << 12) | (s->symbol_rate_lo_1 << 4) | s->symbol_rate_lo_2;
+}
+
+int CableDeliverySystemDescriptor::getFecInner() const {
+   return s->fec_inner;
+}
+
+void CableDeliverySystemDescriptor::Parse() {
+   s=data.getData<const descr_cable_delivery_system>();
+}
+
+int TerrestrialDeliverySystemDescriptor::getFrequency() const {
+   return (HILO(s->frequency_hi) << 16) | HILO(s->frequency_lo);
+}
+
+int TerrestrialDeliverySystemDescriptor::getBandwidth() const {
+   return s->bandwidth;
+}
+
+int TerrestrialDeliverySystemDescriptor::getConstellation() const {
+   return s->constellation;
+}
+
+int TerrestrialDeliverySystemDescriptor::getHierarchy() const {
+   return s->hierarchy;
+}
+
+int TerrestrialDeliverySystemDescriptor::getCodeRateHP() const {
+   return s->code_rate_HP;
+}
+
+int TerrestrialDeliverySystemDescriptor::getCodeRateLP() const {
+   return s->code_rate_LP;
+}
+
+int TerrestrialDeliverySystemDescriptor::getGuardInterval() const {
+   return s->guard_interval;
+}
+
+int TerrestrialDeliverySystemDescriptor::getTransmissionMode() const {
+   return s->transmission_mode;
+}
+
+bool TerrestrialDeliverySystemDescriptor::getOtherFrequency() const {
+   return s->other_frequency_flag;
+}
+
+void TerrestrialDeliverySystemDescriptor::Parse() {
+   s=data.getData<const descr_terrestrial_delivery>();
+}
+
+int ServiceDescriptor::getServiceType() const {
+   return s->service_type;
+}
+
+void ServiceDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_service>(s, offset);
+   providerName.setDataAndOffset(data+offset, s->provider_name_length, offset);
+   const descr_service_mid *mid;
+   data.setPointerAndOffset<const descr_service_mid>(mid, offset);
+   serviceName.setData(data+offset, mid->service_name_length);
+}
+
+void NVODReferenceDescriptor::Parse() {
+   serviceLoop.setData(data+sizeof(descr_nvod_reference), getLength()-sizeof(descr_nvod_reference));
+}
+
+int NVODReferenceDescriptor::Service::getTransportStream() const {
+   return HILO(s->transport_stream_id);
+}
+
+int NVODReferenceDescriptor::Service::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int NVODReferenceDescriptor::Service::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+void NVODReferenceDescriptor::Service::Parse() {
+   s=data.getData<const item_nvod_reference>();
+}
+
+int TimeShiftedServiceDescriptor::getReferenceServiceId() const {
+   return HILO(s->reference_service_id);
+}
+
+void TimeShiftedServiceDescriptor::Parse() {
+   s=data.getData<const descr_time_shifted_service>();
+}
+
+int ComponentDescriptor::getStreamContent() const {
+   return s->stream_content;
+}
+
+int ComponentDescriptor::getComponentType() const {
+   return s->component_type;
+}
+
+int ComponentDescriptor::getComponentTag() const {
+   return s->component_tag;
+}
+
+void ComponentDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_component>(s, offset);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+   description.setData(data+offset, getLength()-offset);
+}
+
+void PrivateDataSpecifierDescriptor::Parse() {
+   s=data.getData<const descr_private_data_specifier>();
+}
+
+int PrivateDataSpecifierDescriptor::getPrivateDataSpecifier() const {
+   return (HILO(s->private_data_specifier_hi) << 16) | HILO(s->private_data_specifier_lo);
+}
+
+void SubtitlingDescriptor::Parse() {
+   subtitlingLoop.setData(data+sizeof(descr_subtitling), getLength()-sizeof(descr_subtitling));
+}
+
+int SubtitlingDescriptor::Subtitling::getSubtitlingType() const {
+   return s->subtitling_type;
+}
+
+int SubtitlingDescriptor::Subtitling::getCompositionPageId() const {
+   return HILO(s->composition_page_id);
+}
+
+int SubtitlingDescriptor::Subtitling::getAncillaryPageId() const {
+   return HILO(s->ancillary_page_id);
+}
+
+void SubtitlingDescriptor::Subtitling::Parse() {
+   s=data.getData<const item_subtitling>();
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+}
+
+int ServiceMoveDescriptor::getNewOriginalNetworkId() const {
+   return HILO(s->new_original_network_id);
+}
+
+int ServiceMoveDescriptor::getNewTransportStreamId() const {
+   return HILO(s->new_transport_stream_id);
+}
+
+int ServiceMoveDescriptor::getNewServiceId() const {
+   return HILO(s->new_service_id);
+}
+
+void ServiceMoveDescriptor::Parse() {
+   s=data.getData<const descr_service_move>();
+}
+
+int FrequencyListDescriptor::getCodingType() const {
+   return s->coding_type;
+}
+
+void FrequencyListDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_frequency_list>(s, offset);
+   frequencies.setData(data+offset, getLength()-offset);
+}
+
+void ServiceIdentifierDescriptor::Parse() {
+   textualServiceIdentifier.setData(data+sizeof(descr_service_identifier), getLength()-sizeof(descr_service_identifier));
+}
+
+void MultilingualNameDescriptor::Parse() {
+   nameLoop.setData(data+sizeof(descr_multilingual_network_name), getLength()-sizeof(descr_multilingual_network_name));
+}
+
+void MultilingualNameDescriptor::Name::Parse() {
+   int offset=0;
+   const entry_multilingual_name *s;
+   data.setPointerAndOffset<const entry_multilingual_name>(s, offset);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+   name.setData(data+offset, s->text_length);
+}
+
+int MultilingualComponentDescriptor::getComponentTag() const {
+   return s->component_tag;
+}
+
+void MultilingualComponentDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_multilingual_component>(s, offset);
+   nameLoop.setData(data+sizeof(descr_multilingual_component), getLength()-sizeof(descr_multilingual_component));
+}
+
+void MultilingualServiceNameDescriptor::Parse() {
+   nameLoop.setData(data+sizeof(descr_multilingual_network_name), getLength()-sizeof(descr_multilingual_network_name));
+}
+
+void MultilingualServiceNameDescriptor::Name::Parse() {
+   int offset=0;
+   const entry_multilingual_name *s;
+   data.setPointerAndOffset<const entry_multilingual_name>(s, offset);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+   providerName.setDataAndOffset(data+offset, s->text_length, offset);
+   const entry_multilingual_service_name_mid *mid;
+   data.setPointerAndOffset<const entry_multilingual_service_name_mid>(mid, offset);
+   name.setData(data+offset, mid->service_name_length);
+}
+
+void LocalTimeOffsetDescriptor::Parse() {
+   localTimeOffsetLoop.setData(data+sizeof(descr_local_time_offset), getLength()-sizeof(descr_local_time_offset));
+}
+
+int LocalTimeOffsetDescriptor::LocalTimeOffset::getCountryId() const {
+   return s->country_region_id;
+}
+
+int LocalTimeOffsetDescriptor::LocalTimeOffset::getLocalTimeOffsetPolarity() const {
+   return s->local_time_offset_polarity;
+}
+
+int LocalTimeOffsetDescriptor::LocalTimeOffset::getLocalTimeOffset() const {
+   return (s->local_time_offset_h << 8) | s->local_time_offset_m;
+}
+
+time_t LocalTimeOffsetDescriptor::LocalTimeOffset::getTimeOfChange() const {
+   return DVBTime::getTime(s->time_of_change_mjd_hi, s->time_of_change_mjd_lo, s->time_of_change_time_h, s->time_of_change_time_m, s->time_of_change_time_s);
+}
+
+int LocalTimeOffsetDescriptor::LocalTimeOffset::getNextTimeOffset() const {
+   return (s->next_time_offset_h << 8) | s->next_time_offset_m;
+}
+
+void LocalTimeOffsetDescriptor::LocalTimeOffset::Parse() {
+   s=data.getData<const local_time_offset_entry>();
+   countryCode[0]=s->country_code1;
+   countryCode[1]=s->country_code2;
+   countryCode[2]=s->country_code3;
+   countryCode[3]=0;
+}
+
+void LinkageDescriptor::Parse() {
+   int offset=0;
+   s1 = NULL;
+   data.setPointerAndOffset<const descr_linkage>(s, offset);
+   if (checkSize(getLength()-offset)) {
+      if (getLinkageType() == LinkageTypeMobileHandover)
+         data.setPointerAndOffset<const descr_linkage_8>(s1, offset);
+      privateData.assign(data.getData(offset), getLength()-offset);
+      }
+}
+
+int LinkageDescriptor::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int LinkageDescriptor::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int LinkageDescriptor::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+LinkageType LinkageDescriptor::getLinkageType() const {
+   return (LinkageType)s->linkage_type;
+}
+
+int LinkageDescriptor::getHandOverType() const {
+   return s1 == NULL ? 0 : s1->hand_over_type;
+}
+
+int LinkageDescriptor::getOriginType() const {
+   return s1 == NULL ? 0 : s1->origin_type;
+}
+
+int LinkageDescriptor::getId() const {
+   return s1 == NULL ? 0 : HILO(s1->id);
+}
+
+void ISO639LanguageDescriptor::Parse() {
+   languageLoop.setData(data+sizeof(descr_iso_639_language), getLength()-sizeof(descr_iso_639_language));
+
+   //all this is for backwards compatibility only
+   Loop::Iterator it;
+   Language first;
+   if (languageLoop.getNext(first, it)) {
+      languageCode[0]=first.languageCode[0];
+      languageCode[1]=first.languageCode[1];
+      languageCode[2]=first.languageCode[2];
+      languageCode[3]=0;
+   } else
+      languageCode[0]=0;
+}
+
+void ISO639LanguageDescriptor::Language::Parse() {
+   s=data.getData<const descr_iso_639_language_loop>();
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+}
+
+AudioType ISO639LanguageDescriptor::Language::getAudioType() {
+   return (AudioType)s->audio_type;
+}
+
+void PDCDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_pdc>(s, offset);
+}
+
+int PDCDescriptor::getDay() const {
+   return ((s->pil0 & 0x0F) << 1) | ((s->pil1 & 0x80) >> 7);
+}
+
+int PDCDescriptor::getMonth() const {
+   return (s->pil1 >> 3) & 0x0F;
+}
+
+int PDCDescriptor::getHour() const {
+   return ((s->pil1 & 0x07) << 2) | ((s->pil2 & 0xC0) >> 6);
+}
+
+int PDCDescriptor::getMinute() const {
+   return s->pil2 & 0x3F;
+}
+
+void AncillaryDataDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_ancillary_data>(s, offset);
+}
+
+int AncillaryDataDescriptor::getAncillaryDataIdentifier() const {
+   return s->ancillary_data_identifier;
+}
+
+int PremiereContentTransmissionDescriptor::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int PremiereContentTransmissionDescriptor::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int PremiereContentTransmissionDescriptor::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+void PremiereContentTransmissionDescriptor::Parse() {
+   s=data.getData<const descr_premiere_content_transmission>();
+   startDayLoop.setData(data+sizeof(descr_premiere_content_transmission), getLength()-sizeof(descr_premiere_content_transmission));
+}
+
+int PremiereContentTransmissionDescriptor::StartDayEntry::getMJD() const {
+   return HILO(s->mjd);
+}
+
+int PremiereContentTransmissionDescriptor::StartDayEntry::getLoopLength() const {
+   return s->start_time_loop;
+}
+
+int PremiereContentTransmissionDescriptor::StartDayEntry::getLength() {
+   return sizeof(item_premiere_content_transmission_day)+getLoopLength();
+}
+
+void PremiereContentTransmissionDescriptor::StartDayEntry::Parse() {
+   s=data.getData<const item_premiere_content_transmission_day>();
+   startTimeLoop.setData(data+sizeof(item_premiere_content_transmission_day), getLoopLength());
+}
+
+time_t PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry::getStartTime(int mjd) const {
+   return DVBTime::getTime(mjd >> 8, mjd & 0xff, s->start_time_h, s->start_time_m, s->start_time_s);
+}
+
+void PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry::Parse() {
+   s=data.getData<const item_premiere_content_transmission_time>();
+}
+
+void ApplicationSignallingDescriptor::Parse() {
+   entryLoop.setData(data+sizeof(descr_application_signalling), getLength()-sizeof(descr_application_signalling));
+}
+
+int ApplicationSignallingDescriptor::ApplicationEntryDescriptor::getApplicationType() const {
+   return HILO(s->application_type);
+}
+
+int ApplicationSignallingDescriptor::ApplicationEntryDescriptor::getAITVersionNumber() const {
+   return s->AIT_version_number;
+}
+
+void ApplicationSignallingDescriptor::ApplicationEntryDescriptor::Parse() {
+   s=data.getData<const application_signalling_entry>();
+}
+
+bool MHP_ApplicationDescriptor::isServiceBound() const {
+   return s->service_bound_flag;
+}
+
+int MHP_ApplicationDescriptor::getVisibility() const {
+   return s->visibility;
+}
+
+int MHP_ApplicationDescriptor::getApplicationPriority() const {
+   return s->application_priority;
+}
+
+void MHP_ApplicationDescriptor::Parse() {
+   int offset=0;
+   const descr_application *dapp;
+   data.setPointerAndOffset<const descr_application>(dapp, offset);
+   profileLoop.setDataAndOffset(data+offset, dapp->application_profiles_length, offset);
+   data.setPointerAndOffset<const descr_application_end>(s, offset);
+   transportProtocolLabels.setData(data+offset, getLength()-offset);
+}
+
+int MHP_ApplicationDescriptor::Profile::getApplicationProfile() const {
+      return HILO(s->application_profile);
+}
+
+int MHP_ApplicationDescriptor::Profile::getVersionMajor() const {
+      return s->version_major;
+}
+
+int MHP_ApplicationDescriptor::Profile::getVersionMinor() const {
+      return s->version_minor;
+}
+
+int MHP_ApplicationDescriptor::Profile::getVersionMicro() const {
+      return s->version_micro;
+}
+
+void MHP_ApplicationDescriptor::Profile::Parse() {
+   s=data.getData<application_profile_entry>();
+}
+
+void MHP_ApplicationNameDescriptor::Parse() {
+   nameLoop.setData(data+sizeof(descr_application_name), getLength()-sizeof(descr_application_name));
+}
+
+void MHP_ApplicationNameDescriptor::NameEntry::Parse() {
+   const descr_application_name_entry *s;
+   s=data.getData<const descr_application_name_entry>();
+   name.setData(data+sizeof(descr_application_name_entry), s->application_name_length);
+   languageCode[0]=s->lang_code1;
+   languageCode[1]=s->lang_code2;
+   languageCode[2]=s->lang_code3;
+   languageCode[3]=0;
+}
+
+int MHP_TransportProtocolDescriptor::getProtocolId() const {
+   return HILO(s->protocol_id);
+}
+
+int MHP_TransportProtocolDescriptor::getProtocolLabel() const {
+   return s->transport_protocol_label;
+}
+
+bool MHP_TransportProtocolDescriptor::isRemote() const {
+   return remote;
+}
+
+int MHP_TransportProtocolDescriptor::getComponentTag() const {
+   return componentTag;
+}
+
+void MHP_TransportProtocolDescriptor::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const descr_transport_protocol>(s, offset);
+   if (getProtocolId() == ObjectCarousel) {
+      const transport_via_oc *oc;
+      data.setPointerAndOffset<const transport_via_oc>(oc, offset);
+      remote=oc->remote;
+      if (remote) {
+         const transport_via_oc_remote_end *rem;
+         data.setPointerAndOffset<const transport_via_oc_remote_end>(rem, offset);
+         componentTag=rem->component_tag;
+      } else {
+         const transport_via_oc_end *rem;
+         data.setPointerAndOffset<const transport_via_oc_end>(rem, offset);
+         componentTag=rem->component_tag;
+      }
+   } else { //unimplemented
+      remote=false;
+      componentTag=-1;
+   }
+}
+
+void MHP_DVBJApplicationDescriptor::Parse() {
+   applicationLoop.setData(data+sizeof(descr_dvbj_application), getLength()-sizeof(descr_dvbj_application));
+}
+
+void MHP_DVBJApplicationDescriptor::ApplicationEntry::Parse() {
+   const descr_dvbj_application_entry *entry=data.getData<const descr_dvbj_application_entry>();
+   parameter.setData(data+sizeof(descr_dvbj_application_entry), entry->parameter_length);
+}
+
+void MHP_DVBJApplicationLocationDescriptor::Parse() {
+   int offset=0;
+   const descr_dvbj_application_location *first;
+   data.setPointerAndOffset<const descr_dvbj_application_location>(first, offset);
+   baseDirectory.setDataAndOffset(data+offset, first->base_directory_length, offset);
+   const descr_dvbj_application_location_mid *mid;
+   data.setPointerAndOffset<const descr_dvbj_application_location_mid>(mid, offset);
+   classPath.setDataAndOffset(data+offset, mid->classpath_extension_length, offset);
+   initialClass.setData(data+offset, getLength()-offset);
+}
+
+int MHP_ApplicationIconsDescriptor::getIconFlags() const {
+   return HILO(s->icon_flags);
+}
+
+void MHP_ApplicationIconsDescriptor::Parse() {
+   int offset=0;
+   const descr_application_icons_descriptor *first;
+   data.setPointerAndOffset<const descr_application_icons_descriptor>(first, offset);
+   iconLocator.setDataAndOffset(data+offset, first->icon_locator_length, offset);
+   data.setPointerAndOffset<const descr_application_icons_descriptor_end>(s, offset);
+}
+
+} //end of namespace
diff --git a/contrib/sasc-ng/sc/libsi/section.c b/contrib/sasc-ng/sc/libsi/section.c
new file mode 100644 (file)
index 0000000..6dd43df
--- /dev/null
@@ -0,0 +1,360 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: section.c 1.5 2006/04/14 10:53:44 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#include "section.h"
+#include <stdio.h>
+
+namespace SI {
+
+/*********************** PAT ***********************/
+
+void PAT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const pat>(s, offset);
+   associationLoop.setData(data+offset, getLength()-offset-4);
+}
+
+int PAT::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int PAT::Association::getServiceId() const {
+   return HILO(s->program_number);
+}
+
+int PAT::Association::getPid() const {
+   return HILO(s->network_pid);
+}
+
+void PAT::Association::Parse() {
+   s=data.getData<pat_prog>();
+}
+
+/*********************** CAT ***********************/
+
+void CAT::Parse() {
+   loop.setData(data+sizeof(cat), getLength()-sizeof(cat)-4);
+}
+
+/*********************** PMT ***********************/
+
+void PMT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const pmt>(s, offset);
+   commonDescriptors.setDataAndOffset(data+offset, HILO(s->program_info_length), offset);
+   streamLoop.setData(data+offset, getLength()-offset-4);
+}
+
+int PMT::getServiceId() const {
+   return HILO(s->program_number);
+}
+
+int PMT::getPCRPid() const {
+   return HILO(s->PCR_PID);
+}
+
+int PMT::Stream::getPid() const {
+   return HILO(s->elementary_PID);
+}
+
+int PMT::Stream::getStreamType() const {
+   return s->stream_type;
+}
+
+void PMT::Stream::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const pmt_info>(s, offset);
+   streamDescriptors.setData(data+offset, HILO(s->ES_info_length));
+}
+
+/*********************** TSDT ***********************/
+
+void TSDT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const tsdt>(s, offset);
+   transportStreamDescriptors.setDataAndOffset(data+offset, getLength()-offset-4, offset);
+}
+
+/*********************** NIT ***********************/
+
+int NIT::getNetworkId() const {
+   return HILO(s->network_id);
+}
+
+void NIT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const nit>(s, offset);
+   commonDescriptors.setDataAndOffset(data+offset, HILO(s->network_descriptor_length), offset);
+   const nit_mid *mid;
+   data.setPointerAndOffset<const nit_mid>(mid, offset);
+   transportStreamLoop.setData(data+offset, HILO(mid->transport_stream_loop_length));
+}
+
+int NIT::TransportStream::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int NIT::TransportStream::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+void NIT::TransportStream::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const ni_ts>(s, offset);
+   transportStreamDescriptors.setData(data+offset, HILO(s->transport_descriptors_length));
+}
+
+/*********************** SDT ***********************/
+
+void SDT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const sdt>(s, offset);
+   serviceLoop.setData(data+offset, getLength()-offset-4); //4 is for CRC
+}
+
+int SDT::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int SDT::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int SDT::Service::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int SDT::Service::getEITscheduleFlag() const {
+   return s->eit_schedule_flag;
+}
+
+int SDT::Service::getEITpresentFollowingFlag() const {
+   return s->eit_present_following_flag;
+}
+
+RunningStatus SDT::Service::getRunningStatus() const {
+   return (RunningStatus)s->running_status;
+}
+
+int SDT::Service::getFreeCaMode() const {
+   return s->free_ca_mode;
+}
+
+void SDT::Service::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const sdt_descr>(s, offset);
+   serviceDescriptors.setData(data+offset, HILO(s->descriptors_loop_length));
+}
+
+/*********************** EIT ***********************/
+
+int EIT::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int EIT::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int EIT::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int EIT::getSegmentLastSectionNumber() const {
+   return s->segment_last_section_number;
+}
+
+int EIT::getLastTableId() const {
+   return s->last_table_id;
+}
+
+bool EIT::isPresentFollowing() const {
+   return getTableId() == TableIdEIT_presentFollowing || getTableId() == TableIdEIT_presentFollowing_other;
+}
+
+bool EIT::isActualTS() const {
+   return
+       (getTableId() ==TableIdEIT_presentFollowing)
+    || (TableIdEIT_schedule_first <= getTableId() && getTableId() <= TableIdEIT_schedule_last);
+}
+
+void EIT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const eit>(s, offset);
+   //printf("%d %d %d %d %d\n", getServiceId(), getTransportStreamId(), getOriginalNetworkId(), isPresentFollowing(), isActualTS());
+   eventLoop.setData(data+offset, getLength()-offset-4); //4 is for CRC
+}
+
+time_t EIT::Event::getStartTime() const {
+   return DVBTime::getTime(s->mjd_hi, s->mjd_lo, s->start_time_h, s->start_time_m, s->start_time_s);
+}
+
+time_t EIT::Event::getDuration() const {
+   return DVBTime::getDuration(s->duration_h, s->duration_m, s->duration_s);
+}
+
+int EIT::Event::getEventId() const {
+   return HILO(s->event_id);
+}
+
+int EIT::Event::getMJD() const {
+   return HILO(s->mjd);
+}
+
+int EIT::Event::getStartTimeHour() const {
+   return DVBTime::bcdToDec(s->start_time_h);
+}
+
+int EIT::Event::getStartTimeMinute() const {
+   return DVBTime::bcdToDec(s->start_time_m);
+}
+
+int EIT::Event::getStartTimeSecond() const {
+   return DVBTime::bcdToDec(s->start_time_s);
+}
+
+int EIT::Event::getDurationHour() const {
+   return DVBTime::bcdToDec(s->duration_h);
+}
+
+int EIT::Event::getDurationMinute() const {
+   return DVBTime::bcdToDec(s->duration_m);
+}
+
+int EIT::Event::getDurationSecond() const {
+   return DVBTime::bcdToDec(s->duration_s);
+}
+
+RunningStatus EIT::Event::getRunningStatus() const {
+   return (RunningStatus)s->running_status;
+}
+
+int EIT::Event::getFreeCaMode() const {
+   return s->free_ca_mode;
+}
+
+void EIT::Event::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const eit_event>(s, offset);
+   //printf("%d %d %d\n", getStartTime(), getDuration(), getRunningStatus());
+   eventDescriptors.setData(data+offset, HILO(s->descriptors_loop_length));
+}
+
+/*********************** TDT ***********************/
+
+time_t TDT::getTime() const {
+   return DVBTime::getTime(s->utc_mjd_hi, s->utc_mjd_lo, s->utc_time_h, s->utc_time_m, s->utc_time_s);
+}
+
+void TDT::Parse() {
+   s=data.getData<const tdt>();
+}
+
+/*********************** TOT ***********************/
+
+time_t TOT::getTime() const {
+   return DVBTime::getTime(s->utc_mjd_hi, s->utc_mjd_lo, s->utc_time_h, s->utc_time_m, s->utc_time_s);
+}
+
+void TOT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const tot>(s, offset);
+   descriptorLoop.setData(data+offset, getLength()-offset-4);
+}
+
+/*********************** RST ***********************/
+
+void RST::Parse() {
+   int offset=0;
+   const rst *s;
+   data.setPointerAndOffset<const rst>(s, offset);
+   infoLoop.setData(data+offset, getLength()-offset);
+}
+
+int RST::RunningInfo::getTransportStreamId() const {
+   return HILO(s->transport_stream_id);
+}
+
+int RST::RunningInfo::getOriginalNetworkId() const {
+   return HILO(s->original_network_id);
+}
+
+int RST::RunningInfo::getServiceId() const {
+   return HILO(s->service_id);
+}
+
+int RST::RunningInfo::getEventId() const {
+   return HILO(s->event_id);
+}
+
+RunningStatus RST::RunningInfo::getRunningStatus() const {
+   return (RunningStatus)s->running_status;
+}
+
+void RST::RunningInfo::Parse() {
+   s=data.getData<const rst_info>();
+}
+
+/*********************** AIT ***********************/
+
+int AIT::getApplicationType() const {
+   return HILO(first->application_type);
+}
+
+int AIT::getAITVersion() const {
+   return first->version_number;
+}
+
+void AIT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const ait>(first, offset);
+   commonDescriptors.setDataAndOffset(data+offset, HILO(first->common_descriptors_length), offset);
+   const ait_mid *mid;
+   data.setPointerAndOffset<const ait_mid>(mid, offset);
+   applicationLoop.setData(data+offset, HILO(mid->application_loop_length));
+}
+
+long AIT::Application::getOrganisationId() const {
+   return data.FourBytes(0);
+}
+
+int AIT::Application::getApplicationId() const {
+   return HILO(s->application_id);
+}
+
+int AIT::Application::getControlCode() const {
+   return s->application_control_code;
+}
+
+void AIT::Application::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const ait_app>(s, offset);
+   applicationDescriptors.setData(data+offset, HILO(s->application_descriptors_length));
+}
+
+/******************* PremiereCIT *******************/
+
+void PremiereCIT::Parse() {
+   int offset=0;
+   data.setPointerAndOffset<const pcit>(s, offset);
+   eventDescriptors.setData(data+offset, HILO(s->descriptors_loop_length));
+}
+
+int PremiereCIT::getContentId() const {
+   return (HILO(s->contentId_hi) << 16) | HILO(s->contentId_lo);
+}
+
+time_t PremiereCIT::getDuration() const {
+   return DVBTime::getDuration(s->duration_h, s->duration_m, s->duration_s);
+}
+
+} //end of namespace
diff --git a/contrib/sasc-ng/sc/libsi/si.c b/contrib/sasc-ng/sc/libsi/si.c
new file mode 100644 (file)
index 0000000..c7f23e8
--- /dev/null
@@ -0,0 +1,537 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg                              *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: si.c 1.16 2006/04/14 10:53:44 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#include <string.h>
+#include "si.h"
+#include "descriptor.h"
+
+namespace SI {
+
+Object::Object() {
+}
+
+Object::Object(CharArray &d) : data(d) {
+}
+
+void Object::setData(const unsigned char*d, int size, bool doCopy) {
+   data.assign(d, size, doCopy);
+}
+
+void Object::setData(CharArray &d) {
+   data=d;
+}
+
+bool Object::checkSize(int offset) {
+   return data.checkSize(offset);
+}
+
+Section::Section(const unsigned char *data, bool doCopy) {
+   setData(data, getLength(data), doCopy);
+}
+
+TableId Section::getTableId() const {
+   return getTableId(data.getData());
+}
+
+int Section::getLength() {
+   return getLength(data.getData());
+}
+
+TableId Section::getTableId(const unsigned char *d) {
+   return (TableId)((const SectionHeader *)d)->table_id;
+}
+
+int Section::getLength(const unsigned char *d) {
+   return HILO(((const SectionHeader *)d)->section_length)+sizeof(SectionHeader);
+}
+
+bool CRCSection::isCRCValid() {
+   return CRC32::isValid((const char *)data.getData(), getLength()/*, data.FourBytes(getLength()-4)*/);
+}
+
+bool CRCSection::CheckCRCAndParse() {
+   if (!isCRCValid())
+      return false;
+   CheckParse();
+   return isValid();
+}
+
+int NumberedSection::getTableIdExtension() const {
+   return getTableIdExtension(data.getData());
+}
+
+int NumberedSection::getTableIdExtension(const unsigned char *d) {
+   return HILO(((const ExtendedSectionHeader *)d)->table_id_extension);
+}
+
+bool NumberedSection::getCurrentNextIndicator() const {
+   return data.getData<ExtendedSectionHeader>()->current_next_indicator;
+}
+
+int NumberedSection::getVersionNumber() const {
+   return data.getData<ExtendedSectionHeader>()->version_number;
+}
+
+int NumberedSection::getSectionNumber() const {
+   return data.getData<ExtendedSectionHeader>()->section_number;
+}
+
+int NumberedSection::getLastSectionNumber() const {
+   return data.getData<ExtendedSectionHeader>()->last_section_number;
+}
+
+int Descriptor::getLength() {
+   return getLength(data.getData());
+}
+
+DescriptorTag Descriptor::getDescriptorTag() const {
+   return getDescriptorTag(data.getData());
+}
+
+int Descriptor::getLength(const unsigned char *d) {
+   return ((const DescriptorHeader*)d)->descriptor_length+sizeof(DescriptorHeader);
+}
+
+DescriptorTag Descriptor::getDescriptorTag(const unsigned char *d) {
+   return (DescriptorTag)((const DescriptorHeader*)d)->descriptor_tag;
+}
+
+Descriptor *DescriptorLoop::getNext(Iterator &it) {
+   if (isValid() && it.i<getLength()) {
+      return createDescriptor(it.i, true);
+   }
+   return 0;
+}
+
+Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag tag, bool returnUnimplemetedDescriptor) {
+   Descriptor *d=0;
+   int len;
+   if (isValid() && it.i<(len=getLength())) {
+      const unsigned char *p=data.getData(it.i);
+      const unsigned char *end=p+len-it.i;
+      while (p < end) {
+         if (Descriptor::getDescriptorTag(p) == tag) {
+            d=createDescriptor(it.i, returnUnimplemetedDescriptor);
+            if (d)
+               break;
+         }
+         it.i+=Descriptor::getLength(p);
+         p+=Descriptor::getLength(p);
+      }
+   }
+   return d;
+}
+
+Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag *tags, int arrayLength, bool returnUnimplementedDescriptor) {
+   Descriptor *d=0;
+   int len;
+   if (isValid() && it.i<(len=getLength())) {
+      const unsigned char *p=data.getData(it.i);
+      const unsigned char *end=p+len-it.i;
+      while (p < end) {
+         for (int u=0; u<arrayLength;u++)
+            if (Descriptor::getDescriptorTag(p) == tags[u]) {
+               d=createDescriptor(it.i, returnUnimplementedDescriptor);
+               break;
+            }
+         if (d)
+            break; //length is added to it.i by createDescriptor, break here
+         it.i+=Descriptor::getLength(p);
+         p+=Descriptor::getLength(p);
+      }
+   }
+   return d;
+}
+
+Descriptor *DescriptorLoop::createDescriptor(int &i, bool returnUnimplemetedDescriptor) {
+   if (!checkSize(Descriptor::getLength(data.getData(i))))
+      return 0;
+   Descriptor *d=Descriptor::getDescriptor(data+i, domain, returnUnimplemetedDescriptor);
+   if (!d)
+      return 0;
+   i+=d->getLength();
+   d->CheckParse();
+   return d;
+}
+
+int DescriptorLoop::getNumberOfDescriptors() {
+   const unsigned char *p=data.getData();
+   const unsigned char *end=p+getLength();
+   int count=0;
+   while (p < end) {
+      count++;
+      p+=Descriptor::getLength(p);
+   }
+   return count;
+}
+
+DescriptorGroup::DescriptorGroup(bool del) {
+   array=0;
+   length=0;
+   deleteOnDesctruction=del;
+}
+
+DescriptorGroup::~DescriptorGroup() {
+   if (deleteOnDesctruction)
+      Delete();
+   delete[] array;
+}
+
+void DescriptorGroup::Delete() {
+   for (int i=0;i<length;i++)
+      if (array[i]!=0) {
+         delete array[i];
+         array[i]=0;
+      }
+}
+
+void DescriptorGroup::Add(GroupDescriptor *d) {
+   if (!array) {
+      length=d->getLastDescriptorNumber()+1;
+      array=new GroupDescriptor*[length]; //numbering is zero-based
+      for (int i=0;i<length;i++)
+         array[i]=0;
+   } else if (length != d->getLastDescriptorNumber()+1)
+      return; //avoid crash in case of misuse
+   array[d->getDescriptorNumber()]=d;
+}
+
+bool DescriptorGroup::isComplete() {
+   for (int i=0;i<length;i++)
+      if (array[i]==0)
+         return false;
+   return true;
+}
+
+char *String::getText() {
+   int len=getLength();
+   if (len < 0 || len > 4095)
+      return strdup("text error"); // caller will delete it!
+   char *data=new char(len+1);
+   decodeText(data, len+1);
+   return data;
+}
+
+char *String::getText(char *buffer, int size) {
+   int len=getLength();
+   if (len < 0 || len >= size) {
+      strncpy(buffer, "text error", size);
+      buffer[size-1] = 0;
+      return buffer;
+   }
+   decodeText(buffer, size);
+   return buffer;
+}
+
+//taken from VDR, Copyright Klaus Schmidinger <kls@cadsoft.de>
+char *String::getText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion) {
+   int len=getLength();
+   if (len < 0 || len >= sizeBuffer) {
+      strncpy(buffer, "text error", sizeBuffer);
+      buffer[sizeBuffer-1] = 0;
+      *shortVersion = 0;
+      return buffer;
+   }
+   decodeText(buffer, shortVersion, sizeBuffer, sizeShortVersion);
+   return buffer;
+}
+
+//taken from libdtv, Copyright Rolf Hakenes <hakenes@hippomi.de>
+void String::decodeText(char *buffer, int size) {
+   const unsigned char *from=data.getData(0);
+   char *to=buffer;
+
+   /* Disable detection of coding tables - libdtv doesn't do it either
+   if ( (0x01 <= *from) && (*from <= 0x1f) ) {
+      codeTable=*from
+   }
+   */
+
+   if (*from == 0x10)
+      from += 3; // skips code table info
+
+   int len=getLength();
+   for (int i = 0; i < len; i++) {
+      if (*from == 0)
+         break;
+      if (    ((' ' <= *from) && (*from <= '~'))
+           || (*from == '\n')
+           || (0xA0 <= *from)
+           || (*from == 0x86 || *from == 0x87)
+         )
+         *to++ = *from;
+      else if (*from == 0x8A)
+         *to++ = '\n';
+      from++;
+      if (to - buffer >= size - 1)
+         break;
+   }
+   *to = '\0';
+}
+
+void String::decodeText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion) {
+   const unsigned char *from=data.getData(0);
+   char *to=buffer;
+   char *toShort=shortVersion;
+   int IsShortName=0;
+
+   if (*from == 0x10)
+      from += 3; // skips code table info
+
+   int len=getLength();
+   for (int i = 0; i < len; i++) {
+      if (    ((' ' <= *from) && (*from <= '~'))
+           || (*from == '\n')
+           || (0xA0 <= *from)
+         )
+      {
+         *to++ = *from;
+         if (IsShortName)
+            *toShort++ = *from;
+      }
+      else if (*from == 0x8A)
+         *to++ = '\n';
+      else if (*from == 0x86)
+         IsShortName++;
+      else if (*from == 0x87)
+         IsShortName--;
+      else if (*from == 0)
+         break;
+      from++;
+      if (to - buffer >= sizeBuffer - 1 || toShort - shortVersion >= sizeShortVersion - 1)
+         break;
+   }
+   *to = '\0';
+   *toShort = '\0';
+}
+
+Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain, bool returnUnimplemetedDescriptor) {
+   Descriptor *d=0;
+   switch (domain) {
+   case SI:
+      switch ((DescriptorTag)da.getData<DescriptorHeader>()->descriptor_tag) {
+         case CaDescriptorTag:
+            d=new CaDescriptor();
+            break;
+         case CarouselIdentifierDescriptorTag:
+            d=new CarouselIdentifierDescriptor();
+            break;
+         case NetworkNameDescriptorTag:
+            d=new NetworkNameDescriptor();
+            break;
+         case ServiceListDescriptorTag:
+            d=new ServiceListDescriptor();
+            break;
+         case SatelliteDeliverySystemDescriptorTag:
+            d=new SatelliteDeliverySystemDescriptor();
+            break;
+         case CableDeliverySystemDescriptorTag:
+            d=new CableDeliverySystemDescriptor();
+            break;
+         case TerrestrialDeliverySystemDescriptorTag:
+            d=new TerrestrialDeliverySystemDescriptor();
+            break;
+         case BouquetNameDescriptorTag:
+            d=new BouquetNameDescriptor();
+            break;
+         case ServiceDescriptorTag:
+            d=new ServiceDescriptor();
+            break;
+         case NVODReferenceDescriptorTag:
+            d=new NVODReferenceDescriptor();
+            break;
+         case TimeShiftedServiceDescriptorTag:
+            d=new TimeShiftedServiceDescriptor();
+            break;
+         case ComponentDescriptorTag:
+            d=new ComponentDescriptor();
+            break;
+         case StreamIdentifierDescriptorTag:
+            d=new StreamIdentifierDescriptor();
+            break;
+         case SubtitlingDescriptorTag:
+            d=new SubtitlingDescriptor();
+            break;
+         case MultilingualNetworkNameDescriptorTag:
+            d=new MultilingualNetworkNameDescriptor();
+            break;
+         case MultilingualBouquetNameDescriptorTag:
+            d=new MultilingualBouquetNameDescriptor();
+            break;
+         case MultilingualServiceNameDescriptorTag:
+            d=new MultilingualServiceNameDescriptor();
+            break;
+         case MultilingualComponentDescriptorTag:
+            d=new MultilingualComponentDescriptor();
+            break;
+         case PrivateDataSpecifierDescriptorTag:
+            d=new PrivateDataSpecifierDescriptor();
+            break;
+         case ServiceMoveDescriptorTag:
+            d=new ServiceMoveDescriptor();
+            break;
+         case FrequencyListDescriptorTag:
+            d=new FrequencyListDescriptor();
+            break;
+         case ServiceIdentifierDescriptorTag:
+            d=new ServiceIdentifierDescriptor();
+            break;
+         case CaIdentifierDescriptorTag:
+            d=new CaIdentifierDescriptor();
+            break;
+         case ShortEventDescriptorTag:
+            d=new ShortEventDescriptor();
+            break;
+         case ExtendedEventDescriptorTag:
+            d=new ExtendedEventDescriptor();
+            break;
+         case TimeShiftedEventDescriptorTag:
+            d=new TimeShiftedEventDescriptor();
+            break;
+         case ContentDescriptorTag:
+            d=new ContentDescriptor();
+            break;
+         case ParentalRatingDescriptorTag:
+            d=new ParentalRatingDescriptor();
+            break;
+         case TeletextDescriptorTag:
+         case VBITeletextDescriptorTag:
+            d=new TeletextDescriptor();
+            break;
+         case ApplicationSignallingDescriptorTag:
+            d=new ApplicationSignallingDescriptor();
+            break;
+         case LocalTimeOffsetDescriptorTag:
+            d=new LocalTimeOffsetDescriptor();
+            break;
+         case LinkageDescriptorTag:
+            d=new LinkageDescriptor();
+            break;
+         case ISO639LanguageDescriptorTag:
+            d=new ISO639LanguageDescriptor();
+            break;
+         case PDCDescriptorTag:
+            d=new PDCDescriptor();
+            break;
+         case AncillaryDataDescriptorTag:
+            d=new AncillaryDataDescriptor();
+            break;
+
+         //note that it is no problem to implement one
+         //of the unimplemented descriptors.
+
+         //defined in ISO-13818-1
+         case VideoStreamDescriptorTag:
+         case AudioStreamDescriptorTag:
+         case HierarchyDescriptorTag:
+         case RegistrationDescriptorTag:
+         case DataStreamAlignmentDescriptorTag:
+         case TargetBackgroundGridDescriptorTag:
+         case VideoWindowDescriptorTag:
+         case SystemClockDescriptorTag:
+         case MultiplexBufferUtilizationDescriptorTag:
+         case CopyrightDescriptorTag:
+         case MaximumBitrateDescriptorTag:
+         case PrivateDataIndicatorDescriptorTag:
+         case SmoothingBufferDescriptorTag:
+         case STDDescriptorTag:
+         case IBPDescriptorTag:
+
+         //defined in ETSI EN 300 468
+         case StuffingDescriptorTag:
+         case VBIDataDescriptorTag:
+         case CountryAvailabilityDescriptorTag:
+         case MocaicDescriptorTag:
+         case TelephoneDescriptorTag:
+         case CellListDescriptorTag:
+         case CellFrequencyLinkDescriptorTag:
+         case ServiceAvailabilityDescriptorTag:
+         case ShortSmoothingBufferDescriptorTag:
+         case PartialTransportStreamDescriptorTag:
+         case DataBroadcastDescriptorTag:
+         case DataBroadcastIdDescriptorTag:
+         case CaSystemDescriptorTag:
+         case AC3DescriptorTag:
+         case DSNGDescriptorTag:
+         case AnnouncementSupportDescriptorTag:
+         case AdaptationFieldDataDescriptorTag:
+         case TransportStreamDescriptorTag:
+         default:
+            if (!returnUnimplemetedDescriptor)
+               return 0;
+            d=new UnimplementedDescriptor();
+            break;
+      }
+      break;
+   case MHP:
+      switch ((DescriptorTag)da.getData<DescriptorHeader>()->descriptor_tag) {
+      // They once again start with 0x00 (see page 234, MHP specification)
+         case MHP_ApplicationDescriptorTag:
+            d=new MHP_ApplicationDescriptor();
+            break;
+         case MHP_ApplicationNameDescriptorTag:
+            d=new MHP_ApplicationNameDescriptor();
+            break;
+         case MHP_TransportProtocolDescriptorTag:
+            d=new MHP_TransportProtocolDescriptor();
+            break;
+         case MHP_DVBJApplicationDescriptorTag:
+            d=new MHP_DVBJApplicationDescriptor();
+            break;
+         case MHP_DVBJApplicationLocationDescriptorTag:
+            d=new MHP_DVBJApplicationLocationDescriptor();
+            break;
+      // 0x05 - 0x0A is unimplemented this library
+         case MHP_ExternalApplicationAuthorisationDescriptorTag:
+         case MHP_IPv4RoutingDescriptorTag:
+         case MHP_IPv6RoutingDescriptorTag:
+         case MHP_DVBHTMLApplicationDescriptorTag:
+         case MHP_DVBHTMLApplicationLocationDescriptorTag:
+         case MHP_DVBHTMLApplicationBoundaryDescriptorTag:
+         case MHP_ApplicationIconsDescriptorTag:
+         case MHP_PrefetchDescriptorTag:
+         case MHP_DelegatedApplicationDescriptorTag:
+         case MHP_ApplicationStorageDescriptorTag:
+         default:
+            if (!returnUnimplemetedDescriptor)
+               return 0;
+            d=new UnimplementedDescriptor();
+            break;
+      }
+      break;
+   case PCIT:
+      switch ((DescriptorTag)da.getData<DescriptorHeader>()->descriptor_tag) {
+         case ContentDescriptorTag:
+            d=new ContentDescriptor();
+            break;
+         case ShortEventDescriptorTag:
+            d=new ShortEventDescriptor();
+            break;
+         case ExtendedEventDescriptorTag:
+            d=new ExtendedEventDescriptor();
+            break;
+         case PremiereContentTransmissionDescriptorTag:
+            d=new PremiereContentTransmissionDescriptor();
+            break;
+         default:
+            if (!returnUnimplemetedDescriptor)
+               return 0;
+            d=new UnimplementedDescriptor();
+            break;
+      }
+      break;
+   }
+   d->setData(da);
+   return d;
+}
+
+} //end of namespace
diff --git a/contrib/sasc-ng/sc/libsi/util.c b/contrib/sasc-ng/sc/libsi/util.c
new file mode 100644 (file)
index 0000000..b0db93e
--- /dev/null
@@ -0,0 +1,284 @@
+/***************************************************************************
+ *       Copyright (c) 2003 by Marcel Wiesweg, Rolf Hakenes                *
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ *   $Id: util.c 1.7 2006/02/18 11:17:50 kls Exp $
+ *                                                                         *
+ ***************************************************************************/
+
+#include <string.h>
+#include "util.h"
+
+namespace SI {
+
+/*---------------------------- CharArray ----------------------------*/
+
+CharArray::CharArray() : data_(0), off(0) {
+}
+
+CharArray::~CharArray() {
+   if (!data_)
+      return;
+   if (--data_->count_ == 0)
+      delete data_;
+}
+
+CharArray::CharArray(const CharArray &f) : data_(f.data_), off(f.off) {
+   if (data_)
+      ++ data_->count_;
+}
+
+CharArray& CharArray::operator=(const CharArray &f) {
+    // DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
+    // (This order properly handles self-assignment)
+    if (f.data_) {
+      ++ f.data_->count_;
+    }
+    if (data_) {
+      if (--data_->count_ == 0)
+         delete data_;
+    }
+    data_ = f.data_;
+    off = f.off;
+    return *this;
+}
+
+void CharArray::assign(const unsigned char*data, int size, bool doCopy) {
+    //immutable
+    if (!data_)
+      data_= doCopy ? (Data*)new DataOwnData() : (Data*)new DataForeignData();
+    // This method might need to change things in *data_
+    // Thus it first checks if this is the only pointer to *data_
+    if (data_->count_ > 1) {
+      Data* d = doCopy ? (Data*)new DataOwnData() : (Data*)new DataForeignData();
+      -- data_->count_;
+      data_ = d;
+    }
+    data_->assign(data, size);
+}
+
+bool CharArray::operator==(const char *string) const {
+   //here we can use strcmp, string is null-terminated.
+   if (!data_)
+      return false;
+   return data_->size ? (!strcmp((const char*)data_->data, string)) : string[0]==0;
+}
+
+bool CharArray::operator==(const CharArray &other) const {
+   if (!data_ || !other.data_)
+      return !(data_ || other.data_); //true if both empty
+
+   if (data_->size != other.data_->size)
+      return false;
+
+   //do _not_ use strcmp! Data is not necessarily null-terminated.
+   for (int i=0;i<data_->size;i++)
+      if (data_->data[i] != other.data_->data[i])
+         return false;
+   return true;
+}
+
+CharArray CharArray::operator+(const int offset) const {
+   CharArray f(*this);
+   f.off+=offset;
+   return f;
+}
+
+CharArray::Data::Data() : data(0), size(0), count_(1), valid(true) {
+   /*
+   lockingPid = 0;
+   locked = 0;
+   pthread_mutex_init(&mutex, NULL);
+   */
+}
+
+CharArray::Data::~Data() {
+   /*
+   if (locked)
+      pthread_mutex_unlock(&mutex);
+   pthread_mutex_destroy(&mutex);
+   */
+}
+
+/*CharArray::Data::Data(const Data& d) : count_(1) {
+   size=0;
+   data=0;
+
+   lockingPid = 0;
+   locked = 0;
+   pthread_mutex_init(&mutex, NULL);
+}*/
+
+CharArray::DataOwnData::~DataOwnData() {
+   Delete();
+}
+
+void CharArray::DataOwnData::assign(const unsigned char*d, int s) {
+   Delete();
+   if (!d || s > 100000 || s <= 0) // ultimate plausibility check
+      return;
+   size=s;
+   unsigned char *newdata=new unsigned char[size];
+   memcpy(newdata, d, size);
+   data=newdata;
+}
+
+void CharArray::DataOwnData::Delete() {
+   delete[] data;
+   size=0;
+   data=0;
+}
+
+CharArray::DataForeignData::~DataForeignData() {
+   Delete();
+}
+
+void CharArray::DataForeignData::assign(const unsigned char*d, int s) {
+   size=s;
+   data=d;
+}
+
+void CharArray::DataForeignData::Delete() {
+   //do not delete!
+}
+
+/*
+void CharArray::Data::assign(int s) {
+   if (data)
+      delete[] data;
+   size=s;
+   if (size) { //new assignment may be zero length
+      data=new unsigned char[size];
+      memset(data, 0, size);
+   }
+}
+
+void CharArray::Data::Lock(void)
+{
+  if ( !pthread_equal(pthread_self(), lockingPid) || !locked) {
+     pthread_mutex_lock(&mutex);
+     lockingPid = pthread_self();
+     }
+  locked++;
+}
+
+void CharArray::Data::Unlock(void)
+{
+   if (!--locked) {
+     lockingPid = 0;
+     pthread_mutex_unlock(&mutex);
+   }
+}
+*/
+
+Parsable::Parsable() {
+   parsed=false;
+}
+
+void Parsable::CheckParse() {
+   if (!parsed) {
+      parsed=true;
+      Parse();
+   }
+}
+
+//taken and adapted from libdtv, (c) Rolf Hakenes and VDR, (c) Klaus Schmidinger
+time_t DVBTime::getTime(unsigned char date_hi, unsigned char date_lo, unsigned char time_hour, unsigned char time_minute, unsigned char time_second) {
+   u_int16_t mjd = date_hi << 8 | date_lo;
+   struct tm t;
+
+   t.tm_sec = bcdToDec(time_second);
+   t.tm_min = bcdToDec(time_minute);
+   t.tm_hour = bcdToDec(time_hour);
+
+   int k;
+   t.tm_year = (int) ((mjd - 15078.2) / 365.25);
+   t.tm_mon = (int) ((mjd - 14956.1 - (int)(t.tm_year * 365.25)) / 30.6001);
+   t.tm_mday = (int) (mjd - 14956 - (int)(t.tm_year * 365.25) - (int)(t.tm_mon * 30.6001));
+   k = (t.tm_mon == 14 || t.tm_mon == 15) ? 1 : 0;
+   t.tm_year = t.tm_year + k;
+   t.tm_mon = t.tm_mon - 1 - k * 12;
+   t.tm_mon--;
+
+   t.tm_isdst = -1;
+   t.tm_gmtoff = 0;
+
+   return timegm(&t);
+}
+
+time_t DVBTime::getDuration(unsigned char time_hour, unsigned char time_minute, unsigned char time_second) {
+   return
+     bcdToDec(time_second)
+   + bcdToDec(time_minute) * 60
+   + bcdToDec(time_hour) *3600;
+}
+
+//taken and adapted from libdtv, (c) Rolf Hakenes
+// CRC32 lookup table for polynomial 0x04c11db7
+u_int32_t CRC32::crc_table[256] = {
+   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
+
+u_int32_t CRC32::crc32 (const char *d, int len, u_int32_t crc)
+{
+   register int i;
+   const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
+
+   for (i=0; i<len; i++)
+      crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
+
+   return crc;
+}
+
+CRC32::CRC32(const char *d, int len, u_int32_t CRCvalue) {
+   data=d;
+   length=len;
+   value=CRCvalue;
+}
+
+} //end of namespace
diff --git a/contrib/sasc-ng/sc/log.cpp b/contrib/sasc-ng/sc/log.cpp
new file mode 100644 (file)
index 0000000..643f122
--- /dev/null
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include "log.h"
+extern int tmprintf(const char *plugin, const char *fmt, ...);
+static const char *plugin_name;
+static unsigned  *log_level, print_level, id;
+
+static void LogPrintSasc(const struct LogHeader *lh, const char *txt) {
+  char tag[80];
+  if(print_level <= ((*log_level >> id) & 3)) {
+    sprintf(tag, "%s(%s)", plugin_name, lh->tag);
+    tmprintf(tag, "%s\n", txt);
+  }
+}
+
+void SetCAMPrint(const char *_plugin_name, unsigned int plugin_id, unsigned int _print_level, unsigned int *_log_level) {
+  plugin_name = _plugin_name;
+  id = plugin_id;
+  log_level = _log_level;
+  print_level = _print_level;
+  cLogging::SetLogPrint(&LogPrintSasc);
+}
diff --git a/contrib/sasc-ng/sc/menuitems.cpp b/contrib/sasc-ng/sc/menuitems.cpp
new file mode 100644 (file)
index 0000000..234e840
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * menuitems.c: General purpose menu items
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: menuitems.c 1.19 2004/06/19 09:45:45 kls Exp $
+ */
+
+#include "include/vdr/menuitems.h"
+#include <ctype.h>
+#include "include/vdr/plugin.h"
+#include "include/vdr/remote.h"
+#include "include/vdr/skins.h"
+#include "include/vdr/status.h"
+
+const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
+
+// --- cMenuEditItem ---------------------------------------------------------
+
+cMenuEditItem::cMenuEditItem(const char *Name)
+{
+  name = strdup(Name ? Name : "???");
+}
+
+cMenuEditItem::~cMenuEditItem()
+{
+  free(name);
+}
+
+void cMenuEditItem::SetValue(const char *Value)
+{
+  char *buffer = NULL;
+  asprintf(&buffer, "%s:\t%s", name, Value);
+  SetText(buffer, false);
+}
+
+// --- cMenuEditIntItem ------------------------------------------------------
+
+cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max, const char *MinString, const char *MaxString)
+:cMenuEditItem(Name)
+{
+  value = Value;
+  min = Min;
+  max = Max;
+  minString = MinString;
+  maxString = MaxString;
+  if (*value < min)
+     *value = min;
+  else if (*value > max)
+     *value = max;
+  Set();
+}
+
+void cMenuEditIntItem::Set(void)
+{
+  if (minString && *value == min)
+     SetValue(minString);
+  else if (maxString && *value == max)
+     SetValue(maxString);
+  else {
+     char buf[16];
+     snprintf(buf, sizeof(buf), "%d", *value);
+     SetValue(buf);
+     }
+}
+
+eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditItem::ProcessKey(Key);
+
+  if (state == osUnknown) {
+     int newValue = *value;
+     Key = NORMALKEY(Key);
+     switch (Key) {
+       case kNone: break;
+       case k0 ... k9:
+            if (fresh) {
+               *value = 0;
+               fresh = false;
+               }
+            newValue = *value * 10 + (Key - k0);
+            break;
+       case kLeft: // TODO might want to increase the delta if repeated quickly?
+            newValue = *value - 1;
+            fresh = true;
+            break;
+       case kRight:
+            newValue = *value + 1;
+            fresh = true;
+            break;
+       default:
+            if (*value < min) { *value = min; Set(); }
+            if (*value > max) { *value = max; Set(); }
+            return state;
+       }
+     if ((!fresh || min <= newValue) && newValue <= max) {
+        *value = newValue;
+        Set();
+        }
+     state = osContinue;
+     }
+  return state;
+}
+
+// --- cMenuEditBoolItem -----------------------------------------------------
+
+cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
+:cMenuEditIntItem(Name, Value, 0, 1)
+{
+  falseString = FalseString ? FalseString : tr("no");
+  trueString = TrueString ? TrueString : tr("yes");
+  Set();
+}
+
+void cMenuEditBoolItem::Set(void)
+{
+  char buf[16];
+  snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
+  SetValue(buf);
+}
+
+// --- cMenuEditBitItem ------------------------------------------------------
+
+cMenuEditBitItem::cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString, const char *TrueString)
+:cMenuEditBoolItem(Name, &bit, FalseString, TrueString)
+{
+  value = Value;
+  bit = (*value & Mask) != 0;
+  mask = Mask;
+  Set();
+}
+
+void cMenuEditBitItem::Set(void)
+{
+  *value = bit ? *value | mask : *value & ~mask;
+  cMenuEditBoolItem::Set();
+}
+
+// --- cMenuEditNumItem ------------------------------------------------------
+
+cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
+:cMenuEditItem(Name)
+{
+  value = Value;
+  length = Length;
+  blind = Blind;
+  Set();
+}
+
+void cMenuEditNumItem::Set(void)
+{
+  if (blind) {
+     char buf[length + 1];
+     int i;
+     for (i = 0; i < length && value[i]; i++)
+         buf[i] = '*';
+     buf[i] = 0;
+     SetValue(buf);
+     }
+  else
+     SetValue(value);
+}
+
+eOSState cMenuEditNumItem::ProcessKey(eKeys Key)
+{
+  eOSState state = cMenuEditItem::ProcessKey(Key);
+
+  if (state == osUnknown) {
+     Key = NORMALKEY(Key);
+     switch (Key) {
+       case kLeft: {
+            int l = strlen(value);
+            if (l > 0)
+               value[l - 1] = 0;
+            }
+            break;
+       case k0 ... k9: {
+            int l = strlen(value);
+            if (l < length) {
+               value[l] = Key - k0 + '0';
+               value[l + 1] = 0;
+               }
+            }
+            break;
+       default: return state;
+       }
+     Set();
+     state = osContinue;
+     }
+  return state;
+}
+
+// --- cMenuEditChrItem ------------------------------------------------------
+
+cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
+:cMenuEditItem(Name)
+{
+  value = Value;
+  allowed = strdup(Allowed);
+  current = strchr(allowed, *Value);
+  if (!current)
+     current = allowed;
+  Set();
+}
+
+cMenuEditChrItem::~cMenuEditChrItem()
+{
+  free(allowed);
+}
+
+void cMenuEditChrItem::Set(void)
+{
+  char buf[2];
+  snprintf(buf, sizeof(buf), "%c", *value);
+  SetValue(buf);
+}
+
+eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuEditStrItem ------------------------------------------------------
+
+cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
+:cMenuEditItem(Name)
+{
+}
+
+cMenuEditStrItem::~cMenuEditStrItem()
+{
+}
+
+void cMenuEditStrItem::SetHelpKeys(void)
+{
+}
+
+void cMenuEditStrItem::Set(void)
+{
+}
+
+uint cMenuEditStrItem::Inc(uint c, bool Up)
+{
+  return '\0';
+}
+
+eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuEditStraItem -----------------------------------------------------
+
+cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
+:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
+{
+}
+
+void cMenuEditStraItem::Set(void)
+{
+}
+
+// --- cMenuEditChanItem -----------------------------------------------------
+
+cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *NoneString)
+:cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, Channels.MaxNumber())
+{
+  noneString = NoneString;
+  Set();
+}
+
+void cMenuEditChanItem::Set(void)
+{
+}
+
+eOSState cMenuEditChanItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuEditTranItem -----------------------------------------------------
+
+cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source)
+:cMenuEditChanItem(Name, Value)
+{
+}
+
+eOSState cMenuEditTranItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuEditDateItem -----------------------------------------------------
+
+cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays)
+:cMenuEditItem(Name)
+{
+}
+
+void cMenuEditDateItem::Set(void)
+{
+}
+
+eOSState cMenuEditDateItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuEditTimeItem -----------------------------------------------------
+
+cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value)
+:cMenuEditItem(Name)
+{
+}
+
+void cMenuEditTimeItem::Set(void)
+{
+}
+
+eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+// --- cMenuSetupPage --------------------------------------------------------
+
+cMenuSetupPage::cMenuSetupPage(void)
+:cOsdMenu("", 33)
+{
+  plugin = NULL;
+}
+
+void cMenuSetupPage::SetSection(const char *Section)
+{
+}
+
+eOSState cMenuSetupPage::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}
+
+void cMenuSetupPage::SetPlugin(cPlugin *Plugin)
+{
+}
+
+void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
+{
+}
+
+void cMenuSetupPage::SetupStore(const char *Name, int Value)
+{
+}
diff --git a/contrib/sasc-ng/sc/misc.cpp b/contrib/sasc-ng/sc/misc.cpp
new file mode 100644 (file)
index 0000000..b803586
--- /dev/null
@@ -0,0 +1,68 @@
+#include "include/vdr/channels.h"
+#include "include/vdr/device.h"
+#include <ctype.h>
+#include <linux/dvb/ca.h>
+int cChannel::Transponder() const {return sid;}
+int cChannel::Transponder(int Frequency, char Polarization)
+{
+  // some satellites have transponders at the same frequency, just with different polarization:
+  switch (tolower(Polarization)) {
+    case 'h': Frequency += 100000; break;
+    case 'v': Frequency += 200000; break;
+    case 'l': Frequency += 300000; break;
+    case 'r': Frequency += 400000; break;
+    }
+  return Frequency;
+}
+cChannel* cChannels::GetByNumber(int a, int b) {
+  cChannel *ch = new cChannel;
+  ch->SetId(0,0,a);
+  return ch;
+}
+void cChannel::SetId(int Nid, int Tid, int Sid, int Rid) {
+  sid = Sid;
+  tid = Tid;
+}
+bool cChannel::SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH) {
+     source = Source;
+     frequency = Frequency;
+     polarization = Polarization;
+     srate = Srate;
+     coderateH = CoderateH;
+     return true;
+}
+void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int Tpid)
+{
+  vpid = Vpid;
+  ppid = Ppid;
+  tpid = Tpid;
+  for (int i = 0; i < MAXAPIDS; i++)
+      apids[i] = Apids[i];
+  apids[MAXAPIDS] = 0;
+  for (int i = 0; i < MAXDPIDS; i++) {
+     dpids[i] = Dpids[i];
+  }
+  dpids[MAXDPIDS] = 0;
+}
+
+cChannel::cChannel() {
+  caids[0]=0x0101;
+  caids[1]=0;
+  source=1;
+  sid=1;
+  groupSep=0;
+  pmtlen = 0;
+}
+cChannel::~cChannel() {
+}
+
+void cChannel::SetPMTBuf(const unsigned char *buf, int len)
+{
+  memcpy(pmtbuf, buf, len);
+  pmtlen = len;
+}
+int cChannel::GetPMTBuf(unsigned char *buf) {
+  memcpy(buf, pmtbuf, pmtlen);
+  return pmtlen;
+}
+
diff --git a/contrib/sasc-ng/sc/osdbase.cpp b/contrib/sasc-ng/sc/osdbase.cpp
new file mode 100644 (file)
index 0000000..9be8abe
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * osdbase.c: Basic interface to the On Screen Display
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osdbase.c 1.14 2004/07/17 13:29:13 kls Exp $
+ */
+
+#include "include/vdr/osdbase.h"
+#include <string.h>
+#include "include/vdr/device.h"
+#include "include/vdr/i18n.h"
+#include "include/vdr/remote.h"
+#include "include/vdr/status.h"
+
+// --- cOsdItem --------------------------------------------------------------
+
+cOsdItem::cOsdItem(eOSState State)
+{
+  text = NULL;
+  state = State;
+  selectable = true;
+  fresh = true;
+}
+
+cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
+{
+  text = NULL;
+  state = State;
+  selectable = Selectable;
+  fresh = true;
+  SetText(Text);
+}
+
+cOsdItem::~cOsdItem()
+{
+  free(text);
+}
+
+void cOsdItem::SetText(const char *Text, bool Copy)
+{
+  free(text);
+  text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership!
+}
+
+void cOsdItem::SetSelectable(bool Selectable)
+{
+  selectable = Selectable;
+}
+
+void cOsdItem::SetFresh(bool Fresh)
+{
+  fresh = Fresh;
+}
+
+eOSState cOsdItem::ProcessKey(eKeys Key)
+{
+  return Key == kOk ? state : osUnknown;
+}
+
+void cOsdObject::Show(void)
+{
+}
+// --- cOsdMenu --------------------------------------------------------------
+
+//cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
+//int cOsdMenu::displayMenuCount = 0;
+//int cOsdMenu::displayMenuItems = 0;//XXX dynamic???
+
+cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
+{
+}
+
+cOsdMenu::~cOsdMenu()
+{
+}
+
+const char *cOsdMenu::hk(const char *s)
+{
+  return NULL;
+}
+
+void cOsdMenu::SetStatus(const char *s)
+{
+}
+
+void cOsdMenu::SetTitle(const char *Title)
+{
+}
+
+void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+{
+}
+
+void cOsdMenu::Del(int Index)
+{
+}
+
+void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
+{
+}
+
+void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
+{
+}
+
+void cOsdMenu::Display(void)
+{
+}
+
+void cOsdMenu::SetCurrent(cOsdItem *Item)
+{
+}
+
+void cOsdMenu::RefreshCurrent(void)
+{
+}
+
+void cOsdMenu::DisplayCurrent(bool Current)
+{
+}
+
+void cOsdMenu::Clear(void)
+{
+}
+
+bool cOsdMenu::SelectableItem(int idx)
+{
+  return true;
+}
+
+void cOsdMenu::CursorUp(void)
+{
+}
+
+void cOsdMenu::CursorDown(void)
+{
+}
+
+void cOsdMenu::PageUp(void)
+{
+}
+
+void cOsdMenu::PageDown(void) 
+{
+}
+
+void cOsdMenu::Mark(void)
+{
+}
+
+eOSState cOsdMenu::HotKey(eKeys Key)
+{
+  return osUnknown;
+}
+
+eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
+{
+  return osUnknown;
+}
+
+eOSState cOsdMenu::CloseSubMenu()
+{
+  return osUnknown;
+}
+
+eOSState cOsdMenu::ProcessKey(eKeys Key)
+{
+  return osUnknown;
+}
+
diff --git a/contrib/sasc-ng/sc/othercamd.cpp b/contrib/sasc-ng/sc/othercamd.cpp
new file mode 100644 (file)
index 0000000..629bddf
--- /dev/null
@@ -0,0 +1,251 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <linux/dvb/ca.h>
+
+void _SetCaPid(ca_pid_t * ca_pid);
+void _SetCaDescr(ca_descr_t * ca_descr);
+void _ForceIndex(int index);
+
+bool is_file(char *path) {
+  struct stat st;
+  if (stat(path, &st) != 0) {
+    return false;
+  }
+  return true;
+}
+
+/* this function finds all pids that are attached to the unix socket 'origstr'.
+   It does this by sniffing through /proc, so it is definitely non-portable */
+int find_pid_from_socket(char *origstr) {
+  int found = 0, pid = 0;
+  char path[256], buf[512];
+  char path1[256], buf1[512];
+  DIR *DH, *DH1;
+  struct dirent *dirent, *dirent1;
+  DH = opendir("/proc");
+  while ((dirent = readdir(DH))) {
+     if ((pid = strtol(dirent->d_name, NULL, 10)) == 0) {
+        continue;
+     }
+     snprintf(path, 256, "/proc/%s/root", dirent->d_name);
+     memset(buf, 0, sizeof(buf));
+     if (readlink(path, buf, 512) == -1)
+        continue;
+     if (strstr(origstr, buf) != origstr)
+        continue;
+     snprintf(path, 256, "/proc/%s/fd", dirent->d_name);
+     DH1 = opendir(path);
+     if(! DH1)
+        continue;
+     if(buf[strlen(buf)-1] == '/')
+       buf[strlen(buf)-1] = 0;
+     while ((dirent1 = readdir(DH1))) {
+        int sock;
+        memset(buf1, 0, sizeof(buf1));
+        snprintf(path1, 256, "%s/%s", path, dirent1->d_name);
+        if (readlink(path1, buf1, 512) == -1)
+          continue;
+        if (strstr(buf1, "socket") != buf1)
+          continue;
+        sock = atoi(buf1+8);
+        if (sock == 0)
+           continue;
+        {
+          FILE *FH;
+          char buf2[256];
+          char *tok;
+          FH=fopen("/proc/net/unix", "r");
+          while (fgets(buf2, 256, FH) ) {
+            int i;
+            char delim[] =" \t";
+            char *ptr = buf2;
+            int trysock;
+            for(i = 0; i < 7; i++) {
+              tok = strtok(ptr, delim);
+              if(! tok)
+                 break;
+              ptr = NULL;
+            }
+            if (! tok)
+              continue;
+            trysock = atoi(tok);
+            if (trysock == 0 || sock != trysock)
+              continue;
+             tok = strtok(NULL, delim);
+             if (! tok)
+               continue;
+             char newstr[256];
+             snprintf(newstr, 256, "%s%s", buf, tok);
+             if (strncmp(origstr, newstr, strlen(origstr)) == 0) {
+                found = 1;
+                break;
+             }
+           }
+           fclose(FH);
+           break;
+         }
+     }
+     closedir(DH1);
+     if (found)
+       break;
+   }
+   closedir(DH);
+   if (found)
+     return pid;
+   return 0;
+}
+
+bool shutdown_evocamd(int cardnum) {
+  char path[256], line[256];
+  int pid = 0;
+  snprintf(path, 256, "/tmp/sasc-chroot-%d/tmp/camd.socket", cardnum);
+  if (is_file(path)) {
+    while((pid = find_pid_from_socket(path))) {
+      printf("Killing evocamd at pid %d\n", pid);
+      kill(pid, SIGTERM);
+      sleep(1);
+    }
+  }
+  snprintf(path, 256, "/tmp/sasc-chroot-%d", cardnum);
+  if (is_file(path)) {
+    snprintf(line, 256, "rm -rf %s", path);
+    system(line);
+  }
+  return true;
+}
+
+int send_evocamd(int cardnum, unsigned short servid, unsigned short msg) {
+    int evocamdSocket;
+    char sockBuffer[3];
+    char sockname[256];
+    struct sockaddr_un servaddr;
+    int clilen;
+
+    snprintf(sockname, 256, "/tmp/sasc-chroot-%d/tmp/camd.socket", cardnum);
+    servaddr.sun_family = AF_UNIX;
+    strcpy(servaddr.sun_path, sockname);
+    clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path);
+    if ((evocamdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+    {
+      fprintf(stderr, "[Evocamd] socket creation failed\n");
+      return 0;
+    }
+    if (connect(evocamdSocket, (struct sockaddr*) &servaddr, clilen) < 0)
+    {
+      fprintf(stderr, "[Evocamd] socket connection failed\n");
+      return 0;
+    }
+    sockBuffer[0] = servid >> 8;
+    sockBuffer[1] = servid & 0xff;
+    sockBuffer[2] = msg;
+    //printf("Sending %d/%d to evocamd\n", servid, msg);
+    if (write(evocamdSocket, sockBuffer, 3) < 0)
+    {
+      fprintf(stderr, "[Evocamd] write failed\n");
+      return 0;
+    }
+    close(evocamdSocket);
+    return 1;
+}
+
+bool init_evocamd(int cardnum, char *scDir) {
+  char path[512];
+  char cmd[512];
+
+  //shutdown any evocamd daemons that are already running
+  shutdown_evocamd(cardnum);
+  snprintf(path, 512, "/tmp/sasc-chroot-%d", cardnum);
+  mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP);
+  chdir(path);
+  snprintf(path, 512, "%s/camd.tar.gz", scDir);
+  if (! is_file(path)) {
+    fprintf(stderr, "Failed to find camd.tar.gz.  Aborting.");
+    return false; 
+  }
+  snprintf(cmd, 512, "tar -xvzf %s", path);
+  if(system(cmd)) {
+    fprintf(stderr, "Evocamd extract failed\n");
+    return false;
+  }
+  snprintf(cmd, 512, "sasc-chroot-dvb %d evocamd", cardnum);
+  if(system(cmd)) {
+    fprintf(stderr, "Evocamd call failed\n");
+    return false;
+  }
+  return true;
+}
+
+int start_loopbackca(int cardnum) {
+  char path[256];
+  int fd;
+
+  snprintf(path, 256, "/proc/ca_emu_%d", cardnum);
+
+  fd = open(path, O_RDONLY);
+  if(fd < 0) {
+    perror("Failed to open loopback-ca\n");
+    return 0;
+  }
+  pid_t pid = fork();
+   if (pid == 0) // child
+   {
+      //ca_emu_* is processed as follows:
+      //  data[0] : number of records to read (<=10)
+      //  data[i*100 + 1]  : ioctl command
+      //  &data[i*100 + 2] : data record from ioctl
+      unsigned char data[1024], *dataptr;
+      _ForceIndex(1);
+      while(1) {
+        int bytes = read(fd, data, 1024);
+        lseek(fd, SEEK_SET, 0);
+        //printf("Read %d bytes from ca-loopback (%d cmds)\n", bytes, data[0]);
+        if (bytes != 1024) {
+            continue;
+        }
+        int count = data[0];
+        for (int i=0; i < count; i++) {
+          unsigned int cmd;
+          memcpy(&cmd, data+ i*100 + 1, sizeof(cmd));
+          //printf("Parsing command: %d\n", cmd);
+          dataptr =  data+ i*100 + 1 + sizeof(cmd);
+          switch(cmd) {
+         case 135: //CA_SET_PID
+            {
+              ca_pid_t ca_pid;
+              memcpy(&ca_pid, dataptr, sizeof(ca_pid));
+              ca_pid.index = 1;
+              _SetCaPid(&ca_pid);
+              break;
+            }
+          case 134: //CA_SET_DESCR
+            {
+              ca_descr_t ca_descr;
+              memcpy(&ca_descr, dataptr, sizeof(ca_descr));
+              ca_descr.index = 1;
+              _SetCaDescr(&ca_descr);
+              break;
+            }
+          default:
+              fprintf(stderr, "CA Loopback unknown command: %d\n", cmd);
+              break;
+          }
+        }
+      }
+      close(fd);
+      exit(0);
+   }
+   close(fd);
+   return(pid);
+}
+
diff --git a/contrib/sasc-ng/sc/othercamd.h b/contrib/sasc-ng/sc/othercamd.h
new file mode 100644 (file)
index 0000000..4243791
--- /dev/null
@@ -0,0 +1,7 @@
+
+int find_pid_from_socket(char *origstr);
+int send_evocamd(int cardnum, unsigned short servid, unsigned short msg);
+bool init_evocamd(int cardnum, char *scDir);
+int start_loopbackca(int cardnum);
+bool shutdown_evocamd(int cardnum);
+
diff --git a/contrib/sasc-ng/sc/sasccam.cpp b/contrib/sasc-ng/sc/sasccam.cpp
new file mode 100644 (file)
index 0000000..4335696
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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
+ */
+
+#define SASC
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "include/vdr/dvbdevice.h"
+#include "include/vdr/channels.h"
+#include "cam.h"
+#include "sasccam.h"
+#include "scsetup.h"
+
+// -- cSascDvbDevice -------------------------------------------------------------
+class cSascDvbDevice : public cScDvbDevice {
+private:
+  int cardidx;
+public:
+  cSascDvbDevice(int n, int cafd) :cScDvbDevice(n, cafd) {cardidx = n;}
+  ~cSascDvbDevice() {};
+  bool SetCaDescr(ca_descr_t *ca_descr, bool initial);
+  bool SetCaPid(ca_pid_t *ca_pid);
+  bool GetPrgCaids(int source, int transponder, int prg, caid_t *c);
+  };
+
+bool cSascDvbDevice::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+  *c++ = 0x0101;
+  *c = 0;
+  return true;
+}
+
+extern void _SetCaDescr(int adapter, ca_descr_t *ca_descr);
+bool cSascDvbDevice::SetCaDescr(ca_descr_t *ca_descr, bool initial)
+{
+  printf("Called cSascDvbDevice::SetCaDescr\n");
+  _SetCaDescr(cardidx, ca_descr);
+  return true;
+}
+
+extern void _SetCaPid(int adapter, ca_pid_t *ca_pid);
+bool cSascDvbDevice::SetCaPid(ca_pid_t *ca_pid)
+{
+  printf("Called cSascDvbDevice::SetCaPid\n");
+  _SetCaPid(cardidx, ca_pid);
+  return true;
+}
+
+//Functions to communicate with the cam from the outside world
+//Initialize the cam
+sascCam::sascCam(int devnum)
+{
+  dev = new cSascDvbDevice(devnum, -1);
+  cam = dev->Cam();
+  ScSetup.ConcurrentFF=8;
+}
+void sascCam::Tune(cChannel *ch)
+{ 
+  cam->Tune(ch);
+}
+void sascCam::Stop()
+{
+  cam->Stop();
+}
+void sascCam::AddPrg(int sid, int *epid)
+{
+  int i = 0;
+  if(! epid)
+    return;
+  cPrg *prg=new cPrg(sid, 1); 
+  while(epid[i]) {
+    cPrgPid *pid=new cPrgPid(5, epid[i++]);
+    prg->pids.Add(pid);
+  }
+  cam->AddPrg(prg);
+}
+    
diff --git a/contrib/sasc-ng/sc/sasccam.h b/contrib/sasc-ng/sc/sasccam.h
new file mode 100644 (file)
index 0000000..7d9fdb1
--- /dev/null
@@ -0,0 +1,13 @@
+class cCam;
+class cSascDvbDevice;
+
+class sascCam {
+private:
+ cCam *cam;
+ cSascDvbDevice *dev;
+public:
+  sascCam(int devNum);
+  void Stop();
+  void Tune(cChannel *ch);
+  void AddPrg(int sid, int *epid);
+};
diff --git a/contrib/sasc-ng/sc/thread.cpp b/contrib/sasc-ng/sc/thread.cpp
new file mode 100644 (file)
index 0000000..b4fd456
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * thread.c: A simple thread base class
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: thread.c 1.45 2005/08/14 11:15:42 kls Exp $
+ */
+
+#include "include/vdr/thread.h"
+#include <errno.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "include/vdr/tools.h"
+
+static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+  struct timeval now;
+  if (gettimeofday(&now, NULL) == 0) {           // get current time
+     now.tv_usec += MillisecondsFromNow * 1000;  // add the timeout
+     while (now.tv_usec >= 1000000) {            // take care of an overflow
+           now.tv_sec++;
+           now.tv_usec -= 1000000;
+           }
+     Abstime->tv_sec = now.tv_sec;          // seconds
+     Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
+     return true;
+     }
+  return false;
+}
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+  signaled = false;
+  pthread_mutex_init(&mutex, NULL);
+  pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+  pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+  cCondWait w;
+  w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+  pthread_mutex_lock(&mutex);
+  if (!signaled) {
+     if (TimeoutMs) {
+        struct timespec abstime;
+        if (GetAbsTime(&abstime, TimeoutMs)) {
+           while (!signaled) {
+                 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+                    break;
+                 }
+           }
+        }
+     else
+        pthread_cond_wait(&cond, &mutex);
+     }
+  bool r = signaled;
+  signaled = false;
+  pthread_mutex_unlock(&mutex);
+  return r;
+}
+
+void cCondWait::Signal(void)
+{
+  pthread_mutex_lock(&mutex);
+  signaled = true;
+  pthread_cond_broadcast(&cond);
+  pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+  pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+  if (Mutex.locked) {
+     int locked = Mutex.locked;
+     Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
+                       // does an implizit unlock of the mutex
+     pthread_cond_wait(&cond, &Mutex.mutex);
+     Mutex.locked = locked;
+     }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+  bool r = true; // true = condition signaled false = timeout
+
+  if (Mutex.locked) {
+     struct timespec abstime;
+     if (GetAbsTime(&abstime, TimeoutMs)) {
+        int locked = Mutex.locked;
+        Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+                          // does an implizit unlock of the mutex.
+        if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
+           r = false;
+        Mutex.locked = locked;
+        }
+     }
+  return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+  pthread_cond_broadcast(&cond);
+}
+
+// --- cRwLock ---------------------------------------------------------------
+
+cRwLock::cRwLock(bool PreferWriter)
+{
+  pthread_rwlockattr_t attr;
+  pthread_rwlockattr_init(&attr);
+  pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
+  pthread_rwlock_init(&rwlock, &attr);
+}
+
+cRwLock::~cRwLock()
+{
+  pthread_rwlock_destroy(&rwlock);
+}
+
+bool cRwLock::Lock(bool Write, int TimeoutMs)
+{
+  int Result = 0;
+  struct timespec abstime;
+  if (TimeoutMs) {
+     if (!GetAbsTime(&abstime, TimeoutMs))
+        TimeoutMs = 0;
+     }
+  if (Write)
+     Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock);
+  else
+     Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock);
+  return Result == 0;
+}
+
+void cRwLock::Unlock(void)
+{
+  pthread_rwlock_unlock(&rwlock);
+}
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+  locked = 0;
+  pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+  pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+  pthread_mutex_lock(&mutex);
+  locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+    pthread_mutex_unlock(&mutex);
+}
+
+// --- cThread ---------------------------------------------------------------
+
+       cThread::cThread(const char *Description)
+       {
+         active = running = false;
+         childTid = 0;
+         description = NULL;
+         SetDescription(Description);
+       }
+
+       cThread::~cThread()
+       {
+         Cancel(); // just in case the derived class didn't call it
+         free(description);
+       }
+
+       void cThread::SetPriority(int Priority)
+       {
+         if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
+            LOG_ERROR;
+       }
+
+       void cThread::SetDescription(const char *Description, ...)
+       {
+         free(description);
+         description = NULL;
+         if (Description) {
+            va_list ap;
+            va_start(ap, Description);
+            vasprintf(&description, Description, ap);
+            va_end(ap);
+            }
+       }
+
+       void *cThread::StartThread(cThread *Thread)
+       {
+         if (Thread->description)
+            dsyslog("%s thread started (pid=%d, tid=%ld)", Thread->description, getpid(), pthread_self());
+         Thread->Action();
+         if (Thread->description)
+            dsyslog("%s thread ended (pid=%d, tid=%ld)", Thread->description, getpid(), pthread_self());
+         Thread->running = false;
+         Thread->active = false;
+         return NULL;
+       }
+
+       extern pthread_attr_t default_attr;
+       bool cThread::Start(void)
+       {
+         if (!active) {
+            active = running = true;
+            if (pthread_create(&childTid, &default_attr, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+               struct sched_param param;
+               memset(&param, 0, sizeof(param));
+               pthread_detach(childTid); // auto-reap
+               pthread_setschedparam(childTid, SCHED_RR, &param);
+               }
+            else {
+               LOG_ERROR;
+               active = running = false;
+               return false;
+               }
+            }
+         return true;
+       }
+
+       bool cThread::Active(void)
+       {
+         if (active) {
+            //
+            // Single UNIX Spec v2 says:
+            //
+            // The pthread_kill() function is used to request
+            // that a signal be delivered to the specified thread.
+            //
+            // As in kill(), if sig is zero, error checking is
+            // performed but no signal is actually sent.
+            //
+            int err;
+            if ((err = pthread_kill(childTid, 0)) != 0) {
+               if (err != ESRCH)
+                  LOG_ERROR;
+               childTid = 0;
+               active = running = false;
+               }
+            else
+               return true;
+            }
+         return false;
+       }
+
+       tThreadId cThread::ThreadId(void)
+       {
+         return syscall(__NR_gettid);
+       }
+
+       void cThread::Cancel(int WaitSeconds)
+       {
+         running = false;
+         if (active) {
+            if (WaitSeconds > 0) {
+               for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
+                   if (!Active())
+                      return;
+                   cCondWait::SleepMs(10);
+                   }
+               esyslog("ERROR: thread %ld won't end (waited %d seconds) - canceling it...", childTid, WaitSeconds);
+               }
+            pthread_cancel(childTid);
+            childTid = 0;
+            active = false;
+            }
+       }
+
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+  mutex = NULL;
+  locked = false;
+  Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+  if (mutex && locked)
+     mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+  if (Mutex && !mutex) {
+     mutex = Mutex;
+     Mutex->Lock();
+     locked = true;
+     return true;
+     }
+  return false;
+}
+
+// --- cThreadLock -----------------------------------------------------------
+
+cThreadLock::cThreadLock(cThread *Thread)
+{
+  thread = NULL;
+  locked = false;
+  Lock(Thread);
+}
+
+cThreadLock::~cThreadLock()
+{
+  if (thread && locked)
+     thread->Unlock();
+}
+
+bool cThreadLock::Lock(cThread *Thread)
+{
+  if (Thread && !thread) {
+     thread = Thread;
+     Thread->Lock();
+     locked = true;
+     return true;
+     }
+  return false;
+}
+
+// --- cPipe -----------------------------------------------------------------
+
+// cPipe::Open() and cPipe::Close() are based on code originally received from
+// Andreas Vitting <Andreas@huji.de>
+
+cPipe::cPipe(void)
+{
+  pid = -1;
+  f = NULL;
+}
+
+cPipe::~cPipe()
+{
+  Close();
+}
+
+bool cPipe::Open(const char *Command, const char *Mode)
+{
+  int fd[2];
+
+  if (pipe(fd) < 0) {
+     LOG_ERROR;
+     return false;
+     }
+  if ((pid = fork()) < 0) { // fork failed
+     LOG_ERROR;
+     close(fd[0]);
+     close(fd[1]);
+     return false;
+     }
+
+  const char *mode = "w";
+  int iopipe = 0;
+
+  if (pid > 0) { // parent process
+     if (strcmp(Mode, "r") == 0) {
+        mode = "r";
+        iopipe = 1;
+        }
+     close(fd[iopipe]);
+     f = fdopen(fd[1 - iopipe], mode);
+     if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
+        LOG_ERROR;
+        close(fd[1 - iopipe]);
+        }
+     return f != NULL;
+     }
+  else { // child process
+     int iofd = STDOUT_FILENO;
+     if (strcmp(Mode, "w") == 0) {
+        mode = "r";
+        iopipe = 1;
+        iofd = STDIN_FILENO;
+        }
+     close(fd[iopipe]);
+     if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
+        LOG_ERROR;
+        close(fd[1 - iopipe]);
+        _exit(-1);
+        }
+     else {
+        int MaxPossibleFileDescriptors = getdtablesize();
+        for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
+            close(i); //close all dup'ed filedescriptors
+        if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
+           LOG_ERROR_STR(Command);
+           close(fd[1 - iopipe]);
+           _exit(-1);
+           }
+        }
+     _exit(0);
+     }
+}
+
+int cPipe::Close(void)
+{
+  int ret = -1;
+
+  if (f) {
+     fclose(f);
+     f = NULL;
+     }
+
+  if (pid > 0) {
+     int status = 0;
+     int i = 5;
+     while (i > 0) {
+           ret = waitpid(pid, &status, WNOHANG);
+           if (ret < 0) {
+              if (errno != EINTR && errno != ECHILD) {
+                 LOG_ERROR;
+                 break;
+                 }
+              }
+           else if (ret == pid)
+              break;
+           i--;
+           cCondWait::SleepMs(100);
+           }
+     if (!i) {
+        kill(pid, SIGKILL);
+        ret = -1;
+        }
+     else if (ret == -1 || !WIFEXITED(status))
+        ret = -1;
+     pid = -1;
+     }
+
+  return ret;
+}
+
+// --- SystemExec ------------------------------------------------------------
+
+int SystemExec(const char *Command, bool Detached)
+{
+  pid_t pid;
+
+  if ((pid = fork()) < 0) { // fork failed
+     LOG_ERROR;
+     return -1;
+     }
+
+  if (pid > 0) { // parent process
+     int status = 0;
+     if (waitpid(pid, &status, 0) < 0) {
+        LOG_ERROR;
+        return -1;
+        }
+     return status;
+     }
+  else { // child process
+     if (Detached) {
+        // Fork again and let first child die - grandchild stays alive without parent
+        if (fork() > 0)
+           exit(0);
+        // Start a new session
+        pid_t sid = setsid();
+        if (sid < 0)
+           LOG_ERROR;
+        // close STDIN and re-open as /dev/null
+        int devnull = open("/dev/null", O_RDONLY);
+        if (devnull < 0 || dup2(devnull, 0) < 0)
+           LOG_ERROR;
+        }
+     int MaxPossibleFileDescriptors = getdtablesize();
+     for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
+         close(i); //close all dup'ed filedescriptors
+     if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
+        LOG_ERROR_STR(Command);
+        _exit(-1);
+        }
+     _exit(0);
+     }
+}
+
diff --git a/contrib/sasc-ng/sc/tools.cpp b/contrib/sasc-ng/sc/tools.cpp
new file mode 100644 (file)
index 0000000..1def15c
--- /dev/null
@@ -0,0 +1,1137 @@
+/*
+ * tools.c: Various tools
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: tools.c 1.97 2005/08/27 14:43:55 kls Exp $
+ */
+
+#include "include/vdr/tools.h"
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <time.h>
+#include <unistd.h>
+#include "include/vdr/i18n.h"
+#include "include/vdr/thread.h"
+
+int SysLogLevel = 3;
+
+#define MAXSYSLOGBUF 256
+
+void syslog_with_tid(int priority, const char *format, ...)
+{
+  va_list ap;
+  char fmt[MAXSYSLOGBUF];
+  snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format);
+  va_start(ap, format);
+  vsyslog(priority, fmt, ap);
+  va_end(ap);
+}
+
+int BCD2INT(int x)
+{
+  return ((1000000 * BCDCHARTOINT((x >> 24) & 0xFF)) +
+            (10000 * BCDCHARTOINT((x >> 16) & 0xFF)) +
+              (100 * BCDCHARTOINT((x >>  8) & 0xFF)) +
+                     BCDCHARTOINT( x        & 0xFF));
+}
+
+ssize_t safe_read(int filedes, void *buffer, size_t size)
+{
+  for (;;) {
+      ssize_t p = read(filedes, buffer, size);
+      if (p < 0 && errno == EINTR) {
+         dsyslog("EINTR while reading from file handle %d - retrying", filedes);
+         continue;
+         }
+      return p;
+      }
+}
+
+ssize_t safe_write(int filedes, const void *buffer, size_t size)
+{
+  ssize_t p = 0;
+  ssize_t written = size;
+  const unsigned char *ptr = (const unsigned char *)buffer;
+  while (size > 0) {
+        p = write(filedes, ptr, size);
+        if (p < 0) {
+           if (errno == EINTR) {
+              dsyslog("EINTR while writing to file handle %d - retrying", filedes);
+              continue;
+              }
+           break;
+           }
+        ptr  += p;
+        size -= p;
+        }
+  return p < 0 ? p : written;
+}
+
+void writechar(int filedes, char c)
+{
+  safe_write(filedes, &c, sizeof(c));
+}
+
+int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs)
+{
+  int written = 0;
+  while (Length > 0) {
+        int w = write(fd, Data + written, Length);
+        if (w > 0) {
+           Length -= w;
+           written += w;
+           }
+        else if (written > 0 && !FATALERRNO) {
+           // we've started writing, so we must finish it!
+           cTimeMs t;
+           cPoller Poller(fd, true);
+           Poller.Poll(RetryMs);
+           if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
+              break;
+           }
+        else
+           // nothing written yet (or fatal error), so we can just return the error code:
+           return w;
+        }
+  return written;
+}
+
+char *strcpyrealloc(char *dest, const char *src)
+{
+  if (src) {
+     int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
+     dest = (char *)realloc(dest, l);
+     if (dest)
+        strcpy(dest, src);
+     else
+        esyslog("ERROR: out of memory");
+     }
+  else {
+     free(dest);
+     dest = NULL;
+     }
+  return dest;
+}
+
+char *strn0cpy(char *dest, const char *src, size_t n)
+{
+  char *s = dest;
+  for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
+  *dest = 0;
+  return s;
+}
+
+char *strreplace(char *s, char c1, char c2)
+{
+  char *p = s;
+
+  while (p && *p) {
+        if (*p == c1)
+           *p = c2;
+        p++;
+        }
+  return s;
+}
+
+char *strreplace(char *s, const char *s1, const char *s2)
+{
+  char *p = strstr(s, s1);
+  if (p) {
+     int of = p - s;
+     int l  = strlen(s);
+     int l1 = strlen(s1);
+     int l2 = strlen(s2);
+     if (l2 > l1)
+        s = (char *)realloc(s, strlen(s) + l2 - l1 + 1);
+     if (l2 != l1)
+        memmove(s + of + l2, s + of + l1, l - of - l1 + 1);
+     strncpy(s + of, s2, l2);
+     }
+  return s;
+}
+
+char *skipspace(const char *s)
+{
+  while (*s && isspace(*s))
+        s++;
+  return (char *)s;
+}
+
+char *stripspace(char *s)
+{
+  if (s && *s) {
+     for (char *p = s + strlen(s) - 1; p >= s; p--) {
+         if (!isspace(*p))
+            break;
+         *p = 0;
+         }
+     }
+  return s;
+}
+
+char *compactspace(char *s)
+{
+  if (s && *s) {
+     char *t = stripspace(skipspace(s));
+     char *p = t;
+     while (p && *p) {
+           char *q = skipspace(p);
+           if (q - p > 1)
+              memmove(p + 1, q, strlen(q) + 1);
+           p++;
+           }
+     if (t != s)
+        memmove(s, t, strlen(t) + 1);
+     }
+  return s;
+}
+
+cString strescape(const char *s, const char *chars)
+{
+  char *buffer;
+  const char *p = s;
+  char *t = NULL;
+  while (*p) {
+        if (strchr(chars, *p)) {
+           if (!t) {
+              buffer = MALLOC(char, 2 * strlen(s) + 1);
+              t = buffer + (p - s);
+              s = strcpy(buffer, s);
+              }
+           *t++ = '\\';
+           }
+        if (t)
+           *t++ = *p;
+        p++;
+        }
+  if (t)
+     *t = 0;
+  return cString(s, t != NULL);
+}
+
+bool startswith(const char *s, const char *p)
+{
+  while (*p) {
+        if (*p++ != *s++)
+           return false;
+        }
+  return true;
+}
+
+bool endswith(const char *s, const char *p)
+{
+  const char *se = s + strlen(s) - 1;
+  const char *pe = p + strlen(p) - 1;
+  while (pe >= p) {
+        if (*pe-- != *se-- || (se < s && pe >= p))
+           return false;
+        }
+  return true;
+}
+
+bool isempty(const char *s)
+{
+  return !(s && *skipspace(s));
+}
+
+int numdigits(int n)
+{
+  char buf[16];
+  snprintf(buf, sizeof(buf), "%d", n);
+  return strlen(buf);
+}
+
+bool isnumber(const char *s)
+{
+  if (!*s)
+     return false;
+  while (*s) {
+        if (!isdigit(*s))
+           return false;
+        s++;
+        }
+  return true;
+}
+
+cString AddDirectory(const char *DirName, const char *FileName)
+{
+  char *buf;
+  asprintf(&buf, "%s/%s", DirName && *DirName ? DirName : ".", FileName);
+  return cString(buf, true);
+}
+
+cString itoa(int n)
+{
+  char buf[16];
+  snprintf(buf, sizeof(buf), "%d", n);
+  return buf;
+}
+
+int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
+{
+  if (UsedMB)
+     *UsedMB = 0;
+  int Free = 0;
+  struct statfs statFs;
+  if (statfs(Directory, &statFs) == 0) {
+     double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize;
+     if (UsedMB)
+        *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg);
+     Free = int(statFs.f_bavail / blocksPerMeg);
+     }
+  else
+     LOG_ERROR_STR(Directory);
+  return Free;
+}
+
+bool DirectoryOk(const char *DirName, bool LogErrors)
+{
+  struct stat ds;
+  if (stat(DirName, &ds) == 0) {
+     if (S_ISDIR(ds.st_mode)) {
+        if (access(DirName, R_OK | W_OK | X_OK) == 0)
+           return true;
+        else if (LogErrors)
+           esyslog("ERROR: can't access %s", DirName);
+        }
+     else if (LogErrors)
+        esyslog("ERROR: %s is not a directory", DirName);
+     }
+  else if (LogErrors)
+     LOG_ERROR_STR(DirName);
+  return false;
+}
+
+bool MakeDirs(const char *FileName, bool IsDirectory)
+{
+  bool result = true;
+  char *s = strdup(FileName);
+  char *p = s;
+  if (*p == '/')
+     p++;
+  while ((p = strchr(p, '/')) != NULL || IsDirectory) {
+        if (p)
+           *p = 0;
+        struct stat fs;
+        if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
+           dsyslog("creating directory %s", s);
+           if (mkdir(s, ACCESSPERMS) == -1) {
+              LOG_ERROR_STR(s);
+              result = false;
+              break;
+              }
+           }
+        if (p)
+           *p++ = '/';
+        else
+           break;
+        }
+  free(s);
+  return result;
+}
+
+bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
+{
+  struct stat st;
+  if (stat(FileName, &st) == 0) {
+     if (S_ISDIR(st.st_mode)) {
+        cReadDir d(FileName);
+        if (d.Ok()) {
+           struct dirent *e;
+           while ((e = d.Next()) != NULL) {
+                 if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
+                    char *buffer;
+                    asprintf(&buffer, "%s/%s", FileName, e->d_name);
+                    if (FollowSymlinks) {
+                       int size = strlen(buffer) * 2; // should be large enough
+                       char *l = MALLOC(char, size);
+                       int n = readlink(buffer, l, size);
+                       if (n < 0) {
+                          if (errno != EINVAL)
+                             LOG_ERROR_STR(buffer);
+                          }
+                       else if (n < size) {
+                          l[n] = 0;
+                          dsyslog("removing %s", l);
+                          if (remove(l) < 0)
+                             LOG_ERROR_STR(l);
+                          }
+                       else
+                          esyslog("ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
+                       free(l);
+                       }
+                    dsyslog("removing %s", buffer);
+                    if (remove(buffer) < 0)
+                       LOG_ERROR_STR(buffer);
+                    free(buffer);
+                    }
+                 }
+           }
+        else {
+           LOG_ERROR_STR(FileName);
+           return false;
+           }
+        }
+     dsyslog("removing %s", FileName);
+     if (remove(FileName) < 0) {
+        LOG_ERROR_STR(FileName);
+        return false;
+        }
+     }
+  else if (errno != ENOENT) {
+     LOG_ERROR_STR(FileName);
+     return false;
+     }
+  return true;
+}
+
+bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis)
+{
+  cReadDir d(DirName);
+  if (d.Ok()) {
+     bool empty = true;
+     struct dirent *e;
+     while ((e = d.Next()) != NULL) {
+           if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) {
+              char *buffer;
+              asprintf(&buffer, "%s/%s", DirName, e->d_name);
+              struct stat st;
+              if (stat(buffer, &st) == 0) {
+                 if (S_ISDIR(st.st_mode)) {
+                    if (!RemoveEmptyDirectories(buffer, true))
+                       empty = false;
+                    }
+                 else
+                    empty = false;
+                 }
+              else {
+                 LOG_ERROR_STR(buffer);
+                 free(buffer);
+                 return false;
+                 }
+              free(buffer);
+              }
+           }
+     if (RemoveThis && empty) {
+        dsyslog("removing %s", DirName);
+        if (remove(DirName) < 0) {
+           LOG_ERROR_STR(DirName);
+           return false;
+           }
+        }
+     return empty;
+     }
+  else
+     LOG_ERROR_STR(DirName);
+  return false;
+}
+
+char *ReadLink(const char *FileName)
+{
+  char RealName[PATH_MAX];
+  const char *TargetName = NULL;
+  int n = readlink(FileName, RealName, sizeof(RealName) - 1);
+  if (n < 0) {
+     if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink
+        TargetName = FileName;
+     else // some other error occurred
+        LOG_ERROR_STR(FileName);
+     }
+  else if (n < int(sizeof(RealName))) { // got it!
+     RealName[n] = 0;
+     TargetName = RealName;
+     }
+  else
+     esyslog("ERROR: symlink's target name too long: %s", FileName);
+  return TargetName ? strdup(TargetName) : NULL;
+}
+
+bool SpinUpDisk(const char *FileName)
+{
+  char *buf = NULL;
+  for (int n = 0; n < 10; n++) {
+      free(buf);
+      if (DirectoryOk(FileName))
+         asprintf(&buf, "%s/vdr-%06d", *FileName ? FileName : ".", n);
+      else
+         asprintf(&buf, "%s.vdr-%06d", FileName, n);
+      if (access(buf, F_OK) != 0) { // the file does not exist
+         timeval tp1, tp2;
+         gettimeofday(&tp1, NULL);
+         int f = open(buf, O_WRONLY | O_CREAT, DEFFILEMODE);
+         // O_SYNC doesn't work on all file systems
+         if (f >= 0) {
+            if (fdatasync(f) < 0)
+               LOG_ERROR_STR(buf);
+            close(f);
+            remove(buf);
+            gettimeofday(&tp2, NULL);
+            double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0;
+            if (seconds > 0.5)
+               dsyslog("SpinUpDisk took %.2f seconds\n", seconds);
+            free(buf);
+            return true;
+            }
+         else
+            LOG_ERROR_STR(buf);
+         }
+      }
+  free(buf);
+  esyslog("ERROR: SpinUpDisk failed");
+  return false;
+}
+
+time_t LastModifiedTime(const char *FileName)
+{
+  struct stat fs;
+  if (stat(FileName, &fs) == 0)
+     return fs.st_mtime;
+  return 0;
+}
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+  Set(Ms);
+}
+
+uint64_t cTimeMs::Now(void)
+{
+  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;
+}
+
+// --- cString ---------------------------------------------------------------
+
+cString::cString(const char *S, bool TakePointer)
+{
+  s = TakePointer ? (char *)S : S ? strdup(S) : NULL;
+}
+
+cString::~cString()
+{
+  free(s);
+}
+
+cString &cString::operator=(const cString &String)
+{
+  s = String.s ? strdup(String.s) : NULL;
+  return *this;
+}
+
+cString cString::sprintf(const char *fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  char *buffer;
+  vasprintf(&buffer, fmt, ap);
+  return cString(buffer, true);
+}
+
+cString WeekDayName(int WeekDay)
+{
+  char buffer[4];
+  WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with monday==0!
+  if (0 <= WeekDay && WeekDay <= 6) {
+     const char *day = "MonTueWedThuFriSatSun";
+     day += WeekDay * 3;
+     strn0cpy(buffer, day, sizeof(buffer));
+     return buffer;
+     }
+  else
+     return "???";
+}
+
+cString WeekDayName(time_t t)
+{
+  struct tm tm_r;
+  return WeekDayName(localtime_r(&t, &tm_r)->tm_wday);
+}
+
+cString DayDateTime(time_t t)
+{
+  char buffer[32];
+  if (t == 0)
+     time(&t);
+  struct tm tm_r;
+  tm *tm = localtime_r(&t, &tm_r);
+  snprintf(buffer, sizeof(buffer), "%s %2d.%02d %02d:%02d", *WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
+  return buffer;
+}
+
+cString TimeToString(time_t t)
+{
+  char buffer[32];
+  if (ctime_r(&t, buffer)) {
+     buffer[strlen(buffer) - 1] = 0; // strip trailing newline
+     return buffer;
+     }
+  return "???";
+}
+
+cString DateString(time_t t)
+{
+  char buf[32];
+  struct tm tm_r;
+  tm *tm = localtime_r(&t, &tm_r);
+  char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
+  *p++ = ' ';
+  strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
+  return buf;
+}
+
+cString TimeString(time_t t)
+{
+  char buf[25];
+  struct tm tm_r;
+  strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r));
+  return buf;
+}
+
+// --- cReadLine -------------------------------------------------------------
+
+char *cReadLine::Read(FILE *f)
+{
+  if (fgets(buffer, sizeof(buffer), f) > 0) {
+     int l = strlen(buffer) - 1;
+     if (l >= 0 && buffer[l] == '\n')
+        buffer[l] = 0;
+     return buffer;
+     }
+  return NULL;
+}
+
+// --- cPoller ---------------------------------------------------------------
+
+cPoller::cPoller(int FileHandle, bool Out)
+{
+  numFileHandles = 0;
+  Add(FileHandle, Out);
+}
+
+bool cPoller::Add(int FileHandle, bool Out)
+{
+  if (FileHandle >= 0) {
+     for (int i = 0; i < numFileHandles; i++) {
+         if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
+            return true;
+         }
+     if (numFileHandles < MaxPollFiles) {
+        pfd[numFileHandles].fd = FileHandle;
+        pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
+        pfd[numFileHandles].revents = 0;
+        numFileHandles++;
+        return true;
+        }
+     esyslog("ERROR: too many file handles in cPoller");
+     }
+  return false;
+}
+
+bool cPoller::Poll(int TimeoutMs)
+{
+  if (numFileHandles) {
+     if (poll(pfd, numFileHandles, TimeoutMs) != 0)
+        return true; // returns true even in case of an error, to let the caller
+                     // access the file and thus see the error code
+     }
+  return false;
+}
+
+// --- cReadDir --------------------------------------------------------------
+
+cReadDir::cReadDir(const char *Directory)
+{
+  directory = opendir(Directory);
+}
+
+cReadDir::~cReadDir()
+{
+  if (directory)
+     closedir(directory);
+}
+
+struct dirent *cReadDir::Next(void)
+{
+  return directory && readdir_r(directory, &u.d, &result) == 0 ? result : NULL;
+}
+
+// --- cFile -----------------------------------------------------------------
+
+bool cFile::files[FD_SETSIZE] = { false };
+int cFile::maxFiles = 0;
+
+cFile::cFile(void)
+{
+  f = -1;
+}
+
+cFile::~cFile()
+{
+  Close();
+}
+
+bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
+{
+  if (!IsOpen())
+     return Open(open(FileName, Flags, Mode));
+  esyslog("ERROR: attempt to re-open %s", FileName);
+  return false;
+}
+
+bool cFile::Open(int FileDes)
+{
+  if (FileDes >= 0) {
+     if (!IsOpen()) {
+        f = FileDes;
+        if (f >= 0) {
+           if (f < FD_SETSIZE) {
+              if (f >= maxFiles)
+                 maxFiles = f + 1;
+              if (!files[f])
+                 files[f] = true;
+              else
+                 esyslog("ERROR: file descriptor %d already in files[]", f);
+              return true;
+              }
+           else
+              esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
+           }
+        }
+     else
+        esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
+     }
+  return false;
+}
+
+void cFile::Close(void)
+{
+  if (f >= 0) {
+     close(f);
+     files[f] = false;
+     f = -1;
+     }
+}
+
+bool cFile::Ready(bool Wait)
+{
+  return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
+}
+
+bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  FD_ZERO(&set);
+  for (int i = 0; i < maxFiles; i++) {
+      if (files[i])
+         FD_SET(i, &set);
+      }
+  if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
+     FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
+  if (TimeoutMs == 0)
+     TimeoutMs = 10; // load gets too heavy with 0
+  struct timeval timeout;
+  timeout.tv_sec  = TimeoutMs / 1000;
+  timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+  return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
+}
+
+bool cFile::FileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs >= 0) {
+     if (TimeoutMs < 100)
+        TimeoutMs = 100;
+     timeout.tv_sec  = TimeoutMs / 1000;
+     timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+     }
+  return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
+}
+
+bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs < 100)
+     TimeoutMs = 100;
+  timeout.tv_sec  = 0;
+  timeout.tv_usec = TimeoutMs * 1000;
+  return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
+}
+
+// --- cSafeFile -------------------------------------------------------------
+
+cSafeFile::cSafeFile(const char *FileName)
+{
+  f = NULL;
+  fileName = ReadLink(FileName);
+  tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
+  if (tempName)
+     strcat(strcpy(tempName, fileName), ".$$$");
+}
+
+cSafeFile::~cSafeFile()
+{
+  if (f)
+     fclose(f);
+  unlink(tempName);
+  free(fileName);
+  free(tempName);
+}
+
+bool cSafeFile::Open(void)
+{
+  if (!f && fileName && tempName) {
+     f = fopen(tempName, "w");
+     if (!f)
+        LOG_ERROR_STR(tempName);
+     }
+  return f != NULL;
+}
+
+bool cSafeFile::Close(void)
+{
+  bool result = true;
+  if (f) {
+     if (ferror(f) != 0) {
+        LOG_ERROR_STR(tempName);
+        result = false;
+        }
+     if (fclose(f) < 0) {
+        LOG_ERROR_STR(tempName);
+        result = false;
+        }
+     f = NULL;
+     if (result && rename(tempName, fileName) < 0) {
+        LOG_ERROR_STR(fileName);
+        result = false;
+        }
+     }
+  else
+     result = false;
+  return result;
+}
+
+// --- cLockFile -------------------------------------------------------------
+
+#define LOCKFILENAME      ".lock-vdr"
+#define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale"
+
+cLockFile::cLockFile(const char *Directory)
+{
+  fileName = NULL;
+  f = -1;
+  if (DirectoryOk(Directory))
+     asprintf(&fileName, "%s/%s", Directory, LOCKFILENAME);
+}
+
+cLockFile::~cLockFile()
+{
+  Unlock();
+  free(fileName);
+}
+
+bool cLockFile::Lock(int WaitSeconds)
+{
+  if (f < 0 && fileName) {
+     time_t Timeout = time(NULL) + WaitSeconds;
+     do {
+        f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE);
+        if (f < 0) {
+           if (errno == EEXIST) {
+              struct stat fs;
+              if (stat(fileName, &fs) == 0) {
+                 if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) {
+                    esyslog("ERROR: removing stale lock file '%s'", fileName);
+                    if (remove(fileName) < 0) {
+                       LOG_ERROR_STR(fileName);
+                       break;
+                       }
+                    continue;
+                    }
+                 }
+              else if (errno != ENOENT) {
+                 LOG_ERROR_STR(fileName);
+                 break;
+                 }
+              }
+           else {
+              LOG_ERROR_STR(fileName);
+              break;
+              }
+           if (WaitSeconds)
+              sleep(1);
+           }
+        } while (f < 0 && time(NULL) < Timeout);
+     }
+  return f >= 0;
+}
+
+void cLockFile::Unlock(void)
+{
+  if (f >= 0) {
+     close(f);
+     remove(fileName);
+     f = -1;
+     }
+}
+
+// --- 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]);
+      }
+}
+
+// --- cHashBase -------------------------------------------------------------
+
+cHashBase::cHashBase(int Size)
+{
+  size = Size;
+  hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*));
+}
+
+cHashBase::~cHashBase(void)
+{
+  for (int i = 0; i < size; i++)
+      delete hashTable[i];
+  free(hashTable);
+}
+
+void cHashBase::Add(cListObject *Object, unsigned int Id)
+{
+  unsigned int hash = hashfn(Id);
+  if (!hashTable[hash])
+     hashTable[hash] = new cList<cHashObject>;
+  hashTable[hash]->Add(new cHashObject(Object, Id));
+}
+
+void cHashBase::Del(cListObject *Object, unsigned int Id)
+{
+  cList<cHashObject> *list = hashTable[hashfn(Id)];
+  if (list) {
+     for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
+         if (hob->object == Object) {
+            list->Del(hob);
+            break;
+            }
+         }
+     }
+}
+
+cListObject *cHashBase::Get(unsigned int Id) const
+{
+  cList<cHashObject> *list = hashTable[hashfn(Id)];
+  if (list) {
+     for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
+         if (hob->id == Id)
+            return hob->object;
+         }
+     }
+  return NULL;
+}
+
+cList<cHashObject> *cHashBase::GetList(unsigned int Id) const
+{
+  return hashTable[hashfn(Id)];
+}
diff --git a/contrib/sasc-ng/sc/vdrcompat.cpp b/contrib/sasc-ng/sc/vdrcompat.cpp
new file mode 100644 (file)
index 0000000..4a88447
--- /dev/null
@@ -0,0 +1,158 @@
+#include "include/vdr/plugin.h"
+#include "include/vdr/interface.h"
+#include "include/vdr/status.h"
+#include "include/vdr/skins.h"
+#include "include/vdr/channels.h"
+//#include "include/vdr/dvbci.h"
+#include "include/vdr/menu.h"
+#include "include/vdr/config.h"
+#include <ctype.h>
+
+cSetup Setup;
+
+const char *I18nTranslate(const char *s, const char *Plugin) {
+  return s;
+}
+
+cSetup::cSetup(void) {}
+void cSetup::Store(const char *Name, const char *Value, const char *Plugin, bool AllowMultiple) {}
+bool cSetup::Save(void) { return true;}
+
+cPlugin::cPlugin() {}
+cPlugin::~cPlugin() {}
+const char* cPlugin::CommandLineHelp() {return NULL;}
+bool cPlugin::ProcessArgs(int count, char**args) {return true;}
+bool cPlugin::Initialize(void) {return true;}
+bool cPlugin::Start(void) {return true;}
+void cPlugin::Stop(void) {}
+void cPlugin::Housekeeping(void) {}
+void cPlugin::MainThreadHook(void) {}
+cString cPlugin::Active(void) {return NULL;}
+time_t cPlugin::WakeupTime(void) {return 0;}
+const char *cPlugin::MainMenuEntry(void) {return NULL;}
+cOsdObject *cPlugin::MainMenuAction(void) {return NULL;}
+cMenuSetupPage *cPlugin::SetupMenu(void) {return NULL;}
+bool cPlugin::SetupParse(const char *Name, const char *Value) {return true;}
+void cPlugin::SetupStore(const char *Name, const char *Value) {}
+void cPlugin::SetupStore(const char *Name, int Value) {}
+void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases){}
+bool cPlugin::Service(const char *Id, void *Data) {return true;}
+const char **cPlugin::SVDRPHelpPages(void) {return NULL;}
+cString cPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) {return NULL;}
+void cPlugin::SetConfigDirectory(const char *Dir) {}
+const char cPlugin::*ConfigDirectory(const char *PluginName) {return NULL;}
+
+bool cInterface::Confirm(char const*, int, bool)  {return true;}
+
+//cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) {return NULL;}
+
+cSkins::cSkins() {}
+cSkins::~cSkins() {}
+eKeys cSkins::Message(eMessageType, char const*, int)  { return kNone;}
+void cSkins::Clear(void) {}
+
+cChannels::cChannels() {
+}
+
+void cStatus::MsgOsdCurrentItem(char const* c) {}
+
+/*
+cCaDefinitions CaDefinitions;
+
+const cCaDefinition *cCaDefinitions::Get(int Number)
+{
+  cCaDefinition *p = First();
+  while (p) {
+        if (p->Number() == Number)
+           return p;
+        p = (cCaDefinition *)p->Next();
+        }
+  return NULL;
+}
+*/
+cSkins Skins;
+cChannels Channels;
+cInterface *Interface;
+
+//const cCaDefinition *caDefinitions::Get(int Number) {return NULL;}
+
+cString cSource::ToString(int Code)
+{
+  char buffer[16];
+  char *q = buffer;
+  switch (Code & st_Mask) {
+    case stCable: *q++ = 'C'; break;
+    case stSat:   *q++ = 'S';
+                  {
+                    int pos = Code & ~st_Mask;
+                    q += snprintf(q, sizeof(buffer) - 2, "%u.%u", (pos & ~st_Neg) / 10, (pos & ~st_Neg) % 10); // can't simply use "%g" here since the silly 'locale' messes up the decimal point
+                    *q++ = (Code & st_Neg) ? 'E' : 'W';
+                  }
+                  break;
+    case stTerr:  *q++ = 'T'; break;
+    default:      *q++ = Code + '0'; // backward compatibility
+    }
+  *q = 0;
+  return buffer;
+}
+
+int cSource::FromString(const char *s)
+{
+  int type = stNone;
+  switch (toupper(*s)) {
+    case 'C': type = stCable; break;
+    case 'S': type = stSat;   break;
+    case 'T': type = stTerr;  break;
+    case '0' ... '9': type = *s - '0'; break; // backward compatibility
+    default: esyslog("ERROR: unknown source key '%c'", *s);
+             return stNone;
+    }
+  int code = type;
+  if (type == stSat) {
+     int pos = 0;
+     bool dot = false;
+     bool neg = false;
+     while (*++s) {
+           switch (toupper(*s)) {
+             case '0' ... '9': pos *= 10;
+                               pos += *s - '0';
+                               break;
+             case '.':         dot = true;
+                               break;
+             case 'E':         neg = true; // fall through to 'W'
+             case 'W':         if (!dot)
+                                  pos *= 10;
+                               break;
+             default: esyslog("ERROR: unknown source character '%c'", *s);
+                      return stNone;
+             }
+           }
+     if (neg)
+        pos |= st_Neg;
+     code |= pos;
+     }
+  return code;
+}
+
+
+cMenuText::cMenuText(const char *Title, const char *Text, eDvbFont Font)
+:cOsdMenu(Title)
+{
+}
+
+cMenuText::~cMenuText()
+{
+}
+
+void cMenuText::SetText(char const *Text)
+{
+}
+
+void cMenuText::Display(void)
+{
+}
+
+eOSState cMenuText::ProcessKey(eKeys Key)
+{
+  return osContinue;
+}