#!/usr/bin/env python

# Cross+Channel .PD archive extraction tool
#
#   Extracts a .PD archive to a directory of files.  Handles both the
# PackOnly and the PackPlus formats.
#
# Last modified 2006-06-12, Edward Keyes, ed-at-insani-dot-org

import sys, os
from array import array
from insani import *

if len(sys.argv) != 3 :
   print 'Please give a Cross+Channel archive filename and a desired'+ \
    ' destination\ndirectory on the command line.'
   sys.exit(0)

arcfile=open(sys.argv[1],'rb')
filesize=os.stat(sys.argv[1]).st_size
dirname=sys.argv[2]
if not os.path.isdir(dirname) :  # Skip creation if directory already exists
   os.mkdir(dirname)

signature = arcfile.read(8)
if signature == 'PackOnly' :
   xorbyte = 0
elif signature == 'PackPlus' :
   xorbyte = 0x00FF
else :
   print 'Unknown file signature %s, aborting.' % \
    escape_string(signature)
   sys.exit(0)
assert_zeroes(arcfile,56,ERROR_WARNING)
numfiles = read_unsigned(arcfile,LONG_LENGTH)
print 'Extracting %d files...' % numfiles
assert (numfiles <= 16384)

entries = []
print 'Reading archive index...'
for i in xrange(numfiles) :
   filename = read_string(arcfile)
   assert_zeroes(arcfile,128-len(filename)-1)
   position = read_unsigned(arcfile,LONG_LENGTH)
   assert (position <= filesize)
   size = read_unsigned(arcfile,LONG_LENGTH)
   assert (position + size <= filesize)
   entries.append( (filename, position, size) )

assert_zeroes(arcfile,144*(16384-numfiles),ERROR_WARNING)

totalsize = arcfile.tell()
for (filename, position, size) in entries :
   print 'Extracting %s (%d bytes) from offset 0x%X' % \
    (filename, size, position)
   outfile=open(os.path.join(dirname,filename),'wb')
   arcfile.seek(position)
   data = array('B',arcfile.read(size))
   if xorbyte!=0 :
      for i in xrange(len(data)) :
         data[i] ^= xorbyte
   outfile.write(data.tostring())
   totalsize += size
   outfile.close()

if (totalsize != filesize) :
   print "Missed extracting %d bytes!" % filesize-totalsize
arcfile.close()

