aboutsummaryrefslogtreecommitdiffstats
path: root/src/epy_reader/tools/KindleUnpack/mobi_sectioner.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/epy_reader/tools/KindleUnpack/mobi_sectioner.py')
-rw-r--r--src/epy_reader/tools/KindleUnpack/mobi_sectioner.py120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/epy_reader/tools/KindleUnpack/mobi_sectioner.py b/src/epy_reader/tools/KindleUnpack/mobi_sectioner.py
new file mode 100644
index 0000000..81f62bb
--- /dev/null
+++ b/src/epy_reader/tools/KindleUnpack/mobi_sectioner.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+from .compatibility_utils import PY2, hexlify, bstr, bord, bchar
+
+import datetime
+
+if PY2:
+ range = xrange
+
+# note: struct pack, unpack, unpack_from all require bytestring format
+# data all the way up to at least python 2.7.5, python 3 okay with bytestring
+import struct
+
+from .unipath import pathof
+
+DUMP = False
+""" Set to True to dump all possible information. """
+
+class unpackException(Exception):
+ pass
+
+
+def describe(data):
+ txtans = ''
+ hexans = hexlify(data)
+ for i in data:
+ if bord(i) < 32 or bord(i) > 127:
+ txtans += '?'
+ else:
+ txtans += bchar(i).decode('latin-1')
+ return '"' + txtans + '"' + ' 0x'+ hexans
+
+def datetimefrompalmtime(palmtime):
+ if palmtime > 0x7FFFFFFF:
+ pythondatetime = datetime.datetime(year=1904,month=1,day=1)+datetime.timedelta(seconds=palmtime)
+ else:
+ pythondatetime = datetime.datetime(year=1970,month=1,day=1)+datetime.timedelta(seconds=palmtime)
+ return pythondatetime
+
+
+class Sectionizer:
+
+ def __init__(self, filename):
+ self.data = b''
+ with open(pathof(filename), 'rb') as f:
+ self.data = f.read()
+ self.palmheader = self.data[:78]
+ self.palmname = self.data[:32]
+ self.ident = self.palmheader[0x3C:0x3C+8]
+ self.num_sections, = struct.unpack_from(b'>H', self.palmheader, 76)
+ self.filelength = len(self.data)
+ sectionsdata = struct.unpack_from(bstr('>%dL' % (self.num_sections*2)), self.data, 78) + (self.filelength, 0)
+ self.sectionoffsets = sectionsdata[::2]
+ self.sectionattributes = sectionsdata[1::2]
+ self.sectiondescriptions = ["" for x in range(self.num_sections+1)]
+ self.sectiondescriptions[-1] = "File Length Only"
+ return
+
+ def dumpsectionsinfo(self):
+ print("Section Offset Length UID Attribs Description")
+ for i in range(self.num_sections):
+ print("%3d %3X 0x%07X 0x%05X % 8d % 7d %s" % (i,i, self.sectionoffsets[i], self.sectionoffsets[
+ i+1] - self.sectionoffsets[i], self.sectionattributes[i]&0xFFFFFF, (self.sectionattributes[i]>>24)&0xFF, self.sectiondescriptions[i]))
+ print("%3d %3X 0x%07X %s" %
+ (self.num_sections,self.num_sections, self.sectionoffsets[self.num_sections], self.sectiondescriptions[self.num_sections]))
+
+ def setsectiondescription(self, section, description):
+ if section < len(self.sectiondescriptions):
+ self.sectiondescriptions[section] = description
+ else:
+ print("Section out of range: %d, description %s" % (section,description))
+
+ def dumppalmheader(self):
+ print("Palm Database Header")
+ print("Database name: " + repr(self.palmheader[:32]))
+ dbattributes, = struct.unpack_from(b'>H', self.palmheader, 32)
+ print("Bitfield attributes: 0x%0X" % dbattributes,)
+ if dbattributes != 0:
+ print(" (",)
+ if (dbattributes & 2):
+ print("Read-only; ",)
+ if (dbattributes & 4):
+ print("Dirty AppInfoArea; ",)
+ if (dbattributes & 8):
+ print("Needs to be backed up; ",)
+ if (dbattributes & 16):
+ print("OK to install over newer; ",)
+ if (dbattributes & 32):
+ print("Reset after installation; ",)
+ if (dbattributes & 64):
+ print("No copying by PalmPilot beaming; ",)
+ print(")")
+ else:
+ print("")
+ print("File version: %d" % struct.unpack_from(b'>H', self.palmheader, 34)[0])
+ dbcreation, = struct.unpack_from(b'>L', self.palmheader, 36)
+ print("Creation Date: " + str(datetimefrompalmtime(dbcreation))+ (" (0x%0X)" % dbcreation))
+ dbmodification, = struct.unpack_from(b'>L', self.palmheader, 40)
+ print("Modification Date: " + str(datetimefrompalmtime(dbmodification))+ (" (0x%0X)" % dbmodification))
+ dbbackup, = struct.unpack_from(b'>L', self.palmheader, 44)
+ if dbbackup != 0:
+ print("Backup Date: " + str(datetimefrompalmtime(dbbackup))+ (" (0x%0X)" % dbbackup))
+ print("Modification No.: %d" % struct.unpack_from(b'>L', self.palmheader, 48)[0])
+ print("App Info offset: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 52)[0])
+ print("Sort Info offset: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 56)[0])
+ print("Type/Creator: %s/%s" % (repr(self.palmheader[60:64]), repr(self.palmheader[64:68])))
+ print("Unique seed: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 68)[0])
+ expectedzero, = struct.unpack_from(b'>L', self.palmheader, 72)
+ if expectedzero != 0:
+ print("Should be zero but isn't: %d" % struct.unpack_from(b'>L', self.palmheader, 72)[0])
+ print("Number of sections: %d" % struct.unpack_from(b'>H', self.palmheader, 76)[0])
+ return
+
+ def loadSection(self, section):
+ before, after = self.sectionoffsets[section:section+2]
+ return self.data[before:after]