dee20a7 by Jos van den Oever at 2009-08-23 1
/* POLE - Portable C++ library to access OLE Storage
2
   Copyright (C) 2002-2005 Ariya Hidayat <ariya@kde.org>
3
4
   Redistribution and use in source and binary forms, with or without
5
   modification, are permitted provided that the following conditions
6
   are met:
7
   * Redistributions of source code must retain the above copyright notice,
8
     this list of conditions and the following disclaimer.
9
   * Redistributions in binary form must reproduce the above copyright notice,
10
     this list of conditions and the following disclaimer in the documentation
11
     and/or other materials provided with the distribution.
12
   * Neither the name of the authors nor the names of its contributors may be
13
     used to endorse or promote products derived from this software without
14
     specific prior written permission.
15
16
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26
   THE POSSIBILITY OF SUCH DAMAGE.
27
*/
28
29
#include <fstream>
30
#include <iostream>
31
#include <list>
32
#include <string>
33
#include <vector>
34
35
#include "pole.h"
36
37
#include <string.h>
38
39
// enable to activate debugging output
40
// #define POLE_DEBUG
41
42
namespace POLE
43
{
44
45
class Header
46
{
6a7d8df by Jos van den Oever at 2010-01-28 47
public:
dee20a7 by Jos van den Oever at 2009-08-23 48
    unsigned char id[8];       // signature, or magic identifier
49
    unsigned b_shift;          // bbat->blockSize = 1 << b_shift
50
    unsigned s_shift;          // sbat->blockSize = 1 << s_shift
51
    unsigned num_bat;          // blocks allocated for big bat
52
    unsigned dirent_start;     // starting block for directory info
53
    unsigned threshold;        // switch from small to big file (usually 4K)
54
    unsigned sbat_start;       // starting block index to store small bat
55
    unsigned num_sbat;         // blocks allocated for small bat
56
    unsigned mbat_start;       // starting block to store meta bat
57
    unsigned num_mbat;         // blocks allocated for meta bat
58
    unsigned long bb_blocks[109];
59
60
    Header();
61
    bool valid();
6a7d8df by Jos van den Oever at 2010-01-28 62
    void load(const unsigned char* buffer);
63
    void save(unsigned char* buffer);
dee20a7 by Jos van den Oever at 2009-08-23 64
    void debug();
65
};
66
67
class AllocTable
68
{
6a7d8df by Jos van den Oever at 2010-01-28 69
public:
dee20a7 by Jos van den Oever at 2009-08-23 70
    static const unsigned Eof;
71
    static const unsigned Avail;
72
    static const unsigned Bat;
73
    static const unsigned MetaBat;
74
    unsigned blockSize;
75
    AllocTable();
76
    void clear();
77
    unsigned long count();
6a7d8df by Jos van den Oever at 2010-01-28 78
    void resize(unsigned long newsize);
79
    void preserve(unsigned long n);
80
    void set(unsigned long index, unsigned long val);
dee20a7 by Jos van den Oever at 2009-08-23 81
    unsigned unused();
6a7d8df by Jos van den Oever at 2010-01-28 82
    void setChain(std::vector<unsigned long>);
83
    std::vector<unsigned long> follow(unsigned long start);
84
    unsigned long operator[](unsigned long index);
85
    void load(const unsigned char* buffer, unsigned len);
86
    void save(unsigned char* buffer);
dee20a7 by Jos van den Oever at 2009-08-23 87
    unsigned size();
88
    void debug();
6a7d8df by Jos van den Oever at 2010-01-28 89
private:
dee20a7 by Jos van den Oever at 2009-08-23 90
    std::vector<unsigned long> data;
6a7d8df by Jos van den Oever at 2010-01-28 91
    AllocTable(const AllocTable&);
92
    AllocTable& operator=(const AllocTable&);
dee20a7 by Jos van den Oever at 2009-08-23 93
};
94
95
class DirEntry
96
{
6a7d8df by Jos van den Oever at 2010-01-28 97
public:
dee20a7 by Jos van den Oever at 2009-08-23 98
    bool valid;            // false if invalid (should be skipped)
99
    std::string name;      // the name, not in unicode anymore
100
    bool dir;              // true if directory
101
    unsigned long size;    // size (not valid if directory)
102
    unsigned long start;   // starting block
103
    unsigned prev;         // previous sibling
104
    unsigned next;         // next sibling
105
    unsigned child;        // first child
106
};
107
108
class DirTree
109
{
6a7d8df by Jos van den Oever at 2010-01-28 110
public:
dee20a7 by Jos van den Oever at 2009-08-23 111
    static const unsigned End;
112
    DirTree();
113
    void clear();
114
    unsigned entryCount();
6a7d8df by Jos van den Oever at 2010-01-28 115
    DirEntry* entry(unsigned index);
116
    DirEntry* entry(const std::string& name, bool create = false);
117
    int indexOf(DirEntry* e);
118
    int parent(unsigned index);
119
    std::string fullName(unsigned index);
120
    std::vector<unsigned> children(unsigned index);
121
    void load(unsigned char* buffer, unsigned len);
122
    void save(unsigned char* buffer);
dee20a7 by Jos van den Oever at 2009-08-23 123
    unsigned size();
124
    void debug();
6a7d8df by Jos van den Oever at 2010-01-28 125
private:
dee20a7 by Jos van den Oever at 2009-08-23 126
    std::vector<DirEntry> entries;
6a7d8df by Jos van den Oever at 2010-01-28 127
    DirTree(const DirTree&);
128
    DirTree& operator=(const DirTree&);
dee20a7 by Jos van den Oever at 2009-08-23 129
};
130
131
class StorageIO
132
{
6a7d8df by Jos van den Oever at 2010-01-28 133
public:
dee20a7 by Jos van den Oever at 2009-08-23 134
    Storage* storage;         // owner
135
    std::string filename;     // filename
136
    std::fstream file;        // associated with above name
137
    int result;               // result of operation
138
    bool opened;              // true if file is opened
139
    unsigned long filesize;   // size of the file
140
141
    Header* header;           // storage header
142
    DirTree* dirtree;         // directory tree
143
    AllocTable* bbat;         // allocation table for big blocks
144
    AllocTable* sbat;         // allocation table for small blocks
145
146
    std::vector<unsigned long> sb_blocks; // blocks for "small" files
147
148
    std::list<Stream*> streams;
149
6a7d8df by Jos van den Oever at 2010-01-28 150
    StorageIO(Storage* storage, const char* filename);
dee20a7 by Jos van den Oever at 2009-08-23 151
    ~StorageIO();
152
153
    bool open();
154
    void close();
155
    void flush();
156
    void load();
157
    void create();
158
6a7d8df by Jos van den Oever at 2010-01-28 159
    unsigned long loadBigBlocks(std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 160
6a7d8df by Jos van den Oever at 2010-01-28 161
    unsigned long loadBigBlock(unsigned long block, unsigned char* buffer, unsigned long maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 162
6a7d8df by Jos van den Oever at 2010-01-28 163
    unsigned long loadSmallBlocks(std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 164
6a7d8df by Jos van den Oever at 2010-01-28 165
    unsigned long loadSmallBlock(unsigned long block, unsigned char* buffer, unsigned long maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 166
6a7d8df by Jos van den Oever at 2010-01-28 167
    StreamIO* streamIO(const std::string& name);
dee20a7 by Jos van den Oever at 2009-08-23 168
6a7d8df by Jos van den Oever at 2010-01-28 169
private:
dee20a7 by Jos van den Oever at 2009-08-23 170
    // no copy or assign
6a7d8df by Jos van den Oever at 2010-01-28 171
    StorageIO(const StorageIO&);
172
    StorageIO& operator=(const StorageIO&);
dee20a7 by Jos van den Oever at 2009-08-23 173
174
};
175
176
class StreamIO
177
{
6a7d8df by Jos van den Oever at 2010-01-28 178
public:
dee20a7 by Jos van den Oever at 2009-08-23 179
    StorageIO* io;
180
    DirEntry* entry;
181
    std::string fullName;
182
    bool eof;
183
    bool fail;
184
6a7d8df by Jos van den Oever at 2010-01-28 185
    StreamIO(StorageIO* io, DirEntry* entry);
dee20a7 by Jos van den Oever at 2009-08-23 186
    ~StreamIO();
187
    unsigned long size();
6a7d8df by Jos van den Oever at 2010-01-28 188
    void seek(unsigned long pos);
dee20a7 by Jos van den Oever at 2009-08-23 189
    unsigned long tell();
190
    int getch();
6a7d8df by Jos van den Oever at 2010-01-28 191
    unsigned long read(unsigned char* data, unsigned long maxlen);
192
    unsigned long read(unsigned long pos, unsigned char* data, unsigned long maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 193
194
6a7d8df by Jos van den Oever at 2010-01-28 195
private:
dee20a7 by Jos van den Oever at 2009-08-23 196
    std::vector<unsigned long> blocks;
197
198
    // no copy or assign
6a7d8df by Jos van den Oever at 2010-01-28 199
    StreamIO(const StreamIO&);
200
    StreamIO& operator=(const StreamIO&);
dee20a7 by Jos van den Oever at 2009-08-23 201
202
    // pointer for read
203
    unsigned long m_pos;
204
205
    // simple cache system to speed-up getch()
206
    unsigned char* cache_data;
207
    unsigned long cache_size;
208
    unsigned long cache_pos;
209
    void updateCache();
210
};
211
212
} // namespace POLE
213
214
using namespace POLE;
215
6a7d8df by Jos van den Oever at 2010-01-28 216
static inline unsigned long readU16(const unsigned char* ptr)
dee20a7 by Jos van den Oever at 2009-08-23 217
{
6a7d8df by Jos van den Oever at 2010-01-28 218
    return ptr[0] + (ptr[1] << 8);
dee20a7 by Jos van den Oever at 2009-08-23 219
}
220
6a7d8df by Jos van den Oever at 2010-01-28 221
static inline unsigned long readU32(const unsigned char* ptr)
dee20a7 by Jos van den Oever at 2009-08-23 222
{
6a7d8df by Jos van den Oever at 2010-01-28 223
    return ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24);
dee20a7 by Jos van den Oever at 2009-08-23 224
}
225
6a7d8df by Jos van den Oever at 2010-01-28 226
static inline void writeU16(unsigned char* ptr, unsigned long data)
dee20a7 by Jos van den Oever at 2009-08-23 227
{
6a7d8df by Jos van den Oever at 2010-01-28 228
    ptr[0] = (unsigned char)(data & 0xff);
229
    ptr[1] = (unsigned char)((data >> 8) & 0xff);
dee20a7 by Jos van den Oever at 2009-08-23 230
}
231
6a7d8df by Jos van den Oever at 2010-01-28 232
static inline void writeU32(unsigned char* ptr, unsigned long data)
dee20a7 by Jos van den Oever at 2009-08-23 233
{
6a7d8df by Jos van den Oever at 2010-01-28 234
    ptr[0] = (unsigned char)(data & 0xff);
235
    ptr[1] = (unsigned char)((data >> 8) & 0xff);
236
    ptr[2] = (unsigned char)((data >> 16) & 0xff);
237
    ptr[3] = (unsigned char)((data >> 24) & 0xff);
dee20a7 by Jos van den Oever at 2009-08-23 238
}
239
6a7d8df by Jos van den Oever at 2010-01-28 240
static const unsigned char pole_magic[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
dee20a7 by Jos van den Oever at 2009-08-23 241
242
// =========== Header ==========
243
244
Header::Header()
245
{
6a7d8df by Jos van den Oever at 2010-01-28 246
    b_shift = 9;
247
    s_shift = 6;
248
    num_bat = 0;
249
    dirent_start = 0;
250
    threshold = 4096;
251
    sbat_start = 0;
252
    num_sbat = 0;
253
    mbat_start = 0;
254
    num_mbat = 0;
dee20a7 by Jos van den Oever at 2009-08-23 255
6a7d8df by Jos van den Oever at 2010-01-28 256
    for (unsigned i = 0; i < 8; i++)
257
        id[i] = pole_magic[i];
258
    for (unsigned i = 0; i < 109; i++)
259
        bb_blocks[i] = AllocTable::Avail;
dee20a7 by Jos van den Oever at 2009-08-23 260
}
261
262
bool Header::valid()
263
{
6a7d8df by Jos van den Oever at 2010-01-28 264
    if (threshold != 4096) return false;
265
    if (num_bat == 0) return false;
266
    if ((num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
267
    if ((num_bat < 109) && (num_mbat != 0)) return false;
268
    if (s_shift > b_shift) return false;
269
    if (b_shift <= 6) return false;
270
    if (b_shift >= 31) return false;
271
272
    return true;
273
}
274
275
void Header::load(const unsigned char* buffer)
276
{
277
    b_shift      = readU16(buffer + 0x1e); // sector shift
278
    s_shift      = readU16(buffer + 0x20); // mini sector shift
279
    num_bat      = readU32(buffer + 0x2c); // number of fat sectors
280
    dirent_start = readU32(buffer + 0x30); // first directory sector location
281
    threshold    = readU32(buffer + 0x38); // transaction signature number
282
    sbat_start   = readU32(buffer + 0x3c); // mini stream cutoff size
283
    num_sbat     = readU32(buffer + 0x40); // first mini fat sector location
284
    mbat_start   = readU32(buffer + 0x44); // first mini difat sector location
285
    num_mbat     = readU32(buffer + 0x48); // number of difat sectors
286
287
    for (unsigned i = 0; i < 8; i++)
288
        id[i] = buffer[i];
289
    for (unsigned i = 0; i < 109; i++)
290
        bb_blocks[i] = readU32(buffer + 0x4C + i * 4);
291
}
292
293
void Header::save(unsigned char* buffer)
294
{
295
    memset(buffer, 0, 0x4c);
296
    memcpy(buffer, pole_magic, 8);          // ole signature
297
    writeU32(buffer + 8, 0);                // unknown
298
    writeU32(buffer + 12, 0);               // unknown
299
    writeU32(buffer + 16, 0);               // unknown
300
    writeU16(buffer + 24, 0x003e);          // revision ?
301
    writeU16(buffer + 26, 3);               // version ?
302
    writeU16(buffer + 28, 0xfffe);          // unknown
303
    writeU16(buffer + 0x1e, b_shift);
304
    writeU16(buffer + 0x20, s_shift);
305
    writeU32(buffer + 0x2c, num_bat);
306
    writeU32(buffer + 0x30, dirent_start);
307
    writeU32(buffer + 0x38, threshold);
308
    writeU32(buffer + 0x3c, sbat_start);
309
    writeU32(buffer + 0x40, num_sbat);
310
    writeU32(buffer + 0x44, mbat_start);
311
    writeU32(buffer + 0x48, num_mbat);
312
313
    for (unsigned i = 0; i < 109; i++)
314
        writeU32(buffer + 0x4C + i*4, bb_blocks[i]);
dee20a7 by Jos van den Oever at 2009-08-23 315
}
316
317
void Header::debug()
318
{
6a7d8df by Jos van den Oever at 2010-01-28 319
    std::cout << std::endl;
320
    std::cout << "b_shift " << b_shift << std::endl;
321
    std::cout << "s_shift " << s_shift << std::endl;
322
    std::cout << "num_bat " << num_bat << std::endl;
323
    std::cout << "dirent_start " << dirent_start << std::endl;
324
    std::cout << "threshold " << threshold << std::endl;
325
    std::cout << "sbat_start " << sbat_start << std::endl;
326
    std::cout << "num_sbat " << num_sbat << std::endl;
327
    std::cout << "mbat_start " << mbat_start << std::endl;
328
    std::cout << "num_mbat " << num_mbat << std::endl;
329
330
    unsigned s = (num_bat <= 109) ? num_bat : 109;
331
    std::cout << "bat blocks: ";
332
    for (unsigned i = 0; i < s; i++)
333
        std::cout << bb_blocks[i] << " ";
334
    std::cout << std::endl;
dee20a7 by Jos van den Oever at 2009-08-23 335
}
336
337
// =========== AllocTable ==========
338
339
const unsigned AllocTable::Avail = 0xffffffff;
340
const unsigned AllocTable::Eof = 0xfffffffe;
341
const unsigned AllocTable::Bat = 0xfffffffd;
342
const unsigned AllocTable::MetaBat = 0xfffffffc;
343
344
AllocTable::AllocTable()
345
{
6a7d8df by Jos van den Oever at 2010-01-28 346
    blockSize = 4096;
347
    // initial size
348
    resize(128);
dee20a7 by Jos van den Oever at 2009-08-23 349
}
350
351
unsigned long AllocTable::count()
352
{
6a7d8df by Jos van den Oever at 2010-01-28 353
    return data.size();
dee20a7 by Jos van den Oever at 2009-08-23 354
}
355
6a7d8df by Jos van den Oever at 2010-01-28 356
void AllocTable::resize(unsigned long newsize)
dee20a7 by Jos van den Oever at 2009-08-23 357
{
6a7d8df by Jos van den Oever at 2010-01-28 358
    unsigned oldsize = data.size();
359
    data.resize(newsize);
360
    if (newsize > oldsize)
361
        for (unsigned i = oldsize; i < newsize; i++)
362
            data[i] = Avail;
dee20a7 by Jos van den Oever at 2009-08-23 363
}
364
365
// make sure there're still free blocks
6a7d8df by Jos van den Oever at 2010-01-28 366
void AllocTable::preserve(unsigned long n)
dee20a7 by Jos van den Oever at 2009-08-23 367
{
6a7d8df by Jos van den Oever at 2010-01-28 368
    std::vector<unsigned long> pre;
369
    for (unsigned i = 0; i < n; i++)
370
        pre.push_back(unused());
dee20a7 by Jos van den Oever at 2009-08-23 371
}
372
6a7d8df by Jos van den Oever at 2010-01-28 373
unsigned long AllocTable::operator[](unsigned long index)
dee20a7 by Jos van den Oever at 2009-08-23 374
{
6a7d8df by Jos van den Oever at 2010-01-28 375
    unsigned long result;
376
    result = data[index];
377
    return result;
dee20a7 by Jos van den Oever at 2009-08-23 378
}
379
6a7d8df by Jos van den Oever at 2010-01-28 380
void AllocTable::set(unsigned long index, unsigned long value)
dee20a7 by Jos van den Oever at 2009-08-23 381
{
6a7d8df by Jos van den Oever at 2010-01-28 382
    if (index >= count()) resize(index + 1);
383
    data[ index ] = value;
dee20a7 by Jos van den Oever at 2009-08-23 384
}
385
6a7d8df by Jos van den Oever at 2010-01-28 386
void AllocTable::setChain(std::vector<unsigned long> chain)
dee20a7 by Jos van den Oever at 2009-08-23 387
{
6a7d8df by Jos van den Oever at 2010-01-28 388
    if (chain.size()) {
389
        for (unsigned i = 0; i < chain.size() - 1; i++)
390
            set(chain[i], chain[i+1]);
391
        set(chain[ chain.size()-1 ], AllocTable::Eof);
392
    }
dee20a7 by Jos van den Oever at 2009-08-23 393
}
394
395
// follow
6a7d8df by Jos van den Oever at 2010-01-28 396
std::vector<unsigned long> AllocTable::follow(unsigned long start)
dee20a7 by Jos van den Oever at 2009-08-23 397
{
6a7d8df by Jos van den Oever at 2010-01-28 398
    std::vector<unsigned long> chain;
dee20a7 by Jos van den Oever at 2009-08-23 399
6a7d8df by Jos van den Oever at 2010-01-28 400
    if (start >= count()) return chain;
dee20a7 by Jos van den Oever at 2009-08-23 401
6a7d8df by Jos van den Oever at 2010-01-28 402
    unsigned long p = start;
403
    while (p < count()) {
404
        if (p == (unsigned long)Eof) break;
405
        if (p == (unsigned long)Bat) break;
406
        if (p == (unsigned long)MetaBat) break;
407
        if (p >= count()) break;
408
        chain.push_back(p);
409
        if (data[p] >= count()) break;
410
        p = data[ p ];
411
    }
dee20a7 by Jos van den Oever at 2009-08-23 412
6a7d8df by Jos van den Oever at 2010-01-28 413
    return chain;
dee20a7 by Jos van den Oever at 2009-08-23 414
}
415
416
unsigned AllocTable::unused()
417
{
6a7d8df by Jos van den Oever at 2010-01-28 418
    // find first available block
419
    for (unsigned i = 0; i < data.size(); i++)
420
        if (data[i] == Avail)
421
            return i;
dee20a7 by Jos van den Oever at 2009-08-23 422
6a7d8df by Jos van den Oever at 2010-01-28 423
    // completely full, so enlarge the table
424
    unsigned block = data.size();
425
    resize(data.size() + 10);
426
    return block;
dee20a7 by Jos van den Oever at 2009-08-23 427
}
428
6a7d8df by Jos van den Oever at 2010-01-28 429
void AllocTable::load(const unsigned char* buffer, unsigned len)
dee20a7 by Jos van den Oever at 2009-08-23 430
{
6a7d8df by Jos van den Oever at 2010-01-28 431
    resize(len / 4);
432
    for (unsigned i = 0; i < count(); i++)
433
        set(i, readU32(buffer + i*4));
dee20a7 by Jos van den Oever at 2009-08-23 434
}
435
436
// return space required to save this dirtree
437
unsigned AllocTable::size()
438
{
6a7d8df by Jos van den Oever at 2010-01-28 439
    return count() * 4;
dee20a7 by Jos van den Oever at 2009-08-23 440
}
441
6a7d8df by Jos van den Oever at 2010-01-28 442
void AllocTable::save(unsigned char* buffer)
dee20a7 by Jos van den Oever at 2009-08-23 443
{
6a7d8df by Jos van den Oever at 2010-01-28 444
    for (unsigned i = 0; i < count(); i++)
445
        writeU32(buffer + i*4, data[i]);
dee20a7 by Jos van den Oever at 2009-08-23 446
}
447
448
void AllocTable::debug()
449
{
6a7d8df by Jos van den Oever at 2010-01-28 450
    std::cout << "block size " << data.size() << std::endl;
451
    for (unsigned i = 0; i < data.size(); i++) {
452
        if (data[i] == Avail) continue;
453
        std::cout << i << ": ";
454
        if (data[i] == Eof) std::cout << "[eof]";
455
        else if (data[i] == Bat) std::cout << "[bat]";
456
        else if (data[i] == MetaBat) std::cout << "[metabat]";
457
        else std::cout << data[i];
458
        std::cout << std::endl;
459
    }
dee20a7 by Jos van den Oever at 2009-08-23 460
}
461
462
// =========== DirTree ==========
463
464
const unsigned DirTree::End = 0xffffffff;
465
466
DirTree::DirTree()
467
{
6a7d8df by Jos van den Oever at 2010-01-28 468
    clear();
dee20a7 by Jos van den Oever at 2009-08-23 469
}
470
471
void DirTree::clear()
472
{
6a7d8df by Jos van den Oever at 2010-01-28 473
    // leave only root entry
474
    entries.resize(1);
475
    entries[0].valid = true;
476
    entries[0].name = "Root Entry";
477
    entries[0].dir = true;
478
    entries[0].size = 0;
479
    entries[0].start = End;
480
    entries[0].prev = End;
481
    entries[0].next = End;
482
    entries[0].child = End;
dee20a7 by Jos van den Oever at 2009-08-23 483
}
484
485
unsigned DirTree::entryCount()
486
{
6a7d8df by Jos van den Oever at 2010-01-28 487
    return entries.size();
dee20a7 by Jos van den Oever at 2009-08-23 488
}
489
6a7d8df by Jos van den Oever at 2010-01-28 490
DirEntry* DirTree::entry(unsigned index)
dee20a7 by Jos van den Oever at 2009-08-23 491
{
6a7d8df by Jos van den Oever at 2010-01-28 492
    if (index >= entryCount()) return (DirEntry*) 0;
493
    return &entries[ index ];
dee20a7 by Jos van den Oever at 2009-08-23 494
}
495
6a7d8df by Jos van den Oever at 2010-01-28 496
int DirTree::indexOf(DirEntry* e)
dee20a7 by Jos van den Oever at 2009-08-23 497
{
6a7d8df by Jos van den Oever at 2010-01-28 498
    for (unsigned i = 0; i < entryCount(); i++)
499
        if (entry(i) == e) return i;
dee20a7 by Jos van den Oever at 2009-08-23 500
6a7d8df by Jos van den Oever at 2010-01-28 501
    return -1;
dee20a7 by Jos van den Oever at 2009-08-23 502
}
503
6a7d8df by Jos van den Oever at 2010-01-28 504
int DirTree::parent(unsigned index)
dee20a7 by Jos van den Oever at 2009-08-23 505
{
6a7d8df by Jos van den Oever at 2010-01-28 506
    // brute-force, basically we iterate for each entries, find its children
507
    // and check if one of the children is 'index'
508
    for (unsigned j = 0; j < entryCount(); j++) {
509
        std::vector<unsigned> chi = children(j);
510
        for (unsigned i = 0; i < chi.size();i++)
511
            if (chi[i] == index)
512
                return j;
513
    }
dee20a7 by Jos van den Oever at 2009-08-23 514
6a7d8df by Jos van den Oever at 2010-01-28 515
    return -1;
dee20a7 by Jos van den Oever at 2009-08-23 516
}
517
6a7d8df by Jos van den Oever at 2010-01-28 518
std::string DirTree::fullName(unsigned index)
dee20a7 by Jos van den Oever at 2009-08-23 519
{
6a7d8df by Jos van den Oever at 2010-01-28 520
    // don't use root name ("Root Entry"), just give "/"
521
    if (index == 0) return "/";
dee20a7 by Jos van den Oever at 2009-08-23 522
6a7d8df by Jos van den Oever at 2010-01-28 523
    std::string result = entry(index)->name;
524
    result.insert(0,  "/");
525
    int p = parent(index);
526
    DirEntry * _entry = 0;
527
    while (p > 0) {
528
        _entry = entry(p);
529
        if (_entry->dir && _entry->valid) {
530
            result.insert(0,  _entry->name);
531
            result.insert(0,  "/");
532
        }
533
        --p;
534
        index = p;
535
        if (index <= 0) break;
dee20a7 by Jos van den Oever at 2009-08-23 536
    }
6a7d8df by Jos van den Oever at 2010-01-28 537
    return result;
dee20a7 by Jos van den Oever at 2009-08-23 538
}
539
540
// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
541
// if not found and create is false, return 0
542
// if create is true, a new entry is returned
6a7d8df by Jos van den Oever at 2010-01-28 543
DirEntry* DirTree::entry(const std::string& name, bool create)
544
{
545
    if (!name.length()) return (DirEntry*)0;
546
547
    // quick check for "/" (that's root)
548
    if (name == "/") return entry(0);
549
550
    // split the names, e.g  "/ObjectPool/_1020961869" will become:
551
    // "ObjectPool" and "_1020961869"
552
    std::list<std::string> names;
553
    std::string::size_type start = 0, end = 0;
554
    if (name[0] == '/') start++;
555
    while (start < name.length()) {
556
        end = name.find_first_of('/', start);
557
        if (end == std::string::npos) end = name.length();
558
        names.push_back(name.substr(start, end - start));
559
        start = end + 1;
560
    }
dee20a7 by Jos van den Oever at 2009-08-23 561
6a7d8df by Jos van den Oever at 2010-01-28 562
    // start from root
563
    int index = 0 ;
564
565
    // trace one by one
566
    std::list<std::string>::iterator it;
567
568
    for (it = names.begin(); it != names.end(); ++it) {
569
        // find among the children of index
570
        std::vector<unsigned> chi = children(index);
571
        unsigned child = 0;
572
        for (unsigned i = 0; i < chi.size(); i++) {
573
            DirEntry* ce = entry(chi[i]);
574
            if (ce)
575
                if (ce->valid && (ce->name.length() > 1))
576
                    if (ce->name == *it)
577
                        child = chi[i];
578
        }
579
580
        // traverse to the child
581
        if (child > 0) index = child;
582
        else {
583
            // not found among children
584
            if (!create) return (DirEntry*)0;
585
586
            // create a new entry
587
            unsigned parent = index;
588
            entries.push_back(DirEntry());
589
            index = entryCount() - 1;
590
            DirEntry* e = entry(index);
591
            e->valid = true;
592
            e->name = *it;
593
            e->dir = false;
594
            e->size = 0;
595
            e->start = 0;
596
            e->child = End;
597
            e->prev = End;
598
            e->next = entry(parent)->child;
599
            entry(parent)->child = index;
600
        }
601
    }
dee20a7 by Jos van den Oever at 2009-08-23 602
6a7d8df by Jos van den Oever at 2010-01-28 603
    return entry(index);
604
}
dee20a7 by Jos van den Oever at 2009-08-23 605
6a7d8df by Jos van den Oever at 2010-01-28 606
// helper function: recursively find siblings of index
607
void dirtree_find_siblings(DirTree* dirtree, std::vector<unsigned>& result,
608
                           unsigned index)
609
{
610
    DirEntry* e = dirtree->entry(index);
611
    if (!e) return;
612
    if (!e->valid) return;
613
614
    // prevent infinite loop
615
    for (unsigned i = 0; i < result.size(); i++)
616
        if (result[i] == index) return;
617
618
    // add myself
619
    result.push_back(index);
620
621
    // visit previous sibling, don't go infinitely
622
    unsigned prev = e->prev;
623
    if ((prev > 0) && (prev < dirtree->entryCount())) {
624
        for (unsigned i = 0; i < result.size(); i++)
625
            if (result[i] == prev) prev = 0;
626
        if (prev) dirtree_find_siblings(dirtree, result, prev);
627
    }
dee20a7 by Jos van den Oever at 2009-08-23 628
6a7d8df by Jos van den Oever at 2010-01-28 629
    // visit next sibling, don't go infinitely
630
    unsigned next = e->next;
631
    if ((next > 0) && (next < dirtree->entryCount())) {
632
        for (unsigned i = 0; i < result.size(); i++)
633
            if (result[i] == next) next = 0;
634
        if (next) dirtree_find_siblings(dirtree, result, next);
635
    }
dee20a7 by Jos van den Oever at 2009-08-23 636
}
637
6a7d8df by Jos van den Oever at 2010-01-28 638
std::vector<unsigned> DirTree::children(unsigned index)
dee20a7 by Jos van den Oever at 2009-08-23 639
{
6a7d8df by Jos van den Oever at 2010-01-28 640
    std::vector<unsigned> result;
dee20a7 by Jos van den Oever at 2009-08-23 641
6a7d8df by Jos van den Oever at 2010-01-28 642
    DirEntry* e = entry(index);
643
    if (e) if (e->valid && e->child < entryCount())
644
            dirtree_find_siblings(this, result, e->child);
dee20a7 by Jos van den Oever at 2009-08-23 645
6a7d8df by Jos van den Oever at 2010-01-28 646
    return result;
dee20a7 by Jos van den Oever at 2009-08-23 647
}
648
6a7d8df by Jos van den Oever at 2010-01-28 649
void DirTree::load(unsigned char* buffer, unsigned size)
dee20a7 by Jos van den Oever at 2009-08-23 650
{
6a7d8df by Jos van den Oever at 2010-01-28 651
    entries.clear();
dee20a7 by Jos van den Oever at 2009-08-23 652
6a7d8df by Jos van den Oever at 2010-01-28 653
    for (unsigned i = 0; i < size / 128; i++) {
654
        unsigned p = i * 128;
dee20a7 by Jos van den Oever at 2009-08-23 655
6a7d8df by Jos van den Oever at 2010-01-28 656
        // would be < 32 if first char in the name isn't printable
657
        unsigned prefix = 32;
dee20a7 by Jos van den Oever at 2009-08-23 658
6a7d8df by Jos van den Oever at 2010-01-28 659
        // parse name of this entry, which stored as Unicode 16-bit
660
        std::string name;
661
        int name_len = readU16(buffer + 0x40 + p);
662
        if (name_len > 64) name_len = 64;
663
        for (int j = 0; (buffer[j+p]) && (j < name_len); j += 2)
664
            name.append(1, buffer[j+p]);
dee20a7 by Jos van den Oever at 2009-08-23 665
6a7d8df by Jos van den Oever at 2010-01-28 666
        // first char isn't printable ? remove it...
667
        if (buffer[p] < 32) {
668
            prefix = buffer[0];
669
            name.erase(0, 1);
670
        }
dee20a7 by Jos van den Oever at 2009-08-23 671
6a7d8df by Jos van den Oever at 2010-01-28 672
        // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
673
        unsigned type = buffer[ 0x42 + p];
dee20a7 by Jos van den Oever at 2009-08-23 674
6a7d8df by Jos van den Oever at 2010-01-28 675
        DirEntry e;
676
        e.valid = true;
677
        e.name = name;
678
        e.start = readU32(buffer + 0x74 + p);
679
        e.size = readU32(buffer + 0x78 + p);
680
        e.prev = readU32(buffer + 0x44 + p);
681
        e.next = readU32(buffer + 0x48 + p);
682
        e.child = readU32(buffer + 0x4C + p);
683
        e.dir = (type != 2);
684
        
685
        // sanity checks
686
        if ((type != 2) && (type != 1) && (type != 5)) e.valid = false;
687
        if (name_len < 1) e.valid = false;
dee20a7 by Jos van den Oever at 2009-08-23 688
6a7d8df by Jos van den Oever at 2010-01-28 689
        // CLSID, contains a object class GUI if this entry is a storage or root
690
        // storage or all zero if not.
9f955ab by Jos van den Oever at 2010-01-29 691
#ifdef POLE_DEBUG
6a7d8df by Jos van den Oever at 2010-01-28 692
        printf("DirTree::load name=%s type=%i prev=%i next=%i child=%i start=%i size=%i clsid=%i.%i.%i.%i\n",
693
               name.c_str(),type,e.prev,e.next,e.child,e.start,e.size,readU32(buffer+0x50+p),readU32(buffer+0x54+p),readU32(buffer+0x58+p),readU32(buffer+0x5C+p));
9f955ab by Jos van den Oever at 2010-01-29 694
#endif
6a7d8df by Jos van den Oever at 2010-01-28 695
        entries.push_back(e);
696
    }
dee20a7 by Jos van den Oever at 2009-08-23 697
}
698
699
// return space required to save this dirtree
700
unsigned DirTree::size()
701
{
6a7d8df by Jos van den Oever at 2010-01-28 702
    return entryCount() * 128;
703
}
704
705
void DirTree::save(unsigned char* buffer)
706
{
707
    memset(buffer, 0, size());
708
709
    // root is fixed as "Root Entry"
710
    DirEntry* root = entry(0);
711
    std::string name = "Root Entry";
712
    for (unsigned j = 0; j < name.length(); j++)
713
        buffer[ j*2 ] = name[j];
714
    writeU16(buffer + 0x40, name.length()*2 + 2);
715
    writeU32(buffer + 0x74, 0xffffffff);
716
    writeU32(buffer + 0x78, 0);
717
    writeU32(buffer + 0x44, 0xffffffff);
718
    writeU32(buffer + 0x48, 0xffffffff);
719
    writeU32(buffer + 0x4c, root->child);
720
    buffer[ 0x42 ] = 5;
721
    buffer[ 0x43 ] = 1;
722
723
    for (unsigned i = 1; i < entryCount(); i++) {
724
        DirEntry* e = entry(i);
725
        if (!e) continue;
726
        if (e->dir) {
727
            e->start = 0xffffffff;
728
            e->size = 0;
729
        }
730
731
        // max length for name is 32 chars
732
        std::string name = e->name;
733
        if (name.length() > 32)
734
            name.erase(32, name.length());
735
736
        // write name as Unicode 16-bit
737
        for (unsigned j = 0; j < name.length(); j++)
738
            buffer[ i*128 + j*2 ] = name[j];
739
740
        writeU16(buffer + i*128 + 0x40, name.length()*2 + 2);
741
        writeU32(buffer + i*128 + 0x74, e->start);
742
        writeU32(buffer + i*128 + 0x78, e->size);
743
        writeU32(buffer + i*128 + 0x44, e->prev);
744
        writeU32(buffer + i*128 + 0x48, e->next);
745
        writeU32(buffer + i*128 + 0x4c, e->child);
746
        buffer[ i*128 + 0x42 ] = e->dir ? 1 : 2;
747
        buffer[ i*128 + 0x43 ] = 1; // always black
dee20a7 by Jos van den Oever at 2009-08-23 748
    }
749
}
750
751
void DirTree::debug()
752
{
6a7d8df by Jos van den Oever at 2010-01-28 753
    for (unsigned i = 0; i < entryCount(); i++) {
754
        DirEntry* e = entry(i);
755
        if (!e) continue;
756
        std::cout << i << ": ";
757
        if (!e->valid) std::cout << "INVALID ";
758
        std::cout << e->name << " ";
759
        if (e->dir) std::cout << "(Dir) ";
760
        else std::cout << "(File) ";
761
        std::cout << e->size << " ";
762
        std::cout << "s:" << e->start << " ";
763
        std::cout << "(";
764
        if (e->child == End) std::cout << "-"; else std::cout << e->child;
765
        std::cout << " ";
766
        if (e->prev == End) std::cout << "-"; else std::cout << e->prev;
767
        std::cout << ":";
768
        if (e->next == End) std::cout << "-"; else std::cout << e->next;
769
        std::cout << ")";
770
        std::cout << std::endl;
771
    }
dee20a7 by Jos van den Oever at 2009-08-23 772
}
773
774
// =========== StorageIO ==========
775
6a7d8df by Jos van den Oever at 2010-01-28 776
StorageIO::StorageIO(Storage* st, const char* fname)
dee20a7 by Jos van den Oever at 2009-08-23 777
{
6a7d8df by Jos van den Oever at 2010-01-28 778
    storage = st;
779
    filename = fname;
780
    result = Storage::Ok;
781
    opened = false;
dee20a7 by Jos van den Oever at 2009-08-23 782
6a7d8df by Jos van den Oever at 2010-01-28 783
    header = new Header();
784
    dirtree = new DirTree();
785
    bbat = new AllocTable();
786
    sbat = new AllocTable();
dee20a7 by Jos van den Oever at 2009-08-23 787
6a7d8df by Jos van den Oever at 2010-01-28 788
    filesize = 0;
789
    bbat->blockSize = 1 << header->b_shift;
790
    sbat->blockSize = 1 << header->s_shift;
dee20a7 by Jos van den Oever at 2009-08-23 791
}
792
793
StorageIO::~StorageIO()
794
{
6a7d8df by Jos van den Oever at 2010-01-28 795
    if (opened) close();
796
    delete sbat;
797
    delete bbat;
798
    delete dirtree;
799
    delete header;
dee20a7 by Jos van den Oever at 2009-08-23 800
}
801
802
bool StorageIO::open()
803
{
6a7d8df by Jos van den Oever at 2010-01-28 804
    // already opened ? close first
805
    if (opened) close();
dee20a7 by Jos van den Oever at 2009-08-23 806
6a7d8df by Jos van den Oever at 2010-01-28 807
    load();
dee20a7 by Jos van den Oever at 2009-08-23 808
6a7d8df by Jos van den Oever at 2010-01-28 809
    return result == Storage::Ok;
dee20a7 by Jos van den Oever at 2009-08-23 810
}
811
812
void StorageIO::load()
813
{
6a7d8df by Jos van den Oever at 2010-01-28 814
    unsigned char* buffer = 0;
815
    unsigned long buflen = 0;
816
    std::vector<unsigned long> blocks;
817
818
    // open the file, check for error
819
    result = Storage::OpenFailed;
820
    file.open(filename.c_str(), std::ios::binary | std::ios::in);
821
    if (!file.good()) return;
822
823
    // find size of input file
824
    file.seekg(0, std::ios::end);
825
    filesize = file.tellg();
826
827
    // load header
828
    buffer = new unsigned char[512];
829
    file.seekg(0);
830
    file.read((char*)buffer, 512);
831
    if (!file.good()) {
832
        delete[] buffer;
833
        return;
834
    }
835
    header->load(buffer);
dee20a7 by Jos van den Oever at 2009-08-23 836
    delete[] buffer;
6a7d8df by Jos van den Oever at 2010-01-28 837
838
    // check OLE magic id
839
    result = Storage::NotOLE;
840
    for (unsigned i = 0; i < 8; i++)
841
        if (header->id[i] != pole_magic[i])
842
            return;
843
844
    // sanity checks
845
    result = Storage::BadOLE;
846
    if (!header->valid()) return;
847
    if (header->threshold != 4096) return;
848
849
    // important block size
850
    bbat->blockSize = 1 << header->b_shift;
851
    sbat->blockSize = 1 << header->s_shift;
852
853
    // find blocks allocated to store big bat
854
    // the first 109 blocks are in header, the rest in meta bat
855
    blocks.clear();
856
    blocks.resize(header->num_bat);
857
    for (unsigned i = 0; i < 109; i++)
858
        if (i >= header->num_bat) break;
859
        else blocks[i] = header->bb_blocks[i];
860
    if ((header->num_bat > 109) && (header->num_mbat > 0)) {
861
        unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
862
        unsigned k = 109;
863
        unsigned mblock = header->mbat_start;
864
        for (unsigned r = 0; r < header->num_mbat; r++) {
9f955ab by Jos van den Oever at 2010-01-29 865
            unsigned long rr = loadBigBlock(mblock, buffer2, bbat->blockSize);
866
            if (rr != bbat->blockSize) {
6a7d8df by Jos van den Oever at 2010-01-28 867
                delete[] buffer2;
868
                return;
869
            }
870
            for (unsigned s = 0; s < bbat->blockSize - 4; s += 4) {
871
                if (k >= header->num_bat) break;
872
                else  blocks[k++] = readU32(buffer2 + s);
873
            }
874
            mblock = readU32(buffer2 + bbat->blockSize - 4);
875
        }
876
        delete[] buffer2;
877
    }
878
879
    // load big bat
880
    buflen = blocks.size() * bbat->blockSize;
881
    if (buflen > 0) {
882
        buffer = new unsigned char[ buflen ];
883
        unsigned long r = loadBigBlocks(blocks, buffer, buflen);
884
        if (r != buflen) {
885
            delete[] buffer;
886
            return;
887
        }
9f955ab by Jos van den Oever at 2010-01-29 888
        bbat->load(buffer, buflen);
6a7d8df by Jos van den Oever at 2010-01-28 889
        delete[] buffer;
890
    }
891
892
    // load small bat
893
    blocks.clear();
894
    blocks = bbat->follow(header->sbat_start);
895
    buflen = blocks.size() * bbat->blockSize;
896
    if (buflen > 0) {
897
        buffer = new unsigned char[ buflen ];
898
        unsigned long r = loadBigBlocks(blocks, buffer, buflen);
899
        if (r != buflen) {
900
            delete[] buffer;
901
            return;
902
        }
903
        sbat->load(buffer, buflen);
904
        delete[] buffer;
905
    }
906
907
    // load directory tree
908
    blocks.clear();
909
    blocks = bbat->follow(header->dirent_start);
910
    buflen = blocks.size() * bbat->blockSize;
dee20a7 by Jos van den Oever at 2009-08-23 911
    buffer = new unsigned char[ buflen ];
6a7d8df by Jos van den Oever at 2010-01-28 912
    unsigned long r = loadBigBlocks(blocks, buffer, buflen);
913
    if (r != buflen) {
914
        delete[] buffer;
915
        return;
916
    }
917
    dirtree->load(buffer, buflen);
918
    unsigned sb_start = readU32(buffer + 0x74);
dee20a7 by Jos van den Oever at 2009-08-23 919
    delete[] buffer;
6a7d8df by Jos van den Oever at 2010-01-28 920
921
    // fetch block chain as data for small-files
922
    sb_blocks = bbat->follow(sb_start);   // small files
923
924
    // for troubleshooting, just enable this block
925
#ifdef POLE_DEBUG
926
    header->debug();
927
    sbat->debug();
928
    bbat->debug();
929
    dirtree->debug();
dee20a7 by Jos van den Oever at 2009-08-23 930
#endif
931
6a7d8df by Jos van den Oever at 2010-01-28 932
    // so far so good
933
    result = Storage::Ok;
934
    opened = true;
dee20a7 by Jos van den Oever at 2009-08-23 935
}
936
937
void StorageIO::create()
938
{
6a7d8df by Jos van den Oever at 2010-01-28 939
    // std::cout << "Creating " << filename << std::endl;
dee20a7 by Jos van den Oever at 2009-08-23 940
6a7d8df by Jos van den Oever at 2010-01-28 941
    file.open(filename.c_str(), std::ios::out | std::ios::binary);
942
    if (!file.good()) {
943
        std::cerr << "Can't create " << filename << std::endl;
944
        result = Storage::OpenFailed;
945
        return;
946
    }
dee20a7 by Jos van den Oever at 2009-08-23 947
6a7d8df by Jos van den Oever at 2010-01-28 948
    // so far so good
949
    opened = true;
950
    result = Storage::Ok;
dee20a7 by Jos van den Oever at 2009-08-23 951
}
952
953
void StorageIO::flush()
954
{
6a7d8df by Jos van den Oever at 2010-01-28 955
    /* Note on Microsoft implementation:
956
       - directory entries are stored in the last block(s)
957
       - BATs are as second to the last
958
       - Meta BATs are third to the last
959
    */
dee20a7 by Jos van den Oever at 2009-08-23 960
}
961
962
void StorageIO::close()
963
{
6a7d8df by Jos van den Oever at 2010-01-28 964
    if (!opened) return;
dee20a7 by Jos van den Oever at 2009-08-23 965
6a7d8df by Jos van den Oever at 2010-01-28 966
    file.close();
967
    opened = false;
dee20a7 by Jos van den Oever at 2009-08-23 968
6a7d8df by Jos van den Oever at 2010-01-28 969
    std::list<Stream*>::iterator it;
970
    for (it = streams.begin(); it != streams.end(); ++it)
971
        delete *it;
dee20a7 by Jos van den Oever at 2009-08-23 972
}
973
6a7d8df by Jos van den Oever at 2010-01-28 974
StreamIO* StorageIO::streamIO(const std::string& name)
dee20a7 by Jos van den Oever at 2009-08-23 975
{
6a7d8df by Jos van den Oever at 2010-01-28 976
    // sanity check
977
    if (!name.length()) return (StreamIO*)0;
dee20a7 by Jos van den Oever at 2009-08-23 978
6a7d8df by Jos van den Oever at 2010-01-28 979
    // search in the entries
980
    DirEntry* entry = dirtree->entry(name);
981
    //if( entry) std::cout << "FOUND\n";
982
    if (!entry) return (StreamIO*)0;
983
    //if( !entry->dir ) std::cout << "  NOT DIR\n";
984
    if (entry->dir) return (StreamIO*)0;
dee20a7 by Jos van den Oever at 2009-08-23 985
6a7d8df by Jos van den Oever at 2010-01-28 986
    StreamIO* result = new StreamIO(this, entry);
987
    result->fullName = name;
dee20a7 by Jos van den Oever at 2009-08-23 988
6a7d8df by Jos van den Oever at 2010-01-28 989
    return result;
dee20a7 by Jos van den Oever at 2009-08-23 990
}
991
6a7d8df by Jos van den Oever at 2010-01-28 992
unsigned long StorageIO::loadBigBlocks(std::vector<unsigned long> blocks,
993
                                       unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 994
{
6a7d8df by Jos van den Oever at 2010-01-28 995
    // sentinel
996
    if (!data) return 0;
997
    if (!file.good()) return 0;
998
    if (blocks.size() < 1) return 0;
999
    if (maxlen == 0) return 0;
dee20a7 by Jos van den Oever at 2009-08-23 1000
6a7d8df by Jos van den Oever at 2010-01-28 1001
    // read block one by one, seems fast enough
1002
    unsigned long bytes = 0;
1003
    for (unsigned long i = 0; (i < blocks.size()) && (bytes < maxlen); i++) {
1004
        unsigned long block = blocks[i];
1005
        unsigned long pos =  bbat->blockSize * (block + 1);
1006
        unsigned long p = (bbat->blockSize < maxlen - bytes) ? bbat->blockSize : maxlen - bytes;
1007
        if (pos + p > filesize) p = filesize - pos;
1008
        file.seekg(pos);
1009
        file.read((char*)data + bytes, p);
1010
        if (!file.good()) return 0;
1011
        bytes += p;
1012
    }
dee20a7 by Jos van den Oever at 2009-08-23 1013
6a7d8df by Jos van den Oever at 2010-01-28 1014
    return bytes;
dee20a7 by Jos van den Oever at 2009-08-23 1015
}
1016
6a7d8df by Jos van den Oever at 2010-01-28 1017
unsigned long StorageIO::loadBigBlock(unsigned long block,
1018
                                      unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 1019
{
6a7d8df by Jos van den Oever at 2010-01-28 1020
    // sentinel
1021
    if (!data) return 0;
1022
    if (!file.good()) return 0;
dee20a7 by Jos van den Oever at 2009-08-23 1023
6a7d8df by Jos van den Oever at 2010-01-28 1024
    // wraps call for loadBigBlocks
1025
    std::vector<unsigned long> blocks;
1026
    blocks.resize(1);
1027
    blocks[ 0 ] = block;
dee20a7 by Jos van den Oever at 2009-08-23 1028
6a7d8df by Jos van den Oever at 2010-01-28 1029
    return loadBigBlocks(blocks, data, maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 1030
}
1031
1032
// return number of bytes which has been read
6a7d8df by Jos van den Oever at 2010-01-28 1033
unsigned long StorageIO::loadSmallBlocks(std::vector<unsigned long> blocks,
1034
        unsigned char* data, unsigned long maxlen)
1035
{
1036
    // sentinel
1037
    if (!data) return 0;
1038
    if (!file.good()) return 0;
1039
    if (blocks.size() < 1) return 0;
1040
    if (maxlen == 0) return 0;
1041
1042
    // our own local buffer
1043
    unsigned char* buf = new unsigned char[ bbat->blockSize ];
1044
1045
    // read small block one by one
1046
    unsigned long bytes = 0;
1047
    for (unsigned long i = 0; (i < blocks.size()) && (bytes < maxlen); i++) {
1048
        unsigned long block = blocks[i];
1049
1050
        // find where the small-block exactly is
1051
        unsigned long pos = block * sbat->blockSize;
1052
        unsigned long bbindex = pos / bbat->blockSize;
1053
        if (bbindex >= sb_blocks.size()) break;
1054
1055
        unsigned long r = loadBigBlock(sb_blocks[ bbindex ], buf, bbat->blockSize);
1056
        if (r != bbat->blockSize) {
1057
            delete[] buf;
1058
            return 0;
1059
        }
1060
1061
        // copy the data
1062
        unsigned offset = pos % bbat->blockSize;
1063
        unsigned long p = (maxlen - bytes < bbat->blockSize - offset) ? maxlen - bytes :  bbat->blockSize - offset;
1064
        p = (sbat->blockSize < p) ? sbat->blockSize : p;
1065
        memcpy(data + bytes, buf + offset, p);
1066
        bytes += p;
1067
    }
dee20a7 by Jos van den Oever at 2009-08-23 1068
6a7d8df by Jos van den Oever at 2010-01-28 1069
    delete[] buf;
dee20a7 by Jos van den Oever at 2009-08-23 1070
6a7d8df by Jos van den Oever at 2010-01-28 1071
    return bytes;
dee20a7 by Jos van den Oever at 2009-08-23 1072
}
1073
6a7d8df by Jos van den Oever at 2010-01-28 1074
unsigned long StorageIO::loadSmallBlock(unsigned long block,
1075
                                        unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 1076
{
6a7d8df by Jos van den Oever at 2010-01-28 1077
    // sentinel
1078
    if (!data) return 0;
1079
    if (!file.good()) return 0;
dee20a7 by Jos van den Oever at 2009-08-23 1080
6a7d8df by Jos van den Oever at 2010-01-28 1081
    // wraps call for loadSmallBlocks
1082
    std::vector<unsigned long> blocks;
1083
    blocks.resize(1);
1084
    blocks.assign(1, block);
dee20a7 by Jos van den Oever at 2009-08-23 1085
6a7d8df by Jos van den Oever at 2010-01-28 1086
    return loadSmallBlocks(blocks, data, maxlen);
dee20a7 by Jos van den Oever at 2009-08-23 1087
}
1088
1089
// =========== StreamIO ==========
1090
6a7d8df by Jos van den Oever at 2010-01-28 1091
StreamIO::StreamIO(StorageIO* s, DirEntry* e)
dee20a7 by Jos van den Oever at 2009-08-23 1092
{
6a7d8df by Jos van den Oever at 2010-01-28 1093
    io = s;
1094
    entry = e;
1095
    eof = false;
1096
    fail = false;
dee20a7 by Jos van den Oever at 2009-08-23 1097
6a7d8df by Jos van den Oever at 2010-01-28 1098
    m_pos = 0;
dee20a7 by Jos van den Oever at 2009-08-23 1099
6a7d8df by Jos van den Oever at 2010-01-28 1100
    if (entry->size >= io->header->threshold)
1101
        blocks = io->bbat->follow(entry->start);
1102
    else
1103
        blocks = io->sbat->follow(entry->start);
dee20a7 by Jos van den Oever at 2009-08-23 1104
6a7d8df by Jos van den Oever at 2010-01-28 1105
    // prepare cache
1106
    cache_pos = 0;
1107
    cache_size = 4096; // optimal ?
1108
    cache_data = new unsigned char[cache_size];
1109
    updateCache();
dee20a7 by Jos van den Oever at 2009-08-23 1110
}
1111
1112
// FIXME tell parent we're gone
1113
StreamIO::~StreamIO()
1114
{
6a7d8df by Jos van den Oever at 2010-01-28 1115
    delete[] cache_data;
dee20a7 by Jos van den Oever at 2009-08-23 1116
}
1117
6a7d8df by Jos van den Oever at 2010-01-28 1118
void StreamIO::seek(unsigned long pos)
dee20a7 by Jos van den Oever at 2009-08-23 1119
{
6a7d8df by Jos van den Oever at 2010-01-28 1120
    m_pos = pos;
dee20a7 by Jos van den Oever at 2009-08-23 1121
}
1122
1123
unsigned long StreamIO::tell()
1124
{
6a7d8df by Jos van den Oever at 2010-01-28 1125
    return m_pos;
dee20a7 by Jos van den Oever at 2009-08-23 1126
}
1127
1128
int StreamIO::getch()
1129
{
6a7d8df by Jos van den Oever at 2010-01-28 1130
    // past end-of-file ?
1131
    if (m_pos > entry->size) return -1;
dee20a7 by Jos van den Oever at 2009-08-23 1132
6a7d8df by Jos van den Oever at 2010-01-28 1133
    // need to update cache ?
1134
    if (!cache_size || (m_pos < cache_pos) ||
1135
            (m_pos >= cache_pos + cache_size))
1136
        updateCache();
dee20a7 by Jos van den Oever at 2009-08-23 1137
6a7d8df by Jos van den Oever at 2010-01-28 1138
    // something bad if we don't get good cache
1139
    if (!cache_size) return -1;
dee20a7 by Jos van den Oever at 2009-08-23 1140
6a7d8df by Jos van den Oever at 2010-01-28 1141
    int data = cache_data[m_pos - cache_pos];
1142
    m_pos++;
dee20a7 by Jos van den Oever at 2009-08-23 1143
6a7d8df by Jos van den Oever at 2010-01-28 1144
    return data;
dee20a7 by Jos van den Oever at 2009-08-23 1145
}
1146
6a7d8df by Jos van den Oever at 2010-01-28 1147
unsigned long StreamIO::read(unsigned long pos, unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 1148
{
6a7d8df by Jos van den Oever at 2010-01-28 1149
    // sanity checks
1150
    if (!data) return 0;
1151
    if (maxlen == 0) return 0;
1152
1153
    unsigned long totalbytes = 0;
1154
1155
    if (entry->size < io->header->threshold) {
1156
        // small file
1157
        unsigned long index = pos / io->sbat->blockSize;
1158
1159
        if (index >= blocks.size()) return 0;
1160
1161
        unsigned char* buf = new unsigned char[ io->sbat->blockSize ];
1162
        unsigned long offset = pos % io->sbat->blockSize;
1163
        while (totalbytes < maxlen) {
1164
            if (index >= blocks.size()) break;
1165
            io->loadSmallBlock(blocks[index], buf, io->bbat->blockSize);
1166
            unsigned long count = io->sbat->blockSize - offset;
1167
            if (count > maxlen - totalbytes) count = maxlen - totalbytes;
1168
            memcpy(data + totalbytes, buf + offset, count);
1169
            totalbytes += count;
1170
            offset = 0;
1171
            index++;
1172
        }
1173
        delete[] buf;
1174
1175
    } else {
1176
        // big file
1177
        unsigned long index = pos / io->bbat->blockSize;
1178
1179
        if (index >= blocks.size()) return 0;
1180
1181
        unsigned char* buf = new unsigned char[ io->bbat->blockSize ];
1182
        unsigned long offset = pos % io->bbat->blockSize;
1183
        while (totalbytes < maxlen) {
1184
            if (index >= blocks.size()) break;
1185
            unsigned long r = io->loadBigBlock(blocks[index], buf, io->bbat->blockSize);
1186
            if (r != io->bbat->blockSize) {
1187
                delete [] buf;
1188
                return 0;
1189
            }
1190
            unsigned long count = io->bbat->blockSize - offset;
1191
            if (count > maxlen - totalbytes) count = maxlen - totalbytes;
1192
            memcpy(data + totalbytes, buf + offset, count);
1193
            totalbytes += count;
1194
            index++;
1195
            offset = 0;
1196
        }
1197
        delete [] buf;
dee20a7 by Jos van den Oever at 2009-08-23 1198
1199
    }
1200
6a7d8df by Jos van den Oever at 2010-01-28 1201
    return totalbytes;
dee20a7 by Jos van den Oever at 2009-08-23 1202
}
1203
6a7d8df by Jos van den Oever at 2010-01-28 1204
unsigned long StreamIO::read(unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 1205
{
6a7d8df by Jos van den Oever at 2010-01-28 1206
    unsigned long bytes = read(tell(), data, maxlen);
1207
    m_pos += bytes;
1208
    return bytes;
dee20a7 by Jos van den Oever at 2009-08-23 1209
}
1210
1211
void StreamIO::updateCache()
1212
{
6a7d8df by Jos van den Oever at 2010-01-28 1213
    // sanity check
1214
    if (!cache_data) return;
dee20a7 by Jos van den Oever at 2009-08-23 1215
6a7d8df by Jos van den Oever at 2010-01-28 1216
    cache_pos = m_pos - (m_pos % cache_size);
1217
    unsigned long bytes = cache_size;
1218
    if (cache_pos + bytes > entry->size) bytes = entry->size - cache_pos;
1219
    cache_size = read(cache_pos, cache_data, bytes);
dee20a7 by Jos van den Oever at 2009-08-23 1220
}
1221
1222
1223
// =========== Storage ==========
1224
6a7d8df by Jos van den Oever at 2010-01-28 1225
Storage::Storage(const char* filename)
dee20a7 by Jos van den Oever at 2009-08-23 1226
{
6a7d8df by Jos van den Oever at 2010-01-28 1227
    io = new StorageIO(this, filename);
dee20a7 by Jos van den Oever at 2009-08-23 1228
}
1229
1230
Storage::~Storage()
1231
{
6a7d8df by Jos van den Oever at 2010-01-28 1232
    delete io;
dee20a7 by Jos van den Oever at 2009-08-23 1233
}
1234
1235
int Storage::result()
1236
{
6a7d8df by Jos van den Oever at 2010-01-28 1237
    return io->result;
dee20a7 by Jos van den Oever at 2009-08-23 1238
}
1239
1240
bool Storage::open()
1241
{
6a7d8df by Jos van den Oever at 2010-01-28 1242
    return io->open();
dee20a7 by Jos van den Oever at 2009-08-23 1243
}
1244
1245
void Storage::close()
1246
{
6a7d8df by Jos van den Oever at 2010-01-28 1247
    io->close();
dee20a7 by Jos van den Oever at 2009-08-23 1248
}
1249
6a7d8df by Jos van den Oever at 2010-01-28 1250
std::list<std::string> Storage::entries(const std::string& path)
dee20a7 by Jos van den Oever at 2009-08-23 1251
{
6a7d8df by Jos van den Oever at 2010-01-28 1252
    std::list<std::string> result;
1253
    DirTree* dt = io->dirtree;
1254
    DirEntry* e = dt->entry(path, false);
1255
    if (e) {
1256
        if (e->dir) {
1257
            unsigned parent = dt->indexOf(e);
1258
            std::vector<unsigned> children = dt->children(parent);
1259
            for (unsigned i = 0; i < children.size(); i++)
1260
                result.push_back(dt->entry(children[i])->name);
1261
        }
1262
    }
1263
    return result;
dee20a7 by Jos van den Oever at 2009-08-23 1264
}
1265
6a7d8df by Jos van den Oever at 2010-01-28 1266
bool Storage::isDirectory(const std::string& name)
dee20a7 by Jos van den Oever at 2009-08-23 1267
{
6a7d8df by Jos van den Oever at 2010-01-28 1268
    DirEntry* e = io->dirtree->entry(name, false);
1269
    return e ? e->dir : false;
dee20a7 by Jos van den Oever at 2009-08-23 1270
}
1271
1272
// =========== Stream ==========
1273
6a7d8df by Jos van den Oever at 2010-01-28 1274
Stream::Stream(Storage* storage, const std::string& name)
dee20a7 by Jos van den Oever at 2009-08-23 1275
{
6a7d8df by Jos van den Oever at 2010-01-28 1276
    io = storage->io->streamIO(name);
dee20a7 by Jos van den Oever at 2009-08-23 1277
}
1278
1279
// FIXME tell parent we're gone
1280
Stream::~Stream()
1281
{
6a7d8df by Jos van den Oever at 2010-01-28 1282
    delete io;
dee20a7 by Jos van den Oever at 2009-08-23 1283
}
1284
1285
std::string Stream::fullName()
1286
{
6a7d8df by Jos van den Oever at 2010-01-28 1287
    return io ? io->fullName : std::string();
dee20a7 by Jos van den Oever at 2009-08-23 1288
}
1289
1290
unsigned long Stream::tell()
1291
{
6a7d8df by Jos van den Oever at 2010-01-28 1292
    return io ? io->tell() : 0;
dee20a7 by Jos van den Oever at 2009-08-23 1293
}
1294
6a7d8df by Jos van den Oever at 2010-01-28 1295
void Stream::seek(unsigned long newpos)
dee20a7 by Jos van den Oever at 2009-08-23 1296
{
6a7d8df by Jos van den Oever at 2010-01-28 1297
    if (io) io->seek(newpos);
dee20a7 by Jos van den Oever at 2009-08-23 1298
}
1299
1300
unsigned long Stream::size()
1301
{
6a7d8df by Jos van den Oever at 2010-01-28 1302
    return io ? io->entry->size : 0;
dee20a7 by Jos van den Oever at 2009-08-23 1303
}
1304
1305
int Stream::getch()
1306
{
6a7d8df by Jos van den Oever at 2010-01-28 1307
    return io ? io->getch() : 0;
dee20a7 by Jos van den Oever at 2009-08-23 1308
}
1309
6a7d8df by Jos van den Oever at 2010-01-28 1310
unsigned long Stream::read(unsigned char* data, unsigned long maxlen)
dee20a7 by Jos van den Oever at 2009-08-23 1311
{
6a7d8df by Jos van den Oever at 2010-01-28 1312
    return io ? io->read(data, maxlen) : 0;
dee20a7 by Jos van den Oever at 2009-08-23 1313
}
1314
1315
bool Stream::eof()
1316
{
6a7d8df by Jos van den Oever at 2010-01-28 1317
    return io ? io->eof : false;
dee20a7 by Jos van den Oever at 2009-08-23 1318
}
1319
1320
bool Stream::fail()
1321
{
6a7d8df by Jos van den Oever at 2010-01-28 1322
    return io ? io->fail : true;
dee20a7 by Jos van den Oever at 2009-08-23 1323
}