#!/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 from loguru import logger 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] # CG struct.unpack_from(fmt, buffer, offset=0) (self.num_sections,) = struct.unpack_from(b">H", self.palmheader, 76) self.filelength = len(self.data) ## CGDBG ??? ## sectionsdata (9680, 0, 18618, 2, 22275, 4, 25504, 6, 28607, 8,... sectionsdata = struct.unpack_from( bstr(">%dL" % (self.num_sections * 2)), self.data, 78 ) + (self.filelength, 0) ## 所有section的offset和长度 # sectionsoffset (9680, 18618, 22275, 25504, 28607, ... self.sectionoffsets = sectionsdata[::2] # ectionattributes (0, 2, 4, 6, 8, ... self.sectionattributes = sectionsdata[1::2] self.sectiondescriptions = ["" for x in range(self.num_sections + 1)] self.sectiondescriptions[-1] = "File Length Only" # CGDBG upack_from 返回什么?tuple (,) print( 'sectionsdata {} {}'.format(sectionsdata, bstr(">%dL" % (self.num_sections * 2)))) print( 'sectionsoffset {} \n sectionattributes {}'.format( self.sectionoffsets, self.sectionattributes )) print( 'sectionsdescriptions {} '.format( self.sectiondescriptions)) print( bstr(">%dL" % (self.num_sections * 2) ) ) print( struct.unpack_from(bstr(">%dL" % (self.num_sections * 2)) , self.data, 78) ) print( (self.filelength, 0) ) return # sections information def dumpsectionsinfo(self): logger.debug("Section Offset Length UID Attribs Description") for i in range(self.num_sections): ''' logger.debug( "{} {} {} {} {} {} {}\n".format( 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 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]