NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
BasicExcel.cpp
Go to the documentation of this file.
1#include "BasicExcel.hpp"
2
3using namespace std;
4
6{
7/********************************** Start of Class Block *************************************/
8// PURPOSE: Manage a file by treating it as blocks of data of a certain size.
9Block::Block() : filename_(0), blockSize_(512), indexEnd_(0), fileSize_(0)
10{}
11
12bool Block::Create(const wchar_t* filename)
13// PURPOSE: Create a new block file and open it.
14// PURPOSE: If file is present, truncate it and then open it.
15// PROMISE: Return true if file is successfully created and opened, false if otherwise.
16{
17 // Create new file
18 size_t filenameLength = wcslen(filename);
19 char* name = new char[filenameLength+1];
20 wcstombs(name, filename, filenameLength);
21 name[filenameLength] = 0;
22
23 file_.open(name, ios_base::out | ios_base::trunc);
24 file_.close();
25 file_.clear();
26
27 // Open the file
28 bool ret = this->Open(filename);
29 delete[] name;
30 return ret;
31}
32
33bool Block::Open(const wchar_t* filename, ios_base::openmode mode)
34// PURPOSE: Open an existing block file.
35// PROMISE: Return true if file is successfully opened, false if otherwise.
36{
37 // Open existing file for reading or writing or both
38 size_t filenameLength = wcslen(filename);
39 filename_.resize(filenameLength+1, 0);
40 wcstombs(&*(filename_.begin()), filename, filenameLength);
41
42 file_.open(&*(filename_.begin()), mode | ios_base::binary);
43 if (!file_.is_open()) return false;
44
45 mode_ = mode;
46
47 // Calculate filesize
48 if (mode & ios_base::in)
49 {
50 file_.seekg(0, ios_base::end);
51 fileSize_ = file_.tellg();
52 }
53 else if (mode & ios_base::out)
54 {
55 file_.seekp(0, ios_base::end);
56 fileSize_ = file_.tellp();
57 }
58 else
59 {
60 this->Close();
61 return false;
62 }
63
64 // Calculate last index + 1
66 return true;
67}
68
70// PURPOSE: Close the opened block file.
71// PROMISE: Return true if file is successfully closed, false if otherwise.
72{
73 file_.close();
74 file_.clear();
75 filename_.clear();
76 fileSize_ = 0;
77 indexEnd_ = 0;
78 blockSize_ = 512;
79 return !file_.is_open();
80}
81
83// PURPOSE: Check if the block file is still opened.
84// PROMISE: Return true if file is still opened, false if otherwise.
85{
86 return file_.is_open();
87}
88
89bool Block::Read(size_t index, char* block)
90// PURPOSE: Read a block of data from the opened file at the index position.
91// EXPLAIN: index is from [0..].
92// PROMISE: Return true if data are successfully read, false if otherwise.
93{
94 if (!(mode_ & ios_base::in)) return false;
95 if (index < indexEnd_)
96 {
97 file_.seekg(index * blockSize_);
98 file_.read(block, blockSize_);
99 return !file_.fail();
100 }
101 else return false;
102}
103
104bool Block::Write(size_t index, const char* block)
105// PURPOSE: Write a block of data to the opened file at the index position.
106// EXPLAIN: index is from [0..].
107// PROMISE: Return true if data are successfully written, false if otherwise.
108{
109 if (!(mode_ & ios_base::out)) return false;
110 file_.seekp(index * blockSize_);
111 file_.write(block, blockSize_);
112 if (indexEnd_ <= index)
113 {
114 indexEnd_ = index + 1;
116 }
117 file_.close();
118 file_.clear();
119 file_.open(&*(filename_.begin()), mode_ | ios_base::binary);
120 return file_.is_open();
121}
122
123bool Block::Swap(size_t index1, size_t index2)
124// PURPOSE: Swap two blocks of data in the opened file at the index positions.
125// EXPLAIN: index1 and index2 are from [0..].
126// PROMISE: Return true if data are successfully swapped, false if otherwise.
127{
128 if (!(mode_ & ios_base::out)) return false;
129 if (index1 < indexEnd_ && index2 < indexEnd_)
130 {
131 if (index1 == index2) return true;
132
133 char* block1 = new char[blockSize_];
134 if (!this->Read(index1, block1)) return false;
135
136 char* block2 = new char[blockSize_];
137 if (!this->Read(index2, block2)) return false;
138
139 if (!this->Write(index1, block2)) return false;
140 if (!this->Write(index2, block1)) return false;
141
142 delete[] block1;
143 delete[] block2;
144 return true;
145 }
146 else return false;
147}
148
149bool Block::Move(size_t from, size_t to)
150// PURPOSE: Move a block of data in the opened file from an index position to another index position.
151// EXPLAIN: from and to are from [0..].
152// PROMISE: Return true if data are successfully moved, false if otherwise.
153{
154 if (!(mode_ & ios_base::out)) return false;
155 if (from < indexEnd_ && to < indexEnd_)
156 {
157 if (to > from)
158 {
159 for (size_t i=from; i!=to; ++i)
160 {
161 if (!this->Swap(i, i+1)) return false;
162 }
163 }
164 else
165 {
166 for (size_t i=from; i!=to; --i)
167 {
168 if (!this->Swap(i, i-1)) return false;
169 }
170 }
171 return true;
172 }
173 else return false;
174}
175
176bool Block::Insert(size_t index, const char* block)
177// PURPOSE: Insert a new block of data in the opened file at the index position.
178// EXPLAIN: index is from [0..].
179// PROMISE: Return true if data are successfully inserted, false if otherwise.
180{
181 if (!(mode_ & ios_base::out)) return false;
182 if (index <= indexEnd_)
183 {
184 // Write block to end of file
185 if (!this->Write(indexEnd_, block)) return false;
186
187 // Move block to index if necessary
188 if (index < indexEnd_-1) return this->Move(indexEnd_-1, index);
189 else return true;
190 }
191 else
192 {
193 // Write block to index after end of file
194 return this->Write(index, block);
195 }
196}
197
198bool Block::Erase(size_t index)
199// PURPOSE: Erase a block of data in the opened file at the index position.
200// EXPLAIN: index is from [0..].
201// PROMISE: Return true if data are successfully erased, false if otherwise.
202{
203 if (!(mode_ & ios_base::out)) return false;
204 if (index < indexEnd_)
205 {
207 indexEnd_ -= 1;
208
209 // Read entire file except the block to be deleted into memory.
210 char* buffer = new char[fileSize_];
211 for (size_t i=0, j=0; i!=indexEnd_+1; ++i)
212 {
213 file_.seekg(i*blockSize_);
214 if (i != index)
215 {
216 file_.read(buffer+j*blockSize_, blockSize_);
217 ++j;
218 }
219 }
220 file_.close();
221 file_.open(&*(filename_.begin()), ios_base::out | ios_base::trunc | ios_base::binary);
222 file_.write(buffer, fileSize_); // Write the new file.
223 file_.close();
224 file_.open(&*(filename_.begin()), mode_ | ios_base::binary);
225 delete[] buffer;
226 return true;
227 }
228 else return false;
229}
230
231bool Block::Erase(vector<size_t>& indices)
232// PURPOSE: Erase blocks of data in the opened file at the index positions.
233// EXPLAIN: Each index in indices is from [0..].
234// PROMISE: Return true if data are successfully erased, false if otherwise.
235{
236 if (!(mode_ & ios_base::out)) return false;
237
238 // Read entire file except the blocks to be deleted into memory.
239 size_t maxIndices = indices.size();
240 fileSize_ -= maxIndices*blockSize_;
241 char* buffer = new char[fileSize_];
242 for (size_t i=0, k=0; i!=indexEnd_; ++i)
243 {
244 file_.seekg(i*blockSize_);
245 bool toDelete = false;
246 for (size_t j=0; j<maxIndices; ++j)
247 {
248 if (i == indices[j])
249 {
250 toDelete = true;
251 break;
252 }
253 }
254 if (!toDelete)
255 {
256 file_.read(buffer+k*blockSize_, blockSize_);
257 ++k;
258 }
259 }
260 indexEnd_ -= maxIndices;
261
262 file_.close();
263 file_.open(&*(filename_.begin()), ios_base::out | ios_base::trunc | ios_base::binary);
264 file_.write(buffer, fileSize_); // Write the new file.
265 file_.close();
266 file_.open(&*(filename_.begin()), mode_ | ios_base::binary);
267 delete[] buffer;
268 return true;
269}
270/********************************** End of Class Block ***************************************/
271
272/********************************** Start of Class Header ************************************/
273// PURPOSE: Read and write data to a compound file header.
274CompoundFile::Header::Header() : fileType_(0xE11AB1A1E011CFD0LL),
275 uk1_(0), uk2_(0), uk3_(0), uk4_(0), uk5_(0x003B), uk6_(0x0003), uk7_(-2),
276 log2BigBlockSize_(9), log2SmallBlockSize_(6),
277 uk8_(0), uk9_(0),
278 BATCount_(1), propertiesStart_(1),
279 uk10_(0), uk11_(0x00001000),
280 SBATStart_(-2), SBATCount_(0),
281 XBATStart_(-2), XBATCount_(0)
282{
283 BATArray_[0] = 0; // Initial BAT indices at block 0 (=block 1 in Block)
284 fill (BATArray_+1, BATArray_+109, -1); // Rest of the BATArray is empty
285 Initialize();
286}
287
289// PURPOSE: Write header information into a block of data.
290// REQUIRE: Block of data must be at least 512 bytes in size.
291{
292 LittleEndian::Write(block, fileType_, 0x0000, 8);
293 LittleEndian::Write(block, uk1_, 0x0008, 4);
294 LittleEndian::Write(block, uk2_, 0x000C, 4);
295 LittleEndian::Write(block, uk3_, 0x0010, 4);
296 LittleEndian::Write(block, uk4_, 0x0014, 4);
297 LittleEndian::Write(block, uk5_, 0x0018, 2);
298 LittleEndian::Write(block, uk6_, 0x001A, 2);
299 LittleEndian::Write(block, uk7_, 0x001C, 2);
300 LittleEndian::Write(block, log2BigBlockSize_, 0x001E, 2);
301 LittleEndian::Write(block, log2SmallBlockSize_, 0x0020, 4);
302 LittleEndian::Write(block, uk8_, 0x0024, 4);
303 LittleEndian::Write(block, uk9_, 0x0028, 4);
304 LittleEndian::Write(block, BATCount_, 0x002C, 4);
305 LittleEndian::Write(block, propertiesStart_, 0x0030, 4);
306 LittleEndian::Write(block, uk10_, 0x0034, 4);
307 LittleEndian::Write(block, uk11_, 0x0038, 4);
308 LittleEndian::Write(block, SBATStart_, 0x003C, 4);
309 LittleEndian::Write(block, SBATCount_, 0x0040, 4);
310 LittleEndian::Write(block, XBATStart_, 0x0044, 4);
311 LittleEndian::Write(block, XBATCount_, 0x0048, 4);
312 for (size_t i=0; i<109; ++i) LittleEndian::Write(block, BATArray_[i], 0x004C+i*4, 4);
313}
314
316// PURPOSE: Read header information from a block of data.
317// REQUIRE: Block of data must be at least 512 bytes in size.
318{
319 LittleEndian::Read(block, fileType_, 0x0000, 8);
320 LittleEndian::Read(block, uk1_, 0x0008, 4);
321 LittleEndian::Read(block, uk2_, 0x000C, 4);
322 LittleEndian::Read(block, uk3_, 0x0010, 4);
323 LittleEndian::Read(block, uk4_, 0x0014, 4);
324 LittleEndian::Read(block, uk5_, 0x0018, 2);
325 LittleEndian::Read(block, uk6_, 0x001A, 2);
326 LittleEndian::Read(block, uk7_, 0x001C, 2);
327 LittleEndian::Read(block, log2BigBlockSize_, 0x001E, 2);
328 LittleEndian::Read(block, log2SmallBlockSize_, 0x0020, 4);
329 LittleEndian::Read(block, uk8_, 0x0024, 4);
330 LittleEndian::Read(block, uk9_, 0x0028, 4);
331 LittleEndian::Read(block, BATCount_, 0x002C, 4);
332 LittleEndian::Read(block, propertiesStart_, 0x0030, 4);
333 LittleEndian::Read(block, uk10_, 0x0034, 4);
334 LittleEndian::Read(block, uk11_, 0x0038, 4);
335 LittleEndian::Read(block, SBATStart_, 0x003C, 4);
336 LittleEndian::Read(block, SBATCount_, 0x0040, 4);
337 LittleEndian::Read(block, XBATStart_, 0x0044, 4);
338 LittleEndian::Read(block, XBATCount_, 0x0048, 4);
339 for (size_t i=0; i<109; ++i) LittleEndian::Read(block, BATArray_[i], 0x004C+i*4, 4);
340 Initialize();
341}
342
344{
345 bigBlockSize_ = (size_t)pow(2.0, log2BigBlockSize_); // Calculate each big block size.
346 smallBlockSize_ = (size_t)pow(2.0, log2SmallBlockSize_); // Calculate each small block size.
347}
348/********************************** End of Class Header **************************************/
349
350/********************************** Start of Class Property **********************************/
351// PURPOSE: Read and write data to a compound file property.
353 nameSize_(0),
354 propertyType_(1), nodeColor_(1),
355 previousProp_(-1), nextProp_(-1), childProp_(-1),
356 uk1_(0), uk2_(0), uk3_(0), uk4_(0), uk5_(0),
357 seconds1_(0), days1_(0), seconds2_(0), days2_(0),
358 startBlock_(-2), size_(0)
359{
360 fill (name_, name_+32, 0);
361}
362
364// PURPOSE: Write property information from a block of data.
365// REQUIRE: Block of data must be at least 128 bytes in size.
366{
367 LittleEndian::WriteString(block, name_, 0x00, 32);
368 LittleEndian::Write(block, nameSize_, 0x40, 2);
369 LittleEndian::Write(block, propertyType_, 0x42, 1);
370 LittleEndian::Write(block, nodeColor_, 0x43, 1);
371 LittleEndian::Write(block, previousProp_, 0x44, 4);
372 LittleEndian::Write(block, nextProp_, 0x48, 4);
373 LittleEndian::Write(block, childProp_, 0x4C, 4);
374 LittleEndian::Write(block, uk1_, 0x50, 4);
375 LittleEndian::Write(block, uk2_, 0x54, 4);
376 LittleEndian::Write(block, uk3_, 0x58, 4);
377 LittleEndian::Write(block, uk4_, 0x5C, 4);
378 LittleEndian::Write(block, uk5_, 0x60, 4);
379 LittleEndian::Write(block, seconds1_, 0x64, 4);
380 LittleEndian::Write(block, days1_, 0x68, 4);
381 LittleEndian::Write(block, seconds2_, 0x6C, 4);
382 LittleEndian::Write(block, days2_, 0x70, 4);
383 LittleEndian::Write(block, startBlock_, 0x74, 4);
384 LittleEndian::Write(block, size_, 0x78, 4);
385}
386
388// PURPOSE: Read property information from a block of data.
389// REQUIRE: Block of data must be at least 128 bytes in size.
390{
391 LittleEndian::ReadString(block, name_, 0x00, 32);
392 LittleEndian::Read(block, nameSize_, 0x40, 2);
393 LittleEndian::Read(block, propertyType_, 0x42, 1);
394 LittleEndian::Read(block, nodeColor_, 0x43, 1);
395 LittleEndian::Read(block, previousProp_, 0x44, 4);
396 LittleEndian::Read(block, nextProp_, 0x48, 4);
397 LittleEndian::Read(block, childProp_, 0x4C, 4);
398 LittleEndian::Read(block, uk1_, 0x50, 4);
399 LittleEndian::Read(block, uk2_, 0x54, 4);
400 LittleEndian::Read(block, uk3_, 0x58, 4);
401 LittleEndian::Read(block, uk4_, 0x5C, 4);
402 LittleEndian::Read(block, uk5_, 0x60, 4);
403 LittleEndian::Read(block, seconds1_, 0x64, 4);
404 LittleEndian::Read(block, days1_, 0x68, 4);
405 LittleEndian::Read(block, seconds2_, 0x6C, 4);
406 LittleEndian::Read(block, days2_, 0x70, 4);
407 LittleEndian::Read(block, startBlock_, 0x74, 4);
408 LittleEndian::Read(block, size_, 0x78, 4);
409}
410/********************************** End of Class Property ************************************/
411
412/********************************** Start of Class PropertyTree **********************************/
414
416{
417 size_t maxChildren = children_.size();
418 for (size_t i=0; i<maxChildren; ++i) delete children_[i];
419}
420/********************************** End of Class PropertyTree ************************************/
421
422/********************************** Start of Class CompoundFile ******************************/
423// PURPOSE: Manage a compound file.
427{};
429
430/************************* Compound File Functions ***************************/
431bool CompoundFile::Create(const wchar_t* filename)
432// PURPOSE: Create a new compound file and open it.
433// PURPOSE: If file is present, truncate it and then open it.
434// PROMISE: Return true if file is successfully created and opened, false if otherwise.
435{
436 Close();
437 file_.Create(filename);
438
439 // Write compound file header
440 header_ = Header();
441 SaveHeader();
442
443 // Save BAT
444 blocksIndices_.clear();
445 blocksIndices_.resize(128, -1);
446 blocksIndices_[0] = -3;
447 blocksIndices_[1] = -2;
448 SaveBAT();
449
450 // Save properties
451 Property* root = new Property;
452 wcscpy(root->name_, L"Root Entry");
453 root->propertyType_ = 5;
454 properties_.push_back(root);
456
457 // Set property tree
463
464 return true;
465}
466
467bool CompoundFile::Open(const wchar_t* filename, ios_base::openmode mode)
468// PURPOSE: Open an existing compound file.
469// PROMISE: Return true if file is successfully opened, false if otherwise.
470{
471 Close();
472 if (!file_.Open(filename, mode)) return false;
473
474 // Load header
475 if (!LoadHeader()) return false;
476
477 // Load BAT information
478 LoadBAT();
479
480 // Load properties
481 propertyTrees_ = new PropertyTree;
484
485 return true;
486}
487
489// PURPOSE: Close the opened compound file.
490// PURPOSE: Reset BAT indices, SBAT indices, properties and properties tree information.
491// PROMISE: Return true if file is successfully closed, false if otherwise.
492{
493 blocksIndices_.clear();
494 sblocksIndices_.clear();
495
496 size_t maxProperties = properties_.size();
497 for (size_t i=0; i<maxProperties; ++i)
498 {
499 if (properties_[i]) delete properties_[i];
500 }
501 properties_.clear();
502
503 if (propertyTrees_)
504 {
505 delete propertyTrees_;
506 propertyTrees_ = 0;
507 }
508
509 previousDirectories_.clear();
511
512 return file_.Close();
513}
514
516// PURPOSE: Check if the compound file is still opened.
517// PROMISE: Return true if file is still opened, false if otherwise.
518{
519 return file_.IsOpen();
520}
521
522/************************* Directory Functions ***************************/
523int CompoundFile::ChangeDirectory(const wchar_t* path)
524// PURPOSE: Change to a different directory in the compound file.
525// PROMISE: Current directory will not be changed if directory is not present.
526{
528
529 // Handle special cases
530 if (wcscmp(path, L".") == 0)
531 {
532 // Current directory
533 previousDirectories_.pop_back();
534 return SUCCESS;
535 }
536 if (wcscmp(path, L"..") == 0)
537 {
538 // Go up 1 directory
539 if (currentDirectory_->parent_ != 0)
540 {
542 }
543 previousDirectories_.pop_back();
544 return SUCCESS;
545 }
546 if (wcscmp(path, L"\\") == 0)
547 {
548 // Go to root directory
550 previousDirectories_.pop_back();
551 return SUCCESS;
552 }
553
554 // Handle normal cases
555 size_t ipos = 0;
556 size_t npos = 0;
557 size_t pathLength = wcslen(path);
558 if (pathLength > 0 && path[0] == L'\\')
559 {
560 // Start from root directory
562 ++ipos;
563 ++npos;
564 }
565 do
566 {
567 for (; npos<pathLength; ++npos)
568 {
569 if (path[npos] == L'\\') break;
570 }
571
572 wchar_t* directory = new wchar_t[npos-ipos+1];
573 copy (path+ipos, path+npos, directory);
574 directory[npos-ipos] = 0;
576 delete[] directory;
577 ipos = npos + 1;
578 npos = ipos;
579 if (currentDirectory_ == 0)
580 {
581 // Directory not found
583 previousDirectories_.pop_back();
584 return DIRECTORY_NOT_FOUND;
585 }
586 } while (npos < pathLength);
587 previousDirectories_.pop_back();
588 return SUCCESS;
589}
590
591int CompoundFile::MakeDirectory(const wchar_t* path)
592// PURPOSE: Create a new directory in the compound file.
593// PROMISE: Directory will not be created if it is already present or
594// PROMISE: a file with the same name is present.
595{
597 Property* property = new Property;
598 property->propertyType_ = 1;
599 int ret = MakeProperty(path, property);
601 previousDirectories_.pop_back();
602 SaveHeader();
603 SaveBAT();
605 return ret;
606}
607
609// PURPOSE: Get the full path of the current directory in the compound file.
610// REQUIRE: path must be large enough to receive the full path information.
611{
613 vector<wchar_t> fullpath;
614 do
615 {
616 size_t directoryLength = wcslen(currentDirectory_->self_->name_);
617 vector<wchar_t> directory(directoryLength+1);
618 directory[0] = L'\\';
620 currentDirectory_->self_->name_+directoryLength,
621 directory.begin()+1);
622 fullpath.insert(fullpath.begin(), directory.begin(), directory.end());
624
625 fullpath.erase(fullpath.begin(), fullpath.begin()+11);
626 if (fullpath.empty()) fullpath.push_back(L'\\');
627 copy (fullpath.begin(), fullpath.end(), path);
628 path[fullpath.size()] = 0;
630 previousDirectories_.pop_back();
631 return SUCCESS;
632}
633
635// PURPOSE: Get the full path of the current directory in the compound file.
636{
638 path.clear();
639 do
640 {
641 size_t directoryLength = wcslen(currentDirectory_->self_->name_);
642 vector<wchar_t> directory(directoryLength+1);
643 directory[0] = L'\\';
645 currentDirectory_->self_->name_+directoryLength,
646 directory.begin()+1);
647 path.insert(path.begin(), directory.begin(), directory.end());
649
650 path.erase(path.begin(), path.begin()+11);
651 if (path.empty()) path.push_back(L'\\');
653 previousDirectories_.pop_back();
654 return SUCCESS;
655}
656
657int CompoundFile::RemoveDirectory(const wchar_t* path)
658// PURPOSE: Remove a directory in the compound file.
659// PROMISE: Directory will not be removed if it has subdirectories or files under it.
660{
661 PropertyTree* directory = FindProperty(path);
662 if (directory == 0) return DIRECTORY_NOT_FOUND;
663 if (directory->self_->childProp_ != -1) return DIRECTORY_NOT_EMPTY;
664 DeletePropertyTree(directory);
665 SaveHeader();
666 SaveBAT();
668 return SUCCESS;
669}
670
671int CompoundFile::DelTree(const wchar_t* path)
672// PURPOSE: Remove everything in the path in the compound file, including
673// PURPOSE: any files and subdirectories.
674{
676 PropertyTree* directory = FindProperty(path);
677 if (directory == 0) return DIRECTORY_NOT_FOUND;
678 if (directory->self_->childProp_ != -1)
679 {
680 size_t maxChildren = directory->children_.size();
681 wchar_t* curpath = new wchar_t[65535];
682 for (size_t i=0; i<maxChildren; ++i)
683 {
684 currentDirectory_ = directory->children_[i];
686 if (directory->children_[i]->self_->propertyType_ == 1)
687 {
688 // Directory
689 DelTree(curpath);
690 }
691 else if (directory->children_[i]->self_->propertyType_ == 2)
692 {
693 // File
694 RemoveFile(curpath);
695 }
696 }
697 directory->self_->childProp_ = -1;
698 delete[] curpath;
699 }
700
701 if (directory->self_->propertyType_ == 1)
702 {
703 // Directory
704 RemoveDirectory(path);
705 }
706 else if (directory->self_->propertyType_ == 2)
707 {
708 // File
709 RemoveFile(path);
710 }
711
713 previousDirectories_.pop_back();
714 return SUCCESS;
715}
716
717int CompoundFile::DirectoryList(vector<vector<wchar_t> >& list, const wchar_t* path)
718{
720 if (path != 0)
721 {
722 int ret = ChangeDirectory(path);
723 if (ret != SUCCESS) return ret;
724 }
725 list.clear();
726 size_t maxChildren = currentDirectory_->children_.size();
727 vector<wchar_t> name(32);
728 for (size_t i=0; i<maxChildren; ++i)
729 {
730 wcscpy(&*(name.begin()), currentDirectory_->children_[i]->self_->name_);
731 list.push_back(name);
732 }
734 previousDirectories_.pop_back();
735 return SUCCESS;
736}
737
738
739/************************* File Functions ***************************/
740int CompoundFile::MakeFile(const wchar_t* path)
741// PURPOSE: Create a new file in the compound file.
742// PROMISE: File will not be created if it is already present or
743// PROMISE: a directory with the same name is present.
744{
746 Property* property = new Property;
747 property->propertyType_ = 2;
748 int ret = MakeProperty(path, property);
750 previousDirectories_.pop_back();
751 SaveHeader();
752 SaveBAT();
754 return ret;
755}
756
757int CompoundFile::RemoveFile(const wchar_t* path)
758// PURPOSE: Remove a file in the compound file.
759{
760 int ret = WriteFile(path, 0, 0);
761 if (ret == SUCCESS)
762 {
764 SaveHeader();
765 SaveBAT();
767 return SUCCESS;
768 }
769 else return ret;
770}
771
772int CompoundFile::FileSize(const wchar_t* path, size_t& size)
773// PURPOSE: Get the size of a file in the compound file.
774// PROMISE: Return the data size stored in the Root Entry if path = "\".
775// PROMISE: size will not be set if file is not present in the compound file.
776{
777 // Special case of reading root entry
778 if (wcscmp(path, L"\\") == 0)
779 {
780 size = propertyTrees_->self_->size_;
781 return SUCCESS;
782 }
783
784 // Check to see if file is present in the specified directory.
785 PropertyTree* property = FindProperty(path);
786 if (property == 0) return FILE_NOT_FOUND;
787 else
788 {
789 size = property->self_->size_;
790 return SUCCESS;
791 }
792}
793
794int CompoundFile::ReadFile(const wchar_t* path, char* data)
795// PURPOSE: Read a file's data in the compound file.
796// REQUIRE: data must be large enough to receive the file's data.
797// REQUIRE: The required data size can be obtained by using FileSize().
798// PROMISE: Returns the small blocks of data stored by the Root Entry if path = "\".
799// PROMISE: data will not be set if file is not present in the compound file.
800{
801 // Special case of reading root entry
802 char* buffer;
803 if (wcscmp(path, L"\\") == 0)
804 {
805 buffer = new char[DataSize(propertyTrees_->self_->startBlock_, true)];
806 ReadData(propertyTrees_->self_->startBlock_, buffer, true);
807 copy (buffer, buffer+propertyTrees_->self_->size_, data);
808 delete[] buffer;
809 return SUCCESS;
810 }
811
812 // Check to see if file is present in the specified directory.
813 PropertyTree* property = FindProperty(path);
814 if (property == 0) return FILE_NOT_FOUND;
815
816 if (property->self_->size_ >= 4096)
817 {
818 // Data stored in normal big blocks
819 buffer = new char[DataSize(property->self_->startBlock_, true)];
820 ReadData(property->self_->startBlock_, buffer, true);
821 }
822 else
823 {
824 // Data stored in small blocks
825 buffer = new char[DataSize(property->self_->startBlock_, false)];
826 ReadData(property->self_->startBlock_, buffer, false);
827 }
828 // Truncated the retrieved data to the actual file size.
829 copy (buffer, buffer+property->self_->size_, data);
830 delete[] buffer;
831 return SUCCESS;
832}
833
834int CompoundFile::ReadFile(const wchar_t* path, vector<char>& data)
835// PURPOSE: Read a file's data in the compound file.
836// PROMISE: Returns the small blocks of data stored by the Root Entry if path = "\".
837// PROMISE: data will not be set if file is not present in the compound file.
838{
839 data.clear();
840 size_t dataSize;
841 int ret = FileSize(path, dataSize);
842 if (ret != SUCCESS) return ret;
843
844 data.resize(dataSize);
845 return ReadFile(path, &*(data.begin()));
846}
847
848int CompoundFile::WriteFile(const wchar_t* path, const char* data, size_t size)
849// PURPOSE: Write data to a file in the compound file.
850// PROMISE: The file's original data will be replaced by the new data.
851{
852 PropertyTree* property = FindProperty(path);
853 if (property == 0) return FILE_NOT_FOUND;
854
855 if (property->self_->size_ >= 4096)
856 {
857 if (size >= 4096) property->self_->startBlock_ = WriteData(data, size, property->self_->startBlock_, true);
858 else
859 {
860 property->self_->startBlock_ = WriteData(0, 0, property->self_->startBlock_, true);
861 property->self_->startBlock_ = WriteData(data, size, property->self_->startBlock_, false);
862 }
863 }
864 else
865 {
866 if (size < 4096) property->self_->startBlock_ = WriteData(data, size, property->self_->startBlock_, false);
867 else
868 {
869 property->self_->startBlock_ = WriteData(0, 0, property->self_->startBlock_, false);
870 property->self_->startBlock_ = WriteData(data, size, property->self_->startBlock_, true);
871 }
872 }
873 property->self_->size_ = size;
874 SaveHeader();
875 SaveBAT();
877 return SUCCESS;
878}
879
880int CompoundFile::WriteFile(const wchar_t* path, const vector<char>& data, size_t size)
881// PURPOSE: Write data to a file in the compound file.
882// PROMISE: The file's original data will be replaced by the new data.
883{
884 return WriteFile(path, &*(data.begin()), size);
885}
886
887/*************ANSI char compound file, directory and file functions******************/
888bool CompoundFile::Create(const char* filename)
889{
890 size_t filenameLength = strlen(filename);
891 wchar_t* wname = new wchar_t[filenameLength+1];
892 mbstowcs(wname, filename, filenameLength);
893 wname[filenameLength] = 0;
894 bool ret = Create(wname);
895 delete[] wname;
896 return ret;
897}
898
899bool CompoundFile::Open(const char* filename, ios_base::openmode mode)
900{
901 size_t filenameLength = strlen(filename);
902 wchar_t* wname = new wchar_t[filenameLength+1];
903 mbstowcs(wname, filename, filenameLength);
904 wname[filenameLength] = 0;
905 bool ret = Open(wname, mode);
906 delete[] wname;
907 return ret;
908}
909
910int CompoundFile::ChangeDirectory(const char* path)
911{
912 size_t pathLength = strlen(path);
913 wchar_t* wpath = new wchar_t[pathLength+1];
914 mbstowcs(wpath, path, pathLength);
915 wpath[pathLength] = 0;
916 int ret = ChangeDirectory(wpath);
917 delete[] wpath;
918 return ret;
919}
920
921int CompoundFile::MakeDirectory(const char* path)
922{
923 size_t pathLength = strlen(path);
924 wchar_t* wpath = new wchar_t[pathLength+1];
925 mbstowcs(wpath, path, pathLength);
926 wpath[pathLength] = 0;
927 int ret = MakeDirectory(wpath);
928 delete[] wpath;
929 return ret;
930}
931
933{
934 size_t pathLength = strlen(path);
935 wchar_t* wpath = new wchar_t[pathLength+1];
936 int ret = PresentWorkingDirectory(wpath);
937 if (ret == SUCCESS)
938 {
939 pathLength = wcslen(wpath);
940 wcstombs(path, wpath, pathLength);
941 path[pathLength] = 0;
942 }
943 delete[] wpath;
944 return ret;
945}
946
948{
949 vector<wchar_t> wpath;
950 int ret = PresentWorkingDirectory(wpath);
951 if (ret == SUCCESS)
952 {
953 size_t pathLength = wpath.size();
954 path.resize(pathLength);
955 wcstombs(&*(path.begin()), &*(wpath.begin()), pathLength);
956 path[pathLength] = 0;
957 }
958 return ret;
959}
960
961int CompoundFile::RemoveDirectory(const char* path)
962{
963 size_t pathLength = strlen(path);
964 wchar_t* wpath = new wchar_t[pathLength+1];
965 mbstowcs(wpath, path, pathLength);
966 wpath[pathLength] = 0;
967 int ret = RemoveDirectory(wpath);
968 delete[] wpath;
969 return ret;
970}
971
972int CompoundFile::DelTree(const char* path)
973{
974 size_t pathLength = strlen(path);
975 wchar_t* wpath = new wchar_t[pathLength+1];
976 mbstowcs(wpath, path, pathLength);
977 wpath[pathLength] = 0;
978 int ret = DelTree(wpath);
979 delete[] wpath;
980 return ret;
981}
982
983int CompoundFile::MakeFile(const char* path)
984{
985 size_t pathLength = strlen(path);
986 wchar_t* wpath = new wchar_t[pathLength+1];
987 mbstowcs(wpath, path, pathLength);
988 wpath[pathLength] = 0;
989 int ret = MakeFile(wpath);
990 delete[] wpath;
991 return ret;
992}
993
994int CompoundFile::RemoveFile(const char* path)
995{
996 size_t pathLength = strlen(path);
997 wchar_t* wpath = new wchar_t[pathLength+1];
998 mbstowcs(wpath, path, pathLength);
999 wpath[pathLength] = 0;
1000 int ret = RemoveFile(wpath);
1001 delete[] wpath;
1002 return ret;
1003}
1004
1005int CompoundFile::FileSize(const char* path, size_t& size)
1006{
1007 size_t pathLength = strlen(path);
1008 wchar_t* wpath = new wchar_t[pathLength+1];
1009 mbstowcs(wpath, path, pathLength);
1010 wpath[pathLength] = 0;
1011 int ret = FileSize(wpath, size);
1012 delete[] wpath;
1013 return ret;
1014}
1015int CompoundFile::ReadFile(const char* path, char* data)
1016{
1017 size_t pathLength = strlen(path);
1018 wchar_t* wpath = new wchar_t[pathLength+1];
1019 mbstowcs(wpath, path, pathLength);
1020 wpath[pathLength] = 0;
1021 int ret = ReadFile(wpath, data);
1022 delete[] wpath;
1023 return ret;
1024}
1025int CompoundFile::ReadFile(const char* path, vector<char>& data)
1026{
1027 size_t pathLength = strlen(path);
1028 wchar_t* wpath = new wchar_t[pathLength+1];
1029 mbstowcs(wpath, path, pathLength);
1030 wpath[pathLength] = 0;
1031 int ret = ReadFile(wpath, data);
1032 delete[] wpath;
1033 return ret;
1034}
1035int CompoundFile::WriteFile(const char* path, char* data, size_t size)
1036{
1037 size_t pathLength = strlen(path);
1038 wchar_t* wpath = new wchar_t[pathLength+1];
1039 mbstowcs(wpath, path, pathLength);
1040 wpath[pathLength] = 0;
1041 int ret = WriteFile(wpath, data, size);
1042 delete[] wpath;
1043 return ret;
1044}
1045int CompoundFile::WriteFile(const char* path, vector<char>& data, size_t size)
1046{
1047 size_t pathLength = strlen(path);
1048 wchar_t* wpath = new wchar_t[pathLength+1];
1049 mbstowcs(wpath, path, pathLength);
1050 wpath[pathLength] = 0;
1051 int ret = WriteFile(wpath, data, size);
1052 delete[] wpath;
1053 return ret;
1054}
1055
1056/*********************** Inaccessible General Functions ***************************/
1058// PURPOSE: Increase block location references in header, BAT indices and properties,
1059// PURPOSE: which will be affected by the insertion of new indices contained in indices.
1060// PROMISE: Block location references which are smaller than all the new indices
1061// PROMISE: will not be affected.
1062// PROMISE: SBAT location references will not be affected.
1063// PROMISE: Changes will not be written to compound file.
1064{
1065 size_t maxIndices = indices.size();
1066
1067 // Change BAT Array references
1068 {for (size_t i=0; i<109 && header_.BATArray_[i]!=-1; ++i)
1069 {
1070 size_t count = 0;
1071 for (size_t j=0; j<maxIndices; ++j)
1072 {
1073 if (header_.BATArray_[i] >= (int)indices[j] &&
1074 header_.BATArray_[i] != -1) ++count;
1075 }
1076 header_.BATArray_[i] += count;
1077 }}
1078
1079 // Change XBAT start block if any
1080 if (header_.XBATCount_)
1081 {
1082 size_t count = 0;
1083 for (size_t j=0; j<maxIndices; ++j)
1084 {
1085 if (header_.XBATStart_ >= (int)indices[j] &&
1086 header_.XBATStart_ != -2) ++count;
1087 }
1088 header_.XBATStart_ += count;
1089 }
1090
1091 // Change SBAT start block if any
1092 if (header_.SBATCount_)
1093 {
1094 size_t count = 0;
1095 for (size_t j=0; j<maxIndices; ++j)
1096 {
1097 if (header_.SBATStart_ >= (int)indices[j] &&
1098 header_.SBATStart_ != -2) ++count;
1099 }
1100 header_.SBATStart_ += count;
1101 }
1102
1103 // Change BAT block indices
1104 size_t maxBATindices = blocksIndices_.size();
1105 {for (size_t i=0; i<maxBATindices && blocksIndices_[i]!=-1; ++i)
1106 {
1107 size_t count = 0;
1108 for (size_t j=0; j<maxIndices; ++j)
1109 {
1110 if (blocksIndices_[i] > (int)indices[j] &&
1111 blocksIndices_[i] != -2 &&
1112 blocksIndices_[i] != -3) ++count;
1113 }
1114 blocksIndices_[i] += count;
1115 }}
1116
1117 // Change properties start block
1118 size_t count = 0;
1119 {for (size_t i=0; i<maxIndices; ++i)
1120 {
1121 if (header_.propertiesStart_ >= (int)indices[i] &&
1122 header_.propertiesStart_ != -2) ++count;
1123 }}
1124 header_.propertiesStart_ += count;
1125
1126 // Change individual properties start block if their size is more than 4096
1127 size_t maxProperties = properties_.size();
1128 if (!properties_.empty())
1129 {
1130 size_t count = 0;
1131 for (size_t j=0; j<maxIndices; ++j)
1132 {
1133 if (properties_[0]->startBlock_ >= (int)indices[j] &&
1134 properties_[0]->startBlock_ != -2) ++count;
1135 }
1136 properties_[0]->startBlock_ += count;
1137 }
1138 {for (size_t i=1; i<maxProperties; ++i)
1139 {
1140 if (properties_[i]->size_ >= 4096)
1141 {
1142 size_t count = 0;
1143 for (size_t j=0; j<maxIndices; ++j)
1144 {
1145 if (properties_[i]->startBlock_ >= (int)indices[j] &&
1146 properties_[i]->startBlock_ != -2) ++count;
1147 }
1148 properties_[i]->startBlock_ += count;
1149 }
1150 }}
1151}
1152
1154// PURPOSE: Decrease block location references in header, BAT indices and properties,
1155// PURPOSE: which will be affected by the deletion of indices contained in indices.
1156// PROMISE: BAT indices pointing to a deleted index will be redirected to point to
1157// PROMISE: the location where the deleted index original points to.
1158// PROMISE: Block location references which are smaller than all the new indices
1159// PROMISE: will not be affected.
1160// PROMISE: SBAT location references will not be affected.
1161// PROMISE: Changes will not be written to compound file.
1162{
1163 size_t maxIndices = indices.size();
1164
1165 // Change BAT Array references
1166 {for (size_t i=0; i<109 && header_.BATArray_[i]!=-1; ++i)
1167 {
1168 size_t count = 0;
1169 for (size_t j=0; j<maxIndices; ++j)
1170 {
1171 if (header_.BATArray_[i] > (int)indices[j] &&
1172 header_.BATArray_[i] != -1) ++count;
1173 }
1174 header_.BATArray_[i] -= count;
1175 }}
1176
1177 // Change XBAT start block if any
1178 if (header_.XBATCount_)
1179 {
1180 size_t count = 0;
1181 for (size_t j=0; j<maxIndices; ++j)
1182 {
1183 if (header_.XBATStart_ > (int)indices[j] &&
1184 header_.XBATStart_ != -2) ++count;
1185 }
1186 header_.XBATStart_ -= count;
1187 }
1188
1189 // Change SBAT start block if any
1190 if (header_.SBATCount_)
1191 {
1192 size_t count = 0;
1193 for (size_t j=0; j<maxIndices; ++j)
1194 {
1195 if (header_.SBATStart_ > (int)indices[j] &&
1196 header_.SBATStart_ != -2) ++count;
1197 }
1198 header_.SBATStart_ -= count;
1199 }
1200
1201 // Change BAT block indices
1202 // Redirect BAT indices pointing to a deleted index to point to
1203 // the location where the deleted index original points to.
1204 size_t maxBATindices = blocksIndices_.size();
1205 {for (size_t i=0; i<maxBATindices && blocksIndices_[i]!=-1; ++i)
1206 {
1207 bool end;
1208 do
1209 {
1210 end = true;
1211 for (size_t j=0; j<maxIndices; ++j)
1212 {
1213 if (blocksIndices_[i] == (int)indices[j])
1214 {
1215 blocksIndices_[i] = blocksIndices_[indices[j]];
1216 end = false;
1217 break;
1218 }
1219 }
1220 } while (!end);
1221 }}
1222 // Erase indices to be deleted from the block indices
1223 sort (indices.begin(), indices.end(), greater<size_t>());
1224 {for (size_t i=0; i<maxIndices; ++i)
1225 {
1226 blocksIndices_.erase(blocksIndices_.begin()+indices[i]);
1227 blocksIndices_.push_back(-1);
1228 }}
1229
1230 // Decrease block location references for affected block indices.
1231 {for (size_t i=0; i<maxBATindices && blocksIndices_[i]!=-1; ++i)
1232 {
1233 size_t count = 0;
1234 for (size_t j=0; j<maxIndices; ++j)
1235 {
1236 if (blocksIndices_[i] > (int)indices[j] &&
1237 blocksIndices_[i] != -2 &&
1238 blocksIndices_[i] != -3) ++count;
1239 }
1240 blocksIndices_[i] -= count;
1241 }}
1242
1243 // Change properties start block
1244 size_t count = 0;
1245 {for (size_t i=0; i<maxIndices; ++i)
1246 {
1247 if (header_.propertiesStart_ > (int)indices[i] &&
1248 header_.propertiesStart_ != -2) ++count;
1249 }}
1250 header_.propertiesStart_ -= count;
1251
1252 size_t maxProperties = properties_.size();
1253 // Change Root Entry start block
1254 if (!properties_.empty())
1255 {
1256 size_t count = 0;
1257 for (size_t j=0; j<maxIndices; ++j)
1258 {
1259 if (properties_[0]->startBlock_ > (int)indices[j] &&
1260 properties_[0]->startBlock_ != -2) ++count;
1261 }
1262 properties_[0]->startBlock_ -= count;
1263 }
1264 {for (size_t i=1; i<maxProperties; ++i)
1265 {
1266 if (properties_[i]->size_ >= 4096)
1267 {
1268 // Change individual properties start block if their size is more than 4096
1269 size_t count = 0;
1270 for (size_t j=0; j<maxIndices; ++j)
1271 {
1272 if (properties_[i]->startBlock_ > (int)indices[j] &&
1273 properties_[i]->startBlock_ != -2) ++count;
1274 }
1275 properties_[i]->startBlock_ -= count;
1276 }
1277 }}
1278}
1279
1280void CompoundFile::SplitPath(const wchar_t* path,
1281 wchar_t*& parentpath,
1282 wchar_t*& propertyname)
1283// PURPOSE: Get a path's parent path and its name.
1284// EXPLAIN: E.g. path = "\\Abc\\def\\ghi => parentpath = "\\Abc\\def", propertyname = "ghi".
1285// REQUIRE: Calling function is responsible for deleting the memory created for
1286// REQUIRE: parentpath and propertyname.
1287{
1288 size_t pathLength = wcslen(path);
1289
1290 int npos;
1291 for (npos=pathLength-1; npos>0; --npos)
1292 {
1293 if (path[npos] == L'\\') break;
1294 }
1295
1296 if (npos != 0)
1297 {
1298 // Get parent path if available
1299 parentpath = new wchar_t[npos+1];
1300 copy (path, path+npos, parentpath);
1301 parentpath[npos] = 0;
1302 ++npos;
1303 }
1304
1305 // Get property name (ignore initial "\" if present)
1306 if (npos==0 && pathLength > 0 && path[0] == L'\\') ++npos;
1307 propertyname = new wchar_t[pathLength-npos+1];
1308 copy (path+npos, path+pathLength, propertyname);
1309 propertyname[pathLength-npos] = 0;
1310}
1311
1312/*********************** Inaccessible Header Functions ***************************/
1314// PURPOSE: Load header information for compound file.
1315// PROMISE: Return true if file header contain magic number, false if otherwise.
1316{
1317 file_.Read(0, &*(block_.begin()));
1318 header_.Read(&*(block_.begin()));
1319
1320 // Check magic number to see if it is a compound file
1321 if (header_.fileType_ != 0xE11AB1A1E011CFD0LL) return false;
1322
1323 block_.resize(header_.bigBlockSize_); // Resize buffer block
1324 file_.SetBlockSize(header_.bigBlockSize_); // Resize block array block size
1325 return true;
1326}
1327
1329// PURPOSE: Save header information for compound file.
1330{
1331 header_.Write(&*(block_.begin()));
1332 file_.Write(0, &*(block_.begin()));
1333}
1334
1335/*********************** Inaccessible BAT Functions ***************************/
1337// PURPOSE: Load all block allocation table information for compound file.
1338{
1339 // Read BAT indices
1340 {for (size_t i = 0; i < (unsigned)header_.BATCount_; ++i)
1341 {
1342 // Load blocksIndices_
1343 blocksIndices_.resize(blocksIndices_.size()+128, -1);
1344 file_.Read(header_.BATArray_[i]+1, &*(block_.begin()));
1345 for (size_t j=0; j<128; ++j)
1346 {
1347 LittleEndian::Read(&*(block_.begin()), blocksIndices_[j+i*128], j*4, 4);
1348 }
1349 }}
1350
1351 // Read XBAT indices
1352 {for (size_t i = 0; i < (unsigned)header_.XBATCount_; ++i)
1353 {
1354 blocksIndices_.resize(blocksIndices_.size()+128, -1);
1355 file_.Read(header_.XBATStart_+i+1, &*(block_.begin()));
1356 for (size_t j=0; j<128; ++j)
1357 {
1358 LittleEndian::Read(&*(block_.begin()), blocksIndices_[j+((i+109)*128)], j*4, 4);
1359 }
1360 }}
1361
1362 // Read SBAT indices
1363 {for (size_t i = 0; i < (unsigned)header_.SBATCount_; ++i)
1364 {
1365 sblocksIndices_.resize(sblocksIndices_.size()+128, -1);
1366 file_.Read(header_.SBATStart_+i+1, &*(block_.begin()));
1367 for (size_t j=0; j<128; ++j)
1368 {
1369 LittleEndian::Read(&*(block_.begin()), sblocksIndices_[j+i*128], j*4, 4);
1370 }
1371 }}
1372}
1373
1375// PURPOSE: Save all block allocation table information for compound file.
1376{
1377 // Write BAT indices
1378 {for (size_t i = 0; i < (unsigned)header_.BATCount_; ++i)
1379 {
1380 for (size_t j=0; j<128; ++j)
1381 {
1382 LittleEndian::Write(&*(block_.begin()), blocksIndices_[j+i*128], j*4, 4);
1383 }
1384 file_.Write(header_.BATArray_[i]+1, &*(block_.begin()));
1385 }}
1386
1387 // Write XBAT indices
1388 {for (size_t i = 0; i < (unsigned)header_.XBATCount_; ++i)
1389 {
1390 for (size_t j=0; j<128; ++j)
1391 {
1392 LittleEndian::Write(&*(block_.begin()), blocksIndices_[j+((i+109)*128)], j*4, 4);
1393 }
1394 file_.Write(header_.XBATStart_+i+1, &*(block_.begin()));
1395 }}
1396
1397 // Write SBAT indices
1398 {for (size_t i = 0; i < (unsigned)header_.SBATCount_; ++i)
1399 {
1400 for (size_t j=0; j<128; ++j)
1401 {
1402 LittleEndian::Write(&*(block_.begin()), sblocksIndices_[j+i*128], j*4, 4);
1403 }
1404 file_.Write(header_.SBATStart_+i+1, &*(block_.begin()));
1405 }}
1406}
1407
1408size_t CompoundFile::DataSize(size_t startIndex, bool isBig)
1409// PURPOSE: Gets the total size occupied by a property, starting from startIndex.
1410// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1411// PROMISE: Returns the total size occupied by the property which is the total
1412// PROMISE: number of blocks occupied multiply by the block size.
1413{
1414 vector<size_t> indices;
1415 if (isBig)
1416 {
1417 GetBlockIndices(startIndex, indices, true);
1418 return indices.size()*header_.bigBlockSize_;
1419 }
1420 else
1421 {
1422 GetBlockIndices(startIndex, indices, false);
1423 return indices.size()*header_.smallBlockSize_;
1424 }
1425}
1426
1427size_t CompoundFile::ReadData(size_t startIndex, char* data, bool isBig)
1428// PURPOSE: Read a property's data, starting from startIndex.
1429// REQUIRE: data must be large enough to receive the property's data
1430// REQUIRE: The required data size can be obtained by using DataSize().
1431// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1432// PROMISE: Returns the total size occupied by the property which is the total
1433// PROMISE: number of blocks occupied multiply by the block size.
1434{
1435 vector<size_t> indices;
1436 if (isBig)
1437 {
1438 GetBlockIndices(startIndex, indices, true);
1439 size_t maxIndices = indices.size();
1440 for (size_t i=0; i<maxIndices; ++i)
1441 {
1442 file_.Read(indices[i]+1, data+i*header_.bigBlockSize_);
1443 }
1444 return maxIndices*header_.bigBlockSize_;
1445 }
1446 else
1447 {
1448 GetBlockIndices(startIndex, indices, false);
1449 size_t minIndex = *min_element(indices.begin(), indices.end());
1450 //size_t maxIndex = *max_element(indices.begin(), indices.end());
1451 size_t smallBlocksPerBigBlock = header_.bigBlockSize_ / header_.smallBlockSize_;
1452 size_t minBlock = minIndex / smallBlocksPerBigBlock;
1453 /*size_t maxBlock = maxIndex / smallBlocksPerBigBlock +
1454 (maxIndex % smallBlocksPerBigBlock ? 1 : 0);
1455 size_t totalBlocks = maxBlock - minBlock;*/
1456 char* buffer = new char[DataSize(properties_[0]->startBlock_, true)];
1457 ReadData(properties_[0]->startBlock_, buffer, true);
1458
1459 size_t maxIndices = indices.size();
1460 for (size_t i=0; i<maxIndices; ++i)
1461 {
1462 size_t start = (indices[i] - minBlock*smallBlocksPerBigBlock)*header_.smallBlockSize_;
1463 copy (buffer+start,
1464 buffer+start+header_.smallBlockSize_,
1465 data+i*header_.smallBlockSize_);
1466 }
1467 delete[] buffer;
1468 return maxIndices*header_.smallBlockSize_;
1469 }
1470}
1471
1472size_t CompoundFile::WriteData(const char* data, size_t size, int startIndex, bool isBig)
1473// PURPOSE: Write data to a property, starting from startIndex.
1474// EXPLAIN: startIndex can be -2 if property initially has no data.
1475// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1476// PROMISE: The file's original data will be replaced by the new data.
1477// PROMISE: Returns the startIndex of new data for the property.
1478{
1479 if (isBig)
1480 {
1481 if (size==0 && startIndex==-2) return startIndex;
1482
1483 // Get present indices
1484 vector<size_t> indices;
1485 GetBlockIndices(startIndex, indices, true);
1486 size_t maxPresentBlocks = indices.size();
1487
1488 // Calculate how many blocks does the data need
1489 size_t extraSize = size % header_.bigBlockSize_;
1490 size_t maxNewBlocks = size / header_.bigBlockSize_ + (extraSize ? 1 : 0);
1491
1492 // Readjust indices and remove blocks if new data size is smaller than original
1493 int extraBlocks = maxPresentBlocks - maxNewBlocks;
1494 if (extraBlocks > 0)
1495 {
1496 // Place new end marker
1497 if (maxNewBlocks != 0) blocksIndices_[indices[maxNewBlocks]-1] = -2;
1498 else startIndex = -2;
1499
1500 // Get indices of blocks to delete
1501 vector<size_t> indicesToRemove(extraBlocks);
1502 copy (indices.begin()+maxNewBlocks, indices.end(), indicesToRemove.begin());
1503 indices.erase(indices.begin()+maxNewBlocks, indices.end());
1504
1505 // Remove extra blocks and readjust indices
1506 FreeBlocks(indicesToRemove, true);
1507 }
1508
1509 // Write blocks into available space
1510 size_t remainingFullBlocks = size / header_.bigBlockSize_;
1511 size_t curIndex=0;
1512 if (maxPresentBlocks != 0)
1513 {
1514 for (; remainingFullBlocks && curIndex<maxPresentBlocks;
1515 --remainingFullBlocks, ++curIndex)
1516 {
1517 file_.Write(indices[curIndex]+1, data+curIndex*header_.bigBlockSize_);
1518 }
1519 }
1520
1521 // Check if all blocks have been written
1522 size_t index;
1523 if (indices.empty()) index = 0;
1524 else if (curIndex == 0) index = indices[0];
1525 else index = (startIndex != -2) ? indices[curIndex-1] : 0;
1526 if (remainingFullBlocks != 0)
1527 {
1528 // Require extra blocks to write data (i.e. new data is larger than original data
1529 do
1530 {
1531 size_t newIndex = GetFreeBlockIndex(true); // Get new free block to write data
1532 if (startIndex == -2) startIndex = newIndex; // Get start index
1533 else LinkBlocks(index, newIndex, true); // Link last index to new index
1534 file_.Write(newIndex+1, data+curIndex*header_.bigBlockSize_);
1535 ++curIndex;
1536 index = newIndex;
1537 } while (--remainingFullBlocks);
1538 }
1539
1540 if (extraSize != 0)
1541 {
1542 size_t newIndex;
1543 if (curIndex >= maxPresentBlocks)
1544 {
1545 // No more free blocks to write extra block data
1546 newIndex = GetFreeBlockIndex(true); // Get new free block to write data
1547 if (startIndex == -2) startIndex = newIndex;
1548 else LinkBlocks(index, newIndex,true);
1549 }
1550 else newIndex = indices[curIndex];
1551
1552 // Write extra block after increasing its size to the minimum block size
1553 vector<char> tempdata(header_.bigBlockSize_, 0);
1554 copy (data+curIndex*header_.bigBlockSize_, data+curIndex*header_.bigBlockSize_+extraSize, tempdata.begin());
1555 file_.Write(newIndex+1, &*(tempdata.begin()));
1556 }
1557 return startIndex;
1558 }
1559 else
1560 {
1561 if (size==0 && startIndex==-2) return startIndex;
1562
1563 if (size != 0 && properties_[0]->startBlock_ == -2)
1564 {
1565 size_t newIndex = GetFreeBlockIndex(true);
1566 fill (block_.begin(), block_.end(), 0);
1567 file_.Insert(newIndex, &*(block_.begin()));
1568 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1569 properties_[0]->startBlock_ = newIndex;
1570 properties_[0]->size_ = header_.bigBlockSize_;
1571 }
1572
1573 // Get present indices
1574 vector<size_t> indices;
1575 GetBlockIndices(startIndex, indices, false);
1576 size_t maxPresentBlocks = indices.size();
1577
1578 // Calculate how many blocks does the data need
1579 size_t extraSize = size % header_.smallBlockSize_;
1580 size_t maxNewBlocks = size / header_.smallBlockSize_ + (extraSize ? 1 : 0);
1581
1582 vector<char> smallBlocksData;
1583 int extraBlocks = maxPresentBlocks - maxNewBlocks;
1584 if (extraBlocks > 0)
1585 {
1586 // Readjust indices and remove blocks
1587 // Place new end marker
1588 if (maxNewBlocks != 0) sblocksIndices_[indices[maxNewBlocks]-1] = -2;
1589 else startIndex = -2;
1590
1591 // Get indices of blocks to delete
1592 vector<size_t> indicesToRemove(extraBlocks);
1593 copy (indices.begin()+maxNewBlocks, indices.end(), indicesToRemove.begin());
1594 indices.erase(indices.begin()+maxNewBlocks, indices.end());
1595
1596 // Remove extra blocks and readjust indices
1597 FreeBlocks(indicesToRemove, false);
1598 }
1599 else if (extraBlocks < 0)
1600 {
1601 size_t maxBlocks = properties_[0]->size_ / header_.bigBlockSize_ +
1602 (properties_[0]->size_ % header_.bigBlockSize_ ? 1 : 0);
1603 size_t actualSize = maxBlocks * header_.bigBlockSize_;
1604 smallBlocksData.resize(actualSize);
1605 ReadData(properties_[0]->startBlock_, &*(smallBlocksData.begin()), true);
1606 smallBlocksData.resize(properties_[0]->size_);
1607
1608 // Readjust indices and add blocks
1609 size_t newBlocksNeeded = -extraBlocks;
1610 size_t index = maxPresentBlocks - 1;
1611 for (size_t i=0; i<newBlocksNeeded; ++i)
1612 {
1613 size_t newIndex = GetFreeBlockIndex(false); // Get new free block to write data
1614 if (startIndex == -2) startIndex = newIndex; // Get start index
1615 else LinkBlocks(index, newIndex, false); // Link last index to new index
1616 smallBlocksData.insert(smallBlocksData.begin()+newIndex,
1618 index = newIndex;
1619 }
1620 properties_[0]->size_ = newBlocksNeeded * header_.smallBlockSize_;
1621 }
1622 if (smallBlocksData.empty())
1623 {
1624 size_t maxBlocks = properties_[0]->size_ / header_.bigBlockSize_ +
1625 (properties_[0]->size_ % header_.bigBlockSize_ ? 1 : 0);
1626 size_t actualSize = maxBlocks * header_.bigBlockSize_;
1627 smallBlocksData.resize(actualSize);
1628 ReadData(properties_[0]->startBlock_, &*(smallBlocksData.begin()), true);
1629 smallBlocksData.resize(properties_[0]->size_);
1630 }
1631
1632 // Write blocks
1633 GetBlockIndices(startIndex, indices, false);
1634 size_t fullBlocks = size / header_.smallBlockSize_;
1635 for (size_t i=0; i<fullBlocks; ++i)
1636 {
1637 copy (data+i*header_.smallBlockSize_,
1639 smallBlocksData.begin()+indices[i]*header_.smallBlockSize_);
1640 }
1641 if (extraSize != 0)
1642 {
1643 copy (data+fullBlocks*header_.smallBlockSize_,
1644 data+fullBlocks*header_.smallBlockSize_+extraSize,
1645 smallBlocksData.begin()+indices[fullBlocks]*header_.smallBlockSize_);
1646 }
1647 WriteData(&*(smallBlocksData.begin()), properties_[0]->size_,
1648 properties_[0]->startBlock_, true);
1649 return startIndex;
1650 }
1651}
1652
1653void CompoundFile::GetBlockIndices(size_t startIndex, vector<size_t>& indices, bool isBig)
1654// PURPOSE: Get the indices of blocks where data are stored, starting from startIndex.
1655// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1656{
1657 indices.clear();
1658 if (isBig)
1659 {
1660 for (int i = (int)startIndex; i != -2; i = blocksIndices_[i])
1661 indices.push_back(i);
1662 }
1663 else
1664 {
1665 for (int i = (int)startIndex; i != -2; i = sblocksIndices_[i])
1666 indices.push_back(i);
1667 }
1668}
1669
1671// PURPOSE: Get the index of a new block where data can be stored.
1672// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1673// PROMISE: It does not physically create a new block in the compound file.
1674// PROMISE: It only adjust BAT arrays and indices or SBAT arrays and indices so that
1675// PROMISE: it gives the index of a new block where data can be inserted.
1676{
1677 size_t index;
1678 if (isBig)
1679 {
1680 // Find first free location
1681 index = distance(blocksIndices_.begin(),
1682 find(blocksIndices_.begin(),
1683 blocksIndices_.end(), -1));
1684 if (index == blocksIndices_.size())
1685 {
1686 ExpandBATArray(true);
1687 index = distance(blocksIndices_.begin(),
1688 find(blocksIndices_.begin(),
1689 blocksIndices_.end(), -1));
1690 }
1691 blocksIndices_[index] = -2;
1692 }
1693 else
1694 {
1695 // Find first free location
1696 index = distance(sblocksIndices_.begin(),
1697 find(sblocksIndices_.begin(),
1698 sblocksIndices_.end(), -1));
1699 if (index == sblocksIndices_.size())
1700 {
1701 ExpandBATArray(false);
1702 index = distance(sblocksIndices_.begin(),
1703 find(sblocksIndices_.begin(),
1704 sblocksIndices_.end(), -1));
1705 }
1706 sblocksIndices_[index] = -2;
1707 }
1708 return index;
1709}
1710
1712// PURPOSE: Create a new block of BAT or SBAT indices.
1713// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1714{
1715 size_t newIndex;
1716 fill (block_.begin(), block_.end(), -1);
1717
1718 if (isBig)
1719 {
1720 size_t BATindex = distance(&header_.BATArray_[0],
1721 find(header_.BATArray_,
1722 header_.BATArray_+109, -1));
1723 if (BATindex < 109)
1724 {
1725 // Set new BAT index location
1726 newIndex = blocksIndices_.size(); // New index location
1727 file_.Insert(newIndex+1, &*(block_.begin()));
1728 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1729
1730 // Update BAT array
1731 header_.BATArray_[BATindex] = newIndex;
1733 }
1734 else
1735 {
1736 // No free BAT indices. Increment using XBAT
1737 // Set new XBAT index location
1738 if (header_.XBATCount_ != 0)
1739 {
1740 newIndex = header_.XBATStart_ + header_.XBATCount_;
1741 file_.Insert(newIndex, &*(block_.begin()));
1742 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1743 }
1744 else
1745 {
1746 newIndex = blocksIndices_.size();
1747 file_.Insert(newIndex, &*(block_.begin()));
1748 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1749 header_.XBATStart_ = newIndex;
1750 }
1752 }
1753 blocksIndices_.insert(blocksIndices_.begin()+newIndex, -3);
1754 blocksIndices_.resize(blocksIndices_.size()+127, -1);
1755 }
1756 else
1757 {
1758 // Set new SBAT index location
1759 if (header_.SBATCount_ != 0)
1760 {
1761 newIndex = header_.SBATStart_ + header_.SBATCount_;
1762 file_.Insert(newIndex, &*(block_.begin()));
1763 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1764 }
1765 else
1766 {
1767 newIndex = GetFreeBlockIndex(true);
1768 file_.Insert(newIndex, &*(block_.begin()));
1769 IncreaseLocationReferences(vector<size_t>(1, newIndex));
1770 header_.SBATStart_ = newIndex;
1771 }
1773 sblocksIndices_.resize(sblocksIndices_.size()+128, -1);
1774 }
1775}
1776
1777void CompoundFile::LinkBlocks(size_t from, size_t to, bool isBig)
1778// PURPOSE: Link one BAT index to another.
1779// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1780{
1781 if (isBig) blocksIndices_[from] = to;
1782 else sblocksIndices_[from] = to;
1783}
1784
1785void CompoundFile::FreeBlocks(vector<size_t>& indices, bool isBig)
1786// PURPOSE: Delete blocks of data from compound file.
1787// EXPLAIN: indices contains indices to blocks of data to be deleted.
1788// EXPLAIN: isBig is true if property uses big blocks, false if it uses small blocks.
1789{
1790 if (isBig)
1791 {
1792 // Decrease all location references before deleting blocks from file.
1794 size_t maxIndices = indices.size();
1795 {for (size_t i=0; i<maxIndices; ++i) ++indices[i];} // Increase by 1 because Block index 1 corresponds to index 0 here
1796 file_.Erase(indices);
1797
1798 // Shrink BAT indices if necessary
1799 vector<size_t> indicesToRemove;
1800 while (distance(find(blocksIndices_.begin(),
1801 blocksIndices_.end(),-1),
1802 blocksIndices_.end()) >= 128)
1803 {
1804 blocksIndices_.resize(blocksIndices_.size()-128);
1805 if (header_.XBATCount_ != 0)
1806 {
1807 // Shrink XBAT first
1809 indicesToRemove.push_back(header_.XBATStart_+header_.XBATCount_+1); // Add 1 because Block index 1 corresponds to index 0 here
1810 if (header_.XBATCount_ == 0) header_.XBATStart_ = -2;
1811 }
1812 else
1813 {
1814 // No XBAT, delete last occupied BAT array element
1815 size_t BATindex = distance(&header_.BATArray_[0],
1816 find(header_.BATArray_,
1817 header_.BATArray_+109, -1));
1818 if (BATindex != 109)
1819 {
1821 indicesToRemove.push_back(header_.BATArray_[BATindex-1]+1); // Add 1 because Block index 1 corresponds to index 0 here
1822 header_.BATArray_[BATindex-1] = -1;
1823 }
1824 }
1825 }
1826 // Erase extra BAT indices if present
1827 if (!indicesToRemove.empty()) file_.Erase(indicesToRemove);
1828 }
1829 else
1830 {
1831 // Erase block
1832 size_t maxIndices = indices.size();
1833 size_t maxBlocks = properties_[0]->size_ / header_.bigBlockSize_ +
1834 (properties_[0]->size_ % header_.bigBlockSize_ ? 1 : 0);
1835 size_t size = maxBlocks * header_.bigBlockSize_;
1836 char* data = new char[size];
1837 ReadData(properties_[0]->startBlock_, data, true);
1838 size_t maxSmallBlocks = properties_[0]->size_ / header_.smallBlockSize_;
1839 char* newdata = new char[properties_[0]->size_-maxIndices*header_.smallBlockSize_];
1840 {for (size_t i=0, j=0; i<maxSmallBlocks; ++i)
1841 {
1842 if (find(indices.begin(), indices.end(), i) == indices.end())
1843 {
1844 copy (data+i*header_.smallBlockSize_,
1846 newdata+j*header_.smallBlockSize_);
1847 ++j;
1848 }
1849 }}
1850 properties_[0]->startBlock_ = WriteData(newdata, properties_[0]->size_-maxIndices*header_.smallBlockSize_,
1851 properties_[0]->startBlock_, true);
1852 properties_[0]->size_ -= maxIndices*header_.smallBlockSize_;
1853 delete[] data;
1854 delete[] newdata;
1855
1856 // Change SBAT indices
1857 size_t maxSBATindices = sblocksIndices_.size();
1858 {for (size_t i=0; i<maxIndices; ++i)
1859 {
1860 for (size_t j=0; j<maxSBATindices; ++j)
1861 {
1862 if (j == indices[i]) continue;
1863 if ((unsigned)sblocksIndices_[j] == indices[i]) sblocksIndices_[j] = sblocksIndices_[indices[i]];
1864 if ((unsigned)sblocksIndices_[j] > indices[i]
1865 && sblocksIndices_[j] != -1
1866 && sblocksIndices_[j] != -2) --sblocksIndices_[j];
1867 }
1868 }}
1869 sort (indices.begin(), indices.end(), greater<size_t>());
1870 {for (size_t i=0; i<maxIndices; ++i)
1871 {
1872 sblocksIndices_.erase(sblocksIndices_.begin()+indices[i]);
1873 sblocksIndices_.push_back(-1);
1874 }}
1875 vector<size_t> indicesToRemove;
1876 while (distance(find(sblocksIndices_.begin(),
1877 sblocksIndices_.end(),-1),
1878 sblocksIndices_.end()) >= 128)
1879 {
1880 // Shrink SBAT indices if necessary
1881 sblocksIndices_.resize(sblocksIndices_.size()-128);
1883 indicesToRemove.push_back(header_.SBATStart_+header_.SBATCount_);
1884 if (header_.SBATCount_ == 0) header_.SBATStart_ = -2;
1885 }
1886 FreeBlocks(indicesToRemove, true);
1887 }
1888}
1889
1890/*********************** Inaccessible Properties Functions ***************************/
1892// PURPOSE: Load properties information for compound file.
1893{
1894 // Read properties' data from compound file.
1895 size_t propertiesSize = DataSize(header_.propertiesStart_, true);
1896 char* buffer = new char[propertiesSize];
1897 ReadData(header_.propertiesStart_, buffer, true);
1898
1899 // Split properties' data into individual property.
1900 size_t maxPropertiesBlock = propertiesSize / header_.bigBlockSize_;
1901 size_t propertiesPerBlock = header_.bigBlockSize_ / 128;
1902 size_t maxProperties = maxPropertiesBlock * propertiesPerBlock;
1903 size_t maxBlocks = maxProperties / propertiesPerBlock +
1904 (maxProperties % propertiesPerBlock ? 1 : 0);
1905
1906 for (size_t i=0; i<maxBlocks; ++i)
1907 {
1908 for (size_t j=0; j<4; ++j)
1909 {
1910 // Read individual property
1911 Property* property = new Property;
1912 property->Read(buffer+i*512+j*128);
1913 if (wcslen(property->name_) == 0)
1914 {
1915 delete property;
1916 break;
1917 }
1918 properties_.push_back(property);
1919 }
1920 }
1921 delete[] buffer;
1922
1923 // Generate property trees
1927
1929 properties_[properties_[0]->childProp_],
1930 properties_[0]->childProp_);
1931}
1932
1934// PURPOSE: Save properties information for compound file.
1935{
1936 // Calculate total size required by properties
1937 size_t maxProperties = properties_.size();
1938 size_t propertiesPerBlock = header_.bigBlockSize_ / 128;
1939 size_t maxBlocks = maxProperties / propertiesPerBlock +
1940 (maxProperties % propertiesPerBlock ? 1 : 0);
1941 size_t propertiesSize = maxBlocks*header_.bigBlockSize_;
1942 char* buffer = new char[propertiesSize];
1943 {for (size_t i=0; i<propertiesSize; ++i) buffer[i] = 0;}
1944 {for (size_t i=0; i<maxProperties; ++i)
1945 {
1946 // Save individual property
1947 properties_[i]->Write(buffer+i*128);
1948 }}
1949
1950 // Write properties' data to compound file.
1951 WriteData(buffer, propertiesSize, header_.propertiesStart_, true);
1952 delete[] buffer;
1953}
1954
1955int CompoundFile::MakeProperty(const wchar_t* path, CompoundFile::Property* property)
1956// PURPOSE: Create a new property in the compound file.
1957// EXPLAIN: path is the full path name for the property.
1958// EXPLAIN: property contains information on the type of property to be created.
1959{
1960 wchar_t* parentpath = 0;
1961 wchar_t* propertyname = 0;
1962
1963 // Change to the specified directory. If specified directory is not present,
1964 // create it.
1965 if (wcslen(path) != 0)
1966 {
1967 if (path[0] == L'\\') currentDirectory_ = propertyTrees_;
1968 }
1969 SplitPath(path, parentpath, propertyname);
1970
1971 if (propertyname != 0)
1972 {
1973 if (parentpath != 0)
1974 {
1975 if (ChangeDirectory(parentpath) != SUCCESS)
1976 {
1977 int ret = MakeDirectory(parentpath);
1978 if (ret != SUCCESS)
1979 {
1980 delete[] parentpath;
1981 delete[] propertyname;
1982 return ret;
1983 }
1984 else ChangeDirectory(parentpath);
1985 }
1986 delete[] parentpath;
1987 }
1988
1989 // Insert property into specified directory
1990 size_t propertynameLength = wcslen(propertyname);
1991 if (propertynameLength >= 32)
1992 {
1993 delete[] propertyname;
1994 return NAME_TOO_LONG;
1995 }
1996 wcscpy(property->name_, propertyname);
1997 delete[] propertyname;
1998 property->nameSize_ = propertynameLength*2+2;
1999 if (FindProperty(currentDirectory_, property->name_) == 0)
2000 {
2001 // Find location to insert property
2002 size_t maxProperties = properties_.size();
2003 size_t index;
2004 for (index=1; index<maxProperties; ++index)
2005 {
2006 if (*(properties_[index]) > *property) break;
2007 }
2008 if (index != maxProperties)
2009 {
2010 // Change references for all properties affected by the new property
2012 }
2013 properties_.insert(properties_.begin()+index, property);
2014 InsertPropertyTree(currentDirectory_, property, index);
2015 return SUCCESS;
2016 }
2017 else return DUPLICATE_PROPERTY;
2018 }
2019 else
2020 {
2021 if (parentpath != 0) delete[] parentpath;
2022 return INVALID_PATH;
2023 }
2024}
2025
2027// PURPOSE: Find property in the compound file, given the index of the property.
2028// PROMISE: Returns a pointer to the property tree of the property if property
2029// PROMISE: is present, 0 if otherwise.
2030{
2032 PropertyTree* currentTree = previousDirectories_.back();
2033 if (currentTree->index_ != index)
2034 {
2035 size_t maxChildren = currentTree->children_.size();
2036 for (size_t i=0; i<maxChildren; ++i)
2037 {
2038 previousDirectories_.push_back(currentTree->children_[i]);
2039 PropertyTree* child = FindProperty(index);
2040 if (child != 0)
2041 {
2042 previousDirectories_.pop_back();
2043 return child;
2044 }
2045 }
2046 }
2047 else
2048 {
2049 previousDirectories_.pop_back();
2050 return currentTree;
2051 }
2052 previousDirectories_.pop_back();
2053 return 0;
2054}
2055
2057// PURPOSE: Find property in the compound file, given the path of the property.
2058// PROMISE: Returns a pointer to the property tree of the property if property
2059// PROMISE: is present, 0 if otherwise.
2060{
2062
2063 // Change to specified directory
2064 wchar_t* parentpath = 0;
2065 wchar_t* filename = 0;
2066
2067 if (wcslen(path) != 0)
2068 {
2069 if (path[0] == L'\\') currentDirectory_ = propertyTrees_;
2070 }
2071
2072 SplitPath(path, parentpath, filename);
2073 if (parentpath != 0)
2074 {
2075 int ret = ChangeDirectory(parentpath);
2076 delete[] parentpath;
2077 if (ret != SUCCESS)
2078 {
2079 // Cannot change to specified directory
2080 if (filename != 0) delete[] filename;
2082 previousDirectories_.pop_back();
2083 PropertyTree* property = 0;
2084 return property;
2085 }
2086 }
2087
2088 // Check to see if file is present in the specified directory.
2089 PropertyTree* property = 0;
2090 if (filename != 0)
2091 {
2092 property = FindProperty(currentDirectory_, filename);
2093 delete[] filename;
2094 }
2096 previousDirectories_.pop_back();
2097 return property;
2098}
2099
2102 wchar_t* name)
2103// PURPOSE: Find property in the compound file, given the parent property tree and its name.
2104// PROMISE: Returns a pointer to the property tree of the property if property
2105// PROMISE: is present, 0 if otherwise.
2106{
2107 if (parentTree->self_->childProp_ != -1)
2108 {
2109 size_t maxChildren = parentTree->children_.size();
2110 for (size_t i=0; i<maxChildren; ++i)
2111 {
2112 if (wcscmp(parentTree->children_[i]->self_->name_, name) == 0)
2113 {
2114 return parentTree->children_[i];
2115 }
2116 }
2117 }
2118 return 0;
2119}
2120
2122 CompoundFile::Property* property,
2123 size_t index)
2124// PURPOSE: Insert a property and all its siblings and children into the property tree.
2125// REQUIRE: If the property is a new property and its index is already occupied by
2126// REQUIRE: another property, the calling function has to call IncreasePropertyReferences()
2127// REQUIRE: first before calling this function.
2128// EXPLAIN: This function is used by LoadProperty() to initialize the property trees
2129// EXPLAIN: and MakeProperty() thus resulting in the above requirements.
2130// EXPLAIN: parentTree is the parent of the new property.
2131// EXPLAIN: property is the property to be added.
2132// EXPLAIN: index is the index of the new property.
2133// PROMISE: The property will be added as the parent tree's child and the parent's
2134// PROMISE: child property and all the its children previous property and next property
2135// PROMISE: will be readjusted to accomodate the next property.
2136{
2137 PropertyTree* tree = new PropertyTree;
2138 tree->parent_ = parentTree;
2139 tree->self_ = property;
2140 tree->index_ = index;
2141
2142 if (property->previousProp_ != -1)
2143 {
2144 InsertPropertyTree(parentTree,
2145 properties_[property->previousProp_],
2146 property->previousProp_);
2147 }
2148
2149 if (property->nextProp_ != -1)
2150 {
2151 InsertPropertyTree(parentTree,
2152 properties_[property->nextProp_],
2153 property->nextProp_);
2154 }
2155
2156 if (property->childProp_ != -1)
2157 {
2158 InsertPropertyTree(tree,
2159 properties_[property->childProp_],
2160 property->childProp_);
2161 }
2162
2163 // Sort children
2164 size_t maxChildren = parentTree->children_.size();
2165 size_t i;
2166 for (i=0; i<maxChildren; ++i)
2167 {
2168 if (index < parentTree->children_[i]->index_) break;
2169 }
2170 parentTree->children_.insert(parentTree->children_.begin()+i, tree);
2171
2172 // Update children indices
2173 UpdateChildrenIndices(parentTree);
2174}
2175
2177// PURPOSE: Delete a property from properties.
2178// EXPLAIN: tree is the property tree to be deleted.
2179// PROMISE: The tree's parent's child property and all the its children previous property
2180// PROMISE: and next property will be readjusted to accomodate the deleted property.
2181{
2182 // Decrease all property references
2184
2185 // Remove property
2186 if (properties_[tree->index_]) delete properties_[tree->index_];
2187 properties_.erase(properties_.begin()+tree->index_);
2188
2189 // Remove property from property trees
2190 size_t maxChildren = tree->parent_->children_.size();
2191 size_t i;
2192 for (i=0; i<maxChildren; ++i)
2193 {
2194 if (tree->parent_->children_[i]->index_ == tree->index_) break;
2195 }
2196 tree->parent_->children_.erase(tree->parent_->children_.begin()+i);
2197
2198 // Update children indices
2200}
2201
2203{
2204 // Update indices for 1st to middle child
2205 size_t maxChildren = parentTree->children_.size();
2206 if (maxChildren != 0)
2207 {
2208 vector<PropertyTree*>& children = parentTree->children_;
2209 size_t prevChild = 0;
2210 children[0]->self_->previousProp_ = -1;
2211 children[0]->self_->nextProp_ = -1;
2212 size_t curChild;
2213 for (curChild=1; curChild<=maxChildren/2; ++curChild)
2214 {
2215 children[curChild]->self_->previousProp_ = children[prevChild]->index_;
2216 children[curChild]->self_->nextProp_ = -1;
2217 prevChild = curChild;
2218 }
2219
2220 // Update middle child
2221 --curChild;
2222 children[curChild]->parent_->self_->childProp_ = children[curChild]->index_;
2223
2224 // Update from middle to last child
2225 size_t nextChild = curChild + 1;
2226 if (nextChild < maxChildren)
2227 {
2228 children[curChild]->self_->nextProp_ = children[nextChild]->index_;
2229 for (++curChild, ++nextChild;
2230 nextChild<maxChildren;
2231 ++curChild, ++nextChild)
2232 {
2233 children[curChild]->self_->previousProp_ = -1;
2234 children[curChild]->self_->nextProp_ = children[nextChild]->index_;
2235
2236 }
2237 children[curChild]->self_->previousProp_ = -1;
2238 children[curChild]->self_->nextProp_ = -1;
2239 }
2240 }
2241 else
2242 {
2243 parentTree->self_->childProp_ = -1;
2244 }
2245}
2246
2248 size_t index)
2249// PURPOSE: Increase all property references (previous property, next property
2250// PURPOSE: and child property) which will be affected by the insertion of the new index.
2251// EXPLAIN: The recursive method of going through each property tree is used instead of
2252// EXPLAIN: using the iterative method of going through each property in properties_ is
2253// EXPLAIN: because the index in property tree needs to be updated also.
2254{
2255 if (parentTree->index_ >= index) ++parentTree->index_;
2256 if (parentTree->self_->previousProp_ != -1)
2257 {
2258 if (parentTree->self_->previousProp_ >= (int)index)
2259 {
2260 ++parentTree->self_->previousProp_;
2261 }
2262 }
2263 if (parentTree->self_->nextProp_!= -1)
2264 {
2265 if (parentTree->self_->nextProp_ >= (int)index)
2266 {
2267 ++parentTree->self_->nextProp_;
2268 }
2269 }
2270 if (parentTree->self_->childProp_ != -1)
2271 {
2272 if (parentTree->self_->childProp_ >= (int)index)
2273 {
2274 ++parentTree->self_->childProp_;
2275 }
2276 }
2277
2278 size_t maxChildren = parentTree->children_.size();
2279 for (size_t i=0; i<maxChildren; ++i)
2280 {
2281 IncreasePropertyReferences(parentTree->children_[i], index);
2282 }
2283}
2284
2286// PURPOSE: Decrease all property references (previous property, next property
2287// PURPOSE: and child property) which will be affected by the deletion of the index.
2288// EXPLAIN: The recursive method of going through each property tree is used instead of
2289// EXPLAIN: using the iterative method of going through each property in properties_ is
2290// EXPLAIN: because the index in property tree needs to be updated also.
2291{
2292 if (parentTree->index_ > index) --parentTree->index_;
2293 if (parentTree->self_->previousProp_ != -1)
2294 {
2295 if (parentTree->self_->previousProp_ > (int)index)
2296 {
2297 --parentTree->self_->previousProp_;
2298 }
2299 }
2300 if (parentTree->self_->nextProp_!= -1)
2301 {
2302 if (parentTree->self_->nextProp_ > (int)index)
2303 {
2304 --parentTree->self_->nextProp_;
2305 }
2306 }
2307 if (parentTree->self_->childProp_ != -1)
2308 {
2309 if (parentTree->self_->childProp_ > (int)index)
2310 {
2311 --parentTree->self_->childProp_;
2312 }
2313 }
2314
2315 size_t maxChildren = parentTree->children_.size();
2316 for (size_t i=0; i<maxChildren; ++i)
2317 {
2318 DecreasePropertyReferences(parentTree->children_[i], index);
2319 }
2320}
2321} // YCompoundFiles namespace end
2322
2323namespace YExcel
2324{
2325using namespace YCompoundFiles;
2326/************************************************************************************************************/
2327Record::Record() : dataSize_(0), recordSize_(4) {};
2329size_t Record::Read(const char* data)
2330{
2331 LittleEndian::Read(data, code_, 0, 2); // Read operation code.
2332 LittleEndian::Read(data, dataSize_, 2, 2); // Read size of record.
2333 data_.assign(data+4, data+4+dataSize_);
2334
2335 recordSize_ = 4 + dataSize_;
2336
2337 // Check if next record is a continue record
2338 continueIndices_.clear();
2339 short code;
2340 LittleEndian::Read(data, code, dataSize_+4, 2);
2341 while (code == CODE::CONTINUE)
2342 {
2343 continueIndices_.push_back(dataSize_);
2344
2345 size_t size;
2346 LittleEndian::Read(data, size, recordSize_+2, 2);
2347 data_.insert(data_.end(), data+recordSize_+4, data+recordSize_+4+size);
2348 dataSize_ += size;
2349 recordSize_ += 4 + size;
2350
2351 LittleEndian::Read(data, code, recordSize_, 2);
2352 };
2353 return recordSize_;
2354}
2355size_t Record::Write(char* data)
2356{
2357 LittleEndian::Write(data, code_, 0, 2); // Write operation code.
2358 size_t npos = 2;
2359
2360 if (continueIndices_.empty())
2361 {
2362 size_t size = dataSize_;
2363 size_t i=0;
2364 while (size > 8224)
2365 {
2366 LittleEndian::Write(data, 8224, npos, 2); // Write size of record.
2367 npos += 2;
2368 size -= 8224;
2369 copy (data_.begin()+i*8224, data_.begin()+(i+1)*8224, data+npos);
2370 npos += 8224;
2371
2372 if (size != 0)
2373 {
2374 ++i;
2375 LittleEndian::Write(data, 0x3C, npos, 2); // Write CONTINUE code.
2376 npos += 2;
2377 }
2378 }
2379
2380 LittleEndian::Write(data, size, npos, 2); // Write size of record.
2381 npos += 2;
2382 copy (data_.begin()+i*8224, data_.begin()+i*8224+size, data+npos);
2383 npos += size;
2384 }
2385 else
2386 {
2387 size_t maxContinue = continueIndices_.size();
2388 size_t size = continueIndices_[0];
2389 LittleEndian::Write(data, size, npos, 2); // Write size of record
2390 npos += 2;
2391 copy (data_.begin(), data_.begin()+size, data+npos);
2392 npos += size;
2393 size_t c=0;
2394 for (c=1; c<maxContinue; ++c)
2395 {
2396 LittleEndian::Write(data, 0x3C, npos, 2); // Write CONTINUE code.
2397 npos += 2;
2398 size = continueIndices_[c] - continueIndices_[c-1];
2399 LittleEndian::Write(data, size, npos, 2);
2400 npos += 2;
2401 copy (data_.begin()+continueIndices_[c-1],
2402 data_.begin()+continueIndices_[c],
2403 data+npos);
2404 npos += size;
2405 }
2406 LittleEndian::Write(data, 0x3C, npos, 2); // Write CONTINUE code.
2407 npos += 2;
2408 size = data_.size() - continueIndices_[c-1];
2409 LittleEndian::Write(data, size, npos, 2);
2410 npos += 2;
2411 copy (data_.begin()+continueIndices_[c-1],
2412 data_.end(),
2413 data+npos);
2414 npos += size;
2415 }
2416 return npos;
2417}
2418size_t Record::DataSize() {return dataSize_;}
2420
2421/************************************************************************************************************/
2422
2423/************************************************************************************************************/
2425size_t BOF::Read(const char* data)
2426{
2427 Record::Read(data);
2434 return RecordSize();
2435}
2436size_t BOF::Write(char* data)
2437{
2438 data_.resize(dataSize_);
2445 return Record::Write(data);
2446}
2447/************************************************************************************************************/
2448
2449/************************************************************************************************************/
2451/************************************************************************************************************/
2452
2453/************************************************************************************************************/
2454SmallString::SmallString() : wname_(0), name_(0) {};
2457 wname_(0), name_(0), unicode_(s.unicode_)
2458{
2459 if (s.name_)
2460 {
2461 size_t len = strlen(s.name_);
2462 name_ = new char[len+1];
2463 strcpy(name_, s.name_);
2464 }
2465 if (s.wname_)
2466 {
2467 size_t len = wcslen(s.wname_);
2468 wname_ = new wchar_t[len+1];
2469 wcscpy(wname_, s.wname_);
2470 }
2471}
2473{
2474 Reset();
2475 unicode_ = s.unicode_;
2476 if (s.name_)
2477 {
2478 size_t len = strlen(s.name_);
2479 name_ = new char[len+1];
2480 strcpy(name_, s.name_);
2481 }
2482 if (s.wname_)
2483 {
2484 size_t len = wcslen(s.wname_);
2485 wname_ = new wchar_t[len+1];
2486 wcscpy(wname_, s.wname_);
2487 }
2488 return *this;
2489}
2491{
2492 unicode_ = 0;
2493 Reset();
2494 size_t len = strlen(str);
2495 name_ = new char[len+1];
2496 strcpy(name_, str);
2497 return *this;
2498}
2499const SmallString& SmallString::operator=(const wchar_t* str)
2500{
2501 unicode_ = 1;
2502 Reset();
2503 size_t len = wcslen(str);
2504 wname_ = new wchar_t[len+1];
2505 wcscpy(wname_, str);
2506 return *this;
2507}
2509{
2510 if (name_) {delete[] name_; name_ = 0;}
2511 if (wname_) {delete[] wname_; wname_ = 0;}
2512}
2513size_t SmallString::Read(const char* data)
2514{
2515 Reset();
2516 char stringSize;
2517 LittleEndian::Read(data, stringSize, 0, 1);
2518 LittleEndian::Read(data, unicode_, 1, 1);
2519 size_t bytesRead = 2;
2520 if (unicode_ == 0)
2521 {
2522 // ANSI string
2523 name_ = new char[stringSize+1];
2524 LittleEndian::ReadString(data, name_, 2, stringSize);
2525 name_[(size_t)stringSize] = 0;
2526 bytesRead += stringSize;
2527 }
2528 else
2529 {
2530 // UNICODE
2531 wname_ = new wchar_t[stringSize+1];
2532 LittleEndian::ReadString(data, wname_, 2, stringSize);
2533 wname_[(size_t)stringSize] = 0;
2534 bytesRead += stringSize*2;
2535 }
2536 return bytesRead;
2537}
2538size_t SmallString::Write(char* data)
2539{
2540 size_t stringSize = 0;
2541 size_t bytesWrite = 0;
2542 if (unicode_ == 0)
2543 {
2544 // ANSI string
2545 if (name_)
2546 {
2547 stringSize = strlen(name_);
2548 LittleEndian::Write(data, stringSize, 0, 1);
2549 LittleEndian::Write(data, unicode_, 1, 1);
2550 LittleEndian::WriteString(data, name_, 2, stringSize);
2551 bytesWrite = 2 + stringSize;
2552 }
2553 else
2554 {
2555 LittleEndian::Write(data, stringSize, 0, 1);
2556 LittleEndian::Write(data, unicode_, 1, 1);
2557 bytesWrite = 2;
2558 }
2559 }
2560 else
2561 {
2562 // UNICODE
2563 if (wname_)
2564 {
2565 stringSize = wcslen(wname_);
2566 LittleEndian::Write(data, stringSize, 0, 1);
2567 LittleEndian::Write(data, unicode_, 1, 1);
2568 LittleEndian::WriteString(data, wname_, 2, stringSize);
2569 bytesWrite = 2 + stringSize*2;
2570 }
2571 else
2572 {
2573 LittleEndian::Write(data, stringSize, 0, 1);
2574 LittleEndian::Write(data, unicode_, 1, 1);
2575 bytesWrite = 2;
2576 }
2577 }
2578 return bytesWrite;
2579}
2580size_t SmallString::DataSize() {return (unicode_ == 0) ? StringSize()+2 : StringSize()*2+2;}
2583{
2584 if (unicode_ == 0)
2585 {
2586 if (name_) return strlen(name_);
2587 }
2588 else
2589 {
2590 if (wname_) return wcslen(wname_);
2591 }
2592 return 0;
2593}
2594/************************************************************************************************************/
2595
2596/************************************************************************************************************/
2597LargeString::LargeString() : unicode_(-1), richtext_(0), phonetic_(0) {};
2600 wname_(s.wname_), name_(s.name_),
2601 unicode_(s.unicode_), richtext_(s.richtext_), phonetic_(s.phonetic_) {};
2603{
2604 unicode_ = s.unicode_;
2605 richtext_ = s.richtext_;
2606 phonetic_ = s.phonetic_;
2607 name_ = s.name_;
2608 wname_ = s.wname_;
2609 return *this;
2610}
2612{
2613 unicode_ = 0;
2614 richtext_ = 0;
2615 phonetic_ = 0;
2616 wname_.clear();
2617 size_t len = strlen(str);
2618 name_.resize(len+1);
2619 strcpy(&*(name_.begin()), str);
2620 return *this;
2621}
2622const LargeString& LargeString::operator=(const wchar_t* str)
2623{
2624 unicode_ = 1;
2625 richtext_ = 0;
2626 phonetic_ = 0;
2627 name_.clear();
2628 size_t len = wcslen(str);
2629 wname_.resize(len+1);
2630 wcscpy(&*(wname_.begin()), str);
2631 return *this;
2632}
2633size_t LargeString::Read(const char* data)
2634{
2635 size_t stringSize;
2636 LittleEndian::Read(data, stringSize, 0, 2);
2637 LittleEndian::Read(data, unicode_, 2, 1);
2638 size_t npos = 3;
2639 if (unicode_ & 8)
2640 {
2641 LittleEndian::Read(data, richtext_, npos, 2);
2642 npos += 2;
2643 }
2644 if (unicode_ & 4) LittleEndian::Read(data, phonetic_, npos, 4);
2645 name_.clear();
2646 wname_.clear();
2647 size_t bytesRead = 2;
2648 if (stringSize>0) bytesRead += ContinueRead(data+2, stringSize);
2649 else bytesRead = 3;
2650 return bytesRead;
2651}
2652size_t LargeString::ContinueRead(const char* data, size_t size)
2653{
2654 if (size == 0) return 0;
2655
2656 char unicode;
2657 LittleEndian::Read(data, unicode, 0, 1);
2658 if (unicode_ == -1) unicode_ = unicode;
2659 if (unicode_ & 1)
2660 {
2661 // Present stored string is uncompressed (16 bit)
2662 size_t npos = 1;
2663 if (richtext_) npos += 2;
2664 if (phonetic_) npos += 4;
2665
2666 size_t strpos = wname_.size();
2667 wname_.resize(strpos+size, 0);
2668 if (unicode & 1)
2669 {
2670 LittleEndian::ReadString(data, &*(wname_.begin())+strpos, npos, size);
2671 npos += size * SIZEOFWCHAR_T;
2672 }
2673 else
2674 {
2675 // String to be read is in ANSI
2676 vector<char> name(size);
2677 LittleEndian::ReadString(data, &*(name.begin()), npos, size);
2678 mbstowcs(&*(wname_.begin())+strpos, &*(name.begin()), size);
2679 npos += size;
2680 }
2681 if (richtext_) npos += 4*richtext_;
2682 if (phonetic_) npos += phonetic_;
2683 return npos;
2684 }
2685 else
2686 {
2687 // Present stored string has character compression (8 bit)
2688 size_t npos = 1;
2689 if (richtext_) npos += 2;
2690 if (phonetic_) npos += 4;
2691
2692 size_t strpos = name_.size();
2693 name_.resize(strpos+size, 0);
2694 if (unicode & 1)
2695 {
2696 // String to be read is in unicode
2697 vector<wchar_t> name(size);
2698 LittleEndian::ReadString(data, &*(name.begin()), npos, size);
2699 wcstombs(&*(name_.begin())+strpos, &*(name.begin()), size);
2700 npos += size * SIZEOFWCHAR_T;
2701 }
2702 else
2703 {
2704 LittleEndian::ReadString(data, &*(name_.begin())+strpos, npos, size);
2705 npos += size;
2706 }
2707 if (richtext_) npos += 4*richtext_;
2708 if (phonetic_) npos += phonetic_;
2709 return npos;
2710 }
2711}
2712size_t LargeString::Write(char* data)
2713{
2714 size_t stringSize = 0;
2715 size_t bytesWrite = 0;
2716 if (unicode_ & 1)
2717 {
2718 // UNICODE
2719 unicode_ = 1; // Don't handle richtext or phonetic for now.
2720 if (!wname_.empty())
2721 {
2722 stringSize = wname_.size();
2723 LittleEndian::Write(data, stringSize, 0, 2);
2724 LittleEndian::Write(data, unicode_, 2, 1);
2725 LittleEndian::WriteString(data, &*(wname_.begin()), 3, stringSize);
2726 bytesWrite = 3 + stringSize * SIZEOFWCHAR_T;
2727 }
2728 else
2729 {
2730 LittleEndian::Write(data, stringSize, 0, 2);
2731 LittleEndian::Write(data, unicode_, 2, 1);
2732 bytesWrite = 3;
2733 }
2734 }
2735 else
2736 {
2737 // ANSI string
2738 unicode_ = 0; // Don't handle richtext or phonetic for now.
2739 if (!name_.empty())
2740 {
2741 stringSize = name_.size();
2742 LittleEndian::Write(data, stringSize, 0, 2);
2743 LittleEndian::Write(data, unicode_, 2, 1);
2744 LittleEndian::WriteString(data, &*(name_.begin()), 3, stringSize);
2745 bytesWrite = 3 + stringSize;
2746 }
2747 else
2748 {
2749 LittleEndian::Write(data, stringSize, 0, 2);
2750 LittleEndian::Write(data, unicode_, 2, 1);
2751 bytesWrite = 3;
2752 }
2753 }
2754 return bytesWrite;
2755}
2757{
2758 size_t dataSize = StringSize() + 3;
2759 if (richtext_) dataSize += 2 + 4*richtext_;
2760 if (phonetic_) dataSize += 4 + phonetic_;
2761 return dataSize;
2762}
2765{
2766 if (unicode_ & 1) return wname_.size() * SIZEOFWCHAR_T;
2767 else return name_.size();
2768}
2769/************************************************************************************************************/
2770
2771
2772/************************************************************************************************************/
2774{
2775 bof_.version_ = 1536;
2776 bof_.type_ = 5;
2777 bof_.buildIdentifier_ = 6560;
2778 bof_.buildYear_ = 1997;
2779 bof_.fileHistoryFlags_ = 49353;
2781}
2782
2783size_t Workbook::Read(const char* data)
2784{
2785 size_t bytesRead = 0;
2786 short code;
2787 LittleEndian::Read(data, code, 0, 2);
2788 while (code != CODE::YEOF)
2789 {
2790 switch (code)
2791 {
2792 case CODE::BOF:
2793 bytesRead += bof_.Read(data+bytesRead);
2794 break;
2795
2796 case CODE::WINDOW1:
2797 bytesRead += window1_.Read(data+bytesRead);
2798 break;
2799
2800 case CODE::FONT:
2801 fonts_.push_back(Font());
2802 bytesRead += fonts_.back().Read(data+bytesRead);
2803 break;
2804
2805 case CODE::XF:
2806 XFs_.push_back(XF());
2807 bytesRead += XFs_.back().Read(data+bytesRead);
2808 break;
2809
2810 case CODE::STYLE:
2811 styles_.push_back(Style());
2812 bytesRead += styles_.back().Read(data+bytesRead);
2813 break;
2814
2815 case CODE::BOUNDSHEET:
2816 boundSheets_.push_back(BoundSheet());
2817 bytesRead += boundSheets_.back().Read(data+bytesRead);
2818 break;
2819
2820 case CODE::SST:
2821 bytesRead += sst_.Read(data+bytesRead);
2822 break;
2823
2824// case CODE::EXTSST:
2825// bytesRead += extSST_.Read(data+bytesRead);
2826// break;
2827
2828 default:
2829 Record rec;
2830 bytesRead += rec.Read(data+bytesRead);
2831 }
2832 LittleEndian::Read(data, code, bytesRead, 2);
2833 }
2834 bytesRead += eof_.RecordSize();
2835 return bytesRead;
2836}
2837size_t Workbook::Write(char* data)
2838{
2839 size_t bytesWritten = 0;
2840
2841 bytesWritten += bof_.Write(data+bytesWritten);
2842
2843 bytesWritten += window1_.Write(data+bytesWritten);
2844
2845 size_t maxFonts = fonts_.size();
2846 {for (size_t i=0; i<maxFonts; ++i) {bytesWritten += fonts_[i].Write(data+bytesWritten);}}
2847
2848 size_t maxXFs = XFs_.size();
2849 {for (size_t i=0; i<maxXFs; ++i) {bytesWritten += XFs_[i].Write(data+bytesWritten);}}
2850
2851 size_t maxStyles = styles_.size();
2852 {for (size_t i=0; i<maxStyles; ++i) {bytesWritten += styles_[i].Write(data+bytesWritten);}}
2853
2854 size_t maxBoundSheets = boundSheets_.size();
2855 {for (size_t i=0; i<maxBoundSheets; ++i) {bytesWritten += boundSheets_[i].Write(data+bytesWritten);}}
2856
2857 bytesWritten += sst_.Write(data+bytesWritten);
2858// bytesWritten += extSST_.Write(data+bytesWritten);
2859
2860 bytesWritten += eof_.Write(data+bytesWritten);
2861
2862 return bytesWritten;
2863}
2865{
2866 size_t size = 0;
2867 size += bof_.RecordSize();
2868 size += window1_.RecordSize();
2869
2870 size_t maxFonts = fonts_.size();
2871 {for (size_t i=0; i<maxFonts; ++i) {size += fonts_[i].RecordSize();}}
2872
2873 size_t maxXFs = XFs_.size();
2874 {for (size_t i=0; i<maxXFs; ++i) {size += XFs_[i].RecordSize();}}
2875
2876 size_t maxStyles = styles_.size();
2877 {for (size_t i=0; i<maxStyles; ++i) {size += styles_[i].RecordSize();}}
2878
2879 size_t maxBoundSheets = boundSheets_.size();
2880 {for (size_t i=0; i<maxBoundSheets; ++i) {size += boundSheets_[i].RecordSize();}}
2881
2882 size += sst_.RecordSize();
2883// size += extSST_.RecordSize();
2884 size += eof_.RecordSize();
2885 return size;
2886}
2887size_t Workbook::RecordSize() {return DataSize();}
2888/************************************************************************************************************/
2889
2890/************************************************************************************************************/
2892 horizontalPos_(0x78), verticalPos_(0x78), width_(0x3B1F), height_(0x2454),
2893 options_(0x38), activeWorksheetIndex_(0), firstVisibleTabIndex_(0), selectedWorksheetNo_(1),
2894 worksheetTabBarWidth_(0x258) {code_ = CODE::WINDOW1; dataSize_ = 18; recordSize_ = 22;}
2895size_t Workbook::Window1::Read(const char* data)
2896{
2897 Record::Read(data);
2898 LittleEndian::Read(data_, horizontalPos_, 0, 2);
2899 LittleEndian::Read(data_, verticalPos_, 2, 2);
2900 LittleEndian::Read(data_, width_, 4, 2);
2901 LittleEndian::Read(data_, height_, 6, 2);
2902 LittleEndian::Read(data_, options_, 8, 2);
2903 LittleEndian::Read(data_, activeWorksheetIndex_, 10, 2);
2904 LittleEndian::Read(data_, firstVisibleTabIndex_, 12, 2);
2905 LittleEndian::Read(data_, selectedWorksheetNo_, 14, 2);
2906 LittleEndian::Read(data_, worksheetTabBarWidth_, 16, 2);
2907 return RecordSize();
2908}
2910{
2911 data_.resize(dataSize_);
2912 LittleEndian::Write(data_, horizontalPos_, 0, 2);
2913 LittleEndian::Write(data_, verticalPos_, 2, 2);
2914 LittleEndian::Write(data_, width_, 4, 2);
2915 LittleEndian::Write(data_, height_, 6, 2);
2916 LittleEndian::Write(data_, options_, 8, 2);
2917 LittleEndian::Write(data_, activeWorksheetIndex_, 10, 2);
2918 LittleEndian::Write(data_, firstVisibleTabIndex_, 12, 2);
2919 LittleEndian::Write(data_, selectedWorksheetNo_, 14, 2);
2920 LittleEndian::Write(data_, worksheetTabBarWidth_, 16, 2);
2921 return Record::Write(data);
2922}
2923/************************************************************************************************************/
2924
2925/************************************************************************************************************/
2927 height_(200), options_(0), colourIndex_(0x7FFF), weight_(400), escapementType_(0),
2928 underlineType_(0), family_(0), characterSet_(0), unused_(0)
2929{
2930 code_ = CODE::FONT;
2931 dataSize_ = 14;
2932 recordSize_ = 18;
2933 name_ = L"Arial";
2934 name_.unicode_ = 1;
2935}
2936size_t Workbook::Font::Read(const char* data)
2937{
2938 Record::Read(data);
2939 LittleEndian::Read(data_, height_, 0, 2);
2940 LittleEndian::Read(data_, options_, 2, 2);
2941 LittleEndian::Read(data_, colourIndex_, 4, 2);
2942 LittleEndian::Read(data_, weight_, 6, 2);
2943 LittleEndian::Read(data_, escapementType_, 8, 2);
2944 LittleEndian::Read(data_, underlineType_, 10, 1);
2945 LittleEndian::Read(data_, family_, 11, 1);
2946 LittleEndian::Read(data_, characterSet_, 12, 1);
2947 LittleEndian::Read(data_, unused_, 13, 1);
2948 name_.Read(&*(data_.begin())+14);
2949 return RecordSize();
2950}
2951size_t Workbook::Font::Write(char* data)
2952{
2953 data_.resize(DataSize());
2954 LittleEndian::Write(data_, height_, 0, 2);
2955 LittleEndian::Write(data_, options_, 2, 2);
2956 LittleEndian::Write(data_, colourIndex_, 4, 2);
2957 LittleEndian::Write(data_, weight_, 6, 2);
2958 LittleEndian::Write(data_, escapementType_, 8, 2);
2959 LittleEndian::Write(data_, underlineType_, 10, 1);
2960 LittleEndian::Write(data_, family_, 11, 1);
2961 LittleEndian::Write(data_, characterSet_, 12, 1);
2962 LittleEndian::Write(data_, unused_, 13, 1);
2963 name_.Write(&*(data_.begin())+14);
2964 return Record::Write(data);
2965}
2966size_t Workbook::Font::DataSize() {return (dataSize_ = 14 + name_.RecordSize());}
2967size_t Workbook::Font::RecordSize() {return (recordSize_ = DataSize()+4);}
2968/************************************************************************************************************/
2969
2970/************************************************************************************************************/
2972 fontRecordIndex_(0), formatRecordIndex_(0), protectionType_(0xFFF5), alignment_(0x20), rotation_(0x00),
2973 textProperties_(0x00), usedAttributes_(0x00), borderLines_(0x0000), colour1_(0x0000), colour2_(0x20C0)
2974 {code_ = CODE::XF; dataSize_ = 20; recordSize_ = 24;}
2975size_t Workbook::XF::Read(const char* data)
2976{
2977 Record::Read(data);
2978 LittleEndian::Read(data_, fontRecordIndex_, 0, 2);
2979 LittleEndian::Read(data_, formatRecordIndex_, 2, 2);
2980 LittleEndian::Read(data_, protectionType_, 4, 2);
2981 LittleEndian::Read(data_, alignment_, 6, 1);
2982 LittleEndian::Read(data_, rotation_, 7, 1);
2983 LittleEndian::Read(data_, textProperties_, 8, 1);
2984 LittleEndian::Read(data_, usedAttributes_, 9, 1);
2985 LittleEndian::Read(data_, borderLines_, 10, 4);
2986 LittleEndian::Read(data_, colour1_, 14, 4);
2987 LittleEndian::Read(data_, colour2_, 18, 2);
2988 return RecordSize();
2989}
2990size_t Workbook::XF::Write(char* data)
2991{
2992 data_.resize(dataSize_);
2993 LittleEndian::Write(data_, fontRecordIndex_, 0, 2);
2994 LittleEndian::Write(data_, formatRecordIndex_, 2, 2);
2995 LittleEndian::Write(data_, protectionType_, 4, 2);
2996 LittleEndian::Write(data_, alignment_, 6, 1);
2997 LittleEndian::Write(data_, rotation_, 7, 1);
2998 LittleEndian::Write(data_, textProperties_, 8, 1);
2999 LittleEndian::Write(data_, usedAttributes_, 9, 1);
3000 LittleEndian::Write(data_, borderLines_, 10, 4);
3001 LittleEndian::Write(data_, colour1_, 14, 4);
3002 LittleEndian::Write(data_, colour2_, 18, 2);
3003 return Record::Write(data);
3004}
3005/************************************************************************************************************/
3006
3007/************************************************************************************************************/
3009 XFRecordIndex_(0x8000), identifier_(0), level_(0xFF)
3010 {code_ = CODE::STYLE; dataSize_ = 2; recordSize_ = 6;}
3011size_t Workbook::Style::Read(const char* data)
3012{
3013 Record::Read(data);
3014 LittleEndian::Read(data_, XFRecordIndex_, 0, 2);
3015 if (XFRecordIndex_ & 0x8000)
3016 {
3017 // Built-in styles
3018 LittleEndian::Read(data_, identifier_, 2, 1);
3019 LittleEndian::Read(data_, level_, 3, 1);
3020 }
3021 else
3022 {
3023 // User-defined styles
3024 name_.Read(&*(data_.begin())+2);
3025 }
3026 return RecordSize();
3027}
3028size_t Workbook::Style::Write(char* data)
3029{
3030 data_.resize(DataSize());
3031 LittleEndian::Write(data_, XFRecordIndex_, 0, 2);
3032 if (XFRecordIndex_ & 0x8000)
3033 {
3034 // Built-in styles
3035 LittleEndian::Write(data_, identifier_, 2, 1);
3036 LittleEndian::Write(data_, level_, 3, 1);
3037 }
3038 else
3039 {
3040 // User-defined styles
3041 name_.Write(&*(data_.begin())+2);
3042 }
3043 return Record::Write(data);
3044}
3045size_t Workbook::Style::DataSize() {return (dataSize_ = (XFRecordIndex_ & 0x8000) ? 4 : 2+name_.RecordSize());}
3046size_t Workbook::Style::RecordSize() {return (recordSize_ = DataSize()+4);}
3047/************************************************************************************************************/
3048
3049/************************************************************************************************************/
3051 BOFpos_(0x0000), visibility_(0), type_(0)
3052{
3054 dataSize_ = 6;
3055 dataSize_ = 10;
3056 name_ = "Sheet1";
3057 name_.unicode_ = false;
3058}
3059size_t Workbook::BoundSheet::Read(const char* data)
3060{
3061 Record::Read(data);
3062 LittleEndian::Read(data_, BOFpos_, 0, 4);
3063 LittleEndian::Read(data_, visibility_, 4, 1);
3064 LittleEndian::Read(data_, type_, 5, 1);
3065 name_.Read(&*(data_.begin())+6);
3066 return RecordSize();
3067}
3069{
3070 data_.resize(DataSize());
3071 LittleEndian::Write(data_, BOFpos_, 0, 4);
3072 LittleEndian::Write(data_, visibility_, 4, 1);
3073 LittleEndian::Write(data_, type_, 5, 1);
3074 name_.Write(&*(data_.begin())+6);
3075 return Record::Write(data);
3076}
3077size_t Workbook::BoundSheet::DataSize() {return (dataSize_ = 6+name_.RecordSize());}
3078size_t Workbook::BoundSheet::RecordSize() {return (recordSize_ = DataSize()+4);}
3079/************************************************************************************************************/
3080
3081/************************************************************************************************************/
3083 stringsTotal_(0), uniqueStringsTotal_(0) {code_ = CODE::SST; dataSize_ = 8; recordSize_ = 12;}
3085{
3086 Record::Read(data);
3087 LittleEndian::Read(data_, stringsTotal_, 0, 4);
3088 LittleEndian::Read(data_, uniqueStringsTotal_, 4, 4);
3089 strings_.clear();
3090 strings_.resize(uniqueStringsTotal_);
3091
3092 size_t npos = 8;
3093 if (continueIndices_.empty())
3094 {
3095 for (size_t i = 0; i < (size_t)uniqueStringsTotal_; ++i)
3096 {
3097 npos += strings_[i].Read(&*(data_.begin())+npos);
3098 }
3099 }
3100 else
3101 {
3102 // Require special handling since CONTINUE records are present
3103 size_t maxContinue = continueIndices_.size();
3104
3105 for (size_t i = 0, c = 0; i < (size_t)uniqueStringsTotal_; ++i)
3106 {
3107 char unicode;
3108 size_t stringSize;
3109 LittleEndian::Read(data_, stringSize, npos, 2);
3110 LittleEndian::Read(data_, unicode, npos+2, 1);
3111 size_t multiplier = unicode & 1 ? 2 : 1;
3112 if (c >= maxContinue || npos+stringSize*multiplier+3 <= continueIndices_[c])
3113 {
3114 // String to be read is not split into two records
3115 npos += strings_[i].Read(&*(data_.begin())+npos);
3116 }
3117 else
3118 {
3119 // String to be read is split into two or more records
3120 int bytesRead = 2;// Start from unicode field
3121
3122 int size = continueIndices_[c] - npos - 1 - bytesRead;
3123 ++c;
3124 if (size > 0)
3125 {
3126 size /= multiplier; // Number of characters available for string in current record.
3127 bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, size);
3128 stringSize -= size;
3129 size = 0;
3130 }
3131 while (c<maxContinue && npos+stringSize+1>continueIndices_[c])
3132 {
3133 size_t dataSize = (continueIndices_[c] - continueIndices_[c-1] - 1) / multiplier;
3134 bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, dataSize);
3135 stringSize -= dataSize + 1;
3136 ++c;
3137 };
3138 if (stringSize>0)
3139 {
3140 bytesRead += strings_[i].ContinueRead(&*(data_.begin())+npos+bytesRead, stringSize);
3141 }
3142 npos += bytesRead;
3143 }
3144 }
3145 }
3146 return npos + 4*(npos/8224 + 1);
3147}
3149{
3150 data_.resize(DataSize());
3151 LittleEndian::Write(data_, stringsTotal_, 0, 4);
3152 LittleEndian::Write(data_, uniqueStringsTotal_, 4, 4);
3153
3154 size_t maxContinue = continueIndices_.size();
3155 for (size_t i = 0, c = 0, npos = 8; i < (size_t)uniqueStringsTotal_; ++i)
3156 {
3157 npos += strings_[i].Write(&*(data_.begin())+npos);
3158 if (c<maxContinue && npos==continueIndices_[c]) ++c;
3159 else if (c<maxContinue && npos > continueIndices_[c])
3160 {
3161 // Insert unicode flag where appropriate for CONTINUE records.
3162 data_.insert(data_.begin()+continueIndices_[c], strings_[i].unicode_);
3163 data_.pop_back();
3164 ++c;
3165 ++npos;
3166 }
3167 }
3168 return Record::Write(data);
3169}
3171{
3172 dataSize_ = 8;
3173 continueIndices_.clear();
3174 size_t curMax = 8224;
3175 for (size_t i = 0; i < (size_t)uniqueStringsTotal_; ++i)
3176 {
3177 size_t stringSize = strings_[i].StringSize();
3178 if (dataSize_+stringSize+3 <= curMax)
3179 {
3180 dataSize_ += stringSize + 3;
3181 }
3182 else
3183 {
3184 // If have >= 12 bytes (2 for size, 1 for unicode and >=9 for data, can split string
3185 // otherwise, end record and start continue record.
3186 bool unicode = strings_[i].unicode_ & 1;
3187 if (curMax - dataSize_ >= 12)
3188 {
3189 if (unicode && !((curMax-dataSize_)%2)) --curMax; // Make sure space reserved for unicode strings is even.
3190 continueIndices_.push_back(curMax);
3191 stringSize -= (curMax - dataSize_ - 3);
3192 dataSize_ = curMax;
3193 curMax += 8224;
3194
3195 size_t additionalContinueRecords = unicode ? stringSize/8222 : stringSize/8223; // 8222 or 8223 because the first byte is for unicode identifier
3196 for (size_t j=0; j<additionalContinueRecords; ++j)
3197 {
3198 if (unicode)
3199 {
3200 --curMax;
3201 continueIndices_.push_back(curMax);
3202 curMax += 8223;
3203 dataSize_ += 8223;
3204 stringSize -= 8222;
3205 }
3206 else
3207 {
3208 continueIndices_.push_back(curMax);
3209 curMax += 8224;
3210 dataSize_ += 8224;
3211 stringSize -= 8223;
3212 }
3213 }
3214 dataSize_ += stringSize + 1;
3215 }
3216 else
3217 {
3218 continueIndices_.push_back(dataSize_);
3219 curMax = dataSize_ + 8224;
3220 if (dataSize_+stringSize+3 < curMax)
3221 {
3222 dataSize_ += stringSize + 3;
3223 }
3224 else
3225 {
3226 // If have >= 12 bytes (2 for size, 1 for unicode and >=9 for data, can split string
3227 // otherwise, end record and start continue record.
3228 if (curMax - dataSize_ >= 12)
3229 {
3230 if (unicode && !((curMax-dataSize_)%2)) --curMax; // Make sure space reserved for unicode strings is even.
3231 continueIndices_.push_back(curMax);
3232 stringSize -= (curMax - dataSize_ - 3);
3233 dataSize_ = curMax;
3234 curMax += 8224;
3235
3236 size_t additionalContinueRecords = unicode ? stringSize/8222 : stringSize/8223; // 8222 or 8223 because the first byte is for unicode identifier
3237 for (size_t j=0; j<additionalContinueRecords; ++j)
3238 {
3239 if (unicode)
3240 {
3241 --curMax;
3242 continueIndices_.push_back(curMax);
3243 curMax += 8223;
3244 dataSize_ += 8223;
3245 stringSize -= 8222;
3246 }
3247 else
3248 {
3249 continueIndices_.push_back(curMax);
3250 curMax += 8224;
3251 dataSize_ += 8224;
3252 stringSize -= 8223;
3253 }
3254 }
3255 dataSize_ += stringSize + 1;
3256 }
3257 }
3258 }
3259 }
3260 }
3261 return dataSize_;
3262}
3264{
3265 size_t dataSize = DataSize();
3266 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3267}
3268/************************************************************************************************************/
3270 stringsTotal_(0), streamPos_(0), firstStringPos_(0), unused_(0)
3271{
3273 dataSize_ = 2;
3274 recordSize_ = 6;
3275}
3276
3277size_t Workbook::ExtSST::Read(const char* data)
3278{
3279 Record::Read(data);
3280 LittleEndian::Read(data_, stringsTotal_, 0, 2);
3281
3282 size_t maxPortions = (dataSize_-2) / 8;
3283 streamPos_.clear();
3284 streamPos_.resize(maxPortions);
3285 firstStringPos_.clear();
3286 firstStringPos_.resize(maxPortions);
3287 unused_.clear();
3288 unused_.resize(maxPortions);
3289
3290 for (size_t i=0, npos=2; i<maxPortions; ++i)
3291 {
3292 LittleEndian::Read(data_, streamPos_[i], npos, 4);
3293 LittleEndian::Read(data_, firstStringPos_[i], npos+4, 2);
3294 LittleEndian::Read(data_, unused_[i], npos+6, 2);
3295 npos += 8;
3296 }
3297 return RecordSize();
3298}
3299
3300size_t Workbook::ExtSST::Write(char* data)
3301{
3302 data_.resize(DataSize());
3303 LittleEndian::Write(data_, stringsTotal_, 0, 2);
3304
3305 size_t maxPortions = streamPos_.size();
3306 for (size_t i=0, npos=2; i<maxPortions; ++i)
3307 {
3308 LittleEndian::Write(data_, streamPos_[i], npos, 4);
3309 LittleEndian::Write(data_, firstStringPos_[i], npos+4, 2);
3310 LittleEndian::Write(data_, unused_[i], npos+6, 2);
3311 npos += 8;
3312 }
3313 return Record::Write(data);
3314}
3315
3317{
3318 dataSize_ = 2 + streamPos_.size()*8;
3319 dataSize_ += (int)(dataSize_/8224)*4;
3320 return dataSize_;
3321}
3322
3323size_t Workbook::ExtSST::RecordSize() {return (recordSize_ = DataSize()+(int)((2+streamPos_.size()*8)/8224)*4)+4;}
3324/************************************************************************************************************/
3325
3326
3327
3328
3329/************************************************************************************************************/
3331{
3332 bof_.version_ = 1536;
3333 bof_.type_ = 16;
3334 bof_.buildIdentifier_ = 6560;
3335 bof_.buildYear_ = 1997;
3336 bof_.fileHistoryFlags_ = 49353;
3338}
3339
3340size_t Worksheet::Read(const char* data)
3341{
3342 size_t bytesRead = 0;
3343 short code;
3344 LittleEndian::Read(data, code, 0, 2);
3345 while (code != CODE::YEOF)
3346 {
3347 switch (code)
3348 {
3349 case CODE::BOF:
3350 bytesRead += bof_.Read(data+bytesRead);
3351 break;
3352
3353 case CODE::INDEX:
3354 bytesRead += index_.Read(data+bytesRead);
3355 break;
3356
3357 case CODE::DIMENSIONS:
3358 bytesRead += dimensions_.Read(data+bytesRead);
3359 break;
3360
3361 case CODE::ROW:
3362 bytesRead += cellTable_.Read(data+bytesRead);
3363 break;
3364
3365 case CODE::WINDOW2:
3366 bytesRead += window2_.Read(data+bytesRead);
3367 break;
3368
3369 default:
3370 Record rec;
3371 bytesRead += rec.Read(data+bytesRead);
3372 }
3373 LittleEndian::Read(data, code, bytesRead, 2);
3374 }
3375 bytesRead += eof_.RecordSize();
3376 return bytesRead;
3377}
3378size_t Worksheet::Write(char* data)
3379{
3380 size_t bytesWritten = 0;
3381 bytesWritten += bof_.Write(data+bytesWritten);
3382
3383 bytesWritten += index_.Write(data+bytesWritten);
3384
3385 bytesWritten += dimensions_.Write(data+bytesWritten);
3386
3387 bytesWritten += cellTable_.Write(data+bytesWritten);
3388
3389 bytesWritten += window2_.Write(data+bytesWritten);
3390
3391 bytesWritten += eof_.Write(data+bytesWritten);
3392
3393 return bytesWritten;
3394}
3396{
3397 size_t dataSize = 0;
3398 dataSize += bof_.RecordSize();
3399 dataSize += index_.RecordSize();
3400 dataSize += dimensions_.RecordSize();
3401 dataSize += cellTable_.RecordSize();
3402 dataSize += window2_.RecordSize();
3403 dataSize += eof_.RecordSize();
3404 return dataSize;
3405}
3407/************************************************************************************************************/
3408
3409/************************************************************************************************************/
3411 unused1_(0), firstUsedRowIndex_(0), firstUnusedRowIndex_(0), unused2_(0)
3412 {code_ = CODE::INDEX; dataSize_ = 16; recordSize_ = 20; DBCellPos_.resize(1);}
3413size_t Worksheet::Index::Read(const char* data)
3414{
3415 Record::Read(data);
3416 LittleEndian::Read(data_, unused1_, 0, 4);
3417 LittleEndian::Read(data_, firstUsedRowIndex_, 4, 4);
3418 LittleEndian::Read(data_, firstUnusedRowIndex_, 8, 4);
3419 LittleEndian::Read(data_, unused2_, 12, 4);
3420 size_t nm = int(firstUnusedRowIndex_ - firstUsedRowIndex_ - 1) / 32 + 1;
3421 DBCellPos_.clear();
3422 DBCellPos_.resize(nm);
3423 if (dataSize_>16)
3424 {
3425 for (size_t i=0; i<nm; ++i)
3426 {
3427 LittleEndian::Read(data_, DBCellPos_[i], 16+i*4, 4);
3428 }
3429 }
3430 return RecordSize();
3431}
3432size_t Worksheet::Index::Write(char* data)
3433{
3434 data_.resize(DataSize());
3435 LittleEndian::Write(data_, unused1_, 0, 4);
3436 LittleEndian::Write(data_, firstUsedRowIndex_, 4, 4);
3437 LittleEndian::Write(data_, firstUnusedRowIndex_, 8, 4);
3438 LittleEndian::Write(data_, unused2_, 12, 4);
3439 size_t nm = DBCellPos_.size();
3440 for (size_t i=0; i<nm; ++i)
3441 {
3442 LittleEndian::Write(data_, DBCellPos_[i], 16+i*4, 4);
3443 }
3444 return Record::Write(data);
3445}
3446size_t Worksheet::Index::DataSize() {return (dataSize_ = 16 + DBCellPos_.size()*4);}
3448{
3449 size_t dataSize = DataSize();
3450 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3451}
3452
3453/************************************************************************************************************/
3454
3455/************************************************************************************************************/
3457 firstUsedRowIndex_(0), lastUsedRowIndexPlusOne_(0),
3458 firstUsedColIndex_(0), lastUsedColIndexPlusOne_(0),
3459 unused_(0) {code_ = CODE::DIMENSIONS; dataSize_ = 14; recordSize_ = 18;}
3460size_t Worksheet::Dimensions::Read(const char* data)
3461{
3462 Record::Read(data);
3463 LittleEndian::Read(data_, firstUsedRowIndex_, 0, 4);
3464 LittleEndian::Read(data_, lastUsedRowIndexPlusOne_, 4, 4);
3465 LittleEndian::Read(data_, firstUsedColIndex_, 8, 2);
3466 LittleEndian::Read(data_, lastUsedColIndexPlusOne_, 10, 2);
3467 LittleEndian::Read(data_, unused_, 12, 2);
3468 return RecordSize();
3469}
3471{
3472 data_.resize(DataSize());
3473 LittleEndian::Write(data_, firstUsedRowIndex_, 0, 4);
3474 LittleEndian::Write(data_, lastUsedRowIndexPlusOne_, 4, 4);
3475 LittleEndian::Write(data_, firstUsedColIndex_, 8, 2);
3476 LittleEndian::Write(data_, lastUsedColIndexPlusOne_, 10, 2);
3477 LittleEndian::Write(data_, unused_, 12, 2);
3478 return Record::Write(data);
3479}
3480/************************************************************************************************************/
3481
3482/************************************************************************************************************/
3484 rowIndex_(0), colIndex_(0), XFRecordIndex_(0) {code_ = CODE::BLANK; dataSize_ = 6; recordSize_ = 10;}
3486{
3487 Record::Read(data);
3488 LittleEndian::Read(data_, rowIndex_, 0, 2);
3489 LittleEndian::Read(data_, colIndex_, 2, 2);
3490 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3491 return RecordSize();
3492}
3494{
3495 data_.resize(DataSize());
3496 LittleEndian::Write(data_, rowIndex_, 0, 2);
3497 LittleEndian::Write(data_, colIndex_, 2, 2);
3498 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3499 return Record::Write(data);
3500}
3501
3503 rowIndex_(0), colIndex_(0), XFRecordIndex_(0), value_(0), error_(0)
3506{
3507 Record::Read(data);
3508 LittleEndian::Read(data_, rowIndex_, 0, 2);
3509 LittleEndian::Read(data_, colIndex_, 2, 2);
3510 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3511 LittleEndian::Read(data_, value_, 6, 1);
3512 LittleEndian::Read(data_, error_, 7, 1);
3513 return RecordSize();
3514}
3516{
3517 data_.resize(DataSize());
3518 LittleEndian::Write(data_, rowIndex_, 0, 2);
3519 LittleEndian::Write(data_, colIndex_, 2, 2);
3520 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3521 LittleEndian::Write(data_, value_, 6, 1);
3522 LittleEndian::Write(data_, error_, 7, 1);
3523 return Record::Write(data);
3524}
3525
3527 rowIndex_(0), colIndex_(0), XFRecordIndex_(0), SSTRecordIndex_(0)
3528 {code_ = CODE::LABELSST; dataSize_ = 10; recordSize_ = 14;}
3530{
3531 Record::Read(data);
3532 LittleEndian::Read(data_, rowIndex_, 0, 2);
3533 LittleEndian::Read(data_, colIndex_, 2, 2);
3534 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3535 LittleEndian::Read(data_, SSTRecordIndex_, 6, 4);
3536 return RecordSize();
3537}
3539{
3540 data_.resize(DataSize());
3541 LittleEndian::Write(data_, rowIndex_, 0, 2);
3542 LittleEndian::Write(data_, colIndex_, 2, 2);
3543 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3544 LittleEndian::Write(data_, SSTRecordIndex_, 6, 4);
3545 return Record::Write(data);
3546}
3547
3549 rowIndex_(0), firstColIndex_(0), lastColIndex_(0)
3550 {code_ = CODE::MULBLANK; dataSize_ = 10; recordSize_ = 14;}
3552{
3553 Record::Read(data);
3554 LittleEndian::Read(data_, rowIndex_, 0, 2);
3555 LittleEndian::Read(data_, firstColIndex_, 2, 2);
3556 LittleEndian::Read(data_, lastColIndex_, dataSize_-2, 2);
3557 size_t nc = lastColIndex_ - firstColIndex_ + 1;
3558 XFRecordIndices_.clear();
3559 XFRecordIndices_.resize(nc);
3560 for (size_t i=0; i<nc; ++i)
3561 {
3562 LittleEndian::Read(data_, XFRecordIndices_[i], 4+i*2, 2);
3563 }
3564 return RecordSize();
3565}
3567{
3568 data_.resize(DataSize());
3569 LittleEndian::Write(data_, rowIndex_, 0, 2);
3570 LittleEndian::Write(data_, firstColIndex_, 2, 2);
3571 LittleEndian::Write(data_, lastColIndex_, dataSize_-2, 2);
3572 size_t nc = XFRecordIndices_.size();
3573 for (size_t i=0; i<nc; ++i)
3574 {
3575 LittleEndian::Write(data_, XFRecordIndices_[i], 4+i*2, 2);
3576 }
3577 return Record::Write(data);
3578}
3579size_t Worksheet::CellTable::RowBlock::CellBlock::MulBlank::DataSize() {return (dataSize_ = 6 + XFRecordIndices_.size()*2);}
3581{
3582 size_t dataSize = DataSize();
3583 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3584}
3585
3587 XFRecordIndex_(0), RKValue_(0) {};
3589{
3590 LittleEndian::Read(data, XFRecordIndex_, 0, 2);
3591 LittleEndian::Read(data, RKValue_, 2, 4);
3592}
3594{
3595 LittleEndian::Write(data, XFRecordIndex_, 0, 2);
3596 LittleEndian::Write(data, RKValue_, 2, 4);
3597}
3598
3602{
3603 Record::Read(data);
3604 LittleEndian::Read(data_, rowIndex_, 0, 2);
3605 LittleEndian::Read(data_, firstColIndex_, 2, 2);
3606 LittleEndian::Read(data_, lastColIndex_, dataSize_-2, 2);
3607 size_t nc = lastColIndex_ - firstColIndex_ + 1;
3608 XFRK_.clear();
3609 XFRK_.resize(nc);
3610 for (size_t i=0; i<nc; ++i)
3611 {
3612 XFRK_[i].Read(&*(data_.begin())+4+i*6);
3613 }
3614 return RecordSize();
3615}
3617{
3618 data_.resize(DataSize());
3619 LittleEndian::Write(data_, rowIndex_, 0, 2);
3620 LittleEndian::Write(data_, firstColIndex_, 2, 2);
3621 LittleEndian::Write(data_, lastColIndex_, dataSize_-2, 2);
3622 size_t nc = XFRK_.size();
3623 for (size_t i=0; i<nc; ++i)
3624 {
3625 XFRK_[i].Write(&*(data_.begin())+4+i*6);
3626 }
3627 return Record::Write(data);
3628}
3629size_t Worksheet::CellTable::RowBlock::CellBlock::MulRK::DataSize() {return (dataSize_ = 6 + XFRK_.size()*6);}
3631{
3632 size_t dataSize = DataSize();
3633 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3634}
3635
3637 rowIndex_(0), colIndex_(0), XFRecordIndex_(0), value_(0) {code_ = CODE::NUMBER; dataSize_ = 14; recordSize_ = 18;}
3639{
3640 Record::Read(data);
3641 LittleEndian::Read(data_, rowIndex_, 0, 2);
3642 LittleEndian::Read(data_, colIndex_, 2, 2);
3643 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3644 long long value;
3645 LittleEndian::Read(data_, value, 6, 8);
3646 intdouble_.intvalue_ = value;
3647 value_ = intdouble_.doublevalue_;
3648 return RecordSize();
3649}
3651{
3652 data_.resize(DataSize());
3653 LittleEndian::Write(data_, rowIndex_, 0, 2);
3654 LittleEndian::Write(data_, colIndex_, 2, 2);
3655 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3656 intdouble_.doublevalue_ = value_;
3657 long long value = intdouble_.intvalue_;
3658 LittleEndian::Write(data_, value, 6, 8);
3659 return Record::Write(data);
3660}
3661
3663 rowIndex_(0), colIndex_(0), XFRecordIndex_(0), value_(0) {code_ = CODE::RK; dataSize_ = 10; recordSize_ = 14;}
3665{
3666 Record::Read(data);
3667 LittleEndian::Read(data_, rowIndex_, 0, 2);
3668 LittleEndian::Read(data_, colIndex_, 2, 2);
3669 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3670 LittleEndian::Read(data_, value_, 6, 4);
3671 return RecordSize();
3672}
3674{
3675 data_.resize(DataSize());
3676 LittleEndian::Write(data_, rowIndex_, 0, 2);
3677 LittleEndian::Write(data_, colIndex_, 2, 2);
3678 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3679 LittleEndian::Write(data_, value_, 6, 4);
3680 return Record::Write(data);
3681}
3682
3684 rowIndex_(0), colIndex_(0), XFRecordIndex_(0), options_(0), unused_(0), type_(-1)
3685 {code_ = CODE::FORMULA; dataSize_ = 18; recordSize_ = 22;}
3687{
3688 Record::Read(data);
3689 LittleEndian::Read(data_, rowIndex_, 0, 2);
3690 LittleEndian::Read(data_, colIndex_, 2, 2);
3691 LittleEndian::Read(data_, XFRecordIndex_, 4, 2);
3692 LittleEndian::ReadString(data_, result_, 6, 8);
3693 LittleEndian::Read(data_, options_, 14, 2);
3694 LittleEndian::Read(data_, unused_, 16, 2);
3695 RPNtoken_.clear();
3696 RPNtoken_.resize(dataSize_-18);
3697 LittleEndian::ReadString(data_, &*(RPNtoken_.begin()), 18, dataSize_-18);
3698
3699 size_t offset = dataSize_ + 4;
3700 short code;
3701 LittleEndian::Read(data, code, offset, 2);
3702 switch (code)
3703 {
3704 case CODE::ARRAY:
3705 type_ = code;
3706 array_.Read(data+offset);
3707 offset += array_.RecordSize();
3708 break;
3709
3710 case CODE::SHRFMLA:
3711 type_ = code;
3712 shrfmla_.Read(data+offset);
3713 offset += shrfmla_.RecordSize();
3714 break;
3715
3716 case CODE::SHRFMLA1:
3717 type_ = code;
3718 shrfmla1_.Read(data+offset);
3719 offset += shrfmla1_.RecordSize();
3720 break;
3721
3722 case CODE::TABLE:
3723 type_ = code;
3724 table_.Read(data+offset);
3725 offset += table_.RecordSize();
3726 break;
3727 }
3728 LittleEndian::Read(data, code, offset, 2);
3729 if (code == CODE::STRING) string_.Read(data+offset);
3730 return RecordSize();
3731}
3733{
3734 data_.resize(DataSize());
3735 LittleEndian::Write(data_, rowIndex_, 0, 2);
3736 LittleEndian::Write(data_, colIndex_, 2, 2);
3737 LittleEndian::Write(data_, XFRecordIndex_, 4, 2);
3738 LittleEndian::WriteString(data_, result_, 6, 8);
3739 LittleEndian::Write(data_, options_, 14, 2);
3740 LittleEndian::Write(data_, unused_, 16, 2);
3741 LittleEndian::WriteString(data_, &*(RPNtoken_.begin()), 18, RPNtoken_.size());
3742 Record::Write(data);
3743
3744 size_t offset = dataSize_ + 4;
3745 switch (type_)
3746 {
3747 case CODE::ARRAY:
3748 array_.Write(data+offset);
3749 offset += array_.RecordSize();
3750 break;
3751
3752 case CODE::SHRFMLA:
3753 shrfmla_.Write(data+offset);
3754 offset += shrfmla_.RecordSize();
3755 break;
3756
3757 case CODE::SHRFMLA1:
3758 shrfmla1_.Write(data+offset);
3759 offset += shrfmla1_.RecordSize();
3760 break;
3761
3762 case CODE::TABLE:
3763 table_.Write(data+offset);
3764 offset += table_.RecordSize();
3765 break;
3766 }
3767 if (string_.DataSize() != 0) string_.Write(data+offset);
3768 return RecordSize();
3769}
3770size_t Worksheet::CellTable::RowBlock::CellBlock::Formula::DataSize() {return (dataSize_ = 18 + RPNtoken_.size());}
3772{
3773 size_t dataSize = DataSize();
3774 recordSize_ = dataSize + 4*(dataSize/8224 + 1);
3775
3776 switch (type_)
3777 {
3778 case CODE::ARRAY:
3779 recordSize_ += array_.RecordSize();
3780 break;
3781
3782 case CODE::SHRFMLA:
3783 recordSize_ += shrfmla_.RecordSize();
3784 break;
3785
3786 case CODE::SHRFMLA1:
3787 recordSize_ += shrfmla1_.RecordSize();
3788 break;
3789
3790 case CODE::TABLE:
3791 recordSize_ += table_.RecordSize();
3792 break;
3793 }
3794 if (string_.DataSize() != 0) recordSize_ += string_.RecordSize();
3795 return (recordSize_);
3796}
3797
3799 firstRowIndex_(0), lastRowIndex_(0), firstColIndex_(0), lastColIndex_(0),
3800 options_(0), unused_(0)
3801 {code_ = CODE::ARRAY; dataSize_ = 12; recordSize_ = 16;}
3803{
3804 Record::Read(data);
3805 LittleEndian::Read(data_, firstRowIndex_, 0, 2);
3806 LittleEndian::Read(data_, lastRowIndex_, 2, 2);
3807 LittleEndian::Read(data_, firstColIndex_, 4, 1);
3808 LittleEndian::Read(data_, lastColIndex_, 5, 1);
3811 formula_.clear();
3812 formula_.resize(dataSize_-12);
3813 LittleEndian::ReadString(data_, &*(formula_.begin()), 12, dataSize_-12);
3814 return RecordSize();
3815}
3817{
3818 data_.resize(DataSize());
3819 LittleEndian::Write(data_, firstRowIndex_, 0, 2);
3820 LittleEndian::Write(data_, lastRowIndex_, 2, 2);
3821 LittleEndian::Write(data_, firstColIndex_, 4, 1);
3822 LittleEndian::Write(data_, lastColIndex_, 5, 1);
3825 LittleEndian::WriteString(data_, &*(formula_.begin()), 12, formula_.size());
3826 return Record::Write(data);
3827}
3830{
3831 size_t dataSize = DataSize();
3832 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3833}
3834
3836 firstRowIndex_(0), lastRowIndex_(0), firstColIndex_(0), lastColIndex_(0),
3837 unused_(0)
3840{
3841 Record::Read(data);
3842 LittleEndian::Read(data_, firstRowIndex_, 0, 2);
3843 LittleEndian::Read(data_, lastRowIndex_, 2, 2);
3844 LittleEndian::Read(data_, firstColIndex_, 4, 1);
3845 LittleEndian::Read(data_, lastColIndex_, 5, 1);
3847 formula_.clear();
3848 formula_.resize(dataSize_-8);
3850 return RecordSize();
3851}
3853{
3854 data_.resize(DataSize());
3855 LittleEndian::Write(data_, firstRowIndex_, 0, 2);
3856 LittleEndian::Write(data_, lastRowIndex_, 2, 2);
3857 LittleEndian::Write(data_, firstColIndex_, 4, 1);
3858 LittleEndian::Write(data_, lastColIndex_, 5, 1);
3860 LittleEndian::WriteString(data_, &*(formula_.begin()), 8, formula_.size());
3861 return Record::Write(data);
3862}
3865{
3866 size_t dataSize = DataSize();
3867 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3868}
3869
3871 firstRowIndex_(0), lastRowIndex_(0), firstColIndex_(0), lastColIndex_(0),
3872 unused_(0)
3875{
3876 Record::Read(data);
3877 LittleEndian::Read(data_, firstRowIndex_, 0, 2);
3878 LittleEndian::Read(data_, lastRowIndex_, 2, 2);
3879 LittleEndian::Read(data_, firstColIndex_, 4, 1);
3880 LittleEndian::Read(data_, lastColIndex_, 5, 1);
3882 formula_.clear();
3883 formula_.resize(dataSize_-8);
3885 return RecordSize();
3886}
3888{
3889 data_.resize(DataSize());
3890 LittleEndian::Write(data_, firstRowIndex_, 0, 2);
3891 LittleEndian::Write(data_, lastRowIndex_, 2, 2);
3892 LittleEndian::Write(data_, firstColIndex_, 4, 1);
3893 LittleEndian::Write(data_, lastColIndex_, 5, 1);
3895 LittleEndian::WriteString(data_, &*(formula_.begin()), 8, formula_.size());
3896 return Record::Write(data);
3897}
3900{
3901 size_t dataSize = DataSize();
3902 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3903}
3904
3906 firstRowIndex_(0), lastRowIndex_(0), firstColIndex_(0), lastColIndex_(0), options_(0),
3907 inputCellRowIndex_(0), inputCellColIndex_(0),
3908 inputCellColumnInputRowIndex_(0), inputCellColumnInputColIndex_(0)
3909 {code_ = CODE::TABLE; dataSize_ = 16; recordSize_ = 20;}
3911{
3912 Record::Read(data);
3913 LittleEndian::Read(data_, firstRowIndex_, 0, 2);
3914 LittleEndian::Read(data_, lastRowIndex_, 2, 2);
3915 LittleEndian::Read(data_, firstColIndex_, 4, 1);
3916 LittleEndian::Read(data_, lastColIndex_, 5, 1);
3918 LittleEndian::Read(data_, inputCellRowIndex_, 8, 2);
3919 LittleEndian::Read(data_, inputCellColIndex_, 10, 2);
3920 LittleEndian::Read(data_, inputCellColumnInputRowIndex_, 12, 2);
3921 LittleEndian::Read(data_, inputCellColumnInputColIndex_, 14, 2);
3922 return RecordSize();
3923}
3925{
3926 data_.resize(DataSize());
3927 LittleEndian::Write(data_, firstRowIndex_, 0, 2);
3928 LittleEndian::Write(data_, lastRowIndex_, 2, 2);
3929 LittleEndian::Write(data_, firstColIndex_, 4, 1);
3930 LittleEndian::Write(data_, lastColIndex_, 5, 1);
3932 LittleEndian::Write(data_, inputCellRowIndex_, 8, 2);
3933 LittleEndian::Write(data_, inputCellColIndex_, 10, 2);
3934 LittleEndian::Write(data_, inputCellColumnInputRowIndex_, 12, 2);
3935 LittleEndian::Write(data_, inputCellColumnInputColIndex_, 14, 2);
3936 return Record::Write(data);
3937}
3938
3942{
3943 Record::Read(data);
3944 string_.clear();
3945 string_.resize(dataSize_);
3947 return RecordSize();
3948}
3950{
3951 data_.resize(DataSize());
3952 LittleEndian::WriteString(data_, &*(string_.begin()), 0, string_.size());
3953 return Record::Write(data);
3954}
3957{
3958 size_t dataSize = DataSize();
3959 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
3960}
3961/************************************************************************************************************/
3962
3963/************************************************************************************************************/
3965 rowIndex_(0), firstCellColIndex_(0), lastCellColIndexPlusOne_(0), height_(255),
3966 unused1_(0), unused2_(0), options_(0) {code_ = CODE::ROW; dataSize_ = 16; recordSize_ = 20;}
3968{
3969 Record::Read(data);
3970 LittleEndian::Read(data_, rowIndex_, 0, 2);
3971 LittleEndian::Read(data_, firstCellColIndex_, 2, 2);
3972 LittleEndian::Read(data_, lastCellColIndexPlusOne_, 4, 2);
3973 LittleEndian::Read(data_, height_, 6, 2);
3974 LittleEndian::Read(data_, unused1_, 8, 2);
3975 LittleEndian::Read(data_, unused2_, 10, 2);
3976 LittleEndian::Read(data_, options_, 12, 4);
3977 return RecordSize();
3978}
3980{
3981 data_.resize(DataSize());
3982 LittleEndian::Write(data_, rowIndex_, 0, 2);
3983 LittleEndian::Write(data_, firstCellColIndex_, 2, 2);
3984 LittleEndian::Write(data_, lastCellColIndexPlusOne_, 4, 2);
3985 LittleEndian::Write(data_, height_, 6, 2);
3986 LittleEndian::Write(data_, unused1_, 8, 2);
3987 LittleEndian::Write(data_, unused2_, 10, 2);
3988 LittleEndian::Write(data_, options_, 12, 4);
3989 return Record::Write(data);
3990}
3991/************************************************************************************************************/
3992
3993/************************************************************************************************************/
3995 type_(-1), normalType_(true) {};
3998{
3999 size_t bytesRead = 0;
4000 LittleEndian::Read(data, type_, 0, 2);
4001 switch (type_)
4002 {
4003 case CODE::BLANK:
4004 bytesRead += blank_.Read(data);
4005 break;
4006
4007 case CODE::BOOLERR:
4008 bytesRead += boolerr_.Read(data);
4009 break;
4010
4011 case CODE::LABELSST:
4012 bytesRead += labelsst_.Read(data);
4013 break;
4014
4015 case CODE::MULBLANK:
4016 bytesRead += mulblank_.Read(data);
4017 break;
4018
4019 case CODE::MULRK:
4020 bytesRead += mulrk_.Read(data);
4021 break;
4022
4023 case CODE::NUMBER:
4024 bytesRead += number_.Read(data);
4025 break;
4026
4027 case CODE::RK:
4028 bytesRead += rk_.Read(data);
4029 break;
4030
4031 case CODE::FORMULA:
4032 bytesRead += formula_.Read(data);
4033 break;
4034 }
4035 return bytesRead;
4036}
4038{
4039 size_t bytesWritten = 0;
4040 switch (type_)
4041 {
4042 case CODE::BLANK:
4043 bytesWritten += blank_.Write(data);
4044 break;
4045
4046 case CODE::BOOLERR:
4047 bytesWritten += boolerr_.Write(data);
4048 break;
4049
4050 case CODE::LABELSST:
4051 bytesWritten += labelsst_.Write(data);
4052 break;
4053
4054 case CODE::MULBLANK:
4055 bytesWritten += mulblank_.Write(data);
4056 break;
4057
4058 case CODE::MULRK:
4059 bytesWritten += mulrk_.Write(data);
4060 break;
4061
4062 case CODE::NUMBER:
4063 bytesWritten += number_.Write(data);
4064 break;
4065
4066 case CODE::RK:
4067 bytesWritten += rk_.Write(data);
4068 break;
4069
4070 case CODE::FORMULA:
4071 bytesWritten += formula_.Write(data);
4072 break;
4073 }
4074 return bytesWritten;
4075}
4077{
4078 switch (type_)
4079 {
4080 case CODE::BLANK:
4081 return blank_.DataSize();
4082
4083 case CODE::BOOLERR:
4084 return boolerr_.DataSize();
4085
4086 case CODE::LABELSST:
4087 return labelsst_.DataSize();
4088
4089 case CODE::MULBLANK:
4090 return mulblank_.DataSize();
4091
4092 case CODE::MULRK:
4093 return mulrk_.DataSize();
4094
4095 case CODE::NUMBER:
4096 return number_.DataSize();
4097
4098 case CODE::RK:
4099 return rk_.DataSize();
4100
4101 case CODE::FORMULA:
4102 return formula_.DataSize();
4103 }
4104 abort();
4105}
4107{
4108 switch (type_)
4109 {
4110 case CODE::BLANK:
4111 return blank_.RecordSize();
4112
4113 case CODE::BOOLERR:
4114 return boolerr_.RecordSize();
4115
4116 case CODE::LABELSST:
4117 return labelsst_.RecordSize();
4118
4119 case CODE::MULBLANK:
4120 return mulblank_.RecordSize();
4121
4122 case CODE::MULRK:
4123 return mulrk_.RecordSize();
4124
4125 case CODE::NUMBER:
4126 return number_.RecordSize();
4127
4128 case CODE::RK:
4129 return rk_.RecordSize();
4130
4131 case CODE::FORMULA:
4132 return formula_.RecordSize();
4133 }
4134 abort();
4135}
4137{
4138 switch (type_)
4139 {
4140 case CODE::BLANK:
4141 return blank_.rowIndex_;
4142
4143 case CODE::BOOLERR:
4144 return boolerr_.rowIndex_;
4145
4146 case CODE::LABELSST:
4147 return labelsst_.rowIndex_;
4148
4149 case CODE::MULBLANK:
4150 return mulblank_.rowIndex_;
4151
4152 case CODE::MULRK:
4153 return mulrk_.rowIndex_;
4154
4155 case CODE::NUMBER:
4156 return number_.rowIndex_;
4157
4158 case CODE::RK:
4159 return rk_.rowIndex_;
4160
4161 case CODE::FORMULA:
4162 return formula_.rowIndex_;
4163 }
4164 abort();
4165}
4167{
4168 switch (type_)
4169 {
4170 case CODE::BLANK:
4171 return blank_.colIndex_;
4172
4173 case CODE::BOOLERR:
4174 return boolerr_.colIndex_;
4175
4176 case CODE::LABELSST:
4177 return labelsst_.colIndex_;
4178
4179 case CODE::MULBLANK:
4180 return mulblank_.firstColIndex_;
4181
4182 case CODE::MULRK:
4183 return mulrk_.firstColIndex_;
4184
4185 case CODE::NUMBER:
4186 return number_.colIndex_;
4187
4188 case CODE::RK:
4189 return rk_.colIndex_;
4190
4191 case CODE::FORMULA:
4192 return formula_.colIndex_;
4193 }
4194 abort();
4195}
4196
4197/************************************************************************************************************/
4198
4199/************************************************************************************************************/
4201 firstRowOffset_(0) {code_ = CODE::DBCELL; dataSize_ = 4; recordSize_ = 8;}
4203{
4204 Record::Read(data);
4205 LittleEndian::Read(data_, firstRowOffset_, 0, 4);
4206 size_t nm = (dataSize_-4) / 2;
4207 offsets_.clear();
4208 offsets_.resize(nm);
4209 for (size_t i=0; i<nm; ++i)
4210 {
4211 LittleEndian::Read(data_, offsets_[i], 4+i*2, 2);
4212 }
4213 return RecordSize();
4214}
4216{
4217 data_.resize(DataSize());
4218 LittleEndian::Write(data_, firstRowOffset_, 0, 4);
4219 size_t nm = offsets_.size();
4220 for (size_t i=0; i<nm; ++i)
4221 {
4222 LittleEndian::Write(data_, offsets_[i], 4+i*2, 2);
4223 }
4224 return Record::Write(data);
4225}
4226size_t Worksheet::CellTable::RowBlock::DBCell::DataSize() {return (dataSize_ = 4+offsets_.size()*2);}
4228{
4229 size_t dataSize = DataSize();
4230 return (recordSize_ = dataSize + 4*(dataSize/8224 + 1));
4231}
4232/************************************************************************************************************/
4233
4234/************************************************************************************************************/
4236{
4237 size_t bytesRead = 0;
4238 short code;
4239 LittleEndian::Read(data, code, 0, 2);
4240 Row row;
4241 CellBlock cellBlock;
4242 cellBlocks_.reserve(1000);
4243 while (code != CODE::DBCELL)
4244 {
4245 switch (code)
4246 {
4247 case CODE::ROW:
4248 rows_.push_back(row);
4249 bytesRead += rows_.back().Read(data+bytesRead);
4250 break;
4251
4252 case CODE::BLANK:
4253 case CODE::BOOLERR:
4254 case CODE::LABELSST:
4255 case CODE::MULBLANK:
4256 case CODE::MULRK:
4257 case CODE::NUMBER:
4258 case CODE::RK:
4259 case CODE::FORMULA:
4260 cellBlocks_.push_back(cellBlock);
4261 if (cellBlocks_.size()%1000==0) cellBlocks_.reserve(cellBlocks_.size()+1000);
4262 bytesRead += cellBlocks_[cellBlocks_.size()-1].Read(data+bytesRead);
4263 break;
4264
4265 default:
4266 Record rec;
4267 bytesRead += rec.Read(data+bytesRead);
4268 }
4269 LittleEndian::Read(data, code, bytesRead, 2);
4270 }
4271 bytesRead += dbcell_.Read(data+bytesRead);
4272 return bytesRead;
4273}
4275{
4276 size_t bytesWritten = 0;
4277 size_t maxRows = rows_.size();
4278 {for (size_t i=0; i<maxRows; ++i)
4279 {
4280 bytesWritten += rows_[i].Write(data+bytesWritten);
4281 }}
4282
4283 size_t maxCellBlocks = cellBlocks_.size();
4284 {for (size_t i=0; i<maxCellBlocks; ++i)
4285 {
4286 bytesWritten += cellBlocks_[i].Write(data+bytesWritten);
4287 }}
4288
4289 bytesWritten += dbcell_.Write(data+bytesWritten);
4290 return bytesWritten;
4291}
4293{
4294 size_t dataSize = 0;
4295 size_t maxRows = rows_.size();
4296 {for (size_t i=0; i<maxRows; ++i) dataSize += rows_[i].RecordSize();}
4297
4298 size_t maxCellBlocks = cellBlocks_.size();
4299 {for (size_t i=0; i<maxCellBlocks; ++i) dataSize += cellBlocks_[i].RecordSize();}
4300
4301 dataSize += dbcell_.RecordSize();
4302 return dataSize;
4303}
4305/************************************************************************************************************/
4306
4307/************************************************************************************************************/
4308size_t Worksheet::CellTable::Read(const char* data)
4309{
4310 size_t bytesRead = 0;
4311
4312 short code;
4313 LittleEndian::Read(data, code, 0, 2);
4314 RowBlock rowBlock;
4315 rowBlocks_.reserve(1000);
4316 while (code == CODE::ROW)
4317 {
4318 rowBlocks_.push_back(rowBlock);
4319 bytesRead += rowBlocks_.back().Read(data+bytesRead);
4320 LittleEndian::Read(data, code, bytesRead, 2);
4321 }
4322 return bytesRead;
4323}
4325{
4326 size_t bytesWritten = 0;
4327 size_t maxRowBlocks_ = rowBlocks_.size();
4328 for (size_t i=0; i<maxRowBlocks_; ++i)
4329 {
4330 bytesWritten += rowBlocks_[i].Write(data+bytesWritten);
4331 }
4332 return bytesWritten;
4333}
4335{
4336 size_t dataSize = 0;
4337 size_t maxRowBlocks_ = rowBlocks_.size();
4338 for (size_t i=0; i<maxRowBlocks_; ++i) dataSize += rowBlocks_[i].RecordSize();
4339 return dataSize;
4340}
4342/************************************************************************************************************/
4343
4344/************************************************************************************************************/
4346 options_(1718), firstVisibleRowIndex_(0), firstVisibleColIndex_(0), gridLineColourIndex_(64),
4347 unused1_(0), magnificationFactorPageBreakPreview_(0), magnificationFactorNormalView_(0), unused2_(0)
4348 {code_ = CODE::WINDOW2; dataSize_ = 18; recordSize_ = 22;}
4349
4350size_t Worksheet::Window2::Read(const char* data)
4351{
4352 Record::Read(data);
4353 LittleEndian::Read(data_, options_, 0, 2);
4354 LittleEndian::Read(data_, firstVisibleRowIndex_, 2, 2);
4355 LittleEndian::Read(data_, firstVisibleColIndex_, 4, 2);
4356 LittleEndian::Read(data_, gridLineColourIndex_, 6, 2);
4357 LittleEndian::Read(data_, unused1_, 8, 2);
4358 LittleEndian::Read(data_, magnificationFactorPageBreakPreview_, 10, 2);
4359 LittleEndian::Read(data_, magnificationFactorNormalView_, 12, 2);
4360 LittleEndian::Read(data_, unused2_, 14, 4);
4361 return RecordSize();
4362}
4364{
4365 data_.resize(DataSize());
4366 LittleEndian::Write(data_, options_, 0, 2);
4367 LittleEndian::Write(data_, firstVisibleRowIndex_, 2, 2);
4368 LittleEndian::Write(data_, firstVisibleColIndex_, 4, 2);
4369 LittleEndian::Write(data_, gridLineColourIndex_, 6, 2);
4370 LittleEndian::Write(data_, unused1_, 8, 2);
4371 LittleEndian::Write(data_, magnificationFactorPageBreakPreview_, 10, 2);
4372 LittleEndian::Write(data_, magnificationFactorNormalView_, 12, 2);
4373 LittleEndian::Write(data_, unused2_, 14, 4);
4374 return Record::Write(data);
4375}
4376/************************************************************************************************************/
4377
4378/************************************************************************************************************/
4379// Returns true if the supplied rk value contains an integer.
4380bool IsRKValueAnInteger(int rkValue)
4381{
4382 return (rkValue & 2);
4383}
4384
4385// Returns true if the supplied rk value contains a double.
4386bool IsRKValueADouble(int rkValue)
4387{
4388 return !(rkValue & 2);
4389}
4390
4391// Convert a rk value to a double.
4392double GetDoubleFromRKValue(int rkValue)
4393{
4394 union
4395 {
4396 long long intvalue_;
4397 double doublevalue_;
4398 } intdouble;
4399
4400 bool isMultiplied = rkValue & 1;
4401 rkValue >>= 2;
4402 intdouble.intvalue_ = rkValue;
4403 intdouble.intvalue_ <<= 34;
4404 if (isMultiplied) intdouble.doublevalue_ *= 0.01;
4405 return intdouble.doublevalue_;
4406}
4407
4408// Convert a rk value to an integer.
4410{
4411 bool isMultiplied = rkValue & 1;
4412 rkValue >>= 2;
4413 if (isMultiplied) rkValue *= 0.01;
4414 return rkValue;
4415}
4416
4417// Convert a double to a rk value.
4418int GetRKValueFromDouble(double value)
4419{
4420 union
4421 {
4422 long long intvalue_;
4423 double doublevalue_;
4424 } intdouble;
4425
4426 bool isMultiplied = false;
4427 int testVal1 = value;
4428 testVal1 *= 100;
4429 int testVal2 = value * 100;
4430 if (testVal1!=testVal2)
4431 {
4432 isMultiplied = true;
4433 value *= 100;
4434 }
4435
4436 intdouble.doublevalue_ = value;
4437 intdouble.intvalue_ >>= 34;
4438
4439 int rkValue = intdouble.intvalue_;
4440 rkValue <<= 2;
4441 rkValue |= isMultiplied;
4442 return rkValue;
4443}
4444
4445// Convert an integer to a rk value.
4447{
4448 value <<= 2;
4449 value |= 2;
4450 return value;
4451}
4452
4453// Returns true if the supplied double can be stored as a rk value.
4454bool CanStoreAsRKValue(double value)
4455{
4456 int testVal1 = value * 100;
4457 testVal1 *= 100;
4458 int testVal2 = value * 10000;
4459 if (testVal1!=testVal2) return false;
4460 else return true;
4461}
4462
4463/************************************************************************************************************/
4464
4465/************************************************************************************************************/
4467BasicExcel::BasicExcel(const char* filename)
4468{
4469 Load(filename);
4470}
4471
4473{
4474 if (file_.IsOpen()) file_.Close();
4475}
4476
4477// Create a new Excel workbook with a given number of spreadsheets (Minimum 1)
4478void BasicExcel::New(int sheets)
4479{
4480 workbook_ = Workbook();
4481 worksheets_.clear();
4482
4483 workbook_.fonts_.resize(4);
4484 workbook_.XFs_.resize(21);
4485 workbook_.styles_.resize(6);
4486 workbook_.boundSheets_.resize(1);
4487 worksheets_.resize(1);
4488 UpdateYExcelWorksheet();
4489
4490 for (int i=0; i<sheets-1; ++i) AddWorksheet();
4491}
4492
4493// Load an Excel workbook from a file.
4494bool BasicExcel::Load(const char* filename)
4495{
4496 if (file_.IsOpen()) file_.Close();
4497 if (file_.Open(filename))
4498 {
4499 workbook_ = Workbook();
4500 worksheets_.clear();
4501
4502 vector<char> data;
4503 file_.ReadFile("Workbook", data);
4504 Read(&*(data.begin()), data.size());
4505 UpdateYExcelWorksheet();
4506 return true;
4507 }
4508 else return false;
4509}
4510
4511// Save current Excel workbook to opened file.
4513{
4514 if (file_.IsOpen())
4515 {
4516 // Prepare Raw Worksheets for saving.
4517 UpdateWorksheets();
4518
4519 AdjustStreamPositions();
4520
4521 // Calculate bytes needed for a workbook.
4522 size_t minBytes = workbook_.RecordSize();
4523 size_t maxWorkSheets = worksheets_.size();
4524 for (size_t i=0; i<maxWorkSheets; ++i)
4525 {
4526 minBytes += worksheets_[i].RecordSize();
4527 }
4528
4529 // Create new workbook.
4530 vector<char> data(minBytes,0);
4531 Write(&*(data).begin());
4532
4533 if (file_.WriteFile("Workbook", data, data.size())!=CompoundFile::SUCCESS) return false;
4534 return true;
4535 }
4536 else return false;
4537}
4538
4539// Save current Excel workbook to a file.
4540bool BasicExcel::SaveAs(const char* filename)
4541{
4542 if (file_.IsOpen()) file_.Close();
4543
4544 if (!file_.Create(filename)) return false;
4545 if (file_.MakeFile("Workbook")!=CompoundFile::SUCCESS) return false;
4546
4547 return Save();
4548}
4549
4550// Total number of Excel worksheets in current Excel workbook.
4552{
4553 return worksheets_.size();
4554}
4555
4556// Get a pointer to an Excel worksheet at the given index.
4557// Index starts from 0.
4558// Returns 0 if index is invalid.
4560{
4561 return &(yesheets_[sheetIndex]);
4562}
4563
4564// Get a pointer to an Excel worksheet that has given ANSI name.
4565// Returns 0 if there is no Excel worksheet with the given name.
4567{
4568 size_t maxWorksheets = yesheets_.size();
4569 for (size_t i=0; i<maxWorksheets; ++i)
4570 {
4571 if (workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
4572 if (strcmp(name, workbook_.boundSheets_[i].name_.name_) == 0) return &(yesheets_[i]);
4573 }
4574 return 0;
4575}
4576
4577// Get a pointer to an Excel worksheet that has given Unicode name.
4578// Returns 0 if there is no Excel worksheet with the given name.
4580{
4581 size_t maxWorksheets = yesheets_.size();
4582 for (size_t i=0; i<maxWorksheets; ++i)
4583 {
4584 if (!(workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
4585 if (wcscmp(name, workbook_.boundSheets_[i].name_.wname_) == 0) return &(yesheets_[i]);
4586 }
4587 return 0;
4588}
4589
4590// Add a new Excel worksheet to the given index.
4591// Name given to worksheet is SheetX, where X is a number which starts from 1.
4592// Index starts from 0.
4593// Worksheet is added to the last position if sheetIndex == -1.
4594// Returns a pointer to the worksheet if successful, 0 if otherwise.
4596{
4597 size_t sheetNo = yesheets_.size() + 1;
4598 BasicExcelWorksheet* yesheet = 0;
4599 do
4600 {
4601 char sname[50];
4602 sprintf(sname, "Sheet%d", sheetNo++);
4603 yesheet = AddWorksheet(sname, sheetIndex);
4604 } while (!yesheet);
4605 return yesheet;
4606}
4607
4608// Add a new Excel worksheet with given ANSI name to the given index.
4609// Index starts from 0.
4610// Worksheet is added to the last position if sheetIndex == -1.
4611// Returns a pointer to the worksheet if successful, 0 if otherwise.
4613{
4614 size_t maxWorksheets = yesheets_.size();
4615 for (size_t i=0; i<maxWorksheets; ++i)
4616 {
4617 if (workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
4618 if (strcmp(name, workbook_.boundSheets_[i].name_.name_) == 0) return 0;
4619 }
4620
4621 Workbook::BoundSheet* boundSheet;
4622 Worksheet* worksheet;
4623 BasicExcelWorksheet* yesheet;
4624 if (sheetIndex == -1)
4625 {
4626 workbook_.boundSheets_.push_back(Workbook::BoundSheet());
4627 worksheets_.push_back(Worksheet());
4628 yesheets_.push_back(BasicExcelWorksheet(this, worksheets_.size()-1));
4629 boundSheet = &(workbook_.boundSheets_.back());
4630 worksheet = &(worksheets_.back());
4631 yesheet = &(yesheets_.back());
4632 }
4633 else
4634 {
4635 boundSheet = &*(workbook_.boundSheets_.insert(workbook_.boundSheets_.begin()+sheetIndex, Workbook::BoundSheet()));
4636 worksheet = &*(worksheets_.insert(worksheets_.begin()+sheetIndex, Worksheet()));
4637 yesheet = &*(yesheets_.insert(yesheets_.begin()+sheetIndex, BasicExcelWorksheet(this, sheetIndex)));
4638 size_t maxSheets = worksheets_.size();
4639 for (size_t i=sheetIndex+1; i<maxSheets; ++i)
4640 {
4641 yesheets_[i].sheetIndex_ = i;
4642 }
4643 }
4644 boundSheet->name_ = name;
4645 worksheet->window2_.options_ &= ~0x200;
4646 return yesheet;
4647}
4648
4649// Add a new Excel worksheet with given Unicode name to the given index.
4650// Index starts from 0.
4651// Worksheet is added to the last position if sheetIndex == -1.
4652// Returns a pointer to the worksheet if successful, 0 if otherwise.
4653BasicExcelWorksheet* BasicExcel::AddWorksheet(const wchar_t* name, int sheetIndex)
4654{
4655 size_t maxWorksheets = yesheets_.size();
4656 for (size_t i=0; i<maxWorksheets; ++i)
4657 {
4658 if (!(workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
4659 if (wcscmp(name, workbook_.boundSheets_[i].name_.wname_) == 0) return 0;
4660 }
4661
4662 Workbook::BoundSheet* boundSheet;
4663 Worksheet* worksheet;
4664 BasicExcelWorksheet* yesheet;
4665 if (sheetIndex == -1)
4666 {
4667 workbook_.boundSheets_.push_back(Workbook::BoundSheet());
4668 worksheets_.push_back(Worksheet());
4669 yesheets_.push_back(BasicExcelWorksheet(this, worksheets_.size()-1));
4670 boundSheet = &(workbook_.boundSheets_.back());
4671 worksheet = &(worksheets_.back());
4672 yesheet = &(yesheets_.back());
4673 }
4674 else
4675 {
4676 boundSheet = &*(workbook_.boundSheets_.insert(workbook_.boundSheets_.begin()+sheetIndex, Workbook::BoundSheet()));
4677 worksheet = &*(worksheets_.insert(worksheets_.begin()+sheetIndex, Worksheet()));
4678 yesheet = &*(yesheets_.insert(yesheets_.begin()+sheetIndex, BasicExcelWorksheet(this, sheetIndex)));
4679 size_t maxSheets = worksheets_.size();
4680 for (size_t i=sheetIndex+1; i<maxSheets; ++i)
4681 {
4682 yesheets_[i].sheetIndex_ = i;
4683 }
4684 }
4685 boundSheet->name_ = name;
4686 worksheet->window2_.options_ &= ~0x200;
4687 return yesheet;
4688}
4689
4690// Delete an Excel worksheet at the given index.
4691// Index starts from 0.
4692// Returns true if successful, false if otherwise.
4693bool BasicExcel::DeleteWorksheet(size_t sheetIndex)
4694{
4695 if (sheetIndex<workbook_.boundSheets_.size())
4696 {
4697 workbook_.boundSheets_.erase(workbook_.boundSheets_.begin()+sheetIndex);
4698 worksheets_.erase(worksheets_.begin()+sheetIndex);
4699 yesheets_.erase(yesheets_.begin()+sheetIndex);
4700 return true;
4701 }
4702 else return false;
4703}
4704
4705// Delete an Excel worksheet that has given ANSI name.
4706// Returns true if successful, false if otherwise.
4708{
4709 size_t maxWorksheets = yesheets_.size();
4710 for (size_t i=0; i<maxWorksheets; ++i)
4711 {
4712 if (workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
4713 if (strcmp(name, workbook_.boundSheets_[i].name_.name_) == 0) return DeleteWorksheet(i);
4714 }
4715 return false;
4716}
4717
4718// Delete an Excel worksheet that has given Unicode name.
4719// Returns true if successful, false if otherwise.
4721{
4722 size_t maxWorksheets = worksheets_.size();
4723 for (size_t i=0; i<maxWorksheets; ++i)
4724 {
4725 if (!(workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
4726 if (wcscmp(name, workbook_.boundSheets_[i].name_.wname_) == 0) return DeleteWorksheet(i);
4727 }
4728 return false;
4729}
4730
4731// Get the worksheet name at the given index.
4732// Index starts from 0.
4733// Returns 0 if name is in Unicode format.
4734char* BasicExcel::GetAnsiSheetName(size_t sheetIndex)
4735{
4736 if (!(workbook_.boundSheets_[sheetIndex].name_.unicode_ & 1))
4737 {
4738 return workbook_.boundSheets_[sheetIndex].name_.name_;
4739 }
4740 else return 0;
4741}
4742
4743// Get the worksheet name at the given index.
4744// Index starts from 0.
4745// Returns 0 if name is in Ansi format.
4746wchar_t* BasicExcel::GetUnicodeSheetName(size_t sheetIndex)
4747{
4748 if (workbook_.boundSheets_[sheetIndex].name_.unicode_ & 1)
4749 {
4750 return workbook_.boundSheets_[sheetIndex].name_.wname_;
4751 }
4752 else return 0;
4753}
4754
4755// Get the worksheet name at the given index.
4756// Index starts from 0.
4757// Returns false if name is in Unicode format.
4758bool BasicExcel::GetSheetName(size_t sheetIndex, char* name)
4759{
4760 if (!(workbook_.boundSheets_[sheetIndex].name_.unicode_ & 1))
4761 {
4762 strcpy(name, workbook_.boundSheets_[sheetIndex].name_.name_);
4763 return true;
4764 }
4765 else return false;
4766}
4767
4768// Get the worksheet name at the given index.
4769// Index starts from 0.
4770// Returns false if name is in Ansi format.
4771bool BasicExcel::GetSheetName(size_t sheetIndex, wchar_t* name)
4772{
4773 if (workbook_.boundSheets_[sheetIndex].name_.unicode_ & 1)
4774 {
4775 wcscpy(name, workbook_.boundSheets_[sheetIndex].name_.wname_);
4776 return true;
4777 }
4778 else return false;
4779}
4780
4781// Rename an Excel worksheet at the given index to the given ANSI name.
4782// Index starts from 0.
4783// Returns true if successful, false if otherwise.
4784bool BasicExcel::RenameWorksheet(size_t sheetIndex, const char* to)
4785{
4786 size_t maxWorksheets = yesheets_.size();
4787 if (sheetIndex < maxWorksheets)
4788 {
4789 for (size_t i=0; i<maxWorksheets; ++i)
4790 {
4791 if (workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
4792 if (strcmp(to, workbook_.boundSheets_[i].name_.name_) == 0) return false;
4793 }
4794 workbook_.boundSheets_[sheetIndex].name_ = to;
4795 return true;
4796 }
4797 else return false;
4798}
4799
4800// Rename an Excel worksheet at the given index to the given Unicode name.
4801// Index starts from 0.
4802// Returns true if successful, false if otherwise.
4803bool BasicExcel::RenameWorksheet(size_t sheetIndex, const wchar_t* to)
4804{
4805 size_t maxWorksheets = yesheets_.size();
4806 if (sheetIndex < maxWorksheets)
4807 {
4808 for (size_t i=0; i<maxWorksheets; ++i)
4809 {
4810 if (!(workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
4811 if (wcscmp(to, workbook_.boundSheets_[i].name_.wname_) == 0) return false;
4812 }
4813 workbook_.boundSheets_[sheetIndex].name_ = to;
4814 return true;
4815 }
4816 else return false;
4817}
4818
4819// Rename an Excel worksheet that has given ANSI name to another ANSI name.
4820// Returns true if successful, false if otherwise.
4821bool BasicExcel::RenameWorksheet(const char* from, const char* to)
4822{
4823 size_t maxWorksheets = yesheets_.size();
4824 for (size_t i=0; i<maxWorksheets; ++i)
4825 {
4826 if (workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
4827 if (strcmp(from, workbook_.boundSheets_[i].name_.name_) == 0)
4828 {
4829 for (size_t j=0; j<maxWorksheets; ++j)
4830 {
4831 if (workbook_.boundSheets_[j].name_.unicode_ & 1) continue;
4832 if (strcmp(to, workbook_.boundSheets_[j].name_.name_) == 0) return false;
4833 }
4834 workbook_.boundSheets_[i].name_ = to;
4835 return true;
4836 }
4837 }
4838 return false;
4839}
4840
4841// Rename an Excel worksheet that has given Unicode name to another Unicode name.
4842// Returns true if successful, false if otherwise.
4843bool BasicExcel::RenameWorksheet(const wchar_t* from, const wchar_t* to)
4844{
4845 size_t maxWorksheets = worksheets_.size();
4846 for (size_t i=0; i<maxWorksheets; ++i)
4847 {
4848 if (!(workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
4849 if (wcscmp(from, workbook_.boundSheets_[i].name_.wname_) == 0)
4850 {
4851 for (size_t j=0; j<maxWorksheets; ++j)
4852 {
4853 if (!(workbook_.boundSheets_[j].name_.unicode_ & 1)) continue;
4854 if (wcscmp(to, workbook_.boundSheets_[j].name_.wname_) == 0) return false;
4855 }
4856 workbook_.boundSheets_[i].name_ = to;
4857 return true;
4858 }
4859 }
4860 return false;
4861}
4862
4863size_t BasicExcel::Read(const char* data, size_t dataSize)
4864{
4865 size_t bytesRead = 0;
4866 BOF bof;
4867 short code;
4868 LittleEndian::Read(data, code, 0, 2);
4869 Record rec;
4870 while (code == CODE::BOF)
4871 {
4872 bof.Read(data+bytesRead);
4873 switch (bof.type_)
4874 {
4875 case WORKBOOK_GLOBALS:
4876 bytesRead += workbook_.Read(data+bytesRead);
4877 break;
4878
4879 case VISUAL_BASIC_MODULE:
4880 bytesRead += rec.Read(data+bytesRead);
4881 break;
4882
4883 case WORKSHEET:
4884 worksheets_.push_back(Worksheet());
4885 bytesRead += worksheets_.back().Read(data+bytesRead);
4886 break;
4887
4888 case CHART:
4889 bytesRead += rec.Read(data+bytesRead);
4890 break;
4891
4892 default:
4893 bytesRead += rec.Read(data+bytesRead);
4894 break;
4895 }
4896 if (bytesRead < dataSize) LittleEndian::Read(data, code, bytesRead, 2);
4897 else break;
4898 }
4899 return bytesRead;
4900}
4901
4902size_t BasicExcel::Write(char* data)
4903{
4904 size_t bytesWritten = 0;
4905 bytesWritten += workbook_.Write(data+bytesWritten);
4906
4907 size_t maxWorkSheets = worksheets_.size();
4908 for (size_t i=0; i<maxWorkSheets; ++i)
4909 {
4910 bytesWritten += worksheets_[i].Write(data+bytesWritten);
4911 }
4912 return bytesWritten;
4913}
4914
4916{
4917// AdjustExtSSTPositions();
4918 AdjustBoundSheetBOFPositions();
4919 AdjustDBCellPositions();
4920}
4921
4923{
4924 size_t offset = workbook_.RecordSize();
4925 size_t maxBoundSheets = workbook_.boundSheets_.size();
4926 for (size_t i=0; i<maxBoundSheets; ++i)
4927 {
4928 workbook_.boundSheets_[i].BOFpos_ = offset;
4929 offset += worksheets_[i].RecordSize();
4930 }
4931}
4932
4934{
4935 size_t offset = workbook_.RecordSize();
4936 size_t maxSheets = worksheets_.size();
4937 for (size_t i=0; i<maxSheets; ++i)
4938 {
4939 offset += worksheets_[i].bof_.RecordSize();
4940 offset += worksheets_[i].index_.RecordSize();
4941 offset += worksheets_[i].dimensions_.RecordSize();
4942
4943 size_t maxRowBlocks_ = worksheets_[i].cellTable_.rowBlocks_.size();
4944 for (size_t j=0; j<maxRowBlocks_; ++j)
4945 {
4946 size_t firstRowOffset = 0;
4947
4948 size_t maxRows = worksheets_[i].cellTable_.rowBlocks_[j].rows_.size();
4949 {for (size_t k=0; k<maxRows; ++k)
4950 {
4951 offset += worksheets_[i].cellTable_.rowBlocks_[j].rows_[k].RecordSize();
4952 firstRowOffset += worksheets_[i].cellTable_.rowBlocks_[j].rows_[k].RecordSize();
4953 }}
4954 size_t cellOffset = firstRowOffset - 20; // a ROW record is 20 bytes long
4955
4956 size_t maxCellBlocks = worksheets_[i].cellTable_.rowBlocks_[j].cellBlocks_.size();
4957 {for (size_t k=0; k<maxCellBlocks; ++k)
4958 {
4959 offset += worksheets_[i].cellTable_.rowBlocks_[j].cellBlocks_[k].RecordSize();
4960 firstRowOffset += worksheets_[i].cellTable_.rowBlocks_[j].cellBlocks_[k].RecordSize();
4961 }}
4962
4963 // Adjust Index DBCellPos_ absolute offset
4964 worksheets_[i].index_.DBCellPos_[j] = offset;
4965
4966 offset += worksheets_[i].cellTable_.rowBlocks_[j].dbcell_.RecordSize();
4967
4968 // Adjust DBCell first row offsets
4969 worksheets_[i].cellTable_.rowBlocks_[j].dbcell_.firstRowOffset_ = firstRowOffset;
4970
4971 // Adjust DBCell offsets
4972 size_t l=0;
4973 {for (size_t k=0; k<maxRows; ++k)
4974 {
4975 for (; l<maxCellBlocks; ++l)
4976 {
4977 if (worksheets_[i].cellTable_.rowBlocks_[j].rows_[k].rowIndex_ <=
4978 worksheets_[i].cellTable_.rowBlocks_[j].cellBlocks_[l].RowIndex())
4979 {
4980 worksheets_[i].cellTable_.rowBlocks_[j].dbcell_.offsets_[k] = cellOffset;
4981 break;
4982 }
4983 cellOffset += worksheets_[i].cellTable_.rowBlocks_[j].cellBlocks_[l].RecordSize();
4984 }
4985 cellOffset = 0;
4986 }}
4987 }
4988
4989 offset += worksheets_[i].cellTable_.RecordSize();
4990 offset += worksheets_[i].window2_.RecordSize();
4991 offset += worksheets_[i].eof_.RecordSize();
4992 }
4993}
4994
4996{
4997 size_t offset = workbook_.bof_.RecordSize();
4998 offset += workbook_.bof_.RecordSize();
4999 offset += workbook_.window1_.RecordSize();
5000
5001 size_t maxFonts = workbook_.fonts_.size();
5002 {for (size_t i=0; i<maxFonts; ++i) {offset += workbook_.fonts_[i].RecordSize();}}
5003
5004 size_t maxXFs = workbook_.XFs_.size();
5005 {for (size_t i=0; i<maxXFs; ++i) {offset += workbook_.XFs_[i].RecordSize();}}
5006
5007 size_t maxStyles = workbook_.styles_.size();
5008 {for (size_t i=0; i<maxStyles; ++i) {offset += workbook_.styles_[i].RecordSize();}}
5009
5010 size_t maxBoundSheets = workbook_.boundSheets_.size();
5011 {for (size_t i=0; i<maxBoundSheets; ++i) {offset += workbook_.boundSheets_[i].RecordSize();}}
5012
5013 workbook_.extSST_.stringsTotal_ = 10;
5014 size_t maxPortions = workbook_.sst_.uniqueStringsTotal_ / workbook_.extSST_.stringsTotal_ +
5015 (workbook_.sst_.uniqueStringsTotal_%workbook_.extSST_.stringsTotal_ ? 1 : 0);
5016 workbook_.extSST_.streamPos_.resize(maxPortions);
5017 workbook_.extSST_.firstStringPos_.resize(maxPortions);
5018 workbook_.extSST_.unused_.resize(maxPortions);
5019
5020 size_t relativeOffset = 8;
5021 for (size_t i=0; i<maxPortions; ++i)
5022 {
5023 workbook_.extSST_.streamPos_[i] = offset + 4 + relativeOffset;
5024 workbook_.extSST_.firstStringPos_[i] = 4 + relativeOffset;
5025 workbook_.extSST_.unused_[i] = 0;
5026
5027 for (size_t j = 0; j < (size_t)workbook_.extSST_.stringsTotal_; ++j)
5028 {
5029 if (i*workbook_.extSST_.stringsTotal_+j >= workbook_.sst_.strings_.size()) break;
5030 size_t stringSize = workbook_.sst_.strings_[i*workbook_.extSST_.stringsTotal_+j].StringSize();
5031 if (relativeOffset+stringSize+3 < 8224)
5032 {
5033 relativeOffset += stringSize + 3;
5034 }
5035 else
5036 {
5037 // If have >= 12 bytes (2 for size, 1 for unicode and >=9 for data, can split string
5038 // otherwise, end record and start continue record.
5039 if (8224 - relativeOffset >= 12)
5040 {
5041 stringSize -= (8224 - relativeOffset - 3);
5042 offset += 12 + relativeOffset;
5043 relativeOffset = 0;
5044
5045 size_t additionalContinueRecords = stringSize / 8223; // 8223 because the first byte is for unicode
5046 for (size_t k=0; k<additionalContinueRecords; ++k)
5047 {
5048 stringSize -= 8223;
5049 }
5050 relativeOffset += stringSize + 1;
5051 }
5052 else
5053 {
5054 if (relativeOffset+stringSize+3 < 8224)
5055 {
5056 relativeOffset += stringSize + 3;
5057 }
5058 else
5059 {
5060 // If have >= 12 bytes (2 for size, 1 for unicode and >=9 for data, can split string
5061 // otherwise, end record and start continue record.
5062 if (8224 - relativeOffset >= 12)
5063 {
5064 stringSize -= (8224 - relativeOffset - 3);
5065 offset += 12 + relativeOffset;
5066 relativeOffset = 0;
5067
5068 size_t additionalContinueRecords = stringSize / 8223; // 8223 because the first byte is for unicode
5069 for (size_t k=0; k<additionalContinueRecords; ++k)
5070 {
5071 stringSize -= 8223;
5072 }
5073 relativeOffset += stringSize + 1;
5074 }
5075 }
5076 }
5077 }
5078 }
5079 }
5080}
5081
5082// Update yesheets_ using information from worksheets_.
5084{
5085 size_t maxWorksheets = worksheets_.size();
5086 yesheets_.clear();
5087 yesheets_.reserve(maxWorksheets);
5088 for (size_t i=0; i<maxWorksheets; ++i)
5089 {
5090 yesheets_.push_back(BasicExcelWorksheet(this,i));
5091 }
5092}
5093
5094// Update worksheets_ using information from yesheets_.
5096{
5097 // Constants.
5098 const size_t maxWorksheets = yesheets_.size();
5103 LargeString largeString;
5104
5105 map<vector<char>, size_t> stringMap;
5106 map<vector<char>, size_t>::iterator stringMapIt;
5107 map<vector<wchar_t>, size_t> wstringMap;
5108 map<vector<wchar_t>, size_t>::iterator wstringMapIt;
5109
5110 // Reset worksheets and string table.
5111 worksheets_.clear();
5112 worksheets_.resize(maxWorksheets);
5113
5114 workbook_.sst_.stringsTotal_ = 0;
5115 workbook_.sst_.uniqueStringsTotal_ = 0;
5116 workbook_.sst_.strings_.clear();
5117
5118 for (size_t s=0; s<maxWorksheets; ++s)
5119 {
5120 size_t maxRows = yesheets_[s].GetTotalRows();
5121 size_t maxCols = yesheets_[s].GetTotalCols();
5122
5123 // Modify Index
5124 worksheets_[s].index_.firstUsedRowIndex_ = 100000; // Use 100000 to indicate that firstUsedRowIndex is not set yet since maximum allowed rows in Excel is 65535.
5125 worksheets_[s].index_.firstUnusedRowIndex_ = maxRows;
5126
5127 // Modify Dimensions
5128 worksheets_[s].dimensions_.firstUsedRowIndex_ = 100000; // Use 100000 to indicate that firstUsedRowIndex is not set yet since maximum allowed rows in Excel is 65535.
5129 worksheets_[s].dimensions_.firstUsedColIndex_ = 1000; // Use 1000 to indicate that firstUsedColIndex is not set yet since maximum allowed columns in Excel is 255.
5130 worksheets_[s].dimensions_.lastUsedRowIndexPlusOne_ = maxRows;
5131 worksheets_[s].dimensions_.lastUsedColIndexPlusOne_ = maxCols;
5132
5133 // Make first sheet selected and other sheets unselected
5134 if (s > 0) worksheets_[s].window2_.options_ &= ~0x200;
5135
5136 // References and pointers to shorten code
5137 vector<Worksheet::CellTable::RowBlock>& rRowBlocks = worksheets_[s].cellTable_.rowBlocks_;
5138 vector<Worksheet::CellTable::RowBlock::CellBlock>* pCellBlocks;
5140 rRowBlocks.resize(maxRows/32 + (maxRows%32 ? 1 : 0));
5141 for (size_t r=0, curRowBlock=0; r<maxRows; ++r)
5142 {
5143 if (r%32==0)
5144 {
5145 // New row block for every 32 rows.
5146 pCellBlocks = &(rRowBlocks[curRowBlock++].cellBlocks_);
5147 }
5148 bool newRow = true; // Keep track whether current row contains data.
5149 pCellBlocks->reserve(1000);
5150 for (size_t c=0; c<maxCols; ++c)
5151 {
5152 BasicExcelCell* cell = yesheets_[s].Cell(r,c);
5153 int cellType = cell->Type();
5154 if (cellType != BasicExcelCell::UNDEFINED) // Current cell contains some data
5155 {
5156 if (worksheets_[s].index_.firstUsedRowIndex_ == 100000)
5157 {
5158 // Set firstUsedRowIndex.
5159 worksheets_[s].index_.firstUsedRowIndex_ = r;
5160 worksheets_[s].dimensions_.firstUsedRowIndex_ = r;
5161
5162 // Resize DBCellPos.
5163 size_t nm = int(worksheets_[s].index_.firstUnusedRowIndex_ - worksheets_[s].index_.firstUsedRowIndex_ - 1) / 32 + 1;
5164 worksheets_[s].index_.DBCellPos_.resize(nm);
5165 }
5166 if (worksheets_[s].dimensions_.firstUsedColIndex_ == 1000)
5167 {
5168 // Set firstUsedColIndex.
5169 worksheets_[s].dimensions_.firstUsedColIndex_ = c;
5170 }
5171
5172 if (newRow)
5173 {
5174 // Prepare Row and DBCell for new row with data.
5175 Worksheet::CellTable::RowBlock& rRowBlock = rRowBlocks[curRowBlock-1];
5176 rRowBlock.rows_.push_back(row);
5177 rRowBlock.rows_.back().rowIndex_ = r;
5178 rRowBlock.rows_.back().lastCellColIndexPlusOne_ = maxCols;
5179 rRowBlock.dbcell_.offsets_.push_back(0);
5180 newRow = false;
5181 }
5182
5183 // Create new cellblock to store cell.
5184 pCellBlocks->push_back(cellBlock);
5185 if (pCellBlocks->size()%1000==0) pCellBlocks->reserve(pCellBlocks->size()+1000);
5186 pCell = &(pCellBlocks->back());
5187
5188 // Store cell.
5189 switch(cellType)
5190 {
5192 {
5193 // Check whether it is a single cell or range of cells.
5194 size_t cl = c + 1;
5195 for (; cl<maxCols; ++cl)
5196 {
5197 BasicExcelCell* cellNext = yesheets_[s].Cell(r,cl);
5198 if (cellNext->Type()==BasicExcelCell::UNDEFINED ||
5199 cellNext->Type()!=cell->Type()) break;
5200 }
5201
5202 if (cl > c+1)
5203 {
5204 // MULRK cells
5205 pCell->type_ = CODE::MULRK;
5206 pCell->normalType_ = true;
5207 pCell->mulrk_.rowIndex_ = r;
5208 pCell->mulrk_.firstColIndex_ = c;
5209 pCell->mulrk_.lastColIndex_ = cl - 1;
5210 pCell->mulrk_.XFRK_.resize(cl-c);
5211 for (size_t i=0; c<cl; ++c, ++i)
5212 {
5213 cell = yesheets_[s].Cell(r,c);
5214 pCell->mulrk_.XFRK_[i].RKValue_ = GetRKValueFromInteger(cell->GetInteger());
5215 }
5216 --c;
5217 }
5218 else
5219 {
5220 // Single cell
5221 pCell->normalType_ = true;
5222 pCell->type_ = CODE::RK;
5223 pCell->rk_.rowIndex_ = r;
5224 pCell->rk_.colIndex_ = c;
5225 pCell->rk_.value_ = GetRKValueFromInteger(cell->GetInteger());
5226 }
5227 break;
5228 }
5229
5231 {
5232 // Check whether it is a single cell or range of cells.
5233 // Double values which cannot be stored as RK values will be stored as single cells.
5234 bool canStoreAsRKValue = CanStoreAsRKValue(cell->GetDouble());
5235 size_t cl = c + 1;
5236 for (; cl<maxCols; ++cl)
5237 {
5238 BasicExcelCell* cellNext = yesheets_[s].Cell(r,cl);
5239 if (cellNext->Type()==BasicExcelCell::UNDEFINED ||
5240 cellNext->Type()!=cell->Type() ||
5241 canStoreAsRKValue!=CanStoreAsRKValue(cellNext->GetDouble())) break;
5242 }
5243
5244 if (cl > c+1 && canStoreAsRKValue)
5245 {
5246 // MULRK cells
5247 pCell->type_ = CODE::MULRK;
5248 pCell->normalType_ = true;
5249 pCell->mulrk_.rowIndex_ = r;
5250 pCell->mulrk_.firstColIndex_ = c;
5251 pCell->mulrk_.lastColIndex_ = cl - 1;
5252 pCell->mulrk_.XFRK_.resize(cl-c);
5253 for (size_t i=0; c<cl; ++c, ++i)
5254 {
5255 cell = yesheets_[s].Cell(r,c);
5256 pCell->mulrk_.XFRK_[i].RKValue_ = GetRKValueFromDouble(cell->GetDouble());
5257 }
5258 --c;
5259 }
5260 else
5261 {
5262 // Single cell
5263 pCell->normalType_ = true;
5264 if (canStoreAsRKValue)
5265 {
5266 pCell->type_ = CODE::RK;
5267 pCell->rk_.rowIndex_ = r;
5268 pCell->rk_.colIndex_ = c;
5269 pCell->rk_.value_ = GetRKValueFromDouble(cell->GetDouble());
5270 }
5271 else
5272 {
5273 pCell->type_ = CODE::NUMBER;
5274 pCell->number_.rowIndex_ = r;
5275 pCell->number_.colIndex_ = c;
5276 pCell->number_.value_ = cell->GetDouble();
5277 }
5278 }
5279 break;
5280 }
5281
5283 {
5284 // Fill cell information
5285 pCell->type_ = CODE::LABELSST;
5286 pCell->normalType_ = true;
5287 pCell->labelsst_.rowIndex_ = r;
5288 pCell->labelsst_.colIndex_ = c;
5289
5290 // Get cell string
5291 vector<char> str(cell->GetStringLength()+1);
5292 cell->Get(&*(str.begin()));
5293 str.pop_back(); // Remove null character because LargeString does not store null character.
5294
5295 // Check if string is present in Shared string table.
5296 ++workbook_.sst_.stringsTotal_;
5297 size_t maxUniqueStrings = workbook_.sst_.uniqueStringsTotal_;
5298 size_t strIndex = 0;
5299 stringMapIt = stringMap.find(str);
5300 if (stringMapIt != stringMap.end()) strIndex = stringMapIt->second;
5301 else strIndex = maxUniqueStrings;
5302
5303 if (strIndex < maxUniqueStrings)
5304 {
5305 // String is present in Shared string table.
5306 pCell->labelsst_.SSTRecordIndex_ = strIndex;
5307 }
5308 else
5309 {
5310 // New unique string.
5311 stringMap[str] = maxUniqueStrings;
5312 workbook_.sst_.strings_.push_back(largeString);
5313 workbook_.sst_.strings_[maxUniqueStrings].name_ = str;
5314 workbook_.sst_.strings_[maxUniqueStrings].unicode_ = 0;
5315 pCell->labelsst_.SSTRecordIndex_ = maxUniqueStrings;
5316 ++workbook_.sst_.uniqueStringsTotal_;
5317 }
5318 break;
5319 }
5320
5322 {
5323 // Fill cell information
5324 pCell->type_ = CODE::LABELSST;
5325 pCell->normalType_ = true;
5326 pCell->labelsst_.rowIndex_ = r;
5327 pCell->labelsst_.colIndex_ = c;
5328
5329 // Get cell string
5330 vector<wchar_t> str(cell->GetStringLength()+1);
5331 cell->Get(&*(str.begin()));
5332 str.pop_back(); // Remove null character because LargeString does not store null character.
5333
5334 // Check if string is present in Shared string table.
5335 ++workbook_.sst_.stringsTotal_;
5336 size_t maxUniqueStrings = workbook_.sst_.strings_.size();
5337 size_t strIndex = 0;
5338 wstringMapIt = wstringMap.find(str);
5339 if (wstringMapIt != wstringMap.end()) strIndex = wstringMapIt->second;
5340 else strIndex = maxUniqueStrings;
5341
5342 if (strIndex < maxUniqueStrings)
5343 {
5344 // String is present in Shared string table.
5345 pCell->labelsst_.SSTRecordIndex_ = strIndex;
5346 }
5347 else
5348 {
5349 // New unique string
5350 wstringMap[str] = maxUniqueStrings;
5351 workbook_.sst_.strings_.push_back(largeString);
5352 workbook_.sst_.strings_[maxUniqueStrings].wname_ = str;
5353 workbook_.sst_.strings_[maxUniqueStrings].unicode_ = 1;
5354 pCell->labelsst_.SSTRecordIndex_ = maxUniqueStrings;
5355 ++workbook_.sst_.uniqueStringsTotal_;
5356 }
5357 break;
5358 }
5359 }
5360 }
5361 }
5362 }
5363
5364 // If worksheet has no data
5365 if (worksheets_[s].index_.firstUsedRowIndex_ == 100000)
5366 {
5367 // Set firstUsedRowIndex.
5368 worksheets_[s].index_.firstUsedRowIndex_ = 0;
5369 worksheets_[s].dimensions_.firstUsedRowIndex_ = 0;
5370
5371 // Resize DBCellPos.
5372 size_t nm = int(worksheets_[s].index_.firstUnusedRowIndex_ - worksheets_[s].index_.firstUsedRowIndex_ - 1) / 32 + 1;
5373 worksheets_[s].index_.DBCellPos_.resize(nm);
5374 }
5375 if (worksheets_[s].dimensions_.firstUsedColIndex_ == 1000)
5376 {
5377 // Set firstUsedColIndex.
5378 worksheets_[s].dimensions_.firstUsedColIndex_ = 0;
5379 }
5380 }
5381}
5382/************************************************************************************************************/
5383
5384/************************************************************************************************************/
5386 excel_(excel), sheetIndex_(sheetIndex)
5387{
5388 UpdateCells();
5389}
5390
5391// Get the current worksheet name.
5392// Returns 0 if name is in Unicode format.
5394{
5395 if (!(excel_->workbook_.boundSheets_[sheetIndex_].name_.unicode_ & 1))
5396 {
5397 return excel_->workbook_.boundSheets_[sheetIndex_].name_.name_;
5398 }
5399 else return 0;
5400}
5401
5402// Get the current worksheet name.
5403// Returns 0 if name is in Ansi format.
5405{
5406 if (excel_->workbook_.boundSheets_[sheetIndex_].name_.unicode_ & 1)
5407 {
5408 return excel_->workbook_.boundSheets_[sheetIndex_].name_.wname_;
5409 }
5410 else return 0;
5411}
5412
5413// Get the current worksheet name.
5414// Returns false if name is in Unicode format.
5416{
5417 if (!(excel_->workbook_.boundSheets_[sheetIndex_].name_.unicode_ & 1))
5418 {
5419 strcpy(name, excel_->workbook_.boundSheets_[sheetIndex_].name_.name_);
5420 return true;
5421 }
5422 else return false;
5423}
5424
5425// Get the current worksheet name.
5426// Returns false if name is in Ansi format.
5428{
5429 if (excel_->workbook_.boundSheets_[sheetIndex_].name_.unicode_ & 1)
5430 {
5431 wcscpy(name, excel_->workbook_.boundSheets_[sheetIndex_].name_.wname_);
5432 return true;
5433 }
5434 else return false;
5435}
5436
5437// Rename current Excel worksheet to another ANSI name.
5438// Returns true if successful, false if otherwise.
5440{
5441 size_t maxWorksheets = excel_->workbook_.boundSheets_.size();
5442 for (size_t i=0; i<maxWorksheets; ++i)
5443 {
5444 if (excel_->workbook_.boundSheets_[i].name_.unicode_ & 1) continue;
5445 if (strcmp(to, excel_->workbook_.boundSheets_[i].name_.name_) == 0) return false;
5446 }
5447
5449 return true;
5450}
5451
5452// Rename current Excel worksheet to another Unicode name.
5453// Returns true if successful, false if otherwise.
5454bool BasicExcelWorksheet::Rename(const wchar_t* to)
5455{
5456 size_t maxWorksheets = excel_->workbook_.boundSheets_.size();
5457 for (size_t i=0; i<maxWorksheets; ++i)
5458 {
5459 if (!(excel_->workbook_.boundSheets_[i].name_.unicode_ & 1)) continue;
5460 if (wcscmp(to, excel_->workbook_.boundSheets_[i].name_.wname_) == 0) return false;
5461 }
5462
5464 return true;
5465}
5466
5469void BasicExcelWorksheet::Print(ostream& os, char delimiter, char textQualifier)
5470{
5471 for (size_t r=0; r<maxRows_; ++r)
5472 {
5473 for (size_t c=0; c<maxCols_; ++c)
5474 {
5475 BasicExcelCell* cell = Cell(r,c);
5476 switch (cell->Type())
5477 {
5479 break;
5480
5482 os << cell->GetInteger();
5483 break;
5484
5486 os << setprecision(15) << cell->GetDouble();
5487 break;
5488
5490 {
5491 if (textQualifier != '\0')
5492 {
5493 // Get string.
5494 size_t maxLength = cell->GetStringLength();
5495 vector<char> cellString(maxLength+1);
5496 cell->Get(&*(cellString.begin()));
5497
5498 // Duplicate textQualifier if found in string.
5499 vector<char>::iterator it;
5500 size_t npos = 0;
5501 while ((it=find(cellString.begin()+npos, cellString.end(), textQualifier)) != cellString.end())
5502 {
5503 npos = distance(cellString.begin(), cellString.insert(it, textQualifier)) + 2;
5504 }
5505
5506 // Print out string enclosed with textQualifier.
5507 os << textQualifier << &*(cellString.begin()) << textQualifier;
5508 }
5509 else os << cell->GetString();
5510 break;
5511 }
5512
5514 {
5515 // Print out string enclosed with textQualifier (does not work).
5516 //os << textQualifier << cell->GetWString() << textQualifier;
5517 break;
5518 }
5519 }
5520 if (c < maxCols_-1) os << delimiter;
5521 }
5522 os << endl;
5523 }
5524}
5525
5526// Total number of rows in current Excel worksheet.
5528{
5529 return maxRows_;
5530}
5531
5532// Total number of columns in current Excel worksheet.
5534{
5535 return maxCols_;
5536}
5537
5538// Return a pointer to an Excel cell.
5539// row and col starts from 0.
5540// Returns 0 if row exceeds 65535 or col exceeds 255.
5542{
5543 // Check to ensure row and col does not exceed maximum allowable range for an Excel worksheet.
5544 if (row>65535 || col>255) return 0;
5545
5546 // Increase size of cells matrix if necessary
5547 if (col>=maxCols_)
5548 {
5549 // Increase number of columns.
5550 maxCols_ = col + 1;
5551 for (size_t i=0; i<maxRows_; ++i) cells_[i].resize(maxCols_);
5552 }
5553 if (row>=maxRows_)
5554 {
5555 // Increase number of rows.
5556 maxRows_ = row + 1;
5557 cells_.resize(maxRows_, vector<BasicExcelCell>(maxCols_));
5558 }
5559
5560 return &(cells_[row][col]);
5561}
5562
5563// Erase content of a cell. row and col starts from 0.
5564// Returns true if successful, false if row or col exceeds range.
5565bool BasicExcelWorksheet::EraseCell(size_t row, size_t col)
5566{
5567 if (row<maxRows_ && col<maxCols_)
5568 {
5569 cells_[row][col].EraseContents();
5570 return true;
5571 }
5572 else return false;
5573}
5574
5575// Update cells using information from BasicExcel.worksheets_.
5577{
5578 // Define some reference
5579 Worksheet::Dimensions& dimension = excel_->worksheets_[sheetIndex_].dimensions_;
5580 vector<Worksheet::CellTable::RowBlock>& rRowBlocks = excel_->worksheets_[sheetIndex_].cellTable_.rowBlocks_;
5581
5582 vector<wchar_t> wstr;
5583 vector<char> str;
5584
5587
5588 // Resize the cells to the size of the worksheet
5589 vector<BasicExcelCell> cellCol(maxCols_);
5590 cells_.resize(maxRows_, cellCol);
5591
5592 size_t maxRowBlocks = rRowBlocks.size();
5593 for (size_t i=0; i<maxRowBlocks; ++i)
5594 {
5595 vector<Worksheet::CellTable::RowBlock::CellBlock>& rCellBlocks = rRowBlocks[i].cellBlocks_;
5596 size_t maxCells = rCellBlocks.size();
5597 for (size_t j=0; j<maxCells; ++j)
5598 {
5599 size_t row = rCellBlocks[j].RowIndex();
5600 size_t col = rCellBlocks[j].ColIndex();
5601 switch (rCellBlocks[j].type_)
5602 {
5603 case CODE::BLANK:
5604 break;
5605
5606 case CODE::BOOLERR:
5607 if (rCellBlocks[j].boolerr_.error_ == 0)
5608 {
5609 cells_[row][col].Set(rCellBlocks[j].boolerr_.value_);
5610 }
5611 break;
5612
5613 case CODE::LABELSST:
5614 {
5615 vector<LargeString>& ss = excel_->workbook_.sst_.strings_;
5616 if (ss[rCellBlocks[j].labelsst_.SSTRecordIndex_].unicode_ & 1)
5617 {
5618 wstr = ss[rCellBlocks[j].labelsst_.SSTRecordIndex_].wname_;
5619 wstr.resize(wstr.size()+1);
5620 wstr.back() = L'\0';
5621 cells_[row][col].Set(&*(wstr.begin()));
5622 }
5623 else
5624 {
5625 str = ss[rCellBlocks[j].labelsst_.SSTRecordIndex_].name_;
5626 str.resize(str.size()+1);
5627 str.back() = '\0';
5628 cells_[row][col].Set(&*(str.begin()));
5629 }
5630 break;
5631 }
5632
5633 case CODE::MULBLANK:
5634 break;
5635
5636 case CODE::MULRK:
5637 {
5638 size_t maxCols = rCellBlocks[j].mulrk_.lastColIndex_ - rCellBlocks[j].mulrk_.firstColIndex_ + 1;
5639 for (size_t k=0; k<maxCols; ++k, ++col)
5640 {
5641 // Get values of the whole range
5642 int rkValue = rCellBlocks[j].mulrk_.XFRK_[k].RKValue_;
5643 if (IsRKValueAnInteger(rkValue))
5644 {
5645 cells_[row][col].Set(GetIntegerFromRKValue(rkValue));
5646 }
5647 else
5648 {
5649 cells_[row][col].Set(GetDoubleFromRKValue(rkValue));
5650 }
5651 }
5652 break;
5653 }
5654
5655 case CODE::NUMBER:
5656 cells_[row][col].Set(rCellBlocks[j].number_.value_);
5657 break;
5658
5659 case CODE::RK:
5660 {
5661 int rkValue = rCellBlocks[j].rk_.value_;
5662 if (IsRKValueAnInteger(rkValue))
5663 {
5664 cells_[row][col].Set(GetIntegerFromRKValue(rkValue));
5665 }
5666 else
5667 {
5668 cells_[row][col].Set(GetDoubleFromRKValue(rkValue));
5669 }
5670 break;
5671 }
5672
5673 case CODE::FORMULA:
5674 break;
5675 }
5676 }
5677 }
5678}
5679/************************************************************************************************************/
5680
5681/************************************************************************************************************/
5682BasicExcelCell::BasicExcelCell() : type_(UNDEFINED) {};
5683
5684// Get type of value stored in current Excel cell.
5685// Returns one of the enums.
5686int BasicExcelCell::Type() const {return type_;}
5687
5688// Get an integer value.
5689// Returns false if cell does not contain an integer or a double.
5690bool BasicExcelCell::Get(int& val) const
5691{
5692 if (type_ == INT)
5693 {
5694 val = ival_;
5695 return true;
5696 }
5697 else if (type_ == DOUBLE)
5698 {
5699 val = (int)dval_;
5700 return true;
5701 }
5702 else return false;
5703}
5704
5705// Get a double value.
5706// Returns false if cell does not contain a double or an integer.
5707bool BasicExcelCell::Get(double& val) const
5708{
5709 if (type_ == DOUBLE)
5710 {
5711 val = dval_;
5712 return true;
5713 }
5714 else if (type_ == INT)
5715 {
5716 val = (double)ival_;
5717 return true;
5718 }
5719 else return false;
5720}
5721
5722// Get an ANSI string.
5723// Returns false if cell does not contain an ANSI string.
5724bool BasicExcelCell::Get(char* str) const
5725{
5726 if (type_ == STRING)
5727 {
5728 if (str_.empty()) *str = '\0';
5729 else strcpy(str, &*(str_.begin()));
5730 return true;
5731 }
5732 else return false;
5733}
5734
5735// Get an Unicode string.
5736// Returns false if cell does not contain an Unicode string.
5737bool BasicExcelCell::Get(wchar_t* str) const
5738{
5739 if (type_ == WSTRING)
5740 {
5741 if (wstr_.empty()) *str = L'\0';
5742 else wcscpy(str, &*(wstr_.begin()));
5743 return true;
5744 }
5745 else return false;
5746}
5747
5748// Return length of ANSI or Unicode string (excluding null character).
5750{
5751 if (type_ == STRING) return str_.size() - 1;
5752 else return wstr_.size() - 1;
5753}
5754
5755// Get an integer value.
5756// Returns 0 if cell does not contain an integer.
5758{
5759 int val;
5760 if (Get(val)) return val;
5761 else return 0;
5762}
5763
5764// Get a double value.
5765// Returns 0.0 if cell does not contain a double.
5767{
5768 double val;
5769 if (Get(val)) return val;
5770 else return 0.0;
5771}
5772
5773// Get an ANSI string.
5774// Returns 0 if cell does not contain an ANSI string.
5775const char* BasicExcelCell::GetString() const
5776{
5777 vector<char> str(str_.size());
5778 if (!str.empty() && Get(&*(str.begin()))) return &*(str_.begin());
5779 else return 0;
5780}
5781
5782// Get an Unicode string.
5783// Returns 0 if cell does not contain an Unicode string.
5784const wchar_t* BasicExcelCell::GetWString() const
5785{
5786 vector<wchar_t> wstr(wstr_.size());
5787 if (!wstr.empty() && Get(&*(wstr.begin()))) return &*(wstr_.begin());
5788 else return 0;
5789}
5790
5791// Set content of current Excel cell to an integer.
5793{
5794 SetInteger(val);
5795}
5796
5797// Set content of current Excel cell to a double.
5798void BasicExcelCell::Set(double val)
5799{
5800 SetDouble(val);
5801}
5802
5803// Set content of current Excel cell to an ANSI string.
5804void BasicExcelCell::Set(const char* str)
5805{
5806 SetString(str);
5807}
5808
5809// Set content of current Excel cell to an Unicode string.
5810void BasicExcelCell::Set(const wchar_t* str)
5811{
5812 SetWString(str);
5813}
5814
5815// Set content of current Excel cell to an integer.
5817{
5818 type_ = INT;
5819 ival_ = val;
5820}
5821
5822// Set content of current Excel cell to a double.
5824{
5825 type_ = DOUBLE;
5826 dval_ = val;
5827}
5828
5829// Set content of current Excel cell to an ANSI string.
5830void BasicExcelCell::SetString(const char* str)
5831{
5832 size_t length = strlen(str);
5833 if (length > 0)
5834 {
5835 type_ = STRING;
5836 str_ = vector<char>(length+1);
5837 strcpy(&*(str_.begin()), str);
5838 wstr_.clear();
5839 }
5840 else EraseContents();
5841}
5842
5843// Set content of current Excel cell to an Unicode string.
5844void BasicExcelCell::SetWString(const wchar_t* str)
5845{
5846 size_t length = wcslen(str);
5847 if (length > 0)
5848 {
5849 type_ = WSTRING;
5850 wstr_ = vector<wchar_t>(length+1);
5851 wcscpy(&*(wstr_.begin()), str);
5852 str_.clear();
5853 }
5854 else EraseContents();
5855}
5856
5857// Erase the content of current Excel cell.
5858// Set type to UNDEFINED.
5860{
5861 type_ = UNDEFINED;
5862 str_.clear();
5863 wstr_.clear();
5864}
5865
5868ostream& operator<<(ostream& os, const BasicExcelCell& cell)
5869{
5870 switch (cell.Type())
5871 {
5873 os << '\0';
5874 break;
5875
5877 os << cell.GetInteger();
5878 break;
5879
5881 os << cell.GetDouble();
5882 break;
5883
5885 os << cell.GetString();
5886 break;
5887
5889 os << cell.GetWString();
5890 break;
5891 }
5892 return os;
5893}
5894
5895} // YExcel namespace end
#define SIZEOFWCHAR_T
Definition: BasicExcel.hpp:62
void SetBlockSize(size_t size)
Definition: BasicExcel.hpp:92
std::ios_base::openmode mode_
Definition: BasicExcel.hpp:100
bool Swap(size_t index1, size_t index2)
Definition: BasicExcel.cpp:123
bool Move(size_t from, size_t to)
Definition: BasicExcel.cpp:149
bool Write(size_t index, const char *block)
Definition: BasicExcel.cpp:104
bool Insert(size_t index, const char *block)
Definition: BasicExcel.cpp:176
bool Open(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Definition: BasicExcel.cpp:33
bool Read(size_t index, char *block)
Definition: BasicExcel.cpp:89
bool Create(const wchar_t *filename)
Definition: BasicExcel.cpp:12
bool Erase(size_t index)
Definition: BasicExcel.cpp:198
std::vector< char > filename_
Definition: BasicExcel.hpp:99
std::vector< PropertyTree * > children_
Definition: BasicExcel.hpp:414
void IncreasePropertyReferences(PropertyTree *parentTree, size_t index)
bool Create(const wchar_t *filename)
Definition: BasicExcel.cpp:431
int ReadFile(const wchar_t *path, char *data)
Definition: BasicExcel.cpp:794
std::vector< int > sblocksIndices_
Definition: BasicExcel.hpp:356
void DecreasePropertyReferences(PropertyTree *parentTree, size_t index)
void IncreaseLocationReferences(std::vector< size_t > indices)
void UpdateChildrenIndices(PropertyTree *parentTree)
void SplitPath(const wchar_t *path, wchar_t *&parentpath, wchar_t *&propertyname)
void DecreaseLocationReferences(std::vector< size_t > indices)
bool Open(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
int WriteFile(const wchar_t *path, const char *data, size_t size)
Definition: BasicExcel.cpp:848
size_t WriteData(const char *data, size_t size, int startIndex, bool isBig)
int MakeFile(const wchar_t *path)
Definition: BasicExcel.cpp:740
int MakeProperty(const wchar_t *path, Property *property)
int RemoveFile(const wchar_t *path)
Definition: BasicExcel.cpp:757
std::vector< PropertyTree * > previousDirectories_
Definition: BasicExcel.hpp:430
std::vector< Property * > properties_
Definition: BasicExcel.hpp:429
std::vector< char > block_
Definition: BasicExcel.hpp:301
PropertyTree * currentDirectory_
Definition: BasicExcel.hpp:428
int DirectoryList(std::vector< std::vector< wchar_t > > &list, const wchar_t *path=0)
Definition: BasicExcel.cpp:717
void GetBlockIndices(size_t startIndex, std::vector< size_t > &indices, bool isBig)
void InsertPropertyTree(PropertyTree *parentTree, Property *property, size_t index)
int PresentWorkingDirectory(wchar_t *path)
Definition: BasicExcel.cpp:608
int RemoveDirectory(const wchar_t *path)
Definition: BasicExcel.cpp:657
size_t DataSize(size_t startIndex, bool isBig)
size_t GetFreeBlockIndex(bool isBig)
size_t ReadData(size_t startIndex, char *data, bool isBig)
int ChangeDirectory(const wchar_t *path)
Definition: BasicExcel.cpp:523
PropertyTree * FindProperty(size_t index)
int FileSize(const wchar_t *path, size_t &size)
Definition: BasicExcel.cpp:772
void DeletePropertyTree(PropertyTree *tree)
void FreeBlocks(std::vector< size_t > &indices, bool isBig)
int DelTree(const wchar_t *path)
Definition: BasicExcel.cpp:671
void LinkBlocks(size_t from, size_t to, bool isBig)
std::vector< int > blocksIndices_
Definition: BasicExcel.hpp:355
void ExpandBATArray(bool isBig)
int MakeDirectory(const wchar_t *path)
Definition: BasicExcel.cpp:591
double dval_
Double value stored in current Excel cell.
void SetDouble(double val)
Set content of current Excel cell to a double.
void Set(int val)
Set content of current Excel cell to an integer.
int Type() const
Get type of value stored in current Excel cell. Returns one of the above enums.
void SetInteger(int val)
Set content of current Excel cell to an integer.
bool Get(int &val) const
Get an integer value. Returns false if cell does not contain an integer or a double.
std::vector< wchar_t > wstr_
Unicode string stored in current Excel cell. Include null character.
size_t GetStringLength() const
Return length of ANSI or Unicode string (excluding null character).
std::vector< char > str_
ANSI string stored in current Excel cell. Include null character.
const char * GetString() const
Get an ANSI string. Returns 0 if cell does not contain an ANSI string.
int type_
Type of value stored in current Excel cell. Contains one of the above enums.
void EraseContents()
Erase the content of current Excel cell. Set type to UNDEFINED.
const wchar_t * GetWString() const
Get an Unicode string. Returns 0 if cell does not contain an Unicode string.
int ival_
Integer value stored in current Excel cell.
double GetDouble() const
Get a double value. Returns 0.0 if cell does not contain a double.
int GetInteger() const
Get an integer value. Returns 0 if cell does not contain an integer.
void SetString(const char *str)
Set content of current Excel cell to an ANSI string.
void SetWString(const wchar_t *str)
Set content of current Excel cell to an Unicode string.
wchar_t * GetUnicodeSheetName(size_t sheetIndex)
Get the worksheet name at the given index. Index starts from 0. Returns 0 if name is in Ansi format.
bool DeleteWorksheet(size_t sheetIndex)
Delete an Excel worksheet at the given index. Index starts from 0. Returns true if successful,...
void UpdateYExcelWorksheet()
Update yesheets_ using information from worksheets_.
void AdjustDBCellPositions()
void UpdateWorksheets()
Update worksheets_ using information from yesheets_.
void AdjustStreamPositions()
size_t Write(char *data)
void New(int sheets=3)
Create a new Excel workbook with a given number of spreadsheets (Minimum 1).
BasicExcelWorksheet * GetWorksheet(size_t sheetIndex)
Get a pointer to an Excel worksheet at the given index. Index starts from 0. Returns 0 if index is in...
size_t Read(const char *data, size_t dataSize)
std::vector< Worksheet > worksheets_
Raw Worksheets.
size_t GetTotalWorkSheets()
Total number of Excel worksheets in current Excel workbook.
void AdjustBoundSheetBOFPositions()
bool Load(const char *filename)
Load an Excel workbook from a file.
bool GetSheetName(size_t sheetIndex, char *name)
Get the worksheet name at the given index. Index starts from 0. Returns false if name is in Unicode f...
char * GetAnsiSheetName(size_t sheetIndex)
Get the worksheet name at the given index. Index starts from 0. Returns 0 if name is in Unicode forma...
BasicExcelWorksheet * AddWorksheet(int sheetIndex=-1)
Add a new Excel worksheet to the given index. Name given to worksheet is SheetX, where X is a number ...
bool SaveAs(const char *filename)
Save current Excel workbook to a file.
void AdjustExtSSTPositions()
bool Save()
Save current Excel workbook to opened file.
Workbook workbook_
Raw Workbook.
bool RenameWorksheet(size_t sheetIndex, const char *to)
Rename an Excel worksheet at the given index to the given ANSI name. Index starts from 0....
size_t sheetIndex_
Index of worksheet in workbook.
std::vector< std::vector< BasicExcelCell > > cells_
Cells matrix.
size_t GetTotalCols()
Total number of columns in current Excel worksheet.
BasicExcelCell * Cell(size_t row, size_t col)
Return a pointer to an Excel cell. row and col starts from 0. Returns 0 if row exceeds 65535 or col e...
size_t maxRows_
Total number of rows in worksheet.
char * GetAnsiSheetName()
Get the current worksheet name. Returns 0 if name is in Unicode format.
bool Rename(const char *to)
Rename current Excel worksheet to another ANSI name. Returns true if successful, false if otherwise.
bool EraseCell(size_t row, size_t col)
Erase content of a cell. row and col starts from 0. Returns true if successful, false if row or col e...
BasicExcelWorksheet(BasicExcel *excel, size_t sheetIndex)
wchar_t * GetUnicodeSheetName()
Get the current worksheet name. Returns 0 if name is in Ansi format.
void Print(std::ostream &os, char delimiter=',', char textQualifier='\0')
Print entire worksheet to an output stream, separating each column with the defined delimiter and enc...
bool GetSheetName(char *name)
Get the current worksheet name. Returns false if name is in Unicode format.
BasicExcel * excel_
Pointer to instance of BasicExcel.
size_t maxCols_
Total number of columns in worksheet.
size_t GetTotalRows()
Total number of rows in current Excel worksheet.
void UpdateCells()
Update cells using information from BasicExcel.worksheets_.
virtual ~Record()
virtual size_t DataSize()
virtual size_t Write(char *data)
size_t recordSize_
Definition: BasicExcel.hpp:547
virtual size_t RecordSize()
std::vector< char > data_
Definition: BasicExcel.hpp:545
virtual size_t Read(const char *data)
std::vector< size_t > continueIndices_
Definition: BasicExcel.hpp:548
SharedStringTable sst_
Definition: BasicExcel.hpp:740
size_t Write(char *data)
std::vector< BoundSheet > boundSheets_
Definition: BasicExcel.hpp:739
std::vector< XF > XFs_
Definition: BasicExcel.hpp:737
std::vector< Style > styles_
Definition: BasicExcel.hpp:738
size_t Read(const char *data)
std::vector< Font > fonts_
Definition: BasicExcel.hpp:736
size_t Read(const char *data)
size_t Write(char *data)
CellTable cellTable_
Dimensions dimensions_
ostream & operator<<(ostream &os, const BasicExcelCell &cell)
int GetRKValueFromDouble(double value)
Convert a double to a rk value.
int GetIntegerFromRKValue(int rkValue)
Convert a rk value to an integer.
double GetDoubleFromRKValue(int rkValue)
Convert a rk value to a double.
bool CanStoreAsRKValue(double value)
Returns true if the supplied double can be stored as a rk value.
bool IsRKValueADouble(int rkValue)
Returns true if the supplied rk value contains a double.
int GetRKValueFromInteger(int value)
Convert an integer to a rk value.
bool IsRKValueAnInteger(int rkValue)
Returns true if the supplied rk value contains an integer.
CONSTCD11 std::enable_if<!std::chrono::treat_as_floating_point< T >::value, T >::type trunc(T t) NOEXCEPT
Definition: date.h:1113
char name[32]
Definition: resampler.cpp:371
std::string wcstombs(const std::wstring &wStr)
This function is a wrapper for the usual wcstombs function, which can handle wstrings.
static void ReadString(const char *buffer, Type *str, int pos=0, int bytes=0)
Definition: BasicExcel.hpp:121
static void WriteString(char *buffer, Type *str, int pos=0, int bytes=0)
Definition: BasicExcel.hpp:138
static void Read(const char *buffer, Type &retVal, int pos=0, int bytes=0)
Definition: BasicExcel.hpp:110
static void Write(char *buffer, Type val, int pos=0, int bytes=0)
Definition: BasicExcel.hpp:127
int fileHistoryFlags_
Definition: BasicExcel.hpp:560
virtual size_t Read(const char *data)
short buildYear_
Definition: BasicExcel.hpp:559
int lowestExcelVersion_
Definition: BasicExcel.hpp:561
short buildIdentifier_
Definition: BasicExcel.hpp:558
short version_
Definition: BasicExcel.hpp:556
virtual size_t Write(char *data)
LargeString & operator=(const LargeString &s)
std::vector< wchar_t > wname_
Definition: BasicExcel.hpp:603
size_t Read(const char *data)
size_t Write(char *data)
std::vector< char > name_
Definition: BasicExcel.hpp:604
size_t ContinueRead(const char *data, size_t size)
SmallString & operator=(const SmallString &s)
size_t Read(const char *data)
size_t Write(char *data)
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
virtual size_t DataSize()
virtual size_t RecordSize()
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
virtual size_t Read(const char *data)
virtual size_t RecordSize()
virtual size_t DataSize()
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
std::vector< LargeString > strings_
Definition: BasicExcel.hpp:715
virtual size_t Write(char *data)
virtual size_t DataSize()
virtual size_t Read(const char *data)
virtual size_t Write(char *data)
virtual size_t RecordSize()
virtual size_t Read(const char *data)
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
virtual size_t Read(const char *data)
std::vector< CellBlock > cellBlocks_
std::vector< RowBlock > rowBlocks_
size_t Write(char *data)
size_t Read(const char *data)
virtual size_t Write(char *data)
virtual size_t Read(const char *data)
virtual size_t Read(const char *data)
virtual size_t DataSize()
std::vector< size_t > DBCellPos_
Definition: BasicExcel.hpp:763
virtual size_t Write(char *data)
virtual size_t RecordSize()
virtual size_t Read(const char *data)
virtual size_t Write(char *data)