diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..a139f846
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,32 @@
+name: CLEO 5 Release Build
+
+on:
+ push:
+ branch:
+ - gh
+
+jobs:
+ build:
+ runs-on: windows-2019
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: "recursive"
+
+ - name: Add msbuild to PATH
+ uses: microsoft/setup-msbuild@v1
+
+ - name: Build Projects
+ shell: cmd
+ run: |
+ set PLUGIN_SDK_DIR=%GITHUB_WORKSPACE%\third-party\plugin-sdk
+ msbuild -m CLEO4.sln /property:Configuration=Release /property:Platform=GTASA
+
+ - name: VirusTotal Scan
+ uses: crazy-max/ghaction-virustotal@v4
+ with:
+ vt_api_key: ${{ secrets.VT_KEY }}
+ files: |
+ ./.output/Release/CLEO.asi
+ ./output/Release/CLEO.asi
diff --git a/CLEO4.vcxproj b/CLEO4.vcxproj
index b1bad3be..49177435 100644
--- a/CLEO4.vcxproj
+++ b/CLEO4.vcxproj
@@ -91,14 +91,14 @@
DynamicLibrary
false
MultiByte
- v143
+ v142
true
DynamicLibrary
true
MultiByte
- v143
+ v142
diff --git a/source/CDebug.h b/source/CDebug.h
index 4aafa23f..4f64a3c6 100644
--- a/source/CDebug.h
+++ b/source/CDebug.h
@@ -27,7 +27,7 @@ class CDebug
#ifdef _DEBUG
Trace("CLEO v%s DEBUG", CLEO_VERSION_DOT_STR);
-#elif
+#else
Trace("CLEO v%s", CLEO_VERSION_DOT_STR);
#endif
}
diff --git a/third-party/plugin-sdk/injector/assembly.hpp b/third-party/plugin-sdk/injector/assembly.hpp
new file mode 100644
index 00000000..69d418a3
--- /dev/null
+++ b/third-party/plugin-sdk/injector/assembly.hpp
@@ -0,0 +1,178 @@
+/*
+ * Injectors - Useful Assembly Stuff
+ *
+ * Copyright (C) 2012-2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+
+// This header is very restrict about compiler and architecture
+#ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly
+#error Cannot use this header in another compiler other than MSVC
+#endif
+#ifndef _M_IX86
+#error Supported only in x86
+#endif
+
+//
+#include "injector.hpp"
+
+namespace injector
+{
+ struct reg_pack
+ {
+ // The ordering is very important, don't change
+ // The first field is the last to be pushed and first to be poped
+
+ // PUSHFD / POPFD
+ uint32_t ef;
+
+ // PUSHAD/POPAD -- must be the lastest fields (because of esp)
+ union
+ {
+ uint32_t arr[8];
+ struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; };
+ };
+
+ enum reg_name {
+ reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax
+ };
+
+ enum ef_flag {
+ carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7,
+ direction_flag = 10, overflow_flag = 11
+ };
+
+ uint32_t& operator[](size_t i)
+ { return this->arr[i]; }
+ const uint32_t& operator[](size_t i) const
+ { return this->arr[i]; }
+
+ template // bit starts from 0, use ef_flag enum
+ bool flag()
+ {
+ return (this->ef & (1 << bit)) != 0;
+ }
+
+ bool jnb()
+ {
+ return flag() == false;
+ }
+ };
+
+ // Lowest level stuff (actual assembly) goes on the following namespace
+ // PRIVATE! Skip this, not interesting for you.
+ namespace injector_asm
+ {
+ // Wrapper functor, so the assembly can use some templating
+ template
+ struct wrapper
+ {
+ static void call(reg_pack* regs)
+ {
+ T fun; fun(*regs);
+ }
+ };
+
+ // Constructs a reg_pack and calls the wrapper functor
+ template // where W is of type wrapper
+ inline void __declspec(naked) make_reg_pack_and_call()
+ {
+ _asm
+ {
+ // Construct the reg_pack structure on the stack
+ pushad // Pushes general purposes registers to reg_pack
+ add [esp+12], 4 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called
+ pushfd // Pushes EFLAGS to reg_pack
+
+ // Call wrapper sending reg_pack as parameter
+ push esp
+ call W::call
+ add esp, 4
+
+ // Destructs the reg_pack from the stack
+ sub [esp+12+4], 4 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags)
+ popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-)
+ popad
+
+ // Back to normal flow
+ ret
+ }
+ }
+ };
+
+
+ /*
+ * MakeInline
+ * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address
+ */
+ template
+ void MakeInline(memory_pointer_tr at)
+ {
+ typedef injector_asm::wrapper functor;
+ if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail
+ MakeCALL(at, injector_asm::make_reg_pack_and_call);
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline
+ */
+ template
+ void MakeInline(memory_pointer_tr at, memory_pointer_tr end)
+ {
+ MakeRangedNOP(at, end);
+ MakeInline(at);
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but (at,end) are template parameters.
+ * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT
+ */
+ template
+ void MakeInline(FuncT func)
+ {
+ static std::unique_ptr static_func;
+ static_func.reset(new FuncT(std::move(func)));
+
+ // Encapsulates the call to static_func
+ struct Caps
+ {
+ void operator()(reg_pack& regs)
+ { (*static_func)(regs); }
+ };
+
+ // Does the actual MakeInline
+ return MakeInline(lazy_pointer::get(), lazy_pointer::get());
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but (end) is calculated by the length of a call instruction
+ */
+ template
+ void MakeInline(FuncT func)
+ {
+ return MakeInline(func);
+ }
+};
diff --git a/third-party/plugin-sdk/injector/calling.hpp b/third-party/plugin-sdk/injector/calling.hpp
new file mode 100644
index 00000000..0a1dd624
--- /dev/null
+++ b/third-party/plugin-sdk/injector/calling.hpp
@@ -0,0 +1,127 @@
+/*
+ * Injectors - Function Calls Using Variadic Templates
+ *
+ * Copyright (C) 2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+#include "injector.hpp"
+#include
+#include
+
+#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013
+#else
+#error "This feature is not supported on this compiler"
+#endif
+
+namespace injector
+{
+ template
+ struct cstd;
+
+ template
+ struct cstd
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(*)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct stdcall;
+
+ template
+ struct stdcall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__stdcall *)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct fastcall;
+
+ template
+ struct fastcall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__fastcall *)(Args...)) p.get();;
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct thiscall;
+
+ template
+ struct thiscall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__thiscall *)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ // Call function at the index @i from the vtable of the object @a[0]
+ template
+ static Ret vtbl(Args... a)
+ {
+ auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...)));
+ auto p = raw_ptr( (*obj.template get()) [i] );
+ return call(p, std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+}
+
diff --git a/third-party/plugin-sdk/injector/gvm/gvm.hpp b/third-party/plugin-sdk/injector/gvm/gvm.hpp
new file mode 100644
index 00000000..e47d4dd5
--- /dev/null
+++ b/third-party/plugin-sdk/injector/gvm/gvm.hpp
@@ -0,0 +1,229 @@
+/*
+ * Injectors - Base Header
+ *
+ * Copyright (C) 2012-2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+#include
+#include
+#include
+
+namespace injector
+{
+
+#if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those...
+
+/*
+ * game_version_manager
+ * Detects the game, the game version and the game region
+ * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended.
+ */
+#ifndef INJECTOR_OWN_GVM
+#ifndef INJECTOR_GVM_DUMMY
+class game_version_manager
+{
+ public:
+ // Set this if you would like that MessagesBox contain PluginName as caption
+ const char* PluginName;
+
+ private:
+ char game, region, major, minor, majorRevision, minorRevision, cracker, steam;
+
+ public:
+ game_version_manager()
+ {
+ #ifdef INJECTOR_GVM_PLUGIN_NAME
+ PluginName = INJECTOR_GVM_PLUGIN_NAME;
+ #else
+ PluginName = "Plugin-SDK Plugin";
+ #endif
+
+ this->Clear();
+ }
+
+
+ // Clear any information about game version
+ void Clear()
+ {
+ game = region = major = minor = majorRevision = minorRevision = cracker = steam = 0;
+ }
+
+ // Checks if I don't know the game we are attached to
+ bool IsUnknown() { return game == 0; }
+ // Checks if this is the steam version
+ bool IsSteam() { return steam != 0; }
+ // Gets the game we are attached to (0, '3', 'V', 'S', 'I', 'E')
+ char GetGame() { return game; }
+ // Gets the region from the game we are attached to (0, 'U', 'E');
+ char GetRegion() { return region; }
+ // Get major and minor version of the game (e.g. [major = 1, minor = 0] = 1.0)
+ int GetMajorVersion() { return major; }
+ int GetMinorVersion() { return minor; }
+ int GetMajorRevisionVersion() { return majorRevision; }
+ int GetMinorRevisionVersion() { return minorRevision; }
+
+ bool IsHoodlum() { return cracker == 'H'; }
+
+ // Region conditions
+ bool IsUS() { return region == 'U'; }
+ bool IsEU() { return region == 'E'; }
+
+ // Game Conditions
+ bool IsIII() { return game == '3'; }
+ bool IsVC () { return game == 'V'; }
+ bool IsSA () { return game == 'S'; }
+ bool IsIV () { return game == 'I'; }
+ bool IsEFLC(){ return game == 'E'; }
+
+ // Detects game, region and version; returns false if could not detect it
+ bool Detect();
+
+ // Gets the game version as text, the buffer must contain at least 32 bytes of space.
+ char* GetVersionText(char* buffer)
+ {
+ if(this->IsUnknown())
+ {
+ strcpy(buffer, "UNKNOWN GAME");
+ return buffer;
+ }
+
+ const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK";
+ const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION";
+ const char* s = this->IsSteam()? "Steam" : "";
+ sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s);
+ return buffer;
+ }
+
+
+ public:
+ // Raises a error saying that you could not detect the game version
+ void RaiseCouldNotDetect()
+ {
+ MessageBoxA(0,
+ "Could not detect the game version\nContact the mod creator!",
+ PluginName, MB_ICONERROR
+ );
+ }
+
+ // Raises a error saying that the exe version is incompatible (and output the exe name)
+ void RaiseIncompatibleVersion()
+ {
+ char buf[128], v[32];
+ sprintf(buf,
+ "An incompatible exe version has been detected! (%s)\nContact the mod creator!",
+ GetVersionText(v)
+ );
+ MessageBoxA(0, buf, PluginName, MB_ICONERROR);
+ }
+};
+#else // INJECTOR_GVM_DUMMY
+class game_version_manager
+{
+ public:
+ bool Detect() { return true; }
+};
+#endif // INJECTOR_GVM_DUMMY
+#endif // INJECTOR_OWN_GVM
+
+
+/*
+ * address_manager
+ * Address translator from 1.0 executables to other executables offsets
+ * Inherits from game_version_manager ;)
+ */
+class address_manager : public game_version_manager
+{
+ private:
+ address_manager()
+ {
+ this->Detect();
+ }
+
+ // You could implement your translator for the address your plugin uses
+ // If not implemented, the translator won't translate anything, just return the samething as before
+ #ifdef INJECTOR_GVM_HAS_TRANSLATOR
+ void* translator(void* p);
+ #else
+ void* translator(void* p) { return p; }
+ #endif
+
+ public:
+ // Translates address p to the running executable pointer
+ void* translate(void* p)
+ {
+ return translator(p);
+ }
+
+
+ public:
+ // Address manager singleton
+ static address_manager& singleton()
+ {
+ static address_manager m;
+ return m;
+ }
+
+ // Static version of translate()
+ static void* translate_address(void* p)
+ {
+ return singleton().translate(p);
+ }
+
+ //
+ static void set_name(const char* modname)
+ {
+ singleton().PluginName = modname;
+ }
+
+ public:
+ // Functors for memory translation:
+
+ // Translates aslr translator
+ struct fn_mem_translator_aslr
+ {
+ void* operator()(void* p) const
+ {
+ static uintptr_t module = (uintptr_t)GetModuleHandle(NULL);
+ return (void*)((uintptr_t)(p)-(0x400000 - module));
+ }
+ };
+
+ // Translates nothing translator
+ struct fn_mem_translator_nop
+ {
+ void* operator()(void* p) const
+ { return p; }
+ };
+
+ // Real translator
+ struct fn_mem_translator
+ {
+ void* operator()(void* p) const
+ { return translate_address(p); }
+ };
+};
+
+#endif // #if 1
+
+
+}
\ No newline at end of file
diff --git a/third-party/plugin-sdk/injector/gvm/translator.hpp b/third-party/plugin-sdk/injector/gvm/translator.hpp
new file mode 100644
index 00000000..c6ac4303
--- /dev/null
+++ b/third-party/plugin-sdk/injector/gvm/translator.hpp
@@ -0,0 +1,203 @@
+/*
+ * Injectors - Address Translation Management
+ *
+ * Copyright (C) 2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+
+#if !defined(INJECTOR_GVM_HAS_TRANSLATOR)
+#error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions
+#endif
+
+/*
+ * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself
+ * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it.
+ * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator.
+ * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr;
+ * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here
+ * (such as return the pointer as is or output a error message)
+ */
+
+#include "../injector.hpp"
+#include
+#include