ソースを参照

large filesystem update. Filesysytem is now fully functional, everything should work.
- completely replaced CLodHandler, removed bitmaph and spriteh
- replaced CLodStream in favour of CCompressedStream (2 new files)
- renamed CResourceLoaderFactory and ResourceIndetifier to shorter names
NOTES:
- campaign loading is currently broken. Will fix.
- I am going to remove several unused files in several days (e.g. LodHandler)

Ivan Savenko 13 年 前
コミット
b3c17d2788
55 ファイル変更1329 行追加777 行削除
  1. 4 1
      client/BattleInterface/CCreatureAnimation.cpp
  2. 11 10
      client/CAnimation.cpp
  3. 50 86
      client/CBitmapHandler.cpp
  4. 2 25
      client/CBitmapHandler.h
  5. 3 1
      client/CDefHandler.cpp
  6. 19 9
      client/CMT.cpp
  7. 63 67
      client/CPreGame.cpp
  8. 6 6
      client/CPreGame.h
  9. 2 2
      client/Client.cpp
  10. 9 11
      client/Graphics.cpp
  11. 62 0
      config/filesystem.json
  12. 24 24
      config/mainmenu.json
  13. 7 4
      lib/CArtHandler.cpp
  14. 5 3
      lib/CBuildingHandler.cpp
  15. 19 42
      lib/CCampaignHandler.cpp
  16. 4 4
      lib/CCampaignHandler.h
  17. 9 5
      lib/CCreatureHandler.cpp
  18. 0 1
      lib/CCreatureHandler.h
  19. 10 8
      lib/CDefObjInfoHandler.cpp
  20. 0 1
      lib/CDefObjInfoHandler.h
  21. 2 2
      lib/CFileUtility.cpp
  22. 3 4
      lib/CFileUtility.h
  23. 4 1
      lib/CGameState.cpp
  24. 49 40
      lib/CGeneralTextHandler.cpp
  25. 7 4
      lib/CHeroHandler.cpp
  26. 9 12
      lib/CLodHandler.cpp
  27. 27 29
      lib/CLodHandler.h
  28. 1 1
      lib/CMakeLists.txt
  29. 4 4
      lib/CMapInfo.cpp
  30. 3 4
      lib/CMapInfo.h
  31. 0 1
      lib/CObjectHandler.cpp
  32. 5 4
      lib/CSpellHandler.cpp
  33. 162 0
      lib/Filesystem/CCompressedStream.cpp
  34. 102 0
      lib/Filesystem/CCompressedStream.h
  35. 35 12
      lib/Filesystem/CFileInfo.cpp
  36. 10 3
      lib/Filesystem/CFileInfo.h
  37. 28 19
      lib/Filesystem/CFileInputStream.cpp
  38. 9 18
      lib/Filesystem/CFileInputStream.h
  39. 9 11
      lib/Filesystem/CFilesystemLoader.cpp
  40. 7 9
      lib/Filesystem/CFilesystemLoader.h
  41. 1 1
      lib/Filesystem/CInputStream.h
  42. 18 13
      lib/Filesystem/CLodArchiveLoader.cpp
  43. 1 1
      lib/Filesystem/CLodArchiveLoader.h
  44. 26 39
      lib/Filesystem/CLodStream.cpp
  45. 94 91
      lib/Filesystem/CLodStream.h
  46. 163 26
      lib/Filesystem/CResourceLoader.cpp
  47. 173 50
      lib/Filesystem/CResourceLoader.h
  48. 19 8
      lib/Filesystem/ISimpleResourceLoader.cpp
  49. 11 3
      lib/Filesystem/ISimpleResourceLoader.h
  50. 2 2
      lib/Makefile.am
  51. 14 14
      lib/Makefile.in
  52. 7 18
      lib/VCMI_Lib.cpp
  53. 0 2
      lib/VCMI_Lib.h
  54. 9 7
      lib/map.cpp
  55. 6 14
      server/CVCMIServer.cpp

+ 4 - 1
client/BattleInterface/CCreatureAnimation.cpp

@@ -1,6 +1,7 @@
 #include "StdInc.h"
 #include "CCreatureAnimation.h"
 
+#include "../../lib/Filesystem/CResourceLoader.h"
 #include "../../lib/CLodHandler.h"
 #include "../../lib/VCMI_Lib.h"
 #include "../../lib/vcmi_endian.h"
@@ -42,7 +43,9 @@ void CCreatureAnimation::setType(CCreatureAnim::EAnimType type)
 
 CCreatureAnimation::CCreatureAnimation(std::string name) : internalFrame(0), once(false)
 {
-	FDef = spriteh->giveFile(name, FILE_ANIMATION); //load main file
+	//load main file
+	FDef = CResourceHandler::get()->loadData(
+	           ResourceID(std::string("SPRITES/") + name, EResType::ANIMATION)).first.release();
 
 	//init anim data
 	int i,j, totalInBlock;

+ 11 - 10
client/CAnimation.cpp

@@ -1,6 +1,7 @@
 #include "StdInc.h"
 #include <SDL_image.h>
 
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "../lib/CLodHandler.h"
 #include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"
@@ -21,9 +22,6 @@
  *
  */
 
-extern DLL_LINKAGE CLodHandler *spriteh;
-extern DLL_LINKAGE CLodHandler *bitmaph;
-
 typedef std::map <size_t, std::vector <JsonNode> > source_map;
 typedef std::map<size_t, IImage* > image_map;
 typedef std::map<size_t, image_map > group_map;
@@ -90,8 +88,9 @@ CDefFile::CDefFile(std::string Name):
 		{   0,   0,   0, 128},//  50% - shadow body   below selection
 		{   0,   0,   0, 192} // 75% - shadow border below selection
 	};
+	data = CResourceHandler::get()->loadData(
+	           ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION)).first.release();
 
-	data = spriteh->giveFile(Name, FILE_ANIMATION);
 	palette = new SDL_Color[256];
 	int it = 0;
 
@@ -936,13 +935,13 @@ void CAnimation::init(CDefFile * file)
 			source[mapIt->first].resize(mapIt->second);
 	}
 
-	if (spriteh->haveFile(name, FILE_TEXT))
+	ResourceID identifier(std::string("SPRITES/") + name, EResType::TEXT);
+
+	if (CResourceHandler::get()->existsResource(identifier))
 	{
-		int size = 0;
-		ui8 * configFile = spriteh->giveFile(name, FILE_TEXT, &size);
+		auto configFile = CResourceHandler::get()->loadData(identifier);
 
-		const JsonNode config((char*)configFile, size);
-		delete[] configFile;
+		const JsonNode config((char*)configFile.first.get(), configFile.second);
 
 		std::string basepath;
 		basepath = config["basepath"].String();
@@ -977,7 +976,9 @@ void CAnimation::init(CDefFile * file)
 
 CDefFile * CAnimation::getFile() const
 {
-	if (spriteh->haveFile(name, FILE_ANIMATION))
+	ResourceID identifier(std::string("SPRITES/") + name, EResType::ANIMATION);
+
+	if (CResourceHandler::get()->existsResource(identifier))
 		return new CDefFile(name);
 	return NULL;
 }

+ 50 - 86
client/CBitmapHandler.cpp

@@ -1,9 +1,12 @@
 #include "StdInc.h"
 
+#include "../lib/Filesystem/CResourceLoader.h"
+#include "../lib/Filesystem/CFileInfo.h"
 #include "SDL.h"
 #include "SDL_image.h"
 #include "CBitmapHandler.h"
 #include "CDefHandler.h"
+#include "UIFramework/SDL_Extensions.h"
 #include "../lib/CLodHandler.h"
 #include "../lib/vcmi_endian.h"
 
@@ -17,54 +20,37 @@
  *
  */
 
-extern DLL_LINKAGE CLodHandler *bitmaph;
-extern DLL_LINKAGE CLodHandler *bitmaph_ab;
-extern DLL_LINKAGE CLodHandler *spriteh;
-
-void CPCXConv::openPCX(char * PCX, int len)
-{
-	pcxs=len;
-	pcx=(ui8*)PCX;
-}
-void CPCXConv::fromFile(std::string path)
+bool isPCX(const ui8 *header)//check whether file can be PCX according to header
 {
-	std::ifstream is;
-	is.open(path.c_str(),std::ios::binary);
-	is.seekg(0,std::ios::end); // to the end
-	pcxs = is.tellg();  // read length
-	is.seekg(0,std::ios::beg); // wracamy na poczatek
-	pcx = new ui8[pcxs]; // allocate memory 
-	is.read((char*)pcx, pcxs); // read map file to buffer
-	is.close();
+	int fSize  = read_le_u32(header + 0);
+	int width  = read_le_u32(header + 4);
+	int height = read_le_u32(header + 8);
+	return fSize == width*height || fSize == width*height*3;
 }
 
-void CPCXConv::saveBMP(std::string path) const
+enum Epcxformat
 {
-	std::ofstream os;
-	os.open(path.c_str(), std::ios::binary);
-	os.write(reinterpret_cast<const char*>(bmp), bmps);
-	os.close();
-}
+	PCX8B,
+	PCX24B
+};
 
-SDL_Surface * CPCXConv::getSurface() const
+SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size)
 {
 	SDL_Surface * ret;
 
-	int width = -1, height = -1;
 	Epcxformat format;
-	int fSize;
 	int it=0;
 
-	fSize = read_le_u32(pcx + it); it+=4;
-	width = read_le_u32(pcx + it); it+=4;
-	height = read_le_u32(pcx + it); it+=4;
-	
+	ui32 fSize = read_le_u32(pcx + it); it+=4;
+	ui32 width = read_le_u32(pcx + it); it+=4;
+	ui32 height = read_le_u32(pcx + it); it+=4;
+
 	if (fSize==width*height*3)
 		format=PCX24B;
 	else if (fSize==width*height)
 		format=PCX8B;
-	else 
-		return NULL;
+	else
+		return nullptr;
 
 	if (format==PCX8B)
 	{
@@ -77,31 +63,23 @@ SDL_Surface * CPCXConv::getSurface() const
 			it+= width;
 		}
 
-		it = pcxs-256*3;
+		//palette - last 256*3 bytes
+		it = size-256*3;
 		for (int i=0;i<256;i++)
 		{
 			SDL_Color tp;
 			tp.r = pcx[it++];
 			tp.g = pcx[it++];
 			tp.b = pcx[it++];
-			tp.unused = 255;
+			tp.unused = SDL_ALPHA_OPAQUE;
 			ret->format->palette->colors[i] = tp;
 		}
 	}
 	else
 	{
-#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
-		int bmask = 0xff0000;
-		int gmask = 0x00ff00;
-		int rmask = 0x0000ff;
-#else
-		int bmask = 0x0000ff;
-		int gmask = 0x00ff00;
-		int rmask = 0xff0000;
-#endif
-		ret = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, rmask, gmask, bmask, 0);
+		ret = CSDL_Ext::createSurfaceWithBpp<3>(width, height);
 
-		it = 0xC;
+		//it == 0xC;
 		for (int i=0; i<height; i++)
 		{
 			memcpy((char*)ret->pixels + ret->pitch * i, pcx + it, width*3);
@@ -112,64 +90,51 @@ SDL_Surface * CPCXConv::getSurface() const
 	return ret;
 }
 
-bool isPCX(const ui8 *header)//check whether file can be PCX according to 1st 12 bytes
-{
-	int fSize  = read_le_u32(header + 0);
-	int width  = read_le_u32(header + 4);
-	int height = read_le_u32(header + 8);
-	return fSize == width*height || fSize == width*height*3;
-}
-
-SDL_Surface * BitmapHandler::loadBitmapFromLod(CLodHandler *lod, std::string fname, bool setKey)
+SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fname, bool setKey)
 {
 	if(!fname.size())
 	{
 		tlog2 << "Call to loadBitmap with void fname!\n";
 		return NULL;
 	}
-	if (!lod->haveFile(fname, FILE_GRAPHICS))
+	if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE)))
 	{
-		//it is possible that this image will be found in another lod archive. Disabling this warning
-		//tlog2<<"Entry for file "<<fname<<" was not found"<<std::endl;
-		return NULL;
+		return nullptr;
 	}
 
 	SDL_Surface * ret=NULL;
-	int size;
-	ui8 * file = 0;
-	file = lod->giveFile(fname, FILE_GRAPHICS, &size);
+
+	auto readFile = CResourceHandler::get()->loadData(
+	                              ResourceID(path + fname, EResType::IMAGE));
+
 	
-	if (isPCX(file))
+	if (isPCX(readFile.first.get()))
 	{//H3-style PCX
-		CPCXConv cp;
-		cp.openPCX((char*)file,size);
-		ret = cp.getSurface();
-		if (!ret)
-			tlog1<<"Failed to open "<<fname<<" as H3 PCX!\n";
-		if(ret->format->BytesPerPixel == 1  &&  setKey)
+		ret = loadH3PCX(readFile.first.get(), readFile.second);
+		if (ret)
 		{
-			const SDL_Color &c = ret->format->palette->colors[0];
-			SDL_SetColorKey(ret,SDL_SRCCOLORKEY,SDL_MapRGB(ret->format, c.r, c.g, c.b));
+			if(ret->format->BytesPerPixel == 1  &&  setKey)
+			{
+				const SDL_Color &c = ret->format->palette->colors[0];
+				SDL_SetColorKey(ret,SDL_SRCCOLORKEY,SDL_MapRGB(ret->format, c.r, c.g, c.b));
+			}
 		}
+		else
+			tlog1<<"Failed to open "<<fname<<" as H3 PCX!\n";
 	}
 	else
 	{ //loading via SDL_Image
-		std::string filename = lod->getFileName(fname, FILE_GRAPHICS);
-		std::string ext;
-		lod->convertName(filename, &ext);
+		CFileInfo info(CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)));
 
-		if (ext == ".TGA")//Special case - targa can't be loaded by IMG_Load_RW (no magic constants in header)
-		{
-			SDL_RWops *rw = SDL_RWFromMem((void*)file, size);
-			ret = IMG_LoadTGA_RW( rw );
-			SDL_FreeRW(rw);
-		}
-		else
-			ret = IMG_Load_RW( SDL_RWFromMem((void*)file, size), 1);
+		ret = IMG_LoadTyped_RW(
+		          //create SDL_RW with our data (will be deleted by SDL)
+		          SDL_RWFromConstMem((void*)readFile.first.release(), readFile.second),
+		          1, // mark it for auto-deleting
+		          &info.getExtension()[0] + 1); //pass extension without dot (+1 character)
 
 		if (!ret)
 			tlog1<<"Failed to open "<<fname<<" via SDL_Image\n";
-		delete [] file;
+
 		if (ret->format->palette)
 		{
 			//set correct value for alpha\unused channel
@@ -184,9 +149,8 @@ SDL_Surface * BitmapHandler::loadBitmap(std::string fname, bool setKey)
 {
 	SDL_Surface *bitmap;
 
-	if (!(bitmap = loadBitmapFromLod(bitmaph, fname, setKey)) &&
-		!(bitmap = loadBitmapFromLod(bitmaph_ab, fname, setKey)) &&
-		!(bitmap = loadBitmapFromLod(spriteh, fname, setKey)))
+	if (!(bitmap = loadBitmapFromDir("DATA/", fname, setKey)) &&
+		!(bitmap = loadBitmapFromDir("SPRITES/", fname, setKey)))
 		tlog0<<"Error: Failed to find file "<<fname<<"\n";
 
 	return bitmap;

+ 2 - 25
client/CBitmapHandler.h

@@ -3,7 +3,6 @@
 
 
 struct SDL_Surface;
-class CLodHandler;
 
 /*
  * CBitmapHandler.h, part of VCMI engine
@@ -15,39 +14,17 @@ class CLodHandler;
  *
  */
 
-enum Epcxformat {PCX8B, PCX24B};
-
 /// Struct which stands for a simple rgba palette
 struct BMPPalette
 {
 	ui8 R,G,B,F;
 };
 
-/// Class which converts pcx to bmp images
-class CPCXConv
-{	
-public:
-	ui8 * pcx, *bmp;
-	int pcxs, bmps;
-	void fromFile(std::string path);
-	void saveBMP(std::string path) const;
-	void openPCX(char * PCX, int len);
-	SDL_Surface * getSurface() const; //for standard H3 PCX
-	//SDL_Surface * getSurfaceZ(); //for ZSoft PCX
-	CPCXConv() //c-tor
-	: pcx(NULL), bmp(NULL), pcxs(0), bmps(0)
-	{}
-	~CPCXConv() //d-tor
-	{
-		if (pcxs) delete[] pcx;
-		if (bmps) delete[] bmp;
-	}
-};
-
 namespace BitmapHandler
 {
+	SDL_Surface * loadH3PCX(ui8 * data, size_t size);
 	//Load file from specific LOD
-	SDL_Surface * loadBitmapFromLod(CLodHandler *lod, std::string fname, bool setKey=true);
+	SDL_Surface * loadBitmapFromDir(std::string path, std::string fname, bool setKey=true);
 	//Load file from any LODs
 	SDL_Surface * loadBitmap(std::string fname, bool setKey=true);
 };

+ 3 - 1
client/CDefHandler.cpp

@@ -2,6 +2,7 @@
 #include "SDL.h"
 #include "CDefHandler.h"
 
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "../lib/CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/vcmi_endian.h"
@@ -363,7 +364,8 @@ CDefEssential * CDefHandler::essentialize()
 
 CDefHandler * CDefHandler::giveDef(const std::string & defName)
 {
-	ui8 * data = spriteh->giveFile(defName, FILE_ANIMATION);
+	ui8 * data = CResourceHandler::get()->loadData(
+	                 ResourceID(std::string("SPRITES/") + defName, EResType::ANIMATION)).first.release();
 	if(!data)
 		throw std::runtime_error("bad def name!");
 	CDefHandler * nh = new CDefHandler();

+ 19 - 9
client/CMT.cpp

@@ -7,6 +7,7 @@
 #include "CGameInfo.h"
 #include "mapHandler.h"
 
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "CPreGame.h"
 #include "CCastleInterface.h"
 #include "../lib/CConsoleHandler.h"
@@ -476,16 +477,25 @@ void processCommand(const std::string &message)
 	}*/
 	else if(message=="get txt")
 	{
+		tlog0<<"Command accepted.\t";
 		boost::filesystem::create_directory("Extracted_txts");
-		tlog0<<"Command accepted. Opening .lod file...\t";
-		CLodHandler * txth = new CLodHandler;
-		txth->init(GameConstants::DATA_DIR + "/Data/H3bitmap.lod","");
-		tlog0<<"done.\nScanning .lod file\n";
-
-		BOOST_FOREACH(Entry e, txth->entries)
-			if( e.type == FILE_TEXT )
-				txth->extractFile(std::string(GVCMIDirs.UserPath + "/Extracted_txts/")+e.name, e.name, FILE_TEXT);
-		tlog0<<"\rExtracting done :)\n";
+		auto iterator = CResourceHandler::get()->getIterator([](const ResourceID & ident)
+		{
+			return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
+		});
+
+		std::string basePath = CResourceHandler::get()->getResourceName(std::string("DATA")) + "/Extracted_txts/";
+		while (iterator.hasNext())
+		{
+			std::ofstream file(basePath + iterator->getName() + ".TXT");
+			auto text = CResourceHandler::get()->loadData(*iterator);
+
+			file.write((char*)text.first.get(), text.second);
+			++iterator;
+		}
+
+		tlog0 << "\rExtracting done :)\n";
+		tlog0 << " Extracted files can be found in " << basePath << " directory\n";
 	}
 	else if(cn=="crash")
 	{

+ 63 - 67
client/CPreGame.cpp

@@ -1,7 +1,10 @@
 #include "StdInc.h"
 #include "CPreGame.h"
 
-#include <zlib.h>
+#include "../lib/Filesystem/CResourceLoader.h"
+#include "../lib/Filesystem/CFileInfo.h"
+#include "../lib/Filesystem/CCompressedStream.h"
+
 #include "../lib/CStopWatch.h"
 #include "UIFramework/SDL_Extensions.h"
 #include "CGameInfo.h"
@@ -385,7 +388,8 @@ CreditsScreen::CreditsScreen()
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	pos.w = CGP->menu->pos.w;
 	pos.h = CGP->menu->pos.h;
-	std::string text = bitmaph->getTextFile("CREDITS");
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREDITS.TXT"));
+	std::string text((char*)textFile.first.get(), textFile.second);
 	size_t firstQuote = text.find('\"')+1;
 	text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote );
 	credits = new CTextBox(text, Rect(pos.w - 350, 600, 350, 32000), 0, FONT_CREDITS, CENTER, Colors::Cornsilk);
@@ -767,7 +771,7 @@ void CSelectionScreen::changeSelection( const CMapInfo *to )
 	   SEL->sInfo.difficulty = to->scenarioOpts->difficulty;
 	if(screenType != CMenuScreen::campaignList)
 	{
-		updateStartInfo(to ? to->filename : "", sInfo, to ? to->mapHeader : NULL);
+		updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader : NULL);
 	}
 	card->changeSelection(to);
 	if(screenType != CMenuScreen::campaignList)
@@ -789,7 +793,7 @@ void CSelectionScreen::startCampaign()
 {
 	if (SEL->current)
 	{
-		CCampaign * ourCampaign = CCampaignHandler::getCampaign(SEL->current->filename, SEL->current->lodCmpgn);
+		CCampaign * ourCampaign = CCampaignHandler::getCampaign(SEL->current->fileURI);
 		CCampaignState * campState = new CCampaignState();
 		campState->camp = ourCampaign;
 		GH.pushInt( new CBonusSelection(campState) );
@@ -1021,42 +1025,56 @@ void SelectionTab::filter( int size, bool selectFirst )
 	}
 }
 
-void SelectionTab::getFiles(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext)
+std::vector<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType)
 {
-	CFileUtility::getFilesWithExt(out, dirname, ext);
-	allItems.resize(out.size());
+	std::vector<ResourceID> ret;
+	boost::to_upper(dirURI);
+
+	auto iterator = CResourceHandler::get()->getIterator([&](const ResourceID & ident)
+	{
+		return ident.getType() == resType
+		    && boost::algorithm::starts_with(ident.getName(), dirURI);
+	});
+
+	while (iterator.hasNext())
+	{
+		ret.push_back(*iterator);
+		++iterator;
+	}
+
+	allItems.resize(ret.size());
+	return ret;
 }
 
