]> xmof Git - DeDRM.git/commitdiff
tools v2.1
authorApprentice Alf <apprenticealf@gmail.com>
Tue, 26 Oct 2010 17:18:46 +0000 (18:18 +0100)
committerApprentice Alf <apprenticealf@gmail.com>
Tue, 3 Mar 2015 18:18:52 +0000 (18:18 +0000)
combined kindle/mobi plugin

110 files changed:
Calibre_Plugins/K4PCDeDRM_plugin.zip [deleted file]
Calibre_Plugins/K4PCDeDRM_plugin/K4PCDeDRM_plugin.py [deleted file]
Calibre_Plugins/MobiDeDRM_plugin.zip [deleted file]
Calibre_Plugins/README-K4MobiDeDRM-plugin.txt [new file with mode: 0644]
Calibre_Plugins/README-K4PCDeDRM-plugin.txt [deleted file]
Calibre_Plugins/README-eReaderPDB2PML-plugin.txt [new file with mode: 0644]
Calibre_Plugins/README_MobiDeDRM_plugin.txt [deleted file]
Calibre_Plugins/eReaderPDB2PML_plugin.zip [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML-README.txt [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/erdr2pml.py [moved from eReader_Tools/lib/erdr2pml.py with 100% similarity]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/all-wcprops [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/dir-prop-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/entries [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/__init__.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/classes.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/core.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/kdictproxy.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/logger.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/profiler.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/support.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/__init__.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/classes.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/core.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/kdictproxy.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/logger.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/profiler.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/support.py.svn-base [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/__init__.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/classes.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/core.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/kdictproxy.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/logger.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/profiler.py [new file with mode: 0644]
Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/support.py [new file with mode: 0644]
Calibre_Plugins/k4mobidedrm_plugin.zip [new file with mode: 0644]
Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py [new file with mode: 0644]
Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py [new file with mode: 0644]
Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py [new file with mode: 0644]
Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py [moved from Kindle_Mobi_Tools/lib/mobidedrm.py with 89% similarity]
Kindle_4_PC_Tools/K4PCDeDRM.pyw [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe [deleted file]
Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/ReadMe.txt [deleted file]
Kindle_4_PC_Tools/Other_Tools/README_Other_K4PC_Tools.txt [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/Makefile [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/README [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/b64.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/libz.a [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.exe [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.c [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/zconf.h [deleted file]
Kindle_4_PC_Tools/Other_Tools/skindle-06/zlib.h [deleted file]
Kindle_4_PC_Tools/README_K4PCDeDRM.txt [deleted file]
Kindle_4_PC_Tools/lib/k4pcdedrm.py [deleted file]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw [moved from Kindle_Mobi_Tools/MobiDeDRM.pyw with 71% similarity]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/README_K4MobiDeDRM.txt [new file with mode: 0644]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py [new file with mode: 0644]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mutils.py [new file with mode: 0644]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4pcutils.py [new file with mode: 0644]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py [moved from Calibre_Plugins/MobiDeDRM_plugin/MobiDeDRM_plugin.py with 89% similarity]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/scrolltextwidget.py [moved from Kindle_4_Mac_Tools/lib/scrolltextwidget.py with 100% similarity]
Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/subasyncio.py [moved from Kindle_4_Mac_Tools/lib/subasyncio.py with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/K4Munswindle.pyw [moved from Kindle_4_Mac_Tools/K4Munswindle.pyw with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/README_K4Munswindle.txt [moved from Kindle_4_Mac_Tools/README_K4Mac_Tools.txt with 70% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/gdb_kindle_cmds_r1.txt [moved from Kindle_4_Mac_Tools/gdb_kindle_cmds_r1.txt with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/gdb_kindle_cmds_r2.txt [moved from Kindle_4_Mac_Tools/gdb_kindle_cmds_r2.txt with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py [moved from Kindle_4_Mac_Tools/lib/mobidedrm.py with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/scrolltextwidget.py [moved from Kindle_4_PC_Tools/lib/scrolltextwidget.py with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/subasyncio.py [moved from Kindle_4_PC_Tools/lib/subasyncio.py with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/README-unswindlev7.txt [new file with mode: 0644]
Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/mobidedrm.py [moved from Kindle_4_PC_Tools/Other_Tools/unswindle/mobidedrm.py with 100% similarity]
Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/unswindle.pyw [moved from Kindle_4_PC_Tools/Other_Tools/unswindle/unswindle_v7.pyw with 100% similarity]
Kindle_Mobi_Tools/PIDCheck.py [deleted file]
Kindle_Mobi_Tools/REAME_MobiDeDRM.txt [deleted file]
Kindle_Mobi_Tools/lib/readme.txt [deleted file]
Mobi_Additional_Tools/KindlePID.pyw [moved from Kindle_Mobi_Tools/KindlePID.pyw with 100% similarity]
Mobi_Additional_Tools/Kindleizer.pyw [moved from Kindle_Mobi_Tools/Kindleizer.pyw with 100% similarity]
Mobi_Additional_Tools/README_Kindle_for_iPad_iPhone_iPodTouch.txt [moved from Kindle_Mobi_Tools/README_Kindle_for_iPad_iPhone_iPodTouch.txt with 100% similarity]
Mobi_Additional_Tools/lib/kindlefix.py [moved from Kindle_Mobi_Tools/lib/kindlefix.py with 100% similarity]
Mobi_Additional_Tools/lib/kindlepid.py [moved from Kindle_Mobi_Tools/lib/kindlepid.py with 100% similarity]
Mobi_Additional_Tools/lib/mobidedrm.py [new file with mode: 0644]
Mobi_Additional_Tools/lib/mobihuff.py [moved from Kindle_Mobi_Tools/lib/mobihuff.py with 100% similarity]
Mobi_Additional_Tools/lib/prc.py [moved from Kindle_Mobi_Tools/lib/prc.py with 100% similarity]
Mobi_Additional_Tools/lib/scrolltextwidget.py [moved from Kindle_Mobi_Tools/lib/scrolltextwidget.py with 100% similarity]
Mobi_Additional_Tools/lib/subasyncio.py [moved from Kindle_Mobi_Tools/lib/subasyncio.py with 100% similarity]
eReader_PDB_Tools/Pml2HTML.pyw [moved from eReader_Tools/Pml2HTML.pyw with 100% similarity]
eReader_PDB_Tools/README_eReaderPDB.txt [moved from eReader_Tools/README_eReaderPDB.txt with 100% similarity]
eReader_PDB_Tools/eReaderPDB2PML.pyw [moved from eReader_Tools/eReaderPDB2PML.pyw with 100% similarity]
eReader_PDB_Tools/eReaderPDB2PMLZ.pyw [moved from eReader_Tools/eReaderPDB2PMLZ.pyw with 100% similarity]
eReader_PDB_Tools/lib/erdr2pml.py [new file with mode: 0644]
eReader_PDB_Tools/lib/ereader2html.py [moved from eReader_Tools/lib/ereader2html.py with 100% similarity]
eReader_PDB_Tools/lib/scrolltextwidget.py [moved from eReader_Tools/lib/scrolltextwidget.py with 100% similarity]
eReader_PDB_Tools/lib/subasyncio.py [moved from eReader_Tools/lib/subasyncio.py with 100% similarity]
eReader_PDB_Tools/lib/xpml2xhtml.py [moved from eReader_Tools/lib/xpml2xhtml.py with 100% similarity]

diff --git a/Calibre_Plugins/K4PCDeDRM_plugin.zip b/Calibre_Plugins/K4PCDeDRM_plugin.zip
deleted file mode 100644 (file)
index ec61348..0000000
Binary files a/Calibre_Plugins/K4PCDeDRM_plugin.zip and /dev/null differ
diff --git a/Calibre_Plugins/K4PCDeDRM_plugin/K4PCDeDRM_plugin.py b/Calibre_Plugins/K4PCDeDRM_plugin/K4PCDeDRM_plugin.py
deleted file mode 100644 (file)
index 509987e..0000000
+++ /dev/null
@@ -1,682 +0,0 @@
-#!/usr/bin/env python
-#
-# This is a WINDOWS python script. You need a Python interpreter to run it.
-# For example, ActiveState Python, which exists for windows.
-#
-# It can run standalone to convert K4PC files, or it can be installed as a
-# plugin for Calibre (http://calibre-ebook.com/about) so that importing
-# K4PC files with DRM is no londer a multi-step process.
-#
-# ***NOTE*** Calibre and K4PC must be installed on the same windows machine
-# for the plugin version to function properly.
-#
-# To create a Calibre plugin, rename this file so that the filename
-# ends in '_plugin.py', put it into a ZIP file and import that ZIP into Calibre
-# using its plugin configuration GUI.
-#
-# Thanks to The Dark Reverser for MobiDeDrm and CMBDTC for cmbdtc_dump from
-# which this script steals most unashamedly.
-#
-# Changelog
-#  0.01 - Initial version - Utilizes skindle and CMBDTC method of obtaining
-#         book specific pids from K4PC books. If Calibre and K4PC are installed
-#         on the same windows machine, Calibre plugin functionality is once
-#         again restored.
-
-
-"""
-
-Comprehensive Mazama Book DRM with Topaz Cryptography V2.0
-
------BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
-M8hYfnNEI0yQmn5Ti+W8biT7EatpauE/5jgQMPBmdNrDr1hbHyHBSP7xeC2qlRWC
-B62UCxeu/fpfnvNHDN/wPWWH4jynZ2M6cdcnE5LQ+FfeKqZn7gnG2No1U9h7oOHx
-y2/pHuYme7U1TsgSjwIDAQAB
------END PUBLIC KEY-----
-
-"""
-
-from __future__ import with_statement
-
-import csv
-import sys
-import os
-import getopt
-import zlib
-import binascii
-from struct import pack
-from struct import unpack
-from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
-    create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
-    string_at, Structure, c_void_p, cast
-import _winreg as winreg
-import traceback
-import hashlib
-
-__version__ = '0.01'
-
-global kindleDatabase
-MAX_PATH = 255
-kernel32 = windll.kernel32
-advapi32 = windll.advapi32
-crypt32 = windll.crypt32
-
-
-#
-# Various character maps used to decrypt books. Probably supposed to act as obfuscation
-#
-charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
-charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
-charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
-charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
-
-
-#
-# Exceptions for all the problems that might happen during the script
-#
-class DrmException(Exception):
-    pass
-    
-
-class DataBlob(Structure):
-    _fields_ = [('cbData', c_uint),
-                ('pbData', c_void_p)]
-DataBlob_p = POINTER(DataBlob)
-
-
-def GetSystemDirectory():
-    GetSystemDirectoryW = kernel32.GetSystemDirectoryW
-    GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
-    GetSystemDirectoryW.restype = c_uint
-    def GetSystemDirectory():
-        buffer = create_unicode_buffer(MAX_PATH + 1)
-        GetSystemDirectoryW(buffer, len(buffer))
-        return buffer.value
-    return GetSystemDirectory
-GetSystemDirectory = GetSystemDirectory()
-
-
-def GetVolumeSerialNumber():
-    GetVolumeInformationW = kernel32.GetVolumeInformationW
-    GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
-                                      POINTER(c_uint), POINTER(c_uint),
-                                      POINTER(c_uint), c_wchar_p, c_uint]
-    GetVolumeInformationW.restype = c_uint
-    def GetVolumeSerialNumber(path):
-        vsn = c_uint(0)
-        GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
-        return vsn.value
-    return GetVolumeSerialNumber
-GetVolumeSerialNumber = GetVolumeSerialNumber()
-
-
-def GetUserName():
-    GetUserNameW = advapi32.GetUserNameW
-    GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
-    GetUserNameW.restype = c_uint
-    def GetUserName():
-        buffer = create_unicode_buffer(32)
-        size = c_uint(len(buffer))
-        while not GetUserNameW(buffer, byref(size)):
-            buffer = create_unicode_buffer(len(buffer) * 2)
-            size.value = len(buffer)
-        return buffer.value.encode('utf-16-le')[::2]
-    return GetUserName
-GetUserName = GetUserName()
-
-
-def CryptUnprotectData():
-    _CryptUnprotectData = crypt32.CryptUnprotectData
-    _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
-                                   c_void_p, c_void_p, c_uint, DataBlob_p]
-    _CryptUnprotectData.restype = c_uint
-    def CryptUnprotectData(indata, entropy):
-        indatab = create_string_buffer(indata)
-        indata = DataBlob(len(indata), cast(indatab, c_void_p))
-        entropyb = create_string_buffer(entropy)
-        entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
-        outdata = DataBlob()
-        if not _CryptUnprotectData(byref(indata), None, byref(entropy),
-                                   None, None, 0, byref(outdata)):
-            raise DrmException("Failed to Unprotect Data")
-        return string_at(outdata.pbData, outdata.cbData)
-    return CryptUnprotectData
-CryptUnprotectData = CryptUnprotectData()
-
-
-#
-# Returns the MD5 digest of "message"
-#
-def MD5(message):
-    ctx = hashlib.md5()
-    ctx.update(message)
-    return ctx.digest()
-
-
-#
-# Returns the MD5 digest of "message"
-#
-def SHA1(message):
-    ctx = hashlib.sha1()
-    ctx.update(message)
-    return ctx.digest()
-
-
-#
-# Locate and open the Kindle.info file.
-#
-def openKindleInfo():
-    regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
-    path = winreg.QueryValueEx(regkey, 'Local AppData')[0] 
-    return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
-
-
-#
-# Parse the Kindle.info file and return the records as a list of key-values
-#
-def parseKindleInfo():
-    DB = {}
-    infoReader = openKindleInfo()
-    infoReader.read(1)
-    data = infoReader.read()
-    items = data.split('{')
-    
-    for item in items:
-        splito = item.split(':')
-        DB[splito[0]] =splito[1]
-    return DB
-
-
-#
-# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. (Totally not optimal)
-#
-def findNameForHash(hash):
-    names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
-    result = ""
-    for name in names:
-        if hash == encodeHash(name, charMap2):
-           result = name
-           break
-    return name
-
-    
-#
-# Print all the records from the kindle.info file.
-#
-def printKindleInfo():
-    for record in kindleDatabase:
-        name = findNameForHash(record)
-        if name != "" :
-            print (name)
-            print ("--------------------------\n")
-        else :
-            print ("Unknown Record")
-        print getKindleInfoValueForHash(record)
-        print "\n"
-
-
-#
-# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
-#
-def getKindleInfoValueForHash(hashedKey):
-    global kindleDatabase
-    encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
-    return CryptUnprotectData(encryptedValue,"")
-
-
-#
-#  Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
-#
-def getKindleInfoValueForKey(key):
-    return getKindleInfoValueForHash(encodeHash(key,charMap2))
-
-
-#
-# 8 bits to six bits encoding from hash to generate PID string
-#  
-def encodePID(hash):
-    global charMap3
-    PID = ""
-    for position in range (0,8):
-        PID += charMap3[getSixBitsFromBitField(hash,position)]
-    return PID
-
-
-#
-# Hash the bytes in data and then encode the digest with the characters in map
-#
-def encodeHash(data,map):
-    return encode(MD5(data),map)
-
-   
-#
-# Encode the bytes in data with the characters in map
-#
-def encode(data, map):
-    result = ""
-    for char in data:
-        value = ord(char)
-        Q = (value ^ 0x80) // len(map)
-        R = value % len(map)
-        result += map[Q]
-        result += map[R]
-    return result
-
-
-#
-# Decode the string in data with the characters in map. Returns the decoded bytes
-#
-def decode(data,map):
-    result = ""
-    for i in range (0,len(data),2):
-        high = map.find(data[i])
-        low = map.find(data[i+1])
-        value = (((high * 0x40) ^ 0x80) & 0xFF) + low
-        result += pack("B",value)
-    return result
-
-
-#
-# Encryption table used to generate the device PID
-#
-def generatePidEncryptionTable() :
-    table = []
-    for counter1 in range (0,0x100):
-        value = counter1
-        for counter2 in range (0,8):
-            if (value & 1 == 0) :
-                value = value >> 1
-            else :
-                value = value >> 1
-                value = value ^ 0xEDB88320
-        table.append(value)
-    return table
-
-
-#
-# Seed value used to generate the device PID
-#
-def generatePidSeed(table,dsn) :
-    value = 0
-    for counter in range (0,4) :
-       index = (ord(dsn[counter]) ^ value) &0xFF
-       value = (value >> 8) ^ table[index]
-    return value
-
-   
-#
-# Generate the device PID
-#
-def generateDevicePID(table,dsn,nbRoll):
-    seed = generatePidSeed(table,dsn)
-    pidAscii = ""
-    pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
-    index = 0
-    
-    for counter in range (0,nbRoll):
-        pid[index] = pid[index] ^ ord(dsn[counter])
-        index = (index+1) %8
-    for counter in range (0,8):
-        index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
-        pidAscii += charMap4[index]
-    return pidAscii
-
-
-#
-# Returns two bit at offset from a bit field
-#
-def getTwoBitsFromBitField(bitField,offset):
-    byteNumber = offset // 4
-    bitPosition = 6 - 2*(offset % 4)
-    
-    return ord(bitField[byteNumber]) >> bitPosition & 3
-
-
-#
-# Returns the six bits at offset from a bit field
-#    
-def getSixBitsFromBitField(bitField,offset):
-    offset *= 3
-    value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
-    return value
-
-
-#
-# MobiDeDrm-0.16 Stuff
-#
-class Unbuffered:
-    def __init__(self, stream):
-        self.stream = stream
-    def write(self, data):
-        self.stream.write(data)
-        self.stream.flush()
-    def __getattr__(self, attr):
-        return getattr(self.stream, attr)
-
-
-# Implementation of Pukall Cipher 1
-def PC1(key, src, decryption=True):
-    sum1 = 0;
-    sum2 = 0;
-    keyXorVal = 0;
-    if len(key)!=16:
-        print "Bad key length!"
-        return None
-    wkey = []
-    for i in xrange(8):
-        wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
-
-    dst = ""
-    for i in xrange(len(src)):
-        temp1 = 0;
-        byteXorVal = 0;
-        for j in xrange(8):
-            temp1 ^= wkey[j]
-            sum2  = (sum2+j)*20021 + sum1
-            sum1  = (temp1*346)&0xFFFF
-            sum2  = (sum2+sum1)&0xFFFF
-            temp1 = (temp1*20021+1)&0xFFFF
-            byteXorVal ^= temp1 ^ sum2
-        curByte = ord(src[i])
-        if not decryption:
-            keyXorVal = curByte * 257;
-        curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
-        if decryption:
-            keyXorVal = curByte * 257;
-        for j in xrange(8):
-            wkey[j] ^= keyXorVal;
-        dst+=chr(curByte)
-    return dst
-
-
-def getSizeOfTrailingDataEntries(ptr, size, flags):
-    def getSizeOfTrailingDataEntry(ptr, size):
-        bitpos, result = 0, 0
-        if size <= 0:
-            return result
-        while True:
-            v = ord(ptr[size-1])
-            result |= (v & 0x7F) << bitpos
-            bitpos += 7
-            size -= 1
-            if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
-                return result
-    num = 0
-    testflags = flags >> 1
-    while testflags:
-        if testflags & 1:
-            num += getSizeOfTrailingDataEntry(ptr, size - num)
-        testflags >>= 1
-    # Multibyte data, if present, is included in the encryption, so
-    # we do not need to check the low bit.
-    # if flags & 1:
-    #    num += (ord(ptr[size - num - 1]) & 0x3) + 1
-    return num
-
-
-#
-# This class does all the heavy lifting.
-#
-class DrmStripper:
-    def loadSection(self, section):
-        if (section + 1 == self.num_sections):
-            endoff = len(self.data_file)
-        else:
-            endoff = self.sections[section + 1][0]
-        off = self.sections[section][0]
-        return self.data_file[off:endoff]
-
-    def patch(self, off, new):
-        self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
-
-    def patchSection(self, section, new, in_off = 0):
-        if (section + 1 == self.num_sections):
-            endoff = len(self.data_file)
-        else:
-            endoff = self.sections[section + 1][0]
-        off = self.sections[section][0]
-        assert off + in_off + len(new) <= endoff
-        self.patch(off + in_off, new)
-
-    def parseDRM(self, data, count, pid):
-        pid = pid.ljust(16,'\0')
-        keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
-        temp_key = PC1(keyvec1, pid, False)
-        temp_key_sum = sum(map(ord,temp_key)) & 0xff
-        found_key = None
-        for i in xrange(count):
-            verification, size, type, cksum, cookie = unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
-            cookie = PC1(temp_key, cookie)
-            ver,flags,finalkey,expiry,expiry2 = unpack('>LL16sLL', cookie)
-            if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
-                found_key = finalkey
-                break
-        if not found_key:
-            # Then try the default encoding that doesn't require a PID
-            temp_key = keyvec1
-            temp_key_sum = sum(map(ord,temp_key)) & 0xff
-            for i in xrange(count):
-                verification, size, type, cksum, cookie = unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
-                cookie = PC1(temp_key, cookie)
-                ver,flags,finalkey,expiry,expiry2 = unpack('>LL16sLL', cookie)
-                if verification == ver and cksum == temp_key_sum:
-                    found_key = finalkey
-                    break
-        return found_key
-
-    def __init__(self, data_file):
-        self.data_file = data_file
-        header = data_file[0:72]
-        if header[0x3C:0x3C+8] != 'BOOKMOBI':
-            raise DrmException("invalid file format")
-        self.num_sections, = unpack('>H', data_file[76:78])
-
-        self.sections = []
-        for i in xrange(self.num_sections):
-            offset, a1,a2,a3,a4 = unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
-            flags, val = a1, a2<<16|a3<<8|a4
-            self.sections.append( (offset, flags, val) )
-
-        sect = self.loadSection(0)
-        records, = unpack('>H', sect[0x8:0x8+2])
-        mobi_length, = unpack('>L',sect[0x14:0x18])
-        mobi_version, = unpack('>L',sect[0x68:0x6C])
-        extra_data_flags = 0
-        print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length)
-        if (mobi_length >= 0xE4) and (mobi_version >= 5):
-            extra_data_flags, = unpack('>H', sect[0xF2:0xF4])
-            print "Extra Data Flags = %d" %extra_data_flags
-
-        crypto_type, = unpack('>H', sect[0xC:0xC+2])
-        if crypto_type == 0:
-            print "This book is not encrypted."
-        else:
-            if crypto_type == 1:
-                raise DrmException("cannot decode Mobipocket encryption type 1")
-            if crypto_type != 2:
-                raise DrmException("unknown encryption type: %d" % crypto_type)
-
-            # determine the EXTH Offset.
-            exth_off = unpack('>I', sect[20:24])[0] + 16 + self.sections[0][0]
-            # Grab the entire EXTH block and feed it to the getK4PCPids function.
-            exth = data_file[exth_off:self.sections[0+1][0]]
-            pid = getK4PCPids(exth)
-
-            # calculate the keys
-            drm_ptr, drm_count, drm_size, drm_flags = unpack('>LLLL', sect[0xA8:0xA8+16])
-            if drm_count == 0:
-                raise DrmException("no PIDs found in this file")
-            found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
-            if not found_key:
-                raise DrmException("no key found. maybe the PID is incorrect")
-
-            # kill the drm keys
-            self.patchSection(0, "\0" * drm_size, drm_ptr)
-            # kill the drm pointers
-            self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
-            # clear the crypto type
-            self.patchSection(0, "\0" * 2, 0xC)
-
-            # decrypt sections
-            print "\nDecrypting. Please wait . . .",
-            new_data = self.data_file[:self.sections[1][0]]
-            for i in xrange(1, records+1):
-                data = self.loadSection(i)
-                extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
-                if i%100 == 0:
-                    print ".",
-                # print "record %d, extra_size %d" %(i,extra_size)
-                new_data += PC1(found_key, data[0:len(data) - extra_size])
-                if extra_size > 0:
-                    new_data += data[-extra_size:]
-                #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
-            if self.num_sections > records+1:
-                new_data += self.data_file[self.sections[records+1][0]:]
-            self.data_file = new_data
-            print "done!"
-            print "\nPlease only use your new-found powers for good."
-
-    def getResult(self):
-        return self.data_file
-
-
-#
-# DiapDealer's stuff: Parse the EXTH header records and parse the Kindleinfo
-# file to calculate the book pid.
-#
-def getK4PCPids(exth):
-    global kindleDatabase
-    try:
-        kindleDatabase = parseKindleInfo()
-    except Exception as message:
-        print(message)
-    
-    if kindleDatabase != None :
-  
-        # Get the Mazama Random number
-        MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
-    
-        # Get the HDD serial
-        encodedSystemVolumeSerialNumber = encodeHash(str(GetVolumeSerialNumber(GetSystemDirectory().split('\\')[0] + '\\')),charMap1)
-    
-        # Get the current user name
-        encodedUsername = encodeHash(GetUserName(),charMap1)
-    
-        # concat, hash and encode to calculate the DSN
-        DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
-       
-        print("\nDSN: " + DSN)
-    
-
-        # Compute the device PID (for which I can tell, is used for nothing).
-        # But hey, stuff being printed out is apparently cool.
-        table =  generatePidEncryptionTable()
-        devicePID = generateDevicePID(table,DSN,4)
-            
-        print("Device PID: " + devicePID)
-            
-        # Compute book PID
-        exth_records = {}
-        nitems, = unpack('>I', exth[8:12])
-        pos = 12
-        # Parse the EXTH records, storing data indexed by type
-        for i in xrange(nitems):
-            type, size = unpack('>II', exth[pos: pos + 8])
-            content = exth[pos + 8: pos + size]
-
-            exth_records[type] = content
-            pos += size
-
-        # Grab the contents of the type 209 exth record
-        if exth_records[209] != None:
-            data = exth_records[209]
-        else:
-            raise DrmException("\nNo EXTH record type 209 - Perhaps not a K4PC file?")
-        
-        # Parse the 209 data to find the the exth record with the token data.
-        # The last character of the 209 data points to the record with the token.
-        # Always 208 from my experience, but I'll leave the logic in case that changes.
-        for i in xrange(len(data)):
-            if ord(data[i]) != 0:
-                if exth_records[ord(data[i])] != None:
-                    token = exth_records[ord(data[i])]
-
-        # Get the kindle account token
-        kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
-    
-        print("Account Token: " + kindleAccountToken)
-
-        pidHash = SHA1(DSN+kindleAccountToken+exth_records[209]+token)
-   
-        bookPID = encodePID(pidHash)
-
-        if exth_records[503] != None:
-            print "Pid for " + exth_records[503] + ": " + bookPID
-        else:
-            print ("Book PID: " + bookPID )
-            
-        return bookPID
-    
-    raise DrmException("\nCould not access K4PC data - Perhaps K4PC is not installed/configured?")
-    return null
-
-if not __name__ == "__main__":
-    from calibre.customize import FileTypePlugin
-
-    class K4PCDeDRM(FileTypePlugin):
-        name                = 'K4PCDeDRM' # Name of the plugin
-        description         = 'Removes DRM from K4PC files'
-        supported_platforms = ['windows'] # Platforms this plugin will run on
-        author              = 'DiapDealer' # The author of this plugin
-        version             = (0, 0, 1)   # The version number of this plugin
-        file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
-        on_import           = True # Run this plugin during the import
-
-        def run(self, path_to_ebook):
-            from calibre.gui2 import is_ok_to_use_qt
-            from PyQt4.Qt import QMessageBox
-            data_file = file(path_to_ebook, 'rb').read()
-
-            try:
-                unlocked_file = DrmStripper(data_file).getResult()
-            except DrmException:
-                # ignore the error
-                pass
-            else:
-                of = self.temporary_file('.mobi')
-                of.write(unlocked_file)
-                of.close()
-                return of.name
-
-            if is_ok_to_use_qt():
-                d = QMessageBox(QMessageBox.Warning, "K4PCDeDRM Plugin", "Couldn't decode: %s\n\nImporting encrypted version." % path_to_ebook)
-                d.show()
-                d.raise_()
-                d.exec_()
-            return path_to_ebook
-
-        #def customization_help(self, gui=False):
-        #    return 'Enter PID (separate multiple PIDs with comma)'
-
-if __name__ == "__main__":
-    sys.stdout=Unbuffered(sys.stdout)
-    print ('K4PCDeDrm v%(__version__)s '
-          'provided DiapDealer.' % globals())
-    if len(sys.argv)<3:
-        print "Removes DRM protection from K4PC books"
-        print "Usage:"
-        print "    %s <infile> <outfile>" % sys.argv[0]
-        sys.exit(1)
-    else:
-        infile = sys.argv[1]
-        outfile = sys.argv[2]
-        data_file = file(infile, 'rb').read()
-        try:
-            strippedFile = DrmStripper(data_file)
-            file(outfile, 'wb').write(strippedFile.getResult())
-        except DrmException, e:
-            print "Error: %s" % e
-            sys.exit(1)
-    sys.exit(0)
\ No newline at end of file
diff --git a/Calibre_Plugins/MobiDeDRM_plugin.zip b/Calibre_Plugins/MobiDeDRM_plugin.zip
deleted file mode 100644 (file)
index dc184a3..0000000
Binary files a/Calibre_Plugins/MobiDeDRM_plugin.zip and /dev/null differ
diff --git a/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt b/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt
new file mode 100644 (file)
index 0000000..036cfb0
--- /dev/null
@@ -0,0 +1,23 @@
+Plugin for K4PC, K4Mac and Mobi Books
+
+Will work on Linux (standard DRM Mobi books only), Mac OS X (standard DRM Mobi books and "Kindle for Mac" books, and Windows (standard DRM Mobi books and "Kindle for PC" books.
+
+This plugin supersedes MobiDeDRM, K4DeDRM, and K4PCDeDRM plugins.  If you install this plugin, those plugins can be safely removed.
+
+This plugin is meant to convert "Kindle for PC", "Kindle for Mac" and "Mobi" ebooks with DRM to unlocked Mobi files. Calibre can then convert them to whatever format you desire. It is meant to function without having to install any  dependencies except for Calibre being on your same machine and in the same account as your "Kindle for PC" or "Kindle for Mac" application if you are going to remove the DRM from those types of books.
+
+Installation:
+Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file  (k4mobidedrm_vXX_plugin.zip) and click the 'Add' button. You're done.
+
+Configuration:
+Highlight the plugin (K4MobiDeDRM under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter a comma separated list of your 10 digit PIDs. This is not needed if you only want to decode "Kindle for PC" or "Kindle for Mac" books. 
+
+
+Troubleshooting:
+If you find that it's not working for you (imported azw's are not converted to mobi format), you can save a lot of time and trouble by trying to add the azw file to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might
+as well get used to it. ;)
+
+Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.azw". Don't type the quotes and obviously change the 'your_ebook.azw' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
+
+** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
+
diff --git a/Calibre_Plugins/README-K4PCDeDRM-plugin.txt b/Calibre_Plugins/README-K4PCDeDRM-plugin.txt
deleted file mode 100644 (file)
index 229dbd4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-K4PCDeDRM - K4PCDeDRM_X.XX_plugin.zip
-Requires Calibre version 0.6.44 or higher.
-
-This work is based on the work of cmbtc, skindle, mobidedrm. and skindleAll I had the much easier job of converting them to a Calibre plugin.
-
-This plugin is meant to Kindle for PC azw ebooks that are protected
-with Amazon's Mobi based encryption. It is meant to function without having to install any dependencies... other than having both Calibre installed and Kindle for PC on the same machine, of course. 
-
-Installation:
-
-Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file  (K4PCDeDRM_X.XX_plugin.zip) and click the 'Add' button. you're done.
-
diff --git a/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt b/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt
new file mode 100644 (file)
index 0000000..75dfda5
--- /dev/null
@@ -0,0 +1,21 @@
+eReader PDB2PML - eReaderPDB2PML_vXX_plugin.zip
+
+All credit given to The Dark Reverser for the original standalone script. I had the much easier job of converting it to a Calibre plugin.
+
+This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ files. Calibre can then convert it to whatever format you desire. It is meant to function without having to install any  dependencies... other than having Calibre installed, of course. I've included the psyco libraries (compiled for each platform) for speed. If your system can use them, great! Otherwise, they won't be used and things will just work slower.
+
+Installation:
+Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file  (eReaderPDB2PML_vXX_plugin.zip) and click the 'Add' button. You're done.
+
+Configuration:
+Highlight the plugin (eReader PDB 2 PML under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your name and last 8 digits of the credit card number separated by a comma: Your Name,12341234
+
+If you've purchased books with more than one credit card, separate the info with a colon: Your Name,12341234:Other Name,23452345 (NOTE: Do NOT put quotes around your name like you do with the original script!!)
+
+Troubleshooting:
+If you find that it's not working for you (imported pdb's are not converted to pmlz format), you can save a lot of time and trouble by trying to add the pdb to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might
+as well get used to it. ;)
+
+Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdb". Don't type the quotes and obviously change the 'your_ebook.pdb' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
+
+** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
diff --git a/Calibre_Plugins/README_MobiDeDRM_plugin.txt b/Calibre_Plugins/README_MobiDeDRM_plugin.txt
deleted file mode 100644 (file)
index eaef29e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-MobiDeDRM - MobiDeDRM_X.XX_plugin.zip
-Requires Calibre version 0.6.44 or higher.
-
-This work is based on the current mobidedrm.py code.
-
-This plugin is meant to Mobipocket and Kindle ebooks that are protected
-with Amazon's Mobi based encryption. It is meant to function without having to install any dependencies... other than having both Calibre installed.  You must know the PID orf the device you are using or the book specific PID to use this plugin.
-
-Installation:
-
-Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file  (MobiDeDRM_X.XX_plugin.zip) and click the 'Add' button. 
-
-Then enter your PIDS in the plugin customization window separated by commas (with no spaces).
\ No newline at end of file
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin.zip b/Calibre_Plugins/eReaderPDB2PML_plugin.zip
new file mode 100644 (file)
index 0000000..c1d3563
Binary files /dev/null and b/Calibre_Plugins/eReaderPDB2PML_plugin.zip differ
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML-README.txt b/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML-README.txt
new file mode 100644 (file)
index 0000000..75dfda5
--- /dev/null
@@ -0,0 +1,21 @@
+eReader PDB2PML - eReaderPDB2PML_vXX_plugin.zip
+
+All credit given to The Dark Reverser for the original standalone script. I had the much easier job of converting it to a Calibre plugin.
+
+This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ files. Calibre can then convert it to whatever format you desire. It is meant to function without having to install any  dependencies... other than having Calibre installed, of course. I've included the psyco libraries (compiled for each platform) for speed. If your system can use them, great! Otherwise, they won't be used and things will just work slower.
+
+Installation:
+Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file  (eReaderPDB2PML_vXX_plugin.zip) and click the 'Add' button. You're done.
+
+Configuration:
+Highlight the plugin (eReader PDB 2 PML under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your name and last 8 digits of the credit card number separated by a comma: Your Name,12341234
+
+If you've purchased books with more than one credit card, separate the info with a colon: Your Name,12341234:Other Name,23452345 (NOTE: Do NOT put quotes around your name like you do with the original script!!)
+
+Troubleshooting:
+If you find that it's not working for you (imported pdb's are not converted to pmlz format), you can save a lot of time and trouble by trying to add the pdb to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might
+as well get used to it. ;)
+
+Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdb". Don't type the quotes and obviously change the 'your_ebook.pdb' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
+
+** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py b/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py
new file mode 100644 (file)
index 0000000..64b3019
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+# eReaderPDB2PML_v01_plugin.py
+# Released under the terms of the GNU General Public Licence, version 3 or
+# later.  <http://www.gnu.org/licenses/>
+#
+# All credit given to The Dark Reverser for the original standalone script.
+# I had the much easier job of converting it to Calibre a plugin.
+#
+# This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ files.
+# Calibre can then convert it to whatever format you desire.
+# It is meant to function without having to install any dependencies...
+# other than having Calibre installed, of course. I've included the psyco libraries
+# (compiled for each platform) for speed. If your system can use them, great!
+# Otherwise, they won't be used and things will just work slower.
+#
+# Installation:
+# Go to Calibre's Preferences page... click on the Plugins button. Use the file
+# dialog button to select the plugin's zip file (eReaderPDB2PML_vXX_plugin.zip) and
+# click the 'Add' button. You're done.
+#
+# Configuration:
+# Highlight the plugin (eReader PDB 2 PML) and click the
+# "Customize Plugin" button on Calibre's Preferences->Plugins page.
+# Enter your name and the last 8 digits of the credit card number separated by
+# a comma: Your Name,12341234
+#
+# If you've purchased books with more than one credit card, separate the info with
+# a colon: Your Name,12341234:Other Name,23452345
+# NOTE: Do NOT put quotes around your name like you do with the original script!!
+#
+# Revision history:
+#   0.1 - Initial release
+
+import sys, os
+
+from calibre.customize import FileTypePlugin
+
+class eRdrDeDRM(FileTypePlugin):
+    name                = 'eReader PDB 2 PML' # Name of the plugin
+    description         = 'Removes DRM from secure pdb files. \
+                            Credit given to The Dark Reverser for the original standalone script.'
+    supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
+    author              = 'DiapDealer' # The author of this plugin
+    version             = (0, 0, 1)   # The version number of this plugin
+    file_types          = set(['pdb']) # The file types that this plugin will be applied to
+    on_import           = True # Run this plugin during the import
+
+    def run(self, path_to_ebook):
+        from calibre.ptempfile import PersistentTemporaryDirectory
+        from calibre.constants import iswindows, isosx
+        pdir = 'windows' if iswindows else 'osx' if isosx else 'linux'
+        ppath = os.path.join(self.sys_insertion_path, pdir)
+        sys.path.insert(0, ppath)
+        #sys.path.append(ppath)
+        
+        global bookname, erdr2pml
+        import erdr2pml
+        
+        if 'psyco' in sys.modules:
+            print 'Using psyco acceleration for %s.' % pdir
+        else:
+            print 'NOT using psyco acceleration for %s. Conversion may be slow.' % pdir
+        
+        infile = path_to_ebook
+        bookname = os.path.splitext(os.path.basename(infile))[0]
+        outdir = PersistentTemporaryDirectory()
+        pmlzfile = self.temporary_file(bookname + '.pmlz')
+        
+        if self.site_customization:
+            keydata = self.site_customization
+            ar = keydata.split(':')
+            for i in ar:
+                try:
+                    name, cc = i.split(',')
+                except ValueError:
+                    sys.path.remove(ppath)
+                    print '   Error parsing user supplied data.'
+                    return path_to_ebook
+
+                try:
+                    print "Processing..."
+                    import time
+                    start_time = time.time()
+                    pmlfilepath = self.convertEreaderToPml(infile, name, cc, outdir)
+                    
+                    if pmlfilepath and pmlfilepath != 1:
+                        import zipfile
+                        import shutil
+                        print "   Creating PMLZ file"
+                        myZipFile = zipfile.ZipFile(pmlzfile.name,'w',zipfile.ZIP_STORED, False)
+                        list = os.listdir(outdir)
+                        for file in list:
+                            localname = file
+                            filePath = os.path.join(outdir,file)
+                            if os.path.isfile(filePath):
+                                myZipFile.write(filePath, localname)
+                            elif os.path.isdir(filePath):
+                                imageList = os.listdir(filePath)
+                                localimgdir = os.path.basename(filePath)
+                                for image in imageList:
+                                    localname = os.path.join(localimgdir,image)
+                                    imagePath = os.path.join(filePath,image)
+                                    if os.path.isfile(imagePath):
+                                        myZipFile.write(imagePath, localname)
+                        myZipFile.close()
+                        end_time = time.time()
+                        search_time = end_time - start_time
+                        print 'elapsed time: %.2f seconds' % (search_time, ) 
+                        print "done"
+                        return pmlzfile.name
+                    else:
+                        raise ValueError('Error Creating PML file.')
+                except ValueError, e:
+                        print "Error: %s" % e
+                        pass
+            raise Exception('Couldn\'t decrypt pdb file.')
+        else:
+            raise Exception('No name and CC# provided.')
+        
+    def convertEreaderToPml(self, infile, name, cc, outdir):
+
+        print "   Decoding File"
+        sect = erdr2pml.Sectionizer(infile, 'PNRdPPrs')
+        er = erdr2pml.EreaderProcessor(sect.loadSection, name, cc)
+
+        if er.getNumImages() > 0:
+            print "   Extracting images"
+            #imagedir = bookname + '_img/'
+            imagedir = 'images/'
+            imagedirpath = os.path.join(outdir,imagedir)
+            if not os.path.exists(imagedirpath):
+                os.makedirs(imagedirpath)
+            for i in xrange(er.getNumImages()):
+                name, contents = er.getImage(i)
+                file(os.path.join(imagedirpath, name), 'wb').write(contents)
+
+        print "   Extracting pml"
+        pml_string = er.getText()
+        pmlfilename = bookname + ".pml"
+        try:
+            file(os.path.join(outdir, pmlfilename),'wb').write(erdr2pml.cleanPML(pml_string))
+            return os.path.join(outdir, pmlfilename)
+        except:
+            return 1
+    def customization_help(self, gui=False):
+        return 'Enter Account Name & Last 8 digits of Credit Card number (separate with a comma)'
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/all-wcprops b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/all-wcprops
new file mode 100644 (file)
index 0000000..881cf30
--- /dev/null
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 41
+/svn/!svn/ver/70200/psyco/dist/py-support
+END
+core.py
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/70200/psyco/dist/py-support/core.py
+END
+support.py
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/49315/psyco/dist/py-support/support.py
+END
+classes.py
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/35003/psyco/dist/py-support/classes.py
+END
+__init__.py
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/35003/psyco/dist/py-support/__init__.py
+END
+logger.py
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/23284/psyco/dist/py-support/logger.py
+END
+kdictproxy.py
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/35003/psyco/dist/py-support/kdictproxy.py
+END
+profiler.py
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/70200/psyco/dist/py-support/profiler.py
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/dir-prop-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/dir-prop-base
new file mode 100644 (file)
index 0000000..a87157b
--- /dev/null
@@ -0,0 +1,7 @@
+K 10
+svn:ignore
+V 14
+*~
+*.pyc
+*.pyo
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/entries b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/entries
new file mode 100644 (file)
index 0000000..936b265
--- /dev/null
@@ -0,0 +1,266 @@
+10
+
+dir
+78269
+http://codespeak.net/svn/psyco/dist/py-support
+http://codespeak.net/svn
+
+
+
+2009-12-18T16:35:35.119276Z
+70200
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
+\f
+core.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+3b362177a839893c9e867880b3a7cef3
+2009-12-18T16:35:35.119276Z
+70200
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8144
+\f
+support.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+b0551e975d774f2f7f58a29ed4b6b90e
+2007-12-03T12:27:25.632574Z
+49315
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6043
+\f
+classes.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+5932ed955198d16ec17285dfb195d341
+2006-11-26T13:03:26.949973Z
+35003
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1440
+\f
+__init__.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+219582b5182dfa38a9119d059a71965f
+2006-11-26T13:03:26.949973Z
+35003
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1895
+\f
+logger.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+aa21f905df036af43082e1ea2a2561ee
+2006-02-13T15:02:51.744168Z
+23284
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2678
+\f
+kdictproxy.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+1c8611748dcee5b29848bf25be3ec473
+2006-11-26T13:03:26.949973Z
+35003
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4369
+\f
+profiler.py
+file
+
+
+
+
+2010-10-25T15:10:42.000000Z
+858162366cbc39cd9e249e35e6f510c4
+2009-12-18T16:35:35.119276Z
+70200
+arigo
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11238
+\f
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/__init__.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/__init__.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/classes.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/classes.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/core.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/core.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/kdictproxy.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/kdictproxy.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/logger.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/logger.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/profiler.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/profiler.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/support.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/prop-base/support.py.svn-base
new file mode 100644 (file)
index 0000000..7b57b30
--- /dev/null
@@ -0,0 +1,9 @@
+K 13
+svn:eol-style
+V 6
+native
+K 12
+svn:keywords
+V 23
+Author Date Id Revision
+END
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/__init__.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/__init__.py.svn-base
new file mode 100644 (file)
index 0000000..d25e197
--- /dev/null
@@ -0,0 +1,54 @@
+###########################################################################
+# 
+#  Psyco top-level file of the Psyco package.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco -- the Python Specializing Compiler.
+
+Typical usage: add the following lines to your application's main module,
+preferably after the other imports:
+
+try:
+    import psyco
+    psyco.full()
+except ImportError:
+    print 'Psyco not installed, the program will just run slower'
+"""
+###########################################################################
+
+
+#
+# This module is present to make 'psyco' a package and to
+# publish the main functions and variables.
+#
+# More documentation can be found in core.py.
+#
+
+
+# Try to import the dynamic-loading _psyco and report errors
+try:
+    import _psyco
+except ImportError, e:
+    extramsg = ''
+    import sys, imp
+    try:
+        file, filename, (suffix, mode, type) = imp.find_module('_psyco', __path__)
+    except ImportError:
+        ext = [suffix for suffix, mode, type in imp.get_suffixes()
+               if type == imp.C_EXTENSION]
+        if ext:
+            extramsg = (" (cannot locate the compiled extension '_psyco%s' "
+                        "in the package path '%s')" % (ext[0], '; '.join(__path__)))
+    else:
+        extramsg = (" (check that the compiled extension '%s' is for "
+                    "the correct Python version; this is Python %s)" %
+                    (filename, sys.version.split()[0]))
+    raise ImportError, str(e) + extramsg
+
+# Publish important data by importing them in the package
+from support import __version__, error, warning, _getrealframe, _getemulframe
+from support import version_info, __version__ as hexversion
+from core import full, profile, background, runonly, stop, cannotcompile
+from core import log, bind, unbind, proxy, unproxy, dumpcodebuf
+from _psyco import setfilter
+from _psyco import compact, compacttype
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/classes.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/classes.py.svn-base
new file mode 100644 (file)
index 0000000..0563f84
--- /dev/null
@@ -0,0 +1,42 @@
+###########################################################################
+# 
+#  Psyco class support module.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco class support module.
+
+'psyco.classes.psyobj' is an alternate Psyco-optimized root for classes.
+Any class inheriting from it or using the metaclass '__metaclass__' might
+get optimized specifically for Psyco. It is equivalent to call
+psyco.bind() on the class object after its creation.
+
+Importing everything from psyco.classes in a module will import the
+'__metaclass__' name, so all classes defined after a
+
+       from psyco.classes import *
+
+will automatically use the Psyco-optimized metaclass.
+"""
+###########################################################################
+
+__all__ = ['psyobj', 'psymetaclass', '__metaclass__']
+
+
+from _psyco import compacttype
+import core
+from types import FunctionType
+
+class psymetaclass(compacttype):
+    "Psyco-optimized meta-class. Turns all methods into Psyco proxies."
+
+    def __new__(cls, name, bases, dict):
+        bindlist = dict.get('__psyco__bind__')
+        if bindlist is None:
+            bindlist = [key for key, value in dict.items()
+                        if isinstance(value, FunctionType)]
+        for attr in bindlist:
+            dict[attr] = core.proxy(dict[attr])
+        return super(psymetaclass, cls).__new__(cls, name, bases, dict)
+
+psyobj = psymetaclass("psyobj", (), {})
+__metaclass__ = psymetaclass
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/core.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/core.py.svn-base
new file mode 100644 (file)
index 0000000..995c9ae
--- /dev/null
@@ -0,0 +1,231 @@
+###########################################################################
+# 
+#  Psyco main functions.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco main functions.
+
+Here are the routines that you can use from your applications.
+These are mostly interfaces to the C core, but they depend on
+the Python version.
+
+You can use these functions from the 'psyco' module instead of
+'psyco.core', e.g.
+
+    import psyco
+    psyco.log('/tmp/psyco.log')
+    psyco.profile()
+"""
+###########################################################################
+
+import _psyco
+import types
+from support import *
+
+newfunction = types.FunctionType
+newinstancemethod = types.MethodType
+
+
+# Default charge profiler values
+default_watermark     = 0.09     # between 0.0 (0%) and 1.0 (100%)
+default_halflife      = 0.5      # seconds
+default_pollfreq_profile    = 20       # Hz
+default_pollfreq_background = 100      # Hz -- a maximum for sleep's resolution
+default_parentframe   = 0.25     # should not be more than 0.5 (50%)
+
+
+def full(memory=None, time=None, memorymax=None, timemax=None):
+    """Compile as much as possible.
+
+Typical use is for small scripts performing intensive computations
+or string handling."""
+    import profiler
+    p = profiler.FullCompiler()
+    p.run(memory, time, memorymax, timemax)
+
+
+def profile(watermark   = default_watermark,
+            halflife    = default_halflife,
+            pollfreq    = default_pollfreq_profile,
+            parentframe = default_parentframe,
+            memory=None, time=None, memorymax=None, timemax=None):
+    """Turn on profiling.
+
+The 'watermark' parameter controls how easily running functions will
+be compiled. The smaller the value, the more functions are compiled."""
+    import profiler
+    p = profiler.ActivePassiveProfiler(watermark, halflife,
+                                       pollfreq, parentframe)
+    p.run(memory, time, memorymax, timemax)
+
+
+def background(watermark   = default_watermark,
+               halflife    = default_halflife,
+               pollfreq    = default_pollfreq_background,
+               parentframe = default_parentframe,
+               memory=None, time=None, memorymax=None, timemax=None):
+    """Turn on passive profiling.
+
+This is a very lightweight mode in which only intensively computing
+functions can be detected. The smaller the 'watermark', the more functions
+are compiled."""
+    import profiler
+    p = profiler.PassiveProfiler(watermark, halflife, pollfreq, parentframe)
+    p.run(memory, time, memorymax, timemax)
+
+
+def runonly(memory=None, time=None, memorymax=None, timemax=None):
+    """Nonprofiler.
+
+XXX check if this is useful and document."""
+    import profiler
+    p = profiler.RunOnly()
+    p.run(memory, time, memorymax, timemax)
+
+
+def stop():
+    """Turn off all automatic compilation.  bind() calls remain in effect."""
+    import profiler
+    profiler.go([])
+
+
+def log(logfile='', mode='w', top=10):
+    """Enable logging to the given file.
+
+If the file name is unspecified, a default name is built by appending
+a 'log-psyco' extension to the main script name.
+
+Mode is 'a' to append to a possibly existing file or 'w' to overwrite
+an existing file. Note that the log file may grow quickly in 'a' mode."""
+    import profiler, logger
+    if not logfile:
+        import os
+        logfile, dummy = os.path.splitext(sys.argv[0])
+        if os.path.basename(logfile):
+            logfile += '.'
+        logfile += 'log-psyco'
+    if hasattr(_psyco, 'VERBOSE_LEVEL'):
+        print >> sys.stderr, 'psyco: logging to', logfile
+    # logger.current should be a real file object; subtle problems
+    # will show up if its write() and flush() methods are written
+    # in Python, as Psyco will invoke them while compiling.
+    logger.current = open(logfile, mode)
+    logger.print_charges = top
+    profiler.logger = logger
+    logger.writedate('Logging started')
+    cannotcompile(logger.psycowrite)
+    _psyco.statwrite(logger=logger.psycowrite)
+
+
+def bind(x, rec=None):
+    """Enable compilation of the given function, method, or class object.
+
+If C is a class (or anything with a '__dict__' attribute), bind(C) will
+rebind all functions and methods found in C.__dict__ (which means, for
+classes, all methods defined in the class but not in its parents).
+
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        if rec is None:
+            x.func_code = _psyco.proxycode(x)
+        else:
+            x.func_code = _psyco.proxycode(x, rec)
+        return
+    if hasattr(x, '__dict__'):
+        funcs = [o for o in x.__dict__.values()
+                 if isinstance(o, types.MethodType)
+                 or isinstance(o, types.FunctionType)]
+        if not funcs:
+            raise error, ("nothing bindable found in %s object" %
+                          type(x).__name__)
+        for o in funcs:
+            bind(o, rec)
+        return
+    raise TypeError, "cannot bind %s objects" % type(x).__name__
+
+
+def unbind(x):
+    """Reverse of bind()."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        try:
+            f = _psyco.unproxycode(x.func_code)
+        except error:
+            pass
+        else:
+            x.func_code = f.func_code
+        return
+    if hasattr(x, '__dict__'):
+        for o in x.__dict__.values():
+            if (isinstance(o, types.MethodType)
+             or isinstance(o, types.FunctionType)):
+                unbind(o)
+        return
+    raise TypeError, "cannot unbind %s objects" % type(x).__name__
+
+
+def proxy(x, rec=None):
+    """Return a Psyco-enabled copy of the function.
+
+The original function is still available for non-compiled calls.
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection."""
+    if isinstance(x, types.FunctionType):
+        if rec is None:
+            code = _psyco.proxycode(x)
+        else:
+            code = _psyco.proxycode(x, rec)
+        return newfunction(code, x.func_globals, x.func_name)
+    if isinstance(x, types.MethodType):
+        p = proxy(x.im_func, rec)
+        return newinstancemethod(p, x.im_self, x.im_class)
+    raise TypeError, "cannot proxy %s objects" % type(x).__name__
+
+
+def unproxy(proxy):
+    """Return a new copy of the original function of method behind a proxy.
+The result behaves like the original function in that calling it
+does not trigger compilation nor execution of any compiled code."""
+    if isinstance(proxy, types.FunctionType):
+        return _psyco.unproxycode(proxy.func_code)
+    if isinstance(proxy, types.MethodType):
+        f = unproxy(proxy.im_func)
+        return newinstancemethod(f, proxy.im_self, proxy.im_class)
+    raise TypeError, "%s objects cannot be proxies" % type(proxy).__name__
+
+
+def cannotcompile(x):
+    """Instruct Psyco never to compile the given function, method
+or code object."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        x = x.func_code
+    if isinstance(x, types.CodeType):
+        _psyco.cannotcompile(x)
+    else:
+        raise TypeError, "unexpected %s object" % type(x).__name__
+
+
+def dumpcodebuf():
+    """Write in file psyco.dump a copy of the emitted machine code,
+provided Psyco was compiled with a non-zero CODE_DUMP.
+See py-utils/httpxam.py to examine psyco.dump."""
+    if hasattr(_psyco, 'dumpcodebuf'):
+        _psyco.dumpcodebuf()
+
+
+###########################################################################
+# Psyco variables
+#   error         * the error raised by Psyco
+#   warning       * the warning raised by Psyco
+#   __in_psyco__  * a new built-in variable which is always zero, but which
+#                     Psyco special-cases by returning 1 instead. So
+#                     __in_psyco__ can be used in a function to know if
+#                     that function is being executed by Psyco or not.
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/kdictproxy.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/kdictproxy.py.svn-base
new file mode 100644 (file)
index 0000000..c764e5e
--- /dev/null
@@ -0,0 +1,133 @@
+###########################################################################
+#
+#  Support code for the 'psyco.compact' type.
+
+from __future__ import generators
+
+try:
+    from UserDict import DictMixin
+except ImportError:
+
+    # backported from Python 2.3 to Python 2.2
+    class DictMixin:
+        # Mixin defining all dictionary methods for classes that already have
+        # a minimum dictionary interface including getitem, setitem, delitem,
+        # and keys. Without knowledge of the subclass constructor, the mixin
+        # does not define __init__() or copy().  In addition to the four base
+        # methods, progressively more efficiency comes with defining
+        # __contains__(), __iter__(), and iteritems().
+
+        # second level definitions support higher levels
+        def __iter__(self):
+            for k in self.keys():
+                yield k
+        def has_key(self, key):
+            try:
+                value = self[key]
+            except KeyError:
+                return False
+            return True
+        def __contains__(self, key):
+            return self.has_key(key)
+
+        # third level takes advantage of second level definitions
+        def iteritems(self):
+            for k in self:
+                yield (k, self[k])
+        def iterkeys(self):
+            return self.__iter__()
+
+        # fourth level uses definitions from lower levels
+        def itervalues(self):
+            for _, v in self.iteritems():
+                yield v
+        def values(self):
+            return [v for _, v in self.iteritems()]
+        def items(self):
+            return list(self.iteritems())
+        def clear(self):
+            for key in self.keys():
+                del self[key]
+        def setdefault(self, key, default):
+            try:
+                return self[key]
+            except KeyError:
+                self[key] = default
+            return default
+        def pop(self, key, *args):
+            if len(args) > 1:
+                raise TypeError, "pop expected at most 2 arguments, got "\
+                                  + repr(1 + len(args))
+            try:
+                value = self[key]
+            except KeyError:
+                if args:
+                    return args[0]
+                raise
+            del self[key]
+            return value
+        def popitem(self):
+            try:
+                k, v = self.iteritems().next()
+            except StopIteration:
+                raise KeyError, 'container is empty'
+            del self[k]
+            return (k, v)
+        def update(self, other):
+            # Make progressively weaker assumptions about "other"
+            if hasattr(other, 'iteritems'):  # iteritems saves memory and lookups
+                for k, v in other.iteritems():
+                    self[k] = v
+            elif hasattr(other, '__iter__'): # iter saves memory
+                for k in other:
+                    self[k] = other[k]
+            else:
+                for k in other.keys():
+                    self[k] = other[k]
+        def get(self, key, default=None):
+            try:
+                return self[key]
+            except KeyError:
+                return default
+        def __repr__(self):
+            return repr(dict(self.iteritems()))
+        def __cmp__(self, other):
+            if other is None:
+                return 1
+            if isinstance(other, DictMixin):
+                other = dict(other.iteritems())
+            return cmp(dict(self.iteritems()), other)
+        def __len__(self):
+            return len(self.keys())
+
+###########################################################################
+
+from _psyco import compact
+
+
+class compactdictproxy(DictMixin):
+
+    def __init__(self, ko):
+        self._ko = ko    # compact object of which 'self' is the dict
+
+    def __getitem__(self, key):
+        return compact.__getslot__(self._ko, key)
+
+    def __setitem__(self, key, value):
+        compact.__setslot__(self._ko, key, value)
+
+    def __delitem__(self, key):
+        compact.__delslot__(self._ko, key)
+
+    def keys(self):
+        return compact.__members__.__get__(self._ko)
+
+    def clear(self):
+        keys = self.keys()
+        keys.reverse()
+        for key in keys:
+            del self[key]
+
+    def __repr__(self):
+        keys = ', '.join(self.keys())
+        return '<compactdictproxy object {%s}>' % (keys,)
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/logger.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/logger.py.svn-base
new file mode 100644 (file)
index 0000000..a3c5219
--- /dev/null
@@ -0,0 +1,96 @@
+###########################################################################
+# 
+#  Psyco logger.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco logger.
+
+See log() in core.py.
+"""
+###########################################################################
+
+
+import _psyco
+from time import time, localtime, strftime
+
+
+current = None
+print_charges = 10
+dump_delay = 0.2
+dump_last = 0.0
+
+def write(s, level):
+    t = time()
+    f = t-int(t)
+    try:
+        current.write("%s.%02d  %-*s%s\n" % (
+            strftime("%X", localtime(int(t))),
+            int(f*100.0), 63-level, s,
+            "%"*level))
+        current.flush()
+    except (OSError, IOError):
+        pass
+
+def psycowrite(s):
+    t = time()
+    f = t-int(t)
+    try:
+        current.write("%s.%02d  %-*s%s\n" % (
+            strftime("%X", localtime(int(t))),
+            int(f*100.0), 60, s.strip(),
+            "% %"))
+        current.flush()
+    except (OSError, IOError):
+        pass
+
+##def writelines(lines, level=0):
+##    if lines:
+##        t = time()
+##        f = t-int(t)
+##        timedesc = strftime("%x %X", localtime(int(t)))
+##        print >> current, "%s.%03d  %-*s %s" % (
+##            timedesc, int(f*1000),
+##            50-level, lines[0],
+##            "+"*level)
+##        timedesc = " " * (len(timedesc)+5)
+##        for line in lines[1:]:
+##            print >> current, timedesc, line
+
+def writememory():
+    write("memory usage: %d+ kb" % _psyco.memory(), 1)
+
+def dumpcharges():
+    global dump_last
+    if print_charges:
+        t = time()
+        if not (dump_last <= t < dump_last+dump_delay):
+            if t <= dump_last+1.5*dump_delay:
+                dump_last += dump_delay
+            else:
+                dump_last = t
+            #write("%s: charges:" % who, 0)
+            lst = _psyco.stattop(print_charges)
+            if lst:
+                f = t-int(t)
+                lines = ["%s.%02d   ______\n" % (
+                    strftime("%X", localtime(int(t))),
+                    int(f*100.0))]
+                i = 1
+                for co, charge in lst:
+                    detail = co.co_filename
+                    if len(detail) > 19:
+                        detail = '...' + detail[-17:]
+                    lines.append("        #%-3d |%4.1f %%|  %-26s%20s:%d\n" %
+                                 (i, charge*100.0, co.co_name, detail,
+                                  co.co_firstlineno))
+                    i += 1
+                current.writelines(lines)
+                current.flush()
+
+def writefinalstats():
+    dumpcharges()
+    writememory()
+    writedate("program exit")
+
+def writedate(msg):
+    write('%s, %s' % (msg, strftime("%x")), 20)
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/profiler.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/profiler.py.svn-base
new file mode 100644 (file)
index 0000000..f1d06f1
--- /dev/null
@@ -0,0 +1,379 @@
+###########################################################################
+# 
+#  Psyco profiler (Python part).
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco profiler (Python part).
+
+The implementation of the non-time-critical parts of the profiler.
+See profile() and full() in core.py for the easy interface.
+"""
+###########################################################################
+
+import _psyco
+from support import *
+import math, time, types, atexit
+now = time.time
+try:
+    import thread
+except ImportError:
+    import dummy_thread as thread
+
+
+# current profiler instance
+current = None
+
+# enabled profilers, in order of priority
+profilers = []
+
+# logger module (when enabled by core.log())
+logger = None
+
+# a lock for a thread-safe go()
+go_lock = thread.allocate_lock()
+
+def go(stop=0):
+    # run the highest-priority profiler in 'profilers'
+    global current
+    go_lock.acquire()
+    try:
+        prev = current
+        if stop:
+            del profilers[:]
+        if prev:
+            if profilers and profilers[0] is prev:
+                return    # best profiler already running
+            prev.stop()
+            current = None
+        for p in profilers[:]:
+            if p.start():
+                current = p
+                if logger: # and p is not prev:
+                    logger.write("%s: starting" % p.__class__.__name__, 5)
+                return
+    finally:
+        go_lock.release()
+    # no profiler is running now
+    if stop:
+        if logger:
+            logger.writefinalstats()
+    else:
+        tag2bind()
+
+atexit.register(go, 1)
+
+
+def buildfncache(globals, cache):
+    if hasattr(types.IntType, '__dict__'):
+        clstypes = (types.ClassType, types.TypeType)
+    else:
+        clstypes = types.ClassType
+    for x in globals.values():
+        if isinstance(x, types.MethodType):
+            x = x.im_func
+        if isinstance(x, types.FunctionType):
+            cache[x.func_code] = x, ''
+        elif isinstance(x, clstypes):
+            for y in x.__dict__.values():
+                if isinstance(y, types.MethodType):
+                    y = y.im_func
+                if isinstance(y, types.FunctionType):
+                    cache[y.func_code] = y, x.__name__
+
+# code-to-function mapping (cache)
+function_cache = {}
+
+def trytobind(co, globals, log=1):
+    try:
+        f, clsname = function_cache[co]
+    except KeyError:
+        buildfncache(globals, function_cache)
+        try:
+            f, clsname = function_cache[co]
+        except KeyError:
+            if logger:
+                logger.write('warning: cannot find function %s in %s' %
+                             (co.co_name, globals.get('__name__', '?')), 3)
+            return  # give up
+    if logger and log:
+        modulename = globals.get('__name__', '?')
+        if clsname:
+            modulename += '.' + clsname
+        logger.write('bind function: %s.%s' % (modulename, co.co_name), 1)
+    f.func_code = _psyco.proxycode(f)
+
+
+# the list of code objects that have been tagged
+tagged_codes = []
+
+def tag(co, globals):
+    if logger:
+        try:
+            f, clsname = function_cache[co]
+        except KeyError:
+            buildfncache(globals, function_cache)
+            try:
+                f, clsname = function_cache[co]
+            except KeyError:
+                clsname = ''  # give up
+        modulename = globals.get('__name__', '?')
+        if clsname:
+            modulename += '.' + clsname
+        logger.write('tag function: %s.%s' % (modulename, co.co_name), 1)
+    tagged_codes.append((co, globals))
+    _psyco.turbo_frame(co)
+    _psyco.turbo_code(co)
+
+def tag2bind():
+    if tagged_codes:
+        if logger:
+            logger.write('profiling stopped, binding %d functions' %
+                         len(tagged_codes), 2)
+        for co, globals in tagged_codes:
+            trytobind(co, globals, 0)
+        function_cache.clear()
+        del tagged_codes[:]
+
+
+class Profiler:
+    MemoryTimerResolution = 0.103
+
+    def run(self, memory, time, memorymax, timemax):
+        self.memory = memory
+        self.memorymax = memorymax
+        self.time = time
+        if timemax is None:
+            self.endtime = None
+        else:
+            self.endtime = now() + timemax
+        self.alarms = []
+        profilers.append(self)
+        go()
+    
+    def start(self):
+        curmem = _psyco.memory()
+        memlimits = []
+        if self.memorymax is not None:
+            if curmem >= self.memorymax:
+                if logger:
+                    logger.writememory()
+                return self.limitreached('memorymax')
+            memlimits.append(self.memorymax)
+        if self.memory is not None:
+            if self.memory <= 0:
+                if logger:
+                    logger.writememory()
+                return self.limitreached('memory')
+            memlimits.append(curmem + self.memory)
+            self.memory_at_start = curmem
+
+        curtime = now()
+        timelimits = []
+        if self.endtime is not None:
+            if curtime >= self.endtime:
+                return self.limitreached('timemax')
+            timelimits.append(self.endtime - curtime)
+        if self.time is not None:
+            if self.time <= 0.0:
+                return self.limitreached('time')
+            timelimits.append(self.time)
+            self.time_at_start = curtime
+        
+        try:
+            self.do_start()
+        except error, e:
+            if logger:
+                logger.write('%s: disabled by psyco.error:' % (
+                    self.__class__.__name__), 4)
+                logger.write('    %s' % str(e), 3)
+            return 0
+        
+        if memlimits:
+            self.memlimits_args = (time.sleep, (self.MemoryTimerResolution,),
+                                   self.check_memory, (min(memlimits),))
+            self.alarms.append(_psyco.alarm(*self.memlimits_args))
+        if timelimits:
+            self.alarms.append(_psyco.alarm(time.sleep, (min(timelimits),),
+                                            self.time_out))
+        return 1
+    
+    def stop(self):
+        for alarm in self.alarms:
+            alarm.stop(0)
+        for alarm in self.alarms:
+            alarm.stop(1)   # wait for parallel threads to stop
+        del self.alarms[:]
+        if self.time is not None:
+            self.time -= now() - self.time_at_start
+        if self.memory is not None:
+            self.memory -= _psyco.memory() - self.memory_at_start
+
+        try:
+            self.do_stop()
+        except error:
+            return 0
+        return 1
+
+    def check_memory(self, limit):
+        if _psyco.memory() < limit:
+            return self.memlimits_args
+        go()
+
+    def time_out(self):
+        self.time = 0.0
+        go()
+
+    def limitreached(self, limitname):
+        try:
+            profilers.remove(self)
+        except ValueError:
+            pass
+        if logger:
+            logger.write('%s: disabled (%s limit reached)' % (
+                self.__class__.__name__, limitname), 4)
+        return 0
+
+
+class FullCompiler(Profiler):
+
+    def do_start(self):
+        _psyco.profiling('f')
+
+    def do_stop(self):
+        _psyco.profiling('.')
+
+
+class RunOnly(Profiler):
+
+    def do_start(self):
+        _psyco.profiling('n')
+
+    def do_stop(self):
+        _psyco.profiling('.')
+
+
+class ChargeProfiler(Profiler):
+
+    def __init__(self, watermark, parentframe):
+        self.watermark = watermark
+        self.parent2 = parentframe * 2.0
+        self.lock = thread.allocate_lock()
+
+    def init_charges(self):
+        _psyco.statwrite(watermark = self.watermark,
+                         parent2   = self.parent2)
+
+    def do_stop(self):
+        _psyco.profiling('.')
+        _psyco.statwrite(callback = None)
+
+
+class ActiveProfiler(ChargeProfiler):
+
+    def active_start(self):
+        _psyco.profiling('p')
+
+    def do_start(self):
+        self.init_charges()
+        self.active_start()
+        _psyco.statwrite(callback = self.charge_callback)
+
+    def charge_callback(self, frame, charge):
+        tag(frame.f_code, frame.f_globals)
+
+
+class PassiveProfiler(ChargeProfiler):
+
+    initial_charge_unit   = _psyco.statread('unit')
+    reset_stats_after     = 120      # half-lives (maximum 200!)
+    reset_limit           = initial_charge_unit * (2.0 ** reset_stats_after)
+
+    def __init__(self, watermark, halflife, pollfreq, parentframe):
+        ChargeProfiler.__init__(self, watermark, parentframe)
+        self.pollfreq = pollfreq
+        # self.progress is slightly more than 1.0, and computed so that
+        # do_profile() will double the change_unit every 'halflife' seconds.
+        self.progress = 2.0 ** (1.0 / (halflife * pollfreq))
+
+    def reset(self):
+        _psyco.statwrite(unit = self.initial_charge_unit, callback = None)
+        _psyco.statreset()
+        if logger:
+            logger.write("%s: resetting stats" % self.__class__.__name__, 1)
+
+    def passive_start(self):
+        self.passivealarm_args = (time.sleep, (1.0 / self.pollfreq,),
+                                  self.do_profile)
+        self.alarms.append(_psyco.alarm(*self.passivealarm_args))
+
+    def do_start(self):
+        tag2bind()
+        self.init_charges()
+        self.passive_start()
+
+    def do_profile(self):
+        _psyco.statcollect()
+        if logger:
+            logger.dumpcharges()
+        nunit = _psyco.statread('unit') * self.progress
+        if nunit > self.reset_limit:
+            self.reset()
+        else:
+            _psyco.statwrite(unit = nunit, callback = self.charge_callback)
+        return self.passivealarm_args
+
+    def charge_callback(self, frame, charge):
+        trytobind(frame.f_code, frame.f_globals)
+
+
+class ActivePassiveProfiler(PassiveProfiler, ActiveProfiler):
+
+    def do_start(self):
+        self.init_charges()
+        self.active_start()
+        self.passive_start()
+
+    def charge_callback(self, frame, charge):
+        tag(frame.f_code, frame.f_globals)
+
+
+
+#
+# we register our own version of sys.settrace(), sys.setprofile()
+# and thread.start_new_thread().
+#
+
+def psyco_settrace(*args, **kw):
+    "This is the Psyco-aware version of sys.settrace()."
+    result = original_settrace(*args, **kw)
+    go()
+    return result
+
+def psyco_setprofile(*args, **kw):
+    "This is the Psyco-aware version of sys.setprofile()."
+    result = original_setprofile(*args, **kw)
+    go()
+    return result
+
+def psyco_thread_stub(callable, args, kw):
+    _psyco.statcollect()
+    if kw is None:
+        return callable(*args)
+    else:
+        return callable(*args, **kw)
+
+def psyco_start_new_thread(callable, args, kw=None):
+    "This is the Psyco-aware version of thread.start_new_thread()."
+    return original_start_new_thread(psyco_thread_stub, (callable, args, kw))
+
+original_settrace         = sys.settrace
+original_setprofile       = sys.setprofile
+original_start_new_thread = thread.start_new_thread
+sys.settrace            = psyco_settrace
+sys.setprofile          = psyco_setprofile
+thread.start_new_thread = psyco_start_new_thread
+# hack to patch threading._start_new_thread if the module is
+# already loaded
+if ('threading' in sys.modules and
+    hasattr(sys.modules['threading'], '_start_new_thread')):
+    sys.modules['threading']._start_new_thread = psyco_start_new_thread
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/support.py.svn-base b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/.svn/text-base/support.py.svn-base
new file mode 100644 (file)
index 0000000..387321a
--- /dev/null
@@ -0,0 +1,191 @@
+###########################################################################
+# 
+#  Psyco general support module.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco general support module.
+
+For internal use.
+"""
+###########################################################################
+
+import sys, _psyco, __builtin__
+
+error = _psyco.error
+class warning(Warning):
+    pass
+
+_psyco.NoLocalsWarning = warning
+
+def warn(msg):
+    from warnings import warn
+    warn(msg, warning, stacklevel=2)
+
+#
+# Version checks
+#
+__version__ = 0x010600f0
+if _psyco.PSYVER != __version__:
+    raise error, "version mismatch between Psyco parts, reinstall it"
+
+version_info = (__version__ >> 24,
+                (__version__ >> 16) & 0xff,
+                (__version__ >> 8) & 0xff,
+                {0xa0: 'alpha',
+                 0xb0: 'beta',
+                 0xc0: 'candidate',
+                 0xf0: 'final'}[__version__ & 0xf0],
+                __version__ & 0xf)
+
+
+VERSION_LIMITS = [0x02020200,   # 2.2.2
+                  0x02030000,   # 2.3
+                  0x02040000]   # 2.4
+
+if ([v for v in VERSION_LIMITS if v <= sys.hexversion] !=
+    [v for v in VERSION_LIMITS if v <= _psyco.PYVER  ]):
+    if sys.hexversion < VERSION_LIMITS[0]:
+        warn("Psyco requires Python version 2.2.2 or later")
+    else:
+        warn("Psyco version does not match Python version. "
+             "Psyco must be updated or recompiled")
+
+
+if hasattr(_psyco, 'ALL_CHECKS') and hasattr(_psyco, 'VERBOSE_LEVEL'):
+    print >> sys.stderr, ('psyco: running in debugging mode on %s' %
+                          _psyco.PROCESSOR)
+
+
+###########################################################################
+# sys._getframe() gives strange results on a mixed Psyco- and Python-style
+# stack frame. Psyco provides a replacement that partially emulates Python
+# frames from Psyco frames. The new sys._getframe() may return objects of
+# a custom "Psyco frame" type, which is a subtype of the normal frame type.
+#
+# The same problems require some other built-in functions to be replaced
+# as well. Note that the local variables are not available in any
+# dictionary with Psyco.
+
+
+class Frame:
+    pass
+
+
+class PythonFrame(Frame):
+
+    def __init__(self, frame):
+        self.__dict__.update({
+            '_frame': frame,
+            })
+
+    def __getattr__(self, attr):
+        if attr == 'f_back':
+            try:
+                result = embedframe(_psyco.getframe(self._frame))
+            except ValueError:
+                result = None
+            except error:
+                warn("f_back is skipping dead Psyco frames")
+                result = self._frame.f_back
+            self.__dict__['f_back'] = result
+            return result
+        else:
+            return getattr(self._frame, attr)
+
+    def __setattr__(self, attr, value):
+        setattr(self._frame, attr, value)
+
+    def __delattr__(self, attr):
+        delattr(self._frame, attr)
+
+
+class PsycoFrame(Frame):
+
+    def __init__(self, tag):
+        self.__dict__.update({
+            '_tag'     : tag,
+            'f_code'   : tag[0],
+            'f_globals': tag[1],
+            })
+
+    def __getattr__(self, attr):
+        if attr == 'f_back':
+            try:
+                result = embedframe(_psyco.getframe(self._tag))
+            except ValueError:
+                result = None
+        elif attr == 'f_lineno':
+            result = self.f_code.co_firstlineno  # better than nothing
+        elif attr == 'f_builtins':
+            result = self.f_globals['__builtins__']
+        elif attr == 'f_restricted':
+            result = self.f_builtins is not __builtins__
+        elif attr == 'f_locals':
+            raise AttributeError, ("local variables of functions run by Psyco "
+                                   "cannot be accessed in any way, sorry")
+        else:
+            raise AttributeError, ("emulated Psyco frames have "
+                                   "no '%s' attribute" % attr)
+        self.__dict__[attr] = result
+        return result
+
+    def __setattr__(self, attr, value):
+        raise AttributeError, "Psyco frame objects are read-only"
+
+    def __delattr__(self, attr):
+        if attr == 'f_trace':
+            # for bdb which relies on CPython frames exhibiting a slightly
+            # buggy behavior: you can 'del f.f_trace' as often as you like
+            # even without having set it previously.
+            return
+        raise AttributeError, "Psyco frame objects are read-only"
+
+
+def embedframe(result):
+    if type(result) is type(()):
+        return PsycoFrame(result)
+    else:
+        return PythonFrame(result)
+
+def _getframe(depth=0):
+    """Return a frame object from the call stack. This is a replacement for
+sys._getframe() which is aware of Psyco frames.
+
+The returned objects are instances of either PythonFrame or PsycoFrame
+instead of being real Python-level frame object, so that they can emulate
+the common attributes of frame objects.
+
+The original sys._getframe() ignoring Psyco frames altogether is stored in
+psyco._getrealframe(). See also psyco._getemulframe()."""
+    # 'depth+1' to account for this _getframe() Python function
+    return embedframe(_psyco.getframe(depth+1))
+
+def _getemulframe(depth=0):
+    """As _getframe(), but the returned objects are real Python frame objects
+emulating Psyco frames. Some of their attributes can be wrong or missing,
+however."""
+    # 'depth+1' to account for this _getemulframe() Python function
+    return _psyco.getframe(depth+1, 1)
+
+def patch(name, module=__builtin__):
+    f = getattr(_psyco, name)
+    org = getattr(module, name)
+    if org is not f:
+        setattr(module, name, f)
+        setattr(_psyco, 'original_' + name, org)
+
+_getrealframe = sys._getframe
+sys._getframe = _getframe
+patch('globals')
+patch('eval')
+patch('execfile')
+patch('locals')
+patch('vars')
+patch('dir')
+patch('input')
+_psyco.original_raw_input = raw_input
+__builtin__.__in_psyco__ = 0==1   # False
+
+if hasattr(_psyco, 'compact'):
+    import kdictproxy
+    _psyco.compactdictproxy = kdictproxy.compactdictproxy
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/__init__.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/__init__.py
new file mode 100644 (file)
index 0000000..d25e197
--- /dev/null
@@ -0,0 +1,54 @@
+###########################################################################
+# 
+#  Psyco top-level file of the Psyco package.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco -- the Python Specializing Compiler.
+
+Typical usage: add the following lines to your application's main module,
+preferably after the other imports:
+
+try:
+    import psyco
+    psyco.full()
+except ImportError:
+    print 'Psyco not installed, the program will just run slower'
+"""
+###########################################################################
+
+
+#
+# This module is present to make 'psyco' a package and to
+# publish the main functions and variables.
+#
+# More documentation can be found in core.py.
+#
+
+
+# Try to import the dynamic-loading _psyco and report errors
+try:
+    import _psyco
+except ImportError, e:
+    extramsg = ''
+    import sys, imp
+    try:
+        file, filename, (suffix, mode, type) = imp.find_module('_psyco', __path__)
+    except ImportError:
+        ext = [suffix for suffix, mode, type in imp.get_suffixes()
+               if type == imp.C_EXTENSION]
+        if ext:
+            extramsg = (" (cannot locate the compiled extension '_psyco%s' "
+                        "in the package path '%s')" % (ext[0], '; '.join(__path__)))
+    else:
+        extramsg = (" (check that the compiled extension '%s' is for "
+                    "the correct Python version; this is Python %s)" %
+                    (filename, sys.version.split()[0]))
+    raise ImportError, str(e) + extramsg
+
+# Publish important data by importing them in the package
+from support import __version__, error, warning, _getrealframe, _getemulframe
+from support import version_info, __version__ as hexversion
+from core import full, profile, background, runonly, stop, cannotcompile
+from core import log, bind, unbind, proxy, unproxy, dumpcodebuf
+from _psyco import setfilter
+from _psyco import compact, compacttype
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/classes.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/classes.py
new file mode 100644 (file)
index 0000000..0563f84
--- /dev/null
@@ -0,0 +1,42 @@
+###########################################################################
+# 
+#  Psyco class support module.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco class support module.
+
+'psyco.classes.psyobj' is an alternate Psyco-optimized root for classes.
+Any class inheriting from it or using the metaclass '__metaclass__' might
+get optimized specifically for Psyco. It is equivalent to call
+psyco.bind() on the class object after its creation.
+
+Importing everything from psyco.classes in a module will import the
+'__metaclass__' name, so all classes defined after a
+
+       from psyco.classes import *
+
+will automatically use the Psyco-optimized metaclass.
+"""
+###########################################################################
+
+__all__ = ['psyobj', 'psymetaclass', '__metaclass__']
+
+
+from _psyco import compacttype
+import core
+from types import FunctionType
+
+class psymetaclass(compacttype):
+    "Psyco-optimized meta-class. Turns all methods into Psyco proxies."
+
+    def __new__(cls, name, bases, dict):
+        bindlist = dict.get('__psyco__bind__')
+        if bindlist is None:
+            bindlist = [key for key, value in dict.items()
+                        if isinstance(value, FunctionType)]
+        for attr in bindlist:
+            dict[attr] = core.proxy(dict[attr])
+        return super(psymetaclass, cls).__new__(cls, name, bases, dict)
+
+psyobj = psymetaclass("psyobj", (), {})
+__metaclass__ = psymetaclass
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/core.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/core.py
new file mode 100644 (file)
index 0000000..995c9ae
--- /dev/null
@@ -0,0 +1,231 @@
+###########################################################################
+# 
+#  Psyco main functions.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco main functions.
+
+Here are the routines that you can use from your applications.
+These are mostly interfaces to the C core, but they depend on
+the Python version.
+
+You can use these functions from the 'psyco' module instead of
+'psyco.core', e.g.
+
+    import psyco
+    psyco.log('/tmp/psyco.log')
+    psyco.profile()
+"""
+###########################################################################
+
+import _psyco
+import types
+from support import *
+
+newfunction = types.FunctionType
+newinstancemethod = types.MethodType
+
+
+# Default charge profiler values
+default_watermark     = 0.09     # between 0.0 (0%) and 1.0 (100%)
+default_halflife      = 0.5      # seconds
+default_pollfreq_profile    = 20       # Hz
+default_pollfreq_background = 100      # Hz -- a maximum for sleep's resolution
+default_parentframe   = 0.25     # should not be more than 0.5 (50%)
+
+
+def full(memory=None, time=None, memorymax=None, timemax=None):
+    """Compile as much as possible.
+
+Typical use is for small scripts performing intensive computations
+or string handling."""
+    import profiler
+    p = profiler.FullCompiler()
+    p.run(memory, time, memorymax, timemax)
+
+
+def profile(watermark   = default_watermark,
+            halflife    = default_halflife,
+            pollfreq    = default_pollfreq_profile,
+            parentframe = default_parentframe,
+            memory=None, time=None, memorymax=None, timemax=None):
+    """Turn on profiling.
+
+The 'watermark' parameter controls how easily running functions will
+be compiled. The smaller the value, the more functions are compiled."""
+    import profiler
+    p = profiler.ActivePassiveProfiler(watermark, halflife,
+                                       pollfreq, parentframe)
+    p.run(memory, time, memorymax, timemax)
+
+
+def background(watermark   = default_watermark,
+               halflife    = default_halflife,
+               pollfreq    = default_pollfreq_background,
+               parentframe = default_parentframe,
+               memory=None, time=None, memorymax=None, timemax=None):
+    """Turn on passive profiling.
+
+This is a very lightweight mode in which only intensively computing
+functions can be detected. The smaller the 'watermark', the more functions
+are compiled."""
+    import profiler
+    p = profiler.PassiveProfiler(watermark, halflife, pollfreq, parentframe)
+    p.run(memory, time, memorymax, timemax)
+
+
+def runonly(memory=None, time=None, memorymax=None, timemax=None):
+    """Nonprofiler.
+
+XXX check if this is useful and document."""
+    import profiler
+    p = profiler.RunOnly()
+    p.run(memory, time, memorymax, timemax)
+
+
+def stop():
+    """Turn off all automatic compilation.  bind() calls remain in effect."""
+    import profiler
+    profiler.go([])
+
+
+def log(logfile='', mode='w', top=10):
+    """Enable logging to the given file.
+
+If the file name is unspecified, a default name is built by appending
+a 'log-psyco' extension to the main script name.
+
+Mode is 'a' to append to a possibly existing file or 'w' to overwrite
+an existing file. Note that the log file may grow quickly in 'a' mode."""
+    import profiler, logger
+    if not logfile:
+        import os
+        logfile, dummy = os.path.splitext(sys.argv[0])
+        if os.path.basename(logfile):
+            logfile += '.'
+        logfile += 'log-psyco'
+    if hasattr(_psyco, 'VERBOSE_LEVEL'):
+        print >> sys.stderr, 'psyco: logging to', logfile
+    # logger.current should be a real file object; subtle problems
+    # will show up if its write() and flush() methods are written
+    # in Python, as Psyco will invoke them while compiling.
+    logger.current = open(logfile, mode)
+    logger.print_charges = top
+    profiler.logger = logger
+    logger.writedate('Logging started')
+    cannotcompile(logger.psycowrite)
+    _psyco.statwrite(logger=logger.psycowrite)
+
+
+def bind(x, rec=None):
+    """Enable compilation of the given function, method, or class object.
+
+If C is a class (or anything with a '__dict__' attribute), bind(C) will
+rebind all functions and methods found in C.__dict__ (which means, for
+classes, all methods defined in the class but not in its parents).
+
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        if rec is None:
+            x.func_code = _psyco.proxycode(x)
+        else:
+            x.func_code = _psyco.proxycode(x, rec)
+        return
+    if hasattr(x, '__dict__'):
+        funcs = [o for o in x.__dict__.values()
+                 if isinstance(o, types.MethodType)
+                 or isinstance(o, types.FunctionType)]
+        if not funcs:
+            raise error, ("nothing bindable found in %s object" %
+                          type(x).__name__)
+        for o in funcs:
+            bind(o, rec)
+        return
+    raise TypeError, "cannot bind %s objects" % type(x).__name__
+
+
+def unbind(x):
+    """Reverse of bind()."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        try:
+            f = _psyco.unproxycode(x.func_code)
+        except error:
+            pass
+        else:
+            x.func_code = f.func_code
+        return
+    if hasattr(x, '__dict__'):
+        for o in x.__dict__.values():
+            if (isinstance(o, types.MethodType)
+             or isinstance(o, types.FunctionType)):
+                unbind(o)
+        return
+    raise TypeError, "cannot unbind %s objects" % type(x).__name__
+
+
+def proxy(x, rec=None):
+    """Return a Psyco-enabled copy of the function.
+
+The original function is still available for non-compiled calls.
+The optional second argument specifies the number of recursive
+compilation levels: all functions called by func are compiled
+up to the given depth of indirection."""
+    if isinstance(x, types.FunctionType):
+        if rec is None:
+            code = _psyco.proxycode(x)
+        else:
+            code = _psyco.proxycode(x, rec)
+        return newfunction(code, x.func_globals, x.func_name)
+    if isinstance(x, types.MethodType):
+        p = proxy(x.im_func, rec)
+        return newinstancemethod(p, x.im_self, x.im_class)
+    raise TypeError, "cannot proxy %s objects" % type(x).__name__
+
+
+def unproxy(proxy):
+    """Return a new copy of the original function of method behind a proxy.
+The result behaves like the original function in that calling it
+does not trigger compilation nor execution of any compiled code."""
+    if isinstance(proxy, types.FunctionType):
+        return _psyco.unproxycode(proxy.func_code)
+    if isinstance(proxy, types.MethodType):
+        f = unproxy(proxy.im_func)
+        return newinstancemethod(f, proxy.im_self, proxy.im_class)
+    raise TypeError, "%s objects cannot be proxies" % type(proxy).__name__
+
+
+def cannotcompile(x):
+    """Instruct Psyco never to compile the given function, method
+or code object."""
+    if isinstance(x, types.MethodType):
+        x = x.im_func
+    if isinstance(x, types.FunctionType):
+        x = x.func_code
+    if isinstance(x, types.CodeType):
+        _psyco.cannotcompile(x)
+    else:
+        raise TypeError, "unexpected %s object" % type(x).__name__
+
+
+def dumpcodebuf():
+    """Write in file psyco.dump a copy of the emitted machine code,
+provided Psyco was compiled with a non-zero CODE_DUMP.
+See py-utils/httpxam.py to examine psyco.dump."""
+    if hasattr(_psyco, 'dumpcodebuf'):
+        _psyco.dumpcodebuf()
+
+
+###########################################################################
+# Psyco variables
+#   error         * the error raised by Psyco
+#   warning       * the warning raised by Psyco
+#   __in_psyco__  * a new built-in variable which is always zero, but which
+#                     Psyco special-cases by returning 1 instead. So
+#                     __in_psyco__ can be used in a function to know if
+#                     that function is being executed by Psyco or not.
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/kdictproxy.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/kdictproxy.py
new file mode 100644 (file)
index 0000000..c764e5e
--- /dev/null
@@ -0,0 +1,133 @@
+###########################################################################
+#
+#  Support code for the 'psyco.compact' type.
+
+from __future__ import generators
+
+try:
+    from UserDict import DictMixin
+except ImportError:
+
+    # backported from Python 2.3 to Python 2.2
+    class DictMixin:
+        # Mixin defining all dictionary methods for classes that already have
+        # a minimum dictionary interface including getitem, setitem, delitem,
+        # and keys. Without knowledge of the subclass constructor, the mixin
+        # does not define __init__() or copy().  In addition to the four base
+        # methods, progressively more efficiency comes with defining
+        # __contains__(), __iter__(), and iteritems().
+
+        # second level definitions support higher levels
+        def __iter__(self):
+            for k in self.keys():
+                yield k
+        def has_key(self, key):
+            try:
+                value = self[key]
+            except KeyError:
+                return False
+            return True
+        def __contains__(self, key):
+            return self.has_key(key)
+
+        # third level takes advantage of second level definitions
+        def iteritems(self):
+            for k in self:
+                yield (k, self[k])
+        def iterkeys(self):
+            return self.__iter__()
+
+        # fourth level uses definitions from lower levels
+        def itervalues(self):
+            for _, v in self.iteritems():
+                yield v
+        def values(self):
+            return [v for _, v in self.iteritems()]
+        def items(self):
+            return list(self.iteritems())
+        def clear(self):
+            for key in self.keys():
+                del self[key]
+        def setdefault(self, key, default):
+            try:
+                return self[key]
+            except KeyError:
+                self[key] = default
+            return default
+        def pop(self, key, *args):
+            if len(args) > 1:
+                raise TypeError, "pop expected at most 2 arguments, got "\
+                                  + repr(1 + len(args))
+            try:
+                value = self[key]
+            except KeyError:
+                if args:
+                    return args[0]
+                raise
+            del self[key]
+            return value
+        def popitem(self):
+            try:
+                k, v = self.iteritems().next()
+            except StopIteration:
+                raise KeyError, 'container is empty'
+            del self[k]
+            return (k, v)
+        def update(self, other):
+            # Make progressively weaker assumptions about "other"
+            if hasattr(other, 'iteritems'):  # iteritems saves memory and lookups
+                for k, v in other.iteritems():
+                    self[k] = v
+            elif hasattr(other, '__iter__'): # iter saves memory
+                for k in other:
+                    self[k] = other[k]
+            else:
+                for k in other.keys():
+                    self[k] = other[k]
+        def get(self, key, default=None):
+            try:
+                return self[key]
+            except KeyError:
+                return default
+        def __repr__(self):
+            return repr(dict(self.iteritems()))
+        def __cmp__(self, other):
+            if other is None:
+                return 1
+            if isinstance(other, DictMixin):
+                other = dict(other.iteritems())
+            return cmp(dict(self.iteritems()), other)
+        def __len__(self):
+            return len(self.keys())
+
+###########################################################################
+
+from _psyco import compact
+
+
+class compactdictproxy(DictMixin):
+
+    def __init__(self, ko):
+        self._ko = ko    # compact object of which 'self' is the dict
+
+    def __getitem__(self, key):
+        return compact.__getslot__(self._ko, key)
+
+    def __setitem__(self, key, value):
+        compact.__setslot__(self._ko, key, value)
+
+    def __delitem__(self, key):
+        compact.__delslot__(self._ko, key)
+
+    def keys(self):
+        return compact.__members__.__get__(self._ko)
+
+    def clear(self):
+        keys = self.keys()
+        keys.reverse()
+        for key in keys:
+            del self[key]
+
+    def __repr__(self):
+        keys = ', '.join(self.keys())
+        return '<compactdictproxy object {%s}>' % (keys,)
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/logger.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/logger.py
new file mode 100644 (file)
index 0000000..a3c5219
--- /dev/null
@@ -0,0 +1,96 @@
+###########################################################################
+# 
+#  Psyco logger.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco logger.
+
+See log() in core.py.
+"""
+###########################################################################
+
+
+import _psyco
+from time import time, localtime, strftime
+
+
+current = None
+print_charges = 10
+dump_delay = 0.2
+dump_last = 0.0
+
+def write(s, level):
+    t = time()
+    f = t-int(t)
+    try:
+        current.write("%s.%02d  %-*s%s\n" % (
+            strftime("%X", localtime(int(t))),
+            int(f*100.0), 63-level, s,
+            "%"*level))
+        current.flush()
+    except (OSError, IOError):
+        pass
+
+def psycowrite(s):
+    t = time()
+    f = t-int(t)
+    try:
+        current.write("%s.%02d  %-*s%s\n" % (
+            strftime("%X", localtime(int(t))),
+            int(f*100.0), 60, s.strip(),
+            "% %"))
+        current.flush()
+    except (OSError, IOError):
+        pass
+
+##def writelines(lines, level=0):
+##    if lines:
+##        t = time()
+##        f = t-int(t)
+##        timedesc = strftime("%x %X", localtime(int(t)))
+##        print >> current, "%s.%03d  %-*s %s" % (
+##            timedesc, int(f*1000),
+##            50-level, lines[0],
+##            "+"*level)
+##        timedesc = " " * (len(timedesc)+5)
+##        for line in lines[1:]:
+##            print >> current, timedesc, line
+
+def writememory():
+    write("memory usage: %d+ kb" % _psyco.memory(), 1)
+
+def dumpcharges():
+    global dump_last
+    if print_charges:
+        t = time()
+        if not (dump_last <= t < dump_last+dump_delay):
+            if t <= dump_last+1.5*dump_delay:
+                dump_last += dump_delay
+            else:
+                dump_last = t
+            #write("%s: charges:" % who, 0)
+            lst = _psyco.stattop(print_charges)
+            if lst:
+                f = t-int(t)
+                lines = ["%s.%02d   ______\n" % (
+                    strftime("%X", localtime(int(t))),
+                    int(f*100.0))]
+                i = 1
+                for co, charge in lst:
+                    detail = co.co_filename
+                    if len(detail) > 19:
+                        detail = '...' + detail[-17:]
+                    lines.append("        #%-3d |%4.1f %%|  %-26s%20s:%d\n" %
+                                 (i, charge*100.0, co.co_name, detail,
+                                  co.co_firstlineno))
+                    i += 1
+                current.writelines(lines)
+                current.flush()
+
+def writefinalstats():
+    dumpcharges()
+    writememory()
+    writedate("program exit")
+
+def writedate(msg):
+    write('%s, %s' % (msg, strftime("%x")), 20)
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/profiler.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/profiler.py
new file mode 100644 (file)
index 0000000..f1d06f1
--- /dev/null
@@ -0,0 +1,379 @@
+###########################################################################
+# 
+#  Psyco profiler (Python part).
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco profiler (Python part).
+
+The implementation of the non-time-critical parts of the profiler.
+See profile() and full() in core.py for the easy interface.
+"""
+###########################################################################
+
+import _psyco
+from support import *
+import math, time, types, atexit
+now = time.time
+try:
+    import thread
+except ImportError:
+    import dummy_thread as thread
+
+
+# current profiler instance
+current = None
+
+# enabled profilers, in order of priority
+profilers = []
+
+# logger module (when enabled by core.log())
+logger = None
+
+# a lock for a thread-safe go()
+go_lock = thread.allocate_lock()
+
+def go(stop=0):
+    # run the highest-priority profiler in 'profilers'
+    global current
+    go_lock.acquire()
+    try:
+        prev = current
+        if stop:
+            del profilers[:]
+        if prev:
+            if profilers and profilers[0] is prev:
+                return    # best profiler already running
+            prev.stop()
+            current = None
+        for p in profilers[:]:
+            if p.start():
+                current = p
+                if logger: # and p is not prev:
+                    logger.write("%s: starting" % p.__class__.__name__, 5)
+                return
+    finally:
+        go_lock.release()
+    # no profiler is running now
+    if stop:
+        if logger:
+            logger.writefinalstats()
+    else:
+        tag2bind()
+
+atexit.register(go, 1)
+
+
+def buildfncache(globals, cache):
+    if hasattr(types.IntType, '__dict__'):
+        clstypes = (types.ClassType, types.TypeType)
+    else:
+        clstypes = types.ClassType
+    for x in globals.values():
+        if isinstance(x, types.MethodType):
+            x = x.im_func
+        if isinstance(x, types.FunctionType):
+            cache[x.func_code] = x, ''
+        elif isinstance(x, clstypes):
+            for y in x.__dict__.values():
+                if isinstance(y, types.MethodType):
+                    y = y.im_func
+                if isinstance(y, types.FunctionType):
+                    cache[y.func_code] = y, x.__name__
+
+# code-to-function mapping (cache)
+function_cache = {}
+
+def trytobind(co, globals, log=1):
+    try:
+        f, clsname = function_cache[co]
+    except KeyError:
+        buildfncache(globals, function_cache)
+        try:
+            f, clsname = function_cache[co]
+        except KeyError:
+            if logger:
+                logger.write('warning: cannot find function %s in %s' %
+                             (co.co_name, globals.get('__name__', '?')), 3)
+            return  # give up
+    if logger and log:
+        modulename = globals.get('__name__', '?')
+        if clsname:
+            modulename += '.' + clsname
+        logger.write('bind function: %s.%s' % (modulename, co.co_name), 1)
+    f.func_code = _psyco.proxycode(f)
+
+
+# the list of code objects that have been tagged
+tagged_codes = []
+
+def tag(co, globals):
+    if logger:
+        try:
+            f, clsname = function_cache[co]
+        except KeyError:
+            buildfncache(globals, function_cache)
+            try:
+                f, clsname = function_cache[co]
+            except KeyError:
+                clsname = ''  # give up
+        modulename = globals.get('__name__', '?')
+        if clsname:
+            modulename += '.' + clsname
+        logger.write('tag function: %s.%s' % (modulename, co.co_name), 1)
+    tagged_codes.append((co, globals))
+    _psyco.turbo_frame(co)
+    _psyco.turbo_code(co)
+
+def tag2bind():
+    if tagged_codes:
+        if logger:
+            logger.write('profiling stopped, binding %d functions' %
+                         len(tagged_codes), 2)
+        for co, globals in tagged_codes:
+            trytobind(co, globals, 0)
+        function_cache.clear()
+        del tagged_codes[:]
+
+
+class Profiler:
+    MemoryTimerResolution = 0.103
+
+    def run(self, memory, time, memorymax, timemax):
+        self.memory = memory
+        self.memorymax = memorymax
+        self.time = time
+        if timemax is None:
+            self.endtime = None
+        else:
+            self.endtime = now() + timemax
+        self.alarms = []
+        profilers.append(self)
+        go()
+    
+    def start(self):
+        curmem = _psyco.memory()
+        memlimits = []
+        if self.memorymax is not None:
+            if curmem >= self.memorymax:
+                if logger:
+                    logger.writememory()
+                return self.limitreached('memorymax')
+            memlimits.append(self.memorymax)
+        if self.memory is not None:
+            if self.memory <= 0:
+                if logger:
+                    logger.writememory()
+                return self.limitreached('memory')
+            memlimits.append(curmem + self.memory)
+            self.memory_at_start = curmem
+
+        curtime = now()
+        timelimits = []
+        if self.endtime is not None:
+            if curtime >= self.endtime:
+                return self.limitreached('timemax')
+            timelimits.append(self.endtime - curtime)
+        if self.time is not None:
+            if self.time <= 0.0:
+                return self.limitreached('time')
+            timelimits.append(self.time)
+            self.time_at_start = curtime
+        
+        try:
+            self.do_start()
+        except error, e:
+            if logger:
+                logger.write('%s: disabled by psyco.error:' % (
+                    self.__class__.__name__), 4)
+                logger.write('    %s' % str(e), 3)
+            return 0
+        
+        if memlimits:
+            self.memlimits_args = (time.sleep, (self.MemoryTimerResolution,),
+                                   self.check_memory, (min(memlimits),))
+            self.alarms.append(_psyco.alarm(*self.memlimits_args))
+        if timelimits:
+            self.alarms.append(_psyco.alarm(time.sleep, (min(timelimits),),
+                                            self.time_out))
+        return 1
+    
+    def stop(self):
+        for alarm in self.alarms:
+            alarm.stop(0)
+        for alarm in self.alarms:
+            alarm.stop(1)   # wait for parallel threads to stop
+        del self.alarms[:]
+        if self.time is not None:
+            self.time -= now() - self.time_at_start
+        if self.memory is not None:
+            self.memory -= _psyco.memory() - self.memory_at_start
+
+        try:
+            self.do_stop()
+        except error:
+            return 0
+        return 1
+
+    def check_memory(self, limit):
+        if _psyco.memory() < limit:
+            return self.memlimits_args
+        go()
+
+    def time_out(self):
+        self.time = 0.0
+        go()
+
+    def limitreached(self, limitname):
+        try:
+            profilers.remove(self)
+        except ValueError:
+            pass
+        if logger:
+            logger.write('%s: disabled (%s limit reached)' % (
+                self.__class__.__name__, limitname), 4)
+        return 0
+
+
+class FullCompiler(Profiler):
+
+    def do_start(self):
+        _psyco.profiling('f')
+
+    def do_stop(self):
+        _psyco.profiling('.')
+
+
+class RunOnly(Profiler):
+
+    def do_start(self):
+        _psyco.profiling('n')
+
+    def do_stop(self):
+        _psyco.profiling('.')
+
+
+class ChargeProfiler(Profiler):
+
+    def __init__(self, watermark, parentframe):
+        self.watermark = watermark
+        self.parent2 = parentframe * 2.0
+        self.lock = thread.allocate_lock()
+
+    def init_charges(self):
+        _psyco.statwrite(watermark = self.watermark,
+                         parent2   = self.parent2)
+
+    def do_stop(self):
+        _psyco.profiling('.')
+        _psyco.statwrite(callback = None)
+
+
+class ActiveProfiler(ChargeProfiler):
+
+    def active_start(self):
+        _psyco.profiling('p')
+
+    def do_start(self):
+        self.init_charges()
+        self.active_start()
+        _psyco.statwrite(callback = self.charge_callback)
+
+    def charge_callback(self, frame, charge):
+        tag(frame.f_code, frame.f_globals)
+
+
+class PassiveProfiler(ChargeProfiler):
+
+    initial_charge_unit   = _psyco.statread('unit')
+    reset_stats_after     = 120      # half-lives (maximum 200!)
+    reset_limit           = initial_charge_unit * (2.0 ** reset_stats_after)
+
+    def __init__(self, watermark, halflife, pollfreq, parentframe):
+        ChargeProfiler.__init__(self, watermark, parentframe)
+        self.pollfreq = pollfreq
+        # self.progress is slightly more than 1.0, and computed so that
+        # do_profile() will double the change_unit every 'halflife' seconds.
+        self.progress = 2.0 ** (1.0 / (halflife * pollfreq))
+
+    def reset(self):
+        _psyco.statwrite(unit = self.initial_charge_unit, callback = None)
+        _psyco.statreset()
+        if logger:
+            logger.write("%s: resetting stats" % self.__class__.__name__, 1)
+
+    def passive_start(self):
+        self.passivealarm_args = (time.sleep, (1.0 / self.pollfreq,),
+                                  self.do_profile)
+        self.alarms.append(_psyco.alarm(*self.passivealarm_args))
+
+    def do_start(self):
+        tag2bind()
+        self.init_charges()
+        self.passive_start()
+
+    def do_profile(self):
+        _psyco.statcollect()
+        if logger:
+            logger.dumpcharges()
+        nunit = _psyco.statread('unit') * self.progress
+        if nunit > self.reset_limit:
+            self.reset()
+        else:
+            _psyco.statwrite(unit = nunit, callback = self.charge_callback)
+        return self.passivealarm_args
+
+    def charge_callback(self, frame, charge):
+        trytobind(frame.f_code, frame.f_globals)
+
+
+class ActivePassiveProfiler(PassiveProfiler, ActiveProfiler):
+
+    def do_start(self):
+        self.init_charges()
+        self.active_start()
+        self.passive_start()
+
+    def charge_callback(self, frame, charge):
+        tag(frame.f_code, frame.f_globals)
+
+
+
+#
+# we register our own version of sys.settrace(), sys.setprofile()
+# and thread.start_new_thread().
+#
+
+def psyco_settrace(*args, **kw):
+    "This is the Psyco-aware version of sys.settrace()."
+    result = original_settrace(*args, **kw)
+    go()
+    return result
+
+def psyco_setprofile(*args, **kw):
+    "This is the Psyco-aware version of sys.setprofile()."
+    result = original_setprofile(*args, **kw)
+    go()
+    return result
+
+def psyco_thread_stub(callable, args, kw):
+    _psyco.statcollect()
+    if kw is None:
+        return callable(*args)
+    else:
+        return callable(*args, **kw)
+
+def psyco_start_new_thread(callable, args, kw=None):
+    "This is the Psyco-aware version of thread.start_new_thread()."
+    return original_start_new_thread(psyco_thread_stub, (callable, args, kw))
+
+original_settrace         = sys.settrace
+original_setprofile       = sys.setprofile
+original_start_new_thread = thread.start_new_thread
+sys.settrace            = psyco_settrace
+sys.setprofile          = psyco_setprofile
+thread.start_new_thread = psyco_start_new_thread
+# hack to patch threading._start_new_thread if the module is
+# already loaded
+if ('threading' in sys.modules and
+    hasattr(sys.modules['threading'], '_start_new_thread')):
+    sys.modules['threading']._start_new_thread = psyco_start_new_thread
diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/support.py b/Calibre_Plugins/eReaderPDB2PML_plugin/osx/psyco/support.py
new file mode 100644 (file)
index 0000000..387321a
--- /dev/null
@@ -0,0 +1,191 @@
+###########################################################################
+# 
+#  Psyco general support module.
+#   Copyright (C) 2001-2002  Armin Rigo et.al.
+
+"""Psyco general support module.
+
+For internal use.
+"""
+###########################################################################
+
+import sys, _psyco, __builtin__
+
+error = _psyco.error
+class warning(Warning):
+    pass
+
+_psyco.NoLocalsWarning = warning
+
+def warn(msg):
+    from warnings import warn
+    warn(msg, warning, stacklevel=2)
+
+#
+# Version checks
+#
+__version__ = 0x010600f0
+if _psyco.PSYVER != __version__:
+    raise error, "version mismatch between Psyco parts, reinstall it"
+
+version_info = (__version__ >> 24,
+                (__version__ >> 16) & 0xff,
+                (__version__ >> 8) & 0xff,
+                {0xa0: 'alpha',
+                 0xb0: 'beta',
+                 0xc0: 'candidate',
+                 0xf0: 'final'}[__version__ & 0xf0],
+                __version__ & 0xf)
+
+
+VERSION_LIMITS = [0x02020200,   # 2.2.2
+                  0x02030000,   # 2.3
+                  0x02040000]   # 2.4
+
+if ([v for v in VERSION_LIMITS if v <= sys.hexversion] !=
+    [v for v in VERSION_LIMITS if v <= _psyco.PYVER  ]):
+    if sys.hexversion < VERSION_LIMITS[0]:
+        warn("Psyco requires Python version 2.2.2 or later")
+    else:
+        warn("Psyco version does not match Python version. "
+             "Psyco must be updated or recompiled")
+
+
+if hasattr(_psyco, 'ALL_CHECKS') and hasattr(_psyco, 'VERBOSE_LEVEL'):
+    print >> sys.stderr, ('psyco: running in debugging mode on %s' %
+                          _psyco.PROCESSOR)
+
+
+###########################################################################
+# sys._getframe() gives strange results on a mixed Psyco- and Python-style
+# stack frame. Psyco provides a replacement that partially emulates Python
+# frames from Psyco frames. The new sys._getframe() may return objects of
+# a custom "Psyco frame" type, which is a subtype of the normal frame type.
+#
+# The same problems require some other built-in functions to be replaced
+# as well. Note that the local variables are not available in any
+# dictionary with Psyco.
+
+
+class Frame:
+    pass
+
+
+class PythonFrame(Frame):
+
+    def __init__(self, frame):
+        self.__dict__.update({
+            '_frame': frame,
+            })
+
+    def __getattr__(self, attr):
+        if attr == 'f_back':
+            try:
+                result = embedframe(_psyco.getframe(self._frame))
+            except ValueError:
+                result = None
+            except error:
+                warn("f_back is skipping dead Psyco frames")
+                result = self._frame.f_back
+            self.__dict__['f_back'] = result
+            return result
+        else:
+            return getattr(self._frame, attr)
+
+    def __setattr__(self, attr, value):
+        setattr(self._frame, attr, value)
+
+    def __delattr__(self, attr):
+        delattr(self._frame, attr)
+
+
+class PsycoFrame(Frame):
+
+    def __init__(self, tag):
+        self.__dict__.update({
+            '_tag'     : tag,
+            'f_code'   : tag[0],
+            'f_globals': tag[1],
+            })
+
+    def __getattr__(self, attr):
+        if attr == 'f_back':
+            try:
+                result = embedframe(_psyco.getframe(self._tag))
+            except ValueError:
+                result = None
+        elif attr == 'f_lineno':
+            result = self.f_code.co_firstlineno  # better than nothing
+        elif attr == 'f_builtins':
+            result = self.f_globals['__builtins__']
+        elif attr == 'f_restricted':
+            result = self.f_builtins is not __builtins__
+        elif attr == 'f_locals':
+            raise AttributeError, ("local variables of functions run by Psyco "
+                                   "cannot be accessed in any way, sorry")
+        else:
+            raise AttributeError, ("emulated Psyco frames have "
+                                   "no '%s' attribute" % attr)
+        self.__dict__[attr] = result
+        return result
+
+    def __setattr__(self, attr, value):
+        raise AttributeError, "Psyco frame objects are read-only"
+
+    def __delattr__(self, attr):
+        if attr == 'f_trace':
+            # for bdb which relies on CPython frames exhibiting a slightly
+            # buggy behavior: you can 'del f.f_trace' as often as you like
+            # even without having set it previously.
+            return
+        raise AttributeError, "Psyco frame objects are read-only"
+
+
+def embedframe(result):
+    if type(result) is type(()):
+        return PsycoFrame(result)
+    else:
+        return PythonFrame(result)
+
+def _getframe(depth=0):
+    """Return a frame object from the call stack. This is a replacement for
+sys._getframe() which is aware of Psyco frames.
+
+The returned objects are instances of either PythonFrame or PsycoFrame
+instead of being real Python-level frame object, so that they can emulate
+the common attributes of frame objects.
+
+The original sys._getframe() ignoring Psyco frames altogether is stored in
+psyco._getrealframe(). See also psyco._getemulframe()."""
+    # 'depth+1' to account for this _getframe() Python function
+    return embedframe(_psyco.getframe(depth+1))
+
+def _getemulframe(depth=0):
+    """As _getframe(), but the returned objects are real Python frame objects
+emulating Psyco frames. Some of their attributes can be wrong or missing,
+however."""
+    # 'depth+1' to account for this _getemulframe() Python function
+    return _psyco.getframe(depth+1, 1)
+
+def patch(name, module=__builtin__):
+    f = getattr(_psyco, name)
+    org = getattr(module, name)
+    if org is not f:
+        setattr(module, name, f)
+        setattr(_psyco, 'original_' + name, org)
+
+_getrealframe = sys._getframe
+sys._getframe = _getframe
+patch('globals')
+patch('eval')
+patch('execfile')
+patch('locals')
+patch('vars')
+patch('dir')
+patch('input')
+_psyco.original_raw_input = raw_input
+__builtin__.__in_psyco__ = 0==1   # False
+
+if hasattr(_psyco, 'compact'):
+    import kdictproxy
+    _psyco.compactdictproxy = kdictproxy.compactdictproxy
diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip
new file mode 100644 (file)
index 0000000..c2f4a71
Binary files /dev/null and b/Calibre_Plugins/k4mobidedrm_plugin.zip differ
diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py
new file mode 100644 (file)
index 0000000..6a5c071
--- /dev/null
@@ -0,0 +1,490 @@
+#!/usr/bin/env python
+
+# engine to remove drm from Kindle for Mac and Kindle for PC books
+# for personal use for archiving and converting your ebooks
+
+# PLEASE DO NOT PIRATE EBOOKS! 
+
+# We want all authors and publishers, and eBook stores to live
+# long and prosperous lives but at the same time  we just want to 
+# be able to read OUR books on whatever device we want and to keep 
+# readable for a long, long time
+
+#  This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle, 
+#    unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates 
+#    and many many others
+
+# It can run standalone to convert K4M/K4PC/Mobi files, or it can be installed as a
+# plugin for Calibre (http://calibre-ebook.com/about) so that importing
+# K4 or Mobi with DRM is no londer a multi-step process.
+#
+# ***NOTE*** If you are using this script as a calibre plugin for a K4M or K4PC ebook
+# then calibre must be installed on the same machine and in the same account as K4PC or K4M
+# for the plugin version to function properly.
+#
+# To create a Calibre plugin, rename this file so that the filename
+# ends in '_plugin.py', put it into a ZIP file with all its supporting python routines
+# and import that ZIP into Calibre using its plugin configuration GUI.
+
+from __future__ import with_statement
+
+__version__ = '1.1'
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+import os, csv, getopt
+import binascii
+import zlib
+from struct import pack, unpack, unpack_from
+
+
+#Exception Handling
+class DrmException(Exception):
+    pass
+
+#
+# crypto digestroutines
+#
+
+import hashlib
+
+def MD5(message):
+    ctx = hashlib.md5()
+    ctx.update(message)
+    return ctx.digest()
+
+def SHA1(message):
+    ctx = hashlib.sha1()
+    ctx.update(message)
+    return ctx.digest()
+
+# determine if we are running as a calibre plugin
+if 'calibre' in sys.modules:
+    inCalibre = True
+    global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+else:
+    inCalibre = False
+
+#
+# start of Kindle specific routines
+#
+
+if not inCalibre:
+    import mobidedrm
+    if sys.platform.startswith('win'):
+        from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+    if sys.platform.startswith('darwin'):
+        from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+
+global kindleDatabase
+
+# Encode the bytes in data with the characters in map
+def encode(data, map):
+    result = ""
+    for char in data:
+        value = ord(char)
+        Q = (value ^ 0x80) // len(map)
+        R = value % len(map)
+        result += map[Q]
+        result += map[R]
+    return result
+  
+# Hash the bytes in data and then encode the digest with the characters in map
+def encodeHash(data,map):
+    return encode(MD5(data),map)
+
+# Decode the string in data with the characters in map. Returns the decoded bytes
+def decode(data,map):
+    result = ""
+    for i in range (0,len(data)-1,2):
+        high = map.find(data[i])
+        low = map.find(data[i+1])
+        if (high == -1) or (low == -1) :
+            break
+        value = (((high * len(map)) ^ 0x80) & 0xFF) + low
+        result += pack("B",value)
+    return result
+
+
+# Parse the Kindle.info file and return the records as a list of key-values
+def parseKindleInfo():
+    DB = {}
+    infoReader = openKindleInfo()
+    infoReader.read(1)
+    data = infoReader.read()
+    if sys.platform.startswith('win'):
+        items = data.split('{')
+    else :
+        items = data.split('[')
+    for item in items:
+        splito = item.split(':')
+        DB[splito[0]] =splito[1]
+    return DB
+
+# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
+def getKindleInfoValueForHash(hashedKey):
+    global kindleDatabase
+    encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
+    if sys.platform.startswith('win'):
+        return CryptUnprotectData(encryptedValue,"")
+    else:
+        cleartext = CryptUnprotectData(encryptedValue)
+        return decode(cleartext, charMap1)
+#  Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
+def getKindleInfoValueForKey(key):
+    return getKindleInfoValueForHash(encodeHash(key,charMap2))
+
+# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string.
+def findNameForHash(hash):
+    names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
+    result = ""
+    for name in names:
+        if hash == encodeHash(name, charMap2):
+           result = name
+           break
+    return result
+    
+# Print all the records from the kindle.info file (option -i)
+def printKindleInfo():
+    for record in kindleDatabase:
+        name = findNameForHash(record)
+        if name != "" :
+            print (name)
+            print ("--------------------------")
+        else :
+            print ("Unknown Record")
+        print getKindleInfoValueForHash(record)
+        print "\n"
+
+#
+# PID generation routines
+#
+  
+# Returns two bit at offset from a bit field
+def getTwoBitsFromBitField(bitField,offset):
+    byteNumber = offset // 4
+    bitPosition = 6 - 2*(offset % 4)
+    return ord(bitField[byteNumber]) >> bitPosition & 3
+
+# Returns the six bits at offset from a bit field
+def getSixBitsFromBitField(bitField,offset):
+     offset *= 3
+     value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
+     return value
+     
+# 8 bits to six bits encoding from hash to generate PID string
+def encodePID(hash):
+    global charMap3
+    PID = ""
+    for position in range (0,8):
+        PID += charMap3[getSixBitsFromBitField(hash,position)]
+    return PID
+
+# Encryption table used to generate the device PID
+def generatePidEncryptionTable() :
+    table = []
+    for counter1 in range (0,0x100):
+        value = counter1
+        for counter2 in range (0,8):
+            if (value & 1 == 0) :
+                value = value >> 1
+            else :
+                value = value >> 1
+                value = value ^ 0xEDB88320
+        table.append(value)
+    return table
+
+# Seed value used to generate the device PID
+def generatePidSeed(table,dsn) :
+    value = 0
+    for counter in range (0,4) :
+       index = (ord(dsn[counter]) ^ value) &0xFF
+       value = (value >> 8) ^ table[index]
+    return value
+
+# Generate the device PID
+def generateDevicePID(table,dsn,nbRoll):
+    seed = generatePidSeed(table,dsn)
+    pidAscii = ""
+    pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
+    index = 0
+    for counter in range (0,nbRoll):
+        pid[index] = pid[index] ^ ord(dsn[counter])
+        index = (index+1) %8
+    for counter in range (0,8):
+        index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
+        pidAscii += charMap4[index]
+    return pidAscii
+
+# convert from 8 digit PID to 10 digit PID with checksum
+def checksumPid(s):
+    letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+    crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
+    crc = crc ^ (crc >> 16)
+    res = s
+    l = len(letters)
+    for i in (0,1):
+        b = crc & 0xff
+        pos = (b // l) ^ (b % l)
+        res += letters[pos%l]
+        crc >>= 8
+    return res
+
+
+class MobiPeek:
+    def loadSection(self, section):
+        before, after = self.sections[section:section+2]
+        self.f.seek(before)
+        return self.f.read(after - before)
+    def __init__(self, filename):
+        self.f = file(filename, 'rb')
+        self.header = self.f.read(78)
+        self.ident = self.header[0x3C:0x3C+8]
+        if self.ident != 'BOOKMOBI' and self.ident != 'TEXtREAd':
+            raise DrmException('invalid file format')
+        self.num_sections, = unpack_from('>H', self.header, 76)
+        sections = self.f.read(self.num_sections*8)
+        self.sections = unpack_from('>%dL' % (self.num_sections*2), sections, 0)[::2] + (0xfffffff, )
+        self.sect0 = self.loadSection(0)
+        self.f.close()
+    def getBookTitle(self):
+        # get book title
+        toff, tlen = unpack('>II', self.sect0[0x54:0x5c])
+        tend = toff + tlen
+        title = self.sect0[toff:tend]
+        return title
+    def getexthData(self):
+        # if exth region exists then grab it
+        # get length of this header
+        length, type, codepage, unique_id, version = unpack('>LLLLL', self.sect0[20:40])
+        exth_flag, = unpack('>L', self.sect0[0x80:0x84])
+        exth = ''
+        if exth_flag & 0x40:
+            exth = self.sect0[16 + length:]
+        return exth
+    def isNotEncrypted(self):
+        lock_type, = unpack('>H', self.sect0[0xC:0xC+2])
+        if lock_type == 0:
+            return True
+        return False
+
+# DiapDealer's stuff: Parse the EXTH header records and parse the Kindleinfo
+# file to calculate the book pid.
+def getK4Pids(exth, title):
+    global kindleDatabase
+    try:
+        kindleDatabase = parseKindleInfo()
+    except Exception as message:
+        print(message)
+    
+    if kindleDatabase != None :
+        # Get the Mazama Random number
+        MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
+
+        # Get the HDD serial
+        encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1)
+
+        # Get the current user name
+        encodedUsername = encodeHash(GetUserName(),charMap1)
+
+        # concat, hash and encode to calculate the DSN
+        DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
+       
+        print("\nDSN: " + DSN)
+
+        # Compute the device PID (for which I can tell, is used for nothing).
+        # But hey, stuff being printed out is apparently cool.
+        table =  generatePidEncryptionTable()
+        devicePID = generateDevicePID(table,DSN,4)
+            
+        print("Device PID: " + checksumPid(devicePID))
+            
+        # Compute book PID
+        exth_records = {}
+        nitems, = unpack('>I', exth[8:12])
+        pos = 12
+        # Parse the exth records, storing data indexed by type
+        for i in xrange(nitems):
+            type, size = unpack('>II', exth[pos: pos + 8])
+            content = exth[pos + 8: pos + size]
+
+            exth_records[type] = content
+            pos += size
+
+        # Grab the contents of the type 209 exth record
+        if exth_records[209] != None:
+            data = exth_records[209]
+        else:
+            raise DrmException("\nNo EXTH record type 209 - Perhaps not a K4 file?")
+        
+        # Parse the 209 data to find the the exth record with the token data.
+        # The last character of the 209 data points to the record with the token.
+        # Always 208 from my experience, but I'll leave the logic in case that changes.
+        for i in xrange(len(data)):
+            if ord(data[i]) != 0:
+                if exth_records[ord(data[i])] != None:
+                    token = exth_records[ord(data[i])]
+
+        # Get the kindle account token
+        kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
+    
+        print("Account Token: " + kindleAccountToken)
+
+        pidHash = SHA1(DSN+kindleAccountToken+exth_records[209]+token)
+   
+        bookPID = encodePID(pidHash)
+        bookPID = checksumPid(bookPID)
+
+        if exth_records[503] != None:
+            print "Pid for " + exth_records[503] + ": " + bookPID
+        else:
+            print "Pid for " + title + ":" + bookPID
+        return bookPID
+    
+    raise DrmException("\nCould not access K4 data - Perhaps K4 is not installed/configured?")
+    return null
+
+#
+# Main
+#   
+def main(argv=sys.argv):
+    global kindleDatabase
+    import mobidedrm
+    print ('K4MobiDeDrm v%(__version__)s '
+          'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
+
+    if len(argv)<3:
+        print "Removes DRM protection from K4PC, K4M, and Mobi ebooks"
+        print "Usage:"
+        print "    %s <infile> <outfile> [<pidnums>]" % argv[0]
+        return 1
+
+    if len(argv) == 4:
+        pidnums = argv[3]
+
+    if len(argv) == 3:
+        pidnums = "" 
+
+    kindleDatabase = None
+    infile = argv[1]
+    outfile = argv[2]
+    try:
+        # first try with K4PC/K4M
+        ex = MobiPeek(infile)
+        if ex.isNotEncrypted():
+            print "File was Not Encrypted"
+            return 2
+        title = ex.getBookTitle()
+        exth = ex.getexthData()
+        pid = getK4Pids(exth, title)
+        unlocked_file = mobidedrm.getUnencryptedBook(infile, pid)
+    except DrmException:
+        pass
+    except mobidedrm.DrmException:
+        pass
+    else:
+        file(outfile, 'wb').write(unlocked_file)
+        return 0
+
+    # now try from the pid list
+    pids = pidnums.split(',')
+    for pid in pids:
+        try:
+            print 'Trying: "'+ pid + '"'
+            unlocked_file = mobidedrm.getUnencryptedBook(infile, pid)
+        except mobidedrm.DrmException:
+            pass
+        else:
+            file(outfile, 'wb').write(unlocked_file)
+            return 0
+
+    # we could not unencrypt book
+    print "Error: Could Not Unencrypt Book"
+    return 1
+
+
+if __name__ == '__main__':
+    sys.stdout=Unbuffered(sys.stdout)
+    sys.exit(main())
+
+
+if not __name__ == "__main__" and inCalibre:
+    from calibre.customize import FileTypePlugin
+
+    class K4DeDRM(FileTypePlugin):
+        name                = 'K4PC, K4Mac, Mobi DeDRM' # Name of the plugin
+        description         = 'Removes DRM from K4PC, K4Mac, and Mobi files. \
+                                Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.'
+        supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on
+        author              = 'DiapDealer, SomeUpdates' # The author of this plugin
+        version             = (0, 0, 1)   # The version number of this plugin
+        file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
+        on_import           = True # Run this plugin during the import
+        priority            = 200  # run this plugin before mobidedrm, k4pcdedrm, k4dedrm
+
+        def run(self, path_to_ebook):
+            from calibre.gui2 import is_ok_to_use_qt
+            from PyQt4.Qt import QMessageBox
+            global kindleDatabase
+            global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            if sys.platform.startswith('win'):
+                from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            if sys.platform.startswith('darwin'):
+                from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            import mobidedrm
+
+            pidnums = self.site_customization
+
+            # first try with book specifc pid from K4PC or K4M
+            try:
+                kindleDatabase = None
+                ex = MobiPeek(path_to_ebook)
+                if ex.isNotEncrypted():
+                    return path_to_ebook
+                title = ex.getBookTitle()
+                exth = ex.getexthData()
+                pid = getK4Pids(exth, title)
+                unlocked_file = mobidedrm.getUnencryptedBook(path_to_ebook,pid)
+            except DrmException:
+                pass
+            except mobidedrm.DrmException:
+                pass
+            else:
+                of = self.temporary_file('.mobi')
+                of.write(unlocked_file)
+                of.close()
+                return of.name
+
+            # now try from the pid list
+            pids = pidnums.split(',')
+            for pid in pids:
+                try:
+                    unlocked_file = mobidedrm.getUnencryptedBook(path_to_ebook, pid)
+                except mobidedrm.DrmException:
+                    pass
+                else:
+                    of = self.temporary_file('.mobi')
+                    of.write(unlocked_file)
+                    of.close()
+                    return of.name
+
+            #if you reached here then no luck raise and exception
+            if is_ok_to_use_qt():
+                d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook)
+                d.show()
+                d.raise_()
+                d.exec_()
+            raise Exception("K4MobiDeDRM plugin could not decode the file")
+            return ""
+
+        def customization_help(self, gui=False):
+            return 'Enter each 10 character PID separated by a comma (no spaces).'
diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py
new file mode 100644 (file)
index 0000000..cb13e5f
--- /dev/null
@@ -0,0 +1,319 @@
+# standlone set of Mac OSX specific routines needed for K4DeDRM
+
+from __future__ import with_statement
+
+import sys
+import os
+
+#Exception Handling
+class K4MDrmException(Exception):
+    pass
+
+import signal
+import threading
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+# **heavily** chopped up and modfied version of asyncproc.py
+# to make it actually work on Windows as well as Mac/Linux
+# For the original see:
+# "http://www.lysator.liu.se/~bellman/download/"
+# author is  "Thomas Bellman <bellman@lysator.liu.se>"
+# available under GPL version 3 or Later
+
+# create an asynchronous subprocess whose output can be collected in
+# a non-blocking manner
+
+# What a mess!  Have to use threads just to get non-blocking io
+# in a cross-platform manner
+
+# luckily all thread use is hidden within this class
+
+class Process(object):
+    def __init__(self, *params, **kwparams):
+        if len(params) <= 3:
+            kwparams.setdefault('stdin', subprocess.PIPE)
+        if len(params) <= 4:
+            kwparams.setdefault('stdout', subprocess.PIPE)
+        if len(params) <= 5:
+            kwparams.setdefault('stderr', subprocess.PIPE)
+        self.__pending_input = []
+        self.__collected_outdata = []
+        self.__collected_errdata = []
+        self.__exitstatus = None
+        self.__lock = threading.Lock()
+        self.__inputsem = threading.Semaphore(0)
+        self.__quit = False
+
+        self.__process = subprocess.Popen(*params, **kwparams)
+
+        if self.__process.stdin:
+            self.__stdin_thread = threading.Thread(
+                name="stdin-thread",
+                target=self.__feeder, args=(self.__pending_input,
+                                            self.__process.stdin))
+            self.__stdin_thread.setDaemon(True)
+            self.__stdin_thread.start()
+
+        if self.__process.stdout:
+            self.__stdout_thread = threading.Thread(
+                name="stdout-thread",
+                target=self.__reader, args=(self.__collected_outdata,
+                                           self.__process.stdout))
+            self.__stdout_thread.setDaemon(True)
+            self.__stdout_thread.start()
+
+        if self.__process.stderr:
+            self.__stderr_thread = threading.Thread(
+                name="stderr-thread",
+                target=self.__reader, args=(self.__collected_errdata,
+                                           self.__process.stderr))
+            self.__stderr_thread.setDaemon(True)
+            self.__stderr_thread.start()
+
+    def pid(self):
+        return self.__process.pid
+
+    def kill(self, signal):
+        self.__process.send_signal(signal)
+
+    # check on subprocess (pass in 'nowait') to act like poll
+    def wait(self, flag):
+        if flag.lower() == 'nowait':
+            rc = self.__process.poll()
+        else:
+            rc = self.__process.wait()
+        if rc != None:
+            if self.__process.stdin:
+                self.closeinput()
+            if self.__process.stdout:
+                self.__stdout_thread.join()
+            if self.__process.stderr:
+                self.__stderr_thread.join()
+        return self.__process.returncode
+
+    def terminate(self):
+        if self.__process.stdin:
+            self.closeinput()
+        self.__process.terminate()
+
+    # thread gets data from subprocess stdout
+    def __reader(self, collector, source):
+        while True:
+            data = os.read(source.fileno(), 65536)
+            self.__lock.acquire()
+            collector.append(data)
+            self.__lock.release()
+            if data == "":
+                source.close()
+                break
+        return
+
+    # thread feeds data to subprocess stdin
+    def __feeder(self, pending, drain):
+        while True:
+            self.__inputsem.acquire()
+            self.__lock.acquire()
+            if not pending  and self.__quit:
+                drain.close()
+                self.__lock.release()
+                break
+            data = pending.pop(0)
+            self.__lock.release()
+            drain.write(data)
+
+    # non-blocking read of data from subprocess stdout
+    def read(self):
+        self.__lock.acquire()
+        outdata = "".join(self.__collected_outdata)
+        del self.__collected_outdata[:]
+        self.__lock.release()
+        return outdata
+
+    # non-blocking read of data from subprocess stderr
+    def readerr(self):
+        self.__lock.acquire()
+        errdata = "".join(self.__collected_errdata)
+        del self.__collected_errdata[:]
+        self.__lock.release()
+        return errdata
+
+    # non-blocking write to stdin of subprocess
+    def write(self, data):
+        if self.__process.stdin is None:
+            raise ValueError("Writing to process with stdin not a pipe")
+        self.__lock.acquire()
+        self.__pending_input.append(data)
+        self.__inputsem.release()
+        self.__lock.release()
+
+    # close stdinput of subprocess
+    def closeinput(self):
+        self.__lock.acquire()
+        self.__quit = True
+        self.__inputsem.release()
+        self.__lock.release()
+
+
+# interface to needed routines in openssl's libcrypto
+def _load_crypto_libcrypto():
+    from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
+        Structure, c_ulong, create_string_buffer, addressof, string_at, cast
+    from ctypes.util import find_library
+
+    libcrypto = find_library('crypto')
+    if libcrypto is None:
+        raise K4MDrmException('libcrypto not found')
+    libcrypto = CDLL(libcrypto)
+
+    AES_MAXNR = 14
+    c_char_pp = POINTER(c_char_p)
+    c_int_p = POINTER(c_int)
+
+    class AES_KEY(Structure):
+        _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
+    AES_KEY_p = POINTER(AES_KEY)
+    
+    def F(restype, name, argtypes):
+        func = getattr(libcrypto, name)
+        func.restype = restype
+        func.argtypes = argtypes
+        return func
+    
+    AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int])
+
+    AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
+
+    PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1', 
+                                [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
+    
+    class LibCrypto(object):
+        def __init__(self):
+            self._blocksize = 0
+            self._keyctx = None
+            self.iv = 0
+
+        def set_decrypt_key(self, userkey, iv):
+            self._blocksize = len(userkey)
+            if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
+                raise K4MDrmException('AES improper key used')
+                return
+            keyctx = self._keyctx = AES_KEY()
+            self.iv = iv
+            rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
+            if rv < 0:
+                raise K4MDrmException('Failed to initialize AES key')
+
+        def decrypt(self, data):
+            out = create_string_buffer(len(data))
+            rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0)
+            if rv == 0:
+                raise K4MDrmException('AES decryption failed')
+            return out.raw
+
+        def keyivgen(self, passwd):
+            salt = '16743'
+            saltlen = 5
+            passlen = len(passwd)
+            iter = 0x3e8
+            keylen = 80
+            out = create_string_buffer(keylen)
+            rv = PKCS5_PBKDF2_HMAC_SHA1(passwd, passlen, salt, saltlen, iter, keylen, out)
+            return out.raw
+    return LibCrypto
+
+def _load_crypto():
+    LibCrypto = None
+    try:
+        LibCrypto = _load_crypto_libcrypto()
+    except (ImportError, K4MDrmException):
+        pass
+    return LibCrypto
+
+LibCrypto = _load_crypto()
+
+#
+# Utility Routines
+#
+
+# uses a sub process to get the Hard Drive Serial Number using ioreg
+# returns with the first found serial number in that class
+def GetVolumeSerialNumber():
+    cmdline = '/usr/sbin/ioreg -r -c AppleAHCIDiskDriver'
+    cmdline = cmdline.encode(sys.getfilesystemencoding())
+    p = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+    poll = p.wait('wait')
+    results = p.read()
+    reslst = results.split('\n')
+    sernum = '9999999999'
+    cnt = len(reslst)
+    for j in xrange(cnt):
+        resline = reslst[j]
+        pp = resline.find('"Serial Number" = "')
+        if pp >= 0:
+            sernum = resline[pp+19:]
+            sernum = sernum[:-1]
+            sernum = sernum.lstrip()
+            break
+    return sernum
+
+# uses unix env to get username instead of using sysctlbyname 
+def GetUserName():
+    username = os.getenv('USER')
+    return username
+
+# Various character maps used to decrypt books. Probably supposed to act as obfuscation
+charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
+charMap2 = "ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM" 
+charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+
+def encode(data, map):
+    result = ""
+    for char in data:
+        value = ord(char)
+        Q = (value ^ 0x80) // len(map)
+        R = value % len(map)
+        result += map[Q]
+        result += map[R]
+    return result
+
+import hashlib
+
+def SHA256(message):
+    ctx = hashlib.sha256()
+    ctx.update(message)
+    return ctx.digest()
+
+# implements an Pseudo Mac Version of Windows built-in Crypto routine
+def CryptUnprotectData(encryptedData):
+    sp = GetVolumeSerialNumber() + '!@#' + GetUserName()
+    passwdData = encode(SHA256(sp),charMap1)
+    crp = LibCrypto()
+    key_iv = crp.keyivgen(passwdData)
+    key = key_iv[0:32]
+    iv = key_iv[32:48]
+    crp.set_decrypt_key(key,iv)
+    cleartext = crp.decrypt(encryptedData)
+    return cleartext
+
+# Locate and open the .kindle-info file
+def openKindleInfo():
+    home = os.getenv('HOME')
+    cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"'
+    cmdline = cmdline.encode(sys.getfilesystemencoding())
+    p1 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+    poll = p1.wait('wait')
+    results = p1.read()
+    reslst = results.split('\n')
+    kinfopath = 'NONE'
+    cnt = len(reslst)
+    for j in xrange(cnt):
+        resline = reslst[j]
+        pp = resline.find('.kindle-info')
+        if pp >= 0:
+            kinfopath = resline
+            break
+    if not os.path.exists(kinfopath):
+        raise K4MDrmException('Error: .kindle-info file can not be found')
+    return open(kinfopath,'r')
diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py
new file mode 100644 (file)
index 0000000..777376d
--- /dev/null
@@ -0,0 +1,107 @@
+# K4PC Windows specific routines
+
+from __future__ import with_statement
+
+import sys, os
+
+from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
+    create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
+    string_at, Structure, c_void_p, cast
+
+import _winreg as winreg
+
+import traceback
+
+MAX_PATH = 255
+
+kernel32 = windll.kernel32
+advapi32 = windll.advapi32
+crypt32 = windll.crypt32
+
+
+#
+# Various character maps used to decrypt books. Probably supposed to act as obfuscation
+#
+charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
+charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
+charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+
+#
+# Exceptions for all the problems that might happen during the script
+#
+class DrmException(Exception):
+    pass
+    
+
+class DataBlob(Structure):
+    _fields_ = [('cbData', c_uint),
+                ('pbData', c_void_p)]
+DataBlob_p = POINTER(DataBlob)
+
+
+def GetSystemDirectory():
+    GetSystemDirectoryW = kernel32.GetSystemDirectoryW
+    GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
+    GetSystemDirectoryW.restype = c_uint
+    def GetSystemDirectory():
+        buffer = create_unicode_buffer(MAX_PATH + 1)
+        GetSystemDirectoryW(buffer, len(buffer))
+        return buffer.value
+    return GetSystemDirectory
+GetSystemDirectory = GetSystemDirectory()
+
+def GetVolumeSerialNumber():
+    GetVolumeInformationW = kernel32.GetVolumeInformationW
+    GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
+                                      POINTER(c_uint), POINTER(c_uint),
+                                      POINTER(c_uint), c_wchar_p, c_uint]
+    GetVolumeInformationW.restype = c_uint
+    def GetVolumeSerialNumber(path = GetSystemDirectory().split('\\')[0] + '\\'):
+        vsn = c_uint(0)
+        GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
+        return str(vsn.value)
+    return GetVolumeSerialNumber
+GetVolumeSerialNumber = GetVolumeSerialNumber()
+
+
+def GetUserName():
+    GetUserNameW = advapi32.GetUserNameW
+    GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
+    GetUserNameW.restype = c_uint
+    def GetUserName():
+        buffer = create_unicode_buffer(32)
+        size = c_uint(len(buffer))
+        while not GetUserNameW(buffer, byref(size)):
+            buffer = create_unicode_buffer(len(buffer) * 2)
+            size.value = len(buffer)
+        return buffer.value.encode('utf-16-le')[::2]
+    return GetUserName
+GetUserName = GetUserName()
+
+
+def CryptUnprotectData():
+    _CryptUnprotectData = crypt32.CryptUnprotectData
+    _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
+                                   c_void_p, c_void_p, c_uint, DataBlob_p]
+    _CryptUnprotectData.restype = c_uint
+    def CryptUnprotectData(indata, entropy):
+        indatab = create_string_buffer(indata)
+        indata = DataBlob(len(indata), cast(indatab, c_void_p))
+        entropyb = create_string_buffer(entropy)
+        entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
+        outdata = DataBlob()
+        if not _CryptUnprotectData(byref(indata), None, byref(entropy),
+                                   None, None, 0, byref(outdata)):
+            raise DrmException("Failed to Unprotect Data")
+        return string_at(outdata.pbData, outdata.cbData)
+    return CryptUnprotectData
+CryptUnprotectData = CryptUnprotectData()
+
+#
+# Locate and open the Kindle.info file.
+#
+def openKindleInfo():
+    regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
+    path = winreg.QueryValueEx(regkey, 'Local AppData')[0] 
+    return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
similarity index 89%
rename from Kindle_Mobi_Tools/lib/mobidedrm.py
rename to Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py
index 07d5f6f5eca968c5492909043cc4cab7df3f1e15..5ed58a52aeafc886838f1bdce2506b8657f3ac4c 100644 (file)
 #         trailing data byte flags - version 5 and higher AND header size >= 0xE4. 
 #  0.15 - Now outputs 'hearbeat', and is also quicker for long files.
 #  0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
+#  0.17 - added modifications to support its use as an imported python module
+#         both inside calibre and also in other places (ie K4DeDRM tools)
+# 0.17a - disabled the standalone plugin feature since a plugin can not import
+#         a plugin
 
-__version__ = '0.16'
+__version__ = '0.17'
 
 import sys
 import struct
@@ -248,7 +252,42 @@ class DrmStripper:
     def getResult(self):
         return self.data_file
 
-if not __name__ == "__main__":
+def getUnencryptedBook(infile,pid):
+    sys.stdout=Unbuffered(sys.stdout)
+    data_file = file(infile, 'rb').read()
+    strippedFile = DrmStripper(data_file, pid)
+    return strippedFile.getResult()
+
+def main(argv=sys.argv):
+    sys.stdout=Unbuffered(sys.stdout)
+    print ('MobiDeDrm v%(__version__)s. '
+          'Copyright 2008-2010 The Dark Reverser.' % globals())
+    if len(argv)<4:
+        print "Removes protection from Mobipocket books"
+        print "Usage:"
+        print "    %s <infile> <outfile> <PID>" % sys.argv[0]
+        return 1
+    else:
+        infile = argv[1]
+        outfile = argv[2]
+        pid = argv[3]
+        try:
+            stripped_file = getUnencryptedBook(infile, pid)
+            file(outfile, 'wb').write(stripped_file)
+        except DrmException, e:
+            print "Error: %s" % e
+            return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
+
+#if not __name__ == "__main__":
+if False:
+
+    # note a calibre plugin can not import code with another calibre plugin
+    # in it as it ends up registering two different plugins 
     from calibre.customize import FileTypePlugin
 
     class MobiDeDRM(FileTypePlugin):
@@ -256,7 +295,7 @@ if not __name__ == "__main__":
         description         = 'Removes DRM from secure Mobi files'
         supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
         author              = 'The Dark Reverser' # The author of this plugin
-        version             = (0, 1, 6)   # The version number of this plugin
+        version             = (0, 1, 7)   # The version number of this plugin
         file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
         on_import           = True # Run this plugin during the import
 
@@ -270,41 +309,17 @@ if not __name__ == "__main__":
                 try:
                     unlocked_file = DrmStripper(data_file, i).getResult()
                 except DrmException:
-                    # ignore the error
-                    pass
+                    if is_ok_to_use_qt():
+                        d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook)
+                        d.show()
+                        d.raise_()
+                        d.exec_()
+                    raise Exception("MobiDeDRM Plugin: Error decoding ebook")
                 else:
                     of = self.temporary_file('.mobi')
                     of.write(unlocked_file)
                     of.close()
                     return of.name
-            if is_ok_to_use_qt():
-                d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Couldn't decode: %s\n\nImporting encrypted version." % path_to_ebook)
-                d.show()
-                d.raise_()
-                d.exec_()
-            return path_to_ebook
 
         def customization_help(self, gui=False):
             return 'Enter PID (separate multiple PIDs with comma)'
-
-if __name__ == "__main__":
-    sys.stdout=Unbuffered(sys.stdout)
-    print ('MobiDeDrm v%(__version__)s. '
-          'Copyright 2008-2010 The Dark Reverser.' % globals())
-    if len(sys.argv)<4:
-        print "Removes protection from Mobipocket books"
-        print "Usage:"
-        print "    %s <infile> <outfile> <PID>" % sys.argv[0]
-        sys.exit(1)
-    else:
-        infile = sys.argv[1]
-        outfile = sys.argv[2]
-        pid = sys.argv[3]
-        data_file = file(infile, 'rb').read()
-        try:
-            strippedFile = DrmStripper(data_file, pid)
-            file(outfile, 'wb').write(strippedFile.getResult())
-        except DrmException, e:
-            print "Error: %s" % e
-            sys.exit(1)
-    sys.exit(0)
\ No newline at end of file
diff --git a/Kindle_4_PC_Tools/K4PCDeDRM.pyw b/Kindle_4_PC_Tools/K4PCDeDRM.pyw
deleted file mode 100644 (file)
index 87009c5..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/env python
-# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-
-import sys
-sys.path.append('lib')
-import os, os.path, urllib
-import subprocess
-from subprocess import Popen, PIPE, STDOUT
-import subasyncio
-from subasyncio import Process
-import Tkinter
-import Tkconstants
-import tkFileDialog
-import tkMessageBox
-from scrolltextwidget import ScrolledText
-
-class MainDialog(Tkinter.Frame):
-    def __init__(self, root):
-        Tkinter.Frame.__init__(self, root, border=5)
-        self.root = root
-        self.interval = 2000
-        self.p2 = None
-        self.status = Tkinter.Label(self, text='Remove Encryption from a K4PC eBook')
-        self.status.pack(fill=Tkconstants.X, expand=1)
-        body = Tkinter.Frame(self)
-        body.pack(fill=Tkconstants.X, expand=1)
-        sticky = Tkconstants.E + Tkconstants.W
-        body.grid_columnconfigure(1, weight=2)
-
-        Tkinter.Label(body, text='K4PC eBook input file').grid(row=0, sticky=Tkconstants.E)
-        self.mobipath = Tkinter.Entry(body, width=50)
-        self.mobipath.grid(row=0, column=1, sticky=sticky)
-        cwd = os.getcwdu()
-        cwd = cwd.encode('utf-8')
-        self.mobipath.insert(0, cwd)
-        button = Tkinter.Button(body, text="...", command=self.get_mobipath)
-        button.grid(row=0, column=2)
-
-        Tkinter.Label(body, text='Name for Unencrypted Output File').grid(row=1, sticky=Tkconstants.E)
-        self.outpath = Tkinter.Entry(body, width=50)
-        self.outpath.grid(row=1, column=1, sticky=sticky)
-        self.outpath.insert(0, '')
-        button = Tkinter.Button(body, text="...", command=self.get_outpath)
-        button.grid(row=1, column=2)
-
-        msg1 = 'Conversion Log \n\n'
-        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
-        self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky)
-        self.stext.insert(Tkconstants.END,msg1)
-
-        buttons = Tkinter.Frame(self)
-        buttons.pack()
-        self.sbotton = Tkinter.Button(
-            buttons, text="Start", width=10, command=self.convertit)
-        self.sbotton.pack(side=Tkconstants.LEFT)
-
-        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
-        self.qbutton = Tkinter.Button(
-            buttons, text="Quit", width=10, command=self.quitting)
-        self.qbutton.pack(side=Tkconstants.RIGHT)
-
-    # read from subprocess pipe without blocking
-    # invoked every interval via the widget "after"
-    # option being used, so need to reset it for the next time
-    def processPipe(self):
-        poll = self.p2.wait('nowait')
-        if poll != None: 
-            text = self.p2.readerr()
-            text += self.p2.read()
-            msg = text + '\n\n' + 'Encryption successfully removed\n'
-            if poll != 0:
-                msg = text + '\n\n' + 'Error: Encryption Removal Failed\n'
-            self.showCmdOutput(msg)
-            self.p2 = None
-            self.sbotton.configure(state='normal')
-            return
-        text = self.p2.readerr()
-        text += self.p2.read()
-        self.showCmdOutput(text)
-        # make sure we get invoked again by event loop after interval 
-        self.stext.after(self.interval,self.processPipe)
-        return
-
-    # post output from subprocess in scrolled text widget
-    def showCmdOutput(self, msg):
-        if msg and msg !='':
-            msg = msg.encode('utf-8')
-            self.stext.insert(Tkconstants.END,msg)
-            self.stext.yview_pickplace(Tkconstants.END)
-        return
-
-    # run as a subprocess via pipes and collect stdout
-    def mobirdr(self, infile, outfile):
-        # os.putenv('PYTHONUNBUFFERED', '1')
-        cmdline = 'python ./lib/k4pcdedrm.py "' + infile + '" "' + outfile + '"'
-        if sys.platform[0:3] == 'win':
-            search_path = os.environ['PATH']
-            search_path = search_path.lower()
-            if search_path.find('python') >= 0: 
-                cmdline = 'python lib\k4pcdedrm.py "' + infile + '" "' + outfile + '"'
-            else :
-                cmdline = 'lib\k4pcdedrm.py "' + infile + '" "' + outfile + '"'
-
-        cmdline = cmdline.encode(sys.getfilesystemencoding())
-        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
-        return p2
-
-
-    def get_mobipath(self):
-        mobipath = tkFileDialog.askopenfilename(
-            parent=None, title='Select K4PC eBook File',
-            defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),
-                                                ('All Files', '.*')])
-        if mobipath:
-            mobipath = os.path.normpath(mobipath)
-            self.mobipath.delete(0, Tkconstants.END)
-            self.mobipath.insert(0, mobipath)
-        return
-
-    def get_outpath(self):
-        mobipath = self.mobipath.get()
-        initname = os.path.basename(mobipath)
-        p = initname.find('.')
-        if p >= 0: initname = initname[0:p]
-        initname += '_nodrm.mobi' 
-        outpath = tkFileDialog.asksaveasfilename(
-            parent=None, title='Select Unencrypted Mobi File to produce',
-            defaultextension='.mobi', initialfile=initname,
-            filetypes=[('Mobi files', '.mobi'), ('All files', '.*')])
-        if outpath:
-            outpath = os.path.normpath(outpath)
-            self.outpath.delete(0, Tkconstants.END)
-            self.outpath.insert(0, outpath)
-        return
-
-    def quitting(self):
-        # kill any still running subprocess
-        if self.p2 != None:
-            if (self.p2.wait('nowait') == None):
-                self.p2.terminate()
-        self.root.destroy()
-
-    # actually ready to run the subprocess and get its output
-    def convertit(self):
-        # now disable the button to prevent multiple launches
-        self.sbotton.configure(state='disabled')
-        mobipath = self.mobipath.get()
-        outpath = self.outpath.get()
-        if not mobipath or not os.path.exists(mobipath):
-            self.status['text'] = 'Specified K4PC eBook file does not exist'
-            self.sbotton.configure(state='normal')
-            return
-        if not outpath:
-            self.status['text'] = 'No output file specified'
-            self.sbotton.configure(state='normal')
-            return
-
-        log = 'Command = "python k4pcdedrm.py"\n'
-        log += 'K4PC Path = "'+ mobipath + '"\n'
-        log += 'Output File = "' + outpath + '"\n'
-        log += '\n\n'
-        log += 'Please Wait ...\n\n'
-        log = log.encode('utf-8')
-        self.stext.insert(Tkconstants.END,log)
-        self.p2 = self.mobirdr(mobipath, outpath)
-
-        # python does not seem to allow you to create
-        # your own eventloop which every other gui does - strange 
-        # so need to use the widget "after" command to force
-        # event loop to run non-gui events every interval
-        self.stext.after(self.interval,self.processPipe)
-        return
-
-
-def main(argv=None):
-    root = Tkinter.Tk()
-    root.title('K4PC eBook Encryption Removal')
-    root.resizable(True, False)
-    root.minsize(300, 0)
-    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
-    root.mainloop()
-    return 0
-    
-
-if __name__ == "__main__":
-    sys.exit(main())
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp
deleted file mode 100644 (file)
index 535df90..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#include <cstdlib>
-#include <iostream>
-#include <conio.h>
-#include <fstream>
-
-using namespace std;
-
-int main(int argc, char *argv[])
-{
-// Variables
-       int  TopazTrue    = 0;
-       int  strlength    = 0;
-       char uinfile[80];
-       char outfile[80];
-       char command[80];
-       char buffer[80];
-
-// String initialization
-       strcpy(uinfile,"");
-       strcpy(outfile,"");
-       strcpy(buffer,"");
-       strcpy(command,"skindle ");          // string preloaded with "skindle "
-       
-       
-    cout << "\n\n\n     Please enter the name of the book to be converted:\n\n     ";
-    cout << "     Don't forget the prc file extension!\n\n     ";
-    cout << "     Watch out for zeros and Os. Zeros are skinny and Os are fat.\n\n\n     ";
-
-      cin >> uinfile;                             // get file name of the book to be converted from user
-
-
-         ifstream infile(uinfile);
-         infile.getline(buffer,4);
-
-
-         if (strncmp (buffer,"TPZ",3)==0)      // open file and test first 3 char if TPZ then book is topaz
-         { 
-                TopazTrue = 1;                             // This is a Topaz file
-         }
-
-
-         strlength = strlen(uinfile);
-
-         if(strlength > 13)
-           {
-         strncat(outfile,uinfile,10);                // Create output file name using first 10 char of input file name
-           }
-         else
-           {
-              strncat(outfile,uinfile, (strlength - 4));  // If file name is less than 10 characters
-           }
-         if(TopazTrue == 1)                         // This is Topaz Book
-                {
-                                strcat(command,"-d ");          // Add the topaz switch to the command line
-
-                                strcat(outfile,".tpz");         // give tpz file extension to topaz output file
-                } // end of TopazTrue
-         else
-         {
-                 strcat(outfile,".azw"); 
-         } // if not Topaz make it azw
-
-         strcat(command,"-i ");                     // Add the input switch to the command line
-         strcat(command,uinfile);                    // add the input file name to the command line
-         strcat(command," -o ");                    // add the output switch to the command line
-         strcat(command,outfile);                   // Add the output file name to the command line
-
-       cout << "\n\n   The skindle program is called here.\n";
-       cout << "   Any errors reported between here and \"The command line used was:\"\n";
-       cout << "   Are errors from the skindle program. Not EZskindle4PC.\n\n";
-         
-
-        system(command);                            // call skindle program to convert the book
-         
-         
-       cout << "\n\n   The command line used was:\n\n";
-       cout << " " << command << "\n";
-       cout << "\n\n\n   Please note the output file is created from the input";
-       cout << "\n   file name. The file extension is changed to tpz for Topaz";
-       cout << "\n   files and to azw for non-Topaz files. Also, _EBOK is removed ";
-       cout << "\n   from the file name.  This is to make it eaiser to identify ";
-    cout << "\n   the file with no DRM.";
-
-       
-
-    system("PAUSE");
-    return EXIT_SUCCESS;
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe
deleted file mode 100644 (file)
index b40343c..0000000
Binary files a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe and /dev/null differ
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt
deleted file mode 100644 (file)
index 59d97e0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-LZskindle4PCv1_1    The Lazy skindle program for those who are typing impared
-
-To setup:
-
-1.  Create a new folder:   example C:\skindle
-
-2.  Place LZskindle4PCv1_1.exe and skindle.exe in this folder.
-
-3.  Create 2 subfolders:    C:\skindle\input 
-    and                     C:\skindle\output
-
-
-To run:
-
-1.  Copy the book(s) you wish to remove DRM from into the input directory 
-(leave the originals in my kindle folder).
-
-2.  Double click on LZskindle4PCv1_0.exe
-
-3.  A DOS window will open and will show skindle being called for 
-each book in the input directory.
-
-4.  The books with the DRM removed will now be in the output directory.
-
-Rev1_1
-
-fixed program to allow any file extension.  My testing indicates that 
-skindle does not care what file extension a file has.  If it is a file type
-that it can convert it will.  If the file is not compatible it will close
-and give an unknown file type message.
-
-Rev1_0
-
-If the program is run with no files in the input directory you will get a 
-\93File not Found\94 error and the program will terminate.
-
-PLEASE USE ONLY FOR YOUR PERSONAL USE \96 ON BOOKS YOU PAID FOR!!
-
-This program is provided to allow you to archive your library in a format that 
-will outlast the kindle.  Technology moves on and you should not have to reinvest 
-in an entire new library when the Kindle is obsolete.  Please do not use this program 
-for piracy.
-
-
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp
deleted file mode 100644 (file)
index 1adaa93..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#include <cstdlib>
-#include <iostream>
-#include <fstream>
-//#include <conio.h>
-
-using namespace std;
-
-int main(int argc, char *argv[])
-{
-    // Variable Declarations ??
-    char buffer[80];
-    int error = 0;
-//    int YesNo = 0;
-//    int exit  = 0;
- // Variables  EZskindle4PC 
-       int  TopazTrue    = 0;
-       int  strlength    = 0;
-       char uinfile[80];
-       char outfile[80];
-       char command[80];
-       char buffer2[20];
-       char tempfile[80];
-   
-  // Initialize strings
-       strcpy(uinfile,"");
-       strcpy(outfile,"");
-       strcpy(buffer,"");
-       strcpy(buffer2,"");
-       strcpy(command,"skindle ");          // string preloaded with "skindle "
-
-      
-   //// Beginning of program code //////////////////////////////////////////////////////////// 
-
-    system("dir /b .\\input\\*.* > books.txt");  // Create txt file with list of books
-                                                 // No testing of file type being done
-                                                 // I am letting skindle determing if valid 
-                                                 // file type
-    //  Read in the list of book file names
-    
-   ifstream infile("books.txt");   
-
-    do  // while not end of file
-      {
-        infile.getline(buffer,50);  // load the first 50 characters of the line to buffer
-        
-        if(strcmp(buffer, "")!= 0)  // If there is file name in the buffer do this on last loop buffer will be empty
-         {
-            strcpy(uinfile,buffer);      // load file name from buffer
-
-               strcpy(tempfile,".\\input\\");  // load directory name for input files
-            strcat(tempfile,buffer);        // load the file name
-               ifstream infile2(tempfile);     // open the book file for reading
-               infile2.getline(buffer2,4);     // load first 4 char from file
-
-            infile2.close();    // close the book file
-
-
-               if (strncmp (buffer2,"TPZ",3)==0)      // open file and test first 3 char if TPZ then book is topaz
-                 { 
-                        TopazTrue = 1;                             // This is a Topaz file
-                 }
-
-
-           strlength = strlen(uinfile);
-
-              if(strlength > 13)
-                {
-               strncat(outfile,uinfile,10);                // Create output file name using first 10 char of input file name
-                }
-               else
-                 {
-                    strncat(outfile,uinfile, (strlength - 4));  // If file name is less than 10 characters
-                 }
-             if(TopazTrue == 1)                         // This is Topaz Book
-                {
-                                strcat(command,"-d ");          // Add the topaz switch to the command line
-
-                                strcat(outfile,".tpz");         // give tpz file extension to topaz output file
-                } // end of TopazTrue
-              else
-               {
-                      strcat(outfile,".azw"); 
-               } // if not Topaz make it azw
-
-         strcat(command,"-i ");                     // Add the input switch to the command line
-         strcat(command,".\\input\\");              // Add the input directory to the command line
-            strcat(command,uinfile);                   // add the input file name to the command line
-            strcat(command," -o ");                    // add the output switch to the command line
-            strcat(command,".\\output\\");             // Add directory for out files
-            strcat(command,outfile);                   // Add the output file name to the command line
-
-            cout << "\n\n   The skindle program is called here.\n";
-            cout << "   Any errors reported between here and \"The command line used was:\"\n";
-            cout << "   Are errors from the skindle program. Not EZskindle4PC.\n\n";
-         
-
-            system(command);                            // call skindle program to convert the book
-         
-         
-            cout << "\n\n   The command line used was:\n\n";
-            cout << " " << command << "\n\n\n\n";
-
-
-       }// end of file name in the buffer required to prevent execution on EOF
-
-
-          strcpy(command,"skindle ");          // reset strings and variables for next book
-          strcpy(outfile,"");
-          strcpy(uinfile,"");
-          strcpy(buffer,"");
-          strcpy(buffer2,"");
-          TopazTrue = 0;
-          strlength = 0;
-
-      }while (! infile.eof() );  // no more books in the file
-    
-    infile.close();    // close books.txt
-      
-
-//    cout << "\n\n\n Do you want to delete all of the books from the input directory?\n\n";  
-//    cout << " DO NOT DELETE IF THESE ARE ONLY COPY OF YOUR BOOKS!!!!\n\n";
-//    cout << " Y or N: ";
-
-
-//    do {  // while not yes or no
-//          YesNo = getch();           // This is a DOS/Windows console command not standard C may not be
-//                                     // Usable under Unix or Mac implementations 
-//             
-//             if((YesNo == 121)||(YesNo == 89))  // y or Y is true
-//               {
-//                 exit = 1;  // valid input exit do while loop
-//                cout << "\n\n";
-//                 system("del .\\input\\*.*");   // delete everything in the input directory      
-//                 cout << "\n\n";
-//               }
-//             if((YesNo == 110)||(YesNo == 78))  // n or N is true
-//               {
-//                 exit = 1;  // valid input exit do while loop      
-//               }
-//       
-//       }while (exit != 1);
-//    cout << "\n\nYesNo = " << YesNo << "\n\n";
-
-    system("PAUSE");
-    
-    system("del books.txt");  // Delete txt file with list of books
-    return EXIT_SUCCESS;
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe
deleted file mode 100644 (file)
index d6e3a8f..0000000
Binary files a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe and /dev/null differ
diff --git a/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/ReadMe.txt b/Kindle_4_PC_Tools/Other_Tools/LZskindle4PCv1_1/ReadMe.txt
deleted file mode 100644 (file)
index 40989fd..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-Ezskindle4PC.exe 
-
-This executable program makes using skindle easier for people using Windows PC\92s.
-
-I do not know if it will work under any other operating system, however, I have included 
-the source code should anyone want to port it into other operating systems.
-
-To use this program:
-
-1. Copy the ezskindle4PC.exe into the same directory with the skindle files.
-2. Copy the kindle book into the same directory.
-3. double click the EZskindle4PCv1_0.exe file.
-a. A DOS window will open and you will be asked for the name of the file you want to work with.
-4. Type in the book\92s file name.  (it will look something like B000WCTBTA_EBOK.prc)
-5. The program will then check if it is a Topaz file and then create the output file name using the 
-first part of the input file name. It will use \93tpz\94 file extension for Topaz books and will use \93azw\94 
-for non topaz books.  The files with the \93azw\94 format can be converted to other ebook formats using 
-Calibre.  If you want to convert Topaz books to other formats you need to use Topaz tools not skindle.
-6. The program will then create a command line and call the skindle program to process the book and 
-remove the DRM.
-7. The program will pause and allow you to see the result of the skindle process.  
-8. Press any key to close the program.
-
-version 1.1
-Ok
-
-Found a new 32 bit compiler and I think I have worked out the kinks.
diff --git a/Kindle_4_PC_Tools/Other_Tools/README_Other_K4PC_Tools.txt b/Kindle_4_PC_Tools/Other_Tools/README_Other_K4PC_Tools.txt
deleted file mode 100644 (file)
index ff1513c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Readme.txt
-
-- LZskindle4PC is a front end for running skindle that makes it easier for some people who do not like command lines to use it.
-
-- skindle has completely reverse engineered how the book specific PID is generated for Kindle4PC so it can be very useful when new version of Kindle4PC are released and unswindle has not yet been updated.  Unfortunately, skindle has some minor bugs that can actually result in corrupted eBooks.  No one has yet tracked them down and fixed them.  Until they do, use at your own risk.
-
-- unswindle can be used to find the book specific PID but it needs to be updated for each version of Kindle4PC that Amazon releases (and therefore is also useful for Linux users who have Wine). This program “patches” the Kindle4PC executable and therefore is very release specific.
-Unfortunately unswindle v7 the latest, has not been updated to work with the latest version of Kindle for PC.  You will need to find one of the older versions of Kindle4PC and prevent later updates in order to use this tool.
-
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/Makefile b/Kindle_4_PC_Tools/Other_Tools/skindle-06/Makefile
deleted file mode 100644 (file)
index d4f6938..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-
-OBJS=skindle.o md5.o sha1.o b64.o skinutils.o cbuf.o mobi.o tpz.o
-
-CC=gcc
-LD=gcc
-EXE=skindle
-EXTRALIBS=libz.a -lCrypt32 -lAdvapi32
-CFLAGS=-mno-cygwin
-
-#use the following to strip your binary
-LDFLAGS=-s -mno-cygwin
-#LDFLAGS=-mno-cygwin
-
-all: $(EXE)
-
-%.o: %.c
-       $(CC) -c $(CFLAGS) -g $(INC) $< -o $@
-
-$(EXE): $(OBJS)
-       $(LD) $(LDFLAGS) -o $@ -g $(OBJS) $(EXTRALIBS)
-
-clean:
-       -@rm *.o
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/README b/Kindle_4_PC_Tools/Other_Tools/skindle-06/README
deleted file mode 100644 (file)
index bf0a889..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-/*
- * Dependencies: zlib (included)
- * build on cygwin using make and the included make file
- * A fully functionaly windows executable is included
- */
-
-/*
- * MUST be run on the computer on which KindleForPC is installed
- * under the account that was used to purchase DRM'ed content.
- * Requires your kindle.info file which can be found in something like:
- * <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}
- * where ... varies by platform but is "Local Settings\Application Data" on XP
- * skindle will attempt to find this file automatically.
- */
-
-/*
-  What: KindleForPC DRM removal utility to preserve your fair use rights!
-  Why: Fair use is a well established doctrine, and I am no fan of vendor
-       lockin.
-  How: This utility implements the PID extraction, DRM key generation and
-       decryption algorithms employed by the KindleForPC application. This
-       is a stand alone app that does not require you to determine a PID on
-       your own, and it does not need to run KindleForPC in order to extract
-       any data from memory.
-  
-  Shoutz: The DarkReverser - thanks for mobidedrm!  The last part of this
-       is just a C port of mobidedrm.
-       labba and I<3cabbages for motivating me to do this the right way.
-       You guys shouldn't need to spend all your time responding to all the
-       changes Amazon is going to force you to make in unswindle each time
-       the release a new version.
-       CMBDTC - nice work on the topaz break!
-       Lawrence Lessig - You are my hero. 'Nuff said.
-       Cory Doctorow - A voice of reason in a sea of insanity
-   Thumbs down: Disney, MPAA, RIAA - you guys suck.  Making billions off 
-       of the exploitation of works out of copyright while vigourously
-       pushing copyright extension to prevent others from doing the same
-       is the height of hypocrasy.
-       Congress - you guys suck too.  Why you arrogant pricks think you
-       are smarter than the founding fathers is beyond me.
- */
-
-Rationale:
-Need a tool to enable fair use of purchased ebook content.
-Need a tool that is not dependent on any particular version of
-KindleForPC and that does not need to run KindleForPC in order to
-extract a PID. The tool documents the structure of the kindle.info
-file and the data and algorthims that are used to derive per book
-PID values.
-
-Installing:
-A compiled binary is included.  Though it was built using cygwin, it
-should not require a cygwin installation in order to run it.  To build
-from source, you will need cygwin with gcc and make.  
-This has not been tested with Visual Studio, though you may be able to
-pile all the files into a project and add the Crypt32.lib, Advapi32 and
-zlib1 dependencies to build it.
-
-usage: ./skindle [-d] [-v] -i <ebook file> -o <output file> [-k kindle.info file] [-p pid]
-    -d optional, for topaz files only, produce a decompressed output file
-    -i required name of the input mobi or topaz file
-    -o required name of the output file to generate
-    -k optional kindle.info path
-    -v dump the contents of kindle.info
-    -p additional PID values to attempt (can specifiy multiple times)
-
-You only need to specify a kindle.info path if skindle can't find
-your kindle.info file automatically
\ No newline at end of file
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/b64.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/b64.c
deleted file mode 100644 (file)
index 74c82d2..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*********************************************************************\
-LICENCE:        Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
-
-                Permission is hereby granted, free of charge, to any person
-                obtaining a copy of this software and associated
-                documentation files (the "Software"), to deal in the
-                Software without restriction, including without limitation
-                the rights to use, copy, modify, merge, publish, distribute,
-                sublicense, and/or sell copies of the Software, and to
-                permit persons to whom the Software is furnished to do so,
-                subject to the following conditions:
-
-                The above copyright notice and this permission notice shall
-                be included in all copies or substantial portions of the
-                Software.
-
-                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-                KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-                WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-                PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
-                OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-                OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-                SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-VERSION HISTORY:
-                Bob Trower 08/04/01 -- Create Version 0.00.00B
-
-\******************************************************************* */
-
-#include <stdlib.h>
-
-/*
-** Translation Table as described in RFC1113
-*/
-static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/*
-** encodeblock
-**
-** encode 3 8-bit binary bytes as 4 '6-bit' characters
-*/
-void encodeblock(unsigned char in[3], unsigned char out[4], int len) {
-    out[0] = cb64[ in[0] >> 2 ];
-    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
-    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
-    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
-}
-
-/*
-** encode
-**
-** base64 encode a stream adding padding and line breaks as per spec.
-*/
-unsigned int base64(unsigned char *inbuf, unsigned int len, unsigned char *outbuf) {
-   unsigned char in[3], out[4];
-   int c;
-   unsigned int i = 0;
-   unsigned int outlen = 0;
-   while (i < len) {
-      int n = 0;
-      for(c = 0; c < 3; c++, i++) {
-         if (i < len) {
-            in[c] = inbuf[i];
-            n++;
-         }
-         else {
-            in[c] = 0;
-         }
-      }
-      if (n) {
-         encodeblock(in, out, n);
-         for(c = 0; c < 4; c++) {
-            outbuf[outlen++] = out[c];
-         }
-      }
-   }
-   return outlen;
-}
-
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.c
deleted file mode 100644 (file)
index 60112e2..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#include <stdlib.h>
-#include <string.h>
-#include "cbuf.h"
-
-cbuf *b_new(unsigned int size) {
-   cbuf *b = (cbuf*)calloc(sizeof(cbuf), 1);
-   if (b) {
-      b->buf = (unsigned char *)malloc(size);
-      b->size = b->buf ? size : 0;
-   }
-   return b;
-}
-
-void b_free(cbuf *b) {
-   if (b) {
-      free(b->buf);
-      free(b);
-   }
-}
-
-void b_add_byte(cbuf *b, unsigned char ch) {
-   if (b == NULL) return;
-   if (b->idx == b->size) {
-      unsigned char *p = realloc(b->buf, b->size * 2);
-      if (p) {
-         b->buf = p;
-         b->size = b->size * 2;
-      }
-   }
-   if (b->idx < b->size) {
-      b->buf[b->idx++] = ch;
-   }
-}
-
-void b_add_buf(cbuf *b, unsigned char *buf, unsigned int len) {
-   if (b == NULL) return;
-   unsigned int new_sz = b->idx + len;
-   while (b->size < new_sz) {
-      unsigned char *p = realloc(b->buf, b->size * 2);
-      if (p) {
-         b->buf = p;
-         b->size = b->size * 2;
-      }
-      else break;
-   }
-   if ((b->idx + len) <= b->size) {
-      memcpy(b->buf + b->idx, buf, len);
-      b->idx += len;
-   }
-}
-
-void b_add_str(cbuf *b, const char *buf) {
-   if (b == NULL) return;
-   unsigned int len = strlen(buf);
-   unsigned int new_sz = b->idx + len;
-   while (b->size < new_sz) {
-      unsigned char *p = realloc(b->buf, b->size * 2);
-      if (p) {
-         b->buf = p;
-         b->size = b->size * 2;
-      }
-      else break;
-   }
-   if ((b->idx + len) <= b->size) {
-      memcpy(b->buf + b->idx, buf, len);
-      b->idx += len;
-   }
-}
-
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/cbuf.h
deleted file mode 100644 (file)
index 738dfd2..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#ifndef __CBUF_H
-#define __CBUF_H
-
-typedef struct _cbuf {
-   unsigned int size;  //current size
-   unsigned int idx;   //current position
-   unsigned char *buf;
-} cbuf;
-
-cbuf *b_new(unsigned int size);
-void b_free(cbuf *b);
-void b_add_byte(cbuf *b, unsigned char ch);
-void b_add_buf(cbuf *b, unsigned char *buf, unsigned int len);
-void b_add_str(cbuf *b, const char *buf);
-
-#endif
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/libz.a b/Kindle_4_PC_Tools/Other_Tools/skindle-06/libz.a
deleted file mode 100644 (file)
index 0a2e3b5..0000000
Binary files a/Kindle_4_PC_Tools/Other_Tools/skindle-06/libz.a and /dev/null differ
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.c
deleted file mode 100644 (file)
index c35d96c..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
-  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  L. Peter Deutsch
-  ghost@aladdin.com
-
- */
-/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
-/*
-  Independent implementation of MD5 (RFC 1321).
-
-  This code implements the MD5 Algorithm defined in RFC 1321, whose
-  text is available at
-       http://www.ietf.org/rfc/rfc1321.txt
-  The code is derived from the text of the RFC, including the test suite
-  (section A.5) but excluding the rest of Appendix A.  It does not include
-  any code or documentation that is identified in the RFC as being
-  copyrighted.
-
-  The original and principal author of md5.c is L. Peter Deutsch
-  <ghost@aladdin.com>.  Other authors are noted in the change history
-  that follows (in reverse chronological order):
-
-  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
-       either statically or dynamically; added missing #include <string.h>
-       in library.
-  2002-03-11 lpd Corrected argument list for main(), and added int return
-       type, in test program and T value program.
-  2002-02-21 lpd Added missing #include <stdio.h> in test program.
-  2000-07-03 lpd Patched to eliminate warnings about "constant is
-       unsigned in ANSI C, signed in traditional"; made test program
-       self-checking.
-  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
-  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
-  1999-05-03 lpd Original version.
- */
-
-#include "md5.h"
-#include <string.h>
-
-#undef BYTE_ORDER      /* 1 = big-endian, -1 = little-endian, 0 = unknown */
-#ifdef ARCH_IS_BIG_ENDIAN
-#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
-#else
-#  define BYTE_ORDER 0
-#endif
-
-#define T_MASK ((md5_word_t)~0)
-#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
-#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
-#define T3    0x242070db
-#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
-#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
-#define T6    0x4787c62a
-#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
-#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
-#define T9    0x698098d8
-#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
-#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
-#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
-#define T13    0x6b901122
-#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
-#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
-#define T16    0x49b40821
-#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
-#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
-#define T19    0x265e5a51
-#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
-#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
-#define T22    0x02441453
-#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
-#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
-#define T25    0x21e1cde6
-#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
-#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
-#define T28    0x455a14ed
-#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
-#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
-#define T31    0x676f02d9
-#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
-#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
-#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
-#define T35    0x6d9d6122
-#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
-#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
-#define T38    0x4bdecfa9
-#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
-#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
-#define T41    0x289b7ec6
-#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
-#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
-#define T44    0x04881d05
-#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
-#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
-#define T47    0x1fa27cf8
-#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
-#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
-#define T50    0x432aff97
-#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
-#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
-#define T53    0x655b59c3
-#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
-#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
-#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
-#define T57    0x6fa87e4f
-#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
-#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
-#define T60    0x4e0811a1
-#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
-#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
-#define T63    0x2ad7d2bb
-#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
-
-
-static void
-md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
-{
-    md5_word_t
-       a = pms->abcd[0], b = pms->abcd[1],
-       c = pms->abcd[2], d = pms->abcd[3];
-    md5_word_t t;
-#if BYTE_ORDER > 0
-    /* Define storage only for big-endian CPUs. */
-    md5_word_t X[16];
-#else
-    /* Define storage for little-endian or both types of CPUs. */
-    md5_word_t xbuf[16];
-    const md5_word_t *X;
-#endif
-
-    {
-#if BYTE_ORDER == 0
-       /*
-        * Determine dynamically whether this is a big-endian or
-        * little-endian machine, since we can use a more efficient
-        * algorithm on the latter.
-        */
-       static const int w = 1;
-
-       if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
-#endif
-#if BYTE_ORDER <= 0            /* little-endian */
-       {
-           /*
-            * On little-endian machines, we can process properly aligned
-            * data without copying it.
-            */
-           if (!((data - (const md5_byte_t *)0) & 3)) {
-               /* data are properly aligned */
-               X = (const md5_word_t *)data;
-           } else {
-               /* not aligned */
-               memcpy(xbuf, data, 64);
-               X = xbuf;
-           }
-       }
-#endif
-#if BYTE_ORDER == 0
-       else                    /* dynamic big-endian */
-#endif
-#if BYTE_ORDER >= 0            /* big-endian */
-       {
-           /*
-            * On big-endian machines, we must arrange the bytes in the
-            * right order.
-            */
-           const md5_byte_t *xp = data;
-           int i;
-
-#  if BYTE_ORDER == 0
-           X = xbuf;           /* (dynamic only) */
-#  else
-#    define xbuf X             /* (static only) */
-#  endif
-           for (i = 0; i < 16; ++i, xp += 4)
-               xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
-       }
-#endif
-    }
-
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
-
-    /* Round 1. */
-    /* Let [abcd k s i] denote the operation
-       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
-#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + F(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-    /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  7,  T1);
-    SET(d, a, b, c,  1, 12,  T2);
-    SET(c, d, a, b,  2, 17,  T3);
-    SET(b, c, d, a,  3, 22,  T4);
-    SET(a, b, c, d,  4,  7,  T5);
-    SET(d, a, b, c,  5, 12,  T6);
-    SET(c, d, a, b,  6, 17,  T7);
-    SET(b, c, d, a,  7, 22,  T8);
-    SET(a, b, c, d,  8,  7,  T9);
-    SET(d, a, b, c,  9, 12, T10);
-    SET(c, d, a, b, 10, 17, T11);
-    SET(b, c, d, a, 11, 22, T12);
-    SET(a, b, c, d, 12,  7, T13);
-    SET(d, a, b, c, 13, 12, T14);
-    SET(c, d, a, b, 14, 17, T15);
-    SET(b, c, d, a, 15, 22, T16);
-#undef SET
-
-     /* Round 2. */
-     /* Let [abcd k s i] denote the operation
-          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
-#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + G(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  1,  5, T17);
-    SET(d, a, b, c,  6,  9, T18);
-    SET(c, d, a, b, 11, 14, T19);
-    SET(b, c, d, a,  0, 20, T20);
-    SET(a, b, c, d,  5,  5, T21);
-    SET(d, a, b, c, 10,  9, T22);
-    SET(c, d, a, b, 15, 14, T23);
-    SET(b, c, d, a,  4, 20, T24);
-    SET(a, b, c, d,  9,  5, T25);
-    SET(d, a, b, c, 14,  9, T26);
-    SET(c, d, a, b,  3, 14, T27);
-    SET(b, c, d, a,  8, 20, T28);
-    SET(a, b, c, d, 13,  5, T29);
-    SET(d, a, b, c,  2,  9, T30);
-    SET(c, d, a, b,  7, 14, T31);
-    SET(b, c, d, a, 12, 20, T32);
-#undef SET
-
-     /* Round 3. */
-     /* Let [abcd k s t] denote the operation
-          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + H(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  5,  4, T33);
-    SET(d, a, b, c,  8, 11, T34);
-    SET(c, d, a, b, 11, 16, T35);
-    SET(b, c, d, a, 14, 23, T36);
-    SET(a, b, c, d,  1,  4, T37);
-    SET(d, a, b, c,  4, 11, T38);
-    SET(c, d, a, b,  7, 16, T39);
-    SET(b, c, d, a, 10, 23, T40);
-    SET(a, b, c, d, 13,  4, T41);
-    SET(d, a, b, c,  0, 11, T42);
-    SET(c, d, a, b,  3, 16, T43);
-    SET(b, c, d, a,  6, 23, T44);
-    SET(a, b, c, d,  9,  4, T45);
-    SET(d, a, b, c, 12, 11, T46);
-    SET(c, d, a, b, 15, 16, T47);
-    SET(b, c, d, a,  2, 23, T48);
-#undef SET
-
-     /* Round 4. */
-     /* Let [abcd k s t] denote the operation
-          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + I(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  6, T49);
-    SET(d, a, b, c,  7, 10, T50);
-    SET(c, d, a, b, 14, 15, T51);
-    SET(b, c, d, a,  5, 21, T52);
-    SET(a, b, c, d, 12,  6, T53);
-    SET(d, a, b, c,  3, 10, T54);
-    SET(c, d, a, b, 10, 15, T55);
-    SET(b, c, d, a,  1, 21, T56);
-    SET(a, b, c, d,  8,  6, T57);
-    SET(d, a, b, c, 15, 10, T58);
-    SET(c, d, a, b,  6, 15, T59);
-    SET(b, c, d, a, 13, 21, T60);
-    SET(a, b, c, d,  4,  6, T61);
-    SET(d, a, b, c, 11, 10, T62);
-    SET(c, d, a, b,  2, 15, T63);
-    SET(b, c, d, a,  9, 21, T64);
-#undef SET
-
-     /* Then perform the following additions. (That is increment each
-        of the four registers by the value it had before this block
-        was started.) */
-    pms->abcd[0] += a;
-    pms->abcd[1] += b;
-    pms->abcd[2] += c;
-    pms->abcd[3] += d;
-}
-
-void
-md5_init(md5_state_t *pms)
-{
-    pms->count[0] = pms->count[1] = 0;
-    pms->abcd[0] = 0x67452301;
-    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
-    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
-    pms->abcd[3] = 0x10325476;
-}
-
-void
-md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
-{
-    const md5_byte_t *p = data;
-    int left = nbytes;
-    int offset = (pms->count[0] >> 3) & 63;
-    md5_word_t nbits = (md5_word_t)(nbytes << 3);
-
-    if (nbytes <= 0)
-       return;
-
-    /* Update the message length. */
-    pms->count[1] += nbytes >> 29;
-    pms->count[0] += nbits;
-    if (pms->count[0] < nbits)
-       pms->count[1]++;
-
-    /* Process an initial partial block. */
-    if (offset) {
-       int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
-
-       memcpy(pms->buf + offset, p, copy);
-       if (offset + copy < 64)
-           return;
-       p += copy;
-       left -= copy;
-       md5_process(pms, pms->buf);
-    }
-
-    /* Process full blocks. */
-    for (; left >= 64; p += 64, left -= 64)
-       md5_process(pms, p);
-
-    /* Process a final partial block. */
-    if (left)
-       memcpy(pms->buf, p, left);
-}
-
-void
-md5_finish(md5_state_t *pms, md5_byte_t digest[16])
-{
-    static const md5_byte_t pad[64] = {
-       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-    };
-    md5_byte_t data[8];
-    int i;
-
-    /* Save the length before padding. */
-    for (i = 0; i < 8; ++i)
-       data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
-    /* Pad to 56 bytes mod 64. */
-    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
-    /* Append the length. */
-    md5_append(pms, data, 8);
-    for (i = 0; i < 16; ++i)
-       digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/md5.h
deleted file mode 100644 (file)
index 698c995..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  L. Peter Deutsch
-  ghost@aladdin.com
-
- */
-/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
-/*
-  Independent implementation of MD5 (RFC 1321).
-
-  This code implements the MD5 Algorithm defined in RFC 1321, whose
-  text is available at
-       http://www.ietf.org/rfc/rfc1321.txt
-  The code is derived from the text of the RFC, including the test suite
-  (section A.5) but excluding the rest of Appendix A.  It does not include
-  any code or documentation that is identified in the RFC as being
-  copyrighted.
-
-  The original and principal author of md5.h is L. Peter Deutsch
-  <ghost@aladdin.com>.  Other authors are noted in the change history
-  that follows (in reverse chronological order):
-
-  2002-04-13 lpd Removed support for non-ANSI compilers; removed
-       references to Ghostscript; clarified derivation from RFC 1321;
-       now handles byte order either statically or dynamically.
-  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
-  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
-       added conditionalization for C++ compilation from Martin
-       Purschke <purschke@bnl.gov>.
-  1999-05-03 lpd Original version.
- */
-
-#ifndef md5_INCLUDED
-#  define md5_INCLUDED
-
-/*
- * This package supports both compile-time and run-time determination of CPU
- * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
- * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
- * defined as non-zero, the code will be compiled to run only on big-endian
- * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
- * run on either big- or little-endian CPUs, but will run slightly less
- * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
- */
-
-typedef unsigned char md5_byte_t; /* 8-bit byte */
-typedef unsigned int md5_word_t; /* 32-bit word */
-
-/* Define the state of the MD5 Algorithm. */
-typedef struct md5_state_s {
-    md5_word_t count[2];       /* message length in bits, lsw first */
-    md5_word_t abcd[4];                /* digest buffer */
-    md5_byte_t buf[64];                /* accumulate block */
-} md5_state_t;
-
-#ifdef __cplusplus
-extern "C" 
-{
-#endif
-
-/* Initialize the algorithm. */
-void md5_init(md5_state_t *pms);
-
-/* Append a string to the message. */
-void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
-
-/* Finish the message and return the digest. */
-void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
-
-#ifdef __cplusplus
-}  /* end extern "C" */
-#endif
-
-#endif /* md5_INCLUDED */
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.c
deleted file mode 100644 (file)
index 25de87c..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "mobi.h"
-
-unsigned char *getExthData(MobiFile *book, unsigned int type, unsigned int *len) {
-   unsigned int i;
-   unsigned int exthRecords = bswap_l(book->exth->recordCount);
-   ExthRecHeader *erh = book->exth->records;
-
-   *len = 0;
-
-   for (i = 0; i < exthRecords; i++) {
-      unsigned int recType = bswap_l(erh->type);
-      unsigned int recLen = bswap_l(erh->len);
-
-      if (recLen < 8) {
-         fprintf(stderr, "Invalid exth record length %d\n", i);
-         return NULL;
-      }
-
-      if (recType == type) {
-         *len = recLen - 8;
-         return (unsigned char*)(erh + 1);
-      }
-      erh = (ExthRecHeader*)(recLen  + (char*)erh);
-   }
-   return NULL;
-}
-
-void enumExthRecords(ExthHeader *eh) {
-   unsigned int exthRecords = bswap_l(eh->recordCount);
-   unsigned int i;
-   unsigned char *data;
-   ExthRecHeader *erh = eh->records;
-
-   for (i = 0; i < exthRecords; i++) {
-      unsigned int recType = bswap_l(erh->type);
-      unsigned int recLen = bswap_l(erh->len);
-
-      fprintf(stderr, "%d: type - %d, len %d\n", i, recType, recLen);
-
-      if (recLen < 8) {
-         fprintf(stderr, "Invalid exth record length %d\n", i);
-         return;
-      }
-
-      data = (unsigned char*)(erh + 1);
-      switch (recType) {
-         case 1: //drm_server_id
-            fprintf(stderr, "drm_server_id: %s\n", data);
-            break;
-         case 2: //drm_commerce_id
-            fprintf(stderr, "drm_commerce_id: %s\n", data);
-            break;
-         case 3: //drm_ebookbase_book_id
-            fprintf(stderr, "drm_ebookbase_book_id: %s\n", data);
-            break;
-         case 100: //author
-            fprintf(stderr, "author: %s\n", data);
-            break;
-         case 101: //publisher
-            fprintf(stderr, "publisher: %s\n", data);
-            break;
-         case 106: //publishingdate
-            fprintf(stderr, "publishingdate: %s\n", data);
-            break;
-         case 113: //asin
-            fprintf(stderr, "asin: %s\n", data);
-            break;
-         case 208: //book unique drm key
-            fprintf(stderr, "book drm key: %s\n", data);
-            break;
-         case 503: //updatedtitle
-            fprintf(stderr, "updatedtitle: %s\n", data);
-            break;
-         default:
-            break;
-      }
-      erh = (ExthRecHeader*)(recLen  + (char*)erh);
-   }
-
-}
-
-//implementation of Pukall Cipher 1
-unsigned char *PC1(unsigned char *key, unsigned int klen, unsigned char *src,
-                   unsigned char *dest, unsigned int len, int decryption) {
-    unsigned int sum1 = 0;
-    unsigned int sum2 = 0;
-    unsigned int keyXorVal = 0;
-    unsigned short wkey[8];
-    unsigned int i;
-    if (klen != 16) {
-        fprintf(stderr, "Bad key length!\n");
-        return NULL;
-    }
-    for (i = 0; i < 8; i++) {
-        wkey[i] = (key[i * 2] << 8) | key[i * 2 + 1];
-    }
-    for (i = 0; i < len; i++) {
-        unsigned int temp1 = 0;
-        unsigned int byteXorVal = 0;
-        unsigned int j, curByte;
-        for (j = 0; j < 8; j++) {
-            temp1 ^= wkey[j];
-            sum2 = (sum2 + j) * 20021 + sum1;
-            sum1 = (temp1 * 346) & 0xFFFF;
-            sum2 = (sum2 + sum1) & 0xFFFF;
-            temp1 = (temp1 * 20021 + 1) & 0xFFFF;
-            byteXorVal ^= temp1 ^ sum2;
-        }
-        curByte = src[i];
-        if (!decryption) {
-            keyXorVal = curByte * 257;
-        }
-        curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF;
-        if (decryption) {
-            keyXorVal = curByte * 257;
-        }
-        for (j = 0; j < 8; j++) {
-            wkey[j] ^= keyXorVal;
-        }
-        dest[i] = curByte;
-    }
-    return dest;
-}
-
-unsigned int getSizeOfTrailingDataEntry(unsigned char *ptr, unsigned int size) {
-   unsigned int bitpos = 0;
-   unsigned int result = 0;
-   if (size <= 0) {
-      return result;
-   }
-   while (1) {
-      unsigned int v = ptr[size - 1];
-      result |= (v & 0x7F) << bitpos;
-      bitpos += 7;
-      size -= 1;
-      if ((v & 0x80) != 0 || (bitpos >= 28) || (size == 0)) {
-         return result;
-      }
-   }
-}
-
-unsigned int getSizeOfTrailingDataEntries(unsigned char *ptr, unsigned int size, unsigned int flags) {
-   unsigned int num = 0;
-   unsigned int testflags = flags >> 1;
-   while (testflags) {
-      if (testflags & 1) {
-         num += getSizeOfTrailingDataEntry(ptr, size - num);
-      }
-      testflags >>= 1;
-   }
-   if (flags & 1) {
-      num += (ptr[size - num - 1] & 0x3) + 1;
-   }
-   return num;
-}
-
-unsigned char *parseDRM(unsigned char *data, unsigned int count, unsigned char *pid, unsigned int pidlen) {
-   unsigned int i;
-   unsigned char temp_key_sum = 0;
-   unsigned char *found_key = NULL;
-   unsigned char *keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96";
-   unsigned char temp_key[16];
-
-   memset(temp_key, 0, 16);
-   memcpy(temp_key, pid, 8);
-   PC1(keyvec1, 16, temp_key, temp_key, 16, 0);
-
-   for (i = 0; i < 16; i++) {
-      temp_key_sum += temp_key[i];
-   }
-
-   for (i = 0; i < count; i++) {
-      unsigned char kk[32];
-      vstruct *v = (vstruct*)(data + i * 0x30);
-      kstruct *k = (kstruct*)PC1(temp_key, 16, v->cookie, kk, 32, 1);
-
-      if (v->verification == k->ver && v->cksum[0] == temp_key_sum &&
-          (bswap_l(k->flags) & 0x1F) == 1) {
-         found_key = (unsigned char*)malloc(16);
-         memcpy(found_key, k->finalkey, 16);
-         break;
-      }
-   }
-   return found_key;
-}
-
-void freeMobiFile(MobiFile *book) {
-   free(book->hr);
-   free(book->record0);
-   free(book);
-}
-
-MobiFile *parseMobiHeader(FILE *f) {
-   unsigned int numRecs, i, magic;
-   MobiFile *book = (MobiFile*)calloc(sizeof(MobiFile), 1);
-   book->f = f;
-   if (fread(&book->pdb, sizeof(PDB), 1, f) != 1) {
-      fprintf(stderr, "Failed to read Palm headers\n");
-      free(book);
-      return NULL;
-   }
-
-   //do BOOKMOBI check
-   if (book->pdb.type != 0x4b4f4f42 || book->pdb.creator != 0x49424f4d) {
-      fprintf(stderr, "Invalid header type or creator\n");
-      free(book);
-      return NULL;
-   }
-
-   book->recs = bswap_s(book->pdb.numRecs);
-
-   book->hr = (HeaderRec*)malloc(book->recs * sizeof(HeaderRec));
-   if (fread(book->hr, sizeof(HeaderRec), book->recs, f) != book->recs) {
-      fprintf(stderr, "Failed read of header record\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->record0_offset = bswap_l(book->hr[0].offset);
-   book->record0_size = bswap_l(book->hr[1].offset) - book->record0_offset;
-
-   if (fseek(f, book->record0_offset, SEEK_SET) == -1) {
-      fprintf(stderr, "bad seek to header record offset\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->record0 = (unsigned char*)malloc(book->record0_size);
-
-   if (fread(book->record0, book->record0_size, 1, f) != 1) {
-      fprintf(stderr, "bad read of record0\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->pdh = (PalmDocHeader*)(book->record0);
-   if (bswap_s(book->pdh->encryptionType) != 2) {
-      fprintf(stderr, "MOBI BOOK is not encrypted\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->textRecs = bswap_s(book->pdh->recordCount);
-
-   book->mobi = (MobiHeader*)(book->pdh + 1);
-   if (book->mobi->id != 0x49424f4d) {
-      fprintf(stderr, "MOBI header not found\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->mobiLen = bswap_l(book->mobi->hdrLen);
-   book->extra_data_flags = 0;
-   
-   if (book->mobiLen >= 0xe4) {
-      book->extra_data_flags = bswap_s(book->mobi->extra_flags);
-   }
-
-   if ((bswap_l(book->mobi->exthFlags) & 0x40) == 0) {
-      fprintf(stderr, "Missing exth header\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   book->exth = (ExthHeader*)(book->mobiLen + (char*)(book->mobi));
-   if (book->exth->id != 0x48545845) {
-      fprintf(stderr, "EXTH header not found\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-  
-   //if you want a list of EXTH records, uncomment the following
-//   enumExthRecords(exth);
-
-   book->drmCount = bswap_l(book->mobi->drmCount);
-
-   if (book->drmCount == 0) {
-      fprintf(stderr, "no PIDs found in this file\n");
-      freeMobiFile(book);
-      return NULL;
-   }
-
-   return book;
-}
-
-int writeMobiOutputFile(MobiFile *book, FILE *out, unsigned char *key, 
-                        unsigned int drmOffset, unsigned int drm_len) {
-   int i;
-   struct stat statbuf;
-
-   fstat(fileno(book->f), &statbuf);
-
-   // kill the drm keys
-   memset(book->record0 + drmOffset, 0, drm_len);
-   // kill the drm pointers
-   book->mobi->drmOffset = 0xffffffff;
-   book->mobi->drmCount = book->mobi->drmSize = book->mobi->drmFlags = 0;
-   // clear the crypto type
-   book->pdh->encryptionType = 0;
-
-   fwrite(&book->pdb, sizeof(PDB), 1, out);
-   fwrite(book->hr, sizeof(HeaderRec), book->recs, out);
-   fwrite("\x00\x00", 1, 2, out);
-   fwrite(book->record0, book->record0_size, 1, out);
-
-   //need to zero out exth 209 data
-   for (i = 1; i < book->recs; i++) {
-      unsigned int offset = bswap_l(book->hr[i].offset);
-      unsigned int len, extra_size = 0;
-      unsigned char *rec;
-      if (i == (book->recs - 1)) {  //last record extends to end of file
-         len = statbuf.st_size - offset;
-      }
-      else {
-         len = bswap_l(book->hr[i + 1].offset) - offset;
-      }
-      //make sure we are at proper offset
-      while (ftell(out) < offset) {
-         fwrite("\x00", 1, 1, out);
-      }
-      rec = (unsigned char *)malloc(len);
-      if (fseek(book->f, offset, SEEK_SET) != 0) {
-         fprintf(stderr, "Failed record seek on input\n");
-         freeMobiFile(book);
-         free(rec);
-         return 0;
-      }
-      if (fread(rec, len, 1, book->f) != 1) {
-         fprintf(stderr, "Failed record read on input\n");
-         freeMobiFile(book);
-         free(rec);
-         return 0;
-      }
-
-      if (i <= book->textRecs) { //decrypt if necessary
-         extra_size = getSizeOfTrailingDataEntries(rec, len, book->extra_data_flags);
-         PC1(key, 16, rec, rec, len - extra_size, 1);
-      }
-      fwrite(rec, len, 1, out);
-      free(rec);
-   }
-   return 1;
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/mobi.h
deleted file mode 100644 (file)
index 61758c6..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#ifndef __MOBI_H
-#define __MOBI_H
-
-#include <stdio.h>
-#include "skinutils.h"
-
-#pragma pack(2)
-typedef struct _PDB {
-   char name[32];
-   unsigned short attrib;
-   unsigned short version;
-   unsigned int created;
-   unsigned int modified;
-   unsigned int backup;
-   unsigned int modNum;
-   unsigned int appInfoID;
-   unsigned int sortInfoID;
-   unsigned int type;
-   unsigned int creator;
-   unsigned int uniqueIDseed;
-   unsigned int nextRecordListID;
-   unsigned short numRecs;
-} PDB;
-
-typedef struct _HeaderRec {
-   unsigned int offset;
-   unsigned int attribId;
-} HeaderRec;
-
-#define attrib(x) ((x)&0xFF)
-#define id(x) (bswap_l((x) & 0xFFFFFF00))
-
-typedef struct _PalmDocHeader {
-   unsigned short compression;
-   unsigned short reserverd1;
-   unsigned int textLength;
-   unsigned short recordCount;
-   unsigned short recordSize;
-   unsigned short encryptionType;
-   unsigned short reserved2;
-} PalmDocHeader;
-
-
-//checked lengths are 24, 116, 208, 228
-typedef struct _MobiHeader {
-   unsigned int id;
-   unsigned int hdrLen;
-   unsigned int type;
-   unsigned int encoding;
-   unsigned int uniqueId;
-   unsigned int generator;
-   unsigned char reserved1[40];
-   unsigned int firstNonBookIdx;
-   unsigned int nameOffset;
-   unsigned int nameLength;
-   unsigned int language;
-   unsigned int inputLang;
-   unsigned int outputLang;
-   unsigned int formatVersion;
-   unsigned int firstImageIdx;
-   unsigned char unknown1[16];
-   unsigned int exthFlags;
-   unsigned char unknown2[36];
-   unsigned int drmOffset;
-   unsigned int drmCount;
-   unsigned int drmSize;
-   unsigned int drmFlags;
-   unsigned char unknown3[58];
-   unsigned short extra_flags;
-} MobiHeader;
-
-typedef struct _ExthRecHeader {
-   unsigned int type;
-   unsigned int len;
-} ExthRecHeader;
-
-typedef struct _ExthHeader {
-   unsigned int id;
-   unsigned int hdrLen;
-   unsigned int recordCount;
-   ExthRecHeader records[1];
-} ExthHeader;
-
-typedef struct _vstruct {
-   unsigned int verification;
-   unsigned int size;
-   unsigned int type;
-   unsigned char cksum[4];
-   unsigned char cookie[32];
-} vstruct;
-
-typedef struct _kstruct {
-   unsigned int ver;
-   unsigned int flags;
-   unsigned char finalkey[16];
-   unsigned int expiry;
-   unsigned int expiry2;
-} kstruct;
-
-typedef struct _MobiFile {
-   FILE *f;
-   PDB pdb;
-   HeaderRec *hr;
-   PalmDocHeader *pdh;
-   MobiHeader *mobi;
-   ExthHeader *exth; 
-   unsigned char *record0; 
-   unsigned int record0_offset;
-   unsigned int record0_size;
-   unsigned int mobiLen;
-   unsigned int extra_data_flags;
-   unsigned int recs;
-   unsigned int drmCount;
-   unsigned int textRecs;
-   PidList *pids;    //extra pids to try from command line
-} MobiFile;
-
-unsigned char *getExthData(MobiFile *book, unsigned int type, unsigned int *len);
-void enumExthRecords(ExthHeader *eh);
-unsigned char *PC1(unsigned char *key, unsigned int klen, unsigned char *src,
-                   unsigned char *dest, unsigned int len, int decryption);
-unsigned int getSizeOfTrailingDataEntry(unsigned char *ptr, unsigned int size);
-unsigned int getSizeOfTrailingDataEntries(unsigned char *ptr, unsigned int size, unsigned int flags);
-unsigned char *parseDRM(unsigned char *data, unsigned int count, unsigned char *pid, unsigned int pidlen);
-
-void freeMobiFile(MobiFile *book);
-MobiFile *parseMobiHeader(FILE *f);
-int writeMobiOutputFile(MobiFile *book, FILE *out, unsigned char *key, 
-                        unsigned int drmOffset, unsigned int drm_len);
-
-#endif
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.c
deleted file mode 100644 (file)
index cecae83..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- sha1.c: Implementation of SHA-1 Secure Hash Algorithm-1
-
- Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
-   http://www.itl.nist.gov/fipspubs/fip180-1.htm
-
- Non-official Japanese Translation by HIRATA Yasuyuki:
-   http://yasu.asuka.net/translations/SHA-1.html
-
- Copyright (C) 2002 vi@nwr.jp. All rights reserved.
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgement in the product documentation would be
-    appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as beging the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- Note:
-   The copyright notice above is copied from md5.h by L. Peter Deutsch
-   <ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
- */
-#include <string.h>
-#include "sha1.h"
-
-/*
- * Packing bytes to a word
- *
- * Should not assume p is aligned to word boundary
- */
-static sha1_word_t packup(sha1_byte_t *p)
-{
-  /* Portable, but slow */
-  return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3] << 0;
-}
-
-/*
- * Unpacking a word to bytes
- *
- * Should not assume p is aligned to word boundary
- */
-static void unpackup(sha1_byte_t *p, sha1_word_t q)
-{
-  p[0] = (q >> 24) & 0xff;
-  p[1] = (q >> 16) & 0xff;
-  p[2] = (q >>  8) & 0xff;
-  p[3] = (q >>  0) & 0xff;
-}
-
-/*
- * Processing a block
- */
-static void sha1_update_now(sha1_state_s *pms, sha1_byte_t *bp)
-{
-  sha1_word_t  tmp, a, b, c, d, e, w[16+16];
-  int  i, s;
-
-  /* pack 64 bytes into 16 words */
-  for(i = 0; i < 16; i++) {
-    w[i] = packup(bp + i * sizeof(sha1_word_t));
-  }
-  memcpy(w + 16, w + 0, sizeof(sha1_word_t) * 16);
-
-  a = pms->sha1_h[0], b = pms->sha1_h[1], c = pms->sha1_h[2], d = pms->sha1_h[3], e = pms->sha1_h[4];
-
-#define        rot(x,n) (((x) << n) | ((x) >> (32-n)))
-#define        f0(b, c, d)     ((b&c)|(~b&d))
-#define        f1(b, c, d)     (b^c^d)
-#define        f2(b, c, d)     ((b&c)|(b&d)|(c&d))
-#define        f3(b, c, d)     (b^c^d)
-#define k0             0x5a827999
-#define        k1              0x6ed9eba1
-#define        k2              0x8f1bbcdc
-#define        k3              0xca62c1d6
-
-  /* t=0-15 */
-  s = 0;
-  for(i = 0; i < 16; i++) {
-    tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
-    e = d; d = c; c = rot(b, 30); b = a; a = tmp;
-    s = (s + 1) % 16;
-  }
-
-  /* t=16-19 */
-  for(i = 16; i < 20; i++) {
-    w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
-    w[s+16] = w[s];
-    tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
-    e = d; d = c; c = rot(b, 30); b = a; a = tmp;
-    s = (s + 1) % 16;
-  }
-
-  /* t=20-39 */
-  for(i = 0; i < 20; i++) {
-    w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
-    w[s+16] = w[s];
-    tmp = rot(a, 5) + f1(b, c, d) + e + w[s] + k1;
-    e = d; d = c; c = rot(b, 30); b = a; a = tmp;
-    s = (s + 1) % 16;
-  }
-
-  /* t=40-59 */
-  for(i = 0; i < 20; i++) {
-    w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
-    w[s+16] = w[s];
-    tmp = rot(a, 5) + f2(b, c, d) + e + w[s] + k2;
-    e = d; d = c; c = rot(b, 30); b = a; a = tmp;
-    s = (s + 1) % 16;
-  }
-
-  /* t=60-79 */
-  for(i = 0; i < 20; i++) {
-    w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
-    w[s+16] = w[s];
-    tmp = rot(a, 5) + f3(b, c, d) + e + w[s] + k3;
-    e = d; d = c; c = rot(b, 30); b = a; a = tmp;
-    s = (s + 1) % 16;
-  }
-
-  pms->sha1_h[0] += a, pms->sha1_h[1] += b, pms->sha1_h[2] += c, pms->sha1_h[3] += d, pms->sha1_h[4] += e;
-}
-
-/*
- * Increment sha1_size1, sha1_size2 field of sha1_state_s
- */
-static void incr(sha1_state_s *pms, int v)
-{
-  sha1_word_t  q;
-
-  q = pms->sha1_size1 + v * BITS;
-  if(q < pms->sha1_size1) {
-    pms->sha1_size2++;
-  }
-  pms->sha1_size1 = q;
-}
-
-/*
- * Initialize sha1_state_s as FIPS specifies
- */
-void   sha1_init(sha1_state_s *pms)
-{
-  memset(pms, 0, sizeof(*pms));
-  pms->sha1_h[0] = 0x67452301; /* Initialize H[0]-H[4] */
-  pms->sha1_h[1] = 0xEFCDAB89;
-  pms->sha1_h[2] = 0x98BADCFE;
-  pms->sha1_h[3] = 0x10325476;
-  pms->sha1_h[4] = 0xC3D2E1F0;
-}
-
-/*
- * Fill block and update output when needed
- */
-void   sha1_update(sha1_state_s *pms, sha1_byte_t *bufp, int length)
-{
-  /* Is the buffer partially filled? */
-  if(pms->sha1_count != 0) {
-    if(pms->sha1_count + length >= (signed) sizeof(pms->sha1_buf)) {   /* buffer is filled enough */
-      int fil = sizeof(pms->sha1_buf) - pms->sha1_count;               /* length to copy */
-
-      memcpy(pms->sha1_buf + pms->sha1_count, bufp, fil);
-      sha1_update_now(pms, pms->sha1_buf);
-      length -= fil;
-      bufp += fil;
-      pms->sha1_count = 0;
-      incr(pms, fil);
-    } else {
-      memcpy(pms->sha1_buf + pms->sha1_count, bufp, length);
-      pms->sha1_count += length;
-      incr(pms, length);
-      return;
-    }
-  }
-
-  /* Loop to update state */
-  for(;;) {
-    if(length < (signed) sizeof(pms->sha1_buf)) {              /* Short to fill up the buffer */
-      if(length) {
-        memcpy(pms->sha1_buf, bufp, length);
-      }
-      pms->sha1_count = length;
-      incr(pms, length);
-      break;
-    }
-    sha1_update_now(pms, bufp);
-    length -= sizeof(pms->sha1_buf);
-    bufp += sizeof(pms->sha1_buf);
-    incr(pms, sizeof(pms->sha1_buf));
-  }
-}
-
-void   sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE])
-{
-  int i;
-  sha1_byte_t buf[1];
-
-  /* fill a bit */
-  buf[0] = 0x80;
-  sha1_update(pms, buf, 1);
-
-  /* Decrement sha1_size1, sha1_size2 */
-  if((pms->sha1_size1 -= BITS) == 0) {
-    pms->sha1_size2--;
-  }
-
-  /* fill zeros */
-  if(pms->sha1_count > (signed) (sizeof(pms->sha1_buf) - 2 * sizeof(sha1_word_t))) {
-    memset(pms->sha1_buf + pms->sha1_count, 0, sizeof(pms->sha1_buf) - pms->sha1_count);
-    sha1_update_now(pms, pms->sha1_buf);
-    pms->sha1_count = 0;
-  }
-  memset(pms->sha1_buf + pms->sha1_count, 0,
-    sizeof(pms->sha1_buf) - pms->sha1_count - sizeof(sha1_word_t) * 2);
-
-  /* fill last length */
-  unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 2, pms->sha1_size2);
-  unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 1, pms->sha1_size1);
-
-  /* final update */
-  sha1_update_now(pms, pms->sha1_buf);
-
-  /* move hash value to output byte array */
-  for(i = 0; i < (signed) (sizeof(pms->sha1_h)/sizeof(sha1_word_t)); i++) {
-    unpackup(output + i * sizeof(sha1_word_t), pms->sha1_h[i]);
-  }
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/sha1.h
deleted file mode 100644 (file)
index bae61e0..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- sha1.h: Implementation of SHA-1 Secure Hash Algorithm-1
-
- Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
-   http://www.itl.nist.gov/fipspubs/fip180-1.htm
-
- Non-official Japanese Translation by HIRATA Yasuyuki:
-   http://yasu.asuka.net/translations/SHA-1.html
-
- Copyright (C) 2002 vi@nwr.jp. All rights reserved.
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgement in the product documentation would be
-    appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as beging the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- Note:
-   The copyright notice above is copied from md5.h by L. Petet Deutsch
-   <ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
- */
-#ifndef        SHA1_H
-#define        SHA1_H
-
-typedef        unsigned int    sha1_word_t;    /* 32bits unsigned integer */
-typedef unsigned char  sha1_byte_t;    /* 8bits unsigned integer */
-#define        BITS            8
-
-/* Define the state of SHA-1 algorithm */
-typedef struct {
-  sha1_byte_t  sha1_buf[64];   /* 512 bits */
-  int          sha1_count;     /* How many bytes are used */
-  sha1_word_t  sha1_size1;             /* Length counter Lower Word */
-  sha1_word_t  sha1_size2;             /* Length counter Upper Word */
-  sha1_word_t  sha1_h[5];              /* Hash output */
-} sha1_state_s;
-#define        SHA1_OUTPUT_SIZE        20      /* in bytes */
-
-/* External Functions */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Initialize SHA-1 algorithm */
-void   sha1_init(sha1_state_s *pms);
-
-/* Append a string to SHA-1 algorithm */
-void   sha1_update(sha1_state_s *pms, sha1_byte_t *input_buffer, int length);
-
-/* Finish the SHA-1 algorithm and return the hash */
-void   sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE]);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.c
deleted file mode 100644 (file)
index f9637aa..0000000
+++ /dev/null
@@ -1,461 +0,0 @@
-
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-/*
- * Dependencies: none
- * build on cygwin: 
- *     gcc -o skindle skindle.c md5.c sha1.c b64.c -lCrypt32
- * or  gcc -o skindle skindle.c md5.c sha1.c b64.c -lCrypt32 -mno-cygwin
- * Under cygwin, you can just type make to build it.
- * The code should compile with Visual Studio, just add all the files to
- * a project and add the Crypt32.lib dependency and it should build as a
- * Win32 console app.
- */
-
-/*
- * MUST be run on the computer on which KindleForPC is installed
- * under the account that was used to purchase DRM'ed content.
- * Requires your kindle.info file which can be found in something like:
- * <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}
- * where ... varies by platform but is "Local Settings\Application Data" on XP
- */
-
-/*
-  What: KindleForPC DRM removal utility to preserve your fair use rights!
-  Why: Fair use is a well established doctrine, and I am no fan of vendor
-       lockin.
-  How: This utility implements the PID extraction, DRM key generation and
-       decryption algorithms employed by the KindleForPC application. This
-       is a stand alone app that does not require you to determine a PID on
-       your own, and it does not need to run KindleForPC in order to extract
-       any data from memory.
-  
-  Shoutz: The DarkReverser - thanks for mobidedrm!  The last part of this
-       is just a C port of mobidedrm.
-       labba and I<3cabbages for motivating me to do this the right way.
-       You guys shouldn't need to spend all your time responding to all the
-       changes Amazon is going to force you to make in unswindle each time
-       the release a new version.
-       Lawrence Lessig - You are my hero. 'Nuff said.
-   Thumbs down: Disney, MPAA, RIAA - you guys suck.  Making billions off 
-       of the exploitation of works out of copyright while vigourously
-       pushing copyright extension to prevent others from doing the same
-       is the height of hypocrasy.
-       Congress - you guys suck too.  Why you arrogant pricks think you
-       are smarter than the founding fathers is beyond me.
- */
-
-#include <windows.h>
-#include <Wincrypt.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "skinutils.h"
-#include "cbuf.h"
-#include "mobi.h"
-#include "tpz.h"
-
-#include "zlib.h"
-
-int processTopaz(FILE *in, char *outFile, int explode, PidList *extraPids) {
-   //had to pile all these up here to please VS2009
-   cbuf *tpzHeaders, *tpzBody;
-   struct stat statbuf;
-   FILE *out;
-   unsigned int i;
-   char *keysRecord, *keysRecordRecord;
-   TopazFile *topaz;
-   char *pid;
-   
-   fstat(fileno(in), &statbuf);
-
-   topaz = parseTopazHeader(in);
-   if (topaz == NULL) {
-      fprintf(stderr, "Failed to parse topaz headers\n");
-      return 0;
-   }
-   topaz->pids = extraPids;
-   
-   tpzHeaders = b_new(topaz->bodyOffset);
-   tpzBody = b_new(statbuf.st_size);
-   
-   parseMetadata(topaz);
-   
-//   dumpMap(bookMetadata);
-
-   keysRecord = getMetadata(topaz, "keys");
-   if (keysRecord == NULL) {
-      //fail
-   }
-   keysRecordRecord = getMetadata(topaz, keysRecord);
-   if (keysRecordRecord == NULL) {
-      //fail
-   }
-
-   pid = getBookPid(keysRecord, strlen(keysRecord), keysRecordRecord, strlen(keysRecordRecord));
-
-   if (pid == NULL) {
-      fprintf(stderr, "Failed to extract pid automatically\n");
-   }
-   else {
-      char *title = getMetadata(topaz, "Title");   
-      fprintf(stderr, "PID for %s is: %s\n", title ? title : "UNK", pid);
-   }
-   
-/*
-   unique pid is computed as:
-   base64(sha1(idArray . kindleToken . 209_data . 209_tokens))
-*/
-
-   //
-   //  Decrypt book key
-   //
-   Payload *dkey = getBookPayloadRecord(topaz, "dkey", 0, 0);
-   
-   if (dkey == NULL) {
-      fprintf(stderr, "No dkey record found\n");
-      freeTopazFile(topaz);
-      return 0;
-   }
-   
-   if (pid) {
-      topaz->bookKey = decryptDkeyRecords(dkey, pid);     
-      free(pid);
-   }
-   if (topaz->bookKey == NULL) {
-      if (extraPids) {
-         int p;
-         freePayload(dkey);
-         for (p = 0; p < extraPids->numPids; p++) {
-            dkey = getBookPayloadRecord(topaz, "dkey", 0, 0);
-            topaz->bookKey = decryptDkeyRecords(dkey, extraPids->pidList[p]);
-            if (topaz->bookKey) break;
-         }
-      }
-      if (topaz->bookKey == NULL) {
-         fprintf(stderr, "No valid pids available, failed to find DRM key\n");
-         freeTopazFile(topaz);
-         freePayload(dkey);
-         return 0;
-      }
-   }
-      
-   fprintf(stderr, "Found a DRM key!\n");
-   for (i = 0; i < 8; i++) {
-      fprintf(stderr, "%02x", topaz->bookKey[i]);
-   }
-   fprintf(stderr, "\n");
-
-   out = fopen(outFile, "wb");
-   if (out == NULL) {
-      fprintf(stderr, "Failed to open output file, quitting\n");
-      freeTopazFile(topaz);
-      freePayload(dkey);
-      return 0;
-   }
-
-   writeTopazOutputFile(topaz, out, tpzHeaders, tpzBody, explode);
-   fwrite(tpzHeaders->buf, tpzHeaders->idx, 1, out);
-   fwrite(tpzBody->buf, tpzBody->idx, 1, out);
-   fclose(out);
-   b_free(tpzHeaders);
-   b_free(tpzBody);
-
-   freePayload(dkey);
-
-   freeTopazFile(topaz);
-   return 1;
-}
-
-int processMobi(FILE *prc, char *outFile, PidList *extraPids) {
-   //had to pile all these up here to please VS2009
-   PDB header;
-   cbuf *keyBuf;
-   char *pid;
-   FILE *out;
-   unsigned int i, keyPtrLen;
-   unsigned char *keyPtr;
-   unsigned int drmOffset, drm_len;
-   unsigned char *drm, *found_key = NULL;
-   MobiFile *book;
-   int result;
-   
-   book = parseMobiHeader(prc);
-
-   if (book == NULL) {
-      fprintf(stderr, "Failed to read mobi headers\n");
-      return 0;
-   }
-
-   book->pids = extraPids;
-   keyPtr = getExthData(book, 209, &keyPtrLen);
-
-   keyBuf = b_new(128);
-   if (keyPtr != NULL) {
-      unsigned int idx;
-      for (idx = 0; idx < keyPtrLen; idx += 5) {
-         unsigned char *rec;
-         unsigned int dlen;
-         unsigned int rtype = bswap_l(*(unsigned int*)(keyPtr + idx + 1));
-         rec = getExthData(book, rtype, &dlen);
-         if (rec != NULL) {
-            b_add_buf(keyBuf, rec, dlen);
-         }
-      }
-   }
-
-   pid = getBookPid(keyPtr, keyPtrLen, keyBuf->buf, keyBuf->idx);
-
-   b_free(keyBuf);
-
-   if (pid == NULL) {
-      fprintf(stderr, "Failed to extract pid automatically\n");
-   }
-   else {
-      fprintf(stderr, "PID for %s is: %s\n", book->pdb.name, pid);
-   }
-
-/*
-   unique pid is computed as:
-   base64(sha1(idArray . kindleToken . 209_data . 209_tokens))
-*/
-
-   drmOffset = bswap_l(book->mobi->drmOffset);
-
-   drm_len = bswap_l(book->mobi->drmSize);
-   drm = book->record0 + drmOffset;
-
-   if (pid) {
-      found_key = parseDRM(drm, book->drmCount, pid, 8);
-      free(pid);
-   }
-   if (found_key == NULL) {
-      if (extraPids) {
-         int p;
-         for (p = 0; p < extraPids->numPids; p++) {
-            found_key = parseDRM(drm, book->drmCount, extraPids->pidList[p], 8);
-            if (found_key) break;
-         }
-      }
-      if (found_key == NULL) {
-         fprintf(stderr, "No valid pids available, failed to find DRM key\n");
-         freeMobiFile(book);
-         return 0;
-      }
-   }
-   fprintf(stderr, "Found a DRM key!\n");
-
-   out = fopen(outFile, "wb");
-   if (out == NULL) {
-      fprintf(stderr, "Failed to open output file, quitting\n");
-      freeMobiFile(book);
-      free(found_key);
-      return 0;
-   }
-
-   result = writeMobiOutputFile(book, out, found_key, drmOffset, drm_len);
-
-   fclose(out);
-   if (result == 0) {
-      _unlink(outFile);
-   }
-   freeMobiFile(book);
-   free(found_key);
-   return result;
-}
-
-enum {
-   FileTypeUnk,
-   FileTypeMobi,
-   FileTypeTopaz
-};
-
-int getFileType(FILE *in) {
-   PDB p;
-   int type = FileTypeUnk;
-   fseek(in, 0, SEEK_SET);
-   fread(&p, sizeof(p), 1, in);
-   if (p.type == 0x4b4f4f42 && p.creator == 0x49424f4d) {
-      type = FileTypeMobi;
-   }
-   else if (strncmp(p.name, "TPZ0", 4) == 0) {
-      type = FileTypeTopaz;
-   }
-   fseek(in, 0, SEEK_SET);
-   return type;
-}
-
-void usage() {
-   fprintf(stderr, "usage: ./skindle [-d] [-v] -i <ebook file> -o <output file> [-k kindle.info file] [-p pid]\n");
-   fprintf(stderr, "    -d optional, for topaz files only, produce a decompressed output file\n");
-   fprintf(stderr, "    -i required name of the input mobi or topaz file\n");
-   fprintf(stderr, "    -o required name of the output file to generate\n");
-   fprintf(stderr, "    -k optional kindle.info path\n");   
-   fprintf(stderr, "    -v dump the contents of kindle.info\n");   
-   fprintf(stderr, "    -p additional PID values to attempt (can specifiy multiple times)\n");   
-}
-
-extern char *optarg;
-extern int optind;
-
-int main(int argc, char **argv) {
-   //had to pile all these up here to please VS2009
-   FILE *in;
-   int type, explode = 0;
-   int result = 0;
-   int firstArg = 1;
-   int opt;
-   PidList *pids = NULL;
-   char *infile = NULL, *outfile = NULL, *kinfo = NULL;
-   int dump = 0;
-   
-   while ((opt = getopt(argc, argv, "vdp:i:o:k:")) != -1) {
-      switch (opt) {
-         case 'v':
-            dump = 1;
-            break;
-         case 'd':
-            explode = 1;
-            break;
-         case 'p': {
-            int l = strlen(optarg);
-            if (l == 10) {
-               if (!verifyPidChecksum(optarg)) {
-                  fprintf(stderr, "Invalid pid %s, skipping\n", optarg);
-                  break;
-               }
-               optarg[8] = 0;
-            }
-            else if (l != 8) {
-               fprintf(stderr, "Invalid pid length for %s, skipping\n", optarg);
-               break;
-            }
-            if (pids == NULL) {
-               pids = (PidList*)malloc(sizeof(PidList));
-               pids->numPids = 1;
-               pids->pidList[0] = optarg;
-            }
-            else {
-               pids = (PidList*)realloc(pids, sizeof(PidList) + pids->numPids * sizeof(unsigned char*));
-               pids->pidList[pids->numPids++] = optarg;
-            }
-            break;
-         }
-         case 'k':
-            kinfo = optarg;
-            break;
-         case 'i':
-            infile = optarg;
-            break;
-         case 'o':
-            outfile = optarg;
-            break;
-         default: /* '?' */
-            usage();
-            exit(1);
-      }
-   }
-
-   if (optind != argc) {
-      fprintf(stderr, "Extra options ignored\n");
-   }
-
-   if (!buildKindleMap(kinfo)) {
-      fprintf(stderr, "buildKindleMap failed\n");
-      usage();
-      exit(1);
-   }
-
-   //The following loop dumps the contents of your kindle.info file
-   if (dump) {
-      MapList *ml;
-//     dumpKindleMap();
-      fprintf(stderr, "\nDumping kindle.info contents:\n");
-      for (ml = kindleMap; ml; ml = ml->next) {
-         DATA_BLOB DataIn;
-         DATA_BLOB DataOut;
-         DataIn.pbData = mazamaDecode(ml->value, (int*)&DataIn.cbData);
-         if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
-            fprintf(stderr, "%s ==> %s\n", ml->key, translateKindleKey(ml->key));
-            fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
-            fprintf(stderr, "\n\n");
-            LocalFree(DataOut.pbData);
-         }
-         else {
-            fprintf(stderr, "CryptUnprotectData failed\n");
-         }
-         free(DataIn.pbData);
-      }
-   }
-
-   if (infile == NULL && outfile == NULL) {
-      //special case, user just wants to see kindle.info
-      freeMap(kindleMap);
-      exit(1);
-   }
-
-   if (infile == NULL) {
-      fprintf(stderr, "Missing input file name\n");
-      usage();
-      freeMap(kindleMap);
-      exit(1);
-   }
-
-   if (outfile == NULL) {
-      fprintf(stderr, "Missing output file name\n");
-      usage();
-      freeMap(kindleMap);
-      exit(1);
-   }
-   
-   in = fopen(infile, "rb");
-   if (in == NULL) {
-      fprintf(stderr, "%s bad open, quitting\n", infile);
-      freeMap(kindleMap);
-      exit(1);
-   }
-
-   type = getFileType(in);
-   if (type == FileTypeTopaz) {
-      result = processTopaz(in, outfile, explode, pids);
-   }
-   else if (type == FileTypeMobi) {
-      result = processMobi(in, outfile, pids);
-   }
-   else {
-      fprintf(stderr, "%s file type unknown, quitting\n", infile);
-      fclose(in);
-      freeMap(kindleMap);
-      exit(1);
-   }
-
-   fclose(in);
-   if (result) {
-      fprintf(stderr, "Success! Enjoy!\n");
-   }
-   else {
-      fprintf(stderr, "An error occurred, unable to process input file!\n");
-   }
-   
-   freeMap(kindleMap);
-   return 0;
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.exe b/Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.exe
deleted file mode 100644 (file)
index 3dc187f..0000000
Binary files a/Kindle_4_PC_Tools/Other_Tools/skindle-06/skindle.exe and /dev/null differ
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.c
deleted file mode 100644 (file)
index add3336..0000000
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#include <windows.h>
-#include <Wincrypt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "skinutils.h"
-
-/* The kindle.info file created when you install KindleForPC is a set
- * of key:value pairs delimited by '{'.  The keys and values are encoded
- * in a variety of ways.  Keys are the mazama64 encoded md5 hash of the
- * key name, while values are the mazama64 encoding of the blob returned
- * by the Windows CryptProtectData function.  The use of CryptProtectData
- * is what locks things to a particular user/machine
-
- * kindle.info layout
-
- * Key:AbaZZ6z4a7ZxzLzkZcaqauZMZjZ_Ztz6   ("kindle.account.tokens")
- * Value: mazama64Encode(CryptProtectData(some sha1 hash))
-
- * Key:AsAWa4ZJAQaCZ7A3zrZSaZavZMarZFAw  ("kindle.cookie.item")
- * Value: mazama64Encode(CryptProtectData(base64(144 bytes of data)))
-
- * Key:ZHatAla4a-zTzWA-AvaeAvZQzKZ-agAz   ("eulaVersionAccepted")
- * Value: mazama64Encode(CryptProtectData(kindle version?))
-
- * Key:ZiajZga7Z9zjZRz7AfZ-zRzUANZNZJzP   ("login_date")
- * Value: mazama64Encode(CryptProtectData(registration date))
-
- * Key:ZkzeAUA-Z2ZYA2Z_ayA_ahZEATaEAOaG  ("kindle.token.item")
- * Value: mazama64Encode(CryptProtectData(multi-field crypto data))
- * {enc:xxx}{iv:xxx}{key:xxx}{name:xxx}{serial:xxx}
- * enc:base64(binary blob)
- * iv:base64(16 bytes)
- * key:base64(256 bytes)
- * name:base64("ADPTokenEncryptionKey")
- * serial:base64("1")
-
- * Key:aVzrzRAFZ7aIzmASZOzVzIAGAKawzwaU    ("login")
- * Value: mazama64Encode(CryptProtectData(your amazon email))
-
- * Key:avalzbzkAcAPAQA5ApZgaOZPzQZzaiaO   mazama64Encode(md5("MazamaRandomNumber"))
- * Value: mazama64Encode(CryptProtectData(mazama32Encode(32 bytes random data)))
-
- * Key:zgACzqAjZ2zzAmAJa6ZFaZALaYAlZrz-   ("kindle.key.item")
- * Value: mazama64Encode(CryptProtectData(RSA private key)) no password
-
- * Key:zga-aIANZPzbzfZ1zHZWZcA4afZMZcA_   ("kindle.name.info")
- * Value: mazama64Encode(CryptProtectData(your name))
-
- * Key:zlZ9afz1AfAVZjacaqa-ZHa1aIa_ajz7   ("kindle.device.info");
- * Value: mazama64Encode(CryptProtectData(the name of your kindle))
-*/
-
-char *kindleKeys[] = {
-   "AbaZZ6z4a7ZxzLzkZcaqauZMZjZ_Ztz6", "kindle.account.tokens",
-   "AsAWa4ZJAQaCZ7A3zrZSaZavZMarZFAw", "kindle.cookie.item",
-   "ZHatAla4a-zTzWA-AvaeAvZQzKZ-agAz", "eulaVersionAccepted",
-   "ZiajZga7Z9zjZRz7AfZ-zRzUANZNZJzP", "login_date",
-   "ZkzeAUA-Z2ZYA2Z_ayA_ahZEATaEAOaG", "kindle.token.item",
-   "aVzrzRAFZ7aIzmASZOzVzIAGAKawzwaU", "login",
-   "avalzbzkAcAPAQA5ApZgaOZPzQZzaiaO", "MazamaRandomNumber",
-   "zgACzqAjZ2zzAmAJa6ZFaZALaYAlZrz-", "kindle.key.item",
-   "zga-aIANZPzbzfZ1zHZWZcA4afZMZcA_", "kindle.name.info",
-   "zlZ9afz1AfAVZjacaqa-ZHa1aIa_ajz7", "kindle.device.info"
-};
-
-MapList *kindleMap;
-
-unsigned short bswap_s(unsigned short s) {
-   return (s >> 8) | (s << 8);
-}
-
-unsigned int bswap_l(unsigned int s) {
-   unsigned int u = bswap_s(s);
-   unsigned int l = bswap_s(s >> 16);
-   return (u << 16) | l;
-}
-
-char *translateKindleKey(char *key) {
-   int n = sizeof(kindleKeys) / sizeof(char*);
-   int i;
-   for (i = 0; i < n; i += 2) {
-      if (strcmp(key, kindleKeys[i]) == 0) {
-         return kindleKeys[i + 1];
-      }
-   }
-   return NULL;
-}
-
-MapList *findNode(MapList *map, char *key) {
-   MapList *l;
-   for (l = map; l; l = l->next) {
-      if (strcmp(key, l->key) == 0) {
-         return l;
-      }
-   }
-   return NULL;
-}
-
-MapList *findKindleNode(char *key) {
-   return findNode(kindleMap, key);
-}
-
-char *getNodeValue(MapList *map, char *key) {
-   MapList *l;
-   for (l = map; l; l = l->next) {
-      if (strcmp(key, l->key) == 0) {
-         return l->value;
-      }
-   }
-   return NULL;
-}
-
-char *getKindleValue(char *key) {
-   return getNodeValue(kindleMap, key);
-}
-
-MapList *addMapNode(MapList *map, char *key, char *value) {
-   MapList *ml;
-   ml = findNode(map, key);
-   if (ml) {
-      free(ml->value);
-      ml->value = value;
-      return map;
-   }
-   else {
-      ml = (MapList*)malloc(sizeof(MapList));
-      ml->key = key;
-      ml->value = value;
-      ml->next = map;
-      return ml;
-   }
-}
-
-void dumpMap(MapList *m) {
-   MapList *l;
-   for (l = m; l; l = l->next) {
-      fprintf(stderr, "%s:%s\n", l->key, l->value);
-   }
-}
-
-void freeMap(MapList *m) {
-   MapList *n;
-   while (m) {
-      n = m;
-      m = m->next;
-      free(n->key);
-      free(n->value);
-      free(n);
-   }
-}
-
-void parseLine(char *line) {
-   char *colon = strchr(line, ':');
-   if (colon) {
-      char *key, *value;
-      int len = colon - line;
-      key = (char*)malloc(len + 1);
-      *colon++ = 0;
-      strcpy(key, line);
-      len = strlen(colon);
-      value = (char*)malloc(len + 1);
-      strcpy(value, colon);
-      value[len] = 0;
-      kindleMap = addMapNode(kindleMap, key, value);
-   }
-}
-
-void dumpKindleMap() {
-   dumpMap(kindleMap);
-}
-
-int buildKindleMap(char *infoFile) {
-   int result = 0;
-   struct stat statbuf;
-   char ki[512];
-   DWORD len = sizeof(ki);
-   if (infoFile == NULL) {
-      HKEY regkey;
-      fprintf(stderr, "Attempting to locate kindle.info\n");
-      if (RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\", &regkey) != ERROR_SUCCESS) {
-         fprintf(stderr, "Unable to locate kindle.info, please specify path on command line\n");
-         return result;      
-      }
-      
-//      if (RegGetValue(regkey, "Local AppData", NULL, NULL, ki, &len) != ERROR_SUCCESS) {
-      if (RegQueryValueEx(regkey, "Local AppData", NULL, NULL, ki, &len) != ERROR_SUCCESS) {
-         RegCloseKey(regkey);
-         fprintf(stderr, "Unable to locate kindle.info, please specify path on command line\n");
-         return result;      
-      }
-      ki[len] = 0;
-      strncat(ki, "\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info", sizeof(ki) - 1 - strlen(ki));
-      infoFile = ki;      
-      fprintf(stderr, "Found kindle.info location\n");
-   }
-   if (stat(infoFile, &statbuf) == 0) {
-      FILE *fd = fopen(infoFile, "rb");
-      char *infoBuf = (char*)malloc(statbuf.st_size + 1);
-      infoBuf[statbuf.st_size] = 0;
-      if (fread(infoBuf, statbuf.st_size, 1, fd) == 1) {
-         char *end = infoBuf + statbuf.st_size;
-         char *b = infoBuf, *e;
-         while (e = strchr(b, '{')) {
-            *e = 0;
-            if ((e - b) > 2) {
-               parseLine(b);
-            }
-            e++;
-            b = e;
-         }
-         if (b < end) {
-            parseLine(b);
-         }
-      }
-      else {
-         fprintf(stderr, "short read on info file\n");
-      }
-      free(infoBuf);
-      fclose(fd);
-      return 1;
-   }
-   return 0;
-}
-
-static unsigned int crc_table[256];
-
-void png_crc_table_init() {
-   unsigned int i;
-   if (crc_table[255]) return;
-   for (i = 0; i < 256; i++) {
-      unsigned int n = i;
-      unsigned int j;
-      for (j = 0; j < 8; j++) {
-         if (n & 1) {
-            n = 0xEDB88320 ^ (n >> 1);
-         }
-         else {
-            n >>= 1;
-         }
-      }
-      crc_table[i] = n;
-   }
-}
-
-unsigned int do_crc(unsigned char *input, unsigned int len) {
-   unsigned int crc = 0;
-   unsigned int i;
-   png_crc_table_init();
-   for (i = 0; i < len; i++) {
-      unsigned int v = (input[i] ^ crc) & 0xff;
-      crc = crc_table[v] ^ (crc >> 8);
-   }
-   return crc;
-}
-
-char *decodeString = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
-
-void doPngDecode(unsigned char *input, unsigned int len, unsigned char *output) {
-//   unsigned int crc_table[256];
-   unsigned int crc, i, x = 0;
-   unsigned int *out = (unsigned int*)output;
-   crc = bswap_l(do_crc(input, len));
-   memset(output, 0, 8);
-   for (i = 0; i < len; i++) {
-      output[x++] ^= input[i];
-      if (x == 8) x = 0;
-   }
-   out[0] ^= crc;
-   out[1] ^= crc;
-   for (i = 0; i < 8; i++) {
-      unsigned char v = output[i];
-      output[i] = decodeString[((((v >> 5) & 3) ^ v) & 0x1F) + (v >> 7)];
-   }
-}
-
-static char *string_32 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M";
-static char *string_64 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_";
-
-char *mazamaEncode32(unsigned char *input, unsigned int len) {
-   return mazamaEncode(input, len, 32);
-}
-
-char *mazamaEncode64(unsigned char *input, unsigned int len) {
-   return mazamaEncode(input, len, 64);
-}
-
-char *mazamaEncode(unsigned char *input, unsigned int len, unsigned char choice) {
-   unsigned int i;
-   char *enc, *out;
-   if (choice == 0x20) enc = string_32;
-   else if (choice == 0x40) enc = string_64;
-   else return NULL;
-   out = (char*)malloc(len * 2 + 1);
-   out[len * 2] = 0;
-   for (i = 0; i < len; i++) {
-      unsigned char v = input[i] + 128;
-      unsigned char q = v / choice;
-      unsigned char m = v % choice;
-      out[i * 2] = enc[q];
-      out[i * 2 + 1] = enc[m];
-   }
-   return out;
-}
-
-unsigned char *mazamaDecode(char *input, int *outlen) {
-   unsigned char *out;
-   int len = strlen(input);
-   char *dec = NULL;
-   int i, choice = 0x20;
-   *outlen = 0;
-   for (i = 0; i < 8 && i < len; i++) {
-      if (*input == string_32[i]) {
-         dec = string_32;
-         break;
-      }
-   }
-   if (dec == NULL) {
-      for (i = 0; i < 4 && i < len; i++) {
-         if (*input == string_64[i]) {
-            dec = string_64;
-            choice = 0x40;
-            break;
-         }
-      }
-   }
-   if (dec == NULL) {
-      return NULL;
-   }
-   out = (unsigned char*)malloc(len / 2 + 1);
-   out[len / 2] = 0;
-   for (i = 0; i < len; i += 2) {
-      int q, m, v;
-      char *p = strchr(dec, input[i]);
-      if (p == NULL) break;
-      q = p - dec;
-      p = strchr(dec, input[i + 1]);
-      if (p == NULL) break;
-      m = p - dec;
-      v = (choice * q + m) - 128;
-      out[(*outlen)++] = (unsigned char)v;
-   }
-   return out;
-}
-
-#ifndef HEADER_MD5_H
-
-void md5(unsigned char *in, int len, unsigned char *md) {
-   MD5_CTX s;
-   MD5_Init(&s);
-       MD5_Update(&s, in, len);
-       MD5_Final(md, &s);
-}
-#endif
-
-#ifndef HEADER_SHA_H
-
-void sha1(unsigned char *in, int len, unsigned char *md) {
-   SHA_CTX s;
-   SHA1_Init(&s);
-       SHA1_Update(&s, in, len);
-       SHA1_Final(md, &s);
-}
-#endif
-
-char *getBookPid(unsigned char *keys, unsigned int klen, unsigned char *keysValue, unsigned int kvlen) {
-   unsigned char *vsn, *username, *mrn_key, *kat_key, *pid;
-   char drive[256];
-   char name[256];
-   DWORD nlen = sizeof(name);
-   char *d;
-   char volumeName[256];
-   DWORD volumeSerialNumber;
-   char fileSystemNameBuffer[256];
-   char volumeID[32];
-   unsigned char md5sum[MD5_DIGEST_LENGTH];
-   unsigned char sha1sum[SHA_DIGEST_LENGTH];
-   SHA_CTX sha1_ctx;
-   char *mv;
-
-   if (GetUserName(name, &nlen) == 0) {
-      fprintf(stderr, "GetUserName failed\n");
-      return NULL;
-   }
-   fprintf(stderr, "Using UserName = \"%s\"\n", name);
-
-   d = getenv("SystemDrive");
-   if (d) {
-      strcpy(drive, d);
-      strcat(drive, "\\");
-   }
-   else {
-      strcpy(drive, "c:\\");
-   }
-   fprintf(stderr, "Using SystemDrive = \"%s\"\n", drive);
-   if (GetVolumeInformation(drive, volumeName, sizeof(volumeName), &volumeSerialNumber,
-                        NULL, NULL, fileSystemNameBuffer, sizeof(fileSystemNameBuffer))) {
-      sprintf(volumeID, "%u", volumeSerialNumber);
-   }
-   else {
-      strcpy(volumeID, "9999999999");
-   }
-   fprintf(stderr, "Using VolumeSerialNumber = \"%s\"\n", volumeID);
-   MD5(volumeID, strlen(volumeID), md5sum);
-   vsn = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x20);
-
-   MD5(name, strlen(name), md5sum);
-   username = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x20);
-
-   MD5("MazamaRandomNumber", 18, md5sum);
-   mrn_key = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x40);
-
-   MD5("kindle.account.tokens", 21, md5sum);
-   kat_key = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x40);
-
-   SHA1_Init(&sha1_ctx);
-
-   mv = getKindleValue(mrn_key);
-   if (mv) {
-      DATA_BLOB DataIn;
-      DATA_BLOB DataOut;
-      DataIn.pbData = mazamaDecode(mv, (int*)&DataIn.cbData);
-      if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
-         char *devId = (char*)malloc(DataOut.cbData + 4 * MD5_DIGEST_LENGTH + 1);
-         char *finalDevId;
-         unsigned char pidbuf[10];
-         
-//         fprintf(stderr, "CryptUnprotectData success\n");
-//         fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
-//         fprintf(stderr, "\n");
-
-         memcpy(devId, DataOut.pbData, DataOut.cbData);
-         strcpy(devId + DataOut.cbData, vsn);
-         strcat(devId + DataOut.cbData, username);
-
-//         fprintf(stderr, "Computing sha1 over %d bytes\n", DataOut.cbData + 4 * MD5_DIGEST_LENGTH);
-         sha1(devId, DataOut.cbData + 4 * MD5_DIGEST_LENGTH, sha1sum);
-         finalDevId = mazamaEncode(sha1sum, SHA_DIGEST_LENGTH, 0x20);
-//         fprintf(stderr, "finalDevId: %s\n", finalDevId);
-
-         SHA1_Update(&sha1_ctx, finalDevId, strlen(finalDevId));
-
-         pidbuf[8] = 0;
-         doPngDecode(finalDevId, 4, (unsigned char*)pidbuf);
-         fprintf(stderr, "Device PID: %s\n", pidbuf);
-
-         LocalFree(DataOut.pbData);
-         free(devId);
-         free(finalDevId);
-      }
-      else {
-         fprintf(stderr, "CryptUnprotectData failed, quitting\n");
-         free(kat_key);
-         free(mrn_key);
-         return NULL;
-      }
-
-      free(DataIn.pbData);
-   }
-   else {
-      fprintf(stderr, "Failed to find map node: %s\n", mrn_key);
-   }
-
-   mv = getKindleValue(kat_key);
-   if (mv) {
-      DATA_BLOB DataIn;
-      DATA_BLOB DataOut;
-      DataIn.pbData = mazamaDecode(mv, (int*)&DataIn.cbData);
-      if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
-//         fprintf(stderr, "CryptUnprotectData success\n");
-//         fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
-//         fprintf(stderr, "\n");
-
-         SHA1_Update(&sha1_ctx, DataOut.pbData, DataOut.cbData);
-
-         LocalFree(DataOut.pbData);
-      }
-      else {
-         fprintf(stderr, "CryptUnprotectData failed, quitting\n");
-         free(kat_key);
-         free(mrn_key);
-         return NULL;
-      }
-
-      free(DataIn.pbData);
-   }
-   else {
-      fprintf(stderr, "Failed to find map node: %s\n", kat_key);
-   }
-
-   SHA1_Update(&sha1_ctx, keys, klen);
-   SHA1_Update(&sha1_ctx, keysValue, kvlen);
-   SHA1_Final(sha1sum, &sha1_ctx);
-
-   pid = (char*)malloc(SHA_DIGEST_LENGTH * 2);
-   base64(sha1sum, SHA_DIGEST_LENGTH, pid);
-
-   pid[8] = 0;
-
-   free(mrn_key);
-   free(kat_key);
-   free(vsn);
-   free(username);
-
-   return pid;
-}
-
-static char *letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
-
-int verifyPidChecksum(char *pid) {
-   int l = strlen(letters);
-   unsigned int crc = ~do_crc(pid, 8);
-   unsigned char b;
-   crc = crc ^ (crc >> 16);
-   b = crc & 0xff;
-   if (pid[8] != letters[((b / l) ^ (b % l)) % l]) return 0;
-   crc >>= 8;
-   b = crc & 0xff;
-   if (pid[9] != letters[((b / l) ^ (b % l)) % l]) return 0;
-   return 1;
-}
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/skinutils.h
deleted file mode 100644 (file)
index 4b88c6d..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#ifndef __SKINUTILS_H
-#define __SKINUTILS_H
-
-typedef struct _PidList {
-   unsigned int numPids;
-   char *pidList[1];  //extra pids to try from command line
-} PidList;
-
-typedef struct _MapList {
-   char *key;
-   char *value;
-   struct _MapList *next;
-} MapList;
-
-extern MapList *kindleMap;
-
-unsigned int base64(unsigned char *inbuf, unsigned int len, unsigned char *outbuf);
-
-unsigned short bswap_s(unsigned short s);
-unsigned int bswap_l(unsigned int s);
-
-char *translateKindleKey(char *key);
-MapList *findNode(MapList *map, char *key);
-MapList *findKindleNode(char *key);
-
-//don't free the result of getNodeValue;
-char *getNodeValue(MapList *map, char *key);
-char *getKindleValue(char *key);
-
-MapList *addMapNode(MapList *map, char *key, char *value);
-void dumpMap(MapList *m);
-
-void freeMap(MapList *m);
-
-int buildKindleMap(char *infoFile);
-void dumpKindleMap();
-
-//void png_crc_table_init(unsigned int *crc_table);
-unsigned int do_crc(unsigned char *input, unsigned int len);
-void doPngDecode(unsigned char *input, unsigned int len, unsigned char *output);
-
-char *mazamaEncode(unsigned char *input, unsigned int len, unsigned char choice);
-char *mazamaEncode32(unsigned char *input, unsigned int len);
-char *mazamaEncode64(unsigned char *input, unsigned int len);
-
-unsigned char *mazamaDecode(char *input, int *outlen);
-
-int verifyPidChecksum(char *pid);
-
-//If you prefer to use openssl uncomment the following
-//#include <openssl/sha.h>
-//#include <openssl/md5.h>
-
-#ifndef HEADER_MD5_H
-#include "md5.h"
-
-#define MD5_DIGEST_LENGTH 16
-
-#define MD5_CTX md5_state_t
-#define MD5_Init md5_init
-#define MD5_Update md5_append
-#define MD5_Final(x, y) md5_finish(y, x)
-#define MD5 md5
-
-void md5(unsigned char *in, int len, unsigned char *md);
-#endif
-
-#ifndef HEADER_SHA_H
-
-#include "sha1.h"
-
-#define SHA_DIGEST_LENGTH 20
-#define SHA_CTX sha1_state_s
-#define SHA1_Init sha1_init
-#define SHA1_Update sha1_update
-#define SHA1_Final(x, y) sha1_finish(y, x)
-#define SHA1 sha1
-
-void sha1(unsigned char *in, int len, unsigned char *md);
-#endif
-
-char *getBookPid(unsigned char *keys, unsigned int klen, unsigned char *keysValue, unsigned int kvlen);
-
-#endif
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.c b/Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.c
deleted file mode 100644 (file)
index 564f31a..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "skinutils.h"
-#include "cbuf.h"
-#include "tpz.h"
-#include "zlib.h"
-
-//
-// Context initialisation for the Topaz Crypto
-//
-void topazCryptoInit(TpzCtx *ctx, unsigned char *key, int klen) {
-   int i = 0; 
-   ctx->v[0] = 0x0CAFFE19E;
-    
-   for (i = 0; i < klen; i++) {
-      ctx->v[1] = ctx->v[0]; 
-      ctx->v[0] = ((ctx->v[0] >> 2) * (ctx->v[0] >> 7)) ^  
-                  (key[i] * key[i] * 0x0F902007);
-   }
-}
-
-//
-// decrypt data with the context prepared by topazCryptoInit()
-//
-    
-void topazCryptoDecrypt(TpzCtx *ctx, unsigned char *in, unsigned char *out, int len) {
-   unsigned int ctx1 = ctx->v[0];
-   unsigned int ctx2 = ctx->v[1];
-   int i;
-   for (i = 0; i < len; i++) {
-      unsigned char m = in[i] ^ (ctx1 >> 3) ^ (ctx2 << 3);
-      ctx2 = ctx1;
-      ctx1 = ((ctx1 >> 2) * (ctx1 >> 7)) ^ (m * m * 0x0F902007);
-      out[i] = m;
-   }
-}
-
-int bookReadEncodedNumber(FILE *f) {
-   int flag = 0;
-   int data = fgetc(f);
-   if (data == 0xFF) {  //negative number flag
-      flag = 1;
-      data = fgetc(f);
-   }
-   if (data >= 0x80) {
-      int datax = data & 0x7F;
-      while (data >= 0x80) {
-         data = fgetc(f);
-         datax = (datax << 7) + (data & 0x7F);
-      }
-      data = datax;
-   }
-   
-   if (flag) {
-      data = -data;
-   }
-   return data;
-}
-
-//
-// Encode a number in 7 bit format
-//
-
-int encodeNumber(int number, unsigned char *out) {
-   unsigned char *b = out;
-   unsigned char flag = 0;
-   int len;
-   int neg = number < 0;
-   
-   if (neg) {
-      number = -number + 1;
-   }
-   
-   do {
-      *b++ = (number & 0x7F) | flag;
-      number >>= 7;
-      flag = 0x80;
-   } while (number);  
-
-   if (neg) {
-      *b++ = 0xFF;
-   }
-   len = b - out;
-   b--;
-   while (out < b) {
-      unsigned char v = *out;
-      *out++ = *b;
-      *b-- = v;
-   } 
-   return len;
-}
-
-//
-// Get a length prefixed string from the file 
-//
-
-char *bookReadString(FILE *f) {
-   int len = bookReadEncodedNumber(f);
-   char *s = (char*)malloc(len + 1);
-   s[len] = 0;
-   if (fread(s, 1, len, f) != len) {
-      fprintf(stderr, "String read failed at filepos %x\n", ftell(f));
-      free(s);
-      s = NULL;
-   }
-   return s;
-}
-    
-//
-// Read and return the data of one header record at the current book file position [[offset,compressedLength,decompressedLength],...]
-//
-
-Record *bookReadHeaderRecordData(FILE *f) {
-   int nbValues = bookReadEncodedNumber(f);
-   Record *result = NULL;
-   Record *tail = NULL;
-   unsigned int i;
-   if (nbValues == -1) {
-      fprintf(stderr, "Parse Error : EOF encountered\n");
-      return NULL;
-   }
-   for (i = 0; i < nbValues; i++) {
-      Record *r = (Record*)malloc(sizeof(Record));
-      r->offset = bookReadEncodedNumber(f);
-      r->length = bookReadEncodedNumber(f);
-      r->compressed = bookReadEncodedNumber(f);
-      r->next = NULL;
-      if (result == NULL) {
-         result = r;
-      }
-      else {
-         tail->next = r;
-      }
-      tail = r;
-   }
-   return result;
-}
-
-//
-// Read and parse one header record at the current book file position and return the associated data [[offset,compressedLength,decompressedLength],...]
-//
-
-void freeRecordList(Record *r) {
-   Record *n;
-   while (r) {
-      n = r;
-      r = r->next;
-      free(n);
-   }
-}
-
-void freeHeaderList(HeaderRecord *r) {
-   HeaderRecord *n;
-   while (r) {
-      free(r->tag);
-      freeRecordList(r->rec);
-      n = r;
-      r = r->next;
-      free(n);
-   }
-}
-
-void freeTopazFile(TopazFile *t) {
-   freeHeaderList(t->hdrs);
-   freeMap(t->metadata);
-   free(t);
-}
-
-HeaderRecord *parseTopazHeaderRecord(FILE *f) {
-   char *tag;
-   Record *record;
-   if (fgetc(f) != 0x63) {
-      fprintf(stderr, "Parse Error : Invalid Header at 0x%x\n", ftell(f) - 1);
-      return NULL;
-   }
-    
-   tag = bookReadString(f);
-   record = bookReadHeaderRecordData(f);
-   if (tag && record) {
-      HeaderRecord *r = (HeaderRecord*)malloc(sizeof(Record));
-      r->tag = tag;
-      r->rec = record;
-      r->next = NULL;
-      return r;
-   }
-   return NULL;
-}
-
-//
-// Parse the header of a Topaz file, get all the header records and the offset for the payload
-//
-HeaderRecord *addRecord(HeaderRecord *head, HeaderRecord *r) {
-   HeaderRecord *i;
-   for (i = head; i; i = i->next) {
-      if (i->next == NULL) {
-         i->next = r;
-         return head;
-      }
-   }
-   return r;
-}
-
-TopazFile *parseTopazHeader(FILE *f) {
-   unsigned int numRecs, i, magic;
-   TopazFile *tpz;
-   if (fread(&magic, sizeof(magic), 1, f) != 1) {
-      fprintf(stderr, "Failed to read file magic\n");
-      return NULL;
-   }
-   
-   if (magic != 0x305a5054) {
-      fprintf(stderr, "Parse Error : Invalid Header, not a Topaz file");
-      return NULL;
-   }
-   
-   numRecs = fgetc(f);
-
-   tpz = (TopazFile*)calloc(sizeof(TopazFile), 1);
-   tpz->f = f;
-
-   for (i = 0; i < numRecs; i++) {
-      HeaderRecord *result = parseTopazHeaderRecord(f);
-      if (result == NULL) {
-         break;
-      }
-      tpz->hdrs = addRecord(tpz->hdrs, result);
-   }
-   
-   if (fgetc(f) != 0x64) {
-      fprintf(stderr, "Parse Error : Invalid Header end at pos 0x%x\n", ftell(f) - 1);
-      //empty list
-      freeTopazFile(tpz);
-      return NULL;
-   }
-   
-   tpz->bodyOffset = ftell(f);
-   return tpz;
-}
-
-HeaderRecord *findHeader(TopazFile *tpz, char *tag) {
-   HeaderRecord *hr;
-   for (hr = tpz->hdrs; hr; hr = hr->next) {
-      if (strcmp(hr->tag, tag) == 0) {
-         break;
-      }
-   }
-   return hr;
-}
-
-void freePayload(Payload *p) {
-   free(p->blob);
-   free(p);
-}
-
-//
-//Get a record in the book payload, given its name and index. If necessary the record is decrypted. The record is not decompressed
-//
-Payload *getBookPayloadRecord(TopazFile *t, char *name, int index, int explode) {   
-   int encrypted = 0;
-   int recordOffset, i, recordIndex;
-   Record *r;
-   int fileSize;
-   char *tag;
-   Payload *p;
-   off_t fileOffset;
-   HeaderRecord *hr = findHeader(t, name);
-
-   if (hr == NULL) {
-      fprintf(stderr, "Parse Error : Invalid Record, record %s not found\n", name);
-      return NULL;
-   }
-   r = hr->rec;
-   for (i = 0; r && i < index; i++) {
-      r = r->next;
-   }
-   if (r == NULL) {
-      fprintf(stderr, "Parse Error : Invalid Record, record %s:%d not found\n", name, index);
-      return NULL;
-   }
-   recordOffset = r->offset;
-    
-   if (fseek(t->f, t->bodyOffset + recordOffset, SEEK_SET) == -1) {
-      fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d\n", name, index);
-      return NULL;
-   }
-    
-   tag = bookReadString(t->f);
-   if (strcmp(tag, name)) {
-      fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d name doesn't match\n", name, index);
-      return NULL;
-   }    
-   recordIndex = bookReadEncodedNumber(t->f);
-    
-   if (recordIndex < 0) {
-      encrypted = 1;
-      recordIndex = -recordIndex - 1;
-   }
-    
-   if (recordIndex != index) {
-      fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d index doesn't match\n", name, index);
-      return NULL;
-   }
-   
-   fileSize = r->compressed ? r->compressed : r->length;
-   p = (Payload*)malloc(sizeof(Payload));
-   p->blob = (unsigned char*)malloc(fileSize);
-   p->len = fileSize;
-   p->name = name;
-   p->index = index;
-   fileOffset = ftell(t->f);
-   if (fread(p->blob, fileSize, 1, t->f) != 1) {
-      freePayload(p);
-      fprintf(stderr, "Parse Error : Failed payload read of record %s:%d offset 0x%x:0x%x\n", name, index, fileOffset, fileSize);
-      return NULL;
-   }     
-   
-   if (encrypted) {
-      TpzCtx ctx;
-      topazCryptoInit(&ctx, t->bookKey, 8);
-      topazCryptoDecrypt(&ctx, p->blob, p->blob, p->len);
-   }
-   
-   if (r->compressed && explode) {
-      unsigned char *db = (unsigned char *)malloc(r->length);
-      uLongf dl = r->length;
-      switch (uncompress(db, &dl, p->blob, p->len)) {
-         case Z_OK:
-            free(p->blob);
-            p->blob = db;
-            p->len = dl;
-            break;
-         case Z_MEM_ERROR:
-            free(db);
-            fprintf(stderr, "out of memory\n");
-            break;         
-         case Z_BUF_ERROR:
-            free(db);
-            fprintf(stderr, "output buffer wasn't large enough!\n");
-            break;
-      }
-   }
-   
-   return p;
-}
-
-//
-// Parse the metadata record from the book payload and return a list of [key,values]
-//
-
-char *getMetadata(TopazFile *t, char *key) {
-   return getNodeValue(t->metadata, key);
-}
-
-void parseMetadata(TopazFile *t) {
-   char *tag;
-   int flags, nbRecords, i;
-   HeaderRecord *hr = findHeader(t, "metadata");
-    
-   fseek(t->f, t->bodyOffset + hr->rec->offset, SEEK_SET);
-   tag = bookReadString(t->f);
-   if (strcmp(tag, "metadata")) {
-      //raise CMBDTCFatal("Parse Error : Record Names Don't Match")
-      return;
-   }
-    
-   flags = fgetc(t->f);
-   nbRecords = bookReadEncodedNumber(t->f);
-    
-   for (i = 0; i < nbRecords; i++) {
-      char *key = bookReadString(t->f);
-      char *value = bookReadString(t->f);
-      t->metadata = addMapNode(t->metadata, key, value);
-   }
-}
-
-//
-// Decrypt a payload record with the PID
-//
-
-void decryptRecord(unsigned char *in, int len, unsigned char *out, unsigned char *PID) {
-   TpzCtx ctx;
-   topazCryptoInit(&ctx, PID, 8);  //is this length correct
-   topazCryptoDecrypt(&ctx, in, out, len);
-}
-
-//
-// Try to decrypt a dkey record (contains the book PID)
-//
-unsigned char *decryptDkeyRecord(unsigned char *data, int len, unsigned char *PID) {
-   decryptRecord(data, len, data, PID);
-   //fields = unpack("3sB8sB8s3s",record);
-    
-   if (strncmp(data, "PID", 3) || strncmp(data + 21, "pid", 3)) {
-      fprintf(stderr, "Didn't find PID magic numbers in record\n");
-      return NULL;
-   }
-   else if (data[3] != 8 || data[12] != 8) {
-      fprintf(stderr, "Record didn't contain correct length fields\n");
-      return NULL;
-   }
-   else if (strncmp(data + 4, PID, 8)) {
-      fprintf(stderr, "Record didn't contain PID\n");
-      return NULL;
-   }
-   return data + 13;
-}
-    
-//
-// Decrypt all the book's dkey records (contain the book PID)
-//
-unsigned char *decryptDkeyRecords(Payload *data, unsigned char *PID) {
-   int nbKeyRecords = data->blob[0];  //is this encoded number?
-   int i, idx;
-   idx = 1;
-   unsigned char *key = NULL;
-//   records = []
-   for (i = 0; i < nbKeyRecords && idx < data->len; i++) {
-      int length = data->blob[idx++];
-      key = decryptDkeyRecord(data->blob + idx, length, PID);
-      if (key) break;    //???
-      idx += length;
-   }
-   return key;
-}
-
-void bufEncodeInt(cbuf *b, int i) {
-   unsigned char encoded[16];
-   int len = encodeNumber(i, encoded);
-   b_add_buf(b, encoded, len);
-}
-
-void bufEncodeString(cbuf *b, char *s) {
-   bufEncodeInt(b, strlen(s));
-   b_add_str(b, s);
-}
-
-void writeTopazOutputFile(TopazFile *t, FILE *out, cbuf *tpzHeaders, 
-                          cbuf *tpzBody, int explode) {
-   int i, numHdrs = 0;
-   HeaderRecord *h;
-   b_add_str(tpzHeaders, "TPZ0");
-   for (h = t->hdrs; h; h = h->next) {
-      if (strcmp(h->tag, "dkey")) {
-         numHdrs++;
-      }
-   }
-   bufEncodeInt(tpzHeaders, numHdrs);
-
-   b_add_byte(tpzBody, 0x40);
-
-   for (h = t->hdrs; h; h = h->next) {
-      Record *r;
-      int nr = 0, idx = 0;
-      if (strcmp(h->tag, "dkey") == 0) continue;
-      b_add_byte(tpzHeaders, 0x63);
-      bufEncodeString(tpzHeaders, h->tag);
-      for (r = h->rec; r; r = r->next) nr++;
-      bufEncodeInt(tpzHeaders, nr);
-      for (r = h->rec; r; r = r->next) {
-         Payload *p;
-         int b, e;
-         bufEncodeInt(tpzHeaders, tpzBody->idx);         
-         bufEncodeString(tpzBody, h->tag);
-         bufEncodeInt(tpzBody, idx);
-         b = tpzBody->idx;
-         p = getBookPayloadRecord(t, h->tag, idx++, explode);
-         b_add_buf(tpzBody, p->blob, p->len);
-         e = tpzBody->idx;
-
-         bufEncodeInt(tpzHeaders, r->length);   //this is length of blob portion after decompression
-         if (explode) {
-            bufEncodeInt(tpzHeaders, 0); //this is the length in the file if compressed
-         }
-         else {
-            bufEncodeInt(tpzHeaders, r->compressed); //this is the length in the file if compressed
-         }
-         
-         freePayload(p);
-      }
-   }
-   
-   b_add_byte(tpzHeaders, 0x64);
-}
-
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/tpz.h
deleted file mode 100644 (file)
index 4138171..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-   Copyright 2010 BartSimpson aka skindle
-   
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-#ifndef __TPZ_H
-#define __TPZ_H
-
-#include <stdio.h>
-#include "skinutils.h"
-
-typedef struct _TpzCtx {
-   unsigned int v[2];
-} TpzCtx;
-
-void topazCryptoInit(TpzCtx *ctx, unsigned char *key, int klen);
-void topazCryptoDecrypt(TpzCtx *ctx, unsigned char *in, unsigned char *out, int len);
-int bookReadEncodedNumber(FILE *f);
-int encodeNumber(int number, unsigned char *out);
-char *bookReadString(FILE *f);
-
-typedef struct _Payload {
-   unsigned char *blob;
-   unsigned int len;
-   char *name;
-   int index;
-} Payload;
-
-typedef struct _Record {
-   int offset;
-   int length;
-   int compressed;
-   struct _Record *next;
-} Record;
-
-typedef struct _HeaderRecord {
-   char *tag;
-   Record *rec;
-   struct _HeaderRecord *next;
-} HeaderRecord;
-
-typedef struct _TopazFile {
-   FILE *f;
-   HeaderRecord *hdrs;
-   unsigned char *bookKey;
-   unsigned int bodyOffset;
-   MapList *metadata;
-   PidList *pids;    //extra pids to try from command line
-} TopazFile;
-
-Record *bookReadHeaderRecordData(FILE *f);
-void freeRecordList(Record *r);
-void freeHeaderList(HeaderRecord *r);
-void freeTopazFile(TopazFile *t);
-HeaderRecord *parseTopazHeaderRecord(FILE *f);
-HeaderRecord *addRecord(HeaderRecord *head, HeaderRecord *r);
-TopazFile *parseTopazHeader(FILE *f);
-void freeTopazFile(TopazFile *tpz);
-HeaderRecord *findHeader(TopazFile *tpz, char *tag);
-void freePayload(Payload *p);
-Payload *getBookPayloadRecord(TopazFile *t, char *name, int index, int explode);
-char *getMetadata(TopazFile *t, char *key);
-void parseMetadata(TopazFile *t);
-void decryptRecord(unsigned char *in, int len, unsigned char *out, unsigned char *PID);
-unsigned char *decryptDkeyRecord(unsigned char *data, int len, unsigned char *PID);
-unsigned char *decryptDkeyRecords(Payload *data, unsigned char *PID);
-void writeTopazOutputFile(TopazFile *t, FILE *out, cbuf *tpzHeaders, 
-                          cbuf *tpzBody, int explode);
-
-
-#endif
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/zconf.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/zconf.h
deleted file mode 100644 (file)
index 03a9431..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-/* zconf.h -- configuration of the zlib compression library
- * Copyright (C) 1995-2005 Jean-loup Gailly.
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* @(#) $Id$ */
-
-#ifndef ZCONF_H
-#define ZCONF_H
-
-/*
- * If you *really* need a unique prefix for all types and library functions,
- * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
- */
-#ifdef Z_PREFIX
-#  define deflateInit_          z_deflateInit_
-#  define deflate               z_deflate
-#  define deflateEnd            z_deflateEnd
-#  define inflateInit_          z_inflateInit_
-#  define inflate               z_inflate
-#  define inflateEnd            z_inflateEnd
-#  define deflateInit2_         z_deflateInit2_
-#  define deflateSetDictionary  z_deflateSetDictionary
-#  define deflateCopy           z_deflateCopy
-#  define deflateReset          z_deflateReset
-#  define deflateParams         z_deflateParams
-#  define deflateBound          z_deflateBound
-#  define deflatePrime          z_deflatePrime
-#  define inflateInit2_         z_inflateInit2_
-#  define inflateSetDictionary  z_inflateSetDictionary
-#  define inflateSync           z_inflateSync
-#  define inflateSyncPoint      z_inflateSyncPoint
-#  define inflateCopy           z_inflateCopy
-#  define inflateReset          z_inflateReset
-#  define inflateBack           z_inflateBack
-#  define inflateBackEnd        z_inflateBackEnd
-#  define compress              z_compress
-#  define compress2             z_compress2
-#  define compressBound         z_compressBound
-#  define uncompress            z_uncompress
-#  define adler32               z_adler32
-#  define crc32                 z_crc32
-#  define get_crc_table         z_get_crc_table
-#  define zError                z_zError
-
-#  define alloc_func            z_alloc_func
-#  define free_func             z_free_func
-#  define in_func               z_in_func
-#  define out_func              z_out_func
-#  define Byte                  z_Byte
-#  define uInt                  z_uInt
-#  define uLong                 z_uLong
-#  define Bytef                 z_Bytef
-#  define charf                 z_charf
-#  define intf                  z_intf
-#  define uIntf                 z_uIntf
-#  define uLongf                z_uLongf
-#  define voidpf                z_voidpf
-#  define voidp                 z_voidp
-#endif
-
-#if defined(__MSDOS__) && !defined(MSDOS)
-#  define MSDOS
-#endif
-#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
-#  define OS2
-#endif
-#if defined(_WINDOWS) && !defined(WINDOWS)
-#  define WINDOWS
-#endif
-#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
-#  ifndef WIN32
-#    define WIN32
-#  endif
-#endif
-#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
-#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
-#    ifndef SYS16BIT
-#      define SYS16BIT
-#    endif
-#  endif
-#endif
-
-/*
- * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
- * than 64k bytes at a time (needed on systems with 16-bit int).
- */
-#ifdef SYS16BIT
-#  define MAXSEG_64K
-#endif
-#ifdef MSDOS
-#  define UNALIGNED_OK
-#endif
-
-#ifdef __STDC_VERSION__
-#  ifndef STDC
-#    define STDC
-#  endif
-#  if __STDC_VERSION__ >= 199901L
-#    ifndef STDC99
-#      define STDC99
-#    endif
-#  endif
-#endif
-#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
-#  define STDC
-#endif
-#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
-#  define STDC
-#endif
-#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
-#  define STDC
-#endif
-#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
-#  define STDC
-#endif
-
-#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
-#  define STDC
-#endif
-
-#ifndef STDC
-#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
-#    define const       /* note: need a more gentle solution here */
-#  endif
-#endif
-
-/* Some Mac compilers merge all .h files incorrectly: */
-#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
-#  define NO_DUMMY_DECL
-#endif
-
-/* Maximum value for memLevel in deflateInit2 */
-#ifndef MAX_MEM_LEVEL
-#  ifdef MAXSEG_64K
-#    define MAX_MEM_LEVEL 8
-#  else
-#    define MAX_MEM_LEVEL 9
-#  endif
-#endif
-
-/* Maximum value for windowBits in deflateInit2 and inflateInit2.
- * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
- * created by gzip. (Files created by minigzip can still be extracted by
- * gzip.)
- */
-#ifndef MAX_WBITS
-#  define MAX_WBITS   15 /* 32K LZ77 window */
-#endif
-
-/* The memory requirements for deflate are (in bytes):
-            (1 << (windowBits+2)) +  (1 << (memLevel+9))
- that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
- plus a few kilobytes for small objects. For example, if you want to reduce
- the default memory requirements from 256K to 128K, compile with
-     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
- Of course this will generally degrade compression (there's no free lunch).
-
-   The memory requirements for inflate are (in bytes) 1 << windowBits
- that is, 32K for windowBits=15 (default value) plus a few kilobytes
- for small objects.
-*/
-
-                        /* Type declarations */
-
-#ifndef OF /* function prototypes */
-#  ifdef STDC
-#    define OF(args)  args
-#  else
-#    define OF(args)  ()
-#  endif
-#endif
-
-/* The following definitions for FAR are needed only for MSDOS mixed
- * model programming (small or medium model with some far allocations).
- * This was tested only with MSC; for other MSDOS compilers you may have
- * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
- * just define FAR to be empty.
- */
-#ifdef SYS16BIT
-#  if defined(M_I86SM) || defined(M_I86MM)
-     /* MSC small or medium model */
-#    define SMALL_MEDIUM
-#    ifdef _MSC_VER
-#      define FAR _far
-#    else
-#      define FAR far
-#    endif
-#  endif
-#  if (defined(__SMALL__) || defined(__MEDIUM__))
-     /* Turbo C small or medium model */
-#    define SMALL_MEDIUM
-#    ifdef __BORLANDC__
-#      define FAR _far
-#    else
-#      define FAR far
-#    endif
-#  endif
-#endif
-
-#if defined(WINDOWS) || defined(WIN32)
-   /* If building or using zlib as a DLL, define ZLIB_DLL.
-    * This is not mandatory, but it offers a little performance increase.
-    */
-#  ifdef ZLIB_DLL
-#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
-#      ifdef ZLIB_INTERNAL
-#        define ZEXTERN extern __declspec(dllexport)
-#      else
-#        define ZEXTERN extern __declspec(dllimport)
-#      endif
-#    endif
-#  endif  /* ZLIB_DLL */
-   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
-    * define ZLIB_WINAPI.
-    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
-    */
-#  ifdef ZLIB_WINAPI
-#    ifdef FAR
-#      undef FAR
-#    endif
-#    include <windows.h>
-     /* No need for _export, use ZLIB.DEF instead. */
-     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
-#    define ZEXPORT WINAPI
-#    ifdef WIN32
-#      define ZEXPORTVA WINAPIV
-#    else
-#      define ZEXPORTVA FAR CDECL
-#    endif
-#  endif
-#endif
-
-#if defined (__BEOS__)
-#  ifdef ZLIB_DLL
-#    ifdef ZLIB_INTERNAL
-#      define ZEXPORT   __declspec(dllexport)
-#      define ZEXPORTVA __declspec(dllexport)
-#    else
-#      define ZEXPORT   __declspec(dllimport)
-#      define ZEXPORTVA __declspec(dllimport)
-#    endif
-#  endif
-#endif
-
-#ifndef ZEXTERN
-#  define ZEXTERN extern
-#endif
-#ifndef ZEXPORT
-#  define ZEXPORT
-#endif
-#ifndef ZEXPORTVA
-#  define ZEXPORTVA
-#endif
-
-#ifndef FAR
-#  define FAR
-#endif
-
-#if !defined(__MACTYPES__)
-typedef unsigned char  Byte;  /* 8 bits */
-#endif
-typedef unsigned int   uInt;  /* 16 bits or more */
-typedef unsigned long  uLong; /* 32 bits or more */
-
-#ifdef SMALL_MEDIUM
-   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
-#  define Bytef Byte FAR
-#else
-   typedef Byte  FAR Bytef;
-#endif
-typedef char  FAR charf;
-typedef int   FAR intf;
-typedef uInt  FAR uIntf;
-typedef uLong FAR uLongf;
-
-#ifdef STDC
-   typedef void const *voidpc;
-   typedef void FAR   *voidpf;
-   typedef void       *voidp;
-#else
-   typedef Byte const *voidpc;
-   typedef Byte FAR   *voidpf;
-   typedef Byte       *voidp;
-#endif
-
-#if 0           /* HAVE_UNISTD_H -- this line is updated by ./configure */
-#  include <sys/types.h> /* for off_t */
-#  include <unistd.h>    /* for SEEK_* and off_t */
-#  ifdef VMS
-#    include <unixio.h>   /* for off_t */
-#  endif
-#  define z_off_t off_t
-#endif
-#ifndef SEEK_SET
-#  define SEEK_SET        0       /* Seek from beginning of file.  */
-#  define SEEK_CUR        1       /* Seek from current position.  */
-#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
-#endif
-#ifndef z_off_t
-#  define z_off_t long
-#endif
-
-#if defined(__OS400__)
-#  define NO_vsnprintf
-#endif
-
-#if defined(__MVS__)
-#  define NO_vsnprintf
-#  ifdef FAR
-#    undef FAR
-#  endif
-#endif
-
-/* MVS linker does not support external names larger than 8 bytes */
-#if defined(__MVS__)
-#   pragma map(deflateInit_,"DEIN")
-#   pragma map(deflateInit2_,"DEIN2")
-#   pragma map(deflateEnd,"DEEND")
-#   pragma map(deflateBound,"DEBND")
-#   pragma map(inflateInit_,"ININ")
-#   pragma map(inflateInit2_,"ININ2")
-#   pragma map(inflateEnd,"INEND")
-#   pragma map(inflateSync,"INSY")
-#   pragma map(inflateSetDictionary,"INSEDI")
-#   pragma map(compressBound,"CMBND")
-#   pragma map(inflate_table,"INTABL")
-#   pragma map(inflate_fast,"INFA")
-#   pragma map(inflate_copyright,"INCOPY")
-#endif
-
-#endif /* ZCONF_H */
diff --git a/Kindle_4_PC_Tools/Other_Tools/skindle-06/zlib.h b/Kindle_4_PC_Tools/Other_Tools/skindle-06/zlib.h
deleted file mode 100644 (file)
index 0228179..0000000
+++ /dev/null
@@ -1,1357 +0,0 @@
-/* zlib.h -- interface of the 'zlib' general purpose compression library
-  version 1.2.3, July 18th, 2005
-
-  Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  Jean-loup Gailly        Mark Adler
-  jloup@gzip.org          madler@alumni.caltech.edu
-
-
-  The data format used by the zlib library is described by RFCs (Request for
-  Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
-  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
-*/
-
-#ifndef ZLIB_H
-#define ZLIB_H
-
-#include "zconf.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ZLIB_VERSION "1.2.3"
-#define ZLIB_VERNUM 0x1230
-
-/*
-     The 'zlib' compression library provides in-memory compression and
-  decompression functions, including integrity checks of the uncompressed
-  data.  This version of the library supports only one compression method
-  (deflation) but other algorithms will be added later and will have the same
-  stream interface.
-
-     Compression can be done in a single step if the buffers are large
-  enough (for example if an input file is mmap'ed), or can be done by
-  repeated calls of the compression function.  In the latter case, the
-  application must provide more input and/or consume the output
-  (providing more output space) before each call.
-
-     The compressed data format used by default by the in-memory functions is
-  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
-  around a deflate stream, which is itself documented in RFC 1951.
-
-     The library also supports reading and writing files in gzip (.gz) format
-  with an interface similar to that of stdio using the functions that start
-  with "gz".  The gzip format is different from the zlib format.  gzip is a
-  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
-
-     This library can optionally read and write gzip streams in memory as well.
-
-     The zlib format was designed to be compact and fast for use in memory
-  and on communications channels.  The gzip format was designed for single-
-  file compression on file systems, has a larger header than zlib to maintain
-  directory information, and uses a different, slower check method than zlib.
-
-     The library does not install any signal handler. The decoder checks
-  the consistency of the compressed data, so the library should never
-  crash even in case of corrupted input.
-*/
-
-typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
-typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
-
-struct internal_state;
-
-typedef struct z_stream_s {
-    Bytef    *next_in;  /* next input byte */
-    uInt     avail_in;  /* number of bytes available at next_in */
-    uLong    total_in;  /* total nb of input bytes read so far */
-
-    Bytef    *next_out; /* next output byte should be put there */
-    uInt     avail_out; /* remaining free space at next_out */
-    uLong    total_out; /* total nb of bytes output so far */
-
-    char     *msg;      /* last error message, NULL if no error */
-    struct internal_state FAR *state; /* not visible by applications */
-
-    alloc_func zalloc;  /* used to allocate the internal state */
-    free_func  zfree;   /* used to free the internal state */
-    voidpf     opaque;  /* private data object passed to zalloc and zfree */
-
-    int     data_type;  /* best guess about the data type: binary or text */
-    uLong   adler;      /* adler32 value of the uncompressed data */
-    uLong   reserved;   /* reserved for future use */
-} z_stream;
-
-typedef z_stream FAR *z_streamp;
-
-/*
-     gzip header information passed to and from zlib routines.  See RFC 1952
-  for more details on the meanings of these fields.
-*/
-typedef struct gz_header_s {
-    int     text;       /* true if compressed data believed to be text */
-    uLong   time;       /* modification time */
-    int     xflags;     /* extra flags (not used when writing a gzip file) */
-    int     os;         /* operating system */
-    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
-    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
-    uInt    extra_max;  /* space at extra (only when reading header) */
-    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
-    uInt    name_max;   /* space at name (only when reading header) */
-    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
-    uInt    comm_max;   /* space at comment (only when reading header) */
-    int     hcrc;       /* true if there was or will be a header crc */
-    int     done;       /* true when done reading gzip header (not used
-                           when writing a gzip file) */
-} gz_header;
-
-typedef gz_header FAR *gz_headerp;
-
-/*
-   The application must update next_in and avail_in when avail_in has
-   dropped to zero. It must update next_out and avail_out when avail_out
-   has dropped to zero. The application must initialize zalloc, zfree and
-   opaque before calling the init function. All other fields are set by the
-   compression library and must not be updated by the application.
-
-   The opaque value provided by the application will be passed as the first
-   parameter for calls of zalloc and zfree. This can be useful for custom
-   memory management. The compression library attaches no meaning to the
-   opaque value.
-
-   zalloc must return Z_NULL if there is not enough memory for the object.
-   If zlib is used in a multi-threaded application, zalloc and zfree must be
-   thread safe.
-
-   On 16-bit systems, the functions zalloc and zfree must be able to allocate
-   exactly 65536 bytes, but will not be required to allocate more than this
-   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
-   pointers returned by zalloc for objects of exactly 65536 bytes *must*
-   have their offset normalized to zero. The default allocation function
-   provided by this library ensures this (see zutil.c). To reduce memory
-   requirements and avoid any allocation of 64K objects, at the expense of
-   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
-
-   The fields total_in and total_out can be used for statistics or
-   progress reports. After compression, total_in holds the total size of
-   the uncompressed data and may be saved for use in the decompressor
-   (particularly if the decompressor wants to decompress everything in
-   a single step).
-*/
-
-                        /* constants */
-
-#define Z_NO_FLUSH      0
-#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
-#define Z_SYNC_FLUSH    2
-#define Z_FULL_FLUSH    3
-#define Z_FINISH        4
-#define Z_BLOCK         5
-/* Allowed flush values; see deflate() and inflate() below for details */
-
-#define Z_OK            0
-#define Z_STREAM_END    1
-#define Z_NEED_DICT     2
-#define Z_ERRNO        (-1)
-#define Z_STREAM_ERROR (-2)
-#define Z_DATA_ERROR   (-3)
-#define Z_MEM_ERROR    (-4)
-#define Z_BUF_ERROR    (-5)
-#define Z_VERSION_ERROR (-6)
-/* Return codes for the compression/decompression functions. Negative
- * values are errors, positive values are used for special but normal events.
- */
-
-#define Z_NO_COMPRESSION         0
-#define Z_BEST_SPEED             1
-#define Z_BEST_COMPRESSION       9
-#define Z_DEFAULT_COMPRESSION  (-1)
-/* compression levels */
-
-#define Z_FILTERED            1
-#define Z_HUFFMAN_ONLY        2
-#define Z_RLE                 3
-#define Z_FIXED               4
-#define Z_DEFAULT_STRATEGY    0
-/* compression strategy; see deflateInit2() below for details */
-
-#define Z_BINARY   0
-#define Z_TEXT     1
-#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
-#define Z_UNKNOWN  2
-/* Possible values of the data_type field (though see inflate()) */
-
-#define Z_DEFLATED   8
-/* The deflate compression method (the only one supported in this version) */
-
-#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
-
-#define zlib_version zlibVersion()
-/* for compatibility with versions < 1.0.2 */
-
-                        /* basic functions */
-
-ZEXTERN const char * ZEXPORT zlibVersion OF((void));
-/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
-   If the first character differs, the library code actually used is
-   not compatible with the zlib.h header file used by the application.
-   This check is automatically made by deflateInit and inflateInit.
- */
-
-/*
-ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
-
-     Initializes the internal stream state for compression. The fields
-   zalloc, zfree and opaque must be initialized before by the caller.
-   If zalloc and zfree are set to Z_NULL, deflateInit updates them to
-   use default allocation functions.
-
-     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
-   1 gives best speed, 9 gives best compression, 0 gives no compression at
-   all (the input data is simply copied a block at a time).
-   Z_DEFAULT_COMPRESSION requests a default compromise between speed and
-   compression (currently equivalent to level 6).
-
-     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
-   enough memory, Z_STREAM_ERROR if level is not a valid compression level,
-   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
-   with the version assumed by the caller (ZLIB_VERSION).
-   msg is set to null if there is no error message.  deflateInit does not
-   perform any compression: this will be done by deflate().
-*/
-
-
-ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
-/*
-    deflate compresses as much data as possible, and stops when the input
-  buffer becomes empty or the output buffer becomes full. It may introduce some
-  output latency (reading input without producing any output) except when
-  forced to flush.
-
-    The detailed semantics are as follows. deflate performs one or both of the
-  following actions:
-
-  - Compress more input starting at next_in and update next_in and avail_in
-    accordingly. If not all input can be processed (because there is not
-    enough room in the output buffer), next_in and avail_in are updated and
-    processing will resume at this point for the next call of deflate().
-
-  - Provide more output starting at next_out and update next_out and avail_out
-    accordingly. This action is forced if the parameter flush is non zero.
-    Forcing flush frequently degrades the compression ratio, so this parameter
-    should be set only when necessary (in interactive applications).
-    Some output may be provided even if flush is not set.
-
-  Before the call of deflate(), the application should ensure that at least
-  one of the actions is possible, by providing more input and/or consuming
-  more output, and updating avail_in or avail_out accordingly; avail_out
-  should never be zero before the call. The application can consume the
-  compressed output when it wants, for example when the output buffer is full
-  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
-  and with zero avail_out, it must be called again after making room in the
-  output buffer because there might be more output pending.
-
-    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
-  decide how much data to accumualte before producing output, in order to
-  maximize compression.
-
-    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
-  flushed to the output buffer and the output is aligned on a byte boundary, so
-  that the decompressor can get all input data available so far. (In particular
-  avail_in is zero after the call if enough output space has been provided
-  before the call.)  Flushing may degrade compression for some compression
-  algorithms and so it should be used only when necessary.
-
-    If flush is set to Z_FULL_FLUSH, all output is flushed as with
-  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
-  restart from this point if previous compressed data has been damaged or if
-  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
-  compression.
-
-    If deflate returns with avail_out == 0, this function must be called again
-  with the same value of the flush parameter and more output space (updated
-  avail_out), until the flush is complete (deflate returns with non-zero
-  avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
-  avail_out is greater than six to avoid repeated flush markers due to
-  avail_out == 0 on return.
-
-    If the parameter flush is set to Z_FINISH, pending input is processed,
-  pending output is flushed and deflate returns with Z_STREAM_END if there
-  was enough output space; if deflate returns with Z_OK, this function must be
-  called again with Z_FINISH and more output space (updated avail_out) but no
-  more input data, until it returns with Z_STREAM_END or an error. After
-  deflate has returned Z_STREAM_END, the only possible operations on the
-  stream are deflateReset or deflateEnd.
-
-    Z_FINISH can be used immediately after deflateInit if all the compression
-  is to be done in a single step. In this case, avail_out must be at least
-  the value returned by deflateBound (see below). If deflate does not return
-  Z_STREAM_END, then it must be called again as described above.
-
-    deflate() sets strm->adler to the adler32 checksum of all input read
-  so far (that is, total_in bytes).
-
-    deflate() may update strm->data_type if it can make a good guess about
-  the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
-  binary. This field is only for information purposes and does not affect
-  the compression algorithm in any manner.
-
-    deflate() returns Z_OK if some progress has been made (more input
-  processed or more output produced), Z_STREAM_END if all input has been
-  consumed and all output has been produced (only when flush is set to
-  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
-  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
-  (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
-  fatal, and deflate() can be called again with more input and more output
-  space to continue compressing.
-*/
-
-
-ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
-/*
-     All dynamically allocated data structures for this stream are freed.
-   This function discards any unprocessed input and does not flush any
-   pending output.
-
-     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
-   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
-   prematurely (some input or output was discarded). In the error case,
-   msg may be set but then points to a static string (which must not be
-   deallocated).
-*/
-
-
-/*
-ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
-
-     Initializes the internal stream state for decompression. The fields
-   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
-   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
-   value depends on the compression method), inflateInit determines the
-   compression method from the zlib header and allocates all data structures
-   accordingly; otherwise the allocation will be deferred to the first call of
-   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
-   use default allocation functions.
-
-     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
-   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
-   version assumed by the caller.  msg is set to null if there is no error
-   message. inflateInit does not perform any decompression apart from reading
-   the zlib header if present: this will be done by inflate().  (So next_in and
-   avail_in may be modified, but next_out and avail_out are unchanged.)
-*/
-
-
-ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
-/*
-    inflate decompresses as much data as possible, and stops when the input
-  buffer becomes empty or the output buffer becomes full. It may introduce
-  some output latency (reading input without producing any output) except when
-  forced to flush.
-
-  The detailed semantics are as follows. inflate performs one or both of the
-  following actions:
-
-  - Decompress more input starting at next_in and update next_in and avail_in
-    accordingly. If not all input can be processed (because there is not
-    enough room in the output buffer), next_in is updated and processing
-    will resume at this point for the next call of inflate().
-
-  - Provide more output starting at next_out and update next_out and avail_out
-    accordingly.  inflate() provides as much output as possible, until there
-    is no more input data or no more space in the output buffer (see below
-    about the flush parameter).
-
-  Before the call of inflate(), the application should ensure that at least
-  one of the actions is possible, by providing more input and/or consuming
-  more output, and updating the next_* and avail_* values accordingly.
-  The application can consume the uncompressed output when it wants, for
-  example when the output buffer is full (avail_out == 0), or after each
-  call of inflate(). If inflate returns Z_OK and with zero avail_out, it
-  must be called again after making room in the output buffer because there
-  might be more output pending.
-
-    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
-  Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
-  output as possible to the output buffer. Z_BLOCK requests that inflate() stop
-  if and when it gets to the next deflate block boundary. When decoding the
-  zlib or gzip format, this will cause inflate() to return immediately after
-  the header and before the first block. When doing a raw inflate, inflate()
-  will go ahead and process the first block, and will return when it gets to
-  the end of that block, or when it runs out of data.
-
-    The Z_BLOCK option assists in appending to or combining deflate streams.
-  Also to assist in this, on return inflate() will set strm->data_type to the
-  number of unused bits in the last byte taken from strm->next_in, plus 64
-  if inflate() is currently decoding the last block in the deflate stream,
-  plus 128 if inflate() returned immediately after decoding an end-of-block
-  code or decoding the complete header up to just before the first byte of the
-  deflate stream. The end-of-block will not be indicated until all of the
-  uncompressed data from that block has been written to strm->next_out.  The
-  number of unused bits may in general be greater than seven, except when
-  bit 7 of data_type is set, in which case the number of unused bits will be
-  less than eight.
-
-    inflate() should normally be called until it returns Z_STREAM_END or an
-  error. However if all decompression is to be performed in a single step
-  (a single call of inflate), the parameter flush should be set to
-  Z_FINISH. In this case all pending input is processed and all pending
-  output is flushed; avail_out must be large enough to hold all the
-  uncompressed data. (The size of the uncompressed data may have been saved
-  by the compressor for this purpose.) The next operation on this stream must
-  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
-  is never required, but can be used to inform inflate that a faster approach
-  may be used for the single inflate() call.
-
-     In this implementation, inflate() always flushes as much output as
-  possible to the output buffer, and always uses the faster approach on the
-  first call. So the only effect of the flush parameter in this implementation
-  is on the return value of inflate(), as noted below, or when it returns early
-  because Z_BLOCK is used.
-
-     If a preset dictionary is needed after this call (see inflateSetDictionary
-  below), inflate sets strm->adler to the adler32 checksum of the dictionary
-  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
-  strm->adler to the adler32 checksum of all output produced so far (that is,
-  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
-  below. At the end of the stream, inflate() checks that its computed adler32
-  checksum is equal to that saved by the compressor and returns Z_STREAM_END
-  only if the checksum is correct.
-
-    inflate() will decompress and check either zlib-wrapped or gzip-wrapped
-  deflate data.  The header type is detected automatically.  Any information
-  contained in the gzip header is not retained, so applications that need that
-  information should instead use raw inflate, see inflateInit2() below, or
-  inflateBack() and perform their own processing of the gzip header and
-  trailer.
-
-    inflate() returns Z_OK if some progress has been made (more input processed
-  or more output produced), Z_STREAM_END if the end of the compressed data has
-  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
-  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
-  corrupted (input stream not conforming to the zlib format or incorrect check
-  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
-  if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
-  Z_BUF_ERROR if no progress is possible or if there was not enough room in the
-  output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
-  inflate() can be called again with more input and more output space to
-  continue decompressing. If Z_DATA_ERROR is returned, the application may then
-  call inflateSync() to look for a good compression block if a partial recovery
-  of the data is desired.
-*/
-
-
-ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
-/*
-     All dynamically allocated data structures for this stream are freed.
-   This function discards any unprocessed input and does not flush any
-   pending output.
-
-     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
-   was inconsistent. In the error case, msg may be set but then points to a
-   static string (which must not be deallocated).
-*/
-
-                        /* Advanced functions */
-
-/*
-    The following functions are needed only in some special applications.
-*/
-
-/*
-ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
-                                     int  level,
-                                     int  method,
-                                     int  windowBits,
-                                     int  memLevel,
-                                     int  strategy));
-
-     This is another version of deflateInit with more compression options. The
-   fields next_in, zalloc, zfree and opaque must be initialized before by
-   the caller.
-
-     The method parameter is the compression method. It must be Z_DEFLATED in
-   this version of the library.
-
-     The windowBits parameter is the base two logarithm of the window size
-   (the size of the history buffer). It should be in the range 8..15 for this
-   version of the library. Larger values of this parameter result in better
-   compression at the expense of memory usage. The default value is 15 if
-   deflateInit is used instead.
-
-     windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
-   determines the window size. deflate() will then generate raw deflate data
-   with no zlib header or trailer, and will not compute an adler32 check value.
-
-     windowBits can also be greater than 15 for optional gzip encoding. Add
-   16 to windowBits to write a simple gzip header and trailer around the
-   compressed data instead of a zlib wrapper. The gzip header will have no
-   file name, no extra data, no comment, no modification time (set to zero),
-   no header crc, and the operating system will be set to 255 (unknown).  If a
-   gzip stream is being written, strm->adler is a crc32 instead of an adler32.
-
-     The memLevel parameter specifies how much memory should be allocated
-   for the internal compression state. memLevel=1 uses minimum memory but
-   is slow and reduces compression ratio; memLevel=9 uses maximum memory
-   for optimal speed. The default value is 8. See zconf.h for total memory
-   usage as a function of windowBits and memLevel.
-
-     The strategy parameter is used to tune the compression algorithm. Use the
-   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
-   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
-   string match), or Z_RLE to limit match distances to one (run-length
-   encoding). Filtered data consists mostly of small values with a somewhat
-   random distribution. In this case, the compression algorithm is tuned to
-   compress them better. The effect of Z_FILTERED is to force more Huffman
-   coding and less string matching; it is somewhat intermediate between
-   Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
-   Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
-   parameter only affects the compression ratio but not the correctness of the
-   compressed output even if it is not set appropriately.  Z_FIXED prevents the
-   use of dynamic Huffman codes, allowing for a simpler decoder for special
-   applications.
-
-      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
-   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
-   method). msg is set to null if there is no error message.  deflateInit2 does
-   not perform any compression: this will be done by deflate().
-*/
-
-ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
-                                             const Bytef *dictionary,
-                                             uInt  dictLength));
-/*
-     Initializes the compression dictionary from the given byte sequence
-   without producing any compressed output. This function must be called
-   immediately after deflateInit, deflateInit2 or deflateReset, before any
-   call of deflate. The compressor and decompressor must use exactly the same
-   dictionary (see inflateSetDictionary).
-
-     The dictionary should consist of strings (byte sequences) that are likely
-   to be encountered later in the data to be compressed, with the most commonly
-   used strings preferably put towards the end of the dictionary. Using a
-   dictionary is most useful when the data to be compressed is short and can be
-   predicted with good accuracy; the data can then be compressed better than
-   with the default empty dictionary.
-
-     Depending on the size of the compression data structures selected by
-   deflateInit or deflateInit2, a part of the dictionary may in effect be
-   discarded, for example if the dictionary is larger than the window size in
-   deflate or deflate2. Thus the strings most likely to be useful should be
-   put at the end of the dictionary, not at the front. In addition, the
-   current implementation of deflate will use at most the window size minus
-   262 bytes of the provided dictionary.
-
-     Upon return of this function, strm->adler is set to the adler32 value
-   of the dictionary; the decompressor may later use this value to determine
-   which dictionary has been used by the compressor. (The adler32 value
-   applies to the whole dictionary even if only a subset of the dictionary is
-   actually used by the compressor.) If a raw deflate was requested, then the
-   adler32 value is not computed and strm->adler is not set.
-
-     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
-   parameter is invalid (such as NULL dictionary) or the stream state is
-   inconsistent (for example if deflate has already been called for this stream
-   or if the compression method is bsort). deflateSetDictionary does not
-   perform any compression: this will be done by deflate().
-*/
-
-ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
-                                    z_streamp source));
-/*
-     Sets the destination stream as a complete copy of the source stream.
-
-     This function can be useful when several compression strategies will be
-   tried, for example when there are several ways of pre-processing the input
-   data with a filter. The streams that will be discarded should then be freed
-   by calling deflateEnd.  Note that deflateCopy duplicates the internal
-   compression state which can be quite large, so this strategy is slow and
-   can consume lots of memory.
-
-     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
-   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
-   (such as zalloc being NULL). msg is left unchanged in both source and
-   destination.
-*/
-
-ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
-/*
-     This function is equivalent to deflateEnd followed by deflateInit,
-   but does not free and reallocate all the internal compression state.
-   The stream will keep the same compression level and any other attributes
-   that may have been set by deflateInit2.
-
-      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent (such as zalloc or state being NULL).
-*/
-
-ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
-                                      int level,
-                                      int strategy));
-/*
-     Dynamically update the compression level and compression strategy.  The
-   interpretation of level and strategy is as in deflateInit2.  This can be
-   used to switch between compression and straight copy of the input data, or
-   to switch to a different kind of input data requiring a different
-   strategy. If the compression level is changed, the input available so far
-   is compressed with the old level (and may be flushed); the new level will
-   take effect only at the next call of deflate().
-
-     Before the call of deflateParams, the stream state must be set as for
-   a call of deflate(), since the currently available input may have to
-   be compressed and flushed. In particular, strm->avail_out must be non-zero.
-
-     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
-   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
-   if strm->avail_out was zero.
-*/
-
-ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
-                                    int good_length,
-                                    int max_lazy,
-                                    int nice_length,
-                                    int max_chain));
-/*
-     Fine tune deflate's internal compression parameters.  This should only be
-   used by someone who understands the algorithm used by zlib's deflate for
-   searching for the best matching string, and even then only by the most
-   fanatic optimizer trying to squeeze out the last compressed bit for their
-   specific input data.  Read the deflate.c source code for the meaning of the
-   max_lazy, good_length, nice_length, and max_chain parameters.
-
-     deflateTune() can be called after deflateInit() or deflateInit2(), and
-   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
- */
-
-ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
-                                       uLong sourceLen));
-/*
-     deflateBound() returns an upper bound on the compressed size after
-   deflation of sourceLen bytes.  It must be called after deflateInit()
-   or deflateInit2().  This would be used to allocate an output buffer
-   for deflation in a single pass, and so would be called before deflate().
-*/
-
-ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
-                                     int bits,
-                                     int value));
-/*
-     deflatePrime() inserts bits in the deflate output stream.  The intent
-  is that this function is used to start off the deflate output with the
-  bits leftover from a previous deflate stream when appending to it.  As such,
-  this function can only be used for raw deflate, and must be used before the
-  first deflate() call after a deflateInit2() or deflateReset().  bits must be
-  less than or equal to 16, and that many of the least significant bits of
-  value will be inserted in the output.
-
-      deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent.
-*/
-
-ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
-                                         gz_headerp head));
-/*
-      deflateSetHeader() provides gzip header information for when a gzip
-   stream is requested by deflateInit2().  deflateSetHeader() may be called
-   after deflateInit2() or deflateReset() and before the first call of
-   deflate().  The text, time, os, extra field, name, and comment information
-   in the provided gz_header structure are written to the gzip header (xflag is
-   ignored -- the extra flags are set according to the compression level).  The
-   caller must assure that, if not Z_NULL, name and comment are terminated with
-   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
-   available there.  If hcrc is true, a gzip header crc is included.  Note that
-   the current versions of the command-line version of gzip (up through version
-   1.3.x) do not support header crc's, and will report that it is a "multi-part
-   gzip file" and give up.
-
-      If deflateSetHeader is not used, the default gzip header has text false,
-   the time set to zero, and os set to 255, with no extra, name, or comment
-   fields.  The gzip header is returned to the default state by deflateReset().
-
-      deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent.
-*/
-
-/*
-ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
-                                     int  windowBits));
-
-     This is another version of inflateInit with an extra parameter. The
-   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
-   before by the caller.
-
-     The windowBits parameter is the base two logarithm of the maximum window
-   size (the size of the history buffer).  It should be in the range 8..15 for
-   this version of the library. The default value is 15 if inflateInit is used
-   instead. windowBits must be greater than or equal to the windowBits value
-   provided to deflateInit2() while compressing, or it must be equal to 15 if
-   deflateInit2() was not used. If a compressed stream with a larger window
-   size is given as input, inflate() will return with the error code
-   Z_DATA_ERROR instead of trying to allocate a larger window.
-
-     windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
-   determines the window size. inflate() will then process raw deflate data,
-   not looking for a zlib or gzip header, not generating a check value, and not
-   looking for any check values for comparison at the end of the stream. This
-   is for use with other formats that use the deflate compressed data format
-   such as zip.  Those formats provide their own check values. If a custom
-   format is developed using the raw deflate format for compressed data, it is
-   recommended that a check value such as an adler32 or a crc32 be applied to
-   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
-   most applications, the zlib format should be used as is. Note that comments
-   above on the use in deflateInit2() applies to the magnitude of windowBits.
-
-     windowBits can also be greater than 15 for optional gzip decoding. Add
-   32 to windowBits to enable zlib and gzip decoding with automatic header
-   detection, or add 16 to decode only the gzip format (the zlib format will
-   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is
-   a crc32 instead of an adler32.
-
-     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
-   memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
-   is set to null if there is no error message.  inflateInit2 does not perform
-   any decompression apart from reading the zlib header if present: this will
-   be done by inflate(). (So next_in and avail_in may be modified, but next_out
-   and avail_out are unchanged.)
-*/
-
-ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
-                                             const Bytef *dictionary,
-                                             uInt  dictLength));
-/*
-     Initializes the decompression dictionary from the given uncompressed byte
-   sequence. This function must be called immediately after a call of inflate,
-   if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
-   can be determined from the adler32 value returned by that call of inflate.
-   The compressor and decompressor must use exactly the same dictionary (see
-   deflateSetDictionary).  For raw inflate, this function can be called
-   immediately after inflateInit2() or inflateReset() and before any call of
-   inflate() to set the dictionary.  The application must insure that the
-   dictionary that was used for compression is provided.
-
-     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
-   parameter is invalid (such as NULL dictionary) or the stream state is
-   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
-   expected one (incorrect adler32 value). inflateSetDictionary does not
-   perform any decompression: this will be done by subsequent calls of
-   inflate().
-*/
-
-ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
-/*
-    Skips invalid compressed data until a full flush point (see above the
-  description of deflate with Z_FULL_FLUSH) can be found, or until all
-  available input is skipped. No output is provided.
-
-    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
-  if no more input was provided, Z_DATA_ERROR if no flush point has been found,
-  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
-  case, the application may save the current current value of total_in which
-  indicates where valid compressed data was found. In the error case, the
-  application may repeatedly call inflateSync, providing more input each time,
-  until success or end of the input data.
-*/
-
-ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
-                                    z_streamp source));
-/*
-     Sets the destination stream as a complete copy of the source stream.
-
-     This function can be useful when randomly accessing a large stream.  The
-   first pass through the stream can periodically record the inflate state,
-   allowing restarting inflate at those points when randomly accessing the
-   stream.
-
-     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
-   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
-   (such as zalloc being NULL). msg is left unchanged in both source and
-   destination.
-*/
-
-ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
-/*
-     This function is equivalent to inflateEnd followed by inflateInit,
-   but does not free and reallocate all the internal decompression state.
-   The stream will keep attributes that may have been set by inflateInit2.
-
-      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent (such as zalloc or state being NULL).
-*/
-
-ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
-                                     int bits,
-                                     int value));
-/*
-     This function inserts bits in the inflate input stream.  The intent is
-  that this function is used to start inflating at a bit position in the
-  middle of a byte.  The provided bits will be used before any bytes are used
-  from next_in.  This function should only be used with raw inflate, and
-  should be used before the first inflate() call after inflateInit2() or
-  inflateReset().  bits must be less than or equal to 16, and that many of the
-  least significant bits of value will be inserted in the input.
-
-      inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent.
-*/
-
-ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
-                                         gz_headerp head));
-/*
-      inflateGetHeader() requests that gzip header information be stored in the
-   provided gz_header structure.  inflateGetHeader() may be called after
-   inflateInit2() or inflateReset(), and before the first call of inflate().
-   As inflate() processes the gzip stream, head->done is zero until the header
-   is completed, at which time head->done is set to one.  If a zlib stream is
-   being decoded, then head->done is set to -1 to indicate that there will be
-   no gzip header information forthcoming.  Note that Z_BLOCK can be used to
-   force inflate() to return immediately after header processing is complete
-   and before any actual data is decompressed.
-
-      The text, time, xflags, and os fields are filled in with the gzip header
-   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
-   was valid if done is set to one.)  If extra is not Z_NULL, then extra_max
-   contains the maximum number of bytes to write to extra.  Once done is true,
-   extra_len contains the actual extra field length, and extra contains the
-   extra field, or that field truncated if extra_max is less than extra_len.
-   If name is not Z_NULL, then up to name_max characters are written there,
-   terminated with a zero unless the length is greater than name_max.  If
-   comment is not Z_NULL, then up to comm_max characters are written there,
-   terminated with a zero unless the length is greater than comm_max.  When
-   any of extra, name, or comment are not Z_NULL and the respective field is
-   not present in the header, then that field is set to Z_NULL to signal its
-   absence.  This allows the use of deflateSetHeader() with the returned
-   structure to duplicate the header.  However if those fields are set to
-   allocated memory, then the application will need to save those pointers
-   elsewhere so that they can be eventually freed.
-
-      If inflateGetHeader is not used, then the header information is simply
-   discarded.  The header is always checked for validity, including the header
-   CRC if present.  inflateReset() will reset the process to discard the header
-   information.  The application would need to call inflateGetHeader() again to
-   retrieve the header from the next gzip stream.
-
-      inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent.
-*/
-
-/*
-ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
-                                        unsigned char FAR *window));
-
-     Initialize the internal stream state for decompression using inflateBack()
-   calls.  The fields zalloc, zfree and opaque in strm must be initialized
-   before the call.  If zalloc and zfree are Z_NULL, then the default library-
-   derived memory allocation routines are used.  windowBits is the base two
-   logarithm of the window size, in the range 8..15.  window is a caller
-   supplied buffer of that size.  Except for special applications where it is
-   assured that deflate was used with small window sizes, windowBits must be 15
-   and a 32K byte window must be supplied to be able to decompress general
-   deflate streams.
-
-     See inflateBack() for the usage of these routines.
-
-     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
-   the paramaters are invalid, Z_MEM_ERROR if the internal state could not
-   be allocated, or Z_VERSION_ERROR if the version of the library does not
-   match the version of the header file.
-*/
-
-typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
-typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
-
-ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
-                                    in_func in, void FAR *in_desc,
-                                    out_func out, void FAR *out_desc));
-/*
-     inflateBack() does a raw inflate with a single call using a call-back
-   interface for input and output.  This is more efficient than inflate() for
-   file i/o applications in that it avoids copying between the output and the
-   sliding window by simply making the window itself the output buffer.  This
-   function trusts the application to not change the output buffer passed by
-   the output function, at least until inflateBack() returns.
-
-     inflateBackInit() must be called first to allocate the internal state
-   and to initialize the state with the user-provided window buffer.
-   inflateBack() may then be used multiple times to inflate a complete, raw
-   deflate stream with each call.  inflateBackEnd() is then called to free
-   the allocated state.
-
-     A raw deflate stream is one with no zlib or gzip header or trailer.
-   This routine would normally be used in a utility that reads zip or gzip
-   files and writes out uncompressed files.  The utility would decode the
-   header and process the trailer on its own, hence this routine expects
-   only the raw deflate stream to decompress.  This is different from the
-   normal behavior of inflate(), which expects either a zlib or gzip header and
-   trailer around the deflate stream.
-
-     inflateBack() uses two subroutines supplied by the caller that are then
-   called by inflateBack() for input and output.  inflateBack() calls those
-   routines until it reads a complete deflate stream and writes out all of the
-   uncompressed data, or until it encounters an error.  The function's
-   parameters and return types are defined above in the in_func and out_func
-   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
-   number of bytes of provided input, and a pointer to that input in buf.  If
-   there is no input available, in() must return zero--buf is ignored in that
-   case--and inflateBack() will return a buffer error.  inflateBack() will call
-   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out()
-   should return zero on success, or non-zero on failure.  If out() returns
-   non-zero, inflateBack() will return with an error.  Neither in() nor out()
-   are permitted to change the contents of the window provided to
-   inflateBackInit(), which is also the buffer that out() uses to write from.
-   The length written by out() will be at most the window size.  Any non-zero
-   amount of input may be provided by in().
-
-     For convenience, inflateBack() can be provided input on the first call by
-   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
-   in() will be called.  Therefore strm->next_in must be initialized before
-   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
-   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
-   must also be initialized, and then if strm->avail_in is not zero, input will
-   initially be taken from strm->next_in[0 .. strm->avail_in - 1].
-
-     The in_desc and out_desc parameters of inflateBack() is passed as the
-   first parameter of in() and out() respectively when they are called.  These
-   descriptors can be optionally used to pass any information that the caller-
-   supplied in() and out() functions need to do their job.
-
-     On return, inflateBack() will set strm->next_in and strm->avail_in to
-   pass back any unused input that was provided by the last in() call.  The
-   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
-   if in() or out() returned an error, Z_DATA_ERROR if there was a format
-   error in the deflate stream (in which case strm->msg is set to indicate the
-   nature of the error), or Z_STREAM_ERROR if the stream was not properly
-   initialized.  In the case of Z_BUF_ERROR, an input or output error can be
-   distinguished using strm->next_in which will be Z_NULL only if in() returned
-   an error.  If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
-   out() returning non-zero.  (in() will always be called before out(), so
-   strm->next_in is assured to be defined if out() returns non-zero.)  Note
-   that inflateBack() cannot return Z_OK.
-*/
-
-ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
-/*
-     All memory allocated by inflateBackInit() is freed.
-
-     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
-   state was inconsistent.
-*/
-
-ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
-/* Return flags indicating compile-time options.
-
-    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
-     1.0: size of uInt
-     3.2: size of uLong
-     5.4: size of voidpf (pointer)
-     7.6: size of z_off_t
-
-    Compiler, assembler, and debug options:
-     8: DEBUG
-     9: ASMV or ASMINF -- use ASM code
-     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
-     11: 0 (reserved)
-
-    One-time table building (smaller code, but not thread-safe if true):
-     12: BUILDFIXED -- build static block decoding tables when needed
-     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
-     14,15: 0 (reserved)
-
-    Library content (indicates missing functionality):
-     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
-                          deflate code when not needed)
-     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
-                    and decode gzip streams (to avoid linking crc code)
-     18-19: 0 (reserved)
-
-    Operation variations (changes in library functionality):
-     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
-     21: FASTEST -- deflate algorithm with only one, lowest compression level
-     22,23: 0 (reserved)
-
-    The sprintf variant used by gzprintf (zero is best):
-     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
-     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
-     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
-
-    Remainder:
-     27-31: 0 (reserved)
- */
-
-
-                        /* utility functions */
-
-/*
-     The following utility functions are implemented on top of the
-   basic stream-oriented functions. To simplify the interface, some
-   default options are assumed (compression level and memory usage,
-   standard memory allocation functions). The source code of these
-   utility functions can easily be modified if you need special options.
-*/
-
-ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
-                                 const Bytef *source, uLong sourceLen));
-/*
-     Compresses the source buffer into the destination buffer.  sourceLen is
-   the byte length of the source buffer. Upon entry, destLen is the total
-   size of the destination buffer, which must be at least the value returned
-   by compressBound(sourceLen). Upon exit, destLen is the actual size of the
-   compressed buffer.
-     This function can be used to compress a whole file at once if the
-   input file is mmap'ed.
-     compress returns Z_OK if success, Z_MEM_ERROR if there was not
-   enough memory, Z_BUF_ERROR if there was not enough room in the output
-   buffer.
-*/
-
-ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
-                                  const Bytef *source, uLong sourceLen,
-                                  int level));
-/*
-     Compresses the source buffer into the destination buffer. The level
-   parameter has the same meaning as in deflateInit.  sourceLen is the byte
-   length of the source buffer. Upon entry, destLen is the total size of the
-   destination buffer, which must be at least the value returned by
-   compressBound(sourceLen). Upon exit, destLen is the actual size of the
-   compressed buffer.
-
-     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
-   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
-   Z_STREAM_ERROR if the level parameter is invalid.
-*/
-
-ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
-/*
-     compressBound() returns an upper bound on the compressed size after
-   compress() or compress2() on sourceLen bytes.  It would be used before
-   a compress() or compress2() call to allocate the destination buffer.
-*/
-
-ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
-                                   const Bytef *source, uLong sourceLen));
-/*
-     Decompresses the source buffer into the destination buffer.  sourceLen is
-   the byte length of the source buffer. Upon entry, destLen is the total
-   size of the destination buffer, which must be large enough to hold the
-   entire uncompressed data. (The size of the uncompressed data must have
-   been saved previously by the compressor and transmitted to the decompressor
-   by some mechanism outside the scope of this compression library.)
-   Upon exit, destLen is the actual size of the compressed buffer.
-     This function can be used to decompress a whole file at once if the
-   input file is mmap'ed.
-
-     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
-   enough memory, Z_BUF_ERROR if there was not enough room in the output
-   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
-*/
-
-
-typedef voidp gzFile;
-
-ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode));
-/*
-     Opens a gzip (.gz) file for reading or writing. The mode parameter
-   is as in fopen ("rb" or "wb") but can also include a compression level
-   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
-   Huffman only compression as in "wb1h", or 'R' for run-length encoding
-   as in "wb1R". (See the description of deflateInit2 for more information
-   about the strategy parameter.)
-
-     gzopen can be used to read a file which is not in gzip format; in this
-   case gzread will directly read from the file without decompression.
-
-     gzopen returns NULL if the file could not be opened or if there was
-   insufficient memory to allocate the (de)compression state; errno
-   can be checked to distinguish the two cases (if errno is zero, the
-   zlib error is Z_MEM_ERROR).  */
-
-ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode));
-/*
-     gzdopen() associates a gzFile with the file descriptor fd.  File
-   descriptors are obtained from calls like open, dup, creat, pipe or
-   fileno (in the file has been previously opened with fopen).
-   The mode parameter is as in gzopen.
-     The next call of gzclose on the returned gzFile will also close the
-   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
-   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
-     gzdopen returns NULL if there was insufficient memory to allocate
-   the (de)compression state.
-*/
-
-ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
-/*
-     Dynamically update the compression level or strategy. See the description
-   of deflateInit2 for the meaning of these parameters.
-     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
-   opened for writing.
-*/
-
-ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len));
-/*
-     Reads the given number of uncompressed bytes from the compressed file.
-   If the input file was not in gzip format, gzread copies the given number
-   of bytes into the buffer.
-     gzread returns the number of uncompressed bytes actually read (0 for
-   end of file, -1 for error). */
-
-ZEXTERN int ZEXPORT    gzwrite OF((gzFile file,
-                                   voidpc buf, unsigned len));
-/*
-     Writes the given number of uncompressed bytes into the compressed file.
-   gzwrite returns the number of uncompressed bytes actually written
-   (0 in case of error).
-*/
-
-ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...));
-/*
-     Converts, formats, and writes the args to the compressed file under
-   control of the format string, as in fprintf. gzprintf returns the number of
-   uncompressed bytes actually written (0 in case of error).  The number of
-   uncompressed bytes written is limited to 4095. The caller should assure that
-   this limit is not exceeded. If it is exceeded, then gzprintf() will return
-   return an error (0) with nothing written. In this case, there may also be a
-   buffer overflow with unpredictable consequences, which is possible only if
-   zlib was compiled with the insecure functions sprintf() or vsprintf()
-   because the secure snprintf() or vsnprintf() functions were not available.
-*/
-
-ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
-/*
-      Writes the given null-terminated string to the compressed file, excluding
-   the terminating null character.
-      gzputs returns the number of characters written, or -1 in case of error.
-*/
-
-ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
-/*
-      Reads bytes from the compressed file until len-1 characters are read, or
-   a newline character is read and transferred to buf, or an end-of-file
-   condition is encountered.  The string is then terminated with a null
-   character.
-      gzgets returns buf, or Z_NULL in case of error.
-*/
-
-ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c));
-/*
-      Writes c, converted to an unsigned char, into the compressed file.
-   gzputc returns the value that was written, or -1 in case of error.
-*/
-
-ZEXTERN int ZEXPORT    gzgetc OF((gzFile file));
-/*
-      Reads one byte from the compressed file. gzgetc returns this byte
-   or -1 in case of end of file or error.
-*/
-
-ZEXTERN int ZEXPORT    gzungetc OF((int c, gzFile file));
-/*
-      Push one character back onto the stream to be read again later.
-   Only one character of push-back is allowed.  gzungetc() returns the
-   character pushed, or -1 on failure.  gzungetc() will fail if a
-   character has been pushed but not read yet, or if c is -1. The pushed
-   character will be discarded if the stream is repositioned with gzseek()
-   or gzrewind().
-*/
-
-ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush));
-/*
-     Flushes all pending output into the compressed file. The parameter
-   flush is as in the deflate() function. The return value is the zlib
-   error number (see function gzerror below). gzflush returns Z_OK if
-   the flush parameter is Z_FINISH and all output could be flushed.
-     gzflush should be called only when strictly necessary because it can
-   degrade compression.
-*/
-
-ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file,
-                                      z_off_t offset, int whence));
-/*
-      Sets the starting position for the next gzread or gzwrite on the
-   given compressed file. The offset represents a number of bytes in the
-   uncompressed data stream. The whence parameter is defined as in lseek(2);
-   the value SEEK_END is not supported.
-     If the file is opened for reading, this function is emulated but can be
-   extremely slow. If the file is opened for writing, only forward seeks are
-   supported; gzseek then compresses a sequence of zeroes up to the new
-   starting position.
-
-      gzseek returns the resulting offset location as measured in bytes from
-   the beginning of the uncompressed stream, or -1 in case of error, in
-   particular if the file is opened for writing and the new starting position
-   would be before the current position.
-*/
-
-ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
-/*
-     Rewinds the given file. This function is supported only for reading.
-
-   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
-*/
-
-ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
-/*
-     Returns the starting position for the next gzread or gzwrite on the
-   given compressed file. This position represents a number of bytes in the
-   uncompressed data stream.
-
-   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
-*/
-
-ZEXTERN int ZEXPORT gzeof OF((gzFile file));
-/*
-     Returns 1 when EOF has previously been detected reading the given
-   input stream, otherwise zero.
-*/
-
-ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
-/*
-     Returns 1 if file is being read directly without decompression, otherwise
-   zero.
-*/
-
-ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
-/*
-     Flushes all pending output if necessary, closes the compressed file
-   and deallocates all the (de)compression state. The return value is the zlib
-   error number (see function gzerror below).
-*/
-
-ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
-/*
-     Returns the error message for the last error which occurred on the
-   given compressed file. errnum is set to zlib error number. If an
-   error occurred in the file system and not in the compression library,
-   errnum is set to Z_ERRNO and the application may consult errno
-   to get the exact error code.
-*/
-
-ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
-/*
-     Clears the error and end-of-file flags for file. This is analogous to the
-   clearerr() function in stdio. This is useful for continuing to read a gzip
-   file that is being written concurrently.
-*/
-
-                        /* checksum functions */
-
-/*
-     These functions are not related to compression but are exported
-   anyway because they might be useful in applications using the
-   compression library.
-*/
-
-ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
-/*
-     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
-   return the updated checksum. If buf is NULL, this function returns
-   the required initial value for the checksum.
-   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
-   much faster. Usage example:
-
-     uLong adler = adler32(0L, Z_NULL, 0);
-
-     while (read_buffer(buffer, length) != EOF) {
-       adler = adler32(adler, buffer, length);
-     }
-     if (adler != original_adler) error();
-*/
-
-ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
-                                          z_off_t len2));
-/*
-     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
-   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
-   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
-   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
-*/
-
-ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
-/*
-     Update a running CRC-32 with the bytes buf[0..len-1] and return the
-   updated CRC-32. If buf is NULL, this function returns the required initial
-   value for the for the crc. Pre- and post-conditioning (one's complement) is
-   performed within this function so it shouldn't be done by the application.
-   Usage example:
-
-     uLong crc = crc32(0L, Z_NULL, 0);
-
-     while (read_buffer(buffer, length) != EOF) {
-       crc = crc32(crc, buffer, length);
-     }
-     if (crc != original_crc) error();
-*/
-
-ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
-
-/*
-     Combine two CRC-32 check values into one.  For two sequences of bytes,
-   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
-   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
-   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
-   len2.
-*/
-
-
-                        /* various hacks, don't look :) */
-
-/* deflateInit and inflateInit are macros to allow checking the zlib version
- * and the compiler's view of z_stream:
- */
-ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
-                                     const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
-                                     const char *version, int stream_size));
-ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
-                                      int windowBits, int memLevel,
-                                      int strategy, const char *version,
-                                      int stream_size));
-ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
-                                      const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
-                                         unsigned char FAR *window,
-                                         const char *version,
-                                         int stream_size));
-#define deflateInit(strm, level) \
-        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream))
-#define inflateInit(strm) \
-        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream))
-#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
-        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
-                      (strategy),           ZLIB_VERSION, sizeof(z_stream))
-#define inflateInit2(strm, windowBits) \
-        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
-#define inflateBackInit(strm, windowBits, window) \
-        inflateBackInit_((strm), (windowBits), (window), \
-        ZLIB_VERSION, sizeof(z_stream))
-
-
-#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
-    struct internal_state {int dummy;}; /* hack for buggy compilers */
-#endif
-
-ZEXTERN const char   * ZEXPORT zError           OF((int));
-ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z));
-ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void));
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ZLIB_H */
diff --git a/Kindle_4_PC_Tools/README_K4PCDeDRM.txt b/Kindle_4_PC_Tools/README_K4PCDeDRM.txt
deleted file mode 100644 (file)
index d9f4780..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-Readme.txt
-
-From Apprentice Alf's Blog:
-
-When Amazon released Kindle for PC, there was a lot of interest in finding the PID used by an installed copy. Unfortunately, it turned out that Amazon had taken the opportunity to increase the complexity of the DRM applied to ebooks downloaded for use with Kindle for PC. In short, every book has its own PID.
-
-The current best method is a script called K4PCDeDRM.py, which takes code from several previous tools and puts them together, giving a one-script solution to removing the DRM that is not dependent on the exact version of the Kindle for PC software.
-
-To use this script requires Python Version 2.X 32 bit version.  On Windows, we recommend downloading ActiveState's ActivePython for Windows (Version 2.X NOT 3.X (32bit).
-
-We strongly recommend that at the moment people with Kindle for Windows should use K4PCDeDRM.pyw instead of any of the other methods (skindle, unswindle).
-
-K4PCDeDRM is available in the large archive mentioned in the first post.
-
-To run it simply completely extract the tools archive and open the Kindle_4_PC_Tools.  Then double-click on K4PCDeDRM.pyw to run the gui version.
-
-Please note, the tools archive must be on the same machine in the same account as the Kindle for PC application for things to work.
-
-FYI:
-Books downloaded to Kindle for PC are stored in a folder called “My Kindle Content” in the current user’s home folder.
-
-IMPORTANT: There are two kinds of Kindle ebooks. Mobipocket and Topaz. For Topaz books it’s really necessary to use the Topaz specific tools mentioned in this post which not only remove the DRM but also convert the Topaz file into a format that can be converted into other formats.
-
diff --git a/Kindle_4_PC_Tools/lib/k4pcdedrm.py b/Kindle_4_PC_Tools/lib/k4pcdedrm.py
deleted file mode 100644 (file)
index 509987e..0000000
+++ /dev/null
@@ -1,682 +0,0 @@
-#!/usr/bin/env python
-#
-# This is a WINDOWS python script. You need a Python interpreter to run it.
-# For example, ActiveState Python, which exists for windows.
-#
-# It can run standalone to convert K4PC files, or it can be installed as a
-# plugin for Calibre (http://calibre-ebook.com/about) so that importing
-# K4PC files with DRM is no londer a multi-step process.
-#
-# ***NOTE*** Calibre and K4PC must be installed on the same windows machine
-# for the plugin version to function properly.
-#
-# To create a Calibre plugin, rename this file so that the filename
-# ends in '_plugin.py', put it into a ZIP file and import that ZIP into Calibre
-# using its plugin configuration GUI.
-#
-# Thanks to The Dark Reverser for MobiDeDrm and CMBDTC for cmbdtc_dump from
-# which this script steals most unashamedly.
-#
-# Changelog
-#  0.01 - Initial version - Utilizes skindle and CMBDTC method of obtaining
-#         book specific pids from K4PC books. If Calibre and K4PC are installed
-#         on the same windows machine, Calibre plugin functionality is once
-#         again restored.
-
-
-"""
-
-Comprehensive Mazama Book DRM with Topaz Cryptography V2.0
-
------BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
-M8hYfnNEI0yQmn5Ti+W8biT7EatpauE/5jgQMPBmdNrDr1hbHyHBSP7xeC2qlRWC
-B62UCxeu/fpfnvNHDN/wPWWH4jynZ2M6cdcnE5LQ+FfeKqZn7gnG2No1U9h7oOHx
-y2/pHuYme7U1TsgSjwIDAQAB
------END PUBLIC KEY-----
-
-"""
-
-from __future__ import with_statement
-
-import csv
-import sys
-import os
-import getopt
-import zlib
-import binascii
-from struct import pack
-from struct import unpack
-from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
-    create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
-    string_at, Structure, c_void_p, cast
-import _winreg as winreg
-import traceback
-import hashlib
-
-__version__ = '0.01'
-
-global kindleDatabase
-MAX_PATH = 255
-kernel32 = windll.kernel32
-advapi32 = windll.advapi32
-crypt32 = windll.crypt32
-
-
-#
-# Various character maps used to decrypt books. Probably supposed to act as obfuscation
-#
-charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
-charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
-charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
-charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
-
-
-#
-# Exceptions for all the problems that might happen during the script
-#
-class DrmException(Exception):
-    pass
-    
-
-class DataBlob(Structure):
-    _fields_ = [('cbData', c_uint),
-                ('pbData', c_void_p)]
-DataBlob_p = POINTER(DataBlob)
-
-
-def GetSystemDirectory():
-    GetSystemDirectoryW = kernel32.GetSystemDirectoryW
-    GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
-    GetSystemDirectoryW.restype = c_uint
-    def GetSystemDirectory():
-        buffer = create_unicode_buffer(MAX_PATH + 1)
-        GetSystemDirectoryW(buffer, len(buffer))
-        return buffer.value
-    return GetSystemDirectory
-GetSystemDirectory = GetSystemDirectory()
-
-
-def GetVolumeSerialNumber():
-    GetVolumeInformationW = kernel32.GetVolumeInformationW
-    GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
-                                      POINTER(c_uint), POINTER(c_uint),
-                                      POINTER(c_uint), c_wchar_p, c_uint]
-    GetVolumeInformationW.restype = c_uint
-    def GetVolumeSerialNumber(path):
-        vsn = c_uint(0)
-        GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
-        return vsn.value
-    return GetVolumeSerialNumber
-GetVolumeSerialNumber = GetVolumeSerialNumber()
-
-
-def GetUserName():
-    GetUserNameW = advapi32.GetUserNameW
-    GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
-    GetUserNameW.restype = c_uint
-    def GetUserName():
-        buffer = create_unicode_buffer(32)
-        size = c_uint(len(buffer))
-        while not GetUserNameW(buffer, byref(size)):
-            buffer = create_unicode_buffer(len(buffer) * 2)
-            size.value = len(buffer)
-        return buffer.value.encode('utf-16-le')[::2]
-    return GetUserName
-GetUserName = GetUserName()
-
-
-def CryptUnprotectData():
-    _CryptUnprotectData = crypt32.CryptUnprotectData
-    _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
-                                   c_void_p, c_void_p, c_uint, DataBlob_p]
-    _CryptUnprotectData.restype = c_uint
-    def CryptUnprotectData(indata, entropy):
-        indatab = create_string_buffer(indata)
-        indata = DataBlob(len(indata), cast(indatab, c_void_p))
-        entropyb = create_string_buffer(entropy)
-        entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
-        outdata = DataBlob()
-        if not _CryptUnprotectData(byref(indata), None, byref(entropy),
-                                   None, None, 0, byref(outdata)):
-            raise DrmException("Failed to Unprotect Data")
-        return string_at(outdata.pbData, outdata.cbData)
-    return CryptUnprotectData
-CryptUnprotectData = CryptUnprotectData()
-
-
-#
-# Returns the MD5 digest of "message"
-#
-def MD5(message):
-    ctx = hashlib.md5()
-    ctx.update(message)
-    return ctx.digest()
-
-
-#
-# Returns the MD5 digest of "message"
-#
-def SHA1(message):
-    ctx = hashlib.sha1()
-    ctx.update(message)
-    return ctx.digest()
-
-
-#
-# Locate and open the Kindle.info file.
-#
-def openKindleInfo():
-    regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
-    path = winreg.QueryValueEx(regkey, 'Local AppData')[0] 
-    return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
-
-
-#
-# Parse the Kindle.info file and return the records as a list of key-values
-#
-def parseKindleInfo():
-    DB = {}
-    infoReader = openKindleInfo()
-    infoReader.read(1)
-    data = infoReader.read()
-    items = data.split('{')
-    
-    for item in items:
-        splito = item.split(':')
-        DB[splito[0]] =splito[1]
-    return DB
-
-
-#
-# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. (Totally not optimal)
-#
-def findNameForHash(hash):
-    names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
-    result = ""
-    for name in names:
-        if hash == encodeHash(name, charMap2):
-           result = name
-           break
-    return name
-
-    
-#
-# Print all the records from the kindle.info file.
-#
-def printKindleInfo():
-    for record in kindleDatabase:
-        name = findNameForHash(record)
-        if name != "" :
-            print (name)
-            print ("--------------------------\n")
-        else :
-            print ("Unknown Record")
-        print getKindleInfoValueForHash(record)
-        print "\n"
-
-
-#
-# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
-#
-def getKindleInfoValueForHash(hashedKey):
-    global kindleDatabase
-    encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
-    return CryptUnprotectData(encryptedValue,"")
-
-
-#
-#  Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
-#
-def getKindleInfoValueForKey(key):
-    return getKindleInfoValueForHash(encodeHash(key,charMap2))
-
-
-#
-# 8 bits to six bits encoding from hash to generate PID string
-#  
-def encodePID(hash):
-    global charMap3
-    PID = ""
-    for position in range (0,8):
-        PID += charMap3[getSixBitsFromBitField(hash,position)]
-    return PID
-
-
-#
-# Hash the bytes in data and then encode the digest with the characters in map
-#
-def encodeHash(data,map):
-    return encode(MD5(data),map)
-
-   
-#
-# Encode the bytes in data with the characters in map
-#
-def encode(data, map):
-    result = ""
-    for char in data:
-        value = ord(char)
-        Q = (value ^ 0x80) // len(map)
-        R = value % len(map)
-        result += map[Q]
-        result += map[R]
-    return result
-
-
-#
-# Decode the string in data with the characters in map. Returns the decoded bytes
-#
-def decode(data,map):
-    result = ""
-    for i in range (0,len(data),2):
-        high = map.find(data[i])
-        low = map.find(data[i+1])
-        value = (((high * 0x40) ^ 0x80) & 0xFF) + low
-        result += pack("B",value)
-    return result
-
-
-#
-# Encryption table used to generate the device PID
-#
-def generatePidEncryptionTable() :
-    table = []
-    for counter1 in range (0,0x100):
-        value = counter1
-        for counter2 in range (0,8):
-            if (value & 1 == 0) :
-                value = value >> 1
-            else :
-                value = value >> 1
-                value = value ^ 0xEDB88320
-        table.append(value)
-    return table
-
-
-#
-# Seed value used to generate the device PID
-#
-def generatePidSeed(table,dsn) :
-    value = 0
-    for counter in range (0,4) :
-       index = (ord(dsn[counter]) ^ value) &0xFF
-       value = (value >> 8) ^ table[index]
-    return value
-
-   
-#
-# Generate the device PID
-#
-def generateDevicePID(table,dsn,nbRoll):
-    seed = generatePidSeed(table,dsn)
-    pidAscii = ""
-    pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
-    index = 0
-    
-    for counter in range (0,nbRoll):
-        pid[index] = pid[index] ^ ord(dsn[counter])
-        index = (index+1) %8
-    for counter in range (0,8):
-        index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
-        pidAscii += charMap4[index]
-    return pidAscii
-
-
-#
-# Returns two bit at offset from a bit field
-#
-def getTwoBitsFromBitField(bitField,offset):
-    byteNumber = offset // 4
-    bitPosition = 6 - 2*(offset % 4)
-    
-    return ord(bitField[byteNumber]) >> bitPosition & 3
-
-
-#
-# Returns the six bits at offset from a bit field
-#    
-def getSixBitsFromBitField(bitField,offset):
-    offset *= 3
-    value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
-    return value
-
-
-#
-# MobiDeDrm-0.16 Stuff
-#
-class Unbuffered:
-    def __init__(self, stream):
-        self.stream = stream
-    def write(self, data):
-        self.stream.write(data)
-        self.stream.flush()
-    def __getattr__(self, attr):
-        return getattr(self.stream, attr)
-
-
-# Implementation of Pukall Cipher 1
-def PC1(key, src, decryption=True):
-    sum1 = 0;
-    sum2 = 0;
-    keyXorVal = 0;
-    if len(key)!=16:
-        print "Bad key length!"
-        return None
-    wkey = []
-    for i in xrange(8):
-        wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
-
-    dst = ""
-    for i in xrange(len(src)):
-        temp1 = 0;
-        byteXorVal = 0;
-        for j in xrange(8):
-            temp1 ^= wkey[j]
-            sum2  = (sum2+j)*20021 + sum1
-            sum1  = (temp1*346)&0xFFFF
-            sum2  = (sum2+sum1)&0xFFFF
-            temp1 = (temp1*20021+1)&0xFFFF
-            byteXorVal ^= temp1 ^ sum2
-        curByte = ord(src[i])
-        if not decryption:
-            keyXorVal = curByte * 257;
-        curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
-        if decryption:
-            keyXorVal = curByte * 257;
-        for j in xrange(8):
-            wkey[j] ^= keyXorVal;
-        dst+=chr(curByte)
-    return dst
-
-
-def getSizeOfTrailingDataEntries(ptr, size, flags):
-    def getSizeOfTrailingDataEntry(ptr, size):
-        bitpos, result = 0, 0
-        if size <= 0:
-            return result
-        while True:
-            v = ord(ptr[size-1])
-            result |= (v & 0x7F) << bitpos
-            bitpos += 7
-            size -= 1
-            if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
-                return result
-    num = 0
-    testflags = flags >> 1
-    while testflags:
-        if testflags & 1:
-            num += getSizeOfTrailingDataEntry(ptr, size - num)
-        testflags >>= 1
-    # Multibyte data, if present, is included in the encryption, so
-    # we do not need to check the low bit.
-    # if flags & 1:
-    #    num += (ord(ptr[size - num - 1]) & 0x3) + 1
-    return num
-
-
-#
-# This class does all the heavy lifting.
-#
-class DrmStripper:
-    def loadSection(self, section):
-        if (section + 1 == self.num_sections):
-            endoff = len(self.data_file)
-        else:
-            endoff = self.sections[section + 1][0]
-        off = self.sections[section][0]
-        return self.data_file[off:endoff]
-
-    def patch(self, off, new):
-        self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
-
-    def patchSection(self, section, new, in_off = 0):
-        if (section + 1 == self.num_sections):
-            endoff = len(self.data_file)
-        else:
-            endoff = self.sections[section + 1][0]
-        off = self.sections[section][0]
-        assert off + in_off + len(new) <= endoff
-        self.patch(off + in_off, new)
-
-    def parseDRM(self, data, count, pid):
-        pid = pid.ljust(16,'\0')
-        keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
-        temp_key = PC1(keyvec1, pid, False)
-        temp_key_sum = sum(map(ord,temp_key)) & 0xff
-        found_key = None
-        for i in xrange(count):
-            verification, size, type, cksum, cookie = unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
-            cookie = PC1(temp_key, cookie)
-            ver,flags,finalkey,expiry,expiry2 = unpack('>LL16sLL', cookie)
-            if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
-                found_key = finalkey
-                break
-        if not found_key:
-            # Then try the default encoding that doesn't require a PID
-            temp_key = keyvec1
-            temp_key_sum = sum(map(ord,temp_key)) & 0xff
-            for i in xrange(count):
-                verification, size, type, cksum, cookie = unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
-                cookie = PC1(temp_key, cookie)
-                ver,flags,finalkey,expiry,expiry2 = unpack('>LL16sLL', cookie)
-                if verification == ver and cksum == temp_key_sum:
-                    found_key = finalkey
-                    break
-        return found_key
-
-    def __init__(self, data_file):
-        self.data_file = data_file
-        header = data_file[0:72]
-        if header[0x3C:0x3C+8] != 'BOOKMOBI':
-            raise DrmException("invalid file format")
-        self.num_sections, = unpack('>H', data_file[76:78])
-
-        self.sections = []
-        for i in xrange(self.num_sections):
-            offset, a1,a2,a3,a4 = unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
-            flags, val = a1, a2<<16|a3<<8|a4
-            self.sections.append( (offset, flags, val) )
-
-        sect = self.loadSection(0)
-        records, = unpack('>H', sect[0x8:0x8+2])
-        mobi_length, = unpack('>L',sect[0x14:0x18])
-        mobi_version, = unpack('>L',sect[0x68:0x6C])
-        extra_data_flags = 0
-        print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length)
-        if (mobi_length >= 0xE4) and (mobi_version >= 5):
-            extra_data_flags, = unpack('>H', sect[0xF2:0xF4])
-            print "Extra Data Flags = %d" %extra_data_flags
-
-        crypto_type, = unpack('>H', sect[0xC:0xC+2])
-        if crypto_type == 0:
-            print "This book is not encrypted."
-        else:
-            if crypto_type == 1:
-                raise DrmException("cannot decode Mobipocket encryption type 1")
-            if crypto_type != 2:
-                raise DrmException("unknown encryption type: %d" % crypto_type)
-
-            # determine the EXTH Offset.
-            exth_off = unpack('>I', sect[20:24])[0] + 16 + self.sections[0][0]
-            # Grab the entire EXTH block and feed it to the getK4PCPids function.
-            exth = data_file[exth_off:self.sections[0+1][0]]
-            pid = getK4PCPids(exth)
-
-            # calculate the keys
-            drm_ptr, drm_count, drm_size, drm_flags = unpack('>LLLL', sect[0xA8:0xA8+16])
-            if drm_count == 0:
-                raise DrmException("no PIDs found in this file")
-            found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
-            if not found_key:
-                raise DrmException("no key found. maybe the PID is incorrect")
-
-            # kill the drm keys
-            self.patchSection(0, "\0" * drm_size, drm_ptr)
-            # kill the drm pointers
-            self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
-            # clear the crypto type
-            self.patchSection(0, "\0" * 2, 0xC)
-
-            # decrypt sections
-            print "\nDecrypting. Please wait . . .",
-            new_data = self.data_file[:self.sections[1][0]]
-            for i in xrange(1, records+1):
-                data = self.loadSection(i)
-                extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
-                if i%100 == 0:
-                    print ".",
-                # print "record %d, extra_size %d" %(i,extra_size)
-                new_data += PC1(found_key, data[0:len(data) - extra_size])
-                if extra_size > 0:
-                    new_data += data[-extra_size:]
-                #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
-            if self.num_sections > records+1:
-                new_data += self.data_file[self.sections[records+1][0]:]
-            self.data_file = new_data
-            print "done!"
-            print "\nPlease only use your new-found powers for good."
-
-    def getResult(self):
-        return self.data_file
-
-
-#
-# DiapDealer's stuff: Parse the EXTH header records and parse the Kindleinfo
-# file to calculate the book pid.
-#
-def getK4PCPids(exth):
-    global kindleDatabase
-    try:
-        kindleDatabase = parseKindleInfo()
-    except Exception as message:
-        print(message)
-    
-    if kindleDatabase != None :
-  
-        # Get the Mazama Random number
-        MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
-    
-        # Get the HDD serial
-        encodedSystemVolumeSerialNumber = encodeHash(str(GetVolumeSerialNumber(GetSystemDirectory().split('\\')[0] + '\\')),charMap1)
-    
-        # Get the current user name
-        encodedUsername = encodeHash(GetUserName(),charMap1)
-    
-        # concat, hash and encode to calculate the DSN
-        DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
-       
-        print("\nDSN: " + DSN)
-    
-
-        # Compute the device PID (for which I can tell, is used for nothing).
-        # But hey, stuff being printed out is apparently cool.
-        table =  generatePidEncryptionTable()
-        devicePID = generateDevicePID(table,DSN,4)
-            
-        print("Device PID: " + devicePID)
-            
-        # Compute book PID
-        exth_records = {}
-        nitems, = unpack('>I', exth[8:12])
-        pos = 12
-        # Parse the EXTH records, storing data indexed by type
-        for i in xrange(nitems):
-            type, size = unpack('>II', exth[pos: pos + 8])
-            content = exth[pos + 8: pos + size]
-
-            exth_records[type] = content
-            pos += size
-
-        # Grab the contents of the type 209 exth record
-        if exth_records[209] != None:
-            data = exth_records[209]
-        else:
-            raise DrmException("\nNo EXTH record type 209 - Perhaps not a K4PC file?")
-        
-        # Parse the 209 data to find the the exth record with the token data.
-        # The last character of the 209 data points to the record with the token.
-        # Always 208 from my experience, but I'll leave the logic in case that changes.
-        for i in xrange(len(data)):
-            if ord(data[i]) != 0:
-                if exth_records[ord(data[i])] != None:
-                    token = exth_records[ord(data[i])]
-
-        # Get the kindle account token
-        kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
-    
-        print("Account Token: " + kindleAccountToken)
-
-        pidHash = SHA1(DSN+kindleAccountToken+exth_records[209]+token)
-   
-        bookPID = encodePID(pidHash)
-
-        if exth_records[503] != None:
-            print "Pid for " + exth_records[503] + ": " + bookPID
-        else:
-            print ("Book PID: " + bookPID )
-            
-        return bookPID
-    
-    raise DrmException("\nCould not access K4PC data - Perhaps K4PC is not installed/configured?")
-    return null
-
-if not __name__ == "__main__":
-    from calibre.customize import FileTypePlugin
-
-    class K4PCDeDRM(FileTypePlugin):
-        name                = 'K4PCDeDRM' # Name of the plugin
-        description         = 'Removes DRM from K4PC files'
-        supported_platforms = ['windows'] # Platforms this plugin will run on
-        author              = 'DiapDealer' # The author of this plugin
-        version             = (0, 0, 1)   # The version number of this plugin
-        file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
-        on_import           = True # Run this plugin during the import
-
-        def run(self, path_to_ebook):
-            from calibre.gui2 import is_ok_to_use_qt
-            from PyQt4.Qt import QMessageBox
-            data_file = file(path_to_ebook, 'rb').read()
-
-            try:
-                unlocked_file = DrmStripper(data_file).getResult()
-            except DrmException:
-                # ignore the error
-                pass
-            else:
-                of = self.temporary_file('.mobi')
-                of.write(unlocked_file)
-                of.close()
-                return of.name
-
-            if is_ok_to_use_qt():
-                d = QMessageBox(QMessageBox.Warning, "K4PCDeDRM Plugin", "Couldn't decode: %s\n\nImporting encrypted version." % path_to_ebook)
-                d.show()
-                d.raise_()
-                d.exec_()
-            return path_to_ebook
-
-        #def customization_help(self, gui=False):
-        #    return 'Enter PID (separate multiple PIDs with comma)'
-
-if __name__ == "__main__":
-    sys.stdout=Unbuffered(sys.stdout)
-    print ('K4PCDeDrm v%(__version__)s '
-          'provided DiapDealer.' % globals())
-    if len(sys.argv)<3:
-        print "Removes DRM protection from K4PC books"
-        print "Usage:"
-        print "    %s <infile> <outfile>" % sys.argv[0]
-        sys.exit(1)
-    else:
-        infile = sys.argv[1]
-        outfile = sys.argv[2]
-        data_file = file(infile, 'rb').read()
-        try:
-            strippedFile = DrmStripper(data_file)
-            file(outfile, 'wb').write(strippedFile.getResult())
-        except DrmException, e:
-            print "Error: %s" % e
-            sys.exit(1)
-    sys.exit(0)
\ No newline at end of file
similarity index 71%
rename from Kindle_Mobi_Tools/MobiDeDRM.pyw
rename to Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw
index c63ed9c72f5aeb6a45ad8ac8dd207e24b1020e34..467d87ee1d31ca32940a2cbc02d48cc3191ab34a 100644 (file)
@@ -4,15 +4,15 @@
 import sys
 sys.path.append('lib')
 import os, os.path, urllib
-import subprocess
-from subprocess import Popen, PIPE, STDOUT
-import subasyncio
-from subasyncio import Process
 import Tkinter
 import Tkconstants
 import tkFileDialog
 import tkMessageBox
 from scrolltextwidget import ScrolledText
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import subasyncio
+from subasyncio import Process
 
 class MainDialog(Tkinter.Frame):
     def __init__(self, root):
@@ -20,14 +20,14 @@ class MainDialog(Tkinter.Frame):
         self.root = root
         self.interval = 2000
         self.p2 = None
-        self.status = Tkinter.Label(self, text='Remove Encryption from a Mobi eBook')
+        self.status = Tkinter.Label(self, text='Remove Encryption from a K4PC, K4M, or Mobi eBook')
         self.status.pack(fill=Tkconstants.X, expand=1)
         body = Tkinter.Frame(self)
         body.pack(fill=Tkconstants.X, expand=1)
         sticky = Tkconstants.E + Tkconstants.W
         body.grid_columnconfigure(1, weight=2)
 
-        Tkinter.Label(body, text='Mobi eBook input file').grid(row=0, sticky=Tkconstants.E)
+        Tkinter.Label(body, text='K4 or Mobi eBook input file').grid(row=0, sticky=Tkconstants.E)
         self.mobipath = Tkinter.Entry(body, width=50)
         self.mobipath.grid(row=0, column=1, sticky=sticky)
         cwd = os.getcwdu()
@@ -36,21 +36,24 @@ class MainDialog(Tkinter.Frame):
         button = Tkinter.Button(body, text="...", command=self.get_mobipath)
         button.grid(row=0, column=2)
 
-        Tkinter.Label(body, text='Name for Unencrypted Output File').grid(row=1, sticky=Tkconstants.E)
+        Tkinter.Label(body, text='Directory for the Unencrypted Output File').grid(row=1, sticky=Tkconstants.E)
         self.outpath = Tkinter.Entry(body, width=50)
         self.outpath.grid(row=1, column=1, sticky=sticky)
-        self.outpath.insert(0, '')
+        cwd = os.getcwdu()
+        cwd = cwd.encode('utf-8')
+        outname = cwd
+        self.outpath.insert(0, outname)
         button = Tkinter.Button(body, text="...", command=self.get_outpath)
         button.grid(row=1, column=2)
 
-        Tkinter.Label(body, text='10 Character PID').grid(row=2, sticky=Tkconstants.E)
-        self.pidnum = Tkinter.StringVar()
-        self.pidinfo = Tkinter.Entry(body, width=12, textvariable=self.pidnum)
+        Tkinter.Label(body, text='Comma Separated List of 10 Character PIDs (no spaces)').grid(row=2, sticky=Tkconstants.E)
+        self.pidnums = Tkinter.StringVar()
+        self.pidinfo = Tkinter.Entry(body, width=50, textvariable=self.pidnums)
         self.pidinfo.grid(row=2, column=1, sticky=sticky)
 
         msg1 = 'Conversion Log \n\n'
         self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
-        self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
         self.stext.insert(Tkconstants.END,msg1)
 
         buttons = Tkinter.Frame(self)
@@ -73,8 +76,10 @@ class MainDialog(Tkinter.Frame):
             text = self.p2.readerr()
             text += self.p2.read()
             msg = text + '\n\n' + 'Encryption successfully removed\n'
-            if poll != 0:
+            if poll == 1:
                 msg = text + '\n\n' + 'Error: Encryption Removal Failed\n'
+            if poll == 2:
+                msg = text + '\n\n' + 'Input File was Not Encrypted - No Output File Needed\n'
             self.showCmdOutput(msg)
             self.p2 = None
             self.sbotton.configure(state='normal')
@@ -95,16 +100,16 @@ class MainDialog(Tkinter.Frame):
         return
 
     # run as a subprocess via pipes and collect stdout
-    def mobirdr(self, infile, outfile, pidnum):
+    def mobirdr(self, infile, outfile, pidnums):
         # os.putenv('PYTHONUNBUFFERED', '1')
-        cmdline = 'python ./lib/mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
-        if sys.platform[0:3] == 'win':
+        cmdline = 'python ./lib/k4mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnums + '"'
+        if sys.platform.startswith('win'):
             search_path = os.environ['PATH']
             search_path = search_path.lower()
             if search_path.find('python') >= 0: 
-                cmdline = 'python lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
+                cmdline = 'python lib\k4mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnums + '"'
             else :
-                cmdline = 'lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"'
+                cmdline = 'lib\k4mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnums + '"'
 
         cmdline = cmdline.encode(sys.getfilesystemencoding())
         p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
@@ -112,8 +117,10 @@ class MainDialog(Tkinter.Frame):
 
 
     def get_mobipath(self):
+        cpath = self.mobipath.get()
         mobipath = tkFileDialog.askopenfilename(
-            parent=None, title='Select Mobi eBook File',
+            initialdir = cpath,
+            parent=None, title='Select K4PC, K4M or Mobi eBook File',
             defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),
                                                 ('All Files', '.*')])
         if mobipath:
@@ -123,15 +130,11 @@ class MainDialog(Tkinter.Frame):
         return
 
     def get_outpath(self):
-        mobipath = self.mobipath.get()
-        initname = os.path.basename(mobipath)
-        p = initname.find('.')
-        if p >= 0: initname = initname[0:p]
-        initname += '_nodrm.mobi' 
-        outpath = tkFileDialog.asksaveasfilename(
-            parent=None, title='Select Unencrypted Mobi File to produce',
-            defaultextension='.mobi', initialfile=initname,
-            filetypes=[('Mobi files', '.mobi'), ('All files', '.*')])
+        cwd = os.getcwdu()
+        cwd = cwd.encode('utf-8')
+        outpath = tkFileDialog.askdirectory(
+            parent=None, title='Directory to Store Unencrypted file into',
+            initialdir=cwd, initialfile=None)
         if outpath:
             outpath = os.path.normpath(outpath)
             self.outpath.delete(0, Tkconstants.END)
@@ -151,29 +154,34 @@ class MainDialog(Tkinter.Frame):
         self.sbotton.configure(state='disabled')
         mobipath = self.mobipath.get()
         outpath = self.outpath.get()
-        pidnum = self.pidinfo.get()
+        pidnums = self.pidinfo.get()
+
         if not mobipath or not os.path.exists(mobipath):
-            self.status['text'] = 'Specified Mobi eBook file does not exist'
+            self.status['text'] = 'Specified K4PC, K4M or Mobi eBook file does not exist'
             self.sbotton.configure(state='normal')
             return
         if not outpath:
-            self.status['text'] = 'No output file specified'
+            self.status['text'] = 'No output directory specified'
             self.sbotton.configure(state='normal')
             return
-        if not pidnum or pidnum == '':
-            self.status['text'] = 'No PID specified'
+        if not os.path.isdir(outpath):
+            self.status['text'] = 'Error specified output directory does not exist'
             self.sbotton.configure(state='normal')
             return
+        # default output file name to be input file name + '_nodrm.mobi'
+        initname = os.path.splitext(os.path.basename(mobipath))[0]
+        initname += '_nodrm.mobi' 
+        outpath += os.sep + initname
 
-        log = 'Command = "python mobidedrm.py"\n'
-        log += 'Mobi Path = "'+ mobipath + '"\n'
+        log = 'Command = "python k4mobidedrm.py"\n'
+        log += 'K4PC, K4M or Mobi Path = "'+ mobipath + '"\n'
         log += 'Output File = "' + outpath + '"\n'
-        log += 'PID = "' + pidnum + '"\n'
+        log += 'PID list = "' + pidnums + '"\n'
         log += '\n\n'
         log += 'Please Wait ...\n\n'
         log = log.encode('utf-8')
         self.stext.insert(Tkconstants.END,log)
-        self.p2 = self.mobirdr(mobipath, outpath, pidnum)
+        self.p2 = self.mobirdr(mobipath, outpath, pidnums)
 
         # python does not seem to allow you to create
         # your own eventloop which every other gui does - strange 
@@ -185,7 +193,7 @@ class MainDialog(Tkinter.Frame):
 
 def main(argv=None):
     root = Tkinter.Tk()
-    root.title('Mobi eBook Encryption Removal')
+    root.title('K4PC/K4M/Mobi eBook Encryption Removal')
     root.resizable(True, False)
     root.minsize(300, 0)
     MainDialog(root).pack(fill=Tkconstants.X, expand=1)
diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/README_K4MobiDeDRM.txt b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/README_K4MobiDeDRM.txt
new file mode 100644 (file)
index 0000000..ce97ee3
--- /dev/null
@@ -0,0 +1,23 @@
+K4MobiDeDRM
+
+This tools combines functionality of MobiDeDRM with that of K4PCDeDRM, K4MDeDRM, and K4DeDRM.  Effectively, it provides one-stop shopping for all your Mobipocket, Kindle for iPhone/iPad/iPodTouch, Kindle for PC, and Kindle for Mac needs.
+
+****
+Please Note: If you a happy user of MobiDeDRM, K4DeDRM, K4PCDeDRM, or K4MUnswindle, please continue to use these programs as there is no additional capability provided by this tool over the others.  In the long run, if you have problems with any of those tools, you might want to try this one as it will continue under development eventually replacing all of those tools.
+****
+
+1. double-click on K4MobiDeDRM.pyw
+
+2. In the window that opens:
+hit the first '...' button to locate your DRM Kindle-style ebook
+
+3. Then hit the second '...' button to select an output directory for the unlocked file
+
+4. Then add in any PIDs you need from KindleV1, Kindle for iPhone/iPad/iPodTouch, or other single PID devices to the provided box as a comma separated list of 10 digit PID numbers.
+
+If this is a Kindle for Mac or a Kindle for PC book then you can leave this box blank
+
+5.  hit the 'Start' button
+
+After a short delay, you should see progress in the Conversion Log window indicating is the unlocking was a success or failure.
+
diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py
new file mode 100644 (file)
index 0000000..6a5c071
--- /dev/null
@@ -0,0 +1,490 @@
+#!/usr/bin/env python
+
+# engine to remove drm from Kindle for Mac and Kindle for PC books
+# for personal use for archiving and converting your ebooks
+
+# PLEASE DO NOT PIRATE EBOOKS! 
+
+# We want all authors and publishers, and eBook stores to live
+# long and prosperous lives but at the same time  we just want to 
+# be able to read OUR books on whatever device we want and to keep 
+# readable for a long, long time
+
+#  This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle, 
+#    unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates 
+#    and many many others
+
+# It can run standalone to convert K4M/K4PC/Mobi files, or it can be installed as a
+# plugin for Calibre (http://calibre-ebook.com/about) so that importing
+# K4 or Mobi with DRM is no londer a multi-step process.
+#
+# ***NOTE*** If you are using this script as a calibre plugin for a K4M or K4PC ebook
+# then calibre must be installed on the same machine and in the same account as K4PC or K4M
+# for the plugin version to function properly.
+#
+# To create a Calibre plugin, rename this file so that the filename
+# ends in '_plugin.py', put it into a ZIP file with all its supporting python routines
+# and import that ZIP into Calibre using its plugin configuration GUI.
+
+from __future__ import with_statement
+
+__version__ = '1.1'
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+import os, csv, getopt
+import binascii
+import zlib
+from struct import pack, unpack, unpack_from
+
+
+#Exception Handling
+class DrmException(Exception):
+    pass
+
+#
+# crypto digestroutines
+#
+
+import hashlib
+
+def MD5(message):
+    ctx = hashlib.md5()
+    ctx.update(message)
+    return ctx.digest()
+
+def SHA1(message):
+    ctx = hashlib.sha1()
+    ctx.update(message)
+    return ctx.digest()
+
+# determine if we are running as a calibre plugin
+if 'calibre' in sys.modules:
+    inCalibre = True
+    global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+else:
+    inCalibre = False
+
+#
+# start of Kindle specific routines
+#
+
+if not inCalibre:
+    import mobidedrm
+    if sys.platform.startswith('win'):
+        from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+    if sys.platform.startswith('darwin'):
+        from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+
+global kindleDatabase
+
+# Encode the bytes in data with the characters in map
+def encode(data, map):
+    result = ""
+    for char in data:
+        value = ord(char)
+        Q = (value ^ 0x80) // len(map)
+        R = value % len(map)
+        result += map[Q]
+        result += map[R]
+    return result
+  
+# Hash the bytes in data and then encode the digest with the characters in map
+def encodeHash(data,map):
+    return encode(MD5(data),map)
+
+# Decode the string in data with the characters in map. Returns the decoded bytes
+def decode(data,map):
+    result = ""
+    for i in range (0,len(data)-1,2):
+        high = map.find(data[i])
+        low = map.find(data[i+1])
+        if (high == -1) or (low == -1) :
+            break
+        value = (((high * len(map)) ^ 0x80) & 0xFF) + low
+        result += pack("B",value)
+    return result
+
+
+# Parse the Kindle.info file and return the records as a list of key-values
+def parseKindleInfo():
+    DB = {}
+    infoReader = openKindleInfo()
+    infoReader.read(1)
+    data = infoReader.read()
+    if sys.platform.startswith('win'):
+        items = data.split('{')
+    else :
+        items = data.split('[')
+    for item in items:
+        splito = item.split(':')
+        DB[splito[0]] =splito[1]
+    return DB
+
+# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
+def getKindleInfoValueForHash(hashedKey):
+    global kindleDatabase
+    encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
+    if sys.platform.startswith('win'):
+        return CryptUnprotectData(encryptedValue,"")
+    else:
+        cleartext = CryptUnprotectData(encryptedValue)
+        return decode(cleartext, charMap1)
+#  Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
+def getKindleInfoValueForKey(key):
+    return getKindleInfoValueForHash(encodeHash(key,charMap2))
+
+# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string.
+def findNameForHash(hash):
+    names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
+    result = ""
+    for name in names:
+        if hash == encodeHash(name, charMap2):
+           result = name
+           break
+    return result
+    
+# Print all the records from the kindle.info file (option -i)
+def printKindleInfo():
+    for record in kindleDatabase:
+        name = findNameForHash(record)
+        if name != "" :
+            print (name)
+            print ("--------------------------")
+        else :
+            print ("Unknown Record")
+        print getKindleInfoValueForHash(record)
+        print "\n"
+
+#
+# PID generation routines
+#
+  
+# Returns two bit at offset from a bit field
+def getTwoBitsFromBitField(bitField,offset):
+    byteNumber = offset // 4
+    bitPosition = 6 - 2*(offset % 4)
+    return ord(bitField[byteNumber]) >> bitPosition & 3
+
+# Returns the six bits at offset from a bit field
+def getSixBitsFromBitField(bitField,offset):
+     offset *= 3
+     value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
+     return value
+     
+# 8 bits to six bits encoding from hash to generate PID string
+def encodePID(hash):
+    global charMap3
+    PID = ""
+    for position in range (0,8):
+        PID += charMap3[getSixBitsFromBitField(hash,position)]
+    return PID
+
+# Encryption table used to generate the device PID
+def generatePidEncryptionTable() :
+    table = []
+    for counter1 in range (0,0x100):
+        value = counter1
+        for counter2 in range (0,8):
+            if (value & 1 == 0) :
+                value = value >> 1
+            else :
+                value = value >> 1
+                value = value ^ 0xEDB88320
+        table.append(value)
+    return table
+
+# Seed value used to generate the device PID
+def generatePidSeed(table,dsn) :
+    value = 0
+    for counter in range (0,4) :
+       index = (ord(dsn[counter]) ^ value) &0xFF
+       value = (value >> 8) ^ table[index]
+    return value
+
+# Generate the device PID
+def generateDevicePID(table,dsn,nbRoll):
+    seed = generatePidSeed(table,dsn)
+    pidAscii = ""
+    pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
+    index = 0
+    for counter in range (0,nbRoll):
+        pid[index] = pid[index] ^ ord(dsn[counter])
+        index = (index+1) %8
+    for counter in range (0,8):
+        index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
+        pidAscii += charMap4[index]
+    return pidAscii
+
+# convert from 8 digit PID to 10 digit PID with checksum
+def checksumPid(s):
+    letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+    crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
+    crc = crc ^ (crc >> 16)
+    res = s
+    l = len(letters)
+    for i in (0,1):
+        b = crc & 0xff
+        pos = (b // l) ^ (b % l)
+        res += letters[pos%l]
+        crc >>= 8
+    return res
+
+
+class MobiPeek:
+    def loadSection(self, section):
+        before, after = self.sections[section:section+2]
+        self.f.seek(before)
+        return self.f.read(after - before)
+    def __init__(self, filename):
+        self.f = file(filename, 'rb')
+        self.header = self.f.read(78)
+        self.ident = self.header[0x3C:0x3C+8]
+        if self.ident != 'BOOKMOBI' and self.ident != 'TEXtREAd':
+            raise DrmException('invalid file format')
+        self.num_sections, = unpack_from('>H', self.header, 76)
+        sections = self.f.read(self.num_sections*8)
+        self.sections = unpack_from('>%dL' % (self.num_sections*2), sections, 0)[::2] + (0xfffffff, )
+        self.sect0 = self.loadSection(0)
+        self.f.close()
+    def getBookTitle(self):
+        # get book title
+        toff, tlen = unpack('>II', self.sect0[0x54:0x5c])
+        tend = toff + tlen
+        title = self.sect0[toff:tend]
+        return title
+    def getexthData(self):
+        # if exth region exists then grab it
+        # get length of this header
+        length, type, codepage, unique_id, version = unpack('>LLLLL', self.sect0[20:40])
+        exth_flag, = unpack('>L', self.sect0[0x80:0x84])
+        exth = ''
+        if exth_flag & 0x40:
+            exth = self.sect0[16 + length:]
+        return exth
+    def isNotEncrypted(self):
+        lock_type, = unpack('>H', self.sect0[0xC:0xC+2])
+        if lock_type == 0:
+            return True
+        return False
+
+# DiapDealer's stuff: Parse the EXTH header records and parse the Kindleinfo
+# file to calculate the book pid.
+def getK4Pids(exth, title):
+    global kindleDatabase
+    try:
+        kindleDatabase = parseKindleInfo()
+    except Exception as message:
+        print(message)
+    
+    if kindleDatabase != None :
+        # Get the Mazama Random number
+        MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
+
+        # Get the HDD serial
+        encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1)
+
+        # Get the current user name
+        encodedUsername = encodeHash(GetUserName(),charMap1)
+
+        # concat, hash and encode to calculate the DSN
+        DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
+       
+        print("\nDSN: " + DSN)
+
+        # Compute the device PID (for which I can tell, is used for nothing).
+        # But hey, stuff being printed out is apparently cool.
+        table =  generatePidEncryptionTable()
+        devicePID = generateDevicePID(table,DSN,4)
+            
+        print("Device PID: " + checksumPid(devicePID))
+            
+        # Compute book PID
+        exth_records = {}
+        nitems, = unpack('>I', exth[8:12])
+        pos = 12
+        # Parse the exth records, storing data indexed by type
+        for i in xrange(nitems):
+            type, size = unpack('>II', exth[pos: pos + 8])
+            content = exth[pos + 8: pos + size]
+
+            exth_records[type] = content
+            pos += size
+
+        # Grab the contents of the type 209 exth record
+        if exth_records[209] != None:
+            data = exth_records[209]
+        else:
+            raise DrmException("\nNo EXTH record type 209 - Perhaps not a K4 file?")
+        
+        # Parse the 209 data to find the the exth record with the token data.
+        # The last character of the 209 data points to the record with the token.
+        # Always 208 from my experience, but I'll leave the logic in case that changes.
+        for i in xrange(len(data)):
+            if ord(data[i]) != 0:
+                if exth_records[ord(data[i])] != None:
+                    token = exth_records[ord(data[i])]
+
+        # Get the kindle account token
+        kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
+    
+        print("Account Token: " + kindleAccountToken)
+
+        pidHash = SHA1(DSN+kindleAccountToken+exth_records[209]+token)
+   
+        bookPID = encodePID(pidHash)
+        bookPID = checksumPid(bookPID)
+
+        if exth_records[503] != None:
+            print "Pid for " + exth_records[503] + ": " + bookPID
+        else:
+            print "Pid for " + title + ":" + bookPID
+        return bookPID
+    
+    raise DrmException("\nCould not access K4 data - Perhaps K4 is not installed/configured?")
+    return null
+
+#
+# Main
+#   
+def main(argv=sys.argv):
+    global kindleDatabase
+    import mobidedrm
+    print ('K4MobiDeDrm v%(__version__)s '
+          'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
+
+    if len(argv)<3:
+        print "Removes DRM protection from K4PC, K4M, and Mobi ebooks"
+        print "Usage:"
+        print "    %s <infile> <outfile> [<pidnums>]" % argv[0]
+        return 1
+
+    if len(argv) == 4:
+        pidnums = argv[3]
+
+    if len(argv) == 3:
+        pidnums = "" 
+
+    kindleDatabase = None
+    infile = argv[1]
+    outfile = argv[2]
+    try:
+        # first try with K4PC/K4M
+        ex = MobiPeek(infile)
+        if ex.isNotEncrypted():
+            print "File was Not Encrypted"
+            return 2
+        title = ex.getBookTitle()
+        exth = ex.getexthData()
+        pid = getK4Pids(exth, title)
+        unlocked_file = mobidedrm.getUnencryptedBook(infile, pid)
+    except DrmException:
+        pass
+    except mobidedrm.DrmException:
+        pass
+    else:
+        file(outfile, 'wb').write(unlocked_file)
+        return 0
+
+    # now try from the pid list
+    pids = pidnums.split(',')
+    for pid in pids:
+        try:
+            print 'Trying: "'+ pid + '"'
+            unlocked_file = mobidedrm.getUnencryptedBook(infile, pid)
+        except mobidedrm.DrmException:
+            pass
+        else:
+            file(outfile, 'wb').write(unlocked_file)
+            return 0
+
+    # we could not unencrypt book
+    print "Error: Could Not Unencrypt Book"
+    return 1
+
+
+if __name__ == '__main__':
+    sys.stdout=Unbuffered(sys.stdout)
+    sys.exit(main())
+
+
+if not __name__ == "__main__" and inCalibre:
+    from calibre.customize import FileTypePlugin
+
+    class K4DeDRM(FileTypePlugin):
+        name                = 'K4PC, K4Mac, Mobi DeDRM' # Name of the plugin
+        description         = 'Removes DRM from K4PC, K4Mac, and Mobi files. \
+                                Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.'
+        supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on
+        author              = 'DiapDealer, SomeUpdates' # The author of this plugin
+        version             = (0, 0, 1)   # The version number of this plugin
+        file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
+        on_import           = True # Run this plugin during the import
+        priority            = 200  # run this plugin before mobidedrm, k4pcdedrm, k4dedrm
+
+        def run(self, path_to_ebook):
+            from calibre.gui2 import is_ok_to_use_qt
+            from PyQt4.Qt import QMessageBox
+            global kindleDatabase
+            global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            if sys.platform.startswith('win'):
+                from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            if sys.platform.startswith('darwin'):
+                from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
+            import mobidedrm
+
+            pidnums = self.site_customization
+
+            # first try with book specifc pid from K4PC or K4M
+            try:
+                kindleDatabase = None
+                ex = MobiPeek(path_to_ebook)
+                if ex.isNotEncrypted():
+                    return path_to_ebook
+                title = ex.getBookTitle()
+                exth = ex.getexthData()
+                pid = getK4Pids(exth, title)
+                unlocked_file = mobidedrm.getUnencryptedBook(path_to_ebook,pid)
+            except DrmException:
+                pass
+            except mobidedrm.DrmException:
+                pass
+            else:
+                of = self.temporary_file('.mobi')
+                of.write(unlocked_file)
+                of.close()
+                return of.name
+
+            # now try from the pid list
+            pids = pidnums.split(',')
+            for pid in pids:
+                try:
+                    unlocked_file = mobidedrm.getUnencryptedBook(path_to_ebook, pid)
+                except mobidedrm.DrmException:
+                    pass
+                else:
+                    of = self.temporary_file('.mobi')
+                    of.write(unlocked_file)
+                    of.close()
+                    return of.name
+
+            #if you reached here then no luck raise and exception
+            if is_ok_to_use_qt():
+                d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook)
+                d.show()
+                d.raise_()
+                d.exec_()
+            raise Exception("K4MobiDeDRM plugin could not decode the file")
+            return ""
+
+        def customization_help(self, gui=False):
+            return 'Enter each 10 character PID separated by a comma (no spaces).'
diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mutils.py b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mutils.py
new file mode 100644 (file)
index 0000000..cb13e5f
--- /dev/null
@@ -0,0 +1,319 @@
+# standlone set of Mac OSX specific routines needed for K4DeDRM
+
+from __future__ import with_statement
+
+import sys
+import os
+
+#Exception Handling
+class K4MDrmException(Exception):
+    pass
+
+import signal
+import threading
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+# **heavily** chopped up and modfied version of asyncproc.py
+# to make it actually work on Windows as well as Mac/Linux
+# For the original see:
+# "http://www.lysator.liu.se/~bellman/download/"
+# author is  "Thomas Bellman <bellman@lysator.liu.se>"
+# available under GPL version 3 or Later
+
+# create an asynchronous subprocess whose output can be collected in
+# a non-blocking manner
+
+# What a mess!  Have to use threads just to get non-blocking io
+# in a cross-platform manner
+
+# luckily all thread use is hidden within this class
+
+class Process(object):
+    def __init__(self, *params, **kwparams):
+        if len(params) <= 3:
+            kwparams.setdefault('stdin', subprocess.PIPE)
+        if len(params) <= 4:
+            kwparams.setdefault('stdout', subprocess.PIPE)
+        if len(params) <= 5:
+            kwparams.setdefault('stderr', subprocess.PIPE)
+        self.__pending_input = []
+        self.__collected_outdata = []
+        self.__collected_errdata = []
+        self.__exitstatus = None
+        self.__lock = threading.Lock()
+        self.__inputsem = threading.Semaphore(0)
+        self.__quit = False
+
+        self.__process = subprocess.Popen(*params, **kwparams)
+
+        if self.__process.stdin:
+            self.__stdin_thread = threading.Thread(
+                name="stdin-thread",
+                target=self.__feeder, args=(self.__pending_input,
+                                            self.__process.stdin))
+            self.__stdin_thread.setDaemon(True)
+            self.__stdin_thread.start()
+
+        if self.__process.stdout:
+            self.__stdout_thread = threading.Thread(
+                name="stdout-thread",
+                target=self.__reader, args=(self.__collected_outdata,
+                                           self.__process.stdout))
+            self.__stdout_thread.setDaemon(True)
+            self.__stdout_thread.start()
+
+        if self.__process.stderr:
+            self.__stderr_thread = threading.Thread(
+                name="stderr-thread",
+                target=self.__reader, args=(self.__collected_errdata,
+                                           self.__process.stderr))
+            self.__stderr_thread.setDaemon(True)
+            self.__stderr_thread.start()
+
+    def pid(self):
+        return self.__process.pid
+
+    def kill(self, signal):
+        self.__process.send_signal(signal)
+
+    # check on subprocess (pass in 'nowait') to act like poll
+    def wait(self, flag):
+        if flag.lower() == 'nowait':
+            rc = self.__process.poll()
+        else:
+            rc = self.__process.wait()
+        if rc != None:
+            if self.__process.stdin:
+                self.closeinput()
+            if self.__process.stdout:
+                self.__stdout_thread.join()
+            if self.__process.stderr:
+                self.__stderr_thread.join()
+        return self.__process.returncode
+
+    def terminate(self):
+        if self.__process.stdin:
+            self.closeinput()
+        self.__process.terminate()
+
+    # thread gets data from subprocess stdout
+    def __reader(self, collector, source):
+        while True:
+            data = os.read(source.fileno(), 65536)
+            self.__lock.acquire()
+            collector.append(data)
+            self.__lock.release()
+            if data == "":
+                source.close()
+                break
+        return
+
+    # thread feeds data to subprocess stdin
+    def __feeder(self, pending, drain):
+        while True:
+            self.__inputsem.acquire()
+            self.__lock.acquire()
+            if not pending  and self.__quit:
+                drain.close()
+                self.__lock.release()
+                break
+            data = pending.pop(0)
+            self.__lock.release()
+            drain.write(data)
+
+    # non-blocking read of data from subprocess stdout
+    def read(self):
+        self.__lock.acquire()
+        outdata = "".join(self.__collected_outdata)
+        del self.__collected_outdata[:]
+        self.__lock.release()
+        return outdata
+
+    # non-blocking read of data from subprocess stderr
+    def readerr(self):
+        self.__lock.acquire()
+        errdata = "".join(self.__collected_errdata)
+        del self.__collected_errdata[:]
+        self.__lock.release()
+        return errdata
+
+    # non-blocking write to stdin of subprocess
+    def write(self, data):
+        if self.__process.stdin is None:
+            raise ValueError("Writing to process with stdin not a pipe")
+        self.__lock.acquire()
+        self.__pending_input.append(data)
+        self.__inputsem.release()
+        self.__lock.release()
+
+    # close stdinput of subprocess
+    def closeinput(self):
+        self.__lock.acquire()
+        self.__quit = True
+        self.__inputsem.release()
+        self.__lock.release()
+
+
+# interface to needed routines in openssl's libcrypto
+def _load_crypto_libcrypto():
+    from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
+        Structure, c_ulong, create_string_buffer, addressof, string_at, cast
+    from ctypes.util import find_library
+
+    libcrypto = find_library('crypto')
+    if libcrypto is None:
+        raise K4MDrmException('libcrypto not found')
+    libcrypto = CDLL(libcrypto)
+
+    AES_MAXNR = 14
+    c_char_pp = POINTER(c_char_p)
+    c_int_p = POINTER(c_int)
+
+    class AES_KEY(Structure):
+        _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
+    AES_KEY_p = POINTER(AES_KEY)
+    
+    def F(restype, name, argtypes):
+        func = getattr(libcrypto, name)
+        func.restype = restype
+        func.argtypes = argtypes
+        return func
+    
+    AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int])
+
+    AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
+
+    PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1', 
+                                [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
+    
+    class LibCrypto(object):
+        def __init__(self):
+            self._blocksize = 0
+            self._keyctx = None
+            self.iv = 0
+
+        def set_decrypt_key(self, userkey, iv):
+            self._blocksize = len(userkey)
+            if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
+                raise K4MDrmException('AES improper key used')
+                return
+            keyctx = self._keyctx = AES_KEY()
+            self.iv = iv
+            rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
+            if rv < 0:
+                raise K4MDrmException('Failed to initialize AES key')
+
+        def decrypt(self, data):
+            out = create_string_buffer(len(data))
+            rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0)
+            if rv == 0:
+                raise K4MDrmException('AES decryption failed')
+            return out.raw
+
+        def keyivgen(self, passwd):
+            salt = '16743'
+            saltlen = 5
+            passlen = len(passwd)
+            iter = 0x3e8
+            keylen = 80
+            out = create_string_buffer(keylen)
+            rv = PKCS5_PBKDF2_HMAC_SHA1(passwd, passlen, salt, saltlen, iter, keylen, out)
+            return out.raw
+    return LibCrypto
+
+def _load_crypto():
+    LibCrypto = None
+    try:
+        LibCrypto = _load_crypto_libcrypto()
+    except (ImportError, K4MDrmException):
+        pass
+    return LibCrypto
+
+LibCrypto = _load_crypto()
+
+#
+# Utility Routines
+#
+
+# uses a sub process to get the Hard Drive Serial Number using ioreg
+# returns with the first found serial number in that class
+def GetVolumeSerialNumber():
+    cmdline = '/usr/sbin/ioreg -r -c AppleAHCIDiskDriver'
+    cmdline = cmdline.encode(sys.getfilesystemencoding())
+    p = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+    poll = p.wait('wait')
+    results = p.read()
+    reslst = results.split('\n')
+    sernum = '9999999999'
+    cnt = len(reslst)
+    for j in xrange(cnt):
+        resline = reslst[j]
+        pp = resline.find('"Serial Number" = "')
+        if pp >= 0:
+            sernum = resline[pp+19:]
+            sernum = sernum[:-1]
+            sernum = sernum.lstrip()
+            break
+    return sernum
+
+# uses unix env to get username instead of using sysctlbyname 
+def GetUserName():
+    username = os.getenv('USER')
+    return username
+
+# Various character maps used to decrypt books. Probably supposed to act as obfuscation
+charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
+charMap2 = "ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM" 
+charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+
+def encode(data, map):
+    result = ""
+    for char in data:
+        value = ord(char)
+        Q = (value ^ 0x80) // len(map)
+        R = value % len(map)
+        result += map[Q]
+        result += map[R]
+    return result
+
+import hashlib
+
+def SHA256(message):
+    ctx = hashlib.sha256()
+    ctx.update(message)
+    return ctx.digest()
+
+# implements an Pseudo Mac Version of Windows built-in Crypto routine
+def CryptUnprotectData(encryptedData):
+    sp = GetVolumeSerialNumber() + '!@#' + GetUserName()
+    passwdData = encode(SHA256(sp),charMap1)
+    crp = LibCrypto()
+    key_iv = crp.keyivgen(passwdData)
+    key = key_iv[0:32]
+    iv = key_iv[32:48]
+    crp.set_decrypt_key(key,iv)
+    cleartext = crp.decrypt(encryptedData)
+    return cleartext
+
+# Locate and open the .kindle-info file
+def openKindleInfo():
+    home = os.getenv('HOME')
+    cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"'
+    cmdline = cmdline.encode(sys.getfilesystemencoding())
+    p1 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+    poll = p1.wait('wait')
+    results = p1.read()
+    reslst = results.split('\n')
+    kinfopath = 'NONE'
+    cnt = len(reslst)
+    for j in xrange(cnt):
+        resline = reslst[j]
+        pp = resline.find('.kindle-info')
+        if pp >= 0:
+            kinfopath = resline
+            break
+    if not os.path.exists(kinfopath):
+        raise K4MDrmException('Error: .kindle-info file can not be found')
+    return open(kinfopath,'r')
diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4pcutils.py b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4pcutils.py
new file mode 100644 (file)
index 0000000..777376d
--- /dev/null
@@ -0,0 +1,107 @@
+# K4PC Windows specific routines
+
+from __future__ import with_statement
+
+import sys, os
+
+from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
+    create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
+    string_at, Structure, c_void_p, cast
+
+import _winreg as winreg
+
+import traceback
+
+MAX_PATH = 255
+
+kernel32 = windll.kernel32
+advapi32 = windll.advapi32
+crypt32 = windll.crypt32
+
+
+#
+# Various character maps used to decrypt books. Probably supposed to act as obfuscation
+#
+charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
+charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
+charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+
+#
+# Exceptions for all the problems that might happen during the script
+#
+class DrmException(Exception):
+    pass
+    
+
+class DataBlob(Structure):
+    _fields_ = [('cbData', c_uint),
+                ('pbData', c_void_p)]
+DataBlob_p = POINTER(DataBlob)
+
+
+def GetSystemDirectory():
+    GetSystemDirectoryW = kernel32.GetSystemDirectoryW
+    GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
+    GetSystemDirectoryW.restype = c_uint
+    def GetSystemDirectory():
+        buffer = create_unicode_buffer(MAX_PATH + 1)
+        GetSystemDirectoryW(buffer, len(buffer))
+        return buffer.value
+    return GetSystemDirectory
+GetSystemDirectory = GetSystemDirectory()
+
+def GetVolumeSerialNumber():
+    GetVolumeInformationW = kernel32.GetVolumeInformationW
+    GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
+                                      POINTER(c_uint), POINTER(c_uint),
+                                      POINTER(c_uint), c_wchar_p, c_uint]
+    GetVolumeInformationW.restype = c_uint
+    def GetVolumeSerialNumber(path = GetSystemDirectory().split('\\')[0] + '\\'):
+        vsn = c_uint(0)
+        GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
+        return str(vsn.value)
+    return GetVolumeSerialNumber
+GetVolumeSerialNumber = GetVolumeSerialNumber()
+
+
+def GetUserName():
+    GetUserNameW = advapi32.GetUserNameW
+    GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
+    GetUserNameW.restype = c_uint
+    def GetUserName():
+        buffer = create_unicode_buffer(32)
+        size = c_uint(len(buffer))
+        while not GetUserNameW(buffer, byref(size)):
+            buffer = create_unicode_buffer(len(buffer) * 2)
+            size.value = len(buffer)
+        return buffer.value.encode('utf-16-le')[::2]
+    return GetUserName
+GetUserName = GetUserName()
+
+
+def CryptUnprotectData():
+    _CryptUnprotectData = crypt32.CryptUnprotectData
+    _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
+                                   c_void_p, c_void_p, c_uint, DataBlob_p]
+    _CryptUnprotectData.restype = c_uint
+    def CryptUnprotectData(indata, entropy):
+        indatab = create_string_buffer(indata)
+        indata = DataBlob(len(indata), cast(indatab, c_void_p))
+        entropyb = create_string_buffer(entropy)
+        entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
+        outdata = DataBlob()
+        if not _CryptUnprotectData(byref(indata), None, byref(entropy),
+                                   None, None, 0, byref(outdata)):
+            raise DrmException("Failed to Unprotect Data")
+        return string_at(outdata.pbData, outdata.cbData)
+    return CryptUnprotectData
+CryptUnprotectData = CryptUnprotectData()
+
+#
+# Locate and open the Kindle.info file.
+#
+def openKindleInfo():
+    regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
+    path = winreg.QueryValueEx(regkey, 'Local AppData')[0] 
+    return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
similarity index 89%
rename from Calibre_Plugins/MobiDeDRM_plugin/MobiDeDRM_plugin.py
rename to Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py
index 07d5f6f5eca968c5492909043cc4cab7df3f1e15..5ed58a52aeafc886838f1bdce2506b8657f3ac4c 100644 (file)
 #         trailing data byte flags - version 5 and higher AND header size >= 0xE4. 
 #  0.15 - Now outputs 'hearbeat', and is also quicker for long files.
 #  0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
+#  0.17 - added modifications to support its use as an imported python module
+#         both inside calibre and also in other places (ie K4DeDRM tools)
+# 0.17a - disabled the standalone plugin feature since a plugin can not import
+#         a plugin
 
-__version__ = '0.16'
+__version__ = '0.17'
 
 import sys
 import struct
@@ -248,7 +252,42 @@ class DrmStripper:
     def getResult(self):
         return self.data_file
 
-if not __name__ == "__main__":
+def getUnencryptedBook(infile,pid):
+    sys.stdout=Unbuffered(sys.stdout)
+    data_file = file(infile, 'rb').read()
+    strippedFile = DrmStripper(data_file, pid)
+    return strippedFile.getResult()
+
+def main(argv=sys.argv):
+    sys.stdout=Unbuffered(sys.stdout)
+    print ('MobiDeDrm v%(__version__)s. '
+          'Copyright 2008-2010 The Dark Reverser.' % globals())
+    if len(argv)<4:
+        print "Removes protection from Mobipocket books"
+        print "Usage:"
+        print "    %s <infile> <outfile> <PID>" % sys.argv[0]
+        return 1
+    else:
+        infile = argv[1]
+        outfile = argv[2]
+        pid = argv[3]
+        try:
+            stripped_file = getUnencryptedBook(infile, pid)
+            file(outfile, 'wb').write(stripped_file)
+        except DrmException, e:
+            print "Error: %s" % e
+            return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
+
+#if not __name__ == "__main__":
+if False:
+
+    # note a calibre plugin can not import code with another calibre plugin
+    # in it as it ends up registering two different plugins 
     from calibre.customize import FileTypePlugin
 
     class MobiDeDRM(FileTypePlugin):
@@ -256,7 +295,7 @@ if not __name__ == "__main__":
         description         = 'Removes DRM from secure Mobi files'
         supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
         author              = 'The Dark Reverser' # The author of this plugin
-        version             = (0, 1, 6)   # The version number of this plugin
+        version             = (0, 1, 7)   # The version number of this plugin
         file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
         on_import           = True # Run this plugin during the import
 
@@ -270,41 +309,17 @@ if not __name__ == "__main__":
                 try:
                     unlocked_file = DrmStripper(data_file, i).getResult()
                 except DrmException:
-                    # ignore the error
-                    pass
+                    if is_ok_to_use_qt():
+                        d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook)
+                        d.show()
+                        d.raise_()
+                        d.exec_()
+                    raise Exception("MobiDeDRM Plugin: Error decoding ebook")
                 else:
                     of = self.temporary_file('.mobi')
                     of.write(unlocked_file)
                     of.close()
                     return of.name
-            if is_ok_to_use_qt():
-                d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Couldn't decode: %s\n\nImporting encrypted version." % path_to_ebook)
-                d.show()
-                d.raise_()
-                d.exec_()
-            return path_to_ebook
 
         def customization_help(self, gui=False):
             return 'Enter PID (separate multiple PIDs with comma)'
-
-if __name__ == "__main__":
-    sys.stdout=Unbuffered(sys.stdout)
-    print ('MobiDeDrm v%(__version__)s. '
-          'Copyright 2008-2010 The Dark Reverser.' % globals())
-    if len(sys.argv)<4:
-        print "Removes protection from Mobipocket books"
-        print "Usage:"
-        print "    %s <infile> <outfile> <PID>" % sys.argv[0]
-        sys.exit(1)
-    else:
-        infile = sys.argv[1]
-        outfile = sys.argv[2]
-        pid = sys.argv[3]
-        data_file = file(infile, 'rb').read()
-        try:
-            strippedFile = DrmStripper(data_file, pid)
-            file(outfile, 'wb').write(strippedFile.getResult())
-        except DrmException, e:
-            print "Error: %s" % e
-            sys.exit(1)
-    sys.exit(0)
\ No newline at end of file
similarity index 70%
rename from Kindle_4_Mac_Tools/README_K4Mac_Tools.txt
rename to Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/README_K4Munswindle.txt
index 43b46647e92b149613d0c0056825d0b78e25069b..46f6a38d6c8621e82d27067d79d206648ba4476f 100644 (file)
@@ -1,13 +1,4 @@
-Kindle_4_Mac_Tools version 1.2
-
-Kindle_4_Mac_Tools_v1.2.zip
-http://www.mediafire.com/?8nz9rkg6p9nq23r
-
-
-New in this release:
- - support to identify Topaz Books and print their PID
-   so that standard Topaz_Tools can be used later if
-   desired (only works with Kindle Version 1.2.0)
+K4MUnswindle
 
 Prerequisites:
 
@@ -26,11 +17,7 @@ Prerequisites:
 
 The directions for use are:
 
-1. double-click to unzip the Kindle_4_Mac_Tools_v1.2.zip
-
-2. open the Kindle_4_Mac_Tools folder 
-
-3. double-click on K4Munswindle.pyw
+1. double-click on K4Munswindle.pyw
 
 In the window that opens:
 
@@ -44,18 +31,18 @@ In the window that opens:
 
 After a short delay, your Kindle application should open up automagically
 
-In Kindle for Mac:
+2. In Kindle for Mac:
 
  - hit the  “Home” button to go home. 
 
  - double-click on ONE of your books.
    This should open the book.
 
-Once the book you want is open
+3. Once the book you want is open
 
 -  hit the “Home” button and then exit the Kindle for Mac application
 
-Once you have exited the Kindle for Mac application you should see one of the following:
+4. Once you have exited the Kindle for Mac application you should see one of the following:
 
   - If the book you selected was a Topaz Book:
 
diff --git a/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/README-unswindlev7.txt b/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/README-unswindlev7.txt
new file mode 100644 (file)
index 0000000..46dc417
--- /dev/null
@@ -0,0 +1,5 @@
+README
+
+unswindle can be used to find the book specific PID but it needs to be updated for each version of Kindle4PC that Amazon releases (and therefore is also useful for Linux users who have Wine). This program “patches” the Kindle4PC executable and therefore is very release specific.
+
+Unfortunately unswindle v7 the latest, has not been updated to work with the latest version of Kindle for PC.  You will need to find one of the older versions of Kindle4PC and prevent later updates in order to use this tool.
diff --git a/Kindle_Mobi_Tools/PIDCheck.py b/Kindle_Mobi_Tools/PIDCheck.py
deleted file mode 100644 (file)
index 1960453..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/python
-#
-# This is a python script. You need a Python interpreter to run it.
-# For example, ActiveState Python, which exists for windows.
-#
-# Changelog
-#  1.00 - Initial version
-
-__version__ = '1.00'
-
-import sys
-import struct
-import binascii
-
-def checksumPid(s):
-    letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
-    crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
-    crc = crc ^ (crc >> 16)
-    res = s
-    l = len(letters)
-    for i in (0,1):
-        b = crc & 0xff
-        pos = (b // l) ^ (b % l)
-        res += letters[pos%l]
-        crc >>= 8
-    return res
-
-if __name__ == "__main__":
-    if len(sys.argv) != 2:
-        print "Checks Mobipocket PID checksum"
-        print "Usage:"
-        print "    %s <PID>" % sys.argv[0]
-        sys.exit(1)
-    else:
-        pid = sys.argv[1]
-        if len(pid) == 8:
-           pid = checksumPid(pid)
-        else:
-           pid = checksumPid(pid[:8])
-        print pid
-    sys.exit(0)
\ No newline at end of file
diff --git a/Kindle_Mobi_Tools/REAME_MobiDeDRM.txt b/Kindle_Mobi_Tools/REAME_MobiDeDRM.txt
deleted file mode 100644 (file)
index f3c6ef7..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-- MobiDeDRM.pyw is a simple graphical front end to the mobidedrm.py progam that actually does the DRM removal as long as you know the correct PID to use with it (which depends on the versions of Kindle software used). This is a gui program, simply double-click to launch it.
-
-PIDCHeck.py is a command line python tools to take an 8 digit PID and add the 2 checksum digits to the end to generate a 10 digit PID 
-
-All of these scripts are python programs.   Python 2.X (32 bit) is already installed in Mac OSX and Linux.  We recommend ActiveState's Active Python Version 2.X (32 bit) for Windows users.
-
diff --git a/Kindle_Mobi_Tools/lib/readme.txt b/Kindle_Mobi_Tools/lib/readme.txt
deleted file mode 100644 (file)
index 3a53cdb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-Kindle Mobipocket tools 0.2
-Copyright (c) 2007, 2009 Igor Skochinsky <skochinsky@mail.ru>
-
-These scripts allow one to read legally purchased Secure Mobipocket books
-on Amazon Kindle or Kindle for iPhone.
-
-* kindlepid.py
-  This script generates Mobipocket PID from the Kindle serial number or iPhone/iPod Touch
-  identifier (UDID). That PID can then be added at a Mobi retailer site and used for downloading 
-  books locked to the Kindle.
-  
-  Example: 
-    
-    > kindlepid.py B001BAB012345678
-    Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007, 2009 Igor Skochinsky
-    Kindle 1 serial number detected
-    Mobipocked PID for Kindle serial# B001BAB012345678 is V176CXM*FZ
-
-* kindlefix.py
-  This script adds a "CustomDRM" flag necessary for opening Secure
-  Mobipocket books on Kindle. The book has to be enabled for Kindle's PID
-  (generated by kindlepid.py). The "fixed" book is written with  
-  extension ".azw". That file can then be uploaded to Kindle for reading.
-
-  Example:
-    > kindlefix.py MyBook.mobi V176CXM*FZ
-    The Kindleizer v0.2. Copyright (c) 2007, 2009 Igor Skochinsky
-    Encryption: 2
-    Mobi publication type: 2
-    Mobi format version: 4
-    Found the matching record; setting the CustomDRM flag for Kindle
-    Output written to MyBook.azw
-
-* History
-  2007-12-12 Initial release
-  2009-03-10 Updated scripts to version 0.2
-    kindlepid.py: Added support for generating PID for iPhone (thanks to mbp)
-    kindlefix.py: Fixed corrupted metadata issue (thanks to Mark Peek)
diff --git a/Mobi_Additional_Tools/lib/mobidedrm.py b/Mobi_Additional_Tools/lib/mobidedrm.py
new file mode 100644 (file)
index 0000000..5ed58a5
--- /dev/null
@@ -0,0 +1,325 @@
+#!/usr/bin/python
+#
+# This is a python script. You need a Python interpreter to run it.
+# For example, ActiveState Python, which exists for windows.
+#
+# It can run standalone to convert files, or it can be installed as a
+# plugin for Calibre (http://calibre-ebook.com/about) so that
+# importing files with DRM 'Just Works'.
+#
+# To create a Calibre plugin, rename this file so that the filename
+# ends in '_plugin.py', put it into a ZIP file and import that Calibre
+# using its plugin configuration GUI.
+#
+# Changelog
+#  0.01 - Initial version
+#  0.02 - Huffdic compressed books were not properly decrypted
+#  0.03 - Wasn't checking MOBI header length
+#  0.04 - Wasn't sanity checking size of data record
+#  0.05 - It seems that the extra data flags take two bytes not four
+#  0.06 - And that low bit does mean something after all :-)
+#  0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
+#  0.08 - ...and also not in Mobi header version < 6
+#  0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
+#  0.10 - Outputs unencrypted files as-is, so that when run as a Calibre
+#         import filter it works when importing unencrypted files.
+#         Also now handles encrypted files that don't need a specific PID.
+#  0.11 - use autoflushed stdout and proper return values
+#  0.12 - Fix for problems with metadata import as Calibre plugin, report errors
+#  0.13 - Formatting fixes: retabbed file, removed trailing whitespace
+#         and extra blank lines, converted CR/LF pairs at ends of each line,
+#         and other cosmetic fixes.
+#  0.14 - Working out when the extra data flags are present has been problematic
+#         Versions 7 through 9 have tried to tweak the conditions, but have been
+#         only partially successful. Closer examination of lots of sample
+#         files reveals that a confusin has arisen because trailing data entries
+#         are not encrypted, but it turns out that the multibyte entries
+#         in utf8 file are encrypted. (Although neither kind gets compressed.)
+#         This knowledge leads to a simplification of the test for the 
+#         trailing data byte flags - version 5 and higher AND header size >= 0xE4. 
+#  0.15 - Now outputs 'hearbeat', and is also quicker for long files.
+#  0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
+#  0.17 - added modifications to support its use as an imported python module
+#         both inside calibre and also in other places (ie K4DeDRM tools)
+# 0.17a - disabled the standalone plugin feature since a plugin can not import
+#         a plugin
+
+__version__ = '0.17'
+
+import sys
+import struct
+import binascii
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+class DrmException(Exception):
+    pass
+
+# Implementation of Pukall Cipher 1
+def PC1(key, src, decryption=True):
+    sum1 = 0;
+    sum2 = 0;
+    keyXorVal = 0;
+    if len(key)!=16:
+        print "Bad key length!"
+        return None
+    wkey = []
+    for i in xrange(8):
+        wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
+
+    dst = ""
+    for i in xrange(len(src)):
+        temp1 = 0;
+        byteXorVal = 0;
+        for j in xrange(8):
+            temp1 ^= wkey[j]
+            sum2  = (sum2+j)*20021 + sum1
+            sum1  = (temp1*346)&0xFFFF
+            sum2  = (sum2+sum1)&0xFFFF
+            temp1 = (temp1*20021+1)&0xFFFF
+            byteXorVal ^= temp1 ^ sum2
+        curByte = ord(src[i])
+        if not decryption:
+            keyXorVal = curByte * 257;
+        curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
+        if decryption:
+            keyXorVal = curByte * 257;
+        for j in xrange(8):
+            wkey[j] ^= keyXorVal;
+        dst+=chr(curByte)
+    return dst
+
+def checksumPid(s):
+    letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+    crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
+    crc = crc ^ (crc >> 16)
+    res = s
+    l = len(letters)
+    for i in (0,1):
+        b = crc & 0xff
+        pos = (b // l) ^ (b % l)
+        res += letters[pos%l]
+        crc >>= 8
+    return res
+
+def getSizeOfTrailingDataEntries(ptr, size, flags):
+    def getSizeOfTrailingDataEntry(ptr, size):
+        bitpos, result = 0, 0
+        if size <= 0:
+            return result
+        while True:
+            v = ord(ptr[size-1])
+            result |= (v & 0x7F) << bitpos
+            bitpos += 7
+            size -= 1
+            if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
+                return result
+    num = 0
+    testflags = flags >> 1
+    while testflags:
+        if testflags & 1:
+            num += getSizeOfTrailingDataEntry(ptr, size - num)
+        testflags >>= 1
+    # Multibyte data, if present, is included in the encryption, so
+    # we do not need to check the low bit.
+    # if flags & 1:
+    #    num += (ord(ptr[size - num - 1]) & 0x3) + 1
+    return num
+
+class DrmStripper:
+    def loadSection(self, section):
+        if (section + 1 == self.num_sections):
+            endoff = len(self.data_file)
+        else:
+            endoff = self.sections[section + 1][0]
+        off = self.sections[section][0]
+        return self.data_file[off:endoff]
+
+    def patch(self, off, new):
+        self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
+
+    def patchSection(self, section, new, in_off = 0):
+        if (section + 1 == self.num_sections):
+            endoff = len(self.data_file)
+        else:
+            endoff = self.sections[section + 1][0]
+        off = self.sections[section][0]
+        assert off + in_off + len(new) <= endoff
+        self.patch(off + in_off, new)
+
+    def parseDRM(self, data, count, pid):
+        pid = pid.ljust(16,'\0')
+        keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
+        temp_key = PC1(keyvec1, pid, False)
+        temp_key_sum = sum(map(ord,temp_key)) & 0xff
+        found_key = None
+        for i in xrange(count):
+            verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
+            cookie = PC1(temp_key, cookie)
+            ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
+            if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
+                found_key = finalkey
+                break
+        if not found_key:
+            # Then try the default encoding that doesn't require a PID
+            temp_key = keyvec1
+            temp_key_sum = sum(map(ord,temp_key)) & 0xff
+            for i in xrange(count):
+                verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
+                cookie = PC1(temp_key, cookie)
+                ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
+                if verification == ver and cksum == temp_key_sum:
+                    found_key = finalkey
+                    break
+        return found_key
+
+    def __init__(self, data_file, pid):
+        if checksumPid(pid[0:-2]) != pid:
+            raise DrmException("invalid PID checksum")
+        pid = pid[0:-2]
+
+        self.data_file = data_file
+        header = data_file[0:72]
+        if header[0x3C:0x3C+8] != 'BOOKMOBI':
+            raise DrmException("invalid file format")
+        self.num_sections, = struct.unpack('>H', data_file[76:78])
+
+        self.sections = []
+        for i in xrange(self.num_sections):
+            offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
+            flags, val = a1, a2<<16|a3<<8|a4
+            self.sections.append( (offset, flags, val) )
+
+        sect = self.loadSection(0)
+        records, = struct.unpack('>H', sect[0x8:0x8+2])
+        mobi_length, = struct.unpack('>L',sect[0x14:0x18])
+        mobi_version, = struct.unpack('>L',sect[0x68:0x6C])
+        extra_data_flags = 0
+        print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length)
+        if (mobi_length >= 0xE4) and (mobi_version >= 5):
+            extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
+            print "Extra Data Flags = %d" %extra_data_flags
+
+        crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
+        if crypto_type == 0:
+            print "This book is not encrypted."
+        else:
+            if crypto_type == 1:
+                raise DrmException("cannot decode Mobipocket encryption type 1")
+            if crypto_type != 2:
+                raise DrmException("unknown encryption type: %d" % crypto_type)
+
+            # calculate the keys
+            drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
+            if drm_count == 0:
+                raise DrmException("no PIDs found in this file")
+            found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
+            if not found_key:
+                raise DrmException("no key found. maybe the PID is incorrect")
+
+            # kill the drm keys
+            self.patchSection(0, "\0" * drm_size, drm_ptr)
+            # kill the drm pointers
+            self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
+            # clear the crypto type
+            self.patchSection(0, "\0" * 2, 0xC)
+
+            # decrypt sections
+            print "Decrypting. Please wait . . .",
+            new_data = self.data_file[:self.sections[1][0]]
+            for i in xrange(1, records+1):
+                data = self.loadSection(i)
+                extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
+                if i%100 == 0:
+                    print ".",
+                # print "record %d, extra_size %d" %(i,extra_size)
+                new_data += PC1(found_key, data[0:len(data) - extra_size])
+                if extra_size > 0:
+                    new_data += data[-extra_size:]
+                #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
+            if self.num_sections > records+1:
+                new_data += self.data_file[self.sections[records+1][0]:]
+            self.data_file = new_data
+            print "done"
+
+    def getResult(self):
+        return self.data_file
+
+def getUnencryptedBook(infile,pid):
+    sys.stdout=Unbuffered(sys.stdout)
+    data_file = file(infile, 'rb').read()
+    strippedFile = DrmStripper(data_file, pid)
+    return strippedFile.getResult()
+
+def main(argv=sys.argv):
+    sys.stdout=Unbuffered(sys.stdout)
+    print ('MobiDeDrm v%(__version__)s. '
+          'Copyright 2008-2010 The Dark Reverser.' % globals())
+    if len(argv)<4:
+        print "Removes protection from Mobipocket books"
+        print "Usage:"
+        print "    %s <infile> <outfile> <PID>" % sys.argv[0]
+        return 1
+    else:
+        infile = argv[1]
+        outfile = argv[2]
+        pid = argv[3]
+        try:
+            stripped_file = getUnencryptedBook(infile, pid)
+            file(outfile, 'wb').write(stripped_file)
+        except DrmException, e:
+            print "Error: %s" % e
+            return 1
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
+
+#if not __name__ == "__main__":
+if False:
+
+    # note a calibre plugin can not import code with another calibre plugin
+    # in it as it ends up registering two different plugins 
+    from calibre.customize import FileTypePlugin
+
+    class MobiDeDRM(FileTypePlugin):
+        name                = 'MobiDeDRM' # Name of the plugin
+        description         = 'Removes DRM from secure Mobi files'
+        supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
+        author              = 'The Dark Reverser' # The author of this plugin
+        version             = (0, 1, 7)   # The version number of this plugin
+        file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
+        on_import           = True # Run this plugin during the import
+
+        def run(self, path_to_ebook):
+            from calibre.gui2 import is_ok_to_use_qt
+            from PyQt4.Qt import QMessageBox
+            PID = self.site_customization
+            data_file = file(path_to_ebook, 'rb').read()
+            ar = PID.split(',')
+            for i in ar:
+                try:
+                    unlocked_file = DrmStripper(data_file, i).getResult()
+                except DrmException:
+                    if is_ok_to_use_qt():
+                        d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook)
+                        d.show()
+                        d.raise_()
+                        d.exec_()
+                    raise Exception("MobiDeDRM Plugin: Error decoding ebook")
+                else:
+                    of = self.temporary_file('.mobi')
+                    of.write(unlocked_file)
+                    of.close()
+                    return of.name
+
+        def customization_help(self, gui=False):
+            return 'Enter PID (separate multiple PIDs with comma)'
diff --git a/eReader_PDB_Tools/lib/erdr2pml.py b/eReader_PDB_Tools/lib/erdr2pml.py
new file mode 100644 (file)
index 0000000..089d000
--- /dev/null
@@ -0,0 +1,692 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+#
+# erdr2pml.py
+#
+# This is a python script. You need a Python interpreter to run it.
+# For example, ActiveState Python, which exists for windows.
+# Changelog
+#
+#  Based on ereader2html version 0.08 plus some later small fixes
+#
+#  0.01 - Initial version
+#  0.02 - Support more eReader files. Support bold text and links. Fix PML decoder parsing bug.
+#  0.03 - Fix incorrect variable usage at one place.
+#  0.03b - enhancement by DeBockle (version 259 support)
+# Custom version 0.03 - no change to eReader support, only usability changes
+#   - start of pep-8 indentation (spaces not tab), fix trailing blanks
+#   - version variable, only one place to change
+#   - added main routine, now callable as a library/module, 
+#     means tools can add optional support for ereader2html
+#   - outdir is no longer a mandatory parameter (defaults based on input name if missing)
+#   - time taken output to stdout
+#   - Psyco support - reduces runtime by a factor of (over) 3!
+#     E.g. (~600Kb file) 90 secs down to 24 secs
+#       - newstyle classes
+#       - changed map call to list comprehension
+#         may not work with python 2.3
+#         without Psyco this reduces runtime to 90%
+#         E.g. 90 secs down to 77 secs
+#         Psyco with map calls takes longer, do not run with map in Psyco JIT!
+#       - izip calls used instead of zip (if available), further reduction
+#         in run time (factor of 4.5).
+#         E.g. (~600Kb file) 90 secs down to 20 secs
+#   - Python 2.6+ support, avoid DeprecationWarning with sha/sha1
+#  0.04 - Footnote support, PML output, correct charset in html, support more PML tags
+#   - Feature change, dump out PML file
+#   - Added supprt for footnote tags. NOTE footnote ids appear to be bad (not usable)
+#       in some pdb files :-( due to the same id being used multiple times
+#   - Added correct charset encoding (pml is based on cp1252)
+#   - Added logging support.
+#  0.05 - Improved type 272 support for sidebars, links, chapters, metainfo, etc
+#  0.06 - Merge of 0.04 and 0.05. Improved HTML output
+#         Placed images in subfolder, so that it's possible to just
+#         drop the book.pml file onto DropBook to make an unencrypted
+#         copy of the eReader file.
+#         Using that with Calibre works a lot better than the HTML
+#         conversion in this code.
+#  0.07 - Further Improved type 272 support for sidebars with all earlier fixes
+#  0.08 - fixed typos, removed extraneous things
+#  0.09 - fixed typos in first_pages to first_page to again support older formats
+#  0.10 - minor cleanups
+#  0.11 - fixups for using correct xml for footnotes and sidebars for use with Dropbook
+#  0.12 - Fix added to prevent lowercasing of image names when the pml code itself uses a different case in the link name.
+#  0.13 - change to unbuffered stdout for use with gui front ends
+#  0.14 - contributed enhancement to support --make-pmlz switch
+#  0.15 - enabled high-ascii to pml character encoding. DropBook now works on Mac.
+
+__version__='0.15'
+
+# Import Psyco if available
+try:
+    # Dumb speed hack 1
+    # http://psyco.sourceforge.net
+    import psyco
+    psyco.full()
+    pass
+except ImportError:
+    pass
+try:
+    # Dumb speed hack 2
+    # All map() calls converted to list comprehension (some use zip)
+    # override zip with izip - saves memory and in rough testing
+    # appears to be faster zip() is only used in the converted map() calls
+    from itertools import izip as zip
+except ImportError:
+    pass
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile
+
+try:
+    from hashlib import sha1
+except ImportError:
+    # older Python release
+    import sha
+    sha1 = lambda s: sha.new(s)
+import cgi
+import logging
+
+logging.basicConfig()
+#logging.basicConfig(level=logging.DEBUG)
+
+ECB =  0
+CBC =  1
+class Des(object):
+    __pc1 = [56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,
+          9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35,
+         62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
+         13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3]
+    __left_rotations = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
+    __pc2 = [13, 16, 10, 23,  0,  4,2, 27, 14,  5, 20,  9,
+        22, 18, 11,  3, 25,  7,        15,  6, 26, 19, 12,  1,
+        40, 51, 30, 36, 46, 54,        29, 39, 50, 44, 32, 47,
+        43, 48, 38, 55, 33, 52,        45, 41, 49, 35, 28, 31]
+    __ip = [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,
+        56, 48, 40, 32, 24, 16, 8,  0, 58, 50, 42, 34, 26, 18, 10, 2,
+        60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6]
+    __expansion_table = [31,  0,  1,  2,  3,  4, 3,  4,  5,  6,  7,  8,
+         7,  8,  9, 10, 11, 12,11, 12, 13, 14, 15, 16,
+        15, 16, 17, 18, 19, 20,19, 20, 21, 22, 23, 24,
+        23, 24, 25, 26, 27, 28,27, 28, 29, 30, 31,  0]
+    __sbox = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+         0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+         4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+         15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
+        [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+         3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+         0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+         13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
+        [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+         13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+         13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+         1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
+        [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+         13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+         10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+         3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
+        [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+         14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+         4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+         11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
+        [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+         10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+         9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+         4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
+        [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+         13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+         1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+         6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
+        [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+         1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+         7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+         2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],]
+    __p = [15, 6, 19, 20, 28, 11,27, 16, 0, 14, 22, 25,
+        4, 17, 30, 9, 1, 7,23,13, 31, 26, 2, 8,18, 12, 29, 5, 21, 10,3, 24]
+    __fp = [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,32,  0, 40,  8, 48, 16, 56, 24]
+    # Type of crypting being done
+    ENCRYPT =  0x00
+    DECRYPT =  0x01
+    def __init__(self, key, mode=ECB, IV=None):
+        if len(key) != 8:
+            raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
+        self.block_size = 8
+        self.key_size = 8
+        self.__padding = ''
+        self.setMode(mode)
+        if IV:
+            self.setIV(IV)
+        self.L = []
+        self.R = []
+        self.Kn = [ [0] * 48 ] * 16    # 16 48-bit keys (K1 - K16)
+        self.final = []
+        self.setKey(key)
+    def getKey(self):
+        return self.__key
+    def setKey(self, key):
+        self.__key = key
+        self.__create_sub_keys()
+    def getMode(self):
+        return self.__mode
+    def setMode(self, mode):
+        self.__mode = mode
+    def getIV(self):
+        return self.__iv
+    def setIV(self, IV):
+        if not IV or len(IV) != self.block_size:
+            raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
+        self.__iv = IV
+    def getPadding(self):
+        return self.__padding
+    def __String_to_BitList(self, data):
+        l = len(data) * 8
+        result = [0] * l
+        pos = 0
+        for c in data:
+            i = 7
+            ch = ord(c)
+            while i >= 0:
+                if ch & (1 << i) != 0:
+                    result[pos] = 1
+                else:
+                    result[pos] = 0
+                pos += 1
+                i -= 1
+        return result
+    def __BitList_to_String(self, data):
+        result = ''
+        pos = 0
+        c = 0
+        while pos < len(data):
+            c += data[pos] << (7 - (pos % 8))
+            if (pos % 8) == 7:
+                result += chr(c)
+                c = 0
+            pos += 1
+        return result
+    def __permutate(self, table, block):
+        return [block[x] for x in table]
+    def __create_sub_keys(self):
+        key = self.__permutate(Des.__pc1, self.__String_to_BitList(self.getKey()))
+        i = 0
+        self.L = key[:28]
+        self.R = key[28:]
+        while i < 16:
+            j = 0
+            while j < Des.__left_rotations[i]:
+                self.L.append(self.L[0])
+                del self.L[0]
+                self.R.append(self.R[0])
+                del self.R[0]
+                j += 1
+            self.Kn[i] = self.__permutate(Des.__pc2, self.L + self.R)
+            i += 1
+    def __des_crypt(self, block, crypt_type):
+        block = self.__permutate(Des.__ip, block)
+        self.L = block[:32]
+        self.R = block[32:]
+        if crypt_type == Des.ENCRYPT:
+            iteration = 0
+            iteration_adjustment = 1
+        else:
+            iteration = 15
+            iteration_adjustment = -1
+        i = 0
+        while i < 16:
+            tempR = self.R[:]
+            self.R = self.__permutate(Des.__expansion_table, self.R)
+            self.R = [x ^ y for x,y in zip(self.R, self.Kn[iteration])]
+            B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
+            j = 0
+            Bn = [0] * 32
+            pos = 0
+            while j < 8:
+                m = (B[j][0] << 1) + B[j][5]
+                n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
+                v = Des.__sbox[j][(m << 4) + n]
+                Bn[pos] = (v & 8) >> 3
+                Bn[pos + 1] = (v & 4) >> 2
+                Bn[pos + 2] = (v & 2) >> 1
+                Bn[pos + 3] = v & 1
+                pos += 4
+                j += 1
+            self.R = self.__permutate(Des.__p, Bn)
+            self.R = [x ^ y for x, y in zip(self.R, self.L)]
+            self.L = tempR
+            i += 1
+            iteration += iteration_adjustment
+        self.final = self.__permutate(Des.__fp, self.R + self.L)
+        return self.final
+    def crypt(self, data, crypt_type):
+        if not data:
+            return ''
+        if len(data) % self.block_size != 0:
+            if crypt_type == Des.DECRYPT: # Decryption must work on 8 byte blocks
+                raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
+            if not self.getPadding():
+                raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
+            else:
+                data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
+        if self.getMode() == CBC:
+            if self.getIV():
+                iv = self.__String_to_BitList(self.getIV())
+            else:
+                raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
+        i = 0
+        dict = {}
+        result = []
+        while i < len(data):
+            block = self.__String_to_BitList(data[i:i+8])
+            if self.getMode() == CBC:
+                if crypt_type == Des.ENCRYPT:
+                    block = [x ^ y for x, y in zip(block, iv)]
+                processed_block = self.__des_crypt(block, crypt_type)
+                if crypt_type == Des.DECRYPT:
+                    processed_block = [x ^ y for x, y in zip(processed_block, iv)]
+                    iv = block
+                else:
+                    iv = processed_block
+            else:
+                processed_block = self.__des_crypt(block, crypt_type)
+            result.append(self.__BitList_to_String(processed_block))
+            i += 8
+        if crypt_type == Des.DECRYPT and self.getPadding():
+            s = result[-1]
+            while s[-1] == self.getPadding():
+                s = s[:-1]
+            result[-1] = s
+        return ''.join(result)
+    def encrypt(self, data, pad=''):
+        self.__padding = pad
+        return self.crypt(data, Des.ENCRYPT)
+    def decrypt(self, data, pad=''):
+        self.__padding = pad
+        return self.crypt(data, Des.DECRYPT)
+
+class Sectionizer(object):
+    def __init__(self, filename, ident):
+        self.contents = file(filename, 'rb').read()
+        self.header = self.contents[0:72]
+        self.num_sections, = struct.unpack('>H', self.contents[76:78])
+        if self.header[0x3C:0x3C+8] != ident:
+            raise ValueError('Invalid file format')
+        self.sections = []
+        for i in xrange(self.num_sections):
+            offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.contents[78+i*8:78+i*8+8])
+            flags, val = a1, a2<<16|a3<<8|a4
+            self.sections.append( (offset, flags, val) )
+    def loadSection(self, section):
+        if section + 1 == self.num_sections:
+            end_off = len(self.contents)
+        else:
+            end_off = self.sections[section + 1][0]
+        off = self.sections[section][0]
+        return self.contents[off:end_off]
+
+def sanitizeFileName(s):
+    r = ''
+    for c in s:
+        if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-":
+            r += c
+    return r
+
+def fixKey(key):
+    def fixByte(b):
+        return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80)
+    return     "".join([chr(fixByte(ord(a))) for a in key])
+
+def deXOR(text, sp, table):
+    r=''
+    j = sp
+    for i in xrange(len(text)):
+        r += chr(ord(table[j]) ^ ord(text[i]))
+        j = j + 1
+        if j == len(table):
+            j = 0
+    return r
+
+class EreaderProcessor(object):
+    def __init__(self, section_reader, username, creditcard):
+        self.section_reader = section_reader
+        data = section_reader(0)
+        version,  = struct.unpack('>H', data[0:2])
+        self.version = version
+        logging.info('eReader file format version %s', version)
+        if version != 272 and version != 260 and version != 259:
+            raise ValueError('incorrect eReader version %d (error 1)' % version)
+        data = section_reader(1)
+        self.data = data
+        des = Des(fixKey(data[0:8]))
+        cookie_shuf, cookie_size = struct.unpack('>LL', des.decrypt(data[-8:]))
+        if cookie_shuf < 3 or cookie_shuf > 0x14 or cookie_size < 0xf0 or cookie_size > 0x200:
+            raise ValueError('incorrect eReader version (error 2)')
+        input = des.decrypt(data[-cookie_size:])
+        def unshuff(data, shuf):
+            r = [''] * len(data)
+            j = 0
+            for i in xrange(len(data)):
+                j = (j + shuf) % len(data)
+                r[j] = data[i]
+            assert     len("".join(r)) == len(data)
+            return "".join(r)
+        r = unshuff(input[0:-8], cookie_shuf)
+
+        def fixUsername(s):
+            r = ''
+            for c in s.lower():
+                if (c >= 'a' and c <= 'z' or c >= '0' and c <= '9'):
+                    r += c
+            return r
+
+        user_key = struct.pack('>LL', binascii.crc32(fixUsername(username)) & 0xffffffff, binascii.crc32(creditcard[-8:])& 0xffffffff)
+        drm_sub_version = struct.unpack('>H', r[0:2])[0]
+        self.num_text_pages = struct.unpack('>H', r[2:4])[0] - 1
+        self.num_image_pages = struct.unpack('>H', r[26:26+2])[0]
+        self.first_image_page = struct.unpack('>H', r[24:24+2])[0]
+        if self.version == 272:
+            self.num_footnote_pages = struct.unpack('>H', r[46:46+2])[0]
+            self.first_footnote_page = struct.unpack('>H', r[44:44+2])[0]
+            self.num_sidebar_pages = struct.unpack('>H', r[38:38+2])[0]
+            self.first_sidebar_page = struct.unpack('>H', r[36:36+2])[0]
+            # self.num_bookinfo_pages = struct.unpack('>H', r[34:34+2])[0]
+            # self.first_bookinfo_page = struct.unpack('>H', r[32:32+2])[0]
+            # self.num_chapter_pages = struct.unpack('>H', r[22:22+2])[0]
+            # self.first_chapter_page = struct.unpack('>H', r[20:20+2])[0]
+            # self.num_link_pages = struct.unpack('>H', r[30:30+2])[0]
+            # self.first_link_page = struct.unpack('>H', r[28:28+2])[0]
+            # self.num_xtextsize_pages = struct.unpack('>H', r[54:54+2])[0]
+            # self.first_xtextsize_page = struct.unpack('>H', r[52:52+2])[0]
+
+            # **before** data record 1 was decrypted and unshuffled, it contained data
+            # to create an XOR table and which is used to fix footnote record 0, link records, chapter records, etc
+            self.xortable_offset  = struct.unpack('>H', r[40:40+2])[0]
+            self.xortable_size = struct.unpack('>H', r[42:42+2])[0]
+            self.xortable = self.data[self.xortable_offset:self.xortable_offset + self.xortable_size]
+        else:
+            self.num_footnote_pages = 0
+            self.num_sidebar_pages = 0
+            self.first_footnote_page = -1
+            self.first_sidebar_page = -1
+            # self.num_bookinfo_pages = 0
+            # self.num_chapter_pages = 0
+            # self.num_link_pages = 0
+            # self.num_xtextsize_pages = 0
+            # self.first_bookinfo_page = -1
+            # self.first_chapter_page = -1
+            # self.first_link_page = -1
+            # self.first_xtextsize_page = -1
+
+        logging.debug('self.num_text_pages %d', self.num_text_pages)
+        logging.debug('self.num_footnote_pages %d, self.first_footnote_page %d', self.num_footnote_pages , self.first_footnote_page)
+        logging.debug('self.num_sidebar_pages %d, self.first_sidebar_page %d', self.num_sidebar_pages , self.first_sidebar_page)
+        self.flags = struct.unpack('>L', r[4:8])[0]
+        reqd_flags = (1<<9) | (1<<7) | (1<<10)
+        if (self.flags & reqd_flags) != reqd_flags:
+            print "Flags: 0x%X" % self.flags
+            raise ValueError('incompatible eReader file')
+        des = Des(fixKey(user_key))
+        if version == 259:
+            if drm_sub_version != 7:
+                raise ValueError('incorrect eReader version %d (error 3)' % drm_sub_version)
+            encrypted_key_sha = r[44:44+20]
+            encrypted_key = r[64:64+8]
+        elif version == 260:
+            if drm_sub_version != 13:
+                raise ValueError('incorrect eReader version %d (error 3)' % drm_sub_version)
+            encrypted_key = r[44:44+8]
+            encrypted_key_sha = r[52:52+20]
+        elif version == 272:
+            encrypted_key = r[172:172+8]
+            encrypted_key_sha = r[56:56+20]
+        self.content_key = des.decrypt(encrypted_key)
+        if sha1(self.content_key).digest() != encrypted_key_sha:
+            raise ValueError('Incorrect Name and/or Credit Card')
+
+    def getNumImages(self):
+        return self.num_image_pages
+
+    def getImage(self, i):
+        sect = self.section_reader(self.first_image_page + i)
+        name = sect[4:4+32].strip('\0')
+        data = sect[62:]
+        return sanitizeFileName(name), data
+
+
+    # def getChapterNamePMLOffsetData(self):
+    #     cv = ''
+    #     if self.num_chapter_pages > 0:
+    #         for i in xrange(self.num_chapter_pages):
+    #             chaps = self.section_reader(self.first_chapter_page + i)
+    #             j = i % self.xortable_size
+    #             offname = deXOR(chaps, j, self.xortable)
+    #             offset = struct.unpack('>L', offname[0:4])[0]
+    #             name = offname[4:].strip('\0')
+    #             cv += '%d|%s\n' % (offset, name) 
+    #     return cv
+
+    # def getLinkNamePMLOffsetData(self):
+    #     lv = ''
+    #     if self.num_link_pages > 0:
+    #         for i in xrange(self.num_link_pages):
+    #             links = self.section_reader(self.first_link_page + i)
+    #             j = i % self.xortable_size
+    #             offname = deXOR(links, j, self.xortable)
+    #             offset = struct.unpack('>L', offname[0:4])[0]
+    #             name = offname[4:].strip('\0')
+    #             lv += '%d|%s\n' % (offset, name) 
+    #     return lv
+
+    # def getExpandedTextSizesData(self):
+    #      ts = ''
+    #      if self.num_xtextsize_pages > 0:
+    #          tsize = deXOR(self.section_reader(self.first_xtextsize_page), 0, self.xortable)
+    #          for i in xrange(self.num_text_pages):
+    #              xsize = struct.unpack('>H', tsize[0:2])[0]
+    #              ts += "%d\n" % xsize
+    #              tsize = tsize[2:]
+    #      return ts
+
+    # def getBookInfo(self):
+    #     bkinfo = ''
+    #     if self.num_bookinfo_pages > 0:
+    #         info = self.section_reader(self.first_bookinfo_page)
+    #         bkinfo = deXOR(info, 0, self.xortable)
+    #         bkinfo = bkinfo.replace('\0','|')
+    #         bkinfo += '\n'
+    #     return bkinfo
+
+    def getText(self):
+        des = Des(fixKey(self.content_key))
+        r = ''
+        for i in xrange(self.num_text_pages):
+            logging.debug('get page %d', i)
+            r += zlib.decompress(des.decrypt(self.section_reader(1 + i)))
+             
+        # now handle footnotes pages
+        if self.num_footnote_pages > 0:
+            r += '\n'
+            # the record 0 of the footnote section must pass through the Xor Table to make it useful
+            sect = self.section_reader(self.first_footnote_page)
+            fnote_ids = deXOR(sect, 0, self.xortable)
+            # the remaining records of the footnote sections need to be decoded with the content_key and zlib inflated
+            des = Des(fixKey(self.content_key))
+            for i in xrange(1,self.num_footnote_pages):
+                logging.debug('get footnotepage %d', i)
+                id_len = ord(fnote_ids[2])
+                id = fnote_ids[3:3+id_len]
+                fmarker = '<footnote id="%s">\n' % id
+                fmarker += zlib.decompress(des.decrypt(self.section_reader(self.first_footnote_page + i)))
+                fmarker += '\n</footnote>\n'
+                r += fmarker
+                fnote_ids = fnote_ids[id_len+4:]
+
+        # now handle sidebar pages
+        if self.num_sidebar_pages > 0:
+            r += '\n'
+            # the record 0 of the sidebar section must pass through the Xor Table to make it useful
+            sect = self.section_reader(self.first_sidebar_page)
+            sbar_ids = deXOR(sect, 0, self.xortable)
+            # the remaining records of the sidebar sections need to be decoded with the content_key and zlib inflated
+            des = Des(fixKey(self.content_key))
+            for i in xrange(1,self.num_sidebar_pages):
+                id_len = ord(sbar_ids[2])
+                id = sbar_ids[3:3+id_len]
+                smarker = '<sidebar id="%s">\n' % id
+                smarker += zlib.decompress(des.decrypt(self.section_reader(self.first_footnote_page + i)))
+                smarker += '\n</sidebar>\n'
+                r += smarker
+                sbar_ids = sbar_ids[id_len+4:]
+
+        return r
+
+def cleanPML(pml):
+       # Convert special characters to proper PML code.  High ASCII start at (\x80, \a128) and go up to (\xff, \a255)
+       pml2 = pml
+       for k in xrange(128,256):
+               badChar = chr(k)
+               pml2 = pml2.replace(badChar, '\\a%03d' % k)
+       return pml2
+
+def convertEreaderToPml(infile, name, cc, outdir):
+    if not os.path.exists(outdir):
+        os.makedirs(outdir)
+
+    print "   Decoding File"
+    sect = Sectionizer(infile, 'PNRdPPrs')
+    er = EreaderProcessor(sect.loadSection, name, cc)
+
+    if er.getNumImages() > 0:
+        print "   Extracting images"
+        imagedir = bookname + '_img/'
+        imagedirpath = os.path.join(outdir,imagedir)
+        if not os.path.exists(imagedirpath):
+            os.makedirs(imagedirpath)
+        for i in xrange(er.getNumImages()):
+            name, contents = er.getImage(i)
+            file(os.path.join(imagedirpath, name), 'wb').write(contents)
+
+    print "   Extracting pml"
+    pml_string = er.getText()
+    pmlfilename = bookname + ".pml"
+    file(os.path.join(outdir, pmlfilename),'wb').write(cleanPML(pml_string))
+
+    # bkinfo = er.getBookInfo()
+    # if bkinfo != '':
+    #     print "   Extracting book meta information"
+    #     file(os.path.join(outdir, 'bookinfo.txt'),'wb').write(bkinfo)
+
+
+def usage():
+    print "Converts DRMed eReader books to PML Source"
+    print "Usage:"
+    print "  erdr2pml [options] infile.pdb [outdir] \"your name\" credit_card_number "
+    print " "
+    print "Options: "
+    print "  -h                prints this message"
+    print "  --make-pmlz       create PMLZ instead of using output directory"
+    print " "
+    print "Note:"
+    print "  if ommitted, outdir defaults based on 'infile.pdb'"
+    print "  It's enough to enter the last 8 digits of the credit card number"
+    return
+
+def main(argv=None):
+    global bookname
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "h", ["make-pmlz"])
+    except getopt.GetoptError, err:
+        print str(err)
+        usage()
+        return 1
+    make_pmlz = False
+    zipname = None
+    for o, a in opts:
+        if o == "-h":
+            usage()
+            return 0
+        elif o == "--make-pmlz":
+            make_pmlz = True
+            zipname = ''
+    
+    print "eRdr2Pml v%s. Copyright (c) 2009 The Dark Reverser" % __version__
+
+    if len(args)!=3 and len(args)!=4:
+        usage()
+        return 1
+    else:
+        if len(args)==3:
+            infile, name, cc = args[0], args[1], args[2]
+            outdir = infile[:-4] + '_Source'
+        elif len(args)==4:
+            infile, outdir, name, cc = args[0], args[1], args[2], args[3]
+
+        if make_pmlz :
+            # ignore specified outdir, use tempdir instead
+            outdir = tempfile.mkdtemp()
+                
+        bookname = os.path.splitext(os.path.basename(infile))[0]
+
+        try:
+            print "Processing..."
+            import time
+            start_time = time.time()
+            convertEreaderToPml(infile, name, cc, outdir)
+
+            if make_pmlz :
+                import zipfile
+                import shutil
+                print "   Creating PMLZ file"
+                zipname = infile[:-4] + '.pmlz'
+                myZipFile = zipfile.ZipFile(zipname,'w',zipfile.ZIP_STORED, False)
+                list = os.listdir(outdir)
+                for file in list:
+                    localname = file
+                    filePath = os.path.join(outdir,file)
+                    if os.path.isfile(filePath):
+                        myZipFile.write(filePath, localname)
+                    elif os.path.isdir(filePath):
+                        imageList = os.listdir(filePath)
+                        localimgdir = os.path.basename(filePath)
+                        for image in imageList:
+                            localname = os.path.join(localimgdir,image)
+                            imagePath = os.path.join(filePath,image)
+                            if os.path.isfile(imagePath):
+                                myZipFile.write(imagePath, localname)
+                myZipFile.close()
+                # remove temporary directory
+                shutil.rmtree(outdir)
+
+            end_time = time.time()
+            search_time = end_time - start_time
+            print 'elapsed time: %.2f seconds' % (search_time, ) 
+            if make_pmlz :
+                print 'output is %s' % zipname
+            else :
+                print 'output in %s' % outdir 
+            print "done"
+        except ValueError, e:
+            print "Error: %s" % e
+            return 1
+    return 0
+
+if __name__ == "__main__":
+    #import cProfile
+    #command = """sys.exit(main())"""
+    #cProfile.runctx( command, globals(), locals(), filename="cprofile.profile" )
+    
+    sys.exit(main())