1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-14 02:33:51 +02:00
vcmi/lib/Filesystem/CLodArchiveLoader.cpp
Michał W. Urbańczyk d390113c23 * New files for lib: CBattleCallback.cpp and CBattleCallback.h
* Updated MSVC project files
* Filesystem: My version of .SND archive does not have \0 after WAV extension. Fixed (though hardcoded 3-char extension length).
* New bonus types: BLOCK_MAGIC_ABOVE for blocking casting spells above given level and BLOCK_ALL_MAGIC for blocking all magic.
* Heavy rewrite of battle callbacks. Fixed some minor bugs. Code reusage between lib/client/server (removed proxy calls). Better access control and support for various perspectives.
* Fixed #1031
* Fixed Orb of Inhibition and Recanter's Cloak (they were incorrectly implemented). Fixed #97.
* Fleeing hero won't lose artifacts. Spellbook won't be captured. Fixed #980.
* Fixed crash when attacking stack dies before counterattack (ie. because of Fire Shield)
* Server does some basic checks if action requests during battle are valid
* Minor stuff.
2012-08-26 09:07:48 +00:00

232 lines
5.7 KiB
C++

#include "StdInc.h"
#include "CLodArchiveLoader.h"
#include "CInputStream.h"
#include "CFileInputStream.h"
#include "CCompressedStream.h"
#include "CBinaryReader.h"
#include "CFileInfo.h"
#include <SDL_endian.h>
ArchiveEntry::ArchiveEntry()
: offset(0), realSize(0), size(0)
{
}
CLodArchiveLoader::CLodArchiveLoader(const std::string & archive)
{
// Open archive file(.snd, .vid, .lod)
this->archive = archive;
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(fileStream);
}
else if(ext == ".VID")
{
initVIDArchive(fileStream);
}
else if(ext == ".SND")
{
initSNDArchive(fileStream);
}
else
{
throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive);
}
}
void CLodArchiveLoader::initLODArchive(CFileInputStream & fileStream)
{
// Define LodEntryBlock struct
struct LodEntryBlock
{
char filename[16];
ui32 offset;
ui32 uncompressedSize;
ui32 unused;
ui32 size;
};
// Read count of total files
CBinaryReader reader(fileStream);
fileStream.seek(8);
ui32 totalFiles = reader.readUInt32();
// Get all entries from file
fileStream.seek(0x5c);
struct LodEntryBlock * lodEntries = new struct LodEntryBlock[totalFiles];
fileStream.read(reinterpret_cast<ui8 *>(lodEntries), sizeof(struct LodEntryBlock) * totalFiles);
// Insert entries to list
for(ui32 i = 0; i < totalFiles; i++)
{
// Create archive entry
ArchiveEntry entry;
entry.name = lodEntries[i].filename;
entry.offset= SDL_SwapLE32(lodEntries[i].offset);
entry.realSize = SDL_SwapLE32(lodEntries[i].uncompressedSize);
entry.size = SDL_SwapLE32(lodEntries[i].size);
// Add lod entry to local entries map
entries[entry.name] = entry;
}
// Delete lod entries array
delete[] lodEntries;
}
void CLodArchiveLoader::initVIDArchive(CFileInputStream & fileStream)
{
// Define VideoEntryBlock struct
struct VideoEntryBlock
{
char filename[40];
ui32 offset;
};
// Read count of total files
CBinaryReader reader(fileStream);
fileStream.seek(0);
ui32 totalFiles = reader.readUInt32();
// Get all entries from file
fileStream.seek(4);
struct VideoEntryBlock * vidEntries = new struct VideoEntryBlock[totalFiles];
fileStream.read(reinterpret_cast<ui8 *>(vidEntries), sizeof(struct VideoEntryBlock) * totalFiles);
// Insert entries to list
for(ui32 i = 0; i < totalFiles; i++)
{
VideoEntryBlock vidEntry = vidEntries[i];
ArchiveEntry entry;
entry.name = vidEntry.filename;
entry.offset = SDL_SwapLE32(vidEntry.offset);
entry.size = 0;
// There is no size, so check where the next file is
if (i == totalFiles - 1)
{
entry.realSize = fileStream.getSize() - entry.offset;
}
else
{
VideoEntryBlock nextVidEntry = vidEntries[i + 1];
entry.realSize = SDL_SwapLE32(nextVidEntry.offset) - entry.offset;
}
entries[entry.name] = entry;
}
// Delete vid entries array
delete[] vidEntries;
}
void CLodArchiveLoader::initSNDArchive(CFileInputStream & fileStream)
{
// Define SoundEntryBlock struct
struct SoundEntryBlock
{
char filename[40];
ui32 offset;
ui32 size;
};
// Read count of total files
CBinaryReader reader(fileStream);
fileStream.seek(0);
ui32 totalFiles = reader.readUInt32();
// Get all entries from file
fileStream.seek(4);
struct SoundEntryBlock * sndEntries = new struct SoundEntryBlock[totalFiles];
fileStream.read(reinterpret_cast<ui8 *>(sndEntries), sizeof(struct SoundEntryBlock) * totalFiles);
// Insert entries to list
for(ui32 i = 0; i < totalFiles; i++)
{
SoundEntryBlock sndEntry = sndEntries[i];
ArchiveEntry entry;
//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)
entry.name = std::string(sndEntry.filename, 40);
entry.name.resize(entry.name.find_first_of('\0') + 4); //+4 because we take dot and 3-char extension
entry.name[entry.name.find_first_of('\0')] = '.';
entry.offset = SDL_SwapLE32(sndEntry.offset);
entry.realSize = SDL_SwapLE32(sndEntry.size);
entry.size = 0;
entries[entry.name] = entry;
}
// Delete snd entries array
delete[] sndEntries;
}
std::unique_ptr<CInputStream> CLodArchiveLoader::load(const std::string & resourceName) const
{
assert(existsEntry(resourceName));
const ArchiveEntry & entry = entries.find(resourceName)->second;
if (entry.size != 0) //compressed data
{
std::unique_ptr<CInputStream> fileStream(new CFileInputStream(getOrigin(), entry.offset, entry.size));
return std::unique_ptr<CInputStream>(new CCompressedStream(std::move(fileStream), false, entry.realSize));
}
else
{
return std::unique_ptr<CInputStream>(new CFileInputStream(getOrigin(), entry.offset, entry.realSize));
}
}
boost::unordered_map<ResourceID, std::string> CLodArchiveLoader::getEntries() const
{
boost::unordered_map<ResourceID, std::string> retList;
for(auto it = entries.begin(); it != entries.end(); ++it)
{
const ArchiveEntry & entry = it->second;
retList[ResourceID(entry.name)] = entry.name;
}
return retList;
}
const ArchiveEntry * CLodArchiveLoader::getArchiveEntry(const std::string & resourceName) const
{
auto it = entries.find(resourceName);
if(it != entries.end())
{
return &(it->second);
}
return nullptr;
}
bool CLodArchiveLoader::existsEntry(const std::string & resourceName) const
{
return entries.find(resourceName) != entries.end();
}
std::string CLodArchiveLoader::getOrigin() const
{
return archive;
}