📄 osga_archive.cpp
字号:
#include <osg/Notify>#include <osg/Endian>#include <osgDB/Registry>#include <osgDB/FileNameUtils>#include "OSGA_Archive.h"using namespace osgDB;/*Functions to convert between std::streampos ( typedef'ed as iostream::pos_type used as seekp,seekg argument and tellp,tellg return value )and OSGA_Archive::pos_type (64 bit file position index)Purpose: To allow using OSGA files larger than 4GiB in Windows. std::streampos is used as argument to iostreams seekp and seekg methods and is returned as result from iostream tellp and tellg methods. std::streampos can be implicitly cast from/to std::streamoff as std::streampos class defines appropriate constructor and cast operator. Since std::streamoff is usually defined as simple int, it is possible to call seekp( ), seekg( ) with int argument and assign tellp(), tellg() result to int type. But this implicit methods fail when std::streamoff is 32 bit and std::streampos actually points past 32 bit addressable range (2 GiB). Even if std::streamoff is 32 bit and incapable of representing 64 bit file positions, original std::streampos may be prefectly able to handle them. But, when such situation occurs more elaborate conversion methods from/to std::streampos are needed. Functions below employ such methods. I made this fix for use with 32 bit Windows OSG. Acutally this solution is not dependent on OS but standard C++ library. Microsoft SDKs always use some version of Dinkumware libs. Practically this tweak is made for Dinkumware C++ libs. I hope it can be easily extended to other 32bit systems supporting 64bit files, provided their std::streampos implementations are similar. I based my solution on a small portion of boost iostreams code. For additional reference look at: http://boost.org/boost/iostreams/positioning.hpp*//* Recognize Dinkumware std C++ lib implementation. Its used by Microsoft, but method is more generic - should work in all Dinkumware environments. Complex condition below was taken from http://boost.org/boost/iostreams/positioning.hpp Great thanks to J.Tukanis and G. Sylvester-Bradley for figuring it out.*/#if ((defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER)) && \ !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION) \ && !defined(__QNX__)inline std::streampos STREAM_POS( const OSGA_Archive::pos_type pos ){ return std::streampos( std::mbstate_t(), pos );}inline OSGA_Archive::pos_type ARCHIVE_POS( const std::streampos & pos ){ #if defined(_CPPLIB_VER)//newer Dinkumware(eg: one included with VC++ 2003,2005) fpos_t position = pos.seekpos();#else // older Dinkumware (eg: one included in Win Server 2003 Platform SDK ) fpos_t position = pos.get_fpos_t();#endif std::streamoff offset = pos.operator std::streamoff( ) - _FPOSOFF( position ); return OSGA_Archive::pos_type( position + offset );}#else // non Dinkumware std C++ lib implementations // do the old school streampos <-> streamoff castsinline std::streampos STREAM_POS( const OSGA_Archive::pos_type pos ){ return std::streampos( pos );}inline OSGA_Archive::pos_type ARCHIVE_POS( const std::streampos & pos ){ return OSGA_Archive::pos_type( pos );}#endif // Dinkumware std C++ lib ////////////////////////////////////////////////////////////////////////////////float OSGA_Archive::s_currentSupportedVersion = 0.0;const unsigned int ENDIAN_TEST_NUMBER = 0x00000001;OSGA_Archive::IndexBlock::IndexBlock(unsigned int blockSize): _requiresWrite(false), _filePosition(0), _blockSize(0), _filePositionNextIndexBlock(0), _offsetOfNextAvailableSpace(0), _data(0){ allocateData(blockSize);}OSGA_Archive::IndexBlock::~IndexBlock(){ delete [] _data;}void OSGA_Archive::IndexBlock::allocateData(unsigned int blockSize){ _data = (blockSize!=0) ? new char[blockSize] : 0; if (_data) { _blockSize = blockSize; // initialize the array char* end = _data + _blockSize; for(char* ptr=_data; ptr < end; ++ptr) *ptr = 0; } else { _blockSize = 0; }}OSGA_Archive::IndexBlock* OSGA_Archive::IndexBlock::read(std::istream& in, bool doEndianSwap){ if (!in) return 0; osg::ref_ptr<IndexBlock> indexBlock = new IndexBlock; indexBlock->_filePosition = ARCHIVE_POS( in.tellg() ); in.read(reinterpret_cast<char*>(&indexBlock->_blockSize), sizeof(indexBlock->_blockSize)); in.read(reinterpret_cast<char*>(&indexBlock->_filePositionNextIndexBlock), sizeof(indexBlock->_filePositionNextIndexBlock)); in.read(reinterpret_cast<char*>(&indexBlock->_offsetOfNextAvailableSpace), sizeof(indexBlock-> _offsetOfNextAvailableSpace)); if (doEndianSwap) { osg::swapBytes(reinterpret_cast<char*>(&indexBlock->_blockSize), sizeof(indexBlock->_blockSize)); osg::swapBytes(reinterpret_cast<char*>(&indexBlock->_filePositionNextIndexBlock), sizeof(indexBlock->_filePositionNextIndexBlock)); osg::swapBytes(reinterpret_cast<char*>(&indexBlock->_offsetOfNextAvailableSpace), sizeof(indexBlock-> _offsetOfNextAvailableSpace)); }// osg::notify(osg::INFO)<<"indexBlock->_blockSize="<<indexBlock->_blockSize<<std::endl;// osg::notify(osg::INFO)<<"indexBlock->_filePositionNextIndexBlock="<<indexBlock->_filePositionNextIndexBlock<<std::endl;// osg::notify(osg::INFO)<<"indexBlock->_offsetOfNextAvailableSpace="<<indexBlock->_offsetOfNextAvailableSpace<<std::endl; indexBlock->allocateData(indexBlock->_blockSize); if (indexBlock->_data) { in.read(reinterpret_cast<char*>(indexBlock->_data),indexBlock->_blockSize); if (doEndianSwap) { char* ptr = indexBlock->_data; char* end_ptr = indexBlock->_data + indexBlock->_offsetOfNextAvailableSpace; while (ptr<end_ptr) { osg::swapBytes(ptr,sizeof(pos_type)); ptr += sizeof(pos_type); osg::swapBytes(ptr,sizeof(size_type)); ptr += sizeof(size_type); osg::swapBytes(ptr,sizeof(unsigned int)); unsigned int filename_size; // = *(reinterpret_cast<unsigned int*>(ptr)); _read(ptr, filename_size); ptr += sizeof(unsigned int); ptr += filename_size; osg::notify(osg::INFO)<<"filename size="<<filename_size<<std::endl; } } } else { osg::notify(osg::INFO)<<"Allocation Problem in OSGA_Archive::IndexBlock::read(std::istream& in)"<<std::endl; return 0; } osg::notify(osg::INFO)<<"Read index block"<<std::endl; return indexBlock.release(); }std::string OSGA_Archive::IndexBlock::getFirstFileName() const{ char* ptr = _data; char* end_ptr = _data + _offsetOfNextAvailableSpace; if (ptr<end_ptr) { ptr += sizeof(pos_type); ptr += sizeof(size_type); unsigned int filename_size; // = *(reinterpret_cast<unsigned int*>(ptr)); _read(ptr, filename_size); ptr += sizeof(unsigned int); return std::string(ptr, ptr+filename_size); } else { return std::string(); }}bool OSGA_Archive::IndexBlock::getFileReferences(FileNamePositionMap& indexMap) const{ if (!_data || _offsetOfNextAvailableSpace==0) return false; bool valuesAdded = false; char* ptr = _data; char* end_ptr = _data + _offsetOfNextAvailableSpace; while (ptr<end_ptr) { pos_type position; // = *(reinterpret_cast<pos_type*>(ptr)); _read(ptr, position); ptr += sizeof(pos_type); size_type size; // = *(reinterpret_cast<size_type*>(ptr)); _read(ptr, size); ptr += sizeof(size_type); unsigned int filename_size; // = *(reinterpret_cast<unsigned int*>(ptr)); _read(ptr, filename_size); ptr += sizeof(unsigned int); std::string filename(ptr, ptr+filename_size); // record this entry into the FileNamePositionMap. // Requests for files will be in unix style even on Win32 so need unix style keys in map. indexMap[osgDB::convertFileNameToUnixStyle(filename)] = PositionSizePair(position,size); ptr += filename_size; valuesAdded = true; } return valuesAdded;}void OSGA_Archive::IndexBlock::write(std::ostream& out){ pos_type currentPos = ARCHIVE_POS( out.tellp() ); if (_filePosition==pos_type(0)) { osg::notify(osg::INFO)<<"OSGA_Archive::IndexBlock::write() setting _filePosition"<<std::endl; _filePosition = currentPos; } else { out.seekp( STREAM_POS( _filePosition ) ); } osg::notify(osg::INFO)<<"OSGA_Archive::IndexBlock::write() to _filePosition"<< ARCHIVE_POS( out.tellp() )<<std::endl; out.write(reinterpret_cast<char*>(&_blockSize), sizeof(_blockSize)); out.write(reinterpret_cast<char*>(&_filePositionNextIndexBlock), sizeof(_filePositionNextIndexBlock)); out.write(reinterpret_cast<char*>(&_offsetOfNextAvailableSpace), sizeof(_offsetOfNextAvailableSpace)); out.write(reinterpret_cast<char*>(_data),_blockSize); if( _filePosition < currentPos ) // move file ptr to the end of file out.seekp( STREAM_POS( currentPos ) ); osg::notify(osg::INFO)<<"OSGA_Archive::IndexBlock::write() end"<<std::endl;}bool OSGA_Archive::IndexBlock::addFileReference(pos_type position, size_type size, const std::string& filename){ if (spaceAvailable(position, size, filename)) { char* ptr = _data+_offsetOfNextAvailableSpace; //*(reinterpret_cast<pos_type*>(ptr)) = position; _write(ptr, position); ptr += sizeof(pos_type); //*(reinterpret_cast<size_type*>(ptr)) = size; _write(ptr, size); ptr += sizeof(size_type); //*(reinterpret_cast<unsigned int*>(ptr)) = filename.size(); _write(ptr, static_cast<unsigned int>(filename.size())); ptr += sizeof(unsigned int); for(unsigned int i=0;i<filename.size();++i, ++ptr) { *ptr = filename[i]; } _offsetOfNextAvailableSpace = ptr-_data; _requiresWrite = true; osg::notify(osg::INFO)<<"OSGA_Archive::IndexBlock::addFileReference("<<(unsigned int)position<<", "<<filename<<")"<<std::endl; return true; } else { return false; }}void OSGA_Archive::IndexBlock::setPositionNextIndexBlock(pos_type position){ _filePositionNextIndexBlock = position; _requiresWrite = true;}OSGA_Archive::OSGA_Archive(){}OSGA_Archive::~OSGA_Archive(){ close();}bool OSGA_Archive::open(const std::string& filename, ArchiveStatus status, unsigned int indexBlockSize){ SERIALIZER(); if (status==READ) { _status = status; _input.open(filename.c_str(), std::ios_base::binary | std::ios_base::in); return _open(_input); } else { if (status==WRITE && open(filename,READ)) { pos_type file_size( 0 ); _input.seekg( 0, std::ios_base::end ); file_size = ARCHIVE_POS( _input.tellg() ); if( _input.is_open() && file_size <= 0 ) { // compute end of file postition manually ... // seekp( 0, ios::end ), tellp( ) fails in 32 bit windows with files > 4 GiB size_t BlockHeaderSize = sizeof( unsigned int /*_blockSize*/ ) + sizeof( pos_type /*_filePositionNextIndexBlock*/ ) + sizeof( unsigned int /*_offsetOfNextAvailableSpace*/ ); for(IndexBlockList::iterator itr=_indexBlockList.begin(); itr!=_indexBlockList.end(); ++itr) { pos_type end = (*itr)->getPosition() + BlockHeaderSize + (*itr)->getBlockSize(); if( file_size < end ) file_size = end; } for(FileNamePositionMap::iterator mitr=_indexMap.begin(); mitr!=_indexMap.end(); ++mitr) { pos_type end = mitr->second.first + mitr->second.second; if( file_size < end ) file_size = end; } } _input.close(); _status = WRITE; _output.open(filename.c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::out); osg::notify(osg::INFO)<<"File position after open = "<<ARCHIVE_POS( _output.tellp() )<<" is_open "<<_output.is_open()<<std::endl; // place write position at end of file. _output.seekp( STREAM_POS( file_size ) ); osg::notify(osg::INFO)<<"File position after seekp = "<<ARCHIVE_POS( _output.tellp() )<<std::endl; osg::notify(osg::INFO)<<"OSGA_Archive::open("<<filename<<") open for writing"<<std::endl; return true; } else // no file opened or using create so resort to creating the archive. { osg::notify(osg::INFO)<<"OSGA_Archive::open("<<filename<<"), archive being created."<<std::endl; _status = WRITE; _output.open(filename.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); _output<<"osga"; _output.write(reinterpret_cast<const char*>(&ENDIAN_TEST_NUMBER),4); _output.write(reinterpret_cast<char*>(&s_currentSupportedVersion),sizeof(float)); IndexBlock *indexBlock = new IndexBlock(indexBlockSize); if (indexBlock) { indexBlock->write(_output); _indexBlockList.push_back(indexBlock); } osg::notify(osg::INFO)<<"File position after write = "<<ARCHIVE_POS( _output.tellp() )<<std::endl; return true; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -