about summary refs log tree commit diff
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2010-10-06 23:07:35 +0200
committeroy <Tom_Adams@web.de>2010-10-06 23:07:35 +0200
commit3a98f7a048c044ce0d870320fc0b1def0db4f7fb (patch)
tree7dfa1826e537355b37d00e77558ca623a915bac2
parenta62a7413d1ff25c8e07657b96a485f001004619a (diff)
downloadzcatch-3a98f7a048c044ce0d870320fc0b1def0db4f7fb.tar.gz
zcatch-3a98f7a048c044ce0d870320fc0b1def0db4f7fb.zip
added mod system. Closes #34
-rw-r--r--scripts/make_release.py2
-rw-r--r--src/engine/client.h5
-rw-r--r--src/engine/client/client.cpp17
-rw-r--r--src/engine/client/client.h4
-rw-r--r--src/engine/client/graphics.cpp12
-rw-r--r--src/engine/client/graphics.h4
-rw-r--r--src/engine/client/sound.cpp2
-rw-r--r--src/engine/client/text.cpp2
-rw-r--r--src/engine/graphics.h4
-rw-r--r--src/engine/server/server.cpp2
-rw-r--r--src/engine/shared/config.cpp2
-rw-r--r--src/engine/shared/console.cpp2
-rw-r--r--src/engine/shared/datafile.cpp6
-rw-r--r--src/engine/shared/datafile.h2
-rw-r--r--src/engine/shared/demo.cpp14
-rw-r--r--src/engine/shared/demo.h2
-rw-r--r--src/engine/shared/map.cpp2
-rw-r--r--src/engine/shared/masterserver.cpp4
-rw-r--r--src/engine/shared/storage.cpp290
-rw-r--r--src/engine/storage.h13
-rw-r--r--src/game/client/components/console.cpp2
-rw-r--r--src/game/client/components/mapimages.cpp3
-rw-r--r--src/game/client/components/maplayers.cpp2
-rw-r--r--src/game/client/components/menus.cpp3
-rw-r--r--src/game/client/components/menus.h4
-rw-r--r--src/game/client/components/menus_demo.cpp18
-rw-r--r--src/game/client/components/menus_settings.cpp6
-rw-r--r--src/game/client/components/skins.cpp2
-rw-r--r--src/game/client/gameclient.cpp11
-rw-r--r--src/game/editor/ed_editor.cpp46
-rw-r--r--src/game/editor/ed_editor.h22
-rw-r--r--src/game/editor/ed_io.cpp4
-rw-r--r--src/game/localization.cpp5
-rw-r--r--src/game/localization.h2
-rw-r--r--src/tools/map_resave.cpp2
-rw-r--r--storage.cfg31
36 files changed, 341 insertions, 213 deletions
diff --git a/scripts/make_release.py b/scripts/make_release.py
index 7bc74b15..1a135904 100644
--- a/scripts/make_release.py
+++ b/scripts/make_release.py
@@ -61,6 +61,7 @@ os.mkdir(package_dir)
 print "adding files"
 shutil.copy("readme.txt", package_dir)
 shutil.copy("license.txt", package_dir)
+shutil.copy("storage.cfg", package_dir)
 
 if include_data and not use_bundle:
 	os.mkdir(os.path.join(package_dir, "data"))
@@ -133,6 +134,7 @@ if use_bundle:
 	os.mkdir(serverbundle_resource_dir)
 	os.mkdir(os.path.join(serverbundle_resource_dir, "data"))
 	os.mkdir(os.path.join(serverbundle_resource_dir, "data/maps"))
+	os.mkdir(os.path.join(serverbundle_resource_dir, "data/mapres"))
 	copydir("data/maps", serverbundle_resource_dir)
 	shutil.copy("other/icons/Teeworlds_srv.icns", serverbundle_resource_dir)
 	shutil.copy(name+"_srv"+exe_ext, serverbundle_bin_dir)
diff --git a/src/engine/client.h b/src/engine/client.h
index fe8e91f2..11ba7385 100644
--- a/src/engine/client.h
+++ b/src/engine/client.h
@@ -73,7 +73,7 @@ public:
 	virtual void Connect(const char *pAddress) = 0;
 	virtual void Disconnect() = 0;
 	virtual void Quit() = 0;
-	virtual const char *DemoPlayer_Play(const char *pFilename) = 0;
+	virtual const char *DemoPlayer_Play(const char *pFilename, int StorageType) = 0;
 	virtual void DemoRecorder_Start(const char *pFilename) = 0;
 
 	// networking
@@ -121,9 +121,6 @@ public:
 		return SendMsg(&Packer, Flags);
 	}
 	
-	//
-	virtual const char *UserDirectory() = 0;
-	
 	// 
 	virtual const char *ErrorString() = 0;
 	virtual const char *LatestVersion() = 0;
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 4952446f..7c0c632e 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -585,7 +585,7 @@ void CClient::ServerInfoRequest()
 
 int CClient::LoadData()
 {
-	m_DebugFont = Graphics()->LoadTexture("debug_font.png", CImageInfo::FORMAT_AUTO, IGraphics::TEXLOAD_NORESAMPLE);
+	m_DebugFont = Graphics()->LoadTexture("debug_font.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, IGraphics::TEXLOAD_NORESAMPLE);
 	return 1;
 }
 
@@ -990,7 +990,7 @@ void CClient::ProcessPacket(CNetChunk *pPacket)
 
 						m_MapdownloadChunk = 0;
 						str_copy(m_aMapdownloadName, pMap, sizeof(m_aMapdownloadName));
-						m_MapdownloadFile = Storage()->OpenFile(m_aMapdownloadFilename, IOFLAG_WRITE);
+						m_MapdownloadFile = Storage()->OpenFile(m_aMapdownloadFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 						m_MapdownloadCrc = MapCrc;
 						m_MapdownloadTotalsize = -1;
 						m_MapdownloadAmount = 0;
@@ -1537,13 +1537,6 @@ void CClient::Update()
 	m_ServerBrowser.Update();
 }
 
-const char *CClient::UserDirectory()
-{
-	static char saPath[1024] = {0};
-	fs_storage_path("Teeworlds", saPath, sizeof(saPath));
-	return saPath;
-}
-
 void CClient::VersionUpdate()
 {
 	if(m_VersionInfo.m_State == 0)
@@ -1871,7 +1864,7 @@ void CClient::Con_AddFavorite(IConsole::IResult *pResult, void *pUserData)
 		pSelf->m_ServerBrowser.AddFavorite(Addr);
 }
 
-const char *CClient::DemoPlayer_Play(const char *pFilename)
+const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType)
 {
 	int Crc;
 	const char *pError;
@@ -1881,7 +1874,7 @@ const char *CClient::DemoPlayer_Play(const char *pFilename)
 	// try to start playback
 	m_DemoPlayer.SetListner(this);
 
-	if(m_DemoPlayer.Load(Storage(), m_pConsole, pFilename))
+	if(m_DemoPlayer.Load(Storage(), m_pConsole, pFilename, StorageType))
 		return "error loading demo";
 
 	// load map
@@ -1926,7 +1919,7 @@ const char *CClient::DemoPlayer_Play(const char *pFilename)
 void CClient::Con_Play(IConsole::IResult *pResult, void *pUserData)
 {
 	CClient *pSelf = (CClient *)pUserData;
-	pSelf->DemoPlayer_Play(pResult->GetString(0));
+	pSelf->DemoPlayer_Play(pResult->GetString(0), IStorage::TYPE_ALL);
 }
 
 void CClient::DemoRecorder_Start(const char *pFilename)
diff --git a/src/engine/client/client.h b/src/engine/client/client.h
index aa381467..d9199b94 100644
--- a/src/engine/client/client.h
+++ b/src/engine/client/client.h
@@ -259,8 +259,6 @@ public:
 
 	void Update();
 
-	virtual const char *UserDirectory();
-
 	void InitEngine(const char *pAppname);
 	void RegisterInterfaces();
 	void InitInterfaces();
@@ -283,7 +281,7 @@ public:
 
 	void RegisterCommands();
 
-	const char *DemoPlayer_Play(const char *pFilename);
+	const char *DemoPlayer_Play(const char *pFilename, int StorageType);
 	void DemoRecorder_Start(const char *pFilename);
 
 	virtual class CEngine *Engine() { return &m_Engine; }
diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp
index 14a88394..665f9ce7 100644
--- a/src/engine/client/graphics.cpp
+++ b/src/engine/client/graphics.cpp
@@ -354,7 +354,7 @@ int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const vo
 }
 
 // simple uncompressed RGBA loaders
