<?php

/**
 * $Id: pilotdoc.inc,v 1.1 2000/11/09 05:08:58 lord Exp $
 *
 * This class contains methods for working with pilot PDB and DOC files
 * from php.
 * Currently it produces uncompressed file.
 *
 * Vadim Zaliva <lord@crocodile.org>
 */

class IOObject
{
    function write()
    {
        print "ERROR! write() not implemented!";
    }
    
    function read()
    {
        print "ERROR! write() not implemented!";
    }
    
    function writeByte($b)
    {
        printf("%c", $b+0);
    }
}

class BoundString extends IOObject
{
    var $value;
    var $length;
    
    function BoundString($length=0, $val="")
    {
        $this->value  = $val;
        $this->length = $length;
    }
        
    function write()
    {
        $l=strlen($this->value);
        if($l >= $this->length)
        {
            print substr($this->value,0,$this->length);
        } else 
        {
            print $this->value;
            // pad with spaces
            for($i=0;$i<($this->length-$l);$i++)
            {
                print " ";
            }
        }
    }
}

class BoundNullTerminatedString extends BoundString
{
    // length includes space for terminator.
    function BoundNullTerminatedString($length=1, $val="")
    {
        $this->BoundString($length, $val);
    }
    
    function write()
    {
        $l=strlen($this->value);
        if($l >= ($this->length-1))
        {
            print substr($this->value,0,$this->length-1);
            $this->writeByte(0);
        } else 
        {
            print $this->value;
            $this->writeByte(0);
            // pad with spaces
            for($i=0;$i<($this->length-$l-1);$i++)
            {
                print " ";
            }
        }
    }
}

class Byte extends IOObject
{
    var $value;
        
    function Byte($val=0)
    {
        $this->value=$val;
    }

    function write()
    {
        $this->writeByte($this->value  & 0xff);
    }

}

class Word extends IOObject
{
    var $value;

    function Word($val=0)
    {
        $this->value=$val;
    }

    function write()
    {
        $this->writeByte(($this->value>>8) & 0xff);
        $this->writeByte($this->value      & 0xff);
    }
}

class DWord extends IOObject
{
    var $value;
        
    function DWord($val=0)
    {
        $this->value=$val;
    }

    function write()
    {
        $this->writeByte(($this->value>>24) & 0xff);
        $this->writeByte(($this->value>>16) & 0xff);
        $this->writeByte(($this->value>>8)  & 0xff);
        $this->writeByte($this->value       & 0xff);
    }
}

// 3-byte int
class TByte extends IOObject
{
    var $value;
        
    function TByte($val=0)
    {
        $this->value=$val;
    }

    function write()
    {
        $this->writeByte(($this->value>>16) & 0xff);
        $this->writeByte(($this->value>>8)  & 0xff);
        $this->writeByte($this->value       & 0xff);
    }
}

class PDBRecHeader extends IOObject
{
    var $offset     ;
    var $attributes ;
    var $uniqueID   ;

    function PDBRecHeader()
    {
        $this->offset     = new DWord()        ;
        $this->attributes = new Byte (0x40)    ; // dirty
        $this->uniqueID   = new TByte()        ;
    }
        
    function write()
    {
        $this->offset     ->write();
        $this->attributes ->write();
        $this->uniqueID   ->write();
    }

    // TODO: attribute access methods.
    //	 int delete    : 1;
    //	 int dirty     : 1;
    //	 int busy      : 1;
    //	 int secret    : 1;
    //	 int category  : 4;
}

class PDBRecord
{
    var $header ;
    var $data   ;
    var $lenght ;

    function PDBRecord($data="", $length=0)
    {
        $this->header = new PDBRecHeader() ;
        $this->data   = $data              ;
        $this->length = $length            ;
    }
}

// This class represents Palm Pilot PDB file
// see pdb(4) for details
class PDBFile extends IOObject
{
    var $name               ;
    var $attributes         ;
    var $version            ;
    var $create_time        ;
    var $modify_time        ;
    var $backup_time        ;
    var $modificationNumber ;
    var $appInfoID          ;
    var $sortInfoID         ;
    var $type               ;
    var $creator            ;
    var $id_seed            ;
    var $nextRecordList     ;
    var $numRecords         ;

    var $records            ;
    
