Files
FC1/CryAnimation/ChunkFileReader.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

206 lines
5.8 KiB
C++

#include "StdAfx.h"
#include "FileMapping.h"
#include "chunkfilereader.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const char* CChunkFileReader::gLastError = "";
CChunkFileReader::CChunkFileReader():
m_pChunks(NULL)
//,m_arrChunkSize ("CChunkFileReader.ChunkSize")
{
}
CChunkFileReader::~CChunkFileReader()
{
close();
}
bool CChunkFileReader::open (const string& strFileName, unsigned nFlags)
{
return open (strFileName.c_str(), nFlags);
}
//////////////////////////////////////////////////////////////////////////
// attaches the file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool CChunkFileReader::open(CFileMapping* pFile)
{
close();
m_pFile = pFile;
bool bSuccess = false;
if ( (m_pFile != (CFileMapping*)NULL) && (m_pFile->getData() != NULL) )
{
if (m_pFile->getSize() >= sizeof(FileHeader))
{// the file must contain the header
const FileHeader& fileHeader = getFileHeader();
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int)
&& (int)fileHeader.ChunkTableOffset > (int)sizeof(fileHeader)
)
{// there must be room for the chunk table header
unsigned numChunks = *static_cast<const unsigned*>(m_pFile->getData(fileHeader.ChunkTableOffset));
unsigned nChunk;
if (m_pFile->getSize() >= fileHeader.ChunkTableOffset + sizeof(int) + numChunks*sizeof(ChunkHeader)
&& numChunks <= (pFile->getSize () - fileHeader.ChunkTableOffset - sizeof(int)) / sizeof(ChunkHeader))
{
// the file must contain the full chunk table
m_pChunks = (const ChunkHeader*)m_pFile->getData(fileHeader.ChunkTableOffset + sizeof(int));
bool bInvalidChunkOffsetsFound = false; // sets to true if there are chunks pointing outside the raw data section of the file
// step through all the chunks to fetch file offsets
std::set<int> setOffsets;
for (nChunk = 0; nChunk < numChunks; ++nChunk)
{
int nOffset = m_pChunks[nChunk].FileOffset;
if (nOffset < sizeof(FileHeader) || nOffset >= fileHeader.ChunkTableOffset)
{
gLastError = "CryFile is corrupted: chunk table is corrupted (invalid chunk offsets found)";
bInvalidChunkOffsetsFound = true;
}
setOffsets.insert(nOffset);
}
m_arrChunkSize.clear();
m_arrChunkSize.resize (numChunks);
for (nChunk = 0; nChunk < numChunks; ++nChunk)
{
int nOffset = m_pChunks[nChunk].FileOffset;
int nSize = 0; // the size for invalid chunks (with invalid offsets)
if (nOffset >= sizeof(FileHeader) && nOffset < fileHeader.ChunkTableOffset)
{
// find the next offset
std::set<int>::const_iterator it = setOffsets.find (nOffset);
assert (it != setOffsets.end());
assert (*it == nOffset);
++it;
nSize = (it==setOffsets.end()?fileHeader.ChunkTableOffset:*it) - nOffset;
}
assert (nSize >= 0);
m_arrChunkSize[nChunk] = nSize;
}
bSuccess = true;
// don't let the files with invalid chunks..
//bSuccess = !bInvalidChunkOffsetsFound;
}
else
gLastError = "CryFile is corrupted: chunk table is corrupted or truncated (file too small)";
}
else
gLastError = "CryFile is corrupted: chunk table is trucated (file header is probably corrupted)";
}
else
gLastError = "CryFile is corrupted: header is truncated (file too small)";
}
else
gLastError = "Invalid file mapping passed to CChunkFileReader::open";
if (!bSuccess)
close();
return bSuccess;
}
//////////////////////////////////////////////////////////////////////////
// attaches a new file mapping object to this file reader and checks
// whether the file is a valid chunked file
bool CChunkFileReader::open(const char* szFileName, unsigned nFlags)
{
CFileMapping_AutoPtr pFileMapping = new CFileMapping(szFileName, nFlags);
if (!pFileMapping->getData())
{
gLastError = "Cannot open file";
return false;
}
return open (pFileMapping);
}
void CChunkFileReader::close()
{
m_arrChunkSize.clear();
m_pFile = NULL;
m_pChunks = NULL;
}
// returns the raw data of the file from the given offset
const void* CChunkFileReader::getRawData(unsigned nOffset)const
{
if ((m_pFile != (CFileMapping*)NULL) && m_pFile->getData())
return ((char*)m_pFile->getData())+nOffset;
else
return NULL;
}
// retrieves the raw chunk header, as it appears in the file
const CChunkFileReader::ChunkHeader& CChunkFileReader::getChunkHeader(int nChunkIdx)const
{
return m_pChunks[nChunkIdx];
}
// returns the raw data of the i-th chunk
const void* CChunkFileReader::getChunkData(int nChunkIdx)const
{
if (nChunkIdx>= 0 && nChunkIdx < numChunks())
{
int nOffset = m_pChunks[nChunkIdx].FileOffset;
if (nOffset < sizeof(FileHeader) || nOffset >= getFileHeader().ChunkTableOffset)
return 0;
else
return m_pFile->getData(nOffset);
}
else
return 0;
}
// number of chunks
int CChunkFileReader::numChunks()const
{
return (int)m_arrChunkSize.size();
}
// number of chunks of the specified type
int CChunkFileReader::numChunksOfType (ChunkTypes nChunkType)const
{
int nResult = 0;
for (int i = 0; i < numChunks(); ++i)
{
if (m_pChunks[i].ChunkType == nChunkType)
++nResult;
}
return nResult;
}
// returns the file headers
const CChunkFileReader::FileHeader& CChunkFileReader::getFileHeader() const
{
return m_pFile?*((const FileHeader*)(m_pFile->getData())):*(const FileHeader*)NULL;
}
bool CChunkFileReader::isValid () const
{
return m_pFile != (CFileMapping*)NULL;
}
//////////////////////////////////////////////////////////////////////////
// calculates the chunk size, based on the very next chunk with greater offset
// or the end of the raw data portion of the file
int CChunkFileReader::getChunkSize(int nChunkIdx) const
{
assert (nChunkIdx >= 0 && nChunkIdx < numChunks());
return m_arrChunkSize[nChunkIdx];
}