-int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StoreFormat, int Flags)
+int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags)
 {
 	int l = str_length(pFilename);
 	int Id;
@@ -362,7 +362,7 @@ int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StoreFormat, int Fl
 	
 	if(l < 3)
 		return -1;
-	if(LoadPNG(&Img, pFilename))
+	if(LoadPNG(&Img, pFilename, StorageType))
 	{
 		if (StoreFormat == CImageInfo::FORMAT_AUTO)
 			StoreFormat = Img.m_Format;
@@ -375,7 +375,7 @@ int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StoreFormat, int Fl
 	return m_InvalidTexture;
 }
 
-int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename)
+int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
 {
 	char aCompleteFilename[512];
 	unsigned char *pBuffer;
@@ -384,7 +384,7 @@ int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename)
 	// open file for reading
 	png_init(0,0); // ignore_convention
 
-	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, aCompleteFilename, sizeof(aCompleteFilename));
+	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType, aCompleteFilename, sizeof(aCompleteFilename));
 	if(File)
 		io_close(File);
 	else
@@ -450,7 +450,7 @@ void CGraphics_OpenGL::ScreenshotDirect(const char *pFilename)
 		char aWholePath[1024];
 		png_t Png; // ignore_convention
 
-		IOHANDLE File  = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, aWholePath, sizeof(aWholePath));
+		IOHANDLE File  = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath));
 		if(File)
 			io_close(File);
 	
@@ -898,7 +898,7 @@ void CGraphics_SDL::Swap()
 		{
 			IOHANDLE io;
 			str_format(aFilename, sizeof(aFilename), "screenshots/screenshot%s-%05d.png", aDate, Index);
-			io = m_pStorage->OpenFile(aFilename, IOFLAG_READ);
+			io = m_pStorage->OpenFile(aFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
 			if(io)
 				io_close(io);
 			else
diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h
index fa360a22..ef6360e8 100644
--- a/src/engine/client/graphics.h
+++ b/src/engine/client/graphics.h
@@ -87,8 +87,8 @@ public:
 	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags);
 
 	// simple uncompressed RGBA loaders
-	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags);
-	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename);
+	virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags);
+	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType);
 
 	void ScreenshotDirect(const char *pFilename);
 
diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp
index b829686c..69fd7462 100644
--- a/src/engine/client/sound.cpp
+++ b/src/engine/client/sound.cpp
@@ -325,7 +325,7 @@ int CSound::LoadWV(const char *pFilename)
 	if(!m_pStorage)
 		return -1;
 
-	ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ); // TODO: use system.h stuff for this
+	ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 	if(!ms_File)
 	{
 		dbg_msg("sound/wv", "failed to open %s", pFilename);
diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp
index bdf71dbb..0fb6b043 100644
--- a/src/engine/client/text.cpp
+++ b/src/engine/client/text.cpp
@@ -70,7 +70,7 @@ struct CFontSizeData
 class CFont
 {
 public:
-	char m_aFilename[128];
+	char m_aFilename[512];
 	FT_Face m_FtFace;
 	CFontSizeData m_aSizes[NUM_FONT_SIZES];
 };
diff --git a/src/engine/graphics.h b/src/engine/graphics.h
index 4d3c0bc0..cbd7bbff 100644
--- a/src/engine/graphics.h
+++ b/src/engine/graphics.h
@@ -74,10 +74,10 @@ public:
 	virtual void BlendAdditive() = 0;
 	virtual int MemoryUsage() const = 0;
 	
-	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename) =0;
+	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) =0;
 	virtual int UnloadTexture(int Index) = 0;
 	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0;
-	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) = 0;
+	virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0;
 	virtual void TextureSet(int TextureID) = 0;
 	
 	struct CLineItem
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index 37d5770a..c7b728d4 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -1043,7 +1043,7 @@ int CServer::LoadMap(const char *pMapName)
 	
 	// load compelate map into memory for download
 	{
-		IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ);
+		IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ, IStorage::TYPE_ALL);
 		m_CurrentMapSize = (int)io_length(File);
 		if(m_pCurrentMapData)
 			mem_free(m_pCurrentMapData);
diff --git a/src/engine/shared/config.cpp b/src/engine/shared/config.cpp
index 4ff02689..85c6b5ec 100644
--- a/src/engine/shared/config.cpp
+++ b/src/engine/shared/config.cpp
@@ -74,7 +74,7 @@ public:
 	{
 		if(!m_pStorage)
 			return;
-		m_ConfigFile = m_pStorage->OpenFile("settings.cfg", IOFLAG_WRITE);
+		m_ConfigFile = m_pStorage->OpenFile("settings.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE);
 		
 		if(!m_ConfigFile)
 			return;
diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp
index 808b64ed..e32a0f81 100644
--- a/src/engine/shared/console.cpp
+++ b/src/engine/shared/console.cpp
@@ -300,7 +300,7 @@ void CConsole::ExecuteFile(const char *pFilename)
 	m_pFirstExec = &ThisFile;
 
 	// exec the file
-	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ);
+	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 	
 	char aBuf[256];
 	if(File)
diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp
index 4ef278cd..81d0e2cf 100644
--- a/src/engine/shared/datafile.cpp
+++ b/src/engine/shared/datafile.cpp
@@ -65,11 +65,11 @@ struct CDatafile
 	char *m_pData;
 };
 
-bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename)
+bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int StorageType)
 {
 	dbg_msg("datafile", "loading. filename='%s'", pFilename);
 
-	IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ);
+	IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType);
 	if(!File)
 	{
 		dbg_msg("datafile", "could not open '%s'", pFilename);
@@ -394,7 +394,7 @@ unsigned CDataFileReader::Crc()
 bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename)
 {
 	dbg_assert(!m_File, "a file already exists");
-	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE);
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 	if(!m_File)
 		return false;
 	
diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h
index e0f08b7c..0392e493 100644
--- a/src/engine/shared/datafile.h
+++ b/src/engine/shared/datafile.h
@@ -12,7 +12,7 @@ public:
 	
 	bool IsOpen() const { return m_pDataFile != 0; }
 	
-	bool Open(class IStorage *pStorage, const char *pFilename);
+	bool Open(class IStorage *pStorage, const char *pFilename, int StorageType);
 	bool Close();
 	
 	void *GetData(int Index);
diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp
index 5ae7a340..4ae4a4c5 100644
--- a/src/engine/shared/demo.cpp
+++ b/src/engine/shared/demo.cpp
@@ -40,12 +40,12 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con
 	char aMapFilename[128];
 	// try the normal maps folder
 	str_format(aMapFilename, sizeof(aMapFilename), "maps/%s.map", pMap);
-	IOHANDLE MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ);
+	IOHANDLE MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 	if(!MapFile)
 	{
 		// try the downloaded maps
 		str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%08x.map", pMap, Crc);
-		MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ);
+		MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 	}
 	if(!MapFile)
 	{
@@ -55,7 +55,7 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con
 		return -1;
 	}
 
-	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE);
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 	if(!m_File)
 	{
 		io_close(MapFile);
@@ -513,10 +513,10 @@ void CDemoPlayer::Unpause()
 	}
 }
 
-int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
+int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType)
 {
 	m_pConsole = pConsole;
-	m_File = pStorage->OpenFile(pFilename, IOFLAG_READ);
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType);
 	if(!m_File)
 	{
 		char aBuf[256];
@@ -566,7 +566,7 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const
 		int Crc = (m_Info.m_Header.m_aCrc[0]<<24) | (m_Info.m_Header.m_aCrc[1]<<16) | (m_Info.m_Header.m_aCrc[2]<<8) | (m_Info.m_Header.m_aCrc[3]);
 		char aMapFilename[128];
 		str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%08x.map", m_Info.m_Header.m_aMap, Crc);
-		IOHANDLE MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ);
+		IOHANDLE MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 		
 		if(MapFile)
 		{
@@ -580,7 +580,7 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const
 			io_read(m_File, pMapData, MapSize);
 			
 			// save map
-			MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_WRITE);
+			MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 			io_write(MapFile, pMapData, MapSize);
 			io_close(MapFile);
 			
diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h
index eeaa1f99..9d510542 100644
--- a/src/engine/shared/demo.h
+++ b/src/engine/shared/demo.h
@@ -103,7 +103,7 @@ public:
 	
 	void SetListner(IListner *pListner);
 		
-	int Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename);
+	int Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType);
 	int Play();
 	void Pause();
 	void Unpause();
diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp
index 505d18e9..827930aa 100644
--- a/src/engine/shared/map.cpp
+++ b/src/engine/shared/map.cpp
@@ -28,7 +28,7 @@ public:
 		IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
 		if(!pStorage)
 			return false;
-		return m_DataFile.Open(pStorage, pMapName);
+		return m_DataFile.Open(pStorage, pMapName, IStorage::TYPE_ALL);
 	}
 	
 	virtual bool IsLoaded()
diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp
index f10469bd..9ecf3f71 100644
--- a/src/engine/shared/masterserver.cpp
+++ b/src/engine/shared/masterserver.cpp
@@ -119,7 +119,7 @@ public:
 			return -1;
 		
 		// try to open file
-		File = pStorage->OpenFile("masters.cfg", IOFLAG_READ);
+		File = pStorage->OpenFile("masters.cfg", IOFLAG_READ, IStorage::TYPE_SAVE);
 		if(!File)
 			return -1;
 		
@@ -165,7 +165,7 @@ public:
 			return -1;
 			
 		// try to open file
-		File = pStorage->OpenFile("masters.cfg", IOFLAG_WRITE);
+		File = pStorage->OpenFile("masters.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE);
 		if(!File)
 			return -1;
 
diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp
index 8785a5bf..cfe64e4b 100644
--- a/src/engine/shared/storage.cpp
+++ b/src/engine/shared/storage.cpp
@@ -3,6 +3,7 @@
 #include <base/system.h>
 #include <engine/storage.h>
 #include "engine.h"
+#include "linereader.h"
 
 // compiled-in data-dir path
 #define DATA_DIR "data"
@@ -10,36 +11,29 @@
 class CStorage : public IStorage
 {
 public:
-	char m_aApplicationSavePath[512];
-	char m_aDatadir[512];
+	enum
+	{
+		MAX_PATHS = 16,
+		MAX_PATH_LENGTH = 512
+	};
+
+	char m_aaStoragePaths[MAX_PATHS][MAX_PATH_LENGTH];
+	int m_NumPaths;
+	char m_aDatadir[MAX_PATH_LENGTH];
+	char m_aUserdir[MAX_PATH_LENGTH];
 	
 	CStorage()
 	{
-		m_aApplicationSavePath[0] = 0;
+		mem_zero(m_aaStoragePaths, sizeof(m_aaStoragePaths));
+		m_NumPaths = 0;
 		m_aDatadir[0] = 0;
+		m_aUserdir[0] = 0;
 	}
 	
 	int Init(const char *pApplicationName, int NumArgs, const char **ppArguments)
 	{
-		char aPath[1024] = {0};
-		fs_storage_path(pApplicationName, m_aApplicationSavePath, sizeof(m_aApplicationSavePath));
-		if(fs_makedir(m_aApplicationSavePath) == 0)
-		{		
-			str_format(aPath, sizeof(aPath), "%s/screenshots", m_aApplicationSavePath);
-			fs_makedir(aPath);
-
-			str_format(aPath, sizeof(aPath), "%s/maps", m_aApplicationSavePath);
-			fs_makedir(aPath);
-
-			str_format(aPath, sizeof(aPath), "%s/dumps", m_aApplicationSavePath);
-			fs_makedir(aPath);
-
-			str_format(aPath, sizeof(aPath), "%s/downloadedmaps", m_aApplicationSavePath);
-			fs_makedir(aPath);
-
-			str_format(aPath, sizeof(aPath), "%s/demos", m_aApplicationSavePath);
-			fs_makedir(aPath);
-		}
+		// get userdir
+		fs_storage_path(pApplicationName, m_aUserdir, sizeof(m_aUserdir));
 
 		// check for datadir override
 		for(int i = 1; i < NumArgs; i++)
@@ -50,54 +44,160 @@ public:
 				break;
 			}
 		}
-		
-		return FindDatadir(ppArguments[0]);
+		// get datadir
+		FindDatadir(ppArguments[0]);
+
+		// load paths from storage.cfg
+		LoadPaths(ppArguments[0]);
+
+		if(!m_NumPaths)
+		{
+			dbg_msg("storage", "using standard paths");
+			AddDefaultPaths();
+		}
+
+		// add save directories
+		if(m_NumPaths && (!m_aaStoragePaths[TYPE_SAVE][0] || !fs_makedir(m_aaStoragePaths[TYPE_SAVE])))
+		{
+			char aPath[MAX_PATH_LENGTH];
+			fs_makedir(GetPath(TYPE_SAVE, "screenshots", aPath, sizeof(aPath)));
+			fs_makedir(GetPath(TYPE_SAVE, "maps", aPath, sizeof(aPath)));
+			fs_makedir(GetPath(TYPE_SAVE, "dumps", aPath, sizeof(aPath)));
+			fs_makedir(GetPath(TYPE_SAVE, "downloadedmaps", aPath, sizeof(aPath)));
+			fs_makedir(GetPath(TYPE_SAVE, "demos", aPath, sizeof(aPath)));
+		}
+
+		return m_NumPaths ? 0 : 1;
+	}
+
+	void LoadPaths(const char *pArgv0)
+	{
+		// check current directory
+		IOHANDLE File = io_open("storage.cfg", IOFLAG_READ);
+		if(!File)
+		{
+			// check usable path in argv[0]
+			unsigned int Pos = ~0U;
+			for(unsigned i = 0; pArgv0[i]; i++)
+				if(pArgv0[i] == '/' || pArgv0[i] == '\\')
+					Pos = i;
+			if(Pos < MAX_PATH_LENGTH)
+			{
+				char aBuffer[MAX_PATH_LENGTH];
+				str_copy(aBuffer, pArgv0, Pos+1);
+				str_append(aBuffer, "/storage.cfg", sizeof(aBuffer));
+				File = io_open(aBuffer, IOFLAG_READ);
+			}
+			
+			if(Pos >= MAX_PATH_LENGTH || !File)
+			{
+				dbg_msg("storage", "couldn't open storage.cfg");
+				return;
+			}
+		}
+
+		char *pLine;
+		CLineReader LineReader;
+		LineReader.Init(File);
+
+		while((pLine = LineReader.Get()))
+		{
+			if(str_length(pLine) > 9 && !str_comp_num(pLine, "add_path ", 9))
+				AddPath(pLine+9);
+		}
+
+		io_close(File);
+
+		if(!m_NumPaths)
+			dbg_msg("storage", "no paths found in storage.cfg");
+	}
+
+	void AddDefaultPaths()
+	{
+		AddPath("$USERDIR");
+		AddPath("$DATADIR");
+		AddPath("$CURRENTDIR");
+	}
+
+	void AddPath(const char *pPath)
+	{
+		if(m_NumPaths >= MAX_PATHS || !pPath[0])
+			return;
+
+		int OldNum = m_NumPaths;
+
+		if(!str_comp(pPath, "$USERDIR"))
+		{
+			if(m_aUserdir[0])
+				str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir, MAX_PATH_LENGTH);
+		}
+		else if(!str_comp(pPath, "$DATADIR"))
+		{
+			if(m_aDatadir[0])
+				str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir, MAX_PATH_LENGTH);
+		}
+		else if(!str_comp(pPath, "$CURRENTDIR"))
+		{
+			m_aaStoragePaths[m_NumPaths++][0] = 0;
+		}
+		else
+		{
+			if(fs_is_dir(pPath))
+				str_copy(m_aaStoragePaths[m_NumPaths++], pPath, MAX_PATH_LENGTH);
+		}
+
+		if(OldNum != m_NumPaths)
+			dbg_msg("storage", "added path '%s'", pPath);
 	}
 		
