diff --git a/Client/mods/deathmatch/logic/CResource.h b/Client/mods/deathmatch/logic/CResource.h index 37a2ab8560..99df87fc3c 100644 --- a/Client/mods/deathmatch/logic/CResource.h +++ b/Client/mods/deathmatch/logic/CResource.h @@ -69,6 +69,7 @@ class CResource void SetResourceEntity(CClientEntity* pEntity) { m_pResourceEntity = pEntity; } class CClientEntity* GetResourceDynamicEntity() { return m_pResourceDynamicEntity; } void SetResourceDynamicEntity(CClientEntity* pEntity) { m_pResourceDynamicEntity = pEntity; } + SString GetResourceDirectoryPath() { return GetResourceDirectoryPath(eAccessType::ACCESS_PUBLIC, ""); } SString GetResourceDirectoryPath(eAccessType accessType, const SString& strMetaPath); class CClientEntity* GetResourceGUIEntity() { return m_pResourceGUIEntity; } void SetResourceGUIEntity(CClientEntity* pEntity) { m_pResourceGUIEntity = pEntity; } diff --git a/Shared/mods/deathmatch/logic/lua/CLuaShared.cpp b/Shared/mods/deathmatch/logic/lua/CLuaShared.cpp index 1ca16ce6f4..2b83816e1c 100644 --- a/Shared/mods/deathmatch/logic/lua/CLuaShared.cpp +++ b/Shared/mods/deathmatch/logic/lua/CLuaShared.cpp @@ -77,6 +77,7 @@ void CLuaShared::LoadFunctions() CLuaCryptDefs::LoadFunctions(); CLuaFileDefs::LoadFunctions(); CLuaXMLDefs::LoadFunctions(); + CLuaPathDefs::LoadFunctions(); CLuaTrainTrackDefs::LoadFunctions(); CLuaUTFDefs::LoadFunctions(); CLuaUtilDefs::LoadFunctions(); @@ -85,6 +86,7 @@ void CLuaShared::LoadFunctions() void CLuaShared::AddClasses(lua_State* luaVM) { CLuaFileDefs::AddClass(luaVM); + CLuaPathDefs::AddClass(luaVM); CLuaXMLDefs::AddClass(luaVM); } diff --git a/Shared/mods/deathmatch/logic/lua/CLuaShared.h b/Shared/mods/deathmatch/logic/lua/CLuaShared.h index e484148c58..ee78828134 100644 --- a/Shared/mods/deathmatch/logic/lua/CLuaShared.h +++ b/Shared/mods/deathmatch/logic/lua/CLuaShared.h @@ -12,8 +12,9 @@ // Lua function definitions (shared) #include "luadefs/CLuaBitDefs.h" #include "luadefs/CLuaCryptDefs.h" -#include +#include "luadefs/CLuaFileDefs.h" #include "luadefs/CLuaMatrixDefs.h" +#include "luadefs/CLuaPathDefs.h" #include "luadefs/CLuaTrainTrackDefs.h" #include "luadefs/CLuaUTFDefs.h" #include "luadefs/CLuaUtilDefs.h" diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp index c02b7db854..bacfe9843b 100644 --- a/Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp @@ -38,10 +38,22 @@ static auto getResourceFilePath(CResource* thisResource, CResource* fileResource void CLuaFileDefs::LoadFunctions() { constexpr static const std::pair functions[]{ - {"fileOpen", fileOpen}, {"fileCreate", fileCreate}, {"fileExists", fileExists}, {"fileCopy", fileCopy}, - {"fileRename", fileRename}, {"fileDelete", fileDelete}, {"fileClose", fileClose}, {"fileFlush", fileFlush}, - {"fileRead", fileRead}, {"fileWrite", fileWrite}, {"fileGetPos", fileGetPos}, {"fileGetSize", fileGetSize}, - {"fileGetPath", fileGetPath}, {"fileIsEOF", fileIsEOF}, {"fileSetPos", fileSetPos}, {"fileGetContents", ArgumentParser}, + {"fileOpen", fileOpen}, + {"fileCreate", fileCreate}, + {"fileExists", fileExists}, + {"fileCopy", fileCopy}, + {"fileRename", fileRename}, + {"fileDelete", fileDelete}, + {"fileClose", fileClose}, + {"fileFlush", fileFlush}, + {"fileRead", fileRead}, + {"fileWrite", fileWrite}, + {"fileGetPos", fileGetPos}, + {"fileGetSize", fileGetSize}, + {"fileGetPath", fileGetPath}, + {"fileIsEOF", fileIsEOF}, + {"fileSetPos", fileSetPos}, + {"fileGetContents", ArgumentParser}, }; // Add functions @@ -388,8 +400,6 @@ int CLuaFileDefs::fileExists(lua_State* luaVM) CResource* pResource = pLuaMain->GetResource(); if (CResourceManager::ParseResourcePathInput(strInputPath, pResource, &strAbsPath)) { - SString strFilePath; - // Does file exist? bool bResult = FileExists(strAbsPath); lua_pushboolean(luaVM, bResult); diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.cpp new file mode 100644 index 0000000000..e626ed15f7 --- /dev/null +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.cpp @@ -0,0 +1,111 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" + +#ifndef MTA_CLIENT + // NOTE: Must be included before ILuaModuleManager.h which defines its own CChecksum type. + #include "CChecksum.h" +#endif + +#include "CLuaPathDefs.h" +#include "CScriptFile.h" +#include "CScriptArgReader.h" +#include + +void CLuaPathDefs::LoadFunctions() +{ + constexpr static const std::pair functions[]{ + {"pathListDir", ArgumentParser}, + {"pathIsFile", ArgumentParser}, + {"pathIsDirectory", ArgumentParser}, + }; + + // Add functions + for (const auto& [name, func] : functions) + CLuaCFunctions::AddFunction(name, func); +} + +void CLuaPathDefs::AddClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + + lua_classfunction(luaVM, "listDir", "pathListDir"); + lua_classfunction(luaVM, "isFile", "pathIsFile"); + lua_classfunction(luaVM, "isDirectory", "pathIsDirectory"); + + lua_registerclass(luaVM, "path"); +} + +std::optional> CLuaPathDefs::pathListDir( + lua_State* luaVM, + std::string path +) { + CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); + if (!pLuaMain) + return std::nullopt; + + std::string strAbsPath; + + CResource* pResource = pLuaMain->GetResource(); + if (!CResourceManager::ParseResourcePathInput(path, pResource, &strAbsPath)) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot parse provided path: \"%s\"", + path.c_str()); + return std::nullopt; + } + + if (!DirectoryExists(strAbsPath)) + { + m_pScriptDebugging->LogWarning(luaVM, "Directory \"%s\" doesn't exist!", + path.c_str()); + return std::nullopt; + } + + return SharedUtil::ListDir(strAbsPath.c_str()); +} + +bool CLuaPathDefs::pathIsFile(lua_State* luaVM, std::string path) +{ + CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); + if (!pLuaMain) + return false; + + std::string strAbsPath; + + CResource* pResource = pLuaMain->GetResource(); + if (!CResourceManager::ParseResourcePathInput(path, pResource, &strAbsPath)) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot parse provided path: \"%s\"", + path.c_str()); + return false; + } + + return SharedUtil::FileExists(strAbsPath); +} + +bool CLuaPathDefs::pathIsDirectory(lua_State* luaVM, std::string path) +{ + CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); + if (!pLuaMain) + return false; + + std::string strAbsPath; + + CResource* pResource = pLuaMain->GetResource(); + if (!CResourceManager::ParseResourcePathInput(path, pResource, &strAbsPath)) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot parse provided path: \"%s\"", + path.c_str()); + return false; + } + + return SharedUtil::DirectoryExists(strAbsPath.c_str()); +} diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.h b/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.h new file mode 100644 index 0000000000..cfc8ae1bae --- /dev/null +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaPathDefs.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.h + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once +#include "luadefs/CLuaDefs.h" + +class CLuaPathDefs : public CLuaDefs +{ +public: + static void LoadFunctions(); + static void AddClass(lua_State* luaVM); + +private: + static std::optional> pathListDir(lua_State* luaVM, std::string path); + + static bool pathIsFile(lua_State* luaVM, std::string path); + static bool pathIsDirectory(lua_State* luaVM, std::string path); +}; diff --git a/Shared/sdk/SharedUtil.File.h b/Shared/sdk/SharedUtil.File.h index 88436852c4..3def283a19 100644 --- a/Shared/sdk/SharedUtil.File.h +++ b/Shared/sdk/SharedUtil.File.h @@ -20,8 +20,8 @@ namespace SharedUtil // // Returns true if the file/directory exists // - bool FileExists(const SString& strFilename); - bool DirectoryExists(const SString& strPath); + bool FileExists(const std::string& strFilename) noexcept; + bool DirectoryExists(const std::string& strPath) noexcept; // // Load from a file @@ -102,6 +102,8 @@ namespace SharedUtil WString FromUTF8(const SString& strPath); SString ToUTF8(const WString& strPath); + std::vector ListDir(const char* szPath) noexcept; + namespace File { FILE* Fopen(const char* szFilename, const char* szMode); diff --git a/Shared/sdk/SharedUtil.File.hpp b/Shared/sdk/SharedUtil.File.hpp index 4fa724e489..351afa63c3 100644 --- a/Shared/sdk/SharedUtil.File.hpp +++ b/Shared/sdk/SharedUtil.File.hpp @@ -14,8 +14,9 @@ #include "SharedUtil.Misc.h" #include "SharedUtil.Buffer.h" #include +#include -#ifdef WIN32 +#ifdef _WIN32 #ifndef NOMINMAX #define NOMINMAX #endif @@ -29,38 +30,53 @@ #include #include #include - #include #endif // // Returns true if the file exists // -bool SharedUtil::FileExists(const SString& strFilename) +bool SharedUtil::FileExists(const std::string& strFilename) noexcept { -#ifdef WIN32 - DWORD dwAtr = GetFileAttributes(strFilename); +#if __cplusplus >= 201703L + namespace fs = std::filesystem; + std::error_code errorCode; + return fs::is_regular_file(strFilename.c_str(), errorCode); +#else + #ifdef _WIN32 + DWORD dwAtr = GetFileAttributes(strFilename.c_str()); if (dwAtr == INVALID_FILE_ATTRIBUTES) return false; - return ((dwAtr & FILE_ATTRIBUTE_DIRECTORY) == 0); -#else - std::error_code ec{}; - return std::filesystem::is_regular_file(static_cast(strFilename), ec); + return !(dwAtr & FILE_ATTRIBUTE_DIRECTORY); + #else + struct stat s; + if (!stat(strFilename.c_str(), &s)) + return false; + return s.st_mode & S_IFREG; + #endif #endif } // // Returns true if the directory exists // -bool SharedUtil::DirectoryExists(const SString& strPath) +bool SharedUtil::DirectoryExists(const std::string& strPath) noexcept { -#ifdef WIN32 - DWORD dwAtr = GetFileAttributes(strPath); +#if __cplusplus >= 201703L + namespace fs = std::filesystem; + std::error_code errorCode; + return fs::is_directory(strPath.c_str(), errorCode); +#else + #ifdef _WIN32 + DWORD dwAtr = GetFileAttributes(strPath.c_str()); if (dwAtr == INVALID_FILE_ATTRIBUTES) return false; - return ((dwAtr & FILE_ATTRIBUTE_DIRECTORY) != 0); -#else - std::error_code ec{}; - return std::filesystem::is_directory(static_cast(strPath), ec); + return (dwAtr & FILE_ATTRIBUTE_DIRECTORY) != 0; + #else + struct stat s; + if (!stat(strPath.c_str(), &s)) + return false; + return s.st_mode & S_IFDIR; + #endif #endif } @@ -85,7 +101,7 @@ bool SharedUtil::FileLoad(std::nothrow_t, const SString& filePath, SString& outB if (offset > GIBIBYTE) return false; -#if WIN32 +#ifdef _WIN32 WString wideFilePath; try @@ -192,7 +208,7 @@ bool SharedUtil::FileAppend(const SString& strFilename, const SString& strBuffer bool SharedUtil::FileDelete(const SString& strFilename, bool bForce) { -#ifdef WIN32 +#ifdef _WIN32 if (bForce) SetFileAttributes(strFilename, FILE_ATTRIBUTE_NORMAL); #endif @@ -201,7 +217,7 @@ bool SharedUtil::FileDelete(const SString& strFilename, bool bForce) bool SharedUtil::FileRename(const SString& strFilenameOld, const SString& strFilenameNew, int* pOutErrorCode) { -#ifdef WIN32 +#ifdef _WIN32 if (MoveFileExW(FromUTF8(strFilenameOld), FromUTF8(strFilenameNew), MOVEFILE_COPY_ALLOWED) == 0) { int errorCode = GetLastError(); @@ -268,7 +284,7 @@ bool SharedUtil::FileLoad(const SString& strFilename, std::vector& buffer, // bool SharedUtil::FileSave(const SString& strFilename, const void* pBuffer, unsigned long ulSize, bool bForce) { -#ifdef WIN32 +#ifdef _WIN32 if (bForce) SetFileAttributes(strFilename, FILE_ATTRIBUTE_NORMAL); #endif @@ -292,7 +308,7 @@ bool SharedUtil::FileSave(const SString& strFilename, const void* pBuffer, unsig // bool SharedUtil::FileAppend(const SString& strFilename, const void* pBuffer, unsigned long ulSize, bool bForce) { -#ifdef WIN32 +#ifdef _WIN32 if (bForce) SetFileAttributes(strFilename, FILE_ATTRIBUTE_NORMAL); #endif @@ -319,7 +335,7 @@ uint64 SharedUtil::FileSize(const SString& strFilename) return 0; // Get size fseek(fh, 0, SEEK_END); -#ifdef WIN32 +#ifdef _WIN32 uint64 size = _ftelli64(fh); #elif defined(__APPLE__) uint64 size = ftello(fh); @@ -336,7 +352,7 @@ uint64 SharedUtil::FileSize(const SString& strFilename) // void SharedUtil::MakeSureDirExists(const SString& strPath) { -#ifdef WIN32 +#ifdef _WIN32 std::vector parts; PathConform(strPath).Split(PATH_SEPERATOR, parts); @@ -359,7 +375,7 @@ void SharedUtil::MakeSureDirExists(const SString& strPath) } #else std::filesystem::path filePath = static_cast(PathConform(strPath)); - std::error_code ec{}; + std::error_code ec{}; std::filesystem::create_directories(filePath.parent_path(), ec); #endif } @@ -367,7 +383,7 @@ void SharedUtil::MakeSureDirExists(const SString& strPath) SString SharedUtil::PathConform(const SString& strPath) { // Make slashes the right way and remove duplicates, except for UNC type indicators -#if WIN32 +#ifdef _WIN32 SString strTemp = strPath.Replace("/", PATH_SEPERATOR); #else SString strTemp = strPath.Replace("\\", PATH_SEPERATOR); @@ -427,7 +443,7 @@ SString SharedUtil::PathMakeRelative(const SString& strInBasePath, const SString SString SharedUtil::GetSystemCurrentDirectory() { -#ifdef WIN32 +#ifdef _WIN32 wchar_t szResult[1024] = L""; GetCurrentDirectoryW(NUMELMS(szResult), szResult); if (IsShortPathName(szResult)) @@ -440,7 +456,7 @@ SString SharedUtil::GetSystemCurrentDirectory() #endif } -#ifdef WIN32 +#ifdef _WIN32 #ifdef MTA_CLIENT SString SharedUtil::GetSystemDllDirectory() @@ -593,7 +609,7 @@ SString SharedUtil::GetDriveNameWithNotEnoughSpace(uint uiResourcesPathMinMB, ui } #endif // #ifdef MTA_CLIENT -#endif // #ifdef WIN32 +#endif // #ifdef _WIN32 WString SharedUtil::FromUTF8(const SString& strPath) { @@ -634,7 +650,7 @@ SString SharedUtil::ToUTF8(const WString& strPath) #endif } -#ifdef WIN32 +#ifdef _WIN32 /////////////////////////////////////////////////////////////// // // DelTree @@ -697,7 +713,7 @@ bool SharedUtil::FileCopy(const SString& strSrc, const SString& strDest, bool bF if (bForce) MakeSureDirExists(strDest); -#ifdef WIN32 +#ifdef _WIN32 if (bForce) SetFileAttributes(strDest, FILE_ATTRIBUTE_NORMAL); #endif @@ -729,7 +745,7 @@ bool SharedUtil::FileCopy(const SString& strSrc, const SString& strDest, bool bF return true; } -#ifdef WIN32 +#ifdef _WIN32 /////////////////////////////////////////////////////////////// // // FindFiles @@ -747,7 +763,7 @@ std::vector SharedUtil::FindFiles(const SString& strInMatch, bool bFile if (strMatch.Right(1) == PATH_SEPERATOR) strMatch += "*"; - WIN32_FIND_DATAW findData; + _WIN32_FIND_DATAW findData; HANDLE hFind = FindFirstFileW(FromUTF8(strMatch), &findData); if (hFind != INVALID_HANDLE_VALUE) { @@ -903,7 +919,7 @@ SString SharedUtil::MakeUniquePath(const SString& strInPathFilename) SString strTest = strPathFilename; int iCount = 1; -#ifdef WIN32 +#ifdef _WIN32 while (GetFileAttributes(strTest) != INVALID_FILE_ATTRIBUTES) #else while (DirectoryExists(strTest) || FileExists(strTest)) @@ -968,14 +984,14 @@ bool SharedUtil::IsAbsolutePath(const SString& strInPath) if (strPath.BeginsWith(PATH_SEPERATOR)) return true; -#ifdef WIN32 +#ifdef _WIN32 if (strPath.length() > 0 && strPath[1] == ':') return true; #endif return false; } -#ifdef WIN32 +#ifdef _WIN32 bool SharedUtil::IsShortPathName(const char* szPath) { return strchr(szPath, '~') != NULL; @@ -1003,11 +1019,11 @@ SString SharedUtil::GetSystemLongPathName(const SString& strPath) return strPath; return ToUTF8(szBuffer); } -#endif // WIN32 +#endif // _WIN32 FILE* SharedUtil::File::Fopen(const char* szFilename, const char* szMode) { -#ifdef WIN32 +#ifdef _WIN32 return _wfsopen(FromUTF8(szFilename), FromUTF8(szMode), _SH_DENYNO); #else return fopen(szFilename, szMode); @@ -1016,7 +1032,7 @@ FILE* SharedUtil::File::Fopen(const char* szFilename, const char* szMode) int SharedUtil::File::Mkdir(const char* szPath, int iMode) { -#ifdef WIN32 +#ifdef _WIN32 return _wmkdir(FromUTF8(szPath)); #else return mkdir(szPath, (mode_t)iMode); @@ -1025,7 +1041,7 @@ int SharedUtil::File::Mkdir(const char* szPath, int iMode) int SharedUtil::File::Chdir(const char* szPath) { -#ifdef WIN32 +#ifdef _WIN32 return _wchdir(FromUTF8(szPath)); #else return chdir(szPath); @@ -1034,7 +1050,7 @@ int SharedUtil::File::Chdir(const char* szPath) int SharedUtil::File::Rmdir(const char* szPath) { -#ifdef WIN32 +#ifdef _WIN32 return _wrmdir(FromUTF8(szPath)); #else return rmdir(szPath); @@ -1043,7 +1059,7 @@ int SharedUtil::File::Rmdir(const char* szPath) int SharedUtil::File::Delete(const char* szFilename) { -#ifdef WIN32 +#ifdef _WIN32 return _wremove(FromUTF8(szFilename)); #else return remove(szFilename); @@ -1052,9 +1068,63 @@ int SharedUtil::File::Delete(const char* szFilename) int SharedUtil::File::Rename(const char* szOldFilename, const char* szNewFilename) { -#ifdef WIN32 +#ifdef _WIN32 return _wrename(FromUTF8(szOldFilename), FromUTF8(szNewFilename)); #else return rename(szOldFilename, szNewFilename); #endif } + +std::vector SharedUtil::ListDir(const char* szPath) noexcept +{ + std::vector entries; +#if __cplusplus >= 201703L + namespace fs = std::filesystem; + if (!DirectoryExists(szPath)) + return {}; + + try + { + for (const auto& entry : fs::directory_iterator(szPath)) + { + if (entry.is_regular_file() || entry.is_directory()) + entries.push_back(entry.path().filename().string()); + } + } + catch (...) {} // catch all possible errors and ignore them +#else + #ifdef _WIN32 + std::string search_path = szPath; + if (search_path.empty()) + return {}; + + if (search_path.back() != '/') + search_path += "/*"; + + WIN32_FIND_DATA fd; + HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd); + if (hFind == INVALID_HANDLE_VALUE) + return {}; + + do + { + entries.push_back(fd.cFileName); + } while (::FindNextFile(hFind, &fd)); + ::FindClose(hFind); + + #else + DIR* dir; + struct dirent* ent; + if (!(dir = opendir(szPath))) + return {}; + + while ((ent = readdir(dir))) + { + entries.push_back(ent->d_name); + } + closedir(dir); + #endif +#endif + + return entries; +}