record goes. I added a new function to read a continued record body. I added code in the existing ReadRecord routine to peek ahead for a CONTINUATION_OP if the current record could possibly be continued. opcodes.h -- Besides adding the opcode for CONTINUATION_OP, I also added new 15.8 opcodes. I labeled opcodes as "ignored" if I could easily discern that our loader wasn't doing anything with them. For historical reasons, I added all obsolete opcodes, prefixed with "OBS_". LocalVertexPoolRecord.h, LocalVertexPoolRecord.cpp -- This is one of three types of records that can be continued with a CONTINUATION_OP record. I removed all invalid assertions that assumed the record length would always be less than 65535. I replaced the "vertex size" calculation with a more efficient method based on caching the size from attribute bits, rather than taking the length of the record and dividing it by numVerts (which would have been incorrect if the record had been continued)."
264 lines
5.5 KiB
C++
264 lines
5.5 KiB
C++
// Input.cpp
|
|
|
|
#include <string>
|
|
// #include <malloc.h>
|
|
#include <assert.h>
|
|
|
|
#include <osg/Notify>
|
|
#include <osgDB/FileUtils>
|
|
|
|
#include "Input.h"
|
|
#include "Record.h"
|
|
#include "Registry.h"
|
|
|
|
using namespace flt;
|
|
|
|
FileInput::FileInput()
|
|
{
|
|
_init();
|
|
}
|
|
|
|
|
|
FileInput::~FileInput()
|
|
{
|
|
close();
|
|
}
|
|
|
|
|
|
void FileInput::_init()
|
|
{
|
|
_lRecOffset = 0L;
|
|
_file = NULL;
|
|
_eof = true;
|
|
}
|
|
|
|
|
|
size_t FileInput::_read(void *buffer, size_t size)
|
|
{
|
|
if (_eof) return 0;
|
|
|
|
size_t nItemsRead = ::fread(buffer, size, 1, _file);
|
|
if (nItemsRead != 1)
|
|
_eof = true;
|
|
|
|
return nItemsRead;
|
|
}
|
|
|
|
|
|
bool FileInput::eof()
|
|
{
|
|
return _eof;
|
|
}
|
|
|
|
|
|
bool FileInput::open(const std::string& fileName)
|
|
{
|
|
_file=::fopen( fileName.c_str(), "rb");
|
|
if (_file == NULL)
|
|
{
|
|
// ok havn't found file, resort to using findFile...
|
|
std::string newFileName = osgDB::findDataFile(fileName);
|
|
|
|
if (newFileName.empty()) return false;
|
|
_file=::fopen( newFileName.c_str(), "rb");
|
|
if (_file == NULL) return false;
|
|
}
|
|
_eof = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
void FileInput::close()
|
|
{
|
|
if (_file) ::fclose(_file);
|
|
_init();
|
|
}
|
|
|
|
|
|
bool FileInput::rewindLast()
|
|
{
|
|
if (_file == NULL) return false;
|
|
return (fseek(_file, _lRecOffset, SEEK_SET) == 0);
|
|
}
|
|
|
|
|
|
long FileInput::offset()
|
|
{
|
|
return _lRecOffset;
|
|
}
|
|
|
|
|
|
// read opcode and size
|
|
|
|
bool FileInput::_readHeader(SRecHeader* pHdr)
|
|
{
|
|
int nItemsRead;
|
|
|
|
// Save file position for rewind operation
|
|
_lRecOffset = ::ftell( _file );
|
|
|
|
// Read record header (4 bytes)
|
|
nItemsRead = _read(pHdr, sizeof(SRecHeader));
|
|
if (nItemsRead != 1)
|
|
return false;
|
|
|
|
if (isLittleEndianMachine())
|
|
pHdr->endian();
|
|
|
|
if ((unsigned)pHdr->length() < sizeof(SRecHeader))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool FileInput::_readBody(SRecHeader* pData)
|
|
{
|
|
// Read record body
|
|
int nBodySize = pData->length() - sizeof(SRecHeader);
|
|
if (nBodySize > 0)
|
|
{
|
|
int nItemsRead = _read(pData+1, nBodySize);
|
|
if (nItemsRead != 1)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool FileInput::_readContinuedBody(char* pData, int nBytes)
|
|
{
|
|
// Read record body
|
|
if (nBytes > 0)
|
|
{
|
|
int nItemsRead = _read(pData, nBytes);
|
|
if (nItemsRead != 1)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
SRecHeader* FileInput::readRecord()
|
|
{
|
|
SRecHeader hdr;
|
|
SRecHeader* pData;
|
|
|
|
if (!_readHeader(&hdr))
|
|
return NULL;
|
|
|
|
// Allocate buffer for record (including header)
|
|
// This buffer is extended later in Record::cloneRecord()
|
|
// if defined struct is bigger than read.
|
|
pData = (SRecHeader*)::malloc(hdr.length());
|
|
if (pData == NULL)
|
|
return NULL;
|
|
|
|
*pData = hdr;
|
|
|
|
// Some records contains only the header
|
|
if (hdr.length() == sizeof(SRecHeader))
|
|
return pData;
|
|
|
|
if (!_readBody(pData))
|
|
return NULL;
|
|
|
|
|
|
// Add support for OpenFlight 15.7 (1570) continuation records
|
|
//
|
|
|
|
// Record and FaceRecord both want to rewindLast, so save and restore
|
|
// the current file offset.
|
|
const long lRecOffsetSave = _lRecOffset;
|
|
|
|
int nTotalLen = hdr.length();
|
|
// From spec, in practice only these three records can be continued:
|
|
bool bContinuationPossible = (
|
|
(hdr.opcode()==COLOR_NAME_PALETTE_OP) ||
|
|
(hdr.opcode()==EXTENSION_OP) ||
|
|
(hdr.opcode()==LOCAL_VERTEX_POOL_OP) );
|
|
|
|
while (bContinuationPossible)
|
|
{
|
|
SRecHeader hdr2;
|
|
if (_readHeader( &hdr2 ))
|
|
{
|
|
if (hdr2.opcode() == CONTINUATION_OP)
|
|
{
|
|
int nNewChunkLen = hdr2.length() - 4;
|
|
size_t nNewLen = nTotalLen + nNewChunkLen;
|
|
pData = (SRecHeader*)::realloc( (void*)pData, nNewLen );
|
|
if (pData == NULL)
|
|
return NULL;
|
|
|
|
if (!_readContinuedBody( ((char*)pData) + nTotalLen, nNewChunkLen ))
|
|
return NULL;
|
|
nTotalLen = (int)nNewLen;
|
|
}
|
|
else
|
|
{
|
|
// Not a continuation record. Rewind, then exit loop.
|
|
rewindLast();
|
|
bContinuationPossible = false;
|
|
}
|
|
}
|
|
else
|
|
// Probably EOF
|
|
bContinuationPossible = false;
|
|
}
|
|
|
|
_lRecOffset = lRecOffsetSave;
|
|
|
|
//
|
|
// END support for continuation records
|
|
|
|
|
|
return pData;
|
|
}
|
|
|
|
|
|
Record* Input::readCreateRecord(FltFile* pFltFile)
|
|
{
|
|
SRecHeader* pData = readRecord();
|
|
|
|
if (pData == NULL) return NULL;
|
|
|
|
// find matching record prototype class
|
|
Record* pProto = Registry::instance()->getPrototype(pData->opcode());
|
|
if (pProto == NULL)
|
|
pProto = Registry::instance()->getPrototype(0);
|
|
|
|
if (pProto == NULL)
|
|
{
|
|
// Should not be possible to end up here!
|
|
osg::notify(osg::INFO) << "UnknownRecord not in registry!" << std::endl;
|
|
::free(pData);
|
|
return NULL;
|
|
}
|
|
|
|
// clone protoype
|
|
Record* pRec = pProto->cloneRecord(pData);
|
|
if (pRec == NULL)
|
|
{
|
|
osg::notify(osg::INFO) << "Can't clone record!" << std::endl;
|
|
::free(pData);
|
|
return NULL;
|
|
}
|
|
|
|
pRec->_pFltFile = pFltFile;
|
|
|
|
#if 0
|
|
osg::notify(osg::ALWAYS) << "class = " << pRec->className();
|
|
osg::notify(osg::ALWAYS) << " op = " << pRec->getOpcode();
|
|
osg::notify(osg::ALWAYS) << " name = " << pRec->getName();
|
|
osg::notify(osg::ALWAYS) << " offset = " << offset() << std::endl;
|
|
#endif
|
|
|
|
// Perform any post-read initializations.
|
|
pRec->postReadInit();
|
|
|
|
return pRec;
|
|
}
|