-	int FindDatadir(const char *pArgv0)
+	void FindDatadir(const char *pArgv0)
 	{
 		// 1) use provided data-dir override
 		if(m_aDatadir[0])
 		{
-			if(fs_is_dir(m_aDatadir))
-				return 0;
-			else
+			char aBuffer[MAX_PATH_LENGTH];
+			str_format(aBuffer, sizeof(aBuffer), "%s/mapres", m_aDatadir);
+			if(!fs_is_dir(aBuffer))
 			{
-				dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", m_aDatadir);
-				return -1;
+				dbg_msg("storage", "specified data directory '%s' does not exist", m_aDatadir);
+				m_aDatadir[0] = 0;
 			}
+			else
+				return;
 		}
 		
 		// 2) use data-dir in PWD if present
 		if(fs_is_dir("data/mapres"))
 		{
 			str_copy(m_aDatadir, "data", sizeof(m_aDatadir));
-			return 0;
+			return;
 		}
 		
 		// 3) use compiled-in data-dir if present
-		if (fs_is_dir(DATA_DIR "/mapres"))
+		if(fs_is_dir(DATA_DIR "/mapres"))
 		{
 			str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir));
-			return 0;
+			return;
 		}
 		
 		// 4) check for usable path in argv[0]
 		{
 			unsigned int Pos = ~0U;
 			for(unsigned i = 0; pArgv0[i]; i++)
-				if(pArgv0[i] == '/')
+				if(pArgv0[i] == '/' || pArgv0[i] == '\\')
 					Pos = i;
 			
-			if (Pos < sizeof(m_aDatadir))
+			if(Pos < MAX_PATH_LENGTH)
 			{
-				char aBaseDir[sizeof(m_aDatadir)];
-				str_copy(aBaseDir, pArgv0, Pos);
-				aBaseDir[Pos] = '\0';
+				char aBaseDir[MAX_PATH_LENGTH];
+				str_copy(aBaseDir, pArgv0, Pos+1);
 				str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aBaseDir);
+				str_append(aBaseDir, "/data/mapres", sizeof(aBaseDir));
 				
-				if (fs_is_dir(m_aDatadir))
-					return 0;
+				if(fs_is_dir(aBaseDir))
+					return;
+				else
+					m_aDatadir[0] = 0;
 			}
 		}
 		
@@ -105,11 +205,11 @@ public:
 		// 5) check for all default locations
 		{
 			const char *aDirs[] = {
-				"/usr/share/teeworlds/data",
-				"/usr/share/games/teeworlds/data",
-				"/usr/local/share/teeworlds/data",
-				"/usr/local/share/games/teeworlds/data",
-				"/opt/teeworlds/data"
+				"/usr/share/teeworlds/data/mapres",
+				"/usr/share/games/teeworlds/data/mapres",
+				"/usr/local/share/teeworlds/data/mapres",
+				"/usr/local/share/games/teeworlds/data/mapres",
+				"/opt/teeworlds/data/mapres"
 			};
 			const int DirsCount = sizeof(aDirs) / sizeof(aDirs[0]);
 			
@@ -119,55 +219,41 @@ public:
 				if (fs_is_dir(aDirs[i]))
 				{
 					str_copy(m_aDatadir, aDirs[i], sizeof(m_aDatadir));
-					return 0;
+					return;
 				}
 			}
 		}
 	#endif
 		
 		// no data-dir found
-		dbg_msg("engine/datadir", "warning no data directory found");
-		return -1;
+		dbg_msg("storage", "warning no data directory found");
 	}
 
-	virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser)
+	virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser)
 	{
-		char aBuffer[1024];
-		
-		// list current directory
-		if(Types&TYPE_CURRENT)
+		char aBuffer[MAX_PATH_LENGTH];
+		if(Type == TYPE_ALL)
 		{
-			fs_listdir(pPath, pfnCallback, TYPE_CURRENT, pUser);
+			// list all available directories
+			for(int i = 0; i < m_NumPaths; ++i)
+				fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
 		}
-		
-		// list users directory
-		if(Types&TYPE_SAVE)
+		else if(Type >= 0 && Type < m_NumPaths)
 		{
-			str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aApplicationSavePath, pPath);
-			fs_listdir(aBuffer, pfnCallback, TYPE_SAVE, pUser);
+			// list wanted directory
+			fs_listdir(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser);
 		}
