1
0
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:
Ivan Savenko 2013-07-28 14:49:50 +00:00
parent aa6626bc35
commit b87897096c
64 changed files with 1203 additions and 1521 deletions

View File

@ -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"

View File

@ -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());

View File

@ -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,9 +112,7 @@ 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
@ -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)

View File

@ -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();

View File

@ -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);

View File

@ -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)
{

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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
{

View File

@ -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];

View File

@ -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;
}

View File

@ -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];

View File

@ -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;

View File

@ -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)

View File

@ -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}
]
}
}

View File

@ -10,7 +10,7 @@
#include "CTownHandler.h"
#include "NetPacks.h"
#include "JsonNode.h"
#include "filesystem/CResourceLoader.h"
#include "filesystem/Filesystem.h"
/*

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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"

View File

@ -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;

View File

@ -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"

View File

@ -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)
{

View File

@ -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"

View File

@ -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

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "filesystem/CResourceLoader.h"
#include "filesystem/Filesystem.h"
#include "VCMI_Lib.h"
#include "JsonNode.h"

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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;

View 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());
}
}

View 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);
};

View 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;
}

View File

@ -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;
};

View File

@ -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;

View File

@ -1,3 +1,4 @@
#pragma once
/*
* CBinaryReader.h, part of VCMI engine
@ -9,8 +10,6 @@
*
*/
#pragma once
class CInputStream;
/**

View File

@ -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

View File

@ -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;

View File

@ -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.

View 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)
{

View File

@ -1,3 +1,4 @@
#pragma once
/*
* CFileInputStream.h, part of VCMI engine
@ -9,8 +10,6 @@
*
*/
#pragma once
#include "CInputStream.h"
class CFileInfo;

View File

@ -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
{
if(elem.second == resourceName)
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 (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;
}
std::unordered_map<ResourceID, std::string> CFilesystemLoader::getEntries() const
{
return fileList;
}
filename = filename.substr(mountPoint.size());
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;
}
}

View File

@ -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;
};

View File

@ -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());
}
};

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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;
};

View File

@ -1,3 +1,4 @@
#pragma once
/*
* CMemoryStream.h, part of VCMI engine
@ -9,8 +10,6 @@
*
*/
#pragma once
#include "CInputStream.h"
/**

View File

@ -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);
}
}

View File

@ -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);
};

View 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
View 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);
};

View File

@ -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 *>();
}
};

View File

@ -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"

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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),

View File

@ -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);

View File

@ -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;
}

View File

@ -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.";