25
25
PalmDoc::PalmDoc(): PalmDB()
27
m_result = PalmDoc::OK;
27
m_result = PalmDoc::OK;
31
31
PalmDoc::~PalmDoc()
35
bool PalmDoc::load( const char* filename )
35
bool PalmDoc::load(const char* filename)
39
ok = PalmDB::load( filename );
42
m_result = PalmDoc::ReadError;
46
if( type() != "TEXt" )
48
qDebug( "Type is \"%s\", not \"TEXt\", so this is not Palm DOC!", type().latin1() );
49
m_result = PalmDoc::InvalidFormat;
53
if( creator() != "REAd" )
55
qDebug( "Creator is \"%s\", not \"REAd\", so this is not Palm DOC!",
57
m_result = PalmDoc::InvalidFormat;
61
// must have at least two records
62
if( records.count() < 2 )
64
qDebug( "Palm DOC has at least 2 records!" );
65
m_result = PalmDoc::InvalidFormat;
69
// the very first record is DOC header
70
// NOTE: this is not PDB header (which is handled in PalmDB) !
71
QByteArray header( *records.at( 0 ) );
74
int format = ((int)header[0]<<8) + (int)header[1];
75
qDebug( "DOC format: %d (%s)", format,
76
(format==1) ? "Plain" : (format==2) ? "Compressed" : "Unknown" );
78
// supported is only Plain or Compressed
79
if( ( format != 1 ) && ( format != 2 ) )
81
qDebug( "Unknown format of document!" );
82
m_result = PalmDoc::InvalidFormat;
89
// assemble the records
92
for( unsigned r = 1; r < records.count(); r++ )
94
QByteArray *p = records.at( r );
96
rec.resize( rec.size() + p->size() );
97
for( unsigned s=0; s<p->size(); s++ )
98
rec[i++] = p->at( s );
101
// if the text is compressed, then uncompress
103
setText( uncompress( rec ) );
105
// if the text is not compressed, simply append as string
107
setText( QString::fromLatin1( rec.data(),rec.count() ) );
39
ok = PalmDB::load(filename);
41
m_result = PalmDoc::ReadError;
45
if (type() != "TEXt") {
46
qDebug("Type is \"%s\", not \"TEXt\", so this is not Palm DOC!", qPrintable(type()));
47
m_result = PalmDoc::InvalidFormat;
51
if (creator() != "REAd") {
52
qDebug("Creator is \"%s\", not \"REAd\", so this is not Palm DOC!",
53
qPrintable(creator()));
54
m_result = PalmDoc::InvalidFormat;
58
// must have at least two records
59
if (records.count() < 2) {
60
qDebug("Palm DOC has at least 2 records!");
61
m_result = PalmDoc::InvalidFormat;
65
// the very first record is DOC header
66
// NOTE: this is not PDB header (which is handled in PalmDB) !
67
QByteArray header(*records.at(0));
70
int format = ((int)header[0] << 8) + (int)header[1];
71
qDebug("DOC format: %d (%s)", format,
72
(format == 1) ? "Plain" : (format == 2) ? "Compressed" : "Unknown");
74
// supported is only Plain or Compressed
75
if ((format != 1) && (format != 2)) {
76
qDebug("Unknown format of document!");
77
m_result = PalmDoc::InvalidFormat;
84
// assemble the records
87
for (unsigned r = 1; r < records.count(); r++) {
88
QByteArray *p = records.at(r);
90
rec.resize(rec.size() + p->size());
91
for (unsigned s = 0; s < p->size(); s++)
95
// if the text is compressed, then uncompress
97
setText(uncompress(rec));
99
// if the text is not compressed, simply append as string
101
setText(QString::fromLatin1(rec.data(), rec.count()));
114
bool PalmDoc::save( const char* filename )
108
bool PalmDoc::save(const char* filename)
116
// set proper database type and creator
118
setCreator( "REAd" );
120
// "touch" the database :-)
121
setModificationDate( QDateTime::currentDateTime() );
123
// Palm record size is always 4 KB
124
unsigned recsize = 4096;
127
QByteArray data = compress( text() );
129
// prepare the records
131
for( unsigned i=0; i<data.count(); )
133
QByteArray* ptr = new QByteArray;
134
unsigned rs = data.count() - i;
135
if( rs > recsize ) rs = recsize;
137
for( unsigned m=0; m<rs; m++ )
138
(*ptr)[m] = data[i++];
139
records.append( ptr );
142
// prepare the header
143
QByteArray header( 16 );
144
int docsize = m_text.length();
145
header[0] = 0; header[1] = 2; // 1=plain, 2=compressed
146
header[2] = header[3] = 0; // reserved word, set to 0
147
header[4] = (docsize >> 24) & 255; // uncompressed size
148
header[5] = (docsize >> 16) & 255;
149
header[6] = (docsize >> 8) & 255;
150
header[7] = docsize & 255;
151
header[8] = records.count()>> 8; // no of records
152
header[9] = records.count() & 255;
153
header[10] = recsize >>8; // record size
154
header[11] = recsize & 255;
155
header[12] = header[13] = 0;
156
header[14] = header[15] = 0;
158
// header should be the very first record
159
records.prepend( new QByteArray( header ) );
162
bool ok = PalmDB::save( filename );
165
m_result = WriteError;
110
// set proper database type and creator
114
// "touch" the database :-)
115
setModificationDate(QDateTime::currentDateTime());
117
// Palm record size is always 4 KB
118
unsigned recsize = 4096;
121
QByteArray data = compress(text());
123
// prepare the records
125
for (unsigned i = 0; i < data.count();) {
126
QByteArray* ptr = new QByteArray;
127
unsigned rs = data.count() - i;
128
if (rs > recsize) rs = recsize;
130
for (unsigned m = 0; m < rs; m++)
131
(*ptr)[m] = data[i++];
135
// prepare the header
136
QByteArray header(16);
137
int docsize = m_text.length();
138
header[0] = 0; header[1] = 2; // 1=plain, 2=compressed
139
header[2] = header[3] = 0; // reserved word, set to 0
140
header[4] = (docsize >> 24) & 255; // uncompressed size
141
header[5] = (docsize >> 16) & 255;
142
header[6] = (docsize >> 8) & 255;
143
header[7] = docsize & 255;
144
header[8] = records.count() >> 8; // no of records
145
header[9] = records.count() & 255;
146
header[10] = recsize >> 8; // record size
147
header[11] = recsize & 255;
148
header[12] = header[13] = 0;
149
header[14] = header[15] = 0;
151
// header should be the very first record
152
records.prepend(new QByteArray(header));
155
bool ok = PalmDB::save(filename);
157
m_result = WriteError;
174
166
// TODO describe in brief about compression algorithm
175
QByteArray PalmDoc::compress( const QString& text )
167
QByteArray PalmDoc::compress(const QString& text)
178
unsigned textlen = text.length();
179
const char *ctext = text.latin1();
182
// we don't know the compressed size yet
183
// therefore allocate buffer big enough
184
result.resize( textlen );
186
for( i=j=0; i<textlen; )
189
int start = (i < horizon) ? 0 : i-horizon;
191
int match_pos=0, match_len=0;
193
// look for match in the buffer
194
for( int back = i-1; (!match) && (back > start); back-- )
195
if( ctext[i] == ctext[back] )
196
if( ctext[i+1] == ctext[back+1] )
197
if( ctext[i+2] == ctext[back+2] )
204
if( ctext[i+3] == ctext[back+3] )
208
if( ctext[i+4] == ctext[back+4] )
218
unsigned char p = 0x80 | ((match_pos >> 5)&0x3f);
219
unsigned char q = ((match_pos & 0x1f) << 3) | (match_len-3);
226
char ch = ctext[i++] & 0x7f;
227
bool space_pack = false;
231
if( ctext[i] >= 0x40 )
234
if( !space_pack ) result[j++] = ch;
235
else result[j++] = ctext[i++] | 0x80;
170
unsigned textlen = text.length();
171
const char *ctext = text.toLatin1();
174
// we don't know the compressed size yet
175
// therefore allocate buffer big enough
176
result.resize(textlen);
178
for (i = j = 0; i < textlen;) {
180
int start = (i < horizon) ? 0 : i - horizon;
182
int match_pos = 0, match_len = 0;
184
// look for match in the buffer
185
for (int back = i - 1; (!match) && (back > start); back--)
186
if (ctext[i] == ctext[back])
187
if (ctext[i+1] == ctext[back+1])
188
if (ctext[i+2] == ctext[back+2]) {
190
match_pos = i - back;
194
if (ctext[i+3] == ctext[back+3]) {
197
if (ctext[i+4] == ctext[back+4]) {
205
unsigned char p = 0x80 | ((match_pos >> 5) & 0x3f);
206
unsigned char q = ((match_pos & 0x1f) << 3) | (match_len - 3);
211
char ch = ctext[i++] & 0x7f;
212
bool space_pack = false;
216
if (ctext[i] >= 0x40)
219
if (!space_pack) result[j++] = ch;
220
else result[j++] = ctext[i++] | 0x80;
245
230
#define INRANGE(v,p,q) ((v)>=(p))&&((v)<=(q))
247
232
// TODO describe in brief about decompression algorithm
248
QString PalmDoc::uncompress( const QByteArray& rec )
233
QString PalmDoc::uncompress(const QByteArray& rec)
252
for( unsigned i = 0; i < rec.size(); i++ )
254
unsigned char c = rec[i];
260
for( unsigned char v = rec[i]; c>0; c-- )
264
else if( INRANGE(c,0x09,0x7F) )
267
else if( INRANGE(c,0xC0,0xFF) )
268
result.append( 32 ).append( c^ 0x80 );
270
else if( INRANGE(c,0x80,0xBF) )
272
unsigned char d = rec[++i];
273
int back = (((c<<8)+d) & 0x3fff) >> 3;
274
int count = (d & 7) + 3;
275
if( result.length()-back >= 0 )
276
for(; count>0; count-- )
277
result.append( result[result.length()-back] );
237
for (unsigned i = 0; i < rec.size(); i++) {
238
unsigned char c = rec[i];
240
if (INRANGE(c, 1, 8)) {
243
for (unsigned char v = rec[i]; c > 0; c--)
247
else if (INRANGE(c, 0x09, 0x7F))
250
else if (INRANGE(c, 0xC0, 0xFF))
251
result.append(32).append(c ^ 0x80);
253
else if (INRANGE(c, 0x80, 0xBF)) {
254
unsigned char d = rec[++i];
255
int back = (((c << 8) + d) & 0x3fff) >> 3;
256
int count = (d & 7) + 3;
257
if (result.length() - back >= 0)
258
for (; count > 0; count--)
259
result.append(result[result.length()-back]);