#!/Library/Frameworks/Python.framework/Versions/2.4/Resources/Python.app/Contents/MacOS/python

from socket import ntohl,ntohs
from datetime import datetime,timedelta

class Byte:
  "Holds a Newton book Byte"
  length=1
  numOfBits=8
  def __init__(self,value=0):
    self.data=value
    self.rawData=''
    count=0
    while count<self.__class__.length:
      self.rawData='%s%s'%(chr(value&0xFF),self.rawData)
      value>>=Byte.numOfBits
      count+=1
  def load(self,loc):
    count=0
    self.data=0L
    self.rawData=InBuf[loc:loc+self.length]
    while count<self.__class__.length:
      self.data<<=Byte.numOfBits
      self.data+=ord(InBuf[loc])
      count+=1
      loc+=1
    # self.data=ntohl(self.data)
    return loc,self.__class__(self.data)
  def __len__(self):
    return self.length
  def __nonzero__(self):
    return self.data!=0
  def __str__(self):
    return str(self.data)
  def __int__(self):
    return int(self.data)
  def __long__(self):
    return long(self.data)
  __abs__=__long__
  def __repr__(self):
    return str(int(self.data))
  def __hex__(self):
    return hex(self.data)
  def __oct__(self):
    return oct(self.data)
  def hollerith(self):
    return self.rawData
  def __add__(self,other):
    return self.__class__(self.data+int(other))
  __radd__=__iadd__=__add__
  def __mul__(self,other):
    return self.__class__(self.data*int(other))
  __rmul__=__imul__=__mul__
  def __sub__(self,other):
    return self.__class__(self.data-int(other))
  __isub__=__sub__
  def __div__(self,other):
    return self.__class__(self.data/int(other))
  __idiv__=__div__
  def __eq__(self,other):
    return int(self)==int(other)
  def __ne__(self,other):
    return int(self)!=int(other)
  def __gt__(self,other):
    return int(self)>int(other)
  def __ge__(self,other):
    return int(self)>=int(other)
  def __lt__(self,other):
    return int(self)<int(other)
  def __le__(self,other):
    return int(self)<=int(other)
  def __getitem__(self,index):
    return bool(self.data>>(index-1))
  def __and__(self,other):
    return self.__class__(self.data&int(other))
  __rand__=__iand__=__and__
  def __or__(self,other):
    return self.__class__(self.data|int(other))
  __ror__=__ior__=__or__
  def __xor__(self,other):
    return self.__class__(self.data^int(other))
  __rxor__=__ixor__=__xor__
  def __invert__(self):
    return self.__class__(~self.data)
  def __rshift__(self,amount):
    return self.__class__(self.data>>int(amount))
  __rrshift__=__irshift__=__rshift__
  def __lshift__(self,amount):
    return self.__class__(self.data<<int(amount))
  __rlshift__=__ilshift__=__lshift__

class UShort(Byte):
  "Holds a Newton book UShort"
  length=2
  numOfBits=length*Byte.numOfBits

class ULong(Byte):
  "Holds a Newton book ULong"
  length=4
  numOfBits=length*Byte.numOfBits

class Real(Byte):
  "Holds a Newton book Real"
  length=8
  numOfBits=length*Byte.numOfBits
  def __init__(self,value=0):
    Byte.__init__(self,value)
    self.negative=bool(self.data>>63)
    self.exponent=(self.data>>52)&0x7FF
    self.mantissa=self.data&0xFFFFFFFFFFFFF
    self.frac=float('.%d'%self.mantissa)
  def load(self,loc):
    Byte.load(loc)
    self.negative=bool(self.data>>63)
    self.exponent=(self.data>>52)&0x7FF
    self.mantissa=self.data&0xFFFFFFFFFFFFF
    self.frac=float('.%d'%self.mantissa)
  def isNaN(self):
    return self.exponent==0x7FF and self.mantissa!=0
  def isInfinite(self):
    return self.exponent==0x7FF and self.mantissa==0
  def isNegative(self):
    return self.negative
  def __float__(self):
    if self.exponent>0:
      value=(1+self.frac)*2**(self.exponent-1023)
    else:
      value=self.frac*2**(-1022)
    if self.negative:
      value*=-1
    return value