-		
-		// list datadir directory
-		if(Types&TYPE_DATA)
-		{
-			str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aDatadir, pPath);
-			fs_listdir(aBuffer, pfnCallback, TYPE_DATA, pUser);
-		}		
 	}
 
-	virtual const char *GetDirectory(int Type) const
+	const char *GetPath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize)
 	{
-		switch(Type)
-		{
-		case TYPE_SAVE: return m_aApplicationSavePath;
-		case TYPE_DATA: return m_aDatadir;
-		default: return "";
-		}
+		str_format(pBuffer, BufferSize, "%s%s%s", m_aaStoragePaths[Type], !m_aaStoragePaths[Type][0] ? "" : "/", pDir);
+		return pBuffer;
 	}
 	
-	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0)
+	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0)
 	{
-		char aBuffer[1024];
+		char aBuffer[MAX_PATH_LENGTH];
 		if(!pBuffer)
 		{
 			pBuffer = aBuffer;
@@ -176,45 +262,42 @@ public:
 		
 		if(Flags&IOFLAG_WRITE)
 		{
-			str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename);
-			return io_open(pBuffer, Flags);
+			return io_open(GetPath(TYPE_SAVE, pFilename, pBuffer, BufferSize), Flags);
 		}
 		else
 		{
 			IOHANDLE Handle = 0;
-			
-			// check current directory
-			Handle = io_open(pFilename, Flags);
-			if(Handle)
-				return Handle;
-				
-			// check user directory
-			str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename);
-			Handle = io_open(pBuffer, Flags);
-			if(Handle)
-				return Handle;
-				
-			// check normal data directory
-			str_format(pBuffer, BufferSize, "%s/%s", m_aDatadir, pFilename);
-			Handle = io_open(pBuffer, Flags);
-			if(Handle)
-				return Handle;
+
+			if(Type == TYPE_ALL)
+			{
+				// check all available directories
+				for(int i = 0; i < m_NumPaths; ++i)
+				{
+					Handle = io_open(GetPath(i, pFilename, pBuffer, BufferSize), Flags);
+					if(Handle)
+						return Handle;
+				}
+			}
+			else if(Type >= 0 && Type < m_NumPaths)
+			{
+				// check wanted directory
+				Handle = io_open(GetPath(Type, pFilename, pBuffer, BufferSize), Flags);
+				if(Handle)
+					return Handle;
+			}
 		}
 		
 		pBuffer[0] = 0;
 		return 0;		
 	}
  	
-	virtual bool RemoveFile(const char *pFilename)
+	virtual bool RemoveFile(const char *pFilename, int Type)
 	{
-		char aBuffer[1024];
-		str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aApplicationSavePath, pFilename);
-		bool Fail = remove(aBuffer);
-		
-		if(Fail)
-			Fail = remove(pFilename);
-		
-		return !Fail;
+		if(Type < 0 || Type >= m_NumPaths)
+			return false;
+
+		char aBuffer[MAX_PATH_LENGTH];
+		return remove(GetPath(Type, pFilename, aBuffer, sizeof(aBuffer)));
 	}
 
 	static IStorage *Create(const char *pApplicationName, int NumArgs, const char **ppArguments)
@@ -222,6 +305,7 @@ public:
 		CStorage *p = new CStorage();
 		if(p && p->Init(pApplicationName, NumArgs, ppArguments))
 		{
+			dbg_msg("storage", "initialisation failed");
 			delete p;
 			p = 0;
 		}
diff --git a/src/engine/storage.h b/src/engine/storage.h
index b9b02239..c483c52d 100644
--- a/src/engine/storage.h
+++ b/src/engine/storage.h
@@ -9,16 +9,13 @@ class IStorage : public IInterface
 public:
 	enum
 	{
-		TYPE_SAVE = 1,
-		TYPE_CURRENT = 2,
-		TYPE_DATA = 4,
-		TYPE_ALL = ~0
+		TYPE_SAVE = 0,
+		TYPE_ALL = -1
 	};
 	
-	virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0;
-	virtual const char *GetDirectory(int Type) const = 0;
-	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0) = 0;
-	virtual bool RemoveFile(const char *pFilename) = 0;
+	virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0;
+	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0) = 0;
+	virtual bool RemoveFile(const char *pFilename, int Type) = 0;
 };
 
 extern IStorage *CreateStorage(const char *pApplicationName, int NumArgs, const char **ppArguments);
diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp
index cdf4239a..5fa328dd 100644
--- a/src/game/client/components/console.cpp
+++ b/src/game/client/components/console.cpp
@@ -588,7 +588,7 @@ void CGameConsole::Dump(int Type)
 	{
 		IOHANDLE io;
 		str_format(aFilename, sizeof(aFilename), "dumps/%s_dump%s-%05d.txt", Type==1?"remote_console":"local_console", aDate, i);
-		io = Storage()->OpenFile(aFilename, IOFLAG_WRITE);
+		io = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 		if(io)
 		{
 			#if defined(CONF_FAMILY_WINDOWS)
diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp
index 9be450d1..8870c5c6 100644
--- a/src/game/client/components/mapimages.cpp
+++ b/src/game/client/components/mapimages.cpp
@@ -1,5 +1,6 @@
 #include <engine/graphics.h>
 #include <engine/map.h>
+#include <engine/storage.h>
 #include <game/client/component.h>
 #include <game/mapitems.h>
 
@@ -36,7 +37,7 @@ void CMapImages::OnMapLoad()
 			char Buf[256];
 			char *pName = (char *)pMap->GetData(pImg->m_ImageName);
 			str_format(Buf, sizeof(Buf), "mapres/%s.png", pName);
-			m_aTextures[i] = Graphics()->LoadTexture(Buf, CImageInfo::FORMAT_AUTO, 0);
+			m_aTextures[i] = Graphics()->LoadTexture(Buf, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
 		}
 		else
 		{
diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp
index 9c93c771..d3a819c0 100644
--- a/src/game/client/components/maplayers.cpp
+++ b/src/game/client/components/maplayers.cpp
@@ -135,7 +135,7 @@ void CMapLayers::OnRender()
 				Client()->GetServerInfo(&CurrentServerInfo);
 				char aFilename[256];
 				str_format(aFilename, sizeof(aFilename), "dumps/tilelayer_dump_%s-%d-%d-%dx%d.txt", CurrentServerInfo.m_aMap, g, l, pTMap->m_Width, pTMap->m_Height);
-				IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE);
+				IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
 				if(File)
 				{
 					#if defined(CONF_FAMILY_WINDOWS)
diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp
index 4a53d29b..caad5a80 100644
--- a/src/game/client/components/menus.cpp
+++ b/src/game/client/components/menus.cpp
@@ -12,6 +12,7 @@
 #include <engine/textrender.h>
 #include <engine/serverbrowser.h>
 #include <engine/keys.h>
+#include <engine/storage.h>
 #include <engine/shared/config.h>
 
 #include <game/version.h>
@@ -1259,7 +1260,7 @@ void CMenus::RenderBackground()
 	//Graphics()->Clear(1,1,1);
 	//render_sunrays(0,0);
 	if(gs_TextureBlob == -1)
-		gs_TextureBlob = Graphics()->LoadTexture("blob.png", CImageInfo::FORMAT_AUTO, 0);
+		gs_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
 
 
 	float sw = 300*Graphics()->ScreenAspect();
diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h
index a1e75f61..ea8be21f 100644
--- a/src/game/client/components/menus.h
+++ b/src/game/client/components/menus.h
@@ -159,7 +159,7 @@ class CMenus : public CComponent
 		char m_aFilename[128];
 		char m_aName[128];
 		bool m_IsDir;
-		int m_DirType;
+		int m_StorageType;
 		
 		bool operator<(const CDemoItem &Other) { return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false :
 														m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false :
@@ -174,7 +174,7 @@ class CMenus : public CComponent
 	
 	void DemolistOnUpdate(bool Reset);
 	void DemolistPopulate();
-	static void DemolistFetchCallback(const char *pName, int IsDir, int DirType, void *pUser);
+	static void DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser);
 	
 	// found in menus.cpp
 	int Render();
diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp
index e08114ad..e960769a 100644
--- a/src/game/client/components/menus_demo.cpp
+++ b/src/game/client/components/menus_demo.cpp
@@ -417,7 +417,7 @@ int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated)
 	return gs_ListBoxNewSelected;
 }
 
-void CMenus::DemolistFetchCallback(const char *pName, int IsDir, int DirType, void *pUser)
+void CMenus::DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser)
 {
 	CMenus *pSelf = (CMenus *)pUser;
 	int Length = str_length(pName);
@@ -433,14 +433,14 @@ void CMenus::DemolistFetchCallback(const char *pName, int IsDir, int DirType, vo
 	else
 		str_format(Item.m_aName, min(static_cast<int>(sizeof(Item.m_aName)), Length), "    %s", pName);
 	Item.m_IsDir = IsDir != 0;
-	Item.m_DirType = DirType;
+	Item.m_StorageType = StorageType;
 	pSelf->m_lDemos.add(Item);
 }
 
 void CMenus::DemolistPopulate()
 {
 	m_lDemos.clear();
-	Storage()->ListDirectory(IStorage::TYPE_SAVE|IStorage::TYPE_CURRENT, m_aCurrentDemoFolder, DemolistFetchCallback, this);
+	Storage()->ListDirectory(IStorage::TYPE_ALL, m_aCurrentDemoFolder, DemolistFetchCallback, this);
 }
 
 void CMenus::DemolistOnUpdate(bool Reset)
@@ -466,10 +466,8 @@ void CMenus::RenderDemoList(CUIRect MainView)
 		if(m_DemolistSelectedIndex >= 0 && !m_DemolistSelectedIsDir)
 		{
 			char aBuf[512];
-			str_format(aBuf, sizeof(aBuf), "%s%s%s/%s", Storage()->GetDirectory(m_lDemos[m_DemolistSelectedIndex].m_DirType),
-				Storage()->GetDirectory(m_lDemos[m_DemolistSelectedIndex].m_DirType)[0] ? "/" : "",
-				m_aCurrentDemoFolder, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
-			Storage()->RemoveFile(aBuf);
+			str_format(aBuf, sizeof(aBuf), "%s/%s", m_aCurrentDemoFolder, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
+			Storage()->RemoveFile(aBuf, m_lDemos[m_DemolistSelectedIndex].m_StorageType);
 			DemolistPopulate();
 			DemolistOnUpdate(false);
 		}
@@ -529,10 +527,8 @@ void CMenus::RenderDemoList(CUIRect MainView)
 			else // file
 			{
 				char aBuf[512];
-				str_format(aBuf, sizeof(aBuf), "%s%s%s/%s", Storage()->GetDirectory(m_lDemos[m_DemolistSelectedIndex].m_DirType),
-					Storage()->GetDirectory(m_lDemos[m_DemolistSelectedIndex].m_DirType)[0] ? "/" : "",
-					m_aCurrentDemoFolder, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
-				const char *pError = Client()->DemoPlayer_Play(aBuf);
+				str_format(aBuf, sizeof(aBuf), "%s/%s", m_aCurrentDemoFolder, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
+				const char *pError = Client()->DemoPlayer_Play(aBuf, m_lDemos[m_DemolistSelectedIndex].m_StorageType);
 				if(pError)
 					PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("error loading demo"), Localize("Ok"));
 				else
diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp
index c8040bfa..f3c4afe7 100644
--- a/src/game/client/components/menus_settings.cpp
+++ b/src/game/client/components/menus_settings.cpp
@@ -650,7 +650,7 @@ public:
 
 void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<CLanguage> *pLanguages)
 {
-	IOHANDLE File = pStorage->OpenFile("data/languages/index.txt", IOFLAG_READ);
+	IOHANDLE File = pStorage->OpenFile("languages/index.txt", IOFLAG_READ, IStorage::TYPE_ALL);
 	if(!File)
 	{
 		pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "couldn't open index file");
@@ -681,7 +681,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array<
 		}
 		
 		char aFileName[128];
-		str_format(aFileName, sizeof(aFileName), "data/languages/%s.txt", pLine);
+		str_format(aFileName, sizeof(aFileName), "languages/%s.txt", pLine);
 		pLanguages->add(CLanguage(pReplacement+3, aFileName));
 	}
 	io_close(File);
@@ -724,7 +724,7 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView)
 	if(OldSelected != s_SelectedLanguage)
 	{
 		str_copy(g_Config.m_ClLanguagefile, s_Languages[s_SelectedLanguage].m_FileName, sizeof(g_Config.m_ClLanguagefile));
-		g_Localization.Load(s_Languages[s_SelectedLanguage].m_FileName, Console());
+		g_Localization.Load(s_Languages[s_SelectedLanguage].m_FileName, Storage(), Console());
 	}
 }
 
diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp
index 098111ee..52c79220 100644
--- a/src/game/client/components/skins.cpp
+++ b/src/game/client/components/skins.cpp
@@ -20,7 +20,7 @@ void CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
 	char aBuf[512];
 	str_format(aBuf, sizeof(aBuf), "skins/%s", pName);
 	CImageInfo Info;
-	if(!pSelf->Graphics()->LoadPNG(&Info, aBuf))
+	if(!pSelf->Graphics()->LoadPNG(&Info, aBuf, DirType))
 	{
 		str_format(aBuf, sizeof(aBuf), "failed to load skin from %s", pName);
 		pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf);
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index 11841e5b..2805800d 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -224,7 +224,7 @@ void CGameClient::OnInit()
 	//m_pServerBrowser = Kernel()->RequestInterface<IServerBrowser>();
 	
 	// set the language
-	g_Localization.Load(g_Config.m_ClLanguagefile, Console());
+	g_Localization.Load(g_Config.m_ClLanguagefile, Storage(), Console());
 	
 	// init all components
 	for(int i = 0; i < m_All.m_Num; i++)
@@ -239,7 +239,12 @@ void CGameClient::OnInit()
 	// load default font	
 	static CFont *pDefaultFont;
 	//default_font = gfx_font_load("data/fonts/sazanami-gothic.ttf");
-	pDefaultFont = TextRender()->LoadFont("data/fonts/vera.ttf");
+
+	char aFilename[512];
+	IOHANDLE File = Storage()->OpenFile("fonts/vera.ttf", IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename));
+	if(File)
+		io_close(File);
+	pDefaultFont = TextRender()->LoadFont(aFilename);
 	TextRender()->SetDefaultFont(pDefaultFont);
 
 	g_Config.m_ClThreadsoundloading = 0;
