diff --git a/.gitignore b/.gitignore index 6771e7bcf..312e897e5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ compile_commands.json /NATPunchServer/Server/NATCompleteServer/Debug /NATPunchServer/Server/NATCompleteServer/Release /Documentation/Doxygen/Output -MemCleanupInfo.txt \ No newline at end of file +MemCleanupInfo.txt +.vscode diff --git a/Activities/GAScripted.cpp b/Activities/GAScripted.cpp index c5c7eee11..9c9fe7069 100644 --- a/Activities/GAScripted.cpp +++ b/Activities/GAScripted.cpp @@ -114,7 +114,7 @@ int GAScripted::Create(const GAScripted &reference) { int GAScripted::ReadProperty(const std::string_view &propName, Reader &reader) { if (propName == "ScriptPath") { - m_ScriptPath = CorrectBackslashesInPath(reader.ReadPropValue()); + m_ScriptPath = CorrectBackslashesInPath(g_PresetMan.FullModulePath(CorrectBackslashesInPath(reader.ReadPropValue()))); } else if (propName == "LuaClassName") { reader >> m_LuaClassName; } else if (propName == "AddPieSlice") { @@ -510,7 +510,7 @@ void GAScripted::Draw(BITMAP *pTargetBitmap, const Vector &targetPos) { void GAScripted::CollectRequiredAreas() { // Open the script file so we can check it out - std::ifstream *pScriptFile = new std::ifstream(m_ScriptPath.c_str()); + std::ifstream *pScriptFile = new std::ifstream(g_PresetMan.FullModulePath(m_ScriptPath.c_str())); if (!pScriptFile->good()) { return; } diff --git a/Entities/MovableObject.cpp b/Entities/MovableObject.cpp index d32dfbebd..c8f8ce11a 100644 --- a/Entities/MovableObject.cpp +++ b/Entities/MovableObject.cpp @@ -332,8 +332,8 @@ int MovableObject::ReadProperty(const std::string_view &propName, Reader &reader else if (propName == "HUDVisible") reader >> m_HUDVisible; else if (propName == "ScriptPath") { - std::string scriptPath = CorrectBackslashesInPath(reader.ReadPropValue()); - switch (LoadScript(scriptPath)) { + std::string scriptPath = g_PresetMan.FullModulePath(CorrectBackslashesInPath(reader.ReadPropValue())); + switch (LoadScript(CorrectBackslashesInPath(scriptPath))) { case 0: break; case -1: diff --git a/Entities/Scene.cpp b/Entities/Scene.cpp index 45b8a7a1c..927ba8425 100644 --- a/Entities/Scene.cpp +++ b/Entities/Scene.cpp @@ -1025,14 +1025,15 @@ int Scene::ExpandAIPlanAssemblySchemes() int Scene::SaveData(std::string pathBase) { - if (pathBase.empty()) + const std::string fullPathBase = g_PresetMan.FullModulePath(pathBase); + if (fullPathBase.empty()) return -1; if (!m_pTerrain) return 0; // Save Terrain's data - if (m_pTerrain->SaveData(pathBase) < 0) + if (m_pTerrain->SaveData(fullPathBase) < 0) { RTEAbort("Saving Terrain " + m_pTerrain->GetPresetName() + "\'s data failed!"); return -1; @@ -1048,7 +1049,7 @@ int Scene::SaveData(std::string pathBase) { std::snprintf(str, sizeof(str), "T%d", team); // Save unseen layer data to disk - if (m_apUnseenLayer[team]->SaveData(pathBase + " US" + str + ".png") < 0) + if (m_apUnseenLayer[team]->SaveData(fullPathBase + " US" + str + ".bmp") < 0) { g_ConsoleMan.PrintString("ERROR: Saving unseen layer " + m_apUnseenLayer[team]->GetPresetName() + "\'s data failed!"); return -1; diff --git a/GUI/GUIControlManager.cpp b/GUI/GUIControlManager.cpp index 065a165bb..7566bc062 100644 --- a/GUI/GUIControlManager.cpp +++ b/GUI/GUIControlManager.cpp @@ -1,4 +1,5 @@ #include "GUI.h" +#include "PresetMan.h" using namespace RTE; @@ -404,7 +405,8 @@ bool GUIControlManager::Save(GUIWriter *W) { bool GUIControlManager::Load(const std::string &Filename, bool keepOld) { GUIReader reader; - if (reader.Create(Filename.c_str()) != 0) { + const std::string pathFile = g_PresetMan.FullModulePath(Filename); + if (reader.Create(pathFile.c_str()) != 0) { return false; } diff --git a/GUI/GUISkin.cpp b/GUI/GUISkin.cpp index 24dc3fdd6..a98fd18ea 100644 --- a/GUI/GUISkin.cpp +++ b/GUI/GUISkin.cpp @@ -1,5 +1,6 @@ #include "GUI.h" #include "GUIReader.h" +#include "PresetMan.h" using namespace RTE; @@ -37,7 +38,7 @@ bool GUISkin::Load(const std::string &directory, const std::string &fileName) { // Destroy any previous instances Destroy(); - m_Directory = !directory.empty() ? (directory + "/") : ""; + m_Directory = g_PresetMan.FullModulePath(!directory.empty() ? (directory + "/") : ""); GUIReader skinFile; if (skinFile.Create(m_Directory + fileName) == -1) { diff --git a/Lua/LuaBindingsManagers.cpp b/Lua/LuaBindingsManagers.cpp index 8dc296a1e..bb73cdfd1 100644 --- a/Lua/LuaBindingsManagers.cpp +++ b/Lua/LuaBindingsManagers.cpp @@ -193,7 +193,10 @@ namespace RTE { .def("ReadReflectedPreset", &PresetMan::ReadReflectedPreset) .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset1) .def("ReloadEntityPreset", &LuaAdaptersPresetMan::ReloadEntityPreset2) - .def("ReloadAllScripts", &PresetMan::ReloadAllScripts); + .def("ReloadAllScripts", &PresetMan::ReloadAllScripts) + .def("IsModuleOfficial", &PresetMan::IsModuleOfficial) + .def("IsModuleUserdata", &PresetMan::IsModuleUserdata) + .def("FullModulePath", &PresetMan::FullModulePath); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Managers/ActivityMan.cpp b/Managers/ActivityMan.cpp index fea8551ad..67a3d37f9 100644 --- a/Managers/ActivityMan.cpp +++ b/Managers/ActivityMan.cpp @@ -91,7 +91,7 @@ namespace RTE { modifiableScene->SetSavedGameInternal(true); // Block the main thread for a bit to let the Writer access the relevant data. - std::unique_ptr writer(std::make_unique(c_UserScriptedSavesModuleName + "/" + fileName + ".ini")); + std::unique_ptr writer(std::make_unique(g_PresetMan.FullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ini")); writer->NewPropertyWithValue("Activity", activity); writer->NewPropertyWithValue("OriginalScenePresetName", scene->GetPresetName()); writer->NewPropertyWithValue("PlaceObjectsIfSceneIsRestarted", g_SceneMan.GetPlaceObjectsOnLoad()); @@ -120,7 +120,7 @@ namespace RTE { std::unique_ptr scene(std::make_unique()); std::unique_ptr activity(std::make_unique()); - Reader reader(c_UserScriptedSavesModuleName + "/" + fileName + ".ini", true, nullptr, true); + Reader reader(g_PresetMan.FullModulePath(c_UserScriptedSavesModuleName) + "/" + fileName + ".ini", true, nullptr, true); if (!reader.ReaderOK()) { g_ConsoleMan.PrintString("ERROR: Game loading failed! Make sure you have a saved game called \"" + fileName + "\""); return false; diff --git a/Managers/AudioMan.cpp b/Managers/AudioMan.cpp index b53d55044..4d500256c 100644 --- a/Managers/AudioMan.cpp +++ b/Managers/AudioMan.cpp @@ -8,6 +8,7 @@ #include "ActivityMan.h" #include "SoundContainer.h" #include "GUISound.h" +#include "PresetMan.h" namespace RTE { @@ -246,7 +247,8 @@ namespace RTE { void AudioMan::PlayMusic(const char *filePath, int loops, float volumeOverrideIfNotMuted) { if (m_AudioEnabled) { - if (m_IsInMultiplayerMode) { RegisterMusicEvent(-1, MUSIC_PLAY, filePath, loops); } + const std::string fullFilePath = g_PresetMan.FullModulePath(filePath); + if (m_IsInMultiplayerMode) { RegisterMusicEvent(-1, MUSIC_PLAY, fullFilePath.c_str(), loops); } bool musicIsPlaying; FMOD_RESULT result = m_MusicChannelGroup->isPlaying(&musicIsPlaying); @@ -263,15 +265,15 @@ namespace RTE { FMOD::Sound *musicStream; - result = m_AudioSystem->createStream(filePath, FMOD_3D_HEADRELATIVE | ((loops == 0 || loops == 1) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL), nullptr, &musicStream); + result = m_AudioSystem->createStream(fullFilePath.c_str(), FMOD_3D_HEADRELATIVE | ((loops == 0 || loops == 1) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL), nullptr, &musicStream); if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Could not open music file " + std::string(filePath) + ": " + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Could not open music file " + fullFilePath + ": " + std::string(FMOD_ErrorString(result))); return; } result = musicStream->setLoopCount(loops); if (result != FMOD_OK && (loops != 0 && loops != 1)) { - g_ConsoleMan.PrintString("ERROR: Failed to set looping for music file: " + std::string(filePath) + ". This means it will only play 1 time, instead of " + (loops == 0 ? "looping endlessly." : loops + " times.") + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Failed to set looping for music file: " + fullFilePath + ". This means it will only play 1 time, instead of " + (loops == 0 ? "looping endlessly." : loops + " times.") + std::string(FMOD_ErrorString(result))); } FMOD::Channel *musicChannel; @@ -282,7 +284,7 @@ namespace RTE { result = musicChannel->set3DAttributes(&zero_vector, nullptr); } if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Could not play music file: " + std::string(filePath) + ": " + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Could not play music file: " + fullFilePath + ": " + std::string(FMOD_ErrorString(result))); return; } result = musicChannel->setPriority(PRIORITY_HIGH); @@ -292,11 +294,11 @@ namespace RTE { volumeOverrideIfNotMuted = std::clamp((volumeOverrideIfNotMuted > 1.0F ? volumeOverrideIfNotMuted / 100.0F : volumeOverrideIfNotMuted), 0.0F, 1.0F); result = musicChannel->setVolume(volumeOverrideIfNotMuted); if (result != FMOD_OK && (loops != 0 && loops != 1)) { - g_ConsoleMan.PrintString("ERROR: Failed to set volume override for music file: " + std::string(filePath) + ". This means it will stay at " + std::to_string(m_MusicVolume) + ": " + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Failed to set volume override for music file: " + fullFilePath + ". This means it will stay at " + std::to_string(m_MusicVolume) + ": " + std::string(FMOD_ErrorString(result))); } } - m_MusicPath = filePath; + m_MusicPath = fullFilePath; result = musicChannel->setCallback(MusicChannelEndedCallback); if (result != FMOD_OK) { diff --git a/Managers/ConsoleMan.cpp b/Managers/ConsoleMan.cpp index fa35d44b1..cba4c0141 100644 --- a/Managers/ConsoleMan.cpp +++ b/Managers/ConsoleMan.cpp @@ -44,11 +44,11 @@ namespace RTE { if (!m_GUIInput) { m_GUIInput = new AllegroInput(-1); } if (!m_GUIControlManager) { m_GUIControlManager = new GUIControlManager(); } - if (!m_GUIControlManager->Create(m_GUIScreen, m_GUIInput, "Base.rte/GUIs/Skins/Menus", m_ConsoleUseMonospaceFont ? "ConsoleMonospaceSkin.ini" : "ConsoleSkin.ini")) { + if (!m_GUIControlManager->Create(m_GUIScreen, m_GUIInput, "Data/Base.rte/GUIs/Skins/Menus", m_ConsoleUseMonospaceFont ? "ConsoleMonospaceSkin.ini" : "ConsoleSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/ConsoleSkin.ini"); } - m_GUIControlManager->Load("Base.rte/GUIs/ConsoleGUI.ini"); + m_GUIControlManager->Load("Data/Base.rte/GUIs/ConsoleGUI.ini"); m_GUIControlManager->EnableMouse(false); // Stretch the invisible root box to fill the screen diff --git a/Managers/FrameMan.cpp b/Managers/FrameMan.cpp index aeb328870..bf2f6a2ba 100644 --- a/Managers/FrameMan.cpp +++ b/Managers/FrameMan.cpp @@ -1,6 +1,7 @@ #include "FrameMan.h" #include "PostProcessMan.h" +#include "PresetMan.h" #include "PrimitiveMan.h" #include "PerformanceMan.h" #include "ActivityMan.h" @@ -825,8 +826,9 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FrameMan::LoadPalette(const std::string &palettePath) { - BITMAP *tempBitmap = load_bitmap(palettePath.c_str(), m_Palette); - RTEAssert(tempBitmap, ("Failed to load palette from bitmap with following path:\n\n" + palettePath).c_str()); + const std::string fullPalettePath = g_PresetMan.FullModulePath(palettePath); + BITMAP *tempBitmap = load_bitmap(fullPalettePath.c_str(), m_Palette); + RTEAssert(tempBitmap, ("Failed to load palette from bitmap with following path:\n\n" + fullPalettePath).c_str()); set_palette(m_Palette); diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index 9549b83f9..32a1d2b74 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -209,6 +209,10 @@ namespace RTE { // Override "math.random" in the lua state to use RTETools MT19937 implementation. Preserve return types of original to not break all the things. "math.random = function(lower, upper) if lower ~= nil and upper ~= nil then return SelectRand(lower, upper); elseif lower ~= nil then return SelectRand(1, lower); else return PosRand(); end end" ); + // Override dofile() to be able to account for Data/ or Mods/ subfolder + luaL_dostring(m_MasterState, + "OriginalDoFile = dofile dofile = function(filePath) filePath = PresetMan:FullModulePath(filePath) if filePath ~= '' then return OriginalDoFile(filePath) end end;" + ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -353,12 +357,13 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int LuaMan::RunScriptFile(const std::string &filePath, bool consoleErrors) { - if (filePath.empty()) { + const std::string fullScriptPath = g_PresetMan.FullModulePath(filePath); + if (fullScriptPath.empty()) { m_LastError = "Can't run a script file with an empty filepath!"; return -1; } - if (!System::PathExistsCaseSensitive(filePath)) { + if (!System::PathExistsCaseSensitive(fullScriptPath)) { m_LastError = "Script file: " + filePath + " doesn't exist!"; if (consoleErrors) { g_ConsoleMan.PrintString("ERROR: " + m_LastError); @@ -370,8 +375,9 @@ namespace RTE { int error = 0; lua_pushcfunction(m_MasterState, &AddFileAndLineToError); + SetLuaPath(m_MasterState, fullScriptPath); // Load the script file's contents onto the stack and then execute it with pcall. Pcall will call the file and line error handler if there's an error by pointing 2 up the stack to it. - if (luaL_loadfile(m_MasterState, filePath.c_str()) || lua_pcall(m_MasterState, 0, LUA_MULTRET, -2)) { + if (luaL_loadfile(m_MasterState, fullScriptPath.c_str()) || lua_pcall(m_MasterState, 0, LUA_MULTRET, -2)) { m_LastError = lua_tostring(m_MasterState, -1); lua_pop(m_MasterState, 1); if (consoleErrors) { @@ -386,6 +392,27 @@ namespace RTE { return error; } + void LuaMan::SetLuaPath( lua_State *luaState, const std::string &filePath ) + { + const std::string moduleName = g_PresetMan.GetModuleNameFromPath( filePath ); + const std::string moduleFolder = g_PresetMan.IsModuleOfficial(moduleName) ? "Data/" : System::GetModDirectory() + "/"; + const std::string scriptPath = moduleFolder + moduleName + "/?.lua"; + + lua_getglobal( m_MasterState, "package" ); + lua_getfield( m_MasterState, -1, "path" ); // get field "path" from table at top of stack (-1) + std::string cur_path = lua_tostring( m_MasterState, -1 ); // grab path string from top of stack + + if (cur_path.find(scriptPath) == cur_path.npos) { // check if scriptPath is already in there + cur_path.append( ";" ); + cur_path.append( scriptPath ); // if not add it + } + + lua_pop( m_MasterState, 1 ); // get rid of the string on the stack we just pushed previously + lua_pushstring( m_MasterState, cur_path.c_str() ); // push the new one + lua_setfield( m_MasterState, -2, "path" ); // set the field "path" in table at -2 with value at top of stack + lua_pop( m_MasterState, 1 ); // get rid of package table from top of stack + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int LuaMan::RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects) { diff --git a/Managers/LuaMan.h b/Managers/LuaMan.h index 1a0736a15..79cd38131 100644 --- a/Managers/LuaMan.h +++ b/Managers/LuaMan.h @@ -118,6 +118,13 @@ namespace RTE { /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. int RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects); + + /// + /// Sets the proper package.path for the script to run. + /// + /// The script parent state. + /// The path to the file to load and run. + void SetLuaPath(lua_State* luaState, const std::string& filePath); #pragma endregion #pragma region diff --git a/Managers/PresetMan.cpp b/Managers/PresetMan.cpp index f72ee3fc0..726311941 100644 --- a/Managers/PresetMan.cpp +++ b/Managers/PresetMan.cpp @@ -29,8 +29,17 @@ #include "LoadingScreen.h" #include "SettingsMan.h" + + namespace RTE { + static const std::array officialModules = { "Base.rte", "Coalition.rte", "Imperatus.rte", "Techion.rte", "Dummy.rte", "Ronin.rte", "Browncoats.rte", "Uzira.rte", "MuIlaak.rte", "Missions.rte" }; + static const std::array, 3> userdataModules = { { + {c_UserScenesModuleName, "User Scenes"}, + {c_UserConquestSavesModuleName, "Conquest Saves"}, + {c_UserScriptedSavesModuleName, "Scripted Activity Saves" } + } }; + ////////////////////////////////////////////////////////////////////////////////////////// // Method: Clear ////////////////////////////////////////////////////////////////////////////////////////// @@ -142,13 +151,6 @@ bool PresetMan::LoadAllDataModules() { FindAndExtractZippedModules(); - std::array officialModules = { "Base.rte", "Coalition.rte", "Imperatus.rte", "Techion.rte", "Dummy.rte", "Ronin.rte", "Browncoats.rte", "Uzira.rte", "MuIlaak.rte", "Missions.rte" }; - std::array, 3> userdataModules = {{ - {c_UserScenesModuleName, "User Scenes"}, - {c_UserConquestSavesModuleName, "Conquest Saves"}, - {c_UserScriptedSavesModuleName, "Scripted Activity Saves" } - }}; - // Load all the official modules first! for (const std::string &officialModule : officialModules) { if (!LoadDataModule(officialModule, true, false, LoadingScreen::LoadingSplashProgressReport)) { @@ -157,29 +159,27 @@ bool PresetMan::LoadAllDataModules() { } // If a single module is specified, skip loading all other unofficial modules and load specified module only. - if (!m_SingleModuleToLoad.empty() && std::find(officialModules.begin(), officialModules.end(), m_SingleModuleToLoad) == officialModules.end()) { + if (!m_SingleModuleToLoad.empty() && !IsModuleOfficial(m_SingleModuleToLoad)) { if (!LoadDataModule(m_SingleModuleToLoad, false, false, LoadingScreen::LoadingSplashProgressReport)) { g_ConsoleMan.PrintString("ERROR: Failed to load DataModule \"" + m_SingleModuleToLoad + "\"! Only official modules were loaded!"); return false; } } else { - std::vector workingDirectoryFolders; - std::copy_if(std::filesystem::directory_iterator(System::GetWorkingDirectory()), std::filesystem::directory_iterator(), std::back_inserter(workingDirectoryFolders), + std::vector modDirectoryFolders; + const std::string modDirectory = System::GetWorkingDirectory() + System::GetModDirectory() + "/"; + std::copy_if(std::filesystem::directory_iterator(modDirectory), std::filesystem::directory_iterator(), std::back_inserter(modDirectoryFolders), [](auto dirEntry){ return std::filesystem::is_directory(dirEntry); } ); - std::sort(workingDirectoryFolders.begin(), workingDirectoryFolders.end()); + std::sort(modDirectoryFolders.begin(), modDirectoryFolders.end()); - for (const std::filesystem::directory_entry &directoryEntry : workingDirectoryFolders) { + for (const std::filesystem::directory_entry &directoryEntry : modDirectoryFolders) { std::string directoryEntryPath = directoryEntry.path().generic_string(); if (std::regex_match(directoryEntryPath, std::regex(".*\.rte"))) { std::string moduleName = directoryEntryPath.substr(directoryEntryPath.find_last_of('/') + 1, std::string::npos); - if (!g_SettingsMan.IsModDisabled(moduleName) && std::find(officialModules.begin(), officialModules.end(), moduleName) == officialModules.end()) { - auto userdataModuleItr = std::find_if(userdataModules.begin(), userdataModules.end(), [&moduleName](const auto &userdataModulesEntry) { return userdataModulesEntry.first == moduleName; }); - if (userdataModuleItr == userdataModules.end()) { - int moduleID = GetModuleID(moduleName); - // NOTE: LoadDataModule can return false (especially since it may try to load already loaded modules, which is okay) and shouldn't cause stop, so we can ignore its return value here. - if (moduleID < 0 || moduleID >= GetOfficialModuleCount()) { LoadDataModule(moduleName, false, false, LoadingScreen::LoadingSplashProgressReport); } - } + if (!g_SettingsMan.IsModDisabled(moduleName) && !IsModuleOfficial(moduleName) && !IsModuleUserdata(moduleName)) { + int moduleID = GetModuleID(moduleName); + // NOTE: LoadDataModule can return false (especially since it may try to load already loaded modules, which is okay) and shouldn't cause stop, so we can ignore its return value here. + if (moduleID < 0 || moduleID >= GetOfficialModuleCount()) { LoadDataModule(moduleName, false, false, LoadingScreen::LoadingSplashProgressReport); } } } } @@ -283,6 +283,32 @@ int PresetMan::GetModuleID(std::string moduleName) return -1; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +std::string PresetMan::GetModuleNameFromPath(std::string dataPath) +{ + if (dataPath.empty()) { + return ""; + } + + int slashPos = dataPath.find_first_of( '/' ); + if (slashPos == std::string::npos) { + slashPos = dataPath.find_first_of( '\\' ); + } + std::string moduleName = dataPath.substr( 0, slashPos ); + + // Check if path starts with Data/ or the Mods/Userdata dir names and remove that part to get to the actual module name. + if (moduleName == "Data" || moduleName == System::GetModDirectory() || moduleName == System::GetUserdataDirectory()) { + std::string shortenPath = dataPath.substr( slashPos + 1 ); + slashPos = shortenPath.find_first_of( '/' ); + if (slashPos == std::string::npos) { + slashPos = shortenPath.find_first_of( '\\' ); + } + moduleName = shortenPath.substr( 0, slashPos ); + } + + return moduleName; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetModuleIDFromPath @@ -291,16 +317,40 @@ int PresetMan::GetModuleID(std::string moduleName) int PresetMan::GetModuleIDFromPath(std::string dataPath) { - if (dataPath.empty()) + if (dataPath.empty()) { return -1; + } - int slashPos = dataPath.find_first_of('/'); - if (slashPos == std::string::npos) - slashPos = dataPath.find_first_of('\\'); + const std::string moduleName = GetModuleNameFromPath(dataPath); + return GetModuleID(moduleName); +} - return GetModuleID(dataPath.substr(0, slashPos)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool PresetMan::IsModuleOfficial(std::string moduleName) +{ + return std::find(officialModules.begin(), officialModules.end(), moduleName) != officialModules.end(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool PresetMan::IsModuleUserdata(std::string moduleName) { + auto userdataModuleItr = std::find_if(userdataModules.begin(), userdataModules.end(), [&moduleName](const auto &userdataModulesEntry) { return userdataModulesEntry.first == moduleName; }); + return userdataModuleItr != userdataModules.end(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +std::string PresetMan::FullModulePath(std::string modulePath) +{ + const std::string moduleName = GetModuleNameFromPath(modulePath); + const std::string moduleFolder = (IsModuleOfficial(moduleName) ? "Data" : IsModuleUserdata(moduleName) ? System::GetUserdataDirectory() : System::GetModDirectory()) + "/"; + const std::string topFolder = modulePath.substr(0, modulePath.find_first_of("/\\") + 1); + if (topFolder == moduleFolder) { + return modulePath; + } + return moduleFolder + modulePath; +} ////////////////////////////////////////////////////////////////////////////////////////// // Method: AddEntityPreset diff --git a/Managers/PresetMan.h b/Managers/PresetMan.h index 8570cb5a8..6979ea6c0 100644 --- a/Managers/PresetMan.h +++ b/Managers/PresetMan.h @@ -155,6 +155,12 @@ class PresetMan : public Singleton { int GetModuleID(std::string moduleName); + /// + /// Gets the Name of a loaded DataModule, from a full data file path. + /// + /// The full path to a data file inside the data module id you want to get. + /// The requested Name. If no module of the name was found, "" will be returned. + std::string GetModuleNameFromPath(std::string dataPath); ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetModuleIDFromPath @@ -165,6 +171,27 @@ class PresetMan : public Singleton { int GetModuleIDFromPath(std::string dataPath); + /// + /// Returns whether or not the module is vanilla. + /// + /// The name of the module to check, in the form "[moduleName].rte" + /// True if the module is an official data module, otherwise false. + bool IsModuleOfficial(std::string moduleName); + + + /// + /// Returns whether or not the module is vanilla. + /// + /// The name of the module to check, in the form "[moduleName].rte" + /// True if the module is a listed user data module, otherwise false. + bool IsModuleUserdata(std::string moduleName); + + /// + /// Returns the Full path to the module including Data/, Userdata/ or Mods/. + /// + /// The Path to be completed. + /// The complete path to the file, including Data/, Userdata/ or Mods/ based on whether or not it's part of an official module or userdata. + std::string FullModulePath(std::string modulePath); ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetTotalModuleCount diff --git a/Managers/SettingsMan.cpp b/Managers/SettingsMan.cpp index 2fc5f1e30..e31ac9a1b 100644 --- a/Managers/SettingsMan.cpp +++ b/Managers/SettingsMan.cpp @@ -17,7 +17,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsMan::Clear() { - m_SettingsPath = "Base.rte/Settings.ini"; + m_SettingsPath = "Data/Base.rte/Settings.ini"; m_SettingsNeedOverwrite = false; m_FlashOnBrainDamage = true; diff --git a/Menus/LoadingScreen.cpp b/Menus/LoadingScreen.cpp index 4d801ac88..d69438b09 100644 --- a/Menus/LoadingScreen.cpp +++ b/Menus/LoadingScreen.cpp @@ -25,8 +25,8 @@ namespace RTE { void LoadingScreen::Create(AllegroScreen *guiScreen, AllegroInput *guiInput, bool progressReportDisabled) { GUIControlManager loadingScreenManager; - RTEAssert(loadingScreenManager.Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "LoadingScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/LoadingScreenSkin.ini"); - loadingScreenManager.Load("Base.rte/GUIs/LoadingGUI.ini"); + RTEAssert(loadingScreenManager.Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "LoadingScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/LoadingScreenSkin.ini"); + loadingScreenManager.Load("Data/Base.rte/GUIs/LoadingGUI.ini"); int loadingSplashOffset = 0; if (!progressReportDisabled) { diff --git a/Menus/MainMenuGUI.cpp b/Menus/MainMenuGUI.cpp index 3f4455c76..832226611 100644 --- a/Menus/MainMenuGUI.cpp +++ b/Menus/MainMenuGUI.cpp @@ -50,12 +50,12 @@ namespace RTE { void MainMenuGUI::Create(AllegroScreen *guiScreen, AllegroInput *guiInput) { m_MainMenuScreenGUIControlManager = std::make_unique(); - RTEAssert(m_MainMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); - m_MainMenuScreenGUIControlManager->Load("Base.rte/GUIs/MainMenuGUI.ini"); + RTEAssert(m_MainMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuScreenSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuScreenSkin.ini"); + m_MainMenuScreenGUIControlManager->Load("Data/Base.rte/GUIs/MainMenuGUI.ini"); m_SubMenuScreenGUIControlManager = std::make_unique(); - RTEAssert(m_SubMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); - m_SubMenuScreenGUIControlManager->Load("Base.rte/GUIs/MainMenuSubMenuGUI.ini"); + RTEAssert(m_SubMenuScreenGUIControlManager->Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); + m_SubMenuScreenGUIControlManager->Load("Data/Base.rte/GUIs/MainMenuSubMenuGUI.ini"); GUICollectionBox *mainScreenRootBox = dynamic_cast(m_MainMenuScreenGUIControlManager->GetControl("root")); mainScreenRootBox->Resize(g_FrameMan.GetResX(), mainScreenRootBox->GetHeight()); diff --git a/Menus/MetagameGUI.cpp b/Menus/MetagameGUI.cpp index 5ac468c9d..1663bbad5 100644 --- a/Menus/MetagameGUI.cpp +++ b/Menus/MetagameGUI.cpp @@ -370,10 +370,10 @@ int MetagameGUI::Create(Controller *pController) m_pGUIInput = new AllegroInput(-1, true); if (!m_pGUIController) m_pGUIController = new GUIControlManager(); - if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini")) { + if (!m_pGUIController->Create(m_pGUIScreen, m_pGUIInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini")) { RTEAbort("Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); } - m_pGUIController->Load("Base.rte/GUIs/MetagameGUI.ini"); + m_pGUIController->Load("Data/Base.rte/GUIs/MetagameGUI.ini"); // Make sure we have convenient points to the containing GUI colleciton boxes that we will manipulate the positions of GUICollectionBox *pRootBox = m_apScreenBox[ROOTBOX] = dynamic_cast(m_pGUIController->GetControl("root")); @@ -1244,6 +1244,7 @@ bool MetagameGUI::LoadGame() bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resaveSceneData) { + const std::string fullSavePath = g_PresetMan.FullModulePath(savePath); // If specified, first load all bitmap data of all Scenes in the current Metagame that have once saved em, so we can re-save them to the new files if (resaveSceneData) g_MetaMan.LoadSceneData(); @@ -1256,7 +1257,7 @@ bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resa g_MetaMan.SaveSceneData(METASAVEPATH + saveName); // Whichever new or existing, create a writer with the path - Writer metaWriter(savePath.c_str()); + Writer metaWriter(fullSavePath.c_str()); // Now that all the updated data files have been written to disk and their paths updated, send the MetaMan state for actual writing to an ini if (g_MetaMan.Save(metaWriter) < 0) return false; @@ -1269,7 +1270,7 @@ bool MetagameGUI::SaveGame(std::string saveName, std::string savePath, bool resa // Create a new MetaSave preset that will hold the runtime info of this new save (so it shows up as something we can overwrite later this same runtime) MetaSave newSave; // This will automatically set all internal members to represent what MetaMan's current state is - newSave.Create(savePath); + newSave.Create(fullSavePath); newSave.SetPresetName(saveName); // Now add or update the actual Preset diff --git a/Menus/ModManagerGUI.cpp b/Menus/ModManagerGUI.cpp index 44425ae79..3f76efe3b 100644 --- a/Menus/ModManagerGUI.cpp +++ b/Menus/ModManagerGUI.cpp @@ -20,8 +20,8 @@ namespace RTE { ModManagerGUI::ModManagerGUI(AllegroScreen *guiScreen, AllegroInput *guiInput) { m_GUIControlManager = std::make_unique(); - RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); - m_GUIControlManager->Load("Base.rte/GUIs/ModManagerGUI.ini"); + RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); + m_GUIControlManager->Load("Data/Base.rte/GUIs/ModManagerGUI.ini"); GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(g_FrameMan.GetResX(), g_FrameMan.GetResY()); diff --git a/Menus/ScenarioGUI.cpp b/Menus/ScenarioGUI.cpp index e334e06bc..57e5ccfe3 100644 --- a/Menus/ScenarioGUI.cpp +++ b/Menus/ScenarioGUI.cpp @@ -52,8 +52,8 @@ namespace RTE { void ScenarioGUI::Create(AllegroScreen *guiScreen, AllegroInput *guiInput) { m_GUIControlManager = std::make_unique(); - RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); - m_GUIControlManager->Load("Base.rte/GUIs/ScenarioGUI.ini"); + RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); + m_GUIControlManager->Load("Data/Base.rte/GUIs/ScenarioGUI.ini"); m_RootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); m_RootBox->Resize(g_FrameMan.GetResX(), g_FrameMan.GetResY()); diff --git a/Menus/SettingsGUI.cpp b/Menus/SettingsGUI.cpp index 36347be91..ad6b55da2 100644 --- a/Menus/SettingsGUI.cpp +++ b/Menus/SettingsGUI.cpp @@ -14,8 +14,8 @@ namespace RTE { SettingsGUI::SettingsGUI(AllegroScreen *guiScreen, AllegroInput *guiInput) { m_GUIControlManager = std::make_unique(); - RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); - m_GUIControlManager->Load("Base.rte/GUIs/SettingsGUI.ini"); + RTEAssert(m_GUIControlManager->Create(guiScreen, guiInput, "Data/Base.rte/GUIs/Skins/Menus", "MainMenuSubMenuSkin.ini"), "Failed to create GUI Control Manager and load it from Base.rte/GUIs/Skins/Menus/MainMenuSubMenuSkin.ini"); + m_GUIControlManager->Load("Data/Base.rte/GUIs/SettingsGUI.ini"); GUICollectionBox *rootBox = dynamic_cast(m_GUIControlManager->GetControl("root")); rootBox->Resize(g_FrameMan.GetResX(), g_FrameMan.GetResY()); diff --git a/System/ContentFile.cpp b/System/ContentFile.cpp index 196074356..b0a099391 100644 --- a/System/ContentFile.cpp +++ b/System/ContentFile.cpp @@ -117,7 +117,8 @@ namespace RTE { } } if (fetchFileInfo) { - FILE *imageFile = fopen(m_DataPath.c_str(), "rb"); + std::string altDataPath = g_PresetMan.FullModulePath(m_DataPath); + FILE *imageFile = fopen(altDataPath.c_str(), "rb"); RTEAssert(imageFile, "Failed to open file prior to reading info of image file with following path and name:\n\n" + m_DataPath + "\n\nThe file may not exist or be corrupt."); if (m_DataPathExtension == ".png") { @@ -202,6 +203,7 @@ namespace RTE { BITMAP *returnBitmap = nullptr; const int bitDepth = (conversionMode == COLORCONV_8_TO_32) ? BitDepths::ThirtyTwo : BitDepths::Eight; std::string dataPathToLoad = dataPathToSpecificFrame.empty() ? m_DataPath : dataPathToSpecificFrame; + dataPathToLoad = g_PresetMan.FullModulePath(dataPathToLoad); SetFormattedReaderPosition(GetFormattedReaderPosition()); // Check if the file has already been read and loaded from the disk and, if so, use that data. @@ -235,19 +237,21 @@ namespace RTE { if (m_DataPath.empty() || frameCount < 1) { return; } + const std::string dataPathToLoad = g_PresetMan.FullModulePath(m_DataPath); + const std::string dataPathWithoutExtensionToLoad = g_PresetMan.FullModulePath(m_DataPathWithoutExtension); vectorToFill.reserve(frameCount); SetFormattedReaderPosition(GetFormattedReaderPosition()); if (frameCount == 1) { // Check for 000 in the file name in case it is part of an animation but the FrameCount was set to 1. Do not warn about this because it's normal operation, but warn about incorrect extension. - if (!System::PathExistsCaseSensitive(m_DataPath)) { + if (!System::PathExistsCaseSensitive(dataPathToLoad)) { const std::string altFileExtension = (m_DataPathExtension == ".png") ? ".bmp" : ".png"; - if (System::PathExistsCaseSensitive(m_DataPathWithoutExtension + "000" + m_DataPathExtension)) { - SetDataPath(m_DataPathWithoutExtension + "000" + m_DataPathExtension); - } else if (System::PathExistsCaseSensitive(m_DataPathWithoutExtension + "000" + altFileExtension)) { + if (System::PathExistsCaseSensitive(dataPathWithoutExtensionToLoad + "000" + m_DataPathExtension)) { + SetDataPath(dataPathWithoutExtensionToLoad + "000" + m_DataPathExtension); + } else if (System::PathExistsCaseSensitive(dataPathWithoutExtensionToLoad + "000" + altFileExtension)) { g_ConsoleMan.AddLoadWarningLogExtensionMismatchEntry(m_DataPath, m_FormattedReaderPosition, altFileExtension); - SetDataPath(m_DataPathWithoutExtension + "000" + altFileExtension); + SetDataPath(dataPathWithoutExtensionToLoad + "000" + altFileExtension); } } vectorToFill.emplace_back(GetAsBitmap(conversionMode)); @@ -287,16 +291,18 @@ namespace RTE { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; } + const std::string dataPathToLoad = g_PresetMan.FullModulePath(m_DataPath); + FMOD::Sound *returnSample = nullptr; - std::unordered_map::iterator foundSound = s_LoadedSamples.find(m_DataPath); + std::unordered_map::iterator foundSound = s_LoadedSamples.find(dataPathToLoad); if (foundSound != s_LoadedSamples.end()) { returnSample = (*foundSound).second; } else { returnSample = LoadAndReleaseSound(abortGameForInvalidSound, asyncLoading); //NOTE: This takes ownership of the sample file // Insert the Sound object into the map, PASSING OVER OWNERSHIP OF THE LOADED FILE - s_LoadedSamples.try_emplace(m_DataPath, returnSample); + s_LoadedSamples.try_emplace(dataPathToLoad, returnSample); } return returnSample; } @@ -307,13 +313,14 @@ namespace RTE { if (m_DataPath.empty() || !g_AudioMan.IsAudioEnabled()) { return nullptr; } - - if (!System::PathExistsCaseSensitive(m_DataPath)) { + const std::string dataPathToLoad = g_PresetMan.FullModulePath(m_DataPath); + if (!System::PathExistsCaseSensitive(dataPathToLoad)) { bool foundAltExtension = false; for (const std::string &altFileExtension : c_SupportedAudioFormats) { - if (System::PathExistsCaseSensitive(m_DataPathWithoutExtension + altFileExtension)) { + const std::string altDataPathToLoad = g_PresetMan.FullModulePath(m_DataPathWithoutExtension + altFileExtension); + if (System::PathExistsCaseSensitive(altDataPathToLoad)) { g_ConsoleMan.AddLoadWarningLogExtensionMismatchEntry(m_DataPath, m_FormattedReaderPosition, altFileExtension); - SetDataPath(m_DataPathWithoutExtension + altFileExtension); + SetDataPath(altDataPathToLoad); foundAltExtension = true; break; } @@ -325,7 +332,7 @@ namespace RTE { return nullptr; } } - if (std::filesystem::file_size(m_DataPath) == 0) { + if (std::filesystem::file_size(dataPathToLoad) == 0) { const std::string errorMessage = "Failed to create sound because the file was empty. The path and name were: "; RTEAssert(!abortGameForInvalidSound, errorMessage + "\n\n" + m_DataPathAndReaderPosition); g_ConsoleMan.PrintString("ERROR: " + errorMessage + m_DataPath); @@ -334,7 +341,7 @@ namespace RTE { FMOD::Sound *returnSample = nullptr; FMOD_MODE fmodFlags = FMOD_CREATESAMPLE | FMOD_3D | (asyncLoading ? FMOD_NONBLOCKING : FMOD_DEFAULT); - FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(m_DataPath.c_str(), fmodFlags, nullptr, &returnSample); + FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(dataPathToLoad.c_str(), fmodFlags, nullptr, &returnSample); if (result != FMOD_OK) { const std::string errorMessage = "Failed to create sound because of FMOD error:\n" + std::string(FMOD_ErrorString(result)) + "\nThe path and name were: "; diff --git a/System/DataModule.cpp b/System/DataModule.cpp index ad3859ab9..e7796e5bd 100644 --- a/System/DataModule.cpp +++ b/System/DataModule.cpp @@ -43,8 +43,8 @@ namespace RTE { if (progressCallback) { progressCallback(m_FileName + " " + static_cast(-43) + " loading:", true); } Reader reader; - std::string indexPath(m_FileName + "/Index.ini"); - std::string mergedIndexPath(m_FileName + "/MergedIndex.ini"); + std::string indexPath = g_PresetMan.FullModulePath(m_FileName + "/Index.ini"); + std::string mergedIndexPath = g_PresetMan.FullModulePath(m_FileName + "/MergedIndex.ini"); // NOTE: This looks for the MergedIndex.ini generated by the index merger tool. The tool is mostly superseded by disabling loading visuals, but still provides some benefit. if (std::filesystem::exists(mergedIndexPath)) { indexPath = mergedIndexPath; } @@ -70,7 +70,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool DataModule::CreateOnDiskAsUserdata(const std::string &moduleName, const std::string_view &friendlyName, bool ignoreMissingItems, bool scanFolderContents) { - std::string moduleNameWithPackageExtension = moduleName + ((moduleName.find(System::GetModulePackageExtension()) == moduleName.length() - System::GetModulePackageExtension().length()) ? "" : System::GetModulePackageExtension()); + std::string moduleNameWithPackageExtension = System::GetUserdataDirectory() + "/" + moduleName + ((moduleName.find(System::GetModulePackageExtension()) == moduleName.length() - System::GetModulePackageExtension().length()) ? "" : System::GetModulePackageExtension()); if (Writer writer(moduleNameWithPackageExtension + "/Index.ini", false, true); writer.WriterOK()) { DataModule newModule; newModule.m_IsUserdata = true; @@ -423,7 +423,8 @@ namespace RTE { int DataModule::FindAndRead(const ProgressCallback &progressCallback) { int result = 0; - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + m_FileName)) { + const std::string directoryToScan = g_PresetMan.FullModulePath(m_FileName); + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + directoryToScan)) { if (directoryEntry.path().extension() == ".ini" && directoryEntry.path().filename() != "Index.ini") { Reader iniReader; if (iniReader.Create(m_FileName + "/" + directoryEntry.path().filename().generic_string(), false, progressCallback) >= 0) { diff --git a/System/Reader.cpp b/System/Reader.cpp index 9e2fbadce..72ee60fcd 100644 --- a/System/Reader.cpp +++ b/System/Reader.cpp @@ -28,14 +28,14 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int Reader::Create(const std::string &fileName, bool overwrites, const ProgressCallback &progressCallback, bool failOK) { - m_FilePath = std::filesystem::path(fileName).generic_string(); + m_FilePath = g_PresetMan.FullModulePath(std::filesystem::path(fileName).generic_string()); if (m_FilePath.empty()) { return -1; } // Extract the file name and module name from the path m_FileName = m_FilePath.substr(m_FilePath.find_last_of("/\\") + 1); - m_DataModuleName = m_FilePath.substr(0, m_FilePath.find_first_of("/\\")); + m_DataModuleName = g_PresetMan.GetModuleNameFromPath(m_FilePath); m_DataModuleID = g_PresetMan.GetModuleID(m_DataModuleName); m_CanFail = failOK; @@ -291,7 +291,7 @@ namespace RTE { if (m_ReportProgress) { m_ReportProgress(m_ReportTabs + m_FileName + " on line " + std::to_string(m_CurrentLine) + " includes:", false); } // Get the file path from the current stream before pushing it into the StreamStack, otherwise we can't open a new stream after releasing it because we can't read. - std::string includeFilePath = std::filesystem::path(ReadPropValue()).generic_string(); + std::string includeFilePath = g_PresetMan.FullModulePath(std::filesystem::path(ReadPropValue()).generic_string()); // Push the current stream onto the StreamStack for future retrieval when the new include file has run out of data. m_StreamStack.push(StreamInfo(m_Stream.release(), m_FilePath, m_CurrentLine, m_PreviousIndent)); diff --git a/System/System.cpp b/System/System.cpp index ac6b4f1f0..7c8074f99 100644 --- a/System/System.cpp +++ b/System/System.cpp @@ -15,8 +15,9 @@ namespace RTE { std::vector System::s_WorkingTree; std::filesystem::file_time_type System::s_ProgramStartTime = std::filesystem::file_time_type::clock::now(); bool System::s_CaseSensitive = true; - const std::string System::s_ScreenshotDirectory = "_ScreenShots"; - const std::string System::s_ModDirectory = "_Mods"; + const std::string System::s_ScreenshotDirectory = "ScreenShots"; + const std::string System::s_ModDirectory = "Mods"; + const std::string System::s_UserdataDirectory = "Userdata"; const std::string System::s_ModulePackageExtension = ".rte"; const std::string System::s_ZippedModulePackageExtension = ".rte.zip"; const std::unordered_set System::s_SupportedExtensions = { ".ini", ".txt", ".lua", ".cfg", ".bmp", ".png", ".jpg", ".jpeg", ".wav", ".ogg", ".mp3", ".flac" }; @@ -27,8 +28,9 @@ namespace RTE { s_WorkingDirectory = std::filesystem::current_path().generic_string(); if (s_WorkingDirectory.back() != '/') { s_WorkingDirectory.append("/"); } - if (!std::filesystem::exists(s_WorkingDirectory + s_ScreenshotDirectory)) { MakeDirectory(s_WorkingDirectory + s_ScreenshotDirectory); } - //if (!std::filesystem::exists(s_WorkingDirectory + s_ModDirectory)) { MakeDirectory(s_WorkingDirectory + s_ModDirectory); } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ScreenshotDirectory)) { MakeDirectory(s_WorkingDirectory + s_ScreenshotDirectory); } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_ModDirectory)) { MakeDirectory(s_WorkingDirectory + s_ModDirectory); } + if (!PathExistsCaseSensitive(s_WorkingDirectory + s_UserdataDirectory)) { MakeDirectory(s_WorkingDirectory + s_UserdataDirectory); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/System/System.h b/System/System.h index 847d24af3..af331a27d 100644 --- a/System/System.h +++ b/System/System.h @@ -55,6 +55,13 @@ namespace RTE { /// Folder name of the mod directory. static const std::string & GetModDirectory() { return s_ModDirectory; } + + /// + /// Gets the userdata directory name. + /// + /// Folder name of the userdata directory. + static const std::string &GetUserdataDirectory() { return s_UserdataDirectory; } + /// /// Gets the extension that determines a directory/file is an RTE module. /// @@ -170,6 +177,7 @@ namespace RTE { static bool s_CaseSensitive; //!< Whether case sensitivity is enforced when checking for file existence. static const std::string s_ScreenshotDirectory; //!< String containing the folder name of the screenshots directory. static const std::string s_ModDirectory; //!< String containing the folder name of the mod directory. + static const std::string s_UserdataDirectory; //!< String containing the folder name of the userdata directory. static const std::string s_ModulePackageExtension; //!< The extension that determines a directory/file is a RTE module. static const std::string s_ZippedModulePackageExtension; //!< The extension that determines a file is a zipped RTE module.