1
Fork 0
moonfall/FileManifest.cpp

262 lines
7.7 KiB
C++

/*
Moonfall Copyright (C) 2008 Alex Yatskov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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, see <http://www.gnu.org/licenses/>.
*/
#include "Pch.h"
#include "FileManifest.h"
bool FileManifest::LoadManifest(const char* filename)
{
boost::shared_ptr<const Buffer> buffer = LoadFileExternal(filename);
if (!buffer)
{
TRACE_ERROR(boost::format("Unable to load manifest %s") % filename);
return false;
}
Deserializer data(buffer.get());
PackageHeader packageHeader;
if (!packageHeader.Deserialize(&data) || packageHeader.token != PACKAGE_TOKEN || packageHeader.version != PACKAGE_VERSION)
{
TRACE_ERROR(boost::format("Invalid package header data (token = %x, version = %d) in manifest %s") % packageHeader.token % packageHeader.version % filename);
return false;
}
for (int i = 0; i < packageHeader.count; ++i)
{
PackageFileHeader packageFileHeader;
if (packageFileHeader.Deserialize(&data))
{
if (m_manifest.find(packageFileHeader.filename) == m_manifest.end())
{
m_manifest.insert(std::make_pair(packageFileHeader.filename, PackageFileEntry(packageFileHeader, filename)));
}
else
{
TRACE_WARNING(boost::format("Duplicate file %s in manifest %s") % packageFileHeader.filename % filename);
}
if (!data.SetOffset(packageFileHeader.sizeCompressed, true))
{
TRACE_ERROR(boost::format("Inconsistant file header data for %s in manifest %s") % packageFileHeader.filename % filename);
return false;
}
}
}
return true;
}
bool FileManifest::SaveManifest(const char* filename, const std::vector<std::string>& filenames)
{
Buffer buffer;
Serializer data(&buffer);
PackageHeader packageHeader;
packageHeader.token = PACKAGE_TOKEN;
packageHeader.version = PACKAGE_VERSION;
packageHeader.flags = 0;
packageHeader.count = filenames.size();
packageHeader.Serialize(&data);
for (std::vector<std::string>::const_iterator iter = filenames.begin(); iter != filenames.end(); ++iter)
{
FILE* fp = fopen(iter->c_str(), "rb");
if (fp == NULL)
{
TRACE_ERROR(boost::format("Cannot package file %s into manifest %s") % iter->c_str() % filename);
return false;
}
fseek(fp, 0, SEEK_END);
const size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
Buffer contentsUncompressed(size);
Buffer contentsCompressed(size);
fread(contentsUncompressed.Get(), 1, contentsUncompressed.GetSize(), fp);
fclose(fp);
if (!Zlib::Compress(contentsUncompressed, &contentsCompressed))
{
TRACE_ERROR(boost::format("Cannot compress file %s into manifest %s") % iter->c_str() % filename);
return false;
}
PackageFileHeader packageFileHeader;
packageFileHeader.filename = *iter;
packageFileHeader.sizeUncompressed = contentsUncompressed.GetSize();
packageFileHeader.sizeCompressed = contentsCompressed.GetSize();
packageFileHeader.offset =
data.GetOffset() +
packageFileHeader.filename.size() + 1 +
sizeof(packageFileHeader.offset) +
sizeof(packageFileHeader.sizeCompressed) +
sizeof(packageFileHeader.sizeUncompressed);
packageFileHeader.Serialize(&data);
data.Write(contentsCompressed.Get(), contentsCompressed.GetSize());
}
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
TRACE_ERROR(boost::format("Cannot save manifest file %s") % filename);
return false;
}
fwrite(buffer.Get(), 1, buffer.GetSize(), fp);
fclose(fp);
return true;
}
void FileManifest::ClearManifest()
{
m_manifest.clear();
}
bool FileManifest::SaveFile(const char* filename, const Buffer& buffer)
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
TRACE_ERROR(boost::format("Cannot save external file %s") % filename);
return false;
}
fwrite(buffer.Get(), 1, buffer.GetSize(), fp);
fclose(fp);
FlushFile(filename);
TRACE_INFO(boost::format("Saved external file %s") % filename);
return true;
}
void FileManifest::FlushFile(const char* filename)
{
m_manager.RemoveClient(filename);
}
void FileManifest::FlushFiles()
{
m_manager.ClearClients();
TRACE_INFO("Flushed cached files");
}
boost::shared_ptr<const Buffer> FileManifest::LoadFile(const char* filename)
{
boost::shared_ptr<const Buffer> buffer;
bool cached = false;
if (buffer = LoadFileExternal(filename, &cached))
{
if (!cached)
{
TRACE_INFO(boost::format("Loaded external file %s") % filename);
}
}
else if (buffer = LoadFileInternal(filename, &cached))
{
if (!cached)
{
TRACE_INFO(boost::format("Loaded internal file %s") % filename);
}
}
else
{
TRACE_ERROR(boost::format("Cannot load file %s") % filename);
}
return buffer;
}
boost::shared_ptr<const Buffer> FileManifest::LoadFileInternal(const char* filename, bool* cached)
{
boost::shared_ptr<const Buffer> bufferCached = m_manager.FindClient(filename);
if (cached != NULL)
{
*cached = bufferCached;
}
if (bufferCached)
{
return bufferCached;
}
std::map<std::string, PackageFileEntry>::iterator iter = m_manifest.find(filename);
if (iter == m_manifest.end())
{
return boost::shared_ptr<const Buffer>();
}
FILE* fp = fopen(iter->second.filename.c_str(), "rb");
if (fp == NULL)
{
return boost::shared_ptr<const Buffer>();
}
const int sizeCompressed = iter->second.header.sizeCompressed;
const int sizeUncompressed = iter->second.header.sizeUncompressed;
const int offset = iter->second.header.offset;
Buffer bufferCompressed(sizeCompressed);
boost::shared_ptr<Buffer> bufferUncompressed = boost::shared_ptr<Buffer>(new Buffer(sizeUncompressed));
fseek(fp, offset, SEEK_SET);
fread(bufferCompressed.Get(), 1, bufferCompressed.GetSize(), fp);
fclose(fp);
if (!Zlib::Decompress(bufferCompressed, bufferUncompressed.get()))
{
TRACE_ERROR(boost::format("Cannot inflate file %s") % filename);
return boost::shared_ptr<const Buffer>();
}
m_manager.AddClient(filename, bufferUncompressed);
return bufferUncompressed;
}
boost::shared_ptr<const Buffer> FileManifest::LoadFileExternal(const char* filename, bool* cached)
{
ADJUST_FILENAME(filename);
boost::shared_ptr<const Buffer> buffer = m_manager.FindClient(filename);
if (cached != NULL)
{
*cached = buffer;
}
if (buffer)
{
return buffer;
}
FILE* fp = fopen(filename, "rb");
if (fp != NULL)
{
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
boost::shared_ptr<Buffer> temp(new Buffer(size));
fread(temp->Get(), 1, temp->GetSize(), fp);
fclose(fp);
buffer = temp;
m_manager.AddClient(filename, buffer);
}
return buffer;
}