Skip to content

Commit 0c87ec9

Browse files
authored
Merge pull request #15 from cortex-command-community/filesystem-functions
Add filesystem functions
2 parents c343b50 + 0ef01a2 commit 0c87ec9

File tree

5 files changed

+333
-53
lines changed

5 files changed

+333
-53
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
120120

121121
- New `GameActivity::LockControlledActor` Lua function to allow grab player input in the way menus (buy menu/full inventorymenu) do.
122122

123+
- New `LuaMan` Lua I/O functions `DirectoryCreate`, `FileExists`, `DirectoryExists`, `FileRemove`, `DirectoryRemove`, `FileRename`, `DirectoryRename` and `IsValidModulePath`.
124+
123125
- New `FrameMan` Lua functions `SetHudDisabled(disabled, screenId)` and `IsHudDisabled(screenId)` that allows disabling a given screen's HUD, and checking whether it's currently disabled. The screenId parameters are optional and default to screen 0.
124126

125127
- Exposed `FrameMan` property `ScreenCount` to Lua (R).

Source/Managers/LuaMan.cpp

Lines changed: 201 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,17 @@ namespace RTE {
7171
.def("GetDirectoryList", &LuaStateWrapper::DirectoryList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator)
7272
.def("GetFileList", &LuaStateWrapper::FileList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator)
7373
.def("FileExists", &LuaStateWrapper::FileExists)
74+
.def("DirectoryExists", &LuaStateWrapper::DirectoryExists)
75+
.def("IsValidModulePath", &LuaStateWrapper::IsValidModulePath)
7476
.def("FileOpen", &LuaStateWrapper::FileOpen)
7577
.def("FileClose", &LuaStateWrapper::FileClose)
78+
.def("FileRemove", &LuaStateWrapper::FileRemove)
79+
.def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate1)
80+
.def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate2)
81+
.def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove1)
82+
.def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove2)
83+
.def("FileRename", &LuaStateWrapper::FileRename)
84+
.def("DirectoryRename", &LuaStateWrapper::DirectoryRename)
7685
.def("FileReadLine", &LuaStateWrapper::FileReadLine)
7786
.def("FileWriteLine", &LuaStateWrapper::FileWriteLine)
7887
.def("FileEOF", &LuaStateWrapper::FileEOF),
@@ -273,12 +282,21 @@ namespace RTE {
273282
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
274283

275284
// Passthrough LuaMan Functions
276-
const std::vector<std::string>* LuaStateWrapper::DirectoryList(const std::string& relativeDirectory) { return g_LuaMan.DirectoryList(relativeDirectory); }
277-
const std::vector<std::string>* LuaStateWrapper::FileList(const std::string& relativeDirectory) { return g_LuaMan.FileList(relativeDirectory); }
278-
bool LuaStateWrapper::FileExists(const std::string &fileName) { return g_LuaMan.FileExists(fileName); }
279-
int LuaStateWrapper::FileOpen(const std::string& fileName, const std::string& accessMode) { return g_LuaMan.FileOpen(fileName, accessMode); }
285+
const std::vector<std::string>* LuaStateWrapper::DirectoryList(const std::string& path) { return g_LuaMan.DirectoryList(path); }
286+
const std::vector<std::string>* LuaStateWrapper::FileList(const std::string& path) { return g_LuaMan.FileList(path); }
287+
bool LuaStateWrapper::FileExists(const std::string &path) { return g_LuaMan.FileExists(path); }
288+
bool LuaStateWrapper::DirectoryExists(const std::string &path) { return g_LuaMan.DirectoryExists(path); }
289+
bool LuaStateWrapper::IsValidModulePath(const std::string &path) { return g_LuaMan.IsValidModulePath(path); }
290+
int LuaStateWrapper::FileOpen(const std::string& path, const std::string& accessMode) { return g_LuaMan.FileOpen(path, accessMode); }
280291
void LuaStateWrapper::FileClose(int fileIndex) { return g_LuaMan.FileClose(fileIndex); }
281292
void LuaStateWrapper::FileCloseAll() { return g_LuaMan.FileCloseAll(); }
293+
bool LuaStateWrapper::FileRemove(const std::string& path) { return g_LuaMan.FileRemove(path); }
294+
bool LuaStateWrapper::DirectoryCreate1(const std::string& path) { return g_LuaMan.DirectoryCreate(path, false); }
295+
bool LuaStateWrapper::DirectoryCreate2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryCreate(path, recursive); }
296+
bool LuaStateWrapper::DirectoryRemove1(const std::string& path) { return g_LuaMan.DirectoryRemove(path, false); }
297+
bool LuaStateWrapper::DirectoryRemove2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryRemove(path, recursive); }
298+
bool LuaStateWrapper::FileRename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.FileRename(oldPath, newPath); }
299+
bool LuaStateWrapper::DirectoryRename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.DirectoryRename(oldPath, newPath); }
282300
std::string LuaStateWrapper::FileReadLine(int fileIndex) { return g_LuaMan.FileReadLine(fileIndex); }
283301
void LuaStateWrapper::FileWriteLine(int fileIndex, const std::string& line) { return g_LuaMan.FileWriteLine(fileIndex, line); }
284302
bool LuaStateWrapper::FileEOF(int fileIndex) { return g_LuaMan.FileEOF(fileIndex); }
@@ -349,7 +367,7 @@ namespace RTE {
349367

350368
// TODO
351369
// It would be nice to assign to least-saturated state, but that's a bit tricky with MO registering...
352-
/*auto itr = std::min_element(m_ScriptStates.begin(), m_ScriptStates.end(),
370+
/*auto itr = std::min_element(m_ScriptStates.begin(), m_ScriptStates.end(),
353371
[](const LuaStateWrapper& lhs, const LuaStateWrapper& rhs) { return lhs.GetRegisteredMOs().size() < rhs.GetRegisteredMOs().size(); }
354372
);
355373
@@ -364,7 +382,7 @@ namespace RTE {
364382
bool success = m_ScriptStates[ourState].GetMutex().try_lock();
365383
RTEAssert(success, "Script mutex was already locked while in a non-multithreaded environment!");
366384

367-
return &m_ScriptStates[ourState];
385+
return &m_ScriptStates[ourState];
368386
}
369387

370388
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -394,13 +412,13 @@ namespace RTE {
394412

395413
void LuaMan::ExecuteLuaScriptCallbacks() {
396414
std::vector<std::function<void()>> callbacks;
397-
415+
398416
// Move our functions into the local buffer to clear the existing callbacks and to lock for as little time as possible
399417
{
400418
std::scoped_lock lock(m_ScriptCallbacksMutex);
401419
callbacks.swap(m_ScriptCallbacks);
402420
}
403-
421+
404422
for (const std::function<void()> &callback : callbacks) {
405423
callback();
406424
}
@@ -860,7 +878,7 @@ namespace RTE {
860878

861879
bool LuaStateWrapper::TableEntryIsDefined(const std::string &tableName, const std::string &indexName) {
862880
std::lock_guard<std::recursive_mutex> lock(m_Mutex);
863-
881+
864882
// Push the table onto the stack, checking if it even exists.
865883
lua_getglobal(m_State, tableName.c_str());
866884
if (!lua_istable(m_State, -1)) {
@@ -928,40 +946,80 @@ namespace RTE {
928946

929947
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
930948

931-
const std::vector<std::string> * LuaMan::DirectoryList(const std::string &filePath) {
949+
const std::vector<std::string> * LuaMan::DirectoryList(const std::string &path) {
950+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
932951
auto *directoryPaths = new std::vector<std::string>();
933952

934-
for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + filePath)) {
935-
if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); }
953+
if (IsValidModulePath(fullPath)) {
954+
#ifndef _WIN32
955+
fullPath = GetCaseInsensitiveFullPath(fullPath);
956+
#endif
957+
if (std::filesystem::exists(fullPath))
958+
{
959+
for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) {
960+
if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); }
961+
}
962+
}
936963
}
937964
return directoryPaths;
938965
}
939966

940967
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
941968

942-
const std::vector<std::string> * LuaMan::FileList(const std::string &filePath) {
969+
const std::vector<std::string> * LuaMan::FileList(const std::string &path) {
970+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
943971
auto *filePaths = new std::vector<std::string>();
944972

945-
for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + filePath)) {
946-
if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); }
973+
if (IsValidModulePath(fullPath)) {
974+
#ifndef _WIN32
975+
fullPath = GetCaseInsensitiveFullPath(fullPath);
976+
#endif
977+
if (std::filesystem::exists(fullPath))
978+
{
979+
for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) {
980+
if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); }
981+
}
982+
}
947983
}
948984
return filePaths;
949985
}
950986

951987
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
952988

953-
bool LuaMan::FileExists(const std::string &fileName) {
954-
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(fileName);
955-
if ((fullPath.find("..") == std::string::npos) && (fullPath.find(System::GetModulePackageExtension()) != std::string::npos)) {
956-
return std::filesystem::exists(fullPath);
989+
bool LuaMan::FileExists(const std::string &path) {
990+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
991+
if (IsValidModulePath(fullPath)) {
992+
#ifndef _WIN32
993+
fullPath = GetCaseInsensitiveFullPath(fullPath);
994+
#endif
995+
return std::filesystem::is_regular_file(fullPath);
957996
}
997+
return false;
998+
}
999+
1000+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9581001

1002+
bool LuaMan::DirectoryExists(const std::string &path) {
1003+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
1004+
if (IsValidModulePath(fullPath)) {
1005+
#ifndef _WIN32
1006+
fullPath = GetCaseInsensitiveFullPath(fullPath);
1007+
#endif
1008+
return std::filesystem::is_directory(fullPath);
1009+
}
9591010
return false;
9601011
}
9611012

9621013
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9631014

964-
int LuaMan::FileOpen(const std::string &fileName, const std::string &accessMode) {
1015+
// TODO: Move to ModuleMan, once the ModuleMan PR has been merged
1016+
bool LuaMan::IsValidModulePath(const std::string &path) {
1017+
return (path.find("..") == std::string::npos) && (path.find(System::GetModulePackageExtension()) != std::string::npos);
1018+
}
1019+
1020+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1021+
1022+
int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) {
9651023
if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) {
9661024
g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified.");
9671025
return -1;
@@ -979,37 +1037,43 @@ namespace RTE {
9791037
return -1;
9801038
}
9811039

982-
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(fileName);
983-
if ((fullPath.find("..") == std::string::npos) && (fullPath.find(System::GetModulePackageExtension()) != std::string::npos)) {
984-
1040+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
1041+
if (IsValidModulePath(fullPath)) {
9851042
#ifdef _WIN32
9861043
FILE *file = fopen(fullPath.c_str(), accessMode.c_str());
9871044
#else
9881045
FILE *file = [&fullPath, &accessMode]() -> FILE* {
9891046
std::filesystem::path inspectedPath = System::GetWorkingDirectory();
9901047
const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath);
9911048

1049+
// Iterate over all path parts
9921050
for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) {
9931051
bool pathPartExists = false;
9941052

995-
// Check if a path part (directory or file) exists in the filesystem.
1053+
// Iterate over all entries in the path part's directory,
1054+
// to check if the path part is in there case insensitively
9961055
for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) {
997-
if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), (*relativeFilePathIterator).generic_string())) {
1056+
if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) {
9981057
inspectedPath = filesystemEntryPath;
1058+
1059+
// If the path part is found, stop looking for it
9991060
pathPartExists = true;
10001061
break;
10011062
}
10021063
}
1064+
10031065
if (!pathPartExists) {
1004-
// If this is the last part, then all directories in relativeFilePath exist, but the file doesn't.
1066+
// If this is the last part, then all directories in relativeFilePath exist, but the file doesn't
10051067
if (std::next(relativeFilePathIterator) == relativeFilePath.end()) {
10061068
return fopen((inspectedPath / relativeFilePath.filename()).generic_string().c_str(), accessMode.c_str());
10071069
}
1008-
// Some directory in relativeFilePath doesn't exist, so the file can't be created.
1070+
1071+
// Some directory in relativeFilePath doesn't exist, so the file can't be created
10091072
return nullptr;
10101073
}
10111074
}
1012-
// If the file exists, open it.
1075+
1076+
// If the file exists, open it
10131077
return fopen(inspectedPath.generic_string().c_str(), accessMode.c_str());
10141078
}();
10151079
#endif
@@ -1018,7 +1082,7 @@ namespace RTE {
10181082
return fileIndex;
10191083
}
10201084
}
1021-
g_ConsoleMan.PrintString("ERROR: Failed to open file " + fileName);
1085+
g_ConsoleMan.PrintString("ERROR: Failed to open file " + path);
10221086
return -1;
10231087
}
10241088

@@ -1039,6 +1103,112 @@ namespace RTE {
10391103
}
10401104
}
10411105

1106+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1107+
1108+
bool LuaMan::FileRemove(const std::string& path) {
1109+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
1110+
if (IsValidModulePath(fullPath)) {
1111+
#ifndef _WIN32
1112+
fullPath = GetCaseInsensitiveFullPath(fullPath);
1113+
#endif
1114+
if (std::filesystem::is_regular_file(fullPath)) {
1115+
return std::filesystem::remove(fullPath);
1116+
}
1117+
}
1118+
g_ConsoleMan.PrintString("ERROR: Failed to remove file " + path);
1119+
return false;
1120+
}
1121+
1122+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1123+
1124+
bool LuaMan::DirectoryCreate(const std::string& path, bool recursive) {
1125+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
1126+
if (IsValidModulePath(fullPath)) {
1127+
#ifndef _WIN32
1128+
fullPath = GetCaseInsensitiveFullPath(fullPath);
1129+
#endif
1130+
try {
1131+
if (recursive) {
1132+
return std::filesystem::create_directories(fullPath);
1133+
} else {
1134+
return std::filesystem::create_directory(fullPath);
1135+
}
1136+
} catch (const std::filesystem::filesystem_error &e) {}
1137+
}
1138+
g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path);
1139+
return false;
1140+
}
1141+
1142+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1143+
1144+
bool LuaMan::DirectoryRemove(const std::string& path, bool recursive) {
1145+
std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path);
1146+
if (IsValidModulePath(fullPath)) {
1147+
#ifndef _WIN32
1148+
fullPath = GetCaseInsensitiveFullPath(fullPath);
1149+
#endif
1150+
if (std::filesystem::is_directory(fullPath)) {
1151+
try {
1152+
if (recursive) {
1153+
return std::filesystem::remove_all(fullPath) > 0;
1154+
} else {
1155+
return std::filesystem::remove(fullPath);
1156+
}
1157+
} catch (const std::filesystem::filesystem_error &e) {}
1158+
}
1159+
}
1160+
g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path);
1161+
return false;
1162+
}
1163+
1164+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1165+
1166+
bool LuaMan::FileRename(const std::string& oldPath, const std::string& newPath) {
1167+
std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath);
1168+
std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath);
1169+
if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) {
1170+
#ifndef _WIN32
1171+
fullOldPath = GetCaseInsensitiveFullPath(fullOldPath);
1172+
fullNewPath = GetCaseInsensitiveFullPath(fullNewPath);
1173+
#endif
1174+
// Ensures parity between Linux which can overwrite an empty directory, while Windows can't
1175+
// Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can
1176+
if (std::filesystem::is_regular_file(fullOldPath) && !std::filesystem::exists(fullNewPath))
1177+
{
1178+
try {
1179+
std::filesystem::rename(fullOldPath, fullNewPath);
1180+
return true;
1181+
} catch (const std::filesystem::filesystem_error &e) {}
1182+
}
1183+
}
1184+
g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath);
1185+
return false;
1186+
}
1187+
1188+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1189+
1190+
bool LuaMan::DirectoryRename(const std::string& oldPath, const std::string& newPath) {
1191+
std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath);
1192+
std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath);
1193+
if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) {
1194+
#ifndef _WIN32
1195+
fullOldPath = GetCaseInsensitiveFullPath(fullOldPath);
1196+
fullNewPath = GetCaseInsensitiveFullPath(fullNewPath);
1197+
#endif
1198+
// Ensures parity between Linux which can overwrite an empty directory, while Windows can't
1199+
// Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can
1200+
if (std::filesystem::is_directory(fullOldPath) && !std::filesystem::exists(fullNewPath))
1201+
{
1202+
try {
1203+
std::filesystem::rename(fullOldPath, fullNewPath);
1204+
return true;
1205+
} catch (const std::filesystem::filesystem_error &e) {}
1206+
}
1207+
}
1208+
g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath);
1209+
return false;
1210+
}
1211+
10421212
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10431213

10441214
std::string LuaMan::FileReadLine(int fileIndex) {
@@ -1096,15 +1266,15 @@ namespace RTE {
10961266

10971267
void LuaMan::StartAsyncGarbageCollection() {
10981268
ZoneScoped;
1099-
1269+
11001270
std::vector<LuaStateWrapper*> allStates;
11011271
allStates.reserve(m_ScriptStates.size() + 1);
11021272

11031273
allStates.push_back(&m_MasterScriptState);
11041274
for (LuaStateWrapper& wrapper : m_ScriptStates) {
11051275
allStates.push_back(&wrapper);
11061276
}
1107-
1277+
11081278
m_GarbageCollectionTask = BS::multi_future<void>();
11091279
for (LuaStateWrapper* luaState : allStates) {
11101280
m_GarbageCollectionTask.push_back(

0 commit comments

Comments
 (0)