@@ -254,7 +259,7 @@ void CGameClient::OnInit()
 	for(int i = 0; i < g_pData->m_NumImages; i++)
 	{
 		g_GameClient.m_pMenus->RenderLoading(gs_LoadCurrent/(float)gs_LoadTotal);
-		g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, CImageInfo::FORMAT_AUTO, 0);
+		g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
 		gs_LoadCurrent++;
 	}
 
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp
index b1dce3f9..ea83fb73 100644
--- a/src/game/editor/ed_editor.cpp
+++ b/src/game/editor/ed_editor.cpp
@@ -559,6 +559,7 @@ CQuad *CEditor::GetSelectedQuad()
 	return 0;
 }
 
+// TODO: fix/rework the whole file dialog stuff
 static void CallbackOpenDir(const char *pFileName, void *pUser)
 {
 	CEditor *pEditor = (CEditor*)pUser;
@@ -586,7 +587,7 @@ static void CallbackOpenMap(const char *pFileName, void *pUser)
 		return;
 	}
 	char aCompleteFilename[512];
-	str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
+	//str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
 	if(fs_is_dir(aCompleteFilename))
 	{
 		CallbackOpenDir(pFileName, pUser);
@@ -609,7 +610,7 @@ static void CallbackAppendMap(const char *pFileName, void *pUser)
 		return;
 	}
 	char aCompleteFilename[512];
-	str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
+	//str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
 	if(fs_is_dir(aCompleteFilename))
 	{
 		CallbackOpenDir(pFileName, pUser);
@@ -632,7 +633,7 @@ static void CallbackSaveMap(const char *pFileName, void *pUser)
 		return;
 	}
 	char aCompleteFilename[512];
-	str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
+	//str_format(aCompleteFilename, sizeof(aCompleteFilename), "%s/%s", pEditor->Client()->UserDirectory(), pFileName);
 	if(fs_is_dir(aCompleteFilename))
 	{
 		CallbackOpenDir(pFileName, pUser);
@@ -1899,7 +1900,7 @@ void CEditor::ReplaceImage(const char *pFileName, void *pUser)
 {
 	CEditor *pEditor = (CEditor *)pUser;
 	CEditorImage ImgInfo(pEditor);
-	if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName))
+	if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, IStorage::TYPE_ALL))
 		return;
 
 	CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage];
@@ -1914,7 +1915,7 @@ void CEditor::AddImage(const char *pFileName, void *pUser)
 {
 	CEditor *pEditor = (CEditor *)pUser;
 	CEditorImage ImgInfo(pEditor);
-	if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName))
+	if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, IStorage::TYPE_ALL))
 		return;
 
 	CEditorImage *pImg = new CEditorImage(pEditor);
@@ -2146,17 +2147,26 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 }
 
 
-static void EditorListdirCallback(const char *pName, int IsDir, int DirType, void *pUser)
+static void EditorListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
 {
 	CEditor *pEditor = (CEditor*)pUser;
+	int Length = str_length(pName);
 	if(pName[0] == '.' && (pName[1] == 0 ||
 		(pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_aFileDialogPath, "maps") || !str_comp(pEditor->m_aFileDialogPath, "mapres")))))
 		return;
 
-	pEditor->m_FileList.add(string(pName));
+	CEditor::CFilelistItem Item;
+	str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
+	if(IsDir)
+		str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
+	else
+		str_format(Item.m_aName, static_cast<int>(sizeof(Item.m_aName)), "    %s", pName);
+	Item.m_IsDir = IsDir != 0;
+	Item.m_StorageType = StorageType;
+	pEditor->m_FileList.add(Item);
 }
 
