klaola.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1999 Werner Trobin <trobin@kde.org>
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#include <klaola.h>
21#include <kdebug.h>
22
23const int KLaola::s_area = 30510;
24
25KLaola::KLaola(const myFile &file) {
26
27 smallBlockDepot=0L;
28 bigBlockDepot=0L;
29 smallBlockFile=0L;
30 bbd_list=0L;
31 ok=true;
32 m_nodeList.setAutoDelete(true);
33 //m_currentPath.setAutoDelete(true);
34
35 if( (file.length % 0x200) != 0 ) {
36 kdError(s_area) << "KLaola::KLaola(): Invalid file size!" << endl;
37 ok=false;
38 }
39 if(ok) {
40 m_file=file;
41 maxblock = file.length / 0x200 - 2;
42 maxSblock=0; // will be set in readSmallBlockDepot
43
44 if(!parseHeader())
45 ok=false;
46 if(ok) {
47 readBigBlockDepot();
48 readSmallBlockDepot();
49 readSmallBlockFile();
50 readRootList();
51 }
52 }
53 m_currentPath.clear();
54 testIt();
55
56 // current path=root dirHandle
57 m_currentPath.clear();
58 if ( !m_nodeTree.isEmpty() )
59 m_currentPath.append(m_nodeTree.getFirst()->getFirst()->node);
60}
61
62KLaola::~KLaola() {
63
64 delete [] bigBlockDepot;
65 bigBlockDepot=0L;
66 delete [] smallBlockDepot;
67 smallBlockDepot=0L;
68 delete [] smallBlockFile;
69 smallBlockFile=0L;
70 delete [] bbd_list;
71 bbd_list=0L;
72}
73
74// Comvert the given list of nodes into a tree.
75void KLaola::createTree(const int handle, const short index) {
76
77 Node *node = dynamic_cast<Node *>(m_nodeList.at(handle));
78 SubTree *subtree;
79
80 TreeNode *tree=new TreeNode;
81 tree->node=node;
82 tree->subtree=-1;
83
84 //TQString nix="### entering create tree: handle=";
85 //nix+=TQString::number(handle);
86 //nix+=" index=";
87 //nix+=TQString::number(index);
88 //kdDebug(s_area) << nix << endl;
89
90 if(node->prevHandle!=-1) {
91 //kdDebug(s_area) << "create tree: prevHandle" << endl;
92 createTree(node->prevHandle, index);
93 }
94 if(node->dirHandle!=-1) {
95 subtree=new SubTree;
96 subtree->setAutoDelete(true);
97 m_nodeTree.append(subtree);
98 tree->subtree=m_nodeTree.at();
99 //kdDebug(s_area) << "create tree: dirHandle" << endl;
100 createTree(node->dirHandle, tree->subtree);
101 }
102 subtree=m_nodeTree.at(index);
103 //kdDebug(s_area) << "create tree: APPEND " << handle << " tree node " << tree << endl;
104 subtree->append(tree);
105 if(node->nextHandle!=-1) {
106 //kdDebug(s_area) << "create tree: nextHandle" << endl;
107 createTree(node->nextHandle, index);
108 }
109}
110
111const KLaola::NodeList KLaola::currentPath() const {
112 return m_currentPath;
113}
114
115bool KLaola::enterDir(const OLENode *dirHandle) {
116
117 NodeList nodes;
118 Node *node;
119
120 if(ok) {
121 nodes = parseCurrentDir();
122 for (node = dynamic_cast<Node *>(nodes.first()); node; node = dynamic_cast<Node *>(nodes.next()))
123 {
124 if(node->m_handle==dirHandle->handle() && node->isDirectory() && !node->deadDir) {
125 m_currentPath.append(node);
126 return true;
127 }
128 }
129 }
130 return false;
131}
132
133const KLaola::NodeList KLaola::find(const TQString &name, bool onlyCurrentDir) {
134
135 OLENode *node;
136 NodeList ret;
137 int i=0;
138
139 if(ok) {
140 if(!onlyCurrentDir) {
141 for(node=m_nodeList.first(); node; node=m_nodeList.next()) {
142 if(node->name()==name) {
143 ret.append(node);
144 ++i;
145 }
146 }
147 }
148 else {
149 NodeList list=parseCurrentDir();
150
151 for(node=list.first(); node; node=list.next()) {
152 if(node->name()==name) {
153 ret.append(node);
154 ++i;
155 }
156 }
157 }
158 }
159 return ret;
160}
161
162bool KLaola::leaveDir() {
163
164 if (ok) {
165 return m_currentPath.removeLast();
166 }
167 return false;
168}
169
170int KLaola::nextBigBlock(int pos) const
171{
172
173 int x=pos*4;
174 return ( (bigBlockDepot[x+3] << 24) + (bigBlockDepot[x+2] << 16) +
175 (bigBlockDepot[x+1] << 8) + bigBlockDepot[x] );
176}
177
178int KLaola::nextSmallBlock(int pos) const
179{
180
181 if(smallBlockDepot) {
182 int x=pos*4;
183 return ( (smallBlockDepot[x+3] << 24) + (smallBlockDepot[x+2] << 16) +
184 (smallBlockDepot[x+1] << 8) + smallBlockDepot[x] );
185 }
186 else
187 return -2; // Emergency Break :)
188}
189
190bool KLaola::parseHeader() {
191
192 if(tqstrncmp((const char*)m_file.data,"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1",8 )!=0) {
193 kdError(s_area) << "KLaola::parseHeader(): Invalid file format (unexpected id in header)!" << endl;
194 return false;
195 }
196
197 num_of_bbd_blocks=read32(0x2c);
198 root_startblock=read32(0x30);
199 sbd_startblock=read32(0x3c);
200
201 if (num_of_bbd_blocks >= 0x800000) {
202 kdError(s_area) << "KLaola::parseHeader(): Too many bbd blocks found in header!" << endl;
203 return false;
204 }
205 bbd_list=new unsigned int[num_of_bbd_blocks];
206
207 unsigned int i, j;
208 for(i=0, j=0; i<num_of_bbd_blocks; ++i, j=j+4) {
209 bbd_list[i]=read32(0x4c+j);
210 if (bbd_list[i] >= (0x800000 - 1)) {
211 kdError(s_area) << "KLaola::parseHeader(): bbd " << i << " offset (" << bbd_list[i] << ") too large" << endl;
212 return false;
213 }
214 }
215 return true;
216}
217
218KLaola::NodeList KLaola::parseCurrentDir() {
219
220 Node *node;
221 SubTree *subtree;
222 NodeList nodeList;
223 TreeNode *treeNode;
224 unsigned int i;
225 bool found;
226
227 if(ok) {
228 for(i=0, subtree=m_nodeTree.first(); i<m_currentPath.count(); ++i) {
229 treeNode=subtree->first();
230 found=false;
231 do {
232 if(treeNode==0) {
233 kdError(s_area) << "KLaola::parseCurrentDir(): path seems to be corrupted!" << endl;
234 ok=false;
235 }
236 else if(treeNode->node->handle()==m_currentPath.at(i)->handle() && treeNode->subtree!=-1) {
237 found=true;
238 }
239 else
240 treeNode=subtree->next();
241 } while(!found && ok);
242 subtree=m_nodeTree.at(treeNode->subtree);
243 }
244 }
245 if(ok) {
246 for(treeNode=subtree->first(); treeNode!=0; treeNode=subtree->next()) {
247 node=new Node(*treeNode->node);
248 node->deadDir = (node->dirHandle==-1 && node->isDirectory());
249 // this is a strange situation :)
250 if (node->deadDir)
251 kdDebug(s_area) << "ignoring: " << node->describe() << " is empty" << endl;
252 else
253 nodeList.append(node);
254 }
255 }
256 return nodeList;
257}
258
259KLaola::NodeList KLaola::parseRootDir() {
260
261 NodeList tmpNodeList;
262 NodeList tmp;
263
264 if(ok) {
265 tmp=m_currentPath;
266 m_currentPath.clear(); // current path=root dirHandle
267 m_currentPath.append(m_nodeTree.getFirst()->getFirst()->node);
268 tmpNodeList=parseCurrentDir();
269 m_currentPath=tmp;
270 }
271 return tmpNodeList;
272}
273
274unsigned char KLaola::read8(int i) const
275{
276 return m_file.data[i];
277}
278
279unsigned short KLaola::read16(int i) const
280{
281 return ( (m_file.data[i+1] << 8) + m_file.data[i] );
282}
283
284unsigned int KLaola::read32(int i) const
285{
286 return ( (read16(i+2) << 16) + read16(i) );
287}
288
289const unsigned char *KLaola::readBBStream(int start, bool setmaxSblock)
290{
291
292 int i=0, tmp;
293 unsigned char *p=0;
294
295 tmp=start;
296 /* 0x10000 chosen as arbitrary "too many blocks" limit to not loop forver */
297 while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxblock)) {
298 ++i;
299 tmp=nextBigBlock(tmp);
300 }
301 if(i!=0) {
302 p=new unsigned char[i*0x200];
303 if(setmaxSblock)
304 maxSblock=i*8-1;
305 i=0;
306 tmp=start;
307 while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxblock)) {
308 memcpy(&p[i*0x200], &m_file.data[(tmp+1)*0x200], 0x200);
309 tmp=nextBigBlock(tmp);
310 ++i;
311 }
312 }
313 return p;
314}
315
316const unsigned char *KLaola::readSBStream(int start) const {
317
318 int i=0, tmp;
319 unsigned char *p=0;
320
321 tmp=start;
322 /* 0x10000 chosen as arbitrary "too many blocks" limit to not loop forver */
323 while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxSblock)) {
324 ++i;
325 tmp=nextSmallBlock(tmp);
326 }
327 if(i!=0) {
328 p=new unsigned char[i*0x40];
329 i=0;
330 tmp=start;
331 while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxSblock)) {
332 memcpy(&p[i*0x40], &smallBlockFile[tmp*0x40], 0x40);
333 tmp=nextSmallBlock(tmp);
334 ++i;
335 }
336 }
337 return p;
338}
339
340void KLaola::readBigBlockDepot() {
341 if (num_of_bbd_blocks >= 0x800000)
342 return;
343
344 bigBlockDepot=new unsigned char[0x200*num_of_bbd_blocks];
345 for(unsigned int i=0; i<num_of_bbd_blocks; ++i) {
346 unsigned int offset = (bbd_list[i]+1)*0x200;
347 if (offset > m_file.length - 0x200) {
348 /* attempting to read past end of file */
349 memset(&bigBlockDepot[i*0x200], 0, 0x200);
350 }
351 else {
352 memcpy(&bigBlockDepot[i*0x200], &m_file.data[offset], 0x200);
353 }
354 }
355}
356
357void KLaola::readSmallBlockDepot() {
358 smallBlockDepot=const_cast<unsigned char*>(readBBStream(sbd_startblock));
359}
360
361void KLaola::readSmallBlockFile() {
362 smallBlockFile=const_cast<unsigned char*>(readBBStream( read32( (root_startblock+1)*0x200 + 0x74), true));
363}
364
365void KLaola::readRootList() {
366
367 int pos=root_startblock;
368 int handle=0;
369
370 while(pos!=-2 && pos>=0 && pos<=static_cast<int>(maxblock)) {
371 for(int i=0; i<4; ++i, ++handle)
372 readPPSEntry((pos+1)*0x200+0x80*i, handle);
373 pos=nextBigBlock(pos);
374 }
375 SubTree *subtree=new SubTree;
376 subtree->setAutoDelete(true);
377 m_nodeTree.append(subtree);
378
379 createTree(0, 0); // build the tree with a recursive method :)
380}
381
382// Add the given OLE node to the list of nodes.
383void KLaola::readPPSEntry(int pos, const int handle) {
384
385 int nameSize = read16(pos + 0x40);
386
387 // Does the PPS Entry seem to be valid?
388
389 if (nameSize)
390 {
391 int i;
392 Node *node = new Node(this);
393
394 // The first character of the name can be a prefix.
395 node->m_prefix = static_cast<Prefix>(read16(pos));
396 if (node->m_prefix <= RESERVED_LAST)
397 {
398 i = 1;
399 }
400 else
401 {
402 node->m_prefix = NONE;
403 i = 0;
404 }
405
406 // Get the rest of the name.
407 for (; i < (nameSize / 2) - 1; ++i)
408 {
409 TQChar tmp;
410
411 tmp = read16(pos + 2 * i);
412 node->m_name += tmp;
413 }
414 node->m_handle = handle;
415 node->type = static_cast<NodeType>(read8(pos + 0x42));
416 node->prevHandle = static_cast<int>(read32(pos + 0x44));
417 node->nextHandle = static_cast<int>(read32(pos + 0x48));
418 node->dirHandle = static_cast<int>(read32(pos + 0x4C));
419 node->ts1s = static_cast<int>(read32(pos + 0x64));
420 node->ts1d = static_cast<int>(read32(pos + 0x68));
421 node->ts2s = static_cast<int>(read32(pos + 0x6C));
422 node->ts2d = static_cast<int>(read32(pos + 0x70));
423 node->sb = read32(pos + 0x74);
424 node->size = read32(pos + 0x78);
425 node->deadDir = false;
426 m_nodeList.append(node);
427 }
428}
429
430myFile KLaola::stream(const OLENode *node) {
431
432 const Node *realNode = dynamic_cast<const Node *>(node);
433 const unsigned char *temp;
434 myFile ret;
435
436 if(ok) {
437 if(realNode->size>=0x1000)
438 temp = readBBStream(realNode->sb);
439 else
440 temp = readSBStream(realNode->sb);
441 ret.setRawData(temp, realNode->size);
442 }
443 return ret;
444}
445
446myFile KLaola::stream(unsigned handle) {
447
448 OLENode *node;
449
450 node = m_nodeList.at(handle);
451 return stream(node);
452}
453
454void KLaola::testIt(TQString prefix)
455{
456
457 NodeList nodes;
458 OLENode *node;
459
460 nodes = parseCurrentDir();
461 for (node = nodes.first(); node; node = nodes.next())
462 {
463 kdDebug(s_area) << prefix + node->describe() << endl;
464 if (node->isDirectory())
465 {
466 enterDir(node);
467 testIt(prefix + " ");
468 }
469 }
470}
471
472// Return a human-readable description of a stream.
473TQString KLaola::Node::describe() const
474{
475 TQString description;
476 myFile file;
477 unsigned i;
478
479 description = TQString::number(m_handle) + " " +
480 m_name + "(" +
481 TQString::number(sb) + " " +
482 TQString::number(size) + " bytes)";
483 if (isDirectory())
484 description += ", directory";
485 switch (m_prefix)
486 {
487 case OLE_MANAGED_0:
488 description += ", OLE_0";
489 break;
490 case CLSID:
491 description += ", CLSID=";
492 description += readClassStream();
493 file = m_laola->stream(this);
494 description += ", ";
495 for (i = 16; i < file.length; i++)
496 {
497 description += TQString::number((file.data[i] >> 4) & 0xf, 16);
498 description += TQString::number(file.data[i] & 0xf, 16);
499 }
500 description += ", ";
501 for (i = 16; i < file.length; i++)
502 {
503 TQChar tmp = file.data[i];
504
505 if (tmp.isPrint())
506 description += tmp;
507 else
508 description += '.';
509 }
510 break;
511 case OLE_MANAGED_2:
512 description += ", OLE_2";
513 break;
514 case PARENT_MANAGED:
515 description += ", parent managed";
516 break;
517 case STRUCTURED_STORAGE:
518 description += ", reserved 0x" + TQString::number(m_prefix, 16);
519 break;
520 case NONE:
521 break;
522 default:
523 description += ", reserved 0x" + TQString::number(m_prefix, 16);
524 break;
525 }
526 return description;
527}
528
529TQString KLaola::Node::name() const
530{
531 return m_name;
532}
533
534// See "Associating Code with Storage" in Inside OLE.
535TQString KLaola::Node::readClassStream() const
536{
537 if (isDirectory())
538 return TQString();
539 if (m_prefix == CLSID)
540 {
541 myFile file;
542 unsigned i;
543 TQString clsid;
544
545 // CLSID format is: 00020900-0000-0000-C000-000000000046
546 file = m_laola->stream(this);
547 for (i = 0; i < 4; i++)
548 {
549 clsid += TQString::number((file.data[i] >> 4) & 0xf, 16);
550 clsid += TQString::number(file.data[i] & 0xf, 16);
551 }
552 clsid += '-';
553 for (; i < 6; i++)
554 {
555 clsid += TQString::number((file.data[i] >> 4) & 0xf, 16);
556 clsid += TQString::number(file.data[i] & 0xf, 16);
557 }
558 clsid += '-';
559 for (; i < 8; i++)
560 {
561 clsid += TQString::number((file.data[i] >> 4) & 0xf, 16);
562 clsid += TQString::number(file.data[i] & 0xf, 16);
563 }
564 clsid += '-';
565 for (; i < 10; i++)
566 {
567 clsid += TQString::number((file.data[i] >> 4) & 0xf, 16);
568 clsid += TQString::number(file.data[i] & 0xf, 16);
569 }
570 clsid += '-';
571 for (; i < 16; i++)
572 {
573 clsid += TQString::number((file.data[i] >> 4) & 0xf, 16);
574 clsid += TQString::number(file.data[i] & 0xf, 16);
575 }
576 return clsid;
577 }
578 return TQString();
579}