mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
feat(baselib): gmatch, table.removemulti, math hyperbolics, difftime, newproxy, scrub
The base-library names that 1.12.1's Lua 5.0 and its compat.lua do not have: - gmatch: the global alias of string.gmatch (5.0's str_gfind, 0x7FCFA0), set by value like strmatch and strrev. - table.removemulti(t [, pos [, count]]): removes count elements from pos, shifts the rest down with lua_rawgeti/lua_rawseti and returns the removed elements. 5.0 keeps the length apart from the keys, so like the engine's table.remove (0x7FB770) it records the new length with luaL_setn (0x6F4EA0). - math.cosh, math.sinh, math.tanh: luaL_checknumber (0x6F4C80) and the C function. - difftime(t2 [, t1]): C difftime over the two times, next to the engine's date and time globals. - newproxy([false | true | proxy]): an empty userdata from lua_newuserdata. The closure's upvalue is a weak table (its own metatable, __mode "kv") that marks the metatables newproxy(true) makes, so only those can be shared. - scrub(...): replaces each argument that is not a boolean, number or string with nil, in place with lua_replace (0x6F32B0). All are registered on both Lua states. Game::Lua gains RawSetI (lua_rawseti, 0x6F3F60), Replace, CheckLString (luaL_checklstring, 0x6F4BE0), CheckNumber and OptNumber (luaL_optnumber, 0x6F4CD0); string.byte now uses them instead of local typedefs. issecure, issecurevariable and forceinsecure are left out. 1.12.1 has no taint, so they could only return constants, and a constant "secure" from issecurevariable makes AceHook-3.0 refuse every plain hook.
This commit is contained in:
+70
-1
@@ -673,12 +673,16 @@ build instructions.
|
||||
- [`getfenv` / `setfenv` environment protection](#getfenv--setfenv-environment-protection)
|
||||
- [`select(index, ...)`](#selectindex-)
|
||||
- [`unpack(list [, i [, j]])`](#unpacklist--i--j)
|
||||
- [`newproxy([proxy])`](#newproxyproxy)
|
||||
- [`scrub(...)`](#scrub)
|
||||
- [`difftime(time2 [, time1])`](#difftimetime2--time1)
|
||||
- [`xpcall(f, msgh [, arg1, ...])`](#xpcallf-msgh--arg1-)
|
||||
- [`GetCallstackHeight()` / `GetErrorCallstackHeight()` / `SetErrorCallstackHeight([height])`](#getcallstackheight--geterrorcallstackheight--seterrorcallstackheightheight)
|
||||
- [`collectgarbage(opt [, arg])`](#collectgarbageopt--arg)
|
||||
- [`table.wipe(t)` / `wipe(t)`](#tablewipet--wipet)
|
||||
- [`table.count(tbl)`](#tablecounttbl)
|
||||
- [`table.maxn(t)`](#tablemaxnt)
|
||||
- [`table.removemulti(t [, pos [, count]])`](#tableremovemultit--pos--count)
|
||||
- [Stale table lengths (5.1 healing)](#stale-table-lengths-51-healing)
|
||||
- [`Mixin(object, ...)` / `CreateFromMixins(...)`](#mixinobject---createfrommixins)
|
||||
- [`string.match` / `string.gmatch`](#stringmatch--stringgmatch)
|
||||
@@ -696,6 +700,7 @@ build instructions.
|
||||
- [`math.fmod(x, y)`](#mathfmodx-y)
|
||||
- [`math.modf(x)`](#mathmodfx)
|
||||
- [`math.huge`](#mathhuge)
|
||||
- [`math.cosh(x)` / `math.sinh(x)` / `math.tanh(x)`](#mathcoshx--mathsinhx--mathtanhx)
|
||||
- [`coroutine.create(fn)`](#coroutinecreatefn)
|
||||
- [`coroutine.resume(co, ...)`](#coroutineresumeco-)
|
||||
- [`coroutine.yield(...)`](#coroutineyield)
|
||||
@@ -16032,6 +16037,44 @@ unpack({10, 20, 30}, 2, 2) -- 20
|
||||
length, so a vararg `arg` table's `n` field is honored and embedded nils
|
||||
keep their slots. Errors on a non-table first argument, exactly like 5.1.
|
||||
|
||||
### `newproxy([proxy])`
|
||||
|
||||
Returns a new empty userdata. The argument sets its metatable:
|
||||
|
||||
- `false` or no argument: the userdata has no metatable.
|
||||
- `true`: the userdata gets a new empty metatable. Use `getmetatable` to get
|
||||
this metatable and add metamethods to it.
|
||||
- A userdata from `newproxy(true)`: the new userdata shares the metatable of
|
||||
that userdata.
|
||||
|
||||
Any other argument causes the error `boolean or proxy expected`.
|
||||
|
||||
```lua
|
||||
local proxy = newproxy(true)
|
||||
getmetatable(proxy).__gc = function() print("collected") end
|
||||
local twin = newproxy(proxy) -- same metatable as proxy
|
||||
```
|
||||
|
||||
### `scrub(...)`
|
||||
|
||||
Returns its arguments. Each argument that is not a boolean, a number or a
|
||||
string becomes `nil`.
|
||||
|
||||
```lua
|
||||
scrub(1, "a", true, {}, print) -- 1, "a", true, nil, nil
|
||||
```
|
||||
|
||||
### `difftime(time2 [, time1])`
|
||||
|
||||
Returns the number of seconds from `time1` to `time2`. `time1` is 0 by
|
||||
default. Both values are times in seconds, as `time()` returns.
|
||||
|
||||
```lua
|
||||
local start = time()
|
||||
-- ...
|
||||
difftime(time(), start) -- the seconds since start
|
||||
```
|
||||
|
||||
### `xpcall(f, msgh [, arg1, ...])`
|
||||
|
||||
Calls `f` under the message handler `msgh`, passing the trailing arguments
|
||||
@@ -16167,6 +16210,21 @@ table.maxn({ a = 1 }) -- 0
|
||||
|
||||
Errors on non-table input.
|
||||
|
||||
### `table.removemulti(t [, pos [, count]])`
|
||||
|
||||
Removes `count` elements from `t`, starting at position `pos`, and returns
|
||||
the removed elements in order. The elements after them move down to fill the
|
||||
gap. `pos` is the last position by default, and `count` is 1 by default.
|
||||
|
||||
If `t` is empty, the function returns no values. If the range is not inside
|
||||
the table, the function causes the error `parameters out of bounds`.
|
||||
|
||||
```lua
|
||||
local t = { "a", "b", "c", "d", "e" }
|
||||
table.removemulti(t, 2, 3) -- "b", "c", "d"
|
||||
-- t is now { "a", "e" }
|
||||
```
|
||||
|
||||
### Stale table lengths (5.1 healing)
|
||||
|
||||
Lua 5.0 stores a table's `table.insert` / `table.getn` length **out of
|
||||
@@ -16253,7 +16311,8 @@ for word in string.gmatch("a,bb,ccc", "[^,]+") do print(word) end -- a / bb / cc
|
||||
Both call forms work: `string.match(s, p)` and `s:match(p)`. See
|
||||
[String methods](#string-methods-supper-sformat). `string.match` is also the
|
||||
global `strmatch`, as on 3.x clients, alongside 1.12's own `strfind`, `strsub`
|
||||
and the other globals for the 5.0 string functions.
|
||||
and the other globals for the 5.0 string functions. `string.gmatch` is also the
|
||||
global `gmatch`.
|
||||
|
||||
### `string.gsub` replacements
|
||||
|
||||
@@ -16486,6 +16545,16 @@ local best = math.huge
|
||||
for _, v in ipairs(costs) do best = math.min(best, v) end
|
||||
```
|
||||
|
||||
### `math.cosh(x)` / `math.sinh(x)` / `math.tanh(x)`
|
||||
|
||||
The hyperbolic cosine, sine and tangent of `x`.
|
||||
|
||||
```lua
|
||||
math.cosh(0) -- 1
|
||||
math.sinh(0) -- 0
|
||||
math.tanh(1) -- 0.76159415595576
|
||||
```
|
||||
|
||||
### `coroutine.*`
|
||||
|
||||
The Lua 5.1 `coroutine` library: `create`, `resume`, `running`,
|
||||
|
||||
@@ -73,12 +73,14 @@ namespace Lua {
|
||||
F(GetTable, lua_gettable, LUA_GET_TABLE) \
|
||||
F(RawGet, lua_rawget, LUA_RAW_GET) \
|
||||
F(RawGetI, lua_rawgeti, LUA_RAWGETI) \
|
||||
F(RawSetI, lua_rawseti, LUA_RAW_SET_I) \
|
||||
F(SetTable, lua_settable, LUA_SET_TABLE) \
|
||||
F(RawSet, lua_rawset, LUA_RAW_SET) \
|
||||
F(GetMetatable,lua_getmetatable,LUA_GET_METATABLE)\
|
||||
F(SetMetatable,lua_setmetatable,LUA_SET_METATABLE)\
|
||||
F(Insert, lua_insert, LUA_INSERT) \
|
||||
F(Remove, lua_remove, LUA_REMOVE) \
|
||||
F(Replace, lua_replace, LUA_REPLACE) \
|
||||
F(GetTop, lua_gettop, LUA_GET_TOP) \
|
||||
F(SetTop, lua_settop, LUA_SET_TOP) \
|
||||
F(Call, lua_call, LUA_CALL) \
|
||||
@@ -98,6 +100,9 @@ namespace Lua {
|
||||
F(Resume, lua_resume, LUA_RESUME) \
|
||||
F(Yield, lua_yield, LUA_YIELD) \
|
||||
F(ArgError, luaL_argerror, LUAL_ARG_ERROR) \
|
||||
F(CheckLString,luaL_checklstring,LUAL_CHECK_LSTRING)\
|
||||
F(CheckNumber, luaL_checknumber,LUAL_CHECK_NUMBER)\
|
||||
F(OptNumber, luaL_optnumber, LUAL_OPT_NUMBER) \
|
||||
F(SetN, luaL_setn, LUAL_SETN) \
|
||||
F(Ref, luaL_ref, LUA_REF_REF) \
|
||||
F(Unref, luaL_unref, LUA_REF_UNREF) \
|
||||
|
||||
+14
@@ -270,6 +270,8 @@ using lua_rawget_t = void(__fastcall *)(void *L, int idx);
|
||||
// Pushes t[n] for the table at idx, raw (no metamethods). With idx =
|
||||
// REGISTRY_INDEX it pushes a value stored under an integer ref.
|
||||
using lua_rawgeti_t = void(__fastcall *)(void *L, int idx, int n);
|
||||
// Pops the top value into t[n] for the table at idx, raw.
|
||||
using lua_rawseti_t = void(__fastcall *)(void *L, int idx, int n);
|
||||
using lua_settable_t = void(__fastcall *)(void *L, int idx);
|
||||
using lua_rawset_t = void(__fastcall *)(void *L, int idx);
|
||||
// Pushes the metatable of the value at idx and returns 1, or pushes
|
||||
@@ -280,6 +282,8 @@ using lua_getmetatable_t = int(__fastcall *)(void *L, int idx);
|
||||
using lua_setmetatable_t = int(__fastcall *)(void *L, int idx);
|
||||
using lua_insert_t = void(__fastcall *)(void *L, int idx);
|
||||
using lua_remove_t = void(__fastcall *)(void *L, int idx);
|
||||
// Pops the top value into slot idx.
|
||||
using lua_replace_t = void(__fastcall *)(void *L, int idx);
|
||||
using lua_gettop_t = int(__fastcall *)(void *L);
|
||||
using lua_settop_t = void(__fastcall *)(void *L, int idx);
|
||||
using lua_call_t = void(__fastcall *)(void *L, int nargs, int nresults);
|
||||
@@ -313,6 +317,11 @@ using lua_newthread_t = void *(__fastcall *)(void *L);
|
||||
using lua_resume_t = int(__fastcall *)(void *L, int nargs);
|
||||
using lua_yield_t = int(__fastcall *)(void *L, int nresults);
|
||||
using luaL_argerror_t = void(__fastcall *)(void *L, int narg, const char *msg);
|
||||
// The auxlib argument readers: each raises the engine's own "bad argument"
|
||||
// error. CheckLString's `len` may be null.
|
||||
using luaL_checklstring_t = const char *(__fastcall *)(void *L, int narg, unsigned int *len);
|
||||
using luaL_checknumber_t = double(__fastcall *)(void *L, int narg);
|
||||
using luaL_optnumber_t = double(__fastcall *)(void *L, int narg, double def);
|
||||
using luaL_setn_t = void(__fastcall *)(void *L, int t, int n);
|
||||
// Pops the top value into the table at `t` under a fresh integer key and
|
||||
// returns the key (LUA_REFNIL, -1, for a nil). With REGISTRY_INDEX it anchors
|
||||
@@ -358,12 +367,14 @@ extern const lua_newtable_t NewTable;
|
||||
extern const lua_gettable_t GetTable;
|
||||
extern const lua_rawget_t RawGet;
|
||||
extern const lua_rawgeti_t RawGetI;
|
||||
extern const lua_rawseti_t RawSetI;
|
||||
extern const lua_settable_t SetTable;
|
||||
extern const lua_rawset_t RawSet;
|
||||
extern const lua_getmetatable_t GetMetatable;
|
||||
extern const lua_setmetatable_t SetMetatable;
|
||||
extern const lua_insert_t Insert;
|
||||
extern const lua_remove_t Remove;
|
||||
extern const lua_replace_t Replace;
|
||||
extern const lua_gettop_t GetTop;
|
||||
extern const lua_settop_t SetTop;
|
||||
extern const lua_call_t Call;
|
||||
@@ -383,6 +394,9 @@ extern const lua_newthread_t NewThread;
|
||||
extern const lua_resume_t Resume;
|
||||
extern const lua_yield_t Yield;
|
||||
extern const luaL_argerror_t ArgError;
|
||||
extern const luaL_checklstring_t CheckLString;
|
||||
extern const luaL_checknumber_t CheckNumber;
|
||||
extern const luaL_optnumber_t OptNumber;
|
||||
extern const luaL_setn_t SetN;
|
||||
extern const luaL_ref_t Ref;
|
||||
extern const luaL_unref_t Unref;
|
||||
|
||||
@@ -8360,6 +8360,9 @@ enum Offsets {
|
||||
// CObjects — which is exactly why the engine pushes handler/frame
|
||||
// through it rather than a plain rawget.
|
||||
LUA_RAWGETI = 0x6F3BC0,
|
||||
// `lua_rawseti(L, idx, n)` — __fastcall (n on the stack): t[n] = top,
|
||||
// pops it. Calls luaH_setnum (0x006FAD80); stamps the GC context tag.
|
||||
LUA_RAW_SET_I = 0x6F3F60,
|
||||
// `luaA_indexAcceptable(L, idx)` — the API's index walk: stack, pseudo
|
||||
// (registry / globals) or upvalue index → TObject*, or NULL for a
|
||||
// positive index at or past the top. __fastcall(L /*ecx*/, idx /*edx*/),
|
||||
@@ -8479,6 +8482,9 @@ enum Offsets {
|
||||
// `luaL_optnumber(L, narg, double def)` — __fastcall (def on the stack,
|
||||
// ret 8), returns the double in ST0. As str_gsub calls it (0x007FD12F).
|
||||
LUAL_OPT_NUMBER = 0x6F4CD0,
|
||||
// `luaL_checknumber(L, narg)` — __fastcall, returns the double in ST0.
|
||||
// As str_format calls it (0x007FD570).
|
||||
LUAL_CHECK_NUMBER = 0x6F4C80,
|
||||
// `luaL_getn(L, tableIndex)` — Lua 5.0 auxlib table-length getter,
|
||||
// `int __fastcall(L /*ecx*/, idx /*edx*/)`, plain RET (no stack
|
||||
// args), length in eax. Verified by disassembly: a negative idx is
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `difftime(t2 [, t1])` — the seconds from `t1` (0 by default) to `t2`, the
|
||||
// companion of the engine's `date` and `time` globals. 4.3.4's
|
||||
// Script_difftime: C `difftime` over the two arguments as time_t.
|
||||
//
|
||||
// Registered on BOTH states, as the engine registers `date` and `time`.
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace BaseLib::DiffTime {
|
||||
|
||||
namespace {
|
||||
|
||||
int __fastcall Script_difftime(void *L) {
|
||||
const auto t2 = static_cast<time_t>(Game::Lua::CheckNumber(L, 1));
|
||||
const auto t1 = static_cast<time_t>(Game::Lua::OptNumber(L, 2, 0));
|
||||
Game::Lua::PushNumber(L, std::difftime(t2, t1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const Game::Doc::Field kDifftimeArgs[] = {
|
||||
Game::Doc::Req("time2", "number", "A time in seconds, as `time()` returns."),
|
||||
Game::Doc::Opt("time1", "number", "0"),
|
||||
};
|
||||
const Game::Doc::Field kDifftimeRets[] = {
|
||||
Game::Doc::Req("seconds", "number", "time2 minus time1."),
|
||||
};
|
||||
const Game::Doc::Function kDifftime{"The number of seconds from time1 to time2.",
|
||||
kDifftimeArgs, kDifftimeRets, "LuaGlobals"};
|
||||
|
||||
void RegisterInGame() {
|
||||
Game::Lua::RegisterGlobalFunction("difftime", &Script_difftime, &kDifftime);
|
||||
}
|
||||
|
||||
void RegisterGlue() {
|
||||
Game::Lua::RegisterGlueFunction("difftime", &Script_difftime, &kDifftime);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&RegisterInGame};
|
||||
const Game::GlueModuleAutoRegister _autoregGlue{&RegisterGlue};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace BaseLib::DiffTime
|
||||
+22
-1
@@ -16,13 +16,16 @@
|
||||
// - `math.fmod(x, y)` — floating-point remainder of `x / y`.
|
||||
// - `math.modf(x)` — split into integral + fractional parts.
|
||||
// - `math.huge` — positive infinity constant.
|
||||
// - `math.cosh(x)`, `math.sinh(x)`, `math.tanh(x)` — hyperbolic functions.
|
||||
//
|
||||
// `math.fmod` is a pure rename: Lua 5.0 ships the exact same C function under
|
||||
// the name `math.mod`; 5.1 renamed the Lua-facing entry to `math.fmod`. We
|
||||
// register it as a direct alias of the engine's `math.mod` C function
|
||||
// (`FUN_LUA_MATH_FMOD`) — no wrapper. `math.mod` stays available too, so code
|
||||
// written for either name works. `modf` and `huge` are genuinely new in 5.1
|
||||
// (not in 1.12's mathlib table), so we implement/define them here.
|
||||
// (not in 1.12's mathlib table), so we implement/define them here, as are the
|
||||
// hyperbolic functions (5.1's math_cosh/sinh/tanh: luaL_checknumber, the C
|
||||
// function, one number).
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
@@ -53,9 +56,27 @@ int __fastcall Script_math_modf(void *L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int __fastcall Script_math_cosh(void *L) {
|
||||
Game::Lua::PushNumber(L, std::cosh(Game::Lua::CheckNumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __fastcall Script_math_sinh(void *L) {
|
||||
Game::Lua::PushNumber(L, std::sinh(Game::Lua::CheckNumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __fastcall Script_math_tanh(void *L) {
|
||||
Game::Lua::PushNumber(L, std::tanh(Game::Lua::CheckNumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RegisterFns() {
|
||||
Game::Lua::RegisterTableFunction("math", "fmod", Script_math_fmod);
|
||||
Game::Lua::RegisterTableFunction("math", "modf", &Script_math_modf);
|
||||
Game::Lua::RegisterTableFunction("math", "cosh", &Script_math_cosh);
|
||||
Game::Lua::RegisterTableFunction("math", "sinh", &Script_math_sinh);
|
||||
Game::Lua::RegisterTableFunction("math", "tanh", &Script_math_tanh);
|
||||
|
||||
// `math.huge` — a numeric constant, not a function. Fetch the (now
|
||||
// guaranteed-present) `math` table and set the field directly.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `newproxy([false | true | proxy])` — Lua 5.1's empty userdata (luaB_newproxy,
|
||||
// lbaselib.c). `false` or nothing: no metatable. `true`: a new metatable of
|
||||
// its own. Another proxy: share that proxy's metatable. Only metatables made
|
||||
// by `newproxy(true)` count; any other argument raises "boolean or proxy
|
||||
// expected". 5.0 has userdata and their `__gc`, only not this function.
|
||||
//
|
||||
// As in 5.1, the closure's upvalue is a weak table that marks the valid
|
||||
// metatables (weak[m] = true), and is its own metatable with __mode "kv".
|
||||
//
|
||||
// Registered on BOTH states (language-level helper, both are 5.0).
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
namespace BaseLib::NewProxy {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace Lua = Game::Lua;
|
||||
|
||||
int __fastcall Script_newproxy(void *L) {
|
||||
Lua::SetTop(L, 1);
|
||||
Lua::NewUserdata(L, 0); // the proxy, at 2
|
||||
if (Lua::ToBoolean(L, 1) == 0)
|
||||
return 1; // no metatable
|
||||
if (Lua::Type(L, 1) == Lua::TYPE_BOOLEAN) {
|
||||
Lua::NewTable(L); // a new metatable m ...
|
||||
Lua::PushValue(L, -1);
|
||||
Lua::PushBoolean(L, 1);
|
||||
Lua::RawSet(L, Lua::UpvalueIndex(1)); // ... marked valid: weak[m] = true
|
||||
} else {
|
||||
bool valid = false;
|
||||
if (Lua::GetMetatable(L, 1)) {
|
||||
Lua::RawGet(L, Lua::UpvalueIndex(1));
|
||||
valid = Lua::ToBoolean(L, -1) != 0;
|
||||
Lua::SetTop(L, -2);
|
||||
}
|
||||
if (!valid)
|
||||
Lua::ArgError(L, 1, "boolean or proxy expected");
|
||||
Lua::GetMetatable(L, 1);
|
||||
}
|
||||
Lua::SetMetatable(L, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Register() {
|
||||
void *L = Lua::State();
|
||||
if (L == nullptr)
|
||||
return;
|
||||
Lua::PushString(L, "newproxy");
|
||||
Lua::NewTable(L); // the weak table w
|
||||
Lua::PushString(L, "__mode");
|
||||
Lua::PushString(L, "kv");
|
||||
Lua::RawSet(L, -3);
|
||||
Lua::PushValue(L, -1);
|
||||
Lua::SetMetatable(L, -2); // w is its own metatable
|
||||
Lua::PushCClosure(L, &Script_newproxy, 1);
|
||||
Lua::SetTable(L, Lua::GLOBALS_INDEX);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&Register};
|
||||
const Game::GlueModuleAutoRegister _autoregGlue{&Register};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace BaseLib::NewProxy
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `scrub(...)` — returns its arguments, each one that is not a boolean,
|
||||
// number or string replaced by nil. 4.3.4's Script_scrub.
|
||||
//
|
||||
// Registered on both Lua states, like the base-library helpers.
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
namespace BaseLib::Scrub {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace Lua = Game::Lua;
|
||||
|
||||
int __fastcall Script_scrub(void *L) {
|
||||
const int top = Lua::GetTop(L);
|
||||
Lua::CheckStack(L, 1);
|
||||
for (int i = 1; i <= top; i++) {
|
||||
const int type = Lua::Type(L, i);
|
||||
if (type != Lua::TYPE_BOOLEAN && type != Lua::TYPE_NUMBER && type != Lua::TYPE_STRING) {
|
||||
Lua::PushNil(L);
|
||||
Lua::Replace(L, i);
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
const Game::Doc::Field kScrubArgs[] = {
|
||||
Game::Doc::Vararg("any"),
|
||||
};
|
||||
const Game::Doc::Field kScrubRets[] = {
|
||||
Game::Doc::Vararg("any", "Each argument, or nil when it is not a boolean, number or string."),
|
||||
};
|
||||
const Game::Doc::Function kScrub{
|
||||
"Returns its arguments, with each one that is not a boolean, number or string replaced by nil.",
|
||||
kScrubArgs, kScrubRets, "LuaGlobals"};
|
||||
|
||||
void RegisterInGame() {
|
||||
Game::Lua::RegisterGlobalFunction("scrub", &Script_scrub, &kScrub);
|
||||
}
|
||||
|
||||
void RegisterGlue() {
|
||||
Game::Lua::RegisterGlueFunction("scrub", &Script_scrub, &kScrub);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&RegisterInGame};
|
||||
const Game::GlueModuleAutoRegister _autoregGlue{&RegisterGlue};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace BaseLib::Scrub
|
||||
+14
-16
@@ -15,7 +15,7 @@
|
||||
//
|
||||
// - `string.match(s, pattern [, init])` — first-match extraction (5.1),
|
||||
// also the global `strmatch`, as `string.find` is `strfind`.
|
||||
// - `string.gmatch(s, pattern)` — match iterator (5.1).
|
||||
// - `string.gmatch(s, pattern)` — match iterator (5.1), also `gmatch`.
|
||||
// - `strsplit(sep, str [, pieces])` — WoW global; split on any char.
|
||||
// - `strjoin(delimiter, ...)` — WoW global; join varargs.
|
||||
// - `strtrim(str [, chars])` — WoW global; trim a char set.
|
||||
@@ -347,8 +347,6 @@ int __fastcall Script_string_reverse(void *L) {
|
||||
// result for every `i`. Positions truncate toward zero as the engine's
|
||||
// str_byte and str_sub do. The engine's lua_checkstack (0x6F2F30) refuses
|
||||
// more than 2048 stack slots, so the longest range is about 2000 bytes.
|
||||
using luaL_checklstring_t = const char *(__fastcall *)(void *L, int narg, unsigned int *len);
|
||||
using luaL_optnumber_t = double(__fastcall *)(void *L, int narg, double def);
|
||||
|
||||
// 5.1's posrelat: a negative position counts from the end; before the start is 0.
|
||||
long PosRelat(long pos, long len) {
|
||||
@@ -358,14 +356,12 @@ long PosRelat(long pos, long len) {
|
||||
}
|
||||
|
||||
int __fastcall Script_string_byte(void *L) {
|
||||
const auto checkLString =
|
||||
reinterpret_cast<luaL_checklstring_t>(Offsets::LUAL_CHECK_LSTRING);
|
||||
const auto optNumber = reinterpret_cast<luaL_optnumber_t>(Offsets::LUAL_OPT_NUMBER);
|
||||
unsigned int l = 0;
|
||||
const char *s = checkLString(L, 1, &l);
|
||||
const char *s = Game::Lua::CheckLString(L, 1, &l);
|
||||
const long len = static_cast<long>(l);
|
||||
long posi = PosRelat(static_cast<long>(optNumber(L, 2, 1)), len);
|
||||
long pose = PosRelat(static_cast<long>(optNumber(L, 3, static_cast<double>(posi))), len);
|
||||
long posi = PosRelat(static_cast<long>(Game::Lua::OptNumber(L, 2, 1)), len);
|
||||
long pose =
|
||||
PosRelat(static_cast<long>(Game::Lua::OptNumber(L, 3, static_cast<double>(posi))), len);
|
||||
if (posi <= 0)
|
||||
posi = 1;
|
||||
if (pose > len)
|
||||
@@ -439,13 +435,15 @@ void RegisterFns() {
|
||||
Game::Lua::RegisterTableFunction("string", "join", &Script_strjoin);
|
||||
Game::Lua::RegisterTableFunction("string", "concat", &Script_strconcat, &kStrconcat);
|
||||
// 4.3.4's compat.lua binds each short global and its string-table twin
|
||||
// to ONE function by assignment (`strmatch = str.match`, `strrev =
|
||||
// str.reverse`, `str.trim = strtrim`, `str.split = strsplit`, `str.join =
|
||||
// strjoin`), so `strtrim == string.trim`. A second Register* call would
|
||||
// mint a second closure over the same C function, so the globals alias
|
||||
// the table entries by value instead. The string-table half is what lets
|
||||
// `(" x "):trim()` work and libraries capture it at file scope
|
||||
// (AceConfigCmd-3.0: `strtrim = string.trim`).
|
||||
// to ONE function by assignment (`gmatch = str.gmatch`, `strmatch =
|
||||
// str.match`, `strrev = str.reverse`, `str.trim = strtrim`, `str.split =
|
||||
// strsplit`, `str.join = strjoin`), so `strtrim == string.trim`. A second
|
||||
// Register* call would mint a second closure over the same C function, so
|
||||
// the globals alias the table entries by value instead. The string-table
|
||||
// half is what lets `(" x "):trim()` work and libraries capture it at
|
||||
// file scope (AceConfigCmd-3.0: `strtrim = string.trim`). 1.12's own
|
||||
// compat.lua set `strbyte` from the old string.byte, so it is re-aliased.
|
||||
Game::Lua::RegisterGlobalAlias("gmatch", "string", "gmatch");
|
||||
Game::Lua::RegisterGlobalAlias("strmatch", "string", "match");
|
||||
Game::Lua::RegisterGlobalAlias("strsplit", "string", "split");
|
||||
Game::Lua::RegisterGlobalAlias("strjoin", "string", "join");
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `table.removemulti(t [, pos [, count]])` — WoW's addition to the table
|
||||
// library (4.3.4's tremovemulti, ltablib.c): removes `count` elements (1 by
|
||||
// default) from position `pos` (the last element by default), shifts the
|
||||
// rest down and returns the removed elements in order.
|
||||
//
|
||||
// 5.0 keeps a table's length apart from its keys, so like the engine's own
|
||||
// table.remove (0x7FB770) this records the new length with luaL_setn.
|
||||
//
|
||||
// Registered on BOTH states (language-level helper, both are 5.0).
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
|
||||
namespace Table::RemoveMulti {
|
||||
|
||||
namespace {
|
||||
|
||||
using luaL_getn_t = int(__fastcall *)(void *L, int t);
|
||||
|
||||
int __fastcall Script_table_removemulti(void *L) {
|
||||
if (Game::Lua::Type(L, 1) != Game::Lua::TYPE_TABLE)
|
||||
Game::Lua::ArgError(L, 1, "table expected");
|
||||
const int e = reinterpret_cast<luaL_getn_t>(Offsets::LUAL_GETN)(L, 1);
|
||||
int pos = static_cast<int>(Game::Lua::OptNumber(L, 2, e));
|
||||
const int n = static_cast<int>(Game::Lua::OptNumber(L, 3, 1));
|
||||
if (e == 0)
|
||||
return 0;
|
||||
if (pos <= 0 || n < 0 || pos > e || pos + n - 1 > e)
|
||||
Game::Lua::Error(L, "parameters out of bounds");
|
||||
if (!Game::Lua::CheckStack(L, n))
|
||||
Game::Lua::Error(L, "stack overflow");
|
||||
for (int i = pos; i < pos + n; i++)
|
||||
Game::Lua::RawGetI(L, 1, i);
|
||||
for (; pos < e; pos++) {
|
||||
Game::Lua::RawGetI(L, 1, pos + n);
|
||||
Game::Lua::RawSetI(L, 1, pos);
|
||||
}
|
||||
for (; pos < e + n; pos++) {
|
||||
Game::Lua::PushNil(L);
|
||||
Game::Lua::RawSetI(L, 1, pos);
|
||||
}
|
||||
Game::Lua::SetN(L, 1, e - n);
|
||||
return n;
|
||||
}
|
||||
|
||||
void Register() {
|
||||
Game::Lua::RegisterTableFunction("table", "removemulti", &Script_table_removemulti);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&Register};
|
||||
const Game::GlueModuleAutoRegister _autoregGlue{&Register};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace Table::RemoveMulti
|
||||
Reference in New Issue
Block a user