diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py b/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py index 9303ea7..7081e78 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py @@ -16,24 +16,23 @@ import re class K4DeDRM(FileTypePlugin): name = 'K4PC, K4Mac, Kindle Mobi and Topaz DeDRM' # Name of the plugin - description = 'Removes DRM from K4PC and Mac, Kindle Mobi and Topaz files. Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' + description = 'Removes DRM from Mobipocket, Kindle/Mobi, Kindle/Topaz and Kindle/Print Replica 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, 3, 6) # The version number of this plugin - file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to + version = (0, 3, 7) # The version number of this plugin + file_types = set(['prc','mobi','azw','azw1','azw4','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm minimum_calibre_version = (0, 7, 55) def run(self, path_to_ebook): - + plug_ver = '.'.join(str(self.version).strip('()').replace(' ', '').split(',')) k4 = True if sys.platform.startswith('linux'): k4 = False pids = [] serials = [] kInfoFiles = [] - # Get supplied list of PIDs to try from plugin customization. customvalues = self.site_customization.split(',') for customvalue in customvalues: @@ -46,12 +45,12 @@ class K4DeDRM(FileTypePlugin): serials.append(customvalue) else: print "%s is not a valid Kindle serial number or PID." % str(customvalue) - + # Load any kindle info files (*.info) included Calibre's config directory. try: # Find Calibre's configuration directory. confpath = os.path.split(os.path.split(self.plugin_path)[0])[0] - print 'K4MobiDeDRM: Calibre configuration directory = %s' % confpath + print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, confpath) files = os.listdir(confpath) filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE) files = filter(filefilter.search, files) @@ -59,9 +58,9 @@ class K4DeDRM(FileTypePlugin): for filename in files: fpath = os.path.join(confpath, filename) kInfoFiles.append(fpath) - print 'K4MobiDeDRM: Kindle info/kinf file %s found in config folder.' % filename + print 'K4MobiDeDRM v%s: Kindle info/kinf file %s found in config folder.' % (plug_ver, filename) except IOError: - print 'K4MobiDeDRM: Error reading kindle info/kinf files from config directory.' + print 'K4MobiDeDRM v%s: Error reading kindle info/kinf files from config directory.' % plug_ver pass mobi = True @@ -83,30 +82,34 @@ class K4DeDRM(FileTypePlugin): try: mb.processBook(pidlst) - except mobidedrm.DrmException: + except mobidedrm.DrmException, e: #if you reached here then no luck raise and exception if is_ok_to_use_qt(): from PyQt4.Qt import QMessageBox - d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook) + d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM v%s Plugin" % plug_ver, "Error: " + str(e) + "... %s\n" % path_to_ebook) d.show() d.raise_() d.exec_() - raise Exception("K4MobiDeDRM plugin could not decode the file") - except topazextract.TpzDRMError: + raise Exception("K4MobiDeDRM plugin v%s Error: %s" % (plug_ver, str(e))) + except topazextract.TpzDRMError, e: #if you reached here then no luck raise and exception if is_ok_to_use_qt(): from PyQt4.Qt import QMessageBox - d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook) + d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM v%s Plugin" % plug_ver, "Error: " + str(e) + "... %s\n" % path_to_ebook) d.show() d.raise_() d.exec_() - raise Exception("K4MobiDeDRM plugin could not decode the file") + raise Exception("K4MobiDeDRM plugin v%s Error: %s" % (plug_ver, str(e))) print "Success!" if mobi: - of = self.temporary_file(bookname+'.mobi') + if mb.getPrintReplica(): + of = self.temporary_file(bookname+'.azw4') + print 'K4MobiDeDRM v%s: Print Replica format detected.' % plug_ver + else: + of = self.temporary_file(bookname+'.mobi') mb.getMobiFile(of.name) - else : + else: of = self.temporary_file(bookname+'.htmlz') mb.getHTMLZip(of.name) mb.cleanup() diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py b/Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py index de877cd..daa19a5 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py @@ -17,7 +17,7 @@ from __future__ import with_statement # and many many others -__version__ = '3.6' +__version__ = '3.7' class Unbuffered: def __init__(self, stream): @@ -76,7 +76,7 @@ def cleanup_name(name): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): # handle the obvious cases at the beginning if not os.path.isfile(infile): - print "Error: Input file does not exist" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist" return 1 mobi = True @@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): mb.processBook(pidlst) except mobidedrm.DrmException, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except topazextract.TpzDRMError, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except Exception, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 if mobi: - outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') + if mb.getPrintReplica(): + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4') + else: + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') mb.getMobiFile(outfile) return 0 @@ -158,7 +161,6 @@ def main(argv=sys.argv): print ('K4MobiDeDrm v%(__version__)s ' 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) - print ' ' try: opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") except getopt.GetoptError, err: diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip index faba7b4..925b75c 100644 Binary files a/Calibre_Plugins/k4mobidedrm_plugin.zip and b/Calibre_Plugins/k4mobidedrm_plugin.zip differ diff --git a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py index 1892020..4d978b3 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py @@ -53,8 +53,9 @@ # files, but they are not for HUFF/CDIC compress files! # 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.31 - The multibyte encrytion info is true for version 7 files too. +# 0.32 - Added support for "Print Replica" Kindle ebooks -__version__ = '0.31' +__version__ = '0.32' import sys @@ -163,6 +164,9 @@ class MobiBook: return self.data_file[off:endoff] def __init__(self, infile): + print ('MobiDeDrm v%(__version__)s. ' + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) + # initial sanity check on file self.data_file = file(infile, 'rb').read() self.mobi_data = '' @@ -193,6 +197,7 @@ class MobiBook: self.meta_array = {} return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) self.extra_data_flags = 0 @@ -230,8 +235,13 @@ class MobiBook: except: self.meta_array = {} pass + self.print_replica = False def getBookTitle(self): + codec_map = { + 1252 : 'windows-1252', + 65001 : 'utf-8', + } title = '' if 503 in self.meta_array: title = self.meta_array[503] @@ -242,7 +252,10 @@ class MobiBook: if title == '': title = self.header[:32] title = title.split("\0")[0] - return title + codec = 'windows-1252' + if self.mobi_codepage in codec_map.keys(): + codec = codec_map[self.mobi_codepage] + return unicode(title, codec).encode('utf-8') def getPIDMetaInfo(self): rec209 = '' @@ -306,6 +319,9 @@ class MobiBook: def getMobiFile(self, outpath): file(outpath,'wb').write(self.mobi_data) + + def getPrintReplica(self): + return self.print_replica def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) @@ -313,10 +329,17 @@ class MobiBook: self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." + # we must still check for Print Replica + self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + if 406 in self.meta_array: + data406 = self.meta_array[406] + val406, = struct.unpack('>Q',data406) + if val406 != 0: + raise DrmException("Cannot decode library or rented ebooks.") goodpids = [] for pid in pidlist: @@ -367,7 +390,10 @@ class MobiBook: if i%100 == 0: print ".", # print "record %d, extra_size %d" %(i,extra_size) - self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) + decoded_data = PC1(found_key, data[0:len(data) - extra_size]) + if i==1: + self.print_replica = (decoded_data[0:4] == '%MOP') + self.mobi_data += decoded_data if extra_size > 0: self.mobi_data += data[-extra_size:] if self.num_sections > self.records+1: @@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist): def main(argv=sys.argv): print ('MobiDeDrm v%(__version__)s. ' - 'Copyright 2008-2010 The Dark Reverser.' % globals()) + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) if len(argv)<3 or len(argv)>4: - print "Removes protection from Mobipocket books" + print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Usage:" print " %s []" % sys.argv[0] return 1 @@ -402,9 +428,9 @@ def main(argv=sys.argv): infile = argv[1] outfile = argv[2] if len(argv) is 4: - pidlist = argv[3].split(',') + pidlist = argv[3].split(',') else: - pidlist = {} + pidlist = {} try: stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) diff --git a/DeDRM_Macintosh_Application/DeDRM.app.txt b/DeDRM_Macintosh_Application/DeDRM.app.txt index 856958e..75b4e74 100644 Binary files a/DeDRM_Macintosh_Application/DeDRM.app.txt and b/DeDRM_Macintosh_Application/DeDRM.app.txt differ diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist index c457566..9528c7a 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist @@ -24,17 +24,17 @@ CFBundleExecutable droplet CFBundleGetInfoString - DeDRM 2.9, Written 2010–2011 by Apprentice Alf and others. + DeDRM 3.0, Written 2010–2011 by Apprentice Alf and others. CFBundleIconFile droplet CFBundleInfoDictionaryVersion 6.0 CFBundleName - DeDRM + DeDRM 3.0 CFBundlePackageType APPL CFBundleShortVersionString - 2.9 + 3.0 CFBundleSignature dplt LSMinimumSystemVersion @@ -43,18 +43,14 @@ WindowState - dividerCollapsed - - eventLogLevel - -1 name ScriptWindowState positionOfDivider - 0 + 274 savedFrame - 1578 27 862 788 1440 -150 1680 1050 + 39 376 439 476 0 0 1440 878 selectedTabView - event log + result diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet b/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet index 6a47312..c715860 100644 Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet differ diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt index cd55cb7..d3487d6 100644 Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt differ diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/description.rtfd/TXT.rtf b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/description.rtfd/TXT.rtf index 33192ea..0f8be61 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/description.rtfd/TXT.rtf +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/description.rtfd/TXT.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 +{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540 {\fonttbl} {\colortbl;\red255\green255\blue255;} } \ No newline at end of file diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc index 0601ac5..3cb2cea 100644 Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc differ diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py index de877cd..daa19a5 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py @@ -17,7 +17,7 @@ from __future__ import with_statement # and many many others -__version__ = '3.6' +__version__ = '3.7' class Unbuffered: def __init__(self, stream): @@ -76,7 +76,7 @@ def cleanup_name(name): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): # handle the obvious cases at the beginning if not os.path.isfile(infile): - print "Error: Input file does not exist" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist" return 1 mobi = True @@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): mb.processBook(pidlst) except mobidedrm.DrmException, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except topazextract.TpzDRMError, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except Exception, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 if mobi: - outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') + if mb.getPrintReplica(): + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4') + else: + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') mb.getMobiFile(outfile) return 0 @@ -158,7 +161,6 @@ def main(argv=sys.argv): print ('K4MobiDeDrm v%(__version__)s ' 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) - print ' ' try: opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") except getopt.GetoptError, err: diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py index 1892020..4d978b3 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py @@ -53,8 +53,9 @@ # files, but they are not for HUFF/CDIC compress files! # 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.31 - The multibyte encrytion info is true for version 7 files too. +# 0.32 - Added support for "Print Replica" Kindle ebooks -__version__ = '0.31' +__version__ = '0.32' import sys @@ -163,6 +164,9 @@ class MobiBook: return self.data_file[off:endoff] def __init__(self, infile): + print ('MobiDeDrm v%(__version__)s. ' + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) + # initial sanity check on file self.data_file = file(infile, 'rb').read() self.mobi_data = '' @@ -193,6 +197,7 @@ class MobiBook: self.meta_array = {} return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) self.extra_data_flags = 0 @@ -230,8 +235,13 @@ class MobiBook: except: self.meta_array = {} pass + self.print_replica = False def getBookTitle(self): + codec_map = { + 1252 : 'windows-1252', + 65001 : 'utf-8', + } title = '' if 503 in self.meta_array: title = self.meta_array[503] @@ -242,7 +252,10 @@ class MobiBook: if title == '': title = self.header[:32] title = title.split("\0")[0] - return title + codec = 'windows-1252' + if self.mobi_codepage in codec_map.keys(): + codec = codec_map[self.mobi_codepage] + return unicode(title, codec).encode('utf-8') def getPIDMetaInfo(self): rec209 = '' @@ -306,6 +319,9 @@ class MobiBook: def getMobiFile(self, outpath): file(outpath,'wb').write(self.mobi_data) + + def getPrintReplica(self): + return self.print_replica def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) @@ -313,10 +329,17 @@ class MobiBook: self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." + # we must still check for Print Replica + self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + if 406 in self.meta_array: + data406 = self.meta_array[406] + val406, = struct.unpack('>Q',data406) + if val406 != 0: + raise DrmException("Cannot decode library or rented ebooks.") goodpids = [] for pid in pidlist: @@ -367,7 +390,10 @@ class MobiBook: if i%100 == 0: print ".", # print "record %d, extra_size %d" %(i,extra_size) - self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) + decoded_data = PC1(found_key, data[0:len(data) - extra_size]) + if i==1: + self.print_replica = (decoded_data[0:4] == '%MOP') + self.mobi_data += decoded_data if extra_size > 0: self.mobi_data += data[-extra_size:] if self.num_sections > self.records+1: @@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist): def main(argv=sys.argv): print ('MobiDeDrm v%(__version__)s. ' - 'Copyright 2008-2010 The Dark Reverser.' % globals()) + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) if len(argv)<3 or len(argv)>4: - print "Removes protection from Mobipocket books" + print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Usage:" print " %s []" % sys.argv[0] return 1 @@ -402,9 +428,9 @@ def main(argv=sys.argv): infile = argv[1] outfile = argv[2] if len(argv) is 4: - pidlist = argv[3].split(',') + pidlist = argv[3].split(',') else: - pidlist = {} + pidlist = {} try: stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw index 1b81ade..6d9af63 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw @@ -263,6 +263,7 @@ class PrefsDialog(Toplevel): filetypes=[('ePub Files','.epub'), ('Kindle','.azw'), ('Kindle','.azw1'), + ('Kindle','.azw4'), ('Kindle','.tpz'), ('Kindle','.mobi'), ('Kindle','.prc'), @@ -465,7 +466,7 @@ class ConvDialog(Toplevel): if ext == '.pdb': self.p2 = processPDB(apphome, infile, outdir, rscpath) return 0 - if ext in ['.azw', '.azw1', '.prc', '.mobi', '.tpz']: + if ext in ['.azw', '.azw1', '.azw4', '.prc', '.mobi', '.tpz']: self.p2 = processK4MOBI(apphome, infile, outdir, rscpath) return 0 if ext == '.pdf': diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py index de877cd..daa19a5 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py @@ -17,7 +17,7 @@ from __future__ import with_statement # and many many others -__version__ = '3.6' +__version__ = '3.7' class Unbuffered: def __init__(self, stream): @@ -76,7 +76,7 @@ def cleanup_name(name): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): # handle the obvious cases at the beginning if not os.path.isfile(infile): - print "Error: Input file does not exist" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist" return 1 mobi = True @@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): mb.processBook(pidlst) except mobidedrm.DrmException, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except topazextract.TpzDRMError, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except Exception, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 if mobi: - outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') + if mb.getPrintReplica(): + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4') + else: + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') mb.getMobiFile(outfile) return 0 @@ -158,7 +161,6 @@ def main(argv=sys.argv): print ('K4MobiDeDrm v%(__version__)s ' 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) - print ' ' try: opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") except getopt.GetoptError, err: diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py index 1892020..4d978b3 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py @@ -53,8 +53,9 @@ # files, but they are not for HUFF/CDIC compress files! # 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.31 - The multibyte encrytion info is true for version 7 files too. +# 0.32 - Added support for "Print Replica" Kindle ebooks -__version__ = '0.31' +__version__ = '0.32' import sys @@ -163,6 +164,9 @@ class MobiBook: return self.data_file[off:endoff] def __init__(self, infile): + print ('MobiDeDrm v%(__version__)s. ' + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) + # initial sanity check on file self.data_file = file(infile, 'rb').read() self.mobi_data = '' @@ -193,6 +197,7 @@ class MobiBook: self.meta_array = {} return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) self.extra_data_flags = 0 @@ -230,8 +235,13 @@ class MobiBook: except: self.meta_array = {} pass + self.print_replica = False def getBookTitle(self): + codec_map = { + 1252 : 'windows-1252', + 65001 : 'utf-8', + } title = '' if 503 in self.meta_array: title = self.meta_array[503] @@ -242,7 +252,10 @@ class MobiBook: if title == '': title = self.header[:32] title = title.split("\0")[0] - return title + codec = 'windows-1252' + if self.mobi_codepage in codec_map.keys(): + codec = codec_map[self.mobi_codepage] + return unicode(title, codec).encode('utf-8') def getPIDMetaInfo(self): rec209 = '' @@ -306,6 +319,9 @@ class MobiBook: def getMobiFile(self, outpath): file(outpath,'wb').write(self.mobi_data) + + def getPrintReplica(self): + return self.print_replica def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) @@ -313,10 +329,17 @@ class MobiBook: self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." + # we must still check for Print Replica + self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + if 406 in self.meta_array: + data406 = self.meta_array[406] + val406, = struct.unpack('>Q',data406) + if val406 != 0: + raise DrmException("Cannot decode library or rented ebooks.") goodpids = [] for pid in pidlist: @@ -367,7 +390,10 @@ class MobiBook: if i%100 == 0: print ".", # print "record %d, extra_size %d" %(i,extra_size) - self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) + decoded_data = PC1(found_key, data[0:len(data) - extra_size]) + if i==1: + self.print_replica = (decoded_data[0:4] == '%MOP') + self.mobi_data += decoded_data if extra_size > 0: self.mobi_data += data[-extra_size:] if self.num_sections > self.records+1: @@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist): def main(argv=sys.argv): print ('MobiDeDrm v%(__version__)s. ' - 'Copyright 2008-2010 The Dark Reverser.' % globals()) + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) if len(argv)<3 or len(argv)>4: - print "Removes protection from Mobipocket books" + print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Usage:" print " %s []" % sys.argv[0] return 1 @@ -402,9 +428,9 @@ def main(argv=sys.argv): infile = argv[1] outfile = argv[2] if len(argv) is 4: - pidlist = argv[3].split(',') + pidlist = argv[3].split(',') else: - pidlist = {} + pidlist = {} try: stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) diff --git a/KindleBooks/KindleBooks.pyw b/KindleBooks/KindleBooks.pyw index 63ef77d..3184e0e 100644 --- a/KindleBooks/KindleBooks.pyw +++ b/KindleBooks/KindleBooks.pyw @@ -109,7 +109,7 @@ class MainDialog(Tkinter.Frame): # post output from subprocess in scrolled text widget def showCmdOutput(self, msg): if msg and msg !='': - msg = msg.encode('utf-8') + # msg = msg.encode('utf-8') if sys.platform.startswith('win'): msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) @@ -149,7 +149,7 @@ class MainDialog(Tkinter.Frame): mobipath = tkFileDialog.askopenfilename( initialdir = cpath, parent=None, title='Select Kindle/Mobi/Topaz eBook File', - defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),('Mobi eBook File', '.tpz'),('Mobi eBook File', '.azw1'),('All Files', '.*')]) + defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),('Mobi eBook File', '.tpz'),('Mobi eBook File', '.azw1'),('Mobi azw4 eBook File', '.azw4'),('All Files', '.*')]) if mobipath: mobipath = os.path.normpath(mobipath) self.mobipath.delete(0, Tkconstants.END) diff --git a/KindleBooks/lib/k4mobidedrm.py b/KindleBooks/lib/k4mobidedrm.py index de877cd..daa19a5 100644 --- a/KindleBooks/lib/k4mobidedrm.py +++ b/KindleBooks/lib/k4mobidedrm.py @@ -17,7 +17,7 @@ from __future__ import with_statement # and many many others -__version__ = '3.6' +__version__ = '3.7' class Unbuffered: def __init__(self, stream): @@ -76,7 +76,7 @@ def cleanup_name(name): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): # handle the obvious cases at the beginning if not os.path.isfile(infile): - print "Error: Input file does not exist" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist" return 1 mobi = True @@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): mb.processBook(pidlst) except mobidedrm.DrmException, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except topazextract.TpzDRMError, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 except Exception, e: - print "Error: " + str(e) + "\nDRM Removal Failed.\n" + print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n" return 1 if mobi: - outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') + if mb.getPrintReplica(): + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4') + else: + outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') mb.getMobiFile(outfile) return 0 @@ -158,7 +161,6 @@ def main(argv=sys.argv): print ('K4MobiDeDrm v%(__version__)s ' 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) - print ' ' try: opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") except getopt.GetoptError, err: diff --git a/KindleBooks/lib/mobidedrm.py b/KindleBooks/lib/mobidedrm.py index 1892020..4d978b3 100644 --- a/KindleBooks/lib/mobidedrm.py +++ b/KindleBooks/lib/mobidedrm.py @@ -53,8 +53,9 @@ # files, but they are not for HUFF/CDIC compress files! # 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.31 - The multibyte encrytion info is true for version 7 files too. +# 0.32 - Added support for "Print Replica" Kindle ebooks -__version__ = '0.31' +__version__ = '0.32' import sys @@ -163,6 +164,9 @@ class MobiBook: return self.data_file[off:endoff] def __init__(self, infile): + print ('MobiDeDrm v%(__version__)s. ' + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) + # initial sanity check on file self.data_file = file(infile, 'rb').read() self.mobi_data = '' @@ -193,6 +197,7 @@ class MobiBook: self.meta_array = {} return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) self.extra_data_flags = 0 @@ -230,8 +235,13 @@ class MobiBook: except: self.meta_array = {} pass + self.print_replica = False def getBookTitle(self): + codec_map = { + 1252 : 'windows-1252', + 65001 : 'utf-8', + } title = '' if 503 in self.meta_array: title = self.meta_array[503] @@ -242,7 +252,10 @@ class MobiBook: if title == '': title = self.header[:32] title = title.split("\0")[0] - return title + codec = 'windows-1252' + if self.mobi_codepage in codec_map.keys(): + codec = codec_map[self.mobi_codepage] + return unicode(title, codec).encode('utf-8') def getPIDMetaInfo(self): rec209 = '' @@ -306,6 +319,9 @@ class MobiBook: def getMobiFile(self, outpath): file(outpath,'wb').write(self.mobi_data) + + def getPrintReplica(self): + return self.print_replica def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) @@ -313,10 +329,17 @@ class MobiBook: self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." + # we must still check for Print Replica + self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + if 406 in self.meta_array: + data406 = self.meta_array[406] + val406, = struct.unpack('>Q',data406) + if val406 != 0: + raise DrmException("Cannot decode library or rented ebooks.") goodpids = [] for pid in pidlist: @@ -367,7 +390,10 @@ class MobiBook: if i%100 == 0: print ".", # print "record %d, extra_size %d" %(i,extra_size) - self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) + decoded_data = PC1(found_key, data[0:len(data) - extra_size]) + if i==1: + self.print_replica = (decoded_data[0:4] == '%MOP') + self.mobi_data += decoded_data if extra_size > 0: self.mobi_data += data[-extra_size:] if self.num_sections > self.records+1: @@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist): def main(argv=sys.argv): print ('MobiDeDrm v%(__version__)s. ' - 'Copyright 2008-2010 The Dark Reverser.' % globals()) + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) if len(argv)<3 or len(argv)>4: - print "Removes protection from Mobipocket books" + print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Usage:" print " %s []" % sys.argv[0] return 1 @@ -402,9 +428,9 @@ def main(argv=sys.argv): infile = argv[1] outfile = argv[2] if len(argv) is 4: - pidlist = argv[3].split(',') + pidlist = argv[3].split(',') else: - pidlist = {} + pidlist = {} try: stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) diff --git a/Mobi_Additional_Tools/lib/mobidedrm.py b/Mobi_Additional_Tools/lib/mobidedrm.py index 1892020..4d978b3 100644 --- a/Mobi_Additional_Tools/lib/mobidedrm.py +++ b/Mobi_Additional_Tools/lib/mobidedrm.py @@ -53,8 +53,9 @@ # files, but they are not for HUFF/CDIC compress files! # 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.31 - The multibyte encrytion info is true for version 7 files too. +# 0.32 - Added support for "Print Replica" Kindle ebooks -__version__ = '0.31' +__version__ = '0.32' import sys @@ -163,6 +164,9 @@ class MobiBook: return self.data_file[off:endoff] def __init__(self, infile): + print ('MobiDeDrm v%(__version__)s. ' + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) + # initial sanity check on file self.data_file = file(infile, 'rb').read() self.mobi_data = '' @@ -193,6 +197,7 @@ class MobiBook: self.meta_array = {} return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) self.extra_data_flags = 0 @@ -230,8 +235,13 @@ class MobiBook: except: self.meta_array = {} pass + self.print_replica = False def getBookTitle(self): + codec_map = { + 1252 : 'windows-1252', + 65001 : 'utf-8', + } title = '' if 503 in self.meta_array: title = self.meta_array[503] @@ -242,7 +252,10 @@ class MobiBook: if title == '': title = self.header[:32] title = title.split("\0")[0] - return title + codec = 'windows-1252' + if self.mobi_codepage in codec_map.keys(): + codec = codec_map[self.mobi_codepage] + return unicode(title, codec).encode('utf-8') def getPIDMetaInfo(self): rec209 = '' @@ -306,6 +319,9 @@ class MobiBook: def getMobiFile(self, outpath): file(outpath,'wb').write(self.mobi_data) + + def getPrintReplica(self): + return self.print_replica def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) @@ -313,10 +329,17 @@ class MobiBook: self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." + # we must still check for Print Replica + self.print_replica = (self.loadSection(1)[0:4] == '%MOP') self.mobi_data = self.data_file return if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + if 406 in self.meta_array: + data406 = self.meta_array[406] + val406, = struct.unpack('>Q',data406) + if val406 != 0: + raise DrmException("Cannot decode library or rented ebooks.") goodpids = [] for pid in pidlist: @@ -367,7 +390,10 @@ class MobiBook: if i%100 == 0: print ".", # print "record %d, extra_size %d" %(i,extra_size) - self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) + decoded_data = PC1(found_key, data[0:len(data) - extra_size]) + if i==1: + self.print_replica = (decoded_data[0:4] == '%MOP') + self.mobi_data += decoded_data if extra_size > 0: self.mobi_data += data[-extra_size:] if self.num_sections > self.records+1: @@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist): def main(argv=sys.argv): print ('MobiDeDrm v%(__version__)s. ' - 'Copyright 2008-2010 The Dark Reverser.' % globals()) + 'Copyright 2008-2011 The Dark Reverser et al.' % globals()) if len(argv)<3 or len(argv)>4: - print "Removes protection from Mobipocket books" + print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Usage:" print " %s []" % sys.argv[0] return 1 @@ -402,9 +428,9 @@ def main(argv=sys.argv): infile = argv[1] outfile = argv[2] if len(argv) is 4: - pidlist = argv[3].split(',') + pidlist = argv[3].split(',') else: - pidlist = {} + pidlist = {} try: stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file)