1
0
forked from redo/BlockLua

Compare commits

..

5 Commits

33 changed files with 2124 additions and 2543 deletions

View File

@@ -1,14 +0,0 @@
BasedOnStyle: LLVM
SortIncludes: CaseSensitive
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '<Windows\.h>'
Priority: 1
- Regex: '<Psapi\.h>'
Priority: 2
- Regex: '"BlHooks\\.hpp"'
Priority: 3
- Regex: '"BlFuncs\\.hpp"'
Priority: 4
- Regex: ".*"
Priority: 5

View File

@@ -1,9 +0,0 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

4
.gitignore vendored
View File

@@ -1,2 +1,2 @@
build/ .*
.cache/ !.gitignore

11
.vscode/settings.json vendored
View File

@@ -1,11 +0,0 @@
{
"Lua.diagnostics.globals": [
"_bllua_ts",
"_bllua_requiresecure",
"_bllua_on_unload"
],
"Lua.runtime.version": "Lua 5.1",
"Lua.diagnostics.disable": ["lowercase-global", "undefined-global"],
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
"C_Cpp.default.compilerPath": "C:/msys64/mingw32/bin/g++.exe"
}

Binary file not shown.

View File

@@ -1,56 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(BlockLua CXX)
# Export compile_commands.json for VSCode IntelliSense
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Output directories to mirror compile.bat's build folder
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/build)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
# Global compile options to mirror compile.bat
add_compile_options(
-Wall
-Werror
-m32
-static-libgcc
-static-libstdc++
)
# Include paths
include_directories(
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/inc/tsfuncs
${CMAKE_SOURCE_DIR}/inc/lua
)
# Link directories (for -L.) and libraries from compile.bat
link_directories(
${CMAKE_SOURCE_DIR}
)
# Safe DLL
add_library(BlockLua SHARED src/bllua4.cpp)
# Ensure output name matches compile.bat
set_target_properties(BlockLua PROPERTIES OUTPUT_NAME "BlockLua")
# Linker flags and libraries
if(MSVC)
# Not expected with mingw, but keep placeholder
else()
target_link_libraries(BlockLua PRIVATE psapi lua5.1)
endif()
# Unsafe DLL (with BLLUA_UNSAFE definition)
add_library(BlockLuaUnsafe SHARED src/bllua4.cpp)
set_target_properties(BlockLuaUnsafe PROPERTIES OUTPUT_NAME "BlockLua-Unsafe")
target_compile_definitions(BlockLuaUnsafe PRIVATE BLLUA_UNSAFE)
if(MSVC)
# Not expected with mingw, but keep placeholder
else()
target_link_libraries(BlockLuaUnsafe PRIVATE psapi lua5.1)
endif()

View File

@@ -1,13 +0,0 @@
@echo off
cd /d %~dp0
set "PATH=C:\msys64\mingw32\bin;%PATH%"
if not exist build mkdir build
set buildargs=-Wall -Werror -m32 -shared -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1 -static-libgcc -static-libstdc++
echo on
g++ src/bllua4.cpp %buildargs% -o build\BlockLua.dll
g++ -DBLLUA_UNSAFE src/bllua4.cpp %buildargs% -o build\BlockLua-Unsafe.dll
@echo off

View File

@@ -1,52 +0,0 @@
## Compiling on Windows with MSYS2 (32-bit)
Follow these steps to build `BlockLua.dll` for 32-bit Windows using MSYS2's MinGW-w64 i686 toolchain.
### 1) Install MSYS2
- Download and install MSYS2 from `https://www.msys2.org/`.
- After installing, open the "MSYS2 MSYS" terminal once and update the package database if prompted.
### 2) Install required packages (i686 / 32-bit)
Run this in the "MSYS2 MSYS" or "MSYS2 MinGW 32-bit" terminal:
```bash
pacman -Sy --needed mingw-w64-i686-toolchain mingw-w64-i686-binutils mingw-w64-i686-lua51
```
What these packages are for:
- mingw-w64-i686-toolchain: 32-bit C/C++ compiler suite (g++, libstdc++, runtime libs) to build Windows binaries.
- mingw-w64-i686-binutils: Linker and binary utilities (ld, as, objdump) used by the compiler and for optional inspection.
- mingw-w64-i686-lua51: Lua 5.1 development files for 32-bit (import library). We use its import library to link; at runtime you can use the provided `lua5.1.dll`.
### 3) Build (PowerShell, recommended)
- Open PowerShell in the repo root.
- Run the script:
```powershell
compile.bat
```
What the script does:
- Temporarily prepends `C:\\msys64\\mingw32\\bin` to PATH so the 32-bit toolchain is used.
- Compiles the project with `-m32` and links against `lua5.1`.
- Produces `build\BlockLua.dll` and `build\BlockLua-Unsafe.dll`.
### 4) Optional: Verify 32-bit output
If you installed binutils, you can check the architecture:
```powershell
objdump -f build\BlockLua.dll | Select-String i386
```
You should see `architecture: i386` in the output.
### Notes
- Ensure you installed the i686 (32-bit) variants of the packages; x86_64 packages wont work for a 32-bit build.
- If the linker cannot find `-llua5.1`, confirm `mingw-w64-i686-lua51` is installed and you are using the `mingw32` toolchain (not `x86_64`).

View File

