From 008ab97553fccff58bc80f622552bdcf5693269d Mon Sep 17 00:00:00 2001 From: leslie Date: Thu, 27 Dec 2007 21:58:46 +0100 Subject: [PATCH] release 0.8.0 --- COPYING | 340 +++ FFdecsa/COPYING | 339 +++ FFdecsa/ChangeLog | 206 ++ FFdecsa/FFdecsa.c | 878 +++++++ FFdecsa/FFdecsa.h | 62 + FFdecsa/FFdecsa_test.c | 174 ++ FFdecsa/FFdecsa_test_testcases.h | 279 +++ FFdecsa/Makefile | 55 + FFdecsa/README | 50 + FFdecsa/docs/FAQ.txt | 77 + FFdecsa/docs/how_to_compile.txt | 114 + FFdecsa/docs/how_to_release.txt | 21 + FFdecsa/docs/how_to_understand.txt | 15 + FFdecsa/docs/how_to_use.txt | 239 ++ FFdecsa/docs/technical_background.txt | 341 +++ FFdecsa/fftable.h | 55 + FFdecsa/logic/Makefile | 10 + FFdecsa/logic/logic.c | 330 +++ FFdecsa/parallel_032_4char.h | 206 ++ FFdecsa/parallel_032_4charA.h | 171 ++ FFdecsa/parallel_032_int.h | 55 + FFdecsa/parallel_064_2int.h | 175 ++ FFdecsa/parallel_064_8char.h | 274 ++ FFdecsa/parallel_064_8charA.h | 171 ++ FFdecsa/parallel_064_long.h | 39 + FFdecsa/parallel_064_mmx.h | 83 + FFdecsa/parallel_128_16char.h | 411 +++ FFdecsa/parallel_128_16charA.h | 172 ++ FFdecsa/parallel_128_2long.h | 175 ++ FFdecsa/parallel_128_2mmx.h | 201 ++ FFdecsa/parallel_128_4int.h | 207 ++ FFdecsa/parallel_128_sse.h | 95 + FFdecsa/parallel_128_sse2.h | 82 + FFdecsa/parallel_generic.h | 102 + FFdecsa/parallel_std_def.h | 29 + FFdecsa/stream.c | 906 +++++++ HISTORY | 846 +++++++ Makefile | 191 ++ Makefile.system | 96 + README | 345 +++ README.FFdecsa | 81 + cam.c | 2905 ++++++++++++++++++++++ cam.h | 178 ++ common.h | 40 + crypto-bn.h | 54 + crypto.c | 415 ++++ crypto.h | 136 + data.c | 832 +++++++ data.h | 271 ++ diff.exclude | 12 + examples/Ird-Beta.KID | 12 + examples/Seca.KID | 9 + examples/SoftCam.Key | 108 + examples/Viaccess.KID | 11 + examples/cardclient.conf.example | 48 + examples/dialup.sh.example | 24 + examples/externalau.sh.example | 8 + examples/smartcard.conf.example | 70 + filter.c | 292 +++ filter.h | 83 + helper.h | 95 + i18n.c | 780 ++++++ i18n.h | 27 + log-core.h | 54 + log-sc.h | 32 + log-sys.h | 35 + log.c | 395 +++ log.h | 123 + misc.c | 288 +++ misc.h | 107 + network.c | 353 +++ network.h | 66 + openssl-compat.h | 31 + opts.h | 117 + parse.c | 805 ++++++ parse.h | 317 +++ patches/dvb-cwidx-old.diff | 75 + patches/dvb-cwidx.diff | 93 + patches/dvb-sct-cc.diff | 13 + patches/vdr-1.4.x-sc7.diff | 272 ++ sc.c | 1428 +++++++++++ sc.h | 40 + scsetup.h | 45 + smartcard.c | 1472 +++++++++++ smartcard.h | 235 ++ support/aes.h | 83 + support/aes_core.c | 1425 +++++++++++ support/i_cbc.c | 168 ++ support/i_skey.c | 156 ++ support/idea.h | 103 + support/idea_lcl.h | 215 ++ system-common.c | 195 ++ system-common.h | 108 + system.c | 618 +++++ system.h | 276 ++ systems/cardclient/aroureos.c | 124 + systems/cardclient/camd.c | 705 ++++++ systems/cardclient/cc.c | 320 +++ systems/cardclient/cc.h | 99 + systems/cardclient/cc.mk | 6 + systems/cardclient/gbox.c | 173 ++ systems/cardclient/newcamd.c | 518 ++++ systems/cardclient/radegast.c | 309 +++ systems/conax/conax.c | 191 ++ systems/conax/conax.mk | 5 + systems/constcw/constcw.c | 145 ++ systems/constcw/constcw.mk | 5 + systems/cryptoworks/cryptoworks.c | 559 +++++ systems/cryptoworks/cryptoworks.mk | 5 + systems/irdeto/irdeto.c | 514 ++++ systems/irdeto/irdeto.mk | 5 + systems/nagra/cpu.c | 1048 ++++++++ systems/nagra/cpu.h | 157 ++ systems/nagra/log-nagra.h | 37 + systems/nagra/nagra.c | 227 ++ systems/nagra/nagra.h | 83 + systems/nagra/nagra.mk | 12 + systems/nagra/nagra1.c | 1005 ++++++++ systems/nagra/nagra2-0101.c | 324 +++ systems/nagra/nagra2-0501.c | 99 + systems/nagra/nagra2-1101.c | 29 + systems/nagra/nagra2-4101.c | 47 + systems/nagra/nagra2-map57.c | 677 +++++ systems/nagra/nagra2.c | 859 +++++++ systems/sc-conax/sc-conax.c | 357 +++ systems/sc-conax/sc-conax.mk | 5 + systems/sc-cryptoworks/sc-cryptoworks.c | 607 +++++ systems/sc-cryptoworks/sc-cryptoworks.mk | 5 + systems/sc-irdeto/sc-irdeto.c | 710 ++++++ systems/sc-irdeto/sc-irdeto.mk | 6 + systems/sc-nagra/sc-nagra.c | 569 +++++ systems/sc-nagra/sc-nagra.mk | 5 + systems/sc-seca/sc-seca.c | 432 ++++ systems/sc-seca/sc-seca.mk | 5 + systems/sc-viaccess/sc-viaccess.c | 361 +++ systems/sc-viaccess/sc-viaccess.mk | 5 + systems/seca/seca.c | 1657 ++++++++++++ systems/seca/seca.mk | 6 + systems/shl/shl.c | 656 +++++ systems/shl/shl.mk | 6 + systems/viaccess/log-viaccess.h | 34 + systems/viaccess/opentv.h | 57 + systems/viaccess/st20.c | 390 +++ systems/viaccess/st20.h | 82 + systems/viaccess/tps.c | 1175 +++++++++ systems/viaccess/tps.h | 137 + systems/viaccess/viaccess.c | 539 ++++ systems/viaccess/viaccess.h | 30 + systems/viaccess/viaccess.mk | 5 + testing/Makefile | 66 + testing/compat.c | 457 ++++ testing/compat.h | 6 + testing/filterhelper.c | 117 + testing/testECM.c | 33 + testing/testEMM.c | 38 + testing/testN1Emu.c | 138 + testing/testN2Emu.c | 172 ++ version.h | 29 + 158 files changed, 42035 insertions(+) create mode 100644 COPYING create mode 100644 FFdecsa/COPYING create mode 100644 FFdecsa/ChangeLog create mode 100644 FFdecsa/FFdecsa.c create mode 100644 FFdecsa/FFdecsa.h create mode 100644 FFdecsa/FFdecsa_test.c create mode 100644 FFdecsa/FFdecsa_test_testcases.h create mode 100644 FFdecsa/Makefile create mode 100644 FFdecsa/README create mode 100644 FFdecsa/docs/FAQ.txt create mode 100644 FFdecsa/docs/how_to_compile.txt create mode 100644 FFdecsa/docs/how_to_release.txt create mode 100644 FFdecsa/docs/how_to_understand.txt create mode 100644 FFdecsa/docs/how_to_use.txt create mode 100644 FFdecsa/docs/technical_background.txt create mode 100644 FFdecsa/fftable.h create mode 100644 FFdecsa/logic/Makefile create mode 100644 FFdecsa/logic/logic.c create mode 100644 FFdecsa/parallel_032_4char.h create mode 100644 FFdecsa/parallel_032_4charA.h create mode 100644 FFdecsa/parallel_032_int.h create mode 100644 FFdecsa/parallel_064_2int.h create mode 100644 FFdecsa/parallel_064_8char.h create mode 100644 FFdecsa/parallel_064_8charA.h create mode 100644 FFdecsa/parallel_064_long.h create mode 100644 FFdecsa/parallel_064_mmx.h create mode 100644 FFdecsa/parallel_128_16char.h create mode 100644 FFdecsa/parallel_128_16charA.h create mode 100644 FFdecsa/parallel_128_2long.h create mode 100644 FFdecsa/parallel_128_2mmx.h create mode 100644 FFdecsa/parallel_128_4int.h create mode 100644 FFdecsa/parallel_128_sse.h create mode 100644 FFdecsa/parallel_128_sse2.h create mode 100644 FFdecsa/parallel_generic.h create mode 100644 FFdecsa/parallel_std_def.h create mode 100644 FFdecsa/stream.c create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 Makefile.system create mode 100644 README create mode 100644 README.FFdecsa create mode 100644 cam.c create mode 100644 cam.h create mode 100644 common.h create mode 100644 crypto-bn.h create mode 100644 crypto.c create mode 100644 crypto.h create mode 100644 data.c create mode 100644 data.h create mode 100644 diff.exclude create mode 100644 examples/Ird-Beta.KID create mode 100644 examples/Seca.KID create mode 100644 examples/SoftCam.Key create mode 100644 examples/Viaccess.KID create mode 100644 examples/cardclient.conf.example create mode 100755 examples/dialup.sh.example create mode 100755 examples/externalau.sh.example create mode 100644 examples/smartcard.conf.example create mode 100644 filter.c create mode 100644 filter.h create mode 100644 helper.h create mode 100644 i18n.c create mode 100644 i18n.h create mode 100644 log-core.h create mode 100644 log-sc.h create mode 100644 log-sys.h create mode 100644 log.c create mode 100644 log.h create mode 100644 misc.c create mode 100644 misc.h create mode 100644 network.c create mode 100644 network.h create mode 100644 openssl-compat.h create mode 100644 opts.h create mode 100644 parse.c create mode 100644 parse.h create mode 100644 patches/dvb-cwidx-old.diff create mode 100644 patches/dvb-cwidx.diff create mode 100644 patches/dvb-sct-cc.diff create mode 100644 patches/vdr-1.4.x-sc7.diff create mode 100644 sc.c create mode 100644 sc.h create mode 100644 scsetup.h create mode 100644 smartcard.c create mode 100644 smartcard.h create mode 100644 support/aes.h create mode 100644 support/aes_core.c create mode 100644 support/i_cbc.c create mode 100644 support/i_skey.c create mode 100644 support/idea.h create mode 100644 support/idea_lcl.h create mode 100644 system-common.c create mode 100644 system-common.h create mode 100644 system.c create mode 100644 system.h create mode 100644 systems/cardclient/aroureos.c create mode 100644 systems/cardclient/camd.c create mode 100644 systems/cardclient/cc.c create mode 100644 systems/cardclient/cc.h create mode 100644 systems/cardclient/cc.mk create mode 100644 systems/cardclient/gbox.c create mode 100644 systems/cardclient/newcamd.c create mode 100644 systems/cardclient/radegast.c create mode 100644 systems/conax/conax.c create mode 100644 systems/conax/conax.mk create mode 100644 systems/constcw/constcw.c create mode 100644 systems/constcw/constcw.mk create mode 100644 systems/cryptoworks/cryptoworks.c create mode 100644 systems/cryptoworks/cryptoworks.mk create mode 100644 systems/irdeto/irdeto.c create mode 100644 systems/irdeto/irdeto.mk create mode 100644 systems/nagra/cpu.c create mode 100644 systems/nagra/cpu.h create mode 100644 systems/nagra/log-nagra.h create mode 100644 systems/nagra/nagra.c create mode 100644 systems/nagra/nagra.h create mode 100644 systems/nagra/nagra.mk create mode 100644 systems/nagra/nagra1.c create mode 100644 systems/nagra/nagra2-0101.c create mode 100644 systems/nagra/nagra2-0501.c create mode 100644 systems/nagra/nagra2-1101.c create mode 100644 systems/nagra/nagra2-4101.c create mode 100644 systems/nagra/nagra2-map57.c create mode 100644 systems/nagra/nagra2.c create mode 100644 systems/sc-conax/sc-conax.c create mode 100644 systems/sc-conax/sc-conax.mk create mode 100644 systems/sc-cryptoworks/sc-cryptoworks.c create mode 100644 systems/sc-cryptoworks/sc-cryptoworks.mk create mode 100644 systems/sc-irdeto/sc-irdeto.c create mode 100644 systems/sc-irdeto/sc-irdeto.mk create mode 100644 systems/sc-nagra/sc-nagra.c create mode 100644 systems/sc-nagra/sc-nagra.mk create mode 100644 systems/sc-seca/sc-seca.c create mode 100644 systems/sc-seca/sc-seca.mk create mode 100644 systems/sc-viaccess/sc-viaccess.c create mode 100644 systems/sc-viaccess/sc-viaccess.mk create mode 100644 systems/seca/seca.c create mode 100644 systems/seca/seca.mk create mode 100644 systems/shl/shl.c create mode 100644 systems/shl/shl.mk create mode 100644 systems/viaccess/log-viaccess.h create mode 100644 systems/viaccess/opentv.h create mode 100644 systems/viaccess/st20.c create mode 100644 systems/viaccess/st20.h create mode 100644 systems/viaccess/tps.c create mode 100644 systems/viaccess/tps.h create mode 100644 systems/viaccess/viaccess.c create mode 100644 systems/viaccess/viaccess.h create mode 100644 systems/viaccess/viaccess.mk create mode 100644 testing/Makefile create mode 100644 testing/compat.c create mode 100644 testing/compat.h create mode 100644 testing/filterhelper.c create mode 100644 testing/testECM.c create mode 100644 testing/testEMM.c create mode 100644 testing/testN1Emu.c create mode 100644 testing/testN2Emu.c create mode 100644 version.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -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. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + , 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/FFdecsa/COPYING b/FFdecsa/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/FFdecsa/COPYING @@ -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. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + 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. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. + + , 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/FFdecsa/ChangeLog b/FFdecsa/ChangeLog new file mode 100644 index 0000000..d1c4f0c --- /dev/null +++ b/FFdecsa/ChangeLog @@ -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/FFdecsa/FFdecsa.c b/FFdecsa/FFdecsa.c new file mode 100644 index 0000000..8ba7322 --- /dev/null +++ b/FFdecsa/FFdecsa.c @@ -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 +#include +#include +#include + +#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>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>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>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>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>16) | (b&0xffff0000) ; + } + } + } +//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM); +// now 01230123 + for(g=0;g=0;i--){ + { + MEMALIGN batch tkkmulti=kkmulti[i]; + batch *si=(batch *)sbox_in; + batch *r6_N=(batch *)(r+roff+GROUP_PARALLELISM*6); + for(g=0;gck,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<iA_g[by][bi]=(key->iA[by]&(1<iB_g[by][bi]=(key->iB[by]&(1<ck,key->kk); + for(i=0;i<56;i++){ + for(j=0;jkkmulti[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=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;ig_n[i]){ + g_swap(i,j); + } + } + } + DBG(fprintf(stderr,"POSTSORTING\n")); + for(i=0;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>>>>ITER 0\n")); + iter=0; + stream_cypher_group_init(®s,k->iA_g,k->iB_g,stream_in); + // fill first ib + for(g=0;g0;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(®s,stream_out); +//dump_mem("stream_out",stream_out,GROUP_PARALLELISM*8,BYPG); + + // alive packets: calc ib + for(g=0;g>>>>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 +#include +#include + +#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;i100%, 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/FFdecsa/docs/FAQ.txt b/FFdecsa/docs/FAQ.txt new file mode 100644 index 0000000..2d46f06 --- /dev/null +++ b/FFdecsa/docs/FAQ.txt @@ -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/FFdecsa/docs/how_to_compile.txt b/FFdecsa/docs/how_to_compile.txt new file mode 100644 index 0000000..4f8c141 --- /dev/null +++ b/FFdecsa/docs/how_to_compile.txt @@ -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/FFdecsa/docs/how_to_release.txt b/FFdecsa/docs/how_to_release.txt new file mode 100644 index 0000000..923a61b --- /dev/null +++ b/FFdecsa/docs/how_to_release.txt @@ -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/FFdecsa/docs/how_to_understand.txt b/FFdecsa/docs/how_to_understand.txt new file mode 100644 index 0000000..4b3f2f1 --- /dev/null +++ b/FFdecsa/docs/how_to_understand.txt @@ -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/FFdecsa/docs/how_to_use.txt b/FFdecsa/docs/how_to_use.txt new file mode 100644 index 0000000..46fedd3 --- /dev/null +++ b/FFdecsa/docs/how_to_use.txt @@ -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;pslice 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/FFdecsa/fftable.h b/FFdecsa/fftable.h new file mode 100644 index 0000000..ed6345f --- /dev/null +++ b/FFdecsa/fftable.h @@ -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 + + +/* + * 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<=0;j--){ + printf("%i",i&(1<=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; +} + + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_032_4charA.h b/FFdecsa/parallel_032_4charA.h new file mode 100644 index 0000000..a8f295b --- /dev/null +++ b/FFdecsa/parallel_032_4charA.h @@ -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=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; +} + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_032_int.h b/FFdecsa/parallel_032_int.h new file mode 100644 index 0000000..a21fe31 --- /dev/null +++ b/FFdecsa/parallel_032_int.h @@ -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=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/FFdecsa/parallel_064_2int.h b/FFdecsa/parallel_064_2int.h new file mode 100644 index 0000000..ffe331a --- /dev/null +++ b/FFdecsa/parallel_064_2int.h @@ -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; + res.s2=a.s2>>n; + return res; +} + + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_064_8char.h b/FFdecsa/parallel_064_8char.h new file mode 100644 index 0000000..956c980 --- /dev/null +++ b/FFdecsa/parallel_064_8char.h @@ -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; + 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/FFdecsa/parallel_064_8charA.h b/FFdecsa/parallel_064_8charA.h new file mode 100644 index 0000000..b99490b --- /dev/null +++ b/FFdecsa/parallel_064_8charA.h @@ -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; + return res; +} + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_064_long.h b/FFdecsa/parallel_064_long.h new file mode 100644 index 0000000..09f7b95 --- /dev/null +++ b/FFdecsa/parallel_064_long.h @@ -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/FFdecsa/parallel_064_mmx.h b/FFdecsa/parallel_064_mmx.h new file mode 100644 index 0000000..3979233 --- /dev/null +++ b/FFdecsa/parallel_064_mmx.h @@ -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 + +#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/FFdecsa/parallel_128_16char.h b/FFdecsa/parallel_128_16char.h new file mode 100644 index 0000000..ed28c61 --- /dev/null +++ b/FFdecsa/parallel_128_16char.h @@ -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; + 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/FFdecsa/parallel_128_16charA.h b/FFdecsa/parallel_128_16charA.h new file mode 100644 index 0000000..2a0daa1 --- /dev/null +++ b/FFdecsa/parallel_128_16charA.h @@ -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; + return res; +} + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_128_2long.h b/FFdecsa/parallel_128_2long.h new file mode 100644 index 0000000..1a3bdd9 --- /dev/null +++ b/FFdecsa/parallel_128_2long.h @@ -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; + res.s2=a.s2>>n; + return res; +} + + +void static inline M_EMPTY(void){ +} diff --git a/FFdecsa/parallel_128_2mmx.h b/FFdecsa/parallel_128_2mmx.h new file mode 100644 index 0000000..4afb7a7 --- /dev/null +++ b/FFdecsa/parallel_128_2mmx.h @@ -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 + +#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; + 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/FFdecsa/parallel_128_sse.h b/FFdecsa/parallel_128_sse.h new file mode 100644 index 0000000..a26e6b3 --- /dev/null +++ b/FFdecsa/parallel_128_sse.h @@ -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 + +#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/FFdecsa/parallel_128_sse2.h b/FFdecsa/parallel_128_sse2.h new file mode 100644 index 0000000..5a537a9 --- /dev/null +++ b/FFdecsa/parallel_128_sse2.h @@ -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 + +#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/FFdecsa/parallel_generic.h b/FFdecsa/parallel_generic.h new file mode 100644 index 0000000..2af4c1c --- /dev/null +++ b/FFdecsa/parallel_generic.h @@ -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/FFdecsa/parallel_std_def.h b/FFdecsa/parallel_std_def.h new file mode 100644 index 0000000..10517d4 --- /dev/null +++ b/FFdecsa/parallel_std_def.h @@ -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/FFdecsa/stream.c b/FFdecsa/stream.c new file mode 100644 index 0000000..1bda852 --- /dev/null +++ b/FFdecsa/stream.c @@ -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 *)®s->A[aboff+0][dbg],BYPG,BYPG)); + DBG(fprintf(stderr,"dbg B0[%i]=",dbg)); + DBG(dump_mem("",(unsigned char *)®s->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 *)®s->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 *)®s->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 *)®s->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 *)®s->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 *)®s->F[dbg],BYPG,BYPG)); +} +DBG(fprintf(stderr,"r=")); +DBG(dump_mem("",(unsigned char *)®s->r,BYPG,BYPG)); +for(dbg=0;dbg<4;dbg++){ + DBG(fprintf(stderr,"E[%i]=",dbg)); + DBG(dump_mem("",(unsigned char *)®s->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 *)®s->X[dbg],BYPG,BYPG)); +} +for(dbg=0;dbg<4;dbg++){ + DBG(fprintf(stderr,"Y[%i]=",dbg)); + DBG(dump_mem("",(unsigned char *)®s->Y[dbg],BYPG,BYPG)); +} +for(dbg=0;dbg<4;dbg++){ + DBG(fprintf(stderr,"Z[%i]=",dbg)); + DBG(dump_mem("",(unsigned char *)®s->Z[dbg],BYPG,BYPG)); +} +DBG(fprintf(stderr,"p=")); +DBG(dump_mem("",(unsigned char *)®s->p,BYPG,BYPG)); +DBG(fprintf(stderr,"q=")); +DBG(dump_mem("",(unsigned char *)®s->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/HISTORY b/HISTORY new file mode 100644 index 0000000..2fcc38e --- /dev/null +++ b/HISTORY @@ -0,0 +1,846 @@ +VDR Plugin 'sc' Revision History +-------------------------------- + +22.06.2007: Version 0.8.0 +- 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/Makefile b/Makefile new file mode 100644 index 0000000..facf6ed --- /dev/null +++ b/Makefile @@ -0,0 +1,191 @@ +# +# 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 (taken from the main source file): + +VERSION = $(shell grep 'define SCVERSION' version.h | awk '{ print $$3 }' | sed -e 's/[";]//g') +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 + +### 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 name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### The object files (add further files here): + +OBJS = $(PLUGIN).o data.o filter.o system.o i18n.o misc.o cam.o \ + smartcard.o network.o crypto.o system-common.o parse.o log.o + +# +# 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 + +# export for system makefiles +export SCAPIVERS +export APIVERSION +export INCLUDES +export SHAREDDEFINES +export SHAREDLIBS +export CXX +export CXXFLAGS + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +$(DEPFILE): $(OBJS:%.o=%.c) $(wildcard *.h) + @$(MAKEDEP) $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +ifdef STATIC +BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).a systems +SHAREDDEFINES += -DSTATICBUILD +else +BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION) systems systems-pre +endif + +all: $(BUILDTARGETS) +.PHONY: systems systems-pre clean clean-core clean-systems clean-pre dist srcdist + +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 + +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 + @$(MAKE) -C $(FFDECSADIR) clean + @-rm -f $(LIBDIR)/libsc-*-$(SCAPIVERS).so.$(APIVERSION) + @-rm -f $(LIBDIR)/libvdr-$(PLUGIN).a $(LIBDIR)/libsc-*.a + @-rm -f $(OBJS) $(DEPFILE) *.so *.tar.gz core* *~ + +clean-pre: + @-find "$(PREDIR)" -type f -not -iname "*-$(SCAPIVERS).so.*" | xargs rm -f + +clean: clean-core clean-systems + +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) + @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 $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tar.gz + +fulldist: clean clean-pre + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE)-full.tar.gz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Full distribution package created as $(PACKAGE)-full.tar.gz diff --git a/Makefile.system b/Makefile.system new file mode 100644 index 0000000..9d4ec61 --- /dev/null +++ b/Makefile.system @@ -0,0 +1,96 @@ +# +# 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 + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(SINCLUDES) $< + +# 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 + +$(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/README b/README new file mode 100644 index 0000000..ee0222e --- /dev/null +++ b/README @@ -0,0 +1,345 @@ +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 + +----------------------------------------------------------------------- + + + +WARNING! This is the SC plugin development branch !! + +This is a beta release. We encourage everybody to try this release, nevertheless +it should be used under controlled conditions. + +Feel free to put your bugreports, suggestions and/or wishes to the 4free or DVBN +board. A bugreports should at least include the console log. + + + +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 main development goal for this branch is a full-featured implementation +without the needs to patch VDR. Recent changes in the new VDR development branch +opened a possibility for that. + +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. + +For testing purpose you should start VDR in foreground always. The plugin gives +a lot of additional information to the console. This may be helpful in case it +doesn't work at once. + + + +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--.so., +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, +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 must be located in a subdirectory (of your VDR config +directory) called "plugins". The private plugin cache file is saved to this +directory too. The keyfile must be named "SoftCam.Key". + +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 +"plugins/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. + +Although there is support for Viaccess TPS AU, you still can provide a "tps.bin" +file in the "plugins/viaccess" subdirectory in case AU doesn't work for your +provider. Be aware that in most cases this file has to be updated on a daily +basis. + +Note, that for this @SHL implementation the key must be in Z 00 00 format +(the V 000000 00 format doesn't work). + +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. + +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 "plugins/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. 08 for BEV). The files +must contain the joined contents of all Rom/Eeprom pages. The plugin searches +for these files in the "plugins/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",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. + + + +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. + + + +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. + + 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). diff --git a/README.FFdecsa b/README.FFdecsa new file mode 100644 index 0000000..fe6968e --- /dev/null +++ b/README.FFdecsa @@ -0,0 +1,81 @@ + +FFdecsa implementation +====================== + +This plugin package contains a FFdecsa implementation for software descrambling +on budget cards. On full featured cards this is done by the hardware ECD chip. + +FFdecsa only works with budget cards (a full featured card isn't able to deliver +an encrypted stream due to hardware design). + +This package uses code from the great FFdecsa-1.0.0 package. FFdecsa-1.0.0 is +copyright by fatih89r and released under the GPL. Many thanks for this +outstanding work! You'll find the slightly modified FFdecsa code in the FFdecsa +directory. + +There are several options to configure optimization at compile time. Use: + +CPUOPT=type to determine your CPU type. With gcc 3.2 valid options are: + pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, + k6, k6-2, k6-3, athlon, athlon-tbird, athlon4, athlon-xp and + athlon-mp. You should set your CPU type as close as possible for + best performance. Defaults to pentium. + +PARALLEL=mode to determine which datatype should be used for parallel + processing. Valid modes are: + PARALLEL_32_4CHAR + PARALLEL_32_4CHARA + PARALLEL_32_INT + 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 + Hints: if you have a Pentium4 or AthlonXP and a recent compiler + try PARALLEL_64_MMX. If you have a 64-bit CPU, try + PARALLEL_128_SSE. If you're unsure take PARALLEL_32_INT (which + isn't the fastest, but quite good and very portable). + For additional information read the FFdecsa documentation. + Defaults to PARALLEL_32_INT. + +CSAFLAGS=opts to set compiler options. + Defaults to -Wall -fPIC -g -O3 -mmmx -fomit-frame-pointer + -fexpensive-optimizations -funroll-loops + +All of them can be added to VDR's make.config file. + + + +History: +-------- + +30.01.2007: +- Integrated into SC plugin as of version 0.7.0. Future changes will be reported + in HISTORY file. + +02.01.2006: Version 0.1.3 +- Now locking the CSA engine properly to prevent potentional problems when + updating CWs during decryption. Thanks to NooneImportant for pointing this + out. +- Added -fPIC to default compiler flags. + +09.11.2005: Version 0.1.2 +- Fixed segfault when starting VDR with devices in non-native sequence i.e. by + using -D switches. Thanks to noneed for reporting. + +03.10.2005: Version 0.1.1 +- Removed static vars from stream code. Caused glitches when using two budgets + cards concurrently. Thanks to dingo35 for reporting. +- Some small performace improvements. Thanks to dingo35. +- Removed ugly tmp_autogenerated stuff. + +18.09.2005: Version 0.1.0 +- Initial version. +- Supporting concurrent decryption. +- Requires sc plugin 0.5.0 or newer. diff --git a/cam.c b/cam.c new file mode 100644 index 0000000..092071c --- /dev/null +++ b/cam.c @@ -0,0 +1,2905 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#if APIVERSNUM >= 10500 +#include +#endif +#include +#include + +#include "FFdecsa/FFdecsa.h" + +#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 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; iSAMPLE) { + 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 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 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 chains; + cSimpleList active; + // + cPidFilter *catfilt; + int catVers; + // + 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); + }; + +cLogger::cLogger(int CardNum, bool soft) +:cAction("logger",CardNum) +{ + cardNum=CardNum; softCSA=soft; + catfilt=0; up=false; + 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; + } + 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; + } + } + SetChains(); + Unlock(); +} + +void cLogger::SetChains(void) +{ + for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) { + bool act=false; + if(ScSetup.AutoUpdate>1) 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,false); + } +} + +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; icaid==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(); + } + } + 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 { +private: + bool del; +public: + cEcmData(void); + cEcmData(cEcmInfo *e); + bool Save(FILE *f); + bool Parse(const char *buf); + void Delete(void) { del=true; } + bool IsDeleted(void) const { return del; } + }; + +cEcmData::cEcmData(void) +:cEcmInfo() +{ + del=false; +} + +cEcmData::cEcmData(cEcmInfo *e) +:cEcmInfo(e) +{ + del=false; +} + +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[nu]; + if(GetHex(line,dat,nu,true)==nu) AddData(dat,nu); + } + return true; + } + return false; +} + +bool cEcmData::Save(FILE *f) +{ + fprintf(f,"%d:%x:%x:%s:%x/%x:%x:%x/%x",prgId,source,transponder,name,caId,emmCaId,provId,ecm_pid,ecm_table); + if(data) { + char str[dataLen*2+2]; + fprintf(f,":%d:%s\n",dataLen,HexStr(str,data,dataLen)); + } + else + fprintf(f,":0\n"); + return ferror(f)==0; +} + +// -- cEcmCache ---------------------------------------------------------------- + +cEcmCache ecmcache; + +cEcmCache::cEcmCache(void) +:cLoader("ECM") +{} + +void cEcmCache::New(cEcmInfo *e) +{ + Lock(); + 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(); + } + e->SetCached(); + Unlock(); +} + +cEcmData *cEcmCache::Exists(cEcmInfo *e) +{ + for(cEcmData *dat=First(); dat; dat=Next(dat)) + if(!dat->IsDeleted() && dat->Compare(e)) return dat; + return 0; +} + +int cEcmCache::GetCached(cSimpleList *list, int sid, int Source, int Transponder) +{ + int n=0; + list->Clear(); + Lock(); + for(cEcmData *dat=First(); dat; dat=Next(dat)) { + if(!dat->IsDeleted() && 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++; + } + } + } + Unlock(); + return n; +} + +void cEcmCache::Delete(cEcmInfo *e) +{ + Lock(); + cEcmData *dat=Exists(e); + if(dat) { + dat->Delete(); + Modified(); + 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); + } + Unlock(); +} + +void cEcmCache::Flush(void) +{ + Lock(); + Clear(); + Modified(); + PRINTF(L_CORE_ECM,"cache flushed"); + Unlock(); +} + +void cEcmCache::Load(void) +{ + Lock(); + Clear(); + Unlock(); +} + +bool cEcmCache::Save(FILE *f) +{ + bool res=true; + Lock(); + for(cEcmData *dat=First(); dat;) { + if(!dat->IsDeleted()) { + if(!dat->Save(f)) { res=false; break; } + dat=Next(dat); + } + else { + cEcmData *n=Next(dat); + Del(dat); + dat=n; + } + } + Modified(!res); + Unlock(); + return res; +} + +bool cEcmCache::ParseLine(const char *line, bool fromCache) +{ + bool res=false; + cEcmData *dat=new cEcmData; + if(dat && dat->Parse(line)) { + if(!Exists(dat)) { Add(dat); dat=0; } + res=true; + } + delete dat; + return res; +} + +// -- cPrgPid --------------------------------------------------------------------- + +class cPrgPid : public cSimpleItem { +private: + int type; + int pid; + bool proc; +public: + cPrgPid(int Type, int Pid); + int Pid(void) { return pid; } + int Type(void) { return type; } + bool Proc(void) { return proc; } + void Proc(bool is) { proc=is; }; + }; + +cPrgPid::cPrgPid(int Type, int Pid) +{ + type=Type; pid=Pid; + proc=false; +} + +// -- cPrg --------------------------------------------------------------------- + +class cPrg : public cSimpleItem { +private: + int prg; + bool isUpdate; +public: + cSimpleList pids; + // + cPrg(int Prg, bool IsUpdate); + int Prg(void) { return prg; } + bool IsUpdate(void) { return isUpdate; } + }; + +cPrg::cPrg(int Prg, bool IsUpdate) +{ + prg=Prg; isUpdate=IsUpdate; +} + +// -- 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 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 ecmList; + cSimpleList 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); + 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(modeSetIdleTime(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()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->pripri) + ecmPriList.Ins(ep); + else { + do { + eppp=ecmPriList.Next(epp); + if(!eppp || eppp->pripri) { + 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); + 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); + } + cLoaders::SaveCache(); +} + +void cEcmHandler::EcmFail(void) +{ + ecm->Fail(true); +} + +void cEcmHandler::ParseCAInfo(int SysId) +{ + 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; indexIsSoftCSA(),sysPri))) { + sysPri=sys->Pri(); + cSimpleList ecms; + sys->ParseCADescriptor(&ecms,sysId,&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::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(void) +{ + cMutexLock lock(this); + for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler)) + if(!handler->IsIdle()) { + 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) { + 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::LogEcmStatus(const cEcmInfo *ecm, bool on) +{ + cMutexLock lock(this); + if(!logger && on && ScSetup.AutoUpdate) { + logger=new cLogger(cardNum,IsSoftCSA()); + LogStatsUp(); + } + 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(indexSetCaPid(&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(indexSetCaDescr(&ca_descr)) + 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)) + 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; idxSid()==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; +} + +// --- 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(numcaidsSid() && source==channel->Source() && transponder==channel->Transponder(); +} + +void cChannelCaids::Sort(void) +{ + caid_t tmp[MAX_CI_SLOT_CAIDS]; + int c=0xFFFF; + for(int i=0; id && caids[j]0) Sort(); + caids[numcaids]=0; + break; + } +} + +bool cChannelCaids::HasCaid(caid_t caid) +{ + for(int i=0; inumcaids) 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 { +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; iStop(); +} + +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; iGroupSep() && 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; i0); + 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(cDel(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 + +class cDeCSA : public cMutex { +private: + int cs; + unsigned char **range; + unsigned char pidmap[MAX_CSA_PIDS]; + void *keys[MAX_CSA_IDX]; + // + bool GetKeyStruct(int idx); +public: + cDeCSA(void); + ~cDeCSA(); + bool Decrypt(unsigned char *data, int len, bool force); + bool SetDescr(ca_descr_t *ca_descr); + bool SetCaPid(ca_pid_t *ca_pid); + }; + +cDeCSA::cDeCSA(void) +{ + cs=get_suggested_cluster_size(); + PRINTF(L_CORE_CSA,"clustersize=%d rangesize=%d",cs,cs*2+5); + range=MALLOC(unsigned char *,(cs*2+5)); + memset(keys,0,sizeof(keys)); + memset(pidmap,0,sizeof(pidmap)); +} + +cDeCSA::~cDeCSA() +{ + for(int i=0; iindex; + if(idxparity==0) { // even key + set_even_control_word(keys[idx],ca_descr->cw); + PRINTF(L_CORE_CSA,"even key set"); + } + else { // odd key + set_odd_control_word(keys[idx],ca_descr->cw); + PRINTF(L_CORE_CSA,"odd key set"); + } + } + return true; +} + +bool cDeCSA::SetCaPid(ca_pid_t *ca_pid) +{ + cMutexLock lock(this); + if(ca_pid->indexpidpid]=ca_pid->index; + PRINTF(L_CORE_CSA,"set pid %04x to index %d",ca_pid->pid,ca_pid->index); + } + return true; +} + +bool cDeCSA::Decrypt(unsigned char *data, int len, bool force) +{ + cMutexLock lock(this); + int r=-2, ccs=0, currIdx=-1; + bool newRange=true; + range[0]=0; + len-=(TS_SIZE-1); + for(int l=0; l=cs) break; + } + else { // other index, create hole + PRINTF(L_CORE_CSA,"other index. current=%d idx=%d",currIdx,idx); + newRange=true; + } + } + else { // unencrypted + // nothing, we don't create holes for unencrypted packets + } + } + if(force) PRINTF(L_CORE_CSA,"forced"); + if(r>cs*2) PRINTF(L_CORE_CSA,"range overflow"); + if(r<0) PRINTF(L_CORE_CSA,"nothing to decrypt"); + if(r>=0 && (ccs>=cs || force) && GetKeyStruct(currIdx)) { // we have some range + LBSTARTF(L_CORE_CSA); + LBPUT("decrypting %3d packets (ccs=%3d cs=%3d %s)",ccs,ccs,cs,ccs>=cs?"OK ":"INC"); + int n=decrypt_packets(keys[currIdx],range); + LBPUT(" -> %3d packets decrypted\n",n); + if(n>0) return true; + LBEND(); + } + else { + if(ccsSetTimeouts(100,100); + Start(); +} + +cDeCsaTSBuffer::~cDeCsaTSBuffer() +{ + Cancel(3); + delete ringBuffer; +} + +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; iDel(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; +} + +// -- cScDvbDevice ------------------------------------------------------------- + +int cScDvbDevice::budget=0; + +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; + } +} + +void cScDvbDevice::Shutdown(void) +{ + for(int n=cDevice::NumDevices(); --n>=0;) { + cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(n)); + if(dev) dev->EarlyShutdown(); + } +} + +void cScDvbDevice::Startup(void) +{ + for(int n=cDevice::NumDevices(); --n>=0;) { + cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(n)); + if(dev) dev->LateInit(); + } +} + +void cScDvbDevice::SetForceBudget(int n) +{ + if(n>=0 && n/vdr | grep -E "(useDevice|nextCardIndex)" + Insert the symbol names below. +*/ + vdr_nci=(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice13nextCardIndexE"); + vdr_ud =(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice9useDeviceE"); + 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=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); + 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; iCa(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); + return cDvbDevice::SetChannelDevice(Channel,LiveView); +} + +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; jCa()>=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 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::OpenDvr(void) +{ + CloseDvr(); + fd_dvr=DvbOpen(DEV_DVB_DVR,CardIndex(),O_RDONLY|O_NONBLOCK,true); + if(fd_dvr>=0) { +#if APIVERSNUM >= 10500 + bool active=dynamic_cast(CamSlot())!=0; +#else + bool active=cam && softcsa; +#endif + tsBuffer=new cDeCsaTSBuffer(fd_dvr,MEGABYTE(2),CardIndex()+1,decsa,active); + } + return fd_dvr>=0; +} + +void cScDvbDevice::CloseDvr(void) +{ + delete tsBuffer; tsBuffer=0; + 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) +{ + if(!softcsa) { + cMutexLock lock(&cafdMutex); + return ioctl(fd_ca,CA_SET_DESCR,ca_descr)>=0; + } + else if(decsa) return decsa->SetDescr(ca_descr); + 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(); + } + } + } +} diff --git a/cam.h b/cam.h new file mode 100644 index 0000000..8f80d44 --- /dev/null +++ b/cam.h @@ -0,0 +1,178 @@ +/* + * 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 +#include +#include +#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 cLoader, cMutex, cSimpleList { +private: + cEcmData *Exists(cEcmInfo *e); +public: + cEcmCache(void); + void Load(void); + void New(cEcmInfo *e); + int GetCached(cSimpleList *list, int sid, int Source, int Transponder); + void Delete(cEcmInfo *e); + void Flush(void); + virtual bool Save(FILE *f); + virtual bool ParseLine(const char *line, bool fromCache); + }; + +extern cEcmCache ecmcache; + +// ---------------------------------------------------------------- + +#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 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); +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(void); + void HouseKeeping(void); + void Tune(const cChannel *channel); + 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; +#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; + // + void LateInit(void); + void EarlyShutdown(void); + int FindLRUPrg(int source, int transponder, int prg); +protected: +#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); +public: + cScDvbDevice(int n, int cafd); + ~cScDvbDevice(); +#if APIVERSNUM >= 10501 + virtual bool HasCi(void); +#else + virtual int ProvidesCa(const cChannel *Channel) const; +#endif + 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); + bool SetCaDescr(ca_descr_t *ca_descr); + bool SetCaPid(ca_pid_t *ca_pid); + void DumpAV7110(void); + cCam *Cam(void) { return cam; } + bool SoftCSA(void) { return softcsa; } + bool GetPrgCaids(int source, int transponder, int prg, caid_t *c); + }; + +#endif // ___CAM_H diff --git a/common.h b/common.h new file mode 100644 index 0000000..5396444 --- /dev/null +++ b/common.h @@ -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/crypto-bn.h b/crypto-bn.h new file mode 100644 index 0000000..14ce54d --- /dev/null +++ b/crypto-bn.h @@ -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 ___CRYPTO_BN_H +#define ___CRYPTO_BN_H + +#include + +// ---------------------------------------------------------------- + +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; } + 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/crypto.c b/crypto.c new file mode 100644 index 0000000..ed1e12a --- /dev/null +++ b/crypto.c @@ -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 +#include +#include + +#include "crypto.h" +#include "helper.h" +#include "log.h" + +// ----------------------------------------------------------------------------- + +void RotateBytes(unsigned char *out, const unsigned char *in, int n) +{ + // loop is executed atleast once, so it's not a good idea to + // call with n=0 !! + out+=n; + do { *(--out)=*(in++); } while(--n); +} + +void RotateBytes(unsigned char *in, int n) +{ + // loop is executed atleast once, so it's not a good idea to + // call with n=0 !! + unsigned char *e=in+n-1; + do { + unsigned char temp=*in; + *in++=*e; + *e-- =temp; + } while(inn) { + unsigned char buff[s]; + BN_bn2bin(&big,buff); + memcpy(out,buff+s-n,n); + } + else if(sGetLE(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=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 +#include + +#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 +#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 +#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/data.c b/data.c new file mode 100644 index 0000000..b05204e --- /dev/null +++ b/data.c @@ -0,0 +1,832 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#include "data.h" +#include "misc.h" +#include "scsetup.h" +#include "log-core.h" + +#define KEY_FILE "SoftCam.Key" +#define CACACHE_FILE "ca.cache" +#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; +} + +// -- cConfRead ---------------------------------------------------------------- + +bool cConfRead::ConfRead(const char *type, const char *filename) +{ + bool res=false; + FILE *f=fopen(filename,"r"); + if(f) { + res=true; + PRINTF(L_GEN_INFO,"loading %s from %s",type,filename); + char buff[1024]; + while(fgets(buff,sizeof(buff),f)) { + if(!index(buff,'\n') && !feof(f)) + PRINTF(L_GEN_ERROR,"confread %s fgets readbuffer overflow",type); + char *line=skipspace(stripspace(buff)); + if(line[0]==0 || line[0]==';' || line[0]=='#') continue; // skip empty & comment lines + if(!ParseLine(line,false)) { + PRINTF(L_GEN_ERROR,"file '%s' has error in line '%s'",filename,buff); + res=false; + } + } + fclose(f); + } + else PRINTF(L_GEN_ERROR,"Failed to open file '%s': %s",filename,strerror(errno)); + return res; +} + +// -- cLineDummy --------------------------------------------------------------- + +class cLineDummy : public cSimpleItem { +private: + char *store; +public: + cLineDummy(void); + ~cLineDummy(); + bool Parse(const char *line); + bool Save(FILE *f); + }; + +cLineDummy::cLineDummy(void) +{ + store=0; +} + +cLineDummy::~cLineDummy() +{ + free(store); +} + +bool cLineDummy::Parse(const char *line) +{ + free(store); + store=strdup(line); + return store!=0; +} + +bool cLineDummy::Save(FILE *f) +{ + fprintf(f,"%s",store); + return ferror(f)==0; +} + +// -- cLoaderDummy ------------------------------------------------------------- + +class cLoaderDummy : public cSimpleList, public cLoader { +public: + cLoaderDummy(const char *Id); + virtual bool ParseLine(const char *line, bool fromCache); + virtual bool Save(FILE *f); + }; + +cLoaderDummy::cLoaderDummy(const char *id) +:cLoader(id) +{} + +bool cLoaderDummy::ParseLine(const char *line, bool fromCache) +{ + if(fromCache) { + cLineDummy *k=new cLineDummy; + if(k) { + if(k->Parse(line)) Add(k); + else delete k; + return true; + } + PRINTF(L_GEN_ERROR,"not enough memory for %s loader dummy!",Id()); + } + return false; +} + +bool cLoaderDummy::Save(FILE *f) +{ + bool res=true; + for(cLineDummy *k=First(); k; k=Next(k)) + if(!k->Save(f)) { res=false; break; } + Modified(!res); + return res; +} + +// -- cLoader ------------------------------------------------------------------ + +cLoader::cLoader(const char *Id) +{ + id=Id; modified=false; + cLoaders::Register(this); +} + +// -- cLoaders ----------------------------------------------------------------- + +cLoader *cLoaders::first=0; +cMutex cLoaders::lock; +char *cLoaders::cacheFile=0; + +void cLoaders::Register(cLoader *ld) +{ + PRINTF(L_CORE_DYN,"loaders: registering loader %s",ld->id); + ld->next=first; + first=ld; +} + +void cLoaders::LoadCache(const char *cfgdir) +{ + lock.Lock(); + cacheFile=strdup(AddDirectory(cfgdir,CACACHE_FILE)); + if(access(cacheFile,F_OK)==0) { + PRINTF(L_GEN_INFO,"loading ca cache from %s",cacheFile); + FILE *f=fopen(cacheFile,"r"); + if(f) { + char buf[512]; + cLoader *ld=0; + while(fgets(buf,sizeof(buf),f)) { + if(!index(buf,'\n')) + PRINTF(L_GEN_ERROR,"loaders fgets readbuffer overflow"); + if(buf[0]=='#') continue; + if(!strncmp(buf,":::",3)) { // new loader section + ld=FindLoader(stripspace(&buf[3])); + if(!ld) { + PRINTF(L_CORE_LOAD,"unknown loader section '%s', adding dummy",&buf[3]); + ld=new cLoaderDummy(strdup(&buf[3])); + } + } + else if(ld) { + if(!ld->ParseLine(buf,true)) { + PRINTF(L_CORE_LOAD,"loader '%s' failed on line '%s'",ld->Id(),buf); + } + } + } + fclose(f); + } + else LOG_ERROR_STR(cacheFile); + } + lock.Unlock(); +} + +void cLoaders::SaveCache(void) +{ + lock.Lock(); + if(cacheFile && IsModified()) { + cSafeFile f(cacheFile); + if(f.Open()) { + fprintf(f,"## This is a generated file. DO NOT EDIT!!\n" + "## This file will be OVERWRITTEN WITHOUT WARNING!!\n"); + + cLoader *ld=first; + while(ld) { + fprintf(f,":::%s\n",ld->Id()); + if(!ld->Save(f)) break; + ld=ld->next; + } + f.Close(); + PRINTF(L_CORE_LOAD,"saved cache to file"); + } + } + lock.Unlock(); +} + +bool cLoaders::IsModified(void) +{ + bool res=false; + lock.Lock(); + cLoader *ld=first; + while(ld) { + if(ld->IsModified()) { + res=true; break; + } + ld=ld->next; + } + lock.Unlock(); + return res; +} + +cLoader *cLoaders::FindLoader(const char *id) +{ + lock.Lock(); + cLoader *ld=first; + while(ld) { + if(!strcmp(id,ld->Id())) break; + ld=ld->next; + } + lock.Unlock(); + return ld; +} + +// -- 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) +{ + au=del=false; + 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; +} + +bool cPlainKey::Save(FILE *f) +{ + fprintf(f,"%s\n",*ToString(false)); + return ferror(f)==0; +} + +cString cPlainKey::ToString(bool hide) +{ + return cString::sprintf(hide ? "%c %.*X %s %.4s..." : "%c %.*X %s %s",type,IdSize(),id,*PrintKeyNr(),*Print()); +} + +void cPlainKey::FormatError(const char *type, const char *sline) +{ + PRINTF(L_GEN_WARN,"%s key: bad format '%.15s%s'\n",type,sline,(strlen(sline)>15)?"...":""); +} + +// -- 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(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); +} + +// -- cPlainKeys --------------------------------------------------------------- + +const char *externalAU=0; + +cPlainKeys keys; + +cPlainKeyType *cPlainKeys::first=0; + +cPlainKeys::cPlainKeys(void) +:cLoader("KEY") +//,cThread("ExternalAU") +{ + mark=0; +} + +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; +} + +cPlainKey *cPlainKeys::FindKey(int Type, int Id, int Keynr, int Size, cPlainKey *key) +{ + key=FindKeyNoTrig(Type,Id,Keynr,Size,key); + if(!key) { + static int lastType=-1, lastId=-1, lastKeynr=-1; + if(externalAU && (lastType!=Type || lastId!=Id || lastKeynr!=Keynr)) { + PRINTF(L_CORE_AUEXTERN,"triggered from findkey (type=%X id=%X keynr=%X)",Type,Id,Keynr); + lastType=Type; lastId=Id; lastKeynr=Keynr; + } + ExternalUpdate(); + } + return key; +} + +cPlainKey *cPlainKeys::FindKeyNoTrig(int Type, int Id, int Keynr, int Size, cPlainKey *key) +{ + Lock(); + if(key) key=Next(key); else key=First(); + while(key) { + if(!key->IsInvalid() && key->type==Type && key->id==Id && key->keynr==Keynr && (Size<0 || key->Size()==Size)) break; + key=Next(key); + } + Unlock(); + return key; +} + +bool cPlainKeys::NewKey(int Type, int Id, int Keynr, void *Key, int Keylen) +{ + cPlainKey *k=0; + while((k=FindKeyNoTrig(Type,Id,Keynr,-1,k))) + if(k->Cmp(Key,Keylen)) return false; + + cPlainKey *nk=NewFromType(Type); + if(nk) { + nk->Set(Type,Id,Keynr,Key,Keylen); + AddNewKey(nk,2,true); + return true; + } + else PRINTF(L_GEN_ERROR,"no memory for new key ID %c %.2x!",Type,Id); + return false; +} + +void cPlainKeys::AddNewKey(cPlainKey *nk, int mode, bool log) +{ + if(mode>=1) { + nk->SetAuto(); + if(log) PRINTF(L_GEN_INFO,"key update for ID %s",*nk->ToString(true)); + if(nk->CanSupersede()) { + cPlainKey *k=0; + while((k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,nk->Size(),k))) { + if(!k->IsInvalid()) { + k->SetInvalid(); + if(k->IsAuto()) Modified(); + if(log) PRINTF(L_GEN_INFO,"supersedes key: %s%s",*k->ToString(true),k->IsAuto()?" (auto)":""); + } + } + } + } + Lock(); + switch(mode) { + case 0: Add(nk); break; + case 1: if(!mark) Ins(nk); else Add(nk,mark); + mark=nk; + break; + case 2: Ins(nk); Modified(); break; + } + Unlock(); +} + +bool cPlainKeys::Load(const char *cfgdir) +{ + Lock(); + Clear(); mark=0; + cString cname=AddDirectory(cfgdir,KEY_FILE); + ConfRead("keys",cname); + int n=Count(); + PRINTF(L_CORE_LOAD,"loaded %d keys from %s",n,*cname); + if(n && LOG(L_CORE_KEYS)) { + cPlainKey *dat=First(); + while(dat) { + if(!dat->IsInvalid()) PRINTF(L_CORE_KEYS,"keys %s",*dat->ToString(false)); + dat=Next(dat); + } + } + Unlock(); + return (n!=0); +} + +void cPlainKeys::HouseKeeping(void) +{ + cLoaders::SaveCache(); + 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; + cPlainKey *nk=NewFromType(toupper(line[0])); + if(nk && nk->Parse(line)) { + cPlainKey *k=0; + while((k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,-1,k))) + if(k->Cmp(nk)) break; + if(!k) { + AddNewKey(nk,2,true); + nk=0; + } + } + delete nk; + } + } + pipe.Close(); + PRINTF(L_CORE_AUEXTERN,"done (elapsed %d)",(int)start.Elapsed()); +} + +cPlainKey *cPlainKeys::NewFromType(int type) +{ + cPlainKeyType *pkt=first; + while(pkt) { + if(pkt->type==type) return pkt->Create(); + pkt=pkt->next; + } + PRINTF(L_CORE_LOAD,"unknown key type '%c', adding dummy",type); + pkt=new cPlainKeyTypeDummy(type); + return pkt->Create(); +} + +bool cPlainKeys::ParseLine(const char *line, bool fromCache) +{ + char *s=skipspace(line); + cPlainKey *k=NewFromType(toupper(*s)); + if(k) { + if(k->Parse((char *)line)) AddNewKey(k,fromCache?1:0,false); + else delete k; + return true; + } + return false; +} + +bool cPlainKeys::Save(FILE *f) +{ + bool res=true; + Lock(); + cPlainKey *dat=First(); + while(dat) { + if(dat->IsAuto() && !dat->IsInvalid()) { + if(!dat->Save(f)) { res=false; break; } + } + dat=Next(dat); + } + Modified(!res); + Unlock(); + return res; +} diff --git a/data.h b/data.h new file mode 100644 index 0000000..2d0e727 --- /dev/null +++ b/data.h @@ -0,0 +1,271 @@ +/* + * 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 +#include +#include "misc.h" + +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, 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 cConfRead { +public: + virtual ~cConfRead() {} + bool ConfRead(const char *type, const char *filename); + virtual bool ParseLine(const char *line, bool fromCache)=0; + }; + +// ---------------------------------------------------------------- + +class cLoader { +friend class cLoaders; +private: + cLoader *next; + bool modified; + const char *id; +protected: + void Modified(bool mod=true) { modified=mod; } +public: + cLoader(const char *Id); + virtual ~cLoader() {} + virtual bool ParseLine(const char *line, bool fromCache)=0; + virtual bool Save(FILE *f)=0; + bool IsModified(void) const { return modified; } + const char *Id(void) const { return id; } + }; + +// ---------------------------------------------------------------- + +class cLoaders { +friend class cLoader; +private: + static cLoader *first; + static cMutex lock; + static char *cacheFile; + // + static void Register(cLoader *ld); + static cLoader *FindLoader(const char *id); + static bool IsModified(void); +public: + static void LoadCache(const char *cfgdir); + static void SaveCache(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 { +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 cSimpleItem { +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(); + 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 cSimpleItem { +friend class cPlainKeys; +friend class cMutableKey; +private: + bool au, del, super; +protected: + void SetInvalid(void) { del=true; } + void SetAuto(void) { au=true; } + bool IsAuto(void) const { return au; } + bool IsInvalid(void) const { return del; } + 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); + void FormatError(const char *type, const char *sline); +public: + int type, id, keynr; + // + cPlainKey(bool CanSupersede); + virtual bool Parse(const char *line)=0; + bool Save(FILE *f); + cString ToString(bool hide=false); + 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 cPlainKeyTypeReg : public cPlainKeyType { +public: + cPlainKeyTypeReg(void):cPlainKeyType(KT,SUP) {} + virtual cPlainKey *Create(void) { return new PKT(SUP); } + }; + +// ---------------------------------------------------------------- + +extern const char *externalAU; + +class cPlainKeys : public cLoader, private cConfRead, private cThread, public cSimpleList { +friend class cPlainKeyType; +private: + static cPlainKeyType *first; + cPlainKey *mark; + cTimeMs trigger, last; + // + static void Register(cPlainKeyType *pkt, bool Super); + cPlainKey *NewFromType(int type); + void AddNewKey(cPlainKey *nk, int mode, bool log); + void ExternalUpdate(void); +protected: + virtual void Action(void); +public: + cPlainKeys(void); + bool Load(const char *cfgdir); + virtual bool Save(FILE *f); + virtual bool ParseLine(const char *line, bool Au); + 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); + bool NewKey(int Type, int Id, int Keynr, void *Key, int Keylen); + void HouseKeeping(void); + }; + +extern cPlainKeys keys; + +#endif //___DATA_H diff --git a/diff.exclude b/diff.exclude new file mode 100644 index 0000000..bf65518 --- /dev/null +++ b/diff.exclude @@ -0,0 +1,12 @@ +patches +systems-pre +.dependencies +diff.exclude +*.so +*.a +*.o +*.save +*.orig +*.rej +*.bak +nagra2-prov.c diff --git a/examples/Ird-Beta.KID b/examples/Ird-Beta.KID new file mode 100644 index 0000000..3598b4a --- /dev/null +++ b/examples/Ird-Beta.KID @@ -0,0 +1,12 @@ +; Irdeto/Betacrypt cards +; +; AAAAAA BBBBBBBBBBBBBBBBBBBB CC DDDDDD EEEEEEEEEEEEEEEE +; +; AAAAAA - HS hex serial +; BBBBBBBBBBBBBBBBBBBB - HMK hex master key +; CC - provider +; DDDDDD - provider ID +; EEEEEEEEEEEEEEEE - PMK plain master key +; +123456 12345612345612345612 00 123456 1234567890123456 ; dummy + diff --git a/examples/Seca.KID b/examples/Seca.KID new file mode 100644 index 0000000..65edc54 --- /dev/null +++ b/examples/Seca.KID @@ -0,0 +1,9 @@ +; Seca cards +; +; AAAA BBBBBBBB CCCCCCCCCCCCCCCC +; +; AAAA - provider ident +; BBBBBB - shared address +; CCCCCCCCCCCCCCCC - key 01 +; +1234 123456 1234561234561234 ; dummy diff --git a/examples/SoftCam.Key b/examples/SoftCam.Key new file mode 100644 index 0000000..7038809 --- /dev/null +++ b/examples/SoftCam.Key @@ -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/examples/Viaccess.KID b/examples/Viaccess.KID new file mode 100644 index 0000000..a1bc85c --- /dev/null +++ b/examples/Viaccess.KID @@ -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/examples/cardclient.conf.example b/examples/cardclient.conf.example new file mode 100644 index 0000000..f72df6a --- /dev/null +++ b/examples/cardclient.conf.example @@ -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/examples/dialup.sh.example b/examples/dialup.sh.example new file mode 100755 index 0000000..71925eb --- /dev/null +++ b/examples/dialup.sh.example @@ -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/examples/externalau.sh.example b/examples/externalau.sh.example new file mode 100755 index 0000000..3bf09d5 --- /dev/null +++ b/examples/externalau.sh.example @@ -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/
.*$//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/examples/smartcard.conf.example b/examples/smartcard.conf.example
new file mode 100644
index 0000000..7c7bf4c
--- /dev/null
+++ b/examples/smartcard.conf.example
@@ -0,0 +1,70 @@
+;
+; Comment lines can start with # or ;
+;
+
+; 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/filter.c b/filter.c
new file mode 100644
index 0000000..e8a8f9f
--- /dev/null
+++ b/filter.c
@@ -0,0 +1,292 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include 
+
+#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);
+  while(Running()) {
+    int num=filters.Count();
+    if(num<=0) {
+      cCondWait::SleepMs(100);
+      }
+    else {
+      // first build pfd data
+      Lock();
+      cPidFilter *filter;
+      struct pollfd pfd[num];
+      num=0;
+      memset(pfd,0,sizeof(pfd));
+      for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+        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 ; rfd==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();
+      }
+    }
+}
diff --git a/filter.h b/filter.h
new file mode 100644
index 0000000..f93c671
--- /dev/null
+++ b/filter.h
@@ -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 
+#include "misc.h"
+
+class cPidFilter;
+
+// ----------------------------------------------------------------
+
+#define MAX_SECT_SIZE 4096
+
+class cAction : protected cThread {
+private:
+  char *id;
+  int dvbNum, unique, pri;
+  cSimpleList 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/helper.h b/helper.h
new file mode 100644
index 0000000..3430208
--- /dev/null
+++ b/helper.h
@@ -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 
+
+#if defined __i386__
+#define get_misalign(_a)    *(_a)
+#define put_misalign(_b,_a) *(_a)=(_b)
+#else
+template inline T get_misalign(T *p)
+{ struct s { T v; } __attribute__((packed)); return ((s *)p)->v; }
+
+template 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/i18n.c b/i18n.c
new file mode 100644
index 0000000..6c2bd39
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,780 @@
+/*
+ * 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 "i18n.h"
+
+const tI18nPhrase ScPhrases[] = {
+  { "Update keys (AU)",
+    "Keys updaten (AU)",
+    "",
+    "",
+    "Keys updaten (AU)",
+    "",
+    "Mise à jour des Clés (AU)",
+    "",
+    "Tilausten päivitys (AU)",
+    "Aktualizuj klucze (AU)",
+    "",
+    "",
+    "Nyckeluppdatering (AU)",
+    "",
+    "",
+    "",
+    "¾ÑÝÞÒÛïâì ÚÛîçØ (AU)",
+  },
+  { "active CAIDs",
+    "aktive CAIDs",
+    "",
+    "",
+    "actieve CAIDS",
+    "",
+    "CAIDs actifs",
+    "",
+    "aktiiviset CAID:t",
+    "aktywne CAID",
+    "",
+    "",
+    "aktiva CAID",
+    "",
+    "",
+    "",
+    "ÐÚâØÒÝëÕ CAID",
+  },
+  { "all CAIDs",
+    "alle CAIDs",
+    "",
+    "",
+    "alle CAIDs",
+    "",
+    "tous les CAIDs",
+    "",
+    "kaikki CAID:t",
+    "wszystkie CAID",
+    "",
+    "",
+    "alla CAID",
+    "",
+    "",
+    "",
+    "ÒáÕ CAID",
+  },
+  { "Active on DVB card",
+    "Aktiv auf DVB Karte",
+    "",
+    "",
+    "Actief op DVB kaart",
+    "",
+    "Actif sur la carte DVB",
+    "",
+    "Aktiivinen DVB-kortilla",
+    "Aktywny na karcie DVB",
+    "",
+    "",
+    "Aktiv på DVB-kort",
+    "",
+    "",
+    "",
+    "°ÚâØÒÝëÙ ÝÐ ßÛÐâÕ DVB",
+  },
+  { "Concurrent FF streams",
+    "Gleichzeitige FF Streams",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Yhtäaikainen salauksenpurku (FF)",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¿ÐàÐÛÛÕÛìÝëÕ ßÞâÞÚØ FF",
+  },
+  { "Prefer local systems",
+    "Lokale Systeme bevorzugen",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Suosi paikallista salauksenpurkua",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¿àÕÔßÞçØâÐâì ÛÞÚÐÛìÝëÕ áØáâÕÜë",
+  },
+  { "Ignore CAID",
+    "Ignoriere CAID",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Jätä huomioimatta CAID",
+    "",
+    "",
+    "",
+    "Ignorera CAID",
+    "",
+    "",
+    "",
+    "¸ÓÝÞàØàÞÒÐâì CAID",
+  },
+  { "Key update status:",
+    "Key update Status:",
+    "",
+    "",
+    "Key update status:",
+    "",
+    "Mise à jour des clés Statut:",
+    "",
+    "Tilausten päivitys:",
+    "Stan aktualizacji kluczy:",
+    "",
+    "",
+    "Nyckeluppdateringsstatus:",
+    "",
+    "",
+    "",
+    "¾ÑÝÞÒÛÕÝØÕ ÚÛîçÕÙ:",
+  },
+  { "  [Seen keys]",      // 2 leading spaces
+    "  [Gefundene Keys]",
+    "",
+    "  [Chiavi trovate]",
+    "",
+    "  [Gevonden keys]",
+    "  [Clés trouvées]",
+    "",
+    "  [Löydetyt päivitykset]",
+    "  [widzianych kluczy]",
+    "",
+    "  [vriskomena klidia]",
+    "  [Påträffade nycklar]",
+    "",
+    "",
+    "",
+    "  [ÚÛîçÕÙ ÝÐÙÔÕÝÞ]",
+  },
+  { "  [New keys]",       // 2 leading spaces
+    "  [Neue Keys]",
+    "",
+    "  [Nuove chiavi]",
+    "  [Nieuwe keys]",
+    "",
+    "  [Nouvelles clés]",
+    "",
+    "  [Uudet päivitykset]",
+    "  [nowych kluczy]",
+    "",
+    "  [Nea klidia]",
+    "  [Nya nycklar]",
+    "",
+    "",
+    "",
+    "  [ÝÞÒëå ÚÛîçÕÙ]",
+  },
+  { "Current keys:",
+    "Aktuelle Keys:",
+    "",
+    "Chiave in uso:",
+    "Huidige keys:",
+    "",
+    "Clé courante:",
+    "",
+    "Nykyiset avaimet:",
+    "Obecne klucze:",
+    "",
+    "Energo klidi kartas:",
+    "Aktuell nyckel:",
+    "",
+    "",
+    "",
+    "¸áßÞÛì×ãÕÜëÕ ÚÛîçØ:",
+  },
+  { "(none)",
+    "(keiner)",
+    "",
+    "(nessuno)",
+    "(geen)",
+    "",
+    "(aucune)",
+    "",
+    "(ei)",
+    "(brak)",
+    "",
+    "(kanena)",
+    "(ingen)",
+    "",
+    "",
+    "",
+    "(ÝÕâ)",
+  },
+  { "undisclosed key",
+    "unbekannter Key",
+    "",
+    "",
+    "onbekende key",
+    "",
+    "Clé non-révélée",
+    "",
+    "tuntematon avain",
+    "niejawny klucz",
+    "",
+    "",
+    "ej avslöjad nyckel",
+    "",
+    "",
+    "",
+    "ÝÕØ×ÒÕáâÝëÙ ÚÛîç",
+  },
+  { "Status information...",
+    "Status Informationen...",
+    "",
+    "",
+    "Status informatie...",
+    "",
+    "Statut information...",
+    "",
+    "Tilannetiedot...",
+    "Informacje o stanie...",
+    "",
+    "",
+    "Statusinformation...",
+    "",
+    "",
+    "",
+    "ÁÞáâÞïÝØÕ...",
+  },
+  { "Flush ECM cache",
+    "ECM Zwischenspeicher leeren",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tyhjennä ECM-välimuisti",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¾çØáâØâì Úíè ECM",
+  },
+  { "Really flush ECM cache?",
+    "ECM Zwischenspeicher wirklich leeren?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tyhjennetäänkö ECM-välimuisti?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ ÞçØáâØâì Úíè ECM?",
+  },
+  { "Reload files",
+    "Dateien neu laden",
+    "",
+    "",
+    "Opnieuw laden bestanden?",
+    "",
+    "Recharger les fichiers",
+    "",
+    "Lataa avaintiedostot uudelleen",
+    "Prze³aduj pliki",
+    "",
+    "Ksanadiavasma arxeion",
+    "Ladda om filer",
+    "",
+    "",
+    "",
+    "¿ÕàÕ×ÐÓàãרâì äÐÙÛë",
+  },
+  { "Really reload files?",
+    "Dateien wirklich neu laden?",
+    "",
+    "Ricarica il file delle chiavi?",
+    "Werkelijk opnieuw laden bestanden?",
+    "",
+    "Vraiment recharger les fichiers?",
+    "",
+    "Ladataanko avaintiedostot uudelleen?",
+    "Na pewno prze³adowaæ klucze?",
+    "",
+    "Na diavastoun pali ta arxeia?",
+    "Bekräfta omladdning av filer?",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ ßÕàÕ×ÐÓàãרâì äÐÙÛë?",
+  },
+  { "Active! Can't reload files now",
+    "Aktiv! Kann Dateien jetzt nicht neu laden",
+    "",
+    "Chiave in uso!",
+    "Actief! Kan bestanden niet herladen",
+    "",
+    "Actif ! Je ne peux pas recharger maintenant",
+    "",
+    "Aktiivinen! Uudelleen lataus ei onnistu",
+    "Aktywny! Nie mo¿na teraz prze³adowaæ plików.",
+    "",
+    "Energo! Den ine dinato to ksanadiavasma",
+    "Aktiv! Kan inte ladda om filer nu",
+    "",
+    "",
+    "",
+    "·ÐÝïâ! ½Õ ÜÞÓã ßÕàÕ×ÐÓàãרâì äÐÙÛë",
+  },
+  { "Smartcard",
+    "Smartcard",
+    "",
+    "",
+    "Smartcard",
+    "",
+    "Smartcard",
+    "",
+    "Älykortti",
+    "Smartcard",
+    "",
+    "",
+    "Smartcard",
+    "",
+    "",
+    "",
+    "ºÐàâÞçÚÐ",
+  },
+  { "Reset card",
+    "Karte reseten",
+    "",
+    "",
+    "Reset smartcard",
+    "",
+    "Réinitialiser la carte",
+    "",
+    "Nollaa kortti",
+    "Resetuj kartê",
+    "",
+    "",
+    "Nollställ kortet",
+    "",
+    "",
+    "",
+    "ÁÑàÞáØâì ÚÐàâÞçÚã",
+  },
+  { "Really reset card?",
+    "Karte wirklich reseten?",
+    "",
+    "",
+    "Werkelijk smartcard resetten?",
+    "",
+    "Réinitialiser vraiment la carte?",
+    "",
+    "Nollataanko kortti?",
+    "Na pewno zrestartowaæ kartê?",
+    "",
+    "",
+    "Bekräfta nollställning av kortet?",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÚÐàâÞçÚã?",
+  },
+  { "Smartcard interface",
+    "Smartcard Interface",
+    "",
+    "",
+    "Smartcard interface",
+    "",
+    "Interface Smartcard",
+    "",
+    "Älykorttiliitäntä",
+    "Interfejs Smartcard",
+    "",
+    "",
+    "Smartcard-gränssnitt",
+    "",
+    "",
+    "",
+    "ºÐàâÞçÝëÙ ÜÞÔãÛì",
+  },
+  { "(empty)",
+    "(leer)",
+    "",
+    "(nessuna)",
+    "(leeg)",
+    "",
+    "(vide)",
+    "",
+    "(tyhjä)",
+    "(puste)",
+    "",
+    "",
+    "(tom)",
+    "",
+    "",
+    "",
+    "(ßãáâÞ)",
+  },
+  { "SoftCAM",
+    "SoftCAM",
+    "",
+    "SoftCAM",
+    "SoftCAM",
+    "",
+    "SoftCAM",
+    "",
+    "SoftCAM",
+    "SoftCAM",
+    "",
+    "SoftCAM",
+    "SoftCAM",
+    "",
+    "",
+    "",
+    "SoftCAM",
+  },
+  { "A software emulated CAM",
+    "Ein Software emuliertes CAM",
+    "",
+    "",
+    "In software geëmuleerde CAM",
+    "",
+    "Un logiciel emulateur de CAM",
+    "",
+    "Ohjelmistopohjainen salauksenpurku",
+    "Programowo emulowany CAM",
+    "",
+    "",
+    "En mjukvaruemulerad CAM",
+    "",
+    "",
+    "",
+    "¿àÞÓàÐÜÝëÙ íÜãÛïâÞà CAM",
+  },
+  { "Cryptsystem options...",
+    "Cryptsystem Optionen...",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Salausjärjestelmien asetukset...",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¾ßæØØ ÚàØßâÞáØáâÕÜ...",
+  },
+  { "Cryptsystem options",
+    "Cryptsystem Optionen",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Salausjärjestelmien asetukset",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¾ßæØØ ÚàØßâÞáØáâÕÜ",
+  },
+  { "Message logging...",
+    "Meldungsprotokolierung...",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Viestien tulostus...",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ...",
+  },
+  { "Message logging",
+    "Meldungsprotokolierung",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Viestien tulostus",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ",
+  },
+  { "Log to console",
+    "Meldungen auf Konsole",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tulosta konsoliin",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "ÁÞÞÑéÕÝØï Ò ÚÞÝáÞÛÕ",
+  },
+  { "Log to file",
+    "Meldungen in Datei",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tulosta tiedostoon",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "ÁÞÞÑéÕÝØï Ò äÐÙÛ",
+  },
+  { "Filename",
+    "Dateiname",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tiedoston nimi",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "½Ð×ÒÐÝØÕ äÐÙÛÐ",
+  },
+  { "Log to syslog",
+    "Meldungen in Syslog",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Tulosta systeemilokiin",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "ÁÞÞÑéÕÝØï Ò Syslog",
+  },
+  { "Disable ALL modules",
+    "ALLE Module ausschalten",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Poista kaikki moduulit käytöstä",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "²ëÚÛîçØâì ÒáÕ ÜÞÔãÛØ",
+  },
+  { "Really disable ALL modules?",
+    "Wirklich ALLE Module ausschalten?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Poistetaanko kaikki moduulit käytöstä?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ ÒëÚÛîçØâì ÒáÕ ÜÞÔãÛØ?",
+  },
+  { "Reset ALL modules to default",
+    "ALLE Module auf Standard zurücksetzen",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nollaa kaikki moduulit oletusarvoihin",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "ÁÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî",
+  },
+  { "Really reset ALL modules to default?",
+    "Wirklich ALLE Module auf Standard zurücksetzen?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nollataanko kaikki moduulit oletusarvoihin?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî?",
+  },
+  { "Module",
+    "Modul",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Moduuli",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "¼ÞÔãÛì",
+  },
+  { "Module config",
+    "Modul Einstellungen",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Moduulin asetukset",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "½ÐáâàÞÙÚÐ ÜÞÔãÛï",
+  },
+  { "Reset module to default",
+    "Modul auf Standard zurücksetzen",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nollaa moduuli oletusarvoihin",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "ÁÑàÞá ÜÞÔãÛï ßÞ ãÜÞÛçÐÝØî",
+  },
+  { "Really reset module to default?",
+    "Wirklich Modul auf Standard zurücksetzen?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nollataanko moduuli oletusarvoihin?",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÜÞÔãÛì ßÞ ãÜÞÛçÐÝØî?",
+  },
+  { NULL }
+  };
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..b39b4b7
--- /dev/null
+++ b/i18n.h
@@ -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 ___I18N_H
+#define ___I18N_H
+
+#include 
+
+extern const tI18nPhrase ScPhrases[];
+
+#endif //___I18N_H
diff --git a/log-core.h b/log-core.h
new file mode 100644
index 0000000..31fe10f
--- /dev/null
+++ b/log-core.h
@@ -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/log-sc.h b/log-sc.h
new file mode 100644
index 0000000..b6d2b39
--- /dev/null
+++ b/log-sc.h
@@ -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/log-sys.h b/log-sys.h
new file mode 100644
index 0000000..184f2a8
--- /dev/null
+++ b/log-sys.h
@@ -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/log.c b/log.c
new file mode 100644
index 0000000..58b12ea
--- /dev/null
+++ b/log.c
@@ -0,0 +1,395 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#include "log.h"
+#include "misc.h"
+
+#define LMOD_SUP 32
+#define LMOD_CFG_VALID  0x80000000
+
+struct LogHeader {
+  int c;
+  char stamp[32];
+  char tag[64];
+  };
+
+struct LogConfig logcfg = {
+  1,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 unsigned int lastCrc=0;
+static int lastC=0, lastCount=0;
+static cTimeMs lastTime;
+static cMutex lastMutex;
+
+// -- cLogging -----------------------------------------------------------------
+
+bool cLogging::AddModule(int m, const struct LogModule *lm)
+{
+  if(mName,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;
+}
+
+const struct LogModule *cLogging::GetModule(int c)
+{
+  int m=LMOD(c);
+  return mstamp,sizeof(lh->stamp),"%b %e %T",localtime_r(&tt,&tm_r));
+    int i, o=LOPT(c)&~LMOD_ENABLE;
+    for(i=0; i>=1) if(o&1) break;
+    snprintf(lh->tag,sizeof(lh->tag),"%s.%s",lm->Name,(i>=1 && iOptName[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;
+        }
+      }
+    }
+
+  if(logcfg.logCon)
+    printf("%s [%s] %s\n",lh->stamp,lh->tag,txt);
+
+  if(logcfg.logFile) {
+    if((!logfile && !logfileShutup) || logfileReopen) {
+      logfileMutex.Lock();
+      if(logfileReopen) {
+        logfileReopen=false;
+        if(logfile) {
+          PRINTF(L_GEN_DEBUG,"logfile closed, reopen as '%s'",logcfg.logFilename);
+          fclose(logfile);
+          logfile=0;
+          }
+        }
+      if(!logfile) {
+        logfile=fopen(logcfg.logFilename,"a");
+        if(logfile) {
+          setlinebuf(logfile);
+          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));
+          }
+        }
+      logfileMutex.Unlock();
+      }
+    if(logfile)
+      fprintf(logfile,"%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 mLength()>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=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>=1) if(o&1) break;
+      return (i>=1 && iOptName[i-1]) ? lm->OptName[i-1] : 0;
+      }
+    }
+  return 0;
+}
+
+bool cLogging::GetConfig(cLineBuff *lb)
+{
+  lb->Flush();
+  bool cont=false;
+  for(int m=1; mPrintf("%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 && mName)==l && !strncasecmp(name,lm->Name,l)) {
+        s++;
+        if(!strcasecmp(s,"enable"))
+           return LCLASS(m,1);
+        for(int o=1; oOptSupported&(1<OptName[o-1] && !strcasecmp(s,lm->OptName[o-1]))
+            return LCLASS(m,1<>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,t...)      cLogging::Printf((c),t)
+#define PUTS(c,t)           cLogging::Puts((c),(t))
+#define PUTLB(c,lb)         cLogging::PutLB((c),(lb))
+#define HEXDUMP(c,d,n,t...) cLogging::Dump((c),(d),(n),t)
+#define LDUMP(c,d,n,t...)   cLogging::LineDump((c),(d),(n),t)
+#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(t...)         __llb.Printf(t)
+#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;
+  char logFilename[128];
+  };
+
+extern struct LogConfig logcfg;
+
+struct LogModule {
+  int OptSupported, OptDefault;
+  const char *Name, *OptName[LOPT_NUM];
+  };
+
+struct LogHeader;
+
+class cLogging {
+private:
+  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 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 cLogLineBuff : public cLineBuff {
+private:
+  int c;
+public:
+  cLogLineBuff(int C);
+  ~cLogLineBuff();
+  void Flush(void);
+  };
+
+#endif //___LOG_H
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..e0f83e2
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,288 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#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='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=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-blen0) 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/misc.h b/misc.h
new file mode 100644
index 0000000..d6404a1
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,107 @@
+/*
+ * 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
+
+// ----------------------------------------------------------------
+
+#define WORD(buffer,index,mask) (((buffer[(index)]<<8) + buffer[(index)+1]) & mask)
+#define SCT_LEN(sct) (3+(((sct)[1]&0x0f)<<8)+(sct)[2])
+
+// ----------------------------------------------------------------
+
+#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 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/network.c b/network.c
new file mode 100644
index 0000000..9e87911
--- /dev/null
+++ b/network.c
@@ -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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#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 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/network.h b/network.h
new file mode 100644
index 0000000..713bbe6
--- /dev/null
+++ b/network.h
@@ -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 
+#include 
+#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/openssl-compat.h b/openssl-compat.h
new file mode 100644
index 0000000..ea1ab27
--- /dev/null
+++ b/openssl-compat.h
@@ -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 
+
+#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/opts.h b/opts.h
new file mode 100644
index 0000000..7a43086
--- /dev/null
+++ b/opts.h
@@ -0,0 +1,117 @@
+/*
+ * 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
+
+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/parse.c b/parse.c
new file mode 100644
index 0000000..e03c491
--- /dev/null
+++ b/parse.c
@@ -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 
+#include 
+#include 
+
+#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; jlen) {
+          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]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[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[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 + 2];
+  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 + 2);
+  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/parse.h b/parse.h
new file mode 100644
index 0000000..4d7cb15
--- /dev/null
+++ b/parse.h
@@ -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 {
+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 {
+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/patches/dvb-cwidx-old.diff b/patches/dvb-cwidx-old.diff
new file mode 100644
index 0000000..610f2a9
--- /dev/null
+++ b/patches/dvb-cwidx-old.diff
@@ -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/patches/dvb-cwidx.diff b/patches/dvb-cwidx.diff
new file mode 100644
index 0000000..7e28cd7
--- /dev/null
+++ b/patches/dvb-cwidx.diff
@@ -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 
+ #include 
+ #include 
++#ifndef LINUX_VERSION_CODE
++#include 
++#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/patches/dvb-sct-cc.diff b/patches/dvb-sct-cc.diff
new file mode 100644
index 0000000..a9e7354
--- /dev/null
+++ b/patches/dvb-sct-cc.diff
@@ -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/patches/vdr-1.4.x-sc7.diff b/patches/vdr-1.4.x-sc7.diff
new file mode 100644
index 0000000..b043a43
--- /dev/null
+++ b/patches/vdr-1.4.x-sc7.diff
@@ -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-05-13 18:41:42.000000000 +0200
+@@ -85,10 +85,12 @@
+ class cCiCaProgramData : public cListObject {
+ public:
+   int programNumber;
++  bool modified;
+   cList pidList;
+   cCiCaProgramData(int ProgramNumber)
+   {
+     programNumber = ProgramNumber;
++    modified = true;
+   }
+   };
+ 
+@@ -96,6 +98,8 @@
+ class cCiTransportLayer;
+ class cCiTransportConnection;
+ 
++#define VDR_IS_SC_PATCHED 401
++
+ 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 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-05-13 18:04:04.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();
++                 }
+               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/sc.c b/sc.c
new file mode 100644
index 0000000..9444d89
--- /dev/null
+++ b/sc.c
@@ -0,0 +1,1428 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#ifndef STATICBUILD
+#include 
+#include 
+#include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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<401
+#error Your VDR core is patched with an outdated patch version. Please upgrade to the supplied version.
+#endif
+#endif //APIVERSNUM >= 10500
+#if APIVERSNUMmax) *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 && newValuemax) newValue=min;
+      break;
+    default:
+      if(*valuemax) { *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; iAdd(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; i0) storage[k++]=value[i];
+    return true;
+    }
+  return false;
+}
+
+void cOptMInt::Store(const char *PreStr)
+{
+  char b[256];
+  int p=0;
+  for(int i=0; i1 ? "%x ":"%d ",storage[i]);
+  ScPlugin->SetupStore(FullName(PreStr),p>0?b:0);
+}
+
+void cOptMInt::Create(cOsdMenu *menu)
+{
+  for(int i=0; iAdd(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; iPersistant() && !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; iSet()) res=true;
+        if(opts[i]->Persistant()) opts[i]->Store(preStr);
+        }
+    }
+  return res;
+}
+
+void cOpts::Backup(void)
+{
+  if(opts) {
+    for(int i=0; iBackup();
+    }
+}
+
+void cOpts::Create(cOsdMenu *menu)
+{
+  if(opts) {
+    for(int i=0; iCreate(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; dConfirm(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; iConfirm(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(Get(Current()));
+        if(item) {
+          int o=item->Option();
+          cLogging::SetModuleOption(LCLASS(m,1<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; mConfirm(tr("Really disable ALL modules?"))) {
+        for(int m=1; mConfirm(tr("Really reset ALL modules to default?"))) {
+        for(int m=1; m(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; iConfirm(tr("Really flush ECM cache?"))) {
+        ecmcache.Flush();
+        cLoaders::SaveCache();
+        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()) {
+        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;
+}
+
+void cScSetup::Check(void)
+{
+  if(AutoUpdate==0)
+    PRINTF(L_GEN_WARN,"Keys updates (AU) are disabled.");
+  for(int i=0; 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",AutoUpdate?(AutoUpdate==1?"enabled (active CAIDs)":"enabled (all CAIDs)"):"DISABLED");
+  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 ");
+  LBSTART(L_CORE_LOAD);
+  LBPUT("** ScCaps are"); for(int i=0; iStore(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(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(void)
+{
+  for(int n=cDevice::NumDevices(); --n>=0;) {
+    cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(n));
+    if(dev && dev->Cam() && dev->Cam()->Active()) return true;
+    }
+  return false;
+}
+
+void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on)
+{
+  cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(CardNum));
+  if(dev && dev->Cam()) dev->Cam()->LogEcmStatus(ecm,on);
+}
+
+void cSoftCAM::AddHook(int CardNum, cLogHook *hook)
+{
+  cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(CardNum));
+  if(dev && dev->Cam()) dev->Cam()->AddHook(hook);
+}
+
+bool cSoftCAM::TriggerHook(int CardNum, int id)
+{
+  cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(CardNum));
+  return dev && dev->Cam() && dev->Cam()->TriggerHook(id);
+}
+
+#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 {
+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:
+  tI18nPhrase *phrases;
+#ifndef STATICBUILD
+  cScDlls dlls;
+#endif
+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);
+  virtual void Housekeeping(void);
+  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[] = { "off","active CAIDs","all CAIDs" };
+  ScOpts=new cOpts(0,5);
+  ScOpts->Add(new cOptSel  ("AutoUpdate"   ,"Update keys (AU)"     ,&ScSetup.AutoUpdate,3,logg));
+  ScOpts->Add(new cOptBool ("ConcurrentFF" ,"Concurrent FF streams",&ScSetup.ConcurrentFF));
+  ScOpts->Add(new cOptBool ("LocalPriority","Prefer local systems" ,&ScSetup.LocalPriority));
+  ScOpts->Add(new cOptMInt ("ScCaps"       ,"Active on DVB card"   , ScSetup.ScCaps,MAXSCCAPS,0));
+  ScOpts->Add(new cOptMInt ("CaIgnore"     ,"Ignore CAID"          , ScSetup.CaIgnore,MAXCAIGN,2));
+  LogOpts=new cOpts(0,4);
+  LogOpts->Add(new cOptBool ("LogConsole"  ,"Log to console"      ,&logcfg.logCon));
+  LogOpts->Add(new cOptBool ("LogFile"     ,"Log to file"         ,&logcfg.logFile));
+  LogOpts->Add(new cOptStr  ("LogFileName" ,"Filename"            ,logcfg.logFilename,sizeof(logcfg.logFilename),FileNameChars));
+  LogOpts->Add(new cOptBool ("LogSyslog"   ,"Log to syslog"       ,&logcfg.logSys));
+  phrases=0;
+#ifndef STATICBUILD
+  dlls.Load();
+#endif
+  cScDvbDevice::Capture();
+}
+
+cScPlugin::~cScPlugin()
+{
+  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(APIVERSNUMParse(Name,Value)) ||
+     (LogOpts && LogOpts->Parse(Name,Value)) ||
+     cSystems::ConfigParse(Name,Value)) ;
+  else if(!strcasecmp(Name,"LogConfig")) cLogging::ParseConfig(Value);
+  else return false;
+  return true;
+}
+
+void cScPlugin::Housekeeping(void)
+{
+  for(int n=cDevice::NumDevices(); --n>=0;) {
+    cScDvbDevice *dev=dynamic_cast(cDevice::GetDevice(n));
+    if(dev && dev->Cam()) dev->Cam()->HouseKeeping();
+    }
+  if(Feature.KeyFile()) keys.HouseKeeping();
+}
+
+const char **cScPlugin::SVDRPHelpPages(void)
+{
+  static const char *HelpPages[] = {
+    "RELOAD\n"
+    "    Reload all configuration files.",
+    "LOG  [,...]\n"
+    "    Turn the given message class(es) on or off.",
+    "LOGCFG\n"
+    "    Display available message classes and their status.",
+    NULL
+    };
+  return HelpPages;
+}
+
+cString cScPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+  if(!strcasecmp(Command,"RELOAD")) {
+    if(cSoftCAM::Active()) {
+      ReplyCode=550;
+      return "Softcam active. Can't reload files now";
+      }
+    else {
+      if(cSoftCAM::Load(ConfigDirectory()))
+        return "Files reloaded successfully";
+      else {
+        ReplyCode=901;
+        return "Reloading files not entirely successfull";
+        }
+      }
+    }
+  else if(!strcasecmp(Command,"LOG")) {
+    if(Option && *Option) {
+      char *opt=strdup(Option);
+      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=0) {
+          for(int i=0; i0) return lb.Line();
+    ReplyCode=901; return "No config available";
+    }
+  return NULL;
+}
+
+VDRPLUGINCREATOR(cScPlugin); // Don't touch this!
diff --git a/sc.h b/sc.h
new file mode 100644
index 0000000..dcdcf4d
--- /dev/null
+++ b/sc.h
@@ -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(void);
+  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/scsetup.h b/scsetup.h
new file mode 100644
index 0000000..5790518
--- /dev/null
+++ b/scsetup.h
@@ -0,0 +1,45 @@
+/*
+ * 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;
+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/smartcard.c b/smartcard.c
new file mode 100644
index 0000000..03bb70d
--- /dev/null
+++ b/smartcard.c
@@ -0,0 +1,1472 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#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 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, 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; }
+  };
+
+cSerial::cSerial(const char *DevName, bool invCD, bool InvRST)
+{
+  devName=strdup(DevName);
+  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);
+    }
+}
+
+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(n0 && 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(n0?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(buffCount0) {
+        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 ; idata[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 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->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]==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->D=Dtable[ atr[len]    &0x0F];
+        LBPUT("F=%d D=%f ",Atr->F,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_SERIAL);
+  LBPUT("%d: historical:",id);
+  for(int i=0 ; ihistLen ; i++) LBPUT(" %02x",atr[len+i]);
+  LBFLUSH();
+  LBPUT("%d: historical: '",id);
+  for(int i=0 ; ihistLen ; 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<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[len];
+  if(atr->convention==SM_INDIRECT) {
+    memcpy(tmp,data,len);
+    Invert(tmp,len);
+    data=tmp;
+    }
+  int r=ser->Write(data,len);
+  if(r>0) {
+    unsigned char buff[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)\n",buff,ins);
+        return -1;
+        }
+      if(r>restLen) {
+        LBPUT("data overrun r=%d restLen=%d\n",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")
+{
+  for(int i=0 ; iname,scl->id);
+  scl->next=first;
+  first=scl;
+}
+
+void cSmartCards::LaunchWatcher(void)
+{
+  for(int i=0 ; iOpen()) {
+        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;
+}
+
+void cSmartCards::LoadData(const char *cfgdir)
+{
+  mutex.Lock();
+  dataList.Clear();
+  ConfRead("smartcard data",AddDirectory(cfgdir,DATAFILE));
+  mutex.Unlock();
+}
+
+bool cSmartCards::ParseLine(const char *line, bool fromCache)
+{
+  char *r=index(line,':');
+  if(!r) return false;
+  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)) {
+        dataList.Add(scd);
+        break;
+        }
+      else {
+        delete scd;
+        return false;
+        }
+      }
+    }
+  return true;
+}
+
+cSmartCardData *cSmartCards::FindCardData(cSmartCardData *param)
+{
+  cMutexLock lock(&mutex);
+  for(cSmartCardData *cd=dataList.First(); cd; cd=dataList.Next(cd))
+    if(cd->ident==param->ident && cd->Matches(param))
+      return cd;
+  return 0;
+}
+
+bool cSmartCards::HaveCard(int id)
+{
+  cMutexLock lock(&mutex);
+  while(Running() && firstRun) cond.Wait(mutex);
+  for(int i=0 ; iLock();
+      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 ; iCheckCAR();
+}
+
+bool cSmartCards::CardReset(struct Port *port)
+{
+  if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,port->Clock))
+    return false;
+  if(!DoPTS(port)) {
+    // reset card again and continue without PTS
+    if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,port->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(20);
+  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)
+{
+  const struct Atr *atr=&port->Atr;
+  if(atr->F!=372 || atr->D!=1.0) { // PTS required
+    cSerial *ser=port->Serial;
+    const int id=port->PortNum;
+    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 ; iSerial;
+      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 ; modeSetMode(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(numGetCardInfoStr(str,len);
+    mutex.Unlock();
+    }
+  return res;
+}
+
+void cSmartCards::CardReset(int num)
+{
+  if(numTriggerReset();
+    mutex.Unlock();
+    }
+}
diff --git a/smartcard.h b/smartcard.h
new file mode 100644
index 0000000..5c87de3
--- /dev/null
+++ b/smartcard.h
@@ -0,0 +1,235 @@
+/*
+ * 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 
+#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))
+
+// ----------------------------------------------------------------
+
+#define DEBUG_ISO // debug iso/smartcard functions
+#ifdef DEBUG_ISO
+#define di(x) { (x); }
+#else
+#define di(x) ;
+#endif
+
+#ifdef DEBUG_ISO
+#define DUMP(data,len) { const int l=(len); \
+                         for(int i=0 ; i dataList;
+  //
+  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 bool ParseLine(const char *line, bool fromCache);
+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, bool invCD, bool invRST, int clock);
+  void LaunchWatcher(void);
+  void LoadData(const char *cfgdir);
+  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/support/aes.h b/support/aes.h
new file mode 100644
index 0000000..a6d90ec
--- /dev/null
+++ b/support/aes.h
@@ -0,0 +1,83 @@
+#ifndef HEADER_AES_H
+#define HEADER_AES_H
+
+/*
+#ifdef OPENSSL_NO_AES
+#error AES is disabled.
+#endif
+*/
+
+#define AES_ENCRYPT	1
+#define AES_DECRYPT	0
+
+/* Because array size can't be a const in C, the following two are macros.
+   Both sizes are in bytes. */
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined(_MSC_VER) && !defined(OPENSSL_SYS_WINCE)
+# define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+# define GETU32(p) SWAP(*((u32 *)(p)))
+# define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+#endif
+
+typedef unsigned long u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+
+#define MAXKC   (256/32)
+#define MAXKB   (256/8)
+#define MAXNR   14
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+
+/* This should be a hidden type, but EVP requires that the size be known */
+struct aes_key_st {
+    unsigned long rd_key[4 *(AES_MAXNR + 1)];
+    int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+const char *AES_options(void);
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+	AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+	AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+	const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+	const AES_KEY *key);
+
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
+	const AES_KEY *key, const int enc);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char *ivec, const int enc);
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char *ivec, int *num, const int enc);
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char *ivec, int *num);
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char ivec[AES_BLOCK_SIZE],
+	unsigned char ecount_buf[AES_BLOCK_SIZE],
+	unsigned int *num);
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* !HEADER_AES_H */
diff --git a/support/aes_core.c b/support/aes_core.c
new file mode 100644
index 0000000..189eb90
--- /dev/null
+++ b/support/aes_core.c
@@ -0,0 +1,1425 @@
+#include 
+#include 
+#include 
+#include "aes.h"
+
+static const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+			AES_KEY *key) {
+
+	u32 *rk;
+   	int i = 0;
+	u32 temp;
+
+	if (!userKey || !key)
+		return -1;
+	if (bits != 128 && bits != 192 && bits != 256)
+		return -2;
+
+	rk = key->rd_key;
+
+	if (bits==128)
+		key->rounds = 10;
+	else if (bits==192)
+		key->rounds = 12;
+	else
+		key->rounds = 14;
+
+	rk[0] = GETU32(userKey     );
+	rk[1] = GETU32(userKey +  4);
+	rk[2] = GETU32(userKey +  8);
+	rk[3] = GETU32(userKey + 12);
+	if (bits == 128) {
+		while (1) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			if (++i == 10) {
+				return 0;
+			}
+			rk += 4;
+		}
+	}
+	rk[4] = GETU32(userKey + 16);
+	rk[5] = GETU32(userKey + 20);
+	if (bits == 192) {
+		while (1) {
+			temp = rk[ 5];
+			rk[ 6] = rk[ 0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[ 7] = rk[ 1] ^ rk[ 6];
+			rk[ 8] = rk[ 2] ^ rk[ 7];
+			rk[ 9] = rk[ 3] ^ rk[ 8];
+			if (++i == 8) {
+				return 0;
+			}
+			rk[10] = rk[ 4] ^ rk[ 9];
+			rk[11] = rk[ 5] ^ rk[10];
+			rk += 6;
+		}
+	}
+	rk[6] = GETU32(userKey + 24);
+	rk[7] = GETU32(userKey + 28);
+	if (bits == 256) {
+		while (1) {
+			temp = rk[ 7];
+			rk[ 8] = rk[ 0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[ 9] = rk[ 1] ^ rk[ 8];
+			rk[10] = rk[ 2] ^ rk[ 9];
+			rk[11] = rk[ 3] ^ rk[10];
+			if (++i == 7) {
+				return 0;
+			}
+			temp = rk[11];
+			rk[12] = rk[ 4] ^
+				(Te4[(temp >> 24)       ] & 0xff000000) ^
+				(Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp      ) & 0xff] & 0x000000ff);
+			rk[13] = rk[ 5] ^ rk[12];
+			rk[14] = rk[ 6] ^ rk[13];
+			rk[15] = rk[ 7] ^ rk[14];
+
+			rk += 8;
+        	}
+	}
+	return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+			 AES_KEY *key) {
+
+        u32 *rk;
+	int i, j, status;
+	u32 temp;
+
+	/* first, start with an encryption schedule */
+	status = AES_set_encrypt_key(userKey, bits, key);
+	if (status < 0)
+		return status;
+
+	rk = key->rd_key;
+
+	/* invert the order of the round keys: */
+	for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+		temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+	}
+	/* apply the inverse MixColumn transform to all round keys but the first and the last: */
+	for (i = 1; i < (key->rounds); i++) {
+		rk += 4;
+		rk[0] =
+			Td0[Te4[(rk[0] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[0] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[0]      ) & 0xff] & 0xff];
+		rk[1] =
+			Td0[Te4[(rk[1] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[1] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[1]      ) & 0xff] & 0xff];
+		rk[2] =
+			Td0[Te4[(rk[2] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[2] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[2]      ) & 0xff] & 0xff];
+		rk[3] =
+			Td0[Te4[(rk[3] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[3] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[3]      ) & 0xff] & 0xff];
+	}
+	return 0;
+}
+
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+		 const AES_KEY *key) {
+
+	const u32 *rk;
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	assert(in && out && key);
+	rk = key->rd_key;
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(in     ) ^ rk[0];
+	s1 = GETU32(in +  4) ^ rk[1];
+	s2 = GETU32(in +  8) ^ rk[2];
+	s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+	/* round 1: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+   	/* round 2: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+	/* round 3: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+   	/* round 4: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+	/* round 5: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+   	/* round 6: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+	/* round 7: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+   	/* round 8: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+	/* round 9: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+        }
+    }
+    rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Te0[(s0 >> 24)       ] ^
+            Te1[(s1 >> 16) & 0xff] ^
+            Te2[(s2 >>  8) & 0xff] ^
+            Te3[(s3      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Te0[(s1 >> 24)       ] ^
+            Te1[(s2 >> 16) & 0xff] ^
+            Te2[(s3 >>  8) & 0xff] ^
+            Te3[(s0      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Te0[(s2 >> 24)       ] ^
+            Te1[(s3 >> 16) & 0xff] ^
+            Te2[(s0 >>  8) & 0xff] ^
+            Te3[(s1      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Te0[(s3 >> 24)       ] ^
+            Te1[(s0 >> 16) & 0xff] ^
+            Te2[(s1 >>  8) & 0xff] ^
+            Te3[(s2      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Te0[(t0 >> 24)       ] ^
+            Te1[(t1 >> 16) & 0xff] ^
+            Te2[(t2 >>  8) & 0xff] ^
+            Te3[(t3      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Te0[(t1 >> 24)       ] ^
+            Te1[(t2 >> 16) & 0xff] ^
+            Te2[(t3 >>  8) & 0xff] ^
+            Te3[(t0      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Te0[(t2 >> 24)       ] ^
+            Te1[(t3 >> 16) & 0xff] ^
+            Te2[(t0 >>  8) & 0xff] ^
+            Te3[(t1      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Te0[(t3 >> 24)       ] ^
+            Te1[(t0 >> 16) & 0xff] ^
+            Te2[(t1 >>  8) & 0xff] ^
+            Te3[(t2      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 =
+		(Te4[(t0 >> 24)       ] & 0xff000000) ^
+		(Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t3      ) & 0xff] & 0x000000ff) ^
+		rk[0];
+	PUTU32(out     , s0);
+	s1 =
+		(Te4[(t1 >> 24)       ] & 0xff000000) ^
+		(Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t0      ) & 0xff] & 0x000000ff) ^
+		rk[1];
+	PUTU32(out +  4, s1);
+	s2 =
+		(Te4[(t2 >> 24)       ] & 0xff000000) ^
+		(Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t1      ) & 0xff] & 0x000000ff) ^
+		rk[2];
+	PUTU32(out +  8, s2);
+	s3 =
+		(Te4[(t3 >> 24)       ] & 0xff000000) ^
+		(Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t2      ) & 0xff] & 0x000000ff) ^
+		rk[3];
+	PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+		 const AES_KEY *key) {
+
+	const u32 *rk;
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	assert(in && out && key);
+	rk = key->rd_key;
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+    /* round 1: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+    /* round 2: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+    /* round 3: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+    /* round 4: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+    /* round 5: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+    /* round 6: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+    /* round 7: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+    /* round 8: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+    /* round 9: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+        }
+    }
+	rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Td0[(s0 >> 24)       ] ^
+            Td1[(s3 >> 16) & 0xff] ^
+            Td2[(s2 >>  8) & 0xff] ^
+            Td3[(s1      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Td0[(s1 >> 24)       ] ^
+            Td1[(s0 >> 16) & 0xff] ^
+            Td2[(s3 >>  8) & 0xff] ^
+            Td3[(s2      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Td0[(s2 >> 24)       ] ^
+            Td1[(s1 >> 16) & 0xff] ^
+            Td2[(s0 >>  8) & 0xff] ^
+            Td3[(s3      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Td0[(s3 >> 24)       ] ^
+            Td1[(s2 >> 16) & 0xff] ^
+            Td2[(s1 >>  8) & 0xff] ^
+            Td3[(s0      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Td0[(t0 >> 24)       ] ^
+            Td1[(t3 >> 16) & 0xff] ^
+            Td2[(t2 >>  8) & 0xff] ^
+            Td3[(t1      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Td0[(t1 >> 24)       ] ^
+            Td1[(t0 >> 16) & 0xff] ^
+            Td2[(t3 >>  8) & 0xff] ^
+            Td3[(t2      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Td0[(t2 >> 24)       ] ^
+            Td1[(t1 >> 16) & 0xff] ^
+            Td2[(t0 >>  8) & 0xff] ^
+            Td3[(t3      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Td0[(t3 >> 24)       ] ^
+            Td1[(t2 >> 16) & 0xff] ^
+            Td2[(t1 >>  8) & 0xff] ^
+            Td3[(t0      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+   	s0 =
+   		(Td4[(t0 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t1      ) & 0xff] & 0x000000ff) ^
+   		rk[0];
+	PUTU32(out     , s0);
+   	s1 =
+   		(Td4[(t1 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t2      ) & 0xff] & 0x000000ff) ^
+   		rk[1];
+	PUTU32(out +  4, s1);
+   	s2 =
+   		(Td4[(t2 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t3      ) & 0xff] & 0x000000ff) ^
+   		rk[2];
+	PUTU32(out +  8, s2);
+   	s3 =
+   		(Td4[(t3 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t0      ) & 0xff] & 0x000000ff) ^
+   		rk[3];
+	PUTU32(out + 12, s3);
+}
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+		     const unsigned long length, const AES_KEY *key,
+		     unsigned char *ivec, const int enc) {
+
+	unsigned long n;
+	unsigned long len = length;
+	unsigned char tmp[AES_BLOCK_SIZE];
+
+	assert(in && out && key && ivec);
+	assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
+
+	if (AES_ENCRYPT == enc) {
+		while (len >= AES_BLOCK_SIZE) {
+			for(n=0; n < AES_BLOCK_SIZE; ++n)
+				tmp[n] = in[n] ^ ivec[n];
+			AES_encrypt(tmp, out, key);
+			memcpy(ivec, out, AES_BLOCK_SIZE);
+			len -= AES_BLOCK_SIZE;
+			in += AES_BLOCK_SIZE;
+			out += AES_BLOCK_SIZE;
+		}
+		if (len) {
+			for(n=0; n < len; ++n)
+				tmp[n] = in[n] ^ ivec[n];
+			for(n=len; n < AES_BLOCK_SIZE; ++n)
+				tmp[n] = ivec[n];
+			AES_encrypt(tmp, tmp, key);
+			memcpy(out, tmp, AES_BLOCK_SIZE);
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+		}			
+	} else {
+		while (len >= AES_BLOCK_SIZE) {
+			memcpy(tmp, in, AES_BLOCK_SIZE);
+			AES_decrypt(in, out, key);
+			for(n=0; n < AES_BLOCK_SIZE; ++n)
+				out[n] ^= ivec[n];
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+			len -= AES_BLOCK_SIZE;
+			in += AES_BLOCK_SIZE;
+			out += AES_BLOCK_SIZE;
+		}
+		if (len) {
+			memcpy(tmp, in, AES_BLOCK_SIZE);
+			AES_decrypt(tmp, tmp, key);
+			for(n=0; n < len; ++n)
+				out[n] ^= ivec[n];
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+		}			
+	}
+}
+
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char *ivec, int *num, const int enc) {
+
+	unsigned int n;
+	unsigned long l = length;
+	unsigned char c;
+
+	assert(in && out && key && ivec && num);
+
+	n = *num;
+
+	if (enc) {
+		while (l--) {
+			if (n == 0) {
+				AES_encrypt(ivec, ivec, key);
+			}
+			ivec[n] = *(out++) = *(in++) ^ ivec[n];
+			n = (n+1) % AES_BLOCK_SIZE;
+		}
+	} else {
+		while (l--) {
+			if (n == 0) {
+				AES_encrypt(ivec, ivec, key);
+			}
+			c = *(in);
+			*(out++) = *(in++) ^ ivec[n];
+			ivec[n] = c;
+			n = (n+1) % AES_BLOCK_SIZE;
+		}
+	}
+
+	*num=n;
+}
+
+/* increment counter (128-bit int) by 1 */
+static void AES_ctr128_inc(unsigned char *counter) {
+	unsigned long c;
+
+	/* Grab bottom dword of counter and increment */
+#ifdef L_ENDIAN
+	c = GETU32(counter +  0);
+	c++;
+	PUTU32(counter +  0, c);
+#else
+	c = GETU32(counter + 12);
+	c++;
+	PUTU32(counter + 12, c);
+#endif
+
+	/* if no overflow, we're done */
+	if (c)
+		return;
+
+	/* Grab 1st dword of counter and increment */
+#ifdef L_ENDIAN
+	c = GETU32(counter +  4);
+	c++;
+	PUTU32(counter +  4, c);
+#else
+	c = GETU32(counter +  8);
+	c++;
+	PUTU32(counter +  8, c);
+#endif
+
+	/* if no overflow, we're done */
+	if (c)
+		return;
+
+	/* Grab 2nd dword of counter and increment */
+#ifdef L_ENDIAN
+	c = GETU32(counter +  8);
+	c++;
+	PUTU32(counter +  8, c);
+#else
+	c = GETU32(counter +  4);
+	c++;
+	PUTU32(counter +  4, c);
+#endif
+
+	/* if no overflow, we're done */
+	if (c)
+		return;
+
+	/* Grab top dword of counter and increment */
+#ifdef L_ENDIAN
+	c = GETU32(counter + 12);
+	c++;
+	PUTU32(counter + 12, c);
+#else
+	c = GETU32(counter +  0);
+	c++;
+	PUTU32(counter +  0, c);
+#endif
+
+}
+
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char ivec[AES_BLOCK_SIZE],
+	unsigned char ecount_buf[AES_BLOCK_SIZE],
+	unsigned int *num) {
+
+	unsigned int n;
+	unsigned long l=length;
+
+//	assert(in && out && key && counter && num);
+	assert(in && out && key && num);
+	assert(*num < AES_BLOCK_SIZE);
+
+	n = *num;
+
+	while (l--) {
+		if (n == 0) {
+			AES_encrypt(ivec, ecount_buf, key);
+ 			AES_ctr128_inc(ivec);
+		}
+		*(out++) = *(in++) ^ ecount_buf[n];
+		n = (n+1) % AES_BLOCK_SIZE;
+	}
+
+	*num=n;
+}
+
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
+		     const AES_KEY *key, const int enc) {
+
+        assert(in && out && key);
+	assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
+
+	if (AES_ENCRYPT == enc)
+		AES_encrypt(in, out, key);
+	else
+		AES_decrypt(in, out, key);
+}
+
+const char *AES_version="AES";
+
+const char *AES_options(void) {
+#ifdef FULL_UNROLL
+        return "aes(full)";
+#else   
+        return "aes(partial)";
+#endif
+}
+
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+	const unsigned long length, const AES_KEY *key,
+	unsigned char *ivec, int *num) {
+
+	unsigned int n;
+	unsigned long l=length;
+
+	assert(in && out && key && ivec && num);
+
+	n = *num;
+
+	while (l--) {
+		if (n == 0) {
+			AES_encrypt(ivec, ivec, key);
+		}
+		*(out++) = *(in++) ^ ivec[n];
+		n = (n+1) % AES_BLOCK_SIZE;
+	}
+
+	*num=n;
+}
diff --git a/support/i_cbc.c b/support/i_cbc.c
new file mode 100644
index 0000000..b964437
--- /dev/null
+++ b/support/i_cbc.c
@@ -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/support/i_skey.c b/support/i_skey.c
new file mode 100644
index 0000000..feb9489
--- /dev/null
+++ b/support/i_skey.c
@@ -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/support/idea.h b/support/idea.h
new file mode 100644
index 0000000..7072e81
--- /dev/null
+++ b/support/idea.h
@@ -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 */ /* 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/support/idea_lcl.h b/support/idea_lcl.h
new file mode 100644
index 0000000..463aa36
--- /dev/null
+++ b/support/idea_lcl.h
@@ -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  */
+/* 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 
+ * 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/system-common.c b/system-common.c
new file mode 100644
index 0000000..3011bcc
--- /dev/null
+++ b/system-common.c
@@ -0,0 +1,195 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+
+#include 
+
+#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(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(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[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];
+  const char *sline=line;
+  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;
+    }
+  FormatError("standard",sline);
+  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/system-common.h b/system-common.h
new file mode 100644
index 0000000..6728b49
--- /dev/null
+++ b/system-common.h
@@ -0,0 +1,108 @@
+/*
+ * 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"
+
+#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
+
+// ----------------------------------------------------------------
+
+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);
+  };
+
+// ----------------------------------------------------------------
+
+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/system.c b/system.c
new file mode 100644
index 0000000..cd29695
--- /dev/null
+++ b/system.c
@@ -0,0 +1,618 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+
+#include "sc.h"
+#include "scsetup.h"
+#include "system.h"
+#include "data.h"
+#include "opts.h"
+#include "log-core.h"
+
+// --- cFeature ----------------------------------------------------------------
+
+#define MAX_PHRASES 10
+
+cFeature Feature;
+
+bool cFeature::keyfile=false;
+bool cFeature::smartcard=false;
+bool cFeature::network=false;
+int cFeature::pcount=0;
+const tI18nPhrase *cFeature::phrases[MAX_PHRASES];
+
+void cFeature::AddPhrases(const tI18nPhrase * const Phrases)
+{
+  if(pcountKeyFail(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;
+  lastType=0; 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 *ecms, unsigned short sysId, const unsigned char *data, int len)
+{
+  const int pid=WORD(data,2,0x1FFF);
+  switch(sysId>>8) {
+    case 0x01: // Seca style
+      for(int p=2; pAddData(&data[p+5],10);
+        ecms->Add(n);
+        }
+      break;
+    case 0x05: // Viaccess style
+      for(int p=4; pAdd(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) { // 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 && maxcaid.sys06.startFlag=true;
+        if(check->caid.sys06.startFlag) {
+          if(cur<=max) check->caid.sys06.chids[cur]=chid;
+          if(cur==max) {
+            for(int i=0 ; icaid.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(pk->type!=lastType || pk->id!=lastId || pk->keynr!=lastKeynr) {
+    lastType=pk->type; lastId=pk->id; lastKeynr=pk->keynr;
+    strn0cpy(currentKeyStr,pk->ToString(),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)
+{
+  if(type!=lastType || id!=lastId || keynr!=lastKeynr) {
+    lastType=type; lastId=id; lastKeynr=keynr;
+    if(doLog)
+      PRINTF(L_CORE_ECM,"system: no key found for %c %.2x %.2x",lastType,lastId,lastKeynr);
+    }
+}
+
+// -- 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 0 -> get lowest pri system
+
+  cSystemLink *sl=first, *csl=0;
+  while(sl) {
+    if((!ff || !sl->noFF) && sl->CanHandle(SysId)
+       && sl->pripri>csl->pri)
+           || (oldPri>0 && sl->pripri)
+          )
+       ) 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]!='.' && iopts) 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;
+    switch(s->mode) {
+      case 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;
+      case FAIL1...FAIL2:
+        PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAIL%d (%d)",getpid(),this,s->mode,id);
+        s->mode|=QUEUED;
+        return id;
+      case FAILN:
+      default:
+        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 {
+    switch(s->mode&MASK) {
+      case GOOD:
+        s->mode=FREE;
+        // fall through
+      case FREE:
+      case FAIL1...FAIL2:
+        s->mode=(s->mode&MASK)+1;
+        if(s->modemode);
+          break;
+          }
+        // fall through
+      case FAILN:
+        s->mode=FAILN;
+        LBPUT("(FAILN)");
+        break;
+      }
+    return s->mode;
+    }
+  LBEND();
+}
diff --git a/system.h b/system.h
new file mode 100644
index 0000000..194a442
--- /dev/null
+++ b/system.h
@@ -0,0 +1,276 @@
+/*
+ * 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 
+#include 
+
+#include 
+#include 
+
+#include "data.h"
+#include "misc.h"
+#include "log-core.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, network;
+  static int pcount;
+  static const tI18nPhrase *phrases[];
+public:
+  void AddPhrases(const tI18nPhrase * const Phrases);
+  tI18nPhrase *GetPhrases(void);
+  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;
+  int lastType, lastId, lastKeynr;
+  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 *ecms, unsigned short sysId, 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);
+  };
+
+// ----------------------------------------------------------------
+
+template class cCardInfos : public cSimpleList, public cLoader, cConfRead {
+private:
+  const char *sysname;
+public:
+  cCardInfos(const char *id):cLoader(id) {}
+  virtual ~cCardInfos() {}
+  bool HaveCards(void) { return (this->Count()!=0); }
+  bool Load(const char *cfgdir, const char *SysName, const char *kidName)
+    {
+    sysname=SysName;
+    this->Clear();
+    char type[32];
+    snprintf(type,sizeof(type),"%s card infos",SysName);
+    cString cname=AddDirectory(cfgdir,kidName);
+    ConfRead(type,cname);
+    PRINTF(L_CORE_LOAD,"loaded %d %s cards from %s",this->Count(),SysName,*cname);
+    return HaveCards();
+    }
+  virtual bool ParseLine(const char *line, bool fromCache)
+    {
+      T *k=new T;
+      if(k) {
+        if(k->Parse((char *)line)) {
+          if(!fromCache) { Add(k); k=0; }
+          else {
+            T *o=this->First();
+            while(o) {
+              if(o->Cmp(k)) {
+                Add(k,o); k->Updated(); k=0;
+                Del(o);
+                break;
+                }
+              o=this->Next(o);
+              }
+            }
+          }
+        delete k;
+        return true;
+        }
+      PRINTF(L_GEN_ERROR,"not enough memory for %s card infos!",sysname);
+      return false;
+    }
+  virtual bool Save(FILE *f)
+    {
+    bool res=true;
+    T *k=this->First();
+    while(k) {
+      if(k->IsUpdated() && !k->Save(f)) { res=false; break; }
+      k=this->Next(k);
+      }
+    Modified(!res);
+    return res;
+    }
+  };
+
+// ----------------------------------------------------------------
+
+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/systems/cardclient/aroureos.c b/systems/cardclient/aroureos.c
new file mode 100644
index 0000000..93c44bb
--- /dev/null
+++ b/systems/cardclient/aroureos.c
@@ -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 
+#include 
+
+#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 __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[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/systems/cardclient/camd.c b/systems/cardclient/camd.c
new file mode 100644
index 0000000..56d6b4b
--- /dev/null
+++ b/systems/cardclient/camd.c
@@ -0,0 +1,705 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include "cc.h"
+#include "network.h"
+#include "crypto.h"
+#include "misc.h"
+#include "parse.h"
+
+#include 
+
+#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];
+  int CAID;
+  unsigned char lastEmmReq[32];
+  //
+  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);
+  void HandleEMMRequest(const unsigned char *buff, int len);
+public:
+  cCardClientCommon(const char *Name, bool ConReply, bool LogReply, bool DoAES, int MinMsgLen);
+  virtual bool CanHandle(unsigned short SysId);
+  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;
+  CAID=0;
+  memset(lastEmmReq,0,sizeof(lastEmmReq));
+}
+
+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[minMsgLen];
+  if(len0) { 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::CanHandle(unsigned short SysId)
+{
+  return (emmProcessing && SysId==CAID) || cCardClient::CanHandle(SysId);
+}
+
+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;
+}
+
+void cCardClientCommon::HandleEMMRequest(const unsigned char *buff, int len)
+{
+  if(len>=13 && buff[0]==0 && !CheckNull(buff,len) && memcmp(buff,lastEmmReq,13)) {
+    CAID=buff[1]*256+buff[2];
+    ResetIdSet();
+    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;
+    }
+}
+
+bool cCardClientCommon::ProcessEMM(int caSys, const unsigned char *source)
+{
+  if(emmProcessing && 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[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[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 {
+public:
+  cCardClientCamd33(const char *Name);
+  };
+
+static cCardClientLinkReg __camd33("Camd33");
+
+cCardClientCamd33::cCardClientCamd33(const char *Name)
+:cCardClientCommon(Name,true,true,true,0)
+{}
+
+// -- cCardClientCardd ---------------------------------------------------------
+
+class cCardClientCardd : public cCardClientCommon {
+public:
+  cCardClientCardd(const char *Name);
+  };
+
+static cCardClientLinkReg __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 __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; iudp_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, 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 __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[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)nudp_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>=112) {
+      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;
+        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<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(" ");
+              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(lendata[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 int 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/systems/cardclient/cc.c b/systems/cardclient/cc.c
new file mode 100644
index 0000000..eae431c
--- /dev/null
+++ b/systems/cardclient/cc.c
@@ -0,0 +1,320 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+
+#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(numCaidConnected() && !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, private cConfRead, cSimpleList {
+protected:
+  virtual bool ParseLine(const char *line, bool fromCache);
+public:
+  cSystemLinkCardClient(void);
+  virtual bool CanHandle(unsigned short SysId);
+  virtual cSystem *Create(void);
+  virtual bool Init(const char *cfgdir);
+  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 -----------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+  { "Cardclient: connect immediately",
+    "Cardclient: sofort verbinden",
+    "",
+    "",
+    "Cardclient: direct contact maken",
+    "",
+    "Cardclient: connecter immediatement",
+    "",
+    "Korttiasiakas: yhdistä välittömästi",
+    "Klient karty: pod³±cz natychmiast",
+    "",
+    "",
+    "",
+  },
+  { NULL }
+  };
+
+cSystemLinkCardClient::cSystemLinkCardClient(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptBool("Immediate","Cardclient: connect immediately",&immediate));
+  Feature.AddPhrases(Phrases);
+}
+
+cCardClient *cSystemLinkCardClient::FindBySysId(unsigned short id, cCardClient *cc)
+{
+  if(cc) cc=Next(cc); else cc=First();
+  while(cc) {
+    if(cc->CanHandle(id)) return cc;
+    cc=Next(cc);
+    }
+  return 0;
+}
+
+bool cSystemLinkCardClient::CanHandle(unsigned short SysId)
+{
+  return FindBySysId(SysId,0)!=0;
+}
+
+cSystem *cSystemLinkCardClient::Create(void)
+{
+  return new cSystemCardClient();
+}
+
+bool cSystemLinkCardClient::Init(const char *cfgdir)
+{
+  Clear();
+  ConfRead("cardclient config",AddDirectory(cfgdir,CONF_FILE));
+  PRINTF(L_CC_CORE,"created %d client(s)",Count());
+  return true;
+}
+
+bool cSystemLinkCardClient::ParseLine(const char *line, bool fromCache)
+{
+  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)
+{
+  Clear();
+}
diff --git a/systems/cardclient/cc.h b/systems/cardclient/cc.h
new file mode 100644
index 0000000..ee81783
--- /dev/null
+++ b/systems/cardclient/cc.h
@@ -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 
+#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 cSimpleItem, 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 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/systems/cardclient/cc.mk b/systems/cardclient/cc.mk
new file mode 100644
index 0000000..e220060
--- /dev/null
+++ b/systems/cardclient/cc.mk
@@ -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/systems/cardclient/gbox.c b/systems/cardclient/gbox.c
new file mode 100644
index 0000000..fa7c88a
--- /dev/null
+++ b/systems/cardclient/gbox.c
@@ -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 
+
+#include 
+
+#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 __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=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)
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "cc.h"
+#include "network.h"
+#include "misc.h"
+#include "parse.h"
+
+#include 
+
+#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 __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) || 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 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 0x18: if(caId==0x1801) {
+                     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(" ");
+            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/systems/cardclient/radegast.c b/systems/cardclient/radegast.c
new file mode 100644
index 0000000..e497df0
--- /dev/null
+++ b/systems/cardclient/radegast.c
@@ -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 
+#include 
+
+#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 __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>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-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(pos0 && 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=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; icaId>>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); l0) {
+          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/systems/conax/conax.c b/systems/conax/conax.c
new file mode 100644
index 0000000..eced1a8
--- /dev/null
+++ b/systems/conax/conax.c
@@ -0,0 +1,191 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include "system.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(iGet(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 KeyReg;
+
+cPlainKeyConax::cPlainKeyConax(bool Super)
+:cBNKey(Super)
+{}
+
+bool cPlainKeyConax::Parse(const char *line)
+{
+  unsigned char sid, skey[PLAINLEN_CONAX];
+  const char *sline=line;
+  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;
+    }
+  FormatError("conax",sline);
+  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/systems/conax/conax.mk b/systems/conax/conax.mk
new file mode 100644
index 0000000..27d38dd
--- /dev/null
+++ b/systems/conax/conax.mk
@@ -0,0 +1,5 @@
+#
+# Conax
+#
+TARGET = conax
+OBJS   = conax.o
diff --git a/systems/constcw/constcw.c b/systems/constcw/constcw.c
new file mode 100644
index 0000000..1a5fc67
--- /dev/null
+++ b/systems/constcw/constcw.c
@@ -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 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#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 KeyReg;
+
+cPlainKeyConstCw::cPlainKeyConstCw(bool Super)
+:cHexKey(Super)
+{}
+
+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];
+  const char *sline=line;
+  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;
+        }
+      }
+    }
+  FormatError("constcw",sline);
+  return false;
+}
+
+cString cPlainKeyConstCw::PrintKeyNr(void)
+{
+  return 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/systems/constcw/constcw.mk b/systems/constcw/constcw.mk
new file mode 100644
index 0000000..845f92d
--- /dev/null
+++ b/systems/constcw/constcw.mk
@@ -0,0 +1,5 @@
+#
+# Const CW
+#
+TARGET = constcw
+OBJS   = constcw.o
diff --git a/systems/cryptoworks/cryptoworks.c b/systems/cryptoworks/cryptoworks.c
new file mode 100644
index 0000000..c807d33
--- /dev/null
+++ b/systems/cryptoworks/cryptoworks.c
@@ -0,0 +1,559 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+
+#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; i7) {
+      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[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>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 KeyReg;
+
+cPlainKeyCryptoworks::cPlainKeyCryptoworks(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyCryptoworks::IsBNKey(void) const
+{
+  return TYPE(keynr)==0x10;
+}
+
+bool cPlainKeyCryptoworks::Parse(const char *line)
+{
+  const char *sline=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[keylen];
+      if(GetHex(line,skey,keylen)) {
+        SetBinKey(skey,keylen);
+        return true;
+        }
+      }
+    }
+  FormatError("cryptoworks",sline);
+  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; icaId,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; icaId,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
+#include 
+
+#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 KeyReg;
+
+// -- cIrdCardInfo -------------------------------------------------------------
+
+class cIrdCardInfo : public cProviderIrdeto, public cCardIrdeto {
+private:
+  bool updated;
+public:
+  unsigned char PMK[8], HMK[10];
+  bool haveHMK;
+  //
+  cIrdCardInfo(void);
+  bool Parse(const char *line);
+  bool Save(FILE *f);
+  bool Update(unsigned char *pmk, unsigned char *id);
+  bool IsUpdated(void) { return updated; }
+  void Updated(void) { updated=true; }
+  bool Cmp(cIrdCardInfo *ci);
+  };
+
+cIrdCardInfo::cIrdCardInfo(void)
+{
+  updated=false;
+}
+
+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));
+}
+
+bool cIrdCardInfo::Save(FILE *f)
+{
+  char s1[20], s2[20], s3[20], s4[20];
+  fprintf(f,"%s %s %02x %s %s\n",
+     HexStr(s1,hexSer,sizeof(hexSer)),HexStr(s2,HMK,sizeof(HMK)),provBase,
+     HexStr(s3,provId,sizeof(provId)),HexStr(s4,PMK,sizeof(PMK)));
+  return ferror(f)==0;
+}
+
+bool cIrdCardInfo::Cmp(cIrdCardInfo *ci)
+{
+  return (!memcmp(hexSer,ci->hexSer,sizeof(hexSer)) &&
+          !memcmp(HMK,ci->HMK,sizeof(HMK)));
+}
+
+bool cIrdCardInfo::Update(unsigned char *pmk, unsigned char *id)
+{
+  bool res=false;
+  char str[20], str2[12];
+  if(memcmp(PMK,pmk,sizeof(PMK))) {
+    PRINTF(L_GEN_INFO,"new PMK for I card %s: %s",HexStr(str2,hexSer,sizeof(hexSer)),KeyStr(str,pmk));
+    memcpy(PMK,pmk,sizeof(PMK));
+    Updated(); res=true;
+    }
+  if(id && memcmp(provId,id,sizeof(provId))) {
+    PRINTF(L_GEN_INFO,"new PrvID for I card %s: %s",HexStr(str2,hexSer,sizeof(hexSer)),HexStr(str,id,sizeof(provId)));
+    memcpy(provId,id,sizeof(provId));
+    Updated(); res=true;
+    }
+  return res;
+}
+
+// -- cIrdCardInfos ------------------------------------------------------------
+
+class cIrdCardInfos : public cCardInfos {
+public:
+  cIrdCardInfos(void):cCardInfos(SYSTEM_NAME) {}
+  bool Update(cIrdCardInfo *ci, unsigned char *pmk, unsigned char *id);
+  };
+
+static cIrdCardInfos Icards;
+
+bool cIrdCardInfos::Update(cIrdCardInfo *ci, unsigned char *pmk, unsigned char *id)
+{
+  bool res=ci->Update(pmk,id);
+  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; countGet(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>6)+1; // key counter
+        if(nlen!=k*9) { badnano=true; break; }
+        for(i=0 ; i0 && (id[0]!=lastKey || numKeys>1)) || mk) {
+    memcpy(savebuf,buffer,n); // save the buffer
+    cIrdCardInfo *ci=Icards.First();
+    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 ; iPMK,date);
+          }
+        unsigned char chkkey[max(sizeof(ci->PMK),sizeof(ci->HMK))];
+        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 ; iprovBase,id[i],pk[i],8)) NewKey();
+            }
+          if(mk) {
+            FoundKey();
+            if(Icards.Update(ci,mk,prvId[0]?prvId:0)) NewKey();
+            }
+          cLoaders::SaveCache();
+          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; }
+  virtual bool Init(const char *cfgdir);
+  };
+
+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);
+}
+
+bool cSystemLinkIrd::Init(const char *cfgdir)
+{
+  Icards.Load(cfgdir,SYSTEM_NAME,"Ird-Beta.KID");
+  return true;
+}
diff --git a/systems/irdeto/irdeto.mk b/systems/irdeto/irdeto.mk
new file mode 100644
index 0000000..7fff9f5
--- /dev/null
+++ b/systems/irdeto/irdeto.mk
@@ -0,0 +1,5 @@
+#
+# Irdeto
+#
+TARGET = irdeto
+OBJS   = irdeto.o
diff --git a/systems/nagra/cpu.c b/systems/nagra/cpu.c
new file mode 100644
index 0000000..e9af43f
--- /dev/null
+++ b/systems/nagra/cpu.c
@@ -0,0 +1,1048 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include "data.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define LOGLBPUT(t...)  loglb->Printf(t)
+#define CCLOGLBPUT(t...)  do { if(doDisAsm) loglb->Printf(t); } 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 && eaMap()) {
+    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);
+}
+
+unsigned char cMapRom::Get(unsigned short ea)
+{
+  return (ea>=offset && ea=offset && eaMap()) {
+    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 && ea=offset+otpSize && eaIsFine()) {
+    if(nextMapperloglb=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");
+  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;
+}
+
+void c6805::ClearMapper(void)
+{
+  for(int i=0; iGet(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)
+{
+  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)
+{
+  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)
+{
+  pc=addr;
+}
+
+void c6805::PopPc(void)
+{
+  poppc();
+}
+
+void c6805::PopCr(void)
+{
+  cr=pop();
+}
+
+void c6805::AddBreakpoint(unsigned short addr)
+{
+  if(numBp0)?"%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
+
+  disAsmLogClass=L_SYS_DISASM;
+  if(LOG(L_SYS_DISASM)) doDisAsm=true;
+  else {
+    doDisAsm=false;
+    if(LOG(L_SYS_DISASM80)) disAsmLogClass=L_SYS_DISASM80;
+    }
+  PRINTF(disAsmLogClass,"cr:-pc- aa xx yy dr -sp- VHINZC -mem@pc- -mem@sp-");
+
+  int count=0;
+  while (1) {
+    if(spspLow && sp>spHi) {
+      PRINTF(L_SYS_EMU,"stack underflow (count=%d)",count);
+      return 1;
+      }
+    count++;
+
+    if(!LOG(L_SYS_DISASM) && LOG(L_SYS_DISASM80)) {
+      bool flag=(pc>=0x80 && pc<0xE0);
+      if(doDisAsm && !flag) PRINTF(disAsmLogClass,"[...]");
+      doDisAsm=flag;
+      }
+
+    CCLOGLBPUT("%02x:%04x %02x %02x %02x %02x %04x %c%c%c%c%c%c %02x%02x%02x%02x %02x%02x%02x%02x ",
+               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));
+
+    Stepper();
+    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';
+ 
+    // check pre-bytes
+    switch(ins) {
+      case 0x31: // use SP indexed or indirect paged mode (ST19)
+        ins=Get(pc++);
+        switch(ins) {
+          case 0x22 ... 0x27: vbra=true; PRINTF(L_SYS_EMU,"WARN: V-flag not yet calculated"); break;
+          case 0x75:
+          case 0x8D:
+          case 0xC0 ... 0xCB:
+          case 0xCE ... 0xCF:
+          case 0xD0 ... 0xDB:
+          case 0xDE ... 0xDF: paged=true; indirect=true; break;
+          case 0xE0 ... 0xEB:
+          case 0xEE ... 0xEF: idx=sp; xi='S'; break;
+          }
+        break;
+      case 0x32: // use indirect SP indexed or indirect paged Y indexed mode (ST19)
+        ins=Get(pc++);
+        switch(ins) {
+          case 0x22 ... 0x27: vbra=true; PRINTF(L_SYS_EMU,"WARN: V-flag not yet calculated"); indirect=true; break;
+          case 0xC3:
+          case 0xCE ... 0xCF:
+          case 0xD0 ... 0xDB:
+          case 0xDE ... 0xDF: paged=true; indirect=true; ex=&y; idx=*ex; xs='Y'; xi='Y'; break;
+          case 0xE0 ... 0xEB:
+          case 0xEE ... 0xEF: indirect=true; idx=sp; xi='S'; break;
+          }
+        break;
+      case 0x91: // use Y register with indirect addr mode (ST7)
+        indirect=true;
+        // fall through
+      case 0x90: // use Y register (ST7)
+        ex=&y; idx=*ex; xs='Y'; xi='Y';
+        ins=Get(pc++);
+        break;
+      case 0x92: // use indirect addr mode (ST7)
+        indirect=true;
+        ins=Get(pc++);
+        break;
+      }
+
+    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);
+          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;
+        }
+      }
+
+    // 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++; tst(op); break;
+      case 0x3A: // DEC
+      case 0x4A:
+      case 0x5A:
+      case 0x6A:
+      case 0x7A:
+        op--; 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; if(!op) cc.c=0; 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
+        {
+        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
+        {
+        int bit=(ins&0x0F)>>1;
+        CCLOGLBPUT(",#%x",bit);
+        if(ins&0x01) op &= ~(1< ",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);
+        return 3;
+      default:
+        PRINTF(L_SYS_EMU,"unsupported instruction 0x%02x (count=%d)",ins,count);
+        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;
+        }
+      }
+    PUTLB(disAsmLogClass,loglb);
+
+    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;
+  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;
+  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/systems/nagra/cpu.h b/systems/nagra/cpu.h
new file mode 100644
index 0000000..4f94e8d
--- /dev/null
+++ b/systems/nagra/cpu.h
@@ -0,0 +1,157 @@
+/*
+ * 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 {
+private:
+  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 4
+#define MAX_MAPPER      8
+#define MAX_PAGES       4
+#define PAGE_SIZE       32*1024
+
+#define bitset(d,bit) (((d)>>(bit))&1)
+
+class c6805 {
+private:
+  unsigned short pc, sp, spHi, spLow;
+  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;
+  //
+  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;
+  //
+  int Run(int max_count);
+  void AddBreakpoint(unsigned short addr);
+  void ClearBreakpoints(void);
+  bool AddMapper(cMap *map, unsigned short start, int size, unsigned char seg=0);
+  void ResetMapper(void);
+  unsigned char Get(unsigned short ea) const;
+  unsigned char Get(unsigned char seg, unsigned short ea) const;
+  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 short GetPc(void) const { return pc; }
+  void PopPc(void);
+  void PopCr(void);
+  virtual void Stepper(void)=0;
+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();
+  };
+
+#endif
diff --git a/systems/nagra/log-nagra.h b/systems/nagra/log-nagra.h
new file mode 100644
index 0000000..68468ed
--- /dev/null
+++ b/systems/nagra/log-nagra.h
@@ -0,0 +1,37 @@
+/*
+ * 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_ALL      LALL(L_SYS_RAWEMM)
+
+#define bprint(a) {fprintf(stdout, #a "="); BN_print_fp(stdout,a); fprintf(stdout,"\n");}
+
+#endif
+
diff --git a/systems/nagra/nagra.c b/systems/nagra/nagra.c
new file mode 100644
index 0000000..74d60a4
--- /dev/null
+++ b/systems/nagra/nagra.c
@@ -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 
+#include 
+#include 
+
+#include "nagra.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" }
+  };
+ADD_MODULE(L_SYS,lm_sys)
+
+int minEcmTime=400; // ms
+
+#ifndef TESTER
+
+// -- 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 KeyReg;
+
+cPlainKeyNagra::cPlainKeyNagra(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyNagra::IsBNKey(void) const
+{
+  switch(keynr & 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;
+}
+
+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];
+  const char *sline=line;
+  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;
+      }
+    }
+  FormatError("nagra",sline);
+  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;
+}
+
+#endif // TESTER
+
+// -- 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/systems/nagra/nagra.h b/systems/nagra/nagra.h
new file mode 100644
index 0000000..e794b26
--- /dev/null
+++ b/systems/nagra/nagra.h
@@ -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 __NAGRA_NAGRA_H
+#define __NAGRA_NAGRA_H
+
+#include "system-common.h"
+#include "crypto.h"
+
+// ----------------------------------------------------------------
+
+#define SYSTEM_NAGRA         0x1800
+#define SYSTEM_NAGRA2        0x1801
+#define SYSTEM_NAGRA_BEV     0x1234
+
+extern int minEcmTime;
+
+// ----------------------------------------------------------------
+
+// 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
+
+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);
+  };
+
+// ----------------------------------------------------------------
+
+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/systems/nagra/nagra.mk b/systems/nagra/nagra.mk
new file mode 100644
index 0000000..a8e8430
--- /dev/null
+++ b/systems/nagra/nagra.mk
@@ -0,0 +1,12 @@
+#
+# Nagra
+#
+TARGET = nagra
+OBJS   = nagra.o nagra1.o nagra2.o cpu.o
+LIBS   = -lcrypto
+CLEAN_RM = nagra2-prov.c
+
+nagra2-prov.c:
+	echo >$@ "/* generated file, do not edit */"
+	find -name "nagra2-[0-9][0-9][0-9][0-9].c" -printf '#include "%f"\n' >>$@
+	@rm .dependencies
diff --git a/systems/nagra/nagra1.c b/systems/nagra/nagra1.c
new file mode 100644
index 0000000..473573e
--- /dev/null
+++ b/systems/nagra/nagra1.c
@@ -0,0 +1,1005 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include "system.h"
+#include "misc.h"
+#include "opts.h"
+#include "helper.h"
+
+#include "nagra.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 {
+protected:
+  virtual void Stepper(void);
+  bool CoreInitSetup(void);
+  bool DoMaps(bool hasExt);
+  };
+
+void cEmuRom3Core::Stepper(void)
+{
+  int rnd=random();
+  unsigned char mem4=Get(0x04);
+  if(!bitset(mem4,2)) {
+    Set(0x06,(rnd&0xff00)>>8);
+    Set(0x07,rnd&0xff);
+    }
+}
+
+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 ... 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>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[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[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=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 ... 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(iMatches(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();
+            }
+          cLoaders::SaveCache();
+          }
+        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();
+      cLoaders::SaveCache();
+      }
+    }
+}
+
+// -- cSystemLinkNagra ---------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+  { "Nagra: min. ECM processing time",
+    "Nagra: min. ECM Bearbeitungszeit",
+    "",
+    "",
+    "Nagra: min. ECM bewerkingstijd",
+    "",
+    "Nagra: min. durée du processus ECM",
+    "",
+    "Nagra: Min. ECM-prosessointiaika",
+    "Nagra: min. czas przetwarzania ECM",
+    "",
+    "",
+    "",
+  },
+  { NULL }
+  };
+
+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","Nagra: min. ECM processing time",&minEcmTime,0,5000));
+  Feature.NeedsKeyFile();
+  Feature.AddPhrases(Phrases);
+}
+
+bool cSystemLinkNagra::CanHandle(unsigned short SysId)
+{
+  return SysId==SYSTEM_NAGRA; // || SysId==SYSTEM_NAGRA_BEV;
+}
+
+#endif //TESTER
diff --git a/systems/nagra/nagra2-0101.c b/systems/nagra/nagra2-0101.c
new file mode 100644
index 0000000..1eeb497
--- /dev/null
+++ b/systems/nagra/nagra2-0101.c
@@ -0,0 +1,324 @@
+/*
+ * 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
+ */
+
+// -- cAuxSrv ------------------------------------------------------------------
+
+/*
+#define HAS_AUXSRV
+
+static int auxPort=7777;
+static char auxAddr[80]="localhost";
+static char auxPassword[250]="auxserver";
+
+class cAuxSrv : public cMutex {
+private:
+  cNetSocket so;
+  //
+  bool Login(void);
+public:
+  cAuxSrv(void);
+  bool 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) return true;
+    PRINTF(L_SYS_MAP,"auxsrv: login write failed");
+    }
+  so.Disconnect();
+  return false;
+}
+
+bool cAuxSrv::Map(int map, unsigned char *data, int len, int outlen)
+{
+  if(len>500 || outlen>500) return false;
+  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 l=buff[2]*256+buff[3];
+          if(len>=l+5 && l==outlen+1) {
+            if(buff[l+4]==0xFF) {
+              memcpy(data,buff+5,outlen);
+              return true;
+              }
+            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 want=%d)",map,l-1,outlen);
+          }
+        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 false;
+}
+*/
+
+// -- cMap0101 ----------------------------------------------------------------
+
+#include "nagra2-map57.c"
+
+class cMap0101 : public cMapCore {
+private:
+  static const unsigned char primes[];
+  unsigned char residues[53];
+//  cAuxSrv aux;
+  cN2Map57 map57;
+protected:
+  void DoMap(int f, unsigned char *data=0, int l=0);
+public:
+  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
+  };
+
+cMap0101::cMap0101(void)
+{
+}
+
+void cMap0101::DoMap(int f, unsigned char *data, int l)
+{
+  PRINTF(L_SYS_MAP,"0101: calling function %02X",f);
+  switch(f) {
+    case 0x3b:
+      MakeJ();
+      BN_zero(R);
+      BN_set_bit(R,132); // or 66*wordsize ?
+      BN_mod(H,R,D,ctx);
+      for(int i=0; i<4; i++) MonMul(H,H,H);
+      MonMul(B,A,H);
+      break;
+    case 0x4d:
+      for(int i=0; i<53; i++) residues[i]=BN_mod_word(A,primes[i]);
+      break;
+    case 0x4e:
+      {
+      bool isPrime;
+      do {
+        BN_add_word(A,2);
+        isPrime=true;
+        for(int i=0; i<53; i++) {
+          residues[i]+=2;
+          residues[i]%=primes[i];
+          if(residues[i]==0) isPrime=false;
+          }
+        } while(!isPrime);
+      break;
+      }
+    case 0x57:
+      map57.Map57(data);
+      //aux.Map(0x57,data,l,l);
+      break;
+    default:
+      if(!cMapCore::DoMap(f,data,l))
+        PRINTF(L_SYS_MAP,"0101: unsupported call %02x",f);
+      break;
+    }
+}
+
+// -- cN2Prov0101 ----------------------------------------------------------------
+
+class cN2Prov0101 : public cN2Prov, public cN2Emu, private cMap0101 {
+protected:
+  virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw);
+  virtual void Stepper(void);
+public:
+  cN2Prov0101(int Id, int Flags):cN2Prov(Id,Flags) {}
+  virtual int ProcessBx(unsigned char *data, int len, int pos);
+  };
+
+static cN2ProvLinkReg staticPL0101;
+
+bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
+{
+  if(algo==0x40) {
+    memcpy(hw,hd,3);
+    ExpandInput(hw);
+    hw[0x18]|=1; hw[0x40]|=1;
+    SetWordSize(2);
+    ImportReg(IMPORT_A,hw,3);
+    ImportReg(IMPORT_D,hw+24);
+    DoMap(0x3b);
+    ExportReg(EXPORT_C,hw);
+    ImportReg(IMPORT_A,hw+40,3);
+    ImportReg(IMPORT_D,hw+64);
+    DoMap(0x3b);
+    ExportReg(EXPORT_C,hw+40);
+    DoMap(0x43);
+    DoMap(0x44,hw);
+    DoMap(0x44,hw+64);
+    hw[0]=hw[64+4]; // ctx.h3&0xFF
+    hw[1]=hw[64+5]; //(ctx.h3>>8)&0xFF
+    memset(&hw[2],0,128-2);
+    return true;
+    }
+  else if(algo==0x60) { // map 4D/4E/57
+    memcpy(hw,hd,5);
+    ExpandInput(hw);
+    hw[127]|=0x80; hw[0]|=0x01;
+    SetWordSize(16);
+    ImportReg(IMPORT_A,hw);
+    DoMap(0x4d);
+    DoMap(0x4e);
+    ExportReg(EXPORT_A,hw,16,true);
+    ImportReg(IMPORT_A,hw);
+    DoMap(0x4e);
+    ExportReg(EXPORT_A,hw);
+    DoMap(0x57,hw,128);
+    return true;
+    }
+
+  PRINTF(L_SYS_ECM,"%04X: unknown MECM algo %02x",id,algo);
+  return false;
+}
+
+int cN2Prov0101::ProcessBx(unsigned char *data, int len, int pos)
+{
+  if(Init(0x0101,102)) {
+    SetMem(0x80,data,len);
+    SetPc(0x80+pos);
+    SetSp(0x0FFF,0x0FF0);
+    ClearBreakpoints();
+    AddBreakpoint(0x0000);
+    AddBreakpoint(0x9569);
+    AddBreakpoint(0xA822); // map handler
+    while(!Run(1000)) {
+      if(GetPc()==0x9569) {
+        GetMem(0x80,data,len);
+        return max((int)a,6);
+        }
+      if(GetPc()==0x0000)
+        break;
+      if(GetPc()==0xA822) {
+        int size=wordsize<<3;
+        unsigned char tmp[size];
+        unsigned short addr=(Get(0x44)<<8)+Get(0x45);
+        switch(a) {
+          case SETSIZE:
+            SetWordSize(Get(0x48)); break;
+          case IMPORT_A ... IMPORT_D:
+            GetMem(addr,tmp,size,0); ImportReg(a,tmp); break;
+          case EXPORT_A ... EXPORT_D:
+            ExportReg(a,tmp); SetMem(addr,tmp,size,0); break;
+          case 0x0F:
+            {
+            unsigned char tmp2[size];
+            GetMem(addr,tmp2,size,0);
+            ExportReg(EXPORT_A,tmp);
+            ImportReg(IMPORT_A,tmp2);
+            SetMem(addr,tmp,size,0);
+            break;
+            }
+          default:
+            PRINTF(L_SYS_EMM,"%04X: unrecognized map call %02x",id,a);
+            return -1;
+          }
+        PopCr(); PopPc();
+        }
+      }
+    }
+  return -1;
+}
+
+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);
+        }
+      }
+    }
+}
+
+// -- cN2Prov0901 --------------------------------------------------------------
+
+class cN2Prov0901 : public cN2Prov0101 {
+public:
+  cN2Prov0901(int Id, int Flags):cN2Prov0101(Id,Flags) {}
+  virtual int ProcessBx(unsigned char *data, int len, int pos);
+  };
+
+static cN2ProvLinkReg staticPL0901;
+
+int cN2Prov0901::ProcessBx(unsigned char *data, int len, int pos)
+{
+  if(Init(0x0801,102)) {
+    return cN2Prov0101::ProcessBx(data,len,pos);
+    }
+  return -1;
+}
diff --git a/systems/nagra/nagra2-0501.c b/systems/nagra/nagra2-0501.c
new file mode 100644
index 0000000..bc2fec2
--- /dev/null
+++ b/systems/nagra/nagra2-0501.c
@@ -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
+ */
+
+// -- cMap0501 -----------------------------------------------------------------
+
+class cMap0501 : public cMapCore {
+private:
+protected:
+  void DoMap(int f, unsigned char *data=0, int l=0);
+public:
+  cMap0501(void);
+  };
+
+cMap0501::cMap0501(void)
+{
+}
+
+void cMap0501::DoMap(int f, unsigned char *data, int l)
+{
+  PRINTF(L_SYS_MAP,"0501: calling function %02X",f);
+  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:
+      if(!cMapCore::DoMap(f,data,l))
+        PRINTF(L_SYS_MAP,"0501: unsupported call %02x",f);
+      break;
+    }
+}
+
+// -- cN2Prov0501 --------------------------------------------------------------
+
+class cN2Prov0501 : public cN2Prov, private cMap0501 {
+protected:
+  virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw);
+  virtual bool NeedsCwSwap(void) { return true; }
+public:
+  cN2Prov0501(int Id, int Flags):cN2Prov(Id,Flags) {}
+  };
+
+static cN2ProvLinkReg staticPL0501;
+
+bool cN2Prov0501::Algo(int algo, const unsigned char *hd, unsigned char *hw)
+{
+  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 true;
+    }
+
+  PRINTF(L_SYS_ECM,"0501: unknown MECM algo %02x",algo);
+  return false;
+}
+
diff --git a/systems/nagra/nagra2-1101.c b/systems/nagra/nagra2-1101.c
new file mode 100644
index 0000000..2553cae
--- /dev/null
+++ b/systems/nagra/nagra2-1101.c
@@ -0,0 +1,29 @@
+/*
+ * 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
+ */
+
+// -- cN2Prov1101 ----------------------------------------------------------------
+
+class cN2Prov1101 : public cN2Prov {
+protected:
+  virtual bool NeedsCwSwap(void) { return true; }
+public:
+  cN2Prov1101(int Id, int Flags):cN2Prov(Id,Flags) {}
+  };
+
+static cN2ProvLinkReg staticPL1101;
diff --git a/systems/nagra/nagra2-4101.c b/systems/nagra/nagra2-4101.c
new file mode 100644
index 0000000..a68e08f
--- /dev/null
+++ b/systems/nagra/nagra2-4101.c
@@ -0,0 +1,47 @@
+/*
+ * 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
+ */
+
+// -- cN2Prov4101 ----------------------------------------------------------------
+
+class cN2Prov4101 : public cN2Prov {
+public:
+  cN2Prov4101(int Id, int Flags):cN2Prov(Id,Flags) {}
+  virtual bool PostProcAU(int id, unsigned char *data);
+  };
+
+static cN2ProvLinkReg staticPL4101;
+
+bool cN2Prov4101::PostProcAU(int id, unsigned char *data)
+{
+  if(data[1]==0x01) {
+    cPlainKey *pk;
+    if(!(pk=keys.FindKey('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;
+}
diff --git a/systems/nagra/nagra2-map57.c b/systems/nagra/nagra2-map57.c
new file mode 100644
index 0000000..a1408a9
--- /dev/null
+++ b/systems/nagra/nagra2-map57.c
@@ -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, 0x80);
+  RotateBytes(tmpdata, 0x80);
+  BN_bin2bn(tmpdata, 16, var78);
+  BN_bin2bn(tmpdata+0x10, 16, varb8);
+  BN_bin2bn(tmpdata+0x20, 16, var98);
+  BN_bin2bn(tmpdata+0x40, 16, vard8);
+  BN_bin2bn(tmpdata+0x60, 16, var38);
+  BN_bin2bn(tmpdata+0x70, 16, var58);
+
+  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 + (8<<2))= *(unsigned int *)(res + (11<<2));
+  *(unsigned int *)(data + (9<<2))= *(unsigned int *)(res + (10<<2));
+  *(unsigned int *)(data + (10<<2))= *(unsigned int *)(res + (9<<2));
+  *(unsigned int *)(data + (11<<2))= *(unsigned int *)(res + (8<<2));
+  *(unsigned int *)(data + (12<<2))= *(unsigned int *)(res + (12<<2));
+  *(unsigned int *)(data + (13<<2))= *(unsigned int *)(res + (13<<2));
+  *(unsigned int *)(data + (14<<2))= *(unsigned int *)(res + (14<<2));
+  *(unsigned int *)(data + (15<<2))= *(unsigned int *)(res + (15<<2));
+  *(unsigned int *)(data + (16<<2))= *(unsigned int *)(res + (19<<2));
+  *(unsigned int *)(data + (17<<2))= *(unsigned int *)(res + (18<<2));
+  *(unsigned int *)(data + (18<<2))= *(unsigned int *)(res + (17<<2));
+  *(unsigned int *)(data + (19<<2))= *(unsigned int *)(res + (16<<2));
+  *(unsigned int *)(data + (20<<2))= *(unsigned int *)(res + (20<<2));
+  *(unsigned int *)(data + (21<<2))= *(unsigned int *)(res + (21<<2));
+  *(unsigned int *)(data + (22<<2))= *(unsigned int *)(res + (22<<2));
+  *(unsigned int *)(data + (23<<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/systems/nagra/nagra2.c b/systems/nagra/nagra2.c
new file mode 100644
index 0000000..9f6056e
--- /dev/null
+++ b/systems/nagra/nagra2.c
@@ -0,0 +1,859 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include "system.h"
+#include "misc.h"
+#include "opts.h"
+#include "network.h"
+#include "crypto.h"
+#include "helper.h"
+
+#include 
+#include 
+#include "openssl-compat.h"
+
+#include "nagra.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define SYSTEM_NAME          "Nagra2"
+#define SYSTEM_PRI           -10
+
+// -- cN2Emu -------------------------------------------------------------------
+
+class cN2Emu : protected c6805 {
+private:
+  bool initDone;
+protected:
+  bool Init(int id, int romv);
+  virtual void Stepper(void) {}
+public:
+  cN2Emu(void);
+  virtual ~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,0x4000,0x02)) return false;
+
+    snprintf(buff,sizeof(buff),"EEP%02X_%d.bin",(id>>8)&0xFF,romv);
+    // Eeprom00 0x00:0x3000-0x37ff OTP 0x80
+    if(!AddMapper(new cMapRom(0x3000,buff,0x0000),0x3000,0x0800,0x00)) return false;
+    //XXX if(!AddMapper(new cMapEeprom(0x3000,buff,128,0x0000),0x3000,0x0800,0x00)) return false;
+    // Eeprom80 0x80:0x8000-0xbfff
+    if(!AddMapper(new cMapRom(0x8000,buff,0x0800),0x8000,0x4000,0x80)) return false;
+    //XXX if(!AddMapper(new cMapEeprom(0x8000,buff,  0,0x0800),0x8000,0x4000,0x80)) return false;
+    initDone=true;
+    }
+  return true;
+}
+
+// -- cMapCore -----------------------------------------------------------------
+
+#define SETSIZE  0x02
+#define IMPORT_J 0x03
+#define IMPORT_A 0x04
+#define IMPORT_B 0x05
+#define IMPORT_C 0x06
+#define IMPORT_D 0x07
+#define EXPORT_A 0x0A
+#define EXPORT_B 0x0B
+#define EXPORT_C 0x0C
+#define EXPORT_D 0x0D
+
+class cMapCore {
+private:
+  cBN x, y, s, j;
+  SHA_CTX sctx;
+protected:
+  cBN A, B, C, D, J;
+  cBN H, R;
+  cBNctx ctx;
+  int wordsize;
+  //
+  void ImportReg(unsigned char reg, const unsigned char *data, int l=0);
+  void ExportReg(unsigned char reg, unsigned char *data, int l=0, bool BE=false);
+  void SetWordSize(int l) { wordsize=l; }
+  void MakeJ(void);
+  void MonMul(BIGNUM *o, BIGNUM *i1, BIGNUM *i2);
+  bool DoMap(int f, unsigned char *data=0, int l=0);
+public:
+  cMapCore(void);
+  };
+
+cMapCore::cMapCore(void)
+{
+  wordsize=4;
+}
+
+void cMapCore::ImportReg(unsigned char reg, const unsigned char *in, int l)
+{
+  l=(l?l:wordsize)<<3;
+  switch(reg) {
+    case IMPORT_J: J.GetLE(in,8); break;
+    case IMPORT_A: A.GetLE(in,l); break;
+    case IMPORT_B: B.GetLE(in,l); break;
+    case IMPORT_C: C.GetLE(in,l); break;
+    case IMPORT_D: D.GetLE(in,l); break;
+    default: PRINTF(L_GEN_DEBUG,"internal: nagramap import register not supported"); return;
+    }
+}
+
+void cMapCore::ExportReg(unsigned char reg, unsigned char *out, int l, bool BE)
+{
+  l=(l?l:wordsize)<<3;
+  cBN *ptr;
+  switch(reg) {
+    case EXPORT_A: ptr=&A; break;
+    case EXPORT_B: ptr=&B; break;
+    case EXPORT_C: ptr=&C; break;
+    case EXPORT_D: ptr=&D; break;
+    default: PRINTF(L_GEN_DEBUG,"internal: nagramap export register not supported"); return;
+    }
+  if(!BE) ptr->PutLE(out,l);
+  else ptr->Put(out,l);
+}
+
+void cMapCore::MakeJ(void)
+{
+#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);
+  BN_set_bit(J,0);
+  BN_set_bit(x,64);
+  BN_mod_inverse(J,J,x,ctx);
+}
+
+void cMapCore::MonMul(BIGNUM *o, BIGNUM *i1, BIGNUM *i2)
+{
+  int words=(BN_num_bytes(i1)+7)>>3;
+  BN_zero(s);
+  for(int i=0; i1) RotateBytes(data,wordsize);
+        SHA1_Update(&sctx,data,wordsize);
+        }
+      memset(data,0,64);
+      SHA1_Final(data+64,&sctx);
+      break;
+    default:
+      return false;
+    }
+  return true;
+}
+
+// -- cN2Prov ------------------------------------------------------------------
+
+class cN2Prov {
+private:
+  unsigned seed[5], cwkey[8];
+  bool keyValid;
+  cIDEA idea;
+protected:
+  int id, flags;
+  //
+  virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw) { return false; }
+  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, unsigned char *cws);
+  void SwapCW(unsigned char *cw);
+  virtual int ProcessBx(unsigned char *data, int len, int pos) { return -1; }
+  virtual bool PostProcAU(int id, unsigned char *data) { return true; }
+  bool CanHandle(int Id) { return ((Id^id)&~0x107)==0; }
+  bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+  };
+
+cN2Prov::cN2Prov(int Id, int Flags)
+{
+  keyValid=false; id=Id; flags=Flags;
+}
+
+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, unsigned char *cw)
+{
+  unsigned char hd[5], hw[128+64], buf[20];
+  hd[0]=in15&0x7F;
+  hd[1]=cw[14];
+  hd[2]=cw[15];
+  hd[3]=cw[6];
+  hd[4]=cw[7];
+
+  if(keyValid && !memcmp(seed,hd,5)) {	// key cached
+    memcpy(buf,cwkey,8);
+    }
+  else {				// key not cached
+    memset(hw,0,sizeof(hw));
+    if(!Algo(algo,hd,hw)) return false;
+    memcpy(&hw[128],hw,64);
+    RotateBytes(&hw[64],128);
+    SHA1(&hw[64],128,buf);
+    RotateBytes(buf,20);
+
+    memcpy(seed,hd,5);
+    memcpy(cwkey,buf,8);
+    keyValid=true;
+    }  
+
+  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];
+  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);
+    }
+}
+
+// -- cN2ProvLink & cN2Providers -----------------------------------------------
+
+#define N2FLAG_NONE     0
+#define N2FLAG_MECM     1
+#define N2FLAG_Bx       2
+#define N2FLAG_POSTAU   4
+#define N2FLAG_INV      128
+
+class cN2Providers;
+
+class cN2ProvLink {
+friend class cN2Providers;
+private:
+  cN2ProvLink *next;
+protected:
+  int id, flags;
+  //
+  virtual cN2Prov *Create(void)=0;
+  bool CanHandle(int Id) { return ((Id^id)&~0x107)==0; }
+  bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+public:
+  cN2ProvLink(int Id, int Flags);
+  virtual ~cN2ProvLink() {}
+  };
+
+class cN2Providers {
+friend class cN2ProvLink;
+private:
+  static cN2ProvLink *first;
+  //
+  static void Register(cN2ProvLink *plink);
+public:
+  static cN2Prov *GetProv(int Id, int Flags);
+  };
+
+template class cN2ProvLinkReg : public cN2ProvLink {
+public:
+  cN2ProvLinkReg(void):cN2ProvLink(ID,FLAGS) {}
+  virtual cN2Prov *Create(void) { return new PROV(id,flags); }
+  };
+
+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(int Id, int Flags)
+{
+  id=Id; flags=Flags;
+  cN2Providers::Register(this);
+}
+
+#include "nagra2-prov.c"
+
+#ifndef TESTER
+
+// -- 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; i64) 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 ------------------------------------------------------------
+
+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)
+{
+  int cmdLen=data[4]-5;
+  int id=(data[5]*256)+data[6];
+  cTimeMs minTime;
+
+  if(id==0x4101) StartLog(ecm,0x1881); // D+ AU
+    
+  if(cmdLen<64 || SCT_LEN(data)>4;
+  cKeySnoop ks(this,'N',id,keyNr);
+  cPlainKey *pk;
+  cBN m1;
+  unsigned char ideaKey[16], vKey[16];
+  bool hasVerifyKey=false;
+  if(!(pk=keys.FindKey('N',id,MBC('M','1'),-1)))  {
+    if(doLog) PRINTF(L_SYS_KEY,"missing %04x M1 key",id);
+    return false;
+    }
+  pk->Get(m1);
+  if((pk=keys.FindKey('N',id,'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',id,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) PRINTF(L_SYS_ECM,"provider %04x capabilities%s%s%s%s",id,
+                      ecmP->HasFlags(N2FLAG_MECM)    ?" MECM":"",
+                      ecmP->HasFlags(N2FLAG_Bx)      ?" Bx":"",
+                      ecmP->HasFlags(N2FLAG_POSTAU)  ?" POSTPROCAU":"",
+                      ecmP->HasFlags(N2FLAG_INV)     ?" INVCW":"");
+    }
+  lastEcmId=id;
+
+  int l=0, mecmAlgo=0;
+  LBSTARTF(L_SYS_ECM);
+  bool contFail=false;
+  for(int i=16; i0) {
+    if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+      if(!ecmP->MECM(buff[15],mecmAlgo,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];
+
+  if(cmdLen<96 || SCT_LEN(buffer)>1;
+  cPlainKey *pk;
+  cBN n;
+  unsigned char ideaKey[24], vKey[16];
+  bool hasVerifyKey=false;
+  if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset+0x10+rsasel),96)))  {
+    PRINTF(L_SYS_EMM,"missing %04x NN %.02X RSA key (96 bytes)",id,keyset+0x10+rsasel);
+    return;
+    }
+  pk->Get(n);
+  if((pk=keys.FindKey('N',id,MBC(N2_MAGIC,0x03+sigsel),sizeof(vKey)))) {
+    pk->Get(vKey);
+    hasVerifyKey=true;
+    }
+  else if(id!=lastEmmId) PRINTF(L_SYS_EMM,"missing %04x NN %.02X signature key (non-fatal)",id,0x03+sigsel);
+  if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset),24))) {
+    if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset+sel),16))) {
+      PRINTF(L_SYS_EMM,"missing %04x NN %.02x IDEA key (24 or 16 bytes)",id,keyset+sel);
+      return;
+      }
+    memset(ideaKey+16,0,8);
+    }
+  pk->Get(ideaKey);
+
+  unsigned char emmdata[256];
+  if(!DecryptEMM(buffer+14,emmdata,ideaKey,cmdLen,hasVerifyKey?vKey:0,n)) {
+    PRINTF(L_SYS_EMM,"decrypt of EMM failed (%04x)",id);
+    return;
+    }
+  if((!emmP && id!=lastEmmId) || (emmP && !emmP->CanHandle(id))) {
+    delete emmP;
+    emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+    if(emmP) PRINTF(L_SYS_EMM,"provider %04x capabilities%s%s%s%s",id,
+                      emmP->HasFlags(N2FLAG_MECM)    ?" MECM":"",
+                      emmP->HasFlags(N2FLAG_Bx)      ?" Bx":"",
+                      emmP->HasFlags(N2FLAG_POSTAU)  ?" POSTPROCAU":"",
+                      emmP->HasFlags(N2FLAG_INV)     ?" INVCW":"");
+    }
+  lastEmmId=id;
+
+  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; iPostProcAU(id,&emmdata[i])) {
+            FoundKey();
+            if(keys.NewKey('N',id,(emmdata[i+3]&0x40)>>6,&emmdata[i+7],16)) NewKey();
+            cLoaders::SaveCache();
+            }
+          }
+        i+=23;
+        break;
+      case 0xE0: // DN key update
+        if(emmdata[i+1]==0x25) {
+          FoundKey();
+          if(keys.NewKey('N',id,(emmdata[i+16]&0x40)>>6,&emmdata[i+23],16)) NewKey();
+          cLoaders::SaveCache();
+          }
+        i+=39;
+        break;
+      case 0x83: // change data prov. id
+        id=(emmdata[i+1]<<8)|emmdata[i+2];
+        i+=3;
+        break;
+      case 0xA4: // conditional (always no match assumed for now)
+        i+=emmdata[i+1]+2+4;
+        break;
+      case 0xA6:
+        i+=15;
+        break;
+      case 0xAE:
+        i+=11;
+        break;
+      case 0x13 ... 0x17: // Date
+        i+=4;
+        break;
+      case 0xB0 ... 0xBF: // Update with ROM CPU code
+        {
+        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 0xE3: // Eeprom update
+        i+=emmdata[i+4]+4;
+        break;
+      case 0xE1:
+      case 0xE2:
+      case 0x00: // end of processing
+        i=cmdLen;
+        break;
+      default:
+        if(!contFail) LBPUT("unknown EMM nano");
+        LBPUT(" %02x",emmdata[i]);
+        contFail=true;
+        i++;
+        continue;
+      }
+    LBFLUSH(); contFail=false;
+    }
+  LBEND();
+}
+
+// -- cSystemLinkNagra2 --------------------------------------------------------
+
+static const tI18nPhrase Phrases2[] = {
+  { "Nagra2: AUXserver hostname",
+    "Nagra2: AUXserver Hostname",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nagra2: AUX-palvelimen osoite",
+    "",
+    "",
+    "",
+    "",
+  },
+  { "Nagra2: AUXserver port",
+    "Nagra2: AUXserver Port",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nagra2: AUX-palvelimen portti",
+    "",
+    "",
+    "",
+    "",
+  },
+  { "Nagra2: AUXserver password",
+    "Nagra2: AUXserver Passwort",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "Nagra2: AUX-palvelimen salasana",
+    "",
+    "",
+    "",
+    "",
+  },
+  { NULL }
+  };
+
+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)
+{
+#ifdef HAS_AUXSRV
+  static const char allowed_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz-.";
+  opts=new cOpts(SYSTEM_NAME,3);
+  opts->Add(new cOptStr("AuxServerAddr","Nagra2: AUXserver hostname",auxAddr,sizeof(auxAddr),allowed_chars));
+  opts->Add(new cOptInt("AuxServerPort","Nagra2: AUXserver port",&auxPort,0,65535));
+  opts->Add(new cOptStr("AuxServerPass","Nagra2: AUXserver password",auxPassword,sizeof(auxPassword),allowed_chars));
+  Feature.AddPhrases(Phrases2);
+#endif
+  Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkNagra2::CanHandle(unsigned short SysId)
+{
+  return ((SysId&SYSTEM_MASK)==SYSTEM_NAGRA && (SysId&0xFF)>0) ||
+          SysId==SYSTEM_NAGRA_BEV;
+}
+
+#endif //TESTER
diff --git a/systems/sc-conax/sc-conax.c b/systems/sc-conax/sc-conax.c
new file mode 100644
index 0000000..b20a082
--- /dev/null
+++ b/systems/sc-conax/sc-conax.c
@@ -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 
+#include 
+
+#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; i0) {
+    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>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=13) {
+            int idx=buff[i+4];
+            if(idx<=1) {
+              gotIdx|=(1<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; i0) {
+      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/systems/sc-conax/sc-conax.mk b/systems/sc-conax/sc-conax.mk
new file mode 100644
index 0000000..2ab4e12
--- /dev/null
+++ b/systems/sc-conax/sc-conax.mk
@@ -0,0 +1,5 @@
+#
+# Smartcard Conax
+#
+TARGET = sc_conax
+OBJS   = sc-conax.o
diff --git a/systems/sc-cryptoworks/sc-cryptoworks.c b/systems/sc-cryptoworks/sc-cryptoworks.c
new file mode 100644
index 0000000..843c718
--- /dev/null
+++ b/systems/sc-cryptoworks/sc-cryptoworks.c
@@ -0,0 +1,607 @@
+/*
+ * 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 
+#include 
+#include 
+
+#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"
+
+#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 --------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+  { "SC-Cryptoworks: Parental rating",
+    "SC-Cryptoworks: Altersbeschränkung",
+    "",
+    "",
+    "SC-Cryptoworks: Leeftijdsadvies",
+    "",
+    "SC-Cryptoworks: Autorisation parentale",
+    "",
+    "SC-Cryptoworks: Ikäraja",
+    "SC-Cryptoworks: wska¼nik rodzica",
+    "",
+    "",
+    "",
+  },
+  { "don't touch",
+    "nicht ändern",
+    "",
+    "",
+    "niet wijzigen",
+    "",
+    "Ne pas modifier",
+    "",
+    "älä koske",
+    "nie dotykaj",
+    "",
+    "",
+    "",
+  },
+  { "disable",
+    "ausschalten",
+    "",
+    "",
+    "uitschakelen",
+    "",
+    "Désactiver",
+    "",
+    "poista",
+    "wy³±cz",
+    "",
+    "",
+    "",
+  },
+  { NULL }
+  };
+
+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[] = {
+    "don't touch",
+    "disable"
+    };
+
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptSel("DisableParental","SC-Cryptoworks: Parental rating",&disableParental,sizeof(rat)/sizeof(char *),rat));
+  Feature.NeedsSmartCard();
+  Feature.AddPhrases(Phrases);
+}
+
+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) {
+    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=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>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; i0 && ReadData(buff,l)==l) {
+      int r=0;
+      for(int i=0; in) {
+        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/systems/sc-cryptoworks/sc-cryptoworks.mk b/systems/sc-cryptoworks/sc-cryptoworks.mk
new file mode 100644
index 0000000..45faad2
--- /dev/null
+++ b/systems/sc-cryptoworks/sc-cryptoworks.mk
@@ -0,0 +1,5 @@
+#
+# Smartcard Cryptoworks
+#
+TARGET = sc_cryptoworks
+OBJS   = sc-cryptoworks.o
diff --git a/systems/sc-irdeto/sc-irdeto.c b/systems/sc-irdeto/sc-irdeto.c
new file mode 100644
index 0000000..797d383
--- /dev/null
+++ b/systems/sc-irdeto/sc-irdeto.c
@@ -0,0 +1,710 @@
+/*
+ * 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 
+#include 
+
+#include 
+
+#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[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; i0 && 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; k0 && 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);
+//        if(data[len+3]==0x01 && data[len+4]==0x00) {
+          memcpy(cmd+sizeof(emmCmd)+ADDRLEN,&data[len+5],dataLen);
+          if(DoCmd(cmd,0x0000)>0 && Status()) return true;
+//          }
+//        else d(printf("smartcardirdeto: bad EMM format, 0x0100 marker is 0x%02x%02x\n",data[len+3],data[len+4]))
+        }
+      }
+    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/systems/sc-irdeto/sc-irdeto.mk b/systems/sc-irdeto/sc-irdeto.mk
new file mode 100644
index 0000000..8dfdd54
--- /dev/null
+++ b/systems/sc-irdeto/sc-irdeto.mk
@@ -0,0 +1,6 @@
+#
+# Smartcard Irdeto/Beta
+#
+TARGET = sc_irdeto
+OBJS   = sc-irdeto.o
+LIBS   = -lcrypto
diff --git a/systems/sc-nagra/sc-nagra.c b/systems/sc-nagra/sc-nagra.c
new file mode 100644
index 0000000..507bdc1
--- /dev/null
+++ b/systems/sc-nagra/sc-nagra.c
@@ -0,0 +1,569 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include 
+
+#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=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; i0) 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/systems/sc-nagra/sc-nagra.mk b/systems/sc-nagra/sc-nagra.mk
new file mode 100644
index 0000000..266a409
--- /dev/null
+++ b/systems/sc-nagra/sc-nagra.mk
@@ -0,0 +1,5 @@
+#
+# Smartcard Nagra
+#
+TARGET = sc_nagra
+OBJS   = sc-nagra.o
diff --git a/systems/sc-seca/sc-seca.c b/systems/sc-seca/sc-seca.c
new file mode 100644
index 0000000..5562589
--- /dev/null
+++ b/systems/sc-seca/sc-seca.c
@@ -0,0 +1,432 @@
+/*
+ * 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 
+#include 
+#include 
+
+#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 tI18nPhrase Phrases[] = {
+  { "SC-Seca: EMM updates",
+    "SC-Seca: EMM updates",
+    "",
+    "",
+    "SC-Seca: EMM updates",
+    "",
+    "SC-Seca: Mise à jour EMM",
+    "",
+    "SC-Seca: EMM-päivitykset",
+    "SC-Seca: aktualizacje EMM",
+    "",
+    "",
+    "",
+  },
+  { "allow ALL",
+    "alle erlauben",
+    "",
+    "",
+    "ALLES toestaan",
+    "",
+    "Permettre tous",
+    "",
+    "salli kaikki",
+    "dopu¶æ wszystkie",
+    "",
+    "",
+    "",
+  },
+  { "block UNIQUE",
+    "UNIQUE blocken",
+    "",
+    "",
+    "UNIEKE blokkeren",
+    "",
+    "Bloquer UNIQUE",
+    "",
+    "estä uniikit",
+    "blokuj unikaty",
+    "",
+    "",
+    "",
+  },
+  { "block SHARED",
+    "SHARED blocken",
+    "",
+    "",
+    "GEDEELDE blokkeren",
+    "",
+    "Bloquer PARTAGE",
+    "",
+    "estä jaetut",
+    "blokuj dzielone",
+    "",
+    "",
+    "",
+  },
+  { "block ALL",
+    "alle blocken",
+    "",
+    "",
+    "ALLES blokkeren",
+    "",
+    "Bloquer TOUS",
+    "",
+    "estä kaikki",
+    "blokuj wszystkie",
+    "",
+    "",
+    "",
+  },
+  { "SC-Seca: activate PPV",
+    "SC-Seca: PPV aktivieren",
+    "",
+    "",
+    "SC-Seca: activeer PPV",
+    "",
+    "SC-Seca: activer PPV",
+    "",
+    "SC-Seca: Aktivoi PPV",
+    "SC-Seca: aktywuj PPV",
+    "",
+    "",
+    "",
+  },
+  { NULL }
+  };
+
+static const char *block[] = {
+  "allow ALL",
+  "block UNIQUE",
+  "block SHARED",
+  "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","SC-Seca: EMM updates",&blocker,sizeof(block)/sizeof(char *),block));
+  cOpt *opt=new cOptBool("Ppv","SC-Seca: activate PPV",&ppv);
+  if(opt) opt->Persistant(false);
+  opts->Add(opt);
+  Feature.NeedsSmartCard();
+  Feature.AddPhrases(Phrases);
+}
+
+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");
+  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<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/systems/sc-seca/sc-seca.mk b/systems/sc-seca/sc-seca.mk
new file mode 100644
index 0000000..7f5e09c
--- /dev/null
+++ b/systems/sc-seca/sc-seca.mk
@@ -0,0 +1,5 @@
+#
+# Smartcard Seca
+#
+TARGET = sc_seca
+OBJS   = sc-seca.o
diff --git a/systems/sc-viaccess/sc-viaccess.c b/systems/sc-viaccess/sc-viaccess.c
new file mode 100644
index 0000000..3907943
--- /dev/null
+++ b/systems/sc-viaccess/sc-viaccess.c
@@ -0,0 +1,361 @@
+/*
+ * 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 
+#include 
+#include 
+
+#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 *ecms, unsigned short sysId, const unsigned char *data, int len);
+  };
+
+cSystemScViaccess::cSystemScViaccess(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Viaccess")
+{
+  hasLogger=true;
+}
+
+void cSystemScViaccess::ParseCADescriptor(cSimpleList *ecms, unsigned short sysId, 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,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+(atr->histLen-2),verifyBytes,sizeof(verifyBytes)))) {
+    PRINTF(L_SC_INIT,"doesn't look like a Viaccess card");
+    return false;
+    }
+
+  infoStr.Begin();
+  infoStr.Strcat("Viaccess smartcard\n");
+  char *ver=0;
+  switch((atr->hist[atr->histLen-4]<<8)|atr->hist[atr->histLen-3]) {
+    case 0x6268: ver="2.3"; break;
+    case 0x6668: ver="2.4(?)"; break;
+    case 0xa268:
+    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; javailKeys); 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/systems/sc-viaccess/sc-viaccess.mk b/systems/sc-viaccess/sc-viaccess.mk
new file mode 100644
index 0000000..bd119e6
--- /dev/null
+++ b/systems/sc-viaccess/sc-viaccess.mk
@@ -0,0 +1,5 @@
+#
+# Smartcard Viaccess
+#
+TARGET = sc_viaccess
+OBJS   = sc-viaccess.o
diff --git a/systems/seca/seca.c b/systems/seca/seca.c
new file mode 100644
index 0000000..8376da2
--- /dev/null
+++ b/systems/seca/seca.c
@@ -0,0 +1,1657 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#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 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];
+  const char *sline=line;
+  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[keylen];
+      len=GetHex(line,skey,keylen,false);
+      if(IsBNKey()) {
+        if(C2(keynr)=='E' && len==PLAINLEN_SECA_E) {
+          // support short exponent keys
+          memset(&skey[len],0,keylen-len);
+          len=keylen;
+          }
+        }
+      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;
+        }
+      }
+    }
+  FormatError("seca",sline);
+  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 cProviderSeca {
+private:
+  int len;
+public:
+  unsigned char key[16];
+  //
+  bool Parse(const char *line);
+  bool Save(FILE *f) { return true; }
+  bool IsUpdated(void) { return false; }
+  void Updated(void) {}
+  bool Cmp(cSecaCardInfo *ci) { return false; }
+  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 {
+public:
+  cSecaCardInfos(void):cCardInfos(SYSTEM_NAME) {}
+  };
+
+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=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(),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 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; iSHA1End;
+    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 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 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 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 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);
+  while((pk=keys.FindKey('S',ecmD->provId,keyNr&0x0F,key8?8:16,pk))) {
+    unsigned char buff[msgLen];
+    memcpy(buff,ecm,sizeof(buff)); // 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> 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.FindKey('S',provId,MBC3('E',rsaKeynr,EMM_MAGIC),-1))) return;
+    pk->Get(exp);
+    if(!(pk=keys.FindKey('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;
+      }
+    }
+
+  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 buff[msgLen], signature[20];
+      memcpy(buff,emm,sizeof(buff)); // if decoding fails we need the original de-sse'd data
+
+      if(!SE) {
+        for(int i=0 ; i<=64 && iCalcSHASignature(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>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(numKeysprovId,2),HexStr(str2,ci->sa,3),HexStr(str3,ci->key,8));
+        for(unsigned int i=0 ; isa,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; }
+  virtual bool Init(const char *cfgdir);
+  };
+
+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);
+}
+
+bool cSystemLinkSeca::Init(const char *cfgdir)
+{
+  Scards.Load(cfgdir,SYSTEM_NAME,"Seca.KID");
+  return true;
+}
diff --git a/systems/seca/seca.mk b/systems/seca/seca.mk
new file mode 100644
index 0000000..19ba449
--- /dev/null
+++ b/systems/seca/seca.mk
@@ -0,0 +1,6 @@
+#
+# Seca
+#
+TARGET = seca
+OBJS   = seca.o
+LIBS   = -lcrypto
diff --git a/systems/shl/shl.c b/systems/shl/shl.c
new file mode 100644
index 0000000..95a8877
--- /dev/null
+++ b/systems/shl/shl.c
@@ -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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#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 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; ifilter) {
+      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; ifirst, *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; inext=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()Pid()==pid) { dd=&d[i]; break; }
+        if(!old ||
+           (d[i].lastRead<0 && d[i].createTimecreateTime) ||
+           (d[i].lastRead>=0 && d[i].lastReadlastRead))
+          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; ifilters);
+      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; ipids[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(nprovId,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<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/systems/shl/shl.mk b/systems/shl/shl.mk
new file mode 100644
index 0000000..b35e358
--- /dev/null
+++ b/systems/shl/shl.mk
@@ -0,0 +1,6 @@
+#
+# @SHL
+#
+TARGET = shl
+OBJS   = shl.o
+LIBS   = -lcrypto
diff --git a/systems/viaccess/log-viaccess.h b/systems/viaccess/log-viaccess.h
new file mode 100644
index 0000000..e2f48db
--- /dev/null
+++ b/systems/viaccess/log-viaccess.h
@@ -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/systems/viaccess/opentv.h b/systems/viaccess/opentv.h
new file mode 100644
index 0000000..4d122dc
--- /dev/null
+++ b/systems/viaccess/opentv.h
@@ -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/systems/viaccess/st20.c b/systems/viaccess/st20.c
new file mode 100644
index 0000000..c619dcc
--- /dev/null
+++ b/systems/viaccess/st20.c
@@ -0,0 +1,390 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+
+#include 
+
+#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)
+{
+  switch(off) {
+    case FLASHS ... FLASHE:
+#ifndef SAVE_DEBUG
+      return &flash[off-FLASHS];
+#else
+      off-=FLASHS; if(off>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(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<(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/systems/viaccess/st20.h b/systems/viaccess/st20.h
new file mode 100644
index 0000000..12eced0
--- /dev/null
+++ b/systems/viaccess/st20.h
@@ -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(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/systems/viaccess/tps.c b/systems/viaccess/tps.c
new file mode 100644
index 0000000..dfa2ca0
--- /dev/null
+++ b/systems/viaccess/tps.c
@@ -0,0 +1,1175 @@
+/*
+ * 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 
+#include 
+#include 
+
+#include 
+
+#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"
+
+// -- 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  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<>(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; vRC6_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 -------------------------------------------------------------
+
+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());
+      }
+    }
+}
+
+// -- cSatTime -----------------------------------------------------------------
+
+class cSatTime {
+private:
+  static cMutex mutex;
+  static cSimpleList list;
+  //
+  int cardNum;
+  cTransponderTime *ttime;
+  //
+  void CheckHandler(void);
+public:
+  cSatTime(int CardNum, int Source, int Transponder);
+  time_t Now(void);
+  };
+
+cMutex cSatTime::mutex;
+cSimpleList 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 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()
+{
+  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>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 ... 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 ... 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 ... 0xF:
+            NIBBLE(off);
+            off|=cmd&0x0F; len=((cmd>>4)&0x3)+2;
+            break;
+          }
+        const unsigned char *from=out_ptr-(off+1);
+        if(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;
+    }
+}
+
+// -- cTpsAuHook ---------------------------------------------------------------
+
+#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);
+  };
+
+// -- cTpsKeys -----------------------------------------------------------------
+
+cTpsKeys tpskeys;
+
+cTpsKeys::cTpsKeys(void)
+:cLoader("TpsAu")
+,lastLoad(-LOADBIN_TIME)
+,lastAu(-TPSAU_TIME)
+{
+  list=new cSimpleList;
+  first=last=0; algomem=0;
+}
+
+cTpsKeys::~cTpsKeys()
+{
+  delete list;
+  free(algomem);
+}
+
+const cTpsKey *cTpsKeys::GetKey(time_t t)
+{
+  cMutexLock lock(this);
+  for(cTpsKey *k=list->First(); k; k=list->Next(k))
+    if(tTimestamp()) return k;
+  return 0;
+}
+
+void cTpsKeys::Check(time_t now, int cardnum)
+{
+  checkMutex.Lock();
+  if(first==0 && last==0 && list->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)
+{
+  cMutexLock lock(this);
+  PRINTF(L_SYS_TPSAU,"purging TPS keylist");
+  bool del=false;
+  for(cTpsKey *k=list->First(); k;) {
+    cTpsKey *n=list->Next(k);
+    if(k->Timestamp()Del(k); del=true; }
+    k=n;
+    }
+  if(del) { 
+    GetFirstLast();
+    Modified();
+    cLoaders::SaveCache();
+    }
+}
+
+void cTpsKeys::Join(cSimpleList *nlist)
+{
+  cMutexLock lock(this);
+  cTpsKey *k;
+  while((k=nlist->First())) {
+    nlist->Del(k,false);
+    cTpsKey *p=list->First();
+    do {
+      if(!p) {
+        list->Add(k);
+        Modified();
+        break;
+        }
+      cTpsKey *n=list->Next(p);
+      if(k->Timestamp()==p->Timestamp()) {
+        p->Set(k);
+        Modified();
+        delete k;
+        break;
+        }
+      if(k->Timestamp()>p->Timestamp() && (!n || k->Timestamp()Timestamp())) {
+        list->Add(k,p);
+        Modified();
+        break;
+        }
+      p=n;
+      } while(p);
+    }
+  delete nlist;
+  GetFirstLast();
+  cLoaders::SaveCache();
+}
+
+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(list->Count()>0) {
+    cTpsKey *k=list->First();
+    first=last=k->Timestamp();
+    for(; k; k=list->Next(k)) {
+      if(k->Timestamp()Timestamp();
+      }
+    PRINTF(L_SYS_TPS,"%d TPS keys available (from %s to %s)",list->Count(),*Time(first),*Time(last));
+    }
+  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; isize; 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+1]==0x00 && d[addr+3]==0x00 && d[addr+4]==3) kd=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((d[addr]&0xF0)==0x60 && (d[addr+1]&0xF0)==0xB0) {
+          int vajw = (int)(((~(d[addr]&0x0F))<<4)|(d[addr+1]&0x0F));
+          unsigned char hits=0;
+          for(int j=2; j < 0x30; j++) {
+            int vld = ((d[addr+j]&0x0F)<<4)|(d[addr+j+1]&0x0F);
+            if((d[addr+j]&0xF0)==0x20 && (d[addr+j+1]&0xF0)==0x70) {
+              int val=vajw+vld;
+              if(val==3 || val==4 || val==5) hits++;
+              }
+            }
+          if(hits==3) cb3=addr;
+          else if(hits==2) cb2=addr;
+          }
+        }
+      }
+    }
+  if(!kd || !cb1 || !cb2 || !cb3) {
+    PRINTF(L_SYS_TPSAU,"couldn't locate all pointers in data section");
+    return false;
+    }
+  RegisterAlgo3(d,cb1,cb2,cb3,kd);
+
+  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=d+datahdr->dlen) {
+    PRINTF(L_SYS_TPSAU,"section 6 exceeds buffer");
+    return false;
+    }
+  for(int i=0; i *nlist=new cSimpleList;
+  for(int i=0; iSet(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 *nlist=new cSimpleList;
+  for(int i=68; iAddr()+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)));
+    }
+}
+*/
+
+bool cTpsKeys::ParseLine(const char *line, bool fromCache)
+{
+  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]); list->Add(k); }
+        return true;
+        }
+      else PRINTF(L_SYS_TPS,"CRC failed during cache load");
+      }
+    }
+  return false;
+}
+
+bool cTpsKeys::Save(FILE *f)
+{
+  cMutexLock lock(this);
+  bool res=true;
+  char str[420];
+  for(cTpsKey *k=list->First(); k; k=list->Next(k)) {
+    unsigned char tmp[60];
+    k->Put(&tmp[4]);
+    *((unsigned int *)tmp)=crc32_le(0,&tmp[4],sizeof(tmp)-4);
+    fprintf(f,"%s\n",HexStr(str,tmp,sizeof(tmp)));
+    res=(ferror(f)==0 && res);
+    }
+  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);
+    res=(ferror(f)==0 && res);
+    for(int i=0; ifilter) {
+              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; }
+          }
+        }
+      }
+    }
+}
+
+// -- 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 kd)
+{
+  cMutexLock lock(&st20Mutex);
+  free(mem);
+  if(!(mem=MALLOC(unsigned char,kd))) return false;
+  memcpy(mem,data,kd);
+  memLen=kd; 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; iNow();
+  tpskeys.Check(now,cardNum);
+  if(now<0) return -1;
+
+  const cTpsKey *k=tpskeys.GetKey(now);
+  if(!k) {
+    PRINTF(L_SYS_TPS,"no TPS key available for current time of day");
+    return -1;
+    }
+  doPost=0;
+  int opmode=k->Opmode(), 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;
+    }
+  for(int i=0; i>((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));
+        break;
+      }
+  postMode=k->Mode(1); memcpy(postKey,k->Key(1),sizeof(postKey));
+  return ret;
+}
+
+void cTPS::PostProc(unsigned char *cw)
+{
+  if(doPost) TpsDecrypt(cw,postMode,postKey);
+}
diff --git a/systems/viaccess/tps.h b/systems/viaccess/tps.h
new file mode 100644
index 0000000..375eb62
--- /dev/null
+++ b/systems/viaccess/tps.h
@@ -0,0 +1,137 @@
+/*
+ * 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 
+#include 
+#include 
+#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 kd);
+  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 cSimpleItem {
+private:
+  time_t timestamp;
+  int opmode;
+  struct {
+    unsigned char mode;
+    unsigned char key[16];
+    } step[3];
+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);
+  void Put(unsigned char *mem) const;
+  };
+
+// ----------------------------------------------------------------
+
+class cTpsKeys : public cMutex, public cLoader, private cTPSDecrypt {
+friend class cTpsAuHook;
+private:
+  cSimpleList *list;
+  time_t first, last;
+  //
+  cTimeMs lastCheck, lastLoad, lastAu;
+  cMutex checkMutex;
+  //
+  unsigned char *algomem;
+  int algolen, algoread, cb1off, cb2off, cb3off;
+  //
+  void Join(cSimpleList *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);
+public:
+  cTpsKeys(void);
+  ~cTpsKeys();
+  const cTpsKey *GetKey(time_t t);
+  void Check(time_t now, int cardnum);
+  virtual bool ParseLine(const char *line, bool fromCache);
+  virtual bool Save(FILE *f);
+  };
+
+extern cTpsKeys tpskeys;
+
+#endif
diff --git a/systems/viaccess/viaccess.c b/systems/viaccess/viaccess.c
new file mode 100644
index 0000000..c649f41
--- /dev/null
+++ b/systems/viaccess/viaccess.c
@@ -0,0 +1,539 @@
+/*
+ * 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 
+#include 
+
+#include "viaccess.h"
+#include "tps.h"
+#include "system-common.h"
+#include "misc.h"
+#include "parse.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 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 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];
+  const char *sline=line;
+  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;
+       }
+    }
+  FormatError("viaccess",sline);
+  return false;
+}
+
+cString cPlainKeyVia::PrintKeyNr(void)
+{
+  return cString::sprintf((keynr==MBC3('T','P','S'))?"TPS":"%02X",keynr);
+}
+
+// -- cViaccessCardInfo --------------------------------------------------------
+
+class cViaccessCardInfo : public cProviderViaccess, public cCardViaccess {
+public:
+  unsigned char keyno, key[8];
+  //
+  bool Parse(const char *line);
+  bool Save(FILE *f) { return true; }
+  bool IsUpdated(void) { return false; }
+  void Updated(void) {}
+  bool Cmp(cViaccessCardInfo *ci) { return false; }
+  };
+
+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 {
+public:
+  cViaccessCardInfos(void):cCardInfos(SYSTEM_NAME) {}
+  };
+
+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 *ecms, unsigned short sysId, const unsigned char *data, int len);
+  };
+
+cSystemViaccess::cSystemViaccess(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+  hasLogger=true;
+}
+
+void cSystemViaccess::ParseCADescriptor(cSimpleList *ecms, unsigned short sysId, 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,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; i0) {
+          unsigned char newKey[MAX_NEW_KEYS][8];
+          int numKeys=0, updPrv[MAX_NEW_KEYS]={}, updKey[MAX_NEW_KEYS]={};
+
+          for(unsigned int cnt=0; cntkey[7];
+                  for(unsigned int kc=0 ; kcsa,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();
+                    }
+                  cLoaders::SaveCache();
+                  }
+                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; }
+  virtual bool Init(const char *cfgdir);
+  };
+
+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);
+}
+
+bool cSystemLinkViaccess::Init(const char *cfgdir)
+{
+  Vcards.Load(cfgdir,SYSTEM_NAME,"Viaccess.KID");
+  return true;
+}
diff --git a/systems/viaccess/viaccess.h b/systems/viaccess/viaccess.h
new file mode 100644
index 0000000..47cff12
--- /dev/null
+++ b/systems/viaccess/viaccess.h
@@ -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/systems/viaccess/viaccess.mk b/systems/viaccess/viaccess.mk
new file mode 100644
index 0000000..c5fde80
--- /dev/null
+++ b/systems/viaccess/viaccess.mk
@@ -0,0 +1,5 @@
+#
+# Viaccess
+#
+TARGET = viaccess
+OBJS   = viaccess.o tps.o st20.o
diff --git a/testing/Makefile b/testing/Makefile
new file mode 100644
index 0000000..209f8e7
--- /dev/null
+++ b/testing/Makefile
@@ -0,0 +1,66 @@
+
+### 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
+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
+
+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 ../systems/nagra/nagra2*.c ../systems/nagra/cpu.c
+testN2Emu: testN2Emu.o $(SHAREDOBJS) $(NOBJS)
+	$(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+filterhelper: filterhelper.o
+	$(CXX) $(CXXFLAGS) $^ -o $@
+clean:
+	@-rm -f *.o core* *~
+	@-rm -f testECM testEMM testN1Emu testN2Emu
+	@-rm -f filterhelper
diff --git a/testing/compat.c b/testing/compat.c
new file mode 100644
index 0000000..d7c3caa
--- /dev/null
+++ b/testing/compat.c
@@ -0,0 +1,457 @@
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#define DEBUG
+#include "common.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.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;
+  cSystems::ConfigParse("Cardclient.Immediate","0");
+
+  filemaps.SetCfgDir(cfgdir);
+  if(!keys.Load(cfgdir)) printf("ERROR: no keys loaded for softcam!\n");
+  if(!cSystems::Init(cfgdir)) exit(2);
+  cLoaders::LoadCache(cfgdir);
+  smartcards.LoadData(cfgdir);
+#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=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; iPersistant() && !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 
+
+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 
+
+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/testing/compat.h b/testing/compat.h
new file mode 100644
index 0000000..f5b2b3f
--- /dev/null
+++ b/testing/compat.h
@@ -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/testing/filterhelper.c b/testing/filterhelper.c
new file mode 100644
index 0000000..bdbd8e8
--- /dev/null
+++ b/testing/filterhelper.c
@@ -0,0 +1,117 @@
+
+#include 
+#include 
+#include 
+#include 
+
+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; i255) {
+        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/testing/testECM.c b/testing/testECM.c
new file mode 100644
index 0000000..b314dea
--- /dev/null
+++ b/testing/testECM.c
@@ -0,0 +1,33 @@
+
+#include 
+#include 
+
+#include "data.h"
+#include "system.h"
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+  if(argc<5) {
+    printf("usage: %s     \n",argv[0]);
+    return 1;
+    }
+  DllsLoad(argv[1]);
+  InitAll(argv[2]);
+  unsigned char ecm[4096];
+  ReadRaw(argv[5],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);
+  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);
+    delete sys;
+    if(res) break;
+    }
+}
diff --git a/testing/testEMM.c b/testing/testEMM.c
new file mode 100644
index 0000000..f93d037
--- /dev/null
+++ b/testing/testEMM.c
@@ -0,0 +1,38 @@
+
+#include 
+#include 
+
+#include "common.h"
+#include "data.h"
+#include "system.h"
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+  if(argc<4) {
+    printf("usage: %s    \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; iProcessEMM(0x123,caid,&emm[i]);
+        i+=s;
+        }
+      }
+    delete sys;
+    }
+}
diff --git a/testing/testN1Emu.c b/testing/testN1Emu.c
new file mode 100644
index 0000000..32eb11f
--- /dev/null
+++ b/testing/testN1Emu.c
@@ -0,0 +1,138 @@
+
+#include 
+#include 
+
+#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(iMatches(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   \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/testing/testN2Emu.c b/testing/testN2Emu.c
new file mode 100644
index 0000000..00b1b3b
--- /dev/null
+++ b/testing/testN2Emu.c
@@ -0,0 +1,172 @@
+
+#include 
+#include 
+
+#define TESTER
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/cpu.c"
+#include "systems/nagra/nagra2.c"
+
+#include "compat.h"
+
+static const unsigned char key3des[16] = {  };
+
+void Emm(unsigned char *emmdata, int cmdLen, int id)
+{
+  cN2Prov *emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+  if(emmP) printf("provider %04x capabilities%s%s%s%s\n",id,
+                    emmP->HasFlags(N2FLAG_MECM)    ?" MECM":"",
+                    emmP->HasFlags(N2FLAG_Bx)      ?" Bx":"",
+                    emmP->HasFlags(N2FLAG_POSTAU)  ?" POSTPROCAU":"",
+                    emmP->HasFlags(N2FLAG_INV)     ?" INVCW":"");
+
+  for(int i=8+2+4+4; iPostProcAU(id,&emmdata[i])) {
+            printf("key%02x: ",(emmdata[i+3]&0x40)>>6);
+            SDump(&emmdata[i+7],16);
+            }
+          }
+        i+=23;
+        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+=39;
+        break;
+      case 0x83: // change data prov. id
+        id=(emmdata[i+1]<<8)|emmdata[i+2];
+        printf("keyid: %04x\n",id);
+        i+=3;
+        break;
+      case 0xA4: // conditional (always no match assumed for now)
+        i+=emmdata[i+1]+2+4;
+        break;
+      case 0xA6:
+        i+=emmdata[i+1]+1;
+        break;
+      case 0x13:
+      case 0x14:
+      case 0x15:
+        i+=4;
+        break;
+      case 0xB0 ... 0xBF: // Update with ROM CPU code
+        {
+        int bx=emmdata[i]&15;
+        if(!emmP || !emmP->HasFlags(N2FLAG_Bx)) {
+          printf("B%X for provider %04x not supported\n",bx,id);
+          i=cmdLen;
+          break;
+          }
+        int r;
+        if((r=emmP->ProcessBx(emmdata,cmdLen,i+1))>0)
+          i+=r;
+        else {
+          printf("B%X executing failed for %04x\n",bx,id);
+          i=cmdLen;
+          }
+        break;
+        }
+      case 0xE3: // Eeprom update
+        i+=emmdata[i+4]+4;
+        break;
+      case 0xE1:
+      case 0xE2:
+      case 0x00: // end of processing
+        i=cmdLen;
+        break;
+      default:
+        i++;
+        continue;
+      }
+    }
+}
+
+bool Ecm(unsigned char *buff, int cmdLen, int id)
+{
+  unsigned char cw[16];
+
+  cN2Prov *ecmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+  if(ecmP) printf("provider %04x capabilities%s%s%s%s\n",id,
+                    ecmP->HasFlags(N2FLAG_MECM)    ?" MECM":"",
+                    ecmP->HasFlags(N2FLAG_Bx)      ?" Bx":"",
+                    ecmP->HasFlags(N2FLAG_POSTAU)  ?" POSTPROCAU":"",
+                    ecmP->HasFlags(N2FLAG_INV)     ?" INVCW":"");
+
+  int l=0, mecmAlgo=0;
+  for(int i=16; i0) {
+    if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+      if(!ecmP->MECM(buff[15],mecmAlgo,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<4) {
+    printf("usage: %s    \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]);
+  unsigned char data[256];
+  int len=ReadRaw(argv[4],data,sizeof(data));
+  if((mode==1 && len!=64) || (mode==2 && len!=96)) {
+    printf("bad raw file format\n");
+    return 1;
+    }
+
+  int id=strtol(argv[2],0,0);
+  if(mode==1) Ecm(data,len,id);
+  else if(mode==2) Emm(data,len,id);
+}
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..5c4ba1b
--- /dev/null
+++ b/version.h
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+// all release versions must end with 0xFF !!
+#define SCVERSNUM 0x000800FF
+#define SCVERSION "0.8.0"
+
+extern const char *ScVersion;
+
+#endif
-- 
2.39.5