-void SelectionTab::parseMaps(std::vector<FileInfo> &files, int start, int threads)
+void SelectionTab::parseMaps(const std::vector<ResourceID> &files, int start, int threads)
 {
-	int read=0;
 	ui8 mapBuffer[1500];
 
 	while(start < allItems.size())
 	{
-		gzFile tempf = gzopen(files[start].name.c_str(),"rb");
-		read = gzread(tempf, mapBuffer, 1500);
-		gzclose(tempf);
+		auto compressed = CResourceHandler::get()->load(files[start]);
+		CCompressedStream stream(compressed, true);
+		int read = stream.read(mapBuffer, 1500);
+
 		if(read < 50  ||  !mapBuffer[4])
 		{
-			tlog3 << "\t\tWarning: corrupted map file: " << files[start].name << std::endl;
+			tlog3 << "\t\tWarning: corrupted map file: " << files[start].getName() << std::endl;
 		}
 		else //valid map
 		{
-			allItems[start].mapInit(files[start].name, mapBuffer);
-			//allItems[start].date = "DATEDATE";// files[start].date;
+			allItems[start].mapInit(files[start].getName(), mapBuffer);
 		}
 		start += threads;
 	}
 }
 
-void SelectionTab::parseGames(std::vector<FileInfo> &files, bool multi)
+void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
 {
 	for(int i=0; i<files.size(); i++)
 	{
 		try
 		{
-			CLoadFile lf(files[i].name);
+			CLoadFile lf(CResourceHandler::get()->getResourceName(files[i]));
 
 			ui8 sign[8];
 			lf >> sign;
@@ -1065,9 +1083,10 @@ void SelectionTab::parseGames(std::vector<FileInfo> &files, bool multi)
 
 			allItems[i].mapHeader = new CMapHeader();
 			lf >> *(allItems[i].mapHeader) >> allItems[i].scenarioOpts;
-			allItems[i].filename = files[i].name;
+			allItems[i].fileURI = files[i].getName();
 			allItems[i].countPlayers();
-			allItems[i].date = std::asctime(std::localtime(&files[i].date));
+			std::time_t time = CFileInfo(CResourceHandler::get()->getResourceName(files[i])).getDate();
+			allItems[i].date = std::asctime(std::localtime(&time));
 
 			if((allItems[i].actualHumanPlayers > 1) != multi) //if multi mode then only multi games, otherwise single
 			{
@@ -1077,18 +1096,17 @@ void SelectionTab::parseGames(std::vector<FileInfo> &files, bool multi)
 		catch(std::exception &e)
 		{
 			vstd::clear_pointer(allItems[i].mapHeader);
-			tlog3 << "Failed to process " << files[i].name <<": " << e.what() << std::endl;
+			tlog3 << "Failed to process " << files[i].getName() <<": " << e.what() << std::endl;
 		}
 	}
 }
 
-void SelectionTab::parseCampaigns( std::vector<FileInfo> & files )
+void SelectionTab::parseCampaigns(const std::vector<ResourceID> & files )
 {
 	for(int i=0; i<files.size(); i++)
 	{
 		//allItems[i].date = std::asctime(std::localtime(&files[i].date));
-		allItems[i].filename = files[i].name;
-		allItems[i].lodCmpgn = files[i].inLod;
+		allItems[i].fileURI = files[i].getName();
 		allItems[i].campaignInit();
 	}
 }
@@ -1122,25 +1140,17 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 	}
 	else
 	{
-		std::vector<FileInfo> toParse;
 		std::vector<CCampaignHeader> cpm;
 		switch(tabType)
 		{
 		case CMenuScreen::newGame:
-			getFiles(toParse, GameConstants::DATA_DIR + "/Maps", "h3m"); //get all maps
-			/* Load maps from user directory too, unless it is also the
-			 * same as the data directory (as is the case on
-			 * windows). */
-			if (GVCMIDirs.UserPath != GameConstants::DATA_DIR)
-				getFiles(toParse, GVCMIDirs.UserPath + "/Maps", "h3m"); //get all maps
-			parseMaps(toParse);
+			parseMaps(getFiles("Maps/", EResType::MAP));
 			positions = 18;
 			break;
 
 		case CMenuScreen::loadGame:
 		case CMenuScreen::saveGame:
-			getFiles(toParse, GVCMIDirs.UserPath + "/Games", "vlgm1"); //get all saves
-			parseGames(toParse, MultiPlayer);
+			parseGames(getFiles("Saves/", EResType::LIB_SAVEGAME), MultiPlayer);
 			if(tabType == CMenuScreen::loadGame)
 			{
 				positions = 18;
@@ -1155,32 +1165,9 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 				txt->filters.add(CTextInput::filenameFilter);
 			}
 			break;
+
 		case CMenuScreen::campaignList:
-			getFiles(toParse, GameConstants::DATA_DIR + "/Maps", "h3c"); //get all campaigns
-			/* Load campaingns from user directory too, unless it is also the
-			 * same as the data directory (as is the case on
-			 * windows). */
-			if (GVCMIDirs.UserPath != GameConstants::DATA_DIR)
-				getFiles(toParse, GVCMIDirs.UserPath + "/Maps", "h3c"); //get all maps
-
-			for (int g=0; g<toParse.size(); ++g)
-			{
-				toParse[g].inLod = false;
-			}
-			//add lod cmpgns
-			cpm = CCampaignHandler::getCampaignHeaders(CCampaignHandler::Custom);
-			for (int g = 0; g < cpm.size(); g++)
-			{
-				FileInfo fi;
-				fi.inLod = cpm[g].loadFromLod;
-				fi.name = cpm[g].filename;
-				if (cpm[g].loadFromLod)
-				{
-					toParse.push_back(fi);
-					allItems.push_back(CMapInfo(false));
-				}
-			}
-			parseCampaigns(toParse);
+			parseCampaigns(getFiles("Maps/", EResType::CAMPAIGN));
 			positions = 18;
 			break;
 
@@ -1229,7 +1216,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 	switch(tabType)
 	{
 	case CMenuScreen::newGame:
-		selectFName(GameConstants::DATA_DIR + "/Maps/Arrogance.h3m");
+		selectFName("Maps/Arrogance");
 		break;
 	case CMenuScreen::loadGame:
 	case CMenuScreen::campaignList:
@@ -1296,7 +1283,11 @@ void SelectionTab::select( int position )
 		slider->moveTo(slider->value + position - positions + 1);
 
 	if(txt)
-		txt->setTxt(fs::basename(curItems[py]->filename));
+	{
+		std::string filename = CResourceHandler::get()->getResourceName(
+		                           ResourceID(curItems[py]->fileURI, EResType::MAP));
+		txt->setTxt(CFileInfo(filename).getBaseName());
+	}
 
 	onSelect(curItems[py]);
 }
@@ -1348,7 +1339,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 		{
 			//amount of players
 			std::ostringstream ostr(std::ostringstream::out);
-			ostr << currentItem->playerAmnt << "/" << currentItem->humenPlayers;
+			ostr << currentItem->playerAmnt << "/" << currentItem->humanPlayers;
 			CSDL_Ext::printAt(ostr.str(), POS(29, 120), FONT_SMALL, itemColor, to);
 
 			//map size
@@ -1387,7 +1378,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 				break;
 			default:
 				// Unknown version. Be safe and ignore that map
-				tlog2 << "Warning: " << currentItem->filename << " has wrong version!\n";
+				tlog2 << "Warning: " << currentItem->fileURI << " has wrong version!\n";
 				continue;
 			}
 			blitAt(format->ourImages[temp].bitmap, POS(88, 117), to);
@@ -1427,7 +1418,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 		}
 		else
 		{
-			name = fs::basename(currentItem->filename);
+			name = fs::basename(currentItem->fileURI);
 		}
 
 		//print name
@@ -1528,11 +1519,12 @@ int SelectionTab::getLine()
 	return line;
 }
 
-void SelectionTab::selectFName( const std::string &fname )
+void SelectionTab::selectFName( std::string fname )
 {
+	boost::to_upper(fname);
 	for(int i = curItems.size() - 1; i >= 0; i--)
 	{
-		if(curItems[i]->filename == fname)
+		if(curItems[i]->fileURI == fname)
 		{
 			slider->moveTo(i);
 			selectAbs(i);
@@ -3553,7 +3545,7 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode &config )
 
 	status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
 
-	CCampaignHeader header = CCampaignHandler::getHeader(campFile, true);
+	CCampaignHeader header = CCampaignHandler::getHeader(campFile);
 	hoverText = header.name;
 
 	if (status != CCampaignScreen::DISABLED)
@@ -3571,11 +3563,15 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode &config )
 
 void CCampaignScreen::CCampaignButton::clickLeft(tribool down, bool previousState)
 {
+	// Campaign screen is broken. Disabled for now
+	return;
+
 	if (down)
 	{
 		// Close running video and open the selected campaign
 		CCS->videoh->close();
-		CCampaign * ourCampaign = CCampaignHandler::getCampaign(campFile, true);
+
+		CCampaign * ourCampaign = CCampaignHandler::getCampaign(campFile);
 		CCampaignState * campState = new CCampaignState();
 		campState->camp = ourCampaign;
 		GH.pushInt( new CBonusSelection(campState) );

+ 6 - 6
client/CPreGame.h

@@ -1,6 +1,6 @@
 #pragma once
 
-
+#include "../lib/Filesystem/CResourceLoader.h"
 #include <SDL.h>
 #include "../lib/StartInfo.h"
 #include "GUIClasses.h"
@@ -143,10 +143,10 @@ class SelectionTab : public CIntObject
 private:
 	CDefHandler *format; //map size
 
-	void parseMaps(std::vector<FileInfo> &files, int start = 0, int threads = 1);
-	void parseGames(std::vector<FileInfo> &files, bool multi);
-	void parseCampaigns( std::vector<FileInfo> & files );
-	void getFiles(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext);
+	void parseMaps(const std::vector<ResourceID> &files, int start = 0, int threads = 1);
+	void parseGames(const std::vector<ResourceID> &files, bool multi);
+	void parseCampaigns(const std::vector<ResourceID> & files );
+	std::vector<ResourceID> getFiles(std::string dirURI, int resType);
 	CMenuScreen::EState tabType;
 public:
 	int positions; //how many entries (games/maps) can be shown
@@ -172,7 +172,7 @@ public:
 	void sort();
 	void printMaps(SDL_Surface *to);
 	int getLine();
-	void selectFName(const std::string &fname);
+	void selectFName(std::string fname);
 
 	void showAll(SDL_Surface * to);
 	void clickLeft(tribool down, bool previousState);

+ 2 - 2
client/Client.cpp

@@ -236,7 +236,7 @@ void CClient::loadGame( const std::string & fname )
 		const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
 		StartInfo *si;
 
-		CLoadFile lf(fname + ".vlgm1");
+		CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
 		lf >> sig >> dum >> si;
 		tlog0 <<"Reading save signature: "<<tmh.getDiff()<<std::endl;
 		
@@ -277,7 +277,7 @@ void CClient::loadGame( const std::string & fname )
 	tlog0 <<"Sent info to server: "<<tmh.getDiff()<<std::endl;
 	
 	{
-		CLoadFile lf(fname + ".vcgm1");
+		CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)));
 		lf >> *this;
 	}
 }

+ 9 - 11
client/Graphics.cpp

@@ -1,6 +1,7 @@
 #include "StdInc.h"
 #include "Graphics.h"
 
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "CDefHandler.h"
 #include "UIFramework/SDL_Extensions.h"
 #include <SDL_ttf.h>