    var $PDB_HEADER_SIZE ;
    var $PDB_RECORD_HEADER_SIZE;

    // Constructor
    function PDBFile()
    {
        // instance variables
        $this->name               = new BoundNullTerminatedString(32) ;
        $this->attributes         = new Word ;
        $this->version            = new Word  ;
        $this->create_time        = new DWord(time());
        $this->modify_time        = new DWord(time());
        $this->backup_time        = new DWord(0);
        $this->modificationNumber = new DWord ;
        $this->appInfoID          = new DWord ;
        $this->sortInfoID         = new DWord ;
        $this->type               = new BoundString(4) ;
        $this->creator            = new BoundString(4) ;
        $this->id_seed            = new DWord ;
        $this->nextRecordList     = new DWord ;
        $this->numRecords         = new Word  ;

        $this->records            = array();
        
        //constants
        $this->PDB_HEADER_SIZE        = 78 ;
        $this->PDB_RECORD_HEADER_SIZE = 8  ;
    }

    function write()
    {
        // Write header
        $this->name               ->write();
        $this->attributes         ->write();
        $this->version            ->write();
        $this->create_time        ->write();
        $this->modify_time        ->write();
        $this->backup_time        ->write();
        $this->modificationNumber ->write();
        $this->appInfoID          ->write();
        $this->sortInfoID         ->write();
        $this->type               ->write();
        $this->creator            ->write();
        $this->id_seed            ->write();
        $this->nextRecordList     ->write();
        $this->numRecords         ->write();

        // Write record headers
	$o = $this->PDB_HEADER_SIZE + $this->PDB_RECORD_HEADER_SIZE * $this->numRecords->value;
        $index = 0x6F8000; /* unique ID */
        reset($this->records);
        $r=current($this->records);
        while($r)
        {
            $r->header->uniqueID->value = $index++;
            $r->header->offset->value   = $o;
            $r->header->write();
            $o+=$r->length;
            $r=next($this->records);
        }

        // Write records
        reset($this->records);
        $r=current($this->records);
        while($r)
        {
            $r->data->write();
            $r=next($this->records);
         }
    }

    function add_record($p)
    {
        $this->records[]=$p;
        $this->numRecords->value++;
    }
}

class PilotDOCRecord0 extends IOObject
{

    var $version;      /* 1 = plain text, 2 = compressed text */
    var $reserved1;
    var $doc_size;     /* uncompressed size in bytes */
    var $num_recs;     /* not counting itself */
    var $rec_size;     /* in bytes: usually 4096 (4K) */
    var $reserved2;
         
    function PilotDOCRecord0()
    {
        $this->version   = new Word  (1)    ;
        $this->reserved1 = new Word  (0)    ;
        $this->doc_size  = new DWord (0)    ;
        $this->num_recs  = new Word  (0)    ;
        $this->rec_size  = new Word  (4096) ;
        $this->reserved2 = new DWord (0)    ;
    }

    function write()
    {
        $this->version   ->write();
        $this->reserved1 ->write();
        $this->doc_size  ->write();
        $this->num_recs  ->write();
        $this->rec_size  ->write();
        $this->reserved2 ->write();
    }

}

class PilotDOCFile extends PDBFile
{
    function PilotDOCFile($data, $title)
    {
        $this->PDBFile(); //call parent constructor

        $this->name->value    = $title; // database title
        $this->type->value    = "TEXt";	// database type
        $this->creator->value = "REAd";	// database creator
            
        $h = new PilotDOCRecord0();
        $h->doc_size->value=strlen($data);
        $h->num_recs->value=(int)($h->doc_size->value/$h->rec_size->value);
        if($h->doc_size->value != ($h->rec_size->value*$h->num_recs->value))
        {
            $h->num_recs->value++;
        }
        
        $this->add_record(new PDBRecord($h, 16));
        for($i=0;$i<$h->num_recs->value;$i++)
        {
            if($i==$h->num_recs->value)
            {
                $rdata=substr($data, $i*$h->rec_size->value);
            }
            else
            {
                $rdata=substr($data, $i*$h->rec_size->value, $h->rec_size->value);
            }

            $this->add_record(
                new PDBRecord(new BoundString($h->rec_size->value, $rdata),
                              $h->rec_size->value
                )
            );
        }
    }
}

?>
