/* This file is part of the wvWare 2 project Copyright (C) 2003 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef WORD_HELPER_H #define WORD_HELPER_H #include "olestream.h" #include "wvlog.h" #include #include namespace wvWare { namespace Word97 { class PHE; class BTE; } namespace Word95 { class PHE; class BTE; Word97::PHE toWord97( const Word95::PHE& phe ); // fake, to make gcc 3.4 happy :-( Word97::BTE toWord97( const Word95::BTE& s ); // fake, to make gcc 3.4 happy :-( } class OLEStreamReader; class OLEStreamWriter; class ParagraphProperties; class UString; class STTBF { public: STTBF( U16 lid, OLEStreamReader* reader, bool preservePos = false ); STTBF( U16 lid, const U8* ptr ); STTBF( const STTBF& rhs ); ~STTBF(); unsigned int count() const; unsigned int extraDataLength() const { return m_extraDataLength; } UString firstString() const; UString nextString() const; UString prevString() const; UString lastString() const; UString stringAt( unsigned int index ) const; const U8* firstExtra() const; const U8* nextExtra() const; const U8* prevExtra() const; const U8* lastExtra() const; const U8* extraAt( unsigned int index ) const; void dumpStrings() const; private: STTBF& operator=( const STTBF& rhs ); // Internal helper methods to avoid code duplication in the CTORs void init( U16 lid, OLEStreamReader* reader, const U8* ptr ); U16 readU16( OLEStreamReader* reader, const U8** ptr ) const; U8 readU8( OLEStreamReader* reader, const U8** ptr ) const; bool read( OLEStreamReader* reader, const U8** ptr, U8* buffer, size_t length ) const; std::vector m_strings; mutable std::vector::const_iterator m_stringIt; U16 m_extraDataLength; std::vector m_extraData; mutable std::vector::const_iterator m_extraIt; }; // Attention: structs used as template parameters for this class need to // enable the sizeOf field in the code generator (see comment (7)) in // the header of generate.pl // Use the short count for "broken" plfs like the LSTF one (surprisingly // labeled plcflst(!) (see spec_defects for more information) template class PLF { public: PLF( OLEStreamReader* reader, bool preservePos = false ); PLF( const U8* ptr ); ~PLF(); size_t count() const { return m_items.size(); } const T* first() const { it = m_items.begin(); if ( it != m_items.end() ) return *it; return 0; } const T* next() const; const T* prev() const; const T* last() const { it = m_items.end(); if ( it == m_items.begin() ) return 0; --it; return *it; } const T* current() const { if ( it != m_items.end() ) return *it; return 0; } const T* at( unsigned int index ) const { if ( index < m_items.size() ) return m_items[ index ]; return 0; } private: // don't copy or assign it PLF( const PLF& rhs ); PLF& operator=( const PLF& rhs ); std::vector m_items; mutable typename std::vector::const_iterator it; }; template PLF::PLF( OLEStreamReader* reader, bool preservePos ) { if ( preservePos ) reader->push(); U32 count = 0; if ( shortCount ) // work around a broken spec, e.g. for LSTF count = reader->readU16(); else count = reader->readU32(); for ( U32 i = 0; i < count; ++i ) m_items.push_back( new T( reader, false ) ); if ( preservePos ) reader->pop(); it = m_items.begin(); } template PLF::PLF( const U8* ptr ) { U32 count = 0; if ( shortCount ) { // work around a broken spec, e.g. for LSTF count = readU16( ptr ); ptr += 2; } else { count = readU32( ptr ); ptr += 4; } for ( U32 i = 0; i < count; ++i, ptr += T::sizeOf ) m_items.push_back( new T( ptr ) ); it = m_items.begin(); } template PLF::~PLF() { for ( it = m_items.begin(); it != m_items.end(); ++it ) delete *it; } template const T* PLF::next() const { if ( it == m_items.end() ) return 0; ++it; if ( it != m_items.end() ) return *it; return 0; } template const T* PLF::prev() const { if ( m_items.size() == 0 ) return 0; if ( it != m_items.begin() ) --it; return *it; } template class PLCF; template class PLCFIterator; template PLCF* convertPLCF( const PLCF& old ); // evil, eh? :-) template class PLCF { friend class PLCFIterator; template friend PLCF* convertPLCF( const PLCF& old ); public: PLCF( U32 length, OLEStreamReader *reader, bool preservePos = false ); PLCF( U32 length, const U8* ptr ); PLCF( const PLCF& rhs ); ~PLCF(); PLCFIterator at( unsigned int index ) const; size_t count() const { return m_items.size(); } bool isEmpty() const { return m_items.empty(); } // Inserts the given index/item pair at the end of the PLCF (but before(!) the final n+1 index) // Normally you won't need that method. The ownership of the item is transferred. void insert( U32 index, T* item ); void dumpCPs() const; private: // don't assign it PLCF& operator=( const PLCF& rhs ); // An empty default constructor for the convertPLCF friend. Don't use it // unless you know what you are doing :-) PLCF() {} U32 calculateCount( U32 length ); std::vector m_indices; std::vector m_items; }; template PLCF::PLCF( U32 length, OLEStreamReader* reader, bool preservePos ) { if ( preservePos ) reader->push(); U32 count = calculateCount( length ); for ( U32 i = 0; i < count + 1; ++i ) // n+1 CPs/FCs m_indices.push_back( reader->readU32() ); for ( U32 i = 0; i < count; ++i ) // n "T"s m_items.push_back( new T( reader, false ) ); if ( preservePos ) reader->pop(); } template PLCF::PLCF( U32 length, const U8* ptr ) { U32 count = calculateCount( length ); for ( U32 i = 0; i < count + 1; ++i, ptr += 4 ) // n+1 CPs/FCs m_indices.push_back( readU32( ptr ) ); for ( U32 i = 0; i < count; ++i, ptr += T::sizeOf ) // n "T"s m_items.push_back( new T( ptr ) ); } template PLCF::PLCF( const PLCF& rhs ) : m_indices( rhs.m_indices ) { typename std::vector::const_iterator it = rhs.m_items.begin(); typename std::vector::const_iterator end = rhs.m_items.end(); for ( ; it != end; ++it ) m_items.push_back( new T( **it ) ); } template PLCF::~PLCF() { typename std::vector::const_iterator it = m_items.begin(); for ( ; it != m_items.end(); ++it ) delete *it; } template PLCFIterator PLCF::at( unsigned int index ) const { PLCFIterator it( *this ); it.m_itemIt += index; it.m_indexIt += index; return it; } template void PLCF::insert( U32 index, T* item ) { if ( m_indices.empty() ) { delete item; return; } std::vector::iterator it( m_indices.end() ); --it; m_indices.insert( it, index ); m_items.push_back( item ); } template void PLCF::dumpCPs() const { wvlog << "PLCF: count=" << count() << std::endl; std::vector::const_iterator it = m_indices.begin(); std::vector::const_iterator end = m_indices.end(); for ( ; it != end; ++it ) wvlog << " " << ( *it ) << std::endl; wvlog << "PLCF done." << std::endl; } template U32 PLCF::calculateCount( U32 length ) { if ( ( length - 4 ) % ( T::sizeOf + 4 ) ) { wvlog << "Warning: PLCF size seems to be screwed" << std::endl; wvlog << "Warning: length: " << length << ", size: " << T::sizeOf << ", mod: " << ( length - 4 ) % ( T::sizeOf + 4 ) << std::endl; return 0; } return ( length - 4 ) / ( T::sizeOf + 4 ); } // A method to "upgrade" the type of a PLCF from Word 6/7 to Word 8 // data structures, using the generated conversion code. Tricky :-) // The ownership of the new PLCF is transferred to you! template PLCF* convertPLCF( const PLCF& old ) { PLCF* ret( new PLCF ); ret->m_indices = old.m_indices; // the indices remain the same typename std::vector::const_iterator oldIt( old.m_items.begin() ); typename std::vector::const_iterator oldEnd( old.m_items.end() ); for ( ; oldIt != oldEnd; ++oldIt ) ret->m_items.push_back( new NewT( Word95::toWord97( **oldIt ) ) ); return ret; } template class PLCFIterator { friend PLCFIterator PLCF::at( unsigned int ) const; public: PLCFIterator( const PLCF& plcf ) : m_plcf( plcf ) { m_itemIt = m_plcf.m_items.begin(); m_indexIt = m_plcf.m_indices.begin(); } unsigned int count() const { return m_plcf.m_items.count(); } bool isEmpty() const { return m_plcf.m_items.count() == 0; } T* toFirst(); T* toLast(); U32 currentStart() const { if ( m_itemIt != m_plcf.m_items.end() ) return *m_indexIt; return 0; } U32 currentLim() const; T* current() const { if ( m_itemIt != m_plcf.m_items.end() ) return *m_itemIt; return 0; } U32 currentRun() const { return currentLim() - currentStart(); } PLCFIterator& operator++(); PLCFIterator& operator--(); private: // don't assign it PLCFIterator& operator=( const PLCFIterator& rhs ); const PLCF& m_plcf; typename std::vector::const_iterator m_itemIt; std::vector::const_iterator m_indexIt; }; template T* PLCFIterator::toFirst() { m_itemIt = m_plcf.m_items.begin(); m_indexIt = m_plcf.m_indices.begin(); if ( m_itemIt != m_plcf.m_items.end() ) return *m_itemIt; return 0; } // Note: m_indexIt-=2 as we have n+1 indices! template T* PLCFIterator::toLast() { m_itemIt = m_plcf.m_items.end(); m_indexIt = m_plcf.m_indices.end(); if ( m_itemIt == m_plcf.m_items.begin() ) return 0; --m_itemIt; m_indexIt -= 2; return *m_itemIt; } template U32 PLCFIterator::currentLim() const { std::vector::const_iterator it = m_indexIt; if ( m_itemIt == m_plcf.m_items.end() ) return 0; ++it; return *it; } template PLCFIterator& PLCFIterator::operator++() { if ( m_itemIt == m_plcf.m_items.end() ) return *this; ++m_itemIt; ++m_indexIt; return *this; } template PLCFIterator& PLCFIterator::operator--() { if ( m_plcf.m_items.size() != 0 && m_itemIt != m_plcf.m_items.begin() ) { --m_itemIt; --m_indexIt; } return *this; } template struct BX; template class FKP; template class FKPIterator; FKP< BX >* convertFKP( const FKP< BX >& old ); template class FKP { friend class FKPIterator; friend FKP< BX >* convertFKP( const FKP< BX >& old ); public: FKP( OLEStreamReader* reader, bool preservePos = false ); FKP( const U8* ptr ); FKP( const FKP& rhs ); ~FKP() { delete [] m_rgfc; delete [] m_rgb; delete [] m_fkp; } unsigned int crun() const { return m_crun; } bool isEmpty() const { return m_crun == 0; } private: // don't assign it FKP& operator=( const FKP& rhs ); // An empty default constructor for the convertFKP friend. Don't use it // unless you know what you are doing :-) FKP() {} U8 m_crun; U32* m_rgfc; // array of FCs (crun+1) Offset* m_rgb; // array of offsets/BXs U16 m_internalOffset; // offset to the start position of the "rest" U8* m_fkp; // the "rest" of the FKP }; template FKP::FKP( OLEStreamReader* reader, bool preservePos ) { if ( preservePos ) reader->push(); reader->push(); reader->seek( 511, G_SEEK_CUR ); m_crun = reader->readU8(); reader->pop(); m_rgfc = new U32[ m_crun + 1 ]; for ( U8 i = 0; i <= m_crun; ++i ) // <= crun, because of crun+1 FCs! m_rgfc[ i ] = reader->readU32(); m_rgb = new Offset[ m_crun ]; for ( U8 i = 0; i < m_crun; ++i ) m_rgb[ i ].read( reader, false ); m_internalOffset = ( static_cast( m_crun ) + 1 ) * 4 + static_cast( m_crun ) * Offset::sizeOf; // store the rest of the FKP in an internal array for later use const U16 length = 511 - m_internalOffset; m_fkp = new U8[ length ]; // 511, because we don't need crun for ( U16 i = 0; i < length; ++i ) m_fkp[ i ] = reader->readU8(); if ( preservePos ) reader->pop(); } template FKP::FKP( const U8* ptr ) { m_crun = ptr[ 511 ]; m_rgfc = new U32[ m_crun + 1 ]; for ( U8 i = 0; i <= m_crun; ++i, ptr += 4 ) // <= crun, because of crun+1 FCs! m_rgfc[ i ] = readU32( ptr ); m_rgb = new Offset[ m_crun ]; for ( U8 i = 0; i < m_crun; ++i, ptr += Offset::sizeOf ) m_rgb[ i ].readPtr( ptr ); m_internalOffset = ( static_cast( m_crun ) + 1 ) * 4 + static_cast( m_crun ) * Offset::sizeOf; // store the rest of the FKP in an internal array for later use U16 length = 511 - m_internalOffset; m_fkp = new U8[ length ]; // 511, because we don't need crun for ( U16 i = 0; i < length; ++i, ++ptr ) m_fkp[ i ] = *ptr; } template FKP::FKP( const FKP& rhs ) : m_crun( rhs.m_crun ), m_internalOffset( rhs.m_internalOffset ) { m_rgfc = new U32[ m_crun + 1 ]; ::memcpy( m_rgfc, rhs.m_rgfc, sizeof( U32 ) * ( m_crun + 1 ) ); m_rgb = new Offset[ m_crun ]; for ( U8 i = 0; i < m_crun; ++i ) m_rgb[ i ] = rhs.m_rgb[ i ]; const U16 length = 511 - m_internalOffset; m_fkp = new U8[ length ]; // 511, because we don't need crun ::memcpy( m_fkp, rhs.m_fkp, sizeof( U8 ) * length ); } // Attention: This iterator has a non standard behavior of the current() // method. Use it that way: for( ; !it.atEnd(); ++it) template class FKPIterator { public: FKPIterator( const FKP& fkp ) : m_fkp( fkp ), m_index( 0 ) {} void toFirst() { m_index = 0; } void toLast() { m_index = m_fkp.m_crun - 1; } U32 currentStart() const { if ( m_index < m_fkp.m_crun ) return m_fkp.m_rgfc[ m_index ]; return 0; } U32 currentLim() const { if ( m_index < m_fkp.m_crun ) return m_fkp.m_rgfc[ m_index + 1 ]; return 0; } Offset currentOffset() const { if ( m_index < m_fkp.m_crun ) return m_fkp.m_rgb[ m_index ]; return Offset(); } // Pointer to the start of the current CHPX/PAPX/..., 0 if we are at the end of the array // Attention: This iterator has a non standard behavior of the current() // method. Use it that way: for( ; !it.atEnd(); ++it) const U8* current() const; FKPIterator& operator++() { if ( m_index < m_fkp.m_crun ) ++m_index; return *this; } FKPIterator& operator--() { if ( m_index > 0 ) --m_index; return *this; } U8 index() const { return m_index; } void setIndex( U8 index ) { if ( index < m_fkp.m_crun ) m_index = index; } bool atEnd() { return m_index >= m_fkp.m_crun; } private: // don't copy or assign it FKPIterator( const FKPIterator& rhs ); FKPIterator& operator=( const FKPIterator& rhs ); const FKP& m_fkp; U8 m_index; }; template const U8* FKPIterator::current() const { if ( m_index < m_fkp.m_crun ) { // Note: The first byte of the "offset" types (BX or U8) is always // the word offset into the array (or 0!). const U8 tmp = m_fkp.m_rgb[ m_index ].offset; // Now we have to calculate the real offset and then locate it // within our cached array... if ( tmp != 0 ) { const int pos = tmp * 2 - m_fkp.m_internalOffset; if ( pos < 0 ) { wvlog << "ERROR: FKP internalOffset (" << m_fkp.m_internalOffset << ") is bigger than " << "2*" << (int)tmp << ", FKP array index would be negative!" << std::endl; return 0; } else if ( pos >= 511 - m_fkp.m_internalOffset ) { wvlog << "ERROR: FKP array index (" << pos << " is bigger than allocated size (" << 511 - m_fkp.m_internalOffset << ")" << std::endl; return 0; } else { return &m_fkp.m_fkp[ pos ]; } } } return 0; } /** * BX entry in a PAP FKP (1 byte word offset + 1 PHE). This is a template, as Word 95 * PHEs are shorter than Word 97 ones. */ template struct BX { /** * Creates an empty BX structure and sets the defaults */ BX() { clear(); } /** * Simply calls read(...) */ BX( OLEStreamReader* stream, bool preservePos = false ) { clear(); read( stream, preservePos ); } /** * Simply calls readPtr(...) */ BX( const U8* ptr ) { clear(); readPtr( ptr ); } /** * This method reads the BX structure from the stream. * If preservePos is true we push/pop the position of * the stream to save the state. If it's false the state * of stream will be changed! */ bool read( OLEStreamReader* stream, bool preservePos = false ) { if ( preservePos ) stream->push(); offset = stream->readU8(); phe.read( stream, false ); if ( preservePos ) stream->pop(); return true; } /** * This method reads the struct from a pointer */ void readPtr( const U8* ptr ) { offset = *ptr; ++ptr; phe.readPtr( ptr ); } /** * Same as reading :) */ bool write( OLEStreamWriter* stream, bool preservePos = false ) const { if ( preservePos ) stream->push(); stream->write( offset ); phe.write( stream, false ); if ( preservePos ) stream->pop(); return true; } /** * Set all the fields to the inital value (default is 0) */ void clear() { offset = 0; phe.clear(); } // Size of the structure static const unsigned int sizeOf; // Data /** * One byte offset. This byte is a word offset, so that we can * address 512 bytes with one unsigned char and all the CHPX/PAPX * grpprls are aligned at 2 byte boundaries. */ U8 offset; /** * The PHE for the paragraph described by this BX structure. */ PHE phe; }; template const unsigned int BX::sizeOf = 1 + PHE::sizeOf; template bool operator==( const BX& lhs, const BX& rhs ) { return lhs.offset == rhs.offset && lhs.phe == rhs.phe; } template bool operator!=( const BX& lhs, const BX& rhs ) { return !( lhs == rhs ); } /** * A fake "BX" entry in a CHP FKP (1 byte word offset only). */ struct CHPFKP_BX { /** * Creates an empty CHPFKP_BX structure and sets the defaults */ CHPFKP_BX(); /** * Simply calls read(...) */ CHPFKP_BX( OLEStreamReader* stream, bool preservePos = false ); /** * Simply calls readPtr(...) */ CHPFKP_BX( const U8* ptr ); /** * This method reads the CHPFKP_BX structure from the stream. * If preservePos is true we push/pop the position of * the stream to save the state. If it's false the state * of stream will be changed! */ bool read( OLEStreamReader* stream, bool preservePos = false ); /** * This method reads the struct from a pointer */ void readPtr( const U8* ptr ); /** * Same as reading :) */ bool write( OLEStreamWriter* stream, bool preservePos = false ) const; /** * Set all the fields to the inital value (default is 0) */ void clear(); // Size of the structure static const unsigned int sizeOf; // Data /** * One byte offset. This byte is a word offset, so that we can * address 512 bytes with one unsigned char and all the CHPX/PAPX * grpprls are aligned at 2 byte boundaries. */ U8 offset; }; bool operator==( const CHPFKP_BX& lhs, const CHPFKP_BX& rhs ); bool operator!=( const CHPFKP_BX& lhs, const CHPFKP_BX& rhs ); // This enum is a "convenience enum" for reading the piece table typedef enum { clxtGrpprl = 1, clxtPlcfpcd = 2 } clxtENUM; } // namespace wvWare #endif // WORD_HELPER_H