├── AUTHORS ├── COMPILERS.txt ├── ChangeLog ├── LICENSE ├── README ├── pole.cpp ├── pole.h ├── poledump ├── poledump.cpp ├── poleview.cpp ├── poleview.h ├── poleview.pro └── testbed.cpp /AUTHORS: -------------------------------------------------------------------------------- 1 | Ariya Hidayat 2 | Dmitry Fedorov 3 | Michel Boudinot 4 | Stephen Baum 5 | -------------------------------------------------------------------------------- /COMPILERS.txt: -------------------------------------------------------------------------------- 1 | VisualStudio project file 6-2008 2 | ------------------------------ 3 | 4 | qmake -t vcapp -spec win32-msvc poleview.pro 5 | qmake -t vcapp -spec win32-msvc.net poleview.pro 6 | qmake -t vcapp -spec win32-msvc2005 poleview.pro 7 | qmake -t vcapp -spec win32-msvc2008 poleview.pro 8 | 9 | 10 | Intel compiler under windows 11 | ------------------------------ 12 | qmake -spec win32-icc poleview.pro 13 | 14 | 15 | XCode for MacOSX 16 | ------------------------------ 17 | qmake -spec macx-xcode poleview.pro 18 | 19 | 20 | Borland C++ Compiler 5.5.1 21 | ------------------------------ 22 | The command-line Borland C++ Compiler version 5.5.1 is available for free 23 | (as 'free' in 'free beer') from: 24 | http://www.borland.com/products/downloads/download_cbuilder.html 25 | 26 | OpenWatcom 1.3 27 | ------------------------------ 28 | OpenWatcom 1.3 does not have STL implementation, therefore you need to 29 | install STLport (tested with version 4.6, earlier version may or may not 30 | work). Because mainstream STLport can not be used directly with OpenWatcom, 31 | please download and use the patched version from this site: 32 | http://www.ecet.vtc.edu/~pchapin/OW/index.html 33 | 34 | Digital Mars 8.41n 35 | ------------------------------- 36 | Digital Mars command-line C/C++ compilers are available for download 37 | from http://www.digitalmars.com. STLport is required and must be 38 | installed as well. 39 | 40 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | POLE 0.5 (released Mach 2013 by Stephen Baum) 2 | * Considerable rework to allow for creation and updating of structured storage. 3 | 4 | POLE 0.4 (released March 2010 by Michel Boudinot) 5 | * Fix for more than 236 mbat block entries 6 | 7 | POLE 0.3 (released March 2009 by Dmitry Fedorov) 8 | * Speed optimization 9 | * Qt 4.x compatibile viewer 10 | 11 | POLE 0.2 (released March 2005) 12 | 13 | * changed license to BSD license 14 | * poleview: graphical tool to view a structured storage 15 | * fixed occasional incorrect reading of big file 16 | * fixed rare memory problem, found by valgrind 17 | * fixed compile with Microsoft Visual C++ and Borland C++ (Windows) 18 | * modified storage to become stateless (with better API) 19 | * performance improvement, especially with very large file 20 | 21 | POLE 0.1 (released March 2004) 22 | 23 | * initial release 24 | * support for reading structured storages, even large ones 25 | * no support yet for creating or modifying a storage 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | POLE - Portable C++ library to access OLE Storage 2 | Copyright (C) 2002-2005 Ariya Hidayat. All rights reserved. 3 | Copyright (C) 2009-2014 Dmitry Fedorov, Center for Bio-Image Informatics. All rights reserved. 4 | Copyright (C) 2010 Michel Boudinot. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of the authors nor the names of its contributors may be 15 | used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | POLE - portable library for structured storage 2 | 3 | POLE is a portable C++ library to create and read structured storage. With a 4 | structured storage, you can store files inside another file, or you can even 5 | create complex directory tree, if you want to. The structured storage created by 6 | POLE is compatible with Microsoft structured storage, also sometimes called as 7 | OLE Compound Document, which are used among others in Microsoft Office. However, 8 | POLE has the advantage that it's portable (i.e you can read or write a 9 | structured storage in any supported platform), you don't need to rely on Windows 10 | library. 11 | 12 | Download 13 | ========== 14 | 15 | 0.3 and up is available at: http://www.dimin.net/ 16 | 17 | Source code is available at http://developer.berlios.de/projects/pole/. 18 | Please always use the latest stable version. 19 | 20 | Install 21 | ======== 22 | 23 | To compile POLE, you need a C++ compiler, with standard C++ library which 24 | supports STL (Standard Template Library). On Linux and other Unices, g++ will do 25 | the job. 26 | 27 | POLE is just a library, you have to integrate it as part of your 28 | application/project. Since it consists of one source file and one header file, 29 | it doesn't make much sense to compile POLE as separate library (either static or 30 | dynamic). Simply add these files to your project and you're done. 31 | 32 | The source tarball of POLE demonstrates the use of POLE to build poledump, a 33 | small utility to extract any stream inside a structured storage. If you use g++ 34 | as the compiler, you can compile poledump using the following command: 35 | 36 | g++ -o poledump pole.cpp poledump.cpp 37 | 38 | You may use poledump like the example below: 39 | 40 | poledump budget.xls Workbook result.xls 41 | 42 | The above command will extract a stream named Workbook inside Excel document 43 | (budget.xls) and save it as result.xls. File result.xls will still be recognized 44 | as a valid workbook (in raw format). Launch Microsoft Excel, open this file, and 45 | convince yourself. 46 | 47 | Copyright and License 48 | ===================== 49 | 50 | POLE - Portable C++ library to access OLE Storage 51 | Copyright (C) 2002-2005 Ariya Hidayat. All rights reserved. 52 | Copyright (C) 2009 Dmitry Fedorov, Center for Bio-Image Informatics. All rights reserved. 53 | Copyright (C) 2010 Michel Boudinot. All rights reserved. 54 | Copyright (c) 2013 Stephen Baum. All rights reserved. 55 | 56 | Redistribution and use in source and binary forms, with or without 57 | modification, are permitted provided that the following conditions 58 | are met: 59 | * Redistributions of source code must retain the above copyright notice, 60 | this list of conditions and the following disclaimer. 61 | * Redistributions in binary form must reproduce the above copyright notice, 62 | this list of conditions and the following disclaimer in the documentation 63 | and/or other materials provided with the distribution. 64 | * Neither the name of the authors nor the names of its contributors may be 65 | used to endorse or promote products derived from this software without 66 | specific prior written permission. 67 | 68 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 69 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 70 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 71 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 72 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 73 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 74 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 75 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 76 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 77 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 78 | THE POSSIBILITY OF SUCH DAMAGE. 79 | -------------------------------------------------------------------------------- /pole.cpp: -------------------------------------------------------------------------------- 1 | /* POLE - Portable C++ library to access OLE Storage 2 | Copyright (C) 2002-2005 Ariya Hidayat 3 | 4 | Performance optimization: Dmitry Fedorov 5 | Copyright 2009 6 | 7 | Fix for more than 236 mbat block entries : Michel Boudinot 8 | Copyright 2010 9 | 10 | Considerable rework to allow for creation and updating of structured storage : Stephen Baum 11 | Copyright 2013 12 | 13 | Added GetAllStreams, reworked datatypes 14 | Copyright 2013 Felix Gorny from Bitplane 15 | 16 | More datatype changes to allow for 32 and 64 bit code, some fixes involving incremental updates, flushing 17 | Copyright 2013 18 | 19 | Version: 0.5.2 20 | 21 | Redistribution and use in source and binary forms, with or without 22 | modification, are permitted provided that the following conditions 23 | are met: 24 | * Redistributions of source code must retain the above copyright notice, 25 | this list of conditions and the following disclaimer. 26 | * Redistributions in binary form must reproduce the above copyright notice, 27 | this list of conditions and the following disclaimer in the documentation 28 | and/or other materials provided with the distribution. 29 | * Neither the name of the authors nor the names of its contributors may be 30 | used to endorse or promote products derived from this software without 31 | specific prior written permission. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 34 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 37 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 38 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 41 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 42 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 43 | THE POSSIBILITY OF SUCH DAMAGE. 44 | */ 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include 55 | 56 | #include "pole.h" 57 | 58 | #ifdef POLE_USE_UTF16_FILENAMES 59 | #include 60 | #endif //POLE_USE_UTF16_FILENAMES 61 | 62 | // enable to activate debugging output 63 | // #define POLE_DEBUG 64 | #define CACHEBUFSIZE 4096 //a presumably reasonable size for the read cache 65 | 66 | namespace POLE 67 | { 68 | 69 | class Header 70 | { 71 | public: 72 | unsigned char id[8]; // signature, or magic identifier 73 | uint64 b_shift; // bbat->blockSize = 1 << b_shift 74 | uint64 s_shift; // sbat->blockSize = 1 << s_shift 75 | uint64 num_bat; // blocks allocated for big bat 76 | uint64 dirent_start; // starting block for directory info 77 | uint64 threshold; // switch from small to big file (usually 4K) 78 | uint64 sbat_start; // starting block index to store small bat 79 | uint64 num_sbat; // blocks allocated for small bat 80 | uint64 mbat_start; // starting block to store meta bat 81 | uint64 num_mbat; // blocks allocated for meta bat 82 | uint64 bb_blocks[109]; 83 | bool dirty; // Needs to be written 84 | 85 | Header(); 86 | bool valid(); 87 | void load( const unsigned char* buffer ); 88 | void save( unsigned char* buffer ); 89 | void debug(); 90 | }; 91 | 92 | class AllocTable 93 | { 94 | public: 95 | static const uint64 Eof; 96 | static const uint64 Avail; 97 | static const uint64 Bat; 98 | static const uint64 MetaBat; 99 | uint64 blockSize; 100 | AllocTable(); 101 | void clear(); 102 | uint64 count(); 103 | uint64 unusedCount(); 104 | void resize( uint64 newsize ); 105 | void preserve( uint64 n ); 106 | void set( uint64 index, uint64 val ); 107 | unsigned unused(); 108 | void setChain( std::vector ); 109 | std::vector follow( uint64 start ); 110 | uint64 operator[](uint64 index ); 111 | void load( const unsigned char* buffer, uint64 len ); 112 | void save( unsigned char* buffer ); 113 | uint64 size(); 114 | void debug(); 115 | bool isDirty(); 116 | void markAsDirty(uint64 dataIndex, int64 bigBlockSize); 117 | void flush(std::vector blocks, StorageIO *const io, int64 bigBlockSize); 118 | private: 119 | std::vector data; 120 | std::vector dirtyBlocks; 121 | bool bMaybeFragmented; 122 | AllocTable( const AllocTable& ); 123 | AllocTable& operator=( const AllocTable& ); 124 | }; 125 | 126 | class DirEntry 127 | { 128 | public: 129 | DirEntry(): valid(), name(), dir(), size(), start(), prev(), next(), child() {} 130 | bool valid; // false if invalid (should be skipped) 131 | std::string name; // the name, not in unicode anymore 132 | bool dir; // true if directory 133 | uint64 size; // size (not valid if directory) 134 | uint64 start; // starting block 135 | uint64 prev; // previous sibling 136 | uint64 next; // next sibling 137 | uint64 child; // first child 138 | int compare(const DirEntry& de); 139 | int compare(const std::string& name2); 140 | 141 | }; 142 | 143 | class DirTree 144 | { 145 | public: 146 | static const uint64 End; 147 | DirTree(int64 bigBlockSize); 148 | void clear(int64 bigBlockSize); 149 | inline uint64 entryCount(); 150 | uint64 unusedEntryCount(); 151 | DirEntry* entry( uint64 index ); 152 | DirEntry* entry( const std::string& name, bool create = false, int64 bigBlockSize = 0, StorageIO *const io = 0, int64 streamSize = 0); 153 | int64 indexOf( DirEntry* e ); 154 | int64 parent( uint64 index ); 155 | std::string fullName( uint64 index ); 156 | std::vector children( uint64 index ); 157 | uint64 find_child( uint64 index, const std::string& name, uint64 &closest ); 158 | void load( unsigned char* buffer, uint64 len ); 159 | void save( unsigned char* buffer ); 160 | uint64 size(); 161 | void debug(); 162 | bool isDirty(); 163 | void markAsDirty(uint64 dataIndex, int64 bigBlockSize); 164 | void flush(std::vector blocks, StorageIO *const io, int64 bigBlockSize, uint64 sb_start, uint64 sb_size); 165 | uint64 unused(); 166 | void findParentAndSib(uint64 inIdx, const std::string& inFullName, uint64 &parentIdx, uint64 &sibIdx); 167 | uint64 findSib(uint64 inIdx, uint64 sibIdx); 168 | void deleteEntry(DirEntry *entry, const std::string& inFullName, int64 bigBlockSize); 169 | private: 170 | std::vector entries; 171 | std::vector dirtyBlocks; 172 | DirTree( const DirTree& ); 173 | DirTree& operator=( const DirTree& ); 174 | }; 175 | 176 | class StorageIO 177 | { 178 | public: 179 | Storage* storage; // owner 180 | std::string filename; // filename 181 | std::fstream file; // associated with above name 182 | int64 result; // result of operation 183 | bool opened; // true if file is opened 184 | uint64 filesize; // size of the file 185 | bool writeable; // true if the file can be modified 186 | 187 | Header* header; // storage header 188 | DirTree* dirtree; // directory tree 189 | AllocTable* bbat; // allocation table for big blocks 190 | AllocTable* sbat; // allocation table for small blocks 191 | 192 | std::vector sb_blocks; // blocks for "small" files 193 | std::vector mbat_blocks; // blocks for doubly indirect indices to big blocks 194 | std::vector mbat_data; // the additional indices to big blocks 195 | bool mbatDirty; // If true, mbat_blocks need to be written 196 | 197 | std::list streams; 198 | 199 | StorageIO( Storage* storage, const char* filename ); 200 | ~StorageIO(); 201 | 202 | bool open(bool bWriteAccess = false, bool bCreate = false); 203 | void close(); 204 | void flush(); 205 | void load(bool bWriteAccess); 206 | void create(); 207 | void init(); 208 | bool deleteByName(const std::string& fullName); 209 | 210 | bool deleteNode(DirEntry *entry, const std::string& fullName); 211 | 212 | bool deleteLeaf(DirEntry *entry, const std::string& fullName); 213 | 214 | uint64 loadBigBlocks( std::vector blocks, unsigned char* buffer, uint64 maxlen ); 215 | 216 | uint64 loadBigBlock( uint64 block, unsigned char* buffer, uint64 maxlen ); 217 | 218 | uint64 saveBigBlocks( std::vector blocks, uint64 offset, unsigned char* buffer, uint64 len ); 219 | 220 | uint64 saveBigBlock( uint64 block, uint64 offset, unsigned char*buffer, uint64 len ); 221 | 222 | uint64 loadSmallBlocks( std::vector blocks, unsigned char* buffer, uint64 maxlen ); 223 | 224 | uint64 loadSmallBlock( uint64 block, unsigned char* buffer, uint64 maxlen ); 225 | 226 | uint64 saveSmallBlocks( std::vector blocks, uint64 offset, unsigned char* buffer, uint64 len, int64 startAtBlock = 0 ); 227 | 228 | uint64 saveSmallBlock( uint64 block, uint64 offset, unsigned char* buffer, uint64 len ); 229 | 230 | StreamIO* streamIO( const std::string& name, bool bCreate = false, int64 streamSize = 0 ); 231 | 232 | void flushbbat(); 233 | 234 | void flushsbat(); 235 | 236 | std::vector getbbatBlocks(bool bLoading); 237 | 238 | uint64 ExtendFile( std::vector *chain ); 239 | 240 | void addbbatBlock(); 241 | 242 | private: 243 | // no copy or assign 244 | StorageIO( const StorageIO& ); 245 | StorageIO& operator=( const StorageIO& ); 246 | 247 | }; 248 | 249 | class StreamIO 250 | { 251 | public: 252 | StorageIO* io; 253 | int64 entryIdx; //needed because a pointer to DirEntry will change whenever entries vector changes. 254 | std::string fullName; 255 | bool eof; 256 | bool fail; 257 | 258 | StreamIO( StorageIO* io, DirEntry* entry ); 259 | ~StreamIO(); 260 | uint64 size(); 261 | void setSize(uint64 newSize); 262 | void seek( uint64 pos ); 263 | uint64 tell(); 264 | int64 getch(); 265 | uint64 read( unsigned char* data, uint64 maxlen ); 266 | uint64 read( uint64 pos, unsigned char* data, uint64 maxlen ); 267 | uint64 write( unsigned char* data, uint64 len ); 268 | uint64 write( uint64 pos, unsigned char* data, uint64 len ); 269 | void flush(); 270 | 271 | private: 272 | std::vector blocks; 273 | 274 | // no copy or assign 275 | StreamIO( const StreamIO& ); 276 | StreamIO& operator=( const StreamIO& ); 277 | 278 | // pointer for read 279 | uint64 m_pos; 280 | 281 | // simple cache system to speed-up getch() 282 | unsigned char* cache_data; 283 | uint64 cache_size; 284 | uint64 cache_pos; 285 | void updateCache(); 286 | }; 287 | 288 | }; // namespace POLE 289 | 290 | using namespace POLE; 291 | 292 | #ifdef POLE_USE_UTF16_FILENAMES 293 | 294 | std::string POLE::UTF16toUTF8(const std::wstring &utf16) { 295 | std::wstring_convert, wchar_t> converter; 296 | return converter.to_bytes(utf16); 297 | } 298 | 299 | std::wstring POLE::UTF8toUTF16(const std::string &utf8) { 300 | std::wstring_convert, wchar_t> converter; 301 | return converter.from_bytes(utf8); 302 | } 303 | 304 | #endif //POLE_USE_UTF16_FILENAMES 305 | 306 | static void fileCheck(std::fstream &file) 307 | { 308 | bool bGood, bFail, bEof, bBad; 309 | bool bNOTOK; 310 | bGood = file.good(); 311 | bFail = file.fail(); 312 | bEof = file.eof(); 313 | bBad = file.bad(); 314 | if (bFail || bEof || bBad) 315 | bNOTOK = true; //this doesn't really do anything, but it is a good place to set a breakpoint! 316 | file.clear(); 317 | } 318 | 319 | 320 | static inline uint32 readU16( const unsigned char* ptr ) 321 | { 322 | return ptr[0]+(ptr[1]<<8); 323 | } 324 | 325 | static inline uint32 readU32( const unsigned char* ptr ) 326 | { 327 | return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24); 328 | } 329 | 330 | static inline void writeU16( unsigned char* ptr, uint32 data ) 331 | { 332 | ptr[0] = (unsigned char)(data & 0xff); 333 | ptr[1] = (unsigned char)((data >> 8) & 0xff); 334 | } 335 | 336 | static inline void writeU32( unsigned char* ptr, uint32 data ) 337 | { 338 | ptr[0] = (unsigned char)(data & 0xff); 339 | ptr[1] = (unsigned char)((data >> 8) & 0xff); 340 | ptr[2] = (unsigned char)((data >> 16) & 0xff); 341 | ptr[3] = (unsigned char)((data >> 24) & 0xff); 342 | } 343 | 344 | static const unsigned char pole_magic[] = 345 | { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; 346 | 347 | // =========== Header ========== 348 | 349 | Header::Header() 350 | : b_shift(9), // [1EH,02] size of sectors in power-of-two; typically 9 indicating 512-byte sectors 351 | s_shift(6), // [20H,02] size of mini-sectors in power-of-two; typically 6 indicating 64-byte mini-sectors 352 | num_bat(0), // [2CH,04] number of SECTs in the FAT chain 353 | dirent_start(0), // [30H,04] first SECT in the directory chain 354 | threshold(4096), // [38H,04] maximum size for a mini stream; typically 4096 bytes 355 | sbat_start(0), // [3CH,04] first SECT in the MiniFAT chain 356 | num_sbat(0), // [40H,04] number of SECTs in the MiniFAT chain 357 | mbat_start(AllocTable::Eof),// [44H,04] first SECT in the DIFAT chain 358 | num_mbat(0), // [48H,04] number of SECTs in the DIFAT chain 359 | dirty(true) 360 | 361 | { 362 | for( unsigned int i = 0; i < 8; i++ ) 363 | id[i] = pole_magic[i]; 364 | for( unsigned int i=0; i<109; i++ ) 365 | bb_blocks[i] = AllocTable::Avail; 366 | } 367 | 368 | bool Header::valid() 369 | { 370 | if( threshold != 4096 ) return false; 371 | if( num_bat == 0 ) return false; 372 | //if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false; // dima: incorrect check, number may be arbitrary larger 373 | if( (num_bat < 109) && (num_mbat != 0) ) return false; 374 | if( s_shift > b_shift ) return false; 375 | if( b_shift <= 6 ) return false; 376 | if( b_shift >=31 ) return false; 377 | 378 | return true; 379 | } 380 | 381 | void Header::load( const unsigned char* buffer ) { 382 | b_shift = readU16( buffer + 0x1e ); // [1EH,02] size of sectors in power-of-two; typically 9 indicating 512-byte sectors and 12 for 4096 383 | s_shift = readU16( buffer + 0x20 ); // [20H,02] size of mini-sectors in power-of-two; typically 6 indicating 64-byte mini-sectors 384 | num_bat = readU32( buffer + 0x2c ); // [2CH,04] number of SECTs in the FAT chain 385 | dirent_start = readU32( buffer + 0x30 ); // [30H,04] first SECT in the directory chain 386 | threshold = readU32( buffer + 0x38 ); // [38H,04] maximum size for a mini stream; typically 4096 bytes 387 | sbat_start = readU32( buffer + 0x3c ); // [3CH,04] first SECT in the MiniFAT chain 388 | num_sbat = readU32( buffer + 0x40 ); // [40H,04] number of SECTs in the MiniFAT chain 389 | mbat_start = readU32( buffer + 0x44 ); // [44H,04] first SECT in the DIFAT chain 390 | num_mbat = readU32( buffer + 0x48 ); // [48H,04] number of SECTs in the DIFAT chain 391 | 392 | for( unsigned int i = 0; i < 8; i++ ) 393 | id[i] = buffer[i]; 394 | 395 | // [4CH,436] the SECTs of first 109 FAT sectors 396 | for( unsigned int i=0; i<109; i++ ) 397 | bb_blocks[i] = readU32( buffer + 0x4C+i*4 ); 398 | dirty = false; 399 | } 400 | 401 | void Header::save( unsigned char* buffer ) 402 | { 403 | memset( buffer, 0, 0x4c ); 404 | memcpy( buffer, pole_magic, 8 ); // ole signature 405 | writeU32( buffer + 8, 0 ); // unknown 406 | writeU32( buffer + 12, 0 ); // unknown 407 | writeU32( buffer + 16, 0 ); // unknown 408 | writeU16( buffer + 24, 0x003e ); // revision ? 409 | writeU16( buffer + 26, 3 ); // version ? 410 | writeU16( buffer + 28, 0xfffe ); // unknown 411 | writeU16( buffer + 0x1e, (uint32) b_shift ); 412 | writeU16( buffer + 0x20, (uint32) s_shift ); 413 | writeU32( buffer + 0x2c, (uint32) num_bat ); 414 | writeU32( buffer + 0x30, (uint32) dirent_start ); 415 | writeU32( buffer + 0x38, (uint32) threshold ); 416 | writeU32( buffer + 0x3c, (uint32) sbat_start ); 417 | writeU32( buffer + 0x40, (uint32) num_sbat ); 418 | writeU32( buffer + 0x44, (uint32) mbat_start ); 419 | writeU32( buffer + 0x48, (uint32) num_mbat ); 420 | 421 | for( unsigned int i=0; i<109; i++ ) 422 | writeU32( buffer + 0x4C+i*4, (uint32) bb_blocks[i] ); 423 | dirty = false; 424 | } 425 | 426 | void Header::debug() 427 | { 428 | std::cout << std::endl; 429 | std::cout << "b_shift " << b_shift << std::endl; 430 | std::cout << "s_shift " << s_shift << std::endl; 431 | std::cout << "num_bat " << num_bat << std::endl; 432 | std::cout << "dirent_start " << dirent_start << std::endl; 433 | std::cout << "threshold " << threshold << std::endl; 434 | std::cout << "sbat_start " << sbat_start << std::endl; 435 | std::cout << "num_sbat " << num_sbat << std::endl; 436 | std::cout << "mbat_start " << mbat_start << std::endl; 437 | std::cout << "num_mbat " << num_mbat << std::endl; 438 | 439 | uint64 s = (num_bat<=109) ? num_bat : 109; 440 | std::cout << "bat blocks: "; 441 | for( uint64 i = 0; i < s; i++ ) 442 | std::cout << bb_blocks[i] << " "; 443 | std::cout << std::endl; 444 | } 445 | 446 | // =========== AllocTable ========== 447 | 448 | const uint64 AllocTable::Avail = 0xffffffff; 449 | const uint64 AllocTable::Eof = 0xfffffffe; 450 | const uint64 AllocTable::Bat = 0xfffffffd; 451 | const uint64 AllocTable::MetaBat = 0xfffffffc; 452 | 453 | AllocTable::AllocTable() 454 | : blockSize(4096), 455 | data(), 456 | dirtyBlocks(), 457 | bMaybeFragmented(true) 458 | { 459 | // initial size 460 | resize( 128 ); 461 | } 462 | 463 | uint64 AllocTable::count() 464 | { 465 | return static_cast(data.size()); 466 | } 467 | 468 | uint64 AllocTable::unusedCount() 469 | { 470 | uint64 maxIdx = count(); 471 | uint64 nFound = 0; 472 | for (uint64 idx = 0; idx < maxIdx; idx++) 473 | { 474 | if( data[idx] == Avail ) 475 | nFound++; 476 | } 477 | return nFound; 478 | } 479 | 480 | void AllocTable::resize( uint64 newsize ) 481 | { 482 | uint64 oldsize = static_cast(data.size()); 483 | data.resize( newsize ); 484 | if( newsize > oldsize ) 485 | for( uint64 i = oldsize; i pre; 493 | for( unsigned i=0; i < n; i++ ) 494 | pre.push_back( unused() ); 495 | } 496 | 497 | uint64 AllocTable::operator[]( uint64 index ) 498 | { 499 | uint64 result; 500 | result = data[index]; 501 | return result; 502 | } 503 | 504 | void AllocTable::set( uint64 index, uint64 value ) 505 | { 506 | if( index >= count() ) resize( index + 1); 507 | data[ index ] = value; 508 | if (value == Avail) 509 | bMaybeFragmented = true; 510 | } 511 | 512 | void AllocTable::setChain( std::vector chain ) 513 | { 514 | if( chain.size() ) 515 | { 516 | for( unsigned i=0; i AllocTable::follow( uint64 start ) 524 | { 525 | std::vector chain; 526 | 527 | if( start >= count() ) return chain; 528 | 529 | uint64 p = start; 530 | while( p < count() ) 531 | { 532 | if( p == (uint64)Eof ) break; 533 | if( p == (uint64)Bat ) break; 534 | if( p == (uint64)MetaBat ) break; 535 | if( p >= count() ) break; 536 | chain.push_back( p ); 537 | if( data[p] >= count() ) break; 538 | p = data[ p ]; 539 | } 540 | 541 | return chain; 542 | } 543 | 544 | unsigned AllocTable::unused() 545 | { 546 | // find first available block 547 | unsigned int maxIdx = (unsigned int) data.size(); 548 | if (bMaybeFragmented) 549 | { 550 | for( unsigned i = 0; i < maxIdx; i++ ) 551 | if( data[i] == Avail ) 552 | return i; 553 | } 554 | 555 | // completely full, so enlarge the table 556 | unsigned int block = maxIdx; 557 | resize( maxIdx ); 558 | bMaybeFragmented = false; 559 | return block; 560 | } 561 | 562 | void AllocTable::load( const unsigned char* buffer, uint64 len ) 563 | { 564 | resize( len / 4 ); 565 | for( unsigned i = 0; i < count(); i++ ) 566 | set( i, readU32( buffer + i*4 ) ); 567 | } 568 | 569 | // return space required to save this dirtree 570 | uint64 AllocTable::size() 571 | { 572 | return count() * 4; 573 | } 574 | 575 | void AllocTable::save( unsigned char* buffer ) 576 | { 577 | for( uint64 i = 0; i < count(); i++ ) 578 | writeU32( buffer + i*4, (uint32) data[i] ); 579 | } 580 | 581 | bool AllocTable::isDirty() 582 | { 583 | return (dirtyBlocks.size() > 0); 584 | } 585 | 586 | void AllocTable::markAsDirty(uint64 dataIndex, int64 bigBlockSize) 587 | { 588 | uint64 dbidx = dataIndex / (bigBlockSize / sizeof(uint32)); 589 | for (uint64 idx = 0; idx < static_cast(dirtyBlocks.size()); idx++) 590 | { 591 | if (dirtyBlocks[idx] == dbidx) 592 | return; 593 | } 594 | dirtyBlocks.push_back(dbidx); 595 | } 596 | 597 | void AllocTable::flush(std::vector blocks, StorageIO *const io, int64 bigBlockSize) 598 | { 599 | unsigned char *buffer = new unsigned char[bigBlockSize * blocks.size()]; 600 | save(buffer); 601 | for (uint64 idx = 0; idx < static_cast(blocks.size()); idx++) 602 | { 603 | bool bDirty = false; 604 | for (uint64 idx2 = 0; idx2 < static_cast(dirtyBlocks.size()); idx2++) 605 | { 606 | if (dirtyBlocks[idx2] == idx) 607 | { 608 | bDirty = true; 609 | break; 610 | } 611 | } 612 | if (bDirty) 613 | io->saveBigBlock(blocks[idx], 0, &buffer[bigBlockSize*idx], bigBlockSize); 614 | } 615 | dirtyBlocks.clear(); 616 | delete[] buffer; 617 | } 618 | 619 | void AllocTable::debug() 620 | { 621 | std::cout << "block size " << data.size() << std::endl; 622 | for( unsigned i=0; i< data.size(); i++ ) 623 | { 624 | if( data[i] == Avail ) continue; 625 | std::cout << i << ": "; 626 | if( data[i] == Eof ) std::cout << "[eof]"; 627 | else if( data[i] == Bat ) std::cout << "[bat]"; 628 | else if( data[i] == MetaBat ) std::cout << "[metabat]"; 629 | else std::cout << data[i]; 630 | std::cout << std::endl; 631 | } 632 | } 633 | 634 | // =========== DirEntry ========== 635 | // "A node with a shorter name is less than a node with a inter name" 636 | // "For nodes with the same length names, compare the two names." 637 | // --Windows Compound Binary File Format Specification, Section 2.5 638 | int DirEntry::compare(const DirEntry& de) 639 | { 640 | return compare(de.name); 641 | } 642 | 643 | int DirEntry::compare(const std::string& name2) 644 | { 645 | if (name.length() < name2.length()) 646 | return -1; 647 | else if (name.length() > name2.length()) 648 | return 1; 649 | else 650 | return name.compare(name2); 651 | } 652 | 653 | 654 | // =========== DirTree ========== 655 | 656 | const uint64 DirTree::End = 0xffffffff; 657 | 658 | DirTree::DirTree(int64 bigBlockSize) 659 | : entries(), 660 | dirtyBlocks() 661 | { 662 | clear(bigBlockSize); 663 | } 664 | 665 | void DirTree::clear(int64 bigBlockSize) 666 | { 667 | // leave only root entry 668 | entries.resize( 1 ); 669 | entries[0].valid = true; 670 | entries[0].name = "Root Entry"; 671 | entries[0].dir = true; 672 | entries[0].size = 0; 673 | entries[0].start = End; 674 | entries[0].prev = End; 675 | entries[0].next = End; 676 | entries[0].child = End; 677 | markAsDirty(0, bigBlockSize); 678 | } 679 | 680 | inline uint64 DirTree::entryCount() 681 | { 682 | return entries.size(); 683 | } 684 | 685 | uint64 DirTree::unusedEntryCount() 686 | { 687 | uint64 nFound = 0; 688 | for (uint64 idx = 0; idx < entryCount(); idx++) 689 | { 690 | if (!entries[idx].valid) 691 | nFound++; 692 | } 693 | return nFound; 694 | } 695 | 696 | DirEntry* DirTree::entry( uint64 index ) 697 | { 698 | if( index >= entryCount() ) return (DirEntry*) 0; 699 | return &entries[ index ]; 700 | } 701 | 702 | int64 DirTree::indexOf( DirEntry* e ) 703 | { 704 | for( uint64 i = 0; i < entryCount(); i++ ) 705 | if( entry( i ) == e ) return i; 706 | 707 | return -1; 708 | } 709 | 710 | int64 DirTree::parent( uint64 index ) 711 | { 712 | // brute-force, basically we iterate for each entries, find its children 713 | // and check if one of the children is 'index' 714 | for( uint64 j=0; j chi = children( j ); 717 | for( unsigned i=0; iname; 731 | result.insert( 0, "/" ); 732 | uint64 p = parent( index ); 733 | DirEntry * _entry = 0; 734 | while( p > 0 ) 735 | { 736 | _entry = entry( p ); 737 | if (_entry->dir && _entry->valid) 738 | { 739 | result.insert( 0, _entry->name); 740 | result.insert( 0, "/" ); 741 | } 742 | --p; 743 | index = p; 744 | if( index <= 0 ) break; 745 | } 746 | return result; 747 | } 748 | 749 | // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry 750 | // if not found and create is false, return 0 751 | // if create is true, a new entry is returned 752 | DirEntry* DirTree::entry( const std::string& name, bool create, int64 bigBlockSize, StorageIO *const io, int64 streamSize) 753 | { 754 | if( !name.length() ) return (DirEntry*)0; 755 | 756 | // quick check for "/" (that's root) 757 | if( name == "/" ) return entry( 0 ); 758 | 759 | // split the names, e.g "/ObjectPool/_1020961869" will become: 760 | // "ObjectPool" and "_1020961869" 761 | std::list names; 762 | std::string::size_type start = 0, end = 0; 763 | if( name[0] == '/' ) start++; 764 | int levelsLeft = 0; 765 | while( start < name.length() ) 766 | { 767 | end = name.find_first_of( '/', start ); 768 | if( end == std::string::npos ) end = name.length(); 769 | names.push_back( name.substr( start, end-start ) ); 770 | levelsLeft++; 771 | start = end+1; 772 | } 773 | 774 | // start from root 775 | int64 index = 0 ; 776 | 777 | // trace one by one 778 | std::list::iterator it; 779 | 780 | for( it = names.begin(); it != names.end(); ++it ) 781 | { 782 | // find among the children of index 783 | levelsLeft--; 784 | uint64 child = 0; 785 | 786 | 787 | /* 788 | // dima: this block is really inefficient 789 | std::vector chi = children( index ); 790 | for( unsigned i = 0; i < chi.size(); i++ ) 791 | { 792 | DirEntry* ce = entry( chi[i] ); 793 | if( ce ) 794 | if( ce->valid && ( ce->name.length()>1 ) ) 795 | if( ce->name == *it ) { 796 | child = chi[i]; 797 | break; 798 | } 799 | } 800 | */ 801 | // dima: performance optimisation of the previous 802 | uint64 closest = End; 803 | child = find_child( index, *it, closest ); 804 | 805 | // traverse to the child 806 | if( child > 0 ) index = child; 807 | else 808 | { 809 | // not found among children 810 | if( !create || !io->writeable) return (DirEntry*)0; 811 | 812 | // create a new entry 813 | uint64 parent2 = index; 814 | index = unused(); 815 | DirEntry* e = entry( index ); 816 | e->valid = true; 817 | e->name = *it; 818 | e->dir = (levelsLeft > 0); 819 | if (!e->dir) 820 | e->size = streamSize; 821 | else 822 | e->size = 0; 823 | e->start = AllocTable::Eof; 824 | e->child = End; 825 | if (closest == End) 826 | { 827 | e->prev = End; 828 | e->next = entry(parent2)->child; 829 | entry(parent2)->child = index; 830 | markAsDirty(parent2, bigBlockSize); 831 | } 832 | else 833 | { 834 | DirEntry* closeE = entry( closest ); 835 | if (closeE->compare(*e) < 0) 836 | { 837 | e->prev = closeE->next; 838 | e->next = End; 839 | closeE->next = index; 840 | } 841 | else 842 | { 843 | e->next = closeE->prev; 844 | e->prev = End; 845 | closeE->prev = index; 846 | } 847 | markAsDirty(closest, bigBlockSize); 848 | } 849 | markAsDirty(index, bigBlockSize); 850 | uint64 bbidx = index / (bigBlockSize / 128); 851 | std::vector blocks = io->bbat->follow(io->header->dirent_start); 852 | while (blocks.size() <= bbidx) 853 | { 854 | uint64 nblock = io->bbat->unused(); 855 | if (blocks.size() > 0) 856 | { 857 | io->bbat->set(blocks[static_cast(blocks.size())-1], nblock); 858 | io->bbat->markAsDirty(blocks[static_cast(blocks.size())-1], bigBlockSize); 859 | } 860 | io->bbat->set(nblock, AllocTable::Eof); 861 | io->bbat->markAsDirty(nblock, bigBlockSize); 862 | blocks.push_back(nblock); 863 | uint64 bbidx = nblock / (io->bbat->blockSize / sizeof(uint64)); 864 | while (bbidx >= io->header->num_bat) 865 | io->addbbatBlock(); 866 | } 867 | } 868 | } 869 | 870 | return entry( index ); 871 | } 872 | 873 | // helper function: recursively find siblings of index 874 | void dirtree_find_siblings( DirTree* dirtree, std::vector& result, 875 | uint64 index ) 876 | { 877 | DirEntry* e = dirtree->entry( index ); 878 | if (!e) return; 879 | if (e->prev != DirTree::End) 880 | dirtree_find_siblings(dirtree, result, e->prev); 881 | result.push_back(index); 882 | if (e->next != DirTree::End) 883 | dirtree_find_siblings(dirtree, result, e->next); 884 | } 885 | 886 | std::vector DirTree::children( uint64 index ) 887 | { 888 | std::vector result; 889 | 890 | DirEntry* e = entry( index ); 891 | if( e ) if( e->valid && e->child < entryCount() ) 892 | dirtree_find_siblings( this, result, e->child ); 893 | 894 | return result; 895 | } 896 | 897 | uint64 dirtree_find_sibling( DirTree* dirtree, uint64 index, const std::string& name, uint64& closest ) { 898 | 899 | uint64 count = dirtree->entryCount(); 900 | DirEntry* e = dirtree->entry( index ); 901 | if (!e || !e->valid) return 0; 902 | int cval = e->compare(name); 903 | if (cval == 0) 904 | return index; 905 | if (cval > 0) 906 | { 907 | if (e->prev > 0 && e->prev < count) 908 | return dirtree_find_sibling( dirtree, e->prev, name, closest ); 909 | } 910 | else 911 | { 912 | if (e->next > 0 && e->next < count) 913 | return dirtree_find_sibling( dirtree, e->next, name, closest ); 914 | } 915 | closest = index; 916 | return 0; 917 | } 918 | 919 | uint64 DirTree::find_child( uint64 index, const std::string& name, uint64& closest ) { 920 | 921 | uint64 count = entryCount(); 922 | DirEntry* p = entry( index ); 923 | if (p && p->valid && p->child < count ) 924 | return dirtree_find_sibling( this, p->child, name, closest ); 925 | 926 | return 0; 927 | } 928 | 929 | void DirTree::load( unsigned char* buffer, uint64 size ) 930 | { 931 | entries.clear(); 932 | 933 | for( uint64 i = 0; i < size/128; i++ ) 934 | { 935 | uint64 p = i * 128; 936 | 937 | // would be < 32 if first char in the name isn't printable 938 | unsigned prefix = 32; 939 | 940 | // parse name of this entry, which stored as Unicode 16-bit 941 | std::string name; 942 | int name_len = readU16( buffer + 0x40+p ); 943 | if( name_len > 64 ) name_len = 64; 944 | for( int j=0; ( buffer[j+p]) && (j(name.length()*2 + 2) ); 991 | writeU32( buffer + 0x74, 0xffffffff ); 992 | writeU32( buffer + 0x78, 0 ); 993 | writeU32( buffer + 0x44, 0xffffffff ); 994 | writeU32( buffer + 0x48, 0xffffffff ); 995 | writeU32( buffer + 0x4c, (uint32) root->child ); 996 | buffer[ 0x42 ] = 5; 997 | //buffer[ 0x43 ] = 1; 998 | 999 | for( unsigned int i = 1; i < entryCount(); i++ ) 1000 | { 1001 | DirEntry* e = entry( i ); 1002 | if( !e ) continue; 1003 | if( e->dir ) 1004 | { 1005 | e->start = 0xffffffff; 1006 | e->size = 0; 1007 | } 1008 | 1009 | // max length for name is 32 chars 1010 | std::string name = e->name; 1011 | if( name.length() > 32 ) 1012 | name.erase( 32, name.length() ); 1013 | 1014 | // write name as Unicode 16-bit 1015 | for( unsigned j = 0; j < name.length(); j++ ) 1016 | buffer[ i*128 + j*2 ] = name[j]; 1017 | 1018 | writeU16( buffer + i*128 + 0x40, static_cast(name.length()*2 + 2) ); 1019 | writeU32( buffer + i*128 + 0x74, (uint32) e->start ); 1020 | writeU32( buffer + i*128 + 0x78, (uint32) e->size ); 1021 | writeU32( buffer + i*128 + 0x44, (uint32) e->prev ); 1022 | writeU32( buffer + i*128 + 0x48, (uint32) e->next ); 1023 | writeU32( buffer + i*128 + 0x4c, (uint32) e->child ); 1024 | if (!e->valid) 1025 | buffer[ i*128 + 0x42 ] = 0; //STGTY_INVALID 1026 | else 1027 | buffer[ i*128 + 0x42 ] = e->dir ? 1 : 2; //STGTY_STREAM or STGTY_STORAGE 1028 | buffer[ i*128 + 0x43 ] = 1; // always black 1029 | } 1030 | } 1031 | 1032 | bool DirTree::isDirty() 1033 | { 1034 | return (dirtyBlocks.size() > 0); 1035 | } 1036 | 1037 | 1038 | void DirTree::markAsDirty(uint64 dataIndex, int64 bigBlockSize) 1039 | { 1040 | uint64 dbidx = dataIndex / (bigBlockSize / 128); 1041 | for (uint64 idx = 0; idx < static_cast(dirtyBlocks.size()); idx++) 1042 | { 1043 | if (dirtyBlocks[idx] == dbidx) 1044 | return; 1045 | } 1046 | dirtyBlocks.push_back(dbidx); 1047 | } 1048 | 1049 | void DirTree::flush(std::vector blocks, StorageIO *const io, int64 bigBlockSize, uint64 sb_start, uint64 sb_size) 1050 | { 1051 | uint64 bufLen = size(); 1052 | unsigned char *buffer = new unsigned char[bufLen]; 1053 | save(buffer); 1054 | writeU32( buffer + 0x74, (uint32) sb_start ); 1055 | writeU32( buffer + 0x78, (uint32) sb_size ); 1056 | for (uint64 idx = 0; idx < static_cast(blocks.size()); idx++) 1057 | { 1058 | bool bDirty = false; 1059 | for (uint64 idx2 = 0; idx2 < static_cast(dirtyBlocks.size()); idx2++) 1060 | { 1061 | if (dirtyBlocks[idx2] == idx) 1062 | { 1063 | bDirty = true; 1064 | break; 1065 | } 1066 | } 1067 | uint64 bytesToWrite = bigBlockSize; 1068 | uint64 pos = bigBlockSize*idx; 1069 | if ((bufLen - pos) < bytesToWrite) 1070 | bytesToWrite = bufLen - pos; 1071 | if (bDirty) 1072 | io->saveBigBlock(blocks[idx], 0, &buffer[pos], bytesToWrite); 1073 | } 1074 | dirtyBlocks.clear(); 1075 | delete[] buffer; 1076 | } 1077 | 1078 | uint64 DirTree::unused() 1079 | { 1080 | for (uint64 idx = 0; idx < static_cast(entryCount()); idx++) 1081 | { 1082 | if (!entries[idx].valid) 1083 | return idx; 1084 | } 1085 | entries.push_back(DirEntry()); 1086 | return entryCount()-1; 1087 | } 1088 | 1089 | // Utility function to get the index of the parent dirEntry, given that we already have a full name it is relatively fast. 1090 | // Then look for a sibling dirEntry that points to inIdx. In some circumstances, the dirEntry at inIdx will be the direct child 1091 | // of the parent, in which case sibIdx will be returned as 0. A failure is indicated if both parentIdx and sibIdx are returned as 0. 1092 | 1093 | void DirTree::findParentAndSib(uint64 inIdx, const std::string& inFullName, uint64& parentIdx, uint64& sibIdx) 1094 | { 1095 | sibIdx = 0; 1096 | parentIdx = 0; 1097 | if (inIdx == 0 || inIdx >= entryCount() || inFullName == "/" || inFullName == "") 1098 | return; 1099 | std::string localName = inFullName; 1100 | if (localName[0] != '/') 1101 | localName = '/' + localName; 1102 | std::string parentName = localName; 1103 | if (parentName[parentName.size()-1] == '/') 1104 | parentName = parentName.substr(0, parentName.size()-1); 1105 | std::string::size_type lastSlash; 1106 | lastSlash = parentName.find_last_of('/'); 1107 | if (lastSlash == std::string::npos) 1108 | return; 1109 | if (lastSlash == 0) 1110 | lastSlash = 1; //leave root 1111 | parentName = parentName.substr(0, lastSlash); 1112 | DirEntry *parent2 = entry(parentName); 1113 | parentIdx = indexOf(parent2); 1114 | if (parent2->child == inIdx) 1115 | return; //successful return, no sibling points to inIdx 1116 | sibIdx = findSib(inIdx, parent2->child); 1117 | } 1118 | 1119 | // Utility function to get the index of the sibling dirEntry which points to inIdx. It is the responsibility of the original caller 1120 | // to start with the root sibling - i.e., sibIdx should be pointed to by the parent node's child. 1121 | 1122 | uint64 DirTree::findSib(uint64 inIdx, uint64 sibIdx) 1123 | { 1124 | DirEntry *sib = entry(sibIdx); 1125 | if (!sib || !sib->valid) 1126 | return 0; 1127 | if (sib->next == inIdx || sib->prev == inIdx) 1128 | return sibIdx; 1129 | DirEntry *targetSib = entry(inIdx); 1130 | int cval = sib->compare(*targetSib); 1131 | if (cval > 0) 1132 | return findSib(inIdx, sib->prev); 1133 | else 1134 | return findSib(inIdx, sib->next); 1135 | } 1136 | 1137 | void DirTree::deleteEntry(DirEntry *dirToDel, const std::string& inFullName, int64 bigBlockSize) 1138 | { 1139 | uint64 parentIdx; 1140 | uint64 sibIdx; 1141 | uint64 inIdx = indexOf(dirToDel); 1142 | uint64 nEntries = entryCount(); 1143 | findParentAndSib(inIdx, inFullName, parentIdx, sibIdx); 1144 | uint64 replIdx; 1145 | if (!dirToDel->next || dirToDel->next > nEntries) 1146 | replIdx = dirToDel->prev; 1147 | else 1148 | { 1149 | DirEntry *sibNext = entry(dirToDel->next); 1150 | if (!sibNext->prev || sibNext->prev > nEntries) 1151 | { 1152 | replIdx = dirToDel->next; 1153 | sibNext->prev = dirToDel->prev; 1154 | markAsDirty(replIdx, bigBlockSize); 1155 | } 1156 | else 1157 | { 1158 | DirEntry *smlSib = sibNext; 1159 | int64 smlIdx = dirToDel->next; 1160 | DirEntry *smlrSib; 1161 | int64 smlrIdx = -1; 1162 | for ( ; ; ) 1163 | { 1164 | smlrIdx = smlSib->prev; 1165 | smlrSib = entry(smlrIdx); 1166 | if (!smlrSib->prev || smlrSib->prev > nEntries) 1167 | break; 1168 | smlSib = smlrSib; 1169 | smlIdx = smlrIdx; 1170 | } 1171 | replIdx = smlSib->prev; 1172 | smlSib->prev = smlrSib->next; 1173 | smlrSib->prev = dirToDel->prev; 1174 | smlrSib->next = dirToDel->next; 1175 | markAsDirty(smlIdx, bigBlockSize); 1176 | markAsDirty(smlrIdx, bigBlockSize); 1177 | } 1178 | } 1179 | if (sibIdx) 1180 | { 1181 | DirEntry *sib = entry(sibIdx); 1182 | if (sib->next == inIdx) 1183 | sib->next = replIdx; 1184 | else 1185 | sib->prev = replIdx; 1186 | markAsDirty(sibIdx, bigBlockSize); 1187 | } 1188 | else 1189 | { 1190 | DirEntry *parNode = entry(parentIdx); 1191 | parNode->child = replIdx; 1192 | markAsDirty(parentIdx, bigBlockSize); 1193 | } 1194 | dirToDel->valid = false; //indicating that this entry is not in use 1195 | markAsDirty(inIdx, bigBlockSize); 1196 | } 1197 | 1198 | 1199 | void DirTree::debug() 1200 | { 1201 | for( unsigned i = 0; i < entryCount(); i++ ) 1202 | { 1203 | DirEntry* e = entry( i ); 1204 | if( !e ) continue; 1205 | std::cout << i << ": "; 1206 | if( !e->valid ) std::cout << "INVALID "; 1207 | std::cout << e->name << " "; 1208 | if( e->dir ) std::cout << "(Dir) "; 1209 | else std::cout << "(File) "; 1210 | std::cout << e->size << " "; 1211 | std::cout << "s:" << e->start << " "; 1212 | std::cout << "("; 1213 | if( e->child == End ) std::cout << "-"; else std::cout << e->child; 1214 | std::cout << " "; 1215 | if( e->prev == End ) std::cout << "-"; else std::cout << e->prev; 1216 | std::cout << ":"; 1217 | if( e->next == End ) std::cout << "-"; else std::cout << e->next; 1218 | std::cout << ")"; 1219 | std::cout << std::endl; 1220 | } 1221 | } 1222 | 1223 | // =========== StorageIO ========== 1224 | 1225 | StorageIO::StorageIO( Storage* st, const char* fname ) 1226 | : storage(st), 1227 | filename(fname), 1228 | file(), 1229 | result(Storage::Ok), 1230 | opened(false), 1231 | filesize(0), 1232 | writeable(false), 1233 | header(new Header()), 1234 | dirtree(new DirTree(1 << header->b_shift)), 1235 | bbat(new AllocTable()), 1236 | sbat(new AllocTable()), 1237 | sb_blocks(), 1238 | mbat_blocks(), 1239 | mbat_data(), 1240 | mbatDirty(), 1241 | streams() 1242 | { 1243 | bbat->blockSize = (uint64) 1 << header->b_shift; 1244 | sbat->blockSize = (uint64) 1 << header->s_shift; 1245 | } 1246 | 1247 | StorageIO::~StorageIO() 1248 | { 1249 | if( opened ) close(); 1250 | delete sbat; 1251 | delete bbat; 1252 | delete dirtree; 1253 | delete header; 1254 | } 1255 | 1256 | bool StorageIO::open(bool bWriteAccess, bool bCreate) 1257 | { 1258 | // already opened ? close first 1259 | if (opened) 1260 | close(); 1261 | if (bCreate) 1262 | { 1263 | create(); 1264 | init(); 1265 | writeable = true; 1266 | } 1267 | else 1268 | { 1269 | writeable = bWriteAccess; 1270 | load(bWriteAccess); 1271 | } 1272 | 1273 | return result == Storage::Ok; 1274 | } 1275 | 1276 | void StorageIO::load(bool bWriteAccess) 1277 | { 1278 | unsigned char* buffer = 0; 1279 | uint64 buflen = 0; 1280 | std::vector blocks; 1281 | 1282 | // open the file, check for error 1283 | result = Storage::OpenFailed; 1284 | 1285 | #if defined(POLE_USE_UTF16_FILENAMES) 1286 | if (bWriteAccess) 1287 | file.open(UTF8toUTF16(filename).c_str(), std::ios::binary | std::ios::in | std::ios::out); 1288 | else 1289 | file.open(UTF8toUTF16(filename).c_str(), std::ios::binary | std::ios::in); 1290 | #else 1291 | if (bWriteAccess) 1292 | file.open(filename.c_str(), std::ios::binary | std::ios::in | std::ios::out); 1293 | else 1294 | file.open(filename.c_str(), std::ios::binary | std::ios::in); 1295 | #endif //defined(POLE_USE_UTF16_FILENAMES) && defined(POLE_WIN) 1296 | 1297 | if( !file.good() ) return; 1298 | 1299 | // find size of input file 1300 | file.seekg(0, std::ios::end ); 1301 | filesize = static_cast(file.tellg()); 1302 | 1303 | // load header 1304 | buffer = new unsigned char[512]; 1305 | file.seekg( 0 ); 1306 | file.read( (char*)buffer, 512 ); 1307 | fileCheck(file); 1308 | header->load( buffer ); 1309 | delete[] buffer; 1310 | 1311 | // check OLE magic id 1312 | result = Storage::NotOLE; 1313 | for( unsigned i=0; i<8; i++ ) 1314 | if( header->id[i] != pole_magic[i] ) 1315 | return; 1316 | 1317 | // sanity checks 1318 | result = Storage::BadOLE; 1319 | if( !header->valid() ) return; 1320 | if( header->threshold != 4096 ) return; 1321 | 1322 | // important block size 1323 | bbat->blockSize = (uint64) 1 << header->b_shift; 1324 | sbat->blockSize = (uint64) 1 << header->s_shift; 1325 | 1326 | blocks = getbbatBlocks(true); 1327 | 1328 | // load big bat 1329 | buflen = static_cast(blocks.size())*bbat->blockSize; 1330 | if( buflen > 0 ) 1331 | { 1332 | buffer = new unsigned char[ buflen ]; 1333 | loadBigBlocks( blocks, buffer, buflen ); 1334 | bbat->load( buffer, buflen ); 1335 | delete[] buffer; 1336 | } 1337 | 1338 | // load small bat 1339 | blocks.clear(); 1340 | blocks = bbat->follow( header->sbat_start ); 1341 | buflen = static_cast(blocks.size())*bbat->blockSize; 1342 | if( buflen > 0 ) 1343 | { 1344 | buffer = new unsigned char[ buflen ]; 1345 | loadBigBlocks( blocks, buffer, buflen ); 1346 | sbat->load( buffer, buflen ); 1347 | delete[] buffer; 1348 | } 1349 | 1350 | // load directory tree 1351 | blocks.clear(); 1352 | blocks = bbat->follow( header->dirent_start ); 1353 | buflen = static_cast(blocks.size())*bbat->blockSize; 1354 | buffer = new unsigned char[ buflen ]; 1355 | loadBigBlocks( blocks, buffer, buflen ); 1356 | dirtree->load( buffer, buflen ); 1357 | unsigned sb_start = readU32( buffer + 0x74 ); 1358 | delete[] buffer; 1359 | 1360 | // fetch block chain as data for small-files 1361 | sb_blocks = bbat->follow( sb_start ); // small files 1362 | 1363 | // for troubleshooting, just enable this block 1364 | #if 0 1365 | header->debug(); 1366 | sbat->debug(); 1367 | bbat->debug(); 1368 | dirtree->debug(); 1369 | #endif 1370 | 1371 | // so far so good 1372 | result = Storage::Ok; 1373 | opened = true; 1374 | } 1375 | 1376 | void StorageIO::create() { 1377 | // std::cout << "Creating " << filename << std::endl; 1378 | 1379 | #if defined(POLE_USE_UTF16_FILENAMES) 1380 | file.open(UTF8toUTF16(filename).c_str(), std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); 1381 | #else 1382 | file.open( filename.c_str(), std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); 1383 | #endif 1384 | if( !file.good() ) 1385 | { 1386 | std::cerr << "Can't create " << filename << std::endl; 1387 | result = Storage::OpenFailed; 1388 | return; 1389 | } 1390 | 1391 | // so far so good 1392 | opened = true; 1393 | result = Storage::Ok; 1394 | } 1395 | 1396 | void StorageIO::init() 1397 | { 1398 | // Initialize parts of the header, directory entries, and big and small allocation tables 1399 | header->bb_blocks[0] = 0; 1400 | header->dirent_start = 1; 1401 | header->sbat_start = 2; 1402 | header->num_bat = 1; 1403 | header->num_sbat = 1; 1404 | header->dirty = true; 1405 | bbat->set(0, AllocTable::Eof); 1406 | bbat->markAsDirty(0, bbat->blockSize); 1407 | bbat->set(1, AllocTable::Eof); 1408 | bbat->markAsDirty(1, bbat->blockSize); 1409 | bbat->set(2, AllocTable::Eof); 1410 | bbat->markAsDirty(2, bbat->blockSize); 1411 | bbat->set(3, AllocTable::Eof); 1412 | bbat->markAsDirty(3, bbat->blockSize); 1413 | sb_blocks = bbat->follow( 3 ); 1414 | mbatDirty = false; 1415 | } 1416 | 1417 | void StorageIO::flush() 1418 | { 1419 | if (header->dirty) 1420 | { 1421 | unsigned char *buffer = new unsigned char[512]; 1422 | header->save( buffer ); 1423 | file.seekp( 0 ); 1424 | file.write( (char*)buffer, 512 ); 1425 | fileCheck(file); 1426 | delete[] buffer; 1427 | } 1428 | if (bbat->isDirty()) 1429 | flushbbat(); 1430 | if (sbat->isDirty()) 1431 | flushsbat(); 1432 | if (dirtree->isDirty()) 1433 | { 1434 | std::vector blocks; 1435 | blocks = bbat->follow(header->dirent_start); 1436 | uint64 sb_start = 0xffffffff; 1437 | if (sb_blocks.size() > 0) 1438 | sb_start = sb_blocks[0]; 1439 | dirtree->flush(blocks, this, bbat->blockSize, sb_start, static_cast(sb_blocks.size())*bbat->blockSize); 1440 | } 1441 | if (mbatDirty && mbat_blocks.size() > 0) 1442 | { 1443 | uint64 nBytes = bbat->blockSize * static_cast(mbat_blocks.size()); 1444 | unsigned char *buffer = new unsigned char[nBytes]; 1445 | uint64 sIdx = 0; 1446 | uint64 dcount = 0; 1447 | uint64 blockCapacity = bbat->blockSize / sizeof(uint64) - 1; 1448 | uint64 blockIdx = 0; 1449 | for (unsigned mdIdx = 0; mdIdx < mbat_data.size(); mdIdx++) 1450 | { 1451 | writeU32(buffer + sIdx, (uint32) mbat_data[mdIdx]); 1452 | sIdx += 4; 1453 | dcount++; 1454 | if (dcount == blockCapacity) 1455 | { 1456 | blockIdx++; 1457 | if (blockIdx == mbat_blocks.size()) 1458 | writeU32(buffer + sIdx, AllocTable::Eof); 1459 | else 1460 | writeU32(buffer + sIdx, (uint32) mbat_blocks[blockIdx]); 1461 | sIdx += 4; 1462 | dcount = 0; 1463 | } 1464 | } 1465 | saveBigBlocks(mbat_blocks, 0, buffer, nBytes); 1466 | delete[] buffer; 1467 | mbatDirty = false; 1468 | } 1469 | file.flush(); 1470 | fileCheck(file); 1471 | 1472 | /* Note on Microsoft implementation: 1473 | - directory entries are stored in the last block(s) 1474 | - BATs are as second to the last 1475 | - Meta BATs are third to the last 1476 | */ 1477 | } 1478 | 1479 | void StorageIO::close() 1480 | { 1481 | if( !opened ) return; 1482 | 1483 | file.close(); 1484 | opened = false; 1485 | 1486 | std::list::iterator it; 1487 | for( it = streams.begin(); it != streams.end(); ++it ) 1488 | delete *it; 1489 | } 1490 | 1491 | 1492 | StreamIO* StorageIO::streamIO( const std::string& name, bool bCreate, int64 streamSize ) 1493 | { 1494 | // sanity check 1495 | if( !name.length() ) return (StreamIO*)0; 1496 | 1497 | // search in the entries 1498 | DirEntry* entry = dirtree->entry( name, bCreate, bbat->blockSize, this, streamSize ); 1499 | //if( entry) std::cout << "FOUND\n"; 1500 | if( !entry ) return (StreamIO*)0; 1501 | //if( !entry->dir ) std::cout << " NOT DIR\n"; 1502 | if( entry->dir ) return (StreamIO*)0; 1503 | 1504 | StreamIO* result2 = new StreamIO( this, entry ); 1505 | result2->fullName = name; 1506 | 1507 | return result2; 1508 | } 1509 | 1510 | bool StorageIO::deleteByName(const std::string& fullName) 1511 | { 1512 | if (!fullName.length()) 1513 | return false; 1514 | if (!writeable) 1515 | return false; 1516 | DirEntry* entry = dirtree->entry(fullName); 1517 | if (!entry) 1518 | return false; 1519 | bool retVal; 1520 | if (entry->dir) 1521 | retVal = deleteNode(entry, fullName); 1522 | else 1523 | retVal = deleteLeaf(entry, fullName); 1524 | if (retVal) 1525 | flush(); 1526 | return retVal; 1527 | } 1528 | 1529 | bool StorageIO::deleteNode(DirEntry *entry, const std::string& fullName) 1530 | { 1531 | std::string lclName = fullName; 1532 | if (lclName[lclName.size()-1] != '/') 1533 | lclName += '/'; 1534 | bool retVal = true; 1535 | while (entry->child && entry->child < dirtree->entryCount()) 1536 | { 1537 | DirEntry* childEnt = dirtree->entry(entry->child); 1538 | std::string childFullName = lclName + childEnt->name; 1539 | if (childEnt->dir) 1540 | retVal = deleteNode(childEnt, childFullName); 1541 | else 1542 | retVal = deleteLeaf(childEnt, childFullName); 1543 | if (!retVal) 1544 | return false; 1545 | } 1546 | dirtree->deleteEntry(entry, fullName, bbat->blockSize); 1547 | return retVal; 1548 | } 1549 | 1550 | bool StorageIO::deleteLeaf(DirEntry *entry, const std::string& fullName) 1551 | { 1552 | std::vector blocks; 1553 | if (entry->size >= header->threshold) 1554 | { 1555 | blocks = bbat->follow(entry->start); 1556 | for (unsigned idx = 0; idx < blocks.size(); idx++) 1557 | { 1558 | bbat->set(blocks[idx], AllocTable::Avail); 1559 | bbat->markAsDirty(idx, bbat->blockSize); 1560 | } 1561 | } 1562 | else 1563 | { 1564 | blocks = sbat->follow(entry->start); 1565 | for (unsigned idx = 0; idx < blocks.size(); idx++) 1566 | { 1567 | sbat->set(blocks[idx], AllocTable::Avail); 1568 | sbat->markAsDirty(idx, bbat->blockSize); 1569 | } 1570 | } 1571 | dirtree->deleteEntry(entry, fullName, bbat->blockSize); 1572 | return true; 1573 | } 1574 | 1575 | uint64 StorageIO::loadBigBlocks( std::vector blocks, 1576 | unsigned char* data, uint64 maxlen ) 1577 | { 1578 | // sentinel 1579 | if( !data ) return 0; 1580 | fileCheck(file); 1581 | if( !file.good() ) return 0; 1582 | if( blocks.size() < 1 ) return 0; 1583 | if( maxlen == 0 ) return 0; 1584 | 1585 | // read block one by one, seems fast enough 1586 | uint64 bytes = 0; 1587 | for( unsigned int i=0; (i < blocks.size() ) & ( bytesblockSize * ( block+1 ); 1591 | uint64 p = (bbat->blockSize < maxlen-bytes) ? bbat->blockSize : maxlen-bytes; 1592 | if( pos + p > filesize ) 1593 | p = filesize - pos; 1594 | file.seekg( pos ); 1595 | file.read( (char*)data + bytes, p ); 1596 | fileCheck(file); 1597 | // should use gcount to see how many bytes were really returned - eof check... 1598 | bytes += p; 1599 | } 1600 | 1601 | return bytes; 1602 | } 1603 | 1604 | uint64 StorageIO::loadBigBlock( uint64 block, 1605 | unsigned char* data, uint64 maxlen ) 1606 | { 1607 | // sentinel 1608 | if( !data ) return 0; 1609 | fileCheck(file); 1610 | if( !file.good() ) return 0; 1611 | 1612 | // wraps call for loadBigBlocks 1613 | std::vector blocks; 1614 | blocks.resize( 1 ); 1615 | blocks[ 0 ] = block; 1616 | 1617 | return loadBigBlocks( blocks, data, maxlen ); 1618 | } 1619 | 1620 | uint64 StorageIO::saveBigBlocks( std::vector blocks, uint64 offset, unsigned char* data, uint64 len ) 1621 | { 1622 | // sentinel 1623 | if( !data ) return 0; 1624 | fileCheck(file); 1625 | if( !file.good() ) return 0; 1626 | if( blocks.size() < 1 ) return 0; 1627 | if( len == 0 ) return 0; 1628 | 1629 | // write block one by one, seems fast enough 1630 | uint64 bytes = 0; 1631 | for( unsigned int i=0; (i < blocks.size() ) & ( bytesblockSize * ( block+1 ) ) + offset; 1635 | uint64 maxWrite = bbat->blockSize - offset; 1636 | uint64 tobeWritten = len - bytes; 1637 | if (tobeWritten > maxWrite) 1638 | tobeWritten = maxWrite; 1639 | file.seekp( pos ); 1640 | file.write( (char*)data + bytes, tobeWritten ); 1641 | fileCheck(file); 1642 | 1643 | bytes += tobeWritten; 1644 | offset = 0; 1645 | if (filesize < pos + tobeWritten) 1646 | filesize = pos + tobeWritten; 1647 | } 1648 | 1649 | return bytes; 1650 | 1651 | } 1652 | 1653 | uint64 StorageIO::saveBigBlock( uint64 block, uint64 offset, unsigned char* data, uint64 len ) 1654 | { 1655 | if ( !data ) return 0; 1656 | fileCheck(file); 1657 | if ( !file.good() ) return 0; 1658 | //wrap call for saveBigBlocks 1659 | std::vector blocks; 1660 | blocks.resize( 1 ); 1661 | blocks[ 0 ] = block; 1662 | return saveBigBlocks(blocks, offset, data, len ); 1663 | } 1664 | 1665 | // return number of bytes which has been read 1666 | uint64 StorageIO::loadSmallBlocks( std::vector blocks, 1667 | unsigned char* data, uint64 maxlen ) 1668 | { 1669 | // sentinel 1670 | if( !data ) return 0; 1671 | fileCheck(file); 1672 | if( !file.good() ) return 0; 1673 | if( blocks.size() < 1 ) return 0; 1674 | if( maxlen == 0 ) return 0; 1675 | 1676 | // our own local buffer 1677 | unsigned char* buf = new unsigned char[ bbat->blockSize ]; 1678 | 1679 | // read small block one by one 1680 | uint64 bytes = 0; 1681 | for( unsigned int i=0; ( iblockSize; 1687 | uint64 bbindex = pos / bbat->blockSize; 1688 | if( bbindex >= sb_blocks.size() ) break; 1689 | 1690 | loadBigBlock( sb_blocks[ bbindex ], buf, bbat->blockSize ); 1691 | 1692 | // copy the data 1693 | uint64 offset = pos % bbat->blockSize; 1694 | uint64 p = (maxlen-bytes < bbat->blockSize-offset ) ? maxlen-bytes : bbat->blockSize-offset; 1695 | p = (sbat->blockSize