-void CEditor::AddFileDialogEntry(const char *pName, CUIRect *pView)
+void CEditor::AddFileDialogEntry(const CFilelistItem *pItem, CUIRect *pView)
 {
 	if(m_FilesCur > m_FilesNum)
 		m_FilesNum = m_FilesCur;
@@ -2169,9 +2179,9 @@ void CEditor::AddFileDialogEntry(const char *pName, CUIRect *pView)
 	pView->HSplitTop(15.0f, &Button, pView);
 	pView->HSplitTop(2.0f, 0, pView);
 
-	if(DoButton_File((void*)(10+(int)Button.y), pName, 0, &Button, 0, 0))
+	if(DoButton_File((void*)(10+(int)Button.y), pItem->m_aName, 0, &Button, 0, 0))
 	{
-		str_copy(m_aFileDialogFileName, pName, sizeof(m_aFileDialogFileName));
+		str_copy(m_aFileDialogFileName, pItem->m_aFilename, sizeof(m_aFileDialogFileName));
 		
 		str_format(m_aFileDialogCompleteFilename, sizeof(m_aFileDialogCompleteFilename), "%s/%s", m_aFileDialogPath, m_aFileDialogFileName);
 
@@ -2248,7 +2258,7 @@ void CEditor::RenderFileDialog()
 	UI()->ClipEnable(&View);
 
 	for(int i = 0; i < m_FileList.size(); i++)
-		AddFileDialogEntry(m_FileList[i].cstr(), &View);
+		AddFileDialogEntry(&m_FileList[i], &View);
 
 	// disable clipping again
 	UI()->ClipDisable();
@@ -2274,14 +2284,14 @@ void CEditor::RenderFileDialog()
 void CEditor::FilelistPopulate()
 {
 	m_FileList.clear();
-	Storage()->ListDirectory(m_FileDialogDirTypes, m_aFileDialogPath, EditorListdirCallback, this);
+	Storage()->ListDirectory(m_FileDialogStorageType, m_aFileDialogPath, EditorListdirCallback, this);
 }
 
-void CEditor::InvokeFileDialog(int ListDirTypes, const char *pTitle, const char *pButtonText,
+void CEditor::InvokeFileDialog(int StorageType, const char *pTitle, const char *pButtonText,
 	const char *pBasePath, const char *pDefaultName,
 	void (*pfnFunc)(const char *pFileName, void *pUser), void *pUser)
 {
-	m_FileDialogDirTypes = ListDirTypes;
+	m_FileDialogStorageType = StorageType;
 	m_pFileDialogTitle = pTitle;
 	m_pFileDialogButtonText = pButtonText;
 	m_pfnFileDialogFunc = pfnFunc;
@@ -3072,10 +3082,10 @@ void CEditor::Init()
 	m_UI.SetGraphics(m_pGraphics, m_pTextRender);
 	m_Map.m_pEditor = this;
 
-	ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", CImageInfo::FORMAT_AUTO, 0);
-	ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", CImageInfo::FORMAT_AUTO, 0);
-	ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", CImageInfo::FORMAT_AUTO, 0);
-	ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", CImageInfo::FORMAT_AUTO, 0);
+	ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
+	ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
+	ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
+	ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
 
 	m_TilesetPicker.m_pEditor = this;
 	m_TilesetPicker.MakePalette();
diff --git a/src/game/editor/ed_editor.h b/src/game/editor/ed_editor.h
index 30ec7203..225ff4ab 100644
--- a/src/game/editor/ed_editor.h
+++ b/src/game/editor/ed_editor.h
@@ -459,7 +459,7 @@ public:
 
 		m_aFileName[0] = 0;
 		
-		m_FileDialogDirTypes = 0;
+		m_FileDialogStorageType = 0;
 		m_pFileDialogTitle = 0;
 		m_pFileDialogButtonText = 0;
 		m_pFileDialogUser = 0;
@@ -508,7 +508,7 @@ public:
 	virtual void UpdateAndRender();
 	
 	void FilelistPopulate();
-	void InvokeFileDialog(int ListdirType, const char *pTitle, const char *pButtonText,
+	void InvokeFileDialog(int StorageType, const char *pTitle, const char *pButtonText,
 		const char *pBasepath, const char *pDefaultName,
 		void (*pfnFunc)(const char *pFilename, void *pUser), void *pUser);
 	
@@ -531,7 +531,7 @@ public:
 
 	char m_aFileName[512];
 	
-	int m_FileDialogDirTypes;
+	int m_FileDialogStorageType;
 	const char *m_pFileDialogTitle;
 	const char *m_pFileDialogButtonText;
 	void (*m_pfnFileDialogFunc)(const char *pFileName, void *pUser);
@@ -540,7 +540,19 @@ public:
 	char m_aFileDialogPath[512];
 	char m_aFileDialogCompleteFilename[512];
 	int m_FilesNum;
-	sorted_array<string> m_FileList;
+
+	struct CFilelistItem
+	{
+		char m_aFilename[128];
+		char m_aName[128];
+		bool m_IsDir;
+		int m_StorageType;
+		
+		bool operator<(const CFilelistItem &Other) { return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false :
+														m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false :
+														str_comp_filenames(m_aFilename, Other.m_aFilename) < 0; }
+	};
+	sorted_array<CFilelistItem> m_FileList;
 	int m_FilesStartAt;
 	int m_FilesCur;
 	int m_FilesStopAt;
@@ -643,7 +655,7 @@ public:
 	void RenderMenubar(CUIRect Menubar);
 	void RenderFileDialog();
 
-	void AddFileDialogEntry(const char *pName, CUIRect *pView);
+	void AddFileDialogEntry(const CFilelistItem *pItem, CUIRect *pView);
 	void SortImages();
 };
 
diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/ed_io.cpp
index 6eae9fb5..62e3cf65 100644
--- a/src/game/editor/ed_io.cpp
+++ b/src/game/editor/ed_io.cpp
@@ -372,7 +372,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName)
 {
 	CDataFileReader DataFile;
 	//DATAFILE *df = datafile_load(filename);
-	if(!DataFile.Open(pStorage, pFileName))
+	if(!DataFile.Open(pStorage, pFileName, IStorage::TYPE_ALL))
 		return 0;
 		
 	Clean();
@@ -411,7 +411,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName)
 					
 					// load external
 					CEditorImage ImgInfo(m_pEditor);
-					if(m_pEditor->Graphics()->LoadPNG(&ImgInfo, aBuf))
+					if(m_pEditor->Graphics()->LoadPNG(&ImgInfo, aBuf, IStorage::TYPE_ALL))
 					{
 						*pImg = ImgInfo;
 						pImg->m_TexId = m_pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
diff --git a/src/game/localization.cpp b/src/game/localization.cpp
index fc9ac3df..82de858f 100644
--- a/src/game/localization.cpp
+++ b/src/game/localization.cpp
@@ -4,6 +4,7 @@
 
 #include <engine/shared/linereader.h>
 #include <engine/console.h>
+#include <engine/storage.h>
 
 const char *Localize(const char *pStr)
 {
@@ -40,7 +41,7 @@ void CLocalizationDatabase::AddString(const char *pOrgStr, const char *pNewStr)
 	m_Strings.add(s);
 }
 
-bool CLocalizationDatabase::Load(const char *pFilename, IConsole *pConsole)
+bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, IConsole *pConsole)
 {
 	// empty string means unload
 	if(pFilename[0] == 0)
@@ -50,7 +51,7 @@ bool CLocalizationDatabase::Load(const char *pFilename, IConsole *pConsole)
 		return true;
 	}
 	
-	IOHANDLE IoHandle = io_open(pFilename, IOFLAG_READ);
+	IOHANDLE IoHandle = pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL);
 	if(!IoHandle)
 		return false;
 	
diff --git a/src/game/localization.h b/src/game/localization.h
index e117c4ff..9a2016c8 100644
--- a/src/game/localization.h
+++ b/src/game/localization.h
@@ -24,7 +24,7 @@ class CLocalizationDatabase
 public:
 	CLocalizationDatabase();
 
-	bool Load(const char *pFilename, class IConsole *pConsole);
+	bool Load(const char *pFilename, class IStorage *pStorage, class IConsole *pConsole);
 
 	int Version() { return m_CurrentVersion; }
 	
diff --git a/src/tools/map_resave.cpp b/src/tools/map_resave.cpp
index ce17e336..fd2fecfa 100644
--- a/src/tools/map_resave.cpp
+++ b/src/tools/map_resave.cpp
@@ -17,7 +17,7 @@ int main(int argc, const char **argv)
 
 	str_format(aFileName, sizeof(aFileName), "maps/%s", argv[2]);
 
-	if(!DataFile.Open(pStorage, argv[1]))
+	if(!DataFile.Open(pStorage, argv[1], IStorage::TYPE_ALL))
 		return -1;
 	if(!df.Open(pStorage, aFileName))
 		return -1;
diff --git a/storage.cfg b/storage.cfg
new file mode 100644
index 00000000..15f4ab18
--- /dev/null
+++ b/storage.cfg
@@ -0,0 +1,31 @@
+####
+# This specifies where and in which order Teeworlds looks
+# for its data (sounds, skins, ...). The search goes top
+# down which means the first path has the highest priority.
+# Furthermore the top entry also defines the save path where
+# all data (settings.cfg, screenshots, ...) are stored.
+# There are 3 special paths available:
+#	$USERDIR
+#	- ~/.appname on UNIX based systems
+#	- ~/Library/Applications Support/appname on Mac OS X
+#	- %APPDATA%/Appname on Windows based systems
+#	$DATADIR
+#	- the 'data' directory which is part of an official
+#	release
+#	$CURRENTDIR
+#	- current working directory
+#
+#
+# The default file has the following entries:
+#	add_path $USERDIR
+#	add_path $DATADIR
+#	add_path $CURRENTDIR
+#
+# A customised one could look like this:
+#	add_path user
+#	add_path mods/mymod
+####
+
+add_path $USERDIR
+add_path $DATADIR
+add_path $CURRENTDIR