]> xmof Git - DeDRM.git/commitdiff
tools v4.7
authorApprentice Alf <apprenticealf@gmail.com>
Mon, 5 Sep 2011 06:17:36 +0000 (07:17 +0100)
committerApprentice Alf <apprenticealf@gmail.com>
Fri, 6 Mar 2015 07:18:01 +0000 (07:18 +0000)
19 files changed:
Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py
Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py
Calibre_Plugins/k4mobidedrm_plugin.zip
Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py
DeDRM_Macintosh_Application/DeDRM.app.txt
DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist
DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/description.rtfd/TXT.rtf
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py
KindleBooks/KindleBooks.pyw
KindleBooks/lib/k4mobidedrm.py
KindleBooks/lib/mobidedrm.py
Mobi_Additional_Tools/lib/mobidedrm.py

index 9303ea7e7baf2d3246017be8eafc7e56fa1e21cf..7081e78602ceabe1b930323b55c99492ef3eb2a9 100644 (file)
@@ -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()
index de877cd35eae38e254a80568d40fce2b6849b96d..daa19a5bb8297c138868a2603a9047a1aef0354b 100644 (file)
@@ -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:
index faba7b403cf2548428dbc87bc2f6c16a2f517fb3..925b75cc0e188209b2c8fb06fa4b3db0ebb8a5a4 100644 (file)
Binary files a/Calibre_Plugins/k4mobidedrm_plugin.zip and b/Calibre_Plugins/k4mobidedrm_plugin.zip differ
index 1892020ea1e363c2350beb34ca183800c3503a97..4d978b377ae0cb64cb057212b5d82b314117176a 100644 (file)
@@ -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 <infile> <outfile> [<Comma separated list of PIDs to try>]" % 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)
index 856958e029367525257ef53947476a5d888bbd9a..75b4e74dd3672fe7cf40c3148c8f2c7e80f0a63e 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app.txt and b/DeDRM_Macintosh_Application/DeDRM.app.txt differ
index c457566d0c1a8e30319f93b52efdce6f5e739637..9528c7a13ac346f39c5c364bdbfaad2671d0b26f 100644 (file)
        <key>CFBundleExecutable</key>
        <string>droplet</string>
        <key>CFBundleGetInfoString</key>
-       <string>DeDRM 2.9, Written 2010–2011 by Apprentice Alf and others.</string>
+       <string>DeDRM 3.0, Written 2010–2011 by Apprentice Alf and others.</string>
        <key>CFBundleIconFile</key>
        <string>droplet</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
-       <string>DeDRM</string>
+       <string>DeDRM 3.0</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>2.9</string>
+       <string>3.0</string>
        <key>CFBundleSignature</key>
        <string>dplt</string>
        <key>LSMinimumSystemVersion</key>
        <true/>
        <key>WindowState</key>
        <dict>
-               <key>dividerCollapsed</key>
-               <true/>
-               <key>eventLogLevel</key>
-               <integer>-1</integer>
                <key>name</key>
                <string>ScriptWindowState</string>
                <key>positionOfDivider</key>
-               <real>0</real>
+               <real>274</real>
                <key>savedFrame</key>
-               <string>1578 27 862 788 1440 -150 1680 1050 </string>
+               <string>39 376 439 476 0 0 1440 878 </string>
                <key>selectedTabView</key>
-               <string>event log</string>
+               <string>result</string>
        </dict>
 </dict>
 </plist>
index 6a4731203faab0c2c04422745e3f25741f928321..c715860463c5434cb471de58b710cbe95976c23f 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/MacOS/droplet differ
index cd55cb75f4b33127136994514d02eb513cf9b85d..d3487d6ff4ef153783feb9f065ed41ddc7727795 100644 (file)
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
index 33192eae3ef01b4c6e7ec4f66810ff0a2f3a6d75..0f8be61c4ef5a03538444534b5d6fddd1d0162af 100644 (file)
@@ -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
index 0601ac5df420165530054ff9ed931153aa2df7fe..3cb2cea58312d350196e96ded923419960a5aa75 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc differ
index de877cd35eae38e254a80568d40fce2b6849b96d..daa19a5bb8297c138868a2603a9047a1aef0354b 100644 (file)
@@ -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:
index 1892020ea1e363c2350beb34ca183800c3503a97..4d978b377ae0cb64cb057212b5d82b314117176a 100644 (file)
@@ -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 <infile> <outfile> [<Comma separated list of PIDs to try>]" % 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)
index 1b81adee3adec06c5985746d8dfb4f6367302307..6d9af630527e39c3f4c1b31779cbabbcdc57d5b0 100644 (file)
@@ -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':
index de877cd35eae38e254a80568d40fce2b6849b96d..daa19a5bb8297c138868a2603a9047a1aef0354b 100644 (file)
@@ -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:
index 1892020ea1e363c2350beb34ca183800c3503a97..4d978b377ae0cb64cb057212b5d82b314117176a 100644 (file)
@@ -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 <infile> <outfile> [<Comma separated list of PIDs to try>]" % 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)
index 63ef77d7651c8233b160f00a64a4527cdf54d408..3184e0e7a1ae3fe91794300dfcac3b03006787f9 100644 (file)
@@ -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)
index de877cd35eae38e254a80568d40fce2b6849b96d..daa19a5bb8297c138868a2603a9047a1aef0354b 100644 (file)
@@ -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:
index 1892020ea1e363c2350beb34ca183800c3503a97..4d978b377ae0cb64cb057212b5d82b314117176a 100644 (file)
@@ -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 <infile> <outfile> [<Comma separated list of PIDs to try>]" % 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)
index 1892020ea1e363c2350beb34ca183800c3503a97..4d978b377ae0cb64cb057212b5d82b314117176a 100644 (file)
@@ -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 <infile> <outfile> [<Comma separated list of PIDs to try>]" % 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)