#!/usr/bin/env python # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab import datetime from loguru import logger from .compatibility_utils import PY2, bchar, bord, bstr, hexlify 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): logger.debug("Section Offset Length UID Attribs Description") for i in range(self.num_sections): logger.debug( "%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], ) ) logger.debug( "%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: logger.debug("Section out of range: %d, description %s" % (section, description)) def dumppalmheader(self): logger.debug("Palm Database Header") logger.debug("Database name: " + repr(self.palmheader[:32])) (dbattributes,) = struct.unpack_from(b">H", self.palmheader, 32) logger.debug( "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("") logger.debug("File version: %d" % struct.unpack_from(b">H", self.palmheader, 34)[0]) (dbcreation,) = struct.unpack_from(b">L", self.palmheader, 36) logger.debug("Creation Date: " + str(datetimefrompalmtime(dbcreation)) + (" (0x%0X)" % dbcreation)) (dbmodification,) = struct.unpack_from(b">L", self.palmheader, 40) logger.debug("Modification Date: " + str(datetimefrompalmtime(dbmodification)) + (" (0x%0X)" % dbmodification)) (dbbackup,) = struct.unpack_from(b">L", self.palmheader, 44) if dbbackup != 0: logger.debug("Backup Date: " + str(datetimefrompalmtime(dbbackup)) + (" (0x%0X)" % dbbackup)) logger.debug("Modification No.: %d" % struct.unpack_from(b">L", self.palmheader, 48)[0]) logger.debug("App Info offset: 0x%0X" % struct.unpack_from(b">L", self.palmheader, 52)[0]) logger.debug("Sort Info offset: 0x%0X" % struct.unpack_from(b">L", self.palmheader, 56)[0]) logger.debug("Type/Creator: %s/%s" % (repr(self.palmheader[60:64]), repr(self.palmheader[64:68]))) logger.debug("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: logger.debug("Should be zero but isn't: %d" % struct.unpack_from(b">L", self.palmheader, 72)[0]) logger.debug("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]