diff --git a/CLEO4.vcxproj b/CLEO4.vcxproj index 455665a4..49177435 100644 --- a/CLEO4.vcxproj +++ b/CLEO4.vcxproj @@ -60,6 +60,7 @@ + @@ -110,15 +111,15 @@ - $(SolutionDir)output\$(Configuration)\ - $(SolutionDir)output\.obj\$(Configuration)\ + $(SolutionDir).output\$(Configuration)\ + $(SolutionDir).output\.obj\$(Configuration)\ CLEO .asi $(PLUGIN_SDK_DIR)\shared\;$(PLUGIN_SDK_DIR)\shared\game\;$(IncludePath) - $(SolutionDir)output\$(Configuration)\ - $(SolutionDir)output\.obj\$(Configuration)\ + $(SolutionDir).output\$(Configuration)\ + $(SolutionDir).output\.obj\$(Configuration)\ CLEO .asi $(PLUGIN_SDK_DIR)\shared\;$(PLUGIN_SDK_DIR)\shared\game\;$(IncludePath) diff --git a/cleo_sdk/CLEO.h b/cleo_sdk/CLEO.h index 7eaa39db..fe3cda13 100644 --- a/cleo_sdk/CLEO.h +++ b/cleo_sdk/CLEO.h @@ -41,6 +41,12 @@ typedef union #define globalVarSString 0x0A //s$ #define localVarSString 0x0B //@s +// CLEO virtual paths prefixes. Expandable with CLEO_ResolvePath +const char DIR_GAME[] = "0:"; // game root directory +const char DIR_USER[] = "1:"; // game save directory +const char DIR_SCRIPT[] = "2:"; // current script directory +const char DIR_CLEO[] = "3:"; // game\cleo directory +const char DIR_MODULES[] = "4:"; // game\cleo\modules directory typedef int SCRIPT_HANDLE; typedef SCRIPT_HANDLE HANDLE_ACTOR, ACTOR, HACTOR, PED, HPED, HANDLE_PED; @@ -140,6 +146,9 @@ void WINAPI CLEO_AddScriptDeleteDelegate(FuncScriptDeleteDelegateT func); void WINAPI CLEO_RemoveScriptDeleteDelegate(FuncScriptDeleteDelegateT func); +// convert to absolute file path +void WINAPI CLEO_ResolvePath(CScriptThread* thread, char* inOutPath, DWORD pathMaxLen); + #ifdef __cplusplus } #endif //__cplusplus diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index e135a5b3..89db1ca1 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -6,7 +6,7 @@ #include "CTextManager.h" #include "CModelInfo.h" -#include +// #include namespace CLEO { DWORD FUNC_fopen; @@ -843,6 +843,7 @@ namespace CLEO { static const size_t store_size = 0x400; static ScmFunction *Store[store_size]; static size_t allocationPlace; // contains an index of last allocated object + void* moduleExportRef = 0; // modules switching. Points to modules baseIP in case if this is export call std::string savedScriptFileDir; // modules switching std::string savedScriptFileName; // modules switching @@ -1043,12 +1044,10 @@ namespace CLEO { //0A92=-1,create_custom_thread %1d% OpcodeResult __stdcall opcode_0A92(CRunningScript *thread) { - const char *script_name = readString(thread); - TRACE("[0A92] Starting new custom script %s from thread named %s", script_name, thread->GetName()); - char cwd[MAX_PATH]; - _getcwd(cwd, sizeof(cwd)); - _chdir(cleo_dir); - auto cs = new CCustomScript(script_name); + auto filename = thread->ResolvePath(readString(thread), DIR_CLEO); // legacy: default search location is game\cleo directory + TRACE("[0A92] Starting new custom script %s from thread named %s", filename.c_str(), thread->GetName()); + + auto cs = new CCustomScript(filename.c_str()); SetScriptCondResult(thread, cs && cs->IsOK()); if (cs && cs->IsOK()) { @@ -1059,9 +1058,9 @@ namespace CLEO { { if (cs) delete cs; SkipUnusedParameters(thread); - TRACE("[0A92] Failed to load script '%s' from script '%s'.", script_name, thread->GetName()); + TRACE("[0A92] Failed to load script '%s' from script '%s'.", filename.c_str(), thread->GetName()); } - _chdir(cwd); + return OR_CONTINUE; } @@ -1081,14 +1080,11 @@ namespace CLEO { //0A94=-1,create_custom_mission %1d% OpcodeResult __stdcall opcode_0A94(CRunningScript *thread) { - char script_name[MAX_PATH]; - readString(thread, script_name); - strcat(script_name, ".cm"); // add custom mission extension - TRACE("[0A94] Starting new custom mission %s from thread named %s", script_name, thread->GetName()); - char cwd[MAX_PATH]; - _getcwd(cwd, sizeof(cwd)); - _chdir(cleo_dir); - auto cs = new CCustomScript(script_name, true); + auto filename = thread->ResolvePath(readString(thread), DIR_CLEO); // legacy: default search location is game\cleo directory + filename += ".cm"; // add custom mission extension + TRACE("[0A94] Starting new custom mission %s from thread named %s", filename.c_str(), thread->GetName()); + + auto cs = new CCustomScript(filename.c_str(), true); SetScriptCondResult(thread, cs && cs->IsOK()); if (cs && cs->IsOK()) { @@ -1102,9 +1098,9 @@ namespace CLEO { { if (cs) delete cs; SkipUnusedParameters(thread); - TRACE("[0A94] Failed to load mission '%s' from script '%s'.", script_name, thread->GetName()); + TRACE("[0A94] Failed to load mission '%s' from script '%s'.", filename.c_str(), thread->GetName()); } - _chdir(cwd); + return OR_CONTINUE; } @@ -1148,20 +1144,17 @@ namespace CLEO { auto paramType = *thread->GetBytePointer(); if (paramType >= 1 && paramType <= 8) { - // integer param + // numbered predefined paths DWORD param; *thread >> param; - //_chdir(param ? GetUserDirectory() : ""); - if (param) ChangeToUserDir(); - else ChangeToProgramDir(""); + + std::string path = std::to_string(param); + path += ":"; + thread->SetWorkDir(path.c_str()); } else { - // string param - char buf[MAX_PATH]; - std::fill(buf, buf + sizeof(buf), '\0'); - GetScriptStringParam(thread, buf, (BYTE)sizeof(buf)); - _chdir(buf); + thread->SetWorkDir(readString(thread)); } return OR_CONTINUE; } @@ -1169,7 +1162,7 @@ namespace CLEO { //0A9A=3,%3d% = openfile %1d% mode %2d% // IF and SET OpcodeResult __stdcall opcode_0A9A(CRunningScript *thread) { - const char *fname = readString(thread); + auto filename = thread->ResolvePath(readString(thread)); auto paramType = *thread->GetBytePointer(); char mode[0x10]; @@ -1195,7 +1188,7 @@ namespace CLEO { GetScriptStringParam(thread, mode, sizeof(mode)); } - if (auto hfile = open_file(fname, mode, bLegacyMode)) + if (auto hfile = open_file(filename.c_str(), mode, bLegacyMode)) { GetInstance().OpcodeSystem.m_hFiles.insert(hfile); @@ -1208,9 +1201,6 @@ namespace CLEO { SetScriptCondResult(thread, false); } - char szBlah[MAX_PATH]; - _getcwd(szBlah, MAX_PATH); - return OR_CONTINUE; } @@ -1295,7 +1285,9 @@ namespace CLEO { //0AA2=2,%2h% = load_library %1d% // IF and SET OpcodeResult __stdcall opcode_0AA2(CRunningScript *thread) { - auto libHandle = LoadLibrary(readString(thread)); + auto filename = thread->ResolvePath(readString(thread)); + + auto libHandle = LoadLibrary(filename.c_str()); *thread << libHandle; SetScriptCondResult(thread, libHandle != nullptr); if (libHandle) GetInstance().OpcodeSystem.m_hNativeLibs.insert(libHandle); @@ -1596,7 +1588,9 @@ namespace CLEO { //0AAB=1, file_exists %1d% OpcodeResult __stdcall opcode_0AAB(CRunningScript *thread) { - DWORD fAttr = GetFileAttributes(readString(thread)); + auto filename = thread->ResolvePath(readString(thread)); + + DWORD fAttr = GetFileAttributes(filename.c_str()); SetScriptCondResult(thread, (fAttr != INVALID_FILE_ATTRIBUTES) && !(fAttr & FILE_ATTRIBUTE_DIRECTORY)); return OR_CONTINUE; } @@ -1604,7 +1598,9 @@ namespace CLEO { //0AAC=2, %2d% = load_audiostream %1d% // IF and SET OpcodeResult __stdcall opcode_0AAC(CRunningScript *thread) { - auto stream = GetInstance().SoundSystem.LoadStream(readString(thread)); + auto filename = thread->ResolvePath(readString(thread)); + + auto stream = GetInstance().SoundSystem.LoadStream(filename.c_str()); *thread << stream; SetScriptCondResult(thread, stream != nullptr); return OR_CONTINUE; @@ -1662,7 +1658,6 @@ namespace CLEO { //0AB1=-1,call_scm_func %1p% OpcodeResult __stdcall opcode_0AB1(CRunningScript *thread) { - BYTE* base = nullptr; int label = 0; char* moduleTxt = nullptr; @@ -1676,7 +1671,6 @@ namespace CLEO { case DT_LVAR: case DT_VAR_ARRAY: case DT_LVAR_ARRAY: - base = thread->GetBasePointer(); // current script *thread >> label; break; @@ -1702,26 +1696,30 @@ namespace CLEO { return OR_INTERRUPT; } } + + ScmFunction* scmFunc = new ScmFunction(thread); // parse module reference text if (moduleTxt != nullptr) { - std::string str(moduleTxt); + std::string_view str(moduleTxt); auto pos = str.find('@'); if (pos == str.npos) { std::string err(128, '\0'); - sprintf(err.data(), "Invalid module reference '%s' in 0AB1 opcode in script '%s'", str.c_str(), thread->GetScriptFileName()); + sprintf(err.data(), "Invalid module reference '%s' in 0AB1 opcode in script '%s'", moduleTxt, thread->GetScriptFileName()); Error(err.data()); return OR_INTERRUPT; } - str[pos] = '\0'; // split into two texts + std::string_view strExport = str.substr(0, pos); + std::string_view strModule = str.substr(pos + 1); - // get module's absolute path - std::string modulePath(&str[pos + 1]); - modulePath = ResolvePath(modulePath.c_str(), thread->GetScriptFileDir()); + // get module's file absolute path + auto modulePath = std::string(strModule); + modulePath = thread->ResolvePath(modulePath.c_str(), DIR_SCRIPT); // by default search relative to current script location - auto scriptRef = GetInstance().ModuleSystem.GetExport(modulePath.c_str(), &str[0]); + // get export reference + auto scriptRef = GetInstance().ModuleSystem.GetExport(modulePath, strExport); if (!scriptRef.Valid()) { std::string err(128, '\0'); @@ -1729,16 +1727,17 @@ namespace CLEO { Error(err.data()); return OR_INTERRUPT; } + scmFunc->moduleExportRef = scriptRef.base; // to be released on return - base = (BYTE*)scriptRef.base; + //thread->SetScriptFileDir(std::filesystem::path(modulePath).parent_path().string().c_str()); + //thread->SetScriptFileName(std::filesystem::path(modulePath).filename().string().c_str()); + thread->SetBaseIp(scriptRef.base); label = scriptRef.offset; } DWORD nParams = 0; if(*thread->GetBytePointer()) *thread >> nParams; - ScmFunction* scmFunc = new ScmFunction(thread); - static SCRIPT_VAR arguments[32]; SCRIPT_VAR* locals = thread->IsMission() ? missionLocals : thread->GetVarPtr(); SCRIPT_VAR* localsEnd = locals + 32; @@ -1804,7 +1803,6 @@ namespace CLEO { } // jump to label - thread->SetBaseIp(base); // script space ThreadJump(thread, label); // script offset return OR_CONTINUE; } @@ -1812,8 +1810,6 @@ namespace CLEO { //0AB2=-1,ret OpcodeResult __stdcall opcode_0AB2(CRunningScript *thread) { - GetInstance().ModuleSystem.ReleaseModuleRef((char*)thread->GetBasePointer()); // release module if one used - ScmFunction *scmFunc = ScmFunction::Store[reinterpret_cast(thread)->GetScmFunction()]; DWORD nRetParams = 0; @@ -1823,6 +1819,10 @@ namespace CLEO { scmFunc->Return(thread); if (nRetParams) SetScriptParams(thread, nRetParams); SkipUnusedParameters(thread); + + if(scmFunc->moduleExportRef != nullptr) + GetInstance().ModuleSystem.ReleaseModuleRef((char*)scmFunc->moduleExportRef); // exiting export - release module + delete scmFunc; return OR_CONTINUE; } @@ -1830,8 +1830,7 @@ namespace CLEO { //0AB3=2,var %1d% = %2d% OpcodeResult __stdcall opcode_0AB3(CRunningScript *thread) { - DWORD varId, - value; + DWORD varId, value; *thread >> varId >> value; GetInstance().ScriptEngine.CleoVariables[varId].dwParam = value; return OR_CONTINUE; @@ -2358,7 +2357,7 @@ namespace CLEO { *thread >> mi; CVehicleModelInfo* model; - // if 1.0 US, prefer GetModelInfo function — makes it compatible with fastman92's limit adjuster + // if 1.0 US, prefer GetModelInfo function � makes it compatible with fastman92's limit adjuster if (CLEO::GetInstance().VersionManager.GetGameVersion() == CLEO::GV_US10) { model = plugin::CallAndReturn(mi); } @@ -2385,7 +2384,7 @@ namespace CLEO { *thread >> mi; CVehicleModelInfo* model; - // if 1.0 US, prefer GetModelInfo function — makes it compatible with fastman92's limit adjuster + // if 1.0 US, prefer GetModelInfo function � makes it compatible with fastman92's limit adjuster if (CLEO::GetInstance().VersionManager.GetGameVersion() == CLEO::GV_US10) { model = plugin::CallAndReturn(mi); } @@ -2917,17 +2916,17 @@ extern "C" CRunningScript* WINAPI CLEO_CreateCustomScript(CRunningScript* fromThread, const char *script_name, int label) { + auto filename = fromThread->ResolvePath(script_name, DIR_CLEO); // legacy: default search location is game\cleo directory + if (label != 0) // create from label { - TRACE("Starting new custom script from thread named %s label %i", script_name, label); + TRACE("Starting new custom script from thread named %s label %i", filename.c_str(), label); } else { - TRACE("Starting new custom script %s", script_name); + TRACE("Starting new custom script %s", filename.c_str()); } - char cwd[MAX_PATH]; - _getcwd(cwd, sizeof(cwd)); - _chdir(cleo_dir); + // if "label == 0" then "script_name" need to be the file name auto cs = new CCustomScript(script_name, false, reinterpret_cast(fromThread), label); if (fromThread) SetScriptCondResult(fromThread, cs && cs->IsOK()); @@ -2941,8 +2940,9 @@ extern "C" if (cs) delete cs; if (fromThread) SkipUnusedParameters(fromThread); TRACE("Failed to load script '%s'.", script_name); + return nullptr; } - _chdir(cwd); + return cs; } @@ -2961,4 +2961,18 @@ extern "C" scriptDeleteDelegate -= func; } + void WINAPI CLEO_ResolvePath(CRunningScript* thread, char* inOutPath, DWORD pathMaxLen) + { + if (thread == nullptr || inOutPath == nullptr || pathMaxLen < 1) + { + return; // invalid param + } + + auto resolved = thread->ResolvePath(inOutPath); + + if (resolved.length() >= pathMaxLen) + resolved.resize(pathMaxLen - 1); // and terminator character + + std::memcpy(inOutPath, resolved.c_str(), resolved.length() + 1); // with terminator + } } \ No newline at end of file diff --git a/source/CCustomOpcodeSystem.h b/source/CCustomOpcodeSystem.h index 362d8878..c66f02d3 100644 --- a/source/CCustomOpcodeSystem.h +++ b/source/CCustomOpcodeSystem.h @@ -103,5 +103,6 @@ namespace CLEO RwTexture* WINAPI CLEO_GetScriptTextureById(CRunningScript* thread, int id); HSTREAM WINAPI CLEO_GetInternalAudioStream(CRunningScript* thread, CAudioStream* stream); CRunningScript* WINAPI CLEO_CreateCustomScript(CRunningScript* fromThread, const char* fileName, int label); + void WINAPI CLEO_ResolvePath(CRunningScript* thread, char* inOutPath, DWORD pathMaxLen); } } diff --git a/source/CGameVersionManager.cpp b/source/CGameVersionManager.cpp index eca415ff..7fcd200f 100644 --- a/source/CGameVersionManager.cpp +++ b/source/CGameVersionManager.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "CGameVersionManager.h" +#include "CleoVersion.h" namespace CLEO { @@ -148,7 +149,7 @@ namespace CLEO int __stdcall CLEO_GetVersion() { - return VERSION_LONG; + return CLEO_VERSION; } } } diff --git a/source/CModuleSystem.cpp b/source/CModuleSystem.cpp index 284f4f59..50ee476c 100644 --- a/source/CModuleSystem.cpp +++ b/source/CModuleSystem.cpp @@ -1,9 +1,11 @@ #include "stdafx.h" #include "cleo.h" #include "CModuleSystem.h" +#include "CFileMgr.h" +#include "FileEnumerator.h" #include -#include +// #include #include using namespace CLEO; @@ -13,21 +15,20 @@ void CModuleSystem::Clear() modules.clear(); } -const ScriptDataRef CModuleSystem::GetExport(const char* moduleName, const char* exportName) +const ScriptDataRef CModuleSystem::GetExport(std::string modulePath, std::string_view exportName) { - std::string path(moduleName); - NormalizePath(path); + NormalizePath(modulePath); - auto& it = modules.find(path); + auto& it = modules.find(modulePath); if (it == modules.end()) // module not loaded yet? { - if (!LoadFile(path.c_str())) + if (!LoadFile(modulePath.c_str())) { return {}; } // check if available now - it = modules.find(path); + it = modules.find(modulePath); if (it == modules.end()) { return {}; @@ -35,7 +36,7 @@ const ScriptDataRef CModuleSystem::GetExport(const char* moduleName, const char* } auto& module = it->second; - auto e = module.GetExport(exportName); + auto e = module.GetExport(std::string(exportName)); if (e.Valid()) { module.refCount++; @@ -59,31 +60,19 @@ bool CModuleSystem::LoadFile(const char* path) bool CModuleSystem::LoadDirectory(const char* path) { bool result = true; - - auto p = CLEO::ResolvePath(path); // actual absolute path - try - { - for (auto& it : std::filesystem::recursive_directory_iterator(p)) - { - auto& filePath = it.path(); - if (filePath.extension() == ".s") - { - result &= LoadFile(filePath.string().c_str()); - } - } - } - catch (const std::exception& ex) + FilesWalk(path, ".s", [&](const char* filename) { - TRACE("Error while iterating CLEO Modules: %s", ex.what()); - return false; - } + result &= LoadFile(filename); + }); return result; } bool CModuleSystem::LoadCleoModules() { - return LoadDirectory("3:\\"); // cleo\cleo_modules + std::string path = CFileMgr::ms_rootDirName; + path += "\\cleo\\cleo_modules"; + return LoadDirectory(path.c_str()); } void CLEO::CModuleSystem::AddModuleRef(const char* baseIP) @@ -129,43 +118,43 @@ void CModuleSystem::NormalizePath(std::string& path) void CModuleSystem::CModule::Update() { - while (updateActive) - { - if (!updateNeeded) - { - std::filesystem::file_time_type time; - try - { - time = std::filesystem::last_write_time(filepath); - } - catch (...) - { - time = {}; - } - - // file not exists or up to date - if (time == std::filesystem::file_time_type{} || time == fileTime) - { - // query files once a second - for(size_t i = 0; i < 100 && updateActive; i++) - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - continue; - } - - updateNeeded = true; - } - - if (refCount != 0) - { - continue; // module currently in use - } - - auto file = filepath; - auto result = LoadFromFile(file.c_str()); - updateNeeded = false; - TRACE("Module reload %s '%s'", result ? "OK" : "FAILED", file.c_str()); - } + // while (updateActive) + // { + // if (!updateNeeded) + // { + // std::filesystem::file_time_type time; + // try + // { + // time = std::filesystem::last_write_time(filepath); + // } + // catch (...) + // { + // time = {}; + // } + + // // file not exists or up to date + // if (time == std::filesystem::file_time_type{} || time == fileTime) + // { + // // query files once a second + // for(size_t i = 0; i < 100 && updateActive; i++) + // std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + // continue; + // } + + // updateNeeded = true; + // } + + // if (refCount != 0) + // { + // continue; // module currently in use + // } + + // auto file = filepath; + // auto result = LoadFromFile(file.c_str()); + // updateNeeded = false; + // TRACE("Module reload %s '%s'", result ? "OK" : "FAILED", file.c_str()); + // } } CModuleSystem::CModule::CModule() : @@ -193,7 +182,7 @@ void CModuleSystem::CModule::Clear() exports.clear(); refCount = 0; - fileTime = {}; + //fileTime = {}; } const char* CModuleSystem::CModule::GetFilepath() const @@ -209,14 +198,14 @@ bool CModuleSystem::CModule::LoadFromFile(const char* path) filepath = path; - try + /*try { fileTime = std::filesystem::last_write_time(path); } catch(...) { fileTime = {}; - } + }*/ std::ifstream file(path, std::ios::binary); if (!file.good()) @@ -359,12 +348,11 @@ bool CModuleSystem::CModule::LoadFromFile(const char* path) return true; } -const ScriptDataRef CModuleSystem::CModule::GetExport(const char* name) +const ScriptDataRef CModuleSystem::CModule::GetExport(std::string name) { - auto normalized = std::string(name); - ModuleExport::NormalizeName(normalized); + ModuleExport::NormalizeName(name); - auto& it = exports.find(normalized); + auto& it = exports.find(name); if (it == exports.end()) { return {}; diff --git a/source/CModuleSystem.h b/source/CModuleSystem.h index 286bb4c6..3d7a0355 100644 --- a/source/CModuleSystem.h +++ b/source/CModuleSystem.h @@ -1,6 +1,6 @@ #pragma once #include -#include +// #include #include #include #include @@ -24,7 +24,7 @@ namespace CLEO void Clear(); // registers module reference. Needs to be released with ReleaseModuleRef - const ScriptDataRef GetExport(const char* moduleName, const char* exportName); + const ScriptDataRef GetExport(std::string modulePath, std::string_view exportName); bool LoadFile(const char* const path); // single file bool LoadDirectory(const char* const path); // all modules in directory @@ -58,7 +58,7 @@ namespace CLEO // hot reloading when source file modified std::atomic refCount = 0; - std::filesystem::file_time_type fileTime; // last write time of source file + //std::filesystem::file_time_type fileTime; // last write time of source file void Update(); std::atomic updateActive = true; std::atomic updateNeeded = false; @@ -72,7 +72,7 @@ namespace CLEO void Clear(); const char* GetFilepath() const; bool LoadFromFile(const char* path); - const ScriptDataRef GetExport(const char* name); + const ScriptDataRef GetExport(std::string name); }; std::map modules; diff --git a/source/CPluginSystem.h b/source/CPluginSystem.h index 168659a8..f69bd7b3 100644 --- a/source/CPluginSystem.h +++ b/source/CPluginSystem.h @@ -14,16 +14,15 @@ namespace CLEO public: CPluginSystem() { - TRACE("Unloading plugins..."); - FilesWalk("cleo/*.cleo", [this](const char *libName) { - char libPath[MAX_PATH] = "cleo/"; - strcat(libPath, libName); - TRACE("Loading plugin %s", libPath); - HMODULE hlib = LoadLibrary(libPath); + TRACE("Loading plugins..."); + FilesWalk("cleo\\cleo_plugins", ".cleo", [this](const char *filename) + { + TRACE("Loading plugin %s", filename); + HMODULE hlib = LoadLibrary(filename); if (!hlib) { char message[MAX_PATH + 40]; - sprintf(message, "Error loading plugin %s", libPath); + sprintf(message, "Error loading plugin %s", filename); Warning(message); } else plugins.push_back(hlib); @@ -35,6 +34,6 @@ namespace CLEO std::for_each(plugins.begin(), plugins.end(), FreeLibrary); } - inline size_t GetNumPlugins() { return plugins.size(); } + inline size_t GetNumPlugins() { return plugins.size(); } }; } diff --git a/source/CScriptEngine.cpp b/source/CScriptEngine.cpp index de5e8d44..df8f33ac 100644 --- a/source/CScriptEngine.cpp +++ b/source/CScriptEngine.cpp @@ -3,7 +3,7 @@ #include "CFileMgr.h" #include "CGame.h" -#include +// #include namespace CLEO { @@ -692,21 +692,17 @@ namespace CLEO { if (CGame::bMissionPackGame == 0) // regular main game { - //MainScriptFileDir = "0:\\data\\script"; // at user data TODO: enable when CLEO virtual paths available - MainScriptFileDir = CFileMgr::ms_rootDirName; - MainScriptFileDir += "data\\script"; - + MainScriptFileDir = std::string(DIR_GAME) + "\\data\\script"; MainScriptFileName = "main.scm"; } else // mission pack { - //MainScriptFileDir = "1:\\MPACK\\MPACK"; // at user data TODO: enable when CLEO virtual paths available - MainScriptFileDir = CLEO::GetUserDirectory(); - MainScriptFileDir += "\\MPACK\\MPACK"; + MainScriptFileDir = std::string(DIR_USER) + "\\MPACK\\MPACK"; MainScriptFileDir += std::to_string(CGame::bMissionPackGame); - MainScriptFileName = "scr.scm"; } + + MainScriptCurWorkDir = DIR_GAME; } void CScriptEngine::LoadCustomScripts(bool load_mode) @@ -765,25 +761,25 @@ namespace CLEO memset(CleoVariables, 0, sizeof(CleoVariables)); } - char cwd[MAX_PATH]; - _getcwd(cwd, sizeof(cwd)); - _chdir(cleo_dir); + // [game root]\cleo + std::string scriptsDir = CFileMgr::ms_rootDirName; + scriptsDir += "\\cleo"; TRACE("Searching for cleo scripts"); - FilesWalk(cs_mask, [this](const char *filename) { + FilesWalk(scriptsDir.c_str(), cs_ext, [this](const char *filename) { LoadScript(filename); }); - FilesWalk(cs4_mask, [this](const char *filename) { + + FilesWalk(scriptsDir.c_str(), cs4_ext, [this](const char *filename) { auto cs = LoadScript(filename); if (cs) cs->SetCompatibility(CLEO_VER_4); }); - FilesWalk(cs3_mask, [this](const char *filename) { + + FilesWalk(scriptsDir.c_str(), cs3_ext, [this](const char *filename) { auto cs = LoadScript(filename); if (cs) cs->SetCompatibility(CLEO_VER_3); }); - - _chdir(cwd); } CCustomScript * CScriptEngine::LoadScript(const char * szFilePath) @@ -1023,7 +1019,7 @@ namespace CLEO CCustomScript::CCustomScript(const char *szFileName, bool bIsMiss, CCustomScript *parent, int label) : CRunningScript(), bSaveEnabled(false), bOK(false), LastSearchPed(0), LastSearchCar(0), LastSearchObj(0), - CompatVer(CLEO_VERSION) + CompatVer(CLEO_VER_4_4) { IsCustom(1); bIsMission = bUseMissionCleanup = bIsMiss; @@ -1034,10 +1030,12 @@ namespace CLEO TRACE("Loading custom script %s...", szFileName); // store script file directory and name - std::filesystem::path path = szFileName; - path = std::filesystem::absolute(path); - scriptFileDir = path.parent_path().string(); - scriptFileName = path.filename().string(); + // std::filesystem::path path = szFileName; + // path = std::filesystem::absolute(path); + // scriptFileDir = path.parent_path().string(); + // scriptFileName = path.filename().string(); + + workDir = "0:"; // game root try { diff --git a/source/CScriptEngine.h b/source/CScriptEngine.h index a1dac01e..9dc31aac 100644 --- a/source/CScriptEngine.h +++ b/source/CScriptEngine.h @@ -4,10 +4,9 @@ namespace CLEO { - const char cleo_dir[] = "./cleo"; - const char cs_mask[] = "./*.cs"; - const char cs4_mask[] = "./*.cs4"; - const char cs3_mask[] = "./*.cs3"; + const char cs_ext[] = ".cs"; + const char cs4_ext[] = ".cs4"; + const char cs3_ext[] = ".cs3"; class CCustomScript : public CRunningScript { @@ -31,6 +30,7 @@ namespace CLEO std::string scriptFileDir; std::string scriptFileName; + std::string workDir; public: inline RwTexture* GetScriptTextureById(unsigned int id) @@ -50,8 +50,8 @@ namespace CLEO inline void SetNotFlag(bool b) { NotFlag = b; } inline char GetNotFlag() { return NotFlag; } inline void IsCustom(bool b) { MemWrite(reinterpret_cast(this) + 0xDF, b); } - inline bool IsCustom() { return MemRead(reinterpret_cast(this) + 0xDF); } - inline bool IsOK() { return bOK; } + inline bool IsCustom() const { return MemRead(reinterpret_cast(this) + 0xDF); } + inline bool IsOK() const { return bOK; } inline void enable_saving(bool en = true) { bSaveEnabled = en; } inline void SetCompatibility(CLEO_Version ver) { CompatVer = ver; } inline CLEO_Version GetCompatibility() { return CompatVer; } @@ -81,6 +81,10 @@ namespace CLEO // filename with type extension of script's source file const char* GetScriptFileName() const { return scriptFileName.c_str(); } void SetScriptFileName(const char* filename) { scriptFileName = filename; } + + // current working directory of this script. Can be changed ith 0A99 + const char* GetWorkDir() const { return workDir.c_str(); } + void SetWorkDir(const char* directory) { workDir = directory; } }; class CScriptEngine : VInjectible @@ -95,6 +99,7 @@ namespace CLEO public: std::string MainScriptFileDir; std::string MainScriptFileName; + std::string MainScriptCurWorkDir; static SCRIPT_VAR CleoVariables[0x400]; diff --git a/source/CTextManager.cpp b/source/CTextManager.cpp index 245b4a74..3a4ba40d 100644 --- a/source/CTextManager.cpp +++ b/source/CTextManager.cpp @@ -101,17 +101,11 @@ namespace CLEO return szResult; } - const char fxt_mask[] = "./*.fxt"; - const char fxt_dir[] = "./cleo/cleo_text"; - CTextManager::CTextManager() : fxts(1, crc32FromUpcaseStdString) { - char cwd[MAX_PATH]; - _getcwd(cwd, sizeof(cwd)); - _chdir(fxt_dir); - // parse FXT files - FilesWalk(fxt_mask, [this](const char *fname) { + FilesWalk("cleo\\cleo_text", ".fxt", [this](const char *fname) + { TRACE("Parsing FXT file %s", fname); try { @@ -126,7 +120,6 @@ namespace CLEO Warning(ss.str().c_str()); } }); - _chdir(cwd); } const char* CTextManager::Get(const char* key) diff --git a/source/CTheScripts.cpp b/source/CTheScripts.cpp index bf510969..98c1010a 100644 --- a/source/CTheScripts.cpp +++ b/source/CTheScripts.cpp @@ -1,54 +1,130 @@ #include "stdafx.h" #include "CTheScripts.h" #include "cleo.h" -//#include "CScriptEngine.h" +// #include "CFileMgr.h" +using namespace CLEO; -bool CRunningScript::IsCustom() + +bool CRunningScript::IsCustom() const { - auto cs = reinterpret_cast(this); + auto cs = reinterpret_cast(this); return cs->IsCustom(); } -const char* CRunningScript::GetScriptFileDir() +const char* CRunningScript::GetScriptFileDir() const { if (IsCustom()) { - return reinterpret_cast(this)->GetScriptFileDir(); + return reinterpret_cast(this)->GetScriptFileDir(); } - return CLEO::GetInstance().ScriptEngine.MainScriptFileDir.c_str(); + return GetInstance().ScriptEngine.MainScriptFileDir.c_str(); } void CRunningScript::SetScriptFileDir(const char* directory) { if (IsCustom()) { - reinterpret_cast(this)->SetScriptFileDir(directory); + reinterpret_cast(this)->SetScriptFileDir(directory); return; } - CLEO::GetInstance().ScriptEngine.MainScriptFileDir = directory; + GetInstance().ScriptEngine.MainScriptFileDir = directory; } -const char* CRunningScript::GetScriptFileName() +const char* CRunningScript::GetScriptFileName() const { if (IsCustom()) { - return reinterpret_cast(this)->GetScriptFileName(); + return reinterpret_cast(this)->GetScriptFileName(); } - return CLEO::GetInstance().ScriptEngine.MainScriptFileName.c_str(); + return GetInstance().ScriptEngine.MainScriptFileName.c_str(); } void CRunningScript::SetScriptFileName(const char* filename) { if (IsCustom()) { - reinterpret_cast(this)->SetScriptFileName(filename); + reinterpret_cast(this)->SetScriptFileName(filename); + return; + } + + GetInstance().ScriptEngine.MainScriptFileName = filename; +} + +const char* CRunningScript::GetWorkDir() const +{ + if (IsCustom()) + { + return reinterpret_cast(this)->GetWorkDir(); + } + + return GetInstance().ScriptEngine.MainScriptCurWorkDir.c_str(); +} + +void CRunningScript::SetWorkDir(const char* directory) +{ + if (IsCustom()) + { + reinterpret_cast(this)->SetWorkDir(directory); return; } - CLEO::GetInstance().ScriptEngine.MainScriptFileName = filename; + GetInstance().ScriptEngine.MainScriptCurWorkDir = directory; +} + +std::string CRunningScript::ResolvePath(const char* path, const char* customWorkDir) const +{ + return {}; + //if (path == nullptr) + //{ + // return {}; + //} + + //std::string result; + //if (strlen(path) < 2 || path[1] != ':') // does not start with drive letter + //{ + // result = (customWorkDir != nullptr) ? customWorkDir : GetWorkDir(); + // result.push_back('\\'); + // result += path; + //} + //else + //{ + // result = path; + //} + + //// predefined CLEO paths starting with '[digit]:' + //if (result.length() < 2 || result[1] != ':' || + // result[0] < DIR_GAME[0] || result[0] > DIR_MODULES[0]) // supported range + //{ + // return result; // not predefined path prefix found + //} + + //if (result[0] == DIR_USER[0]) // saves/settings location + //{ + // return std::string(GetUserDirectory()) + &result[2]; // original path without '1:' prefix; + //} + + //if (result[0] == DIR_SCRIPT[0]) // current script location + //{ + // return std::string(GetScriptFileDir()) + &result[2]; // original path without '2:' prefix; + //} + + // game root directory + //std::string resolved = CFileMgr::ms_rootDirName; + + //if (result[0] == DIR_CLEO[0]) // cleo directory + //{ + // resolved += "\\cleo"; + //} + //else if (result[0] == DIR_MODULES[0]) // cleo modules directory + //{ + // resolved += "\\cleo\\cleo_modules"; + //} + + //resolved += &result[2]; // original path without 'X:' prefix + //return resolved; } diff --git a/source/CTheScripts.h b/source/CTheScripts.h index 3b189950..0ee03462 100644 --- a/source/CTheScripts.h +++ b/source/CTheScripts.h @@ -288,13 +288,20 @@ class CRunningScript bWastedBustedCheck = 1; } - bool IsCustom(); // is it CLEO Script? + bool IsCustom() const; // is it CLEO Script? // absolute path to directory where script's source file is located - const char* GetScriptFileDir(); + const char* GetScriptFileDir() const; void SetScriptFileDir(const char* directory); // filename with type extension of script's source file - const char* GetScriptFileName(); + const char* GetScriptFileName() const; void SetScriptFileName(const char* filename); + + // current working directory of this script. Can be changed ith 0A99 + const char* GetWorkDir() const; + void SetWorkDir(const char* directory); + + // convert to absolute path + std::string ResolvePath(const char* path, const char* customWorkDir = nullptr) const; }; diff --git a/source/CleoVersion.h b/source/CleoVersion.h new file mode 100644 index 00000000..8e8cf00e --- /dev/null +++ b/source/CleoVersion.h @@ -0,0 +1,16 @@ +#pragma once + +#define CLEO_VERSION_MAIN 4 +#define CLEO_VERSION_MAJOR 5 +#define CLEO_VERSION_MINOR 0 + +#define CLEO_VERSION ((CLEO_VERSION_MAIN << 24)|(CLEO_VERSION_MAJOR << 16)|(CLEO_VERSION_MINOR << 8)) + +#define CLEO_VERSION_DOT CLEO_VERSION_MAIN.CLEO_VERSION_MAJOR.CLEO_VERSION_MINOR +#define CLEO_VERSION_COMMA CLEO_VERSION_MAIN,CLEO_VERSION_MAJOR,CLEO_VERSION_MINOR + +#define __TO_STR(x) #x +#define TO_STR(x) __TO_STR(x) + +#define CLEO_VERSION_DOT_STR TO_STR(CLEO_VERSION_DOT) +#define CLEO_VERSION_COMMA_STR TO_STR(CLEO_VERSION_COMMA) diff --git a/source/FileEnumerator.h b/source/FileEnumerator.h index a61baffd..9cc52d25 100644 --- a/source/FileEnumerator.h +++ b/source/FileEnumerator.h @@ -1,18 +1,31 @@ #pragma once +// #include template -void FilesWalk(const char *file_mask, T cb) +void FilesWalk(const char* directory, const char* extension, T callback) { - HANDLE hSearch = NULL; - WIN32_FIND_DATA wfd; - memset(&wfd, 0, sizeof(WIN32_FIND_DATA)); + // try + // { + // for (auto& it : std::filesystem::directory_iterator(directory)) + // { + // if (it.is_regular_file()) + // { + // auto& filePath = it.path(); - if ((hSearch = FindFirstFile(file_mask, &wfd)) == INVALID_HANDLE_VALUE) return; + // if (extension != nullptr) + // { + // if (_stricmp(filePath.extension().string().c_str(), extension) != 0) + // { + // continue; + // } + // } - do - { - if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) cb(wfd.cFileName); - } while (FindNextFile(hSearch, &wfd)); - - FindClose(hSearch); + // callback(std::filesystem::absolute(filePath).string().c_str()); + // } + // } + // } + // catch (const std::exception& ex) + // { + // TRACE("Error while iterating directory: %s", ex.what()); + // } } diff --git a/source/cleo.cpp b/source/cleo.cpp index e4db8cac..5e0350c7 100644 --- a/source/cleo.cpp +++ b/source/cleo.cpp @@ -7,71 +7,6 @@ namespace CLEO CCleoInstance CleoInstance; CCleoInstance& GetInstance() { return CleoInstance; } - std::string ResolvePath(const char* path, const char* workDir) - { - if (path == nullptr) - { - return {}; - } - - std::string result; - if (strlen(path) < 2 || path[1] != ':') // does not start with drive letter - { - if (workDir != nullptr) - { - result = std::string(workDir) + '\\' + path; - } - else - { - // application's current working dir. Can be set with 0A99 - result = std::string(MAX_PATH, '\0'); - _getcwd(result.data(), MAX_PATH); - result.resize(strlen(result.data())); - - result.push_back('\\'); - result.append(path); - } - } - else - { - result = path; - } - - return result; - - // TODO: CLEO virtual paths. Enable later - // predefined CLEO paths starting with '[digit]:' - /*if (result.length() < 2 || result[1] != ':' || - result[0] < '0' || result[0] > '3') // supported range - { - return result; // not predefined path prefix found - } - - std::string resolved(MAX_PATH, '\0'); - - if (result[0] == '1') // 1: game saves - { - // TODO: move logic from CScriptEngine::Initialize() - resolved += &result[2]; // original path without '1:' prefix - return resolved; - } - - // 0: game root directory - // TODO: move logic from CScriptEngine::Initialize() - - if (result[0] == '2') // 2: cleo directory - { - resolved += "\\cleo"; - } - else if (result[0] == '3') // 3: cleo modules directory - { - resolved += "\\cleo\\cleo_modules"; - } - - resolved += &result[2]; // original path without 'X:' prefix - return resolved;*/ - } - void __declspec(naked) CCleoInstance::OnUpdateGameLogics() { //GetInstance().UpdateGameLogics(); // ! diff --git a/source/cleo.def b/source/cleo.def index c6383e8e..dc63e3b9 100644 --- a/source/cleo.def +++ b/source/cleo.def @@ -27,3 +27,4 @@ EXPORTS _CLEO_GetLastCreatedCustomScript@0 @24 _CLEO_AddScriptDeleteDelegate@4 @25 _CLEO_RemoveScriptDeleteDelegate@4 @26 + _CLEO_ResolvePath@12 @27 diff --git a/source/cleo.h b/source/cleo.h index 29093adb..4eaae708 100644 --- a/source/cleo.h +++ b/source/cleo.h @@ -6,6 +6,7 @@ #include "CDebug.h" #include "CDmaFix.h" #include "CGameMenu.h" +#include "CleoVersion.h" #include "CModuleSystem.h" #include "CPluginSystem.h" #include "CScriptEngine.h" @@ -17,6 +18,13 @@ namespace CLEO { + // CLEO virtual paths prefixes. Expandable with ResolvePath + const char DIR_GAME[] = "0:"; // game root directory + const char DIR_USER[] = "1:"; // game save directory + const char DIR_SCRIPT[] = "2:"; // current script directory + const char DIR_CLEO[] = "3:"; // game\cleo directory + const char DIR_MODULES[] = "4:"; // game\cleo\modules directory + class CCleoInstance { bool m_bStarted; @@ -50,10 +58,10 @@ namespace CLEO void Start() { CreateDirectory("cleo", NULL); - //CreateDirectory("cleo/cleo_modules", NULL); // TODO: enbale if cleo_modules approved + CreateDirectory("cleo/cleo_modules", NULL); CreateDirectory("cleo/cleo_saves", NULL); CreateDirectory("cleo/cleo_text", NULL); - CodeInjector.OpenReadWriteAccess(); // must do this earlier to ensure plugins write access on init + CodeInjector.OpenReadWriteAccess(); // must do this earlier to ensure plugins write access on init GameMenu.Inject(CodeInjector); DmaFix.Inject(CodeInjector); UpdateGameLogics = VersionManager.TranslateMemoryAddress(MA_UPDATE_GAME_LOGICS_FUNCTION); @@ -71,9 +79,6 @@ namespace CLEO }; CCleoInstance& GetInstance(); - - // get absolute path - std::string ResolvePath(const char* path, const char* workDir = nullptr); } #endif diff --git a/source/stdafx.h b/source/stdafx.h index c16fa8b5..6414ca8c 100644 --- a/source/stdafx.h +++ b/source/stdafx.h @@ -27,6 +27,7 @@ #include #include +#include "CleoVersion.h" #include "CTheScripts.h" enum CLEO_Version @@ -40,7 +41,7 @@ enum CLEO_Version CLEO_VER_4_4 = 0x0404, CLEO_VER_4 = CLEO_VER_4_4, - CLEO_VERSION = CLEO_VER_4, + //CLEO_VERSION = CLEO_VER_4, }; #define VERSION_LONG 0x04040400 #define VERSION_STRING "4.4.4"