blockSize : p; 1696 | memcpy( data + bytes, buf + offset, p ); 1697 | bytes += p; 1698 | } 1699 | 1700 | delete[] buf; 1701 | 1702 | return bytes; 1703 | } 1704 | 1705 | uint64 StorageIO::loadSmallBlock( uint64 block, 1706 | unsigned char* data, uint64 maxlen ) 1707 | { 1708 | // sentinel 1709 | if( !data ) return 0; 1710 | fileCheck(file); 1711 | if( !file.good() ) return 0; 1712 | 1713 | // wraps call for loadSmallBlocks 1714 | std::vector blocks; 1715 | blocks.resize( 1 ); 1716 | blocks.assign( 1, block ); 1717 | 1718 | return loadSmallBlocks( blocks, data, maxlen ); 1719 | } 1720 | 1721 | 1722 | uint64 StorageIO::saveSmallBlocks( std::vector blocks, uint64 offset, 1723 | unsigned char* data, uint64 len, int64 startAtBlock ) 1724 | { 1725 | // sentinel 1726 | if( !data ) return 0; 1727 | fileCheck(file); 1728 | if( !file.good() ) return 0; 1729 | if( blocks.size() < 1 ) return 0; 1730 | if( len == 0 ) return 0; 1731 | 1732 | // write block one by one, seems fast enough 1733 | uint64 bytes = 0; 1734 | for( uint64 i = startAtBlock; (i < blocks.size() ) & ( bytesblockSize; 1739 | uint64 bbindex = pos / bbat->blockSize; 1740 | if( bbindex >= sb_blocks.size() ) break; 1741 | uint64 offset2 = pos % bbat->blockSize; 1742 | uint64 maxWrite = sbat->blockSize - offset; 1743 | uint64 tobeWritten = len - bytes; 1744 | if (tobeWritten > maxWrite) 1745 | tobeWritten = maxWrite; 1746 | saveBigBlock( sb_blocks[ bbindex ], offset2 + offset, data + bytes, tobeWritten); 1747 | bytes += tobeWritten; 1748 | offset = 0; 1749 | if (filesize < pos + tobeWritten) 1750 | filesize = pos + tobeWritten; 1751 | } 1752 | return bytes; 1753 | } 1754 | 1755 | uint64 StorageIO::saveSmallBlock( uint64 block, uint64 offset, unsigned char* data, uint64 len ) 1756 | { 1757 | if ( !data ) return 0; 1758 | fileCheck(file); 1759 | if ( !file.good() ) return 0; 1760 | //wrap call for saveSmallBlocks 1761 | std::vector blocks; 1762 | blocks.resize( 1 ); 1763 | blocks[ 0 ] = block; 1764 | return saveSmallBlocks(blocks, offset, data, len ); 1765 | } 1766 | 1767 | void StorageIO::flushbbat() 1768 | { 1769 | std::vector blocks; 1770 | blocks = getbbatBlocks(false); 1771 | bbat->flush(blocks, this, bbat->blockSize); 1772 | } 1773 | 1774 | void StorageIO::flushsbat() 1775 | { 1776 | std::vector blocks; 1777 | blocks = bbat->follow(header->sbat_start); 1778 | sbat->flush(blocks, this, bbat->blockSize); 1779 | } 1780 | 1781 | std::vector StorageIO::getbbatBlocks(bool bLoading) 1782 | { 1783 | std::vector blocks; 1784 | // find blocks allocated to store big bat 1785 | // the first 109 blocks are in header, the rest in meta bat 1786 | blocks.clear(); 1787 | blocks.resize( header->num_bat ); 1788 | 1789 | for( unsigned i = 0; i < 109; i++ ) 1790 | { 1791 | if( i >= header->num_bat ) 1792 | break; 1793 | else 1794 | blocks[i] = header->bb_blocks[i]; 1795 | } 1796 | if (bLoading) 1797 | { 1798 | mbat_blocks.clear(); 1799 | mbat_data.clear(); 1800 | if( (header->num_bat > 109) && (header->num_mbat > 0) ) 1801 | { 1802 | unsigned char* buffer2 = new unsigned char[ bbat->blockSize ]; 1803 | uint64 k = 109; 1804 | uint64 sector; 1805 | uint64 mdidx = 0; 1806 | for( uint64 r = 0; r < header->num_mbat; r++ ) 1807 | { 1808 | if(r == 0) // 1st meta bat location is in file header. 1809 | sector = header->mbat_start; 1810 | else // next meta bat location is the last current block value. 1811 | { 1812 | sector = blocks[--k]; 1813 | mdidx--; 1814 | } 1815 | mbat_blocks.push_back(sector); 1816 | mbat_data.resize(mbat_blocks.size()*(bbat->blockSize/4)); 1817 | loadBigBlock( sector, buffer2, bbat->blockSize ); 1818 | for( uint64 s=0; s < bbat->blockSize; s+=4 ) 1819 | { 1820 | if( k >= header->num_bat ) 1821 | break; 1822 | else 1823 | { 1824 | blocks[k] = readU32( buffer2 + s ); 1825 | mbat_data[mdidx++] = blocks[k]; 1826 | k++; 1827 | } 1828 | } 1829 | } 1830 | if (mbat_data.size() != mdidx) mbat_data.resize(mdidx); 1831 | delete[] buffer2; 1832 | } 1833 | } 1834 | else 1835 | { 1836 | unsigned i = 109; 1837 | for (unsigned int idx = 0; idx < mbat_data.size(); idx++) 1838 | { 1839 | blocks[i++] = mbat_data[idx]; 1840 | if (i == header->num_bat) 1841 | break; 1842 | } 1843 | } 1844 | return blocks; 1845 | } 1846 | 1847 | uint64 StorageIO::ExtendFile( std::vector *chain ) 1848 | { 1849 | uint64 newblockIdx = bbat->unused(); 1850 | bbat->set(newblockIdx, AllocTable::Eof); 1851 | uint64 bbidx = newblockIdx / (bbat->blockSize / sizeof(uint64)); 1852 | while (bbidx >= header->num_bat) 1853 | addbbatBlock(); 1854 | bbat->markAsDirty(newblockIdx, bbat->blockSize); 1855 | if (chain->size() > 0) 1856 | { 1857 | bbat->set((*chain)[chain->size()-1], newblockIdx); 1858 | bbat->markAsDirty((*chain)[chain->size()-1], bbat->blockSize); 1859 | } 1860 | chain->push_back(newblockIdx); 1861 | return newblockIdx; 1862 | } 1863 | 1864 | void StorageIO::addbbatBlock() 1865 | { 1866 | uint64 newblockIdx = bbat->unused(); 1867 | bbat->set(newblockIdx, AllocTable::MetaBat); 1868 | 1869 | if (header->num_bat < 109) 1870 | header->bb_blocks[header->num_bat] = newblockIdx; 1871 | else 1872 | { 1873 | mbatDirty = true; 1874 | mbat_data.push_back(newblockIdx); 1875 | uint64 metaIdx = header->num_bat - 109; 1876 | uint64 idxPerBlock = bbat->blockSize / sizeof(uint64) - 1; //reserve room for index to next block 1877 | uint64 idxBlock = metaIdx / idxPerBlock; 1878 | if (idxBlock == mbat_blocks.size()) 1879 | { 1880 | uint64 newmetaIdx = bbat->unused(); 1881 | bbat->set(newmetaIdx, AllocTable::MetaBat); 1882 | mbat_blocks.push_back(newmetaIdx); 1883 | if (header->num_mbat == 0) 1884 | header->mbat_start = newmetaIdx; 1885 | header->num_mbat++; 1886 | } 1887 | } 1888 | header->num_bat++; 1889 | header->dirty = true; 1890 | } 1891 | 1892 | 1893 | // =========== StreamIO ========== 1894 | 1895 | StreamIO::StreamIO( StorageIO* s, DirEntry* e) 1896 | : io(s), 1897 | entryIdx(io->dirtree->indexOf(e)), 1898 | fullName(), 1899 | blocks(), 1900 | eof(false), 1901 | fail(false), 1902 | m_pos(0), 1903 | cache_data(new unsigned char[CACHEBUFSIZE]), 1904 | cache_size(0), // indicating an empty cache 1905 | cache_pos(0) 1906 | { 1907 | if( e->size >= io->header->threshold ) 1908 | blocks = io->bbat->follow( e->start ); 1909 | else 1910 | blocks = io->sbat->follow( e->start ); 1911 | } 1912 | 1913 | // FIXME tell parent we're gone 1914 | StreamIO::~StreamIO() 1915 | { 1916 | delete[] cache_data; 1917 | } 1918 | 1919 | void StreamIO::setSize(uint64 newSize) 1920 | { 1921 | bool bThresholdCrossed = false; 1922 | bool bOver = false; 1923 | 1924 | if(!io->writeable ) 1925 | return; 1926 | DirEntry *entry = io->dirtree->entry(entryIdx); 1927 | if (newSize >= io->header->threshold && entry->size < io->header->threshold) 1928 | { 1929 | bThresholdCrossed = true; 1930 | bOver = true; 1931 | } 1932 | else if (newSize < io->header->threshold && entry->size >= io->header->threshold) 1933 | { 1934 | bThresholdCrossed = true; 1935 | bOver = false; 1936 | } 1937 | if (bThresholdCrossed) 1938 | { 1939 | // first, read what is already in the stream, limited by the requested new size. Note 1940 | // that the read can work precisely because we have not yet reset the size. 1941 | uint64 len = newSize; 1942 | if (len > entry->size) 1943 | len = entry->size; 1944 | unsigned char *buffer = 0; 1945 | uint64 savePos = tell(); 1946 | if (len) 1947 | { 1948 | buffer = new unsigned char[len]; 1949 | seek(0); 1950 | read(buffer, len); 1951 | } 1952 | // Now get rid of the existing blocks 1953 | if (bOver) 1954 | { 1955 | for (unsigned int idx = 0; idx < blocks.size(); idx++) 1956 | { 1957 | io->sbat->set(blocks[idx], AllocTable::Avail); 1958 | io->sbat->markAsDirty(idx, io->bbat->blockSize); 1959 | } 1960 | } 1961 | else 1962 | { 1963 | for (unsigned int idx = 0; idx < blocks.size(); idx++) 1964 | { 1965 | io->bbat->set(blocks[idx], AllocTable::Avail); 1966 | io->bbat->markAsDirty(idx, io->bbat->blockSize); 1967 | } 1968 | } 1969 | blocks.clear(); 1970 | entry->start = DirTree::End; 1971 | // Now change the size, and write the old data back into the stream, if any 1972 | entry->size = newSize; 1973 | io->dirtree->markAsDirty(io->dirtree->indexOf(entry), io->bbat->blockSize); 1974 | if (len) 1975 | { 1976 | write(0, buffer, len); 1977 | delete buffer; 1978 | } 1979 | if (savePos <= entry->size) 1980 | seek(savePos); 1981 | } 1982 | else if (entry->size != newSize) //simple case - no threshold was crossed, so just change the size 1983 | { 1984 | entry->size = newSize; 1985 | io->dirtree->markAsDirty(io->dirtree->indexOf(entry), io->bbat->blockSize); 1986 | } 1987 | 1988 | } 1989 | 1990 | void StreamIO::seek( uint64 pos ) 1991 | { 1992 | m_pos = pos; 1993 | } 1994 | 1995 | uint64 StreamIO::tell() 1996 | { 1997 | return m_pos; 1998 | } 1999 | 2000 | int64 StreamIO::getch() 2001 | { 2002 | // past end-of-file ? 2003 | DirEntry *entry = io->dirtree->entry(entryIdx); 2004 | if( m_pos >= entry->size ) return -1; 2005 | 2006 | // need to update cache ? 2007 | if( !cache_size || ( m_pos < cache_pos ) || 2008 | ( m_pos >= cache_pos + cache_size ) ) 2009 | updateCache(); 2010 | 2011 | // something bad if we don't get good cache 2012 | if( !cache_size ) return -1; 2013 | 2014 | int64 data = cache_data[m_pos - cache_pos]; 2015 | m_pos++; 2016 | 2017 | return data; 2018 | } 2019 | 2020 | uint64 StreamIO::read( uint64 pos, unsigned char* data, uint64 maxlen ) 2021 | { 2022 | // sanity checks 2023 | if( !data ) return 0; 2024 | if( maxlen == 0 ) return 0; 2025 | 2026 | uint64 totalbytes = 0; 2027 | 2028 | DirEntry *entry = io->dirtree->entry(entryIdx); 2029 | if (pos + maxlen > entry->size) 2030 | maxlen = entry->size - pos; 2031 | if ( entry->size < io->header->threshold ) 2032 | { 2033 | // small file 2034 | uint64 index = pos / io->sbat->blockSize; 2035 | 2036 | if( index >= blocks.size() ) return 0; 2037 | 2038 | unsigned char* buf = new unsigned char[ io->sbat->blockSize ]; 2039 | uint64 offset = pos % io->sbat->blockSize; 2040 | while( totalbytes < maxlen ) 2041 | { 2042 | if( index >= blocks.size() ) break; 2043 | io->loadSmallBlock( blocks[index], buf, io->bbat->blockSize ); 2044 | uint64 count = io->sbat->blockSize - offset; 2045 | if( count > maxlen-totalbytes ) count = maxlen-totalbytes; 2046 | memcpy( data+totalbytes, buf + offset, count ); 2047 | totalbytes += count; 2048 | offset = 0; 2049 | index++; 2050 | } 2051 | delete[] buf; 2052 | 2053 | } 2054 | else 2055 | { 2056 | // big file 2057 | uint64 index = pos / io->bbat->blockSize; 2058 | 2059 | if( index >= blocks.size() ) return 0; 2060 | 2061 | unsigned char* buf = new unsigned char[ io->bbat->blockSize ]; 2062 | uint64 offset = pos % io->bbat->blockSize; 2063 | while( totalbytes < maxlen ) 2064 | { 2065 | if( index >= blocks.size() ) break; 2066 | io->loadBigBlock( blocks[index], buf, io->bbat->blockSize ); 2067 | uint64 count = io->bbat->blockSize - offset; 2068 | if( count > maxlen-totalbytes ) count = maxlen-totalbytes; 2069 | memcpy( data+totalbytes, buf + offset, count ); 2070 | totalbytes += count; 2071 | index++; 2072 | offset = 0; 2073 | } 2074 | delete [] buf; 2075 | 2076 | } 2077 | 2078 | return totalbytes; 2079 | } 2080 | 2081 | uint64 StreamIO::read( unsigned char* data, uint64 maxlen ) 2082 | { 2083 | uint64 bytes = read( tell(), data, maxlen ); 2084 | m_pos += bytes; 2085 | return bytes; 2086 | } 2087 | 2088 | uint64 StreamIO::write( unsigned char* data, uint64 len ) 2089 | { 2090 | return write( tell(), data, len ); 2091 | } 2092 | 2093 | uint64 StreamIO::write( uint64 pos, unsigned char* data, uint64 len ) 2094 | { 2095 | // sanity checks 2096 | if( !data ) return 0; 2097 | if( len == 0 ) return 0; 2098 | if( !io->writeable ) return 0; 2099 | 2100 | DirEntry *entry = io->dirtree->entry(entryIdx); 2101 | if (pos + len > entry->size) 2102 | setSize(pos + len); //reset size, possibly changing from small to large blocks 2103 | uint64 totalbytes = 0; 2104 | if ( entry->size < io->header->threshold ) 2105 | { 2106 | // small file 2107 | uint64 index = (pos + len - 1) / io->sbat->blockSize; 2108 | while (index >= blocks.size()) 2109 | { 2110 | uint64 nblock = io->sbat->unused(); 2111 | if (blocks.size() > 0) 2112 | { 2113 | io->sbat->set(blocks[blocks.size()-1], nblock); 2114 | io->sbat->markAsDirty(blocks[blocks.size()-1], io->bbat->blockSize); 2115 | } 2116 | io->sbat->set(nblock, AllocTable::Eof); 2117 | io->sbat->markAsDirty(nblock, io->bbat->blockSize); 2118 | blocks.resize(blocks.size()+1); 2119 | blocks[blocks.size()-1] = nblock; 2120 | uint64 bbidx = nblock / (io->bbat->blockSize / sizeof(unsigned int)); 2121 | while (bbidx >= io->header->num_sbat) 2122 | { 2123 | std::vector sbat_blocks = io->bbat->follow(io->header->sbat_start); 2124 | io->ExtendFile(&sbat_blocks); 2125 | io->header->num_sbat++; 2126 | io->header->dirty = true; //Header will have to be rewritten 2127 | } 2128 | uint64 sidx = nblock * io->sbat->blockSize / io->bbat->blockSize; 2129 | while (sidx >= io->sb_blocks.size()) 2130 | { 2131 | io->ExtendFile(&io->sb_blocks); 2132 | io->dirtree->markAsDirty(0, io->bbat->blockSize); //make sure to rewrite first directory block 2133 | } 2134 | } 2135 | uint64 offset = pos % io->sbat->blockSize; 2136 | index = pos / io->sbat->blockSize; 2137 | //if (index == 0) 2138 | totalbytes = io->saveSmallBlocks(blocks, offset, data, len, index); 2139 | } 2140 | else 2141 | { 2142 | uint64 index = (pos + len - 1) / io->bbat->blockSize; 2143 | while (index >= blocks.size()) 2144 | io->ExtendFile(&blocks); 2145 | uint64 offset = pos % io->bbat->blockSize; 2146 | uint64 remainder = len; 2147 | index = pos / io->bbat->blockSize; 2148 | while( remainder > 0 ) 2149 | { 2150 | if( index >= blocks.size() ) break; 2151 | uint64 count = io->bbat->blockSize - offset; 2152 | if ( remainder < count ) 2153 | count = remainder; 2154 | io->saveBigBlock( blocks[index], offset, data + totalbytes, count ); 2155 | totalbytes += count; 2156 | remainder -= count; 2157 | index++; 2158 | offset = 0; 2159 | } 2160 | } 2161 | if (blocks.size() > 0 && entry->start != blocks[0]) 2162 | { 2163 | entry->start = blocks[0]; 2164 | io->dirtree->markAsDirty(io->dirtree->indexOf(entry), io->bbat->blockSize); 2165 | } 2166 | m_pos += len; 2167 | return totalbytes; 2168 | } 2169 | 2170 | void StreamIO::flush() 2171 | { 2172 | io->flush(); 2173 | } 2174 | 2175 | void StreamIO::updateCache() 2176 | { 2177 | // sanity check 2178 | if( !cache_data ) return; 2179 | 2180 | DirEntry *entry = io->dirtree->entry(entryIdx); 2181 | cache_pos = m_pos - (m_pos % CACHEBUFSIZE); 2182 | uint64 bytes = CACHEBUFSIZE; 2183 | if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos; 2184 | cache_size = read( cache_pos, cache_data, bytes ); 2185 | } 2186 | 2187 | 2188 | // =========== Storage ========== 2189 | 2190 | Storage::Storage( const char* filename ) 2191 | { 2192 | io = new StorageIO( this, filename ); 2193 | } 2194 | 2195 | Storage::~Storage() 2196 | { 2197 | delete io; 2198 | } 2199 | 2200 | int Storage::result() 2201 | { 2202 | return (int) io->result; 2203 | } 2204 | 2205 | bool Storage::open(bool bWriteAccess, bool bCreate) 2206 | { 2207 | return io->open(bWriteAccess, bCreate); 2208 | } 2209 | 2210 | void Storage::close() 2211 | { 2212 | io->close(); 2213 | } 2214 | 2215 | std::list Storage::entries( const std::string& path ) 2216 | { 2217 | std::list localResult; 2218 | DirTree* dt = io->dirtree; 2219 | DirEntry* e = dt->entry( path, false ); 2220 | if( e && e->dir ) 2221 | { 2222 | uint64 parent = dt->indexOf( e ); 2223 | std::vector children = dt->children( parent ); 2224 | for( uint64 i = 0; i < children.size(); i++ ) 2225 | localResult.push_back( dt->entry( children[i] )->name ); 2226 | } 2227 | 2228 | return localResult; 2229 | } 2230 | 2231 | bool Storage::isDirectory( const std::string& name ) 2232 | { 2233 | DirEntry* e = io->dirtree->entry( name, false ); 2234 | return e ? e->dir : false; 2235 | } 2236 | 2237 | bool Storage::exists( const std::string& name ) 2238 | { 2239 | DirEntry* e = io->dirtree->entry( name, false ); 2240 | return (e != 0); 2241 | } 2242 | 2243 | bool Storage::isWriteable() 2244 | { 2245 | return io->writeable; 2246 | } 2247 | 2248 | bool Storage::deleteByName( const std::string& name ) 2249 | { 2250 | return io->deleteByName(name); 2251 | } 2252 | 2253 | void Storage::GetStats(uint64 *pEntries, uint64 *pUnusedEntries, 2254 | uint64 *pBigBlocks, uint64 *pUnusedBigBlocks, 2255 | uint64 *pSmallBlocks, uint64 *pUnusedSmallBlocks) 2256 | { 2257 | *pEntries = io->dirtree->entryCount(); 2258 | *pUnusedEntries = io->dirtree->unusedEntryCount(); 2259 | *pBigBlocks = io->bbat->count(); 2260 | *pUnusedBigBlocks = io->bbat->unusedCount(); 2261 | *pSmallBlocks = io->sbat->count(); 2262 | *pUnusedSmallBlocks = io->sbat->unusedCount(); 2263 | } 2264 | 2265 | // recursively collect stream names 2266 | void CollectStreams( std::list& result, DirTree* tree, DirEntry* parent, const std::string& path ) 2267 | { 2268 | DirEntry* c = tree->entry( parent->child ); 2269 | std::queue queue; 2270 | if ( c ) queue.push( c ); 2271 | while ( !queue.empty() ) { 2272 | DirEntry* e = queue.front(); 2273 | queue.pop(); 2274 | if ( e->dir ) 2275 | CollectStreams( result, tree, e, path + e->name + "/" ); 2276 | else 2277 | result.push_back( path + e->name ); 2278 | DirEntry* p = tree->entry( e->prev ); 2279 | if ( p ) queue.push( p ); 2280 | DirEntry* n = tree->entry( e->next ); 2281 | if ( n ) queue.push( n ); 2282 | // not testing if p or n have already been processed; potential infinite loop in case of closed Entry chain 2283 | // it seems not to happen, though 2284 | } 2285 | } 2286 | 2287 | std::list Storage::GetAllStreams( const std::string& storageName ) 2288 | { 2289 | std::list vresult; 2290 | DirEntry* e = io->dirtree->entry( storageName, false ); 2291 | if ( e && e->dir ) CollectStreams( vresult, io->dirtree, e, storageName ); 2292 | return vresult; 2293 | } 2294 | 2295 | // =========== Stream ========== 2296 | 2297 | Stream::Stream( Storage* storage, const std::string& name, bool bCreate, int64 streamSize ) 2298 | : io(storage->io->streamIO( name, bCreate, (int) streamSize )) 2299 | { 2300 | } 2301 | 2302 | // FIXME tell parent we're gone 2303 | Stream::~Stream() 2304 | { 2305 | delete io; 2306 | } 2307 | 2308 | std::string Stream::fullName() 2309 | { 2310 | return io ? io->fullName : std::string(); 2311 | } 2312 | 2313 | uint64 Stream::tell() 2314 | { 2315 | return io ? io->tell() : 0; 2316 | } 2317 | 2318 | void Stream::seek( uint64 newpos ) 2319 | { 2320 | if( io ) 2321 | io->seek(newpos); 2322 | } 2323 | 2324 | uint64 Stream::size() 2325 | { 2326 | if (!io) 2327 | return 0; 2328 | DirEntry *entry = io->io->dirtree->entry(io->entryIdx); 2329 | return entry->size; 2330 | } 2331 | 2332 | void Stream::setSize(int64 newSize) 2333 | { 2334 | if (!io) 2335 | return; 2336 | if (newSize < 0) 2337 | return; 2338 | if (newSize > std::numeric_limits::max()) 2339 | return; 2340 | io->setSize(newSize); 2341 | } 2342 | 2343 | int64 Stream::getch() 2344 | { 2345 | return io ? io->getch() : 0; 2346 | } 2347 | 2348 | uint64 Stream::read( unsigned char* data, uint64 maxlen ) 2349 | { 2350 | return io ? io->read( data, maxlen ) : 0; 2351 | } 2352 | 2353 | uint64 Stream::write( unsigned char* data, uint64 len ) 2354 | { 2355 | return io ? io->write( data, len ) : 0; 2356 | } 2357 | 2358 | void Stream::flush() 2359 | { 2360 | if (io) 2361 | io->flush(); 2362 | } 2363 | 2364 | bool Stream::eof() 2365 | { 2366 | return io ? io->eof : false; 2367 | } 2368 | 2369 | bool Stream::fail() 2370 | { 2371 | return io ? io->fail : true; 2372 | } 2373 | -------------------------------------------------------------------------------- /pole.h: -------------------------------------------------------------------------------- 1 | /* POLE - Portable C++ library to access OLE Storage 2 | Copyright (C) 2002-2005 Ariya Hidayat 3 | 4 | Performance optimization, API improvements: Dmitry Fedorov 5 | Copyright 2009-2014 6 | 7 | Fix for more than 236 mbat block entries : Michel Boudinot 8 | Copyright 2010 9 | 10 | Considerable rework to allow for creation and updating of structured storage: Stephen Baum 11 | Copyright 2013 12 | 13 | Added GetAllStreams, reworked datatypes 14 | Copyright 2013 Felix Gorny from Bitplane 15 | 16 | More datatype changes to allow for 32 and 64 bit code, some fixes involving incremental updates, flushing 17 | Copyright 2013 18 | 19 | Version: 0.5.3 20 | 21 | Redistribution and use in source and binary forms, with or without 22 | modification, are permitted provided that the following conditions 23 | are met: 24 | * Redistributions of source code must retain the above copyright notice, 25 | this list of conditions and the following disclaimer. 26 | * Redistributions in binary form must reproduce the above copyright notice, 27 | this list of conditions and the following disclaimer in the documentation 28 | and/or other materials provided with the distribution. 29 | * Neither the name of the authors nor the names of its contributors may be 30 | used to endorse or promote products derived from this software without 31 | specific prior written permission. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 34 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 37 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 38 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 41 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 42 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 43 | THE POSSIBILITY OF SUCH DAMAGE. 44 | */ 45 | 46 | /* 47 | Unicode notes: 48 | 49 | Filenames are considered to be encoded in UTF-8 encoding. On windows they 50 | can be re-encoded into UTF16 and proper wchar_t APIs will be used to open files. 51 | This is a default behavior for windows and is defined by the macro POLE_USE_UTF16_FILENAMES. 52 | 53 | Using a provided function and a modern c++ compiler it's easy to encode a 54 | wide string into utf8 char*: 55 | std::string POLE::UTF16toUTF8(const std::wstring &utf16); 56 | */ 57 | 58 | #ifndef POLE_H 59 | #define POLE_H 60 | 61 | #include 62 | #include 63 | #include 64 | 65 | namespace POLE 66 | { 67 | 68 | #if defined WIN32 || defined WIN64 || defined _WIN32 || defined _WIN64 || defined _MSVC 69 | #define POLE_USE_UTF16_FILENAMES 70 | #define POLE_WIN 71 | typedef __int32 int32; 72 | typedef __int64 int64; 73 | typedef unsigned __int32 uint32; 74 | typedef unsigned __int64 uint64; 75 | #else 76 | typedef int int32; 77 | typedef long long int64; 78 | typedef unsigned int uint32; 79 | typedef unsigned long long uint64; 80 | #endif 81 | 82 | typedef uint64 t_offset; 83 | 84 | #ifdef POLE_USE_UTF16_FILENAMES 85 | std::string UTF16toUTF8(const std::wstring &utf16); 86 | std::wstring UTF8toUTF16(const std::string &utf8); 87 | #endif //POLE_USE_UTF16_FILENAMES 88 | 89 | class StorageIO; 90 | class Stream; 91 | class StreamIO; 92 | 93 | class Storage 94 | { 95 | friend class Stream; 96 | friend class StreamOut; 97 | 98 | public: 99 | 100 | // for Storage::result() 101 | enum { Ok, OpenFailed, NotOLE, BadOLE, UnknownError }; 102 | 103 | /** 104 | * Constructs a storage with name filename. 105 | **/ 106 | Storage( const char* filename ); 107 | 108 | /** 109 | * Destroys the storage. 110 | **/ 111 | ~Storage(); 112 | 113 | /** 114 | * Opens the storage. Returns true if no error occurs. 115 | **/ 116 | bool open(bool bWriteAccess = false, bool bCreate = false); 117 | 118 | /** 119 | * Closes the storage. 120 | **/ 121 | void close(); 122 | 123 | /** 124 | * Returns the error code of last operation. 125 | **/ 126 | int result(); 127 | 128 | /** 129 | * Finds all stream and directories in given path. 130 | **/ 131 | std::list entries( const std::string& path = "/" ); 132 | 133 | /** 134 | * Returns true if specified entry name is a directory. 135 | */ 136 | bool isDirectory( const std::string& name ); 137 | 138 | /** 139 | * Returns true if specified entry name exists. 140 | */ 141 | bool exists( const std::string& name ); 142 | 143 | /** 144 | * Returns true if storage can be modified. 145 | */ 146 | bool isWriteable(); 147 | 148 | /** 149 | * Deletes a specified stream or directory. If directory, it will 150 | * recursively delete everything underneath said directory. 151 | * returns true for success 152 | */ 153 | bool deleteByName( const std::string& name ); 154 | 155 | /** 156 | * Returns an accumulation of information, hopefully useful for determining if the storage 157 | * should be defragmented. 158 | */ 159 | 160 | void GetStats(uint64 *pEntries, uint64 *pUnusedEntries, 161 | uint64 *pBigBlocks, uint64 *pUnusedBigBlocks, 162 | uint64 *pSmallBlocks, uint64 *pUnusedSmallBlocks); 163 | 164 | std::list GetAllStreams( const std::string& storageName ); 165 | 166 | private: 167 | StorageIO* io; 168 | 169 | // no copy or assign 170 | Storage( const Storage& ); 171 | Storage& operator=( const Storage& ); 172 | 173 | }; 174 | 175 | class Stream 176 | { 177 | friend class Storage; 178 | friend class StorageIO; 179 | 180 | public: 181 | 182 | /** 183 | * Creates a new stream. 184 | */ 185 | // name must be absolute, e.g "/Workbook" 186 | Stream( Storage* storage, const std::string& name, bool bCreate = false, int64 streamSize = 0); 187 | 188 | /** 189 | * Destroys the stream. 190 | */ 191 | ~Stream(); 192 | 193 | /** 194 | * Returns the full stream name. 195 | */ 196 | std::string fullName(); 197 | 198 | /** 199 | * Returns the stream size. 200 | **/ 201 | uint64 size(); 202 | 203 | /** 204 | * Changes the stream size (note this is done automatically if you write beyond the old size. 205 | * Use this primarily as a preamble to rewriting a stream that is already open. Of course, you 206 | * could simply delete the stream first). 207 | **/ 208 | void setSize(int64 newSize); 209 | 210 | /** 211 | * Returns the current read/write position. 212 | **/ 213 | uint64 tell(); 214 | 215 | /** 216 | * Sets the read/write position. 217 | **/ 218 | void seek( uint64 pos ); 219 | 220 | /** 221 | * Reads a byte. 222 | **/ 223 | int64 getch(); 224 | 225 | /** 226 | * Reads a block of data. 227 | **/ 228 | uint64 read( unsigned char* data, uint64 maxlen ); 229 | 230 | /** 231 | * Writes a block of data. 232 | **/ 233 | uint64 write( unsigned char* data, uint64 len ); 234 | 235 | /** 236 | * Makes sure that any changes for the stream (and the structured storage) have been written to disk. 237 | **/ 238 | void flush(); 239 | 240 | /** 241 | * Returns true if the read/write position is past the file. 242 | **/ 243 | bool eof(); 244 | 245 | /** 246 | * Returns true whenever error occurs. 247 | **/ 248 | bool fail(); 249 | 250 | private: 251 | StreamIO* io; 252 | 253 | // no copy or assign 254 | Stream( const Stream& ); 255 | Stream& operator=( const Stream& ); 256 | }; 257 | 258 | } 259 | 260 | #endif // POLE_H 261 | -------------------------------------------------------------------------------- /poledump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dav-grig/pole/04cc26d34e4e2ef4cdffd269141585b88e2c0e38/poledump -------------------------------------------------------------------------------- /poledump.cpp: -------------------------------------------------------------------------------- 1 | /* POLE - Portable library to access OLE Storage 2 | Copyright (C) 2002-2005 Ariya Hidayat 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 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "pole.h" 37 | 38 | void visit( int indent, POLE::Storage* storage, std::string path ) 39 | { 40 | std::list entries; 41 | entries = storage->entries( path ); 42 | 43 | std::list::iterator it; 44 | for( it = entries.begin(); it != entries.end(); ++it ) 45 | { 46 | std::string name = *it; 47 | std::string fullname = path + name; 48 | for( int j = 0; j < indent; j++ ) std::cout << " "; 49 | POLE::Stream* ss = new POLE::Stream( storage, fullname ); 50 | std::cout << name; 51 | if( ss ) if( !ss->fail() )std::cout << " (" << ss->size() << ")"; 52 | std::cout << std::endl; 53 | delete ss; 54 | 55 | if( storage->isDirectory( fullname ) ) 56 | visit( indent+1, storage, fullname + "/" ); 57 | } 58 | 59 | } 60 | 61 | void dump( POLE::Storage* storage, char* stream_name ) 62 | { 63 | POLE::Stream* stream = new POLE::Stream( storage, stream_name ); 64 | if( !stream ) return; 65 | if( stream->fail() ) return; 66 | 67 | // std::cout << "Size: " << stream->size() << " bytes" << std::endl; 68 | unsigned char buffer[16]; 69 | for( ;; ) 70 | { 71 | unsigned read = stream->read( buffer, sizeof( buffer ) ); 72 | for( unsigned i = 0; i < read; i++ ) 73 | printf( "%02x ", buffer[i] ); 74 | std::cout << " "; 75 | for( unsigned i = 0; i < read; i++ ) 76 | printf( "%c", ((buffer[i]>=32)&&(buffer[i]<128)) ? buffer[i] : '.' ); 77 | std::cout << std::endl; 78 | if( read < sizeof( buffer ) ) break; 79 | } 80 | 81 | delete stream; 82 | } 83 | 84 | void extract( POLE::Storage* storage, char* stream_name, char* outfile ) 85 | { 86 | POLE::Stream* stream = new POLE::Stream( storage, stream_name ); 87 | if( !stream ) return; 88 | if( stream->fail() ) return; 89 | 90 | std::ofstream file; 91 | file.open( outfile, std::ios::binary|std::ios::out ); 92 | 93 | unsigned char buffer[16]; 94 | for( ;; ) 95 | { 96 | unsigned read = stream->read( buffer, sizeof( buffer ) ); 97 | file.write( (const char*)buffer, read ); 98 | if( read < sizeof( buffer ) ) break; 99 | } 100 | file.close(); 101 | 102 | delete stream; 103 | } 104 | 105 | int main(int argc, char *argv[]) 106 | { 107 | if( argc < 2 ) 108 | { 109 | std::cout << "Usage:" << std::endl; 110 | std::cout << argv[0] << " filename [stream-name [output-file]]" << std::endl; 111 | return 0; 112 | } 113 | 114 | char* filename = argv[1]; 115 | char* streamname = (argc<3) ? 0 : argv[2]; 116 | char* outfile = (argc<4) ? 0 : argv[3]; 117 | 118 | POLE::Storage* storage = new POLE::Storage( filename ); 119 | storage->open(); 120 | if( storage->result() != POLE::Storage::Ok ) 121 | { 122 | std::cout << "Error on file " << filename << std::endl; 123 | return 1; 124 | } 125 | 126 | if( !streamname ) 127 | visit( 0, storage, "/" ); 128 | else if( !outfile ) 129 | dump( storage, streamname ); 130 | else 131 | extract( storage, streamname, outfile ); 132 | 133 | delete storage; 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /poleview.cpp: -------------------------------------------------------------------------------- 1 | /* POLEView - Graphical utility to view structure storage 2 | Copyright (C) 2005 Ariya Hidayat 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 "poleview.h" 30 | #include "pole.h" 31 | 32 | PoleView::PoleView() 33 | { 34 | storage = 0; 35 | 36 | view = new QTreeWidget( this ); 37 | view->setColumnCount( 2 ); 38 | view->setHeaderLabels( QStringList() << tr("Name" ) << tr("Size" ) ); 39 | 40 | view->setAlternatingRowColors ( true ); 41 | setCentralWidget( view ); 42 | 43 | // create actions 44 | 45 | // file 46 | QAction *openAct = new QAction(tr("&Open..."), this); 47 | openAct->setShortcut(tr("Ctrl+O")); 48 | connect(openAct, SIGNAL(triggered()), this, SLOT(choose())); 49 | 50 | QAction *newWindowAct = new QAction(tr("&New Window"), this); 51 | newWindowAct->setShortcut(tr("Ctrl+N")); 52 | connect(newWindowAct, SIGNAL(triggered()), this, SLOT(newWindow())); 53 | 54 | QAction *closeAct = new QAction(tr("&Close"), this); 55 | closeAct->setShortcut(tr("Ctrl+C")); 56 | connect(closeAct, SIGNAL(triggered()), this, SLOT(closeFile())); 57 | 58 | QAction *exitAct = new QAction(tr("&Quit"), this); 59 | exitAct->setShortcut(tr("Ctrl+Q")); 60 | connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); 61 | 62 | // stream 63 | QAction *exportAct = new QAction(tr("&Export..."), this); 64 | connect(exportAct, SIGNAL(triggered()), this, SLOT(exportStream())); 65 | 66 | QAction *viewAct = new QAction(tr("&View..."), this); 67 | connect(viewAct, SIGNAL(triggered()), this, SLOT(viewStream())); 68 | 69 | // help 70 | QAction *aboutAct = new QAction(tr("&About"), this); 71 | connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); 72 | 73 | QAction *aboutQtAct = new QAction(tr("About Qt"), this); 74 | connect(aboutQtAct, SIGNAL(triggered()), this, SLOT(aboutQt())); 75 | 76 | 77 | // create menus 78 | QMenu *fileMenu = new QMenu(tr("&File"), this); 79 | fileMenu->addAction(openAct); 80 | fileMenu->addAction(newWindowAct); 81 | fileMenu->addAction(closeAct); 82 | fileMenu->addSeparator(); 83 | fileMenu->addAction(exitAct); 84 | 85 | QMenu *streamMenu = new QMenu(tr("&Stream"), this); 86 | streamMenu->addAction(exportAct); 87 | streamMenu->addAction(viewAct); 88 | 89 | QMenu *helpMenu = new QMenu(tr("&Help"), this); 90 | helpMenu->addAction(aboutAct); 91 | helpMenu->addAction(aboutQtAct); 92 | 93 | 94 | menuBar()->addMenu(fileMenu); 95 | menuBar()->addMenu(streamMenu); 96 | menuBar()->addMenu(helpMenu); 97 | 98 | resize( 400, 300 ); 99 | setWindowTitle( tr("POLEView" ) ); 100 | statusBar()->showMessage( tr("Ready"), 5000 ); 101 | } 102 | 103 | void PoleView::newWindow() 104 | { 105 | PoleView* v = new PoleView(); 106 | v->show(); 107 | } 108 | 109 | void PoleView::choose() 110 | { 111 | QString form("%1 (%2)" ); 112 | QString allTypes( "*.doc *.xls *.xla *.ppt *.dot *.xlt *.pps" ); 113 | QString filter1 = QString(form).arg( tr("Microsoft Office Files") ).arg( allTypes ); 114 | QString filter2a = QString(form).arg( tr("Microsoft Word Document") ).arg( "*.doc" ); 115 | QString filter2b = QString(form).arg( tr("Microsoft Word Template") ).arg( "*.dot" ); 116 | QString filter3a = QString(form).arg( tr("Microsoft Excel Workbook") ).arg( "*.xls" ); 117 | QString filter3b = QString(form).arg( tr("Microsoft Excel Template") ).arg( "*.xlt" ); 118 | QString filter3c = QString(form).arg( tr("Microsoft Excel Add-In") ).arg( "*.xla" ); 119 | QString filter4a = QString(form).arg( tr("Microsoft PowerPoint Presentation") ).arg( "*.ppt" ); 120 | QString filter4b = QString(form).arg( tr("Microsoft PowerPoint Template") ).arg( "*.pps" ); 121 | QString filter5 = QString(form).arg( tr("All Files") ).arg( "*" ); 122 | 123 | QString filter = filter5.append( ";;" ).append( filter1 ); 124 | filter = filter.append( ";;" ).append( filter2a ); 125 | filter = filter.append( ";;" ).append( filter2b ); 126 | filter = filter.append( ";;" ).append( filter3a ); 127 | filter = filter.append( ";;" ).append( filter3b ); 128 | filter = filter.append( ";;" ).append( filter3c ); 129 | filter = filter.append( ";;" ).append( filter4a ); 130 | filter = filter.append( ";;" ).append( filter4b ); 131 | 132 | QString fn = QFileDialog::getOpenFileName( 0, "", "", filter ); 133 | 134 | if ( !fn.isEmpty() ) openFile( fn ); 135 | else 136 | statusBar()->showMessage( tr("Loading aborted"), 2000 ); 137 | } 138 | 139 | class StreamItem: public QTreeWidgetItem 140 | { 141 | public: 142 | StreamItem( QTreeWidgetItem* parent, const QString& name, POLE::Stream* stream = 0 ); 143 | StreamItem( QTreeWidget* parent, const QString& name ); 144 | QString name; 145 | POLE::Stream* stream; 146 | }; 147 | 148 | StreamItem::StreamItem( QTreeWidgetItem* parent, const QString& n, POLE::Stream* s ): 149 | QTreeWidgetItem( parent ) 150 | { 151 | name = n; 152 | setText(0, name); 153 | stream = s; 154 | if( stream ) 155 | setText( 1, QString::number( stream->size() ) ); 156 | //setOpen( true ); 157 | } 158 | 159 | StreamItem::StreamItem( QTreeWidget* parent, const QString& n ): 160 | QTreeWidgetItem( parent ) 161 | { 162 | name = n; 163 | setText(0, name); 164 | 165 | stream = 0; 166 | //setOpen( true ); 167 | } 168 | 169 | void visit( QTreeWidgetItem* parent, POLE::Storage* storage, const std::string path ) 170 | { 171 | std::list entries; 172 | entries = storage->entries( path ); 173 | 174 | std::list::iterator it; 175 | for( it = entries.begin(); it != entries.end(); ++it ) 176 | { 177 | std::string name = *it; 178 | std::string fullname = path + name; 179 | 180 | if( storage->isDirectory( fullname ) ) 181 | { 182 | StreamItem* item = new StreamItem( parent, name.c_str() ); 183 | visit( item, storage, fullname + "/" ); 184 | } 185 | else 186 | new StreamItem( parent, name.c_str(), new POLE::Stream( storage, fullname.c_str() ) ); 187 | } 188 | } 189 | 190 | void PoleView::openFile( const QString &fileName ) 191 | { 192 | if( storage ) closeFile(); 193 | 194 | QTime t; t.start(); 195 | storage = new POLE::Storage( fileName.toLatin1().data() ); 196 | storage->open(); 197 | 198 | if( storage->result() != POLE::Storage::Ok ) 199 | { 200 | QString msg = QString( tr("Unable to open file %1\n") ).arg(fileName); 201 | QMessageBox::critical( 0, tr("Error"), msg ); 202 | closeFile(); 203 | return; 204 | } 205 | 206 | QString msg = QString( tr("Loading %1 (%2 ms)") ).arg( fileName ).arg( t.elapsed() ); 207 | statusBar()->showMessage( msg, 2000 ); 208 | 209 | view->clear(); 210 | StreamItem* root = new StreamItem( view, tr("Root") ); 211 | visit( root, storage, "/" ); 212 | 213 | setWindowTitle( QString( tr("%1 - POLEView" ).arg( fileName ) ) ); 214 | } 215 | 216 | void PoleView::closeFile() 217 | { 218 | if( storage ) 219 | { 220 | storage->close(); 221 | delete storage; 222 | } 223 | 224 | storage = 0; 225 | view->clear(); 226 | setWindowTitle( tr("POLEView" ) ); 227 | } 228 | 229 | void PoleView::viewStream() { 230 | /* 231 | QModelIndex idx = view->currentIndex(); 232 | for (QModelIndexList::iterator it=idxList.begin(); itcolumn() == 0 ) 234 | files << model->filePath( *it ); 235 | */ 236 | 237 | 238 | 239 | StreamItem* item = (StreamItem*) view->currentItem (); 240 | if( !item ) 241 | { 242 | QMessageBox::warning( 0, tr("View Stream"), 243 | tr("Nothing is selected"), 244 | QMessageBox::Ok, QMessageBox::NoButton ); 245 | return; 246 | } 247 | 248 | QString name = item->text( 0 ); 249 | if( !item->stream ) 250 | { 251 | QMessageBox::warning( 0, tr("View Stream"), 252 | tr("'%1' is not a stream").arg( name ), 253 | QMessageBox::Ok, QMessageBox::NoButton ); 254 | return; 255 | } 256 | 257 | StreamView* sv = new StreamView( item->stream ); 258 | 259 | sv->setWindowTitle( name ); 260 | QTimer::singleShot( 200, sv, SLOT( show() ) ); 261 | } 262 | 263 | void PoleView::exportStream() 264 | { 265 | StreamItem* item = (StreamItem*) view->currentItem(); 266 | if( !item ) 267 | { 268 | QMessageBox::warning( 0, tr("View Stream"), 269 | tr("Nothing is selected"), 270 | QMessageBox::Ok, QMessageBox::NoButton ); 271 | return; 272 | } 273 | 274 | QString name = item->text( 0 ); 275 | if( !item->stream ) 276 | { 277 | QMessageBox::warning( 0, tr("View Stream"), 278 | tr("'%1' is not a stream").arg( name ), 279 | QMessageBox::Ok, QMessageBox::NoButton ); 280 | return; 281 | } 282 | 283 | QString fn = QFileDialog::getSaveFileName( 0, tr("Export Stream As"), name ); 284 | if ( fn.isEmpty() ) 285 | { 286 | statusBar()->showMessage( tr("Export aborted"), 2000 ); 287 | return; 288 | } 289 | 290 | unsigned char buffer[16]; 291 | QFile outf( fn ); 292 | if( !outf.open( QIODevice::WriteOnly ) ) 293 | { 294 | QString msg = QString( tr("Unable to write to file %1\n") ).arg(fn); 295 | QMessageBox::critical( 0, tr("Error"), msg ); 296 | return; 297 | } 298 | 299 | statusBar()->showMessage( tr("Exporting... Please wait") ); 300 | for( ;; ) 301 | { 302 | unsigned read = item->stream->read( buffer, sizeof( buffer ) ); 303 | outf.write( (const char*)buffer, read ); 304 | if( read < sizeof( buffer ) ) break; 305 | } 306 | outf.close(); 307 | statusBar()->showMessage( tr("Stream is exported."), 2000 ); 308 | } 309 | 310 | void PoleView::about() 311 | { 312 | QMessageBox::about( this, tr("About POLEView"), 313 | tr("Simple structured storage viewer\n" 314 | "Copyright (C) 2004 Ariya Hidayat (ariya@kde.org)")); 315 | } 316 | 317 | void PoleView::aboutQt() 318 | { 319 | QMessageBox::aboutQt( this, tr("POLEView") ); 320 | } 321 | 322 | #define STREAM_MAX_SIZE 32 // in KB 323 | 324 | StreamView::StreamView( POLE::Stream* s ): QDialog( 0 ) 325 | { 326 | stream = s; 327 | setModal( false ); 328 | 329 | QVBoxLayout* layout = new QVBoxLayout( this ); 330 | //layout->setAutoAdd( true ); 331 | layout->setMargin( 10 ); 332 | layout->setSpacing( 5 ); 333 | 334 | infoLabel = new QLabel( this ); 335 | 336 | log = new QTextEdit( this ); 337 | //log->setTextFormat( Qt::LogText ); 338 | //log->setFont( QFont("Courier") ); 339 | //log->setMinimumSize( 500, 300 ); 340 | 341 | layout->addWidget( infoLabel ); 342 | layout->addWidget( log ); 343 | QTimer::singleShot( 0, this, SLOT( loadStream() ) ); 344 | } 345 | 346 | void StreamView::loadStream() 347 | { 348 | unsigned size = stream->size(); 349 | 350 | if( size > STREAM_MAX_SIZE*1024 ) 351 | { 352 | infoLabel->setText( tr("This stream is too large. " 353 | "Only the first %1 KB is shown.").arg( STREAM_MAX_SIZE ) ); 354 | size = STREAM_MAX_SIZE*1024; 355 | } 356 | 357 | unsigned char buffer[16]; 358 | stream->seek( 0 ); 359 | for( unsigned j = 0; j < size; j+= 16 ) 360 | { 361 | unsigned read = stream->read( buffer, 16 ); 362 | appendData( buffer, read ); 363 | if( read < sizeof( buffer ) ) break; 364 | } 365 | 366 | QTimer::singleShot( 100, this, SLOT( goTop() ) ); 367 | } 368 | 369 | void StreamView::goTop() 370 | { 371 | //log->ensureVisible( 0, 0 ); 372 | } 373 | 374 | void StreamView::appendData( unsigned char* data, unsigned length ) 375 | { 376 | QString msg; 377 | for( unsigned i = 0; i < length; i++ ) 378 | { 379 | QString s = QString::number( data[i], 16 ); 380 | while( s.length() < 2 ) s.prepend( '0' ); 381 | msg.append( s ); 382 | msg.append( ' ' ); 383 | } 384 | msg.append( " " ); 385 | for( unsigned i = 0; i < length; i++ ) 386 | { 387 | if( (data[i]>31) && (data[i]<128) ) 388 | { 389 | if( data[i] == '<' ) msg.append( "<" ); 390 | else if( data[i] == '>' ) msg.append( ">" ); 391 | else if( data[i] == '&' ) msg.append( "&" ); 392 | else msg.append( data[i] ); 393 | } 394 | else msg.append( '.' ); 395 | } 396 | 397 | log->append( msg ); 398 | } 399 | 400 | // --- main program 401 | 402 | #include 403 | 404 | int main( int argc, char ** argv ) 405 | { 406 | QApplication a( argc, argv ); 407 | 408 | PoleView* v = new PoleView(); 409 | v->show(); 410 | 411 | a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) ); 412 | return a.exec(); 413 | } 414 | -------------------------------------------------------------------------------- /poleview.h: -------------------------------------------------------------------------------- 1 | /* POLEView - Graphical utility to view structure storage 2 | Copyright (C) 2004 Ariya Hidayat 3 | 4 | This program 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., 59 Temple Place - Suite 330, 17 | Boston, MA 02111-1307, US 18 | */ 19 | 20 | #ifndef POLEVIEW 21 | #define POLEVIEW 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "pole.h" 41 | 42 | class PoleView : public QMainWindow 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | PoleView(); 48 | 49 | private slots: 50 | 51 | void newWindow(); 52 | void choose(); 53 | void openFile( const QString &fileName ); 54 | void closeFile(); 55 | void viewStream(); 56 | void exportStream(); 57 | void about(); 58 | void aboutQt(); 59 | 60 | private: 61 | PoleView( const PoleView& ); 62 | PoleView& operator=( const PoleView& ); 63 | POLE::Storage* storage; 64 | QTreeWidget* view; 65 | }; 66 | 67 | 68 | class StreamView: public QDialog 69 | { 70 | Q_OBJECT 71 | 72 | public: 73 | StreamView( POLE::Stream* stream ); 74 | 75 | private slots: 76 | void loadStream(); 77 | void goTop(); 78 | 79 | private: 80 | StreamView( const StreamView& ); 81 | StreamView& operator=( const StreamView& ); 82 | void appendData( unsigned char* data, unsigned length ); 83 | POLE::Stream* stream; 84 | QLabel* infoLabel; 85 | QTextEdit* log; 86 | }; 87 | 88 | #endif // POLEVIEW 89 | -------------------------------------------------------------------------------- /poleview.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | TEMPLATE = app 6 | TARGET = poleview 7 | 8 | HEADERS += pole.h 9 | HEADERS += poleview.h 10 | 11 | SOURCES += pole.cpp 12 | SOURCES += poleview.cpp 13 | 14 | CONFIG += qt 15 | CONFIG += thread 16 | CONFIG += warn_on 17 | CONFIG += exceptions 18 | CONFIG += release 19 | -------------------------------------------------------------------------------- /testbed.cpp: -------------------------------------------------------------------------------- 1 | // testbed.cpp: Take a directory and its contents, and create a Structured Storage File from it. 2 | // Written using Windows - it should be portable to other environments with relatively modest changes. 3 | // This was done mainly as a way to test POLE at various stages, but it has some utility on its own. 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "pole.h" 19 | 20 | POLE::Storage* storage = 0; 21 | 22 | bool DirectoryExists( const char* dirname ){ 23 | 24 | if( _access( dirname, 0 ) == 0 ){ 25 | 26 | struct stat status; 27 | stat( dirname, &status ); 28 | 29 | return (status.st_mode & S_IFDIR) != 0; 30 | } 31 | return false; 32 | } 33 | 34 | void visit( int indent, POLE::Storage* storage, std::string path ) 35 | { 36 | std::list entries; 37 | entries = storage->entries( path ); 38 | 39 | std::list::iterator it; 40 | for( it = entries.begin(); it != entries.end(); ++it ) 41 | { 42 | std::string name = *it; 43 | std::string fullname = path + name; 44 | for( int j = 0; j < indent; j++ ) 45 | std::cout << " "; 46 | POLE::Stream* ss = new POLE::Stream( storage, fullname ); 47 | std::cout << name; 48 | if( ss ) 49 | if( !ss->fail() )std::cout << " (" << ss->size() << ")"; 50 | std::cout << std::endl; 51 | delete ss; 52 | 53 | if( storage->isDirectory( fullname ) ) 54 | visit( indent+1, storage, fullname + "/" ); 55 | } 56 | 57 | } 58 | 59 | bool extract( POLE::Storage* storage, const char* stream_name, const char* outfile ) 60 | { 61 | POLE::Stream* stream = new POLE::Stream( storage, stream_name ); 62 | if( !stream ) 63 | { 64 | printf("A stream could not be opened for %s.\n", stream_name); 65 | return false; 66 | } 67 | if( stream->fail() ) 68 | { 69 | printf("A stream failed for %s.\n", stream_name); 70 | return false; 71 | } 72 | 73 | std::ofstream file; 74 | file.open( outfile, std::ios::binary|std::ios::out ); 75 | 76 | unsigned char buffer[16]; 77 | for( ;; ) 78 | { 79 | unsigned read = stream->read( buffer, sizeof( buffer ) ); 80 | file.write( (const char*)buffer, read ); 81 | if( read < sizeof( buffer ) ) break; 82 | } 83 | file.close(); 84 | 85 | delete stream; 86 | return true; 87 | } 88 | 89 | void extractDir(POLE::Storage* storage, std::string outDir, std::string path, int &nFolders, int &nFiles) 90 | { 91 | const char *cOutDir = outDir.c_str(); 92 | if (DirectoryExists(cOutDir)) 93 | { 94 | std::cout << "You should not specify a directory for output that already exists." << std::endl; 95 | return; 96 | } 97 | 98 | _mkdir(cOutDir); 99 | nFolders++; 100 | std::list entries; 101 | entries = storage->entries( path ); 102 | 103 | std::list::iterator it; 104 | for( it = entries.begin(); it != entries.end(); ++it ) 105 | { 106 | std::string name = *it; 107 | std::string fullname = path + name; 108 | if (storage->isDirectory(fullname)) 109 | { 110 | std::string outdirchild = outDir + name; 111 | extractDir(storage, outdirchild + "/", fullname + "/", nFolders, nFiles); 112 | } 113 | else 114 | { 115 | POLE::Stream* stream = new POLE::Stream(storage, fullname); 116 | if( !stream ) 117 | { 118 | printf("A stream could not be opened for %s.\n", fullname); 119 | return; 120 | } 121 | if( stream->fail() ) 122 | { 123 | printf("A stream failed for %s.\n", fullname); 124 | return; 125 | } 126 | std::ofstream file; 127 | std::string outfname = outDir + name; 128 | const char *coutfname = outfname.c_str(); 129 | file.open(coutfname, std::ios::binary|std::ios::out); 130 | unsigned long bytesLeft = stream->size(); 131 | unsigned char buffer[4096]; 132 | while(bytesLeft > 0) 133 | { 134 | unsigned long bytesToRead = sizeof( buffer ); 135 | if (bytesLeft < bytesToRead) 136 | bytesToRead = bytesLeft; 137 | unsigned read = stream->read( buffer, bytesToRead ); 138 | file.write( (const char*)buffer, read ); 139 | bytesLeft -= read; 140 | if( read < bytesToRead ) 141 | break; //shouldn't happen, probably 142 | } 143 | file.close(); 144 | delete stream; 145 | nFiles++; 146 | } 147 | } 148 | } 149 | 150 | bool AddFile2Storage(POLE::Storage* storage, std::string inFile, std::string name) 151 | { 152 | std::ifstream file; 153 | const char *cinfname = inFile.c_str(); 154 | file.open(cinfname, std::ios::binary|std::ios::in); 155 | // find size of input file 156 | file.seekg( 0, std::ios::end ); 157 | long filesize = file.tellg(); 158 | file.seekg( 0 ); 159 | POLE::Stream* ss = new POLE::Stream( storage, name, true, filesize ); 160 | if( !ss ) 161 | { 162 | printf("A stream could not be opened for %s.\n", name); 163 | file.close(); 164 | return false; 165 | } 166 | if( ss->fail() ) 167 | { 168 | printf("A stream failed for %s.\n", name); 169 | file.close(); 170 | return false; 171 | } 172 | unsigned char buffer[4096]; 173 | int bytesRead = 0; 174 | int bytesToRead; 175 | for (;;) 176 | { 177 | bytesToRead = filesize - bytesRead; 178 | if (bytesToRead <= 0) 179 | break; 180 | if (bytesToRead > 4096) 181 | bytesToRead = 4096; 182 | file.read((char *)buffer, bytesToRead); 183 | ss->write(buffer, bytesToRead); 184 | bytesRead += bytesToRead; 185 | } 186 | ss->flush(); 187 | delete ss; 188 | file.close(); 189 | return true; 190 | } 191 | 192 | void AddDir2Storage(POLE::Storage* storage, std::string inDir, std::string name, int &nFolders, int &nFiles) 193 | { 194 | WIN32_FIND_DATA ffd; 195 | HANDLE h; 196 | 197 | 198 | std::string inDirLcl = inDir + "*"; 199 | std::string outNameLcl = name; 200 | 201 | const char *cInDir = inDirLcl.c_str(); 202 | h = FindFirstFile(cInDir, &ffd); 203 | if (h == INVALID_HANDLE_VALUE) 204 | return; 205 | nFolders++; 206 | do 207 | { 208 | if (ffd.cFileName[0] == '.') 209 | { 210 | if (ffd.cFileName[1] == 0 || (ffd.cFileName[1] == '.' && ffd.cFileName[2] == 0)) 211 | continue; 212 | } 213 | outNameLcl = name + ffd.cFileName; 214 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 215 | { 216 | outNameLcl += "/"; 217 | inDirLcl = inDir + ffd.cFileName; 218 | inDirLcl += "\\"; 219 | AddDir2Storage(storage, inDirLcl, outNameLcl, nFolders, nFiles); 220 | } 221 | else 222 | { 223 | std::ifstream file; 224 | std::string infname = inDir + ffd.cFileName; 225 | const char *cinfname = infname.c_str(); 226 | file.open(cinfname, std::ios::binary|std::ios::in); 227 | // find size of input file 228 | file.seekg( 0, std::ios::end ); 229 | long filesize = file.tellg(); 230 | file.seekg( 0 ); 231 | POLE::Stream* ss = new POLE::Stream( storage, outNameLcl, true, filesize ); 232 | unsigned char buffer[4096]; 233 | int bytesRead = 0; 234 | int bytesToRead; 235 | for (;;) 236 | { 237 | bytesToRead = filesize - bytesRead; 238 | if (bytesToRead <= 0) 239 | break; 240 | if (bytesToRead > 4096) 241 | bytesToRead = 4096; 242 | file.read((char *)buffer, bytesToRead); 243 | ss->write(buffer, bytesToRead); 244 | bytesRead += bytesToRead; 245 | } 246 | ss->flush(); 247 | delete ss; 248 | file.close(); 249 | nFiles++; 250 | } 251 | } 252 | while (FindNextFile(h, &ffd) != 0); 253 | } 254 | 255 | 256 | 257 | void cmdOpen(std::list &entries) 258 | { 259 | if (entries.size() < 1) 260 | std::cout << "You must enter the name of an existing file formatted as structured storage." << std::endl; 261 | else 262 | { 263 | if (storage != 0) 264 | storage->close(); 265 | std::string ssName = entries.front(); 266 | entries.pop_front(); 267 | storage = new POLE::Storage(ssName.c_str()); 268 | bool bWrite = false; 269 | bool bCreate = false; 270 | if (entries.size() > 0) 271 | { 272 | std::string modifiers = entries.front(); 273 | for (unsigned idx = 0; idx < modifiers.size(); idx++) 274 | { 275 | char c = modifiers[idx]; 276 | if (c == 'c') 277 | bCreate = true; 278 | else if (c == 'w') 279 | bWrite = true; 280 | } 281 | } 282 | storage->open(bWrite, bCreate); 283 | int result = storage->result(); 284 | if (result == POLE::Storage::Ok) 285 | std::cout << "Ok" << std::endl; 286 | else 287 | { 288 | delete storage; 289 | storage = 0; 290 | if (result == POLE::Storage::BadOLE) 291 | std::cout << "BadOLE" << std::endl; 292 | else if (result == POLE::Storage::NotOLE) 293 | std::cout << "NotOLE" << std::endl; 294 | else if (result == POLE::Storage::OpenFailed) 295 | std::cout << "OpenFailed" << std::endl; 296 | else 297 | printf("Unrecognizable result - %d\n", result); 298 | } 299 | } 300 | } 301 | 302 | void cmdClose(std::list &entries) 303 | { 304 | if (storage) 305 | { 306 | storage->close(); 307 | delete storage; 308 | storage = 0; 309 | } 310 | } 311 | 312 | void cmdStats(std::list &entries) 313 | { 314 | if (storage) 315 | { 316 | unsigned int nDirs, nUUDirs, nBBs, nUUBBs, nSBs, nUUSBs; 317 | storage->GetStats(&nDirs, &nUUDirs, &nBBs, &nUUBBs, &nSBs, &nUUSBs); 318 | printf("%d Directory Entries, %d unused.\n", nDirs, nUUDirs); 319 | printf("%d Big Blocks, %d unused.\n", nBBs, nUUBBs); 320 | printf("%d Small Blocks, %d unused.\n", nSBs, nUUSBs); 321 | } 322 | } 323 | 324 | void cmdVisit(std::list &entries) 325 | { 326 | if (!storage) 327 | std::cout << "No storage is opened." << std::endl; 328 | else 329 | { 330 | if (entries.size() > 0) 331 | visit(0, storage, entries.front()); 332 | else 333 | visit(0, storage, "/"); 334 | } 335 | } 336 | 337 | void cmdExtract(std::list &entries) 338 | { 339 | if (!storage) 340 | std::cout << "No storage is opened." << std::endl; 341 | else if (entries.size() < 2) 342 | std::cout << "You must specify a path in the open storage to be extracted, and a destination file or folder." << std::endl; 343 | else 344 | { 345 | std::string ssPath = entries.front(); 346 | entries.pop_front(); 347 | std::string filePath = entries.front(); 348 | if (storage->isDirectory(ssPath)) 349 | { 350 | if (filePath[filePath.size()-1] != '/') 351 | filePath += '/'; 352 | if (ssPath[0] != '/') 353 | ssPath = '/' + ssPath; 354 | if (ssPath[ssPath.size()-1] != '/') 355 | ssPath += '/'; 356 | int nFiles = 0; 357 | int nFolders = 0; 358 | extractDir(storage, filePath, ssPath, nFolders, nFiles); 359 | printf("%d folders and %d files were extracted.\n", nFolders, nFiles); 360 | } 361 | else 362 | { 363 | if (extract(storage, ssPath.c_str(), filePath.c_str())) 364 | std::cout << "Ok" << std::endl; 365 | } 366 | } 367 | } 368 | 369 | void cmdAdd(std::list &entries) 370 | { 371 | if (!storage) 372 | std::cout << "No storage is opened." << std::endl; 373 | else if (entries.size() < 2) 374 | std::cout << "You must specify a path in the open storage to be created or modified, and a source file or folder." << std::endl; 375 | else if (!storage->isWriteable()) 376 | std::cout << "The open storage cannot be modified." << std::endl; 377 | else if (entries.front() != "/" && storage->exists(entries.front())) 378 | std::cout << "The specified storage node already exists - to save it again, first delete it." << std::endl; 379 | else 380 | { 381 | std::string ssPath = entries.front(); 382 | entries.pop_front(); 383 | std::string filePath = entries.front(); 384 | if (DirectoryExists(filePath.c_str())) 385 | { 386 | if (filePath[filePath.size()-1] != '/') 387 | filePath += '/'; 388 | if (ssPath[0] != '/') 389 | ssPath = '/' + ssPath; 390 | if (ssPath[ssPath.size()-1] != '/') 391 | ssPath += '/'; 392 | int nFiles = 0; 393 | int nFolders = 0; 394 | AddDir2Storage(storage, filePath, ssPath, nFolders, nFiles); 395 | printf("%d folders and %d files were added.\n", nFolders, nFiles); 396 | } 397 | else 398 | { 399 | if (AddFile2Storage(storage, filePath, ssPath)) 400 | std::cout << "Ok" << std::endl; 401 | } 402 | } 403 | } 404 | 405 | void cmdDelete(std::list &entries) 406 | { 407 | if (!storage) 408 | std::cout << "No storage is opened." << std::endl; 409 | else if (entries.size() < 1) 410 | std::cout << "You must specify a path in the open storage to be deleted." << std::endl; 411 | else if (!storage->isWriteable()) 412 | std::cout << "The open storage cannot be modified." << std::endl; 413 | else 414 | { 415 | if (!storage->deleteByName(entries.front())) 416 | std::cout << "The deletion failed." << std::endl; 417 | else 418 | std::cout << "Ok" << std::endl; 419 | } 420 | } 421 | 422 | bool cmdQuit(std::list &entries) 423 | { 424 | cmdClose(entries); 425 | return true; 426 | } 427 | 428 | void cmdHelp(std::list &entries) 429 | { 430 | std::cout << "You can enter any of the following commands. Note that they are case sensitive." << std::endl; 431 | std::cout << "open filePath [modifier] - modifiers can be any combination of r, w, or c (create)" << std::endl; 432 | std::cout << "visit [ssPath] - list contents of the structured storage, or of one leaf or node" << std::endl; 433 | std::cout << "extract ssPath filePath - Extract the contents of one leaf" << std::endl; 434 | std::cout << "add ssPath filePath - Add the contents of one leaf to the structured storage" << std::endl; 435 | std::cout << "delete ssPath - Delete the leaf or node, and whatever it contains" << std::endl; 436 | std::cout << "stats - show statistics for the open storage" << std::endl; 437 | std::cout << "close" << std::endl; 438 | std::cout << "quit" << std::endl; 439 | } 440 | 441 | std::list clineParse(std::string inCmd) 442 | { 443 | std::list outWords; 444 | std::string oneWord; 445 | char c; 446 | int idx; 447 | int maxIdx = inCmd.size(); 448 | bool bInQuote = false; 449 | for (idx = 0; idx < maxIdx; idx++) 450 | { 451 | c = inCmd[idx]; 452 | if (bInQuote) 453 | { 454 | if (c == '\"') 455 | { 456 | outWords.push_back(oneWord); 457 | oneWord.clear(); 458 | bInQuote = false; 459 | } 460 | else 461 | oneWord+= c; 462 | } 463 | else 464 | { 465 | if (c == '\"' && oneWord.size() == 0) 466 | bInQuote = true; 467 | else if (c == ' ') 468 | { 469 | if (oneWord.size() != 0) 470 | { 471 | outWords.push_back(oneWord); 472 | oneWord.clear(); 473 | } 474 | } 475 | else 476 | oneWord+= c; 477 | } 478 | } 479 | if (oneWord.size() != 0) 480 | outWords.push_back(oneWord); 481 | return outWords; 482 | } 483 | 484 | int main(int argc, char* argv[]) 485 | { 486 | if( argc > 1 ) 487 | { 488 | std::cout << "Usage:" << std::endl; 489 | std::cout << argv[0] << " This takes no arguments - type help in the console window for information. " << std::endl; 490 | return 0; 491 | } 492 | 493 | std::string command; 494 | while (true) 495 | { 496 | std::getline(std::cin, command); 497 | std::list entries = clineParse(command); 498 | if (entries.size() == 0) 499 | continue; 500 | std::string cmdWord = entries.front(); 501 | entries.pop_front(); 502 | if (cmdWord.compare("open") == 0) 503 | cmdOpen(entries); 504 | else if (cmdWord.compare("visit") == 0) 505 | cmdVisit(entries); 506 | else if (cmdWord.compare("extract") == 0) 507 | cmdExtract(entries); 508 | else if (cmdWord.compare("add") == 0) 509 | cmdAdd(entries); 510 | else if (cmdWord.compare("delete") == 0) 511 | cmdDelete(entries); 512 | else if (cmdWord.compare("stats") == 0) 513 | cmdStats(entries); 514 | else if (cmdWord.compare("close") == 0) 515 | cmdClose(entries); 516 | else if (cmdWord.compare("quit") == 0) 517 | { 518 | if (cmdQuit(entries)) 519 | break; 520 | } 521 | else 522 | cmdHelp(entries); 523 | } 524 | #if 0 525 | std::list::iterator it; 526 | for( it = entries.begin(); it != entries.end(); ++it ) 527 | std::cout << *it << std::endl; 528 | open filename [modifier] (modifier can be r, w, c) 529 | visit [sspath] 530 | extract sspath fpath 531 | add sspath fpath 532 | delete sspath 533 | close 534 | 535 | std::string indir = argv[1]; 536 | char* outfile = argv[2]; 537 | if (indir[indir.length()-1] != '\\') 538 | indir += "\\"; 539 | POLE::Storage* storage = new POLE::Storage(outfile); 540 | storage->open(true, true); 541 | AddDir2Storage(storage, indir, "/"); 542 | storage->close(); 543 | storage->open(); 544 | visit(0, storage, "/"); 545 | storage->close(); 546 | #endif 547 | } --------------------------------------------------------------------------------