mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-15 00:05:02 +02:00
Filesystem handling is now more flexible
- removed CResourceLoader class in favor of one that implements resource loader interface - removed global pool of files, in favour of more dynamic approach - renamed some files to match current situation All these changes are needed mostly for future mod manager + .zip support
This commit is contained in:
181
lib/filesystem/CArchiveLoader.cpp
Normal file
181
lib/filesystem/CArchiveLoader.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
#include "StdInc.h"
|
||||
#include "CArchiveLoader.h"
|
||||
|
||||
#include "CFileInputStream.h"
|
||||
#include "CCompressedStream.h"
|
||||
|
||||
#include "CBinaryReader.h"
|
||||
#include "CFileInfo.h"
|
||||
|
||||
ArchiveEntry::ArchiveEntry()
|
||||
: offset(0), fullSize(0), compressedSize(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string & archive):
|
||||
archive(archive),
|
||||
mountPoint(mountPoint)
|
||||
{
|
||||
// Open archive file(.snd, .vid, .lod)
|
||||
CFileInputStream fileStream(archive);
|
||||
|
||||
// Fake .lod file with no data has to be silently ignored.
|
||||
if(fileStream.getSize() < 10)
|
||||
return;
|
||||
|
||||
// Retrieve file extension of archive in uppercase
|
||||
CFileInfo fileInfo(archive);
|
||||
std::string ext = fileInfo.getExtension();
|
||||
boost::to_upper(ext);
|
||||
|
||||
// Init the specific lod container format
|
||||
if(ext == ".LOD" || ext == ".PAC")
|
||||
{
|
||||
initLODArchive(mountPoint, fileStream);
|
||||
}
|
||||
else if(ext == ".VID")
|
||||
{
|
||||
initVIDArchive(mountPoint, fileStream);
|
||||
}
|
||||
else if(ext == ".SND")
|
||||
{
|
||||
initSNDArchive(mountPoint, fileStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive);
|
||||
}
|
||||
}
|
||||
|
||||
void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
|
||||
{
|
||||
// Read count of total files
|
||||
CBinaryReader reader(&fileStream);
|
||||
|
||||
fileStream.seek(8);
|
||||
ui32 totalFiles = reader.readUInt32();
|
||||
|
||||
// Get all entries from file
|
||||
fileStream.seek(0x5c);
|
||||
|
||||
// Insert entries to list
|
||||
for(ui32 i = 0; i < totalFiles; i++)
|
||||
{
|
||||
char filename[16];
|
||||
reader.read(reinterpret_cast<ui8*>(filename), 16);
|
||||
|
||||
// Create archive entry
|
||||
ArchiveEntry entry;
|
||||
entry.name = filename;
|
||||
entry.offset = reader.readUInt32();
|
||||
entry.fullSize = reader.readUInt32();
|
||||
fileStream.skip(4); // unused, unknown
|
||||
entry.compressedSize = reader.readUInt32();
|
||||
|
||||
// Add lod entry to local entries map
|
||||
entries[ResourceID(mountPoint + entry.name)] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStream & fileStream)
|
||||
{
|
||||
|
||||
// Read count of total files
|
||||
CBinaryReader reader(&fileStream);
|
||||
fileStream.seek(0);
|
||||
ui32 totalFiles = reader.readUInt32();
|
||||
|
||||
std::set<int> offsets;
|
||||
|
||||
// Insert entries to list
|
||||
for(ui32 i = 0; i < totalFiles; i++)
|
||||
{
|
||||
char filename[40];
|
||||
reader.read(reinterpret_cast<ui8*>(filename), 40);
|
||||
|
||||
ArchiveEntry entry;
|
||||
entry.name = filename;
|
||||
entry.offset = reader.readInt32();
|
||||
entry.compressedSize = 0;
|
||||
|
||||
offsets.insert(entry.offset);
|
||||
entries[ResourceID(mountPoint + entry.name)] = entry;
|
||||
}
|
||||
offsets.insert(fileStream.getSize());
|
||||
|
||||
// now when we know postion of all files their sizes can be set correctly
|
||||
for (auto & entry : entries)
|
||||
{
|
||||
auto it = offsets.find(entry.second.offset);
|
||||
it++;
|
||||
entry.second.fullSize = *it - entry.second.offset;
|
||||
}
|
||||
}
|
||||
|
||||
void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream)
|
||||
{
|
||||
// Read count of total files
|
||||
CBinaryReader reader(&fileStream);
|
||||
fileStream.seek(0);
|
||||
ui32 totalFiles = reader.readUInt32();
|
||||
|
||||
// Insert entries to list
|
||||
for(ui32 i = 0; i < totalFiles; i++)
|
||||
{
|
||||
char filename[40];
|
||||
reader.read(reinterpret_cast<ui8*>(filename), 40);
|
||||
|
||||
//for some reason entries in snd have format NAME\0WAVRUBBISH....
|
||||
//we need to replace first \0 with dot and take the 3 chars with extension (and drop the rest)
|
||||
ArchiveEntry entry;
|
||||
entry.name = filename; // till 1st \0
|
||||
entry.name += '.';
|
||||
entry.name += std::string(filename + entry.name.size(), 3);
|
||||
|
||||
entry.offset = reader.readInt32();
|
||||
entry.fullSize = reader.readInt32();
|
||||
entry.compressedSize = 0;
|
||||
entries[ResourceID(mountPoint + entry.name)] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CArchiveLoader::load(const ResourceID & resourceName) const
|
||||
{
|
||||
assert(existsResource(resourceName));
|
||||
|
||||
const ArchiveEntry & entry = entries.at(resourceName);
|
||||
|
||||
if (entry.compressedSize != 0) //compressed data
|
||||
{
|
||||
std::unique_ptr<CInputStream> fileStream(new CFileInputStream(archive, entry.offset, entry.compressedSize));
|
||||
|
||||
return std::unique_ptr<CInputStream>(new CCompressedStream(std::move(fileStream), false, entry.fullSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unique_ptr<CInputStream>(new CFileInputStream(archive, entry.offset, entry.fullSize));
|
||||
}
|
||||
}
|
||||
|
||||
bool CArchiveLoader::existsResource(const ResourceID & resourceName) const
|
||||
{
|
||||
return entries.count(resourceName) != 0;
|
||||
}
|
||||
|
||||
std::string CArchiveLoader::getMountPoint() const
|
||||
{
|
||||
return mountPoint;
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> CArchiveLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
|
||||
{
|
||||
std::unordered_set<ResourceID> foundID;
|
||||
|
||||
for (auto & file : entries)
|
||||
{
|
||||
if (filter(file.first))
|
||||
foundID.insert(file.first);
|
||||
}
|
||||
return foundID;
|
||||
}
|
Reference in New Issue
Block a user