class Date(ULong):
  "Holds a Newton book Date"
  epoch=datetime(1904,1,4)
  def __init__(self,value=0):
    # data should be the number of seconds since midnight, Jan. 4, 1904
    ULong.__init__(self,value)
    numOfSeconds=timedelta(seconds=long(self.data))
    self.dateObj=Date.epoch+numOfSeconds
  def __str__(self):
    return str(self.dateObj)
  __repr__=__str__
  def fix(self):
    # Cleans up after a Newton Press bug that tracks minutes
    # although it's supposed to track seconds
    self.data*=60
    numOfSeconds=timedelta(seconds=long(self.data))
    self.dateObj=Date.epoch+numOfSeconds

class InfoRef:
  "Holds a Newton book InfoRef"
  length=2*UShort.length
  def __init__(self,offset=0,length=0):
    self.offset=offset
    self.length=length
  def load(self,loc):
    loc,self.offset=UShort().load(loc)
    loc,self.length=UShort().load(loc)
    return loc,self.__class__(self.offset,self.length)
  def populateFromBuffer(self,buf,uni=True):
    self.data=buf[int(self.offset):int(self.offset)+int(self.length)]
    if uni:
      self.data=unicode(self.data,'utf-16')
  def __len__(self):
    return length
  def __nonzero__(self):
    return self.length!=0
  def __str__(self):
    return self.data
  __repr__=__str__
  def __eq__(self,other):
    if isinstance(other,InfoRef):
      return (int(self.offset)==int(other.offset)) and (int(self.length)==int(other.length))
    else:
      return self.data==other
  def __ne__(self,other):
    if isinstance(other,InfoRef):
      return (int(self.offset)!=int(other.offset)) or (int(self.length)!=int(other.length))
    else:
      return self.data!=other
  def __gt__(self,other):
    return int(self.offset)>int(other.offset)
  def __ge__(self,other):
    return int(self.offset)>=int(other.offset)
  def __lt__(self,other):
    return int(self.offset)<int(other.offset)
  def __le__(self,other):
    return int(self.offset)<=int(other.offset)

class FixedHeader:
  "Holds a Newton book Fixed Header"
  signatureLen=Byte.length*8
  length=signatureLen+7*ULong.length+2*InfoRef.length+2*Date.length
  def __init__(self):
    pass
  def load(self,loc=0):
    self.valid=self.book=True
    self.signature=InBuf[loc:loc+FixedHeader.signatureLen]
    self.formatVersion=int(self.signature[-1])
    loc+=self.signatureLen
    loc,self.reserved1=ULong().load(loc)
    loc,self.flags=ULong().load(loc)
    loc,self.version=ULong().load(loc)
    loc,self.copyright=InfoRef().load(loc)
    loc,self.name=InfoRef().load(loc)
    loc,self.size=ULong().load(loc)
    loc,self.creationDate=Date().load(loc)
    loc,self.modificationDate=Date().load(loc)
    loc,self.reserved3=ULong().load(loc)
    loc,self.directorySize=ULong().load(loc)
    loc,self.numParts=ULong().load(loc)
    if self.signature[:-1]!='package' or \
       self.directorySize>self.size or \
       (self.formatVersion<2 and self.reserved3!=0) or \
       (self.formatVersion==0 and self.flags&0x04000000) or \
       not (self.reserved1==0 or self.reserved1.hollerith()=='xxxx'):
      # Must start with proper signature; reserved3 should generally be 0;
      # directorySize has to be smaller than overall size;
      # relocation blocks can't exist in format version 0;
      # reserved1 should be 'xxxx' for most modern books, but 0 is also valid
      self.valid=False
    if self.flags&0x80000000:
      # kAutoRemoveFlag is not valid for books
      self.book=False
    return loc
  def __len__(self):
    return length
  def __nonzero__(self):
    return self.signature[:-1]!='package'
  def fixDates(self):
    self.creationDate.fix()
    self.modificationDate.fix()
  def isValid(self):
    return self.valid
  def isBook(self):
    return self.book
  def __str__(self):
    return self.signature+'\n'+\
           self.reserved1.hollerith()+'\n'+\
           oct(self.flags)+'\n'+\
           str(self.version)+'\n'+\
           str(self.copyright)+'\n'+\
           str(self.name)+'\n'+\
           str(self.size)+'\n'+\
           str(self.creationDate)+'\n'+\
           str(self.modificationDate)+'\n'+\
           str(self.reserved3)+'\n'+\
           str(self.directorySize)+'\n'+\
           str(self.numParts)+'\n'

