|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2001 Ariya Hidayat <ariyahidayat@yahoo.de>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "palmdoc.h"
|
|
|
|
|
|
|
|
#include <tqcstring.h>
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqptrlist.h>
|
|
|
|
#include <tqstring.h>
|
|
|
|
|
|
|
|
PalmDoc::PalmDoc(): PalmDB()
|
|
|
|
{
|
|
|
|
m_result = PalmDoc::OK;
|
|
|
|
setText( TQString() );
|
|
|
|
}
|
|
|
|
|
|
|
|
PalmDoc::~PalmDoc()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PalmDoc::load( const char* filename )
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
ok = PalmDB::load( filename );
|
|
|
|
if( !ok )
|
|
|
|
{
|
|
|
|
m_result = PalmDoc::ReadError;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( type() != "TEXt" )
|
|
|
|
{
|
|
|
|
tqDebug( "Type is \"%s\", not \"TEXt\", so this is not Palm DOC!", type().latin1() );
|
|
|
|
m_result = PalmDoc::InvalidFormat;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( creator() != "REAd" )
|
|
|
|
{
|
|
|
|
tqDebug( "Creator is \"%s\", not \"REAd\", so this is not Palm DOC!",
|
|
|
|
creator().latin1() );
|
|
|
|
m_result = PalmDoc::InvalidFormat;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// must have at least two records
|
|
|
|
if( records.count() < 2 )
|
|
|
|
{
|
|
|
|
tqDebug( "Palm DOC has at least 2 records!" );
|
|
|
|
m_result = PalmDoc::InvalidFormat;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the very first record is DOC header
|
|
|
|
// NOTE: this is not PDB header (which is handled in PalmDB) !
|
|
|
|
TQByteArray header( *records.at( 0 ) );
|
|
|
|
|
|
|
|
// format of the DOC
|
|
|
|
int format = (header[0]<<8) + header[1];
|
|
|
|
tqDebug( "DOC format: %d (%s)", format,
|
|
|
|
(format==1) ? "Plain" : (format==2) ? "Compressed" : "Unknown" );
|
|
|
|
|
|
|
|
// supported is only Plain or Compressed
|
|
|
|
if( ( format != 1 ) && ( format != 2 ) )
|
|
|
|
{
|
|
|
|
tqDebug( "Unknown format of document!" );
|
|
|
|
m_result = PalmDoc::InvalidFormat;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize
|
|
|
|
setText( TQString() );
|
|
|
|
|
|
|
|
// assemble the records
|
|
|
|
TQByteArray rec;
|
|
|
|
unsigned i = 0;
|
|
|
|
for( unsigned r = 1; r < records.count(); r++ )
|
|
|
|
{
|
|
|
|
TQByteArray *p = records.at( r );
|
|
|
|
if( !p ) continue;
|
|
|
|
rec.resize( rec.size() + p->size() );
|
|
|
|
for( unsigned s=0; s<p->size(); s++ )
|
|
|
|
rec[i++] = p->at( s );
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the text is compressed, then uncompress
|
|
|
|
if( format == 2 )
|
|
|
|
setText( uncompress( rec ) );
|
|
|
|
|
|
|
|
// if the text is not compressed, simply append as string
|
|
|
|
if( format == 1 )
|
|
|
|
setText( TQString::fromLatin1( rec.data(),rec.count() ) );
|
|
|
|
|
|
|
|
// done
|
|
|
|
m_result = OK;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PalmDoc::save( const char* filename )
|
|
|
|
{
|
|
|
|
// set proper database type and creator
|
|
|
|
setType( "TEXt" );
|
|
|
|
setCreator( "REAd" );
|
|
|
|
|
|
|
|
// "touch" the database :-)
|
|
|
|
setModificationDate( TQDateTime::currentDateTime() );
|
|
|
|
|
|
|
|
// Palm record size is always 4 KB
|
|
|
|
unsigned recsize = 4096;
|
|
|
|
|
|
|
|
// compress the text
|
|
|
|
TQByteArray data = compress( text() );
|
|
|
|
|
|
|
|
// prepare the records
|
|
|
|
records.clear();
|
|
|
|
for( unsigned i=0; i<data.count(); )
|
|
|
|
{
|
|
|
|
TQByteArray* ptr = new TQByteArray;
|
|
|
|
unsigned rs = data.count() - i;
|
|
|
|
if( rs > recsize ) rs = recsize;
|
|
|
|
ptr->resize( rs );
|
|
|
|
for( unsigned m=0; m<rs; m++ )
|
|
|
|
(*ptr)[m] = data[i++];
|
|
|
|
records.append( ptr );
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepare the header
|
|
|
|
TQByteArray header( 16 );
|
|
|
|
int docsize = m_text.length();
|
|
|
|
header[0] = 0; header[1] = 2; // 1=plain, 2=compressed
|
|
|
|
header[2] = header[3] = 0; // reserved word, set to 0
|
|
|
|
header[4] = (docsize >> 24) & 255; // uncompressed size
|
|
|
|
header[5] = (docsize >> 16) & 255;
|
|
|
|
header[6] = (docsize >> 8) & 255;
|
|
|
|
header[7] = docsize & 255;
|
|
|
|
header[8] = records.count()>> 8; // no of records
|
|
|
|
header[9] = records.count() & 255;
|
|
|
|
header[10] = recsize >>8; // record size
|
|
|
|
header[11] = recsize & 255;
|
|
|
|
header[12] = header[13] = 0;
|
|
|
|
header[14] = header[15] = 0;
|
|
|
|
|
|
|
|
// header should be the very first record
|
|
|
|
records.prepend( new TQByteArray( header ) );
|
|
|
|
|
|
|
|
// write to file
|
|
|
|
bool ok = PalmDB::save( filename );
|
|
|
|
if( !ok )
|
|
|
|
{
|
|
|
|
m_result = WriteError;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// done
|
|
|
|
m_result = OK;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO describe in brief about compression algorithm
|
|
|
|
TQByteArray PalmDoc::compress( const TQString& text )
|
|
|
|
{
|
|
|
|
TQByteArray result;
|
|
|
|
unsigned textlen = text.length();
|
|
|
|
const char *ctext = text.latin1();
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
// we don't know the compressed size yet
|
|
|
|
// therefore allocate buffer big enough
|
|
|
|
result.resize( textlen );
|
|
|
|
|
|
|
|
for( i=j=0; i<textlen; )
|
|
|
|
{
|
|
|
|
int horizon = 2047;
|
|
|
|
int start = (i < horizon) ? 0 : i-horizon;
|
|
|
|
bool match = false;
|
|
|
|
int match_pos=0, match_len=0;
|
|
|
|
|
|
|
|
// look for match in the buffer
|
|
|
|
for( int back = i-1; (!match) && (back > start); back-- )
|
|
|
|
if( ctext[i] == ctext[back] )
|
|
|
|
if( ctext[i+1] == ctext[back+1] )
|
|
|
|
if( ctext[i+2] == ctext[back+2] )
|
|
|
|
{
|
|
|
|
match = true;
|
|
|
|
match_pos = i-back;
|
|
|
|
match_len = 3;
|
|
|
|
|
|
|
|
if( i+3 < textlen )
|
|
|
|
if( ctext[i+3] == ctext[back+3] )
|
|
|
|
{
|
|
|
|
match_len = 4;
|
|
|
|
if( i+4 < textlen )
|
|
|
|
if( ctext[i+4] == ctext[back+4] )
|
|
|
|
{
|
|
|
|
match_len = 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if( match )
|
|
|
|
{
|
|
|
|
unsigned char p = 0x80 | ((match_pos >> 5)&0x3f);
|
|
|
|
unsigned char q = ((match_pos & 0x1f) << 3) | (match_len-3);
|
|
|
|
result[j++] = p;
|
|
|
|
result[j++] = q;
|
|
|
|
i+= match_len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char ch = ctext[i++] & 0x7f;
|
|
|
|
bool space_pack = false;
|
|
|
|
|
|
|
|
if( ch == 0x20 )
|
|
|
|
if ( i<textlen )
|
|
|
|
if( ctext[i] >= 0x40 )
|
|
|
|
space_pack = true;
|
|
|
|
|
|
|
|
if( !space_pack ) result[j++] = ch;
|
|
|
|
else result[j++] = ctext[i++] | 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
result.resize( j );
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INRANGE(v,p,q) ((v)>=(p))&&((v)<=(q))
|
|
|
|
|
|
|
|
// TODO describe in brief about decompression algorithm
|
|
|
|
TQString PalmDoc::uncompress( TQByteArray rec )
|
|
|
|
{
|
|
|
|
TQString result;
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < rec.size(); i++ )
|
|
|
|
{
|
|
|
|
unsigned char c = rec[i];
|
|
|
|
|
|
|
|
if( INRANGE(c,1,8) )
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
if( i < rec.size() )
|
|
|
|
for( unsigned char v = rec[i]; c>0; c-- )
|
|
|
|
result.append( v );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( INRANGE(c,0x09,0x7F) )
|
|
|
|
result.append( c );
|
|
|
|
|
|
|
|
else if( INRANGE(c,0xC0,0xFF) )
|
|
|
|
result.append( 32 ).append( c^ 0x80 );
|
|
|
|
|
|
|
|
else if( INRANGE(c,0x80,0xBF) )
|
|
|
|
{
|
|
|
|
unsigned char d = rec[++i];
|
|
|
|
int back = (((c<<8)+d) & 0x3fff) >> 3;
|
|
|
|
int count = (d & 7) + 3;
|
|
|
|
if( result.length()-back >= 0 )
|
|
|
|
for(; count>0; count-- )
|
|
|
|
result.append( result[result.length()-back] );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|