#include <string>
#include <fstream>
#include "Lump.h"
#include "ResourceFile.h"
using namespace std;
bool ResourceFile::Open (const string& FileName, OpenMode FileMode)
{
// If the file is already open, then return false
if (mFileStream.is_open())
return false;
// Set up the file name
if (FileName != mFileName)
mFileName = FileName;
// Copy the file mode into our local buffer
mFileMode = FileMode;
if (mFileMode == Write)
{
// Open up the file for read/write replace mode
mFileStream.open(mFileName.c_str(), ios::binary | ios::in | ios::out | ios::trunc);
if (!mFileStream.is_open())
return false;
// Set up the header
mFileHeader.Version = ResFileVer;
mFileHeader.LumpCount = 0;
// We're done as far as setting things up for a new resource file,
// so return true...
return true;
}
else
{
// Open the file for read only
if (mFileMode == Read)
mFileStream.open(mFileName.c_str(), ios::binary | ios::in);
// Open the file for modification
else if (mFileMode == ReadWrite)
mFileStream.open(mFileName.c_str(), ios::binary | ios::in | ios::out);
else
return false;
// Make sure the file was opened properly
if (!mFileStream.is_open())
return false;
// Load in the header and make sure it's a valid resource file
mFileStream.seekp(0, ios::beg);
mFileStream.read(reinterpret_cast<char*>(&mFileHeader), sizeof(mFileHeader));
if (mFileStream.fail())
{
mFileStream.close();
return false;
}
// Make sure the version of the file isn't greater than the library version
if (mFileHeader.Version != ResFileVer)
{
mFileStream.close();
return false;
}
// Load in the lump list (if any)
if (LoadLumpList() == false)
{
mFileStream.close();
return false;
}
// Everything has gone smoothly, so return true
return true;
}
}
bool ResourceFile::Save (const std::string& AltFileName)
{
// Make sure a file is open for write or modification mode
if ((!mFileStream.is_open()) || ((mFileMode != Write) && (mFileMode != ReadWrite)))
return false;
// Open up a temporary resource file to save the new contents to
mSaveStream.open(ResFileTempName.c_str(), ios_base::out | ios_base::trunc | ios_base::binary);
if (!mSaveStream.is_open())
return false;
// Write out the header. Note that we must later write it out again after
// it's fields have been updated, but we're merely doing this so that the
// lumps are aligned properly
try
{
mSaveStream.write(reinterpret_cast<char*>(&mFileHeader), sizeof(mFileHeader));
if (mSaveStream.fail())
throw;
// Save all of the lump data to the resource file
if (SaveLumps() == false)
throw;
// Get the offset of the lump list and store it in the header
mFileHeader.LumpListOffset = static_cast<unsigned long>(mSaveStream.tellp());
if (mSaveStream.fail())
throw;
// Save the lump list to the resource file
if (SaveLumpList() == false)
throw;
// Seek back to the start of the file and write out the new updated header
mSaveStream.seekp(0, ios_base::beg);
if (mSaveStream.fail())
throw;
mSaveStream.write(reinterpret_cast<char*>(&mFileHeader), sizeof(mFileHeader));
if (mSaveStream.fail())
throw;
}
// If this method failed, close the save file and delete it
catch (exception e)
{
mSaveStream.close();
return false;
}
// Close the files, and delete the old file if we're overwriting it and
// rename the temporary file to our original or alternate name
mSaveStream.close();
Close();
if (AltFileName != "" && AltFileName != mFileName)
{
// Update filename if using alternative name
mFileName = AltFileName;
}
// Remove the old file
remove(mFileName.c_str());
// After renaming the temporary file, load it back in
rename(ResFileTempName.c_str(), mFileName.c_str());
return Open(mFileName, ReadWrite);
}
void ResourceFile::Close()
{
// If the file isn't open, return
if (!mFileStream.is_open())
return;
// Unload the lumps and the lump list
UnloadLumpList();
// Close the file
mFileStream.close();
}
bool ResourceFile::LoadLumpList()
{
// Make sure a file is open for read or modification mode
if ((!mFileStream.is_open()) || ((mFileMode != Read) && (mFileMode != ReadWrite)))
return false;
// Seek to the start of the lump list in the resource file
mFileStream.seekp(mFileHeader.LumpListOffset, ios_base::beg);
if (mFileStream.fail())
return false;
Lump *pLump;
bool bError = false;
// Read in each lump into the list
for (unsigned long i = 0; i < mFileHeader.LumpCount; ++i)
{
// Allocate memory for the lump
if ((pLump = new Lump()) == NULL)
{
bError = true;
break;
}
// Load in the lump information
pLump->LoadInfo(mFileStream);
if (mFileStream.fail())
{
bError = true;
break;
}
// Insert lump into list
mLumpList.push_back(pLump);
}
// Check to see if there were any errors
if (bError == true)
{
// There was an error, unload what was loaded in
UnloadLumpList();
return false;
}
// No errors, so return true
return true;
}
bool ResourceFile::SaveLumpList()
{
// Make sure a file is open for write or modification mode
if ((!mSaveStream.is_open()) || ((mFileMode != Write) && (mFileMode != ReadWrite)))
return false;
// Go through the lump list, writing out the info for each lump in the list
std::vector<Lump*>::iterator iter;
for(iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// Write the lump info structure to the file
(*iter)->SaveInfo(mSaveStream);
if (mSaveStream.fail())
return false;
}
// No errors, so return TRUE
return true;
}
void ResourceFile::UnloadLumpList()
{
// Deallocate the entire lump list
std::vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// If the data has been loaded in, unload it
if ((*iter)->GetData() != NULL)
(*iter)->UnloadData();
// Deallocate current node
delete *iter;
}
mLumpList.clear();
}
bool ResourceFile::SaveLumps()
{
// Make sure a file is open for write or modification mode
if ((!mSaveStream.is_open()) || ((mFileMode != Write) && (mFileMode != ReadWrite)))
return false;
// Run through the lump linked list, writing the data for each lump to the
// save resource file
std::vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// If the data has been loaded in, save it to the new file
if ((*iter)->GetData() != NULL)
{
// Get the new file offset for the lump
(*iter)->GetInfo().Position = static_cast<unsigned long>(mSaveStream.tellp());
// Save the lump
if ((*iter)->SaveData(mSaveStream) == false)
return false;
}
else
{
// Otherwise, copy the lump's data from the old file to the new one
// Seek to the lump position in the old file and store the offset
// of the lump in the new file.
mFileStream.seekp((*iter)->GetInfo().Position, ios_base::beg);
(*iter)->GetInfo().Position = static_cast<unsigned long>(mSaveStream.tellp());
// Load in the bytes from the old file and save them out in
// the new file
unsigned long Size = (*iter)->GetInfo().Size;
char Buffer[2048];
while (Size != 0)
{
if (Size >= 2048)
{
mFileStream.read(Buffer, 2048);
mSaveStream.write(Buffer, 2048);
Size -= 2048;
}
else
{
mFileStream.read(Buffer, Size);
mSaveStream.write(Buffer, Size);
Size = 0;
}
}
}
}
// Return true, as no errors occured
return true;
}
bool ResourceFile::CreateLumpFromFile(const std::string& Name, const string& FileName)
{
// First, make sure the file is open for write or modification mode
if ((!mFileStream.is_open()) || ((mFileMode != Write) && (mFileMode != ReadWrite)))
return false;
// Traverse to the end of the lump list. Make sure not to create any lumps
// with the same names
std::vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
if ((*iter)->GetInfo().Name == Name)
return false;
}
Lump *pLump;
if ((pLump = new Lump()) == NULL)
return false;
ifstream inputStream;
inputStream.open(FileName.c_str(), ios::in | ios::binary);
if (!inputStream.is_open())
return false;
inputStream.seekg(0, ios_base::end);
unsigned long size = static_cast<unsigned long>(inputStream.tellg());
inputStream.seekg(0, ios_base::beg);
void* data = reinterpret_cast<void*>(new char[size]);
inputStream.read(reinterpret_cast<char*>(data), size);
inputStream.close();
// Set up the lump node
LumpInfo li;
li.Size = size;
li.Position = 0;
li.Name = Name;
pLump->SetInfo(li);
pLump->SetData(data);
mLumpList.push_back(pLump);
// Increment the number of lumps in the file
mFileHeader.LumpCount++;
// Everything went nice and slick, so return true
return true;
}
bool ResourceFile::CreateLump(const std::string& Name, void* Data)
{
// First, make sure the file is open for write or modification mode
if ((!mFileStream.is_open()) || ((mFileMode != Write) && (mFileMode != ReadWrite)))
return false;
// Traverse to the end of the lump list. Make sure not to create any lumps
// with the same names
std::vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
if ((*iter)->GetInfo().Name == Name)
return false;
}
Lump *pLump;
if ((pLump = new Lump()) == NULL)
return false;
// Set up the lump node
LumpInfo li;
li.Size = static_cast<unsigned long>(sizeof(Data));
li.Position = 0;
li.Name = Name;
pLump->SetInfo(li);
pLump->SetData(Data);
mLumpList.push_back(pLump);
// Increment the number of lumps in the file
mFileHeader.LumpCount++;
// Everything went nice and slick, so return true
return true;
}
bool ResourceFile::DeleteLump(const string& Name)
{
// Traverse the lump list, looking for a lump matching the name
vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// If the name's are the same, we found a match, so remove the lump
if ((*iter)->GetInfo().Name == Name)
{
// Unload the lump's data if we have to
if ((*iter)->GetData() != "")
(*iter)->UnloadData();
// Delete the lump from the list
delete *iter;
mLumpList.erase(iter);
// Decrement the number of lumps in the file
mFileHeader.LumpCount--;
// Return true, now that we've deleted the lump
return true;
}
}
// Damn, there was no lump matching the name, so return false
return false;
}
bool ResourceFile::LoadLump(const string& Name)
{
// Make sure the file is open
if (!mFileStream.is_open())
return false;
// Search through the lump list for a macthing lump name
vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// Check if a match was found
if ((*iter)->GetInfo().Name == Name)
{
// If the data hasn't been loaded in, load it in!
if ((*iter)->GetData() == NULL)
{
// Load in the lump
if(!(*iter)->LoadData(mFileStream))
return false;
}
return true;
}
}
// No lump matching the name was found, so return FALSE :(
return false;
}
bool ResourceFile::UnloadLump(const string& Name)
{
// Search through the lump list for a lump with a matching name
vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
{
// Check to see if this is our match...
if ((*iter)->GetInfo().Name == Name)
{
// Unload the lump's data
(*iter)->UnloadData();
// Return true now that we have unloaded the lump's data
return true;
}
}
// No lumps were found matching the name, so return false
return false;
}
std::vector<Lump*>* ResourceFile::GetLumpList()
{
return &mLumpList;
}
Lump* ResourceFile::GetLump(const string& Name)
{
// Search through the lump list for a lump with a matching name
vector<Lump*>::iterator iter;
for (iter = mLumpList.begin(); iter != mLumpList.end(); ++iter)
// Return the lump pointer if name matches
if ((*iter)->GetInfo().Name == Name)
return *iter;
// Sorry, couldn't find the lump
return NULL;
}
ResourceFile::~ResourceFile()
{
// Close the file if it's open
Close();
}