class PartEntry:
  "Holds a Newton book Part Entry"
  length=7*ULong.length+InfoRef.length
  def __init__(self):
    pass
  def load(self,loc,version=0):
    self.valid=self.book=True
    loc,self.offset=ULong().load(loc)
    loc,self.size=ULong().load(loc)
    loc,self.size2=ULong().load(loc)
    loc,self.partType=ULong().load(loc)
    loc,self.reserved1=ULong().load(loc)
    loc,self.flags=ULong().load(loc)
    loc,self.info=InfoRef().load(loc)
    loc,self.reserved2=ULong().load(loc)
    if self.size!=self.size2 or \
       (version<2 and (self.reserved1!=0 or self.reserved2!=0)):
      # the two sizes must match and the two reserveds should be generally zero
      self.valid=False
    if self.partType.hollerith()!='book' or self.flags&0x00000130 or \
       not self.flags&0x00000080:
      # kAutoLoadFlag, kAutoRemoveFlag, kAutoCopyFlag are all invalid for books,
      # but kNotifyFlag is required for them
      self.book=False
    return loc
  def __len__(self):
    return length
  def __nonzero__(self):
    return self.size!=0
  def isValid(self):
    return self.valid
  def isBook(self):
    return self.book
  def __str__(self):
    return str(self.offset)+'\n'+\
           str(self.size)+'\n'+\
           str(self.size2)+'\n'+\
           self.partType.hollerith()+'\n'+\
           str(self.reserved1)+'\n'+\
           oct(self.flags)+'\n'+\
           str(self.info)+'\n'+\
           str(self.reserved2)+'\n'

class PackageDirectory:
  "Holds a Newton book Package Directory (Fixed Header, Part Entries, Variable-length Data Area)"
  def __init__(self):
    pass
  def load(self,loc=0):
    self.fixedHeader=FixedHeader()
    loc=self.fixedHeader.load(loc)
    self.valid=self.fixedHeader.isValid()
    self.length=int(self.fixedHeader.directorySize)
    self.numOfParts=int(self.fixedHeader.numParts)
    partNum=0
    self.partEntries=[]
    variableLengthData=InBuf[int(FixedHeader.length+self.numOfParts*PartEntry.length):self.length]
    self.fixedHeader.copyright.populateFromBuffer(variableLengthData)
    self.fixedHeader.name.populateFromBuffer(variableLengthData)
    variableLengthObjects=[]
    variableLengthObjects.append(self.fixedHeader.copyright)
    variableLengthObjects.append(self.fixedHeader.name)
    self.bookParts=[]
    while partNum<self.numOfParts:
      partEntry=PartEntry()
      loc=partEntry.load(loc,self.fixedHeader.formatVersion)
      partEntry.info.populateFromBuffer(variableLengthData,False)
      variableLengthObjects.append(partEntry.info)
      self.valid=self.valid and partEntry.isValid()
      if partEntry.isBook() and partEntry.info=='book' and self.fixedHeader.isBook():
        self.bookParts.append(partNum)
      self.partEntries.append(partEntry)
      partNum+=1
    # The following process picks out gaps in the variable length header area.
    # The largest such gap (note that there's usually only one anyway, at the
    # very end) is used to hold the creating software credits.
    variableLengthObjects.sort()
    gaps=[]
    leaveOffPos=0
    for vlo in variableLengthObjects:
      if vlo.offset>leaveOffPos:
        self.gaps.append(InfoRef(leaveOffPos,vlo.offset-leaveOffPos))
      leaveOffPos=vlo.offset+vlo.length
    if leaveOffPos<self.fixedHeader.directorySize:
      gaps.append(InfoRef(leaveOffPos,self.fixedHeader.directorySize-leaveOffPos))
    self.softwareCredits=gaps[0]
    for gap in gaps[1:]:
      if gap.length>self.softwareCredits.length:
        self.softwareCredits=gap
    # Software credits are stuffed in the extra space in the variable length header
    # area, but unlike some of the other entites there they are encoded in simple
    # ASCII rather than Unicode.  Note that if the creator is Newton Press the dates
    # will have to be fixed...
    self.softwareCredits.populateFromBuffer(variableLengthData,False)
    if str(self.softwareCredits)[:24]=='Built with Newton Press\0':
      self.fixedHeader.fixDates()
    return loc
  def __len__(self):
    return self.length
  def __nonzero__(self):
    return self.numOfParts!=0
  def isValid(self):
    return self.valid
  def formatVersion(self):
    return self.fixedHeader.formatVersion
  def getBookParts(self):
    # Each of the items in this list is an independent, valid book
    return self.bookParts
  def hasRelocationInfo(self):
    return bool(self.fixedHeader.flags&0x04000000)
  def __str__(self):
    retVal=str(self.fixedHeader)+'\n'
    for partEntry in self.bookParts:
      retVal+=str(partEntry)+'\n'
    return retVal