@@ -4,14 +4,17 @@
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
#ifndef lauxlib_h #ifndef lauxlib_h
#define lauxlib_h #define lauxlib_h
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include "lua.h" #include "lua.h"
/* extra error code for `luaL_load' */ /* extra error code for `luaL_load' */
#define LUA_ERRFILE (LUA_ERRERR+1) #define LUA_ERRFILE (LUA_ERRERR+1)
@@ -66,6 +69,7 @@ LUALIB_API int(luaL_loadstring)(lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r); const char *r);
@@ -87,6 +91,7 @@ LUALIB_API void(luaL_pushmodule)(lua_State *L, const char *modname,
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
/* /*
** =============================================================== ** ===============================================================
** some useful macros ** some useful macros
@@ -125,6 +130,8 @@ LUALIB_API void(luaL_setmetatable)(lua_State *L, const char *tname);
** ======================================================= ** =======================================================
*/ */
typedef struct luaL_Buffer { typedef struct luaL_Buffer {
char *p; /* current position in buffer */ char *p; /* current position in buffer */
int lvl; /* number of strings in the stack (level) */ int lvl; /* number of strings in the stack (level) */
@@ -148,6 +155,7 @@ LUALIB_API void(luaL_addstring)(luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */ /* }====================================================== */
#endif #endif

View File

@@ -5,26 +5,31 @@
** See Copyright Notice at the end of this file ** See Copyright Notice at the end of this file
*/ */
#ifndef lua_h #ifndef lua_h
#define lua_h #define lua_h
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include "luaconf.h" #include "luaconf.h"
#define LUA_VERSION "Lua 5.1" #define LUA_VERSION "Lua 5.1"
#define LUA_RELEASE "Lua 5.1.4" #define LUA_RELEASE "Lua 5.1.4"
#define LUA_VERSION_NUM 501 #define LUA_VERSION_NUM 501
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" #define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
/* mark for precompiled code (`<esc>Lua') */ /* mark for precompiled code (`<esc>Lua') */
#define LUA_SIGNATURE "\033Lua" #define LUA_SIGNATURE "\033Lua"
/* option for multiple returns in `lua_pcall' and `lua_call' */ /* option for multiple returns in `lua_pcall' and `lua_call' */
#define LUA_MULTRET (-1) #define LUA_MULTRET (-1)
/* /*
** pseudo-indices ** pseudo-indices
*/ */
@@ -33,6 +38,7 @@
#define LUA_GLOBALSINDEX (-10002) #define LUA_GLOBALSINDEX (-10002)
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) #define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
/* thread status */ /* thread status */
#define LUA_OK 0 #define LUA_OK 0
#define LUA_YIELD 1 #define LUA_YIELD 1
@@ -41,10 +47,12 @@
#define LUA_ERRMEM 4 #define LUA_ERRMEM 4
#define LUA_ERRERR 5 #define LUA_ERRERR 5
typedef struct lua_State lua_State; typedef struct lua_State lua_State;
typedef int (*lua_CFunction) (lua_State *L); typedef int (*lua_CFunction) (lua_State *L);
/* /*
** functions that read/write blocks when loading/dumping Lua chunks ** functions that read/write blocks when loading/dumping Lua chunks
*/ */
@@ -52,11 +60,13 @@ typedef const char *(*lua_Reader)(lua_State *L, void *ud, size_t *sz);
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
/* /*
** prototype for memory-allocation functions ** prototype for memory-allocation functions
*/ */
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/* /*
** basic types ** basic types
*/ */
@@ -72,9 +82,12 @@ typedef void *(*lua_Alloc)(void *ud, void *ptr, size_t osize, size_t nsize);
#define LUA_TUSERDATA 7 #define LUA_TUSERDATA 7
#define LUA_TTHREAD 8 #define LUA_TTHREAD 8
/* minimum Lua stack available to a C function */ /* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20 #define LUA_MINSTACK 20
/* /*
** generic extra include file ** generic extra include file
*/ */
@@ -82,12 +95,16 @@ typedef void *(*lua_Alloc)(void *ud, void *ptr, size_t osize, size_t nsize);
#include LUA_USER_H #include LUA_USER_H
#endif #endif
/* type of numbers in Lua */ /* type of numbers in Lua */
typedef LUA_NUMBER lua_Number; typedef LUA_NUMBER lua_Number;
/* type for integer functions */ /* type for integer functions */
typedef LUA_INTEGER lua_Integer; typedef LUA_INTEGER lua_Integer;
/* /*
** state manipulation ** state manipulation
*/ */
@@ -97,6 +114,7 @@ LUA_API lua_State *(lua_newthread)(lua_State * L);
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
/* /*
** basic stack manipulation ** basic stack manipulation
*/ */
@@ -110,6 +128,7 @@ LUA_API int(lua_checkstack)(lua_State *L, int sz);
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
/* /*
** access functions (stack -> C) ** access functions (stack -> C)
*/ */
@@ -135,6 +154,7 @@ LUA_API void *(lua_touserdata)(lua_State * L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx); LUA_API const void *(lua_topointer) (lua_State *L, int idx);
/* /*
** push functions (C -> stack) ** push functions (C -> stack)
*/ */
@@ -151,6 +171,7 @@ LUA_API void(lua_pushboolean)(lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L); LUA_API int (lua_pushthread) (lua_State *L);
/* /*
** get functions (Lua -> stack) ** get functions (Lua -> stack)
*/ */
@@ -163,6 +184,7 @@ LUA_API void *(lua_newuserdata)(lua_State * L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void (lua_getfenv) (lua_State *L, int idx); LUA_API void (lua_getfenv) (lua_State *L, int idx);
/* /*
** set functions (stack -> Lua) ** set functions (stack -> Lua)
*/ */
@@ -173,6 +195,7 @@ LUA_API void(lua_rawseti)(lua_State *L, int idx, int n);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex); LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int (lua_setfenv) (lua_State *L, int idx); LUA_API int (lua_setfenv) (lua_State *L, int idx);
/* /*
** `load' and `call' functions (load and run Lua code) ** `load' and `call' functions (load and run Lua code)
*/ */
@@ -184,6 +207,7 @@ LUA_API int(lua_load)(lua_State *L, lua_Reader reader, void *dt,
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
/* /*
** coroutine functions ** coroutine functions
*/ */
@@ -207,6 +231,7 @@ LUA_API int(lua_status)(lua_State *L);
LUA_API int (lua_gc) (lua_State *L, int what, int data); LUA_API int (lua_gc) (lua_State *L, int what, int data);
/* /*
** miscellaneous functions ** miscellaneous functions
*/ */
@@ -220,6 +245,8 @@ LUA_API void(lua_concat)(lua_State *L, int n);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
/* /*
** =============================================================== ** ===============================================================
** some useful macros ** some useful macros
@@ -253,6 +280,8 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud);
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
/* /*
** compatibility macros and functions ** compatibility macros and functions
*/ */
@@ -266,15 +295,18 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud);
#define lua_Chunkreader lua_Reader #define lua_Chunkreader lua_Reader
#define lua_Chunkwriter lua_Writer #define lua_Chunkwriter lua_Writer
/* hack */ /* hack */
LUA_API void lua_setlevel (lua_State *from, lua_State *to); LUA_API void lua_setlevel (lua_State *from, lua_State *to);
/* /*
** {====================================================================== ** {======================================================================
** Debug API ** Debug API
** ======================================================================= ** =======================================================================
*/ */
/* /*
** Event codes ** Event codes
*/ */
@@ -284,6 +316,7 @@ LUA_API void lua_setlevel(lua_State *from, lua_State *to);
#define LUA_HOOKCOUNT 3 #define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILRET 4 #define LUA_HOOKTAILRET 4
/* /*
** Event masks ** Event masks
*/ */
@@ -294,9 +327,11 @@ LUA_API void lua_setlevel(lua_State *from, lua_State *to);
typedef struct lua_Debug lua_Debug; /* activation record */ typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debuger in specific events */ /* Functions to be called by the debuger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
@@ -321,6 +356,7 @@ LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum);
/* From Lua 5.3. */ /* From Lua 5.3. */
LUA_API int lua_isyieldable (lua_State *L); LUA_API int lua_isyieldable (lua_State *L);
struct lua_Debug { struct lua_Debug {
int event; int event;
const char *name; /* (n) */ const char *name; /* (n) */
@@ -338,6 +374,7 @@ struct lua_Debug {
/* }====================================================================== */ /* }====================================================================== */
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. * Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
* *
@@ -361,4 +398,5 @@ struct lua_Debug {
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/ ******************************************************************************/
#endif #endif

View File

@@ -1,8 +1,9 @@
// C++ wrapper for LuaJIT header files. // C++ wrapper for LuaJIT header files.
extern "C" { extern "C" {
#include "lauxlib.h"
#include "lua.h" #include "lua.h"
#include "luajit.h" #include "lauxlib.h"
#include "lualib.h" #include "lualib.h"
#include "luajit.h"
} }

View File

@@ -9,7 +9,6 @@
#ifndef WINVER #ifndef WINVER
#define WINVER 0x0501 #define WINVER 0x0501
#endif #endif
#include <limits.h> #include <limits.h>
#include <stddef.h> #include <stddef.h>
@@ -21,8 +20,10 @@
*/ */
#define LUA_LDIR "!\\lua\\" #define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\" #define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT ".\\?.lua;" LUA_LDIR "?.lua;" LUA_LDIR "?\\init.lua;" #define LUA_PATH_DEFAULT \
#define LUA_CPATH_DEFAULT ".\\?.dll;" LUA_CDIR "?.dll;" LUA_CDIR "loadall.dll" ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else #else
/* /*
** Note to distribution maintainers: do NOT patch the following lines! ** Note to distribution maintainers: do NOT patch the following lines!
@@ -77,8 +78,8 @@
#define LUA_EXECDIR "!" #define LUA_EXECDIR "!"
#define LUA_IGMARK "-" #define LUA_IGMARK "-"
#define LUA_PATH_CONFIG \ #define LUA_PATH_CONFIG \
LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" LUA_EXECDIR \ LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \
"\n" LUA_IGMARK "\n" LUA_EXECDIR "\n" LUA_IGMARK "\n"
/* Quoting in error messages. */ /* Quoting in error messages. */
#define LUA_QL(x) "'" x "'" #define LUA_QL(x) "'" x "'"
@@ -143,16 +144,9 @@
#define lua_assert(x) assert(x) #define lua_assert(x) assert(x)
#endif #endif
#ifdef LUA_USE_APICHECK #ifdef LUA_USE_APICHECK
#define luai_apicheck(L, o) \ #define luai_apicheck(L, o) { (void)L; assert(o); }
{ \
(void)L; \
assert(o); \
}
#else #else
#define luai_apicheck(L, o) \ #define luai_apicheck(L, o) { (void)L; }
{ \
(void)L; \
}
#endif #endif
#endif #endif

View File

@@ -65,8 +65,8 @@ enum {
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode);
/* Low-overhead profiling API. */ /* Low-overhead profiling API. */
typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, int samples, typedef void (*luaJIT_profile_callback)(void *data, lua_State *L,
int vmstate); int samples, int vmstate);
LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
luaJIT_profile_callback cb, void *data); luaJIT_profile_callback cb, void *data);
LUA_API void luaJIT_profile_stop(lua_State *L); LUA_API void luaJIT_profile_stop(lua_State *L);

View File

@@ -2,18 +2,15 @@
////////////////////////////////////////////////// //////////////////////////////////////////////////
// BlFuncs Version 1.0 // BlFuncs Version 1.0
// Includes
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "BlFuncs.hpp"
#include <Windows.h>
#include "BlHooks.hpp"
#include <cstdarg> // Includes
#include <cstring>
#include "BlHooks.hpp"
#include "BlFuncs.hpp"
#include <stdio.h> #include <stdio.h>
// Scanned structures // Scanned structures
ADDR tsf_mCacheSequence; ADDR tsf_mCacheSequence;
@@ -36,6 +33,7 @@ BlFunctionDefIntern(tsf_BlSimObject__getDataField);
BlFunctionDefIntern(tsf_BlSimObject__setDataField ); BlFunctionDefIntern(tsf_BlSimObject__setDataField );
BlFunctionDefIntern(tsf_BlCon__getReturnBuffer ); BlFunctionDefIntern(tsf_BlCon__getReturnBuffer );
// C->TS Args // C->TS Args
char* tsf_GetIntArg(signed int value) { char* tsf_GetIntArg(signed int value) {
@@ -58,6 +56,7 @@ char *tsf_GetThisArg(ADDR obj) {
return tsf_GetIntArg(*(signed int *)(obj + 32)); return tsf_GetIntArg(*(signed int *)(obj + 32));
} }
// Eval // Eval
const char* tsf_Eval(const char *code) { const char* tsf_Eval(const char *code) {
@@ -75,17 +74,16 @@ const char *tsf_Evalf(const char *fmt, ...) {
return tsf_Eval((const char*)code); return tsf_Eval((const char*)code);
} }
// Objects // Objects
ADDR tsf_FindObject(unsigned int id) { ADDR tsf_FindObject(unsigned int id) {
ADDR obj = *(ADDR*)(*(ADDR*)(tsf_gIdDictionary) + 4*(id & 0xFFF)); ADDR obj = *(ADDR*)(*(ADDR*)(tsf_gIdDictionary) + 4*(id & 0xFFF));
if (!obj) if(!obj) return 0;
return 0;
while(obj && *(unsigned int *)(obj + 32) != id) { while(obj && *(unsigned int *)(obj + 32) != id) {
obj = *(ADDR*)(obj + 16); obj = *(ADDR*)(obj + 16);
if (!obj) if(!obj) return 0;
return 0;
} }
return obj; return obj;
@@ -114,10 +112,10 @@ ADDR tsf_LookupNamespace(const char *ns) {
return tsf_LookupNamespace(ns, nullptr); return tsf_LookupNamespace(ns, nullptr);
} }
// Object Fields // Object Fields
const char *tsf_GetDataField(ADDR simObject, const char *slotName, const char* tsf_GetDataField(ADDR simObject, const char* slotName, const char* array) {
const char *array) {
const char *ste_slotName; const char *ste_slotName;
if(slotName) { if(slotName) {
ste_slotName = tsf_BlStringTable__insert(slotName, 0); ste_slotName = tsf_BlStringTable__insert(slotName, 0);
@@ -128,8 +126,7 @@ const char *tsf_GetDataField(ADDR simObject, const char *slotName,
return tsf_BlSimObject__getDataField(simObject, ste_slotName, array); return tsf_BlSimObject__getDataField(simObject, ste_slotName, array);
} }
void tsf_SetDataField(ADDR simObject, const char *slotName, const char *array, void tsf_SetDataField(ADDR simObject, const char* slotName, const char* array, const char* value) {
const char *value) {
const char* ste_slotName; const char* ste_slotName;
if(slotName) { if(slotName) {
ste_slotName = tsf_BlStringTable__insert(slotName, 0); ste_slotName = tsf_BlStringTable__insert(slotName, 0);
@@ -140,6 +137,7 @@ void tsf_SetDataField(ADDR simObject, const char *slotName, const char *array,
tsf_BlSimObject__setDataField(simObject, ste_slotName, array, value); tsf_BlSimObject__setDataField(simObject, ste_slotName, array, value);
} }
// TS Global Variables // TS Global Variables
const char *tsf_GetVar(const char* name) { const char *tsf_GetVar(const char* name) {
@@ -147,8 +145,7 @@ const char *tsf_GetVar(const char *name) {
} }
void tsf_AddVarInternal(const char* name, signed int varType, void* data) { void tsf_AddVarInternal(const char* name, signed int varType, void* data) {
tsf_BlDictionary__addVariable((ADDR *)tsf_gEvalState_globalVars, name, tsf_BlDictionary__addVariable((ADDR *)tsf_gEvalState_globalVars, name, varType, data);
varType, data);
} }
void tsf_AddVar(const char* name, const char** data) { void tsf_AddVar(const char* name, const char** data) {
@@ -164,12 +161,10 @@ void tsf_AddVar(const char *name, bool *data) {
tsf_AddVarInternal(name, 6, data); tsf_AddVarInternal(name, 6, data);
} }
// TS->C Functions // TS->C Functions
ADDR tsf_AddConsoleFuncInternal(const char *pname, const char *cname, ADDR tsf_AddConsoleFuncInternal(const char* pname, const char* cname, const char* fname, signed int cbtype, const char* usage, signed int mina, signed int maxa) {
const char *fname, signed int cbtype,
const char *usage, signed int mina,
signed int maxa) {
const char *ste_fname = tsf_BlStringTable__insert(fname, 0); const char *ste_fname = tsf_BlStringTable__insert(fname, 0);
ADDR ns = tsf_LookupNamespace(cname, pname); ADDR ns = tsf_LookupNamespace(cname, pname);
ADDR ent = tsf_BlNamespace__createLocalEntry(ns, ste_fname); ADDR ent = tsf_BlNamespace__createLocalEntry(ns, ste_fname);
@@ -185,92 +180,50 @@ ADDR tsf_AddConsoleFuncInternal(const char *pname, const char *cname,
return ent; return ent;
} }
void tsf_AddConsoleFunc(const char *pname, const char *cname, const char *fname, void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_StringCallback sc, const char* usage, signed int mina, signed int maxa) {
tsf_StringCallback sc, const char *usage, ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 1, usage, mina, maxa);
signed int mina, signed int maxa) {
ADDR ent =
tsf_AddConsoleFuncInternal(pname, cname, fname, 1, usage, mina, maxa);
*(tsf_StringCallback *)(ent + 40) = sc; *(tsf_StringCallback *)(ent + 40) = sc;
} }
void tsf_AddConsoleFunc(const char *pname, const char *cname, const char *fname, void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_IntCallback ic, const char* usage, signed int mina, signed int maxa) {
tsf_IntCallback ic, const char *usage, signed int mina, ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 2, usage, mina, maxa);
signed int maxa) {
ADDR ent =
tsf_AddConsoleFuncInternal(pname, cname, fname, 2, usage, mina, maxa);
*(tsf_IntCallback *)(ent + 40) = ic; *(tsf_IntCallback *)(ent + 40) = ic;
} }
void tsf_AddConsoleFunc(const char *pname, const char *cname, const char *fname, void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_FloatCallback fc, const char* usage, signed int mina, signed int maxa) {
tsf_FloatCallback fc, const char *usage, ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 3, usage, mina, maxa);
signed int mina, signed int maxa) {
ADDR ent =
tsf_AddConsoleFuncInternal(pname, cname, fname, 3, usage, mina, maxa);
*(tsf_FloatCallback *)(ent + 40) = fc; *(tsf_FloatCallback *)(ent + 40) = fc;
} }
void tsf_AddConsoleFunc(const char *pname, const char *cname, const char *fname, void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_VoidCallback vc, const char* usage, signed int mina, signed int maxa) {
tsf_VoidCallback vc, const char *usage, signed int mina, ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 4, usage, mina, maxa);
signed int maxa) {
ADDR ent =
tsf_AddConsoleFuncInternal(pname, cname, fname, 4, usage, mina, maxa);
*(tsf_VoidCallback *)(ent + 40) = vc; *(tsf_VoidCallback *)(ent + 40) = vc;
} }
void tsf_AddConsoleFunc(const char *pname, const char *cname, const char *fname, void tsf_AddConsoleFunc(const char* pname, const char* cname, const char* fname, tsf_BoolCallback bc, const char* usage, signed int mina, signed int maxa) {
tsf_BoolCallback bc, const char *usage, signed int mina, ADDR ent = tsf_AddConsoleFuncInternal(pname, cname, fname, 5, usage, mina, maxa);
signed int maxa) {
ADDR ent =
tsf_AddConsoleFuncInternal(pname, cname, fname, 5, usage, mina, maxa);
*(tsf_BoolCallback *)(ent + 40) = bc; *(tsf_BoolCallback *)(ent + 40) = bc;
} }
// Initialization // Initialization
bool tsf_InitInternal() { bool tsf_InitInternal() {
BlScanFunctionText(tsf_BlStringTable__insert , "83 EC 0C 80 3D ? ? ? ? ?" ); BlScanFunctionText(tsf_BlStringTable__insert , "83 EC 0C 80 3D ? ? ? ? ?" );
BlScanFunctionText( BlScanFunctionText(tsf_BlNamespace__find , "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 0C 53 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B DA 8B D1" );
tsf_BlNamespace__find, BlScanFunctionText(tsf_BlNamespace__createLocalEntry, "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 08 53 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 89 4D F0" );
"55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 0C 53 56 57 A1 ? ? ? ? " BlScanFunctionText(tsf_BlDataChunker__freeBlocks , "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 51 53 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B D9 8B 33" );
"33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B DA 8B D1"); BlScanFunctionText(tsf_BlCon__evaluate , "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B 75 10" );
BlScanFunctionText( BlScanFunctionText(tsf_BlCon__executef , "81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 55 56 8B B4 24 ? ? ? ? 33 C9" );
tsf_BlNamespace__createLocalEntry, BlScanFunctionText(tsf_BlCon__executefSimObj , "81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 56 8B B4 24 ? ? ? ? 33 C9" );
"55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 08 53 56 57 A1 ? ? ? ? " BlScanFunctionText(tsf_BlCon__getVariable , "53 56 8B F1 57 85 F6 0F 84 ? ? ? ?" );
"33 C5 50 8D 45 F4 64 A3 ? ? ? ? 89 4D F0");
BlScanFunctionText(tsf_BlDataChunker__freeBlocks,
"55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 51 53 56 57 "
"A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B D9 8B 33");
BlScanFunctionText(tsf_BlCon__evaluate,
"55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 56 57 A1 ? ? "
"? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? 8B 75 10");
BlScanFunctionText(tsf_BlCon__executef,
"81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 55 56 "
"8B B4 24 ? ? ? ? 33 C9");
BlScanFunctionText(tsf_BlCon__executefSimObj,
"81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 56 8B "
"B4 24 ? ? ? ? 33 C9");
BlScanFunctionText(tsf_BlCon__getVariable,
"53 56 8B F1 57 85 F6 0F 84 ? ? ? ?");
BlScanFunctionText(tsf_BlDictionary__addVariable , "8B 44 24 04 56 57 8B F9" ); BlScanFunctionText(tsf_BlDictionary__addVariable , "8B 44 24 04 56 57 8B F9" );
BlScanFunctionText(tsf_BlSim__findObject_name , "57 8B F9 8A 17" ); BlScanFunctionText(tsf_BlSim__findObject_name , "57 8B F9 8A 17" );
BlScanFunctionText(tsf_BlStringStack__getArgBuffer, BlScanFunctionText(tsf_BlStringStack__getArgBuffer , "55 8B EC 83 E4 F8 8B 0D ? ? ? ? A1 ? ? ? ? 56 57 8B 7D 08 8D 14 01 03 D7 3B 15 ? ? ? ? 72 2C 8B 0D" );
"55 8B EC 83 E4 F8 8B 0D ? ? ? ? A1 ? ? ? ? 56 57 8B 7D " BlScanFunctionText(tsf_BlSimObject__getDataField , "51 53 8B D9 55 56 8B 74 24 14" );
"08 8D 14 01 03 D7 3B 15 ? ? ? ? 72 2C 8B 0D"); BlScanFunctionText(tsf_BlSimObject__setDataField , "81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 8B 84 24 ? ? ? ? 53 8B D9 89 44 24 04" );
BlScanFunctionText(tsf_BlSimObject__getDataField,
"51 53 8B D9 55 56 8B 74 24 14");
BlScanFunctionText(tsf_BlSimObject__setDataField,
"81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 8B 84 24 "
"? ? ? ? 53 8B D9 89 44 24 04");
BlScanFunctionText(tsf_BlCon__getReturnBuffer , "81 F9 ? ? ? ? 76 2B" ); BlScanFunctionText(tsf_BlCon__getReturnBuffer , "81 F9 ? ? ? ? 76 2B" );
ADDR BlScanText(tsf_mCacheSequenceLoc, ADDR BlScanText (tsf_mCacheSequenceLoc , "FF 05 ? ? ? ? B9 ? ? ? ? 8B F8 E8 ? ? ? ? 8B 44 24 1C 89 47 18 8B 44 24 14" );
"FF 05 ? ? ? ? B9 ? ? ? ? 8B F8 E8 ? ? ? ? 8B 44 24 1C 89 47 " ADDR BlScanText (tsf_mCacheAllocatorLoc , "89 35 ? ? ? ? C7 06 ? ? ? ? A1 ? ? ? ? 68 ? ? ? ? C7 40 ? ? ? ? ? E8 ? ? ? ? 83 C4 04 8B 4D F4 64 89 0D ? ? ? ? 59 5E 8B E5 5D C3");
"18 8B 44 24 14"); ADDR BlScanText (tsf_gIdDictionaryLoc , "89 15 ? ? ? ? E8 ? ? ? ? 8B F0 89 75 F0" );
ADDR BlScanText( ADDR BlScanText (tsf_gEvalState_globalVarsLoc , "B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? 6A 0A 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ?" );
tsf_mCacheAllocatorLoc,
"89 35 ? ? ? ? C7 06 ? ? ? ? A1 ? ? ? ? 68 ? ? ? ? C7 40 ? ? ? ? ? E8 ? "
"? ? ? 83 C4 04 8B 4D F4 64 89 0D ? ? ? ? 59 5E 8B E5 5D C3");
ADDR BlScanText(tsf_gIdDictionaryLoc,
"89 15 ? ? ? ? E8 ? ? ? ? 8B F0 89 75 F0");
ADDR BlScanText(tsf_gEvalState_globalVarsLoc,
"B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? 6A 0A 68 ? ? ? ? B9 ? ? ? "
"? E8 ? ? ? ? E8 ? ? ? ?");
tsf_mCacheSequence = *(ADDR*)(tsf_mCacheSequenceLoc + 2); tsf_mCacheSequence = *(ADDR*)(tsf_mCacheSequenceLoc + 2);
tsf_mCacheAllocator = *(ADDR*)(tsf_mCacheAllocatorLoc + 2); tsf_mCacheAllocator = *(ADDR*)(tsf_mCacheAllocatorLoc + 2);
@@ -280,4 +233,6 @@ bool tsf_InitInternal() {
return true; return true;
} }
bool tsf_DeinitInternal() { return true; } bool tsf_DeinitInternal() {
return true;
}

View File

@@ -5,10 +5,10 @@
#ifndef _H_BLFUNCS #ifndef _H_BLFUNCS
#define _H_BLFUNCS #define _H_BLFUNCS
// Ensure BlHooks is available (include it if not already included) // Require BlHooks to be included before this header
#ifndef _H_BLHOOKS #ifndef _H_BLHOOKS
#include "BlHooks.hpp" #error "BlFuncs.hpp: You must include BlHooks.hpp first"
#endif #else
typedef const char * (*tsf_StringCallback)(ADDR, signed int, const char *[]); typedef const char * (*tsf_StringCallback)(ADDR, signed int, const char *[]);
typedef signed int (*tsf_IntCallback )(ADDR, signed int, const char *[]); typedef signed int (*tsf_IntCallback )(ADDR, signed int, const char *[]);
@@ -42,21 +42,12 @@ void tsf_AddVar(const char *, signed int *);
void tsf_AddVar(const char *, float *); void tsf_AddVar(const char *, float *);
void tsf_AddVar(const char *, bool *); void tsf_AddVar(const char *, bool *);
ADDR tsf_AddConsoleFuncInternal(const char *, const char *, const char *, ADDR tsf_AddConsoleFuncInternal(const char *, const char *, const char *, signed int, const char *, signed int, signed int);
signed int, const char *, signed int, void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_StringCallback, const char *, signed int, signed int);
signed int); void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_IntCallback, const char *, signed int, signed int);
void tsf_AddConsoleFunc(const char *, const char *, const char *, void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_FloatCallback, const char *, signed int, signed int);
tsf_StringCallback, const char *, signed int, void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_VoidCallback, const char *, signed int, signed int);
signed int); void tsf_AddConsoleFunc(const char *, const char *, const char *, tsf_BoolCallback, const char *, signed int, signed int);
void tsf_AddConsoleFunc(const char *, const char *, const char *,
tsf_IntCallback, const char *, signed int, signed int);
void tsf_AddConsoleFunc(const char *, const char *, const char *,
tsf_FloatCallback, const char *, signed int,
signed int);
void tsf_AddConsoleFunc(const char *, const char *, const char *,
tsf_VoidCallback, const char *, signed int, signed int);
void tsf_AddConsoleFunc(const char *, const char *, const char *,
tsf_BoolCallback, const char *, signed int, signed int);
bool tsf_InitInternal(); bool tsf_InitInternal();
@@ -65,32 +56,21 @@ extern ADDR tsf_mCacheAllocator;
extern ADDR tsf_gIdDictionary; extern ADDR tsf_gIdDictionary;
extern ADDR tsf_gEvalState_globalVars; extern ADDR tsf_gEvalState_globalVars;
BlFunctionDefExtern(const char *, __stdcall, tsf_BlStringTable__insert, BlFunctionDefExtern(const char *, __stdcall, tsf_BlStringTable__insert, const char *, bool);
const char *, bool); BlFunctionDefExtern(ADDR, __fastcall, tsf_BlNamespace__find, const char *, const char *);
BlFunctionDefExtern(ADDR, __fastcall, tsf_BlNamespace__find, const char *, BlFunctionDefExtern(ADDR, __thiscall, tsf_BlNamespace__createLocalEntry, ADDR, const char *);
const char *);
BlFunctionDefExtern(ADDR, __thiscall, tsf_BlNamespace__createLocalEntry, ADDR,
const char *);
BlFunctionDefExtern(void, __thiscall, tsf_BlDataChunker__freeBlocks, ADDR); BlFunctionDefExtern(void, __thiscall, tsf_BlDataChunker__freeBlocks, ADDR);
BlFunctionDefExtern(const char *, , tsf_BlCon__evaluate, ADDR, signed int, BlFunctionDefExtern(const char *, , tsf_BlCon__evaluate, ADDR, signed int, const char **);
const char **);
BlFunctionDefExtern(const char *, , tsf_BlCon__executef, signed int, ...); BlFunctionDefExtern(const char *, , tsf_BlCon__executef, signed int, ...);
BlFunctionDefExtern(const char *, , tsf_BlCon__executefSimObj, ADDR *, BlFunctionDefExtern(const char *, , tsf_BlCon__executefSimObj, ADDR *, signed int, ...);
signed int, ...); BlFunctionDefExtern(const char *, __thiscall, tsf_BlCon__getVariable, const char *);
BlFunctionDefExtern(const char *, __thiscall, tsf_BlCon__getVariable, BlFunctionDefExtern(void, __thiscall, tsf_BlDictionary__addVariable, ADDR *, const char *, signed int, void *);
const char *); BlFunctionDefExtern(ADDR *, __thiscall, tsf_BlSim__findObject_name, const char *);
BlFunctionDefExtern(void, __thiscall, tsf_BlDictionary__addVariable, ADDR *, BlFunctionDefExtern(char *, __stdcall, tsf_BlStringStack__getArgBuffer, unsigned int);
const char *, signed int, void *); BlFunctionDefExtern(const char *, __thiscall, tsf_BlSimObject__getDataField, ADDR, const char *, const char *);
BlFunctionDefExtern(ADDR *, __thiscall, tsf_BlSim__findObject_name, BlFunctionDefExtern(void, __thiscall, tsf_BlSimObject__setDataField, ADDR, const char *, const char *, const char *);
const char *); BlFunctionDefExtern(char *, __fastcall, tsf_BlCon__getReturnBuffer, unsigned int);
BlFunctionDefExtern(char *, __stdcall, tsf_BlStringStack__getArgBuffer,
unsigned int);
BlFunctionDefExtern(const char *, __thiscall, tsf_BlSimObject__getDataField,
ADDR, const char *, const char *);
BlFunctionDefExtern(void, __thiscall, tsf_BlSimObject__setDataField, ADDR,
const char *, const char *, const char *);
BlFunctionDefExtern(char *, __fastcall, tsf_BlCon__getReturnBuffer,
unsigned int);
// Function short names // Function short names
@@ -114,23 +94,16 @@ BlFunctionDefExtern(char *, __fastcall, tsf_BlCon__getReturnBuffer,
#define BlAddFunction tsf_AddConsoleFunc #define BlAddFunction tsf_AddConsoleFunc
#define __22ND_ARGUMENT(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, \ #define __22ND_ARGUMENT(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, ...) a22
a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, ...) \ #define __NUM_LIST(...) __22ND_ARGUMENT(dummy, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
a22 #define BlCall(...) \
#define __NUM_LIST(...) \ tsf_BlCon__executef(__NUM_LIST(__VA_ARGS__), __VA_ARGS__)
__22ND_ARGUMENT(dummy, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, \
11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define BlCall(...) tsf_BlCon__executef(__NUM_LIST(__VA_ARGS__), __VA_ARGS__)
#define BlCallObj(obj, ...) \ #define BlCallObj(obj, ...) \
tsf_BlCon__executefSimObj((ADDR*)obj, __NUM_LIST(__VA_ARGS__), __VA_ARGS__) tsf_BlCon__executefSimObj((ADDR*)obj, __NUM_LIST(__VA_ARGS__), __VA_ARGS__)
#define BlFuncsInit() \ #define BlFuncsInit() if(!tsf_InitInternal()) { return false; }
if (!tsf_InitInternal()) { \ #define BlFuncsDeinit() if(!tsf_DeinitInternal()) { return false; }
return false; \
}
#define BlFuncsDeinit() \
if (!tsf_DeinitInternal()) { \
return false; \
}
#endif #endif
#endif

View File

@@ -2,22 +2,21 @@
////////////////////////////////////////////////// //////////////////////////////////////////////////
// RedoBlHooks Version 3.0 // RedoBlHooks Version 3.0
// Includes // Includes
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <cstdlib>
#endif
#include <Windows.h> #include <Windows.h>
#include <Psapi.h> #include <Psapi.h>
//#include <map> //#include <map>
#include "BlHooks.hpp" #include "BlHooks.hpp"
// Scanned structures // Scanned structures
BlFunctionDefIntern(tsh_BlPrintf); BlFunctionDefIntern(tsh_BlPrintf);
// Sig Scanning // Sig Scanning
ADDR ImageBase; ADDR ImageBase;
@@ -27,8 +26,7 @@ void tsh_i_InitScanner() {
HMODULE module = GetModuleHandle(NULL); HMODULE module = GetModuleHandle(NULL);
if(module) { if(module) {
MODULEINFO info; MODULEINFO info;
GetModuleInformation(GetCurrentProcess(), module, &info, GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(MODULEINFO));
sizeof(MODULEINFO));
ImageBase = (ADDR)info.lpBaseOfDll; ImageBase = (ADDR)info.lpBaseOfDll;
ImageSize = info.SizeOfImage; ImageSize = info.SizeOfImage;
} }
@@ -42,8 +40,7 @@ bool tsh_i_CompareData(BYTE *data, BYTE *pattern, char *mask) {
return (*mask)==0; return (*mask)==0;
} }
ADDR tsh_i_FindPattern(ADDR imageBase, ADDR imageSize, BYTE *pattern, ADDR tsh_i_FindPattern(ADDR imageBase, ADDR imageSize, BYTE *pattern, char *mask){
char *mask) {
for (ADDR i=imageBase; i < imageBase+imageSize; i++){ for (ADDR i=imageBase; i < imageBase+imageSize; i++){
if(tsh_i_CompareData((PBYTE)i, pattern, mask)){ if(tsh_i_CompareData((PBYTE)i, pattern, mask)){
return i; return i;
@@ -90,12 +87,12 @@ void tsh_i_PatternTextToCode(char *text, char **opatt, char **omask) {
*omask = mask; *omask = mask;
} }
// Public functions for sig scanning // Public functions for sig scanning
// Scan using code-style pattern // Scan using code-style pattern
ADDR tsh_ScanCode(char* pattern, char* mask) { ADDR tsh_ScanCode(char* pattern, char* mask) {
return tsh_i_FindPattern(ImageBase, ImageSize - strlen(mask), (BYTE *)pattern, return tsh_i_FindPattern(ImageBase, ImageSize-strlen(mask), (BYTE*)pattern, mask);
mask);
} }
// Scan using a text-style pattern // Scan using a text-style pattern
@@ -112,6 +109,7 @@ ADDR tsh_ScanText(char *text) {
return res; return res;
} }
// Call Patching and Hooking // Call Patching and Hooking
// Remove protection from address // Remove protection from address
@@ -119,8 +117,7 @@ ADDR tsh_ScanText(char *text) {
void tsh_DeprotectAddress(ADDR length, ADDR location) { void tsh_DeprotectAddress(ADDR length, ADDR location) {
DWORD oldProtection; DWORD oldProtection;
VirtualProtect((void *)location, length, PAGE_EXECUTE_READWRITE, VirtualProtect((void*)location, length, PAGE_EXECUTE_READWRITE, &oldProtection);
&oldProtection);
//tsh_DeprotectedAddresses[location] = {length, oldProtection}; //tsh_DeprotectedAddresses[location] = {length, oldProtection};
} }
@@ -138,7 +135,9 @@ void tsh_ReplaceInt(ADDR addr, int rval) {
tsh_PatchBytes(4, addr, (BYTE*)(&rval)); tsh_PatchBytes(4, addr, (BYTE*)(&rval));
} }
int tsh_i_CallOffset(ADDR instr, ADDR func) { return func - (instr + 4); } int tsh_i_CallOffset(ADDR instr, ADDR func) {
return func - (instr+4);
}
void tsh_i_ReplaceCall(ADDR instr, ADDR target) { void tsh_i_ReplaceCall(ADDR instr, ADDR target) {
tsh_ReplaceInt(instr, tsh_i_CallOffset(instr, target)); tsh_ReplaceInt(instr, tsh_i_CallOffset(instr, target));
@@ -162,13 +161,11 @@ void tsh_UnhookFunction(ADDR victim, BYTE *origbytes) {
tsh_i_PatchCopy(victim, (ADDR)origbytes, 6); //restore old data tsh_i_PatchCopy(victim, (ADDR)origbytes, 6); //restore old data
} }
int tsh_PatchAllMatchesCode(ADDR len, char *patt, char *mask, char *replace, int tsh_PatchAllMatchesCode(ADDR len, char* patt, char* mask, char* replace, bool debugprint){
bool debugprint) {
int numpatched = 0; int numpatched = 0;
for(ADDR i=ImageBase; i<ImageBase+ImageSize-len; i++){ for(ADDR i=ImageBase; i<ImageBase+ImageSize-len; i++){
if(tsh_i_CompareData((BYTE*)i, (BYTE*)patt, mask)){ if(tsh_i_CompareData((BYTE*)i, (BYTE*)patt, mask)){
if (debugprint) if(debugprint) BlPrintf("RedoBlHooks: Patching call at %08x", i);
BlPrintf("RedoBlHooks: Patching call at %08x", i);
numpatched++; numpatched++;
tsh_DeprotectAddress(i, len); tsh_DeprotectAddress(i, len);
@@ -180,8 +177,7 @@ int tsh_PatchAllMatchesCode(ADDR len, char *patt, char *mask, char *replace,
return numpatched; return numpatched;
} }
int tsh_PatchAllMatchesHex(ADDR len, char *text, char *replace, int tsh_PatchAllMatchesHex(ADDR len, char* text, char* replace, bool debugprint) {
bool debugprint) {
char* patt; char* patt;
char* mask; char* mask;
tsh_i_PatternTextToCode(text, &patt, &mask); tsh_i_PatternTextToCode(text, &patt, &mask);
@@ -194,16 +190,18 @@ int tsh_PatchAllMatchesHex(ADDR len, char *text, char *replace,
return res; return res;
} }
// Initialization // Initialization
bool tsh_InitInternal(){ bool tsh_InitInternal(){
tsh_i_InitScanner(); tsh_i_InitScanner();
BlScanFunctionText( BlScanFunctionText(tsh_BlPrintf, "8D 44 24 08 33 D2 50 FF 74 24 08 33 C9 E8 ? ? ? ? 83 C4 08 C3");
tsh_BlPrintf,
"8D 44 24 08 33 D2 50 FF 74 24 08 33 C9 E8 ? ? ? ? 83 C4 08 C3");
return true; return true;
} }
bool tsh_DeinitInternal() { return true; } bool tsh_DeinitInternal() {
return true;
}

View File

@@ -5,11 +5,13 @@
#ifndef _H_BLHOOKS #ifndef _H_BLHOOKS
#define _H_BLHOOKS #define _H_BLHOOKS
// Typedefs // Typedefs
typedef unsigned char BYTE; typedef unsigned char BYTE;
typedef unsigned int ADDR; typedef unsigned int ADDR;
// Prototypes // Prototypes
bool tsh_InitInternal(); bool tsh_InitInternal();
@@ -23,6 +25,7 @@ void tsh_PatchByte(ADDR, BYTE);
void tsh_PatchBytes(unsigned int, ADDR, BYTE*); void tsh_PatchBytes(unsigned int, ADDR, BYTE*);
void tsh_PatchInt(ADDR, int); void tsh_PatchInt(ADDR, int);
// Debug print settings // Debug print settings
#ifndef TSH_NO_DEBUG_PRINT #ifndef TSH_NO_DEBUG_PRINT
@@ -31,32 +34,29 @@ void tsh_PatchInt(ADDR, int);
#define tsh_DEBUGPRINT true #define tsh_DEBUGPRINT true
#endif #endif
// Function short names // Function short names
// Use in code when the def is not shared in a header // Use in code when the def is not shared in a header
#define BlFunctionDef(returnType, convention, name, ...) \ #define BlFunctionDef(returnType, convention, name, ...) \
typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \ typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \
tsh_##name##FnT name; tsh_##name##FnT name;
// Use in header for shared function defs when a BlFunctionDefIntern exists in // Use in header for shared function defs when a BlFunctionDefIntern exists in code
// code
#define BlFunctionDefExtern(returnType, convention, name, ...) \ #define BlFunctionDefExtern(returnType, convention, name, ...) \
typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \ typedef returnType (convention *tsh_##name##FnT)(__VA_ARGS__); \
extern tsh_##name##FnT name; extern tsh_##name##FnT name;
// Use in code for shared function defs when a BlFunctionDefExtern exists in // Use in code for shared function defs when a BlFunctionDefExtern exists in header
// header #define BlFunctionDefIntern(name) \
#define BlFunctionDefIntern(name) tsh_##name##FnT name; tsh_##name##FnT name;
// Scan for and assign the pattern to the variable, or err and return if not // Scan for and assign the pattern to the variable, or err and return if not found
// found
#define BlScanFunctionCode(target, patt, mask) \ #define BlScanFunctionCode(target, patt, mask) \
target = (tsh_##target##FnT)tsh_ScanCode((char*)patt, (char*)mask); \ target = (tsh_##target##FnT)tsh_ScanCode((char*)patt, (char*)mask); \
if(!target){ \ if(!target){ \
BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \ BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
return false; \ return false; \
}else{ \ }else{ \
if (tsh_DEBUGPRINT) \ if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
BlPrintf("RedoBlHooks | Found function " #target " at %08x", \
(int)target); \
} }
#define BlScanFunctionText(target, text) \ #define BlScanFunctionText(target, text) \
target = (tsh_##target##FnT)tsh_ScanText((char*)text); \ target = (tsh_##target##FnT)tsh_ScanText((char*)text); \
@@ -64,9 +64,7 @@ void tsh_PatchInt(ADDR, int);
BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \ BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
return false; \ return false; \
}else{ \ }else{ \
if (tsh_DEBUGPRINT) \ if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
BlPrintf("RedoBlHooks | Found function " #target " at %08x", \
(int)target); \
} }
#define BlScanCode(target, patt, mask) \ #define BlScanCode(target, patt, mask) \
target = tsh_ScanCode((char*)patt, (char*)mask); \ target = tsh_ScanCode((char*)patt, (char*)mask); \
@@ -74,8 +72,7 @@ void tsh_PatchInt(ADDR, int);
BlPrintf("RedoBlHooks | Cannot find pattern "#target"!"); \ BlPrintf("RedoBlHooks | Cannot find pattern "#target"!"); \
return false; \ return false; \
}else{ \ }else{ \
if (tsh_DEBUGPRINT) \ if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
BlPrintf("RedoBlHooks | Found " #target " at %08x", (int)target); \
} }
#define BlScanText(target, text) \ #define BlScanText(target, text) \
target = tsh_ScanText((char*)text); \ target = tsh_ScanText((char*)text); \
@@ -83,20 +80,14 @@ void tsh_PatchInt(ADDR, int);
BlPrintf("RedoBlHooks | Cannot find "#target"!"); \ BlPrintf("RedoBlHooks | Cannot find "#target"!"); \
return false; \ return false; \
}else{ \ }else{ \
if (tsh_DEBUGPRINT) \ if(tsh_DEBUGPRINT) BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
BlPrintf("RedoBlHooks | Found " #target " at %08x", (int)target); \
} }
// Use in code to define the data and functions for hooking a function // Use in code to define the data and functions for hooking a function
#define BlFunctionHookDef(func) \ #define BlFunctionHookDef(func) \
BYTE tsh_BlFunctionHook##func##Data[6]; \ BYTE tsh_BlFunctionHook##func##Data[6]; \
void func##HookOn() { \ void func##HookOn(){ tsh_HookFunction((ADDR)func, (ADDR)func##Hook, tsh_BlFunctionHook##func##Data); } \
tsh_HookFunction((ADDR)func, (ADDR)func##Hook, \ void func##HookOff(){ tsh_UnhookFunction((ADDR)func, tsh_BlFunctionHook##func##Data); }
tsh_BlFunctionHook##func##Data); \
} \
void func##HookOff() { \
tsh_UnhookFunction((ADDR)func, tsh_BlFunctionHook##func##Data); \
}
// Use in code to initialize the hook once // Use in code to initialize the hook once
#define BlFunctionHookInit(func) \ #define BlFunctionHookInit(func) \
tsh_DeprotectAddress(6, (ADDR)func); \ tsh_DeprotectAddress(6, (ADDR)func); \
@@ -104,38 +95,28 @@ void tsh_PatchInt(ADDR, int);
// Replace all matches of the pattern with the given byte string // Replace all matches of the pattern with the given byte string
#define BlPatchAllMatchesCode(len, patt, mask, repl) \ #define BlPatchAllMatchesCode(len, patt, mask, repl) \
tsh_PatchAllMatchesCode((ADDR)len, (char *)patt, (char *)mask, (char *)repl, \ tsh_PatchAllMatchesCode((ADDR)len, (char*)patt, (char*)mask, (char*)repl, tsh_DEBUGPRINT);
tsh_DEBUGPRINT);
#define BlPatchAllMatchesText(len, text, repl) \ #define BlPatchAllMatchesText(len, text, repl) \
tsh_PatchAllMatchesCode((ADDR)len, (char *)text, (char *)repl, \ tsh_PatchAllMatchesCode((ADDR)len, (char*)text, (char*)repl, tsh_DEBUGPRINT);
tsh_DEBUGPRINT);
// Deprotect and replace one byte // Deprotect and replace one byte
#define BlPatchByte(addr, byte) tsh_PatchByte((ADDR)addr, (BYTE)byte); #define BlPatchByte(addr, byte) \
tsh_PatchByte((ADDR)addr, (BYTE)byte);
// Deprotect and replace a byte string // Deprotect and replace a byte string
#define BlPatchBytes(len, addr, repl) \ #define BlPatchBytes(len, addr, repl) \
tsh_PatchBytes((ADDR)len, (ADDR)addr, (BYTE*)repl); tsh_PatchBytes((ADDR)len, (ADDR)addr, (BYTE*)repl);
// BlPrintf(char* format, ...) // BlPrintf(char* format, ...)
#define BlPrintf(...) \ #define BlPrintf(...) if(tsh_BlPrintf) { tsh_BlPrintf(__VA_ARGS__); }
if (tsh_BlPrintf) { \
tsh_BlPrintf(__VA_ARGS__); \
}
// BlHooksInit() -> bool: success // BlHooksInit() -> bool: success
#define BlHooksInit() \ #define BlHooksInit() if(!tsh_InitInternal()) { BlPrintf("BlHooksInit failed"); return false; }
if (!tsh_InitInternal()) { \
BlPrintf("BlHooksInit failed"); \
return false; \
}
// BlHooksDeinit() -> bool: success // BlHooksDeinit() -> bool: success
#define BlHooksDeinit() \ #define BlHooksDeinit() if(!tsh_DeinitInternal()) { BlPrintf("BlHooksDeinit failed"); return false; }
if (!tsh_DeinitInternal()) { \
BlPrintf("BlHooksDeinit failed"); \
return false; \
}
// Scanned structures // Scanned structures
BlFunctionDefExtern(void, , tsh_BlPrintf, const char*, ...); BlFunctionDefExtern(void, , tsh_BlPrintf, const char*, ...);
#endif #endif

View File

@@ -1,27 +0,0 @@
@echo off
cd /d %~dp0
REM Ensure MinGW32 toolchain is first in PATH (matches compile.bat)
set "PATH=C:\msys64\mingw32\bin;%PATH%"
REM Configure CMake (generate into build/)
cmake -S . -B build -G "MinGW Makefiles"
if errorlevel 1 goto :error
REM Build (Release by default)
cmake --build build --config Release -j
if errorlevel 1 goto :error
echo.
echo Build completed.
echo Outputs in .\build :
echo - BlockLua.dll
echo - BlockLua-Unsafe.dll
exit /b 0
:error
echo.
echo Build failed. See errors above.
exit /b 1

View File

@@ -14,10 +14,10 @@ Lua scripting for Blockland
### From TorqueScript ### From TorqueScript
`'print('hello world')` - Execute Lua in the console by prepending a `'` (single quote) `'print('hello world')` - Execute Lua in the console by prepending a `'` (single quote)
`luaeval("code");` - Execute Lua code `luaeval("code");` - Execute Lua code
`luacall("funcName", %args...);` - Call a Lua global function `luacall("funcName", %args...);` - Call a Lua function (supports indexing tables and object methods)
`luaexec("fileName");` - Execute a Lua file. Path rules are the same as executing .cs files. `luaexec("fileName");` - Execute a Lua file. Path rules are the same as when executing .cs files, relative paths are allowed.
`luaget("varName");` - Read a Lua global variable `luaget("varName");` - Read a Lua global variable (supports indexing tables)
`luaset("varName", %value);` - Write a Lua global variable `luaset("varName", %value);` - Write a Lua global variable (supports indexing tables)
### From Lua ### From Lua
`bl.eval('code')` - Eval TorqueScript code `bl.eval('code')` - Eval TorqueScript code
@@ -35,7 +35,7 @@ Lua scripting for Blockland
`object.key = value` - Associate Lua data with a Torque object `object.key = value` - Associate Lua data with a Torque object
`object:method(args)` - Call a Torque object method `object:method(args)` - Call a Torque object method
`object[index]` - Access a member of a Torque set or group `object[index]` - Access a member of a Torque set or group
`for childIndex, child in object:members() do` - Iterate objects within of a Torque set or group. Indices start at 0 like in Torque. `for child in object:members() do` - Iterate objects within of a Torque set or group. Indices start at 0 like in Torque.
`bl.isObject(object, objectID, or 'objectName')` - Check if an object exists `bl.isObject(object, objectID, or 'objectName')` - Check if an object exists
`object:exists()` - Check if an object exists `object:exists()` - Check if an object exists
@@ -48,15 +48,25 @@ Lua scripting for Blockland
`for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types. `for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types.
`for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types. `for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types.
### List of Object Classes (for raycasts and searches)
`'all'` - Any object
`'player'` - Players or bots
`'item'` - Items
`'vehicle'` - Vehicles
`'projectile'` - Projectiles
`'brick'` - Bricks with raycasting enabled
`'brickalways'` - All bricks including those with raycasting disabled
Other types: `'static'`, `'environment'`, `'terrain'`, `'water'`, `'trigger'`, `'marker'`, `'gamebase'`, `'shapebase'`, `'camera'`, `'staticshape'`, `'vehicleblocker'`, `'explosion'`, `'corpse'`, `'debris'`, `'physicalzone'`, `'staticts'`, `'staticrendered'`, `'damagableitem'`
### Server-Client Communication ### Server-Client Communication
`bl.addServerCmd('commandName', function(client, args...) yourCode end)` - Register a /command on the server `bl.addServerCmd('commandName', function(client, args...) ... end)` - Register a /command on the server
`bl.addClientCmd('commandName', function(args...) yourCode end)` - Register a client command on the client `bl.addClientCmd('commandName', function(args...) ... end)` - Register a client command on the client
`bl.commandToServer('commandName', args...)` - Execute a server command as a client `bl.commandToServer('commandName', args...)` - As a client, execute a server command
`bl.commandToClient('commandName', args...)` - As the server, execute a client command on a specific client `bl.commandToClient(client, 'commandName', args...)` - As the server, execute a client command on a specific client
`bl.commandToAll('commandName', args...)` - As the server, execute a client command on all clients `bl.commandToAll('commandName', args...)` - As the server, execute a client command on all clients
### Packages/Hooks ### Packages/Hooks
`bl.hook('packageName', 'functionName', 'before'/'after', function(args) yourCode end)` - Hook a Torque function with a Lua function. `bl.hook('packageName', 'functionName', 'before'/'after', function(args) ... end)` - Hook a Torque function with a Lua function.
`args` is an array containing the arguments provided to the function. If the hook is `before`, these can be modified before being passed to the parent function. `args` is an array containing the arguments provided to the function. If the hook is `before`, these can be modified before being passed to the parent function.
If `args._return` is set to anything other than nil by a `before` hook, the parent function will not be called, and the function will simply return that value. Also in this case, any `after` hook will not be executed. If `args._return` is set to anything other than nil by a `before` hook, the parent function will not be called, and the function will simply return that value. Also in this case, any `after` hook will not be executed.
In an `after` hook, `args._return` is set to the value returned by the parent function, and can be modified. In an `after` hook, `args._return` is set to the value returned by the parent function, and can be modified.
@@ -136,14 +146,16 @@ When reading from outside ZIPs, binary files are fully supported.
WIP WIP
### Extended Standard Lua Library ### Extended Standard Lua Library
`string[index]` `str[index]`
`string[{start,stop}]` `str[{start,stop}]`
`string.split(str, separator='' (splits into chars), noregex=false)` `string.split(str, separator='' (splits into chars), noregex=false)`
`string.bytes(str)` `string.bytes(str)`
`string.trim(str, charsToTrim=' \t\r\n')` `string.trim(str, charsToTrim=' \t\r\n')`
`table.empty` `table.empty`
`table.map(func, ...)` `table.map(func, ...)`
`table.mapk(func, ...)`
`table.map_list(func, ...)` `table.map_list(func, ...)`
`table.mapi_list(func, ...)`
`table.swap(tbl)` `table.swap(tbl)`
`table.reverse(list)` `table.reverse(list)`
`table.islist(list)` `table.islist(list)`
@@ -173,43 +185,35 @@ TorqueScript stores no type information; all values in TorqueScript are strings.
### From Lua to TorqueScript ### From Lua to TorqueScript
- `nil` becomes the empty string "" - `nil` becomes the empty string ""
- `true` and `false` become "1" and "0" respectively - `true` and `false` become "1" and "0" respectively
- Torque containers become their object ID - A Torque object container becomes its object ID
- A `vector` becomes a string containing three numbers separated by spaces - A `vector` becomes a string containing three numbers separated by spaces
- A table of two vectors becomes a string containing six numbers separated by spaces - A table of two `vector`s becomes a string containing six numbers separated by spaces
- (WIP) A `matrix` is converted into an axis-angle (a "transform"), a string containing seven numbers separated by spaces
- Any `string` is passed directly as a string - Any `string` is passed directly as a string
- Tables cannot be passed and will throw an error - Tables cannot be passed and will throw an error
### From TorqueScript to Lua ### From TorqueScript to Lua
- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container.
- The empty string "" becomes `nil` - The empty string "" becomes `nil`
- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container.
- A string containing two or three numbers separated by single spaces becomes a `vector` - A string containing two or three numbers separated by single spaces becomes a `vector`
- A string containing six numbers separated by single spaces becomes a table of two vectors, usually defining the corners a bounding box - A string containing six numbers separated by single spaces becomes a table of two vectors, usually defining the corners a bounding box
- (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform" in TorqueScript parlance), and is converted into a `matrix` representing the translation and rotation. - (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform"), and is converted into a `matrix` representing the translation and rotation
- Any other string is passed directly as a `string` - Any other string is passed directly as a `string`
For scenarios where the automatic TorqueScript->Lua conversion rules are insufficient or incorrect, use `bl.type`. For scenarios where the automatic TorqueScript->Lua conversion rules are insufficient or incorrect, use `bl.type`.
To convert objects by hand, use `bl.object`, `bl.boolean`, or `bl.string`. To convert things by hand, use `bl.object`, `bl.boolean`, or `bl.string`.
## I/O and Safety ## I/O and Safety
All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is. All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is.
BlockLua also has access to any C libraries installed in the `modules/lualib` folder, so be careful throwing things in there. BlockLua also has access to any C libraries installed in the `modules/lualib` folder, so be careful throwing things in there.
### Unsafe Mode ### Unsafe Mode
BlockLua can be built in Unsafe Mode by specifying the `-DBLLUA_UNSAFE` compiler flag. This removes the sandboxing of Lua code, allowing it to access any file and use any library, including ffi. BlockLua can be built in Unsafe Mode by specifying the `-DBLLUA_UNSAFE` compiler flag. This removes the sandboxing of Lua code, allowing it to access any file and use any library, including ffi.
Please do not publish add-ons that require unsafe mode. A more limited option is `-DBLLUA_ALLOWFFI`, which allows the use of the `ffi` library. This can still be exploited to grant all the same access as full unsafe mode.
Please do not publish add-ons that require either of these.
### List of Object Types
`'all'` - Any object
`'player'` - Players or bots
`'item'` - Items
`'vehicle'` - Vehicles
`'projectile'` - Projectiles
`'brick'` - Bricks with raycasting enabled
`'brickalways'` - All bricks including those with raycasting disabled
Other types: `'static'`, `'environment'`, `'terrain'`, `'water'`, `'trigger'`, `'marker'`, `'gamebase'`, `'shapebase'`, `'camera'`, `'staticshape'`, `'vehicleblocker'`, `'explosion'`, `'corpse'`, `'debris'`, `'physicalzone'`, `'staticts'`, `'staticrendered'`, `'damagableitem'`
## Compiling ## Compiling
With any *32-bit* variant of GCC installed (such as MinGW or MSYS2), run the following command in the repo directory: With any *32-bit* variant of GCC installed (such as MinGW or MSYS2), run the following command in the repo directory:
`g++ src/bllua4.cpp -o BlockLua.dll -m32 -shared -static-libgcc -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1 src/bllua` `g++ src/bllua4.cpp -o BlockLua.dll -m32 -shared -static-libgcc -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1`
LuaJIT (lua5.1.dll) can be obtained from https://luajit.org/ LuaJIT (lua5.1.dll) can be obtained from https://luajit.org/

View File

@@ -1,16 +1,13 @@
// BlockLua (bllua4): Simple Lua interface for TorqueScript // BlockLua (bllua4): Advanced Lua interface for TorqueScript
// Includes // Includes
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h> #include <Windows.h>
#include <Psapi.h> #include <Psapi.h>
#include "BlFuncs.cpp"
#include "BlHooks.cpp"
#include "lua.hpp" #include "lua.hpp"
#include "BlHooks.cpp"
#include "BlFuncs.cpp"
#include "luainterp.cpp" #include "luainterp.cpp"
#include "lualibts.cpp" #include "lualibts.cpp"
@@ -60,24 +57,26 @@ bool init() {
// Set up Lua environment // Set up Lua environment
BLL_LOAD_LUA(gL, bll_fileLuaEnv); BLL_LOAD_LUA(gL, bll_fileLuaEnv);
#ifdef BLLUA_ALLOWFFI
lua_pushboolean(gL, true);
lua_setglobal(gL, "_bllua_allowffi");
#endif
#ifndef BLLUA_UNSAFE #ifndef BLLUA_UNSAFE
BLL_LOAD_LUA(gL, bll_fileLuaEnvSafe); BLL_LOAD_LUA(gL, bll_fileLuaEnvSafe);
#endif #endif
// Expose Lua API to TS // Load utilities in Lua
BlAddFunction(NULL, NULL, "_bllua_luacall", bll_ts_luacall,
"LuaCall(name, ...) - Call Lua function and return result", 2,
20);
BlEval(bll_fileTsEnv);
// Load utilities
BLL_LOAD_LUA(gL, bll_fileLuaStd); BLL_LOAD_LUA(gL, bll_fileLuaStd);
BLL_LOAD_LUA(gL, bll_fileLuaVector); BLL_LOAD_LUA(gL, bll_fileLuaVector);
BLL_LOAD_LUA(gL, bll_fileLuaMatrix); BLL_LOAD_LUA(gL, bll_fileLuaMatrix);
BLL_LOAD_LUA(gL, bll_fileLuaLibts); BLL_LOAD_LUA(gL, bll_fileLuaLibts);
BlEval(bll_fileTsLibts);
BLL_LOAD_LUA(gL, bll_fileLuaLibbl); BLL_LOAD_LUA(gL, bll_fileLuaLibbl);
BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes); BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes);
// Expose Lua API to TS
BlAddFunction(NULL, NULL, "_bllua_luacall", bll_ts_luacall, "LuaCall(name, ...) - Call Lua function and return result", 2, 20);
BlEval(bll_fileTsEnv);
BlEval(bll_fileTsLibts);
BlEval(bll_fileTsLibblSupport); BlEval(bll_fileTsLibblSupport);
BlEval(bll_fileLoadaddons); BlEval(bll_fileLoadaddons);
@@ -90,8 +89,7 @@ bool init() {
bool deinit() { bool deinit() {
BlPrintf("BlockLua: Unloading"); BlPrintf("BlockLua: Unloading");
BlEval("deactivatePackage(_bllua_main);"); BlEval("$_bllua_active=0;deactivatePackage(_bllua_main);");
BlEval("$_bllua_active = 0;");
bll_LuaEval(gL, "for _,f in pairs(_bllua_on_unload) do f() end"); bll_LuaEval(gL, "for _,f in pairs(_bllua_on_unload) do f() end");
lua_close(gL); lua_close(gL);
@@ -105,11 +103,8 @@ bool deinit() {
bool __stdcall DllMain(HINSTANCE hinstance, DWORD reason, void* reserved) { bool __stdcall DllMain(HINSTANCE hinstance, DWORD reason, void* reserved) {
switch(reason) { switch(reason) {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH: return init();
return init(); case DLL_PROCESS_DETACH: return deinit();
case DLL_PROCESS_DETACH: default : return true;
return deinit();
default:
return true;
} }
} }

View File

@@ -4,9 +4,7 @@
-- Utility: Convert a list of strings into a map of string->true -- Utility: Convert a list of strings into a map of string->true
local function tmap(t) local function tmap(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end
local u = {}; for _, n in ipairs(t) do u[n] = true end; return u;
end
-- Save banned global variables for wrapping with safe functions -- Save banned global variables for wrapping with safe functions
local old_io = io local old_io = io
@@ -14,6 +12,7 @@ local old_require = require
local old_os = os local old_os = os
local old_debug = debug local old_debug = debug
local old_package = package local old_package = package
local old_allowffi = _bllua_allowffi
-- Remove all global variables except a whitelist -- Remove all global variables except a whitelist
local ok_names = tmap { local ok_names = tmap {
@@ -39,13 +38,10 @@ end
-- Sanitize file paths to point only to allowed files within the game directory -- Sanitize file paths to point only to allowed files within the game directory
-- List of allowed directories for reading/writing -- List of allowed directories for reading/writing
-- modules/lualib is also allowed as read-only
local allowed_dirs = tmap { local allowed_dirs = tmap {
'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders' 'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders'
} }
-- List of allowed directories for reading only
local allowed_dirs_readonly = tmap {
'lualib'
}
-- List of disallowed file extensions - basically executable file extensions -- List of disallowed file extensions - basically executable file extensions
-- Note that even without this protection, exploiting would still require somehow -- Note that even without this protection, exploiting would still require somehow
-- getting a file within the allowed directories to autorun, -- getting a file within the allowed directories to autorun,
@@ -81,14 +77,15 @@ local function safe_path(fn, readonly)
end end
-- allow only whitelisted dirs -- allow only whitelisted dirs
local dir = fn:match('^([^/]+)/') local dir = fn:match('^([^/]+)/')
if (not dir) or ( if not (dir and (
(not allowed_dirs[dir:lower()]) and allowed_dirs[dir:lower()] or
((not readonly) or (not allowed_dirs_readonly[dir:lower()]))) then ( readonly and fn:find('^modules/lualib/') ) ))
return nil, 'filename is in disallowed directory ' .. (dir or 'nil') then
return nil, 'File is in disallowed directory '..(dir or 'nil')
end end
-- disallow blacklisted extensions or no extension -- disallow blacklisted extensions
local ext = fn:match('%.([^/%.]+)$') local ext = fn:match('%.([^/%.]+)$')
if (not ext) or (disallowed_exts[ext:lower()]) then if ext and disallowed_exts[ext:lower()] then
return nil, 'Filename \''..fn..'\' has disallowed extension \''.. return nil, 'Filename \''..fn..'\' has disallowed extension \''..
(ext or '')..'\'' (ext or '')..'\''
end end
@@ -106,7 +103,6 @@ function _bllua_io_open(fn, md)
return nil, err return nil, err
end end
end end
-- Allow io.type (works on file handles returned by io.open) -- Allow io.type (works on file handles returned by io.open)
function _bllua_io_type(f) function _bllua_io_type(f)
return old_io.type(f) return old_io.type(f)
@@ -120,6 +116,7 @@ local disallowed_packages = tmap {
'ffi', 'debug', 'package', 'io', 'os', 'ffi', 'debug', 'package', 'io', 'os',
'_bllua_ts', '_bllua_ts',
} }
if old_allowffi then disallowed_packages['ffi'] = nil end
function _bllua_requiresecure(name) function _bllua_requiresecure(name)
if name:find('[^a-zA-Z0-9_%-%.]') or name:find('%.%.') or if name:find('[^a-zA-Z0-9_%-%.]') or name:find('%.%.') or
name:find('^%.') or name:find('%.$') then name:find('^%.') or name:find('%.$') then
@@ -131,7 +128,6 @@ function _bllua_requiresecure(name)
return old_require(name) return old_require(name)
end end
end end
package = { package = {
seeall = old_package.seeall, seeall = old_package.seeall,
} }

View File

@@ -37,5 +37,9 @@ function _bllua_on_error(err)
return table.concat(tracelines, '\n') return table.concat(tracelines, '\n')
end end
-- overridden in lua-env-safe.lua (executed if not BLLUA_UNSAFE)
_bllua_io_open = io.open
_bllua_requiresecure = require
print = _bllua_ts.echo print = _bllua_ts.echo
print(' Executed bllua-env.lua') print(' Executed bllua-env.lua')

View File

@@ -1,11 +1,5 @@
// Handle errors with a Lua function, defined in lua-env.lua // Handle errors with a Lua function, defined in lua-env.lua
#include "luainterp.hpp"
#include "BlHooks.hpp"
#include "lauxlib.h"
#include "lua.h"
#include <cstring>
int bll_error_handler(lua_State *L) { int bll_error_handler(lua_State *L) {
lua_getfield(L, LUA_GLOBALSINDEX, "_bllua_on_error"); lua_getfield(L, LUA_GLOBALSINDEX, "_bllua_on_error");
if (!lua_isfunction(L, -1)) { if (!lua_isfunction(L, -1)) {
@@ -54,26 +48,21 @@ bool bll_LuaEval(lua_State *L, const char *str) {
// Convert a Lua stack entry into a string for providing to TS // Convert a Lua stack entry into a string for providing to TS
// Use static buffer to avoid excessive malloc // Use static buffer to avoid excessive malloc
#define BLL_ARG_COUNT 20
#define BLL_ARG_MAX 8192
char bll_arg_buffer[BLL_ARG_COUNT][BLL_ARG_MAX]; char bll_arg_buffer[BLL_ARG_COUNT][BLL_ARG_MAX];
bool bll_toarg(lua_State* L, char* buf, int i, bool err) { bool bll_toarg(lua_State* L, char* buf, int i, bool err) {
if(lua_isstring(L, i)) { if(lua_isstring(L, i)) {
const char* str = lua_tostring(L, i); const char* str = lua_tostring(L, i);
if(strlen(str) >= BLL_ARG_MAX) { if(strlen(str) >= BLL_ARG_MAX) {
if (err) if(err) luaL_error(L, "argument to TS is too long - max length is 8192");
luaL_error(L, "argument to TS is too long - max length is 8192");
return true; return true;
} else { } else {
#ifdef _MSC_VER strcpy(buf, str);
strncpy_s(buf, BLL_ARG_MAX, str, _TRUNCATE);
#else
strncpy(buf, str, BLL_ARG_MAX - 1);
buf[BLL_ARG_MAX - 1] = '\0';
#endif
return false; return false;
} }
} else { } else {
if (err) if(err) luaL_error(L, "argument to TS must be a string");
luaL_error(L, "argument to TS must be a string");
return true; return true;
} }
} }

View File

@@ -1,17 +0,0 @@
// Shared declarations for Lua <-> TS argument handling
#ifndef _H_LUAINTERP_SHARED
#define _H_LUAINTERP_SHARED
#include "lua.h"
#define BLL_ARG_COUNT 20
#define BLL_ARG_MAX 8192
extern char bll_arg_buffer[BLL_ARG_COUNT][BLL_ARG_MAX];
bool bll_toarg(lua_State *L, char *buf, int i, bool err);
int bll_pcall(lua_State *L, int nargs, int nret);
void bll_printError(lua_State *L, const char *operation, const char *item);
extern lua_State *gL;
#endif

View File

@@ -3,25 +3,14 @@
// TS -> Lua API // TS -> Lua API
// Call a TS function from Lua, push the result to Lua stack // Call a TS function from Lua, push the result to Lua stack
#include "BlFuncs.hpp" int bll_TsCall(lua_State* L, const char* oname, const char* fname, int argc, int ofs) {
#include "BlHooks.hpp"
#include "lauxlib.h"
#include "lua.h"
#include "luainterp.hpp"
int bll_TsCall(lua_State *L, const char *oname, const char *fname, int argc,
int ofs) {
ADDR obj = (ADDR)NULL; ADDR obj = (ADDR)NULL;
if(oname) { if(oname) {
obj = BlObject(oname); obj = BlObject(oname);
if (!obj) { if(!obj) { return luaL_error(L, "Lua->TS call: Object not found"); }
return luaL_error(L, "Lua->TS call: Object not found");
}
} }
if (argc > BLL_ARG_COUNT) { if(argc > BLL_ARG_COUNT) { return luaL_error(L, "Lua->TS call: Too many arguments (Max is 19)"); }
return luaL_error(L, "Lua->TS call: Too many arguments (Max is 19)");
}
char* argv[BLL_ARG_COUNT]; char* argv[BLL_ARG_COUNT];
for(int i=0; i<argc; i++) { for(int i=0; i<argc; i++) {
@@ -34,184 +23,52 @@ int bll_TsCall(lua_State *L, const char *oname, const char *fname, int argc,
const char* res; const char* res;
if(obj) { if(obj) {
switch(argc) { switch(argc) {
case 0: case 0: res = BlCallObj(obj, fname); break; // no idea why this happens sometimes, it shouldnt be possible
res = BlCallObj(obj, fname); case 1: res = BlCallObj(obj, fname); break;
break; // no idea why this happens sometimes, it shouldnt be possible case 2: res = BlCallObj(obj, fname, argv[0]); break;
case 1: case 3: res = BlCallObj(obj, fname, argv[0], argv[1]); break;
res = BlCallObj(obj, fname); case 4: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2]); break;
break; case 5: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3]); break;
case 2: case 6: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4]); break;
res = BlCallObj(obj, fname, argv[0]); case 7: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); break;
break; case 8: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); break;
case 3: case 9: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); break;
res = BlCallObj(obj, fname, argv[0], argv[1]); case 10: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); break;
break; case 11: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); break;
case 4: case 12: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); break;
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2]); case 13: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); break;
break; case 14: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); break;
case 5: case 15: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); break;
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3]); case 16: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); break;
break; case 17: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15]); break;
case 6: case 18: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16]); break;
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4]); case 19: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17]); break;
break; case 20: res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18]); break;
case 7: default: res = ""; luaL_error(L, "Lua->TS object call: Too many arguments (Max is 19)");
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5]);
break;
case 8:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6]);
break;
case 9:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7]);
break;
case 10:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8]);
break;
case 11:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9]);
break;
case 12:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
break;
case 13:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11]);
break;
case 14:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12]);
break;
case 15:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13]);
break;
case 16:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14]);
break;
case 17:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14], argv[15]);
break;
case 18:
res =
BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14], argv[15], argv[16]);
break;
case 19:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14], argv[15],
argv[16], argv[17]);
break;
case 20:
res = BlCallObj(obj, fname, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14], argv[15],
argv[16], argv[17], argv[18]);
break;
default:
res = "";
luaL_error(L, "Lua->TS object call: Too many arguments (Max is 19)");
} }
} else { } else {
switch(argc) { switch(argc) {
case 0: case 0: res = BlCall(fname); break;
res = BlCall(fname); case 1: res = BlCall(fname, argv[0]); break;
break; case 2: res = BlCall(fname, argv[0], argv[1]); break;
case 1: case 3: res = BlCall(fname, argv[0], argv[1], argv[2]); break;
res = BlCall(fname, argv[0]); case 4: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3]); break;
break; case 5: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4]); break;
case 2: case 6: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); break;
res = BlCall(fname, argv[0], argv[1]); case 7: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); break;
break; case 8: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); break;
case 3: case 9: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); break;
res = BlCall(fname, argv[0], argv[1], argv[2]); case 10: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); break;
break; case 11: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); break;
case 4: case 12: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); break;
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3]); case 13: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); break;
break; case 14: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); break;
case 5: case 15: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); break;
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4]); case 16: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15]); break;
break; case 17: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16]); break;
case 6: case 18: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17]); break;
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); case 19: res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18]); break;
break; default: res = ""; luaL_error(L, "Lua->TS call: Too many arguments (Max is 19)");
case 7:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6]);
break;
case 8:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7]);
break;
case 9:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8]);
break;
case 10:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9]);
break;
case 11:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10]);
break;
case 12:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]);
break;
case 13:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12]);
break;
case 14:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13]);
break;
case 15:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14]);
break;
case 16:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14], argv[15]);
break;
case 17:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14], argv[15], argv[16]);
break;
case 18:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14], argv[15], argv[16], argv[17]);
break;
case 19:
res = BlCall(fname, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14], argv[15], argv[16], argv[17],
argv[18]);
break;
default:
res = "";
luaL_error(L, "Lua->TS call: Too many arguments (Max is 19)");
} }
} }
@@ -222,8 +79,7 @@ int bll_TsCall(lua_State *L, const char *oname, const char *fname, int argc,
// Lua lib function: ts.call // Lua lib function: ts.call
int bll_lua_tscall(lua_State* L) { int bll_lua_tscall(lua_State* L) {
int argc = lua_gettop(L)-1; // number of arguments after function name int argc = lua_gettop(L)-1; // number of arguments after function name
if (argc < 0) if(argc < 0) return luaL_error(L, "_bllua_ts.call: Must provide a function name");
return luaL_error(L, "_bllua_ts.call: Must provide a function name");
const char* fname = luaL_checkstring(L, 1); const char* fname = luaL_checkstring(L, 1);
@@ -231,11 +87,8 @@ int bll_lua_tscall(lua_State *L) {
} }
// Lua lib function: ts.callobj // Lua lib function: ts.callobj
int bll_lua_tscallobj(lua_State* L) { int bll_lua_tscallobj(lua_State* L) {
int argc = int argc = lua_gettop(L)-2; // number of arguments after function name and object?
lua_gettop(L) - 2; // number of arguments after function name and object? if(argc < 0) return luaL_error(L, "_bllua_ts.callobj: Must provide an object and function name");
if (argc < 0)
return luaL_error(
L, "_bllua_ts.callobj: Must provide an object and function name");
const char* oname = luaL_checkstring(L, 1); const char* oname = luaL_checkstring(L, 1);
const char* fname = luaL_checkstring(L, 2); const char* fname = luaL_checkstring(L, 2);
@@ -257,9 +110,7 @@ int bll_lua_tsgetfield(lua_State *L) {
const char* oname = luaL_checkstring(L, 1); const char* oname = luaL_checkstring(L, 1);
const char* vname = luaL_checkstring(L, 2); const char* vname = luaL_checkstring(L, 2);
ADDR obj = BlObject(oname); ADDR obj = BlObject(oname);
if (!obj) { if(!obj) { return luaL_error(L, "_bllua_ts.getfield: Object not found"); }
return luaL_error(L, "_bllua_ts.getfield: Object not found");
}
const char* val = BlGetField(obj, vname, NULL); const char* val = BlGetField(obj, vname, NULL);
lua_pushstring(L, val); lua_pushstring(L, val);
@@ -271,9 +122,7 @@ int bll_lua_tssetfield(lua_State *L) {
const char* vname = luaL_checkstring(L, 2); const char* vname = luaL_checkstring(L, 2);
const char* val = luaL_checkstring(L, 3); const char* val = luaL_checkstring(L, 3);
ADDR obj = BlObject(oname); ADDR obj = BlObject(oname);
if (!obj) { if(!obj) { return luaL_error(L, "_bllua_ts.setfield: Object not found"); }
return luaL_error(L, "_bllua_ts.setfield: Object not found");
}
BlSetField(obj, vname, NULL, val); BlSetField(obj, vname, NULL, val);
return 0; return 0;
@@ -296,9 +145,15 @@ int bll_lua_tsecho(lua_State *L) {
} }
const luaL_Reg bll_lua_reg[] = { const luaL_Reg bll_lua_reg[] = {
{"call", bll_lua_tscall}, {"callobj", bll_lua_tscallobj}, {"call" , bll_lua_tscall },
{"getvar", bll_lua_tsgetvar}, {"getfield", bll_lua_tsgetfield}, {"callobj" , bll_lua_tscallobj },
{"setfield", bll_lua_tssetfield}, {"eval", bll_lua_tseval}, {"getvar" , bll_lua_tsgetvar },
{"echo", bll_lua_tsecho}, {NULL, NULL}, {"getfield", bll_lua_tsgetfield},
{"setfield", bll_lua_tssetfield},
{"eval" , bll_lua_tseval },
{"echo" , bll_lua_tsecho },
{NULL, NULL},
}; };
void llibbl_init(lua_State *L) { luaL_register(L, "_bllua_ts", bll_lua_reg); } void llibbl_init(lua_State* L) {
luaL_register(L, "_bllua_ts", bll_lua_reg);
}

View File

@@ -1,10 +1,5 @@
// Call a Lua function from TS, return true if success - result will be on Lua // Call a Lua function from TS, return true if success - result will be on Lua stack
// stack
#include "BlFuncs.hpp"
#include "BlHooks.hpp"
#include "luainterp.hpp"
bool bll_LuaCall(const char* fname, int argc, const char* argv[]) { bool bll_LuaCall(const char* fname, int argc, const char* argv[]) {
lua_getglobal(gL, fname); lua_getglobal(gL, fname);
for(int i=0; i<argc; i++) { for(int i=0; i<argc; i++) {
@@ -19,12 +14,9 @@ bool bll_LuaCall(const char *fname, int argc, const char *argv[]) {
// TS lib function: luacall // TS lib function: luacall
const char* bll_ts_luacall(ADDR obj, int argc, const char* argv[]) { const char* bll_ts_luacall(ADDR obj, int argc, const char* argv[]) {
if (argc < 2) if(argc<2) return "";
return "";
if (!bll_LuaCall(argv[1], argc - 2, &argv[2])) { if(!bll_LuaCall(argv[1], argc-2, &argv[2])) { return ""; }
return "";
}
char* retbuf = BlReturnBuffer(BLL_ARG_MAX); char* retbuf = BlReturnBuffer(BLL_ARG_MAX);
bll_toarg(gL, retbuf, -1, false); // provide returned value to ts bll_toarg(gL, retbuf, -1, false); // provide returned value to ts

View File

@@ -6,15 +6,30 @@ local _bllua_ts = ts
bl = bl or {} bl = bl or {}
-- Config
local tsMaxArgs = 16
local tsArgsLocal = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p'
local tsArgsGlobal =
'$_bllua_hook_arg1,$_bllua_hook_arg2,$_bllua_hook_arg3,$_bllua_hook_arg4,'..
'$_bllua_hook_arg5,$_bllua_hook_arg6,$_bllua_hook_arg7,$_bllua_hook_arg8,'..
'$_bllua_hook_arg9,$_bllua_hook_arg10,$_bllua_hook_arg11,$_bllua_hook_arg12,'..
'$_bllua_hook_arg13,$_bllua_hook_arg14,$_bllua_hook_arg15,$_bllua_hook_arg16'
-- Misc -- Misc
-- Apply a function to each element in a list, building a new list from the returns local function ipairsNilable(t)
local function map(t,f) local maxk = 0
local u = {} for k,_ in pairs(t) do
for i,v in ipairs(t) do if k>maxk then maxk = k end
u[i] = f(v)
end end
return u local i = 0
return function()
i = i+1
if i>maxk then return nil
else return i, t[i] end
end end
end
-- Validation
local function isValidFuncName(name) local function isValidFuncName(name)
return type(name)=='string' and name:find('^[a-zA-Z_][a-zA-Z0-9_]*$') return type(name)=='string' and name:find('^[a-zA-Z_][a-zA-Z0-9_]*$')
end end
@@ -107,13 +122,14 @@ end
-- Type conversion from TS to Lua -- Type conversion from TS to Lua
local fromTsForceTypes = { local fromTsForceTypes = {
['boolean'] = tsBool, ['boolean'] = function(val) return tsBool(val) end,
['object'] = function(val) toTsObject(val) end, -- wrap because toTsObject not defined yet ['object'] = function(val) return toTsObject(val) end, -- wrap because toTsObject not defined yet
['string'] = function(val) return val end, ['string'] = function(val) return val end,
} }
local function forceValFromTs(val, typ) local function forceValFromTs(val, typ)
return fromTsForceTypes[typ](val) or local func = fromTsForceTypes[typ]
error('valFromTs: invalid force type '..typ, 4) if not func then error('valFromTs: invalid force type \''..typ..'\'', 4) end
return func(val)
end end
local function vectorFromTs(val) local function vectorFromTs(val)
local xS,yS,zS = val:match('^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') local xS,yS,zS = val:match('^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$')
@@ -154,7 +170,8 @@ local function multinumericFromTs(val)
end end
end end
bl._forceType = bl._forceType or {} bl._forceType = bl._forceType or {}
local function valFromTs(val, name, name2) -- todo: ensure name and name2 are already lowercase -- todo: ensure name and name2 are already lowercase
local function valFromTs(val, name, name2)
if type(val)~='string' then if type(val)~='string' then
error('valFromTs: expected string, got '..type(val), 3) end error('valFromTs: expected string, got '..type(val), 3) end
if name then if name then
@@ -177,18 +194,26 @@ local function valFromTs(val, name, name2) -- todo: ensure name and name2 are al
-- vector, box, or axis->matrix -- vector, box, or axis->matrix
local vec = multinumericFromTs(val) local vec = multinumericFromTs(val)
if vec then return vec end if vec then return vec end
-- net string
if val:sub(1,1)=='\x01' then
return _bllua_ts.call('getTaggedString', val)
end
-- string -- string
return val return val
end end
local function arglistFromTs(name, argsS) local function arglistFromTs(name, argsS)
local args = {} local args = {}
for i,arg in ipairs(argsS) do for i,arg in ipairsNilable(argsS) do
args[i] = valFromTs(arg, name..':'..i) args[i] = valFromTs(arg, name..':'..i)
end end
return args return args
end end
local function arglistToTs(args) local function arglistToTs(args)
return map(args, valToTs) local argsS = {}
for i,v in ipairsNilable(args) do
table.insert(argsS, valToTs(v))
end
return argsS
end end
local function classFromForceTypeStr(name) local function classFromForceTypeStr(name)
local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$') local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$')
@@ -362,17 +387,19 @@ local tsObjectMeta = {
tsIsFunctionNs(rawget(t,'_tsNamespace'), name) or tsIsFunctionNs(rawget(t,'_tsNamespace'), name) or
tsIsFunctionNs(rawget(t,'_tsName'), name) tsIsFunctionNs(rawget(t,'_tsName'), name)
then then
return function(t, ...) return function(t2, ...)
local args = {...} if t2==nil or type(t2)~='table' or not t2._tsObjectId then
local argsS = arglistToTs(args) error('ts object method: be sure to use :func() not .func()', 2) end
return valFromTs( local argsS = arglistToTs({...})
_bllua_ts.callobj(rawget(t,'_tsObjectId'), name, unpack(argsS)), local res =
rawget(t,'_tsName') and rawget(t,'_tsName')..'::'..name, _bllua_ts.callobj(t2._tsObjectId, name, unpack(argsS))
rawget(t,'_tsNamespace')..'::'..name) return valFromTs(res,
t2._tsName and t2._tsName..'::'..name,
t2._tsNamespace..'::'..name)
end end
else else
return valFromTs( local res = _bllua_ts.getfield(rawget(t,'_tsObjectId'), name)
_bllua_ts.getfield(rawget(t,'_tsObjectId'), name), return valFromTs(res,
rawget(t,'_tsName') and rawget(t,'_tsName')..'.'..name, rawget(t,'_tsName') and rawget(t,'_tsName')..'.'..name,
rawget(t,'_tsNamespace')..'.'..name) rawget(t,'_tsNamespace')..'.'..name)
end end
@@ -437,7 +464,8 @@ local tsObjectMeta = {
local obj = toTsObject(_bllua_ts.callobj(t._tsObjectId, local obj = toTsObject(_bllua_ts.callobj(t._tsObjectId,
'getObject', tostring(idx))) 'getObject', tostring(idx)))
idx = idx+1 idx = idx+1
return idx-1, obj --return idx-1, obj
return obj
else else
return nil return nil
end end
@@ -544,14 +572,13 @@ end
local function safeNamespaceName(name) local function safeNamespaceName(name)
return tostring(name:gsub(':', '_')) return tostring(name:gsub(':', '_'))
end end
local nscallArgStr = '%a,%b,%c,%d,%e,%f,%g,%h'
bl._cachedNamespaceCalls = {} bl._cachedNamespaceCalls = {}
local function tsNamespacedCallTfname(name) local function tsNamespacedCallTfname(name)
local tfname = bl._cachedNamespaceCalls[name] local tfname = bl._cachedNamespaceCalls[name]
if not tfname then if not tfname then
tfname = '_bllua_nscall_'..safeNamespaceName(name) tfname = '_bllua_nscall_'..safeNamespaceName(name)
local tfcode = 'function '..tfname..'('..nscallArgStr..'){'.. local tfcode = 'function '..tfname..'('..tsArgsLocal..'){'..
name..'('..nscallArgStr..');}' name..'('..tsArgsLocal..');}'
_bllua_ts.eval(tfcode) _bllua_ts.eval(tfcode)
bl._cachedNamespaceCalls[name] = tfname bl._cachedNamespaceCalls[name] = tfname
end end
@@ -559,9 +586,9 @@ local function tsNamespacedCallTfname(name)
end end
local function tsCallGen(name) local function tsCallGen(name)
return function(...) return function(...)
local args = {...} local argsS = arglistToTs({...})
local argsS = arglistToTs(args) local res = _bllua_ts.call(name, unpack(argsS))
return valFromTs(_bllua_ts.call(name, unpack(argsS)), name) return valFromTs(res, name)
end end
end end
@@ -587,14 +614,16 @@ local tsMeta = {
if not rest:find('::') and tsIsFunctionNs(ns, rest) then if not rest:find('::') and tsIsFunctionNs(ns, rest) then
return tsCallGen(tsNamespacedCallTfname(name)) return tsCallGen(tsNamespacedCallTfname(name))
else else
return valFromTs(_bllua_ts.getvar(name), name) local res = _bllua_ts.getvar(name)
return valFromTs(res, name)
end end
elseif tsIsFunction(name) then elseif tsIsFunction(name) then
return tsCallGen(name) return tsCallGen(name)
elseif tsIsObject(name) then elseif tsIsObject(name) then
return toTsObject(name) return toTsObject(name)
else else
return valFromTs(_bllua_ts.getvar(name), name) local res = _bllua_ts.getvar(name)
return valFromTs(res, name)
end end
end end
end, end,
@@ -613,10 +642,12 @@ function bl.call(func, ...)
return _bllua_ts.call(func, unpack(argsS)) return _bllua_ts.call(func, unpack(argsS))
end end
function bl.eval(code) function bl.eval(code)
return valFromTs(_bllua_ts.eval(code)) local res = _bllua_ts.eval(code)
return valFromTs(res)
end end
function bl.exec(file) function bl.exec(file)
return valFromTs(_bllua_ts.call('exec', file)) local res = _bllua_ts.call('exec', file)
return valFromTs(res)
end end
function bl.array(name, ...) function bl.array(name, ...)
local rest = {...} local rest = {...}
@@ -645,12 +676,11 @@ end
-- Lua calling from TS -- Lua calling from TS
local luaLookup local luaLookup
luaLookup = function(tbl, name, set, val) luaLookup = function(tbl, name, set, val)
print('lookup', tbl, name, set, val)
if name:find('%.') then if name:find('%.') then
local first, rest = name:match('^([^%.:]+)%.(.+)$') local first, rest = name:match('^([^%.:]+)%.(.+)$')
if not isValidFuncName(first) then if not isValidFuncName(first) then
error('luaLookup: invalid name \''..tostring(first)..'\'', 3) end error('luaLookup: invalid name \''..tostring(first)..'\'', 3) end
if not tbl[first] then if tbl[first]==nil then
if set then tbl[first] = {} if set then tbl[first] = {}
else return nil end else return nil end
end end
@@ -661,9 +691,11 @@ luaLookup = function(tbl, name, set, val)
error('luacall: cannot have : or . after :', 3) end error('luacall: cannot have : or . after :', 3) end
if not isValidFuncName(first) then if not isValidFuncName(first) then
error('luacall: invalid name \''..tostring(first)..'\'', 3) end error('luacall: invalid name \''..tostring(first)..'\'', 3) end
if not isValidFuncName(rest) then
error('luacall: invalid method name \''..tostring(first)..'\'', 3) end
if not tbl[first] then if not tbl[first] then
error('luacall: no object named \''..rest..'\'', 3) end error('luacall: no object named \''..rest..'\'', 3) end
if not tbl[first][rest] then if tbl[first][rest]==nil then
error('luacall: no method named \''..rest..'\'', 3) end error('luacall: no method named \''..rest..'\'', 3) end
return function(...) return function(...)
tbl[first][rest](tbl[first], ...) tbl[first][rest](tbl[first], ...)
@@ -681,7 +713,7 @@ function _bllua_call(fname, ...)
local args = arglistFromTs(fname:lower(), {...}) -- todo: separate lua from ts func names? local args = arglistFromTs(fname:lower(), {...}) -- todo: separate lua from ts func names?
local func = luaLookup(_G, fname) local func = luaLookup(_G, fname)
if not func then if not func then
error('luacall: no global in lua named \''..name..'\'', 2) end error('luacall: no global in lua named \''..fname..'\'', 2) end
local res = func(unpack(args)) local res = func(unpack(args))
return valToTs(res) return valToTs(res)
end end
@@ -743,9 +775,8 @@ local function addCmd(cmd, func)
if not isValidFuncName(cmd) then if not isValidFuncName(cmd) then
error('addCmd: invalid function name \''..tostring(cmd)..'\'') end error('addCmd: invalid function name \''..tostring(cmd)..'\'') end
bl._cmds[cmd] = func bl._cmds[cmd] = func
local arglist = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p' _bllua_ts.eval('function '..cmd..'('..tsArgsLocal..'){'..
_bllua_ts.eval('function '..cmd..'('..arglist..'){'.. '_bllua_luacall(_bllua_process_cmd,"'..cmd..'",'..tsArgsLocal..');}')
'_bllua_luacall(_bllua_process_cmd,"'..cmd..'",'..arglist..');}')
end end
function bl.addServerCmd(name, func) function bl.addServerCmd(name, func)
name = name:lower() name = name:lower()
@@ -763,8 +794,9 @@ function bl.commandToServer(cmd, ...)
_bllua_ts.call('addTaggedString', cmd), _bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({...}))) unpack(arglistToTs({...})))
end end
function bl.commandToClient(cmd, ...) function bl.commandToClient(client, cmd, ...)
_bllua_ts.call('commandToClient', _bllua_ts.call('commandToClient',
valToTs(client),
_bllua_ts.call('addTaggedString', cmd), _bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({...}))) unpack(arglistToTs({...})))
end end
@@ -793,9 +825,6 @@ local function deactivatePackage(pkg)
_bllua_ts.call('deactivatePackage', pkg) _bllua_ts.call('deactivatePackage', pkg)
end end
end end
local hookNargs = 8
local hookArglistLocal = '%a,%b,%c,%d,%e,%f,%g,%h'
local hookArglistGlobal = '$_bllua_hook_arg1,$_bllua_hook_arg2,$_bllua_hook_arg3,$_bllua_hook_arg4,$_bllua_hook_arg5,$_bllua_hook_arg6,$_bllua_hook_arg7,$_bllua_hook_arg8'
bl._hooks = bl._hooks or {} bl._hooks = bl._hooks or {}
function _bllua_process_hook_before(pkgS, nameS, ...) function _bllua_process_hook_before(pkgS, nameS, ...)
local args = arglistFromTs(nameS, {...}) local args = arglistFromTs(nameS, {...})
@@ -809,7 +838,7 @@ function _bllua_process_hook_before(pkgS, nameS, ...)
_bllua_ts.setvar('_bllua_hook_abort', '1') _bllua_ts.setvar('_bllua_hook_abort', '1')
_bllua_ts.setvar('_bllua_hook_return', valToTs(args._return)) _bllua_ts.setvar('_bllua_hook_return', valToTs(args._return))
end end
for i=1,hookNargs do for i=1,tsMaxArgs do
_bllua_ts.setvar('_bllua_hook_arg'..i, valToTs(args[i])) _bllua_ts.setvar('_bllua_hook_arg'..i, valToTs(args[i]))
end end
end end
@@ -827,8 +856,8 @@ end
local function updateHook(pkg, name, hk) local function updateHook(pkg, name, hk)
local beforeCode = hk.before and local beforeCode = hk.before and
('_bllua_luacall("_bllua_process_hook_before","'..pkg..'","'..name.. ('_bllua_luacall("_bllua_process_hook_before","'..pkg..'","'..name..
'",'..hookArglistLocal..');') or '' '",'..tsArgsLocal..');') or ''
local arglist = (hk.before and hookArglistGlobal or hookArglistLocal) local arglist = (hk.before and tsArgsGlobal or tsArgsLocal)
local parentCode = local parentCode =
tsIsFunctionNsname(name) and -- only call parent if it exists tsIsFunctionNsname(name) and -- only call parent if it exists
(hk.before and (hk.before and
@@ -841,10 +870,11 @@ local function updateHook(pkg, name, hk)
arglist..');') or '' arglist..');') or ''
local code = local code =
'package '..pkg..'{'.. 'package '..pkg..'{'..
'function '..name..'('..hookArglistLocal..'){'.. 'function '..name..'('..tsArgsLocal..'){'..
beforeCode..parentCode..afterCode.. beforeCode..parentCode..afterCode..
'}'.. '}'..
'};' '};'
print('bl.hook eval output: [['..code..']]')
_bllua_ts.eval(code) _bllua_ts.eval(code)
end end
function bl.hook(pkg, name, time, func) function bl.hook(pkg, name, time, func)
@@ -870,9 +900,9 @@ end
function bl.unhook(pkg, name, time) function bl.unhook(pkg, name, time)
if not isValidFuncName(pkg) then if not isValidFuncName(pkg) then
error('bl.unhook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end error('bl.unhook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end
if not isValidFuncNameNs(name) then if name and not isValidFuncNameNs(name) then
error('bl.unhook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end error('bl.unhook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end
if time~='before' and time~='after' then if time and time~='before' and time~='after' then
error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2) end error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2) end
if not name then if not name then
@@ -899,10 +929,13 @@ function bl.unhook(pkg, name, time)
if time~='before' and time~='after' then if time~='before' and time~='after' then
error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2) end error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2) end
bl._hooks[pkg][name][time] = nil bl._hooks[pkg][name][time] = nil
if tableEmpty(bl._hooks[pkg][name]) and tableEmpty(bl._hooks[pkg]) then if tableEmpty(bl._hooks[pkg][name]) then
bl._hooks[pkg][name] = nil
end
if tableEmpty(bl._hooks[pkg]) then
bl._hooks[pkg] = nil bl._hooks[pkg] = nil
deactivatePackage(pkg)
updateHook(pkg, name, {}) updateHook(pkg, name, {})
deactivatePackage(pkg)
else else
updateHook(pkg, name, bl._hooks[pkg][name]) updateHook(pkg, name, bl._hooks[pkg][name])
end end
@@ -958,7 +991,7 @@ function bl.raycast(start, stop, mask, ignores)
local stopS = vecToTs(start) local stopS = vecToTs(start)
local maskS = maskToTs(mask) local maskS = maskToTs(mask)
local ignoresS = {} local ignoresS = {}
for _,v in ipairs(ignores) do for _,v in ipairsNilable(ignores) do
table.insert(ignoresS, objToTs(v)) table.insert(ignoresS, objToTs(v))
end end
@@ -1007,7 +1040,7 @@ end
local maxTsArgLen = 8192 local maxTsArgLen = 8192
local function valsToString(vals) local function valsToString(vals)
local strs = {} local strs = {}
for i,v in ipairs(vals) do for i,v in ipairsNilable(vals) do
local tstr = table.tostring(v) local tstr = table.tostring(v)
if #tstr>maxTsArgLen then if #tstr>maxTsArgLen then
tstr = tostring(v) tstr = tostring(v)

View File

@@ -1,3 +1,4 @@
-- This Lua code provides some built-in utilities for writing Lua add-ons -- This Lua code provides some built-in utilities for writing Lua add-ons
-- It is eval'd automatically once BLLua3 has loaded the TS API and environment -- It is eval'd automatically once BLLua3 has loaded the TS API and environment
-- It only has access to the sandboxed lua environment, just like user code. -- It only has access to the sandboxed lua environment, just like user code.
@@ -6,10 +7,7 @@ ts = _bllua_ts
-- Provide limited OS functions -- Provide limited OS functions
os = os or {} os = os or {}
---@diagnostic disable-next-line: duplicate-set-field
function os.time() return math.floor(tonumber(_bllua_ts.call('getSimTime'))/1000) end function os.time() return math.floor(tonumber(_bllua_ts.call('getSimTime'))/1000) end
---@diagnostic disable-next-line: duplicate-set-field
function os.clock() return tonumber(_bllua_ts.call('getSimTime'))/1000 end function os.clock() return tonumber(_bllua_ts.call('getSimTime'))/1000 end
-- Virtual file class, emulating a file object as returned by io.open -- Virtual file class, emulating a file object as returned by io.open
@@ -84,26 +82,24 @@ local function new_file_obj(fn)
return file return file
end end
local function tflip(t) local function tflip(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end
local u = {}; for _, n in ipairs(t) do u[n] = true end; return u;
end
local allowed_zip_dirs = tflip{ local allowed_zip_dirs = tflip{
'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders' 'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders'
} }
local function io_open_absolute(fn, mode) local function io_open_absolute(fn, mode)
-- if file exists, use original mode -- if file exists, use original mode
local res, err = _bllua_io_open(fn, mode) local res, err = _bllua_io_open(fn, mode)
if res then return res end if res then return res
elseif err and not err:find('No such file or directory$') then return nil, err end
-- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader -- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader
local dir = fn:match('^[^/]+') local dir = fn:match('^[^/]+')
if not allowed_zip_dirs[dir:lower()] then return nil, 'File is not in one of the allowed directories' end if not allowed_zip_dirs[dir:lower()] then return nil, 'Zip is not in one of the allowed directories' end
local exist = _bllua_ts.call('isFile', fn) == '1' local exist = _bllua_ts.call('isFile', fn) == '1'
if not exist then return nil, err end if not exist then return nil, err end
if mode~=nil and mode~='r' and mode~='rb' then if mode~=nil and mode~='r' and mode~='rb' then
return nil, 'Files in zips can only be opened in read mode' return nil, 'Files in zips can only be opened in read mode' end
end
-- return a temp lua file object with the data -- return a temp lua file object with the data
local fi = new_file_obj(fn) local fi = new_file_obj(fn)
@@ -111,7 +107,6 @@ local function io_open_absolute(fn, mode)
end end
io = io or {} io = io or {}
---@diagnostic disable-next-line: duplicate-set-field
function io.open(fn, mode, errn) function io.open(fn, mode, errn)
errn = errn or 1 errn = errn or 1
@@ -132,19 +127,13 @@ function io.open(fn, mode, errn)
return fi, err, fn return fi, err, fn
end end
end end
---@diagnostic disable-next-line: duplicate-set-field
function io.lines(fn) function io.lines(fn)
local fi, err, fn2 = io.open(fn, nil, 2) local fi, err, fn2 = io.open(fn, nil, 2)
if not fi then error('Error opening file \''..fn2..'\': '..err, 2) end if not fi then error('Error opening file \''..fn2..'\': '..err, 2) end
return fi:lines() return fi:lines()
end end
---@diagnostic disable-next-line: duplicate-set-field
function io.type(f) function io.type(f)
---@diagnostic disable-next-line: undefined-field
if type(f)=='table' and f._is_file then if type(f)=='table' and f._is_file then
---@diagnostic disable-next-line: undefined-field
return f._is_open and 'file' or 'closed file' return f._is_open and 'file' or 'closed file'
else else
return _bllua_io_type(f) return _bllua_io_type(f)
@@ -210,8 +199,7 @@ local function isValidCode(code)
end end
function _bllua_smarteval(code) function _bllua_smarteval(code)
if (not code:find('^print%(')) and isValidCode('print('..code..')') then if (not code:find('^print%(')) and isValidCode('print('..code..')') then
code = 'print(' .. code .. ')' code = 'print('..code..')' end
end
local f,e = loadstring(code) local f,e = loadstring(code)
if f then if f then
return f() return f()

View File

@@ -1,3 +1,4 @@
-- todo -- todo
-- Matrix class with math operators -- Matrix class with math operators

View File

@@ -1,24 +1,33 @@
-- Basic functionality that should be standard in Lua -- Basic functionality that should be standard in Lua
-- Table / List -- Table / List
-- Whether a table contains no keys -- Whether a table contains no keys
function table.empty(t) function table.empty(t)
return next(t) ~= nil return next(t)==nil
end end
-- Apply a function to each key in a table -- Apply a function to each key in a table
function table.map(f, ...) function table.map(f, ...)
local ts = {...} local ts = {...}
local u = {} local u = {}
for k,_ in pairs(ts[1]) do for k,_ in pairs(ts[1]) do
local args = {} local args = {}
for j = 1, #ts do args[j] = ts[j][i] end for j=1,#ts do args[j] = ts[j][k] end
u[i] = f(unpack(args)) u[k] = f(unpack(args))
end
return u
end
function table.mapk(f, ...)
local ts = {...}
local u = {}
for k,_ in pairs(ts[1]) do
local args = {}
for j=1,#ts do args[j] = ts[j][k] end
u[k] = f(k, unpack(args))
end end
return u return u
end end
function table.map_list(f, ...) function table.map_list(f, ...)
local ts = {...} local ts = {...}
local u = {} local u = {}
@@ -29,21 +38,28 @@ function table.map_list(f, ...)
end end
return u return u
end end
function table.mapi_list(f, ...)
local ts = {...}
local u = {}
for i=1,#ts[1] do
local args = {}
for j=1,#ts do args[j] = ts[j][i] end
u[i] = f(i, unpack(args))
end
return u
end
-- Swap keys/values -- Swap keys/values
function table.swap(t) function table.swap(t)
local u = {} local u = {}
for k,v in pairs(t) do u[v] = k end for k,v in pairs(t) do u[v] = k end
return u return u
end end
-- Reverse a list -- Reverse a list
function table.reverse(l) function table.reverse(l)
local m = {} local m = {}
for i=1,#l do m[#l-i+1] = l[i] end for i=1,#l do m[#l-i+1] = l[i] end
return m return m
end end
-- Whether a table is a list/array (has only monotonic integer keys) -- Whether a table is a list/array (has only monotonic integer keys)
function table.islist(t) function table.islist(t)
local n = 0 local n = 0
@@ -53,7 +69,6 @@ function table.islist(t)
end end
return n==#t return n==#t
end end
-- Append contents of other tables to first table -- Append contents of other tables to first table
function table.append(t, ...) function table.append(t, ...)
local a = {...} local a = {...}
@@ -62,7 +77,6 @@ function table.append(t, ...)
end end
return t return t
end end
-- Create a new table containing all keys from any number of tables -- Create a new table containing all keys from any number of tables
-- latter tables in the arg list override prior ones -- latter tables in the arg list override prior ones
-- overlaps, NOT appends, integer keys -- overlaps, NOT appends, integer keys
@@ -74,7 +88,6 @@ function table.join(...)
end end
return w return w
end end
-- Whether a table contains a certain value in any key -- Whether a table contains a certain value in any key
function table.contains(t,s) function table.contains(t,s)
for _,v in pairs(t) do for _,v in pairs(t) do
@@ -82,34 +95,29 @@ function table.contains(t, s)
end end
return false return false
end end
function table.contains_list(t,s) function table.contains_list(t,s)
for _,v in ipairs(t) do for _,v in ipairs(t) do
if v==s then return true end if v==s then return true end
end end
return false return false
end end
-- Copy a table to another table -- Copy a table to another table
function table.copy(t) function table.copy(t)
local u = {} local u = {}
for k,v in pairs(t) do u[k] = v end for k,v in pairs(t) do u[k] = v end
return u return u
end end
function table.copy_list(l) function table.copy_list(l)
local m = {} local m = {}
for i,v in ipairs(l) do m[i] = v end for i,v in ipairs(l) do m[i] = v end
return m return m
end end
-- Sort a table in a new copy -- Sort a table in a new copy
function table.sortcopy(t, f) function table.sortcopy(t, f)
local u = table.copy_list(t) local u = table.copy_list(t)
table.sort(u, f) table.sort(u, f)
return u return u
end end
-- Remove a value from a table -- Remove a value from a table
function table.removevalue(t, r) function table.removevalue(t, r)
local rem = {} local rem = {}
@@ -118,7 +126,6 @@ function table.removevalue(t, r)
end end
for _,k in ipairs(rem) do t[k] = nil end for _,k in ipairs(rem) do t[k] = nil end
end end
function table.removevalue_list(t, r) function table.removevalue_list(t, r)
for i = #t, 1, -1 do for i = #t, 1, -1 do
if t[i]==r then if t[i]==r then
@@ -126,7 +133,6 @@ function table.removevalue_list(t, r)
end end
end end
end end
-- Export tables into formatted executable strings -- Export tables into formatted executable strings
local function tabs(tabLevel) local function tabs(tabLevel)
return (' '):rep(tabLevel) return (' '):rep(tabLevel)
@@ -193,13 +199,14 @@ valueToString = function(v, tabLevel, seen)
return tostring(v) return tostring(v)
else else
--error('table.tostring: table contains a '..t..' value, cannot serialize') --error('table.tostring: table contains a '..t..' value, cannot serialize')
return 'nil --[[ cannot serialize ' .. tostring(v) .. ' ]]' return 'nil --[[ '..tostring(v)..' ]]'
end end
end end
function table.tostring(t) function table.tostring(t)
return tableToString(t, 0, {}) return tableToString(t, 0, {})
end end
-- String -- String
-- Split string into table by separator -- Split string into table by separator
@@ -207,8 +214,7 @@ end
-- if regex is not true, sep is treated as a regex pattern -- if regex is not true, sep is treated as a regex pattern
function string.split(str, sep, noregex) function string.split(str, sep, noregex)
if type(str)~='string' then if type(str)~='string' then
error('string.split: argument #1: expected string, got ' .. type(str), 2) error('string.split: argument #1: expected string, got '..type(str), 2) end
end
if sep==nil or sep=='' then if sep==nil or sep=='' then
local t = {} local t = {}
local ns = #str local ns = #str
@@ -234,7 +240,6 @@ function string.split(str, sep, noregex)
'string.split: argument #2: expected string or nil, got '..type(sep), 2) 'string.split: argument #2: expected string or nil, got '..type(sep), 2)
end end
end end
-- Split string to a list of char bytes -- Split string to a list of char bytes
function string.bytes(s) function string.bytes(s)
local b = {} local b = {}
@@ -244,13 +249,11 @@ function string.bytes(s)
end end
return b return b
end end
-- Trim leading and trailing whitespace -- Trim leading and trailing whitespace
function string.trim(s, ws) function string.trim(s, ws)
ws = ws or ' \t\r\n' ws = ws or ' \t\r\n'
return s:gsub('^['..ws..']+', ''):gsub('['..ws..']+$', '')..'' return s:gsub('^['..ws..']+', ''):gsub('['..ws..']+$', '')..''
end end
-- String slicing and searching using [] operator -- String slicing and searching using [] operator
local str_meta = getmetatable('') local str_meta = getmetatable('')
local str_meta_index_old= str_meta.__index local str_meta_index_old= str_meta.__index
@@ -266,20 +269,15 @@ function str_meta.__index(s, k)
return string.sub(s,a,b) return string.sub(s,a,b)
end end
end end
-- String iterator -- String iterator
function string.chars(s) function string.chars(s)
local i = 0 local i = 0
return function() return function()
i = i+1 i = i+1
if i <= #s then if i<=#s then return s:sub(i,i)
return s:sub(i, i) else return nil end
else
return nil
end end
end end
end
-- Escape sequences -- Escape sequences
local defaultEscapes = { local defaultEscapes = {
['\\'] = '\\\\', ['\\'] = '\\\\',
@@ -299,7 +297,6 @@ function string.escape(s, escapes)
end end
return table.concat(t) return table.concat(t)
end end
local defaultEscapeChar = '\\' local defaultEscapeChar = '\\'
local defaultUnescapes = { local defaultUnescapes = {
['\\'] = '\\', ['\\'] = '\\',
@@ -330,6 +327,7 @@ function string.unescape(s, escapeChar, unescapes)
return table.concat(t) return table.concat(t)
end end
-- IO -- IO
io = io or {} io = io or {}
@@ -341,7 +339,6 @@ function io.readall(filename)
fi:close() fi:close()
return s return s
end end
-- Write data to file all at once, return true if success / false,err if failure -- Write data to file all at once, return true if success / false,err if failure
function io.writeall(filename, data) function io.writeall(filename, data)
local fi,err = io.open(filename, 'wb') local fi,err = io.open(filename, 'wb')
@@ -351,26 +348,23 @@ function io.writeall(filename, data)
return true,nil return true,nil
end end
-- Math -- Math
-- Round -- Round
function math.round(x) function math.round(x)
return math.floor(x+0.5) return math.floor(x+0.5)
end end
-- Mod that accounts for floating point inaccuracy -- Mod that accounts for floating point inaccuracy
function math.mod(a,b) function math.mod(a,b)
local m = a%b local m = a%b
if m == 0 or math.abs(m) < 1e-15 or math.abs(m - b) < 1e-15 then if m==0 or math.abs(m)<1e-15 or math.abs(m-b)<1e-15 then return 0
return 0 else return m end
else
return m
end end
end
-- Clamp value between min and max -- Clamp value between min and max
function math.clamp(v, n, x) function math.clamp(v, n, x)
return math.min(x, math.max(v, n)) return math.min(x, math.max(v, n))
end end
print(' Executed std.lua') print(' Executed std.lua')

View File

@@ -1,19 +1,18 @@
-- Vector math class with operators -- Vector math class with operators
local vector_meta local vector_meta
local vector_new local vector_new
local function vector_check(v, n, name, argn) local function vector_check(v, n, name, argn)
if not v.__is_vector then if not v.__is_vector then
error('vector '..name..': argument #'..(argn or 1) error('vector '..name..': argument #'..(argn or 1)
.. ': expected vector, got ' .. type(v), n + 1) ..': expected vector, got '..type(v), n+1) end
end
end end
local function vector_checksamelen(v1, v2, name) local function vector_checksamelen(v1, v2, name)
vector_check(v1, 3, name, 1) vector_check(v1, 3, name, 1)
vector_check(v2, 3, name, 2) vector_check(v2, 3, name, 2)
if #v1~=#v2 then if #v1~=#v2 then
error('vector '..name..': vector lengths do not match (lengths are ' error('vector '..name..': vector lengths do not match (lengths are '
.. #v1 .. ' and ' .. #v2 .. ')', 3) ..#v1..' and '..#v2..')', 3) end
end
return #v1 return #v1
end end
local function vector_checklen(v1, v2, name, len) local function vector_checklen(v1, v2, name, len)
@@ -21,8 +20,7 @@ local function vector_checklen(v1, v2, name, len)
vector_check(v2, 3, name, 2) vector_check(v2, 3, name, 2)
if #v1~=len or #v2~=len then if #v1~=len or #v2~=len then
error('vector '..name..': vector lengths are not '..len..' (lengths are ' error('vector '..name..': vector lengths are not '..len..' (lengths are '
.. #v1 .. ' and ' .. #v2 .. ')', 3) ..#v1..' and '..#v2..')', 3) end
end
end end
local function vector_opnnn(name, op) local function vector_opnnn(name, op)
return function(v1, v2) return function(v1, v2)
@@ -71,21 +69,15 @@ local vector_indices = { x = 1, y = 2, z = 3, w = 4, r = 1, g = 2, b = 3, a = 4
local vector_meta = { local vector_meta = {
__is_vector = true, __is_vector = true,
__index = function(t, k) __index = function(t, k)
if tonumber(k) then if tonumber(k) then return rawget(t, k)
return rawget(t, k) elseif vector_indices[k] then return rawget(t, vector_indices[k])
elseif vector_indices[k] then else return getmetatable(t)[k]
return rawget(t, vector_indices[k])
else
return getmetatable(t)[k]
end end
end, end,
__newindex = function(t, k, v) __newindex = function(t, k, v)
if tonumber(k) then if tonumber(k) then rawset(t, k, v)
rawset(t, k, v) elseif vector_indices[k] then rawset(t, vector_indices[k], v)
elseif vector_indices[k] then else return
rawset(t, vector_indices[k], v)
else
return
end end
end, end,
__add = vector_opnnn('add', function(x1, x2) return x1+x2 end), __add = vector_opnnn('add', function(x1, x2) return x1+x2 end),
@@ -123,11 +115,8 @@ local vector_meta = {
local len = #v1 local len = #v1
local v3 = {} local v3 = {}
for i = 1, len do for i = 1, len do
if length == 0 then if length==0 then v3[i] = 0
v3[i] = 0 else v3[i] = v1[i]/length end
else
v3[i] = v1[i] / length
end
end end
return vector_new(v3) return vector_new(v3)
end, end,
@@ -163,28 +152,20 @@ local vector_meta = {
rotateByAngleId = function(v1, r) rotateByAngleId = function(v1, r)
--vector_check(v1, 2, 'rotate') --vector_check(v1, 2, 'rotate')
if type(r)~='number' or r%1~=0 then if type(r)~='number' or r%1~=0 then
error('vector rotateByAngleId: invalid rotation ' .. tostring(r), 2) error('vector rotateByAngleId: invalid rotation '..tostring(r), 2) end
end
r = r%4 r = r%4
local v2 local v2
if r == 0 then if r==0 then v2 = vector_new{ v1[1], v1[2], v1[3] }
v2 = vector_new { v1[1], v1[2], v1[3] } elseif r==1 then v2 = vector_new{ v1[2], -v1[1], v1[3] }
elseif r == 1 then elseif r==2 then v2 = vector_new{ -v1[1], -v1[2], v1[3] }
v2 = vector_new { v1[2], -v1[1], v1[3] } elseif r==3 then v2 = vector_new{ -v1[2], v1[1], v1[3] }
elseif r == 2 then else error('vector rotateByAngleId: invalid rotation '..r, 2) end
v2 = vector_new { -v1[1], -v1[2], v1[3] }
elseif r == 3 then
v2 = vector_new { -v1[2], v1[1], v1[3] }
else
error('vector rotateByAngleId: invalid rotation ' .. r, 2)
end
return v2 return v2
end, end,
rotateZ = function(v, r) rotateZ = function(v, r)
--vector_check(v, 2, 'rotate2d') --vector_check(v, 2, 'rotate2d')
if type(r)~='number' then if type(r)~='number' then
error('vector rotateZ: invalid rotation ' .. tostring(r), 2) error('vector rotateZ: invalid rotation '..tostring(r), 2) end
end
local len = math.sqrt(v[1]^2 + v[2]^2) local len = math.sqrt(v[1]^2 + v[2]^2)
local ang = math.atan2(v[2], v[1]) + r local ang = math.atan2(v[2], v[1]) + r
local v2 = vector_new{ math.cos(ang)*len, math.sin(ang)*len } local v2 = vector_new{ math.cos(ang)*len, math.sin(ang)*len }