filters

palmdb.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002 Ariya Hidayat <ariyahidayat@yahoo.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20/*
21 The database layout for PalmDB files is described in
22 http://www.palmos.com/dev/support/docs/protein_books/FileFormats/Intro.html
23*/
24
25#include "palmdb.h"
26
27#include <tqcstring.h>
28#include <tqdatastream.h>
29#include <tqdatetime.h>
30#include <tqfile.h>
31#include <tqmemarray.h>
32#include <tqptrlist.h>
33#include <tqstring.h>
34
35PalmDB::PalmDB()
36{
37 // some default values
38 setName( "Unnamed" );
39 setAttributes( 0 );
40 setVersion( 0 );
41 setCreationDate( TQDateTime::currentDateTime() );
42 setModificationDate( TQDateTime::currentDateTime() );
43 setLastBackupDate( TQDateTime::currentDateTime() );
44 setType( TQString() );
45 setCreator( TQString() );
46
47 // crash if autodelete ?
48 records.setAutoDelete( true );
49}
50
51PalmDB::~PalmDB()
52{
53 records.clear();
54}
55
56bool PalmDB::load( const char* filename )
57{
58 // open input file
59 TQFile in (filename);
60 if (!in.open (IO_ReadOnly))
61 return false;
62
63 TQDataStream stream;
64 stream.setDevice (&in);
65
66 unsigned filesize = stream.device()->size();
67 if( filesize < 72 ) return false;
68
69 // always big-endian
70 stream.setByteOrder (TQDataStream::BigEndian);
71
72 // now start to read PDB header (72 bytes)
73
74 // read and encode database name
75 // The name field is 32 bytes long, and is NUL terminated.
76 // Use the length parameter of fromLatin1() anyway.
77 TQ_UINT8 name[32];
78 for(int k = 0; k < 32; k++)
79 stream >> name[k];
80 m_name = TQString::fromLatin1( (char*) name, 31 );
81
82 // read database attribute
83 TQ_UINT16 attr;
84 stream >> attr;
85 m_attributes = attr;
86
87 // read database version (app-specific)
88 TQ_UINT16 ver;
89 stream >> ver;
90 m_version = ver;
91
92 // NOTE: PDB specifies date as number of seconds since 1 Jan 1904
93 // TQDateTime::setTime_t expects number of seconds since 1 Jan 1970
94 // so, we make adjustment with a constant offset of 2082844800
95 const int adjust = 2082844800;
96
97 // read creation date
98 TQ_UINT32 creation;
99 stream >> creation;
100 m_creationDate.setTime_t( creation - adjust );
101
102 // read modification date
103 TQ_UINT32 modification;
104 stream >> modification;
105 m_modificationDate.setTime_t( modification - adjust );
106
107 // read last backup date
108 TQ_UINT32 lastbackup;
109 stream >> lastbackup;
110 m_lastBackupDate.setTime_t( lastbackup - adjust );
111
112 // read modification number
113 TQ_UINT32 modnum;
114 stream >> modnum;
115
116 // read app info id and sort info id
117 TQ_UINT32 appid, sortid;
118 stream >> appid;
119 stream >> sortid;
120
121 // read and encode database type
122 TQ_UINT8 dbt[4];
123 stream >> dbt[0] >> dbt[1] >> dbt[2] >> dbt[3];
124 m_type = TQString::fromLatin1( (char*) dbt, 4 );
125
126 // read and encode database creator
127 TQ_UINT8 dbc[4];
128 stream >> dbc[0] >> dbc[1] >> dbc[2] >> dbc[3];
129 m_creator = TQString::fromLatin1( (char*) dbc, 4 );
130
131 // read unique id seed
132 TQ_UINT32 idseed;
133 stream >> idseed;
134 m_uniqueIDSeed = idseed;
135
136 // now start to read PDB record list (variable-length)
137
138 // next record list
139 // FIXME what to do with this ?
140 TQ_UINT32 nextlist;
141 stream >> nextlist;
142
143 // number of records
144 TQ_UINT16 numrec;
145 stream >> numrec;
146
147 // read entries in record list
148 // find out location and size of each record
149 TQMemArray<unsigned> recpos( numrec );
150 TQMemArray<int> recsize( numrec );
151
152 // FIXME any other better way to find record size ?
153 for( int r = 0; r < numrec; r++ )
154 {
155 TQ_UINT32 pos;
156 TQ_UINT8 flag, dummy;
157 stream >> pos >> flag >> dummy >> dummy >> dummy;
158 recpos[r] = pos; recsize[r] = filesize - pos;
159 if( r> 0 ) recsize[r-1] = pos - recpos[r-1]; // fixup
160 }
161
162 // debugging
163#ifdef PDB_DEBUG
164 tqDebug( "name: \"%s\"", m_name.latin1() );
165 tqDebug( "type: \"%s\"", m_type.latin1() );
166 tqDebug( "creator: \"%s\"", m_creator.latin1() );
167 tqDebug( "attributes: 0x%04X", m_attributes );
168 tqDebug( "version: 0x%04X", m_version );
169 tqDebug( "creation date: %s", m_creationDate.toString().latin1() );
170 tqDebug( "modification date: %s", m_modificationDate.toString().latin1() );
171 tqDebug( "last backup date: %s", m_lastBackupDate.toString().latin1() );
172 tqDebug( "number of records: %d", numrec );
173 for( int r = 0; r < numrec; r++ )
174 tqDebug( " rec %d at 0x%X size %d", r, recpos[r], recsize[r] );
175#endif
176
177 // load all records
178 records.clear();
179 for( int r = 0; r < numrec; r++ )
180 {
181 TQByteArray* data = new TQByteArray;
182
183 if( recpos[r] < filesize )
184 if( recsize[r] >= 0 )
185 {
186 data->resize( recsize[r] );
187 stream.device()->at( recpos[r] );
188 for( int q = 0; q < recsize[r]; q++ )
189 { TQ_UINT8 c; stream >> c; data->at(q) = c; }
190 }
191 records.append( data );
192 }
193
194 // close input file
195 in.close();
196
197 return true;
198}
199
200bool PalmDB::save( const char* filename )
201{
202 // open output file
203 TQFile out( filename );
204 if( !out.open( IO_WriteOnly ) )
205 return false;
206
207 TQDataStream stream;
208 stream.setDevice( &out );
209
210 // always big-endian
211 stream.setByteOrder (TQDataStream::BigEndian);
212
213 // now write PDB header (72 bytes)
214
215 // write database name
216 setName( name() );
217 const char *dbname = m_name.latin1();
218 for( unsigned k=0; k<31; k++ )
219 {
220 TQ_UINT8 c = (k<m_name.length()) ? dbname[k] : 0;
221 stream << c;
222 }
223 {
224 // NUL-terminate the database name
225 TQ_UINT8 c = 0;
226 stream << c;
227 }
228
229
230
231 // write database attribute
232 TQ_UINT16 attr = m_attributes;
233 stream << attr;
234
235 // write database version (app-specific)
236 TQ_UINT16 ver = m_version;
237 stream << ver;
238
239 // reference date is 1 Jan 1904
240 // see also note in function load() above
241 TQDateTime ref = TQDate( 1904, 1, 1 );
242
243 // write creation date
244 TQ_UINT32 creation = -m_creationDate.secsTo( ref );
245 stream << creation;
246
247 // write modification date
248 TQ_UINT32 modification = -m_modificationDate.secsTo( ref );
249 stream << modification;
250
251 // write last backup date
252 TQ_UINT32 lastbackup = -m_lastBackupDate.secsTo( ref );
253 stream << lastbackup;
254
255 // write modification number
256 TQ_UINT32 modnum = 0;
257 stream << modnum;
258
259 // write app info id and sort info id
260 TQ_UINT32 appid = 0, sortid = 0;
261 stream << appid;
262 stream << sortid;
263
264 // write and encode database type
265 TQ_UINT8 dbt[4];
266 const char *dbtype = m_type.latin1();
267 for( int p=0; p<4; p++ ) dbt[p]=dbtype[p];
268 stream << dbt[0] << dbt[1] << dbt[2] << dbt[3];
269
270 // write and encode database creator
271 TQ_UINT8 dbc[4];
272 const char *dbcreator = m_creator.latin1();
273 for( int p=0; p<4; p++ ) dbc[p]=dbcreator[p];
274 stream << dbc[0] << dbc[1] << dbc[2] << dbc[3];
275
276 // write unique id seed
277 TQ_UINT32 idseed = 0;
278 stream << idseed;
279
280 // now start to read PDB record list (variable-length)
281
282 // next record list
283 TQ_UINT32 nextlist = 0;
284 stream << nextlist;
285
286 // number of records
287 TQ_UINT16 numrec = records.count();
288 stream << numrec;
289
290 // where is the first record ?
291 // 78 is size of PDB header, 2 is filler before data
292 TQ_UINT32 pos = 78 + 2;
293 pos += records.count()*8;
294
295 // write record list
296 for( unsigned r = 0; r < records.count(); r++ )
297 {
298 TQ_UINT8 flag = 0, dummy = 0;
299 stream << pos;
300 stream << flag;
301 stream << dummy << dummy << dummy;
302 pos += records.at(r)->count();
303 }
304
305 // write 2-byte dummy
306 TQ_UINT16 filler = 0;
307 stream << filler;
308
309 // write all records
310 for( unsigned r = 0; r < records.count(); r++ )
311 {
312 TQByteArray *data = records.at( r );
313 if( !data ) continue;
314 for( unsigned j=0; j<data->count(); j++ )
315 {
316 TQ_UINT8 c = data->at( j );
317 stream << c;
318 }
319 }
320
321 // close output file
322 out.close();
323
324 return true;
325}
326
327void PalmDB::setType( const TQString& t )
328{
329 m_type = t;
330 if( m_type.length() > 4 )
331 m_type = m_type.left( 4 );
332 while( m_type.length() < 4 )
333 m_type.append( 32 );
334}
335
336
337void PalmDB::setCreator( const TQString& c )
338{
339 m_creator = c;
340 if( m_creator.length() > 4 )
341 m_type = m_creator.left( 4 );
342 while( m_creator.length() < 4 )
343 m_creator.append( 32 );
344}