class RelocationSet:
  "Holds a Newton book Relocation Set"
  def __init__(self):
    pass
  def load(self,loc):
    loc,self.pageNumber=UShort().load(loc)
    loc,self.offsetCount=UShort().load(loc)
    self.offsets=InBuf[loc:loc+self.offsetCount]
    self.length=2*UShort.length+self.offsets*Byte.length
    return loc
  def __len__(self):
    return self.length
  def __str__(self):
    return str(self.pageNumber)+'\n'+\
           str(self.offsetCount)+'\n'+\
           str(self.offsets)+'\n'

class RelocationInfo:
  "Holds a Newton book Relocation Information section"
  def __init__(self):
    pass
  def load(self,loc,version=0):
    self.valid=True
    loc,self.reserved=ULong().load(loc)
    loc,self.relocationSize=ULong().load(loc)
    loc,self.pageSize=ULong().load(loc)
    loc,self.numEntries=ULong().load(loc)
    loc,self.baseAddress=ULong().load(loc)
    if self.pageSize!=1024 or (version<2 and self.reserved!=0):
      # pageSize must always be 1024 and reserved should generally be 0
      self.valid=False
    self.length=5*ULong.length
    relocationSetNum=0
    self.relocationSets=[]
    while relocationSetNum<self.numEntries:
      relocationSet=RelocationSet()
      loc=relocationSet.load(loc)
      self.relocationSets.append(relocationSet)
      relocationSetNum+=1
      self.length+=len(relocationSet)
    return loc
  def __len__(self):
    return self.length
  def __nonzero__(self):
    return self.numEntries!=0
  def isValid(self):
    return self.valid
  def __str__(self):
    retVal=str(self.reserved)+'\n'+\
           str(self.relocationSize)+'\n'+\
           str(self.pageSize)+'\n'+\
           str(self.numEntries)+'\n'+\
           str(self.baseAddress)+'\n'
    for relocationSet in self.relocationSets:
      retVal+=str(relocationSet)+'\n'
    return retVal
    

