mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +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:
parent
aa6626bc35
commit
b87897096c
@ -3,7 +3,7 @@
|
||||
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CObjectHandler.h"
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include <SDL_image.h>
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/ISimpleResourceLoader.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
|
||||
@ -107,7 +107,7 @@ public:
|
||||
cache.pop_front();
|
||||
cache.push_back(FileData());
|
||||
|
||||
auto data = CResourceHandler::get()->loadData(rid);
|
||||
auto data = CResourceHandler::get()->load(rid)->readAll();
|
||||
cache.back().name = ResourceID(rid);
|
||||
cache.back().size = data.second;
|
||||
cache.back().data = data.first.release();
|
||||
@ -1023,8 +1023,8 @@ void CAnimation::init(CDefFile * file)
|
||||
{
|
||||
const std::map<size_t, size_t> defEntries = file->getEntries();
|
||||
|
||||
for (auto & defEntrie : defEntries)
|
||||
source[defEntrie.first].resize(defEntrie.second);
|
||||
for (auto & defEntry : defEntries)
|
||||
source[defEntry.first].resize(defEntry.second);
|
||||
}
|
||||
|
||||
ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
|
||||
@ -1032,11 +1032,11 @@ void CAnimation::init(CDefFile * file)
|
||||
if (vstd::contains(graphics->imageLists, resID.getName()))
|
||||
initFromJson(graphics->imageLists[resID.getName()]);
|
||||
|
||||
auto & configList = CResourceHandler::get()->getResourcesWithName(resID);
|
||||
auto configList = CResourceHandler::get()->getResourcesWithName(resID);
|
||||
|
||||
for(auto & entry : configList)
|
||||
for(auto & loader : configList)
|
||||
{
|
||||
auto stream = entry.getLoader()->load(entry.getResourceName());
|
||||
auto stream = loader->load(resID);
|
||||
std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
|
||||
stream->read(textData.get(), stream->getSize());
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CFileInfo.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_image.h"
|
||||
@ -112,10 +112,8 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
|
||||
|
||||
SDL_Surface * ret=nullptr;
|
||||
|
||||
auto readFile = CResourceHandler::get()->loadData(
|
||||
ResourceID(path + fname, EResType::IMAGE));
|
||||
auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll();
|
||||
|
||||
|
||||
if (isPCX(readFile.first.get()))
|
||||
{//H3-style PCX
|
||||
ret = loadH3PCX(readFile.first.get(), readFile.second);
|
||||
@ -132,7 +130,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
|
||||
}
|
||||
else
|
||||
{ //loading via SDL_Image
|
||||
CFileInfo info(CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)));
|
||||
CFileInfo info(*CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)));
|
||||
|
||||
ret = IMG_LoadTyped_RW(
|
||||
//create SDL_RW with our data (will be deleted by SDL)
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "SDL.h"
|
||||
#include "CDefHandler.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "CBitmapHandler.h"
|
||||
|
||||
@ -362,8 +362,9 @@ CDefEssential * CDefHandler::essentialize()
|
||||
|
||||
CDefHandler * CDefHandler::giveDef(const std::string & defName)
|
||||
{
|
||||
ui8 * data = CResourceHandler::get()->loadData(
|
||||
ResourceID(std::string("SPRITES/") + defName, EResType::ANIMATION)).first.release();
|
||||
ResourceID resID(std::string("SPRITES/") + defName, EResType::ANIMATION);
|
||||
|
||||
ui8 * data = CResourceHandler::get()->load(resID)->readAll().first.release();
|
||||
if(!data)
|
||||
throw std::runtime_error("bad def name!");
|
||||
auto nh = new CDefHandler();
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "CGameInfo.h"
|
||||
#include "mapHandler.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "CPreGame.h"
|
||||
#include "CCastleInterface.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
@ -300,8 +300,8 @@ int main(int argc, char** argv)
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!testFile("DATA/HELP.TXT", "Heroes III data") &&
|
||||
!testFile("MODS/VCMI/MOD.JSON", "VCMI mod") &&
|
||||
if (!testFile("DATA/HELP.TXT", "Heroes III data") ||
|
||||
!testFile("MODS/VCMI/MOD.JSON", "VCMI mod") ||
|
||||
!testFile("DATA/StackQueueBgBig.PCX", "VCMI data"))
|
||||
exit(1); // These are unrecoverable errors
|
||||
|
||||
@ -536,22 +536,21 @@ void processCommand(const std::string &message)
|
||||
|
||||
std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
|
||||
|
||||
auto iterator = CResourceHandler::get()->getIterator([](const ResourceID & ident)
|
||||
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
|
||||
});
|
||||
|
||||
while (iterator.hasNext())
|
||||
for (auto & filename : list)
|
||||
{
|
||||
std::string outName = outPath + iterator->getName();
|
||||
std::string outName = outPath + filename.getName();
|
||||
|
||||
boost::filesystem::create_directories(outName.substr(0, outName.find_last_of("/")));
|
||||
|
||||
std::ofstream file(outName + ".TXT");
|
||||
auto text = CResourceHandler::get()->loadData(*iterator);
|
||||
auto text = CResourceHandler::get()->load(filename)->readAll();
|
||||
|
||||
file.write((char*)text.first.get(), text.second);
|
||||
++iterator;
|
||||
}
|
||||
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
@ -661,7 +660,7 @@ void processCommand(const std::string &message)
|
||||
{
|
||||
CDefEssential * cde = CDefHandler::giveDefEss(URI);
|
||||
|
||||
std::string outName = CResourceHandler::get()->getResource(ResourceID("SPRITES/" + URI)).getResourceName();
|
||||
std::string outName = *CResourceHandler::get()->getResourceName(ResourceID("SPRITES/" + URI));
|
||||
std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
|
||||
|
||||
boost::filesystem::create_directories(outPath + outName);
|
||||
@ -682,11 +681,11 @@ void processCommand(const std::string &message)
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
std::string outName = CResourceHandler::get()->getResource(ResourceID(URI)).getResourceName();
|
||||
std::string outName = *CResourceHandler::get()->getResourceName(ResourceID(URI));
|
||||
std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
|
||||
std::string fullPath = outPath + outName;
|
||||
|
||||
auto data = CResourceHandler::get()->loadData(ResourceID(URI));
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/")));
|
||||
std::ofstream outFile(outPath + outName);
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "../client/CGameInfo.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
||||
/*
|
||||
* CMusicHandler.cpp, part of VCMI engine
|
||||
@ -139,7 +139,7 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(soundBase::soundID soundID)
|
||||
// Load and insert
|
||||
try
|
||||
{
|
||||
auto data = CResourceHandler::get()->loadData(ResourceID(std::string("SOUNDS/") + fname, EResType::SOUND));
|
||||
auto data = CResourceHandler::get()->load(ResourceID(std::string("SOUNDS/") + fname, EResType::SOUND))->readAll();
|
||||
|
||||
SDL_RWops *ops = SDL_RWFromMem(data.first.release(), data.second);
|
||||
Mix_Chunk *chunk;
|
||||
@ -162,7 +162,7 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound)
|
||||
// Load and insert
|
||||
try
|
||||
{
|
||||
auto data = CResourceHandler::get()->loadData(ResourceID(std::string("SOUNDS/") + sound, EResType::SOUND)); //TODO: allow other sound folders?
|
||||
auto data = CResourceHandler::get()->load(ResourceID(std::string("SOUNDS/") + sound, EResType::SOUND))->readAll();
|
||||
|
||||
SDL_RWops *ops = SDL_RWFromMem(data.first.release(), data.second);
|
||||
Mix_Chunk *chunk;
|
||||
@ -477,7 +477,7 @@ void MusicEntry::load(std::string musicURI)
|
||||
|
||||
logGlobal->traceStream()<<"Loading music file "<<musicURI;
|
||||
|
||||
music = Mix_LoadMUS(CResourceHandler::get()->getResourceName(ResourceID(musicURI, EResType::MUSIC)).c_str());
|
||||
music = Mix_LoadMUS(CResourceHandler::get()->getResourceName(ResourceID(musicURI, EResType::MUSIC))->c_str());
|
||||
|
||||
if(!music)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CPreGame.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CFileInfo.h"
|
||||
#include "../lib/filesystem/CCompressedStream.h"
|
||||
|
||||
@ -403,7 +403,7 @@ CreditsScreen::CreditsScreen()
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||
pos.w = CGP->menu->pos.w;
|
||||
pos.h = CGP->menu->pos.h;
|
||||
auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREDITS.TXT"));
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/CREDITS.TXT"))->readAll();
|
||||
std::string text((char*)textFile.first.get(), textFile.second);
|
||||
size_t firstQuote = text.find('\"')+1;
|
||||
text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote );
|
||||
@ -1090,27 +1090,20 @@ void SelectionTab::filter( int size, bool selectFirst )
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType)
|
||||
std::unordered_set<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType)
|
||||
{
|
||||
std::vector<ResourceID> ret;
|
||||
boost::to_upper(dirURI);
|
||||
|
||||
auto iterator = CResourceHandler::get()->getIterator([&](const ResourceID & ident)
|
||||
std::unordered_set<ResourceID> ret = CResourceHandler::get()->getFilteredFiles([&](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == resType
|
||||
&& boost::algorithm::starts_with(ident.getName(), dirURI);
|
||||
});
|
||||
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
ret.push_back(*iterator);
|
||||
++iterator;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SelectionTab::parseMaps(const std::vector<ResourceID> & files)
|
||||
void SelectionTab::parseMaps(const std::unordered_set<ResourceID> &files)
|
||||
{
|
||||
allItems.clear();
|
||||
for(auto & file : files)
|
||||
@ -1131,13 +1124,13 @@ void SelectionTab::parseMaps(const std::vector<ResourceID> & files)
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
|
||||
void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool multi)
|
||||
{
|
||||
for(auto & file : files)
|
||||
{
|
||||
try
|
||||
{
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(file));
|
||||
CLoadFile lf(*CResourceHandler::get()->getResourceName(file));
|
||||
lf.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
// ui8 sign[8];
|
||||
// lf >> sign;
|
||||
@ -1153,7 +1146,7 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
|
||||
lf >> *(mapInfo.mapHeader.get()) >> mapInfo.scenarioOpts;
|
||||
mapInfo.fileURI = file.getName();
|
||||
mapInfo.countPlayers();
|
||||
std::time_t time = CFileInfo(CResourceHandler::get()->getResourceName(file)).getDate();
|
||||
std::time_t time = CFileInfo(*CResourceHandler::get()->getResourceName(file)).getDate();
|
||||
mapInfo.date = std::asctime(std::localtime(&time));
|
||||
|
||||
// If multi mode then only multi games, otherwise single
|
||||
@ -1171,14 +1164,16 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseCampaigns(const std::vector<ResourceID> & files )
|
||||
void SelectionTab::parseCampaigns(const std::unordered_set<ResourceID> &files )
|
||||
{
|
||||
allItems.resize(files.size());
|
||||
for(int i=0; i<files.size(); i++)
|
||||
allItems.reserve(files.size());
|
||||
for (auto & file : files)
|
||||
{
|
||||
CMapInfo info;
|
||||
//allItems[i].date = std::asctime(std::localtime(&files[i].date));
|
||||
allItems[i].fileURI = files[i].getName();
|
||||
allItems[i].campaignInit();
|
||||
info.fileURI = file.getName();
|
||||
info.campaignInit();
|
||||
allItems.push_back(std::move(info));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1356,7 +1351,7 @@ void SelectionTab::select( int position )
|
||||
|
||||
if(txt)
|
||||
{
|
||||
std::string filename = CResourceHandler::get()->getResourceName(
|
||||
std::string filename = *CResourceHandler::get()->getResourceName(
|
||||
ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
|
||||
txt->setTxt(CFileInfo(filename).getBaseName());
|
||||
}
|
||||
@ -1489,7 +1484,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
|
||||
}
|
||||
else
|
||||
{
|
||||
name = CFileInfo(CResourceHandler::get()->getResourceName(
|
||||
name = CFileInfo(*CResourceHandler::get()->getResourceName(
|
||||
ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include <SDL.h>
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "GUIClasses.h"
|
||||
@ -145,10 +145,10 @@ class SelectionTab : public CIntObject
|
||||
private:
|
||||
CDefHandler *format; //map size
|
||||
|
||||
void parseMaps(const std::vector<ResourceID> &files);
|
||||
void parseGames(const std::vector<ResourceID> &files, bool multi);
|
||||
void parseCampaigns(const std::vector<ResourceID> & files );
|
||||
std::vector<ResourceID> getFiles(std::string dirURI, int resType);
|
||||
void parseMaps(const std::unordered_set<ResourceID> &files);
|
||||
void parseGames(const std::unordered_set<ResourceID> &files, bool multi);
|
||||
void parseCampaigns(const std::unordered_set<ResourceID> & files );
|
||||
std::unordered_set<ResourceID> getFiles(std::string dirURI, int resType);
|
||||
CMenuScreen::EState tabType;
|
||||
public:
|
||||
int positions; //how many entries (games/maps) can be shown
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/SDL_Extensions.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
||||
extern CGuiHandler GH; //global gui handler
|
||||
|
||||
|
@ -226,12 +226,12 @@ void CClient::loadGame( const std::string & fname )
|
||||
CStopWatch tmh;
|
||||
try
|
||||
{
|
||||
std::string clientSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
|
||||
std::string clientSaveName = *CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
|
||||
std::string controlServerSaveName;
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME)))
|
||||
{
|
||||
controlServerSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME));
|
||||
controlServerSaveName = *CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME));
|
||||
}
|
||||
else// create entry for server savegame. Triggered if save was made after launch and not yet present in res handler
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CBinaryReader.h"
|
||||
#include "CDefHandler.h"
|
||||
#include "gui/SDL_Extensions.h"
|
||||
@ -47,7 +47,7 @@ Graphics * graphics = nullptr;
|
||||
|
||||
void Graphics::loadPaletteAndColors()
|
||||
{
|
||||
auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/PLAYERS.PAL"));
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/PLAYERS.PAL"))->readAll();
|
||||
std::string pals((char*)textFile.first.get(), textFile.second);
|
||||
|
||||
playerColorPalette = new SDL_Color[256];
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CFileInfo.h"
|
||||
#include "../CCallback.h"
|
||||
#include "Client.h"
|
||||
@ -796,7 +796,7 @@ void SaveGame::applyCl(CClient *cl)
|
||||
|
||||
try
|
||||
{
|
||||
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
|
||||
CSaveFile save(*CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
|
||||
cl->saveCommonState(save);
|
||||
save << *cl;
|
||||
}
|
||||
|
@ -975,7 +975,7 @@ void CBattleInterface::newStack(const CStack * stack)
|
||||
|
||||
creAnims[stack->ID] = AnimationControls::getAnimation(turretCreature);
|
||||
|
||||
// Turret positions are read out of the /config/wall_pos.txt
|
||||
// Turret positions are read out of the config/wall_pos.txt
|
||||
int posID = 0;
|
||||
switch (stack->position)
|
||||
{
|
||||
@ -2184,7 +2184,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
||||
break;
|
||||
case FRIENDLY_CREATURE_SPELL:
|
||||
{
|
||||
if (isCastingPossibleHere (sactive, shere, myNumber)); //need to be called before sp is determined
|
||||
if (isCastingPossibleHere (sactive, shere, myNumber)) //need to be called before sp is determined
|
||||
{
|
||||
bool rise = false; //TODO: can you imagine rising hostile creatures?
|
||||
sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
|
||||
#include "../../lib/filesystem/CResourceLoader.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/filesystem/CBinaryReader.h"
|
||||
#include "../../lib/filesystem/CMemoryStream.h"
|
||||
|
||||
@ -155,8 +155,9 @@ CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController contro
|
||||
{
|
||||
// separate block to avoid accidental use of "data" after it was moved into "pixelData"
|
||||
{
|
||||
auto data = CResourceHandler::get()->loadData(
|
||||
ResourceID(std::string("SPRITES/") + name, EResType::ANIMATION));
|
||||
ResourceID resID(std::string("SPRITES/") + name, EResType::ANIMATION);
|
||||
|
||||
auto data = CResourceHandler::get()->load(resID)->readAll();
|
||||
|
||||
pixelData = std::move(data.first);
|
||||
pixelDataSize = data.second;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "SDL_Pixels.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../../lib/vcmi_endian.h"
|
||||
#include "../../lib/filesystem/CResourceLoader.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
|
||||
/*
|
||||
* Fonts.cpp, part of VCMI engine
|
||||
@ -95,7 +95,7 @@ std::array<CBitmapFont::Char, CBitmapFont::totalChars> CBitmapFont::loadChars()
|
||||
}
|
||||
|
||||
CBitmapFont::CBitmapFont(const std::string & filename):
|
||||
data(CResourceHandler::get()->loadData(ResourceID("data/" + filename, EResType::BMP_FONT))),
|
||||
data(CResourceHandler::get()->load(ResourceID("data/" + filename, EResType::BMP_FONT))->readAll()),
|
||||
chars(loadChars()),
|
||||
height(data.first.get()[5])
|
||||
{}
|
||||
@ -198,7 +198,7 @@ void CBitmapFont::renderText(SDL_Surface * surface, const std::string & data, co
|
||||
std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config)
|
||||
{
|
||||
std::string filename = "Data/" + config["file"].String();
|
||||
return CResourceHandler::get()->loadData(ResourceID(filename, EResType::TTF_FONT));
|
||||
return CResourceHandler::get()->load(ResourceID(filename, EResType::TTF_FONT))->readAll();
|
||||
}
|
||||
|
||||
TTF_Font * CTrueTypeFont::loadFont(const JsonNode &config)
|
||||
|
@ -2,59 +2,52 @@
|
||||
// Complete filesystem available after initialization
|
||||
// All paths and names here are case-insensitive
|
||||
// If same filename is found twice entry from latest source will be used
|
||||
// path can start from:
|
||||
// "GLOBAL" - global location for data files. /usr/share/vcmi | C:\Program files\Heroes 3\
|
||||
// "LOCAL" - local user-specific files. ~/.vcmi | same as global (TODO: move it to C:\Users\whatever)
|
||||
// "ALL" - will check local directory first or (if was not found) in global directory
|
||||
// NOTE: this file must be available as "ALL/config/filesystem.json"
|
||||
// NOTES:
|
||||
// - this file must be available as "config/filesystem.json"
|
||||
// - some locations are hardcoded (user config directory, cache/tmp directory, saved games location)
|
||||
"filesystem":
|
||||
{
|
||||
"DATA/" :
|
||||
[
|
||||
{"type" : "lod", "path" : "ALL/Data/H3ab_bmp.lod"},
|
||||
{"type" : "lod", "path" : "ALL/Data/H3bitmap.lod"},
|
||||
{"type" : "dir", "path" : "ALL/Data"}
|
||||
{"type" : "lod", "path" : "Data/H3ab_bmp.lod"},
|
||||
{"type" : "lod", "path" : "Data/H3bitmap.lod"},
|
||||
{"type" : "dir", "path" : "Data"}
|
||||
],
|
||||
"SPRITES/":
|
||||
[
|
||||
{"type" : "lod", "path" : "ALL/Data/H3ab_spr.lod"},
|
||||
{"type" : "lod", "path" : "ALL/Data/H3sprite.lod"},
|
||||
{"type" : "dir", "path" : "ALL/Sprites"}
|
||||
{"type" : "lod", "path" : "Data/H3ab_spr.lod"},
|
||||
{"type" : "lod", "path" : "Data/H3sprite.lod"},
|
||||
{"type" : "dir", "path" : "Sprites"}
|
||||
],
|
||||
"SOUNDS/":
|
||||
[
|
||||
{"type" : "snd", "path" : "ALL/Data/H3ab_ahd.snd"},
|
||||
{"type" : "snd", "path" : "ALL/Data/Heroes3-cd2.snd"},
|
||||
{"type" : "snd", "path" : "ALL/Data/Heroes3.snd"},
|
||||
{"type" : "snd", "path" : "Data/H3ab_ahd.snd"},
|
||||
{"type" : "snd", "path" : "Data/Heroes3-cd2.snd"},
|
||||
{"type" : "snd", "path" : "Data/Heroes3.snd"},
|
||||
//WoG have overriden sounds with .82m extension in Data
|
||||
{"type" : "dir", "path" : "ALL/Data", "depth": 0}
|
||||
{"type" : "dir", "path" : "Data", "depth": 0}
|
||||
],
|
||||
"MUSIC/":
|
||||
[
|
||||
{"type" : "dir", "path" : "ALL/Mp3"}
|
||||
{"type" : "dir", "path" : "Mp3"}
|
||||
],
|
||||
"VIDEO/":
|
||||
[
|
||||
{"type" : "vid", "path" : "ALL/Data/H3ab_ahd.vid"},
|
||||
{"type" : "vid", "path" : "ALL/Data/Heroes3.vid"},
|
||||
{"type" : "vid", "path" : "ALL/Data/video.vid"}
|
||||
{"type" : "vid", "path" : "Data/H3ab_ahd.vid"},
|
||||
{"type" : "vid", "path" : "Data/Heroes3.vid"},
|
||||
{"type" : "vid", "path" : "Data/video.vid"}
|
||||
],
|
||||
"CONFIG/":
|
||||
[
|
||||
{"type" : "dir", "path" : "GLOBAL/Config",}, // separate to avoid overwriting global resources
|
||||
{"type" : "dir", "path" : "LOCAL/Config", "writeable": true}
|
||||
{"type" : "dir", "path" : "config",}
|
||||
],
|
||||
"MAPS/":
|
||||
[
|
||||
{"type" : "dir", "path" : "ALL/Maps"}
|
||||
{"type" : "dir", "path" : "Maps"}
|
||||
],
|
||||
"MODS/":
|
||||
[
|
||||
{"type" : "dir", "path" : "ALL/Mods", "depth": 1}
|
||||
],
|
||||
"SAVES/":
|
||||
[
|
||||
{"type" : "dir", "path" : "LOCAL/Games", "writeable": true},
|
||||
{"type" : "dir", "path" : "Mods", "depth": 1}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "CTownHandler.h"
|
||||
#include "NetPacks.h"
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CArtHandler.h"
|
||||
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CModHandler.h"
|
||||
|
@ -11,9 +11,7 @@
|
||||
#include "CBonusTypeHandler.h"
|
||||
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/ISimpleResourceLoader.h"
|
||||
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CCreatureHandler.h"
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CConfigHandler.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
|
||||
@ -80,7 +80,7 @@ void SettingsStorage::invalidateNode(const std::vector<std::string> &changedPath
|
||||
savedConf.Struct().erase("session");
|
||||
JsonUtils::minimize(savedConf, "vcmi:settings");
|
||||
|
||||
std::ofstream file(CResourceHandler::get()->getResourceName(ResourceID("config/settings.json")), std::ofstream::trunc);
|
||||
std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/settings.json")), std::ofstream::trunc);
|
||||
file << savedConf;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "CCreatureHandler.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CGameState.h"
|
||||
#include "CTownHandler.h"
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CDefObjInfoHandler.h"
|
||||
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "../client/CGameInfo.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "GameConstants.h"
|
||||
@ -35,7 +35,7 @@ CGDefInfo::CGDefInfo()
|
||||
void CGDefInfo::fetchInfoFromMSK()
|
||||
{
|
||||
|
||||
auto msk = CResourceHandler::get()->loadData(ResourceID(std::string("SPRITES/") + name, EResType::MASK));
|
||||
auto msk = CResourceHandler::get()->load(ResourceID(std::string("SPRITES/") + name, EResType::MASK))->readAll();
|
||||
|
||||
width = msk.first.get()[0];
|
||||
height = msk.first.get()[1];
|
||||
@ -50,7 +50,7 @@ CDefObjInfoHandler::CDefObjInfoHandler()
|
||||
{
|
||||
VLC->dobjinfo = this;
|
||||
|
||||
auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/OBJECTS.TXT"));
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/OBJECTS.TXT"))->readAll();
|
||||
|
||||
std::istringstream inp(std::string((char*)textFile.first.get(), textFile.second));
|
||||
int objNumber;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "mapping/CMapInfo.h"
|
||||
#include "BattleState.h"
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "GameConstants.h"
|
||||
#include "rmg/CMapGenerator.h"
|
||||
#include "CStopWatch.h"
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/CInputStream.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "GameConstants.h"
|
||||
#include "CModHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
@ -322,7 +321,7 @@ CGeneralTextHandler::CGeneralTextHandler()
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
std::ifstream ifs(CResourceHandler::get()->getResourceName(ResourceID("config/threatlevel.txt")), std::ios::binary);
|
||||
std::ifstream ifs(*CResourceHandler::get()->getResourceName(ResourceID("config/threatlevel.txt")), std::ios::binary);
|
||||
getline(ifs, buffer); //skip 1st line
|
||||
for (int i = 0; i < 13; ++i)
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "CHeroHandler.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "JsonNode.h"
|
||||
#include "StringConstants.h"
|
||||
|
@ -5,24 +5,28 @@ include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${
|
||||
include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
set(lib_SRCS
|
||||
filesystem/CBinaryReader.cpp
|
||||
filesystem/CFilesystemLoader.cpp
|
||||
filesystem/CMemoryStream.cpp
|
||||
filesystem/CFileInfo.cpp
|
||||
filesystem/CLodArchiveLoader.cpp
|
||||
filesystem/CResourceLoader.cpp
|
||||
filesystem/CFileInputStream.cpp
|
||||
filesystem/AdapterLoaders.cpp
|
||||
filesystem/CCompressedStream.cpp
|
||||
filesystem/CMappedFileLoader.cpp
|
||||
filesystem/CFilesystemLoader.cpp
|
||||
filesystem/CArchiveLoader.cpp
|
||||
filesystem/CFileInfo.cpp
|
||||
filesystem/CMemoryStream.cpp
|
||||
filesystem/CBinaryReader.cpp
|
||||
filesystem/CFileInputStream.cpp
|
||||
filesystem/Filesystem.cpp
|
||||
|
||||
logging/CBasicLogConfigurator.cpp
|
||||
logging/CLogger.cpp
|
||||
|
||||
mapping/CCampaignHandler.cpp
|
||||
mapping/CMap.cpp
|
||||
mapping/CMapEditManager.cpp
|
||||
mapping/CMapInfo.cpp
|
||||
mapping/CMapService.cpp
|
||||
mapping/MapFormatH3M.cpp
|
||||
|
||||
rmg/CMapGenerator.cpp
|
||||
|
||||
BattleAction.cpp
|
||||
BattleHex.cpp
|
||||
BattleState.cpp
|
||||
|
@ -2,8 +2,7 @@
|
||||
#include "CModHandler.h"
|
||||
#include "CDefObjInfoHandler.h"
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/ISimpleResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CArtHandler.h"
|
||||
@ -497,7 +496,7 @@ void CModHandler::initialize(std::vector<std::string> availableMods)
|
||||
modConfig["activeMods"] = resultingList;
|
||||
CResourceHandler::get()->createResource("CONFIG/modSettings.json");
|
||||
|
||||
std::ofstream file(CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc);
|
||||
std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc);
|
||||
file << modConfig;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "VCMI_Lib.h"
|
||||
#include "JsonNode.h"
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <SDL_stdinc.h>
|
||||
#include "CBuildingHandler.h"
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
using namespace boost::assign;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "CSpellHandler.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "JsonNode.h"
|
||||
#include <cctype>
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "CHeroHandler.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
/*
|
||||
* CTownHandler.cpp, part of VCMI engine
|
||||
|
@ -14,8 +14,7 @@
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/ISimpleResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "VCMI_Lib.h" //for identifier resolution
|
||||
#include "CModHandler.h"
|
||||
|
||||
@ -42,7 +41,7 @@ JsonNode::JsonNode(const char *data, size_t datasize):
|
||||
JsonNode::JsonNode(ResourceID && fileURI):
|
||||
type(DATA_NULL)
|
||||
{
|
||||
auto file = CResourceHandler::get()->loadData(fileURI);
|
||||
auto file = CResourceHandler::get()->load(fileURI)->readAll();
|
||||
|
||||
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
|
||||
*this = parser.parse(fileURI.getName());
|
||||
@ -1610,19 +1609,16 @@ JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
|
||||
JsonNode JsonUtils::assembleFromFiles(std::string filename)
|
||||
{
|
||||
JsonNode result;
|
||||
ResourceID resID(filename, EResType::TEXT);
|
||||
|
||||
auto & configList = CResourceHandler::get()->getResourcesWithName(ResourceID(filename, EResType::TEXT));
|
||||
|
||||
for(auto & entry : configList)
|
||||
for(auto & loader : CResourceHandler::get()->getResourcesWithName(resID))
|
||||
{
|
||||
// FIXME: some way to make this code more readable
|
||||
auto stream = entry.getLoader()->load(entry.getResourceName());
|
||||
auto stream = loader->load(resID);
|
||||
std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
|
||||
stream->read(textData.get(), stream->getSize());
|
||||
|
||||
JsonNode section((char*)textData.get(), stream->getSize());
|
||||
//for debug
|
||||
//section.setMeta(entry.getLoader()->getOrigin());
|
||||
merge(result, section);
|
||||
}
|
||||
return result;
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "IGameEventsReceiver.h"
|
||||
#include "CStopWatch.h"
|
||||
#include "VCMIDirs.h"
|
||||
#include "filesystem/CResourceLoader.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "CConsoleHandler.h"
|
||||
|
||||
LibClasses * VLC = nullptr;
|
||||
@ -34,20 +34,20 @@ DLL_LINKAGE void preinitDLL(CConsoleHandler *Console)
|
||||
{
|
||||
console = Console;
|
||||
VLC = new LibClasses;
|
||||
try
|
||||
//try
|
||||
{
|
||||
VLC->loadFilesystem();
|
||||
}
|
||||
HANDLE_EXCEPTION;
|
||||
//HANDLE_EXCEPTION;
|
||||
}
|
||||
|
||||
DLL_LINKAGE void loadDLLClasses()
|
||||
{
|
||||
try
|
||||
//try
|
||||
{
|
||||
VLC->init();
|
||||
}
|
||||
HANDLE_EXCEPTION;
|
||||
//HANDLE_EXCEPTION;
|
||||
}
|
||||
|
||||
const IBonusTypeHandler * LibClasses::getBth() const
|
||||
@ -63,7 +63,7 @@ void LibClasses::loadFilesystem()
|
||||
CResourceHandler::initialize();
|
||||
logGlobal->infoStream()<<"\t Initialization: "<<loadTime.getDiff();
|
||||
|
||||
CResourceHandler::loadFileSystem("", "ALL/config/filesystem.json");
|
||||
CResourceHandler::loadMainFileSystem("config/filesystem.json");
|
||||
logGlobal->infoStream()<<"\t Data loading: "<<loadTime.getDiff();
|
||||
|
||||
modh = new CModHandler;
|
||||
|
138
lib/filesystem/AdapterLoaders.cpp
Normal file
138
lib/filesystem/AdapterLoaders.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "StdInc.h"
|
||||
#include "AdapterLoaders.h"
|
||||
|
||||
#include "../JsonNode.h"
|
||||
|
||||
CMappedFileLoader::CMappedFileLoader(const std::string & mountPoint, const JsonNode &config)
|
||||
{
|
||||
for(auto entry : config.Struct())
|
||||
{
|
||||
fileList[ResourceID(mountPoint + entry.first)] = ResourceID(mountPoint + entry.second.String());
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CMappedFileLoader::load(const ResourceID & resourceName) const
|
||||
{
|
||||
return CResourceHandler::get()->load(fileList.at(resourceName));
|
||||
}
|
||||
|
||||
bool CMappedFileLoader::existsResource(const ResourceID & resourceName) const
|
||||
{
|
||||
return fileList.count(resourceName) != 0;
|
||||
}
|
||||
|
||||
std::string CMappedFileLoader::getMountPoint() const
|
||||
{
|
||||
return ""; // does not have any meaning with this type of data source
|
||||
}
|
||||
|
||||
boost::optional<std::string> CMappedFileLoader::getResourceName(const ResourceID & resourceName) const
|
||||
{
|
||||
return CResourceHandler::get()->getResourceName(fileList.at(resourceName));
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> CMappedFileLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
|
||||
{
|
||||
std::unordered_set<ResourceID> foundID;
|
||||
|
||||
for (auto & file : fileList)
|
||||
{
|
||||
if (filter(file.first))
|
||||
foundID.insert(file.first);
|
||||
}
|
||||
return foundID;
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CFilesystemList::load(const ResourceID & resourceName) const
|
||||
{
|
||||
// load resource from last loader that have it (last overriden version)
|
||||
for (auto & loader : boost::adaptors::reverse(loaders))
|
||||
{
|
||||
if (loader->existsResource(resourceName))
|
||||
return loader->load(resourceName);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Resource with name " + resourceName.getName() + " and type "
|
||||
+ EResTypeHelper::getEResTypeAsString(resourceName.getType()) + " wasn't found.");
|
||||
}
|
||||
|
||||
bool CFilesystemList::existsResource(const ResourceID & resourceName) const
|
||||
{
|
||||
return !getResourcesWithName(resourceName).empty();
|
||||
}
|
||||
|
||||
std::string CFilesystemList::getMountPoint() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
boost::optional<std::string> CFilesystemList::getResourceName(const ResourceID & resourceName) const
|
||||
{
|
||||
if (existsResource(resourceName))
|
||||
return getResourcesWithName(resourceName).back()->getResourceName(resourceName);
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> CFilesystemList::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
|
||||
{
|
||||
std::unordered_set<ResourceID> ret;
|
||||
|
||||
for (auto & loader : loaders)
|
||||
for (auto & entry : loader->getFilteredFiles(filter))
|
||||
ret.insert(entry);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CFilesystemList::createResource(std::string filename, bool update)
|
||||
{
|
||||
logGlobal->traceStream()<< "Creating " << filename;
|
||||
for (auto & loader : boost::adaptors::reverse(loaders))
|
||||
{
|
||||
if (writeableLoaders.count(loader.get()) != 0 // writeable,
|
||||
&& loader->createResource(filename, update)) // successfully created
|
||||
{
|
||||
// Check if resource was created successfully. Possible reasons for this to fail
|
||||
// a) loader failed to create resource (e.g. read-only FS)
|
||||
// b) in update mode, call with filename that does not exists
|
||||
assert(load(ResourceID(filename)));
|
||||
|
||||
logGlobal->traceStream()<< "Resource created successfully";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
logGlobal->traceStream()<< "Failed to create resource";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<const ISimpleResourceLoader *> CFilesystemList::getResourcesWithName(const ResourceID & resourceName) const
|
||||
{
|
||||
std::vector<const ISimpleResourceLoader *> ret;
|
||||
|
||||
for (auto & loader : loaders)
|
||||
boost::range::copy(loader->getResourcesWithName(resourceName), std::back_inserter(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CFilesystemList::addLoader(ISimpleResourceLoader * loader, bool writeable)
|
||||
{
|
||||
loaders.push_back(std::unique_ptr<ISimpleResourceLoader>(loader));
|
||||
if (writeable)
|
||||
writeableLoaders.insert(loader);
|
||||
}
|
||||
|
||||
ISimpleResourceLoader * CResourceHandler::get()
|
||||
{
|
||||
if(resourceLoader != nullptr)
|
||||
{
|
||||
return resourceLoader;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream string;
|
||||
string << "Error: Resource loader wasn't initialized. "
|
||||
<< "Make sure that you set one via FilesystemFactory::initialize";
|
||||
throw std::runtime_error(string.str());
|
||||
}
|
||||
}
|
79
lib/filesystem/AdapterLoaders.h
Normal file
79
lib/filesystem/AdapterLoaders.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* AdapterLoaders.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ISimpleResourceLoader.h"
|
||||
#include "Filesystem.h"
|
||||
|
||||
class CFileInfo;
|
||||
class CInputStream;
|
||||
|
||||
/**
|
||||
* Class that implements file mapping (aka *nix symbolic links)
|
||||
* Uses json file as input, content is map:
|
||||
* "fileA.txt" : "fileB.txt"
|
||||
* Note that extension is necessary, but used only to determine type
|
||||
*
|
||||
* fileA - file which will be replaced
|
||||
* fileB - file which will be used as replacement
|
||||
*/
|
||||
class DLL_LINKAGE CMappedFileLoader : public ISimpleResourceLoader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param config Specifies filesystem configuration
|
||||
*/
|
||||
explicit CMappedFileLoader(const std::string &mountPoint, const JsonNode & config);
|
||||
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
|
||||
bool existsResource(const ResourceID & resourceName) const override;
|
||||
std::string getMountPoint() const override;
|
||||
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
|
||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
|
||||
|
||||
private:
|
||||
/** A list of files in this map
|
||||
* key = ResourceID for resource loader
|
||||
* value = ResourceID to which file this request will be redirected
|
||||
*/
|
||||
std::unordered_map<ResourceID, ResourceID> fileList;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader
|
||||
{
|
||||
std::vector<std::unique_ptr<ISimpleResourceLoader> > loaders;
|
||||
|
||||
std::set<ISimpleResourceLoader *> writeableLoaders;
|
||||
|
||||
public:
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
|
||||
bool existsResource(const ResourceID & resourceName) const override;
|
||||
std::string getMountPoint() const override;
|
||||
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
|
||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
|
||||
bool createResource(std::string filename, bool update = false) override;
|
||||
std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const override;
|
||||
|
||||
/**
|
||||
* Adds a resource loader to the loaders list
|
||||
* Passes loader ownership to this object
|
||||
*
|
||||
* @param loader The simple resource loader object to add
|
||||
* @param writeable - resource shall be treated as writeable
|
||||
*/
|
||||
void addLoader(ISimpleResourceLoader * loader, bool writeable);
|
||||
};
|
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;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CLodArchiveLoader.h, part of VCMI engine
|
||||
* CArchiveLoader.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -9,9 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISimpleResourceLoader.h"
|
||||
#include "Filesystem.h"
|
||||
|
||||
class CFileInfo;
|
||||
class CFileInputStream;
|
||||
@ -33,16 +33,16 @@ struct ArchiveEntry
|
||||
int offset;
|
||||
|
||||
/** Size without compression in bytes **/
|
||||
int realSize;
|
||||
int fullSize;
|
||||
|
||||
/** Size with compression in bytes or 0 if not compressed **/
|
||||
int size;
|
||||
int compressedSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* A class which can scan and load files of a LOD archive.
|
||||
*/
|
||||
class DLL_LINKAGE CLodArchiveLoader : public ISimpleResourceLoader
|
||||
class DLL_LINKAGE CArchiveLoader : public ISimpleResourceLoader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@ -55,42 +55,42 @@ public:
|
||||
*
|
||||
* @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
|
||||
*/
|
||||
explicit CLodArchiveLoader(const std::string & archive);
|
||||
explicit CArchiveLoader(const std::string & mountPoint, const std::string & archive);
|
||||
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
|
||||
std::unordered_map<ResourceID, std::string> getEntries() const override;
|
||||
bool existsEntry(const std::string & resourceName) const override;
|
||||
std::string getOrigin() const override;
|
||||
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
|
||||
bool existsResource(const ResourceID & resourceName) const override;
|
||||
std::string getMountPoint() const override;
|
||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
|
||||
|
||||
private:
|
||||
const ArchiveEntry * getArchiveEntry(const std::string & resourceName) const;
|
||||
|
||||
/**
|
||||
* Initializes a LOD archive.
|
||||
*
|
||||
* @param fileStream File stream to the .lod archive
|
||||
*/
|
||||
void initLODArchive(CFileInputStream & fileStream);
|
||||
void initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||
|
||||
/**
|
||||
* Initializes a VID archive.
|
||||
*
|
||||
* @param fileStream File stream to the .vid archive
|
||||
*/
|
||||
void initVIDArchive(CFileInputStream & fileStream);
|
||||
void initVIDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||
|
||||
/**
|
||||
* Initializes a SND archive.
|
||||
*
|
||||
* @param fileStream File stream to the .snd archive
|
||||
*/
|
||||
void initSNDArchive(CFileInputStream & fileStream);
|
||||
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||
|
||||
/** The file path to the archive which is scanned and indexed. */
|
||||
std::string archive;
|
||||
|
||||
std::string mountPoint;
|
||||
|
||||
/** Holds all entries of the archive file. An entry can be accessed via the entry name. **/
|
||||
std::unordered_map<std::string, ArchiveEntry> entries;
|
||||
std::unordered_map<ResourceID, ArchiveEntry> entries;
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "CBinaryReader.h"
|
||||
|
||||
#include <SDL_endian.h>
|
||||
#include "CInputStream.h"
|
||||
|
||||
@ -12,9 +13,7 @@ CData readLE(CData data)
|
||||
std::reverse(dataPtr, dataPtr + sizeof(data));
|
||||
return data;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template <typename CData>
|
||||
CData readLE(CData data)
|
||||
{
|
||||
@ -24,12 +23,10 @@ CData readLE(CData data)
|
||||
|
||||
CBinaryReader::CBinaryReader() : stream(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CBinaryReader::CBinaryReader(CInputStream * stream) : stream(stream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CInputStream * CBinaryReader::getStream()
|
||||
@ -97,7 +94,6 @@ void CBinaryReader::skip(int count)
|
||||
stream->skip(count);
|
||||
}
|
||||
|
||||
|
||||
std::string CBinaryReader::getEndOfStreamExceptionMsg(long bytesToRead) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CBinaryReader.h, part of VCMI engine
|
||||
@ -9,8 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class CInputStream;
|
||||
|
||||
/**
|
||||
|
@ -81,8 +81,6 @@ si64 CCompressedStream::getSize()
|
||||
|
||||
void CCompressedStream::decompressTill(si64 newSize)
|
||||
{
|
||||
assert(newSize < 100 * 1024 * 1024); //just in case
|
||||
|
||||
if (inflateState == nullptr)
|
||||
return; //file already decompressed
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CLodStream.h, part of VCMI engine
|
||||
@ -9,8 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
|
||||
struct z_stream_s;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CFileInfo.h, part of VCMI engine
|
||||
@ -9,9 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CResourceLoader.h"
|
||||
#include "Filesystem.h"
|
||||
|
||||
/**
|
||||
* A class which holds information about a file.
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CFileInputStream.h"
|
||||
#include "CFileInfo.h"
|
||||
|
||||
#include "CFileInfo.h"
|
||||
|
||||
CFileInputStream::CFileInputStream(const std::string & file, si64 start, si64 size)
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CFileInputStream.h, part of VCMI engine
|
||||
@ -9,8 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
|
||||
class CFileInfo;
|
||||
|
@ -4,55 +4,75 @@
|
||||
#include "CFileInfo.h"
|
||||
#include "CFileInputStream.h"
|
||||
|
||||
CFilesystemLoader::CFilesystemLoader(const std::string & baseDirectory, size_t depth, bool initial):
|
||||
CFilesystemLoader::CFilesystemLoader(const std::string &mountPoint, const std::string & baseDirectory, size_t depth, bool initial):
|
||||
baseDirectory(baseDirectory),
|
||||
fileList(listFiles(depth, initial))
|
||||
mountPoint(mountPoint),
|
||||
fileList(listFiles(mountPoint, depth, initial))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CFilesystemLoader::load(const std::string & resourceName) const
|
||||
std::unique_ptr<CInputStream> CFilesystemLoader::load(const ResourceID & resourceName) const
|
||||
{
|
||||
std::unique_ptr<CInputStream> stream(new CFileInputStream(getOrigin() + '/' + resourceName));
|
||||
assert(fileList.count(resourceName));
|
||||
|
||||
std::unique_ptr<CInputStream> stream(new CFileInputStream(baseDirectory + '/' + fileList.at(resourceName)));
|
||||
return stream;
|
||||
}
|
||||
|
||||
bool CFilesystemLoader::existsEntry(const std::string & resourceName) const
|
||||
bool CFilesystemLoader::existsResource(const ResourceID & resourceName) const
|
||||
{
|
||||
for(auto & elem : fileList)
|
||||
return fileList.count(resourceName);
|
||||
}
|
||||
|
||||
std::string CFilesystemLoader::getMountPoint() const
|
||||
{
|
||||
return mountPoint;
|
||||
}
|
||||
|
||||
boost::optional<std::string> CFilesystemLoader::getResourceName(const ResourceID & resourceName) const
|
||||
{
|
||||
assert(existsResource(resourceName));
|
||||
|
||||
return baseDirectory + '/' + fileList.at(resourceName);
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> CFilesystemLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
|
||||
{
|
||||
std::unordered_set<ResourceID> foundID;
|
||||
|
||||
for (auto & file : fileList)
|
||||
{
|
||||
if(elem.second == resourceName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (filter(file.first))
|
||||
foundID.insert(file.first);
|
||||
} return foundID;
|
||||
}
|
||||
|
||||
bool CFilesystemLoader::createResource(std::string filename, bool update)
|
||||
{
|
||||
ResourceID resID(filename);
|
||||
|
||||
if (fileList.find(resID) != fileList.end())
|
||||
return true;
|
||||
|
||||
if (!boost::iequals(mountPoint, filename.substr(0, mountPoint.size())))
|
||||
{
|
||||
logGlobal->traceStream() << "Can't create file: wrong mount point: " << mountPoint;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
filename = filename.substr(mountPoint.size());
|
||||
|
||||
std::unordered_map<ResourceID, std::string> CFilesystemLoader::getEntries() const
|
||||
{
|
||||
return fileList;
|
||||
}
|
||||
|
||||
std::string CFilesystemLoader::getOrigin() const
|
||||
{
|
||||
return baseDirectory;
|
||||
}
|
||||
|
||||
bool CFilesystemLoader::createEntry(std::string filename, bool update)
|
||||
{
|
||||
ResourceID res(filename);
|
||||
if (fileList.find(res) != fileList.end())
|
||||
return false;
|
||||
|
||||
fileList[res] = filename;
|
||||
if (!update)
|
||||
{
|
||||
std::ofstream newfile (baseDirectory + "/" + filename);
|
||||
if (!newfile.good())
|
||||
return false;
|
||||
}
|
||||
fileList[resID] = filename;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::unordered_map<ResourceID, std::string> CFilesystemLoader::listFiles(size_t depth, bool initial) const
|
||||
std::unordered_map<ResourceID, std::string> CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const
|
||||
{
|
||||
std::set<EResType::Type> initialTypes;
|
||||
initialTypes.insert(EResType::DIRECTORY);
|
||||
@ -77,7 +97,8 @@ std::unordered_map<ResourceID, std::string> CFilesystemLoader::listFiles(size_t
|
||||
{
|
||||
path.resize(it.level()+1);
|
||||
path.back() = it->path().leaf().string();
|
||||
it.no_push(depth <= it.level()); // don't iterate into directory if depth limit reached
|
||||
// don't iterate into directory if depth limit reached OR into hidden directories (like .svn)
|
||||
it.no_push(depth <= it.level() || it->path().leaf().string()[0] == '.');
|
||||
|
||||
type = EResType::DIRECTORY;
|
||||
}
|
||||
@ -92,7 +113,7 @@ std::unordered_map<ResourceID, std::string> CFilesystemLoader::listFiles(size_t
|
||||
filename += path[i] + '/';
|
||||
filename += it->path().leaf().string();
|
||||
|
||||
fileList[ResourceID(filename, type)] = filename;
|
||||
fileList[ResourceID(mountPoint + filename, type)] = filename;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CFilesystemLoader.h, part of VCMI engine
|
||||
@ -9,10 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISimpleResourceLoader.h"
|
||||
#include "CResourceLoader.h"
|
||||
#include "Filesystem.h"
|
||||
|
||||
class CFileInfo;
|
||||
class CInputStream;
|
||||
@ -31,20 +30,23 @@ public:
|
||||
*
|
||||
* @throws std::runtime_error if the base directory is not a directory or if it is not available
|
||||
*/
|
||||
explicit CFilesystemLoader(const std::string & baseDirectory, size_t depth = 16, bool initial = false);
|
||||
explicit CFilesystemLoader(const std::string & mountPoint, const std::string & baseDirectory, size_t depth = 16, bool initial = false);
|
||||
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
|
||||
bool existsEntry(const std::string & resourceName) const override;
|
||||
std::unordered_map<ResourceID, std::string> getEntries() const override;
|
||||
std::string getOrigin() const override;
|
||||
bool createEntry(std::string filename, bool update) override;
|
||||
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
|
||||
bool existsResource(const ResourceID & resourceName) const override;
|
||||
std::string getMountPoint() const override;
|
||||
bool createResource(std::string filename, bool update = false) override;
|
||||
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
|
||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
|
||||
|
||||
private:
|
||||
/** The base directory which is scanned and indexed. */
|
||||
std::string baseDirectory;
|
||||
|
||||
std::string mountPoint;
|
||||
|
||||
/** A list of files in the directory
|
||||
* key = ResourceID for resource loader
|
||||
* value = name that can be used to access file
|
||||
@ -60,5 +62,5 @@ private:
|
||||
* @return a list of pathnames denoting the files and directories in the directory denoted by this pathname
|
||||
* The array will be empty if the directory is empty. Ptr is null if the directory doesn't exist or if it isn't a directory.
|
||||
*/
|
||||
std::unordered_map<ResourceID, std::string> listFiles(size_t depth, bool initial) const;
|
||||
std::unordered_map<ResourceID, std::string> listFiles(const std::string &mountPoint, size_t depth, bool initial) const;
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CInputStream.h, part of VCMI engine
|
||||
@ -9,8 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Abstract class which provides method definitions for reading from a stream.
|
||||
*/
|
||||
@ -62,7 +61,17 @@ public:
|
||||
virtual si64 getSize() = 0;
|
||||
|
||||
/**
|
||||
* Closes the stream and releases any system resources associated with the stream explicitely.
|
||||
* @brief for convenience, reads whole stream at once
|
||||
*
|
||||
* @return pair, first = raw data, second = size of data
|
||||
*/
|
||||
//virtual void close() = 0;
|
||||
std::pair<std::unique_ptr<ui8[]>, size_t> readAll()
|
||||
{
|
||||
std::unique_ptr<ui8[]> data(new ui8[getSize()]);
|
||||
|
||||
size_t readSize = read(data.get(), getSize());
|
||||
assert(readSize == getSize());
|
||||
|
||||
return std::make_pair(std::move(data), getSize());
|
||||
}
|
||||
};
|
||||
|
@ -1,220 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
// 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.realSize = reader.readUInt32();
|
||||
fileStream.skip(4); // unused, unknown
|
||||
entry.size = reader.readUInt32();
|
||||
|
||||
// Add lod entry to local entries map
|
||||
entries[entry.name] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
auto 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);
|
||||
auto 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));
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<ResourceID, std::string> CLodArchiveLoader::getEntries() const
|
||||
{
|
||||
std::unordered_map<ResourceID, std::string> retList;
|
||||
|
||||
for(auto & elem : entries)
|
||||
{
|
||||
const ArchiveEntry & entry = elem.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;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMappedFileLoader.h"
|
||||
#include "CResourceLoader.h"
|
||||
#include "../JsonNode.h"
|
||||
|
||||
CMappedFileLoader::CMappedFileLoader(const JsonNode &config)
|
||||
{
|
||||
for(auto entry : config.Struct())
|
||||
{
|
||||
fileList[ResourceID(entry.first)] = entry.second.String();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CMappedFileLoader::load(const std::string & resourceName) const
|
||||
{
|
||||
return CResourceHandler::get()->load(ResourceID(resourceName));
|
||||
}
|
||||
|
||||
bool CMappedFileLoader::existsEntry(const std::string & resourceName) const
|
||||
{
|
||||
for(auto & elem : fileList)
|
||||
{
|
||||
if(elem.second == resourceName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_map<ResourceID, std::string> CMappedFileLoader::getEntries() const
|
||||
{
|
||||
return fileList;
|
||||
}
|
||||
|
||||
std::string CMappedFileLoader::getOrigin() const
|
||||
{
|
||||
return ""; // does not have any meaning with this type of data source
|
||||
}
|
||||
|
||||
std::string CMappedFileLoader::getFullName(const std::string & resourceName) const
|
||||
{
|
||||
return CResourceHandler::get()->getResourceName(ResourceID(resourceName));
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
|
||||
/*
|
||||
* CMappedFileLoader.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISimpleResourceLoader.h"
|
||||
#include "CResourceLoader.h"
|
||||
|
||||
class CFileInfo;
|
||||
class CInputStream;
|
||||
|
||||
/**
|
||||
* Class that implements file mapping (aka *nix symbolic links)
|
||||
* Uses json file as input, content is map:
|
||||
* "fileA.txt" : "fileB.txt"
|
||||
* Note that extension is necessary, but used only to determine type
|
||||
*
|
||||
* fileA - file which will be replaced
|
||||
* fileB - file which will be used as replacement
|
||||
*/
|
||||
class DLL_LINKAGE CMappedFileLoader : public ISimpleResourceLoader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param config Specifies filesystem configuration
|
||||
*/
|
||||
explicit CMappedFileLoader(const JsonNode & config);
|
||||
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
|
||||
bool existsEntry(const std::string & resourceName) const override;
|
||||
std::unordered_map<ResourceID, std::string> getEntries() const override;
|
||||
std::string getOrigin() const override;
|
||||
std::string getFullName(const std::string & resourceName) const override;
|
||||
|
||||
private:
|
||||
/** A list of files in this map
|
||||
* key = ResourceID for resource loader
|
||||
* value = ResourceID to which file this request will be redirected
|
||||
*/
|
||||
std::unordered_map<ResourceID, std::string> fileList;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* CMemoryStream.h, part of VCMI engine
|
||||
@ -9,8 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
|
||||
/**
|
||||
|
@ -1,491 +0,0 @@
|
||||
#include "StdInc.h"
|
||||
#include "CResourceLoader.h"
|
||||
#include "CFileInfo.h"
|
||||
#include "CLodArchiveLoader.h"
|
||||
#include "CFilesystemLoader.h"
|
||||
#include "CMappedFileLoader.h"
|
||||
|
||||
//For filesystem initialization
|
||||
#include "../JsonNode.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "../VCMIDirs.h"
|
||||
#include "../CStopWatch.h"
|
||||
|
||||
CResourceLoader * CResourceHandler::resourceLoader = nullptr;
|
||||
CResourceLoader * CResourceHandler::initialLoader = nullptr;
|
||||
|
||||
ResourceID::ResourceID()
|
||||
:type(EResType::OTHER)
|
||||
{
|
||||
}
|
||||
|
||||
ResourceID::ResourceID(std::string name)
|
||||
{
|
||||
CFileInfo info(std::move(name));
|
||||
setName(info.getStem());
|
||||
setType(info.getType());
|
||||
}
|
||||
|
||||
ResourceID::ResourceID(std::string name, EResType::Type type)
|
||||
{
|
||||
setName(std::move(name));
|
||||
setType(type);
|
||||
}
|
||||
|
||||
ResourceID::ResourceID(const std::string & prefix, const std::string & name, EResType::Type type)
|
||||
{
|
||||
this->name = name;
|
||||
|
||||
size_t dotPos = this->name.find_last_of("/.");
|
||||
|
||||
if(dotPos != std::string::npos && this->name[dotPos] == '.')
|
||||
this->name.erase(dotPos);
|
||||
|
||||
this->name = prefix + this->name;
|
||||
setType(type);
|
||||
}
|
||||
|
||||
std::string ResourceID::getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
EResType::Type ResourceID::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
void ResourceID::setName(std::string name)
|
||||
{
|
||||
this->name = std::move(name);
|
||||
|
||||
size_t dotPos = this->name.find_last_of("/.");
|
||||
|
||||
if(dotPos != std::string::npos && this->name[dotPos] == '.')
|
||||
this->name.erase(dotPos);
|
||||
|
||||
// strangely enough but this line takes 40-50% of filesystem loading time
|
||||
boost::to_upper(this->name);
|
||||
}
|
||||
|
||||
void ResourceID::setType(EResType::Type type)
|
||||
{
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
CResourceLoader::CResourceLoader()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CResourceLoader::load(const ResourceID & resourceIdent) const
|
||||
{
|
||||
auto resource = resources.find(resourceIdent);
|
||||
|
||||
if(resource == resources.end())
|
||||
{
|
||||
throw std::runtime_error("Resource with name " + resourceIdent.getName() + " and type "
|
||||
+ EResTypeHelper::getEResTypeAsString(resourceIdent.getType()) + " wasn't found.");
|
||||
}
|
||||
|
||||
// get the last added resource(most overriden)
|
||||
const ResourceLocator & locator = resource->second.back();
|
||||
|
||||
// load the resource and return it
|
||||
return locator.getLoader()->load(locator.getResourceName());
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<ui8[]>, ui64> CResourceLoader::loadData(const ResourceID & resourceIdent) const
|
||||
{
|
||||
auto stream = load(resourceIdent);
|
||||
std::unique_ptr<ui8[]> data(new ui8[stream->getSize()]);
|
||||
size_t readSize = stream->read(data.get(), stream->getSize());
|
||||
|
||||
assert(readSize == stream->getSize());
|
||||
return std::make_pair(std::move(data), stream->getSize());
|
||||
}
|
||||
|
||||
ResourceLocator CResourceLoader::getResource(const ResourceID & resourceIdent) const
|
||||
{
|
||||
auto resource = resources.find(resourceIdent);
|
||||
|
||||
if (resource == resources.end())
|
||||
return ResourceLocator(nullptr, "");
|
||||
return resource->second.back();
|
||||
}
|
||||
|
||||
const std::vector<ResourceLocator> & CResourceLoader::getResourcesWithName(const ResourceID & resourceIdent) const
|
||||
{
|
||||
static const std::vector<ResourceLocator> emptyList;
|
||||
auto resource = resources.find(resourceIdent);
|
||||
|
||||
if (resource == resources.end())
|
||||
return emptyList;
|
||||
return resource->second;
|
||||
}
|
||||
|
||||
|
||||
std::string CResourceLoader::getResourceName(const ResourceID & resourceIdent) const
|
||||
{
|
||||
auto locator = getResource(resourceIdent);
|
||||
if (locator.getLoader())
|
||||
return locator.getLoader()->getFullName(locator.getResourceName());
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CResourceLoader::existsResource(const ResourceID & resourceIdent) const
|
||||
{
|
||||
return resources.find(resourceIdent) != resources.end();
|
||||
}
|
||||
|
||||
bool CResourceLoader::createResource(std::string URI, bool update)
|
||||
{
|
||||
std::string filename = URI;
|
||||
boost::to_upper(URI);
|
||||
for (auto & entry : boost::adaptors::reverse(loaders))
|
||||
{
|
||||
if (entry.writeable && boost::algorithm::starts_with(URI, entry.prefix))
|
||||
{
|
||||
// remove loader prefix from filename
|
||||
filename = filename.substr(entry.prefix.size());
|
||||
if (!entry.loader->createEntry(filename))
|
||||
continue;
|
||||
|
||||
resources[ResourceID(URI)].push_back(ResourceLocator(entry.loader.get(), filename));
|
||||
|
||||
// Check if resource was created successfully. Possible reasons for this to fail
|
||||
// a) loader failed to create resource (e.g. read-only FS)
|
||||
// b) in update mode, call with filename that does not exists
|
||||
assert(load(ResourceID(URI)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CResourceLoader::addLoader(std::string mountPoint, shared_ptr<ISimpleResourceLoader> loader, bool writeable)
|
||||
{
|
||||
LoaderEntry loaderEntry;
|
||||
loaderEntry.loader = loader;
|
||||
loaderEntry.prefix = mountPoint;
|
||||
loaderEntry.writeable = writeable;
|
||||
loaders.push_back(loaderEntry);
|
||||
|
||||
// Get entries and add them to the resources list
|
||||
const std::unordered_map<ResourceID, std::string> & entries = loader->getEntries();
|
||||
|
||||
boost::to_upper(mountPoint);
|
||||
|
||||
for (auto & entry : entries)
|
||||
{
|
||||
// Create identifier and locator and add them to the resources list
|
||||
ResourceID ident(mountPoint, entry.first.getName(), entry.first.getType());
|
||||
ResourceLocator locator(loader.get(), entry.second);
|
||||
|
||||
resources[ident].push_back(locator);
|
||||
}
|
||||
}
|
||||
|
||||
CResourceLoader * CResourceHandler::get()
|
||||
{
|
||||
if(resourceLoader != nullptr)
|
||||
{
|
||||
return resourceLoader;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream string;
|
||||
string << "Error: Resource loader wasn't initialized. "
|
||||
<< "Make sure that you set one via CResourceLoaderFactory::initialize";
|
||||
throw std::runtime_error(string.str());
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceHandler::clear()
|
||||
{
|
||||
delete resourceLoader;
|
||||
delete initialLoader;
|
||||
}
|
||||
|
||||
//void CResourceLoaderFactory::setInstance(CResourceLoader * resourceLoader)
|
||||
//{
|
||||
// CResourceLoaderFactory::resourceLoader = resourceLoader;
|
||||
//}
|
||||
|
||||
ResourceLocator::ResourceLocator(ISimpleResourceLoader * loader, const std::string & resourceName)
|
||||
: loader(loader), resourceName(resourceName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ISimpleResourceLoader * ResourceLocator::getLoader() const
|
||||
{
|
||||
return loader;
|
||||
}
|
||||
|
||||
std::string ResourceLocator::getResourceName() const
|
||||
{
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
|
||||
{
|
||||
boost::to_upper(extension);
|
||||
|
||||
static const std::map<std::string, EResType::Type> stringToRes =
|
||||
boost::assign::map_list_of
|
||||
(".TXT", EResType::TEXT)
|
||||
(".JSON", EResType::TEXT)
|
||||
(".DEF", EResType::ANIMATION)
|
||||
(".MSK", EResType::MASK)
|
||||
(".MSG", EResType::MASK)
|
||||
(".H3C", EResType::CAMPAIGN)
|
||||
(".H3M", EResType::MAP)
|
||||
(".FNT", EResType::BMP_FONT)
|
||||
(".TTF", EResType::TTF_FONT)
|
||||
(".BMP", EResType::IMAGE)
|
||||
(".JPG", EResType::IMAGE)
|
||||
(".PCX", EResType::IMAGE)
|
||||
(".PNG", EResType::IMAGE)
|
||||
(".TGA", EResType::IMAGE)
|
||||
(".WAV", EResType::SOUND)
|
||||
(".82M", EResType::SOUND)
|
||||
(".SMK", EResType::VIDEO)
|
||||
(".BIK", EResType::VIDEO)
|
||||
(".MJPG", EResType::VIDEO)
|
||||
(".MPG", EResType::VIDEO)
|
||||
(".AVI", EResType::VIDEO)
|
||||
(".MP3", EResType::MUSIC)
|
||||
(".OGG", EResType::MUSIC)
|
||||
(".LOD", EResType::ARCHIVE_LOD)
|
||||
(".PAC", EResType::ARCHIVE_LOD)
|
||||
(".VID", EResType::ARCHIVE_VID)
|
||||
(".SND", EResType::ARCHIVE_SND)
|
||||
(".PAL", EResType::PALETTE)
|
||||
(".VCGM1", EResType::CLIENT_SAVEGAME)
|
||||
(".VSGM1", EResType::SERVER_SAVEGAME)
|
||||
(".ERM", EResType::ERM)
|
||||
(".ERT", EResType::ERT)
|
||||
(".ERS", EResType::ERS);
|
||||
|
||||
auto iter = stringToRes.find(extension);
|
||||
if (iter == stringToRes.end())
|
||||
return EResType::OTHER;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
|
||||
{
|
||||
#define MAP_ENUM(value) (EResType::value, #value)
|
||||
|
||||
static const std::map<EResType::Type, std::string> stringToRes = boost::assign::map_list_of
|
||||
MAP_ENUM(TEXT)
|
||||
MAP_ENUM(ANIMATION)
|
||||
MAP_ENUM(MASK)
|
||||
MAP_ENUM(CAMPAIGN)
|
||||
MAP_ENUM(MAP)
|
||||
MAP_ENUM(BMP_FONT)
|
||||
MAP_ENUM(TTF_FONT)
|
||||
MAP_ENUM(IMAGE)
|
||||
MAP_ENUM(VIDEO)
|
||||
MAP_ENUM(SOUND)
|
||||
MAP_ENUM(MUSIC)
|
||||
MAP_ENUM(ARCHIVE_LOD)
|
||||
MAP_ENUM(ARCHIVE_SND)
|
||||
MAP_ENUM(ARCHIVE_VID)
|
||||
MAP_ENUM(PALETTE)
|
||||
MAP_ENUM(CLIENT_SAVEGAME)
|
||||
MAP_ENUM(SERVER_SAVEGAME)
|
||||
MAP_ENUM(DIRECTORY)
|
||||
MAP_ENUM(ERM)
|
||||
MAP_ENUM(ERT)
|
||||
MAP_ENUM(ERS)
|
||||
MAP_ENUM(OTHER);
|
||||
|
||||
#undef MAP_ENUM
|
||||
|
||||
auto iter = stringToRes.find(type);
|
||||
assert(iter != stringToRes.end());
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void CResourceHandler::initialize()
|
||||
{
|
||||
//recurse only into specific directories
|
||||
auto recurseInDir = [](std::string URI, int depth)
|
||||
{
|
||||
auto resources = initialLoader->getResourcesWithName(ResourceID(URI, EResType::DIRECTORY));
|
||||
for(const ResourceLocator & entry : resources)
|
||||
{
|
||||
std::string filename = entry.getLoader()->getOrigin() + '/' + entry.getResourceName();
|
||||
if (!filename.empty())
|
||||
{
|
||||
shared_ptr<ISimpleResourceLoader> dir(new CFilesystemLoader(filename, depth, true));
|
||||
initialLoader->addLoader(URI + '/', dir, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//temporary filesystem that will be used to initialize main one.
|
||||
//used to solve several case-sensivity issues like Mp3 vs MP3
|
||||
initialLoader = new CResourceLoader;
|
||||
resourceLoader = new CResourceLoader;
|
||||
|
||||
for (auto path : VCMIDirs::get().dataPaths())
|
||||
{
|
||||
shared_ptr<ISimpleResourceLoader> loader(new CFilesystemLoader(path, 0, true));
|
||||
|
||||
initialLoader->addLoader("GLOBAL/", loader, false);
|
||||
initialLoader->addLoader("ALL/", loader, false);
|
||||
}
|
||||
|
||||
{
|
||||
shared_ptr<ISimpleResourceLoader> loader(new CFilesystemLoader(VCMIDirs::get().userDataPath(), 0, true));
|
||||
|
||||
initialLoader->addLoader("LOCAL/", loader, false);
|
||||
|
||||
if (!vstd::contains(VCMIDirs::get().dataPaths(), VCMIDirs::get().userDataPath()))
|
||||
initialLoader->addLoader("ALL/", loader, false);
|
||||
}
|
||||
|
||||
recurseInDir("ALL/CONFIG", 0);// look for configs
|
||||
recurseInDir("ALL/DATA", 0); // look for archives
|
||||
recurseInDir("ALL/MODS", 2); // look for mods. Depth 2 is required for now but won't cause spped issues if no mods present
|
||||
}
|
||||
|
||||
void CResourceHandler::loadDirectory(const std::string &prefix, const std::string &mountPoint, const JsonNode & config)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
bool writeable = config["writeable"].Bool();
|
||||
int depth = 16;
|
||||
if (!config["depth"].isNull())
|
||||
depth = config["depth"].Float();
|
||||
|
||||
auto resources = initialLoader->getResourcesWithName(ResourceID(URI, EResType::DIRECTORY));
|
||||
|
||||
for(const ResourceLocator & entry : resources)
|
||||
{
|
||||
std::string filename = entry.getLoader()->getOrigin() + '/' + entry.getResourceName();
|
||||
resourceLoader->addLoader(mountPoint,
|
||||
shared_ptr<ISimpleResourceLoader>(new CFilesystemLoader(filename, depth)), writeable);
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceHandler::loadArchive(const std::string &prefix, const std::string &mountPoint, const JsonNode & config, EResType::Type archiveType)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
std::string filename = initialLoader->getResourceName(ResourceID(URI, archiveType));
|
||||
if (!filename.empty())
|
||||
resourceLoader->addLoader(mountPoint,
|
||||
shared_ptr<ISimpleResourceLoader>(new CLodArchiveLoader(filename)), false);
|
||||
}
|
||||
|
||||
void CResourceHandler::loadJsonMap(const std::string &prefix, const std::string &mountPoint, const JsonNode & config)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
std::string filename = initialLoader->getResourceName(ResourceID(URI, EResType::TEXT));
|
||||
if (!filename.empty())
|
||||
{
|
||||
auto configData = initialLoader->loadData(ResourceID(URI, EResType::TEXT));
|
||||
|
||||
const JsonNode config((char*)configData.first.get(), configData.second);
|
||||
|
||||
resourceLoader->addLoader(mountPoint,
|
||||
shared_ptr<ISimpleResourceLoader>(new CMappedFileLoader(config)), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CResourceHandler::loadFileSystem(const std::string & prefix, const std::string &fsConfigURI)
|
||||
{
|
||||
auto fsConfigData = initialLoader->loadData(ResourceID(fsConfigURI, EResType::TEXT));
|
||||
|
||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||
|
||||
loadFileSystem(prefix, fsConfig["filesystem"]);
|
||||
}
|
||||
|
||||
void CResourceHandler::loadFileSystem(const std::string & prefix, const JsonNode &fsConfig)
|
||||
{
|
||||
for(auto & mountPoint : fsConfig.Struct())
|
||||
{
|
||||
for(auto & entry : mountPoint.second.Vector())
|
||||
{
|
||||
CStopWatch timer;
|
||||
logGlobal->debugStream() << "\t\tLoading resource at " << prefix + entry["path"].String();
|
||||
|
||||
if (entry["type"].String() == "map")
|
||||
loadJsonMap(prefix, mountPoint.first, entry);
|
||||
if (entry["type"].String() == "dir")
|
||||
loadDirectory(prefix, mountPoint.first, entry);
|
||||
if (entry["type"].String() == "lod")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_LOD);
|
||||
if (entry["type"].String() == "snd")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_SND);
|
||||
if (entry["type"].String() == "vid")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_VID);
|
||||
|
||||
logGlobal->debugStream() << "Resource loaded in " << timer.getDiff() << " ms.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> CResourceHandler::getAvailableMods()
|
||||
{
|
||||
auto iterator = initialLoader->getIterator([](const ResourceID & ident) -> bool
|
||||
{
|
||||
std::string name = ident.getName();
|
||||
|
||||
return ident.getType() == EResType::DIRECTORY
|
||||
&& std::count(name.begin(), name.end(), '/') == 2
|
||||
&& boost::algorithm::starts_with(name, "ALL/MODS/");
|
||||
});
|
||||
|
||||
//storage for found mods
|
||||
std::vector<std::string> foundMods;
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
std::string name = iterator->getName();
|
||||
|
||||
name.erase(0, name.find_last_of('/') + 1); //Remove path prefix
|
||||
|
||||
if (name == "WOG") // check if wog is actually present. Hack-ish but better than crash
|
||||
{
|
||||
if (!initialLoader->existsResource(ResourceID("ALL/DATA/ZVS", EResType::DIRECTORY)) &&
|
||||
!initialLoader->existsResource(ResourceID("ALL/MODS/WOG/DATA/ZVS", EResType::DIRECTORY)))
|
||||
{
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.empty()) // this is also triggered for "ALL/MODS/" entry
|
||||
foundMods.push_back(name);
|
||||
|
||||
++iterator;
|
||||
}
|
||||
return foundMods;
|
||||
}
|
||||
|
||||
void CResourceHandler::setActiveMods(std::vector<std::string> enabledMods)
|
||||
{
|
||||
// default FS config for mods: directory "Content" that acts as H3 root directory
|
||||
JsonNode defaultFS;
|
||||
|
||||
defaultFS[""].Vector().resize(1);
|
||||
defaultFS[""].Vector()[0]["type"].String() = "dir";
|
||||
defaultFS[""].Vector()[0]["path"].String() = "/Content";
|
||||
|
||||
for(std::string & modName : enabledMods)
|
||||
{
|
||||
ResourceID modConfFile("all/mods/" + modName + "/mod", EResType::TEXT);
|
||||
auto fsConfigData = initialLoader->loadData(modConfFile);
|
||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||
|
||||
if (!fsConfig["filesystem"].isNull())
|
||||
loadFileSystem("all/mods/" + modName, fsConfig["filesystem"]);
|
||||
else
|
||||
loadFileSystem("all/mods/" + modName, defaultFS);
|
||||
}
|
||||
}
|
@ -1,446 +0,0 @@
|
||||
|
||||
/*
|
||||
* CResourceLoader.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
|
||||
class CResourceLoader;
|
||||
class ResourceLocator;
|
||||
class ISimpleResourceLoader;
|
||||
class JsonNode;
|
||||
|
||||
/**
|
||||
* Specifies the resource type.
|
||||
*
|
||||
* Supported file extensions:
|
||||
*
|
||||
* Text: .txt .json
|
||||
* Animation: .def
|
||||
* Mask: .msk .msg
|
||||
* Campaign: .h3c
|
||||
* Map: .h3m
|
||||
* Font: .fnt
|
||||
* Image: .bmp, .jpg, .pcx, .png, .tga
|
||||
* Sound: .wav .82m
|
||||
* Video: .smk, .bik .mjpg .mpg
|
||||
* Music: .mp3, .ogg
|
||||
* Archive: .lod, .snd, .vid .pac
|
||||
* Palette: .pal
|
||||
* Savegame: .v*gm1
|
||||
*/
|
||||
namespace EResType
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
TEXT,
|
||||
ANIMATION,
|
||||
MASK,
|
||||
CAMPAIGN,
|
||||
MAP,
|
||||
BMP_FONT,
|
||||
TTF_FONT,
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
SOUND,
|
||||
MUSIC,
|
||||
ARCHIVE_VID,
|
||||
ARCHIVE_SND,
|
||||
ARCHIVE_LOD,
|
||||
PALETTE,
|
||||
CLIENT_SAVEGAME,
|
||||
SERVER_SAVEGAME,
|
||||
DIRECTORY,
|
||||
ERM,
|
||||
ERT,
|
||||
ERS,
|
||||
OTHER
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A struct which identifies a resource clearly.
|
||||
*/
|
||||
class DLL_LINKAGE ResourceID
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default c-tor.
|
||||
*/
|
||||
ResourceID();
|
||||
|
||||
/**
|
||||
* Move Ctor.
|
||||
*/
|
||||
ResourceID(ResourceID && other)
|
||||
: name(std::move(other.name)), type(other.getType())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy Ctor. Required by clang (or this is standard?) if move constructor is present
|
||||
*/
|
||||
ResourceID(const ResourceID & other)
|
||||
: name(other.getName()), type(other.getType())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctor. Can be used to create indentifier for resource loading using one parameter
|
||||
*
|
||||
* @param name The resource name including extension.
|
||||
*/
|
||||
explicit ResourceID(std::string fullName);
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param name The resource name.
|
||||
* @param type The resource type. A constant from the enumeration EResType.
|
||||
*/
|
||||
ResourceID(std::string name, EResType::Type type);
|
||||
|
||||
/**
|
||||
* Compares this object with a another resource identifier.
|
||||
*
|
||||
* @param other The other resource identifier.
|
||||
* @return Returns true if both are equally, false if not.
|
||||
*/
|
||||
inline bool operator==(ResourceID const & other) const
|
||||
{
|
||||
return name == other.name && type == other.type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move-assignment operator.
|
||||
*/
|
||||
inline ResourceID& operator=(ResourceID && other)
|
||||
{
|
||||
name = std::move(other.name);
|
||||
type = other.getType();
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string getName() const;
|
||||
EResType::Type getType() const;
|
||||
void setName(std::string name);
|
||||
void setType(EResType::Type type);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Ctor for usage strictly in resourceLoader for some speedup
|
||||
*
|
||||
* @param prefix Prefix of ths filename, already in upper case
|
||||
* @param name The resource name, upper case
|
||||
* @param type The resource type. A constant from the enumeration EResType.
|
||||
*/
|
||||
ResourceID(const std::string & prefix, const std::string & name, EResType::Type type);
|
||||
|
||||
friend class CResourceLoader;
|
||||
private:
|
||||
/** Specifies the resource name. No extension so .pcx and .png can override each other, always in upper case. **/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Specifies the resource type. EResType::OTHER if not initialized.
|
||||
* Required to prevent conflicts if files with different types (e.g. text and image) have the same name.
|
||||
*/
|
||||
EResType::Type type;
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <> struct hash<ResourceID>
|
||||
{
|
||||
size_t operator()(const ResourceID & resourceIdent) const
|
||||
{
|
||||
std::hash<int> intHasher;
|
||||
std::hash<std::string> stringHasher;
|
||||
return stringHasher(resourceIdent.getName()) ^ intHasher(static_cast<int>(resourceIdent.getType()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class manages the loading of resources whether standard
|
||||
* or derived from several container formats and the file system.
|
||||
*/
|
||||
class DLL_LINKAGE CResourceLoader
|
||||
{
|
||||
typedef std::unordered_map<ResourceID, std::vector<ResourceLocator> > ResourcesMap;
|
||||
|
||||
public:
|
||||
/// class for iterating over all available files/Identifiers
|
||||
/// can be created via CResourceLoader::getIterator
|
||||
template <typename Comparator, typename Iter>
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
/// find next available item.
|
||||
Iterator& operator++()
|
||||
{
|
||||
assert(begin != end);
|
||||
begin++;
|
||||
findNext();
|
||||
return *this;
|
||||
}
|
||||
bool hasNext()
|
||||
{
|
||||
return begin != end;
|
||||
}
|
||||
|
||||
/// get identifier of current item
|
||||
const ResourceID & operator* () const
|
||||
{
|
||||
assert(begin != end);
|
||||
return begin->first;
|
||||
}
|
||||
|
||||
/// get identifier of current item
|
||||
const ResourceID * operator -> () const
|
||||
{
|
||||
assert(begin != end);
|
||||
return &begin->first;
|
||||
}
|
||||
|
||||
protected:
|
||||
Iterator(Iter begin, Iter end, Comparator comparator):
|
||||
begin(begin),
|
||||
end(end),
|
||||
comparator(comparator)
|
||||
{
|
||||
//find first applicable item
|
||||
findNext();
|
||||
}
|
||||
|
||||
friend class CResourceLoader;
|
||||
|
||||
private:
|
||||
Iter begin;
|
||||
Iter end;
|
||||
Comparator comparator;
|
||||
|
||||
void findNext()
|
||||
{
|
||||
while (begin != end && !comparator(begin->first))
|
||||
begin++;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CResourceLoader();
|
||||
|
||||
/**
|
||||
* Loads the resource specified by the resource identifier.
|
||||
*
|
||||
* @param resourceIdent This parameter identifies the resource to load.
|
||||
* @return a pointer to the input stream, not null
|
||||
*
|
||||
* @throws std::runtime_error if the resource doesn't exists
|
||||
*/
|
||||
std::unique_ptr<CInputStream> load(const ResourceID & resourceIdent) const;
|
||||
/// temporary member to ease transition to new filesystem classes
|
||||
std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const ResourceID & resourceIdent) const;
|
||||
|
||||
/**
|
||||
* Get resource locator for this identifier
|
||||
*
|
||||
* @param resourceIdent This parameter identifies the resource to load.
|
||||
* @return resource locator for this resource or empty one if resource was not found
|
||||
*/
|
||||
ResourceLocator getResource(const ResourceID & resourceIdent) const;
|
||||
|
||||
/// returns ALL overriden resources with same name, including last one acessible via getResource
|
||||
const std::vector<ResourceLocator> & getResourcesWithName(const ResourceID & resourceIdent) const;
|
||||
|
||||
/// returns real name of file in filesystem. Not usable for archives
|
||||
std::string getResourceName(const ResourceID & resourceIdent) const;
|
||||
|
||||
/**
|
||||
* Get iterator for looping all files matching filter
|
||||
* Notes:
|
||||
* - iterating over all files may be slow. Use with caution
|
||||
* - all filenames are in upper case
|
||||
*
|
||||
* @param filter functor with signature bool(ResourceIdentifier) used to check if this file is required
|
||||
* @return resource locator for this resource or empty one if resource was not found
|
||||
*/
|
||||
template<typename Comparator>
|
||||
Iterator<Comparator, ResourcesMap::const_iterator> getIterator(Comparator filter) const
|
||||
{
|
||||
return Iterator<Comparator, ResourcesMap::const_iterator>(resources.begin(), resources.end(), filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the specified resource exists.
|
||||
*
|
||||
* @param resourceIdent the resource which should be checked
|
||||
* @return true if the resource exists, false if not
|
||||
*/
|
||||
bool existsResource(const ResourceID & resourceIdent) const;
|
||||
|
||||
/**
|
||||
* Creates new resource (if not exists) with specified URI.
|
||||
* Type will be determined from extension
|
||||
* File case will be same as in URI
|
||||
*
|
||||
* @param URI file to create
|
||||
* @param update if true - this file is already present but not yet known by res handler
|
||||
* @return true on success, false if resource exists or on error
|
||||
*/
|
||||
bool createResource(std::string URI, bool update = false);
|
||||
|
||||
/**
|
||||
* Adds a simple resource loader to the loaders list and its entries to the resources list.
|
||||
*
|
||||
* The loader object will be destructed when this resource loader is destructed.
|
||||
* Don't delete it manually.
|
||||
* Same loader can be added multiple times (with different mount point)
|
||||
*
|
||||
* @param mountPoint prefix that will be added to all files in this loader
|
||||
* @param loader The simple resource loader object to add
|
||||
*/
|
||||
void addLoader(std::string mountPoint, shared_ptr<ISimpleResourceLoader> loader, bool writeable);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Contains lists of same resources which can be accessed uniquely by an
|
||||
* resource identifier.
|
||||
*/
|
||||
ResourcesMap resources;
|
||||
|
||||
struct LoaderEntry
|
||||
{
|
||||
std::string prefix;
|
||||
shared_ptr<ISimpleResourceLoader> loader;
|
||||
bool writeable;
|
||||
};
|
||||
|
||||
/** A list of resource loader objects */
|
||||
std::vector<LoaderEntry > loaders;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class has static methods for a global resource loader access.
|
||||
*
|
||||
* Class is not thread-safe. Make sure nobody is calling getInstance while somebody else is calling initialize.
|
||||
*/
|
||||
class DLL_LINKAGE CResourceHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Gets an instance of resource loader.
|
||||
*
|
||||
* Make sure that you've set an instance before using it. It'll throw an exception if no instance was set.
|
||||
*
|
||||
* @return Returns an instance of resource loader.
|
||||
*/
|
||||
static CResourceLoader * get();
|
||||
|
||||
/**
|
||||
* Creates instance of resource loader.
|
||||
* Will not fill filesystem with data
|
||||
*
|
||||
*/
|
||||
static void initialize();
|
||||
|
||||
/**
|
||||
* Semi-debug method to track all possible cases of memory leaks
|
||||
* Used before exiting application
|
||||
*
|
||||
*/
|
||||
static void clear();
|
||||
|
||||
/**
|
||||
* Will load all filesystem data from Json data at this path (config/filesystem.json)
|
||||
* @param prefix - prefix for all paths in filesystem config
|
||||
*/
|
||||
static void loadFileSystem(const std::string &prefix, const std::string & fsConfigURI);
|
||||
static void loadFileSystem(const std::string &prefix, const JsonNode & fsConfig);
|
||||
static void loadDirectory(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
|
||||
static void loadArchive(const std::string &prefix, const std::string & mountPoint, const JsonNode & config, EResType::Type archiveType);
|
||||
static void loadJsonMap(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
|
||||
|
||||
/**
|
||||
* Checks all subfolders of MODS directory for presence of mods
|
||||
* If this directory has mod.json file it will be added to resources
|
||||
*/
|
||||
static std::vector<std::string> getAvailableMods();
|
||||
static void setActiveMods(std::vector<std::string> enabledMods); //WARNING: not reentrable. Do not call it twice!!!
|
||||
|
||||
private:
|
||||
/** Instance of resource loader */
|
||||
static CResourceLoader * resourceLoader;
|
||||
static CResourceLoader * initialLoader;
|
||||
};
|
||||
|
||||
/**
|
||||
* A struct which describes the exact position of a resource.
|
||||
*/
|
||||
class DLL_LINKAGE ResourceLocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param archive A pointer to the resource archive object.
|
||||
* @param resourceName Unique resource name in the space of the given resource archive.
|
||||
*/
|
||||
ResourceLocator(ISimpleResourceLoader * loader, const std::string & resourceName);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the resource loader object.
|
||||
*
|
||||
* @return a pointer to the resource loader object
|
||||
*/
|
||||
ISimpleResourceLoader * getLoader() const;
|
||||
|
||||
/**
|
||||
* Gets the resource name.
|
||||
*
|
||||
* @return the resource name.
|
||||
*/
|
||||
std::string getResourceName() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* A pointer to the loader which knows where and how to construct a stream object
|
||||
* which does the loading process actually.
|
||||
*/
|
||||
ISimpleResourceLoader * loader;
|
||||
|
||||
/** A unique name of the resource in space of the loader. */
|
||||
std::string resourceName;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class which provides a functionality to convert extension strings to EResTypes.
|
||||
*/
|
||||
class DLL_LINKAGE EResTypeHelper
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Converts a extension string to a EResType enum object.
|
||||
*
|
||||
* @param extension The extension string e.g. .BMP, .PNG
|
||||
* @return Returns a EResType enum object
|
||||
*/
|
||||
static EResType::Type getTypeFromExtension(std::string extension);
|
||||
|
||||
/**
|
||||
* Gets the EResType as a string representation.
|
||||
*
|
||||
* @param type the EResType
|
||||
* @return the type as a string representation
|
||||
*/
|
||||
static std::string getEResTypeAsString(EResType::Type type);
|
||||
};
|
316
lib/filesystem/Filesystem.cpp
Normal file
316
lib/filesystem/Filesystem.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
#include "StdInc.h"
|
||||
#include "Filesystem.h"
|
||||
|
||||
#include "CFileInfo.h"
|
||||
|
||||
#include "CArchiveLoader.h"
|
||||
#include "CFilesystemLoader.h"
|
||||
#include "AdapterLoaders.h"
|
||||
|
||||
//For filesystem initialization
|
||||
#include "../JsonNode.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "../VCMIDirs.h"
|
||||
#include "../CStopWatch.h"
|
||||
|
||||
CFilesystemList * CResourceHandler::resourceLoader = nullptr;
|
||||
CFilesystemList * CResourceHandler::initialLoader = nullptr;
|
||||
|
||||
ResourceID::ResourceID()
|
||||
:type(EResType::OTHER)
|
||||
{
|
||||
}
|
||||
|
||||
ResourceID::ResourceID(std::string name)
|
||||
{
|
||||
CFileInfo info(std::move(name));
|
||||
setName(info.getStem());
|
||||
setType(info.getType());
|
||||
}
|
||||
|
||||
ResourceID::ResourceID(std::string name, EResType::Type type)
|
||||
{
|
||||
setName(std::move(name));
|
||||
setType(type);
|
||||
}
|
||||
|
||||
std::string ResourceID::getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
EResType::Type ResourceID::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
void ResourceID::setName(std::string name)
|
||||
{
|
||||
this->name = std::move(name);
|
||||
|
||||
size_t dotPos = this->name.find_last_of("/.");
|
||||
|
||||
if(dotPos != std::string::npos && this->name[dotPos] == '.')
|
||||
this->name.erase(dotPos);
|
||||
|
||||
// strangely enough but this line takes 40-50% of filesystem loading time
|
||||
boost::to_upper(this->name);
|
||||
}
|
||||
|
||||
void ResourceID::setType(EResType::Type type)
|
||||
{
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
void CResourceHandler::clear()
|
||||
{
|
||||
delete resourceLoader;
|
||||
delete initialLoader;
|
||||
}
|
||||
|
||||
EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
|
||||
{
|
||||
boost::to_upper(extension);
|
||||
|
||||
static const std::map<std::string, EResType::Type> stringToRes =
|
||||
boost::assign::map_list_of
|
||||
(".TXT", EResType::TEXT)
|
||||
(".JSON", EResType::TEXT)
|
||||
(".DEF", EResType::ANIMATION)
|
||||
(".MSK", EResType::MASK)
|
||||
(".MSG", EResType::MASK)
|
||||
(".H3C", EResType::CAMPAIGN)
|
||||
(".H3M", EResType::MAP)
|
||||
(".FNT", EResType::BMP_FONT)
|
||||
(".TTF", EResType::TTF_FONT)
|
||||
(".BMP", EResType::IMAGE)
|
||||
(".JPG", EResType::IMAGE)
|
||||
(".PCX", EResType::IMAGE)
|
||||
(".PNG", EResType::IMAGE)
|
||||
(".TGA", EResType::IMAGE)
|
||||
(".WAV", EResType::SOUND)
|
||||
(".82M", EResType::SOUND)
|
||||
(".SMK", EResType::VIDEO)
|
||||
(".BIK", EResType::VIDEO)
|
||||
(".MJPG", EResType::VIDEO)
|
||||
(".MPG", EResType::VIDEO)
|
||||
(".AVI", EResType::VIDEO)
|
||||
(".MP3", EResType::MUSIC)
|
||||
(".OGG", EResType::MUSIC)
|
||||
(".LOD", EResType::ARCHIVE_LOD)
|
||||
(".PAC", EResType::ARCHIVE_LOD)
|
||||
(".VID", EResType::ARCHIVE_VID)
|
||||
(".SND", EResType::ARCHIVE_SND)
|
||||
(".PAL", EResType::PALETTE)
|
||||
(".VCGM1", EResType::CLIENT_SAVEGAME)
|
||||
(".VSGM1", EResType::SERVER_SAVEGAME)
|
||||
(".ERM", EResType::ERM)
|
||||
(".ERT", EResType::ERT)
|
||||
(".ERS", EResType::ERS);
|
||||
|
||||
auto iter = stringToRes.find(extension);
|
||||
if (iter == stringToRes.end())
|
||||
return EResType::OTHER;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
|
||||
{
|
||||
#define MAP_ENUM(value) (EResType::value, #value)
|
||||
|
||||
static const std::map<EResType::Type, std::string> stringToRes = boost::assign::map_list_of
|
||||
MAP_ENUM(TEXT)
|
||||
MAP_ENUM(ANIMATION)
|
||||
MAP_ENUM(MASK)
|
||||
MAP_ENUM(CAMPAIGN)
|
||||
MAP_ENUM(MAP)
|
||||
MAP_ENUM(BMP_FONT)
|
||||
MAP_ENUM(TTF_FONT)
|
||||
MAP_ENUM(IMAGE)
|
||||
MAP_ENUM(VIDEO)
|
||||
MAP_ENUM(SOUND)
|
||||
MAP_ENUM(MUSIC)
|
||||
MAP_ENUM(ARCHIVE_LOD)
|
||||
MAP_ENUM(ARCHIVE_SND)
|
||||
MAP_ENUM(ARCHIVE_VID)
|
||||
MAP_ENUM(PALETTE)
|
||||
MAP_ENUM(CLIENT_SAVEGAME)
|
||||
MAP_ENUM(SERVER_SAVEGAME)
|
||||
MAP_ENUM(DIRECTORY)
|
||||
MAP_ENUM(ERM)
|
||||
MAP_ENUM(ERT)
|
||||
MAP_ENUM(ERS)
|
||||
MAP_ENUM(OTHER);
|
||||
|
||||
#undef MAP_ENUM
|
||||
|
||||
auto iter = stringToRes.find(type);
|
||||
assert(iter != stringToRes.end());
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void CResourceHandler::initialize()
|
||||
{
|
||||
//recurse only into specific directories
|
||||
auto recurseInDir = [](std::string URI, int depth)
|
||||
{
|
||||
ResourceID ID(URI, EResType::DIRECTORY);
|
||||
|
||||
for(auto & loader : initialLoader->getResourcesWithName(ID))
|
||||
{
|
||||
auto filename = loader->getResourceName(ID);
|
||||
if (filename)
|
||||
{
|
||||
auto dir = new CFilesystemLoader(URI + "/", *filename, depth, true);
|
||||
initialLoader->addLoader(dir, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//temporary filesystem that will be used to initialize main one.
|
||||
//used to solve several case-sensivity issues like Mp3 vs MP3
|
||||
initialLoader = new CFilesystemList;
|
||||
resourceLoader = new CFilesystemList;
|
||||
|
||||
for (auto & path : VCMIDirs::get().dataPaths())
|
||||
initialLoader->addLoader(new CFilesystemLoader("", path, 0, true), false);
|
||||
|
||||
if (VCMIDirs::get().dataPaths().back() != VCMIDirs::get().userDataPath())
|
||||
initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath(), 0, true), false);
|
||||
|
||||
recurseInDir("CONFIG", 0);// look for configs
|
||||
recurseInDir("DATA", 0); // look for archives
|
||||
//TODO: improve mod loading process so depth 2 will no longer be needed
|
||||
recurseInDir("MODS", 2); // look for mods. Depth 2 is required for now but won't cause speed issues if no mods present
|
||||
}
|
||||
|
||||
void CResourceHandler::loadDirectory(const std::string &prefix, const std::string &mountPoint, const JsonNode & config)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
bool writeable = config["writeable"].Bool();
|
||||
int depth = 16;
|
||||
if (!config["depth"].isNull())
|
||||
depth = config["depth"].Float();
|
||||
|
||||
ResourceID resID(URI, EResType::DIRECTORY);
|
||||
|
||||
for(auto & loader : initialLoader->getResourcesWithName(resID))
|
||||
{
|
||||
auto filename = loader->getResourceName(resID);
|
||||
resourceLoader->addLoader(new CFilesystemLoader(mountPoint, *filename, depth), writeable);
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceHandler::loadArchive(const std::string &prefix, const std::string &mountPoint, const JsonNode & config, EResType::Type archiveType)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
auto filename = initialLoader->getResourceName(ResourceID(URI, archiveType));
|
||||
if (filename)
|
||||
resourceLoader->addLoader(new CArchiveLoader(mountPoint, *filename), false);
|
||||
}
|
||||
|
||||
void CResourceHandler::loadJsonMap(const std::string &prefix, const std::string &mountPoint, const JsonNode & config)
|
||||
{
|
||||
std::string URI = prefix + config["path"].String();
|
||||
auto filename = initialLoader->getResourceName(ResourceID(URI, EResType::TEXT));
|
||||
if (filename)
|
||||
{
|
||||
auto configData = initialLoader->load(ResourceID(URI, EResType::TEXT))->readAll();
|
||||
const JsonNode config((char*)configData.first.get(), configData.second);
|
||||
resourceLoader->addLoader(new CMappedFileLoader(mountPoint, config), false);
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceHandler::loadMainFileSystem(const std::string &fsConfigURI)
|
||||
{
|
||||
auto fsConfigData = initialLoader->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll();
|
||||
|
||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||
|
||||
loadModFileSystem("", fsConfig["filesystem"]);
|
||||
|
||||
// hardcoded system-specific path, may not be inside any of data directories
|
||||
resourceLoader->addLoader(new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()), true);
|
||||
resourceLoader->addLoader(new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()), true);
|
||||
}
|
||||
|
||||
void CResourceHandler::loadModFileSystem(const std::string & prefix, const JsonNode &fsConfig)
|
||||
{
|
||||
for(auto & mountPoint : fsConfig.Struct())
|
||||
{
|
||||
for(auto & entry : mountPoint.second.Vector())
|
||||
{
|
||||
CStopWatch timer;
|
||||
logGlobal->debugStream() << "\t\tLoading resource at " << prefix + entry["path"].String();
|
||||
|
||||
//TODO: replace if's with string->functor map?
|
||||
if (entry["type"].String() == "map")
|
||||
loadJsonMap(prefix, mountPoint.first, entry);
|
||||
if (entry["type"].String() == "dir")
|
||||
loadDirectory(prefix, mountPoint.first, entry);
|
||||
if (entry["type"].String() == "lod")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_LOD);
|
||||
if (entry["type"].String() == "snd")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_SND);
|
||||
if (entry["type"].String() == "vid")
|
||||
loadArchive(prefix, mountPoint.first, entry, EResType::ARCHIVE_VID);
|
||||
|
||||
logGlobal->debugStream() << "Resource loaded in " << timer.getDiff() << " ms.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> CResourceHandler::getAvailableMods()
|
||||
{
|
||||
static const std::string modDir = "MODS/";
|
||||
|
||||
auto list = initialLoader->getFilteredFiles([](const ResourceID & id) -> bool
|
||||
{
|
||||
return id.getType() == EResType::DIRECTORY
|
||||
&& boost::range::count(id.getName(), '/') == 1
|
||||
&& boost::algorithm::starts_with(id.getName(), modDir);
|
||||
});
|
||||
|
||||
//storage for found mods
|
||||
std::vector<std::string> foundMods;
|
||||
for (auto & entry : list)
|
||||
{
|
||||
std::string name = entry.getName();
|
||||
|
||||
name.erase(0, modDir.size()); //Remove path prefix
|
||||
|
||||
if (name == "WOG") // check if wog is actually present. Hack-ish but better than crash
|
||||
{
|
||||
if (!initialLoader->existsResource(ResourceID("DATA/ZVS", EResType::DIRECTORY)) &&
|
||||
!initialLoader->existsResource(ResourceID("MODS/WOG/DATA/ZVS", EResType::DIRECTORY)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foundMods.push_back(name);
|
||||
}
|
||||
return foundMods;
|
||||
}
|
||||
|
||||
void CResourceHandler::setActiveMods(std::vector<std::string> enabledMods)
|
||||
{
|
||||
// default FS config for mods: directory "Content" that acts as H3 root directory
|
||||
JsonNode defaultFS;
|
||||
|
||||
defaultFS[""].Vector().resize(1);
|
||||
defaultFS[""].Vector()[0]["type"].String() = "dir";
|
||||
defaultFS[""].Vector()[0]["path"].String() = "/Content";
|
||||
|
||||
for(std::string & modName : enabledMods)
|
||||
{
|
||||
ResourceID modConfFile("mods/" + modName + "/mod", EResType::TEXT);
|
||||
auto fsConfigData = initialLoader->load(modConfFile)->readAll();
|
||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||
|
||||
if (!fsConfig["filesystem"].isNull())
|
||||
loadModFileSystem("mods/" + modName, fsConfig["filesystem"]);
|
||||
else
|
||||
loadModFileSystem("mods/" + modName, defaultFS);
|
||||
}
|
||||
}
|
208
lib/filesystem/Filesystem.h
Normal file
208
lib/filesystem/Filesystem.h
Normal file
@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Filesystem.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "CInputStream.h"
|
||||
#include "ISimpleResourceLoader.h"
|
||||
|
||||
class CFilesystemList;
|
||||
class JsonNode;
|
||||
|
||||
/**
|
||||
* Specifies the resource type.
|
||||
*
|
||||
* Supported file extensions:
|
||||
*
|
||||
* Text: .txt .json
|
||||
* Animation: .def
|
||||
* Mask: .msk .msg
|
||||
* Campaign: .h3c
|
||||
* Map: .h3m
|
||||
* Font: .fnt
|
||||
* Image: .bmp, .jpg, .pcx, .png, .tga
|
||||
* Sound: .wav .82m
|
||||
* Video: .smk, .bik .mjpg .mpg
|
||||
* Music: .mp3, .ogg
|
||||
* Archive: .lod, .snd, .vid .pac
|
||||
* Palette: .pal
|
||||
* Savegame: .v*gm1
|
||||
*/
|
||||
namespace EResType
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
TEXT,
|
||||
ANIMATION,
|
||||
MASK,
|
||||
CAMPAIGN,
|
||||
MAP,
|
||||
BMP_FONT,
|
||||
TTF_FONT,
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
SOUND,
|
||||
MUSIC,
|
||||
ARCHIVE_VID,
|
||||
ARCHIVE_SND,
|
||||
ARCHIVE_LOD,
|
||||
PALETTE,
|
||||
CLIENT_SAVEGAME,
|
||||
SERVER_SAVEGAME,
|
||||
DIRECTORY,
|
||||
ERM,
|
||||
ERT,
|
||||
ERS,
|
||||
OTHER
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A struct which identifies a resource clearly.
|
||||
*/
|
||||
class DLL_LINKAGE ResourceID
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default c-tor.
|
||||
*/
|
||||
ResourceID();
|
||||
|
||||
/**
|
||||
* Ctor. Can be used to create indentifier for resource loading using one parameter
|
||||
*
|
||||
* @param name The resource name including extension.
|
||||
*/
|
||||
explicit ResourceID(std::string fullName);
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*
|
||||
* @param name The resource name.
|
||||
* @param type The resource type. A constant from the enumeration EResType.
|
||||
*/
|
||||
ResourceID(std::string name, EResType::Type type);
|
||||
|
||||
/**
|
||||
* Compares this object with a another resource identifier.
|
||||
*
|
||||
* @param other The other resource identifier.
|
||||
* @return Returns true if both are equally, false if not.
|
||||
*/
|
||||
inline bool operator==(ResourceID const & other) const
|
||||
{
|
||||
return name == other.name && type == other.type;
|
||||
}
|
||||
|
||||
std::string getName() const;
|
||||
EResType::Type getType() const;
|
||||
void setName(std::string name);
|
||||
void setType(EResType::Type type);
|
||||
|
||||
private:
|
||||
/** Specifies the resource name. No extension so .pcx and .png can override each other, always in upper case. **/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Specifies the resource type. EResType::OTHER if not initialized.
|
||||
* Required to prevent conflicts if files with different types (e.g. text and image) have the same name.
|
||||
*/
|
||||
EResType::Type type;
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <> struct hash<ResourceID>
|
||||
{
|
||||
size_t operator()(const ResourceID & resourceIdent) const
|
||||
{
|
||||
std::hash<int> intHasher;
|
||||
std::hash<std::string> stringHasher;
|
||||
return stringHasher(resourceIdent.getName()) ^ intHasher(static_cast<int>(resourceIdent.getType()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class has static methods for a global resource loader access.
|
||||
*
|
||||
* Class is not thread-safe. Make sure nobody is calling getInstance while somebody else is calling initialize.
|
||||
*/
|
||||
class DLL_LINKAGE CResourceHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Gets an instance of resource loader.
|
||||
*
|
||||
* Make sure that you've set an instance before using it. It'll throw an exception if no instance was set.
|
||||
*
|
||||
* @return Returns an instance of resource loader.
|
||||
*/
|
||||
static ISimpleResourceLoader * get();
|
||||
|
||||
/**
|
||||
* Creates instance of resource loader.
|
||||
* Will not fill filesystem with data
|
||||
*
|
||||
*/
|
||||
static void initialize();
|
||||
|
||||
/**
|
||||
* Semi-debug method to track all possible cases of memory leaks
|
||||
* Used before exiting application
|
||||
*
|
||||
*/
|
||||
static void clear();
|
||||
|
||||
/**
|
||||
* Will load all filesystem data from Json data at this path (config/filesystem.json)
|
||||
* @param prefix - prefix for all paths in filesystem config
|
||||
*/
|
||||
static void loadMainFileSystem(const std::string & fsConfigURI);
|
||||
static void loadModFileSystem(const std::string &prefix, const JsonNode & fsConfig);
|
||||
static void loadDirectory(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
|
||||
static void loadArchive(const std::string &prefix, const std::string & mountPoint, const JsonNode & config, EResType::Type archiveType);
|
||||
static void loadJsonMap(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
|
||||
|
||||
/**
|
||||
* Checks all subfolders of MODS directory for presence of mods
|
||||
* If this directory has mod.json file it will be added to resources
|
||||
*/
|
||||
static std::vector<std::string> getAvailableMods();
|
||||
static void setActiveMods(std::vector<std::string> enabledMods); //WARNING: not reentrable. Do not call it twice!!!
|
||||
|
||||
private:
|
||||
/** Instance of resource loader */
|
||||
static CFilesystemList * resourceLoader;
|
||||
static CFilesystemList * initialLoader;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class which provides a functionality to convert extension strings to EResTypes.
|
||||
*/
|
||||
class DLL_LINKAGE EResTypeHelper
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Converts a extension string to a EResType enum object.
|
||||
*
|
||||
* @param extension The extension string e.g. .BMP, .PNG
|
||||
* @return Returns a EResType enum object
|
||||
*/
|
||||
static EResType::Type getTypeFromExtension(std::string extension);
|
||||
|
||||
/**
|
||||
* Gets the EResType as a string representation.
|
||||
*
|
||||
* @param type the EResType
|
||||
* @return the type as a string representation
|
||||
*/
|
||||
static std::string getEResTypeAsString(EResType::Type type);
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* ISimpleResourceLoader.h, part of VCMI engine
|
||||
@ -9,10 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CInputStream.h"
|
||||
#include "CResourceLoader.h" //FIXME: move ResourceID + EResType in separate file?
|
||||
class CInputStream;
|
||||
class ResourceID;
|
||||
|
||||
/**
|
||||
* A class which knows the files containing in the archive or system and how to load them.
|
||||
@ -20,9 +19,6 @@
|
||||
class DLL_LINKAGE ISimpleResourceLoader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Dtor.
|
||||
*/
|
||||
virtual ~ISimpleResourceLoader() { };
|
||||
|
||||
/**
|
||||
@ -31,44 +27,59 @@ public:
|
||||
* @param resourceName The unqiue resource name in space of the archive.
|
||||
* @return a input stream object
|
||||
*/
|
||||
virtual std::unique_ptr<CInputStream> load(const std::string & resourceName) const =0;
|
||||
virtual std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const = 0;
|
||||
|
||||
/**
|
||||
* Checks if the entry exists.
|
||||
*
|
||||
* @return Returns true if the entry exists, false if not.
|
||||
*/
|
||||
virtual bool existsEntry(const std::string & resourceName) const =0;
|
||||
virtual bool existsResource(const ResourceID & resourceName) const = 0;
|
||||
|
||||
/**
|
||||
* Gets all entries in the archive or (file) system.
|
||||
* Gets mount point to which this loader was attached
|
||||
*
|
||||
* @return Returns a list of all entries in the archive or (file) system.
|
||||
* @return mount point URI
|
||||
*/
|
||||
virtual std::unordered_map<ResourceID, std::string> getEntries() const =0;
|
||||
|
||||
/**
|
||||
* Gets the origin of the loader.
|
||||
*
|
||||
* @return the file path to source of this loader
|
||||
*/
|
||||
virtual std::string getOrigin() const =0;
|
||||
virtual std::string getMountPoint() const = 0;
|
||||
|
||||
/**
|
||||
* Gets full name of resource, e.g. name of file in filesystem.
|
||||
*
|
||||
* @return path or empty optional if file can't be accessed independently (e.g. file in archive)
|
||||
*/
|
||||
virtual std::string getFullName(const std::string & resourceName) const
|
||||
virtual boost::optional<std::string> getResourceName(const ResourceID & resourceName) const
|
||||
{
|
||||
return getOrigin() + '/' + resourceName;
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of files that matches filter function
|
||||
*
|
||||
* @param filter Filter that returns true if specified ID matches filter
|
||||
* @return Returns list of flies
|
||||
*/
|
||||
virtual std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const = 0;
|
||||
|
||||
/**
|
||||
* Creates new resource with specified filename.
|
||||
*
|
||||
* @returns true if new file was created, false on error or if file already exists
|
||||
* @return true if new file was created, false on error or if file already exists
|
||||
*/
|
||||
virtual bool createEntry(std::string filename, bool update = false)
|
||||
virtual bool createResource(std::string filename, bool update = false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns all loaders that have resource with such name
|
||||
*
|
||||
* @return vector with all loaders
|
||||
*/
|
||||
virtual std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const
|
||||
{
|
||||
if (existsResource(resourceName))
|
||||
return std::vector<const ISimpleResourceLoader *>(1, this);
|
||||
return std::vector<const ISimpleResourceLoader *>();
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CCampaignHandler.h"
|
||||
|
||||
#include "../filesystem/CResourceLoader.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../filesystem/CCompressedStream.h"
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../vcmi_endian.h"
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "CMapEditManager.h"
|
||||
|
||||
#include "../JsonNode.h"
|
||||
#include "../filesystem/CResourceLoader.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../CDefObjInfoHandler.h"
|
||||
|
||||
MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMapService.h"
|
||||
|
||||
#include "../filesystem/CResourceLoader.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../filesystem/CBinaryReader.h"
|
||||
#include "../filesystem/CCompressedStream.h"
|
||||
#include "../filesystem/CMemoryStream.h"
|
||||
|
@ -15,8 +15,7 @@
|
||||
|
||||
#include "../CStopWatch.h"
|
||||
|
||||
#include "../filesystem/CResourceLoader.h"
|
||||
#include "../filesystem/CInputStream.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "CMap.h"
|
||||
|
||||
#include "../CSpellHandler.h"
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "../CDefObjInfoHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../StringConstants.h"
|
||||
#include "../filesystem/CResourceLoader.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
|
||||
CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false),
|
||||
playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(0), compOnlyTeamCount(RANDOM_SIZE),
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CFileInfo.h"
|
||||
#include "../lib/int3.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
@ -2233,7 +2233,7 @@ void CGameHandler::save(const std::string & filename )
|
||||
// }
|
||||
|
||||
{
|
||||
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME)));
|
||||
CSaveFile save(*CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME)));
|
||||
saveCommonState(save);
|
||||
logGlobal->infoStream() << "Saving server state";
|
||||
save << *this;
|
||||
@ -4533,7 +4533,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
|
||||
if (st->hasBonusOfType(Bonus::MANA_DRAIN) && !vstd::contains(st->state, EBattleStackState::DRAINED_MANA))
|
||||
{
|
||||
const CGHeroInstance * enemy = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
|
||||
const CGHeroInstance * owner = gs->curB->getHero(st->owner);
|
||||
//const CGHeroInstance * owner = gs->curB->getHero(st->owner);
|
||||
if (enemy)
|
||||
{
|
||||
ui32 manaDrained = st->valOfBonuses(Bonus::MANA_DRAIN);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/Connection.h"
|
||||
@ -475,7 +475,7 @@ void CVCMIServer::loadGame()
|
||||
// }
|
||||
|
||||
{
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
|
||||
CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
|
||||
gh.loadCommonState(lf);
|
||||
lf >> gh;
|
||||
}
|
||||
|
@ -14,8 +14,9 @@
|
||||
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/filesystem/CResourceLoader.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CFilesystemLoader.h"
|
||||
#include "../lib/filesystem/AdapterLoaders.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/mapping/CMapEditManager.h"
|
||||
#include "../lib/int3.h"
|
||||
@ -81,8 +82,8 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
try
|
||||
{
|
||||
// Load maps and json config
|
||||
auto loader = make_shared<CFilesystemLoader>(".");
|
||||
CResourceHandler::get()->addLoader("test/", loader, false);
|
||||
auto loader = new CFilesystemLoader("test/", ".");
|
||||
dynamic_cast<CFilesystemList*>(CResourceHandler::get())->addLoader(loader, false);
|
||||
const auto originalMap = CMapService::loadMap("test/TerrainViewTest");
|
||||
auto map = CMapService::loadMap("test/TerrainViewTest");
|
||||
logGlobal->infoStream() << "Loaded test map successfully.";
|
||||
|
Loading…
Reference in New Issue
Block a user