@@ -45,7 +46,9 @@ Graphics * graphics = NULL;
 
 void Graphics::loadPaletteAndColors()
 {
-	std::string pals = bitmaph->getTextFile("PLAYERS.PAL", FILE_OTHER);
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/PLAYERS.PAL"));
+	std::string pals((char*)textFile.first.get(), textFile.second);
+
 	playerColorPalette = new SDL_Color[256];
 	neutralColor = new SDL_Color;
 	playerColors = new SDL_Color[GameConstants::PLAYER_LIMIT];
@@ -518,22 +521,17 @@ void Graphics::loadTrueType()
 
 Font * Graphics::loadFont( const char * name )
 {
-	int len = 0;
-	ui8 * hlp = bitmaph->giveFile(name, FILE_FONT, &len);
-	if(!hlp || !len)
+	ui8 * hlp = CResourceHandler::get()->loadData(
+	                ResourceID(std::string("DATA/") + name, EResType::FONT)).first.release();
+	if(!hlp)
 	{
 		tlog1 << "Error: cannot load font: " << name << std::endl;
 		return NULL;
 	}
 
 	int magic =  SDL_SwapLE32(*(const Uint32*)hlp);
-	if(len < 10000  || (magic != 589598 && magic != 589599))
-	{
-		tlog1 << "Suspicious font file (length " << len <<", fname " << name << "), logging to suspicious_" << name << ".fnt\n";
-		std::string suspFName = "suspicious_" + std::string(name) + ".fnt";
-		std::ofstream o(suspFName.c_str());
-		o.write((const char*)hlp, len);
-	}
+	if(magic != 589598 && magic != 589599)
+		tlog1 << "Suspicious font file, fname " << name << "n";
 
 	Font *ret = new Font(hlp);
 	return ret;

+ 62 - 0
config/filesystem.json

@@ -0,0 +1,62 @@
+{
+	// 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"
+	"filesystem":
+	{
+		"DATA/" :
+		[
+			{"type" : "file", "path" : "ALL/Data/H3ab_bmp.lod"},
+			{"type" : "file", "path" : "ALL/Data/H3bitmap.lod"},
+			{"type" : "dir",  "path" : "GLOBAL/Data"},
+			{"type" : "dir",  "path" : "LOCAL/Data"}
+		],
+		"SPRITES/":
+		[
+			{"type" : "file", "path" : "ALL/Data/H3ab_spr.lod"},
+			{"type" : "file", "path" : "ALL/Data/H3sprite.lod"},
+			{"type" : "dir",  "path" : "GLOBAL/Sprites"},
+			{"type" : "dir",  "path" : "LOCAL/Sprites"}
+		],
+		"SOUNDS/":
+		[
+			{"type" : "file", "path" : "ALL/Data/H3ab_ahd.snd"},
+			{"type" : "file", "path" : "ALL/Data/Heroes3.snd"},
+			//WoG have overriden sounds with .82m extension in Data
+			{"type" : "dir",  "path" : "GLOBAL/Data"},
+			{"type" : "dir",  "path" : "LOCAL/Data"}
+		],
+		"MUSIC/":
+		[
+			{"type" : "dir",  "path" : "GLOBAL/Mp3"},
+			{"type" : "dir",  "path" : "LOCAL/Mp3"}
+		],
+		"VIDEO/":
+		[
+			{"type" : "file", "path" : "ALL/Data/H3ab_ahd.vid"},
+			{"type" : "file", "path" : "ALL/Data/H3ab_ahd.vid"},
+			// Location of video files in linux release
+			{"type" : "dir",  "path" : "GLOBAL/Data/Video"},
+			{"type" : "dir",  "path" : "LOCAL/Data/Video"}
+		],
+		"CONFIG/":
+		[
+			{"type" : "dir",  "path" : "GLOBAL/Config"},
+			{"type" : "dir",  "path" : "LOCAL/Config"}
+		],
+		"MAPS/":
+		[
+			{"type" : "dir",  "path" : "GLOBAL/Maps"},
+			{"type" : "dir",  "path" : "LOCAL/Maps"}
+		],
+		"SAVES/":
+		[
+			{"type" : "dir",  "path" : "LOCAL/Games"},
+		]
+	}
+}

+ 24 - 24
config/mainmenu.json

@@ -65,13 +65,13 @@
 			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
 			"items":
 			[
-				{ "x":90,  "y":72,  "file":"GOOD1.H3C",    "image":"CAMPGD1S", "video":"CGOOD1",   "open": true },
-				{ "x":539, "y":72,  "file":"EVIL1.H3C",    "image":"CAMPEV1S", "video":"CEVIL1",   "open": true },
-				{ "x":43,  "y":245, "file":"GOOD2.H3C",    "image":"CAMPGD2S", "video":"CGOOD2",   "open": true },
-				{ "x":313, "y":244, "file":"NEUTRAL.H3C",  "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true },
-				{ "x":586, "y":246, "file":"EVIL2.H3C",    "image":"CAMPEV2S", "video":"CEVIL2",   "open": true },
-				{ "x":34,  "y":417, "file":"GOOD3.H3C",    "image":"CAMPGD3S", "video":"CGOOD3",   "open": true },
-				{ "x":404, "y":414, "file":"SECRET.H3C",   "image":"CAMPSCTS", "video":"CSECRET",  "open": true }
+				{ "x":90,  "y":72,  "file":"DATA/GOOD1.H3C",    "image":"CAMPGD1S", "video":"CGOOD1",   "open": true },
+				{ "x":539, "y":72,  "file":"DATA/EVIL1.H3C",    "image":"CAMPEV1S", "video":"CEVIL1",   "open": true },
+				{ "x":43,  "y":245, "file":"DATA/GOOD2.H3C",    "image":"CAMPGD2S", "video":"CGOOD2",   "open": true },
+				{ "x":313, "y":244, "file":"DATA/NEUTRAL.H3C",  "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true },
+				{ "x":586, "y":246, "file":"DATA/EVIL2.H3C",    "image":"CAMPEV2S", "video":"CEVIL2",   "open": true },
+				{ "x":34,  "y":417, "file":"DATA/GOOD3.H3C",    "image":"CAMPGD3S", "video":"CGOOD3",   "open": true },
+				{ "x":404, "y":414, "file":"DATA/SECRET.H3C",   "image":"CAMPSCTS", "video":"CSECRET",  "open": true }
 			]
 		},
 		{
@@ -85,12 +85,12 @@
 			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
 			"items":
 			[
-				{ "x":90,  "y":72,  "file":"AB.H3C",       "image":"CAMP1AB7", "video":"C1ab7", "open": true },
-				{ "x":539, "y":72,  "file":"BLOOD.H3C",    "image":"CAMP1DB2", "video":"C1db2", "open": true },
-				{ "x":43,  "y":245, "file":"SLAYER.H3C",   "image":"CAMP1DS1", "video":"C1ds1", "open": true },
-				{ "x":313, "y":244, "file":"FESTIVAL.H3C", "image":"CAMP1FL3", "video":"C1fl3", "open": true },
-				{ "x":586, "y":246, "file":"FIRE.H3C",     "image":"CAMP1PF2", "video":"C1pf2", "open": true },
-				{ "x":34,  "y":417, "file":"FOOL.H3C",     "image":"CAMP1FW1", "video":"C1fw1", "open": true }
+				{ "x":90,  "y":72,  "file":"DATA/AB.H3C",       "image":"CAMP1AB7", "video":"C1ab7", "open": true },
+				{ "x":539, "y":72,  "file":"DATA/BLOOD.H3C",    "image":"CAMP1DB2", "video":"C1db2", "open": true },
+				{ "x":43,  "y":245, "file":"DATA/SLAYER.H3C",   "image":"CAMP1DS1", "video":"C1ds1", "open": true },
+				{ "x":313, "y":244, "file":"DATA/FESTIVAL.H3C", "image":"CAMP1FL3", "video":"C1fl3", "open": true },
+				{ "x":586, "y":246, "file":"DATA/FIRE.H3C",     "image":"CAMP1PF2", "video":"C1pf2", "open": true },
+				{ "x":34,  "y":417, "file":"DATA/FOOL.H3C",     "image":"CAMP1FW1", "video":"C1fw1", "open": true }
 			]
 
 		},
@@ -100,13 +100,13 @@
 			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
 			"items":
 			[
-				{ "x":90,  "y":72,  "file":"GEM.H3C",      "image":"CAMPNB1", "video":"NEW",     "open": true },
-				{ "x":539, "y":72,  "file":"GELU.H3C",     "image":"CAMPEL1", "video":"ELIXIR",  "open": true },
-				{ "x":43,  "y":245, "file":"CRAG.H3C",     "image":"CAMPHS1", "video":"HACK",    "open": true },
-				{ "x":313, "y":244, "file":"SANDRO.H3C",   "image":"CAMPRN1", "video":"RISE",    "open": true },
-				{ "x":586, "y":246, "file":"YOG.H3C",      "image":"CAMPBB1", "video":"BIRTH",   "open": true },
-				{ "x":34,  "y":417, "file":"FINAL.H3C",    "image":"CAMPUA1", "video":"UNHOLY",  "open": true },
-				{ "x":404, "y":414, "file":"SECRET1.H3C",  "image":"CAMPSP1", "video":"SPECTRE", "open": true }
+				{ "x":90,  "y":72,  "file":"DATA/GEM.H3C",      "image":"CAMPNB1", "video":"NEW",     "open": true },
+				{ "x":539, "y":72,  "file":"DATA/GELU.H3C",     "image":"CAMPEL1", "video":"ELIXIR",  "open": true },
+				{ "x":43,  "y":245, "file":"DATA/CRAG.H3C",     "image":"CAMPHS1", "video":"HACK",    "open": true },
+				{ "x":313, "y":244, "file":"DATA/SANDRO.H3C",   "image":"CAMPRN1", "video":"RISE",    "open": true },
+				{ "x":586, "y":246, "file":"DATA/YOG.H3C",      "image":"CAMPBB1", "video":"BIRTH",   "open": true },
+				{ "x":34,  "y":417, "file":"DATA/FINAL.H3C",    "image":"CAMPUA1", "video":"UNHOLY",  "open": true },
+				{ "x":404, "y":414, "file":"DATA/SECRET1.H3C",  "image":"CAMPSP1", "video":"SPECTRE", "open": true }
 			]
 
 		},
@@ -116,10 +116,10 @@
 			"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
 			"items":
 			[
-				{ "x":90,  "y":72,  "file":"ZC1.H3C", "image":"CAMPZ01", "open": true},
-				{ "x":539, "y":72,  "file":"ZC2.H3C", "image":"CAMPZ02", "open": true},
-				{ "x":43,  "y":245, "file":"ZC3.H3C", "image":"CAMPZ03", "open": true},
-				{ "x":311, "y":242, "file":"ZC4.H3C", "image":"CAMPZ04", "open": true}
+				{ "x":90,  "y":72,  "file":"DATA/ZC1.H3C", "image":"CAMPZ01", "open": true},
+				{ "x":539, "y":72,  "file":"DATA/ZC2.H3C", "image":"CAMPZ02", "open": true},
+				{ "x":43,  "y":245, "file":"DATA/ZC3.H3C", "image":"CAMPZ03", "open": true},
+				{ "x":311, "y":242, "file":"DATA/ZC4.H3C", "image":"CAMPZ04", "open": true}
 			]
 
 		}

+ 7 - 4
lib/CArtHandler.cpp

@@ -1,16 +1,16 @@
 #include "StdInc.h"
 #include "CArtHandler.h"
 
+#include "Filesystem/CResourceLoader.h"
 #include "CLodHandler.h"
 #include "CGeneralTextHandler.h"
 #include <boost/random/linear_congruential.hpp>
-#include "../lib/VCMI_Lib.h"
+#include "VCMI_Lib.h"
 #include "CSpellHandler.h"
 #include "CObjectHandler.h"
 #include "NetPacks.h"
-#include "../lib/JsonNode.h"
+#include "JsonNode.h"
 
-extern CLodHandler *bitmaph;
 using namespace boost::assign;
 
 /*
@@ -239,7 +239,10 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 	growingArtifacts += 146, 147, 148, 150, 151, 152, 153;
 	static std::map<char, CArtifact::EartClass> classes = 
 	  map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC);
-	std::string buf = bitmaph->getTextFile("ARTRAITS.TXT"), dump, pom;
+
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/ARTRAITS.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
+	std::string dump, pom;
 	int it=0;
 	for(int i=0; i<2; ++i)
 	{

+ 5 - 3
lib/CBuildingHandler.cpp

@@ -3,12 +3,11 @@
 
 #include "CGeneralTextHandler.h"
 #include "CLodHandler.h"
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/JsonNode.h"
 #include "GameConstants.h"
 
-extern CLodHandler * bitmaph;
-
 /*
  * CBuildingHandler.cpp, part of VCMI engine
  *
@@ -44,7 +43,10 @@ static CBuilding * readBg(std::string &buf, int& it)
 }
 void CBuildingHandler::loadBuildings()
 {
-	std::string buf = bitmaph->getTextFile("BUILDING.TXT"), temp;
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/BUILDING.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
+
+	std::string temp;
 	int it=0; //buf iterator
 
 	temp = readTo(buf,it,'\n');temp = readTo(buf,it,'\n');//read 2 lines of file info

+ 19 - 42
lib/CCampaignHandler.cpp

@@ -1,6 +1,8 @@
 #include "StdInc.h"
 #include "CCampaignHandler.h"
 
+#include "Filesystem/CResourceLoader.h"
+#include "Filesystem/CCompressedStream.h"
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/vcmi_endian.h"
@@ -35,7 +37,7 @@ std::vector<CCampaignHeader> CCampaignHandler::getCampaignHeaders(GetMode mode)
 		tlog1 << "Cannot find " << dirname << " directory!\n";
 	}
 
-	if (mode == Custom || mode == ALL) //add custom campaigns
+	if (mode == Custom) //add custom campaigns
 	{
 		fs::path tie(dirname);
 		fs::directory_iterator end_iter;
@@ -44,51 +46,38 @@ std::vector<CCampaignHeader> CCampaignHandler::getCampaignHeaders(GetMode mode)
 			if(fs::is_regular_file(file->status())
 				&& boost::ends_with(file->path().filename().string(), ext))
 			{
-				ret.push_back( getHeader( file->path().string(), false ) );
-			}
-		}
-	}
-	if (mode == ALL) //add all lod campaigns
-	{
-		BOOST_FOREACH(Entry e, bitmaph->entries)
-		{
-			if( e.type == FILE_CAMPAIGN )
-			{
-				ret.push_back( getHeader(e.name, true) );
+				ret.push_back( getHeader( file->path().string()) );
 			}
 		}
 	}
 
-
 	return ret;
 }
 
-CCampaignHeader CCampaignHandler::getHeader( const std::string & name, bool fromLod )
+CCampaignHeader CCampaignHandler::getHeader( const std::string & name)
 {
-	int realSize;
-	ui8 * cmpgn = getFile(name, fromLod, realSize);
+	ui8 * cmpgn = getFile(name).first;
 
 	int it = 0;//iterator for reading
 	CCampaignHeader ret = readHeaderFromMemory(cmpgn, it);
 	ret.filename = name;
-	ret.loadFromLod = fromLod;
 
 	delete [] cmpgn;
 
 	return ret;
 }
 
-CCampaign * CCampaignHandler::getCampaign( const std::string & name, bool fromLod )
+CCampaign * CCampaignHandler::getCampaign( const std::string & name)
 {
 	CCampaign * ret = new CCampaign();
 
-	int realSize;
-	ui8 * cmpgn = getFile(name, fromLod, realSize);
+	auto file = getFile(name);
+	int realSize = file.second;
+	ui8 * cmpgn = file.first;
 
 	int it = 0; //iterator for reading
 	ret->header = readHeaderFromMemory(cmpgn, it);
 	ret->header.filename = name;
-	ret->header.loadFromLod = fromLod;
 
 	int howManyScenarios = VLC->generaltexth->campaignRegionNames[ret->header.mapVersion].size();
 	for(int g=0; g<howManyScenarios; ++g)
@@ -418,24 +407,15 @@ bool CCampaignHandler::startsAt( const ui8 * buffer, int size, int pos )
 	return true;
 }
 
-ui8 * CCampaignHandler::getFile( const std::string & name, bool fromLod, int & outSize )
+std::pair<ui8 *, size_t> CCampaignHandler::getFile(const std::string & name)
 {
-	ui8 * cmpgn = 0;
-	if(fromLod)
-	{
-		if (bitmaph->haveFile(name, FILE_CAMPAIGN))
-			cmpgn = bitmaph->giveFile(name, FILE_CAMPAIGN, &outSize);
-		else if (bitmaph_ab->haveFile(name, FILE_CAMPAIGN))
-			cmpgn = bitmaph_ab->giveFile(name, FILE_CAMPAIGN, &outSize);
-		else
-			tlog1 << "Cannot find file: " << name << std::endl;
-		cmpgn = CLodHandler::getUnpackedData(cmpgn, outSize, &outSize);
-	}
-	else
-	{
-		cmpgn = CLodHandler::getUnpackedFile(name, &outSize);
-	}
-	return cmpgn;
+	std::unique_ptr<CInputStream> stream = CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN));
+	stream.reset(new CCompressedStream(stream, true));
+
+	ui8 * ret = new ui8[stream->getSize()];
+	stream->read(ret, stream->getSize());
+
+	return std::make_pair(ret, stream->getSize());
 }
 
 bool CCampaign::conquerable( int whichScenario ) const
@@ -567,10 +547,7 @@ void CCampaignState::initNewCampaign( const StartInfo &si )
 	campaignName = si.mapname;
 	currentMap = si.whichMapInCampaign;
 
-	//check if campaign is in lod or not
-	bool inLod = campaignName.find('/') == std::string::npos;
-
-	camp = CCampaignHandler::getCampaign(campaignName, inLod); //TODO lod???
+	camp = CCampaignHandler::getCampaign(campaignName);
 	for (ui8 i = 0; i < camp->mapPieces.size(); i++)
 		mapsRemaining.push_back(i);
 }

+ 4 - 4
lib/CCampaignHandler.h

@@ -162,11 +162,11 @@ class DLL_LINKAGE CCampaignHandler
 	static CScenarioTravel readScenarioTravelFromMemory( const ui8 * buffer, int & outIt , int version);
 	static std::vector<ui32> locateH3mStarts(const ui8 * buffer, int start, int size);
 	static bool startsAt( const ui8 * buffer, int size, int pos ); //a simple heuristic that checks if a h3m starts at given pos
-	static ui8 * getFile(const std::string & name, bool fromLod, int & outSize);
+	static std::pair<ui8 *, size_t> getFile(const std::string & name);
 public:
-	enum GetMode {RoE, AB, SoD, WoG, Custom, ALL};
+	enum GetMode {RoE, AB, SoD, WoG, Custom};
 	static std::vector<CCampaignHeader> getCampaignHeaders(GetMode mode);
-	static CCampaignHeader getHeader( const std::string & name, bool fromLod ); //name - name of appropriate file
+	static CCampaignHeader getHeader( const std::string & name); //name - name of appropriate file
 
-	static CCampaign * getCampaign(const std::string & name, bool fromLod); //name - name of appropriate file
+	static CCampaign * getCampaign(const std::string & name); //name - name of appropriate file
 };

+ 9 - 5
lib/CCreatureHandler.cpp

@@ -1,6 +1,7 @@
 #include "StdInc.h"
 #include "CCreatureHandler.h"
 
+#include "Filesystem/CResourceLoader.h"
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/CGameState.h"
@@ -8,7 +9,6 @@
 #include "CHeroHandler.h"
 
 using namespace boost::assign;
-extern CLodHandler * bitmaph;
 
 /*
  * CCreatureHandler.cpp, part of VCMI engine
@@ -268,7 +268,8 @@ void CCreatureHandler::loadCreatures()
 	tlog5 << "\t\tReading config/cr_abils.json and ZCRTRAIT.TXT" << std::endl;
 
 	////////////reading ZCRTRAIT.TXT ///////////////////
-	std::string buf = bitmaph->getTextFile("ZCRTRAIT.TXT");
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/ZCRTRAIT.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
 	int andame = buf.size();
 	int i=0; //buf iterator
 	int hmcr=0;
@@ -495,7 +496,8 @@ void CCreatureHandler::loadCreatures()
 
 	if (GameConstants::STACK_EXP) 	//reading default stack experience bonuses
 	{
-		buf = bitmaph->getTextFile("CREXPBON.TXT");
+		auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREXPBON.TXT"));
+		std::string buf((char*)textFile.first.get(), textFile.second);
 		int it = 0;
 		si32 creid = -1;
 		Bonus b; //prototype with some default properties
@@ -570,7 +572,8 @@ void CCreatureHandler::loadCreatures()
 			}
 		}
 
-		buf = bitmaph->getTextFile("CREXPMOD.TXT"); //could be hardcoded though, lots of useless info
+		textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREXPMOD.TXT"));
+		buf = std::string((char*)textFile.first.get(), textFile.second);
 		it = 0;
 		loadToIt (dump2, buf, it, 3); //ignore first line
 
@@ -637,7 +640,8 @@ void CCreatureHandler::loadCreatures()
 
 void CCreatureHandler::loadAnimationInfo()
 {
-	std::string buf = bitmaph->getTextFile("CRANIM.TXT");
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CRANIM.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
 	int andame = buf.size();
 	int i=0; //buf iterator
 	int hmcr=0;

+ 0 - 1
lib/CCreatureHandler.h

@@ -17,7 +17,6 @@
  */
 #define ALLCREATURESGETDOUBLEMONTHS false
 
-class CLodHandler;
 class CCreatureHandler;
 class CCreature;
 

+ 10 - 8
lib/CDefObjInfoHandler.cpp

@@ -1,13 +1,12 @@
 #include "StdInc.h"
 #include "CDefObjInfoHandler.h"
 
+#include "Filesystem/CResourceLoader.h"
 #include "../client/CGameInfo.h"
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "GameConstants.h"
 
-extern CLodHandler * bitmaph;
-
 /*
  * CDefObjInfoHandler.cpp, part of VCMI engine
  *
@@ -36,21 +35,24 @@ CGDefInfo::CGDefInfo()
 
 void CGDefInfo::fetchInfoFromMSK()
 {
-	std::string msk = spriteh->getTextFile(name, FILE_MASK);
 
-	width = msk[0];
-	height = msk[1];
+	auto msk = CResourceHandler::get()->loadData(ResourceID(std::string("SPRITES/") + name, EResType::MASK));
+
+	width = msk.first.get()[0];
+	height = msk.first.get()[1];
 	for(int i=0; i<6; ++i)
 	{
-		coverageMap[i] = msk[i+2];
-		shadowCoverage[i] = msk[i+8];
+		coverageMap[i] = msk.first.get()[i+2];
+		shadowCoverage[i] = msk.first.get()[i+8];
 	}
 }
 
 void CDefObjInfoHandler::load()
 {
 	VLC->dobjinfo = this;
-	std::istringstream inp(bitmaph->getTextFile("ZOBJCTS.TXT"));
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/ZOBJCTS.TXT"));
+
+	std::istringstream inp(std::string((char*)textFile.first.get(), textFile.second));
 	int objNumber;
 	inp>>objNumber;
 	std::string mapStr;

+ 0 - 1
lib/CDefObjInfoHandler.h

@@ -14,7 +14,6 @@
  */
 
 class CDefEssential;
-class CLodHandler;
 class DLL_LINKAGE CGDefInfo
 {
 public:

+ 2 - 2
lib/CFileUtility.cpp

@@ -11,7 +11,7 @@
  *
  */
 
-
+/*
 namespace fs = boost::filesystem;
 
 CFileUtility::CFileUtility(void)
@@ -52,4 +52,4 @@ void CFileUtility::getFilesWithExt(std::vector<FileInfo> &out, const std::string
 
 		}
 	}
-}
+}*/

+ 3 - 4
lib/CFileUtility.h

@@ -15,11 +15,10 @@
 /// Struct which stores name, date and a value which says if the file is located in LOD
 struct FileInfo
 {
-	std::string name; // file name with full path and extension
+	std::string name; // file URI (upper case, no extension)
 	std::time_t date;
-	bool inLod; //tells if this file is located in Lod
 };
-
+/*
 class DLL_LINKAGE CFileUtility
 {
 public:
@@ -28,4 +27,4 @@ public:
 
 	static void getFilesWithExt(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext);
 };
-
+*/

+ 4 - 1
lib/CGameState.cpp

@@ -20,7 +20,8 @@
 #include "RegisterTypes.h"
 #include "CMapInfo.h"
 #include "BattleState.h"
-#include "../lib/JsonNode.h"
+#include "JsonNode.h"
+#include "Filesystem/CResourceLoader.h"
 #include "GameConstants.h"
 
 DLL_LINKAGE boost::rand48 ran;
@@ -2814,8 +2815,10 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
 		assert(ss.heroPrimSkills.empty() || ss.heroPrimSkills.size() == GameConstants::PRIMARY_SKILLS);
 
 		if(ss.heroId != -1)
+		{
 			BOOST_FOREACH(const JsonNode &spell, n["spells"].Vector())
 				ss.spells.insert(spell.Float());
+		}
 	}
 
 	BOOST_FOREACH(const JsonNode &n, duelData["obstacles"].Vector())

+ 49 - 40
lib/CGeneralTextHandler.cpp

@@ -1,7 +1,8 @@
 #include "StdInc.h"
 #include "CGeneralTextHandler.h"
 
-#include "../lib/VCMI_Lib.h"
+#include "Filesystem/CResourceLoader.h"
+#include "VCMI_Lib.h"
 #include "CLodHandler.h"
 #include "GameConstants.h"
 
@@ -29,9 +30,17 @@ void trimQuotation(std::string &op)
 		op = op.substr(1,op.size()-2);
 }
 