class Ref(ULong):
  "Holds a Newton book Ref; it can be a pointer, integer, character, or special code value."
  def __init__(self,value=0):
    ULong.__init__(self,value)
    self.typeBits=self.data&0x03
  def load(self,loc):
    loc,retVal=ULong.load(self,loc)
    self.typeBits=self.data&0x03
    return loc,retVal
  def __len__(self):
    return Ref.length
  def getData(self):
    retVal=self.data
    if self.refType()=='Integer':
      negative=bool(retVal>>31)
      if negative:
        retVal=~retVal
      retVal>>=2
      if negative:
        retVal+=1
        retVal*=-1
      return retVal
    elif self.refType()=='Pointer':
      return retVal-1
    elif self.refType()=='Character':
      return unichr(retVal>>4)
    elif self.refType()=='Magic Pointer':
      return (retVal>>14,(retVal>>2)&0x3FFF)
    # Special codes
    elif self.data==0x55552:
      return 'Symbol'
    elif self.data==0x1A:
      return 'True'
    elif self.data==0x02:
      return 'Nil'
    else:
      return 'Special'
  def checkPtr(self):
    if self.typeBits==1:
      junk,ptrDest=ULong().load(self.getData())
      if ptrDest&0x03==0:
        return 'Binary'
      elif ptrDest&0x03==1:
        return 'Array'
      elif ptrDest&0x03==3:
        return 'Frame'
      else:
        return 'Invalid'
    else:
      return 'Literal'
  def checkBinary(self):
    if self.typeBits==1 and self.checkPtr()=='Binary':
      junk,classRef=ULong().load(self.getData()+2*ULong.length)
      if classRef==0x55552:
        return 'Symbol'
      else:
        return 'Binary'
    else:
      return 'Not Binary'
  def dereference(self,indent=0):
    if self.checkPtr()=='Binary':
      retVal=BinaryObject(self.getData(),indent)
    elif self.checkPtr()=='Array':
      retVal=ArrayObject(self.getData(),indent)
    elif self.checkPtr()=='Frame':
      retVal=FrameObject(self.getData(),indent)
    else:
      retVal=self.getData()
    return retVal
  def refType(self):
    if self.typeBits==0:
      return 'Integer'
    elif self.typeBits==1:
      return 'Pointer'
    elif self.data&0x0F==0x0A:
      return 'Character'
    elif self.typeBits==2:
      return 'Special'
    else:
      return 'Magic Pointer'
  def __str__(self):
    return str(self.getData())
  def __unicode__(self):
    if self.refType=='Character':
      return self.getData()
    else:
      return str(self.getData())

class Part:
  "Holds the recursive part structure"
  length=4*ULong.length
  def __init__(self,loc):
    self.valid=True
    loc,self.arraySize16=ULong().load(loc)
    loc,self.alignment=ULong().load(loc)
    loc,self.foClass=Ref().load(loc)
    loc,self.partFramePtr=Ref().load(loc)
    if (self.alignment!=1 and self.alignment!=0) or \
       self.arraySize16!=0x0841 or self.foClass.getData()!='Nil':
      # alignment can only be 0 or 1; first word must be 0x1041;
      # must have special Nil data class
      self.valid=False
    if self.partFramePtr.checkPtr()!='Frame':
      self.valid=False
    else:
      if Debug:
        print "Loading part frame..."
      self.partFrame=FrameObject(self.partFramePtr.getData())
  def getIcon(self):
    if 'icon' in self.partFrame['book'].mapping:
      icon=self.partFrame['book']['icon']
      iconWidth=icon['bounds']['right']-icon['bounds']['left']
      iconHeight=icon['bounds']['bottom']-icon['bounds']['top']
      iconXBM='#define icon_width %d\n'%iconWidth
      iconXBM+='#define icon_height %d\n'%iconHeight
      iconXBM+='static unsigned char icon_bits[] = {'
      # for loopVar in range(16,icon['bits'].dataSize):
      for loopVar in range(0,icon['bits'].dataSize):
        if loopVar!=0:
          iconXBM+=','
        if loopVar%16==0:
          iconXBM+='\n';
        byte=ord(icon['bits'].data[loopVar])
        workByte=byte
        reversedByte=0
        for bit in range(0,8):
          reversedByte<<=1
          if workByte&0x01:
            reversedByte+=1
          workByte>>=1
        iconXBM+='0x%02X'%(reversedByte&0xFF)
      iconXBM+='\n};'
      return iconXBM
    else:
      return 'No icon available.'
  def isValid(self):
    return self.valid
  def __len__(self):
    return FirstObject.length
  def __str__(self):
    return 'Array: 0x%X %s 0x%X\n%s\n'%(self.arraySize16,
                                        self.alignment,
                                        self.foClass,
                                        [self.partFrame])

