diff --git a/Adobe_EPUB_Tools/ineptepub.pyw b/Adobe_EPUB_Tools/ineptepub.pyw
index 35e18af..d6c5f7d 100644
--- a/Adobe_EPUB_Tools/ineptepub.pyw
+++ b/Adobe_EPUB_Tools/ineptepub.pyw
@@ -1,18 +1,29 @@
#! /usr/bin/python
+# -*- coding: utf-8 -*-
-# ineptepub.pyw, version 4.1
+# ineptepub.pyw, version 5.2
+# Copyright © 2009-2010 i♥cabbages
-# To run this program install Python 2.6 from http://www.python.org/download/
-# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-# (make sure to install the version for Python 2.6). Save this script file as
-# ineptepub.pyw and double-click on it to run it.
+# Released under the terms of the GNU General Public Licence, version 3 or
+# later.
+
+# Windows users: Before running this program, you must first install Python 2.6
+# from and PyCrypto from
+# (make sure to
+# install the version for Python 2.6). Save this script file as
+# ineptepub.pyw and double-click on it to run it.
+#
+# Mac OS X users: Save this script file as ineptepub.pyw. You can run this
+# program from the command line (pythonw ineptepub.pyw) or by double-clicking
+# it when it has been associated with PythonLauncher.
# Revision history:
# 1 - Initial release
# 2 - Rename to INEPT, fix exit code
-# 3 - Add cmd or gui choosing
-# 4 - support for 1.7.2 support (anon)
-# 4.1 - backward compatibility for 1.7.1 and old adeptkey.der
+# 5 - Version bump to avoid (?) confusion;
+# Improve OS X support by using OpenSSL when available
+# 5.1 - Improve OpenSSL error checking
+# 5.2 - Fix ctypes error causing segfaults on some systems
"""
Decrypt Adobe ADEPT-encrypted EPUB books.
@@ -34,117 +45,224 @@ import Tkconstants
import tkFileDialog
import tkMessageBox
-try:
- from Crypto.Cipher import AES
- from Crypto.PublicKey import RSA
-except ImportError:
- AES = None
- RSA = None
+class ADEPTError(Exception):
+ pass
+
+def _load_crypto_libcrypto():
+ from ctypes import CDLL, POINTER, c_void_p, c_char_p, c_int, c_long, \
+ Structure, c_ulong, create_string_buffer, cast
+ from ctypes.util import find_library
+
+ libcrypto = find_library('crypto')
+ if libcrypto is None:
+ raise ADEPTError('libcrypto not found')
+ libcrypto = CDLL(libcrypto)
+
+ RSA_NO_PADDING = 3
+ AES_MAXNR = 14
+
+ c_char_pp = POINTER(c_char_p)
+ c_int_p = POINTER(c_int)
+
+ class RSA(Structure):
+ pass
+ RSA_p = POINTER(RSA)
+
+ 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
+
+ d2i_RSAPrivateKey = F(RSA_p, 'd2i_RSAPrivateKey',
+ [RSA_p, c_char_pp, c_long])
+ RSA_size = F(c_int, 'RSA_size', [RSA_p])
+ RSA_private_decrypt = F(c_int, 'RSA_private_decrypt',
+ [c_int, c_char_p, c_char_p, RSA_p, c_int])
+ RSA_free = F(None, 'RSA_free', [RSA_p])
+ AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
+ [c_char_p, c_int, AES_KEY_p])
+ AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
+ [c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
+ c_int])
+
+ class RSA(object):
+ def __init__(self, der):
+ buf = create_string_buffer(der)
+ pp = c_char_pp(cast(buf, c_char_p))
+ rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der))
+ if rsa is None:
+ raise ADEPTError('Error parsing ADEPT user key DER')
+
+ def decrypt(self, from_):
+ rsa = self._rsa
+ to = create_string_buffer(RSA_size(rsa))
+ dlen = RSA_private_decrypt(len(from_), from_, to, rsa,
+ RSA_NO_PADDING)
+ if dlen < 0:
+ raise ADEPTError('RSA decryption failed')
+ return to[:dlen]
+
+ def __del__(self):
+ if self._rsa is not None:
+ RSA_free(self._rsa)
+ self._rsa = None
+
+ class AES(object):
+ def __init__(self, userkey):
+ self._blocksize = len(userkey)
+ key = self._key = AES_KEY()
+ rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
+ if rv < 0:
+ raise ADEPTError('Failed to initialize AES key')
+
+ def decrypt(self, data):
+ out = create_string_buffer(len(data))
+ iv = ("\x00" * self._blocksize)
+ rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
+ if rv == 0:
+ raise ADEPTError('AES decryption failed')
+ return out.raw
+
+ return (AES, RSA)
+
+def _load_crypto_pycrypto():
+ from Crypto.Cipher import AES as _AES
+ from Crypto.PublicKey import RSA as _RSA
+
+ # ASN.1 parsing code from tlslite
+ class ASN1Error(Exception):
+ pass
+
+ class ASN1Parser(object):
+ class Parser(object):
+ def __init__(self, bytes):
+ self.bytes = bytes
+ self.index = 0
+
+ def get(self, length):
+ if self.index + length > len(self.bytes):
+ raise ASN1Error("Error decoding ASN.1")
+ x = 0
+ for count in range(length):
+ x <<= 8
+ x |= self.bytes[self.index]
+ self.index += 1
+ return x
+
+ def getFixBytes(self, lengthBytes):
+ bytes = self.bytes[self.index : self.index+lengthBytes]
+ self.index += lengthBytes
+ return bytes
+
+ def getVarBytes(self, lengthLength):
+ lengthBytes = self.get(lengthLength)
+ return self.getFixBytes(lengthBytes)
+
+ def getFixList(self, length, lengthList):
+ l = [0] * lengthList
+ for x in range(lengthList):
+ l[x] = self.get(length)
+ return l
+
+ def getVarList(self, length, lengthLength):
+ lengthList = self.get(lengthLength)
+ if lengthList % length != 0:
+ raise ASN1Error("Error decoding ASN.1")
+ lengthList = int(lengthList/length)
+ l = [0] * lengthList
+ for x in range(lengthList):
+ l[x] = self.get(length)
+ return l
+
+ def startLengthCheck(self, lengthLength):
+ self.lengthCheck = self.get(lengthLength)
+ self.indexCheck = self.index
+
+ def setLengthCheck(self, length):
+ self.lengthCheck = length
+ self.indexCheck = self.index
+
+ def stopLengthCheck(self):
+ if (self.index - self.indexCheck) != self.lengthCheck:
+ raise ASN1Error("Error decoding ASN.1")
+
+ def atLengthCheck(self):
+ if (self.index - self.indexCheck) < self.lengthCheck:
+ return False
+ elif (self.index - self.indexCheck) == self.lengthCheck:
+ return True
+ else:
+ raise ASN1Error("Error decoding ASN.1")
+
+ def __init__(self, bytes):
+ p = self.Parser(bytes)
+ p.get(1)
+ self.length = self._getASN1Length(p)
+ self.value = p.getFixBytes(self.length)
+
+ def getChild(self, which):
+ p = self.Parser(self.value)
+ for x in range(which+1):
+ markIndex = p.index
+ p.get(1)
+ length = self._getASN1Length(p)
+ p.getFixBytes(length)
+ return ASN1Parser(p.bytes[markIndex:p.index])
+
+ def _getASN1Length(self, p):
+ firstLength = p.get(1)
+ if firstLength<=127:
+ return firstLength
+ else:
+ lengthLength = firstLength & 0x7F
+ return p.get(lengthLength)
+
+ class AES(object):
+ def __init__(self, key):
+ self._aes = _AES.new(key, _AES.MODE_CBC)
+
+ def decrypt(self, data):
+ return self._aes.decrypt(data)
+
+ class RSA(object):
+ def __init__(self, der):
+ key = ASN1Parser([ord(x) for x in der])
+ key = [key.getChild(x).value for x in xrange(1, 4)]
+ key = [self.bytesToNumber(v) for v in key]
+ self._rsa = _RSA.construct(key)
+
+ def bytesToNumber(self, bytes):
+ total = 0L
+ for byte in bytes:
+ total = (total << 8) + byte
+ return total
+
+ def decrypt(self, data):
+ return self._rsa.decrypt(data)
+
+ return (AES, RSA)
+
+def _load_crypto():
+ AES = RSA = None
+ for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto):
+ try:
+ AES, RSA = loader()
+ break
+ except (ImportError, ADEPTError):
+ pass
+ return (AES, RSA)
+AES, RSA = _load_crypto()
META_NAMES = ('mimetype', 'META-INF/rights.xml', 'META-INF/encryption.xml')
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
-
-# ASN.1 parsing code from tlslite
-
-def bytesToNumber(bytes):
- total = 0L
- multiplier = 1L
- for count in range(len(bytes)-1, -1, -1):
- byte = bytes[count]
- total += multiplier * byte
- multiplier *= 256
- return total
-
-class ASN1Error(Exception):
- pass
-
-class ASN1Parser(object):
- class Parser(object):
- def __init__(self, bytes):
- self.bytes = bytes
- self.index = 0
-
- def get(self, length):
- if self.index + length > len(self.bytes):
- raise ASN1Error("Error decoding ASN.1")
- x = 0
- for count in range(length):
- x <<= 8
- x |= self.bytes[self.index]
- self.index += 1
- return x
-
- def getFixBytes(self, lengthBytes):
- bytes = self.bytes[self.index : self.index+lengthBytes]
- self.index += lengthBytes
- return bytes
-
- def getVarBytes(self, lengthLength):
- lengthBytes = self.get(lengthLength)
- return self.getFixBytes(lengthBytes)
-
- def getFixList(self, length, lengthList):
- l = [0] * lengthList
- for x in range(lengthList):
- l[x] = self.get(length)
- return l
-
- def getVarList(self, length, lengthLength):
- lengthList = self.get(lengthLength)
- if lengthList % length != 0:
- raise ASN1Error("Error decoding ASN.1")
- lengthList = int(lengthList/length)
- l = [0] * lengthList
- for x in range(lengthList):
- l[x] = self.get(length)
- return l
-
- def startLengthCheck(self, lengthLength):
- self.lengthCheck = self.get(lengthLength)
- self.indexCheck = self.index
-
- def setLengthCheck(self, length):
- self.lengthCheck = length
- self.indexCheck = self.index
-
- def stopLengthCheck(self):
- if (self.index - self.indexCheck) != self.lengthCheck:
- raise ASN1Error("Error decoding ASN.1")
-
- def atLengthCheck(self):
- if (self.index - self.indexCheck) < self.lengthCheck:
- return False
- elif (self.index - self.indexCheck) == self.lengthCheck:
- return True
- else:
- raise ASN1Error("Error decoding ASN.1")
-
- def __init__(self, bytes):
- p = self.Parser(bytes)
- p.get(1)
- self.length = self._getASN1Length(p)
- self.value = p.getFixBytes(self.length)
-
- def getChild(self, which):
- p = self.Parser(self.value)
- for x in range(which+1):
- markIndex = p.index
- p.get(1)
- length = self._getASN1Length(p)
- p.getFixBytes(length)
- return ASN1Parser(p.bytes[markIndex:p.index])
-
- def _getASN1Length(self, p):
- firstLength = p.get(1)
- if firstLength<=127:
- return firstLength
- else:
- lengthLength = firstLength & 0x7F
- return p.get(lengthLength)
-
-
class ZipInfo(zipfile.ZipInfo):
def __init__(self, *args, **kwargs):
if 'compress_type' in kwargs:
@@ -152,11 +270,10 @@ class ZipInfo(zipfile.ZipInfo):
super(ZipInfo, self).__init__(*args, **kwargs)
self.compress_type = compress_type
-
class Decryptor(object):
def __init__(self, bookkey, encryption):
enc = lambda tag: '{%s}%s' % (NSMAP['enc'], tag)
- self._aes = AES.new(bookkey, AES.MODE_CBC)
+ self._aes = AES(bookkey)
encryption = etree.fromstring(encryption)
self._encrypted = encrypted = set()
expr = './%s/%s/%s' % (enc('EncryptedData'), enc('CipherData'),
@@ -165,7 +282,7 @@ class Decryptor(object):
path = elem.get('URI', None)
if path is not None:
encrypted.add(path)
-
+
def decompress(self, bytes):
dc = zlib.decompressobj(-15)
bytes = dc.decompress(bytes)
@@ -173,7 +290,7 @@ class Decryptor(object):
if ex:
bytes = bytes + ex
return bytes
-
+
def decrypt(self, path, data):
if path in self._encrypted:
data = self._aes.decrypt(data)[16:]
@@ -181,16 +298,12 @@ class Decryptor(object):
data = self.decompress(data)
return data
-
-class ADEPTError(Exception):
- pass
-
def cli_main(argv=sys.argv):
progname = os.path.basename(argv[0])
if AES is None:
- print "%s: This script requires PyCrypto, which must be installed " \
- "separately. Read the top-of-script comment for details." % \
- (progname,)
+ print "%s: This script requires OpenSSL or PyCrypto, which must be" \
+ " installed separately. Read the top-of-script comment for" \
+ " details." % (progname,)
return 1
if len(argv) != 4:
print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
@@ -198,9 +311,7 @@ def cli_main(argv=sys.argv):
keypath, inpath, outpath = argv[1:]
with open(keypath, 'rb') as f:
keyder = f.read()
- key = ASN1Parser([ord(x) for x in keyder])
- key = [bytesToNumber(key.getChild(x).value) for x in xrange(1, 4)]
- rsa = RSA.construct(key)
+ rsa = RSA(keyder)
with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = set(inf.namelist())
if 'META-INF/rights.xml' not in namelist or \
@@ -227,7 +338,6 @@ def cli_main(argv=sys.argv):
outf.writestr(path, decryptor.decrypt(path, data))
return 0
-
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
@@ -328,8 +438,9 @@ def gui_main():
root.withdraw()
tkMessageBox.showerror(
"INEPT EPUB Decrypter",
- "This script requires PyCrypto, which must be installed "
- "separately. Read the top-of-script comment for details.")
+ "This script requires OpenSSL or PyCrypto, which must be"
+ " installed separately. Read the top-of-script comment for"
+ " details.")
return 1
root.title('INEPT EPUB Decrypter')
root.resizable(True, False)
diff --git a/Adobe_EPUB_Tools/ineptkey.pyw b/Adobe_EPUB_Tools/ineptkey.pyw
index 4a5d868..3756ae3 100644
--- a/Adobe_EPUB_Tools/ineptkey.pyw
+++ b/Adobe_EPUB_Tools/ineptkey.pyw
@@ -1,25 +1,38 @@
#! /usr/bin/python
+# -*- coding: utf-8 -*-
-# ineptkey.pyw, version 4.4
-# ineptkeyv44
+# ineptkey.pyw, version 5
+# Copyright © 2009-2010 i♥cabbages
-# To run this program install Python 2.6 from http://www.python.org/download/
-# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-# (make sure to install the version for Python 2.6). Save this script file as
-# ineptkey.pyw and double-click on it to run it. It will create a file named
-# adeptkey.der in the same directory. These are your ADEPT user keys.
+# Released under the terms of the GNU General Public Licence, version 3 or
+# later.
+
+# Windows users: Before running this program, you must first install Python 2.6
+# from and PyCrypto from
+# (make certain
+# to install the version for Python 2.6). Then save this script file as
+# ineptkey.pyw and double-click on it to run it. It will create a file named
+# adeptkey.der in the same directory. This is your ADEPT user key.
+#
+# Mac OS X users: Save this script file as ineptkey.pyw. You can run this
+# program from the command line (pythonw ineptkey.pyw) or by double-clicking
+# it when it has been associated with PythonLauncher. It will create a file
+# named adeptkey.der in the same directory. This is your ADEPT user key.
# Revision history:
# 1 - Initial release, for Adobe Digital Editions 1.7
# 2 - Better algorithm for finding pLK; improved error handling
# 3 - Rename to INEPT
+# 4 - Series of changes by joblack (and others?) --
# 4.1 - quick beta fix for ADE 1.7.2 (anon)
# 4.2 - added old 1.7.1 processing
# 4.3 - better key search
# 4.4 - Make it working on 64-bit Python
+# 5 - Clean up and improve 4.x changes;
+# Clean up and merge OS X support by unknown
"""
-Retrieve Adobe ADEPT user key under Windows.
+Retrieve Adobe ADEPT user key.
"""
from __future__ import with_statement
@@ -28,246 +41,305 @@ __license__ = 'GPL v3'
import sys
import os
-from struct import pack
-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, c_ulonglong, \
- sizeof, c_void_p, c_size_t
-
-import _winreg as winreg
+import struct
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
-import hashlib
-import pickle
-
-
-try:
- from Crypto.Cipher import AES
-except ImportError:
- AES = None
-
-
-DEVICE_KEY = 'Software\\Adobe\\Adept\\Device'
-PRIVATE_LICENCE_KEY = 'Software\\Adobe\\Adept\\Activation\\%04d'
-PRIVATE_LICENCE_KEY_KEY = 'Software\\Adobe\\Adept\\Activation\\%04d\\%04d'
-ACTIVATION = 'Software\\Adobe\\Adept\\Activation\\'
-
-MAX_PATH = 255
-
-kernel32 = windll.kernel32
-advapi32 = windll.advapi32
-crypt32 = windll.crypt32
-
class ADEPTError(Exception):
pass
+if sys.platform.startswith('win'):
+ 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, c_size_t, memmove
+ from ctypes.wintypes import LPVOID, DWORD, BOOL
+ import _winreg as winreg
+
+ try:
+ from Crypto.Cipher import AES
+ except ImportError:
+ AES = None
+
+ DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
+ PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
+
+ MAX_PATH = 255
+
+ kernel32 = windll.kernel32
+ advapi32 = windll.advapi32
+ crypt32 = windll.crypt32
-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()
+ 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 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()
+ 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()
-if sizeof(c_void_p) == 4:
- ## 32-bit Python
- CPUID0_INSNS = create_string_buffer("\x53\x31\xc0\x0f\xa2\x8b\x44\x24\x08\x89"
- "\x18\x89\x50\x04\x89\x48\x08\x5b\xc3")
- def cpuid0():
- buffer = create_string_buffer(12)
- cpuid0__ = CFUNCTYPE(c_char_p)(addressof(CPUID0_INSNS))
- def cpuid0():
- cpuid0__(buffer)
- return buffer.raw
- return cpuid0
- cpuid0 = cpuid0()
-
- CPUID1_INSNS = create_string_buffer("\x53\x31\xc0\x40\x0f\xa2\x5b\xc3")
- cpuid1 = CFUNCTYPE(c_uint)(addressof(CPUID1_INSNS))
-else:
- ## 64 bit Python
-
- # In 64-bit we cannot execute instructions stored in a string because
- # the O.S. prevents that to defend against buffer overrun attacks.
- # Therefore we have to allocate a block of memory with the execute
- # permission and copy our code into it.
-
- NULL = c_void_p(0)
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
- VirtualAlloc = windll.kernel32.VirtualAlloc
- VirtualAlloc.restype = c_void_p
- VirtualAlloc.argtypes = (c_void_p, c_size_t, c_uint, c_uint)
+ def VirtualAlloc():
+ _VirtualAlloc = kernel32.VirtualAlloc
+ _VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
+ _VirtualAlloc.restype = LPVOID
+ def VirtualAlloc(addr, size, alloctype=(MEM_COMMIT | MEM_RESERVE),
+ protect=PAGE_EXECUTE_READWRITE):
+ return _VirtualAlloc(addr, size, alloctype, protect)
+ return VirtualAlloc
+ VirtualAlloc = VirtualAlloc()
- from ctypes import memmove
- memmove.restype = c_void_p
- memmove.argtypes = (c_void_p, c_void_p, c_size_t)
+ MEM_RELEASE = 0x8000
- CPUID0_INSNS = (b"\x55" # push %rbp
- "\x48\x89\xe5" # mov %rsp,%rbp
- "\x48\x89\x4d\xf8" # mov %rcx,-0x8(%rbp)
- "\x31\xc0" # xor %eax,%eax
- "\x0f\xa2" # cpuid
- "\x48\x8b\x45\xf8" # mov -0x8(%rbp),%rax
- "\x89\x18" # mov %ebx,(%rax)
- "\x89\x50\x04" # mov %edx,0x4(%rax)
- "\x89\x48\x08" # mov %ecx,0x8(%rax)
- "\x48\x8b\x45\xf8" # mov -0x8(%rbp),%rax
- "\xc9" # leave
- "\xc3" # ret
- )
+ def VirtualFree():
+ _VirtualFree = kernel32.VirtualFree
+ _VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
+ _VirtualFree.restype = BOOL
+ def VirtualFree(addr, size=0, freetype=MEM_RELEASE):
+ return _VirtualFree(addr, size, freetype)
+ return VirtualFree
+ VirtualFree = VirtualFree()
- CPUID1_INSNS = (b"\x31\xc0" # xor %eax,%eax
- "\xff\xc0" # inc %eax
- "\x0f\xa2" # cpuid
- "\xc3" # ret
- )
+ class NativeFunction(object):
+ def __init__(self, restype, argtypes, insns):
+ self._buf = buf = VirtualAlloc(None, len(insns))
+ memmove(buf, insns, len(insns))
+ ftype = CFUNCTYPE(restype, *argtypes)
+ self._native = ftype(buf)
- insnlen0 = len(CPUID0_INSNS)
- insnlen1 = len(CPUID1_INSNS)
- insnlen = insnlen0 + insnlen1
+ def __call__(self, *args):
+ return self._native(*args)
- code_addr = (VirtualAlloc(NULL, insnlen,
- MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))
+ def __del__(self):
+ if self._buf is not None:
+ VirtualFree(self._buf)
+ self._buf = None
- if code_addr is None:
- raise ADEPTError("Failed to allocate memory")
-
- memmove(code_addr, CPUID0_INSNS + CPUID1_INSNS, insnlen)
+ if struct.calcsize("P") == 4:
+ CPUID0_INSNS = (
+ "\x53" # push %ebx
+ "\x31\xc0" # xor %eax,%eax
+ "\x0f\xa2" # cpuid
+ "\x8b\x44\x24\x08" # mov 0x8(%esp),%eax
+ "\x89\x18" # mov %ebx,0x0(%eax)
+ "\x89\x50\x04" # mov %edx,0x4(%eax)
+ "\x89\x48\x08" # mov %ecx,0x8(%eax)
+ "\x5b" # pop %ebx
+ "\xc3" # ret
+ )
+ CPUID1_INSNS = (
+ "\x53" # push %ebx
+ "\x31\xc0" # xor %eax,%eax
+ "\x40" # inc %eax
+ "\x0f\xa2" # cpuid
+ "\x5b" # pop %ebx
+ "\xc3" # ret
+ )
+ else:
+ CPUID0_INSNS = (
+ "\x49\x89\xd8" # mov %rbx,%r8
+ "\x49\x89\xc9" # mov %rcx,%r9
+ "\x48\x31\xc0" # xor %rax,%rax
+ "\x0f\xa2" # cpuid
+ "\x4c\x89\xc8" # mov %r9,%rax
+ "\x89\x18" # mov %ebx,0x0(%rax)
+ "\x89\x50\x04" # mov %edx,0x4(%rax)
+ "\x89\x48\x08" # mov %ecx,0x8(%rax)
+ "\x4c\x89\xc3" # mov %r8,%rbx
+ "\xc3" # retq
+ )
+ CPUID1_INSNS = (
+ "\x53" # push %rbx
+ "\x48\x31\xc0" # xor %rax,%rax
+ "\x48\xff\xc0" # inc %rax
+ "\x0f\xa2" # cpuid
+ "\x5b" # pop %rbx
+ "\xc3" # retq
+ )
def cpuid0():
- buffer = create_string_buffer(12)
- cpuid0__ = CFUNCTYPE(c_ulonglong, c_char_p)(code_addr)
+ _cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
+ buf = create_string_buffer(12)
def cpuid0():
- cpuid0__(buffer)
- return buffer.raw
+ _cpuid0(buf)
+ return buf.raw
return cpuid0
cpuid0 = cpuid0()
- cpuid1 = CFUNCTYPE(c_ulonglong)(code_addr + insnlen0)
+ cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
-class DataBlob(Structure):
- _fields_ = [('cbData', c_uint),
- ('pbData', c_void_p)]
-DataBlob_p = POINTER(DataBlob)
+ class DataBlob(Structure):
+ _fields_ = [('cbData', c_uint),
+ ('pbData', c_void_p)]
+ DataBlob_p = POINTER(DataBlob)
-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 ADEPTError("Failed to decrypt user key key (sic)")
- return string_at(outdata.pbData, outdata.cbData)
- return CryptUnprotectData
-CryptUnprotectData = CryptUnprotectData()
+ 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 ADEPTError("Failed to decrypt user key key (sic)")
+ return string_at(outdata.pbData, outdata.cbData)
+ return CryptUnprotectData
+ CryptUnprotectData = CryptUnprotectData()
-
-def retrieve_key(keypath):
- root = GetSystemDirectory().split('\\')[0] + '\\'
- serial = GetVolumeSerialNumber(root)
- vendor = cpuid0()
- signature = pack('>I', cpuid1())[1:]
- user = GetUserName()
- entropy = pack('>I12s3s13s', serial, vendor, signature, user)
- cuser = winreg.HKEY_CURRENT_USER
- try:
- regkey = winreg.OpenKey(cuser, DEVICE_KEY)
- except WindowsError:
- raise ADEPTError("Adobe Digital Editions not activated")
- device = winreg.QueryValueEx(regkey, 'key')[0]
- keykey = CryptUnprotectData(device, entropy)
- userkey = None
- pkcs = None
- keys = {}
- counter = 0
- for i in xrange(0, 16):
- skey = PRIVATE_LICENCE_KEY % i
+ def retrieve_key(keypath):
+ if AES is None:
+ tkMessageBox.showerror(
+ "ADEPT Key",
+ "This script requires PyCrypto, which must be installed "
+ "separately. Read the top-of-script comment for details.")
+ return False
+ root = GetSystemDirectory().split('\\')[0] + '\\'
+ serial = GetVolumeSerialNumber(root)
+ vendor = cpuid0()
+ signature = struct.pack('>I', cpuid1())[1:]
+ user = GetUserName()
+ entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
+ cuser = winreg.HKEY_CURRENT_USER
try:
- regkey = winreg.OpenKey(cuser, skey)
+ regkey = winreg.OpenKey(cuser, DEVICE_KEY_PATH)
except WindowsError:
- break
- type = winreg.QueryValueEx(regkey, None)[0]
- # obfuscation technique
- if type != 'credentials':
- continue
- for j in xrange(0, 16):
- plkkey = PRIVATE_LICENCE_KEY_KEY % (i, j)
+ raise ADEPTError("Adobe Digital Editions not activated")
+ device = winreg.QueryValueEx(regkey, 'key')[0]
+ keykey = CryptUnprotectData(device, entropy)
+ userkey = None
+ try:
+ plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
+ except WindowsError:
+ raise ADEPTError("Could not locate ADE activation")
+ for i in xrange(0, 16):
try:
- regkey = winreg.OpenKey(cuser, plkkey)
+ plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
except WindowsError:
break
- type = winreg.QueryValueEx(regkey, None)[0]
- if type != 'privateLicenseKey':
+ ktype = winreg.QueryValueEx(plkparent, None)[0]
+ if ktype != 'credentials':
continue
- userkey = winreg.QueryValueEx(regkey, 'value')[0]
- break
- for j in xrange(0, 16):
- plkkey = PRIVATE_LICENCE_KEY_KEY % (i, j)
- try:
- pkcs = winreg.OpenKey(cuser, plkkey)
- except WindowsError:
+ for j in xrange(0, 16):
+ try:
+ plkkey = winreg.OpenKey(plkparent, "%04d" % (j,))
+ except WindowsError:
+ break
+ ktype = winreg.QueryValueEx(plkkey, None)[0]
+ if ktype != 'privateLicenseKey':
+ continue
+ userkey = winreg.QueryValueEx(plkkey, 'value')[0]
break
- type = winreg.QueryValueEx(pkcs, None)[0]
- if type != 'pkcs12':
+ if userkey is not None:
+ break
+ if userkey is None:
+ raise ADEPTError('Could not locate privateLicenseKey')
+ userkey = userkey.decode('base64')
+ userkey = AES.new(keykey, AES.MODE_CBC).decrypt(userkey)
+ userkey = userkey[26:-ord(userkey[-1])]
+ with open(keypath, 'wb') as f:
+ f.write(userkey)
+ return True
+
+elif sys.platform.startswith('darwin'):
+ import xml.etree.ElementTree as etree
+ import Carbon.File
+ import Carbon.Folder
+ import Carbon.Folders
+ import MacOS
+
+ ACTIVATION_PATH = 'Adobe/Digital Editions/activation.dat'
+ NSMAP = {'adept': 'http://ns.adobe.com/adept',
+ 'enc': 'http://www.w3.org/2001/04/xmlenc#'}
+
+ def find_folder(domain, dtype):
+ try:
+ fsref = Carbon.Folder.FSFindFolder(domain, dtype, False)
+ return Carbon.File.pathname(fsref)
+ except MacOS.Error:
+ return None
+
+ def find_app_support_file(subpath):
+ dtype = Carbon.Folders.kApplicationSupportFolderType
+ for domain in Carbon.Folders.kUserDomain, Carbon.Folders.kLocalDomain:
+ path = find_folder(domain, dtype)
+ if path is None:
continue
- pkcs = winreg.QueryValueEx(pkcs, 'value')[0]
- break
- if pkcs is None:
- raise ADEPTError('Could not locate PKCS specification')
- if userkey is None:
- raise ADEPTError('Could not locate privateLicenseKey')
- userkey = userkey.decode('base64')
- userkey = AES.new(keykey, AES.MODE_CBC).decrypt(userkey)
- userkey = userkey[26:-ord(userkey[-1])]
- with open(keypath, 'wb') as f:
- f.write(userkey)
- return
+ path = os.path.join(path, subpath)
+ if os.path.isfile(path):
+ return path
+ return None
+
+ def retrieve_key(keypath):
+ actpath = find_app_support_file(ACTIVATION_PATH)
+ if actpath is None:
+ raise ADEPTError("Could not locate ADE activation")
+ tree = etree.parse(actpath)
+ adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
+ expr = '//%s/%s' % (adept('credentials'), adept('privateLicenseKey'))
+ userkey = tree.findtext(expr)
+ userkey = userkey.decode('base64')
+ userkey = userkey[26:]
+ with open(keypath, 'wb') as f:
+ f.write(userkey)
+ return True
+
+elif sys.platform.startswith('cygwin'):
+ def retrieve_key(keypath):
+ tkMessageBox.showerror(
+ "ADEPT Key",
+ "This script requires a Windows-native Python, and cannot be run "
+ "under Cygwin. Please install a Windows-native Python and/or "
+ "check your file associations.")
+ return False
+
+else:
+ def retrieve_key(keypath):
+ tkMessageBox.showerror(
+ "ADEPT Key",
+ "This script only supports Windows and Mac OS X. For Linux "
+ "you should be able to run ADE and this script under Wine (with "
+ "an appropriate version of Windows Python installed).")
+ return False
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
@@ -279,33 +351,27 @@ class ExceptionDialog(Tkinter.Frame):
self.text.pack(fill=Tkconstants.BOTH, expand=1)
self.text.insert(Tkconstants.END, text)
-
def main(argv=sys.argv):
root = Tkinter.Tk()
root.withdraw()
progname = os.path.basename(argv[0])
- if AES is None:
- tkMessageBox.showerror(
- "ADEPT Key",
- "This script requires PyCrypto, which must be installed "
- "separately. Read the top-of-script comment for details.")
- return 1
keypath = 'adeptkey.der'
+ success = False
try:
- retrieve_key(keypath)
+ success = retrieve_key(keypath)
except ADEPTError, e:
tkMessageBox.showerror("ADEPT Key", "Error: " + str(e))
- return 1
except Exception:
root.wm_state('normal')
root.title('ADEPT Key')
text = traceback.format_exc()
ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
root.mainloop()
+ if not success:
return 1
tkMessageBox.showinfo(
"ADEPT Key", "Key successfully retrieved to %s" % (keypath))
return 0
if __name__ == '__main__':
- sys.exit(main())
\ No newline at end of file
+ sys.exit(main())
diff --git a/Adobe_EPUB_Tools/ineptkeymac.pyw b/Adobe_EPUB_Tools/ineptkeymac.pyw
deleted file mode 100644
index 0dd1307..0000000
--- a/Adobe_EPUB_Tools/ineptkeymac.pyw
+++ /dev/null
@@ -1,123 +0,0 @@
-#! /usr/bin/env python
-
-# ineptkeymac.py, version 1
-
-# This program runs on Mac OS X, version 10.6.2 and probably several other
-# versions. It uses Python 2.6, but it probably also runs on all versions
-# 2.x with x >= 5.
-
-# This program extracts the private RSA key for your ADE account in a
-# standard binary form (DER format) in a file of your choosing. Its purpose
-# is to make a backup of that key so that your legally bought ADE encoded
-# ebooks can be salvaged in case they would no longer be supported by ADE
-# software. No other usages are intended.
-
-# It has been tested with the key storage structure of ADE 1.7.1 and 1.7.2
-# and Sony Reader Library.
-
-# This software does not contain any encryption code. Its only use of
-# external encryption software is the use of openssl for the conversion of
-# the private key from pem to der format. It doesn't use encryption or
-# decryption, however.
-
-# You can run this program from the command line (python ineptkeymac.py
-# filename), or by doubleclicking when it has been associated with
-# Pythonlauncher. When no filename is given it will show a dialog to obtain one.
-
-from __future__ import with_statement
-
-__license__ = 'GPL v3'
-
-import sys
-import os
-import xml.etree.ElementTree as etree
-from contextlib import closing
-import Tkinter
-import Tkconstants
-import tkFileDialog
-from tkMessageBox import showerror
-from subprocess import Popen, PIPE
-import textwrap
-
-NS = 'http://ns.adobe.com/adept'
-ACTFILE = '~/Library/Application Support/Adobe/Digital Editions/activation.dat'
-HEADER = '-----BEGIN PRIVATE KEY-----\n'
-FOOTER = '\n-----END PRIVATE KEY-----\n'
-
-Gui = False
-
-def get_key():
- '''Returns the private key as a binary string (DER format)'''
- try:
- filename = os.path.expanduser(ACTFILE)
- tree = etree.parse(filename)
- xpath = '//{%s}credentials/{%s}privateLicenseKey' % (NS, NS)
- b64key = tree.findtext(xpath)
- pemkey = HEADER + textwrap.fill(b64key, 64) + FOOTER
-
- cmd = ['openssl', 'rsa', '-outform', 'der']
- proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- stdout, stderr = proc.communicate(pemkey)
-
- if proc.returncode != 0:
- error("openssl error: " + stderr)
- return None
- return stdout
-
- except IOError:
- error("Can find keyfile. Maybe you should activate your Adobe ID.")
- sys.exit(1)
-
-def store_key(key, keypath):
- '''Store the key in the file given as keypath. If no keypath is given a
- dialog will ask for one.'''
-
- try:
- if keypath is None:
- keypath = get_keypath()
- if not keypath: # Cancelled
- return
-
- with closing(open(keypath, 'wb')) as outf:
- outf.write(key)
-
- except IOError, e:
- error("Can write keyfile: " + str(e))
-
-def get_keypath():
- keypath = tkFileDialog.asksaveasfilename(
- parent = None, title = 'Select file to store ADEPT key',
- initialdir = os.path.expanduser('~/Desktop'),
- initialfile = 'adeptkey.der',
- defaultextension = '.der', filetypes = [('DER-encoded files', '.der'),
- ('All Files', '.*')])
- if keypath:
- keypath = os.path.normpath(keypath)
- return keypath
-
-def error(text):
- print text
- if Gui: showerror('Error!', text)
-
-def gui_main():
- root = Tkinter.Tk()
- root.iconify()
- global Gui
- Gui = True
- store_key(get_key(), None)
-
- return 0
-
-def main(argv=sys.argv):
- progname = os.path.basename(argv[0])
-
- if len(argv) == 1: # assume GUI if no argument given
- return gui_main()
- if len(argv) != 2:
- print "usage: %s KEYFILE" % (progname,)
- return 1
-
- store_key(get_key(), argv[1])
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/Kindle_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp b/Kindle_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp
new file mode 100644
index 0000000..535df90
--- /dev/null
+++ b/Kindle_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.cpp
@@ -0,0 +1,88 @@
+#include
+#include
+#include
+#include
+
+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_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe b/Kindle_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe
new file mode 100644
index 0000000..b40343c
Binary files /dev/null and b/Kindle_Mobi_Tools/LZskindle4PCv1_1/EZskindle4PCv1_1_1.exe differ
diff --git a/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt
new file mode 100644
index 0000000..59d97e0
--- /dev/null
+++ b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle Read Me.txt
@@ -0,0 +1,44 @@
+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
+“File not Found” error and the program will terminate.
+
+PLEASE USE ONLY FOR YOUR PERSONAL USE – 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_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp
new file mode 100644
index 0000000..1adaa93
--- /dev/null
+++ b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.cpp
@@ -0,0 +1,150 @@
+#include
+#include
+#include
+//#include
+
+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_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe
new file mode 100644
index 0000000..d6e3a8f
Binary files /dev/null and b/Kindle_Mobi_Tools/LZskindle4PCv1_1/LZskindle4PCv1_1.exe differ
diff --git a/Kindle_Mobi_Tools/LZskindle4PCv1_1/ReadMe.txt b/Kindle_Mobi_Tools/LZskindle4PCv1_1/ReadMe.txt
new file mode 100644
index 0000000..40989fd
--- /dev/null
+++ b/Kindle_Mobi_Tools/LZskindle4PCv1_1/ReadMe.txt
@@ -0,0 +1,27 @@
+Ezskindle4PC.exe
+
+This executable program makes using skindle easier for people using Windows PC’s.
+
+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’s 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 “tpz” file extension for Topaz books and will use “azw”
+for non topaz books. The files with the “azw” 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_Mobi_Tools/MobiDeDRM.pyw b/Kindle_Mobi_Tools/MobiDeDRM.pyw
index 7e96bef..c63ed9c 100644
--- a/Kindle_Mobi_Tools/MobiDeDRM.pyw
+++ b/Kindle_Mobi_Tools/MobiDeDRM.pyw
@@ -114,7 +114,7 @@ class MainDialog(Tkinter.Frame):
def get_mobipath(self):
mobipath = tkFileDialog.askopenfilename(
parent=None, title='Select Mobi eBook File',
- defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.mobi'),
+ defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),
('All Files', '.*')])
if mobipath:
mobipath = os.path.normpath(mobipath)
diff --git a/Kindle_Mobi_Tools/lib/kindlepid.py b/Kindle_Mobi_Tools/lib/kindlepid.py
index 29e4b30..e6076b5 100644
--- a/Kindle_Mobi_Tools/lib/kindlepid.py
+++ b/Kindle_Mobi_Tools/lib/kindlepid.py
@@ -76,6 +76,8 @@ def main(argv=sys.argv):
print "Kindle 2 Global serial number detected"
elif serial.startswith("B004"):
print "Kindle DX serial number detected"
+ elif serial.startswith("B005"):
+ print "Kindle DX International serial number detected"
else:
print "Warning: unrecognized serial number. Please recheck input."
return 1
diff --git a/Kindle_Mobi_Tools/lib/mobidedrm.py b/Kindle_Mobi_Tools/lib/mobidedrm.py
index 0565356..07d5f6f 100644
--- a/Kindle_Mobi_Tools/lib/mobidedrm.py
+++ b/Kindle_Mobi_Tools/lib/mobidedrm.py
@@ -38,8 +38,9 @@
# 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.
-__version__ = '0.15'
+__version__ = '0.16'
import sys
import struct
@@ -242,7 +243,7 @@ class DrmStripper:
if self.num_sections > records+1:
new_data += self.data_file[self.sections[records+1][0]:]
self.data_file = new_data
- print "done."
+ print "done"
def getResult(self):
return self.data_file
@@ -255,7 +256,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, 5) # The version number of this plugin
+ version = (0, 1, 6) # 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
diff --git a/Kindle_Mobi_Tools/unswindle/mobidedrm.py b/Kindle_Mobi_Tools/unswindle/mobidedrm.py
index 0565356..07d5f6f 100644
--- a/Kindle_Mobi_Tools/unswindle/mobidedrm.py
+++ b/Kindle_Mobi_Tools/unswindle/mobidedrm.py
@@ -38,8 +38,9 @@
# 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.
-__version__ = '0.15'
+__version__ = '0.16'
import sys
import struct
@@ -242,7 +243,7 @@ class DrmStripper:
if self.num_sections > records+1:
new_data += self.data_file[self.sections[records+1][0]:]
self.data_file = new_data
- print "done."
+ print "done"
def getResult(self):
return self.data_file
@@ -255,7 +256,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, 5) # The version number of this plugin
+ version = (0, 1, 6) # 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
diff --git a/Kindle_Mobi_Tools/unswindle/unswindle.pyw b/Kindle_Mobi_Tools/unswindle/unswindle.pyw
index f04b1b6..8eb0771 100644
--- a/Kindle_Mobi_Tools/unswindle/unswindle.pyw
+++ b/Kindle_Mobi_Tools/unswindle/unswindle.pyw
@@ -1,13 +1,13 @@
#! /usr/bin/python
# -*- coding: utf-8 -*-
-# unswindle.pyw, version 6-rc1
-# Copyright © 2009 i♥cabbages
+# unswindle.pyw, version 7
+# Copyright © 2009-2010 i♥cabbages
# Released under the terms of the GNU General Public Licence, version 3 or
# later.
-# To run this program install a 32-bit version of Python 2.6 from
+# Before running this program, you must first install Python 2.6 from
# . Save this script file as unswindle.pyw.
# Find and save in the same directory a copy of mobidedrm.py. Double-click on
# unswindle.pyw. It will run Kindle For PC. Open the book you want to
@@ -22,11 +22,14 @@
# detect unsupported versions of K4PC
# 5 - Work with new (20091222) version of K4PC
# 6 - Detect and just copy DRM-free books
+# 7 - Work with new (20100629) version of K4PC
"""
Decrypt Kindle For PC encrypted Mobipocket books.
"""
+from __future__ import with_statement
+
__license__ = 'GPL v3'
import sys
@@ -622,8 +625,17 @@ class PC1KeyGrabber(object):
0x0054c9e0: '_get_pc1_pid',
0x004f8ac9: '_get_book_path',
},
+ 'd791f52dd2ecc68722212d801ad52cb79d1b6fc9': {
+ 0x0041724d: '_i_like_wine',
+ 0x004bfe3d: '_no_debugger_here',
+ 0x005bd9db: '_no_debugger_here',
+ 0x00565920: '_get_pc1_pid',
+ 0x0050fde9: '_get_book_path',
+ },
}
+ MOBI_EXTENSIONS = set(['.prc', '.pdb', '.mobi', '.azw', '.az1', '.azw1'])
+
@classmethod
def supported_version(cls, hexdigest):
return (hexdigest in cls.HOOKS)
@@ -658,7 +670,8 @@ class PC1KeyGrabber(object):
path = path.decode('utf-16', 'ignore')
if u'\0' in path:
path = path[:path.index(u'\0')]
- if path[-4:].lower() not in ('.prc', '.pdb', '.mobi'):
+ root, ext = os.path.splitext(path)
+ if ext.lower() not in self.MOBI_EXTENSIONS:
return
self.book_path = path
@@ -667,7 +680,6 @@ class PC1KeyGrabber(object):
addr = debugger.read_process_memory(addr, type=ctypes.c_char_p)
pid = debugger.read_process_memory(addr, 8)
pid = self._checksum_pid(pid)
- print pid
self.book_pid = pid
def _checksum_pid(self, s):
diff --git a/Macintosh_Applications/Mobipocket Unlocker.app/Contents/Resources/MobiDeDRM.py b/Macintosh_Applications/Mobipocket Unlocker.app/Contents/Resources/MobiDeDRM.py
index 0565356..07d5f6f 100644
--- a/Macintosh_Applications/Mobipocket Unlocker.app/Contents/Resources/MobiDeDRM.py
+++ b/Macintosh_Applications/Mobipocket Unlocker.app/Contents/Resources/MobiDeDRM.py
@@ -38,8 +38,9 @@
# 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.
-__version__ = '0.15'
+__version__ = '0.16'
import sys
import struct
@@ -242,7 +243,7 @@ class DrmStripper:
if self.num_sections > records+1:
new_data += self.data_file[self.sections[records+1][0]:]
self.data_file = new_data
- print "done."
+ print "done"
def getResult(self):
return self.data_file
@@ -255,7 +256,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, 5) # The version number of this plugin
+ version = (0, 1, 6) # 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
diff --git a/Topaz_Tools/TopazExtract_Kindle4PC.pyw b/Topaz_Tools/TopazExtract_Kindle4PC.pyw
index 48b7f81..924d4c9 100644
--- a/Topaz_Tools/TopazExtract_Kindle4PC.pyw
+++ b/Topaz_Tools/TopazExtract_Kindle4PC.pyw
@@ -121,7 +121,7 @@ class MainDialog(Tkinter.Frame):
def get_tpzpath(self):
tpzpath = tkFileDialog.askopenfilename(
parent=None, title='Select Topaz File',
- defaultextension='.prc', filetypes=[('Topaz azw1', '.azw1'), ('Topaz prc', '.prc'),
+ defaultextension='.prc', filetypes=[('Topaz azw', '.azw'),('Topaz azw1', '.azw1'), ('Topaz prc', '.prc'),
('All Files', '.*')])
if tpzpath:
tpzpath = os.path.normpath(tpzpath)
diff --git a/Topaz_Tools/TopazExtract_KindleV1_iPhone_iPad.pyw b/Topaz_Tools/TopazExtract_KindleV1_iPhone_iPad.pyw
new file mode 100644
index 0000000..83cb79c
--- /dev/null
+++ b/Topaz_Tools/TopazExtract_KindleV1_iPhone_iPad.pyw
@@ -0,0 +1,200 @@
+#!/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 Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+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='Extract Contents of Topaz eBook to a Directory')
+ 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='Topaz eBook input file').grid(row=0, sticky=Tkconstants.E)
+ self.tpzpath = Tkinter.Entry(body, width=50)
+ self.tpzpath.grid(row=0, column=1, sticky=sticky)
+ cwd = os.getcwdu()
+ cwd = cwd.encode('utf-8')
+ self.tpzpath.insert(0, cwd)
+ button = Tkinter.Button(body, text="...", command=self.get_tpzpath)
+ button.grid(row=0, column=2)
+
+ Tkinter.Label(body, text='Output Directory').grid(row=1, sticky=Tkconstants.E)
+ self.outpath = Tkinter.Entry(body, width=50)
+ self.outpath.grid(row=1, column=1, sticky=sticky)
+ cwd = os.getcwdu()
+ cwd = cwd.encode('utf-8')
+ self.outpath.insert(0, cwd)
+ button = Tkinter.Button(body, text="...", command=self.get_outpath)
+ button.grid(row=1, column=2)
+
+ Tkinter.Label(body, text='First 8 characters of PID').grid(row=3, sticky=Tkconstants.E)
+ self.pidnum = Tkinter.StringVar()
+ self.ccinfo = Tkinter.Entry(body, width=10, textvariable=self.pidnum)
+ self.ccinfo.grid(row=3, 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=4, 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' + 'Files successfully extracted\n'
+ if poll != 0:
+ msg = text + '\n\n' + 'Error: File Extraction 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 topazrdr(self, infile, outdir, pidnum):
+ # os.putenv('PYTHONUNBUFFERED', '1')
+ pidoption = ' -p "' + pidnum + '" '
+ outoption = ' -o "' + outdir + '" '
+ cmdline = 'python ./lib/cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+ 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\cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+ else :
+ cmdline = 'lib\cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+
+ 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_tpzpath(self):
+ tpzpath = tkFileDialog.askopenfilename(
+ parent=None, title='Select Topaz File',
+ defaultextension='.prc', filetypes=[('Topaz azw1', '.azw1'), ('Topaz prc', '.prc'),('Topaz azw', '.azw'),
+ ('All Files', '.*')])
+ if tpzpath:
+ tpzpath = os.path.normpath(tpzpath)
+ self.tpzpath.delete(0, Tkconstants.END)
+ self.tpzpath.insert(0, tpzpath)
+ return
+
+ def get_outpath(self):
+ cwd = os.getcwdu()
+ cwd = cwd.encode('utf-8')
+ outpath = tkFileDialog.askdirectory(
+ parent=None, title='Directory to Extract Files into',
+ initialdir=cwd, initialfile=None)
+ 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')
+ tpzpath = self.tpzpath.get()
+ outpath = self.outpath.get()
+ if not tpzpath or not os.path.exists(tpzpath):
+ self.status['text'] = 'Specified Topaz eBook file does not exist'
+ self.sbotton.configure(state='normal')
+ return
+ if not outpath:
+ self.status['text'] = 'No output directory specified'
+ self.sbotton.configure(state='normal')
+ return
+ if not os.path.exists(outpath):
+ os.makedirs(outpath)
+ pidnum = self.pidnum.get()
+ if not pidnum or pidnum == '':
+ self.status['text'] = 'You have not entered a PID '
+ self.sbotton.configure(state='normal')
+ return
+
+ log = 'Command = "python cmbtc_dump_nonK4PC.py"\n'
+ log += 'Topaz Path Path = "'+ tpzpath + '"\n'
+ log += 'Output Directory = "' + outpath + '"\n'
+ log += 'First 8 chars of PID = "' + pidnum + '"\n'
+ log += '\n\n'
+ log += 'Please Wait ...\n'
+ log = log.encode('utf-8')
+ self.stext.insert(Tkconstants.END,log)
+ self.p2 = self.topazrdr(tpzpath, outpath, pidnum)
+
+ # 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('Topaz eBook File Extraction')
+ 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/Topaz_Tools/lib/convert2xml.py b/Topaz_Tools/lib/convert2xml.py
index e3f0fe2..d3ccd48 100644
--- a/Topaz_Tools/lib/convert2xml.py
+++ b/Topaz_Tools/lib/convert2xml.py
@@ -245,12 +245,13 @@ class PageParser(object):
'empty_text_region' : (1, 'snippets', 1, 0),
- 'img' : (1, 'snippets', 1, 0),
- 'img.x' : (1, 'scalar_number', 0, 0),
- 'img.y' : (1, 'scalar_number', 0, 0),
- 'img.h' : (1, 'scalar_number', 0, 0),
- 'img.w' : (1, 'scalar_number', 0, 0),
- 'img.src' : (1, 'scalar_number', 0, 0),
+ 'img' : (1, 'snippets', 1, 0),
+ 'img.x' : (1, 'scalar_number', 0, 0),
+ 'img.y' : (1, 'scalar_number', 0, 0),
+ 'img.h' : (1, 'scalar_number', 0, 0),
+ 'img.w' : (1, 'scalar_number', 0, 0),
+ 'img.src' : (1, 'scalar_number', 0, 0),
+ 'img.color_src' : (1, 'scalar_number', 0, 0),
'paragraph' : (1, 'snippets', 1, 0),
'paragraph.class' : (1, 'scalar_text', 0, 0),
@@ -674,6 +675,8 @@ class PageParser(object):
elif (magic[0:1] == 'p') and (magic[2:9] == '__PAGE_'):
skip = self.fo.read(2)
first_token = 'info'
+ elif (magic[0:1] == 'p') and (magic[2:8] == '_PAGE_'):
+ first_token = 'info'
elif (magic[0:1] == 'g') and (magic[2:9] == '__GLYPH'):
skip = self.fo.read(3)
first_token = 'info'
@@ -706,7 +709,10 @@ class PageParser(object):
else:
if self.debug:
print "Main Loop: Unknown value: %x" % v
-
+ if (v == 0):
+ if (self.peek(1) == 0x5f):
+ skip = self.fo.read(1)
+ first_token = 'info'
# now do snippet injection
if len(self.snippetList) > 0 :
@@ -795,4 +801,4 @@ def main(argv):
return xmlpage
if __name__ == '__main__':
- sys.exit(main(''))
+ sys.exit(main(''))
\ No newline at end of file