Compare commits

..

2 Commits
v4.5 ... v4.7

Author SHA1 Message Date
Apprentice Alf
e95ed1a8ed tools v4.7 2015-03-06 07:18:01 +00:00
Apprentice Alf
ba5927a20d tools v4.6 2015-03-06 07:13:06 +00:00
20 changed files with 306 additions and 126 deletions

View 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, 5) # 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:
@@ -51,7 +50,7 @@ class K4DeDRM(FileTypePlugin):
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()

View File

@@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others
__version__ = '3.5'
__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:

View File

@@ -49,11 +49,13 @@
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They aren't for DOC compressed
# files, but they are for HUFF/CDIC compress files!
# included in the encryption were wrong. They are for DOC compressed
# 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.30'
__version__ = '0.32'
import sys
@@ -162,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 = ''
@@ -192,14 +197,15 @@ 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
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.mobi_version < 7) and (self.compression != 17480):
# multibyte utf8 data is included in the encryption for mobi_version 6 and below
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
@@ -229,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]
@@ -241,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,16 +320,26 @@ 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])
print 'Crypto Type is: ', crypto_type
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:
@@ -366,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:
@@ -391,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
@@ -401,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)

View File

@@ -24,17 +24,17 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM 2.8, Written 20102011 by Apprentice Alf and others.</string>
<string>DeDRM 3.0, Written 20102011 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.8</string>
<string>3.0</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSMinimumSystemVersion</key>
@@ -43,18 +43,14 @@
<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>

View File

@@ -1,4 +1,4 @@
{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540
{\fonttbl}
{\colortbl;\red255\green255\blue255;}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others
__version__ = '3.5'
__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:

View File

@@ -49,11 +49,13 @@
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They aren't for DOC compressed
# files, but they are for HUFF/CDIC compress files!
# included in the encryption were wrong. They are for DOC compressed
# 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.30'
__version__ = '0.32'
import sys
@@ -162,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 = ''
@@ -192,14 +197,15 @@ 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
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.mobi_version < 7) and (self.compression != 17480):
# multibyte utf8 data is included in the encryption for mobi_version 6 and below
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
@@ -229,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]
@@ -241,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,16 +320,26 @@ 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])
print 'Crypto Type is: ', crypto_type
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:
@@ -366,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:
@@ -391,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
@@ -401,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)

View 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':

View File

@@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others
__version__ = '3.5'
__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:

View File

@@ -49,11 +49,13 @@
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They aren't for DOC compressed
# files, but they are for HUFF/CDIC compress files!
# included in the encryption were wrong. They are for DOC compressed
# 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.30'
__version__ = '0.32'
import sys
@@ -162,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 = ''
@@ -192,14 +197,15 @@ 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
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.mobi_version < 7) and (self.compression != 17480):
# multibyte utf8 data is included in the encryption for mobi_version 6 and below
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
@@ -229,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]
@@ -241,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,16 +320,26 @@ 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])
print 'Crypto Type is: ', crypto_type
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:
@@ -366,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:
@@ -391,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
@@ -401,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)

View 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)

View File

@@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others
__version__ = '3.5'
__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:

View File

@@ -49,11 +49,13 @@
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They aren't for DOC compressed
# files, but they are for HUFF/CDIC compress files!
# included in the encryption were wrong. They are for DOC compressed
# 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.30'
__version__ = '0.32'
import sys
@@ -162,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 = ''
@@ -192,14 +197,15 @@ 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
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.mobi_version < 7) and (self.compression != 17480):
# multibyte utf8 data is included in the encryption for mobi_version 6 and below
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
@@ -229,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]
@@ -241,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,16 +320,26 @@ 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])
print 'Crypto Type is: ', crypto_type
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:
@@ -366,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:
@@ -391,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
@@ -401,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)

View File

@@ -49,11 +49,13 @@
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They aren't for DOC compressed
# files, but they are for HUFF/CDIC compress files!
# included in the encryption were wrong. They are for DOC compressed
# 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.30'
__version__ = '0.32'
import sys
@@ -162,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 = ''
@@ -192,14 +197,15 @@ 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
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.mobi_version < 7) and (self.compression != 17480):
# multibyte utf8 data is included in the encryption for mobi_version 6 and below
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
@@ -229,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]
@@ -241,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,16 +320,26 @@ 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])
print 'Crypto Type is: ', crypto_type
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:
@@ -366,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:
@@ -391,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
@@ -401,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)

View File