class BinaryObject:
  "Holds a binary object from the part"
  def __init__(self,loc,indent=0):
    indentFmt='%%%ds'%(2*indent)
    self.valid=True
    loc,self.sizeAndFlags=ULong().load(loc)
    loc,self.zeroMark=ULong().load(loc)
    loc,self.classRef=Ref().load(loc)
    if self.zeroMark!=0 or self.sizeAndFlags&0xFF!=0x40:
      self.valid=False
    self.size=int(self.sizeAndFlags>>8)
    self.dataSize=self.size-3*ULong.length
    if Debug:
      print indentFmt%' '+'Binary Object 0%o %d'%(loc,self.dataSize)
    self.rawData=InBuf[loc:loc+self.dataSize]
    self.data=self.rawData
    self.classObj=self.classRef.dereference(indent+1)
    self.symbolHash=0
    if str(self.classRef)=='Symbol':
      loc,self.symbolHash=ULong().load(loc)
      self.data=InBuf[loc:loc+self.dataSize-1-ULong.length]
      if self.getSymbolHash()!=self.symbolHash:
        self.valid=False
      if Debug:
        print indentFmt%' '+"Class ('%s' [0x%X])"%(self.data,long(self.symbolHash))
    elif self.classRef.checkBinary()=='Symbol':
      if self.classObj.data=='String':
        self.data=unicode(self.rawData[:-2],'utf-16')
        if Debug:
          print indentFmt%' '+'Class (%s: %s)'%(self.classRef.refType(),str(self.classRef))+u'(String: "%s")'%self.data
      elif self.classObj.data=='Real':
        self.data=Real().load(loc)  
        if Debug:
          print indentFmt%' '+'Class (%s: %s) (Real)'%(self.classRef.refType(),str(self.classRef))
      else:
        if Debug:
          print indentFmt%' '+'Class (%s: %s)'%(self.classRef.refType(),str(self.classRef))
    else:
      if Debug:
        print indentFmt%' '+'Class (%s: %s)'%(self.classRef.refType(),str(self.classRef))
  def getSymbolHash(self):
    result=0
    charNum=0
    for wrkChar in self.data:
      if wrkChar>='a' and wrkChar<='z':
        result+=ord(wrkChar)-32
      else:
        result+=ord(wrkChar)
    result*=2654435769
    result&=0xFFFFFFFF
    return result
  def isValid(self):
    return self.valid
  def __len__(self):
    return self.size
  def __str__(self):
    if str(self.classRef)=='Symbol':
      return 'Symbol: 0x%X 0x%X 0x%X %d %d\n%s\n'%(self.sizeAndFlags,
                                                   self.zeroMark,
                                                   self.classRef,
                                                   self.size,
                                                   self.dataSize,
                                                   self.data)
    else:      
      return 'Binary: 0x%X 0x%X 0x%X %d %d\n'%(self.sizeAndFlags,
                                               self.zeroMark,
                                               self.classRef,
                                               self.size,
                                               self.dataSize)
  def __unicode__(self):
    if str(self.classRef)=='Symbol':
      return 'Symbol: 0x%X 0x%X 0x%X %d %d\n%s\n'%(self.sizeAndFlags,
                                                   self.zeroMark,
                                                   self.classRef,
                                                   self.size,
                                                   self.dataSize,
                                                   self.data)
    else:      
      return 'Binary: 0x%X 0x%X 0x%X %d %d\n%s\n'%(self.sizeAndFlags,
                                                   self.zeroMark,
                                                   self.classRef,
                                                   self.size,
                                                   self.dataSize,
                                                   self.data)