+std::string getTextFile(std::string filename)
+{
+	auto file = CResourceHandler::get()->loadData(
+	               ResourceID(std::string("DATA/") + filename, EResType::TEXT));
+
+	return std::string((char*)file.first.get(), file.second);
+}
+
 void CGeneralTextHandler::load()
 {
-	std::string buf1 = bitmaph->getTextFile("ZELP.TXT");
+	std::string buf1 = getTextFile("ZELP.TXT");
 	int itr=0, eol=-1, eolnext=-1, pom;
 	eolnext = buf1.find_first_of('\r',itr);
 	while(itr<buf1.size())
@@ -55,7 +64,7 @@ void CGeneralTextHandler::load()
 		}
 		itr=eol+2;
 	}
-	std::string buf = bitmaph->getTextFile("VCDESC.TXT");
+	std::string buf = getTextFile("VCDESC.TXT");
 	int andame = buf.size();
 	int i=0; //buf iterator
 	for(int gg=0; gg<14; ++gg)
@@ -69,7 +78,7 @@ void CGeneralTextHandler::load()
 		victoryConditions[gg] = buf.substr(befi, i-befi);
 		i+=2;
 	}
-	buf = bitmaph->getTextFile("LCDESC.TXT");
+	buf = getTextFile("LCDESC.TXT");
 	andame = buf.size();
 	i=0; //buf iterator
 	for(int gg=0; gg<4; ++gg)
@@ -86,7 +95,7 @@ void CGeneralTextHandler::load()
 
 	hTxts.resize(GameConstants::HEROES_QUANTITY);
 
-	buf = bitmaph->getTextFile("HEROSPEC.TXT");
+	buf = getTextFile("HEROSPEC.TXT");
 	i=0;
 	std::string dump;
 	for(int iii=0; iii<2; ++iii)
@@ -101,7 +110,7 @@ void CGeneralTextHandler::load()
 		trimQuotation(hTxts[iii].longBonus);
 	}
 
-	buf = bitmaph->getTextFile("HEROBIOS.TXT");
+	buf = getTextFile("HEROBIOS.TXT");
 	i=0;
 	for (int iii=0;iii<hTxts.size();iii++)
 	{
@@ -110,7 +119,7 @@ void CGeneralTextHandler::load()
 	}
 
 	int it;
-	buf = bitmaph->getTextFile("BLDGNEUT.TXT");
+	buf = getTextFile("BLDGNEUT.TXT");
 	andame = buf.size(), it=0;
 
 	for(int b=0;b<15;b++)
@@ -140,7 +149,7 @@ void CGeneralTextHandler::load()
 	}
 	/////done reading "BLDGNEUT.TXT"******************************
 
-	buf = bitmaph->getTextFile("BLDGSPEC.TXT");
+	buf = getTextFile("BLDGSPEC.TXT");
 	andame = buf.size(), it=0;
 	for(int f=0;f<GameConstants::F_NUMBER;f++)
 	{
@@ -156,7 +165,7 @@ void CGeneralTextHandler::load()
 	}
 	/////done reading BLDGSPEC.TXT*********************************
 
-	buf = bitmaph->getTextFile("DWELLING.TXT");
+	buf = getTextFile("DWELLING.TXT");
 	andame = buf.size(), it=0;
 	for(int f=0;f<GameConstants::F_NUMBER;f++)
 	{
@@ -178,7 +187,7 @@ void CGeneralTextHandler::load()
 		}
 	}
 
-	buf = bitmaph->getTextFile("TCOMMAND.TXT");
+	buf = getTextFile("TCOMMAND.TXT");
 	itr=0;
 	while(itr<buf.length()-1)
 	{
@@ -187,7 +196,7 @@ void CGeneralTextHandler::load()
 		tcommands.push_back(tmp);
 	}
 
-	buf = bitmaph->getTextFile("HALLINFO.TXT");
+	buf = getTextFile("HALLINFO.TXT");
 	itr=0;
 	while(itr<buf.length()-1)
 	{
@@ -196,7 +205,7 @@ void CGeneralTextHandler::load()
 		hcommands.push_back(tmp);
 	}
 
-	buf = bitmaph->getTextFile("CASTINFO.TXT");
+	buf = getTextFile("CASTINFO.TXT");
 	itr=0;
 	while(itr<buf.length()-1)
 	{
@@ -206,8 +215,8 @@ void CGeneralTextHandler::load()
 	}
 
 	std::istringstream ins, namess;
-	ins.str(bitmaph->getTextFile("TOWNTYPE.TXT"));
-	namess.str(bitmaph->getTextFile("TOWNNAME.TXT"));
+	ins.str(getTextFile("TOWNTYPE.TXT"));
+	namess.str(getTextFile("TOWNNAME.TXT"));
 	int si=0;
 	char bufname[75];
 	while (!ins.eof())
