diff --git a/README.md b/README.md index 51962c2..c2e2b28 100644 --- a/README.md +++ b/README.md @@ -3,560 +3,39 @@ [![Discord](https://img.shields.io/discord/911487285990674473?style=for-the-badge)](https://discord.gg/d5dZSfgBZr) [![YouTube Channel](https://img.shields.io/badge/YouTube-Channel-FF0000?style=for-the-badge)](https://www.youtube.com/playlist?list=PLNxQuEFtVkeizoLEQiok7qzr1f0mcwfFb) -- [Getting Started](#getting-started) - - [What is CLEO Redux?](#what-is-cleo-redux) - - [Supported Languages](#supported-languages) - - [Supported Releases](#supported-releases) - - [Relation to CLEO Library](#relation-to-cleo-library) - - [Running CLEO Redux as a standalone software](#running-cleo-redux-as-a-standalone-software) - - [Running CLEO Redux as an addon to CLEO library](#running-cleo-redux-as-an-addon-to-cleo-library) -- [Installation](#installation) - - [First time setup](#first-time-setup) - - [Compatibility with re3 and reVC](#compatibility-with-re3-and-revc) - - [Compatibility with The Trilogy: The Definitive Edition](#compatibility-with-the-trilogy-the-definitive-edition) - - [Uninstallation](#uninstallation) -- [Configuration](#configuration) - - [General Configuration](#general-configuration) - - [Permissions](#permissions) - - [All](#all) - - [Lax](#lax) - - [Strict](#strict) - - [None](#none) -- [Log](#log) -- [Custom Scripts](#custom-scripts) - - [Adding a new script](#adding-a-new-script) - - [Removing the script](#removing-the-script) - - [Custom Commands](#custom-commands) - - [Writing CS scripts](#writing-cs-scripts) - - [Writing JS scripts](#writing-js-scripts) -- [JavaScript Support](#javascript-support) - - [Prerequisites](#prerequisites) - - [Script Lifecycle](#script-lifecycle) - - [Native Commands](#native-commands) - - [Class ScriptObject vs Object](#class-scriptobject-vs-object) - - [Class Math](#class-math) - - [Fluent Interface](#fluent-interface) - - [Examples](#examples) - - [Imports](#imports) - - [Custom Bindings](#custom-bindings) - - [Deprecated](#deprecated) -- [Custom Text](#custom-text) -- [Dev Features](#dev-features) - - [Integration with Visual Studio Code](#integration-with-visual-studio-code) - - [SCM Log](#scm-log) - - [Hot Reload](#hot-reload) - - [SDK](#sdk) -- [License](#license) +CLEO Redux is an experimental JavaScript runtime for GTA 3D era games. -## Getting Started +It brings a joy of enhancing the gameplay with countless mini-scripts that are easy to add and remove. Both players and developers can equally benefit from a flexible and secure environment it provides. -### What is CLEO Redux? - -CLEO Redux is a scripting runtime for the GTA 3D era games. It is a proud member of the CLEO family and provides familiar experience to anyone who used CLEO Library for the classic GTA San Andreas or its re-implementations for other games. The main goal of CLEO is to provide a way to easily customize the game with countless user-made scripts. - -If you're new to CLEO visit the [official website](https://cleo.li/) to find more information about it. - -### Supported Languages - -CLEO Redux supports compiled binary scripts (`*.cs`) in the native SCM format and plain text scripts (`*.js`) written in JavaScript. - -CLEO Redux targets JavaScript as the primary language for custom scripts. JavaScript is a popular programming language with rich ecosystem and plenty of available information. It's free from SCM language limits and pitfalls such as lack of support for functions, arrays, or the low number of variables. - -### Supported Releases - -Classic: - -- GTA III 1.0 -- GTA Vice City 1.0 -- GTA San Andreas 1.0 (only with [CLEO 4.4](https://github.com/cleolibrary/CLEO4)) - -Remasters (The Trilogy): - -- GTA III 1.0.0.14718 -- GTA Vice City 1.0.0.14718 -- San Andreas 1.0.0.14296, 1.0.0.14388, 1.0.0.14718 (Title Update 1.03) - -See [The Definitive Edition FAQ](the-definitive-edition-faq.md) for the details. - -Other: - -- re3 (see [Compatibility details](#compatibility-with-re3-and-revc)) -- reVC (see [Compatibility details](#compatibility-with-re3-and-revc)) - -CLEO Redux only supports the PC version of each game. - -For the complete reference on supported features [refer to this page](https://github.com/cleolibrary/CLEO-Redux/wiki/Feature-Support-Matrix). Also there are known limitations [listed here](unsupported.md). - -### Relation to CLEO Library - -CLEO is a common name for the custom libraries designed and created for GTA III, Vice City or San Andreas. Each version can be found and downloaded [here](https://cleo.li/download.html). CLEO Redux is _another_ CLEO implementation made from scratch with a few distinctive features, such as single code base for all games, or support for JavaScript code. - -At the moment CLEO Redux can not be considered as a complete replacement for CLEO Library due to the lack of support for many widely used CLEO commands. To solve this issue and get the best out of the two libraries, CLEO Redux supports two different usage strategies. - -CLEO Redux can run as a standalone software, or as an addon to CLEO Library. In the first case your game directory would only contain `cleo_redux.asi` (or `cleo_redux64.asi`) file. In the second case, your game directory would have both `cleo.asi` (or `III.CLEO.asi` or `VC.CLEO.asi`) and `cleo_redux.asi` (or `cleo_redux64.asi`). - -#### Running CLEO Redux as a standalone software - -As a standalone software CLEO Redux runs compiled scripts and JavaScript and provides access to all native script commands. It also provides a limited set of [custom commands](#custom-commands) backward-compatible to CLEO Library. Existing CLEO scripts may not work if they use custom commands (for example from a third-party plugin) not supported by CLEO Redux. - -This mode does not work in the classic GTA San Andreas. - -#### Running CLEO Redux as an addon to CLEO library - -As an addon CLEO Redux runs alongside CLEO Library delegating it all the care for custom scripts. It means all custom scripts and plugins made for CLEO Library will continue working exactly the same. CLEO Redux only manages JS scripts. All custom commands become available to JavaScript runtime, which means you can use commands currently not implemented natively in CLEO Redux, for example for [files](https://library.sannybuilder.com/#/gta3/classes/File) or [dynamic libraries](https://library.sannybuilder.com/#/gta3/classes/DynamicLibrary). - -This mode works in classic GTA III, GTA Vice City and GTA San Andreas where CLEO Library exists. - -## Installation - -If you run The Definitive Edition (GTA III, VC, or SA): - -- [Follow this guide](the-definitive-edition-faq.md#how-to-install-cleo-redux-in-the-definitive-edition). - -For all other games: - -- Copy `cleo_redux.asi` to the game directory. - -- Run the game - -Plugins for CLEO Redux (`*.cleo`) should be copied to the `CLEO\CLEO_PLUGINS` directory. - -Note: CLEO Redux does not alter any game files. It exploits the fact that the game natively loads `.asi` files as addons to the Miles Sound System library. No extra software is required. - -### First time setup - -There could be a noticeable lag during the first game run as CLEO Redux downloads the files necessary for [JavaScript support](#javascript-support). It won't happen on subsequent runs. - -Also a new folder named `CLEO` should appear in the game directory\*. This is the primary location for all CLEO scripts, plugins and configs. - -\*If CLEO fails to create new files in the game directory due to the lack of write permissions, it fallbacks to using alternate path at `C:\Users\\AppData\Roaming\CLEO Redux`. `cleo_redux.log` and the `CLEO` directory can be found there. - -### Compatibility with re3 and reVC - -CLEO Redux only supports "Windows D3D9 MSS 32bit" version of `re3` or `reVC`. - -When running on `re3` and `reVC` make sure the game directory contains the file `re3.pdb` (for **re3**) or `reVC.pdb` (for **reVC**). Due to the dynamic nature of memory addresses in those implementations CLEO Redux relies on debug information stored in the PDB file to correctly locate itself. - -### Compatibility with The Trilogy: The Definitive Edition - -Check [The Definitive Edition FAQ](the-definitive-edition-faq.md) - -### Uninstallation - -- Delete `cleo_redux.asi` (or `cleo_redux64.asi`). -- Delete the `CLEO` folder (optional). -- Delete the `cleo_redux.log` (optional) - -## Configuration - -CLEO Redux exposes some of the configurable settings in the file `CLEO\.config\cleo.ini`. - -### General Configuration - -- `AllowCs` - when set to `1` CLEO loads and executes `*.cs` files located in the CLEO directory. Enabled by default. -- `AllowJs` - when set to `1` CLEO loads and executes `*.js` files located in the CLEO directory. Enabled by default. -- `AllowFxt` - when set to `1` CLEO loads and [uses](#custom-text) `*.fxt` files located in the CLEO\CLEO_TEXT directory. Enabled by default. -- `CheckUpdates` - when set to `1` CLEO check if there is a new update available for download during the game startup. Enabled by default. -- `LogOpcodes` - when set to `1` CLEO logs all executed opcodes in custom scripts. This log is part of the `cleo_redux.log` file (see [Log](#log)) -- `PermissionLevel` - sets the permission level for unsafe operations (see below). Default is `Lax`. - -### Permissions - -CLEO Redux acknowledges some [custom commands](#custom-commands) (opcodes) as unsafe and requires the user to decide whether to run them or not. Raw access to the process memory, loading external libraries or making network requests can be harmful and produce undesired side-effects. Hence CLEO introduces permission levels for running the unsafe code. - -There are four available levels: - -#### All - -Any unsafe operation is allowed. Use this only when you trust the scripts you run. - -#### Lax - -This is the default permission level. - -No unsafe operation is allowed unless the script explicitly requests it. Currently to request a permission, the name of the script file must include the permissions tokens wrapped in square brackets. - -For example, if the script wants to access the memory via `0A8D READ_MEMORY` the file name must contain `[mem]`, e.g. `cool-spawner[mem].cs`. If the file is named differently CLEO rejects `0A8D` and the script crashes. - -#### Strict - -No unsafe operation is allowed unless the script explicitly requests it (see `"Lax"`) and the CLEO config file permits this type of operation under the `Permissions` section. - -Permissions section in `cleo.ini` allows to enable or disable groups of unsafe operations by using the permission tokens. For example, - -```ini -mem=0 -``` - -disables all memory-related opcodes even if the script has the `[mem]` token in the file name. - -Note: `Permissions` section in the `cleo.ini` only takes effect when `PermissionLevel` is `Strict`. - -#### None - -No unsafe operation is allowed. - -## Log - -CLEO logs important events and errors in the `cleo_redux.log` file located in the game directory (or `C:\Users\\AppData\Roaming\CLEO Redux`, see [First Time Setup](#first-time-setup) note). This file gets overwritten on each game run. If you experience any issue when using CLEO Redux, start investigating the root cause from this file. - -To stream events in your terminal while testing a script, run: - -``` -tail -f cleo_redux.log -``` - -`tail` is a unix command so a compatible environment is needed (for example Git Bash). - -## Custom Scripts - -### Adding a new script - -Generally a script file should just be copied to the `CLEO` directory. Some scripts may require extra steps for installation. In case of any issues check the script documentation or ask its author. - -### Removing the script - -Delete the script file from `CLEO` directory. Some scripts may require extra steps for undoing the installation. In case of any issues check the script documentation or ask its author. - -### Custom Commands - -Note: the following commands are for classic games only. For The Definitive Edition [check this information](the-definitive-edition-faq.md#can-i-use-cleo-opcodes). - -- 0A8C [WRITE_MEMORY](https://library.sannybuilder.com/#/gta3/CLEO/0A8C) (**UNSAFE** - requires `mem` permission) -- 0A8D [READ_MEMORY](https://library.sannybuilder.com/#/gta3/CLEO/0A8D) (**UNSAFE** - requires `mem` permission) -- 0A8E [INT_ADD](https://library.sannybuilder.com/#/gta3/CLEO/0A8E) -- 0A8F [INT_SUB](https://library.sannybuilder.com/#/gta3/CLEO/0A8F) -- 0A90 [INT_MUL](https://library.sannybuilder.com/#/gta3/CLEO/0A90) -- 0A91 [INT_DIV](https://library.sannybuilder.com/#/gta3/CLEO/0A91) -- 0A93 [TERMINATE_THIS_CUSTOM_SCRIPT](https://library.sannybuilder.com/#/gta3/CLEO/0A93) -- 0AA5 [CALL_FUNCTION](https://library.sannybuilder.com/#/gta3/CLEO/0AA5) (**UNSAFE** - requires `mem` permission) -- 0AA6 [CALL_FUNCTION_RETURN](https://library.sannybuilder.com/#/gta3/CLEO/0AA6) (**UNSAFE** - requires `mem` permission) -- 0AA7 [CALL_METHOD](https://library.sannybuilder.com/#/gta3/CLEO/0AA7) (**UNSAFE** - requires `mem` permission) -- 0AA8 [CALL_METHOD_RETURN](https://library.sannybuilder.com/#/gta3/CLEO/0AA8) (**UNSAFE** - requires `mem` permission) -- 0AB0 [IS_KEY_PRESSED](https://library.sannybuilder.com/#/gta3/CLEO/0AB0) - -This list might not be complete as there are custom plugins with extra commands (see [Using SDK](./using-sdk.md)). Refer to [Sanny Builder Library](https://library.sannybuilder.com) for the complete list of available commands for each game. - -### Writing CS scripts - -Use [Sanny Builder 3](https://sannybuilder.com) in GTA III, GTA VC or GTA SA edit modes respectively. Check out [this page](https://cleo.li/scripts.html) for more information. - -[Check the FAQ](the-definitive-edition-faq.md#how-do-i-compile-cleo-scripts-with-sanny-builder) for the information on CS support in the remastered games. - -### Writing JS scripts - -Use VS Code (recommended) or any editor of your choice. Create a new file with `.js` extension and put it in the CLEO folder. See [JavaScript support](#javascript-support) for extra information. - -The runtime supports scripts in [ECMAScript 2020 standard](https://262.ecma-international.org/11.0/). It means you are able to use the most recent JavaScript features out of the box, such as imports, classes, arrow functions, etc. - -CLEO Redux is not Node.js. Don't expect sockets, file system operations or other Node.js features to be available here. - -## JavaScript Support - -### Prerequisites - -When JavaScript is enabled CLEO Redux needs commands definitions from https://library.sannybuilder.com/. On the first run CLEO tries to download them and put into your local `CLEO/.config` directory. If that did not happen, or you don't want to let CLEO make network calls, manually download a required file (see the table below) and place it in the `CLEO/.config` directory. - -| Game | File | Minumum Required Version | -| ----------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------ | -| GTA III, re3 | [gta3.json](https://github.com/sannybuilder/library/blob/master/gta3/gta3.json) | `0.208` | -| GTA VC, reVC | [vc.json](https://github.com/sannybuilder/library/blob/master/vc/vc.json) | `0.210` | -| GTA San Andreas (Classic) 1.0 | [sa.json](https://github.com/sannybuilder/library/blob/master/sa/sa.json) | `0.210` | -| GTA III: The Definitive Edition | [gta3_unreal.json](https://github.com/sannybuilder/library/blob/master/gta3_unreal/gta3_unreal.json) | `0.210` | -| Vice City: The Definitive Edition | [vc_unreal.json](https://github.com/sannybuilder/library/blob/master/vc_unreal/vc_unreal.json) | `0.212` | -| San Andreas: The Definitive Edition | [sa_unreal.json](https://github.com/sannybuilder/library/blob/master/sa_unreal/sa_unreal.json) | `0.216` | - -### Script Lifecycle - -A file with the JavaScript code should have the `*.js` extension and contain known instructions as described below. The script may have no instructions (the empty script). It runs as soon as the the new game starts or a save file is loaded. - -The script terminates automatically when the last instruction has been executed. The runtime also terminates stuck scripts to prevent the game from freezing. The stuck script is the one that took more than `2` seconds to run since the last wait command. If that happened, check out your loops, some of the are missing the wait command. +A complete simple script looks like this: ```js +const VK_F4 = 115; while (true) { - // meaningless infinite loop normally freezing the game - // will be terminated after two seconds -} -``` - -The runtime will terminate this script. To avoid that, add the wait command - -```js -while (true) { - wait(250); - // still meaningless, but does not freeze the game -} -``` - -### Native Commands - -CLEO Redux supports all in-game commands (opcodes) in the class form as defined in Sanny Builder Library. - -#### Class ScriptObject vs Object - -Sanny Builder Library defines a static class `Object` to group commands allowing to create and manipulate 3D objects in-game. At the same time JavaScript has the [native Object class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) with its own methods. - -To avoid mixing the two, CLEO Redux uses `ScriptObject` class instead of the Library's `Object` with [the same interface](https://library.sannybuilder.com/#/gta3/classes/Object). - -```js -var x = ScriptObject.Create(modelId, x, y, z); // opcode 0107, creates a new object in the game - -var x = Object.create(null); // native JavaScript code, creates a new object in JS memory -``` - -#### Class Math - -Similarly to the `Object` class (see above), both the Library and native JavaScript runtime use the same name for mathematical utilities: class `Math`. In this case however, the decision was made to keep both native and script methods under the same class name. - -The priority was given to the native code in those cases where it provides the same functionality as script opcodes. For example, to calculate the absolute value of a number, use native [Math.abs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs), not [Math.Abs](https://library.sannybuilder.com/#/gta3?q=%22abs%22). See [Using Math](using-math.md) for more detailed information. - -#### Fluent Interface - -Methods on constructible entities (such as `Player`, `Car`, `Char` -- any entities created via a constructor method) support chaining (also known as Fluent Interface). It allows to write code like this: - -```js -var p = new Player(0); - -p.giveWeapon(2, 100) - .setHealth(5) - .setCurrentWeapon(2) - .getChar() - .setCoordinates(1144, -600, 14) - .setBleeding(true); -``` - -See demo: https://www.youtube.com/watch?v=LLgJ0fWbklg. - -Destructor methods interrupt the chain. E.g. given the code: - -`Char.Create(0, 0, 0, 0, 0).setCoordinates(0, 0, 0).delete()` - -the chain can not continue after delete method as the character gets removed and its handle is not longer valid. - -#### Examples - -If you were to change the time of day to 22:00, run the following command - -```js -Clock.SetTimeOfDay(22, 0); -``` - -This would be the equivalent of the opcode `00C0: set_time_of_day 22 0` or `SET_TIME_OF_DAY 22 0` in SCM code. - -Change player's money - -```js -var player = new Player(0); -player.addScore(1000); -``` - -`new Player(0);` is the equivalent of getting the player's index using `$PLAYER_CHAR` global variable in Sanny Builder. - -Create a dynamic entity (e.g. a car) - -```js -// request a model -Streaming.RequestModel(101); - -// wait while the game loads the model -while (!Streaming.HasModelLoaded(101)) { wait(250); -} - -// create a car at the coordinates and store the handle -var infernus = Car.Create(101, 1234.0, 567.0, -100.0); -``` -Variable `infernus` will contain a handle of the newly created car. It can be passed directly to any command receiving the `Car` handle - -```js -// create a marker on the created car and store the marker handle -var blip = Blip.AddForCar(infernus); -``` - -The same variable infernus can be used to call the `Car`'s methods, for example, to explode the car - -```js -infernus.explode(); -``` - -This would be the equivalent to the opcode `020B: explode_car $infernus` or `EXPLODE_CAR $infernus` in SCM scripts. - -Also check out the [example scripts](examples). - -### Imports - -You can import other script files in your code to make the code modular and share the common logic. The runtime supports the `import` statement as described in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import - -To avoid running the included `.js` files as standalone scripts, either put them into a separate folder (e.g. `CLEO/includes/`) or use the extension `.mjs`. - -```js -import func from "./other"; // imports default export from other.js or other.mjs located in the same directory - -import { PedType } from "./includes/types"; // imports named export PedType from types.js or types.mjs located in the CLEO/includes directory - -import data from "./vehicles.json"; // imports vehicles.json as a JavaScript value (an array, object). -``` - -Currently only import of `.js` (`.mjs`) and `.json` files is supported. - -### Custom Bindings - -The following variables and functions are only available in JavaScript code. - -- `GAME` - current game id. Possible values: `gta3`, `vc`, `re3`, `reVC`, `sa`, `gta3_unreal`, `vc_unreal`, `sa_unreal` - -```js -if (GAME === "gta3") { - showTextBox("This is GTA III"); -} -if (GAME === "sa") { - showTextBox("This is San Andreas"); -} -if (GAME === "sa_unreal") { - showTextBox("This is San Andreas: The Definitive Edition"); -} -``` - -- `ONMISSION` - global flag controlling whether the player is on a mission now. - -```js -if (!ONMISSION) { - showTextBox("Not on a mission. Setting ONMISSION to true"); - ONMISSION = true; -} -``` - -- `TIMERA` and `TIMERB` - two auto-incrementing timers useful for measuring time intervals. - -```js -while (true) { - TIMERA = 0; - // wait 1000 ms - while (TIMERA < 1000) { - wait(0); + if (Pad.IsKeyPressed(VK_F4)) { + Game.ActivateSaveMenu(); + wait(1000); } - showTextBox("1 second passed"); -} -``` - -- `__dirname` - an absolute path to the directory with the current file - -- `log(...values)` - prints comma-separated `{values}` to the `cleo_redux.log` - -```js -var x = 1; -log("value of x is ", x); -``` - -- `wait(timeInMs)` - pauses the script execution for at least `{timeInMs}` milliseconds - -```js -while (true) { - wait(1000); - log("1 second passed"); } ``` -- `showTextBox(text)` - displays `{text}` in the black rectangular box - -```js -showTextBox("Hello, world!"); -``` - -- `exit(reason?)` - terminates the script immediately. `exit` function accepts an optional string argument that will be added to the `cleo_redux.log`. +[See more examples](https://github.com/cleolibrary/CLEO-Redux/tree/master/examples +) -```js -exit("Script ended"); -``` -- family of static methods in the `Memory` class for manipulating different data types. See the [Memory guide](using-memory.md) for more information. +## Documentation -- `native(command_name, ...input_args)` - a low-level function to execute any in-game command using its name `{command_name}` - -```js -native("SET_TIME_OF_DAY", 12, 30); // sets the time of day to 12:30 -``` - -### Deprecated - -Note: usage of the following commands is not recommended. - -- `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); -``` +https://re.cleo.li/docs -```js -if (op(0x0248, 101)) { - // checks the condition - showTextBox("Model with id 101 has been loaded"); -} -``` - -## Custom Text - -CLEO Redux supports custom text content without the need to edit game files. See [this guide](using-fxt.md) for more information. - -## Dev Features - -### Integration with Visual Studio Code - -See demo: https://youtu.be/jqz8_lGnG4g - -CLEO Redux generates typings for all supported commands that you can use when writing JavaScript in VS Code. Add the following line in your `*.js` script to get the full autocomplete support: - -For `GTA III` or `re3`: - -``` -/// -``` - -For `Vice City` or `reVC` - -``` -/// -``` - -For `San Andreas` - -``` -/// -``` - -This line instructs VS Code where to look for the commands definitions for the autocomplete feature. The `path` can be relative to the script file or be absolute. [Find out more information](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-path-) on the official TypeScript portal. - -### SCM Log - -CLEO Redux has built-in support for tracking SCM instructions. To enable trace for executed commands open up `cleo.ini` and change `LogOpcodes` to 1. Note that it can greatly impact game performance due to frequent microdelays during writes to the log file. Use this option only for debugging purposes. - -### Hot Reload - -CLEO monitors active scripts and reloads them in game as they change - -Demo: https://www.youtube.com/watch?v=WanLojClqFw - -Adding a new script file in CLEO directory or deleting one while the game is running starts or stops the script automatically - -Demo: https://www.youtube.com/watch?v=LAi2syrsxJg +## Installation -Hot reload for CS scripts does not work when CLEO Redux runs alongside CLEO Library (e.g. in classic San Andreas). +https://re.cleo.li/docs/en/installation.html -### SDK +## Contribution -CLEO Redux provides a [C++ and Rust SDK](./plugins/SDK) for developing custom commands. Find more information in the [Using SDK](./using-sdk.md) guide. +There are multiple ways to contribute to the project. We welcome any help with improving our documentation, writing new example scripts, developing plugins using [CLEO SDK](https://re.cleo.li/docs/en/using-sdk.html), sharing your ideas, or testing early builds available on our Discord. ## License diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md deleted file mode 100644 index 67bd4ed..0000000 --- a/TROUBLESHOOTING.md +++ /dev/null @@ -1,13 +0,0 @@ -## CLEO does not work with re3 or reVC - -Check out [this information](https://github.com/cleolibrary/CLEO-Redux#compatibility-with-re3-and-revc). - -## Game crash with CLEO on San Andreas: The Definitive Edition - -- make sure you installed the 64-bit version of Ultimate ASI Loader ([direct link to the latest release](https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases/download/x64-latest/version.zip)). - - Put `version.dll` to `GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64` -- make sure you run the latest CLEO Redux version (0.8.2 and above) -- delete config files from `Documents\Rockstar Games\GTA San Andreas Definitive Edition\Config\WindowsNoEditor` -- run the game (or Rockstar Games Launcher) as administrator - -If CLEO can't create files in `GTA San Andreas - Definitive Edition\Gameface\Binaries\Win64` it will use another directory at `C:\Users\\AppData\Roaming\CLEO Redux`. There should be `cleo_redux.log` and the CLEO folder where all your scripts go. diff --git a/the-definitive-edition-faq.md b/the-definitive-edition-faq.md deleted file mode 100644 index d3e5831..0000000 --- a/the-definitive-edition-faq.md +++ /dev/null @@ -1,104 +0,0 @@ -Here you can find answers to the frequently asked questions about support for The Trilogy remaster. - -- [What versions are supported?](#what-versions-are-supported) -- [How to install CLEO Redux in The Definitive Edition?](#how-to-install-cleo-redux-in-the-definitive-edition) -- [What if I can't find the CLEO directory?](#what-if-i-cant-find-the-cleo-directory) -- [How to uninstall CLEO Redux?](#how-to-uninstall-cleo-redux) -- [Is there any difference from support of the classic games?](#is-there-any-difference-from-support-of-the-classic-games) -- [Can I use original opcodes?](#can-i-use-original-opcodes) -- [How do I know what commands can I use in JavaScript?](#how-do-i-know-what-commands-can-i-use-in-javascript) -- [Can I use CLEO opcodes?](#can-i-use-cleo-opcodes) -- [Can I work with the game memory or call the game functions?](#can-i-work-with-the-game-memory-or-call-the-game-functions) -- [How do I compile CLEO scripts with Sanny Builder?](#how-do-i-compile-cleo-scripts-with-sanny-builder) -- [I can't find an answer to my question here, where do I go?](#i-cant-find-an-answer-to-my-question-here-where-do-i-go) - -### What versions are supported? - -- GTA III: The Definitive Edition **1.0.0.14718** -- GTA Vice City: The Definitive Edition **1.0.0.14718** -- San Andreas: The Definitive Edition **1.0.0.14296**, **1.0.0.14388**, **1.0.0.14718** - -### How to install CLEO Redux in The Definitive Edition? - -- Download and install [Ultimate ASI Loader x64](https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases/download/x64-latest/version.zip) by ThirteenAG (place `version.dll` to the `Gameface\Binaries\Win64` directory) -- Copy `cleo_redux64.asi` to the same directory. - -- Run the game once and you should get a new CLEO directory created in the same directory. If that did not happen, check below. - -### What if I can't find the CLEO directory? - -For many people running their game with CLEO Redux installed results in the immediate crash. It happens if there is no write permissions in the current directory (`Win64`). To remediate this issue CLEO fallbacks to using alternate path at `C:\Users\\AppData\Roaming\CLEO Redux`. `cleo_redux.log` and the `CLEO` directory can be found there. See also the [troubleshooting guide](TROUBLESHOOTING.md). - -### How to uninstall CLEO Redux? - -- Delete `cleo_redux64.asi`. -- Delete the `CLEO` folder (optional). -- Delete the `cleo_redux.log` (optional) - -### Is there any difference from support of the classic games? - -There is. CLEO does not display the version in the game menu. Also CLEO can run only JS scripts in GTA III and GTA VC. In San Andreas both CS and JS scripts are supported. - -### Can I use original opcodes? - -Yes, you can. Refer to the Sanny Builder library https://library.sannybuilder.com/#/sa_unreal. Take a note that some opcodes have been changed from the classic games, so don't expect everything to work like it was in classic. If you run into an issue, find help in [our Discord](https://discord.gg/d5dZSfgBZr). - -### How do I know what commands can I use in JavaScript? - -After each game run, CLEO generates a d.ts file in the CLEO\.config directory. It's called gta3.d.ts, vc.d.ts or sa.d.ts depending on the game. This file lists all supported functions and methods that you can use in JavaScript code. - -To enable autocomplete in VS Code include the following line in your JS script: - -```js -/// -``` - -Update the file name accordingly depending on which game your script is for. - -### Can I use CLEO opcodes? - -Opcodes from CLEO Library (CLEO 4 or CLEO for GTA III and Vice City) are not supported. But CLEO Redux adds its own new opcodes for some operations. - - - 0C00 [IS_KEY_PRESSED](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C00) - - 0C01 [INT_ADD](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C01) - - 0C02 [INT_SUB](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C02) - - 0C03 [INT_MUL](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C03) - - 0C04 [INT_DIV](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C04) - - 0C05 [TERMINATE_THIS_CUSTOM_SCRIPT](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C05) - - 0C06 [WRITE_MEMORY](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C06) (**UNSAFE** - requires `mem` permission) - - 0C07 [READ_MEMORY](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C07) (**UNSAFE** - requires `mem` permission) - - 0C08 [CALL_FUNCTION](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C08) (**UNSAFE** - requires `mem` permission) - - 0C09 [CALL_FUNCTION_RETURN](https://library.sannybuilder.com/#/sa_unreal/CLEO/0C09) (**UNSAFE** - requires `mem` permission) - -Note that Sanny Builder does not support these new opcodes out-of-the-box yet. To enable new opcodes in your CS scripts add the following lines on top of your script: - -``` -{$O 0C00=1, is_key_pressed %1d% } -{$O 0C01=3,%3d% = %1d% + %2d% } -{$O 0C02=3,%3d% = %1d% - %2d% } -{$O 0C03=3,%3d% = %1d% * %2d% } -{$O 0C04=3,%3d% = %1d% / %2d% } -{$O 0C05=0,terminate_this_custom_script } -{$O 0C06=5,write_memory %1d% size %2d% value %3d% virtual_protect %4d% ib %5d% } -{$O 0C07=5,%5d% = read_memory %1d% size %2d% virtual_protect %3d% ib %4d% } -{$O 0C08=-1,call_function %1d% ib %2d% num_params %3d%} -{$O 0C09=-1,call_function_return %1d% ib %2d% num_params %3d%} -``` - -This list might not be complete as there are custom plugins with extra commands (see [Using SDK](./using-sdk.md)). Refer to [Sanny Builder Library](https://library.sannybuilder.com) for the complete list of available commands for each game. - -### Can I work with the game memory or call the game functions? - -Yes, check the [Memory guide](using-memory-64.md) - -### How do I compile CLEO scripts with Sanny Builder? - -Use SA Mobile mode to compile CLEO scripts for San Andreas: The Definitive Edition. Note that CLEO Redux does not support CS scripts in GTA III: DE and VC: DE. JS scripts are supported in all games. - -### I can't find an answer to my question here, where do I go? - -- Check the main [readme file](README.md) -- Check the [troubleshooting guide](TROUBLESHOOTING.md). -- Check the [GitHub tickets](https://github.com/cleolibrary/CLEO-Redux/issues) -- Check the [Feature support page](https://github.com/cleolibrary/CLEO-Redux/wiki/Feature-Support-Matrix) -- Ask a question in [our Discord](https://discord.gg/d5dZSfgBZr) diff --git a/unsupported.md b/unsupported.md deleted file mode 100644 index b746905..0000000 --- a/unsupported.md +++ /dev/null @@ -1,19 +0,0 @@ -## Unsupported or limited support scenarios - -Despite our best effort some scenarios while available in game are not supported or supported with limitations by CLEO Redux. Some of them are imposed by the nature of SCM format or JavaScript language or the difficulties in bridging JavaScript and the native code. - -Check the [Features support page](https://github.com/cleolibrary/CLEO-Redux/wiki/Feature-Support-Matrix) to find out high-level features and the status of their support in different games. - -The following items are known to be not working and there is no specific timeline on getting them fixed. - -### Unsupported features in CS - -- in x64 games (SA: DE) you can't read and write 64-bit values as the script engine only supports 32-bit values. You may need to use other means to access the game memory (e.g. from JavaScript) - -### Unsupported features in JS - -- commands requiring an scm variable (e.g. countdown timers). [Tracking issue](https://github.com/cleolibrary/CLEO-Redux/issues/10). - -- commands implicitly loading models or textures (such as widgets) [Tracking issue](https://github.com/cleolibrary/CLEO-Redux/issues/12). You can circumvent the issue by preloading needed resources, e.g. by calling them in a .CS script first. - -- you can't call the game functions that need references to variables to store the result. There is no "take an address of the variable" syntax. diff --git a/using-fxt.md b/using-fxt.md deleted file mode 100644 index 8c5676e..0000000 --- a/using-fxt.md +++ /dev/null @@ -1,83 +0,0 @@ -## Using FXT - -### Static FXT files - -Demo: https://youtu.be/ctsKy7WnY9o - -CLEO Redux can load and serve static text content. Create a new file with the `.fxt` extension and put it in the `CLEO\CLEO_TEXT` folder. The file name can be any valid name. - -Each FXT file contains a list of the key-value entries in the following format: - -``` - - -... - -``` - -There should be a single space character between a key and a value. The key's maximum length is 7 characters. Try using unique keys that are unlikely to clash with other entries. The text length is unlimited, however each game may impose its own limitations. - -CLEO loads FXT files on startup and merges their content in a single dictionary. It also monitors the files and reloads them if any change is made. - -You can also find an editor for FXT files on the cleo.li website: https://cleo.li/download.html - -To display the custom content in game, use the `Text` class. The key defined in the FXT file is usually the first argument to Text commands, e.g. - -``` -Text.PrintHelp('KEY1') // will display -``` - -You can find the commands available in each game in the Sanny Builder Library, e.g. for San Andreas: DE https://library.sannybuilder.com/#/sa_unreal/classes/Text - - -### FxtStore - -Demo: https://youtu.be/FLyYyrGz1Xg - -CLEO Redux provides an interface for manipulating custom text directly in JavaScript code. There is a static variable, named `FxtStore` with the following interface: - -```ts -declare interface FxtStore { - /** - * Inserts new text content in the script's fxt storage overwriting the previous content and shadowing static fxt with the same key - * @param key GXT key that can be used in text commands (7 characters max) - * @param value text content - */ - insert(key: string, value: string): void; - /** - * Removes the text content associated with the key in the local fxt storage - * @param key GXT key - */ - delete(key: string): void; -} -``` - -Using `FxtStore` you can create unique keys and values in the script and put it in local FXT storage. Each script owns a private storage and keys from one script will not conflict with other scripts. Also keys defined in the FxtStore will shadow the same keys defined in static FXT files. Consider the example: - -custom.fxt: -``` -MY_KEY Text from FXT file -``` - -custom.js: - -```js -Text.PrintHelp('MY_KEY') // this displays "Text from FXT file" -FxtStore.insert('MY_KEY', 'Text from script'); -Text.PrintHelp('MY_KEY') // this displays "Text from script" -FxtStore.delete('MY_KEY') -Text.PrintHelp('MY_KEY') // this displays "Text from FXT file" again -``` - -A private FXT storage is not supported in San Andreas: The Definitive Edition. Each script there modifies the global FXT storage. This behavior may change in the future. - -Custom text can be constructed dynamically, e.g.: - -```js -while(true) { - wait(0); - FxtStore.insert('TIME', 'Timestamp: ' + Date.now()); - Text.PrintHelp('TIME') // this displays "Timestamp: " and the updated timestamp value -} -``` - diff --git a/using-math.md b/using-math.md deleted file mode 100644 index 9447717..0000000 --- a/using-math.md +++ /dev/null @@ -1,77 +0,0 @@ -## Using Math Object - -JavaScript has a built-in `Math` object that provides common mathematical operations, such as `abs`, `sin`, `cos`, `random`, `pow`, `sqr`, etc. CLEO Redux extends this object to include extra operations supported by the game. The interface of `Math` looks as follows: - -```ts -interface Math { - // native code - readonly E: number; - readonly LN10: number; - readonly LN2: number; - readonly LOG2E: number; - readonly LOG10E: number; - readonly PI: number; - readonly SQRT1_2: number; - readonly SQRT2: number; - abs(x: number): number; - acos(x: number): number; - asin(x: number): number; - atan(x: number): number; - atan2(y: number, x: number): number; - ceil(x: number): number; - cos(x: number): number; - exp(x: number): number; - floor(x: number): number; - log(x: number): number; - max(...values: number[]): number; - min(...values: number[]): number; - pow(x: number, y: number): number; - random(): number; - round(x: number): number; - sin(x: number): number; - sqrt(x: number): number; - tan(x: number): number; - clz32(x: number): number; - imul(x: number, y: number): number; - sign(x: number): number; - log10(x: number): number; - log2(x: number): number; - log1p(x: number): number; - expm1(x: number): number; - cosh(x: number): number; - sinh(x: number): number; - tanh(x: number): number; - acosh(x: number): number; - asinh(x: number): number; - atanh(x: number): number; - hypot(...values: number[]): number; - trunc(x: number): number; - fround(x: number): number; - cbrt(x: number): number; - - // GTA III, GTA Vice City, GTA SA commands - ConvertMetersToFeet(meters: int): int; - RandomFloatInRange(min: float, max: float): float; - RandomIntInRange(min: int, max: int): int; - - // GTA Vice City, GTA SA commands - GetDistanceBetweenCoords2D(fromX: float, fromY: float, toX: float, toZ: float): float; - GetDistanceBetweenCoords3D(fromX: float, fromY: float, fromZ: float, toX: float, toY: float, toZ: float): float; - - // GTA SA commands - GetAngleBetween2DVectors(xVector1: float, yVector1: float, xVector2: float, yVector2: float): float; - GetHeadingFromVector2D(_p1: float, _p2: float): float; - LimitAngle(value: float): float; -} -``` - -The first group includes the native constants and methods provided by the JavaScript runtime. They start with a lowercase letter, e.g. `Math.abs`. You can find the detailed documentation for these methods [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). - -Then the game-specific commands go. Following the naming convention, each method that is bound to a script opcode starts with a capital letter, e.g. `Math.RandomIntInRange` (opcode 0209). You can find the documentation in [Sanny Builder Library](https://library.sannybuilder.com/). - - -```js - var x = Math.abs(-1); // x = 1 - var f = Math.ConvertMetersToFeet(10) // f = 32 - var pi = Math.floor(Math.PI) // pi = 3 -``` diff --git a/using-memory-64.md b/using-memory-64.md deleted file mode 100644 index 74e6f25..0000000 --- a/using-memory-64.md +++ /dev/null @@ -1,210 +0,0 @@ -Note: This guide is for the remastered games running as 64-bit applications. For the information on using the Memory class in classic era games [click here](./using-memory.md). - -## Using Memory Object - -An intrinsic object `Memory` provides methods for accessing and manipulating the data or code in the current process. It has the following interface: - -```ts -interface Memory { - ReadFloat(address: int, vp: boolean, ib: boolean): float; - WriteFloat(address: int, value: float, vp: boolean, ib: boolean): void; - ReadI8(address: int, vp: boolean, ib: boolean): int; - ReadI16(address: int, vp: boolean, ib: boolean): int; - ReadI32(address: int, vp: boolean, ib: boolean): int; - ReadU8(address: int, vp: boolean, ib: boolean): int; - ReadU16(address: int, vp: boolean, ib: boolean): int; - ReadU32(address: int, vp: boolean, ib: boolean): int; - WriteI8(address: int, value: int, vp: boolean, ib: boolean): void; - WriteI16(address: int, value: int, vp: boolean, ib: boolean): void; - WriteI32(address: int, value: int, vp: boolean, ib: boolean): void; - WriteU8(address: int, value: int, vp: boolean, ib: boolean): void; - WriteU16(address: int, value: int, vp: boolean, ib: boolean): void; - WriteU32(address: int, value: int, vp: boolean, ib: boolean): void; - Read(address: int, size: int, vp: boolean, ib: boolean): int; - Write(address: int, size: int, value: int, vp: boolean, ib: boolean): void; - - ToFloat(value: int): float; - FromFloat(value: float): int; - ToU8(value: int): int; - ToU16(value: int): int; - ToU32(value: int): int; - ToI8(value: int): int; - ToI16(value: int): int; - ToI32(value: int): int; - - CallFunction(address: int, ib: boolean, numParams: int, ...funcParams: int[]): void; - CallFunctionReturn(address: int, ib: boolean, numParams: int, ...funcParams: int[]): int; - CallFunctionReturnFloat(address: int, ib: boolean, numParams: int, ...funcParams: int[]): float; - - Fn: { - X64(address: int, ib: boolean): (...funcParams: int[]) => int; - X64Float(address: int, ib: boolean): (...funcParams: int[]) => float; - X64I8(address: int, ib: boolean): (...funcParams: int[]) => int; - X64I16(address: int, ib: boolean): (...funcParams: int[]) => int; - X64I32(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U8(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U16(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U32(address: int, ib: boolean): (...funcParams: int[]) => int; - } -} -``` - -### Reading and Writing Values - -Group of memory access methods (`ReadXXX`/`WriteXXX`) can be used for reading or modifying values stored in the memory. Each method is designed for a particular data type. To change a floating-point value (which occupies 4 bytes in the original game) use `Memory.WriteFloat`, e.g.: - -```js - Memory.WriteFloat(address, 1.0, false, false) -``` - -where `address` is a variable storing the memory location, `1.0` is the value to write, the first `false` means it's not necessary to change the memory protection with `VirtualProtect` (the address is already writable). The second `false` is the value of the `ib` flag that instructs CLEO to treat the `address` either as an absolute address (`ib` = `false`) or a relative offset to the current image base address (`ib` = `true`). As the definitive editions use the ASLR feature their absolute memory addresses change when the game runs because the start address changes. Consider the following example: - -``` -0x1400000000 ImageBase -... -... -0x1400000020 SomeValue -``` -You want to change `SomeValue` that is currently located at `0x1400000020`. You can do it with `Memory.Write(0x1400000020, 1, 1, false, false)`. However on the next game run the memory layout might look like this: - -``` -0x1500000000 ImageBase -... -... -0x1500000020 SomeValue -``` - -effectively breaking the script. In this case, calculate a relative offset from the image base ( `0x1500000020` - `0x1500000000` = `0x20` ), that will be permanent for the particular game version. Use Memory.Write as follows: `Memory.Write(0x20, 1, 1, false, true)`. CLEO will sum up the offset (`0x20`) with the current value of the image base (`0x1400000000`, `0x1500000000`, etc) and write to the correct absolute address. - -For your convenience you can find the current value of the image base in the `cleo_redux.log`, e.g.: - -``` -09:27:35 [INFO] Image base address 0x7ff7d1f50000 -``` - -Similarly, to read a value from the memory, use one of the `ReadXXX` methods, depending on what data type the memory address contains. For example, to read a 8-bit signed integer (also known as a `char` or `uint8`) use `Memory.ReadI8`, e.g.: - -```js - var x = Memory.ReadI8(offset, true, true) -``` - -variable `x` now holds a 8-bit integer value in the range (0..255). For the sake of showing possible options, this example uses `true` as the last argument, which means the default protection attribute for this address will be changed to `PAGE_EXECUTE_READWRITE` before the read. - -```js - var gravity = Memory.ReadFloat(gravityOffset, false, true); - gravity += 0.05; - Memory.WriteFloat(gravityOffset, gravity, false, true); -``` - -Finally, last two methods `Read` and `Write` is what other methods use under the hood. They have direct binding to the Rust code that reads and write the memory. In JavaScript code you can use input arguments as large as 53-bit numbers. - -The `size` parameter in the `Read` method can only be `1`, `2`, `4` or `8`. CLEO treats the `value` as a signed integer stored in the little-endian format. - -In the `Write` method any `size` larger than `0` is allowed. Sizes `3`, `5`, `6`, `7` and `9` onwards can only be used together with a single byte `value`. CLEO uses them to fill a continious block of memory starting at the `address` with the given `value` (think of it as `memset` in C++). - -```js - Memory.Write(offset, 0x90, 10, true, true) // "noping" 10 bytes of code starting from offset+image base -``` - -**Note that usage of any of the read/write methods requires the `mem` [permission](README.md#Permissions)**. - - -### Casting methods - -By default `Read` and `Write` methods treat data as signed integer values. It can be inconvinient if the memory holds a floating-point value in IEEE 754 format or a large 32-bit signed integer (e.g. a pointer). In this case use casting methods `ToXXX`/`FromXXX`. They act similarly to [reinterpret_cast](https://docs.microsoft.com/en-us/cpp/cpp/reinterpret-cast-operator?view=msvc-160) operator in C++. - -To get a quick idea what to expect from those methods see the following examples: - -```js - Memory.FromFloat(1.0) => 1065353216 - Memory.ToFloat(1065353216) => 1.0 - Memory.ToU8(-1) => 255 - Memory.ToU16(-1) => 65535 - Memory.ToU32(-1) => 4294967295 - Memory.ToI8(255) => -1 - Memory.ToI16(65535) => -1 - Memory.ToI32(4294967295) => -1 -``` - -Alternatively, use appropriate methods to read/write the value as a float (`ReadFloat`/`WriteFloat`) or as an unsigned integer (`ReadUXXX`/`WriteUXXX`). - -### Calling Foreign Functions - -`Memory` object allows to invoke a foreign (native) function by its address using one of the following methods: - -- `Memory.CallFunction` -- `Memory.CallFunctionReturn` -- `Memory.CallFunctionReturnFloat` - -```js - Memory.CallFunction(0xEFFB30, true, 1, 13) -``` -where `0xEFFB30` is the function offset relative to IMAGE BASE (think of it a randomized start address of the game memory), `true` is the `ib` flag (see below), `1` is the number of input arguments, and `13` are the only argument passed into the function. - -The `ib` parameter in `Memory.CallFunction` has the same meaning as in memory read/write commands. When set to `true` CLEO adds the current known address of the image base to the value provided as the first argument to calculate the absolute memory address of the function. When set to `false` no changes to the first argument are made. - -To pass floating-point values to the function, convert the value to integer using `Memory.FromFloat`: - -```js - Memory.CallFunction(0x1234567, true, 1, Memory.FromFloat(123.456)); -``` - -The returned value of the function called with `Memory.CallFunction` is ignored. To read the result use `Memory.CallFunctionReturn` that has the same parameters. Use `Memory.CallFunctionReturnFloat` to call a function that returns a floating-point value. - - -CLEO Redux supports calling foreign functions with up to 16 parameters. - -**Note that usage of any of the call methods requires the `mem` [permission](README.md#Permissions)**. - -#### Convenience methods with Fn object - -`Memory.Fn` provides convenient methods for calling different types of foreign functions. - -```ts - Fn: { - X64(address: int, ib: boolean): (...funcParams: int[]) => int; - X64Float(address: int, ib: boolean): (...funcParams: int[]) => float; - X64I8(address: int, ib: boolean): (...funcParams: int[]) => int; - X64I16(address: int, ib: boolean): (...funcParams: int[]) => int; - X64I32(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U8(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U16(address: int, ib: boolean): (...funcParams: int[]) => int; - X64U32(address: int, ib: boolean): (...funcParams: int[]) => int; - } -``` - -These methods is designed to cover all supported return types. For example, this code - -```js - Memory.CallFunction(0xEFFB30, true, 1, 13) -``` - -can also be written as - -```js - Memory.Fn.X64(0xEFFB30, true)(13) -``` - -Note a few key differences here. First of all, `Memory.Fn` methods don't invoke a foreign function directly. Instead, they return a new JavaScript function that can be stored in a variable and reused to call the associated foreign function many times with different arguments: - -```js - var f = Memory.Fn.X64(0xEFFB30, true); - f(13) // calls function 0xEFFB30 with the argument of 13 - f(11) // calls method 0xEFFB30 with the argument of 11 -``` - -The second difference is that there is no `numParams` parameter. Each `Fn` method figures it out automatically. - -By default a returned result is considered a 64-bit signed integer value. If the function returns another type (e.g. a boolean), use one of the methods matching the function signature: - -```js - var flag = Memory.Fn.X64U8(0x1234567, true)() -``` - -This code invokes a function at `0x1234567` + IMAGE_BASE with no arguments and stores the result as a 8-bit unsigned integer value. - -```js - var float = Memory.Fn.X64Float(0x456789, true)() -``` - -This code invokes a function at `0x456789` + IMAGE_BASE with no arguments and stores the result as a floating-point value. diff --git a/using-memory.md b/using-memory.md deleted file mode 100644 index 249f83a..0000000 --- a/using-memory.md +++ /dev/null @@ -1,255 +0,0 @@ -Note: 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). - -## Using Memory Object - -An intrinsic object `Memory` provides methods for accessing and manipulating the data or code in the current process. It has the following interface: - -```ts -interface Memory { - ReadFloat(address: int, vp: boolean): float; - WriteFloat(address: int, value: float, vp: boolean): void; - ReadI8(address: int, vp: boolean): int; - ReadI16(address: int, vp: boolean): int; - ReadI32(address: int, vp: boolean): int; - ReadU8(address: int, vp: boolean): int; - ReadU16(address: int, vp: boolean): int; - ReadU32(address: int, vp: boolean): int; - WriteI8(address: int, value: int, vp: boolean): void; - WriteI16(address: int, value: int, vp: boolean): void; - WriteI32(address: int, value: int, vp: boolean): void; - WriteU8(address: int, value: int, vp: boolean): void; - WriteU16(address: int, value: int, vp: boolean): void; - WriteU32(address: int, value: int, vp: boolean): void; - Read(address: int, size: int, vp: boolean): int; - Write(address: int, size: int, value: int, vp: boolean): void; - - ToFloat(value: int): float; - FromFloat(value: float): int; - ToU8(value: int): int; - ToU16(value: int): int; - ToU32(value: int): int; - ToI8(value: int): int; - ToI16(value: int): int; - ToI32(value: int): int; - - Translate(symbol: string): int; - - CallFunction(address: int, numParams: int, pop: int, ...funcParams: int[]): void; - CallFunctionReturn(address: int, numParams: int, pop: int, ...funcParams: int[]): int; - CallMethod(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): void; - CallMethodReturn(address: int, struct: int, numParams: int, pop: int, ...funcParams: int[]): int; - Fn: { - Cdecl(address: int): (...funcParams: int[]) => int; - CdeclFloat(address: int): (...funcParams: int[]) => float; - CdeclI8(address: int): (...funcParams: int[]) => int; - CdeclI16(address: int): (...funcParams: int[]) => int; - CdeclI32(address: int): (...funcParams: int[]) => int; - CdeclU8(address: int): (...funcParams: int[]) => int; - CdeclU16(address: int): (...funcParams: int[]) => int; - CdeclU32(address: int): (...funcParams: int[]) => int; - - Stdcall(address: int): (...funcParams: int[]) => int; - StdcallFloat(address: int): (...funcParams: int[]) => float; - StdcallI8(address: int): (...funcParams: int[]) => int; - StdcallI16(address: int): (...funcParams: int[]) => int; - StdcallI32(address: int): (...funcParams: int[]) => int; - StdcallU8(address: int): (...funcParams: int[]) => int; - StdcallU16(address: int): (...funcParams: int[]) => int; - StdcallU32(address: int): (...funcParams: int[]) => int; - - Thiscall(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallFloat(address: int, struct: int): (...funcParams: int[]) => float; - ThiscallI8(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallI16(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallI32(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU8(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU16(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU32(address: int, struct: int): (...funcParams: int[]) => int; - } -} -``` - -### Reading and Writing Values - -Group of memory access methods (`ReadXXX`/`WriteXXX`) can be used for reading or modifying values stored in the memory. Each method is designed for a particular data type. To change a floating-point value (which occupies 4 bytes in the original game) use `Memory.WriteFloat`, e.g.: - -```js - Memory.WriteFloat(address, 1.0, false) -``` - -where `address` is a variable storing the memory location, `1.0` is the value to write and `false` means it's not necessary to change the memory protection with `VirtualProtect` (the address is already writable). - -Similarly, to read a value from the memory, use one of the `ReadXXX` methods, depending on what data type the memory address contains. For example, to read a 8-bit signed integer (also known as a `char` or `uint8`) use `Memory.ReadI8`, e.g.: - -```js - var x = Memory.ReadI8(address, true) -``` - -variable `x` now holds a 8-bit integer value in the range (0..255). For the sake of showing possible options, this example uses `true` as the last argument, which means the default protection attribute for this address will be changed to `PAGE_EXECUTE_READWRITE` before the read. - -```js - var gravity = Memory.ReadFloat(gravityAddress, false); - gravity += 0.05; - Memory.WriteFloat(gravityAddress, gravity, false); -``` - -Finally, last two methods `Read` and `Write` is what other methods use under the hood. They have direct binding to opcodes [0A8D READ_MEMORY](https://library.sannybuilder.com/#/gta3/CLEO/0A8D) and [0A8C WRITE_MEMORY](https://library.sannybuilder.com/#/gta3/CLEO/0A8C) and produce the same result. - -The `size` parameter in the `Read` method can only be `1`, `2` or `4`. CLEO treats the `value` as a signed integer stored in the little-endian format. - -In the `Write` method any `size` larger than `0` is allowed. Sizes `3` and `5` onwards can only be used together with a single byte `value`. CLEO uses them to fill a continious block of memory starting at the `address` with the given `value` (think of it as `memset` in C++). - -```js - Memory.Write(addr, 0x90, 10, true) // "noping" 10 bytes of code starting from addr -``` - -**Note that usage of any of the read/write methods requires the `mem` [permission](README.md#Permissions)**. - - -### Casting methods - -By default `Read` and `Write` methods treat data as signed integer values. It can be inconvinient if the memory holds a floating-point value in IEEE 754 format or a large 32-bit signed integer (e.g. a pointer). In this case use casting methods `ToXXX`/`FromXXX`. They act similarly to [reinterpret_cast](https://docs.microsoft.com/en-us/cpp/cpp/reinterpret-cast-operator?view=msvc-160) operator in C++. - -To get a quick idea what to expect from those methods see the following examples: - -```js - Memory.FromFloat(1.0) => 1065353216 - Memory.ToFloat(1065353216) => 1.0 - Memory.ToU8(-1) => 255 - Memory.ToU16(-1) => 65535 - Memory.ToU32(-1) => 4294967295 - Memory.ToI8(255) => -1 - Memory.ToI16(65535) => -1 - Memory.ToI32(4294967295) => -1 -``` - -Alternatively, use appropriate methods to read/write the value as a float (`ReadFloat`/`WriteFloat`) or as an unsigned integer (`ReadUXXX`/`WriteUXXX`). - - -### Calling Foreign Functions - -`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) - - -```js - Memory.CallFunction(0x1234567, 2, 0, 1000, 2000) -``` -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. - - -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. - -`Memory.CallFunctionReturn` has the same interface but additionally it writes the result of the function to a variable. - -`Memory.CallMethod` invokes a method of an object: - -```js - Memory.CallMethod(0x2345678, 0x7001234, 2, 0, 1000, 2000) -``` - -The second parameter (`0x7001234`) is the object address. The `pop` parameter is always `0` (the method uses the [thiscall](https://en.wikipedia.org/wiki/X86_calling_conventions#thiscall) convention). - -To call the method and get the result out of it, use `Memory.CallMethodReturn`. - -Note that all arguments are read as 32-bit signed integers. If you need to provide an argument of the float type, use `Memory.FromFloat`, e.g. - -```js - Memory.CallFunction(0x1234567, 1, 1, Memory.FromFloat(123.456)) -``` - -CLEO Redux supports calling foreign functions with up to 16 parameters. - -**Note that usage of any of the call methods requires the `mem` [permission](README.md#Permissions)**. - -#### Convenience methods with Fn object - -`Memory.Fn` provides a lot of convenient methods for calling different types of foreign functions. - -```ts -Fn: { - Cdecl(address: int): (...funcParams: int[]) => int; - CdeclFloat(address: int): (...funcParams: int[]) => float; - CdeclI8(address: int): (...funcParams: int[]) => int; - CdeclI16(address: int): (...funcParams: int[]) => int; - CdeclI32(address: int): (...funcParams: int[]) => int; - CdeclU8(address: int): (...funcParams: int[]) => int; - CdeclU16(address: int): (...funcParams: int[]) => int; - CdeclU32(address: int): (...funcParams: int[]) => int; - - Stdcall(address: int): (...funcParams: int[]) => int; - StdcallFloat(address: int): (...funcParams: int[]) => float; - StdcallI8(address: int): (...funcParams: int[]) => int; - StdcallI16(address: int): (...funcParams: int[]) => int; - StdcallI32(address: int): (...funcParams: int[]) => int; - StdcallU8(address: int): (...funcParams: int[]) => int; - StdcallU16(address: int): (...funcParams: int[]) => int; - StdcallU32(address: int): (...funcParams: int[]) => int; - - Thiscall(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallFloat(address: int, struct: int): (...funcParams: int[]) => float; - ThiscallI8(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallI16(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallI32(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU8(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU16(address: int, struct: int): (...funcParams: int[]) => int; - ThiscallU32(address: int, struct: int): (...funcParams: int[]) => int; - } -``` - -These methods is designed to cover all possible function signatures. For example, this code - -```js - Memory.CallMethod(0x2345678, 0x7001234, 2, 0, 1000, 2000) -``` - -can also be written as - -```js - Memory.Fn.Thiscall(0x2345678, 0x7001234)(1000, 2000) -``` - -Note a few key differences here. First of all, `Memory.Fn` methods don't invoke a foreign function directly. Instead, they return a new JavaScript function that can be stored in a variable and reused to call the associated foreign function many times with different arguments: - -```js - var myMethod = Memory.Fn.Thiscall(0x2345678, 0x7001234); - myMethod(1000, 2000); // calls method 0x2345678 with arguments 1000 and 2000 - myMethod(3000, 5000); // calls method 0x2345678 with arguments 3000 and 5000 -``` - -The second difference is that there are no `numParams` and `pop` parameters. Each `Fn` method figures them out automatically. - -By default a returned result is considered a 32-bit signed integer value. If the function returns another type (a floating-point value, or a signed integer), use one of the methods matching the function signature, e.g.: - -```js - var flag = Memory.Fn.CdeclU8(0x1234567)() -``` - -This code invokes a `cdecl` function at `0x1234567` with no arguments and stores the result as a 8-bit unsigned integer value. - - -### Finding Memory Addresses in re3 and reVC - -Since `re3` and `reVC` use address space layout randomization (ASLR) feature, it can be difficult to locate needed addresses. CLEO Redux provides a helper function `Memory.Translate` that accepts a name of the function or variable and returns its current address. If the requested symbol is not found, the result is 0. - -```js - var addr = Memory.Translate("CTheScripts::MainScriptSize"); - - // check if address is not zero - if (addr) { - showTextBox("MainScriptSize = " + Memory.ReadI32(addr, 0)) - } -``` - -At the moment `Memory.Translate` should only be used in `re3` and `reVC`. In other games you will be getting `0` as a result most of the time. \ No newline at end of file diff --git a/using-sdk.md b/using-sdk.md deleted file mode 100644 index 498cd79..0000000 --- a/using-sdk.md +++ /dev/null @@ -1,105 +0,0 @@ -## CLEO Redux SDK - -SDK provides a way to create new script commands for any game that CLEO Redux supports. It is agnostic to the game title and the underlying runtime (CS or JS). At this moment CLEO provides [SDK for the C++ and Rust languages](./plugins/SDK). - -### Platforms Support - -CLEO Redux provides SDK for both 32-bit and 64-bit games. There is one notable change between the two: on the 32-bit platform the SDK functions `GetIntParam` and `SetIntParam` operate on signed 32-bit numbers, whereas on the 64-bit platform they operate on signed 64-bit numbers (declared as the type `isize`). - -### Plugin structure - -Each plugin is a dynamic library with the `.cleo` extension that must be placed in `CLEO\CLEO_PLUGINS`. CLEO Redux scans this directory on startup and loads all `.cleo` files using WinAPI's function `LoadLibrary`. To register a handler for the new command the plugin must call `RegisterCommand` in the DllMain function. Once a user script encounters this command CLEO Redux invokes the handler with the one argument which is a pointer to the current context. This pointer must be used for calling other SDK methods. - -### Naming convention - -It's recommended for 64-bit plugins to have `64` in their names (e.g. `myplugin64.cleo`). - -### Unsafe commands - -Commands that use low-level WinAPI and can potentially damage user environment must be explicitly registered with a permission token (third argument to the `RegisterCommand`). User can disallow usage of unsafe commands in the scripts using [permission config](https://github.com/cleolibrary/CLEO-Redux#permissions). At the moment three permission tokens are used: `mem`, `fs`, and `dll`. They mark commands operating with the host process, user files and external libraries. - -### 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). - -#### 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 - -#### 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. - -### SDK Version - -The current version is `1`. Changes to SDK will advance this number by one. - -### Path Resolution Convention - -String arguments representing a path to the directory or file must be normalized using SDK's function `ResolvePath`. This function takes a path and returns the absolute path resolved by the following rules: - -- an absolute path gets resolved as is -- path starting with "CLEO/" or "CLEO\\" gets resolved relative to the CLEO directory which is either - - {game}\CLEO or - - {user}\AppData\Roaming\CLEO Redux\CLEO -- all other paths get resolved relative to the current working directory (the game directory) - - -### String Arguments - -Strings passed in and out of the SDK methods are UTF-8 encoded. - -If the script uses an integer value where a string is expected SDK treats this number as a pointer to a null-terminated UTF-8 character sequence to read, or to a large enough buffer to store the result to: - -```js -IniFile.WriteString(0xDEADBEEF, "my.ini", "section", "key") -``` - -SDK will read a string from the address `0xDEADBEEF` and write it to the ini file. - -``` -0AF4: read_string_from_ini_file 'my.ini' section 'section' key 'key' store_to 0xDEADBEEF -``` - -SDK will read a string from the ini file and write it at the address `0xDEADBEEF`. - -### C++ SDK - -Custom plugins may call methods exposed by CLEO Redux using a provided `.lib` file. Include `cleo_redux_sdk.h` in your DLL project and link the binary with `cleo_redux.lib` (or `cleo_redux64.lib` if the target platform is x86_64) and you can start writing new commands. - -#### Example - -See the `IniFiles` plugin that includes a project for Visual Studio 2019. It adds a static class `IniFile` with the following methods: - -```ts -interface IniFile { - ReadFloat(path: string, section: string, key: string): float | undefined; - ReadInt(path: string, section: string, key: string): int | undefined; - ReadString(path: string, section: string, key: string): string | undefined; - WriteFloat(value: float, path: string, section: string, key: string): boolean; - WriteInt(value: int, path: string, section: string, key: string): boolean; - WriteString(value: string, path: string, section: string, key: string): boolean; -} -``` - -See more information in Sanny Builder Library: https://library.sannybuilder.com/#/sa_unreal/classes/IniFile. The usage of the `IniFile` class requires an `fs` [permission](README.md#permissions). - -### Rust SDK - -Rust SDK uses similar to C++ interface with some extra wrapping methods to allow easily convert between C and Rust types. The header file is available as a [crate](https://crates.io/crates/cleo_redux_sdk) on crates.io. See the documentation [here](https://docs.rs/cleo_redux_sdk/latest/). - -#### Example - -See the `Dylib` plugin. It adds a class `DynamicLibrary` with the following methods: - -```ts -declare class DynamicLibrary { - constructor(handle: number); - static Load(libraryFileName: string): DynamicLibrary | undefined; - free(): void; - getProcedure(procName: string): int | undefined; -} -``` - -See more information in Sanny Builder Library: https://library.sannybuilder.com/#/sa_unreal/classes/DynamicLibrary. The usage of the `DynamicLibrary` class requires a `dll` [permission](README.md#permissions). -