908 lines
26 KiB
C++
908 lines
26 KiB
C++
//
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
|
|
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
|
|
// reserved.
|
|
//
|
|
// This program is free software. The contents of this file are subject
|
|
// to the terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 2 of the License, or (at your
|
|
// option) any later version. You may redistribute it and/or modify it
|
|
// only in compliance with the GNU General Public License.
|
|
//
|
|
// This program is distributed in the hope that it will be useful.
|
|
// However, this program is distributed AS-IS WITHOUT ANY
|
|
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
|
|
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
|
|
// for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// Nothing in the GNU General Public License or any other license to use
|
|
// the code or files shall permit you to use Tripwire's trademarks,
|
|
// service marks, or other intellectual property without Tripwire's
|
|
// prior written consent.
|
|
//
|
|
// If you have any questions, please contact Tripwire, Inc. at either
|
|
// info@tripwire.org or www.tripwire.org.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// archive.cpp -- classes that abstract a raw byte archive
|
|
//
|
|
// cArchive -- interface for single-direction (one pass) reads and writes
|
|
// cBidirArchive -- interface for a random-access archive
|
|
// cMemArchive -- implementation of a bidirectional archive in memory
|
|
// cFileArchive -- implementation of a bidirectional archive as a file
|
|
|
|
#include "stdcore.h"
|
|
#include "archive.h"
|
|
#include "fsservices.h"
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <iostream>
|
|
#include "file.h"
|
|
#include "stringutil.h"
|
|
|
|
#include "corestrings.h" // for: STR_ERR2_ARCH_CRYPTO_ERR
|
|
|
|
//=============================================================================
|
|
// eArchiveCrypto
|
|
//=============================================================================
|
|
TSTRING eArchiveCrypto::GetMsg() const
|
|
{
|
|
// RAD: Updated this to use new stringtable
|
|
return (mMsg + TSS_GetString(cCore, core::STR_ERR2_ARCH_CRYPTO_ERR));
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// cArchive
|
|
//=============================================================================
|
|
|
|
// convenience methods
|
|
//
|
|
// Specific Read functions throw eArchive if EOF is reached because
|
|
// if the caller is requesting a certain amount of data to be present,
|
|
// reaching EOF is unexpected
|
|
//
|
|
// ReadBlob and WriteBlob return number of bytes read or written. Notice
|
|
// that ReadBlob does not throw an exception since eventually EOF is expected.
|
|
//
|
|
// ReadBlob can take NULL as a destination pointer
|
|
//
|
|
// All write functions throw exceptions for unexpected events like
|
|
// running out of memory or disk space.
|
|
//
|
|
|
|
void cArchive::ReadInt16(int16& ret) // throw(eArchive)
|
|
{
|
|
if (ReadBlob(&ret, sizeof(int16)) != sizeof(int16))
|
|
throw eArchiveEOF();
|
|
|
|
ret = tw_ntohs(ret);
|
|
}
|
|
|
|
void cArchive::ReadInt32(int32& ret) // throw(eArchive)
|
|
{
|
|
if (ReadBlob(&ret, sizeof(int32)) != sizeof(int32))
|
|
throw eArchiveEOF();
|
|
|
|
ret = tw_ntohl(ret);
|
|
}
|
|
|
|
void cArchive::ReadInt64(int64& ret) // throw(eArchive)
|
|
{
|
|
if (ReadBlob(&ret, sizeof(int64)) != sizeof(int64))
|
|
throw eArchiveEOF();
|
|
|
|
ret = tw_ntohll(ret);
|
|
}
|
|
|
|
// NOTE:BAM 10/11/99 -- we store unsigned size, but it really only works with
|
|
// lengths < INT16_MAX due to sign extension in integral promotion during the
|
|
// resize() in ReadString().
|
|
// format for written string: 16-bit unsigned size, then a list of 16-bit UCS2 (Unicode) characters
|
|
// not including terminating NULL
|
|
void cArchive::ReadString(TSTRING& ret) // throw(eArchive)
|
|
{
|
|
// read in size of string
|
|
int16 size;
|
|
ReadInt16(size);
|
|
|
|
// create buffer for WCHAR16 string
|
|
wc16_string ws;
|
|
ws.resize(size);
|
|
WCHAR16* pwc = (WCHAR16*)ws.data();
|
|
|
|
for (int n = 0; n < size; n++)
|
|
{
|
|
int16 i16;
|
|
ReadInt16(i16);
|
|
*pwc++ = i16;
|
|
}
|
|
|
|
// convert WCHAR16 string to a TSTRING
|
|
ret = cStringUtil::WstrToTstr(ws);
|
|
}
|
|
|
|
int cArchive::ReadBlob(void* pBlob, int count)
|
|
{
|
|
return Read(pBlob, count);
|
|
}
|
|
|
|
void cArchive::WriteInt16(int16 i) // throw(eArchive)
|
|
{
|
|
i = tw_htons(i);
|
|
WriteBlob(&i, sizeof(int16));
|
|
}
|
|
|
|
void cArchive::WriteInt32(int32 i) // throw(eArchive)
|
|
{
|
|
i = tw_htonl(i);
|
|
WriteBlob(&i, sizeof(int32));
|
|
}
|
|
|
|
void cArchive::WriteInt64(int64 i) // throw(eArchive)
|
|
{
|
|
i = tw_htonll(i);
|
|
WriteBlob(&i, sizeof(int64));
|
|
}
|
|
|
|
// NOTE:BAM 10/11/99 -- we store unsigned size, but it really only works with
|
|
// lengths < INT16_MAX due to sign extension in integral promotion during the
|
|
// resize() in ReadString().
|
|
// format for written string: 16-bit unsigned size, then a list of 16-bit UCS2 (Unicode) characters
|
|
// not including terminating NULL
|
|
void cArchive::WriteString(TSTRING s) // throw(eArchive)
|
|
{
|
|
// convert string to a UCS2 string
|
|
wc16_string ws;
|
|
cStringUtil::Convert(ws, s); // Make convert "type-dispatched"
|
|
|
|
// we assume that we can represent the size as a unsigned 16-bit number
|
|
// (we actually write it as a signed number, but we cast it)
|
|
if (ws.length() > TSS_INT16_MAX)
|
|
ThrowAndAssert(eArchiveStringTooLong());
|
|
|
|
WriteInt16(static_cast<int16>(ws.length()));
|
|
|
|
// write out each 16 bit character
|
|
// RAD:09/03/99 -- Optimized for performance with "const"
|
|
wc16_string::const_iterator at = ws.begin();
|
|
while (at != ws.end())
|
|
WriteInt16(*at++);
|
|
}
|
|
|
|
|
|
void cArchive::WriteBlob(const void* pBlob, int count) // throw(eArchive)
|
|
{
|
|
if (Write(pBlob, count) < count)
|
|
ThrowAndAssert(eArchiveWrite());
|
|
}
|
|
|
|
int32 cArchive::GetStorageSize(const TSTRING& str)
|
|
{
|
|
int32 size = sizeof(int32); // the length is always stored
|
|
//
|
|
// after the length, all of the characters in the string are written as 16-bit values,
|
|
// except for the null character
|
|
//
|
|
size += (str.length() * 2);
|
|
|
|
return size;
|
|
}
|
|
|
|
int64 cArchive::Copy(cArchive* pFrom, int64 amt)
|
|
{
|
|
enum
|
|
{
|
|
BUF_SIZE = 2048
|
|
};
|
|
int8 buf[BUF_SIZE];
|
|
int64 amtLeft = amt;
|
|
|
|
while (amtLeft > 0)
|
|
{
|
|
int64 amtToRead = amtLeft > (int64)BUF_SIZE ? (int64)BUF_SIZE : amtLeft;
|
|
int64 amtRead = pFrom->ReadBlob(buf, static_cast<int>(amtToRead));
|
|
amtLeft -= amtRead;
|
|
WriteBlob(buf, static_cast<int>(amtRead));
|
|
if (amtRead < amtToRead)
|
|
break;
|
|
}
|
|
|
|
// return the amount copied ...
|
|
return (amt - amtLeft);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// class cMemMappedArchive -- Archive that can be memory mapped.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
cMemMappedArchive::cMemMappedArchive()
|
|
{
|
|
mpMappedMem = 0;
|
|
mMappedOffset = 0;
|
|
mMappedLength = 0;
|
|
}
|
|
|
|
cMemMappedArchive::~cMemMappedArchive()
|
|
{
|
|
}
|
|
|
|
int64 cMemMappedArchive::GetMappedOffset() const // throw(eArchive)
|
|
{
|
|
if (mpMappedMem == 0)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
return mMappedOffset;
|
|
}
|
|
|
|
int64 cMemMappedArchive::GetMappedLength() const // throw(eArchive)
|
|
{
|
|
if (mpMappedMem == 0)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
return mMappedLength;
|
|
}
|
|
|
|
const void* cMemMappedArchive::GetMap() const // throw(eArchive)
|
|
{
|
|
if (mpMappedMem == 0)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
return mpMappedMem;
|
|
}
|
|
|
|
void* cMemMappedArchive::GetMap() // throw(eArchive)
|
|
{
|
|
if (mpMappedMem == 0)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
return mpMappedMem;
|
|
}
|
|
|
|
void cMemMappedArchive::SetNewMap(void* pMap, int64 offset, int64 length) const
|
|
{
|
|
if (pMap == 0)
|
|
{
|
|
mpMappedMem = 0;
|
|
mMappedOffset = 0;
|
|
mMappedLength = 0;
|
|
}
|
|
else
|
|
{
|
|
mpMappedMem = pMap;
|
|
mMappedOffset = offset;
|
|
mMappedLength = length;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// class cMemoryArchive -- An archive that stores itself in a memory buffer.
|
|
// This buffer can grow as needed up until a pre-specified maximum
|
|
// size. The buffer can be read and written to and can be memory
|
|
// mapped.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
cMemoryArchive::cMemoryArchive(int maxSize) : mMaxAllocatedLen(maxSize)
|
|
{
|
|
ASSERT(maxSize > 0);
|
|
mpMemory = 0;
|
|
mAllocatedLen = 0;
|
|
mLogicalSize = 0;
|
|
mReadHead = 0;
|
|
}
|
|
|
|
cMemoryArchive::~cMemoryArchive()
|
|
{
|
|
delete [] mpMemory;
|
|
}
|
|
|
|
bool cMemoryArchive::EndOfFile()
|
|
{
|
|
return mReadHead >= mLogicalSize;
|
|
}
|
|
|
|
void cMemoryArchive::Seek(int64 offset, SeekFrom from) // throw(eArchive)
|
|
{
|
|
switch (from)
|
|
{
|
|
case cBidirArchive::BEGINNING:
|
|
break;
|
|
case cBidirArchive::CURRENT:
|
|
offset = mReadHead + (int)offset;
|
|
break;
|
|
case cBidirArchive::END:
|
|
offset = mLogicalSize + (int)offset;
|
|
break;
|
|
default:
|
|
ThrowAndAssert(eArchiveSeek(TSS_GetString(cCore, core::STR_MEMARCHIVE_FILENAME),
|
|
TSS_GetString(cCore, core::STR_MEMARCHIVE_ERRSTR)));
|
|
}
|
|
|
|
if (offset > mLogicalSize)
|
|
ThrowAndAssert(eArchiveSeek(TSS_GetString(cCore, core::STR_MEMARCHIVE_FILENAME),
|
|
TSS_GetString(cCore, core::STR_MEMARCHIVE_ERRSTR)));
|
|
|
|
mReadHead = static_cast<int>(offset);
|
|
}
|
|
|
|
int64 cMemoryArchive::CurrentPos() const
|
|
{
|
|
return mReadHead;
|
|
}
|
|
|
|
int64 cMemoryArchive::Length() const
|
|
{
|
|
return mLogicalSize;
|
|
}
|
|
|
|
void cMemoryArchive::Truncate()
|
|
{
|
|
ASSERT(mReadHead >= 0);
|
|
|
|
mLogicalSize = mReadHead;
|
|
AllocateMemory(mLogicalSize);
|
|
}
|
|
|
|
void cMemoryArchive::MapArchive(int64 offset, int64 len) // throw(eArchive)
|
|
{
|
|
if (offset + (int)len > mLogicalSize)
|
|
AllocateMemory(static_cast<int>(offset + len));
|
|
|
|
SetNewMap(mpMemory + offset, offset, len);
|
|
}
|
|
|
|
void cMemoryArchive::MapArchive(int64 offset, int64 len) const // throw(eArchive)
|
|
{
|
|
if (offset + (int)len > mLogicalSize)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
SetNewMap(mpMemory + offset, offset, len);
|
|
}
|
|
|
|
int cMemoryArchive::Read(void* pDest, int count)
|
|
{
|
|
if (mReadHead + count > mLogicalSize)
|
|
count = mLogicalSize - mReadHead;
|
|
|
|
if (pDest != 0)
|
|
memcpy(pDest, mpMemory + mReadHead, count);
|
|
|
|
mReadHead += count;
|
|
|
|
return count;
|
|
}
|
|
|
|
int cMemoryArchive::Write(const void* pDest, int count) // throw(eArchive)
|
|
{
|
|
if (mReadHead + count > mLogicalSize)
|
|
{
|
|
AllocateMemory(mReadHead + count);
|
|
}
|
|
|
|
memcpy(mpMemory + mReadHead, pDest, count);
|
|
|
|
mReadHead += count;
|
|
|
|
return count;
|
|
}
|
|
|
|
void cMemoryArchive::AllocateMemory(int len) // throw(eArchive)
|
|
{
|
|
const int MIN_ALLOCATED_SIZE = 1024;
|
|
|
|
if (len > mAllocatedLen)
|
|
{
|
|
// grow the buffer
|
|
// only error if we are in debug mode
|
|
#ifdef DEBUG
|
|
if (len > mMaxAllocatedLen)
|
|
ThrowAndAssert(eArchiveOutOfMem());
|
|
#endif
|
|
|
|
if (0 == mAllocatedLen)
|
|
mAllocatedLen = MIN_ALLOCATED_SIZE;
|
|
|
|
while (mAllocatedLen < len)
|
|
mAllocatedLen *= 2;
|
|
|
|
int8* pNewMem = new int8[mAllocatedLen];
|
|
if (mpMemory != 0)
|
|
{
|
|
memcpy(pNewMem, mpMemory, mLogicalSize);
|
|
delete [] mpMemory;
|
|
}
|
|
mpMemory = pNewMem;
|
|
mLogicalSize = len;
|
|
|
|
// update memory map if there is one
|
|
if (mpMappedMem)
|
|
SetNewMap(mpMemory + mMappedOffset, mMappedOffset, mMappedLength);
|
|
}
|
|
else
|
|
{
|
|
// check for memory map conflict
|
|
if (mpMappedMem && len < mMappedOffset + mMappedLength)
|
|
ThrowAndAssert(eArchiveMemmap());
|
|
|
|
if (len < (mAllocatedLen >> 1) && mAllocatedLen > MIN_ALLOCATED_SIZE)
|
|
{
|
|
// shrink the buffer
|
|
int8* pNewMem = new int8[len];
|
|
ASSERT(mpMemory);
|
|
memcpy(pNewMem, mpMemory, len);
|
|
delete [] mpMemory;
|
|
mpMemory = pNewMem;
|
|
mLogicalSize = len;
|
|
|
|
// update memory map if there is one
|
|
if (mpMappedMem)
|
|
SetNewMap(mpMemory + mMappedOffset, mMappedOffset, mMappedLength);
|
|
}
|
|
else
|
|
{
|
|
// no need to grow or shrink
|
|
mLogicalSize = len;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
class cFixedMemArchive : public cBidirArchive
|
|
{
|
|
public:
|
|
|
|
int8* mpMemory;
|
|
int32 mSize;
|
|
int32 mReadHead;
|
|
};
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cFixedMemArchive
|
|
//-----------------------------------------------------------------------------
|
|
cFixedMemArchive::cFixedMemArchive() : mpMemory(0), mSize(0), mReadHead(0)
|
|
{
|
|
}
|
|
|
|
cFixedMemArchive::cFixedMemArchive(int8* pMem, int32 size) : mpMemory(0), mSize(0), mReadHead(0)
|
|
{
|
|
Attach(pMem, size);
|
|
}
|
|
|
|
cFixedMemArchive::~cFixedMemArchive()
|
|
{
|
|
}
|
|
|
|
void cFixedMemArchive::Attach(int8* pMem, int32 size)
|
|
{
|
|
mpMemory = pMem;
|
|
mSize = size;
|
|
mReadHead = 0;
|
|
}
|
|
|
|
void cFixedMemArchive::Seek(int64 offset, SeekFrom from) // throw(eArchive)
|
|
{
|
|
switch (from)
|
|
{
|
|
case cBidirArchive::BEGINNING:
|
|
break;
|
|
case cBidirArchive::CURRENT:
|
|
offset = mReadHead + (int)offset;
|
|
break;
|
|
case cBidirArchive::END:
|
|
offset = mSize + (int)offset;
|
|
break;
|
|
default:
|
|
ThrowAndAssert(eArchiveSeek(TSS_GetString(cCore, core::STR_MEMARCHIVE_FILENAME),
|
|
TSS_GetString(cCore, core::STR_MEMARCHIVE_ERRSTR)));
|
|
}
|
|
|
|
if (offset > mSize)
|
|
ThrowAndAssert(eArchiveSeek(TSS_GetString(cCore, core::STR_MEMARCHIVE_FILENAME),
|
|
TSS_GetString(cCore, core::STR_MEMARCHIVE_ERRSTR)));
|
|
|
|
mReadHead = static_cast<int32>(offset);
|
|
}
|
|
|
|
int64 cFixedMemArchive::CurrentPos() const
|
|
{
|
|
return mReadHead;
|
|
}
|
|
|
|
int64 cFixedMemArchive::Length() const
|
|
{
|
|
return mSize;
|
|
}
|
|
|
|
bool cFixedMemArchive::EndOfFile()
|
|
{
|
|
return (mReadHead >= mSize);
|
|
}
|
|
|
|
int cFixedMemArchive::Read(void* pDest, int count) // throw(eArchive)
|
|
{
|
|
ASSERT(pDest);
|
|
if (mReadHead + count > mSize)
|
|
{
|
|
count = static_cast<int>(mSize - mReadHead);
|
|
if (count <= 0)
|
|
return 0;
|
|
}
|
|
|
|
if (pDest != 0)
|
|
memcpy(pDest, mpMemory + mReadHead, count);
|
|
|
|
mReadHead += count;
|
|
|
|
return count;
|
|
}
|
|
|
|
int cFixedMemArchive::Write(const void* pDest, int count) // throw(eArchive)
|
|
{
|
|
if (mReadHead + count > mSize)
|
|
{
|
|
ASSERT(false);
|
|
throw eArchiveWrite();
|
|
}
|
|
|
|
memcpy(mpMemory + mReadHead, pDest, count);
|
|
|
|
mReadHead += count;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// class cFileArchive -- Archive for files...
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Ctor -- Initialize member variables to 0 or NULL equivalents.
|
|
cFileArchive::cFileArchive() : mFileSize(0), mReadHead(0), isWritable(false)
|
|
{
|
|
}
|
|
|
|
cFileArchive::~cFileArchive()
|
|
{
|
|
}
|
|
|
|
bool cFileArchive::EndOfFile()
|
|
{
|
|
return (mReadHead >= mFileSize);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Seek -- This is where the actual offset is performed. The default
|
|
// for each archive will be 0.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cFileArchive::Seek(int64 offset, SeekFrom from) // throw(eArchive)
|
|
{
|
|
try
|
|
{
|
|
switch (from)
|
|
{
|
|
case cBidirArchive::BEGINNING:
|
|
break;
|
|
case cBidirArchive::CURRENT:
|
|
offset = mReadHead + offset;
|
|
break;
|
|
case cBidirArchive::END:
|
|
offset = mFileSize + offset;
|
|
break;
|
|
default:
|
|
throw eArchiveSeek(mCurrentFilename, iFSServices::GetInstance()->GetErrString());
|
|
}
|
|
|
|
if (offset > mFileSize)
|
|
throw eArchiveSeek(mCurrentFilename, iFSServices::GetInstance()->GetErrString());
|
|
mReadHead = offset;
|
|
|
|
mCurrentFile.Seek(mReadHead, cFile::SEEK_BEGIN);
|
|
//This is where the actual read/writehead is set!!
|
|
} //try
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveSeek(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
int64 cFileArchive::CurrentPos(void) const
|
|
{
|
|
return mReadHead;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Length -- Returns the size of the current file archive.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
int64 cFileArchive::Length(void) const
|
|
{
|
|
try
|
|
{
|
|
return mCurrentFile.GetSize();
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveSeek(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// OpenRead -- Opens the file to be read only.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cFileArchive::OpenRead(const TCHAR* filename, uint32 openFlags)
|
|
{
|
|
try
|
|
{
|
|
// set up open flags
|
|
uint32 flags = cFile::OPEN_READ;
|
|
flags |= ((openFlags & FA_OPEN_TRUNCATE) ? cFile::OPEN_TRUNCATE : 0);
|
|
flags |= ((openFlags & FA_OPEN_TEXT) ? cFile::OPEN_TEXT : 0);
|
|
flags |= ((openFlags & FA_SCANNING) ? cFile::OPEN_SCANNING : 0);
|
|
flags |= ((openFlags & FA_DIRECT) ? cFile::OPEN_DIRECT : 0);
|
|
|
|
mOpenFlags = openFlags;
|
|
mCurrentFilename = filename;
|
|
mCurrentFile.Open(filename, flags);
|
|
isWritable = false;
|
|
|
|
mFileSize = mCurrentFile.GetSize();
|
|
mReadHead = mCurrentFile.Seek(0, cFile::SEEK_BEGIN);
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveOpen(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// OpenReadWrite -- Opens the file to be read or written to
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cFileArchive::OpenReadWrite(const TCHAR* filename, uint32 openFlags)
|
|
{
|
|
try
|
|
{
|
|
// set up open flags
|
|
uint32 flags = cFile::OPEN_WRITE;
|
|
flags |= ((openFlags & FA_OPEN_TRUNCATE) ? cFile::OPEN_TRUNCATE : 0);
|
|
flags |= ((openFlags & FA_OPEN_TEXT) ? cFile::OPEN_TEXT : 0);
|
|
flags |= ((openFlags & FA_SCANNING) ? cFile::OPEN_SCANNING : 0);
|
|
flags |= ((openFlags & FA_DIRECT) ? cFile::OPEN_DIRECT : 0);
|
|
|
|
mOpenFlags = openFlags;
|
|
mCurrentFilename = filename;
|
|
mCurrentFile.Open(filename, flags);
|
|
isWritable = true;
|
|
|
|
mFileSize = mCurrentFile.GetSize();
|
|
mReadHead = mCurrentFile.Seek(0, cFile::SEEK_BEGIN);
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveOpen(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// GetCurrentFilename -- Returns the name of the file currently associated
|
|
// with the FileArchive.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
TSTRING cFileArchive::GetCurrentFilename(void) const
|
|
{
|
|
return mCurrentFilename;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Close -- Closes the file currently referenced by mpCurrStream
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cFileArchive::Close()
|
|
{
|
|
try
|
|
{
|
|
mCurrentFile.Close();
|
|
mFileSize = 0;
|
|
mReadHead = 0;
|
|
|
|
mCurrentFilename = _T("");
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchive(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Read -- Read places bytes in location designated by pDest. Returns
|
|
// The actual amount read into *pDest.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
int cFileArchive::Read(void* pDest, int count)
|
|
{
|
|
|
|
try
|
|
{
|
|
if (mReadHead + count > mFileSize && !(mOpenFlags & FA_DIRECT))
|
|
count = static_cast<int>(mFileSize - mReadHead);
|
|
|
|
if (pDest != NULL)
|
|
{
|
|
int nbRead = static_cast<int>(mCurrentFile.Read(pDest, count));
|
|
|
|
// 'count' may not be equal to 'nbRead' if the file is open in
|
|
// text mode.
|
|
count = nbRead;
|
|
if (count < 0)
|
|
count = 0;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
int32 dummy;
|
|
for (i = count;; i -= sizeof(int32))
|
|
{
|
|
if (i < (int)sizeof(int32))
|
|
{
|
|
if (i > 0)
|
|
mCurrentFile.Read(&dummy, i);
|
|
break;
|
|
}
|
|
mCurrentFile.Read(&dummy, i);
|
|
}
|
|
}
|
|
|
|
mReadHead += count;
|
|
return count;
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveRead(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Write -- Writes to file designated by fh. If isWritable is not set,
|
|
// function returns 0. Otherwise, the actual # written is returned.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
int cFileArchive::Write(const void* pDest, int count) // throw(eArchive)
|
|
{
|
|
try
|
|
{
|
|
int64 actual_count = 0;
|
|
ASSERT(mCurrentFile.isWritable);
|
|
|
|
actual_count = mCurrentFile.Write(pDest, count);
|
|
|
|
if (actual_count < count)
|
|
{
|
|
//Disk full??
|
|
throw eArchiveWrite(mCurrentFilename, iFSServices::GetInstance()->GetErrString());
|
|
}
|
|
|
|
// increment the read/write head
|
|
mReadHead += actual_count;
|
|
|
|
// increase the size, if needed
|
|
if (mReadHead > mFileSize)
|
|
{
|
|
#if 0 // IS_SUNPRO \
|
|
// These two lines seem to be all there is between code that crashes and code that works for sunpro
|
|
cDebug d("cFileArchive::Write()");
|
|
d.TraceDebug(_T("file(%s) adjusted mFileSize = %d mReadHead = %d\n"), mCurrentFilename.c_str(), (int)mFileSize, (int)mReadHead);
|
|
#endif
|
|
mFileSize = mReadHead;
|
|
}
|
|
|
|
return (int)actual_count;
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
throw(eArchiveWrite(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Truncate
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cFileArchive::Truncate() // throw(eArchive)
|
|
{
|
|
ASSERT(mCurrentFile.IsOpen());
|
|
ASSERT(mCurrentFile.isWritable);
|
|
|
|
try
|
|
{
|
|
mCurrentFile.Truncate(mReadHead);
|
|
}
|
|
catch (eFile& fileError)
|
|
{
|
|
//TODO: create an error number for truncate...
|
|
throw(eArchiveWrite(mCurrentFilename, fileError.GetDescription()));
|
|
}
|
|
|
|
mFileSize = mReadHead;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// OpenReadWrite -- Opens the file to be read or written to
|
|
//
|
|
// since we'll never open an existing file, the truncateFile flag is unnecessary.
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void cLockedTemporaryFileArchive::OpenReadWrite(const TCHAR* filename, uint32 openFlags)
|
|
{
|
|
TSTRING strTempFile;
|
|
|
|
try
|
|
{
|
|
|
|
ASSERT(!mCurrentFile.IsOpen()); // shouldn't be able to create a new file when we're already open
|
|
if (mCurrentFile.IsOpen())
|
|
throw(eArchive(mCurrentFilename, _T("Internal Error")));
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// if filename is NULL, create a temp file for the caller
|
|
if (filename == NULL)
|
|
{
|
|
try
|
|
{
|
|
iFSServices::GetInstance()->GetTempDirName(strTempFile);
|
|
strTempFile += _T("twtempXXXXXX");
|
|
iFSServices::GetInstance()->MakeTempFilename(strTempFile);
|
|
}
|
|
catch (eFSServices& fileError)
|
|
{
|
|
TSTRING errStr = TSS_GetString(cCore, core::STR_BAD_TEMPDIRECTORY);
|
|
throw eArchiveOpen(strTempFile, errStr);
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// create file
|
|
|
|
// set up flags
|
|
uint32 flags = cFile::OPEN_WRITE | cFile::OPEN_LOCKED_TEMP | cFile::OPEN_CREATE | cFile::OPEN_EXCLUSIVE;
|
|
if (openFlags & FA_OPEN_TRUNCATE)
|
|
flags |= cFile::OPEN_TRUNCATE;
|
|
if (openFlags & FA_OPEN_TEXT)
|
|
flags |= cFile::OPEN_TEXT;
|
|
|
|
// open file
|
|
mCurrentFilename = filename ? filename : strTempFile.c_str();
|
|
mCurrentFile.Open(mCurrentFilename, flags);
|
|
|
|
isWritable = true;
|
|
mFileSize = mCurrentFile.GetSize();
|
|
mReadHead = mCurrentFile.Seek(0, cFile::SEEK_BEGIN);
|
|
|
|
#if 0 // IS_SUNPRO
|
|
cDebug d("cLockedTemporaryFileArchive::OpenReadWrite()");
|
|
d.TraceDebug(_T("file(%s) set mFileSize to %d mReadHead to %d\n"), mCurrentFilename.c_str(), (int)mFileSize, (int)mReadHead);
|
|
#endif
|
|
|
|
} //try
|
|
catch (eFile& fileError)
|
|
{
|
|
TSTRING errStr = TSS_GetString(cCore, core::STR_BAD_TEMPDIRECTORY);
|
|
eArchiveOpen e(strTempFile, errStr);
|
|
throw e;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Close -- Closes the file currently referenced by fh
|
|
void cLockedTemporaryFileArchive::Close()
|
|
{
|
|
// Note: this deletes the file as well
|
|
cFileArchive::Close();
|
|
}
|