diff --git a/CMakeLists.txt b/CMakeLists.txt index 14c07a503..d2396bf85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,8 @@ set(OC_CLONK_SOURCES src/config/C4ConfigShareware.cpp src/config/C4ConfigShareware.h src/config/C4Constants.h + src/config/C4Reloc.cpp + src/config/C4Reloc.h src/config/C4SecurityCertificates.cpp src/config/C4SecurityCertificates.h src/control/C4Control.cpp @@ -926,6 +928,12 @@ endif() ############################################################################ # Assemble compiler flags ############################################################################ +if(UNIX) + # Don't put this into CMAKE_CXX_FLAGS because otherwise it is cached, + # and when the path is changed both the old and new definition appears + # in the list of flags. + add_definitions("-DOC_SYSTEM_DATA_DIR=\"${CMAKE_INSTALL_PREFIX}/share/openclonk\"") +endif() if(OC_CXX_FLAGS) list(REMOVE_DUPLICATES OC_CXX_FLAGS) endif() @@ -1071,11 +1079,11 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY ############################################################################ # installation ############################################################################ - # Install the icon into share/icons/hicolor/48x48/apps/clonk.png. Do this by # extracting the correct size from oc.ico. Currently this is layer 2 - let's # hope that it stays this way. # TODO: Check for convert at configure step? +install(DIRECTORY DESTINATION share/icons/hicolor/48x48/apps) install(CODE " EXECUTE_PROCESS(COMMAND \"convert\" \"${CMAKE_CURRENT_SOURCE_DIR}/src/res/oc.ico[2]\" \"${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps/clonk.png\" RESULT_VARIABLE CONVERT_RESULT) IF(NOT \${CONVERT_RESULT} EQUAL 0) diff --git a/Makefile.am b/Makefile.am index e1bc979cb..e8941b5df 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,7 @@ GCC_FLAGS = WARNING_FLAGS = -Wall endif -AM_CXXFLAGS = $(PTHREAD_CFLAGS) $(WINDOWS_CFLAGS) $(WARNING_FLAGS) $(GCC_FLAGS) +AM_CXXFLAGS = $(PTHREAD_CFLAGS) $(WINDOWS_CFLAGS) $(WARNING_FLAGS) $(GCC_FLAGS) -DOC_SYSTEM_DATA_DIR=\"${datadir}/openclonk\" AM_CFLAGS = -Wall $(GCC_FLAGS) @@ -196,6 +196,8 @@ src/config/C4Config.h \ src/config/C4ConfigShareware.cpp \ src/config/C4ConfigShareware.h \ src/config/C4Constants.h \ +src/config/C4Reloc.cpp \ +src/config/C4Reloc.h \ src/config/C4SecurityCertificates.cpp \ src/config/C4SecurityCertificates.h \ src/control/C4Control.cpp \ diff --git a/src/C4Application.cpp b/src/C4Application.cpp index bba50faec..f79bbdce2 100644 --- a/src/C4Application.cpp +++ b/src/C4Application.cpp @@ -110,8 +110,11 @@ bool C4Application::DoInit(int argc, char * argv[]) Revision.Ref(C4REVISION); + // Initialize game data paths + Reloc.Init(); + // init system group - if (!SystemGroup.Open(C4CFN_System)) + if (!Reloc.Open(SystemGroup, C4CFN_System)) { // Error opening system group - no LogFatal, because it needs language table. // This will *not* use the FatalErrors stack, but this will cause the game @@ -204,7 +207,7 @@ void C4Application::ParseCommandLine(int argc, char * argv[]) ClearCommandLine(); Game.NetworkActive = false; - Config.General.ClearAdditionalDataPaths(); + Reloc.Init(); // re-init from config isEditor = 2; int c; while (1) @@ -303,7 +306,7 @@ void C4Application::ParseCommandLine(int argc, char * argv[]) // startup start screen case 's': C4Startup::SetStartScreen(optarg); break; // additional read-only data path - case 'd': Config.General.AddAdditionalDataPath(optarg); break; + case 'd': Reloc.AddPath(optarg); break; // debug options case 'D': Game.DebugPort = atoi(optarg); break; case 'P': Game.DebugPassword = optarg; break; @@ -345,7 +348,11 @@ void C4Application::ParseCommandLine(int argc, char * argv[]) // Scenario file if (SEqualNoCase(GetExtension(szParameter),"c4s")) { - Game.SetScenarioFilename(Config.AtDataReadPath(szParameter, true)); + if(IsGlobalPath(szParameter)) + Game.SetScenarioFilename(szParameter); + else + Game.SetScenarioFilename((std::string(GetWorkingDirectory()) + DirSep + szParameter).c_str()); + continue; } if (SEqualNoCase(GetFilename(szParameter),"scenario.txt")) @@ -356,8 +363,11 @@ void C4Application::ParseCommandLine(int argc, char * argv[]) // Player file if (SEqualNoCase(GetExtension(szParameter),"c4p")) { - const char *param = Config.AtDataReadPath(szParameter, true); - SAddModule(Game.PlayerFilenames,param); + if(IsGlobalPath(szParameter)) + SAddModule(Game.PlayerFilenames, szParameter); + else + SAddModule(Game.PlayerFilenames, (std::string(GetWorkingDirectory()) + DirSep + szParameter).c_str()); + continue; } // Definition file diff --git a/src/C4Game.cpp b/src/C4Game.cpp index e6a62eb3d..989910edc 100644 --- a/src/C4Game.cpp +++ b/src/C4Game.cpp @@ -181,9 +181,14 @@ bool C4Game::OpenScenario() { LogF("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), (const char *)ScenarioFilename); return false; } } else + { // open directly - if (!ScenarioFile.Open(ScenarioFilename)) + if (!Reloc.Open(ScenarioFile, ScenarioFilename)) { LogF("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), (const char *)ScenarioFilename); return false; } + } + + // Remember full (absolute) path + SCopy(ScenarioFile.GetFullName().getData(), ScenarioFilename, _MAX_PATH); // add scenario to group GroupSet.RegisterGroup(ScenarioFile, false, C4GSPrio_Scenario, C4GSCnt_Scenario); @@ -818,7 +823,7 @@ bool C4Game::InitMaterialTexture() // Find next external material source pMatRes = Game.Parameters.GameRes.iterRes(pMatRes, NRT_Material); if (!pMatRes) break; - if (!Mats.Open(pMatRes->getFile())) + if (!Reloc.Open(Mats, pMatRes->getFile())) return false; } @@ -3210,9 +3215,6 @@ const char* C4Game::FoldersWithLocalsDefs(const char *szPath) static char szDefs[10*_MAX_PATH+1]; szDefs[0]=0; - // Get relative path - szPath = Config.AtRelativePath(szPath); - // Scan path for folder names int32_t cnt,iBackslash; char szFoldername[_MAX_PATH+1]; diff --git a/src/C4Include.h b/src/C4Include.h index 4ca6f4da5..786e83171 100644 --- a/src/C4Include.h +++ b/src/C4Include.h @@ -43,6 +43,7 @@ #include "StdFile.h" #include "StdResStr2.h" #include "C4Log.h" +#include "C4Reloc.h" #include "C4PlayerControl.h" diff --git a/src/c4group/C4Extra.cpp b/src/c4group/C4Extra.cpp index 92d91e233..edcf19d9b 100644 --- a/src/c4group/C4Extra.cpp +++ b/src/c4group/C4Extra.cpp @@ -35,18 +35,20 @@ void C4Extra::Default() void C4Extra::Clear() { // free class members - ExtraSysGrp.Close(); - ExtraUserGrp.Close(); + for(unsigned int i = 0; i < ExtraGroups.size(); ++i) + delete ExtraGroups[i]; + ExtraGroups.clear(); } bool C4Extra::InitGroup() { // register extra root into game group set - if (ItemExists(Config.AtSystemDataPath(C4CFN_Extra)) && ExtraSysGrp.Open(Config.AtSystemDataPath(C4CFN_Extra))) - Game.GroupSet.RegisterGroup(ExtraSysGrp, false, C4GSPrio_ExtraRoot, C4GSCnt_ExtraRoot); - // Add user Extra.c4g - if (ItemExists(Config.AtUserDataPath(C4CFN_Extra)) && ExtraUserGrp.Open(Config.AtUserDataPath(C4CFN_Extra))) - Game.GroupSet.RegisterGroup(ExtraUserGrp, false, C4GSPrio_ExtraRoot, C4GSCnt_ExtraRoot); + for(C4Reloc::iterator iter = Reloc.begin(); iter != Reloc.end(); ++iter) + { + std::auto_ptr pGroup(new C4Group); + if(pGroup->Open( (*iter + DirSep + C4CFN_Extra).getData())) + ExtraGroups.push_back(pGroup.release()); + } // done, success return true; @@ -55,15 +57,23 @@ bool C4Extra::InitGroup() bool C4Extra::Init() { // no group: OK - if (!ExtraSysGrp.IsOpen() && !ExtraUserGrp.IsOpen()) return true; + if (ExtraGroups.empty()) return true; // load from all definitions that are activated // add first definition first, so the priority will be lowest // (according to definition load/overload order) char szSegment[_MAX_PATH+1]; bool fAnythingLoaded=false; for (int cseg=0; SCopySegment(Game.DefinitionFilenames,cseg,szSegment,';',_MAX_PATH); cseg++) - if (LoadDef(ExtraUserGrp,GetFilename(szSegment)) || LoadDef(ExtraSysGrp,GetFilename(szSegment))) - fAnythingLoaded=true; + { + for(unsigned int i = 0; i < ExtraGroups.size(); ++i) + { + if(LoadDef(*ExtraGroups[i], GetFilename(szSegment))) + { + fAnythingLoaded=true; + break; + } + } + } // done, success return true; } @@ -73,7 +83,7 @@ bool C4Extra::LoadDef(C4Group &hGroup, const char *szName) // check if file exists if (!hGroup.FindEntry(szName)) return false; // log that extra group is loaded - LogF(LoadResStr("IDS_PRC_LOADEXTRA"), ExtraSysGrp.GetName(), szName); + LogF(LoadResStr("IDS_PRC_LOADEXTRA"), hGroup.GetName(), szName); // open and add group to set C4Group *pGrp = new C4Group; if (!pGrp->OpenAsChild(&hGroup, szName)) { Log(LoadResStr("IDS_ERR_FAILURE")); delete pGrp; return false; } diff --git a/src/c4group/C4Extra.h b/src/c4group/C4Extra.h index 797665d28..93454b5a3 100644 --- a/src/c4group/C4Extra.h +++ b/src/c4group/C4Extra.h @@ -33,8 +33,7 @@ public: bool Init(); // init extra group, using scneario presets bool InitGroup(); // open extra group - C4Group ExtraSysGrp; // extra.c4g root folder - C4Group ExtraUserGrp; // extra.c4g root folder + std::vector ExtraGroups; // extra.c4g root folders protected: bool LoadDef(C4Group &hGroup, const char *szName); // load preset for definition diff --git a/src/c4group/C4GroupSet.cpp b/src/c4group/C4GroupSet.cpp index c6a891ee0..551ea3a47 100644 --- a/src/c4group/C4GroupSet.cpp +++ b/src/c4group/C4GroupSet.cpp @@ -357,7 +357,7 @@ C4Group *C4GroupSet::RegisterParentFolders(const char *szScenFilename) delete pGroup; return false; } } - else if (!pGroup->Open(szParentfolder+iPos)) + else if (!Reloc.Open(*pGroup, szParentfolder+iPos)) { LogFatal(FormatString("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), szParentfolder+iPos).getData()); delete pGroup; return false; diff --git a/src/c4group/C4Language.cpp b/src/c4group/C4Language.cpp index 78feb565a..da64ce5b0 100644 --- a/src/c4group/C4Language.cpp +++ b/src/c4group/C4Language.cpp @@ -83,39 +83,58 @@ bool C4Language::Init() } }*/ - // Make sure Language.c4g is unpacked - if (ItemExists(C4CFN_Languages)) - if (!DirectoryExists(C4CFN_Languages)) - C4Group_UnpackDirectory(C4CFN_Languages); - - // Look for available language packs in Language.c4g - C4Group *pPack; - char strPackFilename[_MAX_FNAME + 1], strEntry[_MAX_FNAME + 1]; - //Log("Registering languages..."); - if (PackDirectory.Open(C4CFN_Languages)) - while (PackDirectory.FindNextEntry("*.c4g", strEntry)) + // Make sure Language.c4g is unpacked (TODO: This won't work properly if Language.c4g is in system data path) + // Assume for now that Language.c4g is either at a writable location or unpacked already. + // TODO: Use all Language.c4gs that we find, and merge them. + // TODO: Use gettext instead? + StdStrBuf langPath; + C4Reloc::iterator iter; + for(iter = Reloc.begin(); iter != Reloc.end(); ++iter) + { + langPath.Copy(*iter + DirSep + C4CFN_Languages); + if(ItemExists(langPath.getData())) { - sprintf(strPackFilename, "%s" DirSep "%s", C4CFN_Languages, strEntry); - pPack = new C4Group(); - if (pPack->Open(strPackFilename)) + if(DirectoryExists(langPath.getData())) + break; + if(C4Group_UnpackDirectory(langPath.getData())) + break; + } + } + + // Break if no language.c4g found + if(iter != Reloc.end()) + { + // Look for available language packs in Language.c4g + C4Group *pPack; + char strPackFilename[_MAX_FNAME + 1], strEntry[_MAX_FNAME + 1]; + //Log("Registering languages..."); + if (PackDirectory.Open(langPath.getData())) + { + while (PackDirectory.FindNextEntry("*.c4g", strEntry)) { - //sprintf(strLog, " %s...", strPackFilename); Log(strLog); - Packs.RegisterGroup(*pPack, true, C4GSCnt_Language, false); - } - else - { - //sprintf(strLog, "Could not open language pack %s...", strPackFilename); Log(strLog); - delete pPack; + sprintf(strPackFilename, "%s" DirSep "%s", C4CFN_Languages, strEntry); + pPack = new C4Group(); + if (pPack->Open(strPackFilename)) + { + //sprintf(strLog, " %s...", strPackFilename); Log(strLog); + Packs.RegisterGroup(*pPack, true, C4GSCnt_Language, false); + } + else + { + //sprintf(strLog, "Could not open language pack %s...", strPackFilename); Log(strLog); + delete pPack; + } } } - // Log - //sprintf(strLog, "%d external language packs registered.", GetPackCount()); Log(strLog); + // Log + //sprintf(strLog, "%d external language packs registered.", GetPackCount()); Log(strLog); - // Now create a pack group for each language pack (these pack groups are child groups - // that browse along each pack to access requested data) - for (int iPack = 0; (pPack = Packs.GetGroup(iPack)); iPack++) - PackGroups.RegisterGroup(*(new C4Group), true, C4GSPrio_Base, C4GSCnt_Language); + // Now create a pack group for each language pack (these pack groups are child groups + // that browse along each pack to access requested data) + for (int iPack = 0; (pPack = Packs.GetGroup(iPack)); iPack++) + PackGroups.RegisterGroup(*(new C4Group), true, C4GSPrio_Base, C4GSCnt_Language); + } // Load language infos by scanning string tables (the engine doesn't really need this at the moment) InitInfos(); @@ -339,7 +358,7 @@ void C4Language::InitInfos() { C4Group hGroup; // First, look in System.c4g - if (hGroup.Open(C4CFN_System)) + if (Reloc.Open(hGroup, C4CFN_System)) { LoadInfos(hGroup); hGroup.Close(); @@ -436,7 +455,7 @@ bool C4Language::InitStringTable(const char *strCode) { C4Group hGroup; // First, look in System.c4g - if (hGroup.Open(C4CFN_System)) + if (Reloc.Open(hGroup, C4CFN_System)) { if (LoadStringTable(hGroup, strCode)) { hGroup.Close(); return true; } diff --git a/src/config/C4Config.cpp b/src/config/C4Config.cpp index 8ebd4a345..8ce8d65a5 100644 --- a/src/config/C4Config.cpp +++ b/src/config/C4Config.cpp @@ -479,8 +479,14 @@ void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) SCopy(GetWorkingDirectory(),SystemDataPath); AppendBackslash(SystemDataPath); #elif defined(__linux__) - // FIXME: Where to put this? - SCopy(ExePath,SystemDataPath); + +#ifdef OC_SYSTEM_DATA_DIR + SCopy(OC_SYSTEM_DATA_DIR, SystemDataPath); + AppendBackslash(SystemDataPath); +#else +#error Please define OC_SYSTEM_DATA_DIR! + //SCopy(ExePath,SystemDataPath); +#endif #endif // Find user-specific data path @@ -540,21 +546,7 @@ void C4ConfigGeneral::AdoptOldSettings() } } -void C4ConfigGeneral::ClearAdditionalDataPaths() -{ - for (PathList::iterator it = AdditionalDataPaths.begin(); it != AdditionalDataPaths.end(); ++it) - delete[] *it; - AdditionalDataPaths.clear(); -} - -void C4ConfigGeneral::AddAdditionalDataPath(const char *szPath) -{ - char *clone = new char[SLen(szPath)+1]; - SCopy(szPath,clone); - AdditionalDataPaths.push_back(clone); -} - -char AtPathFilename[_MAX_PATH+1]; +static char AtPathFilename[_MAX_PATH+1]; const char* C4Config::AtExePath(const char *szFilename) { @@ -660,54 +652,6 @@ void C4ConfigControls::ResetKeys() StdCompilerNull Comp; Comp.Compile(mkParAdapt(*this, true)); } -const char* C4Config::AtDataReadPath(const char *szFilename, bool fPreferWorkdir) -{ - if (IsGlobalPath(szFilename)) return szFilename; - StdCopyStrBuf sfn(szFilename); - do - { - const char *path = AtDataReadPathCore(sfn.getData(), fPreferWorkdir); - if (path) - { - if (path != AtPathFilename) SCopy(path,AtPathFilename); - AtPathFilename[_MAX_PATH] = '\0'; - AppendBackslash(AtPathFilename); - SAppend(szFilename,AtPathFilename,_MAX_PATH); - return AtPathFilename; - } - } - while (TruncatePath(sfn.getMData())); - return szFilename; -} - -const char* C4Config::AtDataReadPathCore(const char *szFilename, bool fPreferWorkdir) -{ - if (fPreferWorkdir && FileExists(szFilename)) - { - SCopy(GetWorkingDirectory(),AtPathFilename,_MAX_PATH-1); - return AtPathFilename; - } - // Check extra data paths - for (C4ConfigGeneral::PathList::iterator it = General.AdditionalDataPaths.begin(); - it != General.AdditionalDataPaths.end(); - ++it) - { - SCopy(*it, AtPathFilename, _MAX_PATH-1); - AppendBackslash(AtPathFilename); - SAppend(szFilename,AtPathFilename,_MAX_PATH); - if (FileExists(AtPathFilename)) - return *it; - } - if (FileExists(AtUserDataPath(szFilename))) return General.UserDataPath; - if (FileExists(AtSystemDataPath(szFilename))) return General.SystemDataPath; - if (!fPreferWorkdir && FileExists(szFilename)) - { - SCopy(GetWorkingDirectory(),AtPathFilename,_MAX_PATH-1); - return AtPathFilename; - } - return NULL; -} - const char* C4Config::AtUserDataRelativePath(const char *szFilename) { // Specified file is located in UserDataPath: return relative path diff --git a/src/config/C4Config.h b/src/config/C4Config.h index 1d43bf523..e600feda5 100644 --- a/src/config/C4Config.h +++ b/src/config/C4Config.h @@ -68,9 +68,6 @@ public: bool FirstStart; bool UserPortraitsWritten; // set when default portraits have been copied to the UserPath (this is only done once) - // Additional paths to read data from (prioritized lower than UserDataPath/SystemDataPath) - typedef std::list PathList; - PathList AdditionalDataPaths; public: static int GetLanguageSequence(const char *strSource, char *strTarget); void DefaultLanguage(); @@ -78,9 +75,6 @@ public: void AdoptOldSettings(); void DeterminePaths(bool forceWorkingDirectory); void CompileFunc(StdCompiler *pComp); - void AddAdditionalDataPath(const char *szPath); - void ClearAdditionalDataPaths(); - ~C4ConfigGeneral() { ClearAdditionalDataPaths(); } private: struct @@ -292,7 +286,6 @@ public: const char *AtSystemDataPath(const char *szFilename); const char *AtSystemDataRelativePath(const char *szFilename); const char *AtRelativePath(const char *szFilename); // Returns ASDRP or AUDRP depending on location - const char *AtDataReadPath(const char *szFilename, bool fPreferWorkdir = false); const char *GetRegistrationData(const char* strField) { return ""; } void ForceRelativePath(StdStrBuf *sFilename); // try AtRelativePath; force GetC4Filename if not possible void CompileFunc(StdCompiler *pComp); @@ -303,8 +296,6 @@ public: void GetConfigFileName(StdStrBuf &filename, bool forceWorkingDirectory, const char *szConfigFile); static void ExpandEnvironmentVariables(char *strPath, size_t iMaxLen); -private: - const char *AtDataReadPathCore(const char *szFilename, bool fPreferWorkdir = false); }; #include diff --git a/src/config/C4Reloc.cpp b/src/config/C4Reloc.cpp new file mode 100644 index 000000000..b1789c43c --- /dev/null +++ b/src/config/C4Reloc.cpp @@ -0,0 +1,86 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2011 Armin Burgmeier + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#include +#include + +C4Reloc Reloc; // singleton + +void C4Reloc::Init() +{ + Paths.clear(); + + // TODO: On Linux, we might not always want to have ExePath (= working directory) + // here. It's handy for development so that we can easily run the engine in planet/ + // but for distribution it might make sense to disable it. + // TODO: We might also want to add ExePath/planet if it exists, so that we don't + // need to run the engine in planet/. + AddPath(Config.General.ExePath); + AddPath(Config.General.UserDataPath); + AddPath(Config.General.SystemDataPath); +} + +bool C4Reloc::AddPath(const char* path) +{ + if(!IsGlobalPath(path)) + return false; + + if(std::find(Paths.begin(), Paths.end(), path) != Paths.end()) + return false; + + Paths.push_back(StdCopyStrBuf(path)); + return true; +} + +C4Reloc::iterator C4Reloc::begin() const +{ + return Paths.begin(); +} + +C4Reloc::iterator C4Reloc::end() const +{ + return Paths.end(); +} + +bool C4Reloc::Open(C4Group& hGroup, const char* filename) const +{ + if(IsGlobalPath(filename)) return hGroup.Open(filename); + + for(iterator iter = begin(); iter != end(); ++iter) + if(hGroup.Open((*iter + DirSep + filename).getData())) + return true; + + return false; +} + +bool C4Reloc::LocateItem(const char* filename, StdStrBuf& str) const +{ + if(IsGlobalPath(filename)) + { + str.Copy(filename); + return true; + } + + for(iterator iter = begin(); iter != end(); ++iter) + { + str.Copy(*iter + DirSep + filename); + if(ItemExists(str.getData())) + return true; + } + + return false; +} diff --git a/src/config/C4Reloc.h b/src/config/C4Reloc.h new file mode 100644 index 000000000..b88472494 --- /dev/null +++ b/src/config/C4Reloc.h @@ -0,0 +1,46 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2011 Armin Burgmeier + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#ifndef C4RELOC_H +#define C4RELOC_H + +#include + +class C4Reloc +{ +public: + typedef std::vector PathList; + typedef PathList::const_iterator iterator; + + // Can also be used for re-init, drops custom paths added with AddPath. + // Make sure to call after Config.Load. + void Init(); + + bool AddPath(const char* path); + + iterator begin() const; + iterator end() const; + + bool Open(C4Group& hGroup, const char* filename) const; + bool LocateItem(const char* filename, StdStrBuf& str) const; +private: + C4Reloc::PathList Paths; +}; + +extern C4Reloc Reloc; + +#endif // C4RELOC_H diff --git a/src/control/C4Control.cpp b/src/control/C4Control.cpp index 036eac09b..8a5eb8638 100644 --- a/src/control/C4Control.cpp +++ b/src/control/C4Control.cpp @@ -667,35 +667,40 @@ C4ControlJoinPlayer::C4ControlJoinPlayer(const char *szFilename, int32_t iAtClie idInfo(iIDInfo), fByRes(false) { // load from file if filename is given - which may not be the case for script players - if (szFilename) + StdStrBuf filename; + if (szFilename && Reloc.LocateItem(szFilename, filename)) { - StdStrBuf filename_buf; - const char *filename = Config.AtDataReadPath(szFilename); bool file_is_temp = false; - if (DirectoryExists(filename)) + if (DirectoryExists(filename.getData())) { // the player file is unpacked - temp pack and read - filename_buf.Copy(Config.AtTempPath(GetFilenameOnly(filename))); + StdStrBuf filename_buf; + filename_buf.Copy(Config.AtTempPath(GetFilenameOnly(filename.getData()))); MakeTempFilename(&filename_buf); - if (C4Group_PackDirectoryTo(filename, filename_buf.getData())) + if (C4Group_PackDirectoryTo(filename.getData(), filename_buf.getData())) { - filename = filename_buf.getData(); + filename = filename_buf; file_is_temp = true; } else { // pack failed - LogF("[!]Error packing player file %s to %s for join: Pack failed.", filename, filename_buf.getData()); + LogF("[!]Error packing player file %s to %s for join: Pack failed.", filename.getData(), filename_buf.getData()); assert(false); } } - bool fSuccess = PlrData.LoadFromFile(filename); + bool fSuccess = PlrData.LoadFromFile(filename.getData()); if (!fSuccess) { + LogF("[!]Error loading player file from %s.", filename.getData()); assert(false); - LogF("[!]Error loading player file from %s.", filename); } - if (file_is_temp) EraseFile(filename); + if (file_is_temp) EraseFile(filename.getData()); + } + else + { + LogF("[!]Error loading player file from %s.", szFilename ? szFilename : "(null)"); + assert(false); } } diff --git a/src/control/C4PlayerInfo.cpp b/src/control/C4PlayerInfo.cpp index 1be0a0e2b..df2cc3b62 100644 --- a/src/control/C4PlayerInfo.cpp +++ b/src/control/C4PlayerInfo.cpp @@ -77,11 +77,9 @@ bool C4PlayerInfo::LoadFromLocalFile(const char *szFilename) assert(!Game.C4S.Head.Replay); // clear previous Clear(); - // find (possibly overridden) filename - szFilename = Config.AtDataReadPath(szFilename); // open player file group C4Group Grp; - if (!Grp.Open(szFilename)) return false; + if (!Reloc.Open(Grp, szFilename)) return false; // read core C4PlayerInfoCore C4P; diff --git a/src/game/object/C4Def.cpp b/src/game/object/C4Def.cpp index e4f4eade7..fcdd61a59 100644 --- a/src/game/object/C4Def.cpp +++ b/src/game/object/C4Def.cpp @@ -821,7 +821,7 @@ int32_t C4DefList::Load(const char *szSearch, // Load from specified file C4Group hGroup; - if (!hGroup.Open(Config.AtDataReadPath(szSearch))) + if (!Reloc.Open(hGroup, szSearch)) { // Specified file not found (failure) LogFatal(FormatString(LoadResStr("IDS_PRC_DEFNOTFOUND"),szSearch).getData()); diff --git a/src/game/player/C4Player.cpp b/src/game/player/C4Player.cpp index db2d9b399..da9a214ae 100644 --- a/src/game/player/C4Player.cpp +++ b/src/game/player/C4Player.cpp @@ -238,10 +238,6 @@ bool C4Player::Init(int32_t iNumber, int32_t iAtClient, const char *szAtClientNa } // Status init Status=PS_Normal; - if (szFilename) - SCopy(Config.AtDataReadPath(szFilename),Filename); - else - *Filename='\0'; Number = iNumber; ID = pInfo->GetID(); Team = pInfo->GetTeam(); @@ -250,14 +246,14 @@ bool C4Player::Init(int32_t iNumber, int32_t iAtClient, const char *szAtClientNa // At client AtClient=iAtClient; SCopy(szAtClientName,AtClientName,C4MaxTitle); - if (Filename) + if (szFilename) { // Load core & crew info list // do not load portraits for remote players // this will prevent portraits from being shown for "remotely controlled"-Clonks of other players bool fLoadPortraits = (AtClient==C4ClientIDUnknown) || SEqualNoCase(AtClientName, Game.Clients.getLocalName()); // fLoadPortraits = true - if (!Load(Filename, !fScenarioInit, fLoadPortraits)) return false; + if (!Load(szFilename, !fScenarioInit, fLoadPortraits)) return false; } else { @@ -984,7 +980,7 @@ bool C4Player::Load(const char *szFilename, bool fSavegame, bool fLoadPortraits) { C4Group hGroup; // Open group - if (!hGroup.Open(szFilename)) return false; + if (!Reloc.Open(hGroup, szFilename)) return false; // Load core if (!C4PlayerInfoCore::Load(hGroup)) { hGroup.Close(); return false; } diff --git a/src/gui/C4FileSelDlg.cpp b/src/gui/C4FileSelDlg.cpp index e438fd528..6e910b0a6 100644 --- a/src/gui/C4FileSelDlg.cpp +++ b/src/gui/C4FileSelDlg.cpp @@ -569,10 +569,10 @@ C4PortraitSelDlg::C4PortraitSelDlg(C4FileSel_BaseCB *pSelCallback, bool fSetPict char path[_MAX_PATH+1]; // add common picture locations StdStrBuf strLocation; - SCopy(Config.AtUserDataPath(""), path, _MAX_PATH); TruncateBackslash(path); + SCopy(Config.General.UserDataPath, path, _MAX_PATH); TruncateBackslash(path); strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_USERPATH")); AddLocation(strLocation.getData(), path); - SCopy(Config.AtSystemDataPath(""), path, _MAX_PATH); TruncateBackslash(path); + SCopy(Config.General.SystemDataPath, path, _MAX_PATH); TruncateBackslash(path); strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_PROGRAMDIRECTORY")); AddCheckedLocation(strLocation.getData(), Config.General.ExePath); #ifdef _WIN32 @@ -653,7 +653,7 @@ bool C4PortraitSelDlg::SelectPortrait(C4GUI::Screen *pOnScreen, StdStrBuf *pSele { Log("Copying default portraits to user path..."); C4Group hGroup; - if (hGroup.Open(Config.AtSystemDataPath(C4CFN_Graphics))) + if (Reloc.Open(hGroup, C4CFN_Graphics)) { hGroup.Extract("Portrait1.png", Config.AtUserDataPath("Clonk.png")); hGroup.Close(); diff --git a/src/gui/C4LoaderScreen.cpp b/src/gui/C4LoaderScreen.cpp index d073cf6e3..58953e44f 100644 --- a/src/gui/C4LoaderScreen.cpp +++ b/src/gui/C4LoaderScreen.cpp @@ -73,7 +73,7 @@ bool C4LoaderScreen::Init(const char *szLoaderSpec) { // open it GfxGrp.Close(); - if (!GfxGrp.Open(Config.AtSystemDataPath(C4CFN_Graphics))) + if (!Reloc.Open(GfxGrp, C4CFN_Graphics)) { LogFatal(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"),C4CFN_Graphics,GfxGrp.GetError()).getData()); return false; diff --git a/src/gui/C4StartupPlrSelDlg.cpp b/src/gui/C4StartupPlrSelDlg.cpp index a7625cd02..84265b293 100644 --- a/src/gui/C4StartupPlrSelDlg.cpp +++ b/src/gui/C4StartupPlrSelDlg.cpp @@ -73,7 +73,7 @@ static bool GetPortrait(char **ppBytes, size_t *ipSize) C4Group GfxGroup; int iCount; StdStrBuf EntryName; - if (!GfxGroup.Open(Config.AtSystemDataPath(C4CFN_Graphics))) return false; + if (!Reloc.Open(GfxGroup, C4CFN_Graphics)) return false; if ((iCount = GfxGroup.EntryCount("Portrait*.png")) < 1) return false; EntryName.Format("Portrait%d.png", SafeRandom(iCount) + 1); if (!GfxGroup.LoadEntry(EntryName.getData(), ppBytes, ipSize)) return false; @@ -1245,7 +1245,7 @@ C4StartupPlrPropertiesDlg::C4StartupPlrPropertiesDlg(C4StartupPlrSelDlg::PlayerL // Set initial portrait and bigicon C4Group hGroup; StdStrBuf strPortrait; strPortrait.Format("Portrait%d.png", 1 + Random(5)); - if (hGroup.Open(Config.AtSystemDataPath(C4CFN_Graphics))) + if (Reloc.Open(hGroup, C4CFN_Graphics)) { hGroup.Extract(strPortrait.getData(), Config.AtTempPath("Portrait.png")); hGroup.Close(); diff --git a/src/gui/C4StartupScenSelDlg.cpp b/src/gui/C4StartupScenSelDlg.cpp index f64ea6d8b..de1afc605 100644 --- a/src/gui/C4StartupScenSelDlg.cpp +++ b/src/gui/C4StartupScenSelDlg.cpp @@ -1044,7 +1044,7 @@ bool C4ScenarioListLoader::RegularFolder::DoLoadContents(C4ScenarioListLoader *p ClearChildren(); // regular folders must exist and not be within group! assert(!pFromGrp); - if (sFilename[0]) + if (sFilename.getData() && sFilename[0]) Merge(sFilename.getData()); // get number of entries, to estimate progress @@ -1167,7 +1167,10 @@ bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder) if (!BeginActivity(true)) return false; if (pRootFolder) { delete pRootFolder; pRootFolder = NULL; } pCurrFolder = pRootFolder = new RegularFolder(NULL); - pRootFolder->Merge(Config.General.UserDataPath); + // Load regular game data if no explicit path specified + if(!sRootFolder.getData()) + for(C4Reloc::iterator iter = Reloc.begin(); iter != Reloc.end(); ++iter) + pRootFolder->Merge(iter->getData()); bool fSuccess = pRootFolder->LoadContents(this, NULL, &sRootFolder, false, false); EndActivity(); return fSuccess; @@ -1438,7 +1441,7 @@ void C4StartupScenSelDlg::OnShown() // init file list fIsInitialLoading = true; if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(); - pScenLoader->Load(StdStrBuf(Config.General.ExePath)); + pScenLoader->Load(StdStrBuf()); //Config.General.ExePath)); UpdateList(); UpdateSelection(); fIsInitialLoading = false; diff --git a/src/lib/texture/C4GraphicsResource.cpp b/src/lib/texture/C4GraphicsResource.cpp index 29913f0a3..36138fd2d 100644 --- a/src/lib/texture/C4GraphicsResource.cpp +++ b/src/lib/texture/C4GraphicsResource.cpp @@ -318,7 +318,7 @@ bool C4GraphicsResource::RegisterGlobalGraphics() // The cleanest alternative would be to reinit all the fonts whenever a scenario is reloaded // FIXME: Test whether vector fonts from a scenario are correctly reloaded C4Group *pMainGfxGrp = new C4Group(); - if (!pMainGfxGrp->Open(C4CFN_Graphics) || !Files.RegisterGroup(*pMainGfxGrp, true, C4GSPrio_Base, C4GSCnt_Graphics, 1)) + if (!Reloc.Open(*pMainGfxGrp, C4CFN_Graphics) || !Files.RegisterGroup(*pMainGfxGrp, true, C4GSPrio_Base, C4GSCnt_Graphics, 1)) { // error LogFatal(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"),C4CFN_Graphics,pMainGfxGrp->GetError()).getData()); diff --git a/src/network/C4Network2Res.cpp b/src/network/C4Network2Res.cpp index 30e0253a2..595c38242 100644 --- a/src/network/C4Network2Res.cpp +++ b/src/network/C4Network2Res.cpp @@ -386,20 +386,21 @@ bool C4Network2Res::SetByFile(const char *strFilePath, bool fTemp, C4Network2Res SCopy(strFilePath, szFile, sizeof(szFile)-1); // group? C4Group Grp; - if (Grp.Open(strFilePath)) + if (Reloc.Open(Grp, strFilePath)) return SetByGroup(&Grp, fTemp, eType, iResID, szResName, fSilent); // so it needs to be a file - if (!FileExists(szFile)) + StdStrBuf szFullFile; + if (!Reloc.LocateItem(szFile, szFullFile)) { if (!fSilent) LogF("SetByFile: file %s not found!", strFilePath); return false; } // calc checksum uint32_t iCRC32; - if (!C4Group_GetFileCRC(szFile, &iCRC32)) return false; + if (!C4Group_GetFileCRC(szFullFile.getData(), &iCRC32)) return false; #ifdef C4NET2RES_DEBUG_LOG // log LogSilentF("Network: Resource: complete %d:%s is file %s (%s)", iResID, szResName, szFile, fTemp ? "temp" : "static"); #endif // set core - Core.Set(eType, iResID, szResName, iCRC32); + Core.Set(eType, iResID, Config.AtRelativePath(szFullFile.getData()), iCRC32); // set own data fDirty = true; fTempFile = fTemp; diff --git a/src/platform/C4SoundSystem.cpp b/src/platform/C4SoundSystem.cpp index 34c8535fd..343f30b2c 100644 --- a/src/platform/C4SoundSystem.cpp +++ b/src/platform/C4SoundSystem.cpp @@ -447,7 +447,7 @@ bool C4SoundSystem::Init() ClearEffects(); // Open sound file if (!SoundFile.IsOpen()) - if (!SoundFile.Open(Config.AtSystemDataPath(C4CFN_Sound))) return false; + if (!Reloc.Open(SoundFile, C4CFN_Sound)) return false; #ifdef HAVE_LIBSDL_MIXER Mix_AllocateChannels(C4MaxSoundInstances); #endif