/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This file is part of xlslib -- A multiplatform, C/C++ library * for dynamic generation of Excel(TM) files. * * Copyright 2004 Yeico S. A. de C. V. All Rights Reserved. * Copyright 2008-2011 David Hoerl All Rights Reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define OLE_DEBUG 0 #include "../xlslib/record.h" #include "../xlslib/datast.h" #include "../xlslib/extformat.h" #include "../oledoc/oledoc.h" using namespace std; using namespace xlslib_core; /* *********************************** * COleDoc class implementation *********************************** */ const unsigned8_t COleDoc::OLE_FILETYPE[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}; COleDoc::COleDoc() { } #if 0 COleDoc::COleDoc(const string& file_name) { Open(file_name); } #endif COleDoc::~COleDoc() { } int COleDoc::DumpHeader(blocks bks, size_t total_data_size) { size_t i; size_t total_data_blocks; size_t sectorID; size_t msatID; int errcode = NO_ERRORS; total_data_blocks = total_data_size/BIG_BLOCK_SIZE; #if OLE_DEBUG std::cerr << "dataBlocks=" << total_data_blocks << std::endl; #endif // [00]FILETYPE WriteByteArray(COleDoc::OLE_FILETYPE, sizeof(COleDoc::OLE_FILETYPE)); // [08]UK1 WriteSigned32(HEADVAL_DFLT_UK1); // [0c]UK2 WriteSigned32(HEADVAL_DFLT_UK2); // [10]UK2b WriteSigned32(HEADVAL_DFLT_UK2b); // [14]UK3 WriteSigned32(HEADVAL_DFLT_UK3); // [18]UK4 WriteSigned16(HEADVAL_DFLT_UK4); // [1a]UK5 WriteSigned16(HEADVAL_DFLT_UK5); // [1c]UK6 WriteSigned16(HEADVAL_DFLT_UK6); // [1e]LOG_2_BIG_BLOCK WriteSigned16(HEADVAL_DFLT_LOG2_BIGBLOCK); // [20]LOG_2_SMALL_BLOCK WriteSigned32(HEADVAL_DFLT_LOG2_SMALLBLOCK); // [24]UK7 WriteSigned32(HEADVAL_DFLT_UK7); // [28]UK8 WriteSigned32(HEADVAL_DFLT_UK8); // [2c] BAT_COUNT (BBDEPOT NUM BLOCKS) WriteUnsigned32((unsigned32_t)bks.bat_count); //[30] PROPERTIES_START_BLOCK // Since the big block depot will go immediately after the data, I need // to know the size of the data and the size of the BAT in blocks (prev) WriteUnsigned32((unsigned32_t)(bks.msat_count+total_data_blocks+bks.bat_count)); #if OLE_DEBUG std::cerr << "HEADER says directory at " << (bks.msat_count+total_data_blocks+bks.bat_count) << std::endl; #endif // [34] UK9 WriteSigned32(HEADVAL_DFLT_UK9); // [38] UK10 WriteSigned32(HEADVAL_DFLT_UK10); // [3c] SBAT_START // No small blocks will be used, so this is set to the default empty value WriteSigned32(HEADVAL_DFLT_SBAT_START); // [40] SBAT_BLOCKCOUNT_NUMBER // Use the default value WriteSigned32(HEADVAL_DFLT_SBAT_COUNT); // [44] XBAT_START // we will use first and possibly additional blocks for large files WriteSigned32(bks.msat_count ? 0 : HEADVAL_DFLT_XBAT_START); #if OLE_DEBUG std::cerr << "xbatStart=" << (bks.msat_count ? 0 : HEADVAL_DFLT_XBAT_START) << std::endl; #endif // [48] XBAT_COUNT WriteUnsigned32((unsigned32_t)bks.msat_count); // was HEADVAL_DFLT_XBAT_COUNT (0) #if OLE_DEBUG std::cerr << "msat_count=" << bks.msat_count << std::endl; #endif // [4C] BAT_ARRAY // The BAT_ARRAY shall be calculated from the number of BAT blocks and their position // The additional blocks, if needed, are directly below the header block, so we can write // them out contiguously. The special conditions are: // * for each MSAT block, the last entry needs to be a pointer to the next block // * the fill is -1 for all unused entries // * if there are MSAT blocks, the very last entry in the last block is a special marker // first sector ID sectorID = bks.msat_count + total_data_blocks; for(i=0; iGetType() == PTYPE_FILE) { for(StoreList_Itor_t j = (*i)->GetDataPointer()->begin(); j != (*i)->GetDataPointer()->end(); j++) { //unsigned short *val = (unsigned short *)(j->GetBuffer()); //printf("POS=%u wrote=%lu HEX=0x%4.4x\n", Position(), j->GetDataSize(), *val); XL_ASSERT(j->GetBuffer() != NULL); //XL_ASSERT(j->GetDataSize() > 0); errcode = WriteByteArray(j->GetBuffer(), j->GetDataSize()); if (errcode != NO_ERRORS) { break; } } } } return errcode; } int COleDoc::DumpDepots(blocks bks) { int errcode = NO_ERRORS; NodeList_t node_list; GetAllNodes(node_list); signed32_t bat_index; bat_index = 0; // tells Excel that these are used by the MSAT for(size_t i=0; iSetStartBlock(bat_index); // Write the chain for this node element data_size = (*node)->GetDataPointer()->GetDataSize(); chain_len = data_size/BIG_BLOCK_SIZE - 1; #if OLE_DEBUG std::cerr << "NODE[" << foo++ << "]: start_block=" << bat_index << " dataSize=" << data_size << " Sectors=" << chain_len + 1 /* directory_terminator */ << std::endl; #endif for(size_t i = 0; i < chain_len; i++) { WriteSigned32(++bat_index); ++bks._bat_entries; } // Set the terminator number WriteSigned32(BAT_END_CHAIN); ++bat_index; ++bks._bat_entries; } #if OLE_DEBUG std::cerr << "BAT_SELF_PLACE=" << (bat_index+1) << " -> " << (bat_index+1+bks.bat_count+1) << " TOTAL=" << bks.bat_count << std::endl; #endif // Write the -3 number for every index in the BAT that references to some BAT block (uh!?) for(size_t i=0; i bat_block_capacity || bat_num_blocks != bat_blocks_needed) { bat_num_blocks = bat_blocks_needed; if(bat_num_blocks > HEADER_SAT_SIZE) { msat_bats = bat_num_blocks - HEADER_SAT_SIZE; msat_blocks = msat_bats/BAT_BLOCKS_PER_MSAT_BLOCK; if(msat_bats % BAT_BLOCKS_PER_MSAT_BLOCK) {++msat_blocks; } } bat_num_entries = msat_blocks + data_bat_entries + bat_num_blocks + dir_bat_entries; // bat_bat_entries // based on what we know now, this is what we need bat_blocks_needed = bat_num_entries/BAT_ENTRIES_PER_BLOCK; if(bat_num_entries % BAT_ENTRIES_PER_BLOCK) {++bat_blocks_needed; } // number of slots available bat_block_capacity = HEADER_SAT_SIZE + BAT_BLOCKS_PER_MSAT_BLOCK*msat_blocks; #if OLE_DEBUG std::cerr << "bat_blocks=" << bat_num_blocks << " capacity=" << bat_block_capacity << " needed=" << bat_blocks_needed << std::endl; #endif } if(bat_num_blocks > HEADER_SAT_SIZE) { extra_bats = bat_num_blocks - HEADER_SAT_SIZE; bks.msat_count = msat_blocks; bks.header_bat_count = HEADER_SAT_SIZE; bks.extra_bat_count = extra_bats; last_block_extras = extra_bats % BAT_BLOCKS_PER_MSAT_BLOCK; if(last_block_extras) { bks.extra_fill = BAT_BLOCKS_PER_MSAT_BLOCK - last_block_extras; } } else { bks.header_bat_count = bat_num_blocks; bks.header_fill = HEADER_SAT_SIZE - bat_num_blocks; } bks.bat_entries = bat_num_entries; bks.bat_count = bat_num_blocks; #if OLE_DEBUG std::cerr << "entries=" << bks.bat_entries << " bats=" << bks.bat_count << " msats=" << bks.msat_count << " headerBats=" << bks.header_bat_count << " extraBats=" << bks.extra_bat_count << " headFill=" << bks.header_fill << " extraFill=" << bks.extra_fill << std::endl; #endif return bks; } // NOTE: name_unicode has to be deleted after this function finishes. // Ideally, this function should be implemented as part of a std::string // derived class, so the array would be deleted automatically size_t COleDoc::GetUnicodeName(const char* name, char** ppname_unicode) { size_t name_size = strlen(name); if(name_size > PROPERTY_MAX_NAME_LENGTH) { name_size = PROPERTY_MAX_NAME_LENGTH; } size_t size_unicode = (name_size+1)*2; if(*ppname_unicode != NULL) { delete[] *ppname_unicode; } *ppname_unicode = (char*)new unsigned8_t[size_unicode]; memset(*ppname_unicode, 0x00, size_unicode); for(size_t i=0; i<(size_unicode/2-1); i++) { (*ppname_unicode)[2*i] = name[i]; } return size_unicode; } int COleDoc::DumpNode(COleProp& node) { int errcode = NO_ERRORS; char* name_unicode = NULL; // Get the unicode name and its size size_t size_name = GetUnicodeName(node.GetName().c_str(), &name_unicode); // [00] PROPERTY_NAME WriteByteArray((const unsigned8_t*)name_unicode, size_name); // Fill the rest of the name field with 0x00 XL_ASSERT(PPTPOS_NAMELENGTH > size_name); SerializeFixedArray(PROPERTY_DFLT_NOTUSED, PPTPOS_NAMELENGTH - size_name); // [40] NAME_SIZE WriteSigned16((signed16_t)size_name); // [42] PROPERTY_TYPE WriteByte(node.GetType()); // [43] NODE_COLOR WriteByte(node.GetColor()); // [44] PREVIOUS_PROP WriteSigned32(node.GetPreviousIndex()); // [48] NEXT_PROP WriteSigned32(node.GetNextIndex()); // [4c] CHILD_PROP WriteSigned32(node.GetChildIndex()); // printf("NAME: %s TYPE=%d Color=%d Indexes: %4.4x %4.4x %4.4x\n", node.GetName().c_str(), node.GetType(), node.GetColor(), node.GetPreviousIndex(), node.GetNextIndex(), node.GetChildIndex() ); // Fill empty block SerializeFixedArray(PROPERTY_DFLT_NOTUSED, (PPTPOS_SECS1 - PPTPOS_UNUSED_EMPTY0)); //[64]...[70] // SECONDS_1, DAYS_2, SECONDS_2, DAYS_2 WriteSigned32(node.GetCreatedSecs()); WriteSigned32(node.GetCreatedDays()); WriteSigned32(node.GetModifiedDays()); WriteSigned32(node.GetModifiedSecs()); // [74] START_BLOCK #if OLE_DEBUG std::cerr << "START_BLOCK_1=" << node.GetStartBlock() << std::endl; #endif WriteSigned32(node.GetStartBlock()); // [78] SIZE if(node.GetType() == PTYPE_FILE) { WriteSigned32((signed32_t)node.GetSize()); } else { WriteSigned32(0); } // A unused space: WriteSigned32(PROPERTY_DFLT_NOTUSED); delete[] name_unicode; name_unicode = NULL; return errcode; }