diff --git a/CHANGELOG.md b/CHANGELOG.md index edcfdbd..56235b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +### 0.9.3 - Apr 22, 2022 + +- CLEO can be [embedded](https://re.cleo.li/docs/en/embedding.html) and run on unknown hosts via the self-hosted mode [See demo](https://www.youtube.com/watch?v=rk2LvDt7UkI) +- new installer that automatically downloads extra dependencies such as Ultimate ASI Loader and plugins (dylib, IniFiles, or ImGuiRedux) +- support for organizing scripts and its dependencies in sub-directories inside the CLEO folder. See https://re.cleo.li/docs/en/script-lifecycle.html#organizing-scripts +- automatically download the latest `enums.js` file from Sanny Builder Library along with the command definitions. You can import enums in JS with `import * as enums from './.config/enums';` +- memory access operations can run on an unknown host (previously they had a dependency on the `op` command which itself can only run in GTA games) +- `Memory.CallFunctionReturnFloat` and `Memory.CallMethodReturnFloat` are now available for 32-bit hosts. `CallFunctionReturnFloat` has been previously added for 64-bit hosts. + +**SDK AND PLUGINS** +- SDK's method `ResolvePath` now resolves paths starting with `./` or `.\` relative to the script directory. You can use them in commands like `READ_INT_FROM_INI_FILE` or `LOAD_DYNAMIC_LIBRARY` +- new SDK methods `GetHostName`, `SetHostName`, `RuntimeInit`, `RuntimeNextTick`. SDK version is now 2. +- IniFiles plugin updated to 1.2: increased max length of the INI file path +- Dylib plugin updated to 1.1: increased max length of the DLL file path + +**BREAKING CHANGES** +- delete previously deprecated command `op`. Use `native` instead. +- rename `GAME` variable to `HOST` (`GAME` is still available for use but it's recommended to update older scripts) + +| Game | File | Minimum Required Version | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------ | +| GTA III, re3 | [gta3.json](https://github.com/sannybuilder/library/blob/master/gta3/gta3.json) | `0.218` | +| GTA VC, reVC | [vc.json](https://github.com/sannybuilder/library/blob/master/vc/vc.json) | `0.220` | +| GTA San Andreas (Classic) 1.0 | [sa.json](https://github.com/sannybuilder/library/blob/master/sa/sa.json) | `0.236` | +| GTA III: The Definitive Edition | [gta3_unreal.json](https://github.com/sannybuilder/library/blob/master/gta3_unreal/gta3_unreal.json) | `0.213` | +| Vice City: The Definitive Edition | [vc_unreal.json](https://github.com/sannybuilder/library/blob/master/vc_unreal/vc_unreal.json) | `0.215` | +| San Andreas: The Definitive Edition | [sa_unreal.json](https://github.com/sannybuilder/library/blob/master/sa_unreal/sa_unreal.json) | `0.220` | + + ### 0.9.2 - Mar 04, 2022 - add support for The Definitive Edition Title Update 1.04 (GTA III DE 1.0.0.15284, VC DE 1.0.0.15399, SA DE 1.0.0.15483) diff --git a/docs/en/deprecated-bindings.md b/docs/en/deprecated-bindings.md index 28d0f9c..738a56c 100644 --- a/docs/en/deprecated-bindings.md +++ b/docs/en/deprecated-bindings.md @@ -2,28 +2,6 @@ Usage of the following commands is not recommended. -# op +## GAME -`op(opcode_id, ...input_args)` - a low-level function to execute any in-game command with opcode `{opcode_id}`. - - For the commands that return a single value, the result is this value. - - For the commands that return multiple values, the result is an object where each key corresponds to a returned value. The key names match the output names given in the command definition - - For the conditional commands the result is the boolean value `true` or `false` - -```js -op(0x00c0, 12, 30); // sets the time of day to 12:30 -``` - -```js -var pos = op(0x0054, 0); // returns player 0 coordinates vector {x, y, z} -showTextBox("Player pos:", " x = ", pos.x, " y = ", pos.y, " z = ", pos.z); -``` - -```js -if (op(0x0248, 101)) { - // checks the condition - showTextBox("Model with id 101 has been loaded"); -} -``` \ No newline at end of file +`GAME` variable has been renamed to [HOST](./js-bindings.md#host). \ No newline at end of file diff --git a/docs/en/embedding.md b/docs/en/embedding.md index 5cb674e..6ecfbd8 100644 --- a/docs/en/embedding.md +++ b/docs/en/embedding.md @@ -8,7 +8,7 @@ CLEO Redux can be embedded and run JS scripts on an unknown (i.e. not [supported ## Loading into custom process -There are multiple ways of loading ASI file into the target process. [Ultimate ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases) is one of them. The host can load CLEO ASI file as a dynamic library when needed. +There are multiple ways of loading ASI file into the target process. [Ultimate ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases) is one of them. Or use any [DLL injector](https://github.com/search?q=dll+injector) available on GitHub. The host can load CLEO ASI file as a dynamic library when needed. ## Launching the CLEO runtime diff --git a/docs/en/js-bindings.md b/docs/en/js-bindings.md index 5502259..066f0e8 100644 --- a/docs/en/js-bindings.md +++ b/docs/en/js-bindings.md @@ -5,25 +5,27 @@ The following variables and functions are only available in JavaScript code. ## Variables -### GAME +### HOST -current game id. Possible values: `gta3`, `vc`, `re3`, `reVC`, `sa`, `gta3_unreal`, `vc_unreal`, `sa_unreal` +the host name (previously available as `GAME` variable). Possible values include `gta3`, `vc`, `re3`, `reVC`, `sa`, `gta3_unreal`, `vc_unreal`, `sa_unreal`, `unknown`. + +CLEO plugins can use SDK to customize the name for their needs. ```js -if (GAME === "gta3") { +if (HOST === "gta3") { showTextBox("This is GTA III"); } -if (GAME === "sa") { +if (HOST === "sa") { showTextBox("This is San Andreas"); } -if (GAME === "sa_unreal") { - showTextBox("This is San Andreas: The Definitive Edition"); +if (HOST === "unknown") { + showTextBox("This host is not natively supported"); } ``` ### ONMISSION -global flag controlling whether the player is on a mission now. +the global flag controlling whether the player is on a mission now. Not available on an `unknown` host. ```js if (!ONMISSION) { @@ -75,7 +77,7 @@ while (true) { ### showTextBox -`showTextBox(text)` displays `{text}` in the black rectangular box +`showTextBox(text)` displays `{text}` in the black rectangular box. Not available on an `unknown` host. ```js showTextBox("Hello, world!"); @@ -91,12 +93,34 @@ exit("Script ended"); ### native -`native(command_name, ...input_args)` is a low-level function to execute any in-game command using its name `{command_name}` +`native(command_name, ...input_args)` is a low-level function to execute a command using its name `{command_name}`. The command name matches `name` property in a JSON file provided by Sanny Builder Library. ```js native("SET_TIME_OF_DAY", 12, 30); // sets the time of day to 12:30 ``` +For the commands that return a single value, the result is this value. + +```js +const progress = native("GET_PROGRESS_PERCENTAGE"); +showTextBox(`Progress is ${progress}`); +``` + +For the commands that return multiple values, the result is an object where each key corresponds to a returned value. The key names match the output names given in the command definition + +```js +var pos = native("GET_CHAR_COORDINATES", char); // returns char's coordinates vector {x, y, z} +showTextBox(`Character pos: x ${pos.x} y ${pos.y} z ${pos.z}`); +``` + +For the conditional commands the result is the boolean value `true` or `false` +```js +if (native("HAS_MODEL_LOADED", 101)) { + // checks the condition + showTextBox("Model with id 101 has been loaded"); +} +``` + ## Static Objects diff --git a/docs/en/using-memory.md b/docs/en/using-memory.md index 3bbf75b..df39f92 100644 --- a/docs/en/using-memory.md +++ b/docs/en/using-memory.md @@ -1,4 +1,4 @@ -> This guide is for the classic era games. For the information on using the Memory class in the definitive editions [click here](./using-memory-64.md). +> This guide is for x86 hosts (such as classic era games). For the information on using the Memory class on x64 hosts (such as the Definitive edition) [click here](./using-memory-64.md). # Memory Object @@ -36,8 +36,11 @@ interface Memory { CallFunction(address: int, numParams: int, pop: int, ...funcParams: int[]): void; CallFunctionReturn(address: int, numParams: int, pop: int, ...funcParams: int[]): int; + CallFunctionReturnFloat(address: int, numParams: int, pop: int, ...funcParams: int[]): float; CallMethod(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): void; CallMethodReturn(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): int; + CallMethodReturnFloat(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): float; + Fn: { Cdecl(address: int): (...funcParams: int[]) => int; CdeclFloat(address: int): (...funcParams: int[]) => float; @@ -130,10 +133,13 @@ Alternatively, use appropriate methods to read/write the value as a float (`Read `Memory` object allows to invoke a foreign (native) function by its address using one of the following methods: -- `Memory.CallFunction` - binds to [0AA5 CALL_FUNCTION](https://library.sannybuilder.com/#/gta3/CLEO/0AA5) -- `Memory.CallFunctionReturn` - binds to [0AA7 CALL_FUNCTION_RETURN](https://library.sannybuilder.com/#/gta3/CLEO/0AA7) -- `Memory.CallMethod` - binds to [CALL_METHOD](https://library.sannybuilder.com/#/gta3/CLEO/0AA6) -- `Memory.CallMethodReturn` - binds to [CALL_METHOD_RETURN](https://library.sannybuilder.com/#/gta3/CLEO/0AA8) +- `Memory.CallFunction` - calls a function at the address and discards the returned value +- `Memory.CallFunctionReturn` - calls a function and at the address and returns an integer value +- `Memory.CallFunctionReturnFloat` - calls a function and at the address and returns a floating-point value + +- `Memory.CallMethod` - calls a class instance method and discards the returned value +- `Memory.CallMethodReturn` - calls a class instance method and returns an integer value +- `Memory.CallMethodReturnFloat` - calls a class instance method and returns a floating-point value ```js @@ -141,13 +147,12 @@ Alternatively, use appropriate methods to read/write the value as a float (`Read ``` where `0x1234567` is the address of the function, `2` is the number of arguments, `0` is the `pop` parameter (see below), `1000` and `2000` are the two arguments passed into the function. -Note that legacy SCM implementation of the call commands require the arguments of the invoked function to be listed in reverse order. That's it, you would see the same call in SCM as: - -``` -0AA5: call 0x1234567 num_params 2 pop 0 2000 1000 -``` -where `2000` is the second argument passed to the function located at 0x1234567 and `1000` is the first one. - +> Legacy SCM implementation of the call commands require the arguments of the invoked function to be listed in reverse order. That's it, you would see the same call in SCM as: +> +>``` +>0AA5: call 0x1234567 num_params 2 pop 0 2000 1000 +>``` +>where `2000` is the second argument passed to the function located at 0x1234567 and `1000` is the first one. In JS code all input arguments go in the direct order. The third parameter (`pop`) in `Memory.CallFunction` defines the calling convention. When it is set to `0`, the function is called using the [stdcall](https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall) convention. When it is set to the same value as `numParam`, the function is called using the [cdecl](https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl) convention. Any other value breaks the code. diff --git a/docs/en/using-sdk.md b/docs/en/using-sdk.md index 4e82307..6ee3871 100644 --- a/docs/en/using-sdk.md +++ b/docs/en/using-sdk.md @@ -20,19 +20,19 @@ Commands that use low-level WinAPI and can potentially damage user environment m ### Command interface -CLEO Redux uses [Sanny Builder Library](https://library.sannybuilder.com) to know an interface of any command. For a new command to become available in the scripts, the JSON file (`gta3.json`, `vc.json`, `sa.json`) must have the command definition, including the name that matches with the value that the plugin uses `RegisterCommand` with. E.g. if the plugin registers `SHOW_MESSAGE` command, the JSON file must have a command with the name property set to `SHOW_MESSAGE`. The number and order of the input and output parameters in the definition must match the order of methods used by the plugin (i.e. `GetXXXParam` for each input argument and `SetXXXParam` for each output argument). +CLEO Redux uses [Sanny Builder Library](https://library.sannybuilder.com) to know an interface of any command. For a new command to become available in the scripts, the JSON file (`gta3.json`, `vc.json`, `sa.json`) must have the command definition, including the name that matches with the value that the plugin uses `RegisterCommand` with. E.g. if the plugin registers `SHOW_MESSAGE` command, the JSON file must have a command with the `name` property set to `SHOW_MESSAGE`. The number and order of the input and output parameters in the definition must match the order of methods used by the plugin (i.e. `GetXXXParam` for each input argument and `SetXXXParam` for each output argument). #### Claiming Opcodes -Opcodes get assigned to new commands in Sanny Builder Library based on the availability, similarity with existing commands in other games, and other factors. To claim an opcode reach out to Sanny Builder Library maintainers on GitHub https://github.com/sannybuilder/library/issues +Opcodes get assigned to new commands in Sanny Builder Library based on the availability, similarity with existing commands in other games, and other factors. To claim an opcode reach out to Sanny Builder Library maintainers [on GitHub](https://github.com/sannybuilder/library/issues). #### Why use command names and not an id for the command lookup? -One of the common issues with CLEO Library plugins was that commands authored by different people often had id collisions. If two plugins add commands with the same id, it is impossible to use them both. Using string names minimizes the collisions with custom plugins as well as with native opcodes. The library's definitions will ensure each command claims only available id. Also it helps to track and document plugins in one single place. +One of the common issues with CLEO Library plugins was that commands authored by different people often had id collisions. If two plugins add commands with the same id, it is impossible to use them both. Using string names minimizes the collisions with custom plugins as well as with native opcodes. The library's definitions ensure each command claims only an available id. Also it helps to track and document plugins in a single place. ### SDK Version -The current version is `1`. Changes to SDK will advance this number by one. +The current version is `2`. Changes to SDK advance this number by one. ### Path Resolution Convention diff --git a/plugins/Dylib/Cargo.lock b/plugins/Dylib/Cargo.lock index 30fc067..34f58a9 100644 --- a/plugins/Dylib/Cargo.lock +++ b/plugins/Dylib/Cargo.lock @@ -10,9 +10,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cleo_redux_sdk" -version = "0.0.4" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "179143c87d86077f516adc227c01321376273e797b234c0e5f553bfd136270a2" +checksum = "48c1e23e39a04a8a72bc3878d743c9dc32ababf2fe4ea87d3b0f3c930c14e5b0" [[package]] name = "ctor" @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "dylib" -version = "1.0.2" +version = "1.1.0" dependencies = [ "cleo_redux_sdk", "ctor", diff --git a/plugins/Dylib/Cargo.toml b/plugins/Dylib/Cargo.toml index cf8be98..45186c7 100644 --- a/plugins/Dylib/Cargo.toml +++ b/plugins/Dylib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dylib" -version = "1.0.2" +version = "1.1.0" edition = "2021" authors = ["Seemann "] @@ -12,4 +12,4 @@ crate-type = ["cdylib"] [dependencies] ctor = "0.1.21" libloading = "0.7.3" -cleo_redux_sdk = "0.0.4" \ No newline at end of file +cleo_redux_sdk = "0.0.7" \ No newline at end of file diff --git a/plugins/Dylib/src/impl.rs b/plugins/Dylib/src/impl.rs index 9a4d4bf..ea2747c 100644 --- a/plugins/Dylib/src/impl.rs +++ b/plugins/Dylib/src/impl.rs @@ -1,7 +1,7 @@ use cleo_redux_sdk::*; use libloading; -/// https://library.sannybuilder.com/#/sa/CLEO/0AA2?p=1&v=1 +/// https://library.sannybuilder.com/#/unknown_x86/dylib/LOAD_DYNAMIC_LIBRARY pub extern "C" fn load_dynamic_library(ctx: Context) -> HandlerResult { let libname = get_string_param(ctx); @@ -30,7 +30,7 @@ pub extern "C" fn load_dynamic_library(ctx: Context) -> HandlerResult { HandlerResult::CONTINUE } -/// https://library.sannybuilder.com/#/sa/CLEO/0AA3?p=1&v=1 +/// https://library.sannybuilder.com/#/unknown_x86/dylib/FREE_DYNAMIC_LIBRARY pub extern "C" fn free_dynamic_library(ctx: Context) -> HandlerResult { log(format!("disposing dynamic library")); @@ -41,7 +41,7 @@ pub extern "C" fn free_dynamic_library(ctx: Context) -> HandlerResult { HandlerResult::CONTINUE } -/// https://library.sannybuilder.com/#/sa/CLEO/0AA4?p=1&v=1 +/// https://library.sannybuilder.com/#/unknown_x86/dylib/GET_DYNAMIC_LIBRARY_PROCEDURE pub extern "C" fn find_procedure(ctx: Context) -> HandlerResult { let symbol = get_string_param(ctx); let lib = get_int_param(ctx) as *mut libloading::Library; diff --git a/plugins/Dylib/src/lib.rs b/plugins/Dylib/src/lib.rs index ddd57f7..4374752 100644 --- a/plugins/Dylib/src/lib.rs +++ b/plugins/Dylib/src/lib.rs @@ -11,7 +11,7 @@ fn init() { use cleo_redux_sdk::{log, register_command}; use r#impl::{find_procedure, free_dynamic_library, load_dynamic_library}; - log("Dylib plugin 1.0.2"); + log("Dylib plugin 1.1"); register_command("LOAD_DYNAMIC_LIBRARY", load_dynamic_library, Some("dll")); register_command("FREE_DYNAMIC_LIBRARY", free_dynamic_library, Some("dll")); diff --git a/plugins/IniFiles/dllmain.cpp b/plugins/IniFiles/dllmain.cpp index 1b76983..539d794 100644 --- a/plugins/IniFiles/dllmain.cpp +++ b/plugins/IniFiles/dllmain.cpp @@ -9,7 +9,7 @@ class IniFilesPlugin { public: IniFilesPlugin() { - Log("IniFiles plugin 1.1"); + Log("IniFiles plugin 1.2"); RegisterCommand("READ_INT_FROM_INI_FILE", IniFileReadInt, "fs"); RegisterCommand("WRITE_INT_TO_INI_FILE", IniFileWriteInt, "fs"); RegisterCommand("READ_FLOAT_FROM_INI_FILE", IniFileReadFloat, "fs"); @@ -24,7 +24,7 @@ class IniFilesPlugin { 0AF0=4,%4d% = get_int_from_ini_file %1s% section %2s% key %3s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; @@ -45,7 +45,7 @@ class IniFilesPlugin { 0AF1=4,write_int %1d% to_ini_file %2s% section %3s% key %4s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; wchar_t strValue[STR_MAX_LEN]; @@ -67,7 +67,7 @@ class IniFilesPlugin { 0AF2=4,%4d% = get_float_from_ini_file %1s% section %2s% key %3s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; wchar_t strValue[STR_MAX_LEN]; @@ -98,7 +98,7 @@ class IniFilesPlugin { 0AF3=4,write_float %1d% to_ini_file %2s% section %3s% key %4s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; wchar_t strValue[STR_MAX_LEN]; @@ -120,7 +120,7 @@ class IniFilesPlugin { 0AF4=4,%4d% = read_string_from_ini_file %1s% section %2s% key %3s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; wchar_t strValue[STR_MAX_LEN]; @@ -145,7 +145,7 @@ class IniFilesPlugin { 0AF5=4,write_string %1s% to_ini_file %2s% section %3s% key %4s% ****************************************************************/ { - wchar_t iniPath[STR_MAX_LEN]; + wchar_t iniPath[MAX_PATH]; wchar_t sectionName[STR_MAX_LEN]; wchar_t key[STR_MAX_LEN]; wchar_t strValue[STR_MAX_LEN]; @@ -179,10 +179,10 @@ class IniFilesPlugin { static void GetPath(Context ctx, wchar_t* res) { char buf[STR_MAX_LEN]; - char path[STR_MAX_LEN]; + char path[MAX_PATH]; GetStringParam(ctx, buf, sizeof(buf)); ResolvePath(buf, path); - MultiByteToWideChar(CP_UTF8, 0, path, -1, res, STR_MAX_LEN); + MultiByteToWideChar(CP_UTF8, 0, path, -1, res, MAX_PATH); } static BOOL ReadStringFromIni(wchar_t* iniPath, wchar_t* sectionName, wchar_t* key, wchar_t* strValue) { diff --git a/plugins/SDK/cleo_redux.lib b/plugins/SDK/cleo_redux.lib index 30bbad5..bbab6a0 100644 Binary files a/plugins/SDK/cleo_redux.lib and b/plugins/SDK/cleo_redux.lib differ diff --git a/plugins/SDK/cleo_redux64.lib b/plugins/SDK/cleo_redux64.lib index 1ee2203..c7e0267 100644 Binary files a/plugins/SDK/cleo_redux64.lib and b/plugins/SDK/cleo_redux64.lib differ diff --git a/plugins/SDK/cleo_redux_sdk.h b/plugins/SDK/cleo_redux_sdk.h index 0a7f58e..5c2bf35 100644 --- a/plugins/SDK/cleo_redux_sdk.h +++ b/plugins/SDK/cleo_redux_sdk.h @@ -24,7 +24,8 @@ enum class HostId SA = 5, GTA3_UNREAL = 6, VC_UNREAL = 7, - SA_UNREAL = 8 + SA_UNREAL = 8, + UNKNOWN = 255 }; typedef void* Context; @@ -74,4 +75,16 @@ extern "C" { // since v1 // Sets the status of the current condition void UpdateCompareFlag(Context ctx, bool result); + // since v2 + // Copies atmost {maxlen} bytes of a UTF-8 encoded host name to {dest} + void GetHostName(char* dest, unsigned char maxlen); + // since v2 + // Sets the new host name (available in scripts as the HOST constant) + void SetHostName(const char* src); + // since v2 + // Initializes or reloads CLEO runtime + void RuntimeInit(); + // since v2 + // Iterates the main loop + void RuntimeNextTick(unsigned int current_time, int time_step); } diff --git a/plugins/SDK/cleo_redux_sdk.rs b/plugins/SDK/cleo_redux_sdk.rs index 7fb068f..c6b08b2 100644 --- a/plugins/SDK/cleo_redux_sdk.rs +++ b/plugins/SDK/cleo_redux_sdk.rs @@ -24,6 +24,7 @@ pub enum HostId { GTA3_UNREAL = 6, VC_UNREAL = 7, SA_UNREAL = 8, + UNKNOWN = 255, } #[allow(non_camel_case_types)] @@ -78,7 +79,7 @@ extern "C" { /// since v1 /// /// Copies atmost {maxlen} bytes of a UTF-8 encoded character sequence in the script input to {dest} - fn GetStringParam(ctx: Context, dest: *mut c_char, maxlen: u8); + fn GetStringParam(ctx: Context, dest: *mut c_char, maxlen: u8); /// since v1 /// /// Writes the integer {value} (either 32 or 64 bit depending on the target platform) to the script output @@ -95,6 +96,23 @@ extern "C" { /// /// Sets the status of the current condition fn UpdateCompareFlag(ctx: Context, result: bool); + /// since v2 + /// + /// Copies atmost {maxlen} bytes of a UTF-8 encoded host name to {dest} + fn GetHostName(dest: *mut c_char, maxlen: u8); + /// since v2 + /// + /// Sets the new host name (available in scripts as the HOST constant) + fn SetHostName(src: *const c_char); + /// since v2 + /// + /// Initializes or reloads CLEO runtime + fn RuntimeInit(); + /// since v2 + /// + /// Iterates the main loop + fn RuntimeNextTick(current_time: u32, time_step: i32); + } macro_rules! sz { @@ -220,6 +238,36 @@ pub fn update_compare_flag(ctx: Context, value: bool) { unsafe { UpdateCompareFlag(ctx, value) } } +/// since v2 +/// +/// Copies atmost {maxlen} bytes of a UTF-8 encoded host name to {dest} +pub fn get_host_name() -> String { + let mut buf = [0i8; SDK_STRING_MAX_LEN]; + unsafe { GetHostName(buf.as_mut_ptr(), SDK_STRING_MAX_LEN as _) }; + to_rust_string(buf.as_ptr()) +} + +/// since v2 +/// +/// Sets the new host name (available in scripts as the HOST constant) +pub fn set_host_name(value: String) { + unsafe { SetHostName(sz!(value)) }; +} + +/// since v2 +/// +/// Initializes or reloads CLEO runtime +pub fn runtime_init() { + unsafe { RuntimeInit() } +} + +/// since v2 +/// +/// Iterates the main loop +pub fn runtime_next_tick(current_time: u32, time_step: i32) { + unsafe { RuntimeNextTick(current_time, time_step) } +} + fn to_rust_string(addr: *const i8) -> String { unsafe { std::ffi::CStr::from_ptr(addr)