@@ -6,17 +6,17 @@ The set includes tools to remove DRM from eReader PDB books, Barnes and Noble eP
This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started.
Calibre Users (Mac OS X, Linux, Windows)
Calibre Users (Mac OS X, Windows)
-------------
If you are a calibre user, the quickest and easiest way to remove DRM from your ebooks is to open the Calibre_Plugins folder and install each of the plugins following the instructions and configuration directions provided in each plugins README file.
Once installed and configured, you can simply import a DRM book into Calibre and end up with the DeDRM version in the Calibre database.
These plugins work for Windows, Mac OS X, and Linux
These plugins work for Windows and Mac OS X
Mac OS X Users (Mac OS X 10.5 and 10.6)
Mac OS X Users (Mac OS X 10.5, 10.6, and 10.7)
--------------
From the DeDRM_for_Mac_and_Win folder, drag the DeDRM_X.X.app.zip droplet to your Desktop. Double-click on it once to unzip it to create the DeDRM X.X.app droplet. Double-click on the droplet once and it will guide you through collecting the data it needs to remove the DRM.
@@ -33,16 +33,12 @@ From the DeDRM_for_Mac_and_Win folder, fully extract the DeDRM_WinApp_vX.X.zip.
To use it simply drag ebooks or folders onto the DeDRM_Drop_Target short-cut, and it will process the ebooks.
Linux Users
-----------
Since the state of the Linux Desktop is so jumbled and sad with so many different ways to set it up and different configuration files that depend on your version of Linux, making a DeDRM drag and drop tool for multiple versions of Linux is simply an exercise in futility. That said, you should have no problems running the gui tools (or their command line equivalents) described next.
Not a Calibre or a DeDRM User?
------------------------------
There are a number of python based tools that have graphical user interfaces to make them easy to use. To use any of these tools, you need to have Python 2.5, 2.6, or 2.7 for 32 bits installed on your machine as well as a matching PyCrypto or OpenSSL for some tools.
On Mac OS X (10.5 and 10.6) and Linux (recent versions), your systems already have the proper Python and OpenSSL installed. So nothing need be done, you can already run these tools by double-clicking on the .pyw python scripts.
On Mac OS X (10.5, 10.6 and 10.7), your systems already have the proper Python and OpenSSL installed. So nothing need be done, you can already run these tools by double-clicking on the .pyw python scripts.
Users of Mac OS X 10.3 and 10.4, need to download and install the "32-bit Mac Installer disk Image (2.7.X) for OS X 10.3 and later from http://www.python.org/download/releases/2.7.1/
@@ -54,14 +50,11 @@ The scripts are organized by type of ebook you need to remove the DRM from. Cho
"Adobe_PDF_Tools"
"Barnes_and_Noble_ePub_Tools"
"eReader_PDB_Tools"
"KindleBooks_Tools"
"KindleBooks"
"Kindle_for_Android_Patch"
by simply opening that folder.
In the "KindleBooks_Tools" folder the primary tool is in the "KindleBooks" folder.
If you are a Windows user, or a Linux platform using Wine, or Mac OS X or have trouble running the KindleBooks tools, there are two other tools provided. These are called "Kindle_4_Mac_Unswindle" and "Kindle_4_PC_Unswindle".
Look for a README inside of the relevant folder to get you started.
@@ -109,3 +102,47 @@ For PyCrypto:
Once Windows users have installed Python 2.X for 32 bits, and the matching OpenSSL OR PyCrypto pieces, they too are ready to run the scripts.
Linux Users
-----------
Unfortuantely, the Calibre Plugins do not really work well on Linux because of issues running Calibre under Wine. Native versions of Calibre can not be used with the K4MobiDeDRM plugin because the plugin will not be able to find the information it needs to remove the DRM.
Although some of the scripts do work on native Linux, others require the use of a recent version of Wine.
Here are the instructions for using KindleBooks.pyw on Linux under Wine.
1. upgrade to very recent versions of Wine; This has been tested with Wine 1.3.18 1.3.22. It may work with earlier versions but no promises.
2. Some versions of winecfg have a bug in setting the volume serial number, so create a .windows-serial file at root of drive_c to set a proper windows volume serial number (8 digit hex value for unsigned integer).
cd ~
cd .wine
cd drive_c
echo deadbeaf > .windows-serial
Replace deadbeef with whatever you want but I would stay away from the default setting of ffffffff
3. Only ***after*** setting the volume serial number properly download and install under wine K4PC version for Windows. Register it and download from your Archive one of your Kindle ebooks. Versions known to work are K4PC 1.4.1 and earlier. Later version may work but no promises.
4. Download and install under wine ActiveState Active Python 2.7 for Windows 32bit
5. Download and unzip tools_v4.5.zip
6. Then run KindleBook.pyw ***under python running on wine*** using one of the following methods:
From a Linux shell:
wine python KindleBooks.pyw
Or to get a Windows (wine) command prompt
wine cmd
python KindleBooks.pyw
Or to get a "Windows" file explorer:
winefile
and then double-click on any .pyw files to run them in the wine environment