refactor!: repo cleanup (#2650)

- Move cmake files to dedicated cmake folder
- Move scattered python files to `scripts/` folder
- Update CMakeLists.txt to reflect changes
* feat(scripts): add README to folder
This commit is contained in:
Andreas Maerten
2023-12-30 16:05:18 +01:00
committed by GitHub
parent 22e15e95d6
commit 9a1a75b675
16 changed files with 126201 additions and 126162 deletions

39
scripts/README.md Normal file
View File

@ -0,0 +1,39 @@
# Scripts
This directory contains a collection of scripts used to generate certain parts of the code base.
## Doc Gen
`doc_gen.py` is used to generate the Lua documentation that's provided by YimMenu.
It relies on specifically formatted code comments to generate Lua documentation.
## Generate Natives
`generate_natives.py` is responsible for generating the `src/natives.hpp` and `src/invoker/crossmap.hpp` files.
It takes a `natives.json` from [here](https://github.com/alloc8or/gta5-nativedb-data) and a `crossmap.txt` file which needs follow a certain format of:
```csv
[first_seen_native_hash]<comma>[current_native_hash]
```
Example:
```csv
0xE1A0450ED46A7812,0x11FD21BA1B765FE2
0x39BE7CEA8D9CC8E6,0x5F7C6361179DFFC4
0x3C5FD37B5499582E,0x54BC5E0B6A29AE8A
0xE2A99A9B524BEFFF,0x1FDE21A286357401
0x51F1A8E48C3D2F6D,0xD1BAD83E70275AEB
0x0A6D923DFFC9BD89,0x93693D93BD53ACB1
0x112CEF1615A1139F,0x203607236413B185
0xD47A2C1BA117471D,0x4F3198DEED415E95
0xC2F7FE5309181C7D,0xCFE92984BF3486D5
0x23789E777D14CE44,0x2B3725FC402B94A8
0x350AA5EBC03D3BD2,0x606408352C7741AD
0x498C1E05CE5F7877,0x59E8FA762FB527C5
...
```
## Natives Gen
`natives_gen.py` is used to generate the Lua bindings for all the natives currently present in the menu.
It'll read through the `src/natives.hpp` file and generate the appropriate bindings under `src/lua/natives/`.

View File

@ -1,12 +0,0 @@
include(FetchContent)
message("AsyncLogger")
FetchContent_Declare(
AsyncLogger
GIT_REPOSITORY https://github.com/Yimura/AsyncLogger.git
GIT_TAG v0.0.6
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(AsyncLogger)
set_property(TARGET AsyncLogger PROPERTY CXX_STANDARD 23)

View File

@ -1,14 +0,0 @@
include(FetchContent)
set(BUILD_TESTING_BEFORE ${BUILD_TESTING})
set(CURL_DISABLE_TESTS OFF)
FetchContent_Declare(
cpr
GIT_REPOSITORY https://github.com/libcpr/cpr.git
GIT_TAG 1.10.5
GIT_PROGRESS TRUE
)
message("cpr")
FetchContent_MakeAvailable(cpr)
set(BUILD_TESTING ${BUILD_TESTING_BEFORE} CACHE INTERNAL "" FORCE)

6568
scripts/crossmap.txt Normal file

File diff suppressed because it is too large Load Diff

724
scripts/doc_gen.py Normal file
View File

@ -0,0 +1,724 @@
import os
from enum import Enum
src_folder = "../../src/"
lua_api_comment_identifier = "lua api"
lua_api_comment_separator = ":"
tables = {}
classes = {}
functions = {}
class DocKind(Enum):
Table = "table"
Class = "class"
Field = "field"
Constructor = "constructor"
Function = "function"
Tabs = "tabs"
Infraction = "infraction"
class Table:
def __init__(self, name, fields, functions, description):
self.name = name.strip()
self.fields = fields
self.functions = functions
self.description = description
def __str__(self):
s = f"# Table: {self.name}\n"
s += "\n"
if len(self.description) > 0:
s += f"{self.description}\n"
s += "\n"
if len(self.fields) > 0:
s += f"## Fields ({len(self.fields)})\n"
s += "\n"
self.check_for_duplicate_fields_names()
for field in self.fields:
s += field.print_markdown()
if len(self.functions) > 0:
s += f"## Functions ({len(self.functions)})\n"
s += "\n"
for func in self.functions:
s += func.print_markdown(f"{self.name}.")
s += "\n"
return s
def check_for_duplicate_fields_names(self):
seen = set()
duplicates = [x for x in self.fields if x.name in seen or seen.add(x.name)]
if len(duplicates) > 0:
print("Error while building lua doc. Duplicate field names:")
for dup in duplicates:
print(dup)
exit(1)
class Class:
def __init__(self, name, inheritance, fields, constructors, functions, description):
self.name = name.strip()
self.inheritance = inheritance
self.fields = fields
self.constructors = constructors
self.functions = functions
self.description = description
def __str__(self):
s = f"# Class: {self.name}\n"
s += "\n"
if len(self.inheritance) > 0:
inherited_class_names = ", ".join(self.inheritance)
s += f"## Inherit from {len(self.inheritance)} class: {inherited_class_names}\n"
s += "\n"
if len(self.description) > 0:
s += f"{self.description}\n"
s += "\n"
if len(self.fields) > 0:
s += f"## Fields ({len(self.fields)})\n"
s += "\n"
self.check_for_duplicate_fields_names()
for field in self.fields:
s += field.print_markdown()
if len(self.constructors) > 0:
s += f"## Constructors ({len(self.constructors)})\n"
s += "\n"
for ctor in self.constructors:
s += ctor.print_markdown()
if len(self.functions) > 0:
s += f"## Functions ({len(self.functions)})\n"
s += "\n"
for func in self.functions:
s += func.print_markdown(f"{self.name}:")
s += "\n"
return s
def check_for_duplicate_fields_names(self):
seen = set()
duplicates = [x for x in self.fields if x.name in seen or seen.add(x.name)]
if len(duplicates) > 0:
print("Error while building lua doc. Duplicate field names:")
for dup in duplicates:
print(dup)
exit(1)
class Field:
def __init__(self, name, type_, description):
self.name = name.strip()
self.type_ = type_.strip()
self.description = description
def __str__(self):
s = f"Field: {self.name}\n"
s += f"Type: {self.type_}\n"
s += f"Description: {self.description.strip()}\n"
return s
def print_markdown(self):
s = ""
s += f"### `{self.name}`\n"
s += "\n"
if len(self.description) > 0:
s += f"{self.description}\n"
s += "\n"
if self.type_ is not None and len(self.type_) > 0:
s += f"- Type: `{self.type_}`\n"
s += "\n"
return s
class Constructor:
def __init__(self, parent, parameters, description):
self.parent = parent
self.parameters = parameters
self.description = description
def print_markdown(self):
s = ""
parameters_str = ", ".join(p.name for p in self.parameters)
s += f"### `new({parameters_str})`\n"
s += "\n"
if len(self.description) > 0:
s += f"{self.description}\n"
s += "\n"
if len(self.parameters) > 0:
s += f"- **Parameters:**\n"
for param in self.parameters:
s += f" - `{param.name}` ({param.type_})"
if len(param.description) > 0:
s += f": {param.description}\n"
else:
s += f"\n"
s += "\n"
s += f"**Example Usage:**\n"
s += "```lua\n"
s += f"myInstance = {self.parent.name}:new({parameters_str})\n"
s += "```\n"
s += "\n"
return s
class Parameter:
def __init__(self, name, type_, description):
self.name = name.strip()
self.type_ = type_.strip()
self.description = description
def __str__(self):
s = f"Parameter: {self.name}\n"
s += f"Type: {self.type_}\n"
s += f"Description: {self.description.strip()}\n"
return s
class Function:
def __init__(
self, name, parent, parameters, return_type, return_description, description
):
self.name = name.strip()
self.parent = parent
self.parameters = parameters
self.return_type = return_type
self.return_description = return_description
self.description = description
def __str__(self):
s = f"Function: {self.name}\n"
type_name = str(type(self.parent)).split(".")[1][:-2]
s += f"Parent: {self.parent.name} ({type_name})\n"
s += f"Parameters: {len(self.parameters)}\n"
i = 1
for param in self.parameters:
s += f"Parameter {i}\n"
s += str(param) + "\n"
i += 1
s += f"Return Type: {self.return_type}\n"
s += f"Return Description: {self.return_description}\n"
s += f"Description: {self.description}\n"
return s
def print_markdown(self, prefix):
s = ""
parameters_str = ", ".join(p.name for p in self.parameters)
s += f"### `{self.name}({parameters_str})`\n"
s += "\n"
if len(self.description) > 0:
s += f"{self.description}\n"
s += "\n"
if len(self.parameters) > 0:
s += f"- **Parameters:**\n"
for param in self.parameters:
s += f" - `{param.name}` ({param.type_})"
if len(param.description) > 0:
s += f": {param.description}\n"
else:
s += f"\n"
s += "\n"
if self.return_type is not None and len(self.return_type) > 0:
s += f"- **Returns:**\n"
s += f" - `{self.return_type}`: {self.return_description}\n"
s += "\n"
s += f"**Example Usage:**\n"
s += "```lua\n"
if self.return_type is not None and len(self.return_type) > 0:
s += self.return_type + " = "
if "Global Table" in prefix:
prefix = ""
s += f"{prefix}{self.name}({parameters_str})\n"
s += "```\n"
s += "\n"
return s
def make_table(table_name):
if table_name not in tables:
tables[table_name] = Table(table_name, [], [], "")
cur_table = tables[table_name]
return cur_table
def make_class(class_name):
if class_name not in classes:
classes[class_name] = Class(class_name, [], [], [], [], "")
cur_class = classes[class_name]
return cur_class
def is_comment_a_lua_api_doc_comment(text_lower):
return (
lua_api_comment_identifier in text_lower
and lua_api_comment_separator in text_lower
and "//" in text_lower
)
def parse_lua_api_doc(folder_path):
for root, dirs, files in os.walk(folder_path):
for file_name in files:
if os.path.splitext(file_name)[1].startswith((".c", ".h")):
file_path = os.path.join(root, file_name)
with open(file_path, "r") as file:
doc_kind = None
cur_table = None
cur_class = None
cur_function = None
cur_field = None
cur_constructor = None
for line in file:
line = line.strip()
line_lower = line.lower()
if is_comment_a_lua_api_doc_comment(line_lower):
doc_kind = DocKind(
line.split(lua_api_comment_separator, 1)[1]
.strip()
.lower()
)
if (
doc_kind is not DocKind.Tabs
and doc_kind is not DocKind.Infraction
):
continue
if doc_kind is not None and "//" in line:
match doc_kind:
case DocKind.Table:
cur_table = parse_table_doc(
cur_table, line, line_lower
)
case DocKind.Class:
cur_class = parse_class_doc(
cur_class, line, line_lower
)
case DocKind.Function:
(
cur_function,
cur_table,
cur_class,
) = parse_function_doc(
cur_function,
cur_table,
cur_class,
line,
line_lower,
)
case DocKind.Field:
(cur_field, cur_table, cur_class) = parse_field_doc(
cur_field,
cur_table,
cur_class,
line,
line_lower,
)
case DocKind.Constructor:
(
cur_constructor,
cur_class,
) = parse_constructor_doc(
cur_constructor,
cur_class,
line,
line_lower,
)
case DocKind.Tabs:
parse_tabs_doc(file)
case DocKind.Infraction:
parse_infraction_doc(file)
case _:
# print("unsupported doc kind: " + str(doc_kind))
pass
else:
doc_kind = None
def parse_table_doc(cur_table, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "name"):
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_table = make_table(table_name)
else:
if len(cur_table.description) != 0:
cur_table.description += "\n"
cur_table.description += sanitize_description(line)
return cur_table
def parse_class_doc(cur_class, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "name"):
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name)
elif is_lua_doc_comment_startswith(line_lower, "inherit"):
inherited_class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class.inheritance.append(inherited_class_name)
else:
if len(cur_class.description) != 0:
cur_class.description += "\n"
cur_class.description += sanitize_description(line)
return cur_class
def parse_function_doc(cur_function, cur_table, cur_class, line, line_lower):
if (
is_lua_doc_comment_startswith(line_lower, "table")
and lua_api_comment_separator in line_lower
):
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_table = make_table(table_name)
cur_function = Function("Didnt get name yet", cur_table, [], None, "", "")
cur_table.functions.append(cur_function)
elif (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name)
cur_function = Function("Didnt get name yet", cur_class, [], None, "", "")
cur_class.functions.append(cur_function)
elif (
is_lua_doc_comment_startswith(line_lower, "name")
and lua_api_comment_separator in line_lower
):
function_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_function.name = function_name
if function_name not in functions:
functions[function_name] = cur_function
elif (
is_lua_doc_comment_startswith(line_lower, "param")
and lua_api_comment_separator in line_lower
):
parameter = make_parameter_from_doc_line(line)
cur_function.parameters.append(parameter)
elif (
is_lua_doc_comment_startswith(line_lower, "return")
and lua_api_comment_separator in line_lower
):
return_info = line.split(lua_api_comment_separator, 2)
try:
cur_function.return_type = return_info[1].strip()
cur_function.return_description = return_info[2].strip()
except IndexError:
pass
else:
if len(cur_function.description) != 0:
cur_function.description += "\n"
cur_function.description += sanitize_description(line)
return cur_function, cur_table, cur_class
def parse_field_doc(cur_field, cur_table, cur_class, line, line_lower):
if (
is_lua_doc_comment_startswith(line_lower, "table")
and lua_api_comment_separator in line_lower
):
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_table = make_table(table_name)
cur_field = Field("Didnt get name yet", "", "")
cur_table.fields.append(cur_field)
elif (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name)
cur_field = Field("Didnt get name yet", "", "")
cur_class.fields.append(cur_field)
elif (
is_lua_doc_comment_startswith(line_lower, "field")
and lua_api_comment_separator in line_lower
):
field_info = line.split(lua_api_comment_separator, 2)
cur_field.name = field_info[1].strip()
cur_field.type_ = field_info[2].strip()
else:
if len(cur_field.description) != 0:
cur_field.description += "\n"
if line.startswith("// "):
line = line[3:]
cur_field.description += sanitize_description(line)
return cur_field, cur_table, cur_class
def parse_constructor_doc(cur_constructor, cur_class, line, line_lower):
if (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name)
cur_constructor = Constructor(cur_class, [], "")
cur_class.constructors.append(cur_constructor)
elif (
is_lua_doc_comment_startswith(line_lower, "param")
and lua_api_comment_separator in line_lower
):
parameter = make_parameter_from_doc_line(line)
cur_constructor.parameters.append(parameter)
else:
if len(cur_constructor.description) != 0:
cur_constructor.description += "\n"
cur_constructor.description += sanitize_description(line)
return cur_constructor, cur_class
tabs_enum = []
def parse_tabs_doc(file):
start_parsing = False
for line in file:
if "enum class" in line.lower():
start_parsing = True
continue
if start_parsing:
if "};" in line.lower():
return
if "{" == line.lower().strip():
continue
if "//" in line.lower():
continue
if "" == line.lower().strip():
continue
else:
tabs_enum.append(line.replace(",", "").strip())
infraction_enum = []
def parse_infraction_doc(file):
start_parsing = False
for line in file:
if "enum class" in line.lower():
start_parsing = True
continue
if start_parsing:
if "};" in line.lower():
return
if "{" == line.lower().strip():
continue
if "//" in line.lower():
continue
if "" == line.lower().strip():
continue
else:
infraction_enum.append(line.replace(",", "").strip())
def make_parameter_from_doc_line(line):
param_info = line.split(lua_api_comment_separator, 3)[1:]
param_name = param_type = param_desc = ""
try:
param_name = param_info[0].strip()
param_type = param_info[1].strip()
param_desc = param_info[2].strip()
except IndexError:
pass
return Parameter(param_name, param_type, param_desc)
def sanitize_description(line):
if line.startswith("// ") and line[3] != " ":
line = line[3:]
if line.startswith("//"):
line = line[2:]
return line.rstrip()
def is_lua_doc_comment_startswith(line_lower, starts_with_text):
return line_lower.replace("//", "").strip().startswith(starts_with_text)
parse_lua_api_doc(src_folder)
try:
os.makedirs("./tables/")
except:
pass
for table_name, table in tables.items():
file_name = f"./tables/{table_name}.md"
if os.path.exists(file_name):
os.remove(file_name)
f = open(file_name, "ba")
f.write(bytes(str(table), "UTF8"))
f.close()
tabs_file_name = f"./tabs.md"
if os.path.exists(tabs_file_name):
os.remove(tabs_file_name)
f = open(tabs_file_name, "a")
f.write(
"""# Tabs
All the tabs from the menu are listed below, used as parameter for adding gui elements to them.
**Example Usage:**
```lua
missionsTab = gui.get_tab("GUI_TAB_MISSIONS")
missionsTab:add_button("Click me", function ()
log.info("You clicked!")
end)
```
For a complete list of available gui functions, please refer to the tab class documentation and the gui table documentation.
"""
)
f.write(f"## Tab Count: {len(tabs_enum)}\n\n")
# Minus the first, because it's the `NONE` tab, minus the last one because it's for runtime defined tabs.
for i in range(1, len(tabs_enum) - 1):
f.write("### `GUI_TAB_" + tabs_enum[i] + "`\n")
f.close()
infraction_file_name = f"../lua/infraction.md"
if os.path.exists(infraction_file_name):
os.remove(infraction_file_name)
f = open(infraction_file_name, "a")
f.write(
"""# Infraction
All the infraction from the menu are listed below, used as parameter for adding an infraction to a given player, for flagging them as modder.
**Example Usage:**
```lua
network.flag_player_as_modder(player_index, infraction.CUSTOM_REASON, "My custom reason on why the player is flagged as a modder")
```
"""
)
f.write(f"## Infraction Count: {len(infraction_enum)}\n\n")
for i in range(0, len(infraction_enum)):
f.write("### `" + infraction_enum[i] + "`\n")
f.close()
try:
os.makedirs("./classes/")
except:
pass
for class_name, class_ in classes.items():
file_name = f"./classes/{class_name}.md"
if os.path.exists(file_name):
os.remove(file_name)
f = open(file_name, "ba")
f.write(bytes(str(class_), "UTF8"))
f.close()
commands_file_name = f"./commands.md"
if os.path.exists(commands_file_name):
os.remove(commands_file_name)
f = open(commands_file_name, "a")
f.write(
"""# Commands
All the current commands from the menu are listed below.
**Example Usage through Lua:**
```lua
command.call("spawn", {joaat("adder")})
command.call_player(somePlayerIndex, "spawn", {joaat("adder")})
```
For a complete list of available command functions, please refer to the command table documentation.
"""
)
commands = []
with open("./commands_dump.txt", "r") as file:
for line in file:
cmd = line.split("|", 1)[1].strip().split("|")
commands.append(cmd)
f.write(f"## Command Count: {len(commands)}\n\n")
for cmd in commands:
name = cmd[0]
label = cmd[1]
desc = cmd[2]
arg_count = cmd[3]
f.write(f"### {name}\n")
f.write(f"{desc}\n")
f.write(f"Arg Count: {arg_count}\n")
f.write("\n")
f.close()

123
scripts/generate_natives.py Normal file
View File

@ -0,0 +1,123 @@
import json
crossmap = {}
natives = {}
current_idx = 0
crossmap_hash_list = []
class Arg:
def __init__(self, name: str, type: str):
self.name = name
self.type = type#.replace("BOOL", "bool")# .replace("Any*", "void*")
def __str__(self) -> str:
return str(self.type) + " " + str(self.name)
class NativeFunc:
def __init__(self, namespace: str, name: str, hash: int, args: list[dict], return_type: str):
self.namespace = namespace
self.name = name
self.hash = hash
self.args: list[Arg] = []
self.return_type = return_type#.replace("BOOL", "bool")# .replace("Any*", "void*")
self.native_index = -1
self.fix_vectors = "false"
for arg in args:
self.args.append(Arg(arg["name"], arg["type"]))
if arg["type"] == "Vector3*":
self.fix_vectors = "true"
def get_native_def_str(self) -> str:
assert self.native_index != -1
param_decl = ""
param_pass = ""
if len(self.args) > 0:
for arg in self.args:
param_decl += str(arg) + ", "
param_pass += arg.name + ", "
param_decl = param_decl[:-2]
param_pass = param_pass[:-2]
return f"FORCEINLINE constexpr {self.return_type} {self.name}({param_decl}) {{ return big::native_invoker::invoke<{self.native_index}, {self.fix_vectors}, {self.return_type}>({param_pass}); }}"
class CrossmapEntry:
def __init__(self, translated_hash: int):
self.hash = translated_hash
self.native_index = -1
def load_crossmap_data():
global crossmap
data = open("crossmap.txt").readlines()
for item in data:
translation = item.split(",")
crossmap[int(translation[0], 16)] = CrossmapEntry(int(translation[1], 16))
def load_natives_data():
global natives
data = json.load(open("natives.json"))
for ns, natives_list in data.items():
natives[ns] = []
for hash_str, native_data in natives_list.items():
natives[ns].append(NativeFunc(ns, native_data["name"], int(hash_str, 16), native_data["params"], native_data["return_type"]))
def allocate_indices():
global current_idx, crossmap_hash_list
for _, n in natives.items():
for native in n:
hash = native.hash
if hash in crossmap:
crossmap[hash].native_index = current_idx
native.native_index = current_idx
crossmap_hash_list.append(crossmap[hash].hash)
current_idx += 1
def write_crossmap_header():
open("crossmap.hpp", "w+").write(f"""#pragma once
#include <script/scrNativeHandler.hpp>
namespace big
{{
constexpr std::array<rage::scrNativeHash, {len(crossmap_hash_list)}> g_crossmap = {{{",".join([f"0x{x:X}" for x in crossmap_hash_list])}}};
}}
""")
def write_natives_header():
natives_buf = ""
natives_index_buf = ""
for ns, nvs in natives.items():
natives_buf += f"namespace {ns}\n{{\n"
for nat_data in nvs:
if nat_data.native_index == -1:
continue
natives_buf += f"\t{nat_data.get_native_def_str()}\n"
natives_index_buf += f"\t{nat_data.name} = {nat_data.native_index},\n"
natives_buf += "}\n\n"
natives_buf = natives_buf[:-2]
open("../natives.hpp", "w+").write(f"""#pragma once
#include "invoker/invoker.hpp"
// clang-format off
enum class NativeIndex
{{
{natives_index_buf}}};
{natives_buf}
// clang-format on
""")
if __name__ == "__main__":
load_crossmap_data()
load_natives_data()
allocate_indices()
write_crossmap_header()
write_natives_header()

View File

@ -1,38 +0,0 @@
find_package(Git)
if(Git_FOUND)
message("Git found: ${GIT_EXECUTABLE}")
# the commit's SHA1, and whether the building workspace was dirty or not
execute_process(COMMAND
"${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=40 --dirty
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_SHA1
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the date of the commit
execute_process(COMMAND
"${GIT_EXECUTABLE}" log -1 --format=%ad --date=local
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DATE
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# the subject of the commit
execute_process(COMMAND
"${GIT_EXECUTABLE}" log -1 --format=%s
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_COMMIT_SUBJECT
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# Commit messages may have quotes in them, which can affect the const char* variable.
string(REPLACE "\"" "\\\"" GIT_COMMIT_SUBJECT "${GIT_COMMIT_SUBJECT}")
# branch name
execute_process(COMMAND
"${GIT_EXECUTABLE}" branch --show-current
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_BRANCH
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
# generate version.cpp
configure_file("${SRC_DIR}/version.cpp.in" "${SRC_DIR}/version.cpp" @ONLY)
endif()

View File

@ -1,14 +0,0 @@
include(FetchContent)
FetchContent_Declare(
gtav_classes
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
GIT_TAG e8d8bdd2b3152253f6e1fde48720e4caeab19f83
GIT_PROGRESS TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
)
message("GTAV-Classes")
if(NOT gtav_classes_POPULATED)
FetchContent_Populate(gtav_classes)
endif()

View File

@ -1,28 +0,0 @@
include(FetchContent)
FetchContent_Declare(
imgui
GIT_REPOSITORY https://github.com/ocornut/imgui.git
GIT_TAG v1.90
GIT_PROGRESS TRUE
)
message("ImGui")
FetchContent_GetProperties(imgui)
if(NOT imgui_POPULATED)
FetchContent_Populate(imgui)
file(GLOB SRC_IMGUI
"${imgui_SOURCE_DIR}/*.cpp"
"${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp"
"${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp"
"${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.cpp"
)
add_library(imgui STATIC ${SRC_IMGUI} )
source_group(TREE ${imgui_SOURCE_DIR} PREFIX "imgui" FILES ${SRC_IMGUI})
target_include_directories(imgui PRIVATE
"${imgui_SOURCE_DIR}"
"${imgui_SOURCE_DIR}/backends"
"${imgui_SOURCE_DIR}/misc/cpp"
)
endif()
set_property(TARGET imgui PROPERTY CXX_STANDARD 23)

View File

@ -1,12 +0,0 @@
include(FetchContent)
set(JSON_MultipleHeaders OFF)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent.git
GIT_TAG 67e6070f9d9a44b4dec79ebe6b591f39d2285593
GIT_PROGRESS TRUE
)
message("json")
FetchContent_MakeAvailable(json)

View File

@ -1,12 +0,0 @@
include(FetchContent)
message("Lua")
FetchContent_Declare(
Lua
GIT_REPOSITORY https://github.com/walterschell/Lua.git
GIT_TAG a2e0125df529894f5e25d7d477b2df4e37690e0f
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(Lua)
set_property(TARGET lua_static PROPERTY CXX_STANDARD 23)

View File

@ -1,11 +0,0 @@
include(FetchContent)
FetchContent_Declare(
minhook
GIT_REPOSITORY https://github.com/YimMenu/minhook.git
GIT_TAG 902ab63e0a771547961e132ccc0700d62e2a1423
GIT_PROGRESS TRUE
)
message("MinHook")
FetchContent_MakeAvailable(minhook)
set_property(TARGET minhook PROPERTY CXX_STANDARD 23)

119453
scripts/natives.json Normal file

File diff suppressed because one or more lines are too long

330
scripts/natives_gen.py Normal file
View File

@ -0,0 +1,330 @@
import os
natives_hpp = open("../../natives.hpp", "r")
cpp_print_buf = ""
hpp_print_buf = ""
hpp_lua_native_wrappers_print_buf = ""
def print_cpp(text):
global cpp_print_buf
cpp_print_buf += text + "\n"
def print_hpp(text):
global hpp_print_buf
hpp_print_buf += text + "\n"
class Arg:
def __init__(self, name, type_):
self.name = name
self.type_ = type_.replace("BOOL", "bool")
self.is_pointer_arg = "*" in type_ and "const char" not in type_
self.type_no_star = self.type_.replace("*", "")
def __str__(self) -> str:
return str(self.type_) + " " + str(self.name)
class NativeFunc:
def __init__(self, namespace, lua_name, cpp_name, args, return_type):
self.namespace = namespace
self.lua_name = lua_name
self.cpp_name = cpp_name
self.args = args
self.return_type = return_type.replace("BOOL", "bool")
self.out_params = []
if self.return_type != "void":
retvalArg = Arg("retval", self.return_type)
# Any* case: this is incorrect but
# we'd need a custom lua usertype and write code for it inside the native function wrappers,
# it also only affect some of the DATAFILE natives.
retvalArg.is_pointer_arg = False
self.out_params.append(retvalArg)
for arg in self.args:
if arg.is_pointer_arg:
self.out_params.append(arg)
def __str__(self) -> str:
s = ""
returning_multiple_values = False
tuple_type = ""
fixed_return = self.return_type
if len(self.out_params) > 1:
fixed_return = "std::tuple<"
for out_param in self.out_params:
if out_param.is_pointer_arg:
fixed_return += out_param.type_no_star + ", "
else:
fixed_return += out_param.type_ + ", "
fixed_return = fixed_return[:-2] + ">"
returning_multiple_values = True
tuple_type = fixed_return
elif len(self.out_params) == 1:
if self.out_params[0].is_pointer_arg:
fixed_return = self.out_params[0].type_no_star
else:
fixed_return = self.out_params[0].type_
fixed_params = ""
if len(self.args) > 0:
for arg in self.args:
if not arg.is_pointer_arg:
fixed_params += arg.type_ + " " + arg.name + ", "
else:
fixed_params += arg.type_no_star + " " + arg.name + ", "
fixed_params = fixed_params[:-2]
s += (
fixed_return
+ " "
+ "LUA_NATIVE_"
+ self.namespace
+ "_"
+ self.lua_name
+ "("
+ fixed_params
+ ")"
)
s += "\n"
s += "\t{\n"
if self.cpp_name == "ADD_OWNED_EXPLOSION":
s+= "\t\tbig::explosion_anti_cheat_bypass::apply();\n\n"
call_native = "\t\t"
if len(self.out_params) > 0:
if returning_multiple_values:
tuple_declaration = tuple_type + " return_values;"
s += "\t\t" + tuple_declaration + "\n"
if self.return_type != "void":
call_native += "std::get<0>(return_values) = "
if self.return_type == "bool":
call_native += "(bool)"
elif self.return_type != "void":
call_native += "auto retval = "
if self.return_type == "bool":
call_native += "(bool)"
call_native += self.namespace + "::" + self.cpp_name + "("
else:
call_native += self.namespace + "::" + self.cpp_name + "("
if len(self.args) > 0:
for arg in self.args:
if arg.is_pointer_arg:
if arg.type_ == "bool*":
call_native += "(BOOL*)"
call_native += "&"
call_native += arg.name + ", "
call_native = call_native[:-2]
call_native += ");"
s += call_native
if self.cpp_name == "ADD_OWNED_EXPLOSION":
s+= "\n\n\t\tbig::explosion_anti_cheat_bypass::restore();"
if returning_multiple_values:
assign_return_values = "\n"
if self.return_type != "void":
i = 1
else:
i = 0
for arg in self.args:
if arg.is_pointer_arg:
assign_return_values += (
"\t\tstd::get<"
+ str(i)
+ ">(return_values) = "
+ arg.name
+ ";\n"
)
i += 1
s += assign_return_values
return_statement = ""
if len(self.out_params) > 0:
if returning_multiple_values:
return_statement = "return return_values;"
elif self.return_type != "void":
return_statement = "return retval;"
else:
for arg in self.args:
if arg.is_pointer_arg:
return_statement = "return " + arg.name + ";"
s += "\n\t\t" + return_statement
s += "\n\t}"
return s
def get_natives_func_from_natives_hpp_file(natives_hpp):
functions_per_namespaces = {}
current_namespace = ""
start_parsing = False
for line in natives_hpp.readlines():
if "namespace SYSTEM" not in line and not start_parsing:
continue
else:
start_parsing = True
if not start_parsing:
continue
if "namespace " in line:
current_namespace = line.replace("namespace ", "").strip()
functions_per_namespaces[current_namespace] = []
elif "NATIVE_DECL" in line:
words = line.split()
# remove NATIVE_DECL from the words array
words.pop(0)
func_name = ""
for word in words:
if "(" in word:
if func_name == "":
func_name = word.split("(")[0]
continue
# Sol somehow choke on this, terrible software
if func_name == "DRAW_TEXTURED_POLY_WITH_THREE_COLOURS":
continue
args = []
args_start = line.split("(")[1]
if args_start[0] == ")":
# no args
pass
else:
args_str = args_start.split(")")[0]
i = 0
for arg in args_str.split(","):
arg_type = arg[: arg.rfind(" ")].strip()
arg_name = arg[arg.rfind(" ") :].strip()
args.append(Arg(arg_name, arg_type))
i += 1
return_type = (
line[: line.find(func_name)].replace("NATIVE_DECL", "").strip()
)
lua_name = func_name
if lua_name.startswith('_'):
lua_name = lua_name.removeprefix("_")
lua_name = lua_name + "_"
native_func = NativeFunc(current_namespace, lua_name, func_name, args, return_type)
functions_per_namespaces[current_namespace].append(native_func)
return functions_per_namespaces
functions_per_namespaces = get_natives_func_from_natives_hpp_file(natives_hpp)
def generate_native_binding_cpp_and_hpp_files(functions_per_namespaces):
generated_function_name = "void init_native_binding(sol::state& L)"
print_hpp("#pragma once")
# print_hpp('#include "lua/sol.hpp"')
print_hpp("")
print_hpp("namespace lua::native")
print_hpp("{")
print_hpp("\t" + generated_function_name + ";")
print_hpp("")
for namespace_name, native_funcs in functions_per_namespaces.items():
print_hpp("\t" + "void init_native_binding_" + namespace_name + "(sol::state& L);")
print_hpp("}")
print_cpp('#include "lua_native_binding.hpp"')
print_cpp("")
print_cpp("namespace lua::native")
print_cpp("{")
i = 0
for namespace_name, native_funcs in functions_per_namespaces.items():
file_name_cpp = "lua_native_binding_" + namespace_name + ".cpp"
if os.path.exists(file_name_cpp):
os.remove(file_name_cpp)
f = open(file_name_cpp, "a")
file_buffer = ""
file_buffer += '#include "lua_native_binding.hpp"\n'
file_buffer += '#include "natives.hpp"\n'
if namespace_name == "FIRE":
file_buffer += '#include "util/explosion_anti_cheat_bypass.hpp"\n'
file_buffer += "\n"
file_buffer += "namespace lua::native\n"
file_buffer += "{\n"
for native_func in native_funcs:
file_buffer += "\tstatic " + str(native_func) + "\n\n"
file_buffer += "\t" + "void init_native_binding_" + namespace_name + "(sol::state& L)\n"
file_buffer += "\t{\n"
file_buffer += "\t\tauto " + namespace_name + ' = L["' + namespace_name + '"].get_or_create<sol::table>();\n'
for native_func in native_funcs:
i += 1
file_buffer += "\t\t"+ namespace_name+ '.set_function("'+ native_func.lua_name+ '", '+ "LUA_NATIVE_"+ native_func.namespace+ "_"+ native_func.lua_name+ ");\n"
file_buffer+= "\t}\n"
file_buffer+= "}\n"
f.write(file_buffer)
f.close()
print_cpp("\t" + generated_function_name)
print_cpp("\t{")
for namespace_name, native_funcs in functions_per_namespaces.items():
# call each binding functions inside generated_function_name
print_cpp("\t\t" + "init_native_binding_" + namespace_name + "(L);")
print_cpp("\t}")
print_cpp("}")
print(f"Wrote binding for {i} native functions")
generate_native_binding_cpp_and_hpp_files(functions_per_namespaces)
def write_cpp_code(cpp_print_buf):
file_name = "lua_native_binding.cpp"
if os.path.exists(file_name):
os.remove(file_name)
f = open(file_name, "a")
f.write(cpp_print_buf)
f.close()
def write_hpp_code(hpp_print_buf):
file_name = "lua_native_binding.hpp"
if os.path.exists(file_name):
os.remove(file_name)
f = open(file_name, "a")
f.write(hpp_print_buf)
f.close()
write_cpp_code(cpp_print_buf)
write_hpp_code(hpp_print_buf)

View File

@ -1,11 +0,0 @@
include(FetchContent)
FetchContent_Declare(
pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
GIT_TAG a0e064336317c9347a91224112af9933598714e9
GIT_PROGRESS TRUE
)
message("pugixml")
FetchContent_MakeAvailable(pugixml)
set_property(TARGET pugixml PROPERTY CXX_STANDARD 23)