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