diff --git a/client/AdventureMapClasses.cpp b/client/AdventureMapClasses.cpp index 447ac1618..6f1aa8a10 100644 --- a/client/AdventureMapClasses.cpp +++ b/client/AdventureMapClasses.cpp @@ -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" diff --git a/client/CAnimation.cpp b/client/CAnimation.cpp index 04b8d1a67..dd4927749 100644 --- a/client/CAnimation.cpp +++ b/client/CAnimation.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include -#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 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 textData(new ui8[stream->getSize()]); stream->read(textData.get(), stream->getSize()); diff --git a/client/CBitmapHandler.cpp b/client/CBitmapHandler.cpp index 7bf28be81..1af3e83fb 100644 --- a/client/CBitmapHandler.cpp +++ b/client/CBitmapHandler.cpp @@ -1,6 +1,6 @@ #include "StdInc.h" -#include "../lib/filesystem/CResourceLoader.h" +#include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/CFileInfo.h" #include "SDL.h" #include "SDL_image.h" @@ -112,10 +112,8 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna SDL_Surface * ret=nullptr; - auto readFile = CResourceHandler::get()->loadData( - ResourceID(path + fname, EResType::IMAGE)); + auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll(); - if (isPCX(readFile.first.get())) {//H3-style PCX ret = loadH3PCX(readFile.first.get(), readFile.second); @@ -132,7 +130,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna } else { //loading via SDL_Image - CFileInfo info(CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE))); + CFileInfo info(*CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE))); ret = IMG_LoadTyped_RW( //create SDL_RW with our data (will be deleted by SDL) diff --git a/client/CDefHandler.cpp b/client/CDefHandler.cpp index 7b9681125..a4c8cb21b 100644 --- a/client/CDefHandler.cpp +++ b/client/CDefHandler.cpp @@ -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(); diff --git a/client/CMT.cpp b/client/CMT.cpp index e3a727311..937e9f023 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -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); diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index 14d15c2ba..fae86f67b 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -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 "<getResourceName(ResourceID(musicURI, EResType::MUSIC)).c_str()); + music = Mix_LoadMUS(CResourceHandler::get()->getResourceName(ResourceID(musicURI, EResType::MUSIC))->c_str()); if(!music) { diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index ae3d7e720..819e24563 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -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 SelectionTab::getFiles(std::string dirURI, int resType) +std::unordered_set SelectionTab::getFiles(std::string dirURI, int resType) { - std::vector ret; boost::to_upper(dirURI); - auto iterator = CResourceHandler::get()->getIterator([&](const ResourceID & ident) + std::unordered_set 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 & files) +void SelectionTab::parseMaps(const std::unordered_set &files) { allItems.clear(); for(auto & file : files) @@ -1131,13 +1124,13 @@ void SelectionTab::parseMaps(const std::vector & files) } } -void SelectionTab::parseGames(const std::vector &files, bool multi) +void SelectionTab::parseGames(const std::unordered_set &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 &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 &files, bool multi) } } -void SelectionTab::parseCampaigns(const std::vector & files ) +void SelectionTab::parseCampaigns(const std::unordered_set &files ) { - allItems.resize(files.size()); - for(int i=0; igetResourceName( + 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(); } diff --git a/client/CPreGame.h b/client/CPreGame.h index 2ed3065bb..c05a8d211 100644 --- a/client/CPreGame.h +++ b/client/CPreGame.h @@ -1,6 +1,6 @@ #pragma once -#include "../lib/filesystem/CResourceLoader.h" +#include "../lib/filesystem/Filesystem.h" #include #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 &files); - void parseGames(const std::vector &files, bool multi); - void parseCampaigns(const std::vector & files ); - std::vector getFiles(std::string dirURI, int resType); + void parseMaps(const std::unordered_set &files); + void parseGames(const std::unordered_set &files, bool multi); + void parseCampaigns(const std::unordered_set & files ); + std::unordered_set getFiles(std::string dirURI, int resType); CMenuScreen::EState tabType; public: int positions; //how many entries (games/maps) can be shown diff --git a/client/CVideoHandler.cpp b/client/CVideoHandler.cpp index 1a8269bcd..24b536228 100644 --- a/client/CVideoHandler.cpp +++ b/client/CVideoHandler.cpp @@ -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 diff --git a/client/Client.cpp b/client/Client.cpp index 8264c5235..3e44b93f6 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -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 { diff --git a/client/Graphics.cpp b/client/Graphics.cpp index c63dcd506..d01f50ecc 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -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]; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index baea6d54e..74b32f07f 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -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; } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index ed3addfb8..22a0ab4a5 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -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]; diff --git a/client/battle/CCreatureAnimation.cpp b/client/battle/CCreatureAnimation.cpp index bdf471909..c1708c001 100644 --- a/client/battle/CCreatureAnimation.cpp +++ b/client/battle/CCreatureAnimation.cpp @@ -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; diff --git a/client/gui/Fonts.cpp b/client/gui/Fonts.cpp index f21d3d79d..5b7fe0c09 100644 --- a/client/gui/Fonts.cpp +++ b/client/gui/Fonts.cpp @@ -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::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, 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) diff --git a/config/filesystem.json b/config/filesystem.json index 6811ee057..c208df867 100644 --- a/config/filesystem.json +++ b/config/filesystem.json @@ -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} ] } } diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 3d69cde1d..399924d05 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -10,7 +10,7 @@ #include "CTownHandler.h" #include "NetPacks.h" #include "JsonNode.h" -#include "filesystem/CResourceLoader.h" +#include "filesystem/Filesystem.h" /* diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index a91b0b5cd..716ed68f6 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -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" diff --git a/lib/CBonusTypeHandler.cpp b/lib/CBonusTypeHandler.cpp index 43b871438..d291e4231 100644 --- a/lib/CBonusTypeHandler.cpp +++ b/lib/CBonusTypeHandler.cpp @@ -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" diff --git a/lib/CConfigHandler.cpp b/lib/CConfigHandler.cpp index 2de080646..81d757fc8 100644 --- a/lib/CConfigHandler.cpp +++ b/lib/CConfigHandler.cpp @@ -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 &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; } diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index c0a1d4038..6470c092d 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -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" diff --git a/lib/CDefObjInfoHandler.cpp b/lib/CDefObjInfoHandler.cpp index fca122683..55093ec4f 100644 --- a/lib/CDefObjInfoHandler.cpp +++ b/lib/CDefObjInfoHandler.cpp @@ -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; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 4b7456b70..db1912de4 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -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" diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 81bbf3aca..8e23eafd6 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -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) { diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index f6a826e4c..00b321deb 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -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" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 04a552506..58b4ef4e4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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 diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 7ef0e52eb..9fb453234 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -2,8 +2,7 @@ #include "CModHandler.h" #include "CDefObjInfoHandler.h" #include "JsonNode.h" -#include "filesystem/CResourceLoader.h" -#include "filesystem/ISimpleResourceLoader.h" +#include "filesystem/Filesystem.h" #include "CCreatureHandler.h" #include "CArtHandler.h" @@ -497,7 +496,7 @@ void CModHandler::initialize(std::vector 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; } diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 0f7ed32c2..252a0c8a0 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -1,6 +1,6 @@ #pragma once -#include "filesystem/CResourceLoader.h" +#include "filesystem/Filesystem.h" #include "VCMI_Lib.h" #include "JsonNode.h" diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 1daa1823f..be2d29534 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -29,7 +29,7 @@ #include #include "CBuildingHandler.h" #include "JsonNode.h" -#include "filesystem/CResourceLoader.h" +#include "filesystem/Filesystem.h" using namespace boost::assign; diff --git a/lib/CSpellHandler.cpp b/lib/CSpellHandler.cpp index 03285ccd6..b06d96511 100644 --- a/lib/CSpellHandler.cpp +++ b/lib/CSpellHandler.cpp @@ -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 diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index e2f8cb38b..0d1040431 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -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 diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index e1db935b0..7ddee47ed 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -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(file.first.get()), file.second); *this = parser.parse(fileURI.getName()); @@ -1610,19 +1609,16 @@ JsonNode JsonUtils::assembleFromFiles(std::vector 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 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; diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index c7293faca..5014bf6ec 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -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: "<infoStream()<<"\t Data loading: "< 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 CMappedFileLoader::getResourceName(const ResourceID & resourceName) const +{ + return CResourceHandler::get()->getResourceName(fileList.at(resourceName)); +} + +std::unordered_set CMappedFileLoader::getFilteredFiles(std::function filter) const +{ + std::unordered_set foundID; + + for (auto & file : fileList) + { + if (filter(file.first)) + foundID.insert(file.first); + } + return foundID; +} + +std::unique_ptr 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 CFilesystemList::getResourceName(const ResourceID & resourceName) const +{ + if (existsResource(resourceName)) + return getResourcesWithName(resourceName).back()->getResourceName(resourceName); + return boost::optional(); +} + +std::unordered_set CFilesystemList::getFilteredFiles(std::function filter) const +{ + std::unordered_set 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 CFilesystemList::getResourcesWithName(const ResourceID & resourceName) const +{ + std::vector 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(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()); + } +} \ No newline at end of file diff --git a/lib/filesystem/AdapterLoaders.h b/lib/filesystem/AdapterLoaders.h new file mode 100644 index 000000000..e3e675f17 --- /dev/null +++ b/lib/filesystem/AdapterLoaders.h @@ -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 load(const ResourceID & resourceName) const override; + bool existsResource(const ResourceID & resourceName) const override; + std::string getMountPoint() const override; + boost::optional getResourceName(const ResourceID & resourceName) const override; + std::unordered_set getFilteredFiles(std::function 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 fileList; +}; + +class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader +{ + std::vector > loaders; + + std::set writeableLoaders; + +public: + /// Interface implementation + /// @see ISimpleResourceLoader + std::unique_ptr load(const ResourceID & resourceName) const override; + bool existsResource(const ResourceID & resourceName) const override; + std::string getMountPoint() const override; + boost::optional getResourceName(const ResourceID & resourceName) const override; + std::unordered_set getFilteredFiles(std::function filter) const override; + bool createResource(std::string filename, bool update = false) override; + std::vector 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); +}; \ No newline at end of file diff --git a/lib/filesystem/CArchiveLoader.cpp b/lib/filesystem/CArchiveLoader.cpp new file mode 100644 index 000000000..a17e0d06d --- /dev/null +++ b/lib/filesystem/CArchiveLoader.cpp @@ -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(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 offsets; + + // Insert entries to list + for(ui32 i = 0; i < totalFiles; i++) + { + char filename[40]; + reader.read(reinterpret_cast(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(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 CArchiveLoader::load(const ResourceID & resourceName) const +{ + assert(existsResource(resourceName)); + + const ArchiveEntry & entry = entries.at(resourceName); + + if (entry.compressedSize != 0) //compressed data + { + std::unique_ptr fileStream(new CFileInputStream(archive, entry.offset, entry.compressedSize)); + + return std::unique_ptr(new CCompressedStream(std::move(fileStream), false, entry.fullSize)); + } + else + { + return std::unique_ptr(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 CArchiveLoader::getFilteredFiles(std::function filter) const +{ + std::unordered_set foundID; + + for (auto & file : entries) + { + if (filter(file.first)) + foundID.insert(file.first); + } + return foundID; +} \ No newline at end of file diff --git a/lib/filesystem/CLodArchiveLoader.h b/lib/filesystem/CArchiveLoader.h similarity index 65% rename from lib/filesystem/CLodArchiveLoader.h rename to lib/filesystem/CArchiveLoader.h index 35d4dbdf6..a395fb7b1 100644 --- a/lib/filesystem/CLodArchiveLoader.h +++ b/lib/filesystem/CArchiveLoader.h @@ -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 load(const std::string & resourceName) const override; - std::unordered_map getEntries() const override; - bool existsEntry(const std::string & resourceName) const override; - std::string getOrigin() const override; + std::unique_ptr load(const ResourceID & resourceName) const override; + bool existsResource(const ResourceID & resourceName) const override; + std::string getMountPoint() const override; + std::unordered_set getFilteredFiles(std::function 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 entries; + std::unordered_map entries; }; diff --git a/lib/filesystem/CBinaryReader.cpp b/lib/filesystem/CBinaryReader.cpp index 89d65f729..1206f3827 100644 --- a/lib/filesystem/CBinaryReader.cpp +++ b/lib/filesystem/CBinaryReader.cpp @@ -1,5 +1,6 @@ #include "StdInc.h" #include "CBinaryReader.h" + #include #include "CInputStream.h" @@ -12,9 +13,7 @@ CData readLE(CData data) std::reverse(dataPtr, dataPtr + sizeof(data)); return data; } - #else - template 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; diff --git a/lib/filesystem/CBinaryReader.h b/lib/filesystem/CBinaryReader.h index d67d5c869..9926889ed 100644 --- a/lib/filesystem/CBinaryReader.h +++ b/lib/filesystem/CBinaryReader.h @@ -1,3 +1,4 @@ +#pragma once /* * CBinaryReader.h, part of VCMI engine @@ -9,8 +10,6 @@ * */ -#pragma once - class CInputStream; /** diff --git a/lib/filesystem/CCompressedStream.cpp b/lib/filesystem/CCompressedStream.cpp index 690825a95..990d1f3cd 100644 --- a/lib/filesystem/CCompressedStream.cpp +++ b/lib/filesystem/CCompressedStream.cpp @@ -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 diff --git a/lib/filesystem/CCompressedStream.h b/lib/filesystem/CCompressedStream.h index 26d00eea1..2a585b2f1 100644 --- a/lib/filesystem/CCompressedStream.h +++ b/lib/filesystem/CCompressedStream.h @@ -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; diff --git a/lib/filesystem/CFileInfo.h b/lib/filesystem/CFileInfo.h index 975853674..b109ef5f7 100644 --- a/lib/filesystem/CFileInfo.h +++ b/lib/filesystem/CFileInfo.h @@ -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. diff --git a/lib/filesystem/CFileInputStream.cpp b/lib/filesystem/CFileInputStream.cpp index 2909f6471..8b9d1ce47 100644 --- a/lib/filesystem/CFileInputStream.cpp +++ b/lib/filesystem/CFileInputStream.cpp @@ -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) { diff --git a/lib/filesystem/CFileInputStream.h b/lib/filesystem/CFileInputStream.h index 8c7627e46..6fe25be4c 100644 --- a/lib/filesystem/CFileInputStream.h +++ b/lib/filesystem/CFileInputStream.h @@ -1,3 +1,4 @@ +#pragma once /* * CFileInputStream.h, part of VCMI engine @@ -9,8 +10,6 @@ * */ -#pragma once - #include "CInputStream.h" class CFileInfo; diff --git a/lib/filesystem/CFilesystemLoader.cpp b/lib/filesystem/CFilesystemLoader.cpp index ef702ad6d..8457d0884 100644 --- a/lib/filesystem/CFilesystemLoader.cpp +++ b/lib/filesystem/CFilesystemLoader.cpp @@ -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 CFilesystemLoader::load(const std::string & resourceName) const +std::unique_ptr CFilesystemLoader::load(const ResourceID & resourceName) const { - std::unique_ptr stream(new CFileInputStream(getOrigin() + '/' + resourceName)); + assert(fileList.count(resourceName)); + + std::unique_ptr stream(new CFileInputStream(baseDirectory + '/' + fileList.at(resourceName))); return stream; } -bool CFilesystemLoader::existsEntry(const std::string & resourceName) const +bool CFilesystemLoader::existsResource(const ResourceID & resourceName) const { - for(auto & elem : fileList) + return fileList.count(resourceName); +} + +std::string CFilesystemLoader::getMountPoint() const +{ + return mountPoint; +} + +boost::optional CFilesystemLoader::getResourceName(const ResourceID & resourceName) const +{ + assert(existsResource(resourceName)); + + return baseDirectory + '/' + fileList.at(resourceName); +} + +std::unordered_set CFilesystemLoader::getFilteredFiles(std::function filter) const +{ + std::unordered_set foundID; + + for (auto & file : fileList) { - if(elem.second == resourceName) - { - return true; - } + if (filter(file.first)) + foundID.insert(file.first); + } return foundID; +} + +bool CFilesystemLoader::createResource(std::string filename, bool update) +{ + ResourceID resID(filename); + + if (fileList.find(resID) != fileList.end()) + return true; + + if (!boost::iequals(mountPoint, filename.substr(0, mountPoint.size()))) + { + logGlobal->traceStream() << "Can't create file: wrong mount point: " << mountPoint; + return false; } - return false; -} + filename = filename.substr(mountPoint.size()); -std::unordered_map CFilesystemLoader::getEntries() const -{ - return fileList; -} - -std::string CFilesystemLoader::getOrigin() const -{ - return baseDirectory; -} - -bool CFilesystemLoader::createEntry(std::string filename, bool update) -{ - ResourceID res(filename); - if (fileList.find(res) != fileList.end()) - return false; - - fileList[res] = filename; if (!update) + { std::ofstream newfile (baseDirectory + "/" + filename); + if (!newfile.good()) + return false; + } + fileList[resID] = filename; return true; } - -std::unordered_map CFilesystemLoader::listFiles(size_t depth, bool initial) const +std::unordered_map CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const { std::set initialTypes; initialTypes.insert(EResType::DIRECTORY); @@ -77,7 +97,8 @@ std::unordered_map 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 CFilesystemLoader::listFiles(size_t filename += path[i] + '/'; filename += it->path().leaf().string(); - fileList[ResourceID(filename, type)] = filename; + fileList[ResourceID(mountPoint + filename, type)] = filename; } } diff --git a/lib/filesystem/CFilesystemLoader.h b/lib/filesystem/CFilesystemLoader.h index b9f819bb8..a6bb45165 100644 --- a/lib/filesystem/CFilesystemLoader.h +++ b/lib/filesystem/CFilesystemLoader.h @@ -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 load(const std::string & resourceName) const override; - bool existsEntry(const std::string & resourceName) const override; - std::unordered_map getEntries() const override; - std::string getOrigin() const override; - bool createEntry(std::string filename, bool update) override; + std::unique_ptr 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 getResourceName(const ResourceID & resourceName) const override; + std::unordered_set getFilteredFiles(std::function 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 listFiles(size_t depth, bool initial) const; + std::unordered_map listFiles(const std::string &mountPoint, size_t depth, bool initial) const; }; diff --git a/lib/filesystem/CInputStream.h b/lib/filesystem/CInputStream.h index a885ce8cc..e0339286f 100644 --- a/lib/filesystem/CInputStream.h +++ b/lib/filesystem/CInputStream.h @@ -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, size_t> readAll() + { + std::unique_ptr data(new ui8[getSize()]); + + size_t readSize = read(data.get(), getSize()); + assert(readSize == getSize()); + + return std::make_pair(std::move(data), getSize()); + } }; diff --git a/lib/filesystem/CLodArchiveLoader.cpp b/lib/filesystem/CLodArchiveLoader.cpp deleted file mode 100644 index a051200d8..000000000 --- a/lib/filesystem/CLodArchiveLoader.cpp +++ /dev/null @@ -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 - -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(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(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(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 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 fileStream(new CFileInputStream(getOrigin(), entry.offset, entry.size)); - - return std::unique_ptr(new CCompressedStream(std::move(fileStream), false, entry.realSize)); - } - else - { - return std::unique_ptr(new CFileInputStream(getOrigin(), entry.offset, entry.realSize)); - } -} - -std::unordered_map CLodArchiveLoader::getEntries() const -{ - std::unordered_map 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; -} diff --git a/lib/filesystem/CMappedFileLoader.cpp b/lib/filesystem/CMappedFileLoader.cpp deleted file mode 100644 index 765195091..000000000 --- a/lib/filesystem/CMappedFileLoader.cpp +++ /dev/null @@ -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 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 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)); -} diff --git a/lib/filesystem/CMappedFileLoader.h b/lib/filesystem/CMappedFileLoader.h deleted file mode 100644 index 8b7bdccb5..000000000 --- a/lib/filesystem/CMappedFileLoader.h +++ /dev/null @@ -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 load(const std::string & resourceName) const override; - bool existsEntry(const std::string & resourceName) const override; - std::unordered_map 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 fileList; -}; diff --git a/lib/filesystem/CMemoryStream.h b/lib/filesystem/CMemoryStream.h index 4e0041f17..3a9770b10 100644 --- a/lib/filesystem/CMemoryStream.h +++ b/lib/filesystem/CMemoryStream.h @@ -1,3 +1,4 @@ +#pragma once /* * CMemoryStream.h, part of VCMI engine @@ -9,8 +10,6 @@ * */ -#pragma once - #include "CInputStream.h" /** diff --git a/lib/filesystem/CResourceLoader.cpp b/lib/filesystem/CResourceLoader.cpp deleted file mode 100644 index 52e3767ec..000000000 --- a/lib/filesystem/CResourceLoader.cpp +++ /dev/null @@ -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 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, ui64> CResourceLoader::loadData(const ResourceID & resourceIdent) const -{ - auto stream = load(resourceIdent); - std::unique_ptr 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 & CResourceLoader::getResourcesWithName(const ResourceID & resourceIdent) const -{ - static const std::vector 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 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 & 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 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 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 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 loader(new CFilesystemLoader(path, 0, true)); - - initialLoader->addLoader("GLOBAL/", loader, false); - initialLoader->addLoader("ALL/", loader, false); - } - - { - shared_ptr 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(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(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(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 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 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 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); - } -} diff --git a/lib/filesystem/CResourceLoader.h b/lib/filesystem/CResourceLoader.h deleted file mode 100644 index 15504e440..000000000 --- a/lib/filesystem/CResourceLoader.h +++ /dev/null @@ -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 - { - size_t operator()(const ResourceID & resourceIdent) const - { - std::hash intHasher; - std::hash stringHasher; - return stringHasher(resourceIdent.getName()) ^ intHasher(static_cast(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 > ResourcesMap; - -public: - /// class for iterating over all available files/Identifiers - /// can be created via CResourceLoader::getIterator - template - 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 load(const ResourceID & resourceIdent) const; - /// temporary member to ease transition to new filesystem classes - std::pair, 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 & 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 - Iterator getIterator(Comparator filter) const - { - return 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 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 loader; - bool writeable; - }; - - /** A list of resource loader objects */ - std::vector 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 getAvailableMods(); - static void setActiveMods(std::vector 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); -}; diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp new file mode 100644 index 000000000..c4da02b04 --- /dev/null +++ b/lib/filesystem/Filesystem.cpp @@ -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 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 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 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 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 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); + } +} diff --git a/lib/filesystem/Filesystem.h b/lib/filesystem/Filesystem.h new file mode 100644 index 000000000..2994f8cc0 --- /dev/null +++ b/lib/filesystem/Filesystem.h @@ -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 + { + size_t operator()(const ResourceID & resourceIdent) const + { + std::hash intHasher; + std::hash stringHasher; + return stringHasher(resourceIdent.getName()) ^ intHasher(static_cast(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 getAvailableMods(); + static void setActiveMods(std::vector 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); +}; diff --git a/lib/filesystem/ISimpleResourceLoader.h b/lib/filesystem/ISimpleResourceLoader.h index fd4253356..f04762210 100644 --- a/lib/filesystem/ISimpleResourceLoader.h +++ b/lib/filesystem/ISimpleResourceLoader.h @@ -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 load(const std::string & resourceName) const =0; + virtual std::unique_ptr 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 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 getResourceName(const ResourceID & resourceName) const { - return getOrigin() + '/' + resourceName; + return boost::optional(); } + /** + * 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 getFilteredFiles(std::function 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 getResourcesWithName(const ResourceID & resourceName) const + { + if (existsResource(resourceName)) + return std::vector(1, this); + return std::vector(); + } }; diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp index 4715c61fc..1e0343ddf 100644 --- a/lib/mapping/CCampaignHandler.cpp +++ b/lib/mapping/CCampaignHandler.cpp @@ -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" diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index ed455678f..a113bba06 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -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) diff --git a/lib/mapping/CMapService.cpp b/lib/mapping/CMapService.cpp index c1d5e945b..83c50dfc1 100644 --- a/lib/mapping/CMapService.cpp +++ b/lib/mapping/CMapService.cpp @@ -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" diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 5672b1e21..7e0a4edfa 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -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" diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 4965181d3..b12c3b58b 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -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), diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ef0d50dba..1920f7345 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index df6a2d261..e1748eae8 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -2,7 +2,7 @@ #include -#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; } diff --git a/test/CMapEditManagerTest.cpp b/test/CMapEditManagerTest.cpp index 19e6aef5a..1dd4725dc 100644 --- a/test/CMapEditManagerTest.cpp +++ b/test/CMapEditManagerTest.cpp @@ -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("."); - CResourceHandler::get()->addLoader("test/", loader, false); + auto loader = new CFilesystemLoader("test/", "."); + dynamic_cast(CResourceHandler::get())->addLoader(loader, false); const auto originalMap = CMapService::loadMap("test/TerrainViewTest"); auto map = CMapService::loadMap("test/TerrainViewTest"); logGlobal->infoStream() << "Loaded test map successfully.";