2010-05-29 07:25:38 +00:00
|
|
|
// copyright (c) 2007 magnus auvinen, see licence.txt for more info
|
2010-09-03 18:05:22 +00:00
|
|
|
#include <stdio.h> //remove()
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <base/system.h>
|
|
|
|
#include <engine/storage.h>
|
|
|
|
#include "engine.h"
|
2010-10-06 21:07:35 +00:00
|
|
|
#include "linereader.h"
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// compiled-in data-dir path
|
|
|
|
#define DATA_DIR "data"
|
|
|
|
|
|
|
|
class CStorage : public IStorage
|
|
|
|
{
|
|
|
|
public:
|
2010-10-06 21:07:35 +00:00
|
|
|
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];
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
CStorage()
|
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
mem_zero(m_aaStoragePaths, sizeof(m_aaStoragePaths));
|
|
|
|
m_NumPaths = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aDatadir[0] = 0;
|
2010-10-06 21:07:35 +00:00
|
|
|
m_aUserdir[0] = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2010-08-05 18:26:03 +00:00
|
|
|
int Init(const char *pApplicationName, int NumArgs, const char **ppArguments)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// get userdir
|
|
|
|
fs_storage_path(pApplicationName, m_aUserdir, sizeof(m_aUserdir));
|
2010-08-05 18:26:03 +00:00
|
|
|
|
|
|
|
// check for datadir override
|
|
|
|
for(int i = 1; i < NumArgs; i++)
|
|
|
|
{
|
|
|
|
if(ppArguments[i][0] == '-' && ppArguments[i][1] == 'd' && ppArguments[i][2] == 0 && NumArgs - i > 1)
|
|
|
|
{
|
|
|
|
str_copy(m_aDatadir, ppArguments[i+1], sizeof(m_aDatadir));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
// 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);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
void FindDatadir(const char *pArgv0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// 1) use provided data-dir override
|
|
|
|
if(m_aDatadir[0])
|
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
char aBuffer[MAX_PATH_LENGTH];
|
|
|
|
str_format(aBuffer, sizeof(aBuffer), "%s/mapres", m_aDatadir);
|
|
|
|
if(!fs_is_dir(aBuffer))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
dbg_msg("storage", "specified data directory '%s' does not exist", m_aDatadir);
|
|
|
|
m_aDatadir[0] = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
else
|
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 2) use data-dir in PWD if present
|
|
|
|
if(fs_is_dir("data/mapres"))
|
|
|
|
{
|
|
|
|
str_copy(m_aDatadir, "data", sizeof(m_aDatadir));
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 3) use compiled-in data-dir if present
|
2010-10-06 21:07:35 +00:00
|
|
|
if(fs_is_dir(DATA_DIR "/mapres"))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir));
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 4) check for usable path in argv[0]
|
|
|
|
{
|
|
|
|
unsigned int Pos = ~0U;
|
|
|
|
for(unsigned i = 0; pArgv0[i]; i++)
|
2010-10-06 21:07:35 +00:00
|
|
|
if(pArgv0[i] == '/' || pArgv0[i] == '\\')
|
2010-05-29 07:25:38 +00:00
|
|
|
Pos = i;
|
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
if(Pos < MAX_PATH_LENGTH)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
char aBaseDir[MAX_PATH_LENGTH];
|
|
|
|
str_copy(aBaseDir, pArgv0, Pos+1);
|
2010-05-29 07:25:38 +00:00
|
|
|
str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aBaseDir);
|
2010-10-06 21:07:35 +00:00
|
|
|
str_append(aBaseDir, "/data/mapres", sizeof(aBaseDir));
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
if(fs_is_dir(aBaseDir))
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
m_aDatadir[0] = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONF_FAMILY_UNIX)
|
|
|
|
// 5) check for all default locations
|
|
|
|
{
|
|
|
|
const char *aDirs[] = {
|
2010-10-06 21:07:35 +00:00
|
|
|
"/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"
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
const int DirsCount = sizeof(aDirs) / sizeof(aDirs[0]);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < DirsCount; i++)
|
|
|
|
{
|
|
|
|
if (fs_is_dir(aDirs[i]))
|
|
|
|
{
|
|
|
|
str_copy(m_aDatadir, aDirs[i], sizeof(m_aDatadir));
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// no data-dir found
|
2010-10-06 21:07:35 +00:00
|
|
|
dbg_msg("storage", "warning no data directory found");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
char aBuffer[MAX_PATH_LENGTH];
|
|
|
|
if(Type == TYPE_ALL)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// list all available directories
|
|
|
|
for(int i = 0; i < m_NumPaths; ++i)
|
|
|
|
fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
else if(Type >= 0 && Type < m_NumPaths)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// list wanted directory
|
|
|
|
fs_listdir(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-24 11:38:03 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
const char *GetPath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize)
|
2010-09-24 11:38:03 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
str_format(pBuffer, BufferSize, "%s%s%s", m_aaStoragePaths[Type], !m_aaStoragePaths[Type][0] ? "" : "/", pDir);
|
|
|
|
return pBuffer;
|
2010-09-24 11:38:03 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
char aBuffer[MAX_PATH_LENGTH];
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!pBuffer)
|
|
|
|
{
|
|
|
|
pBuffer = aBuffer;
|
|
|
|
BufferSize = sizeof(aBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Flags&IOFLAG_WRITE)
|
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
return io_open(GetPath(TYPE_SAVE, pFilename, pBuffer, BufferSize), Flags);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IOHANDLE Handle = 0;
|
2010-10-06 21:07:35 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pBuffer[0] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2010-09-03 18:05:22 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
virtual bool RemoveFile(const char *pFilename, int Type)
|
2010-09-03 18:05:22 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
if(Type < 0 || Type >= m_NumPaths)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
char aBuffer[MAX_PATH_LENGTH];
|
|
|
|
return remove(GetPath(Type, pFilename, aBuffer, sizeof(aBuffer)));
|
2010-09-03 18:05:22 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2010-08-05 18:26:03 +00:00
|
|
|
static IStorage *Create(const char *pApplicationName, int NumArgs, const char **ppArguments)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CStorage *p = new CStorage();
|
2010-08-05 18:34:16 +00:00
|
|
|
if(p && p->Init(pApplicationName, NumArgs, ppArguments))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
dbg_msg("storage", "initialisation failed");
|
2010-05-29 07:25:38 +00:00
|
|
|
delete p;
|
|
|
|
p = 0;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-08-05 18:26:03 +00:00
|
|
|
IStorage *CreateStorage(const char *pApplicationName, int NumArgs, const char **ppArguments) { return CStorage::Create(pApplicationName, NumArgs, ppArguments); }
|