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..96f82583 100644
--- a/cleo_sdk/CLEO.h
+++ b/cleo_sdk/CLEO.h
@@ -6,8 +6,7 @@
#include
-#define CLEO_VERSION 0x04040400
-#define CLEO_VERSIONTEXT "4.4.4"
+#define CLEO_VERSION 0x04050000
//result of CLEO_GetGameVersion()
#define GV_US10 0 //1.0 us
@@ -41,6 +40,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 +145,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..3f251dbd 100644
--- a/source/CCustomOpcodeSystem.cpp
+++ b/source/CCustomOpcodeSystem.cpp
@@ -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;
@@ -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/CGameMenu.cpp b/source/CGameMenu.cpp
index 3a6abec8..d12f54b3 100644
--- a/source/CGameMenu.cpp
+++ b/source/CGameMenu.cpp
@@ -6,8 +6,6 @@
namespace CLEO
{
- const char CLEO_VERSION_STR[] = VERSION_STRING;
-
void(__cdecl * TextDraw) (float x, float y, const char* text);
void(__cdecl * SetTextAlign) (BYTE nAlign);
void(__cdecl * SetTextFont) (BYTE nFont);
@@ -66,7 +64,7 @@ namespace CLEO
auto cs_count = GetInstance().ScriptEngine.WorkingScriptsCount();
auto plugin_count = GetInstance().PluginSystem.GetNumPlugins();
std::ostringstream cleo_text;
- cleo_text << "CLEO v" << CLEO_VERSION_STR;
+ cleo_text << "CLEO v" << CLEO_VERSION_DOT_STR;
#ifdef _DEBUG
cleo_text << " DEBUG";
#endif
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..44542254 100644
--- a/source/CModuleSystem.cpp
+++ b/source/CModuleSystem.cpp
@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "cleo.h"
#include "CModuleSystem.h"
+#include "CFileMgr.h"
+#include "FileEnumerator.h"
#include
#include
@@ -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)
@@ -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..f385a4d4 100644
--- a/source/CModuleSystem.h
+++ b/source/CModuleSystem.h
@@ -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
@@ -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/CTheScripts.cpp b/source/CTheScripts.cpp
index bf510969..c2326a48 100644
--- a/source/CTheScripts.cpp
+++ b/source/CTheScripts.cpp
@@ -1,54 +1,129 @@
#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;
}
- CLEO::GetInstance().ScriptEngine.MainScriptFileName = filename;
+ 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;
+ }
+
+ GetInstance().ScriptEngine.MainScriptCurWorkDir = directory;
+}
+
+std::string CRunningScript::ResolvePath(const char* path, const char* customWorkDir) const
+{
+ 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..0592a585 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
+ callback(std::filesystem::absolute(filePath).string().c_str());
+ }
+ }
+ }
+ catch (const std::exception& ex)
{
- if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) cb(wfd.cFileName);
- } while (FindNextFile(hSearch, &wfd));
-
- FindClose(hSearch);
+ 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..be7fe2ce 100644
--- a/source/stdafx.h
+++ b/source/stdafx.h
@@ -27,23 +27,21 @@
#include
#include
+#include "CleoVersion.h"
#include "CTheScripts.h"
enum CLEO_Version
{
- //CLEO_VER_1 = 0x0100,
- //CLEO_VER_2 = 0x0200,
- CLEO_VER_3 = 0x0300,
- CLEO_VER_4_MIN = 0x0400,
- CLEO_VER_4_2 = 0x0402,
- CLEO_VER_4_3 = 0x0403,
- CLEO_VER_4_4 = 0x0404,
+ //CLEO_VER_1 = 0x01000000,
+ //CLEO_VER_2 = 0x02000000,
+ CLEO_VER_3 = 0x03000000,
+ CLEO_VER_4_MIN = 0x04000000,
+ CLEO_VER_4_2 = 0x04020000,
+ CLEO_VER_4_3 = 0x04030000,
+ CLEO_VER_4_4 = 0x04040000,
CLEO_VER_4 = CLEO_VER_4_4,
-
- CLEO_VERSION = CLEO_VER_4,
+ CLEO_VER_CUR = CLEO_VERSION,
};
-#define VERSION_LONG 0x04040400
-#define VERSION_STRING "4.4.4"
#define CPOOL_USE_HANDLE_ACCESS