@@ -225,7 +234,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading OBJNAMES \n";
-	buf = bitmaph->getTextFile("OBJNAMES.TXT");
+	buf = getTextFile("OBJNAMES.TXT");
 	it=0; //hope that -1 will not break this
 	while (it<buf.length()-1)
 	{
@@ -239,7 +248,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading ADVEVENT \n";
-	buf = bitmaph->getTextFile("ADVEVENT.TXT");
+	buf = getTextFile("ADVEVENT.TXT");
 	it=0;
 	std::string temp;
 	while (it<buf.length()-1)
@@ -254,7 +263,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading XTRAINFO \n";
-	buf = bitmaph->getTextFile("XTRAINFO.TXT");
+	buf = getTextFile("XTRAINFO.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -263,7 +272,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading MINENAME \n";
-	buf = bitmaph->getTextFile("MINENAME.TXT");
+	buf = getTextFile("MINENAME.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -272,7 +281,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading MINEEVNT \n";
-	buf = bitmaph->getTextFile("MINEEVNT.TXT");
+	buf = getTextFile("MINEEVNT.TXT");
 	it=0;
 	i=0;
 	while (it<buf.length()-1)
@@ -286,7 +295,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading RESTYPES \n";
-	buf = bitmaph->getTextFile("RESTYPES.TXT");
+	buf = getTextFile("RESTYPES.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -295,7 +304,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading TERRNAME \n";
-	buf = bitmaph->getTextFile("TERRNAME.TXT");
+	buf = getTextFile("TERRNAME.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -304,7 +313,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading RANDSIGN \n";
-	buf = bitmaph->getTextFile("RANDSIGN.TXT");
+	buf = getTextFile("RANDSIGN.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -313,7 +322,7 @@ void CGeneralTextHandler::load()
 	}	
 
 	tlog5 << "\t\tReading ZCRGN1 \n";
-	buf = bitmaph->getTextFile("ZCRGN1.TXT");
+	buf = getTextFile("ZCRGN1.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -322,7 +331,7 @@ void CGeneralTextHandler::load()
 	}
 
 	tlog5 << "\t\tReading CRGN4 \n";
-	buf = bitmaph->getTextFile("CRGEN4.TXT");
+	buf = getTextFile("CRGEN4.TXT");
 	it=0;
 	while (it<buf.length()-1)
 	{
@@ -330,7 +339,7 @@ void CGeneralTextHandler::load()
 		creGens4.push_back(temp);
 	}
 
-	buf = bitmaph->getTextFile("GENRLTXT.TXT");
+	buf = getTextFile("GENRLTXT.TXT");
 	std::string tmp;
 	andame = buf.size();
 	i=0; //buf iterator
@@ -350,7 +359,7 @@ void CGeneralTextHandler::load()
 		allTexts.push_back(buflet);
 	}
 
-	std::string  stro = bitmaph->getTextFile("Overview.txt");
+	std::string  stro = getTextFile("Overview.txt");
 	itr=0;
 	while(itr<stro.length()-1)
 	{
@@ -359,7 +368,7 @@ void CGeneralTextHandler::load()
 		overview.push_back(tmp);
 	}
 
-	std::string  strc = bitmaph->getTextFile("PLCOLORS.TXT");
+	std::string  strc = getTextFile("PLCOLORS.TXT");
 	itr=0;
 	while(itr<strc.length()-1)
 	{
@@ -369,7 +378,7 @@ void CGeneralTextHandler::load()
 		capColors.push_back(tmp);
 	}
 
-	std::string  strs = bitmaph->getTextFile("ARRAYTXT.TXT");
+	std::string  strs = getTextFile("ARRAYTXT.TXT");
 
 	itr=0;
 	while(itr<strs.length()-1)
@@ -380,7 +389,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	std::string strin = bitmaph->getTextFile("PRISKILL.TXT");
+	std::string strin = getTextFile("PRISKILL.TXT");
 	for(int hh=0; hh<4; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
@@ -388,7 +397,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	strin = bitmaph->getTextFile("JKTEXT.TXT");
+	strin = getTextFile("JKTEXT.TXT");
 	for(int hh=0; hh<45; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
@@ -397,7 +406,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	strin = bitmaph->getTextFile("TVRNINFO.TXT");
+	strin = getTextFile("TVRNINFO.TXT");
 	for(int hh=0; hh<8; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
@@ -405,7 +414,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	strin = bitmaph->getTextFile("TURNDUR.TXT");
+	strin = getTextFile("TURNDUR.TXT");
 	for(int hh=0; hh<11; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
@@ -413,7 +422,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	strin = bitmaph->getTextFile("HEROSCRN.TXT");
+	strin = getTextFile("HEROSCRN.TXT");
 	for(int hh=0; hh<33; ++hh)
 	{
 		loadToIt(tmp, strin, itr, 3);
@@ -421,7 +430,7 @@ void CGeneralTextHandler::load()
 	}
 
 	itr = 0;
-	strin = bitmaph->getTextFile("ARTEVENT.TXT");
+	strin = getTextFile("ARTEVENT.TXT");
 	for(; itr<strin.size();)
 	{
 		loadToIt(tmp, strin, itr, 2);
@@ -431,7 +440,7 @@ void CGeneralTextHandler::load()
 		artifEvents.push_back(tmp);
 	}
 
-	buf = bitmaph->getTextFile("SSTRAITS.TXT");
+	buf = getTextFile("SSTRAITS.TXT");
 	it=0;
 
 	for(int i=0; i<2; ++i)
@@ -449,7 +458,7 @@ void CGeneralTextHandler::load()
 		for(int j = 0; j < 3; j++)
 			trimQuotation(skillInfoTexts[i][j]);
 	}
-	buf = bitmaph->getTextFile("SKILLLEV.TXT");
+	buf = getTextFile("SKILLLEV.TXT");
 	it=0;
 	for(int i=0; i<6; ++i)
 	{
@@ -458,7 +467,7 @@ void CGeneralTextHandler::load()
 		levels.push_back(buffo);
 	}
 
-	buf = bitmaph->getTextFile ("SEERHUT.TXT");
+	buf = getTextFile ("SEERHUT.TXT");
 	it = 0;
 	loadToIt (dump, buf, it, 3);
 	loadToIt (dump, buf, it, 4); //dump description
@@ -502,7 +511,7 @@ void CGeneralTextHandler::load()
 	for (i = 0; i < 48; ++i)
 		loadToIt(seerNames[i], buf, it, 3);
 
-	buf = bitmaph->getTextFile("TENTCOLR.TXT");
+	buf = getTextFile("TENTCOLR.TXT");
 	itr=0;
 	while(itr<buf.length()-1)
 	{
@@ -512,7 +521,7 @@ void CGeneralTextHandler::load()
 	}
 
 	//campaigns
-	buf = bitmaph->getTextFile ("CAMPTEXT.TXT");
+	buf = getTextFile ("CAMPTEXT.TXT");
 	it = 0;
 	loadToIt (dump, buf, it, 3); //comment
 	std::string nameBuf;
@@ -542,7 +551,7 @@ void CGeneralTextHandler::load()
 		} while (nameBuf.size());
 	}
 
-	buf = bitmaph->getTextFile ("ZCREXP.TXT");
+	buf = getTextFile ("ZCREXP.TXT");
 	it = 0;
 	loadToIt (dump, buf, it, 3); //comment
 	for (int i = 0; i < 459; ++i) //some texts seem to be empty

+ 7 - 4
lib/CHeroHandler.cpp

@@ -1,6 +1,7 @@
 #include "StdInc.h"
 #include "CHeroHandler.h"
 
+#include "Filesystem/CResourceLoader.h"
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/JsonNode.h"
@@ -8,7 +9,6 @@
 #include <boost/version.hpp>
 #include "BattleHex.h"
 
-extern CLodHandler * bitmaph;
 void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
 /*
  * CHeroHandler.cpp, part of VCMI engine
@@ -165,7 +165,8 @@ void CHeroHandler::loadPuzzleInfo()
 void CHeroHandler::loadHeroes()
 {
 	VLC->heroh = this;
-	std::string buf = bitmaph->getTextFile("HOTRAITS.TXT");
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/HOTRAITS.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
 	int it=0;
 	std::string dump;
 	for(int i=0; i<2; ++i)
@@ -286,7 +287,8 @@ void CHeroHandler::loadHeroes()
 	expPerLevel.pop_back();//last value is broken
 
 	//ballistics info
-	buf = bitmaph->getTextFile("BALLIST.TXT");
+	textFile = CResourceHandler::get()->loadData(ResourceID("DATA/BALLIST.TXT"));
+	buf = std::string((char*)textFile.first.get(), textFile.second);
 	it = 0;
 	for(int i=0; i<22; ++i)
 	{
@@ -324,7 +326,8 @@ void CHeroHandler::loadHeroes()
 
 void CHeroHandler::loadHeroClasses()
 {
-	std::istringstream str(bitmaph->getTextFile("HCTRAITS.TXT")); //we'll be reading from it
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/HCTRAITS.TXT"));
+	std::istringstream str(std::string((char*)textFile.first.get(), textFile.second)); //we'll be reading from it
 	const int BUFFER_SIZE = 5000;
 	char buffer[BUFFER_SIZE+1];
 

+ 9 - 12
lib/CLodHandler.cpp

@@ -1,15 +1,11 @@
 #include "StdInc.h"
 #include "CLodHandler.h"
 
-#include "zlib.h"
 #include "vcmi_endian.h"
-#include "VCMIDirs.h"
 #ifdef max
 #undef max
 #endif
 
-extern VCMIDirs GVCMIDirs;
-
 /*
  * CLodHandler.cpp, part of VCMI engine
  *
@@ -21,7 +17,7 @@ extern VCMIDirs GVCMIDirs;
  */
 
 std::string readString(const ui8 * bufor, int &i)
-{					
+{
 	int len = read_le_u32(bufor + i); i+=4;
 	assert(len >= 0 && len <= 500000); //not too long
 	std::string ret; ret.reserve(len);
@@ -31,7 +27,7 @@ std::string readString(const ui8 * bufor, int &i)
 	}
 	return ret;
 }
-
+/*
 void CLodHandler::convertName(std::string &filename, std::string *extension)
 {
 	std::transform(filename.begin(), filename.end(), filename.begin(), toupper);
@@ -132,7 +128,7 @@ DLL_LINKAGE int CLodHandler::infs2(ui8 * in, int size, int realSize, ui8 *& out,
 	out = new ui8 [realSize];
 	int latPosOut = 0;
 
-	/* allocate inflate state */
+	// allocate inflate state
 	strm.zalloc = Z_NULL;
 	strm.zfree = Z_NULL;
 	strm.opaque = Z_NULL;
@@ -151,13 +147,13 @@ DLL_LINKAGE int CLodHandler::infs2(ui8 * in, int size, int realSize, ui8 *& out,
 			break;
 		strm.next_in = in + chunkNumber * NLoadHandlerHelp::fCHUNK;
 
-		/* run inflate() on input until output buffer not full */
+		// run inflate() on input until output buffer not full
 		do
 		{
 			strm.avail_out = realSize - latPosOut;
 			strm.next_out = out + latPosOut;
 			ret = inflate(&strm, Z_NO_FLUSH);
-			//assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+			//assert(ret != Z_STREAM_ERROR);  // state not clobbered
 			bool breakLoop = false;
 			switch (ret)
 			{
@@ -165,7 +161,7 @@ DLL_LINKAGE int CLodHandler::infs2(ui8 * in, int size, int realSize, ui8 *& out,
 				breakLoop = true;
 				break;
 			case Z_NEED_DICT:
-				ret = Z_DATA_ERROR;	 /* and fall through */
+				ret = Z_DATA_ERROR;	 // and fall through
 			case Z_DATA_ERROR:
 			case Z_MEM_ERROR:
 				(void)inflateEnd(&strm);
@@ -180,10 +176,10 @@ DLL_LINKAGE int CLodHandler::infs2(ui8 * in, int size, int realSize, ui8 *& out,
 		} while (strm.avail_out == 0);
 
 		++chunkNumber;
-		/* done when inflate() says it's done */
+		// done when inflate() says it's done
 	} while (ret != Z_STREAM_END);
 
-	/* clean up and return */
+	// clean up and return
 	(void)inflateEnd(&strm);
 	return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
 }
@@ -399,3 +395,4 @@ ui8 * CLodHandler::getUnpackedFile( const std::string & path, int * sizeOut )
 	*sizeOut = mapsize;
 	return initTable;
 }
+*/

+ 27 - 29
lib/CLodHandler.h

@@ -12,22 +12,23 @@
  *
  */
 
-namespace boost
-{class mutex;}
-namespace NLoadHandlerHelp
-{
-	const int dmHelp=0, dmNoExtractingMask=1;
-	//std::string P1,P2,CurDir;
-	const int fCHUNK = 50000;
-}
 
-struct LodEntry {
-	char filename[16];
-	ui32 offset;				/* little endian */
-	ui32 uncompressedSize;	/* little endian */
-	ui32 unused;				/* little endian */
-	ui32 size;				/* little endian */
-};
+//namespace boost
+//{class mutex;}
+//namespace NLoadHandlerHelp
+//{
+//	const int dmHelp=0, dmNoExtractingMask=1;
+//	//std::string P1,P2,CurDir;
+//	const int fCHUNK = 50000;
+//}
+
+//struct LodEntry {
+//	char filename[16];
+//	ui32 offset;				/* little endian */
+//	ui32 uncompressedSize;	/* little endian */
+//	ui32 unused;				/* little endian */
+//	ui32 size;				/* little endian */
+//};
 
 static inline char readChar(const ui8 * bufor, int &i)
 {
@@ -35,7 +36,7 @@ static inline char readChar(const ui8 * bufor, int &i)
 }
 
 DLL_LINKAGE std::string readString(const ui8 * bufor, int &i);
-
+/*
 enum LodFileType{
 	FILE_ANY,
 	FILE_TEXT,
@@ -97,23 +98,20 @@ class DLL_LINKAGE CLodHandler
 	void initEntry(Entry &e, const std::string name);
 	int infs2(ui8 * in, int size, int realSize, ui8*& out, int wBits=15); //zlib fast handler
 
-public:
-	//convert string to upper case and remove extension. If extension!=NULL -> it will be copied here (including dot)
-	void convertName(std::string &filename, std::string *extension=NULL);
+
+	ui8 * giveFile(std::string defName, LodFileType type=FILE_ANY, int * length=NULL);
 
 	boost::unordered_set<Entry> entries;
+	void extractFile(const std::string FName, const std::string name, LodFileType type=FILE_ANY);
+	static ui8 * getUnpackedData(ui8 *data, size_t inputSize, int * outputSize);
+	void convertName(std::string &filename, std::string *extension=NULL);
+	std::string getFileName(std::string lodFile, LodFileType type=FILE_ANY);
+	bool haveFile(std::string name, LodFileType type=FILE_ANY);
+	static ui8 * getUnpackedFile(const std::string & path, int * sizeOut);
+	std::string getTextFile(std::string name, LodFileType type=FILE_TEXT); //extracts one file
 
 	CLodHandler();
 	~CLodHandler();
 	void init(const std::string lodFile, const std::string dirName);
-	std::string getFileName(std::string lodFile, LodFileType type=FILE_ANY);
-	ui8 * giveFile(std::string defName, LodFileType type=FILE_ANY, int * length=NULL); //returns pointer to the decompressed data - it must be deleted when no longer needed!
-	bool haveFile(std::string name, LodFileType type=FILE_ANY);//check if file is present in lod
-	std::string getTextFile(std::string name, LodFileType type=FILE_TEXT); //extracts one file
-	void extractFile(const std::string FName, const std::string name, LodFileType type=FILE_ANY); //extracts a specific file
-
-	//unpack data from memory, input data will be deleted
-	static ui8 * getUnpackedData(ui8 *data, size_t inputSize, int * outputSize);
-	//loads given file, decompresses and returns
-	static ui8 * getUnpackedFile(const std::string & path, int * sizeOut);
 };
+*/

+ 1 - 1
lib/CMakeLists.txt

@@ -12,7 +12,7 @@ set(lib_SRCS
         Filesystem/CLodArchiveLoader.cpp
         Filesystem/CResourceLoader.cpp
         Filesystem/CFileInputStream.cpp
-        Filesystem/CLodStream.cpp
+        Filesystem/CCompressedStream.cpp
         Filesystem/ISimpleResourceLoader.cpp
         BattleAction.cpp
         BattleHex.cpp

+ 4 - 4
lib/CMapInfo.cpp

@@ -8,13 +8,13 @@
 
 void CMapInfo::countPlayers()
 {
-	actualHumanPlayers = playerAmnt = humenPlayers = 0;
+	actualHumanPlayers = playerAmnt = humanPlayers = 0;
 	for(int i=0;i<GameConstants::PLAYER_LIMIT;i++)
 	{
 		if(mapHeader->players[i].canHumanPlay)
 		{
 			playerAmnt++;
-			humenPlayers++;
+			humanPlayers++;
 		}
 		else if(mapHeader->players[i].canComputerPlay)
 		{
@@ -35,7 +35,7 @@ CMapInfo::CMapInfo(bool map)
 
 void CMapInfo::mapInit(const std::string &fname, const ui8 *map )
 {
-	filename = fname;
+	fileURI = fname;
 	int i = 0;
 	mapHeader = new CMapHeader();
 	mapHeader->version = CMapHeader::invalid;
@@ -61,7 +61,7 @@ CMapInfo::~CMapInfo()
 
 void CMapInfo::campaignInit()
 {
-	campaignHeader = new CCampaignHeader( CCampaignHandler::getHeader(filename, lodCmpgn) );
+	campaignHeader = new CCampaignHeader( CCampaignHandler::getHeader(fileURI) );
 }
 
 void CMapInfo::setHeader(CMapHeader *header)

+ 3 - 4
lib/CMapInfo.h

@@ -14,11 +14,10 @@ public:
 	CMapHeader * mapHeader; //may be NULL if campaign
 	CCampaignHeader * campaignHeader; //may be NULL if scenario
 	StartInfo *scenarioOpts; //options with which scenario has been started (used only with saved games)
-	std::string filename;
-	bool lodCmpgn; //tells if this campaign is located in Lod file
+	std::string fileURI;
 	std::string date;
 	int playerAmnt, //players in map
-		humenPlayers; //players ALLOWED to be controlled by human
+		humanPlayers; //players ALLOWED to be controlled by human
 	int actualHumanPlayers; // >1 if multiplayer game
 	CMapInfo(bool map = true);
 	~CMapInfo();
@@ -30,7 +29,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int Version)
 	{
-		h & mapHeader & campaignHeader & scenarioOpts & filename & lodCmpgn & date & playerAmnt & humenPlayers;
+		h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers;
 		h & actualHumanPlayers;
 	}
 };

+ 0 - 1
lib/CObjectHandler.cpp

@@ -43,7 +43,6 @@ std::map<int,std::map<int, std::vector<int> > > CGTeleport::objs;
 std::vector<std::pair<int, int> > CGTeleport::gates;
 IGameCallback * IObjectInterface::cb = NULL;
 DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
-extern CLodHandler * bitmaph;
 extern boost::rand48 ran;
 std::map <ui8, std::set <ui8> > CGKeys::playerKeyMap;
 std::map <si32, std::vector<si32> > CGMagi::eyelist;

+ 5 - 4
lib/CSpellHandler.cpp

@@ -1,7 +1,7 @@
 #include "StdInc.h"
 #include "CSpellHandler.h"
 
-
+#include "Filesystem/CResourceLoader.h"
 #include "CLodHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/JsonNode.h"
@@ -9,8 +9,6 @@
 #include "GameConstants.h"
 #include "BattleHex.h"
 
-extern CLodHandler *bitmaph;
-
 /*
  * CSpellHandler.cpp, part of VCMI engine
  *
@@ -274,7 +272,10 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 
 void CSpellHandler::loadSpells()
 {
-	std::string buf = bitmaph->getTextFile("SPTRAITS.TXT"), pom;
+	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/SPTRAITS.TXT"));
+	std::string buf((char*)textFile.first.get(), textFile.second);
+
+	std::string pom;
 	int andame = buf.size(), i=0; //buf iterator
 	for(int z=0; z<5; ++z)
 		loadToIt(pom,buf,i,3);

+ 162 - 0
lib/Filesystem/CCompressedStream.cpp

@@ -0,0 +1,162 @@
+#include "StdInc.h"
+#include "CCompressedStream.h"
+
+#include <zlib.h>
+
+static const int inflateBlockSize = 10000;
+
+CCompressedStream::CCompressedStream(std::unique_ptr<CInputStream> & stream, bool gzip, size_t decompressedSize):
+    gzipStream(std::move(stream)),
+    buffer(decompressedSize),
+    decompressedSize(0),
+    compressedBuffer(inflateBlockSize),
+    position(0)
+{
+	assert(gzipStream);
+
+	// Allocate inflate state
+	inflateState = new z_stream;
+	inflateState->zalloc = Z_NULL;
+	inflateState->zfree = Z_NULL;
+	inflateState->opaque = Z_NULL;
+	inflateState->avail_in = 0;
+	inflateState->next_in = Z_NULL;
+
+	int wbits = 15;
+	if (gzip)
+		wbits += 16;
+
+	int ret = inflateInit2(inflateState, wbits);
+	if (ret != Z_OK)
+		throw std::runtime_error("Failed to initialize inflate!\n");
+}
+
+CCompressedStream::~CCompressedStream()
+{
+	delete inflateState;
+}
+
+si64 CCompressedStream::read(ui8 * data, si64 size)
+{
+	decompressTill(position + size);
+
+	auto start = buffer.begin() + position;
+	si64 toRead = std::min<si64>(size, decompressedSize - position);
+
+	std::copy(start, start + toRead, data);
+	position += toRead;
+	return size;
+}
+
+si64 CCompressedStream::seek(si64 position)
+{
+	decompressTill(position);
+	this->position = std::min<si64>(position, decompressedSize);
+	return this->position;
+}
+
+si64 CCompressedStream::tell()
+{
+	return position;
+}
+
+si64 CCompressedStream::skip(si64 delta)
+{
+	decompressTill(position + delta);
+
+	si64 oldPosition = position;
+	position = std::min<si64>(position + delta , decompressedSize);
+	return position - oldPosition;
+}
+
+si64 CCompressedStream::getSize()
+{
+	decompressTill(-1);
+	return decompressedSize;
+}
+
+void CCompressedStream::decompressTill(si64 newSize)
+{
+	//TODO: check if inflate have any special mode for inflating (e.g Z_BLOCK) that can
+	// be used for campaigns Technically .h3c consists from multiple
+	// concatenated gz streams (campaign header + every map)
+	// inflatings them separately can be useful for campaigns loading
+
+	assert(newSize < 100 * 1024 * 1024); //just in case
+
+	if (inflateState == nullptr)
+		return; //file already decompressed
+
+	if (newSize >= 0 && newSize < inflateState->total_out)
+		return; //no need to decompress anything
+
+	bool toEnd = newSize < 0;
+
+	if (toEnd && buffer.empty())
+		buffer.resize(16 * 1024); //some space for initial decompression
+
+	if (!toEnd && buffer.size() < newSize)
+		buffer.resize(newSize);
+
+	int ret;
+	int endLoop = false;
+	do
+	{
+		if (inflateState->avail_in == 0)
+		{
+			//inflate ran out of available data or was not initialized yet
+			// get new input data and update state accordingly
+			si64 size = gzipStream->read(compressedBuffer.data(), compressedBuffer.size());
+			if (size != compressedBuffer.size())
+				toEnd = true; //end of file reached
+
+			inflateState->avail_in = size;
+			inflateState->next_in  = compressedBuffer.data();
+		}
+
+		inflateState->avail_out = buffer.size() - inflateState->total_out;
+		inflateState->next_out = buffer.data() + inflateState->total_out;
+
+		ret = inflate(inflateState, Z_NO_FLUSH);
+
+		switch (ret)
+		{
+		case Z_STREAM_END: //end decompression
+			endLoop = true;
+			break;
+		case Z_OK: //decompress next chunk
+			endLoop = false;
+			break;
+		case Z_BUF_ERROR:
+			{
+				if (toEnd)
+				{
+					//not enough memory. Allocate bigger buffer and try again
+					buffer.resize(buffer.size() * 2);
+					endLoop = false;
+				}
+				else
+				{
+					assert(inflateState->total_out == newSize);
+					endLoop = true;
+				}
+			}
+			break;
+		default:
+			throw std::runtime_error("Decompression error!\n");
+		}
+
+	}
+	while (!endLoop);
+
+	// Clean up and return
+	if (toEnd)
+	{
+		inflateEnd(inflateState);
+		buffer.resize(inflateState->total_out);
+		decompressedSize = inflateState->total_out;
+		vstd::clear_pointer(inflateState);
+	}
+	else
+		decompressedSize = inflateState->total_out;
+}

+ 102 - 0
lib/Filesystem/CCompressedStream.h

@@ -0,0 +1,102 @@
+
+/*
+ * CLodStream.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"
+
+struct z_stream_s;
+
+/**
+ * A class which provides method definitions for reading a gzip-compressed file
+ * This class implements lazy loading - data will be decompressed (and cached by this class) only by request
+ */
+class DLL_LINKAGE CCompressedStream : public CInputStream
+{
+public:
+	/**
+	 * C-tor.
+	 *
+	 * @param stream - stream with compresed data
+	 * @param gzip - this is gzipp'ed file e.g. campaign or maps, false for files in lod
+	 * @param decompressedSize - optional parameter to hint size of decompressed data
+	 */
+	CCompressedStream(std::unique_ptr<CInputStream> & stream, bool gzip, size_t decompressedSize=0);
+
+	~CCompressedStream();
+
+	/**
+	 * Reads n bytes from the stream into the data buffer.
+	 *
+	 * @param data A pointer to the destination data array.
+	 * @param size The number of bytes to read.
+	 * @return the number of bytes read actually.
+	 *
+	 * @throws std::runtime_error if the file decompression was not successful
+	 */
+	si64 read(ui8 * data, si64 size);
+
+	/**
+	 * Seeks the internal read pointer to the specified position.
+	 * This will cause decompressing data till this position is found
+	 *
+	 * @param position The read position from the beginning.
+	 * @return the position actually moved to, -1 on error.
+	 */
+	si64 seek(si64 position);
+
+	/**
+	 * Gets the current read position in the stream.
+	 *
+	 * @return the read position.
+	 */
+	si64 tell();
+
+	/**
+	 * Skips delta numbers of bytes.
+	 *
+	 * @param delta The count of bytes to skip.
+	 * @return the count of bytes skipped actually.
+	 */
+	si64 skip(si64 delta);
+
+	/**
+	 * Gets the length in bytes of the stream.
+	 * Causes complete data decompression
+	 *
+	 * @return the length in bytes of the stream.
+	 */
+	si64 getSize();
+
+private:
+	/**
+	 * Decompresses data to ensure that buffer has newSize bytes or end of stream was reached
+	 */
+	void decompressTill(si64 newSize);
+
+	/** The file stream with compressed data. */
+	std::unique_ptr<CInputStream> gzipStream;
+
+	/** buffer with already decompressed data. Please note that size() != decompressed size */
+	std::vector<ui8> buffer;
+
+	/** size of already decompressed data */
+	si64 decompressedSize;
+
+	/** buffer with not yet decompressed data*/
+	std::vector<ui8> compressedBuffer;
+
+	/** struct with current zlib inflate state */
+	z_stream_s * inflateState;
+
+	/** Current read position */
+	si64 position;
+};

+ 35 - 12
lib/Filesystem/CFileInfo.cpp

@@ -65,12 +65,25 @@ std::string CFileInfo::getStem() const
 	if(dotPos != std::string::npos && name[dotPos] == '.')
 		rslt.erase(dotPos);
 
-	// Remove path
-	size_t found = rslt.find_last_of("/\\");
-	return rslt.substr(found + 1);
+	return rslt;
 }
 
-EResType CFileInfo::getType() const
+std::string CFileInfo::getBaseName() const
+{
+	size_t end = name.find_last_of("/.");
+	size_t begin = name.find_last_of("/");
+
+	if(end != std::string::npos && name[end] == '.')
+		end = std::string::npos;
+
+	if(begin == std::string::npos)
+		begin = 0;
+
+	return name.substr(begin, end);
+}
+
+
+EResType::Type CFileInfo::getType() const
 {
 	return EResTypeHelper::getTypeFromExtension(getExtension());
 }
@@ -88,20 +101,30 @@ std::unique_ptr<std::list<CFileInfo> > CFileInfo::listFiles(size_t depth, const
 	{
 		std::list<CFileInfo> * fileList = new std::list<CFileInfo>;
 
+		std::vector<std::string> path;
+
 		boost::filesystem::recursive_directory_iterator enddir;
-		for(boost::filesystem::recursive_directory_iterator it(name); it != enddir; ++it)
+		boost::filesystem::recursive_directory_iterator it(name, boost::filesystem::symlink_option::recurse);
+
+		for(; it != enddir; ++it)
 		{
 			if (boost::filesystem::is_directory(it->status()))
 			{
-				it.no_push(depth >= it.level());
+				path.resize(it.level()+1);
+				path.back() = it->path().leaf().string();
+				it.no_push(depth <= it.level());
 			}
-			if(boost::filesystem::is_regular(it->status()))
+
+			if(extensionFilter.empty() || it->path().extension() == extensionFilter)
 			{
-				if(extensionFilter == "" || it->path().extension() == extensionFilter)
-				{
-					CFileInfo file(it->path().string());
-					fileList->push_back(file);
-				}
+				std::string filename;
+				for (size_t i=0; i<it.level() && i<path.size(); i++)
+					filename += path[i] + '/';
+				filename += it->path().leaf().string();
+
+				//tlog1 << "Found file: " << filename << "\n";
+				CFileInfo file(filename);
+				fileList->push_back(file);
 			}
 		}
 

+ 10 - 3
lib/Filesystem/CFileInfo.h

@@ -81,18 +81,25 @@ public:
 	std::string getFilename() const;
 
 	/**
-	 * Gets the file name exclusive the extension of the file.
+	 * Gets the file name + path exclusive the extension of the file.
 	 *
-	 * @return the file name exclusive the extension of the file. E.g. foo
+	 * @return the file name exclusive the extension of the file. E.g. ./dir/foo
 	 */
 	std::string getStem() const;
 
+	/**
+	 * Gets the file name exclusive the extension of the file.
+	 *
+	 * @return the file name exclusive the extension and a path of the file. E.g. foo
+	 */
+	std::string getBaseName() const;
+
 	/**
 	 * Gets the extension type as a EResType enumeration.
 	 *
 	 * @return the extension type as a EResType enumeration.
 	 */
-	EResType getType() const;
+	EResType::Type getType() const;
 
 	/**
 	 * Gets the timestamp of the file.

+ 28 - 19
lib/Filesystem/CFileInputStream.cpp

@@ -7,14 +7,14 @@ CFileInputStream::CFileInputStream()
 
 }
 
-CFileInputStream::CFileInputStream(const std::string & file)
+CFileInputStream::CFileInputStream(const std::string & file, si64 start, si64 size)
 {
-	open(file);
+	open(file, start, size);
 }
 
-CFileInputStream::CFileInputStream(const CFileInfo & file)
+CFileInputStream::CFileInputStream(const CFileInfo & file, si64 start, si64 size)
 {
-	open(file);
+	open(file.getName(), start, size);
 }
 
 CFileInputStream::~CFileInputStream()
@@ -22,53 +22,62 @@ CFileInputStream::~CFileInputStream()
 	close();
 }
 
-void CFileInputStream::open(const std::string & file)
+void CFileInputStream::open(const std::string & file, si64 start, si64 size)
 {
 	close();
 
 	fileStream.open(file.c_str(), std::ios::in | std::ios::binary);
+
 	if (fileStream.fail())
 	{
 		throw std::runtime_error("File " + file + " isn't available.");
 	}
-}
 
-void CFileInputStream::open(const CFileInfo & file)
-{
-	open(file.getName());
+	dataStart = start;
+	dataSize = size;
+
+	if (dataSize == 0)
+	{
+		fileStream.seekg(0, std::ios::end);
+		dataSize = tell();
+	}
+
+	fileStream.seekg(start, std::ios::beg);
 }
 
 si64 CFileInputStream::read(ui8 * data, si64 size)
 {
-	fileStream.read(reinterpret_cast<char *>(data), size);
+	si64 origin = tell();
+	si64 toRead = std::min(dataSize - origin, size);
+	fileStream.read(reinterpret_cast<char *>(data), toRead);
 	return fileStream.gcount();
 }
 
 si64 CFileInputStream::seek(si64 position)
 {
-	si64 diff = tell();
-	fileStream.seekg(position);
+	fileStream.seekg(dataStart + std::min(position, dataSize));
 
-	return tell() - diff;
+	return tell();
 }
 
 si64 CFileInputStream::tell()
 {
-	return fileStream.tellg();
+	return fileStream.tellg() - dataStart;
 }
 
 si64 CFileInputStream::skip(si64 delta)
 {
-	si64 diff = tell();
-	fileStream.seekg(delta, std::ios::cur);
+	si64 origin = tell();
+	//ensure that we're not seeking past the end of real data
+	si64 toSeek = std::min(dataSize - origin, delta);
+	fileStream.seekg(toSeek, std::ios::cur);
 
-	return tell() - diff;
+	return tell() - origin;
 }
 
 si64 CFileInputStream::getSize()
 {
-	fileStream.seekg(0, std::ios::end);
-	return fileStream.tellg();
+	return dataSize;
 }
 
 void CFileInputStream::close()

+ 9 - 18
lib/Filesystem/CFileInputStream.h

@@ -29,20 +29,16 @@ public:
 	/**
 	 * C-tor. Opens the specified file.
 	 *
-	 * @param file Path to the file.
-	 *
-	 * @throws std::runtime_error if file wasn't found
+	 * @see CFileInputStream::open
 	 */
-	CFileInputStream(const std::string & file);
+	CFileInputStream(const std::string & file, si64 start=0, si64 size=0);
 
 	/**
 	 * C-tor. Opens the specified file.
 	 *
-	 * @param file A file info object, pointing to a location in the file system
-	 *
-	 * @throws std::runtime_error if file wasn't found
+	 * @see CFileInputStream::open
 	 */
-	CFileInputStream(const CFileInfo & file);
+	CFileInputStream(const CFileInfo & file, si64 start=0, si64 size=0);
 
 	/**
 	 * D-tor. Calls the close method implicitely, if the file is still opened.
@@ -53,19 +49,12 @@ public:
 	 * Opens a file. If a file is currently opened, it will be closed.
 	 *
 	 * @param file Path to the file.
+	 * @param start - offset from file start where real data starts (e.g file on archive)
+	 * @param size - size of real data in file (e.g file on archive) or 0 to use whole file
 	 *
 	 * @throws std::runtime_error if file wasn't found
 	 */
-	void open(const std::string & file);
-
-	/**
-	 * Opens a file.
-	 *
-	 * @param file A file info object, pointing to a location in the file system
-	 *
-	 * @throws std::runtime_error if file wasn't found
-	 */
-	void open(const CFileInfo & file);
+	void open(const std::string & file, si64 start, si64 size);
 
 	/**
 	 * Reads n bytes from the stream into the data buffer.
@@ -112,6 +101,8 @@ public:
 	void close();
 
 private:
+	si64 dataStart;
+	si64 dataSize;
 	/** Native c++ input file stream object. */
 	std::ifstream fileStream;
 };

+ 9 - 11
lib/Filesystem/CFilesystemLoader.cpp

@@ -16,7 +16,7 @@ CFilesystemLoader::CFilesystemLoader(const std::string & baseDirectory, size_t d
 
 CFilesystemLoader::CFilesystemLoader(const CFileInfo & baseDirectory, size_t depth)
 {
-	open(baseDirectory, depth);
+	open(baseDirectory.getName(), depth);
 }
 
 void CFilesystemLoader::open(const std::string & baseDirectory, size_t depth)
@@ -27,20 +27,13 @@ void CFilesystemLoader::open(const std::string & baseDirectory, size_t depth)
 	std::unique_ptr<std::list<CFileInfo> > fileList = directory.listFiles(depth);
 	if(fileList)
 	{
-		throw std::runtime_error("Directory " + baseDirectory + " not available.");
+		this->fileList = std::move(*fileList);
 	}
-
-	this->fileList = std::move(*fileList);
-}
-
-void CFilesystemLoader::open(const CFileInfo & baseDirectory, size_t depth)
-{
-	open(baseDirectory.getName(), depth);
 }
 
 std::unique_ptr<CInputStream> CFilesystemLoader::load(const std::string & resourceName) const
 {
-	std::unique_ptr<CInputStream> stream(new CFileInputStream(resourceName));
+	std::unique_ptr<CInputStream> stream(new CFileInputStream(getOrigin() + '/' + resourceName));
 	return stream;
 }
 
@@ -48,7 +41,7 @@ bool CFilesystemLoader::existsEntry(const std::string & resourceName) const
 {
 	for(auto it = fileList.begin(); it != fileList.end(); ++it)
 	{
-		if((*it).getName() == resourceName)
+		if(it->getName() == resourceName)
 		{
 			return true;
 		}
@@ -68,3 +61,8 @@ std::list<std::string> CFilesystemLoader::getEntries() const
 
 	return std::move(retList);
 }
+
+std::string CFilesystemLoader::getOrigin() const
+{
+	return baseDirectory;
+}

+ 7 - 9
lib/Filesystem/CFilesystemLoader.h

@@ -55,15 +55,6 @@ public:
 	 */
 	void open(const std::string & baseDirectory, size_t depth);
 
-	/**
-	 * Opens a base directory to be read and indexed.
-	 *
-	 * @param baseDirectory Specifies the base directory and their sub-directories which should be indexed.
-	 *
-	 * @throws std::runtime_error if the base directory is not a directory or if it is not available
-	 */
-	void open(const CFileInfo & baseDirectory, size_t depth);
-
 	/**
 	 * Loads a resource with the given resource name.
 	 *
@@ -86,6 +77,13 @@ public:
 	 */
 	std::list<std::string> getEntries() const;
 
+	/**
+	 * Gets the origin of the archive loader.
+	 *
+	 * @return the file path to directory with archive (e.g. path/to/h3/mp3)
+	 */
+	std::string getOrigin() const;
+
 private:
 	/** The base directory which is scanned and indexed. */
 	std::string baseDirectory;

+ 1 - 1
lib/Filesystem/CInputStream.h

@@ -64,5 +64,5 @@ public:
 	/**
 	 * Closes the stream and releases any system resources associated with the stream explicitely.
 	 */
-	virtual void close() = 0;
+	//virtual void close() = 0;
 };

+ 18 - 13
lib/Filesystem/CLodArchiveLoader.cpp

@@ -2,6 +2,7 @@
 #include "CLodArchiveLoader.h"
 #include "CInputStream.h"
 #include "CFileInputStream.h"
+#include "CCompressedStream.h"
 #include "CLodStream.h"
 #include "CBinaryReader.h"
 #include "CFileInfo.h"
@@ -40,7 +41,7 @@ void CLodArchiveLoader::open(const std::string & archive)
 	boost::to_upper(ext);
 
 	// Init the specific lod container format
-	if(ext == ".LOD")
+	if(ext == ".LOD" || ext == ".PAC")
 	{
 		initLODArchive(fileStream);
 	}
@@ -170,7 +171,7 @@ void CLodArchiveLoader::initSNDArchive(CFileInputStream & fileStream)
 	fileStream.read(reinterpret_cast<ui8 *>(sndEntries), sizeof(struct SoundEntryBlock) * totalFiles);
 
 	// Insert entries to list
-	for(ui8 i = 0; i < totalFiles; i++)
+	for(ui32 i = 0; i < totalFiles; i++)
 	{
 		SoundEntryBlock sndEntry = sndEntries[i];
 		ArchiveEntry entry;
@@ -188,8 +189,20 @@ void CLodArchiveLoader::initSNDArchive(CFileInputStream & fileStream)
 
 std::unique_ptr<CInputStream> CLodArchiveLoader::load(const std::string & resourceName) const
 {
-	std::unique_ptr<CInputStream> stream(new CLodStream(this, resourceName));
-	return stream;
+	assert(existsEntry(resourceName));
+
+	const ArchiveEntry & entry = entries.find(resourceName)->second;
+
+	if (entry.size != 0) //compressed data
+	{
+		std::unique_ptr<CInputStream> fileStream(new CFileInputStream(getOrigin(), entry.offset, entry.size));
+
+		return std::unique_ptr<CInputStream>(new CCompressedStream(fileStream, false, entry.realSize));
+	}
+	else
+	{
+		return std::unique_ptr<CInputStream>(new CFileInputStream(getOrigin(), entry.offset, entry.realSize));
+	}
 }
 
 std::list<std::string> CLodArchiveLoader::getEntries() const
@@ -219,15 +232,7 @@ const ArchiveEntry * CLodArchiveLoader::getArchiveEntry(const std::string & reso
 
 bool CLodArchiveLoader::existsEntry(const std::string & resourceName) const
 {
-	auto it = entries.find(resourceName);
-	if(it != entries.end())
-	{
-		return true;
-	}
-	else
-	{
-		return false;
-	}
+	return entries.find(resourceName) != entries.end();
 }
 
 std::string CLodArchiveLoader::getOrigin() const

+ 1 - 1
lib/Filesystem/CLodArchiveLoader.h

@@ -35,7 +35,7 @@ struct ArchiveEntry
 	/** Size without compression in bytes **/
 	int realSize;
 
-	/** Size within compression in bytes **/
+	/** Size with compression in bytes or 0 if not compressed **/
 	int size;
 };
 

+ 26 - 39
lib/Filesystem/CLodStream.cpp

@@ -1,8 +1,9 @@
 #include "StdInc.h"
 #include "CLodStream.h"
-
+/*
 #include "CLodArchiveLoader.h"
 #include "CFileInputStream.h"
+#include "CMemoryStream.h"
 
 CLodStream::CLodStream()
 {
@@ -16,74 +17,60 @@ CLodStream::CLodStream(const CLodArchiveLoader * loader, const std::string & res
 
 void CLodStream::open(const CLodArchiveLoader * loader, const std::string & resourceName)
 {
-	close();
+	assert(!fileStream);
 
 	const ArchiveEntry * archiveEntry = loader->getArchiveEntry(resourceName);
 	if(archiveEntry == nullptr)
 	{
 		throw std::runtime_error("Archive entry " + resourceName + " wasn't found in the archive " + loader->getOrigin());
 	}
-	this->archiveEntry = archiveEntry;
+	size = archiveEntry->size;
+	offset = archiveEntry->offset;
 
 	// Open the archive and set the read pointer to the correct position
-	fileStream.open(loader->getOrigin());
-	fileStream.seek(this->archiveEntry->offset);
-}
+	fileStream.reset(new CFileInputStream(loader->getOrigin()));
+	fileStream->seek(archiveEntry->offset);
 
-si64 CLodStream::read(ui8 * data, si64 size)
-{
-	// Test whether the file has to be decompressed
-	if(archiveEntry->size == 0)
+	// Decompress file
+	if(archiveEntry->size != 0)
 	{
-		// No decompression
-		return fileStream.read(data, size);
-	}
-	else
-	{
-		// Decompress file
-
-		// We can't decompress partially, so the size of the output buffer has to be minimum the
-		// size of the decompressed lod entry. If this isn't the case, it is a programming fault.
-		assert(size >= archiveEntry->realSize);
+		// replace original buffer with decompressed one
+		fileStream.reset(CLodArchiveLoader::decompress(*this, archiveEntry->realSize));
+		assert(fileStream->getSize() == archiveEntry->realSize);
 
-		// Read the compressed data into a buffer
-		auto comp = std::unique_ptr<ui8[]>(new ui8[archiveEntry->size]);
-		fileStream.read(comp.get(), archiveEntry->size);
-
-		// Decompress the file
-		data = CLodArchiveLoader::decompressFile(comp.get(), archiveEntry->size, archiveEntry->realSize).first;
-
-		if (!data)
-		{
-			throw std::runtime_error("File decompression wasn't successful. Resource name: " + archiveEntry->name);
-		}
-
-		// We're reading the total size always
-		return archiveEntry->realSize;
+		//in memory stream we no longer need offset
+		offset = 0;
 	}
+	size = archiveEntry->realSize;
+}
+
+si64 CLodStream::read(ui8 * data, si64 size)
+{
+	return fileStream->read(data, size);
 }
 
 si64 CLodStream::seek(si64 position)
 {
-	return fileStream.seek(archiveEntry->offset + position);
+	return fileStream->seek(offset + position);
 }
 
 si64 CLodStream::tell()
 {
-	return fileStream.tell() - archiveEntry->offset;
+	return fileStream->tell() - offset;
 }
 
 si64 CLodStream::skip(si64 delta)
 {
-	return fileStream.skip(delta);
+	return fileStream->skip(delta);
 }
 
 si64 CLodStream::getSize()
 {
-	return archiveEntry->realSize;
+	return size;
 }
 
 void CLodStream::close()
 {
-	fileStream.close();
+	fileStream->close();
 }
+*/

+ 94 - 91
lib/Filesystem/CLodStream.h

@@ -11,95 +11,98 @@
 
 #pragma once
 
-#include "CInputStream.h"
-#include "CFileInputStream.h"
-#include "CLodArchiveLoader.h"
+//#include "CInputStream.h"
+//#include "CFileInputStream.h"
+//#include "CLodArchiveLoader.h"
 
-/**
- * A class which provides method definitions for reading a file from a LOD archive.
- */
-class DLL_LINKAGE CLodStream : public CInputStream
-{
-public:
-	/**
-	 * Default c-tor.
-	 */
-	CLodStream();
-
-	/**
-	 * C-tor.
-	 *
-	 * @param loader The archive loader object which knows about the structure of the archive format.
-	 * @param resourceName The resource name which says what resource should be loaded.
-	 *
-	 * @throws std::runtime_error if the archive entry wasn't found
-	 */
-	CLodStream(const CLodArchiveLoader * loader, const std::string & resourceName);
-
-	/**
-	 * Opens a lod stream. It will close any currently opened file stream.
-	 *
-	 * @param loader The archive loader object which knows about the structure of the archive format.
-	 * @param resourceName The resource name which says what resource should be loaded.
-	 *
-	 * @throws std::runtime_error if the archive entry wasn't found
-	 */
-	void open(const CLodArchiveLoader * loader, const std::string & resourceName);
-
-	/**
-	 * Reads n bytes from the stream into the data buffer.
-	 *
-	 * Warning: You can't read just a part of the archive entry if it's a decompressed one. So reading the total size always is
-	 * recommended unless you really know what you're doing e.g. reading from a video stream.
-	 *
-	 * @param data A pointer to the destination data array.
-	 * @param size The number of bytes to read.
-	 * @return the number of bytes read actually.
-	 *
-	 * @throws std::runtime_error if the file decompression was not successful
-	 */
-	si64 read(ui8 * data, si64 size);
-
-	/**
-	 * Seeks the internal read pointer to the specified position.
-	 *
-	 * @param position The read position from the beginning.
-	 * @return the position actually moved to, -1 on error.
-	 */
-	si64 seek(si64 position);
-
-	/**
-	 * Gets the current read position in the stream.
-	 *
-	 * @return the read position.
-	 */
-	si64 tell();
-
-	/**
-	 * Skips delta numbers of bytes.
-	 *
-	 * @param delta The count of bytes to skip.
-	 * @return the count of bytes skipped actually.
-	 */
-	si64 skip(si64 delta);
-
-	/**
-	 * Gets the length in bytes of the stream.
-	 *
-	 * @return the length in bytes of the stream.
-	 */
-	si64 getSize();
-
-	/**
-	 * Closes the stream and releases any system resources associated with the stream explicitely.
-	 */
-	void close();
-
-private:
-
-	/** The file stream for reading from the LOD archive. */
-	CFileInputStream fileStream;
-
-	/** The archive entry which specifies the length, offset,... of the entry. */
-	const ArchiveEntry * archiveEntry;
-};
+///**
+// * A class which provides method definitions for reading a file from a LOD archive.
+// */
+//class DLL_LINKAGE CLodStream : public CInputStream
+//{
+//public:
+//	/**
+//	 * Default c-tor.
+//	 */
+//	CLodStream();
+
+//	/**
+//	 * C-tor.
+//	 *
+//	 * @param loader The archive loader object which knows about the structure of the archive format.
+//	 * @param resourceName The resource name which says what resource should be loaded.
+//	 *
+//	 * @throws std::runtime_error if the archive entry wasn't found
+//	 */
+//	CLodStream(const CLodArchiveLoader * loader, const std::string & resourceName);
+
+//	/**
+//	 * Opens a lod stream. It will close any currently opened file stream.
+//	 *
+//	 * @param loader The archive loader object which knows about the structure of the archive format.
+//	 * @param resourceName The resource name which says what resource should be loaded.
+//	 *
+//	 * @throws std::runtime_error if the archive entry wasn't found
+//	 */
+//	void open(const CLodArchiveLoader * loader, const std::string & resourceName);
+
+//	/**
+//	 * Reads n bytes from the stream into the data buffer.
+//	 *
+//	 * Warning: You can't read just a part of the archive entry if it's a decompressed one. So reading the total size always is
+//	 * recommended unless you really know what you're doing e.g. reading from a video stream.
+//	 *
+//	 * @param data A pointer to the destination data array.
+//	 * @param size The number of bytes to read.
+//	 * @return the number of bytes read actually.
+//	 *
+//	 * @throws std::runtime_error if the file decompression was not successful
+//	 */
+//	si64 read(ui8 * data, si64 size);
+
+//	/**
+//	 * Seeks the internal read pointer to the specified position.
+//	 *
+//	 * @param position The read position from the beginning.
+//	 * @return the position actually moved to, -1 on error.
+//	 */
+//	si64 seek(si64 position);
+
+//	/**
+//	 * Gets the current read position in the stream.
+//	 *
+//	 * @return the read position.
+//	 */
+//	si64 tell();
+
+//	/**
+//	 * Skips delta numbers of bytes.
+//	 *
+//	 * @param delta The count of bytes to skip.
+//	 * @return the count of bytes skipped actually.
+//	 */
+//	si64 skip(si64 delta);
+
+//	/**
+//	 * Gets the length in bytes of the stream.
+//	 *
+//	 * @return the length in bytes of the stream.
+//	 */
+//	si64 getSize();
+
+//	/**
+//	 * Closes the stream and releases any system resources associated with the stream explicitely.
+//	 */
+//	void close();
+
+//private:
+
+//	/** The file stream for reading from the LOD archive. */
+//	std::unique_ptr<CInputStream> fileStream;
+
+//	/** Offset of data in input stream. Used if input stream represents whole file*/
+//	size_t offset;
+//	/** Size of our data in input stream. */
+//	size_t size;
+
+//};

+ 163 - 26
lib/Filesystem/CResourceLoader.cpp

@@ -2,36 +2,69 @@
 #include "CResourceLoader.h"
 #include "CFileInfo.h"
 #include "CLodArchiveLoader.h"
+#include "CFilesystemLoader.h"
 
-CResourceLoader * CResourceLoaderFactory::resourceLoader = nullptr;
+//For filesystem initialization
+#include "../JsonNode.h"
+#include "../GameConstants.h"
+#include "../VCMIDirs.h"
 
-ResourceIdentifier::ResourceIdentifier()
+//experimental support for ERA-style mods. Requires custom config in mod directory
+#define ENABLE_ERA_FILESYSTEM
+
+CResourceLoader * CResourceHandler::resourceLoader = nullptr;
+CResourceLoader * CResourceHandler::initialLoader = nullptr;
+
+ResourceID::ResourceID()
     :type(EResType::OTHER)
 {
 }
 
-ResourceIdentifier::ResourceIdentifier(const std::string & name, EResType type) : name(name), type(type)
+ResourceID::ResourceID(const std::string & name)
 {
-	boost::to_upper(this->name);
+	CFileInfo info(name);
+	setName(info.getStem());
+	setType(info.getType());
 }
 
-std::string ResourceIdentifier::getName() const
+ResourceID::ResourceID(const std::string & name, EResType::Type type)
+{
+	setName(name);
+	setType(type);
+}
+
+ResourceID::ResourceID(const std::string & prefix, const std::string & name, EResType::Type type)
+{
+	setName(name);
+	this->name = prefix + this->name;
+
+	setType(type);
+}
+
+std::string ResourceID::getName() const
 {
 	return name;
 }
 
-EResType ResourceIdentifier::getType() const
+EResType::Type ResourceID::getType() const
 {
 	return type;
 }
 
-void ResourceIdentifier::setName(const std::string & name)
+void ResourceID::setName(const std::string & name)
 {
 	this->name = 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 ResourceIdentifier::setType(EResType type)
+void ResourceID::setType(EResType::Type type)
 {
 	this->type = type;
 }
@@ -43,13 +76,13 @@ CResourceLoader::CResourceLoader()
 CResourceLoader::~CResourceLoader()
 {
 	// Delete all loader objects
-	BOOST_FOREACH ( ISimpleResourceLoader* it, loaders)
+	BOOST_FOREACH ( auto & it, loaders)
 	{
 		delete it;
 	}
 }
 
-std::unique_ptr<CInputStream> CResourceLoader::load(const ResourceIdentifier & resourceIdent) const
+std::unique_ptr<CInputStream> CResourceLoader::load(const ResourceID & resourceIdent) const
 {
 	auto resource = resources.find(resourceIdent);
 
@@ -66,31 +99,60 @@ std::unique_ptr<CInputStream> CResourceLoader::load(const ResourceIdentifier & r
 	return locator.getLoader()->load(locator.getResourceName());
 }
 
-bool CResourceLoader::existsResource(const ResourceIdentifier & resourceIdent) const
+std::pair<std::unique_ptr<ui8[]>, ui64> CResourceLoader::loadData(const ResourceID & resourceIdent) const
+{
+	auto stream = load(resourceIdent);
+	std::unique_ptr<ui8[]> data(new ui8[stream->getSize()]);
+	size_t readSize = stream->read(data.get(), stream->getSize());
+
+	assert(readSize == stream->getSize());
+	return std::make_pair(std::move(data), stream->getSize());
+}
+
+ResourceLocator CResourceLoader::getResource(const ResourceID & resourceIdent) const
+{
+	auto resource = resources.find(resourceIdent);
+
+	if (resource == resources.end())
+		return ResourceLocator(nullptr, "");
+	return resource->second.back();
+}
+
+std::string CResourceLoader::getResourceName(const ResourceID & resourceIdent) const
+{
+	auto locator = getResource(resourceIdent);
+	if (locator.getLoader())
+		return locator.getLoader()->getOrigin() + '/' + locator.getResourceName();
+	return "";
+}
+
+bool CResourceLoader::existsResource(const ResourceID & resourceIdent) const
 {
 	// Check if resource is registered
 	return resources.find(resourceIdent) != resources.end();
 }
 
-void CResourceLoader::addLoader(ISimpleResourceLoader * loader)
+void CResourceLoader::addLoader(std::string mountPoint, ISimpleResourceLoader * loader)
 {
-	loaders.push_back(loader);
+	loaders.insert(loader);
 
 	// Get entries and add them to the resources list
 	const std::list<std::string> & entries = loader->getEntries();
 
+	boost::to_upper(mountPoint);
+
 	BOOST_FOREACH (const std::string & entry, entries)
 	{
 		CFileInfo file(entry);
 
 		// Create identifier and locator and add them to the resources list
-		ResourceIdentifier ident(file.getStem(), file.getType());
+		ResourceID ident(mountPoint, file.getStem(), file.getType());
 		ResourceLocator locator(loader, entry);
 		resources[ident].push_back(locator);
 	}
 }
 
-CResourceLoader * CResourceLoaderFactory::getInstance()
+CResourceLoader * CResourceHandler::get()
 {
 	if(resourceLoader != nullptr)
 	{
@@ -105,10 +167,10 @@ CResourceLoader * CResourceLoaderFactory::getInstance()
 	}
 }
 
-void CResourceLoaderFactory::setInstance(CResourceLoader * resourceLoader)
-{
-	CResourceLoaderFactory::resourceLoader = resourceLoader;
-}
+//void CResourceLoaderFactory::setInstance(CResourceLoader * resourceLoader)
+//{
+//	CResourceLoaderFactory::resourceLoader = resourceLoader;
+//}
 
 ResourceLocator::ResourceLocator(ISimpleResourceLoader * loader, const std::string & resourceName)
 			: loader(loader), resourceName(resourceName)
@@ -126,11 +188,11 @@ std::string ResourceLocator::getResourceName() const
 	return resourceName;
 }
 
-EResType EResTypeHelper::getTypeFromExtension(std::string extension)
+EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
 {
 	boost::to_upper(extension);
 
-	static const std::map<std::string, EResType> stringToRes =
+	static const std::map<std::string, EResType::Type> stringToRes =
 	        boost::assign::map_list_of
 	        (".TXT",   EResType::TEXT)
 	        (".JSON",  EResType::TEXT)
@@ -146,12 +208,14 @@ EResType EResTypeHelper::getTypeFromExtension(std::string extension)
 	        (".PNG",   EResType::IMAGE)
 	        (".TGA",   EResType::IMAGE)
 	        (".WAV",   EResType::SOUND)
+	        (".82M",   EResType::SOUND)
 	        (".SMK",   EResType::VIDEO)
 	        (".BIK",   EResType::VIDEO)
 	        (".MJPG",  EResType::VIDEO)
 	        (".MP3",   EResType::MUSIC)
 	        (".OGG",   EResType::MUSIC)
 	        (".LOD",   EResType::ARCHIVE)
+	        (".PAC",   EResType::ARCHIVE)
 	        (".VID",   EResType::ARCHIVE)
 	        (".SND",   EResType::ARCHIVE)
 	        (".VCGM1", EResType::CLIENT_SAVEGAME)
@@ -164,13 +228,11 @@ EResType EResTypeHelper::getTypeFromExtension(std::string extension)
 	return iter->second;
 }
 
-std::string EResTypeHelper::getEResTypeAsString(EResType type)
+std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
 {
+#define MAP_ENUM(value) (EResType::value, #value)
 
-#define MAP_ENUM(value) (EResType::value, "value")
-
-	static const std::map<EResType, std::string> stringToRes = boost::assign::map_list_of
-		MAP_ENUM(ANY)
+	static const std::map<EResType::Type, std::string> stringToRes = boost::assign::map_list_of
 		MAP_ENUM(TEXT)
 		MAP_ENUM(ANIMATION)
 		MAP_ENUM(MASK)
@@ -193,5 +255,80 @@ std::string EResTypeHelper::getEResTypeAsString(EResType type)
 	assert(iter != stringToRes.end());
 
 	return iter->second;
+}
+
+void CResourceHandler::initialize()
+{
+	//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;
+
+	auto rootDir = new CFilesystemLoader(GameConstants::DATA_DIR, 3);
+	initialLoader->addLoader("GLOBAL/", rootDir);
+	initialLoader->addLoader("ALL/", rootDir);
+
+	auto userDir = rootDir;
+
+	//add local directory to "ALL" but only if it differs from root dir (true for linux)
+	if (GameConstants::DATA_DIR != GVCMIDirs.UserPath)
+	{
+		userDir = new CFilesystemLoader(GVCMIDirs.UserPath, 3);
+		initialLoader->addLoader("ALL/", userDir);
+	}
+
+	//create "LOCAL" dir with current userDir (may be same as rootDir)
+	initialLoader->addLoader("LOCAL/", userDir);
+}
 
+void CResourceHandler::loadFileSystem(const std::string fsConfigURI)
+{
+	const JsonNode fsConfig(initialLoader->getResourceName(ResourceID(fsConfigURI, EResType::TEXT)));
+
+	BOOST_FOREACH(auto & mountPoint, fsConfig["filesystem"].Struct())
+	{
+		BOOST_FOREACH(auto & entry, mountPoint.second.Vector())
+		{
+			tlog5 << "loading resource at " << entry["path"].String() << ": ";
+			std::string filename = initialLoader->getResourceName(entry["path"].String());
+
+			if (!filename.empty())
+			{
+				if (entry["type"].String() == "dir")
+				{
+					int depth = 16;
+					if (!entry["depth"].isNull())
+						depth = entry["depth"].Float();
+					resourceLoader->addLoader(mountPoint.first, new CFilesystemLoader(filename, depth));
+				}
+
+				if (entry["type"].String() == "file")
+					resourceLoader->addLoader(mountPoint.first, new CLodArchiveLoader(filename));
+
+				tlog5 << "OK\n";
+			}
+			else
+				tlog5 << "Not found\n";
+		}
+	}
+}
+
+void CResourceHandler::loadModsFilesystems()
+{
+#ifdef ENABLE_ERA_FILESYSTEM
+
+	auto iterator = initialLoader->getIterator([](const ResourceID & ident)
+	{
+		return ident.getType() == EResType::TEXT
+		    && boost::algorithm::starts_with(ident.getName(), "ALL/MODS/")
+		    && boost::algorithm::ends_with(ident.getName(), "FILESYSTEM");
+	});
+
+	while (iterator.hasNext())
+	{
+		tlog1 << "Found mod filesystem: " << iterator->getName() << "\n";
+		loadFileSystem(iterator->getName());
+		++iterator;
+	}
+#endif
 }

+ 173 - 50
lib/Filesystem/CResourceLoader.h

@@ -13,6 +13,7 @@
 
 #include "CInputStream.h"
 
+class CResourceLoader;
 class ResourceLocator;
 class ISimpleResourceLoader;
 
@@ -23,47 +24,56 @@ class ISimpleResourceLoader;
  *
  * Text: .txt .json
  * Animation: .def
- * Mask: .msk
+ * Mask: .msk .msg
  * Campaign: .h3c
  * Map: .h3m
  * Font: .fnt
  * Image: .bmp, .jpg, .pcx, .png, .tga
- * Sound: .wav
+ * Sound: .wav .82m
  * Video: .smk, .bik .mjpg
  * Music: .mp3, .ogg
- * Archive: .lod, .snd, .vid
+ * Archive: .lod, .snd, .vid .pac
  * Savegame: .v*gm1
  */
-enum EResType
+namespace EResType
 {
-	ANY,
-	TEXT,
-	ANIMATION,
-	MASK,
-	CAMPAIGN,
-	MAP,
-	FONT,
-	IMAGE,
-	VIDEO,
-	SOUND,
-	MUSIC,
-	ARCHIVE,
-	CLIENT_SAVEGAME,
-	LIB_SAVEGAME,
-	SERVER_SAVEGAME,
-	OTHER
-};
+	enum Type
+	{
+		TEXT,
+		ANIMATION,
+		MASK,
+		CAMPAIGN,
+		MAP,
+		FONT,
+		IMAGE,
+		VIDEO,
+		SOUND,
+		MUSIC,
+		ARCHIVE,
+		CLIENT_SAVEGAME,
+		LIB_SAVEGAME,
+		SERVER_SAVEGAME,
+		OTHER
+	};
+}
 
 /**
  * A struct which identifies a resource clearly.
  */
-class DLL_LINKAGE ResourceIdentifier
+class DLL_LINKAGE ResourceID
 {
 public:
 	/**
 	 * Default c-tor.
 	 */
-	ResourceIdentifier();
+	ResourceID();
+
+	/**
+	 * Ctor. Can be used to implicitly create indentifier for resource loading
+	 *
+	 * @param name The resource name including extension.
+	 */
+	ResourceID(const std::string & fullName);
 
 	/**
 	 * Ctor.
@@ -71,7 +81,7 @@ public:
 	 * @param name The resource name.
 	 * @param type The resource type. A constant from the enumeration EResType.
 	 */
-	ResourceIdentifier(const std::string & name, EResType type);
+	ResourceID(const std::string & name, EResType::Type type);
 
 	/**
 	 * Compares this object with a another resource identifier.
@@ -79,7 +89,7 @@ public:
 	 * @param other The other resource identifier.
 	 * @return Returns true if both are equally, false if not.
 	 */
-	inline bool operator==(ResourceIdentifier const & other) const
+	inline bool operator==(ResourceID const & other) const
 	{
 		return name == other.name && type == other.type;
 	}
@@ -96,7 +106,7 @@ public:
 	 *
 	 * @return the type of the identifier
 	 */
-	EResType getType() const;
+	EResType::Type getType() const;
 
 	/**
 	 * Sets the name of the identifier.
@@ -110,8 +120,19 @@ public:
 	 *
 	 * @param type the type of the identifier.
 	 */
-	void setType(EResType type);
+	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.
+	 * @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;
@@ -120,7 +141,7 @@ private:
 	 * 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;
+	EResType::Type type;
 };
 
 namespace std
@@ -128,17 +149,17 @@ namespace std
 	/**
 	 * Template specialization for std::hash.
 	 */
-    template <>
-    class hash<ResourceIdentifier>
-    {
+	template <>
+	class hash<ResourceID>
+	{
 public:
-    	/**
-    	 * Generates a hash value for the resource identifier object.
-    	 *
-    	 * @param resourceIdent The object from which a hash value should be generated.
-    	 * @return the generated hash value
-    	 */
-		size_t operator()(const ResourceIdentifier & resourceIdent) const
+		/**
+		 * Generates a hash value for the resource identifier object.
+		 *
+		 * @param resourceIdent The object from which a hash value should be generated.
+		 * @return the generated hash value
+		 */
+		size_t operator()(const ResourceID & resourceIdent) const
 		{
 			return hash<string>()(resourceIdent.getName()) ^ hash<int>()(static_cast<int>(resourceIdent.getType()));
 		}
@@ -151,7 +172,67 @@ public:
  */
 class DLL_LINKAGE CResourceLoader : public boost::noncopyable
 {
+	typedef std::unordered_map<ResourceID, std::list<ResourceLocator> > ResourcesMap;
+
 public:
+	/// class for iterating over all available files/Identifiers
+	/// can be created via CResourceLoader::getIterator
+	template <typename Comparator, typename Iter>
+	class Iterator
+	{
+	public:
+		/// find next available item.
+		Iterator& operator++()
+		{
+			assert(begin != end);
+			begin++;
+			findNext();
+			return *this;
+		}
+		bool hasNext()
+		{
+			return begin != end;
+		}
+
+		/// get identifier of current item
+		const ResourceID & operator* () const
+		{
+			assert(begin != end);
+			return begin->first;
+		}
+
+		/// get identifier of current item
+		const ResourceID * operator -> () const
+		{
+			assert(begin != end);
+			return &begin->first;
+		}
+
+	protected:
+		Iterator(Iter begin, Iter end, Comparator comparator):
+		    begin(begin),
+		    end(end),
+		    comparator(comparator)
+		{
+			//find first applicable item
+			findNext();
+		}
+
+		friend class CResourceLoader;
+
+	private:
+		Iter begin;
+		Iter end;
+		Comparator comparator;
+
+		void findNext()
+		{
+			while (begin != end && !comparator(begin->first))
+				begin++;
+		}
+
+	};
+
 	CResourceLoader();
 
 	/**
@@ -167,7 +248,35 @@ public:
 	 *
 	 * @throws std::runtime_error if the resource doesn't exists
 	 */
-	std::unique_ptr<CInputStream> load(const ResourceIdentifier & resourceIdent) const;
+	std::unique_ptr<CInputStream> load(const ResourceID & resourceIdent) const;
+	/// temporary member to ease transition to new filesystem classes
+	std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const ResourceID & resourceIdent) const;
+
+	/**
+	 * Get resource locator for this identifier
+	 *
+	 * @param resourceIdent This parameter identifies the resource to load.
+	 * @return resource locator for this resource or empty one if resource was not found
+	 */
+	ResourceLocator getResource(const ResourceID & resourceIdent) const;
+	/// returns real name of file in filesystem. Not usable for archives
+	std::string getResourceName(const ResourceID & resourceIdent) const;
+	/// return size of file or 0 if not found
+
+	/**
+	 * Get iterator for looping all files matching filter
+	 * Notes:
+	 * - iterating over all files may be slow. Use with caution
+	 * - all filenames are in upper case
+	 *
+	 * @param filter functor with signature bool(ResourceIdentifier) used to check if this file is required
+	 * @return resource locator for this resource or empty one if resource was not found
+	 */
+	template<typename Comparator>
+	Iterator<Comparator, ResourcesMap::const_iterator> getIterator(Comparator filter) const
+	{
+		return Iterator<Comparator, ResourcesMap::const_iterator>(resources.begin(), resources.end(), filter);
+	}
 
 	/**
 	 * Tests whether the specified resource exists.
@@ -175,17 +284,19 @@ public:
 	 * @param resourceIdent the resource which should be checked
 	 * @return true if the resource exists, false if not
 	 */
-	bool existsResource(const ResourceIdentifier & resourceIdent) const;
+	bool existsResource(const ResourceID & resourceIdent) const;
 
 	/**
 	 * 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(ISimpleResourceLoader * loader);
+	void addLoader(std::string mountPoint, ISimpleResourceLoader * loader);
 
 private:
 
@@ -193,10 +304,10 @@ private:
 	 * Contains lists of same resources which can be accessed uniquely by an
 	 * resource identifier.
 	 */
-	std::unordered_map<ResourceIdentifier, std::list<ResourceLocator> > resources;
+	ResourcesMap resources;
 
 	/** A list of resource loader objects */
-	std::list<ISimpleResourceLoader *> loaders;
+	std::set<ISimpleResourceLoader *> loaders;
 };
 
 /**
@@ -209,7 +320,7 @@ private:
  *
  * This class is not thread-safe. Make sure nobody is calling getInstance while somebody else is calling setInstance.
  */
-class DLL_LINKAGE CResourceLoaderFactory
+class DLL_LINKAGE CResourceHandler
 {
 public:
 	/**
@@ -219,18 +330,30 @@ public:
 	 *
 	 * @return Returns an instance of resource loader.
 	 */
-	static CResourceLoader * getInstance();
+	static CResourceLoader * get();
 
 	/**
-	 * Sets an instance of resource loader.
+	 * Creates instance of resource loader.
+	 * Will not fill filesystem with data
 	 *
-	 * @param resourceLoader An instance of resource loader.
 	 */
-	static void setInstance(CResourceLoader * resourceLoader);
+	static void initialize();
+
+	/**
+	 * Will load all filesystem data from Json data at this path (config/filesystem.json)
+	 */
+	static void loadFileSystem(const std::string fsConfigURI);
+
+	/**
+	 * Experimental. Checks all subfolders of MODS directory for presence of ERA-style mods
+	 * If this directory has filesystem.json file it will be added to resources
+	 */
+	static void loadModsFilesystems();
 
 private:
 	/** Instance of resource loader */
 	static CResourceLoader * resourceLoader;
+	static CResourceLoader * initialLoader;
 };
 
 /**
@@ -284,7 +407,7 @@ public:
 	 * @param extension The extension string e.g. .BMP, .PNG
 	 * @return Returns a EResType enum object
 	 */
-	static EResType getTypeFromExtension(std::string extension);
+	static EResType::Type getTypeFromExtension(std::string extension);
 
 	/**
 	 * Gets the EResType as a string representation.
@@ -292,5 +415,5 @@ public:
 	 * @param type the EResType
 	 * @return the type as a string representation
 	 */
-	static std::string getEResTypeAsString(EResType type);
+	static std::string getEResTypeAsString(EResType::Type type);
 };

+ 19 - 8
lib/Filesystem/ISimpleResourceLoader.cpp

@@ -1,14 +1,16 @@
 #include "StdInc.h"
 #include "ISimpleResourceLoader.h"
+#include "CMemoryStream.h"
 
 #include <zlib.h>
 
-std::pair<ui8*, size_t> ISimpleResourceLoader::decompressFile(ui8 * in, size_t size, size_t realSize)
+std::pair<ui8*, size_t> ISimpleResourceLoader::decompress(ui8 * in, size_t size, size_t realSize)
 {
 	std::pair<ui8*, size_t> retError(nullptr, 0);
 
+	//realSize was not set. Use compressed size as basisto gen something usable
 	if (realSize == 0)
-		realSize = 16 * 1024;
+		realSize = size * 2;
 
 	std::unique_ptr<ui8[]> out(new ui8[realSize]);
 
@@ -42,6 +44,10 @@ std::pair<ui8*, size_t> ISimpleResourceLoader::decompressFile(ui8 * in, size_t s
 		// Run inflate() on input until output buffer not full
 		do
 		{
+			//TODO: check if  inflate have any special mode for inflating (e.g Z_BLOCK)
+			//campaigns which consist from several blocks (campaign header + every map)
+			//technically .h3c consists from multiple cancatenated gz streams
+			//inflatings them separately can be useful for campaigns loading
 			strm.avail_out = realSize - lastPosOut;
 			strm.next_out = out.get() + lastPosOut;
 			ret = inflate(&strm, Z_NO_FLUSH);
@@ -49,15 +55,12 @@ std::pair<ui8*, size_t> ISimpleResourceLoader::decompressFile(ui8 * in, size_t s
 			bool breakLoop = false;
 			switch (ret)
 			{
-			case Z_STREAM_END:
+			case Z_STREAM_END: //end decompression
 				breakLoop = true;
+			case Z_OK: //decompress next chunk
 				break;
-			case Z_NEED_DICT:
-				ret = Z_DATA_ERROR;
-			case Z_DATA_ERROR:
-				inflateEnd(&strm);
-				return retError;
 			case Z_MEM_ERROR:
+			case Z_BUF_ERROR:
 				{
 					//not enough memory. Allocate bigger buffer and try again
 					realSize *= 2;
@@ -65,6 +68,11 @@ std::pair<ui8*, size_t> ISimpleResourceLoader::decompressFile(ui8 * in, size_t s
 					std::copy(out.get(), out.get() + strm.total_out, newOut.get());
 					out.reset(newOut.release());
 				}
+			default:
+				ret = Z_DATA_ERROR;
+				inflateEnd(&strm);
+				return retError;
+
 			}
 
 			if(breakLoop)
@@ -83,11 +91,14 @@ std::pair<ui8*, size_t> ISimpleResourceLoader::decompressFile(ui8 * in, size_t s
 	{
 		ret = inflateEnd(&strm);
 
+		//FIXMWE: duplicates previous  switch block
 		switch (ret)
 		{
 		case Z_STREAM_END:
+		case Z_OK:
 			//TODO: trim buffer? may be too time consuming
 			return std::make_pair(out.release(), realSize - strm.avail_out);
+		case Z_MEM_ERROR:
 		case Z_BUF_ERROR:
 			{
 				//not enough memory. Allocate bigger buffer and try again

+ 11 - 3
lib/Filesystem/ISimpleResourceLoader.h

@@ -18,17 +18,18 @@
  */
 class DLL_LINKAGE ISimpleResourceLoader
 {
-public:
+
 	/**
 	 * Decompresses an archive entry.
 	 *
 	 * @param in A pointer to the compressed data array
 	 * @param size The size of the compressed data array
-	 * @param realSize The size of the decompressed real size of the entry or 0 if not known (slower)
+	 * @param realSize - hint decompressed size (if known)
 	 * @returns pair(decompressed data, size)
 	 */
-	static std::pair<ui8*, size_t> decompressFile(ui8 * in, size_t size, size_t realSize);
+	static std::pair<ui8*, size_t> decompress(ui8 * in, size_t size, size_t realSize = 0);
 
+public:
 	/**
 	 * Dtor.
 	 */
@@ -55,4 +56,11 @@ public:
 	 * @return Returns a list of all entries in the archive or (file) system.
 	 */
 	virtual std::list<std::string> getEntries() const =0;
+
+	/**
+	 * Gets the origin of the loader.
+	 *
+	 * @return the file path to source of this loader
+	 */
+	virtual std::string getOrigin() const =0;
 };

+ 2 - 2
lib/Makefile.am

@@ -8,12 +8,12 @@ libvcmi_la_CXXFLAGS = @SDL_CFLAGS@ -DVCMI_DLL
 libvcmi_la_SOURCES = \
 	../Global.h \
 	Filesystem/CBinaryReader.cpp \
+	Filesystem/CCompressedStream.cpp \
 	Filesystem/CFileInfo.cpp \
 	Filesystem/CFileInputStream.cpp \
 	Filesystem/CFilesystemLoader.cpp \
 	Filesystem/CLodArchiveLoader.cpp \
-	Filesystem/CLodStream.cpp \
-	Filesystem/CMemoryStream.cpp \
+		Filesystem/CMemoryStream.cpp \
 	Filesystem/CResourceLoader.cpp \
 	Filesystem/ISimpleResourceLoader.cpp \
 	BattleAction.cpp \

+ 14 - 14
lib/Makefile.in

@@ -86,10 +86,10 @@ am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 libvcmi_la_DEPENDENCIES =
 am_libvcmi_la_OBJECTS = libvcmi_la-CBinaryReader.lo \
-	libvcmi_la-CFileInfo.lo libvcmi_la-CFileInputStream.lo \
-	libvcmi_la-CFilesystemLoader.lo \
-	libvcmi_la-CLodArchiveLoader.lo libvcmi_la-CLodStream.lo \
-	libvcmi_la-CMemoryStream.lo libvcmi_la-CResourceLoader.lo \
+	libvcmi_la-CCompressedStream.lo libvcmi_la-CFileInfo.lo \
+	libvcmi_la-CFileInputStream.lo libvcmi_la-CFilesystemLoader.lo \
+	libvcmi_la-CLodArchiveLoader.lo libvcmi_la-CMemoryStream.lo \
+	libvcmi_la-CResourceLoader.lo \
 	libvcmi_la-ISimpleResourceLoader.lo libvcmi_la-BattleAction.lo \
 	libvcmi_la-BattleState.lo libvcmi_la-CArtHandler.lo \
 	libvcmi_la-CBuildingHandler.lo libvcmi_la-CCampaignHandler.lo \
@@ -301,12 +301,12 @@ libvcmi_la_CXXFLAGS = @SDL_CFLAGS@ -DVCMI_DLL
 libvcmi_la_SOURCES = \
 	../Global.h \
 	Filesystem/CBinaryReader.cpp \
+	Filesystem/CCompressedStream.cpp \
 	Filesystem/CFileInfo.cpp \
 	Filesystem/CFileInputStream.cpp \
 	Filesystem/CFilesystemLoader.cpp \
 	Filesystem/CLodArchiveLoader.cpp \
-	Filesystem/CLodStream.cpp \
-	Filesystem/CMemoryStream.cpp \
+		Filesystem/CMemoryStream.cpp \
 	Filesystem/CResourceLoader.cpp \
 	Filesystem/ISimpleResourceLoader.cpp \
 	BattleAction.cpp \
@@ -461,6 +461,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CBinaryReader.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CBuildingHandler.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CCampaignHandler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CCompressedStream.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CConsoleHandler.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CCreatureHandler.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CCreatureSet.Plo@am__quote@
@@ -475,7 +476,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CHeroHandler.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CLodArchiveLoader.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CLodHandler.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CLodStream.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CLogger.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CMapInfo.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-CMemoryStream.Plo@am__quote@
@@ -523,6 +523,13 @@ libvcmi_la-CBinaryReader.lo: Filesystem/CBinaryReader.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-CBinaryReader.lo `test -f 'Filesystem/CBinaryReader.cpp' || echo '$(srcdir)/'`Filesystem/CBinaryReader.cpp
 
+libvcmi_la-CCompressedStream.lo: Filesystem/CCompressedStream.cpp
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-CCompressedStream.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-CCompressedStream.Tpo -c -o libvcmi_la-CCompressedStream.lo `test -f 'Filesystem/CCompressedStream.cpp' || echo '$(srcdir)/'`Filesystem/CCompressedStream.cpp
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-CCompressedStream.Tpo $(DEPDIR)/libvcmi_la-CCompressedStream.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='Filesystem/CCompressedStream.cpp' object='libvcmi_la-CCompressedStream.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-CCompressedStream.lo `test -f 'Filesystem/CCompressedStream.cpp' || echo '$(srcdir)/'`Filesystem/CCompressedStream.cpp
+
 libvcmi_la-CFileInfo.lo: Filesystem/CFileInfo.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-CFileInfo.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-CFileInfo.Tpo -c -o libvcmi_la-CFileInfo.lo `test -f 'Filesystem/CFileInfo.cpp' || echo '$(srcdir)/'`Filesystem/CFileInfo.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-CFileInfo.Tpo $(DEPDIR)/libvcmi_la-CFileInfo.Plo
@@ -551,13 +558,6 @@ libvcmi_la-CLodArchiveLoader.lo: Filesystem/CLodArchiveLoader.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-CLodArchiveLoader.lo `test -f 'Filesystem/CLodArchiveLoader.cpp' || echo '$(srcdir)/'`Filesystem/CLodArchiveLoader.cpp
 
-libvcmi_la-CLodStream.lo: Filesystem/CLodStream.cpp
-@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-CLodStream.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-CLodStream.Tpo -c -o libvcmi_la-CLodStream.lo `test -f 'Filesystem/CLodStream.cpp' || echo '$(srcdir)/'`Filesystem/CLodStream.cpp
-@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-CLodStream.Tpo $(DEPDIR)/libvcmi_la-CLodStream.Plo
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='Filesystem/CLodStream.cpp' object='libvcmi_la-CLodStream.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-CLodStream.lo `test -f 'Filesystem/CLodStream.cpp' || echo '$(srcdir)/'`Filesystem/CLodStream.cpp
-
 libvcmi_la-CMemoryStream.lo: Filesystem/CMemoryStream.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-CMemoryStream.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-CMemoryStream.Tpo -c -o libvcmi_la-CMemoryStream.lo `test -f 'Filesystem/CMemoryStream.cpp' || echo '$(srcdir)/'`Filesystem/CMemoryStream.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-CMemoryStream.Tpo $(DEPDIR)/libvcmi_la-CMemoryStream.Plo

+ 7 - 18
lib/VCMI_Lib.cpp

@@ -15,6 +15,7 @@
 #include "IGameEventsReceiver.h"
 #include "CStopWatch.h"
 #include "VCMIDirs.h"
+#include "Filesystem/CResourceLoader.h"
 
 /*
  * VCMI_Lib.cpp, part of VCMI engine
@@ -26,11 +27,7 @@
  *
  */
 
-class CLodHandler;
 LibClasses * VLC = NULL;
-DLL_LINKAGE CLodHandler *bitmaph = NULL, 
-	*spriteh = NULL,
-	*bitmaph_ab = NULL;
 
 DLL_LINKAGE VCMIDirs GVCMIDirs;
 
@@ -166,6 +163,12 @@ void LibClasses::init()
 {
 	CStopWatch pomtime;
 
+	CResourceHandler::initialize();
+	CResourceHandler::loadFileSystem("ALL/config/filesystem.json");
+	CResourceHandler::loadModsFilesystems();
+
+	tlog0<<"\tFile system handler: "<<pomtime.getDiff()<<std::endl;
+
 	generaltexth = new CGeneralTextHandler;
 	generaltexth->load();
 	tlog0<<"\tGeneral text handler: "<<pomtime.getDiff()<<std::endl;
@@ -236,20 +239,6 @@ void LibClasses::makeNull()
 
 LibClasses::LibClasses()
 {
-	//load .lod archives
-
-	if(!spriteh) //don't reload lods if we are starting a secoond game
-	{
-		CStopWatch pomtime;
-		spriteh = new CLodHandler();
-		spriteh->init(GameConstants::DATA_DIR + "/Data/H3sprite.lod", GameConstants::DATA_DIR + "/Sprites");
-		bitmaph = new CLodHandler;
-		bitmaph->init(GameConstants::DATA_DIR + "/Data/H3bitmap.lod", GameConstants::DATA_DIR + "/Data");
-		bitmaph_ab = new CLodHandler();
-		bitmaph_ab->init(GameConstants::DATA_DIR + "/Data/H3ab_bmp.lod", GameConstants::DATA_DIR + "/Data");
-		tlog0<<"Loading .lod files: "<<pomtime.getDiff()<<std::endl;
-	}
-
 	//init pointers to handlers
 	makeNull();
 }

+ 0 - 2
lib/VCMI_Lib.h

@@ -10,7 +10,6 @@
  *
  */
 
-class CLodHandler;
 class CArtHandler;
 class CHeroHandler;
 class CCreatureHandler;
@@ -55,7 +54,6 @@ public:
 };
 
 extern DLL_LINKAGE LibClasses * VLC;
-extern DLL_LINKAGE CLodHandler * bitmaph, *spriteh, *bitmaph_ab;
 
 DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
 DLL_LINKAGE void loadToIt(si32 &dest, const std::string &src, int &iter, int mode);

+ 9 - 7
lib/map.cpp

@@ -1,10 +1,11 @@
 #include "StdInc.h"
 #include "map.h"
 
+#include "Filesystem/CResourceLoader.h"
+#include "Filesystem/CCompressedStream.h"
 #include "CObjectHandler.h"
 #include "CDefObjInfoHandler.h"
 #include "VCMI_Lib.h"
-#include <zlib.h>
 #include <boost/crc.hpp>
 #include "CLodHandler.h"
 #include "CArtHandler.h"
@@ -502,18 +503,19 @@ void Mapa::addBlockVisTiles(CGObjectInstance * obj)
 Mapa::Mapa(std::string filename)
 	:grailPos(-1, -1, -1), grailRadious(0)
 {
-	int mapsize = 0;
-
 	tlog0<<"Opening map file: "<<filename<<"\t "<<std::flush;
 	
+	std::unique_ptr<CInputStream> compressed(CResourceHandler::get()->load(ResourceID(filename, EResType::MAP)));
+	std::unique_ptr<CInputStream> decompressed(new CCompressedStream(compressed, true));
+
 	//load file and decompress
-	ui8 * initTable = CLodHandler::getUnpackedFile(filename, &mapsize);
+	size_t mapSize = decompressed->getSize();
+	std::unique_ptr<ui8[]>  data(new ui8 [mapSize] );
+	decompressed->read(data.get(), mapSize);
 
 	tlog0<<"done."<<std::endl;
 
-	initFromBytes(initTable, mapsize);
-
-	delete [] initTable;
+	initFromBytes(data.get(), mapSize);
 }
 
 Mapa::Mapa()

+ 6 - 14
server/CVCMIServer.cpp

@@ -1,5 +1,6 @@
 #include "StdInc.h"
 
+#include "../lib/Filesystem/CResourceLoader.h"
 #include "../lib/CCampaignHandler.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/Connection.h"
@@ -312,25 +313,16 @@ CGameHandler * CVCMIServer::initGhFromHostingConnection(CConnection &c)
 	CGameHandler *gh = new CGameHandler();
 	StartInfo si;
 	c >> si; //get start options
-	int problem;
 
-#ifdef _MSC_VER
-	FILE *f;
-	problem = fopen_s(&f,si.mapname.c_str(),"r");
-#else
-	FILE * f = fopen(si.mapname.c_str(),"r");
-	problem = !f;
-#endif
+	bool mapFound = CResourceHandler::get()->existsResource(ResourceID(si.mapname, EResType::MAP));
 
-	if(problem && si.mode == StartInfo::NEW_GAME) //TODO some checking for campaigns
+	if(!mapFound && si.mode == StartInfo::NEW_GAME) //TODO some checking for campaigns
 	{
-		c << ui8(problem); //WRONG!
+		c << ui8(1); //WRONG!
 		return NULL;
 	}
 	else
 	{
-		if(f)	
-			fclose(f);
 		c << ui8(0); //OK!
 	}
 
@@ -453,7 +445,7 @@ void CVCMIServer::loadGame()
 		CMapHeader dum;
 		StartInfo *si;
 
-		CLoadFile lf(fname + ".vlgm1");
+		CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
 		lf >> sig >> dum >> si;
 		tlog0 <<"Reading save signature"<<std::endl;
 
@@ -466,7 +458,7 @@ void CVCMIServer::loadGame()
 	}
 
 	{
-		CLoadFile lf(fname + ".vsgm1");
+		CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
 		lf >> gh;
 	}