class ArrayObject:
  "Holds an array object from the part"
  def __init__(self,loc,indent=0,isMap=False):
    indentFmt='%%%ds'%(2*indent)
    if Debug:
      print indentFmt%' '+'Array Object 0%o'%loc
    self.valid=True
    self.supermap=False
    slotNum=0
    self.slot=[]
    loc,self.sizeAndFlags=ULong().load(loc)
    loc,self.zeroMark=ULong().load(loc)
    loc,self.classRef=Ref().load(loc)
    if self.zeroMark!=0 or self.sizeAndFlags&0xFF!=0x41:
      self.valid=False
    self.size=self.sizeAndFlags>>8
    if Debug:
      print indentFmt%' '+'Class (%s: %s)'%(self.classRef.refType(),str(self.classRef))
    self.classObj=self.classRef.dereference(indent+1)
    self.actualNumOfSlots=self.numOfSlots=int((self.size-3*ULong.length)/ULong.length)
    while slotNum<self.actualNumOfSlots:
      loc,slotRef=Ref().load(loc)
      if isMap and slotNum==0 and slotRef.getData()!='Nil':
        # We have a supermap
        if Debug:
          print indentFmt%' '+'Supermap!'
        self.supermap=True
        self.slot.append(ArrayObject(slotRef.getData(),indent+1,True))
      else:
        self.slot.append(slotRef.dereference(indent+1))
      if Debug:
        print indentFmt%' '+"Slot %s 0%o (%s: %s)"%(str(slotNum),loc-ULong.length,slotRef.refType(),str(slotRef))
      if isMap and slotRef.refType=='Pointer' and slotRef.checkBinary()!='Symbol':
        self.valid=False
      slotNum+=1
    if self.supermap:
      self.numOfSlots+=self.slot[0].numOfSlots-1
      self.slot=self.slot[0].slot+self.slot[1:]
  def isValid(self):
    return self.valid
  def __getitem__(self,index):
    return self.slot[index]
  def __len__(self):
    return self.size
  def __str__(self):
    return 'Array: 0x%X 0x%X 0x%X %d %d\n%s\n'%(self.sizeAndFlags,
                                                self.zeroMark,
                                                self.classRef,
                                                self.size,
                                                self.numOfSlots,
                                                self.slot)

class FrameObject:
  "Holds a frame object from the part"
  def __init__(self,loc,indent=0):
    indentFmt='%%%ds'%(2*indent)
    if Debug:
      print indentFmt%' '+'Frame Object 0%o'%loc
    self.valid=True
    loc,self.sizeAndFlags=ULong().load(loc)
    loc,self.zeroMark=ULong().load(loc)
    loc,self.mapRef=Ref().load(loc)
    if self.zeroMark!=0 or self.sizeAndFlags&0xFF!=0x43 or self.mapRef.checkPtr()!='Array':
      self.valid=False
    self.size=self.sizeAndFlags>>8
    if Debug:
      print indentFmt%' '+'Map (%s: %s)'%(self.mapRef.refType(),str(self.mapRef))
    self.mapObj=ArrayObject(self.mapRef.getData(),indent+1,True)
    self.numOfSlots=int((self.size-3*ULong.length)/ULong.length)
    if self.numOfSlots!=self.mapObj.numOfSlots-1:
      self.valid=False
    slotNum=0
    self.mapping={}
    while slotNum<self.numOfSlots:
      loc,slotRef=Ref().load(loc)
      if Debug:
        print indentFmt%' '+"Slot %s 0%o (%s: %s)"%(str(slotNum),loc-ULong.length,slotRef.refType(),str(slotRef))
      self.mapping[self.mapObj.slot[slotNum+1].data]=slotRef.dereference(indent+1)
      slotNum+=1
  def isValid(self):
    return self.valid
  def __getitem__(self,index):
    return self.mapping[index]
  def __len__(self):
    return self.size
  def __str__(self):
    return 'Frame: 0x%X 0x%X 0x%X %d %d\n%s\n'%(self.sizeAndFlags,
                                                self.zeroMark,
                                                self.mapRef,
                                                self.size,
                                                self.numOfSlots,
                                                self.mapping.keys())

InBuf=open('/Users/eric/Desktop/NewtonBooks/welcome0.3.pkg','rb').read()      # A buffer with readinto() would be better.
#InBuf=open('/Users/eric/Saugus.net/Customers/NewtonsLibrary/NewtonBooks/class/A Warning.pkg','rb').read()      # A buffer with readinto() would be better.
Debug=1
loc=0
packageDirectory=PackageDirectory()
loc=packageDirectory.load(loc)
if not packageDirectory.isValid():
  print "There's a problem"
if packageDirectory.hasRelocationInfo():
  print "Relocation Information..."
  relocationInfo=RelocationInfo()
  loc=relocationInfo.load(loc,packageDirectory.formatVersion())
for partNum in packageDirectory.getBookParts():
  # Process the actual contents of the book parts
  if Debug:
    print "Part %d..."%partNum
  part=Part(int(packageDirectory.fixedHeader.directorySize+\
                packageDirectory.partEntries[partNum].offset))
  
